WorkerTestUtils.cpp (5553B)
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/dom/WorkerTestUtils.h" 8 9 #include "mozilla/ErrorResult.h" 10 #include "mozilla/Monitor.h" 11 #include "mozilla/RefPtr.h" 12 #include "mozilla/dom/WorkerPrivate.h" 13 #include "mozilla/dom/WorkerRef.h" 14 #include "mozilla/dom/WorkerTestUtilsBinding.h" 15 #include "nsIObserverService.h" 16 #include "nsThreadUtils.h" 17 18 namespace mozilla::dom { 19 20 uint32_t WorkerTestUtils::CurrentTimerNestingLevel(const GlobalObject& aGlobal, 21 ErrorResult& aErr) { 22 MOZ_ASSERT(!NS_IsMainThread()); 23 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); 24 MOZ_ASSERT(worker); 25 return worker->GetCurrentTimerNestingLevel(); 26 } 27 28 bool WorkerTestUtils::IsRunningInBackground(const GlobalObject&, 29 ErrorResult& aErr) { 30 MOZ_ASSERT(!NS_IsMainThread()); 31 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); 32 MOZ_ASSERT(worker); 33 return worker->IsRunningInBackground(); 34 } 35 36 namespace { 37 38 // Helper for HoldStrongWorkerRefUntilMainThreadObserverNotified that optionally 39 // holds a ThreadSafeWorkerRef until the given observer notification is notified 40 // and also notifies a monitor. 41 class WorkerTestUtilsObserver final : public nsIObserver { 42 public: 43 WorkerTestUtilsObserver(const nsACString& aTopic, 44 RefPtr<ThreadSafeWorkerRef>&& aWorkerRef) 45 : mMonitor("WorkerTestUtils"), 46 mTopic(aTopic), 47 mWorkerRef(std::move(aWorkerRef)), 48 mRegistered(false), 49 mObserved(false) {} 50 51 NS_DECL_THREADSAFE_ISUPPORTS 52 NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, 53 const char16_t* aData) override { 54 // We only register for one topic so we don't actually need to compare it. 55 nsCOMPtr<nsIObserverService> observerService = 56 services::GetObserverService(); 57 MOZ_ALWAYS_SUCCEEDS(observerService->RemoveObserver(this, mTopic.get())); 58 59 // The ThreadSafeWorkerRef is responsible for / knows how to drop the 60 // underlying StrongWorkerRef on the worker. 61 mWorkerRef = nullptr; 62 63 MonitorAutoLock lock(mMonitor); 64 mObserved = true; 65 mMonitor.Notify(); 66 67 return NS_OK; 68 } 69 70 void Register() { 71 nsCOMPtr<nsIObserverService> observerService = 72 services::GetObserverService(); 73 MOZ_ALWAYS_SUCCEEDS( 74 observerService->AddObserver(this, mTopic.get(), false)); 75 76 MonitorAutoLock lock(mMonitor); 77 mRegistered = true; 78 mMonitor.Notify(); 79 } 80 81 void WaitOnRegister() { 82 MonitorAutoLock lock(mMonitor); 83 while (!mRegistered) { 84 mMonitor.Wait(); 85 } 86 } 87 88 void WaitOnObserver() { 89 MonitorAutoLock lock(mMonitor); 90 while (!mObserved) { 91 mMonitor.Wait(); 92 } 93 } 94 95 private: 96 ~WorkerTestUtilsObserver() = default; 97 98 Monitor mMonitor; 99 nsAutoCString mTopic; 100 RefPtr<ThreadSafeWorkerRef> mWorkerRef; 101 bool mRegistered; 102 bool mObserved; 103 }; 104 105 NS_IMPL_ISUPPORTS(WorkerTestUtilsObserver, nsIObserver) 106 107 } // anonymous namespace 108 109 void WorkerTestUtils::HoldStrongWorkerRefUntilMainThreadObserverNotified( 110 const GlobalObject&, const nsACString& aTopic, ErrorResult& aErr) { 111 MOZ_ASSERT(!NS_IsMainThread()); 112 113 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 114 MOZ_ASSERT(workerPrivate); 115 116 RefPtr<StrongWorkerRef> strongWorkerRef = 117 StrongWorkerRef::Create(workerPrivate, "WorkerTestUtils"); 118 if (NS_WARN_IF(!strongWorkerRef)) { 119 aErr.Throw(NS_ERROR_FAILURE); 120 return; 121 } 122 123 RefPtr<ThreadSafeWorkerRef> tsWorkerRef = 124 new ThreadSafeWorkerRef(strongWorkerRef); 125 126 auto observer = 127 MakeRefPtr<WorkerTestUtilsObserver>(aTopic, std::move(tsWorkerRef)); 128 129 aErr = NS_DispatchToMainThread(NewRunnableMethod( 130 "WorkerTestUtils::HoldStrongWorkerRefUntilMainThreadObserverNotified", 131 observer, &WorkerTestUtilsObserver::Register)); 132 133 // Wait for the observer to be registered before returning control so that we 134 // can be certain we won't miss an observer notification. 135 observer->WaitOnRegister(); 136 } 137 138 void WorkerTestUtils::BlockUntilMainThreadObserverNotified( 139 const GlobalObject&, const nsACString& aTopic, 140 WorkerTestCallback& aWhenObserving, ErrorResult& aErr) { 141 MOZ_ASSERT(!NS_IsMainThread()); 142 143 auto observer = MakeRefPtr<WorkerTestUtilsObserver>(aTopic, nullptr); 144 145 aErr = NS_DispatchToMainThread( 146 NewRunnableMethod("WorkerTestUtils::BlockUntilMainThreadObserverNotified", 147 observer, &WorkerTestUtilsObserver::Register)); 148 if (aErr.Failed()) { 149 return; 150 } 151 152 observer->WaitOnRegister(); 153 154 aWhenObserving.Call(aErr); 155 if (aErr.Failed()) { 156 return; 157 } 158 159 observer->WaitOnObserver(); 160 } 161 162 void WorkerTestUtils::NotifyObserverOnMainThread(const GlobalObject&, 163 const nsACString& aTopic, 164 ErrorResult& aErr) { 165 MOZ_ASSERT(!NS_IsMainThread()); 166 167 aErr = NS_DispatchToMainThread(NS_NewRunnableFunction( 168 "WorkerTestUtils::NotifyObserverOnMainThread", 169 [topic = nsCString(aTopic)] { 170 nsCOMPtr<nsIObserverService> observerService = 171 services::GetObserverService(); 172 observerService->NotifyObservers(nullptr, topic.get(), nullptr); 173 })); 174 } 175 176 } // namespace mozilla::dom