Bug 1277834 - Silence inspector warnings and add promise rejection handlers; r=jdescottes draft
authorPatrick Brosset <pbrosset@mozilla.com>
Thu, 21 Jul 2016 23:29:19 +0200
changeset 390914 9b96832c8b0258c4de96c22929e1683d4ccec0d8
parent 390913 adc8eeced443083e87301492deb574d8f3d39bca
child 390915 afd75f3e560ce77722630005b227649a4b5dc3ba
push id23767
push userpbrosset@mozilla.com
push dateThu, 21 Jul 2016 21:34:46 +0000
reviewersjdescottes
bugs1277834
milestone50.0a1
Bug 1277834 - Silence inspector warnings and add promise rejection handlers; r=jdescottes MozReview-Commit-ID: 6NhwdVTH57R
devtools/client/inspector/inspector-panel.js
devtools/client/inspector/markup/markup.js
--- a/devtools/client/inspector/inspector-panel.js
+++ b/devtools/client/inspector/inspector-panel.js
@@ -86,16 +86,17 @@ function InspectorPanel(iframeWindow, to
   this._toolbox = toolbox;
   this._target = toolbox._target;
   this.panelDoc = iframeWindow.document;
   this.panelWin = iframeWindow;
   this.panelWin.inspector = this;
 
   this.nodeMenuTriggerInfo = null;
 
+  this._handleRejectionIfNotDestroyed = this._handleRejectionIfNotDestroyed.bind(this);
   this._onBeforeNavigate = this._onBeforeNavigate.bind(this);
   this.onNewRoot = this.onNewRoot.bind(this);
   this._onContextMenu = this._onContextMenu.bind(this);
   this._updateSearchResultsLabel = this._updateSearchResultsLabel.bind(this);
   this.onNewSelection = this.onNewSelection.bind(this);
   this.onBeforeNewSelection = this.onBeforeNewSelection.bind(this);
   this.onDetached = this.onDetached.bind(this);
   this.onPaneToggleButtonActivated = this.onPaneToggleButtonActivated.bind(this);
@@ -162,16 +163,28 @@ InspectorPanel.prototype = {
     return this._target.client.traits.getUsedFontFaces;
   },
 
   get canPasteInnerOrAdjacentHTML() {
     return this._target.client.traits.pasteHTML;
   },
 
   /**
+   * Handle promise rejections for various asynchronous actions, and only log errors if
+   * the inspector panel still exists.
+   * This is useful to silence useless errors that happen when the inspector is closed
+   * while still initializing (and making protocol requests).
+   */
+  _handleRejectionIfNotDestroyed: function (e) {
+    if (!this._panelDestroyer) {
+      console.error(e);
+    }
+  },
+
+  /**
    * Figure out what features the backend supports
    */
   _detectActorFeatures: function () {
     this._supportsDuplicateNode = false;
     this._supportsScrollIntoView = false;
     this._supportsResolveRelativeURL = false;
 
     return promise.all([
@@ -253,17 +266,17 @@ InspectorPanel.prototype = {
     this._destroyMarkup();
     this.isDirty = false;
     this._pendingSelection = null;
   },
 
   _getPageStyle: function () {
     return this._toolbox.inspector.getPageStyle().then(pageStyle => {
       this.pageStyle = pageStyle;
-    });
+    }, this._handleRejectionIfNotDestroyed);
   },
 
   /**
    * Return a promise that will resolve to the default node for selection.
    */
   _getDefaultNodeForSelection: function () {
     if (this._defaultNode) {
       return this._defaultNode;
@@ -533,17 +546,18 @@ InspectorPanel.prototype = {
         if (!this.markup) {
           return;
         }
         this.markup.expandNode(this.selection.nodeFront);
         this.emit("new-root");
       });
     };
     this._pendingSelection = onNodeSelected;
-    this._getDefaultNodeForSelection().then(onNodeSelected, console.error);
+    this._getDefaultNodeForSelection()
+        .then(onNodeSelected, this._handleRejectionIfNotDestroyed);
   },
 
   _selectionCssSelector: null,
 
   /**
    * Set the currently selected node unique css selector.
    * Will store the current target url along with it to allow pre-selection at
    * reload
@@ -612,26 +626,17 @@ InspectorPanel.prototype = {
     }
 
     // On any new selection made by the user, store the unique css selector
     // of the selected node so it can be restored after reload of the same page
     if (this.canGetUniqueSelector &&
         this.selection.isElementNode()) {
       selection.getUniqueSelector().then(selector => {
         this.selectionCssSelector = selector;
-      }).then(null, e => {
-        // Only log this as an error if the panel hasn't been destroyed in the
-        // meantime.
-        if (!this._panelDestroyer) {
-          console.error(e);
-        } else {
-          console.warn("Could not set the unique selector for the newly " +
-            "selected node, the inspector was destroyed.");
-        }
-      });
+      }, this._handleRejectionIfNotDestroyed);
     }
 
     let selfUpdate = this.updating("inspector-panel");
     executeSoon(() => {
       try {
         selfUpdate(selection);
       } catch (ex) {
         console.error(ex);
@@ -1334,17 +1339,17 @@ InspectorPanel.prototype = {
 
   /**
    * Clear any pseudo-class locks applied to the current hierarchy.
    */
   clearPseudoClasses: function () {
     if (!this.walker) {
       return promise.resolve();
     }
-    return this.walker.clearPseudoClassLocks().then(null, console.error);
+    return this.walker.clearPseudoClassLocks().catch(this._handleRejectionIfNotDestroyed);
   },
 
   /**
    * Edit the outerHTML of the selected Node.
    */
   editHTML: function () {
     if (!this.selection.isNode()) {
       return;
--- a/devtools/client/inspector/markup/markup.js
+++ b/devtools/client/inspector/markup/markup.js
@@ -114,16 +114,17 @@ function MarkupView(inspector, frame, co
   this.popup = new AutocompletePopup(inspector._toolbox, options);
 
   this.undo = new UndoStack();
   this.undo.installController(controllerWindow);
 
   this._containers = new Map();
 
   // Binding functions that need to be called in scope.
+  this._handleRejectionIfNotDestroyed = this._handleRejectionIfNotDestroyed.bind(this);
   this._mutationObserver = this._mutationObserver.bind(this);
   this._onDisplayChange = this._onDisplayChange.bind(this);
   this._onMouseClick = this._onMouseClick.bind(this);
   this._onMouseUp = this._onMouseUp.bind(this);
   this._onNewSelection = this._onNewSelection.bind(this);
   this._onCopy = this._onCopy.bind(this);
   this._onFocus = this._onFocus.bind(this);
   this._onMouseMove = this._onMouseMove.bind(this);
@@ -164,16 +165,28 @@ function MarkupView(inspector, frame, co
 MarkupView.prototype = {
   /**
    * How long does a node flash when it mutates (in ms).
    */
   CONTAINER_FLASHING_DURATION: 500,
 
   _selectedContainer: null,
 
+  /**
+   * Handle promise rejections for various asynchronous actions, and only log errors if
+   * the markup view still exists.
+   * This is useful to silence useless errors that happen when the markup view is
+   * destroyed while still initializing (and making protocol requests).
+   */
+  _handleRejectionIfNotDestroyed: function (e) {
+    if (!this._destroyer) {
+      console.error(e);
+    }
+  },
+
   _initTooltips: function () {
     this.eventDetailsTooltip = new HTMLTooltip(this._inspector.toolbox,
       {type: "arrow"});
     this.imagePreviewTooltip = new HTMLTooltip(this._inspector.toolbox,
       {type: "arrow", useXulWrapper: "true"});
     this._enableImagePreviewTooltip();
   },
 
@@ -586,24 +599,17 @@ MarkupView.prototype = {
       }
 
       // Mark the node as selected.
       this.markNodeAsSelected(selection.nodeFront);
 
       // Make sure the new selection is navigated to.
       this.maybeNavigateToNewSelection();
       return undefined;
-    }).catch(e => {
-      if (!this._destroyer) {
-        console.error(e);
-      } else {
-        console.warn("Could not mark node as selected, the markup-view was " +
-          "destroyed while showing the node.");
-      }
-    });
+    }).catch(this._handleRejectionIfNotDestroyed);
 
     promise.all([onShowBoxModel, onShow]).then(done);
   },
 
   /**
    * Maybe make selected the current node selection's MarkupContainer depending
    * on why the current node got selected.
    */
@@ -1026,18 +1032,18 @@ MarkupView.prototype = {
         container.childrenDirty = true;
         this._updateChildren(container, {flash: true});
         container.update();
       }
     }
 
     this._waitForChildren().then(() => {
       if (this._destroyer) {
-        console.warn("Could not fully update after markup mutations, " +
-          "the markup-view was destroyed while waiting for children.");
+        // Could not fully update after markup mutations, the markup-view was destroyed
+        // while waiting for children. Bail out silently.
         return;
       }
       this._flashMutatedNodes(mutations);
       this._inspector.emit("markupmutation", mutations);
 
       // Since the htmlEditor is absolutely positioned, a mutation may change
       // the location in which it should be shown.
       this.htmlEditor.refresh();
@@ -1126,36 +1132,27 @@ MarkupView.prototype = {
 
     return this._waitForChildren().then(() => {
       if (this._destroyer) {
         return promise.reject("markupview destroyed");
       }
       return this._ensureVisible(node);
     }).then(() => {
       scrollIntoViewIfNeeded(this.getContainer(node).editor.elt, centered);
-    }, e => {
-      // Only report this rejection as an error if the panel hasn't been
-      // destroyed in the meantime.
-      if (!this._destroyer) {
-        console.error(e);
-      } else {
-        console.warn("Could not show the node, the markup-view was destroyed " +
-          "while waiting for children");
-      }
-    });
+    }, this._handleRejectionIfNotDestroyed);
   },
 
   /**
    * Expand the container's children.
    */
   _expandContainer: function (container) {
     return this._updateChildren(container, {expand: true}).then(() => {
       if (this._destroyer) {
-        console.warn("Could not expand the node, the markup-view was " +
-          "destroyed");
+        // Could not expand the node, the markup-view was destroyed in the meantime. Just
+        // silently give up.
         return;
       }
       container.setExpanded(true);
     });
   },
 
   /**
    * Expand the node's children.
@@ -1682,17 +1679,17 @@ MarkupView.prototype = {
           if (!children.hasLast) {
             let span = this.template("more-nodes", data);
             fragment.appendChild(span);
           }
         }
 
         container.children.appendChild(fragment);
         return container;
-      }).then(null, console.error);
+      }).catch(this._handleRejectionIfNotDestroyed);
     this._queuedChildUpdates.set(container, updatePromise);
     return updatePromise;
   },
 
   _waitForChildren: function () {
     if (!this._queuedChildUpdates) {
       return promise.resolve(undefined);
     }