Bug 588441 - Remove progress listeners in chrome [r=mbrubeck r=bstover]
authorMark Finkle <mfinkle@mozilla.com>
Mon, 10 Jan 2011 23:53:20 -0500
changeset 67237 3dc424392fa5645f1d273de2b35a8b38fb175fca
parent 67236 6aade8f3b1d2070bfcab11b374a53890c541dbc4
child 67238 0501ea814ad1f524b352890d1b3d15952bb0b276
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmbrubeck, bstover
bugs588441
Bug 588441 - Remove progress listeners in chrome [r=mbrubeck r=bstover]
mobile/chrome/content/bindings/browser.js
mobile/chrome/content/bindings/browser.xml
mobile/chrome/content/browser.js
mobile/chrome/content/content.js
mobile/chrome/tests/browser_navigation.js
mobile/chrome/tests/remote_forms.js
--- a/mobile/chrome/content/bindings/browser.js
+++ b/mobile/chrome/content/bindings/browser.js
@@ -1,122 +1,79 @@
 let Cc = Components.classes;
 let Ci = Components.interfaces;
 
 dump("!! remote browser loaded\n")
 
 let WebProgressListener = {
-  _notifyFlags: [],
-  _calculatedNotifyFlags: 0,
   _lastLocation: null,
 
   init: function() {
-    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress);
-    webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_ALL);
+    let flags = Ci.nsIWebProgress.NOTIFY_LOCATION |
+                Ci.nsIWebProgress.NOTIFY_SECURITY |
+                Ci.nsIWebProgress.NOTIFY_STATE_ALL;
 
-    addMessageListener("WebProgress:AddProgressListener", this);
-    addMessageListener("WebProgress:RemoveProgressListener", this);
+    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress);
+    webProgress.addProgressListener(this, flags);
   },
 
   onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
-    let webProgress = Ci.nsIWebProgressListener;
-    let notifyFlags = 0;
-    if (aStateFlags & webProgress.STATE_IS_REQUEST)
-      notifyFlags |= Ci.nsIWebProgress.NOTIFY_STATE_REQUEST;
-    if (aStateFlags & webProgress.STATE_IS_DOCUMENT)
-      notifyFlags |= Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT;
-    if (aStateFlags & webProgress.STATE_IS_NETWORK)
-      notifyFlags |= Ci.nsIWebProgress.NOTIFY_STATE_NETWORK;
-    if (aStateFlags & webProgress.STATE_IS_WINDOW)
-      notifyFlags |= Ci.nsIWebProgress.NOTIFY_STATE_WINDOW;
+    if (content != aWebProgress.DOMWindow)
+      return;
 
     let json = {
       contentWindowId: content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID,
-      windowId: aWebProgress.DOMWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID,
       stateFlags: aStateFlags,
-      status: aStatus,
-      notifyFlags: notifyFlags
+      status: aStatus
     };
 
-    sendAsyncMessage("WebProgress:StateChange", json);
+    sendAsyncMessage("Content:StateChange", json);
   },
 
   onProgressChange: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
-    let json = {
-      contentWindowId: content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID,
-      windowId: aWebProgress.DOMWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID,
-      curSelf: aCurSelf,
-      maxSelf: aMaxSelf,
-      curTotal: aCurTotal,
-      maxTotal: aMaxTotal
-    };
-
-    if (this._calculatedNotifyFlags & Ci.nsIWebProgress.NOTIFY_PROGRESS)
-      sendAsyncMessage("WebProgress:ProgressChange", json);
   },
 
   onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocationURI) {
+    if (content != aWebProgress.DOMWindow)
+      return;
+
     let spec = aLocationURI ? aLocationURI.spec : "";
     let location = spec.split("#")[0];
 
     let json = {
       contentWindowId: content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID,
-      windowId: aWebProgress.DOMWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID,
       documentURI: aWebProgress.DOMWindow.document.documentURIObject.spec,
       location: spec,
       canGoBack: docShell.canGoBack,
       canGoForward: docShell.canGoForward
     };
-    sendAsyncMessage("WebProgress:LocationChange", json);
+
+    sendAsyncMessage("Content:LocationChange", json);
 
     // Keep track of hash changes
     this.hashChanged = (location == this._lastLocation);
     this._lastLocation = location;
   },
 
   onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
-    let json = {
-      contentWindowId: content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID,
-      windowId: aWebProgress.DOMWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID,
-      status: aStatus,
-      message: aMessage
-    };
-
-    if (this._calculatedNotifyFlags & Ci.nsIWebProgress.NOTIFY_STATUS)
-      sendAsyncMessage("WebProgress:StatusChange", json);
   },
 
   onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) {
+    if (content != aWebProgress.DOMWindow)
+      return;
+
     let serialization = SecurityUI.getSSLStatusAsString();
 
     let json = {
       contentWindowId: content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID,
-      windowId: aWebProgress.DOMWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID,
       SSLStatusAsString: serialization,
       state: aState
     };
-    sendAsyncMessage("WebProgress:SecurityChange", json);
-  },
 
