Bug 1562821 - Mirror state from the <xul:browser> to the <iframe mozbrowser> when in RDM. r=ochameau, a=RyanVM
authorBarret Rennie <barret@brennie.ca>
Wed, 24 Jul 2019 17:00:16 +0000
changeset 541619 35e41c495b3cf7e7720d1eb41d87587ccf5b42ef
parent 541618 7374e601e0b6624451c0bec4c64a036aaddbba11
child 541620 b94ef678a4be6cf724bbe4e2a33c1af8e264e811
push id11652
push userryanvm@gmail.com
push dateMon, 29 Jul 2019 18:53:24 +0000
treeherdermozilla-beta@b94ef678a4be [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersochameau, RyanVM
bugs1562821, 1510569
milestone69.0
Bug 1562821 - Mirror state from the <xul:browser> to the <iframe mozbrowser> when in RDM. r=ochameau, a=RyanVM As part of bug 1510569, the majority of the `nsIWebProgress` event listeners have moved from the `WebProgressChild`/`RemoteWebProgress` to the `BrowserChild`/`BrowserParent`. In responsive design mode, the `RemoteWebProgress` previously would update the state of the `<iframe mozbrowser>` with document URI and title which would be mirrored back to the `<xul:browser>` when leaving RDM. However, the event handlers in the `BrowserParent` call directly into the `<xul:browser>`, skipping the `<iframe mozbrowser>` entirely. Therefore, when RDM is shut down, old state will be mirroed to the `<xul:browser>`, leaving it in an inconsistent state. We now mirror the state from the `<xul:browser>` to the `<iframe mozbrowser>` with an `nsIWebProgressListener` so that the `<iframe mozbrowser>` will not clobber the `<xul:browser>`'s state when leaving RDM. Differential Revision: https://phabricator.services.mozilla.com/D38918
devtools/client/responsive.html/browser/tunnel.js
--- a/devtools/client/responsive.html/browser/tunnel.js
+++ b/devtools/client/responsive.html/browser/tunnel.js
@@ -1,15 +1,16 @@
 /* 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 { Ci, Cu } = require("chrome");
+const ChromeUtils = require("ChromeUtils");
 const Services = require("Services");
 const { BrowserElementWebNavigation } = require("./web-navigation");
 const { getStack } = require("devtools/shared/platform/stack");
 
 // A symbol used to hold onto the frame loader from the outer browser while tunneling.
 const FRAME_LOADER = Symbol("devtools/responsive/frame-loader");
 // Export for use in tests.
 exports.OUTER_FRAME_LOADER_SYMBOL = FRAME_LOADER;
@@ -84,16 +85,47 @@ const PROPERTIES_FROM_BROWSER_WINDOW = [
  *        A <iframe mozbrowser> containing page content to be wired up to the
  *        primary browser UI via the outer browser.
  */
 function tunnelToInnerBrowser(outer, inner) {
   let browserWindow = outer.ownerDocument.defaultView;
   let gBrowser = browserWindow.gBrowser;
   let mmTunnel;
 
+  // Mirror the state updates from the outer <xul:browser> to the inner
+  // <iframe mozbrowser>.
+  const mirroringProgressListener = {
+    onStateChange: (webProgress, request, stateFlags, status) => {
+      if (webProgress && webProgress.isTopLevel) {
+        inner._characterSet = outer._characterSet;
+        inner._documentURI = outer._documentURI;
+        inner._documentContentType = outer._documentContentType;
+      }
+    },
+
+    onLocationChange: (webProgress, request, location, flags) => {
+      if (webProgress && webProgress.isTopLevel) {
+        inner._securityUI = outer._securityUI;
+        inner._documentURI = outer._documentURI;
+        inner._documentContentType = outer._documentContentType;
+        inner._contentTitle = outer._contentTitle;
+        inner._characterSet = outer._characterSet;
+        inner._contentPrincipal = outer._contentPrincipal;
+        inner._imageDocument = outer._imageDocument;
+        inner._isSyntheticDocument = outer._isSyntheticDocument;
+        inner._innerWindowID = outer._innerWindowID;
+      }
+    },
+
+    QueryInterface: ChromeUtils.generateQI([
+      Ci.nsISupportsWeakReference,
+      Ci.nsIWebProgressListener,
+    ]),
+  };
+
   return {
 
     async start() {
       if (outer.isRemoteBrowser) {
         throw new Error("The outer browser must be non-remote.");
       }
       if (!inner.isRemoteBrowser) {
         throw new Error("The inner browser must be remote.");
@@ -203,16 +235,20 @@ function tunnelToInnerBrowser(outer, inn
       // when it creates a new browser, etc.  Since we manually changed the mode
       // above, it caused a fresh webProgress object to be created which does not have any
       // listeners added.  So, we get the listener that gBrowser is using for the tab and
       // reattach it here.
       const tab = gBrowser.getTabForBrowser(outer);
       const filteredProgressListener = gBrowser._tabFilters.get(tab);
       outer.webProgress.addProgressListener(filteredProgressListener,
                                             Ci.nsIWebProgress.NOTIFY_ALL);
+      outer.webProgress.addProgressListener(
+        mirroringProgressListener,
+        Ci.nsIWebProgress.NOTIFY_STATE_ALL | Ci.nsIWebProgress.NOTIFY_LOCATION
+      );
 
       // Add the inner browser to tabbrowser's WeakMap from browser to tab.  This assists
       // with tabbrowser's processing of some events such as MozLayerTreeReady which
       // bubble up from the remote content frame and trigger tabbrowser to lookup the tab
       // associated with the browser that triggered the event.
       gBrowser._tabForBrowser.set(inner, tab);
 
       // All of the browser state from content was swapped onto the inner browser.  Pull
@@ -308,16 +344,17 @@ function tunnelToInnerBrowser(outer, inn
         inner[property] = outer[property];
       }
 
       // Remove the inner browser from the WeakMap from browser to tab.
       gBrowser._tabForBrowser.delete(inner);
 
       // Remove the progress listener we added manually.
       outer.webProgress.removeProgressListener(filteredProgressListener);
+      outer.webProgress.removeProgressListener(mirroringProgressListener);
 
       // Reset the Custom Element back to the original state.
       outer.destroy();
 
       // Reset @remote since this is now back to a regular, non-remote browser
       outer.setAttribute("remote", "false");
       outer.removeAttribute("remoteType");