Bug 1218433 - Use AsyncOpen2 in dom/workers/ScriptLoader.cpp - part 1, r=sicking

This commit is contained in:
Andrea Marchesini 2015-11-11 16:59:46 +00:00
parent 1649d070dd
commit 2714d3a26a
7 changed files with 164 additions and 172 deletions

View File

@ -42,14 +42,9 @@
// test loading with relative url - this should fail since we are
// sandboxed and have a null principal
try {
var worker_js = new Worker('file_iframe_sandbox_worker.js');
} catch (e) {
ok(e.name === "SecurityError", "a worker in a sandboxed document should throw when loading from a relative URI");
}
var worker_js = new Worker('file_iframe_sandbox_worker.js');
worker_js.onerror = function(error) {
ok(false, "a worker in a sandboxed document should not tell the load error via error event");
ok(true, "a worker in a sandboxed document should tell the load error via error event");
}
worker_js.addEventListener('message', function(event) {

View File

@ -4,7 +4,7 @@
<title>frame for storage prevented test</title>
<script type="text/javascript" src="https://example.com/tests/dom/tests/mochitest/general/storagePermissionsUtils.js"></script>
<script type="text/javascript">
<script type="text/javascript;version=1.7">
task(function* () {
// We shouldn't be able to access storage
@ -13,12 +13,24 @@
// This hash of the URI is set to #nullprincipal by the test if the current page has a null principal,
// and thus attempting to create a dedicated worker will throw
if (location.hash == "#nullprincipal") {
try {
new Worker("workerStoragePrevented.js");
ok(false, "Running workers should not have been allowed");
} catch (e) {
ok(true, "Running workers was prevented");
function createWorker() {
return new Promise((resolve, reject) => {
var w;
try {
w = new Worker("workerStoragePrevented.js");
} catch (e) {
ok(true, "Running workers was prevented");
resolve();
}
w.onerror = function() {
ok(true, "Running workers was prevented");
resolve();
}
});
}
yield createWorker();
return;
}

View File

@ -122,46 +122,32 @@ ChannelFromScriptURL(nsIPrincipal* principal,
return NS_ERROR_DOM_SYNTAX_ERR;
}
int16_t shouldLoad = nsIContentPolicy::ACCEPT;
rv = NS_CheckContentLoadPolicy(aContentPolicyType, uri,
principal, parentDoc,
NS_LITERAL_CSTRING("text/javascript"),
nullptr, &shouldLoad,
nsContentUtils::GetContentPolicy(),
secMan);
if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) {
return rv = NS_ERROR_CONTENT_BLOCKED;
}
return rv = NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
}
aLoadFlags |= nsIChannel::LOAD_CLASSIFY_URI;
uint32_t secFlags = aIsMainScript ? nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED
: nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
if (aWorkerScriptType == DebuggerScript) {
bool isChrome = false;
NS_ENSURE_SUCCESS(uri->SchemeIs("chrome", &isChrome),
NS_ERROR_DOM_SECURITY_ERR);
// A DebuggerScript needs to be a local resource like chrome: or resource:
bool isUIResource = false;
rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_UI_RESOURCE,
&isUIResource);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
bool isResource = false;
NS_ENSURE_SUCCESS(uri->SchemeIs("resource", &isResource),
NS_ERROR_DOM_SECURITY_ERR);
if (!isChrome && !isResource) {
if (!isUIResource) {
return NS_ERROR_DOM_SECURITY_ERR;
}
} else if (aIsMainScript) {
// We pass true as the 3rd argument to checkMayLoad here.
// This allows workers in sandboxed documents to load data URLs
// (and other URLs that inherit their principal from their
// creator.)
rv = principal->CheckMayLoad(uri, false, true);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
}
else {
rv = secMan->CheckLoadURIWithPrincipal(principal, uri, 0);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
secFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
}
aLoadFlags |= nsIChannel::LOAD_CLASSIFY_URI;
// Note: this is for backwards compatibility and goes against spec.
// We should find a better solution.
bool isData = false;
if (aIsMainScript && NS_SUCCEEDED(uri->SchemeIs("data", &isData)) && isData) {
secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL;
}
nsCOMPtr<nsIChannel> channel;
// If we have the document, use it
@ -169,7 +155,7 @@ ChannelFromScriptURL(nsIPrincipal* principal,
rv = NS_NewChannel(getter_AddRefs(channel),
uri,
parentDoc,
nsILoadInfo::SEC_NORMAL,
secFlags,
aContentPolicyType,
loadGroup,
nullptr, // aCallbacks
@ -184,7 +170,7 @@ ChannelFromScriptURL(nsIPrincipal* principal,
rv = NS_NewChannel(getter_AddRefs(channel),
uri,
principal,
nsILoadInfo::SEC_NORMAL,
secFlags,
aContentPolicyType,
loadGroup,
nullptr, // aCallbacks
@ -486,14 +472,51 @@ private:
NS_IMPL_ISUPPORTS0(CachePromiseHandler)
class ScriptLoaderRunnable final : public WorkerFeature,
public nsIRunnable,
public nsIStreamLoaderObserver,
public nsIRequestObserver
class LoaderListener final : public nsIStreamLoaderObserver
, public nsIRequestObserver
{
public:
NS_DECL_ISUPPORTS
LoaderListener(ScriptLoaderRunnable* aRunnable, uint32_t aIndex)
: mRunnable(aRunnable)
, mIndex(aIndex)
{
MOZ_ASSERT(mRunnable);
}
NS_IMETHOD
OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
nsresult aStatus, uint32_t aStringLen,
const uint8_t* aString) override;
NS_IMETHOD
OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) override;
NS_IMETHOD
OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
nsresult aStatusCode) override
{
// Nothing to do here!
return NS_OK;
}
private:
~LoaderListener() {}
RefPtr<ScriptLoaderRunnable> mRunnable;
uint32_t mIndex;
};
NS_IMPL_ISUPPORTS(LoaderListener, nsIStreamLoaderObserver, nsIRequestObserver)
class ScriptLoaderRunnable final : public WorkerFeature
, public nsIRunnable
{
friend class ScriptExecutorRunnable;
friend class CachePromiseHandler;
friend class CacheScriptLoader;
friend class LoaderListener;
WorkerPrivate* mWorkerPrivate;
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
@ -571,45 +594,27 @@ private:
}
}
NS_IMETHOD
OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
nsresult
OnStreamComplete(nsIStreamLoader* aLoader, uint32_t aIndex,
nsresult aStatus, uint32_t aStringLen,
const uint8_t* aString) override
const uint8_t* aString)
{
AssertIsOnMainThread();
MOZ_ASSERT(aIndex < mLoadInfos.Length());
nsCOMPtr<nsISupportsPRUint32> indexSupports(do_QueryInterface(aContext));
NS_ASSERTION(indexSupports, "This should never fail!");
uint32_t index = UINT32_MAX;
if (NS_FAILED(indexSupports->GetData(&index)) ||
index >= mLoadInfos.Length()) {
NS_ERROR("Bad index!");
}
ScriptLoadInfo& loadInfo = mLoadInfos[index];
nsresult rv = OnStreamCompleteInternal(aLoader, aContext, aStatus,
aStringLen, aString, loadInfo);
LoadingFinished(index, rv);
nsresult rv = OnStreamCompleteInternal(aLoader, aStatus, aStringLen,
aString, mLoadInfos[aIndex]);
LoadingFinished(aIndex, rv);
return NS_OK;
}
NS_IMETHOD
OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) override
nsresult
OnStartRequest(nsIRequest* aRequest, uint32_t aIndex)
{
AssertIsOnMainThread();
MOZ_ASSERT(aIndex < mLoadInfos.Length());
nsCOMPtr<nsISupportsPRUint32> indexSupports(do_QueryInterface(aContext));
MOZ_ASSERT(indexSupports, "This should never fail!");
uint32_t index = UINT32_MAX;
if (NS_FAILED(indexSupports->GetData(&index)) ||
index >= mLoadInfos.Length()) {
MOZ_CRASH("Bad index!");
}
ScriptLoadInfo& loadInfo = mLoadInfos[index];
ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
MOZ_ASSERT(channel == loadInfo.mChannel);
@ -663,7 +668,7 @@ private:
}
RefPtr<CachePromiseHandler> promiseHandler =
new CachePromiseHandler(this, loadInfo, index);
new CachePromiseHandler(this, loadInfo, aIndex);
cachePromise->AppendNativeHandler(promiseHandler);
loadInfo.mCachePromise.swap(cachePromise);
@ -672,14 +677,6 @@ private:
return NS_OK;
}
NS_IMETHOD
OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
nsresult aStatusCode) override
{
// Nothing to do here!
return NS_OK;
}
virtual bool
Notify(JSContext* aCx, Status aStatus) override
{
@ -782,6 +779,7 @@ private:
++index) {
nsresult rv = LoadScript(index);
if (NS_WARN_IF(NS_FAILED(rv))) {
LoadingFinished(index, rv);
return rv;
}
}
@ -879,27 +877,18 @@ private:
// We need to know which index we're on in OnStreamComplete so we know
// where to put the result.
nsCOMPtr<nsISupportsPRUint32> indexSupports =
do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = indexSupports->SetData(aIndex);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
RefPtr<LoaderListener> listener = new LoaderListener(this, aIndex);
// We don't care about progress so just use the simple stream loader for
// OnStreamComplete notification only.
nsCOMPtr<nsIStreamLoader> loader;
rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
rv = NS_NewStreamLoader(getter_AddRefs(loader), listener);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (loadInfo.mCacheStatus != ScriptLoadInfo::ToBeCached) {
rv = channel->AsyncOpen(loader, indexSupports);
rv = channel->AsyncOpen2(loader);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -918,12 +907,12 @@ private:
nsCOMPtr<nsIStreamListenerTee> tee =
do_CreateInstance(NS_STREAMLISTENERTEE_CONTRACTID);
rv = tee->Init(loader, writer, this);
rv = tee->Init(loader, writer, listener);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsresult rv = channel->AsyncOpen(tee, indexSupports);
nsresult rv = channel->AsyncOpen2(tee);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -935,9 +924,9 @@ private:
}
nsresult
OnStreamCompleteInternal(nsIStreamLoader* aLoader, nsISupports* aContext,
nsresult aStatus, uint32_t aStringLen,
const uint8_t* aString, ScriptLoadInfo& aLoadInfo)
OnStreamCompleteInternal(nsIStreamLoader* aLoader, nsresult aStatus,
uint32_t aStringLen, const uint8_t* aString,
ScriptLoadInfo& aLoadInfo)
{
AssertIsOnMainThread();
@ -1094,13 +1083,6 @@ private:
}
}
}
else {
// We exempt data urls and other URI's that inherit their
// principal again.
if (NS_FAILED(loadPrincipal->CheckMayLoad(finalURI, false, true))) {
return NS_ERROR_DOM_BAD_URI;
}
}
// The principal can change, but it should still match the original
// load group's appId and browser element flag.
@ -1244,9 +1226,21 @@ private:
}
};
NS_IMPL_ISUPPORTS(ScriptLoaderRunnable, nsIRunnable,
nsIStreamLoaderObserver,
nsIRequestObserver)
NS_IMPL_ISUPPORTS(ScriptLoaderRunnable, nsIRunnable)
NS_IMETHODIMP
LoaderListener::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
nsresult aStatus, uint32_t aStringLen,
const uint8_t* aString)
{
return mRunnable->OnStreamComplete(aLoader, mIndex, aStatus, aStringLen, aString);
}
NS_IMETHODIMP
LoaderListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
{
return mRunnable->OnStartRequest(aRequest, mIndex);
}
void
CachePromiseHandler::ResolvedCallback(JSContext* aCx,
@ -1508,9 +1502,14 @@ CacheScriptLoader::ResolvedCallback(JSContext* aCx,
MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::Uncached);
nsresult rv;
if (aValue.isUndefined()) {
mLoadInfo.mCacheStatus = ScriptLoadInfo::ToBeCached;
mRunnable->LoadScript(mIndex);
rv = mRunnable->LoadScript(mIndex);
if (NS_WARN_IF(NS_FAILED(rv))) {
Fail(rv);
}
return;
}
@ -1518,7 +1517,7 @@ CacheScriptLoader::ResolvedCallback(JSContext* aCx,
JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
mozilla::dom::Response* response = nullptr;
nsresult rv = UNWRAP_OBJECT(Response, obj, response);
rv = UNWRAP_OBJECT(Response, obj, response);
if (NS_WARN_IF(NS_FAILED(rv))) {
Fail(rv);
return;

View File

@ -39,10 +39,8 @@ worker.onmessage = function(event) {
}
msg = "Loading data:something";
try {
worker = new Worker("data:application/javascript;base64,ZHVtcCgnaGVsbG8gd29ybGQnKQo=");
ok(false, "Should have thrown!");
} catch (e) {
worker = new Worker("data:application/javascript;base64,ZHVtcCgnaGVsbG8gd29ybGQnKQo=");
worker.onerror = function() {
ok(true, "Threw as expected.");
}

View File

@ -13,55 +13,53 @@
<script class="testbody" type="text/javascript">
"use strict";
function nextTest() {
(function(){
function workerfunc() {
var subworker = new Worker("about:blank");
subworker.onerror = function(e) {
e.preventDefault();
postMessage(e.message);
}
}
var b = new Blob([workerfunc+'workerfunc();']);
var u = URL.createObjectURL(b);
function callworker(i) {
try {
var w = new Worker(u);
URL.revokeObjectURL(u);
is(i, 0, 'worker creation succeeded');
} catch (e) {
is(i, 1, 'worker creation failed');
SimpleTest.finish();
return;
}
w.onmessage = function(e) {
is(e.data.indexOf('Error: Failed to load script'), 0, "Error: Failed to load script");
if (++i < 2) callworker(i);
else SimpleTest.finish();
};
}
callworker(0);
})();
}
try {
var worker = new Worker("about:blank");
ok(false, "Shouldn't success!");
worker.onerror = function(e) {
e.preventDefault();
ok(true, "Shouldn't success!");
nextTest();
}
worker.onmessage = function(event) {
ok(false, "Shouldn't get a message!");
SimpleTest.finish();
}
worker.onerror = function(event) {
ok(false, "Shouldn't get a error message!");
SimpleTest.finish();
}
} catch (e) {
ok(!worker, "worker should not be created");
is(e.name, "SecurityError", "SecurityError should be thrown");
is(e.code, DOMException.SECURITY_ERR, "SECURITY_ERR should be thrown");
ok(false, "This should not happen.");
}
(function(){
function workerfunc() {
try {
var subworker = new Worker("about:blank");
postMessage({});
} catch (e) {
postMessage({name: e.name, code: e.code});
}
}
var b = new Blob([workerfunc+'workerfunc();']);
var u = URL.createObjectURL(b);
function callworker(i) {
try {
var w = new Worker(u);
URL.revokeObjectURL(u);
is(i, 0, 'worker creation succeeded');
} catch (e) {
is(i, 1, 'worker creation failed');
SimpleTest.finish();
return;
}
w.onmessage = function(e) {
is(e.data.name, "SecurityError", "SecurityError should be thrown");
is(e.data.code, DOMException.SECURITY_ERR, "SECURITY_ERR should be thrown");
if (++i < 2) callworker(i);
else SimpleTest.finish();
};
}
callworker(0);
})();
SimpleTest.waitForExplicitFinish();

View File

@ -1,4 +0,0 @@
[004.html]
type: testharness
[importScripts broken script]
expected: FAIL

View File

@ -1,6 +0,0 @@
[006.html]
type: testharness
expected: ERROR
[importScripts uncaught exception]
expected: TIMEOUT