Merge mozilla-central into mozilla-inbound

This commit is contained in:
Ehsan Akhgari 2012-06-01 17:22:53 -04:00
commit f0f2c4c6f3
7 changed files with 135 additions and 39 deletions

View File

@ -194,7 +194,7 @@ appUpdater.prototype =
// true when updating in background is enabled.
get backgroundUpdateEnabled() {
return this.updateEnabled &&
Services.prefs.getBoolPref("app.update.stage.enabled");
gAppUpdater.aus.canStageUpdates;
},
// true when updating is automatic.
@ -482,6 +482,13 @@ appUpdater.prototype =
return;
}
this.setupDownloadingUI();
},
/**
* Switches to the UI responsible for tracking the download.
*/
setupDownloadingUI: function() {
this.downloadStatus = document.getElementById("downloadStatus");
this.downloadStatus.value =
DownloadUtils.getTransferTotal(0, this.update.selectedPatch.size);
@ -527,7 +534,7 @@ appUpdater.prototype =
let self = this;
Services.obs.addObserver(function (aSubject, aTopic, aData) {
// Update the UI when the background updater is finished
let status = update.state;
let status = aData;
if (status == "applied" || status == "applied-service" ||
status == "pending" || status == "pending-service") {
// If the update is successfully applied, or if the updater has
@ -540,6 +547,12 @@ appUpdater.prototype =
// Background update has failed, let's show the UI responsible for
// prompting the user to update manually.
self.selectPanel("downloadFailed");
} else if (status == "downloading") {
// We've fallen back to downloading the full update because the
// partial update failed to get staged in the background.
// Therefore we need to keep our observer.
self.setupDownloadingUI();
return;
}
Services.obs.removeObserver(arguments.callee, "update-staged");
}, "update-staged", false);

View File

@ -90,8 +90,12 @@ function test()
}
var loads = 0;
function waitForLoad(tab) {
gBrowser.getBrowserForTab(gBrowser.tabs[tab]).removeEventListener("load", arguments.callee, true);
function waitForLoad(event, tab, listenerContainer) {
var b = gBrowser.getBrowserForTab(gBrowser.tabs[tab]);
if (b.contentDocument != event.target) {
return;
}
gBrowser.getBrowserForTab(gBrowser.tabs[tab]).removeEventListener("load", listenerContainer.listener, true);
++loads;
if (loads == tabs.length - 1) {
executeSoon(test1);
@ -99,7 +103,9 @@ function test()
}
function fn(f, arg) {
return function () { return f(arg); };
var listenerContainer = { listener: null }
listenerContainer.listener = function (event) { return f(event, arg, listenerContainer); };
return listenerContainer.listener;
}
for (var i = 1; i < tabs.length; ++i) {
gBrowser.getBrowserForTab(tabs[i]).addEventListener("load", fn(waitForLoad,i), true);

View File

@ -239,6 +239,7 @@ StartUpdateProcess(int argc,
if (updateWasSuccessful && argc > 2) {
LPCWSTR installationDir = argv[2];
LPCWSTR updateInfoDir = argv[1];
bool backgroundUpdate = (argc == 4 && !wcscmp(argv[3], L"-1"));
// Launch the PostProcess with admin access in session 0. This is
// actually launching the post update process but it takes in the
@ -247,9 +248,14 @@ StartUpdateProcess(int argc,
// the unelevated updater.exe after the update process is complete
// from the service. We don't know here which session to start
// the user PostUpdate process from.
LOG(("Launching post update process as the service in session 0.\n"));
if (!LaunchWinPostProcess(installationDir, updateInfoDir, true, NULL)) {
LOG(("The post update process could not be launched.\n"));
// Note that we don't need to do this if we're just staging the
// update in the background, as the PostUpdate step runs when
// performing the replacing in that case.
if (!backgroundUpdate) {
LOG(("Launching post update process as the service in session 0.\n"));
if (!LaunchWinPostProcess(installationDir, updateInfoDir, true, NULL)) {
LOG(("The post update process could not be launched.\n"));
}
}
}
}

View File

@ -15,7 +15,6 @@ const CoR = Components.results;
const XMLNS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const PREF_APP_UPDATE_BACKGROUND = "app.update.stage.enabled";
const PREF_APP_UPDATE_BACKGROUNDERRORS = "app.update.backgroundErrors";
const PREF_APP_UPDATE_BILLBOARD_TEST_URL = "app.update.billboard.test_url";
const PREF_APP_UPDATE_CERT_ERRORS = "app.update.cert.errors";
@ -1581,7 +1580,9 @@ var gDownloadingPage = {
LOG("gDownloadingPage", "onStopRequest - patch verification succeeded");
// If the background update pref is set, we should wait until the update
// is actually staged in the background.
if (getPref("getBoolPref", PREF_APP_UPDATE_BACKGROUND, false)) {
var aus = CoC["@mozilla.org/updates/update-service;1"].
getService(CoI.nsIApplicationUpdateService);
if (aus.canStageUpdates) {
this._setUpdateApplying();
} else {
this.cleanUp();
@ -1602,6 +1603,12 @@ var gDownloadingPage = {
*/
observe: function(aSubject, aTopic, aData) {
if (aTopic == "update-staged") {
if (aData == STATE_DOWNLOADING) {
// We've fallen back to downloding the full update because the
// partial update failed to get staged in the background.
this._setStatus("downloading");
return;
}
this.cleanUp();
if (aData == STATE_APPLIED ||
aData == STATE_APPLIED_SVC ||

View File

@ -352,7 +352,7 @@ interface nsIUpdateChecker : nsISupports
* background update checks and provides utilities for selecting and
* downloading update patches.
*/
[scriptable, uuid(7bd62f69-f604-484b-b97c-e7229d7a3ee8)]
[scriptable, uuid(900b4a18-3bef-4f3e-bcf5-84dce0021c6d)]
interface nsIApplicationUpdateService : nsISupports
{
/**
@ -422,6 +422,11 @@ interface nsIApplicationUpdateService : nsISupports
* privileges to the install directory.
*/
readonly attribute boolean canApplyUpdates;
/**
* Whether the Update Service is able to stage updates.
*/
readonly attribute boolean canStageUpdates;
};
/**

View File

@ -432,15 +432,15 @@ XPCOMUtils.defineLazyGetter(this, "gCanStageUpdates", function aus_gCanStageUpda
#endif
try {
var updateTestFile = getUpdateFile([FILE_PERMS_TEST]);
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 = getUpdateDirCreate([]);
updateTestFile = updateTestFile.parent;
updateTestFile = getInstallDirRoot().parent;
updateTestFile.append(FILE_PERMS_TEST);
LOG("gCanStageUpdates - testing write access " + updateTestFile.path);
updateTestFile.createUnique(Ci.nsILocalFile.DIRECTORY_TYPE,
@ -552,6 +552,22 @@ function getUpdateDirCreate(pathArray) {
return FileUtils.getDir(KEY_APPDIR, pathArray, true);
}
/**
* Gets the root of the installation directory which is the application
* bundle directory on Mac OS X and the location of the application binary
* on all other platforms.
*
* @return nsIFile object for the directory
*/
function getInstallDirRoot() {
var dir = FileUtils.getDir(KEY_APPDIR, [], false);
#ifdef XP_MACOSX
// On Mac, we store the Updated.app directory inside the bundle directory.
dir = dir.parent.parent;
#endif
return dir;
}
/**
* Gets the file at the specified hierarchy under the update root directory.
* @param pathArray
@ -990,6 +1006,36 @@ function handleUpdateFailure(update, errorCode) {
return false;
}
/**
* Fall back to downloading a complete update in case an update has failed.
*
* @param update the update object that has failed to apply.
*/
function handleFallbackToCompleteUpdate(update) {
cleanupActiveUpdate();
update.statusText = gUpdateBundle.GetStringFromName("patchApplyFailure");
var oldType = update.selectedPatch ? update.selectedPatch.type
: "complete";
if (update.selectedPatch && oldType == "partial" && update.patchCount == 2) {
// Partial patch application failed, try downloading the complete
// update in the background instead.
LOG("UpdateService:_postUpdateProcessing - install of partial patch " +
"failed, downloading complete patch");
var status = Cc["@mozilla.org/updates/update-service;1"].
getService(Ci.nsIApplicationUpdateService).
downloadUpdate(update, true);
if (status == STATE_NONE)
cleanupActiveUpdate();
}
else {
LOG("handleFallbackToCompleteUpdate - install of complete or " +
"only one patch offered failed.");
}
update.QueryInterface(Ci.nsIWritablePropertyBag);
update.setProperty("patchingFailed", oldType);
}
/**
* Update Patch
* @param patch
@ -1571,26 +1617,8 @@ UpdateService.prototype = {
}
// Something went wrong with the patch application process.
cleanupActiveUpdate();
handleFallbackToCompleteUpdate(update);
update.statusText = gUpdateBundle.GetStringFromName("patchApplyFailure");
var oldType = update.selectedPatch ? update.selectedPatch.type
: "complete";
if (update.selectedPatch && oldType == "partial" && update.patchCount == 2) {
// Partial patch application failed, try downloading the complete
// update in the background instead.
LOG("UpdateService:_postUpdateProcessing - install of partial patch " +
"failed, downloading complete patch");
var status = this.downloadUpdate(update, true);
if (status == STATE_NONE)
cleanupActiveUpdate();
}
else {
LOG("UpdateService:_postUpdateProcessing - install of complete or " +
"only one patch offered failed... showing error.");
}
update.QueryInterface(Ci.nsIWritablePropertyBag);
update.setProperty("patchingFailed", oldType);
prompter.showUpdateError(update);
}
},
@ -2018,6 +2046,13 @@ UpdateService.prototype = {
return gCanApplyUpdates;
},
/**
* See nsIUpdateService.idl
*/
get canStageUpdates() {
return gCanStageUpdates;
},
/**
* See nsIUpdateService.idl
*/
@ -2391,7 +2426,9 @@ UpdateManager.prototype = {
update.state = ary[0];
if (update.state == STATE_FAILED && ary[1]) {
updateSucceeded = false;
handleUpdateFailure(update, ary[1]);
if (!handleUpdateFailure(update, ary[1])) {
handleFallbackToCompleteUpdate(update);
}
}
if (update.state == STATE_APPLIED && shouldUseService()) {
writeStatusFile(getUpdatesDir(), update.state = STATE_APPLIED_SVC);
@ -2409,6 +2446,8 @@ UpdateManager.prototype = {
// Send an observer notification which the update wizard uses in
// order to update its UI.
LOG("UpdateManager:refreshUpdateStatus - Notifying observers that " +
"the update was staged. state: " + update.state + ", status: " + status);
Services.obs.notifyObservers(null, "update-staged", update.state);
// Do this after *everything* else, since it will likely cause the app

View File

@ -1815,6 +1815,23 @@ ProcessReplaceRequest()
LOG(("Begin moving sourceDir (" LOG_S ") to tmpDir (" LOG_S ")\n",
sourceDir, tmpDir));
int rv = rename_file(sourceDir, tmpDir, true);
#ifdef XP_WIN
// On Windows, if Firefox is launched using the shortcut, it will hold a handle
// to its installation directory open, which might not get released in time.
// Therefore we wait a little bit here to see if the handle is released.
// If it's not released, we just fail to perform the replace request.
const int max_retries = 10;
int retries = 0;
while (rv == WRITE_ERROR && (retries++ < max_retries)) {
LOG(("PerformReplaceRequest: sourceDir rename attempt %d failed. " \
"File: " LOG_S ". Last error: %d, err: %d\n", retries,
sourceDir, GetLastError(), rv));
Sleep(100);
rv = rename_file(sourceDir, tmpDir, true);
}
#endif
if (rv) {
LOG(("Moving sourceDir to tmpDir failed, err: %d\n", rv));
return rv;
@ -2428,7 +2445,10 @@ int NS_main(int argc, NS_tchar **argv)
// we need to manually start the PostUpdate process from the
// current user's session of this unelevated updater.exe the
// current process is running as.
if (useService) {
// Note that we don't need to do this if we're just staging the
// update in the background, as the PostUpdate step runs when
// performing the replacing in that case.
if (useService && !sBackgroundUpdate) {
bool updateStatusSucceeded = false;
if (IsUpdateStatusSucceeded(updateStatusSucceeded) &&
updateStatusSucceeded) {
@ -2751,12 +2771,12 @@ int NS_main(int argc, NS_tchar **argv)
// the service to a newer version in that case. If we are not running
// through the service, then MOZ_USING_SERVICE will not exist.
if (!usingService) {
if (!LaunchWinPostProcess(argv[callbackIndex], gSourcePath, false, NULL)) {
LOG(("NS_main: The post update process could not be launched.\n"));
}
NS_tchar installDir[MAXPATHLEN];
if (GetInstallationDir(installDir)) {
if (!LaunchWinPostProcess(installDir, gSourcePath, false, NULL)) {
LOG(("NS_main: The post update process could not be launched.\n"));
}
StartServiceUpdate(installDir);
}
}