InProcessImpl.cpp (9250B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/Services.h" 8 #include "mozilla/dom/InProcessChild.h" 9 #include "mozilla/dom/InProcessParent.h" 10 #include "mozilla/dom/JSProcessActorBinding.h" 11 #include "nsIObserverService.h" 12 13 using namespace mozilla::ipc; 14 15 // This file contains the implementation of core InProcess lifecycle management 16 // facilities. 17 18 namespace mozilla::dom { 19 20 StaticRefPtr<InProcessParent> InProcessParent::sSingleton; 21 StaticRefPtr<InProcessChild> InProcessChild::sSingleton; 22 bool InProcessParent::sShutdown = false; 23 24 ////////////////////////////////////////// 25 // InProcess actor lifecycle management // 26 ////////////////////////////////////////// 27 28 /* static */ 29 InProcessChild* InProcessChild::Singleton() { 30 MOZ_ASSERT(NS_IsMainThread()); 31 32 if (!sSingleton) { 33 InProcessParent::Startup(); 34 } 35 return sSingleton; 36 } 37 38 /* static */ 39 InProcessParent* InProcessParent::Singleton() { 40 MOZ_ASSERT(NS_IsMainThread()); 41 42 if (!sSingleton) { 43 InProcessParent::Startup(); 44 } 45 return sSingleton; 46 } 47 48 /* static */ 49 void InProcessParent::Startup() { 50 MOZ_ASSERT(NS_IsMainThread()); 51 52 if (sShutdown) { 53 NS_WARNING("Could not get in-process actor while shutting down!"); 54 return; 55 } 56 57 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 58 if (!obs) { 59 sShutdown = true; 60 NS_WARNING("Failed to get nsIObserverService for in-process actor"); 61 return; 62 } 63 64 RefPtr<InProcessParent> parent = new InProcessParent(); 65 RefPtr<InProcessChild> child = new InProcessChild(); 66 67 // Observe the shutdown event to close & clean up after ourselves. 68 nsresult rv = obs->AddObserver(parent, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); 69 if (NS_WARN_IF(NS_FAILED(rv))) { 70 return; 71 } 72 73 // Link the two actors 74 if (!child->OpenOnSameThread(parent, ChildSide)) { 75 MOZ_CRASH("Failed to open InProcessChild!"); 76 } 77 78 parent->SetOtherEndpointProcInfo(EndpointProcInfo::Current()); 79 80 // Stash global references to fetch the other side of the reference. 81 InProcessParent::sSingleton = std::move(parent); 82 InProcessChild::sSingleton = std::move(child); 83 } 84 85 /* static */ 86 void InProcessParent::Shutdown() { 87 MOZ_ASSERT(NS_IsMainThread()); 88 89 if (!sSingleton || sShutdown) { 90 return; 91 } 92 93 sShutdown = true; 94 95 RefPtr<InProcessParent> parent = sSingleton; 96 InProcessParent::sSingleton = nullptr; 97 InProcessChild::sSingleton = nullptr; 98 99 // Calling `Close` on the actor will cause the `Dealloc` methods to be called, 100 // freeing the remaining references. 101 parent->Close(); 102 } 103 104 NS_IMETHODIMP 105 InProcessParent::Observe(nsISupports* aSubject, const char* aTopic, 106 const char16_t* aData) { 107 MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)); 108 InProcessParent::Shutdown(); 109 return NS_OK; 110 } 111 112 void InProcessParent::ActorDestroy(ActorDestroyReason aWhy) { 113 JSActorDidDestroy(); 114 InProcessParent::Shutdown(); 115 } 116 117 void InProcessChild::ActorDestroy(ActorDestroyReason aWhy) { 118 JSActorDidDestroy(); 119 InProcessParent::Shutdown(); 120 } 121 122 ///////////////////////// 123 // nsIDOMProcessParent // 124 ///////////////////////// 125 126 NS_IMETHODIMP 127 InProcessParent::GetChildID(uint64_t* aChildID) { 128 *aChildID = 0; 129 return NS_OK; 130 } 131 132 NS_IMETHODIMP 133 InProcessParent::GetOsPid(int32_t* aOsPid) { 134 // InProcessParent always run in the parent process, 135 // so we can return the current process id. 136 *aOsPid = base::GetCurrentProcId(); 137 return NS_OK; 138 } 139 140 NS_IMETHODIMP InProcessParent::GetRemoteType(nsACString& aRemoteType) { 141 aRemoteType = NOT_REMOTE_TYPE; 142 return NS_OK; 143 } 144 145 NS_IMETHODIMP 146 InProcessParent::GetActor(const nsACString& aName, JSContext* aCx, 147 JSProcessActorParent** aActor) { 148 ErrorResult error; 149 RefPtr<JSProcessActorParent> actor = 150 JSActorManager::GetActor(aCx, aName, error) 151 .downcast<JSProcessActorParent>(); 152 if (error.MaybeSetPendingException(aCx)) { 153 return NS_ERROR_FAILURE; 154 } 155 actor.forget(aActor); 156 return NS_OK; 157 } 158 159 NS_IMETHODIMP 160 InProcessParent::GetExistingActor(const nsACString& aName, 161 JSProcessActorParent** aActor) { 162 RefPtr<JSProcessActorParent> actor = 163 JSActorManager::GetExistingActor(aName).downcast<JSProcessActorParent>(); 164 actor.forget(aActor); 165 return NS_OK; 166 } 167 168 already_AddRefed<JSActor> InProcessParent::InitJSActor( 169 JS::Handle<JSObject*> aMaybeActor, const nsACString& aName, 170 ErrorResult& aRv) { 171 RefPtr<JSProcessActorParent> actor; 172 if (aMaybeActor.get()) { 173 aRv = UNWRAP_OBJECT(JSProcessActorParent, aMaybeActor.get(), actor); 174 if (aRv.Failed()) { 175 return nullptr; 176 } 177 } else { 178 actor = new JSProcessActorParent(); 179 } 180 181 MOZ_RELEASE_ASSERT(!actor->Manager(), 182 "mManager was already initialized once!"); 183 actor->Init(aName, this); 184 return actor.forget(); 185 } 186 187 NS_IMETHODIMP 188 InProcessParent::GetCanSend(bool* aCanSend) { 189 *aCanSend = CanSend(); 190 return NS_OK; 191 } 192 193 ContentParent* InProcessParent::AsContentParent() { return nullptr; } 194 195 JSActorManager* InProcessParent::AsJSActorManager() { return this; } 196 197 //////////////////////// 198 // nsIDOMProcessChild // 199 //////////////////////// 200 201 NS_IMETHODIMP 202 InProcessChild::GetChildID(uint64_t* aChildID) { 203 *aChildID = 0; 204 return NS_OK; 205 } 206 207 NS_IMETHODIMP 208 InProcessChild::GetActor(const nsACString& aName, JSContext* aCx, 209 JSProcessActorChild** aActor) { 210 ErrorResult error; 211 RefPtr<JSProcessActorChild> actor = 212 JSActorManager::GetActor(aCx, aName, error) 213 .downcast<JSProcessActorChild>(); 214 if (error.MaybeSetPendingException(aCx)) { 215 return NS_ERROR_FAILURE; 216 } 217 actor.forget(aActor); 218 return NS_OK; 219 } 220 221 NS_IMETHODIMP 222 InProcessChild::GetExistingActor(const nsACString& aName, 223 JSProcessActorChild** aActor) { 224 RefPtr<JSProcessActorChild> actor = 225 JSActorManager::GetExistingActor(aName).downcast<JSProcessActorChild>(); 226 actor.forget(aActor); 227 return NS_OK; 228 } 229 230 already_AddRefed<JSActor> InProcessChild::InitJSActor( 231 JS::Handle<JSObject*> aMaybeActor, const nsACString& aName, 232 ErrorResult& aRv) { 233 RefPtr<JSProcessActorChild> actor; 234 if (aMaybeActor.get()) { 235 aRv = UNWRAP_OBJECT(JSProcessActorChild, aMaybeActor.get(), actor); 236 if (aRv.Failed()) { 237 return nullptr; 238 } 239 } else { 240 actor = new JSProcessActorChild(); 241 } 242 243 MOZ_RELEASE_ASSERT(!actor->Manager(), 244 "mManager was already initialized once!"); 245 actor->Init(aName, this); 246 return actor.forget(); 247 } 248 249 NS_IMETHODIMP 250 InProcessChild::GetCanSend(bool* aCanSend) { 251 *aCanSend = CanSend(); 252 return NS_OK; 253 } 254 255 ContentChild* InProcessChild::AsContentChild() { return nullptr; } 256 257 JSActorManager* InProcessChild::AsJSActorManager() { return this; } 258 259 //////////////////////////////// 260 // In-Process Actor Utilities // 261 //////////////////////////////// 262 263 // Helper method for implementing ParentActorFor and ChildActorFor. 264 static IProtocol* GetOtherInProcessActor(IProtocol* aActor) { 265 MOZ_ASSERT(aActor->GetSide() != UnknownSide, "bad unknown side"); 266 267 // Discover the manager of aActor which is PInProcess. 268 IProtocol* current = aActor; 269 while (current && current->CanRecv()) { 270 if (current->GetProtocolId() == PInProcessMsgStart) { 271 break; // Found the correct actor. 272 } 273 current = current->Manager(); 274 } 275 if (!current || !current->CanRecv()) { 276 return nullptr; // Not a live PInProcess actor, return |nullptr| 277 } 278 279 MOZ_ASSERT(current->GetSide() == aActor->GetSide(), "side changed?"); 280 MOZ_ASSERT_IF(aActor->GetSide() == ParentSide, 281 current == InProcessParent::Singleton()); 282 MOZ_ASSERT_IF(aActor->GetSide() == ChildSide, 283 current == InProcessChild::Singleton()); 284 285 // Check whether this is InProcessParent or InProcessChild, and get the other 286 // side's toplevel actor. 287 IProtocol* otherRoot = nullptr; 288 if (aActor->GetSide() == ParentSide) { 289 otherRoot = InProcessChild::Singleton(); 290 } else { 291 otherRoot = InProcessParent::Singleton(); 292 } 293 if (NS_WARN_IF(!otherRoot)) { 294 return nullptr; 295 } 296 297 // Look up the actor on the other side, and return it. 298 IProtocol* otherActor = otherRoot->Lookup(aActor->Id()); 299 if (otherActor) { 300 MOZ_ASSERT(otherActor->GetSide() != UnknownSide, "bad unknown side"); 301 MOZ_ASSERT(otherActor->GetSide() != aActor->GetSide(), "Wrong side!"); 302 MOZ_ASSERT(otherActor->GetProtocolId() == aActor->GetProtocolId(), 303 "Wrong type of protocol!"); 304 } 305 306 return otherActor; 307 } 308 309 /* static */ 310 IProtocol* InProcessParent::ChildActorFor(IProtocol* aActor) { 311 MOZ_ASSERT(aActor && aActor->GetSide() == ParentSide); 312 return GetOtherInProcessActor(aActor); 313 } 314 315 /* static */ 316 IProtocol* InProcessChild::ParentActorFor(IProtocol* aActor) { 317 MOZ_ASSERT(aActor && aActor->GetSide() == ChildSide); 318 return GetOtherInProcessActor(aActor); 319 } 320 321 NS_IMPL_ISUPPORTS(InProcessParent, nsIDOMProcessParent, nsIObserver) 322 NS_IMPL_ISUPPORTS(InProcessChild, nsIDOMProcessChild) 323 324 } // namespace mozilla::dom