Bug 761227 - Support XMLHttpRequestParameters in workers. r=bent

This commit is contained in:
Andrea Marchesini 2012-09-16 20:20:16 -04:00
parent 156073c088
commit 13e87ce3ba
13 changed files with 326 additions and 19 deletions

View File

@ -457,6 +457,10 @@ nsXMLHttpRequest::InitParameters(JSContext* aCx, const jsval* aParams)
void
nsXMLHttpRequest::InitParameters(bool aAnon, bool aSystem)
{
if (!aAnon && !aSystem) {
return;
}
// Check for permissions.
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetOwner());
if (!window || !window->GetDocShell()) {
@ -485,8 +489,7 @@ nsXMLHttpRequest::InitParameters(bool aAnon, bool aSystem)
}
}
mIsAnon = aAnon;
mIsSystem = aSystem;
SetParameters(aAnon, aSystem);
}
void

View File

@ -203,6 +203,12 @@ public:
nsresult InitParameters(JSContext* aCx, const jsval* aParams);
void InitParameters(bool aAnon, bool aSystem);
void SetParameters(bool aAnon, bool aSystem)
{
mIsAnon = aAnon;
mIsSystem = aSystem;
}
NS_DECL_ISUPPORTS_INHERITED
// nsIXMLHttpRequest

View File

@ -5,14 +5,14 @@
<head>
<meta charset="utf-8">
<title>Test for XMLHttpRequest with system privileges</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="runTests();">
<p id="display">
</p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="application/javascript;version=1.8">

View File

@ -497,8 +497,11 @@ public:
mWorkerPrivate->SetPrincipal(channelPrincipal);
// Set Eval and ContentSecurityPolicy
if (parent) {
// XHR Params Allowed
mWorkerPrivate->SetXHRParamsAllowed(parent->XHRParamsAllowed());
// Set Eval and ContentSecurityPolicy
mWorkerPrivate->SetCSP(parent->GetCSP());
mWorkerPrivate->SetEvalAllowed(parent->IsEvalAllowed());
}

View File

