--- a/devtools/client/inspector/breadcrumbs.js
+++ b/devtools/client/inspector/breadcrumbs.js
@@ -810,20 +810,16 @@ HTMLBreadcrumbs.prototype = {
* @param {Array} mutations An array of mutations in case this was called as
* the "markupmutation" event listener.
*/
update: function (reason, mutations) {
if (this.isDestroyed) {
return;
}
- if (reason !== "markupmutation") {
- this.inspector.hideNodeMenu();
- }
-
let hasInterestingMutations = this._hasInterestingMutations(mutations);
if (reason === "markupmutation" && !hasInterestingMutations) {
return;
}
let cmdDispatcher = this.chromeDoc.commandDispatcher;
this.hadFocus = (cmdDispatcher.focusedElement &&
cmdDispatcher.focusedElement.parentNode == this.container);
--- a/devtools/client/inspector/inspector-panel.js
+++ b/devtools/client/inspector/inspector-panel.js
@@ -15,16 +15,19 @@ var promise = require("promise");
var EventEmitter = require("devtools/shared/event-emitter");
var clipboard = require("sdk/clipboard");
const {executeSoon} = require("devtools/shared/DevToolsUtils");
var {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
var {Task} = require("devtools/shared/task");
const {initCssProperties} = require("devtools/shared/fronts/css-properties");
const nodeConstants = require("devtools/shared/dom-node-constants");
+const Menu = require("devtools/client/framework/menu");
+const MenuItem = require("devtools/client/framework/menu-item");
+
loader.lazyRequireGetter(this, "CSS", "CSS");
loader.lazyRequireGetter(this, "CommandUtils", "devtools/client/shared/developer-toolbar", true);
loader.lazyRequireGetter(this, "ComputedViewTool", "devtools/client/inspector/computed/computed", true);
loader.lazyRequireGetter(this, "FontInspector", "devtools/client/inspector/fonts/fonts", true);
loader.lazyRequireGetter(this, "HTMLBreadcrumbs", "devtools/client/inspector/breadcrumbs", true);
loader.lazyRequireGetter(this, "InspectorSearch", "devtools/client/inspector/inspector-search", true);
loader.lazyRequireGetter(this, "LayoutView", "devtools/client/inspector/layout/layout", true);
@@ -85,33 +88,33 @@ function InspectorPanel(iframeWindow, to
this.panelDoc = iframeWindow.document;
this.panelWin = iframeWindow;
this.panelWin.inspector = this;
this.nodeMenuTriggerInfo = null;
this._onBeforeNavigate = this._onBeforeNavigate.bind(this);
this.onNewRoot = this.onNewRoot.bind(this);
- this._setupNodeMenu = this._setupNodeMenu.bind(this);
- this._resetNodeMenu = this._resetNodeMenu.bind(this);
+ this._onContextMenu = this._onContextMenu.bind(this);
this._updateSearchResultsLabel = this._updateSearchResultsLabel.bind(this);
this.onNewSelection = this.onNewSelection.bind(this);
this.onBeforeNewSelection = this.onBeforeNewSelection.bind(this);
this.onDetached = this.onDetached.bind(this);
this.onPaneToggleButtonClicked = this.onPaneToggleButtonClicked.bind(this);
this._onMarkupFrameLoad = this._onMarkupFrameLoad.bind(this);
let doc = this.panelDoc;
// Handle 'Add Node' toolbar button.
this.addNode = this.addNode.bind(this);
this.addNodeButton = doc.getElementById("inspector-element-add-button");
this.addNodeButton.addEventListener("click", this.addNode);
this._target.on("will-navigate", this._onBeforeNavigate);
+ this._detectingActorFeatures = this._detectActorFeatures();
EventEmitter.decorate(this);
}
exports.InspectorPanel = InspectorPanel;
InspectorPanel.prototype = {
/**
@@ -157,26 +160,42 @@ InspectorPanel.prototype = {
get canGetUsedFontFaces() {
return this._target.client.traits.getUsedFontFaces;
},
get canPasteInnerOrAdjacentHTML() {
return this._target.client.traits.pasteHTML;
},
+ /**
+ * Figure out what features the backend supports
+ */
+ _detectActorFeatures: function () {
+ this._supportsDuplicateNode = false;
+ this._supportsScrollIntoView = false;
+ this._supportsResolveRelativeURL = false;
+
+ return promise.all([
+ this._target.actorHasMethod("domwalker", "duplicateNode").then(value => {
+ this._supportsDuplicateNode = value;
+ }).catch(e => console.error(e)),
+ this._target.actorHasMethod("domnode", "scrollIntoView").then(value => {
+ this._supportsScrollIntoView = value;
+ }).catch(e => console.error(e)),
+ this._target.actorHasMethod("inspector", "resolveRelativeURL").then(value => {
+ this._supportsResolveRelativeURL = value;
+ }).catch(e => console.error(e)),
+ ]);
+ },
+
_deferredOpen: function (defaultSelection) {
let deferred = promise.defer();
this.walker.on("new-root", this.onNewRoot);
- this.nodemenu = this.panelDoc.getElementById("inspector-node-popup");
- this.lastNodemenuItem = this.nodemenu.lastChild;
- this.nodemenu.addEventListener("popupshowing", this._setupNodeMenu, true);
- this.nodemenu.addEventListener("popuphiding", this._resetNodeMenu, true);
-
this.selection.on("new-node-front", this.onNewSelection);
this.selection.on("before-new-node-front", this.onBeforeNewSelection);
this.selection.on("detached-front", this.onDetached);
this.breadcrumbs = new HTMLBreadcrumbs(this);
if (this.target.isLocalTab) {
// Show a warning when the debugger is paused.
@@ -663,353 +682,417 @@ InspectorPanel.prototype = {
}
});
this.sidebar.off("select", this._setDefaultSidebar);
let sidebarDestroyer = this.sidebar.destroy();
this.sidebar = null;
this.addNodeButton.removeEventListener("click", this.addNode);
-
- this.nodemenu.removeEventListener("popupshowing", this._setupNodeMenu, true);
- this.nodemenu.removeEventListener("popuphiding", this._resetNodeMenu, true);
this.breadcrumbs.destroy();
this._paneToggleButton.removeEventListener("mousedown",
this.onPaneToggleButtonClicked);
this._paneToggleButton = null;
this.selection.off("new-node-front", this.onNewSelection);
this.selection.off("before-new-node", this.onBeforeNewSelection);
this.selection.off("before-new-node-front", this.onBeforeNewSelection);
this.selection.off("detached-front", this.onDetached);
let markupDestroyer = this._destroyMarkup();
this.panelWin.inspector = null;
this.target = null;
this.panelDoc = null;
this.panelWin = null;
this.breadcrumbs = null;
- this.lastNodemenuItem = null;
- this.nodemenu = null;
this._toolbox = null;
this.search.destroy();
this.search = null;
this.searchBox = null;
this._panelDestroyer = promise.all([
sidebarDestroyer,
markupDestroyer,
cssPropertiesDestroyer
]);
return this._panelDestroyer;
},
/**
- * Show the node menu.
- */
- showNodeMenu: function (button, position, extraItems) {
- if (extraItems) {
- for (let item of extraItems) {
- this.nodemenu.appendChild(item);
- }
- }
- this.nodemenu.openPopup(button, position, 0, 0, true, false);
- },
-
- hideNodeMenu: function () {
- this.nodemenu.hidePopup();
- },
-
- /**
* Returns the clipboard content if it is appropriate for pasting
* into the current node's outer HTML, otherwise returns null.
*/
_getClipboardContentForPaste: function () {
let flavors = clipboard.currentFlavors;
if (flavors.indexOf("text") != -1 ||
(flavors.indexOf("html") != -1 && flavors.indexOf("image") == -1)) {
let content = clipboard.get();
if (content && content.trim().length > 0) {
return content;
}
}
return null;
},
- /**
- * Update, enable, disable, hide, show any menu item depending on the current
- * element.
- */
- _setupNodeMenu: function (event) {
+ _onContextMenu: function (e) {
+ e.preventDefault();
+ this._openMenu({
+ screenX: e.screenX,
+ screenY: e.screenY,
+ target: e.target,
+ });
+ },
+
+ _openMenu: function ({ target, screenX = 0, screenY = 0 } = { }) {
let markupContainer = this.markup.getContainer(this.selection.nodeFront);
- this.nodeMenuTriggerInfo =
- markupContainer.editor.getInfoAtNode(event.target.triggerNode);
+
+ this.contextMenuTarget = target;
+ this.nodeMenuTriggerInfo = markupContainer &&
+ markupContainer.editor.getInfoAtNode(target);
let isSelectionElement = this.selection.isElementNode() &&
!this.selection.isPseudoElementNode();
let isEditableElement = isSelectionElement &&
!this.selection.isAnonymousNode();
let isDuplicatableElement = isSelectionElement &&
!this.selection.isAnonymousNode() &&
!this.selection.isRoot();
let isScreenshotable = isSelectionElement &&
this.canGetUniqueSelector &&
this.selection.nodeFront.isTreeDisplayed;
+ let menu = new Menu();
+ menu.append(new MenuItem({
+ id: "node-menu-edithtml",
+ label: strings.GetStringFromName("inspectorHTMLEdit.label"),
+ accesskey: strings.GetStringFromName("inspectorHTMLEdit.accesskey"),
+ disabled: !isEditableElement || !this.isOuterHTMLEditable,
+ click: () => this.editHTML(),
+ }));
+ menu.append(new MenuItem({
+ id: "node-menu-add",
+ label: strings.GetStringFromName("inspectorAddNode.label"),
+ accesskey: strings.GetStringFromName("inspectorAddNode.accesskey"),
+ disabled: !this.canAddHTMLChild(),
+ click: () => this.addNode(),
+ }));
+ menu.append(new MenuItem({
+ id: "node-menu-duplicatenode",
+ label: strings.GetStringFromName("inspectorDuplicateNode.label"),
+ hidden: !this._supportsDuplicateNode,
+ disabled: !isDuplicatableElement,
+ click: () => this.duplicateNode(),
+ }));
+ menu.append(new MenuItem({
+ id: "node-menu-delete",
+ label: strings.GetStringFromName("inspectorHTMLDelete.label"),
+ accesskey: strings.GetStringFromName("inspectorHTMLDelete.accesskey"),
+ disabled: !isEditableElement,
+ click: () => this.deleteNode(),
+ }));
+
+ menu.append(new MenuItem({
+ label: strings.GetStringFromName("inspectorAttributesSubmenu.label"),
+ accesskey:
+ strings.GetStringFromName("inspectorAttributesSubmenu.accesskey"),
+ submenu: this._getAttributesSubmenu(isEditableElement),
+ }));
+
+ menu.append(new MenuItem({
+ type: "separator",
+ }));
+
// Set the pseudo classes
for (let name of ["hover", "active", "focus"]) {
- let menu = this.panelDoc.getElementById("node-menu-pseudo-" + name);
+ let menuitem = new MenuItem({
+ id: "node-menu-pseudo-" + name,
+ label: name,
+ type: "checkbox",
+ click: this.togglePseudoClass.bind(this, ":" + name),
+ });
if (isSelectionElement) {
let checked = this.selection.nodeFront.hasPseudoClassLock(":" + name);
- menu.setAttribute("checked", checked);
- menu.removeAttribute("disabled");
+ menuitem.checked = checked;
} else {
- menu.setAttribute("disabled", "true");
+ menuitem.disabled = true;
}
- }
-
- // Disable delete item if needed
- let deleteNode = this.panelDoc.getElementById("node-menu-delete");
- if (isEditableElement) {
- deleteNode.removeAttribute("disabled");
- } else {
- deleteNode.setAttribute("disabled", "true");
- }
-
- // Disable add item if needed
- let addNode = this.panelDoc.getElementById("node-menu-add");
- if (this.canAddHTMLChild()) {
- addNode.removeAttribute("disabled");
- } else {
- addNode.setAttribute("disabled", "true");
- }
-
- // Disable / enable "Copy Unique Selector", "Copy inner HTML",
- // "Copy outer HTML", "Scroll Into View" & "Screenshot Node" as appropriate
- let unique = this.panelDoc.getElementById("node-menu-copyuniqueselector");
- let screenshot = this.panelDoc.getElementById("node-menu-screenshotnode");
- let duplicateNode = this.panelDoc.getElementById("node-menu-duplicatenode");
- let copyInnerHTML = this.panelDoc.getElementById("node-menu-copyinner");
- let copyOuterHTML = this.panelDoc.getElementById("node-menu-copyouter");
- let scrollIntoView = this.panelDoc.getElementById("node-menu-scrollnodeintoview");
- let expandAll = this.panelDoc.getElementById("node-menu-expand");
- let collapse = this.panelDoc.getElementById("node-menu-collapse");
-
- expandAll.setAttribute("disabled", "true");
- collapse.setAttribute("disabled", "true");
- if (this.selection.isNode() && markupContainer.hasChildren) {
- if (markupContainer.expanded) {
- collapse.removeAttribute("disabled");
- }
- expandAll.removeAttribute("disabled");
- }
-
- this._target.actorHasMethod("domwalker", "duplicateNode").then(value => {
- duplicateNode.hidden = !value;
- });
- this._target.actorHasMethod("domnode", "scrollIntoView").then(value => {
- scrollIntoView.hidden = !value;
- });
-
- if (isDuplicatableElement) {
- duplicateNode.removeAttribute("disabled");
- } else {
- duplicateNode.setAttribute("disabled", "true");
- }
-
- if (isSelectionElement) {
- unique.removeAttribute("disabled");
- copyInnerHTML.removeAttribute("disabled");
- copyOuterHTML.removeAttribute("disabled");
- scrollIntoView.removeAttribute("disabled");
- } else {
- unique.setAttribute("disabled", "true");
- copyInnerHTML.setAttribute("disabled", "true");
- copyOuterHTML.setAttribute("disabled", "true");
- scrollIntoView.setAttribute("disabled", "true");
- }
- if (!this.canGetUniqueSelector) {
- unique.hidden = true;
- }
-
- if (isScreenshotable) {
- screenshot.removeAttribute("disabled");
- } else {
- screenshot.setAttribute("disabled", "true");
+ menu.append(menuitem);
}
- // Enable/Disable the link open/copy items.
- this._setupNodeLinkMenu();
+ menu.append(new MenuItem({
+ type: "separator",
+ }));
- // Enable the "edit HTML" item if the selection is an element and the root
- // actor has the appropriate trait (isOuterHTMLEditable)
- let editHTML = this.panelDoc.getElementById("node-menu-edithtml");
- if (isEditableElement && this.isOuterHTMLEditable) {
- editHTML.removeAttribute("disabled");
- } else {
- editHTML.setAttribute("disabled", "true");
- }
+ let copySubmenu = new Menu();
+ copySubmenu.append(new MenuItem({
+ id: "node-menu-copyinner",
+ label: strings.GetStringFromName("inspectorCopyInnerHTML.label"),
+ accesskey: strings.GetStringFromName("inspectorCopyInnerHTML.accesskey"),
+ disabled: !isSelectionElement,
+ click: () => this.copyInnerHTML(),
+ }));
+ copySubmenu.append(new MenuItem({
+ id: "node-menu-copyouter",
+ label: strings.GetStringFromName("inspectorCopyOuterHTML.label"),
+ accesskey: strings.GetStringFromName("inspectorCopyOuterHTML.accesskey"),
+ disabled: !isSelectionElement,
+ click: () => this.copyOuterHTML(),
+ }));
+ copySubmenu.append(new MenuItem({
+ id: "node-menu-copyuniqueselector",
+ label: strings.GetStringFromName("inspectorCopyCSSSelector.label"),
+ accesskey:
+ strings.GetStringFromName("inspectorCopyCSSSelector.accesskey"),
+ disabled: !isSelectionElement,
+ hidden: !this.canGetUniqueSelector,
+ click: () => this.copyUniqueSelector(),
+ }));
+ copySubmenu.append(new MenuItem({
+ id: "node-menu-copyimagedatauri",
+ label: strings.GetStringFromName("inspectorImageDataUri.label"),
+ disabled: !isSelectionElement || !markupContainer ||
+ !markupContainer.isPreviewable(),
+ click: () => this.copyImageDataUri(),
+ }));
- let pasteOuterHTML = this.panelDoc.getElementById("node-menu-pasteouterhtml");
- let pasteInnerHTML = this.panelDoc.getElementById("node-menu-pasteinnerhtml");
- let pasteBefore = this.panelDoc.getElementById("node-menu-pastebefore");
- let pasteAfter = this.panelDoc.getElementById("node-menu-pasteafter");
- let pasteFirstChild = this.panelDoc.getElementById("node-menu-pastefirstchild");
- let pasteLastChild = this.panelDoc.getElementById("node-menu-pastelastchild");
+ menu.append(new MenuItem({
+ label: strings.GetStringFromName("inspectorCopyHTMLSubmenu.label"),
+ submenu: copySubmenu,
+ }));
+
+ menu.append(new MenuItem({
+ label: strings.GetStringFromName("inspectorPasteHTMLSubmenu.label"),
+ submenu: this._getPasteSubmenu(isEditableElement),
+ }));
+
+ menu.append(new MenuItem({
+ type: "separator",
+ }));
- // Is the clipboard content appropriate? Is the element editable?
- if (isEditableElement && this._getClipboardContentForPaste()) {
- pasteInnerHTML.disabled = !this.canPasteInnerOrAdjacentHTML;
- // Enable the "paste outer HTML" item if the selection is an element and
- // the root actor has the appropriate trait (isOuterHTMLEditable).
- pasteOuterHTML.disabled = !this.isOuterHTMLEditable;
- // Don't paste before / after a root or a BODY or a HEAD element.
- pasteBefore.disabled = pasteAfter.disabled =
- !this.canPasteInnerOrAdjacentHTML || this.selection.isRoot() ||
- this.selection.isBodyNode() || this.selection.isHeadNode();
- // Don't paste as a first / last child of a HTML document element.
- pasteFirstChild.disabled = pasteLastChild.disabled =
- !this.canPasteInnerOrAdjacentHTML || (this.selection.isHTMLNode() &&
- this.selection.isRoot());
- } else {
- pasteOuterHTML.disabled = true;
- pasteInnerHTML.disabled = true;
- pasteBefore.disabled = true;
- pasteAfter.disabled = true;
- pasteFirstChild.disabled = true;
- pasteLastChild.disabled = true;
+ let isNodeWithChildren = this.selection.isNode() &&
+ markupContainer.hasChildren;
+ menu.append(new MenuItem({
+ id: "node-menu-expand",
+ label: strings.GetStringFromName("inspectorExpandNode.label"),
+ disabled: !isNodeWithChildren || markupContainer.expanded,
+ click: () => this.expandNode(),
+ }));
+ menu.append(new MenuItem({
+ id: "node-menu-collapse",
+ label: strings.GetStringFromName("inspectorCollapseNode.label"),
+ disabled: !isNodeWithChildren || !markupContainer.expanded,
+ click: () => this.collapseNode(),
+ }));
+
+ menu.append(new MenuItem({
+ type: "separator",
+ }));
+
+ menu.append(new MenuItem({
+ id: "node-menu-scrollnodeintoview",
+ label: strings.GetStringFromName("inspectorScrollNodeIntoView.label"),
+ accesskey:
+ strings.GetStringFromName("inspectorScrollNodeIntoView.accesskey"),
+ hidden: !this._supportsScrollIntoView,
+ disabled: !isSelectionElement,
+ click: () => this.scrollNodeIntoView(),
+ }));
+ menu.append(new MenuItem({
+ id: "node-menu-screenshotnode",
+ label: strings.GetStringFromName("inspectorScreenshotNode.label"),
+ disabled: !isScreenshotable,
+ click: () => this.screenshotNode(),
+ }));
+ menu.append(new MenuItem({
+ id: "node-menu-useinconsole",
+ label: strings.GetStringFromName("inspectorUseInConsole.label"),
+ click: () => this.useInConsole(),
+ }));
+ menu.append(new MenuItem({
+ id: "node-menu-showdomproperties",
+ label: strings.GetStringFromName("inspectorShowDOMProperties.label"),
+ click: () => this.showDOMProperties(),
+ }));
+
+ let nodeLinkMenuItems = this._getNodeLinkMenuItems();
+ if (nodeLinkMenuItems.length > 0) {
+ menu.append(new MenuItem({
+ id: "node-menu-link-separator",
+ type: "separator",
+ }));
}
- // Enable the "copy image data-uri" item if the selection is previewable
- // which essentially checks if it's an image or canvas tag
- let copyImageData = this.panelDoc.getElementById("node-menu-copyimagedatauri");
- if (isSelectionElement && markupContainer && markupContainer.isPreviewable()) {
- copyImageData.removeAttribute("disabled");
- } else {
- copyImageData.setAttribute("disabled", "true");
- }
-
- // Enable / disable "Add Attribute", "Edit Attribute"
- // and "Remove Attribute" items
- this._setupAttributeMenu(isEditableElement);
- },
-
- _setupAttributeMenu: function (isEditableElement) {
- let addAttribute = this.panelDoc.getElementById("node-menu-add-attribute");
- let editAttribute = this.panelDoc.getElementById("node-menu-edit-attribute");
- let removeAttribute = this.panelDoc.getElementById("node-menu-remove-attribute");
- let nodeInfo = this.nodeMenuTriggerInfo;
-
- // Enable "Add Attribute" for all editable elements
- if (isEditableElement) {
- addAttribute.removeAttribute("disabled");
- } else {
- addAttribute.setAttribute("disabled", "true");
+ for (let menuitem of nodeLinkMenuItems) {
+ menu.append(menuitem);
}
- // Enable "Edit Attribute" and "Remove Attribute" only on attribute click
- if (isEditableElement && nodeInfo && nodeInfo.type === "attribute") {
- editAttribute.removeAttribute("disabled");
- editAttribute.setAttribute("label",
- strings.formatStringFromName(
- "inspector.menu.editAttribute.label", [`"${nodeInfo.name}"`], 1));
-
- removeAttribute.removeAttribute("disabled");
- removeAttribute.setAttribute("label",
- strings.formatStringFromName(
- "inspector.menu.removeAttribute.label", [`"${nodeInfo.name}"`], 1));
- } else {
- editAttribute.setAttribute("disabled", "true");
- editAttribute.setAttribute("label",
- strings.formatStringFromName(
- "inspector.menu.editAttribute.label", [""], 1));
-
- removeAttribute.setAttribute("disabled", "true");
- removeAttribute.setAttribute("label",
- strings.formatStringFromName(
- "inspector.menu.removeAttribute.label", [""], 1));
- }
+ menu.popup(screenX, screenY, this._toolbox);
+ return menu;
},
- _resetNodeMenu: function () {
- // Remove any extra items
- while (this.lastNodemenuItem.nextSibling) {
- let toDelete = this.lastNodemenuItem.nextSibling;
- toDelete.parentNode.removeChild(toDelete);
- }
+ _getPasteSubmenu: function (isEditableElement) {
+ let isPasteable = isEditableElement && this._getClipboardContentForPaste();
+ let disableAdjacentPaste = !isPasteable ||
+ !this.canPasteInnerOrAdjacentHTML || this.selection.isRoot() ||
+ this.selection.isBodyNode() || this.selection.isHeadNode();
+ let disableFirstLastPaste = !isPasteable ||
+ !this.canPasteInnerOrAdjacentHTML || (this.selection.isHTMLNode() &&
+ this.selection.isRoot());
+
+ let pasteSubmenu = new Menu();
+ pasteSubmenu.append(new MenuItem({
+ id: "node-menu-pasteinnerhtml",
+ label: strings.GetStringFromName("inspectorPasteInnerHTML.label"),
+ accesskey: strings.GetStringFromName("inspectorPasteInnerHTML.accesskey"),
+ disabled: !isPasteable || !this.canPasteInnerOrAdjacentHTML,
+ click: () => this.pasteInnerHTML(),
+ }));
+ pasteSubmenu.append(new MenuItem({
+ id: "node-menu-pasteouterhtml",
+ label: strings.GetStringFromName("inspectorPasteOuterHTML.label"),
+ accesskey: strings.GetStringFromName("inspectorPasteOuterHTML.accesskey"),
+ disabled: !isPasteable || !this.isOuterHTMLEditable,
+ click: () => this.pasteOuterHTML(),
+ }));
+ pasteSubmenu.append(new MenuItem({
+ id: "node-menu-pastebefore",
+ label: strings.GetStringFromName("inspectorHTMLPasteBefore.label"),
+ accesskey:
+ strings.GetStringFromName("inspectorHTMLPasteBefore.accesskey"),
+ disabled: disableAdjacentPaste,
+ click: () => this.pasteAdjacentHTML("beforeBegin"),
+ }));
+ pasteSubmenu.append(new MenuItem({
+ id: "node-menu-pasteafter",
+ label: strings.GetStringFromName("inspectorHTMLPasteAfter.label"),
+ accesskey:
+ strings.GetStringFromName("inspectorHTMLPasteAfter.accesskey"),
+ disabled: disableAdjacentPaste,
+ click: () => this.pasteAdjacentHTML("afterEnd"),
+ }));
+ pasteSubmenu.append(new MenuItem({
+ id: "node-menu-pastefirstchild",
+ label: strings.GetStringFromName("inspectorHTMLPasteFirstChild.label"),
+ accesskey:
+ strings.GetStringFromName("inspectorHTMLPasteFirstChild.accesskey"),
+ disabled: disableFirstLastPaste,
+ click: () => this.pasteAdjacentHTML("afterBegin"),
+ }));
+ pasteSubmenu.append(new MenuItem({
+ id: "node-menu-pastelastchild",
+ label: strings.GetStringFromName("inspectorHTMLPasteLastChild.label"),
+ accesskey:
+ strings.GetStringFromName("inspectorHTMLPasteLastChild.accesskey"),
+ disabled: disableFirstLastPaste,
+ click: () => this.pasteAdjacentHTML("beforeEnd"),
+ }));
+
+ return pasteSubmenu;
+ },
+
+ _getAttributesSubmenu: function (isEditableElement) {
+ let attributesSubmenu = new Menu();
+ let nodeInfo = this.nodeMenuTriggerInfo;
+ let isAttributeClicked = isEditableElement && nodeInfo &&
+ nodeInfo.type === "attribute";
+
+ attributesSubmenu.append(new MenuItem({
+ id: "node-menu-add-attribute",
+ label: strings.GetStringFromName("inspectorAddAttribute.label"),
+ accesskey: strings.GetStringFromName("inspectorAddAttribute.accesskey"),
+ disabled: !isEditableElement,
+ click: () => this.onAddAttribute(),
+ }));
+ attributesSubmenu.append(new MenuItem({
+ id: "node-menu-edit-attribute",
+ label: strings.formatStringFromName("inspectorEditAttribute.label",
+ [isAttributeClicked ? `"${nodeInfo.name}"` : ""], 1),
+ accesskey: strings.GetStringFromName("inspectorEditAttribute.accesskey"),
+ disabled: !isAttributeClicked,
+ click: () => this.onEditAttribute(),
+ }));
+
+ attributesSubmenu.append(new MenuItem({
+ id: "node-menu-remove-attribute",
+ label: strings.formatStringFromName("inspectorRemoveAttribute.label",
+ [isAttributeClicked ? `"${nodeInfo.name}"` : ""], 1),
+ accesskey:
+ strings.GetStringFromName("inspectorRemoveAttribute.accesskey"),
+ disabled: !isAttributeClicked,
+ click: () => this.onRemoveAttribute(),
+ }));
+
+ return attributesSubmenu;
},
/**
* Link menu items can be shown or hidden depending on the context and
* selected node, and their labels can vary.
+ *
+ * @return {Array} list of visible menu items related to links.
*/
- _setupNodeLinkMenu: function () {
- let linkSeparator = this.panelDoc.getElementById("node-menu-link-separator");
- let linkFollow = this.panelDoc.getElementById("node-menu-link-follow");
- let linkCopy = this.panelDoc.getElementById("node-menu-link-copy");
-
- // Hide all by default.
- linkSeparator.setAttribute("hidden", "true");
- linkFollow.setAttribute("hidden", "true");
- linkCopy.setAttribute("hidden", "true");
+ _getNodeLinkMenuItems: function () {
+ let linkFollow = new MenuItem({
+ id: "node-menu-link-follow",
+ visible: false,
+ click: () => this.onFollowLink(),
+ });
+ let linkCopy = new MenuItem({
+ id: "node-menu-link-copy",
+ visible: false,
+ click: () => this.onCopyLink(),
+ });
// Get information about the right-clicked node.
- let popupNode = this.panelDoc.popupNode;
+ let popupNode = this.contextMenuTarget;
if (!popupNode || !popupNode.classList.contains("link")) {
- return;
+ return [linkFollow, linkCopy];
}
let type = popupNode.dataset.type;
- if (type === "uri" || type === "cssresource" || type === "jsresource") {
- // First make sure the target can resolve relative URLs.
- this.target.actorHasMethod("inspector", "resolveRelativeURL").then(canResolve => {
- if (!canResolve) {
- return;
- }
-
- linkSeparator.removeAttribute("hidden");
+ if (this._supportsResolveRelativeURL &&
+ (type === "uri" || type === "cssresource" || type === "jsresource")) {
+ // Links can't be opened in new tabs in the browser toolbox.
+ if (type === "uri" && !this.target.chrome) {
+ linkFollow.visible = true;
+ linkFollow.label = strings.GetStringFromName(
+ "inspector.menu.openUrlInNewTab.label");
+ } else if (type === "cssresource") {
+ linkFollow.visible = true;
+ linkFollow.label = toolboxStrings.GetStringFromName(
+ "toolbox.viewCssSourceInStyleEditor.label");
+ } else if (type === "jsresource") {
+ linkFollow.visible = true;
+ linkFollow.label = toolboxStrings.GetStringFromName(
+ "toolbox.viewJsSourceInDebugger.label");
+ }
- // Links can't be opened in new tabs in the browser toolbox.
- if (type === "uri" && !this.target.chrome) {
- linkFollow.removeAttribute("hidden");
- linkFollow.setAttribute("label", strings.GetStringFromName(
- "inspector.menu.openUrlInNewTab.label"));
- } else if (type === "cssresource") {
- linkFollow.removeAttribute("hidden");
- linkFollow.setAttribute("label", toolboxStrings.GetStringFromName(
- "toolbox.viewCssSourceInStyleEditor.label"));
- } else if (type === "jsresource") {
- linkFollow.removeAttribute("hidden");
- linkFollow.setAttribute("label", toolboxStrings.GetStringFromName(
- "toolbox.viewJsSourceInDebugger.label"));
- }
+ linkCopy.visible = true;
+ linkCopy.label = strings.GetStringFromName(
+ "inspector.menu.copyUrlToClipboard.label");
+ } else if (type === "idref") {
+ linkFollow.visible = true;
+ linkFollow.label = strings.formatStringFromName(
+ "inspector.menu.selectElement.label", [popupNode.dataset.link], 1);
+ }
- linkCopy.removeAttribute("hidden");
- linkCopy.setAttribute("label", strings.GetStringFromName(
- "inspector.menu.copyUrlToClipboard.label"));
- }, console.error);
- } else if (type === "idref") {
- linkSeparator.removeAttribute("hidden");
- linkFollow.removeAttribute("hidden");
- linkFollow.setAttribute("label", strings.formatStringFromName(
- "inspector.menu.selectElement.label", [popupNode.dataset.link], 1));
- }
+ return [linkFollow, linkCopy];
},
_initMarkup: function () {
let doc = this.panelDoc;
this._markupBox = doc.getElementById("markup-box");
// create tool iframe
this._markupFrame = doc.createElement("iframe");
this._markupFrame.setAttribute("flex", "1");
this._markupFrame.setAttribute("tooltip", "aHTMLTooltip");
- this._markupFrame.setAttribute("context", "inspector-node-popup");
+ this._markupFrame.addEventListener("contextmenu", this._onContextMenu, true);
// This is needed to enable tooltips inside the iframe document.
this._markupFrame.addEventListener("load", this._onMarkupFrameLoad, true);
this._markupBox.setAttribute("collapsed", true);
this._markupBox.appendChild(this._markupFrame);
this._markupFrame.setAttribute("src", "chrome://devtools/content/inspector/markup/markup.xhtml");
this._markupFrame.setAttribute("aria-label",
@@ -1028,16 +1111,17 @@ InspectorPanel.prototype = {
this.emit("markuploaded");
},
_destroyMarkup: function () {
let destroyPromise;
if (this._markupFrame) {
this._markupFrame.removeEventListener("load", this._onMarkupFrameLoad, true);
+ this._markupFrame.removeEventListener("contextmenu", this._onContextMenu, true);
}
if (this.markup) {
destroyPromise = this.markup.destroy();
this.markup = null;
} else {
destroyPromise = promise.resolve();
}
@@ -1410,35 +1494,35 @@ InspectorPanel.prototype = {
this.markup.collapseNode(this.selection.nodeFront);
},
/**
* This method is here for the benefit of the node-menu-link-follow menu item
* in the inspector contextual-menu.
*/
onFollowLink: function () {
- let type = this.panelDoc.popupNode.dataset.type;
- let link = this.panelDoc.popupNode.dataset.link;
+ let type = this.contextMenuTarget.dataset.type;
+ let link = this.contextMenuTarget.dataset.link;
this.followAttributeLink(type, link);
},
/**
* Given a type and link found in a node's attribute in the markup-view,
* attempt to follow that link (which may result in opening a new tab, the
* style editor or debugger).
*/
followAttributeLink: function (type, link) {
if (!type || !link) {
return;
}
if (type === "uri" || type === "cssresource" || type === "jsresource") {
// Open link in a new tab.
- // When the inspector menu was setup on click (see _setupNodeLinkMenu), we
+ // When the inspector menu was setup on click (see _getNodeLinkMenuItems), we
// already checked that resolveRelativeURL existed.
this.inspector.resolveRelativeURL(
link, this.selection.nodeFront).then(url => {
if (type === "uri") {
let browserWin = this.target.tab.ownerDocument.defaultView;
browserWin.openUILinkIn(url, "tab");
} else if (type === "cssresource") {
return this.toolbox.viewSourceInStyleEditor(url);
@@ -1461,24 +1545,24 @@ InspectorPanel.prototype = {
}
},
/**
* This method is here for the benefit of the node-menu-link-copy menu item
* in the inspector contextual-menu.
*/
onCopyLink: function () {
- let link = this.panelDoc.popupNode.dataset.link;
+ let link = this.contextMenuTarget.dataset.link;
this.copyAttributeLink(link);
},
/**
* This method is here for the benefit of copying links.
*/
copyAttributeLink: function (link) {
- // When the inspector menu was setup on click (see _setupNodeLinkMenu), we
+ // When the inspector menu was setup on click (see _getNodeLinkMenuItems), we
// already checked that resolveRelativeURL existed.
this.inspector.resolveRelativeURL(link, this.selection.nodeFront).then(url => {
clipboardHelper.copyString(url);
}, console.error);
}
};
--- a/devtools/client/inspector/inspector.xul
+++ b/devtools/client/inspector/inspector.xul
@@ -19,139 +19,16 @@
<!ENTITY % layoutviewDTD SYSTEM "chrome://devtools/locale/layoutview.dtd"> %layoutviewDTD;
]>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml">
<script type="application/javascript;version=1.8"
src="chrome://devtools/content/shared/theme-switching.js"/>
-
- <popupset id="inspectorPopupSet">
- <!-- Used by the Markup Panel and the Highlighter -->
- <menupopup id="inspector-node-popup">
- <menuitem id="node-menu-edithtml"
- label="&inspectorHTMLEdit.label;"
- accesskey="&inspectorHTMLEdit.accesskey;"
- oncommand="inspector.editHTML()"/>
- <menuitem id="node-menu-add"
- label="&inspectorAddNode.label;"
- accesskey="&inspectorAddNode.accesskey;"
- oncommand="inspector.addNode()"/>
- <menuitem id="node-menu-duplicatenode"
- label="&inspectorDuplicateNode.label;"
- oncommand="inspector.duplicateNode()"/>
- <menuitem id="node-menu-delete"
- label="&inspectorHTMLDelete.label;"
- accesskey="&inspectorHTMLDelete.accesskey;"
- oncommand="inspector.deleteNode()"/>
- <menu label="&inspectorAttributesSubmenu.label;"
- accesskey="&inspectorAttributesSubmenu.accesskey;">
- <menupopup>
- <menuitem id="node-menu-add-attribute"
- label="&inspectorAddAttribute.label;"
- accesskey="&inspectorAddAttribute.accesskey;"
- oncommand="inspector.onAddAttribute()"/>
- <menuitem id="node-menu-edit-attribute"
- label="&inspectorEditAttribute.label;"
- accesskey="&inspectorEditAttribute.accesskey;"
- oncommand="inspector.onEditAttribute()"/>
- <menuitem id="node-menu-remove-attribute"
- label="&inspectorRemoveAttribute.label;"
- accesskey="&inspectorRemoveAttribute.accesskey;"
- oncommand="inspector.onRemoveAttribute()"/>
- </menupopup>
- </menu>
- <menuseparator/>
- <menuitem id="node-menu-pseudo-hover"
- label=":hover" type="checkbox"
- oncommand="inspector.togglePseudoClass(':hover')"/>
- <menuitem id="node-menu-pseudo-active"
- label=":active" type="checkbox"
- oncommand="inspector.togglePseudoClass(':active')"/>
- <menuitem id="node-menu-pseudo-focus"
- label=":focus" type="checkbox"
- oncommand="inspector.togglePseudoClass(':focus')"/>
- <menuseparator/>
- <menu label="&inspectorCopyHTMLSubmenu.label;">
- <menupopup>
- <menuitem id="node-menu-copyinner"
- label="&inspectorCopyInnerHTML.label;"
- accesskey="&inspectorCopyInnerHTML.accesskey;"
- oncommand="inspector.copyInnerHTML()"/>
- <menuitem id="node-menu-copyouter"
- label="&inspectorCopyOuterHTML.label;"
- accesskey="&inspectorCopyOuterHTML.accesskey;"
- oncommand="inspector.copyOuterHTML()"/>
- <menuitem id="node-menu-copyuniqueselector"
- label="&inspectorCopyCSSSelector.label;"
- accesskey="&inspectorCopyCSSSelector.accesskey;"
- oncommand="inspector.copyUniqueSelector()"/>
- <menuitem id="node-menu-copyimagedatauri"
- label="&inspectorImageDataUri.label;"
- oncommand="inspector.copyImageDataUri()"/>
- </menupopup>
- </menu>
- <menu label="&inspectorPasteHTMLSubmenu.label;">
- <menupopup>
- <menuitem id="node-menu-pasteinnerhtml"
- label="&inspectorPasteInnerHTML.label;"
- accesskey="&inspectorPasteInnerHTML.accesskey;"
- oncommand="inspector.pasteInnerHTML()"/>
- <menuitem id="node-menu-pasteouterhtml"
- label="&inspectorPasteOuterHTML.label;"
- accesskey="&inspectorPasteOuterHTML.accesskey;"
- oncommand="inspector.pasteOuterHTML()"/>
- <menuitem id="node-menu-pastebefore"
- label="&inspectorHTMLPasteBefore.label;"
- accesskey="&inspectorHTMLPasteBefore.accesskey;"
- oncommand="inspector.pasteAdjacentHTML('beforeBegin')"/>
- <menuitem id="node-menu-pasteafter"
- label="&inspectorHTMLPasteAfter.label;"
- accesskey="&inspectorHTMLPasteAfter.accesskey;"
- oncommand="inspector.pasteAdjacentHTML('afterEnd')"/>
- <menuitem id="node-menu-pastefirstchild"
- label="&inspectorHTMLPasteFirstChild.label;"
- accesskey="&inspectorHTMLPasteFirstChild.accesskey;"
- oncommand="inspector.pasteAdjacentHTML('afterBegin')"/>
- <menuitem id="node-menu-pastelastchild"
- label="&inspectorHTMLPasteLastChild.label;"
- accesskey="&inspectorHTMLPasteLastChild.accesskey;"
- oncommand="inspector.pasteAdjacentHTML('beforeEnd')"/>
- </menupopup>
- </menu>
- <menuseparator/>
- <menuitem id="node-menu-expand"
- label="&inspectorExpandNode.label;"
- oncommand="inspector.expandNode()"/>
- <menuitem id="node-menu-collapse"
- label="&inspectorCollapseNode.label;"
- oncommand="inspector.collapseNode()"/>
- <menuseparator/>
- <menuitem id="node-menu-scrollnodeintoview"
- label="&inspectorScrollNodeIntoView.label;"
- accesskey="&inspectorScrollNodeIntoView.accesskey;"
- oncommand="inspector.scrollNodeIntoView()"/>
- <menuitem id="node-menu-screenshotnode"
- label="&inspectorScreenshotNode.label;"
- oncommand="inspector.screenshotNode()" />
- <menuitem id="node-menu-useinconsole"
- label="&inspectorUseInConsole.label;"
- oncommand="inspector.useInConsole()"/>
- <menuitem id="node-menu-showdomproperties"
- label="&inspectorShowDOMProperties.label;"
- oncommand="inspector.showDOMProperties()"/>
- <menuseparator id="node-menu-link-separator"/>
- <menuitem id="node-menu-link-follow"
- oncommand="inspector.onFollowLink()"/>
- <menuitem id="node-menu-link-copy"
- oncommand="inspector.onCopyLink()"/>
- </menupopup>
- </popupset>
-
<box flex="1" class="devtools-responsive-container theme-body">
<vbox flex="1" class="devtools-main-content">
<html:div id="inspector-toolbar"
class="devtools-toolbar"
nowindowdrag="true">
<html:button id="inspector-element-add-button"
title="&inspectorAddNode.label;"
class="devtools-button" />
--- a/devtools/client/inspector/markup/test/browser_markup_copy_image_data.js
+++ b/devtools/client/inspector/markup/test/browser_markup_copy_image_data.js
@@ -27,49 +27,29 @@ add_task(function* () {
// Check again that the menu isn't available on the DIV (to make sure our
// menu updating mechanism works)
yield selectNode("div", inspector);
yield assertCopyImageDataNotAvailable(inspector);
});
function* assertCopyImageDataNotAvailable(inspector) {
- let menu = yield openNodeMenu(inspector);
+ let allMenuItems = openContextMenuAndGetAllItems(inspector);
+ let item = allMenuItems.find(item => item.id === "node-menu-copyimagedatauri");
- let item = menu.getElementsByAttribute("id", "node-menu-copyimagedatauri")[0];
ok(item, "The menu item was found in the contextual menu");
- is(item.getAttribute("disabled"), "true", "The menu item is disabled");
-
- yield closeNodeMenu(inspector);
+ ok(item.disabled, "The menu item is disabled");
}
function* assertCopyImageDataAvailable(inspector) {
- let menu = yield openNodeMenu(inspector);
-
- let item = menu.getElementsByAttribute("id", "node-menu-copyimagedatauri")[0];
- ok(item, "The menu item was found in the contextual menu");
- is(item.getAttribute("disabled"), "", "The menu item is enabled");
-
- yield closeNodeMenu(inspector);
-}
+ let allMenuItems = openContextMenuAndGetAllItems(inspector);
+ let item = allMenuItems.find(item => item.id === "node-menu-copyimagedatauri");
-function* openNodeMenu(inspector) {
- let onShown = once(inspector.nodemenu, "popupshown", false);
- inspector.nodemenu.hidden = false;
- inspector.nodemenu.openPopup();
- yield onShown;
- return inspector.nodemenu;
-}
-
-function* closeNodeMenu(inspector) {
- let onHidden = once(inspector.nodemenu, "popuphidden", false);
- inspector.nodemenu.hidden = true;
- inspector.nodemenu.hidePopup();
- yield onHidden;
- return inspector.nodemenu;
+ ok(item, "The menu item was found in the contextual menu");
+ ok(!item.disabled, "The menu item is enabled");
}
function triggerCopyImageUrlAndWaitForClipboard(expected, inspector) {
let def = promise.defer();
SimpleTest.waitForClipboard(expected, () => {
inspector.markup.getContainer(inspector.selection.nodeFront)
.copyImageDataUri();
--- a/devtools/client/inspector/markup/test/browser_markup_links_04.js
+++ b/devtools/client/inspector/markup/test/browser_markup_links_04.js
@@ -70,47 +70,50 @@ const TEST_DATA = [{
selector: "p[for]",
attributeName: "for",
popupNodeSelector: ".attr-value",
isLinkFollowItemVisible: false,
isLinkCopyItemVisible: false
}];
add_task(function* () {
+
let {inspector} = yield openInspectorForURL(TEST_URL);
- let linkFollow = inspector.panelDoc.getElementById("node-menu-link-follow");
- let linkCopy = inspector.panelDoc.getElementById("node-menu-link-copy");
-
for (let test of TEST_DATA) {
info("Selecting test node " + test.selector);
yield selectNode(test.selector, inspector);
info("Finding the popupNode to anchor the context-menu to");
let {editor} = yield getContainerForSelector(test.selector, inspector);
let popupNode = editor.attrElements.get(test.attributeName)
.querySelector(test.popupNodeSelector);
ok(popupNode, "Found the popupNode in attribute " + test.attributeName);
info("Simulating a context click on the popupNode");
- contextMenuClick(popupNode);
+ let allMenuItems = openContextMenuAndGetAllItems(inspector, {
+ target: popupNode,
+ });
+
+ let linkFollow = allMenuItems.find(i => i.id === "node-menu-link-follow");
+ let linkCopy = allMenuItems.find(i => i.id === "node-menu-link-copy");
// The contextual menu setup is async, because it needs to know if the
// inspector has the resolveRelativeURL method first. So call actorHasMethod
// here too to make sure the first call resolves first and the menu is
// properly setup.
yield inspector.target.actorHasMethod("inspector", "resolveRelativeURL");
- is(linkFollow.hasAttribute("hidden"), !test.isLinkFollowItemVisible,
+ is(linkFollow.visible, test.isLinkFollowItemVisible,
"The follow-link item display is correct");
- is(linkCopy.hasAttribute("hidden"), !test.isLinkCopyItemVisible,
+ is(linkCopy.visible, test.isLinkCopyItemVisible,
"The copy-link item display is correct");
if (test.isLinkFollowItemVisible) {
- is(linkFollow.getAttribute("label"), test.linkFollowItemLabel,
+ is(linkFollow.label, test.linkFollowItemLabel,
"the follow-link label is correct");
}
if (test.isLinkCopyItemVisible) {
- is(linkCopy.getAttribute("label"), test.linkCopyItemLabel,
+ is(linkCopy.label, test.linkCopyItemLabel,
"the copy-link label is correct");
}
}
});
--- a/devtools/client/inspector/markup/test/browser_markup_links_05.js
+++ b/devtools/client/inspector/markup/test/browser_markup_links_05.js
@@ -12,18 +12,19 @@ const TEST_URL = URL_ROOT + "doc_markup_
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URL);
info("Select a node with a URI attribute");
yield selectNode("video", inspector);
info("Set the popupNode to the node that contains the uri");
let {editor} = yield getContainerForSelector("video", inspector);
- let popupNode = editor.attrElements.get("poster").querySelector(".link");
- inspector.panelDoc.popupNode = popupNode;
+ openContextMenuAndGetAllItems(inspector, {
+ target: editor.attrElements.get("poster").querySelector(".link"),
+ });
info("Follow the link and wait for the new tab to open");
let onTabOpened = once(gBrowser.tabContainer, "TabOpen");
inspector.onFollowLink();
let {target: tab} = yield onTabOpened;
yield waitForTabLoad(tab);
ok(true, "A new tab opened");
@@ -31,34 +32,36 @@ add_task(function* () {
"The URL for the new tab is correct");
gBrowser.removeTab(tab);
info("Select a node with a IDREF attribute");
yield selectNode("label", inspector);
info("Set the popupNode to the node that contains the ref");
({editor} = yield getContainerForSelector("label", inspector));
- popupNode = editor.attrElements.get("for").querySelector(".link");
- inspector.panelDoc.popupNode = popupNode;
+ openContextMenuAndGetAllItems(inspector, {
+ target: editor.attrElements.get("for").querySelector(".link"),
+ });
info("Follow the link and wait for the new node to be selected");
let onSelection = inspector.selection.once("new-node-front");
inspector.onFollowLink();
yield onSelection;
ok(true, "A new node was selected");
is(inspector.selection.nodeFront.id, "name", "The right node was selected");
info("Select a node with an invalid IDREF attribute");
yield selectNode("output", inspector);
info("Set the popupNode to the node that contains the ref");
({editor} = yield getContainerForSelector("output", inspector));
- popupNode = editor.attrElements.get("for").querySelectorAll(".link")[2];
- inspector.panelDoc.popupNode = popupNode;
+ openContextMenuAndGetAllItems(inspector, {
+ target: editor.attrElements.get("for").querySelectorAll(".link")[2],
+ });
info("Try to follow the link and check that no new node were selected");
let onFailed = inspector.once("idref-attribute-link-failed");
inspector.onFollowLink();
yield onFailed;
ok(true, "The node selection failed");
is(inspector.selection.nodeFront.tagName.toLowerCase(), "output",
--- a/devtools/client/inspector/markup/test/browser_markup_links_06.js
+++ b/devtools/client/inspector/markup/test/browser_markup_links_06.js
@@ -12,18 +12,19 @@ const TEST_URL = URL_ROOT + "doc_markup_
add_task(function* () {
let {toolbox, inspector} = yield openInspectorForURL(TEST_URL);
info("Select a node with a cssresource attribute");
yield selectNode("link", inspector);
info("Set the popupNode to the node that contains the uri");
let {editor} = yield getContainerForSelector("link", inspector);
- let popupNode = editor.attrElements.get("href").querySelector(".link");
- inspector.panelDoc.popupNode = popupNode;
+ openContextMenuAndGetAllItems(inspector, {
+ target: editor.attrElements.get("href").querySelector(".link"),
+ });
info("Follow the link and wait for the style-editor to open");
let onStyleEditorReady = toolbox.once("styleeditor-ready");
inspector.onFollowLink();
yield onStyleEditorReady;
// No real need to test that the editor opened on the right file here as this
// is already tested in /framework/test/browser_toolbox_view_source_*
@@ -32,18 +33,19 @@ add_task(function* () {
info("Switch back to the inspector");
yield toolbox.selectTool("inspector");
info("Select a node with a jsresource attribute");
yield selectNode("script", inspector);
info("Set the popupNode to the node that contains the uri");
({editor} = yield getContainerForSelector("script", inspector));
- popupNode = editor.attrElements.get("src").querySelector(".link");
- inspector.panelDoc.popupNode = popupNode;
+ openContextMenuAndGetAllItems(inspector, {
+ target: editor.attrElements.get("src").querySelector(".link"),
+ });
info("Follow the link and wait for the debugger to open");
let onDebuggerReady = toolbox.once("jsdebugger-ready");
inspector.onFollowLink();
yield onDebuggerReady;
// No real need to test that the debugger opened on the right file here as
// this is already tested in /framework/test/browser_toolbox_view_source_*
--- a/devtools/client/inspector/markup/test/head.js
+++ b/devtools/client/inspector/markup/test/head.js
@@ -290,97 +290,71 @@ function wait(ms) {
* @param {InspectorPanel} inspector
* @param {Boolean} assert Should this function run assertions inline.
* @return A promise that resolves with a boolean indicating whether
* the menu items are disabled once the menu has been checked.
*/
var isEditingMenuDisabled = Task.async(
function* (nodeFront, inspector, assert = true) {
let doc = inspector.panelDoc;
- let deleteMenuItem = doc.getElementById("node-menu-delete");
- let editHTMLMenuItem = doc.getElementById("node-menu-edithtml");
- let pasteHTMLMenuItem = doc.getElementById("node-menu-pasteouterhtml");
// To ensure clipboard contains something to paste.
clipboard.set("<p>test</p>", "html");
- let menu = inspector.nodemenu;
yield selectNode(nodeFront, inspector);
- yield reopenMenu(menu);
+ let allMenuItems = openContextMenuAndGetAllItems(inspector);
- let isDeleteMenuDisabled = deleteMenuItem.hasAttribute("disabled");
- let isEditHTMLMenuDisabled = editHTMLMenuItem.hasAttribute("disabled");
- let isPasteHTMLMenuDisabled = pasteHTMLMenuItem.hasAttribute("disabled");
+ let deleteMenuItem = allMenuItems.find(item => item.id === "node-menu-delete");
+ let editHTMLMenuItem = allMenuItems.find(item => item.id === "node-menu-edithtml");
+ let pasteHTMLMenuItem = allMenuItems.find(item => item.id === "node-menu-pasteouterhtml");
if (assert) {
- ok(isDeleteMenuDisabled, "Delete menu item is disabled");
- ok(isEditHTMLMenuDisabled, "Edit HTML menu item is disabled");
- ok(isPasteHTMLMenuDisabled, "Paste HTML menu item is disabled");
+ ok(deleteMenuItem.disabled, "Delete menu item is disabled");
+ ok(editHTMLMenuItem.disabled, "Edit HTML menu item is disabled");
+ ok(pasteHTMLMenuItem.disabled, "Paste HTML menu item is disabled");
}
- return isDeleteMenuDisabled &&
- isEditHTMLMenuDisabled &&
- isPasteHTMLMenuDisabled;
+ return deleteMenuItem.disabled &&
+ editHTMLMenuItem.disabled &&
+ pasteHTMLMenuItem.disabled;
});
/**
* Check to see if the inspector menu items for editing are enabled.
* Things like Edit As HTML, Delete Node, etc.
* @param {NodeFront} nodeFront
* @param {InspectorPanel} inspector
* @param {Boolean} assert Should this function run assertions inline.
* @return A promise that resolves with a boolean indicating whether
* the menu items are enabled once the menu has been checked.
*/
var isEditingMenuEnabled = Task.async(
function* (nodeFront, inspector, assert = true) {
let doc = inspector.panelDoc;
- let deleteMenuItem = doc.getElementById("node-menu-delete");
- let editHTMLMenuItem = doc.getElementById("node-menu-edithtml");
- let pasteHTMLMenuItem = doc.getElementById("node-menu-pasteouterhtml");
// To ensure clipboard contains something to paste.
clipboard.set("<p>test</p>", "html");
let menu = inspector.nodemenu;
yield selectNode(nodeFront, inspector);
- yield reopenMenu(menu);
+ let allMenuItems = openContextMenuAndGetAllItems(inspector);
- let isDeleteMenuDisabled = deleteMenuItem.hasAttribute("disabled");
- let isEditHTMLMenuDisabled = editHTMLMenuItem.hasAttribute("disabled");
- let isPasteHTMLMenuDisabled = pasteHTMLMenuItem.hasAttribute("disabled");
+ let deleteMenuItem = allMenuItems.find(item => item.id === "node-menu-delete");
+ let editHTMLMenuItem = allMenuItems.find(item => item.id === "node-menu-edithtml");
+ let pasteHTMLMenuItem = allMenuItems.find(item => item.id === "node-menu-pasteouterhtml");
if (assert) {
- ok(!isDeleteMenuDisabled, "Delete menu item is enabled");
- ok(!isEditHTMLMenuDisabled, "Edit HTML menu item is enabled");
- ok(!isPasteHTMLMenuDisabled, "Paste HTML menu item is enabled");
+ ok(!deleteMenuItem.disabled, "Delete menu item is enabled");
+ ok(!editHTMLMenuItem.disabled, "Edit HTML menu item is enabled");
+ ok(!pasteHTMLMenuItem.disabled, "Paste HTML menu item is enabled");
}
- return !isDeleteMenuDisabled &&
- !isEditHTMLMenuDisabled &&
- !isPasteHTMLMenuDisabled;
-});
-
-/**
- * Open a menu (closing it first if necessary).
- * @param {DOMNode} menu A menu that implements hidePopup/openPopup
- * @return a promise that resolves once the menu is opened.
- */
-var reopenMenu = Task.async(function* (menu) {
- // First close it is if it is already opened.
- if (menu.state == "closing" || menu.state == "open") {
- let popuphidden = once(menu, "popuphidden", true);
- menu.hidePopup();
- yield popuphidden;
- }
-
- // Then open it and return once
- let popupshown = once(menu, "popupshown", true);
- menu.openPopup();
- yield popupshown;
+ return !deleteMenuItem.disabled &&
+ !editHTMLMenuItem.disabled &&
+ !pasteHTMLMenuItem.disabled;
});
/**
* Wait for all current promises to be resolved. See this as executeSoon that
* can be used with yield.
*/
function promiseNextTick() {
let deferred = promise.defer();
@@ -486,30 +460,16 @@ function createTestHTTPServer() {
yield destroyed.promise;
});
server.start(-1);
return server;
}
/**
- * A helper that simulates a contextmenu event on the given chrome DOM element.
- */
-function contextMenuClick(element) {
- let evt = element.ownerDocument.createEvent("MouseEvents");
- let buttonRight = 2;
-
- evt.initMouseEvent("contextmenu", true, true,
- element.ownerDocument.defaultView, 1, 0, 0, 0, 0, false, false, false,
- false, buttonRight, null);
-
- element.dispatchEvent(evt);
-}
-
-/**
* Registers new backend tab actor.
*
* @param {DebuggerClient} client RDP client object (toolbox.target.client)
* @param {Object} options Configuration object with the following options:
*
* - moduleUrl {String}: URL of the module that contains actor implementation.
* - prefix {String}: prefix of the actor.
* - actorClass {ActorClass}: Constructor object for the actor.
--- a/devtools/client/inspector/test/browser_inspector_addNode_01.js
+++ b/devtools/client/inspector/test/browser_inspector_addNode_01.js
@@ -7,16 +7,16 @@
// Test that the add node button and context menu items are present in the UI.
const TEST_URL = "data:text/html;charset=utf-8,<h1>Add node</h1>";
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URL);
let {panelDoc} = inspector;
+ let allMenuItems = openContextMenuAndGetAllItems(inspector);
+ let menuItem = allMenuItems.find(item => item.id === "node-menu-add");
+ ok(menuItem, "The item is in the menu");
+
let toolbarButton =
panelDoc.querySelector("#inspector-toolbar #inspector-element-add-button");
- let menuItem =
- panelDoc.querySelector("#inspector-node-popup #node-menu-add");
-
ok(toolbarButton, "The add button is in the toolbar");
- ok(menuItem, "The item is in the menu");
});
--- a/devtools/client/inspector/test/browser_inspector_addNode_02.js
+++ b/devtools/client/inspector/test/browser_inspector_addNode_02.js
@@ -44,19 +44,20 @@ add_task(function* () {
yield selectNode("iframe", inspector);
assertState(false, inspector,
"The button and item are disabled on an IFRAME element");
});
function assertState(isEnabled, inspector, desc) {
let doc = inspector.panelDoc;
let btn = doc.querySelector("#inspector-element-add-button");
- let item = doc.querySelector("#node-menu-add");
// Force an update of the context menu to make sure menu items are updated
// according to the current selection. This normally happens when the menu is
// opened, but for the sake of this test's simplicity, we directly call the
// private update function instead.
- inspector._setupNodeMenu({target: {}});
+ let allMenuItems = openContextMenuAndGetAllItems(inspector);
+ let menuItem = allMenuItems.find(item => item.id === "node-menu-add");
+ ok(menuItem, "The item is in the menu");
+ is(!menuItem.disabled, isEnabled, desc);
is(!btn.hasAttribute("disabled"), isEnabled, desc);
- is(!item.hasAttribute("disabled"), isEnabled, desc);
}
--- a/devtools/client/inspector/test/browser_inspector_delete-selected-node-02.js
+++ b/devtools/client/inspector/test/browser_inspector_delete-selected-node-02.js
@@ -23,26 +23,24 @@ add_task(function* () {
info("Selecting a node, deleting it via context menu and checking that " +
"its parent node is selected and breadcrumbs are updated.");
yield selectNode("#deleteManually", inspector);
info("Getting the node container in the markup view.");
let container = yield getContainerForSelector("#deleteManually", inspector);
- info("Simulating right-click on the markup view container.");
- EventUtils.synthesizeMouse(container.tagLine, 2, 2,
- {type: "contextmenu", button: 2}, inspector.panelWin);
-
- info("Waiting for the context menu to open.");
- yield once(inspector.panelDoc.getElementById("inspectorPopupSet"),
- "popupshown");
+ let allMenuItems = openContextMenuAndGetAllItems(inspector, {
+ target: container.tagLine,
+ });
+ let menuItem = allMenuItems.find(item => item.id === "node-menu-delete");
info("Clicking 'Delete Node' in the context menu.");
- inspector.panelDoc.getElementById("node-menu-delete").click();
+ is(menuItem.disabled, false, "delete menu item is enabled");
+ menuItem.click();
info("Waiting for inspector to update.");
yield inspector.once("inspector-updated");
info("Inspector updated, performing checks.");
yield assertNodeSelectedAndPanelsUpdated("#selectedAfterDelete",
"li#selectedAfterDelete");
}
--- a/devtools/client/inspector/test/browser_inspector_expand-collapse.js
+++ b/devtools/client/inspector/test/browser_inspector_expand-collapse.js
@@ -10,49 +10,52 @@ const TEST_URL = "data:text/html;charset
"<div id='parent-node'><div id='child-node'></div></div>";
add_task(function* () {
// Test is often exceeding time-out threshold, similar to Bug 1137765
requestLongerTimeout(2);
let {inspector} = yield openInspectorForURL(TEST_URL);
- let nodeMenuCollapseElement = inspector.panelDoc.getElementById(
- "node-menu-collapse");
- let nodeMenuExpandElement = inspector.panelDoc.getElementById(
- "node-menu-expand");
-
info("Selecting the parent node");
let front = yield getNodeFrontForSelector("#parent-node", inspector);
yield selectNode(front, inspector);
info("Simulating context menu click on the selected node container.");
- contextMenuClick(getContainerForNodeFront(front, inspector).tagLine);
+ let allMenuItems = openContextMenuAndGetAllItems(inspector, {
+ target: getContainerForNodeFront(front, inspector).tagLine,
+ });
+ let nodeMenuCollapseElement =
+ allMenuItems.find(item => item.id === "node-menu-collapse");
+ let nodeMenuExpandElement =
+ allMenuItems.find(item => item.id === "node-menu-expand");
- ok(nodeMenuCollapseElement.hasAttribute("disabled"),
- "Collapse option is disabled");
- ok(!nodeMenuExpandElement.hasAttribute("disabled"),
- "ExpandAll option is enabled");
+ ok(nodeMenuCollapseElement.disabled, "Collapse option is disabled");
+ ok(!nodeMenuExpandElement.disabled, "ExpandAll option is enabled");
info("Testing whether expansion works properly");
- dispatchCommandEvent(nodeMenuExpandElement);
+ nodeMenuExpandElement.click();
+
info("Waiting for expansion to occur");
yield waitForMultipleChildrenUpdates(inspector);
let markUpContainer = getContainerForNodeFront(front, inspector);
ok(markUpContainer.expanded, "node has been successfully expanded");
- // reslecting node after expansion
+ // reselecting node after expansion
yield selectNode(front, inspector);
info("Testing whether collapse works properly");
info("Simulating context menu click on the selected node container.");
- contextMenuClick(getContainerForNodeFront(front, inspector).tagLine);
+ allMenuItems = openContextMenuAndGetAllItems(inspector, {
+ target: getContainerForNodeFront(front, inspector).tagLine,
+ });
+ nodeMenuCollapseElement =
+ allMenuItems.find(item => item.id === "node-menu-collapse");
- ok(!nodeMenuCollapseElement.hasAttribute("disabled"),
- "Collapse option is enabled");
+ ok(!nodeMenuCollapseElement.disabled, "Collapse option is enabled");
+ nodeMenuCollapseElement.click();
- dispatchCommandEvent(nodeMenuCollapseElement);
info("Waiting for collapse to occur");
yield waitForMultipleChildrenUpdates(inspector);
ok(!markUpContainer.expanded, "node has been successfully collapsed");
});
--- a/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js
+++ b/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js
@@ -229,25 +229,26 @@ add_task(function* () {
yield selectNode(front, inspector);
info("Simulating context menu click on the selected node container.");
let nodeFrontContainer = getContainerForNodeFront(front, inspector);
let contextMenuTrigger = attributeTrigger
? nodeFrontContainer.tagLine.querySelector(
`[data-attr="${attributeTrigger}"]`)
: nodeFrontContainer.tagLine;
- contextMenuClick(contextMenuTrigger);
+
+ let allMenuItems = openContextMenuAndGetAllItems(inspector, {
+ target: contextMenuTrigger,
+ });
- for (let menuitem of ALL_MENU_ITEMS) {
- let elt = inspector.panelDoc.getElementById(menuitem);
- let shouldBeDisabled = disabled.indexOf(menuitem) !== -1;
- let isDisabled = elt.hasAttribute("disabled");
-
- is(isDisabled, shouldBeDisabled,
- `#${menuitem} should be ${shouldBeDisabled ? "disabled" : "enabled"} `);
+ for (let id of ALL_MENU_ITEMS) {
+ let menuItem = allMenuItems.find(item => item.id === id);
+ let shouldBeDisabled = disabled.indexOf(id) !== -1;
+ is(menuItem.disabled, shouldBeDisabled,
+ `#${id} should be ${shouldBeDisabled ? "disabled" : "enabled"} `);
}
}
});
/**
* A helper that fetches a front for a node that matches the given selector or
* doctype node if the selector is falsy.
*/
@@ -270,22 +271,8 @@ function setupClipboard(data, type) {
if (data) {
info("Populating clipboard with " + type + " data.");
clipboard.set(data, type);
} else {
info("Clearing clipboard.");
clipboard.set("", "text");
}
}
-
-/**
- * A helper that simulates a contextmenu event on the given chrome DOM element.
- */
-function contextMenuClick(element) {
- let evt = element.ownerDocument.createEvent("MouseEvents");
- let button = 2;
-
- evt.initMouseEvent("contextmenu", true, true,
- element.ownerDocument.defaultView, 1, 0, 0, 0, 0, false,
- false, false, false, button, null);
-
- element.dispatchEvent(evt);
-}
--- a/devtools/client/inspector/test/browser_inspector_menu-02-copy-items.js
+++ b/devtools/client/inspector/test/browser_inspector_menu-02-copy-items.js
@@ -35,14 +35,15 @@ const COPY_ITEMS_TEST_DATA = [
];
add_task(function* () {
let { inspector } = yield openInspectorForURL(TEST_URL);
for (let {desc, id, selector, text} of COPY_ITEMS_TEST_DATA) {
info("Testing " + desc);
yield selectNode(selector, inspector);
- let item = inspector.panelDoc.getElementById(id);
+ let allMenuItems = openContextMenuAndGetAllItems(inspector);
+ let item = allMenuItems.find(item => item.id === id);
ok(item, "The popup has a " + desc + " menu item.");
- yield waitForClipboard(() => item.doCommand(), text);
+ yield waitForClipboard(() => item.click(), text);
}
});
--- a/devtools/client/inspector/test/browser_inspector_menu-03-paste-items.js
+++ b/devtools/client/inspector/test/browser_inspector_menu-03-paste-items.js
@@ -44,21 +44,22 @@ add_task(function* () {
function* testPasteOuterHTMLMenu() {
info("Testing that 'Paste Outer HTML' menu item works.");
clipboard.set("this was pasted (outerHTML)");
let outerHTMLSelector = "#paste-area h1";
let nodeFront = yield getNodeFront(outerHTMLSelector, inspector);
yield selectNode(nodeFront, inspector);
- contextMenuClick(getContainerForNodeFront(nodeFront, inspector).tagLine);
+ let allMenuItems = openContextMenuAndGetAllItems(inspector, {
+ target: getContainerForNodeFront(nodeFront, inspector).tagLine,
+ });
let onNodeReselected = inspector.markup.once("reselectedonremoved");
- let menu = inspector.panelDoc.getElementById("node-menu-pasteouterhtml");
- dispatchCommandEvent(menu);
+ allMenuItems.find(item => item.id === "node-menu-pasteouterhtml").click();
info("Waiting for inspector selection to update");
yield onNodeReselected;
let outerHTML = yield testActor.getProperty("body", "outerHTML");
ok(outerHTML.includes(clipboard.get()),
"Clipboard content was pasted into the node's outer HTML.");
ok(!(yield testActor.hasNode(outerHTMLSelector)),
@@ -71,22 +72,22 @@ add_task(function* () {
let innerHTMLSelector = "#paste-area .inner";
let getInnerHTML = () => testActor.getProperty(innerHTMLSelector,
"innerHTML");
let origInnerHTML = yield getInnerHTML();
let nodeFront = yield getNodeFront(innerHTMLSelector, inspector);
yield selectNode(nodeFront, inspector);
- contextMenuClick(getContainerForNodeFront(nodeFront, inspector).tagLine);
+ let allMenuItems = openContextMenuAndGetAllItems(inspector, {
+ target: getContainerForNodeFront(nodeFront, inspector).tagLine,
+ });
let onMutation = inspector.once("markupmutation");
- let menu = inspector.panelDoc.getElementById("node-menu-pasteinnerhtml");
- dispatchCommandEvent(menu);
-
+ allMenuItems.find(item => item.id === "node-menu-pasteinnerhtml").click();
info("Waiting for mutation to occur");
yield onMutation;
ok((yield getInnerHTML()) === clipboard.get(),
"Clipboard content was pasted into the node's inner HTML.");
ok((yield testActor.hasNode(innerHTMLSelector)),
"The original node has been preserved.");
yield undoChange(inspector);
@@ -97,59 +98,31 @@ add_task(function* () {
function* testPasteAdjacentHTMLMenu() {
let refSelector = "#paste-area .adjacent .ref";
let adjacentNodeSelector = "#paste-area .adjacent";
let nodeFront = yield getNodeFront(refSelector, inspector);
yield selectNode(nodeFront, inspector);
let markupTagLine = getContainerForNodeFront(nodeFront, inspector).tagLine;
for (let { clipboardData, menuId } of PASTE_ADJACENT_HTML_DATA) {
- let menu = inspector.panelDoc.getElementById(menuId);
- info(`Testing ${getLabelFor(menu)} for ${clipboardData}`);
+ let allMenuItems = openContextMenuAndGetAllItems(inspector, {
+ target: markupTagLine,
+ });
+ info(`Testing ${menuId} for ${clipboardData}`);
clipboard.set(clipboardData);
- contextMenuClick(markupTagLine);
let onMutation = inspector.once("markupmutation");
- dispatchCommandEvent(menu);
-
+ allMenuItems.find(item => item.id === menuId).click();
info("Waiting for mutation to occur");
yield onMutation;
}
let html = yield testActor.getProperty(adjacentNodeSelector, "innerHTML");
ok(html.trim() === "1<span class=\"ref\">234</span><span>5</span>",
"The Paste as Last Child / as First Child / Before / After worked as " +
"expected");
yield undoChange(inspector);
html = yield testActor.getProperty(adjacentNodeSelector, "innerHTML");
ok(html.trim() === "1<span class=\"ref\">234</span>",
"Undo works for paste adjacent HTML");
}
-
- function dispatchCommandEvent(node) {
- info("Dispatching command event on " + node);
- let commandEvent = document.createEvent("XULCommandEvent");
- commandEvent.initCommandEvent("command", true, true, window, 0, false,
- false, false, false, null);
- node.dispatchEvent(commandEvent);
- }
-
- function contextMenuClick(element) {
- info("Simulating contextmenu event on " + element);
- let evt = element.ownerDocument.createEvent("MouseEvents");
- let button = 2;
-
- evt.initMouseEvent("contextmenu", true, true,
- element.ownerDocument.defaultView, 1, 0, 0, 0, 0, false,
- false, false, false, button, null);
-
- element.dispatchEvent(evt);
- }
-
- function getLabelFor(elt) {
- if (typeof elt === "string") {
- elt = inspector.panelDoc.querySelector(elt);
- }
- let isInPasteSubMenu = elt.matches("#node-menu-paste-extra-submenu *");
- return `"${isInPasteSubMenu ? "Paste > " : ""}${elt.label}"`;
- }
});
--- a/devtools/client/inspector/test/browser_inspector_menu-04-use-in-console.js
+++ b/devtools/client/inspector/test/browser_inspector_menu-04-use-in-console.js
@@ -13,35 +13,39 @@ registerCleanupFunction(() => {
add_task(function* () {
let { inspector, toolbox } = yield openInspectorForURL(TEST_URL);
yield testUseInConsole();
function* testUseInConsole() {
info("Testing 'Use in Console' menu item.");
- let useInConsoleNode = inspector.panelDoc.getElementById(
- "node-menu-useinconsole");
yield selectNode("#console-var", inspector);
- dispatchCommandEvent(useInConsoleNode);
+ let container = yield getContainerForSelector("#console-var", inspector);
+ let allMenuItems = openContextMenuAndGetAllItems(inspector, {
+ target: container.tagLine,
+ });
+ let menuItem = allMenuItems.find(i => i.id === "node-menu-useinconsole");
+ menuItem.click();
+
yield inspector.once("console-var-ready");
let hud = toolbox.getPanel("webconsole").hud;
let jsterm = hud.jsterm;
let jstermInput = jsterm.hud.document.querySelector(".jsterm-input-node");
is(jstermInput.value, "temp0", "first console variable is named temp0");
let result = yield jsterm.execute();
isnot(result.textContent.indexOf('<p id="console-var">'), -1,
"variable temp0 references correct node");
yield selectNode("#console-var-multi", inspector);
- dispatchCommandEvent(useInConsoleNode);
+ menuItem.click();
yield inspector.once("console-var-ready");
is(jstermInput.value, "temp1", "second console variable is named temp1");
result = yield jsterm.execute();
isnot(result.textContent.indexOf('<p id="console-var-multi">'), -1,
"variable temp1 references correct node");
--- a/devtools/client/inspector/test/browser_inspector_menu-05-attribute-items.js
+++ b/devtools/client/inspector/test/browser_inspector_menu-05-attribute-items.js
@@ -11,21 +11,20 @@ add_task(function* () {
let { inspector, testActor } = yield openInspectorForURL(TEST_URL);
yield selectNode("#attributes", inspector);
yield testAddAttribute();
yield testEditAttribute();
yield testRemoveAttribute();
function* testAddAttribute() {
- info("Testing 'Add Attribute' menu item");
+ info("Triggering 'Add Attribute' and waiting for mutation to occur");
let addAttribute = getMenuItem("node-menu-add-attribute");
+ addAttribute.click();
- info("Triggering 'Add Attribute' and waiting for mutation to occur");
- dispatchCommandEvent(addAttribute);
EventUtils.synthesizeKey('class="u-hidden"', {});
let onMutation = inspector.once("markupmutation");
EventUtils.synthesizeKey("VK_RETURN", {});
yield onMutation;
let hasAttribute = testActor.hasNode("#attributes.u-hidden");
ok(hasAttribute, "attribute was successfully added");
}
@@ -34,17 +33,17 @@ add_task(function* () {
info("Testing 'Edit Attribute' menu item");
let editAttribute = getMenuItem("node-menu-edit-attribute");
info("Triggering 'Edit Attribute' and waiting for mutation to occur");
inspector.nodeMenuTriggerInfo = {
type: "attribute",
name: "data-edit"
};
- dispatchCommandEvent(editAttribute);
+ editAttribute.click();
EventUtils.synthesizeKey("data-edit='edited'", {});
let onMutation = inspector.once("markupmutation");
EventUtils.synthesizeKey("VK_RETURN", {});
yield onMutation;
let isAttributeChanged =
yield testActor.hasNode("#attributes[data-edit='edited']");
ok(isAttributeChanged, "attribute was successfully edited");
@@ -55,21 +54,26 @@ add_task(function* () {
let removeAttribute = getMenuItem("node-menu-remove-attribute");
info("Triggering 'Remove Attribute' and waiting for mutation to occur");
inspector.nodeMenuTriggerInfo = {
type: "attribute",
name: "data-remove"
};
let onMutation = inspector.once("markupmutation");
- dispatchCommandEvent(removeAttribute);
+ removeAttribute.click();
yield onMutation;
let hasAttribute = yield testActor.hasNode("#attributes[data-remove]");
ok(!hasAttribute, "attribute was successfully removed");
}
function getMenuItem(id) {
- let attribute = inspector.panelDoc.getElementById(id);
- ok(attribute, "Menu item '" + id + "' found");
- return attribute;
+ let allMenuItems = openContextMenuAndGetAllItems(inspector, {
+ target: getContainerForSelector("#attributes", inspector).tagLine,
+ });
+ let menuItem = allMenuItems.find(i => i.id === id);
+ ok(menuItem, "Menu item '" + id + "' found");
+ // Close the menu so synthesizing future keys won't select menu items.
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ return menuItem;
}
});
--- a/devtools/client/inspector/test/browser_inspector_menu-06-other.js
+++ b/devtools/client/inspector/test/browser_inspector_menu-06-other.js
@@ -9,76 +9,81 @@ add_task(function* () {
let { inspector, toolbox, testActor } = yield openInspectorForURL(TEST_URL);
yield testShowDOMProperties();
yield testDuplicateNode();
yield testDeleteNode();
yield testDeleteRootNode();
yield testScrollIntoView();
function* testShowDOMProperties() {
info("Testing 'Show DOM Properties' menu item.");
- let showDOMPropertiesNode = inspector.panelDoc.getElementById(
- "node-menu-showdomproperties");
+ let allMenuItems = openContextMenuAndGetAllItems(inspector);
+ let showDOMPropertiesNode =
+ allMenuItems.find(item => item.id === "node-menu-showdomproperties");
ok(showDOMPropertiesNode, "the popup menu has a show dom properties item");
let consoleOpened = toolbox.once("webconsole-ready");
info("Triggering 'Show DOM Properties' and waiting for inspector open");
- dispatchCommandEvent(showDOMPropertiesNode);
+ showDOMPropertiesNode.click();
yield consoleOpened;
let webconsoleUI = toolbox.getPanel("webconsole").hud.ui;
let messagesAdded = webconsoleUI.once("new-messages");
yield messagesAdded;
info("Checking if 'inspect($0)' was evaluated");
ok(webconsoleUI.jsterm.history[0] === "inspect($0)");
yield toolbox.toggleSplitConsole();
}
function* testDuplicateNode() {
info("Testing 'Duplicate Node' menu item for normal elements.");
yield selectNode(".duplicate", inspector);
is((yield testActor.getNumberOfElementMatches(".duplicate")), 1,
"There should initially be 1 .duplicate node");
- let menuItem = inspector.panelDoc.getElementById("node-menu-duplicatenode");
+ let allMenuItems = openContextMenuAndGetAllItems(inspector);
+ let menuItem =
+ allMenuItems.find(item => item.id === "node-menu-duplicatenode");
ok(menuItem, "'Duplicate node' menu item should exist");
info("Triggering 'Duplicate Node' and waiting for inspector to update");
let updated = inspector.once("markupmutation");
- dispatchCommandEvent(menuItem);
+ menuItem.click();
yield updated;
is((yield testActor.getNumberOfElementMatches(".duplicate")), 2,
"The duplicated node should be in the markup.");
let container = yield getContainerForSelector(".duplicate + .duplicate",
inspector);
ok(container, "A MarkupContainer should be created for the new node");
}
function* testDeleteNode() {
info("Testing 'Delete Node' menu item for normal elements.");
yield selectNode("#delete", inspector);
- let deleteNode = inspector.panelDoc.getElementById("node-menu-delete");
+ let allMenuItems = openContextMenuAndGetAllItems(inspector);
+ let deleteNode = allMenuItems.find(item => item.id === "node-menu-delete");
ok(deleteNode, "the popup menu has a delete menu item");
let updated = inspector.once("inspector-updated");
info("Triggering 'Delete Node' and waiting for inspector to update");
- dispatchCommandEvent(deleteNode);
+ deleteNode.click();
yield updated;
ok(!(yield testActor.hasNode("#delete")), "Node deleted");
}
function* testDeleteRootNode() {
info("Testing 'Delete Node' menu item does not delete root node.");
yield selectNode("html", inspector);
- let deleteNode = inspector.panelDoc.getElementById("node-menu-delete");
- dispatchCommandEvent(deleteNode);
+ let allMenuItems = openContextMenuAndGetAllItems(inspector);
+ let deleteNode = allMenuItems.find(item => item.id === "node-menu-delete");
+ deleteNode.click();
let deferred = promise.defer();
executeSoon(deferred.resolve);
yield deferred.promise;
ok((yield testActor.eval("!!content.document.documentElement")),
"Document element still alive.");
}
--- a/devtools/client/inspector/test/browser_inspector_pseudoclass-menu.js
+++ b/devtools/client/inspector/test/browser_inspector_pseudoclass-menu.js
@@ -10,45 +10,36 @@ const TEST_URI = "data:text/html;charset
"pseudo-class lock node menu tests" +
"<div>test div</div>";
const PSEUDOS = ["hover", "active", "focus"];
add_task(function* () {
let {inspector, testActor} = yield openInspectorForURL(TEST_URI);
yield selectNode("div", inspector);
- info("Getting the inspector ctx menu and opening it");
- let menu = inspector.panelDoc.getElementById("inspector-node-popup");
- yield openMenu(menu);
+ let allMenuItems = openContextMenuAndGetAllItems(inspector);
- yield testMenuItems(testActor, menu, inspector);
-
- menu.hidePopup();
+ yield testMenuItems(testActor, allMenuItems, inspector);
});
-function openMenu(menu) {
- let promise = once(menu, "popupshowing", true);
- menu.openPopup();
- return promise;
-}
-
-function* testMenuItems(testActor, menu, inspector) {
+function* testMenuItems(testActor, allMenuItems, inspector) {
for (let pseudo of PSEUDOS) {
- let menuitem = inspector.panelDoc.getElementById(
- "node-menu-pseudo-" + pseudo);
- ok(menuitem, ":" + pseudo + " menuitem exists");
+ let menuItem =
+ allMenuItems.find(item => item.id === "node-menu-pseudo-" + pseudo);
+ ok(menuItem, ":" + pseudo + " menuitem exists");
+ is(menuItem.disabled, false, ":" + pseudo + " menuitem is enabled");
// Give the inspector panels a chance to update when the pseudoclass changes
let onPseudo = inspector.selection.once("pseudoclass");
let onRefresh = inspector.once("rule-view-refreshed");
// Walker uses SDK-events so calling walker.once does not return a promise.
let onMutations = once(inspector.walker, "mutations");
- menuitem.doCommand();
+ menuItem.click();
yield onPseudo;
yield onRefresh;
yield onMutations;
let hasLock = yield testActor.hasPseudoClassLock("div", ":" + pseudo);
ok(hasLock, "pseudo-class lock has been applied");
}
--- a/devtools/client/inspector/test/head.js
+++ b/devtools/client/inspector/test/head.js
@@ -189,21 +189,24 @@ var openInspectorForURL = Task.async(fun
*/
var openInspector = Task.async(function* (hostType) {
info("Opening the inspector");
let toolbox = yield openToolboxForTab(gBrowser.selectedTab, "inspector",
hostType);
let inspector = toolbox.getPanel("inspector");
- info("Waiting for the inspector to update");
if (inspector._updateProgress) {
+ info("Need to wait for the inspector to update");
yield inspector.once("inspector-updated");
}
+ info("Waiting for actor features to be detected");
+ yield inspector._detectingActorFeatures;
+
yield registerTestActor(toolbox.target.client);
let testActor = yield getTestActor(toolbox);
return {toolbox, inspector, testActor};
});
function getActiveInspector() {
let target = TargetFactory.forTab(gBrowser.selectedTab);
@@ -508,42 +511,16 @@ function redoChange(inspector) {
}
let mutated = inspector.once("markupmutation");
inspector.markup.undo.redo();
return mutated;
}
/**
- * Dispatch a command event on a node (e.g. click on a contextual menu item).
- * @param {DOMNode} node
- */
-function dispatchCommandEvent(node) {
- info("Dispatching command event on " + node);
- let commandEvent = document.createEvent("XULCommandEvent");
- commandEvent.initCommandEvent("command", true, true, window, 0, false, false,
- false, false, null);
- node.dispatchEvent(commandEvent);
-}
-
-/**
- * A helper that simulates a contextmenu event on the given chrome DOM element.
- */
-function contextMenuClick(element) {
- let evt = element.ownerDocument.createEvent("MouseEvents");
- let button = 2;
-
- evt.initMouseEvent("contextmenu", true, true,
- element.ownerDocument.defaultView, 1, 0, 0, 0, 0, false,
- false, false, false, button, null);
-
- element.dispatchEvent(evt);
-}
-
-/**
* 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);
}
@@ -824,8 +801,28 @@ function isHoverTooltipTarget(tooltip, t
*/
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;
+}
--- a/devtools/client/locales/en-US/inspector.dtd
+++ b/devtools/client/locales/en-US/inspector.dtd
@@ -1,179 +1,17 @@
-<!-- LOCALIZATION NOTE (inspectorHTMLEdit.label): This is the label shown
- in the inspector contextual-menu for the item that lets users edit the
- (outer) HTML of the current node -->
-<!ENTITY inspectorHTMLEdit.label "Edit As HTML">
-<!ENTITY inspectorHTMLEdit.accesskey "E">
-
-<!-- LOCALIZATION NOTE (inspectorCopyInnerHTML.label): This is the label shown
- in the inspector contextual-menu for the item that lets users copy the
- inner HTML of the current node -->
-<!ENTITY inspectorCopyInnerHTML.label "Inner HTML">
-<!ENTITY inspectorCopyInnerHTML.accesskey "I">
-
-<!-- LOCALIZATION NOTE (inspectorCopyOuterHTML.label): This is the label shown
- in the inspector contextual-menu for the item that lets users copy the
- outer HTML of the current node -->
-<!ENTITY inspectorCopyOuterHTML.label "Outer HTML">
-<!ENTITY inspectorCopyOuterHTML.accesskey "O">
-
-<!-- LOCALIZATION NOTE (inspectorCopyCSSSelector.label): This is the label
- shown in the inspector contextual-menu for the item that lets users copy
- the CSS Selector of the current node -->
-<!ENTITY inspectorCopyCSSSelector.label "CSS Selector">
-<!ENTITY inspectorCopyCSSSelector.accesskey "S">
-
-<!-- LOCALIZATION NOTE (inspectorPasteOuterHTML.label): This is the label shown
- in the inspector contextual-menu for the item that lets users paste outer
- HTML in the current node -->
-<!ENTITY inspectorPasteOuterHTML.label "Outer HTML">
-<!ENTITY inspectorPasteOuterHTML.accesskey "O">
-
-<!-- LOCALIZATION NOTE (inspectorPasteInnerHTML.label): This is the label shown
- in the inspector contextual-menu for the item that lets users paste inner
- HTML in the current node -->
-<!ENTITY inspectorPasteInnerHTML.label "Inner HTML">
-<!ENTITY inspectorPasteInnerHTML.accesskey "I">
-
-<!-- LOCALIZATION NOTE (inspectorHTMLPasteExtraSubmenu.label): This is the label
- shown in the inspector contextual-menu for the sub-menu of the other Paste
- items, which allow to paste HTML:
- - before the current node
- - after the current node
- - as the first child of the current node
- - as the last child of the current node -->
-<!ENTITY inspectorHTMLPasteExtraSubmenu.label "Paste…">
-<!ENTITY inspectorHTMLPasteExtraSubmenu.accesskey "t">
-
-<!-- LOCALIZATION NOTE (inspectorHTMLPasteBefore.label): This is the label shown
- in the inspector contextual-menu for the item that lets users paste
- the HTML before the current node -->
-<!ENTITY inspectorHTMLPasteBefore.label "Before">
-<!ENTITY inspectorHTMLPasteBefore.accesskey "B">
-
-<!-- LOCALIZATION NOTE (inspectorHTMLPasteAfter.label): This is the label shown
- in the inspector contextual-menu for the item that lets users paste
- the HTML after the current node -->
-<!ENTITY inspectorHTMLPasteAfter.label "After">
-<!ENTITY inspectorHTMLPasteAfter.accesskey "A">
-
-<!-- LOCALIZATION NOTE (inspectorHTMLPasteFirstChild.label): This is the label
- shown in the inspector contextual-menu for the item that lets users paste
- the HTML as the first child the current node -->
-<!ENTITY inspectorHTMLPasteFirstChild.label "As First Child">
-<!ENTITY inspectorHTMLPasteFirstChild.accesskey "F">
-
-<!-- LOCALIZATION NOTE (inspectorHTMLPasteLastChild.label): This is the label
- shown in the inspector contextual-menu for the item that lets users paste
- the HTML as the last child the current node -->
-<!ENTITY inspectorHTMLPasteLastChild.label "As Last Child">
-<!ENTITY inspectorHTMLPasteLastChild.accesskey "L">
-
-<!-- LOCALIZATION NOTE (inspectorScrollNodeIntoView.label): This is the label
- shown in the inspector contextual-menu for the item that lets users scroll
- the current node into view -->
-<!ENTITY inspectorScrollNodeIntoView.label "Scroll Into View">
-<!ENTITY inspectorScrollNodeIntoView.accesskey "S">
-
-<!-- LOCALIZATION NOTE (inspectorHTMLDelete.label): This is the label shown in
- the inspector contextual-menu for the item that lets users delete the
- current node -->
-<!ENTITY inspectorHTMLDelete.label "Delete Node">
-<!ENTITY inspectorHTMLDelete.accesskey "D">
-
-<!-- LOCALIZATION NOTE (inspectorAttributesSubmenu.label): This is the label
- shown in the inspector contextual-menu for the sub-menu of the other
- attribute items, which allow to:
- - add new attribute
- - edit attribute
- - remove attribute -->
-<!ENTITY inspectorAttributesSubmenu.label "Attributes">
-<!ENTITY inspectorAttributesSubmenu.accesskey "A">
-
-<!-- LOCALIZATION NOTE (inspectorAddAttribute.label): This is the label shown in
- the inspector contextual-menu for the item that lets users add attribute
- to current node -->
-<!ENTITY inspectorAddAttribute.label "Add Attribute">
-<!ENTITY inspectorAddAttribute.accesskey "A">
-
-<!-- LOCALIZATION NOTE (inspectorEditAttribute.label): This is the label shown in
- the inspector contextual-menu for the item that lets users edit attribute
- for current node -->
-<!ENTITY inspectorEditAttribute.label "Edit Attribute">
-<!ENTITY inspectorEditAttribute.accesskey "E">
-
<!-- LOCALIZATION NOTE (inspectorRemoveAttribute.label): This is the label shown in
the inspector contextual-menu for the item that lets users delete attribute
from current node -->
<!ENTITY inspectorRemoveAttribute.label "Remove Attribute">
<!ENTITY inspectorRemoveAttribute.accesskey "R">
<!ENTITY inspector.selectButton.tooltip "Select element with mouse">
<!-- LOCALIZATION NOTE (inspectorSearchHTML.label3): This is the label that is
shown as the placeholder for the markup view search in the inspector. -->
<!ENTITY inspectorSearchHTML.label3 "Search HTML">
-<!-- LOCALIZATION NOTE (inspectorImageDataUri.label): This is the label
- shown in the inspector contextual-menu for the item that lets users copy
- the URL embedding the image data encoded in Base 64 (what we name
- here Image Data URL). For more information:
- https://developer.mozilla.org/en-US/docs/Web/HTTP/data_URIs -->
-<!ENTITY inspectorImageDataUri.label "Image Data-URL">
-
-<!-- LOCALIZATION NOTE (inspectorShowDOMProperties.label): This is the label
- shown in the inspector contextual-menu for the item that lets users see
- the DOM properties of the current node. When triggered, this item
- opens the split Console and displays the properties in its side panel. -->
-<!ENTITY inspectorShowDOMProperties.label "Show DOM Properties">
-
-<!-- LOCALIZATION NOTE (inspectorUseInConsole.label): This is the label
- shown in the inspector contextual-menu for the item that outputs a
- variable for the current node to the console. When triggered,
- this item opens the split Console. -->
-<!ENTITY inspectorUseInConsole.label "Use in Console">
-
-<!-- LOCALIZATION NOTE (inspectorExpandNode.label): This is the label
- shown in the inspector contextual-menu for recursively expanding
- mark-up elements -->
-<!ENTITY inspectorExpandNode.label "Expand All">
-
-<!-- LOCALIZATION NOTE (inspectorCollapseNode.label): This is the label
- shown in the inspector contextual-menu for recursively collapsing
- mark-up elements -->
-<!ENTITY inspectorCollapseNode.label "Collapse">
-
-<!-- LOCALIZATION NOTE (inspectorScreenshotNode.label): This is the label
- shown in the inspector contextual-menu for the item that lets users take
- a screenshot of the currently selected node. -->
-<!ENTITY inspectorScreenshotNode.label "Screenshot Node">
-
-<!-- LOCALIZATION NOTE (inspectorDuplicateNode.label): This is the label
- shown in the inspector contextual-menu for the item that lets users
- duplicate the currently selected node. -->
-<!ENTITY inspectorDuplicateNode.label "Duplicate Node">
-
<!-- LOCALIZATION NOTE (inspectorAddNode.label): This is the label shown in
the inspector toolbar for the button that lets users add elements to the
DOM (as children of the currently selected element). -->
<!ENTITY inspectorAddNode.label "Create New Node">
<!ENTITY inspectorAddNode.accesskey "C">
-
-<!-- LOCALIZATION NOTE (inspectorCopyHTMLSubmenu.label): This is the label
- shown in the inspector contextual-menu for the sub-menu of the other
- copy items, which allow to:
- - Copy Inner HTML
- - Copy Outer HTML
- - Copy Unique selector
- - Copy Image data URI -->
-<!ENTITY inspectorCopyHTMLSubmenu.label "Copy">
-
-<!-- LOCALIZATION NOTE (inspectorPasteHTMLSubmenu.label): This is the label
- shown in the inspector contextual-menu for the sub-menu of the other
- paste items, which allow to:
- - Paste Inner HTML
- - Paste Outer HTML
- - Before
- - After
- - As First Child
- - As Last Child -->
-<!ENTITY inspectorPasteHTMLSubmenu.label "Paste">
--- a/devtools/client/locales/en-US/inspector.properties
+++ b/devtools/client/locales/en-US/inspector.properties
@@ -105,27 +105,29 @@ inspector.menu.copyUrlToClipboard.label=
# LOCALIZATION NOTE (inspector.menu.selectElement.label): This is the label of a
# menu item in the inspector contextual-menu that appears when the user right-
# clicks on the attribute of a node in the inspector that is the ID of another
# element in the DOM (like with <label for="input-id">), and that allows to
# select that element in the inspector.
inspector.menu.selectElement.label=Select Element #%S
-# LOCALIZATION NOTE (inspector.menu.editAttribute.label): This is the label of a
+# LOCALIZATION NOTE (inspectorEditAttribute.label): This is the label of a
# sub-menu "Attribute" in the inspector contextual-menu that appears
# when the user right-clicks on the node in the inspector, and that allows
# to edit an attribute on this node.
-inspector.menu.editAttribute.label=Edit Attribute %S
+inspectorEditAttribute.label=Edit Attribute %S
+inspectorEditAttribute.accesskey=E
-# LOCALIZATION NOTE (inspector.menu.removeAttribute.label): This is the label of a
+# LOCALIZATION NOTE (inspectorRemoveAttribute.label): This is the label of a
# sub-menu "Attribute" in the inspector contextual-menu that appears
# when the user right-clicks on the attribute of a node in the inspector,
# and that allows to remove this attribute.
-inspector.menu.removeAttribute.label=Remove Attribute %S
+inspectorRemoveAttribute.label=Remove Attribute %S
+inspectorRemoveAttribute.accesskey=R
# LOCALIZATION NOTE (inspector.nodePreview.selectNodeLabel):
# This string is displayed in a tooltip that is shown when hovering over a DOM
# node preview (e.g. something like "div#foo.bar").
# DOM node previews can be displayed in places like the animation-inspector, the
# console or the object inspector.
# The tooltip invites the user to click on the node in order to select it in the
# inspector panel.
@@ -136,16 +138,188 @@ inspector.nodePreview.selectNodeLabel=Cl
# inspector icon displayed next to a DOM node preview (e.g. next to something
# like "div#foo.bar").
# DOM node previews can be displayed in places like the animation-inspector, the
# console or the object inspector.
# The tooltip invites the user to click on the icon in order to highlight the
# node in the page.
inspector.nodePreview.highlightNodeLabel=Click to highlight this node in the page
+# LOCALIZATION NOTE (inspectorHTMLEdit.label): This is the label shown
+# in the inspector contextual-menu for the item that lets users edit the
+# (outer) HTML of the current node
+inspectorHTMLEdit.label=Edit As HTML
+inspectorHTMLEdit.accesskey=E
+
+# LOCALIZATION NOTE (inspectorCopyInnerHTML.label): This is the label shown
+# in the inspector contextual-menu for the item that lets users copy the
+# inner HTML of the current node
+inspectorCopyInnerHTML.label=Inner HTML
+inspectorCopyInnerHTML.accesskey=I
+
+# LOCALIZATION NOTE (inspectorCopyOuterHTML.label): This is the label shown
+# in the inspector contextual-menu for the item that lets users copy the
+# outer HTML of the current node
+inspectorCopyOuterHTML.label=Outer HTML
+inspectorCopyOuterHTML.accesskey=O
+
+# LOCALIZATION NOTE (inspectorCopyCSSSelector.label): This is the label
+# shown in the inspector contextual-menu for the item that lets users copy
+# the CSS Selector of the current node
+inspectorCopyCSSSelector.label=CSS Selector
+inspectorCopyCSSSelector.accesskey=S
+
+# LOCALIZATION NOTE (inspectorPasteOuterHTML.label): This is the label shown
+# in the inspector contextual-menu for the item that lets users paste outer
+# HTML in the current node
+inspectorPasteOuterHTML.label=Outer HTML
+inspectorPasteOuterHTML.accesskey=O
+
+# LOCALIZATION NOTE (inspectorPasteInnerHTML.label): This is the label shown
+# in the inspector contextual-menu for the item that lets users paste inner
+# HTML in the current node
+inspectorPasteInnerHTML.label=Inner HTML
+inspectorPasteInnerHTML.accesskey=I
+
+# LOCALIZATION NOTE (inspectorHTMLPasteExtraSubmenu.label): This is the label
+# shown in the inspector contextual-menu for the sub-menu of the other Paste
+# items, which allow to paste HTML:
+# - before the current node
+# - after the current node
+# - as the first child of the current node
+# - as the last child of the current node
+inspectorHTMLPasteExtraSubmenu.label=Paste…
+inspectorHTMLPasteExtraSubmenu.accesskey=t
+
+# LOCALIZATION NOTE (inspectorHTMLPasteBefore.label): This is the label shown
+# in the inspector contextual-menu for the item that lets users paste
+# the HTML before the current node
+inspectorHTMLPasteBefore.label=Before
+inspectorHTMLPasteBefore.accesskey=B
+
+# LOCALIZATION NOTE (inspectorHTMLPasteAfter.label): This is the label shown
+# in the inspector contextual-menu for the item that lets users paste
+# the HTML after the current node
+inspectorHTMLPasteAfter.label=After
+inspectorHTMLPasteAfter.accesskey=A
+
+# LOCALIZATION NOTE (inspectorHTMLPasteFirstChild.label): This is the label
+# shown in the inspector contextual-menu for the item that lets users paste
+# the HTML as the first child the current node
+inspectorHTMLPasteFirstChild.label=As First Child
+inspectorHTMLPasteFirstChild.accesskey=F
+
+# LOCALIZATION NOTE (inspectorHTMLPasteLastChild.label): This is the label
+# shown in the inspector contextual-menu for the item that lets users paste
+# the HTML as the last child the current node
+inspectorHTMLPasteLastChild.label=As Last Child
+inspectorHTMLPasteLastChild.accesskey=L
+
+# LOCALIZATION NOTE (inspectorScrollNodeIntoView.label): This is the label
+# shown in the inspector contextual-menu for the item that lets users scroll
+# the current node into view
+inspectorScrollNodeIntoView.label=Scroll Into View
+inspectorScrollNodeIntoView.accesskey=S
+
+# LOCALIZATION NOTE (inspectorHTMLDelete.label): This is the label shown in
+# the inspector contextual-menu for the item that lets users delete the
+# current node
+inspectorHTMLDelete.label=Delete Node
+inspectorHTMLDelete.accesskey=D
+
+# LOCALIZATION NOTE (inspectorAttributesSubmenu.label): This is the label
+# shown in the inspector contextual-menu for the sub-menu of the other
+# attribute items, which allow to:
+# - add new attribute
+# - edit attribute
+# - remove attribute
+inspectorAttributesSubmenu.label=Attributes
+inspectorAttributesSubmenu.accesskey=A
+
+# LOCALIZATION NOTE (inspectorAddAttribute.label): This is the label shown in
+# the inspector contextual-menu for the item that lets users add attribute
+# to current node
+inspectorAddAttribute.label=Add Attribute
+inspectorAddAttribute.accesskey=A
+
+# LOCALIZATION NOTE (inspectorSearchHTML.label2): This is the label shown as
+# the placeholder in inspector search box
+inspectorSearchHTML.label2=Search with CSS Selectors
+inspectorSearchHTML.key=F
+
+# LOCALIZATION NOTE (inspectorSearchHTML.label3): This is the label that is
+# shown as the placeholder for the markup view search in the inspector.
+inspectorSearchHTML.label3=Search HTML
+
+# LOCALIZATION NOTE (inspectorImageDataUri.label): This is the label
+# shown in the inspector contextual-menu for the item that lets users copy
+# the URL embedding the image data encoded in Base 64 (what we name
+# here Image Data URL). For more information:
+# https://developer.mozilla.org/en-US/docs/Web/HTTP/data_URIs
+inspectorImageDataUri.label=Image Data-URL
+
+# LOCALIZATION NOTE (inspectorShowDOMProperties.label): This is the label
+# shown in the inspector contextual-menu for the item that lets users see
+# the DOM properties of the current node. When triggered, this item
+# opens the split Console and displays the properties in its side panel.
+inspectorShowDOMProperties.label=Show DOM Properties
+
+# LOCALIZATION NOTE (inspectorUseInConsole.label): This is the label
+# shown in the inspector contextual-menu for the item that outputs a
+# variable for the current node to the console. When triggered,
+# this item opens the split Console.
+inspectorUseInConsole.label=Use in Console
+
+# LOCALIZATION NOTE (inspectorExpandNode.label): This is the label
+# shown in the inspector contextual-menu for recursively expanding
+# mark-up elements
+inspectorExpandNode.label=Expand All
+
+# LOCALIZATION NOTE (inspectorCollapseNode.label): This is the label
+# shown in the inspector contextual-menu for recursively collapsing
+# mark-up elements
+inspectorCollapseNode.label=Collapse
+
+# LOCALIZATION NOTE (inspectorScreenshotNode.label): This is the label
+# shown in the inspector contextual-menu for the item that lets users take
+# a screenshot of the currently selected node.
+inspectorScreenshotNode.label=Screenshot Node
+
+# LOCALIZATION NOTE (inspectorDuplicateNode.label): This is the label
+# shown in the inspector contextual-menu for the item that lets users
+# duplicate the currently selected node.
+inspectorDuplicateNode.label=Duplicate Node
+
+# LOCALIZATION NOTE (inspectorAddNode.label): This is the label shown in
+# the inspector toolbar for the button that lets users add elements to the
+# DOM (as children of the currently selected element).
+inspectorAddNode.label=Create New Node
+inspectorAddNode.accesskey=C
+
+# LOCALIZATION NOTE (inspectorCopyHTMLSubmenu.label): This is the label
+# shown in the inspector contextual-menu for the sub-menu of the other
+# copy items, which allow to:
+# - Copy Inner HTML
+# - Copy Outer HTML
+# - Copy Unique selector
+# - Copy Image data URI
+inspectorCopyHTMLSubmenu.label=Copy
+
+# LOCALIZATION NOTE (inspectorPasteHTMLSubmenu.label): This is the label
+# shown in the inspector contextual-menu for the sub-menu of the other
+# paste items, which allow to:
+# - Paste Inner HTML
+# - Paste Outer HTML
+# - Before
+# - After
+# - As First Child
+# - As Last Child
+inspectorPasteHTMLSubmenu.label=Paste
+
+
# LOCALIZATION NOTE (inspector.searchHTML.key):
# Key shortcut used to focus the DOM element search box on top-right corner of
# the markup view
inspector.searchHTML.key=CmdOrCtrl+F
# LOCALIZATION NOTE (markupView.hide.key):
# Key shortcut used to hide the selected node in the markup view.
markupView.hide.key=h