Bug 587134 - Context menu item for Highlight Element (highlighter); r=limi, r=gavin
☠☠ backed out by 931650933b13 ☠ ☠
authorPanos Astithas <past@mozilla.com>
Tue, 20 Sep 2011 11:05:56 +0300
changeset 77246 304f93baaa1bfc7ac0249cfd7d303920c0f3294e
parent 77245 eff517413fc19fcd8128ed1236e8ab98675161b9
child 77247 931650933b13a73b536918cebdd4360d5e9ba4c8
push id21188
push userrcampbell@mozilla.com
push dateWed, 21 Sep 2011 11:30:48 +0000
treeherdermozilla-central@3d181775477f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslimi, gavin
bugs587134
milestone9.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
Bug 587134 - Context menu item for Highlight Element (highlighter); r=limi, r=gavin
browser/base/content/browser-context.inc
browser/base/content/browser.js
browser/base/content/inspector.js
browser/base/content/nsContextMenu.js
browser/base/content/test/inspector/browser_inspector_initialization.js
browser/locales/en-US/chrome/browser/browser.dtd
--- a/browser/base/content/browser-context.inc
+++ b/browser/base/content/browser-context.inc
@@ -16,16 +16,17 @@
 #
 # The Initial Developer of the Original Code is
 # Netscape Communications Corporation.
 # Portions created by the Initial Developer are Copyright (C) 2001
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Ehsan Akhgari <ehsan.akhgari@gmail.com>
+#   Rob Campbell <rcampbell@mozilla.com>
 #
 # Alternatively, the contents of this file may be used under the terms of
 # either the GNU General Public License Version 2 or later (the "GPL"), or
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 # in which case the provisions of the GPL or the LGPL are applicable instead
 # of those above. If you wish to allow use of your version of this file only
 # under the terms of either the GPL or the LGPL, and not to allow others to
 # use your version of this file under the terms of the MPL, indicate your
@@ -335,8 +336,13 @@
       <menuitem hidden="true" id="context-bidi-text-direction-toggle"
                 label="&bidiSwitchTextDirectionItem.label;"
                 accesskey="&bidiSwitchTextDirectionItem.accesskey;"
                 command="cmd_switchTextDirection"/>
       <menuitem hidden="true" id="context-bidi-page-direction-toggle"
                 label="&bidiSwitchPageDirectionItem.label;"
                 accesskey="&bidiSwitchPageDirectionItem.accesskey;"
                 oncommand="gContextMenu.switchPageDirection();"/>