-  receiveMessage: function(aMessage) {
-    switch (aMessage.name) {
-      case "WebProgress:AddProgressListener":
-        this._notifyFlags.push(aMessage.json.notifyFlags);
-        this._calculatedNotifyFlags |= aMessage.json.notifyFlags;
-        break;
-      case "WebProgress.RemoveProgressListener":
-        let index = this._notifyFlags.indexOf(aMessage.json.notifyFlags);
-        if (index != -1) {
-          this._notifyFlags.splice(index, 1);
-          this._calculatedNotifyFlags = this._notifyFlags.reduce(function(a, b) {
-            return a | b;
-          }, 0);
-        }
-        break;
-    }
+    sendAsyncMessage("Content:SecurityChange", json);
   },
 
   QueryInterface: function QueryInterface(aIID) {
     if (aIID.equals(Ci.nsIWebProgressListener) ||
         aIID.equals(Ci.nsISupportsWeakReference) ||
         aIID.equals(Ci.nsISupports)) {
         return this;
     }
--- a/mobile/chrome/content/bindings/browser.xml
+++ b/mobile/chrome/content/bindings/browser.xml
@@ -187,155 +187,116 @@
           }
 
           return type;
         ]]></body>
       </method>
 
       <field name="_webProgress"><![CDATA[
         ({
-          _listeners: [],
           _browser: this,
 
           _init: function() {
-            this._browser.messageManager.addMessageListener("WebProgress:StateChange", this);
-            this._browser.messageManager.addMessageListener("WebProgress:ProgressChange", this);
-            this._browser.messageManager.addMessageListener("WebProgress:LocationChange", this);
-            this._browser.messageManager.addMessageListener("WebProgress:StatusChange", this);
-            this._browser.messageManager.addMessageListener("WebProgress:SecurityChange", this);
-          },
-
-          addProgressListener: function(aListener, aNotifyFlags) {
-            function hasFilter(item) {
-              return item.listener == aListener;
-            }
-
-            if (this._listeners.some(hasFilter))
-              return;
-
-            this._listeners.push({
-              listener: aListener,
-              flags: aNotifyFlags
-            });
-
-            this._browser.messageManager.sendAsyncMessage("WebProgress:AddProgressListener", {
-              notifyFlags: aNotifyFlags,
-            });
+            this._browser.messageManager.addMessageListener("Content:StateChange", this);
+            this._browser.messageManager.addMessageListener("Content:LocationChange", this);
+            this._browser.messageManager.addMessageListener("Content:SecurityChange", this);
           },
 
-          removeProgressListener: function(aListener) {
-            let self = this;
-            function hasFilter(item) {
-              if (item.listener == aListener)
-                self._browser.messageManager.sendAsyncMessage("WebProgress:RemoveProgressListener", {
-                  notifyFlags: item.flags,
-                });
-              return item.listener != aListener;
-            }
-
-            this._listeners = this._listeners.filter(hasFilter);
-          },
-          get DOMWindow() { return null; },
-          get isLoadingDocument() { throw "isLoadingDocument: Invalid"; },
-
           receiveMessage: function(aMessage) {
-            let args;
             let json = aMessage.json;
             switch (aMessage.name) {
-              case "WebProgress:StateChange":
-                args = [
-                  { windowId: json.windowId, browser: this._browser },
-                  {},
-                  json.stateFlags,
-                  json.status
-                ];
-
+              case "Content:StateChange":
                 this._browser.updateWindowId(json.contentWindowId);
-                this._notify(json.notifyFlags, "onStateChange", args);
                 break;
 
-              case "WebProgress:ProgressChange":
-                args = [
-                  { windowId: json.windowId, browser: this._browser },
-                  {},
-                  json.curSelf,
-                  json.maxSelf,
-                  json.curTotal,
-                  json.maxTotal
-                ];
-                this._browser.updateWindowId(json.contentWindowId);
-                this._notify(Components.interfaces.nsIWebProgress.NOTIFY_PROGRESS,
-                             "onProgressChange",
-                             args);
-                break;
-
-              case "WebProgress:LocationChange":
-                let locationURI = this._browser._ios.newURI(json.location, null, null);
-
-                args = [
-                  { windowId: json.windowId, browser: this._browser },
-                  {},
-                  locationURI
-                ];
+              case "Content:LocationChange":
+                try {
+                  let locationURI = this._browser._ios.newURI(json.location, null, null);
+                  this._browser._webNavigation._currentURI = locationURI;
+                  this._browser._webNavigation.canGoBack = json.canGoBack;
+                  this._browser._webNavigation.canGoForward = json.canGoForward;
+                } catch(e) {}
 
                 if (this._browser.updateWindowId(json.contentWindowId)) {
                   this._browser._documentURI = json.documentURI;
                   this._browser._searchEngines = [];
                 }
-                this._notify(Components.interfaces.nsIWebProgress.NOTIFY_LOCATION,
-                             "onLocationChange",
-                             args);
-
                 break;
 
-              case "WebProgress:StatusChange":
-                args = [
-                  { windowId: json.windowId, browser: this._browser },
-                  {},
-                  json.status,
-                  json.message
-                ];
-                this._browser.updateWindowId(json.contentWindowId);
-                this._notify(Components.interfaces.nsIWebProgress.NOTIFY_STATUS,
-                             "onStatusChange",
-                             args);
-                break;
+              case "Content:SecurityChange":
+                let serhelper = Cc["@mozilla.org/network/serialization-helper;1"].getService(Ci.nsISerializationHelper);
+                let SSLStatus = json.SSLStatusAsString ? serhelper.deserializeObject(json.SSLStatusAsString) : null;
+                if (SSLStatus) {
+                  SSLStatus.QueryInterface(Ci.nsISSLStatus);
+                  if (SSLStatus) {
+                    // We must check the Extended Validation (EV) state here, on the chrome
+                    // process, because NSS is needed for that determination.
+                    let ii = SSLStatus.serverCert.QueryInterface(Ci.nsIIdentityInfo);
+                    if (ii && ii.isExtendedValidation)
+                      json.state |= Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL;
+                  }
+                }
 
-              case "WebProgress:SecurityChange":
-                args = [
-                  { windowId: json.windowId, browser: this._browser },
-                  {},
-                  json.state
-                ];
-
+                let data = this._getIdentityData(SSLStatus);
                 this._browser._securityUI = {
-                  SSLStatus: json.SSLStatus,
+                  SSLStatus: SSLStatus ? {serverCert: data} : null,
                   state: json.state
                 }
                 this._browser.updateWindowId(json.contentWindowId);
-                this._notify(Components.interfaces.nsIWebProgress.NOTIFY_SECURITY,
-                             "onSecurityChange",
-                             args);
                 break;
             }
           },
 
-          _notify: function(aFlags, aName, aArguments) {
-            this._listeners.forEach(function(item) {
-              if (item.flags & aFlags) {
-                item.listener[aName].apply(item.listener, aArguments);
+          /**
+           * Helper to parse out the important parts of the SSL cert for use in constructing
+           * identity UI strings
+           */
+          _getIdentityData: function(status) {
+            let result = {};
+
+            if (status) {
+              let cert = status.serverCert;
+
+              // Human readable name of Subject
+              result.subjectOrg = cert.organization;
+
+              // SubjectName fields, broken up for individual access
+              if (cert.subjectName) {
+                result.subjectNameFields = {};
+                cert.subjectName.split(",").forEach(function(v) {
+                  var field = v.split("=");
+                  if (field[1])
+                    this[field[0]] = field[1];
+                }, result.subjectNameFields);
+
+                // Call out city, state, and country specifically
+                result.city = result.subjectNameFields.L;
+                result.state = result.subjectNameFields.ST;
+                result.country = result.subjectNameFields.C;
               }
-            });
+
+              // Human readable name of Certificate Authority
+              result.caOrg =  cert.issuerOrganization || cert.issuerCommonName;
+
+              if (!this._overrideService)
+                this._overrideService = Cc["@mozilla.org/security/certoverride;1"].getService(Ci.nsICertOverrideService);
+
+              // Check whether this site is a security exception.
+              let currentURI = this._browser._webNavigation._currentURI;
+              result.isException = !!this._overrideService.hasMatchingOverride(currentURI.asciiHost, currentURI.port, cert, {}, {});
+            }
+
+            return result;
           }
         })
       ]]></field>
 
       <property name="webProgress"
                 readonly="true"
-                onget="return this._webProgress"/>
+                onget="return null"/>
 
       <method name="onPageShow">
         <parameter name="aMessage"/>
         <body>
           <![CDATA[
             this.attachFormFill();
             if (this.pageReport) {
               var json = aMessage.json;
@@ -698,207 +659,16 @@
          }
         })
       ]]></field>
 
       <property name="webNavigation"
                 onget="return this._webNavigation;"
                 readonly="true"/>
 
