JSActorService.cpp (12009B)
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/JSActorService.h" 9 10 #include "mozilla/ArrayAlgorithm.h" 11 #include "mozilla/Logging.h" 12 #include "mozilla/Services.h" 13 #include "mozilla/StaticPtr.h" 14 #include "mozilla/dom/BrowserChild.h" 15 #include "mozilla/dom/BrowserParent.h" 16 #include "mozilla/dom/ChromeUtilsBinding.h" 17 #include "mozilla/dom/ContentChild.h" 18 #include "mozilla/dom/ContentParent.h" 19 #include "mozilla/dom/Event.h" 20 #include "mozilla/dom/EventListenerBinding.h" 21 #include "mozilla/dom/EventTarget.h" 22 #include "mozilla/dom/EventTargetBinding.h" 23 #include "mozilla/dom/InProcessChild.h" 24 #include "mozilla/dom/InProcessParent.h" 25 #include "mozilla/dom/JSActorManager.h" 26 #include "mozilla/dom/JSProcessActorBinding.h" 27 #include "mozilla/dom/JSProcessActorChild.h" 28 #include "mozilla/dom/JSProcessActorProtocol.h" 29 #include "mozilla/dom/JSWindowActorBinding.h" 30 #include "mozilla/dom/JSWindowActorChild.h" 31 #include "mozilla/dom/JSWindowActorProtocol.h" 32 #include "mozilla/dom/MessageManagerBinding.h" 33 #include "mozilla/dom/PContent.h" 34 #include "mozilla/dom/WindowGlobalChild.h" 35 #include "mozilla/dom/WindowGlobalParent.h" 36 #include "nsIObserverService.h" 37 38 namespace mozilla::dom { 39 namespace { 40 StaticRefPtr<JSActorService> gJSActorService; 41 } 42 43 JSActorService::JSActorService() { MOZ_ASSERT(NS_IsMainThread()); } 44 45 JSActorService::~JSActorService() { MOZ_ASSERT(NS_IsMainThread()); } 46 47 /* static */ 48 already_AddRefed<JSActorService> JSActorService::GetSingleton() { 49 MOZ_ASSERT(NS_IsMainThread()); 50 if (!gJSActorService) { 51 gJSActorService = new JSActorService(); 52 ClearOnShutdown(&gJSActorService); 53 } 54 55 RefPtr<JSActorService> service = gJSActorService.get(); 56 return service.forget(); 57 } 58 59 void JSActorService::RegisterWindowActor(const nsACString& aName, 60 const WindowActorOptions& aOptions, 61 ErrorResult& aRv) { 62 MOZ_ASSERT(NS_IsMainThread()); 63 MOZ_ASSERT(XRE_IsParentProcess()); 64 65 if (mProcessActorDescriptors.Contains(aName)) { 66 aRv.ThrowNotSupportedError( 67 nsPrintfCString("'%s' actor is already registered as a process actor.", 68 PromiseFlatCString(aName).get())); 69 return; 70 } 71 72 const auto proto = mWindowActorDescriptors.WithEntryHandle( 73 aName, [&](auto&& entry) -> RefPtr<JSWindowActorProtocol> { 74 if (entry) { 75 aRv.ThrowNotSupportedError( 76 nsPrintfCString("'%s' actor is already registered.", 77 PromiseFlatCString(aName).get())); 78 return nullptr; 79 } 80 81 // Insert a new entry for the protocol. 82 RefPtr<JSWindowActorProtocol> protocol = 83 JSWindowActorProtocol::FromWebIDLOptions(aName, aOptions, aRv); 84 if (NS_WARN_IF(aRv.Failed())) { 85 return nullptr; 86 } 87 88 entry.Insert(protocol); 89 90 return protocol; 91 }); 92 93 if (!proto) { 94 MOZ_ASSERT(aRv.Failed()); 95 return; 96 } 97 98 // Send information about the newly added entry to every existing content 99 // process. 100 AutoTArray<JSWindowActorInfo, 1> windowInfos{proto->ToIPC()}; 101 nsTArray<JSProcessActorInfo> contentInfos{}; 102 for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) { 103 (void)cp->SendInitJSActorInfos(contentInfos, windowInfos); 104 } 105 106 // Register event listeners for any existing chrome targets. 107 for (EventTarget* target : mChromeEventTargets) { 108 proto->RegisterListenersFor(target); 109 } 110 111 // Add observers to the protocol. 112 proto->AddObservers(); 113 } 114 115 void JSActorService::UnregisterWindowActor(const nsACString& aName) { 116 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript()); 117 CrashReporter::AutoRecordAnnotation autoActorName( 118 CrashReporter::Annotation::JSActorName, aName); 119 CrashReporter::AutoRecordAnnotation autoMessageName( 120 CrashReporter::Annotation::JSActorMessage, "<Unregister>"_ns); 121 122 nsAutoCString name(aName); 123 RefPtr<JSWindowActorProtocol> proto; 124 if (mWindowActorDescriptors.Remove(name, getter_AddRefs(proto))) { 125 // Remove listeners for this actor from each of our chrome targets. 126 for (EventTarget* target : mChromeEventTargets) { 127 proto->UnregisterListenersFor(target); 128 } 129 130 // Remove observers for this actor from observer serivce. 131 proto->RemoveObservers(); 132 133 // Tell every content process to also unregister, and accumulate the set of 134 // potential managers, to have the actor disabled. 135 nsTArray<RefPtr<JSActorManager>> managers; 136 if (XRE_IsParentProcess()) { 137 for (auto* cp : ContentParent::AllProcesses(ContentParent::eAll)) { 138 if (cp->CanSend()) { 139 (void)cp->SendUnregisterJSWindowActor(name); 140 } 141 for (const auto& bp : cp->ManagedPBrowserParent()) { 142 for (const auto& wgp : bp->ManagedPWindowGlobalParent()) { 143 managers.AppendElement(static_cast<WindowGlobalParent*>(wgp)); 144 } 145 } 146 } 147 148 for (const auto& wgp : 149 InProcessParent::Singleton()->ManagedPWindowGlobalParent()) { 150 managers.AppendElement(static_cast<WindowGlobalParent*>(wgp)); 151 } 152 for (const auto& wgc : 153 InProcessChild::Singleton()->ManagedPWindowGlobalChild()) { 154 managers.AppendElement(static_cast<WindowGlobalChild*>(wgc)); 155 } 156 } else { 157 for (const auto& bc : 158 ContentChild::GetSingleton()->ManagedPBrowserChild()) { 159 for (const auto& wgc : bc->ManagedPWindowGlobalChild()) { 160 managers.AppendElement(static_cast<WindowGlobalChild*>(wgc)); 161 } 162 } 163 } 164 165 for (auto& mgr : managers) { 166 mgr->JSActorUnregister(name); 167 } 168 } 169 } 170 171 void JSActorService::LoadJSActorInfos(nsTArray<JSProcessActorInfo>& aProcess, 172 nsTArray<JSWindowActorInfo>& aWindow) { 173 MOZ_ASSERT(NS_IsMainThread()); 174 MOZ_ASSERT(XRE_IsContentProcess()); 175 176 for (auto& info : aProcess) { 177 // Create our JSProcessActorProtocol, register it in 178 // mProcessActorDescriptors. 179 auto name = info.name(); 180 RefPtr<JSProcessActorProtocol> proto = 181 JSProcessActorProtocol::FromIPC(std::move(info)); 182 mProcessActorDescriptors.InsertOrUpdate(std::move(name), RefPtr{proto}); 183 184 // Add observers for each actor. 185 proto->AddObservers(); 186 } 187 188 for (auto& info : aWindow) { 189 auto name = info.name(); 190 RefPtr<JSWindowActorProtocol> proto = 191 JSWindowActorProtocol::FromIPC(std::move(info)); 192 mWindowActorDescriptors.InsertOrUpdate(std::move(name), RefPtr{proto}); 193 194 // Register listeners for each chrome target. 195 for (EventTarget* target : mChromeEventTargets) { 196 proto->RegisterListenersFor(target); 197 } 198 199 // Add observers for each actor. 200 proto->AddObservers(); 201 } 202 } 203 204 void JSActorService::GetJSWindowActorInfos( 205 nsTArray<JSWindowActorInfo>& aInfos) { 206 MOZ_ASSERT(NS_IsMainThread()); 207 MOZ_ASSERT(XRE_IsParentProcess()); 208 209 for (const auto& data : mWindowActorDescriptors.Values()) { 210 aInfos.AppendElement(data->ToIPC()); 211 } 212 } 213 214 void JSActorService::RegisterChromeEventTarget(EventTarget* aTarget) { 215 MOZ_ASSERT(!mChromeEventTargets.Contains(aTarget)); 216 mChromeEventTargets.AppendElement(aTarget); 217 218 // Register event listeners on the newly added Window Root. 219 for (const auto& data : mWindowActorDescriptors.Values()) { 220 data->RegisterListenersFor(aTarget); 221 } 222 223 nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 224 obs->NotifyObservers(aTarget, "chrome-event-target-created", nullptr); 225 } 226 227 /* static */ 228 void JSActorService::UnregisterChromeEventTarget(EventTarget* aTarget) { 229 if (gJSActorService) { 230 // NOTE: No need to unregister listeners here, as the target is going away. 231 gJSActorService->mChromeEventTargets.RemoveElement(aTarget); 232 } 233 } 234 235 void JSActorService::RegisterProcessActor(const nsACString& aName, 236 const ProcessActorOptions& aOptions, 237 ErrorResult& aRv) { 238 MOZ_ASSERT(NS_IsMainThread()); 239 MOZ_ASSERT(XRE_IsParentProcess()); 240 241 if (mWindowActorDescriptors.Contains(aName)) { 242 aRv.ThrowNotSupportedError( 243 nsPrintfCString("'%s' actor is already registered as a window actor.", 244 PromiseFlatCString(aName).get())); 245 return; 246 } 247 248 const auto proto = mProcessActorDescriptors.WithEntryHandle( 249 aName, [&](auto&& entry) -> RefPtr<JSProcessActorProtocol> { 250 if (entry) { 251 aRv.ThrowNotSupportedError( 252 nsPrintfCString("'%s' actor is already registered.", 253 PromiseFlatCString(aName).get())); 254 return nullptr; 255 } 256 257 // Insert a new entry for the protocol. 258 RefPtr<JSProcessActorProtocol> protocol = 259 JSProcessActorProtocol::FromWebIDLOptions(aName, aOptions, aRv); 260 if (NS_WARN_IF(aRv.Failed())) { 261 return nullptr; 262 } 263 264 entry.Insert(protocol); 265 266 return protocol; 267 }); 268 269 if (!proto) { 270 MOZ_ASSERT(aRv.Failed()); 271 return; 272 } 273 274 // Send information about the newly added entry to every existing content 275 // process. 276 AutoTArray<JSProcessActorInfo, 1> contentInfos{proto->ToIPC()}; 277 nsTArray<JSWindowActorInfo> windowInfos{}; 278 for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) { 279 (void)cp->SendInitJSActorInfos(contentInfos, windowInfos); 280 } 281 282 // Add observers to the protocol. 283 proto->AddObservers(); 284 } 285 286 void JSActorService::UnregisterProcessActor(const nsACString& aName) { 287 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript()); 288 CrashReporter::AutoRecordAnnotation autoActorName( 289 CrashReporter::Annotation::JSActorName, aName); 290 CrashReporter::AutoRecordAnnotation autoMessageName( 291 CrashReporter::Annotation::JSActorMessage, "<Unregister>"_ns); 292 293 nsAutoCString name(aName); 294 RefPtr<JSProcessActorProtocol> proto; 295 if (mProcessActorDescriptors.Remove(name, getter_AddRefs(proto))) { 296 // Remove observers for this actor from observer serivce. 297 proto->RemoveObservers(); 298 299 // Tell every content process to also unregister, and accumulate the set of 300 // potential managers, to have the actor disabled. 301 nsTArray<RefPtr<JSActorManager>> managers; 302 if (XRE_IsParentProcess()) { 303 for (auto* cp : ContentParent::AllProcesses(ContentParent::eAll)) { 304 if (cp->CanSend()) { 305 (void)cp->SendUnregisterJSProcessActor(name); 306 } 307 managers.AppendElement(cp); 308 } 309 managers.AppendElement(InProcessChild::Singleton()); 310 managers.AppendElement(InProcessParent::Singleton()); 311 } else { 312 managers.AppendElement(ContentChild::GetSingleton()); 313 } 314 315 for (auto& mgr : managers) { 316 mgr->JSActorUnregister(name); 317 } 318 } 319 } 320 321 void JSActorService::GetJSProcessActorInfos( 322 nsTArray<JSProcessActorInfo>& aInfos) { 323 MOZ_ASSERT(NS_IsMainThread()); 324 MOZ_ASSERT(XRE_IsParentProcess()); 325 326 for (const auto& data : mProcessActorDescriptors.Values()) { 327 aInfos.AppendElement(data->ToIPC()); 328 } 329 } 330 331 already_AddRefed<JSProcessActorProtocol> 332 JSActorService::GetJSProcessActorProtocol(const nsACString& aName) { 333 return mProcessActorDescriptors.Get(aName); 334 } 335 336 already_AddRefed<JSWindowActorProtocol> 337 JSActorService::GetJSWindowActorProtocol(const nsACString& aName) { 338 return mWindowActorDescriptors.Get(aName); 339 } 340 341 bool JSActorProtocol::RemoteTypePrefixMatches(const nsACString& aRemoteType) { 342 if (mRemoteTypes.IsEmpty()) { 343 return true; 344 } 345 346 nsDependentCSubstring remoteTypePrefix(RemoteTypePrefix(aRemoteType)); 347 for (auto& remoteType : mRemoteTypes) { 348 // TODO: Maybe this should use glob-style matching instead. See bug 2006165. 349 if (StringBeginsWith(remoteTypePrefix, remoteType)) { 350 return true; 351 } 352 } 353 return false; 354 } 355 356 } // namespace mozilla::dom