Bug 1383711 - Make addOneTimeListener return a Promise; r=ochameau.
Switch some calls to addOneTimeListener from callback-style to Promise.
MozReview-Commit-ID: F9AlSvK0MAH
--- a/devtools/client/debugger/debugger-controller.js
+++ b/devtools/client/debugger/debugger-controller.js
@@ -961,50 +961,52 @@ StackFrames.prototype = {
addMoreFrames: function () {
this.activeThread.fillFrames(
this.activeThread.cachedFrames.length + CALL_STACK_PAGE_SIZE);
},
/**
* Evaluate an expression in the context of the selected frame.
*
- * @param string aExpression
+ * @param string expression
* The expression to evaluate.
- * @param object aOptions [optional]
+ * @param object options [optional]
* Additional options for this client evaluation:
* - depth: the frame depth used for evaluation, 0 being the topmost.
* - meta: some meta-description for what this evaluation represents.
* @return object
* A promise that is resolved when the evaluation finishes,
* or rejected if there was no stack frame available or some
* other error occurred.
*/
- evaluate: function (aExpression, aOptions = {}) {
- let depth = "depth" in aOptions ? aOptions.depth : this.currentFrameDepth;
+ evaluate: async function (expression, options = {}) {
+ let depth = "depth" in options
+ ? options.depth
+ : this.currentFrameDepth;
let frame = this.activeThread.cachedFrames[depth];
if (frame == null) {
- return promise.reject(new Error("No stack frame available."));
+ throw new Error("No stack frame available.");
}
- let deferred = promise.defer();
+ const onThreadPaused = this.activeThread.addOneTimeListener("paused");
+
+ let meta = "meta" in options
+ ? options.meta
+ : FRAME_TYPE.PUBLIC_CLIENT_EVAL;
+ this._currentFrameDescription = meta;
+ this.activeThread.eval(frame.actor, expression);
- this.activeThread.addOneTimeListener("paused", (aEvent, aPacket) => {
- let { type, frameFinished } = aPacket.why;
- if (type == "clientEvaluated") {
- deferred.resolve(frameFinished);
- } else {
- deferred.reject(new Error("Active thread paused unexpectedly."));
- }
- });
+ const packet = await onThreadPaused;
- let meta = "meta" in aOptions ? aOptions.meta : FRAME_TYPE.PUBLIC_CLIENT_EVAL;
- this._currentFrameDescription = meta;
- this.activeThread.eval(frame.actor, aExpression);
+ let { type, frameFinished } = packet.why;
+ if (type !== "clientEvaluated") {
+ throw new Error("Active thread paused unexpectedly.");
+ }
- return deferred.promise;
+ return frameFinished;
},
/**
* Add nodes for special frame references in the innermost scope.
*
* @param Scope aScope
* The scope where the references will be placed into.
* @param object aFrame
--- a/devtools/client/responsive.html/test/browser/head.js
+++ b/devtools/client/responsive.html/test/browser/head.js
@@ -332,24 +332,20 @@ function addDeviceForTest(device) {
addDevice(device);
registerCleanupFunction(() => {
// Note that assertions in cleanup functions are not displayed unless they failed.
ok(removeDevice(device), `Removed Test Device "${device.name}" from the list.`);
});
}
-function waitForClientClose(ui) {
- return new Promise(resolve => {
- info("Waiting for RDM debugger client to close");
- ui.client.addOneTimeListener("closed", () => {
- info("RDM's debugger client is now closed");
- resolve();
- });
- });
+async function waitForClientClose(ui) {
+ info("Waiting for RDM debugger client to close");
+ await ui.client.addOneTimeListener("closed");
+ info("RDM's debugger client is now closed");
}
function* testTouchEventsOverride(ui, expected) {
let { document } = ui.toolWindow;
let touchButton = document.querySelector("#global-touch-simulation-button");
let flag = yield ui.emulationFront.getTouchEventsOverride();
is(flag === Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_ENABLED, expected,
--- a/devtools/server/tests/unit/head_dbg.js
+++ b/devtools/server/tests/unit/head_dbg.js
@@ -658,32 +658,29 @@ const assert = do_check_true;
* Create a promise that is resolved on the next occurence of the given event.
*
* @param DebuggerClient client
* @param String event
* @param Function predicate
* @returns Promise
*/
function waitForEvent(client, type, predicate) {
+ if (!predicate) {
+ return client.addOneTimeListener(type);
+ }
+
return new Promise(function (resolve) {
function listener(type, packet) {
if (!predicate(packet)) {
return;
}
client.removeListener(listener);
resolve(packet);
}
-
- if (predicate) {
- client.addListener(type, listener);
- } else {
- client.addOneTimeListener(type, function (type, packet) {
- resolve(packet);
- });
- }
+ client.addListener(type, listener);
});
}
/**
* Execute the action on the next tick and return a promise that is resolved on
* the next pause.
*
* When using promises and Task.jsm, we often want to do an action that causes a
--- a/devtools/shared/client/event-source.js
+++ b/devtools/shared/client/event-source.js
@@ -43,23 +43,30 @@ function eventSource(proto) {
/**
* Add a listener to the event source for a given event. The
* listener will be removed after it is called for the first time.
*
* @param name string
* The event to listen for.
* @param listener function
* Called when the event is fired.
+ * @returns Promise
+ * Resolved with an array of the arguments of the event.
*/
proto.addOneTimeListener = function (name, listener) {
- let l = (...args) => {
- this.removeListener(name, l);
- listener.apply(null, args);
- };
- this.addListener(name, l);
+ return new Promise(resolve => {
+ let l = (eventName, ...rest) => {
+ this.removeListener(name, l);
+ if (listener) {
+ listener(eventName, ...rest);
+ }
+ resolve(rest[0]);
+ };
+ this.addListener(name, l);
+ });
};
/**
* Remove a listener from the event source previously added with
* addListener().
*
* @param name string
* The event name used during addListener to add the listener.
--- a/devtools/shared/webconsole/test/test_console_assert.html
+++ b/devtools/shared/webconsole/test/test_console_assert.html
@@ -15,22 +15,17 @@ window.onload = async function () {
let state;
try {
state = await new Promise(resolve =>
attachConsole(["ConsoleAPI"], resolve)
);
const {dbgClient} = state;
const consoleAPICall = consoleCall => {
- const onConsoleAPICall = new Promise((resolve, reject) => {
- dbgClient.addOneTimeListener(
- "consoleAPICall",
- (type, packet) => resolve({type, packet})
- );
- });
+ const onConsoleAPICall = dbgClient.addOneTimeListener("consoleAPICall");
consoleCall();
return onConsoleAPICall;
};
await testFalseAssert(consoleAPICall);
await testFalsyAssert(consoleAPICall);
await testUndefinedAssert(consoleAPICall);
await testNullAssert(consoleAPICall);
@@ -40,47 +35,47 @@ window.onload = async function () {
ok(false, `Error thrown: ${e.message}`);
}
closeDebugger(state, () => SimpleTest.finish());
};
async function testFalseAssert(consoleAPICall) {
info(`Testing console.assert(false)`);
- let {packet} = await consoleAPICall(() =>
+ const packet = await consoleAPICall(() =>
top.console.assert(false, "assertion is false"));
checkConsoleAPICall(packet.message, {
arguments: ["assertion is false"]
});
}
async function testFalsyAssert(consoleAPICall) {
info(`Testing console.assert(0")`);
- let {packet} = await consoleAPICall(() =>
+ const packet = await consoleAPICall(() =>
top.console.assert(0, "assertion is false"));
checkConsoleAPICall(packet.message, {
arguments: ["assertion is false"]
});
}
async function testUndefinedAssert(consoleAPICall) {
info(`Testing console.assert(undefined)`);
- let {packet} = await consoleAPICall(() =>
+ const packet = await consoleAPICall(() =>
top.console.assert(undefined, "assertion is false"));
checkConsoleAPICall(packet.message, {
arguments: ["assertion is false"]
});
}
async function testNullAssert(consoleAPICall) {
info(`Testing console.assert(null)`);
- let {packet} = await consoleAPICall(() =>
+ const packet = await consoleAPICall(() =>
top.console.assert(null, "assertion is false"));
checkConsoleAPICall(packet.message, {
arguments: ["assertion is false"]
});
}
async function testTrueAssert(consoleAPICall) {
--- a/devtools/shared/webconsole/test/test_console_group_styling.html
+++ b/devtools/shared/webconsole/test/test_console_group_styling.html
@@ -33,99 +33,94 @@ window.onload = async function () {
ok(false, `Error thrown: ${e.message}`);
}
closeDebugger(state, () => SimpleTest.finish());
};
async function testSingleCustomStyleGroup(debuggerClient) {
info("Testing console.group with a custom style");
- let {packet} = await consoleAPICall(
+ let packet = await consoleAPICall(
debuggerClient,
() => top.console.group("%cfoobar", "color:red")
);
checkConsoleAPICall(packet.message, {
arguments: ["foobar"],
styles: ["color:red"]
});
}
async function testSingleCustomStyleGroupCollapsed(debuggerClient) {
info("Testing console.groupCollapsed with a custom style");
- let {packet} = await consoleAPICall(
+ let packet = await consoleAPICall(
debuggerClient,
() => top.console.groupCollapsed("%cfoobaz", "color:blue")
);
checkConsoleAPICall(packet.message, {
arguments: ["foobaz"],
styles: ["color:blue"]
});
}
async function testMultipleCustomStyleGroup(debuggerClient) {
info("Testing console.group with multiple custom styles");
- let {packet} = await consoleAPICall(
+ let packet = await consoleAPICall(
debuggerClient,
() => top.console.group("%cfoo%cbar", "color:red", "color:blue")
);
checkConsoleAPICall(packet.message, {
arguments: ["foo", "bar"],
styles: ["color:red", "color:blue"]
});
}
async function testMultipleCustomStyleGroupCollapsed(debuggerClient) {
info("Testing console.groupCollapsed with multiple custom styles");
- let {packet} = await consoleAPICall(
+ let packet = await consoleAPICall(
debuggerClient,
() => top.console.group("%cfoo%cbaz", "color:red", "color:green")
);
checkConsoleAPICall(packet.message, {
arguments: ["foo", "baz"],
styles: ["color:red", "color:green"]
});
}
async function testFormatterWithNoStyleGroup(debuggerClient) {
info("Testing console.group with one formatter but no style");
- let {packet} = await consoleAPICall(
+ let packet = await consoleAPICall(
debuggerClient,
() => top.console.group("%cfoobar")
);
checkConsoleAPICall(packet.message, {
arguments: ["%cfoobar"],
styles: []
});
}
async function testFormatterWithNoStyleGroupCollapsed(debuggerClient) {
info("Testing console.groupCollapsed with one formatter but no style");
- let {packet} = await consoleAPICall(
+ let packet = await consoleAPICall(
debuggerClient,
() => top.console.groupCollapsed("%cfoobaz")
);
checkConsoleAPICall(packet.message, {
arguments: ["%cfoobaz"],
styles: []
});
}
function consoleAPICall(debuggerClient, consoleCall) {
- const onConsoleAPICall = new Promise((resolve, reject) => {
- debuggerClient.addOneTimeListener(
- "consoleAPICall",
- (type, packet) => resolve({type, packet})
- );
- });
+ const onConsoleAPICall = debuggerClient.addOneTimeListener("consoleAPICall");
consoleCall();
return onConsoleAPICall;
}
</script>
</head>
<body>
<p id="display"></p>