Bug 1240907 - Tunnel toolbox messages to content in RDM. r=ochameau draft
authorJ. Ryan Stinnett <jryans@gmail.com>
Mon, 06 Jun 2016 18:28:26 -0500
changeset 386521 49d39db3fed8f1d3baa7740e9416b03b40fc6b43
parent 385271 3dd4b5524090701a711a8d78e53f8008943d4e6d
child 386522 a36e8ba82c7887a9505e47425d4a2a429d1f813c
push id22732
push userbmo:jryans@gmail.com
push dateTue, 12 Jul 2016 05:53:16 +0000
reviewersochameau
bugs1240907
milestone50.0a1
Bug 1240907 - Tunnel toolbox messages to content in RDM. r=ochameau MozReview-Commit-ID: LXbVhwNVlcp
devtools/client/animationinspector/test/browser.ini
devtools/client/framework/test/shared-head.js
devtools/client/inspector/computed/test/browser.ini
devtools/client/inspector/fonts/test/browser.ini
devtools/client/inspector/layout/test/browser.ini
devtools/client/inspector/markup/test/browser.ini
devtools/client/inspector/rules/test/browser.ini
devtools/client/inspector/shared/test/browser.ini
devtools/client/inspector/test/browser.ini
devtools/client/inspector/test/head.js
devtools/client/inspector/test/shared-head.js
devtools/client/responsive.html/browser/tunnel.js
devtools/client/responsive.html/test/browser/browser.ini
devtools/client/responsive.html/test/browser/browser_toolbox_computed_view.js
devtools/client/responsive.html/test/browser/browser_toolbox_rule_view.js
devtools/client/responsive.html/test/browser/head.js
devtools/client/styleeditor/test/browser.ini
devtools/server/actors/webbrowser.js
devtools/server/main.js
devtools/shared/transport/transport.js
devtools/shared/webconsole/network-monitor.js
--- a/devtools/client/animationinspector/test/browser.ini
+++ b/devtools/client/animationinspector/test/browser.ini
@@ -11,16 +11,17 @@ support-files =
   doc_pseudo_elements.html
   doc_script_animation.html
   doc_simple_animation.html
   doc_multiple_animation_types.html
   head.js
   !/devtools/client/commandline/test/helpers.js
   !/devtools/client/framework/test/shared-head.js
   !/devtools/client/inspector/test/head.js
+  !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/test-actor-registry.js
   !/devtools/client/shared/test/test-actor.js
 
 [browser_animation_animated_properties_displayed.js]
 [browser_animation_click_selects_animation.js]
 [browser_animation_controller_exposes_document_currentTime.js]
 skip-if = os == "linux" && !debug # Bug 1234567
 [browser_animation_empty_on_invalid_nodes.js]
--- a/devtools/client/framework/test/shared-head.js
+++ b/devtools/client/framework/test/shared-head.js
@@ -456,8 +456,13 @@ function waitForContextMenu(popup, butto
  * @return {Promise} resolves when the preferences have been updated
  */
 function pushPref(preferenceName, value) {
   return new Promise(resolve => {
     let options = {"set": [[preferenceName, value]]};
     SpecialPowers.pushPrefEnv(options, resolve);
   });
 }
+
+var closeToolbox = Task.async(function* () {
+  let target = TargetFactory.forTab(gBrowser.selectedTab);
+  yield gDevTools.closeToolbox(target);
+});
--- a/devtools/client/inspector/computed/test/browser.ini
+++ b/devtools/client/inspector/computed/test/browser.ini
@@ -6,18 +6,19 @@ support-files =
   doc_media_queries.html
   doc_pseudoelement.html
   doc_sourcemaps.css
   doc_sourcemaps.css.map
   doc_sourcemaps.html
   doc_sourcemaps.scss
   head.js
   !/devtools/client/commandline/test/helpers.js
+  !/devtools/client/framework/test/shared-head.js
   !/devtools/client/inspector/test/head.js
-  !/devtools/client/framework/test/shared-head.js
+  !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_computed_browser-styles.js]
 [browser_computed_cycle_color.js]
 [browser_computed_getNodeInfo.js]
 [browser_computed_keybindings_01.js]
 [browser_computed_keybindings_02.js]
--- a/devtools/client/inspector/fonts/test/browser.ini
+++ b/devtools/client/inspector/fonts/test/browser.ini
@@ -3,17 +3,18 @@ tags = devtools
 subsuite = devtools
 support-files =
   browser_fontinspector.html
   test_iframe.html
   ostrich-black.ttf
   ostrich-regular.ttf
   head.js
   !/devtools/client/commandline/test/helpers.js
+  !/devtools/client/framework/test/shared-head.js
   !/devtools/client/inspector/test/head.js
-  !/devtools/client/framework/test/shared-head.js
+  !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_fontinspector.js]
 [browser_fontinspector_edit-previews.js]
 [browser_fontinspector_edit-previews-show-all.js]
 [browser_fontinspector_theme-change.js]
--- a/devtools/client/inspector/layout/test/browser.ini
+++ b/devtools/client/inspector/layout/test/browser.ini
@@ -1,18 +1,19 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
   doc_layout_iframe1.html
   doc_layout_iframe2.html
   head.js
   !/devtools/client/commandline/test/helpers.js
+  !/devtools/client/framework/test/shared-head.js
   !/devtools/client/inspector/test/head.js
-  !/devtools/client/framework/test/shared-head.js
+  !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_layout.js]
 [browser_layout_editablemodel.js]
 # [browser_layout_editablemodel_allproperties.js]
 # Disabled for too many intermittent failures (bug 1009322)
 [browser_layout_editablemodel_bluronclick.js]
