зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 3 changesets (bug 1492265, bug 1496025
) for failing org.mozilla.geckoview.test.AccessibilityTest.testMoveByCharacter on a CLOSED TREE
Backed out changeset 9259cfe05c4e (bug 1492265) Backed out changeset 1292fd4850a7 (bug1496025
) Backed out changeset c9a6fdac66bb (bug1496025
)
This commit is contained in:
Родитель
e84b2e0788
Коммит
43fb61451b
|
@ -137,6 +137,18 @@ uses-unsafe-cpows = true
|
|||
[browser_dbg_progress-listener-bug.js]
|
||||
uses-unsafe-cpows = true
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_promises-allocation-stack.js]
|
||||
uses-unsafe-cpows = true
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_promises-chrome-allocation-stack.js]
|
||||
uses-unsafe-cpows = true
|
||||
skip-if = true # Bug 1177730
|
||||
[browser_dbg_promises-fulfillment-stack.js]
|
||||
uses-unsafe-cpows = true
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_promises-rejection-stack.js]
|
||||
uses-unsafe-cpows = true
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_sources-cache.js]
|
||||
uses-unsafe-cpows = true
|
||||
[browser_dbg_sources-iframe-reload.js]
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* 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 } = require("devtools/shared/fronts/promises");
|
||||
var EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
function test() {
|
||||
Task.spawn(function* () {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.registerAllActors();
|
||||
|
||||
let options = {
|
||||
source: TAB_URL,
|
||||
line: 1
|
||||
};
|
||||
const [ tab, panel ] = yield initDebugger(TAB_URL, options);
|
||||
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
yield connect(client);
|
||||
|
||||
let { tabs } = yield listTabs(client);
|
||||
let targetTab = findTab(tabs, TAB_URL);
|
||||
yield attachTarget(client, targetTab);
|
||||
|
||||
yield testGetAllocationStack(client, targetTab, tab);
|
||||
|
||||
yield close(client);
|
||||
yield closeDebuggerAndFinish(panel);
|
||||
}).catch(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 => {
|
||||
EventEmitter.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,100 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* 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 = require("devtools/shared/fronts/promises");
|
||||
var EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
const STACK_DATA = [
|
||||
{ functionDisplayName: "test/</<" },
|
||||
{ functionDisplayName: "testGetAllocationStack" },
|
||||
];
|
||||
|
||||
function test() {
|
||||
Task.spawn(function* () {
|
||||
requestLongerTimeout(10);
|
||||
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.registerAllActors();
|
||||
DebuggerServer.allowChromeProcess = true;
|
||||
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
yield connect(client);
|
||||
let chrome = yield client.getProcess();
|
||||
let [, tabClient] = yield attachTarget(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.catch(() => {});
|
||||
r.name = "r";
|
||||
});
|
||||
|
||||
yield close(client);
|
||||
finish();
|
||||
}).catch(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 => {
|
||||
EventEmitter.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();
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* 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 fulfillment point.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_promise-get-fulfillment-stack.html";
|
||||
const { PromisesFront } = require("devtools/shared/fronts/promises");
|
||||
var EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
const TEST_DATA = [
|
||||
{
|
||||
functionDisplayName: "returnPromise/<",
|
||||
line: 19,
|
||||
column: 37
|
||||
},
|
||||
{
|
||||
functionDisplayName: "returnPromise",
|
||||
line: 19,
|
||||
column: 14
|
||||
},
|
||||
{
|
||||
functionDisplayName: "makePromise",
|
||||
line: 14,
|
||||
column: 15
|
||||
},
|
||||
];
|
||||
|
||||
function test() {
|
||||
Task.spawn(function* () {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.registerAllActors();
|
||||
|
||||
let options = {
|
||||
source: TAB_URL,
|
||||
line: 1
|
||||
};
|
||||
const [ tab, panel ] = yield initDebugger(TAB_URL, options);
|
||||
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
yield connect(client);
|
||||
|
||||
let { tabs } = yield listTabs(client);
|
||||
let targetTab = findTab(tabs, TAB_URL);
|
||||
yield attachTarget(client, targetTab);
|
||||
|
||||
yield testGetFulfillmentStack(client, targetTab, tab);
|
||||
|
||||
yield close(client);
|
||||
yield closeDebuggerAndFinish(panel);
|
||||
}).catch(error => {
|
||||
ok(false, "Got an error: " + error.message + "\n" + error.stack);
|
||||
});
|
||||
}
|
||||
|
||||
function* testGetFulfillmentStack(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 => {
|
||||
EventEmitter.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, "makePromise");
|
||||
|
||||
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.getPromiseFulfillmentStack(response => {
|
||||
ok(response.fulfillmentStack.length, "Got promise allocation stack.");
|
||||
|
||||
for (let i = 0; i < TEST_DATA.length; i++) {
|
||||
let stack = response.fulfillmentStack[i];
|
||||
let data = TEST_DATA[i];
|
||||
is(stack.source.url, TAB_URL, "Got correct source URL.");
|
||||
is(stack.functionDisplayName, data.functionDisplayName,
|
||||
"Got correct function display name.");
|
||||
is(stack.line, data.line, "Got correct stack line number.");
|
||||
is(stack.column, data.column, "Got correct stack column number.");
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
yield front.detach();
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* 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 rejection point.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_promise-get-rejection-stack.html";
|
||||
const { PromisesFront } = require("devtools/shared/fronts/promises");
|
||||
var EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
// The code in the document above leaves an uncaught rejection. This is only
|
||||
// reported to the testing framework if the code is loaded in the main process.
|
||||
if (!gMultiProcessBrowser) {
|
||||
ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm", this);
|
||||
PromiseTestUtils.expectUncaughtRejection(/hello/);
|
||||
}
|
||||
|
||||
const TEST_DATA = [
|
||||
{
|
||||
functionDisplayName: "returnPromise/<",
|
||||
line: 19,
|
||||
column: 47
|
||||
},
|
||||
{
|
||||
functionDisplayName: "returnPromise",
|
||||
line: 19,
|
||||
column: 14
|
||||
},
|
||||
{
|
||||
functionDisplayName: "makePromise",
|
||||
line: 14,
|
||||
column: 15
|
||||
},
|
||||
];
|
||||
|
||||
function test() {
|
||||
Task.spawn(function* () {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.registerAllActors();
|
||||
|
||||
let options = {
|
||||
source: TAB_URL,
|
||||
line: 1
|
||||
};
|
||||
const [ tab, panel ] = yield initDebugger(TAB_URL, options);
|
||||
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
yield connect(client);
|
||||
|
||||
let { tabs } = yield listTabs(client);
|
||||
let targetTab = findTab(tabs, TAB_URL);
|
||||
yield attachTarget(client, targetTab);
|
||||
|
||||
yield testGetRejectionStack(client, targetTab, tab);
|
||||
|
||||
yield close(client);
|
||||
yield closeDebuggerAndFinish(panel);
|
||||
}).catch(error => {
|
||||
ok(false, "Got an error: " + error.message + "\n" + error.stack);
|
||||
});
|
||||
}
|
||||
|
||||
function* testGetRejectionStack(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 => {
|
||||
EventEmitter.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, "makePromise");
|
||||
|
||||
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.getPromiseRejectionStack(response => {
|
||||
ok(response.rejectionStack.length, "Got promise allocation stack.");
|
||||
|
||||
for (let i = 0; i < TEST_DATA.length; i++) {
|
||||
let stack = response.rejectionStack[i];
|
||||
let data = TEST_DATA[i];
|
||||
is(stack.source.url, TAB_URL, "Got correct source URL.");
|
||||
is(stack.functionDisplayName, data.functionDisplayName,
|
||||
"Got correct function display name.");
|
||||
is(stack.line, data.line, "Got correct stack line number.");
|
||||
is(stack.column, data.column, "Got correct stack column number.");
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
yield front.detach();
|
||||
}
|
|
@ -229,10 +229,8 @@ function TabTarget({ form, client, chrome, tab = null }) {
|
|||
} else {
|
||||
// browser content toolbox's form will be of the form:
|
||||
// server0.conn0.content-process0/contentProcessTarget7
|
||||
// while xpcshell debugging will be:
|
||||
// server1.conn0.contentProcessTarget7
|
||||
const isContentProcessTarget =
|
||||
this._form.actor.match(/conn\d+\.(content-process\d+\/)?contentProcessTarget\d+/);
|
||||
this._form.actor.match(/conn\d+\.content-process\d+\/contentProcessTarget\d+/);
|
||||
this._isBrowsingContext = !this.isLegacyAddon && !isContentProcessTarget;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ DevToolsModules(
|
|||
'preference.js',
|
||||
'pretty-print-worker.js',
|
||||
'process.js',
|
||||
'promises.js',
|
||||
'reflow.js',
|
||||
'root.js',
|
||||
'screenshot.js',
|
||||
|
|
|
@ -724,6 +724,141 @@ const proto = {
|
|||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a protocol request to get the list of dependent promises of a
|
||||
* promise.
|
||||
*
|
||||
* @return object
|
||||
* Returns an object containing an array of object grips of the
|
||||
* dependent promises
|
||||
*/
|
||||
dependentPromises: function() {
|
||||
if (this.obj.class != "Promise") {
|
||||
return this.throwError("objectNotPromise",
|
||||
"'dependentPromises' request is only valid for grips with a 'Promise' class.");
|
||||
}
|
||||
|
||||
const promises = this.obj.promiseDependentPromises
|
||||
.map(p => this.hooks.createValueGrip(p));
|
||||
|
||||
return { promises };
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a protocol request to get the allocation stack of a promise.
|
||||
*/
|
||||
allocationStack: function() {
|
||||
if (this.obj.class != "Promise") {
|
||||
return this.throwError("objectNotPromise",
|
||||
"'allocationStack' request is only valid for grips with a 'Promise' class.");
|
||||
}
|
||||
|
||||
let stack = this.obj.promiseAllocationSite;
|
||||
const allocationStacks = [];
|
||||
|
||||
while (stack) {
|
||||
if (stack.source) {
|
||||
const source = this._getSourceOriginalLocation(stack);
|
||||
|
||||
if (source) {
|
||||
allocationStacks.push(source);
|
||||
}
|
||||
}
|
||||
stack = stack.parent;
|
||||
}
|
||||
|
||||
return Promise.all(allocationStacks);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a protocol request to get the fulfillment stack of a promise.
|
||||
*/
|
||||
fulfillmentStack: function() {
|
||||
if (this.obj.class != "Promise") {
|
||||
return this.throwError("objectNotPromise",
|
||||
"'fulfillmentStack' request is only valid for grips with a 'Promise' class.");
|
||||
}
|
||||
|
||||
let stack = this.obj.promiseResolutionSite;
|
||||
const fulfillmentStacks = [];
|
||||
|
||||
while (stack) {
|
||||
if (stack.source) {
|
||||
const source = this._getSourceOriginalLocation(stack);
|
||||
|
||||
if (source) {
|
||||
fulfillmentStacks.push(source);
|
||||
}
|
||||
}
|
||||
stack = stack.parent;
|
||||
}
|
||||
|
||||
return Promise.all(fulfillmentStacks);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a protocol request to get the rejection stack of a promise.
|
||||
*/
|
||||
rejectionStack: function() {
|
||||
if (this.obj.class != "Promise") {
|
||||
return this.throwError("objectNotPromise",
|
||||
"'rejectionStack' request is only valid for grips with a 'Promise' class.");
|
||||
}
|
||||
|
||||
let stack = this.obj.promiseResolutionSite;
|
||||
const rejectionStacks = [];
|
||||
|
||||
while (stack) {
|
||||
if (stack.source) {
|
||||
const source = this._getSourceOriginalLocation(stack);
|
||||
|
||||
if (source) {
|
||||
rejectionStacks.push(source);
|
||||
}
|
||||
}
|
||||
stack = stack.parent;
|
||||
}
|
||||
|
||||
return Promise.all(rejectionStacks);
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper function for fetching the source location of a SavedFrame stack.
|
||||
*
|
||||
* @param SavedFrame stack
|
||||
* The promise allocation stack frame
|
||||
* @return object
|
||||
* Returns an object containing the source location of the SavedFrame
|
||||
* stack.
|
||||
*/
|
||||
_getSourceOriginalLocation: function(stack) {
|
||||
let source;
|
||||
|
||||
// Catch any errors if the source actor cannot be found
|
||||
try {
|
||||
source = this.hooks.sources().getSourceActorByURL(stack.source);
|
||||
} catch (e) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (!source) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.hooks.sources().getOriginalLocation(new GeneratedLocation(
|
||||
source,
|
||||
stack.line,
|
||||
stack.column
|
||||
)).then((originalLocation) => {
|
||||
return {
|
||||
source: originalLocation.originalSourceActor,
|
||||
line: originalLocation.originalLine,
|
||||
column: originalLocation.originalColumn,
|
||||
functionDisplayName: stack.functionDisplayName
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Release the actor, when it isn't needed anymore.
|
||||
* Protocol.js uses this release method to call the destroy method.
|
||||
|
|
|
@ -0,0 +1,200 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const protocol = require("devtools/shared/protocol");
|
||||
const { promisesSpec } = require("devtools/shared/specs/promises");
|
||||
const { expectState, ActorPool } = require("devtools/server/actors/common");
|
||||
const { ObjectActor } = require("devtools/server/actors/object");
|
||||
const { createValueGrip } = require("devtools/server/actors/object/utils");
|
||||
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
/**
|
||||
* The Promises Actor provides support for getting the list of live promises and
|
||||
* observing changes to their settlement state.
|
||||
*/
|
||||
var PromisesActor = protocol.ActorClassWithSpec(promisesSpec, {
|
||||
/**
|
||||
* @param conn DebuggerServerConnection.
|
||||
* @param parentActor BrowsingContextTargetActor|RootActor
|
||||
*/
|
||||
initialize: function(conn, parentActor) {
|
||||
protocol.Actor.prototype.initialize.call(this, conn);
|
||||
|
||||
this.conn = conn;
|
||||
this.parentActor = parentActor;
|
||||
this.state = "detached";
|
||||
this._dbg = null;
|
||||
this._gripDepth = 0;
|
||||
this._navigationLifetimePool = null;
|
||||
this._newPromises = null;
|
||||
this._promisesSettled = null;
|
||||
|
||||
this.objectGrip = this.objectGrip.bind(this);
|
||||
this._makePromiseEventHandler = this._makePromiseEventHandler.bind(this);
|
||||
this._onWindowReady = this._onWindowReady.bind(this);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this.state === "attached") {
|
||||
this.detach();
|
||||
}
|
||||
|
||||
protocol.Actor.prototype.destroy.call(this, this.conn);
|
||||
},
|
||||
|
||||
get dbg() {
|
||||
if (!this._dbg) {
|
||||
this._dbg = this.parentActor.makeDebugger();
|
||||
}
|
||||
return this._dbg;
|
||||
},
|
||||
|
||||
/**
|
||||
* Attach to the PromisesActor.
|
||||
*/
|
||||
attach: expectState("detached", function() {
|
||||
this.dbg.addDebuggees();
|
||||
|
||||
this._navigationLifetimePool = this._createActorPool();
|
||||
this.conn.addActorPool(this._navigationLifetimePool);
|
||||
|
||||
this._newPromises = [];
|
||||
this._promisesSettled = [];
|
||||
|
||||
this.dbg.findScripts().forEach(s => {
|
||||
this.parentActor.sources.createSourceActors(s.source);
|
||||
});
|
||||
|
||||
this.dbg.onNewScript = s => {
|
||||
this.parentActor.sources.createSourceActors(s.source);
|
||||
};
|
||||
|
||||
EventEmitter.on(this.parentActor, "window-ready", this._onWindowReady);
|
||||
|
||||
this.state = "attached";
|
||||
}, "attaching to the PromisesActor"),
|
||||
|
||||
/**
|
||||
* Detach from the PromisesActor upon Debugger closing.
|
||||
*/
|
||||
detach: expectState("attached", function() {
|
||||
this.dbg.removeAllDebuggees();
|
||||
this.dbg.enabled = false;
|
||||
this._dbg = null;
|
||||
this._newPromises = null;
|
||||
this._promisesSettled = null;
|
||||
|
||||
if (this._navigationLifetimePool) {
|
||||
this.conn.removeActorPool(this._navigationLifetimePool);
|
||||
this._navigationLifetimePool = null;
|
||||
}
|
||||
|
||||
EventEmitter.off(this.parentActor, "window-ready", this._onWindowReady);
|
||||
|
||||
this.state = "detached";
|
||||
}),
|
||||
|
||||
_createActorPool: function() {
|
||||
const pool = new ActorPool(this.conn);
|
||||
pool.objectActors = new WeakMap();
|
||||
return pool;
|
||||
},
|
||||
|
||||
/**
|
||||
* Create an ObjectActor for the given Promise object.
|
||||
*
|
||||
* @param object promise
|
||||
* The promise object
|
||||
* @return object
|
||||
* An ObjectActor object that wraps the given Promise object
|
||||
*/
|
||||
_createObjectActorForPromise: function(promise) {
|
||||
if (this._navigationLifetimePool.objectActors.has(promise)) {
|
||||
return this._navigationLifetimePool.objectActors.get(promise);
|
||||
}
|
||||
|
||||
const actor = new ObjectActor(promise, {
|
||||
getGripDepth: () => this._gripDepth,
|
||||
incrementGripDepth: () => this._gripDepth++,
|
||||
decrementGripDepth: () => this._gripDepth--,
|
||||
createValueGrip: v =>
|
||||
createValueGrip(v, this._navigationLifetimePool, this.objectGrip),
|
||||
sources: () => this.parentActor.sources,
|
||||
createEnvironmentActor: () => DevToolsUtils.reportException(
|
||||
"PromisesActor", Error("createEnvironmentActor not yet implemented")),
|
||||
getGlobalDebugObject: () => null,
|
||||
}, this.conn);
|
||||
|
||||
this._navigationLifetimePool.addActor(actor);
|
||||
this._navigationLifetimePool.objectActors.set(promise, actor);
|
||||
|
||||
return actor;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a grip for the given Promise object.
|
||||
*
|
||||
* @param object value
|
||||
* The Promise object
|
||||
* @return object
|
||||
* The grip for the given Promise object
|
||||
*/
|
||||
objectGrip: function(value) {
|
||||
return this._createObjectActorForPromise(value).form();
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a list of ObjectActors for all live Promise Objects.
|
||||
*/
|
||||
listPromises: function() {
|
||||
const promises = this.dbg.findObjects({ class: "Promise" });
|
||||
|
||||
this.dbg.onNewPromise = this._makePromiseEventHandler(this._newPromises,
|
||||
"new-promises");
|
||||
this.dbg.onPromiseSettled = this._makePromiseEventHandler(
|
||||
this._promisesSettled, "promises-settled");
|
||||
|
||||
return promises.map(p => this._createObjectActorForPromise(p));
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates an event handler for onNewPromise that will add the new
|
||||
* Promise ObjectActor to the array and schedule it to be emitted as a
|
||||
* batch for the provided event.
|
||||
*
|
||||
* @param array array
|
||||
* The list of Promise ObjectActors to emit
|
||||
* @param string eventName
|
||||
* The event name
|
||||
*/
|
||||
_makePromiseEventHandler: function(array, eventName) {
|
||||
return promise => {
|
||||
const actor = this._createObjectActorForPromise(promise);
|
||||
const needsScheduling = array.length == 0;
|
||||
|
||||
array.push(actor);
|
||||
|
||||
if (needsScheduling) {
|
||||
DevToolsUtils.executeSoon(() => {
|
||||
this.emit(eventName, array.splice(0, array.length));
|
||||
});
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
_onWindowReady: expectState("attached", function({ isTopLevel }) {
|
||||
if (!isTopLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._navigationLifetimePool.cleanup();
|
||||
this.dbg.removeAllDebuggees();
|
||||
this.dbg.addDebuggees();
|
||||
})
|
||||
});
|
||||
|
||||
exports.PromisesActor = PromisesActor;
|
|
@ -14,10 +14,6 @@ const { DebuggerServer } = require("devtools/server/main");
|
|||
|
||||
loader.lazyRequireGetter(this, "ChromeWindowTargetActor",
|
||||
"devtools/server/actors/targets/chrome-window", true);
|
||||
loader.lazyRequireGetter(this, "ContentProcessTargetActor",
|
||||
"devtools/server/actors/targets/content-process", true);
|
||||
loader.lazyRequireGetter(this, "ParentProcessTargetActor",
|
||||
"devtools/server/actors/targets/parent-process", true);
|
||||
|
||||
/* Root actor for the remote debugging protocol. */
|
||||
|
||||
|
@ -525,30 +521,16 @@ RootActor.prototype = {
|
|||
// If the request doesn't contains id parameter or id is 0
|
||||
// (id == 0, based on onListProcesses implementation)
|
||||
if ((!("id" in request)) || request.id === 0) {
|
||||
// Check if we are running on xpcshell. hiddenDOMWindow is going to throw on it.
|
||||
// When running on xpcshell, there is no valid browsing context to attach to
|
||||
// and so ParentProcessTargetActor doesn't make sense as it inherits from
|
||||
// BrowsingContextTargetActor. So instead use ContentProcessTargetActor, which
|
||||
// matches xpcshell needs.
|
||||
let isXpcshell = true;
|
||||
try {
|
||||
isXpcshell = !Services.wm.getMostRecentWindow(null) &&
|
||||
!Services.appShell.hiddenDOMWindow;
|
||||
} catch (e) {}
|
||||
|
||||
if (!isXpcshell && this._parentProcessTargetActor &&
|
||||
(!this._parentProcessTargetActor.docShell ||
|
||||
this._parentProcessTargetActor.docShell.isBeingDestroyed)) {
|
||||
if (this._parentProcessTargetActor && (!this._parentProcessTargetActor.docShell ||
|
||||
this._parentProcessTargetActor.docShell.isBeingDestroyed)) {
|
||||
this._parentProcessTargetActor.destroy();
|
||||
this._parentProcessTargetActor = null;
|
||||
}
|
||||
if (!this._parentProcessTargetActor) {
|
||||
// Create the target actor for the parent process
|
||||
if (isXpcshell) {
|
||||
this._parentProcessTargetActor = new ContentProcessTargetActor(this.conn);
|
||||
} else {
|
||||
this._parentProcessTargetActor = new ParentProcessTargetActor(this.conn);
|
||||
}
|
||||
// Create a ParentProcessTargetActor for the parent process
|
||||
const { ParentProcessTargetActor } =
|
||||
require("devtools/server/actors/targets/parent-process");
|
||||
this._parentProcessTargetActor = new ParentProcessTargetActor(this.conn);
|
||||
this._globalActorPool.manage(this._parentProcessTargetActor);
|
||||
}
|
||||
|
||||
|
|
|
@ -231,6 +231,11 @@ const ActorRegistry = {
|
|||
constructor: "AnimationsActor",
|
||||
type: { target: true }
|
||||
});
|
||||
this.registerModule("devtools/server/actors/promises", {
|
||||
prefix: "promises",
|
||||
constructor: "PromisesActor",
|
||||
type: { target: true }
|
||||
});
|
||||
this.registerModule("devtools/server/actors/emulation", {
|
||||
prefix: "emulation",
|
||||
constructor: "EmulationActor",
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Test that we can attach and detach to the PromisesActor under the correct
|
||||
* states.
|
||||
*/
|
||||
|
||||
const { PromisesFront } = require("devtools/shared/fronts/promises");
|
||||
|
||||
add_task(async function() {
|
||||
const client = await startTestDebuggerServer("promises-actor-test");
|
||||
const parentProcessActors = await getParentProcessActors(client);
|
||||
|
||||
// We have to attach the chrome target actor before playing with the PromiseActor
|
||||
await attachTarget(client, parentProcessActors);
|
||||
await testAttach(client, parentProcessActors);
|
||||
|
||||
const response = await listTabs(client);
|
||||
const targetTab = findTab(response.tabs, "promises-actor-test");
|
||||
ok(targetTab, "Found our target tab.");
|
||||
|
||||
await attachTarget(client, targetTab);
|
||||
await testAttach(client, targetTab);
|
||||
|
||||
await close(client);
|
||||
});
|
||||
|
||||
async function testAttach(client, parent) {
|
||||
const promises = PromisesFront(client, parent);
|
||||
|
||||
try {
|
||||
await promises.detach();
|
||||
ok(false, "Should not be able to detach when in a detached state.");
|
||||
} catch (e) {
|
||||
ok(true, "Expected detach to fail when already in a detached state.");
|
||||
}
|
||||
|
||||
await promises.attach();
|
||||
ok(true, "Expected attach to succeed.");
|
||||
|
||||
try {
|
||||
await promises.attach();
|
||||
ok(false, "Should not be able to attach when in an attached state.");
|
||||
} catch (e) {
|
||||
ok(true, "Expected attach to fail when already in an attached state.");
|
||||
}
|
||||
|
||||
await promises.detach();
|
||||
ok(true, "Expected detach to succeed.");
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* eslint-disable no-shadow */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Test that the PromisesActor exists in the BrowsingContextTargetActors and
|
||||
* ParentProcessTargetActors.
|
||||
*/
|
||||
|
||||
add_task(async function() {
|
||||
const client = await startTestDebuggerServer("promises-actor-test");
|
||||
|
||||
const response = await listTabs(client);
|
||||
const targetTab = findTab(response.tabs, "promises-actor-test");
|
||||
Assert.ok(targetTab, "Found our target tab.");
|
||||
|
||||
// Attach to the BrowsingContextTargetActor and check the response
|
||||
await new Promise(resolve => {
|
||||
client.request({ to: targetTab.actor, type: "attach" }, response => {
|
||||
Assert.ok(!("error" in response), "Expect no error in response.");
|
||||
Assert.equal(response.from, targetTab.actor,
|
||||
"Expect the target BrowsingContextTargetActor in response form field.");
|
||||
Assert.equal(response.type, "tabAttached",
|
||||
"Expect tabAttached in the response type.");
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
const parentProcessActors = await getParentProcessActors(client);
|
||||
Assert.ok(typeof parentProcessActors.promisesActor === "string",
|
||||
"Should have a chrome context PromisesActor.");
|
||||
});
|
|
@ -0,0 +1,65 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that we can get the list of all live Promise objects from the
|
||||
* PromisesActor.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const { PromisesFront } = require("devtools/shared/fronts/promises");
|
||||
const SECRET = "MyLittleSecret";
|
||||
|
||||
add_task(async function() {
|
||||
const client = await startTestDebuggerServer("promises-actor-test");
|
||||
const parentProcessActors = await getParentProcessActors(client);
|
||||
|
||||
// We have to attach the chrome target actor before playing with the PromiseActor
|
||||
await attachTarget(client, parentProcessActors);
|
||||
await testListPromises(client, parentProcessActors, v =>
|
||||
new Promise(resolve => resolve(v)));
|
||||
|
||||
const response = await listTabs(client);
|
||||
const targetTab = findTab(response.tabs, "promises-actor-test");
|
||||
ok(targetTab, "Found our target tab.");
|
||||
|
||||
await testListPromises(client, targetTab, v => {
|
||||
const debuggee = DebuggerServer.getTestGlobal("promises-actor-test");
|
||||
return debuggee.Promise.resolve(v);
|
||||
});
|
||||
|
||||
await close(client);
|
||||
});
|
||||
|
||||
async function testListPromises(client, form, makePromise) {
|
||||
const resolution = SECRET + Math.random();
|
||||
const promise = makePromise(resolution);
|
||||
const front = PromisesFront(client, form);
|
||||
|
||||
await front.attach();
|
||||
|
||||
const promises = await front.listPromises();
|
||||
|
||||
let found = false;
|
||||
for (const 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 !== "pending") {
|
||||
equal(typeof p.promiseState.timeToSettle, "number",
|
||||
"Expect time to settle to be a number");
|
||||
}
|
||||
|
||||
if (p.promiseState.state === "fulfilled" &&
|
||||
p.promiseState.value === resolution) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
ok(found, "Found our promise");
|
||||
await front.detach();
|
||||
// Appease eslint
|
||||
void promise;
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that we can get the list of all new Promise objects from the
|
||||
* PromisesActor onNewPromise event handler.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const { PromisesFront } = require("devtools/shared/fronts/promises");
|
||||
|
||||
var EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
add_task(async function() {
|
||||
const client = await startTestDebuggerServer("promises-actor-test");
|
||||
const parentProcessActors = await getParentProcessActors(client);
|
||||
|
||||
ok(Promise.toString().includes("native code"), "Expect native DOM Promise");
|
||||
|
||||
// We have to attach the chrome target actor before playing with the PromiseActor
|
||||
await attachTarget(client, parentProcessActors);
|
||||
await testNewPromisesEvent(client, parentProcessActors,
|
||||
v => new Promise(resolve => resolve(v)));
|
||||
|
||||
const response = await listTabs(client);
|
||||
const targetTab = findTab(response.tabs, "promises-actor-test");
|
||||
ok(targetTab, "Found our target tab.");
|
||||
|
||||
await testNewPromisesEvent(client, targetTab, v => {
|
||||
const debuggee = DebuggerServer.getTestGlobal("promises-actor-test");
|
||||
return debuggee.Promise.resolve(v);
|
||||
});
|
||||
|
||||
await close(client);
|
||||
});
|
||||
|
||||
async function testNewPromisesEvent(client, form, makePromise) {
|
||||
const front = PromisesFront(client, form);
|
||||
const resolution = "MyLittleSecret" + Math.random();
|
||||
let found = false;
|
||||
|
||||
await front.attach();
|
||||
await front.listPromises();
|
||||
|
||||
const onNewPromise = new Promise(resolve => {
|
||||
EventEmitter.on(front, "new-promises", promises => {
|
||||
for (const 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) {
|
||||
found = true;
|
||||
resolve();
|
||||
} else {
|
||||
dump("Found non-target promise\n");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const promise = makePromise(resolution);
|
||||
|
||||
await onNewPromise;
|
||||
ok(found, "Found our new promise");
|
||||
await front.detach();
|
||||
// Appease eslint
|
||||
void promise;
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/* 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";
|
||||
|
||||
ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm", this);
|
||||
|
||||
const { PromisesFront } = require("devtools/shared/fronts/promises");
|
||||
|
||||
var EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
add_task(async function() {
|
||||
const client = await startTestDebuggerServer("promises-actor-test");
|
||||
const parentProcessActors = await getParentProcessActors(client);
|
||||
|
||||
ok(Promise.toString().includes("native code"), "Expect native DOM Promise");
|
||||
|
||||
// We have to attach the chrome target actor before playing with the PromiseActor
|
||||
await attachTarget(client, parentProcessActors);
|
||||
await testPromisesSettled(client, parentProcessActors,
|
||||
v => new Promise(resolve => resolve(v)),
|
||||
v => new Promise((resolve, reject) => reject(v)));
|
||||
|
||||
const response = await listTabs(client);
|
||||
const targetTab = findTab(response.tabs, "promises-actor-test");
|
||||
ok(targetTab, "Found our target tab.");
|
||||
|
||||
await 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);
|
||||
});
|
||||
|
||||
await close(client);
|
||||
});
|
||||
|
||||
async function testPromisesSettled(client, form, makeResolvePromise,
|
||||
makeRejectPromise) {
|
||||
const front = PromisesFront(client, form);
|
||||
const resolution = "MyLittleSecret" + Math.random();
|
||||
|
||||
await front.attach();
|
||||
await front.listPromises();
|
||||
|
||||
let onPromiseSettled = oncePromiseSettled(front, resolution, true, false);
|
||||
const resolvedPromise = makeResolvePromise(resolution);
|
||||
const foundResolvedPromise = await onPromiseSettled;
|
||||
ok(foundResolvedPromise, "Found our resolved promise");
|
||||
|
||||
PromiseTestUtils.expectUncaughtRejection(r => r.message == resolution);
|
||||
onPromiseSettled = oncePromiseSettled(front, resolution, false, true);
|
||||
const rejectedPromise = makeRejectPromise(resolution);
|
||||
const foundRejectedPromise = await onPromiseSettled;
|
||||
ok(foundRejectedPromise, "Found our rejected promise");
|
||||
|
||||
await front.detach();
|
||||
// Appease eslint
|
||||
void resolvedPromise;
|
||||
void rejectedPromise;
|
||||
}
|
||||
|
||||
function oncePromiseSettled(front, resolution, resolveValue, rejectValue) {
|
||||
return new Promise(resolve => {
|
||||
EventEmitter.on(front, "promises-settled", promises => {
|
||||
for (const 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) {
|
||||
resolve(resolveValue);
|
||||
} else if (p.promiseState.state === "rejected" &&
|
||||
p.promiseState.reason === resolution) {
|
||||
resolve(rejectValue);
|
||||
} else {
|
||||
dump("Found non-target promise\n");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
/* 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 } = require("devtools/shared/fronts/promises");
|
||||
|
||||
var EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
add_task(async function() {
|
||||
const client = await startTestDebuggerServer("test-promises-dependentpromises");
|
||||
const parentProcessActors = await getParentProcessActors(client);
|
||||
await attachTarget(client, parentProcessActors);
|
||||
|
||||
ok(Promise.toString().includes("native code"), "Expect native DOM Promise.");
|
||||
|
||||
await testGetDependentPromises(client, parentProcessActors, () => {
|
||||
const p = new Promise(() => {});
|
||||
p.name = "p";
|
||||
const q = p.then();
|
||||
q.name = "q";
|
||||
const r = p.catch(() => {});
|
||||
r.name = "r";
|
||||
|
||||
return p;
|
||||
});
|
||||
|
||||
const response = await listTabs(client);
|
||||
const targetTab = findTab(response.tabs, "test-promises-dependentpromises");
|
||||
ok(targetTab, "Found our target tab.");
|
||||
await attachTarget(client, targetTab);
|
||||
|
||||
await testGetDependentPromises(client, targetTab, () => {
|
||||
const debuggee =
|
||||
DebuggerServer.getTestGlobal("test-promises-dependentpromises");
|
||||
|
||||
const p = new debuggee.Promise(() => {});
|
||||
p.name = "p";
|
||||
const q = p.then();
|
||||
q.name = "q";
|
||||
const r = p.catch(() => {});
|
||||
r.name = "r";
|
||||
|
||||
return p;
|
||||
});
|
||||
|
||||
await close(client);
|
||||
});
|
||||
|
||||
async function testGetDependentPromises(client, form, makePromises) {
|
||||
const front = PromisesFront(client, form);
|
||||
|
||||
await front.attach();
|
||||
await front.listPromises();
|
||||
|
||||
// Get the grip for promise p
|
||||
const onNewPromise = new Promise(resolve => {
|
||||
EventEmitter.on(front, "new-promises", promises => {
|
||||
for (const p of promises) {
|
||||
if (p.preview.ownProperties.name &&
|
||||
p.preview.ownProperties.name.value === "p") {
|
||||
resolve(p);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const promise = makePromises();
|
||||
|
||||
const grip = await onNewPromise;
|
||||
ok(grip, "Found our promise p.");
|
||||
|
||||
const 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
|
||||
await new Promise(resolve => {
|
||||
objectClient.getDependentPromises(response => {
|
||||
const dependentNames = response.promises.map(p =>
|
||||
p.preview.ownProperties.name.value);
|
||||
const 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 (const 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();
|
||||
});
|
||||
});
|
||||
|
||||
await front.detach();
|
||||
// Appease eslint
|
||||
void promise;
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/* 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 } = require("devtools/shared/fronts/promises");
|
||||
|
||||
var EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "Preferences",
|
||||
"resource://gre/modules/Preferences.jsm");
|
||||
|
||||
add_task(async function() {
|
||||
const timerPrecision = Preferences.get("privacy.reduceTimerPrecision");
|
||||
Preferences.set("privacy.reduceTimerPrecision", false);
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
Preferences.set("privacy.reduceTimerPrecision", timerPrecision);
|
||||
});
|
||||
|
||||
const client = await startTestDebuggerServer("promises-object-test");
|
||||
const parentProcessActors = await getParentProcessActors(client);
|
||||
|
||||
ok(Promise.toString().includes("native code"), "Expect native DOM Promise.");
|
||||
|
||||
// We have to attach the chrome target actor before playing with the PromiseActor
|
||||
await attachTarget(client, parentProcessActors);
|
||||
await testPromiseCreationTimestamp(client, parentProcessActors, v => {
|
||||
return new Promise(resolve => resolve(v));
|
||||
});
|
||||
|
||||
const response = await listTabs(client);
|
||||
const targetTab = findTab(response.tabs, "promises-object-test");
|
||||
ok(targetTab, "Found our target tab.");
|
||||
|
||||
await testPromiseCreationTimestamp(client, targetTab, v => {
|
||||
const debuggee = DebuggerServer.getTestGlobal("promises-object-test");
|
||||
return debuggee.Promise.resolve(v);
|
||||
});
|
||||
|
||||
await close(client);
|
||||
});
|
||||
|
||||
async function testPromiseCreationTimestamp(client, form, makePromise) {
|
||||
const front = PromisesFront(client, form);
|
||||
const resolution = "MyLittleSecret" + Math.random();
|
||||
|
||||
await front.attach();
|
||||
await front.listPromises();
|
||||
|
||||
const onNewPromise = new Promise(resolve => {
|
||||
EventEmitter.on(front, "new-promises", promises => {
|
||||
for (const p of promises) {
|
||||
if (p.promiseState.state === "fulfilled" &&
|
||||
p.promiseState.value === resolution) {
|
||||
resolve(p);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const start = Date.now();
|
||||
const promise = makePromise(resolution);
|
||||
const end = Date.now();
|
||||
|
||||
const grip = await onNewPromise;
|
||||
ok(grip, "Found our new promise.");
|
||||
|
||||
const creationTimestamp = grip.promiseState.creationTimestamp;
|
||||
|
||||
ok(start - 1 <= creationTimestamp && creationTimestamp <= end + 1,
|
||||
"Expect promise creation timestamp to be within elapsed time range: " +
|
||||
(start - 1) + " <= " + creationTimestamp + " <= " + (end + 1));
|
||||
|
||||
await front.detach();
|
||||
// Appease eslint
|
||||
void promise;
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/* 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 } = require("devtools/shared/fronts/promises");
|
||||
|
||||
var EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
add_task(async function() {
|
||||
const client = await startTestDebuggerServer("test-promises-timetosettle");
|
||||
const parentProcessActors = await getParentProcessActors(client);
|
||||
|
||||
ok(Promise.toString().includes("native code"), "Expect native DOM Promise.");
|
||||
|
||||
// We have to attach the chrome target actor before playing with the PromiseActor
|
||||
await attachTarget(client, parentProcessActors);
|
||||
await testGetTimeToSettle(client, parentProcessActors, () => {
|
||||
const p = new Promise(() => {});
|
||||
p.name = "p";
|
||||
const q = p.then();
|
||||
q.name = "q";
|
||||
|
||||
return p;
|
||||
});
|
||||
|
||||
const response = await listTabs(client);
|
||||
const targetTab = findTab(response.tabs, "test-promises-timetosettle");
|
||||
ok(targetTab, "Found our target tab.");
|
||||
|
||||
await testGetTimeToSettle(client, targetTab, () => {
|
||||
const debuggee =
|
||||
DebuggerServer.getTestGlobal("test-promises-timetosettle");
|
||||
|
||||
const p = new debuggee.Promise(() => {});
|
||||
p.name = "p";
|
||||
const q = p.then();
|
||||
q.name = "q";
|
||||
|
||||
return p;
|
||||
});
|
||||
|
||||
await close(client);
|
||||
});
|
||||
|
||||
async function testGetTimeToSettle(client, form, makePromises) {
|
||||
const front = PromisesFront(client, form);
|
||||
|
||||
await front.attach();
|
||||
await front.listPromises();
|
||||
|
||||
const onNewPromise = new Promise(resolve => {
|
||||
EventEmitter.on(front, "new-promises", promises => {
|
||||
for (const 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();
|
||||
});
|
||||
});
|
||||
|
||||
const promise = makePromises();
|
||||
|
||||
await onNewPromise;
|
||||
await front.detach();
|
||||
// Appease eslint
|
||||
void promise;
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* eslint-disable mozilla/no-arbitrary-setTimeout */
|
||||
|
||||
/**
|
||||
* Test that we get the expected settlement time for promise time to settle.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const { PromisesFront } = require("devtools/shared/fronts/promises");
|
||||
const { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm", {});
|
||||
|
||||
var EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
add_task(async function() {
|
||||
const client = await startTestDebuggerServer("test-promises-timetosettle");
|
||||
const parentProcessActors = await getParentProcessActors(client);
|
||||
await attachTarget(client, parentProcessActors);
|
||||
|
||||
ok(Promise.toString().includes("native code"), "Expect native DOM Promise.");
|
||||
|
||||
// We have to attach the chrome target actor before playing with the PromiseActor
|
||||
await attachTarget(client, parentProcessActors);
|
||||
await testGetTimeToSettle(client, parentProcessActors,
|
||||
v => new Promise(resolve => setTimeout(() => resolve(v), 100)));
|
||||
|
||||
const response = await listTabs(client);
|
||||
const targetTab = findTab(response.tabs, "test-promises-timetosettle");
|
||||
ok(targetTab, "Found our target tab.");
|
||||
await attachTarget(client, targetTab);
|
||||
|
||||
await testGetTimeToSettle(client, targetTab, v => {
|
||||
const debuggee =
|
||||
DebuggerServer.getTestGlobal("test-promises-timetosettle");
|
||||
return new debuggee.Promise(resolve => setTimeout(() => resolve(v), 100));
|
||||
});
|
||||
|
||||
await close(client);
|
||||
});
|
||||
|
||||
async function testGetTimeToSettle(client, form, makePromise) {
|
||||
const front = PromisesFront(client, form);
|
||||
const resolution = "MyLittleSecret" + Math.random();
|
||||
let found = false;
|
||||
|
||||
await front.attach();
|
||||
await front.listPromises();
|
||||
|
||||
const onNewPromise = new Promise(resolve => {
|
||||
EventEmitter.on(front, "promises-settled", promises => {
|
||||
for (const p of promises) {
|
||||
if (p.promiseState.state === "fulfilled" &&
|
||||
p.promiseState.value === resolution) {
|
||||
const timeToSettle = Math.floor(p.promiseState.timeToSettle / 100) * 100;
|
||||
ok(timeToSettle >= 100,
|
||||
"Expect time to settle for resolved promise to be " +
|
||||
"at least 100ms, got " + timeToSettle + "ms.");
|
||||
found = true;
|
||||
resolve();
|
||||
} else {
|
||||
dump("Found non-target promise.\n");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const promise = makePromise(resolution);
|
||||
|
||||
await onNewPromise;
|
||||
ok(found, "Found our new promise.");
|
||||
await front.detach();
|
||||
// Appease eslint
|
||||
void promise;
|
||||
}
|
|
@ -28,8 +28,9 @@ add_task(async function() {
|
|||
// Even though we have no tabs, getProcess gives us the chromeDebugger.
|
||||
const response = await client.getProcess();
|
||||
|
||||
const { chromeDebugger } = response.form;
|
||||
const [, threadClient] = await client.attachThread(chromeDebugger);
|
||||
const actor = response.form.actor;
|
||||
const [, tabClient] = await client.attachTarget(actor);
|
||||
const [, threadClient] = await tabClient.attachThread(null);
|
||||
const onResumed = new Promise(resolve => {
|
||||
threadClient.addOneTimeListener("paused", (event, packet) => {
|
||||
equal(packet.why.type, "breakpoint",
|
||||
|
|
|
@ -89,6 +89,16 @@ skip-if = (verify && !debug && (os == 'win'))
|
|||
[test_eval-04.js]
|
||||
[test_eval-05.js]
|
||||
[test_format_command.js]
|
||||
[test_promises_actor_attach.js]
|
||||
[test_promises_actor_exist.js]
|
||||
[test_promises_actor_list_promises.js]
|
||||
skip-if = coverage # bug 1336670
|
||||
[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]
|
||||
|
|
|
@ -242,6 +242,65 @@ ObjectClient.prototype = {
|
|||
return packet;
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* Request the promises directly depending on the current promise.
|
||||
*/
|
||||
getDependentPromises: DebuggerClient.requester({
|
||||
type: "dependentPromises"
|
||||
}, {
|
||||
before: function(packet) {
|
||||
if (this._grip.class !== "Promise") {
|
||||
throw new Error("getDependentPromises is only valid for promise " +
|
||||
"grips.");
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Request the stack to the promise's allocation point.
|
||||
*/
|
||||
getPromiseAllocationStack: DebuggerClient.requester({
|
||||
type: "allocationStack"
|
||||
}, {
|
||||
before: function(packet) {
|
||||
if (this._grip.class !== "Promise") {
|
||||
throw new Error("getAllocationStack is only valid for promise grips.");
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Request the stack to the promise's fulfillment point.
|
||||
*/
|
||||
getPromiseFulfillmentStack: DebuggerClient.requester({
|
||||
type: "fulfillmentStack"
|
||||
}, {
|
||||
before: function(packet) {
|
||||
if (this._grip.class !== "Promise") {
|
||||
throw new Error("getPromiseFulfillmentStack is only valid for " +
|
||||
"promise grips.");
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Request the stack to the promise's rejection point.
|
||||
*/
|
||||
getPromiseRejectionStack: DebuggerClient.requester({
|
||||
type: "rejectionStack"
|
||||
}, {
|
||||
before: function(packet) {
|
||||
if (this._grip.class !== "Promise") {
|
||||
throw new Error("getPromiseRejectionStack is only valid for " +
|
||||
"promise grips.");
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
module.exports = ObjectClient;
|
||||
|
|
|
@ -28,6 +28,7 @@ DevToolsModules(
|
|||
'performance-recording.js',
|
||||
'performance.js',
|
||||
'preference.js',
|
||||
'promises.js',
|
||||
'reflow.js',
|
||||
'screenshot.js',
|
||||
'storage.js',
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
Front,
|
||||
FrontClassWithSpec,
|
||||
} = require("devtools/shared/protocol");
|
||||
const { promisesSpec } = require("devtools/shared/specs/promises");
|
||||
|
||||
/**
|
||||
* PromisesFront, the front for the PromisesActor.
|
||||
*/
|
||||
const PromisesFront = FrontClassWithSpec(promisesSpec, {
|
||||
initialize: function(client, form) {
|
||||
Front.prototype.initialize.call(this, client, form);
|
||||
this.actorID = form.promisesActor;
|
||||
this.manage(this);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
Front.prototype.destroy.call(this);
|
||||
}
|
||||
});
|
||||
|
||||
exports.PromisesFront = PromisesFront;
|
|
@ -167,6 +167,11 @@ const Types = exports.__TypesForTests = [
|
|||
spec: "devtools/shared/specs/preference",
|
||||
front: "devtools/shared/fronts/preference",
|
||||
},
|
||||
{
|
||||
types: ["promises"],
|
||||
spec: "devtools/shared/specs/promises",
|
||||
front: "devtools/shared/fronts/promises",
|
||||
},
|
||||
{
|
||||
types: ["propertyIterator"],
|
||||
spec: "devtools/shared/specs/property-iterator",
|
||||
|
|
|
@ -38,6 +38,7 @@ DevToolsModules(
|
|||
'performance-recording.js',
|
||||
'performance.js',
|
||||
'preference.js',
|
||||
'promises.js',
|
||||
'property-iterator.js',
|
||||
'reflow.js',
|
||||
'screenshot.js',
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const {
|
||||
Arg,
|
||||
RetVal,
|
||||
generateActorSpec,
|
||||
types
|
||||
} = require("devtools/shared/protocol");
|
||||
|
||||
// Teach protocol.js how to deal with legacy actor types
|
||||
types.addType("ObjectActor", {
|
||||
write: actor => actor.form(),
|
||||
read: grip => grip
|
||||
});
|
||||
|
||||
const promisesSpec = generateActorSpec({
|
||||
typeName: "promises",
|
||||
|
||||
events: {
|
||||
// Event emitted for new promises allocated in debuggee and bufferred by
|
||||
// sending the list of promise objects in a batch.
|
||||
"new-promises": {
|
||||
type: "new-promises",
|
||||
data: Arg(0, "array:ObjectActor"),
|
||||
},
|
||||
// Event emitted for promise settlements.
|
||||
"promises-settled": {
|
||||
type: "promises-settled",
|
||||
data: Arg(0, "array:ObjectActor")
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
attach: {
|
||||
request: {},
|
||||
response: {},
|
||||
},
|
||||
|
||||
detach: {
|
||||
request: {},
|
||||
response: {},
|
||||
},
|
||||
|
||||
listPromises: {
|
||||
request: {},
|
||||
response: {
|
||||
promises: RetVal("array:ObjectActor"),
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
exports.promisesSpec = promisesSpec;
|
Загрузка…
Ссылка в новой задаче