Bug 1617822 - Implement FetchEvent.handled r=dom-workers-and-storage-reviewers,asuth

Differential Revision: https://phabricator.services.mozilla.com/D93483
This commit is contained in:
Yaron Tausky 2020-10-29 13:50:44 +00:00
Родитель 6e4797dff2
Коммит 5def06e77f
6 изменённых файлов: 49 добавлений и 23 удалений

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

@ -153,6 +153,14 @@ already_AddRefed<FetchEvent> FetchEvent::Constructor(
e->mRequest = aOptions.mRequest;
e->mClientId = aOptions.mClientId;
e->mResultingClientId = aOptions.mResultingClientId;
RefPtr<nsIGlobalObject> global = do_QueryObject(aGlobal.GetAsSupports());
MOZ_ASSERT(global);
ErrorResult rv;
e->mHandled = Promise::Create(global, rv);
if (rv.Failed()) {
rv.SuppressException();
return nullptr;
}
return e.forget();
}

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

@ -138,6 +138,7 @@ class FetchEvent final : public ExtendableEvent {
nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
RefPtr<Request> mRequest;
RefPtr<Promise> mHandled;
nsCString mScriptSpec;
nsCString mPreventDefaultScriptSpec;
nsString mClientId;
@ -184,6 +185,8 @@ class FetchEvent final : public ExtendableEvent {
aResultingClientId = mResultingClientId;
}
Promise* Handled() const { return mHandled; }
void RespondWith(JSContext* aCx, Promise& aArg, ErrorResult& aRv);
// Pull in the Event version of PreventDefault so we don't get

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

@ -131,6 +131,7 @@ class ExtendableEventKeepAliveHandler final
if (mCallback) {
mCallback->FinishedWithResult(mRejected ? Rejected : Resolved);
mCallback = nullptr;
}
Cleanup();
@ -167,6 +168,10 @@ class ExtendableEventKeepAliveHandler final
void Cleanup() {
MOZ_ASSERT(IsCurrentThreadRunningWorker());
if (mCallback) {
mCallback->FinishedWithResult(Rejected);
}
mSelfRef = nullptr;
mWorkerRef = nullptr;
mCallback = nullptr;
@ -1059,6 +1064,7 @@ class MOZ_STACK_CLASS FetchEventOp::AutoCancel {
}
MOZ_ASSERT(!mOwner->mRespondWithPromiseHolder.IsEmpty());
mOwner->mHandled->MaybeRejectWithNetworkError("AutoCancel"_ns);
mOwner->mRespondWithPromiseHolder.Reject(NS_ERROR_INTERCEPTION_FAILED,
__func__);
}
@ -1230,9 +1236,10 @@ void FetchEventOp::MaybeFinished() {
MOZ_ASSERT(!mPromiseHolder.IsEmpty());
if (mResult) {
// mRespondWithPromiseHolder should have been settled in
// {Resolve,Reject}Callback by now.
MOZ_DIAGNOSTIC_ASSERT(mRespondWithPromiseHolder.IsEmpty());
// It's possible that mRespondWithPromiseHolder wasn't settled. That happens
// if the worker was terminated before the respondWith promise settled.
mHandled = nullptr;
ServiceWorkerFetchEventOpResult result(
mResult.value() == Resolved ? NS_OK : NS_ERROR_FAILURE);
@ -1468,6 +1475,14 @@ void FetchEventOp::ResolvedCallback(JSContext* aCx,
autoCancel.Reset();
// https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm Step 26: If
// eventHandled is not null, then resolve eventHandled.
//
// mRespondWithPromiseHolder will resolve a MozPromise that will resolve on
// the worker owner's thread, so it's fine to resolve the mHandled promise now
// because content will not interfere with respondWith getting the Response to
// where it's going.
mHandled->MaybeResolveWithUndefined();
mRespondWithPromiseHolder.Resolve(
FetchEventRespondWithResult(
SynthesizeResponseArgs(ir, mRespondWithClosure.ref())),
@ -1497,6 +1512,11 @@ void FetchEventOp::RejectedCallback(JSContext* aCx,
AsyncLog(sourceSpec, line, column, "InterceptionRejectedResponseWithURL"_ns,
{std::move(requestURL), valueString});
// https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm Step 25.1:
// If eventHandled is not null, then reject eventHandled with a "NetworkError"
// DOMException in workerRealm.
mHandled->MaybeRejectWithNetworkError(
"FetchEvent.respondWith() Promise rejected"_ns);
mRespondWithPromiseHolder.Resolve(
FetchEventRespondWithResult(
CancelInterceptionArgs(NS_ERROR_INTERCEPTION_FAILED)),
@ -1581,6 +1601,7 @@ nsresult FetchEventOp::DispatchFetchEvent(JSContext* aCx,
FetchEvent::Constructor(globalObject, u"fetch"_ns, fetchEventInit);
fetchEvent->SetTrusted(true);
fetchEvent->PostInit(args.workerScriptSpec(), this);
mHandled = fetchEvent->Handled();
/**
* Step 5: Dispatch the FetchEvent to the worker's global object
@ -1590,6 +1611,7 @@ nsresult FetchEventOp::DispatchFetchEvent(JSContext* aCx,
bool dispatchFailed = NS_FAILED(rv) && rv != NS_ERROR_XPC_JS_THREW_EXCEPTION;
if (NS_WARN_IF(dispatchFailed)) {
mHandled = nullptr;
return rv;
}
@ -1629,11 +1651,19 @@ nsresult FetchEventOp::DispatchFetchEvent(JSContext* aCx,
"We don't support system-principal serviceworkers");
if (fetchEvent->DefaultPrevented(CallerType::NonSystem)) {
// https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm
// Step 24.1.1: If eventHandled is not null, then reject eventHandled with
// a "NetworkError" DOMException in workerRealm.
mHandled->MaybeRejectWithNetworkError(
"FetchEvent.preventDefault() called"_ns);
mRespondWithPromiseHolder.Resolve(
FetchEventRespondWithResult(
CancelInterceptionArgs(NS_ERROR_INTERCEPTION_FAILED)),
__func__);
} else {
// https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm
// Step 24.2: If eventHandled is not null, then resolve eventHandled.
mHandled->MaybeResolveWithUndefined();
mRespondWithPromiseHolder.Resolve(
FetchEventRespondWithResult(ResetInterceptionArgs()), __func__);
}

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

@ -168,6 +168,10 @@ class FetchEventOp final : public ExtendableEventOp,
// Worker thread only; set when `FetchEvent::RespondWith()` is called.
Maybe<FetchEventRespondWithClosure> mRespondWithClosure;
// Must be set to `nullptr` on the worker thread because `Promise`'s
// destructor must be called on the worker thread.
RefPtr<Promise> mHandled;
};
} // namespace dom

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

@ -15,6 +15,7 @@ interface FetchEvent : ExtendableEvent {
[SameObject, BinaryName="request_"] readonly attribute Request request;
readonly attribute DOMString clientId;
readonly attribute DOMString resultingClientId;
readonly attribute Promise<void> handled;
[Throws]
void respondWith(Promise<Response> r);

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

@ -1,20 +0,0 @@
[fetch-event-handled.https.html]
max-asserts: 2
[FetchEvent.handled should reject when the promise provided to respondWith() is rejected]
expected: FAIL
[FetchEvent.handled should resolve when the promise provided to respondWith() is resolved]
expected: FAIL
[FetchEvent.handled should reject when the promise provided to respondWith() is resolved to an invalid response]
expected: FAIL
[FetchEvent.handled should reject when respondWith() is not called and the event is canceled]
expected: FAIL
[FetchEvent.handled should resolve when respondWith() is not called for a sub-resource request]
expected: FAIL
[FetchEvent.handled should resolve when respondWith() is not called for a navigation request]
expected: FAIL