@ -15,6 +15,7 @@
#include "nsIDocShell.h"
#include "nsIJSContextStack.h"
#include "nsIMemoryReporter.h"
#include "nsIPermissionManager.h"
#include "nsIScriptError.h"
#include "nsIScriptGlobalObject.h"
#include "nsIScriptSecurityManager.h"
@ -2458,7 +2459,8 @@ WorkerPrivate::WorkerPrivate(JSContext* aCx, JSObject* aObject,
nsCOMPtr<nsIURI>& aBaseURI,
nsCOMPtr<nsIPrincipal>& aPrincipal,
nsCOMPtr<nsIContentSecurityPolicy>& aCSP,
bool aEvalAllowed)
bool aEvalAllowed,
bool aXHRParamsAllowed)
: WorkerPrivateParent<WorkerPrivate>(aCx, aObject, aParent, aParentJSContext,
aScriptURL, aIsChromeWorker, aDomain,
aWindow, aParentScriptContext, aBaseURI,
@ -2466,7 +2468,8 @@ WorkerPrivate::WorkerPrivate(JSContext* aCx, JSObject* aObject,
mJSContext(nullptr), mErrorHandlerRecursionCount(0), mNextTimeoutId(1),
mStatus(Pending), mSuspended(false), mTimerRunning(false),
mRunningExpiredTimeouts(false), mCloseHandlerStarted(false),
mCloseHandlerFinished(false), mMemoryReporterRunning(false)
mCloseHandlerFinished(false), mMemoryReporterRunning(false),
mXHRParamsAllowed(aXHRParamsAllowed)
{
MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate);
}
@ -2493,6 +2496,8 @@ WorkerPrivate::Create(JSContext* aCx, JSObject* aObj, WorkerPrivate* aParent,
JSContext* parentContext;
bool xhrParamsAllowed = false;
if (aParent) {
aParent->AssertIsOnWorkerThread();
@ -2615,6 +2620,8 @@ WorkerPrivate::Create(JSContext* aCx, JSObject* aObj, WorkerPrivate* aParent,
}
}
}
xhrParamsAllowed = CheckXHRParamsAllowed(window);
}
else {
// Not a window
@ -2632,6 +2639,8 @@ WorkerPrivate::Create(JSContext* aCx, JSObject* aObj, WorkerPrivate* aParent,
return nullptr;
}
}
xhrParamsAllowed = true;
}
NS_ASSERTION(principal, "Must have a principal now!");
@ -2663,7 +2672,7 @@ WorkerPrivate::Create(JSContext* aCx, JSObject* aObj, WorkerPrivate* aParent,
nsRefPtr<WorkerPrivate> worker =
new WorkerPrivate(aCx, aObj, aParent, parentContext, scriptURL,
aIsChromeWorker, domain, window, scriptContext, baseURI,
principal, csp, evalAllowed);
principal, csp, evalAllowed, xhrParamsAllowed);
worker->SetIsDOMBinding();
worker->SetWrapper(aObj);
@ -3007,6 +3016,36 @@ WorkerPrivate::ProcessAllControlRunnables()
return result;
}
bool
WorkerPrivate::CheckXHRParamsAllowed(nsPIDOMWindow* aWindow)
{
AssertIsOnMainThread();
NS_ASSERTION(aWindow, "Wrong cannot be null");
if (!aWindow->GetDocShell()) {
return false;
}
nsCOMPtr<nsIDocument> doc = do_QueryInterface(aWindow->GetExtantDocument());
if (!doc) {
return false;
}
nsCOMPtr<nsIPermissionManager> permMgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
if (!permMgr) {
return false;
}
uint32_t permission;
nsresult rv = permMgr->TestPermissionFromPrincipal(doc->NodePrincipal(),
"systemXHR", &permission);
if (NS_FAILED(rv) || permission != nsIPermissionManager::ALLOW_ACTION) {
return false;
}
return true;
}
bool
WorkerPrivate::Dispatch(WorkerRunnable* aEvent, EventQueue* aQueue)
{

View File

@ -570,6 +570,7 @@ class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
bool mCloseHandlerStarted;
bool mCloseHandlerFinished;
bool mMemoryReporterRunning;
bool mXHRParamsAllowed;
#ifdef DEBUG
nsCOMPtr<nsIThread> mThread;
@ -710,6 +711,18 @@ public:
bool
DisableMemoryReporter();
bool
XHRParamsAllowed() const
{
return mXHRParamsAllowed;
}
void
SetXHRParamsAllowed(bool aAllowed)
{
mXHRParamsAllowed = aAllowed;
}
#ifdef JS_GC_ZEAL
void
UpdateGCZealInternal(JSContext* aCx, uint8_t aGCZeal);
@ -751,7 +764,8 @@ private:
nsCOMPtr<nsPIDOMWindow>& aWindow,
nsCOMPtr<nsIScriptContext>& aScriptContext,
nsCOMPtr<nsIURI>& aBaseURI, nsCOMPtr<nsIPrincipal>& aPrincipal,
nsCOMPtr<nsIContentSecurityPolicy>& aCSP, bool aEvalAllowed);
nsCOMPtr<nsIContentSecurityPolicy>& aCSP, bool aEvalAllowed,
bool aXHRParamsAllowed);
static bool
GetContentSecurityPolicy(JSContext *aCx,
@ -811,6 +825,9 @@ private:
bool
ProcessAllControlRunnables();
static bool
CheckXHRParamsAllowed(nsPIDOMWindow* aWindow);
};
WorkerPrivate*

View File

