1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/b2g/components/ErrorPage.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,187 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +'use strict'; 1.9 + 1.10 +this.EXPORTED_SYMBOLS = ['ErrorPage']; 1.11 + 1.12 +const Cu = Components.utils; 1.13 +const Cc = Components.classes; 1.14 +const Ci = Components.interfaces; 1.15 +const kErrorPageFrameScript = 'chrome://b2g/content/ErrorPage.js'; 1.16 + 1.17 +Cu.import('resource://gre/modules/Services.jsm'); 1.18 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.19 + 1.20 +XPCOMUtils.defineLazyGetter(this, "CertOverrideService", function () { 1.21 + return Cc["@mozilla.org/security/certoverride;1"] 1.22 + .getService(Ci.nsICertOverrideService); 1.23 +}); 1.24 + 1.25 +/** 1.26 + * A class to add exceptions to override SSL certificate problems. 1.27 + * The functionality itself is borrowed from exceptionDialog.js. 1.28 + */ 1.29 +function SSLExceptions(aCallback, aUri, aWindow) { 1.30 + this._finishCallback = aCallback; 1.31 + this._uri = aUri; 1.32 + this._window = aWindow; 1.33 +}; 1.34 + 1.35 +SSLExceptions.prototype = { 1.36 + _finishCallback: null, 1.37 + _window: null, 1.38 + _uri: null, 1.39 + _temporary: null, 1.40 + _sslStatus: null, 1.41 + 1.42 + getInterface: function SSLE_getInterface(aIID) { 1.43 + return this.QueryInterface(aIID); 1.44 + }, 1.45 + 1.46 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIBadCertListener2]), 1.47 + 1.48 + /** 1.49 + * To collect the SSL status we intercept the certificate error here 1.50 + * and store the status for later use. 1.51 + */ 1.52 + notifyCertProblem: function SSLE_notifyCertProblem(aSocketInfo, 1.53 + aSslStatus, 1.54 + aTargetHost) { 1.55 + this._sslStatus = aSslStatus.QueryInterface(Ci.nsISSLStatus); 1.56 + Services.tm.currentThread.dispatch({ 1.57 + run: this._addOverride.bind(this) 1.58 + }, Ci.nsIThread.DISPATCH_NORMAL); 1.59 + return true; // suppress error UI 1.60 + }, 1.61 + 1.62 + /** 1.63 + * Attempt to download the certificate for the location specified to get 1.64 + * the SSLState for the certificate and the errors. 1.65 + */ 1.66 + _checkCert: function SSLE_checkCert() { 1.67 + this._sslStatus = null; 1.68 + if (!this._uri) { 1.69 + return; 1.70 + } 1.71 + let req = new this._window.XMLHttpRequest(); 1.72 + try { 1.73 + req.open("GET", this._uri.prePath, true); 1.74 + req.channel.notificationCallbacks = this; 1.75 + let xhrHandler = (function() { 1.76 + req.removeEventListener("load", xhrHandler); 1.77 + req.removeEventListener("error", xhrHandler); 1.78 + if (!this._sslStatus) { 1.79 + // Got response from server without an SSL error. 1.80 + if (this._finishCallback) { 1.81 + this._finishCallback(); 1.82 + } 1.83 + } 1.84 + }).bind(this); 1.85 + req.addEventListener("load", xhrHandler); 1.86 + req.addEventListener("error", xhrHandler); 1.87 + req.send(null); 1.88 + } catch (e) { 1.89 + // We *expect* exceptions if there are problems with the certificate 1.90 + // presented by the site. Log it, just in case, but we can proceed here, 1.91 + // with appropriate sanity checks 1.92 + Components.utils.reportError("Attempted to connect to a site with a bad certificate in the add exception dialog. " + 1.93 + "This results in a (mostly harmless) exception being thrown. " + 1.94 + "Logged for information purposes only: " + e); 1.95 + } 1.96 + }, 1.97 + 1.98 + /** 1.99 + * Internal method to create an override. 1.100 + */ 1.101 + _addOverride: function SSLE_addOverride() { 1.102 + let SSLStatus = this._sslStatus; 1.103 + let uri = this._uri; 1.104 + let flags = 0; 1.105 + 1.106 + if (SSLStatus.isUntrusted) { 1.107 + flags |= Ci.nsICertOverrideService.ERROR_UNTRUSTED; 1.108 + } 1.109 + if (SSLStatus.isDomainMismatch) { 1.110 + flags |= Ci.nsICertOverrideService.ERROR_MISMATCH; 1.111 + } 1.112 + if (SSLStatus.isNotValidAtThisTime) { 1.113 + flags |= Ci.nsICertOverrideService.ERROR_TIME; 1.114 + } 1.115 + 1.116 + CertOverrideService.rememberValidityOverride( 1.117 + uri.asciiHost, 1.118 + uri.port, 1.119 + SSLStatus.serverCert, 1.120 + flags, 1.121 + this._temporary); 1.122 + 1.123 + if (this._finishCallback) { 1.124 + this._finishCallback(); 1.125 + } 1.126 + }, 1.127 + 1.128 + /** 1.129 + * Creates a permanent exception to override all overridable errors for 1.130 + * the given URL. 1.131 + */ 1.132 + addException: function SSLE_addException(aTemporary) { 1.133 + this._temporary = aTemporary; 1.134 + this._checkCert(); 1.135 + } 1.136 +}; 1.137 + 1.138 +let ErrorPage = { 1.139 + _addCertException: function(aMessage) { 1.140 + let frameLoaderOwner = aMessage.target.QueryInterface(Ci.nsIFrameLoaderOwner); 1.141 + let win = frameLoaderOwner.ownerDocument.defaultView; 1.142 + let mm = frameLoaderOwner.frameLoader.messageManager; 1.143 + 1.144 + let uri = Services.io.newURI(aMessage.data.url, null, null); 1.145 + let sslExceptions = new SSLExceptions((function() { 1.146 + mm.sendAsyncMessage('ErrorPage:ReloadPage'); 1.147 + }).bind(this), uri, win); 1.148 + try { 1.149 + sslExceptions.addException(!aMessage.data.isPermanent); 1.150 + } catch (e) { 1.151 + dump("Failed to set cert exception: " + e + "\n"); 1.152 + } 1.153 + }, 1.154 + 1.155 + _listenError: function(frameLoader) { 1.156 + let self = this; 1.157 + let frameElement = frameLoader.ownerElement; 1.158 + let injectErrorPageScript = function() { 1.159 + let mm = frameLoader.messageManager; 1.160 + try { 1.161 + mm.loadFrameScript(kErrorPageFrameScript, true, true); 1.162 + } catch (e) { 1.163 + dump('Error loading ' + kErrorPageFrameScript + ' as frame script: ' + e + '\n'); 1.164 + } 1.165 + mm.addMessageListener('ErrorPage:AddCertException', self._addCertException.bind(self)); 1.166 + frameElement.removeEventListener('mozbrowsererror', injectErrorPageScript, true); 1.167 + }; 1.168 + 1.169 + frameElement.addEventListener('mozbrowsererror', 1.170 + injectErrorPageScript, 1.171 + true // use capture 1.172 + ); 1.173 + }, 1.174 + 1.175 + init: function errorPageInit() { 1.176 + Services.obs.addObserver(this, 'inprocess-browser-shown', false); 1.177 + Services.obs.addObserver(this, 'remote-browser-shown', false); 1.178 + }, 1.179 + 1.180 + observe: function errorPageObserve(aSubject, aTopic, aData) { 1.181 + let frameLoader = aSubject.QueryInterface(Ci.nsIFrameLoader); 1.182 + // Ignore notifications that aren't from a BrowserOrApp 1.183 + if (!frameLoader.ownerIsBrowserOrAppFrame) { 1.184 + return; 1.185 + } 1.186 + this._listenError(frameLoader); 1.187 + } 1.188 +}; 1.189 + 1.190 +ErrorPage.init();