mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 15:23:51 +00:00
Bug 1513574 - Remove UserAgentOverrides.jsm and UserAgentUpdates.jsm r=michal
Also removes the UA cache attached to nsILoadGroup and nsIRequestContext and the "http-on-useragent-request" observer notification. If overriding the user agent is needed "http-on-modify-request" is equally usable (but should be used rarely, for performance reasons). A better way is using nsIDocShell.customUserAgent. Depends on D14750 Differential Revision: https://phabricator.services.mozilla.com/D14751 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
44d8431a8c
commit
b27c4726d0
@ -13,7 +13,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1339722
|
||||
|
||||
/**
|
||||
* Test for Bug 1339722
|
||||
* 1. Wait for "http-on-useragent-request" for the iframe load.
|
||||
* 1. Wait for "http-on-modify-request" for the iframe load.
|
||||
* 2. In the observer, access it's window proxy to trigger DOMWindowCreated.
|
||||
* 3. In the event handler, delete the iframe so that the frameloader would be
|
||||
* destoryed in the middle of ReallyStartLoading.
|
||||
@ -22,7 +22,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1339722
|
||||
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const TOPIC = "http-on-useragent-request";
|
||||
// This topic used to be http-on-useragent-request, but that got removed in
|
||||
// bug 1513574. on-modify-request is called around the same time, and should
|
||||
// behave similarly.
|
||||
const TOPIC = "http-on-modify-request";
|
||||
let win;
|
||||
Services.obs.addObserver({
|
||||
observe(subject, topic, data) {
|
||||
|
@ -693,12 +693,6 @@ pref("dom.phonenumber.substringmatching.VE", 7);
|
||||
|
||||
pref("gfx.canvas.azure.backends", "skia");
|
||||
|
||||
// See ua-update.json.in for the packaged UA override list
|
||||
pref("general.useragent.updates.enabled", true);
|
||||
pref("general.useragent.updates.url", "https://dynamicua.cdn.mozilla.net/0/%APP_ID%");
|
||||
pref("general.useragent.updates.interval", 604800); // 1 week
|
||||
pref("general.useragent.updates.retry", 86400); // 1 day
|
||||
|
||||
// When true, phone number linkification is enabled.
|
||||
pref("browser.ui.linkify.phone", false);
|
||||
|
||||
|
@ -205,10 +205,6 @@ pref("security.remote_settings.crlite_filters.signer", "onecrl.content-signature
|
||||
|
||||
pref("general.useragent.compatMode.firefox", false);
|
||||
|
||||
// This pref exists only for testing purposes. In order to disable all
|
||||
// overrides by default, don't initialize UserAgentOverrides.jsm.
|
||||
pref("general.useragent.site_specific_overrides", true);
|
||||
|
||||
pref("general.config.obscure_value", 13); // for MCD .cfg files
|
||||
|
||||
pref("general.warnOnAboutConfig", true);
|
||||
|
@ -60,7 +60,6 @@ class RequestContext final : public nsIRequestContext, public nsITimerCallback {
|
||||
uint64_t mID;
|
||||
Atomic<uint32_t> mBlockingTransactionCount;
|
||||
nsAutoPtr<SpdyPushCache> mSpdyCache;
|
||||
nsCString mUserAgentOverride;
|
||||
|
||||
typedef nsCOMPtr<nsIRequestTailUnblockCallback> PendingTailRequest;
|
||||
// Number of known opened non-tailed requets
|
||||
@ -187,15 +186,6 @@ void RequestContext::SetSpdyPushCache(SpdyPushCache* aSpdyPushCache) {
|
||||
|
||||
uint64_t RequestContext::GetID() { return mID; }
|
||||
|
||||
const nsACString& RequestContext::GetUserAgentOverride() {
|
||||
return mUserAgentOverride;
|
||||
}
|
||||
|
||||
void RequestContext::SetUserAgentOverride(
|
||||
const nsACString& aUserAgentOverride) {
|
||||
mUserAgentOverride = aUserAgentOverride;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RequestContext::AddNonTailRequest() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
@ -95,10 +95,4 @@ interface nsILoadGroup : nsIRequest
|
||||
* the docShell has created the default request.)
|
||||
*/
|
||||
attribute nsLoadFlags defaultLoadFlags;
|
||||
|
||||
/**
|
||||
* The cached user agent override created by UserAgentOverrides.jsm. Used
|
||||
* for all sub-resource requests in the loadgroup.
|
||||
*/
|
||||
attribute ACString userAgentOverrideCache;
|
||||
};
|
||||
|
@ -93,11 +93,6 @@ interface nsIRequestContext : nsISupports
|
||||
*/
|
||||
[notxpcom,nostdcall] attribute SpdyPushCachePtr spdyPushCache;
|
||||
|
||||
/**
|
||||
* This holds a cached value of the user agent override.
|
||||
*/
|
||||
[notxpcom,nostdcall] attribute ACString userAgentOverride;
|
||||
|
||||
/**
|
||||
* Increases/decrease the number of non-tailed requests in this context.
|
||||
* If the count drops to zero, all tail-blocked callbacks are notified
|
||||
|
@ -715,19 +715,6 @@ nsLoadGroup::SetDefaultLoadFlags(uint32_t aFlags) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLoadGroup::GetUserAgentOverrideCache(nsACString& aUserAgentOverrideCache) {
|
||||
aUserAgentOverrideCache = mUserAgentOverrideCache;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLoadGroup::SetUserAgentOverrideCache(
|
||||
const nsACString& aUserAgentOverrideCache) {
|
||||
mUserAgentOverrideCache = aUserAgentOverrideCache;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void nsLoadGroup::TelemetryReport() {
|
||||
|
@ -87,8 +87,6 @@ class nsLoadGroup : public nsILoadGroup,
|
||||
mozilla::TimeStamp mDefaultRequestCreationTime;
|
||||
uint32_t mTimedRequests;
|
||||
uint32_t mCachedRequests;
|
||||
|
||||
nsCString mUserAgentOverrideCache;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
|
@ -485,8 +485,7 @@ HttpBaseChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpBaseChannel::SetDocshellUserAgentOverride() {
|
||||
// This sets the docshell specific user agent override, it will be overwritten
|
||||
// by UserAgentOverrides.jsm if site-specific user agent overrides are set.
|
||||
// This sets the docshell specific user agent override
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsILoadContext> loadContext;
|
||||
NS_QueryNotificationCallbacks(this, loadContext);
|
||||
|
@ -1,188 +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";
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,333 +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";
|
||||
|
||||
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]),
|
||||
};
|
@ -123,11 +123,6 @@ IPDL_SOURCES += [
|
||||
'PHttpChannel.ipdl',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'UserAgentOverrides.jsm',
|
||||
'UserAgentUpdates.jsm',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
@ -444,9 +444,6 @@ nsresult nsHttpChannel::PrepareToConnect() {
|
||||
// notify "http-on-modify-request" observers
|
||||
CallOnModifyRequestObservers();
|
||||
|
||||
SetLoadGroupUserAgentOverride();
|
||||
|
||||
// Check if request was cancelled during on-modify-request or on-useragent.
|
||||
if (mCanceled) {
|
||||
return mStatus;
|
||||
}
|
||||
@ -512,8 +509,7 @@ void nsHttpChannel::HandleOnBeforeConnect() {
|
||||
nsresult nsHttpChannel::OnBeforeConnect() {
|
||||
nsresult rv;
|
||||
|
||||
// Check if request was cancelled during suspend AFTER on-modify-request or
|
||||
// on-useragent.
|
||||
// Check if request was cancelled during suspend AFTER on-modify-request
|
||||
if (mCanceled) {
|
||||
return mStatus;
|
||||
}
|
||||
@ -6197,9 +6193,7 @@ nsHttpChannel::CancelByURLClassifier(nsresult aErrorCode) {
|
||||
// notify "http-on-modify-request" observers
|
||||
CallOnModifyRequestObservers();
|
||||
|
||||
SetLoadGroupUserAgentOverride();
|
||||
|
||||
// Check if request was cancelled during on-modify-request or on-useragent.
|
||||
// Check if request was cancelled during on-modify-request
|
||||
if (mCanceled) {
|
||||
return mStatus;
|
||||
}
|
||||
@ -9644,48 +9638,6 @@ void nsHttpChannel::MaybeWarnAboutAppCache() {
|
||||
}
|
||||
}
|
||||
|
||||
void nsHttpChannel::SetLoadGroupUserAgentOverride() {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
GetURI(getter_AddRefs(uri));
|
||||
nsAutoCString uriScheme;
|
||||
if (uri) {
|
||||
uri->GetScheme(uriScheme);
|
||||
}
|
||||
|
||||
// We don't need a UA for file: protocols.
|
||||
if (uriScheme.EqualsLiteral("file")) {
|
||||
gHttpHandler->OnUserAgentRequest(this);
|
||||
return;
|
||||
}
|
||||
|
||||
nsIRequestContextService* rcsvc = gHttpHandler->GetRequestContextService();
|
||||
nsCOMPtr<nsIRequestContext> rc;
|
||||
if (rcsvc) {
|
||||
rcsvc->GetRequestContext(mRequestContextID, getter_AddRefs(rc));
|
||||
}
|
||||
|
||||
nsAutoCString ua;
|
||||
if (nsContentUtils::IsNonSubresourceRequest(this)) {
|
||||
gHttpHandler->OnUserAgentRequest(this);
|
||||
if (rc) {
|
||||
GetRequestHeader(NS_LITERAL_CSTRING("User-Agent"), ua);
|
||||
rc->SetUserAgentOverride(ua);
|
||||
}
|
||||
} else {
|
||||
GetRequestHeader(NS_LITERAL_CSTRING("User-Agent"), ua);
|
||||
// Don't overwrite the UA if it is already set (eg by an XHR with explicit
|
||||
// UA).
|
||||
if (ua.IsEmpty()) {
|
||||
if (rc) {
|
||||
SetRequestHeader(NS_LITERAL_CSTRING("User-Agent"),
|
||||
rc->GetUserAgentOverride(), false);
|
||||
} else {
|
||||
gHttpHandler->OnUserAgentRequest(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 10 of HTTP-network-or-cache fetch
|
||||
void nsHttpChannel::SetOriginHeader() {
|
||||
if (mRequestHead.IsGet() || mRequestHead.IsHead()) {
|
||||
|
@ -533,8 +533,6 @@ class nsHttpChannel final : public HttpBaseChannel,
|
||||
|
||||
void MaybeWarnAboutAppCache();
|
||||
|
||||
void SetLoadGroupUserAgentOverride();
|
||||
|
||||
void SetOriginHeader();
|
||||
void SetDoNotTrack();
|
||||
|
||||
|
@ -383,24 +383,6 @@ void nsHttpHandler::SetFastOpenOSSupport() {
|
||||
mFastOpenSupported ? "" : "not"));
|
||||
}
|
||||
|
||||
void nsHttpHandler::EnsureUAOverridesInit() {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
static bool initDone = false;
|
||||
|
||||
if (initDone) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsISupports> bootstrapper =
|
||||
do_GetService("@mozilla.org/network/ua-overrides-bootstrapper;1", &rv);
|
||||
MOZ_ASSERT(bootstrapper);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
initDone = true;
|
||||
}
|
||||
|
||||
nsHttpHandler::~nsHttpHandler() {
|
||||
LOG(("Deleting nsHttpHandler [this=%p]\n", this));
|
||||
|
||||
@ -2064,11 +2046,6 @@ nsHttpHandler::NewProxiedChannel(nsIURI* uri, nsIProxyInfo* givenProxyInfo,
|
||||
net_EnsurePSMInit();
|
||||
}
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
// Load UserAgentOverrides.jsm before any HTTP request is issued.
|
||||
EnsureUAOverridesInit();
|
||||
}
|
||||
|
||||
uint64_t channelId;
|
||||
nsresult rv = NewChannelId(channelId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -375,11 +375,6 @@ class nsHttpHandler final : public nsIHttpProtocolHandler,
|
||||
NotifyObservers(chan, NS_HTTP_ON_STOP_REQUEST_TOPIC);
|
||||
}
|
||||
|
||||
// Called by the channel and cached in the loadGroup
|
||||
void OnUserAgentRequest(nsIHttpChannel* chan) {
|
||||
NotifyObservers(chan, NS_HTTP_ON_USERAGENT_REQUEST_TOPIC);
|
||||
}
|
||||
|
||||
// Called by the channel before setting up the transaction
|
||||
void OnBeforeConnect(nsIHttpChannel* chan) {
|
||||
NotifyObservers(chan, NS_HTTP_ON_BEFORE_CONNECT_TOPIC);
|
||||
@ -487,8 +482,6 @@ class nsHttpHandler final : public nsIHttpProtocolHandler,
|
||||
|
||||
void SetFastOpenOSSupport();
|
||||
|
||||
void EnsureUAOverridesInit();
|
||||
|
||||
// Checks if there are any user certs or active smart cards on a different
|
||||
// thread. Updates mSpeculativeConnectEnabled when done.
|
||||
void MaybeEnableSpeculativeConnect();
|
||||
|
@ -181,15 +181,6 @@ interface nsIHttpProtocolHandler : nsIProxiedProtocolHandler
|
||||
*/
|
||||
#define NS_HTTP_ON_MAY_CHANGE_PROCESS_TOPIC "http-on-may-change-process"
|
||||
|
||||
/**
|
||||
* Before an HTTP request corresponding to a channel with the LOAD_DOCUMENT_URI
|
||||
* flag is sent to the server, this observer topic is notified. The observer of
|
||||
* this topic can then choose to modify the user agent for this request before
|
||||
* the request is actually sent to the server. Additionally, the modified user
|
||||
* agent will be propagated to sub-resource requests from the same load group.
|
||||
*/
|
||||
#define NS_HTTP_ON_USERAGENT_REQUEST_TOPIC "http-on-useragent-request"
|
||||
|
||||
/**
|
||||
* This topic is notified for every http channel right after it called
|
||||
* OnStopRequest on its listener, regardless whether it was finished
|
||||
|
@ -3,8 +3,6 @@ support-files =
|
||||
method.sjs
|
||||
partial_content.sjs
|
||||
rel_preconnect.sjs
|
||||
user_agent.sjs
|
||||
user_agent_update.sjs
|
||||
set_cookie_xhr.sjs
|
||||
reset_cookie_xhr.sjs
|
||||
web_packaged_app.sjs
|
||||
@ -67,10 +65,7 @@ support-files =
|
||||
[test_rel_preconnect.html]
|
||||
[test_redirect_ref.html]
|
||||
[test_uri_scheme.html]
|
||||
[test_user_agent_overrides.html]
|
||||
[test_user_agent_updates.html]
|
||||
skip-if = (verify && debug && os == 'mac')
|
||||
[test_user_agent_updates_reset.html]
|
||||
[test_viewsource_unlinkable.html]
|
||||
[test_xhr_method_case.html]
|
||||
[test_1331680.html]
|
||||
|
@ -1,247 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=782453
|
||||
-->
|
||||
<head>
|
||||
<title>Test for User Agent Overrides</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=782453">Mozilla Bug 782453</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
const PREF_OVERRIDES_ENABLED = "general.useragent.site_specific_overrides";
|
||||
const PREF_OVERRIDES_BRANCH = "general.useragent.override.";
|
||||
|
||||
const DEFAULT_UA = navigator.userAgent;
|
||||
|
||||
const UA_WHOLE_OVERRIDE = "DummyUserAgent";
|
||||
const UA_WHOLE_EXPECTED = UA_WHOLE_OVERRIDE;
|
||||
|
||||
const UA_PARTIAL_FROM = "\\wozilla"; // /\wozilla
|
||||
const UA_PARTIAL_SEP = "#";
|
||||
const UA_PARTIAL_TO = UA_WHOLE_OVERRIDE;
|
||||
const UA_PARTIAL_OVERRIDE = UA_PARTIAL_FROM + UA_PARTIAL_SEP + UA_PARTIAL_TO;
|
||||
const UA_PARTIAL_EXPECTED = DEFAULT_UA.replace(new RegExp(UA_PARTIAL_FROM, 'g'), UA_PARTIAL_TO);
|
||||
|
||||
const UA_PARTIAL_FROM2 = "[0-9]+";
|
||||
const UA_PARTIAL_TO2 = "number";
|
||||
const UA_PARTIAL_OVERRIDE2 = UA_PARTIAL_FROM2 + UA_PARTIAL_SEP + UA_PARTIAL_TO2;
|
||||
const UA_PARTIAL_EXPECTED2 = DEFAULT_UA.replace(new RegExp(UA_PARTIAL_FROM2, 'g'), UA_PARTIAL_TO2);
|
||||
|
||||
function testUAIFrame(host, expected, sameQ, message, testNavQ, navSameQ, navMessage, callback) {
|
||||
let url = location.pathname;
|
||||
url = host + url.slice(0, url.lastIndexOf('/')) + '/user_agent.sjs';
|
||||
let ifr = document.createElement('IFRAME');
|
||||
|
||||
ifr.src = url;
|
||||
|
||||
document.getElementById('content').appendChild(ifr);
|
||||
|
||||
window.addEventListener("message", function recv(e) {
|
||||
ok(sameQ == (e.data.header.includes(expected)), message);
|
||||
if (testNavQ) {
|
||||
ok(navSameQ == (e.data.nav.includes(expected)), navMessage);
|
||||
}
|
||||
window.removeEventListener("message", recv);
|
||||
callback();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function testUAIFrameNoNav(host, expected, sameQ, message, callback) {
|
||||
testUAIFrame(host, expected, sameQ, message, false, true, '', callback);
|
||||
}
|
||||
|
||||
function testUA(options, callback) {
|
||||
var [domain, override, test_hosts, expected] =
|
||||
[options.domain, options.override, options.test_hosts, options.expected];
|
||||
|
||||
(function nextTest() {
|
||||
let test_host = test_hosts.shift();
|
||||
|
||||
info("Overriding " + domain + " with " + override + " for " + test_host);
|
||||
|
||||
function is_subdomain(host) {
|
||||
var [test_domain] = host.slice(host.lastIndexOf('/') + 1).split(':', 1);
|
||||
return test_domain === domain || test_domain.endsWith('.' + domain);
|
||||
}
|
||||
|
||||
var localhost = location.origin;
|
||||
var overrideNavigator = is_subdomain(localhost);
|
||||
var navigator_ua, test_ua;
|
||||
|
||||
if (overrideNavigator) {
|
||||
navigator_ua = navigator.userAgent;
|
||||
}
|
||||
|
||||
let url = location.pathname;
|
||||
url = test_host + url.slice(0, url.lastIndexOf('/')) + '/user_agent.sjs';
|
||||
let ifr = document.createElement('IFRAME');
|
||||
ifr.src = url;
|
||||
|
||||
document.getElementById('content').appendChild(ifr);
|
||||
|
||||
window.addEventListener("message", function recv(e) {
|
||||
test_ua = e.data.header;
|
||||
SpecialPowers.pushPrefEnv({
|
||||
set: [[PREF_OVERRIDES_BRANCH + domain, override]],
|
||||
}, function () {
|
||||
testUAIFrame(test_host, expected, true, 'Header UA not overridden at step ' + (++step), true,
|
||||
true, 'Navigator UA not overridden at step ' + (++step), function () {
|
||||
// clear the override pref to undo overriding the UA
|
||||
SpecialPowers.pushPrefEnv({
|
||||
clear: [[PREF_OVERRIDES_BRANCH + domain]],
|
||||
}, function () {
|
||||
testUAIFrameNoNav(test_host, test_ua, true, 'Header UA not restored at step ' + (++step), function() {
|
||||
test_hosts.length ? nextTest() : callback();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
window.removeEventListener("message", recv);
|
||||
});
|
||||
})();
|
||||
}
|
||||
|
||||
var step = 0; // for logging
|
||||
var tests = [
|
||||
// should override both header and navigator.userAgent
|
||||
{
|
||||
domain: location.hostname,
|
||||
override: UA_WHOLE_OVERRIDE,
|
||||
test_hosts: [location.origin],
|
||||
expected: UA_WHOLE_EXPECTED
|
||||
},
|
||||
|
||||
// should support partial overrides
|
||||
{
|
||||
domain: location.hostname,
|
||||
override: UA_PARTIAL_OVERRIDE,
|
||||
test_hosts: [location.origin],
|
||||
expected: UA_PARTIAL_EXPECTED
|
||||
},
|
||||
|
||||
{
|
||||
domain: location.hostname,
|
||||
override: UA_PARTIAL_OVERRIDE2,
|
||||
test_hosts: [location.origin],
|
||||
expected: UA_PARTIAL_EXPECTED2
|
||||
},
|
||||
|
||||
// should match domain and subdomains
|
||||
{
|
||||
domain: 'example.org',
|
||||
override: UA_WHOLE_OVERRIDE,
|
||||
test_hosts: ['http://example.org',
|
||||
'http://test1.example.org',
|
||||
'http://sub1.test1.example.org'],
|
||||
expected: UA_WHOLE_EXPECTED
|
||||
},
|
||||
|
||||
// should not match superdomains
|
||||
{
|
||||
domain: 'sub1.test1.example.org',
|
||||
override: UA_WHOLE_OVERRIDE,
|
||||
test_hosts: ['http://example.org',
|
||||
'http://test1.example.org'],
|
||||
expected: DEFAULT_UA
|
||||
},
|
||||
|
||||
// should work with https
|
||||
{
|
||||
domain: 'example.com',
|
||||
override: UA_WHOLE_OVERRIDE,
|
||||
test_hosts: ['https://example.com',
|
||||
'https://test1.example.com',
|
||||
'https://sub1.test1.example.com'],
|
||||
expected: UA_WHOLE_EXPECTED
|
||||
},
|
||||
];
|
||||
|
||||
// test that UA is not overridden when the 'site_specific_overrides' pref is off
|
||||
function testInactive(callback) {
|
||||
SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
[PREF_OVERRIDES_ENABLED, false],
|
||||
[PREF_OVERRIDES_BRANCH + location.hostname, UA_WHOLE_OVERRIDE]
|
||||
]
|
||||
}, function () {
|
||||
testUAIFrame(location.origin, UA_WHOLE_OVERRIDE, false, 'Failed to disabled header UA override at step ' + (++step),
|
||||
true, false, 'Failed to disable navigator UA override at step + ' + (++step), function () {
|
||||
SpecialPowers.pushPrefEnv({
|
||||
clear: [
|
||||
[PREF_OVERRIDES_ENABLED],
|
||||
[PREF_OVERRIDES_BRANCH + location.hostname]
|
||||
]
|
||||
}, callback);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testPriority(callback) {
|
||||
// foo.bar.com override should have priority over bar.com override
|
||||
var tests = [
|
||||
['example.org', 'test1.example.org', 'sub1.test1.example.org'],
|
||||
['example.org', 'test1.example.org', 'sub2.test1.example.org'],
|
||||
['example.org', 'test2.example.org', 'sub1.test2.example.org'],
|
||||
['example.org', 'test2.example.org', 'sub2.test2.example.org'],
|
||||
];
|
||||
(function nextTest() {
|
||||
var [level0, level1, level2] = tests.shift();
|
||||
var host = 'http://' + level2;
|
||||
SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
[PREF_OVERRIDES_ENABLED, true],
|
||||
[PREF_OVERRIDES_BRANCH + level1, UA_WHOLE_OVERRIDE]
|
||||
]
|
||||
}, function () {
|
||||
// should use first override at this point
|
||||
testUAIFrameNoNav(host, UA_WHOLE_EXPECTED, true, 'UA not overridden at step ' + (++step), function() {
|
||||
// add a second override that should be used
|
||||
testUA({
|
||||
domain: level2,
|
||||
override: UA_PARTIAL_OVERRIDE,
|
||||
test_hosts: [host],
|
||||
expected: UA_PARTIAL_EXPECTED
|
||||
}, function () {
|
||||
// add a third override that should not be used
|
||||
testUA({
|
||||
domain: level0,
|
||||
override: UA_PARTIAL_OVERRIDE,
|
||||
test_hosts: [host],
|
||||
expected: UA_WHOLE_EXPECTED
|
||||
}, tests.length ? nextTest : callback);
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
}
|
||||
|
||||
function testOverrides(callback) {
|
||||
SpecialPowers.pushPrefEnv({
|
||||
set: [[PREF_OVERRIDES_ENABLED, true]]
|
||||
}, function nextTest() {
|
||||
testUA(tests.shift(), function() { tests.length ? nextTest() : callback() });
|
||||
});
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.requestCompleteLog();
|
||||
SimpleTest.requestLongerTimeout(5);
|
||||
|
||||
testOverrides(function() {
|
||||
testInactive(function() {
|
||||
testPriority(SimpleTest.finish)
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,357 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=897221
|
||||
-->
|
||||
<head>
|
||||
<title>Test for User Agent Updates</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=897221">Mozilla Bug 897221</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
const PREF_APP_UPDATE_TIMERMINIMUMDELAY = "app.update.timerMinimumDelay";
|
||||
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_TIMEOUT = PREF_UPDATES + "timeout";
|
||||
|
||||
const DEFAULT_UA = navigator.userAgent;
|
||||
const UA_OVERRIDE = "DummyUserAgent";
|
||||
const UA_ALT_OVERRIDE = "AltUserAgent";
|
||||
|
||||
const UA_PARTIAL_FROM = "\\wozilla"; // /\wozilla
|
||||
const UA_PARTIAL_SEP = "#";
|
||||
const UA_PARTIAL_TO = UA_OVERRIDE;
|
||||
const UA_PARTIAL_OVERRIDE = UA_PARTIAL_FROM + UA_PARTIAL_SEP + UA_PARTIAL_TO;
|
||||
const UA_PARTIAL_EXPECTED = DEFAULT_UA.replace(new RegExp(UA_PARTIAL_FROM, 'g'), UA_PARTIAL_TO);
|
||||
|
||||
function getUA(host) {
|
||||
var url = location.pathname;
|
||||
url = host + url.slice(0, url.lastIndexOf('/')) + '/user_agent.sjs';
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', url, false); // sync request
|
||||
xhr.send();
|
||||
is(xhr.status, 200, 'request failed');
|
||||
is(typeof xhr.response, 'string', 'invalid response');
|
||||
return xhr.response;
|
||||
}
|
||||
|
||||
function testUAIFrame(host, expected, sameQ, message, testNavQ, navSameQ, navMessage, callback) {
|
||||
let url = location.pathname;
|
||||
url = host + url.slice(0, url.lastIndexOf('/')) + '/user_agent.sjs';
|
||||
let ifr = document.createElement('IFRAME');
|
||||
|
||||
ifr.src = url;
|
||||
|
||||
document.getElementById('content').appendChild(ifr);
|
||||
|
||||
window.addEventListener("message", function recv(e) {
|
||||
ok(sameQ == (e.data.header.includes(expected)), message);
|
||||
if (testNavQ) {
|
||||
ok(navSameQ == (e.data.nav.includes(expected)), navMessage);
|
||||
}
|
||||
window.removeEventListener("message", recv);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function testUAIFrameNoNav(host, expected, sameQ, message, callback) {
|
||||
testUAIFrame(host, expected, sameQ, message, false, true, '', callback);
|
||||
}
|
||||
|
||||
const OVERRIDES = [
|
||||
{
|
||||
domain: 'example.org',
|
||||
override: '%DATE%',
|
||||
host: 'http://example.org'
|
||||
},
|
||||
{
|
||||
domain: 'test1.example.org',
|
||||
override: '%PRODUCT%',
|
||||
expected: SpecialPowers.Services.appinfo.name,
|
||||
host: 'http://test1.example.org'
|
||||
},
|
||||
{
|
||||
domain: 'test2.example.org',
|
||||
override: '%APP_ID%',
|
||||
expected: SpecialPowers.Services.appinfo.ID,
|
||||
host: 'http://test2.example.org'
|
||||
},
|
||||
{
|
||||
domain: 'sub1.test1.example.org',
|
||||
override: '%APP_VERSION%',
|
||||
expected: SpecialPowers.Services.appinfo.version,
|
||||
host: 'http://sub1.test1.example.org'
|
||||
},
|
||||
{
|
||||
domain: 'sub2.test1.example.org',
|
||||
override: '%BUILD_ID%',
|
||||
expected: SpecialPowers.Services.appinfo.appBuildID,
|
||||
host: 'http://sub2.test1.example.org'
|
||||
},
|
||||
{
|
||||
domain: 'sub1.test2.example.org',
|
||||
override: '%OS%',
|
||||
expected: SpecialPowers.Services.appinfo.OS,
|
||||
host: 'http://sub1.test2.example.org'
|
||||
},
|
||||
{
|
||||
domain: 'sub2.test2.example.org',
|
||||
override: UA_PARTIAL_OVERRIDE,
|
||||
expected: UA_PARTIAL_EXPECTED,
|
||||
host: 'http://sub2.test2.example.org'
|
||||
},
|
||||
];
|
||||
|
||||
function getServerURL() {
|
||||
var url = location.pathname;
|
||||
return location.origin + url.slice(0, url.lastIndexOf('/')) + '/user_agent_update.sjs?';
|
||||
}
|
||||
|
||||
function getUpdateURL() {
|
||||
var url = getServerURL();
|
||||
var overrides = {};
|
||||
overrides[location.hostname] = UA_OVERRIDE;
|
||||
OVERRIDES.forEach(function (val) {
|
||||
overrides[val.domain] = val.override;
|
||||
});
|
||||
url = url + encodeURIComponent(JSON.stringify(overrides)).replace(/%25/g, '%');
|
||||
return url;
|
||||
}
|
||||
|
||||
function testDownload(callback) {
|
||||
var startTime = Date.now();
|
||||
var url = getUpdateURL();
|
||||
isnot(navigator.userAgent, UA_OVERRIDE, 'UA already overridden');
|
||||
info('Waiting for UA update: ' + url);
|
||||
|
||||
chromeScript.sendAsyncMessage("notify-on-update");
|
||||
SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
[PREF_UPDATES_ENABLED, true],
|
||||
[PREF_UPDATES_URL, url],
|
||||
[PREF_UPDATES_TIMEOUT, 10000],
|
||||
[PREF_UPDATES_INTERVAL, 1] // 1 second interval
|
||||
]
|
||||
});
|
||||
|
||||
function waitForUpdate() {
|
||||
info("Update Happened");
|
||||
testUAIFrameNoNav(location.origin, UA_OVERRIDE, true, 'Header UA not overridden', function() {
|
||||
var updateTime = parseInt(getUA('http://example.org'));
|
||||
todo(startTime <= updateTime, 'Update was before start time');
|
||||
todo(updateTime <= Date.now(), 'Update was after present time');
|
||||
|
||||
let overs = OVERRIDES;
|
||||
(function nextOverride() {
|
||||
val = overs.shift();
|
||||
if (val.expected) {
|
||||
testUAIFrameNoNav(val.host, val.expected, true, 'Incorrect URL parameter: ' + val.override, function() {
|
||||
overs.length ? nextOverride() : callback();
|
||||
});
|
||||
} else {
|
||||
nextOverride();
|
||||
}
|
||||
})();
|
||||
});
|
||||
}
|
||||
|
||||
chromeScript.addMessageListener("useragent-update-complete", waitForUpdate);
|
||||
}
|
||||
|
||||
function testBadUpdate(callback) {
|
||||
var url = getServerURL() + 'invalid-json';
|
||||
var prevOverride = navigator.userAgent;
|
||||
SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
[PREF_UPDATES_URL, url],
|
||||
[PREF_UPDATES_INTERVAL, 1] // 1 second interval
|
||||
]
|
||||
}, function () { setTimeout(function () {
|
||||
var ifr = document.createElement('IFRAME');
|
||||
ifr.src = "about:blank";
|
||||
|
||||
ifr.addEventListener('load', function() {
|
||||
// We want to make sure a bad update doesn't cancel out previous
|
||||
// overrides. We do this by waiting for 5 seconds (assuming the update
|
||||
// occurs within 5 seconds), and check that the previous override hasn't
|
||||
// changed.
|
||||
is(navigator.userAgent, prevOverride,
|
||||
'Invalid update deleted previous override');
|
||||
callback();
|
||||
});
|
||||
document.getElementById('content').appendChild(ifr);
|
||||
}, 5000); });
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.requestFlakyTimeout("Test sets timeouts to wait for updates to happen.");
|
||||
|
||||
SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
[PREF_APP_UPDATE_TIMERMINIMUMDELAY, 0]
|
||||
]
|
||||
}, function () {
|
||||
// Sets the OVERRIDES var in the chrome script.
|
||||
// We do this to avoid code duplication.
|
||||
chromeScript.sendAsyncMessage("set-overrides", OVERRIDES);
|
||||
|
||||
// testProfileLoad, testDownload, and testProfileSave must run in this order
|
||||
// because testDownload depends on testProfileLoad and testProfileSave depends
|
||||
// on testDownload to save overrides to the profile
|
||||
chromeScript.sendAsyncMessage("testProfileLoad", location.hostname);
|
||||
});
|
||||
|
||||
|
||||
const chromeScript = SpecialPowers.loadChromeScript(_ => {
|
||||
// Enter update timer manager test mode
|
||||
Cc["@mozilla.org/updates/timer-manager;1"].getService(
|
||||
Ci.nsIObserver).observe(null, "utm-test-init", "");
|
||||
|
||||
var _notifyOnUpdate = false;
|
||||
|
||||
const {FileUtils} = ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
|
||||
var FU = FileUtils;
|
||||
|
||||
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
||||
var OSF = OS.File;
|
||||
|
||||
const KEY_PREFDIR = "PrefD";
|
||||
const KEY_APPDIR = "XCurProcD";
|
||||
const FILE_UPDATES = "ua-update.json";
|
||||
|
||||
const UA_OVERRIDE = "DummyUserAgent";
|
||||
const UA_ALT_OVERRIDE = "AltUserAgent";
|
||||
|
||||
const PREF_UPDATES = "general.useragent.updates.";
|
||||
const PREF_UPDATES_ENABLED = PREF_UPDATES + "enabled";
|
||||
const PREF_UPDATES_LASTUPDATED = PREF_UPDATES + "lastupdated";
|
||||
|
||||
let { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
Services.prefs.addObserver(PREF_UPDATES_LASTUPDATED, () => {
|
||||
if (_notifyOnUpdate) {
|
||||
_notifyOnUpdate = false; // Only notify once, for the first update.
|
||||
sendAsyncMessage("useragent-update-complete");
|
||||
}
|
||||
});
|
||||
|
||||
var OVERRIDES = null;
|
||||
|
||||
function is(value, expected, message) {
|
||||
sendAsyncMessage("is-message", {value, expected, message});
|
||||
}
|
||||
|
||||
function info(message) {
|
||||
sendAsyncMessage("info-message", message);
|
||||
}
|
||||
|
||||
function testProfileSave(hostname) {
|
||||
info('Waiting for saving to profile');
|
||||
var file = FU.getFile(KEY_PREFDIR, [FILE_UPDATES]).path;
|
||||
(function waitForSave() {
|
||||
OSF.exists(file).then(
|
||||
(exists) => {
|
||||
if (!exists) {
|
||||
setTimeout(waitForSave, 100);
|
||||
return;
|
||||
}
|
||||
return OSF.read(file).then(
|
||||
(bytes) => {
|
||||
info('Saved new overrides');
|
||||
var decoder = new TextDecoder();
|
||||
var overrides = JSON.parse(decoder.decode(bytes));
|
||||
is(overrides[hostname], UA_OVERRIDE, 'Incorrect saved override');
|
||||
OVERRIDES.forEach(function (val) {
|
||||
val.expected && is(overrides[val.domain], val.expected,
|
||||
'Incorrect saved override: ' + val.override);
|
||||
});
|
||||
sendAsyncMessage("testProfileSaveDone");
|
||||
}
|
||||
);
|
||||
}
|
||||
).catch(
|
||||
(reason) => {
|
||||
throw reason
|
||||
}
|
||||
);
|
||||
})();
|
||||
}
|
||||
|
||||
function testProfileLoad(hostname) {
|
||||
var file = FU.getFile(KEY_APPDIR, [FILE_UPDATES]).path;
|
||||
var encoder = new TextEncoder();
|
||||
var overrides = {};
|
||||
overrides[hostname] = UA_ALT_OVERRIDE;
|
||||
var bytes = encoder.encode(JSON.stringify(overrides));
|
||||
|
||||
var badfile = FU.getFile(KEY_PREFDIR, [FILE_UPDATES]).path;
|
||||
var badbytes = encoder.encode("null");
|
||||
|
||||
OSF.writeAtomic(file, bytes, {tmpPath: file + ".tmp"}).then(
|
||||
() => OSF.writeAtomic(badfile, badbytes, {tmpPath: badfile + ".tmp"})
|
||||
).then(
|
||||
() => {
|
||||
sendAsyncMessage("testProfileLoadDone");
|
||||
},
|
||||
(reason) => {
|
||||
throw reason
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
addMessageListener("testProfileSave", testProfileSave);
|
||||
addMessageListener("testProfileLoad", testProfileLoad);
|
||||
addMessageListener("set-overrides", function(overrides) { OVERRIDES = overrides});
|
||||
addMessageListener("notify-on-update", () => { _notifyOnUpdate = true });
|
||||
}, { wantGlobalProperties: ["ChromeUtils", "TextEncoder", "TextDecoder"]});
|
||||
|
||||
chromeScript.addMessageListener("testProfileSaveDone", SimpleTest.finish);
|
||||
chromeScript.addMessageListener("testProfileLoadDone", function() {
|
||||
SpecialPowers.pushPrefEnv({
|
||||
set: [[PREF_UPDATES_ENABLED, true]]
|
||||
}, function () {
|
||||
(function waitForLoad() {
|
||||
var ifr = document.createElement('IFRAME');
|
||||
ifr.src = location.origin;
|
||||
|
||||
ifr.addEventListener('load', function() {
|
||||
var nav = ifr.contentWindow.navigator;
|
||||
if (nav.userAgent !== UA_ALT_OVERRIDE) {
|
||||
setTimeout(waitForLoad, 100);
|
||||
return;
|
||||
}
|
||||
testUAIFrameNoNav(location.origin, UA_ALT_OVERRIDE, true, 'Did not apply saved override', function () {
|
||||
testDownload(function() {
|
||||
testBadUpdate(function() {
|
||||
chromeScript.sendAsyncMessage("testProfileSave", location.hostname);
|
||||
})
|
||||
})
|
||||
});
|
||||
}, true);
|
||||
|
||||
document.getElementById('content').appendChild(ifr);
|
||||
})();
|
||||
});
|
||||
});
|
||||
|
||||
chromeScript.addMessageListener("is-message", function(params) {
|
||||
let {value, expected, message} = params;
|
||||
is(value, expected, message);
|
||||
});
|
||||
chromeScript.addMessageListener("info-message", function(message) {
|
||||
info(message);
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,44 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=942470
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 942470</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=942470">Mozilla Bug 942470</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Bug 942470 **/
|
||||
|
||||
function getUA(host) {
|
||||
var url = location.pathname;
|
||||
url = host + url.slice(0, url.lastIndexOf('/')) + '/user_agent.sjs';
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', url, false); // sync request
|
||||
xhr.send();
|
||||
is(xhr.status, 200, 'request failed');
|
||||
is(typeof xhr.response, 'string', 'invalid response');
|
||||
return xhr.response;
|
||||
}
|
||||
|
||||
const UA_OVERRIDE = "DummyUserAgent";
|
||||
|
||||
info("User agent is " + navigator.userAgent);
|
||||
isnot(navigator.userAgent, UA_OVERRIDE,
|
||||
"navigator.userAgent is not reverted");
|
||||
isnot(getUA(location.origin), UA_OVERRIDE,
|
||||
"User-Agent is not reverted");
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,21 +0,0 @@
|
||||
|
||||
function handleRequest(request, response)
|
||||
{
|
||||
// avoid confusing cache behaviors
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
response.setHeader("Content-Type", "text/html", false);
|
||||
response.setHeader("Access-Control-Allow-Origin", "*", false);
|
||||
|
||||
// used by test_user_agent tests
|
||||
response.write(
|
||||
"<html><body>\
|
||||
<script type='text/javascript'>\
|
||||
var msg = {\
|
||||
header: '" + request.getHeader('User-Agent') + "',\
|
||||
nav: navigator.userAgent\
|
||||
};\
|
||||
self.parent.postMessage(msg, '*');\
|
||||
</script>\
|
||||
</body></html>"
|
||||
);
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
|
||||
function handleRequest(request, response)
|
||||
{
|
||||
// avoid confusing cache behaviors
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
response.setHeader("Content-Type", "application/json", false);
|
||||
|
||||
// used by test_user_agent_updates test
|
||||
response.write(decodeURIComponent(request.queryString));
|
||||
}
|
Loading…
Reference in New Issue
Block a user