Bug 1069401 - UserAgent cannot be changed for specific websites in workers, r=khuey, r=bz

This commit is contained in:
Andrea Marchesini 2014-09-23 22:26:00 +02:00
parent 334e776a84
commit 1aa22e75ce
14 changed files with 297 additions and 106 deletions

View File

@ -313,30 +313,19 @@ Navigator::Invalidate()
NS_IMETHODIMP
Navigator::GetUserAgent(nsAString& aUserAgent)
{
nsresult rv = NS_GetNavigatorUserAgent(aUserAgent);
NS_ENSURE_SUCCESS(rv, rv);
if (!mWindow || !mWindow->GetDocShell()) {
return NS_OK;
}
nsIDocument* doc = mWindow->GetExtantDoc();
if (!doc) {
return NS_OK;
}
nsCOMPtr<nsIURI> codebaseURI;
doc->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
if (!codebaseURI) {
return NS_OK;
nsCOMPtr<nsPIDOMWindow> window;
if (mWindow && mWindow->GetDocShell()) {
window = mWindow;
nsIDocument* doc = mWindow->GetExtantDoc();
if (doc) {
doc->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
}
}
nsCOMPtr<nsISiteSpecificUserAgent> siteSpecificUA =
do_GetService("@mozilla.org/dom/site-specific-user-agent;1");
NS_ENSURE_TRUE(siteSpecificUA, NS_OK);
return siteSpecificUA->GetUserAgentForURIAndWindow(codebaseURI, mWindow,
aUserAgent);
return GetUserAgent(window, codebaseURI, nsContentUtils::IsCallerChrome(),
aUserAgent);
}
NS_IMETHODIMP
@ -2395,6 +2384,8 @@ Navigator::GetPlatform(nsAString& aPlatform, bool aUsePrefOverriddenValue)
/* static */ nsresult
Navigator::GetAppVersion(nsAString& aAppVersion, bool aUsePrefOverriddenValue)
{
MOZ_ASSERT(NS_IsMainThread());
if (aUsePrefOverriddenValue && !nsContentUtils::IsCallerChrome()) {
const nsAdoptingString& override =
mozilla::Preferences::GetString("general.appversion.override");
@ -2430,6 +2421,8 @@ Navigator::GetAppVersion(nsAString& aAppVersion, bool aUsePrefOverriddenValue)
/* static */ void
Navigator::AppName(nsAString& aAppName, bool aUsePrefOverriddenValue)
{
MOZ_ASSERT(NS_IsMainThread());
if (aUsePrefOverriddenValue && !nsContentUtils::IsCallerChrome()) {
const nsAdoptingString& override =
mozilla::Preferences::GetString("general.appname.override");
@ -2443,21 +2436,52 @@ Navigator::AppName(nsAString& aAppName, bool aUsePrefOverriddenValue)
aAppName.AssignLiteral("Netscape");
}
} // namespace dom
} // namespace mozilla
nsresult
NS_GetNavigatorUserAgent(nsAString& aUserAgent)
Navigator::GetUserAgent(nsPIDOMWindow* aWindow, nsIURI* aURI,
bool aIsCallerChrome,
nsAString& aUserAgent)
{
nsresult rv;
MOZ_ASSERT(NS_IsMainThread());
if (!aIsCallerChrome) {
const nsAdoptingString& override =
mozilla::Preferences::GetString("general.useragent.override");
if (override) {
aUserAgent = override;
return NS_OK;
}
}
nsresult rv;
nsCOMPtr<nsIHttpProtocolHandler>
service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsAutoCString ua;
rv = service->GetUserAgent(ua);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
CopyASCIItoUTF16(ua, aUserAgent);
return rv;
if (!aWindow || !aURI) {
return NS_OK;
}
MOZ_ASSERT(aWindow->GetDocShell());
nsCOMPtr<nsISiteSpecificUserAgent> siteSpecificUA =
do_GetService("@mozilla.org/dom/site-specific-user-agent;1");
if (!siteSpecificUA) {
return NS_OK;
}
return siteSpecificUA->GetUserAgentForURIAndWindow(aURI, aWindow, aUserAgent);
}
} // namespace dom
} // namespace mozilla

View File

