зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1735551 - [devtools] Turn WalkerFront#findNodeFront into a command. r=ochameau.
The callsites are migrated to the new command, except from the webconsole test in which we replace usage with existing inspector test helper. Differential Revision: https://phabricator.services.mozilla.com/D128712
This commit is contained in:
Родитель
90fcf31b4f
Коммит
c64f0fb67c
|
@ -391,64 +391,6 @@ class WalkerFront extends FrontClassWithSpec(walkerSpec) {
|
|||
documentNode.reparent(parentNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the cross iframes query selectors for the current walker front.
|
||||
*
|
||||
* @param {Array} selectors
|
||||
* An array of CSS selectors to find the target accessible object.
|
||||
* Several selectors can be needed if the element is nested in frames
|
||||
* and not directly in the root document.
|
||||
* @return {Promise} a promise that resolves when the node front is found for
|
||||
* selection using inspector tools.
|
||||
*/
|
||||
async findNodeFront(nodeSelectors) {
|
||||
const querySelectors = async nodeFront => {
|
||||
const selector = nodeSelectors.shift();
|
||||
if (!selector) {
|
||||
return nodeFront;
|
||||
}
|
||||
nodeFront = await nodeFront.walkerFront.querySelector(
|
||||
nodeFront,
|
||||
selector
|
||||
);
|
||||
// It's possible the containing iframe isn't available by the time
|
||||
// walkerFront.querySelector is called, which causes the re-selected node to be
|
||||
// unavailable. There also isn't a way for us to know when all iframes on the page
|
||||
// have been created after a reload. Because of this, we should should bail here.
|
||||
if (!nodeFront) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (nodeSelectors.length > 0) {
|
||||
await nodeFront.waitForFrameLoad();
|
||||
|
||||
const { nodes } = await this.children(nodeFront);
|
||||
|
||||
// If there are remaining selectors to process, they will target a document or a
|
||||
// document-fragment under the current node. Whether the element is a frame or
|
||||
// a web component, it can only contain one document/document-fragment, so just
|
||||
// select the first one available.
|
||||
nodeFront = nodes.find(node => {
|
||||
const { nodeType } = node;
|
||||
return (
|
||||
nodeType === Node.DOCUMENT_FRAGMENT_NODE ||
|
||||
nodeType === Node.DOCUMENT_NODE
|
||||
);
|
||||
});
|
||||
|
||||
// The iframe selector might have matched an element which is not an
|
||||
// iframe in the new page (or an iframe with no document?). In this
|
||||
// case, bail out and fallback to the root body element.
|
||||
if (!nodeFront) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return querySelectors(nodeFront) || nodeFront;
|
||||
};
|
||||
const nodeFront = await this.getRootNode();
|
||||
return querySelectors(nodeFront);
|
||||
}
|
||||
|
||||
_onRootNodeAvailable(rootNode) {
|
||||
if (rootNode.isTopLevelDocument) {
|
||||
this.rootNode = rootNode;
|
||||
|
|
|
@ -571,7 +571,12 @@ Inspector.prototype = {
|
|||
// Try to find a default node using three strategies:
|
||||
const defaultNodeSelectors = [
|
||||
// - first try to match css selectors for the selection
|
||||
() => (cssSelectors.length ? walker.findNodeFront(cssSelectors) : null),
|
||||
() =>
|
||||
cssSelectors.length
|
||||
? this.commands.inspectorCommand.findNodeFrontFromSelectors(
|
||||
cssSelectors
|
||||
)
|
||||
: null,
|
||||
// - otherwise try to get the "body" element
|
||||
() => walker.querySelector(rootNodeFront, "body"),
|
||||
// - finally get the documentElement element if nothing else worked.
|
||||
|
|
|
@ -1381,7 +1381,9 @@ class HighlightersOverlay {
|
|||
return;
|
||||
}
|
||||
|
||||
const nodeFront = await this.inspectorFront.walker.findNodeFront(selectors);
|
||||
const nodeFront = await this.inspector.commands.inspectorCommand.findNodeFrontFromSelectors(
|
||||
selectors
|
||||
);
|
||||
|
||||
if (nodeFront) {
|
||||
await showFunction(nodeFront, options);
|
||||
|
|
|
@ -11,6 +11,12 @@ const FILE_FOLDER = `browser/devtools/client/webconsole/test/browser`;
|
|||
const TEST_URI = `https://example.com/${FILE_FOLDER}/test-console-evaluation-context-selector.html`;
|
||||
const IFRAME_PATH = `${FILE_FOLDER}/test-console-evaluation-context-selector-child.html`;
|
||||
|
||||
// Import helpers for the inspector
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js",
|
||||
this
|
||||
);
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
add_task(async function() {
|
||||
|
@ -29,7 +35,7 @@ add_task(async function() {
|
|||
await waitForEagerEvaluationResult(hud, `"example.com"`);
|
||||
|
||||
info("Go to the inspector panel");
|
||||
const inspector = await openInspector();
|
||||
const inspector = await hud.toolbox.selectTool("inspector");
|
||||
|
||||
info("Expand all the nodes");
|
||||
await inspector.markup.expandAll();
|
||||
|
@ -38,7 +44,7 @@ add_task(async function() {
|
|||
await hud.toolbox.openSplitConsole();
|
||||
|
||||
info("Select the first iframe h2 element");
|
||||
await selectIframeContentElement(inspector, ".iframe-1", "h2");
|
||||
await selectNodeInFrames([".iframe-1", "h2"], inspector);
|
||||
|
||||
await waitFor(() =>
|
||||
evaluationContextSelectorButton.innerText.includes("example.org")
|
||||
|
@ -49,7 +55,7 @@ add_task(async function() {
|
|||
ok(true, "The instant evaluation result is updated in the iframe context");
|
||||
|
||||
info("Select the second iframe h2 element");
|
||||
await selectIframeContentElement(inspector, ".iframe-2", "h2");
|
||||
await selectNodeInFrames([".iframe-2", "h2"], inspector);
|
||||
|
||||
await waitFor(() =>
|
||||
evaluationContextSelectorButton.innerText.includes("example.net")
|
||||
|
@ -60,9 +66,7 @@ add_task(async function() {
|
|||
ok(true, "The instant evaluation result is updated in the iframe context");
|
||||
|
||||
info("Select an element in the top document");
|
||||
const h1NodeFront = await inspector.walker.findNodeFront(["h1"]);
|
||||
inspector.selection.setNodeFront(null);
|
||||
inspector.selection.setNodeFront(h1NodeFront);
|
||||
await selectNodeInFrames(["h1"], inspector);
|
||||
|
||||
await waitForEagerEvaluationResult(hud, `"example.com"`);
|
||||
await waitFor(() =>
|
||||
|
@ -75,8 +79,7 @@ add_task(async function() {
|
|||
await testUseInConsole(
|
||||
hud,
|
||||
inspector,
|
||||
".iframe-1",
|
||||
"h2",
|
||||
[".iframe-1", "h2"],
|
||||
"temp0",
|
||||
`<h2 id="iframe-1">`
|
||||
);
|
||||
|
@ -91,8 +94,7 @@ add_task(async function() {
|
|||
await testUseInConsole(
|
||||
hud,
|
||||
inspector,
|
||||
".iframe-2",
|
||||
"h2",
|
||||
[".iframe-2", "h2"],
|
||||
"temp0",
|
||||
`<h2 id="iframe-2">`
|
||||
);
|
||||
|
@ -107,8 +109,7 @@ add_task(async function() {
|
|||
await testUseInConsole(
|
||||
hud,
|
||||
inspector,
|
||||
":root",
|
||||
"h1",
|
||||
["h1"],
|
||||
"temp0",
|
||||
`<h1 id="top-level">`
|
||||
);
|
||||
|
@ -118,35 +119,14 @@ add_task(async function() {
|
|||
ok(true, "The context selector was updated");
|
||||
});
|
||||
|
||||
async function selectIframeContentElement(
|
||||
inspector,
|
||||
iframeSelector,
|
||||
iframeContentSelector
|
||||
) {
|
||||
inspector.selection.setNodeFront(null);
|
||||
const iframeNodeFront = await inspector.walker.findNodeFront([
|
||||
iframeSelector,
|
||||
]);
|
||||
const childrenNodeFront = await iframeNodeFront
|
||||
.treeChildren()[0]
|
||||
.walkerFront.findNodeFront([iframeContentSelector]);
|
||||
inspector.selection.setNodeFront(childrenNodeFront);
|
||||
return childrenNodeFront;
|
||||
}
|
||||
|
||||
async function testUseInConsole(
|
||||
hud,
|
||||
inspector,
|
||||
iframeSelector,
|
||||
iframeContentSelector,
|
||||
selectors,
|
||||
variableName,
|
||||
expectedTextResult
|
||||
) {
|
||||
const nodeFront = await selectIframeContentElement(
|
||||
inspector,
|
||||
iframeSelector,
|
||||
iframeContentSelector
|
||||
);
|
||||
const nodeFront = await selectNodeInFrames(selectors, inspector);
|
||||
const container = inspector.markup.getContainer(nodeFront);
|
||||
|
||||
// Clear the input before clicking on "Use in Console" to workaround an bug
|
||||
|
|
|
@ -134,6 +134,98 @@ class InspectorCommand {
|
|||
// Descending sort the list by count, i.e. second element of the arrays
|
||||
return sortSuggestions(mergedSuggestions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a nodeFront from an array of selectors. The last item of the array is the selector
|
||||
* for the element in its owner document, and the previous items are selectors to iframes
|
||||
* that lead to the frame where the searched node lives in.
|
||||
*
|
||||
* For example, with the following markup
|
||||
* <html>
|
||||
* <iframe id="level-1" src="…">
|
||||
* <iframe id="level-2" src="…">
|
||||
* <h1>Waldo</h1>
|
||||
* </iframe>
|
||||
* </iframe>
|
||||
*
|
||||
* If you want to retrieve the `<h1>` nodeFront, `selectors` would be:
|
||||
* [
|
||||
* "#level-1",
|
||||
* "#level-2",
|
||||
* "h1",
|
||||
* ]
|
||||
*
|
||||
* @param {Array} selectors
|
||||
* An array of CSS selectors to find the target accessible object.
|
||||
* Several selectors can be needed if the element is nested in frames
|
||||
* and not directly in the root document.
|
||||
* @return {Promise<NodeFront|null>} a promise that resolves when the node front is found
|
||||
* for selection using inspector tools. It resolves with the deepest frame document
|
||||
* that could be retrieved when the "final" nodeFront couldn't be found in the page.
|
||||
*/
|
||||
async findNodeFrontFromSelectors(nodeSelectors) {
|
||||
if (
|
||||
!nodeSelectors ||
|
||||
!Array.isArray(nodeSelectors) ||
|
||||
nodeSelectors.length === 0
|
||||
) {
|
||||
console.warn(
|
||||
"findNodeFrontFromSelectors expect a non-empty array but got",
|
||||
nodeSelectors
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const { walker } = await this.commands.targetCommand.targetFront.getFront(
|
||||
"inspector"
|
||||
);
|
||||
const querySelectors = async nodeFront => {
|
||||
const selector = nodeSelectors.shift();
|
||||
if (!selector) {
|
||||
return nodeFront;
|
||||
}
|
||||
nodeFront = await nodeFront.walkerFront.querySelector(
|
||||
nodeFront,
|
||||
selector
|
||||
);
|
||||
// It's possible the containing iframe isn't available by the time
|
||||
// walkerFront.querySelector is called, which causes the re-selected node to be
|
||||
// unavailable. There also isn't a way for us to know when all iframes on the page
|
||||
// have been created after a reload. Because of this, we should should bail here.
|
||||
if (!nodeFront) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (nodeSelectors.length > 0) {
|
||||
await nodeFront.waitForFrameLoad();
|
||||
|
||||
const { nodes } = await walker.children(nodeFront);
|
||||
|
||||
// If there are remaining selectors to process, they will target a document or a
|
||||
// document-fragment under the current node. Whether the element is a frame or
|
||||
// a web component, it can only contain one document/document-fragment, so just
|
||||
// select the first one available.
|
||||
nodeFront = nodes.find(node => {
|
||||
const { nodeType } = node;
|
||||
return (
|
||||
nodeType === Node.DOCUMENT_FRAGMENT_NODE ||
|
||||
nodeType === Node.DOCUMENT_NODE
|
||||
);
|
||||
});
|
||||
|
||||
// The iframe selector might have matched an element which is not an
|
||||
// iframe in the new page (or an iframe with no document?). In this
|
||||
// case, bail out and fallback to the root body element.
|
||||
if (!nodeFront) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
const childrenNodeFront = await querySelectors(nodeFront);
|
||||
return childrenNodeFront || nodeFront;
|
||||
};
|
||||
const rootNodeFront = await walker.getRootNode();
|
||||
return querySelectors(rootNodeFront);
|
||||
}
|
||||
}
|
||||
|
||||
// This is a fork of the server sort:
|
||||
|
|
|
@ -7,5 +7,6 @@ support-files =
|
|||
!/devtools/client/shared/test/highlighter-test-actor.js
|
||||
head.js
|
||||
|
||||
[browser_inspector_command_findNodeFrontFromSelectors.js]
|
||||
[browser_inspector_command_getSuggestionsForQuery.js]
|
||||
[browser_inspector_command_search.js]
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(async () => {
|
||||
// Build a simple test page with a remote iframe, using two distinct origins .com and .org
|
||||
const iframeHtml = encodeURIComponent(`<h2 id="in-iframe"></h2>`);
|
||||
const html = encodeURIComponent(
|
||||
`<main class="foo bar">
|
||||
<button id="child">Click</button>
|
||||
</main>
|
||||
<iframe src="https://example.org/document-builder.sjs?html=${iframeHtml}"></iframe>`
|
||||
);
|
||||
const tab = await addTab(
|
||||
"https://example.com/document-builder.sjs?html=" + html
|
||||
);
|
||||
|
||||
const commands = await CommandsFactory.forTab(tab);
|
||||
await commands.targetCommand.startListening();
|
||||
|
||||
info("Check that it returns null when no params are passed");
|
||||
let nodeFront = await commands.inspectorCommand.findNodeFrontFromSelectors();
|
||||
is(
|
||||
nodeFront,
|
||||
null,
|
||||
`findNodeFrontFromSelectors returns null when no param is passed`
|
||||
);
|
||||
|
||||
info("Check that it returns null when a string is passed");
|
||||
nodeFront = await commands.inspectorCommand.findNodeFrontFromSelectors(
|
||||
"body main"
|
||||
);
|
||||
is(
|
||||
nodeFront,
|
||||
null,
|
||||
`findNodeFrontFromSelectors returns null when passed a string`
|
||||
);
|
||||
|
||||
info("Check it returns null when an empty array is passed");
|
||||
nodeFront = await commands.inspectorCommand.findNodeFrontFromSelectors([]);
|
||||
is(
|
||||
nodeFront,
|
||||
null,
|
||||
`findNodeFrontFromSelectors returns null when passed an empty array`
|
||||
);
|
||||
|
||||
info("Check that passing a selector for a non-matching element returns null");
|
||||
nodeFront = await commands.inspectorCommand.findNodeFrontFromSelectors([
|
||||
"h1",
|
||||
]);
|
||||
is(
|
||||
nodeFront,
|
||||
null,
|
||||
"findNodeFrontFromSelectors returns null as there's no <h1> element in the page"
|
||||
);
|
||||
|
||||
info("Check passing a selector for an element in the top document");
|
||||
nodeFront = await commands.inspectorCommand.findNodeFrontFromSelectors([
|
||||
"button",
|
||||
]);
|
||||
is(
|
||||
nodeFront.typeName,
|
||||
"domnode",
|
||||
"findNodeFrontFromSelectors returns a nodeFront"
|
||||
);
|
||||
is(
|
||||
nodeFront.displayName,
|
||||
"button",
|
||||
"findNodeFrontFromSelectors returned the appropriate nodeFront"
|
||||
);
|
||||
|
||||
info("Check passing a selector for an element in an iframe");
|
||||
nodeFront = await commands.inspectorCommand.findNodeFrontFromSelectors([
|
||||
"iframe",
|
||||
"#in-iframe",
|
||||
]);
|
||||
is(
|
||||
nodeFront.displayName,
|
||||
"h2",
|
||||
"findNodeFrontFromSelectors returned the appropriate nodeFront"
|
||||
);
|
||||
|
||||
info(
|
||||
"Check passing a selector for an non-existing element in an existing iframe"
|
||||
);
|
||||
nodeFront = await commands.inspectorCommand.findNodeFrontFromSelectors([
|
||||
"iframe",
|
||||
"#non-existant-id",
|
||||
]);
|
||||
is(
|
||||
nodeFront.displayName,
|
||||
"#document",
|
||||
"findNodeFrontFromSelectors returned the last matching iframe document if the children selector isn't found"
|
||||
);
|
||||
is(
|
||||
nodeFront.parentNode().displayName,
|
||||
"iframe",
|
||||
"findNodeFrontFromSelectors returned the last matching iframe document if the children selector isn't found"
|
||||
);
|
||||
|
||||
await commands.destroy();
|
||||
});
|
Загрузка…
Ссылка в новой задаче