mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 967319 - Sort object properties in natural order in the Variables View. r=jaws (e604fc318) - Bug 1023386 - Split and filter properties remotely for objects. r=past (b4889035f) - Bug 1149115 - Make sure source actors are created before breakpoints are reset;r=jlong (76561a3fd) - Bug 1154606 - improve error in debugger when loading source fails r=jsantell (3678eca90) - Bug 1159731 - Move all Addon actor subclasses to a dedicated file. r=ejpbruel (45e012a98) - Bug 1096294 - Display pseudo-arrays like arrays in the console; r=pbrosset (b4b129b3a) - Bug 1169064 - Part 1: Move ObjectActor to object.js r=fitzgen (579cb4f86) - Bug 1169064 - Part 2: Formatted object.js and removed unused protocol request arguments r=fitzgen (4f3178a6a) - Bug 792063 - Add status console shortcut to return the previous command result;r=past (fc1317f9b) - Bug 1169064 - Part 3: Refactor LongStringActor, createValueGrip, stringIsLong and longStripGrip from script.js to object.js r=fitzgen (ecdff41b7) - AltiVec/VMX is 32bit only, use double cast passing uintptr_t to int to fix compilation on PPC64 (93985b589) - Bug 1084525 - Part 7: Expose Promise life time in object grip r=fitzgen (db5000041) - Bug 1084525 - Part 8: Expose Promise time to settle in object grip r=fitzgen (65b7beb26) - Bug 1084525 - Part 9: Implement getDependentPromises method in ObjectClient r=fitzgen (8cc8be31d) - Bug 1148753 - Update browser content toolbox to create a TabSources instance. r=jryans (df6bf505f) - Bug 1050691 - Click on a function on the console should go to the debugger. r=jlongster (e0d225db1) - Bug 1084525 - Part 10: Implement getAllocationStack method in ObjectClient r=fitzgen (199ce4dd9) - Bug 1084525 - Part 12: Fix eslint complaints in promise.js r=fitzgen (7d0a38afe) - Bug 1084525 - Part 13: Add test for asserting the Promise allocation stack in chrome debugging r=fitzgen (60278cf1d) - Bug 1164564 - Refactor Promise-backend.js so it can be required as a CommonJS module on the main thread;r=paolo (7cfe3cdd9) - Bug 1181506 - Define Cc and Ci. r=Yoric (bc3968be3)
This commit is contained in:
+4
-4
@@ -295,10 +295,10 @@ void loop(int32_t startIdx, int32_t endIdx,
|
||||
vector float reciprocal,
|
||||
int32_t y)
|
||||
{
|
||||
int topLeftIndex = reinterpret_cast<uint32_t>(topLeftBase + startIdx) & 0xf ? 0 : 1;
|
||||
int topRightIndex = reinterpret_cast<uint32_t>(topRightBase + startIdx) & 0xf ? 0 : 1;
|
||||
int bottomRightIndex = reinterpret_cast<uint32_t>(bottomRightBase + startIdx) & 0xf ? 0 : 1;
|
||||
int bottomLeftIndex = reinterpret_cast<uint32_t>(bottomLeftBase + startIdx) & 0xf ? 0 : 1;
|
||||
int topLeftIndex = static_cast<int>(reinterpret_cast<uintptr_t>(topLeftBase + startIdx)) & 0xf ? 0 : 1;
|
||||
int topRightIndex = static_cast<int>(reinterpret_cast<uintptr_t>(topRightBase + startIdx)) & 0xf ? 0 : 1;
|
||||
int bottomRightIndex = static_cast<int>(reinterpret_cast<uintptr_t>(bottomRightBase + startIdx)) & 0xf ? 0 : 1;
|
||||
int bottomLeftIndex = static_cast<int>(reinterpret_cast<uintptr_t>(bottomLeftBase + startIdx)) & 0xf ? 0 : 1;
|
||||
|
||||
vector unsigned char topLeftMask = vec_lvsl(0, reinterpret_cast<unsigned char*>(topLeftBase + startIdx));
|
||||
vector unsigned int topLeftVector1 = vec_ld(0, topLeftBase + startIdx);
|
||||
|
||||
@@ -5075,6 +5075,20 @@
|
||||
"n_buckets": "80 + 1",
|
||||
"description": "The time (in milliseconds) after showing a PopupNotification that the mainAction was first triggered"
|
||||
},
|
||||
"DEVTOOLS_DEBUGGER_RDP_LOCAL_ENUMPROPERTIES_MS": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "10000",
|
||||
"n_buckets": "1000",
|
||||
"description": "The time (in milliseconds) that it took a 'enumProperties' request to go round trip."
|
||||
},
|
||||
"DEVTOOLS_DEBUGGER_RDP_REMOTE_ENUMPROPERTIES_MS": {
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": "10000",
|
||||
"n_buckets": "1000",
|
||||
"description": "The time (in milliseconds) that it took a 'enumProperties' request to go round trip."
|
||||
},
|
||||
"VIDEO_CANPLAYTYPE_H264_CONSTRAINT_SET_FLAG": {
|
||||
"expires_in_version": "40",
|
||||
"kind": "enumerated",
|
||||
|
||||
@@ -49,7 +49,7 @@ exports.reportException = function reportException(aWho, aException) {
|
||||
|
||||
dump(msg + "\n");
|
||||
|
||||
if (Cu.reportError) {
|
||||
if (Cu && Cu.reportError) {
|
||||
/*
|
||||
* Note that the xpcshell test harness registers an observer for
|
||||
* console messages, so when we're running tests, this will cause
|
||||
|
||||
@@ -30,7 +30,6 @@ this.EXPORTED_SYMBOLS = ["DevToolsLoader", "devtools", "BuiltinProvider",
|
||||
let loaderModules = {
|
||||
"Services": Object.create(Services),
|
||||
"toolkit/loader": loader,
|
||||
"promise": promise,
|
||||
"PromiseDebugging": PromiseDebugging
|
||||
};
|
||||
XPCOMUtils.defineLazyGetter(loaderModules, "Debugger", () => {
|
||||
@@ -96,6 +95,7 @@ BuiltinProvider.prototype = {
|
||||
"devtools/content-observer": "resource://gre/modules/devtools/content-observer",
|
||||
"gcli": "resource://gre/modules/devtools/gcli",
|
||||
"projecteditor": "resource://gre/modules/devtools/projecteditor",
|
||||
"promise": "resource://gre/modules/Promise-backend.js",
|
||||
"acorn": "resource://gre/modules/devtools/acorn",
|
||||
"acorn/util/walk": "resource://gre/modules/devtools/acorn/walk.js",
|
||||
"tern": "resource://gre/modules/devtools/tern",
|
||||
@@ -135,6 +135,7 @@ SrcdirProvider.prototype = {
|
||||
srcdir = OS.Path.normalize(srcdir.data.trim());
|
||||
let devtoolsDir = OS.Path.join(srcdir, "browser", "devtools");
|
||||
let toolkitDir = OS.Path.join(srcdir, "toolkit", "devtools");
|
||||
let modulesDir = OS.Path.join(srcdir, "toolkit", "modules");
|
||||
let mainURI = this.fileURI(OS.Path.join(devtoolsDir, "main.js"));
|
||||
let definitionsURI = this.fileURI(OS.Path.join(devtoolsDir, "definitions.js"));
|
||||
let devtoolsURI = this.fileURI(devtoolsDir);
|
||||
@@ -153,6 +154,7 @@ SrcdirProvider.prototype = {
|
||||
let contentObserverURI = this.fileURI(OS.Path.join(toolkitDir), "content-observer.js");
|
||||
let gcliURI = this.fileURI(OS.Path.join(toolkitDir, "gcli", "source", "lib", "gcli"));
|
||||
let projecteditorURI = this.fileURI(OS.Path.join(devtoolsDir, "projecteditor"));
|
||||
let promiseURI = this.fileURI(OS.Path.join(modulesDir, "promise-backend.js"));
|
||||
let acornURI = this.fileURI(OS.Path.join(toolkitDir, "acorn"));
|
||||
let acornWalkURI = OS.Path.join(acornURI, "walk.js");
|
||||
let ternURI = OS.Path.join(toolkitDir, "tern");
|
||||
@@ -180,6 +182,7 @@ SrcdirProvider.prototype = {
|
||||
"devtools/content-observer": contentObserverURI,
|
||||
"gcli": gcliURI,
|
||||
"projecteditor": projecteditorURI,
|
||||
"promise": promiseURI,
|
||||
"acorn": acornURI,
|
||||
"acorn/util/walk": acornWalkURI,
|
||||
"tern": ternURI,
|
||||
|
||||
@@ -2171,7 +2171,36 @@ ObjectClient.prototype = {
|
||||
}
|
||||
return aPacket;
|
||||
}
|
||||
})
|
||||
}),
|
||||
|
||||
/**
|
||||
* Request the promises directly depending on the current promise.
|
||||
*/
|
||||
getDependentPromises: DebuggerClient.requester({
|
||||
type: "dependentPromises"
|
||||
}, {
|
||||
before: function(aPacket) {
|
||||
if (this._grip.class !== "Promise") {
|
||||
throw new Error("getDependentPromises is only valid for promise " +
|
||||
"grips.");
|
||||
}
|
||||
return aPacket;
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Request the stack to the promise's allocation point.
|
||||
*/
|
||||
getPromiseAllocationStack: DebuggerClient.requester({
|
||||
type: "allocationStack"
|
||||
}, {
|
||||
before: function(aPacket) {
|
||||
if (this._grip.class !== "Promise") {
|
||||
throw new Error("getAllocationStack is only valid for promise grips.");
|
||||
}
|
||||
return aPacket;
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -2320,6 +2349,39 @@ SourceClient.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Request a PropertyIteratorClient instance to ease listing
|
||||
* properties for this object.
|
||||
*
|
||||
* @param options Object
|
||||
* A dictionary object with various boolean attributes:
|
||||
* - ignoreSafeGetters Boolean
|
||||
* If true, do not iterate over safe getters.
|
||||
* - ignoreIndexedProperties Boolean
|
||||
* If true, filters out Array items.
|
||||
* e.g. properties names between `0` and `object.length`.
|
||||
* - ignoreNonIndexedProperties Boolean
|
||||
* If true, filters out items that aren't array items
|
||||
* e.g. properties names that are not a number between `0`
|
||||
* and `object.length`.
|
||||
* - sort Boolean
|
||||
* If true, the iterator will sort the properties by name
|
||||
* before dispatching them.
|
||||
* @param aOnResponse function Called with the client instance.
|
||||
*/
|
||||
enumProperties: DebuggerClient.requester({
|
||||
type: "enumProperties",
|
||||
options: args(0)
|
||||
}, {
|
||||
after: function(aResponse) {
|
||||
if (aResponse.iterator) {
|
||||
return { iterator: new PropertyIteratorClient(this._client, aResponse.iterator) };
|
||||
}
|
||||
return aResponse;
|
||||
},
|
||||
telemetry: "ENUMPROPERTIES"
|
||||
}),
|
||||
|
||||
/**
|
||||
* Pretty print this source's text.
|
||||
*/
|
||||
@@ -2458,6 +2520,74 @@ SourceClient.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A PropertyIteratorClient provides a way to access to property names and
|
||||
* values of an object efficiently, slice by slice.
|
||||
* Note that the properties can be sorted in the backend,
|
||||
* this is controled while creating the PropertyIteratorClient
|
||||
* from ObjectClient.enumProperties.
|
||||
*
|
||||
* @param aClient DebuggerClient
|
||||
* The debugger client parent.
|
||||
* @param aGrip Object
|
||||
* A PropertyIteratorActor grip returned by the protocol via
|
||||
* TabActor.enumProperties request.
|
||||
*/
|
||||
function PropertyIteratorClient(aClient, aGrip) {
|
||||
this._grip = aGrip;
|
||||
this._client = aClient;
|
||||
this.request = this._client.request;
|
||||
}
|
||||
|
||||
PropertyIteratorClient.prototype = {
|
||||
get actor() { return this._grip.actor; },
|
||||
|
||||
/**
|
||||
* Get the total number of properties available in the iterator.
|
||||
*/
|
||||
get count() { return this._grip.count; },
|
||||
|
||||
/**
|
||||
* Get one or more property names that correspond to the positions in the
|
||||
* indexes parameter.
|
||||
*
|
||||
* @param indexes Array
|
||||
* An array of property indexes.
|
||||
* @param aCallback Function
|
||||
* The function called when we receive the property names.
|
||||
*/
|
||||
names: DebuggerClient.requester({
|
||||
type: "names",
|
||||
indexes: args(0)
|
||||
}, {}),
|
||||
|
||||
/**
|
||||
* Get a set of following property value(s).
|
||||
*
|
||||
* @param start Number
|
||||
* The index of the first property to fetch.
|
||||
* @param count Number
|
||||
* The number of properties to fetch.
|
||||
* @param aCallback Function
|
||||
* The function called when we receive the property values.
|
||||
*/
|
||||
slice: DebuggerClient.requester({
|
||||
type: "slice",
|
||||
start: args(0),
|
||||
count: args(1)
|
||||
}, {}),
|
||||
|
||||
/**
|
||||
* Get all the property values.
|
||||
*
|
||||
* @param aCallback Function
|
||||
* The function called when we receive the property values.
|
||||
*/
|
||||
all: DebuggerClient.requester({
|
||||
type: "all"
|
||||
}, {}),
|
||||
};
|
||||
|
||||
/**
|
||||
* Breakpoint clients are used to remove breakpoints that are no longer used.
|
||||
*
|
||||
|
||||
@@ -424,7 +424,8 @@ let DebuggerView = {
|
||||
deferred.resolve([aSource, aText, aContentType]);
|
||||
},
|
||||
([, aError]) => {
|
||||
let msg = L10N.getStr("errorLoadingText") + DevToolsUtils.safeErrorString(aError);
|
||||
let url = aError;
|
||||
let msg = L10N.getFormatStr("errorLoadingText2", url);
|
||||
this._setEditorText(msg);
|
||||
Cu.reportError(msg);
|
||||
dumpn(msg);
|
||||
|
||||
@@ -78,6 +78,7 @@ support-files =
|
||||
doc_pretty-print-2.html
|
||||
doc_pretty-print-3.html
|
||||
doc_pretty-print-on-paused.html
|
||||
doc_promise-get-allocation-stack.html
|
||||
doc_promise.html
|
||||
doc_random-javascript.html
|
||||
doc_recursion-stack.html
|
||||
@@ -330,7 +331,11 @@ skip-if = e10s && debug
|
||||
[browser_dbg_pretty-print-on-paused.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_progress-listener-bug.js]
|
||||
skip-if = e10a && debug
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_promises-allocation-stack.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_promises-chrome-allocation-stack.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_reload-preferred-script-01.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_reload-preferred-script-02.js]
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that we can get a stack to a promise's allocation point.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_promise-get-allocation-stack.html";
|
||||
const { PromisesFront } = devtools.require("devtools/server/actors/promises");
|
||||
let events = devtools.require("sdk/event/core");
|
||||
|
||||
function test() {
|
||||
Task.spawn(function* () {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.addBrowserActors();
|
||||
|
||||
const [ tab,, panel ] = yield initDebugger(TAB_URL);
|
||||
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
yield connect(client);
|
||||
|
||||
let { tabs } = yield listTabs(client);
|
||||
let targetTab = findTab(tabs, TAB_URL);
|
||||
yield attachTab(client, targetTab);
|
||||
|
||||
yield testGetAllocationStack(client, targetTab, tab);
|
||||
|
||||
yield close(client);
|
||||
yield closeDebuggerAndFinish(panel);
|
||||
}).then(null, error => {
|
||||
ok(false, "Got an error: " + error.message + "\n" + error.stack);
|
||||
});
|
||||
}
|
||||
|
||||
function* testGetAllocationStack(client, form, tab) {
|
||||
let front = PromisesFront(client, form);
|
||||
|
||||
yield front.attach();
|
||||
yield front.listPromises();
|
||||
|
||||
// Get the grip for promise p
|
||||
let onNewPromise = new Promise(resolve => {
|
||||
events.on(front, "new-promises", promises => {
|
||||
for (let p of promises) {
|
||||
if (p.preview.ownProperties.name &&
|
||||
p.preview.ownProperties.name.value === "p") {
|
||||
resolve(p);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
callInTab(tab, "makePromises");
|
||||
|
||||
let grip = yield onNewPromise;
|
||||
ok(grip, "Found our promise p");
|
||||
|
||||
let objectClient = new ObjectClient(client, grip);
|
||||
ok(objectClient, "Got Object Client");
|
||||
|
||||
yield new Promise(resolve => {
|
||||
objectClient.getPromiseAllocationStack(response => {
|
||||
ok(response.allocationStack.length, "Got promise allocation stack.");
|
||||
|
||||
for (let stack of response.allocationStack) {
|
||||
is(stack.source.url, TAB_URL, "Got correct source URL.");
|
||||
is(stack.functionDisplayName, "makePromises",
|
||||
"Got correct function display name.");
|
||||
is(typeof stack.line, "number", "Expect stack line to be a number.");
|
||||
is(typeof stack.column, "number",
|
||||
"Expect stack column to be a number.");
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
yield front.detach();
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that we can get a stack to a promise's allocation point in the chrome
|
||||
* process.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const SOURCE_URL = "browser_dbg_promises-chrome-allocation-stack.js";
|
||||
const { PromisesFront } = devtools.require("devtools/server/actors/promises");
|
||||
let events = devtools.require("sdk/event/core");
|
||||
|
||||
const STACK_DATA = [
|
||||
{ functionDisplayName: "test/</<" },
|
||||
{ functionDisplayName: "testGetAllocationStack" },
|
||||
];
|
||||
|
||||
function test() {
|
||||
Task.spawn(function* () {
|
||||
requestLongerTimeout(10);
|
||||
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.addBrowserActors();
|
||||
DebuggerServer.allowChromeProcess = true;
|
||||
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
yield connect(client);
|
||||
let chrome = yield client.getProcess();
|
||||
let [, tabClient] = yield attachTab(client, chrome.form);
|
||||
yield tabClient.attachThread();
|
||||
|
||||
yield testGetAllocationStack(client, chrome.form, () => {
|
||||
let p = new Promise(() => {});
|
||||
p.name = "p";
|
||||
let q = p.then();
|
||||
q.name = "q";
|
||||
let r = p.then(null, () => {});
|
||||
r.name = "r";
|
||||
});
|
||||
|
||||
yield close(client);
|
||||
finish();
|
||||
}).then(null, error => {
|
||||
ok(false, "Got an error: " + error.message + "\n" + error.stack);
|
||||
});
|
||||
}
|
||||
|
||||
function* testGetAllocationStack(client, form, makePromises) {
|
||||
let front = PromisesFront(client, form);
|
||||
|
||||
yield front.attach();
|
||||
yield front.listPromises();
|
||||
|
||||
// Get the grip for promise p
|
||||
let onNewPromise = new Promise(resolve => {
|
||||
events.on(front, "new-promises", promises => {
|
||||
for (let p of promises) {
|
||||
if (p.preview.ownProperties.name &&
|
||||
p.preview.ownProperties.name.value === "p") {
|
||||
resolve(p);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
makePromises();
|
||||
|
||||
let grip = yield onNewPromise;
|
||||
ok(grip, "Found our promise p");
|
||||
|
||||
let objectClient = new ObjectClient(client, grip);
|
||||
ok(objectClient, "Got Object Client");
|
||||
|
||||
yield new Promise(resolve => {
|
||||
objectClient.getPromiseAllocationStack(response => {
|
||||
ok(response.allocationStack.length, "Got promise allocation stack.");
|
||||
|
||||
for (let i = 0; i < STACK_DATA.length; i++) {
|
||||
let data = STACK_DATA[i];
|
||||
let stack = response.allocationStack[i];
|
||||
|
||||
ok(stack.source.url.startsWith("chrome:"), "Got a chrome source URL");
|
||||
ok(stack.source.url.endsWith(SOURCE_URL), "Got correct source URL.");
|
||||
is(stack.functionDisplayName, data.functionDisplayName,
|
||||
"Got correct function display name.");
|
||||
is(typeof stack.line, "number", "Expect stack line to be a number.");
|
||||
is(typeof stack.column, "number",
|
||||
"Expect stack column to be a number.");
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
yield front.detach();
|
||||
}
|
||||
@@ -36,7 +36,7 @@ function showBogusSource() {
|
||||
}
|
||||
|
||||
function testDebuggerLoadingError() {
|
||||
ok(gEditor.getText().contains(gL10N.getStr("errorLoadingText")),
|
||||
ok(gEditor.getText().includes(gL10N.getFormatStr("errorLoadingText2", "noSuchActor")),
|
||||
"The valid error loading message is displayed.");
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ function initialChecks() {
|
||||
|
||||
is(objectVar.target.querySelector(".name").getAttribute("value"), "largeObject",
|
||||
"Should have the right property name for 'largeObject'.");
|
||||
is(objectVar.target.querySelector(".value").getAttribute("value"), "Object",
|
||||
is(objectVar.target.querySelector(".value").getAttribute("value"), "Object[10000]",
|
||||
"Should have the right property value for 'largeObject'.");
|
||||
ok(objectVar.target.querySelector(".value").className.contains("token-other"),
|
||||
"Should have the right token class for 'largeObject'.");
|
||||
@@ -70,10 +70,7 @@ function initialChecks() {
|
||||
is(objectVar.expanded, false,
|
||||
"The 'largeObject' variable shouldn't be expanded.");
|
||||
|
||||
let finished = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_PROPERTIES, 2);
|
||||
arrayVar.expand();
|
||||
objectVar.expand();
|
||||
return finished;
|
||||
return promise.all([arrayVar.expand(),objectVar.expand()]);
|
||||
}
|
||||
|
||||
function verifyFirstLevel() {
|
||||
@@ -96,55 +93,56 @@ function verifyFirstLevel() {
|
||||
"The 'largeObject' should contain all the created non-enumerable elements.");
|
||||
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[0].getAttribute("value"),
|
||||
0 + gEllipsis + 1999, "The first page in the 'largeArray' is named correctly.");
|
||||
"[0" + gEllipsis + "2499]", "The first page in the 'largeArray' is named correctly.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[0].getAttribute("value"),
|
||||
"", "The first page in the 'largeArray' should not have a corresponding value.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[1].getAttribute("value"),
|
||||
2000 + gEllipsis + 3999, "The second page in the 'largeArray' is named correctly.");
|
||||
"[2500" + gEllipsis + "4999]", "The second page in the 'largeArray' is named correctly.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[1].getAttribute("value"),
|
||||
"", "The second page in the 'largeArray' should not have a corresponding value.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[2].getAttribute("value"),
|
||||
4000 + gEllipsis + 5999, "The third page in the 'largeArray' is named correctly.");
|
||||
"[5000" + gEllipsis + "7499]", "The third page in the 'largeArray' is named correctly.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[2].getAttribute("value"),
|
||||
"", "The third page in the 'largeArray' should not have a corresponding value.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[3].getAttribute("value"),
|
||||
6000 + gEllipsis + 9999, "The fourth page in the 'largeArray' is named correctly.");
|
||||
"[7500" + gEllipsis + "9999]", "The fourth page in the 'largeArray' is named correctly.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[3].getAttribute("value"),
|
||||
"", "The fourth page in the 'largeArray' should not have a corresponding value.");
|
||||
|
||||
is(objectVar.target.querySelectorAll(".variables-view-property .name")[0].getAttribute("value"),
|
||||
0 + gEllipsis + 1999, "The first page in the 'largeObject' is named correctly.");
|
||||
"[0" + gEllipsis + "2499]", "The first page in the 'largeObject' is named correctly.");
|
||||
is(objectVar.target.querySelectorAll(".variables-view-property .value")[0].getAttribute("value"),
|
||||
"", "The first page in the 'largeObject' should not have a corresponding value.");
|
||||
is(objectVar.target.querySelectorAll(".variables-view-property .name")[1].getAttribute("value"),
|
||||
2000 + gEllipsis + 3999, "The second page in the 'largeObject' is named correctly.");
|
||||
"[2500" + gEllipsis + "4999]", "The second page in the 'largeObject' is named correctly.");
|
||||
is(objectVar.target.querySelectorAll(".variables-view-property .value")[1].getAttribute("value"),
|
||||
"", "The second page in the 'largeObject' should not have a corresponding value.");
|
||||
is(objectVar.target.querySelectorAll(".variables-view-property .name")[2].getAttribute("value"),
|
||||
4000 + gEllipsis + 5999, "The thrid page in the 'largeObject' is named correctly.");
|
||||
"[5000" + gEllipsis + "7499]", "The thrid page in the 'largeObject' is named correctly.");
|
||||
is(objectVar.target.querySelectorAll(".variables-view-property .value")[2].getAttribute("value"),
|
||||
"", "The thrid page in the 'largeObject' should not have a corresponding value.");
|
||||
is(objectVar.target.querySelectorAll(".variables-view-property .name")[3].getAttribute("value"),
|
||||
6000 + gEllipsis + 9999, "The fourth page in the 'largeObject' is named correctly.");
|
||||
"[7500" + gEllipsis + "9999]", "The fourth page in the 'largeObject' is named correctly.");
|
||||
is(objectVar.target.querySelectorAll(".variables-view-property .value")[3].getAttribute("value"),
|
||||
"", "The fourth page in the 'largeObject' should not have a corresponding value.");
|
||||
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[4].getAttribute("value"),
|
||||
"length", "The other properties 'largeArray' are named correctly.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[4].getAttribute("value"),
|
||||
"10000", "The other properties 'largeArray' have the correct value.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[5].getAttribute("value"),
|
||||
"buffer", "The other properties 'largeArray' are named correctly.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[5].getAttribute("value"),
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[4].getAttribute("value"),
|
||||
"ArrayBuffer", "The other properties 'largeArray' have the correct value.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[6].getAttribute("value"),
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[5].getAttribute("value"),
|
||||
"byteLength", "The other properties 'largeArray' are named correctly.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[6].getAttribute("value"),
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[5].getAttribute("value"),
|
||||
"10000", "The other properties 'largeArray' have the correct value.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[7].getAttribute("value"),
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[6].getAttribute("value"),
|
||||
"byteOffset", "The other properties 'largeArray' are named correctly.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[7].getAttribute("value"),
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[6].getAttribute("value"),
|
||||
"0", "The other properties 'largeArray' have the correct value.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[7].getAttribute("value"),
|
||||
"length", "The other properties 'largeArray' are named correctly.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[7].getAttribute("value"),
|
||||
"10000", "The other properties 'largeArray' have the correct value.");
|
||||
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .name")[8].getAttribute("value"),
|
||||
"__proto__", "The other properties 'largeArray' are named correctly.");
|
||||
is(arrayVar.target.querySelectorAll(".variables-view-property .value")[8].getAttribute("value"),
|
||||
@@ -160,10 +158,13 @@ function verifyNextLevels() {
|
||||
let localScope = gVariables.getScopeAtIndex(0);
|
||||
let objectVar = localScope.get("largeObject");
|
||||
|
||||
let lastPage1 = objectVar.get(6000 + gEllipsis + 9999);
|
||||
let lastPage1 = objectVar.get("[7500" + gEllipsis + "9999]");
|
||||
ok(lastPage1, "The last page in the first level was retrieved successfully.");
|
||||
lastPage1.expand();
|
||||
return lastPage1.expand()
|
||||
.then(verifyNextLevels2.bind(null, lastPage1));
|
||||
}
|
||||
|
||||
function verifyNextLevels2(lastPage1) {
|
||||
let pageEnums1 = lastPage1.target.querySelector(".variables-view-element-details.enum").childNodes;
|
||||
let pageNonEnums1 = lastPage1.target.querySelector(".variables-view-element-details.nonenum").childNodes;
|
||||
is(pageEnums1.length, 0,
|
||||
@@ -172,61 +173,44 @@ function verifyNextLevels() {
|
||||
"The last page in the first level should contain all the created non-enumerable elements.");
|
||||
|
||||
is(lastPage1._nonenum.querySelectorAll(".variables-view-property .name")[0].getAttribute("value"),
|
||||
6000 + gEllipsis + 6999, "The first page in this level named correctly (1).");
|
||||
"[7500" + gEllipsis + "8124]", "The first page in this level named correctly (1).");
|
||||
is(lastPage1._nonenum.querySelectorAll(".variables-view-property .name")[1].getAttribute("value"),
|
||||
7000 + gEllipsis + 7999, "The second page in this level named correctly (1).");
|
||||
"[8125" + gEllipsis + "8749]", "The second page in this level named correctly (1).");
|
||||
is(lastPage1._nonenum.querySelectorAll(".variables-view-property .name")[2].getAttribute("value"),
|
||||
8000 + gEllipsis + 8999, "The third page in this level named correctly (1).");
|
||||
"[8750" + gEllipsis + "9374]", "The third page in this level named correctly (1).");
|
||||
is(lastPage1._nonenum.querySelectorAll(".variables-view-property .name")[3].getAttribute("value"),
|
||||
9000 + gEllipsis + 9999, "The fourth page in this level named correctly (1).");
|
||||
"[9375" + gEllipsis + "9999]", "The fourth page in this level named correctly (1).");
|
||||
|
||||
let lastPage2 = lastPage1.get(9000 + gEllipsis + 9999);
|
||||
let lastPage2 = lastPage1.get("[9375" + gEllipsis + "9999]");
|
||||
ok(lastPage2, "The last page in the second level was retrieved successfully.");
|
||||
lastPage2.expand();
|
||||
return lastPage2.expand()
|
||||
.then(verifyNextLevels3.bind(null, lastPage2));
|
||||
}
|
||||
|
||||
function verifyNextLevels3(lastPage2) {
|
||||
let pageEnums2 = lastPage2.target.querySelector(".variables-view-element-details.enum").childNodes;
|
||||
let pageNonEnums2 = lastPage2.target.querySelector(".variables-view-element-details.nonenum").childNodes;
|
||||
is(pageEnums2.length, 0,
|
||||
"The last page in the second level shouldn't contain any enumerable elements.");
|
||||
is(pageNonEnums2.length, 4,
|
||||
"The last page in the second level should contain all the created non-enumerable elements.");
|
||||
|
||||
is(lastPage2._nonenum.querySelectorAll(".variables-view-property .name")[0].getAttribute("value"),
|
||||
9000 + gEllipsis + 9199, "The first page in this level named correctly (2).");
|
||||
is(lastPage2._nonenum.querySelectorAll(".variables-view-property .name")[1].getAttribute("value"),
|
||||
9200 + gEllipsis + 9399, "The second page in this level named correctly (2).");
|
||||
is(lastPage2._nonenum.querySelectorAll(".variables-view-property .name")[2].getAttribute("value"),
|
||||
9400 + gEllipsis + 9599, "The third page in this level named correctly (2).");
|
||||
is(lastPage2._nonenum.querySelectorAll(".variables-view-property .name")[3].getAttribute("value"),
|
||||
9600 + gEllipsis + 9999, "The fourth page in this level named correctly (2).");
|
||||
|
||||
let lastPage3 = lastPage2.get(9600 + gEllipsis + 9999);
|
||||
ok(lastPage3, "The last page in the third level was retrieved successfully.");
|
||||
lastPage3.expand();
|
||||
|
||||
let pageEnums3 = lastPage3.target.querySelector(".variables-view-element-details.enum").childNodes;
|
||||
let pageNonEnums3 = lastPage3.target.querySelector(".variables-view-element-details.nonenum").childNodes;
|
||||
is(pageEnums3.length, 400,
|
||||
is(pageEnums2.length, 625,
|
||||
"The last page in the third level should contain all the created enumerable elements.");
|
||||
is(pageNonEnums3.length, 0,
|
||||
is(pageNonEnums2.length, 0,
|
||||
"The last page in the third level shouldn't contain any non-enumerable elements.");
|
||||
|
||||
is(lastPage3._enum.querySelectorAll(".variables-view-property .name")[0].getAttribute("value"),
|
||||
9600, "The properties in this level are named correctly (3).");
|
||||
is(lastPage3._enum.querySelectorAll(".variables-view-property .name")[1].getAttribute("value"),
|
||||
9601, "The properties in this level are named correctly (3).");
|
||||
is(lastPage3._enum.querySelectorAll(".variables-view-property .name")[398].getAttribute("value"),
|
||||
is(lastPage2._enum.querySelectorAll(".variables-view-property .name")[0].getAttribute("value"),
|
||||
9375, "The properties in this level are named correctly (3).");
|
||||
is(lastPage2._enum.querySelectorAll(".variables-view-property .name")[1].getAttribute("value"),
|
||||
9376, "The properties in this level are named correctly (3).");
|
||||
is(lastPage2._enum.querySelectorAll(".variables-view-property .name")[623].getAttribute("value"),
|
||||
9998, "The properties in this level are named correctly (3).");
|
||||
is(lastPage3._enum.querySelectorAll(".variables-view-property .name")[399].getAttribute("value"),
|
||||
is(lastPage2._enum.querySelectorAll(".variables-view-property .name")[624].getAttribute("value"),
|
||||
9999, "The properties in this level are named correctly (3).");
|
||||
|
||||
is(lastPage3._enum.querySelectorAll(".variables-view-property .value")[0].getAttribute("value"),
|
||||
399, "The properties in this level have the correct value (3).");
|
||||
is(lastPage3._enum.querySelectorAll(".variables-view-property .value")[1].getAttribute("value"),
|
||||
398, "The properties in this level have the correct value (3).");
|
||||
is(lastPage3._enum.querySelectorAll(".variables-view-property .value")[398].getAttribute("value"),
|
||||
is(lastPage2._enum.querySelectorAll(".variables-view-property .value")[0].getAttribute("value"),
|
||||
624, "The properties in this level have the correct value (3).");
|
||||
is(lastPage2._enum.querySelectorAll(".variables-view-property .value")[1].getAttribute("value"),
|
||||
623, "The properties in this level have the correct value (3).");
|
||||
is(lastPage2._enum.querySelectorAll(".variables-view-property .value")[623].getAttribute("value"),
|
||||
1, "The properties in this level have the correct value (3).");
|
||||
is(lastPage3._enum.querySelectorAll(".variables-view-property .value")[399].getAttribute("value"),
|
||||
is(lastPage2._enum.querySelectorAll(".variables-view-property .value")[624].getAttribute("value"),
|
||||
0, "The properties in this level have the correct value (3).");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Promise test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
function makePromises() {
|
||||
var p = new Promise(() => {});
|
||||
p.name = "p";
|
||||
var q = p.then();
|
||||
q.name = "q";
|
||||
var r = p.then(null, () => {});
|
||||
r.name = "r";
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -20,7 +20,8 @@ let { require } = devtools;
|
||||
let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
|
||||
let { BrowserToolboxProcess } = Cu.import("resource://gre/modules/devtools/ToolboxProcess.jsm", {});
|
||||
let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
|
||||
let { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
|
||||
let { DebuggerClient, ObjectClient } =
|
||||
Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
|
||||
let { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
|
||||
let EventEmitter = require("devtools/toolkit/event-emitter");
|
||||
const { promiseInvoke } = require("devtools/async-utils");
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
/* 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";
|
||||
|
||||
let { Ci, Cu } = require("chrome");
|
||||
let Services = require("Services");
|
||||
let { ActorPool } = require("devtools/server/actors/common");
|
||||
let { TabSources } = require("./utils/TabSources");
|
||||
let makeDebugger = require("./utils/make-debugger");
|
||||
let { ConsoleAPIListener } = require("devtools/toolkit/webconsole/utils");
|
||||
let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
let { dbg_assert, update } = DevToolsUtils;
|
||||
|
||||
loader.lazyRequireGetter(this, "AddonThreadActor", "devtools/server/actors/script", true);
|
||||
loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/script", true);
|
||||
loader.lazyRequireGetter(this, "mapURIToAddonID", "devtools/server/actors/utils/map-uri-to-addon-id");
|
||||
loader.lazyRequireGetter(this, "WebConsoleActor", "devtools/server/actors/webconsole", true);
|
||||
|
||||
loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
|
||||
|
||||
function BrowserAddonActor(aConnection, aAddon) {
|
||||
this.conn = aConnection;
|
||||
this._addon = aAddon;
|
||||
this._contextPool = new ActorPool(this.conn);
|
||||
this.conn.addActorPool(this._contextPool);
|
||||
this.threadActor = null;
|
||||
this._global = null;
|
||||
|
||||
this._shouldAddNewGlobalAsDebuggee = this._shouldAddNewGlobalAsDebuggee.bind(this);
|
||||
|
||||
this.makeDebugger = makeDebugger.bind(null, {
|
||||
findDebuggees: this._findDebuggees.bind(this),
|
||||
shouldAddNewGlobalAsDebuggee: this._shouldAddNewGlobalAsDebuggee
|
||||
});
|
||||
|
||||
AddonManager.addAddonListener(this);
|
||||
}
|
||||
exports.BrowserAddonActor = BrowserAddonActor;
|
||||
|
||||
BrowserAddonActor.prototype = {
|
||||
actorPrefix: "addon",
|
||||
|
||||
get exited() {
|
||||
return !this._addon;
|
||||
},
|
||||
|
||||
get id() {
|
||||
return this._addon.id;
|
||||
},
|
||||
|
||||
get url() {
|
||||
return this._addon.sourceURI ? this._addon.sourceURI.spec : undefined;
|
||||
},
|
||||
|
||||
get attached() {
|
||||
return this.threadActor;
|
||||
},
|
||||
|
||||
get global() {
|
||||
return this._global;
|
||||
},
|
||||
|
||||
get sources() {
|
||||
if (!this._sources) {
|
||||
dbg_assert(this.threadActor, "threadActor should exist when creating sources.");
|
||||
this._sources = new TabSources(this.threadActor, this._allowSource);
|
||||
}
|
||||
return this._sources;
|
||||
},
|
||||
|
||||
|
||||
form: function BAA_form() {
|
||||
dbg_assert(this.actorID, "addon should have an actorID.");
|
||||
if (!this._consoleActor) {
|
||||
this._consoleActor = new AddonConsoleActor(this._addon, this.conn, this);
|
||||
this._contextPool.addActor(this._consoleActor);
|
||||
}
|
||||
|
||||
return {
|
||||
actor: this.actorID,
|
||||
id: this.id,
|
||||
name: this._addon.name,
|
||||
url: this.url,
|
||||
debuggable: this._addon.isDebuggable,
|
||||
consoleActor: this._consoleActor.actorID,
|
||||
|
||||
traits: {
|
||||
highlightable: false,
|
||||
networkMonitor: false,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
disconnect: function BAA_disconnect() {
|
||||
this.conn.removeActorPool(this._contextPool);
|
||||
this._contextPool = null;
|
||||
this._consoleActor = null;
|
||||
this._addon = null;
|
||||
this._global = null;
|
||||
AddonManager.removeAddonListener(this);
|
||||
},
|
||||
|
||||
setOptions: function BAA_setOptions(aOptions) {
|
||||
if ("global" in aOptions) {
|
||||
this._global = aOptions.global;
|
||||
}
|
||||
},
|
||||
|
||||
onDisabled: function BAA_onDisabled(aAddon) {
|
||||
if (aAddon != this._addon) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._global = null;
|
||||
},
|
||||
|
||||
onUninstalled: function BAA_onUninstalled(aAddon) {
|
||||
if (aAddon != this._addon) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.attached) {
|
||||
this.onDetach();
|
||||
this.conn.send({ from: this.actorID, type: "tabDetached" });
|
||||
}
|
||||
|
||||
this.disconnect();
|
||||
},
|
||||
|
||||
onAttach: function BAA_onAttach() {
|
||||
if (this.exited) {
|
||||
return { type: "exited" };
|
||||
}
|
||||
|
||||
if (!this.attached) {
|
||||
this.threadActor = new AddonThreadActor(this.conn, this);
|
||||
this._contextPool.addActor(this.threadActor);
|
||||
}
|
||||
|
||||
return { type: "tabAttached", threadActor: this.threadActor.actorID };
|
||||
},
|
||||
|
||||
onDetach: function BAA_onDetach() {
|
||||
if (!this.attached) {
|
||||
return { error: "wrongState" };
|
||||
}
|
||||
|
||||
this._contextPool.removeActor(this.threadActor);
|
||||
|
||||
this.threadActor = null;
|
||||
this._sources = null;
|
||||
|
||||
return { type: "detached" };
|
||||
},
|
||||
|
||||
preNest: function() {
|
||||
let e = Services.wm.getEnumerator(null);
|
||||
while (e.hasMoreElements()) {
|
||||
let win = e.getNext();
|
||||
let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
windowUtils.suppressEventHandling(true);
|
||||
windowUtils.suspendTimeouts();
|
||||
}
|
||||
},
|
||||
|
||||
postNest: function() {
|
||||
let e = Services.wm.getEnumerator(null);
|
||||
while (e.hasMoreElements()) {
|
||||
let win = e.getNext();
|
||||
let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
windowUtils.resumeTimeouts();
|
||||
windowUtils.suppressEventHandling(false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Return true if the given global is associated with this addon and should be
|
||||
* added as a debuggee, false otherwise.
|
||||
*/
|
||||
_shouldAddNewGlobalAsDebuggee: function (aGlobal) {
|
||||
const global = unwrapDebuggerObjectGlobal(aGlobal);
|
||||
try {
|
||||
// This will fail for non-Sandbox objects, hence the try-catch block.
|
||||
let metadata = Cu.getSandboxMetadata(global);
|
||||
if (metadata) {
|
||||
return metadata.addonID === this.id;
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
if (global instanceof Ci.nsIDOMWindow) {
|
||||
let id = {};
|
||||
if (mapURIToAddonID(global.document.documentURIObject, id)) {
|
||||
return id.value === this.id;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the global for a __URI__ property and then try to map that to an
|
||||
// add-on
|
||||
let uridescriptor = aGlobal.getOwnPropertyDescriptor("__URI__");
|
||||
if (uridescriptor && "value" in uridescriptor && uridescriptor.value) {
|
||||
let uri;
|
||||
try {
|
||||
uri = Services.io.newURI(uridescriptor.value, null, null);
|
||||
}
|
||||
catch (e) {
|
||||
DevToolsUtils.reportException(
|
||||
"BrowserAddonActor.prototype._shouldAddNewGlobalAsDebuggee",
|
||||
new Error("Invalid URI: " + uridescriptor.value)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
let id = {};
|
||||
if (mapURIToAddonID(uri, id)) {
|
||||
return id.value === this.id;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Override the eligibility check for scripts and sources to make
|
||||
* sure every script and source with a URL is stored when debugging
|
||||
* add-ons.
|
||||
*/
|
||||
_allowSource: function(aSource) {
|
||||
// XPIProvider.jsm evals some code in every add-on's bootstrap.js. Hide it.
|
||||
if (aSource.url === "resource://gre/modules/addons/XPIProvider.jsm") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Yield the current set of globals associated with this addon that should be
|
||||
* added as debuggees.
|
||||
*/
|
||||
_findDebuggees: function (dbg) {
|
||||
return dbg.findAllGlobals().filter(this._shouldAddNewGlobalAsDebuggee);
|
||||
}
|
||||
};
|
||||
|
||||
BrowserAddonActor.prototype.requestTypes = {
|
||||
"attach": BrowserAddonActor.prototype.onAttach,
|
||||
"detach": BrowserAddonActor.prototype.onDetach
|
||||
};
|
||||
|
||||
/**
|
||||
* The AddonConsoleActor implements capabilities needed for the add-on web
|
||||
* console feature.
|
||||
*
|
||||
* @constructor
|
||||
* @param object aAddon
|
||||
* The add-on that this console watches.
|
||||
* @param object aConnection
|
||||
* The connection to the client, DebuggerServerConnection.
|
||||
* @param object aParentActor
|
||||
* The parent BrowserAddonActor actor.
|
||||
*/
|
||||
function AddonConsoleActor(aAddon, aConnection, aParentActor)
|
||||
{
|
||||
this.addon = aAddon;
|
||||
WebConsoleActor.call(this, aConnection, aParentActor);
|
||||
}
|
||||
|
||||
AddonConsoleActor.prototype = Object.create(WebConsoleActor.prototype);
|
||||
|
||||
update(AddonConsoleActor.prototype, {
|
||||
constructor: AddonConsoleActor,
|
||||
|
||||
actorPrefix: "addonConsole",
|
||||
|
||||
/**
|
||||
* The add-on that this console watches.
|
||||
*/
|
||||
addon: null,
|
||||
|
||||
/**
|
||||
* The main add-on JS global
|
||||
*/
|
||||
get window() {
|
||||
return this.parentActor.global;
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy the current AddonConsoleActor instance.
|
||||
*/
|
||||
disconnect: function ACA_disconnect()
|
||||
{
|
||||
WebConsoleActor.prototype.disconnect.call(this);
|
||||
this.addon = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for the "startListeners" request.
|
||||
*
|
||||
* @param object aRequest
|
||||
* The JSON request object received from the Web Console client.
|
||||
* @return object
|
||||
* The response object which holds the startedListeners array.
|
||||
*/
|
||||
onStartListeners: function ACA_onStartListeners(aRequest)
|
||||
{
|
||||
let startedListeners = [];
|
||||
|
||||
while (aRequest.listeners.length > 0) {
|
||||
let listener = aRequest.listeners.shift();
|
||||
switch (listener) {
|
||||
case "ConsoleAPI":
|
||||
if (!this.consoleAPIListener) {
|
||||
this.consoleAPIListener =
|
||||
new ConsoleAPIListener(null, this, "addon/" + this.addon.id);
|
||||
this.consoleAPIListener.init();
|
||||
}
|
||||
startedListeners.push(listener);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {
|
||||
startedListeners: startedListeners,
|
||||
nativeConsoleAPI: true,
|
||||
traits: this.traits,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
AddonConsoleActor.prototype.requestTypes = Object.create(WebConsoleActor.prototype.requestTypes);
|
||||
AddonConsoleActor.prototype.requestTypes.startListeners = AddonConsoleActor.prototype.onStartListeners;
|
||||
@@ -11,12 +11,14 @@ const { WebConsoleActor } = require("devtools/server/actors/webconsole");
|
||||
const makeDebugger = require("devtools/server/actors/utils/make-debugger");
|
||||
const { ActorPool } = require("devtools/server/main");
|
||||
const Services = require("Services");
|
||||
const { dbg_assert } = require("devtools/toolkit/DevToolsUtils");
|
||||
const { TabSources } = require("./utils/TabSources");
|
||||
|
||||
function ChildProcessActor(aConnection) {
|
||||
this.conn = aConnection;
|
||||
this._contextPool = new ActorPool(this.conn);
|
||||
this.conn.addActorPool(this._contextPool);
|
||||
this._threadActor = null;
|
||||
this.threadActor = null;
|
||||
|
||||
// Use a see-everything debugger
|
||||
this.makeDebugger = makeDebugger.bind(null, {
|
||||
@@ -52,15 +54,23 @@ ChildProcessActor.prototype = {
|
||||
return this._consoleScope;
|
||||
},
|
||||
|
||||
get sources() {
|
||||
if (!this._sources) {
|
||||
dbg_assert(this.threadActor, "threadActor should exist when creating sources.");
|
||||
this._sources = new TabSources(this.threadActor);
|
||||
}
|
||||
return this._sources;
|
||||
},
|
||||
|
||||
form: function() {
|
||||
if (!this._consoleActor) {
|
||||
this._consoleActor = new WebConsoleActor(this.conn, this);
|
||||
this._contextPool.addActor(this._consoleActor);
|
||||
}
|
||||
|
||||
if (!this._threadActor) {
|
||||
this._threadActor = new ChromeDebuggerActor(this.conn, this);
|
||||
this._contextPool.addActor(this._threadActor);
|
||||
if (!this.threadActor) {
|
||||
this.threadActor = new ChromeDebuggerActor(this.conn, this);
|
||||
this._contextPool.addActor(this.threadActor);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -68,7 +78,7 @@ ChildProcessActor.prototype = {
|
||||
name: "Content process",
|
||||
|
||||
consoleActor: this._consoleActor.actorID,
|
||||
chromeDebugger: this._threadActor.actorID,
|
||||
chromeDebugger: this.threadActor.actorID,
|
||||
|
||||
traits: {
|
||||
highlightable: false,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,10 +7,13 @@
|
||||
const protocol = require("devtools/server/protocol");
|
||||
const { method, RetVal, Arg, types } = protocol;
|
||||
const { expectState, ActorPool } = require("devtools/server/actors/common");
|
||||
const { ObjectActor, createValueGrip } = require("devtools/server/actors/object");
|
||||
const { ObjectActor,
|
||||
createValueGrip } = require("devtools/server/actors/object");
|
||||
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
loader.lazyRequireGetter(this, "events", "sdk/event/core");
|
||||
|
||||
/* global events */
|
||||
|
||||
// Teach protocol.js how to deal with legacy actor types
|
||||
types.addType("ObjectActor", {
|
||||
write: actor => actor.grip(),
|
||||
@@ -86,6 +89,14 @@ let PromisesActor = protocol.ActorClass({
|
||||
this._newPromises = [];
|
||||
this._promisesSettled = [];
|
||||
|
||||
this.dbg.findScripts().forEach(s => {
|
||||
this.parent.sources.createSourceActors(s.source);
|
||||
});
|
||||
|
||||
this.dbg.onNewScript = s => {
|
||||
this.parent.sources.createSourceActors(s.source);
|
||||
};
|
||||
|
||||
events.on(this.parent, "window-ready", this._onWindowReady);
|
||||
|
||||
this.state = "attached";
|
||||
@@ -142,10 +153,11 @@ let PromisesActor = protocol.ActorClass({
|
||||
decrementGripDepth: () => this._gripDepth--,
|
||||
createValueGrip: v =>
|
||||
createValueGrip(v, this._navigationLifetimePool, this.objectGrip),
|
||||
sources: () => DevToolsUtils.reportException("PromisesActor",
|
||||
Error("sources not yet implemented")),
|
||||
sources: () => this.parent.sources,
|
||||
createEnvironmentActor: () => DevToolsUtils.reportException(
|
||||
"PromisesActor", Error("createEnvironmentActor not yet implemented"))
|
||||
"PromisesActor", Error("createEnvironmentActor not yet implemented")),
|
||||
getGlobalDebugObject: () => DevToolsUtils.reportException(
|
||||
"PromisesActor", Error("getGlobalDebugObject not yet implemented")),
|
||||
});
|
||||
|
||||
this._navigationLifetimePool.addActor(actor);
|
||||
@@ -198,7 +210,7 @@ let PromisesActor = protocol.ActorClass({
|
||||
*/
|
||||
_makePromiseEventHandler: function(array, eventName) {
|
||||
return promise => {
|
||||
let actor = this._createObjectActorForPromise(promise)
|
||||
let actor = this._createObjectActorForPromise(promise);
|
||||
let needsScheduling = array.length == 0;
|
||||
|
||||
array.push(actor);
|
||||
@@ -208,7 +220,7 @@ let PromisesActor = protocol.ActorClass({
|
||||
events.emit(this, eventName, array.splice(0, array.length));
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
_onWindowReady: expectState("attached", function({ isTopLevel }) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@ const events = require("sdk/event/core");
|
||||
const { on: systemOn, off: systemOff } = require("sdk/system/events");
|
||||
const protocol = require("devtools/server/protocol");
|
||||
const { CallWatcherActor, CallWatcherFront } = require("devtools/server/actors/call-watcher");
|
||||
const { ThreadActor } = require("devtools/server/actors/script");
|
||||
const { createValueGrip } = require("devtools/server/actors/object");
|
||||
const AutomationTimeline = require("./utils/automation-timeline");
|
||||
const { on, once, off, emit } = events;
|
||||
const { types, method, Arg, Option, RetVal } = protocol;
|
||||
@@ -321,13 +321,8 @@ let AudioNodeActor = exports.AudioNodeActor = protocol.ActorClass({
|
||||
// AudioBuffer or Float32Array references and the like,
|
||||
// so this just formats the value to be displayed in the VariablesView,
|
||||
// without using real grips and managing via actor pools.
|
||||
let grip;
|
||||
try {
|
||||
grip = ThreadActor.prototype.createValueGrip(value);
|
||||
}
|
||||
catch (e) {
|
||||
grip = createObjectGrip(value);
|
||||
}
|
||||
let grip = createValueGrip(value, null, createObjectGrip);
|
||||
|
||||
return grip;
|
||||
}, {
|
||||
request: {
|
||||
|
||||
@@ -13,15 +13,15 @@ let { ActorPool, createExtraActors, appendExtraActors } = require("devtools/serv
|
||||
let { DebuggerServer } = require("devtools/server/main");
|
||||
let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
let { dbg_assert } = DevToolsUtils;
|
||||
let { TabSources, isHiddenSource } = require("./utils/TabSources");
|
||||
let { TabSources } = require("./utils/TabSources");
|
||||
let makeDebugger = require("./utils/make-debugger");
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
loader.lazyRequireGetter(this, "RootActor", "devtools/server/actors/root", true);
|
||||
loader.lazyRequireGetter(this, "AddonThreadActor", "devtools/server/actors/script", true);
|
||||
loader.lazyRequireGetter(this, "ThreadActor", "devtools/server/actors/script", true);
|
||||
loader.lazyRequireGetter(this, "mapURIToAddonID", "devtools/server/actors/utils/map-uri-to-addon-id");
|
||||
loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/script", true);
|
||||
loader.lazyRequireGetter(this, "BrowserAddonActor", "devtools/server/actors/addon", true);
|
||||
loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
|
||||
|
||||
// Assumptions on events module:
|
||||
@@ -111,35 +111,6 @@ function sendShutdownEvent() {
|
||||
|
||||
exports.sendShutdownEvent = sendShutdownEvent;
|
||||
|
||||
/**
|
||||
* Unwrap a global that is wrapped in a |Debugger.Object|, or if the global has
|
||||
* become a dead object, return |undefined|.
|
||||
*
|
||||
* @param Debugger.Object wrappedGlobal
|
||||
* The |Debugger.Object| which wraps a global.
|
||||
*
|
||||
* @returns {Object|undefined}
|
||||
* Returns the unwrapped global object or |undefined| if unwrapping
|
||||
* failed.
|
||||
*/
|
||||
const unwrapDebuggerObjectGlobal = wrappedGlobal => {
|
||||
try {
|
||||
// Because of bug 991399 we sometimes get nuked window references here. We
|
||||
// just bail out in that case.
|
||||
//
|
||||
// Note that addon sandboxes have a DOMWindow as their prototype. So make
|
||||
// sure that we can touch the prototype too (whatever it is), in case _it_
|
||||
// is it a nuked window reference. We force stringification to make sure
|
||||
// that any dead object proxies make themselves known.
|
||||
let global = wrappedGlobal.unsafeDereference();
|
||||
Object.getPrototypeOf(global) + "";
|
||||
return global;
|
||||
}
|
||||
catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a root actor appropriate for use in a server running in a
|
||||
* browser. The returned root actor:
|
||||
@@ -1848,238 +1819,6 @@ BrowserAddonList.prototype.onUninstalled = function (aAddon) {
|
||||
|
||||
exports.BrowserAddonList = BrowserAddonList;
|
||||
|
||||
function BrowserAddonActor(aConnection, aAddon) {
|
||||
this.conn = aConnection;
|
||||
this._addon = aAddon;
|
||||
this._contextPool = new ActorPool(this.conn);
|
||||
this.conn.addActorPool(this._contextPool);
|
||||
this._threadActor = null;
|
||||
this._global = null;
|
||||
|
||||
this._shouldAddNewGlobalAsDebuggee = this._shouldAddNewGlobalAsDebuggee.bind(this);
|
||||
|
||||
this.makeDebugger = makeDebugger.bind(null, {
|
||||
findDebuggees: this._findDebuggees.bind(this),
|
||||
shouldAddNewGlobalAsDebuggee: this._shouldAddNewGlobalAsDebuggee
|
||||
});
|
||||
|
||||
AddonManager.addAddonListener(this);
|
||||
}
|
||||
|
||||
BrowserAddonActor.prototype = {
|
||||
actorPrefix: "addon",
|
||||
|
||||
get exited() {
|
||||
return !this._addon;
|
||||
},
|
||||
|
||||
get id() {
|
||||
return this._addon.id;
|
||||
},
|
||||
|
||||
get url() {
|
||||
return this._addon.sourceURI ? this._addon.sourceURI.spec : undefined;
|
||||
},
|
||||
|
||||
get attached() {
|
||||
return this._threadActor;
|
||||
},
|
||||
|
||||
get global() {
|
||||
return this._global;
|
||||
},
|
||||
|
||||
get sources() {
|
||||
if (!this._sources) {
|
||||
dbg_assert(this.threadActor, "threadActor should exist when creating sources.");
|
||||
this._sources = new TabSources(this._threadActor, this._allowSource);
|
||||
}
|
||||
return this._sources;
|
||||
},
|
||||
|
||||
|
||||
form: function BAA_form() {
|
||||
dbg_assert(this.actorID, "addon should have an actorID.");
|
||||
if (!this._consoleActor) {
|
||||
let {AddonConsoleActor} = require("devtools/server/actors/webconsole");
|
||||
this._consoleActor = new AddonConsoleActor(this._addon, this.conn, this);
|
||||
this._contextPool.addActor(this._consoleActor);
|
||||
}
|
||||
|
||||
return {
|
||||
actor: this.actorID,
|
||||
id: this.id,
|
||||
name: this._addon.name,
|
||||
url: this.url,
|
||||
debuggable: this._addon.isDebuggable,
|
||||
consoleActor: this._consoleActor.actorID,
|
||||
|
||||
traits: {
|
||||
highlightable: false,
|
||||
networkMonitor: false,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
disconnect: function BAA_disconnect() {
|
||||
this.conn.removeActorPool(this._contextPool);
|
||||
this._contextPool = null;
|
||||
this._consoleActor = null;
|
||||
this._addon = null;
|
||||
this._global = null;
|
||||
AddonManager.removeAddonListener(this);
|
||||
},
|
||||
|
||||
setOptions: function BAA_setOptions(aOptions) {
|
||||
if ("global" in aOptions) {
|
||||
this._global = aOptions.global;
|
||||
}
|
||||
},
|
||||
|
||||
onDisabled: function BAA_onDisabled(aAddon) {
|
||||
if (aAddon != this._addon) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._global = null;
|
||||
},
|
||||
|
||||
onUninstalled: function BAA_onUninstalled(aAddon) {
|
||||
if (aAddon != this._addon) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.attached) {
|
||||
this.onDetach();
|
||||
this.conn.send({ from: this.actorID, type: "tabDetached" });
|
||||
}
|
||||
|
||||
this.disconnect();
|
||||
},
|
||||
|
||||
onAttach: function BAA_onAttach() {
|
||||
if (this.exited) {
|
||||
return { type: "exited" };
|
||||
}
|
||||
|
||||
if (!this.attached) {
|
||||
this._threadActor = new AddonThreadActor(this.conn, this);
|
||||
this._contextPool.addActor(this._threadActor);
|
||||
}
|
||||
|
||||
return { type: "tabAttached", threadActor: this._threadActor.actorID };
|
||||
},
|
||||
|
||||
onDetach: function BAA_onDetach() {
|
||||
if (!this.attached) {
|
||||
return { error: "wrongState" };
|
||||
}
|
||||
|
||||
this._contextPool.removeActor(this._threadActor);
|
||||
|
||||
this._threadActor = null;
|
||||
this._sources = null;
|
||||
|
||||
return { type: "detached" };
|
||||
},
|
||||
|
||||
preNest: function() {
|
||||
let e = Services.wm.getEnumerator(null);
|
||||
while (e.hasMoreElements()) {
|
||||
let win = e.getNext();
|
||||
let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
windowUtils.suppressEventHandling(true);
|
||||
windowUtils.suspendTimeouts();
|
||||
}
|
||||
},
|
||||
|
||||
postNest: function() {
|
||||
let e = Services.wm.getEnumerator(null);
|
||||
while (e.hasMoreElements()) {
|
||||
let win = e.getNext();
|
||||
let windowUtils = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
windowUtils.resumeTimeouts();
|
||||
windowUtils.suppressEventHandling(false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Return true if the given global is associated with this addon and should be
|
||||
* added as a debuggee, false otherwise.
|
||||
*/
|
||||
_shouldAddNewGlobalAsDebuggee: function (aGlobal) {
|
||||
const global = unwrapDebuggerObjectGlobal(aGlobal);
|
||||
try {
|
||||
// This will fail for non-Sandbox objects, hence the try-catch block.
|
||||
let metadata = Cu.getSandboxMetadata(global);
|
||||
if (metadata) {
|
||||
return metadata.addonID === this.id;
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
if (global instanceof Ci.nsIDOMWindow) {
|
||||
let id = {};
|
||||
if (mapURIToAddonID(global.document.documentURIObject, id)) {
|
||||
return id.value === this.id;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the global for a __URI__ property and then try to map that to an
|
||||
// add-on
|
||||
let uridescriptor = aGlobal.getOwnPropertyDescriptor("__URI__");
|
||||
if (uridescriptor && "value" in uridescriptor && uridescriptor.value) {
|
||||
let uri;
|
||||
try {
|
||||
uri = Services.io.newURI(uridescriptor.value, null, null);
|
||||
}
|
||||
catch (e) {
|
||||
DevToolsUtils.reportException(
|
||||
"BrowserAddonActor.prototype._shouldAddNewGlobalAsDebuggee",
|
||||
new Error("Invalid URI: " + uridescriptor.value)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
let id = {};
|
||||
if (mapURIToAddonID(uri, id)) {
|
||||
return id.value === this.id;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Override the eligibility check for scripts and sources to make
|
||||
* sure every script and source with a URL is stored when debugging
|
||||
* add-ons.
|
||||
*/
|
||||
_allowSource: function(aSource) {
|
||||
// XPIProvider.jsm evals some code in every add-on's bootstrap.js. Hide it.
|
||||
if (aSource.url === "resource://gre/modules/addons/XPIProvider.jsm") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Yield the current set of globals associated with this addon that should be
|
||||
* added as debuggees.
|
||||
*/
|
||||
_findDebuggees: function (dbg) {
|
||||
return dbg.findAllGlobals().filter(this._shouldAddNewGlobalAsDebuggee);
|
||||
}
|
||||
};
|
||||
|
||||
BrowserAddonActor.prototype.requestTypes = {
|
||||
"attach": BrowserAddonActor.prototype.onAttach,
|
||||
"detach": BrowserAddonActor.prototype.onDetach
|
||||
};
|
||||
|
||||
/**
|
||||
* The DebuggerProgressListener object is an nsIWebProgressListener which
|
||||
* handles onStateChange events for the inspected browser. If the user tries to
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
|
||||
const { Cc, Ci, Cu } = require("chrome");
|
||||
const { DebuggerServer, ActorPool } = require("devtools/server/main");
|
||||
const { EnvironmentActor, LongStringActor, ObjectActor, ThreadActor } = require("devtools/server/actors/script");
|
||||
const { update } = require("devtools/toolkit/DevToolsUtils");
|
||||
const { EnvironmentActor, ThreadActor } = require("devtools/server/actors/script");
|
||||
const { ObjectActor, LongStringActor, createValueGrip, stringIsLong } = require("devtools/server/actors/object");
|
||||
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
@@ -71,7 +72,9 @@ function WebConsoleActor(aConnection, aParentActor)
|
||||
this._netEvents = new Map();
|
||||
this._gripDepth = 0;
|
||||
this._listeners = new Set();
|
||||
this._lastConsoleInputEvaluation = undefined;
|
||||
|
||||
this.objectGrip = this.objectGrip.bind(this);
|
||||
this._onWillNavigate = this._onWillNavigate.bind(this);
|
||||
this._onChangedToplevelDocument = this._onChangedToplevelDocument.bind(this);
|
||||
events.on(this.parentActor, "changed-toplevel-document", this._onChangedToplevelDocument);
|
||||
@@ -302,6 +305,10 @@ WebConsoleActor.prototype =
|
||||
|
||||
actorPrefix: "console",
|
||||
|
||||
get globalDebugObject() {
|
||||
return this.parentActor.threadActor.globalDebugObject;
|
||||
},
|
||||
|
||||
grip: function WCA_grip()
|
||||
{
|
||||
return { actor: this.actorID };
|
||||
@@ -319,8 +326,6 @@ WebConsoleActor.prototype =
|
||||
return isNative;
|
||||
},
|
||||
|
||||
_createValueGrip: ThreadActor.prototype.createValueGrip,
|
||||
_stringIsLong: ThreadActor.prototype._stringIsLong,
|
||||
_findProtoChain: ThreadActor.prototype._findProtoChain,
|
||||
_removeFromProtoChain: ThreadActor.prototype._removeFromProtoChain,
|
||||
|
||||
@@ -358,6 +363,7 @@ WebConsoleActor.prototype =
|
||||
this._actorPool = null;
|
||||
|
||||
this._jstermHelpersCache = null;
|
||||
this._lastConsoleInputEvaluation = null;
|
||||
this._evalWindow = null;
|
||||
this._netEvents.clear();
|
||||
this.dbg.enabled = false;
|
||||
@@ -400,7 +406,7 @@ WebConsoleActor.prototype =
|
||||
*/
|
||||
createValueGrip: function WCA_createValueGrip(aValue)
|
||||
{
|
||||
return this._createValueGrip(aValue, this._actorPool);
|
||||
return createValueGrip(aValue, this._actorPool, this.objectGrip);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -443,7 +449,16 @@ WebConsoleActor.prototype =
|
||||
*/
|
||||
objectGrip: function WCA_objectGrip(aObject, aPool)
|
||||
{
|
||||
let actor = new ObjectActor(aObject, this);
|
||||
let actor = new ObjectActor(aObject, {
|
||||
getGripDepth: () => this._gripDepth,
|
||||
incrementGripDepth: () => this._gripDepth++,
|
||||
decrementGripDepth: () => this._gripDepth--,
|
||||
createValueGrip: v => this.createValueGrip(v),
|
||||
sources: () => DevToolsUtils.reportException("WebConsoleActor",
|
||||
Error("sources not yet implemented")),
|
||||
createEnvironmentActor: (env) => this.createEnvironmentActor(env),
|
||||
getGlobalDebugObject: () => this.globalDebugObject
|
||||
});
|
||||
aPool.addActor(actor);
|
||||
return actor.grip();
|
||||
},
|
||||
@@ -460,7 +475,7 @@ WebConsoleActor.prototype =
|
||||
*/
|
||||
longStringGrip: function WCA_longStringGrip(aString, aPool)
|
||||
{
|
||||
let actor = new LongStringActor(aString, this);
|
||||
let actor = new LongStringActor(aString);
|
||||
aPool.addActor(actor);
|
||||
return actor.grip();
|
||||
},
|
||||
@@ -477,7 +492,7 @@ WebConsoleActor.prototype =
|
||||
*/
|
||||
_createStringGrip: function NEA__createStringGrip(aString)
|
||||
{
|
||||
if (aString && this._stringIsLong(aString)) {
|
||||
if (aString && stringIsLong(aString)) {
|
||||
return this.longStringGrip(aString, this._actorPool);
|
||||
}
|
||||
return aString;
|
||||
@@ -505,6 +520,17 @@ WebConsoleActor.prototype =
|
||||
this._actorPool.removeActor(aActor.actorID);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the latest web console input evaluation.
|
||||
* This is undefined if no evaluations have been completed.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
getLastConsoleInputEvaluation: function WCU_getLastConsoleInputEvaluation()
|
||||
{
|
||||
return this._lastConsoleInputEvaluation;
|
||||
},
|
||||
|
||||
//////////////////
|
||||
// Request handlers for known packet types.
|
||||
//////////////////
|
||||
@@ -818,6 +844,8 @@ WebConsoleActor.prototype =
|
||||
errorMessage = e;
|
||||
}
|
||||
|
||||
this._lastConsoleInputEvaluation = result;
|
||||
|
||||
return {
|
||||
from: this.actorID,
|
||||
input: input,
|
||||
@@ -1527,91 +1555,6 @@ WebConsoleActor.prototype.requestTypes =
|
||||
|
||||
exports.WebConsoleActor = WebConsoleActor;
|
||||
|
||||
|
||||
/**
|
||||
* The AddonConsoleActor implements capabilities needed for the add-on web
|
||||
* console feature.
|
||||
*
|
||||
* @constructor
|
||||
* @param object aAddon
|
||||
* The add-on that this console watches.
|
||||
* @param object aConnection
|
||||
* The connection to the client, DebuggerServerConnection.
|
||||
* @param object aParentActor
|
||||
* The parent BrowserAddonActor actor.
|
||||
*/
|
||||
function AddonConsoleActor(aAddon, aConnection, aParentActor)
|
||||
{
|
||||
this.addon = aAddon;
|
||||
WebConsoleActor.call(this, aConnection, aParentActor);
|
||||
}
|
||||
|
||||
AddonConsoleActor.prototype = Object.create(WebConsoleActor.prototype);
|
||||
|
||||
update(AddonConsoleActor.prototype, {
|
||||
constructor: AddonConsoleActor,
|
||||
|
||||
actorPrefix: "addonConsole",
|
||||
|
||||
/**
|
||||
* The add-on that this console watches.
|
||||
*/
|
||||
addon: null,
|
||||
|
||||
/**
|
||||
* The main add-on JS global
|
||||
*/
|
||||
get window() {
|
||||
return this.parentActor.global;
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy the current AddonConsoleActor instance.
|
||||
*/
|
||||
disconnect: function ACA_disconnect()
|
||||
{
|
||||
WebConsoleActor.prototype.disconnect.call(this);
|
||||
this.addon = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for the "startListeners" request.
|
||||
*
|
||||
* @param object aRequest
|
||||
* The JSON request object received from the Web Console client.
|
||||
* @return object
|
||||
* The response object which holds the startedListeners array.
|
||||
*/
|
||||
onStartListeners: function ACA_onStartListeners(aRequest)
|
||||
{
|
||||
let startedListeners = [];
|
||||
|
||||
while (aRequest.listeners.length > 0) {
|
||||
let listener = aRequest.listeners.shift();
|
||||
switch (listener) {
|
||||
case "ConsoleAPI":
|
||||
if (!this.consoleAPIListener) {
|
||||
this.consoleAPIListener =
|
||||
new ConsoleAPIListener(null, this, "addon/" + this.addon.id);
|
||||
this.consoleAPIListener.init();
|
||||
}
|
||||
startedListeners.push(listener);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {
|
||||
startedListeners: startedListeners,
|
||||
nativeConsoleAPI: true,
|
||||
traits: this.traits,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
AddonConsoleActor.prototype.requestTypes = Object.create(WebConsoleActor.prototype.requestTypes);
|
||||
AddonConsoleActor.prototype.requestTypes.startListeners = AddonConsoleActor.prototype.onStartListeners;
|
||||
|
||||
exports.AddonConsoleActor = AddonConsoleActor;
|
||||
|
||||
/**
|
||||
* Creates an actor for a network event.
|
||||
*
|
||||
|
||||
@@ -53,6 +53,7 @@ EXTRA_JS_MODULES.devtools.server += [
|
||||
|
||||
EXTRA_JS_MODULES.devtools.server.actors += [
|
||||
'actors/actor-registry.js',
|
||||
'actors/addon.js',
|
||||
'actors/animation.js',
|
||||
'actors/call-watcher.js',
|
||||
'actors/canvas.js',
|
||||
@@ -72,6 +73,7 @@ EXTRA_JS_MODULES.devtools.server.actors += [
|
||||
'actors/layout.js',
|
||||
'actors/memory.js',
|
||||
'actors/monitor.js',
|
||||
'actors/object.js',
|
||||
'actors/preference.js',
|
||||
'actors/pretty-print-worker.js',
|
||||
'actors/profiler.js',
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const { LongStringActor } = devtools.require("devtools/server/actors/script");
|
||||
const { LongStringActor } = devtools.require("devtools/server/actors/object");
|
||||
|
||||
function run_test()
|
||||
{
|
||||
@@ -23,7 +23,7 @@ function makeMockLongStringActor()
|
||||
actor.actorID = "longString1";
|
||||
actor.registeredPool = {
|
||||
longStringActors: {
|
||||
longString1: actor
|
||||
[string]: actor
|
||||
}
|
||||
};
|
||||
return actor;
|
||||
@@ -32,10 +32,10 @@ function makeMockLongStringActor()
|
||||
function test_LSA_disconnect()
|
||||
{
|
||||
let actor = makeMockLongStringActor();
|
||||
do_check_eq(actor.registeredPool.longStringActors[actor.actorID], actor);
|
||||
do_check_eq(actor.registeredPool.longStringActors[TEST_STRING], actor);
|
||||
|
||||
actor.disconnect();
|
||||
do_check_eq(actor.registeredPool.longStringActors[actor.actorID], void 0);
|
||||
do_check_eq(actor.registeredPool.longStringActors[TEST_STRING], void 0);
|
||||
}
|
||||
|
||||
function test_LSA_substring()
|
||||
|
||||
@@ -43,6 +43,10 @@ function* testListPromises(client, form, makePromise) {
|
||||
for (let p of promises) {
|
||||
equal(p.type, "object", "Expect type to be Object");
|
||||
equal(p.class, "Promise", "Expect class to be Promise");
|
||||
equal(typeof p.promiseState.creationTimestamp, "number",
|
||||
"Expect creation timestamp to be a number");
|
||||
equal(typeof p.promiseState.timeToSettle, "number",
|
||||
"Expect time to settle to be a number");
|
||||
|
||||
if (p.promiseState.state === "fulfilled" &&
|
||||
p.promiseState.value === resolution) {
|
||||
|
||||
@@ -46,6 +46,8 @@ function* testNewPromisesEvent(client, form, makePromise) {
|
||||
for (let p of promises) {
|
||||
equal(p.type, "object", "Expect type to be Object");
|
||||
equal(p.class, "Promise", "Expect class to be Promise");
|
||||
equal(typeof p.promiseState.creationTimestamp, "number",
|
||||
"Expect creation timestamp to be a number");
|
||||
|
||||
if (p.promiseState.state === "fulfilled" &&
|
||||
p.promiseState.value === resolution) {
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that we can get the list of Promise objects that have settled from the
|
||||
* PromisesActor onPromiseSettled event handler.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const { PromisesFront } = devtools.require("devtools/server/actors/promises");
|
||||
|
||||
let events = devtools.require("sdk/event/core");
|
||||
|
||||
add_task(function*() {
|
||||
let client = yield startTestDebuggerServer("promises-actor-test");
|
||||
let chromeActors = yield getChromeActors(client);
|
||||
|
||||
ok(Promise.toString().contains("native code"), "Expect native DOM Promise");
|
||||
|
||||
yield testPromisesSettled(client, chromeActors,
|
||||
v => new Promise(resolve => resolve(v)),
|
||||
v => new Promise((resolve, reject) => reject(v)));
|
||||
|
||||
let response = yield listTabs(client);
|
||||
let targetTab = findTab(response.tabs, "promises-actor-test");
|
||||
ok(targetTab, "Found our target tab.");
|
||||
|
||||
yield testPromisesSettled(client, targetTab, v => {
|
||||
const debuggee = DebuggerServer.getTestGlobal("promises-actor-test");
|
||||
return debuggee.Promise.resolve(v);
|
||||
}, v => {
|
||||
const debuggee = DebuggerServer.getTestGlobal("promises-actor-test");
|
||||
return debuggee.Promise.reject(v);
|
||||
});
|
||||
|
||||
yield close(client);
|
||||
});
|
||||
|
||||
function* testPromisesSettled(client, form, makeResolvePromise,
|
||||
makeRejectPromise) {
|
||||
let front = PromisesFront(client, form);
|
||||
let resolution = "MyLittleSecret" + Math.random();
|
||||
|
||||
yield front.attach();
|
||||
yield front.listPromises();
|
||||
|
||||
let onPromiseSettled = oncePromiseSettled(front, resolution, true, false);
|
||||
let resolvedPromise = makeResolvePromise(resolution);
|
||||
let foundResolvedPromise = yield onPromiseSettled;
|
||||
ok(foundResolvedPromise, "Found our resolved promise");
|
||||
|
||||
onPromiseSettled = oncePromiseSettled(front, resolution, false, true);
|
||||
let rejectedPromise = makeRejectPromise(resolution);
|
||||
let foundRejectedPromise = yield onPromiseSettled;
|
||||
ok(foundRejectedPromise, "Found our rejected promise");
|
||||
|
||||
yield front.detach();
|
||||
// Appease eslint
|
||||
void resolvedPromise;
|
||||
void rejectedPromise;
|
||||
}
|
||||
|
||||
function oncePromiseSettled(front, resolution, resolveValue, rejectValue) {
|
||||
return new Promise(resolve => {
|
||||
events.on(front, "promises-settled", promises => {
|
||||
for (let p of promises) {
|
||||
equal(p.type, "object", "Expect type to be Object");
|
||||
equal(p.class, "Promise", "Expect class to be Promise");
|
||||
|
||||
if (p.promiseState.state === "fulfilled" &&
|
||||
p.promiseState.value === resolution) {
|
||||
resolve(resolveValue);
|
||||
} else if (p.promiseState.state === "rejected" &&
|
||||
p.promiseState.reason === resolution) {
|
||||
resolve(rejectValue);
|
||||
} else {
|
||||
dump("Found non-target promise\n");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that we can get the list of dependent promises from the ObjectClient.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const { PromisesFront } = devtools.require("devtools/server/actors/promises");
|
||||
|
||||
let events = devtools.require("sdk/event/core");
|
||||
|
||||
add_task(function*() {
|
||||
let client = yield startTestDebuggerServer("test-promises-dependentpromises");
|
||||
let chromeActors = yield getChromeActors(client);
|
||||
|
||||
ok(Promise.toString().contains("native code"), "Expect native DOM Promise.");
|
||||
|
||||
yield testGetDependentPromises(client, chromeActors, () => {
|
||||
let p = new Promise(() => {});
|
||||
p.name = "p";
|
||||
let q = p.then();
|
||||
q.name = "q";
|
||||
let r = p.then(null, () => {});
|
||||
r.name = "r";
|
||||
|
||||
return p;
|
||||
});
|
||||
|
||||
let response = yield listTabs(client);
|
||||
let targetTab = findTab(response.tabs, "test-promises-dependentpromises");
|
||||
ok(targetTab, "Found our target tab.");
|
||||
|
||||
yield testGetDependentPromises(client, targetTab, () => {
|
||||
const debuggee =
|
||||
DebuggerServer.getTestGlobal("test-promises-dependentpromises");
|
||||
|
||||
let p = new debuggee.Promise(() => {});
|
||||
p.name = "p";
|
||||
let q = p.then();
|
||||
q.name = "q";
|
||||
let r = p.then(null, () => {});
|
||||
r.name = "r";
|
||||
|
||||
return p;
|
||||
});
|
||||
|
||||
yield close(client);
|
||||
});
|
||||
|
||||
function* testGetDependentPromises(client, form, makePromises) {
|
||||
let front = PromisesFront(client, form);
|
||||
|
||||
yield front.attach();
|
||||
yield front.listPromises();
|
||||
|
||||
// Get the grip for promise p
|
||||
let onNewPromise = new Promise(resolve => {
|
||||
events.on(front, "new-promises", promises => {
|
||||
for (let p of promises) {
|
||||
if (p.preview.ownProperties.name &&
|
||||
p.preview.ownProperties.name.value === "p") {
|
||||
resolve(p);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let promise = makePromises();
|
||||
|
||||
let grip = yield onNewPromise;
|
||||
ok(grip, "Found our promise p.");
|
||||
|
||||
let objectClient = new ObjectClient(client, grip);
|
||||
ok(objectClient, "Got Object Client.");
|
||||
|
||||
// Get the dependent promises for promise p and assert that the list of
|
||||
// dependent promises is correct
|
||||
yield new Promise(resolve => {
|
||||
objectClient.getDependentPromises(response => {
|
||||
let dependentNames = response.promises.map(p =>
|
||||
p.preview.ownProperties.name.value);
|
||||
let expectedDependentNames = ["q", "r"];
|
||||
|
||||
equal(dependentNames.length, expectedDependentNames.length,
|
||||
"Got expected number of dependent promises.");
|
||||
|
||||
for (let i = 0; i < dependentNames.length; i++) {
|
||||
equal(dependentNames[i], expectedDependentNames[i],
|
||||
"Got expected dependent name.");
|
||||
}
|
||||
|
||||
for (let p of response.promises) {
|
||||
equal(p.type, "object", "Expect type to be Object.");
|
||||
equal(p.class, "Promise", "Expect class to be Promise.");
|
||||
equal(typeof p.promiseState.creationTimestamp, "number",
|
||||
"Expect creation timestamp to be a number.");
|
||||
ok(!p.promiseState.timeToSettle,
|
||||
"Expect time to settle to be undefined.");
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
yield front.detach();
|
||||
// Appease eslint
|
||||
void promise;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that we get the approximate time range for promise creation timestamp.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const { PromisesFront } = devtools.require("devtools/server/actors/promises");
|
||||
|
||||
let events = devtools.require("sdk/event/core");
|
||||
|
||||
add_task(function*() {
|
||||
let client = yield startTestDebuggerServer("promises-object-test");
|
||||
let chromeActors = yield getChromeActors(client);
|
||||
|
||||
ok(Promise.toString().contains("native code"), "Expect native DOM Promise.");
|
||||
|
||||
yield testPromiseCreationTimestamp(client, chromeActors, v => {
|
||||
return new Promise(resolve => resolve(v));
|
||||
});
|
||||
|
||||
let response = yield listTabs(client);
|
||||
let targetTab = findTab(response.tabs, "promises-object-test");
|
||||
ok(targetTab, "Found our target tab.");
|
||||
|
||||
yield testPromiseCreationTimestamp(client, targetTab, v => {
|
||||
const debuggee = DebuggerServer.getTestGlobal("promises-object-test");
|
||||
return debuggee.Promise.resolve(v);
|
||||
});
|
||||
|
||||
yield close(client);
|
||||
});
|
||||
|
||||
function* testPromiseCreationTimestamp(client, form, makePromise) {
|
||||
let front = PromisesFront(client, form);
|
||||
let resolution = "MyLittleSecret" + Math.random();
|
||||
|
||||
yield front.attach();
|
||||
yield front.listPromises();
|
||||
|
||||
let onNewPromise = new Promise(resolve => {
|
||||
events.on(front, "new-promises", promises => {
|
||||
for (let p of promises) {
|
||||
if (p.promiseState.state === "fulfilled" &&
|
||||
p.promiseState.value === resolution) {
|
||||
resolve(p);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let start = Date.now();
|
||||
let promise = makePromise(resolution);
|
||||
let end = Date.now();
|
||||
|
||||
let grip = yield onNewPromise;
|
||||
ok(grip, "Found our new promise.");
|
||||
|
||||
let creationTimestamp = grip.promiseState.creationTimestamp;
|
||||
|
||||
ok(start - 1 <= creationTimestamp && creationTimestamp <= end + 1,
|
||||
"Expect promise creation timestamp to be within elapsed time range.");
|
||||
|
||||
yield front.detach();
|
||||
// Appease eslint
|
||||
void promise;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test whether or not we get the time to settle depending on the state of the
|
||||
* promise.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const { PromisesFront } = devtools.require("devtools/server/actors/promises");
|
||||
|
||||
let events = devtools.require("sdk/event/core");
|
||||
|
||||
add_task(function*() {
|
||||
let client = yield startTestDebuggerServer("test-promises-timetosettle");
|
||||
let chromeActors = yield getChromeActors(client);
|
||||
|
||||
ok(Promise.toString().contains("native code"), "Expect native DOM Promise.");
|
||||
|
||||
yield testGetTimeToSettle(client, chromeActors, () => {
|
||||
let p = new Promise(() => {});
|
||||
p.name = "p";
|
||||
let q = p.then();
|
||||
q.name = "q";
|
||||
|
||||
return p;
|
||||
});
|
||||
|
||||
let response = yield listTabs(client);
|
||||
let targetTab = findTab(response.tabs, "test-promises-timetosettle");
|
||||
ok(targetTab, "Found our target tab.");
|
||||
|
||||
yield testGetTimeToSettle(client, targetTab, () => {
|
||||
const debuggee =
|
||||
DebuggerServer.getTestGlobal("test-promises-timetosettle");
|
||||
|
||||
let p = new debuggee.Promise(() => {});
|
||||
p.name = "p";
|
||||
let q = p.then();
|
||||
q.name = "q";
|
||||
|
||||
return p;
|
||||
});
|
||||
|
||||
yield close(client);
|
||||
});
|
||||
|
||||
function* testGetTimeToSettle(client, form, makePromises) {
|
||||
let front = PromisesFront(client, form);
|
||||
|
||||
yield front.attach();
|
||||
yield front.listPromises();
|
||||
|
||||
let onNewPromise = new Promise(resolve => {
|
||||
events.on(front, "new-promises", promises => {
|
||||
for (let p of promises) {
|
||||
if (p.promiseState.state === "pending") {
|
||||
ok(!p.promiseState.timeToSettle,
|
||||
"Expect no time to settle for unsettled promise.");
|
||||
} else {
|
||||
ok(p.promiseState.timeToSettle,
|
||||
"Expect time to settle for settled promise.");
|
||||
equal(typeof p.promiseState.timeToSettle, "number",
|
||||
"Expect time to settle to be a number.");
|
||||
}
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
let promise = makePromises();
|
||||
|
||||
yield onNewPromise;
|
||||
yield front.detach();
|
||||
// Appease eslint
|
||||
void promise;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that we get the expected settlement time for promise time to settle.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const { PromisesFront } = devtools.require("devtools/server/actors/promises");
|
||||
const { setTimeout } = devtools.require("sdk/timers");
|
||||
|
||||
let events = devtools.require("sdk/event/core");
|
||||
|
||||
add_task(function*() {
|
||||
let client = yield startTestDebuggerServer("test-promises-timetosettle");
|
||||
let chromeActors = yield getChromeActors(client);
|
||||
|
||||
ok(Promise.toString().contains("native code"), "Expect native DOM Promise.");
|
||||
|
||||
yield testGetTimeToSettle(client, chromeActors,
|
||||
v => new Promise(resolve => setTimeout(() => resolve(v), 100)));
|
||||
|
||||
let response = yield listTabs(client);
|
||||
let targetTab = findTab(response.tabs, "test-promises-timetosettle");
|
||||
ok(targetTab, "Found our target tab.");
|
||||
|
||||
yield testGetTimeToSettle(client, targetTab, v => {
|
||||
const debuggee =
|
||||
DebuggerServer.getTestGlobal("test-promises-timetosettle");
|
||||
return new debuggee.Promise(resolve => setTimeout(() => resolve(v), 100));
|
||||
});
|
||||
|
||||
yield close(client);
|
||||
});
|
||||
|
||||
function* testGetTimeToSettle(client, form, makePromise) {
|
||||
let front = PromisesFront(client, form);
|
||||
let resolution = "MyLittleSecret" + Math.random();
|
||||
let found = false;
|
||||
|
||||
yield front.attach();
|
||||
yield front.listPromises();
|
||||
|
||||
let onNewPromise = new Promise(resolve => {
|
||||
events.on(front, "promises-settled", promises => {
|
||||
for (let p of promises) {
|
||||
if (p.promiseState.state === "fulfilled" &&
|
||||
p.promiseState.value === resolution) {
|
||||
equal(Math.floor(p.promiseState.timeToSettle / 100) * 100, 100,
|
||||
"Expect time to settle for resolved promise to be " +
|
||||
"approximately 100ms.");
|
||||
found = true;
|
||||
resolve();
|
||||
} else {
|
||||
dump("Found non-target promise.\n");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let promise = makePromise(resolution);
|
||||
|
||||
yield onNewPromise;
|
||||
ok(found, "Found our new promise.");
|
||||
yield front.detach();
|
||||
// Appease eslint
|
||||
void promise;
|
||||
}
|
||||
@@ -95,7 +95,7 @@ TestTabActor.prototype = {
|
||||
actorPrefix: "TestTabActor",
|
||||
|
||||
get window() {
|
||||
return { wrappedJSObject: this._global };
|
||||
return this._global;
|
||||
},
|
||||
|
||||
get url() {
|
||||
|
||||
@@ -83,7 +83,10 @@ support-files =
|
||||
[test_promises_actor_exist.js]
|
||||
[test_promises_actor_list_promises.js]
|
||||
[test_promises_actor_onnewpromise.js]
|
||||
[test_promises_actor_onpromisesettled.js]
|
||||
[test_promises_client_getdependentpromises.js]
|
||||
[test_promises_object_creationtimestamp.js]
|
||||
[test_promises_object_timetosettle-01.js]
|
||||
[test_promises_object_timetosettle-02.js]
|
||||
[test_protocol_abort.js]
|
||||
[test_protocol_async.js]
|
||||
[test_protocol_children.js]
|
||||
|
||||
@@ -12,7 +12,6 @@ const DBG_STRINGS_URI = "chrome://global/locale/devtools/debugger.properties";
|
||||
const LAZY_EMPTY_DELAY = 150; // ms
|
||||
const LAZY_EXPAND_DELAY = 50; // ms
|
||||
const SCROLL_PAGE_SIZE_DEFAULT = 0;
|
||||
const APPEND_PAGE_SIZE_DEFAULT = 500;
|
||||
const PAGE_SIZE_SCROLL_HEIGHT_RATIO = 100;
|
||||
const PAGE_SIZE_MAX_JUMPS = 30;
|
||||
const SEARCH_ACTION_MAX_DELAY = 300; // ms
|
||||
@@ -246,12 +245,6 @@ VariablesView.prototype = {
|
||||
*/
|
||||
scrollPageSize: SCROLL_PAGE_SIZE_DEFAULT,
|
||||
|
||||
/**
|
||||
* The maximum number of elements allowed in a scope, variable or property
|
||||
* that allows pagination when appending children.
|
||||
*/
|
||||
appendPageSize: APPEND_PAGE_SIZE_DEFAULT,
|
||||
|
||||
/**
|
||||
* Function called each time a variable or property's value is changed via
|
||||
* user interaction. If null, then value changes are disabled.
|
||||
@@ -556,6 +549,14 @@ VariablesView.prototype = {
|
||||
* The variable or property to search for.
|
||||
*/
|
||||
_doSearch: function(aToken) {
|
||||
if (this.controller.supportsSearch()) {
|
||||
this.empty();
|
||||
let scope = this.addScope(aToken);
|
||||
scope.expanded = true; // Expand the scope by default.
|
||||
scope.locked = true; // Prevent collapsing the scope.
|
||||
this.controller.performSearch(scope, aToken);
|
||||
return;
|
||||
}
|
||||
for (let scope of this._store) {
|
||||
switch (aToken) {
|
||||
case "":
|
||||
@@ -1214,7 +1215,6 @@ function Scope(aView, aName, aFlags = {}) {
|
||||
// Inherit properties and flags from the parent view. You can override
|
||||
// each of these directly onto any scope, variable or property instance.
|
||||
this.scrollPageSize = aView.scrollPageSize;
|
||||
this.appendPageSize = aView.appendPageSize;
|
||||
this.eval = aView.eval;
|
||||
this.switch = aView.switch;
|
||||
this.delete = aView.delete;
|
||||
@@ -1320,82 +1320,13 @@ Scope.prototype = {
|
||||
* Additional options for adding the properties. Supported options:
|
||||
* - sorted: true to sort all the properties before adding them
|
||||
* - callback: function invoked after each item is added
|
||||
* @param string aKeysType [optional]
|
||||
* Helper argument in the case of paginated items. Can be either
|
||||
* "just-strings" or "just-numbers". Humans shouldn't use this argument.
|
||||
*/
|
||||
addItems: function(aItems, aOptions = {}, aKeysType = "") {
|
||||
addItems: function(aItems, aOptions = {}) {
|
||||
let names = Object.keys(aItems);
|
||||
|
||||
// Building the view when inspecting an object with a very large number of
|
||||
// properties may take a long time. To avoid blocking the UI, group
|
||||
// the items into several lazily populated pseudo-items.
|
||||
let exceedsThreshold = names.length >= this.appendPageSize;
|
||||
let shouldPaginate = exceedsThreshold && aKeysType != "just-strings";
|
||||
if (shouldPaginate && this.allowPaginate) {
|
||||
// Group the items to append into two separate arrays, one containing
|
||||
// number-like keys, the other one containing string keys.
|
||||
if (aKeysType == "just-numbers") {
|
||||
var numberKeys = names;
|
||||
var stringKeys = [];
|
||||
} else {
|
||||
var numberKeys = [];
|
||||
var stringKeys = [];
|
||||
for (let name of names) {
|
||||
// Be very careful. Avoid Infinity, NaN and non Natural number keys.
|
||||
let coerced = +name;
|
||||
if (Number.isInteger(coerced) && coerced > -1) {
|
||||
numberKeys.push(name);
|
||||
} else {
|
||||
stringKeys.push(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This object contains a very large number of properties, but they're
|
||||
// almost all strings that can't be coerced to numbers. Don't paginate.
|
||||
if (numberKeys.length < this.appendPageSize) {
|
||||
this.addItems(aItems, aOptions, "just-strings");
|
||||
return;
|
||||
}
|
||||
|
||||
// Slices a section of the { name: descriptor } data properties.
|
||||
let paginate = (aArray, aBegin = 0, aEnd = aArray.length) => {
|
||||
let store = {}
|
||||
for (let i = aBegin; i < aEnd; i++) {
|
||||
let name = aArray[i];
|
||||
store[name] = aItems[name];
|
||||
}
|
||||
return store;
|
||||
};
|
||||
|
||||
// Creates a pseudo-item that populates itself with the data properties
|
||||
// from the corresponding page range.
|
||||
let createRangeExpander = (aArray, aBegin, aEnd, aOptions, aKeyTypes) => {
|
||||
let rangeVar = this.addItem(aArray[aBegin] + Scope.ellipsis + aArray[aEnd - 1]);
|
||||
rangeVar.onexpand = () => {
|
||||
let pageItems = paginate(aArray, aBegin, aEnd);
|
||||
rangeVar.addItems(pageItems, aOptions, aKeyTypes);
|
||||
}
|
||||
rangeVar.showArrow();
|
||||
rangeVar.target.setAttribute("pseudo-item", "");
|
||||
};
|
||||
|
||||
// Divide the number keys into quarters.
|
||||
let page = +Math.round(numberKeys.length / 4).toPrecision(1);
|
||||
createRangeExpander(numberKeys, 0, page, aOptions, "just-numbers");
|
||||
createRangeExpander(numberKeys, page, page * 2, aOptions, "just-numbers");
|
||||
createRangeExpander(numberKeys, page * 2, page * 3, aOptions, "just-numbers");
|
||||
createRangeExpander(numberKeys, page * 3, numberKeys.length, aOptions, "just-numbers");
|
||||
|
||||
// Append all the string keys together.
|
||||
this.addItems(paginate(stringKeys), aOptions, "just-strings");
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort all of the properties before adding them, if preferred.
|
||||
if (aOptions.sorted && aKeysType != "just-numbers") {
|
||||
names.sort();
|
||||
if (aOptions.sorted) {
|
||||
names.sort(this._naturalSort);
|
||||
}
|
||||
|
||||
// Add the properties to the current scope.
|
||||
@@ -1536,7 +1467,11 @@ Scope.prototype = {
|
||||
this._isExpanded = true;
|
||||
|
||||
if (this.onexpand) {
|
||||
this.onexpand(this);
|
||||
// We return onexpand as it sometimes returns a promise
|
||||
// (up to the user of VariableView to do it)
|
||||
// that can indicate when the view is done expanding
|
||||
// and attributes are available. (Mostly used for tests)
|
||||
return this.onexpand(this);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1601,6 +1536,22 @@ Scope.prototype = {
|
||||
this._isHeaderVisible = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sort in ascending order
|
||||
* This only needs to compare non-numbers since it is dealing with an array
|
||||
* which numeric-based indices are placed in order.
|
||||
*
|
||||
* @param string a
|
||||
* @param string b
|
||||
* @return number
|
||||
* -1 if a is less than b, 0 if no change in order, +1 if a is greater than 0
|
||||
*/
|
||||
_naturalSort: function(a,b) {
|
||||
if (isNaN(parseFloat(a)) && isNaN(parseFloat(b))) {
|
||||
return a < b ? -1 : 1;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows the scope's expand/collapse arrow.
|
||||
*/
|
||||
@@ -2232,8 +2183,9 @@ Variable.prototype = Heritage.extend(Scope.prototype, {
|
||||
|
||||
// Sort all of the properties before adding them, if preferred.
|
||||
if (aOptions.sorted) {
|
||||
propertyNames.sort();
|
||||
propertyNames.sort(this._naturalSort);
|
||||
}
|
||||
|
||||
// Add all the variable properties.
|
||||
for (let name of propertyNames) {
|
||||
let descriptor = Object.getOwnPropertyDescriptor(aObject, name);
|
||||
|
||||
@@ -32,8 +32,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "console",
|
||||
"resource://gre/modules/devtools/Console.jsm");
|
||||
|
||||
const MAX_LONG_STRING_LENGTH = 200000;
|
||||
const MAX_PROPERTY_ITEMS = 2000;
|
||||
const DBG_STRINGS_URI = "chrome://global/locale/devtools/debugger.properties";
|
||||
|
||||
const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["VariablesViewController", "StackFrameUtils"];
|
||||
|
||||
|
||||
@@ -158,6 +161,166 @@ VariablesViewController.prototype = {
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds pseudo items in case there is too many properties to display.
|
||||
* Each item can expand into property slices.
|
||||
*
|
||||
* @param Scope aTarget
|
||||
* The Scope where the properties will be placed into.
|
||||
* @param object aGrip
|
||||
* The property iterator grip.
|
||||
* @param object aIterator
|
||||
* The property iterator client.
|
||||
*/
|
||||
_populatePropertySlices: function(aTarget, aGrip, aIterator) {
|
||||
if (aGrip.count < MAX_PROPERTY_ITEMS) {
|
||||
return this._populateFromPropertyIterator(aTarget, aGrip);
|
||||
}
|
||||
|
||||
// Divide the keys into quarters.
|
||||
let items = Math.ceil(aGrip.count / 4);
|
||||
|
||||
let promises = [];
|
||||
for(let i = 0; i < 4; i++) {
|
||||
let start = aGrip.start + i * items;
|
||||
let count = i != 3 ? items : aGrip.count - i * items;
|
||||
|
||||
// Create a new kind of grip, with additional fields to define the slice
|
||||
let sliceGrip = {
|
||||
type: "property-iterator",
|
||||
propertyIterator: aIterator,
|
||||
start: start,
|
||||
count: count
|
||||
};
|
||||
|
||||
// Query the name of the first and last items for this slice
|
||||
let deferred = promise.defer();
|
||||
aIterator.names([start, start + count - 1], ({ names }) => {
|
||||
let label = "[" + names[0] + ELLIPSIS + names[1] + "]";
|
||||
let item = aTarget.addItem(label);
|
||||
item.showArrow();
|
||||
this.addExpander(item, sliceGrip);
|
||||
deferred.resolve();
|
||||
});
|
||||
promises.push(deferred.promise);
|
||||
}
|
||||
|
||||
return promise.all(promises);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a property slice for a Variable in the view using the already
|
||||
* property iterator
|
||||
*
|
||||
* @param Scope aTarget
|
||||
* The Scope where the properties will be placed into.
|
||||
* @param object aGrip
|
||||
* The property iterator grip.
|
||||
*/
|
||||
_populateFromPropertyIterator: function(aTarget, aGrip) {
|
||||
if (aGrip.count >= MAX_PROPERTY_ITEMS) {
|
||||
// We already started to split, but there is still too many properties, split again.
|
||||
return this._populatePropertySlices(aTarget, aGrip, aGrip.propertyIterator);
|
||||
}
|
||||
// We started slicing properties, and the slice is now small enough to be displayed
|
||||
let deferred = promise.defer();
|
||||
aGrip.propertyIterator.slice(aGrip.start, aGrip.count,
|
||||
({ ownProperties }) => {
|
||||
// Add all the variable properties.
|
||||
if (Object.keys(ownProperties).length > 0) {
|
||||
aTarget.addItems(ownProperties, {
|
||||
sorted: true,
|
||||
// Expansion handlers must be set after the properties are added.
|
||||
callback: this.addExpander
|
||||
});
|
||||
}
|
||||
deferred.resolve();
|
||||
});
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds the properties for a Variable in the view using a new feature in FF40+
|
||||
* that allows iteration over properties in slices.
|
||||
*
|
||||
* @param Scope aTarget
|
||||
* The Scope where the properties will be placed into.
|
||||
* @param object aGrip
|
||||
* The grip to use to populate the target.
|
||||
* @param string aQuery [optional]
|
||||
* The query string used to fetch only a subset of properties
|
||||
*/
|
||||
_populateFromObjectWithIterator: function(aTarget, aGrip, aQuery) {
|
||||
// FF40+ starts exposing `ownPropertyLength` on ObjectActor's grip,
|
||||
// as well as `enumProperties` request.
|
||||
let deferred = promise.defer();
|
||||
let objectClient = this._getObjectClient(aGrip);
|
||||
let isArray = aGrip.preview && aGrip.preview.kind === "ArrayLike";
|
||||
if (isArray) {
|
||||
// First enumerate array items, e.g. properties from `0` to `array.length`.
|
||||
let options = {
|
||||
ignoreNonIndexedProperties: true,
|
||||
ignoreSafeGetters: true,
|
||||
query: aQuery
|
||||
};
|
||||
objectClient.enumProperties(options, ({ iterator }) => {
|
||||
let sliceGrip = {
|
||||
type: "property-iterator",
|
||||
propertyIterator: iterator,
|
||||
start: 0,
|
||||
count: iterator.count
|
||||
};
|
||||
this._populatePropertySlices(aTarget, sliceGrip, iterator)
|
||||
.then(() => {
|
||||
// Then enumerate the rest of the properties, like length, buffer, etc.
|
||||
let options = {
|
||||
ignoreIndexedProperties: true,
|
||||
sort: true,
|
||||
query: aQuery
|
||||
};
|
||||
objectClient.enumProperties(options, ({ iterator }) => {
|
||||
let sliceGrip = {
|
||||
type: "property-iterator",
|
||||
propertyIterator: iterator,
|
||||
start: 0,
|
||||
count: iterator.count
|
||||
};
|
||||
deferred.resolve(this._populatePropertySlices(aTarget, sliceGrip, iterator));
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// For objects, we just enumerate all the properties sorted by name.
|
||||
objectClient.enumProperties({ sort: true, query: aQuery }, ({ iterator }) => {
|
||||
let sliceGrip = {
|
||||
type: "property-iterator",
|
||||
propertyIterator: iterator,
|
||||
start: 0,
|
||||
count: iterator.count
|
||||
};
|
||||
deferred.resolve(this._populatePropertySlices(aTarget, sliceGrip, iterator));
|
||||
});
|
||||
|
||||
}
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds the given prototype in the view.
|
||||
*
|
||||
* @param Scope aTarget
|
||||
* The Scope where the properties will be placed into.
|
||||
* @param object aProtype
|
||||
* The prototype grip.
|
||||
*/
|
||||
_populateObjectPrototype: function(aTarget, aPrototype) {
|
||||
// Add the variable's __proto__.
|
||||
if (aPrototype && aPrototype.type != "null") {
|
||||
let proto = aTarget.addItem("__proto__", { value: aPrototype });
|
||||
this.addExpander(proto, aPrototype);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds properties to a Scope, Variable, or Property in the view. Triggered
|
||||
* when a scope is expanded or certain variables are hovered.
|
||||
@@ -168,7 +331,19 @@ VariablesViewController.prototype = {
|
||||
* The grip to use to populate the target.
|
||||
*/
|
||||
_populateFromObject: function(aTarget, aGrip) {
|
||||
let deferred = promise.defer();
|
||||
// Fetch properties by slices if there is too many in order to prevent UI freeze.
|
||||
if ("ownPropertyLength" in aGrip && aGrip.ownPropertyLength >= MAX_PROPERTY_ITEMS) {
|
||||
return this._populateFromObjectWithIterator(aTarget, aGrip)
|
||||
.then(() => {
|
||||
let deferred = promise.defer();
|
||||
let objectClient = this._getObjectClient(aGrip);
|
||||
objectClient.getPrototype(({ prototype }) => {
|
||||
this._populateObjectPrototype(aTarget, prototype);
|
||||
deferred.resolve();
|
||||
});
|
||||
return deferred.promise;
|
||||
});
|
||||
}
|
||||
|
||||
if (aGrip.class === "Promise" && aGrip.promiseState) {
|
||||
const { state, value, reason } = aGrip.promiseState;
|
||||
@@ -179,10 +354,16 @@ VariablesViewController.prototype = {
|
||||
this.addExpander(aTarget.addItem("<reason>", { value: reason }), reason);
|
||||
}
|
||||
}
|
||||
return this._populateProperties(aTarget, aGrip);
|
||||
},
|
||||
|
||||
_populateProperties: function(aTarget, aGrip, aOptions) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
let objectClient = this._getObjectClient(aGrip);
|
||||
objectClient.getPrototypeAndProperties(aResponse => {
|
||||
let { ownProperties, prototype } = aResponse;
|
||||
let ownProperties = aResponse.ownProperties || {};
|
||||
let prototype = aResponse.prototype || null;
|
||||
// 'safeGetterValues' is new and isn't necessary defined on old actors.
|
||||
let safeGetterValues = aResponse.safeGetterValues || {};
|
||||
let sortable = VariablesView.isSortable(aGrip.class);
|
||||
@@ -200,21 +381,15 @@ VariablesViewController.prototype = {
|
||||
}
|
||||
|
||||
// Add all the variable properties.
|
||||
if (ownProperties) {
|
||||
aTarget.addItems(ownProperties, {
|
||||
// Not all variables need to force sorted properties.
|
||||
sorted: sortable,
|
||||
// Expansion handlers must be set after the properties are added.
|
||||
callback: this.addExpander
|
||||
});
|
||||
}
|
||||
aTarget.addItems(ownProperties, {
|
||||
// Not all variables need to force sorted properties.
|
||||
sorted: sortable,
|
||||
// Expansion handlers must be set after the properties are added.
|
||||
callback: this.addExpander
|
||||
});
|
||||
|
||||
// Add the variable's __proto__.
|
||||
if (prototype && prototype.type != "null") {
|
||||
let proto = aTarget.addItem("__proto__", { value: prototype });
|
||||
// Expansion handlers must be set after the properties are added.
|
||||
this.addExpander(proto, prototype);
|
||||
}
|
||||
this._populateObjectPrototype(aTarget, prototype);
|
||||
|
||||
// If the object is a function we need to fetch its scope chain
|
||||
// to show them as closures for the respective function.
|
||||
@@ -389,6 +564,10 @@ VariablesViewController.prototype = {
|
||||
let deferred = promise.defer();
|
||||
aTarget._fetched = deferred.promise;
|
||||
|
||||
if (aSource.type === "property-iterator") {
|
||||
return this._populateFromPropertyIterator(aTarget, aSource);
|
||||
}
|
||||
|
||||
// If the target is a Variable or Property then we're fetching properties.
|
||||
if (VariablesView.isVariable(aTarget)) {
|
||||
this._populateFromObject(aTarget, aSource).then(() => {
|
||||
@@ -438,6 +617,29 @@ VariablesViewController.prototype = {
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Indicates to the view if the targeted actor supports properties search
|
||||
*
|
||||
* @return boolean True, if the actor supports enumProperty request
|
||||
*/
|
||||
supportsSearch: function () {
|
||||
// FF40+ starts exposing ownPropertyLength on object actor's grip
|
||||
// as well as enumProperty which allows to query a subset of properties.
|
||||
return this.objectActor && ("ownPropertyLength" in this.objectActor);
|
||||
},
|
||||
|
||||
/**
|
||||
* Try to use the actor to perform an attribute search.
|
||||
*
|
||||
* @param Scope aScope
|
||||
* The Scope instance to populate with properties
|
||||
* @param string aToken
|
||||
* The query string
|
||||
*/
|
||||
performSearch: function(aScope, aToken) {
|
||||
this._populateFromObjectWithIterator(aScope, this.objectActor, aToken);
|
||||
},
|
||||
|
||||
/**
|
||||
* Release an actor from the controller.
|
||||
*
|
||||
@@ -497,6 +699,8 @@ VariablesViewController.prototype = {
|
||||
let populated;
|
||||
|
||||
if (aOptions.objectActor) {
|
||||
// Save objectActor for properties filtering
|
||||
this.objectActor = aOptions.objectActor;
|
||||
populated = this.populate(variable, aOptions.objectActor);
|
||||
variable.expand();
|
||||
} else if (aOptions.rawObject) {
|
||||
|
||||
@@ -341,6 +341,10 @@ ConsoleOutput.prototype = {
|
||||
this.owner.owner.openLink.apply(this.owner.owner, arguments);
|
||||
},
|
||||
|
||||
openLocationInDebugger: function ({url, line}) {
|
||||
return this.owner.owner.viewSourceInDebugger(url, line);
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the variables view to inspect an object actor.
|
||||
* @see JSTerm.openVariablesView() in webconsole.js
|
||||
@@ -2476,6 +2480,8 @@ Widgets.JSObject.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
|
||||
options.onClick = options.href ? this._onClickAnchor : this._onClick;
|
||||
}
|
||||
|
||||
options.onContextMenu = options.onContextMenu || this._onContextMenu;
|
||||
|
||||
let anchor = this.el("a", {
|
||||
class: options.className,
|
||||
draggable: false,
|
||||
@@ -2484,6 +2490,8 @@ Widgets.JSObject.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
|
||||
|
||||
this.message._addLinkCallback(anchor, options.onClick);
|
||||
|
||||
anchor.addEventListener("contextmenu", options.onContextMenu.bind(this));
|
||||
|
||||
if (options.appendTo) {
|
||||
options.appendTo.appendChild(anchor);
|
||||
} else if (!("appendTo" in options) && this.element) {
|
||||
@@ -2493,16 +2501,38 @@ Widgets.JSObject.prototype = Heritage.extend(Widgets.BaseWidget.prototype,
|
||||
return anchor;
|
||||
},
|
||||
|
||||
openObjectInVariablesView: function()
|
||||
{
|
||||
this.output.openVariablesView({
|
||||
label: VariablesView.getString(this.objectActor, { concise: true }),
|
||||
objectActor: this.objectActor,
|
||||
autofocus: true,
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* The click event handler for objects shown inline.
|
||||
* @private
|
||||
*/
|
||||
_onClick: function()
|
||||
{
|
||||
this.output.openVariablesView({
|
||||
label: VariablesView.getString(this.objectActor, { concise: true }),
|
||||
objectActor: this.objectActor,
|
||||
autofocus: true,
|
||||
this.openObjectInVariablesView();
|
||||
},
|
||||
|
||||
_onContextMenu: function(ev) {
|
||||
// TODO offer a nice API for the context menu.
|
||||
// Probably worth to take a look at Firebug's way
|
||||
// https://github.com/firebug/firebug/blob/master/extension/content/firebug/chrome/menu.js
|
||||
let doc = ev.target.ownerDocument;
|
||||
let cmPopup = doc.getElementById("output-contextmenu");
|
||||
let openInVarViewCmd = doc.getElementById("menu_openInVarView");
|
||||
let openVarView = this.openObjectInVariablesView.bind(this);
|
||||
openInVarViewCmd.addEventListener("command", openVarView);
|
||||
openInVarViewCmd.removeAttribute("disabled");
|
||||
cmPopup.addEventListener("popuphiding", function onPopupHiding() {
|
||||
cmPopup.removeEventListener("popuphiding", onPopupHiding);
|
||||
openInVarViewCmd.removeEventListener("command", openVarView);
|
||||
openInVarViewCmd.setAttribute("disabled", "true");
|
||||
});
|
||||
},
|
||||
|
||||
@@ -2670,6 +2700,16 @@ Widgets.ObjectRenderers.add({
|
||||
|
||||
this._text(")");
|
||||
},
|
||||
|
||||
_onClick: function () {
|
||||
let location = this.objectActor.location;
|
||||
if (location) {
|
||||
this.output.openLocationInDebugger(location);
|
||||
}
|
||||
else {
|
||||
this.openObjectInVariablesView();
|
||||
}
|
||||
}
|
||||
}); // Widgets.ObjectRenderers.byClass.Function
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,393 @@
|
||||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
support-files =
|
||||
head.js
|
||||
test-bug-585956-console-trace.html
|
||||
test-bug-593003-iframe-wrong-hud-iframe.html
|
||||
test-bug-593003-iframe-wrong-hud.html
|
||||
test-bug-595934-canvas-css.html
|
||||
test-bug-595934-canvas-css.js
|
||||
test-bug-595934-css-loader.css
|
||||
test-bug-595934-css-loader.css^headers^
|
||||
test-bug-595934-css-loader.html
|
||||
test-bug-595934-css-parser.css
|
||||
test-bug-595934-css-parser.html
|
||||
test-bug-595934-empty-getelementbyid.html
|
||||
test-bug-595934-empty-getelementbyid.js
|
||||
test-bug-595934-html.html
|
||||
test-bug-595934-image.html
|
||||
test-bug-595934-image.jpg
|
||||
test-bug-595934-imagemap.html
|
||||
test-bug-595934-malformedxml-external.html
|
||||
test-bug-595934-malformedxml-external.xml
|
||||
test-bug-595934-malformedxml.xhtml
|
||||
test-bug-595934-svg.xhtml
|
||||
test-bug-595934-workers.html
|
||||
test-bug-595934-workers.js
|
||||
test-bug-597136-external-script-errors.html
|
||||
test-bug-597136-external-script-errors.js
|
||||
test-bug-597756-reopen-closed-tab.html
|
||||
test-bug-599725-response-headers.sjs
|
||||
test-bug-600183-charset.html
|
||||
test-bug-600183-charset.html^headers^
|
||||
test-bug-601177-log-levels.html
|
||||
test-bug-601177-log-levels.js
|
||||
test-bug-603750-websocket.html
|
||||
test-bug-603750-websocket.js
|
||||
test-bug-613013-console-api-iframe.html
|
||||
test-bug-618078-network-exceptions.html
|
||||
test-bug-621644-jsterm-dollar.html
|
||||
test-bug-630733-response-redirect-headers.sjs
|
||||
test-bug-632275-getters.html
|
||||
test-bug-632347-iterators-generators.html
|
||||
test-bug-644419-log-limits.html
|
||||
test-bug-646025-console-file-location.html
|
||||
test-bug-658368-time-methods.html
|
||||
test-bug-737873-mixedcontent.html
|
||||
test-bug-752559-ineffective-iframe-sandbox-warning0.html
|
||||
test-bug-752559-ineffective-iframe-sandbox-warning1.html
|
||||
test-bug-752559-ineffective-iframe-sandbox-warning2.html
|
||||
test-bug-752559-ineffective-iframe-sandbox-warning3.html
|
||||
test-bug-752559-ineffective-iframe-sandbox-warning4.html
|
||||
test-bug-752559-ineffective-iframe-sandbox-warning5.html
|
||||
test-bug-752559-ineffective-iframe-sandbox-warning-inner.html
|
||||
test-bug-752559-ineffective-iframe-sandbox-warning-nested1.html
|
||||
test-bug-752559-ineffective-iframe-sandbox-warning-nested2.html
|
||||
test-bug-762593-insecure-passwords-about-blank-web-console-warning.html
|
||||
test-bug-762593-insecure-passwords-web-console-warning.html
|
||||
test-bug-766001-console-log.js
|
||||
test-bug-766001-js-console-links.html
|
||||
test-bug-766001-js-errors.js
|
||||
test-bug-782653-css-errors-1.css
|
||||
test-bug-782653-css-errors-2.css
|
||||
test-bug-782653-css-errors.html
|
||||
test-bug-837351-security-errors.html
|
||||
test-bug-846918-hsts-invalid-headers.html
|
||||
test-bug-846918-hsts-invalid-headers.html^headers^
|
||||
test-bug-859170-longstring-hang.html
|
||||
test-bug-869003-iframe.html
|
||||
test-bug-869003-top-window.html
|
||||
test-closure-optimized-out.html
|
||||
test-closures.html
|
||||
test-console-assert.html
|
||||
test-console-count.html
|
||||
test-console-count-external-file.js
|
||||
test-console-extras.html
|
||||
test-console-replaced-api.html
|
||||
test-console.html
|
||||
test-console-workers.html
|
||||
test-console-table.html
|
||||
test-console-output-02.html
|
||||
test-console-output-03.html
|
||||
test-console-output-04.html
|
||||
test-console-output-dom-elements.html
|
||||
test-console-output-events.html
|
||||
test-console-output-regexp.html
|
||||
test-console-column.html
|
||||
test-consoleiframes.html
|
||||
test-certificate-messages.html
|
||||
test-data.json
|
||||
test-data.json^headers^
|
||||
test-duplicate-error.html
|
||||
test-encoding-ISO-8859-1.html
|
||||
test-error.html
|
||||
test-eval-in-stackframe.html
|
||||
test-file-location.js
|
||||
test-filter.html
|
||||
test-for-of.html
|
||||
test-iframe-762593-insecure-form-action.html
|
||||
test-iframe-762593-insecure-frame.html
|
||||
test-iframe1.html
|
||||
test-iframe2.html
|
||||
test-iframe3.html
|
||||
test-image.png
|
||||
test-mixedcontent-securityerrors.html
|
||||
test-mutation.html
|
||||
test-network-request.html
|
||||
test-network.html
|
||||
test-observe-http-ajax.html
|
||||
test-own-console.html
|
||||
test-property-provider.html
|
||||
test-repeated-messages.html
|
||||
test-result-format-as-string.html
|
||||
test-webconsole-error-observer.html
|
||||
test_bug_770099_violation.html
|
||||
test_bug_770099_violation.html^headers^
|
||||
test-autocomplete-in-stackframe.html
|
||||
testscript.js
|
||||
test-bug_923281_console_log_filter.html
|
||||
test-bug_923281_test1.js
|
||||
test-bug_923281_test2.js
|
||||
test-bug_939783_console_trace_duplicates.html
|
||||
test-bug-952277-highlight-nodes-in-vview.html
|
||||
test-bug-609872-cd-iframe-parent.html
|
||||
test-bug-609872-cd-iframe-child.html
|
||||
test-bug-989025-iframe-parent.html
|
||||
test-bug_1050691_click_function_to_source.html
|
||||
test-bug_1050691_click_function_to_source.js
|
||||
test-console-api-stackframe.html
|
||||
test_bug_1010953_cspro.html^headers^
|
||||
test_bug_1010953_cspro.html
|
||||
test_bug1045902_console_csp_ignore_reflected_xss_message.html^headers^
|
||||
test_bug1045902_console_csp_ignore_reflected_xss_message.html
|
||||
test_bug1092055_shouldwarn.js^headers^
|
||||
test_bug1092055_shouldwarn.js
|
||||
test_bug1092055_shouldwarn.html
|
||||
|
||||
[browser_bug1045902_console_csp_ignore_reflected_xss_message.js]
|
||||
[browser_bug664688_sandbox_update_after_navigation.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole e10s tests (intermittent Linux debug)
|
||||
[browser_bug_638949_copy_link_location.js]
|
||||
[browser_bug_862916_console_dir_and_filter_off.js]
|
||||
[browser_bug_865288_repeat_different_objects.js]
|
||||
[browser_bug_865871_variables_view_close_on_esc_key.js]
|
||||
[browser_bug_869003_inspect_cross_domain_object.js]
|
||||
[browser_bug_871156_ctrlw_close_tab.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole e10s tests (intermittent Linux debug)
|
||||
[browser_cached_messages.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1042253 - webconsole e10s tests (expectUncaughtException)
|
||||
[browser_console.js]
|
||||
[browser_console_addonsdk_loader_exception.js]
|
||||
[browser_console_clear_on_reload.js]
|
||||
[browser_console_click_focus.js]
|
||||
[browser_console_consolejsm_output.js]
|
||||
[browser_console_copy_command.js]
|
||||
[browser_console_dead_objects.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_console_copy_entire_message_context_menu.js]
|
||||
[browser_console_error_source_click.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1042253 - webconsole e10s tests
|
||||
[browser_console_filters.js]
|
||||
[browser_console_iframe_messages.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1042253 - webconsole e10s tests
|
||||
[browser_console_keyboard_accessibility.js]
|
||||
[browser_console_log_inspectable_object.js]
|
||||
[browser_console_native_getters.js]
|
||||
[browser_console_navigation_marker.js]
|
||||
[browser_console_nsiconsolemessage.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_console_optimized_out_vars.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_console_private_browsing.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1042253 - webconsole e10s tests
|
||||
[browser_console_variables_view.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_console_variables_view_dom_nodes.js]
|
||||
[browser_console_variables_view_dont_sort_non_sortable_classes_properties.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_console_variables_view_while_debugging.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_console_variables_view_while_debugging_and_inspecting.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_eval_in_debugger_stackframe.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_eval_in_debugger_stackframe2.js]
|
||||
[browser_jsterm_inspect.js]
|
||||
[browser_longstring_hang.js]
|
||||
[browser_netpanel_longstring_expand.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_output_breaks_after_console_dir_uninspectable.js]
|
||||
[browser_output_longstring_expand.js]
|
||||
[browser_repeated_messages_accuracy.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_result_format_as_string.js]
|
||||
[browser_warn_user_about_replaced_api.js]
|
||||
[browser_webconsole_abbreviate_source_url.js]
|
||||
[browser_webconsole_allow_mixedcontent_securityerrors.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_webconsole_assert.js]
|
||||
[browser_webconsole_basic_net_logging.js]
|
||||
[browser_webconsole_block_mixedcontent_securityerrors.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_webconsole_bug_579412_input_focus.js]
|
||||
[browser_webconsole_bug_580001_closing_after_completion.js]
|
||||
[browser_webconsole_bug_580030_errors_after_page_reload.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s (expectUncaughtException)
|
||||
[browser_webconsole_bug_580454_timestamp_l10n.js]
|
||||
[browser_webconsole_bug_582201_duplicate_errors.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s (expectUncaughtException)
|
||||
[browser_webconsole_bug_583816_No_input_and_Tab_key_pressed.js]
|
||||
[browser_webconsole_bug_585237_line_limit.js]
|
||||
[browser_webconsole_bug_585956_console_trace.js]
|
||||
[browser_webconsole_bug_585991_autocomplete_keys.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_webconsole_bug_585991_autocomplete_popup.js]
|
||||
[browser_webconsole_bug_586388_select_all.js]
|
||||
[browser_webconsole_bug_587617_output_copy.js]
|
||||
[browser_webconsole_bug_588342_document_focus.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_webconsole_bug_588730_text_node_insertion.js]
|
||||
[browser_webconsole_bug_588967_input_expansion.js]
|
||||
[browser_webconsole_bug_589162_css_filter.js]
|
||||
[browser_webconsole_bug_592442_closing_brackets.js]
|
||||
[browser_webconsole_bug_593003_iframe_wrong_hud.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_webconsole_bug_594477_clickable_output.js]
|
||||
[browser_webconsole_bug_594497_history_arrow_keys.js]
|
||||
[browser_webconsole_bug_595223_file_uri.js]
|
||||
[browser_webconsole_bug_595350_multiple_windows_and_tabs.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_webconsole_bug_595934_message_categories.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s (expectUncaughtException)
|
||||
[browser_webconsole_bug_597103_deactivateHUDForContext_unfocused_window.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_webconsole_bug_597136_external_script_errors.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s (expectUncaughtException)
|
||||
[browser_webconsole_bug_597136_network_requests_from_chrome.js]
|
||||
[browser_webconsole_bug_597460_filter_scroll.js]
|
||||
[browser_webconsole_bug_597756_reopen_closed_tab.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s (expectUncaughtException)
|
||||
[browser_webconsole_bug_599725_response_headers.js]
|
||||
[browser_webconsole_bug_600183_charset.js]
|
||||
[browser_webconsole_bug_601177_log_levels.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s (expectUncaughtException)
|
||||
[browser_webconsole_bug_601352_scroll.js]
|
||||
[browser_webconsole_bug_601667_filter_buttons.js]
|
||||
[browser_webconsole_bug_602572_log_bodies_checkbox.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_webconsole_bug_603750_websocket.js]
|
||||
[browser_webconsole_bug_611795.js]
|
||||
[browser_webconsole_bug_613013_console_api_iframe.js]
|
||||
[browser_webconsole_bug_613280_jsterm_copy.js]
|
||||
[browser_webconsole_bug_613642_maintain_scroll.js]
|
||||
[browser_webconsole_bug_613642_prune_scroll.js]
|
||||
[browser_webconsole_bug_614793_jsterm_scroll.js]
|
||||
[browser_webconsole_bug_618078_network_exceptions.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s (expectUncaughtException)
|
||||
[browser_webconsole_bug_618311_close_panels.js]
|
||||
[browser_webconsole_bug_621644_jsterm_dollar.js]
|
||||
[browser_webconsole_bug_622303_persistent_filters.js]
|
||||
[browser_webconsole_bug_623749_ctrl_a_select_all_winnt.js]
|
||||
skip-if = os != "win"
|
||||
[browser_webconsole_bug_630733_response_redirect_headers.js]
|
||||
[browser_webconsole_bug_632275_getters_document_width.js]
|
||||
[browser_webconsole_bug_632347_iterators_generators.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_webconsole_bug_632817.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_webconsole_bug_642108_pruneTest.js]
|
||||
[browser_webconsole_autocomplete_and_selfxss.js]
|
||||
[browser_webconsole_bug_644419_log_limits.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s (expectUncaughtException)
|
||||
[browser_webconsole_bug_646025_console_file_location.js]
|
||||
[browser_webconsole_bug_651501_document_body_autocomplete.js]
|
||||
[browser_webconsole_bug_653531_highlighter_console_helper.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_webconsole_bug_658368_time_methods.js]
|
||||
[browser_webconsole_bug_659907_console_dir.js]
|
||||
[browser_webconsole_bug_660806_history_nav.js]
|
||||
[browser_webconsole_bug_664131_console_group.js]
|
||||
[browser_webconsole_bug_686937_autocomplete_JSTerm_helpers.js]
|
||||
[browser_webconsole_bug_704295.js]
|
||||
[browser_webconsole_bug_734061_No_input_change_and_Tab_key_pressed.js]
|
||||
[browser_webconsole_bug_737873_mixedcontent.js]
|
||||
[browser_webconsole_bug_752559_ineffective_iframe_sandbox_warning.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
|
||||
[browser_webconsole_bug_762593_insecure_passwords_about_blank_web_console_warning.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_webconsole_bug_762593_insecure_passwords_web_console_warning.js]
|
||||
skip-if = true # Bug 1110500 - mouse event failure in test
|
||||
[browser_webconsole_bug_764572_output_open_url.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_webconsole_bug_766001_JS_Console_in_Debugger.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1042253 - webconsole e10s tests (expectUncaughtException)
|
||||
[browser_webconsole_bug_770099_violation.js]
|
||||
[browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_webconsole_bug_804845_ctrl_key_nav.js]
|
||||
skip-if = os != "mac"
|
||||
[browser_webconsole_bug_817834_add_edited_input_to_history.js]
|
||||
[browser_webconsole_bug_837351_securityerrors.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_webconsole_bug_846918_hsts_invalid-headers.js]
|
||||
skip-if = buildapp == 'mulet' || e10s # Bug 1042253 - webconsole e10s tests
|
||||
[browser_webconsole_bug_915141_toggle_response_logging_with_keyboard.js]
|
||||
[browser_webconsole_filter_buttons_contextmenu.js]
|
||||
[browser_webconsole_bug_1006027_message_timestamps_incorrect.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug intermittent)
|
||||
[browser_webconsole_bug_1010953_cspro.js]
|
||||
[browser_webconsole_certificate_messages.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_webconsole_show_subresource_security_errors.js]
|
||||
[browser_webconsole_cached_autocomplete.js]
|
||||
[browser_webconsole_change_font_size.js]
|
||||
[browser_webconsole_chrome.js]
|
||||
[browser_webconsole_clickable_urls.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
|
||||
[browser_webconsole_closure_inspection.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_webconsole_completion.js]
|
||||
[browser_webconsole_console_extras.js]
|
||||
[browser_webconsole_console_logging_api.js]
|
||||
[browser_webconsole_console_logging_workers_api.js]
|
||||
[browser_webconsole_count.js]
|
||||
[browser_webconsole_dont_navigate_on_doubleclick.js]
|
||||
[browser_webconsole_execution_scope.js]
|
||||
[browser_webconsole_for_of.js]
|
||||
[browser_webconsole_history.js]
|
||||
[browser_webconsole_input_field_focus_on_panel_select.js]
|
||||
[browser_webconsole_inspect-parsed-documents.js]
|
||||
[browser_webconsole_js_input_expansion.js]
|
||||
[browser_webconsole_jsterm.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
|
||||
[browser_webconsole_live_filtering_of_message_types.js]
|
||||
[browser_webconsole_live_filtering_on_search_strings.js]
|
||||
[browser_webconsole_message_node_id.js]
|
||||
[browser_webconsole_netlogging.js]
|
||||
[browser_webconsole_network_panel.js]
|
||||
[browser_webconsole_notifications.js]
|
||||
[browser_webconsole_open-links-without-callback.js]
|
||||
[browser_webconsole_promise.js]
|
||||
[browser_webconsole_output_copy_newlines.js]
|
||||
[browser_webconsole_output_order.js]
|
||||
[browser_webconsole_property_provider.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_webconsole_scratchpad_panel_link.js]
|
||||
[browser_webconsole_split.js]
|
||||
[browser_webconsole_split_escape_key.js]
|
||||
[browser_webconsole_split_focus.js]
|
||||
[browser_webconsole_split_persist.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
|
||||
[browser_webconsole_view_source.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s (expectUncaughtException)
|
||||
[browser_webconsole_reflow.js]
|
||||
[browser_webconsole_log_file_filter.js]
|
||||
[browser_webconsole_expandable_timestamps.js]
|
||||
[browser_webconsole_autocomplete_in_debugger_stackframe.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_webconsole_autocomplete_popup_close_on_tab_switch.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
|
||||
[browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js]
|
||||
[browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js]
|
||||
[browser_console_history_persist.js]
|
||||
[browser_webconsole_output_01.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole e10s tests
|
||||
[browser_webconsole_output_02.js]
|
||||
[browser_webconsole_output_03.js]
|
||||
[browser_webconsole_output_04.js]
|
||||
[browser_webconsole_output_05.js]
|
||||
[browser_webconsole_output_06.js]
|
||||
[browser_webconsole_output_dom_elements_01.js]
|
||||
[browser_webconsole_output_dom_elements_02.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
|
||||
[browser_webconsole_output_dom_elements_03.js]
|
||||
[browser_webconsole_output_dom_elements_04.js]
|
||||
skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
|
||||
[browser_webconsole_output_events.js]
|
||||
[browser_webconsole_output_regexp.js]
|
||||
[browser_webconsole_output_table.js]
|
||||
[browser_console_variables_view_highlighter.js]
|
||||
[browser_webconsole_start_netmon_first.js]
|
||||
[browser_webconsole_console_trace_duplicates.js]
|
||||
[browser_webconsole_cd_iframe.js]
|
||||
[browser_webconsole_autocomplete_crossdomain_iframe.js]
|
||||
[browser_webconsole_console_custom_styles.js]
|
||||
[browser_webconsole_console_api_stackframe.js]
|
||||
[browser_webconsole_column_numbers.js]
|
||||
[browser_console_open_or_focus.js]
|
||||
[browser_webconsole_bug_922212_console_dirxml.js]
|
||||
[browser_webconsole_shows_reqs_in_netmonitor.js]
|
||||
[browser_netmonitor_shows_reqs_in_webconsole.js]
|
||||
[browser_webconsole_bug_1050691_click_function_to_source.js]
|
||||
[browser_webconsole_context_menu_open_in_var_view.js]
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
/* Test case that ensures Array and other list types are not sorted in variables
|
||||
* view.
|
||||
*
|
||||
* The tested types are:
|
||||
* - Array
|
||||
* - Int8Array
|
||||
* - Int16Array
|
||||
* - Int32Array
|
||||
* - Uint8Array
|
||||
* - Uint16Array
|
||||
* - Uint32Array
|
||||
* - Uint8ClampedArray
|
||||
* - Float32Array
|
||||
* - Float64Array
|
||||
* - NodeList
|
||||
*/
|
||||
|
||||
function test() {
|
||||
const TEST_URI = "data:text/html;charset=utf-8, \
|
||||
<html> \
|
||||
<head> \
|
||||
<title>Test document for bug 977500</title> \
|
||||
</head> \
|
||||
<body> \
|
||||
<div></div> \
|
||||
<div></div> \
|
||||
<div></div> \
|
||||
<div></div> \
|
||||
<div></div> \
|
||||
<div></div> \
|
||||
<div></div> \
|
||||
<div></div> \
|
||||
<div></div> \
|
||||
<div></div> \
|
||||
<div></div> \
|
||||
<div></div> \
|
||||
</body> \
|
||||
</html>";
|
||||
|
||||
let jsterm;
|
||||
|
||||
function* runner() {
|
||||
const typedArrayTypes = ["Int8Array", "Int16Array", "Int32Array",
|
||||
"Uint8Array", "Uint16Array", "Uint32Array",
|
||||
"Uint8ClampedArray", "Float32Array",
|
||||
"Float64Array"];
|
||||
|
||||
const {tab} = yield loadTab(TEST_URI);
|
||||
const hud = yield openConsole(tab);
|
||||
jsterm = hud.jsterm;
|
||||
|
||||
// Create an ArrayBuffer of 80 bytes to test TypedArrays. 80 bytes is
|
||||
// enough to get 10 items in all different TypedArrays.
|
||||
yield jsterm.execute("let buf = new ArrayBuffer(80);");
|
||||
|
||||
// Array
|
||||
yield testNotSorted("Array(0,1,2,3,4,5,6,7,8,9,10)");
|
||||
// NodeList
|
||||
yield testNotSorted("document.querySelectorAll('div')");
|
||||
// Object
|
||||
yield testSorted("Object({'hello':1,1:5,10:2,4:2,'abc':1})");
|
||||
|
||||
// Typed arrays.
|
||||
for (let type of typedArrayTypes) {
|
||||
yield testNotSorted("new " + type + "(buf)");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper that ensures the properties are not sorted when an object
|
||||
* specified by aObject is inspected.
|
||||
*
|
||||
* @param string aObject
|
||||
* A string that, once executed, creates and returns the object to
|
||||
* inspect.
|
||||
*/
|
||||
function testNotSorted(aObject) {
|
||||
info("Testing " + aObject);
|
||||
let deferred = promise.defer();
|
||||
jsterm.once("variablesview-fetched", (_, aVar) => deferred.resolve(aVar));
|
||||
jsterm.execute("inspect(" + aObject + ")");
|
||||
|
||||
let variableScope = yield deferred.promise;
|
||||
ok(variableScope, "Variables view opened");
|
||||
|
||||
// If the properties are sorted: keys = ["0", "1", "10",...] <- incorrect
|
||||
// If the properties are not sorted: keys = ["0", "1", "2",...] <- correct
|
||||
let keyIterator = variableScope._store.keys();
|
||||
is(keyIterator.next().value, "0", "First key is 0");
|
||||
is(keyIterator.next().value, "1", "Second key is 1");
|
||||
|
||||
// If the properties are sorted, the next one will be 10.
|
||||
is(keyIterator.next().value, "2", "Third key is 2, not 10");
|
||||
}
|
||||
/**
|
||||
* A helper that ensures the properties are sorted when an object
|
||||
* specified by aObject is inspected.
|
||||
*
|
||||
* @param string aObject
|
||||
* A string that, once executed, creates and returns the object to
|
||||
* inspect.
|
||||
*/
|
||||
function testSorted(aObject) {
|
||||
info("Testing " + aObject);
|
||||
let deferred = promise.defer();
|
||||
jsterm.once("variablesview-fetched", (_, aVar) => deferred.resolve(aVar));
|
||||
jsterm.execute("inspect(" + aObject + ")");
|
||||
|
||||
let variableScope = yield deferred.promise;
|
||||
ok(variableScope, "Variables view opened");
|
||||
|
||||
// If the properties are sorted: keys = ["1", "4", "10",..., "abc", "hello"] <- correct
|
||||
// If the properties are not sorted: keys = ["1", "10", "4",...] <- incorrect
|
||||
let keyIterator = variableScope._store.keys();
|
||||
is(keyIterator.next().value, "1", "First key should be 1");
|
||||
is(keyIterator.next().value, "4", "Second key should be 4");
|
||||
|
||||
// If the properties are sorted, the next one will be 10.
|
||||
is(keyIterator.next().value, "10", "Third key is 10");
|
||||
// If sorted next properties should be "abc" then "hello"
|
||||
is(keyIterator.next().value, "abc", "Fourth key is abc");
|
||||
is(keyIterator.next().value, "hello", "Fifth key is hello");
|
||||
}
|
||||
|
||||
Task.spawn(runner).then(finishTest);
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* 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/. */
|
||||
|
||||
// Tests that clicking on a function displays its source in the debugger.
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug_1050691_click_function_to_source.html";
|
||||
|
||||
let test = asyncTest(function*() {
|
||||
yield loadTab(TEST_URI);
|
||||
let hud = yield openConsole();
|
||||
|
||||
yield testWithoutDebuggerOpen(hud);
|
||||
|
||||
// Open the Debugger panel.
|
||||
let debuggerPanel = yield openDebugger();
|
||||
// And right after come back to the Console panel.
|
||||
yield openConsole();
|
||||
yield testWithDebuggerOpen(hud, debuggerPanel);
|
||||
});
|
||||
|
||||
function* testWithoutDebuggerOpen(hud) {
|
||||
let clickable = yield printFunction(hud);
|
||||
let onVariablesViewOpen = hud.jsterm.once("variablesview-fetched");
|
||||
synthesizeClick(clickable, hud);
|
||||
return onVariablesViewOpen;
|
||||
}
|
||||
|
||||
function* testWithDebuggerOpen(hud, debuggerPanel) {
|
||||
let clickable = yield printFunction(hud);
|
||||
let panelWin = debuggerPanel.panelWin;
|
||||
let onEditorLocationSet = panelWin.once(panelWin.EVENTS.EDITOR_LOCATION_SET);
|
||||
synthesizeClick(clickable, hud);
|
||||
yield onEditorLocationSet;
|
||||
ok(isDebuggerCaretPos(debuggerPanel, 7),
|
||||
"Clicking on a function should go to its source in the debugger view");
|
||||
}
|
||||
|
||||
function synthesizeClick(clickable, hud) {
|
||||
EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow);
|
||||
}
|
||||
|
||||
let printFunction = Task.async(function* (hud) {
|
||||
hud.jsterm.clearOutput();
|
||||
content.wrappedJSObject.foo();
|
||||
let [result] = yield waitForMessages({
|
||||
webconsole: hud,
|
||||
messages: [{
|
||||
category: CATEGORY_WEBDEV,
|
||||
severity: SEVERITY_LOG,
|
||||
}],
|
||||
});
|
||||
let msg = [...result.matched][0];
|
||||
let clickable = msg.querySelector("a");
|
||||
ok(clickable, "clickable item for object should exist");
|
||||
return clickable;
|
||||
});
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// Check that inspecting a closure in the variables view sidebar works when
|
||||
// execution is paused.
|
||||
|
||||
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-closures.html";
|
||||
|
||||
let gWebConsole, gJSTerm, gVariablesView;
|
||||
|
||||
function test()
|
||||
{
|
||||
registerCleanupFunction(() => {
|
||||
gWebConsole = gJSTerm = gVariablesView = null;
|
||||
});
|
||||
|
||||
loadTab(TEST_URI).then(() => {
|
||||
openConsole().then((hud) => {
|
||||
openDebugger().then(({ toolbox, panelWin }) => {
|
||||
let deferred = promise.defer();
|
||||
panelWin.gThreadClient.addOneTimeListener("resumed", (aEvent, aPacket) => {
|
||||
ok(true, "Debugger resumed");
|
||||
deferred.resolve({ toolbox: toolbox, panelWin: panelWin });
|
||||
});
|
||||
return deferred.promise;
|
||||
}).then(({ toolbox, panelWin }) => {
|
||||
let deferred = promise.defer();
|
||||
panelWin.once(panelWin.EVENTS.FETCHED_SCOPES, (aEvent, aPacket) => {
|
||||
ok(true, "Scopes were fetched");
|
||||
toolbox.selectTool("webconsole").then(() => consoleOpened(hud));
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
let button = content.document.querySelector("button");
|
||||
ok(button, "button element found");
|
||||
EventUtils.synthesizeMouseAtCenter(button, {}, content);
|
||||
|
||||
return deferred.promise;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function consoleOpened(hud)
|
||||
{
|
||||
gWebConsole = hud;
|
||||
gJSTerm = hud.jsterm;
|
||||
gJSTerm.execute("window.george.getName");
|
||||
|
||||
waitForMessages({
|
||||
webconsole: gWebConsole,
|
||||
messages: [{
|
||||
text: "function _pfactory/<.getName()",
|
||||
category: CATEGORY_OUTPUT,
|
||||
objects: true,
|
||||
}],
|
||||
}).then(onExecuteGetName);
|
||||
}
|
||||
|
||||
function onExecuteGetName(aResults)
|
||||
{
|
||||
let clickable = aResults[0].clickableElements[0];
|
||||
ok(clickable, "clickable object found");
|
||||
|
||||
gJSTerm.once("variablesview-fetched", onGetNameFetch);
|
||||
let contextMenu =
|
||||
gWebConsole.iframeWindow.document.getElementById("output-contextmenu");
|
||||
waitForContextMenu(contextMenu, clickable, () => {
|
||||
let openInVarView = contextMenu.querySelector("#menu_openInVarView");
|
||||
ok(openInVarView.disabled === false,
|
||||
"the \"Open In Variables View\" context menu item should be clickable");
|
||||
// EventUtils.synthesizeMouseAtCenter seems to fail here in Mac OSX
|
||||
openInVarView.click();
|
||||
});
|
||||
}
|
||||
|
||||
function onGetNameFetch(aEvent, aVar)
|
||||
{
|
||||
gVariablesView = aVar._variablesView;
|
||||
ok(gVariablesView, "variables view object");
|
||||
|
||||
findVariableViewProperties(aVar, [
|
||||
{ name: /_pfactory/, value: "" },
|
||||
], { webconsole: gWebConsole }).then(onExpandClosure);
|
||||
}
|
||||
|
||||
function onExpandClosure(aResults)
|
||||
{
|
||||
let prop = aResults[0].matchedProp;
|
||||
ok(prop, "matched the name property in the variables view");
|
||||
|
||||
gVariablesView.window.focus();
|
||||
gJSTerm.once("sidebar-closed", finishTest);
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* 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/. */
|
||||
|
||||
// Tests that the "Open in Variables View" context menu item is enabled
|
||||
// only for objects.
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_URI = `data:text/html,<script>
|
||||
console.log("foo");
|
||||
console.log("foo", window);
|
||||
</script>`;
|
||||
|
||||
let test = asyncTest(function*() {
|
||||
yield loadTab(TEST_URI);
|
||||
let hud = yield openConsole();
|
||||
|
||||
let [result] = yield waitForMessages({
|
||||
webconsole: hud,
|
||||
messages: [{
|
||||
category: CATEGORY_WEBDEV,
|
||||
severity: SEVERITY_LOG,
|
||||
count: 2,
|
||||
text: /foo/
|
||||
}],
|
||||
});
|
||||
|
||||
let [msgWithText, msgWithObj] = [...result.matched];
|
||||
ok(msgWithText && msgWithObj, "Two messages should have appeared");
|
||||
|
||||
let contextMenu = hud.iframeWindow.
|
||||
document.getElementById("output-contextmenu");
|
||||
let openInVarViewItem = contextMenu.querySelector("#menu_openInVarView");
|
||||
let obj = msgWithObj.querySelector(".cm-variable");
|
||||
let text = msgWithText.querySelector(".console-string");
|
||||
|
||||
yield waitForContextMenu(contextMenu, obj, () => {
|
||||
ok(openInVarViewItem.disabled === false, "The \"Open In Variables View\" " +
|
||||
"context menu item should be available for objects");
|
||||
}, () => {
|
||||
ok(openInVarViewItem.disabled === true, "The \"Open In Variables View\" " +
|
||||
"context menu item should be disabled on popup hiding");
|
||||
});
|
||||
|
||||
yield waitForContextMenu(contextMenu, text, () => {
|
||||
ok(openInVarViewItem.disabled === true, "The \"Open In Variables View\" " +
|
||||
"context menu item should be disabled for texts");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
[DEFAULT]
|
||||
skip-if = buildapp == 'b2g'
|
||||
support-files =
|
||||
common.js
|
||||
data.json
|
||||
data.json^headers^
|
||||
network_requests_iframe.html
|
||||
sandboxed_iframe.html
|
||||
|
||||
[test_basics.html]
|
||||
[test_bug819670_getter_throws.html]
|
||||
[test_cached_messages.html]
|
||||
[test_consoleapi.html]
|
||||
[test_consoleapi_innerID.html]
|
||||
[test_file_uri.html]
|
||||
[test_reflow.html]
|
||||
[test_jsterm.html]
|
||||
[test_jsterm_cd_iframe.html]
|
||||
[test_jsterm_last_result.html]
|
||||
[test_network_get.html]
|
||||
[test_network_longstring.html]
|
||||
[test_network_post.html]
|
||||
[test_network_security-hpkp.html]
|
||||
[test_network_security-hsts.html]
|
||||
[test_nsiconsolemessage.html]
|
||||
[test_object_actor.html]
|
||||
[test_object_actor_native_getters.html]
|
||||
[test_object_actor_native_getters_lenient_this.html]
|
||||
[test_page_errors.html]
|
||||
[test_throw.html]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html dir="ltr" xml:lang="en-US" lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Click on function should point to source</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<script type="text/javascript" src="test-bug_1050691_click_function_to_source.js"></script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* this
|
||||
* is
|
||||
* a
|
||||
* function
|
||||
*/
|
||||
function foo() {
|
||||
console.log(foo);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf8">
|
||||
<title>Test for the $_ getter</title>
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript;version=1.8" src="common.js"></script>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
</head>
|
||||
<body>
|
||||
<p>Test for the $_ getter</p>
|
||||
|
||||
<iframe id="content-iframe" src="http://example.com/chrome/toolkit/devtools/webconsole/test/sandboxed_iframe.html"></iframe>
|
||||
|
||||
<script class="testbody" type="text/javascript;version=1.8">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
let gState;
|
||||
|
||||
function evaluateJS(input, callback) {
|
||||
return new Promise((resolve, reject) => {
|
||||
gState.client.evaluateJSAsync(input, response => {
|
||||
if (callback) {
|
||||
callback(response);
|
||||
}
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function startTest()
|
||||
{
|
||||
removeEventListener("load", startTest);
|
||||
attachConsole([], state => {
|
||||
gState = state;
|
||||
let tests = [checkUndefinedResult,checkAdditionResult,checkObjectResult];
|
||||
runTests(tests, testEnd);
|
||||
}, true);
|
||||
}
|
||||
|
||||
let checkUndefinedResult = Task.async(function*() {
|
||||
info ("$_ returns undefined if nothing has evaluated yet");
|
||||
let response = yield evaluateJS("$_");
|
||||
basicResultCheck(response, "$_", undefined);
|
||||
nextTest();
|
||||
});
|
||||
|
||||
let checkAdditionResult = Task.async(function*() {
|
||||
info ("$_ returns last value and performs basic arithmetic");
|
||||
let response = yield evaluateJS("2+2");
|
||||
basicResultCheck(response, "2+2", 4);
|
||||
|
||||
response = yield evaluateJS("$_");
|
||||
basicResultCheck(response, "$_", 4);
|
||||
|
||||
response = yield evaluateJS("$_ + 2");
|
||||
basicResultCheck(response, "$_ + 2", 6);
|
||||
|
||||
response = yield evaluateJS("$_ + 4");
|
||||
basicResultCheck(response, "$_ + 4", 10);
|
||||
|
||||
nextTest();
|
||||
});
|
||||
|
||||
let checkObjectResult = Task.async(function*() {
|
||||
info ("$_ has correct references to objects");
|
||||
|
||||
let response = yield evaluateJS("var foo = {bar:1}; foo;");
|
||||
basicResultCheck(response, "var foo = {bar:1}; foo;", {
|
||||
type: "object",
|
||||
class: "Object",
|
||||
actor: /[a-z]/,
|
||||
});
|
||||
checkObject(response.result.preview.ownProperties, {
|
||||
bar: {
|
||||
value: 1
|
||||
}
|
||||
});
|
||||
|
||||
response = yield evaluateJS("$_");
|
||||
basicResultCheck(response, "$_", {
|
||||
type: "object",
|
||||
class: "Object",
|
||||
actor: /[a-z]/,
|
||||
});
|
||||
checkObject(response.result.preview.ownProperties, {
|
||||
bar: {
|
||||
value: 1
|
||||
}
|
||||
});
|
||||
|
||||
top.foo.bar = 2;
|
||||
|
||||
response = yield evaluateJS("$_");
|
||||
basicResultCheck(response, "$_", {
|
||||
type: "object",
|
||||
class: "Object",
|
||||
actor: /[a-z]/,
|
||||
});
|
||||
checkObject(response.result.preview.ownProperties, {
|
||||
bar: {
|
||||
value: 2
|
||||
}
|
||||
});
|
||||
|
||||
nextTest();
|
||||
});
|
||||
|
||||
function basicResultCheck(response, input, output) {
|
||||
checkObject(response, {
|
||||
from: gState.actor,
|
||||
input: input,
|
||||
result: output,
|
||||
});
|
||||
ok(!response.exception, "no eval exception");
|
||||
ok(!response.helperResult, "no helper result");
|
||||
}
|
||||
|
||||
function testEnd()
|
||||
{
|
||||
closeDebugger(gState, function() {
|
||||
gState = null;
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
addEventListener("load", startTest);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1552,6 +1552,20 @@ function JSTermHelpers(aOwner)
|
||||
return aOwner.window.document.querySelectorAll(aSelector);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the result of the last console input evaluation
|
||||
*
|
||||
* @return object|undefined
|
||||
* Returns last console evaluation or undefined
|
||||
*/
|
||||
Object.defineProperty(aOwner.sandbox, "$_", {
|
||||
get: function() {
|
||||
return aOwner.consoleActor.getLastConsoleInputEvaluation();
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
/**
|
||||
* Runs an xPath query and returns all matched nodes.
|
||||
*
|
||||
|
||||
@@ -76,6 +76,8 @@ function goUpdateConsoleCommands() {
|
||||
<menuitem id="menu_copyURL" label="©URLCmd.label;"
|
||||
accesskey="©URLCmd.accesskey;" command="consoleCmd_copyURL"
|
||||
selection="network" selectionType="single"/>
|
||||
<menuitem id="menu_openInVarView" label="&openInVarViewCmd.label;"
|
||||
accesskey="&openInVarViewCmd.accesskey;" disabled="true"/>
|
||||
<menuitem id="cMenu_copy"/>
|
||||
<menuitem id="cMenu_selectAll"/>
|
||||
</menupopup>
|
||||
|
||||
@@ -214,9 +214,9 @@ breakpointMenuItem.deleteAll=Remove all breakpoints
|
||||
# yet.
|
||||
loadingText=Loading\u2026
|
||||
|
||||
# LOCALIZATION NOTE (errorLoadingText): The text that is displayed in the debugger
|
||||
# LOCALIZATION NOTE (errorLoadingText2): The text that is displayed in the debugger
|
||||
# viewer when there is an error loading a file
|
||||
errorLoadingText=Error loading source:\n
|
||||
errorLoadingText2=Error loading this URL: %S
|
||||
|
||||
# LOCALIZATION NOTE (addWatchExpressionText): The text that is displayed in the
|
||||
# watch expressions list to add a new item.
|
||||
@@ -320,4 +320,4 @@ variablesViewOptimizedOut=(optimized away)
|
||||
variablesViewUninitialized=(uninitialized)
|
||||
variablesViewMissingArgs=(unavailable)
|
||||
|
||||
evalGroupLabel=Evaluated Sources
|
||||
evalGroupLabel=Evaluated Sources
|
||||
|
||||
@@ -97,3 +97,5 @@
|
||||
<!ENTITY closeCmd.key "W">
|
||||
<!ENTITY findCmd.key "F">
|
||||
<!ENTITY clearOutputCtrl.key "L">
|
||||
<!ENTITY openInVarViewCmd.label "Open in Variables View">
|
||||
<!ENTITY openInVarViewCmd.accesskey "V">
|
||||
|
||||
@@ -22,9 +22,30 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Globals
|
||||
|
||||
// Do not load the FinalizationWitnessService is we are being required as a
|
||||
// CommonJS module, because the Components object is not available in workers.
|
||||
if (!isWorker) {
|
||||
// Obtain an instance of Cu. How this instance is obtained depends on how this
|
||||
// file is loaded.
|
||||
//
|
||||
// This file can be loaded in three different ways:
|
||||
// 1. As a CommonJS module, by Loader.jsm, on the main thread.
|
||||
// 2. As a CommonJS module, by worker-loader.js, on a worker thread.
|
||||
// 3. As a subscript, by Promise.jsm, on the main thread.
|
||||
//
|
||||
// If require is defined, the file is loaded as a CommonJS module. Components
|
||||
// will not be defined in that case, but we can obtain an instance of Cu from
|
||||
// the chrome module. Otherwise, this file is loaded as a subscript, and we can
|
||||
// obtain an instance of Cu from Components directly.
|
||||
//
|
||||
// If the file is loaded as a CommonJS module on a worker thread, the instance
|
||||
// of Cu obtained from the chrome module will be null. The reason for this is
|
||||
// that Components is not defined in worker threads, so no instance of Cu can
|
||||
// be obtained.
|
||||
|
||||
let Cu = this.require ? require("chrome").Cu : Components.utils;
|
||||
let Cc = this.require ? require("chrome").Cc : Components.classes;
|
||||
let Ci = this.require ? require("chrome").Ci : Components.interfaces;
|
||||
|
||||
// If Cu is defined, use it to lazily define the FinalizationWitnessService.
|
||||
if (Cu) {
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
@@ -46,7 +67,7 @@ const salt = Math.floor(Math.random() * 100);
|
||||
const N_INTERNALS = "{private:internals:" + salt + "}";
|
||||
|
||||
// We use DOM Promise for scheduling the walker loop.
|
||||
const DOMPromise = isWorker ? null : Promise;
|
||||
const DOMPromise = Cu ? Promise : null;
|
||||
|
||||
/////// Warn-upon-finalization mechanism
|
||||
//
|
||||
@@ -252,10 +273,9 @@ let PendingErrors = {
|
||||
}
|
||||
};
|
||||
|
||||
// Do not initialize the warn-on-finalization mechanism if we are being required
|
||||
// as a CommonJS module by the worker loader, because the Components object (and
|
||||
// therefore the FinalizationWitnessService) is not available.
|
||||
if (!isWorker) {
|
||||
// Initialize the warn-upon-finalization mechanism if and only if Cu is defined.
|
||||
// Otherwise, FinalizationWitnessService won't be defined (see above).
|
||||
if (Cu) {
|
||||
PendingErrors.init();
|
||||
}
|
||||
|
||||
@@ -628,9 +648,9 @@ Object.freeze(Promise.Debugging);
|
||||
|
||||
Object.freeze(Promise);
|
||||
|
||||
// Make sure to export the Promise object if we are being required as a CommonJS
|
||||
// module by the worker loader.
|
||||
if (isWorker) {
|
||||
// If module is defined, this file is loaded as a CommonJS module. Make sure
|
||||
// Promise is exported in that case.
|
||||
if (this.module) {
|
||||
module.exports = Promise;
|
||||
}
|
||||
|
||||
@@ -685,7 +705,7 @@ this.PromiseWalker = {
|
||||
aPromise[N_INTERNALS].value = aValue;
|
||||
if (aPromise[N_INTERNALS].handlers.length > 0) {
|
||||
this.schedulePromise(aPromise);
|
||||
} else if (!isWorker && aStatus == STATUS_REJECTED) {
|
||||
} else if (Cu && aStatus == STATUS_REJECTED) {
|
||||
// This is a rejection and the promise is the last in the chain.
|
||||
// For the time being we therefore have an uncaught error.
|
||||
let id = PendingErrors.register(aValue);
|
||||
@@ -701,10 +721,25 @@ this.PromiseWalker = {
|
||||
scheduleWalkerLoop: function()
|
||||
{
|
||||
this.walkerLoopScheduled = true;
|
||||
if (isWorker) {
|
||||
setImmediate(this.walkerLoop);
|
||||
} else {
|
||||
|
||||
// If this file is loaded on a worker thread, DOMPromise will not behave as
|
||||
// expected: because native promises are not aware of nested event loops
|
||||
// created by the debugger, their respective handlers will not be called
|
||||
// until after leaving the nested event loop. The debugger server relies
|
||||
// heavily on the use promises, so this could cause the debugger to hang.
|
||||
//
|
||||
// To work around this problem, any use of native promises in the debugger
|
||||
// server should be avoided when it is running on a worker thread. Because
|
||||
// it is still necessary to be able to schedule runnables on the event
|
||||
// queue, the worker loader defines the function setImmediate as a
|
||||
// per-module global for this purpose.
|
||||
//
|
||||
// If Cu is defined, this file is loaded on the main thread. Otherwise, it
|
||||
// is loaded on the worker thread.
|
||||
if (Cu) {
|
||||
DOMPromise.resolve().then(() => this.walkerLoop());
|
||||
} else {
|
||||
setImmediate(this.walkerLoop);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -96,17 +96,6 @@ this.Ci = Components.interfaces;
|
||||
this.Cu = Components.utils;
|
||||
this.Cr = Components.results;
|
||||
|
||||
// Promise-backend.js can either be loaded as a subscript by this file, or
|
||||
// required as a CommonJS module by the worker loader. Because certain APIS (in
|
||||
// particular, Components) are not available in workers, Promise-backend.js
|
||||
// behaves slightly different in the latter case.
|
||||
//
|
||||
// To distinguish between these two cases, the worker loader defines a global
|
||||
// variable isWorker, and sets it to true. When loading Promise-backend.js as
|
||||
// a subscript, we need to make sure this variable is defined as well, and set
|
||||
// it to false.
|
||||
this.isWorker = false;
|
||||
|
||||
this.Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(this.Ci.mozIJSSubScriptLoader)
|
||||
.loadSubScript("resource://gre/modules/Promise-backend.js", this);
|
||||
|
||||
Reference in New Issue
Block a user