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:
2021-04-20 09:17:23 +08:00
parent 7529dc68e8
commit a101af758e
52 changed files with 6204 additions and 2256 deletions
+4 -4
View File
@@ -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",
+1 -1
View File
@@ -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
+4 -1
View File
@@ -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,
+131 -1
View File
@@ -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.
*
+2 -1
View File
@@ -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);
+6 -1
View File
@@ -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>
+2 -1
View File
@@ -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");
+335
View File
@@ -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
+18 -6
View File
@@ -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
+3 -8
View File
@@ -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: {
+3 -264
View File
@@ -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
+36 -93
View File
@@ -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.
*
+2
View File
@@ -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) {
+44 -4
View File
@@ -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]
@@ -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);
}
@@ -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>
+14
View File
@@ -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="&copyURLCmd.label;"
accesskey="&copyURLCmd.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">
+50 -15
View File
@@ -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);
}
},
-11
View File
@@ -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);