summaryrefslogtreecommitdiffstats
path: root/devtools/client/inspector/test/head.js
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /devtools/client/inspector/test/head.js
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'devtools/client/inspector/test/head.js')
-rw-r--r--devtools/client/inspector/test/head.js732
1 files changed, 732 insertions, 0 deletions
diff --git a/devtools/client/inspector/test/head.js b/devtools/client/inspector/test/head.js
new file mode 100644
index 000000000..f251568df
--- /dev/null
+++ b/devtools/client/inspector/test/head.js
@@ -0,0 +1,732 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+/* import-globals-from ../../framework/test/shared-head.js */
+/* import-globals-from ../../commandline/test/helpers.js */
+/* import-globals-from ../../shared/test/test-actor-registry.js */
+/* import-globals-from ../../inspector/test/shared-head.js */
+"use strict";
+
+// Load the shared-head file first.
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
+ this);
+
+// Services.prefs.setBoolPref("devtools.debugger.log", true);
+// SimpleTest.registerCleanupFunction(() => {
+// Services.prefs.clearUserPref("devtools.debugger.log");
+// });
+
+// Import the GCLI test helper
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/devtools/client/commandline/test/helpers.js",
+ this);
+
+// Import helpers registering the test-actor in remote targets
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/devtools/client/shared/test/test-actor-registry.js",
+ this);
+
+// Import helpers for the inspector that are also shared with others
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js",
+ this);
+
+const {LocalizationHelper} = require("devtools/shared/l10n");
+const INSPECTOR_L10N =
+ new LocalizationHelper("devtools/client/locales/inspector.properties");
+
+flags.testing = true;
+registerCleanupFunction(() => {
+ flags.testing = false;
+});
+
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
+});
+
+registerCleanupFunction(function* () {
+ // Move the mouse outside inspector. If the test happened fake a mouse event
+ // somewhere over inspector the pointer is considered to be there when the
+ // next test begins. This might cause unexpected events to be emitted when
+ // another test moves the mouse.
+ EventUtils.synthesizeMouseAtPoint(1, 1, {type: "mousemove"}, window);
+});
+
+var navigateTo = Task.async(function* (inspector, url) {
+ let markuploaded = inspector.once("markuploaded");
+ let onNewRoot = inspector.once("new-root");
+ let onUpdated = inspector.once("inspector-updated");
+
+ info("Navigating to: " + url);
+ let activeTab = inspector.toolbox.target.activeTab;
+ yield activeTab.navigateTo(url);
+
+ info("Waiting for markup view to load after navigation.");
+ yield markuploaded;
+
+ info("Waiting for new root.");
+ yield onNewRoot;
+
+ info("Waiting for inspector to update after new-root event.");
+ yield onUpdated;
+});
+
+/**
+ * Start the element picker and focus the content window.
+ * @param {Toolbox} toolbox
+ * @param {Boolean} skipFocus - Allow tests to bypass the focus event.
+ */
+var startPicker = Task.async(function* (toolbox, skipFocus) {
+ info("Start the element picker");
+ toolbox.win.focus();
+ yield toolbox.highlighterUtils.startPicker();
+ if (!skipFocus) {
+ // By default make sure the content window is focused since the picker may not focus
+ // the content window by default.
+ yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
+ content.focus();
+ });
+ }
+});
+
+/**
+ * Highlight a node and set the inspector's current selection to the node or
+ * the first match of the given css selector.
+ * @param {String|NodeFront} selector
+ * @param {InspectorPanel} inspector
+ * The instance of InspectorPanel currently loaded in the toolbox
+ * @return a promise that resolves when the inspector is updated with the new
+ * node
+ */
+function selectAndHighlightNode(selector, inspector) {
+ info("Highlighting and selecting the node " + selector);
+ return selectNode(selector, inspector, "test-highlight");
+}
+
+/**
+ * Select node for a given selector, make it focusable and set focus in its
+ * container element.
+ * @param {String|NodeFront} selector
+ * @param {InspectorPanel} inspector The current inspector-panel instance.
+ * @return {MarkupContainer}
+ */
+function* focusNode(selector, inspector) {
+ getContainerForNodeFront(inspector.walker.rootNode, inspector).elt.focus();
+ let nodeFront = yield getNodeFront(selector, inspector);
+ let container = getContainerForNodeFront(nodeFront, inspector);
+ yield selectNode(nodeFront, inspector);
+ EventUtils.sendKey("return", inspector.panelWin);
+ return container;
+}
+
+/**
+ * Set the inspector's current selection to null so that no node is selected
+ *
+ * @param {InspectorPanel} inspector
+ * The instance of InspectorPanel currently loaded in the toolbox
+ * @return a promise that resolves when the inspector is updated
+ */
+function clearCurrentNodeSelection(inspector) {
+ info("Clearing the current selection");
+ let updated = inspector.once("inspector-updated");
+ inspector.selection.setNodeFront(null);
+ return updated;
+}
+
+/**
+ * Open the inspector in a tab with given URL.
+ * @param {string} url The URL to open.
+ * @param {String} hostType Optional hostType, as defined in Toolbox.HostType
+ * @return A promise that is resolved once the tab and inspector have loaded
+ * with an object: { tab, toolbox, inspector }.
+ */
+var openInspectorForURL = Task.async(function* (url, hostType) {
+ let tab = yield addTab(url);
+ let { inspector, toolbox, testActor } = yield openInspector(hostType);
+ return { tab, inspector, toolbox, testActor };
+});
+
+function getActiveInspector() {
+ let target = TargetFactory.forTab(gBrowser.selectedTab);
+ return gDevTools.getToolbox(target).getPanel("inspector");
+}
+
+/**
+ * Right click on a node in the test page and click on the inspect menu item.
+ * @param {TestActor}
+ * @param {String} selector The selector for the node to click on in the page.
+ * @return {Promise} Resolves to the inspector when it has opened and is updated
+ */
+var clickOnInspectMenuItem = Task.async(function* (testActor, selector) {
+ info("Showing the contextual menu on node " + selector);
+ let contentAreaContextMenu = document.querySelector(
+ "#contentAreaContextMenu");
+ let contextOpened = once(contentAreaContextMenu, "popupshown");
+
+ yield testActor.synthesizeMouse({
+ selector: selector,
+ center: true,
+ options: {type: "contextmenu", button: 2}
+ });
+
+ yield contextOpened;
+
+ info("Triggering the inspect action");
+ yield gContextMenu.inspectNode();
+
+ info("Hiding the menu");
+ let contextClosed = once(contentAreaContextMenu, "popuphidden");
+ contentAreaContextMenu.hidePopup();
+ yield contextClosed;
+
+ return getActiveInspector();
+});
+
+/**
+ * Get the NodeFront for a node that matches a given css selector inside a
+ * given iframe.
+ * @param {String|NodeFront} selector
+ * @param {String|NodeFront} frameSelector A selector that matches the iframe
+ * the node is in
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return {Promise} Resolves when the inspector is updated with the new node
+ */
+var getNodeFrontInFrame = Task.async(function* (selector, frameSelector,
+ inspector) {
+ let iframe = yield getNodeFront(frameSelector, inspector);
+ let {nodes} = yield inspector.walker.children(iframe);
+ return inspector.walker.querySelector(nodes[0], selector);
+});
+
+var focusSearchBoxUsingShortcut = Task.async(function* (panelWin, callback) {
+ info("Focusing search box");
+ let searchBox = panelWin.document.getElementById("inspector-searchbox");
+ let focused = once(searchBox, "focus");
+
+ panelWin.focus();
+
+ synthesizeKeyShortcut(INSPECTOR_L10N.getStr("inspector.searchHTML.key"));
+
+ yield focused;
+
+ if (callback) {
+ callback();
+ }
+});
+
+/**
+ * Get the MarkupContainer object instance that corresponds to the given
+ * NodeFront
+ * @param {NodeFront} nodeFront
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return {MarkupContainer}
+ */
+function getContainerForNodeFront(nodeFront, {markup}) {
+ return markup.getContainer(nodeFront);
+}
+
+/**
+ * Get the MarkupContainer object instance that corresponds to the given
+ * selector
+ * @param {String|NodeFront} selector
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return {MarkupContainer}
+ */
+var getContainerForSelector = Task.async(function* (selector, inspector) {
+ info("Getting the markup-container for node " + selector);
+ let nodeFront = yield getNodeFront(selector, inspector);
+ let container = getContainerForNodeFront(nodeFront, inspector);
+ info("Found markup-container " + container);
+ return container;
+});
+
+/**
+ * Simulate a mouse-over on the markup-container (a line in the markup-view)
+ * that corresponds to the selector passed.
+ * @param {String|NodeFront} selector
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return {Promise} Resolves when the container is hovered and the higlighter
+ * is shown on the corresponding node
+ */
+var hoverContainer = Task.async(function* (selector, inspector) {
+ info("Hovering over the markup-container for node " + selector);
+
+ let nodeFront = yield getNodeFront(selector, inspector);
+ let container = getContainerForNodeFront(nodeFront, inspector);
+
+ let highlit = inspector.toolbox.once("node-highlight");
+ EventUtils.synthesizeMouseAtCenter(container.tagLine, {type: "mousemove"},
+ inspector.markup.doc.defaultView);
+ return highlit;
+});
+
+/**
+ * Simulate a click on the markup-container (a line in the markup-view)
+ * that corresponds to the selector passed.
+ * @param {String|NodeFront} selector
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return {Promise} Resolves when the node has been selected.
+ */
+var clickContainer = Task.async(function* (selector, inspector) {
+ info("Clicking on the markup-container for node " + selector);
+
+ let nodeFront = yield getNodeFront(selector, inspector);
+ let container = getContainerForNodeFront(nodeFront, inspector);
+
+ let updated = inspector.once("inspector-updated");
+ EventUtils.synthesizeMouseAtCenter(container.tagLine, {type: "mousedown"},
+ inspector.markup.doc.defaultView);
+ EventUtils.synthesizeMouseAtCenter(container.tagLine, {type: "mouseup"},
+ inspector.markup.doc.defaultView);
+ return updated;
+});
+
+/**
+ * Simulate the mouse leaving the markup-view area
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return a promise when done
+ */
+function mouseLeaveMarkupView(inspector) {
+ info("Leaving the markup-view area");
+ let def = defer();
+
+ // Find another element to mouseover over in order to leave the markup-view
+ let btn = inspector.toolbox.doc.querySelector("#toolbox-controls");
+
+ EventUtils.synthesizeMouseAtCenter(btn, {type: "mousemove"},
+ inspector.toolbox.win);
+ executeSoon(def.resolve);
+
+ return def.promise;
+}
+
+/**
+ * Dispatch the copy event on the given element
+ */
+function fireCopyEvent(element) {
+ let evt = element.ownerDocument.createEvent("Event");
+ evt.initEvent("copy", true, true);
+ element.dispatchEvent(evt);
+}
+
+/**
+ * Undo the last markup-view action and wait for the corresponding mutation to
+ * occur
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return a promise that resolves when the markup-mutation has been treated or
+ * rejects if no undo action is possible
+ */
+function undoChange(inspector) {
+ let canUndo = inspector.markup.undo.canUndo();
+ ok(canUndo, "The last change in the markup-view can be undone");
+ if (!canUndo) {
+ return promise.reject();
+ }
+
+ let mutated = inspector.once("markupmutation");
+ inspector.markup.undo.undo();
+ return mutated;
+}
+
+/**
+ * Redo the last markup-view action and wait for the corresponding mutation to
+ * occur
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return a promise that resolves when the markup-mutation has been treated or
+ * rejects if no redo action is possible
+ */
+function redoChange(inspector) {
+ let canRedo = inspector.markup.undo.canRedo();
+ ok(canRedo, "The last change in the markup-view can be redone");
+ if (!canRedo) {
+ return promise.reject();
+ }
+
+ let mutated = inspector.once("markupmutation");
+ inspector.markup.undo.redo();
+ return mutated;
+}
+
+/**
+ * A helper that fetches a front for a node that matches the given selector or
+ * doctype node if the selector is falsy.
+ */
+function* getNodeFrontForSelector(selector, inspector) {
+ if (selector) {
+ info("Retrieving front for selector " + selector);
+ return getNodeFront(selector, inspector);
+ }
+
+ info("Retrieving front for doctype node");
+ let {nodes} = yield inspector.walker.children(inspector.walker.rootNode);
+ return nodes[0];
+}
+
+/**
+ * A simple polling helper that executes a given function until it returns true.
+ * @param {Function} check A generator function that is expected to return true at some
+ * stage.
+ * @param {String} desc A text description to be displayed when the polling starts.
+ * @param {Number} attemptes Optional number of times we poll. Defaults to 10.
+ * @param {Number} timeBetweenAttempts Optional time to wait between each attempt.
+ * Defaults to 200ms.
+ */
+function* poll(check, desc, attempts = 10, timeBetweenAttempts = 200) {
+ info(desc);
+
+ for (let i = 0; i < attempts; i++) {
+ if (yield check()) {
+ return;
+ }
+ yield new Promise(resolve => setTimeout(resolve, timeBetweenAttempts));
+ }
+
+ throw new Error(`Timeout while: ${desc}`);
+}
+
+/**
+ * Encapsulate some common operations for highlighter's tests, to have
+ * the tests cleaner, without exposing directly `inspector`, `highlighter`, and
+ * `testActor` if not needed.
+ *
+ * @param {String}
+ * The highlighter's type
+ * @return
+ * A generator function that takes an object with `inspector` and `testActor`
+ * properties. (see `openInspector`)
+ */
+const getHighlighterHelperFor = (type) => Task.async(
+ function* ({inspector, testActor}) {
+ let front = inspector.inspector;
+ let highlighter = yield front.getHighlighterByType(type);
+
+ let prefix = "";
+
+ // Internals for mouse events
+ let prevX, prevY;
+
+ // Highlighted node
+ let highlightedNode = null;
+
+ return {
+ set prefix(value) {
+ prefix = value;
+ },
+
+ get highlightedNode() {
+ if (!highlightedNode) {
+ return null;
+ }
+
+ return {
+ getComputedStyle: function* (options = {}) {
+ return yield inspector.pageStyle.getComputed(
+ highlightedNode, options);
+ }
+ };
+ },
+
+ show: function* (selector = ":root", options) {
+ highlightedNode = yield getNodeFront(selector, inspector);
+ return yield highlighter.show(highlightedNode, options);
+ },
+
+ hide: function* () {
+ yield highlighter.hide();
+ },
+
+ isElementHidden: function* (id) {
+ return (yield testActor.getHighlighterNodeAttribute(
+ prefix + id, "hidden", highlighter)) === "true";
+ },
+
+ getElementTextContent: function* (id) {
+ return yield testActor.getHighlighterNodeTextContent(
+ prefix + id, highlighter);
+ },
+
+ getElementAttribute: function* (id, name) {
+ return yield testActor.getHighlighterNodeAttribute(
+ prefix + id, name, highlighter);
+ },
+
+ waitForElementAttributeSet: function* (id, name) {
+ yield poll(function* () {
+ let value = yield testActor.getHighlighterNodeAttribute(
+ prefix + id, name, highlighter);
+ return !!value;
+ }, `Waiting for element ${id} to have attribute ${name} set`);
+ },
+
+ waitForElementAttributeRemoved: function* (id, name) {
+ yield poll(function* () {
+ let value = yield testActor.getHighlighterNodeAttribute(
+ prefix + id, name, highlighter);
+ return !value;
+ }, `Waiting for element ${id} to have attribute ${name} removed`);
+ },
+
+ synthesizeMouse: function* (options) {
+ options = Object.assign({selector: ":root"}, options);
+ yield testActor.synthesizeMouse(options);
+ },
+
+ // This object will synthesize any "mouse" prefixed event to the
+ // `testActor`, using the name of method called as suffix for the
+ // event's name.
+ // If no x, y coords are given, the previous ones are used.
+ //
+ // For example:
+ // mouse.down(10, 20); // synthesize "mousedown" at 10,20
+ // mouse.move(20, 30); // synthesize "mousemove" at 20,30
+ // mouse.up(); // synthesize "mouseup" at 20,30
+ mouse: new Proxy({}, {
+ get: (target, name) =>
+ function* (x = prevX, y = prevY) {
+ prevX = x;
+ prevY = y;
+ yield testActor.synthesizeMouse({
+ selector: ":root", x, y, options: {type: "mouse" + name}});
+ }
+ }),
+
+ reflow: function* () {
+ yield testActor.reflow();
+ },
+
+ finalize: function* () {
+ highlightedNode = null;
+ yield highlighter.finalize();
+ }
+ };
+ }
+);
+
+// The expand all operation of the markup-view calls itself recursively and
+// there's not one event we can wait for to know when it's done so use this
+// helper function to wait until all recursive children updates are done.
+function* waitForMultipleChildrenUpdates(inspector) {
+ // As long as child updates are queued up while we wait for an update already
+ // wait again
+ if (inspector.markup._queuedChildUpdates &&
+ inspector.markup._queuedChildUpdates.size) {
+ yield waitForChildrenUpdated(inspector);
+ return yield waitForMultipleChildrenUpdates(inspector);
+ }
+ return null;
+}
+
+/**
+ * Using the markupview's _waitForChildren function, wait for all queued
+ * children updates to be handled.
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return a promise that resolves when all queued children updates have been
+ * handled
+ */
+function waitForChildrenUpdated({markup}) {
+ info("Waiting for queued children updates to be handled");
+ let def = defer();
+ markup._waitForChildren().then(() => {
+ executeSoon(def.resolve);
+ });
+ return def.promise;
+}
+
+/**
+ * Wait for the toolbox to emit the styleeditor-selected event and when done
+ * wait for the stylesheet identified by href to be loaded in the stylesheet
+ * editor
+ *
+ * @param {Toolbox} toolbox
+ * @param {String} href
+ * Optional, if not provided, wait for the first editor to be ready
+ * @return a promise that resolves to the editor when the stylesheet editor is
+ * ready
+ */
+function waitForStyleEditor(toolbox, href) {
+ let def = defer();
+
+ info("Waiting for the toolbox to switch to the styleeditor");
+ toolbox.once("styleeditor-selected").then(() => {
+ let panel = toolbox.getCurrentPanel();
+ ok(panel && panel.UI, "Styleeditor panel switched to front");
+
+ // A helper that resolves the promise once it receives an editor that
+ // matches the expected href. Returns false if the editor was not correct.
+ let gotEditor = (event, editor) => {
+ let currentHref = editor.styleSheet.href;
+ if (!href || (href && currentHref.endsWith(href))) {
+ info("Stylesheet editor selected");
+ panel.UI.off("editor-selected", gotEditor);
+
+ editor.getSourceEditor().then(sourceEditor => {
+ info("Stylesheet editor fully loaded");
+ def.resolve(sourceEditor);
+ });
+
+ return true;
+ }
+
+ info("The editor was incorrect. Waiting for editor-selected event.");
+ return false;
+ };
+
+ // The expected editor may already be selected. Check the if the currently
+ // selected editor is the expected one and if not wait for an
+ // editor-selected event.
+ if (!gotEditor("styleeditor-selected", panel.UI.selectedEditor)) {
+ // The expected editor is not selected (yet). Wait for it.
+ panel.UI.on("editor-selected", gotEditor);
+ }
+ });
+
+ return def.promise;
+}
+
+/**
+ * Checks if document's active element is within the given element.
+ * @param {HTMLDocument} doc document with active element in question
+ * @param {DOMNode} container element tested on focus containment
+ * @return {Boolean}
+ */
+function containsFocus(doc, container) {
+ let elm = doc.activeElement;
+ while (elm) {
+ if (elm === container) {
+ return true;
+ }
+ elm = elm.parentNode;
+ }
+ return false;
+}
+
+/**
+ * Listen for a new tab to open and return a promise that resolves when one
+ * does and completes the load event.
+ *
+ * @return a promise that resolves to the tab object
+ */
+var waitForTab = Task.async(function* () {
+ info("Waiting for a tab to open");
+ yield once(gBrowser.tabContainer, "TabOpen");
+ let tab = gBrowser.selectedTab;
+ yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ info("The tab load completed");
+ return tab;
+});
+
+/**
+ * Simulate the key input for the given input in the window.
+ *
+ * @param {String} input
+ * The string value to input
+ * @param {Window} win
+ * The window containing the panel
+ */
+function synthesizeKeys(input, win) {
+ for (let key of input.split("")) {
+ EventUtils.synthesizeKey(key, {}, win);
+ }
+}
+
+/**
+ * Given a tooltip object instance (see Tooltip.js), checks if it is set to
+ * toggle and hover and if so, checks if the given target is a valid hover
+ * target. This won't actually show the tooltip (the less we interact with XUL
+ * panels during test runs, the better).
+ *
+ * @return a promise that resolves when the answer is known
+ */
+function isHoverTooltipTarget(tooltip, target) {
+ if (!tooltip._toggle._baseNode || !tooltip.panel) {
+ return promise.reject(new Error(
+ "The tooltip passed isn't set to toggle on hover or is not a tooltip"));
+ }
+ return tooltip._toggle.isValidHoverTarget(target);
+}
+
+/**
+ * Same as isHoverTooltipTarget except that it will fail the test if there is no
+ * tooltip defined on hover of the given element
+ *
+ * @return a promise
+ */
+function assertHoverTooltipOn(tooltip, element) {
+ return isHoverTooltipTarget(tooltip, element).then(() => {
+ ok(true, "A tooltip is defined on hover of the given element");
+ }, () => {
+ ok(false, "No tooltip is defined on hover of the given element");
+ });
+}
+
+/**
+ * Open the inspector menu and return all of it's items in a flat array
+ * @param {InspectorPanel} inspector
+ * @param {Object} options to pass into openMenu
+ * @return An array of MenuItems
+ */
+function openContextMenuAndGetAllItems(inspector, options) {
+ let menu = inspector._openMenu(options);
+
+ // Flatten all menu items into a single array to make searching through it easier
+ let allItems = [].concat.apply([], menu.items.map(function addItem(item) {
+ if (item.submenu) {
+ return addItem(item.submenu.items);
+ }
+ return item;
+ }));
+
+ return allItems;
+}
+
+/**
+ * Get the rule editor from the rule-view given its index
+ *
+ * @param {CssRuleView} view
+ * The instance of the rule-view panel
+ * @param {Number} childrenIndex
+ * The children index of the element to get
+ * @param {Number} nodeIndex
+ * The child node index of the element to get
+ * @return {DOMNode} The rule editor if any at this index
+ */
+function getRuleViewRuleEditor(view, childrenIndex, nodeIndex) {
+ return nodeIndex !== undefined ?
+ view.element.children[childrenIndex].childNodes[nodeIndex]._ruleEditor :
+ view.element.children[childrenIndex]._ruleEditor;
+}
+
+/**
+ * Get the text displayed for a given DOM Element's textContent within the
+ * markup view.
+ *
+ * @param {String} selector
+ * @param {InspectorPanel} inspector
+ * @return {String} The text displayed in the markup view
+ */
+function* getDisplayedNodeTextContent(selector, inspector) {
+ // We have to ensure that the textContent is displayed, for that the DOM
+ // Element has to be selected in the markup view and to be expanded.
+ yield selectNode(selector, inspector);
+
+ let container = yield getContainerForSelector(selector, inspector);
+ yield inspector.markup.expandNode(container.node);
+ yield waitForMultipleChildrenUpdates(inspector);
+ if (container) {
+ let textContainer = container.elt.querySelector("pre");
+ return textContainer.textContent;
+ }
+ return null;
+}