@ -27,6 +27,7 @@ class nsDOMCameraManager;
class nsDOMDeviceStorage;
class nsIDOMBlob;
class nsIPrincipal;
class nsIURI;
namespace mozilla {
namespace dom {
@ -167,6 +168,11 @@ public:
static nsresult GetAppVersion(nsAString& aAppVersion,
bool aUsePrefOverriddenValue);
static nsresult GetUserAgent(nsPIDOMWindow* aWindow,
nsIURI* aURI,
bool aIsCallerChrome,
nsAString& aUserAgent);
already_AddRefed<Promise> GetDataStores(const nsAString& aName,
const nsAString& aOwner,
ErrorResult& aRv);
@ -356,6 +362,4 @@ private:
} // namespace dom
} // namespace mozilla
nsresult NS_GetNavigatorUserAgent(nsAString& aUserAgent);
#endif // mozilla_dom_Navigator_h

View File

@ -11511,6 +11511,7 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic,
// very likely situation where an event handler will try to read its value.
if (mNavigator) {
NavigatorBinding::ClearCachedLanguageValue(mNavigator);
NavigatorBinding::ClearCachedLanguagesValue(mNavigator);
}

View File

@ -87,3 +87,4 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop spec
[test_window_indexing.html]
[test_window_named_frame_enumeration.html]
[test_writable-replaceable.html]
[test_navigatorPrefOverride.html]

View File

@ -0,0 +1,51 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Test for navigator property override</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script class="testbody" type="text/javascript">
ok(navigator.appName, "This is used just to populate the cache");
ok(navigator.appVersion, "This is used just to populate the cache");
ok(navigator.platform, "This is used just to populate the cache");
ok(navigator.userAgent, "This is used just to populate the cache");
SpecialPowers.pushPrefEnv({"set": [
["general.appname.override", "appName overridden"],
["general.appversion.override", "appVersion overridden"],
["general.platform.override", "platform overridden"],
["general.useragent.override", "userAgent overridden"],
]},
function() {
var ifr = document.createElement('IFRAME');
ifr.src = "about:blank";
ifr.addEventListener('load', function() {
var nav = ifr.contentWindow.navigator;
isnot(navigator.appName, nav.appName, "appName should match");
isnot(navigator.appVersion, nav.appVersion, "appVersion should match");
isnot(navigator.platform, nav.platform, "platform should match");
isnot(navigator.userAgent, nav.userAgent, "userAgent should match");
SimpleTest.finish();
}, false);
document.getElementById('content').appendChild(ifr);
}
);
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

View File

@ -33,17 +33,17 @@ Navigator implements NavigatorFeatures;
[NoInterfaceObject, Exposed=(Window,Worker)]
interface NavigatorID {
// WebKit/Blink/Trident/Presto support this (hardcoded "Mozilla").
[Constant]
[Constant, Cached]
readonly attribute DOMString appCodeName; // constant "Mozilla"
[Constant]
[Constant, Cached]
readonly attribute DOMString appName;
[Constant]
[Constant, Cached]
readonly attribute DOMString appVersion;
[Constant]
[Constant, Cached]
readonly attribute DOMString platform;
[Constant]
[Constant, Cached]
readonly attribute DOMString userAgent;
[Constant]
[Constant, Cached]
readonly attribute DOMString product; // constant "Gecko"
// Everyone but WebKit/Blink supports this. See bug 679971.
@ -52,6 +52,11 @@ interface NavigatorID {
[NoInterfaceObject, Exposed=(Window,Worker)]
interface NavigatorLanguage {
// These 2 values are cached. They are updated when pref
// intl.accept_languages is changed.
[Pure, Cached]
readonly attribute DOMString? language;
[Pure, Cached, Frozen]
readonly attribute sequence<DOMString> languages;

View File

@ -15,6 +15,8 @@
#include "nsProxyRelease.h"
#include "RuntimeService.h"
#include "nsIDocument.h"
#include "WorkerPrivate.h"
#include "WorkerRunnable.h"
#include "WorkerScope.h"
@ -325,4 +327,59 @@ WorkerNavigator::GetPlatform(nsString& aPlatform) const
}
}
namespace {
class GetUserAgentRunnable MOZ_FINAL : public WorkerMainThreadRunnable
{
nsString& mUA;
public:
GetUserAgentRunnable(WorkerPrivate* aWorkerPrivate, nsString& aUA)
: WorkerMainThreadRunnable(aWorkerPrivate)
, mUA(aUA)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
}
virtual bool MainThreadRun() MOZ_OVERRIDE
{
AssertIsOnMainThread();
nsCOMPtr<nsPIDOMWindow> window = mWorkerPrivate->GetWindow();
nsCOMPtr<nsIURI> uri;
if (window && window->GetDocShell()) {
nsIDocument* doc = window->GetExtantDoc();
if (doc) {
doc->NodePrincipal()->GetURI(getter_AddRefs(uri));
}
}
bool isCallerChrome = mWorkerPrivate->UsesSystemPrincipal();
nsresult rv = dom::Navigator::GetUserAgent(window, uri,
isCallerChrome, mUA);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to retrieve user-agent from the worker thread.");
}
return true;
}
};
} // anonymous namespace
void
WorkerNavigator::GetUserAgent(nsString& aUserAgent) const
{
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
nsRefPtr<GetUserAgentRunnable> runnable =
new GetUserAgentRunnable(workerPrivate, aUserAgent);
if (!runnable->Dispatch(workerPrivate->GetJSContext())) {
JS_ReportPendingException(workerPrivate->GetJSContext());
}
}
END_WORKERS_NAMESPACE