-      <field name="_webProgress"><![CDATA[
-        ({
-          _listeners: [],
-          _browser: this,
-
-          _init: function() {
-            this._browser.messageManager.addMessageListener("WebProgress:StateChange", this);
-            this._browser.messageManager.addMessageListener("WebProgress:ProgressChange", this);
-            this._browser.messageManager.addMessageListener("WebProgress:LocationChange", this);
-            this._browser.messageManager.addMessageListener("WebProgress:StatusChange", this);
-            this._browser.messageManager.addMessageListener("WebProgress:SecurityChange", this);
-          },
-
-          addProgressListener: function(aListener, aNotifyFlags) {
-            function hasFilter(item) {
-              return item.listener == aListener;
-            }
-
-            if (this._listeners.some(hasFilter))
-              return;
-
-            this._listeners.push({
-              listener: aListener,
-              flags: aNotifyFlags
-            });
-          },
-
-          removeProgressListener: function(aListener) {
-            function hasFilter(item) {
-              return item.listener != aListener;
-            }
-
-            this._listeners = this._listeners.filter(hasFilter);
-          },
-          get DOMWindow() { return null; },
-          get isLoadingDocument() { throw "isLoadingDocument: Invalid"; },
-
-          receiveMessage: function(aMessage) {
-            let args;
-            let json = aMessage.json;
-            switch (aMessage.name) {
-              case "WebProgress:StateChange":
-                args = [
-                  { windowId: json.windowId, browser: this._browser },
-                  {},
-                  json.stateFlags,
-                  json.status
-                ];
-
-                this._browser.updateWindowId(json.contentWindowId);
-                this._notify(json.notifyFlags, "onStateChange", args);
-                break;
-
-              case "WebProgress:ProgressChange":
-                args = [
-                  { windowId: json.windowId, browser: this._browser },
-                  {},
-                  json.curSelf,
-                  json.maxSelf,
-                  json.curTotal,
-                  json.maxTotal
-                ];
-                this._browser.updateWindowId(json.contentWindowId);
-                this._notify(Components.interfaces.nsIWebProgress.NOTIFY_PROGRESS,
-                             "onProgressChange",
-                             args);
-                break;
-
-              case "WebProgress:LocationChange":
-                let locationURI = this._browser._ios.newURI(json.location, null, null);
-                this._browser._webNavigation._currentURI = locationURI;
-                this._browser._webNavigation.canGoBack = json.canGoBack;
-                this._browser._webNavigation.canGoForward = json.canGoForward;
-
-                args = [
-                  { windowId: json.windowId, browser: this._browser },
-                  {},
-                  locationURI
-                ];
-
-                if (this._browser.updateWindowId(json.contentWindowId)) {
-                  this._browser._documentURI = json.documentURI;
-                  this._browser._searchEngines = [];
-                }
-
-                this._notify(Components.interfaces.nsIWebProgress.NOTIFY_LOCATION,
-                             "onLocationChange",
-                             args);
-
-                break;
-
-              case "WebProgress:StatusChange":
-                args = [
-                  { windowId: json.windowId, browser: this._browser },
-                  {},
-                  json.status,
-                  json.message
-                ];
-                this._browser.updateWindowId(json.contentWindowId);
-                this._notify(Components.interfaces.nsIWebProgress.NOTIFY_STATUS,
-                             "onStatusChange",
-                             args);
-                break;
-
-              case "WebProgress:SecurityChange":
-                let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
-                                .getService(Ci.nsISerializationHelper);
-                let SSLStatus = json.SSLStatusAsString ?
-                    serhelper.deserializeObject(json.SSLStatusAsString) : null;
-
-                if (SSLStatus) {
-                  SSLStatus.QueryInterface(Ci.nsISSLStatus);
-                  if (SSLStatus) {
-                    // We must check the Extended Validation (EV) state here, on the chrome
-                    // process, because NSS is needed for that determination.
-                    let ii = SSLStatus.serverCert.QueryInterface(Ci.nsIIdentityInfo);
-                    if (ii && ii.isExtendedValidation)
-                      json.state |= Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL;
-                  }
-                }
-
-                let data = this._getIdentityData(SSLStatus);
-                this._browser._securityUI = {
-                  SSLStatus: SSLStatus ? {serverCert: data} : null,
-                  state: json.state
-                }
-
-                args = [
-                  { windowId: json.windowId, browser: this._browser },
-                  {},
-                  json.state
-                ];
-                this._browser.updateWindowId(json.contentWindowId);
-                this._notify(Components.interfaces.nsIWebProgress.NOTIFY_SECURITY,
-                             "onSecurityChange",
-                             args);
-                break;
-            }
-          },
-
-          _notify: function(aFlags, aName, aArguments) {
-            this._listeners.forEach(function(item) {
-              if (item.flags & aFlags)
-                item.listener[aName].apply(item.listener, aArguments);
-            });
-          },
-
-          /**
-           * Helper to parse out the important parts of the SSL cert for use in constructing
-           * identity UI strings
-           */
-          _getIdentityData: function(status) {
-            let result = {};
-
-            if (status) {
-              let cert = status.serverCert;
-
-              // Human readable name of Subject
-              result.subjectOrg = cert.organization;
-
-              // SubjectName fields, broken up for individual access
-              if (cert.subjectName) {
-                result.subjectNameFields = {};
-                cert.subjectName.split(",").forEach(function(v) {
-                  var field = v.split("=");
-                  if (field[1])
-                    this[field[0]] = field[1];
-                }, result.subjectNameFields);
-
-                // Call out city, state, and country specifically
-                result.city = result.subjectNameFields.L;
-                result.state = result.subjectNameFields.ST;
-                result.country = result.subjectNameFields.C;
-              }
-
-              // Human readable name of Certificate Authority
-              result.caOrg =  cert.issuerOrganization || cert.issuerCommonName;
-
-              if (!this._overrideService)
-                this._overrideService = Cc["@mozilla.org/security/certoverride;1"].getService(Ci.nsICertOverrideService);
-
-              // Check whether this site is a security exception.
-              let currentURI = this._browser._webNavigation._currentURI;
-              result.isException = !!this._overrideService.hasMatchingOverride(currentURI.asciiHost, currentURI.port, cert, {}, {});
-            }
-
-            return result;
-          }
-        })
-      ]]></field>
-
       <property name="contentWindow"
                 readonly="true"
                 onget="return null"/>
 
       <property name="sessionHistory"
                 onget="return null"
                 readonly="true"/>
 
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -191,16 +191,19 @@ var Browser = {
       dump("###########" + e + "\n");
     }
 
     // XXX change
 
     /* handles dispatching clicks on browser into clicks in content or zooms */
     Elements.browsers.customDragger = new Browser.MainDragger();
 
