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"
26 class DispatchEventRunnable
: public nsRunnable
28 friend class MessagePort
;
31 explicit DispatchEventRunnable(MessagePort
* aPort
)
39 nsRefPtr
<DispatchEventRunnable
> mKungFuDeathGrip(this);
41 mPort
->mDispatchRunnable
= nullptr;
48 nsRefPtr
<MessagePort
> mPort
;
51 class PostMessageRunnable
: public nsRunnable
53 friend class MessagePort
;
62 ~PostMessageRunnable()
66 JSAutoStructuredCloneBuffer
& Buffer()
71 bool StoreISupports(nsISupports
* aSupports
)
73 mSupportsArray
.AppendElement(aSupports
);
77 void Dispatch(MessagePort
* aPort
)
80 NS_DispatchToCurrentThread(this);
84 nsRefPtr
<MessagePort
> mPort
;
85 JSAutoStructuredCloneBuffer mBuffer
;
87 nsTArray
<nsCOMPtr
<nsISupports
> > mSupportsArray
;
92 struct StructuredCloneInfo
94 PostMessageRunnable
* mEvent
;
96 nsRefPtrHashtable
<nsRefPtrHashKey
<MessagePortBase
>, MessagePortBase
> mPorts
;
100 PostMessageReadStructuredClone(JSContext
* cx
,
101 JSStructuredCloneReader
* reader
,
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);
129 PostMessageWriteStructuredClone(JSContext
* cx
,
130 JSStructuredCloneWriter
* writer
,
131 JS::Handle
<JSObject
*> obj
,
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
));
142 nsISupports
* supports
= wrappedNative
->Native();
144 nsCOMPtr
<nsIDOMBlob
> blob
= do_QueryInterface(supports
);
146 scTag
= SCTAG_DOM_BLOB
;
149 nsCOMPtr
<nsIDOMFileList
> list
= do_QueryInterface(supports
);
151 scTag
= SCTAG_DOM_FILELIST
;
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);
172 PostMessageReadTransferStructuredClone(JSContext
* aCx
,
173 JSStructuredCloneReader
* reader
,
174 uint32_t tag
, void* data
,
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
)) {
192 MOZ_ASSERT(port
->GetOwner() == scInfo
->mPort
->GetOwner());
193 returnObject
.set(obj
);
201 PostMessageTransferStructuredClone(JSContext
* aCx
,
202 JS::Handle
<JSObject
*> aObj
,
205 JS::TransferableOwnership
* aOwnership
,
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
))) {
221 newPort
= port
->Clone();
222 scInfo
->mPorts
.Put(port
, newPort
);
224 *aTag
= SCTAG_DOM_MAP_MESSAGEPORT
;
225 *aOwnership
= JS::SCTAG_TMO_CUSTOM
;
236 PostMessageFreeTransferStructuredClone(uint32_t aTag
, JS::TransferableOwnership aOwnership
,
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
,
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
;
273 PostMessageRunnable::Run()
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
;
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
));
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()
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()
380 MessagePort::WrapObject(JSContext
* aCx
)
382 return MessagePortBinding::Wrap(aCx
, this);
386 MessagePort::PostMessageMoz(JSContext
* aCx
, JS::Handle
<JS::Value
> aMessage
,
387 const Optional
<Sequence
<JS::Value
>>& aTransferable
,
390 nsRefPtr
<PostMessageRunnable
> event
= new PostMessageRunnable();
392 // We *must* clone the data here, or the JS::Value could be modified
394 StructuredCloneInfo scInfo
;
395 scInfo
.mEvent
= event
;
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());
409 JS_NewArrayObject(aCx
, elements
);
411 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
414 transferable
.setObject(*array
);
417 if (!event
->Buffer().write(aCx
, aMessage
, transferable
,
418 &kPostMessageCallbacks
, &scInfo
)) {
419 aRv
.Throw(NS_ERROR_DOM_DATA_CLONE_ERR
);
423 if (!mEntangledPort
) {
427 mEntangledPort
->mMessageQueue
.AppendElement(event
);
428 mEntangledPort
->Dispatch();
434 if (mMessageQueueEnabled
) {
438 mMessageQueueEnabled
= true;
443 MessagePort::Dispatch()
445 if (!mMessageQueueEnabled
|| mMessageQueue
.IsEmpty() || mDispatchRunnable
) {
449 nsRefPtr
<PostMessageRunnable
> event
= mMessageQueue
.ElementAt(0);
450 mMessageQueue
.RemoveElementAt(0);
452 event
->Dispatch(this);
454 mDispatchRunnable
= new DispatchEventRunnable(this);
455 NS_DispatchToCurrentThread(mDispatchRunnable
);
461 if (!mEntangledPort
) {
465 // This avoids loops.
466 nsRefPtr
<MessagePort
> port
= mEntangledPort
;
467 mEntangledPort
= nullptr;
469 // Let's disentangle the 2 ports symmetrically.
474 MessagePort::GetOnmessage()
476 if (NS_IsMainThread()) {
477 return GetEventHandler(nsGkAtoms::onmessage
, EmptyString());
479 return GetEventHandler(nullptr, NS_LITERAL_STRING("message"));
483 MessagePort::SetOnmessage(EventHandlerNonNull
* aCallback
)
485 if (NS_IsMainThread()) {
486 SetEventHandler(nsGkAtoms::onmessage
, EmptyString(), aCallback
);
488 SetEventHandler(nullptr, NS_LITERAL_STRING("message"), aCallback
);
491 // When using onmessage, the call to start() is implied.
496 MessagePort::Entangle(MessagePort
* aMessagePort
)
498 MOZ_ASSERT(aMessagePort
);
499 MOZ_ASSERT(aMessagePort
!= this);
503 mEntangledPort
= aMessagePort
;
506 already_AddRefed
<MessagePortBase
>
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();
526 } // namespace mozilla