Bug 1128959 - Implement the WHATWG Streams spec - part 12 - starting body consuming and passing the JSContext down from the binding entrypoints to where the ReadableStream could be read, r=bz

This patch does 2 things:

. when SetBodyUsed() is called, the pump for the stream reading is activated.

. Just because of the reading of the stream could end up executing JS code, we
  need to pass the JSContext in the correct state down to SetBodyUsed.
This commit is contained in:
Andrea Marchesini 2017-08-10 18:04:55 -07:00
Родитель 4928cd7694
Коммит cb3c4dda07
22 изменённых файлов: 365 добавлений и 75 удалений

Просмотреть файл

@ -112,11 +112,13 @@ DOMInterfaces = {
},
'Cache': {
'implicitJSContext': [ 'add', 'addAll' ],
'implicitJSContext': [ 'add', 'addAll', 'match', 'matchAll', 'put',
'delete', 'keys' ],
'nativeType': 'mozilla::dom::cache::Cache',
},
'CacheStorage': {
'implicitJSContext': [ 'match' ],
'nativeType': 'mozilla::dom::cache::CacheStorage',
},

8
dom/cache/AutoUtils.cpp поставляемый
Просмотреть файл

@ -290,9 +290,9 @@ MatchInPutList(InternalRequest* aRequest,
} // namespace
void
AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction,
SchemeAction aSchemeAction, Response& aResponse,
ErrorResult& aRv)
AutoChildOpArgs::Add(JSContext* aCx, InternalRequest* aRequest,
BodyAction aBodyAction, SchemeAction aSchemeAction,
Response& aResponse, ErrorResult& aRv)
{
MOZ_DIAGNOSTIC_ASSERT(!mSent);
@ -330,7 +330,7 @@ AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction,
mTypeUtils->ToCacheRequest(pair.request(), aRequest, aBodyAction,
aSchemeAction, mStreamCleanupList, aRv);
if (!aRv.Failed()) {
mTypeUtils->ToCacheResponse(pair.response(), aResponse,
mTypeUtils->ToCacheResponse(aCx, pair.response(), aResponse,
mStreamCleanupList, aRv);
}

2
dom/cache/AutoUtils.h поставляемый
Просмотреть файл

@ -56,7 +56,7 @@ public:
void Add(InternalRequest* aRequest, BodyAction aBodyAction,
SchemeAction aSchemeAction, ErrorResult& aRv);
void Add(InternalRequest* aRequest, BodyAction aBodyAction,
void Add(JSContext* aCx, InternalRequest* aRequest, BodyAction aBodyAction,
SchemeAction aSchemeAction, Response& aResponse, ErrorResult& aRv);
const CacheOpArgs& SendAsOpArgs();

40
dom/cache/Cache.cpp поставляемый
Просмотреть файл

@ -202,7 +202,10 @@ public:
// Now store the unwrapped Response list in the Cache.
ErrorResult result;
RefPtr<Promise> put = mCache->PutAll(mRequestList, responseList, result);
// TODO: Here we use the JSContext as received by the ResolvedCallback, and
// its state could be the wrong one. The spec doesn't say anything
// about it, yet (bug 1384006)
RefPtr<Promise> put = mCache->PutAll(aCx, mRequestList, responseList, result);
if (NS_WARN_IF(result.Failed())) {
// TODO: abort the fetch requests we have running (bug 1157434)
mPromise->MaybeReject(result);
@ -263,7 +266,7 @@ Cache::Cache(nsIGlobalObject* aGlobal, CacheChild* aActor)
}
already_AddRefed<Promise>
Cache::Match(const RequestOrUSVString& aRequest,
Cache::Match(JSContext* aCx, const RequestOrUSVString& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv)
{
if (NS_WARN_IF(!mActor)) {
@ -273,7 +276,8 @@ Cache::Match(const RequestOrUSVString& aRequest,
CacheChild::AutoLock actorLock(mActor);
RefPtr<InternalRequest> ir = ToInternalRequest(aRequest, IgnoreBody, aRv);
RefPtr<InternalRequest> ir =
ToInternalRequest(aCx, aRequest, IgnoreBody, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
@ -292,7 +296,7 @@ Cache::Match(const RequestOrUSVString& aRequest,
}
already_AddRefed<Promise>
Cache::MatchAll(const Optional<RequestOrUSVString>& aRequest,
Cache::MatchAll(JSContext* aCx, const Optional<RequestOrUSVString>& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv)
{
if (NS_WARN_IF(!mActor)) {
@ -308,8 +312,8 @@ Cache::MatchAll(const Optional<RequestOrUSVString>& aRequest,
AutoChildOpArgs args(this, CacheMatchAllArgs(void_t(), params), 1);
if (aRequest.WasPassed()) {
RefPtr<InternalRequest> ir = ToInternalRequest(aRequest.Value(),
IgnoreBody, aRv);
RefPtr<InternalRequest> ir = ToInternalRequest(aCx, aRequest.Value(),
IgnoreBody, aRv);
if (aRv.Failed()) {
return nullptr;
}
@ -409,8 +413,8 @@ Cache::AddAll(JSContext* aContext,
}
already_AddRefed<Promise>
Cache::Put(const RequestOrUSVString& aRequest, Response& aResponse,
ErrorResult& aRv)
Cache::Put(JSContext* aCx, const RequestOrUSVString& aRequest,
Response& aResponse, ErrorResult& aRv)
{
if (NS_WARN_IF(!mActor)) {
aRv.Throw(NS_ERROR_UNEXPECTED);
@ -427,14 +431,14 @@ Cache::Put(const RequestOrUSVString& aRequest, Response& aResponse,
return nullptr;
}
RefPtr<InternalRequest> ir = ToInternalRequest(aRequest, ReadBody, aRv);
RefPtr<InternalRequest> ir = ToInternalRequest(aCx, aRequest, ReadBody, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
AutoChildOpArgs args(this, CachePutAllArgs(), 1);
args.Add(ir, ReadBody, TypeErrorOnInvalidScheme,
args.Add(aCx, ir, ReadBody, TypeErrorOnInvalidScheme,
aResponse, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
@ -444,7 +448,7 @@ Cache::Put(const RequestOrUSVString& aRequest, Response& aResponse,
}
already_AddRefed<Promise>
Cache::Delete(const RequestOrUSVString& aRequest,
Cache::Delete(JSContext* aCx, const RequestOrUSVString& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv)
{
if (NS_WARN_IF(!mActor)) {
@ -454,7 +458,8 @@ Cache::Delete(const RequestOrUSVString& aRequest,
CacheChild::AutoLock actorLock(mActor);
RefPtr<InternalRequest> ir = ToInternalRequest(aRequest, IgnoreBody, aRv);
RefPtr<InternalRequest> ir =
ToInternalRequest(aCx, aRequest, IgnoreBody, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
@ -473,7 +478,7 @@ Cache::Delete(const RequestOrUSVString& aRequest,
}
already_AddRefed<Promise>
Cache::Keys(const Optional<RequestOrUSVString>& aRequest,
Cache::Keys(JSContext* aCx, const Optional<RequestOrUSVString>& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv)
{
if (NS_WARN_IF(!mActor)) {
@ -489,8 +494,8 @@ Cache::Keys(const Optional<RequestOrUSVString>& aRequest,
AutoChildOpArgs args(this, CacheKeysArgs(void_t(), params), 1);
if (aRequest.WasPassed()) {
RefPtr<InternalRequest> ir = ToInternalRequest(aRequest.Value(),
IgnoreBody, aRv);
RefPtr<InternalRequest> ir =
ToInternalRequest(aCx, aRequest.Value(), IgnoreBody, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
@ -651,7 +656,7 @@ Cache::AddAll(const GlobalObject& aGlobal,
}
already_AddRefed<Promise>
Cache::PutAll(const nsTArray<RefPtr<Request>>& aRequestList,
Cache::PutAll(JSContext* aCx, const nsTArray<RefPtr<Request>>& aRequestList,
const nsTArray<RefPtr<Response>>& aResponseList,
ErrorResult& aRv)
{
@ -668,7 +673,8 @@ Cache::PutAll(const nsTArray<RefPtr<Request>>& aRequestList,
for (uint32_t i = 0; i < aRequestList.Length(); ++i) {
RefPtr<InternalRequest> ir = aRequestList[i]->GetInternalRequest();
args.Add(ir, ReadBody, TypeErrorOnInvalidScheme, *aResponseList[i], aRv);
args.Add(aCx, ir, ReadBody, TypeErrorOnInvalidScheme, *aResponseList[i],
aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}

16
dom/cache/Cache.h поставляемый
Просмотреть файл

@ -45,10 +45,10 @@ public:
// webidl interface methods
already_AddRefed<Promise>
Match(const RequestOrUSVString& aRequest, const CacheQueryOptions& aOptions,
ErrorResult& aRv);
Match(JSContext* aCx, const RequestOrUSVString& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv);
already_AddRefed<Promise>
MatchAll(const Optional<RequestOrUSVString>& aRequest,
MatchAll(JSContext* aCx, const Optional<RequestOrUSVString>& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv);
already_AddRefed<Promise>
Add(JSContext* aContext, const RequestOrUSVString& aRequest,
@ -58,13 +58,13 @@ public:
const Sequence<OwningRequestOrUSVString>& aRequests,
CallerType aCallerType, ErrorResult& aRv);
already_AddRefed<Promise>
Put(const RequestOrUSVString& aRequest, Response& aResponse,
Put(JSContext* aCx, const RequestOrUSVString& aRequest, Response& aResponse,
ErrorResult& aRv);
already_AddRefed<Promise>
Delete(const RequestOrUSVString& aRequest, const CacheQueryOptions& aOptions,
ErrorResult& aRv);
Delete(JSContext* aCx, const RequestOrUSVString& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv);
already_AddRefed<Promise>
Keys(const Optional<RequestOrUSVString>& aRequest,
Keys(JSContext* aCx, const Optional<RequestOrUSVString>& aRequest,
const CacheQueryOptions& aParams, ErrorResult& aRv);
// binding methods
@ -103,7 +103,7 @@ private:
CallerType aCallerType, ErrorResult& aRv);
already_AddRefed<Promise>
PutAll(const nsTArray<RefPtr<Request>>& aRequestList,
PutAll(JSContext* aCx, const nsTArray<RefPtr<Request>>& aRequestList,
const nsTArray<RefPtr<Response>>& aResponseList,
ErrorResult& aRv);

6
dom/cache/CacheStorage.cpp поставляемый
Просмотреть файл

@ -307,7 +307,7 @@ CacheStorage::CacheStorage(nsresult aFailureResult)
}
already_AddRefed<Promise>
CacheStorage::Match(const RequestOrUSVString& aRequest,
CacheStorage::Match(JSContext* aCx, const RequestOrUSVString& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv)
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
@ -317,8 +317,8 @@ CacheStorage::Match(const RequestOrUSVString& aRequest,
return nullptr;
}
RefPtr<InternalRequest> request = ToInternalRequest(aRequest, IgnoreBody,
aRv);
RefPtr<InternalRequest> request =
ToInternalRequest(aCx, aRequest, IgnoreBody, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}

6
dom/cache/CacheStorage.h поставляемый
Просмотреть файл

@ -60,9 +60,9 @@ public:
DefineCaches(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
// webidl interface methods
already_AddRefed<Promise> Match(const RequestOrUSVString& aRequest,
const CacheQueryOptions& aOptions,
ErrorResult& aRv);
already_AddRefed<Promise>
Match(JSContext* aCx, const RequestOrUSVString& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv);
already_AddRefed<Promise> Has(const nsAString& aKey, ErrorResult& aRv);
already_AddRefed<Promise> Open(const nsAString& aKey, ErrorResult& aRv);
already_AddRefed<Promise> Delete(const nsAString& aKey, ErrorResult& aRv);

25
dom/cache/TypeUtils.cpp поставляемый
Просмотреть файл

@ -78,7 +78,7 @@ ToHeadersEntryList(nsTArray<HeadersEntry>& aOut, InternalHeaders* aHeaders)
} // namespace
already_AddRefed<InternalRequest>
TypeUtils::ToInternalRequest(const RequestOrUSVString& aIn,
TypeUtils::ToInternalRequest(JSContext* aCx, const RequestOrUSVString& aIn,
BodyAction aBodyAction, ErrorResult& aRv)
{
if (aIn.IsRequest()) {
@ -86,7 +86,7 @@ TypeUtils::ToInternalRequest(const RequestOrUSVString& aIn,
// Check and set bodyUsed flag immediately because its on Request
// instead of InternalRequest.
CheckAndSetBodyUsed(&request, aBodyAction, aRv);
CheckAndSetBodyUsed(aCx, &request, aBodyAction, aRv);
if (aRv.Failed()) { return nullptr; }
return request.GetInternalRequest();
@ -96,7 +96,8 @@ TypeUtils::ToInternalRequest(const RequestOrUSVString& aIn,
}
already_AddRefed<InternalRequest>
TypeUtils::ToInternalRequest(const OwningRequestOrUSVString& aIn,
TypeUtils::ToInternalRequest(JSContext* aCx,
const OwningRequestOrUSVString& aIn,
BodyAction aBodyAction, ErrorResult& aRv)
{
@ -105,7 +106,7 @@ TypeUtils::ToInternalRequest(const OwningRequestOrUSVString& aIn,
// Check and set bodyUsed flag immediately because its on Request
// instead of InternalRequest.
CheckAndSetBodyUsed(request, aBodyAction, aRv);
CheckAndSetBodyUsed(aCx, request, aBodyAction, aRv);
if (aRv.Failed()) { return nullptr; }
return request->GetInternalRequest();
@ -203,7 +204,7 @@ TypeUtils::ToCacheResponseWithoutBody(CacheResponse& aOut,
}
void
TypeUtils::ToCacheResponse(CacheResponse& aOut, Response& aIn,
TypeUtils::ToCacheResponse(JSContext* aCx, CacheResponse& aOut, Response& aIn,
nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
ErrorResult& aRv)
{
@ -221,7 +222,10 @@ TypeUtils::ToCacheResponse(CacheResponse& aOut, Response& aIn,
nsCOMPtr<nsIInputStream> stream;
ir->GetUnfilteredBody(getter_AddRefs(stream));
if (stream) {
aIn.SetBodyUsed();
aIn.SetBodyUsed(aCx, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
}
SerializeCacheStream(stream, &aOut.body(), aStreamCleanupList, aRv);
@ -425,8 +429,8 @@ TypeUtils::ProcessURL(nsACString& aUrl, bool* aSchemeValidOut,
}
void
TypeUtils::CheckAndSetBodyUsed(Request* aRequest, BodyAction aBodyAction,
ErrorResult& aRv)
TypeUtils::CheckAndSetBodyUsed(JSContext* aCx, Request* aRequest,
BodyAction aBodyAction, ErrorResult& aRv)
{
MOZ_DIAGNOSTIC_ASSERT(aRequest);
@ -442,7 +446,10 @@ TypeUtils::CheckAndSetBodyUsed(Request* aRequest, BodyAction aBodyAction,
nsCOMPtr<nsIInputStream> stream;
aRequest->GetBody(getter_AddRefs(stream));
if (stream) {
aRequest->SetBodyUsed();
aRequest->SetBodyUsed(aCx, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
}
}

12
dom/cache/TypeUtils.h поставляемый
Просмотреть файл

@ -73,12 +73,12 @@ public:
GetIPCManager() = 0;
already_AddRefed<InternalRequest>
ToInternalRequest(const RequestOrUSVString& aIn, BodyAction aBodyAction,
ErrorResult& aRv);
ToInternalRequest(JSContext* aCx, const RequestOrUSVString& aIn,
BodyAction aBodyAction, ErrorResult& aRv);
already_AddRefed<InternalRequest>
ToInternalRequest(const OwningRequestOrUSVString& aIn, BodyAction aBodyAction,
ErrorResult& aRv);
ToInternalRequest(JSContext* aCx, const OwningRequestOrUSVString& aIn,
BodyAction aBodyAction, ErrorResult& aRv);
void
ToCacheRequest(CacheRequest& aOut, InternalRequest* aIn,
@ -91,7 +91,7 @@ public:
ErrorResult& aRv);
void
ToCacheResponse(CacheResponse& aOut, Response& aIn,
ToCacheResponse(JSContext* aCx, CacheResponse& aOut, Response& aIn,
nsTArray<UniquePtr<mozilla::ipc::AutoIPCStream>>& aStreamCleanupList,
ErrorResult& aRv);
@ -133,7 +133,7 @@ public:
private:
void
CheckAndSetBodyUsed(Request* aRequest, BodyAction aBodyAction,
CheckAndSetBodyUsed(JSContext* aCx, Request* aRequest, BodyAction aBodyAction,
ErrorResult& aRv);
already_AddRefed<InternalRequest>

Просмотреть файл

@ -970,16 +970,17 @@ bool
FetchBody<Response>::BodyUsed() const;
template <class Derived>
already_AddRefed<Promise>
FetchBody<Derived>::ConsumeBody(JSContext* aCx, FetchConsumeType aType,
ErrorResult& aRv)
void
FetchBody<Derived>::SetBodyUsed(JSContext* aCx, ErrorResult& aRv)
{
if (BodyUsed()) {
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
return nullptr;
MOZ_ASSERT(aCx);
MOZ_ASSERT(mOwner->EventTargetFor(TaskCategory::Other)->IsOnCurrentThread());
if (mBodyUsed) {
return;
}
SetBodyUsed();
mBodyUsed = true;
// If we already have a ReadableStreamBody and it has been created by DOM, we
// have to lock it now because it can have been shared with other objects.
@ -989,7 +990,7 @@ FetchBody<Derived>::ConsumeBody(JSContext* aCx, FetchConsumeType aType,
JS::ReadableStreamMode::ExternalSource) {
LockStream(aCx, readableStreamObj, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
return;
}
} else {
// If this is not a native ReadableStream, let's activate the
@ -998,12 +999,36 @@ FetchBody<Derived>::ConsumeBody(JSContext* aCx, FetchConsumeType aType,
JS::Rooted<JSObject*> reader(aCx);
mFetchStreamReader->StartConsuming(aCx, readableStreamObj, &reader, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
return;
}
mReadableStreamReader = reader;
}
}
}
template
void
FetchBody<Request>::SetBodyUsed(JSContext* aCx, ErrorResult& aRv);
template
void
FetchBody<Response>::SetBodyUsed(JSContext* aCx, ErrorResult& aRv);
template <class Derived>
already_AddRefed<Promise>
FetchBody<Derived>::ConsumeBody(JSContext* aCx, FetchConsumeType aType,
ErrorResult& aRv)
{
if (BodyUsed()) {
aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
return nullptr;
}
SetBodyUsed(aCx, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
nsCOMPtr<nsIGlobalObject> global = DerivedClass()->GetParentObject();

Просмотреть файл

@ -192,11 +192,28 @@ public:
// Utility public methods accessed by various runnables.
// This method _must_ be called in order to set the body as used. If the body
// is a ReadableStream, this method will start reading the stream.
// More in details, this method does:
// 1) It uses an internal flag to track if the body is used. This is tracked
// separately from the ReadableStream disturbed state due to purely native
// streams.
// 2) If there is a ReadableStream reflector for the native stream it is
// Locked.
// 3) If there is a JS ReadableStream then we begin pumping it into the native
// body stream. This effectively locks and disturbs the stream.
//
// Note that JSContext is used only if there is a ReadableStream (this can
// happen because the body is a ReadableStream or because attribute body has
// already been used by content). If something goes wrong using
// ReadableStream, errors will be reported via ErrorResult and not as JS
// exceptions in JSContext. This is done in order to have a centralized error
// reporting way.
//
// Exceptions generated when reading from the ReadableStream are directly sent
// to the Console (NOTE FOR THE REVIEWER: this is part of patch 16)
void
SetBodyUsed()
{
mBodyUsed = true;
}
SetBodyUsed(JSContext* aCx, ErrorResult& aRv);
const nsCString&
MimeType() const

Просмотреть файл

@ -606,7 +606,10 @@ Request::Constructor(const GlobalObject& aGlobal,
inputReq->GetBody(getter_AddRefs(body));
if (body) {
inputReq->SetBody(nullptr);
inputReq->SetBodyUsed();
inputReq->SetBodyUsed(aGlobal.Context(), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
}
}
return domRequest.forget();

Просмотреть файл

@ -87,7 +87,13 @@ FlyWebFetchEvent::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
if (response && response->Type() != ResponseType::Opaque) {
intResponse = response->GetInternalResponse();
response->SetBodyUsed();
IgnoredErrorResult rv;
response->SetBodyUsed(aCx, rv);
if (NS_WARN_IF(rv.Failed())) {
// Let's nullify the response. In this way we end up using a NetworkError
// response.
intResponse = nullptr;
}
}
if (!intResponse) {

Просмотреть файл

@ -80,6 +80,63 @@ function test_pendingStream() {
});
}
async function test_nativeStream_cache() {
info("test_nativeStream_cache");
let origBody = '123456789abcdef';
let url = '/nativeStream';
let cache = await caches.open('nativeStream');
info("Storing a body as a string");
await cache.put(url, new Response(origBody));
info("Retrieving the stored value");
let cacheResponse = await cache.match(url);
info("Converting the response to text");
let cacheBody = await cacheResponse.text();
is(origBody, cacheBody, "Bodies match");
await caches.delete('nativeStream');
next();
};
async function test_nonNativeStream_cache() {
info("test_nonNativeStream_cache");
let url = '/nonNativeStream';
let cache = await caches.open('nonNativeStream');
info("Storing a body as a string");
let r = new Response(new ReadableStream({start : controller => {
controller.enqueue(new Uint8Array([0x01, 0x02, 0x03]));
controller.close();
}}));
await cache.put(url, r);
info("Retrieving the stored value");
let cacheResponse = await cache.match(url);
info("Converting the response to text");
let cacheBody = await cacheResponse.arrayBuffer();
ok(cacheBody instanceof ArrayBuffer, "Body is an array buffer");
is(cacheBody.byteLength, 3, "Body length is correct");
is(new Uint8Array(cacheBody)[0], 0x01, "First byte is correct");
is(new Uint8Array(cacheBody)[1], 0x02, "Second byte is correct");
is(new Uint8Array(cacheBody)[2], 0x03, "Third byte is correct");
await caches.delete('nonNativeStream');
next();
};
function workify(func) {
info("Workifing " + func);

Просмотреть файл

@ -11,16 +11,31 @@
<script type="application/javascript">
let tests = [
function() {
SpecialPowers.pushPrefEnv({
"set": [["dom.caches.enabled", true],
["dom.caches.testing.enabled", true],
["dom.quotaManager.testing", true]]
}, next);
},
test_nativeStream,
function() { workify('test_nativeStream'); },
test_nonNativeStream,
function() { workify('test_nonNativeStream'); },
test_pendingStream,
function() { workify('test_pendingStream'); },
test_noUint8Array,
function() { workify('test_noUint8Array'); },
test_nativeStream_cache,
function() { workify('test_nativeStream_cache'); },
test_nonNativeStream_cache,
function() { workify('test_nonNativeStream_cache'); },
];
function next() {

Просмотреть файл

@ -8,6 +8,10 @@ function ok(a, message) {
postMessage({type: 'test', test: !!a, message });
}
function is(a, b, message) {
ok(a === b, message);
}
function next() {
postMessage({type: 'done'});
}

Просмотреть файл

@ -726,9 +726,14 @@ private:
request.SetAsUSVString().Rebind(loadInfo.mFullURL.Data(),
loadInfo.mFullURL.Length());
// This JSContext will not end up executing JS code because here there are
// no ReadableStreams involved.
AutoJSAPI jsapi;
jsapi.Init();
ErrorResult error;
RefPtr<Promise> cachePromise =
mCacheCreator->Cache_()->Put(request, *response, error);
mCacheCreator->Cache_()->Put(jsapi.cx(), request, *response, error);
if (NS_WARN_IF(error.Failed())) {
nsresult rv = error.StealNSResult();
channel->Cancel(rv);
@ -1619,8 +1624,13 @@ CacheScriptLoader::Load(Cache* aCache)
mozilla::dom::CacheQueryOptions params;
// This JSContext will not end up executing JS code because here there are
// no ReadableStreams involved.
AutoJSAPI jsapi;
jsapi.Init();
ErrorResult error;
RefPtr<Promise> promise = aCache->Match(request, params, error);
RefPtr<Promise> promise = aCache->Match(jsapi.cx(), request, params, error);
if (NS_WARN_IF(error.Failed())) {
Fail(error.StealNSResult());
return;

Просмотреть файл

@ -515,6 +515,44 @@ public:
}
}
// This function steals the error message from a ErrorResult.
void
SetCancelErrorResult(JSContext* aCx, ErrorResult& aRv)
{
MOZ_DIAGNOSTIC_ASSERT(aRv.Failed());
MOZ_DIAGNOSTIC_ASSERT(!JS_IsExceptionPending(aCx));
// Storing the error as exception in the JSContext.
if (!aRv.MaybeSetPendingException(aCx)) {
return;
}
MOZ_ASSERT(!aRv.Failed());
// Let's take the pending exception.
JS::Rooted<JS::Value> exn(aCx);
if (!JS_GetPendingException(aCx, &exn)) {
return;
}
JS_ClearPendingException(aCx);
// Converting the exception in a js::ErrorReport.
js::ErrorReport report(aCx);
if (!report.init(aCx, exn, js::ErrorReport::WithSideEffects)) {
JS_ClearPendingException(aCx);
return;
}
MOZ_ASSERT(mOwner);
MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
MOZ_ASSERT(mParams.Length() == 1);
// Let's store the error message here.
mMessageName.Assign(report.toStringResult().c_str());
mParams.Clear();
}
template<typename... Params>
void SetCancelMessage(const nsACString& aMessageName, Params&&... aParams)
{
@ -670,7 +708,12 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
ir->GetUnfilteredBody(getter_AddRefs(body));
// Errors and redirects may not have a body.
if (body) {
response->SetBodyUsed();
IgnoredErrorResult error;
response->SetBodyUsed(aCx, error);
if (NS_WARN_IF(error.Failed())) {
autoCancel.SetCancelErrorResult(aCx, error);
return;
}
nsCOMPtr<nsIOutputStream> responseBody;
rv = mInterceptedChannel->GetResponseBody(getter_AddRefs(responseBody));

Просмотреть файл

@ -423,8 +423,10 @@ private:
Optional<RequestOrUSVString> request;
CacheQueryOptions options;
ErrorResult error;
RefPtr<Promise> promise = mOldCache->Keys(request, options, error);
RefPtr<Promise> promise = mOldCache->Keys(aCx, request, options, error);
if (NS_WARN_IF(error.Failed())) {
// No exception here because there are no ReadableStreams involved here.
MOZ_ASSERT(!error.IsJSException());
rv = error.StealNSResult();
return;
}
@ -521,7 +523,7 @@ private:
MOZ_ASSERT(mPendingCount == 0);
for (uint32_t i = 0; i < mCNList.Length(); ++i) {
// We bail out immediately when something goes wrong.
rv = WriteToCache(cache, mCNList[i]);
rv = WriteToCache(aCx, cache, mCNList[i]);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
@ -559,7 +561,7 @@ private:
}
nsresult
WriteToCache(Cache* aCache, CompareNetwork* aCN)
WriteToCache(JSContext* aCx, Cache* aCache, CompareNetwork* aCN)
{
AssertIsOnMainThread();
MOZ_ASSERT(aCache);
@ -602,8 +604,10 @@ private:
// For now we have to wait until the Put Promise is fulfilled before we can
// continue since Cache does not yet support starting a read that is being
// written to.
RefPtr<Promise> cachePromise = aCache->Put(request, *response, result);
RefPtr<Promise> cachePromise = aCache->Put(aCx, request, *response, result);
if (NS_WARN_IF(result.Failed())) {
// No exception here because there are no ReadableStreams involved here.
MOZ_ASSERT(!result.IsJSException());
MOZ_ASSERT(!result.IsErrorWithMessage());
return result.StealNSResult();
}
@ -1003,12 +1007,19 @@ CompareCache::Initialize(Cache* const aCache, const nsAString& aURL)
MOZ_ASSERT(aCache);
MOZ_DIAGNOSTIC_ASSERT(mState == WaitingForInitialization);
// This JSContext will not end up executing JS code because here there are
// no ReadableStreams involved.
AutoJSAPI jsapi;
jsapi.Init();
RequestOrUSVString request;
request.SetAsUSVString().Rebind(aURL.Data(), aURL.Length());
ErrorResult error;
CacheQueryOptions params;
RefPtr<Promise> promise = aCache->Match(request, params, error);
RefPtr<Promise> promise = aCache->Match(jsapi.cx(), request, params, error);
if (NS_WARN_IF(error.Failed())) {
// No exception here because there are no ReadableStreams involved here.
MOZ_ASSERT(!error.IsJSException());
mState = Finished;
return error.StealNSResult();
}

Просмотреть файл

@ -127,6 +127,36 @@ fetchXHR('http://user:pass@mochi.test:8888/user-pass', function(xhr) {
finish();
});
fetchXHR('readable-stream.txt', function(xhr) {
my_ok(xhr.status == 200, "loading completed");
my_ok(xhr.responseText == 'Hello!', "The message is correct!");
finish();
});
fetchXHR('readable-stream-locked.txt', function(xhr) {
my_ok(false, "This should not be called!");
finish();
}, function() {
my_ok(true, "The exception has been correctly handled!");
finish();
});
fetchXHR('readable-stream-with-exception.txt', function(xhr) {
my_ok(false, "This should not be called!");
finish();
}, function() {
my_ok(true, "The exception has been correctly handled!");
finish();
});
fetchXHR('readable-stream-already-consumed.txt', function(xhr) {
my_ok(false, "This should not be called!");
finish();
}, function() {
my_ok(true, "The exception has been correctly handled!");
finish();
});
var expectedUncompressedResponse = "";
for (var i = 0; i < 10; ++i) {
expectedUncompressedResponse += "hello";

Просмотреть файл

@ -99,6 +99,58 @@ onfetch = function(ev) {
));
}
else if (ev.request.url.includes("readable-stream.txt")) {
ev.respondWith(
new Response(
new ReadableStream({
start: function(controller) {
controller.enqueue(new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21]));
controller.close();
}
})
));
}
else if (ev.request.url.includes("readable-stream-locked.txt")) {
let stream = new ReadableStream({
start(controller) {
controller.enqueue(new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21]));
controller.close();
}
});
ev.respondWith(new Response(stream));
// This locks the stream.
stream.getReader();
}
else if (ev.request.url.includes("readable-stream-with-exception.txt")) {
ev.respondWith(
new Response(
new ReadableStream({
start(controller) {},
pull() {
throw "EXCEPTION!";
}
})
));
}
else if (ev.request.url.includes("readable-stream-already-consumed.txt")) {
let r = new Response(
new ReadableStream({
start(controller) {
controller.enqueue(new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21]));
controller.close();
}
}));
r.blob();
ev.respondWith(r);
}
else if (ev.request.url.includes('user-pass')) {
ev.respondWith(new Response(ev.request.url));
}

Просмотреть файл

@ -74,6 +74,8 @@
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true],
["javascript.options.streams", true],
["dom.streams.enabled", true],
]}, runTest);
</script>
</pre>