зеркало из https://github.com/mozilla/gecko-dev.git
Bug 394984: Enable any admin user on OSX to update Firefox, front-end and updater changes. r=rstrong
This commit is contained in:
Родитель
e084e2d68f
Коммит
f38b8146dd
|
@ -10,6 +10,9 @@ Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
|
||||
const PREF_APP_UPDATE_CANCELATIONS_OSX = "app.update.cancelations.osx";
|
||||
const PREF_APP_UPDATE_ELEVATE_NEVER = "app.update.elevate.never";
|
||||
|
||||
var gAppUpdater;
|
||||
|
||||
function onUnload(aEvent) {
|
||||
|
@ -76,7 +79,8 @@ function appUpdater()
|
|||
// update checks, but also in the About dialog, by presenting a
|
||||
// "Check for updates" button.
|
||||
// If updates are found, the user is then asked if he wants to "Update to <version>".
|
||||
if (!this.updateEnabled) {
|
||||
if (!this.updateEnabled ||
|
||||
Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_NEVER)) {
|
||||
this.selectPanel("checkForUpdates");
|
||||
return;
|
||||
}
|
||||
|
@ -98,11 +102,13 @@ appUpdater.prototype =
|
|||
get isPending() {
|
||||
if (this.update) {
|
||||
return this.update.state == "pending" ||
|
||||
this.update.state == "pending-service";
|
||||
this.update.state == "pending-service" ||
|
||||
this.update.state == "pending-elevate";
|
||||
}
|
||||
return this.um.activeUpdate &&
|
||||
(this.um.activeUpdate.state == "pending" ||
|
||||
this.um.activeUpdate.state == "pending-service");
|
||||
this.um.activeUpdate.state == "pending-service" ||
|
||||
this.um.activeUpdate.state == "pending-elevate");
|
||||
},
|
||||
|
||||
// true when there is an update already installed in the background.
|
||||
|
@ -183,6 +189,13 @@ appUpdater.prototype =
|
|||
* Check for updates
|
||||
*/
|
||||
checkForUpdates: function() {
|
||||
// Clear prefs that could prevent a user from discovering available updates.
|
||||
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CANCELATIONS_OSX)) {
|
||||
Services.prefs.clearUserPref(PREF_APP_UPDATE_CANCELATIONS_OSX);
|
||||
}
|
||||
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_NEVER)) {
|
||||
Services.prefs.clearUserPref(PREF_APP_UPDATE_ELEVATE_NEVER);
|
||||
}
|
||||
this.selectPanel("checkingForUpdates");
|
||||
this.isChecking = true;
|
||||
this.checker.checkForUpdates(this.updateCheckListener, true);
|
||||
|
@ -194,30 +207,32 @@ appUpdater.prototype =
|
|||
* which is presented after the download has been downloaded.
|
||||
*/
|
||||
buttonRestartAfterDownload: function() {
|
||||
if (!this.isPending && !this.isApplied)
|
||||
if (!this.isPending && !this.isApplied) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify all windows that an application quit has been requested.
|
||||
let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"].
|
||||
createInstance(Components.interfaces.nsISupportsPRBool);
|
||||
Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
|
||||
// Notify all windows that an application quit has been requested.
|
||||
let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"].
|
||||
createInstance(Components.interfaces.nsISupportsPRBool);
|
||||
Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
|
||||
|
||||
// Something aborted the quit process.
|
||||
if (cancelQuit.data)
|
||||
return;
|
||||
// Something aborted the quit process.
|
||||
if (cancelQuit.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
let appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"].
|
||||
getService(Components.interfaces.nsIAppStartup);
|
||||
let appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"].
|
||||
getService(Components.interfaces.nsIAppStartup);
|
||||
|
||||
// If already in safe mode restart in safe mode (bug 327119)
|
||||
if (Services.appinfo.inSafeMode) {
|
||||
appStartup.restartInSafeMode(Components.interfaces.nsIAppStartup.eAttemptQuit);
|
||||
return;
|
||||
}
|
||||
// If already in safe mode restart in safe mode (bug 327119)
|
||||
if (Services.appinfo.inSafeMode) {
|
||||
appStartup.restartInSafeMode(Components.interfaces.nsIAppStartup.eAttemptQuit);
|
||||
return;
|
||||
}
|
||||
|
||||
appStartup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit |
|
||||
Components.interfaces.nsIAppStartup.eRestart);
|
||||
},
|
||||
appStartup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit |
|
||||
Components.interfaces.nsIAppStartup.eRestart);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles oncommand for the "Apply Update…" button
|
||||
|
@ -371,7 +386,8 @@ appUpdater.prototype =
|
|||
// Update the UI when the background updater is finished
|
||||
let status = aData;
|
||||
if (status == "applied" || status == "applied-service" ||
|
||||
status == "pending" || status == "pending-service") {
|
||||
status == "pending" || status == "pending-service" ||
|
||||
status == "pending-elevate") {
|
||||
// If the update is successfully applied, or if the updater has
|
||||
// fallen back to non-staged updates, show the "Restart to Update"
|
||||
// button.
|
||||
|
|
|
@ -2566,6 +2566,7 @@ var gMenuButtonUpdateBadge = {
|
|||
enabled: false,
|
||||
badgeWaitTime: 0,
|
||||
timer: null,
|
||||
cancelObserverRegistered: false,
|
||||
|
||||
init: function () {
|
||||
try {
|
||||
|
@ -2590,6 +2591,10 @@ var gMenuButtonUpdateBadge = {
|
|||
Services.obs.removeObserver(this, "update-downloaded");
|
||||
this.enabled = false;
|
||||
}
|
||||
if (this.cancelObserverRegistered) {
|
||||
Services.obs.removeObserver(this, "update-canceled");
|
||||
this.cancelObserverRegistered = false;
|
||||
}
|
||||
},
|
||||
|
||||
onMenuPanelCommand: function(event) {
|
||||
|
@ -2610,11 +2615,15 @@ var gMenuButtonUpdateBadge = {
|
|||
},
|
||||
|
||||
observe: function (subject, topic, status) {
|
||||
if (topic == "update-canceled") {
|
||||
this.reset();
|
||||
return;
|
||||
}
|
||||
if (status == "failed") {
|
||||
// Background update has failed, let's show the UI responsible for
|
||||
// prompting the user to update manually.
|
||||
this.displayBadge(false);
|
||||
this.uninit();
|
||||
this.displayBadge(false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2629,8 +2638,8 @@ var gMenuButtonUpdateBadge = {
|
|||
// If the update is successfully applied, or if the updater has fallen back
|
||||
// to non-staged updates, add a badge to the hamburger menu to indicate an
|
||||
// update will be applied once the browser restarts.
|
||||
this.displayBadge(true);
|
||||
this.uninit();
|
||||
this.displayBadge(true);
|
||||
},
|
||||
|
||||
displayBadge: function (succeeded) {
|
||||
|
@ -2646,6 +2655,8 @@ var gMenuButtonUpdateBadge = {
|
|||
stringId = "appmenu.restartNeeded.description";
|
||||
updateButtonText = gNavigatorBundle.getFormattedString(stringId,
|
||||
[brandShortName]);
|
||||
Services.obs.addObserver(this, "update-canceled", false);
|
||||
this.cancelObserverRegistered = true;
|
||||
} else {
|
||||
stringId = "appmenu.updateFailed.description";
|
||||
updateButtonText = gNavigatorBundle.getString(stringId);
|
||||
|
@ -2655,6 +2666,15 @@ var gMenuButtonUpdateBadge = {
|
|||
updateButton.setAttribute("label", updateButtonText);
|
||||
updateButton.setAttribute("update-status", status);
|
||||
updateButton.hidden = false;
|
||||
},
|
||||
|
||||
reset: function () {
|
||||
gMenuButtonBadgeManager.removeBadge(
|
||||
gMenuButtonBadgeManager.BADGEID_APPUPDATE);
|
||||
let updateButton = document.getElementById("PanelUI-update-status");
|
||||
updateButton.hidden = true;
|
||||
this.uninit();
|
||||
this.init();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -75,6 +75,10 @@
|
|||
<!-- LOCALIZATION NOTE (finishedBackground.more): This string describes the button labels defined by restartNowButton and restartLaterButton in updates.properties. -->
|
||||
<!ENTITY finishedBackground.more "The update will be installed the next time &brandShortName; starts. You
|
||||
can restart &brandShortName; now, or continue working and restart later.">
|
||||
<!ENTITY finishedBackground.moreElevated "This update requires administrator privileges. The update will be
|
||||
installed the next time &brandShortName; starts. You can restart
|
||||
&brandShortName; now, continue working and restart later, or decline this
|
||||
update.">
|
||||
|
||||
<!ENTITY installed.title "Update Installed">
|
||||
<!ENTITY installed.intro "The update was successfully installed.">
|
||||
|
|
|
@ -70,6 +70,7 @@ updatesfound_major.title=New Version Available
|
|||
installSuccess=The Update was successfully installed
|
||||
installPending=Install Pending
|
||||
patchApplyFailure=The Update could not be installed (patch apply failed)
|
||||
elevationFailure=You don’t have the permissions necessary to install this update. Please contact your system administrator.
|
||||
|
||||
# LOCALIZATION NOTE: %S is the amount downloaded so far
|
||||
# example: Paused — 879 KB of 2.1 MB
|
||||
|
|
|
@ -103,6 +103,12 @@ this.AUSTLMY = {
|
|||
CHK_INVALID_USER_OVERRIDE_URL: 33,
|
||||
// Invalid url for app.update.url.override user preference (no notification)
|
||||
CHK_INVALID_DEFAULT_OVERRIDE_URL: 34,
|
||||
// Update elevation failures or cancelations threshold reached for this
|
||||
// version, OSX only (no notification)
|
||||
CHK_ELEVATION_DISABLED_FOR_VERSION: 35,
|
||||
// User opted out of elevated updates for the available update version, OSX
|
||||
// only (no notification)
|
||||
CHK_ELEVATION_OPTOUT_FOR_VERSION: 36,
|
||||
|
||||
/**
|
||||
* Submit a telemetry ping for the update check result code or a telemetry
|
||||
|
|
|
@ -18,6 +18,7 @@ const XMLNS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul
|
|||
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";
|
||||
const PREF_APP_UPDATE_ELEVATE_NEVER = "app.update.elevate.never";
|
||||
const PREF_APP_UPDATE_ENABLED = "app.update.enabled";
|
||||
const PREF_APP_UPDATE_LOG = "app.update.log";
|
||||
const PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED = "app.update.notifiedUnsupported";
|
||||
|
@ -34,10 +35,11 @@ const URI_UPDATES_PROPERTIES = "chrome://mozapps/locale/update/updates.properti
|
|||
|
||||
const STATE_DOWNLOADING = "downloading";
|
||||
const STATE_PENDING = "pending";
|
||||
const STATE_PENDING_SVC = "pending-service";
|
||||
const STATE_PENDING_SERVICE = "pending-service";
|
||||
const STATE_PENDING_ELEVATE = "pending-elevate";
|
||||
const STATE_APPLYING = "applying";
|
||||
const STATE_APPLIED = "applied";
|
||||
const STATE_APPLIED_SVC = "applied-service";
|
||||
const STATE_APPLIED_SERVICE = "applied-service";
|
||||
const STATE_SUCCEEDED = "succeeded";
|
||||
const STATE_DOWNLOAD_FAILED = "download-failed";
|
||||
const STATE_FAILED = "failed";
|
||||
|
@ -234,9 +236,19 @@ var gUpdates = {
|
|||
never: function () {
|
||||
// If the user clicks "No Thanks", we should not prompt them to update to
|
||||
// this version again unless they manually select "Check for Updates..."
|
||||
// which will clear all of the "never" prefs.
|
||||
var neverPrefName = PREFBRANCH_APP_UPDATE_NEVER + this.update.appVersion;
|
||||
// which will clear all of the "never" prefs. There are currently two
|
||||
// "never" prefs: the older PREFBRANCH_APP_UPDATE_NEVER as well as the
|
||||
// OSX-only PREF_APP_UPDATE_ELEVATE_NEVER. We set both of these prefs (if
|
||||
// applicable) to ensure that we don't prompt the user regardless of which
|
||||
// pref is checked.
|
||||
let neverPrefName = PREFBRANCH_APP_UPDATE_NEVER + this.update.appVersion;
|
||||
Services.prefs.setBoolPref(neverPrefName, true);
|
||||
let aus = CoC["@mozilla.org/updates/update-service;1"].
|
||||
getService(CoI.nsIApplicationUpdateService);
|
||||
if (aus.elevationRequired) {
|
||||
Services.prefs.setCharPref(PREF_APP_UPDATE_ELEVATE_NEVER,
|
||||
this.update.appVersion);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -414,9 +426,10 @@ var gUpdates = {
|
|||
// the Update.
|
||||
switch (state) {
|
||||
case STATE_PENDING:
|
||||
case STATE_PENDING_SVC:
|
||||
case STATE_PENDING_SERVICE:
|
||||
case STATE_PENDING_ELEVATE:
|
||||
case STATE_APPLIED:
|
||||
case STATE_APPLIED_SVC:
|
||||
case STATE_APPLIED_SERVICE:
|
||||
this.sourceEvent = SRCEVT_BACKGROUND;
|
||||
aCallback("finishedBackground");
|
||||
return;
|
||||
|
@ -525,16 +538,21 @@ var gCheckingPage = {
|
|||
// then canceled. If we don't clear the "never" prefs future
|
||||
// notifications will never happen.
|
||||
Services.prefs.deleteBranch(PREFBRANCH_APP_UPDATE_NEVER);
|
||||
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_NEVER)) {
|
||||
Services.prefs.clearUserPref(PREF_APP_UPDATE_ELEVATE_NEVER);
|
||||
}
|
||||
|
||||
// The user will be notified if there is an error so clear the background
|
||||
// check error count.
|
||||
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDERRORS))
|
||||
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDERRORS)) {
|
||||
Services.prefs.clearUserPref(PREF_APP_UPDATE_BACKGROUNDERRORS);
|
||||
}
|
||||
|
||||
// The preference will be set back to true if the system is still
|
||||
// unsupported.
|
||||
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED))
|
||||
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED)) {
|
||||
Services.prefs.clearUserPref(PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED);
|
||||
}
|
||||
|
||||
this._checker = CoC["@mozilla.org/updates/update-checker;1"].
|
||||
createInstance(CoI.nsIUpdateChecker);
|
||||
|
@ -568,7 +586,7 @@ var gCheckingPage = {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!aus.canApplyUpdates) {
|
||||
if (!aus.canApplyUpdates || gUpdates.update.elevationFailure) {
|
||||
// Prevent multiple notifications for the same update when the user is
|
||||
// unable to apply updates.
|
||||
gUpdates.never();
|
||||
|
@ -1296,9 +1314,10 @@ var gDownloadingPage = {
|
|||
}
|
||||
this.cleanUp();
|
||||
if (aData == STATE_APPLIED ||
|
||||
aData == STATE_APPLIED_SVC ||
|
||||
aData == STATE_APPLIED_SERVICE ||
|
||||
aData == STATE_PENDING ||
|
||||
aData == STATE_PENDING_SVC) {
|
||||
aData == STATE_PENDING_SERVICE ||
|
||||
aData == STATE_PENDING_ELEVATE) {
|
||||
// If the update is successfully applied, or if the updater has
|
||||
// fallen back to non-staged updates, go to the finish page.
|
||||
gUpdates.wiz.goTo("finished");
|
||||
|
@ -1399,7 +1418,7 @@ var gErrorPatchingPage = {
|
|||
onWizardNext: function() {
|
||||
switch (gUpdates.update.selectedPatch.state) {
|
||||
case STATE_PENDING:
|
||||
case STATE_PENDING_SVC:
|
||||
case STATE_PENDING_SERVICE:
|
||||
gUpdates.wiz.goTo("finished");
|
||||
break;
|
||||
case STATE_DOWNLOADING:
|
||||
|
@ -1421,8 +1440,17 @@ var gFinishedPage = {
|
|||
* Initialize
|
||||
*/
|
||||
onPageShow: function() {
|
||||
gUpdates.setButtons("restartLaterButton", null, "restartNowButton",
|
||||
true);
|
||||
let aus = CoC["@mozilla.org/updates/update-service;1"].
|
||||
getService(CoI.nsIApplicationUpdateService);
|
||||
if (aus.elevationRequired) {
|
||||
LOG("gFinishedPage", "elevationRequired");
|
||||
gUpdates.setButtons("restartLaterButton", "noThanksButton",
|
||||
"restartNowButton", true);
|
||||
} else {
|
||||
LOG("gFinishedPage", "not elevationRequired");
|
||||
gUpdates.setButtons("restartLaterButton", null, "restartNowButton",
|
||||
true);
|
||||
}
|
||||
gUpdates.wiz.getButton("finish").focus();
|
||||
},
|
||||
|
||||
|
@ -1431,18 +1459,36 @@ var gFinishedPage = {
|
|||
*/
|
||||
onPageShowBackground: function() {
|
||||
this.onPageShow();
|
||||
var updateFinishedName = document.getElementById("updateFinishedName");
|
||||
let updateFinishedName = document.getElementById("updateFinishedName");
|
||||
updateFinishedName.value = gUpdates.update.name;
|
||||
|
||||
var link = document.getElementById("finishedBackgroundLink");
|
||||
let link = document.getElementById("finishedBackgroundLink");
|
||||
if (gUpdates.update.detailsURL) {
|
||||
link.setAttribute("url", gUpdates.update.detailsURL);
|
||||
// The details link is stealing focus so it is disabled by default and
|
||||
// should only be enabled after onPageShow has been called.
|
||||
link.disabled = false;
|
||||
}
|
||||
else
|
||||
} else {
|
||||
link.hidden = true;
|
||||
}
|
||||
let aus = CoC["@mozilla.org/updates/update-service;1"].
|
||||
getService(CoI.nsIApplicationUpdateService);
|
||||
if (aus.elevationRequired) {
|
||||
let more = document.getElementById("finishedBackgroundMore");
|
||||
more.setAttribute("hidden", "true");
|
||||
let moreElevated =
|
||||
document.getElementById("finishedBackgroundMoreElevated");
|
||||
moreElevated.setAttribute("hidden", "false");
|
||||
let moreElevatedLink =
|
||||
document.getElementById("finishedBackgroundMoreElevatedLink");
|
||||
moreElevatedLink.setAttribute("hidden", "false");
|
||||
let moreElevatedLinkLabel =
|
||||
document.getElementById("finishedBackgroundMoreElevatedLinkLabel");
|
||||
let manualURL = Services.urlFormatter.formatURLPref(PREF_APP_UPDATE_URL_MANUAL);
|
||||
moreElevatedLinkLabel.value = manualURL;
|
||||
moreElevatedLinkLabel.setAttribute("url", manualURL);
|
||||
moreElevatedLinkLabel.setAttribute("hidden", "false");
|
||||
}
|
||||
|
||||
if (getPref("getBoolPref", PREF_APP_UPDATE_TEST_LOOP, false)) {
|
||||
setTimeout(function () {
|
||||
|
@ -1459,6 +1505,16 @@ var gFinishedPage = {
|
|||
// Do the restart
|
||||
LOG("gFinishedPage" , "onWizardFinish - restarting the application");
|
||||
|
||||
let aus = CoC["@mozilla.org/updates/update-service;1"].
|
||||
getService(CoI.nsIApplicationUpdateService);
|
||||
if (aus.elevationRequired) {
|
||||
let um = CoC["@mozilla.org/updates/update-manager;1"].
|
||||
getService(CoI.nsIUpdateManager);
|
||||
if (um) {
|
||||
um.elevationOptedIn();
|
||||
}
|
||||
}
|
||||
|
||||
// disable the "finish" (Restart) and "extra1" (Later) buttons
|
||||
// because the Software Update wizard is still up at the point,
|
||||
// and will remain up until we return and we close the
|
||||
|
@ -1499,7 +1555,19 @@ var gFinishedPage = {
|
|||
*/
|
||||
onExtra1: function() {
|
||||
gUpdates.wiz.cancel();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* When elevation is required and the user clicks "No Thanks" in the wizard.
|
||||
*/
|
||||
onExtra2: Task.async(function*() {
|
||||
Services.obs.notifyObservers(null, "update-canceled", null);
|
||||
let um = CoC["@mozilla.org/updates/update-manager;1"].
|
||||
getService(CoI.nsIUpdateManager);
|
||||
um.cleanupActiveUpdate();
|
||||
gUpdates.never();
|
||||
gUpdates.wiz.cancel();
|
||||
}),
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -205,6 +205,7 @@
|
|||
|
||||
<wizardpage id="finishedBackground" pageid="finishedBackground"
|
||||
object="gFinishedPage" onextra1="gFinishedPage.onExtra1()"
|
||||
onextra2="gFinishedPage.onExtra2()"
|
||||
onpageshow="gFinishedPage.onPageShowBackground();">
|
||||
<updateheader label="&finishedPage.title;"/>
|
||||
<vbox class="update-content" flex="1">
|
||||
|
@ -218,6 +219,14 @@
|
|||
</hbox>
|
||||
<spacer flex="1"/>
|
||||
<label id="finishedBackgroundMore">&finishedBackground.more;</label>
|
||||
<label id="finishedBackgroundMoreElevated"
|
||||
hidden="true">&finishedBackground.moreElevated;</label>
|
||||
<label id="finishedBackgroundMoreElevatedLink"
|
||||
hidden="true">&errorManual.label;</label>
|
||||
<hbox>
|
||||
<label class="text-link" id="finishedBackgroundMoreElevatedLinkLabel"
|
||||
value="" onclick="openUpdateURL(event);" hidden="true"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</wizardpage>
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ interface nsIUpdatePatch : nsISupports
|
|||
* that the front end and other application services can use to learn more
|
||||
* about what is going on.
|
||||
*/
|
||||
[scriptable, uuid(6b0b7721-6746-443d-8cb0-c6199d7f28a6)]
|
||||
[scriptable, uuid(e094c045-f4ff-41fd-92da-cd2effd2c7c9)]
|
||||
interface nsIUpdate : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -221,6 +221,7 @@ interface nsIUpdate : nsISupports
|
|||
* "downloading" The update is being downloaded.
|
||||
* "pending" The update is ready to be applied.
|
||||
* "pending-service" The update is ready to be applied with the service.
|
||||
* "pending-elevate" The update is ready to be applied but requires elevation.
|
||||
* "applying" The update is being applied.
|
||||
* "applied" The update is ready to be switched to.
|
||||
* "applied-os" The update is OS update and to be installed.
|
||||
|
@ -242,6 +243,11 @@ interface nsIUpdate : nsISupports
|
|||
*/
|
||||
attribute long errorCode;
|
||||
|
||||
/**
|
||||
* Whether an elevation failure has been encountered for this update.
|
||||
*/
|
||||
attribute boolean elevationFailure;
|
||||
|
||||
/**
|
||||
* The number of patches supplied by this update.
|
||||
*/
|
||||
|
@ -344,7 +350,7 @@ interface nsIUpdateChecker : nsISupports
|
|||
* background update checks and provides utilities for selecting and
|
||||
* downloading update patches.
|
||||
*/
|
||||
[scriptable, uuid(9f9b51f5-340e-47ce-85ae-9eb077c6cd39)]
|
||||
[scriptable, uuid(1107d207-a263-403a-b268-05772ec10757)]
|
||||
interface nsIApplicationUpdateService : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -423,6 +429,12 @@ interface nsIApplicationUpdateService : nsISupports
|
|||
*/
|
||||
readonly attribute boolean canCheckForUpdates;
|
||||
|
||||
/**
|
||||
* Whether or not the installation requires elevation. Currently only
|
||||
* implemented on OSX, returns false on other platforms.
|
||||
*/
|
||||
readonly attribute boolean elevationRequired;
|
||||
|
||||
/**
|
||||
* Whether or not the Update Service can download and install updates.
|
||||
* On Windows, this is a function of whether or not the maintenance service
|
||||
|
@ -468,7 +480,7 @@ interface nsIUpdateProcessor : nsISupports
|
|||
* An interface describing a global application service that maintains a list
|
||||
* of updates previously performed as well as the current active update.
|
||||
*/
|
||||
[scriptable, uuid(f8371237-10a6-46a5-b23f-f6f7684e9d71)]
|
||||
[scriptable, uuid(0f1098e9-a447-4af9-b030-6f8f35c85f89)]
|
||||
interface nsIUpdateManager : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -498,13 +510,24 @@ interface nsIUpdateManager : nsISupports
|
|||
* Refresh the update status based on the information in update.status.
|
||||
*/
|
||||
void refreshUpdateStatus();
|
||||
|
||||
/**
|
||||
* The user agreed to proceed with an elevated update and we are now
|
||||
* permitted to show an elevation prompt.
|
||||
*/
|
||||
void elevationOptedIn();
|
||||
|
||||
/**
|
||||
* Clean up and remove the active update without applying it.
|
||||
*/
|
||||
void cleanupActiveUpdate();
|
||||
};
|
||||
|
||||
/**
|
||||
* An interface describing an object that can show various kinds of Update
|
||||
* notification UI to the user.
|
||||
*/
|
||||
[scriptable, uuid(599fd3c6-ec68-4499-ada5-2997739c97a6)]
|
||||
[scriptable, uuid(cee3bd60-c564-42ff-a2bf-d442cb15f75c)]
|
||||
interface nsIUpdatePrompt : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -562,4 +585,11 @@ interface nsIUpdatePrompt : nsISupports
|
|||
* An nsIDOMWindow to set as the parent for this window. Can be null.
|
||||
*/
|
||||
void showUpdateHistory(in nsIDOMWindow parent);
|
||||
|
||||
/**
|
||||
* Shows the application update downloaded user interface advising that an
|
||||
* update, which requires elevation, has now been downloaded and a restart is
|
||||
* necessary to complete the update.
|
||||
*/
|
||||
void showUpdateElevationRequired();
|
||||
};
|
||||
|
|
|
@ -18,36 +18,40 @@ Cu.import("resource://gre/modules/AppConstants.jsm", this);
|
|||
const UPDATESERVICE_CID = Components.ID("{B3C290A6-3943-4B89-8BBE-C01EB7B3B311}");
|
||||
const UPDATESERVICE_CONTRACTID = "@mozilla.org/updates/update-service;1";
|
||||
|
||||
const PREF_APP_UPDATE_ALTWINDOWTYPE = "app.update.altwindowtype";
|
||||
const PREF_APP_UPDATE_AUTO = "app.update.auto";
|
||||
const PREF_APP_UPDATE_BACKGROUND_INTERVAL = "app.update.download.backgroundInterval";
|
||||
const PREF_APP_UPDATE_BACKGROUNDERRORS = "app.update.backgroundErrors";
|
||||
const PREF_APP_UPDATE_BACKGROUNDMAXERRORS = "app.update.backgroundMaxErrors";
|
||||
const PREF_APP_UPDATE_CANCELATIONS = "app.update.cancelations";
|
||||
const PREF_APP_UPDATE_CERTS_BRANCH = "app.update.certs.";
|
||||
const PREF_APP_UPDATE_CERT_CHECKATTRS = "app.update.cert.checkAttributes";
|
||||
const PREF_APP_UPDATE_CERT_ERRORS = "app.update.cert.errors";
|
||||
const PREF_APP_UPDATE_CERT_MAXERRORS = "app.update.cert.maxErrors";
|
||||
const PREF_APP_UPDATE_CERT_REQUIREBUILTIN = "app.update.cert.requireBuiltIn";
|
||||
const PREF_APP_UPDATE_ENABLED = "app.update.enabled";
|
||||
const PREF_APP_UPDATE_IDLETIME = "app.update.idletime";
|
||||
const PREF_APP_UPDATE_INTERVAL = "app.update.interval";
|
||||
const PREF_APP_UPDATE_LOG = "app.update.log";
|
||||
const PREF_APP_UPDATE_NEVER_BRANCH = "app.update.never.";
|
||||
const PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED = "app.update.notifiedUnsupported";
|
||||
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_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";
|
||||
const PREF_APP_UPDATE_SERVICE_ENABLED = "app.update.service.enabled";
|
||||
const PREF_APP_UPDATE_SERVICE_ERRORS = "app.update.service.errors";
|
||||
const PREF_APP_UPDATE_SERVICE_MAX_ERRORS = "app.update.service.maxErrors";
|
||||
const PREF_APP_UPDATE_SOCKET_ERRORS = "app.update.socket.maxErrors";
|
||||
const PREF_APP_UPDATE_RETRY_TIMEOUT = "app.update.socket.retryTimeout";
|
||||
const PREF_APP_UPDATE_ALTWINDOWTYPE = "app.update.altwindowtype";
|
||||
const PREF_APP_UPDATE_AUTO = "app.update.auto";
|
||||
const PREF_APP_UPDATE_BACKGROUND_INTERVAL = "app.update.download.backgroundInterval";
|
||||
const PREF_APP_UPDATE_BACKGROUNDERRORS = "app.update.backgroundErrors";
|
||||
const PREF_APP_UPDATE_BACKGROUNDMAXERRORS = "app.update.backgroundMaxErrors";
|
||||
const PREF_APP_UPDATE_CANCELATIONS = "app.update.cancelations";
|
||||
const PREF_APP_UPDATE_CANCELATIONS_OSX = "app.update.cancelations.osx";
|
||||
const PREF_APP_UPDATE_CERTS_BRANCH = "app.update.certs.";
|
||||
const PREF_APP_UPDATE_CERT_CHECKATTRS = "app.update.cert.checkAttributes";
|
||||
const PREF_APP_UPDATE_CERT_ERRORS = "app.update.cert.errors";
|
||||
const PREF_APP_UPDATE_CERT_MAXERRORS = "app.update.cert.maxErrors";
|
||||
const PREF_APP_UPDATE_CERT_REQUIREBUILTIN = "app.update.cert.requireBuiltIn";
|
||||
const PREF_APP_UPDATE_ELEVATE_NEVER = "app.update.elevate.never";
|
||||
const PREF_APP_UPDATE_ELEVATE_VERSION = "app.update.elevate.version";
|
||||
const PREF_APP_UPDATE_ENABLED = "app.update.enabled";
|
||||
const PREF_APP_UPDATE_IDLETIME = "app.update.idletime";
|
||||
const PREF_APP_UPDATE_INTERVAL = "app.update.interval";
|
||||
const PREF_APP_UPDATE_LOG = "app.update.log";
|
||||
const PREF_APP_UPDATE_MAX_OSX_CANCELATIONS = "app.update.cancelations.osx.max";
|
||||
const PREF_APP_UPDATE_NEVER_BRANCH = "app.update.never.";
|
||||
const PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED = "app.update.notifiedUnsupported";
|
||||
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_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";
|
||||
const PREF_APP_UPDATE_SERVICE_ENABLED = "app.update.service.enabled";
|
||||
const PREF_APP_UPDATE_SERVICE_ERRORS = "app.update.service.errors";
|
||||
const PREF_APP_UPDATE_SERVICE_MAX_ERRORS = "app.update.service.maxErrors";
|
||||
const PREF_APP_UPDATE_SOCKET_ERRORS = "app.update.socket.maxErrors";
|
||||
const PREF_APP_UPDATE_RETRY_TIMEOUT = "app.update.socket.retryTimeout";
|
||||
|
||||
const URI_UPDATE_PROMPT_DIALOG = "chrome://mozapps/content/update/updates.xul";
|
||||
const URI_UPDATE_HISTORY_DIALOG = "chrome://mozapps/content/update/history.xul";
|
||||
|
@ -79,11 +83,12 @@ const FILE_BACKUP_LOG = "backup-update.log";
|
|||
const STATE_NONE = "null";
|
||||
const STATE_DOWNLOADING = "downloading";
|
||||
const STATE_PENDING = "pending";
|
||||
const STATE_PENDING_SVC = "pending-service";
|
||||
const STATE_PENDING_SERVICE = "pending-service";
|
||||
const STATE_PENDING_ELEVATE = "pending-elevate";
|
||||
const STATE_APPLYING = "applying";
|
||||
const STATE_APPLIED = "applied";
|
||||
const STATE_APPLIED_OS = "applied-os";
|
||||
const STATE_APPLIED_SVC = "applied-service";
|
||||
const STATE_APPLIED_SERVICE = "applied-service";
|
||||
const STATE_SUCCEEDED = "succeeded";
|
||||
const STATE_DOWNLOAD_FAILED = "download-failed";
|
||||
const STATE_FAILED = "failed";
|
||||
|
@ -183,6 +188,10 @@ const DEFAULT_SOCKET_MAX_ERRORS = 10;
|
|||
// The number of milliseconds to wait before retrying a connection error.
|
||||
const DEFAULT_UPDATE_RETRY_TIMEOUT = 2000;
|
||||
|
||||
// Default maximum number of elevation cancelations per update version before
|
||||
// giving up.
|
||||
const DEFAULT_MAX_OSX_CANCELATIONS = 3;
|
||||
|
||||
// This maps app IDs to their respective notification topic which signals when
|
||||
// the application's user interface has been displayed.
|
||||
const APPID_TO_TOPIC = {
|
||||
|
@ -347,6 +356,66 @@ function hasUpdateMutex() {
|
|||
return !!gUpdateMutexHandle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not all descendants of a directory are writeable.
|
||||
* Note: Does not check the root directory itself for writeability.
|
||||
*
|
||||
* @return true if all descendants are writeable, false otherwise
|
||||
*/
|
||||
function areDirectoryEntriesWriteable(aDir) {
|
||||
let items = aDir.directoryEntries;
|
||||
while (items.hasMoreElements()) {
|
||||
let item = items.getNext().QueryInterface(Ci.nsIFile);
|
||||
if (!item.isWritable()) {
|
||||
LOG("areDirectoryEntriesWriteable - unable to write to " + item.path);
|
||||
return false;
|
||||
}
|
||||
if (item.isDirectory() && !areDirectoryEntriesWriteable(item)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* OSX only function to determine if the user requires elevation to be able to
|
||||
* write to the application bundle.
|
||||
*
|
||||
* @return true if elevation is required, false otherwise
|
||||
*/
|
||||
function getElevationRequired() {
|
||||
if (AppConstants.platform != "macosx") {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// Recursively check that the application bundle (and its descendants) can
|
||||
// be written to.
|
||||
LOG("getElevationRequired - recursively testing write access on " +
|
||||
getInstallDirRoot().path);
|
||||
if (!getInstallDirRoot().isWritable() ||
|
||||
!areDirectoryEntriesWriteable(getInstallDirRoot())) {
|
||||
LOG("getElevationRequired - unable to write to application bundle, " +
|
||||
"elevation required");
|
||||
return true;
|
||||
}
|
||||
} catch (ex) {
|
||||
LOG("getElevationRequired - unable to write to application bundle, " +
|
||||
"elevation required. Exception: " + ex);
|
||||
return true;
|
||||
}
|
||||
LOG("getElevationRequired - able to write to application bundle, elevation " +
|
||||
"not required");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not an update can be applied. This is always true on
|
||||
* Windows when the service is used. Also, this is always true on OSX because we
|
||||
* offer users the option to perform an elevated update when necessary.
|
||||
*
|
||||
* @return true if an update can be applied, false otherwise
|
||||
*/
|
||||
function getCanApplyUpdates() {
|
||||
let useService = false;
|
||||
if (shouldUseService()) {
|
||||
|
@ -356,37 +425,27 @@ function getCanApplyUpdates() {
|
|||
useService = true;
|
||||
}
|
||||
|
||||
if (!useService) {
|
||||
if (!useService && AppConstants.platform != "macosx") {
|
||||
try {
|
||||
let updateTestFile = getUpdateFile([FILE_PERMS_TEST]);
|
||||
LOG("getCanApplyUpdates - testing write access " + updateTestFile.path);
|
||||
testWriteAccess(updateTestFile, false);
|
||||
if (AppConstants.platform == "macosx") {
|
||||
// Check that the application bundle can be written to.
|
||||
let appDirTestFile = getAppBaseDir();
|
||||
appDirTestFile.append(FILE_PERMS_TEST);
|
||||
LOG("getCanApplyUpdates - testing write access " + appDirTestFile.path);
|
||||
if (appDirTestFile.exists()) {
|
||||
appDirTestFile.remove(false);
|
||||
}
|
||||
appDirTestFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
|
||||
appDirTestFile.remove(false);
|
||||
} else if (AppConstants.platform == "win") {
|
||||
if (AppConstants.platform == "win") {
|
||||
// Example windowsVersion: Windows XP == 5.1
|
||||
let windowsVersion = Services.sysinfo.getProperty("version");
|
||||
LOG("getCanApplyUpdates - windowsVersion = " + windowsVersion);
|
||||
|
||||
/**
|
||||
* For Vista, updates can be performed to a location requiring admin
|
||||
* privileges by requesting elevation via the UAC prompt when launching
|
||||
* updater.exe if the appDir is under the Program Files directory
|
||||
* (e.g. C:\Program Files\) and UAC is turned on and we can elevate
|
||||
* (e.g. user has a split token).
|
||||
*
|
||||
* Note: this does note attempt to handle the case where UAC is turned on
|
||||
* and the installation directory is in a restricted location that
|
||||
* requires admin privileges to update other than Program Files.
|
||||
*/
|
||||
/**
|
||||
* For Vista, updates can be performed to a location requiring admin
|
||||
* privileges by requesting elevation via the UAC prompt when launching
|
||||
* updater.exe if the appDir is under the Program Files directory
|
||||
* (e.g. C:\Program Files\) and UAC is turned on and we can elevate
|
||||
* (e.g. user has a split token).
|
||||
*
|
||||
* Note: this does note attempt to handle the case where UAC is turned on
|
||||
* and the installation directory is in a restricted location that
|
||||
* requires admin privileges to update other than Program Files.
|
||||
*/
|
||||
let userCanElevate = false;
|
||||
|
||||
if (parseFloat(windowsVersion) >= 6) {
|
||||
|
@ -458,9 +517,20 @@ function getCanApplyUpdates() {
|
|||
*/
|
||||
XPCOMUtils.defineLazyGetter(this, "gCanStageUpdatesSession",
|
||||
function aus_gCanStageUpdatesSession() {
|
||||
if (getElevationRequired()) {
|
||||
LOG("gCanStageUpdatesSession - unable to stage updates because elevation " +
|
||||
"is required.");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
let updateTestFile = getInstallDirRoot();
|
||||
updateTestFile.append(FILE_PERMS_TEST);
|
||||
let updateTestFile;
|
||||
if (AppConstants.platform == "macosx") {
|
||||
updateTestFile = getUpdateFile([FILE_PERMS_TEST]);
|
||||
} else {
|
||||
updateTestFile = getInstallDirRoot();
|
||||
updateTestFile.append(FILE_PERMS_TEST);
|
||||
}
|
||||
LOG("gCanStageUpdatesSession - testing write access " +
|
||||
updateTestFile.path);
|
||||
testWriteAccess(updateTestFile, true);
|
||||
|
@ -1223,16 +1293,44 @@ function handleUpdateFailure(update, errorCode) {
|
|||
}
|
||||
|
||||
if (update.errorCode == ELEVATION_CANCELED) {
|
||||
writeStatusFile(getUpdatesDir(), update.state = STATE_PENDING);
|
||||
let cancelations = getPref("getIntPref", PREF_APP_UPDATE_CANCELATIONS, 0);
|
||||
cancelations++;
|
||||
Services.prefs.setIntPref(PREF_APP_UPDATE_CANCELATIONS, cancelations);
|
||||
if (AppConstants.platform == "macosx") {
|
||||
let osxCancelations = getPref("getIntPref",
|
||||
PREF_APP_UPDATE_CANCELATIONS_OSX, 0);
|
||||
osxCancelations++;
|
||||
Services.prefs.setIntPref(PREF_APP_UPDATE_CANCELATIONS_OSX,
|
||||
osxCancelations);
|
||||
let maxCancels = getPref("getIntPref",
|
||||
PREF_APP_UPDATE_MAX_OSX_CANCELATIONS,
|
||||
DEFAULT_MAX_OSX_CANCELATIONS);
|
||||
if (osxCancelations >= DEFAULT_MAX_OSX_CANCELATIONS) {
|
||||
cleanupActiveUpdate();
|
||||
} else {
|
||||
writeStatusFile(getUpdatesDir(),
|
||||
update.state = STATE_PENDING_ELEVATE);
|
||||
}
|
||||
update.statusText = gUpdateBundle.GetStringFromName("elevationFailure");
|
||||
let oldType = update.selectedPatch ? update.selectedPatch.type
|
||||
: "complete";
|
||||
update.QueryInterface(Ci.nsIWritablePropertyBag);
|
||||
update.setProperty("patchingFailed", oldType);
|
||||
let prompter = Cc["@mozilla.org/updates/update-prompt;1"].
|
||||
createInstance(Ci.nsIUpdatePrompt);
|
||||
prompter.showUpdateError(update);
|
||||
} else {
|
||||
writeStatusFile(getUpdatesDir(), update.state = STATE_PENDING);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CANCELATIONS)) {
|
||||
Services.prefs.clearUserPref(PREF_APP_UPDATE_CANCELATIONS);
|
||||
}
|
||||
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CANCELATIONS_OSX)) {
|
||||
Services.prefs.clearUserPref(PREF_APP_UPDATE_CANCELATIONS_OSX);
|
||||
}
|
||||
|
||||
// Replace with Array.prototype.includes when it has stabilized.
|
||||
if (SERVICE_ERRORS.indexOf(update.errorCode) != -1) {
|
||||
|
@ -1320,7 +1418,7 @@ function pingStateAndStatusCodes(aUpdate, aStartup, aStatus) {
|
|||
case STATE_PENDING:
|
||||
stateCode = 4;
|
||||
break;
|
||||
case STATE_PENDING_SVC:
|
||||
case STATE_PENDING_SERVICE:
|
||||
stateCode = 5;
|
||||
break;
|
||||
case STATE_APPLYING:
|
||||
|
@ -1332,7 +1430,7 @@ function pingStateAndStatusCodes(aUpdate, aStartup, aStatus) {
|
|||
case STATE_APPLIED_OS:
|
||||
stateCode = 8;
|
||||
break;
|
||||
case STATE_APPLIED_SVC:
|
||||
case STATE_APPLIED_SERVICE:
|
||||
stateCode = 9;
|
||||
break;
|
||||
case STATE_SUCCEEDED:
|
||||
|
@ -1344,6 +1442,9 @@ function pingStateAndStatusCodes(aUpdate, aStartup, aStatus) {
|
|||
case STATE_FAILED:
|
||||
stateCode = 12;
|
||||
break;
|
||||
case STATE_PENDING_ELEVATE:
|
||||
stateCode = 13;
|
||||
break;
|
||||
default:
|
||||
stateCode = 1;
|
||||
}
|
||||
|
@ -2018,7 +2119,8 @@ UpdateService.prototype = {
|
|||
// If it's "applying", we know that we've already been here once, so
|
||||
// we really want to start from a clean state.
|
||||
if (update &&
|
||||
(update.state == STATE_PENDING || update.state == STATE_PENDING_SVC)) {
|
||||
(update.state == STATE_PENDING ||
|
||||
update.state == STATE_PENDING_SERVICE)) {
|
||||
LOG("UpdateService:_postUpdateProcessing - patch found in applying " +
|
||||
"state for the first time");
|
||||
update.state = STATE_APPLYING;
|
||||
|
@ -2084,8 +2186,10 @@ UpdateService.prototype = {
|
|||
|
||||
// Done with this update. Clean it up.
|
||||
cleanupActiveUpdate();
|
||||
}
|
||||
else {
|
||||
} else if (status == STATE_PENDING_ELEVATE) {
|
||||
prompter.showUpdateElevationRequired();
|
||||
return;
|
||||
} else {
|
||||
// If we hit an error, then the error code will be included in the status
|
||||
// string following a colon and a space. If we had an I/O error, then we
|
||||
// assume that the patch is not invalid, and we re-stage the patch so that
|
||||
|
@ -2308,13 +2412,21 @@ UpdateService.prototype = {
|
|||
AUSTLMY.pingBoolPref("UPDATE_NOT_PREF_UPDATE_STAGING_ENABLED_" +
|
||||
this._pingSuffix,
|
||||
PREF_APP_UPDATE_STAGING_ENABLED, true, true);
|
||||
if (AppConstants.platform == "win") {
|
||||
if (AppConstants.platform == "win" || AppConstants.platform == "macosx") {
|
||||
// Histogram IDs:
|
||||
// UPDATE_PREF_UPDATE_CANCELATIONS_EXTERNAL
|
||||
// UPDATE_PREF_UPDATE_CANCELATIONS_NOTIFY
|
||||
AUSTLMY.pingIntPref("UPDATE_PREF_UPDATE_CANCELATIONS_" + this._pingSuffix,
|
||||
PREF_APP_UPDATE_CANCELATIONS, 0, 0);
|
||||
}
|
||||
if (AppConstants.platform == "macosx") {
|
||||
// Histogram IDs:
|
||||
// UPDATE_PREF_UPDATE_CANCELATIONS_OSX_EXTERNAL
|
||||
// UPDATE_PREF_UPDATE_CANCELATIONS_OSX_NOTIFY
|
||||
AUSTLMY.pingIntPref("UPDATE_PREF_UPDATE_CANCELATIONS_OSX_" +
|
||||
this._pingSuffix,
|
||||
PREF_APP_UPDATE_CANCELATIONS_OSX, 0, 0);
|
||||
}
|
||||
if (AppConstants.MOZ_MAINTENANCE_SERVICE) {
|
||||
// Histogram IDs:
|
||||
// UPDATE_NOT_PREF_UPDATE_SERVICE_ENABLED_EXTERNAL
|
||||
|
@ -2353,7 +2465,8 @@ UpdateService.prototype = {
|
|||
|
||||
if (this._downloader && this._downloader.patchIsStaged) {
|
||||
let readState = readStatusFile(getUpdatesDir());
|
||||
if (readState == STATE_PENDING || readState == STATE_PENDING_SVC) {
|
||||
if (readState == STATE_PENDING || readState == STATE_PENDING_SVC ||
|
||||
readState == STATE_PENDING_ELEVATE) {
|
||||
AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_IS_DOWNLOADED);
|
||||
} else {
|
||||
AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_IS_STAGED);
|
||||
|
@ -2470,8 +2583,66 @@ UpdateService.prototype = {
|
|||
}
|
||||
});
|
||||
|
||||
var update = minorUpdate || majorUpdate;
|
||||
if (!update) {
|
||||
let update = minorUpdate || majorUpdate;
|
||||
if (AppConstants.platform == "macosx" && update) {
|
||||
if (getElevationRequired()) {
|
||||
let installAttemptVersion = getPref("getCharPref",
|
||||
PREF_APP_UPDATE_ELEVATE_VERSION,
|
||||
null);
|
||||
if (vc.compare(installAttemptVersion, update.appVersion) != 0) {
|
||||
Services.prefs.setCharPref(PREF_APP_UPDATE_ELEVATE_VERSION,
|
||||
update.appVersion);
|
||||
if (Services.prefs.prefHasUserValue(
|
||||
PREF_APP_UPDATE_CANCELATIONS_OSX)) {
|
||||
Services.prefs.clearUserPref(PREF_APP_UPDATE_CANCELATIONS_OSX);
|
||||
}
|
||||
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_NEVER)) {
|
||||
Services.prefs.clearUserPref(PREF_APP_UPDATE_ELEVATE_NEVER);
|
||||
}
|
||||
} else {
|
||||
let numCancels = getPref("getIntPref",
|
||||
PREF_APP_UPDATE_CANCELATIONS_OSX,
|
||||
0);
|
||||
let rejectedVersion = getPref("getCharPref",
|
||||
PREF_APP_UPDATE_ELEVATE_NEVER, "");
|
||||
let maxCancels = getPref("getIntPref",
|
||||
PREF_APP_UPDATE_MAX_OSX_CANCELATIONS,
|
||||
DEFAULT_MAX_OSX_CANCELATIONS);
|
||||
if (numCancels >= maxCancels) {
|
||||
LOG("UpdateService:selectUpdate - the user requires elevation to " +
|
||||
"install this update, but the user has exceeded the max " +
|
||||
"number of elevation attempts.");
|
||||
update.elevationFailure = true;
|
||||
AUSTLMY.pingCheckCode(
|
||||
this._pingSuffix,
|
||||
AUSTLMY.CHK_ELEVATION_DISABLED_FOR_VERSION);
|
||||
} else if (vc.compare(rejectedVersion, update.appVersion) == 0) {
|
||||
LOG("UpdateService:selectUpdate - the user requires elevation to " +
|
||||
"install this update, but elevation is disabled for this " +
|
||||
"version.");
|
||||
update.elevationFailure = true;
|
||||
AUSTLMY.pingCheckCode(this._pingSuffix,
|
||||
AUSTLMY.CHK_ELEVATION_OPTOUT_FOR_VERSION);
|
||||
} else {
|
||||
LOG("UpdateService:selectUpdate - the user requires elevation to " +
|
||||
"install the update.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Clear elevation-related prefs since they no longer apply (the user
|
||||
// may have gained write access to the Firefox directory or an update
|
||||
// was executed with a different profile).
|
||||
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_VERSION)) {
|
||||
Services.prefs.clearUserPref(PREF_APP_UPDATE_ELEVATE_VERSION);
|
||||
}
|
||||
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CANCELATIONS_OSX)) {
|
||||
Services.prefs.clearUserPref(PREF_APP_UPDATE_CANCELATIONS_OSX);
|
||||
}
|
||||
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_NEVER)) {
|
||||
Services.prefs.clearUserPref(PREF_APP_UPDATE_ELEVATE_NEVER);
|
||||
}
|
||||
}
|
||||
} else if (!update) {
|
||||
AUSTLMY.pingCheckCode(this._pingSuffix, lastCheckCode);
|
||||
}
|
||||
|
||||
|
@ -2508,7 +2679,7 @@ UpdateService.prototype = {
|
|||
}
|
||||
|
||||
var update = this.selectUpdate(updates, updates.length);
|
||||
if (!update) {
|
||||
if (!update || update.elevationFailure) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2599,6 +2770,13 @@ UpdateService.prototype = {
|
|||
return gCanCheckForUpdates && hasUpdateMutex();
|
||||
},
|
||||
|
||||
/**
|
||||
* See nsIUpdateService.idl
|
||||
*/
|
||||
get elevationRequired() {
|
||||
return getElevationRequired();
|
||||
},
|
||||
|
||||
/**
|
||||
* See nsIUpdateService.idl
|
||||
*/
|
||||
|
@ -3045,8 +3223,9 @@ UpdateManager.prototype = {
|
|||
for (let i = updates.length - 1; i >= 0; --i) {
|
||||
let state = updates[i].state;
|
||||
if (state == STATE_NONE || state == STATE_DOWNLOADING ||
|
||||
state == STATE_APPLIED || state == STATE_APPLIED_SVC ||
|
||||
state == STATE_PENDING || state == STATE_PENDING_SVC) {
|
||||
state == STATE_APPLIED || state == STATE_APPLIED_SERVICE ||
|
||||
state == STATE_PENDING || state == STATE_PENDING_SERVICE ||
|
||||
state == STATE_PENDING_ELEVATE) {
|
||||
updates.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
@ -3077,13 +3256,15 @@ UpdateManager.prototype = {
|
|||
}
|
||||
}
|
||||
if (update.state == STATE_APPLIED && shouldUseService()) {
|
||||
writeStatusFile(getUpdatesDir(), update.state = STATE_APPLIED_SVC);
|
||||
writeStatusFile(getUpdatesDir(), update.state = STATE_APPLIED_SERVICE);
|
||||
}
|
||||
var um = Cc["@mozilla.org/updates/update-manager;1"].
|
||||
getService(Ci.nsIUpdateManager);
|
||||
um.saveUpdates();
|
||||
|
||||
if (update.state != STATE_PENDING && update.state != STATE_PENDING_SVC) {
|
||||
if (update.state != STATE_PENDING &&
|
||||
update.state != STATE_PENDING_SERVICE &&
|
||||
update.state != STATE_PENDING_ELEVATE) {
|
||||
// Destroy the updates directory, since we're done with it.
|
||||
// Make sure to not do this when the updater has fallen back to
|
||||
// non-staged updates.
|
||||
|
@ -3118,16 +3299,52 @@ UpdateManager.prototype = {
|
|||
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).
|
||||
if (update.state == STATE_APPLIED ||
|
||||
update.state == STATE_APPLIED_SERVICE ||
|
||||
update.state == STATE_PENDING ||
|
||||
update.state == STATE_PENDING_SERVICE ||
|
||||
update.state == STATE_PENDING_ELEVATE) {
|
||||
// Notify the user that an update has been staged and is ready for
|
||||
// installation (i.e. that they should restart the application).
|
||||
let prompter = Cc["@mozilla.org/updates/update-prompt;1"].
|
||||
createInstance(Ci.nsIUpdatePrompt);
|
||||
prompter.showUpdateDownloaded(update, true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* See nsIUpdateService.idl
|
||||
*/
|
||||
elevationOptedIn: function UM_elevationOptedIn() {
|
||||
// The user has been been made aware that the update requires elevation.
|
||||
let update = this._activeUpdate;
|
||||
if (!update) {
|
||||
return;
|
||||
}
|
||||
let status = readStatusFile(getUpdatesDir());
|
||||
let parts = status.split(":");
|
||||
update.state = parts[0];
|
||||
if (update.state == STATE_PENDING_ELEVATE) {
|
||||
// Proceed with the pending update.
|
||||
// Note: STATE_PENDING_ELEVATE stands for "pending user's approval to
|
||||
// proceed with an elevated update". As long as we see this state, we will
|
||||
// notify the user of the availability of an update that requires
|
||||
// elevation. |elevationOptedIn| (this function) is called when the user
|
||||
// gives us approval to proceed, so we want to switch to STATE_PENDING.
|
||||
// The updater then detects whether or not elevation is required and
|
||||
// displays the elevation prompt if necessary. This last step does not
|
||||
// depend on the state in the status file.
|
||||
writeStatusFile(getUpdatesDir(), STATE_PENDING);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* See nsIUpdateService.idl
|
||||
*/
|
||||
cleanupActiveUpdate: function UM_cleanupActiveUpdate() {
|
||||
cleanupActiveUpdate();
|
||||
},
|
||||
|
||||
classID: Components.ID("{093C2356-4843-4C65-8709-D7DBCBBE7DFB}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdateManager, Ci.nsIObserver])
|
||||
};
|
||||
|
@ -3473,8 +3690,9 @@ Downloader.prototype = {
|
|||
// Note that if we decide to download and apply new updates after another
|
||||
// update has been successfully applied in the background, we need to stop
|
||||
// checking for the APPLIED state here.
|
||||
return readState == STATE_PENDING || readState == STATE_PENDING_SVC ||
|
||||
readState == STATE_APPLIED || readState == STATE_APPLIED_SVC;
|
||||
return readState == STATE_PENDING || readState == STATE_PENDING_SERVICE ||
|
||||
readState == STATE_PENDING_ELEVATE ||
|
||||
readState == STATE_APPLIED || readState == STATE_APPLIED_SERVICE;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -3600,7 +3818,8 @@ Downloader.prototype = {
|
|||
LOG("Downloader:_selectPatch - already downloaded and staged");
|
||||
return null;
|
||||
}
|
||||
} else if (state == STATE_PENDING || state == STATE_PENDING_SVC) {
|
||||
} else if (state == STATE_PENDING || state == STATE_PENDING_SERVICE ||
|
||||
state == STATE_PENDING_ELEVATE) {
|
||||
LOG("Downloader:_selectPatch - already downloaded and staged");
|
||||
return null;
|
||||
}
|
||||
|
@ -3725,7 +3944,11 @@ Downloader.prototype = {
|
|||
if (patchFile.fileSize == this._patch.size) {
|
||||
LOG("Downloader:downloadUpdate - patchFile appears to be fully downloaded");
|
||||
// Bump the status along so that we don't try to redownload again.
|
||||
status = STATE_PENDING;
|
||||
if (getElevationRequired()) {
|
||||
status = STATE_PENDING_ELEVATE;
|
||||
} else {
|
||||
status = STATE_PENDING;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG("Downloader:downloadUpdate - patchFile " + patchFile.path +
|
||||
|
@ -3738,7 +3961,11 @@ Downloader.prototype = {
|
|||
// It looks like the patch was downloaded, but got interrupted while it
|
||||
// was being verified or applied. So we'll fake the downloading portion.
|
||||
|
||||
writeStatusFile(updateDir, STATE_PENDING);
|
||||
if (getElevationRequired()) {
|
||||
writeStatusFile(updateDir, STATE_PENDING_ELEVATE);
|
||||
} else {
|
||||
writeStatusFile(updateDir, STATE_PENDING);
|
||||
}
|
||||
|
||||
// Since the code expects the onStopRequest callback to happen
|
||||
// asynchronously (And you have to call AUS_addDownloadListener
|
||||
|
@ -3967,7 +4194,13 @@ Downloader.prototype = {
|
|||
"max fail: " + maxFail + ", " + "retryTimeout: " + retryTimeout);
|
||||
if (Components.isSuccessCode(status)) {
|
||||
if (this._verifyDownload()) {
|
||||
state = shouldUseService() ? STATE_PENDING_SVC : STATE_PENDING;
|
||||
if (shouldUseService()) {
|
||||
state = STATE_PENDING_SERVICE;
|
||||
} else if (getElevationRequired()) {
|
||||
state = STATE_PENDING_ELEVATE;
|
||||
} else {
|
||||
state = STATE_PENDING;
|
||||
}
|
||||
if (this.background) {
|
||||
shouldShowPrompt = !getCanStageUpdates();
|
||||
}
|
||||
|
@ -4130,7 +4363,8 @@ Downloader.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
if (state == STATE_PENDING || state == STATE_PENDING_SVC) {
|
||||
if (state == STATE_PENDING || state == STATE_PENDING_SERVICE ||
|
||||
state == STATE_PENDING_ELEVATE) {
|
||||
if (getCanStageUpdates()) {
|
||||
LOG("Downloader:onStopRequest - attempting to stage update: " +
|
||||
this._update.name);
|
||||
|
@ -4227,8 +4461,9 @@ UpdatePrompt.prototype = {
|
|||
*/
|
||||
showUpdateAvailable: function UP_showUpdateAvailable(update) {
|
||||
if (getPref("getBoolPref", PREF_APP_UPDATE_SILENT, false) ||
|
||||
this._getUpdateWindow() || this._getAltUpdateWindow())
|
||||
this._getUpdateWindow() || this._getAltUpdateWindow()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._showUnobtrusiveUI(null, URI_UPDATE_PROMPT_DIALOG, null,
|
||||
UPDATE_WINDOW_NAME, "updatesavailable", update);
|
||||
|
@ -4262,23 +4497,15 @@ UpdatePrompt.prototype = {
|
|||
showUpdateInstalled: function UP_showUpdateInstalled() {
|
||||
if (getPref("getBoolPref", PREF_APP_UPDATE_SILENT, false) ||
|
||||
!getPref("getBoolPref", PREF_APP_UPDATE_SHOW_INSTALLED_UI, false) ||
|
||||
this._getUpdateWindow())
|
||||
this._getUpdateWindow()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var page = "installed";
|
||||
var win = this._getUpdateWindow();
|
||||
if (win) {
|
||||
if (page && "setCurrentPage" in win)
|
||||
win.setCurrentPage(page);
|
||||
win.focus();
|
||||
}
|
||||
else {
|
||||
var openFeatures = "chrome,centerscreen,dialog=no,resizable=no,titlebar,toolbar=no";
|
||||
var arg = Cc["@mozilla.org/supports-string;1"].
|
||||
createInstance(Ci.nsISupportsString);
|
||||
arg.data = page;
|
||||
Services.ww.openWindow(null, URI_UPDATE_PROMPT_DIALOG, null, openFeatures, arg);
|
||||
}
|
||||
let openFeatures = "chrome,centerscreen,dialog=no,resizable=no,titlebar,toolbar=no";
|
||||
let arg = Cc["@mozilla.org/supports-string;1"].
|
||||
createInstance(Ci.nsISupportsString);
|
||||
arg.data = "installed";
|
||||
Services.ww.openWindow(null, URI_UPDATE_PROMPT_DIALOG, null, openFeatures, arg);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -4326,6 +4553,21 @@ UpdatePrompt.prototype = {
|
|||
"Update:History", null, null);
|
||||
},
|
||||
|
||||
/**
|
||||
* See nsIUpdateService.idl
|
||||
*/
|
||||
showUpdateElevationRequired: function UP_showUpdateElevationRequired() {
|
||||
if (getPref("getBoolPref", PREF_APP_UPDATE_SILENT, false) ||
|
||||
this._getAltUpdateWindow()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let um = Cc["@mozilla.org/updates/update-manager;1"].
|
||||
getService(Ci.nsIUpdateManager);
|
||||
this._showUI(null, URI_UPDATE_PROMPT_DIALOG, null,
|
||||
UPDATE_WINDOW_NAME, "finishedBackground", um.activeUpdate);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the update window if present.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>org.mozilla.updater</string>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -24,6 +24,9 @@ int InitProgressUI(int *argc, NS_tchar ***argv);
|
|||
// Called on the main thread at startup
|
||||
int ShowProgressUI(bool indeterminate = false, bool initUIStrings = true);
|
||||
int InitProgressUIStrings();
|
||||
#elif defined(XP_MACOSX)
|
||||
// Called on the main thread at startup
|
||||
int ShowProgressUI(bool indeterminate = false);
|
||||
#else
|
||||
// Called on the main thread at startup
|
||||
int ShowProgressUI();
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
#define TIMER_INTERVAL 0.2
|
||||
|
||||
static float sProgressVal; // between 0 and 100
|
||||
static BOOL sQuit = FALSE;
|
||||
static BOOL sQuit = NO;
|
||||
static BOOL sIndeterminate = NO;
|
||||
static StringTable sLabels;
|
||||
static const char *sUpdatePath;
|
||||
|
||||
|
@ -30,7 +31,7 @@ static const char *sUpdatePath;
|
|||
-(void)awakeFromNib
|
||||
{
|
||||
NSWindow *w = [progressBar window];
|
||||
|
||||
|
||||
[w setTitle:[NSString stringWithUTF8String:sLabels.title]];
|
||||
[progressTextField setStringValue:[NSString stringWithUTF8String:sLabels.info]];
|
||||
|
||||
|
@ -48,7 +49,7 @@ static const char *sUpdatePath;
|
|||
|
||||
[w center];
|
||||
|
||||
[progressBar setIndeterminate:NO];
|
||||
[progressBar setIndeterminate:sIndeterminate];
|
||||
[progressBar setDoubleValue:0.0];
|
||||
|
||||
[[NSTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self
|
||||
|
@ -98,13 +99,13 @@ InitProgressUI(int *pargc, char ***pargv)
|
|||
}
|
||||
|
||||
int
|
||||
ShowProgressUI()
|
||||
ShowProgressUI(bool indeterminate)
|
||||
{
|
||||
// Only show the Progress UI if the process is taking a significant amount of
|
||||
// time where a significant amount of time is defined as .5 seconds after
|
||||
// ShowProgressUI is called sProgress is less than 70.
|
||||
usleep(500000);
|
||||
|
||||
|
||||
if (sQuit || sProgressVal > 70.0f)
|
||||
return 0;
|
||||
|
||||
|
@ -118,7 +119,8 @@ ShowProgressUI()
|
|||
if (!(strlen(sLabels.title) < MAX_TEXT_LEN - 1 &&
|
||||
strlen(sLabels.info) < MAX_TEXT_LEN - 1))
|
||||
return -1;
|
||||
|
||||
|
||||
sIndeterminate = indeterminate;
|
||||
[NSApplication sharedApplication];
|
||||
[NSBundle loadNibNamed:@"MainMenu" owner:NSApp];
|
||||
[NSApp run];
|
||||
|
@ -130,7 +132,7 @@ ShowProgressUI()
|
|||
void
|
||||
QuitProgressUI()
|
||||
{
|
||||
sQuit = TRUE;
|
||||
sQuit = YES;
|
||||
}
|
||||
|
||||
// Called on a background thread
|
||||
|
|
|
@ -53,6 +53,9 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include "updatelogging.h"
|
||||
#ifdef XP_MACOSX
|
||||
#include "updaterfileutils_osx.h"
|
||||
#endif // XP_MACOSX
|
||||
|
||||
#include "mozilla/Compiler.h"
|
||||
#include "mozilla/Types.h"
|
||||
|
@ -73,8 +76,19 @@
|
|||
|
||||
#if defined(XP_MACOSX)
|
||||
// These functions are defined in launchchild_osx.mm
|
||||
void LaunchChild(int argc, char **argv);
|
||||
void CleanupElevatedMacUpdate(bool aFailureOccurred);
|
||||
bool IsOwnedByGroupAdmin(const char* aAppBundle);
|
||||
bool IsRecursivelyWritable(const char* aPath);
|
||||
void LaunchChild(int argc, const char** argv);
|
||||
void LaunchMacPostProcess(const char* aAppBundle);
|
||||
bool ObtainUpdaterArguments(int* argc, char*** argv);
|
||||
bool ServeElevatedUpdate(int argc, const char** argv);
|
||||
void SetGroupOwnershipAndPermissions(const char* aAppBundle);
|
||||
struct UpdateServerThreadArgs
|
||||
{
|
||||
int argc;
|
||||
const NS_tchar** argv;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef _O_BINARY
|
||||
|
@ -1995,7 +2009,7 @@ LaunchCallbackApp(const NS_tchar *workingDir,
|
|||
#if defined(USE_EXECV)
|
||||
execv(argv[0], argv);
|
||||
#elif defined(XP_MACOSX)
|
||||
LaunchChild(argc, argv);
|
||||
LaunchChild(argc, (const char**)argv);
|
||||
#elif defined(XP_WIN)
|
||||
// Do not allow the callback to run when running an update through the
|
||||
// service as session 0. The unelevated updater.exe will do the launching.
|
||||
|
@ -2518,8 +2532,93 @@ UpdateThreadFunc(void *param)
|
|||
QuitProgressUI();
|
||||
}
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
static void
|
||||
ServeElevatedUpdateThreadFunc(void* param)
|
||||
{
|
||||
UpdateServerThreadArgs* threadArgs = (UpdateServerThreadArgs*)param;
|
||||
gSucceeded = ServeElevatedUpdate(threadArgs->argc, threadArgs->argv);
|
||||
if (!gSucceeded) {
|
||||
WriteStatusFile(ELEVATION_CANCELED);
|
||||
}
|
||||
QuitProgressUI();
|
||||
}
|
||||
|
||||
void freeArguments(int argc, char** argv)
|
||||
{
|
||||
for (int i = 0; i < argc; i++) {
|
||||
free(argv[i]);
|
||||
}
|
||||
free(argv);
|
||||
}
|
||||
#endif
|
||||
|
||||
int LaunchCallbackAndPostProcessApps(int argc, NS_tchar** argv,
|
||||
int callbackIndex
|
||||
#ifdef XP_WIN
|
||||
, const WCHAR* elevatedLockFilePath
|
||||
, HANDLE updateLockFileHandle
|
||||
#elif XP_MACOSX
|
||||
, bool isElevated
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (argc > callbackIndex) {
|
||||
#if defined(XP_WIN)
|
||||
if (gSucceeded) {
|
||||
if (!LaunchWinPostProcess(gInstallDirPath, gPatchDirPath)) {
|
||||
fprintf(stderr, "The post update process was not launched");
|
||||
}
|
||||
|
||||
// The service update will only be executed if it is already installed.
|
||||
// For first time installs of the service, the install will happen from
|
||||
// the PostUpdate process. We do the service update process here
|
||||
// because it's possible we are updating with updater.exe without the
|
||||
// service if the service failed to apply the update. We want to update
|
||||
// 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 (!sUsingService) {
|
||||
StartServiceUpdate(gInstallDirPath);
|
||||
}
|
||||
}
|
||||
EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 0);
|
||||
#elif XP_MACOSX
|
||||
if (!isElevated) {
|
||||
if (gSucceeded) {
|
||||
LaunchMacPostProcess(gInstallDirPath);
|
||||
}
|
||||
#endif
|
||||
|
||||
LaunchCallbackApp(argv[5],
|
||||
argc - callbackIndex,
|
||||
argv + callbackIndex,
|
||||
sUsingService);
|
||||
#ifdef XP_MACOSX
|
||||
} // if (!isElevated)
|
||||
#endif /* XP_MACOSX */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NS_main(int argc, NS_tchar **argv)
|
||||
{
|
||||
// The callback is the remaining arguments starting at callbackIndex.
|
||||
// The argument specified by callbackIndex is the callback executable and the
|
||||
// argument prior to callbackIndex is the working directory.
|
||||
const int callbackIndex = 6;
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
bool isElevated =
|
||||
strstr(argv[0], "/Library/PrivilegedHelperTools/org.mozilla.updater") != 0;
|
||||
if (isElevated) {
|
||||
if (!ObtainUpdaterArguments(&argc, &argv)) {
|
||||
// Won't actually get here because ObtainUpdaterArguments will terminate
|
||||
// the current process on failure.
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(MOZ_WIDGET_GONK)
|
||||
if (EnvHasValue("LD_PRELOAD")) {
|
||||
// If the updater is launched with LD_PRELOAD set, then we wind up
|
||||
|
@ -2552,7 +2651,13 @@ int NS_main(int argc, NS_tchar **argv)
|
|||
}
|
||||
#endif
|
||||
|
||||
InitProgressUI(&argc, &argv);
|
||||
#ifdef XP_MACOSX
|
||||
if (!isElevated) {
|
||||
#endif
|
||||
InitProgressUI(&argc, &argv);
|
||||
#ifdef XP_MACOSX
|
||||
}
|
||||
#endif
|
||||
|
||||
// To process an update the updater command line must at a minimum have the
|
||||
// directory path containing the updater.mar file to process as the first
|
||||
|
@ -2570,11 +2675,18 @@ int NS_main(int argc, NS_tchar **argv)
|
|||
// launched.
|
||||
if (argc < 4) {
|
||||
fprintf(stderr, "Usage: updater patch-dir install-dir apply-to-dir [wait-pid [callback-working-dir callback-path args...]]\n");
|
||||
#ifdef XP_MACOSX
|
||||
if (isElevated) {
|
||||
freeArguments(argc, argv);
|
||||
CleanupElevatedMacUpdate(true);
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
// The directory containing the update information.
|
||||
gPatchDirPath = argv[1];
|
||||
|
||||
// The directory we're going to update to.
|
||||
// We copy this string because we need to remove trailing slashes. The C++
|
||||
// standard says that it's always safe to write to strings pointed to by argv
|
||||
|
@ -2655,6 +2767,27 @@ int NS_main(int argc, NS_tchar **argv)
|
|||
*slash = NS_T('\0');
|
||||
}
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
if (!isElevated && !IsRecursivelyWritable(argv[2])) {
|
||||
// If the app directory isn't recursively writeable, an elevated update is
|
||||
// required.
|
||||
UpdateServerThreadArgs threadArgs;
|
||||
threadArgs.argc = argc;
|
||||
threadArgs.argv = const_cast<const NS_tchar**>(argv);
|
||||
|
||||
Thread t1;
|
||||
if (t1.Run(ServeElevatedUpdateThreadFunc, &threadArgs) == 0) {
|
||||
// Show an indeterminate progress bar while an elevated update is in
|
||||
// progress.
|
||||
ShowProgressUI(true);
|
||||
}
|
||||
t1.Join();
|
||||
|
||||
LaunchCallbackAndPostProcessApps(argc, argv, callbackIndex, false);
|
||||
return gSucceeded ? 0 : 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (EnvHasValue("MOZ_OS_UPDATE")) {
|
||||
sIsOSUpdate = true;
|
||||
putenv(const_cast<char*>("MOZ_OS_UPDATE="));
|
||||
|
@ -2683,6 +2816,12 @@ int NS_main(int argc, NS_tchar **argv)
|
|||
|
||||
if (!WriteStatusFile("applying")) {
|
||||
LOG(("failed setting status to 'applying'"));
|
||||
#ifdef XP_MACOSX
|
||||
if (isElevated) {
|
||||
freeArguments(argc, argv);
|
||||
CleanupElevatedMacUpdate(true);
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -2778,11 +2917,6 @@ int NS_main(int argc, NS_tchar **argv)
|
|||
#endif
|
||||
}
|
||||
|
||||
// The callback is the remaining arguments starting at callbackIndex.
|
||||
// The argument specified by callbackIndex is the callback executable and the
|
||||
// argument prior to callbackIndex is the working directory.
|
||||
const int callbackIndex = 6;
|
||||
|
||||
#if defined(XP_WIN)
|
||||
#ifdef MOZ_MAINTENANCE_SERVICE
|
||||
sUsingService = EnvHasValue("MOZ_USING_SERVICE");
|
||||
|
@ -3118,10 +3252,22 @@ int NS_main(int argc, NS_tchar **argv)
|
|||
// Try changing the current directory again
|
||||
if (NS_tchdir(gWorkingDirPath) != 0) {
|
||||
// OK, time to give up!
|
||||
#ifdef XP_MACOSX
|
||||
if (isElevated) {
|
||||
freeArguments(argc, argv);
|
||||
CleanupElevatedMacUpdate(true);
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
// Failed to create the directory, bail out
|
||||
#ifdef XP_MACOSX
|
||||
if (isElevated) {
|
||||
freeArguments(argc, argv);
|
||||
CleanupElevatedMacUpdate(true);
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -3334,12 +3480,17 @@ int NS_main(int argc, NS_tchar **argv)
|
|||
}
|
||||
#endif /* XP_WIN */
|
||||
|
||||
// Run update process on a background thread. ShowProgressUI may return
|
||||
// Run update process on a background thread. ShowProgressUI may return
|
||||
// before QuitProgressUI has been called, so wait for UpdateThreadFunc to
|
||||
// terminate. Avoid showing the progress UI when staging an update.
|
||||
// terminate. Avoid showing the progress UI when staging an update, or if this
|
||||
// is an elevated process on OSX.
|
||||
Thread t;
|
||||
if (t.Run(UpdateThreadFunc, nullptr) == 0) {
|
||||
if (!sStagedUpdate && !sReplaceRequest) {
|
||||
if (!sStagedUpdate && !sReplaceRequest
|
||||
#ifdef XP_MACOSX
|
||||
&& !isElevated
|
||||
#endif
|
||||
) {
|
||||
ShowProgressUI();
|
||||
}
|
||||
}
|
||||
|
@ -3418,43 +3569,32 @@ int NS_main(int argc, NS_tchar **argv)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isElevated) {
|
||||
SetGroupOwnershipAndPermissions(gInstallDirPath);
|
||||
freeArguments(argc, argv);
|
||||
CleanupElevatedMacUpdate(false);
|
||||
} else if (IsOwnedByGroupAdmin(gInstallDirPath)) {
|
||||
// If the group ownership of the Firefox .app bundle was set to the "admin"
|
||||
// group during a previous elevated update, we need to ensure that all files
|
||||
// in the bundle have group ownership of "admin" as well as write permission
|
||||
// for the group to not break updates in the future.
|
||||
SetGroupOwnershipAndPermissions(gInstallDirPath);
|
||||
}
|
||||
#endif /* XP_MACOSX */
|
||||
|
||||
LogFinish();
|
||||
|
||||
if (argc > callbackIndex) {
|
||||
#if defined(XP_WIN)
|
||||
if (gSucceeded) {
|
||||
if (!LaunchWinPostProcess(gInstallDirPath, gPatchDirPath)) {
|
||||
fprintf(stderr, "The post update process was not launched");
|
||||
}
|
||||
int retVal = LaunchCallbackAndPostProcessApps(argc, argv, callbackIndex
|
||||
#ifdef XP_WIN
|
||||
, elevatedLockFilePath
|
||||
, updateLockFileHandle
|
||||
#elif XP_MACOSX
|
||||
, isElevated
|
||||
#endif
|
||||
);
|
||||
|
||||
// The service update will only be executed if it is already installed.
|
||||
// For first time installs of the service, the install will happen from
|
||||
// the PostUpdate process. We do the service update process here
|
||||
// because it's possible we are updating with updater.exe without the
|
||||
// service if the service failed to apply the update. We want to update
|
||||
// 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 (!sUsingService) {
|
||||
StartServiceUpdate(gInstallDirPath);
|
||||
}
|
||||
}
|
||||
EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 0);
|
||||
#endif /* XP_WIN */
|
||||
#ifdef XP_MACOSX
|
||||
if (gSucceeded) {
|
||||
LaunchMacPostProcess(gInstallDirPath);
|
||||
}
|
||||
#endif /* XP_MACOSX */
|
||||
|
||||
LaunchCallbackApp(argv[5],
|
||||
argc - callbackIndex,
|
||||
argv + callbackIndex,
|
||||
sUsingService);
|
||||
}
|
||||
|
||||
return gSucceeded ? 0 : 1;
|
||||
return retVal ? retVal : (gSucceeded ? 0 : 1);
|
||||
}
|
||||
|
||||
class ActionList
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "nsILocalFileMac.h"
|
||||
#include "nsCommandLineServiceMac.h"
|
||||
#include "MacLaunchHelper.h"
|
||||
#include "updaterfileutils_osx.h"
|
||||
#endif
|
||||
|
||||
#if defined(XP_WIN)
|
||||
|
@ -83,6 +84,8 @@ GetUpdateLog()
|
|||
|
||||
#ifdef XP_WIN
|
||||
#define UPDATER_BIN "updater.exe"
|
||||
#elif XP_MACOSX
|
||||
#define UPDATER_BIN "org.mozilla.updater"
|
||||
#else
|
||||
#define UPDATER_BIN "updater"
|
||||
#endif
|
||||
|
@ -251,8 +254,9 @@ typedef enum {
|
|||
eNoUpdateAction,
|
||||
ePendingUpdate,
|
||||
ePendingService,
|
||||
ePendingElevate,
|
||||
eAppliedUpdate,
|
||||
eAppliedService
|
||||
eAppliedService,
|
||||
} UpdateStatus;
|
||||
|
||||
/**
|
||||
|
@ -271,8 +275,12 @@ GetUpdateStatus(nsIFile* dir, nsCOMPtr<nsIFile> &statusFile)
|
|||
if (GetStatusFileContents(statusFile, buf)) {
|
||||
const char kPending[] = "pending";
|
||||
const char kPendingService[] = "pending-service";
|
||||
const char kPendingElevate[] = "pending-elevate";
|
||||
const char kApplied[] = "applied";
|
||||
const char kAppliedService[] = "applied-service";
|
||||
if (!strncmp(buf, kPendingElevate, sizeof(kPendingElevate) - 1)) {
|
||||
return ePendingElevate;
|
||||
}
|
||||
if (!strncmp(buf, kPendingService, sizeof(kPendingService) - 1)) {
|
||||
return ePendingService;
|
||||
}
|
||||
|
@ -952,13 +960,21 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile,
|
|||
_exit(0);
|
||||
}
|
||||
#elif defined(XP_MACOSX)
|
||||
CommandLineServiceMac::SetupMacCommandLine(argc, argv, true);
|
||||
// LaunchChildMac uses posix_spawnp and prefers the current
|
||||
// architecture when launching. It doesn't require a
|
||||
// null-terminated string but it doesn't matter if we pass one.
|
||||
LaunchChildMac(argc, argv, 0, outpid);
|
||||
if (restart) {
|
||||
CommandLineServiceMac::SetupMacCommandLine(argc, argv, restart);
|
||||
// We need to detect whether elevation is required for this update. This can
|
||||
// occur when an admin user installs the application, but another admin
|
||||
// user attempts to update (see bug 394984).
|
||||
if (restart && !IsRecursivelyWritable(installDirPath.get())) {
|
||||
if (!LaunchElevatedUpdate(argc, argv, 0, outpid)) {
|
||||
LOG(("Failed to launch elevated update!"));
|
||||
exit(1);
|
||||
}
|
||||
exit(0);
|
||||
} else {
|
||||
LaunchChildMac(argc, argv, 0, outpid);
|
||||
if (restart) {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
#else
|
||||
*outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr);
|
||||
|
@ -1023,6 +1039,19 @@ ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir,
|
|||
nsCOMPtr<nsIFile> statusFile;
|
||||
UpdateStatus status = GetUpdateStatus(updatesDir, statusFile);
|
||||
switch (status) {
|
||||
case ePendingElevate: {
|
||||
if (NS_IsMainThread()) {
|
||||
// Only do this if we're called from the main thread.
|
||||
nsCOMPtr<nsIUpdatePrompt> up =
|
||||
do_GetService("@mozilla.org/updates/update-prompt;1");
|
||||
if (up) {
|
||||
up->ShowUpdateElevationRequired();
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Intentional fallthrough to ePendingUpdate and ePendingService.
|
||||
MOZ_FALLTHROUGH;
|
||||
}
|
||||
case ePendingUpdate:
|
||||
case ePendingService: {
|
||||
nsCOMPtr<nsIFile> versionFile;
|
||||
|
|
Загрузка…
Ссылка в новой задаче