Bug 1290021 - Implement a prototype version of Houdini "Worklets Level 1" spec - part 4 - cache for the imports, r=smaug

--HG--
rename : dom/worklet/tests/file_basic.html => dom/worklet/tests/file_import_with_cache.html
rename : dom/worklet/tests/test_basic.html => dom/worklet/tests/test_import_with_cache.html
This commit is contained in:
Andrea Marchesini 2016-11-06 09:55:20 +01:00
parent f8cf3b6565
commit 1c9916b76f
7 changed files with 252 additions and 18 deletions

View File

@ -21,7 +21,8 @@
namespace mozilla {
namespace dom {
namespace {
// ---------------------------------------------------------------------------
// WorkletFetchHandler
class WorkletFetchHandler : public PromiseNativeHandler
, public nsIStreamLoaderObserver
@ -32,6 +33,8 @@ public:
static already_AddRefed<Promise>
Fetch(Worklet* aWorklet, const nsAString& aModuleURL, ErrorResult& aRv)
{
MOZ_ASSERT(aWorklet);
nsCOMPtr<nsIGlobalObject> global =
do_QueryInterface(aWorklet->GetParentObject());
MOZ_ASSERT(global);
@ -41,6 +44,40 @@ public:
return nullptr;
}
nsCOMPtr<nsPIDOMWindowInner> window = aWorklet->GetParentObject();
MOZ_ASSERT(window);
nsCOMPtr<nsIDocument> doc;
doc = window->GetExtantDoc();
if (!doc) {
promise->MaybeReject(NS_ERROR_FAILURE);
return promise.forget();
}
nsCOMPtr<nsIURI> baseURI = doc->GetBaseURI();
nsCOMPtr<nsIURI> resolvedURI;
nsresult rv = NS_NewURI(getter_AddRefs(resolvedURI), aModuleURL, nullptr, baseURI);
if (NS_WARN_IF(NS_FAILED(rv))) {
promise->MaybeReject(rv);
return promise.forget();
}
nsAutoCString spec;
rv = resolvedURI->GetSpec(spec);
if (NS_WARN_IF(NS_FAILED(rv))) {
promise->MaybeReject(rv);
return promise.forget();
}
// Maybe we already have an handler for this URI
{
WorkletFetchHandler* handler = aWorklet->GetImportFetchHandler(spec);
if (handler) {
handler->AddPromise(promise);
return promise.forget();
}
}
RequestOrUSVString request;
request.SetAsUSVString().Rebind(aModuleURL.Data(), aModuleURL.Length());
@ -56,6 +93,7 @@ public:
new WorkletFetchHandler(aWorklet, aModuleURL, promise);
fetchPromise->AppendNativeHandler(handler);
aWorklet->AddImportFetchHandler(spec, handler);
return promise.forget();
}
@ -63,46 +101,46 @@ public:
ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
{
if (!aValue.isObject()) {
mPromise->MaybeReject(NS_ERROR_FAILURE);
RejectPromises(NS_ERROR_FAILURE);
return;
}
RefPtr<Response> response;
nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response);
if (NS_WARN_IF(NS_FAILED(rv))) {
mPromise->MaybeReject(rv);
RejectPromises(NS_ERROR_FAILURE);
return;
}
if (!response->Ok()) {
mPromise->MaybeReject(NS_ERROR_DOM_NETWORK_ERR);
RejectPromises(NS_ERROR_DOM_NETWORK_ERR);
return;
}
nsCOMPtr<nsIInputStream> inputStream;
response->GetBody(getter_AddRefs(inputStream));
if (!inputStream) {
mPromise->MaybeReject(NS_ERROR_DOM_NETWORK_ERR);
RejectPromises(NS_ERROR_DOM_NETWORK_ERR);
return;
}
nsCOMPtr<nsIInputStreamPump> pump;
rv = NS_NewInputStreamPump(getter_AddRefs(pump), inputStream);
if (NS_WARN_IF(NS_FAILED(rv))) {
mPromise->MaybeReject(rv);
RejectPromises(rv);
return;
}
nsCOMPtr<nsIStreamLoader> loader;
rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
if (NS_WARN_IF(NS_FAILED(rv))) {
mPromise->MaybeReject(rv);
RejectPromises(rv);
return;
}
rv = pump->AsyncRead(loader, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
mPromise->MaybeReject(rv);
RejectPromises(rv);
return;
}
@ -125,7 +163,7 @@ public:
MOZ_ASSERT(NS_IsMainThread());
if (NS_FAILED(aStatus)) {
mPromise->MaybeReject(aStatus);
RejectPromises(aStatus);
return NS_OK;
}
@ -136,7 +174,7 @@ public:
NS_LITERAL_STRING("UTF-8"), nullptr,
scriptTextBuf, scriptTextLength);
if (NS_WARN_IF(NS_FAILED(rv))) {
mPromise->MaybeReject(rv);
RejectPromises(rv);
return NS_OK;
}
@ -172,41 +210,107 @@ public:
if (!JS::Evaluate(cx, compileOptions, buffer, &unused)) {
ErrorResult error;
error.StealExceptionFromJSContext(cx);
mPromise->MaybeReject(error);
RejectPromises(error.StealNSResult());
return NS_OK;
}
// All done.
mPromise->MaybeResolveWithUndefined();
ResolvePromises();
return NS_OK;
}
virtual void
RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
{
mPromise->MaybeReject(aCx, aValue);
RejectPromises(NS_ERROR_DOM_NETWORK_ERR);
}
private:
WorkletFetchHandler(Worklet* aWorklet, const nsAString& aURL,
Promise* aPromise)
: mWorklet(aWorklet)
, mPromise(aPromise)
, mStatus(ePending)
, mErrorStatus(NS_OK)
, mURL(aURL)
{}
{
MOZ_ASSERT(aWorklet);
MOZ_ASSERT(aPromise);
mPromises.AppendElement(aPromise);
}
~WorkletFetchHandler()
{}
void
AddPromise(Promise* aPromise)
{
MOZ_ASSERT(aPromise);
switch (mStatus) {
case ePending:
mPromises.AppendElement(aPromise);
return;
case eRejected:
MOZ_ASSERT(NS_FAILED(mErrorStatus));
aPromise->MaybeReject(mErrorStatus);
return;
case eResolved:
aPromise->MaybeResolveWithUndefined();
return;
}
}
void
RejectPromises(nsresult aResult)
{
MOZ_ASSERT(mStatus == ePending);
MOZ_ASSERT(NS_FAILED(aResult));
for (uint32_t i = 0; i < mPromises.Length(); ++i) {
mPromises[i]->MaybeReject(aResult);
}
mPromises.Clear();
mStatus = eRejected;
mErrorStatus = aResult;
mWorklet = nullptr;
}
void
ResolvePromises()
{
MOZ_ASSERT(mStatus == ePending);
for (uint32_t i = 0; i < mPromises.Length(); ++i) {
mPromises[i]->MaybeResolveWithUndefined();
}
mPromises.Clear();
mStatus = eResolved;
mWorklet = nullptr;
}
RefPtr<Worklet> mWorklet;
RefPtr<Promise> mPromise;
nsTArray<RefPtr<Promise>> mPromises;
enum {
ePending,
eRejected,
eResolved
} mStatus;
nsresult mErrorStatus;
nsString mURL;
};
NS_IMPL_ISUPPORTS(WorkletFetchHandler, nsIStreamLoaderObserver)
} // anonymous namespace
// ---------------------------------------------------------------------------
// Worklet
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Worklet, mWindow, mScope)
NS_IMPL_CYCLE_COLLECTING_ADDREF(Worklet)
@ -263,5 +367,21 @@ Worklet::GetOrCreateGlobalScope(JSContext* aCx)
return mScope;
}
WorkletFetchHandler*
Worklet::GetImportFetchHandler(const nsACString& aURI)
{
return mImportHandlers.GetWeak(aURI);
}
void
Worklet::AddImportFetchHandler(const nsACString& aURI,
WorkletFetchHandler* aHandler)
{
MOZ_ASSERT(aHandler);
MOZ_ASSERT(!mImportHandlers.GetWeak(aURI));
mImportHandlers.Put(aURI, aHandler);
}
} // dom namespace
} // mozilla namespace