+    /* handles web progress management for open browsers */
+    Elements.browsers.webProgress = new Browser.WebProgress();
+
     let keySender = new ContentCustomKeySender(Elements.browsers);
     let mouseModule = new MouseModule();
     let gestureModule = new GestureModule(Elements.browsers);
     let scrollWheelModule = new ScrollwheelModule(Elements.browsers);
 
     ContentTouchHandler.init();
 
     // Warning, total hack ahead. All of the real-browser related scrolling code
@@ -1302,19 +1305,128 @@ Browser.MainDragger.prototype = {
 
   _hideScrollbars: function _hideScrollbars() {
     this._horizontalScrollbar.removeAttribute("panning");
     this._verticalScrollbar.removeAttribute("panning");
   }
 };
 
 
-function nsBrowserAccess()
-{
-}
+Browser.WebProgress = function WebProgress() {
+  messageManager.addMessageListener("Content:StateChange", this);
+  messageManager.addMessageListener("Content:LocationChange", this);
+  messageManager.addMessageListener("Content:SecurityChange", this);
+};
+
+Browser.WebProgress.prototype = {
+  receiveMessage: function receiveMessage(aMessage) {
+    let json = aMessage.json;
+    let tab = Browser.getTabForBrowser(aMessage.target);
+
+    switch (aMessage.name) {
+      case "Content:StateChange": {
+        if (json.stateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
+          if (json.stateFlags & Ci.nsIWebProgressListener.STATE_START)
+            this._networkStart(tab);
+          else if (json.stateFlags & Ci.nsIWebProgressListener.STATE_STOP)
+            this._networkStop(tab);
+        }
+
+        if (json.stateFlags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT) {
+          if (json.stateFlags & Ci.nsIWebProgressListener.STATE_STOP)
+            this._documentStop(tab);
+        }
+        break;
+      }
+
+      case "Content:LocationChange": {
+        let spec = json.location;
+        let location = spec.split("#")[0]; // Ignore fragment identifier changes.
+
+        if (tab == Browser.selectedTab)
+          BrowserUI.updateURI();
+
+        let locationHasChanged = (location != tab.browser.lastLocation);
+        if (locationHasChanged) {
+          TapHighlightHelper.hide();
+
+          Browser.getNotificationBox(tab.browser).removeTransientNotifications();
+          tab.resetZoomLevel();
+          tab.hostChanged = true;
+          tab.browser.lastLocation = location;
+          tab.browser.userTypedValue = "";
+
+#ifdef MOZ_CRASH_REPORTER
+          if (CrashReporter.enabled)
+            CrashReporter.annotateCrashReport("URL", spec);
+#endif
+          if (tab == Browser.selectedTab) {
+            // We're about to have new page content, so scroll the content area
+            // to the top so the new paints will draw correctly.
+            // (background tabs are delayed scrolled to top in _documentStop)
+            Browser.scrollContentToTop({ x: 0 });
+          }
+
+          tab.useFallbackWidth = false;
+          tab.updateViewportSize();
+        }
+
+        let event = document.createEvent("UIEvents");
+        event.initUIEvent("URLChanged", true, false, window, locationHasChanged);
+        tab.browser.dispatchEvent(event);
+        break;
+      }
+
+      case "Content:SecurityChange": {
+        // Don't need to do anything if the data we use to update the UI hasn't changed
+        if (tab.state == json.state && !tab.hostChanged)
+          return;
+
+        tab.hostChanged = false;
+        tab.state = json.state;
+
+        if (tab == Browser.selectedTab)
+          getIdentityHandler().checkIdentity();
+        break;
+      }
+    }
+  },
+
+  _networkStart: function _networkStart(aTab) {
+    aTab.startLoading();
+
+    if (aTab == Browser.selectedTab) {
+      BrowserUI.update(TOOLBARSTATE_LOADING);
+
+      // We should at least show something in the URLBar until
+      // the load has progressed further along
+      if (aTab.browser.currentURI.spec == "about:blank")
+        BrowserUI.updateURI();
+    }
+  },
+
+  _networkStop: function _networkStop(aTab) {
+    aTab.endLoading();
+
+    if (aTab == Browser.selectedTab)
+      BrowserUI.update(TOOLBARSTATE_LOADED);
+
+    if (aTab.browser.currentURI.spec != "about:blank")
+      aTab.updateThumbnail();
+  },
+
+  _documentStop: function _documentStop(aTab) {
+      // Make sure the URLbar is in view. If this were the selected tab,
+      // onLocationChange would scroll to top.
+      aTab.pageScrollOffset = new Point(0, 0);
+  }
+};
+
+
+function nsBrowserAccess() { }
 
 nsBrowserAccess.prototype = {
   QueryInterface: function(aIID) {
     if (aIID.equals(Ci.nsIBrowserDOMWindow) || aIID.equals(Ci.nsISupports))
       return this;
     throw Cr.NS_NOINTERFACE;
   },
 
@@ -1524,18 +1636,17 @@ const ContentTouchHandler = {
 
   tapDown: function tapDown(aX, aY) {
     // Ensure that the content process has gets an activate event
     let browser = getBrowser();
     let fl = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
     browser.focus();
     try {
       fl.activateRemoteFrame();
-    } catch (e) {
-    }
+    } catch (e) {}
     this._dispatchMouseEvent("Browser:MouseDown", aX, aY);
   },
 
   tapOver: function tapOver(aX, aY) {
     this._dispatchMouseEvent("Browser:MouseOver", aX, aY);
   },
 
   tapUp: function tapUp(aX, aY) {
@@ -2216,157 +2327,16 @@ function importDialog(aParent, aSrc, aAr
 }
 
 function showDownloadManager(aWindowContext, aID, aReason) {
   BrowserUI.showPanel("downloads-container");
   // TODO: select the download with aID
 }
 
 
-function ProgressController(tab) {
-  this._tab = tab;
-
-  // Properties used to cache security state used to update the UI
-  this.state = null;
-  this._hostChanged = false; // onLocationChange will flip this bit
-}
-
-ProgressController.prototype = {
-  get browser() {
-    return this._tab.browser;
-  },
-
-  onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
-    // Ignore notification that aren't about the main document (iframes, etc)
-    if (aWebProgress.windowId != this._tab.browser.contentWindowId && this._tab.browser.contentWindowId)
-      return;
-
-    // If you want to observe other state flags, be sure they're listed in the
-    // Tab._createBrowser's call to addProgressListener
-    if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
-      if (aStateFlags & Ci.nsIWebProgressListener.STATE_START)
-        this._networkStart();
-      else if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP)
-        this._networkStop();
-    }
-
-    if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT) {
-      if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP)
-        this._documentStop();
-    }
-  },
-
-  /** This method is called to indicate progress changes for the currently loading page. */
-  onProgressChange: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
-    // To use this method, add NOTIFY_PROGRESS to the flags in Tab._createBrowser
-  },
-
-  /** This method is called to indicate a change to the current location. */
-  onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocationURI) {
-    // ignore notification that aren't about the main document (iframes, etc)
-    if (aWebProgress.windowId != this._tab.browser.contentWindowId)
-      return;
-
-    let spec = aLocationURI ? aLocationURI.spec : "";
-    let location = spec.split("#")[0]; // Ignore fragment identifier changes.
-
-    this._hostChanged = true;
-
-    if (this._tab == Browser.selectedTab)
-      BrowserUI.updateURI();
-
-    let locationHasChanged = (location != this.browser.lastLocation);
-    if (locationHasChanged) {
-      TapHighlightHelper.hide();
-
-      this.browser.lastLocation = location;
-      this.browser.userTypedValue = "";
-      Browser.getNotificationBox(this.browser).removeTransientNotifications();
-      this._tab.resetZoomLevel();
-
-#ifdef MOZ_CRASH_REPORTER
-      if (CrashReporter.enabled)
-        CrashReporter.annotateCrashReport("URL", spec);
-#endif
-      if (this._tab == Browser.selectedTab) {
-        // We're about to have new page content, so scroll the content area
-        // to the top so the new paints will draw correctly.
-        // (background tabs are delayed scrolled to top in _documentStop)
-        Browser.scrollContentToTop({ x: 0 });
-      }
-      this._tab.useFallbackWidth = false;
-      this._tab.updateViewportSize();
-    }
-
-    let event = document.createEvent("UIEvents");
-    event.initUIEvent("URLChanged", true, false, window, locationHasChanged);
-    this.browser.dispatchEvent(event);
-  },
-
-  /**
-   * This method is called to indicate a status changes for the currently
-   * loading page.  The message is already formatted for display.
-   */
-  onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
-    // To use this method, add NOTIFY_STATUS to the flags in Tab._createBrowser
-  },
-
-  /** This method is called when the security state of the browser changes. */
-  onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) {
-    // Don't need to do anything if the data we use to update the UI hasn't changed
-    if (this.state == aState && !this._hostChanged)
-      return;
-
-    this._hostChanged = false;
-    this.state = aState;
-
-    if (this._tab == Browser.selectedTab) {
-      getIdentityHandler().checkIdentity();
-    }
-  },
-
-  QueryInterface: function(aIID) {
-    if (aIID.equals(Ci.nsIWebProgressListener) ||
-        aIID.equals(Ci.nsISupportsWeakReference) ||
-        aIID.equals(Ci.nsISupports))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
-
-  _networkStart: function _networkStart() {
-    this._tab.startLoading();
-
-    if (this._tab == Browser.selectedTab) {
-      BrowserUI.update(TOOLBARSTATE_LOADING);
-
-      // We should at least show something in the URLBar until
-      // the load has progressed further along
-      if (this._tab.browser.currentURI.spec == "about:blank")
-        BrowserUI.updateURI();
-    }
-  },
-
-  _networkStop: function _networkStop() {
-    this._tab.endLoading();
-
-    if (this._tab == Browser.selectedTab)
-      BrowserUI.update(TOOLBARSTATE_LOADED);
-
-    if (this.browser.currentURI.spec != "about:blank")
-      this._tab.updateThumbnail();
-  },
-
-  _documentStop: function _documentStop() {
-      // Make sure the URLbar is in view. If this were the selected tab,
-      // onLocationChange would scroll to top.
-      this._tab.pageScrollOffset = new Point(0, 0);
-  }
-};
-
 var OfflineApps = {
   offlineAppRequested: function(aRequest, aTarget) {
     if (!Services.prefs.getBoolPref("browser.offline-apps.notify"))
       return;
 
     let currentURI = Services.io.newURI(aRequest.location, aRequest.charset, null);
 
     // don't bother showing UI if the user has already made a decision
@@ -2439,24 +2409,26 @@ var OfflineApps = {
     }
   }
 };
 
 function Tab(aURI, aParams) {
   this._id = null;
   this._browser = null;
   this._notification = null;
-  this._state = null;
-  this._listener = null;
   this._loading = false;
   this._chromeTab = null;
   this._metadata = null;
+
   this.useFallbackWidth = false;
   this.owner = null;
 
+  this.hostChanged = false;
+  this.state = null;
+
   // Set to 0 since new tabs that have not been viewed yet are good tabs to
   // toss if app needs more memory.
   this.lastSelected = 0;
 
   // aParams is an object that contains some properties for the initial tab
   // loading like flags, a referrerURI, a charset or even a postData.
   this.create(aURI, aParams || {});
 }
