Bug 1068400 - Restore toolbox when switching from in-parent-process to OOP tab. r=jryans draft
authorAlexandre Poirot <poirot.alex@gmail.com>
Mon, 24 Oct 2016 07:37:23 -0700
changeset 432523 0f3e4d365dc2a7ffa6e04ff9fba733156cc5f838
parent 432230 3e73fd638e687a4d7f46613586e5156b8e2af846
child 535685 d76e7fccc59483fab6809a9fa8baf6a71bb10ee0
push id34349
push userbmo:poirot.alex@gmail.com
push dateWed, 02 Nov 2016 09:21:58 +0000
reviewersjryans
bugs1068400
milestone52.0a1
Bug 1068400 - Restore toolbox when switching from in-parent-process to OOP tab. r=jryans MozReview-Commit-ID: K51gp7PavC7
devtools/client/framework/target.js
devtools/client/framework/test/browser.ini
devtools/client/framework/test/browser_toolbox_remoteness_change.js
devtools/client/responsive.html/browser/swap.js
--- a/devtools/client/framework/target.js
+++ b/devtools/client/framework/target.js
@@ -9,24 +9,26 @@ const promise = require("promise");
 const defer = require("devtools/shared/defer");
 const EventEmitter = require("devtools/shared/event-emitter");
 const Services = require("Services");
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
 loader.lazyRequireGetter(this, "DebuggerClient",
   "devtools/shared/client/main", true);
+loader.lazyRequireGetter(this, "gDevTools",
+  "devtools/client/framework/devtools", true);
 
 const targets = new WeakMap();
 const promiseTargets = new WeakMap();
 
 /**
  * Functions for creating Targets
  */
