--- 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;