View File

@ -93,10 +93,7 @@ public:
aLanguages = mProperties.mLanguages;
}
void GetUserAgent(nsString& aUserAgent) const
{
aUserAgent = mProperties.mUserAgent;
}
void GetUserAgent(nsString& aUserAgent) const;
bool OnLine() const
{

View File

@ -1515,8 +1515,7 @@ RuntimeService::RegisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
if (NS_FAILED(Navigator::GetAppVersion(mNavigatorProperties.mAppVersion,
false /* aUsePrefOverriddenValue */)) ||
NS_FAILED(Navigator::GetPlatform(mNavigatorProperties.mPlatform,
false /* aUsePrefOverriddenValue */)) ||
NS_FAILED(NS_GetNavigatorUserAgent(mNavigatorProperties.mUserAgent))) {
false /* aUsePrefOverriddenValue */))) {
JS_ReportError(aCx, "Failed to load navigator strings!");
UnregisterWorker(aCx, aWorkerPrivate);
return false;

View File

@ -110,7 +110,6 @@ public:
nsString mAppVersionOverridden;
nsString mPlatform;
nsString mPlatformOverridden;
nsString mUserAgent;
nsTArray<nsString> mLanguages;
};

View File

@ -19,13 +19,21 @@
var worker = new Worker("bug1062920_worker.js");
worker.onmessage = function(event) {
is(event.data.appCodeName, navigator.appCodeName, "appCodeName should match");
is(event.data.appName, navigator.appName, "appName should match");
is(event.data.appVersion, navigator.appVersion, "appVersion should match");
is(event.data.platform, navigator.platform, "platform should match");
is(event.data.userAgent, navigator.userAgent, "userAgent should match");
is(event.data.product, navigator.product, "product should match");
runTests();
var ifr = document.createElement('IFRAME');
ifr.src = "about:blank";
ifr.addEventListener('load', function() {
var nav = ifr.contentWindow.navigator;
is(event.data.appCodeName, nav.appCodeName, "appCodeName should match");
is(event.data.appName, nav.appName, "appName should match");
is(event.data.appVersion, nav.appVersion, "appVersion should match");
is(event.data.platform, nav.platform, "platform should match");
is(event.data.userAgent, nav.userAgent, "userAgent should match");
is(event.data.product, nav.product, "product should match");
runTests();
}, false);
document.getElementById('content').appendChild(ifr);
};
}
@ -33,7 +41,8 @@
SpecialPowers.pushPrefEnv({"set": [
["general.appname.override", "appName overridden"],
["general.appversion.override", "appVersion overridden"],
["general.platform.override", "platform overridden"]
["general.platform.override", "platform overridden"],
["general.useragent.override", "userAgent overridden"]
]}, checkValues);
}

View File