@ -90,6 +90,10 @@ public:
WorkerPrivate* mWorkerPrivate;
XMLHttpRequest* mXMLHttpRequestPrivate;
// XHR Params:
bool mMozAnon;
bool mMozSystem;
// Only touched on the main thread.
nsRefPtr<nsXMLHttpRequest> mXHR;
nsCOMPtr<nsIXMLHttpRequestUpload> mXHRUpload;
@ -121,8 +125,9 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMEVENTLISTENER
Proxy(XMLHttpRequest* aXHRPrivate)
Proxy(XMLHttpRequest* aXHRPrivate, bool aMozAnon, bool aMozSystem)
: mWorkerPrivate(nullptr), mXMLHttpRequestPrivate(aXHRPrivate),
mMozAnon(aMozAnon), mMozSystem(aMozSystem),
mInnerEventStreamId(0), mInnerChannelId(0), mOutstandingSendCount(0),
mOuterEventStreamId(0), mOuterChannelId(0), mLastLoaded(0), mLastTotal(0),
mLastUploadLoaded(0), mLastUploadTotal(0), mIsSyncXHR(false),
@ -158,6 +163,8 @@ public:
return false;
}
mXHR->SetParameters(mMozAnon, mMozSystem);
if (NS_FAILED(mXHR->GetUpload(getter_AddRefs(mXHRUpload)))) {
mXHR = nullptr;
return false;
@ -1434,7 +1441,7 @@ XMLHttpRequest::XMLHttpRequest(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
mWorkerPrivate(aWorkerPrivate),
mResponseType(XMLHttpRequestResponseTypeValues::Text), mTimeout(0),
mJSObjectRooted(false), mMultipart(false), mBackgroundRequest(false),
mWithCredentials(false), mCanceled(false)
mWithCredentials(false), mCanceled(false), mMozAnon(false), mMozSystem(false)
{
mWorkerPrivate->AssertIsOnWorkerThread();
}
@ -1479,7 +1486,10 @@ XMLHttpRequest::Constructor(JSContext* aCx,
return NULL;
}
// TODO: process aParams. See bug 761227
if (workerPrivate->XHRParamsAllowed()) {
xhr->mMozAnon = aParams.mozAnon;
xhr->mMozSystem = aParams.mozSystem;
}
xhr->mJSObject = xhr->GetJSObject();
return xhr;
@ -1744,7 +1754,7 @@ XMLHttpRequest::Open(const nsAString& aMethod, const nsAString& aUrl,
}
}
else {
mProxy = new Proxy(this);
mProxy = new Proxy(this, mMozAnon, mMozSystem);
}
mProxy->mOuterEventStreamId++;

View File

@ -59,6 +59,9 @@ private:
bool mWithCredentials;
bool mCanceled;
bool mMozAnon;
bool mMozSystem;
protected:
XMLHttpRequest(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
virtual ~XMLHttpRequest();
@ -262,14 +265,14 @@ public:
mStateData.mResponse = JSVAL_NULL;
}
bool MozAnon() {
// TODO: bug 761227
return false;
bool MozAnon() const
{
return mMozAnon;
}
bool MozSystem() {
// TODO: bug 761227
return false;
bool MozSystem() const
{
return mMozSystem;
}
private:

View File

@ -91,6 +91,10 @@ MOCHITEST_FILES = \
test_xhr_implicit_cancel.html \
xhr_implicit_cancel_worker.js \
test_xhr_timeout.html \
test_xhr_parameters.html \
test_xhr_parameters.js \
test_xhr_system.html \
test_xhr_system.js \
test_blobConstructor.html \
test_csp.html \
test_csp.js \

View File

@ -0,0 +1,63 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test for XMLHttpRequest with system privileges</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">
<script class="testbody" type="application/javascript;version=1.8">
function message(event) {
if (event.data.test == 'ok')
ok(event.data.a, event.data.event);
else if(event.data.test == 'is')
is(event.data.a, event.data.b, event.data.event);
else if(event.data.test == 'finish') {
run();
}
};
function test1() {
var worker = new Worker("test_xhr_parameters.js");
worker.onmessage = message;
// Run the tests once without API privileges...
worker.postMessage(false);
}
function test2() {
// ...and once with privileges.
SpecialPowers.addPermission("systemXHR", true, document);
var worker = new Worker("test_xhr_parameters.js");
worker.onmessage = message;
worker.postMessage(true);
}
var tests = [ test1, test2 ];
function run() {
if (!tests.length) {
SpecialPowers.removePermission("systemXHR", document);
SimpleTest.finish();
return;
}
var func = tests.shift();
func();
}
SimpleTest.waitForExplicitFinish();
run();
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,75 @@
function ok(what, msg) {
postMessage({ event: msg, test: 'ok', a: what });
}
function is(a, b, msg) {
postMessage({ event: msg, test: 'is', a: a, b: b });
}
// This is a copy of content/base/test/test_XHR_parameters.js
var validParameters = [
undefined,
null,
{},
{mozSystem: ""},
{mozSystem: 0},
{mozAnon: 1},
{mozAnon: []},
{get mozAnon() { return true; }},
0,
7,
Math.PI,
"string",
true,
false,
];
var invalidParameters = [
{get mozSystem() { throw "Bla"; } },
];
function testParameters(havePrivileges) {
function testValidParameter(value) {
var xhr;
try {
xhr = new XMLHttpRequest(value);
} catch (ex) {
ok(false, "Got unexpected exception: " + ex);
return;
}
ok(!!xhr, "passed " + JSON.stringify(value));
// If the page doesnt have privileges to create a system or anon XHR,
// these flags will always be false no matter what is passed.
var expectedAnon = false;
var expectedSystem = false;
if (havePrivileges) {
expectedAnon = Boolean(value && value.mozAnon);
expectedSystem = Boolean(value && value.mozSystem);
}
is(xhr.mozAnon, expectedAnon, "testing mozAnon");
is(xhr.mozSystem, expectedSystem, "testing mozSystem");
}
function testInvalidParameter(value) {
try {
new XMLHttpRequest(value);
ok(false, "invalid parameter did not cause exception: " +
JSON.stringify(value));
} catch (ex) {
ok(true, "invalid parameter raised exception as expected: " +
JSON.stringify(ex));
}
}
validParameters.forEach(testValidParameter);
invalidParameters.forEach(testInvalidParameter);
}
self.onmessage = function onmessage(event) {
testParameters(event.data);
postMessage({test: "finish"});
};

View File

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test for XMLHttpRequest with system privileges</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">
<script class="testbody" type="application/javascript;version=1.8">
function message(event) {
if (event.data.test == 'ok')
ok(event.data.a, event.data.event);
else if(event.data.test == 'is')
is(event.data.a, event.data.b, event.data.event);
else if(event.data.test == 'finish') {
run();
}
};
function test1() {
// ...and once with privileges.
SpecialPowers.addPermission("systemXHR", true, document);
var worker = new Worker("test_xhr_system.js");
worker.onmessage = message;
worker.postMessage(true);
}
var tests = [ test1 ];
function run() {
if (!tests.length) {
SpecialPowers.removePermission("systemXHR", document);
SimpleTest.finish();
return;
}
var func = tests.shift();
func();
}
SimpleTest.waitForExplicitFinish();
run();
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,30 @@
function ok(what, msg) {
postMessage({ event: msg, test: 'ok', a: what });
}
function is(a, b, msg) {
postMessage({ event: msg, test: 'is', a: a, b: b });
}
self.onmessage = function onmessage(event) {
// An XHR with system privileges will be able to do cross-site calls.
const TEST_URL = "http://example.com/tests/content/base/test/test_XHR_system.html";
is(location.hostname, "mochi.test", "hostname");
var xhr = new XMLHttpRequest({mozSystem: true});
is(xhr.mozSystem, true, ".mozSystem == true");
xhr.open("GET", TEST_URL);
xhr.onload = function onload() {
is(xhr.status, 200);
ok(xhr.responseText != null);
ok(xhr.responseText.length);
postMessage({test: "finish"});
};
xhr.onerror = function onerror() {
ok(false, "Got an error event!");
postMessage({test: "finish"});
}
xhr.send();
};