mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
1822 lines
61 KiB
JavaScript
1822 lines
61 KiB
JavaScript
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* 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 Update Service.
|
|
*
|
|
* 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)
|
|
* Asaf Romano <mozilla.mano@sent.com>
|
|
* Jeff Walden <jwalden+code@mit.edu>
|
|
* Robert Strong <robert.bugzilla@gmail.com>
|
|
*
|
|
* 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 ***** */
|
|
|
|
Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
|
|
Components.utils.import("resource://gre/modules/AddonManager.jsm");
|
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
|
|
|
// Firefox's macBrowserOverlay.xul includes scripts that define Cc, Ci, and Cr
|
|
// so we have to use different names.
|
|
const CoC = Components.classes;
|
|
const CoI = Components.interfaces;
|
|
const CoR = Components.results;
|
|
|
|
const XMLNS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
|
|
|
const PREF_APP_UPDATE_BACKGROUNDERRORS = "app.update.backgroundErrors";
|
|
const PREF_APP_UPDATE_BILLBOARD_TEST_URL = "app.update.billboard.test_url";
|
|
const PREF_APP_UPDATE_CERT_ERRORS = "app.update.cert.errors";
|
|
const PREF_APP_UPDATE_ENABLED = "app.update.enabled";
|
|
const PREF_APP_UPDATE_LOG = "app.update.log";
|
|
const PREF_APP_UPDATE_MANUAL_URL = "app.update.url.manual";
|
|
const PREF_APP_UPDATE_NEVER_BRANCH = "app.update.never.";
|
|
const PREF_APP_UPDATE_TEST_LOOP = "app.update.test.loop";
|
|
const PREF_PLUGINS_UPDATEURL = "plugins.update.url";
|
|
|
|
const PREF_EM_HOTFIX_ID = "extensions.hotfix.id";
|
|
|
|
const UPDATE_TEST_LOOP_INTERVAL = 2000;
|
|
|
|
const URI_UPDATES_PROPERTIES = "chrome://mozapps/locale/update/updates.properties";
|
|
|
|
const STATE_DOWNLOADING = "downloading";
|
|
const STATE_PENDING = "pending";
|
|
const STATE_PENDING_SVC = "pending-service";
|
|
const STATE_APPLYING = "applying";
|
|
const STATE_SUCCEEDED = "succeeded";
|
|
const STATE_DOWNLOAD_FAILED = "download-failed";
|
|
const STATE_FAILED = "failed";
|
|
|
|
const SRCEVT_FOREGROUND = 1;
|
|
const SRCEVT_BACKGROUND = 2;
|
|
|
|
const CERT_ATTR_CHECK_FAILED_NO_UPDATE = 100;
|
|
const CERT_ATTR_CHECK_FAILED_HAS_UPDATE = 101;
|
|
const BACKGROUNDCHECK_MULTIPLE_FAILURES = 110;
|
|
|
|
var gLogEnabled = false;
|
|
var gUpdatesFoundPageId;
|
|
|
|
// Notes:
|
|
// 1. use the wizard's goTo method whenever possible to change the wizard
|
|
// page since it is simpler than most other methods and behaves nicely with
|
|
// mochitests.
|
|
// 2. using a page's onPageShow method to then change to a different page will
|
|
// of course call that page's onPageShow method which can make mochitests
|
|
// overly complicated and fragile so avoid doing this if at all possible.
|
|
// This is why a page's next attribute is set prior to the page being shown
|
|
// whenever possible.
|
|
|
|
/**
|
|
* Logs a string to the error console.
|
|
* @param string
|
|
* The string to write to the error console..
|
|
*/
|
|
function LOG(module, string) {
|
|
if (gLogEnabled) {
|
|
dump("*** AUS:UI " + module + ":" + string + "\n");
|
|
Services.console.logStringMessage("AUS:UI " + module + ":" + string);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Opens a URL using the event target's url attribute for the URL. This is a
|
|
* workaround for Bug 263433 which prevents respecting tab browser preferences
|
|
* for where to open a URL.
|
|
*/
|
|
function openUpdateURL(event) {
|
|
if (event.button == 0)
|
|
openURL(event.target.getAttribute("url"));
|
|
}
|
|
|
|
/**
|
|
* 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 Services.prefs[func](preference);
|
|
}
|
|
catch (e) {
|
|
LOG("General", "getPref - failed to get preference: " + preference);
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
/**
|
|
* A set of shared data and control functions for the wizard as a whole.
|
|
*/
|
|
var gUpdates = {
|
|
/**
|
|
* The nsIUpdate object being used by this window (either for downloading,
|
|
* notification or both).
|
|
*/
|
|
update: null,
|
|
|
|
/**
|
|
* List of incompatible add-ons
|
|
*/
|
|
addons: [],
|
|
|
|
/**
|
|
* The updates.properties <stringbundle> element.
|
|
*/
|
|
strings: null,
|
|
|
|
/**
|
|
* The Application brandShortName (e.g. "Firefox")
|
|
*/
|
|
brandName: null,
|
|
|
|
/**
|
|
* The <wizard> element
|
|
*/
|
|
wiz: null,
|
|
|
|
/**
|
|
* Whether to run the unload handler. This will be set to false when the user
|
|
* exits the wizard via onWizardCancel or onWizardFinish.
|
|
*/
|
|
_runUnload: true,
|
|
|
|
/**
|
|
* Helper function for setButtons
|
|
* Resets button to original label & accesskey if string is null.
|
|
*/
|
|
_setButton: function(button, string) {
|
|
if (string) {
|
|
var label = this.getAUSString(string);
|
|
if (label.indexOf("%S") != -1)
|
|
label = label.replace(/%S/, this.brandName);
|
|
button.label = label;
|
|
button.setAttribute("accesskey",
|
|
this.getAUSString(string + ".accesskey"));
|
|
} else {
|
|
button.label = button.defaultLabel;
|
|
button.setAttribute("accesskey", button.defaultAccesskey);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Sets the attributes needed for this Wizard's control buttons (labels,
|
|
* disabled, hidden, etc.)
|
|
* @param extra1ButtonString
|
|
* The property in the stringbundle containing the label to put on
|
|
* the first extra button, or null to hide the first extra button.
|
|
* @param extra2ButtonString
|
|
* The property in the stringbundle containing the label to put on
|
|
* the second extra button, or null to hide the second extra button.
|
|
* @param nextFinishButtonString
|
|
* The property in the stringbundle containing the label to put on
|
|
* the Next / Finish button, or null to hide the button. The Next and
|
|
* Finish buttons are never displayed at the same time in a wizard
|
|
* with the the Finish button only being displayed when there are no
|
|
* additional pages to display in the wizard.
|
|
* @param canAdvance
|
|
* true if the wizard can be advanced (e.g. the next / finish button
|
|
* should be enabled), false otherwise.
|
|
* @param showCancel
|
|
* true if the wizard's cancel button should be shown, false
|
|
* otherwise. If not specified this will default to false.
|
|
*
|
|
* Note:
|
|
* Per Bug 324121 the wizard should not look like a wizard and to accomplish
|
|
* this the back button is never displayed and the cancel button is only
|
|
* displayed for the checking and the incompatibleCheck pages. This causes the
|
|
* wizard buttons to be arranged as follows on Windows with the next and
|
|
* finish buttons never being displayed at the same time.
|
|
* +--------------------------------------------------------------+
|
|
* | [ extra1 ] [ extra2 ] [ next or finish ] |
|
|
* +--------------------------------------------------------------+
|
|
*/
|
|
setButtons: function(extra1ButtonString, extra2ButtonString,
|
|
nextFinishButtonString, canAdvance, showCancel) {
|
|
this.wiz.canAdvance = canAdvance;
|
|
|
|
var bnf = this.wiz.getButton(this.wiz.onLastPage ? "finish" : "next");
|
|
var be1 = this.wiz.getButton("extra1");
|
|
var be2 = this.wiz.getButton("extra2");
|
|
var bc = this.wiz.getButton("cancel");
|
|
|
|
// Set the labels for the next / finish, extra1, and extra2 buttons
|
|
this._setButton(bnf, nextFinishButtonString);
|
|
this._setButton(be1, extra1ButtonString);
|
|
this._setButton(be2, extra2ButtonString);
|
|
|
|
bnf.hidden = bnf.disabled = !nextFinishButtonString;
|
|
be1.hidden = be1.disabled = !extra1ButtonString;
|
|
be2.hidden = be2.disabled = !extra2ButtonString;
|
|
bc.hidden = bc.disabled = !showCancel;
|
|
|
|
// Hide and disable the back button each time setButtons is called
|
|
// (see bug 464765).
|
|
var btn = this.wiz.getButton("back");
|
|
btn.hidden = btn.disabled = true;
|
|
|
|
// Hide and disable the finish button if not on the last page or the next
|
|
// button if on the last page each time setButtons is called.
|
|
btn = this.wiz.getButton(this.wiz.onLastPage ? "next" : "finish");
|
|
btn.hidden = btn.disabled = true;
|
|
},
|
|
|
|
getAUSString: function(key, strings) {
|
|
if (strings)
|
|
return this.strings.getFormattedString(key, strings);
|
|
return this.strings.getString(key);
|
|
},
|
|
|
|
never: function () {
|
|
// If the user clicks "No Thanks", we should not prompt them to update to
|
|
// this version again unless they manually select "Check for Updates..."
|
|
// which will clear all of the "never" prefs.
|
|
var neverPrefName = PREF_APP_UPDATE_NEVER_BRANCH + this.update.appVersion;
|
|
Services.prefs.setBoolPref(neverPrefName, true);
|
|
},
|
|
|
|
/**
|
|
* A hash of |pageid| attribute to page object. Can be used to dispatch
|
|
* function calls to the appropriate page.
|
|
*/
|
|
_pages: { },
|
|
|
|
/**
|
|
* Called when the user presses the "Finish" button on the wizard, dispatches
|
|
* the function call to the selected page.
|
|
*/
|
|
onWizardFinish: function() {
|
|
this._runUnload = false;
|
|
var pageid = document.documentElement.currentPage.pageid;
|
|
if ("onWizardFinish" in this._pages[pageid])
|
|
this._pages[pageid].onWizardFinish();
|
|
},
|
|
|
|
/**
|
|
* Called when the user presses the "Cancel" button on the wizard, dispatches
|
|
* the function call to the selected page.
|
|
*/
|
|
onWizardCancel: function() {
|
|
this._runUnload = false;
|
|
var pageid = document.documentElement.currentPage.pageid;
|
|
if ("onWizardCancel" in this._pages[pageid])
|
|
this._pages[pageid].onWizardCancel();
|
|
},
|
|
|
|
/**
|
|
* Called when the user presses the "Next" button on the wizard, dispatches
|
|
* the function call to the selected page.
|
|
*/
|
|
onWizardNext: function() {
|
|
var cp = document.documentElement.currentPage;
|
|
if (!cp)
|
|
return;
|
|
var pageid = cp.pageid;
|
|
if ("onWizardNext" in this._pages[pageid])
|
|
this._pages[pageid].onWizardNext();
|
|
},
|
|
|
|
/**
|
|
* The checking process that spawned this update UI. There are two types:
|
|
* SRCEVT_FOREGROUND:
|
|
* Some user-generated event caused this UI to appear, e.g. the Help
|
|
* menu item or the button in preferences. When in this mode, the UI
|
|
* should remain active for the duration of the download.
|
|
* SRCEVT_BACKGROUND:
|
|
* A background update check caused this UI to appear, probably because
|
|
* incompatibilities in Extensions or other addons were discovered and
|
|
* the user's consent to continue was required. When in this mode, the
|
|
* UI will disappear after the user's consent is obtained.
|
|
*/
|
|
sourceEvent: SRCEVT_FOREGROUND,
|
|
|
|
/**
|
|
* Helper function for onLoad
|
|
* Saves default button label & accesskey for use by _setButton
|
|
*/
|
|
_cacheButtonStrings: function (buttonName) {
|
|
var button = this.wiz.getButton(buttonName);
|
|
button.defaultLabel = button.label;
|
|
button.defaultAccesskey = button.getAttribute("accesskey");
|
|
},
|
|
|
|
/**
|
|
* Called when the wizard UI is loaded.
|
|
*/
|
|
onLoad: function() {
|
|
this.wiz = document.documentElement;
|
|
|
|
gLogEnabled = getPref("getBoolPref", PREF_APP_UPDATE_LOG, false)
|
|
|
|
this.strings = document.getElementById("updateStrings");
|
|
var brandStrings = document.getElementById("brandStrings");
|
|
this.brandName = brandStrings.getString("brandShortName");
|
|
|
|
var pages = this.wiz.childNodes;
|
|
for (var i = 0; i < pages.length; ++i) {
|
|
var page = pages[i];
|
|
if (page.localName == "wizardpage")
|
|
this._pages[page.pageid] = eval(page.getAttribute("object"));
|
|
}
|
|
|
|
// Cache the standard button labels in case we need to restore them
|
|
this._cacheButtonStrings("next");
|
|
this._cacheButtonStrings("finish");
|
|
this._cacheButtonStrings("extra1");
|
|
this._cacheButtonStrings("extra2");
|
|
|
|
// Advance to the Start page.
|
|
this.getStartPageID(function(startPageID) {
|
|
LOG("gUpdates", "onLoad - setting current page to startpage " + startPageID);
|
|
gUpdates.wiz.currentPage = document.getElementById(startPageID);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Called when the wizard UI is unloaded.
|
|
*/
|
|
onUnload: function() {
|
|
if (this._runUnload) {
|
|
var cp = this.wiz.currentPage;
|
|
if (cp.pageid != "finished" && cp.pageid != "finishedBackground")
|
|
this.onWizardCancel();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Gets the ID of the <wizardpage> object that should be displayed first. This
|
|
* is an asynchronous method that passes the resulting object to a callback
|
|
* function.
|
|
*
|
|
* This is determined by how we were called by the update prompt:
|
|
*
|
|
* Prompt Method: Arg0: Update State: Src Event: Failed: Result:
|
|
* showUpdateAvailable nsIUpdate obj -- background -- see Note below
|
|
* showUpdateDownloaded nsIUpdate obj pending background -- finishedBackground
|
|
* showUpdateInstalled "installed" -- -- -- installed
|
|
* showUpdateError nsIUpdate obj failed either partial errorpatching
|
|
* showUpdateError nsIUpdate obj failed either complete errors
|
|
* checkForUpdates null -- foreground -- checking
|
|
* checkForUpdates null downloading foreground -- downloading
|
|
*
|
|
* Note: the page returned (e.g. Result) for showUpdateAvaulable is as follows:
|
|
* New enabled incompatible add-ons : incompatibleCheck page
|
|
* No new enabled incompatible add-ons: either updatesfoundbasic or
|
|
* updatesfoundbillboard as determined by
|
|
* updatesFoundPageId
|
|
* @param aCallback
|
|
* A callback to pass the <wizardpage> object to be displayed first to.
|
|
*/
|
|
getStartPageID: function(aCallback) {
|
|
if ("arguments" in window && window.arguments[0]) {
|
|
var arg0 = window.arguments[0];
|
|
if (arg0 instanceof CoI.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);
|
|
if (this.update.errorCode == CERT_ATTR_CHECK_FAILED_NO_UPDATE ||
|
|
this.update.errorCode == CERT_ATTR_CHECK_FAILED_HAS_UPDATE ||
|
|
this.update.errorCode == BACKGROUNDCHECK_MULTIPLE_FAILURES) {
|
|
aCallback("errorextra");
|
|
return;
|
|
}
|
|
|
|
var p = this.update.selectedPatch;
|
|
if (p) {
|
|
var state = p.state;
|
|
var patchFailed;
|
|
try {
|
|
patchFailed = this.update.getProperty("patchingFailed");
|
|
}
|
|
catch (e) {
|
|
}
|
|
if (patchFailed) {
|
|
if (patchFailed == "partial" && this.update.patchCount == 2) {
|
|
// If the system failed to apply the partial patch, show the
|
|
// screen which best describes this condition, which is triggered
|
|
// by the |STATE_FAILED| state.
|
|
state = STATE_FAILED;
|
|
}
|
|
else {
|
|
// Otherwise, if the complete patch failed, which is far less
|
|
// likely, show the error text held by the update object in the
|
|
// generic errors page, triggered by the |STATE_DOWNLOAD_FAILED|
|
|
// state.
|
|
state = STATE_DOWNLOAD_FAILED;
|
|
}
|
|
}
|
|
|
|
// Now select the best page to start with, given the current state of
|
|
// the Update.
|
|
switch (state) {
|
|
case STATE_PENDING:
|
|
case STATE_PENDING_SVC:
|
|
this.sourceEvent = SRCEVT_BACKGROUND;
|
|
aCallback("finishedBackground");
|
|
return;
|
|
case STATE_DOWNLOADING:
|
|
aCallback("downloading");
|
|
return;
|
|
case STATE_FAILED:
|
|
window.getAttention();
|
|
aCallback("errorpatching");
|
|
return;
|
|
case STATE_DOWNLOAD_FAILED:
|
|
case STATE_APPLYING:
|
|
aCallback("errors");
|
|
return;
|
|
}
|
|
}
|
|
if (this.update.licenseURL)
|
|
this.wiz.getPageById(this.updatesFoundPageId).setAttribute("next", "license");
|
|
|
|
var self = this;
|
|
this.getShouldCheckAddonCompatibility(function(shouldCheck) {
|
|
if (shouldCheck) {
|
|
var incompatCheckPage = document.getElementById("incompatibleCheck");
|
|
incompatCheckPage.setAttribute("next", self.updatesFoundPageId);
|
|
aCallback(incompatCheckPage.id);
|
|
}
|
|
else {
|
|
aCallback(self.updatesFoundPageId);
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
else if (arg0 == "installed") {
|
|
aCallback("installed");
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
var um = CoC["@mozilla.org/updates/update-manager;1"].
|
|
getService(CoI.nsIUpdateManager);
|
|
if (um.activeUpdate) {
|
|
this.setUpdate(um.activeUpdate);
|
|
aCallback("downloading");
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Provide the ability to test the billboard html
|
|
var billboardTestURL = getPref("getCharPref", PREF_APP_UPDATE_BILLBOARD_TEST_URL, null);
|
|
if (billboardTestURL) {
|
|
var updatesFoundBillboardPage = document.getElementById("updatesfoundbillboard");
|
|
updatesFoundBillboardPage.setAttribute("next", "dummy");
|
|
gUpdatesFoundBillboardPage.onExtra1 = function(){ gUpdates.wiz.cancel(); };
|
|
gUpdatesFoundBillboardPage.onExtra2 = function(){ gUpdates.wiz.cancel(); };
|
|
this.onWizardNext = function() { gUpdates.wiz.cancel(); };
|
|
this.update = { billboardURL : billboardTestURL,
|
|
brandName : this.brandName,
|
|
displayVersion : "Billboard Test 1.0",
|
|
showNeverForVersion : true,
|
|
type : "major" };
|
|
aCallback(updatesFoundBillboardPage.id);
|
|
}
|
|
else {
|
|
aCallback("checking");
|
|
}
|
|
},
|
|
|
|
getShouldCheckAddonCompatibility: function(aCallback) {
|
|
// this early return should never happen
|
|
if (!this.update) {
|
|
aCallback(false);
|
|
return;
|
|
}
|
|
|
|
if (!this.update.appVersion ||
|
|
Services.vc.compare(this.update.appVersion, Services.appinfo.version) == 0) {
|
|
aCallback(false);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
var hotfixID = Services.prefs.getCharPref(PREF_EM_HOTFIX_ID);
|
|
}
|
|
catch (e) { }
|
|
|
|
var self = this;
|
|
AddonManager.getAllAddons(function(addons) {
|
|
self.addons = [];
|
|
addons.forEach(function(addon) {
|
|
// Protect against code that overrides the add-ons manager and doesn't
|
|
// implement the isCompatibleWith or the findUpdates method.
|
|
if (!("isCompatibleWith" in addon) || !("findUpdates" in addon)) {
|
|
let errMsg = "Add-on doesn't implement either the isCompatibleWith " +
|
|
"or the findUpdates method!";
|
|
if (addon.id)
|
|
errMsg += " Add-on ID: " + addon.id;
|
|
Components.utils.reportError(errMsg);
|
|
return;
|
|
}
|
|
|
|
// If an add-on isn't appDisabled and isn't userDisabled then it is
|
|
// either active now or the user expects it to be active after the
|
|
// restart. If that is the case and the add-on is not installed by the
|
|
// application and is not compatible with the new application version
|
|
// then the user should be warned that the add-on will become
|
|
// incompatible. If an addon's type equals plugin it is skipped since
|
|
// checking plugins compatibility information isn't supported and
|
|
// getting the scope property of a plugin breaks in some environments
|
|
// (see bug 566787). The hotfix add-on is also ignored as it shouldn't
|
|
// block the user from upgrading.
|
|
try {
|
|
if (addon.type != "plugin" && addon.id != hotfixID &&
|
|
!addon.appDisabled && !addon.userDisabled &&
|
|
addon.scope != AddonManager.SCOPE_APPLICATION &&
|
|
addon.isCompatible &&
|
|
!addon.isCompatibleWith(self.update.appVersion,
|
|
self.update.platformVersion))
|
|
self.addons.push(addon);
|
|
}
|
|
catch (e) {
|
|
Components.utils.reportError(e);
|
|
}
|
|
});
|
|
|
|
aCallback(self.addons.length != 0);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Returns the string page ID for the appropriate updates found page based
|
|
* on the update's metadata.
|
|
*/
|
|
get updatesFoundPageId() {
|
|
if (gUpdatesFoundPageId)
|
|
return gUpdatesFoundPageId;
|
|
return gUpdatesFoundPageId = this.update.billboardURL ? "updatesfoundbillboard"
|
|
: "updatesfoundbasic";
|
|
},
|
|
|
|
/**
|
|
* Sets the Update object for this wizard
|
|
* @param update
|
|
* The update object
|
|
*/
|
|
setUpdate: function(update) {
|
|
this.update = update;
|
|
if (this.update)
|
|
this.update.QueryInterface(CoI.nsIWritablePropertyBag);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The "Checking for Updates" page. Provides feedback on the update checking
|
|
* process.
|
|
*/
|
|
var gCheckingPage = {
|
|
/**
|
|
* The nsIUpdateChecker that is currently checking for updates. We hold onto
|
|
* this so we can cancel the update check if the user closes the window.
|
|
*/
|
|
_checker: null,
|
|
|
|
/**
|
|
* Initialize
|
|
*/
|
|
onPageShow: function() {
|
|
gUpdates.setButtons(null, null, null, false, true);
|
|
gUpdates.wiz.getButton("cancel").focus();
|
|
|
|
// Clear all of the "never" prefs to handle the scenario where the user
|
|
// clicked "never" for an update, selected "Check for Updates...", and
|
|
// then canceled. If we don't clear the "never" prefs future
|
|
// notifications will never happen.
|
|
Services.prefs.deleteBranch(PREF_APP_UPDATE_NEVER_BRANCH);
|
|
|
|
// The user will be notified if there is an error so clear the background
|
|
// check error count.
|
|
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDERRORS))
|
|
Services.prefs.clearUserPref(PREF_APP_UPDATE_BACKGROUNDERRORS);
|
|
|
|
this._checker = CoC["@mozilla.org/updates/update-checker;1"].
|
|
createInstance(CoI.nsIUpdateChecker);
|
|
this._checker.checkForUpdates(this.updateListener, true);
|
|
},
|
|
|
|
/**
|
|
* The user has closed the window, either by pressing cancel or using a Window
|
|
* Manager control, so stop checking for updates.
|
|
*/
|
|
onWizardCancel: function() {
|
|
this._checker.stopChecking(CoI.nsIUpdateChecker.CURRENT_CHECK);
|
|
},
|
|
|
|
/**
|
|
* An object implementing nsIUpdateCheckListener that is notified as the
|
|
* update check commences.
|
|
*/
|
|
updateListener: {
|
|
/**
|
|
* See nsIUpdateCheckListener
|
|
*/
|
|
onProgress: function(request, position, totalSize) {
|
|
var pm = document.getElementById("checkingProgress");
|
|
pm.mode = "normal";
|
|
pm.value = Math.floor(100 * (position / totalSize));
|
|
},
|
|
|
|
/**
|
|
* See nsIUpdateCheckListener
|
|
*/
|
|
onCheckComplete: function(request, updates, updateCount) {
|
|
var aus = CoC["@mozilla.org/updates/update-service;1"].
|
|
getService(CoI.nsIApplicationUpdateService);
|
|
gUpdates.setUpdate(aus.selectUpdate(updates, updates.length));
|
|
if (gUpdates.update) {
|
|
LOG("gCheckingPage", "onCheckComplete - update found");
|
|
if (!aus.canApplyUpdates) {
|
|
// Prevent multiple notifications for the same update when the user is
|
|
// unable to apply updates.
|
|
gUpdates.never();
|
|
gUpdates.wiz.goTo("manualUpdate");
|
|
return;
|
|
}
|
|
|
|
if (gUpdates.update.licenseURL) {
|
|
// gUpdates.updatesFoundPageId returns the pageid and not the
|
|
// element's id so use the wizard's getPageById method.
|
|
gUpdates.wiz.getPageById(gUpdates.updatesFoundPageId).setAttribute("next", "license");
|
|
}
|
|
|
|
gUpdates.getShouldCheckAddonCompatibility(function(shouldCheck) {
|
|
if (shouldCheck) {
|
|
var incompatCheckPage = document.getElementById("incompatibleCheck");
|
|
incompatCheckPage.setAttribute("next", gUpdates.updatesFoundPageId);
|
|
gUpdates.wiz.goTo("incompatibleCheck");
|
|
}
|
|
else {
|
|
gUpdates.wiz.goTo(gUpdates.updatesFoundPageId);
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
|
|
LOG("gCheckingPage", "onCheckComplete - no update found");
|
|
gUpdates.wiz.goTo("noupdatesfound");
|
|
},
|
|
|
|
/**
|
|
* See nsIUpdateCheckListener
|
|
*/
|
|
onError: function(request, update) {
|
|
LOG("gCheckingPage", "onError - proceeding to error page");
|
|
gUpdates.setUpdate(update);
|
|
if (update.errorCode &&
|
|
(update.errorCode == CERT_ATTR_CHECK_FAILED_NO_UPDATE ||
|
|
update.errorCode == CERT_ATTR_CHECK_FAILED_HAS_UPDATE)) {
|
|
gUpdates.wiz.goTo("errorextra");
|
|
}
|
|
else {
|
|
gUpdates.wiz.goTo("errors");
|
|
}
|
|
},
|
|
|
|
/**
|
|
* See nsISupports.idl
|
|
*/
|
|
QueryInterface: function(aIID) {
|
|
if (!aIID.equals(CoI.nsIUpdateCheckListener) &&
|
|
!aIID.equals(CoI.nsISupports))
|
|
throw CoR.NS_ERROR_NO_INTERFACE;
|
|
return this;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The "You have outdated plugins" page
|
|
*/
|
|
var gPluginsPage = {
|
|
/**
|
|
* URL of the plugin updates page
|
|
*/
|
|
_url: null,
|
|
|
|
/**
|
|
* Initialize
|
|
*/
|
|
onPageShow: function() {
|
|
var prefs = Services.prefs;
|
|
if (prefs.getPrefType(PREF_PLUGINS_UPDATEURL) == prefs.PREF_INVALID) {
|
|
gUpdates.wiz.goTo("noupdatesfound");
|
|
return;
|
|
}
|
|
|
|
this._url = Services.urlFormatter.formatURLPref(PREF_PLUGINS_UPDATEURL);
|
|
var link = document.getElementById("pluginupdateslink");
|
|
link.setAttribute("href", this._url);
|
|
|
|
|
|
var phs = CoC["@mozilla.org/plugin/host;1"].
|
|
getService(CoI.nsIPluginHost);
|
|
var plugins = phs.getPluginTags();
|
|
var blocklist = CoC["@mozilla.org/extensions/blocklist;1"].
|
|
getService(CoI.nsIBlocklistService);
|
|
|
|
var hasOutdated = false;
|
|
for (let i = 0; i < plugins.length; i++) {
|
|
let pluginState = blocklist.getPluginBlocklistState(plugins[i]);
|
|
if (pluginState == CoI.nsIBlocklistService.STATE_OUTDATED) {
|
|
hasOutdated = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!hasOutdated) {
|
|
gUpdates.wiz.goTo("noupdatesfound");
|
|
return;
|
|
}
|
|
|
|
gUpdates.setButtons(null, null, "okButton", true);
|
|
gUpdates.wiz.getButton("finish").focus();
|
|
},
|
|
|
|
/**
|
|
* Finish button clicked.
|
|
*/
|
|
onWizardFinish: function() {
|
|
openURL(this._url);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The "No Updates Are Available" page
|
|
*/
|
|
var gNoUpdatesPage = {
|
|
/**
|
|
* Initialize
|
|
*/
|
|
onPageShow: function() {
|
|
LOG("gNoUpdatesPage", "onPageShow - could not select an appropriate " +
|
|
"update. Either there were no updates or |selectUpdate| failed");
|
|
|
|
if (getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true))
|
|
document.getElementById("noUpdatesAutoEnabled").hidden = false;
|
|
else
|
|
document.getElementById("noUpdatesAutoDisabled").hidden = false;
|
|
|
|
gUpdates.setButtons(null, null, "okButton", true);
|
|
gUpdates.wiz.getButton("finish").focus();
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* The page that checks if there are any incompatible add-ons.
|
|
*/
|
|
var gIncompatibleCheckPage = {
|
|
/**
|
|
* Count of incompatible add-ons to check for updates
|
|
*/
|
|
_totalCount: 0,
|
|
|
|
/**
|
|
* Count of incompatible add-ons that have beend checked for updates
|
|
*/
|
|
_completedCount: 0,
|
|
|
|
/**
|
|
* The progress bar for this page
|
|
*/
|
|
_pBar: null,
|
|
|
|
/**
|
|
* Initialize
|
|
*/
|
|
onPageShow: function() {
|
|
LOG("gIncompatibleCheckPage", "onPageShow - checking for updates to " +
|
|
"incompatible add-ons");
|
|
|
|
gUpdates.setButtons(null, null, null, false, true);
|
|
gUpdates.wiz.getButton("cancel").focus();
|
|
this._pBar = document.getElementById("incompatibleCheckProgress");
|
|
this._totalCount = gUpdates.addons.length;
|
|
|
|
this._pBar.mode = "normal";
|
|
gUpdates.addons.forEach(function(addon) {
|
|
addon.findUpdates(this, AddonManager.UPDATE_WHEN_NEW_APP_DETECTED,
|
|
gUpdates.update.appVersion,
|
|
gUpdates.update.platformVersion);
|
|
}, this);
|
|
},
|
|
|
|
// Addon UpdateListener
|
|
onCompatibilityUpdateAvailable: function(addon) {
|
|
// Remove the add-on from the list of add-ons that will become incompatible
|
|
// with the new version of the application.
|
|
for (var i = 0; i < gUpdates.addons.length; ++i) {
|
|
if (gUpdates.addons[i].id == addon.id) {
|
|
LOG("gIncompatibleCheckPage", "onCompatibilityUpdateAvailable - " +
|
|
"found update for add-on ID: " + addon.id);
|
|
gUpdates.addons.splice(i, 1);
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
|
|
onUpdateAvailable: function(addon, install) {
|
|
// If the new version of this add-on is blocklisted for the new application
|
|
// then it isn't a valid update and the user should still be warned that
|
|
// the add-on will become incompatible.
|
|
let bs = CoC["@mozilla.org/extensions/blocklist;1"].
|
|
getService(CoI.nsIBlocklistService);
|
|
if (bs.isAddonBlocklisted(addon.id, install.version,
|
|
gUpdates.update.appVersion,
|
|
gUpdates.update.platformVersion))
|
|
return;
|
|
|
|
// Compatibility or new version updates mean the same thing here.
|
|
this.onCompatibilityUpdateAvailable(addon);
|
|
},
|
|
|
|
onUpdateFinished: function(addon) {
|
|
++this._completedCount;
|
|
this._pBar.value = Math.ceil((this._completedCount / this._totalCount) * 100);
|
|
|
|
if (this._completedCount < this._totalCount)
|
|
return;
|
|
|
|
if (gUpdates.addons.length == 0) {
|
|
LOG("gIncompatibleCheckPage", "onUpdateFinished - updates were found " +
|
|
"for all incompatible add-ons");
|
|
}
|
|
else {
|
|
LOG("gIncompatibleCheckPage", "onUpdateFinished - there are still " +
|
|
"incompatible add-ons");
|
|
if (gUpdates.update.licenseURL) {
|
|
document.getElementById("license").setAttribute("next", "incompatibleList");
|
|
}
|
|
else {
|
|
// gUpdates.updatesFoundPageId returns the pageid and not the element's
|
|
// id so use the wizard's getPageById method.
|
|
gUpdates.wiz.getPageById(gUpdates.updatesFoundPageId).setAttribute("next", "incompatibleList");
|
|
}
|
|
}
|
|
gUpdates.wiz.goTo(gUpdates.updatesFoundPageId);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The "Unable to Update" page. Provides the user information about why they
|
|
* were unable to update and a manual download url.
|
|
*/
|
|
var gManualUpdatePage = {
|
|
onPageShow: function() {
|
|
var manualURL = Services.urlFormatter.formatURLPref(PREF_APP_UPDATE_MANUAL_URL);
|
|
var manualUpdateLinkLabel = document.getElementById("manualUpdateLinkLabel");
|
|
manualUpdateLinkLabel.value = manualURL;
|
|
manualUpdateLinkLabel.setAttribute("url", manualURL);
|
|
|
|
gUpdates.setButtons(null, null, "okButton", true);
|
|
gUpdates.wiz.getButton("finish").focus();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The "Updates Are Available" page. Provides the user information about the
|
|
* available update.
|
|
*/
|
|
var gUpdatesFoundBasicPage = {
|
|
/**
|
|
* Initialize
|
|
*/
|
|
onPageShow: function() {
|
|
gUpdates.wiz.canRewind = false;
|
|
var update = gUpdates.update;
|
|
gUpdates.setButtons("askLaterButton",
|
|
update.showNeverForVersion ? "noThanksButton" : null,
|
|
"updateButton_" + update.type, true);
|
|
var btn = gUpdates.wiz.getButton("next");
|
|
btn.focus();
|
|
|
|
var updateName = update.name;
|
|
if (update.channel == "nightly") {
|
|
updateName = gUpdates.getAUSString("updateNightlyName",
|
|
[gUpdates.brandName,
|
|
update.displayVersion,
|
|
update.buildID]);
|
|
}
|
|
var updateNameElement = document.getElementById("updateName");
|
|
updateNameElement.value = updateName;
|
|
|
|
var introText = gUpdates.getAUSString("intro_" + update.type,
|
|
[gUpdates.brandName, update.displayVersion]);
|
|
var introElem = document.getElementById("updatesFoundInto");
|
|
introElem.setAttribute("severity", update.type);
|
|
introElem.textContent = introText;
|
|
|
|
var updateMoreInfoURL = document.getElementById("updateMoreInfoURL");
|
|
if (update.detailsURL)
|
|
updateMoreInfoURL.setAttribute("url", update.detailsURL);
|
|
else
|
|
updateMoreInfoURL.hidden = true;
|
|
|
|
var updateTitle = gUpdates.getAUSString("updatesfound_" + update.type +
|
|
".title");
|
|
document.getElementById("updatesFoundBasicHeader").setAttribute("label", updateTitle);
|
|
},
|
|
|
|
onExtra1: function() {
|
|
gUpdates.wiz.cancel();
|
|
},
|
|
|
|
onExtra2: function() {
|
|
gUpdates.never();
|
|
gUpdates.wiz.cancel();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The "Updates Are Available" page with a billboard. Provides the user
|
|
* information about the available update.
|
|
*/
|
|
var gUpdatesFoundBillboardPage = {
|
|
/**
|
|
* If this page has been previously loaded
|
|
*/
|
|
_billboardLoaded: false,
|
|
|
|
/**
|
|
* Initialize
|
|
*/
|
|
onPageShow: function() {
|
|
var update = gUpdates.update;
|
|
gUpdates.setButtons("askLaterButton",
|
|
update.showNeverForVersion ? "noThanksButton" : null,
|
|
"updateButton_" + update.type, true);
|
|
gUpdates.wiz.getButton("next").focus();
|
|
|
|
if (this._billboardLoaded)
|
|
return;
|
|
|
|
var remoteContent = document.getElementById("updateMoreInfoContent");
|
|
remoteContent.addEventListener("load",
|
|
gUpdatesFoundBillboardPage.onBillboardLoad,
|
|
false);
|
|
// update_name and update_version need to be set before url
|
|
// so that when attempting to download the url, we can show
|
|
// the formatted "Download..." string
|
|
remoteContent.update_name = gUpdates.brandName;
|
|
remoteContent.update_version = update.displayVersion;
|
|
|
|
var billboardTestURL = getPref("getCharPref", PREF_APP_UPDATE_BILLBOARD_TEST_URL, null);
|
|
if (billboardTestURL) {
|
|
// Allow file urls when testing the billboard and fallback to the
|
|
// normal method if the URL isn't a file.
|
|
var scheme = Services.io.newURI(billboardTestURL, null, null).scheme;
|
|
if (scheme == "file")
|
|
remoteContent.testFileUrl = update.billboardURL;
|
|
else
|
|
remoteContent.url = update.billboardURL;
|
|
}
|
|
else
|
|
remoteContent.url = update.billboardURL;
|
|
|
|
this._billboardLoaded = true;
|
|
},
|
|
|
|
/**
|
|
* When the billboard document has loaded
|
|
*/
|
|
onBillboardLoad: function(aEvent) {
|
|
var remoteContent = document.getElementById("updateMoreInfoContent");
|
|
// Note: may be called multiple times due to multiple onLoad events.
|
|
var state = remoteContent.getAttribute("state");
|
|
if (state == "loading" || aEvent.originalTarget != remoteContent)
|
|
return;
|
|
|
|
remoteContent.removeEventListener("load", gUpdatesFoundBillboardPage.onBillboardLoad, false);
|
|
if (state == "error") {
|
|
gUpdatesFoundPageId = "updatesfoundbasic";
|
|
var next = gUpdates.wiz.getPageById("updatesfoundbillboard").getAttribute("next");
|
|
gUpdates.wiz.getPageById(gUpdates.updatesFoundPageId).setAttribute("next", next);
|
|
gUpdates.wiz.goTo(gUpdates.updatesFoundPageId);
|
|
}
|
|
},
|
|
|
|
onExtra1: function() {
|
|
this.onWizardCancel();
|
|
gUpdates.wiz.cancel();
|
|
},
|
|
|
|
onExtra2: function() {
|
|
this.onWizardCancel();
|
|
gUpdates.never();
|
|
gUpdates.wiz.cancel();
|
|
},
|
|
|
|
/**
|
|
* When the user cancels the wizard
|
|
*/
|
|
onWizardCancel: function() {
|
|
try {
|
|
var remoteContent = document.getElementById("updateMoreInfoContent");
|
|
if (remoteContent)
|
|
remoteContent.stopDownloading();
|
|
}
|
|
catch (e) {
|
|
LOG("gUpdatesFoundBillboardPage", "onWizardCancel - " +
|
|
"moreInfoContent.stopDownloading() failed: " + e);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The page which shows the user a license associated with an update. The
|
|
* user must agree to the terms of the license before continuing to install
|
|
* the update.
|
|
*/
|
|
var gLicensePage = {
|
|
/**
|
|
* If the license url has been previously loaded
|
|
*/
|
|
_licenseLoaded: false,
|
|
|
|
/**
|
|
* Initialize
|
|
*/
|
|
onPageShow: function() {
|
|
gUpdates.setButtons("backButton", null, "acceptTermsButton", false);
|
|
|
|
var licenseContent = document.getElementById("licenseContent");
|
|
if (this._licenseLoaded || licenseContent.getAttribute("state") == "error") {
|
|
this.onAcceptDeclineRadio();
|
|
var licenseGroup = document.getElementById("acceptDeclineLicense");
|
|
licenseGroup.focus();
|
|
return;
|
|
}
|
|
|
|
gUpdates.wiz.canAdvance = false;
|
|
|
|
// Disable the license radiogroup until the EULA has been downloaded
|
|
document.getElementById("acceptDeclineLicense").disabled = true;
|
|
gUpdates.update.setProperty("licenseAccepted", "false");
|
|
|
|
licenseContent.addEventListener("load", gLicensePage.onLicenseLoad, false);
|
|
// The update_name and update_version need to be set before url so the ui
|
|
// can display the formatted "Download..." string when attempting to
|
|
// download the url.
|
|
licenseContent.update_name = gUpdates.brandName;
|
|
licenseContent.update_version = gUpdates.update.displayVersion;
|
|
licenseContent.url = gUpdates.update.licenseURL;
|
|
},
|
|
|
|
/**
|
|
* When the license document has loaded
|
|
*/
|
|
onLicenseLoad: function(aEvent) {
|
|
var licenseContent = document.getElementById("licenseContent");
|
|
// Disable or enable the radiogroup based on the state attribute of
|
|
// licenseContent.
|
|
// Note: may be called multiple times due to multiple onLoad events.
|
|
var state = licenseContent.getAttribute("state");
|
|
if (state == "loading" || aEvent.originalTarget != licenseContent)
|
|
return;
|
|
|
|
licenseContent.removeEventListener("load", gLicensePage.onLicenseLoad, false);
|
|
|
|
if (state == "error") {
|
|
gUpdates.wiz.goTo("manualUpdate");
|
|
return;
|
|
}
|
|
|
|
gLicensePage._licenseLoaded = true;
|
|
document.getElementById("acceptDeclineLicense").disabled = false;
|
|
gUpdates.wiz.getButton("extra1").disabled = false;
|
|
},
|
|
|
|
/**
|
|
* When the user changes the state of the accept / decline radio group
|
|
*/
|
|
onAcceptDeclineRadio: function() {
|
|
// Return early if this page hasn't been loaded (bug 405257). This event is
|
|
// fired during the construction of the wizard before gUpdates has received
|
|
// its onload event (bug 452389).
|
|
if (!this._licenseLoaded)
|
|
return;
|
|
|
|
var selectedIndex = document.getElementById("acceptDeclineLicense")
|
|
.selectedIndex;
|
|
// 0 == Accept, 1 == Decline
|
|
var licenseAccepted = (selectedIndex == 0);
|
|
gUpdates.wiz.canAdvance = licenseAccepted;
|
|
},
|
|
|
|
/**
|
|
* The non-standard "Back" button.
|
|
*/
|
|
onExtra1: function() {
|
|
gUpdates.wiz.goTo(gUpdates.updatesFoundPageId);
|
|
},
|
|
|
|
/**
|
|
* When the user clicks next after accepting the license
|
|
*/
|
|
onWizardNext: function() {
|
|
try {
|
|
gUpdates.update.setProperty("licenseAccepted", "true");
|
|
var um = CoC["@mozilla.org/updates/update-manager;1"].
|
|
getService(CoI.nsIUpdateManager);
|
|
um.saveUpdates();
|
|
}
|
|
catch (e) {
|
|
LOG("gLicensePage", "onWizardNext - nsIUpdateManager:saveUpdates() " +
|
|
"failed: " + e);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* When the user cancels the wizard
|
|
*/
|
|
onWizardCancel: function() {
|
|
try {
|
|
var licenseContent = document.getElementById("licenseContent");
|
|
// If the license was downloading, stop it.
|
|
if (licenseContent)
|
|
licenseContent.stopDownloading();
|
|
}
|
|
catch (e) {
|
|
LOG("gLicensePage", "onWizardCancel - " +
|
|
"licenseContent.stopDownloading() failed: " + e);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The page which shows add-ons that are incompatible and do not have updated
|
|
* compatibility information or a version update available to make them
|
|
* compatible.
|
|
*/
|
|
var gIncompatibleListPage = {
|
|
/**
|
|
* Initialize
|
|
*/
|
|
onPageShow: function() {
|
|
gUpdates.setButtons("backButton", null, "okButton", true);
|
|
var listbox = document.getElementById("incompatibleListbox");
|
|
if (listbox.children.length > 0)
|
|
return;
|
|
|
|
var intro = gUpdates.getAUSString("incompatAddons_" + gUpdates.update.type,
|
|
[gUpdates.brandName,
|
|
gUpdates.update.displayVersion]);
|
|
document.getElementById("incompatibleListDesc").textContent = intro;
|
|
|
|
var addons = gUpdates.addons;
|
|
for (var i = 0; i < addons.length; ++i) {
|
|
var listitem = document.createElement("listitem");
|
|
var addonLabel = gUpdates.getAUSString("addonLabel", [addons[i].name,
|
|
addons[i].version]);
|
|
listitem.setAttribute("label", addonLabel);
|
|
listbox.appendChild(listitem);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* The non-standard "Back" button.
|
|
*/
|
|
onExtra1: function() {
|
|
gUpdates.wiz.goTo(gUpdates.update.licenseURL ? "license"
|
|
: gUpdates.updatesFoundPageId);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The "Update is Downloading" page - provides feedback for the download
|
|
* process plus a pause/resume UI
|
|
*/
|
|
var gDownloadingPage = {
|
|
/**
|
|
* DOM Elements
|
|
*/
|
|
_downloadStatus: null,
|
|
_downloadProgress: null,
|
|
_pauseButton: null,
|
|
|
|
/**
|
|
* Whether or not we are currently paused
|
|
*/
|
|
_paused: false,
|
|
|
|
/**
|
|
* Label cache to hold the 'Connecting' string
|
|
*/
|
|
_label_downloadStatus: null,
|
|
|
|
/**
|
|
* Member variables for updating download status
|
|
*/
|
|
_lastSec: Infinity,
|
|
_startTime: null,
|
|
_pausedStatus: "",
|
|
|
|
_hiding: false,
|
|
|
|
/**
|
|
* Initialize
|
|
*/
|
|
onPageShow: function() {
|
|
this._downloadStatus = document.getElementById("downloadStatus");
|
|
this._downloadProgress = document.getElementById("downloadProgress");
|
|
this._pauseButton = document.getElementById("pauseButton");
|
|
this._label_downloadStatus = this._downloadStatus.textContent;
|
|
|
|
this._pauseButton.setAttribute("tooltiptext",
|
|
gUpdates.getAUSString("pauseButtonPause"));
|
|
|
|
// move focus to the pause/resume button and then disable it (bug #353177)
|
|
this._pauseButton.focus();
|
|
this._pauseButton.disabled = true;
|
|
|
|
var aus = CoC["@mozilla.org/updates/update-service;1"].
|
|
getService(CoI.nsIApplicationUpdateService);
|
|
|
|
var um = CoC["@mozilla.org/updates/update-manager;1"].
|
|
getService(CoI.nsIUpdateManager);
|
|
var activeUpdate = um.activeUpdate;
|
|
if (activeUpdate)
|
|
gUpdates.setUpdate(activeUpdate);
|
|
|
|
if (!gUpdates.update) {
|
|
LOG("gDownloadingPage", "onPageShow - no valid update to download?!");
|
|
return;
|
|
}
|
|
|
|
this._startTime = Date.now();
|
|
|
|
try {
|
|
// Say that this was a foreground download, not a background download,
|
|
// since the user cared enough to look in on this process.
|
|
gUpdates.update.QueryInterface(CoI.nsIWritablePropertyBag);
|
|
gUpdates.update.setProperty("foregroundDownload", "true");
|
|
|
|
// Pause any active background download and restart it as a foreground
|
|
// download.
|
|
aus.pauseDownload();
|
|
var state = aus.downloadUpdate(gUpdates.update, false);
|
|
if (state == "failed") {
|
|
// We've tried as hard as we could to download a valid update -
|
|
// we fell back from a partial patch to a complete patch and even
|
|
// then we couldn't validate. Show a validation error with instructions
|
|
// on how to manually update.
|
|
this.removeDownloadListener();
|
|
gUpdates.wiz.goTo("errors");
|
|
return;
|
|
}
|
|
else {
|
|
// Add this UI as a listener for active downloads
|
|
aus.addDownloadListener(this);
|
|
}
|
|
|
|
if (activeUpdate)
|
|
this._setUIState(!aus.isDownloading);
|
|
}
|
|
catch(e) {
|
|
LOG("gDownloadingPage", "onPageShow - error: " + e);
|
|
}
|
|
|
|
gUpdates.setButtons("hideButton", null, null, false);
|
|
gUpdates.wiz.getButton("extra1").focus();
|
|
},
|
|
|
|
/**
|
|
* Updates the text status message
|
|
*/
|
|
_setStatus: function(status) {
|
|
// Don't bother setting the same text more than once. This can happen
|
|
// due to the asynchronous behavior of the downloader.
|
|
if (this._downloadStatus.textContent == status)
|
|
return;
|
|
while (this._downloadStatus.hasChildNodes())
|
|
this._downloadStatus.removeChild(this._downloadStatus.firstChild);
|
|
this._downloadStatus.appendChild(document.createTextNode(status));
|
|
},
|
|
|
|
/**
|
|
* Update download progress status to show time left, speed, and progress.
|
|
* Also updates the status needed for pausing the download.
|
|
*
|
|
* @param aCurr
|
|
* Current number of bytes transferred
|
|
* @param aMax
|
|
* Total file size of the download
|
|
* @return Current active download status
|
|
*/
|
|
_updateDownloadStatus: function(aCurr, aMax) {
|
|
let status;
|
|
|
|
// Get the download time left and progress
|
|
let rate = aCurr / (Date.now() - this._startTime) * 1000;
|
|
[status, this._lastSec] =
|
|
DownloadUtils.getDownloadStatus(aCurr, aMax, rate, this._lastSec);
|
|
|
|
// Get the download progress for pausing
|
|
this._pausedStatus = DownloadUtils.getTransferTotal(aCurr, aMax);
|
|
|
|
return status;
|
|
},
|
|
|
|
/**
|
|
* Adjust UI to suit a certain state of paused-ness
|
|
* @param paused
|
|
* Whether or not the download is paused
|
|
*/
|
|
_setUIState: function(paused) {
|
|
var u = gUpdates.update;
|
|
if (paused) {
|
|
if (this._downloadProgress.mode != "normal")
|
|
this._downloadProgress.mode = "normal";
|
|
this._pauseButton.setAttribute("tooltiptext",
|
|
gUpdates.getAUSString("pauseButtonResume"));
|
|
this._pauseButton.setAttribute("paused", "true");
|
|
var p = u.selectedPatch.QueryInterface(CoI.nsIPropertyBag);
|
|
var status = p.getProperty("status");
|
|
if (status) {
|
|
let pausedStatus = gUpdates.getAUSString("downloadPausedStatus", [status]);
|
|
this._setStatus(pausedStatus);
|
|
}
|
|
}
|
|
else {
|
|
if (this._downloadProgress.mode != "undetermined")
|
|
this._downloadProgress.mode = "undetermined";
|
|
this._pauseButton.setAttribute("paused", "false");
|
|
this._pauseButton.setAttribute("tooltiptext",
|
|
gUpdates.getAUSString("pauseButtonPause"));
|
|
this._setStatus(this._label_downloadStatus);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Removes the download listener.
|
|
*/
|
|
removeDownloadListener: function() {
|
|
var aus = CoC["@mozilla.org/updates/update-service;1"].
|
|
getService(CoI.nsIApplicationUpdateService);
|
|
aus.removeDownloadListener(this);
|
|
},
|
|
|
|
/**
|
|
* When the user clicks the Pause/Resume button
|
|
*/
|
|
onPause: function() {
|
|
var aus = CoC["@mozilla.org/updates/update-service;1"].
|
|
getService(CoI.nsIApplicationUpdateService);
|
|
if (this._paused)
|
|
aus.downloadUpdate(gUpdates.update, false);
|
|
else {
|
|
var patch = gUpdates.update.selectedPatch;
|
|
patch.QueryInterface(CoI.nsIWritablePropertyBag);
|
|
patch.setProperty("status", this._pausedStatus);
|
|
aus.pauseDownload();
|
|
}
|
|
this._paused = !this._paused;
|
|
|
|
// Update the UI
|
|
this._setUIState(this._paused);
|
|
},
|
|
|
|
/**
|
|
* When the user has closed the window using a Window Manager control (this
|
|
* page doesn't have a cancel button) cancel the update in progress.
|
|
*/
|
|
onWizardCancel: function() {
|
|
if (this._hiding)
|
|
return;
|
|
|
|
this.removeDownloadListener();
|
|
},
|
|
|
|
/**
|
|
* When the user closes the Wizard UI by clicking the Hide button
|
|
*/
|
|
onHide: function() {
|
|
// Set _hiding to true to prevent onWizardCancel from cancelling the update
|
|
// that is in progress.
|
|
this._hiding = true;
|
|
|
|
// Remove ourself as a download listener so that we don't continue to be
|
|
// fed progress and state notifications after the UI we're updating has
|
|
// gone away.
|
|
this.removeDownloadListener();
|
|
|
|
var aus = CoC["@mozilla.org/updates/update-service;1"].
|
|
getService(CoI.nsIApplicationUpdateService);
|
|
var um = CoC["@mozilla.org/updates/update-manager;1"].
|
|
getService(CoI.nsIUpdateManager);
|
|
um.activeUpdate = gUpdates.update;
|
|
|
|
// If the download was paused by the user, ask the user if they want to
|
|
// have the update resume in the background.
|
|
var downloadInBackground = true;
|
|
if (this._paused) {
|
|
var title = gUpdates.getAUSString("resumePausedAfterCloseTitle");
|
|
var message = gUpdates.getAUSString("resumePausedAfterCloseMsg",
|
|
[gUpdates.brandName]);
|
|
var ps = Services.prompt;
|
|
var flags = ps.STD_YES_NO_BUTTONS;
|
|
// Focus the software update wizard before prompting. This will raise
|
|
// the software update wizard if it is minimized making it more obvious
|
|
// what the prompt is for and will solve the problem of windows
|
|
// obscuring the prompt. See bug #350299 for more details.
|
|
window.focus();
|
|
var rv = ps.confirmEx(window, title, message, flags, null, null, null,
|
|
null, { });
|
|
if (rv == CoI.nsIPromptService.BUTTON_POS_0)
|
|
downloadInBackground = false;
|
|
}
|
|
if (downloadInBackground) {
|
|
// Continue download in the background at full speed.
|
|
LOG("gDownloadingPage", "onHide - continuing download in background " +
|
|
"at full speed");
|
|
aus.downloadUpdate(gUpdates.update, false);
|
|
}
|
|
gUpdates.wiz.cancel();
|
|
},
|
|
|
|
/**
|
|
* When the data transfer begins
|
|
* @param request
|
|
* The nsIRequest object for the transfer
|
|
* @param context
|
|
* Additional data
|
|
*/
|
|
onStartRequest: function(request, context) {
|
|
// This !paused test is necessary because onStartRequest may fire after
|
|
// the download was paused (for those speedy clickers...)
|
|
if (this._paused)
|
|
return;
|
|
|
|
if (this._downloadProgress.mode != "undetermined")
|
|
this._downloadProgress.mode = "undetermined";
|
|
this._setStatus(this._label_downloadStatus);
|
|
},
|
|
|
|
/**
|
|
* 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) {
|
|
let status = this._updateDownloadStatus(progress, maxProgress);
|
|
var currentProgress = Math.round(100 * (progress / maxProgress));
|
|
|
|
var p = gUpdates.update.selectedPatch;
|
|
p.QueryInterface(CoI.nsIWritablePropertyBag);
|
|
p.setProperty("progress", currentProgress);
|
|
p.setProperty("status", status);
|
|
|
|
// This !paused test is necessary because onProgress may fire after
|
|
// the download was paused (for those speedy clickers...)
|
|
if (this._paused)
|
|
return;
|
|
|
|
if (this._downloadProgress.mode != "normal")
|
|
this._downloadProgress.mode = "normal";
|
|
if (this._downloadProgress.value != currentProgress)
|
|
this._downloadProgress.value = currentProgress;
|
|
if (this._pauseButton.disabled)
|
|
this._pauseButton.disabled = false;
|
|
|
|
// If the update has completed downloading and the download status contains
|
|
// the original text return early to avoid an assertion in debug builds.
|
|
// Since the page will advance immmediately due to the update completing the
|
|
// download updating the status is not important.
|
|
// nsTextFrame::GetTrimmedOffsets 'Can only call this on frames that have
|
|
// been reflowed'.
|
|
if (progress == maxProgress &&
|
|
this._downloadStatus.textContent == this._label_downloadStatus)
|
|
return;
|
|
|
|
this._setStatus(status);
|
|
},
|
|
|
|
/**
|
|
* 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) {
|
|
this._setStatus(statusText);
|
|
},
|
|
|
|
/**
|
|
* 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) {
|
|
if (this._downloadProgress.mode != "normal")
|
|
this._downloadProgress.mode = "normal";
|
|
|
|
var u = gUpdates.update;
|
|
switch (status) {
|
|
case CoR.NS_ERROR_UNEXPECTED:
|
|
if (u.selectedPatch.state == STATE_DOWNLOAD_FAILED &&
|
|
(u.isCompleteUpdate || u.patchCount != 2)) {
|
|
// Verification error of complete patch, informational text is held in
|
|
// the update object.
|
|
this.removeDownloadListener();
|
|
gUpdates.wiz.goTo("errors");
|
|
break;
|
|
}
|
|
// Verification failed for a partial patch, complete patch is now
|
|
// downloading so return early and do NOT remove the download listener!
|
|
|
|
// Reset the progress meter to "undertermined" mode so that we don't
|
|
// show old progress for the new download of the "complete" patch.
|
|
this._downloadProgress.mode = "undetermined";
|
|
this._pauseButton.disabled = true;
|
|
document.getElementById("verificationFailed").hidden = false;
|
|
break;
|
|
case CoR.NS_BINDING_ABORTED:
|
|
LOG("gDownloadingPage", "onStopRequest - pausing download");
|
|
// Do not remove UI listener since the user may resume downloading again.
|
|
break;
|
|
case CoR.NS_OK:
|
|
LOG("gDownloadingPage", "onStopRequest - patch verification succeeded");
|
|
this.removeDownloadListener();
|
|
gUpdates.wiz.goTo("finished");
|
|
break;
|
|
default:
|
|
LOG("gDownloadingPage", "onStopRequest - transfer failed");
|
|
// Some kind of transfer error, die.
|
|
this.removeDownloadListener();
|
|
gUpdates.wiz.goTo("errors");
|
|
break;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* See nsISupports.idl
|
|
*/
|
|
QueryInterface: function(iid) {
|
|
if (!iid.equals(CoI.nsIRequestObserver) &&
|
|
!iid.equals(CoI.nsIProgressEventSink) &&
|
|
!iid.equals(CoI.nsISupports))
|
|
throw CoR.NS_ERROR_NO_INTERFACE;
|
|
return this;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The "There was an error during the update" page.
|
|
*/
|
|
var gErrorsPage = {
|
|
/**
|
|
* Initialize
|
|
*/
|
|
onPageShow: function() {
|
|
gUpdates.setButtons(null, null, "okButton", true);
|
|
gUpdates.wiz.getButton("finish").focus();
|
|
|
|
var statusText = gUpdates.update.statusText;
|
|
LOG("gErrorsPage" , "onPageShow - update.statusText: " + statusText);
|
|
|
|
var errorReason = document.getElementById("errorReason");
|
|
errorReason.value = statusText;
|
|
var manualURL = Services.urlFormatter.formatURLPref(PREF_APP_UPDATE_MANUAL_URL);
|
|
var errorLinkLabel = document.getElementById("errorLinkLabel");
|
|
errorLinkLabel.value = manualURL;
|
|
errorLinkLabel.setAttribute("url", manualURL);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The page shown when there is a background check or a certificate attribute
|
|
* error.
|
|
*/
|
|
var gErrorExtraPage = {
|
|
/**
|
|
* Initialize
|
|
*/
|
|
onPageShow: function() {
|
|
gUpdates.setButtons(null, null, "okButton", true);
|
|
gUpdates.wiz.getButton("finish").focus();
|
|
|
|
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CERT_ERRORS))
|
|
Services.prefs.clearUserPref(PREF_APP_UPDATE_CERT_ERRORS);
|
|
|
|
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDERRORS))
|
|
Services.prefs.clearUserPref(PREF_APP_UPDATE_BACKGROUNDERRORS);
|
|
|
|
if (gUpdates.update.errorCode == CERT_ATTR_CHECK_FAILED_HAS_UPDATE) {
|
|
document.getElementById("errorCertAttrHasUpdateLabel").hidden = false;
|
|
}
|
|
else {
|
|
if (gUpdates.update.errorCode == CERT_ATTR_CHECK_FAILED_NO_UPDATE)
|
|
document.getElementById("errorCertCheckNoUpdateLabel").hidden = false;
|
|
else
|
|
document.getElementById("genericBackgroundErrorLabel").hidden = false;
|
|
var manualURL = Services.urlFormatter.formatURLPref(PREF_APP_UPDATE_MANUAL_URL);
|
|
var errorLinkLabel = document.getElementById("errorExtraLinkLabel");
|
|
errorLinkLabel.value = manualURL;
|
|
errorLinkLabel.setAttribute("url", manualURL);
|
|
errorLinkLabel.hidden = false;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The "There was an error applying a partial patch" page.
|
|
*/
|
|
var gErrorPatchingPage = {
|
|
/**
|
|
* Initialize
|
|
*/
|
|
onPageShow: function() {
|
|
gUpdates.setButtons(null, null, "okButton", true);
|
|
},
|
|
|
|
onWizardNext: function() {
|
|
switch (gUpdates.update.selectedPatch.state) {
|
|
case STATE_PENDING:
|
|
case STATE_PENDING_SVC:
|
|
gUpdates.wiz.goTo("finished");
|
|
break;
|
|
case STATE_DOWNLOADING:
|
|
gUpdates.wiz.goTo("downloading");
|
|
break;
|
|
case STATE_DOWNLOAD_FAILED:
|
|
gUpdates.wiz.goTo("errors");
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The "Update has been downloaded" page. Shows information about what
|
|
* was downloaded.
|
|
*/
|
|
var gFinishedPage = {
|
|
/**
|
|
* Initialize
|
|
*/
|
|
onPageShow: function() {
|
|
gUpdates.setButtons("restartLaterButton", null, "restartNowButton",
|
|
true);
|
|
gUpdates.wiz.getButton("finish").focus();
|
|
},
|
|
|
|
/**
|
|
* Initialize the Wizard Page for a Background Source Event
|
|
*/
|
|
onPageShowBackground: function() {
|
|
this.onPageShow();
|
|
var updateFinishedName = document.getElementById("updateFinishedName");
|
|
updateFinishedName.value = gUpdates.update.name;
|
|
|
|
var link = document.getElementById("finishedBackgroundLink");
|
|
if (gUpdates.update.detailsURL) {
|
|
link.setAttribute("url", gUpdates.update.detailsURL);
|
|
// The details link is stealing focus so it is disabled by default and
|
|
// should only be enabled after onPageShow has been called.
|
|
link.disabled = false;
|
|
}
|
|
else
|
|
link.hidden = true;
|
|
|
|
if (getPref("getBoolPref", PREF_APP_UPDATE_TEST_LOOP, false)) {
|
|
setTimeout(function () {
|
|
gUpdates.wiz.getButton("finish").click();
|
|
}, UPDATE_TEST_LOOP_INTERVAL);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Called when the wizard finishes, i.e. the "Restart Now" button is
|
|
* clicked.
|
|
*/
|
|
onWizardFinish: function() {
|
|
// Do the restart
|
|
LOG("gFinishedPage" , "onWizardFinish - restarting the application");
|
|
|
|
// disable the "finish" (Restart) and "extra1" (Later) buttons
|
|
// because the Software Update wizard is still up at the point,
|
|
// and will remain up until we return and we close the
|
|
// window with a |window.close()| in wizard.xml
|
|
// (it was the firing the "wizardfinish" event that got us here.)
|
|
// This prevents the user from switching back
|
|
// to the Software Update dialog and clicking "Restart" or "Later"
|
|
// when dealing with the "confirm close" prompts.
|
|
// See bug #350299 for more details.
|
|
gUpdates.wiz.getButton("finish").disabled = true;
|
|
gUpdates.wiz.getButton("extra1").disabled = true;
|
|
|
|
// Notify all windows that an application quit has been requested.
|
|
var cancelQuit = CoC["@mozilla.org/supports-PRBool;1"].
|
|
createInstance(CoI.nsISupportsPRBool);
|
|
Services.obs.notifyObservers(cancelQuit, "quit-application-requested",
|
|
"restart");
|
|
|
|
// Something aborted the quit process.
|
|
if (cancelQuit.data)
|
|
return;
|
|
|
|
// If already in safe mode restart in safe mode (bug 327119)
|
|
if (Services.appinfo.inSafeMode) {
|
|
let env = CoC["@mozilla.org/process/environment;1"].
|
|
getService(CoI.nsIEnvironment);
|
|
env.set("MOZ_SAFE_MODE_RESTART", "1");
|
|
}
|
|
|
|
// Restart the application
|
|
CoC["@mozilla.org/toolkit/app-startup;1"].getService(CoI.nsIAppStartup).
|
|
quit(CoI.nsIAppStartup.eAttemptQuit | CoI.nsIAppStartup.eRestart);
|
|
},
|
|
|
|
/**
|
|
* When the user clicks the "Restart Later" instead of the Restart Now" button
|
|
* in the wizard after an update has been downloaded.
|
|
*/
|
|
onExtra1: function() {
|
|
// XXXrstrong - reminding the user to restart is broken (see bug 464835)
|
|
gUpdates.wiz.cancel();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The "Update was Installed Successfully" page.
|
|
*/
|
|
var gInstalledPage = {
|
|
/**
|
|
* Initialize
|
|
*/
|
|
onPageShow: function() {
|
|
var branding = document.getElementById("brandStrings");
|
|
try {
|
|
// whatsNewURL should just be a pref (bug 546609).
|
|
var url = branding.getFormattedString("whatsNewURL", [Services.appinfo.version]);
|
|
var whatsnewLink = document.getElementById("whatsnewLink");
|
|
whatsnewLink.setAttribute("url", url);
|
|
whatsnewLink.hidden = false;
|
|
}
|
|
catch (e) {
|
|
}
|
|
|
|
gUpdates.setButtons(null, null, "okButton", true);
|
|
gUpdates.wiz.getButton("finish").focus();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Callback for the Update Prompt to set the current page if an Update Wizard
|
|
* window is already found to be open.
|
|
* @param pageid
|
|
* The ID of the page to switch to
|
|
*/
|
|
function setCurrentPage(pageid) {
|
|
gUpdates.wiz.currentPage = document.getElementById(pageid);
|
|
}
|