Bug 827604 - Pseudoclass lock isn't clearing; r=dcamp
authorHeather Arthur <fayearthur@gmail.com>
Fri, 18 Jan 2013 14:03:22 -0800
changeset 119387 a7234e6f89f2fe0c28608c209916db45533e92fe
parent 119127 b52c02f77cf5b76027fea25412e629cd3d551cb3
child 119388 0b629aad54cc98f4e7a56742bd6858b14dcdad1f
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersdcamp
bugs827604
milestone21.0a1
Bug 827604 - Pseudoclass lock isn't clearing; r=dcamp
browser/devtools/inspector/InspectorPanel.jsm
browser/devtools/inspector/Selection.jsm
browser/devtools/inspector/test/browser_inspector_pseudoclass_lock.js
--- a/browser/devtools/inspector/InspectorPanel.jsm
+++ b/browser/devtools/inspector/InspectorPanel.jsm
@@ -60,16 +60,18 @@ InspectorPanel.prototype = {
     this._resetNodeMenu = this._resetNodeMenu.bind(this);
     this.nodemenu.addEventListener("popupshowing", this._setupNodeMenu, true);
     this.nodemenu.addEventListener("popuphiding", this._resetNodeMenu, true);
 
     // Create an empty selection
     this._selection = new Selection();
     this.onNewSelection = this.onNewSelection.bind(this);
     this.selection.on("new-node", this.onNewSelection);
+    this.onBeforeNewSelection = this.onBeforeNewSelection.bind(this);
+    this.selection.on("before-new-node", this.onBeforeNewSelection);
     this.onDetached = this.onDetached.bind(this);
     this.selection.on("detached", this.onDetached);
 
     this.breadcrumbs = new HTMLBreadcrumbs(this);
 
     if (this.target.isLocalTab) {
       this.browser = this.target.tab.linkedBrowser;
       this.scheduleLayoutChange = this.scheduleLayoutChange.bind(this);
@@ -315,16 +317,27 @@ InspectorPanel.prototype = {
   /**
    * When a new node is selected.
    */
   onNewSelection: function InspectorPanel_onNewSelection() {
     this.cancelLayoutChange();
   },
 
   /**
+   * When a new node is selected, before the selection has changed.
+   */
+  onBeforeNewSelection: function InspectorPanel_onBeforeNewSelection(event,
+                                                                     node) {
+    if (this.breadcrumbs.indexOf(node) == -1) {
+      // only clear locks if we'd have to update breadcrumbs
+      this.clearPseudoClasses();
+    }
+  },
+
+  /**
    * When a node is deleted, select its parent node.
    */
   onDetached: function InspectorPanel_onDetached(event, parentNode) {
     this.cancelLayoutChange();
     this.breadcrumbs.cutAfter(this.breadcrumbs.indexOf(parentNode));
     this.selection.setNode(parentNode, "detached");
   },
 
@@ -362,16 +375,17 @@ InspectorPanel.prototype = {
     this.sidebar.off("select", this._setDefaultSidebar);
     this.sidebar.destroy();
     this.sidebar = null;
 
     this.nodemenu.removeEventListener("popupshowing", this._setupNodeMenu, true);
     this.nodemenu.removeEventListener("popuphiding", this._resetNodeMenu, true);
     this.breadcrumbs.destroy();
     this.selection.off("new-node", this.onNewSelection);
+    this.selection.off("before-new-node", this.onBeforeNewSelection);
     this.selection.off("detached", this.onDetached);
     this._destroyMarkup();
     this._selection.destroy();
     this._selection = null;
     this.panelWin.inspector = null;
     this.target = null;
     this.panelDoc = null;
     this.panelWin = null;
@@ -496,16 +510,25 @@ InspectorPanel.prototype = {
           node = node.parentNode;
         } while (hierarchical && node.parentNode)
       }
     }
     this.selection.emit("pseudoclass");
   },
 
   /**
+   * Clear any pseudo-class locks applied to the current hierarchy.
+   */
+  clearPseudoClasses: function InspectorPanel_clearPseudoClasses() {
+    this.breadcrumbs.nodeHierarchy.forEach(function(crumb) {
+      DOMUtils.clearPseudoClassLocks(crumb.node);
+    });
+  },
+
+  /**
    * Toggle the highlighter when ruleview is hovered.
    */
   toggleHighlighter: function InspectorPanel_toggleHighlighter(event)
   {
     if (event.type == "mouseover") {
       this.highlighter.hide();
     }
     else if (event.type == "mouseout") {
--- a/browser/devtools/inspector/Selection.jsm
+++ b/browser/devtools/inspector/Selection.jsm
@@ -37,16 +37,17 @@ this.EXPORTED_SYMBOLS = ["Selection"];
  *   isCommentNode()
  *   isDocumentNode()
  *   isDocumentTypeNode()
  *   isDocumentFragmentNode()
  *   isNotationNode()
  *
  * Events:
  *   "new-node" when the inner node changed
+ *   "before-new-node" when the inner node is set to change
  *   "attribute-changed" when an attribute is changed (only if tracked)
  *   "detached" when the node (or one of its parents) is removed from the document (only if tracked)
  *   "reparented" when the node (or one of its parents) is moved under a different node (only if tracked)
  */
 
 /**
  * A Selection object. Hold a reference to a node.
  * Includes some helpers, fire some helpful events.
@@ -121,16 +122,17 @@ Selection.prototype = {
   destroy: function SN_destroy() {
     this._detachEvents();
     this.setNode(null);
   },
 
   setNode: function SN_setNode(value, reason="unknown") {
     this.reason = reason;
     if (value !== this._node) {
+      this.emit("before-new-node", value, reason);
       let previousNode = this._node;
       this._detachEvents();
       this._node = value;
       this._attachEvents();
       this.emit("new-node", previousNode, this.reason);
     }
   },
 
--- a/browser/devtools/inspector/test/browser_inspector_pseudoclass_lock.js
+++ b/browser/devtools/inspector/test/browser_inspector_pseudoclass_lock.js
@@ -3,17 +3,17 @@
 
 let tempScope = {};
 Cu.import("resource:///modules/devtools/Target.jsm", tempScope);
 let TargetFactory = tempScope.TargetFactory;
 
 let DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 
 let doc;
-let div;
+let parentDiv, div, div2;
 let inspector;
 let ruleview;
 
 let pseudo = ":hover";
 
 function test()
 {
   waitForExplicitFinish();
@@ -25,26 +25,34 @@ function test()
     waitForFocus(createDocument, content);
   }, true);
 
   content.location = "data:text/html,pseudo-class lock tests";
 }
 
 function createDocument()
 {
+  parentDiv = doc.createElement("div");
+  parentDiv.textContent = "parent div";
+
   div = doc.createElement("div");
   div.textContent = "test div";
 
+  div2 = doc.createElement("div");
+  div2.textContent = "test div2";
+
   let head = doc.getElementsByTagName('head')[0];
   let style = doc.createElement('style');
   let rules = doc.createTextNode('div { color: red; } div:hover { color: blue; }');
 
   style.appendChild(rules);
   head.appendChild(style);
-  doc.body.appendChild(div);
+  parentDiv.appendChild(div);
+  parentDiv.appendChild(div2);
+  doc.body.appendChild(parentDiv);
 
   openInspector(selectNode);
 }
 
 function selectNode(aInspector)
 {
   inspector = aInspector;
   inspector.selection.setNode(div);
@@ -66,20 +74,41 @@ function performTests()
   inspector.togglePseudoClass(pseudo);
 
   testRemoved();
   testRemovedFromUI();
 
   // toggle it back on
   inspector.togglePseudoClass(pseudo);
 
+  testNavigate();
+
   // close the inspector
   finishUp();
 }
 
+function testNavigate()
+{
+  inspector.selection.setNode(parentDiv);
+
+  // make sure it's still on after naving to parent
+  is(DOMUtils.hasPseudoClassLock(div, pseudo), true,
+       "pseudo-class lock is still applied after inspecting ancestor");
+
+  inspector.selection.setNode(div2);
+
+  // make sure it's removed after naving to a non-hierarchy node
+  is(DOMUtils.hasPseudoClassLock(div, pseudo), false,
+       "pseudo-class lock is removed after inspecting sibling node");
+
+  // toggle it back on
+  inspector.selection.setNode(div);
+  inspector.togglePseudoClass(pseudo);
+}
+
 function testAdded()
 {
   // lock is applied to it and ancestors
   let node = div;
   do {
     is(DOMUtils.hasPseudoClassLock(node, pseudo), true,
        "pseudo-class lock has been applied");
     node = node.parentNode;
@@ -94,34 +123,34 @@ function testAdded()
      "rule view is showing 3 rules for pseudo-class locked div");
 
   is(ruleview.element.children[1]._ruleEditor.rule.selectorText,
      "div:hover", "rule view is showing " + pseudo + " rule");
 }
 
 function testRemoved()
 {
-  // lock removed from node and ancestors  
+  // lock removed from node and ancestors
   let node = div;
   do {
     is(DOMUtils.hasPseudoClassLock(node, pseudo), false,
        "pseudo-class lock has been removed");
     node = node.parentNode;
   } while (node.parentNode)
 }
 
 function testRemovedFromUI()
 {
   // infobar selector doesn't contain pseudo-class
   let pseudoClassesBox = getActiveInspector().highlighter.nodeInfo.pseudoClassesBox;
-  is(pseudoClassesBox.textContent, "", "pseudo-class removed from infobar selector");    
+  is(pseudoClassesBox.textContent, "", "pseudo-class removed from infobar selector");
 
   // ruleview no longer contains pseudo-class rule
   is(ruleview.element.children.length, 2,
-     "rule view is showing 2 rules after removing lock");    
+     "rule view is showing 2 rules after removing lock");
 }
 
 function finishUp()
 {
   gDevTools.once("toolbox-destroyed", function() {
     testRemoved();
     inspector = ruleview = null;
     doc = div = null;