Bug 1201475 - Refresh DOM panel only if selected; r=linclark
authorJan Odvarko <odvarko@gmail.com>
Mon, 18 Apr 2016 17:12:10 +0200
changeset 332347 bfda97a555e0d003b1b6040614709d979db72c03
parent 332346 60df1f524d7f3303c20a289f25d546dc18bbac8e
child 332348 4832ac37867f93eb09ec5f057b82c7195de54456
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslinclark
bugs1201475
milestone48.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 1201475 - Refresh DOM panel only if selected; r=linclark MozReview-Commit-ID: 3sr85QHQdjS
devtools/client/dom/content/dom-decorator.js
devtools/client/dom/content/grip-provider.js
devtools/client/dom/dom-panel.js
devtools/client/dom/test/head.js
devtools/client/dom/test/page_basic.html
--- a/devtools/client/dom/content/dom-decorator.js
+++ b/devtools/client/dom/content/dom-decorator.js
@@ -29,16 +29,18 @@ DomDecorator.prototype = {
         names.push("writable");
       }
       if (value.configurable) {
         names.push("configurable");
       }
 
       return names;
     }
+
+    return null;
   },
 
   /**
    * Return custom React template for specified object. The template
    * might depend on specified column.
    */
   getValueRep: function(value, colId) {
   }
--- a/devtools/client/dom/content/grip-provider.js
+++ b/devtools/client/dom/content/grip-provider.js
@@ -61,38 +61,36 @@ GripProvider.prototype = {
         let k = preview.kind;
         let objectsWithProps = ["DOMNode", "ObjectWithURL"];
         hasChildren = hasChildren || (objectsWithProps.indexOf(k) != -1);
         hasChildren = hasChildren || (k == "ArrayLike" && preview.length > 0);
       }
 
       return (value.type == "object" && hasChildren);
     }
+
+    return null;
   },
 
   getValue: function(object) {
     if (object instanceof Property) {
       let value = object.value;
       return (typeof value.value != "undefined") ? value.value :
         value.getterValue;
     }
 
     return object;
   },
 
   getLabel: function(object) {
-    if (object instanceof Property) {
-      return object.name;
-    }
+    return (object instanceof Property) ? object.name : null;
   },
 
   getKey: function(object) {
-    if (object instanceof Property) {
-      return object.key;
-    }
+    return (object instanceof Property) ? object.key : null;
   },
 
   getType: function(object) {
     return object.class ? object.class : "";
   },
 };
 
 // Exports from this module
