Bug 1333714 - update grid highlighter and layout panel on markupmutation;r=gl draft
authorJulian Descottes <jdescottes@mozilla.com>
Tue, 07 Mar 2017 18:02:41 +0100
changeset 496894 77eae80dd8f610a5cb303d367d114be0cfa6bc4b
parent 496893 6dfbbdf78f7b59ab8264397f837fa61601bf72ee
child 548743 225d4596322b1aa7118c5b8799e70e59e9ee2069
push id48736
push userjdescottes@mozilla.com
push dateFri, 10 Mar 2017 21:28:21 +0000
reviewersgl
bugs1333714
milestone55.0a1
Bug 1333714 - update grid highlighter and layout panel on markupmutation;r=gl MozReview-Commit-ID: 9TAUxqTiT4M
devtools/client/inspector/grids/grid-inspector.js
devtools/client/inspector/inspector.js
devtools/client/inspector/rules/test/browser.ini
devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-mutation.js
devtools/client/inspector/shared/highlighters-overlay.js
--- a/devtools/client/inspector/grids/grid-inspector.js
+++ b/devtools/client/inspector/grids/grid-inspector.js
@@ -42,16 +42,17 @@ function GridInspector(inspector, window
   this.walker = this.inspector.walker;
 
   this.getSwatchColorPickerTooltip = this.getSwatchColorPickerTooltip.bind(this);
   this.setSelectedNode = this.setSelectedNode.bind(this);
   this.updateGridPanel = this.updateGridPanel.bind(this);
 
   this.onGridLayoutChange = this.onGridLayoutChange.bind(this);
   this.onHighlighterChange = this.onHighlighterChange.bind(this);
+  this.onMarkupMutation = this.onMarkupMutation.bind(this);
   this.onSetGridOverlayColor = this.onSetGridOverlayColor.bind(this);
   this.onShowBoxModelHighlighterForNode =
     this.onShowBoxModelHighlighterForNode.bind(this);
   this.onShowGridAreaHighlight = this.onShowGridAreaHighlight.bind(this);
   this.onShowGridCellHighlight = this.onShowGridCellHighlight.bind(this);
   this.onSidebarSelect = this.onSidebarSelect.bind(this);
   this.onToggleGridHighlighter = this.onToggleGridHighlighter.bind(this);
   this.onToggleShowGridLineNumbers = this.onToggleShowGridLineNumbers.bind(this);
@@ -81,28 +82,30 @@ GridInspector.prototype = {
       this.inspector,
       {
         supportsCssColor4ColorFunction: () => false
       }
     );
 
     this.highlighters.on("grid-highlighter-hidden", this.onHighlighterChange);
     this.highlighters.on("grid-highlighter-shown", this.onHighlighterChange);
+    this.inspector.on("markupmutation", this.onMarkupMutation);
     this.inspector.sidebar.on("select", this.onSidebarSelect);
 
     this.onSidebarSelect();
   }),
 
   /**
    * Destruction function called when the inspector is destroyed. Removes event listeners
    * and cleans up references.
    */
   destroy() {
     this.highlighters.off("grid-highlighter-hidden", this.onHighlighterChange);
     this.highlighters.off("grid-highlighter-shown", this.onHighlighterChange);
+    this.inspector.off("markupmutation", this.onMarkupMutation);
     this.inspector.sidebar.off("select", this.onSidebarSelect);
     this.layoutInspector.off("grid-layout-changed", this.onGridLayoutChange);
 
     this.swatchColorPickerTooltip.destroy();
 
     this.document = null;
     this.highlighters = null;
     this.inspector = null;
@@ -294,16 +297,24 @@ GridInspector.prototype = {
   onHighlighterChange(event, nodeFront, options) {
     let highlighted = event === "grid-highlighter-shown";
     let { color } = options;
     this.store.dispatch(updateGridHighlighted(nodeFront, highlighted));
     this.store.dispatch(updateGridColor(nodeFront, color));
   },
 
   /**
+   * Handler for the "markupmutation" event fired by the inspector. On markup mutations,
+   * update the grid panel content.
+   */
+  onMarkupMutation() {
+    this.updateGridPanel();
+  },
+
+  /**
    * Handler for a change in the grid overlay color picker for a grid container.
    *
    * @param  {NodeFront} node
    *         The NodeFront of the grid container element for which the grid color is
    *         being updated.
    * @param  {String} color
    *         A hex string representing the color to use.
    */
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -81,16 +81,18 @@ const PORTRAIT_MODE_WIDTH = 700;
  *      to source-mapped files)
  * - rule-view-refreshed
  *      Fired when the rule view updates to a new node
  * - rule-view-sourcelinks-updated
  *      Fired when the stylesheet source links have been updated (when switching
  *      to source-mapped files)
  */
 function Inspector(toolbox) {
+  EventEmitter.decorate(this);
+
   this._toolbox = toolbox;
   this._target = toolbox.target;
   this.panelDoc = window.document;
   this.panelWin = window;
   this.panelWin.inspector = this;
 
   this.highlighters = new HighlightersOverlay(this);
   this.store = Store();
@@ -109,18 +111,16 @@ function Inspector(toolbox) {
   this.onPaneToggleButtonClicked = this.onPaneToggleButtonClicked.bind(this);
   this._onMarkupFrameLoad = this._onMarkupFrameLoad.bind(this);
   this.onPanelWindowResize = this.onPanelWindowResize.bind(this);
   this.onSidebarShown = this.onSidebarShown.bind(this);
   this.onSidebarHidden = this.onSidebarHidden.bind(this);
 
   this._target.on("will-navigate", this._onBeforeNavigate);
   this._detectingActorFeatures = this._detectActorFeatures();
-
-  EventEmitter.decorate(this);
 }
 
 Inspector.prototype = {
   /**
    * open is effectively an asynchronous constructor
    */
   init: Task.async(function* () {
     // Localize all the nodes containing a data-localization attribute.
--- a/devtools/client/inspector/rules/test/browser.ini
+++ b/devtools/client/inspector/rules/test/browser.ini
@@ -145,16 +145,17 @@ skip-if = os == "mac" # Bug 1245996 : cl
 [browser_rules_edit-value-after-name_04.js]
 [browser_rules_editable-field-focus_01.js]
 [browser_rules_editable-field-focus_02.js]
 [browser_rules_eyedropper.js]
 [browser_rules_filtereditor-appears-on-swatch-click.js]
 [browser_rules_filtereditor-commit-on-ENTER.js]
 [browser_rules_filtereditor-revert-on-ESC.js]
 skip-if = (os == "win" && debug) # bug 963492: win.
+[browser_rules_grid-highlighter-on-mutation.js]
 [browser_rules_grid-highlighter-on-navigate.js]
 [browser_rules_grid-highlighter-on-reload.js]
 [browser_rules_grid-highlighter-restored-after-reload.js]
 [browser_rules_grid-toggle_01.js]
 [browser_rules_grid-toggle_01b.js]
 [browser_rules_grid-toggle_02.js]
 [browser_rules_grid-toggle_03.js]
 [browser_rules_guessIndentation.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/browser_rules_grid-highlighter-on-mutation.js
@@ -0,0 +1,44 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the grid highlighter is hidden when the highlighted grid container is
+// removed from the page.
+
+const TEST_URI = `
+  <style type='text/css'>
+    #grid {
+      display: grid;
+    }
+  </style>
+  <div id="grid">
+    <div id="cell1">cell1</div>
+    <div id="cell2">cell2</div>
+  </div>
+`;
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view, testActor} = yield openRuleView();
+  let highlighters = view.highlighters;
+
+  yield selectNode("#grid", inspector);
+  let container = getRuleViewProperty(view, "#grid", "display").valueSpan;
+  let gridToggle = container.querySelector(".ruleview-grid");
+
+  info("Toggling ON the CSS grid highlighter from the rule-view.");
+  let onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  gridToggle.click();
+  yield onHighlighterShown;
+  ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown.");
+
+  let onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
+  info("Remove the #grid container in the content page");
+  testActor.eval(`
+    content.document.querySelector("#grid").remove();
+  `);
+  yield onHighlighterHidden;
+  ok(!highlighters.gridHighlighterShown, "CSS grid highlighter is hidden.");
+});
--- a/devtools/client/inspector/shared/highlighters-overlay.js
+++ b/devtools/client/inspector/shared/highlighters-overlay.js
@@ -35,23 +35,25 @@ function HighlightersOverlay(inspector) 
   this.selectorHighlighterShown = null;
   // Saved state to be restore on page navigation.
   this.state = {
     // Only the grid highlighter state is saved at the moment.
     grid: {}
   };
 
   this.onClick = this.onClick.bind(this);
+  this.onMarkupMutation = this.onMarkupMutation.bind(this);
   this.onMouseMove = this.onMouseMove.bind(this);
   this.onMouseOut = this.onMouseOut.bind(this);
   this.onWillNavigate = this.onWillNavigate.bind(this);
   this.onNavigate = this.onNavigate.bind(this);
   this._handleRejection = this._handleRejection.bind(this);
 
-  // Add target events, not specific to a given view.
+  // Add inspector events, not specific to a given view.
+  this.inspector.on("markupmutation", this.onMarkupMutation);
   this.inspector.target.on("navigate", this.onNavigate);
   this.inspector.target.on("will-navigate", this.onWillNavigate);
 
   EventEmitter.decorate(this);
 }
 
 HighlightersOverlay.prototype = {
   get isRuleView() {
@@ -371,16 +373,40 @@ HighlightersOverlay.prototype = {
     }
 
     // Otherwise, hide the highlighter.
     this._lastHovered = null;
     this._hideHoveredHighlighter();
   },
 
   /**
+   * Handler function for "markupmutation" events. Hides the grid highlighter if the grid
+   * container is no longer in the DOM tree.
+   */
+  onMarkupMutation: Task.async(function* (evt, mutations) {
+    let hasInterestingMutation = mutations.some(mut => mut.type === "childList");
+    if (!hasInterestingMutation || !this.gridHighlighterShown) {
+      // Bail out if the mutations did not remove nodes, or if no grid highlighter is
+      // displayed.
+      return;
+    }
+
+    let nodeFront = this.gridHighlighterShown;
+
+    try {
+      let isInTree = yield this.inspector.walker.isInDOMTree(nodeFront);
+      if (!isInTree) {
+        this.hideGridHighlighter(nodeFront);
+      }
+    } catch (e) {
+      console.error(e);
+    }
+  }),
+
+  /**
    * Restore saved highlighter state after navigate.
    */
   onNavigate: Task.async(function* () {
     try {
       yield this.restoreState();
     } catch (e) {
       this._handleRejection(e);
     }
@@ -405,17 +431,18 @@ HighlightersOverlay.prototype = {
   destroy: function () {
     for (let type in this.highlighters) {
       if (this.highlighters[type]) {
         this.highlighters[type].finalize();
         this.highlighters[type] = null;
       }
     }
 
-    // Remove target events.
+    // Remove inspector events.
+    this.inspector.off("markupmutation", this.onMarkupMutation);
     this.inspector.target.off("navigate", this.onNavigate);
     this.inspector.target.off("will-navigate", this.onWillNavigate);
 
     this._lastHovered = null;
 
     this.inspector = null;
     this.highlighters = null;
     this.highlighterUtils = null;