Bug 666801 - Handle webProgress for out-of-process content (r=felipe)
authorBill McCloskey <wmccloskey@mozilla.com>
Wed, 24 Apr 2013 22:29:31 -0700
changeset 140785 84df49f43e7dd0f59e1562f9ef6d11806c657bd9
parent 140784 a8a547fb44a8bf92e2ad57a395fc50dcff47379a
child 140786 02d966a79f149a08b359ac19c6cbf1c1d5d19d56
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfelipe
bugs666801
milestone23.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 666801 - Handle webProgress for out-of-process content (r=felipe)
browser/base/content/browser.css
browser/base/content/browser.js
browser/base/content/tabbrowser.xml
toolkit/content/browser-child.js
toolkit/content/jar.mn
toolkit/content/widgets/remote-browser.xml
toolkit/modules/Makefile.in
toolkit/modules/RemoteWebProgress.jsm
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -4,16 +4,20 @@
 
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 @namespace html url("http://www.w3.org/1999/xhtml");
 
 searchbar {
   -moz-binding: url("chrome://browser/content/search/search.xml#searchbar");
 }
 
+browser[remote="true"] {
+  -moz-binding: url("chrome://global/content/bindings/remote-browser.xml#remote-browser");
+}
+
 tabbrowser {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser");
 }
 
 .tabbrowser-tabs {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tabs");
 }
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -805,22 +805,19 @@ var gBrowserInit = {
     // enable global history
     try {
       if (!gMultiProcessBrowser)
         gBrowser.docShell.QueryInterface(Ci.nsIDocShellHistory).useGlobalHistory = true;
     } catch(ex) {
       Cu.reportError("Places database may be locked: " + ex);
     }
 
-    // Bug 666801 - WebProgress support for e10s
-    if (!gMultiProcessBrowser) {
-      // hook up UI through progress listener
-      gBrowser.addProgressListener(window.XULBrowserWindow);
-      gBrowser.addTabsProgressListener(window.TabsProgressListener);
-    }
+    // hook up UI through progress listener
+    gBrowser.addProgressListener(window.XULBrowserWindow);
+    gBrowser.addTabsProgressListener(window.TabsProgressListener);
 
     // setup our common DOMLinkAdded listener
     gBrowser.addEventListener("DOMLinkAdded", DOMLinkHandler, false);
 
     // setup our MozApplicationManifest listener
     gBrowser.addEventListener("MozApplicationManifest",
                               OfflineApps, false);
 
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -923,30 +923,27 @@
             }
 
             if (updatePageReport)
               this.mCurrentBrowser.updatePageReport();
 
             // Update the URL bar.
             var loc = this.mCurrentBrowser.currentURI;
 
-            // Bug 666801 - WebProgress support for e10s and
             // Bug 666809 - SecurityUI support for e10s