@@ -2644,37 +2616,27 @@ Tab.prototype = {
     // stop about:blank from loading
     browser.stop();
 
     browser.messageManager.addMessageListener("MozScrolledAreaChanged", (function() {
       // ensure that the browser's contentDocumentWidth property adjusts first
       setTimeout(this.scrolledAreaChanged.bind(this), 0);
     }).bind(this));
 
-    // Attach a separate progress listener to the browser
-    let flags = Ci.nsIWebProgress.NOTIFY_LOCATION |
-                Ci.nsIWebProgress.NOTIFY_SECURITY |
-                Ci.nsIWebProgress.NOTIFY_STATE_NETWORK |
-                Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT;
-    this._listener = new ProgressController(this);
-    browser.webProgress.addProgressListener(this._listener, flags);
-
     return browser;
   },
 
   _destroyBrowser: function _destroyBrowser() {
     if (this._browser) {
       let notification = this._notification;
       let browser = this._browser;
-      browser.removeProgressListener(this._listener);
       browser.active = false;
 
       this._notification = null;
       this._browser = null;
-      this._listener = null;
       this._loading = false;
 
       Elements.browsers.removeChild(notification);
     }
   },
 
   clampZoomLevel: function clampZoomLevel(aScale) {
     let browser = this._browser;
--- a/mobile/chrome/content/content.js
+++ b/mobile/chrome/content/content.js
@@ -219,125 +219,41 @@ function getContentClientRects(aElement)
                   top: r.top + offset.y,
                   width: r.width,
                   height: r.height
                 });
   }
   return result;
 };
 
