Bug 1404088 - Hide RDM temporary tabs from WebExtensions. r=ochameau draft
authorJ. Ryan Stinnett <jryans@gmail.com>
Fri, 29 Sep 2017 17:14:32 -0500
changeset 674662 668010cfe91117403ef565fea92caada6b5a64a2
parent 672988 b26e85d5944ed32fcf9867f40163f19bb42dddb6
child 734393 049fe02585f674002a90e2fe7f150a5de1853632
push id82903
push userbmo:jryans@gmail.com
push dateWed, 04 Oct 2017 04:43:01 +0000
reviewersochameau
bugs1404088
milestone58.0a1
Bug 1404088 - Hide RDM temporary tabs from WebExtensions. r=ochameau RDM uses temporary tabs to move content around and into the tool's viewport. This triggers events like `TabOpen` and `TabClose` for the temporary tab, trigger unnecessary work, like alerting WebExtensions. Avoid this noise for WebExtensions and others by absorbing these events. Note that the _original_ browser tab is unaffected. This only changes temporary tabs RDM uses during the swapping process. MozReview-Commit-ID: H8kBYBma6i9
devtools/client/responsive.html/browser/swap.js
--- a/devtools/client/responsive.html/browser/swap.js
+++ b/devtools/client/responsive.html/browser/swap.js
@@ -26,48 +26,69 @@ const { tunnelToInnerBrowser } = require
  * @param containerURL
  *        URL to a page that holds an inner browser.
  * @param getInnerBrowser
  *        Function that returns a Promise to the inner browser within the
  *        container page.  It is called with the outer browser that loaded the
  *        container page.
  */
 function swapToInnerBrowser({ tab, containerURL, getInnerBrowser }) {
-  let gBrowser = tab.ownerDocument.defaultView.gBrowser;
+  let browserWindow = tab.ownerGlobal;
+  let gBrowser = browserWindow.gBrowser;
   let innerBrowser;
   let tunnel;
 
   // Dispatch a custom event each time the _viewport content_ is swapped from one browser
   // to another.  DevTools server code uses this to follow the content if there is an
   // active DevTools connection.  While browser.xml does dispatch it's own SwapDocShells
   // event, this one is easier for DevTools to follow because it's only emitted once per
   // transition, instead of twice like SwapDocShells.
   let dispatchDevToolsBrowserSwap = (from, to) => {
-    let CustomEvent = tab.ownerDocument.defaultView.CustomEvent;
+    let CustomEvent = browserWindow.CustomEvent;
     let event = new CustomEvent("DevTools:BrowserSwap", {
       detail: to,
       bubbles: true,
     });
     from.dispatchEvent(event);
   };
 
+  // A version of `gBrowser.addTab` that absorbs the `TabOpen` event.
+  // The swap process uses a temporary tab, and there's no real need for others to hear
+  // about it.  This hides the temporary tab from things like WebExtensions.
+  let addTabSilently = (uri, options) => {
+    browserWindow.addEventListener("TabOpen", event => {
+      event.stopImmediatePropagation();
+    }, { capture: true, once: true });
+    return gBrowser.addTab(uri, options);
+  };
+
+  // A version of `gBrowser.swapBrowsersAndCloseOther` that absorbs the `TabClose` event.
+  // The swap process uses a temporary tab, and there's no real need for others to hear
+  // about it.  This hides the temporary tab from things like WebExtensions.
+  let swapBrowsersAndCloseOtherSilently = (ourTab, otherTab) => {
+    browserWindow.addEventListener("TabClose", event => {
+      event.stopImmediatePropagation();
+    }, { capture: true, once: true });
+    gBrowser.swapBrowsersAndCloseOther(ourTab, otherTab);
+  };
+
   return {
 
     start: Task.async(function* () {
       tab.isResponsiveDesignMode = true;
 
       // Hide the browser content temporarily while things move around to avoid displaying
       // strange intermediate states.
       tab.linkedBrowser.style.visibility = "hidden";
 
       // 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("about:blank", {
+      let containerTab = addTabSilently("about:blank", {
         skipAnimation: true,
         forceNotRemote: true,
       });
       gBrowser.hideTab(containerTab);
       let containerBrowser = containerTab.linkedBrowser;
       // Even though we load the `containerURL` with `LOAD_FLAGS_BYPASS_HISTORY` below,
       // `SessionHistory.jsm` has a fallback path for tabs with no history which
       // fabricates a history entry by reading the current URL, and this can cause the
@@ -123,17 +144,17 @@ function swapToInnerBrowser({ tab, conta
       // 5. Force the original browser tab to be non-remote since the tool UI
       //    must be loaded in the parent process, and we're about to swap the
       //    tool UI into this tab.
       gBrowser.updateBrowserRemoteness(tab.linkedBrowser, false);
 
       // 6. Swap the tool UI (with viewport showing the content) into the
       //    original browser tab and close the temporary tab used to load the
       //    tool via `swapBrowsersAndCloseOther`.
-      gBrowser.swapBrowsersAndCloseOther(tab, containerTab);
+      swapBrowsersAndCloseOtherSilently(tab, containerTab);
 
       // 7. Start a tunnel from the tool tab's browser to the viewport browser
       //    so that some browser UI functions, like navigation, are connected to
       //    the content in the viewport, instead of the tool page.
       tunnel = tunnelToInnerBrowser(tab.linkedBrowser, innerBrowser);
       yield tunnel.start();
 
       // Swapping browsers disconnects the find bar UI from the browser.
@@ -161,17 +182,17 @@ function swapToInnerBrowser({ tab, conta
       // strange intermediate states.
       tab.linkedBrowser.style.visibility = "hidden";
 
       // 1. Stop the tunnel between outer and inner browsers.
       tunnel.stop();
       tunnel = null;
 
       // 2. Create a temporary, hidden tab to hold the content.
-      let contentTab = gBrowser.addTab("about:blank", {
+      let contentTab = addTabSilently("about:blank", {
         skipAnimation: true,
       });
       gBrowser.hideTab(contentTab);
       let contentBrowser = contentTab.linkedBrowser;
 
       // 3. Mark the content tab browser's docshell as active so the frame
       //    is created eagerly and will be ready to swap.
       contentBrowser.docShellIsActive = true;
@@ -194,30 +215,31 @@ function swapToInnerBrowser({ tab, conta
       gBrowser.updateBrowserRemoteness(tab.linkedBrowser, true, {
         remoteType: contentBrowser.remoteType,
       });
 
       // 6. Swap the content into the original browser tab and close the
       //    temporary tab used to hold the content via
       //    `swapBrowsersAndCloseOther`.
       dispatchDevToolsBrowserSwap(contentBrowser, tab.linkedBrowser);
-      gBrowser.swapBrowsersAndCloseOther(tab, contentTab);
+      swapBrowsersAndCloseOtherSilently(tab, contentTab);
 
       // Swapping browsers disconnects the find bar UI from the browser.
       // If the find bar has been initialized, reconnect it.
       if (gBrowser.isFindBarInitialized(tab)) {
         let findBar = gBrowser.getFindBar(tab);
         findBar.browser = tab.linkedBrowser;
         if (!findBar.hidden) {
           // Force the find bar to activate again, restoring the search string.
           findBar.onFindCommand();
         }
       }
 
       gBrowser = null;
+      browserWindow = 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.isResponsiveDesignMode;