Bug 1499070 - Fix timestamp for evaluation result; r=Honza.
When sending a command to the server, a timestamp is
computed before evaluating the string, and is then
sent back to the client in the packet.
However, if top-level await, or somme :commands, the
evaluation takes more time, which means the timestamp
is now innacurate.
For those cases, we update the timestamp before sending
the packet to the client.
Differential Revision:
https://phabricator.services.mozilla.com/D8734
--- a/devtools/client/webconsole/components/JSTerm.js
+++ b/devtools/client/webconsole/components/JSTerm.js
@@ -545,16 +545,17 @@ class JSTerm extends Component {
const inspectorSelection = this.hud.owner.getInspectorSelection();
if (inspectorSelection && inspectorSelection.nodeFront) {
selectedNodeActor = inspectorSelection.nodeFront.actorID;
}
const { ConsoleCommand } = require("devtools/client/webconsole/types");
const cmdMessage = new ConsoleCommand({
messageText: executeString,
+ timeStamp: Date.now(),
});
this.hud.proxy.dispatchMessageAdd(cmdMessage);
let mappedExpressionRes = null;
try {
mappedExpressionRes = await this.hud.owner.getMappedExpression(executeString);
} catch (e) {
console.warn("Error when calling getMappedExpression", e);
--- a/devtools/client/webconsole/components/message-types/ConsoleCommand.js
+++ b/devtools/client/webconsole/components/message-types/ConsoleCommand.js
@@ -30,26 +30,28 @@ function ConsoleCommand(props) {
} = props;
const {
indent,
source,
type,
level,
messageText,
+ timeStamp,
} = message;
// This uses a Custom Element to syntax highlight when possible. If it's not
// (no CodeMirror editor), then it will just render text.
const messageBody = createElement("syntax-highlighted", null, messageText);
return Message({
source,
type,
level,
topLevelClasses: [],
messageBody,
serviceContainer,
indent,
+ timeStamp,
timestampsVisible,
});
}
module.exports = ConsoleCommand;
--- a/devtools/client/webconsole/test/mochitest/browser_jsterm_await.js
+++ b/devtools/client/webconsole/test/mochitest/browser_jsterm_await.js
@@ -34,16 +34,27 @@ async function performTests() {
);
// Check that the resulting promise of the async iife is not displayed.
let messages = hud.ui.outputNode.querySelectorAll(".message .message-body");
let messagesText = Array.from(messages).map(n => n.textContent).join(" - ");
is(messagesText, `${simpleAwait} - Array [ "await1" ]`,
"The output contains the the expected messages");
+ // Check that the timestamp of the result is accurate
+ const {
+ visibleMessages,
+ messagesById
+ } = hud.ui.consoleOutput.getStore().getState().messages;
+ const [commandId, resultId] = visibleMessages;
+ const delta = messagesById.get(resultId).timeStamp -
+ messagesById.get(commandId).timeStamp;
+ ok(delta >= 500,
+ `The result has a timestamp at least 500ms (${delta}ms) older than the command`);
+
info("Check that assigning the result of a top-level await expression works");
await executeAndWaitForResultMessage(
`x = await new Promise(r => setTimeout(() => r("await2"), 500))`,
`await2`,
);
let message = await executeAndWaitForResultMessage(
`"-" + x + "-"`,
--- a/devtools/client/webconsole/types.js
+++ b/devtools/client/webconsole/types.js
@@ -17,16 +17,17 @@ exports.ConsoleCommand = function(props)
allowRepeating: false,
messageText: null,
source: MESSAGE_SOURCE.JAVASCRIPT,
type: MESSAGE_TYPE.COMMAND,
level: MESSAGE_LEVEL.LOG,
groupId: null,
indent: 0,
private: false,
+ timeStamp: null,
}, props);
};
exports.ConsoleMessage = function(props) {
return Object.assign({
id: null,
allowRepeating: true,
source: null,
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -971,21 +971,24 @@ WebConsoleActor.prototype =
* `resultID` field, and potentially a promise in the `helperResult` or in the
* `awaitResult` field.
*
* @return object
* The response packet to send to with the unique id in the
* `resultID` field, with a sanitized helperResult field.
*/
_waitForResultAndSend: async function(response) {
+ let updateTimestamp = false;
+
// Wait for asynchronous command completion before sending back the response
if (
response.helperResult && typeof response.helperResult.then == "function"
) {
response.helperResult = await response.helperResult;
+ updateTimestamp = true;
} else if (response.awaitResult && typeof response.awaitResult.then === "function") {
let result;
try {
result = await response.awaitResult;
} catch (e) {
// The promise was rejected. We let the engine handle this as it will report a
// `uncaught exception` error.
response.topLevelAwaitRejected = true;
@@ -995,16 +998,24 @@ WebConsoleActor.prototype =
// `createValueGrip` expect a debuggee value, while here we have the raw object.
// We need to call `makeDebuggeeValue` on it to make it work.
const dbgResult = this.makeDebuggeeValue(result);
response.result = this.createValueGrip(dbgResult);
}
// Remove the promise from the response object.
delete response.awaitResult;
+
+ updateTimestamp = true;
+ }
+
+ if (updateTimestamp) {
+ // We need to compute the timestamp again as the one in the response was set before
+ // doing the evaluation, which is now innacurate since we waited for a bit.
+ response.timestamp = Date.now();
}
// Finally, send an unsolicited evaluationResult packet with
// the normal return value
this.conn.sendActorEvent(this.actorID, "evaluationResult", response);
},
/**