summaryrefslogtreecommitdiffstats
path: root/dom/ipc/InProcessImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/ipc/InProcessImpl.cpp')
-rw-r--r--dom/ipc/InProcessImpl.cpp324
1 files changed, 324 insertions, 0 deletions
diff --git a/dom/ipc/InProcessImpl.cpp b/dom/ipc/InProcessImpl.cpp
new file mode 100644
index 0000000000..e076a84c44
--- /dev/null
+++ b/dom/ipc/InProcessImpl.cpp
@@ -0,0 +1,324 @@
+/* -*- 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/InProcessParent.h"
+#include "mozilla/dom/InProcessChild.h"
+#include "mozilla/dom/JSProcessActorBinding.h"
+#include "nsIObserverService.h"
+#include "mozilla/Services.h"
+
+using namespace mozilla::ipc;
+
+// This file contains the implementation of core InProcess lifecycle management
+// facilities.
+
+namespace mozilla::dom {
+
+StaticRefPtr<InProcessParent> InProcessParent::sSingleton;
+StaticRefPtr<InProcessChild> InProcessChild::sSingleton;
+bool InProcessParent::sShutdown = false;
+
+//////////////////////////////////////////
+// InProcess actor lifecycle management //
+//////////////////////////////////////////
+
+/* static */
+InProcessChild* InProcessChild::Singleton() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!sSingleton) {
+ InProcessParent::Startup();
+ }
+ return sSingleton;
+}
+
+/* static */
+InProcessParent* InProcessParent::Singleton() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!sSingleton) {
+ InProcessParent::Startup();
+ }
+ return sSingleton;
+}
+
+/* static */
+void InProcessParent::Startup() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (sShutdown) {
+ NS_WARNING("Could not get in-process actor while shutting down!");
+ return;
+ }
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (!obs) {
+ sShutdown = true;
+ NS_WARNING("Failed to get nsIObserverService for in-process actor");
+ return;
+ }
+
+ RefPtr<InProcessParent> parent = new InProcessParent();
+ RefPtr<InProcessChild> child = new InProcessChild();
+
+ // Observe the shutdown event to close & clean up after ourselves.
+ nsresult rv = obs->AddObserver(parent, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ // Link the two actors
+ if (!child->OpenOnSameThread(parent, ChildSide)) {
+ MOZ_CRASH("Failed to open InProcessChild!");
+ }
+
+ parent->SetOtherProcessId(base::GetCurrentProcId());
+
+ // Stash global references to fetch the other side of the reference.
+ InProcessParent::sSingleton = std::move(parent);
+ InProcessChild::sSingleton = std::move(child);
+}
+
+/* static */
+void InProcessParent::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!sSingleton || sShutdown) {
+ return;
+ }
+
+ sShutdown = true;
+
+ RefPtr<InProcessParent> parent = sSingleton;
+ InProcessParent::sSingleton = nullptr;
+ InProcessChild::sSingleton = nullptr;
+
+ // Calling `Close` on the actor will cause the `Dealloc` methods to be called,
+ // freeing the remaining references.
+ parent->Close();
+}
+
+NS_IMETHODIMP
+InProcessParent::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID));
+ InProcessParent::Shutdown();
+ return NS_OK;
+}
+
+void InProcessParent::ActorDestroy(ActorDestroyReason aWhy) {
+ JSActorDidDestroy();
+ InProcessParent::Shutdown();
+}
+
+void InProcessChild::ActorDestroy(ActorDestroyReason aWhy) {
+ JSActorDidDestroy();
+ InProcessParent::Shutdown();
+}
+
+/////////////////////////
+// nsIDOMProcessParent //
+/////////////////////////
+
+NS_IMETHODIMP
+InProcessParent::GetChildID(uint64_t* aChildID) {
+ *aChildID = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InProcessParent::GetOsPid(int32_t* aOsPid) {
+ // InProcessParent always run in the parent process,
+ // so we can return the current process id.
+ *aOsPid = base::GetCurrentProcId();
+ return NS_OK;
+}
+
+NS_IMETHODIMP InProcessParent::GetRemoteType(nsACString& aRemoteType) {
+ aRemoteType = NOT_REMOTE_TYPE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InProcessParent::GetActor(const nsACString& aName, JSContext* aCx,
+ JSProcessActorParent** aActor) {
+ ErrorResult error;
+ RefPtr<JSProcessActorParent> actor =
+ JSActorManager::GetActor(aCx, aName, error)
+ .downcast<JSProcessActorParent>();
+ if (error.MaybeSetPendingException(aCx)) {
+ return NS_ERROR_FAILURE;
+ }
+ actor.forget(aActor);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InProcessParent::GetExistingActor(const nsACString& aName,
+ JSProcessActorParent** aActor) {
+ RefPtr<JSProcessActorParent> actor =
+ JSActorManager::GetExistingActor(aName).downcast<JSProcessActorParent>();
+ actor.forget(aActor);
+ return NS_OK;
+}
+
+already_AddRefed<JSActor> InProcessParent::InitJSActor(
+ JS::Handle<JSObject*> aMaybeActor, const nsACString& aName,
+ ErrorResult& aRv) {
+ RefPtr<JSProcessActorParent> actor;
+ if (aMaybeActor.get()) {
+ aRv = UNWRAP_OBJECT(JSProcessActorParent, aMaybeActor.get(), actor);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+ } else {
+ actor = new JSProcessActorParent();
+ }
+
+ MOZ_RELEASE_ASSERT(!actor->Manager(),
+ "mManager was already initialized once!");
+ actor->Init(aName, this);
+ return actor.forget();
+}
+
+NS_IMETHODIMP
+InProcessParent::GetCanSend(bool* aCanSend) {
+ *aCanSend = CanSend();
+ return NS_OK;
+}
+
+ContentParent* InProcessParent::AsContentParent() { return nullptr; }
+
+JSActorManager* InProcessParent::AsJSActorManager() { return this; }
+
+////////////////////////
+// nsIDOMProcessChild //
+////////////////////////
+
+NS_IMETHODIMP
+InProcessChild::GetChildID(uint64_t* aChildID) {
+ *aChildID = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InProcessChild::GetActor(const nsACString& aName, JSContext* aCx,
+ JSProcessActorChild** aActor) {
+ ErrorResult error;
+ RefPtr<JSProcessActorChild> actor =
+ JSActorManager::GetActor(aCx, aName, error)
+ .downcast<JSProcessActorChild>();
+ if (error.MaybeSetPendingException(aCx)) {
+ return NS_ERROR_FAILURE;
+ }
+ actor.forget(aActor);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InProcessChild::GetExistingActor(const nsACString& aName,
+ JSProcessActorChild** aActor) {
+ RefPtr<JSProcessActorChild> actor =
+ JSActorManager::GetExistingActor(aName).downcast<JSProcessActorChild>();
+ actor.forget(aActor);
+ return NS_OK;
+}
+
+already_AddRefed<JSActor> InProcessChild::InitJSActor(
+ JS::Handle<JSObject*> aMaybeActor, const nsACString& aName,
+ ErrorResult& aRv) {
+ RefPtr<JSProcessActorChild> actor;
+ if (aMaybeActor.get()) {
+ aRv = UNWRAP_OBJECT(JSProcessActorChild, aMaybeActor.get(), actor);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+ } else {
+ actor = new JSProcessActorChild();
+ }
+
+ MOZ_RELEASE_ASSERT(!actor->Manager(),
+ "mManager was already initialized once!");
+ actor->Init(aName, this);
+ return actor.forget();
+}
+
+NS_IMETHODIMP
+InProcessChild::GetCanSend(bool* aCanSend) {
+ *aCanSend = CanSend();
+ return NS_OK;
+}
+
+ContentChild* InProcessChild::AsContentChild() { return nullptr; }
+
+JSActorManager* InProcessChild::AsJSActorManager() { return this; }
+
+////////////////////////////////
+// In-Process Actor Utilities //
+////////////////////////////////
+
+// Helper method for implementing ParentActorFor and ChildActorFor.
+static IProtocol* GetOtherInProcessActor(IProtocol* aActor) {
+ MOZ_ASSERT(aActor->GetSide() != UnknownSide, "bad unknown side");
+
+ // Discover the manager of aActor which is PInProcess.
+ IProtocol* current = aActor;
+ while (current && current->CanRecv()) {
+ if (current->GetProtocolId() == PInProcessMsgStart) {
+ break; // Found the correct actor.
+ }
+ current = current->Manager();
+ }
+ if (!current || !current->CanRecv()) {
+ return nullptr; // Not a live PInProcess actor, return |nullptr|
+ }
+
+ MOZ_ASSERT(current->GetSide() == aActor->GetSide(), "side changed?");
+ MOZ_ASSERT_IF(aActor->GetSide() == ParentSide,
+ current == InProcessParent::Singleton());
+ MOZ_ASSERT_IF(aActor->GetSide() == ChildSide,
+ current == InProcessChild::Singleton());
+
+ // Check whether this is InProcessParent or InProcessChild, and get the other
+ // side's toplevel actor.
+ IProtocol* otherRoot = nullptr;
+ if (aActor->GetSide() == ParentSide) {
+ otherRoot = InProcessChild::Singleton();
+ } else {
+ otherRoot = InProcessParent::Singleton();
+ }
+ if (NS_WARN_IF(!otherRoot)) {
+ return nullptr;
+ }
+
+ // Look up the actor on the other side, and return it.
+ IProtocol* otherActor = otherRoot->Lookup(aActor->Id());
+ if (otherActor) {
+ MOZ_ASSERT(otherActor->GetSide() != UnknownSide, "bad unknown side");
+ MOZ_ASSERT(otherActor->GetSide() != aActor->GetSide(), "Wrong side!");
+ MOZ_ASSERT(otherActor->GetProtocolId() == aActor->GetProtocolId(),
+ "Wrong type of protocol!");
+ }
+
+ return otherActor;
+}
+
+/* static */
+IProtocol* InProcessParent::ChildActorFor(IProtocol* aActor) {
+ MOZ_ASSERT(aActor && aActor->GetSide() == ParentSide);
+ return GetOtherInProcessActor(aActor);
+}
+
+/* static */
+IProtocol* InProcessChild::ParentActorFor(IProtocol* aActor) {
+ MOZ_ASSERT(aActor && aActor->GetSide() == ChildSide);
+ return GetOtherInProcessActor(aActor);
+}
+
+NS_IMPL_ISUPPORTS(InProcessParent, nsIDOMProcessParent, nsIObserver)
+NS_IMPL_ISUPPORTS(InProcessChild, nsIDOMProcessChild)
+
+} // namespace mozilla::dom