Bug 943510 - Convert to Promise.jsm in the devtools framework. r=pbrosset, r=rcampbell, a=lsblakk
authorBrandon Benvie <bbenvie@mozilla.com>
Mon, 17 Mar 2014 11:11:00 -0700
changeset 191901 76622a1dc44e73f8317397ba4589ef75e2b5f72c
parent 191900 6680a86d1ebceec2ad44b22db53ee5be28f1ce3c
child 191902 bbdc0136061edb72bb94f131886b65cbf01f9737
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspbrosset, rcampbell, lsblakk
bugs943510
milestone30.0a2
Bug 943510 - Convert to Promise.jsm in the devtools framework. r=pbrosset, r=rcampbell, a=lsblakk
browser/devtools/framework/gDevTools.jsm
browser/devtools/framework/sidebar.js
browser/devtools/framework/target.js
browser/devtools/framework/toolbox-hosts.js
browser/devtools/framework/toolbox-options.js
browser/devtools/framework/toolbox.js
browser/devtools/inspector/breadcrumbs.js
browser/devtools/inspector/inspector-panel.js
browser/devtools/inspector/test/browser_inspector_breadcrumbs.js
browser/devtools/inspector/test/browser_inspector_bug_952294_tooltips_dimensions.js
browser/devtools/inspector/test/browser_inspector_changes.js
browser/devtools/inspector/test/browser_inspector_pseudoclass_lock.js
browser/devtools/inspector/test/head.js
browser/devtools/markupview/markup-view.js
browser/devtools/responsivedesign/test/browser_responsivecomputedview.js
browser/devtools/responsivedesign/test/browser_responsiveruleview.js
browser/devtools/responsivedesign/test/head.js
browser/devtools/styleinspector/test/browser_bug946331_close_tooltip_on_new_selection.js
browser/devtools/styleinspector/test/browser_ruleview_original_source_link.js
browser/devtools/styleinspector/test/head.js
--- a/browser/devtools/framework/gDevTools.jsm
+++ b/browser/devtools/framework/gDevTools.jsm
@@ -6,18 +6,18 @@
 
 this.EXPORTED_SYMBOLS = [ "gDevTools", "DevTools", "gDevToolsBrowser" ];
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/devtools/event-emitter.js");
-let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").Promise;
 Cu.import("resource://gre/modules/devtools/Loader.jsm");
+const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 
 var ProfilerController = devtools.require("devtools/profiler/controller");
 
 const FORBIDDEN_IDS = new Set(["toolbox", ""]);
 const MAX_ORDINAL = 99;
 
 
 /**
--- a/browser/devtools/framework/sidebar.js
+++ b/browser/devtools/framework/sidebar.js
@@ -3,17 +3,17 @@
 /* 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/. */
 
 const {Cu} = require("chrome");
 
 Cu.import("resource://gre/modules/Services.jsm");
 
-var promise = require("sdk/core/promise");
+var {Promise: promise} = require("resource://gre/modules/Promise.jsm");
 var EventEmitter = require("devtools/toolkit/event-emitter");
 var Telemetry = require("devtools/shared/telemetry");
 
 const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 /**
  * ToolSidebar provides methods to register tabs in the sidebar.
  * It's assumed that the sidebar contains a xul:tabbox.
--- a/browser/devtools/framework/target.js
+++ b/browser/devtools/framework/target.js
@@ -1,18 +1,17 @@
 /* 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 {Cc, Ci, Cu} = require("chrome");
-
-var promise = require("sdk/core/promise");
-var EventEmitter = require("devtools/toolkit/event-emitter");
+const {Promise: promise} = require("resource://gre/modules/Promise.jsm");
+const EventEmitter = require("devtools/toolkit/event-emitter");
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DebuggerServer",
   "resource://gre/modules/devtools/dbg-server.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DebuggerClient",
   "resource://gre/modules/devtools/dbg-client.jsm");
 
 const targets = new WeakMap();
--- a/browser/devtools/framework/toolbox-hosts.js
+++ b/browser/devtools/framework/toolbox-hosts.js
@@ -1,19 +1,17 @@
 /* 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 {Cu} = require("chrome");
-
-let promise = require("sdk/core/promise");
-let EventEmitter = require("devtools/toolkit/event-emitter");
-
+const EventEmitter = require("devtools/toolkit/event-emitter");
+const {Promise: promise} = require("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource:///modules/devtools/DOMHelpers.jsm");
 
 /**
  * A toolbox host represents an object that contains a toolbox (e.g. the
  * sidebar or a separate window). Any host object should implement the
  * following functions:
  *
@@ -212,27 +210,26 @@ WindowHost.prototype = {
     let deferred = promise.defer();
 
     let flags = "chrome,centerscreen,resizable,dialog=no";
     let win = Services.ww.openWindow(null, this.WINDOW_URL, "_blank",
                                      flags, null);
 
     let frameLoad = function(event) {
       win.removeEventListener("load", frameLoad, true);
+      win.focus();
       this.frame = win.document.getElementById("toolbox-iframe");
       this.emit("ready", this.frame);
 
       deferred.resolve(this.frame);
     }.bind(this);
 
     win.addEventListener("load", frameLoad, true);
     win.addEventListener("unload", this._boundUnload);
 
-    win.focus();
-
     this._window = win;
 
     return deferred.promise;
   },
 
   /**
    * Catch the user closing the window.
    */