-exports.TargetFactory = {
+const TargetFactory = exports.TargetFactory = {
   /**
    * Construct a Target
    * @param {XULTab} tab
    *        The tab to use in creating a new target.
    *
    * @return A target object
    */
   forTab: function (tab) {
@@ -449,29 +451,31 @@ TabTarget.prototype = {
    * Listen to the different events.
    */
   _setupListeners: function () {
     this._webProgressListener = new TabWebProgressListener(this);
     this.tab.linkedBrowser.addProgressListener(this._webProgressListener);
     this.tab.addEventListener("TabClose", this);
     this.tab.parentNode.addEventListener("TabSelect", this);
     this.tab.ownerDocument.defaultView.addEventListener("unload", this);
+    this.tab.addEventListener("TabRemotenessChange", this);
   },
 
   /**
    * Teardown event listeners.
    */
   _teardownListeners: function () {
     if (this._webProgressListener) {
       this._webProgressListener.destroy();
     }
 
     this._tab.ownerDocument.defaultView.removeEventListener("unload", this);
     this._tab.removeEventListener("TabClose", this);
     this._tab.parentNode.removeEventListener("TabSelect", this);
+    this._tab.removeEventListener("TabRemotenessChange", this);
   },
 
   /**
    * Setup listeners for remote debugging, updating existing ones as necessary.
    */
   _setupRemoteListeners: function () {
     this.client.addListener("closed", this.destroy);
 
@@ -543,19 +547,48 @@ TabTarget.prototype = {
         break;
       case "TabSelect":
         if (this.tab.selected) {
           this.emit("visible", event);
         } else {
           this.emit("hidden", event);
         }
         break;
+      case "TabRemotenessChange":
+        this.onRemotenessChange();
+        break;
     }
   },
 
+  // Automatically respawn the toolbox when the tab changes between being
+  // loaded within the parent process and loaded from a content process.
+  // Process change can go in both ways.
+  onRemotenessChange: function () {
+    // Responsive design do a crazy dance around tabs and triggers
+    // remotenesschange events. But we should ignore them as at the end
+    // the content doesn't change its remoteness.
+    if (this._tab.isReponsiveDesignMode) {
+      return;
+    }
+
+    // Save a reference to the tab as it will be nullified on destroy
+    let tab = this._tab;
+    let onToolboxDestroyed = (event, target) => {
+      if (target != this) {
+        return;
+      }
+      gDevTools.off("toolbox-destroyed", target);
+
+      // Recreate a fresh target instance as the current one is now destroyed
+      let newTarget = TargetFactory.forTab(tab);
+      gDevTools.showToolbox(newTarget);
+    };
+    gDevTools.on("toolbox-destroyed", onToolboxDestroyed);
+  },
+
   /**
    * Target is not alive anymore.
    */
   destroy: function () {
     // If several things call destroy then we give them all the same
     // destruction promise so we're sure to destroy only once
     if (this._destroyer) {
       return this._destroyer.promise;
--- a/devtools/client/framework/test/browser.ini
+++ b/devtools/client/framework/test/browser.ini
@@ -56,16 +56,18 @@ skip-if = true # Bug 1177463 - Temporari
 [browser_toolbox_options_disable_buttons.js]
 [browser_toolbox_options_disable_cache-01.js]
 [browser_toolbox_options_disable_cache-02.js]
 [browser_toolbox_options_disable_js.js]
 [browser_toolbox_options_enable_serviceworkers_testing.js]
 # [browser_toolbox_raise.js] # Bug 962258
 # skip-if = os == "win"
 [browser_toolbox_ready.js]
+[browser_toolbox_remoteness_change.js]
+run-if = e10s
 [browser_toolbox_select_event.js]
 skip-if = e10s # Bug 1069044 - destroyInspector may hang during shutdown
 [browser_toolbox_selected_tool_unavailable.js]
 [browser_toolbox_sidebar.js]
 [browser_toolbox_sidebar_events.js]
 [browser_toolbox_sidebar_existing_tabs.js]
 [browser_toolbox_sidebar_overflow_menu.js]
 [browser_toolbox_split_console.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/framework/test/browser_toolbox_remoteness_change.js
@@ -0,0 +1,42 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var {Toolbox} = require("devtools/client/framework/toolbox");
+
+const URL_1 = "about:robots";
+const URL_2 = "data:text/html;charset=UTF-8," +
+  encodeURIComponent("<div id=\"remote-page\">foo</div>");
+
+add_task(function* () {
+  info("Open a tab on a URL supporting only running in parent process");
+  let tab = yield addTab(URL_1);
+  is(tab.linkedBrowser.currentURI.spec, URL_1, "We really are on the expected document");
+  is(tab.linkedBrowser.getAttribute("remote"), "", "And running in parent process");
+
+  let toolbox = yield openToolboxForTab(tab);
+
+  let onToolboxDestroyed = toolbox.once("destroyed");
+  let onToolboxCreated = gDevTools.once("toolbox-created");
+
+  info("Navigate to a URL supporting remote process");
+  let onLoaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+  gBrowser.loadURI(URL_2);
+  yield onLoaded;
+
+  is(tab.linkedBrowser.getAttribute("remote"), "true", "Navigated to a data: URI and switching to remote");
+
+  info("Waiting for the toolbox to be destroyed");
+  yield onToolboxDestroyed;
+
+  info("Waiting for a new toolbox to be created");
+  toolbox = yield onToolboxCreated;
+
+  info("Waiting for the new toolbox to be ready");
+  yield toolbox.once("ready");
+
+  info("Veryify we are inspecting the new document");
+  let console = yield toolbox.selectTool("webconsole");
+  let { jsterm } = console.hud;
+  let url = yield jsterm.execute("document.location.href");
+  is(url.textContent, URL_2, "The console inspects the second document");
+});
--- a/devtools/client/responsive.html/browser/swap.js
+++ b/devtools/client/responsive.html/browser/swap.js
@@ -47,16 +47,18 @@ function swapToInnerBrowser({ tab, conta
       bubbles: true,
     });
     from.dispatchEvent(event);
   };
 
   return {
 
     start: Task.async(function* () {
+      tab.isReponsiveDesignMode = true;
+
       // Freeze navigation temporarily to avoid "blinking" in the location bar.
       freezeNavigationState(tab);
 
       // 1. Create a temporary, hidden tab to load the tool UI.
       let containerTab = gBrowser.addTab(containerURL, {
         skipAnimation: true,
       });
       gBrowser.hideTab(containerTab);
@@ -145,16 +147,18 @@ function swapToInnerBrowser({ tab, conta
       dispatchDevToolsBrowserSwap(contentBrowser, tab.linkedBrowser);
       gBrowser.swapBrowsersAndCloseOther(tab, contentTab);
       gBrowser = null;
 
       // The focus manager seems to get a little dizzy after all this swapping.  If a
       // content element had been focused inside the viewport before stopping, it will
       // have lost focus.  Activate the frame to restore expected focus.
       tab.linkedBrowser.frameLoader.activateRemoteFrame();
+
+      delete tab.isReponsiveDesignMode;
     },
 
   };
 }
 
 /**
  * Browser navigation properties we'll freeze temporarily to avoid "blinking" in the
  * location bar, etc. caused by the containerURL peeking through before the swap is