-            if (!gMultiProcessBrowser) {
-              var webProgress = this.mCurrentBrowser.webProgress;
-              var securityUI = this.mCurrentBrowser.securityUI;
-
-              this._callProgressListeners(null, "onLocationChange",
-                                          [webProgress, null, loc, 0], true,
-                                          false);
-
-              if (securityUI) {
-                this._callProgressListeners(null, "onSecurityChange",
-                                            [webProgress, null, securityUI.state], true, false);
-              }
+            var webProgress = this.mCurrentBrowser.webProgress;
+            var securityUI = this.mCurrentBrowser.securityUI;
+
+            this._callProgressListeners(null, "onLocationChange",
+                                        [webProgress, null, loc, 0], true,
+                                        false);
+
+            if (securityUI) {
+              this._callProgressListeners(null, "onSecurityChange",
+                                          [webProgress, null, securityUI.state], true, false);
             }
 
             var listener = this.mTabListeners[this.tabContainer.selectedIndex] || null;
             if (listener && listener.mStateFlags) {
               this._callProgressListeners(null, "onUpdateCurrentBrowser",
                                           [listener.mStateFlags, listener.mStatus,
                                            listener.mMessage, listener.mTotalProgress],
                                           true, false);
@@ -1395,19 +1392,17 @@
               b.stop();
             }
 
             // wire up a progress listener for the new browser object.
             var tabListener = this.mTabProgressListener(t, b, uriIsAboutBlank);
             const filter = Components.classes["@mozilla.org/appshell/component/browser-status-filter;1"]
                                      .createInstance(Components.interfaces.nsIWebProgress);
             filter.addProgressListener(tabListener, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
-            // Bug 666801 - WebProgress support for e10s
-            if (!gMultiProcessBrowser)
-              b.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
+            b.webProgress.addProgressListener(filter, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
             this.mTabListeners[position] = tabListener;
             this.mTabFilters[position] = filter;
 
             b._fastFind = this.fastFind;
             b.droppedLinkHandler = handleDroppedLink;
 
             // Dispatch a new tab notification.  We do this once we're
             // entirely done, so that things are in a consistent state
@@ -1702,19 +1697,17 @@
               var windowUtils = browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
                                 getInterface(Ci.nsIDOMWindowUtils);
               windowUtils.preventFurtherDialogs();
             }
 
             // Remove the tab's filter and progress listener.
             const filter = this.mTabFilters[aTab._tPos];
 
-            // Bug 666801 - WebProgress support for e10s
-            if (!gMultiProcessBrowser)
-              browser.webProgress.removeProgressListener(filter);
+            browser.webProgress.removeProgressListener(filter);
 
             filter.removeProgressListener(this.mTabListeners[aTab._tPos]);
             this.mTabListeners[aTab._tPos].destroy();
 
             if (browser.registeredOpenURI && !aTabWillBeMoved) {
               this._placesAutocomplete.unregisterOpenPage(browser.registeredOpenURI);
               delete browser.registeredOpenURI;
             }
new file mode 100644
--- /dev/null
+++ b/toolkit/content/browser-child.js
@@ -0,0 +1,150 @@
+/* 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/. */
+
+let Cc = Components.classes;
+let Ci = Components.interfaces;
+let Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+
+let WebProgressListener = {
+  init: function() {
+    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                              .getInterface(Ci.nsIWebProgress);
+    webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_ALL);
+  },
+
+  _requestSpec: function (aRequest) {
+    if (!aRequest || !(aRequest instanceof Ci.nsIChannel))
+      return null;
+    return aRequest.QueryInterface(Ci.nsIChannel).URI.spec;
+  },
+
+  _setupJSON: function setupJSON(aWebProgress, aRequest) {
+    return {
+      isTopLevel: aWebProgress.isTopLevel,
+      requestURI: this._requestSpec(aRequest)
+    };
+  },
+
+  onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
+    let json = this._setupJSON(aWebProgress, aRequest);
+    json.stateFlags = aStateFlags;
+    json.status = aStatus;
+
+    sendAsyncMessage("Content:StateChange", json);
+  },
+
+  onProgressChange: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
+  },
+
+  onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocationURI, aFlags) {
+    let spec = aLocationURI ? aLocationURI.spec : "";
+    let charset = content.document.characterSet;
+
+    let json = this._setupJSON(aWebProgress, aRequest);
+    json.documentURI = aWebProgress.DOMWindow.document.documentURIObject.spec;
+    json.location = spec;
+    json.canGoBack = docShell.canGoBack;
+    json.canGoForward = docShell.canGoForward;
+    json.charset = charset.toString();
+
+    sendAsyncMessage("Content:LocationChange", json);
+  },
+
+  onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
+    let json = this._setupJSON(aWebProgress, aRequest);
+    json.status = aStatus;
+    json.message = aMessage;
+
+    sendAsyncMessage("Content:StatusChange", json);
+  },
+
+  onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) {
+    let json = this._setupJSON(aWebProgress, aRequest);
+    json.state = aState;
+
+    sendAsyncMessage("Content:SecurityChange", json);
+  },
+
+  QueryInterface: function QueryInterface(aIID) {
+    if (aIID.equals(Ci.nsIWebProgressListener) ||
+        aIID.equals(Ci.nsISupportsWeakReference) ||
+        aIID.equals(Ci.nsISupports)) {
+        return this;
+    }
+
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  }
+};
+
+WebProgressListener.init();
+
+let WebNavigation =  {
+  _webNavigation: docShell.QueryInterface(Ci.nsIWebNavigation),
+
+  init: function() {
+    addMessageListener("WebNavigation:GoBack", this);
+    addMessageListener("WebNavigation:GoForward", this);
+    addMessageListener("WebNavigation:GotoIndex", this);
+    addMessageListener("WebNavigation:LoadURI", this);
+    addMessageListener("WebNavigation:Reload", this);
+    addMessageListener("WebNavigation:Stop", this);
+  },
+
+  receiveMessage: function(message) {
+    switch (message.name) {
+      case "WebNavigation:GoBack":
+        this.goBack();
+        break;
+      case "WebNavigation:GoForward":
+        this.goForward();
+        break;
+      case "WebNavigation:GotoIndex":
+        this.gotoIndex(message);
+        break;
+      case "WebNavigation:LoadURI":
+        this.loadURI(message);
+        break;
+      case "WebNavigation:Reload":
+        this.reload(message);
+        break;
+      case "WebNavigation:Stop":
+        this.stop(message);
+        break;
+    }
+  },
+
+  goBack: function() {
+    if (this._webNavigation.canGoBack)
+      this._webNavigation.goBack();
+  },
+
+  goForward: function() {
+    if (this._webNavigation.canGoForward)
+      this._webNavigation.goForward();
+  },
+
+  gotoIndex: function(message) {
+    this._webNavigation.gotoIndex(message.index);
+  },
+
+  loadURI: function(message) {
+    let flags = message.json.flags || this._webNavigation.LOAD_FLAGS_NONE;
+    this._webNavigation.loadURI(message.json.uri, flags, null, null, null);
+  },
+
+  reload: function(message) {
+    let flags = message.json.flags || this._webNavigation.LOAD_FLAGS_NONE;
+    this._webNavigation.reload(flags);
+  },
+
+  stop: function(message) {
+    let flags = message.json.flags || this._webNavigation.STOP_ALL;
+    this._webNavigation.stop(flags);
+  }
+};
+
+WebNavigation.init();
--- a/toolkit/content/jar.mn
+++ b/toolkit/content/jar.mn
@@ -17,16 +17,17 @@ toolkit.jar:
 *  content/global/aboutSupport.js
 *  content/global/aboutSupport.xhtml
 *  content/global/aboutTelemetry.js
    content/global/aboutTelemetry.xhtml
    content/global/aboutTelemetry.css          (aboutTelemetry.css)
    content/global/directionDetector.html
    content/global/plugins.html
    content/global/plugins.css
