|
1 // -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
|
2 // This Source Code Form is subject to the terms of the Mozilla Public |
|
3 // License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 // file, You can obtain one at http://mozilla.org/MPL/2.0/. |
|
5 |
|
6 this.EXPORTED_SYMBOLS = ["RemoteWebProgressManager"]; |
|
7 |
|
8 const Ci = Components.interfaces; |
|
9 const Cc = Components.classes; |
|
10 const Cu = Components.utils; |
|
11 |
|
12 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
13 |
|
14 function newURI(spec) |
|
15 { |
|
16 return Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService) |
|
17 .newURI(spec, null, null); |
|
18 } |
|
19 |
|
20 function RemoteWebProgressRequest(spec) |
|
21 { |
|
22 this.uri = newURI(spec); |
|
23 } |
|
24 |
|
25 RemoteWebProgressRequest.prototype = { |
|
26 QueryInterface : XPCOMUtils.generateQI([Ci.nsIChannel]), |
|
27 |
|
28 get URI() { return this.uri.clone(); } |
|
29 }; |
|
30 |
|
31 function RemoteWebProgress(aManager, aIsTopLevel) { |
|
32 this._manager = aManager; |
|
33 |
|
34 this._isLoadingDocument = false; |
|
35 this._DOMWindow = null; |
|
36 this._isTopLevel = aIsTopLevel; |
|
37 this._loadType = 0; |
|
38 } |
|
39 |
|
40 RemoteWebProgress.prototype = { |
|
41 NOTIFY_STATE_REQUEST: 0x00000001, |
|
42 NOTIFY_STATE_DOCUMENT: 0x00000002, |
|
43 NOTIFY_STATE_NETWORK: 0x00000004, |
|
44 NOTIFY_STATE_WINDOW: 0x00000008, |
|
45 NOTIFY_STATE_ALL: 0x0000000f, |
|
46 NOTIFY_PROGRESS: 0x00000010, |
|
47 NOTIFY_STATUS: 0x00000020, |
|
48 NOTIFY_SECURITY: 0x00000040, |
|
49 NOTIFY_LOCATION: 0x00000080, |
|
50 NOTIFY_REFRESH: 0x00000100, |
|
51 NOTIFY_ALL: 0x000001ff, |
|
52 |
|
53 get isLoadingDocument() { return this._isLoadingDocument }, |
|
54 get DOMWindow() { return this._DOMWindow; }, |
|
55 get DOMWindowID() { return 0; }, |
|
56 get isTopLevel() { return this._isTopLevel }, |
|
57 get loadType() { return this._loadType; }, |
|
58 |
|
59 addProgressListener: function (aListener) { |
|
60 this._manager.addProgressListener(aListener); |
|
61 }, |
|
62 |
|
63 removeProgressListener: function (aListener) { |
|
64 this._manager.removeProgressListener(aListener); |
|
65 } |
|
66 }; |
|
67 |
|
68 function RemoteWebProgressManager (aBrowser) { |
|
69 this._browser = aBrowser; |
|
70 this._topLevelWebProgress = new RemoteWebProgress(this, true); |
|
71 this._progressListeners = []; |
|
72 |
|
73 this._browser.messageManager.addMessageListener("Content:StateChange", this); |
|
74 this._browser.messageManager.addMessageListener("Content:LocationChange", this); |
|
75 this._browser.messageManager.addMessageListener("Content:SecurityChange", this); |
|
76 this._browser.messageManager.addMessageListener("Content:StatusChange", this); |
|
77 } |
|
78 |
|
79 RemoteWebProgressManager.prototype = { |
|
80 get topLevelWebProgress() { |
|
81 return this._topLevelWebProgress; |
|
82 }, |
|
83 |
|
84 addProgressListener: function (aListener) { |
|
85 let listener = aListener.QueryInterface(Ci.nsIWebProgressListener); |
|
86 this._progressListeners.push(listener); |
|
87 }, |
|
88 |
|
89 removeProgressListener: function (aListener) { |
|
90 this._progressListeners = |
|
91 this._progressListeners.filter(l => l != aListener); |
|
92 }, |
|
93 |
|
94 _fixSSLStatusAndState: function (aStatus, aState) { |
|
95 let deserialized = null; |
|
96 if (aStatus) { |
|
97 let helper = Cc["@mozilla.org/network/serialization-helper;1"] |
|
98 .getService(Components.interfaces.nsISerializationHelper); |
|
99 |
|
100 deserialized = helper.deserializeObject(aStatus) |
|
101 deserialized.QueryInterface(Ci.nsISSLStatus); |
|
102 } |
|
103 |
|
104 // We must check the Extended Validation (EV) state here, on the chrome |
|
105 // process, because NSS is needed for that determination. |
|
106 if (deserialized && deserialized.isExtendedValidation) |
|
107 aState |= Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL; |
|
108 |
|
109 return [deserialized, aState]; |
|
110 }, |
|
111 |
|
112 setCurrentURI: function (aURI) { |
|
113 // This function is simpler than nsDocShell::SetCurrentURI since |
|
114 // it doesn't have to deal with child docshells. |
|
115 let webNavigation = this._browser.webNavigation; |
|
116 webNavigation._currentURI = aURI; |
|
117 |
|
118 let webProgress = this.topLevelWebProgress; |
|
119 for (let p of this._progressListeners) { |
|
120 p.onLocationChange(webProgress, null, aURI); |
|
121 } |
|
122 }, |
|
123 |
|
124 _callProgressListeners: function(methodName, ...args) { |
|
125 for (let p of this._progressListeners) { |
|
126 if (p[methodName]) { |
|
127 try { |
|
128 p[methodName].apply(p, args); |
|
129 } catch (ex) { |
|
130 Cu.reportError("RemoteWebProgress failed to call " + methodName + ": " + ex + "\n"); |
|
131 } |
|
132 } |
|
133 } |
|
134 }, |
|
135 |
|
136 receiveMessage: function (aMessage) { |
|
137 let json = aMessage.json; |
|
138 let objects = aMessage.objects; |
|
139 |
|
140 // The top-level WebProgress is always the same, but because we don't |
|
141 // really have a concept of subframes/content we always creat a new object |
|
142 // for those. |
|
143 let webProgress = json.isTopLevel ? this._topLevelWebProgress |
|
144 : new RemoteWebProgress(this, false); |
|
145 |
|
146 // The WebProgressRequest object however is always dynamic. |
|
147 let request = json.requestURI ? new RemoteWebProgressRequest(json.requestURI) |
|
148 : null; |
|
149 |
|
150 // Update the actual WebProgress fields. |
|
151 webProgress._isLoadingDocument = json.isLoadingDocument; |
|
152 webProgress._DOMWindow = objects.DOMWindow; |
|
153 webProgress._loadType = json.loadType; |
|
154 |
|
155 if (json.isTopLevel) { |
|
156 this._browser._contentWindow = objects.contentWindow; |
|
157 this._browser._documentContentType = json.documentContentType; |
|
158 } |
|
159 |
|
160 switch (aMessage.name) { |
|
161 case "Content:StateChange": |
|
162 this._callProgressListeners("onStateChange", webProgress, request, json.stateFlags, json.status); |
|
163 break; |
|
164 |
|
165 case "Content:LocationChange": |
|
166 let location = newURI(json.location); |
|
167 let flags = json.flags; |
|
168 |
|
169 if (json.isTopLevel) { |
|
170 this._browser.webNavigation._currentURI = location; |
|
171 this._browser.webNavigation.canGoBack = json.canGoBack; |
|
172 this._browser.webNavigation.canGoForward = json.canGoForward; |
|
173 this._browser._characterSet = json.charset; |
|
174 this._browser._documentURI = newURI(json.documentURI); |
|
175 this._browser._imageDocument = null; |
|
176 } |
|
177 |
|
178 this._callProgressListeners("onLocationChange", webProgress, request, location, flags); |
|
179 break; |
|
180 |
|
181 case "Content:SecurityChange": |
|
182 let [status, state] = this._fixSSLStatusAndState(json.status, json.state); |
|
183 |
|
184 if (json.isTopLevel) { |
|
185 // Invoking this getter triggers the generation of the underlying object, |
|
186 // which we need to access with ._securityUI, because .securityUI returns |
|
187 // a wrapper that makes _update inaccessible. |
|
188 void this._browser.securityUI; |
|
189 this._browser._securityUI._update(status, state); |
|
190 } |
|
191 |
|
192 this._callProgressListeners("onSecurityChange", webProgress, request, state); |
|
193 break; |
|
194 |
|
195 case "Content:StatusChange": |
|
196 this._callProgressListeners("onStatusChange", webProgress, request, json.status, json.message); |
|
197 break; |
|
198 } |
|
199 } |
|
200 }; |