-/**
- * Responsible for sending messages about security, location, and page load state.
- * @param loadingController Object with methods startLoading and stopLoading
- */
-function ProgressController(loadingController) {
-  this._webNavigation = docShell.QueryInterface(Ci.nsIWebNavigation);
-  this._overrideService = null;
-  this._hostChanged = false;
-  this._state = null;
-  this._loadingController = loadingController || this._defaultLoadingController;
-}
 
-ProgressController.prototype = {
-  // Default loading callbacks do nothing
-  _defaultLoadingController: {
-    startLoading: function() {},
-    stopLoading: function() {}
-  },
-
-  onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
-    // ignore notification that aren't about the main document (iframes, etc)
-    let win = aWebProgress.DOMWindow;
-    if (win != win.parent)
-      return;
+let Content = {
+  init: function init() {
+    this._isZoomedToElement = false;
 
-    // If you want to observe other state flags, be sure they're listed in the
-    // Tab._createBrowser's call to addProgressListener
-    if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
-      if (aStateFlags & Ci.nsIWebProgressListener.STATE_START) {
-        this._loadingController.startLoading();
-      }
-      else if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
-        this._loadingController.stopLoading();
-      }
-    }
-  },
+    addMessageListener("Browser:Blur", this);
+    addMessageListener("Browser:KeyEvent", this);
+    addMessageListener("Browser:MouseOver", this);
+    addMessageListener("Browser:MouseLong", this);
+    addMessageListener("Browser:MouseDown", this);
+    addMessageListener("Browser:MouseUp", this);
+    addMessageListener("Browser:SaveAs", this);
+    addMessageListener("Browser:ZoomToPoint", this);
+    addMessageListener("Browser:MozApplicationCache:Fetch", this);
 
