summaryrefslogtreecommitdiffstats
path: root/dom/ipc/jsactor/JSWindowActorProtocol.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/ipc/jsactor/JSWindowActorProtocol.cpp')
-rw-r--r--dom/ipc/jsactor/JSWindowActorProtocol.cpp380
1 files changed, 380 insertions, 0 deletions
diff --git a/dom/ipc/jsactor/JSWindowActorProtocol.cpp b/dom/ipc/jsactor/JSWindowActorProtocol.cpp
new file mode 100644
index 0000000000..a459d94204
--- /dev/null
+++ b/dom/ipc/jsactor/JSWindowActorProtocol.cpp
@@ -0,0 +1,380 @@
+/* -*- 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/. */
+
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/Event.h"
+#include "mozilla/dom/JSActorBinding.h"
+#include "mozilla/dom/JSActorService.h"
+#include "mozilla/dom/JSWindowActorBinding.h"
+#include "mozilla/dom/JSWindowActorChild.h"
+#include "mozilla/dom/JSWindowActorProtocol.h"
+#include "mozilla/dom/PContent.h"
+#include "mozilla/dom/WindowGlobalChild.h"
+
+#include "mozilla/extensions/MatchPattern.h"
+#include "nsContentUtils.h"
+#include "JSActorProtocolUtils.h"
+
+namespace mozilla::dom {
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(JSWindowActorProtocol)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(JSWindowActorProtocol)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSWindowActorProtocol)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION(JSWindowActorProtocol)
+
+/* static */ already_AddRefed<JSWindowActorProtocol>
+JSWindowActorProtocol::FromIPC(const JSWindowActorInfo& aInfo) {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
+
+ RefPtr<JSWindowActorProtocol> proto = new JSWindowActorProtocol(aInfo.name());
+ JSActorProtocolUtils::FromIPCShared(proto, aInfo);
+
+ // Content processes cannot load chrome browsing contexts, so this flag is
+ // irrelevant and not propagated.
+ proto->mIncludeChrome = false;
+ proto->mAllFrames = aInfo.allFrames();
+ proto->mMatches = aInfo.matches().Clone();
+ proto->mMessageManagerGroups = aInfo.messageManagerGroups().Clone();
+
+ proto->mChild.mEvents.SetCapacity(aInfo.events().Length());
+ for (auto& ipc : aInfo.events()) {
+ auto event = proto->mChild.mEvents.AppendElement();
+ event->mName.Assign(ipc.name());
+ event->mFlags.mCapture = ipc.capture();
+ event->mFlags.mInSystemGroup = ipc.systemGroup();
+ event->mFlags.mAllowUntrustedEvents = ipc.allowUntrusted();
+ if (ipc.passive()) {
+ event->mPassive.Construct(ipc.passive().value());
+ }
+ event->mCreateActor = ipc.createActor();
+ }
+
+ return proto.forget();
+}
+
+JSWindowActorInfo JSWindowActorProtocol::ToIPC() {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
+
+ JSWindowActorInfo info;
+ JSActorProtocolUtils::ToIPCShared(info, this);
+
+ info.allFrames() = mAllFrames;
+ info.matches() = mMatches.Clone();
+ info.messageManagerGroups() = mMessageManagerGroups.Clone();
+
+ info.events().SetCapacity(mChild.mEvents.Length());
+ for (auto& event : mChild.mEvents) {
+ auto ipc = info.events().AppendElement();
+ ipc->name().Assign(event.mName);
+ ipc->capture() = event.mFlags.mCapture;
+ ipc->systemGroup() = event.mFlags.mInSystemGroup;
+ ipc->allowUntrusted() = event.mFlags.mAllowUntrustedEvents;
+ if (event.mPassive.WasPassed()) {
+ ipc->passive() = Some(event.mPassive.Value());
+ }
+ ipc->createActor() = event.mCreateActor;
+ }
+
+ return info;
+}
+
+already_AddRefed<JSWindowActorProtocol>
+JSWindowActorProtocol::FromWebIDLOptions(const nsACString& aName,
+ const WindowActorOptions& aOptions,
+ ErrorResult& aRv) {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
+
+ RefPtr<JSWindowActorProtocol> proto = new JSWindowActorProtocol(aName);
+ if (!JSActorProtocolUtils::FromWebIDLOptionsShared(proto, aOptions, aRv)) {
+ return nullptr;
+ }
+
+ proto->mAllFrames = aOptions.mAllFrames;
+ proto->mIncludeChrome = aOptions.mIncludeChrome;
+
+ if (aOptions.mMatches.WasPassed()) {
+ MOZ_ASSERT(aOptions.mMatches.Value().Length());
+ proto->mMatches = aOptions.mMatches.Value();
+ }
+
+ if (aOptions.mMessageManagerGroups.WasPassed()) {
+ proto->mMessageManagerGroups = aOptions.mMessageManagerGroups.Value();
+ }
+
+ // For each event declared in the source dictionary, initialize the
+ // corresponding event declaration entry in the JSWindowActorProtocol.
+ if (aOptions.mChild.WasPassed() &&
+ aOptions.mChild.Value().mEvents.WasPassed()) {
+ auto& entries = aOptions.mChild.Value().mEvents.Value().Entries();
+ proto->mChild.mEvents.SetCapacity(entries.Length());
+
+ for (auto& entry : entries) {
+ // We don't support the mOnce field, as it doesn't work well in this
+ // environment. For now, throw an error in that case.
+ if (entry.mValue.mOnce) {
+ aRv.ThrowNotSupportedError("mOnce is not supported");
+ return nullptr;
+ }
+
+ // Add the EventDecl to our list of events.
+ EventDecl* evt = proto->mChild.mEvents.AppendElement();
+ evt->mName = entry.mKey;
+ evt->mFlags.mCapture = entry.mValue.mCapture;
+ evt->mFlags.mInSystemGroup = entry.mValue.mMozSystemGroup;
+ evt->mFlags.mAllowUntrustedEvents =
+ entry.mValue.mWantUntrusted.WasPassed()
+ ? entry.mValue.mWantUntrusted.Value()
+ : false;
+ if (entry.mValue.mPassive.WasPassed()) {
+ evt->mPassive.Construct(entry.mValue.mPassive.Value());
+ }
+ evt->mCreateActor = entry.mValue.mCreateActor;
+ }
+ }
+
+ return proto.forget();
+}
+
+/**
+ * This listener only listens for events for the child side of the protocol.
+ * This will work in both content and parent processes.
+ */
+NS_IMETHODIMP JSWindowActorProtocol::HandleEvent(Event* aEvent) {
+ MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+
+ // Determine which inner window we're associated with, and get its
+ // WindowGlobalChild actor.
+ EventTarget* target = aEvent->GetOriginalTarget();
+ if (NS_WARN_IF(!target)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsPIDOMWindowInner> inner =
+ do_QueryInterface(target->GetOwnerGlobal());
+ if (NS_WARN_IF(!inner)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<WindowGlobalChild> wgc = inner->GetWindowGlobalChild();
+ if (NS_WARN_IF(!wgc)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (aEvent->ShouldIgnoreChromeEventTargetListener()) {
+ return NS_OK;
+ }
+
+ // Ensure our actor is present.
+ RefPtr<JSActor> actor = wgc->GetExistingActor(mName);
+ if (!actor) {
+ // Check if we're supposed to create the actor when this event is fired.
+ bool createActor = true;
+ nsAutoString typeStr;
+ aEvent->GetType(typeStr);
+ for (auto& event : mChild.mEvents) {
+ if (event.mName == typeStr) {
+ createActor = event.mCreateActor;
+ break;
+ }
+ }
+
+ // If we're supposed to create the actor, call GetActor to cause it to be
+ // created.
+ if (createActor) {
+ AutoJSAPI jsapi;
+ jsapi.Init();
+ actor = wgc->GetActor(jsapi.cx(), mName, IgnoreErrors());
+ }
+ }
+ if (!actor || NS_WARN_IF(!actor->GetWrapperPreserveColor())) {
+ return NS_OK;
+ }
+
+ // Build our event listener & call it.
+ JS::Rooted<JSObject*> global(RootingCx(),
+ JS::GetNonCCWObjectGlobal(actor->GetWrapper()));
+ RefPtr<EventListener> eventListener =
+ new EventListener(actor->GetWrapper(), global, nullptr, nullptr);
+ eventListener->HandleEvent(*aEvent, "JSWindowActorProtocol::HandleEvent");
+ return NS_OK;
+}
+
+NS_IMETHODIMP JSWindowActorProtocol::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData) {
+ MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+
+ nsCOMPtr<nsPIDOMWindowInner> inner = do_QueryInterface(aSubject);
+ RefPtr<WindowGlobalChild> wgc;
+
+ if (!inner) {
+ nsCOMPtr<nsPIDOMWindowOuter> outer = do_QueryInterface(aSubject);
+ if (NS_WARN_IF(!outer)) {
+ nsContentUtils::LogSimpleConsoleError(
+ NS_ConvertUTF8toUTF16(nsPrintfCString(
+ "JSWindowActor %s: expected window subject for topic '%s'.",
+ mName.get(), aTopic)),
+ "JSActor"_ns,
+ /* aFromPrivateWindow */ false,
+ /* aFromChromeContext */ true);
+ return NS_ERROR_FAILURE;
+ }
+ if (NS_WARN_IF(!outer->GetCurrentInnerWindow())) {
+ return NS_ERROR_FAILURE;
+ }
+ wgc = outer->GetCurrentInnerWindow()->GetWindowGlobalChild();
+ } else {
+ wgc = inner->GetWindowGlobalChild();
+ }
+
+ if (NS_WARN_IF(!wgc)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Ensure our actor is present.
+ AutoJSAPI jsapi;
+ jsapi.Init();
+ RefPtr<JSActor> actor = wgc->GetActor(jsapi.cx(), mName, IgnoreErrors());
+ if (!actor || NS_WARN_IF(!actor->GetWrapperPreserveColor())) {
+ return NS_OK;
+ }
+
+ // Build a observer callback.
+ JS::Rooted<JSObject*> global(jsapi.cx(),
+ JS::GetNonCCWObjectGlobal(actor->GetWrapper()));
+ RefPtr<MozObserverCallback> observerCallback =
+ new MozObserverCallback(actor->GetWrapper(), global, nullptr, nullptr);
+ observerCallback->Observe(aSubject, nsDependentCString(aTopic),
+ aData ? nsDependentString(aData) : VoidString());
+ return NS_OK;
+}
+
+void JSWindowActorProtocol::RegisterListenersFor(EventTarget* aTarget) {
+ EventListenerManager* elm = aTarget->GetOrCreateListenerManager();
+
+ for (auto& event : mChild.mEvents) {
+ elm->AddEventListenerByType(EventListenerHolder(this), event.mName,
+ event.mFlags, event.mPassive);
+ }
+}
+
+void JSWindowActorProtocol::UnregisterListenersFor(EventTarget* aTarget) {
+ EventListenerManager* elm = aTarget->GetOrCreateListenerManager();
+
+ for (auto& event : mChild.mEvents) {
+ elm->RemoveEventListenerByType(EventListenerHolder(this), event.mName,
+ event.mFlags);
+ }
+}
+
+void JSWindowActorProtocol::AddObservers() {
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ for (auto& topic : mChild.mObservers) {
+ // This makes the observer service hold an owning reference to the
+ // JSWindowActorProtocol. The JSWindowActorProtocol objects will be living
+ // for the full lifetime of the content process, thus the extra strong
+ // referencec doesn't have a negative impact.
+ os->AddObserver(this, topic.get(), false);
+ }
+}
+
+void JSWindowActorProtocol::RemoveObservers() {
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ for (auto& topic : mChild.mObservers) {
+ os->RemoveObserver(this, topic.get());
+ }
+}
+
+extensions::MatchPatternSetCore* JSWindowActorProtocol::GetURIMatcher() {
+ // If we've already created the pattern set, return it.
+ if (mURIMatcher || mMatches.IsEmpty()) {
+ return mURIMatcher;
+ }
+
+ nsTArray<RefPtr<extensions::MatchPatternCore>> patterns(mMatches.Length());
+ for (const nsString& pattern : mMatches) {
+ patterns.AppendElement(new extensions::MatchPatternCore(
+ pattern, false, false, IgnoreErrors()));
+ }
+ mURIMatcher = new extensions::MatchPatternSetCore(std::move(patterns));
+ return mURIMatcher;
+}
+
+bool JSWindowActorProtocol::RemoteTypePrefixMatches(
+ const nsDependentCSubstring& aRemoteType) {
+ for (auto& remoteType : mRemoteTypes) {
+ if (StringBeginsWith(aRemoteType, remoteType)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool JSWindowActorProtocol::MessageManagerGroupMatches(
+ BrowsingContext* aBrowsingContext) {
+ BrowsingContext* top = aBrowsingContext->Top();
+ for (auto& group : mMessageManagerGroups) {
+ if (group == top->GetMessageManagerGroup()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool JSWindowActorProtocol::Matches(BrowsingContext* aBrowsingContext,
+ nsIURI* aURI, const nsACString& aRemoteType,
+ ErrorResult& aRv) {
+ MOZ_ASSERT(aBrowsingContext, "DocShell without a BrowsingContext!");
+ MOZ_ASSERT(aURI, "Must have URI!");
+
+ if (!mAllFrames && aBrowsingContext->GetParent()) {
+ aRv.ThrowNotSupportedError(nsPrintfCString(
+ "Window protocol '%s' doesn't match subframes", mName.get()));
+ return false;
+ }
+
+ if (!mIncludeChrome && !aBrowsingContext->IsContent()) {
+ aRv.ThrowNotSupportedError(nsPrintfCString(
+ "Window protocol '%s' doesn't match chrome browsing contexts",
+ mName.get()));
+ return false;
+ }
+
+ if (!mRemoteTypes.IsEmpty() &&
+ !RemoteTypePrefixMatches(RemoteTypePrefix(aRemoteType))) {
+ aRv.ThrowNotSupportedError(
+ nsPrintfCString("Window protocol '%s' doesn't match remote type '%s'",
+ mName.get(), PromiseFlatCString(aRemoteType).get()));
+ return false;
+ }
+
+ if (!mMessageManagerGroups.IsEmpty() &&
+ !MessageManagerGroupMatches(aBrowsingContext)) {
+ aRv.ThrowNotSupportedError(nsPrintfCString(
+ "Window protocol '%s' doesn't match message manager group",
+ mName.get()));
+ return false;
+ }
+
+ if (extensions::MatchPatternSetCore* uriMatcher = GetURIMatcher()) {
+ if (!uriMatcher->Matches(aURI)) {
+ aRv.ThrowNotSupportedError(nsPrintfCString(
+ "Window protocol '%s' doesn't match uri %s", mName.get(),
+ nsContentUtils::TruncatedURLForDisplay(aURI).get()));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace mozilla::dom