View File

@ -9,6 +9,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "nsRefPtrHashtable.h"
#include "nsWrapperCache.h"
#include "nsCOMPtr.h"
@ -20,6 +21,7 @@ namespace dom {
class Promise;
class WorkletGlobalScope;
class WorkletFetchHandler;
class Worklet final : public nsISupports
, public nsWrapperCache
@ -47,10 +49,19 @@ public:
private:
~Worklet();
WorkletFetchHandler*
GetImportFetchHandler(const nsACString& aURI);
void
AddImportFetchHandler(const nsACString& aURI, WorkletFetchHandler* aHandler);
nsCOMPtr<nsPIDOMWindowInner> mWindow;
nsCOMPtr<nsIPrincipal> mPrincipal;
RefPtr<WorkletGlobalScope> mScope;
nsRefPtrHashtable<nsCStringHashKey, WorkletFetchHandler> mImportHandlers;
friend class WorkletFetchHandler;
};
} // namespace dom

View File

@ -13,10 +13,21 @@ setupTest();
var worklet = window.createWorklet();
ok(!!worklet, "We have a Worklet");
// First loading
worklet.import("common.js")
.then(() => {
ok(true, "Import should load a resource.");
})
// Second loading - same file
.then(() => {
return worklet.import("common.js")
})
.then(() => {
ok(true, "Import should load a resource.");
})
// 3rd loading - a network error
.then(() => {
return worklet.import("404.js");
})
@ -24,7 +35,20 @@ worklet.import("common.js")
ok(false, "The loading should fail.");
}, () => {
ok(true, "The loading should fail.");
}).then(() => {
})
// 4th loading - a network error
.then(() => {
return worklet.import("404.js");
})
.then(() => {
ok(false, "The loading should fail.");
}, () => {
ok(true, "The loading should fail.");
})
// done
.then(() => {
SimpleTest.finish();
});

