summaryrefslogtreecommitdiffstats
path: root/ipc/glue/test/utility_process_xpcom
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/glue/test/utility_process_xpcom')
-rw-r--r--ipc/glue/test/utility_process_xpcom/UtilityProcessTest.cpp280
-rw-r--r--ipc/glue/test/utility_process_xpcom/UtilityProcessTest.h29
-rw-r--r--ipc/glue/test/utility_process_xpcom/components.conf15
-rw-r--r--ipc/glue/test/utility_process_xpcom/moz.build21
-rw-r--r--ipc/glue/test/utility_process_xpcom/nsIUtilityProcessTest.idl56
5 files changed, 401 insertions, 0 deletions
diff --git a/ipc/glue/test/utility_process_xpcom/UtilityProcessTest.cpp b/ipc/glue/test/utility_process_xpcom/UtilityProcessTest.cpp
new file mode 100644
index 0000000000..6c084a3153
--- /dev/null
+++ b/ipc/glue/test/utility_process_xpcom/UtilityProcessTest.cpp
@@ -0,0 +1,280 @@
+/* -*- 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/. */
+
+#if defined(ENABLE_TESTS)
+# include "mozilla/ipc/UtilityProcessManager.h"
+# include "mozilla/ipc/UtilityProcessTest.h"
+# include "mozilla/dom/Promise.h"
+# include "mozilla/ProcInfo.h"
+# include "mozilla/IntentionalCrash.h"
+
+# ifdef XP_WIN
+# include <handleapi.h>
+# include <processthreadsapi.h>
+# include <tlhelp32.h>
+
+# include "mozilla/WinHandleWatcher.h"
+# include "nsISupports.h"
+# include "nsWindowsHelpers.h"
+# endif
+
+namespace mozilla::ipc {
+
+static UtilityActorName UtilityActorNameFromString(
+ const nsACString& aStringName) {
+ using namespace mozilla::dom;
+
+ // We use WebIDLUtilityActorNames because UtilityActorNames is not designed
+ // for iteration.
+ for (size_t i = 0; i < WebIDLUtilityActorNameValues::Count; ++i) {
+ auto idlName = static_cast<UtilityActorName>(i);
+ const nsDependentCSubstring idlNameString(
+ WebIDLUtilityActorNameValues::GetString(idlName));
+ if (idlNameString.Equals(aStringName)) {
+ return idlName;
+ }
+ }
+ MOZ_CRASH("Unknown utility actor name");
+}
+
+// Find the utility process with the given actor or any utility process if
+// the actor is UtilityActorName::EndGuard_.
+static SandboxingKind FindUtilityProcessWithActor(UtilityActorName aActorName) {
+ RefPtr<UtilityProcessManager> utilityProc =
+ UtilityProcessManager::GetSingleton();
+ MOZ_ASSERT(utilityProc, "No UtilityprocessManager?");
+
+ for (size_t i = 0; i < SandboxingKind::COUNT; ++i) {
+ auto sbKind = static_cast<SandboxingKind>(i);
+ if (!utilityProc->Process(sbKind)) {
+ continue;
+ }
+ if (aActorName == UtilityActorName::EndGuard_) {
+ return sbKind;
+ }
+ for (auto actor : utilityProc->GetActors(sbKind)) {
+ if (actor == aActorName) {
+ return sbKind;
+ }
+ }
+ }
+
+ return SandboxingKind::COUNT;
+}
+
+# ifdef XP_WIN
+namespace {
+// Promise implementation for `UntilChildProcessDead`.
+//
+// Resolves the provided JS promise when the provided Windows HANDLE becomes
+// signaled.
+class WinHandlePromiseImpl final {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(WinHandlePromiseImpl)
+
+ using HandlePtr = mozilla::UniqueFileHandle;
+
+ // Takes ownership of aHandle.
+ static void Create(mozilla::UniqueFileHandle handle,
+ RefPtr<mozilla::dom::Promise> promise) {
+ MOZ_ASSERT(handle);
+ MOZ_ASSERT(promise);
+
+ RefPtr obj{new WinHandlePromiseImpl(std::move(handle), std::move(promise))};
+
+ // WARNING: This creates an owning-reference cycle: (self -> HandleWatcher
+ // -> Runnable -> self). `obj` will therefore only be destroyed when and
+ // if the HANDLE is signaled.
+ obj->watcher.Watch(obj->handle.get(), GetCurrentSerialEventTarget(),
+ NewRunnableMethod("WinHandlePromiseImpl::Resolve", obj,
+ &WinHandlePromiseImpl::Resolve));
+ }
+
+ private:
+ WinHandlePromiseImpl(mozilla::UniqueFileHandle handle,
+ RefPtr<mozilla::dom::Promise> promise)
+ : handle(std::move(handle)), promise(std::move(promise)) {}
+
+ ~WinHandlePromiseImpl() { watcher.Stop(); }
+
+ void Resolve() { promise->MaybeResolveWithUndefined(); }
+
+ mozilla::UniqueFileHandle handle;
+ HandleWatcher watcher;
+ RefPtr<mozilla::dom::Promise> promise;
+};
+
+} // namespace
+# endif
+
+NS_IMETHODIMP
+UtilityProcessTest::StartProcess(const nsTArray<nsCString>& aActorsToRegister,
+ JSContext* aCx,
+ mozilla::dom::Promise** aOutPromise) {
+ NS_ENSURE_ARG(aOutPromise);
+ *aOutPromise = nullptr;
+ nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
+ if (NS_WARN_IF(!global)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ ErrorResult erv;
+ RefPtr<dom::Promise> promise = dom::Promise::Create(global, erv);
+ if (NS_WARN_IF(erv.Failed())) {
+ return erv.StealNSResult();
+ }
+
+ RefPtr<UtilityProcessManager> utilityProc =
+ UtilityProcessManager::GetSingleton();
+ MOZ_ASSERT(utilityProc, "No UtilityprocessManager?");
+
+ auto actors = aActorsToRegister.Clone();
+
+ utilityProc->LaunchProcess(SandboxingKind::GENERIC_UTILITY)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [promise, utilityProc, actors = std::move(actors)] {
+ RefPtr<UtilityProcessParent> utilityParent =
+ utilityProc->GetProcessParent(SandboxingKind::GENERIC_UTILITY);
+ Maybe<int32_t> utilityPid =
+ utilityProc->ProcessPid(SandboxingKind::GENERIC_UTILITY);
+ for (size_t i = 0; i < actors.Length(); ++i) {
+ auto uan = UtilityActorNameFromString(actors[i]);
+ utilityProc->RegisterActor(utilityParent, uan);
+ }
+ if (utilityPid.isSome()) {
+ promise->MaybeResolve(*utilityPid);
+ } else {
+ promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
+ }
+ },
+ [promise](nsresult aError) {
+ MOZ_ASSERT_UNREACHABLE(
+ "UtilityProcessTest; failure to get Utility process");
+ promise->MaybeReject(aError);
+ });
+
+ promise.forget(aOutPromise);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+UtilityProcessTest::NoteIntentionalCrash(uint32_t aPid) {
+ mozilla::NoteIntentionalCrash("utility", aPid);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+UtilityProcessTest::UntilChildProcessDead(
+ uint32_t pid, JSContext* cx, ::mozilla::dom::Promise** aOutPromise) {
+ NS_ENSURE_ARG(aOutPromise);
+ *aOutPromise = nullptr;
+
+# ifdef XP_WIN
+ if (pid == 0) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsIGlobalObject* global = xpc::CurrentNativeGlobal(cx);
+ if (NS_WARN_IF(!global)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ ErrorResult erv;
+ RefPtr<dom::Promise> promise = dom::Promise::Create(global, erv);
+ if (NS_WARN_IF(erv.Failed())) {
+ return erv.StealNSResult();
+ }
+
+ // Get a fresh handle to the child process with the specified PID.
+ mozilla::UniqueFileHandle handle;
+ {
+ bool failed = false;
+ GeckoChildProcessHost::GetAll([&](GeckoChildProcessHost* aProc) {
+ if (handle || failed) {
+ return;
+ }
+ if (aProc->GetChildProcessId() != pid) {
+ return;
+ }
+
+ HANDLE handle_ = nullptr;
+ if (!::DuplicateHandle(
+ ::GetCurrentProcess(), aProc->GetChildProcessHandle(),
+ ::GetCurrentProcess(), &handle_, SYNCHRONIZE, FALSE, 0)) {
+ failed = true;
+ } else {
+ handle.reset(handle_);
+ }
+ });
+
+ if (failed || !handle) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ // Create and attach the resolver for the promise, giving the handle over to
+ // it.
+ WinHandlePromiseImpl::Create(std::move(handle), promise);
+
+ promise.forget(aOutPromise);
+
+ return NS_OK;
+# else // !defined(XP_WIN)
+ return NS_ERROR_NOT_IMPLEMENTED;
+# endif
+}
+
+NS_IMETHODIMP
+UtilityProcessTest::StopProcess(const char* aActorName) {
+ using namespace mozilla::dom;
+
+ SandboxingKind sbKind;
+ if (aActorName) {
+ const nsDependentCString actorStringName(aActorName);
+ UtilityActorName actorName = UtilityActorNameFromString(actorStringName);
+ sbKind = FindUtilityProcessWithActor(actorName);
+ } else {
+ sbKind = FindUtilityProcessWithActor(UtilityActorName::EndGuard_);
+ }
+
+ if (sbKind == SandboxingKind::COUNT) {
+ MOZ_ASSERT_UNREACHABLE(
+ "Attempted to stop process for actor when no "
+ "such process exists");
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<UtilityProcessManager> utilityProc =
+ UtilityProcessManager::GetSingleton();
+ MOZ_ASSERT(utilityProc, "No UtilityprocessManager?");
+
+ utilityProc->CleanShutdown(sbKind);
+ Maybe<int32_t> utilityPid = utilityProc->ProcessPid(sbKind);
+ MOZ_RELEASE_ASSERT(utilityPid.isNothing(),
+ "Should not have a utility process PID anymore");
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+UtilityProcessTest::TestTelemetryProbes() {
+ RefPtr<UtilityProcessManager> utilityProc =
+ UtilityProcessManager::GetSingleton();
+ MOZ_ASSERT(utilityProc, "No UtilityprocessManager?");
+
+ for (RefPtr<UtilityProcessParent>& parent :
+ utilityProc->GetAllProcessesProcessParent()) {
+ Unused << parent->SendTestTelemetryProbes();
+ }
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(UtilityProcessTest, nsIUtilityProcessTest)
+
+} // namespace mozilla::ipc
+#endif // defined(ENABLE_TESTS)
diff --git a/ipc/glue/test/utility_process_xpcom/UtilityProcessTest.h b/ipc/glue/test/utility_process_xpcom/UtilityProcessTest.h
new file mode 100644
index 0000000000..6c80fce71b
--- /dev/null
+++ b/ipc/glue/test/utility_process_xpcom/UtilityProcessTest.h
@@ -0,0 +1,29 @@
+/* -*- 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/. */
+#ifndef _include_ipc_glue_UtilityProcessTest_h_
+#define _include_ipc_glue_UtilityProcessTest_h_
+
+#if defined(ENABLE_TESTS)
+# include "nsServiceManagerUtils.h"
+# include "nsIUtilityProcessTest.h"
+
+namespace mozilla::ipc {
+
+class UtilityProcessTest final : public nsIUtilityProcessTest {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIUTILITYPROCESSTEST
+
+ UtilityProcessTest() = default;
+
+ private:
+ ~UtilityProcessTest() = default;
+};
+
+} // namespace mozilla::ipc
+#endif // defined(ENABLE_TESTS)
+
+#endif // _include_ipc_glue_UtilityProcessTest_h_
diff --git a/ipc/glue/test/utility_process_xpcom/components.conf b/ipc/glue/test/utility_process_xpcom/components.conf
new file mode 100644
index 0000000000..25208ba7fc
--- /dev/null
+++ b/ipc/glue/test/utility_process_xpcom/components.conf
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+ {
+ 'cid': '{0a4478f4-c5ae-4fb1-8686-d5b09fb99afb}',
+ 'contract_ids': ['@mozilla.org/utility-process-test;1'],
+ 'type': 'mozilla::ipc::UtilityProcessTest',
+ 'headers': ['mozilla/ipc/UtilityProcessTest.h'],
+ 'processes': ProcessSelector.MAIN_PROCESS_ONLY,
+ },
+]
diff --git a/ipc/glue/test/utility_process_xpcom/moz.build b/ipc/glue/test/utility_process_xpcom/moz.build
new file mode 100644
index 0000000000..f04b436cbe
--- /dev/null
+++ b/ipc/glue/test/utility_process_xpcom/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla.ipc += ["UtilityProcessTest.h"]
+
+UNIFIED_SOURCES += ["UtilityProcessTest.cpp"]
+
+XPCOM_MANIFESTS += ["components.conf"]
+
+XPIDL_MODULE = "utility_process_xpcom_test"
+
+XPIDL_SOURCES += [
+ "nsIUtilityProcessTest.idl",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
diff --git a/ipc/glue/test/utility_process_xpcom/nsIUtilityProcessTest.idl b/ipc/glue/test/utility_process_xpcom/nsIUtilityProcessTest.idl
new file mode 100644
index 0000000000..142be6f0e1
--- /dev/null
+++ b/ipc/glue/test/utility_process_xpcom/nsIUtilityProcessTest.idl
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* 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 "nsISupports.idl"
+
+[scriptable, uuid(0a4478f4-c5ae-4fb1-8686-d5b09fb99afb)]
+interface nsIUtilityProcessTest : nsISupports
+{
+ /**
+ * ** Test-only Method **
+ *
+ * Start a generic utility process from JS code.
+ *
+ * actorsToAdd: An array of actor names, taken from WebIDLUtilityActorName.
+ * Unlike normal utility processes, test processes launched this way do not
+ * have any associated actor names unless specified here. Empty by default.
+ */
+ [implicit_jscontext]
+ Promise startProcess([optional] in Array<ACString> actorsToAdd);
+
+ /**
+ * ** Test-only Method **
+ *
+ * Report when a child process is actually dead (as opposed to merely having
+ * been removed from our internal list of child processes). Must be called
+ * while the process is still live.
+ *
+ * Only implemented on Windows.
+ */
+ [implicit_jscontext]
+ Promise untilChildProcessDead(in uint32_t pid);
+
+ /**
+ * ** Test-only Method **
+ *
+ * Note that we are going to manually crash a process
+ */
+ void noteIntentionalCrash(in unsigned long pid);
+
+ /**
+ * ** Test-only Method **
+ *
+ * Allowing to stop Utility Process from JS code.
+ * Default behavior is to stop any utility process.
+ */
+ void stopProcess([optional] in string utilityActorName);
+
+ /**
+ * ** Test-only Method **
+ *
+ * Sending Telemetry probes
+ */
+ void testTelemetryProbes();
+};