зеркало из https://github.com/mozilla/pjs.git
Many update improvements: making background update work properly. User nagging after user opts to do something later. Rework timer manager so it's less retarded. etc.
This commit is contained in:
Родитель
c63fc078e3
Коммит
54f6e93d4a
|
@ -47,7 +47,7 @@ static const nsXREAppData kAppData = {
|
|||
sizeof(nsXREAppData),
|
||||
nsnull,
|
||||
"Mozilla",
|
||||
"Firefox",
|
||||
"Firefox Debug",
|
||||
NS_STRINGIFY(APP_VERSION),
|
||||
NS_STRINGIFY(BUILD_ID),
|
||||
"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
|
||||
|
|
|
@ -42,6 +42,8 @@
|
|||
# SYNTAX HINTS: dashes are delimiters. Use underscores instead.
|
||||
# The first character after a period must be alphabetic.
|
||||
|
||||
pref("app.update.logEnabled", true);
|
||||
|
||||
// pref("startup.homepage_override_url","chrome://browser-region/locale/region.properties");
|
||||
pref("general.startup.browser", true);
|
||||
|
||||
|
@ -80,15 +82,27 @@ pref("app.update.autoInstallMode", 0);
|
|||
|
||||
// XXX these prefs and others like them are distribution specific and should move
|
||||
// into chrome://browser
|
||||
// Default service URL for testing.
|
||||
pref("app.update.url", "chrome://mozapps/locale/update/updates.properties");
|
||||
// URL user can browse to manually if for some reason all update installation
|
||||
// attempts fail.
|
||||
pref("app.update.url.manual", "chrome://mozapps/locale/update/updates.properties");
|
||||
// User-settable update preference that overrides app.update.url for testing
|
||||
// purposes.
|
||||
pref("app.update.url.override", "chrome://mozapps/locale/update/updates.properties");
|
||||
pref("app.update.updatesAvailable", false);
|
||||
// Check for updates to Firefox every day
|
||||
pref("app.update.interval", 86400000);
|
||||
|
||||
// Interval: Time between checks for a new version (in seconds)
|
||||
// default=1 day
|
||||
pref("app.update.interval", 86400);
|
||||
// Interval: Time before prompting the user to download a new version that
|
||||
// is available (in seconds) default=1 day
|
||||
pref("app.update.nagTimer.download", 86400);
|
||||
// Interval: Time before prompting the user to restart to install the latest
|
||||
// download (in seconds) default=30 minutes
|
||||
pref("app.update.nagTimer.restart", 1800);
|
||||
// Interval: When all registered timers should be checked (in milliseconds)
|
||||
// default=5 seconds
|
||||
pref("app.update.timer", 5000);
|
||||
// UTC offset when last App update was performed.
|
||||
pref("app.update.lastUpdateDate", 0);
|
||||
|
||||
// Symmetric (can be overridden by individual extensions) update preferences.
|
||||
// e.g.
|
||||
|
|
|
@ -6,6 +6,7 @@ introType_major=A new version of %S is available:
|
|||
verificationError=%S could not confirm the integrity of the update package.
|
||||
errorsPageHeader=Update Failed
|
||||
IAgreeLabel=I Agree
|
||||
IDoNotAgreeLabel=I Disagree
|
||||
license404Error=The license file could not be found. Please contact the distributor.
|
||||
downloadingLicense=Downloading license text...
|
||||
statusSucceededFormat=Installed on: %S
|
||||
|
|
|
@ -40,7 +40,9 @@ const nsIIncrementalDownload = Components.interfaces.nsIIncrementalDownload;
|
|||
|
||||
const XMLNS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
|
||||
const PREF_UPDATE_MANUAL_URL = "app.update.url.manual";
|
||||
const PREF_UPDATE_MANUAL_URL = "app.update.url.manual";
|
||||
const PREF_UPDATE_NAGTIMER_DL = "app.update.nagTimer.download";
|
||||
const PREF_UPDATE_NAGTIMER_RESTART = "app.update.nagTimer.restart";
|
||||
|
||||
const URI_UPDATES_PROPERTIES = "chrome://mozapps/locale/update/updates.properties";
|
||||
|
||||
|
@ -53,6 +55,8 @@ const STATE_FAILED = "failed";
|
|||
const SRCEVT_FOREGROUND = 1;
|
||||
const SRCEVT_BACKGROUND = 2;
|
||||
|
||||
var gPref = null;
|
||||
|
||||
/**
|
||||
* Logs a string to the error console.
|
||||
* @param string
|
||||
|
@ -62,6 +66,28 @@ function LOG(string) {
|
|||
dump("*** " + string + "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a preference value, handling the case where there is no default.
|
||||
* @param func
|
||||
* The name of the preference function to call, on nsIPrefBranch
|
||||
* @param preference
|
||||
* The name of the preference
|
||||
* @param defaultValue
|
||||
* The default value to return in the event the preference has
|
||||
* no setting
|
||||
* @returns The value of the preference, or undefined if there was no
|
||||
* user or default value.
|
||||
*/
|
||||
function getPref(func, preference, defaultValue) {
|
||||
try {
|
||||
return gPref[func](preference);
|
||||
}
|
||||
catch (e) {
|
||||
LOG("FAIL = " + e);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
var gUpdates = {
|
||||
update : null,
|
||||
strings : null,
|
||||
|
@ -111,6 +137,9 @@ var gUpdates = {
|
|||
* Called when the wizard UI is loaded.
|
||||
*/
|
||||
onLoad: function() {
|
||||
gPref = Components.classes["@mozilla.org/preferences-service;1"]
|
||||
.getService(Components.interfaces.nsIPrefBranch2);
|
||||
|
||||
this.strings = document.getElementById("updateStrings");
|
||||
var brandStrings = document.getElementById("brandStrings");
|
||||
this.brandName = brandStrings.getString("brandShortName");
|
||||
|
@ -122,8 +151,16 @@ var gUpdates = {
|
|||
this._pages[page.pageid] = eval(page.getAttribute("object"));
|
||||
}
|
||||
|
||||
var de = document.documentElement;
|
||||
|
||||
// Cache the standard button labels in case we need to restore them
|
||||
this.buttonLabel_back = de.getButton("back").label;
|
||||
this.buttonLabel_next = de.getButton("next").label;
|
||||
this.buttonLabel_finish = de.getButton("finish").label;
|
||||
this.buttonLabel_cancel = de.getButton("cancel").label;
|
||||
|
||||
// Advance to the Start page.
|
||||
document.documentElement.currentPage = this.startPage;
|
||||
de.currentPage = this.startPage;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -145,7 +182,8 @@ var gUpdates = {
|
|||
// their permission to install, and it's ready for download.
|
||||
this.update = arg0;
|
||||
this.sourceEvent = SRCEVT_BACKGROUND;
|
||||
if (this.update.selectedPatch.state == STATE_PENDING)
|
||||
if (this.update.selectedPatch &&
|
||||
this.update.selectedPatch.state == STATE_PENDING)
|
||||
return document.getElementById("finishedBackground");
|
||||
return document.getElementById("updatesfound");
|
||||
}
|
||||
|
@ -185,7 +223,69 @@ var gUpdates = {
|
|||
errorPage.setAttribute("label", pageTitle);
|
||||
document.documentElement.currentPage = document.getElementById("errors");
|
||||
document.documentElement.setAttribute("label", pageTitle);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Registers a timer to nag the user about something relating to update
|
||||
* @param timerID
|
||||
* The ID of the timer to register, used for persistence
|
||||
* @param timerInterval
|
||||
* The interval of the timer
|
||||
* @param methodName
|
||||
* The method to call on the Update Prompter when the timer fires
|
||||
*/
|
||||
registerNagTimer: function(timerID, timerInterval, methodName) {
|
||||
// Remind the user to restart their browser in a little bit.
|
||||
var tm =
|
||||
Components.classes["@mozilla.org/updates/timer-manager;1"].
|
||||
getService(Components.interfaces.nsIUpdateTimerManager);
|
||||
|
||||
/**
|
||||
* An object implementing nsITimerCallback that uses the Update Prompt
|
||||
* component to notify the user about some event relating to app update
|
||||
* that they should take action on.
|
||||
* @param update
|
||||
* The nsIUpdate object in question
|
||||
* @param methodName
|
||||
* The name of the method on the Update Prompter that should be
|
||||
* called
|
||||
* @constructor
|
||||
*/
|
||||
function Callback(update, methodName) {
|
||||
this._update = update;
|
||||
this._methodName = methodName;
|
||||
this._prompter =
|
||||
Components.classes["@mozilla.org/updates/update-prompt;1"].
|
||||
createInstance(Components.interfaces.nsIUpdatePrompt);
|
||||
}
|
||||
Callback.prototype = {
|
||||
/**
|
||||
* The Update we should nag about downloading
|
||||
*/
|
||||
_update: null,
|
||||
|
||||
/**
|
||||
* The Update prompter we can use to notify the user
|
||||
*/
|
||||
_prompter: null,
|
||||
|
||||
/**
|
||||
* The method on the update prompt that should be called
|
||||
*/
|
||||
_methodName: "",
|
||||
|
||||
/**
|
||||
* Called when the timer fires. Notifies the user about whichever event
|
||||
* they need to be nagged about (e.g. update available, please restart,
|
||||
* etc).
|
||||
*/
|
||||
notify: function(timerCallback) {
|
||||
this._prompter[methodName](this._update);
|
||||
}
|
||||
}
|
||||
tm.registerTimer(timerID, (new Callback(gUpdates.update, methodName)),
|
||||
timerInterval);
|
||||
},
|
||||
}
|
||||
|
||||
var gCheckingPage = {
|
||||
|
@ -202,10 +302,10 @@ var gCheckingPage = {
|
|||
var wiz = document.documentElement;
|
||||
wiz.getButton("next").disabled = true;
|
||||
|
||||
var aus =
|
||||
Components.classes["@mozilla.org/updates/update-service;1"].
|
||||
getService(Components.interfaces.nsIApplicationUpdateService);
|
||||
this._checker = aus.checkForUpdates(this.updateListener);
|
||||
this._checker =
|
||||
Components.classes["@mozilla.org/updates/update-checker;1"].
|
||||
createInstance(Components.interfaces.nsIUpdateChecker);
|
||||
this._checker.checkForUpdates(this.updateListener);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -213,8 +313,10 @@ var gCheckingPage = {
|
|||
* Manager control, so stop checking for updates.
|
||||
*/
|
||||
onWizardCancel: function() {
|
||||
if (this._checker)
|
||||
this._checker.stopChecking();
|
||||
if (this._checker) {
|
||||
const nsIUpdateChecker = Components.interfaces.nsIUpdateChecker;
|
||||
this._checker.stopChecking(nsIUpdateChecker.CURRENT_CHECK);
|
||||
}
|
||||
},
|
||||
|
||||
updateListener: {
|
||||
|
@ -271,8 +373,14 @@ var gNoUpdatesPage = {
|
|||
};
|
||||
|
||||
var gUpdatesAvailablePage = {
|
||||
/**
|
||||
* An array of installed addons incompatible with this update.
|
||||
*/
|
||||
_incompatibleItems: null,
|
||||
|
||||
/**
|
||||
* Initialize.
|
||||
*/
|
||||
onPageShow: function() {
|
||||
var updateName = gUpdates.strings.getFormattedString("updateName",
|
||||
[gUpdates.brandName, gUpdates.update.version]);
|
||||
|
@ -307,15 +415,38 @@ var gUpdatesAvailablePage = {
|
|||
downloadNow.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* User said they wanted to install now, so advance to the License or
|
||||
* Downloading page, whichever is appropriate.
|
||||
*/
|
||||
onInstallNow: function() {
|
||||
var nextPageID = gUpdates.update.licenseURL ? "license" : "downloading";
|
||||
document.documentElement.currentPage = document.getElementById(nextPageID);
|
||||
},
|
||||
|
||||
/**
|
||||
* User said that they would install later. Register a timer to remind them
|
||||
* after a day or so.
|
||||
*/
|
||||
onInstallLater: function() {
|
||||
var interval = getPref("getIntPref", PREF_UPDATE_NAGTIMER_DL, 86400000);
|
||||
gUpdates.registerNagTimer("download-nag-timer", interval,
|
||||
"showUpdateAvailable");
|
||||
|
||||
// The user said "Later", so stop all update checks for this session
|
||||
// so that we don't bother them again.
|
||||
const nsIUpdateChecker = Components.interfaces.nsIUpdateChecker;
|
||||
var aus =
|
||||
Components.classes["@mozilla.org/updates/update-service;1"].
|
||||
getService(Components.interfaces.nsIApplicationUpdateService);
|
||||
aus.backgroundChecker.stopChecking(nsIUpdateChecker.CURRENT_SESSION);
|
||||
|
||||
close();
|
||||
},
|
||||
|
||||
/**
|
||||
* Show a list of extensions made incompatible by this update.
|
||||
*/
|
||||
showIncompatibleItems: function() {
|
||||
openDialog("chrome://mozapps/content/update/incompatible.xul", "",
|
||||
"dialog,centerscreen,modal,resizable,titlebar", this._incompatibleItems);
|
||||
|
@ -327,11 +458,15 @@ var gLicensePage = {
|
|||
onPageShow: function() {
|
||||
this._licenseContent = document.getElementById("licenseContent");
|
||||
|
||||
var nextButton = document.documentElement.getButton("next");
|
||||
var de = document.documentElement;
|
||||
var nextButton = de.getButton("next");
|
||||
nextButton.disabled = true;
|
||||
nextButton.label = gUpdates.strings.getString("IAgreeLabel");
|
||||
document.documentElement.getButton("back").disabled = true;
|
||||
document.documentElement.getButton("next").focus();
|
||||
de.getButton("back").disabled = true;
|
||||
de.getButton("next").focus();
|
||||
|
||||
var cancelButton = de.getButton("cancel");
|
||||
cancelButton.label = gUpdates.strings.getString("IDoNotAgreeLabel");
|
||||
|
||||
this._licenseContent.addEventListener("load", this.onLicenseLoad, false);
|
||||
this._licenseContent.url = gUpdates.update.licenseURL;
|
||||
|
@ -345,6 +480,14 @@ var gLicensePage = {
|
|||
|
||||
onWizardCancel: function() {
|
||||
this._licenseContent.stopDownloading();
|
||||
|
||||
// The user said "Do Not Agree", so stop all update checks for this session
|
||||
// so that we don't bother them again.
|
||||
const nsIUpdateChecker = Components.interfaces.nsIUpdateChecker;
|
||||
var aus =
|
||||
Components.classes["@mozilla.org/updates/update-service;1"].
|
||||
getService(Components.interfaces.nsIApplicationUpdateService);
|
||||
aus.backgroundChecker.stopChecking(nsIUpdateChecker.CURRENT_SESSION);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -598,11 +741,14 @@ var gDownloadingPage = {
|
|||
updates.addDownloadListener(this);
|
||||
}
|
||||
|
||||
document.documentElement.getButton("back").disabled = true;
|
||||
document.documentElement.getButton("next").disabled = true;
|
||||
var cancelButton = document.documentElement.getButton("cancel");
|
||||
var de = document.documentElement;
|
||||
de.getButton("back").disabled = true;
|
||||
var cancelButton = de.getButton("cancel");
|
||||
cancelButton.label = gUpdates.strings.getString("closeButtonLabel");
|
||||
cancelButton.focus();
|
||||
var nextButton = de.getButton("next");
|
||||
nextButton.disabled = true;
|
||||
nextButton.label = gUpdates.buttonLabel_next;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -626,13 +772,14 @@ var gDownloadingPage = {
|
|||
*/
|
||||
_togglePausedState: function(paused) {
|
||||
var u = gUpdates.update;
|
||||
var p = u.selectedPatch.QueryInterface(Components.interfaces.nsIPropertyBag);
|
||||
if (paused) {
|
||||
this._oldStatus = this._downloadStatus.textContent;
|
||||
this._oldMode = this._downloadProgress.mode;
|
||||
this._oldProgress = parseInt(this._downloadProgress.progress);
|
||||
this._downloadName.value = gUpdates.strings.getFormattedString(
|
||||
"pausedName", [u.name]);
|
||||
this._setStatus(u.selectedPatch.status);
|
||||
this._setStatus(p.getProperty("status"));
|
||||
this._downloadProgress.mode = "normal";
|
||||
|
||||
this._pauseButton.label = gUpdates.strings.getString("pauseButtonResume");
|
||||
|
@ -640,8 +787,9 @@ var gDownloadingPage = {
|
|||
else {
|
||||
this._downloadName.value = gUpdates.strings.getFormattedString(
|
||||
"downloadingPrefix", [u.name]);
|
||||
this._setStatus(this._oldStatus || u.selectedPatch.status);
|
||||
this._downloadProgress.value = this._oldProgress || u.selectedPatch.progress;
|
||||
this._setStatus(this._oldStatus || p.getProperty("status"));
|
||||
this._downloadProgress.value =
|
||||
this._oldProgress || parseInt(p.getProperty("progress"));
|
||||
this._downloadProgress.mode = this._oldMode || "normal";
|
||||
this._pauseButton.label = gUpdates.strings.getString("pauseButtonPause");
|
||||
}
|
||||
|
@ -657,9 +805,11 @@ var gDownloadingPage = {
|
|||
if (this._paused)
|
||||
updates.downloadUpdate(gUpdates.update, false);
|
||||
else {
|
||||
gUpdates.update.selectedPatch.status =
|
||||
var patch = gUpdates.update.selectedPatch;
|
||||
patch.QueryInterface(Components.interfaces.nsIWritablePropertyBag);
|
||||
patch.setProperty("status",
|
||||
gUpdates.strings.getFormattedString("pausedStatus",
|
||||
[this._statusFormatter.progress]);
|
||||
[this._statusFormatter.progress]));
|
||||
updates.pauseDownload();
|
||||
}
|
||||
this._paused = !this._paused;
|
||||
|
@ -728,15 +878,18 @@ var gDownloadingPage = {
|
|||
request.QueryInterface(nsIIncrementalDownload);
|
||||
// LOG("gDownloadingPage.onProgress: " + request.URI.spec + ", " + progress + "/" + maxProgress);
|
||||
|
||||
gUpdates.update.selectedPatch.status =
|
||||
this._statusFormatter.formatStatus(progress, maxProgress);
|
||||
var p = gUpdates.update.selectedPatch;
|
||||
p.QueryInterface(Components.interfaces.nsIWritablePropertyBag);
|
||||
p.setProperty("progress", Math.round(100 * (progress/maxProgress)));
|
||||
p.setProperty("status",
|
||||
this._statusFormatter.formatStatus(progress, maxProgress));
|
||||
|
||||
this._downloadProgress.mode = "normal";
|
||||
this._downloadProgress.value = gUpdates.update.selectedPatch.progress;
|
||||
this._downloadProgress.value = parseInt(p.getProperty("progress"));
|
||||
this._pauseButton.disabled = false;
|
||||
var name = gUpdates.strings.getFormattedString("downloadingPrefix", [gUpdates.update.name]);
|
||||
this._downloadName.value = name;
|
||||
this._setStatus(gUpdates.update.selectedPatch.status);
|
||||
this._setStatus(p.getProperty("status"));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -909,6 +1062,11 @@ var gFinishedPage = {
|
|||
[gUpdates.brandName]);
|
||||
ps.alert(window, gUpdates.strings.getString("restartLaterTitle"),
|
||||
message);
|
||||
|
||||
var interval = getPref("getIntPref", PREF_UPDATE_NAGTIMER_RESTART,
|
||||
18000000);
|
||||
gUpdates.registerNagTimer("restart-nag-timer", interval,
|
||||
"showUpdateComplete");
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -69,16 +69,6 @@ interface nsIUpdatePatch : nsISupports
|
|||
*/
|
||||
attribute unsigned long size;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
attribute double progress;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
attribute AString status;
|
||||
|
||||
/**
|
||||
* The state of this update
|
||||
*/
|
||||
|
@ -231,18 +221,56 @@ interface nsIUpdatePrompt : nsISupports
|
|||
interface nsIUpdateChecker : nsISupports
|
||||
{
|
||||
/**
|
||||
* Ends any pending update check.
|
||||
* Checks for available updates, notifying a listener of the results.
|
||||
* @param listener
|
||||
* An object implementing nsIUpdateCheckListener which is notified
|
||||
* of the results of an update check.
|
||||
*/
|
||||
void stopChecking();
|
||||
void checkForUpdates(in nsIUpdateCheckListener listener);
|
||||
|
||||
/**
|
||||
* Constants for the |stopChecking| function that tell the Checker how long
|
||||
* to stop checking:
|
||||
*
|
||||
* CURRENT_CHECK: Stops the current (active) check only
|
||||
* CURRENT_SESSION: Stops all checking for the current session
|
||||
* ANY_CHECKS: Stops all checking, any session from now on
|
||||
* (disables update checking preferences)
|
||||
*/
|
||||
const unsigned short CURRENT_CHECK = 1;
|
||||
const unsigned short CURRENT_SESSION = 2;
|
||||
const unsigned short ANY_CHECKS = 3;
|
||||
|
||||
/**
|
||||
* Ends any pending update check.
|
||||
* @param duration
|
||||
* A value representing the set of checks to stop doing.
|
||||
*/
|
||||
void stopChecking(in unsigned short duration);
|
||||
|
||||
/**
|
||||
* Whether or not this Checker can perform update checking.
|
||||
*/
|
||||
readonly attribute boolean enabled;
|
||||
};
|
||||
|
||||
[scriptable, uuid(9849c4bf-5197-4d22-baa8-e3b44a1703d2)]
|
||||
interface nsIApplicationUpdateService : nsISupports
|
||||
{
|
||||
/**
|
||||
*
|
||||
* The Update Checker used for background update checking.
|
||||
*/
|
||||
nsIUpdateChecker checkForUpdates(in nsIUpdateCheckListener listener);
|
||||
readonly attribute nsIUpdateChecker backgroundChecker;
|
||||
|
||||
/**
|
||||
* Selects the best update to install from a list of available updates.
|
||||
* @param updates
|
||||
* An array of updates that are available
|
||||
* @param updateCount
|
||||
* The length of the |updates| array
|
||||
*/
|
||||
nsIUpdate selectUpdate([array, size_is(updateCount)] in nsIUpdate updates,
|
||||
in unsigned long updateCount);
|
||||
|
||||
/**
|
||||
* Adds a listener that receives progress and state information about the
|
||||
|
@ -263,16 +291,6 @@ interface nsIApplicationUpdateService : nsISupports
|
|||
*/
|
||||
void removeDownloadListener(in nsIRequestObserver listener);
|
||||
|
||||
/**
|
||||
* Selects the best update to install from a list of available updates.
|
||||
* @param updates
|
||||
* An array of updates that are available
|
||||
* @param updateCount
|
||||
* The length of the |updates| array
|
||||
*/
|
||||
nsIUpdate selectUpdate([array, size_is(updateCount)] in nsIUpdate updates,
|
||||
in unsigned long updateCount);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -322,22 +340,45 @@ interface nsIUpdateManager : nsISupports
|
|||
interface nsIUpdateTimerManager : nsISupports
|
||||
{
|
||||
/**
|
||||
*
|
||||
* Register an interval with the timer manager. The timer manager
|
||||
* periodically checks to see if the interval has expired and if it has
|
||||
* calls the specified callback. This is persistent across application
|
||||
* restarts and can handle intervals of long durations.
|
||||
* @param id
|
||||
* An id that identifies the interval, used for persistence
|
||||
* @param callback
|
||||
* A nsITimerCallback object that is notified when the interval
|
||||
* expires
|
||||
* @param interval
|
||||
* The length of time, in milliseconds, of the interval
|
||||
*/
|
||||
void registerTimer(in AString id,
|
||||
in nsITimerCallback callback,
|
||||
in unsigned long interval,
|
||||
in unsigned long type);
|
||||
in unsigned long interval);
|
||||
};
|
||||
|
||||
[scriptable, uuid(22d35700-5765-42e1-914b-a0da7c911a8c)]
|
||||
interface nsIVersionChecker : nsISupports
|
||||
{
|
||||
// -ve if B is newer
|
||||
// equal if A == B
|
||||
// +ve if A is newer
|
||||
long compare(in AString aVersionA, in AString aVersionB);
|
||||
/**
|
||||
* Compare two FVF versions
|
||||
* @param versionA
|
||||
* The first version
|
||||
* @param versionB
|
||||
* The second version
|
||||
* @returns < 0 if A < B
|
||||
* = 0 if A == B
|
||||
* > 0 if B > A
|
||||
* XXXben - change the return value here to return a constant
|
||||
*/
|
||||
long compare(in AString versionA, in AString versionB);
|
||||
|
||||
boolean isValidVersion(in AString aVersion);
|
||||
/**
|
||||
* Determines if a string is a valid FVF version
|
||||
* @param string
|
||||
* The string to validate
|
||||
* @returns true if the string is a valid FVF version
|
||||
*/
|
||||
boolean isValidVersion(in AString version);
|
||||
};
|
||||
|
||||
|
|
|
@ -429,7 +429,6 @@ UpdateService.prototype = {
|
|||
var um = Components.classes["@mozilla.org/updates/update-manager;1"]
|
||||
.getService(Components.interfaces.nsIUpdateManager);
|
||||
var activeUpdate = um.activeUpdate;
|
||||
LOG("ACTIVEUPDATE = " + activeUpdate);
|
||||
if (activeUpdate)
|
||||
this.downloadUpdate(activeUpdate, true);
|
||||
break;
|
||||
|
@ -441,8 +440,7 @@ UpdateService.prototype = {
|
|||
Components.classes["@mozilla.org/updates/timer-manager;1"]
|
||||
.getService(Components.interfaces.nsIUpdateTimerManager);
|
||||
var interval = getPref("getIntPref", PREF_APP_UPDATE_INTERVAL, 86400000);
|
||||
tm.registerTimer("background-update-timer", this, interval,
|
||||
Components.interfaces.nsITimer.TYPE_REPEATING_SLACK);
|
||||
tm.registerTimer("background-update-timer", this, interval);
|
||||
break;
|
||||
case "xpcom-shutdown":
|
||||
gOS.removeObserver(this, "xpcom-shutdown");
|
||||
|
@ -472,7 +470,7 @@ UpdateService.prototype = {
|
|||
|
||||
notify: function(timer) {
|
||||
// If a download is in progress, then do nothing.
|
||||
if (this.isDownloading)
|
||||
if (this.isDownloading || this._downloader && this._downloader.patchIsStaged)
|
||||
return;
|
||||
|
||||
var self = this;
|
||||
|
@ -484,7 +482,7 @@ UpdateService.prototype = {
|
|||
onError: function() { },
|
||||
}
|
||||
|
||||
this.checkForUpdates(listener);
|
||||
this.backgroundChecker.checkForUpdates(listener);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -514,12 +512,18 @@ UpdateService.prototype = {
|
|||
// Minor 1 Yes Notify and Confirm
|
||||
// Minor 2 Yes or No Notify and Confirm
|
||||
//
|
||||
// In addition, if there is a license associated with an update, regardless
|
||||
// of type it must be agreed to.
|
||||
//
|
||||
// If app.update.enabled is set to false, an update check is not performed
|
||||
// at all, and so none of the decision making above is entered into.
|
||||
//
|
||||
if (update.licenseURL)
|
||||
return true;
|
||||
|
||||
var updateEnabled = getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true);
|
||||
if (!updateEnabled)
|
||||
return;
|
||||
return false;
|
||||
|
||||
var mode = getPref("getIntPref", PREF_APP_UPDATE_AUTOINSTALL_MODE, 0);
|
||||
var compatible = isCompatible(update);
|
||||
|
@ -580,12 +584,17 @@ UpdateService.prototype = {
|
|||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* The Checker used for background update checks.
|
||||
*/
|
||||
checkForUpdates: function(listener) {
|
||||
var checker = new Checker();
|
||||
checker.findUpdates(listener);
|
||||
return checker;
|
||||
_backgroundChecker: null,
|
||||
|
||||
/**
|
||||
* See nsIUpdateService.idl
|
||||
*/
|
||||
get backgroundChecker() {
|
||||
if (!this._backgroundChecker)
|
||||
this._backgroundChecker = new Checker();
|
||||
return this._backgroundChecker;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -871,15 +880,17 @@ function getPref(func, preference, defaultValue) {
|
|||
* @constructor
|
||||
*/
|
||||
function UpdatePatch(patch) {
|
||||
this.type = patch.getAttribute("type");
|
||||
this.URL = patch.getAttribute("URL");
|
||||
this.hashFunction = patch.getAttribute("hashFunction");
|
||||
this.hashValue = patch.getAttribute("hashValue");
|
||||
this.size = parseInt(patch.getAttribute("size"));
|
||||
this.percentage = parseFloat(patch.getAttribute("percentage"));
|
||||
this.state = patch.getAttribute("state");
|
||||
this.status = patch.getAttribute("status");
|
||||
this.selected = patch.getAttribute("selected") == "true";
|
||||
for (var i = 0; i < patch.attributes.length; ++i) {
|
||||
var attr = patch.attributes[i];
|
||||
switch (attr.name) {
|
||||
case "selected":
|
||||
this.selected = attr.value == "true";
|
||||
break;
|
||||
default:
|
||||
this[attr.name] = attr.value;
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
UpdatePatch.prototype = {
|
||||
/**
|
||||
|
@ -892,18 +903,65 @@ UpdatePatch.prototype = {
|
|||
patch.setAttribute("hashFunction", this.hashFunction);
|
||||
patch.setAttribute("hashValue", this.hashValue);
|
||||
patch.setAttribute("size", this.size);
|
||||
patch.setAttribute("percentage", this.percentage);
|
||||
patch.setAttribute("state", this.state);
|
||||
patch.setAttribute("status", this.status);
|
||||
patch.setAttribute("selected", this.selected);
|
||||
|
||||
for (var p in this._properties) {
|
||||
if (this._properties[p].present)
|
||||
patch.setAttribute(p, this._properties[p].data);
|
||||
}
|
||||
|
||||
return patch;
|
||||
},
|
||||
|
||||
/**
|
||||
* A hash of custom properties
|
||||
*/
|
||||
_properties: { },
|
||||
|
||||
/**
|
||||
* See nsIWritablePropertyBag.idl
|
||||
*/
|
||||
setProperty: function(name, value) {
|
||||
this._properties[name] = { data: value, present: true };
|
||||
},
|
||||
|
||||
/**
|
||||
* See nsIWritablePropertyBag.idl
|
||||
*/
|
||||
deleteProperty: function(name) {
|
||||
if ("name" in this._properties)
|
||||
this._properties[name].present = false;
|
||||
else
|
||||
throw Components.results.NS_ERROR_FAILURE;
|
||||
},
|
||||
|
||||
/**
|
||||
* See nsIPropertyBag.idl
|
||||
*/
|
||||
get enumerator() {
|
||||
var properties = [];
|
||||
for (var p in this._properties)
|
||||
properties.push(this._properties[p].data);
|
||||
return new ArrayEnumerator(properties);
|
||||
},
|
||||
|
||||
/**
|
||||
* See nsIPropertyBag.idl
|
||||
*/
|
||||
getProperty: function(name) {
|
||||
if ("name" in this._properties &&
|
||||
this._properties[name].present)
|
||||
return this._properties[name].data;
|
||||
throw Components.results.NS_ERROR_FAILURE;
|
||||
},
|
||||
|
||||
/**
|
||||
* See nsISupports.idl
|
||||
*/
|
||||
QueryInterface: function(iid) {
|
||||
if (!iid.equals(Components.interfaces.nsIUpdatePatch) &&
|
||||
!iid.equals(Components.interfaces.nsIPropertyBag) &&
|
||||
!iid.equals(Components.interfaces.nsIWritablePropertyBag) &&
|
||||
!iid.equals(Components.interfaces.nsISupports))
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
return this;
|
||||
|
@ -1067,13 +1125,13 @@ Checker.prototype = {
|
|||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* See nsIUpdateService.idl
|
||||
*/
|
||||
findUpdates: function(callback) {
|
||||
if (!callback)
|
||||
checkForUpdates: function(listener) {
|
||||
if (!listener)
|
||||
throw Components.results.NS_ERROR_NULL_POINTER;
|
||||
|
||||
if (!this._updateURL)
|
||||
if (!this._updateURL || !this.enabled)
|
||||
return;
|
||||
|
||||
this._request = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
|
@ -1087,10 +1145,10 @@ Checker.prototype = {
|
|||
this._request.onload = function(event) { self.onLoad(event); };
|
||||
this._request.onprogress = function(event) { self.onProgress(event); };
|
||||
|
||||
LOG("Checker.findUpdates: sending request to " + this._updateURL);
|
||||
LOG("Checker.checkForUpdates: sending request to " + this._updateURL);
|
||||
this._request.send(null);
|
||||
|
||||
this._callback = callback;
|
||||
this._callback = listener;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1157,11 +1215,36 @@ Checker.prototype = {
|
|||
this._callback.onError(event.target);
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether or not we are allowed to do update checking.
|
||||
*/
|
||||
_enabled: true,
|
||||
|
||||
/**
|
||||
* See nsIUpdateService.idl
|
||||
*/
|
||||
stopChecking: function() {
|
||||
get enabled() {
|
||||
return getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true) ||
|
||||
this._enabled;
|
||||
},
|
||||
|
||||
/**
|
||||
* See nsIUpdateService.idl
|
||||
*/
|
||||
stopChecking: function(duration) {
|
||||
// Always stop the current check
|
||||
this._request.abort();
|
||||
|
||||
const nsIUpdateChecker = Components.interfaces.nsIUpdateChecker;
|
||||
switch (duration) {
|
||||
case nsIUpdateChecker.CURRENT_SESSION:
|
||||
this._enabled = false;
|
||||
break;
|
||||
case nsIUpdateChecker.ANY_CHECKS:
|
||||
this._enabled = false;
|
||||
gPref.setBoolPref(PREF_APP_UPDATE_ENABLED, this._enabled);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1209,7 +1292,7 @@ Downloader.prototype = {
|
|||
isCompleteUpdate: null,
|
||||
|
||||
/**
|
||||
*
|
||||
* Cancels the active download.
|
||||
*/
|
||||
cancel: function() {
|
||||
if (this._request &&
|
||||
|
@ -1220,9 +1303,9 @@ Downloader.prototype = {
|
|||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* Retrieves the active Updates directory, as a nsIFile object.
|
||||
*/
|
||||
_getUpdatesDir: function() {
|
||||
get _updatesDir() {
|
||||
// Right now, we only support downloading one patch at a time, so we always
|
||||
// use the same target directory.
|
||||
var fileLocator =
|
||||
|
@ -1262,6 +1345,13 @@ Downloader.prototype = {
|
|||
return readStringFromFile(statusFile);
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether or not a patch has been downloaded and staged for installation.
|
||||
*/
|
||||
get patchIsStaged() {
|
||||
return this._readStatusFile(this._updatesDir) == STATE_PENDING;
|
||||
},
|
||||
|
||||
/**
|
||||
* Verify the downloaded file. We assume that the download is complete at
|
||||
* this point.
|
||||
|
@ -1365,7 +1455,7 @@ Downloader.prototype = {
|
|||
updateDir.remove(true);
|
||||
} catch (e) {}
|
||||
// Restore the updateDir since we may have deleted it.
|
||||
updateDir = this._getUpdatesDir();
|
||||
updateDir = this._updatesDir;
|
||||
|
||||
selectedPatch = null;
|
||||
}
|
||||
|
@ -1408,13 +1498,14 @@ Downloader.prototype = {
|
|||
if (!update)
|
||||
throw Components.results.NS_ERROR_NULL_POINTER;
|
||||
|
||||
var updateDir = this._getUpdatesDir();
|
||||
var updateDir = this._updatesDir;
|
||||
|
||||
this._update = update;
|
||||
|
||||
// This function may return null, which indicates that there are no patches
|
||||
// to download.
|
||||
this._patch = this._selectPatch(update, updateDir);
|
||||
LOG("PATCH = " + this._patch);
|
||||
if (!this._patch) {
|
||||
LOG("no patch to download");
|
||||
return this._readStatusFile(updateDir);
|
||||
|
@ -1441,6 +1532,7 @@ Downloader.prototype = {
|
|||
this._request.start(this, null);
|
||||
|
||||
this._writeStatusFile(updateDir, STATE_DOWNLOADING);
|
||||
this._patch.QueryInterface(Components.interfaces.nsIWritablePropertyBag);
|
||||
this._patch.state = STATE_DOWNLOADING;
|
||||
var um = Components.classes["@mozilla.org/updates/update-manager;1"]
|
||||
.getService(Components.interfaces.nsIUpdateManager);
|
||||
|
@ -1495,8 +1587,6 @@ Downloader.prototype = {
|
|||
request.QueryInterface(nsIIncrementalDownload);
|
||||
LOG("Downloader.onProgress: " + request.URI.spec + ", " + progress + "/" + maxProgress);
|
||||
|
||||
this._patch.progress = Math.round(100 * (progress/maxProgress));
|
||||
|
||||
var listenerCount = this._listeners.length;
|
||||
for (var i = 0; i < listenerCount; ++i) {
|
||||
var listener = this._listeners[i];
|
||||
|
@ -1553,7 +1643,8 @@ Downloader.prototype = {
|
|||
// download initiates.
|
||||
}
|
||||
}
|
||||
this._writeStatusFile(this._getUpdatesDir(), state);
|
||||
this._writeStatusFile(this._updatesDir, state);
|
||||
this._patch.QueryInterface(Components.interfaces.nsIWritablePropertyBag);
|
||||
this._patch.state = state;
|
||||
var um = Components.classes["@mozilla.org/updates/update-manager;1"]
|
||||
.getService(Components.interfaces.nsIUpdateManager);
|
||||
|
@ -1598,82 +1689,61 @@ Downloader.prototype = {
|
|||
* @constructor
|
||||
*/
|
||||
function TimerManager() {
|
||||
const nsITimer = Components.interfaces.nsITimer;
|
||||
this._timer = Components.classes["@mozilla.org/timer;1"]
|
||||
.createInstance(nsITimer);
|
||||
var timerInterval = getPref("getIntPref", PREF_APP_UPDATE_TIMER, 5000);
|
||||
this._timer.initWithCallback(this, timerInterval,
|
||||
nsITimer.TYPE_REPEATING_SLACK);
|
||||
}
|
||||
TimerManager.prototype = {
|
||||
/**
|
||||
* The Checker Timer
|
||||
*/
|
||||
_timer: null,
|
||||
|
||||
/**
|
||||
* The set of registered timers.
|
||||
*/
|
||||
_timers: { },
|
||||
|
||||
/**
|
||||
*
|
||||
* Called when the checking timer fires.
|
||||
* @param timer
|
||||
* The checking timer that fired.
|
||||
*/
|
||||
registerTimer: function(id, callback, interval, type) {
|
||||
const nsITimer = Components.interfaces.nsITimer;
|
||||
var timer = Components.classes["@mozilla.org/timer;1"]
|
||||
.createInstance(nsITimer);
|
||||
var timerInterval = getPref("getIntPref", PREF_APP_UPDATE_TIMER, 5000);
|
||||
notify: function(timer) {
|
||||
for (var timerID in this._timers) {
|
||||
var timerData = this._timers[timerID];
|
||||
var lastUpdateTime = timerData.lastUpdateTime;
|
||||
var now = Math.round(Date.now() / 1000);
|
||||
|
||||
var self = this;
|
||||
// Fudge the lastUpdateTime by some random increment of the update
|
||||
// check interval (e.g. some random slice of 10 minutes) so that when
|
||||
// the time comes to check, we offset each client request by a random
|
||||
// amount so they don't all hit at once.
|
||||
lastUpdateTime += Math.round(Math.random() * timerData.interval);
|
||||
|
||||
/**
|
||||
* A callback object implementing nsITimerCallback that determines if the
|
||||
* user-registered callback should be invoked yet.
|
||||
* @param id
|
||||
* The id of the timer that fired
|
||||
* @param callback
|
||||
* The nsITimerCallback object supplied by the user that should be
|
||||
* notified if the user's interval has expired.
|
||||
* @param interval
|
||||
* The user's interval
|
||||
* @constructor
|
||||
*/
|
||||
function TimerCallback(id, callback, interval) {
|
||||
this.id = id;
|
||||
this.callback = callback;
|
||||
this.interval = interval;
|
||||
}
|
||||
TimerCallback.prototype = {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
notify: function(timer) {
|
||||
// LOG("self._timers = " + self._timers.toSource());
|
||||
var lastUpdateTime = self._timers[this.id].lastUpdateTime;
|
||||
var now = Math.round(Date.now() / 1000);
|
||||
// LOG("notify = " + (now - lastUpdateTime) + " > " + this.interval);
|
||||
|
||||
// Fudge the lastUpdateTime by some random increment of the update
|
||||
// check interval (e.g. some random slice of 10 minutes) so that when
|
||||
// the time comes to check, we offset each client request by a random
|
||||
// amount so they don't all hit at once.
|
||||
lastUpdateTime += Math.round(Math.random() * this.interval);
|
||||
|
||||
if ((now - lastUpdateTime) > this.interval &&
|
||||
this.callback instanceof Components.interfaces.nsITimerCallback) {
|
||||
this.callback.notify(timer);
|
||||
self._timers[this.id].lastUpdateTime = now;
|
||||
var preference = PREF_UPDATE_LASTUPDATETIME_FMT.replace(/%ID%/, this.id);
|
||||
gPref.setIntPref(preference, now);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* See nsISupports.idl
|
||||
*/
|
||||
QueryInterface: function(iid) {
|
||||
if (!iid.equals(Components.interfaces.nsITimerCallback) &&
|
||||
!iid.equals(Components.interfaces.nsISupports))
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
return this;
|
||||
if ((now - lastUpdateTime) > timerData.interval &&
|
||||
timerData.callback instanceof Components.interfaces.nsITimerCallback) {
|
||||
timerData.callback.notify(timer);
|
||||
timerData.lastUpdateTime = now;
|
||||
var preference = PREF_UPDATE_LASTUPDATETIME_FMT.replace(/%ID%/, timerID);
|
||||
gPref.setIntPref(preference, now);
|
||||
}
|
||||
};
|
||||
var tc = new TimerCallback(id, callback, interval);
|
||||
timer.initWithCallback(tc, timerInterval, type);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* See nsIUpdateService.idl
|
||||
*/
|
||||
registerTimer: function(id, callback, interval) {
|
||||
var preference = PREF_UPDATE_LASTUPDATETIME_FMT.replace(/%ID%/, id);
|
||||
var lastUpdateTime = getPref("getIntPref", preference,
|
||||
Math.round(Date.now() / 1000));
|
||||
this._timers[id] = { timer: timer, lastUpdateTime: lastUpdateTime };
|
||||
this._timers[id] = { callback : callback,
|
||||
interval : interval,
|
||||
lastUpdateTime : lastUpdateTime };
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1681,6 +1751,7 @@ TimerManager.prototype = {
|
|||
*/
|
||||
QueryInterface: function(iid) {
|
||||
if (!iid.equals(Components.interfaces.nsIUpdateTimerManager) &&
|
||||
!iid.equals(Components.interfaces.nsITimerCallback) &&
|
||||
!iid.equals(Components.interfaces.nsISupports))
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
return this;
|
||||
|
@ -1820,6 +1891,11 @@ var gModule = {
|
|||
className : "Update Service",
|
||||
factory : #1#(UpdateService)
|
||||
},
|
||||
checker: { CID : Components.ID("{898CDC9B-E43F-422F-9CC4-2F6291B415A3}"),
|
||||
contractID : "@mozilla.org/updates/update-checker;1",
|
||||
className : "Update Checker",
|
||||
factory : #1#(Checker)
|
||||
},
|
||||
manager: { CID : Components.ID("{093C2356-4843-4C65-8709-D7DBCBBE7DFB}"),
|
||||
contractID : "@mozilla.org/updates/update-manager;1",
|
||||
className : "Update Manager",
|
||||
|
@ -1860,12 +1936,14 @@ function NSGetModule(compMgr, fileSpec) {
|
|||
* the specified update, false otherwise.
|
||||
*/
|
||||
function isCompatible(update) {
|
||||
#ifdef MOZ_XULAPP
|
||||
#ifdef MOZ_XUL_APP
|
||||
var em = Components.classes["@mozilla.org/extensions/manager;1"]
|
||||
.getService(Components.interfaces.nsIExtensionManager);
|
||||
var items = em.getIncompatibleItemList("", update.extensionVersion,
|
||||
nsIUpdateItem.TYPE_ADDON, { });
|
||||
return items.length == 0;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче