fix useragent patch

master
Dominika Liberda 2021-10-21 14:51:55 +02:00
parent 6f238fdad6
commit 57d0ee09e7
1 changed files with 604 additions and 41 deletions

View File

@ -1,6 +1,6 @@
diff -ur firefox-93.0/netwerk/base/RequestContextService.cpp firefox-93.0_patched/netwerk/base/RequestContextService.cpp
--- firefox-93.0/netwerk/base/RequestContextService.cpp 2021-09-28 01:17:27.000000000 +0200
+++ firefox-93.0_patched/netwerk/base/RequestContextService.cpp 2021-10-20 21:16:24.413520944 +0200
diff -ur a/netwerk/base/RequestContextService.cpp b/netwerk/base/RequestContextService.cpp
--- a/netwerk/base/RequestContextService.cpp 2021-09-28 01:17:27.000000000 +0200
+++ b/netwerk/base/RequestContextService.cpp 2021-10-20 21:16:24.413520944 +0200
@@ -60,6 +60,7 @@
uint64_t mID;
Atomic<uint32_t> mBlockingTransactionCount;
@ -25,9 +25,9 @@ diff -ur firefox-93.0/netwerk/base/RequestContextService.cpp firefox-93.0_patche
NS_IMETHODIMP
RequestContext::AddNonTailRequest() {
MOZ_ASSERT(NS_IsMainThread());
diff -ur firefox-93.0/netwerk/base/nsILoadGroup.idl firefox-93.0_patched/netwerk/base/nsILoadGroup.idl
--- firefox-93.0/netwerk/base/nsILoadGroup.idl 2021-09-28 00:46:56.000000000 +0200
+++ firefox-93.0_patched/netwerk/base/nsILoadGroup.idl 2021-10-20 21:17:31.740524540 +0200
diff -ur a/netwerk/base/nsILoadGroup.idl b/netwerk/base/nsILoadGroup.idl
--- a/netwerk/base/nsILoadGroup.idl 2021-09-28 00:46:56.000000000 +0200
+++ b/netwerk/base/nsILoadGroup.idl 2021-10-20 21:17:31.740524540 +0200
@@ -96,6 +96,8 @@
*/
attribute nsLoadFlags defaultLoadFlags;
@ -37,9 +37,9 @@ diff -ur firefox-93.0/netwerk/base/nsILoadGroup.idl firefox-93.0_patched/netwerk
/**
* Returns true if the loadGroup belongs to a discarded context, such as, a
* terminated private browsing session.
diff -ur firefox-93.0/netwerk/base/nsIRequestContext.idl firefox-93.0_patched/netwerk/base/nsIRequestContext.idl
--- firefox-93.0/netwerk/base/nsIRequestContext.idl 2021-09-28 00:47:20.000000000 +0200
+++ firefox-93.0_patched/netwerk/base/nsIRequestContext.idl 2021-10-20 21:18:11.556526666 +0200
diff -ur a/netwerk/base/nsIRequestContext.idl b/netwerk/base/nsIRequestContext.idl
--- a/netwerk/base/nsIRequestContext.idl 2021-09-28 00:47:20.000000000 +0200
+++ b/netwerk/base/nsIRequestContext.idl 2021-10-20 21:18:11.556526666 +0200
@@ -93,6 +93,8 @@
*/
[notxpcom,nostdcall] attribute SpdyPushCachePtr spdyPushCache;
@ -49,9 +49,9 @@ diff -ur firefox-93.0/netwerk/base/nsIRequestContext.idl firefox-93.0_patched/ne
/**
* Increases/decrease the number of non-tailed requests in this context.
* If the count drops to zero, all tail-blocked callbacks are notified
diff -ur firefox-93.0/netwerk/base/nsLoadGroup.cpp firefox-93.0_patched/netwerk/base/nsLoadGroup.cpp
--- firefox-93.0/netwerk/base/nsLoadGroup.cpp 2021-09-28 00:47:46.000000000 +0200
+++ firefox-93.0_patched/netwerk/base/nsLoadGroup.cpp 2021-10-20 21:20:24.781533781 +0200
diff -ur a/netwerk/base/nsLoadGroup.cpp b/netwerk/base/nsLoadGroup.cpp
--- a/netwerk/base/nsLoadGroup.cpp 2021-09-28 00:47:46.000000000 +0200
+++ b/netwerk/base/nsLoadGroup.cpp 2021-10-20 21:20:24.781533781 +0200
@@ -767,6 +767,19 @@
return NS_OK;
}
@ -72,9 +72,9 @@ diff -ur firefox-93.0/netwerk/base/nsLoadGroup.cpp firefox-93.0_patched/netwerk/
////////////////////////////////////////////////////////////////////////////////
void nsLoadGroup::TelemetryReport() {
diff -ur firefox-93.0/netwerk/base/nsLoadGroup.h firefox-93.0_patched/netwerk/base/nsLoadGroup.h
--- firefox-93.0/netwerk/base/nsLoadGroup.h 2021-09-28 00:47:20.000000000 +0200
+++ firefox-93.0_patched/netwerk/base/nsLoadGroup.h 2021-10-20 21:19:17.732530200 +0200
diff -ur a/netwerk/base/nsLoadGroup.h b/netwerk/base/nsLoadGroup.h
--- a/netwerk/base/nsLoadGroup.h 2021-09-28 00:47:20.000000000 +0200
+++ b/netwerk/base/nsLoadGroup.h 2021-10-20 21:19:17.732530200 +0200
@@ -103,6 +103,8 @@
mozilla::TimeStamp mDefaultRequestCreationTime;
uint32_t mTimedRequests{0};
@ -84,12 +84,10 @@ diff -ur firefox-93.0/netwerk/base/nsLoadGroup.h firefox-93.0_patched/netwerk/ba
};
} // namespace net
Only in firefox-93.0_patched/netwerk/protocol/http: UAOverridesBootstrapper.jsm
Only in firefox-93.0_patched/netwerk/protocol/http: UserAgentOverrides.jsm
Only in firefox-93.0_patched/netwerk/protocol/http: UserAgentUpdates.jsm
diff -ur firefox-93.0/netwerk/protocol/http/components.conf firefox-93.0_patched/netwerk/protocol/http/components.conf
--- firefox-93.0/netwerk/protocol/http/components.conf 2021-09-28 00:47:17.000000000 +0200
+++ firefox-93.0_patched/netwerk/protocol/http/components.conf 2021-10-20 21:46:26.687617196 +0200
diff -ur a/netwerk/protocol/http/components.conf b/netwerk/protocol/http/components.conf
--- a/netwerk/protocol/http/components.conf 2021-09-28 00:47:17.000000000 +0200
+++ b/netwerk/protocol/http/components.conf 2021-10-20 21:46:26.687617196 +0200
@@ -6,6 +6,13 @@
Classes = [
@ -104,9 +102,9 @@ diff -ur firefox-93.0/netwerk/protocol/http/components.conf firefox-93.0_patched
'cid': '{b4f96c89-5238-450c-8bda-e12c26f1d150}',
'contract_ids': ['@mozilla.org/network/well-known-opportunistic-utils;1'],
'jsm': 'resource://gre/modules/WellKnownOpportunisticUtils.jsm',
diff -ur firefox-93.0/netwerk/protocol/http/moz.build firefox-93.0_patched/netwerk/protocol/http/moz.build
--- firefox-93.0/netwerk/protocol/http/moz.build 2021-09-28 01:17:27.000000000 +0200
+++ firefox-93.0_patched/netwerk/protocol/http/moz.build 2021-10-20 21:48:41.781624411 +0200
diff -ur a/netwerk/protocol/http/moz.build b/netwerk/protocol/http/moz.build
--- a/netwerk/protocol/http/moz.build 2021-09-28 01:17:27.000000000 +0200
+++ b/netwerk/protocol/http/moz.build 2021-10-20 21:48:41.781624411 +0200
@@ -174,6 +174,7 @@
"PHttpTransaction.ipdl",
]
@ -119,16 +117,16 @@ diff -ur firefox-93.0/netwerk/protocol/http/moz.build firefox-93.0_patched/netwe
]
EXTRA_JS_MODULES += [
+ "UAOverridesBootstrapper.jsm",
"WellKnownOpportunisticUtils.jsm",
+ "UAOverridesBootstrapper.jsm",
+ "UserAgentOverrides.jsm",
+ "UserAgentUpdates.jsm",
"WellKnownOpportunisticUtils.jsm",
]
XPCOM_MANIFESTS += [
diff -ur firefox-93.0/netwerk/protocol/http/nsHttpChannel.cpp firefox-93.0_patched/netwerk/protocol/http/nsHttpChannel.cpp
--- firefox-93.0/netwerk/protocol/http/nsHttpChannel.cpp 2021-09-28 01:17:27.000000000 +0200
+++ firefox-93.0_patched/netwerk/protocol/http/nsHttpChannel.cpp 2021-10-20 21:29:52.377564094 +0200
diff -ur a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp
--- a/netwerk/protocol/http/nsHttpChannel.cpp 2021-09-28 01:17:27.000000000 +0200
+++ b/netwerk/protocol/http/nsHttpChannel.cpp 2021-10-20 21:29:52.377564094 +0200
@@ -436,6 +436,8 @@
nsresult nsHttpChannel::OnBeforeConnect() {
nsresult rv;
@ -196,9 +194,9 @@ diff -ur firefox-93.0/netwerk/protocol/http/nsHttpChannel.cpp firefox-93.0_patch
// Step 10 of HTTP-network-or-cache fetch
void nsHttpChannel::SetOriginHeader() {
if (mRequestHead.IsGet() || mRequestHead.IsHead()) {
diff -ur firefox-93.0/netwerk/protocol/http/nsHttpChannel.h firefox-93.0_patched/netwerk/protocol/http/nsHttpChannel.h
--- firefox-93.0/netwerk/protocol/http/nsHttpChannel.h 2021-09-28 01:17:27.000000000 +0200
+++ firefox-93.0_patched/netwerk/protocol/http/nsHttpChannel.h 2021-10-20 21:25:28.422549997 +0200
diff -ur a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h
--- a/netwerk/protocol/http/nsHttpChannel.h 2021-09-28 01:17:27.000000000 +0200
+++ b/netwerk/protocol/http/nsHttpChannel.h 2021-10-20 21:25:28.422549997 +0200
@@ -502,6 +502,8 @@
void SetPushedStreamTransactionAndId(
HttpTransactionShell* aTransWithPushedStream, uint32_t aPushedStreamId);
@ -208,9 +206,9 @@ diff -ur firefox-93.0/netwerk/protocol/http/nsHttpChannel.h firefox-93.0_patched
void SetOriginHeader();
void SetDoNotTrack();
diff -ur firefox-93.0/netwerk/protocol/http/nsHttpHandler.cpp firefox-93.0_patched/netwerk/protocol/http/nsHttpHandler.cpp
--- firefox-93.0/netwerk/protocol/http/nsHttpHandler.cpp 2021-09-28 01:17:27.000000000 +0200
+++ firefox-93.0_patched/netwerk/protocol/http/nsHttpHandler.cpp 2021-10-20 21:35:59.579583705 +0200
diff -ur a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp
--- a/netwerk/protocol/http/nsHttpHandler.cpp 2021-09-28 01:17:27.000000000 +0200
+++ b/netwerk/protocol/http/nsHttpHandler.cpp 2021-10-20 21:35:59.579583705 +0200
@@ -291,6 +291,24 @@
}
}
@ -248,9 +246,9 @@ diff -ur firefox-93.0/netwerk/protocol/http/nsHttpHandler.cpp firefox-93.0_patch
uint64_t channelId;
nsresult rv = NewChannelId(channelId);
NS_ENSURE_SUCCESS(rv, rv);
diff -ur firefox-93.0/netwerk/protocol/http/nsHttpHandler.h firefox-93.0_patched/netwerk/protocol/http/nsHttpHandler.h
--- firefox-93.0/netwerk/protocol/http/nsHttpHandler.h 2021-09-28 01:17:27.000000000 +0200
+++ firefox-93.0_patched/netwerk/protocol/http/nsHttpHandler.h 2021-10-20 21:31:35.602569607 +0200
diff -ur a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h
--- a/netwerk/protocol/http/nsHttpHandler.h 2021-09-28 01:17:27.000000000 +0200
+++ b/netwerk/protocol/http/nsHttpHandler.h 2021-10-20 21:31:35.602569607 +0200
@@ -383,6 +383,11 @@
NotifyObservers(chan, NS_HTTP_ON_STOP_REQUEST_TOPIC);
}
@ -272,9 +270,9 @@ diff -ur firefox-93.0/netwerk/protocol/http/nsHttpHandler.h firefox-93.0_patched
// Checks if there are any user certs or active smart cards on a different
// thread. Updates mSpeculativeConnectEnabled when done.
void MaybeEnableSpeculativeConnect();
diff -ur firefox-93.0/netwerk/protocol/http/nsIHttpProtocolHandler.idl firefox-93.0_patched/netwerk/protocol/http/nsIHttpProtocolHandler.idl
--- firefox-93.0/netwerk/protocol/http/nsIHttpProtocolHandler.idl 2021-09-28 00:47:46.000000000 +0200
+++ firefox-93.0_patched/netwerk/protocol/http/nsIHttpProtocolHandler.idl 2021-10-20 21:38:21.926591307 +0200
diff -ur a/netwerk/protocol/http/nsIHttpProtocolHandler.idl b/netwerk/protocol/http/nsIHttpProtocolHandler.idl
--- a/netwerk/protocol/http/nsIHttpProtocolHandler.idl 2021-09-28 00:47:46.000000000 +0200
+++ b/netwerk/protocol/http/nsIHttpProtocolHandler.idl 2021-10-20 21:38:21.926591307 +0200
@@ -190,6 +190,8 @@
*/
#define NS_HTTP_ON_EXAMINE_CACHED_RESPONSE_TOPIC "http-on-examine-cached-response"
@ -284,3 +282,568 @@ diff -ur firefox-93.0/netwerk/protocol/http/nsIHttpProtocolHandler.idl firefox-9
/**
* This topic is notified for every http channel right after it called
* OnStopRequest on its listener, regardless whether it was finished
--- a/netwerk/protocol/http/UAOverridesBootstrapper.jsm 2021-10-21 14:47:27.380530041 +0200
+++ b/netwerk/protocol/http/UAOverridesBootstrapper.jsm 2021-10-20 21:55:18.750645611 +0200
@@ -0,0 +1,33 @@
+/* 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 { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { UserAgentOverrides } = ChromeUtils.import(
+ "resource://gre/modules/UserAgentOverrides.jsm"
+);
+
+function UAOverridesBootstrapper() {
+ this.init();
+}
+
+UAOverridesBootstrapper.prototype = {
+ init: function uaob_init() {
+ Services.obs.addObserver(this, "profile-change-net-teardown");
+ UserAgentOverrides.init();
+ },
+
+ observe: function uaob_observe(aSubject, aTopic, aData) {
+ if (aTopic == "profile-change-net-teardown") {
+ Services.obs.removeObserver(this, "profile-change-net-teardown");
+ UserAgentOverrides.uninit();
+ }
+ },
+
+ QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
+ classID: Components.ID("{965b0ca8-155b-11e7-93ae-92361f002671}"),
+};
+
+var EXPORTED_SYMBOLS = ["UAOverridesBootstrapper"];
--- a/netwerk/protocol/http/UserAgentOverrides.jsm 2021-10-21 14:47:32.138529785 +0200
+++ b/netwerk/protocol/http/UserAgentOverrides.jsm 2021-10-20 21:55:21.938645781 +0200
@@ -0,0 +1,188 @@
+/* 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";
+
+var EXPORTED_SYMBOLS = ["UserAgentOverrides"];
+
+const { XPCOMUtils } = ChromeUtils.import(
+ "resource://gre/modules/XPCOMUtils.jsm"
+);
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { UserAgentUpdates } = ChromeUtils.import(
+ "resource://gre/modules/UserAgentUpdates.jsm"
+);
+
+const PREF_OVERRIDES_ENABLED = "general.useragent.site_specific_overrides";
+const MAX_OVERRIDE_FOR_HOST_CACHE_SIZE = 250;
+
+// lazy load nsHttpHandler to improve startup performance.
+XPCOMUtils.defineLazyGetter(this, "DEFAULT_UA", function() {
+ return Cc["@mozilla.org/network/protocol;1?name=http"].getService(
+ Ci.nsIHttpProtocolHandler
+ ).userAgent;
+});
+
+var gPrefBranch;
+var gOverrides = new Map();
+var gUpdatedOverrides;
+var gOverrideForHostCache = new Map();
+var gInitialized = false;
+var gOverrideFunctions = [
+ function(aHttpChannel) {
+ return UserAgentOverrides.getOverrideForURI(aHttpChannel.URI);
+ },
+];
+var gBuiltUAs = new Map();
+
+var UserAgentOverrides = {
+ init: function uao_init() {
+ if (gInitialized) {
+ return;
+ }
+
+ gPrefBranch = Services.prefs.getBranch("general.useragent.override.");
+ gPrefBranch.addObserver("", buildOverrides);
+
+ Services.prefs.addObserver(PREF_OVERRIDES_ENABLED, buildOverrides);
+
+ try {
+ Services.obs.addObserver(
+ HTTP_on_useragent_request,
+ "http-on-useragent-request"
+ );
+ } catch (x) {
+ // The http-on-useragent-request notification is disallowed in content processes.
+ }
+
+ try {
+ UserAgentUpdates.init(function(overrides) {
+ gOverrideForHostCache.clear();
+ if (overrides) {
+ for (let domain in overrides) {
+ overrides[domain] = getUserAgentFromOverride(overrides[domain]);
+ }
+ overrides.get = function(key) {
+ return this[key];
+ };
+ }
+ gUpdatedOverrides = overrides;
+ });
+
+ buildOverrides();
+ } catch (e) {
+ // UserAgentOverrides is initialized before profile is ready.
+ // UA override might not work correctly.
+ }
+
+ Services.obs.notifyObservers(null, "useragentoverrides-initialized");
+ gInitialized = true;
+ },
+
+ addComplexOverride: function uao_addComplexOverride(callback) {
+ // Add to front of array so complex overrides have precedence
+ gOverrideFunctions.unshift(callback);
+ },
+
+ getOverrideForURI: function uao_getOverrideForURI(aURI) {
+ let host = aURI.asciiHost;
+ if (!gInitialized || (!gOverrides.size && !gUpdatedOverrides) || !host) {
+ return null;
+ }
+
+ let override = gOverrideForHostCache.get(host);
+ if (override !== undefined) {
+ return override;
+ }
+
+ function findOverride(overrides) {
+ let searchHost = host;
+ let userAgent = overrides.get(searchHost);
+
+ while (!userAgent) {
+ let dot = searchHost.indexOf(".");
+ if (dot === -1) {
+ return null;
+ }
+ searchHost = searchHost.slice(dot + 1);
+ userAgent = overrides.get(searchHost);
+ }
+ return userAgent;
+ }
+
+ override =
+ (gOverrides.size && findOverride(gOverrides)) ||
+ (gUpdatedOverrides && findOverride(gUpdatedOverrides));
+
+ if (gOverrideForHostCache.size >= MAX_OVERRIDE_FOR_HOST_CACHE_SIZE) {
+ gOverrideForHostCache.clear();
+ }
+ gOverrideForHostCache.set(host, override);
+
+ return override;
+ },
+
+ uninit: function uao_uninit() {
+ if (!gInitialized) {
+ return;
+ }
+ gInitialized = false;
+
+ gPrefBranch.removeObserver("", buildOverrides);
+
+ Services.prefs.removeObserver(PREF_OVERRIDES_ENABLED, buildOverrides);
+
+ Services.obs.removeObserver(
+ HTTP_on_useragent_request,
+ "http-on-useragent-request"
+ );
+ },
+};
+
+function getUserAgentFromOverride(override) {
+ let userAgent = gBuiltUAs.get(override);
+ if (userAgent !== undefined) {
+ return userAgent;
+ }
+ let [search, replace] = override.split("#", 2);
+ if (search && replace) {
+ userAgent = DEFAULT_UA.replace(new RegExp(search, "g"), replace);
+ } else {
+ userAgent = override;
+ }
+ gBuiltUAs.set(override, userAgent);
+ return userAgent;
+}
+
+function buildOverrides() {
+ gOverrides.clear();
+ gOverrideForHostCache.clear();
+
+ if (!Services.prefs.getBoolPref(PREF_OVERRIDES_ENABLED)) {
+ return;
+ }
+
+ let domains = gPrefBranch.getChildList("");
+
+ for (let domain of domains) {
+ let override = gPrefBranch.getCharPref(domain);
+ let userAgent = getUserAgentFromOverride(override);
+
+ if (userAgent != DEFAULT_UA) {
+ gOverrides.set(domain, userAgent);
+ }
+ }
+}
+
+function HTTP_on_useragent_request(aSubject, aTopic, aData) {
+ let channel = aSubject.QueryInterface(Ci.nsIHttpChannel);
+
+ for (let callback of gOverrideFunctions) {
+ let modifiedUA = callback(channel, DEFAULT_UA);
+ if (modifiedUA) {
+ channel.setRequestHeader("User-Agent", modifiedUA, false);
+ return;
+ }
+ }
+}
--- a/netwerk/protocol/http/UserAgentUpdates.jsm 2021-10-21 14:47:36.532529549 +0200
+++ b/netwerk/protocol/http/UserAgentUpdates.jsm 2021-10-20 21:55:25.283645960 +0200
@@ -0,0 +1,333 @@
+/* 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";
+
+var EXPORTED_SYMBOLS = ["UserAgentUpdates"];
+
+const { AppConstants } = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { XPCOMUtils } = ChromeUtils.import(
+ "resource://gre/modules/XPCOMUtils.jsm"
+);
+
+XPCOMUtils.defineLazyGlobalGetters(this, ["XMLHttpRequest"]);
+
+ChromeUtils.defineModuleGetter(
+ this,
+ "FileUtils",
+ "resource://gre/modules/FileUtils.jsm"
+);
+
+ChromeUtils.defineModuleGetter(
+ this,
+ "NetUtil",
+ "resource://gre/modules/NetUtil.jsm"
+);
+
+ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
+
+ChromeUtils.defineModuleGetter(
+ this,
+ "UpdateUtils",
+ "resource://gre/modules/UpdateUtils.jsm"
+);
+
+XPCOMUtils.defineLazyServiceGetter(
+ this,
+ "gUpdateTimer",
+ "@mozilla.org/updates/timer-manager;1",
+ "nsIUpdateTimerManager"
+);
+
+XPCOMUtils.defineLazyGetter(this, "gDecoder", function() {
+ return new TextDecoder();
+});
+
+XPCOMUtils.defineLazyGetter(this, "gEncoder", function() {
+ return new TextEncoder();
+});
+
+const TIMER_ID = "user-agent-updates-timer";
+
+const PREF_UPDATES = "general.useragent.updates.";
+const PREF_UPDATES_ENABLED = PREF_UPDATES + "enabled";
+const PREF_UPDATES_URL = PREF_UPDATES + "url";
+const PREF_UPDATES_INTERVAL = PREF_UPDATES + "interval";
+const PREF_UPDATES_RETRY = PREF_UPDATES + "retry";
+const PREF_UPDATES_TIMEOUT = PREF_UPDATES + "timeout";
+const PREF_UPDATES_LASTUPDATED = PREF_UPDATES + "lastupdated";
+
+const KEY_PREFDIR = "PrefD";
+const KEY_APPDIR = "XCurProcD";
+const FILE_UPDATES = "ua-update.json";
+
+const PREF_APP_DISTRIBUTION = "distribution.id";
+const PREF_APP_DISTRIBUTION_VERSION = "distribution.version";
+
+var gInitialized = false;
+
+function readChannel(url) {
+ return new Promise((resolve, reject) => {
+ try {
+ let channel = NetUtil.newChannel({
+ uri: url,
+ loadUsingSystemPrincipal: true,
+ });
+ channel.contentType = "application/json";
+
+ NetUtil.asyncFetch(channel, (inputStream, status) => {
+ if (!Components.isSuccessCode(status)) {
+ reject();
+ return;
+ }
+
+ let data = JSON.parse(
+ NetUtil.readInputStreamToString(inputStream, inputStream.available())
+ );
+ resolve(data);
+ });
+ } catch (ex) {
+ reject(
+ new Error(
+ "UserAgentUpdates: Could not fetch " +
+ url +
+ " " +
+ ex +
+ "\n" +
+ ex.stack
+ )
+ );
+ }
+ });
+}
+
+var UserAgentUpdates = {
+ init(callback) {
+ if (gInitialized) {
+ return;
+ }
+ gInitialized = true;
+
+ this._callback = callback;
+ this._lastUpdated = 0;
+ this._applySavedUpdate();
+
+ Services.prefs.addObserver(PREF_UPDATES, this);
+ },
+
+ uninit() {
+ if (!gInitialized) {
+ return;
+ }
+ gInitialized = false;
+ Services.prefs.removeObserver(PREF_UPDATES, this);
+ },
+
+ _applyUpdate(update) {
+ // Check pref again in case it has changed
+ if (update && this._getPref(PREF_UPDATES_ENABLED, false)) {
+ this._callback(update);
+ } else {
+ this._callback(null);
+ }
+ },
+
+ _applySavedUpdate() {
+ if (!this._getPref(PREF_UPDATES_ENABLED, false)) {
+ // remove previous overrides
+ this._applyUpdate(null);
+ return;
+ }
+ // try loading from profile dir, then from app dir
+ let dirs = [KEY_PREFDIR, KEY_APPDIR];
+
+ dirs
+ .reduce((prevLoad, dir) => {
+ let file = FileUtils.getFile(dir, [FILE_UPDATES], true).path;
+ // tryNext returns promise to read file under dir and parse it
+ let tryNext = () =>
+ OS.File.read(file).then(bytes => {
+ let update = JSON.parse(gDecoder.decode(bytes));
+ if (!update) {
+ throw new Error("invalid update");
+ }
+ return update;
+ });
+ // try to load next one if the previous load failed
+ return prevLoad ? prevLoad.catch(tryNext) : tryNext();
+ }, null)
+ .catch(ex => {
+ if (AppConstants.platform !== "android") {
+ // All previous (non-Android) load attempts have failed, so we bail.
+ throw new Error(
+ "UserAgentUpdates: Failed to load " +
+ FILE_UPDATES +
+ ex +
+ "\n" +
+ ex.stack
+ );
+ }
+ // Make one last attempt to read from the Fennec APK root.
+ return readChannel("resource://android/" + FILE_UPDATES);
+ })
+ .then(update => {
+ // Apply update if loading was successful
+ this._applyUpdate(update);
+ })
+ .catch(Cu.reportError);
+ this._scheduleUpdate();
+ },
+
+ _saveToFile(update) {
+ let file = FileUtils.getFile(KEY_PREFDIR, [FILE_UPDATES], true);
+ let path = file.path;
+ let bytes = gEncoder.encode(JSON.stringify(update));
+ OS.File.writeAtomic(path, bytes, { tmpPath: path + ".tmp" }).then(() => {
+ this._lastUpdated = Date.now();
+ Services.prefs.setCharPref(
+ PREF_UPDATES_LASTUPDATED,
+ this._lastUpdated.toString()
+ );
+ }, Cu.reportError);
+ },
+
+ _getPref(name, def) {
+ try {
+ switch (typeof def) {
+ case "number":
+ return Services.prefs.getIntPref(name);
+ case "boolean":
+ return Services.prefs.getBoolPref(name);
+ }
+ return Services.prefs.getCharPref(name);
+ } catch (e) {
+ return def;
+ }
+ },
+
+ _getParameters() {
+ return {
+ "%DATE%": function() {
+ return Date.now().toString();
+ },
+ "%PRODUCT%": function() {
+ return Services.appinfo.name;
+ },
+ "%APP_ID%": function() {
+ return Services.appinfo.ID;
+ },
+ "%APP_VERSION%": function() {
+ return Services.appinfo.version;
+ },
+ "%BUILD_ID%": function() {
+ return Services.appinfo.appBuildID;
+ },
+ "%OS%": function() {
+ return Services.appinfo.OS;
+ },
+ "%CHANNEL%": function() {
+ return UpdateUtils.UpdateChannel;
+ },
+ "%DISTRIBUTION%": function() {
+ return this._getPref(PREF_APP_DISTRIBUTION, "");
+ },
+ "%DISTRIBUTION_VERSION%": function() {
+ return this._getPref(PREF_APP_DISTRIBUTION_VERSION, "");
+ },
+ };
+ },
+
+ _getUpdateURL() {
+ let url = this._getPref(PREF_UPDATES_URL, "");
+ let params = this._getParameters();
+ return url.replace(/%[A-Z_]+%/g, function(match) {
+ let param = params[match];
+ // preserve the %FOO% string (e.g. as an encoding) if it's not a valid parameter
+ return param ? encodeURIComponent(param()) : match;
+ });
+ },
+
+ _fetchUpdate(url, success, error) {
+ let request = new XMLHttpRequest();
+ request.mozBackgroundRequest = true;
+ request.timeout = this._getPref(PREF_UPDATES_TIMEOUT, 60000);
+ request.open("GET", url, true);
+ request.overrideMimeType("application/json");
+ request.responseType = "json";
+
+ request.addEventListener("load", function() {
+ let response = request.response;
+ response ? success(response) : error();
+ });
+ request.addEventListener("error", error);
+ request.send();
+ },
+
+ _update() {
+ let url = this._getUpdateURL();
+ url &&
+ this._fetchUpdate(
+ url,
+ response => {
+ // success
+ // apply update and save overrides to profile
+ this._applyUpdate(response);
+ this._saveToFile(response);
+ this._scheduleUpdate(); // cancel any retries
+ },
+ response => {
+ // error
+ this._scheduleUpdate(true /* retry */);
+ }
+ );
+ },
+
+ _scheduleUpdate(retry) {
+ // only schedule updates in the main process
+ if (
+ Services.appinfo.processType !== Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT
+ ) {
+ return;
+ }
+ let interval = this._getPref(PREF_UPDATES_INTERVAL, 604800 /* 1 week */);
+ if (retry) {
+ interval = this._getPref(PREF_UPDATES_RETRY, interval);
+ }
+ gUpdateTimer.registerTimer(TIMER_ID, this, Math.max(1, interval));
+ },
+
+ notify(timer) {
+ // timer notification
+ if (this._getPref(PREF_UPDATES_ENABLED, false)) {
+ this._update();
+ }
+ },
+
+ observe(subject, topic, data) {
+ switch (topic) {
+ case "nsPref:changed":
+ if (data === PREF_UPDATES_ENABLED) {
+ this._applySavedUpdate();
+ } else if (data === PREF_UPDATES_INTERVAL) {
+ this._scheduleUpdate();
+ } else if (data === PREF_UPDATES_LASTUPDATED) {
+ // reload from file if there has been an update
+ let lastUpdated = parseInt(
+ this._getPref(PREF_UPDATES_LASTUPDATED, "0"),
+ 0
+ );
+ if (lastUpdated > this._lastUpdated) {
+ this._applySavedUpdate();
+ this._lastUpdated = lastUpdated;
+ }
+ }
+ break;
+ }
+ },
+
+ QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver, Ci.nsITimerCallback]),
+};