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:
Wes Kocher 2015-09-22 11:02:55 -07:00
parent 2e0c208b14
commit 468b697f04
74 changed files with 1201 additions and 2099 deletions

View File

@ -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;

View File

@ -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");

View File

@ -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,

View File

@ -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),

View File

@ -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);

View File

@ -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 ||

View File

@ -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"));
}

View File

@ -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 () {

View File

@ -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"});

View File

@ -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("?");

View File

@ -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, ""); },
};

View File

@ -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);

View File

@ -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

View File

@ -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));

View File

@ -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));

View File

@ -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

View File

@ -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,

View File

@ -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");

View File

@ -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);
}

View File

@ -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 = {

View File

@ -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;

View File

@ -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",

View File

@ -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@",

View File

@ -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();
}

View File

@ -4,3 +4,4 @@ tail =
skip-if = toolkit == 'gonk'
[test_contentAreaUtils.js]
[test_updateChannelModule.js]

View File

@ -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;
}

View File

@ -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;
}
};
/**

View File

@ -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");

View 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;
}
};

View File

@ -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;
});

View File

@ -75,7 +75,7 @@ EXTRA_JS_MODULES += [
'TelemetryTimestamps.jsm',
'Timer.jsm',
'Troubleshoot.jsm',
'UpdateUtils.jsm',
'UpdateChannel.jsm',
'WebChannel.jsm',
'WindowDraggingUtils.jsm',
'ZipUtils.jsm',

View File

@ -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

View 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);
});

View File

@ -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]

View File

@ -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",

View File

@ -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 {

View File

@ -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;
}
})
}

View File

@ -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

View File

@ -12,7 +12,6 @@ EXTRA_JS_MODULES.addons += [
'Content.js',
'GMPProvider.jsm',
'LightweightThemeImageOptimizer.jsm',
'ProductAddonChecker.jsm',
'SpellCheckDictionaryBootstrap.js',
'WebExtensionBootstrap.js',
]

View File

@ -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));

View File

@ -1 +0,0 @@
Not an xml file!

View File

@ -1,3 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<foobar></barfoo>

View File

@ -1,3 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<test></test>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<updates>
<addons></addons>
</updates>

View File

@ -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>

View File

@ -1,3 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<updates></updates>

View File

@ -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) {
}

View File

@ -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>

View File

@ -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) {
}

View File

@ -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>

View File

@ -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) {
}

View File

@ -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>

View File

@ -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) {
}

View File

@ -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>

View File

@ -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);
});

View File

@ -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, []);

View File

@ -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 = {

View File

@ -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();
});

View File

@ -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]

View File

@ -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);
}