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 (bug 1496025)
Backed out changeset c9a6fdac66bb (bug 1496025)
This commit is contained in:
Andreea Pavel 2018-10-08 18:07:26 +03:00
Родитель e84b2e0788
Коммит 43fb61451b
28 изменённых файлов: 1593 добавлений и 29 удалений

Просмотреть файл

@ -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;