mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Backed out 3 changesets (bug 1192924) for Android bustage CLOSED TREE
Backed out changeset 30d612836685 (bug 1192924) Backed out changeset a6860f880e01 (bug 1192924) Backed out changeset 10e1fa2cc23a (bug 1192924) --HG-- rename : toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system1_1.xpi => toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system1@tests.mozilla.org.xpi rename : toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system2_1.xpi => toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system2@tests.mozilla.org.xpi rename : toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system1_2.xpi => toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system1@tests.mozilla.org.xpi rename : toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system3_1.xpi => toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system3@tests.mozilla.org.xpi rename : toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system1_1_badcert.xpi => toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app3/features/system1@tests.mozilla.org.xpi
This commit is contained in:
parent
2e0c208b14
commit
468b697f04
@ -72,7 +72,7 @@ function init(aEvent)
|
||||
let defaults = Services.prefs.getDefaultBranch("");
|
||||
let channelLabel = document.getElementById("currentChannel");
|
||||
let currentChannelText = document.getElementById("currentChannelText");
|
||||
channelLabel.value = UpdateUtils.UpdateChannel;
|
||||
channelLabel.value = UpdateChannel.get();
|
||||
if (/^release($|\-)/.test(channelLabel.value))
|
||||
currentChannelText.hidden = true;
|
||||
#endif
|
||||
@ -89,8 +89,8 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/AddonManager.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
|
||||
var gAppUpdater;
|
||||
|
||||
|
@ -42,8 +42,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Log",
|
||||
"resource://gre/modules/Log.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
|
||||
"resource://gre/modules/AppConstants.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "Favicons",
|
||||
"@mozilla.org/browser/favicon-service;1",
|
||||
"mozIAsyncFavicons");
|
||||
@ -2918,7 +2918,7 @@ var BrowserOnClick = {
|
||||
version: 1,
|
||||
build: gAppInfo.appBuildID,
|
||||
product: gAppInfo.name,
|
||||
channel: UpdateUtils.UpdateChannel
|
||||
channel: UpdateChannel.get()
|
||||
}
|
||||
|
||||
let reportURL = Services.prefs.getCharPref("security.ssl.errorReporting.url");
|
||||
|
@ -18,6 +18,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Rect",
|
||||
"resource://gre/modules/Geometry.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
|
||||
var {
|
||||
links: gLinks,
|
||||
|
@ -60,7 +60,7 @@ add_task(function*() {
|
||||
|
||||
let updateChannel = null;
|
||||
try {
|
||||
updateChannel = Cu.import("resource://gre/modules/UpdateUtils.jsm", {}).UpdateUtils.UpdateChannel;
|
||||
updateChannel = Cu.import("resource://gre/modules/UpdateChannel.jsm", {}).UpdateChannel.get();
|
||||
} catch (ex) {}
|
||||
if (!updateChannel) {
|
||||
Assert.ok(!('updateChannel' in got.message.application),
|
||||
|
@ -25,8 +25,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "PageMetadata",
|
||||
"resource://gre/modules/PageMetadata.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
|
||||
"resource://gre/modules/PluralForm.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UITour",
|
||||
"resource:///modules/UITour.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Social",
|
||||
@ -810,7 +810,7 @@ function injectLoopAPI(targetWindow) {
|
||||
// which doesn't have what we need, so log an error.
|
||||
try {
|
||||
appVersionInfo = Cu.cloneInto({
|
||||
channel: UpdateUtils.UpdateChannel,
|
||||
channel: UpdateChannel.get(),
|
||||
version: appInfo.version,
|
||||
OS: appInfo.OS
|
||||
}, targetWindow);
|
||||
|
@ -124,8 +124,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
|
||||
"resource:///modules/ContentSearch.jsm");
|
||||
|
||||
#ifdef E10S_TESTING_ONLY
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
@ -2977,7 +2977,7 @@ var E10SUINotification = {
|
||||
checkStatus: function() {
|
||||
let skipE10sChecks = false;
|
||||
try {
|
||||
let updateChannel = UpdateUtils.UpdateChannel;
|
||||
let updateChannel = UpdateChannel.get();
|
||||
let channelAuthorized = updateChannel == "nightly" || updateChannel == "aurora";
|
||||
|
||||
skipE10sChecks = !channelAuthorized ||
|
||||
|
@ -165,8 +165,8 @@ var gMainPane = {
|
||||
}
|
||||
|
||||
let tmp = {};
|
||||
Components.utils.import("resource://gre/modules/UpdateUtils.jsm", tmp);
|
||||
if (!e10sCheckbox.checked && tmp.UpdateUtils.UpdateChannel != "default") {
|
||||
Components.utils.import("resource://gre/modules/UpdateChannel.jsm", tmp);
|
||||
if (!e10sCheckbox.checked && tmp.UpdateChannel.get() != "default") {
|
||||
Services.prefs.setBoolPref("browser.requestE10sFeedback", true);
|
||||
Services.prompt.alert(window, brandName, bundle.getString("e10sFeedbackAfterRestart"));
|
||||
}
|
||||
|
@ -20,8 +20,8 @@ Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/AsyncShutdown.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
|
||||
"resource://gre/modules/AddonManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate",
|
||||
@ -275,7 +275,7 @@ Experiments.Policy.prototype = {
|
||||
},
|
||||
|
||||
updatechannel: function () {
|
||||
return UpdateUtils.UpdateChannel;
|
||||
return UpdateChannel.get();
|
||||
},
|
||||
|
||||
locale: function () {
|
||||
|
@ -26,8 +26,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "OS",
|
||||
"resource://gre/modules/osfile.jsm")
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "eTLD",
|
||||
"@mozilla.org/network/effective-tld-service;1",
|
||||
"nsIEffectiveTLDService");
|
||||
@ -280,7 +280,7 @@ var DirectoryLinksProvider = {
|
||||
_fetchAndCacheLinks: function DirectoryLinksProvider_fetchAndCacheLinks(uri) {
|
||||
// Replace with the same display locale used for selecting links data
|
||||
uri = uri.replace("%LOCALE%", this.locale);
|
||||
uri = uri.replace("%CHANNEL%", UpdateUtils.UpdateChannel);
|
||||
uri = uri.replace("%CHANNEL%", UpdateChannel.get());
|
||||
|
||||
return this._downloadJsonData(uri).then(json => {
|
||||
return OS.File.writeAtomic(this._directoryFilePath, json, {tmpPath: this._directoryFilePath + ".tmp"});
|
||||
|
@ -22,7 +22,7 @@ const FLOATY_ICON_XXHDPI = "chrome://browser/skin/images/icon_floaty_xxhdpi.png"
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Messaging.jsm");
|
||||
Cu.import("resource://gre/modules/UpdateUtils.jsm");
|
||||
Cu.import("resource://gre/modules/UpdateChannel.jsm");
|
||||
document.addEventListener("DOMContentLoaded", init, false);
|
||||
|
||||
function dump(a) {
|
||||
@ -138,7 +138,7 @@ function sendFeedback(aEvent) {
|
||||
data["platform"] = Services.appinfo.OS;
|
||||
data["version"] = Services.appinfo.version;
|
||||
data["locale"] = Services.locale.getSystemLocale().getCategory("NSILOCALE_CTYPE");
|
||||
data["channel"] = UpdateUtils.UpdateChannel;
|
||||
data["channel"] = UpdateChannel.get();
|
||||
|
||||
// Source field is added only when Fennec prompts the user.
|
||||
let getParam = window.location.href.split("?");
|
||||
|
@ -27,7 +27,7 @@ XPCOMUtils.defineLazyModuleGetter(
|
||||
this, "Promise", "resource://gre/modules/Promise.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(
|
||||
this, "UpdateUtils", "resource://gre/modules/UpdateUtils.jsm");
|
||||
this, "UpdateChannel", "resource://gre/modules/UpdateChannel.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
this, "gUpdateTimer", "@mozilla.org/updates/timer-manager;1", "nsIUpdateTimerManager");
|
||||
@ -192,7 +192,7 @@ this.UserAgentUpdates = {
|
||||
"%APP_VERSION%": function() { return gApp.version; },
|
||||
"%BUILD_ID%": function() { return gApp.appBuildID; },
|
||||
"%OS%": function() { return gApp.OS; },
|
||||
"%CHANNEL%": function() { return UpdateUtils.UpdateChannel; },
|
||||
"%CHANNEL%": function() { return UpdateChannel.get(); },
|
||||
"%DISTRIBUTION%": function() { return this._getPref(PREF_APP_DISTRIBUTION, ""); },
|
||||
"%DISTRIBUTION_VERSION%": function() { return this._getPref(PREF_APP_DISTRIBUTION_VERSION, ""); },
|
||||
};
|
||||
|
@ -25,7 +25,7 @@ Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://services-common/utils.js");
|
||||
Cu.import("resource://gre/modules/UpdateUtils.jsm");
|
||||
Cu.import("resource://gre/modules/UpdateChannel.jsm");
|
||||
|
||||
// The current policy version number. If the version number stored in the prefs
|
||||
// is smaller than this, data upload will be disabled until the user is re-notified
|
||||
@ -388,7 +388,7 @@ this.DataReportingPolicy.prototype = Object.freeze({
|
||||
*/
|
||||
get minimumPolicyVersion() {
|
||||
// First check if the current channel has an ove
|
||||
let channel = UpdateUtils.getUpdateChannel(false);
|
||||
let channel = UpdateChannel.get(false);
|
||||
let channelPref = this._prefs.get("minimumPolicyVersion.channel-" + channel);
|
||||
return channelPref !== undefined ?
|
||||
channelPref : this._prefs.get("minimumPolicyVersion", 1);
|
||||
|
@ -8,7 +8,7 @@ const {utils: Cu} = Components;
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/services/datareporting/policy.jsm");
|
||||
Cu.import("resource://testing-common/services/datareporting/mocks.jsm");
|
||||
Cu.import("resource://gre/modules/UpdateUtils.jsm");
|
||||
Cu.import("resource://gre/modules/UpdateChannel.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
function getPolicy(name,
|
||||
@ -23,7 +23,7 @@ function getPolicy(name,
|
||||
, defaultBranch: true });
|
||||
defaultPolicyPrefs.set("currentPolicyVersion", aCurrentPolicyVersion);
|
||||
defaultPolicyPrefs.set("minimumPolicyVersion", aMinimumPolicyVersion);
|
||||
let branchOverridePrefName = "minimumPolicyVersion.channel-" + UpdateUtils.getUpdateChannel(false);
|
||||
let branchOverridePrefName = "minimumPolicyVersion.channel-" + UpdateChannel.get(false);
|
||||
if (aBranchMinimumVersionOverride !== undefined)
|
||||
defaultPolicyPrefs.set(branchOverridePrefName, aBranchMinimumVersionOverride);
|
||||
else
|
||||
|
@ -30,8 +30,8 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryController",
|
||||
"resource://gre/modules/TelemetryController.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
|
||||
// Oldest year to allow in date preferences. This module was implemented in
|
||||
// 2012 and no dates older than that should be encountered.
|
||||
@ -1103,7 +1103,7 @@ AbstractHealthReporter.prototype = Object.freeze({
|
||||
}
|
||||
|
||||
try {
|
||||
out["updateChannel"] = UpdateUtils.UpdateChannel;
|
||||
out["updateChannel"] = UpdateChannel.get();
|
||||
} catch (ex) {
|
||||
this._log.warn("Could not obtain update channel: " +
|
||||
CommonUtils.exceptionStr(ex));
|
||||
|
@ -46,8 +46,8 @@ Cu.import("resource://services-common/utils.js");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
|
||||
"resource://gre/modules/AddonManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesDBUtils",
|
||||
"resource://gre/modules/PlacesDBUtils.jsm");
|
||||
|
||||
@ -333,7 +333,7 @@ AppInfoProvider.prototype = Object.freeze({
|
||||
}
|
||||
|
||||
try {
|
||||
yield m.setLastText("updateChannel", UpdateUtils.UpdateChannel);
|
||||
yield m.setLastText("updateChannel", UpdateChannel.get());
|
||||
} catch (ex) {
|
||||
this._log.warn("Could not obtain update channel: " +
|
||||
CommonUtils.exceptionStr(ex));
|
||||
|
@ -95,7 +95,6 @@ user_pref("extensions.update.url", "http://%(server)s/extensions-dummy/updateURL
|
||||
user_pref("extensions.update.background.url", "http://%(server)s/extensions-dummy/updateBackgroundURL");
|
||||
user_pref("extensions.blocklist.url", "http://%(server)s/extensions-dummy/blocklistURL");
|
||||
user_pref("extensions.hotfix.url", "http://%(server)s/extensions-dummy/hotfixURL");
|
||||
user_pref("extensions.systemAddon.update.url", "http://%(server)s/dummy-system-addons.xml");
|
||||
// Turn off extension updates so they don't bother tests
|
||||
user_pref("extensions.update.enabled", false);
|
||||
// Make sure opening about:addons won't hit the network
|
||||
|
@ -140,8 +140,6 @@ DEFAULTS = dict(
|
||||
'http://127.0.0.1/plugins-dummy/updateCheckURL',
|
||||
'media.gmp-manager.url':
|
||||
'http://127.0.0.1/gmpmanager-dummy/update.xml',
|
||||
'extensions.systemAddon.update.url':
|
||||
'http://127.0.0.1/dummy-system-addons.xml',
|
||||
'media.navigator.enabled': True,
|
||||
'media.peerconnection.enabled': True,
|
||||
'media.navigator.permission.disabled': True,
|
||||
|
@ -1505,7 +1505,6 @@ try {
|
||||
.getService(Components.interfaces.nsIPrefBranch);
|
||||
|
||||
prefs.setCharPref("media.gmp-manager.url.override", "http://%(server)s/dummy-gmp-manager.xml");
|
||||
prefs.setCharPref("extensions.systemAddon.update.url", "http://%(server)s/dummy-system-addons.xml");
|
||||
prefs.setCharPref("browser.selfsupport.url", "https://%(server)s/selfsupport-dummy/");
|
||||
prefs.setCharPref("toolkit.telemetry.server", "https://%(server)s/telemetry-dummy");
|
||||
prefs.setCharPref("browser.search.geoip.url", "https://%(server)s/geoip-dummy");
|
||||
|
@ -80,8 +80,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment",
|
||||
"resource://gre/modules/TelemetryEnvironment.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SessionRecorder",
|
||||
"resource://gre/modules/SessionRecorder.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryArchive",
|
||||
"resource://gre/modules/TelemetryArchive.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySession",
|
||||
@ -412,7 +412,7 @@ var Impl = {
|
||||
|
||||
let updateChannel = null;
|
||||
try {
|
||||
updateChannel = UpdateUtils.getUpdateChannel(false);
|
||||
updateChannel = UpdateChannel.get(false);
|
||||
} catch (e) {
|
||||
this._log.trace("assemblePing - Unable to get update channel.", e);
|
||||
}
|
||||
|
@ -32,8 +32,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
|
||||
#endif
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ProfileAge",
|
||||
"resource://gre/modules/ProfileAge.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
|
||||
const CHANGE_THROTTLE_INTERVAL_MS = 5 * 60 * 1000;
|
||||
|
||||
@ -1007,7 +1007,7 @@ EnvironmentCache.prototype = {
|
||||
_updateSettings: function () {
|
||||
let updateChannel = null;
|
||||
try {
|
||||
updateChannel = UpdateUtils.getUpdateChannel(false);
|
||||
updateChannel = UpdateChannel.get(false);
|
||||
} catch (e) {}
|
||||
|
||||
this._currentEnvironment.settings = {
|
||||
|
@ -19,8 +19,8 @@ Cu.import("resource://services-common/observers.js", this);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySend",
|
||||
"resource://gre/modules/TelemetrySend.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
|
||||
const LOGGER_NAME = "Toolkit.Telemetry";
|
||||
const LOGGER_PREFIX = "TelemetryReportingPolicy::";
|
||||
@ -251,7 +251,7 @@ var TelemetryReportingPolicyImpl = {
|
||||
// use the general minimum policy version.
|
||||
let channel = "";
|
||||
try {
|
||||
channel = UpdateUtils.getUpdateChannel(false);
|
||||
channel = UpdateChannel.get(false);
|
||||
} catch(e) {
|
||||
this._log.error("minimumPolicyVersion - Unable to retrieve the current channel.");
|
||||
return minPolicyVersion;
|
||||
|
@ -143,6 +143,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "ThirdPartyCookieProbe",
|
||||
"resource://gre/modules/ThirdPartyCookieProbe.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry",
|
||||
"resource://gre/modules/UITelemetry.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment",
|
||||
"resource://gre/modules/TelemetryEnvironment.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
|
||||
|
@ -25,8 +25,8 @@ Cu.import("resource://gre/modules/Services.jsm");
|
||||
const PREF_APP_DISTRIBUTION = "distribution.id";
|
||||
const PREF_APP_DISTRIBUTION_VERSION = "distribution.version";
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
|
||||
function nsURLFormatterService() {
|
||||
XPCOMUtils.defineLazyGetter(this, "appInfo", function UFS_appInfo() {
|
||||
@ -111,7 +111,7 @@ nsURLFormatterService.prototype = {
|
||||
XPCOMABI: function() this.ABI,
|
||||
BUILD_TARGET: function() this.appInfo.OS + "_" + this.ABI,
|
||||
OS_VERSION: function() this.OSVersion,
|
||||
CHANNEL: function() UpdateUtils.UpdateChannel,
|
||||
CHANNEL: function() UpdateChannel.get(),
|
||||
MOZILLA_API_KEY: function() "@MOZ_MOZILLA_API_KEY@",
|
||||
GOOGLE_API_KEY: function() "@MOZ_GOOGLE_API_KEY@",
|
||||
GOOGLE_OAUTH_API_CLIENTID:function() "@MOZ_GOOGLE_OAUTH_API_CLIENTID@",
|
||||
|
@ -3,10 +3,8 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const { utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/UpdateUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/Preferences.jsm");
|
||||
Components.utils.import("resource://gre/modules/UpdateChannel.jsm");
|
||||
|
||||
const PREF_APP_UPDATE_CHANNEL = "app.update.channel";
|
||||
const TEST_CHANNEL = "TestChannel";
|
||||
@ -15,24 +13,24 @@ const TEST_PARTNER_A = "TestPartnerA";
|
||||
const PREF_PARTNER_B = "app.partner.test_partner_b";
|
||||
const TEST_PARTNER_B = "TestPartnerB";
|
||||
|
||||
add_task(function* test_updatechannel() {
|
||||
function test_get() {
|
||||
let defaultPrefs = new Preferences({ defaultBranch: true });
|
||||
let currentChannel = defaultPrefs.get(PREF_APP_UPDATE_CHANNEL);
|
||||
|
||||
do_check_eq(UpdateUtils.UpdateChannel, currentChannel);
|
||||
do_check_eq(UpdateUtils.getUpdateChannel(true), currentChannel);
|
||||
do_check_eq(UpdateUtils.getUpdateChannel(false), currentChannel);
|
||||
do_check_eq(UpdateChannel.get(), currentChannel);
|
||||
do_check_eq(UpdateChannel.get(false), currentChannel);
|
||||
|
||||
defaultPrefs.set(PREF_APP_UPDATE_CHANNEL, TEST_CHANNEL);
|
||||
do_check_eq(UpdateUtils.UpdateChannel, TEST_CHANNEL);
|
||||
do_check_eq(UpdateUtils.getUpdateChannel(true), TEST_CHANNEL);
|
||||
do_check_eq(UpdateUtils.getUpdateChannel(false), TEST_CHANNEL);
|
||||
do_check_eq(UpdateChannel.get(), TEST_CHANNEL);
|
||||
do_check_eq(UpdateChannel.get(false), TEST_CHANNEL);
|
||||
|
||||
defaultPrefs.set(PREF_PARTNER_A, TEST_PARTNER_A);
|
||||
defaultPrefs.set(PREF_PARTNER_B, TEST_PARTNER_B);
|
||||
do_check_eq(UpdateUtils.UpdateChannel,
|
||||
do_check_eq(UpdateChannel.get(),
|
||||
TEST_CHANNEL + "-cck-" + TEST_PARTNER_A + "-" + TEST_PARTNER_B);
|
||||
do_check_eq(UpdateUtils.getUpdateChannel(true),
|
||||
TEST_CHANNEL + "-cck-" + TEST_PARTNER_A + "-" + TEST_PARTNER_B);
|
||||
do_check_eq(UpdateUtils.getUpdateChannel(false), TEST_CHANNEL);
|
||||
});
|
||||
do_check_eq(UpdateChannel.get(false), TEST_CHANNEL);
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
test_get();
|
||||
}
|
@ -4,3 +4,4 @@ tail =
|
||||
skip-if = toolkit == 'gonk'
|
||||
|
||||
[test_contentAreaUtils.js]
|
||||
[test_updateChannelModule.js]
|
||||
|
@ -8,6 +8,10 @@ this.EXPORTED_SYMBOLS = [];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu, manager: Cm} =
|
||||
Components;
|
||||
// Chunk size for the incremental downloader
|
||||
const DOWNLOAD_CHUNK_BYTES_SIZE = 300000;
|
||||
// Incremental downloader interval
|
||||
const DOWNLOAD_INTERVAL = 0;
|
||||
// 1 day default
|
||||
const DEFAULT_SECONDS_BETWEEN_CHECKS = 60 * 60 * 24;
|
||||
|
||||
@ -26,12 +30,15 @@ Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/ctypes.jsm");
|
||||
Cu.import("resource://gre/modules/GMPUtils.jsm");
|
||||
Cu.import("resource://gre/modules/addons/ProductAddonChecker.jsm");
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["GMPInstallManager", "GMPExtractor", "GMPDownloader",
|
||||
"GMPAddon"];
|
||||
|
||||
var gLocale = null;
|
||||
|
||||
// Shared code for suppressing bad cert dialogs
|
||||
XPCOMUtils.defineLazyGetter(this, "gCertUtils", function() {
|
||||
let temp = { };
|
||||
@ -39,8 +46,18 @@ XPCOMUtils.defineLazyGetter(this, "gCertUtils", function() {
|
||||
return temp;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
|
||||
/**
|
||||
* Number of milliseconds after which we need to cancel `checkForAddons`.
|
||||
*
|
||||
* Bug 1087674 suggests that the XHR we use in `checkForAddons` may
|
||||
* never terminate in presence of network nuisances (e.g. strange
|
||||
* antivirus behavior). This timeout is a defensive measure to ensure
|
||||
* that we fail cleanly in such case.
|
||||
*/
|
||||
const CHECK_FOR_ADDONS_TIMEOUT_DELAY_MS = 20000;
|
||||
|
||||
function getScopedLogger(prefix) {
|
||||
// `PARENT_LOGGER_ID.` being passed here effectively links this logger
|
||||
@ -48,6 +65,138 @@ function getScopedLogger(prefix) {
|
||||
return Log.repository.getLoggerWithMessagePrefix("Toolkit.GMP", prefix + " ");
|
||||
}
|
||||
|
||||
// This is copied directly from nsUpdateService.js
|
||||
// It is used for calculating the URL string w/ var replacement.
|
||||
// TODO: refactor this out somewhere else
|
||||
XPCOMUtils.defineLazyGetter(this, "gOSVersion", function aus_gOSVersion() {
|
||||
let osVersion;
|
||||
try {
|
||||
osVersion = Services.sysinfo.getProperty("name") + " " +
|
||||
Services.sysinfo.getProperty("version");
|
||||
}
|
||||
catch (e) {
|
||||
LOG("gOSVersion - OS Version unknown: updates are not possible.");
|
||||
}
|
||||
|
||||
if (osVersion) {
|
||||
if (AppConstants.platform == "win") {
|
||||
const BYTE = ctypes.uint8_t;
|
||||
const WORD = ctypes.uint16_t;
|
||||
const DWORD = ctypes.uint32_t;
|
||||
const WCHAR = ctypes.char16_t;
|
||||
const BOOL = ctypes.int;
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
|
||||
const SZCSDVERSIONLENGTH = 128;
|
||||
const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
|
||||
[
|
||||
{dwOSVersionInfoSize: DWORD},
|
||||
{dwMajorVersion: DWORD},
|
||||
{dwMinorVersion: DWORD},
|
||||
{dwBuildNumber: DWORD},
|
||||
{dwPlatformId: DWORD},
|
||||
{szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
|
||||
{wServicePackMajor: WORD},
|
||||
{wServicePackMinor: WORD},
|
||||
{wSuiteMask: WORD},
|
||||
{wProductType: BYTE},
|
||||
{wReserved: BYTE}
|
||||
]);
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
|
||||
const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
|
||||
[
|
||||
{wProcessorArchitecture: WORD},
|
||||
{wReserved: WORD},
|
||||
{dwPageSize: DWORD},
|
||||
{lpMinimumApplicationAddress: ctypes.voidptr_t},
|
||||
{lpMaximumApplicationAddress: ctypes.voidptr_t},
|
||||
{dwActiveProcessorMask: DWORD.ptr},
|
||||
{dwNumberOfProcessors: DWORD},
|
||||
{dwProcessorType: DWORD},
|
||||
{dwAllocationGranularity: DWORD},
|
||||
{wProcessorLevel: WORD},
|
||||
{wProcessorRevision: WORD}
|
||||
]);
|
||||
|
||||
let kernel32 = false;
|
||||
try {
|
||||
kernel32 = ctypes.open("Kernel32");
|
||||
} catch (e) {
|
||||
LOG("gOSVersion - Unable to open kernel32! " + e);
|
||||
osVersion += ".unknown (unknown)";
|
||||
}
|
||||
|
||||
if(kernel32) {
|
||||
try {
|
||||
// Get Service pack info
|
||||
try {
|
||||
let GetVersionEx = kernel32.declare("GetVersionExW",
|
||||
ctypes.default_abi,
|
||||
BOOL,
|
||||
OSVERSIONINFOEXW.ptr);
|
||||
let winVer = OSVERSIONINFOEXW();
|
||||
winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
|
||||
|
||||
if(0 !== GetVersionEx(winVer.address())) {
|
||||
osVersion += "." + winVer.wServicePackMajor
|
||||
+ "." + winVer.wServicePackMinor;
|
||||
} else {
|
||||
LOG("gOSVersion - Unknown failure in GetVersionEX (returned 0)");
|
||||
osVersion += ".unknown";
|
||||
}
|
||||
} catch (e) {
|
||||
LOG("gOSVersion - error getting service pack information. Exception: " + e);
|
||||
osVersion += ".unknown";
|
||||
}
|
||||
|
||||
// Get processor architecture
|
||||
let arch = "unknown";
|
||||
try {
|
||||
let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo",
|
||||
ctypes.default_abi,
|
||||
ctypes.void_t,
|
||||
SYSTEM_INFO.ptr);
|
||||
let sysInfo = SYSTEM_INFO();
|
||||
// Default to unknown
|
||||
sysInfo.wProcessorArchitecture = 0xffff;
|
||||
|
||||
GetNativeSystemInfo(sysInfo.address());
|
||||
switch(sysInfo.wProcessorArchitecture) {
|
||||
case 9:
|
||||
arch = "x64";
|
||||
break;
|
||||
case 6:
|
||||
arch = "IA64";
|
||||
break;
|
||||
case 0:
|
||||
arch = "x86";
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
LOG("gOSVersion - error getting processor architecture. Exception: " + e);
|
||||
} finally {
|
||||
osVersion += " (" + arch + ")";
|
||||
}
|
||||
} finally {
|
||||
kernel32.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
|
||||
}
|
||||
catch (e) {
|
||||
// Not all platforms have a secondary widget library, so an error is nothing to worry about.
|
||||
}
|
||||
osVersion = encodeURIComponent(osVersion);
|
||||
}
|
||||
return osVersion;
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides an easy API for downloading and installing GMP Addons
|
||||
*/
|
||||
@ -72,8 +221,24 @@ GMPInstallManager.prototype = {
|
||||
log.info("Using url: " + url);
|
||||
}
|
||||
|
||||
url = UpdateUtils.formatUpdateURL(url);
|
||||
|
||||
url =
|
||||
url.replace(/%PRODUCT%/g, Services.appinfo.name)
|
||||
.replace(/%VERSION%/g, Services.appinfo.version)
|
||||
.replace(/%BUILD_ID%/g, Services.appinfo.appBuildID)
|
||||
.replace(/%BUILD_TARGET%/g, Services.appinfo.OS + "_" + GMPUtils.ABI())
|
||||
.replace(/%OS_VERSION%/g, gOSVersion);
|
||||
if (/%LOCALE%/.test(url)) {
|
||||
// TODO: Get the real local, does it actually matter for GMP plugins?
|
||||
url = url.replace(/%LOCALE%/g, "en-US");
|
||||
}
|
||||
url =
|
||||
url.replace(/%CHANNEL%/g, UpdateChannel.get())
|
||||
.replace(/%PLATFORM_VERSION%/g, Services.appinfo.platformVersion)
|
||||
.replace(/%DISTRIBUTION%/g,
|
||||
GMPPrefs.get(GMPPrefs.KEY_APP_DISTRIBUTION))
|
||||
.replace(/%DISTRIBUTION_VERSION%/g,
|
||||
GMPPrefs.get(GMPPrefs.KEY_APP_DISTRIBUTION_VERSION))
|
||||
.replace(/\+/g, "%2B");
|
||||
log.info("Using url (with replacement): " + url);
|
||||
return url;
|
||||
},
|
||||
@ -95,27 +260,38 @@ GMPInstallManager.prototype = {
|
||||
this._deferred = Promise.defer();
|
||||
let url = this._getURL();
|
||||
|
||||
let allowNonBuiltIn = true;
|
||||
let certs = null;
|
||||
if (!Services.prefs.prefHasUserValue(GMPPrefs.KEY_URL_OVERRIDE)) {
|
||||
allowNonBuiltIn = !GMPPrefs.get(GMPPrefs.KEY_CERT_REQUIREBUILTIN, true);
|
||||
if (GMPPrefs.get(GMPPrefs.KEY_CERT_CHECKATTRS, true)) {
|
||||
certs = gCertUtils.readCertPrefs(GMPPrefs.KEY_CERTS_BRANCH);
|
||||
}
|
||||
this._request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
||||
createInstance(Ci.nsISupports);
|
||||
// This is here to let unit test code override XHR
|
||||
if (this._request.wrappedJSObject) {
|
||||
this._request = this._request.wrappedJSObject;
|
||||
}
|
||||
this._request.open("GET", url, true);
|
||||
let allowNonBuiltIn = !GMPPrefs.get(GMPPrefs.KEY_CERT_CHECKATTRS, true);
|
||||
this._request.channel.notificationCallbacks =
|
||||
new gCertUtils.BadCertHandler(allowNonBuiltIn);
|
||||
// Prevent the request from reading from the cache.
|
||||
this._request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
|
||||
// Prevent the request from writing to the cache.
|
||||
this._request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
|
||||
|
||||
ProductAddonChecker.getProductAddonList(url, allowNonBuiltIn, certs).then((addons) => {
|
||||
if (!addons) {
|
||||
this._deferred.resolve([]);
|
||||
}
|
||||
else {
|
||||
this._deferred.resolve([for (a of addons) new GMPAddon(a)]);
|
||||
}
|
||||
delete this._deferred;
|
||||
}, (ex) => {
|
||||
this._deferred.reject(ex);
|
||||
delete this._deferred;
|
||||
});
|
||||
this._request.overrideMimeType("text/xml");
|
||||
// The Cache-Control header is only interpreted by proxies and the
|
||||
// final destination. It does not help if a resource is already
|
||||
// cached locally.
|
||||
this._request.setRequestHeader("Cache-Control", "no-cache");
|
||||
// HTTP/1.0 servers might not implement Cache-Control and
|
||||
// might only implement Pragma: no-cache
|
||||
this._request.setRequestHeader("Pragma", "no-cache");
|
||||
|
||||
this._request.timeout = CHECK_FOR_ADDONS_TIMEOUT_DELAY_MS;
|
||||
this._request.addEventListener("error", event => this.onFailXML("onErrorXML", event), false);
|
||||
this._request.addEventListener("abort", event => this.onFailXML("onAbortXML", event), false);
|
||||
this._request.addEventListener("timeout", event => this.onFailXML("onTimeoutXML", event), false);
|
||||
this._request.addEventListener("load", event => this.onLoadXML(event), false);
|
||||
|
||||
log.info("sending request to: " + url);
|
||||
this._request.send(null);
|
||||
|
||||
return this._deferred.promise;
|
||||
},
|
||||
@ -317,6 +493,132 @@ GMPInstallManager.prototype = {
|
||||
* This is useful for tests.
|
||||
*/
|
||||
overrideLeaveDownloadedZip: false,
|
||||
|
||||
/**
|
||||
* The XMLHttpRequest succeeded and the document was loaded.
|
||||
* @param event The nsIDOMEvent for the load
|
||||
*/
|
||||
onLoadXML: function(event) {
|
||||
let log = getScopedLogger("GMPInstallManager.onLoadXML");
|
||||
try {
|
||||
log.info("request completed downloading document");
|
||||
let certs = null;
|
||||
if (!Services.prefs.prefHasUserValue(GMPPrefs.KEY_URL_OVERRIDE) &&
|
||||
GMPPrefs.get(GMPPrefs.KEY_CERT_CHECKATTRS, true)) {
|
||||
certs = gCertUtils.readCertPrefs(GMPPrefs.KEY_CERTS_BRANCH);
|
||||
}
|
||||
|
||||
let allowNonBuiltIn = !GMPPrefs.get(GMPPrefs.KEY_CERT_REQUIREBUILTIN,
|
||||
true);
|
||||
log.info("allowNonBuiltIn: " + allowNonBuiltIn);
|
||||
|
||||
gCertUtils.checkCert(this._request.channel, allowNonBuiltIn, certs);
|
||||
|
||||
this.parseResponseXML();
|
||||
} catch (ex) {
|
||||
log.error("could not load xml: " + ex);
|
||||
this._deferred.reject({
|
||||
target: event.target,
|
||||
status: this._getChannelStatus(event.target),
|
||||
message: "" + ex,
|
||||
});
|
||||
delete this._deferred;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the status code for the XMLHttpRequest
|
||||
*/
|
||||
_getChannelStatus: function(request) {
|
||||
let log = getScopedLogger("GMPInstallManager._getChannelStatus");
|
||||
let status = null;
|
||||
try {
|
||||
status = request.status;
|
||||
log.info("request.status is: " + request.status);
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
|
||||
if (status == null) {
|
||||
status = request.channel.QueryInterface(Ci.nsIRequest).status;
|
||||
}
|
||||
return status;
|
||||
},
|
||||
|
||||
/**
|
||||
* There was an error of some kind during the XMLHttpRequest. This
|
||||
* error may have been caused by external factors (e.g. network
|
||||
* issues) or internally (by a timeout).
|
||||
*
|
||||
* @param event The nsIDOMEvent for the error
|
||||
*/
|
||||
onFailXML: function(failure, event) {
|
||||
let log = getScopedLogger("GMPInstallManager.onFailXML " + failure);
|
||||
let request = event.target;
|
||||
let status = this._getChannelStatus(request);
|
||||
let message = "request.status: " + status + " (" + event.type + ")";
|
||||
log.warn(message);
|
||||
this._deferred.reject({
|
||||
target: request,
|
||||
status: status,
|
||||
message: message
|
||||
});
|
||||
delete this._deferred;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an array of GMPAddon objects discovered by the update check.
|
||||
* Or returns an empty array if there were any problems with parsing.
|
||||
* If there's an error, it will be logged if logging is enabled.
|
||||
*/
|
||||
parseResponseXML: function() {
|
||||
try {
|
||||
let log = getScopedLogger("GMPInstallManager.parseResponseXML");
|
||||
let updatesElement = this._request.responseXML.documentElement;
|
||||
if (!updatesElement) {
|
||||
let message = "empty updates document";
|
||||
log.warn(message);
|
||||
this._deferred.reject({
|
||||
target: this._request,
|
||||
message: message
|
||||
});
|
||||
delete this._deferred;
|
||||
return;
|
||||
}
|
||||
|
||||
if (updatesElement.nodeName != "updates") {
|
||||
let message = "got node name: " + updatesElement.nodeName +
|
||||
", expected: updates";
|
||||
log.warn(message);
|
||||
this._deferred.reject({
|
||||
target: this._request,
|
||||
message: message
|
||||
});
|
||||
delete this._deferred;
|
||||
return;
|
||||
}
|
||||
|
||||
const ELEMENT_NODE = Ci.nsIDOMNode.ELEMENT_NODE;
|
||||
let gmpResults = [];
|
||||
for (let i = 0; i < updatesElement.childNodes.length; ++i) {
|
||||
let updatesChildElement = updatesElement.childNodes.item(i);
|
||||
if (updatesChildElement.nodeType != ELEMENT_NODE) {
|
||||
continue;
|
||||
}
|
||||
if (updatesChildElement.localName == "addons") {
|
||||
gmpResults = GMPAddon.parseGMPAddonsNode(updatesChildElement);
|
||||
}
|
||||
}
|
||||
this._deferred.resolve(gmpResults);
|
||||
delete this._deferred;
|
||||
} catch (e) {
|
||||
this._deferred.reject({
|
||||
target: this._request,
|
||||
message: e
|
||||
});
|
||||
delete this._deferred;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@ -324,16 +626,49 @@ GMPInstallManager.prototype = {
|
||||
* GMPAddon objects are returns from GMPInstallManager.checkForAddons
|
||||
* GMPAddon objects can also be used in calls to GMPInstallManager.installAddon
|
||||
*
|
||||
* @param addon The ProductAddonChecker `addon` object
|
||||
* @param gmpAddon The AUS response XML's DOM element `addon`
|
||||
*/
|
||||
function GMPAddon(addon) {
|
||||
function GMPAddon(gmpAddon) {
|
||||
let log = getScopedLogger("GMPAddon.constructor");
|
||||
for (let name of Object.keys(addon)) {
|
||||
this[name] = addon[name];
|
||||
}
|
||||
gmpAddon.QueryInterface(Ci.nsIDOMElement);
|
||||
["id", "URL", "hashFunction",
|
||||
"hashValue", "version", "size"].forEach(name => {
|
||||
if (gmpAddon.hasAttribute(name)) {
|
||||
this[name] = gmpAddon.getAttribute(name);
|
||||
}
|
||||
});
|
||||
this.size = Number(this.size) || undefined;
|
||||
log.info ("Created new addon: " + this.toString());
|
||||
}
|
||||
/**
|
||||
* Parses an XML GMP addons node from AUS into an array
|
||||
* @param addonsElement An nsIDOMElement compatible node with XML from AUS
|
||||
* @return An array of GMPAddon results
|
||||
*/
|
||||
GMPAddon.parseGMPAddonsNode = function(addonsElement) {
|
||||
let log = getScopedLogger("GMPAddon.parseGMPAddonsNode");
|
||||
let gmpResults = [];
|
||||
if (addonsElement.localName !== "addons") {
|
||||
return;
|
||||
}
|
||||
|
||||
addonsElement.QueryInterface(Ci.nsIDOMElement);
|
||||
let addonCount = addonsElement.childNodes.length;
|
||||
for (let i = 0; i < addonCount; ++i) {
|
||||
let addonElement = addonsElement.childNodes.item(i);
|
||||
if (addonElement.localName !== "addon") {
|
||||
continue;
|
||||
}
|
||||
addonElement.QueryInterface(Ci.nsIDOMElement);
|
||||
try {
|
||||
gmpResults.push(new GMPAddon(addonElement));
|
||||
} catch (e) {
|
||||
log.warn("invalid addon: " + e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return gmpResults;
|
||||
};
|
||||
GMPAddon.prototype = {
|
||||
/**
|
||||
* Returns a string representation of the addon
|
||||
@ -464,7 +799,38 @@ function GMPDownloader(gmpAddon)
|
||||
{
|
||||
this._gmpAddon = gmpAddon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the file hash of fileToHash with the specified hash function
|
||||
* @param hashFunctionName A hash function name such as sha512
|
||||
* @param fileToHash An nsIFile to hash
|
||||
* @return a promise which resolve to a digest in binary hex format
|
||||
*/
|
||||
GMPDownloader.computeHash = function(hashFunctionName, fileToHash) {
|
||||
let log = getScopedLogger("GMPDownloader.computeHash");
|
||||
let digest;
|
||||
let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
|
||||
createInstance(Ci.nsIFileInputStream);
|
||||
fileStream.init(fileToHash, FileUtils.MODE_RDONLY,
|
||||
FileUtils.PERMS_FILE, 0);
|
||||
try {
|
||||
let hash = Cc["@mozilla.org/security/hash;1"].
|
||||
createInstance(Ci.nsICryptoHash);
|
||||
let hashFunction =
|
||||
Ci.nsICryptoHash[hashFunctionName.toUpperCase()];
|
||||
if (!hashFunction) {
|
||||
log.error("could not get hash function");
|
||||
return Promise.reject();
|
||||
}
|
||||
hash.init(hashFunction);
|
||||
hash.updateFromStream(fileStream, -1);
|
||||
digest = binaryToHex(hash.finish(false));
|
||||
} catch (e) {
|
||||
log.warn("failed to compute hash: " + e);
|
||||
digest = "";
|
||||
}
|
||||
fileStream.close();
|
||||
return Promise.resolve(digest);
|
||||
},
|
||||
GMPDownloader.prototype = {
|
||||
/**
|
||||
* Starts the download process for an addon.
|
||||
@ -472,10 +838,9 @@ GMPDownloader.prototype = {
|
||||
* See GMPInstallManager.installAddon for resolve/rejected info
|
||||
*/
|
||||
start: function() {
|
||||
let log = getScopedLogger("GMPDownloader");
|
||||
let gmpAddon = this._gmpAddon;
|
||||
|
||||
if (!gmpAddon.isValid) {
|
||||
let log = getScopedLogger("GMPDownloader.start");
|
||||
this._deferred = Promise.defer();
|
||||
if (!this._gmpAddon.isValid) {
|
||||
log.info("gmpAddon is not valid, will not continue");
|
||||
return Promise.reject({
|
||||
target: this,
|
||||
@ -484,14 +849,55 @@ GMPDownloader.prototype = {
|
||||
});
|
||||
}
|
||||
|
||||
return ProductAddonChecker.downloadAddon(gmpAddon).then((zipPath) => {
|
||||
let uri = Services.io.newURI(this._gmpAddon.URL, null, null);
|
||||
this._request = Cc["@mozilla.org/network/incremental-download;1"].
|
||||
createInstance(Ci.nsIIncrementalDownload);
|
||||
let gmpFile = FileUtils.getFile("TmpD", [this._gmpAddon.id + ".zip"]);
|
||||
if (gmpFile.exists()) {
|
||||
gmpFile.remove(false);
|
||||
}
|
||||
|
||||
log.info("downloading from " + uri.spec + " to " + gmpFile.path);
|
||||
this._request.init(uri, gmpFile, DOWNLOAD_CHUNK_BYTES_SIZE,
|
||||
DOWNLOAD_INTERVAL);
|
||||
this._request.start(this, null);
|
||||
return this._deferred.promise;
|
||||
},
|
||||
// For nsIRequestObserver
|
||||
onStartRequest: function(request, context) {
|
||||
},
|
||||
// For nsIRequestObserver
|
||||
// Called when the GMP addon zip file is downloaded
|
||||
onStopRequest: function(request, context, status) {
|
||||
let log = getScopedLogger("GMPDownloader.onStopRequest");
|
||||
log.info("onStopRequest called");
|
||||
if (!Components.isSuccessCode(status)) {
|
||||
log.info("status failed: " + status);
|
||||
this._deferred.reject({
|
||||
target: this,
|
||||
status: status,
|
||||
type: "downloaderr"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let promise = this._verifyDownload();
|
||||
promise.then(() => {
|
||||
log.info("GMP file is ready to unzip");
|
||||
let destination = this._request.destination;
|
||||
|
||||
let zipPath = destination.path;
|
||||
let gmpAddon = this._gmpAddon;
|
||||
let installToDirPath = Cc["@mozilla.org/file/local;1"].
|
||||
createInstance(Ci.nsIFile);
|
||||
let path = OS.Path.join(OS.Constants.Path.profileDir,
|
||||
gmpAddon.id,
|
||||
gmpAddon.version);
|
||||
log.info("install to directory path: " + path);
|
||||
let gmpInstaller = new GMPExtractor(zipPath, path);
|
||||
installToDirPath.initWithPath(path);
|
||||
log.info("install to directory path: " + installToDirPath.path);
|
||||
let gmpInstaller = new GMPExtractor(zipPath, installToDirPath.path);
|
||||
let installPromise = gmpInstaller.install();
|
||||
return installPromise.then(extractedPaths => {
|
||||
installPromise.then(extractedPaths => {
|
||||
// Success, set the prefs
|
||||
let now = Math.round(Date.now() / 1000);
|
||||
GMPPrefs.set(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, now, gmpAddon.id);
|
||||
@ -502,13 +908,78 @@ GMPDownloader.prototype = {
|
||||
// Remember our ABI, so that if the profile is migrated to another
|
||||
// platform or from 32 -> 64 bit, we notice and don't try to load the
|
||||
// unexecutable plugin library.
|
||||
GMPPrefs.set(GMPPrefs.KEY_PLUGIN_ABI, UpdateUtils.ABI, gmpAddon.id);
|
||||
GMPPrefs.set(GMPPrefs.KEY_PLUGIN_ABI, GMPUtils.ABI(), gmpAddon.id);
|
||||
// Setting the version pref signals installation completion to consumers,
|
||||
// if you need to set other prefs etc. do it before this.
|
||||
GMPPrefs.set(GMPPrefs.KEY_PLUGIN_VERSION, gmpAddon.version,
|
||||
gmpAddon.id);
|
||||
return extractedPaths;
|
||||
this._deferred.resolve(extractedPaths);
|
||||
}, err => {
|
||||
this._deferred.reject(err);
|
||||
});
|
||||
}, err => {
|
||||
log.warn("verifyDownload check failed");
|
||||
this._deferred.reject({
|
||||
target: this,
|
||||
status: 200,
|
||||
type: "verifyerr"
|
||||
});
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Verifies that the downloaded zip file's hash matches the GMPAddon hash.
|
||||
* @return a promise which resolves if the download verifies
|
||||
*/
|
||||
_verifyDownload: function() {
|
||||
let verifyDownloadDeferred = Promise.defer();
|
||||
let log = getScopedLogger("GMPDownloader._verifyDownload");
|
||||
log.info("_verifyDownload called");
|
||||
if (!this._request) {
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
let destination = this._request.destination;
|
||||
log.info("for path: " + destination.path);
|
||||
|
||||
// Ensure that the file size matches the expected file size.
|
||||
if (this._gmpAddon.size !== undefined &&
|
||||
destination.fileSize != this._gmpAddon.size) {
|
||||
log.warn("Downloader:_verifyDownload downloaded size " +
|
||||
destination.fileSize + " != expected size " +
|
||||
this._gmpAddon.size + ".");
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
let promise = GMPDownloader.computeHash(this._gmpAddon.hashFunction, destination);
|
||||
promise.then(digest => {
|
||||
let expectedDigest = this._gmpAddon.hashValue.toLowerCase();
|
||||
if (digest !== expectedDigest) {
|
||||
log.warn("hashes do not match! Got: `" +
|
||||
digest + "`, expected: `" + expectedDigest + "`");
|
||||
this._deferred.reject();
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("hashes match!");
|
||||
verifyDownloadDeferred.resolve();
|
||||
}, err => {
|
||||
verifyDownloadDeferred.reject();
|
||||
});
|
||||
return verifyDownloadDeferred.promise;
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequestObserver])
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a string containing binary values to hex.
|
||||
*/
|
||||
function binaryToHex(input) {
|
||||
let result = "";
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
let hex = input.charCodeAt(i).toString(16);
|
||||
if (hex.length == 1)
|
||||
hex = "0" + hex;
|
||||
result += hex;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -126,6 +126,27 @@ this.GMPUtils = {
|
||||
hist.add(value);
|
||||
}
|
||||
},
|
||||
|
||||
ABI: function() {
|
||||
// This is copied directly from nsUpdateService.js
|
||||
let abi = null;
|
||||
try {
|
||||
abi = Services.appinfo.XPCOMABI;
|
||||
}
|
||||
catch (e) {
|
||||
return "unknown";
|
||||
}
|
||||
if (AppConstants.platform == "macosx") {
|
||||
// Mac universal build should report a different ABI than either macppc
|
||||
// or mactel.
|
||||
let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].
|
||||
getService(Ci.nsIMacUtils);
|
||||
|
||||
if (macutils.isUniversalBinary)
|
||||
abi += "-u-" + macutils.architecturesInBinary;
|
||||
}
|
||||
return abi;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -181,7 +181,7 @@ var dataProviders = {
|
||||
};
|
||||
|
||||
if (AppConstants.MOZ_UPDATER)
|
||||
data.updateChannel = Cu.import("resource://gre/modules/UpdateUtils.jsm", {}).UpdateUtils.UpdateChannel;
|
||||
data.updateChannel = Cu.import("resource://gre/modules/UpdateChannel.jsm", {}).UpdateChannel.get();
|
||||
|
||||
try {
|
||||
data.vendor = Services.prefs.getCharPref("app.support.vendor");
|
||||
|
46
toolkit/modules/UpdateChannel.jsm
Normal file
46
toolkit/modules/UpdateChannel.jsm
Normal file
@ -0,0 +1,46 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["UpdateChannel"];
|
||||
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
this.UpdateChannel = {
|
||||
/**
|
||||
* Read the update channel from defaults only. We do this to ensure that
|
||||
* the channel is tightly coupled with the application and does not apply
|
||||
* to other instances of the application that may use the same profile.
|
||||
*
|
||||
* @param [optional] aIncludePartners
|
||||
* Whether or not to include the partner bits. Default: true.
|
||||
*/
|
||||
get: function UpdateChannel_get(aIncludePartners = true) {
|
||||
let channel = AppConstants.MOZ_UPDATE_CHANNEL;
|
||||
let defaults = Services.prefs.getDefaultBranch(null);
|
||||
try {
|
||||
channel = defaults.getCharPref("app.update.channel");
|
||||
} catch (e) {
|
||||
// use default value when pref not found
|
||||
}
|
||||
|
||||
if (aIncludePartners) {
|
||||
try {
|
||||
let partners = Services.prefs.getChildList("app.partner.").sort();
|
||||
if (partners.length) {
|
||||
channel += "-cck";
|
||||
partners.forEach(function (prefName) {
|
||||
channel += "-" + Services.prefs.getCharPref(prefName);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
return channel;
|
||||
}
|
||||
};
|
@ -1,347 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["UpdateUtils"];
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/ctypes.jsm");
|
||||
|
||||
const FILE_UPDATE_LOCALE = "update.locale";
|
||||
const PREF_APP_DISTRIBUTION = "distribution.id";
|
||||
const PREF_APP_DISTRIBUTION_VERSION = "distribution.version";
|
||||
const PREF_APP_B2G_VERSION = "b2g.version";
|
||||
const PREF_APP_UPDATE_CUSTOM = "app.update.custom";
|
||||
const PREF_APP_UPDATE_IMEI_HASH = "app.update.imei_hash";
|
||||
|
||||
|
||||
this.UpdateUtils = {
|
||||
/**
|
||||
* Read the update channel from defaults only. We do this to ensure that
|
||||
* the channel is tightly coupled with the application and does not apply
|
||||
* to other instances of the application that may use the same profile.
|
||||
*
|
||||
* @param [optional] aIncludePartners
|
||||
* Whether or not to include the partner bits. Default: true.
|
||||
*/
|
||||
getUpdateChannel(aIncludePartners = true) {
|
||||
let channel = AppConstants.MOZ_UPDATE_CHANNEL;
|
||||
let defaults = Services.prefs.getDefaultBranch(null);
|
||||
try {
|
||||
channel = defaults.getCharPref("app.update.channel");
|
||||
} catch (e) {
|
||||
// use default value when pref not found
|
||||
}
|
||||
|
||||
if (aIncludePartners) {
|
||||
try {
|
||||
let partners = Services.prefs.getChildList("app.partner.").sort();
|
||||
if (partners.length) {
|
||||
channel += "-cck";
|
||||
partners.forEach(function (prefName) {
|
||||
channel += "-" + Services.prefs.getCharPref(prefName);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
return channel;
|
||||
},
|
||||
|
||||
get UpdateChannel() {
|
||||
return this.getUpdateChannel();
|
||||
},
|
||||
|
||||
/**
|
||||
* Formats a URL by replacing %...% values with OS, build and locale specific
|
||||
* values.
|
||||
*
|
||||
* @param url
|
||||
* The URL to format.
|
||||
* @return The formatted URL.
|
||||
*/
|
||||
formatUpdateURL(url) {
|
||||
url = url.replace(/%PRODUCT%/g, Services.appinfo.name);
|
||||
url = url.replace(/%VERSION%/g, Services.appinfo.version);
|
||||
url = url.replace(/%BUILD_ID%/g, Services.appinfo.appBuildID);
|
||||
url = url.replace(/%BUILD_TARGET%/g, Services.appinfo.OS + "_" + this.ABI);
|
||||
url = url.replace(/%OS_VERSION%/g, this.OSVersion);
|
||||
if (/%LOCALE%/.test(url)) {
|
||||
url = url.replace(/%LOCALE%/g, this.Locale);
|
||||
}
|
||||
url = url.replace(/%CHANNEL%/g, this.UpdateChannel);
|
||||
url = url.replace(/%PLATFORM_VERSION%/g, Services.appinfo.platformVersion);
|
||||
url = url.replace(/%DISTRIBUTION%/g,
|
||||
getDistributionPrefValue(PREF_APP_DISTRIBUTION));
|
||||
url = url.replace(/%DISTRIBUTION_VERSION%/g,
|
||||
getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION));
|
||||
url = url.replace(/%CUSTOM%/g, Preferences.get(PREF_APP_UPDATE_CUSTOM, ""));
|
||||
url = url.replace(/\+/g, "%2B");
|
||||
|
||||
if (AppConstants.platform == "gonk") {
|
||||
let sysLibs = {};
|
||||
Cu.import("resource://gre/modules/systemlibs.js", sysLibs);
|
||||
let productDevice = sysLibs.libcutils.property_get("ro.product.device");
|
||||
let buildType = sysLibs.libcutils.property_get("ro.build.type");
|
||||
url = url.replace(/%PRODUCT_MODEL%/g,
|
||||
sysLibs.libcutils.property_get("ro.product.model"));
|
||||
if (buildType == "user" || buildType == "userdebug") {
|
||||
url = url.replace(/%PRODUCT_DEVICE%/g, productDevice);
|
||||
} else {
|
||||
url = url.replace(/%PRODUCT_DEVICE%/g, productDevice + "-" + buildType);
|
||||
}
|
||||
url = url.replace(/%B2G_VERSION%/g,
|
||||
Preferences.get(PREF_APP_B2G_VERSION, null));
|
||||
url = url.replace(/%IMEI%/g,
|
||||
getPref("getCharPref", PREF_APP_UPDATE_IMEI_HASH, "default"));
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
};
|
||||
|
||||
/* Get the distribution pref values, from defaults only */
|
||||
function getDistributionPrefValue(aPrefName) {
|
||||
var prefValue = "default";
|
||||
|
||||
try {
|
||||
prefValue = Services.prefs.getDefaultBranch(null).getCharPref(aPrefName);
|
||||
} catch (e) {
|
||||
// use default when pref not found
|
||||
}
|
||||
|
||||
return prefValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the locale from the update.locale file for replacing %LOCALE% in the
|
||||
* update url. The update.locale file can be located in the application
|
||||
* directory or the GRE directory with preference given to it being located in
|
||||
* the application directory.
|
||||
*/
|
||||
XPCOMUtils.defineLazyGetter(UpdateUtils, "Locale", function() {
|
||||
let channel;
|
||||
let locale;
|
||||
for (let res of ['app', 'gre']) {
|
||||
channel = Services.io.newChannel2("resource://" + res + "/" + FILE_UPDATE_LOCALE,
|
||||
null,
|
||||
null,
|
||||
null, // aLoadingNode
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_NORMAL,
|
||||
Ci.nsIContentPolicy.TYPE_INTERNAL_XMLHTTPREQUEST);
|
||||
try {
|
||||
let inputStream = channel.open();
|
||||
locale = NetUtil.readInputStreamToString(inputStream, inputStream.available());
|
||||
} catch(e) {}
|
||||
if (locale)
|
||||
return locale.trim();
|
||||
}
|
||||
|
||||
Cu.reportError(FILE_UPDATE_LOCALE + " file doesn't exist in either the " +
|
||||
"application or GRE directories");
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
/* Windows only getter that returns the processor architecture. */
|
||||
XPCOMUtils.defineLazyGetter(this, "gWinCPUArch", function aus_gWinCPUArch() {
|
||||
// Get processor architecture
|
||||
let arch = "unknown";
|
||||
|
||||
const WORD = ctypes.uint16_t;
|
||||
const DWORD = ctypes.uint32_t;
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
|
||||
const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
|
||||
[
|
||||
{wProcessorArchitecture: WORD},
|
||||
{wReserved: WORD},
|
||||
{dwPageSize: DWORD},
|
||||
{lpMinimumApplicationAddress: ctypes.voidptr_t},
|
||||
{lpMaximumApplicationAddress: ctypes.voidptr_t},
|
||||
{dwActiveProcessorMask: DWORD.ptr},
|
||||
{dwNumberOfProcessors: DWORD},
|
||||
{dwProcessorType: DWORD},
|
||||
{dwAllocationGranularity: DWORD},
|
||||
{wProcessorLevel: WORD},
|
||||
{wProcessorRevision: WORD}
|
||||
]);
|
||||
|
||||
let kernel32 = false;
|
||||
try {
|
||||
kernel32 = ctypes.open("Kernel32");
|
||||
} catch (e) {
|
||||
Cu.reportError("Unable to open kernel32! Exception: " + e);
|
||||
}
|
||||
|
||||
if (kernel32) {
|
||||
try {
|
||||
let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo",
|
||||
ctypes.default_abi,
|
||||
ctypes.void_t,
|
||||
SYSTEM_INFO.ptr);
|
||||
let winSystemInfo = SYSTEM_INFO();
|
||||
// Default to unknown
|
||||
winSystemInfo.wProcessorArchitecture = 0xffff;
|
||||
|
||||
GetNativeSystemInfo(winSystemInfo.address());
|
||||
switch (winSystemInfo.wProcessorArchitecture) {
|
||||
case 9:
|
||||
arch = "x64";
|
||||
break;
|
||||
case 6:
|
||||
arch = "IA64";
|
||||
break;
|
||||
case 0:
|
||||
arch = "x86";
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
Cu.reportError("Error getting processor architecture. " +
|
||||
"Exception: " + e);
|
||||
} finally {
|
||||
kernel32.close();
|
||||
}
|
||||
}
|
||||
|
||||
return arch;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(UpdateUtils, "ABI", function() {
|
||||
let abi = null;
|
||||
try {
|
||||
abi = Services.appinfo.XPCOMABI;
|
||||
}
|
||||
catch (e) {
|
||||
Cu.reportError("XPCOM ABI unknown");
|
||||
}
|
||||
|
||||
if (AppConstants.platform == "macosx") {
|
||||
// Mac universal build should report a different ABI than either macppc
|
||||
// or mactel.
|
||||
let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].
|
||||
getService(Ci.nsIMacUtils);
|
||||
|
||||
if (macutils.isUniversalBinary) {
|
||||
abi += "-u-" + macutils.architecturesInBinary;
|
||||
}
|
||||
} else if (AppConstants.platform == "win") {
|
||||
// Windows build should report the CPU architecture that it's running on.
|
||||
abi += "-" + gWinCPUArch;
|
||||
}
|
||||
return abi;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(UpdateUtils, "OSVersion", function() {
|
||||
let osVersion;
|
||||
try {
|
||||
osVersion = Services.sysinfo.getProperty("name") + " " +
|
||||
Services.sysinfo.getProperty("version");
|
||||
}
|
||||
catch (e) {
|
||||
Cu.reportError("OS Version unknown.");
|
||||
}
|
||||
|
||||
if (osVersion) {
|
||||
if (AppConstants.platform == "win") {
|
||||
const BYTE = ctypes.uint8_t;
|
||||
const WORD = ctypes.uint16_t;
|
||||
const DWORD = ctypes.uint32_t;
|
||||
const WCHAR = ctypes.char16_t;
|
||||
const BOOL = ctypes.int;
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
|
||||
const SZCSDVERSIONLENGTH = 128;
|
||||
const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
|
||||
[
|
||||
{dwOSVersionInfoSize: DWORD},
|
||||
{dwMajorVersion: DWORD},
|
||||
{dwMinorVersion: DWORD},
|
||||
{dwBuildNumber: DWORD},
|
||||
{dwPlatformId: DWORD},
|
||||
{szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
|
||||
{wServicePackMajor: WORD},
|
||||
{wServicePackMinor: WORD},
|
||||
{wSuiteMask: WORD},
|
||||
{wProductType: BYTE},
|
||||
{wReserved: BYTE}
|
||||
]);
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
|
||||
const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
|
||||
[
|
||||
{wProcessorArchitecture: WORD},
|
||||
{wReserved: WORD},
|
||||
{dwPageSize: DWORD},
|
||||
{lpMinimumApplicationAddress: ctypes.voidptr_t},
|
||||
{lpMaximumApplicationAddress: ctypes.voidptr_t},
|
||||
{dwActiveProcessorMask: DWORD.ptr},
|
||||
{dwNumberOfProcessors: DWORD},
|
||||
{dwProcessorType: DWORD},
|
||||
{dwAllocationGranularity: DWORD},
|
||||
{wProcessorLevel: WORD},
|
||||
{wProcessorRevision: WORD}
|
||||
]);
|
||||
|
||||
let kernel32 = false;
|
||||
try {
|
||||
kernel32 = ctypes.open("Kernel32");
|
||||
} catch (e) {
|
||||
Cu.reportError("Unable to open kernel32! " + e);
|
||||
osVersion += ".unknown (unknown)";
|
||||
}
|
||||
|
||||
if (kernel32) {
|
||||
try {
|
||||
// Get Service pack info
|
||||
try {
|
||||
let GetVersionEx = kernel32.declare("GetVersionExW",
|
||||
ctypes.default_abi,
|
||||
BOOL,
|
||||
OSVERSIONINFOEXW.ptr);
|
||||
let winVer = OSVERSIONINFOEXW();
|
||||
winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
|
||||
|
||||
if(0 !== GetVersionEx(winVer.address())) {
|
||||
osVersion += "." + winVer.wServicePackMajor +
|
||||
"." + winVer.wServicePackMinor;
|
||||
} else {
|
||||
Cu.reportError("Unknown failure in GetVersionEX (returned 0)");
|
||||
osVersion += ".unknown";
|
||||
}
|
||||
} catch (e) {
|
||||
Cu.reportError("Error getting service pack information. Exception: " + e);
|
||||
osVersion += ".unknown";
|
||||
}
|
||||
} finally {
|
||||
kernel32.close();
|
||||
}
|
||||
|
||||
// Add processor architecture
|
||||
osVersion += " (" + gWinCPUArch + ")";
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
osVersion += " (" + Services.sysinfo.getProperty("secondaryLibrary") + ")";
|
||||
}
|
||||
catch (e) {
|
||||
// Not all platforms have a secondary widget library, so an error is nothing to worry about.
|
||||
}
|
||||
osVersion = encodeURIComponent(osVersion);
|
||||
}
|
||||
return osVersion;
|
||||
});
|
@ -75,7 +75,7 @@ EXTRA_JS_MODULES += [
|
||||
'TelemetryTimestamps.jsm',
|
||||
'Timer.jsm',
|
||||
'Troubleshoot.jsm',
|
||||
'UpdateUtils.jsm',
|
||||
'UpdateChannel.jsm',
|
||||
'WebChannel.jsm',
|
||||
'WindowDraggingUtils.jsm',
|
||||
'ZipUtils.jsm',
|
||||
|
@ -11,9 +11,6 @@ Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm")
|
||||
Cu.import("resource://gre/modules/UpdateUtils.jsm");
|
||||
|
||||
let { computeHash } = Cu.import("resource://gre/modules/addons/ProductAddonChecker.jsm");
|
||||
|
||||
do_get_profile();
|
||||
|
||||
@ -433,7 +430,7 @@ function* test_checkForAddons_installAddon(id, includeSize, wantInstallReject) {
|
||||
let data = "e~=0.5772156649";
|
||||
let zipFile = createNewZipFile(zipFileName, data);
|
||||
let hashFunc = "sha256";
|
||||
let expectedDigest = yield computeHash(hashFunc, zipFile.path);
|
||||
let expectedDigest = yield GMPDownloader.computeHash(hashFunc, zipFile);
|
||||
let fileSize = zipFile.fileSize;
|
||||
if (wantInstallReject) {
|
||||
fileSize = 1;
|
||||
@ -459,6 +456,7 @@ function* test_checkForAddons_installAddon(id, includeSize, wantInstallReject) {
|
||||
let gmpAddon = gmpAddons[0];
|
||||
do_check_false(gmpAddon.isInstalled);
|
||||
|
||||
GMPInstallManager.overrideLeaveDownloadedZip = true;
|
||||
try {
|
||||
let extractedPaths = yield installManager.installAddon(gmpAddon);
|
||||
if (wantInstallReject) {
|
||||
@ -476,6 +474,14 @@ function* test_checkForAddons_installAddon(id, includeSize, wantInstallReject) {
|
||||
let readData = readStringFromFile(extractedFile);
|
||||
do_check_eq(readData, data);
|
||||
|
||||
// Check that the downloaded zip matches the offered zip exactly
|
||||
let downloadedGMPFile = FileUtils.getFile("TmpD",
|
||||
[gmpAddon.id + ".zip"]);
|
||||
do_check_true(downloadedGMPFile.exists());
|
||||
let downloadedBytes = getBinaryFileData(downloadedGMPFile);
|
||||
let sourceBytes = getBinaryFileData(zipFile);
|
||||
do_check_true(compareBinaryData(downloadedBytes, sourceBytes));
|
||||
|
||||
// Make sure the prefs are set correctly
|
||||
do_check_true(!!GMPScope.GMPPrefs.get(
|
||||
GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, "", gmpAddon.id));
|
||||
@ -484,7 +490,7 @@ function* test_checkForAddons_installAddon(id, includeSize, wantInstallReject) {
|
||||
"1.1");
|
||||
do_check_eq(GMPScope.GMPPrefs.get(GMPScope.GMPPrefs.KEY_PLUGIN_ABI, "",
|
||||
gmpAddon.id),
|
||||
UpdateUtils.ABI);
|
||||
GMPScope.GMPUtils.ABI());
|
||||
// Make sure it reports as being installed
|
||||
do_check_true(gmpAddon.isInstalled);
|
||||
|
||||
@ -492,9 +498,16 @@ function* test_checkForAddons_installAddon(id, includeSize, wantInstallReject) {
|
||||
extractedFile.parent.remove(true);
|
||||
zipFile.remove(false);
|
||||
httpServer.stop(function() {});
|
||||
do_print("Removing downloaded GMP file: " + downloadedGMPFile.path);
|
||||
downloadedGMPFile.remove(false);
|
||||
installManager.uninit();
|
||||
} catch(ex) {
|
||||
zipFile.remove(false);
|
||||
let downloadedGMPFile = FileUtils.getFile("TmpD",
|
||||
[gmpAddon.id + ".zip"]);
|
||||
do_print("Removing downloaded GMP file from exception handler: " +
|
||||
downloadedGMPFile.path);
|
||||
downloadedGMPFile.remove(false);
|
||||
if (!wantInstallReject) {
|
||||
do_throw("install update should not reject");
|
||||
}
|
||||
@ -785,6 +798,45 @@ function overrideXHR(status, response, options) {
|
||||
return overrideXHR.myxhr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares binary data of 2 arrays and returns true if they are the same
|
||||
*
|
||||
* @param arr1 The first array to compare
|
||||
* @param arr2 The second array to compare
|
||||
*/
|
||||
function compareBinaryData(arr1, arr2) {
|
||||
do_check_eq(arr1.length, arr2.length);
|
||||
for (let i = 0; i < arr1.length; i++) {
|
||||
if (arr1[i] != arr2[i]) {
|
||||
do_print("Data differs at index " + i +
|
||||
", arr1: " + arr1[i] + ", arr2: " + arr2[i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a file's data and returns it
|
||||
*
|
||||
* @param file The file to read the data from
|
||||
* @return array of bytes for the data in the file.
|
||||
*/
|
||||
function getBinaryFileData(file) {
|
||||
let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
|
||||
createInstance(Ci.nsIFileInputStream);
|
||||
// Open as RD_ONLY with default permissions.
|
||||
fileStream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
|
||||
|
||||
// Check the returned size versus the expected size.
|
||||
let stream = Cc["@mozilla.org/binaryinputstream;1"].
|
||||
createInstance(Ci.nsIBinaryInputStream);
|
||||
stream.setInputStream(fileStream);
|
||||
let bytes = stream.readByteArray(stream.available());
|
||||
fileStream.close();
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new zip file containing a file with the specified data
|
||||
* @param zipName The name of the zip file
|
||||
|
@ -1,295 +0,0 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/UpdateUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
Cu.import("resource://testing-common/AppInfo.jsm");
|
||||
Cu.import("resource://gre/modules/ctypes.jsm");
|
||||
|
||||
const PREF_APP_UPDATE_CHANNEL = "app.update.channel";
|
||||
const PREF_APP_PARTNER_BRANCH = "app.partner.";
|
||||
const PREF_DISTRIBUTION_ID = "distribution.id";
|
||||
const PREF_DISTRIBUTION_VERSION = "distribution.version";
|
||||
|
||||
const URL_PREFIX = "http://localhost/";
|
||||
|
||||
const MSG_SHOULD_EQUAL = " should equal the expected value";
|
||||
|
||||
// For INSTALL_LOCALE
|
||||
load("../../../mozapps/update/tests/data/xpcshellConstantsPP.js");
|
||||
|
||||
updateAppInfo();
|
||||
const gAppInfo = getAppInfo();
|
||||
const gDefaultPrefBranch = Services.prefs.getDefaultBranch(null);
|
||||
|
||||
function setUpdateChannel(aChannel) {
|
||||
gDefaultPrefBranch.setCharPref(PREF_APP_UPDATE_CHANNEL, aChannel);
|
||||
}
|
||||
|
||||
function getServicePack() {
|
||||
// NOTE: This function is a helper function and not a test. Thus,
|
||||
// it uses throw() instead of do_throw(). Any tests that use this function
|
||||
// should catch exceptions thrown in this function and deal with them
|
||||
// appropriately (usually by calling do_throw).
|
||||
const BYTE = ctypes.uint8_t;
|
||||
const WORD = ctypes.uint16_t;
|
||||
const DWORD = ctypes.uint32_t;
|
||||
const WCHAR = ctypes.char16_t;
|
||||
const BOOL = ctypes.int;
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
|
||||
const SZCSDVERSIONLENGTH = 128;
|
||||
const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
|
||||
[
|
||||
{dwOSVersionInfoSize: DWORD},
|
||||
{dwMajorVersion: DWORD},
|
||||
{dwMinorVersion: DWORD},
|
||||
{dwBuildNumber: DWORD},
|
||||
{dwPlatformId: DWORD},
|
||||
{szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
|
||||
{wServicePackMajor: WORD},
|
||||
{wServicePackMinor: WORD},
|
||||
{wSuiteMask: WORD},
|
||||
{wProductType: BYTE},
|
||||
{wReserved: BYTE}
|
||||
]);
|
||||
|
||||
let kernel32 = ctypes.open("kernel32");
|
||||
try {
|
||||
let GetVersionEx = kernel32.declare("GetVersionExW",
|
||||
ctypes.default_abi,
|
||||
BOOL,
|
||||
OSVERSIONINFOEXW.ptr);
|
||||
let winVer = OSVERSIONINFOEXW();
|
||||
winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
|
||||
|
||||
if (0 === GetVersionEx(winVer.address())) {
|
||||
// Using "throw" instead of "do_throw" (see NOTE above)
|
||||
throw("Failure in GetVersionEx (returned 0)");
|
||||
}
|
||||
|
||||
return winVer.wServicePackMajor + "." + winVer.wServicePackMinor;
|
||||
} finally {
|
||||
kernel32.close();
|
||||
}
|
||||
}
|
||||
|
||||
function getProcArchitecture() {
|
||||
// NOTE: This function is a helper function and not a test. Thus,
|
||||
// it uses throw() instead of do_throw(). Any tests that use this function
|
||||
// should catch exceptions thrown in this function and deal with them
|
||||
// appropriately (usually by calling do_throw).
|
||||
const WORD = ctypes.uint16_t;
|
||||
const DWORD = ctypes.uint32_t;
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
|
||||
const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
|
||||
[
|
||||
{wProcessorArchitecture: WORD},
|
||||
{wReserved: WORD},
|
||||
{dwPageSize: DWORD},
|
||||
{lpMinimumApplicationAddress: ctypes.voidptr_t},
|
||||
{lpMaximumApplicationAddress: ctypes.voidptr_t},
|
||||
{dwActiveProcessorMask: DWORD.ptr},
|
||||
{dwNumberOfProcessors: DWORD},
|
||||
{dwProcessorType: DWORD},
|
||||
{dwAllocationGranularity: DWORD},
|
||||
{wProcessorLevel: WORD},
|
||||
{wProcessorRevision: WORD}
|
||||
]);
|
||||
|
||||
let kernel32 = ctypes.open("kernel32");
|
||||
try {
|
||||
let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo",
|
||||
ctypes.default_abi,
|
||||
ctypes.void_t,
|
||||
SYSTEM_INFO.ptr);
|
||||
let sysInfo = SYSTEM_INFO();
|
||||
// Default to unknown
|
||||
sysInfo.wProcessorArchitecture = 0xffff;
|
||||
|
||||
GetNativeSystemInfo(sysInfo.address());
|
||||
switch(sysInfo.wProcessorArchitecture) {
|
||||
case 9:
|
||||
return "x64";
|
||||
case 6:
|
||||
return "IA64";
|
||||
case 0:
|
||||
return "x86";
|
||||
default:
|
||||
// Using "throw" instead of "do_throw" (see NOTE above)
|
||||
throw("Unknown architecture returned from GetNativeSystemInfo: " + sysInfo.wProcessorArchitecture);
|
||||
}
|
||||
} finally {
|
||||
kernel32.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function for formatting a url and getting the result we're
|
||||
// interested in
|
||||
function getResult(url) {
|
||||
url = UpdateUtils.formatUpdateURL(url);
|
||||
return url.substr(URL_PREFIX.length).split("/")[0];
|
||||
}
|
||||
|
||||
// url constructed with %PRODUCT%
|
||||
add_task(function* test_product() {
|
||||
let url = URL_PREFIX + "%PRODUCT%/";
|
||||
Assert.equal(getResult(url), gAppInfo.name,
|
||||
"the url param for %PRODUCT%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
// url constructed with %VERSION%
|
||||
add_task(function* test_version() {
|
||||
let url = URL_PREFIX + "%VERSION%/";
|
||||
Assert.equal(getResult(url), gAppInfo.version,
|
||||
"the url param for %VERSION%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
// url constructed with %BUILD_ID%
|
||||
add_task(function* test_build_id() {
|
||||
let url = URL_PREFIX + "%BUILD_ID%/";
|
||||
Assert.equal(getResult(url), gAppInfo.appBuildID,
|
||||
"the url param for %BUILD_ID%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
// url constructed with %BUILD_TARGET%
|
||||
// XXX TODO - it might be nice if we tested the actual ABI
|
||||
add_task(function* test_build_target() {
|
||||
let url = URL_PREFIX + "%BUILD_TARGET%/";
|
||||
|
||||
let abi;
|
||||
try {
|
||||
abi = gAppInfo.XPCOMABI;
|
||||
} catch (e) {
|
||||
do_throw("nsIXULAppInfo:XPCOMABI not defined\n");
|
||||
}
|
||||
|
||||
if (AppConstants.platform == "macosx") {
|
||||
// Mac universal build should report a different ABI than either macppc
|
||||
// or mactel. This is necessary since nsUpdateService.js will set the ABI to
|
||||
// Universal-gcc3 for Mac universal builds.
|
||||
let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].
|
||||
getService(Ci.nsIMacUtils);
|
||||
|
||||
if (macutils.isUniversalBinary) {
|
||||
abi += "-u-" + macutils.architecturesInBinary;
|
||||
}
|
||||
} else if (IS_WIN) {
|
||||
// Windows build should report the CPU architecture that it's running on.
|
||||
abi += "-" + getProcArchitecture();
|
||||
}
|
||||
|
||||
Assert.equal(getResult(url), gAppInfo.OS + "_" + abi,
|
||||
"the url param for %BUILD_TARGET%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
// url constructed with %LOCALE%
|
||||
// Bug 488936 added the update.locale file that stores the update locale
|
||||
add_task(function* test_locale() {
|
||||
// The code that gets the locale accesses the profile which is only available
|
||||
// after calling do_get_profile in xpcshell tests. This prevents an error from
|
||||
// being logged.
|
||||
do_get_profile();
|
||||
|
||||
let url = URL_PREFIX + "%LOCALE%/";
|
||||
Assert.equal(getResult(url), INSTALL_LOCALE,
|
||||
"the url param for %LOCALE%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
// url constructed with %CHANNEL%
|
||||
add_task(function* test_channel() {
|
||||
let url = URL_PREFIX + "%CHANNEL%/";
|
||||
setUpdateChannel("test_channel");
|
||||
Assert.equal(getResult(url), "test_channel",
|
||||
"the url param for %CHANNEL%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
// url constructed with %CHANNEL% with distribution partners
|
||||
add_task(function* test_channel_distribution() {
|
||||
let url = URL_PREFIX + "%CHANNEL%/";
|
||||
gDefaultPrefBranch.setCharPref(PREF_APP_PARTNER_BRANCH + "test_partner1",
|
||||
"test_partner1");
|
||||
gDefaultPrefBranch.setCharPref(PREF_APP_PARTNER_BRANCH + "test_partner2",
|
||||
"test_partner2");
|
||||
Assert.equal(getResult(url),
|
||||
"test_channel-cck-test_partner1-test_partner2",
|
||||
"the url param for %CHANNEL%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
// url constructed with %PLATFORM_VERSION%
|
||||
add_task(function* test_platform_version() {
|
||||
let url = URL_PREFIX + "%PLATFORM_VERSION%/";
|
||||
Assert.equal(getResult(url), gAppInfo.platformVersion,
|
||||
"the url param for %PLATFORM_VERSION%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
// url constructed with %OS_VERSION%
|
||||
add_task(function* test_os_version() {
|
||||
let url = URL_PREFIX + "%OS_VERSION%/";
|
||||
let osVersion;
|
||||
let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
|
||||
osVersion = sysInfo.getProperty("name") + " " + sysInfo.getProperty("version");
|
||||
|
||||
if (IS_WIN) {
|
||||
try {
|
||||
let servicePack = getServicePack();
|
||||
osVersion += "." + servicePack;
|
||||
} catch (e) {
|
||||
do_throw("Failure obtaining service pack: " + e);
|
||||
}
|
||||
|
||||
if ("5.0" === sysInfo.getProperty("version")) { // Win2K
|
||||
osVersion += " (unknown)";
|
||||
} else {
|
||||
try {
|
||||
osVersion += " (" + getProcArchitecture() + ")";
|
||||
} catch (e) {
|
||||
do_throw("Failed to obtain processor architecture: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (osVersion) {
|
||||
try {
|
||||
osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
|
||||
} catch (e) {
|
||||
// Not all platforms have a secondary widget library, so an error is
|
||||
// nothing to worry about.
|
||||
}
|
||||
osVersion = encodeURIComponent(osVersion);
|
||||
}
|
||||
|
||||
Assert.equal(getResult(url), osVersion,
|
||||
"the url param for %OS_VERSION%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
// url constructed with %DISTRIBUTION%
|
||||
add_task(function* test_distribution() {
|
||||
let url = URL_PREFIX + "%DISTRIBUTION%/";
|
||||
gDefaultPrefBranch.setCharPref(PREF_DISTRIBUTION_ID, "test_distro");
|
||||
Assert.equal(getResult(url), "test_distro",
|
||||
"the url param for %DISTRIBUTION%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
// url constructed with %DISTRIBUTION_VERSION%
|
||||
add_task(function* test_distribution_version() {
|
||||
let url = URL_PREFIX + "%DISTRIBUTION_VERSION%/";
|
||||
gDefaultPrefBranch.setCharPref(PREF_DISTRIBUTION_VERSION, "test_distro_version");
|
||||
Assert.equal(getResult(url), "test_distro_version",
|
||||
"the url param for %DISTRIBUTION_VERSION%" + MSG_SHOULD_EQUAL);
|
||||
});
|
||||
|
||||
add_task(function* test_custom() {
|
||||
Services.prefs.setCharPref("app.update.custom", "custom");
|
||||
let url = URL_PREFIX + "%CUSTOM%/";
|
||||
Assert.equal(getResult(url), "custom",
|
||||
"the url query string for %CUSTOM%" + MSG_SHOULD_EQUAL);
|
||||
});
|
@ -57,8 +57,6 @@ skip-if = toolkit == 'android'
|
||||
skip-if = toolkit == 'android'
|
||||
[test_timer.js]
|
||||
skip-if = toolkit == 'android'
|
||||
[test_UpdateUtils_url.js]
|
||||
[test_UpdateUtils_updatechannel.js]
|
||||
[test_web_channel.js]
|
||||
[test_web_channel_broker.js]
|
||||
[test_ZipUtils.js]
|
||||
|
@ -859,13 +859,6 @@ var AddonManagerInternal = {
|
||||
logger.debug(`Provider finished startup: ${providerName(aProvider)}`);
|
||||
},
|
||||
|
||||
_getProviderByName(aName) {
|
||||
for (let provider of this.providers) {
|
||||
if (providerName(provider) == aName)
|
||||
return provider;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes the AddonManager, loading any known providers and initializing
|
||||
* them.
|
||||
@ -1471,9 +1464,9 @@ var AddonManagerInternal = {
|
||||
let buPromise = Task.spawn(function* backgroundUpdateTask() {
|
||||
let hotfixID = this.hotfixID;
|
||||
|
||||
let appUpdateEnabled = Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED) &&
|
||||
Services.prefs.getBoolPref(PREF_APP_UPDATE_AUTO);
|
||||
let checkHotfix = hotfixID && appUpdateEnabled;
|
||||
let checkHotfix = hotfixID &&
|
||||
Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED) &&
|
||||
Services.prefs.getBoolPref(PREF_APP_UPDATE_AUTO);
|
||||
|
||||
logger.debug("Background update check beginning");
|
||||
|
||||
@ -1620,15 +1613,6 @@ var AddonManagerInternal = {
|
||||
}
|
||||
}
|
||||
|
||||
if (appUpdateEnabled) {
|
||||
try {
|
||||
yield AddonManagerInternal._getProviderByName("XPIProvider").updateSystemAddons();
|
||||
}
|
||||
catch (e) {
|
||||
logger.warn("Failed to update system addons", e);
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Background update check complete");
|
||||
Services.obs.notifyObservers(null,
|
||||
"addons-background-update-complete",
|
||||
|
@ -19,7 +19,6 @@ Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/GMPUtils.jsm");
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
Cu.import("resource://gre/modules/UpdateUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(
|
||||
this, "GMPInstallManager", "resource://gre/modules/GMPInstallManager.jsm");
|
||||
@ -491,8 +490,8 @@ GMPWrapper.prototype = {
|
||||
return { installed: false, valid: true };
|
||||
}
|
||||
|
||||
let abi = GMPPrefs.get(GMPPrefs.KEY_PLUGIN_ABI, UpdateUtils.ABI, this._plugin.id);
|
||||
if (abi != UpdateUtils.ABI) {
|
||||
let abi = GMPPrefs.get(GMPPrefs.KEY_PLUGIN_ABI, GMPUtils.ABI(), this._plugin.id);
|
||||
if (abi != GMPUtils.ABI()) {
|
||||
// ABI doesn't match. Possibly this is a profile migrated across platforms
|
||||
// or from 32 -> 64 bit.
|
||||
return {
|
||||
|
@ -1,322 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "ProductAddonChecker" ];
|
||||
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://gre/modules/CertUtils.jsm");
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
let logger = Log.repository.getLogger("addons.productaddons");
|
||||
|
||||
/**
|
||||
* Number of milliseconds after which we need to cancel `downloadXML`.
|
||||
*
|
||||
* Bug 1087674 suggests that the XHR we use in `downloadXML` may
|
||||
* never terminate in presence of network nuisances (e.g. strange
|
||||
* antivirus behavior). This timeout is a defensive measure to ensure
|
||||
* that we fail cleanly in such case.
|
||||
*/
|
||||
const TIMEOUT_DELAY_MS = 20000;
|
||||
// Chunk size for the incremental downloader
|
||||
const DOWNLOAD_CHUNK_BYTES_SIZE = 300000;
|
||||
// Incremental downloader interval
|
||||
const DOWNLOAD_INTERVAL = 0;
|
||||
// How much of a file to read into memory at a time for hashing
|
||||
const HASH_CHUNK_SIZE = 8192;
|
||||
|
||||
/**
|
||||
* Gets the status of an XMLHttpRequest either directly or from its underlying
|
||||
* channel.
|
||||
*
|
||||
* @param request
|
||||
* The XMLHttpRequest.
|
||||
* @return an integer status value.
|
||||
*/
|
||||
function getRequestStatus(request) {
|
||||
let status = null;
|
||||
try {
|
||||
status = request.status;
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
|
||||
if (status != null) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return request.channel.QueryInterface(Ci.nsIRequest).status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads an XML document from a URL optionally testing the SSL certificate
|
||||
* for certain attributes.
|
||||
*
|
||||
* @param url
|
||||
* The url to download from.
|
||||
* @param allowNonBuiltIn
|
||||
* Whether to trust SSL certificates without a built-in CA issuer.
|
||||
* @param allowedCerts
|
||||
* The list of certificate attributes to match the SSL certificate
|
||||
* against or null to skip checks.
|
||||
* @return a promise that resolves to the DOM document downloaded or rejects
|
||||
* with a JS exception in case of error.
|
||||
*/
|
||||
function downloadXML(url, allowNonBuiltIn = false, allowedCerts = null) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
||||
createInstance(Ci.nsISupports);
|
||||
// This is here to let unit test code override XHR
|
||||
if (request.wrappedJSObject) {
|
||||
request = request.wrappedJSObject;
|
||||
}
|
||||
request.open("GET", url, true);
|
||||
request.channel.notificationCallbacks = new BadCertHandler(allowNonBuiltIn);
|
||||
// Prevent the request from reading from the cache.
|
||||
request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
|
||||
// Prevent the request from writing to the cache.
|
||||
request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
|
||||
request.timeout = TIMEOUT_DELAY_MS;
|
||||
|
||||
request.overrideMimeType("text/xml");
|
||||
// The Cache-Control header is only interpreted by proxies and the
|
||||
// final destination. It does not help if a resource is already
|
||||
// cached locally.
|
||||
request.setRequestHeader("Cache-Control", "no-cache");
|
||||
// HTTP/1.0 servers might not implement Cache-Control and
|
||||
// might only implement Pragma: no-cache
|
||||
request.setRequestHeader("Pragma", "no-cache");
|
||||
|
||||
let fail = (event) => {
|
||||
let request = event.target;
|
||||
let status = getRequestStatus(request);
|
||||
let message = "Failed downloading XML, status: " + status + ", reason: " + event.type;
|
||||
logger.warn(message);
|
||||
let ex = new Error(message);
|
||||
ex.status = status;
|
||||
reject(ex);
|
||||
};
|
||||
|
||||
let success = (event) => {
|
||||
logger.info("Completed downloading document");
|
||||
let request = event.target;
|
||||
|
||||
try {
|
||||
checkCert(request.channel, allowNonBuiltIn, allowedCerts);
|
||||
} catch (ex) {
|
||||
logger.error("Request failed certificate checks: " + ex);
|
||||
ex.status = getRequestStatus(request);
|
||||
reject(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(request.responseXML);
|
||||
};
|
||||
|
||||
request.addEventListener("error", fail, false);
|
||||
request.addEventListener("abort", fail, false);
|
||||
request.addEventListener("timeout", fail, false);
|
||||
request.addEventListener("load", success, false);
|
||||
|
||||
logger.info("sending request to: " + url);
|
||||
request.send(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a list of add-ons from a DOM document.
|
||||
*
|
||||
* @param document
|
||||
* The DOM document to parse.
|
||||
* @return null if there is no <addons> element otherwise an array of the addons
|
||||
* listed.
|
||||
*/
|
||||
function parseXML(document) {
|
||||
// Check that the root element is correct
|
||||
if (document.documentElement.localName != "updates") {
|
||||
throw new Error("got node name: " + document.documentElement.localName +
|
||||
", expected: updates");
|
||||
}
|
||||
|
||||
// Check if there are any addons elements in the updates element
|
||||
let addons = document.querySelector("updates:root > addons");
|
||||
if (!addons) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let results = [];
|
||||
let addonList = document.querySelectorAll("updates:root > addons > addon");
|
||||
for (let addonElement of addonList) {
|
||||
let addon = {};
|
||||
|
||||
for (let name of ["id", "URL", "hashFunction", "hashValue", "version", "size"]) {
|
||||
if (addonElement.hasAttribute(name)) {
|
||||
addon[name] = addonElement.getAttribute(name);
|
||||
}
|
||||
}
|
||||
addon.size = Number(addon.size) || undefined;
|
||||
|
||||
results.push(addon);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads file from a URL using the incremental file downloader.
|
||||
*
|
||||
* @param url
|
||||
* The url to download from.
|
||||
* @return a promise that resolves to the path of a temporary file or rejects
|
||||
* with a JS exception in case of error.
|
||||
*/
|
||||
function downloadFile(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let observer = {
|
||||
onStartRequest: function() {},
|
||||
|
||||
onStopRequest: function(request, context, status) {
|
||||
if (!Components.isSuccessCode(status)) {
|
||||
logger.warn("File download failed: 0x" + status.toString(16));
|
||||
tmpFile.remove(true);
|
||||
reject(Components.Exception("File download failed", status));
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(tmpFile.path);
|
||||
}
|
||||
};
|
||||
|
||||
let uri = NetUtil.newURI(url);
|
||||
let request = Cc["@mozilla.org/network/incremental-download;1"].
|
||||
createInstance(Ci.nsIIncrementalDownload);
|
||||
let tmpFile = FileUtils.getFile("TmpD", ["tmpaddon"]);
|
||||
tmpFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
|
||||
|
||||
logger.info("Downloading from " + uri.spec + " to " + tmpFile.path);
|
||||
request.init(uri, tmpFile, DOWNLOAD_CHUNK_BYTES_SIZE, DOWNLOAD_INTERVAL);
|
||||
request.start(observer, null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string containing binary values to hex.
|
||||
*/
|
||||
function binaryToHex(input) {
|
||||
let result = "";
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
let hex = input.charCodeAt(i).toString(16);
|
||||
if (hex.length == 1) {
|
||||
hex = "0" + hex;
|
||||
}
|
||||
result += hex;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the hash of a file.
|
||||
*
|
||||
* @param hashFunction
|
||||
* The type of hash function to use, must be supported by nsICryptoHash.
|
||||
* @param path
|
||||
* The path of the file to hash.
|
||||
* @return a promise that resolves to hash of the file or rejects with a JS
|
||||
* exception in case of error.
|
||||
*/
|
||||
let computeHash = Task.async(function*(hashFunction, path) {
|
||||
let file = yield OS.File.open(path, { existing: true, read: true });
|
||||
try {
|
||||
let hasher = Cc["@mozilla.org/security/hash;1"].
|
||||
createInstance(Ci.nsICryptoHash);
|
||||
hasher.initWithString(hashFunction);
|
||||
|
||||
let bytes;
|
||||
do {
|
||||
bytes = yield file.read(HASH_CHUNK_SIZE);
|
||||
hasher.update(bytes, bytes.length);
|
||||
} while (bytes.length == HASH_CHUNK_SIZE);
|
||||
|
||||
return binaryToHex(hasher.finish(false));
|
||||
}
|
||||
finally {
|
||||
yield file.close();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Verifies that a downloaded file matches what was expected.
|
||||
*
|
||||
* @param properties
|
||||
* The properties to check, `size` and `hashFunction` with `hashValue`
|
||||
* are supported. Any properties missing won't be checked.
|
||||
* @param path
|
||||
* The path of the file to check.
|
||||
* @return a promise that resolves if the file matched or rejects with a JS
|
||||
* exception in case of error.
|
||||
*/
|
||||
let verifyFile = Task.async(function*(properties, path) {
|
||||
if (properties.size !== undefined) {
|
||||
let stat = yield OS.File.stat(path);
|
||||
if (stat.size != properties.size) {
|
||||
throw new Error("Downloaded file was " + stat.size + " bytes but expected " + properties.size + " bytes.");
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.hashFunction !== undefined) {
|
||||
let expectedDigest = properties.hashValue.toLowerCase();
|
||||
let digest = yield computeHash(properties.hashFunction, path);
|
||||
if (digest != expectedDigest) {
|
||||
throw new Error("Hash was `" + digest + "` but expected `" + expectedDigest + "`.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const ProductAddonChecker = {
|
||||
/**
|
||||
* Downloads a list of add-ons from a URL optionally testing the SSL
|
||||
* certificate for certain attributes.
|
||||
*
|
||||
* @param url
|
||||
* The url to download from.
|
||||
* @param allowNonBuiltIn
|
||||
* Whether to trust SSL certificates without a built-in CA issuer.
|
||||
* @param allowedCerts
|
||||
* The list of certificate attributes to match the SSL certificate
|
||||
* against or null to skip checks.
|
||||
* @return a promise that resolves to the list of add-ons or rejects with a JS
|
||||
* exception in case of error.
|
||||
*/
|
||||
getProductAddonList: function(url, allowNonBuiltIn = false, allowedCerts = null) {
|
||||
return downloadXML(url, allowNonBuiltIn, allowedCerts).then(parseXML);
|
||||
},
|
||||
|
||||
/**
|
||||
* Downloads an add-on to a local file and checks that it matches the expected
|
||||
* file. The caller is responsible for deleting the temporary file returned.
|
||||
*
|
||||
* @param addon
|
||||
* The addon to download.
|
||||
* @return a promise that resolves to the temporary file downloaded or rejects
|
||||
* with a JS exception in case of error.
|
||||
*/
|
||||
downloadAddon: Task.async(function*(addon) {
|
||||
let path = yield downloadFile(addon.URL);
|
||||
try {
|
||||
yield verifyFile(addon, path);
|
||||
return path;
|
||||
}
|
||||
catch (e) {
|
||||
yield OS.File.remove(path);
|
||||
throw e;
|
||||
}
|
||||
})
|
||||
}
|
@ -42,10 +42,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "BrowserToolboxProcess",
|
||||
"resource:///modules/devtools/client/framework/ToolboxProcess.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ConsoleAPI",
|
||||
"resource://gre/modules/devtools/shared/Console.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ProductAddonChecker",
|
||||
"resource://gre/modules/addons/ProductAddonChecker.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "Blocklist",
|
||||
"@mozilla.org/extensions/blocklist;1",
|
||||
@ -103,7 +99,6 @@ const PREF_BRANCH_INSTALLED_ADDON = "extensions.installedDistroAddon.";
|
||||
const PREF_SHOWN_SELECTION_UI = "extensions.shownSelectionUI";
|
||||
const PREF_INTERPOSITION_ENABLED = "extensions.interposition.enabled";
|
||||
const PREF_SYSTEM_ADDON_SET = "extensions.systemAddonSet";
|
||||
const PREF_SYSTEM_ADDON_UPDATE_URL = "extensions.systemAddon.update.url";
|
||||
|
||||
const PREF_EM_MIN_COMPAT_APP_VERSION = "extensions.minCompatibleAppVersion";
|
||||
const PREF_EM_MIN_COMPAT_PLATFORM_VERSION = "extensions.minCompatiblePlatformVersion";
|
||||
@ -315,25 +310,6 @@ LAZY_OBJECTS.forEach(name => {
|
||||
});
|
||||
|
||||
|
||||
// Behaves like Promise.all except waits for all promises to resolve/reject
|
||||
// before resolving/rejecting itself
|
||||
function waitForAllPromises(promises) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let shouldReject = false;
|
||||
let rejectValue = null;
|
||||
|
||||
let newPromises = [
|
||||
for (p of promises)
|
||||
p.catch(value => {
|
||||
shouldReject = true;
|
||||
rejectValue = value;
|
||||
})
|
||||
]
|
||||
Promise.all(newPromises)
|
||||
.then((results) => shouldReject ? reject(rejectValue) : resolve(results));
|
||||
});
|
||||
}
|
||||
|
||||
function findMatchingStaticBlocklistItem(aAddon) {
|
||||
for (let item of STATIC_BLOCKLIST_PATTERNS) {
|
||||
if ("creator" in item && typeof item.creator == "string") {
|
||||
@ -2783,91 +2759,6 @@ this.XPIProvider = {
|
||||
!XPIDatabase.writeAddonsList());
|
||||
},
|
||||
|
||||
updateSystemAddons: Task.async(function XPI_updateSystemAddons() {
|
||||
// Download the list of system add-ons
|
||||
let url = Preferences.get(PREF_SYSTEM_ADDON_UPDATE_URL, null);
|
||||
if (!url)
|
||||
return;
|
||||
|
||||
url = UpdateUtils.formatUpdateURL(url);
|
||||
|
||||
logger.info(`Starting system add-on update check from ${url}.`);
|
||||
let addonList = yield ProductAddonChecker.getProductAddonList(url);
|
||||
|
||||
// If there was no list then do nothing.
|
||||
if (!addonList) {
|
||||
logger.info("No system add-ons list was returned.");
|
||||
return;
|
||||
}
|
||||
|
||||
addonList = [for (spec of addonList) { spec, path: null, addon: null }];
|
||||
|
||||
// Bug 1204159: If this matches the current set in the profile or app locations
|
||||
// then just switch to those
|
||||
|
||||
let systemAddonLocation = XPIProvider.installLocationsByName[KEY_APP_SYSTEM_ADDONS];
|
||||
|
||||
// Download all the add-ons
|
||||
// Bug 1204158: If we already have some of these locally then just use those
|
||||
let downloadAddon = Task.async(function*(item) {
|
||||
try {
|
||||
item.path = yield ProductAddonChecker.downloadAddon(item.spec);
|
||||
item.addon = yield loadManifestFromFile(nsIFile(item.path), systemAddonLocation);
|
||||
}
|
||||
catch (e) {
|
||||
logger.error(`Failed to download system add-on ${item.spec.id}`, e);
|
||||
}
|
||||
});
|
||||
yield Promise.all([for (item of addonList) downloadAddon(item)]);
|
||||
|
||||
// The download promises all resolve regardless, now check if they all
|
||||
// succeeded
|
||||
let validateAddon = (item) => {
|
||||
if (item.spec.id != item.addon.id) {
|
||||
logger.warn(`Downloaded system add-on expected to be ${item.spec.id} but was ${item.addon.id}.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item.spec.version != item.addon.version) {
|
||||
logger.warn(`Expected system add-on ${item.spec.id} to be version ${item.version} but was ${item.addon.version}.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!systemAddonLocation.isValidAddon(item.addon))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!addonList.every(item => item.path && item.addon && validateAddon(item))) {
|
||||
throw new Error("Rejecting updated system add-on set that either could not " +
|
||||
"be downloaded or contained unusable add-ons.");
|
||||
}
|
||||
|
||||
// Install into the install location
|
||||
logger.info("Installing new system add-on set");
|
||||
yield systemAddonLocation.installAddonSet([for (item of addonList) item.addon]);
|
||||
|
||||
// Bug 1204156: Switch to the new system add-ons without requiring a restart
|
||||
}
|
||||
finally {
|
||||
// Delete the temporary files
|
||||
logger.info("Deleting temporary files");
|
||||
for (let item of addonList) {
|
||||
// If this item downloaded delete the temporary file.
|
||||
if (item.path) {
|
||||
try {
|
||||
yield OS.File.remove(item.path);
|
||||
}
|
||||
catch (e) {
|
||||
logger.warn(`Failed to remove temporary file ${item.path}.`, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Verifies that all installed add-ons are still correctly signed.
|
||||
*/
|
||||
@ -7465,110 +7356,40 @@ Object.assign(SystemAddonInstallLocation.prototype, {
|
||||
return this._directory != null;
|
||||
},
|
||||
|
||||
isValidAddon: function(aAddon) {
|
||||
if (aAddon.appDisabled) {
|
||||
logger.warn(`System add-on ${aAddon.id} isn't compatible with the application.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aAddon.unpack) {
|
||||
logger.warn(`System add-on ${aAddon.id} isn't a packed add-on.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!aAddon.bootstrap) {
|
||||
logger.warn(`System add-on ${aAddon.id} isn't restartless.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Tests whether the loaded add-on information matches what is expected.
|
||||
*/
|
||||
isValid: function(aAddons) {
|
||||
for (let id of Object.keys(this._addonSet.addons)) {
|
||||
if (!aAddons.has(id)) {
|
||||
logger.warn(`Expected add-on ${id} is missing from the system add-on location.`);
|
||||
logger.warn("Expected add-on " + id + " is missing from the system add-on location.");
|
||||
return false;
|
||||
}
|
||||
|
||||
let addon = aAddons.get(id);
|
||||
if (addon.version != this._addonSet.addons[id].version) {
|
||||
logger.warn(`Expected system add-on ${id} to be version ${this._addonSet.addons[id].version} but was ${addon.version}.`);
|
||||
if (addon.appDisabled) {
|
||||
logger.warn("System add-on " + id + " isn't compatible with the application.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.isValidAddon(addon))
|
||||
if (addon.unpack) {
|
||||
logger.warn("System add-on " + id + " isn't a packed add-on.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!addon.bootstrap) {
|
||||
logger.warn("System add-on " + id + " isn't restartless.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (addon.version != this._addonSet.addons[id].version) {
|
||||
logger.warn("System add-on " + id + " wasn't the correct version.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Installs a new set of system add-ons into the location and updates the
|
||||
* add-on set in prefs. We wait to switch state until a restart.
|
||||
*/
|
||||
installAddonSet: Task.async(function(aAddons) {
|
||||
// Make sure the base dir exists
|
||||
yield OS.File.makeDir(this._baseDir.path, { ignoreExisting: true });
|
||||
|
||||
let newDir = this._baseDir.clone();
|
||||
|
||||
let uuidGen = Cc["@mozilla.org/uuid-generator;1"].
|
||||
getService(Ci.nsIUUIDGenerator);
|
||||
newDir.append("blank");
|
||||
|
||||
while (true) {
|
||||
newDir.leafName = uuidGen.generateUUID().toString();
|
||||
|
||||
try {
|
||||
yield OS.File.makeDir(newDir.path, { ignoreExisting: false });
|
||||
break;
|
||||
}
|
||||
catch (e) {
|
||||
// Directory already exists, pick another
|
||||
}
|
||||
}
|
||||
|
||||
let copyAddon = Task.async(function*(addon) {
|
||||
let target = OS.Path.join(newDir.path, addon.id + ".xpi");
|
||||
logger.info(`Copying ${addon.id} from ${addon._sourceBundle.path} to ${target}.`);
|
||||
try {
|
||||
yield OS.File.copy(addon._sourceBundle.path, target);
|
||||
}
|
||||
catch (e) {
|
||||
logger.error(`Failed to copy ${addon.id} from ${addon._sourceBundle.path} to ${target}.`, e);
|
||||
throw e;
|
||||
}
|
||||
addon._sourceBundle = new nsIFile(target);
|
||||
});
|
||||
|
||||
try {
|
||||
yield waitForAllPromises([for (addon of aAddons) copyAddon(addon)]);
|
||||
}
|
||||
catch (e) {
|
||||
try {
|
||||
yield OS.File.removeDir(newDir.path, { ignorePermissions: true });
|
||||
}
|
||||
catch (e) {
|
||||
logger.warn(`Failed to remove new system add-on directory ${newDir.path}.`, e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
// All add-ons in position, create the new state and store it in prefs
|
||||
let state = { schema: 1, directory: newDir.leafName, addons: {} };
|
||||
for (let addon of aAddons) {
|
||||
state.addons[addon.id] = {
|
||||
version: addon.version
|
||||
}
|
||||
}
|
||||
|
||||
this._saveAddonSet(state);
|
||||
}),
|
||||
});
|
||||
|
||||
#ifdef XP_WIN
|
||||
|
@ -12,7 +12,6 @@ EXTRA_JS_MODULES.addons += [
|
||||
'Content.js',
|
||||
'GMPProvider.jsm',
|
||||
'LightweightThemeImageOptimizer.jsm',
|
||||
'ProductAddonChecker.jsm',
|
||||
'SpellCheckDictionaryBootstrap.js',
|
||||
'WebExtensionBootstrap.js',
|
||||
]
|
||||
|
@ -23,8 +23,8 @@ try {
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
||||
"resource://gre/modules/FileUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS",
|
||||
"resource://gre/modules/osfile.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
@ -559,7 +559,7 @@ Blocklist.prototype = {
|
||||
dsURI = dsURI.replace(/%BUILD_TARGET%/g, gApp.OS + "_" + gABI);
|
||||
dsURI = dsURI.replace(/%OS_VERSION%/g, gOSVersion);
|
||||
dsURI = dsURI.replace(/%LOCALE%/g, getLocale());
|
||||
dsURI = dsURI.replace(/%CHANNEL%/g, UpdateUtils.UpdateChannel);
|
||||
dsURI = dsURI.replace(/%CHANNEL%/g, UpdateChannel.get());
|
||||
dsURI = dsURI.replace(/%PLATFORM_VERSION%/g, gApp.platformVersion);
|
||||
dsURI = dsURI.replace(/%DISTRIBUTION%/g,
|
||||
getDistributionPrefValue(PREF_APP_DISTRIBUTION));
|
||||
|
@ -1 +0,0 @@
|
||||
Not an xml file!
|
@ -1,3 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<foobar></barfoo>
|
@ -1,3 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<test></test>
|
@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<updates>
|
||||
<addons></addons>
|
||||
</updates>
|
@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<updates>
|
||||
<addons>
|
||||
<addon id="test1" URL="http://example.com/test1.xpi"/>
|
||||
<addon id="test2" URL="http://example.com/test2.xpi" hashFunction="md5" hashValue="djhfgsjdhf"/>
|
||||
<addon id="test3" URL="http://example.com/test3.xpi" version="1.0" size="45"/>
|
||||
<addon id="test4"/>
|
||||
<addon URL="http://example.com/test5.xpi"/>
|
||||
</addons>
|
||||
</updates>
|
@ -1,3 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<updates></updates>
|
Binary file not shown.
@ -0,0 +1,18 @@
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const ID = "system1@tests.mozilla.org";
|
||||
const VERSION = "1.0";
|
||||
|
||||
function install(data, reason) {
|
||||
}
|
||||
|
||||
function startup(data, reason) {
|
||||
Services.prefs.setCharPref("bootstraptest." + ID + ".active_version", VERSION);
|
||||
}
|
||||
|
||||
function shutdown(data, reason) {
|
||||
Services.prefs.clearUserPref("bootstraptest." + ID + ".active_version");
|
||||
}
|
||||
|
||||
function uninstall(data, reason) {
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>system1@tests.mozilla.org</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
|
||||
<!-- Front End MetaData -->
|
||||
<em:name>System Add-on 1</em:name>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>5</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
</RDF>
|
@ -0,0 +1,18 @@
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const ID = "system2@tests.mozilla.org";
|
||||
const VERSION = "1.0";
|
||||
|
||||
function install(data, reason) {
|
||||
}
|
||||
|
||||
function startup(data, reason) {
|
||||
Services.prefs.setCharPref("bootstraptest." + ID + ".active_version", VERSION);
|
||||
}
|
||||
|
||||
function shutdown(data, reason) {
|
||||
Services.prefs.clearUserPref("bootstraptest." + ID + ".active_version");
|
||||
}
|
||||
|
||||
function uninstall(data, reason) {
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>system2@tests.mozilla.org</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
|
||||
<!-- Front End MetaData -->
|
||||
<em:name>System Add-on 2</em:name>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>5</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
</RDF>
|
@ -0,0 +1,18 @@
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const ID = "system1@tests.mozilla.org";
|
||||
const VERSION = "2.0";
|
||||
|
||||
function install(data, reason) {
|
||||
}
|
||||
|
||||
function startup(data, reason) {
|
||||
Services.prefs.setCharPref("bootstraptest." + ID + ".active_version", VERSION);
|
||||
}
|
||||
|
||||
function shutdown(data, reason) {
|
||||
Services.prefs.clearUserPref("bootstraptest." + ID + ".active_version");
|
||||
}
|
||||
|
||||
function uninstall(data, reason) {
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>system1@tests.mozilla.org</em:id>
|
||||
<em:version>2.0</em:version>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
|
||||
<!-- Front End MetaData -->
|
||||
<em:name>System Add-on 1</em:name>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>5</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
</RDF>
|
@ -0,0 +1,18 @@
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const ID = "system3@tests.mozilla.org";
|
||||
const VERSION = "1.0";
|
||||
|
||||
function install(data, reason) {
|
||||
}
|
||||
|
||||
function startup(data, reason) {
|
||||
Services.prefs.setCharPref("bootstraptest." + ID + ".active_version", VERSION);
|
||||
}
|
||||
|
||||
function shutdown(data, reason) {
|
||||
Services.prefs.clearUserPref("bootstraptest." + ID + ".active_version");
|
||||
}
|
||||
|
||||
function uninstall(data, reason) {
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>system3@tests.mozilla.org</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
|
||||
<!-- Front End MetaData -->
|
||||
<em:name>System Add-on 3</em:name>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>5</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
</RDF>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,264 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
Components.utils.import("resource://gre/modules/addons/ProductAddonChecker.jsm");
|
||||
Components.utils.import("resource://testing-common/httpd.js");
|
||||
Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
const LocalFile = new Components.Constructor("@mozilla.org/file/local;1", AM_Ci.nsIFile, "initWithPath");
|
||||
|
||||
let testserver = new HttpServer();
|
||||
testserver.registerDirectory("/data/", do_get_file("data/productaddons"));
|
||||
testserver.start();
|
||||
let root = testserver.identity.primaryScheme + "://" +
|
||||
testserver.identity.primaryHost + ":" +
|
||||
testserver.identity.primaryPort + "/data/"
|
||||
|
||||
/**
|
||||
* Compares binary data of 2 arrays and returns true if they are the same
|
||||
*
|
||||
* @param arr1 The first array to compare
|
||||
* @param arr2 The second array to compare
|
||||
*/
|
||||
function compareBinaryData(arr1, arr2) {
|
||||
do_check_eq(arr1.length, arr2.length);
|
||||
for (let i = 0; i < arr1.length; i++) {
|
||||
if (arr1[i] != arr2[i]) {
|
||||
do_print("Data differs at index " + i +
|
||||
", arr1: " + arr1[i] + ", arr2: " + arr2[i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a file's data and returns it
|
||||
*
|
||||
* @param file The file to read the data from
|
||||
* @return array of bytes for the data in the file.
|
||||
*/
|
||||
function getBinaryFileData(file) {
|
||||
let fileStream = AM_Cc["@mozilla.org/network/file-input-stream;1"].
|
||||
createInstance(AM_Ci.nsIFileInputStream);
|
||||
// Open as RD_ONLY with default permissions.
|
||||
fileStream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
|
||||
|
||||
let stream = AM_Cc["@mozilla.org/binaryinputstream;1"].
|
||||
createInstance(AM_Ci.nsIBinaryInputStream);
|
||||
stream.setInputStream(fileStream);
|
||||
let bytes = stream.readByteArray(stream.available());
|
||||
fileStream.close();
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares binary data of 2 files and returns true if they are the same
|
||||
*
|
||||
* @param file1 The first file to compare
|
||||
* @param file2 The second file to compare
|
||||
*/
|
||||
function compareFiles(file1, file2) {
|
||||
return compareBinaryData(getBinaryFileData(file1), getBinaryFileData(file2));
|
||||
}
|
||||
|
||||
add_task(function* test_404() {
|
||||
try {
|
||||
let addons = yield ProductAddonChecker.getProductAddonList(root + "404.xml");
|
||||
do_throw("Should not have returned anything");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_true(true, "Expected to throw for a missing update file");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_not_xml() {
|
||||
try {
|
||||
let addons = yield ProductAddonChecker.getProductAddonList(root + "bad.txt");
|
||||
do_throw("Should not have returned anything");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_true(true, "Expected to throw for a non XML result");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_invalid_xml() {
|
||||
try {
|
||||
let addons = yield ProductAddonChecker.getProductAddonList(root + "bad.xml");
|
||||
do_throw("Should not have returned anything");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_true(true, "Expected to throw for invalid XML");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_wrong_xml() {
|
||||
try {
|
||||
let addons = yield ProductAddonChecker.getProductAddonList(root + "bad2.xml");
|
||||
do_throw("Should not have returned anything");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_true(true, "Expected to throw for a missing <updates> tag");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_missing() {
|
||||
let addons = yield ProductAddonChecker.getProductAddonList(root + "missing.xml");
|
||||
do_check_eq(addons, null);
|
||||
});
|
||||
|
||||
add_task(function* test_empty() {
|
||||
let addons = yield ProductAddonChecker.getProductAddonList(root + "empty.xml");
|
||||
do_check_true(Array.isArray(addons));
|
||||
do_check_eq(addons.length, 0);
|
||||
});
|
||||
|
||||
add_task(function* test_good_xml() {
|
||||
let addons = yield ProductAddonChecker.getProductAddonList(root + "good.xml");
|
||||
do_check_true(Array.isArray(addons));
|
||||
|
||||
// There are three valid entries in the XML
|
||||
do_check_eq(addons.length, 5);
|
||||
|
||||
let addon = addons[0];
|
||||
do_check_eq(addon.id, "test1");
|
||||
do_check_eq(addon.URL, "http://example.com/test1.xpi");
|
||||
do_check_eq(addon.hashFunction, undefined);
|
||||
do_check_eq(addon.hashValue, undefined);
|
||||
do_check_eq(addon.version, undefined);
|
||||
do_check_eq(addon.size, undefined);
|
||||
|
||||
addon = addons[1];
|
||||
do_check_eq(addon.id, "test2");
|
||||
do_check_eq(addon.URL, "http://example.com/test2.xpi");
|
||||
do_check_eq(addon.hashFunction, "md5");
|
||||
do_check_eq(addon.hashValue, "djhfgsjdhf");
|
||||
do_check_eq(addon.version, undefined);
|
||||
do_check_eq(addon.size, undefined);
|
||||
|
||||
addon = addons[2];
|
||||
do_check_eq(addon.id, "test3");
|
||||
do_check_eq(addon.URL, "http://example.com/test3.xpi");
|
||||
do_check_eq(addon.hashFunction, undefined);
|
||||
do_check_eq(addon.hashValue, undefined);
|
||||
do_check_eq(addon.version, "1.0");
|
||||
do_check_eq(addon.size, 45);
|
||||
|
||||
addon = addons[3];
|
||||
do_check_eq(addon.id, "test4");
|
||||
do_check_eq(addon.URL, undefined);
|
||||
do_check_eq(addon.hashFunction, undefined);
|
||||
do_check_eq(addon.hashValue, undefined);
|
||||
do_check_eq(addon.version, undefined);
|
||||
do_check_eq(addon.size, undefined);
|
||||
|
||||
addon = addons[4];
|
||||
do_check_eq(addon.id, undefined);
|
||||
do_check_eq(addon.URL, "http://example.com/test5.xpi");
|
||||
do_check_eq(addon.hashFunction, undefined);
|
||||
do_check_eq(addon.hashValue, undefined);
|
||||
do_check_eq(addon.version, undefined);
|
||||
do_check_eq(addon.size, undefined);
|
||||
});
|
||||
|
||||
add_task(function* test_download_nourl() {
|
||||
try {
|
||||
let path = yield ProductAddonChecker.downloadAddon({});
|
||||
|
||||
yield OS.File.remove(path);
|
||||
do_throw("Should not have downloaded a file with a missing url");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_true(true, "Should have thrown when downloading a file with a missing url.");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_download_missing() {
|
||||
try {
|
||||
let path = yield ProductAddonChecker.downloadAddon({
|
||||
URL: root + "nofile.xpi",
|
||||
});
|
||||
|
||||
yield OS.File.remove(path);
|
||||
do_throw("Should not have downloaded a missing file");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_true(true, "Should have thrown when downloading a missing file.");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_download_noverify() {
|
||||
let path = yield ProductAddonChecker.downloadAddon({
|
||||
URL: root + "unsigned.xpi",
|
||||
});
|
||||
|
||||
let stat = yield OS.File.stat(path);
|
||||
do_check_false(stat.isDir);
|
||||
do_check_eq(stat.size, 452)
|
||||
|
||||
do_check_true(compareFiles(do_get_file("data/productaddons/unsigned.xpi"), new LocalFile(path)));
|
||||
|
||||
yield OS.File.remove(path);
|
||||
});
|
||||
|
||||
add_task(function* test_download_badsize() {
|
||||
try {
|
||||
let path = yield ProductAddonChecker.downloadAddon({
|
||||
URL: root + "unsigned.xpi",
|
||||
size: 400,
|
||||
});
|
||||
|
||||
yield OS.File.remove(path);
|
||||
do_throw("Should not have downloaded a file with a bad size");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_true(true, "Should have thrown when downloading a file with a bad size.");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_download_badhashfn() {
|
||||
try {
|
||||
let path = yield ProductAddonChecker.downloadAddon({
|
||||
URL: root + "unsigned.xpi",
|
||||
hashFunction: "sha2567",
|
||||
hashValue: "9b9abf7ddfc1a6d7ffc7e0247481dcc202363e4445ad3494fb22036f1698c7f3",
|
||||
});
|
||||
|
||||
yield OS.File.remove(path);
|
||||
do_throw("Should not have downloaded a file with a bad hash function");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_true(true, "Should have thrown when downloading a file with a bad hash function.");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_download_badhash() {
|
||||
try {
|
||||
let path = yield ProductAddonChecker.downloadAddon({
|
||||
URL: root + "unsigned.xpi",
|
||||
hashFunction: "sha256",
|
||||
hashValue: "8b9abf7ddfc1a6d7ffc7e0247481dcc202363e4445ad3494fb22036f1698c7f3",
|
||||
});
|
||||
|
||||
yield OS.File.remove(path);
|
||||
do_throw("Should not have downloaded a file with a bad hash");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_true(true, "Should have thrown when downloading a file with a bad hash.");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_download_works() {
|
||||
let path = yield ProductAddonChecker.downloadAddon({
|
||||
URL: root + "unsigned.xpi",
|
||||
size: 452,
|
||||
hashFunction: "sha256",
|
||||
hashValue: "9b9abf7ddfc1a6d7ffc7e0247481dcc202363e4445ad3494fb22036f1698c7f3",
|
||||
});
|
||||
|
||||
let stat = yield OS.File.stat(path);
|
||||
do_check_false(stat.isDir);
|
||||
|
||||
do_check_true(compareFiles(do_get_file("data/productaddons/unsigned.xpi"), new LocalFile(path)));
|
||||
|
||||
yield OS.File.remove(path);
|
||||
});
|
@ -6,7 +6,6 @@
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
var GMPScope = Cu.import("resource://gre/modules/addons/GMPProvider.jsm");
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
Cu.import("resource://gre/modules/UpdateUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "pluginsBundle",
|
||||
() => Services.strings.createBundle("chrome://global/locale/plugins.properties"));
|
||||
@ -339,7 +338,7 @@ add_task(function* test_pluginRegistration() {
|
||||
clearPaths();
|
||||
gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id),
|
||||
TEST_VERSION);
|
||||
gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ABI, addon.id), UpdateUtils.ABI);
|
||||
gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ABI, addon.id), GMPScope.GMPUtils.ABI());
|
||||
yield promiseRestartManager();
|
||||
Assert.notEqual(addedPaths.indexOf(file.path), -1);
|
||||
Assert.deepEqual(removedPaths, []);
|
||||
|
@ -5,22 +5,10 @@ const PREF_SYSTEM_ADDON_SET = "extensions.systemAddonSet";
|
||||
// Enable signature checks for these tests
|
||||
Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, true);
|
||||
|
||||
const featureDir = FileUtils.getDir("ProfD", ["features"]);
|
||||
const featureDir = gProfD.clone();
|
||||
featureDir.append("features");
|
||||
|
||||
// Build the test sets
|
||||
let dir = FileUtils.getDir("ProfD", ["sysfeatures", "app1", "features"], true);
|
||||
do_get_file("data/system_addons/system1_1.xpi").copyTo(dir, "system1@tests.mozilla.org.xpi");
|
||||
do_get_file("data/system_addons/system2_1.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi");
|
||||
|
||||
dir = FileUtils.getDir("ProfD", ["sysfeatures", "app2", "features"], true);
|
||||
do_get_file("data/system_addons/system1_2.xpi").copyTo(dir, "system1@tests.mozilla.org.xpi");
|
||||
do_get_file("data/system_addons/system3_1.xpi").copyTo(dir, "system3@tests.mozilla.org.xpi");
|
||||
|
||||
dir = FileUtils.getDir("ProfD", ["sysfeatures", "app3", "features"], true);
|
||||
do_get_file("data/system_addons/system1_1_badcert.xpi").copyTo(dir, "system1@tests.mozilla.org.xpi");
|
||||
do_get_file("data/system_addons/system3_1.xpi").copyTo(dir, "system3@tests.mozilla.org.xpi");
|
||||
|
||||
const distroDir = FileUtils.getDir("ProfD", ["sysfeatures", "app0"], true);
|
||||
const distroDir = do_get_file("data/system_addons/app0");
|
||||
registerDirectory("XREAppDist", distroDir);
|
||||
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "0");
|
||||
@ -148,10 +136,10 @@ add_task(function* test_updated() {
|
||||
featureDir.append(dirname);
|
||||
|
||||
// Copy in the system add-ons
|
||||
let file = do_get_file("data/system_addons/system2_1.xpi");
|
||||
file.copyTo(featureDir, "system2@tests.mozilla.org.xpi");
|
||||
file = do_get_file("data/system_addons/system3_1.xpi");
|
||||
file.copyTo(featureDir, "system3@tests.mozilla.org.xpi");
|
||||
let file = do_get_file("data/system_addons/app1/features/system2@tests.mozilla.org.xpi");
|
||||
file.copyTo(featureDir, file.leafName);
|
||||
file = do_get_file("data/system_addons/app2/features/system3@tests.mozilla.org.xpi");
|
||||
file.copyTo(featureDir, file.leafName);
|
||||
|
||||
// Inject it into the system set
|
||||
let addonSet = {
|
||||
@ -178,8 +166,8 @@ add_task(function* test_updated() {
|
||||
// An additional add-on in the directory should be ignored
|
||||
add_task(function* test_skips_additional() {
|
||||
// Copy in the system add-ons
|
||||
let file = do_get_file("data/system_addons/system1_1.xpi");
|
||||
file.copyTo(featureDir, "system1@tests.mozilla.org.xpi");
|
||||
let file = do_get_file("data/system_addons/app1/features/system1@tests.mozilla.org.xpi");
|
||||
file.copyTo(featureDir, file.leafName);
|
||||
|
||||
startupManager(false);
|
||||
|
||||
@ -203,8 +191,8 @@ add_task(function* test_revert() {
|
||||
|
||||
// Putting it back will make the set work again
|
||||
add_task(function* test_reuse() {
|
||||
let file = do_get_file("data/system_addons/system2_1.xpi");
|
||||
file.copyTo(featureDir, "system2@tests.mozilla.org.xpi");
|
||||
let file = do_get_file("data/system_addons/app1/features/system2@tests.mozilla.org.xpi");
|
||||
file.copyTo(featureDir, file.leafName);
|
||||
|
||||
startupManager(false);
|
||||
|
||||
@ -226,8 +214,8 @@ add_task(function* test_corrupt_pref() {
|
||||
|
||||
// An add-on with a bad certificate should cause us to use the default set
|
||||
add_task(function* test_bad_profile_cert() {
|
||||
let file = do_get_file("data/system_addons/system1_1_badcert.xpi");
|
||||
file.copyTo(featureDir, "system1@tests.mozilla.org.xpi");
|
||||
let file = do_get_file("data/system_addons/app3/features/system1@tests.mozilla.org.xpi");
|
||||
file.copyTo(featureDir, file.leafName);
|
||||
|
||||
// Inject it into the system set
|
||||
let addonSet = {
|
||||
|
@ -1,469 +0,0 @@
|
||||
// Tests that we reset to the default system add-ons correctly when switching
|
||||
// application versions
|
||||
const PREF_SYSTEM_ADDON_SET = "extensions.systemAddonSet";
|
||||
const PREF_SYSTEM_ADDON_UPDATE_URL = "extensions.systemAddon.update.url";
|
||||
const PREF_XPI_STATE = "extensions.xpiState";
|
||||
const PREF_APP_UPDATE_ENABLED = "app.update.enabled";
|
||||
|
||||
Components.utils.import("resource://testing-common/httpd.js");
|
||||
const { computeHash } = Components.utils.import("resource://gre/modules/addons/ProductAddonChecker.jsm");
|
||||
|
||||
// Enable signature checks for these tests
|
||||
//Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, true);
|
||||
|
||||
const featureDir = FileUtils.getDir("ProfD", ["features"]);
|
||||
|
||||
// Build the test sets
|
||||
let dir = FileUtils.getDir("ProfD", ["features", "prefilled"], true);
|
||||
do_get_file("data/system_addons/system2_2.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi");
|
||||
do_get_file("data/system_addons/system3_2.xpi").copyTo(dir, "system3@tests.mozilla.org.xpi");
|
||||
|
||||
// Mark these in the past so the startup file scan notices when files have changed properly
|
||||
FileUtils.getFile("ProfD", ["features", "prefilled", "system2@tests.mozilla.org.xpi"]).lastModifiedTime -= 10000;
|
||||
FileUtils.getFile("ProfD", ["features", "prefilled", "system3@tests.mozilla.org.xpi"]).lastModifiedTime -= 10000;
|
||||
|
||||
const prefilledSet = {
|
||||
schema: 1,
|
||||
directory: dir.leafName,
|
||||
addons: {
|
||||
"system2@tests.mozilla.org": {
|
||||
version: "2.0"
|
||||
},
|
||||
"system3@tests.mozilla.org": {
|
||||
version: "2.0"
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
dir = FileUtils.getDir("ProfD", ["sysfeatures", "hidden", "features"], true);
|
||||
do_get_file("data/system_addons/system1_1.xpi").copyTo(dir, "system1@tests.mozilla.org.xpi");
|
||||
do_get_file("data/system_addons/system2_1.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi");
|
||||
|
||||
dir = FileUtils.getDir("ProfD", ["sysfeatures", "prefilled", "features"], true);
|
||||
do_get_file("data/system_addons/system2_2.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi");
|
||||
do_get_file("data/system_addons/system3_2.xpi").copyTo(dir, "system3@tests.mozilla.org.xpi");
|
||||
|
||||
const distroDir = FileUtils.getDir("ProfD", ["sysfeatures", "empty"], true);
|
||||
registerDirectory("XREAppDist", distroDir);
|
||||
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2");
|
||||
|
||||
let testserver = new HttpServer();
|
||||
testserver.registerDirectory("/data/", do_get_file("data/system_addons"));
|
||||
testserver.start();
|
||||
let root = testserver.identity.primaryScheme + "://" +
|
||||
testserver.identity.primaryHost + ":" +
|
||||
testserver.identity.primaryPort + "/data/"
|
||||
Services.prefs.setCharPref(PREF_SYSTEM_ADDON_UPDATE_URL, root + "update.xml");
|
||||
|
||||
function makeUUID() {
|
||||
let uuidGen = AM_Cc["@mozilla.org/uuid-generator;1"].
|
||||
getService(AM_Ci.nsIUUIDGenerator);
|
||||
return uuidGen.generateUUID().toString();
|
||||
}
|
||||
|
||||
function* serve_update(xml, perform_update) {
|
||||
testserver.registerPathHandler("/data/update.xml", (request, response) => {
|
||||
response.write(xml);
|
||||
});
|
||||
|
||||
try {
|
||||
yield perform_update();
|
||||
}
|
||||
finally {
|
||||
testserver.registerPathHandler("/data/update.xml", null);
|
||||
}
|
||||
}
|
||||
|
||||
// Runs an update check making it use the passed in xml string. Uses the direct
|
||||
// call to the update function so we get rejections on failure.
|
||||
function* install_system_addons(xml) {
|
||||
do_print("Triggering system add-on update check.");
|
||||
|
||||
yield serve_update(xml, function*() {
|
||||
let { XPIProvider } = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm");
|
||||
yield XPIProvider.updateSystemAddons();
|
||||
});
|
||||
}
|
||||
|
||||
// Runs a full add-on update check which will in some cases do a system add-on
|
||||
// update check. Always succeeds.
|
||||
function* update_all_addons(xml) {
|
||||
do_print("Triggering full add-on update check.");
|
||||
|
||||
yield serve_update(xml, function() {
|
||||
return new Promise(resolve => {
|
||||
Services.obs.addObserver(function() {
|
||||
Services.obs.removeObserver(arguments.callee, "addons-background-update-complete");
|
||||
|
||||
resolve();
|
||||
}, "addons-background-update-complete", false);
|
||||
|
||||
// Trigger the background update timer handler
|
||||
gInternalManager.notify(null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Builds an update.xml file for an update check based on the data passed.
|
||||
function* build_xml(addons) {
|
||||
let xml = `<?xml version="1.0" encoding="UTF-8"?>\n\n<updates>\n`;
|
||||
if (addons) {
|
||||
xml += ` <addons>\n`;
|
||||
for (let addon of addons) {
|
||||
xml += ` <addon id="${addon.id}" URL="${root + addon.path}" version="${addon.version}"`;
|
||||
if (addon.size)
|
||||
xml += ` size="${addon.size}"`;
|
||||
if (addon.hashFunction)
|
||||
xml += ` hashFunction="${addon.hashFunction}"`;
|
||||
if (addon.hashValue)
|
||||
xml += ` hashValue="${addon.hashValue}"`;
|
||||
xml += `/>\n`;
|
||||
}
|
||||
xml += ` </addons>\n`;
|
||||
}
|
||||
xml += `</updates>\n`;
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
function* check_installed(inProfile, ...versions) {
|
||||
for (let i = 0; i < versions.length; i++) {
|
||||
let id = "system" + (i + 1) + "@tests.mozilla.org";
|
||||
let addon = yield promiseAddonByID(id);
|
||||
|
||||
if (versions[i]) {
|
||||
// Add-on should be installed
|
||||
do_check_neq(addon, null);
|
||||
do_check_eq(addon.version, versions[i]);
|
||||
do_check_true(addon.isActive);
|
||||
do_check_false(addon.foreignInstall);
|
||||
|
||||
// Verify the add-ons file is in the right place
|
||||
let uri = addon.getResourceURI(null);
|
||||
do_check_true(uri instanceof AM_Ci.nsIFileURL);
|
||||
|
||||
let file = uri.file.parent;
|
||||
if (inProfile) {
|
||||
file = file.parent;
|
||||
do_check_eq(file.leafName, "features");
|
||||
file = file.parent;
|
||||
do_check_eq(file.path, gProfD.path);
|
||||
}
|
||||
else {
|
||||
do_check_eq(file.leafName, "features");
|
||||
file = file.parent;
|
||||
do_check_eq(file.path, distroDir.path);
|
||||
}
|
||||
|
||||
//do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_SYSTEM);
|
||||
|
||||
// Verify the add-on actually started
|
||||
let installed = Services.prefs.getCharPref("bootstraptest." + id + ".active_version");
|
||||
do_check_eq(installed, versions[i]);
|
||||
}
|
||||
else {
|
||||
if (inProfile) {
|
||||
// Add-on should not be installed
|
||||
do_check_eq(addon, null);
|
||||
}
|
||||
else {
|
||||
// Either add-on should not be installed or it shouldn't be active
|
||||
do_check_true(!addon || !addon.isActive);
|
||||
}
|
||||
|
||||
try {
|
||||
Services.prefs.getCharPref("bootstraptest." + id + ".active_version");
|
||||
do_throw("Expected pref to be missing");
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines the set of initial conditions to run each test against. Each should
|
||||
* define the following properties:
|
||||
*
|
||||
* setup: A task to setup the profile into the initial state.
|
||||
* initialState: The initial expected system add-on state after setup has run.
|
||||
*/
|
||||
const TEST_CONDITIONS = {
|
||||
// Runs tests with no updated or default system add-ons initially installed
|
||||
blank: {
|
||||
setup: function*() {
|
||||
Services.prefs.clearUserPref(PREF_SYSTEM_ADDON_SET);
|
||||
distroDir.leafName = "empty";
|
||||
},
|
||||
initialState: [false, null, null, null, null, null],
|
||||
},
|
||||
|
||||
// Runs tests with default system add-ons installed
|
||||
withAppSet: {
|
||||
setup: function*() {
|
||||
Services.prefs.clearUserPref(PREF_SYSTEM_ADDON_SET);
|
||||
distroDir.leafName = "prefilled";
|
||||
},
|
||||
initialState: [false, null, "2.0", "2.0", null, null],
|
||||
},
|
||||
|
||||
// Runs tests with updated system add-ons installed
|
||||
withProfileSet: {
|
||||
setup: function*() {
|
||||
Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, JSON.stringify(prefilledSet));
|
||||
distroDir.leafName = "empty";
|
||||
},
|
||||
initialState: [true, null, "2.0", "2.0", null, null],
|
||||
},
|
||||
|
||||
// Runs tests with both default and updated system add-ons installed
|
||||
withBothSets: {
|
||||
setup: function*() {
|
||||
Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, JSON.stringify(prefilledSet));
|
||||
distroDir.leafName = "hidden";
|
||||
},
|
||||
initialState: [true, null, "2.0", "2.0", null, null],
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The tests to run. Each test must define an updateList or test. The following
|
||||
* properties are used:
|
||||
*
|
||||
* updateList: The set of add-ons the server should respond with.
|
||||
* test: A function to run to perform the update check (replaces
|
||||
* updateList)
|
||||
* fails: An optional property, if true the update check is expected to
|
||||
* fail.
|
||||
* finalState: An optional property, the expected final state of system add-ons,
|
||||
* if missing the test condition's initialState is used.
|
||||
*/
|
||||
const TESTS = {
|
||||
// Test that an error response does nothing
|
||||
error: {
|
||||
test: function*() {
|
||||
try {
|
||||
yield install_system_addons("foobar");
|
||||
do_throw("Expected to fail the update check");
|
||||
}
|
||||
catch (e) {
|
||||
do_check_true(true, "Expected to fail the update check");
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// Test that a blank response does nothing
|
||||
blank: {
|
||||
updateList: null,
|
||||
},
|
||||
|
||||
// Test that an empty list updates to an empty set of system add-ons
|
||||
empty: {
|
||||
updateList: [],
|
||||
finalState: [true, null, null, null, null, null]
|
||||
},
|
||||
|
||||
// Tests that a new set of system add-ons gets installed
|
||||
newset: {
|
||||
updateList: [
|
||||
{ id: "system4@tests.mozilla.org", version: "1.0", path: "system4_1.xpi" },
|
||||
{ id: "system5@tests.mozilla.org", version: "1.0", path: "system5_1.xpi" }
|
||||
],
|
||||
finalState: [true, null, null, null, "1.0", "1.0"]
|
||||
},
|
||||
|
||||
// Tests that an upgraded set of system add-ons gets installed
|
||||
upgrades: {
|
||||
updateList: [
|
||||
{ id: "system2@tests.mozilla.org", version: "3.0", path: "system2_3.xpi" },
|
||||
{ id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi" }
|
||||
],
|
||||
finalState: [true, null, "3.0", "3.0", null, null]
|
||||
},
|
||||
|
||||
// Tests that a set of system add-ons, some new, some existing gets installed
|
||||
overlapping: {
|
||||
updateList: [
|
||||
{ id: "system1@tests.mozilla.org", version: "1.0", path: "system1_2.xpi" },
|
||||
{ id: "system2@tests.mozilla.org", version: "1.0", path: "system2_2.xpi" },
|
||||
{ id: "system3@tests.mozilla.org", version: "1.0", path: "system3_3.xpi" },
|
||||
{ id: "system4@tests.mozilla.org", version: "1.0", path: "system4_1.xpi" }
|
||||
],
|
||||
finalState: [true, "2.0", "2.0", "3.0", "1.0", null]
|
||||
},
|
||||
|
||||
// Specifying an incorrect version should stop us updating anything
|
||||
badVersion: {
|
||||
fails: true,
|
||||
updateList: [
|
||||
{ id: "system2@tests.mozilla.org", version: "4.0", path: "system2_3.xpi" },
|
||||
{ id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi" }
|
||||
],
|
||||
},
|
||||
|
||||
// Specifying an invalid size should stop us updating anything
|
||||
badSize: {
|
||||
fails: true,
|
||||
updateList: [
|
||||
{ id: "system2@tests.mozilla.org", version: "3.0", path: "system2_3.xpi", size: 2 },
|
||||
{ id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi" }
|
||||
],
|
||||
},
|
||||
|
||||
// Specifying an incorrect hash should stop us updating anything
|
||||
badHash: {
|
||||
fails: true,
|
||||
updateList: [
|
||||
{ id: "system2@tests.mozilla.org", version: "3.0", path: "system2_3.xpi" },
|
||||
{ id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi", hashFunction: "sha1", hashValue: "205a4c49bd513ebd30594e380c19e86bba1f83e2" }
|
||||
],
|
||||
},
|
||||
|
||||
// Correct sizes and hashes should work
|
||||
checkSizeHash: {
|
||||
updateList: [
|
||||
{ id: "system2@tests.mozilla.org", version: "3.0", path: "system2_3.xpi", size: 858 },
|
||||
{ id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi", hashFunction: "sha1", hashValue: "105a4c49bd513ebd30594e380c19e86bba1f83e2" },
|
||||
{ id: "system5@tests.mozilla.org", version: "1.0", path: "system5_1.xpi", size: 857, hashFunction: "sha1", hashValue: "664e9218be3c9acbb9029e715c1e5d2fbb4ea2cc" }
|
||||
],
|
||||
finalState: [true, null, "3.0", "3.0", null, "1.0"]
|
||||
},
|
||||
}
|
||||
|
||||
add_task(function* setup() {
|
||||
// Initialise the profile
|
||||
startupManager();
|
||||
yield promiseShutdownManager();
|
||||
})
|
||||
|
||||
function* setup_conditions(setup) {
|
||||
do_print("Setting up conditions.");
|
||||
yield setup.setup();
|
||||
|
||||
// Blow away the cache to force a rescan of the filesystem
|
||||
Services.prefs.clearUserPref(PREF_XPI_STATE);
|
||||
startupManager(false);
|
||||
|
||||
// Make sure the initial state is correct
|
||||
do_print("Checking initial state.");
|
||||
yield check_installed(...setup.initialState);
|
||||
}
|
||||
|
||||
function* verify_state(finalState) {
|
||||
do_print("Checking final state.");
|
||||
|
||||
// Bug 1204156: Currently switching to the new state requires a restart
|
||||
// yield check_installed(...finalState);
|
||||
|
||||
// Check that the new state is active after a restart
|
||||
yield promiseRestartManager();
|
||||
yield check_installed(...finalState);
|
||||
}
|
||||
|
||||
function* exec_test(setup, test) {
|
||||
yield setup_conditions(setup);
|
||||
|
||||
try {
|
||||
if ("test" in test) {
|
||||
yield test.test();
|
||||
}
|
||||
else {
|
||||
yield install_system_addons(yield build_xml(test.updateList));
|
||||
}
|
||||
|
||||
if (test.fails) {
|
||||
do_throw("Expected this test to fail");
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
if (!test.fails) {
|
||||
do_throw(e);
|
||||
}
|
||||
}
|
||||
|
||||
yield verify_state(test.finalState ? test.finalState : setup.initialState);
|
||||
|
||||
yield promiseShutdownManager();
|
||||
}
|
||||
|
||||
for (let setup of Object.keys(TEST_CONDITIONS)) {
|
||||
for (let test of Object.keys(TESTS)) {
|
||||
add_task(function*() {
|
||||
do_print("Running test " + setup + " " + test);
|
||||
|
||||
yield exec_test(TEST_CONDITIONS[setup], TESTS[test]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Some custom tests
|
||||
|
||||
// Test that the update check is performed as part of the regular add-on update
|
||||
// check
|
||||
add_task(function* test_addon_update() {
|
||||
yield setup_conditions(TEST_CONDITIONS.blank);
|
||||
|
||||
yield update_all_addons(yield build_xml([
|
||||
{ id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
|
||||
{ id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" }
|
||||
]));
|
||||
|
||||
yield verify_state([true, null, "2.0", "2.0", null, null]);
|
||||
|
||||
yield promiseShutdownManager();
|
||||
});
|
||||
|
||||
// Disabling app updates should block system add-on updates
|
||||
add_task(function* test_app_update_disabled() {
|
||||
yield setup_conditions(TEST_CONDITIONS.blank);
|
||||
|
||||
Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, false);
|
||||
yield update_all_addons(yield build_xml([
|
||||
{ id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
|
||||
{ id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" }
|
||||
]));
|
||||
Services.prefs.clearUserPref(PREF_APP_UPDATE_ENABLED);
|
||||
|
||||
yield verify_state(TEST_CONDITIONS.blank.initialState);
|
||||
|
||||
yield promiseShutdownManager();
|
||||
});
|
||||
|
||||
// Tests that a set that matches the hidden default set works
|
||||
add_task(function* test_match_default() {
|
||||
yield setup_conditions(TEST_CONDITIONS.withBothSets);
|
||||
|
||||
yield install_system_addons(yield build_xml([
|
||||
{ id: "system1@tests.mozilla.org", version: "1.0", path: "system1_1.xpi" },
|
||||
{ id: "system2@tests.mozilla.org", version: "1.0", path: "system2_1.xpi" }
|
||||
]));
|
||||
|
||||
// Bug 1204159: This should revert to the default set instead of installing
|
||||
// new versions into the updated set.
|
||||
//yield verify_state([false, "1.0", "1.0", null, null, null]);
|
||||
yield verify_state([true, "1.0", "1.0", null, null, null]);
|
||||
|
||||
yield promiseShutdownManager();
|
||||
});
|
||||
|
||||
// Tests that a set that matches the current set works
|
||||
add_task(function* test_match_current() {
|
||||
yield setup_conditions(TEST_CONDITIONS.withBothSets);
|
||||
|
||||
yield install_system_addons(yield build_xml([
|
||||
{ id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
|
||||
{ id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" }
|
||||
]));
|
||||
|
||||
// Bug 1204159: This should remain with the current set instead of creating
|
||||
// a new copy
|
||||
//let set = JSON.parse(Services.prefs.getCharPref(PREF_SYSTEM_ADDON_SET));
|
||||
//do_check_eq(set.directory, "prefilled");
|
||||
|
||||
yield verify_state([true, null, "2.0", "2.0", null, null]);
|
||||
|
||||
yield promiseShutdownManager();
|
||||
});
|
@ -23,9 +23,7 @@ skip-if = appname != "firefox"
|
||||
[test_provider_shutdown.js]
|
||||
[test_provider_unsafe_access_shutdown.js]
|
||||
[test_provider_unsafe_access_startup.js]
|
||||
[test_ProductAddonChecker.js]
|
||||
[test_shutdown.js]
|
||||
[test_system_update.js]
|
||||
[test_system_reset.js]
|
||||
[test_XPIcancel.js]
|
||||
[test_XPIStates.js]
|
||||
|
@ -30,6 +30,8 @@ const PREF_APP_UPDATE_CERT_CHECKATTRS = "app.update.cert.checkAttributes";
|
||||
const PREF_APP_UPDATE_CERT_ERRORS = "app.update.cert.errors";
|
||||
const PREF_APP_UPDATE_CERT_MAXERRORS = "app.update.cert.maxErrors";
|
||||
const PREF_APP_UPDATE_CERT_REQUIREBUILTIN = "app.update.cert.requireBuiltIn";
|
||||
const PREF_APP_UPDATE_CUSTOM = "app.update.custom";
|
||||
const PREF_APP_UPDATE_IMEI_HASH = "app.update.imei_hash";
|
||||
const PREF_APP_UPDATE_ENABLED = "app.update.enabled";
|
||||
const PREF_APP_UPDATE_IDLETIME = "app.update.idletime";
|
||||
const PREF_APP_UPDATE_INCOMPATIBLE_MODE = "app.update.incompatible.mode";
|
||||
@ -52,6 +54,11 @@ const PREF_APP_UPDATE_SERVICE_MAX_ERRORS = "app.update.service.maxErrors";
|
||||
const PREF_APP_UPDATE_SOCKET_ERRORS = "app.update.socket.maxErrors";
|
||||
const PREF_APP_UPDATE_RETRY_TIMEOUT = "app.update.socket.retryTimeout";
|
||||
|
||||
const PREF_APP_DISTRIBUTION = "distribution.id";
|
||||
const PREF_APP_DISTRIBUTION_VERSION = "distribution.version";
|
||||
|
||||
const PREF_APP_B2G_VERSION = "b2g.version";
|
||||
|
||||
const PREF_EM_HOTFIX_ID = "extensions.hotfix.id";
|
||||
|
||||
const URI_UPDATE_PROMPT_DIALOG = "chrome://mozapps/content/update/updates.xul";
|
||||
@ -80,6 +87,7 @@ const FILE_UPDATE_ACTIVE = "active-update.xml";
|
||||
const FILE_PERMS_TEST = "update.test";
|
||||
const FILE_LAST_LOG = "last-update.log";
|
||||
const FILE_BACKUP_LOG = "backup-update.log";
|
||||
const FILE_UPDATE_LOCALE = "update.locale";
|
||||
|
||||
const STATE_NONE = "null";
|
||||
const STATE_DOWNLOADING = "downloading";
|
||||
@ -202,8 +210,8 @@ XPCOMUtils.defineLazyGetter(this, "gExtStorage", function aus_gExtStorage() {
|
||||
return Services.env.get("EXTERNAL_STORAGE");
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gLogEnabled", function aus_gLogEnabled() {
|
||||
return getPref("getBoolPref", PREF_APP_UPDATE_LOG, false);
|
||||
@ -220,6 +228,199 @@ XPCOMUtils.defineLazyGetter(this, "gCertUtils", function aus_gCertUtils() {
|
||||
return temp;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gABI", function aus_gABI() {
|
||||
let abi = null;
|
||||
try {
|
||||
abi = Services.appinfo.XPCOMABI;
|
||||
}
|
||||
catch (e) {
|
||||
LOG("gABI - XPCOM ABI unknown: updates are not possible.");
|
||||
}
|
||||
|
||||
if (AppConstants.platform == "macosx") {
|
||||
// Mac universal build should report a different ABI than either macppc
|
||||
// or mactel.
|
||||
let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].
|
||||
getService(Ci.nsIMacUtils);
|
||||
|
||||
if (macutils.isUniversalBinary) {
|
||||
abi += "-u-" + macutils.architecturesInBinary;
|
||||
}
|
||||
} else if (AppConstants.platform == "win") {
|
||||
// Windows build should report the CPU architecture that it's running on.
|
||||
abi += "-" + gWinCPUArch;
|
||||
}
|
||||
return abi;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gOSVersion", function aus_gOSVersion() {
|
||||
let osVersion;
|
||||
try {
|
||||
osVersion = Services.sysinfo.getProperty("name") + " " +
|
||||
Services.sysinfo.getProperty("version");
|
||||
}
|
||||
catch (e) {
|
||||
LOG("gOSVersion - OS Version unknown: updates are not possible.");
|
||||
}
|
||||
|
||||
if (osVersion) {
|
||||
if (AppConstants.platform == "win") {
|
||||
const BYTE = ctypes.uint8_t;
|
||||
const WORD = ctypes.uint16_t;
|
||||
const DWORD = ctypes.uint32_t;
|
||||
const WCHAR = ctypes.char16_t;
|
||||
const BOOL = ctypes.int;
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
|
||||
const SZCSDVERSIONLENGTH = 128;
|
||||
const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
|
||||
[
|
||||
{dwOSVersionInfoSize: DWORD},
|
||||
{dwMajorVersion: DWORD},
|
||||
{dwMinorVersion: DWORD},
|
||||
{dwBuildNumber: DWORD},
|
||||
{dwPlatformId: DWORD},
|
||||
{szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
|
||||
{wServicePackMajor: WORD},
|
||||
{wServicePackMinor: WORD},
|
||||
{wSuiteMask: WORD},
|
||||
{wProductType: BYTE},
|
||||
{wReserved: BYTE}
|
||||
]);
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
|
||||
const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
|
||||
[
|
||||
{wProcessorArchitecture: WORD},
|
||||
{wReserved: WORD},
|
||||
{dwPageSize: DWORD},
|
||||
{lpMinimumApplicationAddress: ctypes.voidptr_t},
|
||||
{lpMaximumApplicationAddress: ctypes.voidptr_t},
|
||||
{dwActiveProcessorMask: DWORD.ptr},
|
||||
{dwNumberOfProcessors: DWORD},
|
||||
{dwProcessorType: DWORD},
|
||||
{dwAllocationGranularity: DWORD},
|
||||
{wProcessorLevel: WORD},
|
||||
{wProcessorRevision: WORD}
|
||||
]);
|
||||
|
||||
let kernel32 = false;
|
||||
try {
|
||||
kernel32 = ctypes.open("Kernel32");
|
||||
} catch (e) {
|
||||
LOG("gOSVersion - Unable to open kernel32! " + e);
|
||||
osVersion += ".unknown (unknown)";
|
||||
}
|
||||
|
||||
if (kernel32) {
|
||||
try {
|
||||
// Get Service pack info
|
||||
try {
|
||||
let GetVersionEx = kernel32.declare("GetVersionExW",
|
||||
ctypes.default_abi,
|
||||
BOOL,
|
||||
OSVERSIONINFOEXW.ptr);
|
||||
let winVer = OSVERSIONINFOEXW();
|
||||
winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
|
||||
|
||||
if(0 !== GetVersionEx(winVer.address())) {
|
||||
osVersion += "." + winVer.wServicePackMajor +
|
||||
"." + winVer.wServicePackMinor;
|
||||
} else {
|
||||
LOG("gOSVersion - Unknown failure in GetVersionEX (returned 0)");
|
||||
osVersion += ".unknown";
|
||||
}
|
||||
} catch (e) {
|
||||
LOG("gOSVersion - error getting service pack information. Exception: " + e);
|
||||
osVersion += ".unknown";
|
||||
}
|
||||
} finally {
|
||||
kernel32.close();
|
||||
}
|
||||
|
||||
// Add processor architecture
|
||||
osVersion += " (" + gWinCPUArch + ")";
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
osVersion += " (" + Services.sysinfo.getProperty("secondaryLibrary") + ")";
|
||||
}
|
||||
catch (e) {
|
||||
// Not all platforms have a secondary widget library, so an error is nothing to worry about.
|
||||
}
|
||||
osVersion = encodeURIComponent(osVersion);
|
||||
}
|
||||
return osVersion;
|
||||
});
|
||||
|
||||
/* Windows only getter that returns the processor architecture. */
|
||||
XPCOMUtils.defineLazyGetter(this, "gWinCPUArch", function aus_gWinCPUArch() {
|
||||
// Get processor architecture
|
||||
let arch = "unknown";
|
||||
|
||||
const WORD = ctypes.uint16_t;
|
||||
const DWORD = ctypes.uint32_t;
|
||||
|
||||
// This structure is described at:
|
||||
// http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
|
||||
const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
|
||||
[
|
||||
{wProcessorArchitecture: WORD},
|
||||
{wReserved: WORD},
|
||||
{dwPageSize: DWORD},
|
||||
{lpMinimumApplicationAddress: ctypes.voidptr_t},
|
||||
{lpMaximumApplicationAddress: ctypes.voidptr_t},
|
||||
{dwActiveProcessorMask: DWORD.ptr},
|
||||
{dwNumberOfProcessors: DWORD},
|
||||
{dwProcessorType: DWORD},
|
||||
{dwAllocationGranularity: DWORD},
|
||||
{wProcessorLevel: WORD},
|
||||
{wProcessorRevision: WORD}
|
||||
]);
|
||||
|
||||
let kernel32 = false;
|
||||
try {
|
||||
kernel32 = ctypes.open("Kernel32");
|
||||
} catch (e) {
|
||||
LOG("gWinCPUArch - Unable to open kernel32! Exception: " + e);
|
||||
}
|
||||
|
||||
if (kernel32) {
|
||||
try {
|
||||
let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo",
|
||||
ctypes.default_abi,
|
||||
ctypes.void_t,
|
||||
SYSTEM_INFO.ptr);
|
||||
let winSystemInfo = SYSTEM_INFO();
|
||||
// Default to unknown
|
||||
winSystemInfo.wProcessorArchitecture = 0xffff;
|
||||
|
||||
GetNativeSystemInfo(winSystemInfo.address());
|
||||
switch (winSystemInfo.wProcessorArchitecture) {
|
||||
case 9:
|
||||
arch = "x64";
|
||||
break;
|
||||
case 6:
|
||||
arch = "IA64";
|
||||
break;
|
||||
case 0:
|
||||
arch = "x86";
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
LOG("gWinCPUArch - error getting processor architecture. " +
|
||||
"Exception: " + e);
|
||||
} finally {
|
||||
kernel32.close();
|
||||
}
|
||||
}
|
||||
|
||||
return arch;
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests to make sure that we can write to a given directory.
|
||||
*
|
||||
@ -526,13 +727,13 @@ XPCOMUtils.defineLazyGetter(this, "gCanCheckForUpdates", function aus_gCanCheckF
|
||||
}
|
||||
|
||||
// If we don't know the binary platform we're updating, we can't update.
|
||||
if (!UpdateUtils.ABI) {
|
||||
if (!gABI) {
|
||||
LOG("gCanCheckForUpdates - unable to check for updates, unknown ABI");
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we don't know the OS version we're updating, we can't update.
|
||||
if (!UpdateUtils.OSVersion) {
|
||||
if (!gOSVersion) {
|
||||
LOG("gCanCheckForUpdates - unable to check for updates, unknown OS " +
|
||||
"version");
|
||||
return false;
|
||||
@ -1041,6 +1242,58 @@ function cleanupActiveUpdate() {
|
||||
cleanUpUpdatesDir();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the locale from the update.locale file for replacing %LOCALE% in the
|
||||
* update url. The update.locale file can be located in the application
|
||||
* directory or the GRE directory with preference given to it being located in
|
||||
* the application directory.
|
||||
*/
|
||||
function getLocale() {
|
||||
if (gLocale) {
|
||||
return gLocale;
|
||||
}
|
||||
|
||||
let channel;
|
||||
for (let res of ['app', 'gre']) {
|
||||
channel = Services.io.newChannel2("resource://" + res + "/" + FILE_UPDATE_LOCALE,
|
||||
null,
|
||||
null,
|
||||
null, // aLoadingNode
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_NORMAL,
|
||||
Ci.nsIContentPolicy.TYPE_INTERNAL_XMLHTTPREQUEST);
|
||||
try {
|
||||
var inputStream = channel.open();
|
||||
gLocale = readStringFromInputStream(inputStream);
|
||||
} catch(e) {}
|
||||
if (gLocale)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!gLocale)
|
||||
throw Components.Exception(FILE_UPDATE_LOCALE + " file doesn't exist in " +
|
||||
"either the application or GRE directories",
|
||||
Cr.NS_ERROR_FILE_NOT_FOUND);
|
||||
|
||||
LOG("getLocale - getting locale from file: " + channel.originalURI.spec +
|
||||
", locale: " + gLocale);
|
||||
return gLocale;
|
||||
}
|
||||
|
||||
/* Get the distribution pref values, from defaults only */
|
||||
function getDistributionPrefValue(aPrefName) {
|
||||
var prefValue = "default";
|
||||
|
||||
try {
|
||||
prefValue = Services.prefs.getDefaultBranch(null).getCharPref(aPrefName);
|
||||
} catch (e) {
|
||||
// use default when pref not found
|
||||
}
|
||||
|
||||
return prefValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enumeration of items in a JS array.
|
||||
* @constructor
|
||||
@ -2274,9 +2527,9 @@ UpdateService.prototype = {
|
||||
}
|
||||
// The following checks are done here so they can be differentiated from
|
||||
// foreground checks.
|
||||
if (!UpdateUtils.OSVersion) {
|
||||
if (!gOSVersion) {
|
||||
AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_NO_OS_VERSION);
|
||||
} else if (!UpdateUtils.ABI) {
|
||||
} else if (!gABI) {
|
||||
AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_NO_OS_ABI);
|
||||
} else if (!validUpdateURL) {
|
||||
if (overridePrefHasValue) {
|
||||
@ -3018,14 +3271,14 @@ UpdateManager.prototype = {
|
||||
*/
|
||||
get activeUpdate() {
|
||||
if (this._activeUpdate &&
|
||||
this._activeUpdate.channel != UpdateUtils.UpdateChannel) {
|
||||
this._activeUpdate.channel != UpdateChannel.get()) {
|
||||
LOG("UpdateManager:get activeUpdate - channel has changed, " +
|
||||
"reloading default preferences to workaround bug 802022");
|
||||
// Workaround to get distribution preferences loaded (Bug 774618). This
|
||||
// can be removed after bug 802022 is fixed.
|
||||
let prefSvc = Services.prefs.QueryInterface(Ci.nsIObserver);
|
||||
prefSvc.observe(null, "reload-default-prefs", null);
|
||||
if (this._activeUpdate.channel != UpdateUtils.UpdateChannel) {
|
||||
if (this._activeUpdate.channel != UpdateChannel.get()) {
|
||||
// User switched channels, clear out any old active updates and remove
|
||||
// partial downloads
|
||||
this._activeUpdate = null;
|
||||
@ -3263,7 +3516,40 @@ Checker.prototype = {
|
||||
return null;
|
||||
}
|
||||
|
||||
url = UpdateUtils.formatUpdateURL(url);
|
||||
url = url.replace(/%PRODUCT%/g, Services.appinfo.name);
|
||||
url = url.replace(/%VERSION%/g, Services.appinfo.version);
|
||||
url = url.replace(/%BUILD_ID%/g, Services.appinfo.appBuildID);
|
||||
url = url.replace(/%BUILD_TARGET%/g, Services.appinfo.OS + "_" + gABI);
|
||||
url = url.replace(/%OS_VERSION%/g, gOSVersion);
|
||||
if (/%LOCALE%/.test(url)) {
|
||||
url = url.replace(/%LOCALE%/g, getLocale());
|
||||
}
|
||||
url = url.replace(/%CHANNEL%/g, UpdateChannel.get());
|
||||
url = url.replace(/%PLATFORM_VERSION%/g, Services.appinfo.platformVersion);
|
||||
url = url.replace(/%DISTRIBUTION%/g,
|
||||
getDistributionPrefValue(PREF_APP_DISTRIBUTION));
|
||||
url = url.replace(/%DISTRIBUTION_VERSION%/g,
|
||||
getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION));
|
||||
url = url.replace(/%CUSTOM%/g, getPref("getCharPref", PREF_APP_UPDATE_CUSTOM, ""));
|
||||
url = url.replace(/\+/g, "%2B");
|
||||
|
||||
if (AppConstants.platform == "gonk") {
|
||||
let sysLibs = {};
|
||||
Cu.import("resource://gre/modules/systemlibs.js", sysLibs);
|
||||
let productDevice = sysLibs.libcutils.property_get("ro.product.device");
|
||||
let buildType = sysLibs.libcutils.property_get("ro.build.type");
|
||||
url = url.replace(/%PRODUCT_MODEL%/g,
|
||||
sysLibs.libcutils.property_get("ro.product.model"));
|
||||
if (buildType == "user" || buildType == "userdebug") {
|
||||
url = url.replace(/%PRODUCT_DEVICE%/g, productDevice);
|
||||
} else {
|
||||
url = url.replace(/%PRODUCT_DEVICE%/g, productDevice + "-" + buildType);
|
||||
}
|
||||
url = url.replace(/%B2G_VERSION%/g,
|
||||
getPref("getCharPref", PREF_APP_B2G_VERSION, null));
|
||||
url = url.replace(/%IMEI%/g,
|
||||
getPref("getCharPref", PREF_APP_UPDATE_IMEI_HASH, "default"));
|
||||
}
|
||||
|
||||
if (force) {
|
||||
url += (url.indexOf("?") != -1 ? "&" : "?") + "force=1";
|
||||
@ -3355,7 +3641,7 @@ Checker.prototype = {
|
||||
continue;
|
||||
}
|
||||
update.serviceURL = this.getUpdateURL(this._forced);
|
||||
update.channel = UpdateUtils.UpdateChannel;
|
||||
update.channel = UpdateChannel.get();
|
||||
updates.push(update);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user