mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to inbound.
This commit is contained in:
commit
2866421e0f
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "121f70034b7ef01836aa345f8ff37b61b96ac88e",
|
||||
"revision": "cf23b263b4bbf1024210350f24c7e6eb5872a4be",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1384551293000">
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1385765544000">
|
||||
<emItems>
|
||||
<emItem blockID="i454" id="sqlmoz@facebook.com">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
@ -69,8 +69,8 @@
|
||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i348" id="{13c9f1f9-2322-4d5c-81df-6d4bf8476ba4}">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||
<emItem blockID="i488" id="jid1-4P0kohSJxU1qGg@jetpack">
|
||||
<versionRange minVersion="1.2.50" maxVersion="1.2.50" severity="1">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i486" id="xz123@ya456.com">
|
||||
@ -102,14 +102,18 @@
|
||||
<versionRange minVersion="3.4.1" maxVersion="3.4.1.194" severity="1">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i474" id="{906000a4-88d9-4d52-b209-7a772970d91f}">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
<emItem blockID="i100" id="{394DCBA4-1F92-4f8e-8EC9-8D2CB90CB69B}">
|
||||
<versionRange minVersion="2.5.0" maxVersion="2.5.0" severity="1">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i40" id="{28387537-e3f9-4ed7-860c-11e69af4a8a0}">
|
||||
<versionRange minVersion="0.1" maxVersion="4.3.1.00" severity="1">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i20" id="{AB2CE124-6272-4b12-94A9-7303C7397BD1}">
|
||||
<versionRange minVersion="0.1" maxVersion="5.2.0.7164" severity="1">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i430" id="1chtw@facebook.com">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
</versionRange>
|
||||
@ -181,10 +185,6 @@
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i475" id="{B21F5E31-B8E8-41CD-B74C-168A71A10E49}">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i11" id="yslow@yahoo-inc.com">
|
||||
<versionRange minVersion="2.0.5" maxVersion="2.0.5">
|
||||
<targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
|
||||
@ -256,6 +256,10 @@
|
||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i487" id="{df6bb2ec-333b-4267-8c4f-3f27dc8c6e07}">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i142" id="{a3a5c777-f583-4fef-9380-ab4add1bc2a8}">
|
||||
<versionRange minVersion="2.0.3" maxVersion="2.0.3">
|
||||
</versionRange>
|
||||
@ -311,8 +315,8 @@
|
||||
<versionRange minVersion="0" maxVersion="*">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i100" id="{394DCBA4-1F92-4f8e-8EC9-8D2CB90CB69B}">
|
||||
<versionRange minVersion="2.5.0" maxVersion="2.5.0" severity="1">
|
||||
<emItem blockID="i348" id="{13c9f1f9-2322-4d5c-81df-6d4bf8476ba4}">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i338" id="{1FD91A9C-410C-4090-BBCC-55D3450EF433}">
|
||||
@ -455,6 +459,10 @@
|
||||
<versionRange minVersion="0" maxVersion="*">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i474" id="{906000a4-88d9-4d52-b209-7a772970d91f}">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i453" id="/^brasilescape.*\@facebook\.com$/">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
</versionRange>
|
||||
@ -582,8 +590,8 @@
|
||||
<versionRange severity="3">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i20" id="{AB2CE124-6272-4b12-94A9-7303C7397BD1}">
|
||||
<versionRange minVersion="0.1" maxVersion="5.2.0.7164" severity="1">
|
||||
<emItem blockID="i491" id="{515b2424-5911-40bd-8a2c-bdb20286d8f5}">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i444" id="fplayer@adobe.flash">
|
||||
@ -662,6 +670,10 @@
|
||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i490" id="now.msn.com@services.mozilla.org">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i312" id="extension21804@extension21804.com">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||
</versionRange>
|
||||
@ -775,6 +787,10 @@
|
||||
<versionRange minVersion="0" maxVersion="*">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i489" id="astrovia@facebook.com">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="3">
|
||||
</versionRange>
|
||||
</emItem>
|
||||
<emItem blockID="i68" id="flashupdate@adobe.com">
|
||||
<versionRange minVersion="0" maxVersion="*">
|
||||
</versionRange>
|
||||
|
@ -1447,11 +1447,6 @@
|
||||
|
||||
var uriIsAboutBlank = !aURI || aURI == "about:blank";
|
||||
|
||||
if (!aURI || isBlankPageURL(aURI))
|
||||
t.setAttribute("label", this.mStringBundle.getString("tabs.emptyTabTitle"));
|
||||
else
|
||||
t.setAttribute("label", aURI);
|
||||
|
||||
t.setAttribute("crop", "end");
|
||||
t.setAttribute("onerror", "this.removeAttribute('image');");
|
||||
t.className = "tabbrowser-tab";
|
||||
@ -1558,6 +1553,14 @@
|
||||
// initialized by this point.
|
||||
this.mPanelContainer.appendChild(notificationbox);
|
||||
|
||||
// We've waited until the tab is in the DOM to set the label. This
|
||||
// allows the TabLabelModified event to be properly dispatched.
|
||||
if (!aURI || isBlankPageURL(aURI)) {
|
||||
t.label = this.mStringBundle.getString("tabs.emptyTabTitle");
|
||||
} else {
|
||||
t.label = aURI;
|
||||
}
|
||||
|
||||
this.tabContainer.updateVisibility();
|
||||
|
||||
// wire up a progress listener for the new browser object.
|
||||
@ -3345,8 +3348,7 @@
|
||||
this._closeWindowWithLastTab = Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab");
|
||||
|
||||
var tab = this.firstChild;
|
||||
tab.setAttribute("label",
|
||||
this.tabbrowser.mStringBundle.getString("tabs.emptyTabTitle"));
|
||||
tab.label = this.tabbrowser.mStringBundle.getString("tabs.emptyTabTitle");
|
||||
tab.setAttribute("crop", "end");
|
||||
tab.setAttribute("onerror", "this.removeAttribute('image');");
|
||||
this.adjustTabstrip();
|
||||
@ -4652,7 +4654,8 @@
|
||||
validate="never"
|
||||
role="presentation"/>
|
||||
<xul:label flex="1"
|
||||
xbl:inherits="value=label,crop,accesskey,fadein,pinned,selected"
|
||||
anonid="tab-label"
|
||||
xbl:inherits="value=visibleLabel,crop,accesskey,fadein,pinned,selected"
|
||||
class="tab-text tab-label"
|
||||
role="presentation"/>
|
||||
<xul:toolbarbutton anonid="close-button"
|
||||
@ -4663,6 +4666,32 @@
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<property name="label">
|
||||
<getter>
|
||||
return this.getAttribute("label");
|
||||
</getter>
|
||||
<setter>
|
||||
this.setAttribute("label", val);
|
||||
let event = new CustomEvent("TabLabelModified", {
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
});
|
||||
this.dispatchEvent(event);
|
||||
|
||||
// Let listeners prevent synchronizing the actual label to the
|
||||
// visible label (allowing them to override the visible label).
|
||||
if (!event.defaultPrevented)
|
||||
this.visibleLabel = val;
|
||||
</setter>
|
||||
</property>
|
||||
<property name="visibleLabel">
|
||||
<getter>
|
||||
return this.getAttribute("visibleLabel");
|
||||
</getter>
|
||||
<setter>
|
||||
this.setAttribute("visibleLabel", val);
|
||||
</setter>
|
||||
</property>
|
||||
<property name="pinned" readonly="true">
|
||||
<getter>
|
||||
return this.getAttribute("pinned") == "true";
|
||||
|
@ -109,6 +109,7 @@ run-if = crashreporter
|
||||
[browser_CTP_resize.js]
|
||||
[browser_URLBarSetURI.js]
|
||||
[browser_aboutHealthReport.js]
|
||||
skip-if = os == "linux" # Bug 924307
|
||||
[browser_aboutHome.js]
|
||||
[browser_aboutSyncProgress.js]
|
||||
[browser_addKeywordSearch.js]
|
||||
@ -317,6 +318,7 @@ skip-if = os == "linux" # No tabs in titlebar on linux
|
||||
[browser_urlbar_search_healthreport.js]
|
||||
[browser_utilityOverlay.js]
|
||||
[browser_visibleFindSelection.js]
|
||||
[browser_visibleLabel.js]
|
||||
[browser_visibleTabs.js]
|
||||
[browser_visibleTabs_bookmarkAllPages.js]
|
||||
[browser_visibleTabs_bookmarkAllTabs.js]
|
||||
|
94
browser/base/content/test/general/browser_visibleLabel.js
Normal file
94
browser/base/content/test/general/browser_visibleLabel.js
Normal file
@ -0,0 +1,94 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Tests:
|
||||
* verify that the visibleLabel attribute works
|
||||
* verify the TabLabelModified event works for both existing and new tabs
|
||||
*/
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
registerCleanupFunction(function() {
|
||||
gBrowser.removeCurrentTab({animate: false});
|
||||
});
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank",
|
||||
{skipAnimation: true});
|
||||
tab.linkedBrowser.addEventListener("load", function onLoad(event) {
|
||||
event.currentTarget.removeEventListener("load", onLoad, true);
|
||||
executeSoon(afterLoad);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function afterLoad() {
|
||||
let tab = gBrowser.selectedTab;
|
||||
let xulLabel = document.getAnonymousElementByAttribute(tab, "anonid",
|
||||
"tab-label");
|
||||
// Verify we're starting out on the right foot
|
||||
is(tab.label, "New Tab", "Initial tab label is default");
|
||||
is(xulLabel.value, "New Tab", "Label element is default");
|
||||
is(tab.visibleLabel, "New Tab", "visibleLabel is default");
|
||||
|
||||
// Check that a normal label setting works correctly
|
||||
tab.label = "Hello, world!";
|
||||
is(tab.label, "Hello, world!", "tab label attribute set via tab.label");
|
||||
is(xulLabel.value, "Hello, world!", "xul:label set via tab.label");
|
||||
is(tab.visibleLabel, "Hello, world!", "visibleLabel set via tab.label");
|
||||
|
||||
// Check that setting visibleLabel only affects the label element
|
||||
tab.visibleLabel = "Goodnight, Irene";
|
||||
is(tab.label, "Hello, world!", "Tab.label unaffected by visibleLabel setter");
|
||||
is(xulLabel.value, "Goodnight, Irene",
|
||||
"xul:label set by visibleLabel setter");
|
||||
is(tab.visibleLabel, "Goodnight, Irene",
|
||||
"visibleLabel attribute set by visibleLabel setter");
|
||||
|
||||
// Check that setting the label property hits everything
|
||||
tab.label = "One more label";
|
||||
is(tab.label, "One more label",
|
||||
"Tab label set via label property after diverging from visibleLabel");
|
||||
is(xulLabel.value, "One more label",
|
||||
"xul:label set via label property after diverging from visibleLabel");
|
||||
is(tab.visibleLabel, "One more label",
|
||||
"visibleLabel set from label property after diverging from visibleLabel");
|
||||
|
||||
tab.addEventListener("TabLabelModified", overrideTabLabel, true);
|
||||
tab.label = "This won't be the visibleLabel";
|
||||
}
|
||||
|
||||
function overrideTabLabel(aEvent) {
|
||||
aEvent.target.removeEventListener("TabLabelModified", overrideTabLabel, true);
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopPropagation();
|
||||
aEvent.target.visibleLabel = "Handler set this as the visible label";
|
||||
executeSoon(checkTabLabelModified);
|
||||
}
|
||||
|
||||
function checkTabLabelModified() {
|
||||
let tab = gBrowser.selectedTab;
|
||||
let xulLabel = document.getAnonymousElementByAttribute(tab, "anonid",
|
||||
"tab-label");
|
||||
|
||||
is(tab.label, "This won't be the visibleLabel",
|
||||
"Tab label set via label property that triggered event");
|
||||
is(xulLabel.value, "Handler set this as the visible label",
|
||||
"xul:label set by TabLabelModified handler");
|
||||
is(tab.visibleLabel, "Handler set this as the visible label",
|
||||
"visibleLabel set by TabLabelModified handler");
|
||||
|
||||
gBrowser.removeCurrentTab({animate: false});
|
||||
executeSoon(checkTabLabelModifiedOnNewTab);
|
||||
}
|
||||
|
||||
function checkTabLabelModifiedOnNewTab() {
|
||||
gBrowser.tabContainer.addEventListener("TabLabelModified",
|
||||
handleTabLabelModifiedOnNewTab, true);
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank",
|
||||
{skipAnimation: true});
|
||||
}
|
||||
|
||||
function handleTabLabelModifiedOnNewTab(aEvent) {
|
||||
gBrowser.tabContainer.removeEventListener("TabLabelModified",
|
||||
handleTabLabelModifiedOnNewTab, true);
|
||||
ok(true, "Event received from new tab default being set");
|
||||
executeSoon(finish);
|
||||
}
|
@ -77,10 +77,10 @@ browser.jar:
|
||||
content/browser/sync/aboutSyncTabs.js (content/sync/aboutSyncTabs.js)
|
||||
content/browser/sync/aboutSyncTabs.css (content/sync/aboutSyncTabs.css)
|
||||
content/browser/sync/aboutSyncTabs-bindings.xml (content/sync/aboutSyncTabs-bindings.xml)
|
||||
* content/browser/sync/setup.xul (content/sync/setup.xul)
|
||||
content/browser/sync/setup.xul (content/sync/setup.xul)
|
||||
content/browser/sync/addDevice.js (content/sync/addDevice.js)
|
||||
content/browser/sync/addDevice.xul (content/sync/addDevice.xul)
|
||||
* content/browser/sync/setup.js (content/sync/setup.js)
|
||||
content/browser/sync/setup.js (content/sync/setup.js)
|
||||
content/browser/sync/genericChange.xul (content/sync/genericChange.xul)
|
||||
content/browser/sync/genericChange.js (content/sync/genericChange.js)
|
||||
content/browser/sync/key.xhtml (content/sync/key.xhtml)
|
||||
|
@ -17,10 +17,11 @@
|
||||
<footer id="PanelUI-footer">
|
||||
<!-- The parentNode is used so that the footer is presented as the anchor
|
||||
instead of just the button being the anchor. -->
|
||||
<toolbarbutton id="PanelUI-customize" label="&appMenuCustomize.label;"
|
||||
exitLabel="&appMenuCustomizeExit.label;" tabindex="0"
|
||||
oncommand="gCustomizeMode.toggle();"/>
|
||||
<toolbarbutton id="PanelUI-help" label="&helpMenu.label;" tabindex="0"
|
||||
oncommand="PanelUI.showHelpView(this.parentNode);"/>
|
||||
<toolbarbutton id="PanelUI-customize" label="&appMenuCustomize.label;" tabindex="0"
|
||||
oncommand="gCustomizeMode.toggle();"/>
|
||||
<toolbarbutton id="PanelUI-quit" tabindex="0"
|
||||
#ifdef XP_WIN
|
||||
label="&quitApplicationCmdWin.label;"
|
||||
|
@ -135,6 +135,10 @@ CustomizeMode.prototype = {
|
||||
let mainView = window.PanelUI.mainView;
|
||||
panelHolder.appendChild(mainView);
|
||||
|
||||
let customizeButton = document.getElementById("PanelUI-customize");
|
||||
customizeButton.setAttribute("enterLabel", customizeButton.getAttribute("label"));
|
||||
customizeButton.setAttribute("label", customizeButton.getAttribute("exitLabel"));
|
||||
|
||||
this._transitioning = true;
|
||||
|
||||
let customizer = document.getElementById("customization-container");
|
||||
@ -255,6 +259,10 @@ CustomizeMode.prototype = {
|
||||
window.PanelUI.setMainView(window.PanelUI.mainView);
|
||||
window.PanelUI.menuButton.disabled = false;
|
||||
|
||||
let customizeButton = document.getElementById("PanelUI-customize");
|
||||
customizeButton.setAttribute("exitLabel", customizeButton.getAttribute("label"));
|
||||
customizeButton.setAttribute("label", customizeButton.getAttribute("enterLabel"));
|
||||
|
||||
// We have to use setAttribute/removeAttribute here instead of the
|
||||
// property because the XBL property will be set later, and right
|
||||
// now we'd be setting an expando, which breaks the XBL property.
|
||||
|
@ -34,6 +34,7 @@ const EVENTS = {
|
||||
FETCHED_SCOPES: "Debugger:FetchedScopes",
|
||||
FETCHED_VARIABLES: "Debugger:FetchedVariables",
|
||||
FETCHED_PROPERTIES: "Debugger:FetchedProperties",
|
||||
FETCHED_BUBBLE_PROPERTIES: "Debugger:FetchedBubbleProperties",
|
||||
FETCHED_WATCH_EXPRESSIONS: "Debugger:FetchedWatchExpressions",
|
||||
|
||||
// When a breakpoint has been added or removed on the debugger server.
|
||||
@ -75,6 +76,14 @@ const EVENTS = {
|
||||
LAYOUT_CHANGED: "Debugger:LayoutChanged"
|
||||
};
|
||||
|
||||
// Descriptions for what a stack frame represents after the debugger pauses.
|
||||
const FRAME_TYPE = {
|
||||
NORMAL: 0,
|
||||
CONDITIONAL_BREAKPOINT_EVAL: 1,
|
||||
WATCH_EXPRESSIONS_EVAL: 2,
|
||||
PUBLIC_CLIENT_EVAL: 3
|
||||
};
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
|
||||
@ -86,9 +95,10 @@ Cu.import("resource:///modules/devtools/VariablesViewController.jsm");
|
||||
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
|
||||
|
||||
const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
|
||||
const Editor = require("devtools/sourceeditor/editor");
|
||||
const promise = require("sdk/core/promise");
|
||||
const Editor = require("devtools/sourceeditor/editor");
|
||||
const DebuggerEditor = require("devtools/sourceeditor/debugger.js");
|
||||
const {Tooltip} = require("devtools/shared/widgets/Tooltip");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Parser",
|
||||
"resource:///modules/devtools/Parser.jsm");
|
||||
@ -521,8 +531,7 @@ function StackFrames() {
|
||||
StackFrames.prototype = {
|
||||
get activeThread() DebuggerController.activeThread,
|
||||
currentFrameDepth: -1,
|
||||
_isWatchExpressionsEvaluation: false,
|
||||
_isConditionalBreakpointEvaluation: false,
|
||||
_currentFrameDescription: FRAME_TYPE.NORMAL,
|
||||
_syncedWatchExpressions: null,
|
||||
_currentWatchExpressions: null,
|
||||
_currentBreakpointLocation: null,
|
||||
@ -558,6 +567,7 @@ StackFrames.prototype = {
|
||||
this.activeThread.removeListener("framescleared", this._onFramesCleared);
|
||||
this.activeThread.removeListener("blackboxchange", this._onBlackBoxChange);
|
||||
this.activeThread.removeListener("prettyprintchange", this._onPrettyPrintChange);
|
||||
clearNamedTimeout("frames-cleared");
|
||||
},
|
||||
|
||||
/**
|
||||
@ -611,10 +621,8 @@ StackFrames.prototype = {
|
||||
* Handler for the thread client's resumed notification.
|
||||
*/
|
||||
_onResumed: function() {
|
||||
DebuggerView.editor.clearDebugLocation();
|
||||
|
||||
// Prepare the watch expression evaluation string for the next pause.
|
||||
if (!this._isWatchExpressionsEvaluation) {
|
||||
if (this._currentFrameDescription != FRAME_TYPE.WATCH_EXPRESSIONS_EVAL) {
|
||||
this._currentWatchExpressions = this._syncedWatchExpressions;
|
||||
}
|
||||
},
|
||||
@ -640,16 +648,13 @@ StackFrames.prototype = {
|
||||
// Make sure a breakpoint actually exists at the specified url and line.
|
||||
let breakpointPromise = DebuggerController.Breakpoints._getAdded(breakLocation);
|
||||
if (breakpointPromise) {
|
||||
breakpointPromise.then(aBreakpointClient => {
|
||||
if ("conditionalExpression" in aBreakpointClient) {
|
||||
// Evaluating the current breakpoint's conditional expression will
|
||||
// cause the stack frames to be cleared and active thread to pause,
|
||||
// sending a 'clientEvaluated' packed and adding the frames again.
|
||||
this.evaluate(aBreakpointClient.conditionalExpression, 0);
|
||||
this._isConditionalBreakpointEvaluation = true;
|
||||
waitForNextPause = true;
|
||||
}
|
||||
});
|
||||
breakpointPromise.then(({ conditionalExpression: e }) => { if (e) {
|
||||
// Evaluating the current breakpoint's conditional expression will
|
||||
// cause the stack frames to be cleared and active thread to pause,
|
||||
// sending a 'clientEvaluated' packed and adding the frames again.
|
||||
this.evaluate(e, { depth: 0, meta: FRAME_TYPE.CONDITIONAL_BREAKPOINT_EVAL });
|
||||
waitForNextPause = true;
|
||||
}});
|
||||
}
|
||||
}
|
||||
// We'll get our evaluation of the current breakpoint's conditional
|
||||
@ -657,8 +662,8 @@ StackFrames.prototype = {
|
||||
if (waitForNextPause) {
|
||||
return;
|
||||
}
|
||||
if (this._isConditionalBreakpointEvaluation) {
|
||||
this._isConditionalBreakpointEvaluation = false;
|
||||
if (this._currentFrameDescription == FRAME_TYPE.CONDITIONAL_BREAKPOINT_EVAL) {
|
||||
this._currentFrameDescription = FRAME_TYPE.NORMAL;
|
||||
// If the breakpoint's conditional expression evaluation is falsy,
|
||||
// automatically resume execution.
|
||||
if (VariablesView.isFalsy({ value: this._currentEvaluation.return })) {
|
||||
@ -673,8 +678,7 @@ StackFrames.prototype = {
|
||||
if (watchExpressions) {
|
||||
// Evaluation causes the stack frames to be cleared and active thread to
|
||||
// pause, sending a 'clientEvaluated' packet and adding the frames again.
|
||||
this.evaluate(watchExpressions, 0);
|
||||
this._isWatchExpressionsEvaluation = true;
|
||||
this.evaluate(watchExpressions, { depth: 0, meta: FRAME_TYPE.WATCH_EXPRESSIONS_EVAL });
|
||||
waitForNextPause = true;
|
||||
}
|
||||
// We'll get our evaluation of the current watch expressions the next time
|
||||
@ -682,8 +686,8 @@ StackFrames.prototype = {
|
||||
if (waitForNextPause) {
|
||||
return;
|
||||
}
|
||||
if (this._isWatchExpressionsEvaluation) {
|
||||
this._isWatchExpressionsEvaluation = false;
|
||||
if (this._currentFrameDescription == FRAME_TYPE.WATCH_EXPRESSIONS_EVAL) {
|
||||
this._currentFrameDescription = FRAME_TYPE.NORMAL;
|
||||
// If an error was thrown during the evaluation of the watch expressions,
|
||||
// then at least one expression evaluation could not be performed. So
|
||||
// remove the most recent watch expression and try again.
|
||||
@ -697,6 +701,11 @@ StackFrames.prototype = {
|
||||
// Make sure the debugger view panes are visible, then refill the frames.
|
||||
DebuggerView.showInstrumentsPane();
|
||||
this._refillFrames();
|
||||
|
||||
// No additional processing is necessary for this stack frame.
|
||||
if (this._currentFrameDescription != FRAME_TYPE.NORMAL) {
|
||||
this._currentFrameDescription = FRAME_TYPE.NORMAL;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -714,29 +723,34 @@ StackFrames.prototype = {
|
||||
let title = StackFrameUtils.getFrameTitle(frame);
|
||||
DebuggerView.StackFrames.addFrame(title, location, line, depth, isBlackBoxed);
|
||||
}
|
||||
if (this.currentFrameDepth == -1) {
|
||||
DebuggerView.StackFrames.selectedDepth = 0;
|
||||
}
|
||||
if (this.activeThread.moreFrames) {
|
||||
DebuggerView.StackFrames.dirty = true;
|
||||
}
|
||||
|
||||
DebuggerView.StackFrames.selectedDepth = Math.max(this.currentFrameDepth, 0);
|
||||
DebuggerView.StackFrames.dirty = this.activeThread.moreFrames;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for the thread client's framescleared notification.
|
||||
*/
|
||||
_onFramesCleared: function() {
|
||||
this.currentFrameDepth = -1;
|
||||
this._currentWatchExpressions = null;
|
||||
this._currentBreakpointLocation = null;
|
||||
this._currentEvaluation = null;
|
||||
this._currentException = null;
|
||||
this._currentReturnedValue = null;
|
||||
switch (this._currentFrameDescription) {
|
||||
case FRAME_TYPE.NORMAL:
|
||||
this._currentEvaluation = null;
|
||||
this._currentException = null;
|
||||
this._currentReturnedValue = null;
|
||||
break;
|
||||
case FRAME_TYPE.CONDITIONAL_BREAKPOINT_EVAL:
|
||||
this._currentBreakpointLocation = null;
|
||||
break;
|
||||
case FRAME_TYPE.WATCH_EXPRESSIONS_EVAL:
|
||||
this._currentWatchExpressions = null;
|
||||
break;
|
||||
}
|
||||
|
||||
// After each frame step (in, over, out), framescleared is fired, which
|
||||
// forces the UI to be emptied and rebuilt on framesadded. Most of the times
|
||||
// this is not necessary, and will result in a brief redraw flicker.
|
||||
// To avoid it, invalidate the UI only after a short time if necessary.
|
||||
window.setTimeout(this._afterFramesCleared, FRAME_STEP_CLEAR_DELAY);
|
||||
setNamedTimeout("frames-cleared", FRAME_STEP_CLEAR_DELAY, this._afterFramesCleared);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -744,6 +758,8 @@ StackFrames.prototype = {
|
||||
*/
|
||||
_onBlackBoxChange: function() {
|
||||
if (this.activeThread.state == "paused") {
|
||||
// Hack to avoid selecting the topmost frame after blackboxing a source.
|
||||
this.currentFrameDepth = NaN;
|
||||
this._refillFrames();
|
||||
}
|
||||
},
|
||||
@ -765,6 +781,7 @@ StackFrames.prototype = {
|
||||
if (this.activeThread.cachedFrames.length) {
|
||||
return;
|
||||
}
|
||||
DebuggerView.editor.clearDebugLocation();
|
||||
DebuggerView.StackFrames.empty();
|
||||
DebuggerView.Sources.unhighlightBreakpoint();
|
||||
DebuggerView.WatchExpressions.toggleContents(true);
|
||||
@ -779,10 +796,8 @@ StackFrames.prototype = {
|
||||
*
|
||||
* @param number aDepth
|
||||
* The depth of the frame in the stack.
|
||||
* @param boolean aDontSwitchSources
|
||||
* Flag on whether or not we want to switch the selected source.
|
||||
*/
|
||||
selectFrame: function(aDepth, aDontSwitchSources) {
|
||||
selectFrame: function(aDepth) {
|
||||
// Make sure the frame at the specified depth exists first.
|
||||
let frame = this.activeThread.cachedFrames[this.currentFrameDepth = aDepth];
|
||||
if (!frame) {
|
||||
@ -795,15 +810,22 @@ StackFrames.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
// Move the editor's caret to the proper url and line.
|
||||
DebuggerView.setEditorLocation(where.url, where.line);
|
||||
// Highlight the breakpoint at the specified url and line if it exists.
|
||||
DebuggerView.Sources.highlightBreakpoint(where, { noEditorUpdate: true });
|
||||
// Don't change the editor's location if the execution was paused by a
|
||||
// public client evaluation. This is useful for adding overlays on
|
||||
// top of the editor, like a variable inspection popup.
|
||||
if (this._currentFrameDescription != FRAME_TYPE.PUBLIC_CLIENT_EVAL) {
|
||||
// Move the editor's caret to the proper url and line.
|
||||
DebuggerView.setEditorLocation(where.url, where.line);
|
||||
// Highlight the breakpoint at the specified url and line if it exists.
|
||||
DebuggerView.Sources.highlightBreakpoint(where, { noEditorUpdate: true });
|
||||
}
|
||||
|
||||
// Don't display the watch expressions textbox inputs in the pane.
|
||||
DebuggerView.WatchExpressions.toggleContents(false);
|
||||
// Start recording any added variables or properties in any scope.
|
||||
|
||||
// Start recording any added variables or properties in any scope and
|
||||
// clear existing scopes to create each one dynamically.
|
||||
DebuggerView.Variables.createHierarchy();
|
||||
// Clear existing scopes and create each one dynamically.
|
||||
DebuggerView.Variables.empty();
|
||||
|
||||
// If watch expressions evaluation results are available, create a scope
|
||||
@ -870,14 +892,42 @@ StackFrames.prototype = {
|
||||
*
|
||||
* @param string aExpression
|
||||
* The expression to evaluate.
|
||||
* @param number aFrame [optional]
|
||||
* The frame depth used for evaluation.
|
||||
* @param object aOptions [optional]
|
||||
* Additional options for this client evaluation:
|
||||
* - depth: the frame depth used for evaluation, 0 being the topmost.
|
||||
* - meta: some meta-description for what this evaluation represents.
|
||||
* @return object
|
||||
* A promise that is resolved when the evaluation finishes,
|
||||
* or rejected if there was no stack frame available or some
|
||||
* other error occurred.
|
||||
*/
|
||||
evaluate: function(aExpression, aFrame = this.currentFrameDepth) {
|
||||
let frame = this.activeThread.cachedFrames[aFrame];
|
||||
if (frame) {
|
||||
this.activeThread.eval(frame.actor, aExpression);
|
||||
evaluate: function(aExpression, aOptions = {}) {
|
||||
let depth = "depth" in aOptions ? aOptions.depth : this.currentFrameDepth;
|
||||
let frame = this.activeThread.cachedFrames[depth];
|
||||
if (frame == null) {
|
||||
return promise.reject(new Error("No stack frame available."));
|
||||
}
|
||||
|
||||
let deferred = promise.defer();
|
||||
|
||||
this.activeThread.addOneTimeListener("paused", (aEvent, aPacket) => {
|
||||
let { type, frameFinished } = aPacket.why;
|
||||
if (type == "clientEvaluated") {
|
||||
if (!("terminated" in frameFinished)) {
|
||||
deferred.resolve(frameFinished);
|
||||
} else {
|
||||
deferred.reject(new Error("The execution was abruptly terminated."));
|
||||
}
|
||||
} else {
|
||||
deferred.reject(new Error("Active thread paused unexpectedly."));
|
||||
}
|
||||
});
|
||||
|
||||
let meta = "meta" in aOptions ? aOptions.meta : FRAME_TYPE.PUBLIC_CLIENT_EVAL;
|
||||
this._currentFrameDescription = meta;
|
||||
this.activeThread.eval(frame.actor, aExpression);
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -990,6 +1040,7 @@ StackFrames.prototype = {
|
||||
this._syncedWatchExpressions =
|
||||
this._currentWatchExpressions = null;
|
||||
}
|
||||
|
||||
this.currentFrameDepth = -1;
|
||||
this._onFrames();
|
||||
}
|
||||
|
@ -1265,6 +1265,259 @@ let SourceUtils = {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Functions handling the variables bubble UI.
|
||||
*/
|
||||
function VariableBubbleView() {
|
||||
dumpn("VariableBubbleView was instantiated");
|
||||
|
||||
this._onMouseMove = this._onMouseMove.bind(this);
|
||||
this._onMouseLeave = this._onMouseLeave.bind(this);
|
||||
this._onMouseScroll = this._onMouseScroll.bind(this);
|
||||
this._onPopupHiding = this._onPopupHiding.bind(this);
|
||||
}
|
||||
|
||||
VariableBubbleView.prototype = {
|
||||
/**
|
||||
* Initialization function, called when the debugger is started.
|
||||
*/
|
||||
initialize: function() {
|
||||
dumpn("Initializing the VariableBubbleView");
|
||||
|
||||
this._tooltip = new Tooltip(document);
|
||||
this._editorContainer = document.getElementById("editor");
|
||||
|
||||
this._tooltip.defaultPosition = EDITOR_VARIABLE_POPUP_POSITION;
|
||||
this._tooltip.defaultShowDelay = EDITOR_VARIABLE_HOVER_DELAY;
|
||||
|
||||
this._tooltip.panel.addEventListener("popuphiding", this._onPopupHiding);
|
||||
this._editorContainer.addEventListener("mousemove", this._onMouseMove, false);
|
||||
this._editorContainer.addEventListener("mouseleave", this._onMouseLeave, false);
|
||||
this._editorContainer.addEventListener("scroll", this._onMouseScroll, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Destruction function, called when the debugger is closed.
|
||||
*/
|
||||
destroy: function() {
|
||||
dumpn("Destroying the VariableBubbleView");
|
||||
|
||||
this._tooltip.panel.removeEventListener("popuphiding", this._onPopupHiding);
|
||||
this._editorContainer.removeEventListener("mousemove", this._onMouseMove, false);
|
||||
this._editorContainer.removeEventListener("mouseleave", this._onMouseLeave, false);
|
||||
this._editorContainer.removeEventListener("scroll", this._onMouseScroll, true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Searches for an identifier underneath the specified position in the
|
||||
* source editor, and if found, opens a VariablesView inspection popup.
|
||||
*
|
||||
* @param number x, y
|
||||
* The left/top coordinates where to look for an identifier.
|
||||
*/
|
||||
_findIdentifier: function(x, y) {
|
||||
let editor = DebuggerView.editor;
|
||||
|
||||
// Calculate the editor's line and column at the current x and y coords.
|
||||
let hoveredPos = editor.getPositionFromCoords({ left: x, top: y });
|
||||
let hoveredOffset = editor.getOffset(hoveredPos);
|
||||
let hoveredLine = hoveredPos.line;
|
||||
let hoveredColumn = hoveredPos.ch;
|
||||
|
||||
// A source contains multiple scripts. Find the start index of the script
|
||||
// containing the specified offset relative to its parent source.
|
||||
let contents = editor.getText();
|
||||
let location = DebuggerView.Sources.selectedValue;
|
||||
let parsedSource = DebuggerController.Parser.get(contents, location);
|
||||
let scriptInfo = parsedSource.getScriptInfo(hoveredOffset);
|
||||
|
||||
// If the script length is negative, we're not hovering JS source code.
|
||||
if (scriptInfo.length == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Using the script offset, determine the actual line and column inside the
|
||||
// script, to use when finding identifiers.
|
||||
let scriptStart = editor.getPosition(scriptInfo.start);
|
||||
let scriptLineOffset = scriptStart.line;
|
||||
let scriptColumnOffset = (hoveredLine == scriptStart.line ? scriptStart.ch : 0);
|
||||
|
||||
let scriptLine = hoveredLine - scriptLineOffset;
|
||||
let scriptColumn = hoveredColumn - scriptColumnOffset;
|
||||
let identifierInfo = parsedSource.getIdentifierAt(scriptLine + 1, scriptColumn);
|
||||
|
||||
// If the info is null, we're not hovering any identifier.
|
||||
if (!identifierInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Transform the line and column relative to the parsed script back
|
||||
// to the context of the parent source.
|
||||
let { start: identifierStart, end: identifierEnd } = identifierInfo.location;
|
||||
let identifierCoords = {
|
||||
line: identifierStart.line + scriptLineOffset,
|
||||
column: identifierStart.column + scriptColumnOffset,
|
||||
length: identifierEnd.column - identifierStart.column
|
||||
};
|
||||
|
||||
// Evaluate the identifier in the current stack frame and show the
|
||||
// results in a VariablesView inspection popup.
|
||||
DebuggerController.StackFrames.evaluate(identifierInfo.evalString)
|
||||
.then(frameFinished => {
|
||||
if ("return" in frameFinished) {
|
||||
this.showContents({
|
||||
coords: identifierCoords,
|
||||
evalPrefix: identifierInfo.evalString,
|
||||
objectActor: frameFinished.return
|
||||
});
|
||||
} else {
|
||||
let msg = "Evaluation has thrown for: " + identifierInfo.evalString;
|
||||
console.warn(msg);
|
||||
dumpn(msg);
|
||||
}
|
||||
})
|
||||
.then(null, err => {
|
||||
let msg = "Couldn't evaluate: " + err.message;
|
||||
console.error(msg);
|
||||
dumpn(msg);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows an inspection popup for a specified object actor grip.
|
||||
*
|
||||
* @param string object
|
||||
* An object containing the following properties:
|
||||
* - coords: the inspected identifier coordinates in the editor,
|
||||
* containing the { line, column, length } properties.
|
||||
* - evalPrefix: a prefix for the variables view evaluation macros.
|
||||
* - objectActor: the value grip for the object actor.
|
||||
*/
|
||||
showContents: function({ coords, evalPrefix, objectActor }) {
|
||||
let editor = DebuggerView.editor;
|
||||
let { line, column, length } = coords;
|
||||
|
||||
// Highlight the function found at the mouse position.
|
||||
this._markedText = editor.markText(
|
||||
{ line: line - 1, ch: column },
|
||||
{ line: line - 1, ch: column + length });
|
||||
|
||||
// If the grip represents a primitive value, use a more lightweight
|
||||
// machinery to display it.
|
||||
if (VariablesView.isPrimitive({ value: objectActor })) {
|
||||
let className = VariablesView.getClass(objectActor);
|
||||
let textContent = VariablesView.getString(objectActor);
|
||||
this._tooltip.setTextContent([textContent], className, "plain");
|
||||
} else {
|
||||
this._tooltip.setVariableContent(objectActor, {
|
||||
searchPlaceholder: L10N.getStr("emptyPropertiesFilterText"),
|
||||
searchEnabled: Prefs.variablesSearchboxVisible,
|
||||
eval: aString => {
|
||||
DebuggerController.StackFrames.evaluate(aString);
|
||||
DebuggerView.VariableBubble.hideContents();
|
||||
}
|
||||
}, {
|
||||
getEnvironmentClient: aObject => gThreadClient.environment(aObject),
|
||||
getObjectClient: aObject => gThreadClient.pauseGrip(aObject),
|
||||
simpleValueEvalMacro: this._getSimpleValueEvalMacro(evalPrefix),
|
||||
getterOrSetterEvalMacro: this._getGetterOrSetterEvalMacro(evalPrefix),
|
||||
overrideValueEvalMacro: this._getOverrideValueEvalMacro(evalPrefix)
|
||||
}, {
|
||||
fetched: (aEvent, aType) => {
|
||||
if (aType == "properties") {
|
||||
window.emit(EVENTS.FETCHED_BUBBLE_PROPERTIES);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Calculate the x, y coordinates for the variable bubble anchor.
|
||||
let identifierCenter = { line: line - 1, ch: column + length / 2 };
|
||||
let anchor = editor.getCoordsFromPosition(identifierCenter);
|
||||
|
||||
this._tooltip.defaultOffsetX = anchor.left + EDITOR_VARIABLE_POPUP_OFFSET_X;
|
||||
this._tooltip.defaultOffsetY = anchor.top + EDITOR_VARIABLE_POPUP_OFFSET_Y;
|
||||
this._tooltip.show(this._editorContainer);
|
||||
},
|
||||
|
||||
/**
|
||||
* Hides the inspection popup.
|
||||
*/
|
||||
hideContents: function() {
|
||||
clearNamedTimeout("editor-mouse-move");
|
||||
this._tooltip.hide();
|
||||
},
|
||||
|
||||
/**
|
||||
* Functions for getting customized variables view evaluation macros.
|
||||
*
|
||||
* @param string aPrefix
|
||||
* See the corresponding VariablesView.* functions.
|
||||
*/
|
||||
_getSimpleValueEvalMacro: function(aPrefix) {
|
||||
return (item, string) =>
|
||||
VariablesView.simpleValueEvalMacro(item, string, aPrefix);
|
||||
},
|
||||
_getGetterOrSetterEvalMacro: function(aPrefix) {
|
||||
return (item, string) =>
|
||||
VariablesView.getterOrSetterEvalMacro(item, string, aPrefix);
|
||||
},
|
||||
_getOverrideValueEvalMacro: function(aPrefix) {
|
||||
return (item, string) =>
|
||||
VariablesView.overrideValueEvalMacro(item, string, aPrefix);
|
||||
},
|
||||
|
||||
/**
|
||||
* The mousemove listener for the source editor.
|
||||
*/
|
||||
_onMouseMove: function({ clientX: x, clientY: y }) {
|
||||
// Prevent the variable inspection popup from showing when the thread client
|
||||
// is not paused, or while a popup is already visible.
|
||||
if (gThreadClient && gThreadClient.state != "paused" || !this._tooltip.isHidden()) {
|
||||
clearNamedTimeout("editor-mouse-move");
|
||||
return;
|
||||
}
|
||||
// Allow events to settle down first. If the mouse hovers over
|
||||
// a certain point in the editor long enough, try showing a variable bubble.
|
||||
setNamedTimeout("editor-mouse-move",
|
||||
EDITOR_VARIABLE_HOVER_DELAY, () => this._findIdentifier(x, y));
|
||||
},
|
||||
|
||||
/**
|
||||
* The mouseleave listener for the source editor container node.
|
||||
*/
|
||||
_onMouseLeave: function() {
|
||||
clearNamedTimeout("editor-mouse-move");
|
||||
},
|
||||
|
||||
/**
|
||||
* The mousescroll listener for the source editor container node.
|
||||
*/
|
||||
_onMouseScroll: function() {
|
||||
this.hideContents();
|
||||
},
|
||||
|
||||
/**
|
||||
* Listener handling the popup hiding event.
|
||||
*/
|
||||
_onPopupHiding: function({ target }) {
|
||||
if (this._tooltip.panel != target) {
|
||||
return;
|
||||
}
|
||||
if (this._markedText) {
|
||||
this._markedText.clear();
|
||||
this._markedText = null;
|
||||
}
|
||||
if (!this._tooltip.isEmpty()) {
|
||||
this._tooltip.empty();
|
||||
}
|
||||
},
|
||||
|
||||
_editorContainer: null,
|
||||
_markedText: null,
|
||||
_tooltip: null
|
||||
};
|
||||
|
||||
/**
|
||||
* Functions handling the watch expressions UI.
|
||||
*/
|
||||
@ -2518,6 +2771,7 @@ LineResults.size = function() {
|
||||
* Preliminary setup for the DebuggerView object.
|
||||
*/
|
||||
DebuggerView.Sources = new SourcesView();
|
||||
DebuggerView.VariableBubble = new VariableBubbleView();
|
||||
DebuggerView.WatchExpressions = new WatchExpressionsView();
|
||||
DebuggerView.EventListeners = new EventListenersView();
|
||||
DebuggerView.GlobalSearch = new GlobalSearchView();
|
||||
|
@ -136,6 +136,7 @@ ToolbarView.prototype = {
|
||||
_onResumePressed: function() {
|
||||
if (DebuggerController.activeThread.paused) {
|
||||
let warn = DebuggerController._ensureResumptionOrder;
|
||||
DebuggerController.StackFrames.currentFrameDepth = -1;
|
||||
DebuggerController.activeThread.resume(warn);
|
||||
} else {
|
||||
DebuggerController.activeThread.interrupt();
|
||||
@ -147,6 +148,7 @@ ToolbarView.prototype = {
|
||||
*/
|
||||
_onStepOverPressed: function() {
|
||||
if (DebuggerController.activeThread.paused) {
|
||||
DebuggerController.StackFrames.currentFrameDepth = -1;
|
||||
DebuggerController.activeThread.stepOver();
|
||||
}
|
||||
},
|
||||
@ -156,6 +158,7 @@ ToolbarView.prototype = {
|
||||
*/
|
||||
_onStepInPressed: function() {
|
||||
if (DebuggerController.activeThread.paused) {
|
||||
DebuggerController.StackFrames.currentFrameDepth = -1;
|
||||
DebuggerController.activeThread.stepIn();
|
||||
}
|
||||
},
|
||||
@ -165,6 +168,7 @@ ToolbarView.prototype = {
|
||||
*/
|
||||
_onStepOutPressed: function() {
|
||||
if (DebuggerController.activeThread.paused) {
|
||||
DebuggerController.StackFrames.currentFrameDepth = -1;
|
||||
DebuggerController.activeThread.stepOut();
|
||||
}
|
||||
},
|
||||
@ -477,6 +481,17 @@ StackFramesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
this.selectedItem = aItem => aItem.attachment.depth == aDepth;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the currently selected stack frame's depth in this container.
|
||||
* This will essentially be the opposite of |selectedIndex|, which deals
|
||||
* with the position in the view, where the last item added is actually
|
||||
* the bottommost, not topmost.
|
||||
* @return number
|
||||
*/
|
||||
get selectedDepth() {
|
||||
return this.selectedItem.attachment.depth;
|
||||
},
|
||||
|
||||
/**
|
||||
* Specifies if the active thread has more frames that need to be loaded.
|
||||
*/
|
||||
@ -1384,8 +1399,8 @@ FilteredFunctionsView.prototype = Heritage.extend(ResultsPanelContainer.prototyp
|
||||
}
|
||||
|
||||
for (let [location, contents] of aSources) {
|
||||
let parserMethods = DebuggerController.Parser.get(location, contents);
|
||||
let sourceResults = parserMethods.getNamedFunctionDefinitions(aToken);
|
||||
let parsedSource = DebuggerController.Parser.get(contents, location);
|
||||
let sourceResults = parsedSource.getNamedFunctionDefinitions(aToken);
|
||||
|
||||
for (let scriptResult of sourceResults) {
|
||||
for (let parseResult of scriptResult.parseResults) {
|
||||
|
@ -28,6 +28,10 @@ const SEARCH_FUNCTION_FLAG = "@";
|
||||
const SEARCH_TOKEN_FLAG = "#";
|
||||
const SEARCH_LINE_FLAG = ":";
|
||||
const SEARCH_VARIABLE_FLAG = "*";
|
||||
const EDITOR_VARIABLE_HOVER_DELAY = 350; // ms
|
||||
const EDITOR_VARIABLE_POPUP_OFFSET_X = 5; // px
|
||||
const EDITOR_VARIABLE_POPUP_OFFSET_Y = 0; // px
|
||||
const EDITOR_VARIABLE_POPUP_POSITION = "before_start";
|
||||
|
||||
/**
|
||||
* Object defining the debugger view components.
|
||||
@ -56,6 +60,7 @@ let DebuggerView = {
|
||||
this.ChromeGlobals.initialize();
|
||||
this.StackFrames.initialize();
|
||||
this.Sources.initialize();
|
||||
this.VariableBubble.initialize();
|
||||
this.WatchExpressions.initialize();
|
||||
this.EventListeners.initialize();
|
||||
this.GlobalSearch.initialize();
|
||||
@ -89,6 +94,7 @@ let DebuggerView = {
|
||||
this.ChromeGlobals.destroy();
|
||||
this.StackFrames.destroy();
|
||||
this.Sources.destroy();
|
||||
this.VariableBubble.destroy();
|
||||
this.WatchExpressions.destroy();
|
||||
this.EventListeners.destroy();
|
||||
this.GlobalSearch.destroy();
|
||||
@ -301,6 +307,7 @@ let DebuggerView = {
|
||||
_setEditorText: function(aTextContent = "") {
|
||||
this.editor.setMode(Editor.modes.text);
|
||||
this.editor.setText(aTextContent);
|
||||
this.editor.clearDebugLocation();
|
||||
this.editor.clearHistory();
|
||||
},
|
||||
|
||||
@ -416,8 +423,10 @@ let DebuggerView = {
|
||||
* - columnOffset: column offset for the caret or debug location
|
||||
* - noCaret: don't set the caret location at the specified line
|
||||
* - noDebug: don't set the debug location at the specified line
|
||||
* - align: string specifying whether to align the specified line
|
||||
* at the "top", "center" or "bottom" of the editor
|
||||
* - force: boolean allowing whether we can get the selected url's
|
||||
* text again.
|
||||
* text again
|
||||
* @return object
|
||||
* A promise that is resolved after the source text has been set.
|
||||
*/
|
||||
@ -426,6 +435,7 @@ let DebuggerView = {
|
||||
if (!this.Sources.containsValue(aUrl)) {
|
||||
return promise.reject(new Error("Unknown source for the specified URL."));
|
||||
}
|
||||
|
||||
// If the line is not specified, default to the current frame's position,
|
||||
// if available and the frame's url corresponds to the requested url.
|
||||
if (!aLine) {
|
||||
@ -440,11 +450,6 @@ let DebuggerView = {
|
||||
let sourceItem = this.Sources.getItemByValue(aUrl);
|
||||
let sourceForm = sourceItem.attachment.source;
|
||||
|
||||
// Once we change the editor location, it replaces editor's contents.
|
||||
// This means that the debug location information is now obsolete, so
|
||||
// we need to clear it. We set a new location below, in this function.
|
||||
this.editor.clearDebugLocation();
|
||||
|
||||
// Make sure the requested source client is shown in the editor, then
|
||||
// update the source editor's caret position and debug location.
|
||||
return this._setEditorSource(sourceForm, aFlags).then(() => {
|
||||
@ -453,20 +458,16 @@ let DebuggerView = {
|
||||
if (aLine < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aFlags.charOffset) {
|
||||
aLine += this.editor.getPosition(aFlags.charOffset).line;
|
||||
}
|
||||
|
||||
if (aFlags.lineOffset) {
|
||||
aLine += aFlags.lineOffset;
|
||||
}
|
||||
|
||||
if (!aFlags.noCaret) {
|
||||
this.editor.setCursor({ line: aLine -1, ch: aFlags.columnOffset || 0 },
|
||||
aFlags.align);
|
||||
let location = { line: aLine -1, ch: aFlags.columnOffset || 0 };
|
||||
this.editor.setCursor(location, aFlags.align);
|
||||
}
|
||||
|
||||
if (!aFlags.noDebug) {
|
||||
this.editor.setDebugLocation(aLine - 1);
|
||||
}
|
||||
@ -637,6 +638,7 @@ let DebuggerView = {
|
||||
StackFrames: null,
|
||||
Sources: null,
|
||||
Variables: null,
|
||||
VariableBubble: null,
|
||||
WatchExpressions: null,
|
||||
EventListeners: null,
|
||||
editor: null,
|
||||
|
@ -50,6 +50,7 @@ support-files =
|
||||
doc_pretty-print-2.html
|
||||
doc_random-javascript.html
|
||||
doc_recursion-stack.html
|
||||
doc_scope-variable.html
|
||||
doc_script-switching-01.html
|
||||
doc_script-switching-02.html
|
||||
doc_step-out.html
|
||||
@ -67,7 +68,6 @@ support-files =
|
||||
[browser_dbg_blackboxing-04.js]
|
||||
[browser_dbg_blackboxing-05.js]
|
||||
[browser_dbg_blackboxing-06.js]
|
||||
[browser_dbg_file-reload.js]
|
||||
[browser_dbg_breadcrumbs-access.js]
|
||||
[browser_dbg_break-on-dom-01.js]
|
||||
[browser_dbg_break-on-dom-02.js]
|
||||
@ -94,10 +94,13 @@ skip-if = true
|
||||
[browser_dbg_cmd-dbg.js]
|
||||
[browser_dbg_conditional-breakpoints-01.js]
|
||||
[browser_dbg_conditional-breakpoints-02.js]
|
||||
[browser_dbg_controller-evaluate-01.js]
|
||||
[browser_dbg_controller-evaluate-02.js]
|
||||
[browser_dbg_debugger-statement.js]
|
||||
[browser_dbg_editor-contextmenu.js]
|
||||
[browser_dbg_editor-mode.js]
|
||||
[browser_dbg_event-listeners.js]
|
||||
[browser_dbg_file-reload.js]
|
||||
[browser_dbg_function-display-name.js]
|
||||
[browser_dbg_globalactor.js]
|
||||
[browser_dbg_host-layout.js]
|
||||
@ -114,6 +117,16 @@ skip-if = true
|
||||
[browser_dbg_navigation.js]
|
||||
[browser_dbg_on-pause-highlight.js]
|
||||
[browser_dbg_panel-size.js]
|
||||
[browser_dbg_parser-01.js]
|
||||
[browser_dbg_parser-02.js]
|
||||
[browser_dbg_parser-03.js]
|
||||
[browser_dbg_parser-04.js]
|
||||
[browser_dbg_parser-05.js]
|
||||
[browser_dbg_parser-06.js]
|
||||
[browser_dbg_parser-07.js]
|
||||
[browser_dbg_parser-08.js]
|
||||
[browser_dbg_parser-09.js]
|
||||
[browser_dbg_parser-10.js]
|
||||
[browser_dbg_pause-exceptions-01.js]
|
||||
[browser_dbg_pause-exceptions-02.js]
|
||||
[browser_dbg_pause-resume.js]
|
||||
@ -168,6 +181,7 @@ skip-if = true
|
||||
[browser_dbg_stack-04.js]
|
||||
[browser_dbg_stack-05.js]
|
||||
[browser_dbg_stack-06.js]
|
||||
[browser_dbg_stack-07.js]
|
||||
[browser_dbg_step-out.js]
|
||||
[browser_dbg_tabactor-01.js]
|
||||
[browser_dbg_tabactor-02.js]
|
||||
@ -196,6 +210,14 @@ skip-if = true
|
||||
[browser_dbg_variables-view-frozen-sealed-nonext.js]
|
||||
[browser_dbg_variables-view-hide-non-enums.js]
|
||||
[browser_dbg_variables-view-large-array-buffer.js]
|
||||
[browser_dbg_variables-view-popup-01.js]
|
||||
[browser_dbg_variables-view-popup-02.js]
|
||||
[browser_dbg_variables-view-popup-03.js]
|
||||
[browser_dbg_variables-view-popup-04.js]
|
||||
[browser_dbg_variables-view-popup-05.js]
|
||||
[browser_dbg_variables-view-popup-06.js]
|
||||
[browser_dbg_variables-view-popup-07.js]
|
||||
[browser_dbg_variables-view-popup-08.js]
|
||||
[browser_dbg_variables-view-reexpand-01.js]
|
||||
[browser_dbg_variables-view-reexpand-02.js]
|
||||
[browser_dbg_variables-view-webidl.js]
|
||||
|
@ -242,7 +242,7 @@ function test() {
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
|
||||
let coords = gEditor.cursorCoords({ line: 4, ch: 0 });
|
||||
let coords = gEditor.getCoordsFromPosition({ line: 4, ch: 0 });
|
||||
let rect = iframe.getBoundingClientRect();
|
||||
let left = rect.left + 10;
|
||||
let top = rect.top + coords.top + 4;
|
||||
|
@ -0,0 +1,92 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests the public evaluation API from the debugger controller.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
|
||||
|
||||
function test() {
|
||||
Task.spawn(function() {
|
||||
let [tab, debuggee, panel] = yield initDebugger(TAB_URL);
|
||||
let win = panel.panelWin;
|
||||
let frames = win.DebuggerController.StackFrames;
|
||||
let framesView = win.DebuggerView.StackFrames;
|
||||
let sources = win.DebuggerController.SourceScripts;
|
||||
let sourcesView = win.DebuggerView.Sources;
|
||||
let editorView = win.DebuggerView.editor;
|
||||
let events = win.EVENTS;
|
||||
|
||||
function checkView(frameDepth, selectedSource, caretLine, editorText) {
|
||||
is(win.gThreadClient.state, "paused",
|
||||
"Should only be getting stack frames while paused.");
|
||||
is(framesView.itemCount, 4,
|
||||
"Should have four frames.");
|
||||
is(framesView.selectedDepth, frameDepth,
|
||||
"The correct frame is selected in the widget.");
|
||||
is(sourcesView.selectedIndex, selectedSource,
|
||||
"The correct source is selected in the widget.");
|
||||
ok(isCaretPos(panel, caretLine),
|
||||
"Editor caret location is correct.");
|
||||
is(editorView.getText().search(editorText[0]), editorText[1],
|
||||
"The correct source is not displayed.");
|
||||
}
|
||||
|
||||
// Cache the sources text to avoid having to wait for their retrieval.
|
||||
yield promise.all(sourcesView.attachments.map(e => sources.getText(e.source)));
|
||||
is(sources._cache.size, 2, "There should be two cached sources in the cache.");
|
||||
|
||||
// Eval while not paused.
|
||||
try {
|
||||
yield frames.evaluate("foo");
|
||||
} catch (error) {
|
||||
is(error.message, "No stack frame available.",
|
||||
"Evaluating shouldn't work while the debuggee isn't paused.");
|
||||
}
|
||||
|
||||
// Allow this generator function to yield first.
|
||||
executeSoon(() => debuggee.firstCall());
|
||||
yield waitForSourceAndCaretAndScopes(panel, "-02.js", 6);
|
||||
checkView(0, 1, 6, [/secondCall/, 118]);
|
||||
|
||||
// Eval in the topmost frame, while paused.
|
||||
let updatedView = waitForDebuggerEvents(panel, events.FETCHED_SCOPES);
|
||||
let result = yield frames.evaluate("foo");
|
||||
ok(!result.throw, "The evaluation hasn't thrown.");
|
||||
is(result.return.type, "object", "The evaluation return type is correct.");
|
||||
is(result.return.class, "Function", "The evaluation return class is correct.");
|
||||
|
||||
yield updatedView;
|
||||
checkView(0, 1, 6, [/secondCall/, 118]);
|
||||
ok(true, "Evaluating in the topmost frame works properly.");
|
||||
|
||||
// Eval in a different frame, while paused.
|
||||
let updatedView = waitForDebuggerEvents(panel, events.FETCHED_SCOPES);
|
||||
try {
|
||||
yield frames.evaluate("foo", { depth: 3 }); // oldest frame
|
||||
} catch (result) {
|
||||
is(result.return.type, "object", "The evaluation thrown type is correct.");
|
||||
is(result.return.class, "Error", "The evaluation thrown class is correct.");
|
||||
ok(!result.return, "The evaluation hasn't returned.");
|
||||
}
|
||||
|
||||
yield updatedView;
|
||||
checkView(0, 1, 6, [/secondCall/, 118]);
|
||||
ok(true, "Evaluating in a custom frame works properly.");
|
||||
|
||||
// Eval in a non-existent frame, while paused.
|
||||
waitForDebuggerEvents(panel, events.FETCHED_SCOPES).then(() => {
|
||||
ok(false, "Shouldn't have updated the view when trying to evaluate " +
|
||||
"an expression in a non-existent stack frame.");
|
||||
});
|
||||
try {
|
||||
yield frames.evaluate("foo", { depth: 4 }); // non-existent frame
|
||||
} catch (error) {
|
||||
is(error.message, "No stack frame available.",
|
||||
"Evaluating shouldn't work if the specified frame doesn't exist.");
|
||||
}
|
||||
|
||||
yield resumeDebuggerThenCloseAndFinish(panel);
|
||||
});
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests the public evaluation API from the debugger controller.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
|
||||
|
||||
function test() {
|
||||
Task.spawn(function() {
|
||||
let [tab, debuggee, panel] = yield initDebugger(TAB_URL);
|
||||
let win = panel.panelWin;
|
||||
let frames = win.DebuggerController.StackFrames;
|
||||
let framesView = win.DebuggerView.StackFrames;
|
||||
let sources = win.DebuggerController.SourceScripts;
|
||||
let sourcesView = win.DebuggerView.Sources;
|
||||
let editorView = win.DebuggerView.editor;
|
||||
let events = win.EVENTS;
|
||||
|
||||
function checkView(selectedFrame, selectedSource, caretLine, editorText) {
|
||||
is(win.gThreadClient.state, "paused",
|
||||
"Should only be getting stack frames while paused.");
|
||||
is(framesView.itemCount, 4,
|
||||
"Should have four frames.");
|
||||
is(framesView.selectedDepth, selectedFrame,
|
||||
"The correct frame is selected in the widget.");
|
||||
is(sourcesView.selectedIndex, selectedSource,
|
||||
"The correct source is selected in the widget.");
|
||||
ok(isCaretPos(panel, caretLine),
|
||||
"Editor caret location is correct.");
|
||||
is(editorView.getText().search(editorText[0]), editorText[1],
|
||||
"The correct source is not displayed.");
|
||||
}
|
||||
|
||||
// Cache the sources text to avoid having to wait for their retrieval.
|
||||
yield promise.all(sourcesView.attachments.map(e => sources.getText(e.source)));
|
||||
is(sources._cache.size, 2, "There should be two cached sources in the cache.");
|
||||
|
||||
// Allow this generator function to yield first.
|
||||
executeSoon(() => debuggee.firstCall());
|
||||
yield waitForSourceAndCaretAndScopes(panel, "-02.js", 6);
|
||||
checkView(0, 1, 6, [/secondCall/, 118]);
|
||||
|
||||
// Change the selected frame and eval inside it.
|
||||
let updatedFrame = waitForDebuggerEvents(panel, events.FETCHED_SCOPES);
|
||||
framesView.selectedDepth = 3; // oldest frame
|
||||
yield updatedFrame;
|
||||
checkView(3, 0, 5, [/firstCall/, 118]);
|
||||
|
||||
let updatedView = waitForDebuggerEvents(panel, events.FETCHED_SCOPES);
|
||||
try {
|
||||
yield frames.evaluate("foo");
|
||||
} catch (result) {
|
||||
is(result.return.type, "object", "The evaluation thrown type is correct.");
|
||||
is(result.return.class, "Error", "The evaluation thrown class is correct.");
|
||||
ok(!result.return, "The evaluation hasn't returned.");
|
||||
}
|
||||
|
||||
yield updatedView;
|
||||
checkView(3, 0, 5, [/firstCall/, 118]);
|
||||
ok(true, "Evaluating while in a user-selected frame works properly.");
|
||||
|
||||
yield resumeDebuggerThenCloseAndFinish(panel);
|
||||
});
|
||||
}
|
31
browser/devtools/debugger/test/browser_dbg_parser-01.js
Normal file
31
browser/devtools/debugger/test/browser_dbg_parser-01.js
Normal file
@ -0,0 +1,31 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Check that simple JS can be parsed and cached with the reflection API.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
let { Parser } = Cu.import("resource:///modules/devtools/Parser.jsm", {});
|
||||
|
||||
let source = "let x = 42;";
|
||||
let parser = new Parser();
|
||||
let first = parser.get(source);
|
||||
let second = parser.get(source);
|
||||
|
||||
isnot(first, second,
|
||||
"The two syntax trees should be different.");
|
||||
|
||||
let third = parser.get(source, "url");
|
||||
let fourth = parser.get(source, "url");
|
||||
|
||||
isnot(first, third,
|
||||
"The new syntax trees should be different than the old ones.");
|
||||
is(third, fourth,
|
||||
"The new syntax trees were cached once an identifier was specified.");
|
||||
|
||||
is(parser.errors.length, 0,
|
||||
"There should be no errors logged when parsing.");
|
||||
|
||||
finish();
|
||||
}
|
26
browser/devtools/debugger/test/browser_dbg_parser-02.js
Normal file
26
browser/devtools/debugger/test/browser_dbg_parser-02.js
Normal file
@ -0,0 +1,26 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Check that syntax errors are reported correctly.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
let { Parser } = Cu.import("resource:///modules/devtools/Parser.jsm", {});
|
||||
|
||||
let source = "let x + 42;";
|
||||
let parser = new Parser();
|
||||
let parsed = parser.get(source);
|
||||
|
||||
ok(parsed,
|
||||
"An object should be returned even though the source had a syntax error.");
|
||||
|
||||
is(parser.errors.length, 1,
|
||||
"There should be one error logged when parsing.");
|
||||
is(parser.errors[0].name, "SyntaxError",
|
||||
"The correct exception was caught.");
|
||||
is(parser.errors[0].message, "missing ; before statement",
|
||||
"The correct exception was caught.");
|
||||
|
||||
finish();
|
||||
}
|
77
browser/devtools/debugger/test/browser_dbg_parser-03.js
Normal file
77
browser/devtools/debugger/test/browser_dbg_parser-03.js
Normal file
@ -0,0 +1,77 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Check that JS inside HTML can be separated and parsed correctly.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
let { Parser } = Cu.import("resource:///modules/devtools/Parser.jsm", {});
|
||||
|
||||
let source = [
|
||||
"<!doctype html>",
|
||||
"<head>",
|
||||
"<script>",
|
||||
"let a = 42;",
|
||||
"</script>",
|
||||
"<script type='text/javascript'>",
|
||||
"let b = 42;",
|
||||
"</script>",
|
||||
"<script type='text/javascript;version=1.8'>",
|
||||
"let c = 42;",
|
||||
"</script>",
|
||||
"</head>"
|
||||
].join("\n");
|
||||
let parser = new Parser();
|
||||
let parsed = parser.get(source);
|
||||
|
||||
ok(parsed,
|
||||
"HTML code should be parsed correctly.");
|
||||
is(parser.errors.length, 0,
|
||||
"There should be no errors logged when parsing.");
|
||||
|
||||
is(parsed.scriptCount, 3,
|
||||
"There should be 3 scripts parsed in the parent HTML source.");
|
||||
|
||||
is(parsed.getScriptInfo(0).toSource(), "({start:-1, length:-1})",
|
||||
"There is no script at the beginning of the parent source.");
|
||||
is(parsed.getScriptInfo(source.length - 1).toSource(), "({start:-1, length:-1})",
|
||||
"There is no script at the end of the parent source.");
|
||||
|
||||
is(parsed.getScriptInfo(source.indexOf("let a")).toSource(), "({start:31, length:13})",
|
||||
"The first script was located correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let b")).toSource(), "({start:85, length:13})",
|
||||
"The second script was located correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let c")).toSource(), "({start:151, length:13})",
|
||||
"The third script was located correctly.");
|
||||
|
||||
is(parsed.getScriptInfo(source.indexOf("let a") - 1).toSource(), "({start:31, length:13})",
|
||||
"The left edge of the first script was interpreted correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let b") - 1).toSource(), "({start:85, length:13})",
|
||||
"The left edge of the second script was interpreted correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let c") - 1).toSource(), "({start:151, length:13})",
|
||||
"The left edge of the third script was interpreted correctly.");
|
||||
|
||||
is(parsed.getScriptInfo(source.indexOf("let a") - 2).toSource(), "({start:-1, length:-1})",
|
||||
"The left outside of the first script was interpreted correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let b") - 2).toSource(), "({start:-1, length:-1})",
|
||||
"The left outside of the second script was interpreted correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let c") - 2).toSource(), "({start:-1, length:-1})",
|
||||
"The left outside of the third script was interpreted correctly.");
|
||||
|
||||
is(parsed.getScriptInfo(source.indexOf("let a") + 12).toSource(), "({start:31, length:13})",
|
||||
"The right edge of the first script was interpreted correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let b") + 12).toSource(), "({start:85, length:13})",
|
||||
"The right edge of the second script was interpreted correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let c") + 12).toSource(), "({start:151, length:13})",
|
||||
"The right edge of the third script was interpreted correctly.");
|
||||
|
||||
is(parsed.getScriptInfo(source.indexOf("let a") + 13).toSource(), "({start:-1, length:-1})",
|
||||
"The right outside of the first script was interpreted correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let b") + 13).toSource(), "({start:-1, length:-1})",
|
||||
"The right outside of the second script was interpreted correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let c") + 13).toSource(), "({start:-1, length:-1})",
|
||||
"The right outside of the third script was interpreted correctly.");
|
||||
|
||||
finish();
|
||||
}
|
54
browser/devtools/debugger/test/browser_dbg_parser-04.js
Normal file
54
browser/devtools/debugger/test/browser_dbg_parser-04.js
Normal file
@ -0,0 +1,54 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Check that faulty JS inside HTML can be separated and identified correctly.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
let { Parser } = Cu.import("resource:///modules/devtools/Parser.jsm", {});
|
||||
|
||||
let source = [
|
||||
"<!doctype html>",
|
||||
"<head>",
|
||||
"<SCRIPT>",
|
||||
"let a + 42;",
|
||||
"</SCRIPT>",
|
||||
"<script type='text/javascript'>",
|
||||
"let b = 42;",
|
||||
"</SCRIPT>",
|
||||
"<script type='text/javascript;version=1.8'>",
|
||||
"let c + 42;",
|
||||
"</SCRIPT>",
|
||||
"</head>"
|
||||
].join("\n");
|
||||
let parser = new Parser();
|
||||
let parsed = parser.get(source);
|
||||
|
||||
ok(parsed,
|
||||
"HTML code should be parsed correctly.");
|
||||
is(parser.errors.length, 2,
|
||||
"There should be two errors logged when parsing.");
|
||||
|
||||
is(parser.errors[0].name, "SyntaxError",
|
||||
"The correct first exception was caught.");
|
||||
is(parser.errors[0].message, "missing ; before statement",
|
||||
"The correct first exception was caught.");
|
||||
|
||||
is(parser.errors[1].name, "SyntaxError",
|
||||
"The correct second exception was caught.");
|
||||
is(parser.errors[1].message, "missing ; before statement",
|
||||
"The correct second exception was caught.");
|
||||
|
||||
is(parsed.scriptCount, 1,
|
||||
"There should be 1 script parsed in the parent HTML source.");
|
||||
|
||||
is(parsed.getScriptInfo(source.indexOf("let a")).toSource(), "({start:-1, length:-1})",
|
||||
"The first script shouldn't be considered valid.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let b")).toSource(), "({start:85, length:13})",
|
||||
"The second script was located correctly.");
|
||||
is(parsed.getScriptInfo(source.indexOf("let c")).toSource(), "({start:-1, length:-1})",
|
||||
"The third script shouldn't be considered valid.");
|
||||
|
||||
finish();
|
||||
}
|
43
browser/devtools/debugger/test/browser_dbg_parser-05.js
Normal file
43
browser/devtools/debugger/test/browser_dbg_parser-05.js
Normal file
@ -0,0 +1,43 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Check that JS code containing strings that might look like <script> tags
|
||||
* inside an HTML source is parsed correctly.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
let { Parser } = Cu.import("resource:///modules/devtools/Parser.jsm", {});
|
||||
|
||||
let source = [
|
||||
"let a = [];",
|
||||
"a.push('<script>');",
|
||||
"a.push('var a = 42;');",
|
||||
"a.push('</script>');",
|
||||
"a.push('<script type=\"text/javascript\">');",
|
||||
"a.push('var b = 42;');",
|
||||
"a.push('</script>');",
|
||||
"a.push('<script type=\"text/javascript;version=1.8\">');",
|
||||
"a.push('var c = 42;');",
|
||||
"a.push('</script>');"
|
||||
].join("\n");
|
||||
let parser = new Parser();
|
||||
let parsed = parser.get(source);
|
||||
|
||||
ok(parsed,
|
||||
"The javascript code should be parsed correctly.");
|
||||
is(parser.errors.length, 0,
|
||||
"There should be no errors logged when parsing.");
|
||||
|
||||
is(parsed.scriptCount, 1,
|
||||
"There should be 1 script parsed in the parent source.");
|
||||
|
||||
is(parsed.getScriptInfo(source.indexOf("let a")).toSource(), "({start:0, length:261})",
|
||||
"The script location is correct (1).");
|
||||
is(parsed.getScriptInfo(source.indexOf("<script>")).toSource(), "({start:0, length:261})",
|
||||
"The script location is correct (2).");
|
||||
is(parsed.getScriptInfo(source.indexOf("</script>")).toSource(), "({start:0, length:261})",
|
||||
"The script location is correct (3).");
|
||||
|
||||
finish();
|
||||
}
|
64
browser/devtools/debugger/test/browser_dbg_parser-06.js
Normal file
64
browser/devtools/debugger/test/browser_dbg_parser-06.js
Normal file
@ -0,0 +1,64 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Check that some potentially problematic identifier nodes have the
|
||||
* right location information attached.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
let { Parser, ParserHelpers, SyntaxTreeVisitor } =
|
||||
Cu.import("resource:///modules/devtools/Parser.jsm", {});
|
||||
|
||||
function verify(source, predicate, [sline, scol], [eline, ecol]) {
|
||||
let ast = Parser.reflectionAPI.parse(source);
|
||||
let node = SyntaxTreeVisitor.filter(ast, predicate).pop();
|
||||
let loc = ParserHelpers.getNodeLocation(node);
|
||||
|
||||
is(loc.start.toSource(), { line: sline, column: scol }.toSource(),
|
||||
"The start location was correct for the identifier in: '" + source + "'.");
|
||||
is(loc.end.toSource(), { line: eline, column: ecol }.toSource(),
|
||||
"The end location was correct for the identifier in: '" + source + "'.");
|
||||
}
|
||||
|
||||
// FunctionDeclarations and FunctionExpressions.
|
||||
|
||||
// The location is unavailable for the identifier node "foo".
|
||||
verify("function foo(){}", e => e.name == "foo", [1, 9], [1, 12]);
|
||||
verify("\nfunction\nfoo\n(\n)\n{\n}\n", e => e.name == "foo", [3, 0], [3, 3]);
|
||||
|
||||
verify("({bar:function foo(){}})", e => e.name == "foo", [1, 15], [1, 18]);
|
||||
verify("(\n{\nbar\n:\nfunction\nfoo\n(\n)\n{\n}\n}\n)", e => e.name == "foo", [6, 0], [6, 3]);
|
||||
|
||||
// Just to be sure, check the identifier node "bar" as well.
|
||||
verify("({bar:function foo(){}})", e => e.name == "bar", [1, 2], [1, 5]);
|
||||
verify("(\n{\nbar\n:\nfunction\nfoo\n(\n)\n{\n}\n}\n)", e => e.name == "bar", [3, 0], [3, 3]);
|
||||
|
||||
// MemberExpressions.
|
||||
|
||||
// The location is unavailable for the identifier node "bar".
|
||||
verify("foo.bar", e => e.name == "bar", [1, 4], [1, 7]);
|
||||
verify("\nfoo\n.\nbar\n", e => e.name == "bar", [4, 0], [4, 3]);
|
||||
|
||||
// Just to be sure, check the identifier node "foo" as well.
|
||||
verify("foo.bar", e => e.name == "foo", [1, 0], [1, 3]);
|
||||
verify("\nfoo\n.\nbar\n", e => e.name == "foo", [2, 0], [2, 3]);
|
||||
|
||||
// VariableDeclarator
|
||||
|
||||
// The location is incorrect for the identifier node "foo".
|
||||
verify("let foo = bar", e => e.name == "foo", [1, 4], [1, 7]);
|
||||
verify("\nlet\nfoo\n=\nbar\n", e => e.name == "foo", [3, 0], [3, 3]);
|
||||
|
||||
// Just to be sure, check the identifier node "bar" as well.
|
||||
verify("let foo = bar", e => e.name == "bar", [1, 10], [1, 13]);
|
||||
verify("\nlet\nfoo\n=\nbar\n", e => e.name == "bar", [5, 0], [5, 3]);
|
||||
|
||||
// Just to be sure, check AssignmentExpreesions as well.
|
||||
verify("foo = bar", e => e.name == "foo", [1, 0], [1, 3]);
|
||||
verify("\nfoo\n=\nbar\n", e => e.name == "foo", [2, 0], [2, 3]);
|
||||
verify("foo = bar", e => e.name == "bar", [1, 6], [1, 9]);
|
||||
verify("\nfoo\n=\nbar\n", e => e.name == "bar", [4, 0], [4, 3]);
|
||||
|
||||
finish();
|
||||
}
|
55
browser/devtools/debugger/test/browser_dbg_parser-07.js
Normal file
55
browser/devtools/debugger/test/browser_dbg_parser-07.js
Normal file
@ -0,0 +1,55 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Check that nodes with locaiton information attached can be properly
|
||||
* verified for containing lines and columns.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
let { ParserHelpers } = Cu.import("resource:///modules/devtools/Parser.jsm", {});
|
||||
|
||||
let node1 = { loc: {
|
||||
start: { line: 1, column: 10 },
|
||||
end: { line: 10, column: 1 }
|
||||
}};
|
||||
let node2 = { loc: {
|
||||
start: { line: 1, column: 10 },
|
||||
end: { line: 1, column: 20 }
|
||||
}};
|
||||
|
||||
ok(ParserHelpers.nodeContainsLine(node1, 1), "1st check.");
|
||||
ok(ParserHelpers.nodeContainsLine(node1, 5), "2nd check.");
|
||||
ok(ParserHelpers.nodeContainsLine(node1, 10), "3rd check.");
|
||||
|
||||
ok(!ParserHelpers.nodeContainsLine(node1, 0), "4th check.");
|
||||
ok(!ParserHelpers.nodeContainsLine(node1, 11), "5th check.");
|
||||
|
||||
ok(ParserHelpers.nodeContainsLine(node2, 1), "6th check.");
|
||||
ok(!ParserHelpers.nodeContainsLine(node2, 0), "7th check.");
|
||||
ok(!ParserHelpers.nodeContainsLine(node2, 2), "8th check.");
|
||||
|
||||
ok(!ParserHelpers.nodeContainsPoint(node1, 1, 10), "9th check.");
|
||||
ok(!ParserHelpers.nodeContainsPoint(node1, 10, 1), "10th check.");
|
||||
|
||||
ok(!ParserHelpers.nodeContainsPoint(node1, 0, 10), "11th check.");
|
||||
ok(!ParserHelpers.nodeContainsPoint(node1, 11, 1), "12th check.");
|
||||
|
||||
ok(!ParserHelpers.nodeContainsPoint(node1, 1, 9), "13th check.");
|
||||
ok(!ParserHelpers.nodeContainsPoint(node1, 10, 2), "14th check.");
|
||||
|
||||
ok(ParserHelpers.nodeContainsPoint(node2, 1, 10), "15th check.");
|
||||
ok(ParserHelpers.nodeContainsPoint(node2, 1, 15), "16th check.");
|
||||
ok(ParserHelpers.nodeContainsPoint(node2, 1, 20), "17th check.");
|
||||
|
||||
ok(!ParserHelpers.nodeContainsPoint(node2, 0, 10), "18th check.");
|
||||
ok(!ParserHelpers.nodeContainsPoint(node2, 2, 20), "19th check.");
|
||||
|
||||
ok(!ParserHelpers.nodeContainsPoint(node2, 0, 9), "20th check.");
|
||||
ok(!ParserHelpers.nodeContainsPoint(node2, 2, 21), "21th check.");
|
||||
|
||||
ok(!ParserHelpers.nodeContainsPoint(node2, 1, 9), "22th check.");
|
||||
ok(!ParserHelpers.nodeContainsPoint(node2, 1, 21), "23th check.");
|
||||
|
||||
finish();
|
||||
}
|
289
browser/devtools/debugger/test/browser_dbg_parser-08.js
Normal file
289
browser/devtools/debugger/test/browser_dbg_parser-08.js
Normal file
@ -0,0 +1,289 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that inferring anonymous function information is done correctly.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
let { Parser, ParserHelpers, SyntaxTreeVisitor } =
|
||||
Cu.import("resource:///modules/devtools/Parser.jsm", {});
|
||||
|
||||
function verify(source, predicate, details) {
|
||||
let { name, chain } = details;
|
||||
let [[sline, scol], [eline, ecol]] = details.loc;
|
||||
let ast = Parser.reflectionAPI.parse(source);
|
||||
let node = SyntaxTreeVisitor.filter(ast, predicate).pop();
|
||||
let info = ParserHelpers.inferFunctionExpressionInfo(node);
|
||||
|
||||
is(info.name, name,
|
||||
"The function expression assignment property name is correct.");
|
||||
is(chain ? info.chain.toSource() : info.chain, chain ? chain.toSource() : chain,
|
||||
"The function expression assignment property chain is correct.");
|
||||
is(info.loc.start.toSource(), { line: sline, column: scol }.toSource(),
|
||||
"The start location was correct for the identifier in: '" + source + "'.");
|
||||
is(info.loc.end.toSource(), { line: eline, column: ecol }.toSource(),
|
||||
"The end location was correct for the identifier in: '" + source + "'.");
|
||||
}
|
||||
|
||||
// VariableDeclarator
|
||||
|
||||
verify("var foo=function(){}", e => e.type == "FunctionExpression", {
|
||||
name: "foo",
|
||||
chain: null,
|
||||
loc: [[1, 4], [1, 7]]
|
||||
});
|
||||
verify("\nvar\nfoo\n=\nfunction\n(\n)\n{\n}\n", e => e.type == "FunctionExpression", {
|
||||
name: "foo",
|
||||
chain: null,
|
||||
loc: [[3, 0], [3, 3]]
|
||||
});
|
||||
|
||||
// AssignmentExpression
|
||||
|
||||
verify("foo=function(){}", e => e.type == "FunctionExpression",
|
||||
{ name: "foo", chain: [], loc: [[1, 0], [1, 3]] });
|
||||
|
||||
verify("\nfoo\n=\nfunction\n(\n)\n{\n}\n", e => e.type == "FunctionExpression",
|
||||
{ name: "foo", chain: [], loc: [[2, 0], [2, 3]] });
|
||||
|
||||
verify("foo.bar=function(){}", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["foo"], loc: [[1, 0], [1, 7]] });
|
||||
|
||||
verify("\nfoo.bar\n=\nfunction\n(\n)\n{\n}\n", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["foo"], loc: [[2, 0], [2, 7]] });
|
||||
|
||||
verify("this.foo=function(){}", e => e.type == "FunctionExpression",
|
||||
{ name: "foo", chain: ["this"], loc: [[1, 0], [1, 8]] });
|
||||
|
||||
verify("\nthis.foo\n=\nfunction\n(\n)\n{\n}\n", e => e.type == "FunctionExpression",
|
||||
{ name: "foo", chain: ["this"], loc: [[2, 0], [2, 8]] });
|
||||
|
||||
verify("this.foo.bar=function(){}", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["this", "foo"], loc: [[1, 0], [1, 12]] });
|
||||
|
||||
verify("\nthis.foo.bar\n=\nfunction\n(\n)\n{\n}\n", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["this", "foo"], loc: [[2, 0], [2, 12]] });
|
||||
|
||||
verify("foo.this.bar=function(){}", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["foo", "this"], loc: [[1, 0], [1, 12]] });
|
||||
|
||||
verify("\nfoo.this.bar\n=\nfunction\n(\n)\n{\n}\n", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["foo", "this"], loc: [[2, 0], [2, 12]] });
|
||||
|
||||
// ObjectExpression
|
||||
|
||||
verify("({foo:function(){}})", e => e.type == "FunctionExpression",
|
||||
{ name: "foo", chain: [], loc: [[1, 2], [1, 5]] });
|
||||
|
||||
verify("(\n{\nfoo\n:\nfunction\n(\n)\n{\n}\n}\n)", e => e.type == "FunctionExpression",
|
||||
{ name: "foo", chain: [], loc: [[3, 0], [3, 3]] });
|
||||
|
||||
verify("({foo:{bar:function(){}}})", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["foo"], loc: [[1, 7], [1, 10]] });
|
||||
|
||||
verify("(\n{\nfoo\n:\n{\nbar\n:\nfunction\n(\n)\n{\n}\n}\n}\n)", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["foo"], loc: [[6, 0], [6, 3]] });
|
||||
|
||||
// AssignmentExpression + ObjectExpression
|
||||
|
||||
verify("foo={bar:function(){}}", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["foo"], loc: [[1, 5], [1, 8]] });
|
||||
|
||||
verify("\nfoo\n=\n{\nbar\n:\nfunction\n(\n)\n{\n}\n}\n", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["foo"], loc: [[5, 0], [5, 3]] });
|
||||
|
||||
verify("foo={bar:{baz:function(){}}}", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["foo", "bar"], loc: [[1, 10], [1, 13]] });
|
||||
|
||||
verify("\nfoo\n=\n{\nbar\n:\n{\nbaz\n:\nfunction\n(\n)\n{\n}\n}\n}\n", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["foo", "bar"], loc: [[8, 0], [8, 3]] });
|
||||
|
||||
verify("nested.foo={bar:function(){}}", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["nested", "foo"], loc: [[1, 12], [1, 15]] });
|
||||
|
||||
verify("\nnested.foo\n=\n{\nbar\n:\nfunction\n(\n)\n{\n}\n}\n", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["nested", "foo"], loc: [[5, 0], [5, 3]] });
|
||||
|
||||
verify("nested.foo={bar:{baz:function(){}}}", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["nested", "foo", "bar"], loc: [[1, 17], [1, 20]] });
|
||||
|
||||
verify("\nnested.foo\n=\n{\nbar\n:\n{\nbaz\n:\nfunction\n(\n)\n{\n}\n}\n}\n", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["nested", "foo", "bar"], loc: [[8, 0], [8, 3]] });
|
||||
|
||||
verify("this.foo={bar:function(){}}", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["this", "foo"], loc: [[1, 10], [1, 13]] });
|
||||
|
||||
verify("\nthis.foo\n=\n{\nbar\n:\nfunction\n(\n)\n{\n}\n}\n", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["this", "foo"], loc: [[5, 0], [5, 3]] });
|
||||
|
||||
verify("this.foo={bar:{baz:function(){}}}", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["this", "foo", "bar"], loc: [[1, 15], [1, 18]] });
|
||||
|
||||
verify("\nthis.foo\n=\n{\nbar\n:\n{\nbaz\n:\nfunction\n(\n)\n{\n}\n}\n}\n", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["this", "foo", "bar"], loc: [[8, 0], [8, 3]] });
|
||||
|
||||
verify("this.nested.foo={bar:function(){}}", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["this", "nested", "foo"], loc: [[1, 17], [1, 20]] });
|
||||
|
||||
verify("\nthis.nested.foo\n=\n{\nbar\n:\nfunction\n(\n)\n{\n}\n}\n", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["this", "nested", "foo"], loc: [[5, 0], [5, 3]] });
|
||||
|
||||
verify("this.nested.foo={bar:{baz:function(){}}}", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["this", "nested", "foo", "bar"], loc: [[1, 22], [1, 25]] });
|
||||
|
||||
verify("\nthis.nested.foo\n=\n{\nbar\n:\n{\nbaz\n:\nfunction\n(\n)\n{\n}\n}\n}\n", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["this", "nested", "foo", "bar"], loc: [[8, 0], [8, 3]] });
|
||||
|
||||
verify("nested.this.foo={bar:function(){}}", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["nested", "this", "foo"], loc: [[1, 17], [1, 20]] });
|
||||
|
||||
verify("\nnested.this.foo\n=\n{\nbar\n:\nfunction\n(\n)\n{\n}\n}\n", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["nested", "this", "foo"], loc: [[5, 0], [5, 3]] });
|
||||
|
||||
verify("nested.this.foo={bar:{baz:function(){}}}", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["nested", "this", "foo", "bar"], loc: [[1, 22], [1, 25]] });
|
||||
|
||||
verify("\nnested.this.foo\n=\n{\nbar\n:\n{\nbaz\n:\nfunction\n(\n)\n{\n}\n}\n}\n", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["nested", "this", "foo", "bar"], loc: [[8, 0], [8, 3]] });
|
||||
|
||||
// VariableDeclarator + AssignmentExpression + ObjectExpression
|
||||
|
||||
verify("let foo={bar:function(){}}", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["foo"], loc: [[1, 9], [1, 12]] });
|
||||
|
||||
verify("\nlet\nfoo\n=\n{\nbar\n:\nfunction\n(\n)\n{\n}\n}\n", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["foo"], loc: [[6, 0], [6, 3]] });
|
||||
|
||||
verify("let foo={bar:{baz:function(){}}}", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["foo", "bar"], loc: [[1, 14], [1, 17]] });
|
||||
|
||||
verify("\nlet\nfoo\n=\n{\nbar\n:\n{\nbaz\n:\nfunction\n(\n)\n{\n}\n}\n}\n", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["foo", "bar"], loc: [[9, 0], [9, 3]] });
|
||||
|
||||
// New/CallExpression + AssignmentExpression + ObjectExpression
|
||||
|
||||
verify("foo({bar:function(){}})", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: [], loc: [[1, 5], [1, 8]] });
|
||||
|
||||
verify("\nfoo\n(\n{\nbar\n:\nfunction\n(\n)\n{\n}\n}\n)\n", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: [], loc: [[5, 0], [5, 3]] });
|
||||
|
||||
verify("foo({bar:{baz:function(){}}})", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["bar"], loc: [[1, 10], [1, 13]] });
|
||||
|
||||
verify("\nfoo\n(\n{\nbar\n:\n{\nbaz\n:\nfunction\n(\n)\n{\n}\n}\n}\n)\n", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] });
|
||||
|
||||
verify("nested.foo({bar:function(){}})", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: [], loc: [[1, 12], [1, 15]] });
|
||||
|
||||
verify("\nnested.foo\n(\n{\nbar\n:\nfunction\n(\n)\n{\n}\n}\n)\n", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: [], loc: [[5, 0], [5, 3]] });
|
||||
|
||||
verify("nested.foo({bar:{baz:function(){}}})", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["bar"], loc: [[1, 17], [1, 20]] });
|
||||
|
||||
verify("\nnested.foo\n(\n{\nbar\n:\n{\nbaz\n:\nfunction\n(\n)\n{\n}\n}\n}\n)\n", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] });
|
||||
|
||||
verify("this.foo({bar:function(){}})", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: [], loc: [[1, 10], [1, 13]] });
|
||||
|
||||
verify("\nthis.foo\n(\n{\nbar\n:\nfunction\n(\n)\n{\n}\n}\n)\n", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: [], loc: [[5, 0], [5, 3]] });
|
||||
|
||||
verify("this.foo({bar:{baz:function(){}}})", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["bar"], loc: [[1, 15], [1, 18]] });
|
||||
|
||||
verify("\nthis.foo\n(\n{\nbar\n:\n{\nbaz\n:\nfunction\n(\n)\n{\n}\n}\n}\n)\n", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] });
|
||||
|
||||
verify("this.nested.foo({bar:function(){}})", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: [], loc: [[1, 17], [1, 20]] });
|
||||
|
||||
verify("\nthis.nested.foo\n(\n{\nbar\n:\nfunction\n(\n)\n{\n}\n}\n)\n", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: [], loc: [[5, 0], [5, 3]] });
|
||||
|
||||
verify("this.nested.foo({bar:{baz:function(){}}})", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["bar"], loc: [[1, 22], [1, 25]] });
|
||||
|
||||
verify("\nthis.nested.foo\n(\n{\nbar\n:\n{\nbaz\n:\nfunction\n(\n)\n{\n}\n}\n}\n)\n", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] });
|
||||
|
||||
verify("nested.this.foo({bar:function(){}})", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: [], loc: [[1, 17], [1, 20]] });
|
||||
|
||||
verify("\nnested.this.foo\n(\n{\nbar\n:\nfunction\n(\n)\n{\n}\n}\n)\n", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: [], loc: [[5, 0], [5, 3]] });
|
||||
|
||||
verify("nested.this.foo({bar:{baz:function(){}}})", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["bar"], loc: [[1, 22], [1, 25]] });
|
||||
|
||||
verify("\nnested.this.foo\n(\n{\nbar\n:\n{\nbaz\n:\nfunction\n(\n)\n{\n}\n}\n}\n)\n", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] });
|
||||
|
||||
// New/CallExpression + VariableDeclarator + AssignmentExpression + ObjectExpression
|
||||
|
||||
verify("let target=foo({bar:function(){}})", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["target"], loc: [[1, 16], [1, 19]] });
|
||||
|
||||
verify("\nlet\ntarget=\nfoo\n(\n{\nbar\n:\nfunction\n(\n)\n{\n}\n}\n)\n", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] });
|
||||
|
||||
verify("let target=foo({bar:{baz:function(){}}})", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["target", "bar"], loc: [[1, 21], [1, 24]] });
|
||||
|
||||
verify("\nlet\ntarget=\nfoo\n(\n{\nbar\n:\n{\nbaz\n:\nfunction\n(\n)\n{\n}\n}\n}\n)\n", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] });
|
||||
|
||||
verify("let target=nested.foo({bar:function(){}})", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["target"], loc: [[1, 23], [1, 26]] });
|
||||
|
||||
verify("\nlet\ntarget=\nnested.foo\n(\n{\nbar\n:\nfunction\n(\n)\n{\n}\n}\n)\n", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] });
|
||||
|
||||
verify("let target=nested.foo({bar:{baz:function(){}}})", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["target", "bar"], loc: [[1, 28], [1, 31]] });
|
||||
|
||||
verify("\nlet\ntarget=\nnested.foo\n(\n{\nbar\n:\n{\nbaz\n:\nfunction\n(\n)\n{\n}\n}\n}\n)\n", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] });
|
||||
|
||||
verify("let target=this.foo({bar:function(){}})", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["target"], loc: [[1, 21], [1, 24]] });
|
||||
|
||||
verify("\nlet\ntarget=\nthis.foo\n(\n{\nbar\n:\nfunction\n(\n)\n{\n}\n}\n)\n", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] });
|
||||
|
||||
verify("let target=this.foo({bar:{baz:function(){}}})", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["target", "bar"], loc: [[1, 26], [1, 29]] });
|
||||
|
||||
verify("\nlet\ntarget=\nthis.foo\n(\n{\nbar\n:\n{\nbaz\n:\nfunction\n(\n)\n{\n}\n}\n}\n)\n", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] });
|
||||
|
||||
verify("let target=this.nested.foo({bar:function(){}})", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["target"], loc: [[1, 28], [1, 31]] });
|
||||
|
||||
verify("\nlet\ntarget=\nthis.nested.foo\n(\n{\nbar\n:\nfunction\n(\n)\n{\n}\n}\n)\n", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] });
|
||||
|
||||
verify("let target=this.nested.foo({bar:{baz:function(){}}})", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["target", "bar"], loc: [[1, 33], [1, 36]] });
|
||||
|
||||
verify("\nlet\ntarget=\nthis.nested.foo\n(\n{\nbar\n:\n{\nbaz\n:\nfunction\n(\n)\n{\n}\n}\n}\n)\n", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] });
|
||||
|
||||
verify("let target=nested.this.foo({bar:function(){}})", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["target"], loc: [[1, 28], [1, 31]] });
|
||||
|
||||
verify("\nlet\ntarget=\nnested.this.foo\n(\n{\nbar\n:\nfunction\n(\n)\n{\n}\n}\n)\n", e => e.type == "FunctionExpression",
|
||||
{ name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] });
|
||||
|
||||
verify("let target=nested.this.foo({bar:{baz:function(){}}})", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["target", "bar"], loc: [[1, 33], [1, 36]] });
|
||||
|
||||
verify("\nlet\ntarget=\nnested.this.foo\n(\n{\nbar\n:\n{\nbaz\n:\nfunction\n(\n)\n{\n}\n}\n}\n)\n", e => e.type == "FunctionExpression",
|
||||
{ name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] });
|
||||
|
||||
finish();
|
||||
}
|
290
browser/devtools/debugger/test/browser_dbg_parser-09.js
Normal file
290
browser/devtools/debugger/test/browser_dbg_parser-09.js
Normal file
@ -0,0 +1,290 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that inferring anonymous function information is done correctly
|
||||
* from arrow expressions.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
let { Parser, ParserHelpers, SyntaxTreeVisitor } =
|
||||
Cu.import("resource:///modules/devtools/Parser.jsm", {});
|
||||
|
||||
function verify(source, predicate, details) {
|
||||
let { name, chain } = details;
|
||||
let [[sline, scol], [eline, ecol]] = details.loc;
|
||||
let ast = Parser.reflectionAPI.parse(source);
|
||||
let node = SyntaxTreeVisitor.filter(ast, predicate).pop();
|
||||
let info = ParserHelpers.inferFunctionExpressionInfo(node);
|
||||
|
||||
is(info.name, name,
|
||||
"The function expression assignment property name is correct.");
|
||||
is(chain ? info.chain.toSource() : info.chain, chain ? chain.toSource() : chain,
|
||||
"The function expression assignment property chain is correct.");
|
||||
is(info.loc.start.toSource(), { line: sline, column: scol }.toSource(),
|
||||
"The start location was correct for the identifier in: '" + source + "'.");
|
||||
is(info.loc.end.toSource(), { line: eline, column: ecol }.toSource(),
|
||||
"The end location was correct for the identifier in: '" + source + "'.");
|
||||
}
|
||||
|
||||
// VariableDeclarator
|
||||
|
||||
verify("var foo=()=>{}", e => e.type == "ArrowExpression", {
|
||||
name: "foo",
|
||||
chain: null,
|
||||
loc: [[1, 4], [1, 7]]
|
||||
});
|
||||
verify("\nvar\nfoo\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowExpression", {
|
||||
name: "foo",
|
||||
chain: null,
|
||||
loc: [[3, 0], [3, 3]]
|
||||
});
|
||||
|
||||
// AssignmentExpression
|
||||
|
||||
verify("foo=()=>{}", e => e.type == "ArrowExpression",
|
||||
{ name: "foo", chain: [], loc: [[1, 0], [1, 3]] });
|
||||
|
||||
verify("\nfoo\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowExpression",
|
||||
{ name: "foo", chain: [], loc: [[2, 0], [2, 3]] });
|
||||
|
||||
verify("foo.bar=()=>{}", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["foo"], loc: [[1, 0], [1, 7]] });
|
||||
|
||||
verify("\nfoo.bar\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["foo"], loc: [[2, 0], [2, 7]] });
|
||||
|
||||
verify("this.foo=()=>{}", e => e.type == "ArrowExpression",
|
||||
{ name: "foo", chain: ["this"], loc: [[1, 0], [1, 8]] });
|
||||
|
||||
verify("\nthis.foo\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowExpression",
|
||||
{ name: "foo", chain: ["this"], loc: [[2, 0], [2, 8]] });
|
||||
|
||||
verify("this.foo.bar=()=>{}", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["this", "foo"], loc: [[1, 0], [1, 12]] });
|
||||
|
||||
verify("\nthis.foo.bar\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["this", "foo"], loc: [[2, 0], [2, 12]] });
|
||||
|
||||
verify("foo.this.bar=()=>{}", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["foo", "this"], loc: [[1, 0], [1, 12]] });
|
||||
|
||||
verify("\nfoo.this.bar\n=\n(\n)\n=>\n{\n}\n", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["foo", "this"], loc: [[2, 0], [2, 12]] });
|
||||
|
||||
// ObjectExpression
|
||||
|
||||
verify("({foo:()=>{}})", e => e.type == "ArrowExpression",
|
||||
{ name: "foo", chain: [], loc: [[1, 2], [1, 5]] });
|
||||
|
||||
verify("(\n{\nfoo\n:\n(\n)\n=>\n{\n}\n}\n)", e => e.type == "ArrowExpression",
|
||||
{ name: "foo", chain: [], loc: [[3, 0], [3, 3]] });
|
||||
|
||||
verify("({foo:{bar:()=>{}}})", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["foo"], loc: [[1, 7], [1, 10]] });
|
||||
|
||||
verify("(\n{\nfoo\n:\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n}\n)", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["foo"], loc: [[6, 0], [6, 3]] });
|
||||
|
||||
// AssignmentExpression + ObjectExpression
|
||||
|
||||
verify("foo={bar:()=>{}}", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["foo"], loc: [[1, 5], [1, 8]] });
|
||||
|
||||
verify("\nfoo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["foo"], loc: [[5, 0], [5, 3]] });
|
||||
|
||||
verify("foo={bar:{baz:()=>{}}}", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["foo", "bar"], loc: [[1, 10], [1, 13]] });
|
||||
|
||||
verify("\nfoo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["foo", "bar"], loc: [[8, 0], [8, 3]] });
|
||||
|
||||
verify("nested.foo={bar:()=>{}}", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["nested", "foo"], loc: [[1, 12], [1, 15]] });
|
||||
|
||||
verify("\nnested.foo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["nested", "foo"], loc: [[5, 0], [5, 3]] });
|
||||
|
||||
verify("nested.foo={bar:{baz:()=>{}}}", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["nested", "foo", "bar"], loc: [[1, 17], [1, 20]] });
|
||||
|
||||
verify("\nnested.foo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["nested", "foo", "bar"], loc: [[8, 0], [8, 3]] });
|
||||
|
||||
verify("this.foo={bar:()=>{}}", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["this", "foo"], loc: [[1, 10], [1, 13]] });
|
||||
|
||||
verify("\nthis.foo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["this", "foo"], loc: [[5, 0], [5, 3]] });
|
||||
|
||||
verify("this.foo={bar:{baz:()=>{}}}", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["this", "foo", "bar"], loc: [[1, 15], [1, 18]] });
|
||||
|
||||
verify("\nthis.foo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["this", "foo", "bar"], loc: [[8, 0], [8, 3]] });
|
||||
|
||||
verify("this.nested.foo={bar:()=>{}}", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["this", "nested", "foo"], loc: [[1, 17], [1, 20]] });
|
||||
|
||||
verify("\nthis.nested.foo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["this", "nested", "foo"], loc: [[5, 0], [5, 3]] });
|
||||
|
||||
verify("this.nested.foo={bar:{baz:()=>{}}}", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["this", "nested", "foo", "bar"], loc: [[1, 22], [1, 25]] });
|
||||
|
||||
verify("\nthis.nested.foo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["this", "nested", "foo", "bar"], loc: [[8, 0], [8, 3]] });
|
||||
|
||||
verify("nested.this.foo={bar:()=>{}}", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["nested", "this", "foo"], loc: [[1, 17], [1, 20]] });
|
||||
|
||||
verify("\nnested.this.foo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["nested", "this", "foo"], loc: [[5, 0], [5, 3]] });
|
||||
|
||||
verify("nested.this.foo={bar:{baz:()=>{}}}", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["nested", "this", "foo", "bar"], loc: [[1, 22], [1, 25]] });
|
||||
|
||||
verify("\nnested.this.foo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["nested", "this", "foo", "bar"], loc: [[8, 0], [8, 3]] });
|
||||
|
||||
// VariableDeclarator + AssignmentExpression + ObjectExpression
|
||||
|
||||
verify("let foo={bar:()=>{}}", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["foo"], loc: [[1, 9], [1, 12]] });
|
||||
|
||||
verify("\nlet\nfoo\n=\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["foo"], loc: [[6, 0], [6, 3]] });
|
||||
|
||||
verify("let foo={bar:{baz:()=>{}}}", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["foo", "bar"], loc: [[1, 14], [1, 17]] });
|
||||
|
||||
verify("\nlet\nfoo\n=\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["foo", "bar"], loc: [[9, 0], [9, 3]] });
|
||||
|
||||
// New/CallExpression + AssignmentExpression + ObjectExpression
|
||||
|
||||
verify("foo({bar:()=>{}})", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: [], loc: [[1, 5], [1, 8]] });
|
||||
|
||||
verify("\nfoo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: [], loc: [[5, 0], [5, 3]] });
|
||||
|
||||
verify("foo({bar:{baz:()=>{}}})", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["bar"], loc: [[1, 10], [1, 13]] });
|
||||
|
||||
verify("\nfoo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] });
|
||||
|
||||
verify("nested.foo({bar:()=>{}})", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: [], loc: [[1, 12], [1, 15]] });
|
||||
|
||||
verify("\nnested.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: [], loc: [[5, 0], [5, 3]] });
|
||||
|
||||
verify("nested.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["bar"], loc: [[1, 17], [1, 20]] });
|
||||
|
||||
verify("\nnested.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] });
|
||||
|
||||
verify("this.foo({bar:()=>{}})", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: [], loc: [[1, 10], [1, 13]] });
|
||||
|
||||
verify("\nthis.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: [], loc: [[5, 0], [5, 3]] });
|
||||
|
||||
verify("this.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["bar"], loc: [[1, 15], [1, 18]] });
|
||||
|
||||
verify("\nthis.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] });
|
||||
|
||||
verify("this.nested.foo({bar:()=>{}})", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: [], loc: [[1, 17], [1, 20]] });
|
||||
|
||||
verify("\nthis.nested.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: [], loc: [[5, 0], [5, 3]] });
|
||||
|
||||
verify("this.nested.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["bar"], loc: [[1, 22], [1, 25]] });
|
||||
|
||||
verify("\nthis.nested.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] });
|
||||
|
||||
verify("nested.this.foo({bar:()=>{}})", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: [], loc: [[1, 17], [1, 20]] });
|
||||
|
||||
verify("\nnested.this.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: [], loc: [[5, 0], [5, 3]] });
|
||||
|
||||
verify("nested.this.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["bar"], loc: [[1, 22], [1, 25]] });
|
||||
|
||||
verify("\nnested.this.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["bar"], loc: [[8, 0], [8, 3]] });
|
||||
|
||||
// New/CallExpression + VariableDeclarator + AssignmentExpression + ObjectExpression
|
||||
|
||||
verify("let target=foo({bar:()=>{}})", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["target"], loc: [[1, 16], [1, 19]] });
|
||||
|
||||
verify("\nlet\ntarget=\nfoo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] });
|
||||
|
||||
verify("let target=foo({bar:{baz:()=>{}}})", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["target", "bar"], loc: [[1, 21], [1, 24]] });
|
||||
|
||||
verify("\nlet\ntarget=\nfoo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] });
|
||||
|
||||
verify("let target=nested.foo({bar:()=>{}})", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["target"], loc: [[1, 23], [1, 26]] });
|
||||
|
||||
verify("\nlet\ntarget=\nnested.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] });
|
||||
|
||||
verify("let target=nested.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["target", "bar"], loc: [[1, 28], [1, 31]] });
|
||||
|
||||
verify("\nlet\ntarget=\nnested.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] });
|
||||
|
||||
verify("let target=this.foo({bar:()=>{}})", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["target"], loc: [[1, 21], [1, 24]] });
|
||||
|
||||
verify("\nlet\ntarget=\nthis.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] });
|
||||
|
||||
verify("let target=this.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["target", "bar"], loc: [[1, 26], [1, 29]] });
|
||||
|
||||
verify("\nlet\ntarget=\nthis.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] });
|
||||
|
||||
verify("let target=this.nested.foo({bar:()=>{}})", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["target"], loc: [[1, 28], [1, 31]] });
|
||||
|
||||
verify("\nlet\ntarget=\nthis.nested.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] });
|
||||
|
||||
verify("let target=this.nested.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["target", "bar"], loc: [[1, 33], [1, 36]] });
|
||||
|
||||
verify("\nlet\ntarget=\nthis.nested.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] });
|
||||
|
||||
verify("let target=nested.this.foo({bar:()=>{}})", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["target"], loc: [[1, 28], [1, 31]] });
|
||||
|
||||
verify("\nlet\ntarget=\nnested.this.foo\n(\n{\nbar\n:\n(\n)\n=>\n{\n}\n}\n)\n", e => e.type == "ArrowExpression",
|
||||
{ name: "bar", chain: ["target"], loc: [[7, 0], [7, 3]] });
|
||||
|
||||
verify("let target=nested.this.foo({bar:{baz:()=>{}}})", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["target", "bar"], loc: [[1, 33], [1, 36]] });
|
||||
|
||||
verify("\nlet\ntarget=\nnested.this.foo\n(\n{\nbar\n:\n{\nbaz\n:\n(\n)\n=>\n{\n}\n}\n}\n)\n", e => e.type == "ArrowExpression",
|
||||
{ name: "baz", chain: ["target", "bar"], loc: [[10, 0], [10, 3]] });
|
||||
|
||||
finish();
|
||||
}
|
127
browser/devtools/debugger/test/browser_dbg_parser-10.js
Normal file
127
browser/devtools/debugger/test/browser_dbg_parser-10.js
Normal file
@ -0,0 +1,127 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that creating an evaluation string for certain nodes works properly.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
let { Parser, ParserHelpers, SyntaxTreeVisitor } =
|
||||
Cu.import("resource:///modules/devtools/Parser.jsm", {});
|
||||
|
||||
function verify(source, predicate, string) {
|
||||
let ast = Parser.reflectionAPI.parse(source);
|
||||
let node = SyntaxTreeVisitor.filter(ast, predicate).pop();
|
||||
let info = ParserHelpers.getIdentifierEvalString(node);
|
||||
is(info, string, "The identifier evaluation string is correct.");
|
||||
}
|
||||
|
||||
// Indentifier or Literal
|
||||
|
||||
verify("foo", e => e.type == "Identifier", "foo");
|
||||
verify("undefined", e => e.type == "Identifier", "undefined");
|
||||
verify("null", e => e.type == "Literal", "null");
|
||||
verify("42", e => e.type == "Literal", "42");
|
||||
verify("true", e => e.type == "Literal", "true");
|
||||
verify("\"nasu\"", e => e.type == "Literal", "\"nasu\"");
|
||||
|
||||
// MemberExpression or ThisExpression
|
||||
|
||||
verify("this", e => e.type == "ThisExpression", "this");
|
||||
verify("foo.bar", e => e.name == "foo", "foo");
|
||||
verify("foo.bar", e => e.name == "bar", "foo.bar");
|
||||
|
||||
// MemberExpression + ThisExpression
|
||||
|
||||
verify("this.foo.bar", e => e.type == "ThisExpression", "this");
|
||||
verify("this.foo.bar", e => e.name == "foo", "this.foo");
|
||||
verify("this.foo.bar", e => e.name == "bar", "this.foo.bar");
|
||||
|
||||
verify("foo.this.bar", e => e.name == "foo", "foo");
|
||||
verify("foo.this.bar", e => e.name == "this", "foo.this");
|
||||
verify("foo.this.bar", e => e.name == "bar", "foo.this.bar");
|
||||
|
||||
// ObjectExpression + VariableDeclarator
|
||||
|
||||
verify("let foo={bar:baz}", e => e.name == "baz", "baz");
|
||||
verify("let foo={bar:undefined}", e => e.name == "undefined", "undefined");
|
||||
verify("let foo={bar:null}", e => e.type == "Literal", "null");
|
||||
verify("let foo={bar:42}", e => e.type == "Literal", "42");
|
||||
verify("let foo={bar:true}", e => e.type == "Literal", "true");
|
||||
verify("let foo={bar:\"nasu\"}", e => e.type == "Literal", "\"nasu\"");
|
||||
verify("let foo={bar:this}", e => e.type == "ThisExpression", "this");
|
||||
|
||||
verify("let foo={bar:{nested:baz}}", e => e.name == "baz", "baz");
|
||||
verify("let foo={bar:{nested:undefined}}", e => e.name == "undefined", "undefined");
|
||||
verify("let foo={bar:{nested:null}}", e => e.type == "Literal", "null");
|
||||
verify("let foo={bar:{nested:42}}", e => e.type == "Literal", "42");
|
||||
verify("let foo={bar:{nested:true}}", e => e.type == "Literal", "true");
|
||||
verify("let foo={bar:{nested:\"nasu\"}}", e => e.type == "Literal", "\"nasu\"");
|
||||
verify("let foo={bar:{nested:this}}", e => e.type == "ThisExpression", "this");
|
||||
|
||||
verify("let foo={bar:baz}", e => e.name == "bar", "foo.bar");
|
||||
verify("let foo={bar:baz}", e => e.name == "foo", "foo");
|
||||
|
||||
verify("let foo={bar:{nested:baz}}", e => e.name == "nested", "foo.bar.nested");
|
||||
verify("let foo={bar:{nested:baz}}", e => e.name == "bar", "foo.bar");
|
||||
verify("let foo={bar:{nested:baz}}", e => e.name == "foo", "foo");
|
||||
|
||||
// ObjectExpression + MemberExpression
|
||||
|
||||
verify("parent.foo={bar:baz}", e => e.name == "bar", "parent.foo.bar");
|
||||
verify("parent.foo={bar:baz}", e => e.name == "foo", "parent.foo");
|
||||
verify("parent.foo={bar:baz}", e => e.name == "parent", "parent");
|
||||
|
||||
verify("parent.foo={bar:{nested:baz}}", e => e.name == "nested", "parent.foo.bar.nested");
|
||||
verify("parent.foo={bar:{nested:baz}}", e => e.name == "bar", "parent.foo.bar");
|
||||
verify("parent.foo={bar:{nested:baz}}", e => e.name == "foo", "parent.foo");
|
||||
verify("parent.foo={bar:{nested:baz}}", e => e.name == "parent", "parent");
|
||||
|
||||
verify("this.foo={bar:{nested:baz}}", e => e.name == "nested", "this.foo.bar.nested");
|
||||
verify("this.foo={bar:{nested:baz}}", e => e.name == "bar", "this.foo.bar");
|
||||
verify("this.foo={bar:{nested:baz}}", e => e.name == "foo", "this.foo");
|
||||
verify("this.foo={bar:{nested:baz}}", e => e.type == "ThisExpression", "this");
|
||||
|
||||
verify("this.parent.foo={bar:{nested:baz}}", e => e.name == "nested", "this.parent.foo.bar.nested");
|
||||
verify("this.parent.foo={bar:{nested:baz}}", e => e.name == "bar", "this.parent.foo.bar");
|
||||
verify("this.parent.foo={bar:{nested:baz}}", e => e.name == "foo", "this.parent.foo");
|
||||
verify("this.parent.foo={bar:{nested:baz}}", e => e.name == "parent", "this.parent");
|
||||
verify("this.parent.foo={bar:{nested:baz}}", e => e.type == "ThisExpression", "this");
|
||||
|
||||
verify("parent.this.foo={bar:{nested:baz}}", e => e.name == "nested", "parent.this.foo.bar.nested");
|
||||
verify("parent.this.foo={bar:{nested:baz}}", e => e.name == "bar", "parent.this.foo.bar");
|
||||
verify("parent.this.foo={bar:{nested:baz}}", e => e.name == "foo", "parent.this.foo");
|
||||
verify("parent.this.foo={bar:{nested:baz}}", e => e.name == "this", "parent.this");
|
||||
verify("parent.this.foo={bar:{nested:baz}}", e => e.name == "parent", "parent");
|
||||
|
||||
// FunctionExpression
|
||||
|
||||
verify("function foo(){}", e => e.name == "foo", "foo");
|
||||
verify("var foo=function(){}", e => e.name == "foo", "foo");
|
||||
verify("var foo=function bar(){}", e => e.name == "bar", "bar");
|
||||
|
||||
// New/CallExpression
|
||||
|
||||
verify("foo()", e => e.name == "foo", "foo");
|
||||
verify("new foo()", e => e.name == "foo", "foo");
|
||||
|
||||
verify("foo(bar)", e => e.name == "bar", "bar");
|
||||
verify("foo(bar, baz)", e => e.name == "baz", "baz");
|
||||
verify("foo(undefined)", e => e.name == "undefined", "undefined");
|
||||
verify("foo(null)", e => e.type == "Literal", "null");
|
||||
verify("foo(42)", e => e.type == "Literal", "42");
|
||||
verify("foo(true)", e => e.type == "Literal", "true");
|
||||
verify("foo(\"nasu\")", e => e.type == "Literal", "\"nasu\"");
|
||||
verify("foo(this)", e => e.type == "ThisExpression", "this");
|
||||
|
||||
// New/CallExpression + ObjectExpression + MemberExpression
|
||||
|
||||
verify("fun(this.parent.foo={bar:{nested:baz}})", e => e.name == "nested", "this.parent.foo.bar.nested");
|
||||
verify("fun(this.parent.foo={bar:{nested:baz}})", e => e.name == "bar", "this.parent.foo.bar");
|
||||
verify("fun(this.parent.foo={bar:{nested:baz}})", e => e.name == "foo", "this.parent.foo");
|
||||
verify("fun(this.parent.foo={bar:{nested:baz}})", e => e.name == "parent", "this.parent");
|
||||
verify("fun(this.parent.foo={bar:{nested:baz}})", e => e.type == "ThisExpression", "this");
|
||||
verify("fun(this.parent.foo={bar:{nested:baz}})", e => e.name == "fun", "fun");
|
||||
|
||||
finish();
|
||||
}
|
@ -77,6 +77,8 @@ function testSourcesDisplay() {
|
||||
executeSoon(() => {
|
||||
is(gEditor.getDebugLocation(), 5,
|
||||
"Editor debugger location is correct.");
|
||||
ok(gEditor.hasLineClass(5, "debug-line"),
|
||||
"The debugged line is highlighted appropriately.");
|
||||
|
||||
waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN).then(deferred.resolve);
|
||||
gSources.selectedIndex = 0;
|
||||
@ -106,6 +108,8 @@ function testSwitchPaused1() {
|
||||
"Editor caret location is correct.");
|
||||
is(gEditor.getDebugLocation(), null,
|
||||
"Editor debugger location is correct.");
|
||||
ok(!gEditor.hasLineClass(5, "debug-line"),
|
||||
"The debugged line highlight was removed.");
|
||||
|
||||
waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN).then(deferred.resolve);
|
||||
gSources.selectedIndex = 1;
|
||||
@ -135,6 +139,8 @@ function testSwitchPaused2() {
|
||||
"Editor caret location is correct.");
|
||||
is(gEditor.getDebugLocation(), 5,
|
||||
"Editor debugger location is correct.");
|
||||
ok(gEditor.hasLineClass(5, "debug-line"),
|
||||
"The debugged line is highlighted appropriately.");
|
||||
|
||||
// Step out three times.
|
||||
waitForThreadEvents(gPanel, "paused").then(() => {
|
||||
@ -171,6 +177,8 @@ function testSwitchRunning() {
|
||||
"Editor caret location is correct.");
|
||||
is(gEditor.getDebugLocation(), 4,
|
||||
"Editor debugger location is correct.");
|
||||
ok(gEditor.hasLineClass(4, "debug-line"),
|
||||
"The debugged line is highlighted appropriately.");
|
||||
|
||||
deferred.resolve();
|
||||
});
|
||||
|
@ -72,6 +72,8 @@ function testSourcesDisplay() {
|
||||
executeSoon(() => {
|
||||
is(gEditor.getDebugLocation(), 5,
|
||||
"Editor debugger location is correct.");
|
||||
ok(gEditor.hasLineClass(5, "debug-line"),
|
||||
"The debugged line is highlighted appropriately.");
|
||||
|
||||
waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN).then(deferred.resolve);
|
||||
gSources.selectedLabel = gLabel1;
|
||||
@ -102,6 +104,8 @@ function testSwitchPaused1() {
|
||||
|
||||
is(gEditor.getDebugLocation(), null,
|
||||
"Editor debugger location is correct.");
|
||||
ok(!gEditor.hasLineClass(5, "debug-line"),
|
||||
"The debugged line highlight was removed.");
|
||||
|
||||
waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN).then(deferred.resolve);
|
||||
gSources.selectedLabel = gLabel2;
|
||||
@ -131,6 +135,8 @@ function testSwitchPaused2() {
|
||||
"Editor caret location is correct.");
|
||||
is(gEditor.getDebugLocation(), 5,
|
||||
"Editor debugger location is correct.");
|
||||
ok(gEditor.hasLineClass(5, "debug-line"),
|
||||
"The debugged line is highlighted appropriately.");
|
||||
|
||||
// Step out three times.
|
||||
waitForThreadEvents(gPanel, "paused").then(() => {
|
||||
@ -167,6 +173,8 @@ function testSwitchRunning() {
|
||||
"Editor caret location is correct.");
|
||||
is(gEditor.getDebugLocation(), 4,
|
||||
"Editor debugger location is correct.");
|
||||
ok(gEditor.hasLineClass(4, "debug-line"),
|
||||
"The debugged line is highlighted appropriately.");
|
||||
|
||||
deferred.resolve();
|
||||
});
|
||||
|
@ -175,9 +175,9 @@ function secondJsSearch() {
|
||||
["y" + s + "Y", "-02.js", "test.prototype.sub", 16, 1],
|
||||
["z" + s + "Z", "-02.js", "test.prototype.sub.sub", 18, 1],
|
||||
["t", "-02.js", "test.prototype.sub.sub.sub", 20, 1],
|
||||
["x", "-02.js", "", 20, 32],
|
||||
["y", "-02.js", "", 20, 41],
|
||||
["z", "-02.js", "", 20, 50]
|
||||
["x", "-02.js", "this", 20, 32],
|
||||
["y", "-02.js", "this", 20, 41],
|
||||
["z", "-02.js", "this", 20, 50]
|
||||
];
|
||||
|
||||
for (let [label, value, description, line, column] of expectedResults) {
|
||||
@ -231,11 +231,11 @@ function thirdJsSearch() {
|
||||
["a" + s + "A", "-03.js", "bar", 10, 5],
|
||||
["b" + s + "B", "-03.js", "bar.alpha", 15, 5],
|
||||
["c" + s + "C", "-03.js", "bar.alpha.beta", 20, 5],
|
||||
["d" + s + "D", "-03.js", "theta", 25, 5],
|
||||
["d" + s + "D", "-03.js", "this.theta", 25, 5],
|
||||
["fun", "-03.js", "", 29, 7],
|
||||
["foo", "-03.js", "", 29, 13],
|
||||
["bar", "-03.js", "", 29, 19],
|
||||
["t_foo", "-03.js", "", 29, 25],
|
||||
["t_foo", "-03.js", "this", 29, 25],
|
||||
["w_bar" + s + "baz", "-03.js", "window", 29, 38]
|
||||
];
|
||||
|
||||
|
103
browser/devtools/debugger/test/browser_dbg_stack-07.js
Normal file
103
browser/devtools/debugger/test/browser_dbg_stack-07.js
Normal file
@ -0,0 +1,103 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Make sure that after selecting a different stack frame, resuming reselects
|
||||
* the topmost stackframe, loads the right source in the editor pane and
|
||||
* highlights the proper line.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html";
|
||||
|
||||
let gTab, gDebuggee, gPanel, gDebugger;
|
||||
let gEditor, gSources, gFrames, gToolbar;
|
||||
|
||||
function test() {
|
||||
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gEditor = gDebugger.DebuggerView.editor;
|
||||
gSources = gDebugger.DebuggerView.Sources;
|
||||
gFrames = gDebugger.DebuggerView.StackFrames;
|
||||
gToolbar = gDebugger.DebuggerView.Toolbar;
|
||||
|
||||
waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6).then(performTest);
|
||||
gDebuggee.firstCall();
|
||||
});
|
||||
}
|
||||
|
||||
function performTest() {
|
||||
return Task.spawn(function() {
|
||||
yield selectBottomFrame();
|
||||
testBottomFrame(5);
|
||||
|
||||
yield performStep("StepOver");
|
||||
testTopFrame(3);
|
||||
|
||||
yield selectBottomFrame();
|
||||
testBottomFrame(4);
|
||||
|
||||
yield performStep("StepIn");
|
||||
testTopFrame(2);
|
||||
|
||||
yield selectBottomFrame();
|
||||
testBottomFrame(4);
|
||||
|
||||
yield performStep("StepOut");
|
||||
testTopFrame(2);
|
||||
|
||||
yield resumeDebuggerThenCloseAndFinish(gPanel);
|
||||
});
|
||||
|
||||
function selectBottomFrame() {
|
||||
let updated = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES);
|
||||
gFrames.selectedIndex = 0;
|
||||
return updated.then(waitForTick);
|
||||
}
|
||||
|
||||
function testBottomFrame(debugLocation) {
|
||||
is(gFrames.selectedIndex, 0,
|
||||
"Oldest frame should be selected after click.");
|
||||
is(gSources.selectedIndex, 0,
|
||||
"The first source is now selected in the widget.");
|
||||
is(gEditor.getText().search(/firstCall/), 118,
|
||||
"The first source is displayed.");
|
||||
is(gEditor.getText().search(/debugger/), -1,
|
||||
"The second source is not displayed.");
|
||||
|
||||
is(gEditor.getDebugLocation(), debugLocation,
|
||||
"Editor debugger location is correct.");
|
||||
ok(gEditor.hasLineClass(debugLocation, "debug-line"),
|
||||
"The debugged line is highlighted appropriately.");
|
||||
}
|
||||
|
||||
function performStep(type) {
|
||||
let updated = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES);
|
||||
gToolbar["_on" + type + "Pressed"]();
|
||||
return updated.then(waitForTick);
|
||||
}
|
||||
|
||||
function testTopFrame(frameIndex) {
|
||||
is(gFrames.selectedIndex, frameIndex,
|
||||
"Topmost frame should be selected after click.");
|
||||
is(gSources.selectedIndex, 1,
|
||||
"The second source is now selected in the widget.");
|
||||
is(gEditor.getText().search(/firstCall/), -1,
|
||||
"The second source is displayed.");
|
||||
is(gEditor.getText().search(/debugger/), 172,
|
||||
"The first source is not displayed.");
|
||||
}
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gTab = null;
|
||||
gDebuggee = null;
|
||||
gPanel = null;
|
||||
gDebugger = null;
|
||||
gEditor = null;
|
||||
gSources = null;
|
||||
gFrames = null;
|
||||
gToolbar = null;
|
||||
});
|
@ -187,7 +187,7 @@ function performTest() {
|
||||
function getScroll() {
|
||||
let scrollX = {};
|
||||
let scrollY = {};
|
||||
gVariables._boxObject.getPosition(scrollX, scrollY);
|
||||
gVariables.boxObject.getPosition(scrollX, scrollY);
|
||||
return [scrollX.value, scrollY.value];
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,73 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests opening the variable inspection popup on a variable which has a
|
||||
* simple literal as the value.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_frame-parameters.html";
|
||||
|
||||
function test() {
|
||||
Task.spawn(function() {
|
||||
let [tab, debuggee, panel] = yield initDebugger(TAB_URL);
|
||||
let win = panel.panelWin;
|
||||
let events = win.EVENTS;
|
||||
let editor = win.DebuggerView.editor;
|
||||
let bubble = win.DebuggerView.VariableBubble;
|
||||
let tooltip = bubble._tooltip.panel;
|
||||
|
||||
function openPopup(coords) {
|
||||
let popupshown = once(tooltip, "popupshown");
|
||||
let { left, top } = editor.getCoordsFromPosition(coords);
|
||||
bubble._findIdentifier(left, top);
|
||||
return popupshown;
|
||||
}
|
||||
|
||||
function hidePopup() {
|
||||
let popuphiding = once(tooltip, "popuphiding");
|
||||
bubble.hideContents();
|
||||
return popuphiding.then(waitForTick);
|
||||
}
|
||||
|
||||
function verifyContents(textContent, className) {
|
||||
is(tooltip.querySelectorAll(".variables-view-container").length, 0,
|
||||
"There should be no variables view containers added to the tooltip.");
|
||||
is(tooltip.querySelectorAll(".devtools-tooltip-simple-text").length, 1,
|
||||
"There should be a simple text node added to the tooltip instead.");
|
||||
|
||||
is(tooltip.querySelector(".devtools-tooltip-simple-text").textContent, textContent,
|
||||
"The inspected property's value is correct.");
|
||||
ok(tooltip.querySelector(".devtools-tooltip-simple-text").className.contains(className),
|
||||
"The inspected property's value is colorized correctly.");
|
||||
}
|
||||
|
||||
// Allow this generator function to yield first.
|
||||
executeSoon(() => debuggee.start());
|
||||
yield waitForSourceAndCaretAndScopes(panel, ".html", 24);
|
||||
|
||||
// Inspect variables.
|
||||
yield openPopup({ line: 15, ch: 12 });
|
||||
verifyContents("1", "token-number");
|
||||
|
||||
yield hidePopup().then(() => openPopup({ line: 16, ch: 21 }));
|
||||
verifyContents("1", "token-number");
|
||||
|
||||
yield hidePopup().then(() => openPopup({ line: 17, ch: 21 }));
|
||||
verifyContents("1", "token-number");
|
||||
|
||||
yield hidePopup().then(() => openPopup({ line: 17, ch: 27 }));
|
||||
verifyContents("\"beta\"", "token-string");
|
||||
|
||||
yield hidePopup().then(() => openPopup({ line: 17, ch: 44 }));
|
||||
verifyContents("false", "token-boolean");
|
||||
|
||||
yield hidePopup().then(() => openPopup({ line: 17, ch: 54 }));
|
||||
verifyContents("null", "token-null");
|
||||
|
||||
yield hidePopup().then(() => openPopup({ line: 17, ch: 63 }));
|
||||
verifyContents("undefined", "token-undefined");
|
||||
|
||||
yield resumeDebuggerThenCloseAndFinish(panel);
|
||||
});
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests opening the variable inspection popup on a variable which has a
|
||||
* a property accessible via getters and setters.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_frame-parameters.html";
|
||||
|
||||
function test() {
|
||||
Task.spawn(function() {
|
||||
let [tab, debuggee, panel] = yield initDebugger(TAB_URL);
|
||||
let win = panel.panelWin;
|
||||
let events = win.EVENTS;
|
||||
let editor = win.DebuggerView.editor;
|
||||
let bubble = win.DebuggerView.VariableBubble;
|
||||
let tooltip = bubble._tooltip.panel;
|
||||
|
||||
function openPopup(coords) {
|
||||
let popupshown = once(tooltip, "popupshown");
|
||||
let { left, top } = editor.getCoordsFromPosition(coords);
|
||||
bubble._findIdentifier(left, top);
|
||||
return popupshown;
|
||||
}
|
||||
|
||||
function hidePopup() {
|
||||
let popuphiding = once(tooltip, "popuphiding");
|
||||
bubble.hideContents();
|
||||
return popuphiding.then(waitForTick);
|
||||
}
|
||||
|
||||
function verifyContents(textContent, className) {
|
||||
is(tooltip.querySelectorAll(".variables-view-container").length, 0,
|
||||
"There should be no variables view containers added to the tooltip.");
|
||||
is(tooltip.querySelectorAll(".devtools-tooltip-simple-text").length, 1,
|
||||
"There should be a simple text node added to the tooltip instead.");
|
||||
|
||||
is(tooltip.querySelector(".devtools-tooltip-simple-text").textContent, textContent,
|
||||
"The inspected property's value is correct.");
|
||||
ok(tooltip.querySelector(".devtools-tooltip-simple-text").className.contains(className),
|
||||
"The inspected property's value is colorized correctly.");
|
||||
}
|
||||
|
||||
// Allow this generator function to yield first.
|
||||
executeSoon(() => debuggee.start());
|
||||
yield waitForSourceAndCaretAndScopes(panel, ".html", 24);
|
||||
|
||||
// Inspect properties.
|
||||
yield openPopup({ line: 19, ch: 10 });
|
||||
verifyContents("42", "token-number");
|
||||
|
||||
yield hidePopup().then(() => openPopup({ line: 20, ch: 14 }));
|
||||
verifyContents("42", "token-number");
|
||||
|
||||
yield hidePopup().then(() => openPopup({ line: 21, ch: 14 }));
|
||||
verifyContents("42", "token-number");
|
||||
|
||||
yield resumeDebuggerThenCloseAndFinish(panel);
|
||||
});
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that the inspected indentifier is highlighted.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_frame-parameters.html";
|
||||
|
||||
function test() {
|
||||
Task.spawn(function() {
|
||||
let [tab, debuggee, panel] = yield initDebugger(TAB_URL);
|
||||
let win = panel.panelWin;
|
||||
let events = win.EVENTS;
|
||||
let editor = win.DebuggerView.editor;
|
||||
let bubble = win.DebuggerView.VariableBubble;
|
||||
let tooltip = bubble._tooltip.panel;
|
||||
|
||||
function openPopup(coords) {
|
||||
let popupshown = once(tooltip, "popupshown");
|
||||
let { left, top } = editor.getCoordsFromPosition(coords);
|
||||
bubble._findIdentifier(left, top);
|
||||
return popupshown;
|
||||
}
|
||||
|
||||
function hidePopup() {
|
||||
let popuphiding = once(tooltip, "popuphiding");
|
||||
bubble.hideContents();
|
||||
return popuphiding.then(waitForTick);
|
||||
}
|
||||
|
||||
// Allow this generator function to yield first.
|
||||
executeSoon(() => debuggee.start());
|
||||
yield waitForSourceAndCaretAndScopes(panel, ".html", 24);
|
||||
|
||||
// Inspect variable.
|
||||
yield openPopup({ line: 15, ch: 12 });
|
||||
|
||||
ok(!bubble._tooltip.isEmpty(),
|
||||
"The variable inspection popup isn't empty.");
|
||||
ok(bubble._markedText,
|
||||
"There's some marked text in the editor.");
|
||||
ok(bubble._markedText.clear,
|
||||
"The marked text in the editor can be cleared.");
|
||||
|
||||
yield hidePopup();
|
||||
|
||||
ok(bubble._tooltip.isEmpty(),
|
||||
"The variable inspection popup is now empty.");
|
||||
ok(!bubble._markedText,
|
||||
"The marked text in the editor was removed.");
|
||||
|
||||
yield resumeDebuggerThenCloseAndFinish(panel);
|
||||
});
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that the variable inspection popup is hidden when the editor scrolls.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_frame-parameters.html";
|
||||
|
||||
function test() {
|
||||
Task.spawn(function() {
|
||||
let [tab, debuggee, panel] = yield initDebugger(TAB_URL);
|
||||
let win = panel.panelWin;
|
||||
let events = win.EVENTS;
|
||||
let editor = win.DebuggerView.editor;
|
||||
let bubble = win.DebuggerView.VariableBubble;
|
||||
let tooltip = bubble._tooltip.panel;
|
||||
|
||||
function openPopup(coords) {
|
||||
let popupshown = once(tooltip, "popupshown");
|
||||
let { left, top } = editor.getCoordsFromPosition(coords);
|
||||
bubble._findIdentifier(left, top);
|
||||
return popupshown;
|
||||
}
|
||||
|
||||
function scrollEditor() {
|
||||
let popuphiding = once(tooltip, "popuphiding");
|
||||
editor.setFirstVisibleLine(0);
|
||||
return popuphiding.then(waitForTick);
|
||||
}
|
||||
|
||||
// Allow this generator function to yield first.
|
||||
executeSoon(() => debuggee.start());
|
||||
yield waitForSourceAndCaretAndScopes(panel, ".html", 24);
|
||||
|
||||
// Inspect variable.
|
||||
yield openPopup({ line: 15, ch: 12 });
|
||||
yield scrollEditor();
|
||||
ok(true, "The variable inspection popup was hidden.");
|
||||
|
||||
ok(bubble._tooltip.isEmpty(),
|
||||
"The variable inspection popup is now empty.");
|
||||
ok(!bubble._markedText,
|
||||
"The marked text in the editor was removed.");
|
||||
|
||||
yield resumeDebuggerThenCloseAndFinish(panel);
|
||||
});
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests opening the variable inspection popup on a variable which has a
|
||||
* simple object as the value.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_frame-parameters.html";
|
||||
|
||||
function test() {
|
||||
Task.spawn(function() {
|
||||
let [tab, debuggee, panel] = yield initDebugger(TAB_URL);
|
||||
let win = panel.panelWin;
|
||||
let events = win.EVENTS;
|
||||
let editor = win.DebuggerView.editor;
|
||||
let bubble = win.DebuggerView.VariableBubble;
|
||||
let tooltip = bubble._tooltip.panel;
|
||||
|
||||
function openPopup(coords) {
|
||||
let popupshown = once(tooltip, "popupshown");
|
||||
let fetched = waitForDebuggerEvents(panel, events.FETCHED_BUBBLE_PROPERTIES);
|
||||
let { left, top } = editor.getCoordsFromPosition(coords);
|
||||
bubble._findIdentifier(left, top);
|
||||
return promise.all([popupshown, fetched]);
|
||||
}
|
||||
|
||||
function verifyContents() {
|
||||
is(tooltip.querySelectorAll(".variables-view-container").length, 1,
|
||||
"There should be one variables view container added to the tooltip.");
|
||||
|
||||
is(tooltip.querySelectorAll(".variables-view-scope[non-header]").length, 1,
|
||||
"There should be one scope with no header displayed.");
|
||||
is(tooltip.querySelectorAll(".variables-view-variable[non-header]").length, 1,
|
||||
"There should be one variable with no header displayed.");
|
||||
|
||||
is(tooltip.querySelectorAll(".variables-view-property").length, 2,
|
||||
"There should be 2 properties displayed.");
|
||||
|
||||
is(tooltip.querySelectorAll(".variables-view-property .name")[0].getAttribute("value"), "a",
|
||||
"The first property's name is correct.");
|
||||
is(tooltip.querySelectorAll(".variables-view-property .value")[0].getAttribute("value"), "1",
|
||||
"The first property's value is correct.");
|
||||
|
||||
is(tooltip.querySelectorAll(".variables-view-property .name")[1].getAttribute("value"), "__proto__",
|
||||
"The second property's name is correct.");
|
||||
is(tooltip.querySelectorAll(".variables-view-property .value")[1].getAttribute("value"), "Object",
|
||||
"The second property's value is correct.");
|
||||
}
|
||||
|
||||
// Allow this generator function to yield first.
|
||||
executeSoon(() => debuggee.start());
|
||||
yield waitForSourceAndCaretAndScopes(panel, ".html", 24);
|
||||
|
||||
// Inspect variable.
|
||||
yield openPopup({ line: 16, ch: 12 });
|
||||
verifyContents();
|
||||
|
||||
yield resumeDebuggerThenCloseAndFinish(panel);
|
||||
});
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests opening the variable inspection popup on a variable which has a
|
||||
* complext object as the value.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_frame-parameters.html";
|
||||
|
||||
function test() {
|
||||
Task.spawn(function() {
|
||||
let [tab, debuggee, panel] = yield initDebugger(TAB_URL);
|
||||
let win = panel.panelWin;
|
||||
let events = win.EVENTS;
|
||||
let editor = win.DebuggerView.editor;
|
||||
let bubble = win.DebuggerView.VariableBubble;
|
||||
let tooltip = bubble._tooltip.panel;
|
||||
|
||||
function openPopup(coords) {
|
||||
let popupshown = once(tooltip, "popupshown");
|
||||
let fetched = waitForDebuggerEvents(panel, events.FETCHED_BUBBLE_PROPERTIES);
|
||||
let { left, top } = editor.getCoordsFromPosition(coords);
|
||||
bubble._findIdentifier(left, top);
|
||||
return promise.all([popupshown, fetched]);
|
||||
}
|
||||
|
||||
function verifyContents() {
|
||||
is(tooltip.querySelectorAll(".variables-view-container").length, 1,
|
||||
"There should be one variables view container added to the tooltip.");
|
||||
|
||||
is(tooltip.querySelectorAll(".variables-view-scope[non-header]").length, 1,
|
||||
"There should be one scope with no header displayed.");
|
||||
is(tooltip.querySelectorAll(".variables-view-variable[non-header]").length, 1,
|
||||
"There should be one variable with no header displayed.");
|
||||
|
||||
is(tooltip.querySelectorAll(".variables-view-property").length, 7,
|
||||
"There should be 7 properties displayed.");
|
||||
|
||||
is(tooltip.querySelectorAll(".variables-view-property .name")[0].getAttribute("value"), "a",
|
||||
"The first property's name is correct.");
|
||||
is(tooltip.querySelectorAll(".variables-view-property .value")[0].getAttribute("value"), "1",
|
||||
"The first property's value is correct.");
|
||||
|
||||
is(tooltip.querySelectorAll(".variables-view-property .name")[1].getAttribute("value"), "b",
|
||||
"The second property's name is correct.");
|
||||
is(tooltip.querySelectorAll(".variables-view-property .value")[1].getAttribute("value"), "\"beta\"",
|
||||
"The second property's value is correct.");
|
||||
|
||||
is(tooltip.querySelectorAll(".variables-view-property .name")[2].getAttribute("value"), "c",
|
||||
"The third property's name is correct.");
|
||||
is(tooltip.querySelectorAll(".variables-view-property .value")[2].getAttribute("value"), "3",
|
||||
"The third property's value is correct.");
|
||||
|
||||
is(tooltip.querySelectorAll(".variables-view-property .name")[3].getAttribute("value"), "d",
|
||||
"The fourth property's name is correct.");
|
||||
is(tooltip.querySelectorAll(".variables-view-property .value")[3].getAttribute("value"), "false",
|
||||
"The fourth property's value is correct.");
|
||||
|
||||
is(tooltip.querySelectorAll(".variables-view-property .name")[4].getAttribute("value"), "e",
|
||||
"The fifth property's name is correct.");
|
||||
is(tooltip.querySelectorAll(".variables-view-property .value")[4].getAttribute("value"), "null",
|
||||
"The fifth property's value is correct.");
|
||||
|
||||
is(tooltip.querySelectorAll(".variables-view-property .name")[5].getAttribute("value"), "f",
|
||||
"The sixth property's name is correct.");
|
||||
is(tooltip.querySelectorAll(".variables-view-property .value")[5].getAttribute("value"), "undefined",
|
||||
"The sixth property's value is correct.");
|
||||
|
||||
is(tooltip.querySelectorAll(".variables-view-property .name")[6].getAttribute("value"), "__proto__",
|
||||
"The seventh property's name is correct.");
|
||||
is(tooltip.querySelectorAll(".variables-view-property .value")[6].getAttribute("value"), "Object",
|
||||
"The seventh property's value is correct.");
|
||||
}
|
||||
|
||||
// Allow this generator function to yield first.
|
||||
executeSoon(() => debuggee.start());
|
||||
yield waitForSourceAndCaretAndScopes(panel, ".html", 24);
|
||||
|
||||
// Inspect variable.
|
||||
yield openPopup({ line: 17, ch: 12 });
|
||||
verifyContents();
|
||||
|
||||
yield resumeDebuggerThenCloseAndFinish(panel);
|
||||
});
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests the variable inspection popup behaves correctly when switching
|
||||
* between simple and complex objects.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_frame-parameters.html";
|
||||
|
||||
function test() {
|
||||
Task.spawn(function() {
|
||||
let [tab, debuggee, panel] = yield initDebugger(TAB_URL);
|
||||
let win = panel.panelWin;
|
||||
let events = win.EVENTS;
|
||||
let editor = win.DebuggerView.editor;
|
||||
let bubble = win.DebuggerView.VariableBubble;
|
||||
let tooltip = bubble._tooltip.panel;
|
||||
|
||||
function openSimplePopup(coords) {
|
||||
let popupshown = once(tooltip, "popupshown");
|
||||
let { left, top } = editor.getCoordsFromPosition(coords);
|
||||
bubble._findIdentifier(left, top);
|
||||
return popupshown;
|
||||
}
|
||||
|
||||
function openComplexPopup(coords) {
|
||||
let popupshown = once(tooltip, "popupshown");
|
||||
let fetched = waitForDebuggerEvents(panel, events.FETCHED_BUBBLE_PROPERTIES);
|
||||
let { left, top } = editor.getCoordsFromPosition(coords);
|
||||
bubble._findIdentifier(left, top);
|
||||
return promise.all([popupshown, fetched]);
|
||||
}
|
||||
|
||||
function hidePopup() {
|
||||
let popuphiding = once(tooltip, "popuphiding");
|
||||
bubble.hideContents();
|
||||
return popuphiding.then(waitForTick);
|
||||
}
|
||||
|
||||
function verifySimpleContents(textContent, className) {
|
||||
is(tooltip.querySelectorAll(".variables-view-container").length, 0,
|
||||
"There should be no variables view container added to the tooltip.");
|
||||
is(tooltip.querySelectorAll(".devtools-tooltip-simple-text").length, 1,
|
||||
"There should be one simple text node added to the tooltip.");
|
||||
|
||||
is(tooltip.querySelector(".devtools-tooltip-simple-text").textContent, textContent,
|
||||
"The inspected property's value is correct.");
|
||||
ok(tooltip.querySelector(".devtools-tooltip-simple-text").className.contains(className),
|
||||
"The inspected property's value is colorized correctly.");
|
||||
}
|
||||
|
||||
function verifyComplexContents(propertyCount) {
|
||||
is(tooltip.querySelectorAll(".variables-view-container").length, 1,
|
||||
"There should be one variables view container added to the tooltip.");
|
||||
is(tooltip.querySelectorAll(".devtools-tooltip-simple-text").length, 0,
|
||||
"There should be no simple text node added to the tooltip.");
|
||||
|
||||
is(tooltip.querySelectorAll(".variables-view-scope[non-header]").length, 1,
|
||||
"There should be one scope with no header displayed.");
|
||||
is(tooltip.querySelectorAll(".variables-view-variable[non-header]").length, 1,
|
||||
"There should be one variable with no header displayed.");
|
||||
|
||||
ok(tooltip.querySelectorAll(".variables-view-property").length >= propertyCount,
|
||||
"There should be some properties displayed.");
|
||||
}
|
||||
|
||||
// Allow this generator function to yield first.
|
||||
executeSoon(() => debuggee.start());
|
||||
yield waitForSourceAndCaretAndScopes(panel, ".html", 24);
|
||||
|
||||
// Inspect variables.
|
||||
yield openSimplePopup({ line: 15, ch: 12 });
|
||||
verifySimpleContents("1", "token-number");
|
||||
|
||||
yield hidePopup().then(() => openComplexPopup({ line: 16, ch: 12 }));
|
||||
verifyComplexContents(2);
|
||||
|
||||
yield hidePopup().then(() => openSimplePopup({ line: 19, ch: 10 }));
|
||||
verifySimpleContents("42", "token-number");
|
||||
|
||||
yield hidePopup().then(() => openComplexPopup({ line: 31, ch: 10 }));
|
||||
verifyComplexContents(100);
|
||||
|
||||
yield resumeDebuggerThenCloseAndFinish(panel);
|
||||
});
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests opening inspecting variables works across scopes.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_scope-variable.html";
|
||||
|
||||
function test() {
|
||||
Task.spawn(function() {
|
||||
let [tab, debuggee, panel] = yield initDebugger(TAB_URL);
|
||||
let win = panel.panelWin;
|
||||
let events = win.EVENTS;
|
||||
let editor = win.DebuggerView.editor;
|
||||
let frames = win.DebuggerView.StackFrames;
|
||||
let bubble = win.DebuggerView.VariableBubble;
|
||||
let tooltip = bubble._tooltip.panel;
|
||||
|
||||
function openPopup(coords) {
|
||||
let popupshown = once(tooltip, "popupshown");
|
||||
let { left, top } = editor.getCoordsFromPosition(coords);
|
||||
bubble._findIdentifier(left, top);
|
||||
return popupshown;
|
||||
}
|
||||
|
||||
function hidePopup() {
|
||||
let popuphiding = once(tooltip, "popuphiding");
|
||||
bubble.hideContents();
|
||||
return popuphiding.then(waitForTick);
|
||||
}
|
||||
|
||||
function verifyContents(textContent, className) {
|
||||
is(tooltip.querySelectorAll(".variables-view-container").length, 0,
|
||||
"There should be no variables view containers added to the tooltip.");
|
||||
is(tooltip.querySelectorAll(".devtools-tooltip-simple-text").length, 1,
|
||||
"There should be a simple text node added to the tooltip instead.");
|
||||
|
||||
is(tooltip.querySelector(".devtools-tooltip-simple-text").textContent, textContent,
|
||||
"The inspected property's value is correct.");
|
||||
ok(tooltip.querySelector(".devtools-tooltip-simple-text").className.contains(className),
|
||||
"The inspected property's value is colorized correctly.");
|
||||
}
|
||||
|
||||
function checkView(selectedFrame, caretLine) {
|
||||
is(win.gThreadClient.state, "paused",
|
||||
"Should only be getting stack frames while paused.");
|
||||
is(frames.itemCount, 2,
|
||||
"Should have two frames.");
|
||||
is(frames.selectedDepth, selectedFrame,
|
||||
"The correct frame is selected in the widget.");
|
||||
ok(isCaretPos(panel, caretLine),
|
||||
"Editor caret location is correct.");
|
||||
}
|
||||
|
||||
// Allow this generator function to yield first.
|
||||
executeSoon(() => debuggee.test());
|
||||
yield waitForSourceAndCaretAndScopes(panel, ".html", 20);
|
||||
checkView(0, 20);
|
||||
|
||||
// Inspect variable in topmost frame.
|
||||
yield openPopup({ line: 18, ch: 12 });
|
||||
verifyContents("\"second scope\"", "token-string");
|
||||
checkView(0, 20);
|
||||
|
||||
// Change frame.
|
||||
let updatedFrame = waitForDebuggerEvents(panel, events.FETCHED_SCOPES);
|
||||
yield hidePopup().then(() => frames.selectedDepth = 1);
|
||||
yield updatedFrame;
|
||||
checkView(1, 15);
|
||||
|
||||
// Inspect variable in oldest frame.
|
||||
yield openPopup({ line: 13, ch: 12 });
|
||||
verifyContents("\"first scope\"", "token-string");
|
||||
checkView(1, 15);
|
||||
|
||||
yield resumeDebuggerThenCloseAndFinish(panel);
|
||||
});
|
||||
}
|
25
browser/devtools/debugger/test/doc_scope-variable.html
Normal file
25
browser/devtools/debugger/test/doc_scope-variable.html
Normal file
@ -0,0 +1,25 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Debugger test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
function test() {
|
||||
var a = "first scope";
|
||||
nest();
|
||||
}
|
||||
|
||||
function nest() {
|
||||
var a = "second scope";
|
||||
debugger;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -727,7 +727,7 @@ var Scratchpad = {
|
||||
ch: funcStatement.loc.end.column
|
||||
};
|
||||
|
||||
const marker = this.editor.markText(from, to, { className: "eval-text" });
|
||||
const marker = this.editor.markText(from, to, "eval-text");
|
||||
setTimeout(() => marker.clear(), EVAL_FUNCTION_TIMEOUT);
|
||||
|
||||
return this.evaluate(functionText);
|
||||
|
@ -542,7 +542,7 @@ let ShadersEditorsView = {
|
||||
|
||||
let tooltip = node._markerErrorsTooltip = new Tooltip(document);
|
||||
tooltip.defaultOffsetX = GUTTER_ERROR_PANEL_OFFSET_X;
|
||||
tooltip.setTextContent.apply(tooltip, messages);
|
||||
tooltip.setTextContent(messages);
|
||||
tooltip.startTogglingOnHover(node, () => true, GUTTER_ERROR_PANEL_DELAY);
|
||||
},
|
||||
|
||||
|
@ -32,15 +32,15 @@ function ifWebGLSupported() {
|
||||
let content = tooltip.content;
|
||||
ok(tooltip.content,
|
||||
"Some tooltip's content was set.");
|
||||
is(tooltip.content.className, "devtools-tooltip-simple-text-container",
|
||||
ok(tooltip.content.className.contains("devtools-tooltip-simple-text-container"),
|
||||
"The tooltip's content container was created correctly.");
|
||||
|
||||
let messages = content.childNodes;
|
||||
is(messages.length, 2,
|
||||
"There are two messages displayed in the tooltip.");
|
||||
is(messages[0].className, "devtools-tooltip-simple-text",
|
||||
ok(messages[0].className.contains("devtools-tooltip-simple-text"),
|
||||
"The first message was created correctly.");
|
||||
is(messages[1].className, "devtools-tooltip-simple-text",
|
||||
ok(messages[1].className.contains("devtools-tooltip-simple-text"),
|
||||
"The second message was created correctly.");
|
||||
|
||||
ok(messages[0].textContent.contains("'constructor' : too many arguments"),
|
||||
|
@ -13,26 +13,27 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this,
|
||||
"Reflect", "resource://gre/modules/reflect.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["Parser"];
|
||||
this.EXPORTED_SYMBOLS = ["Parser", "ParserHelpers", "SyntaxTreeVisitor"];
|
||||
|
||||
/**
|
||||
* A JS parser using the reflection API.
|
||||
*/
|
||||
this.Parser = function Parser() {
|
||||
this._cache = new Map();
|
||||
this.errors = [];
|
||||
};
|
||||
|
||||
Parser.prototype = {
|
||||
/**
|
||||
* Gets a collection of parser methods for a specified source.
|
||||
*
|
||||
* @param string aSource
|
||||
* The source text content.
|
||||
* @param string aUrl [optional]
|
||||
* The source url. The AST nodes will be cached, so you can use this
|
||||
* identifier to avoid parsing the whole source again.
|
||||
* @param string aSource
|
||||
* The source text content.
|
||||
*/
|
||||
get: function(aUrl, aSource) {
|
||||
get: function(aSource, aUrl = "") {
|
||||
// Try to use the cached AST nodes, to avoid useless parsing operations.
|
||||
if (this._cache.has(aUrl)) {
|
||||
return this._cache.get(aUrl);
|
||||
@ -63,6 +64,7 @@ Parser.prototype = {
|
||||
let length = aSource.length;
|
||||
syntaxTrees.push(new SyntaxTree(nodes, aUrl, length));
|
||||
} catch (e) {
|
||||
this.errors.push(e);
|
||||
log(aUrl, e);
|
||||
}
|
||||
}
|
||||
@ -76,13 +78,21 @@ Parser.prototype = {
|
||||
let length = script.length;
|
||||
syntaxTrees.push(new SyntaxTree(nodes, aUrl, length, offset));
|
||||
} catch (e) {
|
||||
this.errors.push(e);
|
||||
log(aUrl, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let pool = new SyntaxTreesPool(syntaxTrees);
|
||||
this._cache.set(aUrl, pool);
|
||||
|
||||
// Cache the syntax trees pool by the specified url. This is entirely
|
||||
// optional, but it's strongly encouraged to cache ASTs because
|
||||
// generating them can be costly with big/complex sources.
|
||||
if (aUrl) {
|
||||
this._cache.set(aUrl, pool);
|
||||
}
|
||||
|
||||
return pool;
|
||||
},
|
||||
|
||||
@ -103,7 +113,8 @@ Parser.prototype = {
|
||||
this._cache.delete(aUrl);
|
||||
},
|
||||
|
||||
_cache: null
|
||||
_cache: null,
|
||||
errors: null
|
||||
};
|
||||
|
||||
/**
|
||||
@ -118,6 +129,13 @@ function SyntaxTreesPool(aSyntaxTrees) {
|
||||
}
|
||||
|
||||
SyntaxTreesPool.prototype = {
|
||||
/**
|
||||
* @see SyntaxTree.prototype.getIdentifierAt
|
||||
*/
|
||||
getIdentifierAt: function(aLine, aColumn) {
|
||||
return this._first(this._call("getIdentifierAt", aLine, aColumn));
|
||||
},
|
||||
|
||||
/**
|
||||
* @see SyntaxTree.prototype.getNamedFunctionDefinitions
|
||||
*/
|
||||
@ -126,21 +144,41 @@ SyntaxTreesPool.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Finds the offset and length of the script containing the specified offset
|
||||
* Gets the total number of scripts in the parent source.
|
||||
* @return number
|
||||
*/
|
||||
get scriptCount() {
|
||||
return this._trees.length;
|
||||
},
|
||||
|
||||
/**
|
||||
* Finds the start and length of the script containing the specified offset
|
||||
* relative to its parent source.
|
||||
*
|
||||
* @param number aOffset
|
||||
* The offset relative to the parent source.
|
||||
* @return array
|
||||
* @return object
|
||||
* The offset and length relative to the enclosing script.
|
||||
*/
|
||||
getScriptInfo: function(aOffset) {
|
||||
for (let { offset, length } of this._trees) {
|
||||
if (offset <= aOffset && offset + length >= aOffset) {
|
||||
return [offset, length];
|
||||
return { start: offset, length: length };
|
||||
}
|
||||
}
|
||||
return [-1, -1];
|
||||
return { start: -1, length: -1 };
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the first script results from a source results set.
|
||||
* If no results are found, null is returned.
|
||||
*
|
||||
* @return array
|
||||
* A collection of parse results for the first script in a source.
|
||||
*/
|
||||
_first: function(aSourceResults) {
|
||||
let scriptResult = aSourceResults.filter(e => !!e.parseResults)[0];
|
||||
return scriptResult ? scriptResult.parseResults : null;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -153,9 +191,9 @@ SyntaxTreesPool.prototype = {
|
||||
* @return array
|
||||
* The results given by all known syntax trees.
|
||||
*/
|
||||
_call: function(aFunction, aParams) {
|
||||
_call: function(aFunction, ...aParams) {
|
||||
let results = [];
|
||||
let requestId = aFunction + aParams; // Cache all the things!
|
||||
let requestId = aFunction + aParams.toSource(); // Cache all the things!
|
||||
|
||||
if (this._cache.has(requestId)) {
|
||||
return this._cache.get(requestId);
|
||||
@ -166,7 +204,7 @@ SyntaxTreesPool.prototype = {
|
||||
sourceUrl: syntaxTree.url,
|
||||
scriptLength: syntaxTree.length,
|
||||
scriptOffset: syntaxTree.offset,
|
||||
parseResults: syntaxTree[aFunction](aParams)
|
||||
parseResults: syntaxTree[aFunction].apply(syntaxTree, aParams)
|
||||
});
|
||||
} catch (e) {
|
||||
// Can't guarantee that the tree traversal logic is forever perfect :)
|
||||
@ -203,6 +241,58 @@ function SyntaxTree(aNodes, aUrl, aLength, aOffset = 0) {
|
||||
};
|
||||
|
||||
SyntaxTree.prototype = {
|
||||
/**
|
||||
* Gets the identifier at the specified location.
|
||||
*
|
||||
* @param number aLine
|
||||
* The line in the source.
|
||||
* @param number aColumn
|
||||
* The column in the source.
|
||||
* @return object
|
||||
* An object containing identifier information as { name, location,
|
||||
* evalString } properties, or null if nothing is found.
|
||||
*/
|
||||
getIdentifierAt: function(aLine, aColumn) {
|
||||
let info = null;
|
||||
|
||||
SyntaxTreeVisitor.walk(this.AST, {
|
||||
/**
|
||||
* Callback invoked for each identifier node.
|
||||
* @param Node aNode
|
||||
*/
|
||||
onIdentifier: function(aNode) {
|
||||
if (ParserHelpers.nodeContainsPoint(aNode, aLine, aColumn)) {
|
||||
info = {
|
||||
name: aNode.name,
|
||||
location: ParserHelpers.getNodeLocation(aNode),
|
||||
evalString: ParserHelpers.getIdentifierEvalString(aNode)
|
||||
};
|
||||
|
||||
// Abruptly halt walking the syntax tree.
|
||||
SyntaxTreeVisitor.break = true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback invoked for each literal node.
|
||||
* @param Node aNode
|
||||
*/
|
||||
onLiteral: function(aNode) {
|
||||
this.onIdentifier(aNode);
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback invoked for each 'this' node.
|
||||
* @param Node aNode
|
||||
*/
|
||||
onThisExpression: function(aNode) {
|
||||
this.onIdentifier(aNode);
|
||||
}
|
||||
});
|
||||
|
||||
return info;
|
||||
},
|
||||
|
||||
/**
|
||||
* Searches for all function definitions (declarations and expressions)
|
||||
* whose names (or inferred names) contain a string.
|
||||
@ -228,7 +318,7 @@ SyntaxTree.prototype = {
|
||||
if (functionName.toLowerCase().contains(lowerCaseToken)) {
|
||||
store.push({
|
||||
functionName: functionName,
|
||||
functionLocation: aNode.loc
|
||||
functionLocation: ParserHelpers.getNodeLocation(aNode)
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -240,7 +330,7 @@ SyntaxTree.prototype = {
|
||||
onFunctionExpression: function(aNode) {
|
||||
// Function expressions don't necessarily have a name.
|
||||
let functionName = aNode.id ? aNode.id.name : "";
|
||||
let functionLocation = aNode.loc || null;
|
||||
let functionLocation = ParserHelpers.getNodeLocation(aNode);
|
||||
|
||||
// Infer the function's name from an enclosing syntax tree node.
|
||||
let inferredInfo = ParserHelpers.inferFunctionExpressionInfo(aNode);
|
||||
@ -304,6 +394,56 @@ SyntaxTree.prototype = {
|
||||
* Parser utility methods.
|
||||
*/
|
||||
let ParserHelpers = {
|
||||
/**
|
||||
* Gets the location information for a node. Not all nodes have a
|
||||
* location property directly attached, or the location information
|
||||
* is incorrect, in which cases it's accessible via the parent.
|
||||
*
|
||||
* @param Node aNode
|
||||
* The node who's location needs to be retrieved.
|
||||
* @return object
|
||||
* An object containing { line, column } information.
|
||||
*/
|
||||
getNodeLocation: function(aNode) {
|
||||
if (aNode.type != "Identifier") {
|
||||
return aNode.loc;
|
||||
}
|
||||
// Work around the fact that some identifier nodes don't have the
|
||||
// correct location attached.
|
||||
let { loc: parentLocation, type: parentType } = aNode._parent;
|
||||
let { loc: nodeLocation } = aNode;
|
||||
if (!nodeLocation) {
|
||||
if (parentType == "FunctionDeclaration" ||
|
||||
parentType == "FunctionExpression") {
|
||||
// e.g. "function foo() {}" or "{ bar: function foo() {} }"
|
||||
// The location is unavailable for the identifier node "foo".
|
||||
let loc = JSON.parse(JSON.stringify(parentLocation));
|
||||
loc.end.line = loc.start.line;
|
||||
loc.end.column = loc.start.column + aNode.name.length;
|
||||
return loc;
|
||||
}
|
||||
if (parentType == "MemberExpression") {
|
||||
// e.g. "foo.bar"
|
||||
// The location is unavailable for the identifier node "bar".
|
||||
let loc = JSON.parse(JSON.stringify(parentLocation));
|
||||
loc.start.line = loc.end.line;
|
||||
loc.start.column = loc.end.column - aNode.name.length;
|
||||
return loc;
|
||||
}
|
||||
} else {
|
||||
if (parentType == "VariableDeclarator") {
|
||||
// e.g. "let foo = 42"
|
||||
// The location incorrectly spans across the whole variable declaration,
|
||||
// not just the identifier node "foo".
|
||||
let loc = JSON.parse(JSON.stringify(nodeLocation));
|
||||
loc.end.line = loc.start.line;
|
||||
loc.end.column = loc.start.column + aNode.name.length;
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
return aNode.loc;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if a node's bounds contains a specified line.
|
||||
*
|
||||
@ -314,12 +454,9 @@ let ParserHelpers = {
|
||||
* @return boolean
|
||||
* True if the line and column is contained in the node's bounds.
|
||||
*/
|
||||
isWithinLines: function(aNode, aLine) {
|
||||
// Not all nodes have location information attached.
|
||||
if (!aNode.loc) {
|
||||
return this.isWithinLines(aNode._parent, aLine);
|
||||
}
|
||||
return aNode.loc.start.line <= aLine && aNode.loc.end.line >= aLine;
|
||||
nodeContainsLine: function(aNode, aLine) {
|
||||
let { start: s, end: e } = this.getNodeLocation(aNode);
|
||||
return s.line <= aLine && e.line >= aLine;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -334,13 +471,10 @@ let ParserHelpers = {
|
||||
* @return boolean
|
||||
* True if the line and column is contained in the node's bounds.
|
||||
*/
|
||||
isWithinBounds: function(aNode, aLine, aColumn) {
|
||||
// Not all nodes have location information attached.
|
||||
if (!aNode.loc) {
|
||||
return this.isWithinBounds(aNode._parent, aLine, aColumn);
|
||||
}
|
||||
return aNode.loc.start.line == aLine && aNode.loc.end.line == aLine &&
|
||||
aNode.loc.start.column <= aColumn && aNode.loc.end.column >= aColumn;
|
||||
nodeContainsPoint: function(aNode, aLine, aColumn) {
|
||||
let { start: s, end: e } = this.getNodeLocation(aNode);
|
||||
return s.line == aLine && e.line == aLine &&
|
||||
s.column <= aColumn && e.column >= aColumn;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -364,7 +498,7 @@ let ParserHelpers = {
|
||||
return {
|
||||
name: parent.id.name,
|
||||
chain: null,
|
||||
loc: parent.loc
|
||||
loc: this.getNodeLocation(parent.id)
|
||||
};
|
||||
}
|
||||
|
||||
@ -372,12 +506,12 @@ let ParserHelpers = {
|
||||
// e.g. foo = function(){} or foo.bar = function(){}, in which case it is
|
||||
// possible to infer the assignee name ("foo" and "bar" respectively).
|
||||
if (parent.type == "AssignmentExpression") {
|
||||
let propertyChain = this.getMemberExpressionPropertyChain(parent.left);
|
||||
let propertyChain = this._getMemberExpressionPropertyChain(parent.left);
|
||||
let propertyLeaf = propertyChain.pop();
|
||||
return {
|
||||
name: propertyLeaf,
|
||||
chain: propertyChain,
|
||||
loc: parent.left.loc
|
||||
loc: this.getNodeLocation(parent.left)
|
||||
};
|
||||
}
|
||||
|
||||
@ -385,13 +519,13 @@ let ParserHelpers = {
|
||||
// e.g. { foo: function(){} }, then it is possible to infer the name
|
||||
// from the corresponding property.
|
||||
if (parent.type == "ObjectExpression") {
|
||||
let propertyKey = this.getObjectExpressionPropertyKeyForValue(aNode);
|
||||
let propertyChain = this.getObjectExpressionPropertyChain(parent);
|
||||
let propertyKey = this._getObjectExpressionPropertyKeyForValue(aNode);
|
||||
let propertyChain = this._getObjectExpressionPropertyChain(parent);
|
||||
let propertyLeaf = propertyKey.name;
|
||||
return {
|
||||
name: propertyLeaf,
|
||||
chain: propertyChain,
|
||||
loc: propertyKey.loc
|
||||
loc: this.getNodeLocation(propertyKey)
|
||||
};
|
||||
}
|
||||
|
||||
@ -407,6 +541,9 @@ let ParserHelpers = {
|
||||
* Gets the name of an object expression's property to which a specified
|
||||
* value is assigned.
|
||||
*
|
||||
* Used for inferring function expression information and retrieving
|
||||
* an identifier evaluation string.
|
||||
*
|
||||
* For example, if aNode represents the "bar" identifier in a hypothetical
|
||||
* "{ foo: bar }" object expression, the returned node is the "foo" identifier.
|
||||
*
|
||||
@ -415,7 +552,7 @@ let ParserHelpers = {
|
||||
* @return object
|
||||
* The key identifier node in the object expression.
|
||||
*/
|
||||
getObjectExpressionPropertyKeyForValue: function(aNode) {
|
||||
_getObjectExpressionPropertyKeyForValue: function(aNode) {
|
||||
let parent = aNode._parent;
|
||||
if (parent.type != "ObjectExpression") {
|
||||
return null;
|
||||
@ -431,6 +568,9 @@ let ParserHelpers = {
|
||||
* Gets an object expression's property chain to its parent
|
||||
* variable declarator or assignment expression, if available.
|
||||
*
|
||||
* Used for inferring function expression information and retrieving
|
||||
* an identifier evaluation string.
|
||||
*
|
||||
* For example, if aNode represents the "baz: {}" object expression in a
|
||||
* hypothetical "foo = { bar: { baz: {} } }" assignment expression, the
|
||||
* returned chain is ["foo", "bar", "baz"].
|
||||
@ -442,11 +582,11 @@ let ParserHelpers = {
|
||||
* @return array
|
||||
* The chain to the parent variable declarator, as strings.
|
||||
*/
|
||||
getObjectExpressionPropertyChain: function(aNode, aStore = []) {
|
||||
_getObjectExpressionPropertyChain: function(aNode, aStore = []) {
|
||||
switch (aNode.type) {
|
||||
case "ObjectExpression":
|
||||
this.getObjectExpressionPropertyChain(aNode._parent, aStore);
|
||||
let propertyKey = this.getObjectExpressionPropertyKeyForValue(aNode);
|
||||
this._getObjectExpressionPropertyChain(aNode._parent, aStore);
|
||||
let propertyKey = this._getObjectExpressionPropertyKeyForValue(aNode);
|
||||
if (propertyKey) {
|
||||
aStore.push(propertyKey.name);
|
||||
}
|
||||
@ -459,14 +599,14 @@ let ParserHelpers = {
|
||||
// commonly used when defining an object's prototype methods; e.g:
|
||||
// "Foo.prototype = { ... }".
|
||||
case "AssignmentExpression":
|
||||
this.getMemberExpressionPropertyChain(aNode.left, aStore);
|
||||
this._getMemberExpressionPropertyChain(aNode.left, aStore);
|
||||
break;
|
||||
// Additionally handle stuff like "foo = bar.baz({ ... })", because it's
|
||||
// commonly used in prototype-based inheritance in many libraries; e.g:
|
||||
// "Foo = Bar.extend({ ... })".
|
||||
case "NewExpression":
|
||||
case "CallExpression":
|
||||
this.getObjectExpressionPropertyChain(aNode._parent, aStore);
|
||||
this._getObjectExpressionPropertyChain(aNode._parent, aStore);
|
||||
break;
|
||||
}
|
||||
return aStore;
|
||||
@ -475,6 +615,9 @@ let ParserHelpers = {
|
||||
/**
|
||||
* Gets a member expression's property chain.
|
||||
*
|
||||
* Used for inferring function expression information and retrieving
|
||||
* an identifier evaluation string.
|
||||
*
|
||||
* For example, if aNode represents a hypothetical "foo.bar.baz"
|
||||
* member expression, the returned chain ["foo", "bar", "baz"].
|
||||
*
|
||||
@ -487,22 +630,65 @@ let ParserHelpers = {
|
||||
* @return array
|
||||
* The full member chain, as strings.
|
||||
*/
|
||||
getMemberExpressionPropertyChain: function(aNode, aStore = []) {
|
||||
_getMemberExpressionPropertyChain: function(aNode, aStore = []) {
|
||||
switch (aNode.type) {
|
||||
case "MemberExpression":
|
||||
this.getMemberExpressionPropertyChain(aNode.object, aStore);
|
||||
this.getMemberExpressionPropertyChain(aNode.property, aStore);
|
||||
this._getMemberExpressionPropertyChain(aNode.object, aStore);
|
||||
this._getMemberExpressionPropertyChain(aNode.property, aStore);
|
||||
break;
|
||||
case "ThisExpression":
|
||||
// Such expressions may appear in an assignee chain, for example
|
||||
// "this.foo.bar = baz", however it seems better to ignore such nodes
|
||||
// and limit the chain to ["foo", "bar"].
|
||||
aStore.push("this");
|
||||
break;
|
||||
case "Identifier":
|
||||
aStore.push(aNode.name);
|
||||
break;
|
||||
}
|
||||
return aStore;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an evaluation string which can be used to obtain the
|
||||
* current value for the respective identifier.
|
||||
*
|
||||
* @param Node aNode
|
||||
* The leaf node (e.g. Identifier, Literal) to begin the scan from.
|
||||
* @return string
|
||||
* The corresponding evaluation string, or empty string if
|
||||
* the specified leaf node can't be used.
|
||||
*/
|
||||
getIdentifierEvalString: function(aNode) {
|
||||
switch (aNode._parent.type) {
|
||||
case "ObjectExpression":
|
||||
// If the identifier is the actual property value, it can be used
|
||||
// directly as an evaluation string. Otherwise, construct the property
|
||||
// access chain, since the value might have changed.
|
||||
if (!this._getObjectExpressionPropertyKeyForValue(aNode)) {
|
||||
let propertyChain = this._getObjectExpressionPropertyChain(aNode._parent);
|
||||
let propertyLeaf = aNode.name;
|
||||
return [...propertyChain, propertyLeaf].join(".");
|
||||
}
|
||||
break;
|
||||
case "MemberExpression":
|
||||
// Make sure this is a property identifier, not the parent object.
|
||||
if (aNode._parent.property == aNode) {
|
||||
return this._getMemberExpressionPropertyChain(aNode._parent).join(".");
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch (aNode.type) {
|
||||
case "ThisExpression":
|
||||
return "this";
|
||||
case "Identifier":
|
||||
return aNode.name;
|
||||
case "Literal":
|
||||
if (typeof aNode.value == "string") {
|
||||
return "\"" + aNode.value + "\"";
|
||||
} else {
|
||||
return aNode.value + "";
|
||||
}
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -527,9 +713,26 @@ let SyntaxTreeVisitor = {
|
||||
* types of noes (e.g: onFunctionDeclaration, onBlockStatement etc.).
|
||||
*/
|
||||
walk: function(aTree, aCallbacks) {
|
||||
this.break = false;
|
||||
this[aTree.type](aTree, aCallbacks);
|
||||
},
|
||||
|
||||
/**
|
||||
* Filters all the nodes in this syntax tree based on a predicate.
|
||||
*
|
||||
* @param object aTree
|
||||
* The AST nodes generated by the reflection API
|
||||
* @param function aPredicate
|
||||
* The predicate ran on each node.
|
||||
* @return array
|
||||
* An array of nodes validating the predicate.
|
||||
*/
|
||||
filter: function(aTree, aPredicate) {
|
||||
let store = [];
|
||||
this.walk(aTree, { onNode: e => { if (aPredicate(e)) store.push(e); } });
|
||||
return store;
|
||||
},
|
||||
|
||||
/**
|
||||
* A flag checked on each node in the syntax tree. If true, walking is
|
||||
* abruptly halted.
|
||||
@ -2121,4 +2324,4 @@ function log(aStr, aEx) {
|
||||
dump(msg + "\n");
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(Parser, "reflectionAPI", function() Reflect);
|
||||
XPCOMUtils.defineLazyGetter(Parser, "reflectionAPI", () => Reflect);
|
||||
|
@ -14,7 +14,16 @@ const {colorUtils} = require("devtools/css-color");
|
||||
const Heritage = require("sdk/core/heritage");
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "setNamedTimeout",
|
||||
"resource:///modules/devtools/ViewHelpers.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "clearNamedTimeout",
|
||||
"resource:///modules/devtools/ViewHelpers.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "VariablesView",
|
||||
"resource:///modules/devtools/VariablesView.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "VariablesViewController",
|
||||
"resource:///modules/devtools/VariablesViewController.jsm");
|
||||
|
||||
const GRADIENT_RE = /\b(repeating-)?(linear|radial)-gradient\(((rgb|hsl)a?\(.+?\)|[^\)])+\)/gi;
|
||||
const BORDERCOLOR_RE = /^border-[-a-z]*color$/ig;
|
||||
@ -24,7 +33,6 @@ const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
const SPECTRUM_FRAME = "chrome://browser/content/devtools/spectrum-frame.xhtml";
|
||||
const ESCAPE_KEYCODE = Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE;
|
||||
const ENTER_KEYCODE = Ci.nsIDOMKeyEvent.DOM_VK_RETURN;
|
||||
const SHOW_TIMEOUT = 50;
|
||||
|
||||
/**
|
||||
* Tooltip widget.
|
||||
@ -185,8 +193,9 @@ module.exports.Tooltip = Tooltip;
|
||||
|
||||
Tooltip.prototype = {
|
||||
defaultPosition: "before_start",
|
||||
defaultOffsetX: 0,
|
||||
defaultOffsetY: 0,
|
||||
defaultOffsetX: 0, // px
|
||||
defaultOffsetY: 0, // px
|
||||
defaultShowDelay: 50, // ms
|
||||
|
||||
/**
|
||||
* Show the tooltip. It might be wise to append some content first if you
|
||||
@ -194,13 +203,11 @@ Tooltip.prototype = {
|
||||
* tooltip by setting a XUL node to t.content.
|
||||
* @param {node} anchor
|
||||
* Which node should the tooltip be shown on
|
||||
* @param {string} position
|
||||
* @param {string} position [optional]
|
||||
* Optional tooltip position. Defaults to before_start
|
||||
* https://developer.mozilla.org/en-US/docs/XUL/PopupGuide/Positioning
|
||||
* @param {number} x
|
||||
* Optional x offset. Defaults to 0
|
||||
* @param {number} y
|
||||
* Optional y offset. Defaults to 0
|
||||
* @param {number} x, y [optional]
|
||||
* The left and top offset coordinates, in pixels.
|
||||
*/
|
||||
show: function(anchor,
|
||||
position = this.defaultPosition,
|
||||
@ -231,6 +238,22 @@ Tooltip.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets this panel's visibility state.
|
||||
* @return boolean
|
||||
*/
|
||||
isHidden: function() {
|
||||
return this.panel.state == "closed" || this.panel.state == "hiding";
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets if this panel has any child nodes.
|
||||
* @return boolean
|
||||
*/
|
||||
isEmpty: function() {
|
||||
return !this.panel.hasChildNodes();
|
||||
},
|
||||
|
||||
/**
|
||||
* Get rid of references and event listeners
|
||||
*/
|
||||
@ -253,7 +276,7 @@ Tooltip.prototype = {
|
||||
|
||||
this.doc = null;
|
||||
|
||||
this.panel.parentNode.removeChild(this.panel);
|
||||
this.panel.remove();
|
||||
this.panel = null;
|
||||
},
|
||||
|
||||
@ -287,9 +310,9 @@ Tooltip.prototype = {
|
||||
* tooltip if needed. If omitted, the tooltip will be shown everytime.
|
||||
* @param {Number} showDelay
|
||||
* An optional delay that will be observed before showing the tooltip.
|
||||
* Defaults to SHOW_TIMEOUT
|
||||
* Defaults to this.defaultShowDelay.
|
||||
*/
|
||||
startTogglingOnHover: function(baseNode, targetNodeCb, showDelay=SHOW_TIMEOUT) {
|
||||
startTogglingOnHover: function(baseNode, targetNodeCb, showDelay = this.defaultShowDelay) {
|
||||
if (this._basedNode) {
|
||||
this.stopTogglingOnHover();
|
||||
}
|
||||
@ -353,7 +376,13 @@ Tooltip.prototype = {
|
||||
* A node that can be appended in the tooltip XUL element
|
||||
*/
|
||||
set content(content) {
|
||||
if (this.content == content) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.empty();
|
||||
this.panel.removeAttribute("clamped-dimensions");
|
||||
|
||||
if (content) {
|
||||
this.panel.appendChild(content);
|
||||
}
|
||||
@ -366,18 +395,25 @@ Tooltip.prototype = {
|
||||
/**
|
||||
* Sets some text as the content of this tooltip.
|
||||
*
|
||||
* @param {string[]} messages
|
||||
* @param {array} messages
|
||||
* A list of text messages.
|
||||
* @param {string} messagesClass [optional]
|
||||
* A style class for the text messages.
|
||||
* @param {string} containerClass [optional]
|
||||
* A style class for the text messages container.
|
||||
*/
|
||||
setTextContent: function(...messages) {
|
||||
setTextContent: function(messages,
|
||||
messagesClass = "default-tooltip-simple-text-colors",
|
||||
containerClass = "default-tooltip-simple-text-colors") {
|
||||
|
||||
let vbox = this.doc.createElement("vbox");
|
||||
vbox.className = "devtools-tooltip-simple-text-container";
|
||||
vbox.className = "devtools-tooltip-simple-text-container " + containerClass;
|
||||
vbox.setAttribute("flex", "1");
|
||||
|
||||
for (let text of messages) {
|
||||
let description = this.doc.createElement("description");
|
||||
description.setAttribute("flex", "1");
|
||||
description.className = "devtools-tooltip-simple-text";
|
||||
description.className = "devtools-tooltip-simple-text " + messagesClass;
|
||||
description.textContent = text;
|
||||
vbox.appendChild(description);
|
||||
}
|
||||
@ -385,6 +421,63 @@ Tooltip.prototype = {
|
||||
this.content = vbox;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fill the tooltip with a variables view, inspecting an object via its
|
||||
* corresponding object actor, as specified in the remote debugging protocol.
|
||||
*
|
||||
* @param {object} objectActor
|
||||
* The value grip for the object actor.
|
||||
* @param {object} viewOptions [optional]
|
||||
* Options for the variables view visualization.
|
||||
* @param {object} controllerOptions [optional]
|
||||
* Options for the variables view controller.
|
||||
* @param {object} relayEvents [optional]
|
||||
* A collection of events to listen on the variables view widget.
|
||||
* For example, { fetched: () => ... }
|
||||
* @param {boolean} reuseCachedWidget [optional]
|
||||
* Pass false to instantiate a brand new widget for this variable.
|
||||
* Otherwise, if a variable was previously inspected, its widget
|
||||
* will be reused.
|
||||
*/
|
||||
setVariableContent: function(
|
||||
objectActor,
|
||||
viewOptions = {},
|
||||
controllerOptions = {},
|
||||
relayEvents = {},
|
||||
reuseCachedWidget = true) {
|
||||
|
||||
if (reuseCachedWidget && this._cachedVariablesView) {
|
||||
var [vbox, widget] = this._cachedVariablesView;
|
||||
} else {
|
||||
var vbox = this.doc.createElement("vbox");
|
||||
vbox.className = "devtools-tooltip-variables-view-box";
|
||||
vbox.setAttribute("flex", "1");
|
||||
|
||||
let innerbox = this.doc.createElement("vbox");
|
||||
innerbox.className = "devtools-tooltip-variables-view-innerbox";
|
||||
innerbox.setAttribute("flex", "1");
|
||||
vbox.appendChild(innerbox);
|
||||
|
||||
var widget = new VariablesView(innerbox, viewOptions);
|
||||
for (let e in relayEvents) widget.on(e, relayEvents[e]);
|
||||
VariablesViewController.attach(widget, controllerOptions);
|
||||
|
||||
this._cachedVariablesView = [vbox, widget];
|
||||
}
|
||||
|
||||
// Some of the view options are allowed to change between uses.
|
||||
widget.searchPlaceholder = viewOptions.searchPlaceholder;
|
||||
widget.searchEnabled = viewOptions.searchEnabled;
|
||||
|
||||
// Use the object actor's grip to display it as a variable in the widget.
|
||||
// The controller options are allowed to change between uses.
|
||||
widget.controller.setSingleVariable(
|
||||
{ objectActor: objectActor }, controllerOptions);
|
||||
|
||||
this.content = vbox;
|
||||
this.panel.setAttribute("clamped-dimensions", "");
|
||||
},
|
||||
|
||||
/**
|
||||
* Fill the tooltip with an image, displayed over a tiled background useful
|
||||
* for transparent images. Also adds the image dimension as a label at the
|
||||
|
@ -90,7 +90,6 @@ this.VariablesView = function VariablesView(aParentNode, aFlags = {}) {
|
||||
this._list.addEventListener("keypress", this._onViewKeyPress, false);
|
||||
this._list.addEventListener("keydown", this._onViewKeyDown, false);
|
||||
this._parent.appendChild(this._list);
|
||||
this._boxObject = this._list.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
|
||||
|
||||
for (let name in aFlags) {
|
||||
this[name] = aFlags[name];
|
||||
@ -196,7 +195,6 @@ VariablesView.prototype = {
|
||||
|
||||
this._parent.removeChild(prevList);
|
||||
this._parent.appendChild(currList);
|
||||
this._boxObject = currList.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
|
||||
|
||||
if (!this._store.length) {
|
||||
this._appendEmptyNotice();
|
||||
@ -414,7 +412,7 @@ VariablesView.prototype = {
|
||||
return;
|
||||
}
|
||||
let document = this.document;
|
||||
let ownerView = this._parent.parentNode;
|
||||
let ownerNode = this._parent.parentNode;
|
||||
|
||||
let container = this._searchboxContainer = document.createElement("hbox");
|
||||
container.className = "devtools-toolbar";
|
||||
@ -432,7 +430,7 @@ VariablesView.prototype = {
|
||||
searchbox.addEventListener("keypress", this._onSearchboxKeyPress, false);
|
||||
|
||||
container.appendChild(searchbox);
|
||||
ownerView.insertBefore(container, this._parent);
|
||||
ownerNode.insertBefore(container, this._parent);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -740,7 +738,7 @@ VariablesView.prototype = {
|
||||
aItem.collapse();
|
||||
}
|
||||
aItem._target.focus();
|
||||
this._boxObject.ensureElementIsVisible(aItem._arrow);
|
||||
this.boxObject.ensureElementIsVisible(aItem._arrow);
|
||||
return true;
|
||||
},
|
||||
|
||||
@ -926,6 +924,12 @@ VariablesView.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the parent node holding this view.
|
||||
* @return nsIDOMNode
|
||||
*/
|
||||
get boxObject() this._list.boxObject.QueryInterface(Ci.nsIScrollBoxObject),
|
||||
|
||||
/**
|
||||
* Gets the parent node holding this view.
|
||||
* @return nsIDOMNode
|
||||
@ -954,7 +958,6 @@ VariablesView.prototype = {
|
||||
_nonEnumVisible: true,
|
||||
_parent: null,
|
||||
_list: null,
|
||||
_boxObject: null,
|
||||
_searchboxNode: null,
|
||||
_searchboxContainer: null,
|
||||
_searchboxPlaceholder: "",
|
||||
@ -1056,7 +1059,8 @@ VariablesView.getterOrSetterEvalMacro = function(aItem, aCurrentString, aPrefix
|
||||
// morph it into a plain value.
|
||||
if ((type == "set" && propertyObject.getter.type == "undefined") ||
|
||||
(type == "get" && propertyObject.setter.type == "undefined")) {
|
||||
// Make sure the right getter/setter to value override macro is applied to the target object.
|
||||
// Make sure the right getter/setter to value override macro is applied
|
||||
// to the target object.
|
||||
return propertyObject.evaluationMacro(propertyObject, "undefined", aPrefix);
|
||||
}
|
||||
|
||||
@ -2602,7 +2606,7 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
|
||||
// Replace the specified label with a textbox input element.
|
||||
aLabel.parentNode.replaceChild(input, aLabel);
|
||||
this._variablesView._boxObject.ensureElementIsVisible(input);
|
||||
this._variablesView.boxObject.ensureElementIsVisible(input);
|
||||
input.select();
|
||||
|
||||
// When the value is a string (displayed as "value"), then we probably want
|
||||
@ -2636,7 +2640,7 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
*/
|
||||
_deactivateInput: function(aLabel, aInput, aCallbacks) {
|
||||
aInput.parentNode.replaceChild(aLabel, aInput);
|
||||
this._variablesView._boxObject.scrollBy(-this._target.clientWidth, 0);
|
||||
this._variablesView.boxObject.scrollBy(-this._target.clientWidth, 0);
|
||||
|
||||
aInput.removeEventListener("keypress", aCallbacks.onKeypress, false);
|
||||
aInput.removeEventListener("blur", aCallbacks.onBlur, false);
|
||||
|
@ -46,31 +46,19 @@ this.EXPORTED_SYMBOLS = ["VariablesViewController", "StackFrameUtils"];
|
||||
* The view to attach to.
|
||||
* @param object aOptions [optional]
|
||||
* Options for configuring the controller. Supported options:
|
||||
* - getObjectClient: callback for creating an object grip client
|
||||
* - getLongStringClient: callback for creating a long string grip client
|
||||
* - getEnvironmentClient: callback for creating an environment client
|
||||
* - releaseActor: callback for releasing an actor when it's no longer needed
|
||||
* - overrideValueEvalMacro: callback for creating an overriding eval macro
|
||||
* - getterOrSetterEvalMacro: callback for creating a getter/setter eval macro
|
||||
* - simpleValueEvalMacro: callback for creating a simple value eval macro
|
||||
* - getObjectClient: @see this._setClientGetters
|
||||
* - getLongStringClient: @see this._setClientGetters
|
||||
* - getEnvironmentClient: @see this._setClientGetters
|
||||
* - releaseActor: @see this._setClientGetters
|
||||
* - overrideValueEvalMacro: @see _setEvaluationMacros
|
||||
* - getterOrSetterEvalMacro: @see _setEvaluationMacros
|
||||
* - simpleValueEvalMacro: @see _setEvaluationMacros
|
||||
*/
|
||||
function VariablesViewController(aView, aOptions = {}) {
|
||||
this.addExpander = this.addExpander.bind(this);
|
||||
|
||||
this._getObjectClient = aOptions.getObjectClient;
|
||||
this._getLongStringClient = aOptions.getLongStringClient;
|
||||
this._getEnvironmentClient = aOptions.getEnvironmentClient;
|
||||
this._releaseActor = aOptions.releaseActor;
|
||||
|
||||
if (aOptions.overrideValueEvalMacro) {
|
||||
this._overrideValueEvalMacro = aOptions.overrideValueEvalMacro;
|
||||
}
|
||||
if (aOptions.getterOrSetterEvalMacro) {
|
||||
this._getterOrSetterEvalMacro = aOptions.getterOrSetterEvalMacro;
|
||||
}
|
||||
if (aOptions.simpleValueEvalMacro) {
|
||||
this._simpleValueEvalMacro = aOptions.simpleValueEvalMacro;
|
||||
}
|
||||
this._setClientGetters(aOptions);
|
||||
this._setEvaluationMacros(aOptions);
|
||||
|
||||
this._actors = new Set();
|
||||
this.view = aView;
|
||||
@ -93,6 +81,52 @@ VariablesViewController.prototype = {
|
||||
*/
|
||||
_simpleValueEvalMacro: VariablesView.simpleValueEvalMacro,
|
||||
|
||||
/**
|
||||
* Set the functions used to retrieve debugger client grips.
|
||||
*
|
||||
* @param object aOptions
|
||||
* Options for getting the client grips. Supported options:
|
||||
* - getObjectClient: callback for creating an object grip client
|
||||
* - getLongStringClient: callback for creating a long string grip client
|
||||
* - getEnvironmentClient: callback for creating an environment client
|
||||
* - releaseActor: callback for releasing an actor when it's no longer needed
|
||||
*/
|
||||
_setClientGetters: function(aOptions) {
|
||||
if (aOptions.getObjectClient) {
|
||||
this._getObjectClient = aOptions.getObjectClient;
|
||||
}
|
||||
if (aOptions.getLongStringClient) {
|
||||
this._getLongStringClient = aOptions.getLongStringClient;
|
||||
}
|
||||
if (aOptions.getEnvironmentClient) {
|
||||
this._getEnvironmentClient = aOptions.getEnvironmentClient;
|
||||
}
|
||||
if (aOptions.releaseActor) {
|
||||
this._releaseActor = aOptions.releaseActor;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the functions used when evaluating strings in the variables view.
|
||||
*
|
||||
* @param object aOptions
|
||||
* Options for configuring the macros. Supported options:
|
||||
* - overrideValueEvalMacro: callback for creating an overriding eval macro
|
||||
* - getterOrSetterEvalMacro: callback for creating a getter/setter eval macro
|
||||
* - simpleValueEvalMacro: callback for creating a simple value eval macro
|
||||
*/
|
||||
_setEvaluationMacros: function(aOptions) {
|
||||
if (aOptions.overrideValueEvalMacro) {
|
||||
this._overrideValueEvalMacro = aOptions.overrideValueEvalMacro;
|
||||
}
|
||||
if (aOptions.getterOrSetterEvalMacro) {
|
||||
this._getterOrSetterEvalMacro = aOptions.getterOrSetterEvalMacro;
|
||||
}
|
||||
if (aOptions.simpleValueEvalMacro) {
|
||||
this._simpleValueEvalMacro = aOptions.simpleValueEvalMacro;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Populate a long string into a target using a grip.
|
||||
*
|
||||
@ -137,6 +171,7 @@ VariablesViewController.prototype = {
|
||||
*/
|
||||
_populateFromObject: function(aTarget, aGrip) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
// Mark the specified variable as having retrieved all its properties.
|
||||
let finish = variable => {
|
||||
variable._retrieved = true;
|
||||
@ -147,7 +182,7 @@ VariablesViewController.prototype = {
|
||||
let objectClient = this._getObjectClient(aGrip);
|
||||
objectClient.getPrototypeAndProperties(aResponse => {
|
||||
let { ownProperties, prototype } = aResponse;
|
||||
// safeGetterValues is new and isn't necessary defined on old actors
|
||||
// 'safeGetterValues' is new and isn't necessary defined on old actors.
|
||||
let safeGetterValues = aResponse.safeGetterValues || {};
|
||||
let sortable = VariablesView.isSortable(aGrip.class);
|
||||
|
||||
@ -155,9 +190,9 @@ VariablesViewController.prototype = {
|
||||
// in VariablesView.
|
||||
for (let name of Object.keys(safeGetterValues)) {
|
||||
if (name in ownProperties) {
|
||||
ownProperties[name].getterValue = safeGetterValues[name].getterValue;
|
||||
ownProperties[name].getterPrototypeLevel = safeGetterValues[name]
|
||||
.getterPrototypeLevel;
|
||||
let { getterValue, getterPrototypeLevel } = safeGetterValues[name];
|
||||
ownProperties[name].getterValue = getterValue;
|
||||
ownProperties[name].getterPrototypeLevel = getterPrototypeLevel;
|
||||
} else {
|
||||
ownProperties[name] = safeGetterValues[name];
|
||||
}
|
||||
@ -180,13 +215,16 @@ VariablesViewController.prototype = {
|
||||
this.addExpander(proto, prototype);
|
||||
}
|
||||
|
||||
// If the object is a function we need to fetch its scope chain.
|
||||
// If the object is a function we need to fetch its scope chain
|
||||
// to show them as closures for the respective function.
|
||||
if (aGrip.class == "Function") {
|
||||
objectClient.getScope(aResponse => {
|
||||
if (aResponse.error) {
|
||||
console.error(aResponse.error + ": " + aResponse.message);
|
||||
finish(aTarget);
|
||||
return;
|
||||
// This function is bound to a built-in object or it's not present
|
||||
// in the current scope chain. Not necessarily an actual error,
|
||||
// it just means that there's no closure for the function.
|
||||
console.warn(aResponse.error + ": " + aResponse.message);
|
||||
return void finish(aTarget);
|
||||
}
|
||||
this._addVarScope(aTarget, aResponse.scope).then(() => finish(aTarget));
|
||||
});
|
||||
@ -347,14 +385,14 @@ VariablesViewController.prototype = {
|
||||
if (aTarget._fetched) {
|
||||
return aTarget._fetched;
|
||||
}
|
||||
// Make sure the source grip is available.
|
||||
if (!aSource) {
|
||||
return promise.reject(new Error("No actor grip was given for the variable."));
|
||||
}
|
||||
|
||||
let deferred = promise.defer();
|
||||
aTarget._fetched = deferred.promise;
|
||||
|
||||
if (!aSource) {
|
||||
throw new Error("No actor grip was given for the variable.");
|
||||
}
|
||||
|
||||
// If the target is a Variable or Property then we're fetching properties.
|
||||
if (VariablesView.isVariable(aTarget)) {
|
||||
this._populateFromObject(aTarget, aSource).then(() => {
|
||||
@ -447,19 +485,29 @@ VariablesViewController.prototype = {
|
||||
* Helper function for setting up a single Scope with a single Variable
|
||||
* contained within it.
|
||||
*
|
||||
* This function will empty the variables view.
|
||||
*
|
||||
* @param object aOptions
|
||||
* Options for the contents of the view:
|
||||
* - objectActor: the grip of the new ObjectActor to show.
|
||||
* - rawObject: the new raw object to show.
|
||||
* - label: the new label for the inspected object.
|
||||
* - rawObject: the raw object to show.
|
||||
* - label: the label for the inspected object.
|
||||
* @param object aConfiguration
|
||||
* Additional options for the controller:
|
||||
* - overrideValueEvalMacro: @see _setEvaluationMacros
|
||||
* - getterOrSetterEvalMacro: @see _setEvaluationMacros
|
||||
* - simpleValueEvalMacro: @see _setEvaluationMacros
|
||||
* @return Object
|
||||
* - variable: the created Variable.
|
||||
* - expanded: the Promise that resolves when the variable expands.
|
||||
*/
|
||||
setSingleVariable: function(aOptions) {
|
||||
setSingleVariable: function(aOptions, aConfiguration = {}) {
|
||||
this._setEvaluationMacros(aConfiguration);
|
||||
this.view.empty();
|
||||
|
||||
let scope = this.view.addScope(aOptions.label);
|
||||
scope.expanded = true;
|
||||
scope.locked = true;
|
||||
scope.expanded = true; // Expand the scope by default.
|
||||
scope.locked = true; // Prevent collpasing the scope.
|
||||
|
||||
let variable = scope.addItem("", { enumerable: true });
|
||||
let expanded;
|
||||
|
@ -31,14 +31,11 @@
|
||||
}
|
||||
|
||||
.breakpoint.debugLocation {
|
||||
background-image: url("chrome://browser/skin/devtools/editor-debug-location.png"),
|
||||
background-image:
|
||||
url("chrome://browser/skin/devtools/editor-debug-location.png"),
|
||||
url("chrome://browser/skin/devtools/editor-breakpoint.png");
|
||||
}
|
||||
|
||||
.error-line {
|
||||
background: rgba(255,0,0,0.2);
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
cursor: text;
|
||||
}
|
||||
@ -75,4 +72,4 @@ selector in floating-scrollbar-light.css across all platforms. */
|
||||
|
||||
.CodeMirror-dialog input {
|
||||
font: message-box;
|
||||
}
|
||||
}
|
||||
|
@ -174,8 +174,11 @@ function setDebugLocation(ctx, line) {
|
||||
let { ed } = ctx;
|
||||
let meta = dbginfo.get(ed);
|
||||
|
||||
clearDebugLocation(ctx);
|
||||
|
||||
meta.debugLocation = line;
|
||||
ed.addMarker(line, "breakpoints", "debugLocation");
|
||||
ed.addLineClass(line, "debug-line");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -199,6 +202,7 @@ function clearDebugLocation(ctx) {
|
||||
|
||||
if (meta.debugLocation != null) {
|
||||
ed.removeMarker(meta.debugLocation, "breakpoints", "debugLocation");
|
||||
ed.removeLineClass(meta.debugLocation, "debug-line");
|
||||
meta.debugLocation = null;
|
||||
}
|
||||
}
|
||||
|
@ -77,8 +77,6 @@ const CM_MAPPING = [
|
||||
"redo",
|
||||
"clearHistory",
|
||||
"openDialog",
|
||||
"cursorCoords",
|
||||
"markText",
|
||||
"refresh"
|
||||
];
|
||||
|
||||
@ -237,6 +235,7 @@ Editor.prototype = {
|
||||
}, false);
|
||||
|
||||
cm.on("focus", () => this.emit("focus"));
|
||||
cm.on("scroll", () => this.emit("scroll"));
|
||||
cm.on("change", () => this.emit("change"));
|
||||
cm.on("cursorActivity", (cm) => this.emit("cursorActivity"));
|
||||
|
||||
@ -538,6 +537,17 @@ Editor.prototype = {
|
||||
cm.removeLineClass(line, "wrap", className);
|
||||
},
|
||||
|
||||
/**
|
||||
* Mark a range of text inside the two {line, ch} bounds. Since the range may
|
||||
* be modified, for example, when typing text, this method returns a function
|
||||
* that can be used to remove the mark.
|
||||
*/
|
||||
markText: function(from, to, className = "marked-text") {
|
||||
let cm = editors.get(this);
|
||||
let mark = cm.markText(from, to, { className: className });
|
||||
return { clear: () => mark.clear() };
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculates and returns one or more {line, ch} objects for
|
||||
* a zero-based index who's value is relative to the start of
|
||||
@ -567,11 +577,20 @@ Editor.prototype = {
|
||||
* Returns a {line, ch} object that corresponds to the
|
||||
* left, top coordinates.
|
||||
*/
|
||||
getPositionFromCoords: function (left, top) {
|
||||
getPositionFromCoords: function ({left, top}) {
|
||||
let cm = editors.get(this);
|
||||
return cm.coordsChar({ left: left, top: top });
|
||||
},
|
||||
|
||||
/**
|
||||
* The reverse of getPositionFromCoords. Similarly, returns a {left, top}
|
||||
* object that corresponds to the specified line and character number.
|
||||
*/
|
||||
getCoordsFromPosition: function ({line, ch}) {
|
||||
let cm = editors.get(this);
|
||||
return cm.charCoords({ line: ~~line, ch: ~~ch });
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if there's something to undo and false otherwise.
|
||||
*/
|
||||
|
@ -328,6 +328,7 @@ These should match what Safari and other Apple applications use on OS X Lion. --
|
||||
<!ENTITY showAllHistoryCmd.commandkey "H">
|
||||
|
||||
<!ENTITY appMenuCustomize.label "Customize">
|
||||
<!ENTITY appMenuCustomizeExit.label "Exit Customize">
|
||||
<!ENTITY appMenuHistory.label "History">
|
||||
<!ENTITY appMenuHistory.showAll.label "Show All History">
|
||||
<!ENTITY appMenuHistory.clearRecent.label "Clear Recent History…">
|
||||
|
@ -140,6 +140,10 @@ emptyChromeGlobalsFilterText=Filter chrome globals (%S)
|
||||
# appears in the filter text box for the variables view container.
|
||||
emptyVariablesFilterText=Filter variables
|
||||
|
||||
# LOCALIZATION NOTE (emptyPropertiesFilterText): This is the text that
|
||||
# appears in the filter text box for the editor's variables view bubble.
|
||||
emptyPropertiesFilterText=Filter properties
|
||||
|
||||
# LOCALIZATION NOTE (searchPanelFilter): This is the text that appears in the
|
||||
# filter panel popup for the filter scripts operation.
|
||||
searchPanelFilter=Filter scripts (%S)
|
||||
|
@ -145,6 +145,34 @@
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
/* Variable bubble view */
|
||||
|
||||
.devtools-tooltip-simple-text.token-undefined,
|
||||
.devtools-tooltip-simple-text.token-null {
|
||||
text-align: center;
|
||||
color: #666 !important; /* Override the theme's color. */
|
||||
}
|
||||
|
||||
.devtools-tooltip-simple-text.token-boolean {
|
||||
text-align: center;
|
||||
color: #10c !important;
|
||||
}
|
||||
|
||||
.devtools-tooltip-simple-text.token-number {
|
||||
text-align: center;
|
||||
color: #c00 !important;
|
||||
}
|
||||
|
||||
.devtools-tooltip-simple-text.token-string {
|
||||
text-align: start;
|
||||
color: #282 !important;
|
||||
}
|
||||
|
||||
.devtools-tooltip-simple-text.token-other {
|
||||
text-align: center;
|
||||
color: #333 !important;
|
||||
}
|
||||
|
||||
/* Instruments pane (watch expressions, variables, event listeners...) */
|
||||
|
||||
#instruments-pane > tabs > tab {
|
||||
@ -201,7 +229,7 @@
|
||||
|
||||
/* Searchbox and the search operations help panel */
|
||||
|
||||
.devtools-searchinput {
|
||||
#searchbox {
|
||||
min-width: 220px;
|
||||
-moz-margin-start: 1px;
|
||||
}
|
||||
|
@ -468,6 +468,7 @@
|
||||
}
|
||||
|
||||
.variable-or-property > .title > .value {
|
||||
-moz-box-flex: 1;
|
||||
-moz-padding-start: 6px;
|
||||
-moz-padding-end: 4px;
|
||||
}
|
||||
@ -491,6 +492,9 @@
|
||||
.variables-view-variable {
|
||||
-moz-margin-start: 1px;
|
||||
-moz-margin-end: 1px;
|
||||
}
|
||||
|
||||
.variables-view-variable:not(:last-child) {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
@ -601,6 +605,7 @@
|
||||
}
|
||||
|
||||
.variables-view-container[aligned-values] .title > .value {
|
||||
-moz-box-flex: 0;
|
||||
width: 70vw;
|
||||
}
|
||||
|
||||
|
@ -147,6 +147,34 @@
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
/* Variable bubble view */
|
||||
|
||||
.devtools-tooltip-simple-text.token-undefined,
|
||||
.devtools-tooltip-simple-text.token-null {
|
||||
text-align: center;
|
||||
color: #666 !important; /* Override the theme's color. */
|
||||
}
|
||||
|
||||
.devtools-tooltip-simple-text.token-boolean {
|
||||
text-align: center;
|
||||
color: #10c !important;
|
||||
}
|
||||
|
||||
.devtools-tooltip-simple-text.token-number {
|
||||
text-align: center;
|
||||
color: #c00 !important;
|
||||
}
|
||||
|
||||
.devtools-tooltip-simple-text.token-string {
|
||||
text-align: start;
|
||||
color: #282 !important;
|
||||
}
|
||||
|
||||
.devtools-tooltip-simple-text.token-other {
|
||||
text-align: center;
|
||||
color: #333 !important;
|
||||
}
|
||||
|
||||
/* Instruments pane (watch expressions, variables, event listeners...) */
|
||||
|
||||
#instruments-pane > tabs > tab {
|
||||
@ -203,7 +231,7 @@
|
||||
|
||||
/* Searchbox and the search operations help panel */
|
||||
|
||||
.devtools-searchinput {
|
||||
#searchbox {
|
||||
min-width: 220px;
|
||||
-moz-margin-start: 1px;
|
||||
}
|
||||
|
@ -462,6 +462,7 @@
|
||||
}
|
||||
|
||||
.variable-or-property > .title > .value {
|
||||
-moz-box-flex: 1;
|
||||
-moz-padding-start: 6px;
|
||||
-moz-padding-end: 4px;
|
||||
}
|
||||
@ -485,6 +486,9 @@
|
||||
.variables-view-variable {
|
||||
-moz-margin-start: 1px;
|
||||
-moz-margin-end: 1px;
|
||||
}
|
||||
|
||||
.variables-view-variable:not(:last-child) {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
@ -595,6 +599,7 @@
|
||||
}
|
||||
|
||||
.variables-view-container[aligned-values] .title > .value {
|
||||
-moz-box-flex: 0;
|
||||
width: 70vw;
|
||||
}
|
||||
|
||||
|
@ -212,19 +212,36 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
|
||||
border-bottom-style: none;
|
||||
border-radius: 0;
|
||||
transition: background-color;
|
||||
-moz-box-orient: vertical;
|
||||
flex: 1 1 33.33%;
|
||||
-moz-box-orient: horizontal;
|
||||
}
|
||||
:-moz-any(#PanelUI-help, #PanelUI-customize, #PanelUI-quit) > .toolbarbutton-icon {
|
||||
margin: 0 0 3px;
|
||||
|
||||
#PanelUI-help,
|
||||
#PanelUI-quit {
|
||||
min-width: 46px;
|
||||
}
|
||||
|
||||
#PanelUI-customize > .toolbarbutton-text {
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
#PanelUI-help > .toolbarbutton-text,
|
||||
#PanelUI-quit > .toolbarbutton-text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#PanelUI-help > .toolbarbutton-icon,
|
||||
#PanelUI-quit > .toolbarbutton-icon {
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
#PanelUI-customize {
|
||||
flex: 1;
|
||||
-moz-padding-start: 15px;
|
||||
-moz-border-start-style: none;
|
||||
list-style-image: url(chrome://browser/skin/menuPanel-customize.png);
|
||||
}
|
||||
|
||||
#PanelUI-help {
|
||||
-moz-border-start-style: none;
|
||||
list-style-image: url(chrome://browser/skin/menuPanel-help.png);
|
||||
}
|
||||
|
||||
|
@ -128,6 +128,16 @@
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.devtools-tooltip[clamped-dimensions] {
|
||||
max-height: 400px;
|
||||
max-width: 400px;
|
||||
}
|
||||
.devtools-tooltip[clamped-dimensions] .panel-arrowcontent {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Tooltip: Simple Text */
|
||||
|
||||
.devtools-tooltip-simple-text {
|
||||
max-width: 400px;
|
||||
margin: 0 -4px; /* Compensate for the .panel-arrowcontent padding. */
|
||||
@ -143,6 +153,14 @@
|
||||
margin-bottom: -4px;
|
||||
}
|
||||
|
||||
/* Tooltip: Variables View */
|
||||
|
||||
.devtools-tooltip-variables-view-box {
|
||||
margin: -4px; /* Compensate for the .panel-arrowcontent padding. */
|
||||
}
|
||||
|
||||
/* Tooltip: Tiles */
|
||||
|
||||
.devtools-tooltip-tiles {
|
||||
background-color: #eee;
|
||||
background-image: linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc),
|
||||
|
@ -188,6 +188,27 @@ div.cm-s-mozilla span.CodeMirror-matchingbracket { /* highlight brackets */
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Highlight for a line that contains an error. */
|
||||
div.CodeMirror div.error-line {
|
||||
background: rgba(255,0,0,0.2);
|
||||
}
|
||||
|
||||
/* Highlight for a line that represents a stack frame's location. */
|
||||
div.CodeMirror div.debug-line {
|
||||
background: rgba(0,128,255,0.1);
|
||||
box-shadow:
|
||||
0 1px 0 0 rgba(0,128,255,0.4),
|
||||
0 -1px 0 0 rgba(0,128,255,0.4);
|
||||
}
|
||||
|
||||
/* Generic highlighted text */
|
||||
div.CodeMirror span.marked-text {
|
||||
background: rgba(255,255,0,0.2);
|
||||
border: 1px dashed rgba(192,192,0,0.6);
|
||||
-moz-margin-start: -1px;
|
||||
-moz-margin-end: -1px;
|
||||
}
|
||||
|
||||
/* Highlight for evaluating current statement. */
|
||||
div.CodeMirror span.eval-text {
|
||||
background-color: #556;
|
||||
|
@ -187,6 +187,27 @@ div.cm-s-mozilla span.CodeMirror-matchingbracket { /* highlight brackets */
|
||||
color: black;
|
||||
}
|
||||
|
||||
/* Highlight for a line that contains an error. */
|
||||
div.CodeMirror div.error-line {
|
||||
background: rgba(255,0,0,0.2);
|
||||
}
|
||||
|
||||
/* Highlight for a line that represents a stack frame's location. */
|
||||
div.CodeMirror div.debug-line {
|
||||
background: rgba(0,128,255,0.1);
|
||||
box-shadow:
|
||||
0 1px 0 0 rgba(0,128,255,0.4),
|
||||
0 -1px 0 0 rgba(0,128,255,0.4);
|
||||
}
|
||||
|
||||
/* Generic highlighted text */
|
||||
div.CodeMirror span.marked-text {
|
||||
background: rgba(255,255,0,0.2);
|
||||
border: 1px dashed rgba(192,192,0,0.6);
|
||||
-moz-margin-start: -1px;
|
||||
-moz-margin-end: -1px;
|
||||
}
|
||||
|
||||
/* Highlight for evaluating current statement. */
|
||||
div.CodeMirror span.eval-text {
|
||||
background-color: #ccd;
|
||||
|
@ -145,6 +145,34 @@
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
/* Variable bubble view */
|
||||
|
||||
.devtools-tooltip-simple-text.token-undefined,
|
||||
.devtools-tooltip-simple-text.token-null {
|
||||
text-align: center;
|
||||
color: #666 !important; /* Override the theme's color. */
|
||||
}
|
||||
|
||||
.devtools-tooltip-simple-text.token-boolean {
|
||||
text-align: center;
|
||||
color: #10c !important;
|
||||
}
|
||||
|
||||
.devtools-tooltip-simple-text.token-number {
|
||||
text-align: center;
|
||||
color: #c00 !important;
|
||||
}
|
||||
|
||||
.devtools-tooltip-simple-text.token-string {
|
||||
text-align: start;
|
||||
color: #282 !important;
|
||||
}
|
||||
|
||||
.devtools-tooltip-simple-text.token-other {
|
||||
text-align: center;
|
||||
color: #333 !important;
|
||||
}
|
||||
|
||||
/* Instruments pane (watch expressions, variables, event listeners...) */
|
||||
|
||||
#instruments-pane > tabs > tab {
|
||||
@ -201,7 +229,7 @@
|
||||
|
||||
/* Searchbox and the search operations help panel */
|
||||
|
||||
.devtools-searchinput {
|
||||
#searchbox {
|
||||
min-width: 220px;
|
||||
-moz-margin-start: 1px;
|
||||
}
|
||||
|
@ -465,6 +465,7 @@
|
||||
}
|
||||
|
||||
.variable-or-property > .title > .value {
|
||||
-moz-box-flex: 1;
|
||||
-moz-padding-start: 6px;
|
||||
-moz-padding-end: 4px;
|
||||
}
|
||||
@ -488,6 +489,9 @@
|
||||
.variables-view-variable {
|
||||
-moz-margin-start: 1px;
|
||||
-moz-margin-end: 1px;
|
||||
}
|
||||
|
||||
.variables-view-variable:not(:last-child) {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
@ -598,6 +602,7 @@
|
||||
}
|
||||
|
||||
.variables-view-container[aligned-values] .title > .value {
|
||||
-moz-box-flex: 0;
|
||||
width: 70vw;
|
||||
}
|
||||
|
||||
|
@ -299,6 +299,14 @@ browser.jar:
|
||||
skin/classic/browser/syncQuota.css
|
||||
skin/classic/browser/syncProgress.css
|
||||
#endif
|
||||
skin/classic/browser/devtools/tooltip/arrow-horizontal-dark.png (../shared/devtools/tooltip/arrow-horizontal-dark.png)
|
||||
skin/classic/browser/devtools/tooltip/arrow-horizontal-dark@2x.png (../shared/devtools/tooltip/arrow-horizontal-dark@2x.png)
|
||||
skin/classic/browser/devtools/tooltip/arrow-vertical-dark.png (../shared/devtools/tooltip/arrow-vertical-dark.png)
|
||||
skin/classic/browser/devtools/tooltip/arrow-vertical-dark@2x.png (../shared/devtools/tooltip/arrow-vertical-dark@2x.png)
|
||||
skin/classic/browser/devtools/tooltip/arrow-horizontal-light.png (../shared/devtools/tooltip/arrow-horizontal-light.png)
|
||||
skin/classic/browser/devtools/tooltip/arrow-horizontal-light@2x.png (../shared/devtools/tooltip/arrow-horizontal-light@2x.png)
|
||||
skin/classic/browser/devtools/tooltip/arrow-vertical-light.png (../shared/devtools/tooltip/arrow-vertical-light.png)
|
||||
skin/classic/browser/devtools/tooltip/arrow-vertical-light@2x.png (../shared/devtools/tooltip/arrow-vertical-light@2x.png)
|
||||
|
||||
#ifdef XP_WIN
|
||||
browser.jar:
|
||||
@ -593,11 +601,11 @@ browser.jar:
|
||||
skin/classic/aero/browser/syncProgress.css
|
||||
#endif
|
||||
#endif
|
||||
skin/classic/browser/devtools/tooltip/arrow-horizontal-dark.png (../shared/devtools/tooltip/arrow-horizontal-dark.png)
|
||||
skin/classic/browser/devtools/tooltip/arrow-horizontal-dark@2x.png (../shared/devtools/tooltip/arrow-horizontal-dark@2x.png)
|
||||
skin/classic/browser/devtools/tooltip/arrow-vertical-dark.png (../shared/devtools/tooltip/arrow-vertical-dark.png)
|
||||
skin/classic/browser/devtools/tooltip/arrow-vertical-dark@2x.png (../shared/devtools/tooltip/arrow-vertical-dark@2x.png)
|
||||
skin/classic/browser/devtools/tooltip/arrow-horizontal-light.png (../shared/devtools/tooltip/arrow-horizontal-light.png)
|
||||
skin/classic/browser/devtools/tooltip/arrow-horizontal-light@2x.png (../shared/devtools/tooltip/arrow-horizontal-light@2x.png)
|
||||
skin/classic/browser/devtools/tooltip/arrow-vertical-light.png (../shared/devtools/tooltip/arrow-vertical-light.png)
|
||||
skin/classic/browser/devtools/tooltip/arrow-vertical-light@2x.png (../shared/devtools/tooltip/arrow-vertical-light@2x.png)
|
||||
skin/classic/aero/browser/devtools/tooltip/arrow-horizontal-dark.png (../shared/devtools/tooltip/arrow-horizontal-dark.png)
|
||||
skin/classic/aero/browser/devtools/tooltip/arrow-horizontal-dark@2x.png (../shared/devtools/tooltip/arrow-horizontal-dark@2x.png)
|
||||
skin/classic/aero/browser/devtools/tooltip/arrow-vertical-dark.png (../shared/devtools/tooltip/arrow-vertical-dark.png)
|
||||
skin/classic/aero/browser/devtools/tooltip/arrow-vertical-dark@2x.png (../shared/devtools/tooltip/arrow-vertical-dark@2x.png)
|
||||
skin/classic/aero/browser/devtools/tooltip/arrow-horizontal-light.png (../shared/devtools/tooltip/arrow-horizontal-light.png)
|
||||
skin/classic/aero/browser/devtools/tooltip/arrow-horizontal-light@2x.png (../shared/devtools/tooltip/arrow-horizontal-light@2x.png)
|
||||
skin/classic/aero/browser/devtools/tooltip/arrow-vertical-light.png (../shared/devtools/tooltip/arrow-vertical-light.png)
|
||||
skin/classic/aero/browser/devtools/tooltip/arrow-vertical-light@2x.png (../shared/devtools/tooltip/arrow-vertical-light@2x.png)
|
||||
|
@ -684,6 +684,7 @@ GK_ATOM(ondisabled, "ondisabled")
|
||||
GK_ATOM(ondischargingtimechange, "ondischargingtimechange")
|
||||
GK_ATOM(ondisconnected, "ondisconnected")
|
||||
GK_ATOM(ondisconnecting, "ondisconnecting")
|
||||
GK_ATOM(ondiscoverystatechanged, "ondiscoverystatechanged")
|
||||
GK_ATOM(ondownloading, "ondownloading")
|
||||
GK_ATOM(onDOMActivate, "onDOMActivate")
|
||||
GK_ATOM(onDOMAttrModified, "onDOMAttrModified")
|
||||
|
@ -48,6 +48,10 @@ const kEventConstructors = {
|
||||
return new BluetoothDeviceEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
BluetoothDiscoveryStateChangedEvent: { create: function (aName, aProps) {
|
||||
return new BluetoothDiscoveryStateChangedEvent(aName, aProps);
|
||||
},
|
||||
},
|
||||
BluetoothStatusChangedEvent: { create: function (aName, aProps) {
|
||||
return new BluetoothStatusChangedEvent(aName, aProps);
|
||||
},
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "nsCxPusher.h"
|
||||
#include "nsDOMClassInfo.h"
|
||||
#include "nsIDOMBluetoothDeviceEvent.h"
|
||||
#include "nsIDOMBluetoothDiscoveryStateChangedEvent.h"
|
||||
#include "nsIDOMBluetoothStatusChangedEvent.h"
|
||||
#include "nsTArrayHelpers.h"
|
||||
#include "DOMRequest.h"
|
||||
@ -320,6 +321,19 @@ BluetoothAdapter::Notify(const BluetoothSignal& aData)
|
||||
|
||||
MOZ_ASSERT(arr.Length() == 1);
|
||||
SetPropertyByValue(arr[0]);
|
||||
} else if (aData.name().EqualsLiteral(DISCOVERY_STATE_CHANGED_ID)) {
|
||||
MOZ_ASSERT(v.type() == BluetoothValue::Tbool);
|
||||
bool isDiscovering = v.get_bool();
|
||||
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
NS_NewDOMBluetoothDiscoveryStateChangedEvent(
|
||||
getter_AddRefs(event), this, nullptr, nullptr);
|
||||
|
||||
nsCOMPtr<nsIDOMBluetoothDiscoveryStateChangedEvent> e =
|
||||
do_QueryInterface(event);
|
||||
e->InitBluetoothDiscoveryStateChangedEvent(aData.name(), false, false,
|
||||
isDiscovering);
|
||||
DispatchTrustedEvent(event);
|
||||
} else if (aData.name().EqualsLiteral(PAIRED_STATUS_CHANGED_ID) ||
|
||||
aData.name().EqualsLiteral(HFP_STATUS_CHANGED_ID) ||
|
||||
aData.name().EqualsLiteral(SCO_STATUS_CHANGED_ID) ||
|
||||
|
@ -147,6 +147,7 @@ public:
|
||||
SendMediaPlayStatus(const MediaPlayStatus& aMediaPlayStatus, ErrorResult& aRv);
|
||||
|
||||
IMPL_EVENT_HANDLER(devicefound);
|
||||
IMPL_EVENT_HANDLER(discoverystatechanged);
|
||||
IMPL_EVENT_HANDLER(a2dpstatuschanged);
|
||||
IMPL_EVENT_HANDLER(hfpstatuschanged);
|
||||
IMPL_EVENT_HANDLER(pairedstatuschanged);
|
||||
|
@ -89,6 +89,11 @@ extern bool gBluetoothDebugFlag;
|
||||
*/
|
||||
#define PAIRED_STATUS_CHANGED_ID "pairedstatuschanged"
|
||||
|
||||
/**
|
||||
* This event would be fired when discovery procedure starts or stops.
|
||||
*/
|
||||
#define DISCOVERY_STATE_CHANGED_ID "discoverystatechanged"
|
||||
|
||||
/**
|
||||
* When receiving a query about current play status from remote device, we'll
|
||||
* dispatch an event.
|
||||
|
@ -163,6 +163,7 @@ BluetoothProfileController::Start()
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mDeviceAddress.IsEmpty());
|
||||
MOZ_ASSERT(mProfilesIndex == -1);
|
||||
NS_ENSURE_TRUE_VOID(mProfiles.Length() > 0);
|
||||
|
||||
++mProfilesIndex;
|
||||
BT_LOGR_PROFILE(mProfiles[mProfilesIndex], "");
|
||||
|
@ -40,8 +40,8 @@ BEGIN_BLUETOOTH_NAMESPACE
|
||||
// Bit 18: Major service class = 0x20, Rendering
|
||||
#define HAS_RENDERING(cod) (cod & 0x40000)
|
||||
|
||||
// Major device class = 0xA, Peripheral
|
||||
#define IS_PERIPHERAL(cod) (GET_MAJOR_DEVICE_CLASS(cod) == 0xa)
|
||||
// Major device class = 0x5, Peripheral
|
||||
#define IS_PERIPHERAL(cod) (GET_MAJOR_DEVICE_CLASS(cod) == 0x5)
|
||||
|
||||
class BluetoothProfileManagerBase;
|
||||
class BluetoothReplyRunnable;
|
||||
|
@ -49,7 +49,6 @@ static InfallibleTArray<nsString> sAdapterBondedAddressArray;
|
||||
static InfallibleTArray<BluetoothNamedValue> sRemoteDevicesPack;
|
||||
static nsTArray<nsRefPtr<BluetoothProfileController> > sControllerArray;
|
||||
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sBondingRunnableArray;
|
||||
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sChangeDiscoveryRunnableArray;
|
||||
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sGetDeviceRunnableArray;
|
||||
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sSetPropertyRunnableArray;
|
||||
static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sUnbondingRunnableArray;
|
||||
@ -496,12 +495,16 @@ DiscoveryStateChangedCallback(bt_discovery_state_t aState)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
if (!sChangeDiscoveryRunnableArray.IsEmpty()) {
|
||||
BluetoothValue values(true);
|
||||
DispatchBluetoothReply(sChangeDiscoveryRunnableArray[0],
|
||||
values, EmptyString());
|
||||
bool isDiscovering = (aState == BT_DISCOVERY_STARTED);
|
||||
|
||||
sChangeDiscoveryRunnableArray.RemoveElementAt(0);
|
||||
BluetoothSignal signal(NS_LITERAL_STRING(DISCOVERY_STATE_CHANGED_ID),
|
||||
NS_LITERAL_STRING(KEY_ADAPTER),
|
||||
isDiscovering);
|
||||
|
||||
nsRefPtr<DistributeBluetoothSignalTask>
|
||||
t = new DistributeBluetoothSignalTask(signal);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(t))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
}
|
||||
|
||||
@ -918,17 +921,17 @@ BluetoothServiceBluedroid::StartDiscoveryInternal(
|
||||
if (!IsReady()) {
|
||||
NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
|
||||
DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
int ret = sBtInterface->start_discovery();
|
||||
if (ret != BT_STATUS_SUCCESS) {
|
||||
ReplyStatusError(aRunnable, ret, NS_LITERAL_STRING("StartDiscovery"));
|
||||
|
||||
return NS_OK;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
sChangeDiscoveryRunnableArray.AppendElement(aRunnable);
|
||||
DispatchBluetoothReply(aRunnable, true, EmptyString());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -947,10 +950,10 @@ BluetoothServiceBluedroid::StopDiscoveryInternal(
|
||||
int ret = sBtInterface->cancel_discovery();
|
||||
if (ret != BT_STATUS_SUCCESS) {
|
||||
ReplyStatusError(aRunnable, ret, NS_LITERAL_STRING("StopDiscovery"));
|
||||
return NS_OK;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
sChangeDiscoveryRunnableArray.AppendElement(aRunnable);
|
||||
DispatchBluetoothReply(aRunnable, true, EmptyString());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -1512,6 +1512,20 @@ EventFilter(DBusConnection* aConn, DBusMessage* aMsg, void* aData)
|
||||
errorStr,
|
||||
sAdapterProperties,
|
||||
ArrayLength(sAdapterProperties));
|
||||
|
||||
BluetoothNamedValue& property = v.get_ArrayOfBluetoothNamedValue()[0];
|
||||
if (property.name().EqualsLiteral("Discovering")) {
|
||||
bool isDiscovering = property.value();
|
||||
BluetoothSignal signal(NS_LITERAL_STRING(DISCOVERY_STATE_CHANGED_ID),
|
||||
NS_LITERAL_STRING(KEY_ADAPTER),
|
||||
isDiscovering);
|
||||
|
||||
nsRefPtr<DistributeBluetoothSignalTask>
|
||||
t = new DistributeBluetoothSignalTask(signal);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(t))) {
|
||||
BT_WARNING("Failed to dispatch to main thread!");
|
||||
}
|
||||
}
|
||||
} else if (dbus_message_is_signal(aMsg, DBUS_DEVICE_IFACE,
|
||||
"PropertyChanged")) {
|
||||
ParsePropertyChange(aMsg,
|
||||
|
@ -8,6 +8,7 @@ if CONFIG['MOZ_B2G_BT']:
|
||||
XPIDL_SOURCES += [
|
||||
'nsIDOMBluetoothDevice.idl',
|
||||
'nsIDOMBluetoothDeviceEvent.idl',
|
||||
'nsIDOMBluetoothDiscoveryStateChangedEvent.idl',
|
||||
'nsIDOMBluetoothStatusChangedEvent.idl',
|
||||
]
|
||||
XPIDL_MODULE = 'dom_bluetooth'
|
||||
|
@ -0,0 +1,22 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsIDOMEvent.idl"
|
||||
|
||||
[scriptable, builtinclass, uuid(9de639cb-71c4-4144-8462-09763ec87c20)]
|
||||
interface nsIDOMBluetoothDiscoveryStateChangedEvent : nsIDOMEvent
|
||||
{
|
||||
readonly attribute boolean discovering;
|
||||
|
||||
[noscript]
|
||||
void initBluetoothDiscoveryStateChangedEvent(in DOMString aType,
|
||||
in boolean aCanBubble,
|
||||
in boolean aCancelable,
|
||||
in boolean aDiscovering);
|
||||
};
|
||||
|
||||
dictionary BluetoothDiscoveryStateChangedEventInit : EventInit
|
||||
{
|
||||
bool discovering;
|
||||
};
|
@ -95,6 +95,7 @@ public:
|
||||
|
||||
void GetDiskFreeSpace(int64_t* aSoFar);
|
||||
void GetStatus(nsAString& aStatus);
|
||||
void DoFormat(nsAString& aStatus);
|
||||
static void GetRootDirectoryForType(const nsAString& aStorageType,
|
||||
const nsAString& aStorageName,
|
||||
nsIFile** aFile);
|
||||
@ -237,6 +238,7 @@ public:
|
||||
already_AddRefed<DOMRequest> FreeSpace(ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest> UsedSpace(ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest> Available(ErrorResult& aRv);
|
||||
already_AddRefed<DOMRequest> Format(ErrorResult& aRv);
|
||||
|
||||
bool Default();
|
||||
|
||||
|
@ -103,6 +103,16 @@ DeviceStorageRequestChild::
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageResponseValue::TFormatStorageResponse:
|
||||
{
|
||||
FormatStorageResponse r = aValue;
|
||||
AutoJSContext cx;
|
||||
JS::Rooted<JS::Value> result(
|
||||
cx, StringToJsval(mRequest->GetOwner(), r.mountState()));
|
||||
mRequest->FireSuccess(result);
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageResponseValue::TEnumerationResponse:
|
||||
{
|
||||
EnumerationResponse r = aValue;
|
||||
|
@ -131,6 +131,18 @@ DeviceStorageRequestParent::Dispatch()
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageParams::TDeviceStorageFormatParams:
|
||||
{
|
||||
DeviceStorageFormatParams p = mParams;
|
||||
|
||||
nsRefPtr<DeviceStorageFile> dsf =
|
||||
new DeviceStorageFile(p.type(), p.storageName());
|
||||
nsRefPtr<PostFormatResultEvent> r
|
||||
= new PostFormatResultEvent(this, dsf);
|
||||
NS_DispatchToMainThread(r);
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageParams::TDeviceStorageEnumerationParams:
|
||||
{
|
||||
DeviceStorageEnumerationParams p = mParams;
|
||||
@ -215,6 +227,14 @@ DeviceStorageRequestParent::EnsureRequiredPermissions(
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageParams::TDeviceStorageFormatParams:
|
||||
{
|
||||
DeviceStorageFormatParams p = mParams;
|
||||
type = p.type();
|
||||
requestType = DEVICE_STORAGE_REQUEST_FORMAT;
|
||||
break;
|
||||
}
|
||||
|
||||
case DeviceStorageParams::TDeviceStorageEnumerationParams:
|
||||
{
|
||||
DeviceStorageEnumerationParams p = mParams;
|
||||
@ -726,6 +746,34 @@ DeviceStorageRequestParent::PostAvailableResultEvent::CancelableRun()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
DeviceStorageRequestParent::PostFormatResultEvent::
|
||||
PostFormatResultEvent(DeviceStorageRequestParent* aParent,
|
||||
DeviceStorageFile* aFile)
|
||||
: CancelableRunnable(aParent)
|
||||
, mFile(aFile)
|
||||
{
|
||||
}
|
||||
|
||||
DeviceStorageRequestParent::PostFormatResultEvent::
|
||||
~PostFormatResultEvent()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
DeviceStorageRequestParent::PostFormatResultEvent::CancelableRun()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsString state = NS_LITERAL_STRING("unavailable");
|
||||
if (mFile) {
|
||||
mFile->DoFormat(state);
|
||||
}
|
||||
|
||||
FormatStorageResponse response(state);
|
||||
unused << mParent->Send__delete__(mParent, response);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace devicestorage
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -227,6 +227,16 @@ private:
|
||||
nsRefPtr<DeviceStorageFile> mFile;
|
||||
};
|
||||
|
||||
class PostFormatResultEvent : public CancelableRunnable
|
||||
{
|
||||
public:
|
||||
PostFormatResultEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile);
|
||||
virtual ~PostFormatResultEvent();
|
||||
virtual nsresult CancelableRun();
|
||||
private:
|
||||
nsRefPtr<DeviceStorageFile> mFile;
|
||||
};
|
||||
|
||||
protected:
|
||||
bool AddRunnable(CancelableRunnable* aRunnable) {
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
@ -53,6 +53,11 @@ struct AvailableStorageResponse
|
||||
nsString mountState;
|
||||
};
|
||||
|
||||
struct FormatStorageResponse
|
||||
{
|
||||
nsString mountState;
|
||||
};
|
||||
|
||||
union DeviceStorageResponseValue
|
||||
{
|
||||
ErrorResponse;
|
||||
@ -62,6 +67,7 @@ union DeviceStorageResponseValue
|
||||
FreeSpaceStorageResponse;
|
||||
UsedSpaceStorageResponse;
|
||||
AvailableStorageResponse;
|
||||
FormatStorageResponse;
|
||||
};
|
||||
|
||||
sync protocol PDeviceStorageRequest {
|
||||
|
@ -390,6 +390,7 @@ DeviceStorageTypeChecker::GetAccessForRequest(
|
||||
break;
|
||||
case DEVICE_STORAGE_REQUEST_WRITE:
|
||||
case DEVICE_STORAGE_REQUEST_DELETE:
|
||||
case DEVICE_STORAGE_REQUEST_FORMAT:
|
||||
aAccessResult.AssignLiteral("write");
|
||||
break;
|
||||
case DEVICE_STORAGE_REQUEST_CREATE:
|
||||
@ -1279,6 +1280,36 @@ DeviceStorageFile::IsAvailable()
|
||||
return status.EqualsLiteral("available");
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageFile::DoFormat(nsAString& aStatus)
|
||||
{
|
||||
DeviceStorageTypeChecker* typeChecker
|
||||
= DeviceStorageTypeChecker::CreateOrGet();
|
||||
if (!typeChecker) {
|
||||
return;
|
||||
}
|
||||
if (!typeChecker->IsVolumeBased(mStorageType)) {
|
||||
aStatus.AssignLiteral("notVolume");
|
||||
return;
|
||||
}
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
|
||||
NS_ENSURE_TRUE_VOID(vs);
|
||||
|
||||
nsCOMPtr<nsIVolume> vol;
|
||||
nsresult rv = vs->GetVolumeByName(mStorageName, getter_AddRefs(vol));
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
if (!vol) {
|
||||
return;
|
||||
}
|
||||
|
||||
vol->Format();
|
||||
|
||||
aStatus.AssignLiteral("formatting");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageFile::GetStatus(nsAString& aStatus)
|
||||
{
|
||||
@ -1316,6 +1347,13 @@ DeviceStorageFile::GetStatus(nsAString& aStatus)
|
||||
aStatus.AssignLiteral("shared");
|
||||
return;
|
||||
}
|
||||
bool isFormatting;
|
||||
rv = vol->GetIsFormatting(&isFormatting);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
if (isFormatting) {
|
||||
aStatus.AssignLiteral("unavailable");
|
||||
return;
|
||||
}
|
||||
int32_t volState;
|
||||
rv = vol->GetState(&volState);
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
@ -1848,6 +1886,39 @@ private:
|
||||
nsRefPtr<DOMRequest> mRequest;
|
||||
};
|
||||
|
||||
class PostFormatResultEvent : public nsRunnable
|
||||
{
|
||||
public:
|
||||
PostFormatResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest)
|
||||
: mFile(aFile)
|
||||
, mRequest(aRequest)
|
||||
{
|
||||
}
|
||||
|
||||
~PostFormatResultEvent() {}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsString state = NS_LITERAL_STRING("unavailable");
|
||||
if (mFile) {
|
||||
mFile->DoFormat(state);
|
||||
}
|
||||
|
||||
AutoJSContext cx;
|
||||
JS::Rooted<JS::Value> result(cx,
|
||||
StringToJsval(mRequest->GetOwner(), state));
|
||||
mRequest->FireSuccess(result);
|
||||
mRequest = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<DeviceStorageFile> mFile;
|
||||
nsRefPtr<DOMRequest> mRequest;
|
||||
};
|
||||
|
||||
class PostResultEvent : public nsRunnable
|
||||
{
|
||||
public:
|
||||
@ -2423,6 +2494,23 @@ public:
|
||||
mDeviceStorage->mAllowedToWatchFile = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
case DEVICE_STORAGE_REQUEST_FORMAT:
|
||||
{
|
||||
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||
PDeviceStorageRequestChild* child
|
||||
= new DeviceStorageRequestChild(mRequest, mFile);
|
||||
DeviceStorageFormatParams params(mFile->mStorageType,
|
||||
mFile->mStorageName);
|
||||
ContentChild::GetSingleton()
|
||||
->SendPDeviceStorageRequestConstructor(child, params);
|
||||
return NS_OK;
|
||||
}
|
||||
r = new PostFormatResultEvent(mFile, mRequest);
|
||||
NS_DispatchToMainThread(r);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (r) {
|
||||
@ -3083,6 +3171,26 @@ nsDOMDeviceStorage::Available(ErrorResult& aRv)
|
||||
return request.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
nsDOMDeviceStorage::Format(ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetOwner();
|
||||
if (!win) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<DOMRequest> request = new DOMRequest(win);
|
||||
|
||||
nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
|
||||
mStorageName);
|
||||
nsCOMPtr<nsIRunnable> r
|
||||
= new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_FORMAT,
|
||||
win, mPrincipal, dsf, request);
|
||||
NS_DispatchToMainThread(r);
|
||||
return request.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMDeviceStorage::GetRootDirectoryForFile(const nsAString& aName,
|
||||
nsIFile** aRootDirectory)
|
||||
|
@ -51,7 +51,8 @@ enum DeviceStorageRequestType {
|
||||
DEVICE_STORAGE_REQUEST_WATCH,
|
||||
DEVICE_STORAGE_REQUEST_FREE_SPACE,
|
||||
DEVICE_STORAGE_REQUEST_USED_SPACE,
|
||||
DEVICE_STORAGE_REQUEST_AVAILABLE
|
||||
DEVICE_STORAGE_REQUEST_AVAILABLE,
|
||||
DEVICE_STORAGE_REQUEST_FORMAT
|
||||
};
|
||||
|
||||
class DeviceStorageUsedSpaceCache MOZ_FINAL
|
||||
|
@ -116,6 +116,17 @@ dictionary MozStkMenu
|
||||
* false: no help information available.
|
||||
*/
|
||||
boolean isHelpAvailable;
|
||||
|
||||
/**
|
||||
* List of Next Action Indicators.
|
||||
* Each element should be one of nsIDOMMozIccManager.STK_CMD_*
|
||||
* or nsIDOMMozIccManager.STK_NEXT_ACTION_*
|
||||
* If it's STK_NEXT_ACTION_NULL, the terminal should ignore this action
|
||||
* in corresponding item.
|
||||
*
|
||||
* @see TS 11.14, clause 12.24, Items Next Action Indicator.
|
||||
*/
|
||||
jsval nextActionList; // unsigned short []
|
||||
};
|
||||
|
||||
dictionary MozStkInput
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
interface nsIDOMMozIcc;
|
||||
|
||||
[scriptable, builtinclass, uuid(23067d6f-e0cb-4f34-8648-77c2b25a11f5)]
|
||||
[scriptable, builtinclass, uuid(67e40e8e-35ee-40a4-a5b8-414588675133)]
|
||||
interface nsIDOMMozIccManager : nsIDOMEventTarget
|
||||
{
|
||||
/**
|
||||
@ -47,10 +47,10 @@ interface nsIDOMMozIccManager : nsIDOMEventTarget
|
||||
const unsigned short STK_CMD_PROVIDE_LOCAL_INFO = 0x26;
|
||||
const unsigned short STK_CMD_TIMER_MANAGEMENT = 0x27;
|
||||
const unsigned short STK_CMD_SET_UP_IDLE_MODE_TEXT = 0x28;
|
||||
const unsigned short STK_CMD_OPEN_CHANNEL = 0x30;
|
||||
const unsigned short STK_CMD_CLOSE_CHANNEL = 0x31;
|
||||
const unsigned short STK_CMD_RECEIVE_DATA = 0x32;
|
||||
const unsigned short STK_CMD_SEND_DATA = 0x33;
|
||||
const unsigned short STK_CMD_OPEN_CHANNEL = 0x40;
|
||||
const unsigned short STK_CMD_CLOSE_CHANNEL = 0x41;
|
||||
const unsigned short STK_CMD_RECEIVE_DATA = 0x42;
|
||||
const unsigned short STK_CMD_SEND_DATA = 0x43;
|
||||
|
||||
/**
|
||||
* STK result code.
|
||||
@ -222,6 +222,12 @@ interface nsIDOMMozIccManager : nsIDOMEventTarget
|
||||
const unsigned short STK_BROWSER_TERMINATION_CAUSE_USER = 0x00;
|
||||
const unsigned short STK_BROWSER_TERMINATION_CAUSE_ERROR = 0x01;
|
||||
|
||||
/**
|
||||
* Next Action Indicator.
|
||||
*/
|
||||
const unsigned short STK_NEXT_ACTION_NULL = 0x00;
|
||||
const unsigned short STK_NEXT_ACTION_END_PROACTIVE_SESSION = 0x81;
|
||||
|
||||
/**
|
||||
* Array of iccIds that are currently detected.
|
||||
*/
|
||||
|
@ -22,6 +22,7 @@ qemu = true
|
||||
[test_stk_select_item.js]
|
||||
[test_stk_setup_menu.js]
|
||||
[test_stk_setup_idle_mode_text.js]
|
||||
[test_stk_bip_command.js]
|
||||
[test_icc_access_invalid_object.js]
|
||||
disabled = Bug 933654
|
||||
[test_icc_detected_undetected_event.js]
|
||||
|
48
dom/icc/tests/marionette/test_stk_bip_command.js
Normal file
48
dom/icc/tests/marionette/test_stk_bip_command.js
Normal file
@ -0,0 +1,48 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
MARIONETTE_HEAD_JS = "stk_helper.js";
|
||||
|
||||
function testBipCommand(command, expect) {
|
||||
log("STK CMD " + JSON.stringify(command));
|
||||
|
||||
is(command.typeOfCommand, expect.typeOfCommand, expect.name);
|
||||
is(command.options.text, expect.text, expect.name);
|
||||
|
||||
runNextTest();
|
||||
}
|
||||
|
||||
let tests = [
|
||||
{command: "d04b81030140018202818205074f70656e204944350702030403041f0239020578470a065465737447700272730d08f4557365724c6f670d08f4557365725077643c0301ad9c3e052101010101",
|
||||
func: testBipCommand,
|
||||
expect: {name: "open_channel_1",
|
||||
typeOfCommand: iccManager.STK_CMD_OPEN_CHANNEL,
|
||||
text: "Open ID"}},
|
||||
{command: "d0448103014001820281820500350702030403041f0239020578470a065465737447700272730d08f4557365724c6f670d08f4557365725077643c0301ad9c3e052101010101",
|
||||
func: testBipCommand,
|
||||
expect: {name: "open_channel_2",
|
||||
typeOfCommand: iccManager.STK_CMD_OPEN_CHANNEL,
|
||||
text: ""}},
|
||||
{command: "d05381030140018202818205094f70656e2049442031350702030403041f0239020578470a065465737447700272730d08f4557365724c6f670d08f4557365725077643c0301ad9c3e052101010101d004000900b4",
|
||||
func: testBipCommand,
|
||||
expect: {name: "open_channel_3",
|
||||
typeOfCommand: iccManager.STK_CMD_OPEN_CHANNEL,
|
||||
text: "Open ID 1"}},
|
||||
{command: "d01b810301410082028121850a436c6f73652049442031d004000a00b4",
|
||||
func: testBipCommand,
|
||||
expect: {name: "close_channel_1",
|
||||
typeOfCommand: iccManager.STK_CMD_CLOSE_CHANNEL,
|
||||
text: "Close ID 1"}},
|
||||
{command: "d022810301420082028121850e5265636569766520446174612031b701c8d004000e00b4",
|
||||
func: testBipCommand,
|
||||
expect: {name: "receive_data_1",
|
||||
typeOfCommand: iccManager.STK_CMD_RECEIVE_DATA,
|
||||
text: "Receive Data 1"}},
|
||||
{command: "d026810301430182028121850b53656e6420446174612031b6080001020304050607d004000b00b4",
|
||||
func: testBipCommand,
|
||||
expect: {name: "send_data_1",
|
||||
typeOfCommand: iccManager.STK_CMD_SEND_DATA,
|
||||
text: "Send Data 1"}},
|
||||
];
|
||||
|
||||
runNextTest();
|
@ -12,6 +12,10 @@ function testSelectItem(command, expect) {
|
||||
is(command.options.items[index].identifier, expect.items[index].identifier, expect.name);
|
||||
is(command.options.items[index].text, expect.items[index].text, expect.name);
|
||||
}
|
||||
let length = command.options.nextActionList ? command.options.nextActionList.length : 0;
|
||||
for (let i = 0; i < length; i++) {
|
||||
is(command.options.nextActionList[i], expect.nextActionList[i], expect.name);
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
}
|
||||
@ -58,7 +62,8 @@ let tests = [
|
||||
expect: {name: "select_item_cmd_7",
|
||||
commandQualifier: 0x00,
|
||||
title: "Toolkit Select",
|
||||
items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}]}},
|
||||
items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}],
|
||||
nextActionList: [iccManager.STK_CMD_SEND_SMS, iccManager.STK_CMD_SET_UP_CALL, iccManager.STK_CMD_PROVIDE_LOCAL_INFO]}},
|
||||
{command: "d037810301240082028182850e546f6f6c6b69742053656c6563748f07014974656d20318f07024974656d20328f07034974656d2033900102",
|
||||
func: testSelectItem,
|
||||
expect: {name: "select_item_cmd_8",
|
||||
@ -304,7 +309,14 @@ let tests = [
|
||||
expect: {name: "select_item_cmd_48",
|
||||
commandQualifier: 0x00,
|
||||
title: "82ル0",
|
||||
items: [{identifier: 1, text: "82ル1"}, {identifier: 2, text: "82ル2"}, {identifier: 3, text: "82ル3"}]}}
|
||||
items: [{identifier: 1, text: "82ル1"}, {identifier: 2, text: "82ル2"}, {identifier: 3, text: "82ル3"}]}},
|
||||
{command: "d039810301240082028182850e546f6f6c6b69742053656c6563748f07014974656d20318f07024974656d20328f07034974656d20331803000081",
|
||||
func: testSelectItem,
|
||||
expect: {name: "select_item_cmd_49",
|
||||
commandQualifier: 0x00,
|
||||
title: "Toolkit Select",
|
||||
items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}],
|
||||
nextActionList: [iccManager.STK_NEXT_ACTION_NULL, iccManager.STK_NEXT_ACTION_NULL, iccManager.STK_NEXT_ACTION_END_PROACTIVE_SESSION]}},
|
||||
];
|
||||
|
||||
runNextTest();
|
||||
|
@ -12,6 +12,10 @@ function testSetupMenu(command, expect) {
|
||||
is(command.options.items[index].identifier, expect.items[index].identifier, expect.name);
|
||||
is(command.options.items[index].text, expect.items[index].text, expect.name);
|
||||
}
|
||||
let length = command.options.nextActionList ? command.options.nextActionList.length : 0;
|
||||
for (let i = 0; i < length; i++) {
|
||||
is(command.options.nextActionList[i], expect.nextActionList[i], expect.name);
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
}
|
||||
@ -77,7 +81,8 @@ let tests = [
|
||||
expect: {name: "setup_menu_cmd_7",
|
||||
commandQualifier: 0x00,
|
||||
title: "Toolkit Menu",
|
||||
items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}, {identifier: 4, text: "Item 4"}]}},
|
||||
items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}, {identifier: 4, text: "Item 4"}],
|
||||
nextActionList: [iccManager.STK_CMD_SEND_SMS, iccManager.STK_CMD_SET_UP_CALL, iccManager.STK_CMD_LAUNCH_BROWSER, iccManager.STK_CMD_PROVIDE_LOCAL_INFO]}},
|
||||
{command: "d03c810301250082028182850c546f6f6c6b6974204d656e758f07014974656d20318f07024974656d20328f07034974656d20339e0201019f0401050505",
|
||||
func: testSetupMenu,
|
||||
expect: {name: "setup_menu_cmd_8",
|
||||
@ -222,6 +227,13 @@ let tests = [
|
||||
commandQualifier: 0x00,
|
||||
title: "80ル0",
|
||||
items: [{identifier: 17, text: "80ル5"}, {identifier: 18, text: "80ル6"}]}},
|
||||
{command: "d041810301250082028182850c546f6f6c6b6974204d656e758f07014974656d20318f07024974656d20328f07034974656d20338f07044974656d2034180481000000",
|
||||
func: testSetupMenu,
|
||||
expect: {name: "setup_menu_cmd_32",
|
||||
commandQualifier: 0x00,
|
||||
title: "Toolkit Menu",
|
||||
items: [{identifier: 1, text: "Item 1"}, {identifier: 2, text: "Item 2"}, {identifier: 3, text: "Item 3"}, {identifier: 4, text: "Item 4"}],
|
||||
nextActionList: [iccManager.STK_NEXT_ACTION_END_PROACTIVE_SESSION, iccManager.STK_NEXT_ACTION_NULL, iccManager.STK_NEXT_ACTION_NULL, iccManager.STK_NEXT_ACTION_NULL]}},
|
||||
{command: "D00D81030125008202818285008F00",
|
||||
func: testRemoveSetupMenu},
|
||||
{command:"D03B810301250082028182850C546F6F6C6B6974204D656E758F07014974656D20318F07024974656D20328F07034974656D20338F07044974656D2034",
|
||||
|
@ -79,6 +79,12 @@ struct DeviceStorageAvailableParams
|
||||
nsString storageName;
|
||||
};
|
||||
|
||||
struct DeviceStorageFormatParams
|
||||
{
|
||||
nsString type;
|
||||
nsString storageName;
|
||||
};
|
||||
|
||||
struct DeviceStorageAddParams
|
||||
{
|
||||
nsString type;
|
||||
@ -119,6 +125,7 @@ union DeviceStorageParams
|
||||
DeviceStorageFreeSpaceParams;
|
||||
DeviceStorageUsedSpaceParams;
|
||||
DeviceStorageAvailableParams;
|
||||
DeviceStorageFormatParams;
|
||||
};
|
||||
|
||||
struct FMRadioRequestEnableParams
|
||||
|
@ -554,11 +554,6 @@ TabChild::HandlePossibleViewportChange()
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the viewport height is not shorter than the window when the page
|
||||
// is zoomed out to show its full width. Note that before we set the viewport
|
||||
// width, the "full width" of the page isn't properly defined, so that's why
|
||||
// we have to call SetCSSViewport twice - once to set the width, and the
|
||||
// second time to figure out the height based on the layout at that width.
|
||||
float oldBrowserWidth = mOldViewportWidth;
|
||||
mLastMetrics.mViewport.SizeTo(viewport);
|
||||
if (!oldBrowserWidth) {
|
||||
@ -606,13 +601,6 @@ TabChild::HandlePossibleViewportChange()
|
||||
return;
|
||||
}
|
||||
|
||||
CSSToScreenScale minScale(mInnerSize.width / pageSize.width);
|
||||
minScale = clamped(minScale, viewportInfo.GetMinZoom(), viewportInfo.GetMaxZoom());
|
||||
NS_ENSURE_TRUE_VOID(minScale.scale); // (return early rather than divide by 0)
|
||||
|
||||
viewport.height = std::max(viewport.height, screenH / minScale.scale);
|
||||
SetCSSViewport(viewport);
|
||||
|
||||
float oldScreenWidth = mLastMetrics.mCompositionBounds.width;
|
||||
if (!oldScreenWidth) {
|
||||
oldScreenWidth = mInnerSize.width;
|
||||
|
@ -16,6 +16,13 @@
|
||||
|
||||
#define PEERCONNECTION_CONTRACTID "@mozilla.org/peerconnection;1"
|
||||
|
||||
#include "stun_udp_socket_filter.h"
|
||||
|
||||
NS_DEFINE_NAMED_CID(NS_STUN_UDP_SOCKET_FILTER_HANDLER_CID)
|
||||
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsStunUDPSocketFilterHandler)
|
||||
|
||||
|
||||
namespace sipcc
|
||||
{
|
||||
// Factory defined in sipcc::, defines sipcc::PeerConnectionImplConstructor
|
||||
@ -27,11 +34,13 @@ NS_DEFINE_NAMED_CID(PEERCONNECTION_CID);
|
||||
|
||||
static const mozilla::Module::CIDEntry kCIDs[] = {
|
||||
{ &kPEERCONNECTION_CID, false, nullptr, sipcc::PeerConnectionImplConstructor },
|
||||
{ &kNS_STUN_UDP_SOCKET_FILTER_HANDLER_CID, false, nullptr, nsStunUDPSocketFilterHandlerConstructor },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module::ContractIDEntry kContracts[] = {
|
||||
{ PEERCONNECTION_CONTRACTID, &kPEERCONNECTION_CID },
|
||||
{ NS_STUN_UDP_SOCKET_FILTER_HANDLER_CONTRACTID, &kNS_STUN_UDP_SOCKET_FILTER_HANDLER_CID },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
|
@ -22,6 +22,8 @@ interface nsIUDPSocketChild : nsISupports
|
||||
{
|
||||
readonly attribute unsigned short localPort;
|
||||
readonly attribute AUTF8String localAddress;
|
||||
attribute AUTF8String filterName;
|
||||
|
||||
// Tell the chrome process to bind the UDP socket to a given local host and port
|
||||
void bind(in nsIUDPSocketInternal socket, in AUTF8String host, in unsigned short port);
|
||||
|
||||
|
@ -67,7 +67,7 @@ UDPSocketChild::Bind(nsIUDPSocketInternal *aSocket,
|
||||
mSocket = aSocket;
|
||||
AddIPDLReference();
|
||||
|
||||
gNeckoChild->SendPUDPSocketConstructor(this, nsCString(aHost), aPort);
|
||||
gNeckoChild->SendPUDPSocketConstructor(this, nsCString(aHost), aPort, mFilterName);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -149,6 +149,24 @@ UDPSocketChild::GetLocalAddress(nsACString &aLocalAddress)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::SetFilterName(const nsACString &aFilterName)
|
||||
{
|
||||
if (!mFilterName.IsEmpty()) {
|
||||
// filter name can only be set once.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mFilterName = aFilterName;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UDPSocketChild::GetFilterName(nsACString &aFilterName)
|
||||
{
|
||||
aFilterName = mFilterName;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// PUDPSocketChild Methods
|
||||
bool
|
||||
UDPSocketChild::RecvCallback(const nsCString &aType,
|
||||
|
@ -46,6 +46,7 @@ public:
|
||||
private:
|
||||
uint16_t mLocalPort;
|
||||
nsCString mLocalAddress;
|
||||
nsCString mFilterName;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -1,7 +1,10 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsIServiceManager.h"
|
||||
#include "UDPSocketParent.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsIUDPSocket.h"
|
||||
@ -65,6 +68,7 @@ bool
|
||||
UDPSocketParent::Init(const nsCString &aHost, const uint16_t aPort)
|
||||
{
|
||||
nsresult rv;
|
||||
NS_ASSERTION(mFilter, "No packet filter");
|
||||
|
||||
nsCOMPtr<nsIUDPSocket> sock =
|
||||
do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv);
|
||||
@ -124,6 +128,13 @@ UDPSocketParent::RecvData(const InfallibleTArray<uint8_t> &aData,
|
||||
const uint16_t& aPort)
|
||||
{
|
||||
NS_ENSURE_TRUE(mSocket, true);
|
||||
NS_ASSERTION(mFilter, "No packet filter");
|
||||
// TODO, Bug 933102, filter packets that are sent with hostname.
|
||||
// Until then we simply throw away packets that are sent to a hostname.
|
||||
return true;
|
||||
|
||||
#if 0
|
||||
// Enable this once we have filtering working with hostname delivery.
|
||||
uint32_t count;
|
||||
nsresult rv = mSocket->Send(aRemoteAddress,
|
||||
aPort, aData.Elements(),
|
||||
@ -135,6 +146,7 @@ UDPSocketParent::RecvData(const InfallibleTArray<uint8_t> &aData,
|
||||
NS_ENSURE_SUCCESS(rv, true);
|
||||
NS_ENSURE_TRUE(count > 0, true);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
@ -142,9 +154,20 @@ UDPSocketParent::RecvDataWithAddress(const InfallibleTArray<uint8_t>& aData,
|
||||
const mozilla::net::NetAddr& aAddr)
|
||||
{
|
||||
NS_ENSURE_TRUE(mSocket, true);
|
||||
NS_ASSERTION(mFilter, "No packet filter");
|
||||
|
||||
uint32_t count;
|
||||
nsresult rv = mSocket->SendWithAddress(&aAddr, aData.Elements(),
|
||||
aData.Length(), &count);
|
||||
nsresult rv;
|
||||
bool allowed;
|
||||
rv = mFilter->FilterPacket(&aAddr, aData.Elements(),
|
||||
aData.Length(), nsIUDPSocketFilter::SF_OUTGOING,
|
||||
&allowed);
|
||||
// Sending unallowed data, kill content.
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
NS_ENSURE_TRUE(allowed, false);
|
||||
|
||||
rv = mSocket->SendWithAddress(&aAddr, aData.Elements(),
|
||||
aData.Length(), &count);
|
||||
mozilla::unused <<
|
||||
PUDPSocketParent::SendCallback(NS_LITERAL_CSTRING("onsent"),
|
||||
UDPSendResult(rv),
|
||||
@ -191,6 +214,7 @@ UDPSocketParent::OnPacketReceived(nsIUDPSocket* aSocket, nsIUDPMessage* aMessage
|
||||
if (!mIPCOpen) {
|
||||
return NS_OK;
|
||||
}
|
||||
NS_ASSERTION(mFilter, "No packet filter");
|
||||
|
||||
uint16_t port;
|
||||
nsCString ip;
|
||||
@ -205,6 +229,17 @@ UDPSocketParent::OnPacketReceived(nsIUDPSocket* aSocket, nsIUDPMessage* aMessage
|
||||
const char* buffer = data.get();
|
||||
uint32_t len = data.Length();
|
||||
|
||||
bool allowed;
|
||||
mozilla::net::NetAddr addr;
|
||||
fromAddr->GetNetAddr(&addr);
|
||||
nsresult rv = mFilter->FilterPacket(&addr,
|
||||
(const uint8_t*)buffer, len,
|
||||
nsIUDPSocketFilter::SF_INCOMING,
|
||||
&allowed);
|
||||
// Receiving unallowed data, drop.
|
||||
NS_ENSURE_SUCCESS(rv, NS_OK);
|
||||
NS_ENSURE_TRUE(allowed, NS_OK);
|
||||
|
||||
FallibleTArray<uint8_t> fallibleArray;
|
||||
if (!fallibleArray.InsertElementsAt(0, buffer, len)) {
|
||||
FireInternalError(this, __LINE__);
|
||||
|
@ -1,3 +1,5 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
@ -8,6 +10,7 @@
|
||||
#include "mozilla/net/PUDPSocketParent.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIUDPSocket.h"
|
||||
#include "nsIUDPSocketFilter.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -19,7 +22,10 @@ public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIUDPSOCKETLISTENER
|
||||
|
||||
UDPSocketParent() : mIPCOpen(true) {}
|
||||
UDPSocketParent(nsIUDPSocketFilter* filter) :
|
||||
mIPCOpen(true),
|
||||
mFilter(filter) {}
|
||||
|
||||
virtual ~UDPSocketParent();
|
||||
|
||||
bool Init(const nsCString& aHost, const uint16_t aPort);
|
||||
@ -37,6 +43,7 @@ private:
|
||||
|
||||
bool mIPCOpen;
|
||||
nsCOMPtr<nsIUDPSocket> mSocket;
|
||||
nsCOMPtr<nsIUDPSocketFilter> mFilter;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -1,9 +0,0 @@
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function run_test() {
|
||||
Services.prefs.setBoolPref('media.peerconnection.ipc.enabled', true);
|
||||
run_test_in_child("/udpsocket_child.js", function() {
|
||||
Services.prefs.clearUserPref('media.peerconnection.ipc.enabled');
|
||||
do_test_finished();
|
||||
});
|
||||
}
|
@ -1,164 +0,0 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
'use strict';
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
|
||||
const SERVER_PORT = 12345;
|
||||
const DATA_ARRAY = [0, 255, 254, 0, 1, 2, 3, 0, 255, 255, 254, 0];
|
||||
|
||||
function UDPSocketInternalImpl() {
|
||||
}
|
||||
|
||||
UDPSocketInternalImpl.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIUDPSocketInternal]),
|
||||
callListenerError: function(type, message, filename, lineNumber, columnNumber) {
|
||||
if (this.onerror) {
|
||||
this.onerror();
|
||||
} else {
|
||||
do_throw('Received unexpected error: ' + message + ' at ' + filename +
|
||||
':' + lineNumber + ':' + columnNumber);
|
||||
}
|
||||
},
|
||||
callListenerReceivedData: function(type, host, port, data, dataLength) {
|
||||
do_print('*** recv data(' + dataLength + ')=' + data.join() + '\n');
|
||||
if (this.ondata) {
|
||||
try {
|
||||
this.ondata(data, dataLength);
|
||||
} catch(ex) {
|
||||
if (ex === Cr.NS_ERROR_ABORT)
|
||||
throw ex;
|
||||
do_print('Caught exception: ' + ex + '\n' + ex.stack);
|
||||
do_throw('test is broken; bad ondata handler; see above');
|
||||
}
|
||||
} else {
|
||||
do_throw('Received ' + dataLength + ' bytes of unexpected data!');
|
||||
}
|
||||
},
|
||||
callListenerVoid: function(type) {
|
||||
switch (type) {
|
||||
case 'onopen':
|
||||
if (this.onopen) {
|
||||
this.onopen();
|
||||
}
|
||||
break;
|
||||
case 'onclose':
|
||||
if (this.onclose) {
|
||||
this.onclose();
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
callListenerSent: function(type, value) {
|
||||
if (value != Cr.NS_OK) {
|
||||
do_throw('Previous send was failed with cause: ' + value);
|
||||
}
|
||||
},
|
||||
updateReadyState: function(readyState) {
|
||||
do_print('*** current state: ' + readyState + '\n');
|
||||
},
|
||||
onopen: function() {},
|
||||
onclose: function() {},
|
||||
};
|
||||
|
||||
function makeSuccessCase(name) {
|
||||
return function() {
|
||||
do_print('got expected: ' + name);
|
||||
run_next_test();
|
||||
};
|
||||
}
|
||||
|
||||
function makeJointSuccess(names) {
|
||||
let funcs = {}, successCount = 0;
|
||||
names.forEach(function(name) {
|
||||
funcs[name] = function() {
|
||||
do_print('got excepted: ' + name);
|
||||
if (++successCount === names.length)
|
||||
run_next_test();
|
||||
};
|
||||
});
|
||||
return funcs;
|
||||
}
|
||||
|
||||
function makeExpectedData(expectedData, callback) {
|
||||
return function(receivedData, receivedDataLength) {
|
||||
if (receivedDataLength != expectedData.length) {
|
||||
do_throw('Received data size mismatched, expected ' + expectedData.length +
|
||||
' but got ' + receivedDataLength);
|
||||
}
|
||||
for (let i = 0; i < receivedDataLength; i++) {
|
||||
if (receivedData[i] != expectedData[i]) {
|
||||
do_throw('Received mismatched data at position ' + i);
|
||||
}
|
||||
}
|
||||
if (callback) {
|
||||
callback();
|
||||
} else {
|
||||
run_next_test();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function makeFailureCase(name) {
|
||||
return function() {
|
||||
let argstr;
|
||||
if (arguments.length) {
|
||||
argstr = '(args: ' +
|
||||
Array.map(arguments, function(x) {return x.data + ""; }).join(" ") + ')';
|
||||
} else {
|
||||
argstr = '(no arguments)';
|
||||
}
|
||||
do_throw('got unexpected: ' + name + ' ' + argstr);
|
||||
};
|
||||
}
|
||||
|
||||
function createSocketChild() {
|
||||
return Cc['@mozilla.org/udp-socket-child;1']
|
||||
.createInstance(Ci.nsIUDPSocketChild);
|
||||
}
|
||||
|
||||
var UDPSocket = createSocketChild();
|
||||
var callback = new UDPSocketInternalImpl();
|
||||
|
||||
function connectSock() {
|
||||
UDPSocket.bind(callback, '127.0.0.1', SERVER_PORT);
|
||||
callback.onopen = makeSuccessCase('open');
|
||||
}
|
||||
|
||||
function sendData() {
|
||||
UDPSocket.send('127.0.0.1', SERVER_PORT, DATA_ARRAY, DATA_ARRAY.length);
|
||||
callback.ondata = makeExpectedData(DATA_ARRAY);
|
||||
}
|
||||
|
||||
function clientClose() {
|
||||
UDPSocket.close();
|
||||
callback.ondata = makeFailureCase('data');
|
||||
callback.onclose = makeSuccessCase('close');
|
||||
}
|
||||
|
||||
function connectError() {
|
||||
UDPSocket = createSocketChild();
|
||||
UDPSocket.bind(callback, 'some non-IP string', SERVER_PORT);
|
||||
callback.onerror = makeSuccessCase('error');
|
||||
callback.onopen = makeFailureCase('open');
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
UDPSocket = null;
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_test(connectSock);
|
||||
add_test(sendData);
|
||||
add_test(clientClose);
|
||||
add_test(connectError);
|
||||
add_test(cleanup);
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
@ -1,10 +1,7 @@
|
||||
[DEFAULT]
|
||||
head =
|
||||
tail =
|
||||
support-files =
|
||||
udpsocket_child.js
|
||||
|
||||
[test_tcpsocket_ipc.js]
|
||||
[test_tcpserversocket_ipc.js]
|
||||
[test_udpsocket_ipc.js]
|
||||
run-sequentially = Uses hardcoded port, bug 903830.
|
||||
|
@ -263,6 +263,21 @@ public:
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
void FormatVolume(const nsACString& aVolumeName)
|
||||
{
|
||||
RefPtr<Volume> vol = VolumeManager::FindVolumeByName(aVolumeName);
|
||||
if (!vol) {
|
||||
return;
|
||||
}
|
||||
if (vol->IsFormatRequested()) {
|
||||
return;
|
||||
}
|
||||
vol->SetFormatRequested(true);
|
||||
DBG("Calling UpdateState due to volume %s formatting set to %d",
|
||||
vol->NameStr(), (int)vol->IsFormatRequested());
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
AutoVolumeEventObserver mVolumeEventObserver;
|
||||
@ -428,14 +443,14 @@ AutoMounter::UpdateState()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tryToShare && vol->IsSharingEnabled()) {
|
||||
if ((tryToShare && vol->IsSharingEnabled()) || vol->IsFormatRequested()) {
|
||||
// We're going to try to unmount and share the volumes
|
||||
switch (volState) {
|
||||
case nsIVolume::STATE_MOUNTED: {
|
||||
if (vol->IsMountLocked()) {
|
||||
// The volume is currently locked, so leave it in the mounted
|
||||
// state.
|
||||
LOGW("UpdateState: Mounted volume %s is locked, not sharing",
|
||||
LOGW("UpdateState: Mounted volume %s is locked, not sharing or formatting",
|
||||
vol->NameStr());
|
||||
break;
|
||||
}
|
||||
@ -444,7 +459,11 @@ AutoMounter::UpdateState()
|
||||
// apps which watch device storage notifications to see the volume
|
||||
// go into the shared state, and prompt them to close any open files
|
||||
// that they might have.
|
||||
vol->SetIsSharing(true);
|
||||
if (tryToShare && vol->IsSharingEnabled()) {
|
||||
vol->SetIsSharing(true);
|
||||
} else if (vol->IsFormatRequested()){
|
||||
vol->SetIsFormatting(true);
|
||||
}
|
||||
|
||||
// Check to see if there are any open files on the volume and
|
||||
// don't initiate the unmount while there are open files.
|
||||
@ -461,7 +480,7 @@ AutoMounter::UpdateState()
|
||||
fileInfo.mComm.get(),
|
||||
fileInfo.mExe.get());
|
||||
} while (fileFinder.Next(&fileInfo));
|
||||
LOGW("UpdateState: Mounted volume %s has open files, not sharing",
|
||||
LOGW("UpdateState: Mounted volume %s has open files, not sharing or formatting",
|
||||
vol->NameStr());
|
||||
|
||||
// Check again in a few seconds to see if the files are closed.
|
||||
@ -495,10 +514,23 @@ AutoMounter::UpdateState()
|
||||
return; // UpdateState will be called again when the Unmount command completes
|
||||
}
|
||||
case nsIVolume::STATE_IDLE: {
|
||||
// Volume is unmounted. We can go ahead and share.
|
||||
LOG("UpdateState: Sharing %s", vol->NameStr());
|
||||
vol->StartShare(mResponseCallback);
|
||||
return; // UpdateState will be called again when the Share command completes
|
||||
LOG("UpdateState: Volume %s is nsIVolume::STATE_IDLE", vol->NameStr());
|
||||
if (vol->IsFormatting() && !vol->IsFormatRequested()) {
|
||||
vol->SetFormatRequested(false);
|
||||
LOG("UpdateState: Mounting %s", vol->NameStr());
|
||||
vol->StartMount(mResponseCallback);
|
||||
break;
|
||||
}
|
||||
if (tryToShare && vol->IsSharingEnabled()) {
|
||||
// Volume is unmounted. We can go ahead and share.
|
||||
LOG("UpdateState: Sharing %s", vol->NameStr());
|
||||
vol->StartShare(mResponseCallback);
|
||||
} else if (vol->IsFormatRequested()){
|
||||
// Volume is unmounted. We can go ahead and format.
|
||||
LOG("UpdateState: Formatting %s", vol->NameStr());
|
||||
vol->StartFormat(mResponseCallback);
|
||||
}
|
||||
return; // UpdateState will be called again when the Share/Format command completes
|
||||
}
|
||||
default: {
|
||||
// Not in a state that we can do anything about.
|
||||
@ -578,6 +610,15 @@ SetAutoMounterSharingModeIOThread(const nsCString& aVolumeName, const bool& aAll
|
||||
sAutoMounter->SetSharingMode(aVolumeName, aAllowSharing);
|
||||
}
|
||||
|
||||
static void
|
||||
AutoMounterFormatVolumeIOThread(const nsCString& aVolumeName)
|
||||
{
|
||||
MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
|
||||
MOZ_ASSERT(sAutoMounter);
|
||||
|
||||
sAutoMounter->FormatVolume(aVolumeName);
|
||||
}
|
||||
|
||||
static void
|
||||
UsbCableEventIOThread()
|
||||
{
|
||||
@ -732,10 +773,19 @@ SetAutoMounterSharingMode(const nsCString& aVolumeName, bool aAllowSharing)
|
||||
{
|
||||
XRE_GetIOMessageLoop()->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableFunction(SetAutoMounterSharingModeIOThread,
|
||||
NewRunnableFunction(SetAutoMounterSharingModeIOThread,
|
||||
aVolumeName, aAllowSharing));
|
||||
}
|
||||
|
||||
void
|
||||
AutoMounterFormatVolume(const nsCString& aVolumeName)
|
||||
{
|
||||
XRE_GetIOMessageLoop()->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableFunction(AutoMounterFormatVolumeIOThread,
|
||||
aVolumeName));
|
||||
}
|
||||
|
||||
void
|
||||
ShutdownAutoMounter()
|
||||
{
|
||||
|
@ -59,6 +59,15 @@ GetAutoMounterStatus();
|
||||
void
|
||||
SetAutoMounterSharingMode(const nsCString& aVolumeName, bool aAllowSharing);
|
||||
|
||||
/**
|
||||
* Formats the volume with specified volume name.
|
||||
*
|
||||
* If the volume is ready to format, automounter
|
||||
* will unmount it, format it and then mount it again.
|
||||
*/
|
||||
void
|
||||
AutoMounterFormatVolume(const nsCString& aVolumeName);
|
||||
|
||||
/**
|
||||
* Shuts down the automounter.
|
||||
*
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user