Bug 1113522 - ExtendableMessageEvent.source should be ServiceWorker-aware. r=dom-worker-reviewers,edenchuang

ServiceWorkers aren't exposed as clients, so when we are sending a postMessage
from them, we need to capture their ServiceWorkerDescriptor and propagate that
instead of the client state.

Differential Revision: https://phabricator.services.mozilla.com/D213721
This commit is contained in:
Andrew Sutherland 2024-10-24 03:02:37 +00:00
parent 170e73042c
commit 6e5b348a26
14 changed files with 74 additions and 48 deletions

View File

@ -69,6 +69,21 @@ struct ClientInfoAndState
IPCClientState state;
};
// ExtendableMessageEvent.source can be one of a `ServiceWorker`, a
// `WindowClient`, or a `Client`. We use the same `Client` binding for both
// types of client (and they use the same underlying data source), but our
// `ServiceWorker` binding needs different data.
//
// Note that ClientPostMessageArgs only needs to handle messages originating
// from ServiceWorker instances because, until
// https://github.com/w3c/ServiceWorker/issues/955 is addressed, the Clients API
// is only exposed on ServiceWorkers, which means the source of such a message
// will always be a ServiceWorker.
union PostMessageSource {
ClientInfoAndState;
IPCServiceWorkerDescriptor;
};
struct ClientSourceExecutionReadyArgs
{
nsCString url;
@ -96,6 +111,7 @@ struct ClientNavigateArgs
struct ClientPostMessageArgs
{
ClonedMessageData clonedData;
// See PostMessageSource for why this can only be a ServiceWorker.
IPCServiceWorkerDescriptor serviceWorker;
};

View File

@ -18,7 +18,7 @@ protocol PServiceWorker
parent:
async Teardown();
async PostMessage(ClonedOrErrorMessageData aClonedData, ClientInfoAndState aSource);
async PostMessage(ClonedOrErrorMessageData aClonedData, PostMessageSource aSource);
child:
async __delete__();

View File

@ -252,9 +252,24 @@ void ServiceWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
return;
}
mActor->SendPostMessage(
clonedData,
ClientInfoAndState(clientInfo.ref().ToIPC(), clientState.ref().ToIPC()));
// If this global is a ServiceWorker, we need this global's
// ServiceWorkerDescriptor. While we normally try and normalize things
// through nsIGlobalObject, this is fairly one-off right now, so starting from
// worker-specific logic.
PostMessageSource source;
if (WorkerPrivate* wp = GetCurrentThreadWorkerPrivate()) {
if (wp->IsServiceWorker()) {
source = wp->GetServiceWorkerDescriptor().ToIPC();
} else {
source = ClientInfoAndState(clientInfo.ref().ToIPC(),
clientState.ref().ToIPC());
}
} else {
source =
ClientInfoAndState(clientInfo.ref().ToIPC(), clientState.ref().ToIPC());
}
mActor->SendPostMessage(clonedData, source);
}
void ServiceWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,

View File

