diff options
Diffstat (limited to 'ipc/glue/test/utility_process_xpcom/UtilityProcessTest.cpp')
-rw-r--r-- | ipc/glue/test/utility_process_xpcom/UtilityProcessTest.cpp | 280 |
1 files changed, 280 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) |