gecko/b2g/components/UpdatePrompt.js

205 lines
6.3 KiB
JavaScript

/* -*- Mode: Java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: sw=2 ts=8 et :
*/
/* 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/. */
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
const VERBOSE = 1;
let log =
VERBOSE ?
function log_dump(msg) { dump("UpdatePrompt: "+ msg +"\n"); } :
function log_noop(msg) { };
const APPLY_PROMPT_TIMEOUT =
Services.prefs.getIntPref("b2g.update.apply-prompt-timeout");
const APPLY_WAIT_TIMEOUT =
Services.prefs.getIntPref("b2g.update.apply-wait-timeout");
const SELF_DESTRUCT_TIMEOUT =
Services.prefs.getIntPref("b2g.update.self-destruct-timeout");
function UpdatePrompt() { }
UpdatePrompt.prototype = {
classID: Components.ID("{88b3eb21-d072-4e3b-886d-f89d8c49fe59}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdatePrompt]),
_update: null,
_applyPromptTimer: null,
_applyWaitTimer: null,
_selfDestructTimer: null,
// nsIUpdatePrompt
// FIXME/bug 737601: we should have users opt-in to downloading
// updates when on a billed pipe. Initially, opt-in for 3g, but
// that doesn't cover all cases.
checkForUpdates: function UP_checkForUpdates() { },
showUpdateAvailable: function UP_showUpdateAvailable(aUpdate) { },
showUpdateDownloaded: function UP_showUpdateDownloaded(aUpdate, aBackground) {
if (!this.sendUpdateEvent("update-downloaded", aUpdate,
this.handleDownloadedResult)) {
log("Unable to prompt, forcing restart");
this.restartProcess();
return;
}
// Schedule a fallback timeout in case the UI is unable to respond or show
// a prompt for some reason
this._applyPromptTimer = this.createTimer(APPLY_PROMPT_TIMEOUT);
},
sendUpdateEvent: function UP_sendUpdateEvent(aType, aUpdate, aCallback,
aDetail) {
let browser = Services.wm.getMostRecentWindow("navigator:browser");
if (!browser) {
log("Warning: Couldn't send update event " + aType +
": no content browser");
return false;
}
let content = browser.getContentWindow();
if (!content) {
log("Warning: Couldn't send update event " + aType +
": no content window");
return false;
}
let detail = aDetail || {};
detail.type = aType;
detail.displayVersion = aUpdate.displayVersion;
detail.detailsURL = aUpdate.detailsURL;
let patch = aUpdate.selectedPatch;
if (!patch) {
// For now we just check the first patch to get size information if a
// patch hasn't been selected yet.
if (aUpdate.patchCount == 0) {
log("Warning: no patches available in update");
return false;
}
patch = aUpdate.getPatchAt(0);
}
detail.size = patch.size;
detail.updateType = patch.type;
if (!aCallback) {
browser.shell.sendChromeEvent(detail);
return true;
}
this._update = aUpdate;
let resultType = aType + "-result";
let handleContentEvent = (function(e) {
if (!e.detail) {
return;
}
let detail = e.detail;
if (detail.type == resultType) {
aCallback.call(this, detail);
content.removeEventListener("mozContentEvent", handleContentEvent);
this._update = null;
}
}).bind(this);
content.addEventListener("mozContentEvent", handleContentEvent);
browser.shell.sendChromeEvent(detail);
return true;
},
handleDownloadedResult: function UP_handleDownloadedResult(aDetail) {
if (this._applyPromptTimer) {
this._applyPromptTimer.cancel();
this._applyPromptTimer = null;
}
switch (aDetail.result) {
case "wait":
// Wait for a fixed period of time, allowing the user to temporarily
// postpone applying an update
this._applyWaitTimer = this.createTimer(APPLY_WAIT_TIMEOUT);
break;
case "restart":
this.restartProcess();
break;
}
},
restartProcess: function UP_restartProcess() {
log("Update downloaded, restarting to apply it");
// If not cleanly shut down within 5 seconds, this process will
// explode.
this._selfDestructTimer = this.createTimer(SELF_DESTRUCT_TIMEOUT);
let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
.getService(Ci.nsIAppStartup);
// NB: on Gonk, we rely on the system process manager to restart
// us. Trying to restart here would conflict with the process
// manager. We should be using a runtime check to detect Gonk
// instead of this gross ifdef, but the ifdef works for now.
appStartup.quit(appStartup.eForceQuit
#ifndef ANDROID
| appStartup.eRestart
#endif
);
},
notify: function UP_notify(aTimer) {
if (aTimer == this._selfDestructTimer) {
this._selfDestructTimer = null;
this.selfDestruct();
} else if (aTimer == this._applyPromptTimer) {
log("Timed out waiting for result, restarting");
this._applyPromptTimer = null;
this.restartProcess();
} else if (aTimer == this._applyWaitTimer) {
this._applyWaitTimer = null;
this.showUpdatePrompt();
}
},
selfDestruct: function UP_selfDestruct() {
#ifdef ANDROID
Cu.import("resource://gre/modules/ctypes.jsm");
let libc = ctypes.open("libc.so");
let _exit = libc.declare("_exit", ctypes.default_abi,
ctypes.void_t, // [return]
ctypes.int); // status
log("Self-destruct timer fired; didn't cleanly shut down. BOOM");
_exit(0);
#endif
},
createTimer: function UP_createTimer(aTimeoutMs) {
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
timer.initWithCallback(this, aTimeoutMs, timer.TYPE_ONE_SHOT);
return timer;
},
showUpdateInstalled: function UP_showUpdateInstalled() { },
showUpdateError: function UP_showUpdateError(aUpdate) {
if (aUpdate.state == "failed") {
log("Failed to download update, errorCode: " + aUpdate.errorCode);
}
},
showUpdateHistory: function UP_showUpdateHistory(aParent) { },
};
const NSGetFactory = XPCOMUtils.generateNSGetFactory([UpdatePrompt]);