summaryrefslogtreecommitdiffstats
path: root/b2g/components/ErrorPage.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'b2g/components/ErrorPage.jsm')
-rw-r--r--b2g/components/ErrorPage.jsm187
1 files changed, 187 insertions, 0 deletions
diff --git a/b2g/components/ErrorPage.jsm b/b2g/components/ErrorPage.jsm
new file mode 100644
index 000000000..2c0c64c21
--- /dev/null
+++ b/b2g/components/ErrorPage.jsm
@@ -0,0 +1,187 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+this.EXPORTED_SYMBOLS = ['ErrorPage'];
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const kErrorPageFrameScript = 'chrome://b2g/content/ErrorPage.js';
+
+Cu.import('resource://gre/modules/Services.jsm');
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "CertOverrideService", function () {
+ return Cc["@mozilla.org/security/certoverride;1"]
+ .getService(Ci.nsICertOverrideService);
+});
+
+/**
+ * A class to add exceptions to override SSL certificate problems.
+ * The functionality itself is borrowed from exceptionDialog.js.
+ */
+function SSLExceptions(aCallback, aUri, aWindow) {
+ this._finishCallback = aCallback;
+ this._uri = aUri;
+ this._window = aWindow;
+};
+
+SSLExceptions.prototype = {
+ _finishCallback: null,
+ _window: null,
+ _uri: null,
+ _temporary: null,
+ _sslStatus: null,
+
+ getInterface: function SSLE_getInterface(aIID) {
+ return this.QueryInterface(aIID);
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIBadCertListener2]),
+
+ /**
+ * To collect the SSL status we intercept the certificate error here
+ * and store the status for later use.
+ */
+ notifyCertProblem: function SSLE_notifyCertProblem(aSocketInfo,
+ aSslStatus,
+ aTargetHost) {
+ this._sslStatus = aSslStatus.QueryInterface(Ci.nsISSLStatus);
+ Services.tm.currentThread.dispatch({
+ run: this._addOverride.bind(this)
+ }, Ci.nsIThread.DISPATCH_NORMAL);
+ return true; // suppress error UI
+ },
+
+ /**
+ * Attempt to download the certificate for the location specified to get
+ * the SSLState for the certificate and the errors.
+ */
+ _checkCert: function SSLE_checkCert() {
+ this._sslStatus = null;
+ if (!this._uri) {
+ return;
+ }
+ let req = new this._window.XMLHttpRequest();
+ try {
+ req.open("GET", this._uri.prePath, true);
+ req.channel.notificationCallbacks = this;
+ let xhrHandler = (function() {
+ req.removeEventListener("load", xhrHandler);
+ req.removeEventListener("error", xhrHandler);
+ if (!this._sslStatus) {
+ // Got response from server without an SSL error.
+ if (this._finishCallback) {
+ this._finishCallback();
+ }
+ }
+ }).bind(this);
+ req.addEventListener("load", xhrHandler);
+ req.addEventListener("error", xhrHandler);
+ req.send(null);
+ } catch (e) {
+ // We *expect* exceptions if there are problems with the certificate
+ // presented by the site. Log it, just in case, but we can proceed here,
+ // with appropriate sanity checks
+ Components.utils.reportError("Attempted to connect to a site with a bad certificate in the add exception dialog. " +
+ "This results in a (mostly harmless) exception being thrown. " +
+ "Logged for information purposes only: " + e);
+ }
+ },
+
+ /**
+ * Internal method to create an override.
+ */
+ _addOverride: function SSLE_addOverride() {
+ let SSLStatus = this._sslStatus;
+ let uri = this._uri;
+ let flags = 0;
+
+ if (SSLStatus.isUntrusted) {
+ flags |= Ci.nsICertOverrideService.ERROR_UNTRUSTED;
+ }
+ if (SSLStatus.isDomainMismatch) {
+ flags |= Ci.nsICertOverrideService.ERROR_MISMATCH;
+ }
+ if (SSLStatus.isNotValidAtThisTime) {
+ flags |= Ci.nsICertOverrideService.ERROR_TIME;
+ }
+
+ CertOverrideService.rememberValidityOverride(
+ uri.asciiHost,
+ uri.port,
+ SSLStatus.serverCert,
+ flags,
+ this._temporary);
+
+ if (this._finishCallback) {
+ this._finishCallback();
+ }
+ },
+
+ /**
+ * Creates a permanent exception to override all overridable errors for
+ * the given URL.
+ */
+ addException: function SSLE_addException(aTemporary) {
+ this._temporary = aTemporary;
+ this._checkCert();
+ }
+};
+
+var ErrorPage = {
+ _addCertException: function(aMessage) {
+ let frameLoaderOwner = aMessage.target.QueryInterface(Ci.nsIFrameLoaderOwner);
+ let win = frameLoaderOwner.ownerDocument.defaultView;
+ let mm = frameLoaderOwner.frameLoader.messageManager;
+
+ let uri = Services.io.newURI(aMessage.data.url, null, null);
+ let sslExceptions = new SSLExceptions((function() {
+ mm.sendAsyncMessage('ErrorPage:ReloadPage');
+ }).bind(this), uri, win);
+ try {
+ sslExceptions.addException(!aMessage.data.isPermanent);
+ } catch (e) {
+ dump("Failed to set cert exception: " + e + "\n");
+ }
+ },
+
+ _listenError: function(frameLoader) {
+ let self = this;
+ let frameElement = frameLoader.ownerElement;
+ let injectErrorPageScript = function() {
+ let mm = frameLoader.messageManager;
+ try {
+ mm.loadFrameScript(kErrorPageFrameScript, true, true);
+ } catch (e) {
+ dump('Error loading ' + kErrorPageFrameScript + ' as frame script: ' + e + '\n');
+ }
+ mm.addMessageListener('ErrorPage:AddCertException', self._addCertException.bind(self));
+ frameElement.removeEventListener('mozbrowsererror', injectErrorPageScript, true);
+ };
+
+ frameElement.addEventListener('mozbrowsererror',
+ injectErrorPageScript,
+ true // use capture
+ );
+ },
+
+ init: function errorPageInit() {
+ Services.obs.addObserver(this, 'inprocess-browser-shown', false);
+ Services.obs.addObserver(this, 'remote-browser-shown', false);
+ },
+
+ observe: function errorPageObserve(aSubject, aTopic, aData) {
+ let frameLoader = aSubject.QueryInterface(Ci.nsIFrameLoader);
+ // Ignore notifications that aren't from a BrowserOrApp
+ if (!frameLoader.ownerIsMozBrowserOrAppFrame) {
+ return;
+ }
+ this._listenError(frameLoader);
+ }
+};
+
+ErrorPage.init();