--- a/browser/devtools/framework/toolbox-options.js
+++ b/browser/devtools/framework/toolbox-options.js
@@ -1,20 +1,18 @@
 /* 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 {Cu, Cc, Ci} = require("chrome");
-
-let promise = require("sdk/core/promise");
-let EventEmitter = require("devtools/toolkit/event-emitter");
-
-Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+const EventEmitter = require("devtools/toolkit/event-emitter");
+const {Promise: promise} = require("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource:///modules/devtools/gDevTools.jsm");
 
 exports.OptionsPanel = OptionsPanel;
 
 XPCOMUtils.defineLazyGetter(this, "l10n", function() {
   let bundle = Services.strings.createBundle("chrome://browser/locale/devtools/toolbox.properties");
   let l10n = function(aName, ...aArgs) {
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -5,17 +5,17 @@
 "use strict";
 
 const MAX_ORDINAL = 99;
 const ZOOM_PREF = "devtools.toolbox.zoomValue";
 const MIN_ZOOM = 0.5;
 const MAX_ZOOM = 2;
 
 let {Cc, Ci, Cu} = require("chrome");
-let promise = require("sdk/core/promise");
+let {Promise: promise} = require("resource://gre/modules/Promise.jsm");
 let EventEmitter = require("devtools/toolkit/event-emitter");
 let Telemetry = require("devtools/shared/telemetry");
 let HUDService = require("devtools/webconsole/hudservice");
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource:///modules/devtools/gDevTools.jsm");
 Cu.import("resource:///modules/devtools/scratchpad-manager.jsm");
@@ -1428,25 +1428,25 @@ ToolboxHighlighterUtils.prototype = {
     });
   },
 
   /**
    * Hide the highlighter.
    * @return a promise that resolves when the highlighter is hidden
    */
   unhighlight: function(forceHide=false) {
-    if (this.isRemoteHighlightable) {
-      // If the remote highlighter exists on the target, use it
-      return this.toolbox.initInspector().then(() => {
-        let autohide = forceHide || !gDevTools.testing;
+    let unhighlightPromise;
+    forceHide = forceHide || !gDevTools.testing;
 
-        if (autohide) {
-          return this.toolbox.highlighter.hideBoxModel();
-        }
-        return promise.resolve();
-      });
+    if (forceHide && this.isRemoteHighlightable && this.toolbox.highlighter) {
+      // If the remote highlighter exists on the target, use it
+      unhighlightPromise = this.toolbox.highlighter.hideBoxModel();
     } else {
       // If not, no need to unhighlight as the older highlight method uses a
       // setTimeout to hide itself
-      return promise.resolve();
+      unhighlightPromise = promise.resolve();
     }
+
+    return unhighlightPromise.then(() => {
+      this.toolbox.emit("node-unhighlight");
+    });
   }
 };
--- a/browser/devtools/inspector/breadcrumbs.js
+++ b/browser/devtools/inspector/breadcrumbs.js
@@ -687,18 +687,20 @@ HTMLBreadcrumbs.prototype = {
 
     let doneUpdating = this.inspector.updating("breadcrumbs");
     // Add the first child of the very last node of the breadcrumbs if possible.
     this.ensureFirstChild().then(this.selectionGuard()).then(() => {
       this.updateSelectors();
 
       // Make sure the selected node and its neighbours are visible.
       this.scroll();
-      this.inspector.emit("breadcrumbs-updated", this.selection.nodeFront);
-      doneUpdating();
+      return resolveNextTick().then(() => {
+        this.inspector.emit("breadcrumbs-updated", this.selection.nodeFront);
+        doneUpdating();
+      });
     }).then(null, err => {
       doneUpdating(this.selection.nodeFront);
       this.selectionGuardEnd(err);
     });
   }
 };
 
 XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
