зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1780156 - [devtools] Add EvaluationContextSelector to BrowserConsole. r=ochameau.
Differential Revision: https://phabricator.services.mozilla.com/D150090
This commit is contained in:
Родитель
49704926df
Коммит
a6b5d5b4dc
|
@ -55,6 +55,12 @@ loader.lazyRequireGetter(
|
|||
"devtools/client/webconsole/utils/messages",
|
||||
true
|
||||
);
|
||||
loader.lazyRequireGetter(
|
||||
this,
|
||||
"getSelectedTarget",
|
||||
"devtools/shared/commands/target/selectors/targets",
|
||||
true
|
||||
);
|
||||
|
||||
const HELP_URL =
|
||||
"https://firefox-source-docs.mozilla.org/devtools-user/web_console/helpers/";
|
||||
|
@ -113,7 +119,9 @@ function evaluateExpression(expression, from = "input") {
|
|||
.execute(expression, {
|
||||
frameActor: hud.getSelectedFrameActorID(),
|
||||
selectedNodeActor: webConsoleUI.getSelectedNodeActorID(),
|
||||
selectedTargetFront: toolbox && toolbox.getSelectedTargetFront(),
|
||||
selectedTargetFront: getSelectedTarget(
|
||||
webConsoleUI.hud.commands.targetCommand.store.getState()
|
||||
),
|
||||
mapped,
|
||||
})
|
||||
.then(onSettled, onSettled);
|
||||
|
@ -244,7 +252,8 @@ function handleHelperResult(response) {
|
|||
case "screenshotOutput":
|
||||
const { args, value } = helperResult;
|
||||
const targetFront =
|
||||
toolbox?.getSelectedTargetFront() || hud.currentTarget;
|
||||
getSelectedTarget(hud.commands.targetCommand.store.getState()) ||
|
||||
hud.currentTarget;
|
||||
let screenshotMessages;
|
||||
|
||||
// @backward-compat { version 87 } The screenshot-content actor isn't available
|
||||
|
@ -371,14 +380,7 @@ function setInputValue(value) {
|
|||
* the previous evaluation.
|
||||
*/
|
||||
function terminalInputChanged(expression, force = false) {
|
||||
return async ({
|
||||
dispatch,
|
||||
webConsoleUI,
|
||||
hud,
|
||||
toolbox,
|
||||
commands,
|
||||
getState,
|
||||
}) => {
|
||||
return async ({ dispatch, webConsoleUI, hud, commands, getState }) => {
|
||||
const prefs = getAllPrefs(getState());
|
||||
if (!prefs.eagerEvaluation) {
|
||||
return null;
|
||||
|
@ -417,7 +419,9 @@ function terminalInputChanged(expression, force = false) {
|
|||
const response = await commands.scriptCommand.execute(expression, {
|
||||
frameActor: hud.getSelectedFrameActorID(),
|
||||
selectedNodeActor: webConsoleUI.getSelectedNodeActorID(),
|
||||
selectedTargetFront: toolbox && toolbox.getSelectedTargetFront(),
|
||||
selectedTargetFront: getSelectedTarget(
|
||||
hud.commands.targetCommand.store.getState()
|
||||
),
|
||||
mapped,
|
||||
eager: true,
|
||||
});
|
||||
|
|
|
@ -60,10 +60,7 @@ class EditorToolbar extends Component {
|
|||
}
|
||||
|
||||
renderEvaluationContextSelector() {
|
||||
if (
|
||||
!this.props.webConsoleUI.wrapper.toolbox ||
|
||||
!this.props.showEvaluationContextSelector
|
||||
) {
|
||||
if (!this.props.showEvaluationContextSelector) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -232,13 +232,17 @@ class EvaluationContextSelector extends Component {
|
|||
|
||||
render() {
|
||||
const { webConsoleUI, targets, selectedTarget } = this.props;
|
||||
const doc = webConsoleUI.document;
|
||||
const { toolbox } = webConsoleUI.wrapper;
|
||||
|
||||
if (targets.length <= 1) {
|
||||
// Don't render if there's only one target.
|
||||
// Also bail out if the console is being destroyed (where WebConsoleUI.wrapper gets
|
||||
// nullified).
|
||||
if (targets.length <= 1 || !webConsoleUI.wrapper) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const doc = webConsoleUI.document;
|
||||
const { toolbox } = webConsoleUI.wrapper;
|
||||
|
||||
return MenuButton(
|
||||
{
|
||||
menuId: "webconsole-input-evaluationsButton",
|
||||
|
|
|
@ -1489,11 +1489,7 @@ class JSTerm extends Component {
|
|||
}
|
||||
|
||||
renderEvaluationContextSelector() {
|
||||
if (
|
||||
!this.props.webConsoleUI.wrapper.toolbox ||
|
||||
this.props.editorMode ||
|
||||
!this.props.showEvaluationContextSelector
|
||||
) {
|
||||
if (this.props.editorMode || !this.props.showEvaluationContextSelector) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ skip-if = (os == "linux") || (os == "win") || (os == "mac" && !debug) # Bug 1440
|
|||
[browser_console_enable_network_monitoring.js]
|
||||
skip-if = verify
|
||||
[browser_console_error_source_click.js]
|
||||
[browser_console_evaluation_context_selector.js]
|
||||
[browser_console_filters.js]
|
||||
[browser_console_ignore_debugger_statement.js]
|
||||
[browser_console_jsterm_await.js]
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
/* 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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(async function() {
|
||||
await pushPref("devtools.chrome.enabled", true);
|
||||
|
||||
const hud = await BrowserConsoleManager.toggleBrowserConsole();
|
||||
|
||||
const evaluationContextSelectorButton = await waitFor(() =>
|
||||
hud.ui.outputNode.querySelector(".webconsole-evaluation-selector-button")
|
||||
);
|
||||
|
||||
if (!isFissionEnabled() && !isEveryFrameTargetEnabled()) {
|
||||
is(
|
||||
evaluationContextSelectorButton,
|
||||
null,
|
||||
"context selector is only displayed when Fission or EFT is enabled"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
ok(
|
||||
evaluationContextSelectorButton,
|
||||
"The evaluation context selector is visible"
|
||||
);
|
||||
is(
|
||||
evaluationContextSelectorButton.innerText,
|
||||
"Top",
|
||||
"The button has the expected 'Top' text"
|
||||
);
|
||||
is(
|
||||
evaluationContextSelectorButton.classList.contains(
|
||||
"webconsole-evaluation-selector-button-non-top"
|
||||
),
|
||||
false,
|
||||
"The non-top class isn't applied"
|
||||
);
|
||||
|
||||
await executeAndWaitForResultMessage(
|
||||
hud,
|
||||
"document.location",
|
||||
"chrome://browser/content/browser.xhtml"
|
||||
);
|
||||
ok(true, "The evaluation was done in the top context");
|
||||
|
||||
setInputValue(hud, "document.location.host");
|
||||
await waitForEagerEvaluationResult(hud, `"browser"`);
|
||||
|
||||
info("Check the context selector menu");
|
||||
checkContextSelectorMenuItemAt(hud, 0, {
|
||||
label: "Top",
|
||||
tooltip: "chrome://browser/content/browser.xhtml",
|
||||
checked: true,
|
||||
});
|
||||
checkContextSelectorMenuItemAt(hud, 1, {
|
||||
separator: true,
|
||||
});
|
||||
|
||||
info(
|
||||
"Add a tab with a worker and check both the document and the worker are displayed in the context selector"
|
||||
);
|
||||
const documentFile = "test-evaluate-worker.html";
|
||||
const documentWithWorkerUrl =
|
||||
"https://example.com/browser/devtools/client/webconsole/test/browser/" +
|
||||
documentFile;
|
||||
const tab = await addTab(documentWithWorkerUrl);
|
||||
|
||||
const documentIndex = await waitFor(() => {
|
||||
const i = getContextSelectorItems(hud).findIndex(el =>
|
||||
el.querySelector(".label")?.innerText?.endsWith(documentFile)
|
||||
);
|
||||
return i == -1 ? null : i;
|
||||
});
|
||||
|
||||
info("Select the document target");
|
||||
selectTargetInContextSelector(hud, documentWithWorkerUrl);
|
||||
|
||||
await waitFor(() =>
|
||||
evaluationContextSelectorButton.innerText.includes(documentFile)
|
||||
);
|
||||
ok(true, "The context was set to the selected document");
|
||||
is(
|
||||
evaluationContextSelectorButton.classList.contains(
|
||||
"webconsole-evaluation-selector-button-non-top"
|
||||
),
|
||||
true,
|
||||
"The non-top class is applied"
|
||||
);
|
||||
|
||||
checkContextSelectorMenuItemAt(hud, documentIndex, {
|
||||
label: documentWithWorkerUrl,
|
||||
tooltip: documentWithWorkerUrl,
|
||||
checked: true,
|
||||
});
|
||||
|
||||
await waitForEagerEvaluationResult(hud, `"example.com"`);
|
||||
ok(true, "The instant evaluation result is updated in the document context");
|
||||
|
||||
await executeAndWaitForResultMessage(
|
||||
hud,
|
||||
"document.location",
|
||||
documentWithWorkerUrl
|
||||
);
|
||||
ok(true, "The evaluation is done in the document context");
|
||||
|
||||
// set input text so we can watch for instant evaluation result update
|
||||
setInputValue(hud, "globalThis.location.href");
|
||||
await waitForEagerEvaluationResult(hud, `"${documentWithWorkerUrl}"`);
|
||||
|
||||
info("Select the worker target");
|
||||
const workerFile = "test-evaluate-worker.js";
|
||||
const workerUrl =
|
||||
"https://example.com/browser/devtools/client/webconsole/test/browser/" +
|
||||
workerFile;
|
||||
const workerIndex = await waitFor(() => {
|
||||
const i = getContextSelectorItems(hud).findIndex(el =>
|
||||
el.querySelector(".label")?.innerText?.endsWith(workerFile)
|
||||
);
|
||||
return i == -1 ? null : i;
|
||||
});
|
||||
selectTargetInContextSelector(hud, workerFile);
|
||||
|
||||
await waitFor(() =>
|
||||
evaluationContextSelectorButton.innerText.includes(workerFile)
|
||||
);
|
||||
ok(true, "The context was set to the selected worker");
|
||||
|
||||
await waitForEagerEvaluationResult(hud, `"${workerUrl}"`);
|
||||
ok(true, "The instant evaluation result is updated in the worker context");
|
||||
|
||||
checkContextSelectorMenuItemAt(hud, workerIndex, {
|
||||
label: workerFile,
|
||||
tooltip: workerFile,
|
||||
checked: true,
|
||||
});
|
||||
|
||||
await executeAndWaitForResultMessage(
|
||||
hud,
|
||||
"globalThis.location",
|
||||
`WorkerLocation`
|
||||
);
|
||||
ok(true, "The evaluation is done in the worker context");
|
||||
|
||||
// set input text so we can watch for instant evaluation result update
|
||||
setInputValue(hud, "document.location.host");
|
||||
await waitForEagerEvaluationResult(
|
||||
hud,
|
||||
`ReferenceError: document is not defined`
|
||||
);
|
||||
|
||||
info(
|
||||
"Remove the tab and make sure both the document and worker target are removed from the context selector"
|
||||
);
|
||||
await removeTab(tab);
|
||||
|
||||
await waitFor(() => evaluationContextSelectorButton.innerText == "Top");
|
||||
ok(true, "The context is set back to Top");
|
||||
|
||||
checkContextSelectorMenuItemAt(hud, 0, {
|
||||
label: "Top",
|
||||
tooltip: "chrome://browser/content/browser.xhtml",
|
||||
checked: true,
|
||||
});
|
||||
|
||||
is(
|
||||
getContextSelectorItems(hud).every(el => {
|
||||
const label = el.querySelector(".label")?.innerText;
|
||||
return (
|
||||
!label ||
|
||||
(label !== "test-evaluate-worker.html" && label !== workerFile)
|
||||
);
|
||||
}),
|
||||
true,
|
||||
"the document and worker targets were removed"
|
||||
);
|
||||
|
||||
await waitForEagerEvaluationResult(hud, `"browser"`);
|
||||
ok(true, "The instant evaluation was done in the top context");
|
||||
|
||||
await executeAndWaitForResultMessage(
|
||||
hud,
|
||||
"document.location",
|
||||
"chrome://browser/content/browser.xhtml"
|
||||
);
|
||||
ok(true, "The evaluation was done in the top context");
|
||||
});
|
|
@ -1837,11 +1837,8 @@ function getContextSelectorItems(hud) {
|
|||
* state.
|
||||
*
|
||||
* @param {WebConsole} hud
|
||||
* @param {Array<Object>} expected: An array of object which can have the following shape:
|
||||
* - {String} label: The label of the target
|
||||
* - {String} tooltip: The tooltip of the target element in the menu
|
||||
* - {Boolean} checked: if the target should be selected or not
|
||||
* - {Boolean} separator: if the element is a simple separator
|
||||
* @param {Array<Object>} expected: An array of object (see checkContextSelectorMenuItemAt
|
||||
* for expected properties)
|
||||
*/
|
||||
function checkContextSelectorMenu(hud, expected) {
|
||||
const items = getContextSelectorItems(hud);
|
||||
|
@ -1852,32 +1849,43 @@ function checkContextSelectorMenu(hud, expected) {
|
|||
"The context selector menu has the expected number of items"
|
||||
);
|
||||
|
||||
expected.forEach(({ label, tooltip, checked, separator }, i) => {
|
||||
const el = items[i];
|
||||
|
||||
if (separator === true) {
|
||||
is(
|
||||
el.getAttribute("role"),
|
||||
"menuseparator",
|
||||
"The element is a separator"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const elChecked = el.getAttribute("aria-checked") === "true";
|
||||
const elTooltip = el.getAttribute("title");
|
||||
const elLabel = el.querySelector(".label").innerText;
|
||||
|
||||
is(elLabel, label, `The item has the expected label`);
|
||||
is(elTooltip, tooltip, `Item "${label}" has the expected tooltip`);
|
||||
is(
|
||||
elChecked,
|
||||
checked,
|
||||
`Item "${label}" is ${checked ? "checked" : "unchecked"}`
|
||||
);
|
||||
expected.forEach((expectedItem, i) => {
|
||||
checkContextSelectorMenuItemAt(hud, i, expectedItem);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the evaluation context selector menu has the expected item at the specified index.
|
||||
*
|
||||
* @param {WebConsole} hud
|
||||
* @param {Number} index
|
||||
* @param {Object} expected
|
||||
* @param {String} expected.label: The label of the target
|
||||
* @param {String} expected.tooltip: The tooltip of the target element in the menu
|
||||
* @param {Boolean} expected.checked: if the target should be selected or not
|
||||
* @param {Boolean} expected.separator: if the element is a simple separator
|
||||
*/
|
||||
function checkContextSelectorMenuItemAt(hud, index, expected) {
|
||||
const el = getContextSelectorItems(hud).at(index);
|
||||
|
||||
if (expected.separator === true) {
|
||||
is(el.getAttribute("role"), "menuseparator", "The element is a separator");
|
||||
return;
|
||||
}
|
||||
|
||||
const elChecked = el.getAttribute("aria-checked") === "true";
|
||||
const elTooltip = el.getAttribute("title");
|
||||
const elLabel = el.querySelector(".label").innerText;
|
||||
|
||||
is(elLabel, expected.label, `The item has the expected label`);
|
||||
is(elTooltip, expected.tooltip, `Item "${elLabel}" has the expected tooltip`);
|
||||
is(
|
||||
elChecked,
|
||||
expected.checked,
|
||||
`Item "${elLabel}" is ${expected.checked ? "checked" : "unchecked"}`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a target in the context selector.
|
||||
*
|
||||
|
|
|
@ -50,18 +50,16 @@ loader.lazyGetter(this, "L10N", function() {
|
|||
return new LocalizationHelper("devtools/client/locales/startup.properties");
|
||||
});
|
||||
|
||||
function renderApp({ app, store, toolbox, root }) {
|
||||
function renderApp({ app, store, commands, root }) {
|
||||
return ReactDOM.render(
|
||||
createElement(
|
||||
Provider,
|
||||
{ store },
|
||||
toolbox
|
||||
? createElement(
|
||||
createProvider(toolbox.commands.targetCommand.storeId),
|
||||
{ store: toolbox.commands.targetCommand.store },
|
||||
app
|
||||
)
|
||||
: app
|
||||
createElement(
|
||||
createProvider(commands.targetCommand.storeId),
|
||||
{ store: commands.targetCommand.store },
|
||||
app
|
||||
)
|
||||
),
|
||||
root
|
||||
);
|
||||
|
@ -135,7 +133,7 @@ class WebConsoleWrapper {
|
|||
app,
|
||||
store,
|
||||
root: this.parentNode,
|
||||
toolbox: this.toolbox,
|
||||
commands: this.hud.commands,
|
||||
});
|
||||
} else {
|
||||
// If there's no parentNode, we are in a test. So we can resolve immediately.
|
||||
|
|
Загрузка…
Ссылка в новой задаче