|
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 } |