--- a/browser/devtools/inspector/inspector-panel.js
+++ b/browser/devtools/inspector/inspector-panel.js
@@ -465,19 +465,18 @@ InspectorPanel.prototype = {
     this.selection.setNodeFront(parentNode ? parentNode : this._defaultNode, "detached");
   },
 
   /**
    * Destroy the inspector.
    */
   destroy: function InspectorPanel__destroy() {
     if (this._panelDestroyer) {
-      return this._panelDestroyer.promise;
+      return this._panelDestroyer;
     }
-    this._panelDestroyer = promise.defer();
 
     if (this.walker) {
       this.walker.off("new-root", this.onNewRoot);
       this.pageStyle = null;
     }
 
     this.cancelUpdate();
     this.cancelLayoutChange();
@@ -501,29 +500,28 @@ InspectorPanel.prototype = {
     this.nodemenu.removeEventListener("popuphiding", this._resetNodeMenu, true);
     this.breadcrumbs.destroy();
     this.searchSuggestions.destroy();
     this.searchBox = null;
     this.selection.off("new-node-front", this.onNewSelection);
     this.selection.off("before-new-node", this.onBeforeNewSelection);
     this.selection.off("before-new-node-front", this.onBeforeNewSelection);
     this.selection.off("detached-front", this.onDetached);
-    this._destroyMarkup();
+    this._panelDestroyer = this._destroyMarkup();
     this.panelWin.inspector = null;
     this.target = null;
     this.panelDoc = null;
     this.panelWin = null;
     this.breadcrumbs = null;
     this.searchSuggestions = null;
     this.lastNodemenuItem = null;
     this.nodemenu = null;
     this._toolbox = null;
 
-    this._panelDestroyer.resolve(null);
-    return this._panelDestroyer.promise;
+    return this._panelDestroyer;
   },
 
   /**
    * Show the node menu.
    */
   showNodeMenu: function InspectorPanel_showNodeMenu(aButton, aPosition, aExtraItems) {
     if (aExtraItems) {
       for (let item of aExtraItems) {
@@ -638,32 +636,38 @@ InspectorPanel.prototype = {
 
     let controllerWindow = this._toolbox.doc.defaultView;
     this.markup = new MarkupView(this, this._markupFrame, controllerWindow);
 
     this.emit("markuploaded");
   },
 
   _destroyMarkup: function InspectorPanel__destroyMarkup() {
+    let destroyPromise;
+
     if (this._boundMarkupFrameLoad) {
       this._markupFrame.removeEventListener("load", this._boundMarkupFrameLoad, true);
       this._boundMarkupFrameLoad = null;
     }
 
     if (this.markup) {
-      this.markup.destroy();
+      destroyPromise = this.markup.destroy();
       this.markup = null;
+    } else {
+      destroyPromise = promise.resolve();
     }
 
     if (this._markupFrame) {
       this._markupFrame.parentNode.removeChild(this._markupFrame);
       this._markupFrame = null;
     }
 
     this._markupBox = null;
+
+    return destroyPromise;
   },
 
   /**
    * Toggle a pseudo class.
    */
   togglePseudoClass: function InspectorPanel_togglePseudoClass(aPseudo) {
     if (this.selection.isElementNode()) {
       let node = this.selection.nodeFront;
--- a/browser/devtools/inspector/test/browser_inspector_breadcrumbs.js
+++ b/browser/devtools/inspector/test/browser_inspector_breadcrumbs.js
@@ -14,16 +14,19 @@ function test()
     {nodeId: "i22211", result: "i2 i22 i222 i2221 i22211"},
     {nodeId: "i22", result: "i2 i22 i222 i2221 i22211"},
   ];
 
   let doc;
   let nodes;
   let cursor;
   let inspector;
+  let target;
+  let panel;
+  let container;
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function onload() {
     gBrowser.selectedBrowser.removeEventListener("load", onload, true);
     doc = content.document;
     waitForFocus(setupTest, content);
   }, true);
 
@@ -38,16 +41,19 @@ function test()
     }
 
     openInspector(runTests);
   }
 
   function runTests(aInspector)
   {
     inspector = aInspector;
+    target = TargetFactory.forTab(gBrowser.selectedTab);
+    panel = gDevTools.getToolbox(target).getPanel("inspector");
+    container = panel.panelDoc.getElementById("inspector-breadcrumbs");
     cursor = 0;
     inspector.on("breadcrumbs-updated", nodeSelected);
     executeSoon(function() {
       inspector.selection.setNode(nodes[0].node);
     });
   }
 
   function nodeSelected()
@@ -64,19 +70,16 @@ function test()
     } else {
       let node = nodes[cursor].node;
       inspector.selection.setNode(node);
     }
   }
 
   function performTest()
   {
-    let target = TargetFactory.forTab(gBrowser.selectedTab);
-    let panel = gDevTools.getToolbox(target).getPanel("inspector");
-    let container = panel.panelDoc.getElementById("inspector-breadcrumbs");
     let buttonsLabelIds = nodes[cursor].result.split(" ");
 
     // html > body > …
     is(container.childNodes.length, buttonsLabelIds.length + 2, "Node " + cursor + ": Items count");
 
     for (let i = 2; i < container.childNodes.length; i++) {
       let expectedId = "#" + buttonsLabelIds[i - 2];
       let button = container.childNodes[i];
--- a/browser/devtools/inspector/test/browser_inspector_bug_952294_tooltips_dimensions.js
+++ b/browser/devtools/inspector/test/browser_inspector_bug_952294_tooltips_dimensions.js
@@ -34,17 +34,17 @@ function test() {
 
 function createDocument() {
   contentDoc.body.innerHTML = PAGE_CONTENT;
 
   openInspector(aInspector => {
     inspector = aInspector;
     markupView = inspector.markup;
 
-    inspector.sidebar.once("ruleview-ready", function() {
+    waitForView("ruleview", () => {
       ruleView = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
       inspector.sidebar.select("ruleview");
       startTests();
     });
   });
 }
 
 function endTests() {
--- a/browser/devtools/inspector/test/browser_inspector_changes.js
+++ b/browser/devtools/inspector/test/browser_inspector_changes.js
@@ -16,28 +16,30 @@ function test() {
     openInspector(runInspectorTests);
   }
 
   function getInspectorRuleProp(aName)
   {
     let ruleview = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
     let inlineStyles = ruleview._elementStyle.rules[0];
 
-    for each (let prop in inlineStyles.textProps) {
+    for (let key in inlineStyles.textProps) {
+      let prop = inlineStyles.textProps[key];
       if (prop.name == aName) {
         return prop;
       }
     }
     return null;
   }
 
   function runInspectorTests(aInspector)
   {
     inspector = aInspector;
-    inspector.sidebar.once("computedview-ready", () => {
+
+    waitForView("computedview", () => {
       info("Computed View ready");
       inspector.sidebar.select("computedview");
 
       testDiv = doc.getElementById("testdiv");
 
       testDiv.style.fontSize = "10px";
 
       // Start up the style inspector panel...
--- a/browser/devtools/inspector/test/browser_inspector_pseudoclass_lock.js
+++ b/browser/devtools/inspector/test/browser_inspector_pseudoclass_lock.js
@@ -46,17 +46,18 @@ function createDocument()
   doc.body.appendChild(parentDiv);
 
   openInspector(selectNode);
 }
 
 function selectNode(aInspector)
 {
   inspector = aInspector;
-  inspector.sidebar.once("ruleview-ready", function() {
+
+  waitForView("ruleview", () => {
     ruleview = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
     inspector.sidebar.select("ruleview");
     inspector.selection.setNode(div, "test");
     inspector.once("inspector-updated", performTests);
   });
 }
 
 function performTests()
--- a/browser/devtools/inspector/test/head.js
+++ b/browser/devtools/inspector/test/head.js
@@ -197,16 +197,25 @@ function ruleView()
   return iframe.contentWindow.ruleView;
 }
 
 function getComputedView() {
   let inspector = getActiveInspector();
   return inspector.sidebar.getWindowForTab("computedview").computedview.view;
 }
 
+function waitForView(aName, aCallback) {
+  let inspector = getActiveInspector();
+  if (inspector.sidebar.getTab(aName)) {
+    aCallback();
+  } else {
+    inspector.sidebar.once(aName + "-ready", aCallback);
+  }
+}
+
 function synthesizeKeyFromKeyTag(aKeyId) {
   let key = document.getElementById(aKeyId);
   isnot(key, null, "Successfully retrieved the <key> node");
 
   let modifiersAttr = key.getAttribute("modifiers");
 
   let name = null;
 
--- a/browser/devtools/markupview/markup-view.js
+++ b/browser/devtools/markupview/markup-view.js
@@ -172,17 +172,17 @@ MarkupView.prototype = {
     this._hoveredNode = null;
   },
 
   _showBoxModel: function(nodeFront, options={}) {
     this._inspector.toolbox.highlighterUtils.highlightNodeFront(nodeFront, options);
   },
 
   _hideBoxModel: function(forceHide) {
-    this._inspector.toolbox.highlighterUtils.unhighlight(forceHide);
+    return this._inspector.toolbox.highlighterUtils.unhighlight(forceHide);
   },
 
   _briefBoxModelTimer: null,
   _brieflyShowBoxModel: function(nodeFront, options) {
     let win = this._frame.contentWindow;
 
     if (this._briefBoxModelTimer) {
       win.clearTimeout(this._briefBoxModelTimer);
@@ -1047,19 +1047,23 @@ MarkupView.prototype = {
       center: aCentered
     });
   },
 
   /**
    * Tear down the markup panel.
    */
   destroy: function() {
+    if (this._destroyer) {
+      return this._destroyer;
+    }
+
     // Note that if the toolbox is closed, this will work fine, but will fail
     // in case the browser is closed and will trigger a noSuchActor message.
-    this._hideBoxModel();
+    this._destroyer = this._hideBoxModel();
 
     this._hoveredNode = null;
     this._inspector.toolbox.off("picker-node-hovered", this._onToolboxPickerHover);
 
     this.htmlEditor.destroy();
     this.htmlEditor = null;
 
     this.undo.destroy();
@@ -1103,16 +1107,18 @@ MarkupView.prototype = {
 
     for (let [key, container] of this._containers) {
       container.destroy();
     }
     this._containers = null;
 
     this.tooltip.destroy();
     this.tooltip = null;
+
+    return this._destroyer;
   },
 
   /**
    * Initialize the preview panel.
    */
   _initPreview: function() {
     this._previewEnabled = Services.prefs.getBoolPref("devtools.inspector.markupPreview");
     if (!this._previewEnabled) {
--- a/browser/devtools/responsivedesign/test/browser_responsivecomputedview.js
+++ b/browser/devtools/responsivedesign/test/browser_responsivecomputedview.js
@@ -48,17 +48,17 @@ function test() {
 
     instance.stack.setAttribute("notransition", "true");
     registerCleanupFunction(function() {
       instance.stack.removeAttribute("notransition");
     });
 
     instance.setSize(500, 500);
 
-    openComputedView(onInspectorUIOpen);
+    openView("computedview", onInspectorUIOpen);
   }
 
   function onInspectorUIOpen(aInspector, aComputedView) {
     inspector = aInspector;
     ok(inspector, "Got inspector instance");
 
     let div = content.document.getElementsByTagName("div")[0];
 
--- a/browser/devtools/responsivedesign/test/browser_responsiveruleview.js
+++ b/browser/devtools/responsivedesign/test/browser_responsiveruleview.js
@@ -44,17 +44,17 @@ function test() {
 
     instance.stack.setAttribute("notransition", "true");
     registerCleanupFunction(function() {
       instance.stack.removeAttribute("notransition");
     });
 
     instance.setSize(500, 500);
 
-    openRuleView(onInspectorUIOpen);
+    openView("ruleview", onInspectorUIOpen);
   }
 
   function onInspectorUIOpen(aInspector, aRuleView) {
     inspector = aInspector;
     ruleView = aRuleView;
     ok(inspector, "Got inspector instance");
 
     let div = content.document.getElementsByTagName("div")[0];
--- a/browser/devtools/responsivedesign/test/head.js
+++ b/browser/devtools/responsivedesign/test/head.js
@@ -18,28 +18,24 @@ SimpleTest.registerCleanupFunction(() =>
 function openInspector(callback)
 {
   let target = TargetFactory.forTab(gBrowser.selectedTab);
   gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
     callback(toolbox.getCurrentPanel());
   });
 }
 
-function openComputedView(callback)
+function openView(name, callback)
 {
   openInspector(inspector => {
-    inspector.sidebar.once("computedview-ready", () => {
-      inspector.sidebar.select("computedview");
-      let ruleView = inspector.sidebar.getWindowForTab("computedview").computedview.view;
-      callback(inspector, ruleView);
-    })
+    function onReady() {
+      inspector.sidebar.select(name);
+      let { view } = inspector.sidebar.getWindowForTab(name)[name];
+      callback(inspector, view);
+    }
+
+    if (inspector.sidebar.getTab(name)) {
+      onReady();
+    } else {
+      inspector.sidebar.once(name + "-ready", onReady);
+    }
   });
 }
-function openRuleView(callback)
-{
-  openInspector(inspector => {
-    inspector.sidebar.once("ruleview-ready", () => {
-      inspector.sidebar.select("ruleview");
-      let ruleView = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
-      callback(inspector, ruleView);
-    })
-  });
-}
--- a/browser/devtools/styleinspector/test/browser_bug946331_close_tooltip_on_new_selection.js
+++ b/browser/devtools/styleinspector/test/browser_bug946331_close_tooltip_on_new_selection.js
@@ -20,21 +20,21 @@ function test() {
   }, true);
 
   content.location = "data:text/html,rule/computed views tooltip hiding test";
 }
 
 function createDocument() {
   contentDoc.body.innerHTML = PAGE_CONTENT;
 
-  openRuleView((aInspector, aRuleView) => {
+  openView("ruleview", (aInspector, aRuleView) => {
     inspector = aInspector;
     ruleView = aRuleView;
-    inspector.sidebar.once("computedview-ready", () => {
-      computedView = inspector.sidebar.getWindowForTab("computedview").computedview.view;
+    openView("computedview", (_, aComputedView) => {
+      computedView = aComputedView;
       startTests();
     });
   });
 }
 
 function startTests() {
   inspector.selection.setNode(contentDoc.querySelector(".one"));
   inspector.once("inspector-updated", testRuleView);
@@ -61,17 +61,16 @@ function testRuleView() {
     inspector.selection.setNode(contentDoc.querySelector(".two"));
   });
 }
 
 function testComputedView() {
   info("Testing computed view tooltip closes on new selection");
 
   inspector.sidebar.select("computedview");
-  computedView = inspector.sidebar.getWindowForTab("computedview").computedview.view;
 
   // Show the computed view tooltip
   let tooltip = computedView.tooltip;
   tooltip.show();
   tooltip.once("shown", () => {
     // Select a new node and assert that the tooltip closes
     tooltip.once("hidden", () => {
       ok(true, "Computed view tooltip closed after a new node got selected");
--- a/browser/devtools/styleinspector/test/browser_ruleview_original_source_link.js
+++ b/browser/devtools/styleinspector/test/browser_ruleview_original_source_link.js
@@ -42,17 +42,17 @@ function openToolbox() {
   });
 }
 
 function highlightNode()
 {
   // Highlight a node.
   let div = content.document.getElementsByTagName("div")[0];
 
-  inspector.selection.setNode(div);
+  inspector.selection.setNode(div, "test");
   inspector.once("inspector-updated", () => {
     is(inspector.selection.node, div, "selection matches the div element");
     testRuleViewLink();
   });
 }
 
 function testRuleViewLink() {
   verifyLinkText(SCSS_LOC, testTogglePref);
--- a/browser/devtools/styleinspector/test/head.js
+++ b/browser/devtools/styleinspector/test/head.js
@@ -60,36 +60,41 @@ function openInspector(callback)
 }
 
 function getActiveInspector()
 {
   let target = TargetFactory.forTab(gBrowser.selectedTab);
   return gDevTools.getToolbox(target).getPanel("inspector");
 }
 
-function openRuleView(callback)
+function openView(name, callback)
 {
   openInspector(inspector => {
-    inspector.sidebar.once("ruleview-ready", () => {
-      inspector.sidebar.select("ruleview");
-      let ruleView = inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
-      callback(inspector, ruleView);
-    })
+    function onReady() {
+      inspector.sidebar.select(name);
+      let { view } = inspector.sidebar.getWindowForTab(name)[name];
+      callback(inspector, view);
+    }
+
+    if (inspector.sidebar.getTab(name)) {
+      onReady();
+    } else {
+      inspector.sidebar.once(name + "-ready", onReady);
+    }
   });
 }
 
+function openRuleView(callback)
+{
+  openView("ruleview", callback);
+}
+
 function openComputedView(callback)
 {
-  openInspector(inspector => {
-    inspector.sidebar.once("computedview-ready", () => {
-      inspector.sidebar.select("computedview");
-      let computedView = inspector.sidebar.getWindowForTab("computedview").computedview.view;
-      callback(inspector, computedView);
-    })
-  });
+  openView("computedview", callback);
 }
 
 /**
  * Simple DOM node accesor function that takes either a node or a string css
  * selector as argument and returns the corresponding node
  * @param {String|DOMNode} nodeOrSelector
  * @return {DOMNode}
  */