michael@0: // -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: // This Source Code Form is subject to the terms of the Mozilla Public michael@0: // License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: // file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: this.EXPORTED_SYMBOLS = ["RemoteWebProgressManager"]; michael@0: michael@0: const Ci = Components.interfaces; michael@0: const Cc = Components.classes; michael@0: const Cu = Components.utils; michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: michael@0: function newURI(spec) michael@0: { michael@0: return Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService) michael@0: .newURI(spec, null, null); michael@0: } michael@0: michael@0: function RemoteWebProgressRequest(spec) michael@0: { michael@0: this.uri = newURI(spec); michael@0: } michael@0: michael@0: RemoteWebProgressRequest.prototype = { michael@0: QueryInterface : XPCOMUtils.generateQI([Ci.nsIChannel]), michael@0: michael@0: get URI() { return this.uri.clone(); } michael@0: }; michael@0: michael@0: function RemoteWebProgress(aManager, aIsTopLevel) { michael@0: this._manager = aManager; michael@0: michael@0: this._isLoadingDocument = false; michael@0: this._DOMWindow = null; michael@0: this._isTopLevel = aIsTopLevel; michael@0: this._loadType = 0; michael@0: } michael@0: michael@0: RemoteWebProgress.prototype = { michael@0: NOTIFY_STATE_REQUEST: 0x00000001, michael@0: NOTIFY_STATE_DOCUMENT: 0x00000002, michael@0: NOTIFY_STATE_NETWORK: 0x00000004, michael@0: NOTIFY_STATE_WINDOW: 0x00000008, michael@0: NOTIFY_STATE_ALL: 0x0000000f, michael@0: NOTIFY_PROGRESS: 0x00000010, michael@0: NOTIFY_STATUS: 0x00000020, michael@0: NOTIFY_SECURITY: 0x00000040, michael@0: NOTIFY_LOCATION: 0x00000080, michael@0: NOTIFY_REFRESH: 0x00000100, michael@0: NOTIFY_ALL: 0x000001ff, michael@0: michael@0: get isLoadingDocument() { return this._isLoadingDocument }, michael@0: get DOMWindow() { return this._DOMWindow; }, michael@0: get DOMWindowID() { return 0; }, michael@0: get isTopLevel() { return this._isTopLevel }, michael@0: get loadType() { return this._loadType; }, michael@0: michael@0: addProgressListener: function (aListener) { michael@0: this._manager.addProgressListener(aListener); michael@0: }, michael@0: michael@0: removeProgressListener: function (aListener) { michael@0: this._manager.removeProgressListener(aListener); michael@0: } michael@0: }; michael@0: michael@0: function RemoteWebProgressManager (aBrowser) { michael@0: this._browser = aBrowser; michael@0: this._topLevelWebProgress = new RemoteWebProgress(this, true); michael@0: this._progressListeners = []; michael@0: michael@0: this._browser.messageManager.addMessageListener("Content:StateChange", this); michael@0: this._browser.messageManager.addMessageListener("Content:LocationChange", this); michael@0: this._browser.messageManager.addMessageListener("Content:SecurityChange", this); michael@0: this._browser.messageManager.addMessageListener("Content:StatusChange", this); michael@0: } michael@0: michael@0: RemoteWebProgressManager.prototype = { michael@0: get topLevelWebProgress() { michael@0: return this._topLevelWebProgress; michael@0: }, michael@0: michael@0: addProgressListener: function (aListener) { michael@0: let listener = aListener.QueryInterface(Ci.nsIWebProgressListener); michael@0: this._progressListeners.push(listener); michael@0: }, michael@0: michael@0: removeProgressListener: function (aListener) { michael@0: this._progressListeners = michael@0: this._progressListeners.filter(l => l != aListener); michael@0: }, michael@0: michael@0: _fixSSLStatusAndState: function (aStatus, aState) { michael@0: let deserialized = null; michael@0: if (aStatus) { michael@0: let helper = Cc["@mozilla.org/network/serialization-helper;1"] michael@0: .getService(Components.interfaces.nsISerializationHelper); michael@0: michael@0: deserialized = helper.deserializeObject(aStatus) michael@0: deserialized.QueryInterface(Ci.nsISSLStatus); michael@0: } michael@0: michael@0: // We must check the Extended Validation (EV) state here, on the chrome michael@0: // process, because NSS is needed for that determination. michael@0: if (deserialized && deserialized.isExtendedValidation) michael@0: aState |= Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL; michael@0: michael@0: return [deserialized, aState]; michael@0: }, michael@0: michael@0: setCurrentURI: function (aURI) { michael@0: // This function is simpler than nsDocShell::SetCurrentURI since michael@0: // it doesn't have to deal with child docshells. michael@0: let webNavigation = this._browser.webNavigation; michael@0: webNavigation._currentURI = aURI; michael@0: michael@0: let webProgress = this.topLevelWebProgress; michael@0: for (let p of this._progressListeners) { michael@0: p.onLocationChange(webProgress, null, aURI); michael@0: } michael@0: }, michael@0: michael@0: _callProgressListeners: function(methodName, ...args) { michael@0: for (let p of this._progressListeners) { michael@0: if (p[methodName]) { michael@0: try { michael@0: p[methodName].apply(p, args); michael@0: } catch (ex) { michael@0: Cu.reportError("RemoteWebProgress failed to call " + methodName + ": " + ex + "\n"); michael@0: } michael@0: } michael@0: } michael@0: }, michael@0: michael@0: receiveMessage: function (aMessage) { michael@0: let json = aMessage.json; michael@0: let objects = aMessage.objects; michael@0: michael@0: // The top-level WebProgress is always the same, but because we don't michael@0: // really have a concept of subframes/content we always creat a new object michael@0: // for those. michael@0: let webProgress = json.isTopLevel ? this._topLevelWebProgress michael@0: : new RemoteWebProgress(this, false); michael@0: michael@0: // The WebProgressRequest object however is always dynamic. michael@0: let request = json.requestURI ? new RemoteWebProgressRequest(json.requestURI) michael@0: : null; michael@0: michael@0: // Update the actual WebProgress fields. michael@0: webProgress._isLoadingDocument = json.isLoadingDocument; michael@0: webProgress._DOMWindow = objects.DOMWindow; michael@0: webProgress._loadType = json.loadType; michael@0: michael@0: if (json.isTopLevel) { michael@0: this._browser._contentWindow = objects.contentWindow; michael@0: this._browser._documentContentType = json.documentContentType; michael@0: } michael@0: michael@0: switch (aMessage.name) { michael@0: case "Content:StateChange": michael@0: this._callProgressListeners("onStateChange", webProgress, request, json.stateFlags, json.status); michael@0: break; michael@0: michael@0: case "Content:LocationChange": michael@0: let location = newURI(json.location); michael@0: let flags = json.flags; michael@0: michael@0: if (json.isTopLevel) { michael@0: this._browser.webNavigation._currentURI = location; michael@0: this._browser.webNavigation.canGoBack = json.canGoBack; michael@0: this._browser.webNavigation.canGoForward = json.canGoForward; michael@0: this._browser._characterSet = json.charset; michael@0: this._browser._documentURI = newURI(json.documentURI); michael@0: this._browser._imageDocument = null; michael@0: } michael@0: michael@0: this._callProgressListeners("onLocationChange", webProgress, request, location, flags); michael@0: break; michael@0: michael@0: case "Content:SecurityChange": michael@0: let [status, state] = this._fixSSLStatusAndState(json.status, json.state); michael@0: michael@0: if (json.isTopLevel) { michael@0: // Invoking this getter triggers the generation of the underlying object, michael@0: // which we need to access with ._securityUI, because .securityUI returns michael@0: // a wrapper that makes _update inaccessible. michael@0: void this._browser.securityUI; michael@0: this._browser._securityUI._update(status, state); michael@0: } michael@0: michael@0: this._callProgressListeners("onSecurityChange", webProgress, request, state); michael@0: break; michael@0: michael@0: case "Content:StatusChange": michael@0: this._callProgressListeners("onStatusChange", webProgress, request, json.status, json.message); michael@0: break; michael@0: } michael@0: } michael@0: };