Bumping manifests a=b2g-bump
[gecko.git] / dom / base / MessagePort.cpp
blob872024093621c68238a29904e8930ce339e6982b
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "MessagePort.h"
7 #include "MessageEvent.h"
8 #include "mozilla/dom/Event.h"
9 #include "mozilla/dom/MessageChannel.h"
10 #include "mozilla/dom/MessagePortBinding.h"
11 #include "mozilla/dom/MessagePortList.h"
12 #include "mozilla/dom/StructuredCloneTags.h"
13 #include "nsContentUtils.h"
14 #include "nsGlobalWindow.h"
15 #include "nsPresContext.h"
16 #include "ScriptSettings.h"
18 #include "nsIDocument.h"
19 #include "nsIDOMFile.h"
20 #include "nsIDOMFileList.h"
21 #include "nsIPresShell.h"
23 namespace mozilla {
24 namespace dom {
26 class DispatchEventRunnable : public nsRunnable
28 friend class MessagePort;
30 public:
31 explicit DispatchEventRunnable(MessagePort* aPort)
32 : mPort(aPort)
36 NS_IMETHOD
37 Run()
39 nsRefPtr<DispatchEventRunnable> mKungFuDeathGrip(this);
41 mPort->mDispatchRunnable = nullptr;
42 mPort->Dispatch();
44 return NS_OK;
47 private:
48 nsRefPtr<MessagePort> mPort;
51 class PostMessageRunnable : public nsRunnable
53 friend class MessagePort;
55 public:
56 NS_DECL_NSIRUNNABLE
58 PostMessageRunnable()
62 ~PostMessageRunnable()
66 JSAutoStructuredCloneBuffer& Buffer()
68 return mBuffer;
71 bool StoreISupports(nsISupports* aSupports)
73 mSupportsArray.AppendElement(aSupports);
74 return true;
77 void Dispatch(MessagePort* aPort)
79 mPort = aPort;
80 NS_DispatchToCurrentThread(this);
83 private:
84 nsRefPtr<MessagePort> mPort;
85 JSAutoStructuredCloneBuffer mBuffer;
87 nsTArray<nsCOMPtr<nsISupports> > mSupportsArray;
90 namespace {
92 struct StructuredCloneInfo
94 PostMessageRunnable* mEvent;
95 MessagePort* mPort;
96 nsRefPtrHashtable<nsRefPtrHashKey<MessagePortBase>, MessagePortBase> mPorts;
99 static JSObject*
100 PostMessageReadStructuredClone(JSContext* cx,
101 JSStructuredCloneReader* reader,
102 uint32_t tag,
103 uint32_t data,
104 void* closure)
106 if (tag == SCTAG_DOM_BLOB || tag == SCTAG_DOM_FILELIST) {
107 NS_ASSERTION(!data, "Data should be empty");
109 nsISupports* supports;
110 if (JS_ReadBytes(reader, &supports, sizeof(supports))) {
111 JS::Rooted<JS::Value> val(cx);
112 if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, supports, &val))) {
113 return val.toObjectOrNull();
118 const JSStructuredCloneCallbacks* runtimeCallbacks =
119 js::GetContextStructuredCloneCallbacks(cx);
121 if (runtimeCallbacks) {
122 return runtimeCallbacks->read(cx, reader, tag, data, nullptr);
125 return nullptr;
128 static bool
129 PostMessageWriteStructuredClone(JSContext* cx,
130 JSStructuredCloneWriter* writer,
131 JS::Handle<JSObject*> obj,
132 void *closure)
134 StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
135 NS_ASSERTION(scInfo, "Must have scInfo!");
137 nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
138 nsContentUtils::XPConnect()->
139 GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative));
140 if (wrappedNative) {
141 uint32_t scTag = 0;
142 nsISupports* supports = wrappedNative->Native();
144 nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports);
145 if (blob) {
146 scTag = SCTAG_DOM_BLOB;
149 nsCOMPtr<nsIDOMFileList> list = do_QueryInterface(supports);
150 if (list) {
151 scTag = SCTAG_DOM_FILELIST;
154 if (scTag) {
155 return JS_WriteUint32Pair(writer, scTag, 0) &&
156 JS_WriteBytes(writer, &supports, sizeof(supports)) &&
157 scInfo->mEvent->StoreISupports(supports);
161 const JSStructuredCloneCallbacks* runtimeCallbacks =
162 js::GetContextStructuredCloneCallbacks(cx);
164 if (runtimeCallbacks) {
165 return runtimeCallbacks->write(cx, writer, obj, nullptr);
168 return false;
171 static bool
172 PostMessageReadTransferStructuredClone(JSContext* aCx,
173 JSStructuredCloneReader* reader,
174 uint32_t tag, void* data,
175 uint64_t unused,
176 void* aClosure,
177 JS::MutableHandle<JSObject*> returnObject)
179 StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
180 NS_ASSERTION(scInfo, "Must have scInfo!");
182 if (tag == SCTAG_DOM_MAP_MESSAGEPORT) {
183 MessagePort* port = static_cast<MessagePort*>(data);
184 port->BindToOwner(scInfo->mPort->GetOwner());
185 scInfo->mPorts.Put(port, nullptr);
187 JS::Rooted<JSObject*> obj(aCx, port->WrapObject(aCx));
188 if (!obj || !JS_WrapObject(aCx, &obj)) {
189 return false;
192 MOZ_ASSERT(port->GetOwner() == scInfo->mPort->GetOwner());
193 returnObject.set(obj);
194 return true;
197 return false;
200 static bool
201 PostMessageTransferStructuredClone(JSContext* aCx,
202 JS::Handle<JSObject*> aObj,
203 void* aClosure,
204 uint32_t* aTag,
205 JS::TransferableOwnership* aOwnership,
206 void** aContent,
207 uint64_t *aExtraData)
209 StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
210 NS_ASSERTION(scInfo, "Must have scInfo!");
212 MessagePortBase *port = nullptr;
213 nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
214 if (NS_SUCCEEDED(rv)) {
215 nsRefPtr<MessagePortBase> newPort;
216 if (scInfo->mPorts.Get(port, getter_AddRefs(newPort))) {
217 // No duplicate.
218 return false;
221 newPort = port->Clone();
222 scInfo->mPorts.Put(port, newPort);
224 *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
225 *aOwnership = JS::SCTAG_TMO_CUSTOM;
226 *aContent = newPort;
227 *aExtraData = 0;
229 return true;
232 return false;
235 static void
236 PostMessageFreeTransferStructuredClone(uint32_t aTag, JS::TransferableOwnership aOwnership,
237 void* aData,
238 uint64_t aExtraData,
239 void* aClosure)
241 StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
242 NS_ASSERTION(scInfo, "Must have scInfo!");
244 if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
245 MOZ_ASSERT(aOwnership == JS::SCTAG_TMO_CUSTOM);
246 nsRefPtr<MessagePort> port(static_cast<MessagePort*>(aData));
247 scInfo->mPorts.Remove(port);
251 JSStructuredCloneCallbacks kPostMessageCallbacks = {
252 PostMessageReadStructuredClone,
253 PostMessageWriteStructuredClone,
254 nullptr,
255 PostMessageReadTransferStructuredClone,
256 PostMessageTransferStructuredClone,
257 PostMessageFreeTransferStructuredClone
260 } // anonymous namespace
262 static PLDHashOperator
263 PopulateMessagePortList(MessagePortBase* aKey, MessagePortBase* aValue, void* aClosure)
265 nsTArray<nsRefPtr<MessagePortBase> > *array =
266 static_cast<nsTArray<nsRefPtr<MessagePortBase> > *>(aClosure);
268 array->AppendElement(aKey);
269 return PL_DHASH_NEXT;
272 NS_IMETHODIMP
273 PostMessageRunnable::Run()
275 MOZ_ASSERT(mPort);
277 AutoJSAPI jsapi;
278 if (NS_WARN_IF(!jsapi.Init(mPort->GetParentObject()))) {
279 return NS_ERROR_UNEXPECTED;
281 JSContext* cx = jsapi.cx();
283 // Deserialize the structured clone data
284 JS::Rooted<JS::Value> messageData(cx);
285 StructuredCloneInfo scInfo;
286 scInfo.mEvent = this;
287 scInfo.mPort = mPort;
289 if (!mBuffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) {
290 return NS_ERROR_DOM_DATA_CLONE_ERR;
293 // Create the event
294 nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
295 do_QueryInterface(mPort->GetOwner());
296 nsRefPtr<MessageEvent> event =
297 new MessageEvent(eventTarget, nullptr, nullptr);
299 event->InitMessageEvent(NS_LITERAL_STRING("message"), false /* non-bubbling */,
300 false /* cancelable */, messageData, EmptyString(),
301 EmptyString(), nullptr);
302 event->SetTrusted(true);
303 event->SetSource(mPort);
305 nsTArray<nsRefPtr<MessagePortBase> > ports;
306 scInfo.mPorts.EnumerateRead(PopulateMessagePortList, &ports);
307 event->SetPorts(new MessagePortList(static_cast<dom::Event*>(event.get()), ports));
309 bool status;
310 mPort->DispatchEvent(static_cast<dom::Event*>(event.get()), &status);
311 return status ? NS_OK : NS_ERROR_FAILURE;
314 MessagePortBase::MessagePortBase(nsPIDOMWindow* aWindow)
315 : DOMEventTargetHelper(aWindow)
317 // SetIsDOMBinding() is called by DOMEventTargetHelper's ctor.
320 MessagePortBase::MessagePortBase()
322 SetIsDOMBinding();
325 NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
327 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
328 DOMEventTargetHelper)
329 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEntangledPort)
331 // Custom unlink loop because this array contains nsRunnable objects
332 // which are not cycle colleactable.
333 while (!tmp->mMessageQueue.IsEmpty()) {
334 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageQueue[0]->mPort);
335 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageQueue[0]->mSupportsArray);
336 tmp->mMessageQueue.RemoveElementAt(0);
339 if (tmp->mDispatchRunnable) {
340 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDispatchRunnable->mPort);
343 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
345 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
346 DOMEventTargetHelper)
347 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEntangledPort)
349 // Custom unlink loop because this array contains nsRunnable objects
350 // which are not cycle colleactable.
351 for (uint32_t i = 0, len = tmp->mMessageQueue.Length(); i < len; ++i) {
352 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageQueue[i]->mPort);
353 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageQueue[i]->mSupportsArray);
356 if (tmp->mDispatchRunnable) {
357 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDispatchRunnable->mPort);
360 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
362 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessagePort)
363 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
365 NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper)
366 NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper)
368 MessagePort::MessagePort(nsPIDOMWindow* aWindow)
369 : MessagePortBase(aWindow)
370 , mMessageQueueEnabled(false)
374 MessagePort::~MessagePort()
376 Close();
379 JSObject*
380 MessagePort::WrapObject(JSContext* aCx)
382 return MessagePortBinding::Wrap(aCx, this);
385 void
386 MessagePort::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
387 const Optional<Sequence<JS::Value>>& aTransferable,
388 ErrorResult& aRv)
390 nsRefPtr<PostMessageRunnable> event = new PostMessageRunnable();
392 // We *must* clone the data here, or the JS::Value could be modified
393 // by script
394 StructuredCloneInfo scInfo;
395 scInfo.mEvent = event;
396 scInfo.mPort = this;
398 JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
399 if (aTransferable.WasPassed()) {
400 const Sequence<JS::Value>& realTransferable = aTransferable.Value();
402 // The input sequence only comes from the generated bindings code, which
403 // ensures it is rooted.
404 JS::HandleValueArray elements =
405 JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
406 realTransferable.Elements());
408 JSObject* array =
409 JS_NewArrayObject(aCx, elements);
410 if (!array) {
411 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
412 return;
414 transferable.setObject(*array);
417 if (!event->Buffer().write(aCx, aMessage, transferable,
418 &kPostMessageCallbacks, &scInfo)) {
419 aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
420 return;
423 if (!mEntangledPort) {
424 return;
427 mEntangledPort->mMessageQueue.AppendElement(event);
428 mEntangledPort->Dispatch();
431 void
432 MessagePort::Start()
434 if (mMessageQueueEnabled) {
435 return;
438 mMessageQueueEnabled = true;
439 Dispatch();
442 void
443 MessagePort::Dispatch()
445 if (!mMessageQueueEnabled || mMessageQueue.IsEmpty() || mDispatchRunnable) {
446 return;
449 nsRefPtr<PostMessageRunnable> event = mMessageQueue.ElementAt(0);
450 mMessageQueue.RemoveElementAt(0);
452 event->Dispatch(this);
454 mDispatchRunnable = new DispatchEventRunnable(this);
455 NS_DispatchToCurrentThread(mDispatchRunnable);
458 void
459 MessagePort::Close()
461 if (!mEntangledPort) {
462 return;
465 // This avoids loops.
466 nsRefPtr<MessagePort> port = mEntangledPort;
467 mEntangledPort = nullptr;
469 // Let's disentangle the 2 ports symmetrically.
470 port->Close();
473 EventHandlerNonNull*
474 MessagePort::GetOnmessage()
476 if (NS_IsMainThread()) {
477 return GetEventHandler(nsGkAtoms::onmessage, EmptyString());
479 return GetEventHandler(nullptr, NS_LITERAL_STRING("message"));
482 void
483 MessagePort::SetOnmessage(EventHandlerNonNull* aCallback)
485 if (NS_IsMainThread()) {
486 SetEventHandler(nsGkAtoms::onmessage, EmptyString(), aCallback);
487 } else {
488 SetEventHandler(nullptr, NS_LITERAL_STRING("message"), aCallback);
491 // When using onmessage, the call to start() is implied.
492 Start();
495 void
496 MessagePort::Entangle(MessagePort* aMessagePort)
498 MOZ_ASSERT(aMessagePort);
499 MOZ_ASSERT(aMessagePort != this);
501 Close();
503 mEntangledPort = aMessagePort;
506 already_AddRefed<MessagePortBase>
507 MessagePort::Clone()
509 nsRefPtr<MessagePort> newPort = new MessagePort(nullptr);
511 // Move all the events in the port message queue of original port.
512 newPort->mMessageQueue.SwapElements(mMessageQueue);
514 if (mEntangledPort) {
515 nsRefPtr<MessagePort> port = mEntangledPort;
516 mEntangledPort = nullptr;
518 newPort->Entangle(port);
519 port->Entangle(newPort);
522 return newPort.forget();
525 } // namespace dom
526 } // namespace mozilla