JSWindowActorProtocol.cpp (12730B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 3 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 4 /* This Source Code Form is subject to the terms of the Mozilla Public 5 * License, v. 2.0. If a copy of the MPL was not distributed with this 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 7 8 #include "mozilla/dom/JSWindowActorProtocol.h" 9 10 #include "JSActorProtocolUtils.h" 11 #include "mozilla/dom/ContentParent.h" 12 #include "mozilla/dom/Event.h" 13 #include "mozilla/dom/JSActorBinding.h" 14 #include "mozilla/dom/JSActorService.h" 15 #include "mozilla/dom/JSWindowActorBinding.h" 16 #include "mozilla/dom/JSWindowActorChild.h" 17 #include "mozilla/dom/PContent.h" 18 #include "mozilla/dom/WindowGlobalChild.h" 19 #include "mozilla/extensions/MatchPattern.h" 20 #include "nsContentUtils.h" 21 22 namespace mozilla::dom { 23 24 NS_IMPL_CYCLE_COLLECTING_ADDREF(JSWindowActorProtocol) 25 NS_IMPL_CYCLE_COLLECTING_RELEASE(JSWindowActorProtocol) 26 27 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSWindowActorProtocol) 28 NS_INTERFACE_MAP_ENTRY(nsIObserver) 29 NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) 30 NS_INTERFACE_MAP_END 31 32 NS_IMPL_CYCLE_COLLECTION(JSWindowActorProtocol) 33 34 /* static */ already_AddRefed<JSWindowActorProtocol> 35 JSWindowActorProtocol::FromIPC(const JSWindowActorInfo& aInfo) { 36 MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess()); 37 38 RefPtr<JSWindowActorProtocol> proto = new JSWindowActorProtocol(aInfo.name()); 39 JSActorProtocolUtils::FromIPCShared(proto, aInfo); 40 41 // Content processes cannot load chrome browsing contexts, so this flag is 42 // irrelevant and not propagated. 43 proto->mIncludeChrome = false; 44 proto->mAllFrames = aInfo.allFrames(); 45 proto->mMatches = aInfo.matches().Clone(); 46 proto->mMessageManagerGroups = aInfo.messageManagerGroups().Clone(); 47 48 proto->mChild.mEvents.SetCapacity(aInfo.events().Length()); 49 for (auto& ipc : aInfo.events()) { 50 auto event = proto->mChild.mEvents.AppendElement(); 51 event->mName.Assign(ipc.name()); 52 event->mFlags.mCapture = ipc.capture(); 53 event->mFlags.mInSystemGroup = ipc.systemGroup(); 54 event->mFlags.mAllowUntrustedEvents = ipc.allowUntrusted(); 55 if (ipc.passive()) { 56 event->mPassive.Construct(ipc.passive().value()); 57 } 58 event->mCreateActor = ipc.createActor(); 59 } 60 61 return proto.forget(); 62 } 63 64 JSWindowActorInfo JSWindowActorProtocol::ToIPC() { 65 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess()); 66 67 JSWindowActorInfo info; 68 JSActorProtocolUtils::ToIPCShared(info, this); 69 70 info.allFrames() = mAllFrames; 71 info.matches() = mMatches.Clone(); 72 info.messageManagerGroups() = mMessageManagerGroups.Clone(); 73 74 info.events().SetCapacity(mChild.mEvents.Length()); 75 for (auto& event : mChild.mEvents) { 76 auto ipc = info.events().AppendElement(); 77 ipc->name().Assign(event.mName); 78 ipc->capture() = event.mFlags.mCapture; 79 ipc->systemGroup() = event.mFlags.mInSystemGroup; 80 ipc->allowUntrusted() = event.mFlags.mAllowUntrustedEvents; 81 if (event.mPassive.WasPassed()) { 82 ipc->passive() = Some(event.mPassive.Value()); 83 } 84 ipc->createActor() = event.mCreateActor; 85 } 86 87 return info; 88 } 89 90 already_AddRefed<JSWindowActorProtocol> 91 JSWindowActorProtocol::FromWebIDLOptions(const nsACString& aName, 92 const WindowActorOptions& aOptions, 93 ErrorResult& aRv) { 94 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess()); 95 96 RefPtr<JSWindowActorProtocol> proto = new JSWindowActorProtocol(aName); 97 if (!JSActorProtocolUtils::FromWebIDLOptionsShared(proto, aOptions, aRv)) { 98 return nullptr; 99 } 100 101 proto->mAllFrames = aOptions.mAllFrames; 102 proto->mIncludeChrome = aOptions.mIncludeChrome; 103 104 if (aOptions.mMatches.WasPassed()) { 105 MOZ_ASSERT(aOptions.mMatches.Value().Length()); 106 proto->mMatches = aOptions.mMatches.Value(); 107 } 108 109 if (aOptions.mMessageManagerGroups.WasPassed()) { 110 proto->mMessageManagerGroups = aOptions.mMessageManagerGroups.Value(); 111 } 112 113 // For each event declared in the source dictionary, initialize the 114 // corresponding event declaration entry in the JSWindowActorProtocol. 115 if (aOptions.mChild.WasPassed() && 116 aOptions.mChild.Value().mEvents.WasPassed()) { 117 auto& entries = aOptions.mChild.Value().mEvents.Value().Entries(); 118 proto->mChild.mEvents.SetCapacity(entries.Length()); 119 120 for (auto& entry : entries) { 121 // We don't support the mOnce field, as it doesn't work well in this 122 // environment. For now, throw an error in that case. 123 if (entry.mValue.mOnce) { 124 aRv.ThrowNotSupportedError("mOnce is not supported"); 125 return nullptr; 126 } 127 128 // Add the EventDecl to our list of events. 129 EventDecl* evt = proto->mChild.mEvents.AppendElement(); 130 evt->mName = entry.mKey; 131 evt->mFlags.mCapture = entry.mValue.mCapture; 132 evt->mFlags.mInSystemGroup = entry.mValue.mMozSystemGroup; 133 evt->mFlags.mAllowUntrustedEvents = 134 entry.mValue.mWantUntrusted.WasPassed() 135 ? entry.mValue.mWantUntrusted.Value() 136 : false; 137 if (entry.mValue.mPassive.WasPassed()) { 138 evt->mPassive.Construct(entry.mValue.mPassive.Value()); 139 } 140 evt->mCreateActor = entry.mValue.mCreateActor; 141 } 142 } 143 144 return proto.forget(); 145 } 146 147 /** 148 * This listener only listens for events for the child side of the protocol. 149 * This will work in both content and parent processes. 150 */ 151 NS_IMETHODIMP JSWindowActorProtocol::HandleEvent(Event* aEvent) { 152 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript()); 153 154 // Determine which inner window we're associated with, and get its 155 // WindowGlobalChild actor. 156 EventTarget* target = aEvent->GetOriginalTarget(); 157 if (NS_WARN_IF(!target)) { 158 return NS_ERROR_FAILURE; 159 } 160 161 nsCOMPtr<nsPIDOMWindowInner> inner = 162 do_QueryInterface(target->GetOwnerGlobal()); 163 if (!inner) { 164 return NS_ERROR_FAILURE; 165 } 166 167 RefPtr<WindowGlobalChild> wgc = inner->GetWindowGlobalChild(); 168 if (NS_WARN_IF(!wgc)) { 169 return NS_ERROR_FAILURE; 170 } 171 172 if (aEvent->ShouldIgnoreChromeEventTargetListener()) { 173 return NS_OK; 174 } 175 176 // Ensure our actor is present. 177 RefPtr<JSActor> actor = wgc->GetExistingActor(mName); 178 if (!actor) { 179 // Check if we're supposed to create the actor when this event is fired. 180 bool createActor = true; 181 nsAutoString typeStr; 182 aEvent->GetType(typeStr); 183 for (auto& event : mChild.mEvents) { 184 if (event.mName == typeStr) { 185 createActor = event.mCreateActor; 186 break; 187 } 188 } 189 190 // If we're supposed to create the actor, call GetActor to cause it to be 191 // created. 192 if (createActor) { 193 AutoJSAPI jsapi; 194 jsapi.Init(); 195 actor = wgc->GetActor(jsapi.cx(), mName, IgnoreErrors()); 196 } 197 } 198 if (!actor || NS_WARN_IF(!actor->GetWrapperPreserveColor())) { 199 return NS_OK; 200 } 201 202 // Build our event listener & call it. 203 JS::Rooted<JSObject*> global(RootingCx(), 204 JS::GetNonCCWObjectGlobal(actor->GetWrapper())); 205 RefPtr<EventListener> eventListener = 206 new EventListener(actor->GetWrapper(), global, nullptr, nullptr); 207 eventListener->HandleEvent(*aEvent, "JSWindowActorProtocol::HandleEvent"); 208 return NS_OK; 209 } 210 211 NS_IMETHODIMP JSWindowActorProtocol::Observe(nsISupports* aSubject, 212 const char* aTopic, 213 const char16_t* aData) { 214 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript()); 215 216 nsCOMPtr<nsPIDOMWindowInner> inner = do_QueryInterface(aSubject); 217 RefPtr<WindowGlobalChild> wgc; 218 219 if (!inner) { 220 nsCOMPtr<nsPIDOMWindowOuter> outer = do_QueryInterface(aSubject); 221 if (NS_WARN_IF(!outer)) { 222 nsContentUtils::LogSimpleConsoleError( 223 NS_ConvertUTF8toUTF16(nsPrintfCString( 224 "JSWindowActor %s: expected window subject for topic '%s'.", 225 mName.get(), aTopic)), 226 "JSActor"_ns, 227 /* aFromPrivateWindow */ false, 228 /* aFromChromeContext */ true); 229 return NS_ERROR_FAILURE; 230 } 231 if (NS_WARN_IF(!outer->GetCurrentInnerWindow())) { 232 return NS_ERROR_FAILURE; 233 } 234 wgc = outer->GetCurrentInnerWindow()->GetWindowGlobalChild(); 235 } else { 236 wgc = inner->GetWindowGlobalChild(); 237 } 238 239 if (NS_WARN_IF(!wgc)) { 240 return NS_ERROR_FAILURE; 241 } 242 243 // Ensure our actor is present. 244 AutoJSAPI jsapi; 245 jsapi.Init(); 246 RefPtr<JSActor> actor = wgc->GetActor(jsapi.cx(), mName, IgnoreErrors()); 247 if (!actor || NS_WARN_IF(!actor->GetWrapperPreserveColor())) { 248 return NS_OK; 249 } 250 251 // Build a observer callback. 252 JS::Rooted<JSObject*> global(jsapi.cx(), 253 JS::GetNonCCWObjectGlobal(actor->GetWrapper())); 254 RefPtr<MozObserverCallback> observerCallback = 255 new MozObserverCallback(actor->GetWrapper(), global, nullptr, nullptr); 256 observerCallback->Observe(aSubject, nsDependentCString(aTopic), 257 aData ? nsDependentString(aData) : VoidString()); 258 return NS_OK; 259 } 260 261 void JSWindowActorProtocol::RegisterListenersFor(EventTarget* aTarget) { 262 EventListenerManager* elm = aTarget->GetOrCreateListenerManager(); 263 264 for (auto& event : mChild.mEvents) { 265 elm->AddEventListenerByType(EventListenerHolder(this), event.mName, 266 event.mFlags, event.mPassive); 267 } 268 } 269 270 void JSWindowActorProtocol::UnregisterListenersFor(EventTarget* aTarget) { 271 EventListenerManager* elm = aTarget->GetOrCreateListenerManager(); 272 273 for (auto& event : mChild.mEvents) { 274 elm->RemoveEventListenerByType(EventListenerHolder(this), event.mName, 275 event.mFlags); 276 } 277 } 278 279 void JSWindowActorProtocol::AddObservers() { 280 nsCOMPtr<nsIObserverService> os = services::GetObserverService(); 281 for (auto& topic : mChild.mObservers) { 282 // This makes the observer service hold an owning reference to the 283 // JSWindowActorProtocol. The JSWindowActorProtocol objects will be living 284 // for the full lifetime of the content process, thus the extra strong 285 // referencec doesn't have a negative impact. 286 os->AddObserver(this, topic.get(), false); 287 } 288 } 289 290 void JSWindowActorProtocol::RemoveObservers() { 291 nsCOMPtr<nsIObserverService> os = services::GetObserverService(); 292 for (auto& topic : mChild.mObservers) { 293 os->RemoveObserver(this, topic.get()); 294 } 295 } 296 297 extensions::MatchPatternSetCore* JSWindowActorProtocol::GetURIMatcher() { 298 // If we've already created the pattern set, return it. 299 if (mURIMatcher || mMatches.IsEmpty()) { 300 return mURIMatcher; 301 } 302 303 nsTArray<RefPtr<extensions::MatchPatternCore>> patterns(mMatches.Length()); 304 for (const nsString& pattern : mMatches) { 305 patterns.AppendElement(new extensions::MatchPatternCore( 306 pattern, false, false, IgnoreErrors())); 307 } 308 mURIMatcher = new extensions::MatchPatternSetCore(std::move(patterns)); 309 return mURIMatcher; 310 } 311 312 bool JSWindowActorProtocol::MessageManagerGroupMatches( 313 BrowsingContext* aBrowsingContext) { 314 BrowsingContext* top = aBrowsingContext->Top(); 315 for (auto& group : mMessageManagerGroups) { 316 if (group == top->GetMessageManagerGroup()) { 317 return true; 318 } 319 } 320 return false; 321 } 322 323 bool JSWindowActorProtocol::Matches(BrowsingContext* aBrowsingContext, 324 nsIURI* aURI, const nsACString& aRemoteType, 325 ErrorResult& aRv) { 326 MOZ_ASSERT(aBrowsingContext, "DocShell without a BrowsingContext!"); 327 MOZ_ASSERT(aURI, "Must have URI!"); 328 329 if (!mAllFrames && aBrowsingContext->GetParent()) { 330 aRv.ThrowNotSupportedError(nsPrintfCString( 331 "Window protocol '%s' doesn't match subframes", mName.get())); 332 return false; 333 } 334 335 if (!mIncludeChrome && !aBrowsingContext->IsContent()) { 336 aRv.ThrowNotSupportedError(nsPrintfCString( 337 "Window protocol '%s' doesn't match chrome browsing contexts", 338 mName.get())); 339 return false; 340 } 341 342 if (!RemoteTypePrefixMatches(aRemoteType)) { 343 aRv.ThrowNotSupportedError( 344 nsPrintfCString("Window protocol '%s' doesn't match remote type '%s'", 345 mName.get(), PromiseFlatCString(aRemoteType).get())); 346 return false; 347 } 348 349 if (!mMessageManagerGroups.IsEmpty() && 350 !MessageManagerGroupMatches(aBrowsingContext)) { 351 aRv.ThrowNotSupportedError(nsPrintfCString( 352 "Window protocol '%s' doesn't match message manager group", 353 mName.get())); 354 return false; 355 } 356 357 if (extensions::MatchPatternSetCore* uriMatcher = GetURIMatcher()) { 358 if (!uriMatcher->Matches(aURI)) { 359 aRv.ThrowNotSupportedError(nsPrintfCString( 360 "Window protocol '%s' doesn't match uri %s", mName.get(), 361 nsContentUtils::TruncatedURLForDisplay(aURI).get())); 362 return false; 363 } 364 } 365 366 return true; 367 } 368 369 } // namespace mozilla::dom