View File

@ -0,0 +1,43 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Worklet</title>
<script type="application/javascript" src="common.js"></script>
</head>
<body>
<script type="application/javascript">
setupTest();
var worklet = window.createWorklet();
ok(!!worklet, "We have a Worklet");
function loading() {
worklet.import("server_import_with_cache.sjs")
.then(() => {
ok(true, "Import should load a resource.");
}, () => {
ok(false, "Import should load a resource.");
})
.then(() => {
done();
});
}
var count = 0;
const MAX = 10;
function done() {
if (++count == MAX) {
SimpleTest.finish();
}
}
for (var i = 0; i < MAX; ++i) {
loading();
}
</script>
</body>
</html>

View File

@ -6,3 +6,5 @@ support-files =
support-files=file_basic.html
[test_console.html]
support-files=file_console.html worklet_console.js
[test_import_with_cache.html]
support-files=file_import_with_cache.html server_import_with_cache.sjs

View File

@ -0,0 +1,13 @@
function handleRequest(request, response)
{
response.setHeader("Content-Type", "text/javascript", false);
var state = getState("alreadySent");
if (!state) {
setState("alreadySent", "1");
} else {
response.setStatusLine('1.1', 404, "Not Found");
}
response.write("42");
}

View File

@ -0,0 +1,21 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Worklet</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="application/javascript" src="common.js"></script>
</head>
<body>
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv(
{"set": [["dom.worklet.testing.enabled", true],
["dom.worklet.enabled", true]]},
function() { loadTest("file_import_with_cache.html"); });
</script>
</body>
</html>