1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/modules/RemoteWebProgress.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,200 @@ 1.4 +// -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 +// This Source Code Form is subject to the terms of the Mozilla Public 1.6 +// License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.8 + 1.9 +this.EXPORTED_SYMBOLS = ["RemoteWebProgressManager"]; 1.10 + 1.11 +const Ci = Components.interfaces; 1.12 +const Cc = Components.classes; 1.13 +const Cu = Components.utils; 1.14 + 1.15 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.16 + 1.17 +function newURI(spec) 1.18 +{ 1.19 + return Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService) 1.20 + .newURI(spec, null, null); 1.21 +} 1.22 + 1.23 +function RemoteWebProgressRequest(spec) 1.24 +{ 1.25 + this.uri = newURI(spec); 1.26 +} 1.27 + 1.28 +RemoteWebProgressRequest.prototype = { 1.29 + QueryInterface : XPCOMUtils.generateQI([Ci.nsIChannel]), 1.30 + 1.31 + get URI() { return this.uri.clone(); } 1.32 +}; 1.33 + 1.34 +function RemoteWebProgress(aManager, aIsTopLevel) { 1.35 + this._manager = aManager; 1.36 + 1.37 + this._isLoadingDocument = false; 1.38 + this._DOMWindow = null; 1.39 + this._isTopLevel = aIsTopLevel; 1.40 + this._loadType = 0; 1.41 +} 1.42 + 1.43 +RemoteWebProgress.prototype = { 1.44 + NOTIFY_STATE_REQUEST: 0x00000001, 1.45 + NOTIFY_STATE_DOCUMENT: 0x00000002, 1.46 + NOTIFY_STATE_NETWORK: 0x00000004, 1.47 + NOTIFY_STATE_WINDOW: 0x00000008, 1.48 + NOTIFY_STATE_ALL: 0x0000000f, 1.49 + NOTIFY_PROGRESS: 0x00000010, 1.50 + NOTIFY_STATUS: 0x00000020, 1.51 + NOTIFY_SECURITY: 0x00000040, 1.52 + NOTIFY_LOCATION: 0x00000080, 1.53 + NOTIFY_REFRESH: 0x00000100, 1.54 + NOTIFY_ALL: 0x000001ff, 1.55 + 1.56 + get isLoadingDocument() { return this._isLoadingDocument }, 1.57 + get DOMWindow() { return this._DOMWindow; }, 1.58 + get DOMWindowID() { return 0; }, 1.59 + get isTopLevel() { return this._isTopLevel }, 1.60 + get loadType() { return this._loadType; }, 1.61 + 1.62 + addProgressListener: function (aListener) { 1.63 + this._manager.addProgressListener(aListener); 1.64 + }, 1.65 + 1.66 + removeProgressListener: function (aListener) { 1.67 + this._manager.removeProgressListener(aListener); 1.68 + } 1.69 +}; 1.70 + 1.71 +function RemoteWebProgressManager (aBrowser) { 1.72 + this._browser = aBrowser; 1.73 + this._topLevelWebProgress = new RemoteWebProgress(this, true); 1.74 + this._progressListeners = []; 1.75 + 1.76 + this._browser.messageManager.addMessageListener("Content:StateChange", this); 1.77 + this._browser.messageManager.addMessageListener("Content:LocationChange", this); 1.78 + this._browser.messageManager.addMessageListener("Content:SecurityChange", this); 1.79 + this._browser.messageManager.addMessageListener("Content:StatusChange", this); 1.80 +} 1.81 + 1.82 +RemoteWebProgressManager.prototype = { 1.83 + get topLevelWebProgress() { 1.84 + return this._topLevelWebProgress; 1.85 + }, 1.86 + 1.87 + addProgressListener: function (aListener) { 1.88 + let listener = aListener.QueryInterface(Ci.nsIWebProgressListener); 1.89 + this._progressListeners.push(listener); 1.90 + }, 1.91 + 1.92 + removeProgressListener: function (aListener) { 1.93 + this._progressListeners = 1.94 + this._progressListeners.filter(l => l != aListener); 1.95 + }, 1.96 + 1.97 + _fixSSLStatusAndState: function (aStatus, aState) { 1.98 + let deserialized = null; 1.99 + if (aStatus) { 1.100 + let helper = Cc["@mozilla.org/network/serialization-helper;1"] 1.101 + .getService(Components.interfaces.nsISerializationHelper); 1.102 + 1.103 + deserialized = helper.deserializeObject(aStatus) 1.104 + deserialized.QueryInterface(Ci.nsISSLStatus); 1.105 + } 1.106 + 1.107 + // We must check the Extended Validation (EV) state here, on the chrome 1.108 + // process, because NSS is needed for that determination. 1.109 + if (deserialized && deserialized.isExtendedValidation) 1.110 + aState |= Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL; 1.111 + 1.112 + return [deserialized, aState]; 1.113 + }, 1.114 + 1.115 + setCurrentURI: function (aURI) { 1.116 + // This function is simpler than nsDocShell::SetCurrentURI since 1.117 + // it doesn't have to deal with child docshells. 1.118 + let webNavigation = this._browser.webNavigation; 1.119 + webNavigation._currentURI = aURI; 1.120 + 1.121 + let webProgress = this.topLevelWebProgress; 1.122 + for (let p of this._progressListeners) { 1.123 + p.onLocationChange(webProgress, null, aURI); 1.124 + } 1.125 + }, 1.126 + 1.127 + _callProgressListeners: function(methodName, ...args) { 1.128 + for (let p of this._progressListeners) { 1.129 + if (p[methodName]) { 1.130 + try { 1.131 + p[methodName].apply(p, args); 1.132 + } catch (ex) { 1.133 + Cu.reportError("RemoteWebProgress failed to call " + methodName + ": " + ex + "\n"); 1.134 + } 1.135 + } 1.136 + } 1.137 + }, 1.138 + 1.139 + receiveMessage: function (aMessage) { 1.140 + let json = aMessage.json; 1.141 + let objects = aMessage.objects; 1.142 + 1.143 + // The top-level WebProgress is always the same, but because we don't 1.144 + // really have a concept of subframes/content we always creat a new object 1.145 + // for those. 1.146 + let webProgress = json.isTopLevel ? this._topLevelWebProgress 1.147 + : new RemoteWebProgress(this, false); 1.148 + 1.149 + // The WebProgressRequest object however is always dynamic. 1.150 + let request = json.requestURI ? new RemoteWebProgressRequest(json.requestURI) 1.151 + : null; 1.152 + 1.153 + // Update the actual WebProgress fields. 1.154 + webProgress._isLoadingDocument = json.isLoadingDocument; 1.155 + webProgress._DOMWindow = objects.DOMWindow; 1.156 + webProgress._loadType = json.loadType; 1.157 + 1.158 + if (json.isTopLevel) { 1.159 + this._browser._contentWindow = objects.contentWindow; 1.160 + this._browser._documentContentType = json.documentContentType; 1.161 + } 1.162 + 1.163 + switch (aMessage.name) { 1.164 + case "Content:StateChange": 1.165 + this._callProgressListeners("onStateChange", webProgress, request, json.stateFlags, json.status); 1.166 + break; 1.167 + 1.168 + case "Content:LocationChange": 1.169 + let location = newURI(json.location); 1.170 + let flags = json.flags; 1.171 + 1.172 + if (json.isTopLevel) { 1.173 + this._browser.webNavigation._currentURI = location; 1.174 + this._browser.webNavigation.canGoBack = json.canGoBack; 1.175 + this._browser.webNavigation.canGoForward = json.canGoForward; 1.176 + this._browser._characterSet = json.charset; 1.177 + this._browser._documentURI = newURI(json.documentURI); 1.178 + this._browser._imageDocument = null; 1.179 + } 1.180 + 1.181 + this._callProgressListeners("onLocationChange", webProgress, request, location, flags); 1.182 + break; 1.183 + 1.184 + case "Content:SecurityChange": 1.185 + let [status, state] = this._fixSSLStatusAndState(json.status, json.state); 1.186 + 1.187 + if (json.isTopLevel) { 1.188 + // Invoking this getter triggers the generation of the underlying object, 1.189 + // which we need to access with ._securityUI, because .securityUI returns 1.190 + // a wrapper that makes _update inaccessible. 1.191 + void this._browser.securityUI; 1.192 + this._browser._securityUI._update(status, state); 1.193 + } 1.194 + 1.195 + this._callProgressListeners("onSecurityChange", webProgress, request, state); 1.196 + break; 1.197 + 1.198 + case "Content:StatusChange": 1.199 + this._callProgressListeners("onStatusChange", webProgress, request, json.status, json.message); 1.200 + break; 1.201 + } 1.202 + } 1.203 +};