|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 'use strict'; |
|
6 |
|
7 this.EXPORTED_SYMBOLS = ['ErrorPage']; |
|
8 |
|
9 const Cu = Components.utils; |
|
10 const Cc = Components.classes; |
|
11 const Ci = Components.interfaces; |
|
12 const kErrorPageFrameScript = 'chrome://b2g/content/ErrorPage.js'; |
|
13 |
|
14 Cu.import('resource://gre/modules/Services.jsm'); |
|
15 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
|
16 |
|
17 XPCOMUtils.defineLazyGetter(this, "CertOverrideService", function () { |
|
18 return Cc["@mozilla.org/security/certoverride;1"] |
|
19 .getService(Ci.nsICertOverrideService); |
|
20 }); |
|
21 |
|
22 /** |
|
23 * A class to add exceptions to override SSL certificate problems. |
|
24 * The functionality itself is borrowed from exceptionDialog.js. |
|
25 */ |
|
26 function SSLExceptions(aCallback, aUri, aWindow) { |
|
27 this._finishCallback = aCallback; |
|
28 this._uri = aUri; |
|
29 this._window = aWindow; |
|
30 }; |
|
31 |
|
32 SSLExceptions.prototype = { |
|
33 _finishCallback: null, |
|
34 _window: null, |
|
35 _uri: null, |
|
36 _temporary: null, |
|
37 _sslStatus: null, |
|
38 |
|
39 getInterface: function SSLE_getInterface(aIID) { |
|
40 return this.QueryInterface(aIID); |
|
41 }, |
|
42 |
|
43 QueryInterface: XPCOMUtils.generateQI([Ci.nsIBadCertListener2]), |
|
44 |
|
45 /** |
|
46 * To collect the SSL status we intercept the certificate error here |
|
47 * and store the status for later use. |
|
48 */ |
|
49 notifyCertProblem: function SSLE_notifyCertProblem(aSocketInfo, |
|
50 aSslStatus, |
|
51 aTargetHost) { |
|
52 this._sslStatus = aSslStatus.QueryInterface(Ci.nsISSLStatus); |
|
53 Services.tm.currentThread.dispatch({ |
|
54 run: this._addOverride.bind(this) |
|
55 }, Ci.nsIThread.DISPATCH_NORMAL); |
|
56 return true; // suppress error UI |
|
57 }, |
|
58 |
|
59 /** |
|
60 * Attempt to download the certificate for the location specified to get |
|
61 * the SSLState for the certificate and the errors. |
|
62 */ |
|
63 _checkCert: function SSLE_checkCert() { |
|
64 this._sslStatus = null; |
|
65 if (!this._uri) { |
|
66 return; |
|
67 } |
|
68 let req = new this._window.XMLHttpRequest(); |
|
69 try { |
|
70 req.open("GET", this._uri.prePath, true); |
|
71 req.channel.notificationCallbacks = this; |
|
72 let xhrHandler = (function() { |
|
73 req.removeEventListener("load", xhrHandler); |
|
74 req.removeEventListener("error", xhrHandler); |
|
75 if (!this._sslStatus) { |
|
76 // Got response from server without an SSL error. |
|
77 if (this._finishCallback) { |
|
78 this._finishCallback(); |
|
79 } |
|
80 } |
|
81 }).bind(this); |
|
82 req.addEventListener("load", xhrHandler); |
|
83 req.addEventListener("error", xhrHandler); |
|
84 req.send(null); |
|
85 } catch (e) { |
|
86 // We *expect* exceptions if there are problems with the certificate |
|
87 // presented by the site. Log it, just in case, but we can proceed here, |
|
88 // with appropriate sanity checks |
|
89 Components.utils.reportError("Attempted to connect to a site with a bad certificate in the add exception dialog. " + |
|
90 "This results in a (mostly harmless) exception being thrown. " + |
|
91 "Logged for information purposes only: " + e); |
|
92 } |
|
93 }, |
|
94 |
|
95 /** |
|
96 * Internal method to create an override. |
|
97 */ |
|
98 _addOverride: function SSLE_addOverride() { |
|
99 let SSLStatus = this._sslStatus; |
|
100 let uri = this._uri; |
|
101 let flags = 0; |
|
102 |
|
103 if (SSLStatus.isUntrusted) { |
|
104 flags |= Ci.nsICertOverrideService.ERROR_UNTRUSTED; |
|
105 } |
|
106 if (SSLStatus.isDomainMismatch) { |
|
107 flags |= Ci.nsICertOverrideService.ERROR_MISMATCH; |
|
108 } |
|
109 if (SSLStatus.isNotValidAtThisTime) { |
|
110 flags |= Ci.nsICertOverrideService.ERROR_TIME; |
|
111 } |
|
112 |
|
113 CertOverrideService.rememberValidityOverride( |
|
114 uri.asciiHost, |
|
115 uri.port, |
|
116 SSLStatus.serverCert, |
|
117 flags, |
|
118 this._temporary); |
|
119 |
|
120 if (this._finishCallback) { |
|
121 this._finishCallback(); |
|
122 } |
|
123 }, |
|
124 |
|
125 /** |
|
126 * Creates a permanent exception to override all overridable errors for |
|
127 * the given URL. |
|
128 */ |
|
129 addException: function SSLE_addException(aTemporary) { |
|
130 this._temporary = aTemporary; |
|
131 this._checkCert(); |
|
132 } |
|
133 }; |
|
134 |
|
135 let ErrorPage = { |
|
136 _addCertException: function(aMessage) { |
|
137 let frameLoaderOwner = aMessage.target.QueryInterface(Ci.nsIFrameLoaderOwner); |
|
138 let win = frameLoaderOwner.ownerDocument.defaultView; |
|
139 let mm = frameLoaderOwner.frameLoader.messageManager; |
|
140 |
|
141 let uri = Services.io.newURI(aMessage.data.url, null, null); |
|
142 let sslExceptions = new SSLExceptions((function() { |
|
143 mm.sendAsyncMessage('ErrorPage:ReloadPage'); |
|
144 }).bind(this), uri, win); |
|
145 try { |
|
146 sslExceptions.addException(!aMessage.data.isPermanent); |
|
147 } catch (e) { |
|
148 dump("Failed to set cert exception: " + e + "\n"); |
|
149 } |
|
150 }, |
|
151 |
|
152 _listenError: function(frameLoader) { |
|
153 let self = this; |
|
154 let frameElement = frameLoader.ownerElement; |
|
155 let injectErrorPageScript = function() { |
|
156 let mm = frameLoader.messageManager; |
|
157 try { |
|
158 mm.loadFrameScript(kErrorPageFrameScript, true, true); |
|
159 } catch (e) { |
|
160 dump('Error loading ' + kErrorPageFrameScript + ' as frame script: ' + e + '\n'); |
|
161 } |
|
162 mm.addMessageListener('ErrorPage:AddCertException', self._addCertException.bind(self)); |
|
163 frameElement.removeEventListener('mozbrowsererror', injectErrorPageScript, true); |
|
164 }; |
|
165 |
|
166 frameElement.addEventListener('mozbrowsererror', |
|
167 injectErrorPageScript, |
|
168 true // use capture |
|
169 ); |
|
170 }, |
|
171 |
|
172 init: function errorPageInit() { |
|
173 Services.obs.addObserver(this, 'inprocess-browser-shown', false); |
|
174 Services.obs.addObserver(this, 'remote-browser-shown', false); |
|
175 }, |
|
176 |
|
177 observe: function errorPageObserve(aSubject, aTopic, aData) { |
|
178 let frameLoader = aSubject.QueryInterface(Ci.nsIFrameLoader); |
|
179 // Ignore notifications that aren't from a BrowserOrApp |
|
180 if (!frameLoader.ownerIsBrowserOrAppFrame) { |
|
181 return; |
|
182 } |
|
183 this._listenError(frameLoader); |
|
184 } |
|
185 }; |
|
186 |
|
187 ErrorPage.init(); |