+   content/global/browser-child.js            (browser-child.js)
 *+  content/global/buildconfig.html            (buildconfig.html)
 +  content/global/charsetOverlay.js           (charsetOverlay.js)
 +  content/global/charsetOverlay.xul          (charsetOverlay.xul)
 *  content/global/contentAreaUtils.js         (contentAreaUtils.js)
    content/global/customizeCharset.js         (customizeCharset.js)
    content/global/customizeCharset.xul        (customizeCharset.xul)
    content/global/customizeToolbar.css        (customizeToolbar.css)
 *  content/global/customizeToolbar.js         (customizeToolbar.js)
@@ -64,16 +65,17 @@ toolkit.jar:
    content/global/bindings/menu.xml            (widgets/menu.xml)
    content/global/bindings/menulist.xml        (widgets/menulist.xml)
    content/global/bindings/notification.xml    (widgets/notification.xml)
    content/global/bindings/numberbox.xml       (widgets/numberbox.xml)
    content/global/bindings/popup.xml           (widgets/popup.xml)
 *+ content/global/bindings/preferences.xml     (widgets/preferences.xml)
    content/global/bindings/progressmeter.xml   (widgets/progressmeter.xml)
    content/global/bindings/radio.xml           (widgets/radio.xml)
+   content/global/bindings/remote-browser.xml  (widgets/remote-browser.xml)
    content/global/bindings/resizer.xml         (widgets/resizer.xml)
    content/global/bindings/richlistbox.xml     (widgets/richlistbox.xml)
    content/global/bindings/scale.xml           (widgets/scale.xml)
    content/global/bindings/scrollbar.xml       (widgets/scrollbar.xml)
    content/global/bindings/scrollbox.xml       (widgets/scrollbox.xml)
    content/global/bindings/splitter.xml        (widgets/splitter.xml)
    content/global/bindings/spinbuttons.xml     (widgets/spinbuttons.xml)
    content/global/bindings/stringbundle.xml    (widgets/stringbundle.xml)
new file mode 100644
--- /dev/null
+++ b/toolkit/content/widgets/remote-browser.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0"?>
+
+<!-- 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/. -->
+
+<bindings id="firefoxBrowserBindings"
+          xmlns="http://www.mozilla.org/xbl"
+          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <binding id="remote-browser" extends="chrome://global/content/bindings/browser.xml#browser">
+
+    <implementation type="application/javascript" implements="nsIAccessibleProvider, nsIObserver, nsIDOMEventListener, nsIMessageListener">
+
+      <property name="securityUI"
+                onget="return null;"/>
+
+      <property name="webNavigation"
+                onget="return this._remoteWebNavigation;"
+                readonly="true"/>
+
+      <field name="_remoteWebProgress">null</field>
+
+      <property name="webProgress" readonly="true">
+	<getter>
+	  <![CDATA[
+            if (!this._remoteWebProgress) {
+              let RemoteWebProgress = Components.utils.import("resource://gre/modules/RemoteWebProgress.jsm",
+ {}).RemoteWebProgress;
+              this._remoteWebProgress = new RemoteWebProgress(this);
+            }
+            return this._remoteWebProgress;
+	  ]]>
+	</getter>
+      </property>
+
+      <constructor>
+        <![CDATA[
+          this.messageManager.loadFrameScript("chrome://global/content/browser-child.js", true);
+          this.webProgress._init();
+        ]]>
+      </constructor>
+
+      <destructor>
+        <![CDATA[
+          this.webProgress._destroy();
+        ]]>
+      </destructor>
+
+    </implementation>
+
+  </binding>
+
+</bindings>
--- a/toolkit/modules/Makefile.in
+++ b/toolkit/modules/Makefile.in
@@ -6,15 +6,16 @@ DEPTH     = @DEPTH@
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 EXTRA_JS_MODULES := \
   NewTabUtils.jsm \