-  /** This method is called to indicate progress changes for the currently loading page. */
-  onProgressChange: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
-  },
+    if (Util.isParentProcess())
+      addEventListener("DOMActivate", this, true);
 
-  /** This method is called to indicate a change to the current location. */
-  onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocationURI) {
-  },
+    addEventListener("MozApplicationManifest", this, false);
+    addEventListener("command", this, false);
+    addEventListener("pagehide", this, false);
 
-  /**
-   * This method is called to indicate a status changes for the currently
-   * loading page.  The message is already formatted for display.
-   */
-  onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
+    this._formAssistant = new FormAssistant();
   },
 
-  /** This method is called when the security state of the browser changes. */
-  onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) {
-  },
-
-  QueryInterface: function QueryInterface(aIID) {
-    if (aIID.equals(Ci.nsIWebProgressListener) ||
-        aIID.equals(Ci.nsISupportsWeakReference) ||
-        aIID.equals(Ci.nsISupports)) {
-        return this;
-    }
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
-
-  start: function start() {
-    let flags = Ci.nsIWebProgress.NOTIFY_STATE_NETWORK;
-    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-      .getInterface(Ci.nsIWebProgress);
-    webProgress.addProgressListener(this, flags);
-  },
-
-  stop: function stop() {
-    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
-      .getInterface(Ci.nsIWebProgress);
-    webProgress.removeProgressListener(this);
-  }
-};
-
-
-/** Can't think of a good description of this class.  It probably does too much? */
-function Content() {
-  this._isZoomedToElement = false;
-
-  addMessageListener("Browser:Blur", this);
-  addMessageListener("Browser:KeyEvent", this);
-  addMessageListener("Browser:MouseOver", this);
-  addMessageListener("Browser:MouseDown", this);
-  addMessageListener("Browser:MouseLong", this);
-  addMessageListener("Browser:MouseUp", this);
-  addMessageListener("Browser:SaveAs", this);
-  addMessageListener("Browser:ZoomToPoint", this);
-  addMessageListener("Browser:MozApplicationCache:Fetch", this);
-
-  if (Util.isParentProcess())
-    addEventListener("DOMActivate", this, true);
-
-  addEventListener("MozApplicationManifest", this, false);
-  addEventListener("command", this, false);
-  addEventListener("pagehide", this, false);
-
-  this._progressController = new ProgressController(this);
-  this._progressController.start();
-
-  this._formAssistant = new FormAssistant();
-}
-
-Content.prototype = {
   handleEvent: function handleEvent(aEvent) {
     switch (aEvent.type) {
       case "DOMActivate": {
         // In a local tab, open remote links in new tabs.
         let target = aEvent.originalTarget;
         let href = Util.getHrefForElement(target);
         if (/^http(s?):/.test(href)) {
           aEvent.preventDefault();
@@ -365,17 +281,17 @@ Content.prototype = {
 
         let ot = aEvent.originalTarget;
         let errorDoc = ot.ownerDocument;
 
         // If the event came from an ssl error page, it is probably either the "Add
         // Exception…" or "Get me out of here!" button
         if (/^about:certerror\?e=nssBadCert/.test(errorDoc.documentURI)) {
           let perm = errorDoc.getElementById("permanentExceptionButton");
-          let temp = errorDoc.getElementById("temporaryExceptionButton"); 
+          let temp = errorDoc.getElementById("temporaryExceptionButton");
           if (ot == temp || ot == perm) {
             let action = (ot == perm ? "permanent" : "temporary");
             sendAsyncMessage("Browser:CertException", { url: errorDoc.location.href, action: action });
           }
           else if (ot == errorDoc.getElementById("getMeOutOfHereButton")) {
             sendAsyncMessage("Browser:CertException", { url: errorDoc.location.href, action: "leave" });
           }
         }
@@ -596,28 +512,20 @@ Content.prototype = {
     let scrollOffset = Util.getScrollOffset(content);
     let windowUtils = Util.getWindowUtils(content);
     windowUtils.sendMouseEventToWindow(aName, aX - scrollOffset.x, aY - scrollOffset.y, 0, 1, 0, true);
   },
 
   _setTextZoom: function _setTextZoom(aZoom) {
     let viewer = docShell.contentViewer.QueryInterface(Ci.nsIMarkupDocumentViewer);
     viewer.textZoom = aZoom;
-  },
-
-  startLoading: function startLoading() {
-    this._loading = true;
-  },
-
-  stopLoading: function stopLoading() {
-    this._loading = false;
-  },
+  }
 };
 
-let contentObject = new Content();
+Content.init();
 
 let ViewportHandler = {
   init: function init() {
     addEventListener("DOMWindowCreated", this, false);
     addEventListener("DOMMetaAdded", this, false);
     addEventListener("DOMContentLoaded", this, false);
     addEventListener("pageshow", this, false);
   },
--- a/mobile/chrome/tests/browser_navigation.js
+++ b/mobile/chrome/tests/browser_navigation.js
@@ -357,42 +357,42 @@ gTests.push({
     gCurrentTest._currentTab = BrowserUI.newTab(testURL_01);
     waitFor(gCurrentTest.onPageReady, pageLoaded(testURL_01));
   },
 
   onPageReady: function() {
     ok(back.disabled, "Can't go back");
     ok(forward.disabled, "Can't go forward");
 
-    messageManager.addMessageListener("WebProgress:LocationChange", gCurrentTest.onFragmentLoaded);
+    messageManager.addMessageListener("Content:LocationChange", gCurrentTest.onFragmentLoaded);
     Browser.loadURI(testURL_01 + "#fragment");
   },
 
   onFragmentLoaded: function() {
-    messageManager.removeMessageListener("WebProgress:LocationChange", arguments.callee);
+    messageManager.removeMessageListener("Content:LocationChange", arguments.callee);
 
     ok(!back.disabled, "Can go back");
     ok(forward.disabled, "Can't go forward");
 
-    messageManager.addMessageListener("WebProgress:LocationChange", gCurrentTest.onBack);
+    messageManager.addMessageListener("Content:LocationChange", gCurrentTest.onBack);
     CommandUpdater.doCommand("cmd_back");
   },
 
   onBack: function() {
-    messageManager.removeMessageListener("WebProgress:LocationChange", arguments.callee);
+    messageManager.removeMessageListener("Content:LocationChange", arguments.callee);
 
     ok(back.disabled, "Can't go back");
     ok(!forward.disabled, "Can go forward");
 
-    messageManager.addMessageListener("WebProgress:LocationChange", gCurrentTest.onForward);
+    messageManager.addMessageListener("Content:LocationChange", gCurrentTest.onForward);
     CommandUpdater.doCommand("cmd_forward");
   },
 
   onForward: function() {
-    messageManager.removeMessageListener("WebProgress:LocationChange", arguments.callee);
+    messageManager.removeMessageListener("Content:LocationChange", arguments.callee);
 
     ok(!back.disabled, "Can go back");
     ok(forward.disabled, "Can't go forward");
 
     gCurrentTest.finish();
   },
 
   finish: function() {
--- a/mobile/chrome/tests/remote_forms.js
+++ b/mobile/chrome/tests/remote_forms.js
@@ -1,11 +1,11 @@
 dump("====================== Content Script Loaded =======================\n");
 
-let assistant = contentObject._formAssistant;
+let assistant = Content._formAssistant;
 
 // Copied from http://mxr.mozilla.org/mozilla-central/source/testing/mochitest/tests/SimpleTest/EventUtils.js
 // except the netscape.security.PrivilegeManager.enablePrivilege call
 function sendMouseEvent(aEvent, aTarget, aWindow) {
   if (['click', 'mousedown', 'mouseup', 'mouseover', 'mouseout'].indexOf(aEvent.type) == -1) {
     throw new Error("sendMouseEvent doesn't know about event type '" + aEvent.type + "'");
   }