diff --git a/mozilla/browser/app/profile/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/install.rdf.in b/mozilla/browser/app/profile/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/install.rdf.in
index 3ca7fec1db9..67d80789214 100644
--- a/mozilla/browser/app/profile/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/install.rdf.in
+++ b/mozilla/browser/app/profile/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/install.rdf.in
@@ -20,6 +20,14 @@
Firefox (default)
The default theme
+
+
+ true
true
diff --git a/mozilla/browser/base/content/utilityOverlay.js b/mozilla/browser/base/content/utilityOverlay.js
index 0ace836bd54..5418021dcc8 100644
--- a/mozilla/browser/base/content/utilityOverlay.js
+++ b/mozilla/browser/base/content/utilityOverlay.js
@@ -447,7 +447,7 @@ function checkForUpdates()
{
var prompter = Components.classes["@mozilla.org/updates/update-prompt;1"]
.createInstance(Components.interfaces.nsIUpdatePrompt);
- prompter.checkForUpdates(window);
+ prompter.checkForUpdates();
}
function buildHelpMenu()
diff --git a/mozilla/browser/components/preferences/advanced.js b/mozilla/browser/components/preferences/advanced.js
index 3d18ea7e8b1..a6512e4c37f 100644
--- a/mozilla/browser/components/preferences/advanced.js
+++ b/mozilla/browser/components/preferences/advanced.js
@@ -144,7 +144,7 @@ var gAdvancedPane = {
{
var prompter = Components.classes["@mozilla.org/updates/update-prompt;1"]
.createInstance(Components.interfaces.nsIUpdatePrompt);
- prompter.checkForUpdates(window);
+ prompter.checkForUpdates();
},
showAutoInstallOptions: function ()
diff --git a/mozilla/browser/components/preferences/advanced.xul b/mozilla/browser/components/preferences/advanced.xul
index 1d2864444e1..b1d1c5ce5fd 100644
--- a/mozilla/browser/components/preferences/advanced.xul
+++ b/mozilla/browser/components/preferences/advanced.xul
@@ -151,8 +151,7 @@
+ oncommand="gAdvancedPane.checkForUpdates();"/>
diff --git a/mozilla/extensions/inspector/install.rdf b/mozilla/extensions/inspector/install.rdf
index 56987efb280..5442965fdd7 100644
--- a/mozilla/extensions/inspector/install.rdf
+++ b/mozilla/extensions/inspector/install.rdf
@@ -26,6 +26,14 @@
+
+ true
+
DOM Inspector
Inspect the DOM of HTML, XUL, and XML pages, including the browser chrome.
diff --git a/mozilla/extensions/reporter/install.rdf b/mozilla/extensions/reporter/install.rdf
index cbe142a2a5e..cee6aff0bc9 100644
--- a/mozilla/extensions/reporter/install.rdf
+++ b/mozilla/extensions/reporter/install.rdf
@@ -26,6 +26,14 @@
+
+ true
+
Reporter
Report a broken web site.
diff --git a/mozilla/toolkit/mozapps/extensions/public/nsIExtensionManager.idl b/mozilla/toolkit/mozapps/extensions/public/nsIExtensionManager.idl
index 37bf8c0f258..8f1a3670a0a 100644
--- a/mozilla/toolkit/mozapps/extensions/public/nsIExtensionManager.idl
+++ b/mozilla/toolkit/mozapps/extensions/public/nsIExtensionManager.idl
@@ -194,7 +194,7 @@ interface nsIExtensionDownloadListener : nsISupports
* XXXben - Some of this stuff should go into a management-ey interface,
* some into an app-startup-ey interface.
*/
-[scriptable, uuid(344c6fd1-ed48-4c84-a472-abdec533d1e2)]
+[scriptable, uuid(e454bc28-cba8-4c27-83bd-01f2d5a2096c)]
interface nsIExtensionManager : nsISupports
{
/**
@@ -320,6 +320,9 @@ interface nsIExtensionManager : nsISupports
* The version of the application to check compatibility against
* @param type
* The type of item to return
+ * @param includeDisabled
+ * true if disabled items should be included in the result set,
+ * false otherwise
* @param countRef
* The XPCJS reference to the number of items returned.
* @returns An array of incompatible nsIUpdateItems.
@@ -327,6 +330,7 @@ interface nsIExtensionManager : nsISupports
void getIncompatibleItemList(in AString id,
in AString version,
in unsigned long type,
+ in boolean includeDisabled,
out unsigned long itemCount,
[retval, array, size_is(itemCount)] out nsIUpdateItem items);
diff --git a/mozilla/toolkit/mozapps/extensions/src/nsExtensionManager.js.in b/mozilla/toolkit/mozapps/extensions/src/nsExtensionManager.js.in
index 5b4a769aafe..6aaf39bd46c 100644
--- a/mozilla/toolkit/mozapps/extensions/src/nsExtensionManager.js.in
+++ b/mozilla/toolkit/mozapps/extensions/src/nsExtensionManager.js.in
@@ -3037,7 +3037,8 @@ ExtensionManager.prototype = {
var ds = this.datasource;
var currAppID = gApp.ID;
var items = ds.getIncompatibleItemList(currAppID, currAppVersion,
- nsIUpdateItem.TYPE_ADDON);
+ nsIUpdateItem.TYPE_ADDON,
+ true); // XXXben - maybe we should only show enabled items?
if (items.length > 0) {
// Now disable the items so they won't hurt anything.
for (var i = 0; i < items.length; ++i)
@@ -4301,8 +4302,10 @@ ExtensionManager.prototype = {
/**
* See nsIExtensionManager.idl
*/
- getIncompatibleItemList: function(id, version, type, countRef) {
- var items = this.datasource.getIncompatibleItemList(id, version, type);
+ getIncompatibleItemList: function(id, version, type, includeDisabled,
+ countRef) {
+ var items = this.datasource.getIncompatibleItemList(id, version, type,
+ includeDisabled);
countRef.value = items.length;
return items;
},
@@ -5260,10 +5263,12 @@ ExtensionsDataSource.prototype = {
* The Version of the application to check for incompatibility against.
* @param desiredType
* The nsIUpdateItem type of items to look for
+ * @param includeDisabled
+ * Whether or not disabled items should be included in the set returned
* @returns An array of nsIUpdateItems that are incompatible with the application
* ID/Version supplied.
*/
- getIncompatibleItemList: function(appID, appVersion, desiredType) {
+ getIncompatibleItemList: function(appID, appVersion, desiredType, includeDisabled) {
var items = [];
var ctr = getContainer(this._inner, this._itemRoot);
var elements = ctr.GetElements();
@@ -5271,7 +5276,25 @@ ExtensionsDataSource.prototype = {
var item = elements.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
var type = this.getItemProperty(id, "type");
- if (type != -1 && (type & desiredType) && !this.isCompatible(this, item, appVersion))
+ // Skip this item if we're not seeking disabled items
+ var disabled = this.getItemProperty(id, "disabled") == "true";
+ if (!includeDisabled && disabled)
+ continue;
+
+ // If the id of this item matches one of the items potentially installed
+ // with and maintained by this application AND it is installed in the
+ // global install location (i.e. the place installed by the app installer)
+ // it is and can be managed by the update file - it's not an item that has
+ // been manually installed by the user into their profile dir, and as such
+ // it is always compatible with the next release of the application since
+ // we will continue to support it.
+ var locationKey = this.getItemProperty(id, "installLocation");
+ var appManaged = this.getItemProperty(id, "appManaged") == "true";
+ if (!includeDisabled && appManaged && locationKey == KEY_APP_GLOBAL)
+ continue;
+
+ if (type != -1 && (type & desiredType) &&
+ !this.isCompatible(this, item, appVersion))
items.push(this.getItemForID(id));
}
return items;
@@ -5734,6 +5757,8 @@ ExtensionsDataSource.prototype = {
// (can't be removed or disabled), and hidden (not shown in the UI)
if (installLocation.restricted)
singleProps = singleProps.concat(["locked", "hidden"]);
+ if (installLocation.name == KEY_APP_GLOBAL)
+ singleProps = singleProps.concat(["appManaged"]);
for (var i = 0; i < singleProps.length; ++i) {
var property = EM_R(singleProps[i]);
var literal = installManifest.GetTarget(gInstallManifestRoot, property, true);
diff --git a/mozilla/toolkit/mozapps/update/content/history.xul b/mozilla/toolkit/mozapps/update/content/history.xul
index ebc4c6925d6..081190ea2e5 100644
--- a/mozilla/toolkit/mozapps/update/content/history.xul
+++ b/mozilla/toolkit/mozapps/update/content/history.xul
@@ -47,7 +47,7 @@
- 0) {
// There are addons that are incompatible with this update, so show the
// warning message.
diff --git a/mozilla/toolkit/mozapps/update/public/nsIUpdateService.idl b/mozilla/toolkit/mozapps/update/public/nsIUpdateService.idl
index eb6fab5daa9..6147bacf5f0 100644
--- a/mozilla/toolkit/mozapps/update/public/nsIUpdateService.idl
+++ b/mozilla/toolkit/mozapps/update/public/nsIUpdateService.idl
@@ -228,33 +228,25 @@ interface nsIUpdatePrompt : nsISupports
/**
* Shows a user interface that checks for and then displays the available
* updates.
- * @param parent
- * A parent window to anchor this window to. Can be null.
*/
- void checkForUpdates(in nsIDOMWindow parent);
+ void checkForUpdates();
/**
* Show a message advising that an update is available for download and
* install.
- * @param parent
- * A parent window to anchor this window to. Can be null.
* @param update
* The update to be downloaded and installed
*/
- void showUpdateAvailable(in nsIDOMWindow parent,
- in nsIUpdate update);
+ void showUpdateAvailable(in nsIUpdate update);
/**
* Show a message advising that an update has now been downloaded and that
* the user should restart their software should be restarted so that the
* update can be installed.
- * @param parent
- * A parent window to anchor this window to. Can be null.
* @param update
* The update that was downloaded
*/
- void showUpdateDownloaded(in nsIDOMWindow parent,
- in nsIUpdate update);
+ void showUpdateDownloaded(in nsIUpdate update);
/**
* Shows a message detailing the update which was installed.
diff --git a/mozilla/toolkit/mozapps/update/src/nsUpdateService.js.in b/mozilla/toolkit/mozapps/update/src/nsUpdateService.js.in
index a33f85eca5e..f043090e4a7 100644
--- a/mozilla/toolkit/mozapps/update/src/nsUpdateService.js.in
+++ b/mozilla/toolkit/mozapps/update/src/nsUpdateService.js.in
@@ -320,6 +320,27 @@ function cleanUpUpdatesDir() {
}
}
+/**
+ * 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) {
+ }
+ return defaultValue;
+}
+
/**
* An enumeration of items in a JS array.
* @constructor
@@ -400,805 +421,6 @@ function readStringFromFile(file) {
return text;
}
-/**
- * An object which can prompt the user with information about updates, request
- * action, etc. Embedding clients can override this component with one that
- * invokes a native front end.
- * @constructor
- */
-function UpdatePrompt() {
-}
-UpdatePrompt.prototype = {
- /**
- * See nsIUpdateService.idl
- */
- checkForUpdates: function(parent) {
- this._showUI(parent, URI_UPDATE_PROMPT_DIALOG, null, "Update:Wizard",
- null, null);
- },
-
- /**
- * See nsIUpdateService.idl
- */
- showUpdateAvailable: function(parent, update) {
- if (this._enabled) {
- this._showUI(parent, URI_UPDATE_PROMPT_DIALOG, null, "Update:Wizard",
- "updatesavailable", update);
- }
- },
-
- /**
- * See nsIUpdateService.idl
- */
- showUpdateDownloaded: function(parent, update) {
- if (this._enabled) {
- this._showUI(parent, URI_UPDATE_PROMPT_DIALOG, null, "Update:Wizard",
- "finishedBackground", update);
- }
- },
-
- /**
- * See nsIUpdateService.idl
- */
- showUpdateInstalled: function(update) {
- var showUpdateInstalledUI = getPref("getBoolPref",
- PREF_APP_UPDATE_SHOW_INSTALLED_UI, true);
- if (this._enabled && showUpdateInstalledUI) {
- this._showUI(null, URI_UPDATE_PROMPT_DIALOG, null, "Update:Wizard",
- "installed", update);
- }
- },
-
- /**
- * See nsIUpdateService.idl
- */
- showUpdateError: function(update) {
- if (this._enabled) {
- this._showUI(null, URI_UPDATE_PROMPT_DIALOG, null, "Update:Wizard",
- "errors", update);
- }
- },
-
- /**
- * See nsIUpdateService.idl
- */
- showUpdateHistory: function(parent) {
- this._showUI(parent, URI_UPDATE_HISTORY_DIALOG, "modal", "Update:History",
- null, null);
- },
-
- /**
- * Whether or not we are enabled (i.e. not in Silent mode)
- */
- get _enabled() {
- return !getPref("getBoolPref", PREF_APP_UPDATE_SILENT, false);
- },
-
- /**
- * Show the Update Checking UI
- * @param parent
- * A parent window, can be null
- * @param uri
- * The URI string of the dialog to show
- * @param name
- * The Window Name of the dialog to show, in case it is already open
- * and can merely be focused
- * @param page
- * The page of the wizard to be displayed, if one is already open.
- * @param update
- * An update to pass to the UI in the window arguments.
- * Can be null
- */
- _showUI: function(parent, uri, features, name, page, update) {
- var ary = null;
- if (update) {
- ary = Components.classes["@mozilla.org/supports-array;1"]
- .createInstance(Components.interfaces.nsISupportsArray);
- ary.AppendElement(update);
- }
-
- var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
- .getService(Components.interfaces.nsIWindowMediator);
- var win = wm.getMostRecentWindow(name);
- if (win) {
- if (page && "setCurrentPage" in win)
- win.setCurrentPage(page);
- win.focus();
- }
- else {
- var openFeatures = "chrome,centerscreen,dialog";
- if (features)
- openFeatures += "," + features;
- var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
- .getService(Components.interfaces.nsIWindowWatcher);
- ww.openWindow(parent, uri, "", openFeatures, ary);
- }
- },
-
- /**
- * See nsISupports.idl
- */
- QueryInterface: function(iid) {
- if (!iid.equals(Components.interfaces.nsIUpdatePrompt) &&
- !iid.equals(Components.interfaces.nsISupports))
- throw Components.results.NS_ERROR_NO_INTERFACE;
- return this;
- }
-};
-
-/**
- * UpdateService
- * A Service for managing the discovery and installation of software updates.
- */
-function UpdateService() {
- gApp = Components.classes["@mozilla.org/xre/app-info;1"]
- .getService(Components.interfaces.nsIXULAppInfo)
- .QueryInterface(Components.interfaces.nsIXULRuntime);
- gPref = Components.classes["@mozilla.org/preferences-service;1"]
- .getService(Components.interfaces.nsIPrefBranch2);
- gOS = Components.classes["@mozilla.org/observer-service;1"]
- .getService(Components.interfaces.nsIObserverService);
- gConsole = Components.classes["@mozilla.org/consoleservice;1"]
- .getService(Components.interfaces.nsIConsoleService);
-
- // Start the update timer only after a profile has been selected so that the
- // appropriate values for the update check are read from the user's profile.
- gOS.addObserver(this, "profile-after-change", false);
-
- // Observe xpcom-shutdown to unhook pref branch observers above to avoid
- // shutdown leaks.
- gOS.addObserver(this, "xpcom-shutdown", false);
-}
-
-UpdateService.prototype = {
- _downloader: null,
-
- observe: function(subject, topic, data) {
- switch (topic) {
- case "profile-after-change":
- gOS.removeObserver(this, "profile-after-change");
-
- this._initLoggingPrefs();
-
- //this._postUpdateCleanup();
-
- // Register a background update check timer
- var tm =
- 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);
-
- // Resume fetching...
- var um = Components.classes["@mozilla.org/updates/update-manager;1"]
- .getService(Components.interfaces.nsIUpdateManager);
- var activeUpdate = um.activeUpdate;
- if (activeUpdate)
- this.downloadUpdate(activeUpdate, true);
-
- break;
- case "xpcom-shutdown":
- gOS.removeObserver(this, "xpcom-shutdown");
- break;
- }
- },
-
- /**
- * Perform post-processing on updates lingering in the updates directory
- * from a previous browser session - either report install failures (and
- * optionally attempt to fetch a different version if appropriate) or
- * notify the user of install success.
- */
- _postUpdateCleanup: function() {
- /**
- * Clean up updates list and the updates directory.
- */
- function cleanup() {
- // Move the update from the Active Update list into the Past Updates list.
- um.activeUpdate = null;
- um.saveUpdates();
-
- // Now trash the updates directory, since we're done with it
- cleanUpUpdatesDir();
- }
-
- // Detect installation failures and notify
- var status = readStatusFile(getUpdatesDir());
- if (status != null) {
- // null status means the update.status file is not present, because either:
- // 1) no update was performed, and so there's no UI to show
- // 2) an update was attempted but failed during checking, transfer or
- // verification, and was cleaned up at that point, and UI notifying of
- // that error was shown at that stage.
- var um =
- Components.classes["@mozilla.org/updates/update-manager;1"].
- getService(Components.interfaces.nsIUpdateManager);
- var prompter =
- Components.classes["@mozilla.org/updates/update-prompt;1"].
- createInstance(Components.interfaces.nsIUpdatePrompt);
-
- var shouldCleanup = true;
- var update = um.activeUpdate;
- if (!update)
- update = new Update(null);
- update.state = status;
- var sbs =
- Components.classes["@mozilla.org/intl/stringbundle;1"].
- getService(Components.interfaces.nsIStringBundleService);
- var bundle = sbs.createBundle(URI_UPDATES_PROPERTIES);
- if (status == STATE_SUCCEEDED) {
- update.statusText = bundle.GetStringFromName("installSuccess");
- LOG("UpdateService", "_postUpdateCleanup: Install Succeeded, Showing UI");
- prompter.showUpdateInstalled(update);
-
- // Done with this update. Clean it up.
- cleanup();
- }
- else {
- // Something went wrong with the patch application process.
- cleanup();
-
- update.statusText = bundle.GetStringFromName("patchApplyFailure");
- var oldType = update.selectedPatch ? update.selectedPatch.type
- : "complete";
- if (update.selectedPatch && oldType == "partial") {
- // Partial patch application failed, try downloading the complete
- // update in the background instead.
- LOG("UpdateService", "_postUpdateCleanup: Install of Partial Patch " +
- "failed, downloading Complete Patch and maybe showing UI");
- this.downloadUpdate(update, true);
- }
- else {
- LOG("UpdateService", "_postUpdateCleanup: Install of Complete or " +
- "only patch failed. Showing error.");
- }
- update.QueryInterface(Components.interfaces.nsIWritablePropertyBag);
- update.setProperty("patchingFailed", oldType);
- prompter.showUpdateError(update);
- }
- }
- else {
- LOG("UpdateService", "_postUpdateCleanup: No Status, No Update");
- }
- },
-
- /**
- * Initialize Logging preferences, formatted like so:
- * app.update.log. =
- */
- _initLoggingPrefs: function() {
- try {
- var ps = Components.classes["@mozilla.org/preferences-service;1"]
- .getService(Components.interfaces.nsIPrefService);
- var logBranch = ps.getBranch(PREF_APP_UPDATE_LOG_BRANCH);
- var modules = logBranch.getChildList("", { value: 0 });
-
- for (var i = 0; i < modules.length; ++i) {
- if (logBranch.prefHasUserValue(modules[i]))
- gLogEnabled[modules[i]] = logBranch.getBoolPref(modules[i]);
- }
- }
- catch (e) {
- }
- },
-
- /**
- *
- */
- _needsToPromptForUpdate: function(updates) {
- // First, check for Extension incompatibilities. These trump any preference
- // settings.
- var em = Components.classes["@mozilla.org/extensions/manager;1"]
- .getService(Components.interfaces.nsIExtensionManager);
- var incompatibleList = { };
- for (var i = 0; i < updates.length; ++i) {
- var count = {};
- em.getIncompatibleItemList(gApp.ID, updates[i].extensionVersion,
- nsIUpdateItem.TYPE_ADDON, count);
- if (count.value > 0)
- return true;
- }
-
- // Now, inspect user preferences.
-
- // No prompt necessary, silently update...
- return false;
- },
-
- notify: function(timer) {
- // If a download is in progress, then do nothing.
- if (this.isDownloading || this._downloader && this._downloader.patchIsStaged)
- return;
-
- var self = this;
- var listener = {
- /**
- * See nsIUpdateService.idl
- */
- onProgress: function(request, position, totalSize) {
- },
-
- /**
- * See nsIUpdateService.idl
- */
- onCheckComplete: function(request, updates, updateCount) {
- self._selectAndInstallUpdate(updates);
- },
-
- /**
- * See nsIUpdateService.idl
- */
- onError: function(request, update) {
- LOG("Checker", "Error during background update: " + update.statusText);
- },
- }
- this.backgroundChecker.checkForUpdates(listener, false);
- },
-
- /**
- * Determine whether or not an update requires user confirmation before it
- * can be installed.
- * @param update
- * The update to be installed
- * @returns true if a prompt UI should be shown asking the user if they want
- * to install the update, false if the update should just be
- * silently downloaded and installed.
- */
- _shouldPrompt: function(update) {
- // There are two possible outcomes here:
- // 1. download and install the update automatically
- // 2. alert the user about the presence of an update before doing anything
- //
- // The outcome we follow is determined as follows:
- //
- // Update Type Mode Incompatible Outcome
- // Major 0 Yes or No Auto Install
- // Major 1 No Auto Install
- // Major 1 Yes Notify and Confirm
- // Major 2 Yes or No Notify and Confirm
- // Minor 0 Yes or No Auto Install
- // Minor 1 No Auto Install
- // Minor 1 Yes Notify and Confirm
- // Minor 2 No Auto Install
- // Minor 2 Yes 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.
- //
- update.QueryInterface(Components.interfaces.nsIPropertyBag);
- try {
- var licenseAccepted = update.getProperty("licenseAccepted") == "true";
- }
- catch (e) {
- licenseAccepted = false;
- }
- if (update.licenseURL && !licenseAccepted) {
- LOG("Checker", "_shouldPrompt: Prompting because of an un-accepted " +
- "license");
- return true;
- }
-
- var updateEnabled = getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true);
- if (!updateEnabled) {
- LOG("Checker", "_shouldPrompt: Not prompting because update is " +
- "disabled");
- return false;
- }
-
- // User has turned off automatic download and install
- var autoEnabled = getPref("getBoolPref", PREF_APP_UPDATE_AUTO, true);
- if (!autoEnabled) {
- LOG("Checker", "_shouldPrompt: Prompting because auto install is disabled");
- return true;
- }
-
- switch (getPref("getIntPref", PREF_APP_UPDATE_MODE, 1)) {
- case 1:
- // Mode 1 is do not prompt only if there are no incompatibilities
- // releases
- LOG("Checker", "_shouldPrompt: Prompting if there are incompatibilities");
- return !isCompatible(update);
- case 2:
- // Mode 2 is do not prompt only if there are no incompatibilities for
- // minor updates only
- LOG("Checker", "_shouldPrompt: Prompting if major or there are " +
- "incompatibilities");
- return update.type == "minor" ? !isCompatible(update) : true;
- }
- // Mode 0 is do not prompt regardless of incompatibilities
- LOG("Checker", "_shouldPrompt: Not prompting the user - they choose to " +
- "ignore incompatibilities");
- return false;
- },
-
- /**
- * Determine which of the specified updates should be installed.
- * @param updates
- * An array of available updates
- */
- selectUpdate: function(updates) {
- if (updates.length == 0)
- return null;
-
- // Choose the newest of the available minor and major updates.
- var majorUpdate = null, minorUpdate = null;
- var newestMinor = updates[0], newestMajor = updates[0];
-
- var vc = new VersionChecker();
- for (var i = 0; i < updates.length; ++i) {
- if (updates[i].type == "major" &&
- vc.compare(newestMajor.version, updates[i].version) <= 0)
- majorUpdate = newestMajor = updates[i];
- if (updates[i].type == "minor" &&
- vc.compare(newestMinor.version, updates[i].version) <= 0)
- minorUpdate = newestMinor = updates[i];
- }
-
- // If there's a major update, always try and fetch that one first,
- // otherwise fall back to the newest minor update.
- return majorUpdate || minorUpdate;
- },
-
- /**
- * Determine which of the specified updates should be installed and
- * begin the download/installation process, optionally prompting the
- * user for permission if required.
- * @param updates
- * An array of available updates
- */
- _selectAndInstallUpdate: function(updates) {
- // Don't prompt if there's an active update - the user is already
- // aware and is downloading, or performed some user action to prevent
- // notification.
- var um = Components.classes["@mozilla.org/updates/update-manager;1"]
- .getService(Components.interfaces.nsIUpdateManager);
- if (um.activeUpdate)
- return;
-
- var update = this.selectUpdate(updates, updates.length);
- if (!update)
- return;
- if (this._shouldPrompt(update)) {
- LOG("UpdateService", "_selectAndInstallUpdate: need to prompt user before continuing...");
- var prompter =
- Components.classes["@mozilla.org/updates/update-prompt;1"].
- createInstance(Components.interfaces.nsIUpdatePrompt);
- prompter.showUpdateAvailable(null, update);
- } else {
- this.downloadUpdate(update, true);
- }
- },
-
- /**
- * The Checker used for background update checks.
- */
- _backgroundChecker: null,
-
- /**
- * See nsIUpdateService.idl
- */
- get backgroundChecker() {
- if (!this._backgroundChecker)
- this._backgroundChecker = new Checker();
- return this._backgroundChecker;
- },
-
- /**
- * See nsIUpdateService.idl
- */
- get canUpdate() {
- try {
- var file = getFile(KEY_APPDIR, [FILE_PERMS_TEST]);
- if (!file.exists()) {
- file.create(nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
- file.remove(false);
- }
- }
- catch (e) {
- // No write privileges to install directory
- return false;
- }
- return true;
- },
-
- /**
- * See nsIUpdateService.idl
- */
- addDownloadListener: function(listener) {
- if (!this._downloader) {
- LOG("UpdateService", "addDownloadListener: no downloader!\n");
- return;
- }
- this._downloader.addDownloadListener(listener);
- },
-
- /**
- * See nsIUpdateService.idl
- */
- removeDownloadListener: function(listener) {
- if (!this._downloader) {
- LOG("UpdateService", "removeDownloadListener: no downloader!\n");
- return;
- }
- this._downloader.removeDownloadListener(listener);
- },
-
- /**
- * See nsIUpdateService.idl
- */
- downloadUpdate: function(update, background) {
- if (!update)
- throw Components.results.NS_ERROR_NULL_POINTER;
- if (this.isDownloading) {
- if (update.isCompleteUpdate == this._downloader.isCompleteUpdate &&
- background == this._downloader.background) {
- LOG("UpdateService", "no support for downloading more than one update at a time");
- return;
- }
- this._downloader.cancel();
- }
- this._downloader = new Downloader(background);
- return this._downloader.downloadUpdate(update);
- },
-
- /**
- * See nsIUpdateService.idl
- */
- pauseDownload: function() {
- if (this.isDownloading)
- this._downloader.cancel();
- },
-
- /**
- * See nsIUpdateService.idl
- */
- get isDownloading() {
- return this._downloader && this._downloader.isBusy;
- },
-
- /**
- * See nsISupports.idl
- */
- QueryInterface: function(iid) {
- if (!iid.equals(Components.interfaces.nsIApplicationUpdateService) &&
- !iid.equals(Components.interfaces.nsITimerCallback) &&
- !iid.equals(Components.interfaces.nsIObserver) &&
- !iid.equals(Components.interfaces.nsISupports))
- throw Components.results.NS_ERROR_NO_INTERFACE;
- return this;
- }
-};
-
-/**
- * A service to manage active and past updates.
- * @constructor
- */
-function UpdateManager() {
- // Ensure the Active Update file is loaded
- var updates = this._loadXMLFileIntoArray(getFile(KEY_APPDIR,
- [FILE_UPDATE_ACTIVE]));
- if (updates.length > 0)
- this._activeUpdate = updates[0];
-}
-UpdateManager.prototype = {
- /**
- * All previously downloaded and installed updates, as an array of nsIUpdate
- * objects.
- */
- _updates: null,
-
- /**
- * The current actively downloading/installing update, as a nsIUpdate object.
- */
- _activeUpdate: null,
-
- /**
- * Loads an updates.xml formatted file into an array of nsIUpdate items.
- * @param file
- * A nsIFile for the updates.xml file
- * @returns The array of nsIUpdate items held in the file.
- */
- _loadXMLFileIntoArray: function(file) {
- if (!file.exists()) {
- LOG("UpdateManager", "_loadXMLFileIntoArray: XML File does not exist");
- return [];
- }
-
- var result = [];
- var fileStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
- .createInstance(Components.interfaces.nsIFileInputStream);
- fileStream.init(file, MODE_RDONLY, PERMS_FILE, 0);
- try {
- var sis =
- Components.classes["@mozilla.org/scriptableinputstream;1"].
- createInstance(Components.interfaces.nsIScriptableInputStream);
- sis.init(fileStream);
- var data = sis.read(sis.available());
- var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
- .createInstance(Components.interfaces.nsIDOMParser);
- var doc = parser.parseFromString(data, "text/xml");
-
- var updateCount = doc.documentElement.childNodes.length;
- for (var i = 0; i < updateCount; ++i) {
- var updateElement = doc.documentElement.childNodes.item(i);
- if (updateElement.nodeType != Node.ELEMENT_NODE ||
- updateElement.localName != "update")
- continue;
-
- updateElement.QueryInterface(Components.interfaces.nsIDOMElement);
- result.push(new Update(updateElement));
- }
- }
- catch (e) {
- LOG("UpdateManager", "_loadXMLFileIntoArray: Error constructing update list " +
- e);
- }
- fileStream.close();
- return result;
- },
-
- /**
- * Load the update manager, initializing state from state files.
- */
- _ensureUpdates: function() {
- if (!this._updates)
- this._updates = this._loadXMLFileIntoArray(getFile(KEY_APPDIR,
- [FILE_UPDATES_DB]));
- },
-
- /**
- * See nsIUpdateService.idl
- */
- getUpdateAt: function(index) {
- this._ensureUpdates();
- return this._updates[index];
- },
-
- /**
- * See nsIUpdateService.idl
- */
- get updateCount() {
- this._ensureUpdates();
- return this._updates.length;
- },
-
- /**
- * See nsIUpdateService.idl
- */
- get activeUpdate() {
- if (this._activeUpdate &&
- this._activeUpdate.serviceURL != Checker.prototype.updateURL) {
- // User switched channels, clear out any old active updates and remove
- // partial downloads
- this._activeUpdate = null;
-
- // Destroy the updates directory, since we're done with it.
- cleanUpUpdatesDir();
- }
- return this._activeUpdate;
- },
- set activeUpdate(activeUpdate) {
- this._addUpdate(this._activeUpdate);
- this._activeUpdate = activeUpdate;
- if (!activeUpdate) {
- // If |activeUpdate| is null, we have updated both lists - the active list
- // and the history list, so we want to write both files.
- this.saveUpdates();
- }
- else
- this._writeUpdatesToXMLFile([this._activeUpdate],
- getFile(KEY_APPDIR, [FILE_UPDATE_ACTIVE]));
- return activeUpdate;
- },
-
- /**
- * Add an update to the Updates list. If the item already exists in the list,
- * replace the existing value with the new value.
- * @param update
- * The nsIUpdate object to add.
- */
- _addUpdate: function(update) {
- if (!update)
- return;
- if (this._updates) {
- for (var i = 0; i < this._updates.length; ++i) {
- if (this._updates[i].version == update.version) {
- // Replace the existing entry with the new value, updating
- // all metadata.
- this._updates[i] = update;
- return;
- }
- }
- }
- // Otherwise add it to the front of the list.
- if (this._activeUpdate)
- this._updates = [this._activeUpdate].concat(this._updates);
- },
-
- /**
- * Serializes an array of updates to an XML file
- * @param updates
- * An array of nsIUpdate objects
- * @param file
- * The nsIFile object to serialize to
- */
- _writeUpdatesToXMLFile: function(updates, file) {
- var fos = Components.classes["@mozilla.org/network/safe-file-output-stream;1"]
- .createInstance(Components.interfaces.nsIFileOutputStream);
- var modeFlags = MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE;
- if (!file.exists())
- file.create(nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
- fos.init(file, modeFlags, PERMS_FILE, 0);
-
- try {
- var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
- .createInstance(Components.interfaces.nsIDOMParser);
- const EMPTY_UPDATES_DOCUMENT = " ";
- var doc = parser.parseFromString(EMPTY_UPDATES_DOCUMENT, "text/xml");
-
- for (var i = 0; i < updates.length; ++i) {
- if (updates[i])
- doc.documentElement.appendChild(updates[i].serialize(doc));
- }
-
- var serializer = Components.classes["@mozilla.org/xmlextras/xmlserializer;1"]
- .createInstance(Components.interfaces.nsIDOMSerializer);
- serializer.serializeToStream(doc.documentElement, fos, null);
- }
- catch (e) {
- }
-
- closeSafeOutputStream(fos);
- },
-
- /**
- * See nsIUpdateService.idl
- */
- saveUpdates: function() {
- this._writeUpdatesToXMLFile([this._activeUpdate],
- getFile(KEY_APPDIR, [FILE_UPDATE_ACTIVE]));
- if (this._updates) {
- this._writeUpdatesToXMLFile(this._updates,
- getFile(KEY_APPDIR, [FILE_UPDATES_DB]));
- }
- },
-
- /**
- * See nsISupports.idl
- */
- QueryInterface: function(iid) {
- if (!iid.equals(Components.interfaces.nsIUpdateManager) &&
- !iid.equals(Components.interfaces.nsISupports))
- throw Components.results.NS_ERROR_NO_INTERFACE;
- return this;
- }
-};
-
-
-/**
- * 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) {
- }
- return defaultValue;
-}
-
/**
* Update Patch
* @param patch
@@ -1476,6 +698,695 @@ Update.prototype = {
}
};
+/**
+ * UpdateService
+ * A Service for managing the discovery and installation of software updates.
+ * @constructor
+ */
+function UpdateService() {
+ gApp = Components.classes["@mozilla.org/xre/app-info;1"]
+ .getService(Components.interfaces.nsIXULAppInfo)
+ .QueryInterface(Components.interfaces.nsIXULRuntime);
+ gPref = Components.classes["@mozilla.org/preferences-service;1"]
+ .getService(Components.interfaces.nsIPrefBranch2);
+ gOS = Components.classes["@mozilla.org/observer-service;1"]
+ .getService(Components.interfaces.nsIObserverService);
+ gConsole = Components.classes["@mozilla.org/consoleservice;1"]
+ .getService(Components.interfaces.nsIConsoleService);
+
+ // Start the update timer only after a profile has been selected so that the
+ // appropriate values for the update check are read from the user's profile.
+ gOS.addObserver(this, "profile-after-change", false);
+
+ // Observe xpcom-shutdown to unhook pref branch observers above to avoid
+ // shutdown leaks.
+ gOS.addObserver(this, "xpcom-shutdown", false);
+}
+
+UpdateService.prototype = {
+ /**
+ * The downloader we are using to download updates. There is only ever one of
+ * these.
+ */
+ _downloader: null,
+
+ /**
+ * Handle Observer Service notifications
+ * @param subject
+ * The subject of the notification
+ * @param topic
+ * The notification name
+ * @param data
+ * Additional data
+ */
+ observe: function(subject, topic, data) {
+ switch (topic) {
+ case "profile-after-change":
+ gOS.removeObserver(this, "profile-after-change");
+ this._start();
+ break;
+ case "xpcom-shutdown":
+ gOS.removeObserver(this, "xpcom-shutdown");
+
+ // Release Services
+ gApp = null;
+ gPref = null;
+ gOS = null;
+ gConsole = null;
+ break;
+ }
+ },
+
+ /**
+ * Start the Update Service
+ */
+ _start: function() {
+ // Start logging
+ this._initLoggingPrefs();
+
+ // Clean up any extant updates
+ this._postUpdateProcessing();
+
+ // Register a background update check timer
+ var tm =
+ 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);
+
+ // Resume fetching...
+ var um = Components.classes["@mozilla.org/updates/update-manager;1"]
+ .getService(Components.interfaces.nsIUpdateManager);
+ var activeUpdate = um.activeUpdate;
+ if (activeUpdate)
+ this.downloadUpdate(activeUpdate, true);
+ },
+
+ /**
+ * Perform post-processing on updates lingering in the updates directory
+ * from a previous browser session - either report install failures (and
+ * optionally attempt to fetch a different version if appropriate) or
+ * notify the user of install success.
+ */
+ _postUpdateProcessing: function() {
+ /**
+ * Clean up updates list and the updates directory.
+ */
+ function cleanup() {
+ // Move the update from the Active Update list into the Past Updates list.
+ um.activeUpdate = null;
+ um.saveUpdates();
+
+ // Now trash the updates directory, since we're done with it
+ cleanUpUpdatesDir();
+ }
+
+ // Detect installation failures and notify
+ var status = readStatusFile(getUpdatesDir());
+ if (status != null) {
+ // null status means the update.status file is not present, because either:
+ // 1) no update was performed, and so there's no UI to show
+ // 2) an update was attempted but failed during checking, transfer or
+ // verification, and was cleaned up at that point, and UI notifying of
+ // that error was shown at that stage.
+ var um =
+ Components.classes["@mozilla.org/updates/update-manager;1"].
+ getService(Components.interfaces.nsIUpdateManager);
+ var prompter =
+ Components.classes["@mozilla.org/updates/update-prompt;1"].
+ createInstance(Components.interfaces.nsIUpdatePrompt);
+
+ var shouldCleanup = true;
+ var update = um.activeUpdate;
+ if (!update)
+ update = new Update(null);
+ update.state = status;
+ var sbs =
+ Components.classes["@mozilla.org/intl/stringbundle;1"].
+ getService(Components.interfaces.nsIStringBundleService);
+ var bundle = sbs.createBundle(URI_UPDATES_PROPERTIES);
+ if (status == STATE_SUCCEEDED) {
+ update.statusText = bundle.GetStringFromName("installSuccess");
+ LOG("UpdateService", "_postUpdateCleanup: Install Succeeded, Showing UI");
+ prompter.showUpdateInstalled(update);
+
+ // Done with this update. Clean it up.
+ cleanup();
+ }
+ else {
+ // Something went wrong with the patch application process.
+ cleanup();
+
+ update.statusText = bundle.GetStringFromName("patchApplyFailure");
+ var oldType = update.selectedPatch ? update.selectedPatch.type
+ : "complete";
+ if (update.selectedPatch && oldType == "partial") {
+ // Partial patch application failed, try downloading the complete
+ // update in the background instead.
+ LOG("UpdateService", "_postUpdateCleanup: Install of Partial Patch " +
+ "failed, downloading Complete Patch and maybe showing UI");
+ this.downloadUpdate(update, true);
+ }
+ else {
+ LOG("UpdateService", "_postUpdateCleanup: Install of Complete or " +
+ "only patch failed. Showing error.");
+ }
+ update.QueryInterface(Components.interfaces.nsIWritablePropertyBag);
+ update.setProperty("patchingFailed", oldType);
+ prompter.showUpdateError(update);
+ }
+ }
+ else {
+ LOG("UpdateService", "_postUpdateCleanup: No Status, No Update");
+ }
+ },
+
+ /**
+ * Initialize Logging preferences, formatted like so:
+ * app.update.log. =
+ */
+ _initLoggingPrefs: function() {
+ try {
+ var ps = Components.classes["@mozilla.org/preferences-service;1"]
+ .getService(Components.interfaces.nsIPrefService);
+ var logBranch = ps.getBranch(PREF_APP_UPDATE_LOG_BRANCH);
+ var modules = logBranch.getChildList("", { value: 0 });
+
+ for (var i = 0; i < modules.length; ++i) {
+ if (logBranch.prefHasUserValue(modules[i]))
+ gLogEnabled[modules[i]] = logBranch.getBoolPref(modules[i]);
+ }
+ }
+ catch (e) {
+ }
+ },
+
+ /**
+ *
+ */
+ _needsToPromptForUpdate: function(updates) {
+ // First, check for Extension incompatibilities. These trump any preference
+ // settings.
+ var em = Components.classes["@mozilla.org/extensions/manager;1"]
+ .getService(Components.interfaces.nsIExtensionManager);
+ var incompatibleList = { };
+ for (var i = 0; i < updates.length; ++i) {
+ var count = {};
+ em.getIncompatibleItemList(gApp.ID, updates[i].extensionVersion,
+ nsIUpdateItem.TYPE_ADDON, false, count);
+ if (count.value > 0)
+ return true;
+ }
+
+ // Now, inspect user preferences.
+
+ // No prompt necessary, silently update...
+ return false;
+ },
+
+ /**
+ * Notified when a timer fires
+ * @param timer
+ * The timer that fired
+ */
+ notify: function(timer) {
+ // If a download is in progress, then do nothing.
+ if (this.isDownloading || this._downloader && this._downloader.patchIsStaged)
+ return;
+
+ var self = this;
+ var listener = {
+ /**
+ * See nsIUpdateService.idl
+ */
+ onProgress: function(request, position, totalSize) {
+ },
+
+ /**
+ * See nsIUpdateService.idl
+ */
+ onCheckComplete: function(request, updates, updateCount) {
+ self._selectAndInstallUpdate(updates);
+ },
+
+ /**
+ * See nsIUpdateService.idl
+ */
+ onError: function(request, update) {
+ LOG("Checker", "Error during background update: " + update.statusText);
+ },
+ }
+ this.backgroundChecker.checkForUpdates(listener, false);
+ },
+
+ /**
+ * Determine whether or not an update requires user confirmation before it
+ * can be installed.
+ * @param update
+ * The update to be installed
+ * @returns true if a prompt UI should be shown asking the user if they want
+ * to install the update, false if the update should just be
+ * silently downloaded and installed.
+ */
+ _shouldPrompt: function(update) {
+ // There are two possible outcomes here:
+ // 1. download and install the update automatically
+ // 2. alert the user about the presence of an update before doing anything
+ //
+ // The outcome we follow is determined as follows:
+ //
+ // Update Type Mode Incompatible Outcome
+ // Major 0 Yes or No Auto Install
+ // Major 1 No Auto Install
+ // Major 1 Yes Notify and Confirm
+ // Major 2 Yes or No Notify and Confirm
+ // Minor 0 Yes or No Auto Install
+ // Minor 1 No Auto Install
+ // Minor 1 Yes Notify and Confirm
+ // Minor 2 No Auto Install
+ // Minor 2 Yes 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.
+ //
+ update.QueryInterface(Components.interfaces.nsIPropertyBag);
+ try {
+ var licenseAccepted = update.getProperty("licenseAccepted") == "true";
+ }
+ catch (e) {
+ licenseAccepted = false;
+ }
+ if (update.licenseURL && !licenseAccepted) {
+ LOG("Checker", "_shouldPrompt: Prompting because of an un-accepted " +
+ "license");
+ return true;
+ }
+
+ var updateEnabled = getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true);
+ if (!updateEnabled) {
+ LOG("Checker", "_shouldPrompt: Not prompting because update is " +
+ "disabled");
+ return false;
+ }
+
+ // User has turned off automatic download and install
+ var autoEnabled = getPref("getBoolPref", PREF_APP_UPDATE_AUTO, true);
+ if (!autoEnabled) {
+ LOG("Checker", "_shouldPrompt: Prompting because auto install is disabled");
+ return true;
+ }
+
+ switch (getPref("getIntPref", PREF_APP_UPDATE_MODE, 1)) {
+ case 1:
+ // Mode 1 is do not prompt only if there are no incompatibilities
+ // releases
+ LOG("Checker", "_shouldPrompt: Prompting if there are incompatibilities");
+ return !isCompatible(update);
+ case 2:
+ // Mode 2 is do not prompt only if there are no incompatibilities for
+ // minor updates only
+ LOG("Checker", "_shouldPrompt: Prompting if major or there are " +
+ "incompatibilities");
+ return update.type == "minor" ? !isCompatible(update) : true;
+ }
+ // Mode 0 is do not prompt regardless of incompatibilities
+ LOG("Checker", "_shouldPrompt: Not prompting the user - they choose to " +
+ "ignore incompatibilities");
+ return false;
+ },
+
+ /**
+ * Determine which of the specified updates should be installed.
+ * @param updates
+ * An array of available updates
+ */
+ selectUpdate: function(updates) {
+ if (updates.length == 0)
+ return null;
+
+ // Choose the newest of the available minor and major updates.
+ var majorUpdate = null, minorUpdate = null;
+ var newestMinor = updates[0], newestMajor = updates[0];
+
+ var vc = new VersionChecker();
+ for (var i = 0; i < updates.length; ++i) {
+ if (updates[i].type == "major" &&
+ vc.compare(newestMajor.version, updates[i].version) <= 0)
+ majorUpdate = newestMajor = updates[i];
+ if (updates[i].type == "minor" &&
+ vc.compare(newestMinor.version, updates[i].version) <= 0)
+ minorUpdate = newestMinor = updates[i];
+ }
+
+ // If there's a major update, always try and fetch that one first,
+ // otherwise fall back to the newest minor update.
+ return majorUpdate || minorUpdate;
+ },
+
+ /**
+ * Determine which of the specified updates should be installed and
+ * begin the download/installation process, optionally prompting the
+ * user for permission if required.
+ * @param updates
+ * An array of available updates
+ */
+ _selectAndInstallUpdate: function(updates) {
+ // Don't prompt if there's an active update - the user is already
+ // aware and is downloading, or performed some user action to prevent
+ // notification.
+ var um = Components.classes["@mozilla.org/updates/update-manager;1"]
+ .getService(Components.interfaces.nsIUpdateManager);
+ if (um.activeUpdate)
+ return;
+
+ var update = this.selectUpdate(updates, updates.length);
+ if (!update)
+ return;
+ if (this._shouldPrompt(update)) {
+ LOG("UpdateService", "_selectAndInstallUpdate: need to prompt user before continuing...");
+ var prompter =
+ Components.classes["@mozilla.org/updates/update-prompt;1"].
+ createInstance(Components.interfaces.nsIUpdatePrompt);
+ prompter.showUpdateAvailable(update);
+ } else {
+ this.downloadUpdate(update, true);
+ }
+ },
+
+ /**
+ * The Checker used for background update checks.
+ */
+ _backgroundChecker: null,
+
+ /**
+ * See nsIUpdateService.idl
+ */
+ get backgroundChecker() {
+ if (!this._backgroundChecker)
+ this._backgroundChecker = new Checker();
+ return this._backgroundChecker;
+ },
+
+ /**
+ * See nsIUpdateService.idl
+ */
+ get canUpdate() {
+ try {
+ var file = getFile(KEY_APPDIR, [FILE_PERMS_TEST]);
+ if (!file.exists()) {
+ file.create(nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
+ file.remove(false);
+ }
+ }
+ catch (e) {
+ // No write privileges to install directory
+ return false;
+ }
+ // If the administrator has locked the app update functionality
+ // OFF - this is not just a user setting, so disable the manual
+ // UI too.
+ var enabled = getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true);
+ if (!enabled && gPref.prefIsLocked(PREF_APP_UPDATE_ENABLED))
+ return false;
+ return true;
+ },
+
+ /**
+ * See nsIUpdateService.idl
+ */
+ addDownloadListener: function(listener) {
+ if (!this._downloader) {
+ LOG("UpdateService", "addDownloadListener: no downloader!\n");
+ return;
+ }
+ this._downloader.addDownloadListener(listener);
+ },
+
+ /**
+ * See nsIUpdateService.idl
+ */
+ removeDownloadListener: function(listener) {
+ if (!this._downloader) {
+ LOG("UpdateService", "removeDownloadListener: no downloader!\n");
+ return;
+ }
+ this._downloader.removeDownloadListener(listener);
+ },
+
+ /**
+ * See nsIUpdateService.idl
+ */
+ downloadUpdate: function(update, background) {
+ if (!update)
+ throw Components.results.NS_ERROR_NULL_POINTER;
+ if (this.isDownloading) {
+ if (update.isCompleteUpdate == this._downloader.isCompleteUpdate &&
+ background == this._downloader.background) {
+ LOG("UpdateService", "no support for downloading more than one update at a time");
+ return;
+ }
+ this._downloader.cancel();
+ }
+ this._downloader = new Downloader(background);
+ return this._downloader.downloadUpdate(update);
+ },
+
+ /**
+ * See nsIUpdateService.idl
+ */
+ pauseDownload: function() {
+ if (this.isDownloading)
+ this._downloader.cancel();
+ },
+
+ /**
+ * See nsIUpdateService.idl
+ */
+ get isDownloading() {
+ return this._downloader && this._downloader.isBusy;
+ },
+
+ /**
+ * See nsISupports.idl
+ */
+ QueryInterface: function(iid) {
+ if (!iid.equals(Components.interfaces.nsIApplicationUpdateService) &&
+ !iid.equals(Components.interfaces.nsITimerCallback) &&
+ !iid.equals(Components.interfaces.nsIObserver) &&
+ !iid.equals(Components.interfaces.nsISupports))
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ return this;
+ }
+};
+
+/**
+ * A service to manage active and past updates.
+ * @constructor
+ */
+function UpdateManager() {
+ // Ensure the Active Update file is loaded
+ var updates = this._loadXMLFileIntoArray(getFile(KEY_APPDIR,
+ [FILE_UPDATE_ACTIVE]));
+ if (updates.length > 0)
+ this._activeUpdate = updates[0];
+}
+UpdateManager.prototype = {
+ /**
+ * All previously downloaded and installed updates, as an array of nsIUpdate
+ * objects.
+ */
+ _updates: null,
+
+ /**
+ * The current actively downloading/installing update, as a nsIUpdate object.
+ */
+ _activeUpdate: null,
+
+ /**
+ * Loads an updates.xml formatted file into an array of nsIUpdate items.
+ * @param file
+ * A nsIFile for the updates.xml file
+ * @returns The array of nsIUpdate items held in the file.
+ */
+ _loadXMLFileIntoArray: function(file) {
+ if (!file.exists()) {
+ LOG("UpdateManager", "_loadXMLFileIntoArray: XML File does not exist");
+ return [];
+ }
+
+ var result = [];
+ var fileStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
+ .createInstance(Components.interfaces.nsIFileInputStream);
+ fileStream.init(file, MODE_RDONLY, PERMS_FILE, 0);
+ try {
+ var sis =
+ Components.classes["@mozilla.org/scriptableinputstream;1"].
+ createInstance(Components.interfaces.nsIScriptableInputStream);
+ sis.init(fileStream);
+ var data = sis.read(sis.available());
+ var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
+ .createInstance(Components.interfaces.nsIDOMParser);
+ var doc = parser.parseFromString(data, "text/xml");
+
+ var updateCount = doc.documentElement.childNodes.length;
+ for (var i = 0; i < updateCount; ++i) {
+ var updateElement = doc.documentElement.childNodes.item(i);
+ if (updateElement.nodeType != Node.ELEMENT_NODE ||
+ updateElement.localName != "update")
+ continue;
+
+ updateElement.QueryInterface(Components.interfaces.nsIDOMElement);
+ result.push(new Update(updateElement));
+ }
+ }
+ catch (e) {
+ LOG("UpdateManager", "_loadXMLFileIntoArray: Error constructing update list " +
+ e);
+ }
+ fileStream.close();
+ return result;
+ },
+
+ /**
+ * Load the update manager, initializing state from state files.
+ */
+ _ensureUpdates: function() {
+ if (!this._updates)
+ this._updates = this._loadXMLFileIntoArray(getFile(KEY_APPDIR,
+ [FILE_UPDATES_DB]));
+ },
+
+ /**
+ * See nsIUpdateService.idl
+ */
+ getUpdateAt: function(index) {
+ this._ensureUpdates();
+ return this._updates[index];
+ },
+
+ /**
+ * See nsIUpdateService.idl
+ */
+ get updateCount() {
+ this._ensureUpdates();
+ return this._updates.length;
+ },
+
+ /**
+ * See nsIUpdateService.idl
+ */
+ get activeUpdate() {
+ if (this._activeUpdate &&
+ this._activeUpdate.serviceURL != Checker.prototype.updateURL) {
+ // User switched channels, clear out any old active updates and remove
+ // partial downloads
+ this._activeUpdate = null;
+
+ // Destroy the updates directory, since we're done with it.
+ cleanUpUpdatesDir();
+ }
+ return this._activeUpdate;
+ },
+ set activeUpdate(activeUpdate) {
+ this._addUpdate(this._activeUpdate);
+ this._activeUpdate = activeUpdate;
+ if (!activeUpdate) {
+ // If |activeUpdate| is null, we have updated both lists - the active list
+ // and the history list, so we want to write both files.
+ this.saveUpdates();
+ }
+ else
+ this._writeUpdatesToXMLFile([this._activeUpdate],
+ getFile(KEY_APPDIR, [FILE_UPDATE_ACTIVE]));
+ return activeUpdate;
+ },
+
+ /**
+ * Add an update to the Updates list. If the item already exists in the list,
+ * replace the existing value with the new value.
+ * @param update
+ * The nsIUpdate object to add.
+ */
+ _addUpdate: function(update) {
+ if (!update)
+ return;
+ if (this._updates) {
+ for (var i = 0; i < this._updates.length; ++i) {
+ if (this._updates[i].version == update.version) {
+ // Replace the existing entry with the new value, updating
+ // all metadata.
+ this._updates[i] = update;
+ return;
+ }
+ }
+ }
+ // Otherwise add it to the front of the list.
+ if (this._activeUpdate)
+ this._updates = [this._activeUpdate].concat(this._updates);
+ },
+
+ /**
+ * Serializes an array of updates to an XML file
+ * @param updates
+ * An array of nsIUpdate objects
+ * @param file
+ * The nsIFile object to serialize to
+ */
+ _writeUpdatesToXMLFile: function(updates, file) {
+ var fos = Components.classes["@mozilla.org/network/safe-file-output-stream;1"]
+ .createInstance(Components.interfaces.nsIFileOutputStream);
+ var modeFlags = MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE;
+ if (!file.exists())
+ file.create(nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
+ fos.init(file, modeFlags, PERMS_FILE, 0);
+
+ try {
+ var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
+ .createInstance(Components.interfaces.nsIDOMParser);
+ const EMPTY_UPDATES_DOCUMENT = " ";
+ var doc = parser.parseFromString(EMPTY_UPDATES_DOCUMENT, "text/xml");
+
+ for (var i = 0; i < updates.length; ++i) {
+ if (updates[i])
+ doc.documentElement.appendChild(updates[i].serialize(doc));
+ }
+
+ var serializer = Components.classes["@mozilla.org/xmlextras/xmlserializer;1"]
+ .createInstance(Components.interfaces.nsIDOMSerializer);
+ serializer.serializeToStream(doc.documentElement, fos, null);
+ }
+ catch (e) {
+ }
+
+ closeSafeOutputStream(fos);
+ },
+
+ /**
+ * See nsIUpdateService.idl
+ */
+ saveUpdates: function() {
+ this._writeUpdatesToXMLFile([this._activeUpdate],
+ getFile(KEY_APPDIR, [FILE_UPDATE_ACTIVE]));
+ if (this._updates) {
+ this._writeUpdatesToXMLFile(this._updates,
+ getFile(KEY_APPDIR, [FILE_UPDATES_DB]));
+ }
+ },
+
+ /**
+ * See nsISupports.idl
+ */
+ QueryInterface: function(iid) {
+ if (!iid.equals(Components.interfaces.nsIUpdateManager) &&
+ !iid.equals(Components.interfaces.nsISupports))
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ return this;
+ }
+};
+
/**
* Checker
* Checks for new Updates
@@ -1593,7 +1504,6 @@ Checker.prototype = {
updateElement.QueryInterface(Components.interfaces.nsIDOMElement);
var update = new Update(updateElement);
update.serviceURL = this.updateURL;
- LOG("Checker", "Creating service URL" + update.serviceURL);
updates.push(update);
}
@@ -2184,7 +2094,7 @@ Downloader.prototype = {
var prompter =
Components.classes["@mozilla.org/updates/update-prompt;1"].
createInstance(Components.interfaces.nsIUpdatePrompt);
- prompter.showUpdateDownloaded(null, this._update);
+ prompter.showUpdateDownloaded(this._update);
}
},
@@ -2363,7 +2273,134 @@ VersionChecker.prototype = {
}
};
-var gUpdateManager = null;
+#ifdef MOZ_XUL_APP
+/**
+ * UpdatePrompt
+ * An object which can prompt the user with information about updates, request
+ * action, etc. Embedding clients can override this component with one that
+ * invokes a native front end.
+ * @constructor
+ */
+function UpdatePrompt() {
+}
+UpdatePrompt.prototype = {
+ /**
+ * See nsIUpdateService.idl
+ */
+ checkForUpdates: function() {
+ this._showUI(null, URI_UPDATE_PROMPT_DIALOG, null, "Update:Wizard",
+ null, null);
+ },
+
+ /**
+ * See nsIUpdateService.idl
+ */
+ showUpdateAvailable: function(update) {
+ if (this._enabled) {
+ this._showUI(null, URI_UPDATE_PROMPT_DIALOG, null, "Update:Wizard",
+ "updatesavailable", update);
+ }
+ },
+
+ /**
+ * See nsIUpdateService.idl
+ */
+ showUpdateDownloaded: function(update) {
+ if (this._enabled) {
+ this._showUI(null, URI_UPDATE_PROMPT_DIALOG, null, "Update:Wizard",
+ "finishedBackground", update);
+ }
+ },
+
+ /**
+ * See nsIUpdateService.idl
+ */
+ showUpdateInstalled: function(update) {
+ var showUpdateInstalledUI = getPref("getBoolPref",
+ PREF_APP_UPDATE_SHOW_INSTALLED_UI, true);
+ if (this._enabled && showUpdateInstalledUI) {
+ this._showUI(null, URI_UPDATE_PROMPT_DIALOG, null, "Update:Wizard",
+ "installed", update);
+ }
+ },
+
+ /**
+ * See nsIUpdateService.idl
+ */
+ showUpdateError: function(update) {
+ if (this._enabled) {
+ this._showUI(null, URI_UPDATE_PROMPT_DIALOG, null, "Update:Wizard",
+ "errors", update);
+ }
+ },
+
+ /**
+ * See nsIUpdateService.idl
+ */
+ showUpdateHistory: function(parent) {
+ this._showUI(parent, URI_UPDATE_HISTORY_DIALOG, "modal", "Update:History",
+ null, null);
+ },
+
+ /**
+ * Whether or not we are enabled (i.e. not in Silent mode)
+ */
+ get _enabled() {
+ return !getPref("getBoolPref", PREF_APP_UPDATE_SILENT, false);
+ },
+
+ /**
+ * Show the Update Checking UI
+ * @param parent
+ * A parent window, can be null
+ * @param uri
+ * The URI string of the dialog to show
+ * @param name
+ * The Window Name of the dialog to show, in case it is already open
+ * and can merely be focused
+ * @param page
+ * The page of the wizard to be displayed, if one is already open.
+ * @param update
+ * An update to pass to the UI in the window arguments.
+ * Can be null
+ */
+ _showUI: function(parent, uri, features, name, page, update) {
+ var ary = null;
+ if (update) {
+ ary = Components.classes["@mozilla.org/supports-array;1"]
+ .createInstance(Components.interfaces.nsISupportsArray);
+ ary.AppendElement(update);
+ }
+
+ var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
+ .getService(Components.interfaces.nsIWindowMediator);
+ var win = wm.getMostRecentWindow(name);
+ if (win) {
+ if (page && "setCurrentPage" in win)
+ win.setCurrentPage(page);
+ win.focus();
+ }
+ else {
+ var openFeatures = "chrome,centerscreen,dialog";
+ if (features)
+ openFeatures += "," + features;
+ var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
+ .getService(Components.interfaces.nsIWindowWatcher);
+ ww.openWindow(parent, uri, "", openFeatures, ary);
+ }
+ },
+
+ /**
+ * See nsISupports.idl
+ */
+ QueryInterface: function(iid) {
+ if (!iid.equals(Components.interfaces.nsIUpdatePrompt) &&
+ !iid.equals(Components.interfaces.nsISupports))
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ return this;
+ }
+};
+#endif
var gModule = {
registerSelf: function(componentManager, fileSpec, location, type) {
@@ -2415,11 +2452,13 @@ var gModule = {
className : "Update Checker",
factory : #1#(Checker)
},
+#ifdef MOZ_XUL_APP
prompt: { CID : Components.ID("{27ABA825-35B5-4018-9FDD-F99250A0E722}"),
contractID : "@mozilla.org/updates/update-prompt;1",
className : "Update Prompt",
factory : #1#(UpdatePrompt)
},
+#endif
version: { CID : Components.ID("{9408E0A5-509E-45E7-80C1-0F35B99FF7A9}"),
contractID : "@mozilla.org/updates/version-checker;1",
className : "Version Checker",
@@ -2459,7 +2498,7 @@ function isCompatible(update) {
var em = Components.classes["@mozilla.org/extensions/manager;1"]
.getService(Components.interfaces.nsIExtensionManager);
var items = em.getIncompatibleItemList("", update.extensionVersion,
- nsIUpdateItem.TYPE_ADDON, { });
+ nsIUpdateItem.TYPE_ADDON, false, { });
return items.length == 0;
#else
return true;