--- a/devtools/client/inspector/markup/test/browser.ini
+++ b/devtools/client/inspector/markup/test/browser.ini
@@ -40,18 +40,19 @@ support-files =
   lib_jquery_1.2_min.js
   lib_jquery_1.3_min.js
   lib_jquery_1.4_min.js
   lib_jquery_1.6_min.js
   lib_jquery_1.7_min.js
   lib_jquery_1.11.1_min.js
   lib_jquery_2.1.1_min.js
   !/devtools/client/commandline/test/helpers.js
+  !/devtools/client/framework/test/shared-head.js
   !/devtools/client/inspector/test/head.js
-  !/devtools/client/framework/test/shared-head.js
+  !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_markup_accessibility_focus_blur.js]
 skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
 [browser_markup_accessibility_navigation.js]
 skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
 [browser_markup_accessibility_semantics.js]
--- a/devtools/client/inspector/rules/test/browser.ini
+++ b/devtools/client/inspector/rules/test/browser.ini
@@ -29,18 +29,19 @@ support-files =
   doc_sourcemaps.html
   doc_sourcemaps.scss
   doc_style_editor_link.css
   doc_test_image.png
   doc_urls_clickable.css
   doc_urls_clickable.html
   head.js
   !/devtools/client/commandline/test/helpers.js
+  !/devtools/client/framework/test/shared-head.js
   !/devtools/client/inspector/test/head.js
-  !/devtools/client/framework/test/shared-head.js
+  !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_rules_add-property-and-reselect.js]
 [browser_rules_add-property-cancel_01.js]
 [browser_rules_add-property-cancel_02.js]
 [browser_rules_add-property-cancel_03.js]
 [browser_rules_add-property-commented.js]
--- a/devtools/client/inspector/shared/test/browser.ini
+++ b/devtools/client/inspector/shared/test/browser.ini
@@ -8,18 +8,19 @@ support-files =
   doc_content_stylesheet_imported.css
   doc_content_stylesheet_imported2.css
   doc_content_stylesheet_linked.css
   doc_content_stylesheet_script.css
   doc_content_stylesheet_xul.css
   doc_frame_script.js
   head.js
   !/devtools/client/commandline/test/helpers.js
+  !/devtools/client/framework/test/shared-head.js
   !/devtools/client/inspector/test/head.js
-  !/devtools/client/framework/test/shared-head.js
+  !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_styleinspector_context-menu-copy-color_01.js]
 [browser_styleinspector_context-menu-copy-color_02.js]
 subsuite = clipboard
 [browser_styleinspector_context-menu-copy-urls.js]
 subsuite = clipboard
--- a/devtools/client/inspector/test/browser.ini
+++ b/devtools/client/inspector/test/browser.ini
@@ -26,18 +26,18 @@ support-files =
   doc_inspector_remove-iframe-during-load.html
   doc_inspector_search.html
   doc_inspector_search-reserved.html
   doc_inspector_search-suggestions.html
   doc_inspector_search-svg.html
   doc_inspector_select-last-selected-01.html
   doc_inspector_select-last-selected-02.html
   head.js
+  shared-head.js
   !/devtools/client/commandline/test/helpers.js
-  !/devtools/client/inspector/test/head.js
   !/devtools/client/framework/test/shared-head.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_inspector_addNode_01.js]
 [browser_inspector_addNode_02.js]
 [browser_inspector_addNode_03.js]
 [browser_inspector_breadcrumbs.js]
--- a/devtools/client/inspector/test/head.js
+++ b/devtools/client/inspector/test/head.js
@@ -1,17 +1,17 @@
 /* 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/. */
 /* eslint no-unused-vars: [2, {"vars": "local"}] */
 /* import-globals-from ../../framework/test/shared-head.js */
 /* import-globals-from ../../commandline/test/helpers.js */
 /* import-globals-from ../../shared/test/test-actor-registry.js */
-/* globals registerTestActor, getTestActor */
+/* import-globals-from ../../inspector/test/shared-head.js */
 "use strict";
 
 // Load the shared-head file first.
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
   this);
 
 // Services.prefs.setBoolPref("devtools.debugger.log", true);
@@ -24,16 +24,21 @@ Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/commandline/test/helpers.js",
   this);
 
 // Import helpers registering the test-actor in remote targets
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/shared/test/test-actor-registry.js",
   this);
 
+// Import helpers for the inspector that are also shared with others
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js",
+  this);
+
 DevToolsUtils.testing = true;
 registerCleanupFunction(() => {
   DevToolsUtils.testing = false;
 });
 
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
 });
@@ -117,34 +122,16 @@ var startPicker = Task.async(function* (
  * node
  */
 function selectAndHighlightNode(selector, inspector) {
   info("Highlighting and selecting the node " + selector);
   return selectNode(selector, inspector, "test-highlight");
 }
 
 /**
- * Set the inspector's current selection to the first match of the given css
- * selector
- * @param {String|NodeFront} selector
- * @param {InspectorPanel} inspector The instance of InspectorPanel currently
- * loaded in the toolbox
- * @param {String} reason Defaults to "test" which instructs the inspector not
- * to highlight the node upon selection
- * @return {Promise} Resolves when the inspector is updated with the new node
- */
-var selectNode = Task.async(function* (selector, inspector, reason = "test") {
-  info("Selecting the node for '" + selector + "'");
-  let nodeFront = yield getNodeFront(selector, inspector);
-  let updated = inspector.once("inspector-updated");
-  inspector.selection.setNodeFront(nodeFront, reason);
-  yield updated;
-});
-
-/**
  * Select node for a given selector, make it focusable and set focus in its
  * container element.
  * @param {String|NodeFront} selector
  * @param {InspectorPanel} inspector The current inspector-panel instance.
  * @return {MarkupContainer}
  */
 function* focusNode(selector, inspector) {
   getContainerForNodeFront(inspector.walker.rootNode, inspector).elt.focus();
@@ -177,42 +164,16 @@ function clearCurrentNodeSelection(inspe
  *         with an object: { tab, toolbox, inspector }.
  */
 var openInspectorForURL = Task.async(function* (url, hostType) {
   let tab = yield addTab(url);
   let { inspector, toolbox, testActor } = yield openInspector(hostType);
   return { tab, inspector, toolbox, testActor };
 });
 
-/**
- * Open the toolbox, with the inspector tool visible.
- * @param {String} hostType Optional hostType, as defined in Toolbox.HostType
- * @return a promise that resolves when the inspector is ready
- */
-var openInspector = Task.async(function* (hostType) {
-  info("Opening the inspector");
-
-  let toolbox = yield openToolboxForTab(gBrowser.selectedTab, "inspector",
-                                        hostType);
-  let inspector = toolbox.getPanel("inspector");
-
-  if (inspector._updateProgress) {
-    info("Need to wait for the inspector to update");
-    yield inspector.once("inspector-updated");
-  }
-
-  info("Waiting for actor features to be detected");
-  yield inspector._detectingActorFeatures;
-
-  yield registerTestActor(toolbox.target.client);
-  let testActor = yield getTestActor(toolbox);
-
-  return {toolbox, inspector, testActor};
-});
-
 function getActiveInspector() {
   let target = TargetFactory.forTab(gBrowser.selectedTab);
   return gDevTools.getToolbox(target).getPanel("inspector");
 }
 
 /**
  * Right click on a node in the test page and click on the inspect menu item.
  * @param {TestActor}
@@ -240,113 +201,16 @@ var clickOnInspectMenuItem = Task.async(
   let contextClosed = once(contentAreaContextMenu, "popuphidden");
   contentAreaContextMenu.hidePopup();
   yield contextClosed;
 
   return getActiveInspector();
 });
 
 /**
- * Open the toolbox, with the inspector tool visible, and the one of the sidebar
- * tabs selected.
- *
- * @param {String} id
- *        The ID of the sidebar tab to be opened
- * @return a promise that resolves when the inspector is ready and the tab is
- * visible and ready
- */
-var openInspectorSidebarTab = Task.async(function* (id) {
-  let {toolbox, inspector, testActor} = yield openInspector();
-
-  info("Selecting the " + id + " sidebar");
-  inspector.sidebar.select(id);
-
-  return {
-    toolbox,
-    inspector,
-    testActor
-  };
-});
-
-/**
- * Open the toolbox, with the inspector tool visible, and the rule-view
- * sidebar tab selected.
- *
- * @return a promise that resolves when the inspector is ready and the rule view
- * is visible and ready
- */
-function openRuleView() {
-  return openInspectorSidebarTab("ruleview").then(data => {
-    return {
-      toolbox: data.toolbox,
-      inspector: data.inspector,
-      testActor: data.testActor,
-      view: data.inspector.ruleview.view
-    };
-  });
-}
-
-/**
- * Open the toolbox, with the inspector tool visible, and the computed-view
- * sidebar tab selected.
- *
- * @return a promise that resolves when the inspector is ready and the computed
- * view is visible and ready
- */
-function openComputedView() {
-  return openInspectorSidebarTab("computedview").then(data => {
-    return {
-      toolbox: data.toolbox,
-      inspector: data.inspector,
-      testActor: data.testActor,
-      view: data.inspector.computedview.view
-    };
-  });
-}
-
-/**
- * Select the rule view sidebar tab on an already opened inspector panel.
- *
- * @param {InspectorPanel} inspector
- *        The opened inspector panel
- * @return {CssRuleView} the rule view
- */
-function selectRuleView(inspector) {
-  inspector.sidebar.select("ruleview");
-  return inspector.ruleview.view;
-}
-
-/**
- * Select the computed view sidebar tab on an already opened inspector panel.
- *
- * @param {InspectorPanel} inspector
- *        The opened inspector panel
- * @return {CssComputedView} the computed view
- */
-function selectComputedView(inspector) {
-  inspector.sidebar.select("computedview");
-  return inspector.computedview.view;
-}
-
-/**
- * Get the NodeFront for a node that matches a given css selector, via the
- * protocol.
- * @param {String|NodeFront} selector
- * @param {InspectorPanel} inspector The instance of InspectorPanel currently
- * loaded in the toolbox
- * @return {Promise} Resolves to the NodeFront instance
- */
-function getNodeFront(selector, {walker}) {
-  if (selector._form) {
-    return selector;
-  }
-  return walker.querySelector(walker.rootNode, selector);
-}
-
-/**
  * Get the NodeFront for a node that matches a given css selector inside a
  * given iframe.
  * @param {String|NodeFront} selector
  * @param {String|NodeFront} frameSelector A selector that matches the iframe
  * the node is in
  * @param {InspectorPanel} inspector The instance of InspectorPanel currently
  * loaded in the toolbox
  * @return {Promise} Resolves when the inspector is updated with the new node
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/test/shared-head.js
@@ -0,0 +1,152 @@
+/* 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";
+
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+/* globals registerTestActor, getTestActor, Task, openToolboxForTab, gBrowser */
+
+// This file contains functions related to the inspector that are also of interest to
+// other test directores as well.
+
+/**
+ * Open the toolbox, with the inspector tool visible.
+ * @param {String} hostType Optional hostType, as defined in Toolbox.HostType
+ * @return a promise that resolves when the inspector is ready
+ */
+var openInspector = Task.async(function* (hostType) {
+  info("Opening the inspector");
+
+  let toolbox = yield openToolboxForTab(gBrowser.selectedTab, "inspector",
+                                        hostType);
+  let inspector = toolbox.getPanel("inspector");
+
+  if (inspector._updateProgress) {
+    info("Need to wait for the inspector to update");
+    yield inspector.once("inspector-updated");
+  }
+
+  info("Waiting for actor features to be detected");
+  yield inspector._detectingActorFeatures;
+
+  yield registerTestActor(toolbox.target.client);
+  let testActor = yield getTestActor(toolbox);
+
+  return {toolbox, inspector, testActor};
+});
+
+/**
+ * Open the toolbox, with the inspector tool visible, and the one of the sidebar
+ * tabs selected.
+ *
+ * @param {String} id
+ *        The ID of the sidebar tab to be opened
+ * @return a promise that resolves when the inspector is ready and the tab is
+ * visible and ready
+ */
+var openInspectorSidebarTab = Task.async(function* (id) {
+  let {toolbox, inspector, testActor} = yield openInspector();
+
+  info("Selecting the " + id + " sidebar");
+  inspector.sidebar.select(id);
+
+  return {
+    toolbox,
+    inspector,
+    testActor
+  };
+});
+
+/**
+ * Open the toolbox, with the inspector tool visible, and the rule-view
+ * sidebar tab selected.
+ *
+ * @return a promise that resolves when the inspector is ready and the rule view
+ * is visible and ready
+ */
+function openRuleView() {
+  return openInspectorSidebarTab("ruleview").then(data => {
+    return {
+      toolbox: data.toolbox,
+      inspector: data.inspector,
+      testActor: data.testActor,
+      view: data.inspector.ruleview.view
+    };
+  });
+}
+
+/**
+ * Open the toolbox, with the inspector tool visible, and the computed-view
+ * sidebar tab selected.
+ *
+ * @return a promise that resolves when the inspector is ready and the computed
+ * view is visible and ready
+ */
+function openComputedView() {
+  return openInspectorSidebarTab("computedview").then(data => {
+    return {
+      toolbox: data.toolbox,
+      inspector: data.inspector,
+      testActor: data.testActor,
+      view: data.inspector.computedview.view
+    };
+  });
+}
+
+/**
+ * Select the rule view sidebar tab on an already opened inspector panel.
+ *
+ * @param {InspectorPanel} inspector
+ *        The opened inspector panel
+ * @return {CssRuleView} the rule view
+ */
+function selectRuleView(inspector) {
+  inspector.sidebar.select("ruleview");
+  return inspector.ruleview.view;
+}
+
+/**
+ * Select the computed view sidebar tab on an already opened inspector panel.
+ *
+ * @param {InspectorPanel} inspector
+ *        The opened inspector panel
+ * @return {CssComputedView} the computed view
+ */
+function selectComputedView(inspector) {
+  inspector.sidebar.select("computedview");
+  return inspector.computedview.view;
+}
+
+/**
+ * Get the NodeFront for a node that matches a given css selector, via the
+ * protocol.
+ * @param {String|NodeFront} selector
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @return {Promise} Resolves to the NodeFront instance
+ */
+function getNodeFront(selector, {walker}) {
+  if (selector._form) {
+    return selector;
+  }
+  return walker.querySelector(walker.rootNode, selector);
+}
+
+/**
+ * Set the inspector's current selection to the first match of the given css
+ * selector
+ * @param {String|NodeFront} selector
+ * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+ * loaded in the toolbox
+ * @param {String} reason Defaults to "test" which instructs the inspector not
+ * to highlight the node upon selection
+ * @return {Promise} Resolves when the inspector is updated with the new node
+ */
+var selectNode = Task.async(function* (selector, inspector, reason = "test") {
+  info("Selecting the node for '" + selector + "'");
+  let nodeFront = yield getNodeFront(selector, inspector);
+  let updated = inspector.once("inspector-updated");
+  inspector.selection.setNodeFront(nodeFront, reason);
+  yield updated;
+});
--- a/devtools/client/responsive.html/browser/tunnel.js
+++ b/devtools/client/responsive.html/browser/tunnel.js
@@ -88,22 +88,16 @@ function tunnelToInnerBrowser(outer, inn
       // Replace the outer browser's native messageManager with a message manager tunnel
       // which we can use to route messages of interest to the inner browser instead.
       // Note: The _actual_ messageManager accessible from
       // `browser.frameLoader.messageManager` is not overridable and is left unchanged.
       // Only the XBL getter `browser.messageManager` is overridden.  Browser UI code
       // always uses this getter instead of `browser.frameLoader.messageManager` directly,
       // so this has the effect of overriding the message manager for browser UI code.
       mmTunnel = new MessageManagerTunnel(outer, inner);
-      Object.defineProperty(outer, "messageManager", {
-        value: mmTunnel,
-        writable: false,
-        configurable: true,
-        enumerable: true,
-      });
 
       // We are tunneling to an inner browser with a specific remoteness, so it is simpler
       // for the logic of the browser UI to assume this tab has taken on that remoteness,
       // even though it's not true.  Since the actions the browser UI performs are sent
       // down to the inner browser by this tunnel, the tab's remoteness effectively is the
       // remoteness of the inner browser.
       Object.defineProperty(outer, "isRemoteBrowser", {
         get() {
@@ -206,17 +200,16 @@ function tunnelToInnerBrowser(outer, inn
 
       // Reset the XBL binding back to the default.
       outer.destroy();
       outer.style.MozBinding = "";
 
       // Reset overridden XBL properties and methods.  Deleting the override
       // means it will fallback to the original XBL binding definitions which
       // are on the prototype.
-      delete outer.messageManager;
       delete outer.isRemoteBrowser;
       delete outer.hasContentOpener;
       delete outer.docShellIsActive;
       delete outer.setDocShellIsActiveAndForeground;
 
       mmTunnel.destroy();
       mmTunnel = null;
 
@@ -263,44 +256,51 @@ function copyPermanentKey(outer, inner) 
  * without having to touch the original code paths that use them.
  */
 function MessageManagerTunnel(outer, inner) {
   if (outer.isRemoteBrowser) {
     throw new Error("The outer browser must be non-remote.");
   }
   this.outer = outer;
   this.inner = inner;
+  this.tunneledMessageNames = new Set();
   this.init();
 }
 
 MessageManagerTunnel.prototype = {
 
   /**
    * Most message manager methods are left alone and are just passed along to
-   * the outer browser's real message manager.  `sendAsyncMessage` is only one
-   * with special behavior.
+   * the outer browser's real message manager.
    */
   PASS_THROUGH_METHODS: [
-    "addMessageListener",
-    "loadFrameScript",
     "killChild",
     "assertPermission",
     "assertContainApp",
     "assertAppHasPermission",
     "assertAppHasStatus",
     "removeDelayedFrameScript",
     "getDelayedFrameScripts",
     "loadProcessScript",
     "removeDelayedProcessScript",
     "getDelayedProcessScripts",
-    "removeMessageListener",
     "addWeakMessageListener",
     "removeWeakMessageListener",
   ],
 
+  /**
+   * The following methods are overridden with special behavior while tunneling.
+   */
+  OVERRIDDEN_METHODS: [
+    "loadFrameScript",
+    "addMessageListener",
+    "removeMessageListener",
+    "sendAsyncMessage",
+  ],
+
   OUTER_TO_INNER_MESSAGES: [
     // Messages sent from remote-browser.xml
     "Browser:PurgeSessionHistory",
     "InPermitUnload",
     "PermitUnload",
     // Messages sent from browser.js
     "Browser:Reload",
     // Messages sent from SelectParentHelper.jsm
@@ -333,69 +333,190 @@ MessageManagerTunnel.prototype = {
     "PageVisibility:Hide",
     "PageVisibility:Show",
     // Messages sent to SessionStore.jsm
     "SessionStore:update",
     // Messages sent to BrowserTestUtils.jsm
     "browser-test-utils:loadEvent",
   ],
 
+  OUTER_TO_INNER_MESSAGE_PREFIXES: [
+    // Messages sent from DevTools
+    "debug:",
+  ],
+
+  INNER_TO_OUTER_MESSAGE_PREFIXES: [
+    // Messages sent to DevTools
+    "debug:",
+  ],
+
+  OUTER_TO_INNER_FRAME_SCRIPTS: [
+    // DevTools server for OOP frames
+    "resource://devtools/server/child.js"
+  ],
+
   get outerParentMM() {
+    if (!this.outer.frameLoader) {
+      return null;
+    }
     return this.outer.frameLoader.messageManager;
   },
 
   get outerChildMM() {
     // This is only possible because we require the outer browser to be
     // non-remote, so we're able to reach into its window and use the child
     // side message manager there.
     let docShell = this.outer.frameLoader.docShell;
     return docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIContentFrameMessageManager);
   },
 
   get innerParentMM() {
+    if (!this.inner.frameLoader) {
+      return null;
+    }
     return this.inner.frameLoader.messageManager;
   },
 
+  init() {
+    for (let method of this.PASS_THROUGH_METHODS) {
+      // Workaround bug 449811 to ensure a fresh binding each time through the loop
+      let _method = method;
+      this[_method] = (...args) => {
+        if (!this.outerParentMM) {
+          return null;
+        }
+        return this.outerParentMM[_method](...args);
+      };
+    }
+
+    for (let name of this.INNER_TO_OUTER_MESSAGES) {
+      this.innerParentMM.addMessageListener(name, this);
+      this.tunneledMessageNames.add(name);
+    }
+
+    Services.obs.addObserver(this, "message-manager-close", false);
+
+    // Replace the outer browser's messageManager with this tunnel
+    Object.defineProperty(this.outer, "messageManager", {
+      value: this,
+      writable: false,
+      configurable: true,
+      enumerable: true,
+    });
+  },
+
+  destroy() {
+    if (this.destroyed) {
+      return;
+    }
+    this.destroyed = true;
+    debug("Destroy tunnel");
+
+    Services.obs.removeObserver(this, "message-manager-close");
+
+    // Reset the messageManager.  Deleting the override means it will fallback to the
+    // original XBL binding definitions which are on the prototype.
+    delete this.outer.messageManager;
+
+    for (let name of this.tunneledMessageNames) {
+      this.innerParentMM.removeMessageListener(name, this);
+    }
+
+    // Some objects may have cached this tunnel as the messageManager for a frame.  To
+    // ensure it keeps working after tunnel close, rewrite the overidden methods as pass
+    // through methods.
+    for (let method of this.OVERRIDDEN_METHODS) {
+      // Workaround bug 449811 to ensure a fresh binding each time through the loop
+      let _method = method;
+      this[_method] = (...args) => {
+        if (!this.outerParentMM) {
+          return null;
+        }
+        return this.outerParentMM[_method](...args);
+      };
+    }
+  },
+
+  observe(subject, topic, data) {
+    if (topic != "message-manager-close") {
+      return;
+    }
+    if (subject == this.innerParentMM) {
+      debug("Inner messageManager has closed");
+      this.destroy();
+    }
+  },
+
+  loadFrameScript(url, ...args) {
+    debug(`Calling loadFrameScript for ${url}`);
+
+    if (!this.OUTER_TO_INNER_FRAME_SCRIPTS.includes(url)) {
+      debug(`Should load ${url} into inner?`);
+      this.outerParentMM.loadFrameScript(url, ...args);
+      return;
+    }
+
+    debug(`Load ${url} into inner`);
+    this.innerParentMM.loadFrameScript(url, ...args);
+  },
+
+  addMessageListener(name, ...args) {
+    debug(`Calling addMessageListener for ${name}`);
+
+    debug(`Add outer listener for ${name}`);
+    // Add an outer listener, just like a simple pass through
+    this.outerParentMM.addMessageListener(name, ...args);
+
+    // If the message name is part of a prefix we're tunneling, we also need to add the
+    // tunnel as an inner listener.
+    if (this.INNER_TO_OUTER_MESSAGE_PREFIXES.some(prefix => name.startsWith(prefix))) {
+      debug(`Add inner listener for ${name}`);
+      this.innerParentMM.addMessageListener(name, this);
+      this.tunneledMessageNames.add(name);
+    }
+  },
+
+  removeMessageListener(name, ...args) {
+    debug(`Calling removeMessageListener for ${name}`);
+
+    debug(`Remove outer listener for ${name}`);
+    // Remove an outer listener, just like a simple pass through
+    this.outerParentMM.removeMessageListener(name, ...args);
+
+    // Leave the tunnel as an inner listener for the case of prefix messages to avoid
+    // tracking counts of add calls.  The inner listener will get removed on destroy.
+  },
+
   sendAsyncMessage(name, ...args) {
     debug(`Calling sendAsyncMessage for ${name}`);
 
-    if (!this.OUTER_TO_INNER_MESSAGES.includes(name)) {
+    if (!this._shouldTunnelOuterToInner(name)) {
       debug(`Should ${name} go to inner?`);
       this.outerParentMM.sendAsyncMessage(name, ...args);
       return;
     }
 
     debug(`${name} outer -> inner`);
     this.innerParentMM.sendAsyncMessage(name, ...args);
   },
 
-  init() {
-    for (let method of this.PASS_THROUGH_METHODS) {
-      // Workaround bug 449811 to ensure a fresh binding each time through the loop
-      let _method = method;
-      this[_method] = (...args) => {
-        return this.outerParentMM[_method](...args);
-      };
-    }
-
-    for (let message of this.INNER_TO_OUTER_MESSAGES) {
-      this.innerParentMM.addMessageListener(message, this);
-    }
-  },
-
-  destroy() {
-    for (let message of this.INNER_TO_OUTER_MESSAGES) {
-      this.innerParentMM.removeMessageListener(message, this);
-    }
-  },
-
   receiveMessage({ name, data, objects, principal }) {
-    if (!this.INNER_TO_OUTER_MESSAGES.includes(name)) {
+    if (!this._shouldTunnelInnerToOuter(name)) {
       debug(`Received unexpected message ${name}`);
       return;
     }
 
     debug(`${name} inner -> outer`);
     this.outerChildMM.sendAsyncMessage(name, data, objects, principal);
   },
 
+  _shouldTunnelOuterToInner(name) {
+    return this.OUTER_TO_INNER_MESSAGES.includes(name) ||
+           this.OUTER_TO_INNER_MESSAGE_PREFIXES.some(prefix => name.startsWith(prefix));
+  },
+
+  _shouldTunnelInnerToOuter(name) {
+    return this.INNER_TO_OUTER_MESSAGES.includes(name) ||
+           this.INNER_TO_OUTER_MESSAGE_PREFIXES.some(prefix => name.startsWith(prefix));
+  },
+
 };
--- a/devtools/client/responsive.html/test/browser/browser.ini
+++ b/devtools/client/responsive.html/test/browser/browser.ini
@@ -5,26 +5,30 @@ subsuite = devtools
 skip-if = !e10s
 support-files =
   devices.json
   doc_page_state.html
   head.js
   !/devtools/client/commandline/test/helpers.js
   !/devtools/client/framework/test/shared-head.js
   !/devtools/client/framework/test/shared-redux-head.js
+  !/devtools/client/inspector/test/shared-head.js
+  !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_device_modal_error.js]
 [browser_device_modal_exit.js]
 [browser_device_modal_submit.js]
 [browser_device_width.js]
 [browser_exit_button.js]
 [browser_frame_script_active.js]
 [browser_menu_item_01.js]
 [browser_menu_item_02.js]
 [browser_mouse_resize.js]
 [browser_navigation.js]
 [browser_page_state.js]
 [browser_resize_cmd.js]
-skip-if = true # GCLI target confused after swap, will fix in bug 1240907
+skip-if = true # GCLI target confused after swap, will fix in bug 1240912
 [browser_screenshot_button.js]
 [browser_shutdown_close_sync.js]
+[browser_toolbox_computed_view.js]
+[browser_toolbox_rule_view.js]
 [browser_touch_simulation.js]
 [browser_viewport_basics.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/test/browser/browser_toolbox_computed_view.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Check that when the viewport is resized, the computed-view refreshes.
+
+const TEST_URI = "data:text/html;charset=utf-8,<html><style>" +
+                 "div {" +
+                 "  width: 500px;" +
+                 "  height: 10px;" +
+                 "  background: purple;" +
+                 "} " +
+                 "@media screen and (max-width: 200px) {" +
+                 "  div { " +
+                 "    width: 100px;" +
+                 "  }" +
+                 "};" +
+                 "</style><div></div></html>";
+
+addRDMTask(TEST_URI, function* ({ ui, manager }) {
+  info("Open the responsive design mode and set its size to 500x500 to start");
+  yield setViewportSize(ui, manager, 500, 500);
+
+  info("Open the inspector, computed-view and select the test node");
+  let { inspector, view } = yield openComputedView();
+  yield selectNode("div", inspector);
+
+  info("Try shrinking the viewport and checking the applied styles");
+  yield testShrink(view, inspector, ui, manager);
+
+  info("Try growing the viewport and checking the applied styles");
+  yield testGrow(view, inspector, ui, manager);
+
+  yield closeToolbox();
+});
+
+function* testShrink(computedView, inspector, ui, manager) {
+  is(computedWidth(computedView), "500px", "Should show 500px initially.");
+
+  let onRefresh = inspector.once("computed-view-refreshed");
+  yield setViewportSize(ui, manager, 100, 100);
+  yield onRefresh;
+
+  is(computedWidth(computedView), "100px", "Should be 100px after shrinking.");
+}
+
+function* testGrow(computedView, inspector, ui, manager) {
+  let onRefresh = inspector.once("computed-view-refreshed");
+  yield setViewportSize(ui, manager, 500, 500);
+  yield onRefresh;
+
+  is(computedWidth(computedView), "500px", "Should be 500px after growing.");
+}
+
+function computedWidth(computedView) {
+  for (let prop of computedView.propertyViews) {
+    if (prop.name === "width") {
+      return prop.valueNode.textContent;
+    }
+  }
+  return null;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/test/browser/browser_toolbox_rule_view.js
@@ -0,0 +1,78 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Check that when the viewport is resized, the rule-view refreshes.
+
+const TEST_URI = "data:text/html;charset=utf-8,<html><style>" +
+                 "div {" +
+                 "  width: 500px;" +
+                 "  height: 10px;" +
+                 "  background: purple;" +
+                 "} " +
+                 "@media screen and (max-width: 200px) {" +
+                 "  div { " +
+                 "    width: 100px;" +
+                 "  }" +
+                 "};" +
+                 "</style><div></div></html>";
+
+registerCleanupFunction(() => {
+  Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
+});
+
+addRDMTask(TEST_URI, function* ({ ui, manager }) {
+  info("Open the responsive design mode and set its size to 500x500 to start");
+  yield setViewportSize(ui, manager, 500, 500);
+
+  info("Open the inspector, rule-view and select the test node");
+  let { inspector, view } = yield openRuleView();
+  yield selectNode("div", inspector);
+
+  info("Try shrinking the viewport and checking the applied styles");
+  yield testShrink(view, ui, manager);
+
+  info("Try growing the viewport and checking the applied styles");
+  yield testGrow(view, ui, manager);
+
+  info("Check that ESC still opens the split console");
+  yield testEscapeOpensSplitConsole(inspector);
+
+  yield closeToolbox();
+});
+
+function* testShrink(ruleView, ui, manager) {
+  is(numberOfRules(ruleView), 2, "Should have two rules initially.");
+
+  info("Resize to 100x100 and wait for the rule-view to update");
+  let onRefresh = ruleView.once("ruleview-refreshed");
+  yield setViewportSize(ui, manager, 100, 100);
+  yield onRefresh;
+
+  is(numberOfRules(ruleView), 3, "Should have three rules after shrinking.");
+}
+
+function* testGrow(ruleView, ui, manager) {
+  info("Resize to 500x500 and wait for the rule-view to update");
+  let onRefresh = ruleView.once("ruleview-refreshed");
+  yield setViewportSize(ui, manager, 500, 500);
+  yield onRefresh;
+
+  is(numberOfRules(ruleView), 2, "Should have two rules after growing.");
+}
+
+function* testEscapeOpensSplitConsole(inspector) {
+  ok(!inspector._toolbox._splitConsole, "Console is not split.");
+
+  info("Press escape");
+  let onSplit = inspector._toolbox.once("split-console");
+  EventUtils.synthesizeKey("VK_ESCAPE", {});
+  yield onSplit;
+
+  ok(inspector._toolbox._splitConsole, "Console is split after pressing ESC.");
+}
+
+function numberOfRules(ruleView) {
+  return ruleView.element.querySelectorAll(".ruleview-code").length;
+}
--- a/devtools/client/responsive.html/test/browser/head.js
+++ b/devtools/client/responsive.html/test/browser/head.js
@@ -2,29 +2,40 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 /* eslint no-unused-vars: [2, {"vars": "local"}] */
 /* import-globals-from ../../../framework/test/shared-head.js */
 /* import-globals-from ../../../framework/test/shared-redux-head.js */
 /* import-globals-from ../../../commandline/test/helpers.js */
+/* import-globals-from ../../../inspector/test/shared-head.js */
 
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
   this);
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/framework/test/shared-redux-head.js",
   this);
 
 // Import the GCLI test helper
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/commandline/test/helpers.js",
   this);
 
+// Import helpers registering the test-actor in remote targets
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/shared/test/test-actor-registry.js",
+  this);
+
+// Import helpers for the inspector that are also shared with others
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js",
+  this);
+
 const TEST_URI_ROOT = "http://example.com/browser/devtools/client/responsive.html/test/browser/";
 
 SimpleTest.requestCompleteLog();
 SimpleTest.waitForExplicitFinish();
 
 DevToolsUtils.testing = true;
 Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
 Services.prefs.setCharPref("devtools.devices.url",
--- a/devtools/client/styleeditor/test/browser.ini
+++ b/devtools/client/styleeditor/test/browser.ini
@@ -49,16 +49,17 @@ support-files =
   doc_uncached.css
   doc_uncached.html
   doc_xulpage.xul
   sync.html
   !/devtools/client/commandline/test/helpers.js
   !/devtools/client/framework/test/shared-head.js
   !/devtools/client/inspector/shared/test/head.js
   !/devtools/client/inspector/test/head.js
+  !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/test-actor-registry.js
   !/devtools/client/shared/test/test-actor.js
 
 [browser_styleeditor_autocomplete.js]
 [browser_styleeditor_autocomplete-disabled.js]
 [browser_styleeditor_bug_740541_iframes.js]
 [browser_styleeditor_bug_851132_middle_click.js]
 [browser_styleeditor_bug_870339.js]
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -2244,18 +2244,20 @@ RemoteBrowserTabActor.prototype = {
       this._conn, this._browser, onDestroy);
     return connect.then(form => {
       this._form = form;
       return this;
     });
   },
 
   get _mm() {
-    return this._browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader
-           .messageManager;
+    // Get messageManager from XUL browser (which might be a specialized tunnel for RDM)
+    // or else fallback to asking the frameLoader itself.
+    return this._browser.messageManager ||
+           this._browser.frameLoader.messageManager;
   },
 
   update() {
     // If the child happens to be crashed/close/detach, it won't have _form set,
     // so only request form update if some code is still listening on the other
     // side.
     if (this._form) {
       let deferred = promise.defer();
--- a/devtools/server/main.js
+++ b/devtools/server/main.js
@@ -977,17 +977,19 @@ var DebuggerServer = {
    *        related TabActor)
    * @return object
    *         A promise object that is resolved once the connection is
    *         established.
    */
   connectToChild(connection, frame, onDestroy) {
     let deferred = SyncPromise.defer();
 
-    let mm = frame.frameLoader.messageManager;
+    // Get messageManager from XUL browser (which might be a specialized tunnel for RDM)
+    // or else fallback to asking the frameLoader itself.
+    let mm = frame.messageManager || frame.frameLoader.messageManager;
     mm.loadFrameScript("resource://devtools/server/child.js", false);
     this._childMessageManagers.add(mm);
 
     let actor, childTransport;
     let prefix = connection.allocID("child");
     let netMonitor = null;
 
     // provides hook to actor modules that need to exchange messages
--- a/devtools/shared/transport/transport.js
+++ b/devtools/shared/transport/transport.js
@@ -17,17 +17,17 @@
     factory.call(this, require, this);
   } else {
     // Cu.import
     const Cu = Components.utils;
     const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
     factory.call(this, require, this);
   }
 }).call(this, function (require, exports) {
-  const { Cc, Ci, Cr, CC } = require("chrome");
+  const { Cc, Cr, CC } = require("chrome");
   const DevToolsUtils = require("devtools/shared/DevToolsUtils");
   const { dumpn, dumpv } = DevToolsUtils;
   const StreamUtils = require("devtools/shared/transport/stream-utils");
   const { Packet, JSONPacket, BulkPacket } =
   require("devtools/shared/transport/packets");
   const promise = require("promise");
   const defer = require("devtools/shared/defer");
   const EventEmitter = require("devtools/shared/event-emitter");
@@ -709,17 +709,17 @@
    * multiple servers running in the same child process.
    *
    * This transport exchanges messages named 'debug:<prefix>:packet', where
    * <prefix> is |prefix|, whose data is the protocol packet.
    */
   function ChildDebuggerTransport(sender, prefix) {
     EventEmitter.decorate(this);
 
-    this._sender = sender.QueryInterface(Ci.nsIMessageSender);
+    this._sender = sender;
     this._messageName = "debug:" + prefix + ":packet";
   }
 
   /*
    * To avoid confusion, we use 'message' to mean something that
    * nsIMessageSender conveys, and 'packet' to mean a remote debugging
    * protocol packet.
    */
--- a/devtools/shared/webconsole/network-monitor.js
+++ b/devtools/shared/webconsole/network-monitor.js
@@ -76,18 +76,35 @@ function matchRequest(channel, filters) 
         break;
       }
       win = win.parent;
     }
   }
 
   if (filters.topFrame) {
     let topFrame = NetworkHelper.getTopFrameForRequest(channel);
-    if (topFrame && topFrame === filters.topFrame) {
-      return true;
+    while (topFrame) {
+      // In the normal case, a topFrame filter should match the request's topFrame if it
+      // will match at all.
+      if (topFrame === filters.topFrame) {
+        return true;
+      }
+      // As a stop gap approach for RDM, where `filters.topFrame` will be the
+      // <xul:browser> frame for an entire tab and the request's `topFrame` is the
+      // <iframe mozbrower> that triggered the request, we try to climb up parent frames
+      // above the request's `topFrame` to see if they might also match the filter.
+      // In bug 1240912, we want to rework this, since we don't really want to be passing
+      // a frame down to the network monitor.
+      if (!topFrame.ownerGlobal) {
+        break;
+      }
+      topFrame = topFrame.ownerGlobal
+                         .QueryInterface(Ci.nsIInterfaceRequestor)
+                         .getInterface(Ci.nsIDOMWindowUtils)
+                         .containerElement;
     }
   }
 
   if (filters.appId) {
     let appId = NetworkHelper.getAppIdForRequest(channel);
     if (appId && appId == filters.appId) {
       return true;
     }
@@ -1578,18 +1595,19 @@ NetworkEventActorProxy.prototype = {
  * @constructor
  * @param nsIDOMElement frame
  *        The browser frame to work with (mozbrowser).
  * @param string id
  *        Instance identifier to use for messages.
  */
 function NetworkMonitorManager(frame, id) {
   this.id = id;
-  let mm = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader
-      .messageManager;
+  // Get messageManager from XUL browser (which might be a specialized tunnel for RDM)
+  // or else fallback to asking the frameLoader itself.
+  let mm = frame.messageManager || frame.frameLoader.messageManager;
   this.messageManager = mm;
   this.frame = frame;
   this.onNetMonitorMessage = this.onNetMonitorMessage.bind(this);
   this.onNetworkEvent = this.onNetworkEvent.bind(this);
 
   mm.addMessageListener("debug:netmonitor:" + id, this.onNetMonitorMessage);
 }
 exports.NetworkMonitorManager = NetworkMonitorManager;