summaryrefslogtreecommitdiff
path: root/toolkit/modules/RemoteWebProgress.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/modules/RemoteWebProgress.jsm')
-rw-r--r--toolkit/modules/RemoteWebProgress.jsm285
1 files changed, 285 insertions, 0 deletions
diff --git a/toolkit/modules/RemoteWebProgress.jsm b/toolkit/modules/RemoteWebProgress.jsm
new file mode 100644
index 0000000000..0031d6b98c
--- /dev/null
+++ b/toolkit/modules/RemoteWebProgress.jsm
@@ -0,0 +1,285 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 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 = ["RemoteWebProgressManager"];
+
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function newURI(spec)
+{
+ return Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService)
+ .newURI(spec, null, null);
+}
+
+function RemoteWebProgressRequest(spec, originalSpec, requestCPOW)
+{
+ this.wrappedJSObject = this;
+
+ this._uri = newURI(spec);
+ this._originalURI = newURI(originalSpec);
+ this._requestCPOW = requestCPOW;
+}
+
+RemoteWebProgressRequest.prototype = {
+ QueryInterface : XPCOMUtils.generateQI([Ci.nsIChannel]),
+
+ get URI() { return this._uri.clone(); },
+ get originalURI() { return this._originalURI.clone(); }
+};
+
+function RemoteWebProgress(aManager, aIsTopLevel) {
+ this.wrappedJSObject = this;
+
+ this._manager = aManager;
+
+ this._isLoadingDocument = false;
+ this._DOMWindow = null;
+ this._DOMWindowID = 0;
+ this._isTopLevel = aIsTopLevel;
+ this._loadType = 0;
+}
+
+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,
+
+ get isLoadingDocument() { return this._isLoadingDocument },
+ get DOMWindow() { return this._DOMWindow; },
+ get DOMWindowID() { return this._DOMWindowID; },
+ get isTopLevel() { return this._isTopLevel },
+ get loadType() { return this._loadType; },
+
+ addProgressListener: function (aListener) {
+ this._manager.addProgressListener(aListener);
+ },
+
+ removeProgressListener: function (aListener) {
+ this._manager.removeProgressListener(aListener);
+ }
+};
+
+function RemoteWebProgressManager (aBrowser) {
+ this._topLevelWebProgress = new RemoteWebProgress(this, true);
+ this._progressListeners = [];
+
+ this.swapBrowser(aBrowser);
+}
+
+RemoteWebProgressManager.argumentsForAddonListener = function(kind, args) {
+ function checkType(arg, typ) {
+ if (!arg) {
+ return false;
+ }
+ return (arg instanceof typ) ||
+ (arg instanceof Ci.nsISupports && arg.wrappedJSObject instanceof typ);
+ }
+
+ // Arguments for a tabs listener are shifted over one since the
+ // <browser> element is passed as the first argument.
+ let webProgressIndex = 0;
+ let requestIndex = 1;
+ if (kind == "tabs") {
+ webProgressIndex = 1;
+ requestIndex = 2;
+ }
+
+ if (checkType(args[webProgressIndex], RemoteWebProgress)) {
+ args[webProgressIndex] = args[webProgressIndex].wrappedJSObject._webProgressCPOW;
+ }
+
+ if (checkType(args[requestIndex], RemoteWebProgressRequest)) {
+ args[requestIndex] = args[requestIndex].wrappedJSObject._requestCPOW;
+ }
+
+ return args;
+};
+
+RemoteWebProgressManager.prototype = {
+ swapBrowser: function(aBrowser) {
+ if (this._messageManager) {
+ this._messageManager.removeMessageListener("Content:StateChange", this);
+ this._messageManager.removeMessageListener("Content:LocationChange", this);
+ this._messageManager.removeMessageListener("Content:SecurityChange", this);
+ this._messageManager.removeMessageListener("Content:StatusChange", this);
+ this._messageManager.removeMessageListener("Content:ProgressChange", this);
+ this._messageManager.removeMessageListener("Content:LoadURIResult", this);
+ }
+
+ this._browser = aBrowser;
+ this._messageManager = aBrowser.messageManager;
+ this._messageManager.addMessageListener("Content:StateChange", this);
+ this._messageManager.addMessageListener("Content:LocationChange", this);
+ this._messageManager.addMessageListener("Content:SecurityChange", this);
+ this._messageManager.addMessageListener("Content:StatusChange", this);
+ this._messageManager.addMessageListener("Content:ProgressChange", this);
+ this._messageManager.addMessageListener("Content:LoadURIResult", this);
+ },
+
+ get topLevelWebProgress() {
+ return this._topLevelWebProgress;
+ },
+
+ addProgressListener: function (aListener) {
+ let listener = aListener.QueryInterface(Ci.nsIWebProgressListener);
+ this._progressListeners.push(listener);
+ },
+
+ removeProgressListener: function (aListener) {
+ this._progressListeners =
+ this._progressListeners.filter(l => l != aListener);
+ },
+
+ _fixSSLStatusAndState: function (aStatus, aState) {
+ let deserialized = null;
+ if (aStatus) {
+ let helper = Cc["@mozilla.org/network/serialization-helper;1"]
+ .getService(Components.interfaces.nsISerializationHelper);
+
+ deserialized = helper.deserializeObject(aStatus)
+ deserialized.QueryInterface(Ci.nsISSLStatus);
+ }
+
+ return [deserialized, aState];
+ },
+
+ setCurrentURI: function (aURI) {
+ // This function is simpler than nsDocShell::SetCurrentURI since
+ // it doesn't have to deal with child docshells.
+ let remoteWebNav = this._browser._remoteWebNavigationImpl;
+ remoteWebNav._currentURI = aURI;
+
+ let webProgress = this.topLevelWebProgress;
+ for (let p of this._progressListeners) {
+ p.onLocationChange(webProgress, null, aURI);
+ }
+ },
+
+ _callProgressListeners: function(methodName, ...args) {
+ for (let p of this._progressListeners) {
+ if (p[methodName]) {
+ try {
+ p[methodName].apply(p, args);
+ } catch (ex) {
+ Cu.reportError("RemoteWebProgress failed to call " + methodName + ": " + ex + "\n");
+ }
+ }
+ }
+ },
+
+ receiveMessage: function (aMessage) {
+ let json = aMessage.json;
+ let objects = aMessage.objects;
+ // This message is a custom one we send as a result of a loadURI call.
+ // It shouldn't go through the same processing as all the forwarded
+ // webprogresslistener messages.
+ if (aMessage.name == "Content:LoadURIResult") {
+ this._browser.inLoadURI = false;
+ return;
+ }
+
+ let webProgress = null;
+ let isTopLevel = json.webProgress && json.webProgress.isTopLevel;
+ // The top-level WebProgress is always the same, but because we don't
+ // really have a concept of subframes/content we always create a new object
+ // for those.
+ if (json.webProgress) {
+ webProgress = isTopLevel ? this._topLevelWebProgress
+ : new RemoteWebProgress(this, false);
+
+ // Update the actual WebProgress fields.
+ webProgress._isLoadingDocument = json.webProgress.isLoadingDocument;
+ webProgress._DOMWindow = objects.DOMWindow;
+ webProgress._DOMWindowID = json.webProgress.DOMWindowID;
+ webProgress._loadType = json.webProgress.loadType;
+ webProgress._webProgressCPOW = objects.webProgress;
+ }
+
+ // The WebProgressRequest object however is always dynamic.
+ let request = null;
+ if (json.requestURI) {
+ request = new RemoteWebProgressRequest(json.requestURI,
+ json.originalRequestURI,
+ objects.request);
+ }
+
+ if (isTopLevel) {
+ this._browser._contentWindow = objects.contentWindow;
+ this._browser._documentContentType = json.documentContentType;
+ if (typeof json.inLoadURI != "undefined") {
+ this._browser.inLoadURI = json.inLoadURI;
+ }
+ if (json.charset) {
+ this._browser._characterSet = json.charset;
+ this._browser._mayEnableCharacterEncodingMenu = json.mayEnableCharacterEncodingMenu;
+ }
+ }
+
+ switch (aMessage.name) {
+ case "Content:StateChange":
+ if (isTopLevel) {
+ this._browser._documentURI = newURI(json.documentURI);
+ }
+ this._callProgressListeners("onStateChange", webProgress, request, json.stateFlags, json.status);
+ break;
+
+ case "Content:LocationChange":
+ let location = newURI(json.location);
+ let flags = json.flags;
+ let remoteWebNav = this._browser._remoteWebNavigationImpl;
+
+ // These properties can change even for a sub-frame navigation.
+ remoteWebNav.canGoBack = json.canGoBack;
+ remoteWebNav.canGoForward = json.canGoForward;
+
+ if (isTopLevel) {
+ remoteWebNav._currentURI = location;
+ this._browser._documentURI = newURI(json.documentURI);
+ this._browser._contentTitle = json.title;
+ this._browser._imageDocument = null;
+ this._browser._contentPrincipal = json.principal;
+ this._browser._isSyntheticDocument = json.synthetic;
+ this._browser._innerWindowID = json.innerWindowID;
+ }
+
+ this._callProgressListeners("onLocationChange", webProgress, request, location, flags);
+ break;
+
+ case "Content:SecurityChange":
+ let [status, state] = this._fixSSLStatusAndState(json.status, json.state);
+
+ if (isTopLevel) {
+ // Invoking this getter triggers the generation of the underlying object,
+ // which we need to access with ._securityUI, because .securityUI returns
+ // a wrapper that makes _update inaccessible.
+ void this._browser.securityUI;
+ this._browser._securityUI._update(status, state);
+ }
+
+ this._callProgressListeners("onSecurityChange", webProgress, request, state);
+ break;
+
+ case "Content:StatusChange":
+ this._callProgressListeners("onStatusChange", webProgress, request, json.status, json.message);
+ break;
+
+ case "Content:ProgressChange":
+ this._callProgressListeners("onProgressChange", webProgress, request, json.curSelf, json.maxSelf, json.curTotal, json.maxTotal);
+ break;
+ }
+ },
+};