gecko/b2g/components/SafeMode.jsm

153 lines
5.5 KiB
JavaScript

/* 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 = ["SafeMode"];
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/Webapps.jsm");
const kSafeModePref = "b2g.safe_mode";
const kSafeModePage = "safe_mode.html";
function debug(aStr) {
//dump("-*- SafeMode: " + aStr + "\n");
}
// This module is responsible for checking whether we want to start in safe
// mode or not. The flow is as follow:
// - wait for the `b2g.safe_mode` preference to be set to something different
// than `unset` by nsAppShell
// - If it's set to `no`, just start normally.
// - If it's set to `yes`, we load a stripped down system app from safe_mode.html"
// - This page is responsible to dispatch a mozContentEvent to us.
// - If the user choose SafeMode, we disable all add-ons.
// - We go on with startup.
this.SafeMode = {
// Returns a promise that resolves when nsAppShell has set the
// b2g.safe_mode_state_ready preference to `true`.
_waitForPref: function() {
debug("waitForPref");
try {
let currentMode = Services.prefs.getCharPref(kSafeModePref);
debug("current mode: " + currentMode);
if (currentMode !== "unset") {
return Promise.resolve();
}
} catch(e) { debug("No current mode available!"); }
// Wait for the preference to toggle.
return new Promise((aResolve, aReject) => {
let observer = function(aSubject, aTopic, aData) {
if (Services.prefs.getCharPref(kSafeModePref)) {
Services.prefs.removeObserver(kSafeModePref, observer, false);
aResolve();
}
}
Services.prefs.addObserver(kSafeModePref, observer, false);
});
},
// Resolves once the user has decided how to start.
// Note that all the actions happen here, so there is no other action from
// consumers than to go on.
_waitForUser: function() {
debug("waitForUser");
let isSafeMode = Services.prefs.getCharPref(kSafeModePref) === "yes";
if (!isSafeMode) {
return Promise.resolve();
}
debug("Starting in Safe Mode!");
// Load $system_app/safe_mode.html as a full screen iframe, and wait for
// the user to make a choice.
return DOMApplicationRegistry.registryReady.then(() => {
let shell = SafeMode.window.shell;
let document = SafeMode.window.document;
SafeMode.window.screen.mozLockOrientation("portrait");
let url = Services.io.newURI(shell.homeURL, null, null)
.resolve(kSafeModePage);
debug("Registry is ready, loading " + url);
let frame = document.createElementNS("http://www.w3.org/1999/xhtml", "html:iframe");
frame.setAttribute("mozbrowser", "true");
frame.setAttribute("mozapp", shell.manifestURL);
frame.setAttribute("id", "systemapp"); // To keep screen.js happy.
let contentBrowser = document.body.appendChild(frame);
return new Promise((aResolve, aReject) => {
let content = contentBrowser.contentWindow;
// Stripped down version of the system app bootstrap.
function handleEvent(e) {
switch(e.type) {
case "mozbrowserloadstart":
if (content.document.location == "about:blank") {
contentBrowser.addEventListener("mozbrowserlocationchange", handleEvent, true);
contentBrowser.removeEventListener("mozbrowserloadstart", handleEvent, true);
return;
}
notifyContentStart();
break;
case "mozbrowserlocationchange":
if (content.document.location == "about:blank") {
return;
}
contentBrowser.removeEventListener("mozbrowserlocationchange", handleEvent, true);
notifyContentStart();
break;
case "mozContentEvent":
content.removeEventListener("mozContentEvent", handleEvent, true);
contentBrowser.parentNode.removeChild(contentBrowser);
if (e.detail == "safemode-yes") {
// Really starting in safe mode, let's disable add-ons first.
DOMApplicationRegistry.disableAllAddons().then(aResolve);
} else {
aResolve();
}
break;
}
}
function notifyContentStart() {
let window = SafeMode.window;
window.shell.sendEvent(window, "SafeModeStart");
contentBrowser.setVisible(true);
// browser-ui-startup-complete is used by the AppShell to stop the
// boot animation and start gecko rendering.
Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
content.addEventListener("mozContentEvent", handleEvent, true);
}
contentBrowser.addEventListener("mozbrowserloadstart", handleEvent, true);
contentBrowser.src = url;
});
});
},
// Returns a Promise that resolves once we have decided to run in safe mode
// or not. All the safe mode switching actions happen before resolving the
// promise.
check: function(aWindow) {
debug("check");
this.window = aWindow;
if (AppConstants.platform !== "gonk") {
// For now we only have gonk support.
return Promise.resolve();
}
return this._waitForPref().then(this._waitForUser);
}
}