SharedWorkerOp.cpp (7674B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "SharedWorkerOp.h" 6 7 #include "mozilla/dom/MessagePort.h" 8 #include "mozilla/dom/RemoteWorkerChild.h" 9 #include "mozilla/dom/RemoteWorkerNonLifeCycleOpControllerChild.h" 10 #include "mozilla/dom/WorkerPrivate.h" 11 #include "mozilla/dom/WorkerRunnable.h" 12 #include "mozilla/dom/WorkerScope.h" 13 14 namespace mozilla::dom { 15 16 using remoteworker::Canceled; 17 using remoteworker::Killed; 18 using remoteworker::Pending; 19 using remoteworker::Running; 20 21 namespace { 22 23 // Normal runnable because AddPortIdentifier() is going to exec JS code. 24 class MessagePortIdentifierRunnable final : public WorkerSameThreadRunnable { 25 public: 26 MessagePortIdentifierRunnable( 27 RemoteWorkerNonLifeCycleOpControllerChild* aActor, 28 const MessagePortIdentifier& aPortIdentifier) 29 : WorkerSameThreadRunnable("MessagePortIdentifierRunnable"), 30 mActor(aActor), 31 mPortIdentifier(aPortIdentifier) {} 32 33 private: 34 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 35 if (aWorkerPrivate->GlobalScope()->IsDying()) { 36 mPortIdentifier.ForceClose(); 37 return true; 38 } 39 if (!aWorkerPrivate->ConnectMessagePort(aCx, mPortIdentifier)) { 40 mActor->ErrorPropagation(NS_ERROR_FAILURE); 41 } 42 return true; 43 } 44 45 RefPtr<RemoteWorkerNonLifeCycleOpControllerChild> mActor; 46 UniqueMessagePortId mPortIdentifier; 47 }; 48 49 class UpdateWindowIDRunnable final : public WorkerThreadRunnable { 50 public: 51 UpdateWindowIDRunnable(const uint64_t& aWindowID, const bool& aIsAdd) 52 : WorkerThreadRunnable("UpdateWindowIDRunnable"), 53 mWindowID(aWindowID), 54 mIsAdd(aIsAdd) {} 55 56 private: 57 bool PreDispatch(WorkerPrivate* aWorkerPrivate) final { return true; } 58 void PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) final { 59 } 60 61 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 62 aWorkerPrivate->UpdateWindowIDToDebugger(mWindowID, mIsAdd); 63 return true; 64 } 65 66 uint64_t mWindowID; 67 bool mIsAdd; 68 }; 69 70 } // namespace 71 72 SharedWorkerOp::SharedWorkerOp(SharedWorkerOpArgs&& aArgs) 73 : mOpArgs(std::move(aArgs)) {} 74 75 SharedWorkerOp::~SharedWorkerOp() { MOZ_ASSERT(mStarted); } 76 77 void SharedWorkerOp::Cancel() { 78 #ifdef DEBUG 79 mStarted = true; 80 #endif 81 } 82 83 bool SharedWorkerOp::MaybeStart(RemoteWorkerChild* aOwner, 84 RemoteWorkerState& aState) { 85 MOZ_ASSERT(!mStarted); 86 MOZ_ASSERT(aOwner); 87 // Thread: We are on the Worker Launcher thread. 88 89 // Return false, indicating we should queue this op if our current state is 90 // pending and this isn't a termination op (which should skip the line). 91 if (aState.is<Pending>() && !IsTerminationOp()) { 92 return false; 93 } 94 95 // If the worker is already shutting down (which should be unexpected 96 // because we should be told new operations after a termination op), just 97 // return true to indicate the op should be discarded. 98 if (aState.is<Canceled>() || aState.is<Killed>()) { 99 #ifdef DEBUG 100 mStarted = true; 101 #endif 102 return true; 103 } 104 105 MOZ_ASSERT(aState.is<Running>() || IsTerminationOp()); 106 107 if (mOpArgs.type() == SharedWorkerOpArgs::TSharedWorkerThawOpArgs) { 108 aOwner->SetIsThawing(true); 109 } 110 111 RefPtr<SharedWorkerOp> self = this; 112 RefPtr<RemoteWorkerChild> owner = aOwner; 113 114 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 115 __func__, [self = std::move(self), owner = std::move(owner)]() mutable { 116 { 117 auto lock = owner->mState.Lock(); 118 119 if (NS_WARN_IF(lock->is<Canceled>() || lock->is<Killed>())) { 120 self->Cancel(); 121 return; 122 } 123 } 124 125 self->StartOnMainThread(owner); 126 if (self->mOpArgs.type() == 127 SharedWorkerOpArgs::TSharedWorkerThawOpArgs) { 128 owner->SetIsThawing(false); 129 owner->RunAllPendingOpsOnMainThread(); 130 } 131 }); 132 133 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget())); 134 135 #ifdef DEBUG 136 mStarted = true; 137 #endif 138 139 return true; 140 } 141 142 void SharedWorkerOp::StartOnMainThread(RefPtr<RemoteWorkerChild>& aOwner) { 143 AssertIsOnMainThread(); 144 145 if (IsTerminationOp()) { 146 aOwner->CloseWorkerOnMainThread(); 147 return; 148 } 149 150 auto lock = aOwner->mState.Lock(); 151 MOZ_ASSERT(lock->is<Running>()); 152 if (!lock->is<Running>()) { 153 aOwner->ErrorPropagationDispatch(NS_ERROR_DOM_INVALID_STATE_ERR); 154 return; 155 } 156 157 RefPtr<WorkerPrivate> workerPrivate = lock->as<Running>().mWorkerPrivate; 158 159 MOZ_ASSERT(workerPrivate); 160 161 if (mOpArgs.type() == SharedWorkerOpArgs::TSharedWorkerSuspendOpArgs) { 162 workerPrivate->ParentWindowPaused(); 163 } else if (mOpArgs.type() == SharedWorkerOpArgs::TSharedWorkerResumeOpArgs) { 164 workerPrivate->ParentWindowResumed(); 165 } else if (mOpArgs.type() == SharedWorkerOpArgs::TSharedWorkerFreezeOpArgs) { 166 workerPrivate->Freeze(nullptr); 167 } else if (mOpArgs.type() == SharedWorkerOpArgs::TSharedWorkerThawOpArgs) { 168 workerPrivate->Thaw(nullptr); 169 } else if (mOpArgs.type() == 170 SharedWorkerOpArgs::TSharedWorkerPortIdentifierOpArgs) { 171 MOZ_CRASH( 172 "PortIdentifierOpArgs should not be processed by " 173 "StartOnMainThread!!!"); 174 } else if (mOpArgs.type() == 175 SharedWorkerOpArgs::TSharedWorkerAddWindowIDOpArgs) { 176 aOwner->mWindowIDs.AppendElement( 177 mOpArgs.get_SharedWorkerAddWindowIDOpArgs().windowID()); 178 RefPtr<UpdateWindowIDRunnable> r = new UpdateWindowIDRunnable( 179 mOpArgs.get_SharedWorkerAddWindowIDOpArgs().windowID(), true); 180 (void)r->Dispatch(workerPrivate); 181 } else if (mOpArgs.type() == 182 SharedWorkerOpArgs::TSharedWorkerRemoveWindowIDOpArgs) { 183 aOwner->mWindowIDs.RemoveElement( 184 mOpArgs.get_SharedWorkerRemoveWindowIDOpArgs().windowID()); 185 RefPtr<UpdateWindowIDRunnable> r = new UpdateWindowIDRunnable( 186 mOpArgs.get_SharedWorkerRemoveWindowIDOpArgs().windowID(), false); 187 (void)r->Dispatch(workerPrivate); 188 } else { 189 MOZ_CRASH("Unknown SharedWorkerOpArgs type!"); 190 } 191 192 #ifdef DEBUG 193 if (!mStarted) { 194 mStarted = true; 195 } 196 #endif 197 } 198 199 void SharedWorkerOp::Start(RemoteWorkerNonLifeCycleOpControllerChild* aOwner, 200 RemoteWorkerState& aState) { 201 MOZ_ASSERT(!mStarted); 202 MOZ_ASSERT(aOwner); 203 // Thread: We are on the Worker thread. 204 205 // Only PortIdentifierOp is NonLifeCycle related opertaion. 206 MOZ_ASSERT(mOpArgs.type() == 207 SharedWorkerOpArgs::TSharedWorkerPortIdentifierOpArgs); 208 209 // Should never be Pending state. 210 MOZ_ASSERT(!aState.is<Pending>()); 211 212 // If the worker is already shutting down (which should be unexpected 213 // because we should be told new operations after a termination op), just 214 // return directly. 215 if (aState.is<Canceled>() || aState.is<Killed>()) { 216 #ifdef DEBUG 217 mStarted = true; 218 #endif 219 MessagePort::ForceClose( 220 mOpArgs.get_SharedWorkerPortIdentifierOpArgs().portIdentifier()); 221 return; 222 } 223 224 MOZ_ASSERT(aState.is<Running>()); 225 226 // RefPtr<WorkerPrivate> workerPrivate = aState.as<Running>().mWorkerPrivate; 227 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 228 229 RefPtr<MessagePortIdentifierRunnable> r = new MessagePortIdentifierRunnable( 230 aOwner, mOpArgs.get_SharedWorkerPortIdentifierOpArgs().portIdentifier()); 231 232 if (NS_WARN_IF(!r->Dispatch(workerPrivate))) { 233 aOwner->ErrorPropagation(NS_ERROR_FAILURE); 234 } 235 236 #ifdef DEBUG 237 mStarted = true; 238 #endif 239 } 240 241 bool SharedWorkerOp::IsTerminationOp() const { 242 return mOpArgs.type() == SharedWorkerOpArgs::TSharedWorkerTerminateOpArgs; 243 } 244 245 } // namespace mozilla::dom