@ -240,11 +240,8 @@ uint64_t ServiceWorkerInfo::GetNextID() const {
}
void ServiceWorkerInfo::PostMessage(RefPtr<ServiceWorkerCloneData>&& aData,
const ClientInfo& aClientInfo,
const ClientState& aClientState) {
mServiceWorkerPrivate->SendMessageEvent(
std::move(aData),
ClientInfoAndState(aClientInfo.ToIPC(), aClientState.ToIPC()));
const PostMessageSource& aSource) {
mServiceWorkerPrivate->SendMessageEvent(std::move(aData), aSource);
}
void ServiceWorkerInfo::UpdateInstalledTime() {

View File

@ -17,8 +17,7 @@
namespace mozilla::dom {
class ClientInfoAndState;
class ClientState;
class PostMessageSource;
class ServiceWorkerCloneData;
class ServiceWorkerPrivate;
@ -78,8 +77,7 @@ class ServiceWorkerInfo final : public nsIServiceWorkerInfo {
NS_DECL_NSISERVICEWORKERINFO
void PostMessage(RefPtr<ServiceWorkerCloneData>&& aData,
const ClientInfo& aClientInfo,
const ClientState& aClientState);
const PostMessageSource& aSource);
class ServiceWorkerPrivate* WorkerPrivate() const {
MOZ_ASSERT(mServiceWorkerPrivate);

View File

@ -55,6 +55,7 @@
#include "mozilla/dom/Response.h"
#include "mozilla/dom/RootedDictionary.h"
#include "mozilla/dom/SafeRefPtr.h"
#include "mozilla/dom/ServiceWorker.h"
#include "mozilla/dom/ServiceWorkerBinding.h"
#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
#include "mozilla/dom/WorkerCommon.h"
@ -1004,12 +1005,28 @@ class MessageEventOp final : public ExtendableEventOp {
init.mPorts = std::move(ports);
}
PostMessageSource& ipcSource =
mArgs.get_ServiceWorkerMessageEventOpArgs().source();
nsCString originSource;
switch (ipcSource.type()) {
case PostMessageSource::TClientInfoAndState:
originSource = ipcSource.get_ClientInfoAndState().info().url();
init.mSource.SetValue().SetAsClient() =
new Client(sgo, ipcSource.get_ClientInfoAndState());
break;
case PostMessageSource::TIPCServiceWorkerDescriptor:
originSource = ipcSource.get_IPCServiceWorkerDescriptor().scriptURL();
init.mSource.SetValue().SetAsServiceWorker() = ServiceWorker::Create(
sgo, ServiceWorkerDescriptor(
ipcSource.get_IPCServiceWorkerDescriptor()));
break;
default:
MOZ_ASSERT_UNREACHABLE("Unexpected source type");
return false;
}
nsCOMPtr<nsIURI> url;
nsresult result = NS_NewURI(getter_AddRefs(url),
mArgs.get_ServiceWorkerMessageEventOpArgs()
.clientInfoAndState()
.info()
.url());
nsresult result = NS_NewURI(getter_AddRefs(url), originSource);
if (NS_WARN_IF(NS_FAILED(result))) {
RejectAll(result);
rv.SuppressException();
@ -1033,9 +1050,6 @@ class MessageEventOp final : public ExtendableEventOp {
CopyUTF8toUTF16(origin, init.mOrigin);
init.mSource.SetValue().SetAsClient() = new Client(
sgo, mArgs.get_ServiceWorkerMessageEventOpArgs().clientInfoAndState());
rv.SuppressException();
RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
RefPtr<ExtendableMessageEvent> extendableEvent =

View File

@ -69,7 +69,7 @@ struct ServiceWorkerExtensionAPIEventOpArgs {
};
struct ServiceWorkerMessageEventOpArgs {
ClientInfoAndState clientInfoAndState;
PostMessageSource source;
ClonedOrErrorMessageData clonedData;
};

View File

@ -31,12 +31,11 @@ IPCResult ServiceWorkerParent::RecvTeardown() {
IPCResult ServiceWorkerParent::RecvPostMessage(
const ClonedOrErrorMessageData& aClonedData,
const ClientInfoAndState& aSource) {
const PostMessageSource& aSource) {
RefPtr<ServiceWorkerCloneData> data = new ServiceWorkerCloneData();
data->CopyFromClonedMessageData(aClonedData);
mProxy->PostMessage(std::move(data), ClientInfo(aSource.info()),
ClientState::FromIPC(aSource.state()));
mProxy->PostMessage(std::move(data), aSource);
return IPC_OK();
}

View File

@ -27,7 +27,7 @@ class ServiceWorkerParent final : public PServiceWorkerParent {
mozilla::ipc::IPCResult RecvPostMessage(
const ClonedOrErrorMessageData& aClonedData,
const ClientInfoAndState& aSource) override;
const PostMessageSource& aSource) override;
public:
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerParent, override);

View File

@ -816,8 +816,7 @@ nsresult ServiceWorkerPrivate::CheckScriptEvaluation(
}
nsresult ServiceWorkerPrivate::SendMessageEvent(
RefPtr<ServiceWorkerCloneData>&& aData,
const ClientInfoAndState& aClientInfoAndState) {
RefPtr<ServiceWorkerCloneData>&& aData, const PostMessageSource& aSource) {
AssertIsOnMainThread();
MOZ_ASSERT(aData);
@ -830,7 +829,7 @@ nsresult ServiceWorkerPrivate::SendMessageEvent(
}
ServiceWorkerMessageEventOpArgs args;
args.clientInfoAndState() = aClientInfoAndState;
args.source() = aSource;
if (!aData->BuildClonedMessageData(args.clonedData())) {
return NS_ERROR_DOM_DATA_CLONE_ERR;
}

View File

@ -38,7 +38,7 @@ class JSObjectHolder;
namespace dom {
class ClientInfoAndState;
class PostMessageSource;
class RemoteWorkerControllerChild;
class ServiceWorkerCloneData;
class ServiceWorkerInfo;
@ -83,7 +83,7 @@ class ServiceWorkerPrivate final : public RemoteWorkerObserver {
explicit ServiceWorkerPrivate(ServiceWorkerInfo* aInfo);
nsresult SendMessageEvent(RefPtr<ServiceWorkerCloneData>&& aData,
const ClientInfoAndState& aClientInfoAndState);
const PostMessageSource& aSource);
// This is used to validate the worker script and continue the installation
// process.

View File

@ -101,17 +101,15 @@ void ServiceWorkerProxy::RevokeActor(ServiceWorkerParent* aActor) {
}
void ServiceWorkerProxy::PostMessage(RefPtr<ServiceWorkerCloneData>&& aData,
const ClientInfo& aClientInfo,
const ClientState& aClientState) {
const PostMessageSource& aSource) {
AssertIsOnBackgroundThread();
RefPtr<ServiceWorkerProxy> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__,
[self, data = std::move(aData), aClientInfo, aClientState]() mutable {
__func__, [self, data = std::move(aData), aSource]() mutable {
if (!self->mInfo) {
return;
}
self->mInfo->PostMessage(std::move(data), aClientInfo, aClientState);
self->mInfo->PostMessage(std::move(data), aSource);
});
MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
}

View File

@ -12,8 +12,7 @@
namespace mozilla::dom {
class ClientInfo;
class ClientState;
class PostMessageSource;
class ServiceWorkerCloneData;
class ServiceWorkerInfo;
class ServiceWorkerParent;
@ -51,7 +50,7 @@ class ServiceWorkerProxy final {
void RevokeActor(ServiceWorkerParent* aActor);
void PostMessage(RefPtr<ServiceWorkerCloneData>&& aData,
const ClientInfo& aClientInfo, const ClientState& aState);
const PostMessageSource& aSource);
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ServiceWorkerProxy);
};

View File

@ -1,13 +1,4 @@
[extendable-message-event.https.html]
expected: TIMEOUT
[Post loopback extendable messages]
expected:
if os == "win": [TIMEOUT, NOTRUN]
TIMEOUT
[Post extendable messages among service workers]
expected: NOTRUN
[Post an extendable message from a nested client]
expected:
if os == "win": [PASS, NOTRUN, TIMEOUT]