mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 16:25:38 +00:00
Bug 1602489 - Basic eager evaluation support, r=nchevobbe.
Differential Revision: https://phabricator.services.mozilla.com/D56393 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
d3103de230
commit
662a021b39
@ -2157,6 +2157,10 @@ pref("devtools.webconsole.filter.netxhr", false);
|
||||
// Webconsole autocomplete preference
|
||||
pref("devtools.webconsole.input.autocomplete",true);
|
||||
|
||||
// Set to true to eagerly show the results of webconsole terminal evaluations
|
||||
// when they don't have side effects.
|
||||
pref("devtools.webconsole.input.eagerEvaluation", false);
|
||||
|
||||
// Browser console filters
|
||||
pref("devtools.browserconsole.filter.error", true);
|
||||
pref("devtools.browserconsole.filter.warn", true);
|
||||
|
@ -499,6 +499,10 @@ html #webconsole-notificationbox {
|
||||
fill: var(--theme-icon-checked-color);
|
||||
}
|
||||
|
||||
.eager-evaluation-result * {
|
||||
color: var(--theme-comment) !important;
|
||||
}
|
||||
|
||||
.webconsole-app .cm-auto-complete-shadow-text::after {
|
||||
content: attr(title);
|
||||
color: var(--theme-comment);
|
||||
|
@ -5,7 +5,12 @@
|
||||
"use strict";
|
||||
|
||||
const { Utils: WebConsoleUtils } = require("devtools/client/webconsole/utils");
|
||||
const { EVALUATE_EXPRESSION } = require("devtools/client/webconsole/constants");
|
||||
const {
|
||||
EVALUATE_EXPRESSION,
|
||||
SET_TERMINAL_INPUT,
|
||||
SET_TERMINAL_EAGER_RESULT,
|
||||
} = require("devtools/client/webconsole/constants");
|
||||
const { getAllPrefs } = require("devtools/client/webconsole/selectors/prefs");
|
||||
|
||||
loader.lazyServiceGetter(
|
||||
this,
|
||||
@ -36,6 +41,21 @@ loader.lazyRequireGetter(
|
||||
);
|
||||
const HELP_URL = "https://developer.mozilla.org/docs/Tools/Web_Console/Helpers";
|
||||
|
||||
async function getMappedExpression(hud, expression) {
|
||||
let mapResult;
|
||||
try {
|
||||
mapResult = await hud.getMappedExpression(expression);
|
||||
} catch (e) {
|
||||
console.warn("Error when calling getMappedExpression", e);
|
||||
}
|
||||
|
||||
let mapped = null;
|
||||
if (mapResult) {
|
||||
({ expression, mapped } = mapResult);
|
||||
}
|
||||
return { expression, mapped };
|
||||
}
|
||||
|
||||
function evaluateExpression(expression) {
|
||||
return async ({ dispatch, webConsoleUI, hud, client }) => {
|
||||
if (!expression) {
|
||||
@ -61,16 +81,8 @@ function evaluateExpression(expression) {
|
||||
|
||||
WebConsoleUtils.usageCount++;
|
||||
|
||||
let mappedExpressionRes;
|
||||
try {
|
||||
mappedExpressionRes = await hud.getMappedExpression(expression);
|
||||
} catch (e) {
|
||||
console.warn("Error when calling getMappedExpression", e);
|
||||
}
|
||||
|
||||
expression = mappedExpressionRes
|
||||
? mappedExpressionRes.expression
|
||||
: expression;
|
||||
let mapped;
|
||||
({ expression, mapped } = await getMappedExpression(hud, expression));
|
||||
|
||||
const { frameActor, webConsoleFront } = webConsoleUI.getFrameActor();
|
||||
|
||||
@ -83,7 +95,7 @@ function evaluateExpression(expression) {
|
||||
frameActor,
|
||||
selectedNodeFront: webConsoleUI.getSelectedNodeFront(),
|
||||
webConsoleFront,
|
||||
mapped: mappedExpressionRes ? mappedExpressionRes.mapped : null,
|
||||
mapped,
|
||||
})
|
||||
.then(onSettled, onSettled);
|
||||
|
||||
@ -195,8 +207,56 @@ function setInputValue(value) {
|
||||
};
|
||||
}
|
||||
|
||||
function terminalInputChanged(expression) {
|
||||
return async ({ dispatch, webConsoleUI, hud, client, getState }) => {
|
||||
const prefs = getAllPrefs(getState());
|
||||
if (!prefs.eagerEvaluation) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The server does not support eager evaluation when replaying.
|
||||
if (hud.currentTarget.isReplayEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const originalExpression = expression;
|
||||
dispatch({
|
||||
type: SET_TERMINAL_INPUT,
|
||||
expression,
|
||||
});
|
||||
|
||||
let mapped;
|
||||
({ expression, mapped } = await getMappedExpression(hud, expression));
|
||||
|
||||
const { frameActor, webConsoleFront } = webConsoleUI.getFrameActor();
|
||||
|
||||
const response = await client.evaluateJSAsync(expression, {
|
||||
frameActor,
|
||||
selectedNodeFront: webConsoleUI.getSelectedNodeFront(),
|
||||
webConsoleFront,
|
||||
mapped,
|
||||
eager: true,
|
||||
});
|
||||
|
||||
const result = response.exception || response.result;
|
||||
|
||||
// Don't show syntax errors or undefined results to the user.
|
||||
if (result.isSyntaxError || result.type == "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line consistent-return
|
||||
return dispatch({
|
||||
type: SET_TERMINAL_EAGER_RESULT,
|
||||
expression: originalExpression,
|
||||
result,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
evaluateExpression,
|
||||
focusInput,
|
||||
setInputValue,
|
||||
terminalInputChanged,
|
||||
};
|
||||
|
@ -39,6 +39,9 @@ const JSTerm = createFactory(
|
||||
const ConfirmDialog = createFactory(
|
||||
require("devtools/client/webconsole/components/Input/ConfirmDialog")
|
||||
);
|
||||
const EagerEvaluation = createFactory(
|
||||
require("devtools/client/webconsole/components/Input/EagerEvaluation")
|
||||
);
|
||||
|
||||
// And lazy load the ones that may not be used.
|
||||
loader.lazyGetter(this, "SideBar", () =>
|
||||
@ -332,6 +335,10 @@ class App extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
renderEagerEvaluation() {
|
||||
return EagerEvaluation();
|
||||
}
|
||||
|
||||
renderReverseSearch() {
|
||||
const { serviceContainer, reverseSearchInitialValue } = this.props;
|
||||
|
||||
@ -411,6 +418,7 @@ class App extends Component {
|
||||
const consoleOutput = this.renderConsoleOutput();
|
||||
const notificationBox = this.renderNotificationBox();
|
||||
const jsterm = this.renderJsTerm();
|
||||
const eager = this.renderEagerEvaluation();
|
||||
const reverseSearch = this.renderReverseSearch();
|
||||
const sidebar = this.renderSideBar();
|
||||
const confirmDialog = this.renderConfirmDialog();
|
||||
@ -422,7 +430,8 @@ class App extends Component {
|
||||
{ className: "flexible-output-input", key: "in-out-container" },
|
||||
consoleOutput,
|
||||
notificationBox,
|
||||
jsterm
|
||||
jsterm,
|
||||
eager
|
||||
),
|
||||
editorMode
|
||||
? GridElementWidthResizer({
|
||||
|
@ -0,0 +1,66 @@
|
||||
/* 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 { Component } = require("devtools/client/shared/vendor/react");
|
||||
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||
|
||||
const {
|
||||
getTerminalEagerResult,
|
||||
} = require("devtools/client/webconsole/selectors/history");
|
||||
|
||||
loader.lazyGetter(this, "REPS", function() {
|
||||
return require("devtools/client/shared/components/reps/reps").REPS;
|
||||
});
|
||||
loader.lazyGetter(this, "MODE", function() {
|
||||
return require("devtools/client/shared/components/reps/reps").MODE;
|
||||
});
|
||||
loader.lazyRequireGetter(
|
||||
this,
|
||||
"PropTypes",
|
||||
"devtools/client/shared/vendor/react-prop-types"
|
||||
);
|
||||
|
||||
/**
|
||||
* Show the results of evaluating the current terminal text, if possible.
|
||||
*/
|
||||
class EagerEvaluation extends Component {
|
||||
static get propTypes() {
|
||||
return {
|
||||
terminalEagerResult: PropTypes.any,
|
||||
};
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { terminalEagerResult } = this.props;
|
||||
if (terminalEagerResult !== null) {
|
||||
return dom.span(
|
||||
{
|
||||
className: "devtools-monospace eager-evaluation-result",
|
||||
},
|
||||
REPS.Rep({
|
||||
object: terminalEagerResult.getGrip
|
||||
? terminalEagerResult.getGrip()
|
||||
: terminalEagerResult,
|
||||
mode: MODE.LONG,
|
||||
})
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
terminalEagerResult: getTerminalEagerResult(state),
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = connect(mapStateToProps)(EagerEvaluation);
|
@ -100,6 +100,8 @@ class JSTerm extends Component {
|
||||
editorToggle: PropTypes.func.isRequired,
|
||||
// Dismiss the editor onboarding UI.
|
||||
editorOnboardingDismiss: PropTypes.func.isRequired,
|
||||
// Set the last JS input value.
|
||||
terminalInputChanged: PropTypes.func.isRequired,
|
||||
// Is the input in editor mode.
|
||||
editorMode: PropTypes.bool,
|
||||
editorWidth: PropTypes.number,
|
||||
@ -126,6 +128,14 @@ class JSTerm extends Component {
|
||||
// The delay should be small enough to be unnoticed by the user.
|
||||
this.autocompleteUpdate = debounce(this.props.autocompleteUpdate, 75, this);
|
||||
|
||||
// Updates to the terminal input which can trigger eager evaluations are
|
||||
// similarly debounced.
|
||||
this.terminalInputChanged = debounce(
|
||||
this.props.terminalInputChanged,
|
||||
75,
|
||||
this
|
||||
);
|
||||
|
||||
// Because the autocomplete has a slight delay (75ms), there can be time where the
|
||||
// codeMirror completion text is out-of-date, which might lead to issue when the user
|
||||
// accept the autocompletion while the update of the completion text is still pending.
|
||||
@ -618,6 +628,7 @@ class JSTerm extends Component {
|
||||
*/
|
||||
_setValue(newValue = "") {
|
||||
this.lastInputValue = newValue;
|
||||
this.terminalInputChanged(newValue);
|
||||
|
||||
if (this.editor) {
|
||||
// In order to get the autocomplete popup to work properly, we need to set the
|
||||
@ -767,6 +778,7 @@ class JSTerm extends Component {
|
||||
this.autocompleteUpdate();
|
||||
}
|
||||
this.lastInputValue = value;
|
||||
this.terminalInputChanged(value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1264,6 +1276,8 @@ function mapDispatchToProps(dispatch) {
|
||||
dispatch(actions.evaluateExpression(expression)),
|
||||
editorToggle: () => dispatch(actions.editorToggle()),
|
||||
editorOnboardingDismiss: () => dispatch(actions.editorOnboardingDismiss()),
|
||||
terminalInputChanged: value =>
|
||||
dispatch(actions.terminalInputChanged(value)),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
DevToolsModules(
|
||||
'ConfirmDialog.js',
|
||||
'EagerEvaluation.js',
|
||||
'EditorToolbar.js',
|
||||
'JSTerm.js',
|
||||
'ReverseSearchInput.css',
|
||||
|
@ -15,6 +15,8 @@ const actionTypes = {
|
||||
EDITOR_TOGGLE: "EDITOR_TOGGLE",
|
||||
EDITOR_ONBOARDING_DISMISS: "EDITOR_ONBOARDING_DISMISS",
|
||||
EVALUATE_EXPRESSION: "EVALUATE_EXPRESSION",
|
||||
SET_TERMINAL_INPUT: "SET_TERMINAL_INPUT",
|
||||
SET_TERMINAL_EAGER_RESULT: "SET_TERMINAL_EAGER_RESULT",
|
||||
FILTER_TEXT_SET: "FILTER_TEXT_SET",
|
||||
FILTER_TOGGLE: "FILTER_TOGGLE",
|
||||
FILTERS_CLEAR: "FILTERS_CLEAR",
|
||||
@ -84,6 +86,7 @@ const prefs = {
|
||||
// We use the same pref to enable the sidebar on webconsole and browser console.
|
||||
SIDEBAR_TOGGLE: "devtools.webconsole.sidebarToggle",
|
||||
AUTOCOMPLETE: "devtools.webconsole.input.autocomplete",
|
||||
EAGER_EVALUATION: "devtools.webconsole.input.eagerEvaluation",
|
||||
GROUP_WARNINGS: "devtools.webconsole.groupWarningMessages",
|
||||
BROWSER_TOOLBOX_FISSION: "devtools.browsertoolbox.fission",
|
||||
},
|
||||
|
@ -15,6 +15,8 @@ const {
|
||||
REVERSE_SEARCH_INPUT_CHANGE,
|
||||
REVERSE_SEARCH_BACK,
|
||||
REVERSE_SEARCH_NEXT,
|
||||
SET_TERMINAL_INPUT,
|
||||
SET_TERMINAL_EAGER_RESULT,
|
||||
} = require("devtools/client/webconsole/constants");
|
||||
|
||||
/**
|
||||
@ -39,6 +41,9 @@ function getInitialState() {
|
||||
reverseSearchEnabled: false,
|
||||
currentReverseSearchResults: null,
|
||||
currentReverseSearchResultsPosition: null,
|
||||
|
||||
terminalInput: null,
|
||||
terminalEagerResult: null,
|
||||
};
|
||||
}
|
||||
|
||||
@ -61,6 +66,10 @@ function history(state = getInitialState(), action, prefsState) {
|
||||
return reverseSearchBack(state);
|
||||
case REVERSE_SEARCH_NEXT:
|
||||
return reverseSearchNext(state);
|
||||
case SET_TERMINAL_INPUT:
|
||||
return setTerminalInput(state, action.expression);
|
||||
case SET_TERMINAL_EAGER_RESULT:
|
||||
return setTerminalEagerResult(state, action.expression, action.result);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
@ -217,4 +226,22 @@ function reverseSearchNext(state) {
|
||||
};
|
||||
}
|
||||
|
||||
function setTerminalInput(state, expression) {
|
||||
return {
|
||||
...state,
|
||||
terminalInput: expression,
|
||||
terminalEagerResult: null,
|
||||
};
|
||||
}
|
||||
|
||||
function setTerminalEagerResult(state, expression, result) {
|
||||
if (state.terminalInput == expression) {
|
||||
return {
|
||||
...state,
|
||||
terminalEagerResult: result,
|
||||
};
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
exports.history = history;
|
||||
|
@ -81,6 +81,11 @@ function getReverseSearchTotalResults(state) {
|
||||
return currentReverseSearchResults.length;
|
||||
}
|
||||
|
||||
function getTerminalEagerResult(state) {
|
||||
const { history } = state;
|
||||
return history.terminalEagerResult;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getHistory,
|
||||
getHistoryEntries,
|
||||
@ -88,4 +93,5 @@ module.exports = {
|
||||
getReverseSearchResult,
|
||||
getReverseSearchResultPosition,
|
||||
getReverseSearchTotalResults,
|
||||
getTerminalEagerResult,
|
||||
};
|
||||
|
@ -49,6 +49,7 @@ function configureStore(webConsoleUI, options = {}) {
|
||||
options.logLimit || Math.max(getIntPref("devtools.hud.loglimit"), 1);
|
||||
const sidebarToggle = getBoolPref(PREFS.FEATURES.SIDEBAR_TOGGLE);
|
||||
const autocomplete = getBoolPref(PREFS.FEATURES.AUTOCOMPLETE);
|
||||
const eagerEvaluation = getBoolPref(PREFS.FEATURES.EAGER_EVALUATION);
|
||||
const groupWarnings = getBoolPref(PREFS.FEATURES.GROUP_WARNINGS);
|
||||
const historyCount = getIntPref(PREFS.UI.INPUT_HISTORY_COUNT);
|
||||
|
||||
@ -57,6 +58,7 @@ function configureStore(webConsoleUI, options = {}) {
|
||||
logLimit,
|
||||
sidebarToggle,
|
||||
autocomplete,
|
||||
eagerEvaluation,
|
||||
historyCount,
|
||||
groupWarnings,
|
||||
}),
|
||||
|
@ -251,6 +251,7 @@ skip-if = (os == "win" && processor == "aarch64") # disabled on aarch64 due to 1
|
||||
[browser_jsterm_ctrl_key_nav.js]
|
||||
skip-if = os != 'mac' # The tested ctrl+key shortcuts are OSX only
|
||||
[browser_jsterm_document_no_xray.js]
|
||||
[browser_jsterm_eager_evaluation.js]
|
||||
[browser_jsterm_editor.js]
|
||||
[browser_jsterm_editor_disabled_history_nav_with_keyboard.js]
|
||||
[browser_jsterm_editor_enter.js]
|
||||
|
@ -0,0 +1,49 @@
|
||||
/* 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,
|
||||
<script>
|
||||
let x = 3, y = 4;
|
||||
function foo() {
|
||||
x = 10;
|
||||
}
|
||||
</script>
|
||||
`;
|
||||
|
||||
// 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() {
|
||||
await pushPref("devtools.webconsole.input.eagerEvaluation", true);
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
|
||||
setInputValue(hud, "x + y");
|
||||
await waitForEagerEvaluationResult(hud, "7");
|
||||
|
||||
setInputValue(hud, "x + y + undefined");
|
||||
await waitForEagerEvaluationResult(hud, "NaN");
|
||||
|
||||
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, "foo()");
|
||||
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/);
|
||||
});
|
@ -1154,6 +1154,31 @@ function isReverseSearchInputFocused(hud) {
|
||||
return document.activeElement == reverseSearchInput && documentIsFocused;
|
||||
}
|
||||
|
||||
async function waitForEagerEvaluationResult(hud, text) {
|
||||
await waitUntil(() => {
|
||||
const elem = hud.ui.outputNode.querySelector(".eager-evaluation-result");
|
||||
if (elem) {
|
||||
if (text instanceof RegExp) {
|
||||
return text.test(elem.innerText);
|
||||
}
|
||||
return elem.innerText == text;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
ok(true, `Got eager evaluation result ${text}`);
|
||||
}
|
||||
|
||||
// This just makes sure the eager evaluation result disappears. This will pass
|
||||
// even for inputs which eventually have a result because nothing will be shown
|
||||
// while the evaluation happens. Waiting here does make sure that a previous
|
||||
// input was processed and sent down to the server for evaluating.
|
||||
async function waitForNoEagerEvaluationResult(hud) {
|
||||
await waitUntil(() => {
|
||||
return !hud.ui.outputNode.querySelector(".eager-evaluation-result");
|
||||
});
|
||||
ok(true, `Eager evaluation result disappeared`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a node in the inspector.
|
||||
*
|
||||
|
@ -27,6 +27,7 @@ pref("devtools.webconsole.sidebarToggle", true);
|
||||
pref("devtools.webconsole.groupWarningMessages", false);
|
||||
pref("devtools.webconsole.input.editor", false);
|
||||
pref("devtools.webconsole.input.autocomplete", true);
|
||||
pref("devtools.webconsole.input.eagerEvaluation", false);
|
||||
pref("devtools.browserconsole.contentMessages", true);
|
||||
pref("devtools.webconsole.input.editorWidth", 800);
|
||||
pref("devtools.webconsole.input.editorOnboarding", true);
|
||||
|
@ -1145,6 +1145,7 @@ const WebConsoleActor = ActorClassWithSpec(webconsoleSpec, {
|
||||
url: request.url,
|
||||
selectedNodeActor: request.selectedNodeActor,
|
||||
selectedObjectActor: request.selectedObjectActor,
|
||||
eager: request.eager,
|
||||
};
|
||||
const { mapped } = request;
|
||||
|
||||
|
@ -105,10 +105,15 @@ function isObject(value) {
|
||||
exports.evalWithDebugger = function(string, options = {}, webConsole) {
|
||||
const evalString = getEvalInput(string);
|
||||
const { frame, dbg } = getFrameDbg(options, webConsole);
|
||||
|
||||
// early return for replay
|
||||
if (dbg.replaying) {
|
||||
if (options.eager) {
|
||||
throw new Error("Eager evaluations are not supported while replaying");
|
||||
}
|
||||
return evalReplay(frame, dbg, evalString);
|
||||
}
|
||||
|
||||
const { dbgWindow, bindSelf } = getDbgWindow(options, dbg, webConsole);
|
||||
const helpers = getHelpers(dbgWindow, options, webConsole);
|
||||
const { bindings, helperCache } = bindCommands(
|
||||
@ -126,6 +131,11 @@ exports.evalWithDebugger = function(string, options = {}, webConsole) {
|
||||
|
||||
updateConsoleInputEvaluation(dbg, dbgWindow, webConsole);
|
||||
|
||||
let sideEffectData = null;
|
||||
if (options.eager) {
|
||||
sideEffectData = preventSideEffects(dbg);
|
||||
}
|
||||
|
||||
const result = getEvalResult(
|
||||
evalString,
|
||||
evalOptions,
|
||||
@ -134,6 +144,10 @@ exports.evalWithDebugger = function(string, options = {}, webConsole) {
|
||||
dbgWindow
|
||||
);
|
||||
|
||||
if (options.eager) {
|
||||
allowSideEffects(dbg, sideEffectData);
|
||||
}
|
||||
|
||||
const { helperResult } = helpers;
|
||||
|
||||
// Clean up helpers helpers and bindings
|
||||
@ -163,7 +177,7 @@ function getEvalResult(string, evalOptions, bindings, frame, dbgWindow) {
|
||||
// Attempt to initialize any declarations found in the evaluated string
|
||||
// since they may now be stuck in an "initializing" state due to the
|
||||
// error. Already-initialized bindings will be ignored.
|
||||
if ("throw" in result) {
|
||||
if (result && "throw" in result) {
|
||||
parseErrorOutput(dbgWindow, string);
|
||||
}
|
||||
return result;
|
||||
@ -236,6 +250,62 @@ function parseErrorOutput(dbgWindow, string) {
|
||||
}
|
||||
}
|
||||
|
||||
function preventSideEffects(dbg) {
|
||||
if (dbg.onEnterFrame || dbg.onNativeCall) {
|
||||
throw new Error("Debugger has hook installed");
|
||||
}
|
||||
|
||||
const data = {
|
||||
executedScripts: new Set(),
|
||||
debuggees: dbg.getDebuggees(),
|
||||
|
||||
handler: {
|
||||
hit: () => null,
|
||||
},
|
||||
};
|
||||
|
||||
dbg.addAllGlobalsAsDebuggees();
|
||||
|
||||
dbg.onEnterFrame = frame => {
|
||||
const script = frame.script;
|
||||
|
||||
if (data.executedScripts.has(script)) {
|
||||
return;
|
||||
}
|
||||
data.executedScripts.add(script);
|
||||
|
||||
const offsets = script.getEffectfulOffsets();
|
||||
for (const offset of offsets) {
|
||||
script.setBreakpoint(offset, data.handler);
|
||||
}
|
||||
};
|
||||
|
||||
dbg.onNativeCall = (callee, reason) => {
|
||||
if (reason == "get") {
|
||||
// Native getters are never considered effectful.
|
||||
return undefined;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function allowSideEffects(dbg, data) {
|
||||
for (const script of data.executedScripts) {
|
||||
script.clearBreakpoint(data.handler);
|
||||
}
|
||||
|
||||
for (const global of dbg.getDebuggees()) {
|
||||
if (!data.debuggees.includes(global)) {
|
||||
dbg.removeDebuggee(global);
|
||||
}
|
||||
}
|
||||
|
||||
dbg.onEnterFrame = undefined;
|
||||
dbg.onNativeCall = undefined;
|
||||
}
|
||||
|
||||
function updateConsoleInputEvaluation(dbg, dbgWindow, webConsole) {
|
||||
// Adopt webConsole._lastConsoleInputEvaluation value in the new debugger,
|
||||
// to prevent "Debugger.Object belongs to a different Debugger" exceptions
|
||||
|
@ -273,6 +273,10 @@ class ObjectFront extends FrontClassWithSpec(objectSpec) {
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
get isSyntaxError() {
|
||||
return this._grip.preview && this._grip.preview.name == "SyntaxError";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -200,6 +200,7 @@ class WebConsoleFront extends FrontClassWithSpec(webconsoleSpec) {
|
||||
selectedNodeActor: opts.selectedNodeActor,
|
||||
selectedObjectActor: opts.selectedObjectActor,
|
||||
mapped: opts.mapped,
|
||||
eager: opts.eager,
|
||||
};
|
||||
|
||||
const { resultID } = await super.evaluateJSAsync(options);
|
||||
|
@ -153,6 +153,7 @@ const webconsoleSpecPrototype = {
|
||||
selectedNodeActor: Option(0, "string"),
|
||||
selectedObjectActor: Option(0, "string"),
|
||||
mapped: Option(0, "nullable:json"),
|
||||
eager: Option(0, "nullable:boolean"),
|
||||
},
|
||||
response: RetVal("console.evaluatejsasync"),
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user