dom/workers/MessagePort.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:bacd46b5b5b5
1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "MessagePort.h"
7
8 #include "mozilla/EventDispatcher.h"
9 #include "mozilla/dom/MessagePortBinding.h"
10 #include "nsIDOMEvent.h"
11
12 #include "SharedWorker.h"
13 #include "WorkerPrivate.h"
14 #include "WorkerRunnable.h"
15
16 using mozilla::dom::EventHandlerNonNull;
17 using mozilla::dom::MessagePortBase;
18 using mozilla::dom::Optional;
19 using mozilla::dom::Sequence;
20 using namespace mozilla;
21
22 USING_WORKERS_NAMESPACE
23
24 namespace {
25
26 class DelayedEventRunnable MOZ_FINAL : public WorkerRunnable
27 {
28 nsRefPtr<MessagePort> mMessagePort;
29 nsTArray<nsCOMPtr<nsIDOMEvent>> mEvents;
30
31 public:
32 DelayedEventRunnable(WorkerPrivate* aWorkerPrivate,
33 TargetAndBusyBehavior aBehavior,
34 MessagePort* aMessagePort,
35 nsTArray<nsCOMPtr<nsIDOMEvent>>& aEvents)
36 : WorkerRunnable(aWorkerPrivate, aBehavior), mMessagePort(aMessagePort)
37 {
38 AssertIsOnMainThread();
39 MOZ_ASSERT(aMessagePort);
40 MOZ_ASSERT(aEvents.Length());
41
42 mEvents.SwapElements(aEvents);
43 }
44
45 bool
46 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
47 };
48
49 } // anonymous namespace
50
51 MessagePort::MessagePort(nsPIDOMWindow* aWindow, SharedWorker* aSharedWorker,
52 uint64_t aSerial)
53 : MessagePortBase(aWindow), mSharedWorker(aSharedWorker),
54 mWorkerPrivate(nullptr), mSerial(aSerial), mStarted(false)
55 {
56 AssertIsOnMainThread();
57 MOZ_ASSERT(aSharedWorker);
58 SetIsDOMBinding();
59 }
60
61 MessagePort::MessagePort(WorkerPrivate* aWorkerPrivate, uint64_t aSerial)
62 : mWorkerPrivate(aWorkerPrivate), mSerial(aSerial), mStarted(false)
63 {
64 aWorkerPrivate->AssertIsOnWorkerThread();
65 SetIsDOMBinding();
66 }
67
68 MessagePort::~MessagePort()
69 {
70 Close();
71 }
72
73 void
74 MessagePort::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
75 const Optional<Sequence<JS::Value>>& aTransferable,
76 ErrorResult& aRv)
77 {
78 AssertCorrectThread();
79
80 if (IsClosed()) {
81 aRv = NS_ERROR_DOM_INVALID_STATE_ERR;
82 return;
83 }
84
85 if (mSharedWorker) {
86 mSharedWorker->PostMessage(aCx, aMessage, aTransferable, aRv);
87 }
88 else {
89 mWorkerPrivate->PostMessageToParentMessagePort(aCx, Serial(), aMessage,
90 aTransferable, aRv);
91 }
92 }
93
94 void
95 MessagePort::Start()
96 {
97 AssertCorrectThread();
98
99 if (IsClosed()) {
100 NS_WARNING("Called start() after calling close()!");
101 return;
102 }
103
104 if (mStarted) {
105 return;
106 }
107
108 mStarted = true;
109
110 if (!mQueuedEvents.IsEmpty()) {
111 WorkerPrivate* workerPrivate;
112 WorkerRunnable::TargetAndBusyBehavior behavior;
113
114 if (mWorkerPrivate) {
115 workerPrivate = mWorkerPrivate;
116 behavior = WorkerRunnable::WorkerThreadModifyBusyCount;
117 }
118 else {
119 workerPrivate = mSharedWorker->GetWorkerPrivate();
120 MOZ_ASSERT(workerPrivate);
121
122 behavior = WorkerRunnable::ParentThreadUnchangedBusyCount;
123 }
124
125 nsRefPtr<DelayedEventRunnable> runnable =
126 new DelayedEventRunnable(workerPrivate, behavior, this, mQueuedEvents);
127 runnable->Dispatch(nullptr);
128 }
129 }
130
131 void
132 MessagePort::Close()
133 {
134 AssertCorrectThread();
135
136 if (!IsClosed()) {
137 CloseInternal();
138 }
139 }
140
141 void
142 MessagePort::QueueEvent(nsIDOMEvent* aEvent)
143 {
144 AssertCorrectThread();
145 MOZ_ASSERT(aEvent);
146 MOZ_ASSERT(!IsClosed());
147 MOZ_ASSERT(!mStarted);
148
149 mQueuedEvents.AppendElement(aEvent);
150 }
151
152 EventHandlerNonNull*
153 MessagePort::GetOnmessage()
154 {
155 AssertCorrectThread();
156
157 return NS_IsMainThread() ? GetEventHandler(nsGkAtoms::onmessage, EmptyString())
158 : GetEventHandler(nullptr, NS_LITERAL_STRING("message"));
159 }
160
161 void
162 MessagePort::SetOnmessage(EventHandlerNonNull* aCallback)
163 {
164 AssertCorrectThread();
165
166 if (NS_IsMainThread()) {
167 SetEventHandler(nsGkAtoms::onmessage, EmptyString(), aCallback);
168 }
169 else {
170 SetEventHandler(nullptr, NS_LITERAL_STRING("message"), aCallback);
171 }
172
173 Start();
174 }
175
176 already_AddRefed<MessagePortBase>
177 MessagePort::Clone()
178 {
179 NS_WARNING("Haven't implemented structured clone for these ports yet!");
180 return nullptr;
181 }
182
183 void
184 MessagePort::CloseInternal()
185 {
186 AssertCorrectThread();
187 MOZ_ASSERT(!IsClosed());
188 MOZ_ASSERT_IF(mStarted, mQueuedEvents.IsEmpty());
189
190 NS_WARN_IF_FALSE(mStarted, "Called close() before start()!");
191
192 if (!mStarted) {
193 mQueuedEvents.Clear();
194 }
195
196 mSharedWorker = nullptr;
197 mWorkerPrivate = nullptr;
198 }
199
200 #ifdef DEBUG
201 void
202 MessagePort::AssertCorrectThread() const
203 {
204 if (IsClosed()) {
205 return; // Can't assert anything if we nulled out our pointers.
206 }
207
208 MOZ_ASSERT((mSharedWorker || mWorkerPrivate) &&
209 !(mSharedWorker && mWorkerPrivate));
210
211 if (mSharedWorker) {
212 AssertIsOnMainThread();
213 }
214 else {
215 mWorkerPrivate->AssertIsOnWorkerThread();
216 }
217 }
218 #endif
219
220 NS_IMPL_ADDREF_INHERITED(mozilla::dom::workers::MessagePort, DOMEventTargetHelper)
221 NS_IMPL_RELEASE_INHERITED(mozilla::dom::workers::MessagePort, DOMEventTargetHelper)
222
223 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessagePort)
224 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
225
226 NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
227
228 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
229 DOMEventTargetHelper)
230 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharedWorker)
231 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueuedEvents)
232 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
233
234 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
235 DOMEventTargetHelper)
236 tmp->Close();
237 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
238
239 JSObject*
240 MessagePort::WrapObject(JSContext* aCx)
241 {
242 AssertCorrectThread();
243
244 return MessagePortBinding::Wrap(aCx, this);
245 }
246
247 nsresult
248 MessagePort::PreHandleEvent(EventChainPreVisitor& aVisitor)
249 {
250 AssertCorrectThread();
251
252 nsIDOMEvent*& event = aVisitor.mDOMEvent;
253
254 if (event) {
255 bool preventDispatch = false;
256
257 if (IsClosed()) {
258 preventDispatch = true;
259 } else if (NS_IsMainThread() && mSharedWorker->IsSuspended()) {
260 mSharedWorker->QueueEvent(event);
261 preventDispatch = true;
262 } else if (!mStarted) {
263 QueueEvent(event);
264 preventDispatch = true;
265 }
266
267 if (preventDispatch) {
268 aVisitor.mCanHandle = false;
269 aVisitor.mParentTarget = nullptr;
270 return NS_OK;
271 }
272 }
273
274 return DOMEventTargetHelper::PreHandleEvent(aVisitor);
275 }
276
277 bool
278 DelayedEventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
279 {
280 MOZ_ASSERT(mMessagePort);
281 mMessagePort->AssertCorrectThread();
282 MOZ_ASSERT(mEvents.Length());
283
284 bool ignored;
285 for (uint32_t i = 0; i < mEvents.Length(); i++) {
286 mMessagePort->DispatchEvent(mEvents[i], &ignored);
287 }
288
289 return true;
290 }

mercurial