@ -35,13 +35,14 @@
SpecialPowers.pushPrefEnv({"set": [
["general.appname.override", "appName overridden"],
["general.appversion.override", "appVersion overridden"],
["general.platform.override", "platform overridden"]
["general.platform.override", "platform overridden"],
["general.useragent.override", "userAgent overridden"]
]}, checkValues);
}
var tests = [
checkValues,
replaceAndCheckValues
replaceAndCheckValues,
checkValues
];
function runTests() {

View File

@ -68,33 +68,53 @@ function testUA(options, callback) {
SpecialPowers.pushPrefEnv({
set: [[PREF_OVERRIDES_BRANCH + domain, override]],
}, function () {
// check that the UA has changed after pref change
if (overrideNavigator) {
is(navigator.userAgent, expected,
'Navigator UA not overridden at step ' + (++step));
} else {
is(navigator.userAgent, DEFAULT_UA,
'Navigator UA should not be overridden at step ' + (++step));
}
test_hosts.forEach(function (test_host) {
is(getUA(test_host), expected,
'Header UA not overridden at step ' + (++step));
});
// clear the override pref to undo overriding the UA
SpecialPowers.pushPrefEnv({
clear: [[PREF_OVERRIDES_BRANCH + domain]],
}, function () {
// check that the UA has changed back
var ifr = document.createElement('IFRAME');
ifr.src = "about:blank";
ifr.addEventListener('load', function() {
var nav = ifr.contentWindow.navigator;
// check that the UA has changed after pref change
if (overrideNavigator) {
is(navigator.userAgent, navigator_ua,
'Navigator UA not restored at step ' + (++step));
is(nav.userAgent, expected,
'Navigator UA not overridden at step ' + (++step));
} else {
is(nav.userAgent, DEFAULT_UA,
'Navigator UA should not be overridden at step ' + (++step));
}
test_hosts.forEach(function (test_host, i) {
is(getUA(test_host), test_ua[i],
'Header UA not restored at step ' + (++step));
test_hosts.forEach(function (test_host) {
is(getUA(test_host), expected,
'Header UA not overridden at step ' + (++step));
});
callback();
});
// clear the override pref to undo overriding the UA
SpecialPowers.pushPrefEnv({
clear: [[PREF_OVERRIDES_BRANCH + domain]],
}, function () {
var ifr = document.createElement('IFRAME');
ifr.src = "about:blank";
ifr.addEventListener('load', function() {
var nav = ifr.contentWindow.navigator;
// check that the UA has changed back
if (overrideNavigator) {
is(nav.userAgent, navigator_ua,
'Navigator UA not restored at step ' + (++step));
}
test_hosts.forEach(function (test_host, i) {
is(getUA(test_host), test_ua[i],
'Header UA not restored at step ' + (++step));
});
callback();
});
document.getElementById('content').appendChild(ifr);
});
}, false);
document.getElementById('content').appendChild(ifr);
});
}

View File

@ -121,22 +121,31 @@ function testDownload(callback) {
[PREF_UPDATES_INTERVAL, 1] // 1 second interval
]
}, function waitForUpdate() setTimeout(function () {
if (navigator.userAgent !== UA_OVERRIDE) {
waitForUpdate();
return;
}
info('Overrode navigator UA');
is(getUA(location.origin), UA_OVERRIDE, 'Header UA not overridden');
var ifr = document.createElement('IFRAME');
ifr.src = "about:blank";
var updateTime = parseInt(getUA('http://example.org'));
ok(startTime <= updateTime, 'Update was before start time');
ok(updateTime <= Date.now(), 'Update was after present time');
ifr.addEventListener('load', function() {
var nav = ifr.contentWindow.navigator;
if (nav.userAgent !== UA_OVERRIDE) {
waitForUpdate();
return;
}
OVERRIDES.forEach(function (val) {
val.expected && is(getUA(val.host), val.expected,
'Incorrect URL parameter: ' + val.override);
});
callback();
info('Overrode navigator UA');
is(getUA(location.origin), UA_OVERRIDE, 'Header UA not overridden');
var updateTime = parseInt(getUA('http://example.org'));
ok(startTime <= updateTime, 'Update was before start time');
ok(updateTime <= Date.now(), 'Update was after present time');
OVERRIDES.forEach(function (val) {
val.expected && is(getUA(val.host), val.expected,
'Incorrect URL parameter: ' + val.override);
});
callback();
}, false);
document.getElementById('content').appendChild(ifr);
}, 100));
}
@ -149,12 +158,19 @@ function testBadUpdate(callback) {
[PREF_UPDATES_INTERVAL, 1] // 1 second interval
]
}, function () setTimeout(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();
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();
}, false);
document.getElementById('content').appendChild(ifr);
}, 5000));
}
@ -179,13 +195,21 @@ function testProfileLoad(callback) {
// UserAgentUpdates.jsm and load saved file
UAO.init();
(function waitForLoad() {
if (navigator.userAgent !== UA_ALT_OVERRIDE) {
setTimeout(waitForLoad, 100);
return;
}
is(getUA(location.origin), UA_ALT_OVERRIDE, 'Did not apply saved override');
saveFilePreviousSize = file.fileSize;
callback();
var ifr = document.createElement('IFRAME');
ifr.src = "about:blank";
ifr.addEventListener('load', function() {
var nav = ifr.contentWindow.navigator;
if (nav.userAgent !== UA_ALT_OVERRIDE) {
setTimeout(waitForLoad, 100);
return;
}
is(getUA(location.origin), UA_ALT_OVERRIDE, 'Did not apply saved override');
saveFilePreviousSize = file.fileSize;
callback();
}, false);
document.getElementById('content').appendChild(ifr);
})();
});
},
@ -269,4 +293,3 @@ SpecialPowers.pushPrefEnv({
</pre>
</body>
</html>