Bug 1137681 - Per-tab user agent emulation. r=bz

This commit is contained in:
Tim Nguyen 2015-07-21 08:42:00 +02:00
parent bfff95ae39
commit 6f32982b69
8 changed files with 125 additions and 7 deletions

View File

@ -28,6 +28,7 @@
#include "mozilla/StartupTimeline.h"
#include "mozilla/Telemetry.h"
#include "mozilla/unused.h"
#include "Navigator.h"
#include "URIUtils.h"
#include "nsIContent.h"
@ -3128,6 +3129,38 @@ nsDocShell::NameEquals(const char16_t* aName, bool* aResult)
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::GetCustomUserAgent(nsAString& aCustomUserAgent)
{
aCustomUserAgent = mCustomUserAgent;
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::SetCustomUserAgent(const nsAString& aCustomUserAgent)
{
mCustomUserAgent = aCustomUserAgent;
RefPtr<nsGlobalWindow> win = mScriptGlobal ?
mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
if (win) {
ErrorResult ignored;
Navigator* navigator = win->GetNavigator(ignored);
ignored.SuppressException();
if (navigator) {
navigator->ClearUserAgentCache();
}
}
uint32_t childCount = mChildList.Length();
for (uint32_t i = 0; i < childCount; ++i) {
nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(ChildAt(i));
if (childShell) {
childShell->SetCustomUserAgent(aCustomUserAgent);
}
}
return NS_OK;
}
/* virtual */ int32_t
nsDocShell::ItemType()
{
@ -3255,6 +3288,7 @@ nsDocShell::SetDocLoaderParent(nsDocLoader* aParent)
// If parent is another docshell, we inherit all their flags for
// allowing plugins, scripting etc.
bool value;
nsString customUserAgent;
nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
if (parentAsDocShell) {
if (mAllowPlugins && NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value))) {
@ -3284,6 +3318,10 @@ nsDocShell::SetDocLoaderParent(nsDocLoader* aParent)
if (parentAsDocShell->GetIsPrerendered()) {
SetIsPrerendered(true);
}
if (NS_SUCCEEDED(parentAsDocShell->GetCustomUserAgent(customUserAgent)) &&
!customUserAgent.IsEmpty()) {
SetCustomUserAgent(customUserAgent);
}
if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
value = false;
}

View File

@ -793,6 +793,7 @@ protected:
nsIntRect mBounds;
nsString mName;
nsString mTitle;
nsString mCustomUserAgent;
/**
* Content-Type Hint of the most-recently initiated load. Used for

View File

@ -43,7 +43,7 @@ interface nsITabParent;
typedef unsigned long nsLoadFlags;
[scriptable, builtinclass, uuid(63adb599-6dc9-4746-972e-c22e9018020b)]
[scriptable, builtinclass, uuid(bc3524bd-023c-4fc8-ace1-472bc999fb12)]
interface nsIDocShell : nsIDocShellTreeItem
{
/**
@ -237,6 +237,11 @@ interface nsIDocShell : nsIDocShellTreeItem
*/
attribute nsIDOMEventTarget chromeEventHandler;
/**
* This allows chrome to set a custom User agent on a specific docshell
*/
attribute DOMString customUserAgent;
/**
* Whether to allow plugin execution
*/
@ -250,7 +255,7 @@ interface nsIDocShell : nsIDocShellTreeItem
/**
* Attribute stating if refresh based redirects can be allowed
*/
attribute boolean allowMetaRedirects;
attribute boolean allowMetaRedirects;
/**
* Attribute stating if it should allow subframes (framesets/iframes) or not

View File

@ -84,6 +84,7 @@ skip-if = e10s # Bug 1220927 - Test tries to do addSHistoryListener on content.
[browser_multiple_pushState.js]
[browser_onbeforeunload_navigation.js]
[browser_search_notification.js]
[browser_ua_emulation.js]
[browser_timelineMarkers-01.js]
[browser_timelineMarkers-02.js]
[browser_timelineMarkers-03.js]

View File

@ -0,0 +1,52 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the docShell UA emulation works
add_task(function*() {
yield openUrl("data:text/html;charset=utf-8,<iframe id='test-iframe'></iframe>");
let docshell = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
is(docshell.customUserAgent, "", "There should initially be no customUserAgent");
docshell.customUserAgent = "foo";
is(content.navigator.userAgent, "foo", "The user agent should be changed to foo");
let frameWin = content.document.querySelector("#test-iframe").contentWindow;
is(frameWin.navigator.userAgent, "foo", "The UA should be passed on to frames.");
let newFrame = content.document.createElement("iframe");
content.document.body.appendChild(newFrame);
let newFrameWin = newFrame.contentWindow;
is(newFrameWin.navigator.userAgent, "foo", "Newly created frames should use the new UA");
newFrameWin.location.reload();
yield waitForEvent(newFrameWin, "load");
is(newFrameWin.navigator.userAgent, "foo", "New UA should persist across reloads");
gBrowser.removeCurrentTab();
});
function waitForEvent(target, event) {
return new Promise(function(resolve) {
target.addEventListener(event, resolve);
});
}
function openUrl(url) {
return new Promise(function(resolve, reject) {
window.focus();
let tab = window.gBrowser.selectedTab = window.gBrowser.addTab(url);
let linkedBrowser = tab.linkedBrowser;
linkedBrowser.addEventListener("load", function onload() {
linkedBrowser.removeEventListener("load", onload, true);
resolve(tab);
}, true);
});
}

View File

@ -372,11 +372,22 @@ Navigator::GetUserAgent(nsAString& aUserAgent)
nsCOMPtr<nsIURI> codebaseURI;
nsCOMPtr<nsPIDOMWindow> window;
if (mWindow && mWindow->GetDocShell()) {
if (mWindow) {
window = mWindow;
nsIDocument* doc = mWindow->GetExtantDoc();
if (doc) {
doc->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
nsIDocShell* docshell = window->GetDocShell();
nsString customUserAgent;
if (docshell) {
docshell->GetCustomUserAgent(customUserAgent);
if (!customUserAgent.IsEmpty()) {
aUserAgent = customUserAgent;
return NS_OK;
}
nsIDocument* doc = mWindow->GetExtantDoc();
if (doc) {
doc->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
}
}
}
@ -2741,6 +2752,12 @@ Navigator::AppName(nsAString& aAppName, bool aUsePrefOverriddenValue)
aAppName.AssignLiteral("Netscape");
}
void
Navigator::ClearUserAgentCache()
{
NavigatorBinding::ClearCachedUserAgentValue(this);
}
nsresult
Navigator::GetUserAgent(nsPIDOMWindow* aWindow, nsIURI* aURI,
bool aIsCallerChrome,

View File

@ -182,6 +182,10 @@ public:
bool aIsCallerChrome,
nsAString& aUserAgent);
// Clears the user agent cache by calling:
// NavigatorBinding::ClearCachedUserAgentValue(this);
void ClearUserAgentCache();
already_AddRefed<Promise> GetDataStores(const nsAString& aName,
const nsAString& aOwner,
ErrorResult& aRv);

View File

@ -41,7 +41,7 @@ interface NavigatorID {
readonly attribute DOMString appVersion;
[Constant, Cached]
readonly attribute DOMString platform;
[Constant, Cached, Throws=Workers]
[Pure, Cached, Throws=Workers]
readonly attribute DOMString userAgent;
[Constant, Cached]
readonly attribute DOMString product; // constant "Gecko"