Bug 1231213 - Implement ServiceWorkerOp and its subclasses. r=asuth

Differential Revision: https://phabricator.services.mozilla.com/D26172

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Perry Jiang 2019-08-15 17:27:08 +00:00
parent 5b603200f7
commit bbc5dad9a8
9 changed files with 1991 additions and 23 deletions

View File

@ -18,6 +18,7 @@
#include "mozilla/Unused.h"
#include "mozilla/dom/RemoteWorkerChild.h"
#include "mozilla/dom/RemoteWorkerService.h"
#include "mozilla/dom/ServiceWorkerOp.h"
#include "mozilla/dom/WorkerCommon.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/IPCStreamUtils.h"
@ -54,6 +55,7 @@ nsresult GetIPCSynthesizeResponseArgs(
void FetchEventOpProxyChild::Initialize(
const ServiceWorkerFetchEventOpArgs& aArgs) {
MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
MOZ_ASSERT(!mOp);
mInternalRequest = new InternalRequest(aArgs.internalRequest());
@ -77,6 +79,60 @@ void FetchEventOpProxyChild::Initialize(
Unused << self->Send__delete__(self, aResult);
};
RefPtr<FetchEventOp> op = ServiceWorkerOp::Create(aArgs, std::move(callback))
.template downcast<FetchEventOp>();
MOZ_ASSERT(op);
op->SetActor(this);
mOp = op;
op->GetRespondWithPromise()
->Then(GetCurrentThreadSerialEventTarget(), __func__,
[self = std::move(self)](
FetchEventRespondWithPromise::ResolveOrRejectValue&& aResult) {
self->mRespondWithPromiseRequestHolder.Complete();
if (NS_WARN_IF(aResult.IsReject())) {
MOZ_ASSERT(NS_FAILED(aResult.RejectValue()));
Unused << self->SendRespondWith(
CancelInterceptionArgs(aResult.RejectValue()));
return;
}
auto& result = aResult.ResolveValue();
if (result.is<SynthesizeResponseArgs>()) {
IPCSynthesizeResponseArgs ipcArgs;
UniquePtr<AutoIPCStream> autoBodyStream =
MakeUnique<AutoIPCStream>();
UniquePtr<AutoIPCStream> autoAlternativeBodyStream =
MakeUnique<AutoIPCStream>();
nsresult rv = GetIPCSynthesizeResponseArgs(
&ipcArgs, result.extract<SynthesizeResponseArgs>(),
autoBodyStream, autoAlternativeBodyStream);
if (NS_WARN_IF(NS_FAILED(rv))) {
Unused << self->SendRespondWith(CancelInterceptionArgs(rv));
return;
}
Unused << self->SendRespondWith(ipcArgs);
autoBodyStream->TakeOptionalValue();
autoAlternativeBodyStream->TakeOptionalValue();
} else if (result.is<ResetInterceptionArgs>()) {
Unused << self->SendRespondWith(
result.extract<ResetInterceptionArgs>());
} else {
Unused << self->SendRespondWith(
result.extract<CancelInterceptionArgs>());
}
})
->Track(mRespondWithPromiseRequestHolder);
manager->MaybeStartOp(std::move(op));
}
RefPtr<InternalRequest> FetchEventOpProxyChild::ExtractInternalRequest() {
@ -89,6 +145,9 @@ RefPtr<InternalRequest> FetchEventOpProxyChild::ExtractInternalRequest() {
void FetchEventOpProxyChild::ActorDestroy(ActorDestroyReason) {
Unused << NS_WARN_IF(mRespondWithPromiseRequestHolder.Exists());
mRespondWithPromiseRequestHolder.DisconnectIfExists();
mOp->RevokeActor(this);
mOp = nullptr;
}
} // namespace dom

View File

@ -9,6 +9,7 @@
#include "nsISupportsImpl.h"
#include "ServiceWorkerOp.h"
#include "ServiceWorkerOpPromise.h"
#include "mozilla/RefPtr.h"
#include "mozilla/dom/InternalRequest.h"
@ -41,6 +42,8 @@ class FetchEventOpProxyChild final : public PFetchEventOpProxyChild {
MozPromiseRequestHolder<FetchEventRespondWithPromise>
mRespondWithPromiseRequestHolder;
RefPtr<FetchEventOp> mOp;
// Initialized on RemoteWorkerService::Thread, read on a worker thread.
RefPtr<InternalRequest> mInternalRequest;
};

View File

@ -6,6 +6,8 @@
#include "ServiceWorkerEvents.h"
#include <utility>
#include "nsAutoPtr.h"
#include "nsContentUtils.h"
#include "nsIConsoleReportCollector.h"
@ -41,6 +43,7 @@
#include "mozilla/dom/PushMessageDataBinding.h"
#include "mozilla/dom/PushUtil.h"
#include "mozilla/dom/Request.h"
#include "mozilla/dom/ServiceWorkerOp.h"
#include "mozilla/dom/TypedArray.h"
#include "mozilla/dom/Response.h"
#include "mozilla/dom/WorkerScope.h"
@ -136,6 +139,14 @@ void FetchEvent::PostInit(
mScriptSpec.Assign(aScriptSpec);
}
void FetchEvent::PostInit(const nsACString& aScriptSpec,
RefPtr<FetchEventOp> aRespondWithHandler) {
MOZ_ASSERT(aRespondWithHandler);
mScriptSpec.Assign(aScriptSpec);
mRespondWithHandler = std::move(aRespondWithHandler);
}
/*static*/
already_AddRefed<FetchEvent> FetchEvent::Constructor(
const GlobalObject& aGlobal, const nsAString& aType,
@ -795,11 +806,21 @@ void FetchEvent::RespondWith(JSContext* aCx, Promise& aArg, ErrorResult& aRv) {
StopImmediatePropagation();
mWaitToRespond = true;
RefPtr<RespondWithHandler> handler = new RespondWithHandler(
mChannel, mRegistration, mRequest->Mode(), ir->IsClientRequest(),
mRequest->Redirect(), mScriptSpec, NS_ConvertUTF8toUTF16(requestURL),
ir->GetFragment(), spec, line, column);
aArg.AppendNativeHandler(handler);
if (mChannel) {
RefPtr<RespondWithHandler> handler = new RespondWithHandler(
mChannel, mRegistration, mRequest->Mode(), ir->IsClientRequest(),
mRequest->Redirect(), mScriptSpec, NS_ConvertUTF8toUTF16(requestURL),
ir->GetFragment(), spec, line, column);
aArg.AppendNativeHandler(handler);
} else {
MOZ_ASSERT(mRespondWithHandler);
mRespondWithHandler->RespondWithCalledAt(spec, line, column);
aArg.AppendNativeHandler(mRespondWithHandler);
mRespondWithHandler = nullptr;
}
if (!WaitOnPromise(aArg)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
@ -838,9 +859,16 @@ void FetchEvent::ReportCanceled() {
// nsString requestURL;
// CopyUTF8toUTF16(url, requestURL);
::AsyncLog(mChannel.get(), mPreventDefaultScriptSpec,
mPreventDefaultLineNumber, mPreventDefaultColumnNumber,
NS_LITERAL_CSTRING("InterceptionCanceledWithURL"), requestURL);
if (mChannel) {
::AsyncLog(mChannel.get(), mPreventDefaultScriptSpec,
mPreventDefaultLineNumber, mPreventDefaultColumnNumber,
NS_LITERAL_CSTRING("InterceptionCanceledWithURL"), requestURL);
} else {
mRespondWithHandler->ReportCanceled(mPreventDefaultScriptSpec,
mPreventDefaultLineNumber,
mPreventDefaultColumnNumber);
mRespondWithHandler = nullptr;
}
}
namespace {

View File

@ -26,6 +26,7 @@ namespace dom {
class Blob;
class Client;
class FetchEventOp;
class MessagePort;
struct PushEventInit;
class Request;
@ -50,6 +51,15 @@ class CancelChannelRunnable final : public Runnable {
NS_IMETHOD Run() override;
};
enum ExtendableEventResult { Rejected = 0, Resolved };
class ExtendableEventCallback {
public:
virtual void FinishedWithResult(ExtendableEventResult aResult) = 0;
NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
};
class ExtendableEvent : public Event {
public:
class ExtensionsHandler {
@ -66,7 +76,7 @@ class ExtendableEvent : public Event {
bool WaitOnPromise(Promise& aPromise);
explicit ExtendableEvent(mozilla::dom::EventTarget* aOwner);
~ExtendableEvent() {}
~ExtendableEvent() = default;
public:
NS_DECL_ISUPPORTS_INHERITED
@ -102,6 +112,7 @@ class ExtendableEvent : public Event {
};
class FetchEvent final : public ExtendableEvent {
RefPtr<FetchEventOp> mRespondWithHandler;
nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
RefPtr<Request> mRequest;
@ -132,6 +143,9 @@ class FetchEvent final : public ExtendableEvent {
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
const nsACString& aScriptSpec);
void PostInit(const nsACString& aScriptSpec,
RefPtr<FetchEventOp> aRespondWithHandler);
static already_AddRefed<FetchEvent> Constructor(
const GlobalObject& aGlobal, const nsAString& aType,
const FetchEventInit& aOptions, ErrorResult& aRv);
@ -153,10 +167,6 @@ class FetchEvent final : public ExtendableEvent {
void RespondWith(JSContext* aCx, Promise& aArg, ErrorResult& aRv);
already_AddRefed<Promise> ForwardTo(const nsAString& aUrl);
already_AddRefed<Promise> Default();
// Pull in the Event version of PreventDefault so we don't get
// shadowing warnings.
using Event::PreventDefault;
@ -199,7 +209,7 @@ class PushEvent final : public ExtendableEvent {
protected:
explicit PushEvent(mozilla::dom::EventTarget* aOwner);
~PushEvent() {}
~PushEvent() = default;
public:
NS_DECL_ISUPPORTS_INHERITED

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,172 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_serviceworkerop_h__
#define mozilla_dom_serviceworkerop_h__
#include <functional>
#include "nsISupportsImpl.h"
#include "ServiceWorkerEvents.h"
#include "ServiceWorkerOpPromise.h"
#include "mozilla/Attributes.h"
#include "mozilla/Maybe.h"
#include "mozilla/RefPtr.h"
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/dom/RemoteWorkerChild.h"
#include "mozilla/dom/ServiceWorkerOpArgs.h"
#include "mozilla/dom/WorkerRunnable.h"
namespace mozilla {
namespace dom {
class FetchEventOpProxyChild;
class ServiceWorkerOp : public RemoteWorkerChild::Op {
public:
// `aCallback` will be called when the operation completes or is canceled.
static already_AddRefed<ServiceWorkerOp> Create(
const ServiceWorkerOpArgs& aArgs,
std::function<void(const ServiceWorkerOpResult&)>&& aCallback);
ServiceWorkerOp(
const ServiceWorkerOpArgs& aArgs,
std::function<void(const ServiceWorkerOpResult&)>&& aCallback);
ServiceWorkerOp(const ServiceWorkerOp&) = delete;
ServiceWorkerOp& operator=(const ServiceWorkerOp&) = delete;
ServiceWorkerOp(ServiceWorkerOp&&) = default;
ServiceWorkerOp& operator=(ServiceWorkerOp&&) = default;
// Returns `true` if the operation has started and `false` otherwise.
bool MaybeStart(RemoteWorkerChild* aOwner,
RemoteWorkerChild::State& aState) final;
void Cancel() final;
protected:
~ServiceWorkerOp();
bool Started() const;
bool IsTerminationOp() const;
// Override to provide a runnable that's not a `ServiceWorkerOpRunnable.`
virtual RefPtr<WorkerRunnable> GetRunnable(WorkerPrivate* aWorkerPrivate);
virtual bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) = 0;
// Override to reject any additional MozPromises that subclasses may contain.
virtual void RejectAll(nsresult aStatus);
ServiceWorkerOpArgs mArgs;
// Subclasses must settle this promise when appropriate.
MozPromiseHolder<ServiceWorkerOpPromise> mPromiseHolder;
private:
class ServiceWorkerOpRunnable;
bool mStarted = false;
};
class ExtendableEventOp : public ServiceWorkerOp,
public ExtendableEventCallback {
using ServiceWorkerOp::ServiceWorkerOp;
protected:
~ExtendableEventOp() = default;
void FinishedWithResult(ExtendableEventResult aResult) override;
};
class FetchEventOp final : public ExtendableEventOp,
public PromiseNativeHandler {
using ExtendableEventOp::ExtendableEventOp;
public:
NS_DECL_THREADSAFE_ISUPPORTS
/**
* This must be called once and only once before the first call to
* `MaybeStart()`; `aActor` will be used for `AsyncLog()` and
* `ReportCanceled().`
*/
void SetActor(RefPtr<FetchEventOpProxyChild> aActor);
void RevokeActor(FetchEventOpProxyChild* aActor);
// This must be called at most once before the first call to `MaybeStart().`
RefPtr<FetchEventRespondWithPromise> GetRespondWithPromise();
// This must be called when `FetchEvent::RespondWith()` is called.
void RespondWithCalledAt(const nsCString& aRespondWithScriptSpec,
uint32_t aRespondWithLineNumber,
uint32_t aRespondWithColumnNumber);
void ReportCanceled(const nsCString& aPreventDefaultScriptSpec,
uint32_t aPreventDefaultLineNumber,
uint32_t aPreventDefaultColumnNumber);
private:
class AutoCancel;
~FetchEventOp();
bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
void RejectAll(nsresult aStatus) override;
void FinishedWithResult(ExtendableEventResult aResult) override;
/**
* `{Resolved,Reject}Callback()` are use to handle the
* `FetchEvent::RespondWith()` promise.
*/
void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
void MaybeFinished();
// Requires mRespondWithClosure to be non-empty.
void AsyncLog(const nsCString& aMessageName, nsTArray<nsString> aParams);
void AsyncLog(const nsCString& aScriptSpec, uint32_t aLineNumber,
uint32_t aColumnNumber, const nsCString& aMessageName,
nsTArray<nsString> aParams);
void GetRequestURL(nsAString& aOutRequestURL);
// A failure code means that the dispatch failed.
nsresult DispatchFetchEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
// Worker Launcher thread only. Used for `AsyncLog().`
RefPtr<FetchEventOpProxyChild> mActor;
/**
* Created on the Worker Launcher thread and settled on the worker thread.
* If this isn't settled before `mPromiseHolder` (which it should be),
* `FetchEventOpChild` will cancel the intercepted network request.
*/
MozPromiseHolder<FetchEventRespondWithPromise> mRespondWithPromiseHolder;
// Worker thread only.
Maybe<ExtendableEventResult> mResult;
bool mPostDispatchChecksDone = false;
// Worker thread only; set when `FetchEvent::RespondWith()` is called.
Maybe<FetchEventRespondWithClosure> mRespondWithClosure;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_serviceworkerop_h__

View File

@ -195,15 +195,6 @@ nsresult ServiceWorkerPrivate::CheckScriptEvaluation(
namespace {
enum ExtendableEventResult { Rejected = 0, Resolved };
class ExtendableEventCallback {
public:
virtual void FinishedWithResult(ExtendableEventResult aResult) = 0;
NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
};
class KeepAliveHandler final : public ExtendableEvent::ExtensionsHandler,
public PromiseNativeHandler {
// This class manages lifetime extensions added by calling WaitUntil()

View File

@ -25,6 +25,7 @@ EXPORTS.mozilla.dom += [
'ServiceWorkerManager.h',
'ServiceWorkerManagerChild.h',
'ServiceWorkerManagerParent.h',
'ServiceWorkerOp.h',
'ServiceWorkerOpPromise.h',
'ServiceWorkerRegistrar.h',
'ServiceWorkerRegistration.h',
@ -61,6 +62,7 @@ UNIFIED_SOURCES += [
'ServiceWorkerManagerChild.cpp',
'ServiceWorkerManagerParent.cpp',
'ServiceWorkerManagerService.cpp',
'ServiceWorkerOp.cpp',
'ServiceWorkerParent.cpp',
'ServiceWorkerPrivate.cpp',
'ServiceWorkerProxy.cpp',

View File

@ -26,7 +26,9 @@ namespace mozilla {
namespace dom {
class ErrorValue;
class FetchEventOpProxyChild;
class RemoteWorkerData;
class ServiceWorkerOp;
class WeakWorkerRef;
class WorkerErrorReport;
class WorkerPrivate;
@ -34,7 +36,9 @@ class WorkerPrivate;
class RemoteWorkerChild final
: public SupportsThreadSafeWeakPtr<RemoteWorkerChild>,
public PRemoteWorkerChild {
friend class FetchEventOpProxyChild;
friend class PRemoteWorkerChild;
friend class ServiceWorkerOp;
public:
MOZ_DECLARE_THREADSAFEWEAKREFERENCE_TYPENAME(RemoteWorkerChild)