Main patch - Bug 882007 - Restart notification is not shown when staging is enabled. r=bbondy

This commit is contained in:
Robert Strong 2013-06-19 18:05:04 -07:00
parent 5c6db5dc34
commit 3e07312ff7
2 changed files with 145 additions and 110 deletions

View File

@ -352,7 +352,7 @@ interface nsIUpdateChecker : nsISupports
* background update checks and provides utilities for selecting and
* downloading update patches.
*/
[scriptable, uuid(900b4a18-3bef-4f3e-bcf5-84dce0021c6d)]
[scriptable, uuid(814f185b-cac6-494b-8fd6-63cf7380d857)]
interface nsIApplicationUpdateService : nsISupports
{
/**
@ -394,14 +394,9 @@ interface nsIApplicationUpdateService : nsISupports
*/
AString downloadUpdate(in nsIUpdate update, in boolean background);
/**
* Apply an update in the background.
*/
void applyUpdateInBackground(in nsIUpdate update);
/**
* Get the Active Updates directory
* @returns The active updates directory.
* @returns An nsIFile for the active updates directory.
*/
nsIFile getUpdatesDirectory();

View File

@ -43,7 +43,7 @@ const PREF_APP_UPDATE_POSTUPDATE = "app.update.postupdate";
const PREF_APP_UPDATE_PROMPTWAITTIME = "app.update.promptWaitTime";
const PREF_APP_UPDATE_SHOW_INSTALLED_UI = "app.update.showInstalledUI";
const PREF_APP_UPDATE_SILENT = "app.update.silent";
const PREF_APP_UPDATE_STAGE_ENABLED = "app.update.staging.enabled";
const PREF_APP_UPDATE_STAGING_ENABLED = "app.update.staging.enabled";
const PREF_APP_UPDATE_URL = "app.update.url";
const PREF_APP_UPDATE_URL_DETAILS = "app.update.url.details";
const PREF_APP_UPDATE_URL_OVERRIDE = "app.update.url.override";
@ -81,11 +81,11 @@ const KEY_UPDATE_ARCHIVE_DIR = "UpdArchD"
#endif
#ifdef XP_WIN
#define SKIP_STAGE_UPDATES_TEST
#define CHECK_CAN_USE_SERVICE
#elifdef MOZ_WIDGET_GONK
// In Gonk, the updater will remount the /system partition to move staged files
// into place, so we skip the test here to keep things isolated.
#define SKIP_STAGE_UPDATES_TEST
#define CHECK_CAN_USE_SERVICE
#endif
const DIR_UPDATES = "updates";
@ -455,7 +455,7 @@ function getPerInstallationMutexName(aGlobal) {
hasher.init(hasher.SHA1);
Components.utils.import("resource://gre/modules/Services.jsm");
var exeFile = Services.dirsvc.get("XREExeF", Components.interfaces.nsILocalFile);
var exeFile = Services.dirsvc.get(KEY_EXECUTABLE, Components.interfaces.nsILocalFile);
let converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].
createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
@ -469,10 +469,11 @@ function getPerInstallationMutexName(aGlobal) {
/**
* Whether or not the current instance has the update mutex. The update mutex
* gives protection against 2 browsers from the same installation updating:
* gives protection against 2 applications from the same installation updating:
* 1) Running multiple profiles from the same installation path
* 2) Running a Metro and Desktop browser at the same time from the same path
* 3) 2 browsers running in 2 different user sessions from the same path
* 2) Running a Metro and Desktop application at the same time from the same
* path
* 3) 2 applications running in 2 different user sessions from the same path
*
* @return true if this instance holds the update mutex
*/
@ -490,13 +491,13 @@ function hasUpdateMutex() {
XPCOMUtils.defineLazyGetter(this, "gCanApplyUpdates", function aus_gCanApplyUpdates() {
function submitHasPermissionsTelemetryPing(val) {
try {
let h = Services.telemetry.getHistogramById("UPDATER_HAS_PERMISSIONS");
h.add(+val);
} catch(e) {
// Don't allow any exception to be propagated.
Components.utils.reportError(e);
}
try {
let h = Services.telemetry.getHistogramById("UPDATER_HAS_PERMISSIONS");
h.add(+val);
} catch(e) {
// Don't allow any exception to be propagated.
Components.utils.reportError(e);
}
}
try {
@ -584,8 +585,9 @@ XPCOMUtils.defineLazyGetter(this, "gCanApplyUpdates", function aus_gCanApplyUpda
}
if (!hasUpdateMutex()) {
LOG("gCanApplyUpdates - unable to apply updates because another " +
"browser is already handling updates for this installation.");
LOG("gCanApplyUpdates - unable to apply updates because another instance" +
"of the application is already handling updates for this " +
"installation.");
return false;
}
@ -594,54 +596,74 @@ XPCOMUtils.defineLazyGetter(this, "gCanApplyUpdates", function aus_gCanApplyUpda
return true;
});
XPCOMUtils.defineLazyGetter(this, "gCanStageUpdates", function aus_gCanStageUpdates() {
/**
* Whether or not the application can stage an update.
*
* @return true if updates can be staged.
*/
function getCanStageUpdates() {
// If background updates are disabled, then just bail out!
if (!getPref("getBoolPref", PREF_APP_UPDATE_STAGE_ENABLED, false)) {
LOG("gCanStageUpdates - staging updates is disabled by preference " + PREF_APP_UPDATE_STAGE_ENABLED);
if (!getPref("getBoolPref", PREF_APP_UPDATE_STAGING_ENABLED, false)) {
LOG("getCanStageUpdates - staging updates is disabled by preference " +
PREF_APP_UPDATE_STAGING_ENABLED);
return false;
}
#ifdef SKIP_STAGE_UPDATES_TEST
#ifdef CHECK_CAN_USE_SERVICE
if (getPref("getBoolPref", PREF_APP_UPDATE_SERVICE_ENABLED, false)) {
// No need to perform directory write checks, the maintenance service will
// be able to write to all directories.
LOG("gCanStageUpdates - able to stage updates because we'll use the service");
LOG("getCanStageUpdates - able to stage updates because we'll use the service");
return true;
}
#endif
try {
var updateTestFile = getInstallDirRoot();
updateTestFile.append(FILE_PERMS_TEST);
LOG("gCanStageUpdates - testing write access " + updateTestFile.path);
testWriteAccess(updateTestFile, true);
#ifndef XP_MACOSX
// On all platforms except Mac, we need to test the parent directory as well,
// as we need to be able to move files in that directory during the replacing
// step.
updateTestFile = getInstallDirRoot().parent;
updateTestFile.append(FILE_PERMS_TEST);
LOG("gCanStageUpdates - testing write access " + updateTestFile.path);
updateTestFile.createUnique(Ci.nsILocalFile.DIRECTORY_TYPE,
FileUtils.PERMS_DIRECTORY);
updateTestFile.remove(false);
#endif
}
catch (e) {
LOG("gCanStageUpdates - unable to stage updates. Exception: " + e);
// No write privileges
return false;
}
if (!hasUpdateMutex()) {
LOG("gCanStageUpdates - unable to stage updates because another " +
"browser is already handling updates for this installation.");
LOG("getCanStageUpdates - unable to apply updates because another " +
"instance of the application is already handling updates for this " +
"installation.");
return false;
}
LOG("gCanStageUpdates - able to stage updates");
return true;
});
/**
* Whether or not the application can stage an update for the current session.
* These checks are only performed once per session due to using a lazy getter.
*
* @return true if updates can be staged for this session.
*/
XPCOMUtils.defineLazyGetter(this, "canStageUpdatesSession", function canStageUpdatesSession() {
try {
var updateTestFile = getInstallDirRoot();
updateTestFile.append(FILE_PERMS_TEST);
LOG("canStageUpdatesSession - testing write access " +
updateTestFile.path);
testWriteAccess(updateTestFile, true);
#ifndef XP_MACOSX
// On all platforms except Mac, we need to test the parent directory as
// well, as we need to be able to move files in that directory during the
// replacing step.
updateTestFile = getInstallDirRoot().parent;
updateTestFile.append(FILE_PERMS_TEST);
LOG("canStageUpdatesSession - testing write access " +
updateTestFile.path);
updateTestFile.createUnique(Ci.nsILocalFile.DIRECTORY_TYPE,
FileUtils.PERMS_DIRECTORY);
updateTestFile.remove(false);
#endif
}
catch (e) {
LOG("canStageUpdatesSession - unable to stage updates. Exception: " +
e);
// No write privileges
return false;
}
LOG("canStageUpdatesSession - able to stage updates");
return true;
});
return canStageUpdatesSession;
}
XPCOMUtils.defineLazyGetter(this, "gIsMetro", function aus_gIsMetro() {
#ifdef XP_WIN
@ -663,8 +685,8 @@ XPCOMUtils.defineLazyGetter(this, "gMetroUpdatesEnabled", function aus_gMetroUpd
if (gIsMetro) {
let metroUpdate = getPref("getBoolPref", PREF_APP_UPDATE_METRO_ENABLED, true);
if (!metroUpdate) {
LOG("gMetroUpdatesEnabled - unable to automatically check for metro" +
" updates, disabled by pref");
LOG("gMetroUpdatesEnabled - unable to automatically check for metro " +
"updates, disabled by pref");
return false;
}
}
@ -675,13 +697,13 @@ XPCOMUtils.defineLazyGetter(this, "gMetroUpdatesEnabled", function aus_gMetroUpd
});
XPCOMUtils.defineLazyGetter(this, "gCanCheckForUpdates", function aus_gCanCheckForUpdates() {
// If the administrator has locked the app update functionality
// OFF - this is not just a user setting, so disable the manual
// UI too.
// If the administrator has disabled app update and locked the preference so
// users can't check for updates. This preference check is ok in this lazy
// getter since locked prefs don't change until the application is restarted.
var enabled = getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true);
if (!enabled && Services.prefs.prefIsLocked(PREF_APP_UPDATE_ENABLED)) {
LOG("gCanCheckForUpdates - unable to automatically check for updates, " +
"disabled by pref");
"the preference is disabled and admistratively locked.");
return false;
}
@ -704,7 +726,8 @@ XPCOMUtils.defineLazyGetter(this, "gCanCheckForUpdates", function aus_gCanCheckF
if (!hasUpdateMutex()) {
LOG("gCanCheckForUpdates - unable to apply updates because another " +
"browser is already handling updates for this installation.");
"instance of the application is already handling updates for this " +
"installation.");
return false;
}
@ -771,21 +794,22 @@ function getUpdateDirCreate(pathArray) {
return FileUtils.getDir(KEY_UPDROOT, pathArray, true);
}
/**
# Gets the specified directory at the specified hierarchy under the
# update root directory and without creating it if it doesn't exist.
# @param pathArray
# An array of path components to locate beneath the directory
# specified by |key|
# @return nsIFile object for the location specified.
*/
/**
# Gets the specified directory at the specified hierarchy under the
# update root directory and without creating it if it doesn't exist.
# @param pathArray
# An array of path components to locate beneath the directory
# specified by |key|
# @return nsIFile object for the location specified.
*/
function getUpdateDirNoCreate(pathArray) {
return FileUtils.getDir(KEY_UPDROOT, pathArray, false);
}
/**
# Gets the application base directory.
# @return nsIFile object for the application base directory.
* Gets the application base directory.
*
* @return nsIFile object for the application base directory.
*/
function getAppBaseDir() {
return Services.dirsvc.get(KEY_EXECUTABLE, Ci.nsIFile).parent;
@ -1842,6 +1866,7 @@ const UpdateServiceFactory = {
function UpdateService() {
LOG("Creating UpdateService");
Services.obs.addObserver(this, "xpcom-shutdown", false);
Services.prefs.addObserver("app.update.log", this, false);
#ifdef MOZ_WIDGET_GONK
// PowerManagerService::SyncProfile (which is called for Reboot, PowerOff
// and Restart) sends the profile-change-net-teardown event. We can then
@ -1895,11 +1920,17 @@ UpdateService.prototype = {
case "network:offline-status-changed":
this._offlineStatusChanged(data);
break;
case "nsPref:changed":
if (data == PREF_APP_UPDATE_LOG) {
gLogEnabled = getPref("getBoolPref", PREF_APP_UPDATE_LOG, false);
}
break;
#ifdef MOZ_WIDGET_GONK
case "profile-change-net-teardown": // fall thru
#endif
case "xpcom-shutdown":
Services.obs.removeObserver(this, topic);
Services.prefs.removeObserver("app.update.log", this);
if (this._retryTimer) {
this._retryTimer.cancel();
@ -1923,7 +1954,7 @@ UpdateService.prototype = {
/**
* Perform post-processing on updates lingering in the updates directory
* from a previous browser session - either report install failures (and
* from a previous application session - either report install failures (and
* optionally attempt to fetch a different version if appropriate) or
* notify the user of install success.
*/
@ -2065,7 +2096,7 @@ UpdateService.prototype = {
"UPDATER_UPDATES_METRO_ENABLED");
this._sendBoolPrefTelemetryPing(PREF_APP_UPDATE_AUTO,
"UPDATER_UPDATES_AUTOMATIC");
this._sendBoolPrefTelemetryPing(PREF_APP_UPDATE_STAGE_ENABLED,
this._sendBoolPrefTelemetryPing(PREF_APP_UPDATE_STAGING_ENABLED,
"UPDATER_STAGE_ENABLED");
#ifdef XP_WIN
@ -2688,7 +2719,7 @@ UpdateService.prototype = {
* See nsIUpdateService.idl
*/
get canStageUpdates() {
return gCanStageUpdates;
return getCanStageUpdates();
},
/**
@ -2777,26 +2808,6 @@ UpdateService.prototype = {
return this._downloader.downloadUpdate(update);
},
applyUpdateInBackground: function AUS_applyUpdateInBackground(update) {
// If we can't stage an update, then just bail out!
if (!gCanStageUpdates) {
return;
}
LOG("UpdateService:applyUpdateInBackground called with the following update: " +
update.name);
// Initiate the update in the background
try {
Cc["@mozilla.org/updates/update-processor;1"].
createInstance(Ci.nsIUpdateProcessor).
processUpdate(update);
} catch (e) {
// Fail gracefully in case the application does not support the update
// processor service.
}
},
/**
* See nsIUpdateService.idl
*/
@ -3127,6 +3138,7 @@ UpdateManager.prototype = {
// Do this after *everything* else, since it will likely cause the app
// to shut down.
#ifdef MOZ_WIDGET_GONK
if (update.state == STATE_APPLIED) {
// Notify the user that an update has been staged and is ready for
// installation (i.e. that they should restart the application). We do
@ -3137,6 +3149,23 @@ UpdateManager.prototype = {
} else {
releaseSDCardMountLock();
}
#else
// Only prompt when the UI isn't already open.
let windowType = getPref("getCharPref", PREF_APP_UPDATE_ALTWINDOWTYPE, null);
if (Services.wm.getMostRecentWindow(UPDATE_WINDOW_NAME) ||
windowType && Services.wm.getMostRecentWindow(windowType)) {
return;
}
if (update.state == STATE_APPLIED || update.state == STATE_APPLIED_SVC ||
update.state == STATE_PENDING || update.state == STATE_PENDING_SVC) {
// Notify the user that an update has been staged and is ready for
// installation (i.e. that they should restart the application).
var prompter = Cc["@mozilla.org/updates/update-prompt;1"].
createInstance(Ci.nsIUpdatePrompt);
prompter.showUpdateDownloaded(update, true);
}
#endif
},
classID: Components.ID("{093C2356-4843-4C65-8709-D7DBCBBE7DFB}"),
@ -3968,12 +3997,9 @@ Downloader.prototype = {
if (Components.isSuccessCode(status)) {
if (this._verifyDownload()) {
state = shouldUseService() ? STATE_PENDING_SVC : STATE_PENDING
// We only need to explicitly show the prompt if this is a background
// download, since otherwise some kind of UI is already visible and
// that UI will notify.
if (this.background)
shouldShowPrompt = !getPref("getBoolPref", PREF_APP_UPDATE_STAGE_ENABLED, false);
if (this.background) {
shouldShowPrompt = !getCanStageUpdates();
}
// Tell the updater.exe we're ready to apply.
writeStatusFile(getUpdatesDir(), state);
@ -3984,7 +4010,6 @@ Downloader.prototype = {
else {
LOG("Downloader:onStopRequest - download verification failed");
state = STATE_DOWNLOAD_FAILED;
status = Cr.NS_ERROR_CORRUPTED_CONTENT;
// Yes, this code is a string.
@ -4112,6 +4137,28 @@ Downloader.prototype = {
return;
}
if (state == STATE_PENDING || state == STATE_PENDING_SVC) {
if (getCanStageUpdates()) {
LOG("Downloader:onStopRequest - attempting to stage update: " +
this._update.name);
// Initiate the update in the background
try {
Cc["@mozilla.org/updates/update-processor;1"].
createInstance(Ci.nsIUpdateProcessor).
processUpdate(this._update);
} catch (e) {
// Fail gracefully in case the application does not support the update
// processor service.
LOG("Downloader:onStopRequest - failed to stage update. Exception: " +
e);
if (this.background) {
shouldShowPrompt = true;
}
}
}
}
// Do this after *everything* else, since it will likely cause the app
// to shut down.
if (shouldShowPrompt) {
@ -4123,13 +4170,6 @@ Downloader.prototype = {
prompter.showUpdateDownloaded(this._update, true);
}
if (state == STATE_PENDING || state == STATE_PENDING_SVC) {
// Initiate the background update job.
Cc["@mozilla.org/updates/update-service;1"].
getService(Ci.nsIApplicationUpdateService).
applyUpdateInBackground(this._update);
}
if (shouldRegisterOnlineObserver) {
LOG("Downloader:onStopRequest - Registering online observer");
this.updateService._registerOnlineObserver();