Bug 1348005 - update grid panel on reflows;r=gl
Extracted the reflow tracking logic from the box-model to a dedicated util in
inspector/shared/reflow-tracker.js to use it in both box-model and grid-inspector.
MozReview-Commit-ID: DZCOH3RDY6
--- a/devtools/client/inspector/boxmodel/box-model.js
+++ b/devtools/client/inspector/boxmodel/box-model.js
@@ -1,17 +1,16 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { Task } = require("devtools/shared/task");
const { getCssProperties } = require("devtools/shared/fronts/css-properties");
-const { ReflowFront } = require("devtools/shared/fronts/reflow");
const { InplaceEditor } = require("devtools/client/shared/inplace-editor");
const {
updateGeometryEditorEnabled,
updateLayout,
updateOffsetParent,
} = require("./actions/box-model");
@@ -55,21 +54,17 @@ BoxModel.prototype = {
/**
* Destruction function called when the inspector is destroyed. Removes event listeners
* and cleans up references.
*/
destroy() {
this.inspector.selection.off("new-node-front", this.onNewSelection);
this.inspector.sidebar.off("select", this.onSidebarSelect);
- if (this.reflowFront) {
- this.untrackReflows();
- this.reflowFront.destroy();
- this.reflowFront = null;
- }
+ this.untrackReflows();
this.document = null;
this.highlighters = null;
this.inspector = null;
this.walker = null;
},
/**
@@ -104,40 +99,24 @@ BoxModel.prototype = {
this.inspector.selection.isConnected() &&
this.inspector.selection.isElementNode();
},
/**
* Starts listening to reflows in the current tab.
*/
trackReflows() {
- if (!this.reflowFront) {
- let { target } = this.inspector;
- if (target.form.reflowActor) {
- this.reflowFront = ReflowFront(target.client,
- target.form);
- } else {
- return;
- }
- }
-
- this.reflowFront.on("reflows", this.updateBoxModel);
- this.reflowFront.start();
+ this.inspector.reflowTracker.trackReflows(this, this.updateBoxModel);
},
/**
* Stops listening to reflows in the current tab.
*/
untrackReflows() {
- if (!this.reflowFront) {
- return;
- }
-
- this.reflowFront.off("reflows", this.updateBoxModel);
- this.reflowFront.stop();
+ this.inspector.reflowTracker.untrackReflows(this, this.updateBoxModel);
},
/**
* Updates the box model panel by dispatching the new layout data.
*
* @param {String} reason
* Optional string describing the reason why the boxmodel is updated.
*/
--- 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.updateGridPanel = this.updateGridPanel.bind(this);
this.onGridLayoutChange = this.onGridLayoutChange.bind(this);
this.onHighlighterChange = this.onHighlighterChange.bind(this);
this.onMarkupMutation = this.onMarkupMutation.bind(this);
+ this.onReflow = this.onReflow.bind(this);
this.onSetGridOverlayColor = this.onSetGridOverlayColor.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);
this.onToggleShowInfiniteLines = this.onToggleShowInfiniteLines.bind(this);
@@ -96,16 +97,18 @@ GridInspector.prototype = {
*/
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.inspector.reflowTracker.untrackReflows(this, this.onReflow);
+
this.swatchColorPickerTooltip.destroy();
this.document = null;
this.highlighters = null;
this.inspector = null;
this.layoutInspector = null;
this.store = null;
this.swatchColorPickerTooltip = null;
@@ -290,16 +293,24 @@ GridInspector.prototype = {
* Handler for the "markupmutation" event fired by the inspector. On markup mutations,
* update the grid panel content.
*/
onMarkupMutation() {
this.updateGridPanel();
},
/**
+ * Handler for the "reflow" event fired by the inspector's reflow tracker. On reflows,
+ * update the grid panel content.
+ */
+ onReflow() {
+ 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.
*/
@@ -371,19 +382,21 @@ GridInspector.prototype = {
* Handler for the inspector sidebar select event. Starts listening for
* "grid-layout-changed" if the layout panel is visible. Otherwise, stop
* listening for grid layout changes. Finally, refresh the layout view if
* it is visible.
*/
onSidebarSelect() {
if (!this.isPanelVisible()) {
this.layoutInspector.off("grid-layout-changed", this.onGridLayoutChange);
+ this.inspector.reflowTracker.untrackReflows(this, this.onReflow);
return;
}
+ this.inspector.reflowTracker.trackReflows(this, this.onReflow);
this.layoutInspector.on("grid-layout-changed", this.onGridLayoutChange);
this.updateGridPanel();
},
/**
* Handler for a change in the input checkboxes in the GridList component.
* Toggles on/off the grid highlighter for the provided grid container element.
*
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -24,16 +24,17 @@ const MenuItem = require("devtools/clien
const {HTMLBreadcrumbs} = require("devtools/client/inspector/breadcrumbs");
const BoxModel = require("devtools/client/inspector/boxmodel/box-model");
const {FontInspector} = require("devtools/client/inspector/fonts/fonts");
const GridInspector = require("devtools/client/inspector/grids/grid-inspector");
const {InspectorSearch} = require("devtools/client/inspector/inspector-search");
const {RuleViewTool} = require("devtools/client/inspector/rules/rules");
const HighlightersOverlay = require("devtools/client/inspector/shared/highlighters-overlay");
+const ReflowTracker = require("devtools/client/inspector/shared/reflow-tracker");
const {ToolSidebar} = require("devtools/client/inspector/toolsidebar");
const MarkupView = require("devtools/client/inspector/markup/markup");
const {CommandUtils} = require("devtools/client/shared/developer-toolbar");
const {ViewHelpers} = require("devtools/client/shared/widgets/view-helpers");
const clipboardHelper = require("devtools/shared/platform/clipboard");
const Store = require("devtools/client/inspector/store");
@@ -90,16 +91,17 @@ function Inspector(toolbox) {
this._toolbox = toolbox;
this._target = toolbox.target;
this.panelDoc = window.document;
this.panelWin = window;
this.panelWin.inspector = this;
this.highlighters = new HighlightersOverlay(this);
+ this.reflowTracker = new ReflowTracker(this._target);
this.store = Store();
this.telemetry = new Telemetry();
this.nodeMenuTriggerInfo = null;
this._handleRejectionIfNotDestroyed = this._handleRejectionIfNotDestroyed.bind(this);
this._onBeforeNavigate = this._onBeforeNavigate.bind(this);
this.onNewRoot = this.onNewRoot.bind(this);
@@ -962,16 +964,17 @@ Inspector.prototype = {
this.teardownToolbar();
this.breadcrumbs.destroy();
this.selection.off("new-node-front", this.onNewSelection);
this.selection.off("detached-front", this.onDetached);
let markupDestroyer = this._destroyMarkup();
this.highlighters.destroy();
+ this.reflowTracker.destroy();
this.search.destroy();
this._toolbox = null;
this.breadcrumbs = null;
this.panelDoc = null;
this.panelWin.inspector = null;
this.panelWin = null;
this.sidebar = null;
--- a/devtools/client/inspector/shared/moz.build
+++ b/devtools/client/inspector/shared/moz.build
@@ -3,14 +3,15 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'dom-node-preview.js',
'highlighters-overlay.js',
'node-types.js',
+ 'reflow-tracker.js',
'style-inspector-menu.js',
'tooltips-overlay.js',
'utils.js'
)
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/shared/reflow-tracker.js
@@ -0,0 +1,113 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { ReflowFront } = require("devtools/shared/fronts/reflow");
+
+/**
+ * Simple utility class that listens to reflows on a given target if and only if a
+ * listener is actively listening to reflows.
+ *
+ * @param {Object} target
+ * The current target (as in toolbox target)
+ */
+function ReflowTracker(target) {
+ this.target = target;
+
+ // Hold a Map of all the listeners interested in reflows.
+ this.listeners = new Map();
+
+ this.reflowFront = null;
+
+ this.onReflow = this.onReflow.bind(this);
+}
+
+ReflowTracker.prototype = {
+
+ destroy() {
+ if (this.reflowFront) {
+ this.stopTracking();
+ this.reflowFront.destroy();
+ this.reflowFront = null;
+ }
+
+ this.listeners.clear();
+ },
+
+ startTracking() {
+ // Initialize reflow front if necessary.
+ if (!this.reflowFront && this.target.form.reflowActor) {
+ let { client, form } = this.target;
+ this.reflowFront = ReflowFront(client, form);
+ }
+
+ if (this.reflowFront) {
+ this.reflowFront.on("reflows", this.onReflow);
+ this.reflowFront.start();
+ }
+ },
+
+ stopTracking() {
+ if (this.reflowFront) {
+ this.reflowFront.off("reflows", this.onReflow);
+ this.reflowFront.stop();
+ }
+ },
+
+ /**
+ * Add a listener for reflows.
+ *
+ * @param {Object} listener
+ * Object/instance listening to reflows.
+ * @param {Function} callback
+ * The associated callback.
+ */
+ trackReflows(listener, callback) {
+ if (this.listeners.get(listener) === callback) {
+ return;
+ }
+
+ // No listener interested in reflows yet, start tracking.
+ if (this.listeners.size === 0) {
+ this.startTracking();
+ }
+
+ this.listeners.set(listener, callback);
+ },
+
+ /**
+ * Remove a listener for reflows.
+ *
+ * @param {Object} listener
+ * Object/instance listening to reflows.
+ * @param {Function} callback
+ * The associated callback.
+ */
+ untrackReflows(listener, callback) {
+ if (this.listeners.get(listener) !== callback) {
+ return;
+ }
+
+ this.listeners.delete(listener);
+
+ // No listener interested in reflows anymore, stop tracking.
+ if (this.listeners.size === 0) {
+ this.stopTracking();
+ }
+ },
+
+ /**
+ * Handler called when a reflow happened.
+ */
+ onReflow() {
+ for (let [, callback] of this.listeners) {
+ callback();
+ }
+ },
+};
+
+module.exports = ReflowTracker;