clean up on restart

git-svn-id: svn://10.0.0.236/trunk@175275 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
ben%bengoodger.com
2005-06-29 01:34:36 +00:00
parent 9e28f48109
commit 2dcc022b03
14 changed files with 410 additions and 203 deletions

View File

@@ -75,13 +75,17 @@ pref("extensions.logging.enabled", false);
// Whether or not app updates are enabled
pref("app.update.enabled", false);
// This preference turns on app.update.mode and allows automatic download and
// install to take place. We use a separate boolean toggle for this to make
// the UI easier to construct.
pref("app.update.auto", true);
// Defines how the Application Update Service notifies the user about updates:
//
// AUM Set to: Minor Releases: Major Releases:
// 0 download no prompt download no prompt
// 1 download no prompt download no prompt if no incompatibilities
// 2 download no prompt prompt
// 3 prompt prompt
//
// See chart in nsUpdateService.js.in for more details
//

View File

@@ -101,19 +101,17 @@ var gAdvancedPane = {
getService(Components.interfaces.nsIApplicationUpdateService);
var checkNowButton = document.getElementById("checkNowButton");
checkNowButton.disabled = !aus.canUpdate;
this.updateAutoInstallUI();
this.updateAutoPref();
return undefined;
},
updateAutoInstallUI: function ()
updateAutoPref: function ()
{
var autoInstallPref = document.getElementById("app.update.autoInstallEnabled");
var preference = document.getElementById("app.update.auto");
var updateEnabledPref = document.getElementById("app.update.enabled");
var ids = ["autoInstallMode", "updateAnd"];
var disabled = !updateEnabledPref.value || !autoInstallPref.value;
for (var i = 0; i < ids.length; ++i)
document.getElementById(ids[i]).disabled = disabled;
var autoInstallOptions = document.getElementById("autoInstallOptions");
autoInstallOptions.disabled = !preference.value || !updateEnabledPref.value;
return undefined;
},
@@ -149,6 +147,12 @@ var gAdvancedPane = {
prompter.checkForUpdates(window);
},
showAutoInstallOptions: function ()
{
document.documentElement.openSubDialog("chrome://browser/content/preferences/update.xul",
"", null);
},
showUpdates: function ()
{
var prompter = Components.classes["@mozilla.org/updates/update-prompt;1"]

View File

@@ -58,8 +58,7 @@
<preference id="general.autoScroll" name="general.autoScroll" type="bool"/>
<preference id="general.smoothScroll" name="general.smoothScroll" type="bool"/>
<preference id="app.update.enabled" name="app.update.enabled" type="bool"/>
<preference id="app.update.autoInstallEnabled" name="app.update.autoInstallEnabled" type="bool"/>
<preference id="app.update.mode" name="app.update.mode" type="int"/>
<preference id="app.update.auto" name="app.update.auto" type="bool"/>
<preference id="extensions.update.autoUpdateEnabled"
name="extensions.update.autoUpdateEnabled"
type="bool"/>
@@ -131,34 +130,23 @@
<label>&softwareupdateinfo.label;</label>
<separator class="thin"/>
<vbox align="start">
<checkbox id="enableSmartUpdate"
label="&enableSmartUpdate.label;"
accesskey="&enableSmartUpdate.accesskey;"
<checkbox id="enableAppUpdate"
label="&enableAppUpdate.label;"
accesskey="&enableAppUpdate.accesskey;"
preference="app.update.enabled"
onsyncfrompreference="return gAdvancedPane.updateAppUpdateUI();"/>
<vbox class="indent" align="start">
<checkbox id="enableAutoInstall"
label="&enableAutoInstall.label;"
accesskey="&enableAutoInstall.accesskey;"
preference="app.update.autoInstallEnabled"
onsyncfrompreference="return gAdvancedPane.updateAutoInstallUI();"/>
<radiogroup id="autoInstallMode" preference="app.update.mode">
<grid class="indent">
<columns>
<column/>
<column flex="1"/>
</columns>
<rows>
<row><label id="updateAnd"
preference="app.update.enabled">&updateAnd.label;</label>
<radio label="&autoDownloadAndInstall.label;"
accesskey="&autoDownloadAndInstall.accesskey;" value="0"/></row>
<row><hbox/>
<radio label="&autoDownloadAndPrompt.label;"
accesskey="&autoDownloadAndPrompt.accesskey;" value="1"/></row>
</rows>
</grid>
</radiogroup>
<hbox>
<checkbox id="enableAutoInstall"
label="&enableAutoInstall.label;"
accesskey="&enableAutoInstall.accesskey;"
preference="app.update.auto"
onsyncfrompreference="return gAdvancedPane.updateAutoPref();"/>
<spacer flex="1"/>
<button id="autoInstallOptions"
label="&autoInstallOptions.label;" accesskey="&autoInstallOptions.accesskey;"
oncommand="gAdvancedPane.showAutoInstallOptions();"/>
</hbox>
<separator class="thin"/>
<hbox>
<button id="checkNowButton"
@@ -170,9 +158,7 @@
</hbox>
</vbox>
</vbox>
<separator class="thin"/>
<separator class="groove"/>
<separator class="thin"/>
<separator/>
<vbox align="start">
<checkbox id="enableExtensionUpdate"
label="&enableExtensionUpdate.label;"

View File

@@ -31,3 +31,5 @@ browser.jar:
* content/browser/preferences/sanitize.js
* content/browser/preferences/tabs.xul
* content/browser/preferences/tabs.js
* content/browser/preferences/update.xul
* content/browser/preferences/update.js

View File

@@ -0,0 +1,81 @@
<?xml version="1.0"?>
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the Firefox Preferences System.
#
# The Initial Developer of the Original Code is Google Inc.
# Portions created by the Initial Developer are Copyright (C) 2005
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Ben Goodger <ben@mozilla.org> (Original Author)
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
<!DOCTYPE prefwindow [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
%brandDTD;
<!ENTITY % updateDTD SYSTEM "chrome://browser/locale/preferences/update.dtd">
%updateDTD;
]>
<?xml-stylesheet href="chrome://global/skin/"?>
<?xml-stylesheet href="chrome://mozapps/content/update/updates.css"?>
<prefwindow id="UpdateSettingsDialog" type="child"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="&update.title;"
dlgbuttons="accept,cancel"
style="width: &window.width;; height: &window.height;;">
<prefpane id="UpdateSettingsDialogPane">
<preferences id="preferences">
<preference id="app.update.mode" name="app.update.mode" type="int"/>
</preferences>
<description>&update.intro;</description>
<radiogroup id="autoInstallMode" preference="app.update.mode">
<hbox>
<radio label="&ausMode0Name.label;" accesskey="&ausMode0Name.accesskey;"
value="0" flex="1"/>
</hbox>
<description class="updatePrefsInfo" id="ausMode0Info">&ausMode0Name.info;</description>
<hbox>
<radio label="&ausMode1Name.label;" accesskey="&ausMode1Name.accesskey;"
value="1" flex="1"/>
</hbox>
<description class="updatePrefsInfo" id="ausMode1Info">&ausMode1Name.info;</description>
<radio label="&ausMode2Name.label;" accesskey="&ausMode2Name.accesskey;"
value="2"/>
<description class="updatePrefsInfo" id="ausMode2Info">&ausMode2Name.info;</description>
</radiogroup>
<separator/>
<separator/>
</prefpane>
</prefwindow>

View File

@@ -15,15 +15,13 @@
<!ENTITY updateTab.label "Update">
<!ENTITY softwareupdateinfo.label "Periodically check for updates to:">
<!ENTITY enableSmartUpdate.label "&brandShortName;">
<!ENTITY enableSmartUpdate.accesskey "F">
<!ENTITY enableAutoInstall.label "automatically download updates">
<!ENTITY enableAutoInstall.accesskey "d">
<!ENTITY updateAnd.label "and:">
<!ENTITY autoDownloadAndInstall.label "install them">
<!ENTITY autoDownloadAndInstall.accesskey "i">
<!ENTITY autoDownloadAndPrompt.label "prompt me when ready to install">
<!ENTITY autoDownloadAndPrompt.accesskey "p">
<!ENTITY enableAppUpdate.label "&brandShortName;">
<!ENTITY enableAppUpdate.accesskey "F">
<!ENTITY enableAutoInstall.label "Automatically Download and Install updates">
<!ENTITY enableAutoInstall.accesskey "A">
<!ENTITY autoInstallOptions.label "Advanced...">
<!ENTITY autoInstallOptions.accesskey "d">
<!ENTITY enableExtensionUpdate.label "My Extensions and Themes">
<!ENTITY enableExtensionUpdate.accesskey "x">
<!ENTITY checkNow.label "Check Now...">

View File

@@ -0,0 +1,22 @@
<!ENTITY update.title "Automatic Update">
<!ENTITY window.width "30em">
<!ENTITY window.height "25em">
<!ENTITY update.intro "Automatically download and install updates for:">
<!ENTITY ausMode0Name.label "Security/Incremental Updates and New Releases, even if
some of my Extensions and Themes will stop working">
<!ENTITY ausMode0Name.accesskey "S">
<!ENTITY ausMode0Name.info "&brandShortName; will automatically download and install all
Updates and New Releases in the background">
<!ENTITY ausMode1Name.label "Security/Incremental Updates and New Releases, provided
my Extensions and Themes will still work">
<!ENTITY ausMode1Name.accesskey "e">
<!ENTITY ausMode1Name.info "&brandShortName; will ask your permission before downloading
and installing any New Release that will cause some of your
Extensions and/or Themes to stop working">
<!ENTITY ausMode2Name.label "Security/Incremental Updates only">
<!ENTITY ausMode2Name.accesskey "c">
<!ENTITY ausMode2Name.info "&brandShortName; will ask your permission before downloading
any New Release">

View File

@@ -44,6 +44,7 @@
locale/browser/preferences/privacy.dtd (%chrome/browser/preferences/privacy.dtd)
locale/browser/preferences/sanitize.dtd (%chrome/browser/preferences/sanitize.dtd)
locale/browser/preferences/tabs.dtd (%chrome/browser/preferences/tabs.dtd)
locale/browser/preferences/update.dtd (%chrome/browser/preferences/update.dtd)
locale/browser/sidebar/sidebar.properties (%chrome/browser/sidebar/sidebar.properties)
% locale browser-region @AB_CD@ %locale/browser-region/
locale/browser-region/region.properties (%chrome/browser-region/region.properties)

View File

@@ -58,11 +58,11 @@ var gUpdateHistory = {
for (var i = 0; i < uc; ++i) {
var update = um.getUpdateAt(i);
if (!update || !update.name)
continue;
var element = document.createElementNS(NS_XUL, "update");
this._view.appendChild(element);
if (!update.name)
continue;
element.name = update.name;
element.type = bundle.getString("updateType_" + update.type);
element.installDate = this._formatDate(update.installDate);

View File

@@ -189,3 +189,9 @@ update {
margin-bottom: 1px;
}
.updatePrefsInfo {
font-size: smaller;
margin-left: 27px;
margin-bottom: 10px;
}

View File

@@ -247,22 +247,27 @@ var gUpdates = {
* checkForUpdates null -- foreground
*/
get startPage() {
dump("*** GET STARTPAGE\n");
if (window.arguments) {
var arg0 = window.arguments[0];
dump("*** arg0 = " + arg0 + "\n");
if (arg0 instanceof Components.interfaces.nsIUpdate) {
// If the first argument is a nsIUpdate object, we are notifying the
// user that the background checking found an update that requires
// their permission to install, and it's ready for download.
this.setUpdate(arg0);
var p = this.update.selectedPatch;
dump("*** P = " + p + "\n");
if (p) {
switch (p.state) {
case STATE_PENDING:
this.sourceEvent = SRCEVT_BACKGROUND;
return document.getElementById("finishedBackground");
case STATE_SUCCEEDED:
dump("*** :::::: goats\n");
return document.getElementById("installed");
case STATE_FAILED:
case STATE_APPLYING:
return document.getElementById("errors");
}
}
@@ -346,7 +351,8 @@ var gUpdates = {
* etc).
*/
notify: function(timerCallback) {
this._prompter[methodName](this._update);
if (methodName in this._prompter)
this._prompter[methodName](null, this._update);
}
}
tm.registerTimer(timerID, (new Callback(gUpdates.update, methodName)),
@@ -596,7 +602,6 @@ var gLicensePage = {
nextButton.disabled = true;
nextButton.label = gUpdates.strings.getString("IAgreeLabel");
gUpdates.wiz.getButton("back").disabled = true;
gUpdates.wiz.getButton("next").focus();
var cancelButton = gUpdates.wiz.getButton("cancel");
cancelButton.label = gUpdates.strings.getString("IDoNotAgreeLabel");
@@ -612,6 +617,7 @@ var gLicensePage = {
// Now that the license text is available, the user is in a position to
// agree to it, so enable the Agree button.
gUpdates.wiz.getButton("next").disabled = false;
gUpdates.wiz.getButton("next").focus();
},
/**

View File

@@ -129,6 +129,11 @@ interface nsIUpdate : nsISupports
*/
attribute AString licenseURL;
/**
* The URL to the Update Service that supplied this update.
*/
attribute AString serviceURL;
/**
* Whether or not the update being downloaded is a complete replacement of
* the user's existing installation or a patch representing the difference
@@ -151,6 +156,11 @@ interface nsIUpdate : nsISupports
*/
readonly attribute nsIUpdatePatch selectedPatch;
/**
* The state of the selected patch.
*/
attribute AString state;
/**
* The number of patches supplied by this update.
*/
@@ -372,27 +382,25 @@ interface nsIApplicationUpdateService : nsISupports
interface nsIUpdateManager : nsISupports
{
/**
*
* Gets the update at the specified index
* @param index
* The index within the updates array
* @returns The nsIUpdate object at the specified index
*/
nsIUpdate getUpdateAt(in long index);
/**
*
* Gets the total number of updates in the history list.
*/
readonly attribute long updateCount;
/**
*
* The active (current) update. The active update is not in the history list.
*/
attribute nsIUpdate activeUpdate;
/**
*
*/
void removeUpdateAtIndex(in long index);
/**
*
* Saves all updates to disk.
*/
void saveUpdates();
};
@@ -400,22 +408,22 @@ interface nsIUpdateManager : nsISupports
[scriptable, uuid(0765c92c-6145-4253-9db4-594d8023087e)]
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);
/**
* 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);
};
[scriptable, uuid(22d35700-5765-42e1-914b-a0da7c911a8c)]

View File

@@ -37,6 +37,7 @@
* ***** END LICENSE BLOCK ***** */
const PREF_APP_UPDATE_ENABLED = "app.update.enabled";
const PREF_APP_UPDATE_AUTO = "app.update.auto";
const PREF_APP_UPDATE_MODE = "app.update.mode";
const PREF_APP_UPDATE_SILENT = "app.update.silent";
const PREF_APP_UPDATE_INTERVAL = "app.update.interval";
@@ -482,6 +483,7 @@ UpdatePrompt.prototype = {
*/
_showUI: function(parent, uri, features, name, update) {
var ary = null;
dump("*** UUUU = " + update + "\n");
if (update) {
ary = Components.classes["@mozilla.org/supports-array;1"]
.createInstance(Components.interfaces.nsISupportsArray);
@@ -536,47 +538,6 @@ function UpdateService() {
// Observe xpcom-shutdown to unhook pref branch observers above to avoid
// shutdown leaks.
gOS.addObserver(this, "xpcom-shutdown", false);
/*
// 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 update = um.activeUpdate;
if (!update)
update = new Update(null);
if (status == STATE_SUCCEEDED) {
update.statusText = "Install Succeeded";
//prompter.showUpdateInstalled(update);
}
else {
// Something went wrong with the patch application process.
update.statusText = "goats";
// XXXben todo: synthesize the best message
//prompter.showUpdateError(update);
}
// 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();
}
*/
this._initLoggingPrefs();
}
UpdateService.prototype = {
@@ -584,17 +545,13 @@ UpdateService.prototype = {
observe: function(subject, topic, data) {
switch (topic) {
case "app-startup":
// 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 "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"]
@@ -602,7 +559,13 @@ UpdateService.prototype = {
var interval = getPref("getIntPref", PREF_APP_UPDATE_INTERVAL, 86400000);
tm.registerTimer("background-update-timer", this, interval);
this._initLoggingPrefs();
// 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");
@@ -610,6 +573,55 @@ UpdateService.prototype = {
}
},
/**
*
*/
_postUpdateCleanup: function() {
// 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 update = um.activeUpdate;
if (!update)
update = new Update(null);
update.state = status;
if (status == STATE_SUCCEEDED) {
update.statusText = "Install Succeeded";
LOG("Downloader", "STATE = " + update.selectedPatch.state);
LOG("Downloader", "_postUpdateCleanup: Install Succeeded, Showing UI");
prompter.showUpdateInstalled(update);
}
else {
// Something went wrong with the patch application process.
update.statusText = "goats";
// XXXben todo: synthesize the best message
LOG("Downloader", "_postUpdateCleanup: Install Failed, Showing UI");
prompter.showUpdateError(update);
}
// 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();
}
else {
LOG("Downloader", "_postUpdateCleanup: No Status, No Update");
}
},
/**
* Initialize Logging preferences, formatted like so:
* app.update.log.<moduleName> = <true|false>
@@ -704,13 +716,11 @@ UpdateService.prototype = {
// Major 1 No Auto Install
// Major 1 Yes Notify and Confirm
// Major 2 Yes or No Notify and Confirm
// Major 3 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
// Minor 3 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.
@@ -719,30 +729,49 @@ UpdateService.prototype = {
// at all, and so none of the decision making above is entered into.
//
update.QueryInterface(Components.interfaces.nsIPropertyBag);
var licenseAccepted = update.getProperty("licenseAccepted") == "true";
if (update.licenseURL && !licenseAccepted)
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)
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 0:
// Mode 0 is do not prompt regardless of incompatibilities
return false;
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
if (update.type == "minor")
return !isCompatible(update);
LOG("Checker", "_shouldPrompt: Prompting if major or there are " +
"incompatibilities");
return update.type == "minor" ? !isCompatible(update) : true;
}
// Mode 3 is prompt, regardless.
return true;
// Mode 0 is do not prompt regardless of incompatibilities
LOG("Checker", "_shouldPrompt: Not prompting the user - they choose to " +
"ignore incompatibilities");
return false;
},
/**
@@ -781,6 +810,14 @@ UpdateService.prototype = {
* 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;
@@ -789,7 +826,7 @@ UpdateService.prototype = {
var prompter =
Components.classes["@mozilla.org/updates/update-prompt;1"].
createInstance(Components.interfaces.nsIUpdatePrompt);
prompter.showUpdateAvailable(update);
prompter.showUpdateAvailable(null, update);
} else {
this.downloadUpdate(update, true);
}
@@ -900,6 +937,11 @@ UpdateService.prototype = {
* @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 = {
/**
@@ -920,9 +962,11 @@ UpdateManager.prototype = {
* @returns The array of nsIUpdate items held in the file.
*/
_loadXMLFileIntoArray: function(file) {
if (!file.exists())
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);
@@ -949,6 +993,8 @@ UpdateManager.prototype = {
}
}
catch (e) {
LOG("UpdateManager", "_loadXMLFileIntoArray: Error constructing update list " +
e);
}
fileStream.close();
return result;
@@ -961,21 +1007,8 @@ UpdateManager.prototype = {
if (!this._updates)
this._updates = this._loadXMLFileIntoArray(getFile(KEY_APPDIR,
[FILE_UPDATES_DB]));
this._ensureActiveUpdate();
},
/**
* Ensure that the Active Update file is loaded.
*/
_ensureActiveUpdate: function() {
if (!this._activeUpdate) {
var updates = this._loadXMLFileIntoArray(getFile(KEY_APPDIR,
[FILE_UPDATE_ACTIVE]));
if (updates.length > 0)
this._activeUpdate = updates[0];
}
},
/**
* See nsIUpdateService.idl
*/
@@ -996,12 +1029,18 @@ UpdateManager.prototype = {
* See nsIUpdateService.idl
*/
get activeUpdate() {
this._ensureActiveUpdate();
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._ensureActiveUpdate();
if (!activeUpdate)
this._addUpdate(this._activeUpdate);
this._activeUpdate = activeUpdate;
if (!activeUpdate) {
@@ -1015,14 +1054,6 @@ UpdateManager.prototype = {
return activeUpdate;
},
/**
* See nsIUpdateService.idl
*/
removeUpdateAtIndex: function(index) {
this._ensureUpdates();
tihs._updates.splice(index, 1);
},
/**
* Add an update to the Updates list. If the item already exists in the list,
* replace the existing value with the new value.
@@ -1041,7 +1072,8 @@ UpdateManager.prototype = {
}
}
// Otherwise add it to the front of the list.
this._updates = [this._activeUpdate].concat(this._updates);
if (this._activeUpdate)
this._updates = [this._activeUpdate].concat(this._updates);
},
/**
@@ -1133,7 +1165,8 @@ function getPref(func, preference, defaultValue) {
*/
function UpdatePatch(patch) {
for (var i = 0; i < patch.attributes.length; ++i) {
var attr = patch.attributes[i];
var attr = patch.attributes.item(i);
attr.QueryInterface(Components.interfaces.nsIDOMAttr);
switch (attr.name) {
case "selected":
this.selected = attr.value == "true";
@@ -1249,19 +1282,14 @@ function Update(update) {
}
for (var i = 0; i < update.attributes.length; ++i) {
var attr = update.attributes[i];
switch (attr.name) {
case "installDate":
if (attr.value)
this.installDate = parseInt(attr.value);
break;
case "isCompleteUpdate":
var attr = update.attributes.item(i);
attr.QueryInterface(Components.interfaces.nsIDOMAttr);
if (attr.name == "installDate" && attr.value)
this.installDate = parseInt(attr.value);
else if (attr.name == "isCompleteUpdate")
this.isCompleteUpdate = attr.value == "true";
break;
default:
else
this[attr.name] = attr.value;
break;
};
}
// The Update Name is either the string provided by the <update> element, or
@@ -1294,7 +1322,27 @@ Update.prototype = {
getPatchAt: function(index) {
return this._patches[index];
},
/**
* See nsIUpdateService.idl
*/
_state: "",
set state(state) {
if (this.selectedPatch)
this.selectedPatch.state = state;
else
this._state = state;
return state;
},
get state() {
if (this.selectedPatch)
return this.selectedPatch.state;
return this._state;
},
/**
* See nsIUpdateService.idl
*/
get selectedPatch() {
for (var i = 0; i < this.patchCount; ++i) {
if (this._patches[i].selected)
@@ -1314,6 +1362,7 @@ Update.prototype = {
update.setAttribute("extensionVersion", this.extensionVersion);
update.setAttribute("detailsURL", this.detailsURL);
update.setAttribute("licenseURL", this.licenseURL);
update.setAttribute("serviceURL", this.serviceURL);
update.setAttribute("installDate", this.installDate);
update.setAttribute("statusText", this.statusText);
update.setAttribute("isCompleteUpdate", this.isCompleteUpdate);
@@ -1407,7 +1456,7 @@ Checker.prototype = {
* The URL of the update service XML file to connect to that contains details
* about available updates.
*/
get _updateURL() {
get updateURL() {
var url;
try {
// Use the override URL if specified.
@@ -1446,13 +1495,13 @@ Checker.prototype = {
if (!listener)
throw Components.results.NS_ERROR_NULL_POINTER;
if (!this._updateURL || (!this.enabled && !force))
if (!this.updateURL || (!this.enabled && !force))
return;
this._request =
Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].
createInstance(Components.interfaces.nsIXMLHttpRequest);
this._request.open("GET", this._updateURL, true);
this._request.open("GET", this.updateURL, true);
this._request.overrideMimeType("text/xml");
this._request.setRequestHeader("Cache-Control", "no-cache");
@@ -1461,7 +1510,7 @@ Checker.prototype = {
this._request.onload = function(event) { self.onLoad(event); };
this._request.onprogress = function(event) { self.onProgress(event); };
LOG("Checker", "checkForUpdates: sending request to " + this._updateURL);
LOG("Checker", "checkForUpdates: sending request to " + this.updateURL);
this._request.send(null);
this._callback = listener;
@@ -1500,7 +1549,10 @@ Checker.prototype = {
continue;
updateElement.QueryInterface(Components.interfaces.nsIDOMElement);
updates.push(new Update(updateElement));
var update = new Update(updateElement);
update.serviceURL = this.updateURL;
LOG("Checker", "Creating service URL" + update.serviceURL);
updates.push(update);
}
return updates;
@@ -1856,12 +1908,16 @@ Downloader.prototype = {
},
/**
*
* An array of download listeners to notify when we receive
* nsIRequestObserver or nsIProgressEventSink method calls.
*/
_listeners: [],
/**
*
* Adds a listener to the download process
* @param listener
* A download listener, implementing nsIRequestObserver and
* nsIProgressEventSink
*/
addDownloadListener: function(listener) {
for (var i = 0; i < this._listeners.length; ++i) {
@@ -1872,7 +1928,9 @@ Downloader.prototype = {
},
/**
*
* Removes a download listener
* @param listener
* The listener to remove.
*/
removeDownloadListener: function(listener) {
for (var i = 0; i < this._listeners.length; ++i) {
@@ -1884,7 +1942,11 @@ Downloader.prototype = {
},
/**
*
* When the async request begins
* @param request
* The nsIRequest object for the transfer
* @param context
* Additional data
*/
onStartRequest: function(request, context) {
request.QueryInterface(nsIIncrementalDownload);
@@ -1895,8 +1957,16 @@ Downloader.prototype = {
this._listeners[i].onStartRequest(request, context);
},
/**
*
/**
* When new data has been downloaded
* @param request
* The nsIRequest object for the transfer
* @param context
* Additional data
* @param progress
* The current number of bytes transferred
* @param maxProgress
* The total number of bytes that must be transferred
*/
onProgress: function(request, context, progress, maxProgress) {
request.QueryInterface(nsIIncrementalDownload);
@@ -1910,8 +1980,16 @@ Downloader.prototype = {
}
},
/**
*
/**
* When we have new status text
* @param request
* The nsIRequest object for the transfer
* @param context
* Additional data
* @param status
* A status code
* @param statusText
* Human readable version of |status|
*/
onStatus: function(request, context, status, statusText) {
request.QueryInterface(nsIIncrementalDownload);
@@ -1924,8 +2002,14 @@ Downloader.prototype = {
}
},
/**
*
/**
* When data transfer ceases
* @param request
* The nsIRequest object for the transfer
* @param context
* Additional data
* @param status
* Status code containing the reason for the cessation.
*/
onStopRequest: function(request, context, status) {
request.QueryInterface(nsIIncrementalDownload);
@@ -1947,6 +2031,7 @@ Downloader.prototype = {
shouldShowPrompt = true;
// Tell the updater.exe we're ready to apply.
dump("*** WRITING STATUS = " + state);
this._writeStatusFile(getUpdatesDir(), state);
this._update.installDate = (new Date()).getTime();
this._update.statusText = "Install Pending";
@@ -1995,6 +2080,7 @@ Downloader.prototype = {
deleteActiveUpdate = true;
}
LOG("Downloader", "Setting state to: " + state);
this._patch.state = state;
var um =
Components.classes["@mozilla.org/updates/update-manager;1"].
@@ -2030,7 +2116,7 @@ Downloader.prototype = {
var prompter =
Components.classes["@mozilla.org/updates/update-prompt;1"].
createInstance(Components.interfaces.nsIUpdatePrompt);
prompter.showUpdateDownloaded(this._update);
prompter.showUpdateDownloaded(null, this._update);
}
},
@@ -2076,6 +2162,7 @@ TimerManager.prototype = {
* The checking timer that fired.
*/
notify: function(timer) {
dump("*** STATUS = " + readStatusFile(getUpdatesDir()) + "\n");
for (var timerID in this._timers) {
var timerData = this._timers[timerID];
var lastUpdateTime = timerData.lastUpdateTime;
@@ -2209,6 +2296,8 @@ VersionChecker.prototype = {
}
};
var gUpdateManager = null;
var gModule = {
registerSelf: function(componentManager, fileSpec, location, type) {
componentManager = componentManager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
@@ -2259,11 +2348,6 @@ var gModule = {
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",
factory : #1#(UpdateManager)
},
prompt: { CID : Components.ID("{27ABA825-35B5-4018-9FDD-F99250A0E722}"),
contractID : "@mozilla.org/updates/update-prompt;1",
className : "Update Prompt",
@@ -2278,7 +2362,12 @@ var gModule = {
contractID : "@mozilla.org/updates/timer-manager;1",
className : "Timer Manager",
factory : #1#(TimerManager)
}
},
manager: { CID : Components.ID("{093C2356-4843-4C65-8709-D7DBCBBE7DFB}"),
contractID : "@mozilla.org/updates/update-manager;1",
className : "Update Manager",
factory : #1#(UpdateManager)
},
},
canUnload: function(componentManager) {