-	Preferences.jsm \
+  Preferences.jsm \
+  RemoteWebProgress.jsm \
   Sqlite.jsm \
   TelemetryTimestamps.jsm \
   Timer.jsm \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/toolkit/modules/RemoteWebProgress.jsm
@@ -0,0 +1,119 @@
+// -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// 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/.
+
+this.EXPORTED_SYMBOLS = ["RemoteWebProgress"];
+
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function RemoteWebProgressRequest(spec)
+{
+  this.uri = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService)
+                                                    .newURI(spec, null, null);
+}
+
+RemoteWebProgressRequest.prototype = {
+  QueryInterface : XPCOMUtils.generateQI([Ci.nsIChannel]),
+
+  get URI() { return this.uri.clone(); }
+};
+
+function RemoteWebProgress(browser)
+{
+  this._browser = browser;
+  this._isDocumentLoading = false;
+  this._isTopLevel = true;
+  this._progressListeners = [];
+}
+
+RemoteWebProgress.prototype = {
+  NOTIFY_STATE_REQUEST:  0x00000001,
+  NOTIFY_STATE_DOCUMENT: 0x00000002,
+  NOTIFY_STATE_NETWORK:  0x00000004,
+  NOTIFY_STATE_WINDOW:   0x00000008,
+  NOTIFY_STATE_ALL:      0x0000000f,
+  NOTIFY_PROGRESS:       0x00000010,
+  NOTIFY_STATUS:         0x00000020,
+  NOTIFY_SECURITY:       0x00000040,
+  NOTIFY_LOCATION:       0x00000080,
+  NOTIFY_REFRESH:        0x00000100,
+  NOTIFY_ALL:            0x000001ff,
+
+  _init: function WP_Init() {
+    this._browser.messageManager.addMessageListener("Content:StateChange", this);
+    this._browser.messageManager.addMessageListener("Content:LocationChange", this);
+    this._browser.messageManager.addMessageListener("Content:SecurityChange", this);
+    this._browser.messageManager.addMessageListener("Content:StatusChange", this);
+  },
+
+  _destroy: function WP_Destroy() {
+    this._browser.messageManager.removeMessageListener("Content:StateChange", this);
+    this._browser.messageManager.removeMessageListener("Content:LocationChange", this);
+    this._browser.messageManager.removeMessageListener("Content:SecurityChange", this);
+    this._browser.messageManager.removeMessageListener("Content:StatusChange", this);
+    this._browser = null;
+  },
+
+  get isLoadingDocument() { return this._isDocumentLoading },
+  get DOMWindow() { return null; },
+  get DOMWindowID() { return 0; },
+  get isTopLevel() { return this._isTopLevel; },
+
+  addProgressListener: function WP_AddProgressListener (aListener) {
+    let listener = aListener.QueryInterface(Ci.nsIWebProgressListener);
+    this._progressListeners.push(listener);
+  },
+
+  removeProgressListener: function WP_RemoveProgressListener (aListener) {
+    this._progressListeners =
+      this._progressListeners.filter(function (l) l != aListener);
+  },
+
+  _uriSpec: function (spec) {
+    if (!spec)
+      return null;
+    return new RemoteWebProgressRequest(spec);
+  },
+
+  receiveMessage: function WP_ReceiveMessage(aMessage) {
+    this._isTopLevel = aMessage.json.isTopLevel;
+
+    let req = this._uriSpec(aMessage.json.requestURI);
+    switch (aMessage.name) {
+    case "Content:StateChange":
+      for each (let p in this._progressListeners) {
+        p.onStateChange(this, req, aMessage.json.stateFlags, aMessage.json.status);
+      }
+      break;
+
+    case "Content:LocationChange":
+      let loc = Cc["@mozilla.org/network/io-service;1"]
+                .getService(Ci.nsIIOService)
+                .newURI(aMessage.json.location, null, null);
+      this._browser.webNavigation._currentURI = loc;
+      this._browser.webNavigation.canGoBack = aMessage.json.canGoBack;
+      this._browser.webNavigation.canGoForward = aMessage.json.canGoForward;
+      for each (let p in this._progressListeners) {
+        p.onLocationChange(this, req, loc);
+      }
+      break;
+
+    case "Content:SecurityChange":
+      for each (let p in this._progressListeners) {
+        p.onSecurityChange(this, req, aMessage.json.state);
+      }
+      break;
+
+    case "Content:StatusChange":
+      for each (let p in this._progressListeners) {
+        p.onStatusChange(this, req, aMessage.json.status, aMessage.json.message);
+      }
+      break;
+    }
+  }
+};