+      <menuseparator id="inspect-separator"/>
+      <menuitem id="context-inspect"
+                label="&inspectContextMenu.label;"
+                accesskey="&inspectContextMenu.accesskey;"
+                oncommand="gContextMenu.inspectNode();"/>
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1671,18 +1671,17 @@ function delayedStartup(isLoadingBlank, 
 #ifdef MOZ_SERVICES_SYNC
   // initialize the sync UI
   gSyncUI.init();
 #endif
 
   TabView.init();
 
   // Enable Inspector?
-  let enabled = gPrefService.getBoolPref(InspectorUI.prefEnabledName);
-  if (enabled) {
+  if (InspectorUI.enabled) {
     document.getElementById("menu_pageinspect").hidden = false;
     document.getElementById("Tools:Inspect").removeAttribute("disabled");
 #ifdef MENUBAR_CAN_AUTOHIDE
     document.getElementById("appmenu_pageInspect").hidden = false;
 #endif
   }
 
   // Enable Error Console?
--- a/browser/base/content/inspector.js
+++ b/browser/base/content/inspector.js
@@ -553,17 +553,20 @@ Highlighter.prototype = {
  * Main controller class for the Inspector.
  */
 var InspectorUI = {
   browser: null,
   tools: {},
   showTextNodesWithWhitespace: false,
   inspecting: false,
   treeLoaded: false,
-  prefEnabledName: "devtools.inspector.enabled",
+  get enabled()
+  {
+    return gPrefService.getBoolPref("devtools.inspector.enabled");
+  },
   isDirty: false,
 
   /**
    * Toggle the inspector interface elements on or off.
    *
    * @param aEvent
    *        The event that requested the UI change. Toolbar button or menu.
    */
@@ -645,22 +648,22 @@ var InspectorUI = {
       this.treeIFrame.setAttribute("ondblclick", "InspectorUI.onTreeDblClick(event);");
       this.treeIFrame = this.treePanel.insertBefore(this.treeIFrame, resizerBox);
     }
 
     this.treePanel.addEventListener("popupshown", function treePanelShown() {
       InspectorUI.treePanel.removeEventListener("popupshown",
         treePanelShown, false);
 
-        InspectorUI.treeIFrame.addEventListener("load",
-          function loadedInitializeTreePanel() {
-            InspectorUI.treeIFrame.removeEventListener("load",
-              loadedInitializeTreePanel, true);
-            InspectorUI.initializeTreePanel();
-          }, true);
+      InspectorUI.treeIFrame.addEventListener("load",
+        function loadedInitializeTreePanel() {
+          InspectorUI.treeIFrame.removeEventListener("load",
+            loadedInitializeTreePanel, true);
+          InspectorUI.initializeTreePanel();
+        }, true);
 
       let src = InspectorUI.treeIFrame.getAttribute("src");
       if (src != "chrome://browser/content/inspector.html") {
         InspectorUI.treeIFrame.setAttribute("src",
           "chrome://browser/content/inspector.html");
       } else {
         InspectorUI.treeIFrame.contentWindow.location.reload();
       }
@@ -774,22 +777,43 @@ var InspectorUI = {
 
     if (!next)
       delete this.treeWalker;
 
     return next;
   },
 
   /**
-   * Open inspector UI. tree. Add listeners for document scrolling,
-   * resize, tabContainer.TabSelect and others.
+   * Open inspector UI and HTML tree. Add listeners for document scrolling,
+   * resize, tabContainer.TabSelect and others. If a node is provided, then
+   * start inspecting it.
+   *
+   * @param [optional] aNode
+   *        The node to inspect.
    */
-  openInspectorUI: function IUI_openInspectorUI()
+  openInspectorUI: function IUI_openInspectorUI(aNode)
   {
-    // initialization
+    // Observer used to inspect the specified element from content after the
+    // inspector UI has been opened.
+    function inspectObserver(aElement) {
+      Services.obs.removeObserver(boundInspectObserver,
+                                  INSPECTOR_NOTIFICATIONS.OPENED,
+                                  false);
+      this.inspectNode(aElement);
+      this.stopInspecting();
+    };
+    var boundInspectObserver = inspectObserver.bind(this, aNode);
+
+    if (aNode) {
+      // Add the observer to inspect the node after initialization finishes.
+      Services.obs.addObserver(boundInspectObserver,
+                               INSPECTOR_NOTIFICATIONS.OPENED,
+                               false);
+    }
+    // Start initialization.
     this.browser = gBrowser.selectedBrowser;
     this.win = this.browser.contentWindow;
     this.winID = this.getWindowID(this.win);
     this.toolbar = document.getElementById("inspector-toolbar");
 
     if (!this.domplate) {
       Cu.import("resource:///modules/domplate.jsm", this);
       this.domplateUtils.setDOM(window);
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -41,16 +41,17 @@
 #   Simon B├╝nzli <zeniko@gmail.com>
 #   Gijs Kruitbosch <gijskruitbosch@gmail.com>
 #   Ehsan Akhgari <ehsan.akhgari@gmail.com>
 #   Dan Mosedale <dmose@mozilla.org>
 #   Justin Dolske <dolske@mozilla.com>
 #   Kathleen Brade <brade@pearlcrescent.com>
 #   Mark Smith <mcs@pearlcrescent.com>
 #   Kailas Patil <patilkr24@gmail.com>
+#   Rob Campbell <rcampbell@mozilla.com>
 #
 # Alternatively, the contents of this file may be used under the terms of
 # either the GNU General Public License Version 2 or later (the "GPL"), or
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 # in which case the provisions of the GPL or the LGPL are applicable instead
 # of those above. If you wish to allow use of your version of this file only
 # under the terms of either the GPL or the LGPL, and not to allow others to
 # use your version of this file under the terms of the MPL, indicate your
@@ -241,16 +242,17 @@ nsContextMenu.prototype = {
                   this.onMathML && !this.isContentSelected);
 
     var shouldShow = !(this.isContentSelected ||
                        this.onImage || this.onCanvas ||
                        this.onVideo || this.onAudio ||
                        this.onLink || this.onTextInput);
     this.showItem("context-viewsource", shouldShow);
     this.showItem("context-viewinfo", shouldShow);
+    this.showItem("context-inspect", InspectorUI.enabled);
 
     this.showItem("context-sep-viewsource", shouldShow);
 
     // Set as Desktop background depends on whether an image was clicked on,
     // and only works if we have a shell service.
     var haveSetDesktopBackground = false;
 #ifdef HAVE_SHELL_SERVICE
     // Only enable Set as Desktop Background if we can get the shell service.
@@ -424,16 +426,25 @@ nsContextMenu.prototype = {
       this.setItemAttr("context-media-showcontrols", "disabled", hasError);
       this.setItemAttr("context-media-hidecontrols", "disabled", hasError);
       if (this.onVideo)
         this.setItemAttr("context-video-fullscreen",  "disabled", hasError);
     }
     this.showItem("context-media-sep-commands",  onMedia);
   },
 
+  inspectNode: function CM_inspectNode() {
+    if (InspectorUI.isTreePanelOpen) {
+      InspectorUI.inspectNode(this.target);
+      InspectorUI.stopInspecting();
+    } else {
+      InspectorUI.openInspectorUI(this.target);
+    }
+  },
+
   // Set various context menu attributes based on the state of the world.
   setTarget: function (aNode, aRangeParent, aRangeOffset) {
     const xulNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
     if (aNode.namespaceURI == xulNS ||
         aNode.nodeType == Node.DOCUMENT_NODE ||
         this.isTargetAFormControl(aNode)) {
       this.shouldDisplay = false;
       return;
--- a/browser/base/content/test/inspector/browser_inspector_initialization.js
+++ b/browser/base/content/test/inspector/browser_inspector_initialization.js
@@ -31,44 +31,96 @@
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
+let doc;
+let salutation;
+
+function createDocument()
+{
+  doc.body.innerHTML = '<div id="first" style="{ margin: 10em; ' +
+    'font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA}">\n' +
+    '<h1>Some header text</h1>\n' +
+    '<p id="salutation" style="{font-size: 12pt}">hi.</p>\n' +
+    '<p id="body" style="{font-size: 12pt}">I am a test-case. This text exists ' +
+    'solely to provide some things to test the inspector initialization.</p>\n' +
+    'If you are reading this, you should go do something else instead. Maybe ' +
+    'read a book. Or better yet, write some test-cases for another bit of code. ' +
+    '<span style="{font-style: italic}">Maybe more inspector test-cases!</span></p>\n' +
+    '<p id="closing">end transmission</p>\n' +
+    '</div>';
+  doc.title = "Inspector Initialization Test";
+  startInspectorTests();
+}
 
 function startInspectorTests()
 {
   ok(InspectorUI, "InspectorUI variable exists");
   Services.obs.addObserver(runInspectorTests,
     INSPECTOR_NOTIFICATIONS.OPENED, false);
   InspectorUI.toggleInspectorUI();
 }
 
 function runInspectorTests()
 {
   Services.obs.removeObserver(runInspectorTests,
     INSPECTOR_NOTIFICATIONS.OPENED, false);
-  Services.obs.addObserver(finishInspectorTests,
+  Services.obs.addObserver(runContextMenuTest,
     INSPECTOR_NOTIFICATIONS.CLOSED, false);
 
   ok(!InspectorUI.toolbar.hidden, "toolbar is visible");
   let iframe = document.getElementById("inspector-tree-iframe");
   is(InspectorUI.treeIFrame, iframe, "Inspector IFrame matches");
   ok(InspectorUI.inspecting, "Inspector is inspecting");
   ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
   ok(InspectorUI.highlighter, "Highlighter is up");
 
   executeSoon(function() {
     InspectorUI.closeInspectorUI();
   });
 }
 
+function runContextMenuTest()
+{
+  Services.obs.removeObserver(runContextMenuTest, INSPECTOR_NOTIFICATIONS.CLOSED, false);
+  Services.obs.addObserver(inspectNodesFromContextTest, INSPECTOR_NOTIFICATIONS.OPENED, false);
+  salutation = doc.getElementById("salutation");
+  ok(salutation, "hello, context menu test!");
+  let eventDeets = { type : "contextmenu", button : 2 };
+  let contextMenu = document.getElementById("contentAreaContextMenu");
+  ok(contextMenu, "we have the context menu");
+  let contextInspectMenuItem = document.getElementById("context-inspect");
+  ok(contextInspectMenuItem, "we have the inspect context menu item");
+  EventUtils.synthesizeMouse(salutation, 2, 2, eventDeets);
+  is(contextMenu.state, "showing", "context menu is open");
+  is(contextInspectMenuItem.hidden, !InspectorUI.enabled, "is context menu item enabled?");
+  contextMenu.hidePopup();
+  executeSoon(function() {
+    InspectorUI.openInspectorUI(salutation);
+  });
+}
+
+function inspectNodesFromContextTest()
+{
+  Services.obs.removeObserver(inspectNodesFromContextTest, INSPECTOR_NOTIFICATIONS.OPENED, false);
+  Services.obs.addObserver(finishInspectorTests, INSPECTOR_NOTIFICATIONS.CLOSED, false);
+  ok(!InspectorUI.inspecting, "Inspector is not actively highlighting");
+  is(InspectorUI.selection, salutation, "Inspector is highlighting salutation");
+  ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
+  // TODO: These tests depend on the style inspector patches.
+  todo(InspectorUI.isStylePanelOpen, "Inspector Style Panel is open");
+  todo(InspectorUI.isDOMPanelOpen, "Inspector DOM Panel is open");
+  InspectorUI.closeInspectorUI(true);
+}
+
 function finishInspectorTests()
 {
   Services.obs.removeObserver(finishInspectorTests,
     INSPECTOR_NOTIFICATIONS.CLOSED, false);
 
   ok(!InspectorUI.highlighter, "Highlighter is gone");
   ok(!InspectorUI.isTreePanelOpen, "Inspector Tree Panel is closed");
   ok(!InspectorUI.inspecting, "Inspector is not inspecting");
@@ -79,14 +131,15 @@ function finishInspectorTests()
 }
 
 function test()
 {
   waitForExplicitFinish();
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function() {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
-    waitForFocus(startInspectorTests, content);
+    doc = content.document;
+    waitForFocus(createDocument, content);
   }, true);
 
   content.location = "data:text/html,basic tests for inspector";
 }
 
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -189,16 +189,19 @@ can reach it easily. -->
 <!ENTITY webConsoleCmd.label          "Web Console">
 <!ENTITY webConsoleCmd.accesskey      "W">
 <!ENTITY webConsoleCmd.commandkey     "k">
 
 <!ENTITY inspectMenu.label            "Inspect">
 <!ENTITY inspectMenu.accesskey        "T">
 <!ENTITY inspectMenu.commandkey       "I">
 
+<!ENTITY inspectContextMenu.label     "Inspect Element">
+<!ENTITY inspectContextMenu.accesskey "N">
+
 <!-- LOCALIZATION NOTE (scratchpad.label): This menu item label appears
   -  in the Tools menu. See bug 653093.
   -  The Scratchpad is intended to provide a simple text editor for creating
   -  and evaluating bits of JavaScript code for the purposes of function
   -  prototyping, experimentation and convenient scripting.
   -
   -  It's quite possible that you won't have a good analogue for the word
   -  "Scratchpad" in your locale. You should feel free to find a close