author | Léon McGregor <lemcgregor3@outlook.com> |
Tue, 09 Jun 2015 03:58:00 -0400 | |
changeset 248139 | 846253a39b05f5449f8750d92dcbdb7a480388d4 |
parent 248138 | d94caa738b3fb71b96630ff4c87841c21f3033d8 |
child 248140 | 1bd6254c5f41d21145696dc6c7496a45d73fab6a |
push id | 60888 |
push user | kwierso@gmail.com |
push date | Thu, 11 Jun 2015 01:38:38 +0000 |
treeherder | mozilla-inbound@39e638ed06bf [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | pbrosset |
bugs | 1163332 |
milestone | 41.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/devtools/inspector/inspector-panel.js +++ b/browser/devtools/inspector/inspector-panel.js @@ -23,16 +23,18 @@ loader.lazyGetter(this, "strings", () => }); loader.lazyGetter(this, "toolboxStrings", () => { return Services.strings.createBundle("chrome://browser/locale/devtools/toolbox.properties"); }); loader.lazyGetter(this, "clipboardHelper", () => { return Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper); }); +loader.lazyImporter(this, "CommandUtils", "resource:///modules/devtools/DeveloperToolbar.jsm"); + const LAYOUT_CHANGE_TIMER = 250; /** * Represents an open instance of the Inspector for a tab. * The inspector controls the breadcrumbs, the markup view, and the sidebar * (computed view, rule view, font view and layout view). * * Events: @@ -647,16 +649,19 @@ InspectorPanel.prototype = { /** * Disable the delete item if needed. Update the pseudo classes. */ _setupNodeMenu: function() { let isSelectionElement = this.selection.isElementNode() && !this.selection.isPseudoElementNode(); let isEditableElement = isSelectionElement && !this.selection.isAnonymousNode(); + let isScreenshotable = isSelectionElement && + this.canGetUniqueSelector && + this.selection.nodeFront.isTreeDisplayed; // Set the pseudo classes for (let name of ["hover", "active", "focus"]) { let menu = this.panelDoc.getElementById("node-menu-pseudo-" + name); if (isSelectionElement) { let checked = this.selection.nodeFront.hasPseudoClassLock(":" + name); menu.setAttribute("checked", checked); @@ -670,18 +675,19 @@ InspectorPanel.prototype = { let deleteNode = this.panelDoc.getElementById("node-menu-delete"); if (isEditableElement) { deleteNode.removeAttribute("disabled"); } else { deleteNode.setAttribute("disabled", "true"); } // Disable / enable "Copy Unique Selector", "Copy inner HTML", - // "Copy outer HTML" & "Scroll Into View" as appropriate + // "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 copyInnerHTML = this.panelDoc.getElementById("node-menu-copyinner"); let copyOuterHTML = this.panelDoc.getElementById("node-menu-copyouter"); let scrollIntoView = this.panelDoc.getElementById("node-menu-scrollnodeintoview"); this._target.actorHasMethod("domnode", "scrollIntoView").then(value => { scrollIntoView.hidden = !value; }); @@ -695,16 +701,22 @@ InspectorPanel.prototype = { 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"); + } + // Enable/Disable the link open/copy items. this._setupNodeLinkMenu(); // 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"); @@ -1064,16 +1076,27 @@ InspectorPanel.prototype = { } this.selection.nodeFront.getUniqueSelector().then((selector) => { clipboardHelper.copyString(selector); }).then(null, console.error); }, /** + * Initiate gcli screenshot command on selected node + */ + screenshotNode: function() { + CommandUtils.createRequisition(this._target, { + environment: CommandUtils.createEnvironment(this, '_target') + }).then(requisition => { + requisition.updateExec("screenshot --selector " + this.selectionCssSelector); + }); + }, + + /** * Scroll the node into view. */ scrollNodeIntoView: function() { if (!this.selection.isNode()) { return; } this.selection.nodeFront.scrollIntoView();
--- a/browser/devtools/inspector/inspector.xul +++ b/browser/devtools/inspector/inspector.xul @@ -86,16 +86,19 @@ oncommand="inspector.pasteAdjacentHTML('beforeEnd')"/> </menupopup> </menu> <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-delete" label="&inspectorHTMLDelete.label;" accesskey="&inspectorHTMLDelete.accesskey;" oncommand="inspector.deleteNode()"/> <menuseparator id="node-menu-link-separator"/> <menuitem id="node-menu-link-follow" oncommand="inspector.followAttributeLink()"/> <menuitem id="node-menu-link-copy"
--- a/browser/devtools/inspector/test/browser_inspector_menu-01-sensitivity.js +++ b/browser/devtools/inspector/test/browser_inspector_menu-01-sensitivity.js @@ -22,17 +22,18 @@ const ALL_MENU_ITEMS = [ "node-menu-copyouter", "node-menu-copyuniqueselector", "node-menu-copyimagedatauri", "node-menu-showdomproperties", "node-menu-delete", "node-menu-pseudo-hover", "node-menu-pseudo-active", "node-menu-pseudo-focus", - "node-menu-scrollnodeintoview" + "node-menu-scrollnodeintoview", + "node-menu-screenshotnode" ].concat(PASTE_MENU_ITEMS); const ITEMS_WITHOUT_SHOWDOMPROPS = ALL_MENU_ITEMS.filter(item => item != "node-menu-showdomproperties"); const TEST_CASES = [ { desc: "doctype node with empty clipboard", @@ -88,17 +89,26 @@ const TEST_CASES = [ desc: "<head> with HTML on clipboard", clipboardData: "<p>some text</p>", clipboardDataType: "html", selector: "head", disabled: [ "node-menu-copyimagedatauri", "node-menu-pastebefore", "node-menu-pasteafter", - ] + "node-menu-screenshotnode", + ], + }, + { + desc: "<head> with no html on clipboard", + selector: "head", + disabled: PASTE_MENU_ITEMS.concat([ + "node-menu-copyimagedatauri", + "node-menu-screenshotnode", + ]), }, { desc: "<element> with text on clipboard", clipboardData: "some text", clipboardDataType: undefined, selector: "#paste-area", disabled: ["node-menu-copyimagedatauri"], }, @@ -120,16 +130,32 @@ const TEST_CASES = [ }, { desc: "<element> with whitespace only on clipboard", clipboardData: " \n\n\t\n\n \n", clipboardDataType: undefined, selector: "#paste-area", disabled: PASTE_MENU_ITEMS.concat(["node-menu-copyimagedatauri"]), }, + { + desc: "<element> that isn't visible on the page, empty clipboard", + selector: "#hiddenElement", + disabled: PASTE_MENU_ITEMS.concat([ + "node-menu-copyimagedatauri", + "node-menu-screenshotnode", + ]), + }, + { + desc: "<element> nested in another hidden element, empty clipboard", + selector: "#nestedHiddenElement", + disabled: PASTE_MENU_ITEMS.concat([ + "node-menu-copyimagedatauri", + "node-menu-screenshotnode", + ]), + } ]; let clipboard = require("sdk/clipboard"); registerCleanupFunction(() => { clipboard = null; }); add_task(function *() {
--- a/browser/devtools/inspector/test/doc_inspector_menu.html +++ b/browser/devtools/inspector/test/doc_inspector_menu.html @@ -12,11 +12,14 @@ <p class="adjacent"> <span class="ref">3</span> </p> </div> <p data-id="copy">Paragraph for testing copy</p> <p id="sensitivity">Paragraph for sensitivity</p> <p id="delete">This has to be deleted</p> <img id="copyimage" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQYV2P4DwABAQEAWk1v8QAAAABJRU5ErkJggg==" /> + <div id="hiddenElement" style="display: none;"> + <p id="nestedHiddenElement">Visible element nested inside a non-visible element</p> + </div> </div> </body> </html>
--- a/browser/locales/en-US/chrome/browser/devtools/inspector.dtd +++ b/browser/locales/en-US/chrome/browser/devtools/inspector.dtd @@ -99,8 +99,13 @@ https://developer.mozilla.org/en-US/docs/Web/HTTP/data_URIs --> <!ENTITY inspectorCopyImageDataUri.label "Copy 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 (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">
--- a/toolkit/devtools/server/actors/inspector.js +++ b/toolkit/devtools/server/actors/inspector.js @@ -930,16 +930,27 @@ let NodeFront = protocol.FrontClass(Node }, get isDisplayed() { // The NodeActor's form contains the isDisplayed information as a boolean // starting from FF32. Before that, the property is missing return "isDisplayed" in this._form ? this._form.isDisplayed : true; }, + get isTreeDisplayed() { + let parent = this; + while (parent) { + if (!parent.isDisplayed) { + return false; + } + parent = parent.parentNode(); + } + return true; + }, + getNodeValue: protocol.custom(function() { if (!this.incompleteValue) { return delayedResolve(new ShortLongString(this.shortValue)); } else { return this._getNodeValue(); } }, { impl: "_getNodeValue"