/* -*- 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/ErrorResult.h" #include "mozilla/Monitor.h" #include "mozilla/RefPtr.h" #include "mozilla/dom/WorkerPrivate.h" #include "mozilla/dom/WorkerRef.h" #include "mozilla/dom/WorkerTestUtils.h" #include "mozilla/dom/WorkerTestUtilsBinding.h" #include "nsIObserverService.h" #include "nsThreadUtils.h" namespace mozilla::dom { uint32_t WorkerTestUtils::CurrentTimerNestingLevel(const GlobalObject& aGlobal, ErrorResult& aErr) { MOZ_ASSERT(!NS_IsMainThread()); WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(worker); return worker->GetCurrentTimerNestingLevel(); } bool WorkerTestUtils::IsRunningInBackground(const GlobalObject&, ErrorResult& aErr) { MOZ_ASSERT(!NS_IsMainThread()); WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(worker); return worker->IsRunningInBackground(); } namespace { // Helper for HoldStrongWorkerRefUntilMainThreadObserverNotified that optionally // holds a ThreadSafeWorkerRef until the given observer notification is notified // and also notifies a monitor. class WorkerTestUtilsObserver final : public nsIObserver { public: WorkerTestUtilsObserver(const nsACString& aTopic, RefPtr&& aWorkerRef) : mMonitor("WorkerTestUtils"), mTopic(aTopic), mWorkerRef(std::move(aWorkerRef)), mRegistered(false), mObserved(false) {} NS_DECL_THREADSAFE_ISUPPORTS NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) override { // We only register for one topic so we don't actually need to compare it. nsCOMPtr observerService = services::GetObserverService(); MOZ_ALWAYS_SUCCEEDS(observerService->RemoveObserver(this, mTopic.get())); // The ThreadSafeWorkerRef is responsible for / knows how to drop the // underlying StrongWorkerRef on the worker. mWorkerRef = nullptr; MonitorAutoLock lock(mMonitor); mObserved = true; mMonitor.Notify(); return NS_OK; } void Register() { nsCOMPtr observerService = services::GetObserverService(); MOZ_ALWAYS_SUCCEEDS( observerService->AddObserver(this, mTopic.get(), false)); MonitorAutoLock lock(mMonitor); mRegistered = true; mMonitor.Notify(); } void WaitOnRegister() { MonitorAutoLock lock(mMonitor); while (!mRegistered) { mMonitor.Wait(); } } void WaitOnObserver() { MonitorAutoLock lock(mMonitor); while (!mObserved) { mMonitor.Wait(); } } private: ~WorkerTestUtilsObserver() = default; Monitor mMonitor; nsAutoCString mTopic; RefPtr mWorkerRef; bool mRegistered; bool mObserved; }; NS_IMPL_ISUPPORTS(WorkerTestUtilsObserver, nsIObserver) } // anonymous namespace void WorkerTestUtils::HoldStrongWorkerRefUntilMainThreadObserverNotified( const GlobalObject&, const nsACString& aTopic, ErrorResult& aErr) { MOZ_ASSERT(!NS_IsMainThread()); WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(workerPrivate); RefPtr strongWorkerRef = StrongWorkerRef::Create(workerPrivate, "WorkerTestUtils"); if (NS_WARN_IF(!strongWorkerRef)) { aErr.Throw(NS_ERROR_FAILURE); return; } RefPtr tsWorkerRef = new ThreadSafeWorkerRef(strongWorkerRef); auto observer = MakeRefPtr(aTopic, std::move(tsWorkerRef)); aErr = NS_DispatchToMainThread(NewRunnableMethod( "WorkerTestUtils::HoldStrongWorkerRefUntilMainThreadObserverNotified", observer, &WorkerTestUtilsObserver::Register)); // Wait for the observer to be registered before returning control so that we // can be certain we won't miss an observer notification. observer->WaitOnRegister(); } void WorkerTestUtils::BlockUntilMainThreadObserverNotified( const GlobalObject&, const nsACString& aTopic, WorkerTestCallback& aWhenObserving, ErrorResult& aErr) { MOZ_ASSERT(!NS_IsMainThread()); auto observer = MakeRefPtr(aTopic, nullptr); aErr = NS_DispatchToMainThread( NewRunnableMethod("WorkerTestUtils::BlockUntilMainThreadObserverNotified", observer, &WorkerTestUtilsObserver::Register)); if (aErr.Failed()) { return; } observer->WaitOnRegister(); aWhenObserving.Call(aErr); if (aErr.Failed()) { return; } observer->WaitOnObserver(); } void WorkerTestUtils::NotifyObserverOnMainThread(const GlobalObject&, const nsACString& aTopic, ErrorResult& aErr) { MOZ_ASSERT(!NS_IsMainThread()); aErr = NS_DispatchToMainThread(NS_NewRunnableFunction( "WorkerTestUtils::NotifyObserverOnMainThread", [topic = nsCString(aTopic)] { nsCOMPtr observerService = services::GetObserverService(); observerService->NotifyObservers(nullptr, topic.get(), nullptr); })); } } // namespace mozilla::dom