Files
tubestation/dom/ipc/jsactor/JSWindowActorProtocol.cpp
Andrew McCreight 4b3d63ff45 Bug 1839637, part 2 - Stop warning in HandleEvent when owner is not an inner window. r=nika
After the fix from the previous patch, this warning will be the third most common
warning, with over 41,000 instances, on Linux 64. I'm not sure if the owner global is
null, or if it is something like a BackstagePass.

Differential Revision: https://phabricator.services.mozilla.com/D212736
2024-06-05 22:38:10 +00:00

381 lines
13 KiB
C++

/* -*- 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 (!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