forked from mirrors/gecko-dev
It was wrong to both: * reuse disableBreaks options to add the useInnerBindings as it would prevent overrides for eager evaluation, * use disableBreaks for eager evaluation. It sounds safer to let the server control all the server side details and only pass eager attribute when evaluating js via the console actor. Differential Revision: https://phabricator.services.mozilla.com/D195097
391 lines
12 KiB
JavaScript
391 lines
12 KiB
JavaScript
/* 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 TEST_URI = `data:text/html;charset=utf-8,<!DOCTYPE html>
|
|
<script>
|
|
let x = 3, y = 4;
|
|
function zzyzx() {
|
|
x = 10;
|
|
}
|
|
function zzyzx2() {
|
|
x = 10;
|
|
}
|
|
var obj = {propA: "A", propB: "B"};
|
|
var array = [1, 2, 3];
|
|
var $$ = 42;
|
|
</script>
|
|
<h1>title</h1>
|
|
`;
|
|
|
|
const EAGER_EVALUATION_PREF = "devtools.webconsole.input.eagerEvaluation";
|
|
|
|
// Basic testing of eager evaluation functionality. Expressions which can be
|
|
// eagerly evaluated should show their results, and expressions with side
|
|
// effects should not perform those side effects.
|
|
add_task(async function () {
|
|
// Open the inspector first to select a node, so that we can later test "$0"
|
|
const toolbox = await openNewTabAndToolbox(TEST_URI, "inspector");
|
|
await selectNodeWithPicker(toolbox, "h1");
|
|
|
|
info("Picker mode stopped, <h1> selected, now switching to the console");
|
|
const hud = await openConsole();
|
|
|
|
// Do an evaluation to populate $_
|
|
await executeAndWaitForResultMessage(
|
|
hud,
|
|
"'result: ' + (x + y)",
|
|
"result: 7"
|
|
);
|
|
|
|
setInputValue(hud, "x + y");
|
|
await waitForEagerEvaluationResult(hud, "7");
|
|
|
|
setInputValue(hud, "x + y + undefined");
|
|
await waitForEagerEvaluationResult(hud, "NaN");
|
|
|
|
setInputValue(hud, "1 - 1");
|
|
await waitForEagerEvaluationResult(hud, "0");
|
|
|
|
setInputValue(hud, "!true");
|
|
await waitForEagerEvaluationResult(hud, "false");
|
|
|
|
setInputValue(hud, `"ab".slice(0, 0)`);
|
|
await waitForEagerEvaluationResult(hud, `""`);
|
|
|
|
setInputValue(hud, `JSON.parse("null")`);
|
|
await waitForEagerEvaluationResult(hud, "null");
|
|
|
|
setInputValue(hud, "-x / 0");
|
|
await waitForEagerEvaluationResult(hud, "-Infinity");
|
|
|
|
setInputValue(hud, "x = 10");
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
setInputValue(hud, "x + 1");
|
|
await waitForEagerEvaluationResult(hud, "4");
|
|
|
|
setInputValue(hud, "zzyzx()");
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
setInputValue(hud, "x + 2");
|
|
await waitForEagerEvaluationResult(hud, "5");
|
|
|
|
setInputValue(hud, "x +");
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
setInputValue(hud, "x + z");
|
|
await waitForEagerEvaluationResult(hud, /ReferenceError/);
|
|
|
|
setInputValue(hud, "var a = 5");
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
setInputValue(hud, "x + a");
|
|
await waitForEagerEvaluationResult(hud, /ReferenceError/);
|
|
|
|
setInputValue(hud, '"foobar".slice(1, 5)');
|
|
await waitForEagerEvaluationResult(hud, '"ooba"');
|
|
|
|
setInputValue(hud, '"foobar".toString()');
|
|
await waitForEagerEvaluationResult(hud, '"foobar"');
|
|
|
|
setInputValue(hud, "(new Array()).push(3)");
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
setInputValue(hud, "(new Uint32Array([1,2,3])).includes(2)");
|
|
await waitForEagerEvaluationResult(hud, "true");
|
|
|
|
setInputValue(hud, "Math.round(3.2)");
|
|
await waitForEagerEvaluationResult(hud, "3");
|
|
|
|
info("Check web console commands");
|
|
setInputValue(hud, "help()");
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
setInputValue(hud, "$0");
|
|
await waitForEagerEvaluationResult(hud, `<h1>`);
|
|
|
|
setInputValue(hud, "$('html')");
|
|
await waitForEagerEvaluationResult(hud, `<html>`);
|
|
|
|
setInputValue(hud, "$$");
|
|
await waitForEagerEvaluationResult(hud, `42`);
|
|
|
|
info("Check that $_ wasn't polluted by eager evaluations");
|
|
setInputValue(hud, "$_");
|
|
await waitForEagerEvaluationResult(hud, `"result: 7"`);
|
|
|
|
setInputValue(hud, "'> ' + $_");
|
|
await waitForEagerEvaluationResult(hud, `"> result: 7"`);
|
|
|
|
info("Switch to editor mode");
|
|
await toggleLayout(hud);
|
|
await waitForEagerEvaluationResult(hud, `"> result: 7"`);
|
|
ok(true, "eager evaluation is still displayed in editor mode");
|
|
|
|
setInputValue(hud, "4 + 7");
|
|
await waitForEagerEvaluationResult(hud, "11");
|
|
|
|
// go back to inline layout.
|
|
await toggleLayout(hud);
|
|
|
|
setInputValue(hud, "typeof new Proxy({}, {})");
|
|
await waitForEagerEvaluationResult(hud, `"object"`);
|
|
|
|
setInputValue(hud, "typeof Proxy.revocable({}, {}).revoke");
|
|
await waitForEagerEvaluationResult(hud, `"function"`);
|
|
|
|
setInputValue(hud, "Reflect.apply(() => 1, null, [])");
|
|
await waitForEagerEvaluationResult(hud, "1");
|
|
setInputValue(
|
|
hud,
|
|
`Reflect.apply(() => {
|
|
globalThis.sideEffect = true;
|
|
return 2;
|
|
}, null, [])`
|
|
);
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
setInputValue(hud, "Reflect.construct(Array, []).length");
|
|
await waitForEagerEvaluationResult(hud, "0");
|
|
setInputValue(
|
|
hud,
|
|
`Reflect.construct(function() {
|
|
globalThis.sideEffect = true;
|
|
}, [])`
|
|
);
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
setInputValue(hud, "Reflect.defineProperty({}, 'a', {value: 1})");
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
setInputValue(hud, "Reflect.deleteProperty({a: 1}, 'a')");
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
setInputValue(hud, "Reflect.get({a: 1}, 'a')");
|
|
await waitForEagerEvaluationResult(hud, "1");
|
|
setInputValue(hud, "Reflect.get({get a(){return 2}, 'a')");
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
setInputValue(hud, "Reflect.getOwnPropertyDescriptor({a: 1}, 'a').value");
|
|
await waitForEagerEvaluationResult(hud, "1");
|
|
setInputValue(
|
|
hud,
|
|
`Reflect.getOwnPropertyDescriptor(
|
|
new Proxy({ a: 2 }, { getOwnPropertyDescriptor() {
|
|
globalThis.sideEffect = true;
|
|
return { value: 2 };
|
|
}}),
|
|
"a"
|
|
)`
|
|
);
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
setInputValue(hud, "Reflect.getPrototypeOf({}) === Object.prototype");
|
|
await waitForEagerEvaluationResult(hud, "true");
|
|
setInputValue(
|
|
hud,
|
|
`Reflect.getPrototypeOf(
|
|
new Proxy({}, { getPrototypeOf() {
|
|
globalThis.sideEffect = true;
|
|
return null;
|
|
}})
|
|
)`
|
|
);
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
setInputValue(hud, "Reflect.has({a: 1}, 'a')");
|
|
await waitForEagerEvaluationResult(hud, "true");
|
|
setInputValue(
|
|
hud,
|
|
`Reflect.has(
|
|
new Proxy({ a: 2 }, { has() {
|
|
globalThis.sideEffect = true;
|
|
return true;
|
|
}}), "a"
|
|
)`
|
|
);
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
setInputValue(hud, "Reflect.isExtensible({})");
|
|
await waitForEagerEvaluationResult(hud, "true");
|
|
setInputValue(
|
|
hud,
|
|
`Reflect.isExtensible(
|
|
new Proxy({}, { isExtensible() {
|
|
globalThis.sideEffect = true;
|
|
return true;
|
|
}})
|
|
)`
|
|
);
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
setInputValue(hud, "Reflect.ownKeys({a: 1})[0]");
|
|
await waitForEagerEvaluationResult(hud, `"a"`);
|
|
setInputValue(
|
|
hud,
|
|
`Reflect.ownKeys(
|
|
new Proxy({}, { ownKeys() {
|
|
globalThis.sideEffect = true;
|
|
return ['a'];
|
|
}})
|
|
)`
|
|
);
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
setInputValue(hud, "Reflect.preventExtensions({})");
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
setInputValue(hud, "Reflect.set({}, 'a', 1)");
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
setInputValue(hud, "Reflect.setPrototypeOf({}, null)");
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
setInputValue(hud, "[] instanceof Array");
|
|
await waitForEagerEvaluationResult(hud, "true");
|
|
|
|
setInputValue(hud, "Int8Array.from({length: 1})[0]");
|
|
await waitForEagerEvaluationResult(hud, "0");
|
|
|
|
setInputValue(hud, "Float64Array.of(1)[0]");
|
|
await waitForEagerEvaluationResult(hud, "1");
|
|
|
|
setInputValue(hud, "array.fill()");
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
setInputValue(hud, "array");
|
|
await waitForEagerEvaluationResult(hud, "Array(3) [ 1, 2, 3 ]");
|
|
|
|
info("Check that top-level await expression are not evaluated");
|
|
setInputValue(hud, "await 1; 2 + 3;");
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
ok(true, "instant evaluation is disabled for top-level await expressions");
|
|
});
|
|
|
|
// Test that the currently selected autocomplete result is eagerly evaluated.
|
|
add_task(async function () {
|
|
const hud = await openNewTabAndConsole(TEST_URI);
|
|
const { jsterm } = hud;
|
|
|
|
const { autocompletePopup: popup } = jsterm;
|
|
|
|
ok(!popup.isOpen, "popup is not open");
|
|
let onPopupOpen = popup.once("popup-opened");
|
|
EventUtils.sendString("zzy");
|
|
await onPopupOpen;
|
|
|
|
await waitForEagerEvaluationResult(hud, "function zzyzx()");
|
|
EventUtils.synthesizeKey("KEY_ArrowDown");
|
|
await waitForEagerEvaluationResult(hud, "function zzyzx2()");
|
|
|
|
// works when the input isn't properly cased but matches an autocomplete item
|
|
setInputValue(hud, "o");
|
|
onPopupOpen = popup.once("popup-opened");
|
|
EventUtils.sendString("B");
|
|
await waitForEagerEvaluationResult(hud, `Object { propA: "A", propB: "B" }`);
|
|
|
|
// works when doing element access without quotes
|
|
setInputValue(hud, "obj[p");
|
|
onPopupOpen = popup.once("popup-opened");
|
|
EventUtils.sendString("RoP");
|
|
await waitForEagerEvaluationResult(hud, `"A"`);
|
|
|
|
EventUtils.synthesizeKey("KEY_ArrowDown");
|
|
await waitForEagerEvaluationResult(hud, `"B"`);
|
|
|
|
// closing the autocomplete popup updates the eager evaluation result
|
|
let onPopupClose = popup.once("popup-closed");
|
|
EventUtils.synthesizeKey("KEY_Escape");
|
|
await onPopupClose;
|
|
await waitForNoEagerEvaluationResult(hud);
|
|
|
|
info(
|
|
"Check that closing the popup by adding a space will update the instant eval result"
|
|
);
|
|
await setInputValueForAutocompletion(hud, "x");
|
|
await waitForEagerEvaluationResult(hud, "3");
|
|
|
|
EventUtils.synthesizeKey("KEY_ArrowDown");
|
|
// Navigates to the XMLDocument item in the popup
|
|
await waitForEagerEvaluationResult(hud, `function ()`);
|
|
|
|
onPopupClose = popup.once("popup-closed");
|
|
EventUtils.sendString(" ");
|
|
await waitForEagerEvaluationResult(hud, `3`);
|
|
});
|
|
|
|
// Test that the setting works as expected.
|
|
add_task(async function () {
|
|
// start with the pref off.
|
|
await pushPref(EAGER_EVALUATION_PREF, false);
|
|
const hud = await openNewTabAndConsole(TEST_URI);
|
|
|
|
info("Check that the setting is disabled");
|
|
checkConsoleSettingState(
|
|
hud,
|
|
".webconsole-console-settings-menu-item-eager-evaluation",
|
|
false
|
|
);
|
|
|
|
// Wait for the autocomplete popup to be displayed so we know the eager evaluation could
|
|
// have occured.
|
|
const onPopupOpen = hud.jsterm.autocompletePopup.once("popup-opened");
|
|
await setInputValueForAutocompletion(hud, "x + y");
|
|
await onPopupOpen;
|
|
|
|
is(
|
|
getEagerEvaluationElement(hud),
|
|
null,
|
|
"There's no eager evaluation element"
|
|
);
|
|
hud.jsterm.autocompletePopup.hidePopup();
|
|
|
|
info("Turn on the eager evaluation");
|
|
toggleConsoleSetting(
|
|
hud,
|
|
".webconsole-console-settings-menu-item-eager-evaluation"
|
|
);
|
|
await waitFor(() => getEagerEvaluationElement(hud));
|
|
ok(true, "The eager evaluation element is now displayed");
|
|
is(
|
|
Services.prefs.getBoolPref(EAGER_EVALUATION_PREF),
|
|
true,
|
|
"Pref was changed"
|
|
);
|
|
|
|
setInputValue(hud, "1 + 2");
|
|
await waitForEagerEvaluationResult(hud, "3");
|
|
ok(true, "Eager evaluation result is displayed");
|
|
|
|
info("Turn off the eager evaluation");
|
|
toggleConsoleSetting(
|
|
hud,
|
|
".webconsole-console-settings-menu-item-eager-evaluation"
|
|
);
|
|
await waitFor(() => !getEagerEvaluationElement(hud));
|
|
is(
|
|
Services.prefs.getBoolPref(EAGER_EVALUATION_PREF),
|
|
false,
|
|
"Pref was changed"
|
|
);
|
|
ok(true, "Eager evaluation element is no longer displayed");
|
|
|
|
// reset the preference
|
|
await pushPref(EAGER_EVALUATION_PREF, true);
|
|
});
|
|
|
|
// Test that the console instant evaluation is updated on page navigation
|
|
add_task(async function () {
|
|
const start_uri = "data:text/html, Start uri";
|
|
const new_uri = "data:text/html, Test console refresh instant value";
|
|
const hud = await openNewTabAndConsole(start_uri);
|
|
|
|
setInputValue(hud, "globalThis.location.href");
|
|
await waitForEagerEvaluationResult(hud, `"${start_uri}"`);
|
|
|
|
await navigateTo(new_uri);
|
|
await waitForEagerEvaluationResult(hud, `"${new_uri}"`);
|
|
});
|