--- a/devtools/client/dom/dom-panel.js
+++ b/devtools/client/dom/dom-panel.js
@@ -17,96 +17,132 @@ const EventEmitter = require("devtools/s
  * render Document Object Model of the current debugger target.
  */
 function DomPanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
   this._toolbox = toolbox;
 
   this.onTabNavigated = this.onTabNavigated.bind(this);
   this.onContentMessage = this.onContentMessage.bind(this);
+  this.onPanelVisibilityChange = this.onPanelVisibilityChange.bind(this);
 
   this.pendingRequests = new Map();
 
   EventEmitter.decorate(this);
 }
 
 DomPanel.prototype = {
   /**
    * Open is effectively an asynchronous constructor.
    *
    * @return object
    *         A promise that is resolved when the DOM panel completes opening.
    */
-  open: Task.async(function*() {
+  open: Task.async(function* () {
     if (this._opening) {
       return this._opening;
     }
 
     let deferred = promise.defer();
     this._opening = deferred.promise;
 
     // Local monitoring needs to make the target remote.
     if (!this.target.isRemote) {
       yield this.target.makeRemote();
     }
 
     this.initialize();
 
-    this.once("no-pending-requests", () => {
-      this.isReady = true;
-      this.emit("ready");
-      deferred.resolve(this);
-    });
+    this.isReady = true;
+    this.emit("ready");
+    deferred.resolve(this);
 
     return this._opening;
   }),
 
   // Initialization
 
   initialize: function() {
     this.panelWin.addEventListener("devtools/content/message",
       this.onContentMessage, true);
 
     this.target.on("navigate", this.onTabNavigated);
+    this._toolbox.on("select", this.onPanelVisibilityChange);
 
     let provider = {
       getPrototypeAndProperties: this.getPrototypeAndProperties.bind(this)
     };
 
     exportIntoContentScope(this.panelWin, provider, "DomProvider");
 
-    this.doRefresh();
+    this.shouldRefresh = true;
   },
 
-  destroy: Task.async(function*() {
+  destroy: Task.async(function* () {
     if (this._destroying) {
       return this._destroying;
     }
 
     let deferred = promise.defer();
     this._destroying = deferred.promise;
 
     this.target.off("navigate", this.onTabNavigated);
+    this._toolbox.off("select", this.onPanelVisibilityChange);
 
     this.emit("destroyed");
 
     deferred.resolve();
     return this._destroying;
   }),
 
   // Events
 
-  doRefresh: function() {
-    this.refresh().then(rootGrip => {
+  refresh: function() {
+    // Do not refresh if the panel isn't visible.
+    if (!this.isPanelVisible()) {
+      return;
+    }
+
+    // Do not refresh if it isn't necessary.
+    if (!this.shouldRefresh) {
+      return;
+    }
+
+    // Alright reset the flag we are about to refresh the panel.
+    this.shouldRefresh = false;
+
+    this.getRootGrip().then(rootGrip => {
       this.postContentMessage("initialize", rootGrip);
     });
   },
 
+  /**
+   * Make sure the panel is refreshed when the page is reloaded.
+   * The panel is refreshed immediatelly if it's currently selected
+   * or lazily  when the user actually selects it.
+   */
   onTabNavigated: function() {
-    this.doRefresh();
+    this.shouldRefresh = true;
+    this.refresh();
+  },
+
+  /**
+   * Make sure the panel is refreshed (if needed) when it's selected.
+   */
+  onPanelVisibilityChange: function() {
+    this.refresh();
+  },
+
+  // Helpers
+
+  /**
+   * Return true if the DOM panel is currently selected.
+   */
+  isPanelVisible: function() {
+    return this._toolbox.currentToolId === "dom";
   },
 
   getPrototypeAndProperties: function(grip) {
     let deferred = defer();
 
     if (!grip.actor) {
       console.error("No actor!", grip);
       deferred.reject(new Error("Failed to get actor from grip."));
@@ -136,32 +172,28 @@ DomPanel.prototype = {
       }
     });
 
     this.pendingRequests.set(grip.actor, deferred.promise);
 
     return deferred.promise;
   },
 
-  // Refresh
-
-  refresh: function() {
+  getRootGrip: function() {
     let deferred = defer();
 
     // Attach Console. It might involve RDP communication, so wait
     // asynchronously for the result
     this.target.activeConsole.evaluateJSAsync("window", res => {
       deferred.resolve(res.result);
     });
 
     return deferred.promise;
   },
 
-  // Helpers
-
   postContentMessage: function(type, args) {
     let data = {
       type: type,
       args: args,
     };
 
     let event = new this.panelWin.MessageEvent("devtools/chrome/message", {
       bubbles: true,
--- a/devtools/client/dom/test/head.js
+++ b/devtools/client/dom/test/head.js
@@ -36,20 +36,22 @@ function addTestTab(url) {
 
   return new Promise(resolve => {
     addTab(url).then(tab => {
       // Load devtools/shared/frame-script-utils.js
       getFrameScript();
 
       // Select the DOM panel and wait till it's initialized.
       initDOMPanel(tab).then(panel => {
-        resolve({
-          tab: tab,
-          browser: tab.linkedBrowser,
-          panel: panel
+        waitForDispatch(panel, "FETCH_PROPERTIES").then(() => {
+          resolve({
+            tab: tab,
+            browser: tab.linkedBrowser,
+            panel: panel
+          });
         });
       });
     });
   });
 }
 
 /**
  * Open the DOM panel for the given tab.
@@ -135,30 +137,31 @@ function _afterDispatchDone(store, type)
       // in
       type: "@@service/waitUntil",
       predicate: action => {
         if (action.type === type) {
           return action.status ?
             (action.status === "end" || action.status === "error") :
             true;
         }
+        return false;
       },
       run: (dispatch, getState, action) => {
         resolve(action);
       }
     });
   });
 }
 
 function waitForDispatch(panel, type, eventRepeat = 1) {
   const store = panel.panelWin.view.mainFrame.store;
   const actionType = constants[type];
   let count = 0;
 
-  return Task.spawn(function*() {
+  return Task.spawn(function* () {
     info("Waiting for " + type + " to dispatch " + eventRepeat + " time(s)");
     while (count < eventRepeat) {
       yield _afterDispatchDone(store, actionType);
       count++;
       info(type + " dispatched " + count + " time(s)");
     }
   });
 }
--- a/devtools/client/dom/test/page_basic.html
+++ b/devtools/client/dom/test/page_basic.html
@@ -3,12 +3,13 @@
 <!doctype html>
 <html>
   <head>
     <meta charset="utf-8"/>
     <title>DOM test page</title>
   </head>
   <body>
   <script type="text/javascript">
-    window._a = {_data: 'test'};
+    "use strict";
+    window._a = {_data: "test"};
   </script>
   </body>
 </html>