Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "MessagePort.h"
8 :
9 : #include "MessageEvent.h"
10 : #include "MessagePortChild.h"
11 : #include "mozilla/dom/BlobBinding.h"
12 : #include "mozilla/dom/Event.h"
13 : #include "mozilla/dom/File.h"
14 : #include "mozilla/dom/MessageChannel.h"
15 : #include "mozilla/dom/MessageEventBinding.h"
16 : #include "mozilla/dom/MessagePortBinding.h"
17 : #include "mozilla/dom/MessagePortChild.h"
18 : #include "mozilla/dom/PMessagePort.h"
19 : #include "mozilla/dom/ScriptSettings.h"
20 : #include "mozilla/dom/StructuredCloneTags.h"
21 : #include "mozilla/dom/WorkerPrivate.h"
22 : #include "mozilla/dom/WorkerScope.h"
23 : #include "mozilla/ipc/BackgroundChild.h"
24 : #include "mozilla/ipc/PBackgroundChild.h"
25 : #include "mozilla/MessagePortTimelineMarker.h"
26 : #include "mozilla/TimelineConsumers.h"
27 : #include "mozilla/TimelineMarker.h"
28 : #include "mozilla/Unused.h"
29 : #include "nsContentUtils.h"
30 : #include "nsGlobalWindow.h"
31 : #include "nsPresContext.h"
32 : #include "SharedMessagePortMessage.h"
33 :
34 : #include "nsIBFCacheEntry.h"
35 : #include "nsIDocument.h"
36 : #include "nsIDOMFileList.h"
37 : #include "nsIPresShell.h"
38 : #include "nsISupportsPrimitives.h"
39 : #include "nsServiceManagerUtils.h"
40 :
41 : #ifdef XP_WIN
42 : #undef PostMessage
43 : #endif
44 :
45 : using namespace mozilla::dom::workers;
46 :
47 : namespace mozilla {
48 : namespace dom {
49 :
50 : class PostMessageRunnable final : public CancelableRunnable
51 : {
52 : friend class MessagePort;
53 :
54 : public:
55 0 : PostMessageRunnable(MessagePort* aPort, SharedMessagePortMessage* aData)
56 0 : : CancelableRunnable("dom::PostMessageRunnable")
57 : , mPort(aPort)
58 0 : , mData(aData)
59 : {
60 0 : MOZ_ASSERT(aPort);
61 0 : MOZ_ASSERT(aData);
62 0 : }
63 :
64 : NS_IMETHOD
65 0 : Run() override
66 : {
67 0 : NS_ASSERT_OWNINGTHREAD(Runnable);
68 :
69 : // The port can be cycle collected while this runnable is pending in
70 : // the event queue.
71 0 : if (!mPort) {
72 0 : return NS_OK;
73 : }
74 :
75 0 : MOZ_ASSERT(mPort->mPostMessageRunnable == this);
76 :
77 0 : nsresult rv = DispatchMessage();
78 :
79 : // We must check if we were waiting for this message in order to shutdown
80 : // the port.
81 0 : mPort->UpdateMustKeepAlive();
82 :
83 0 : mPort->mPostMessageRunnable = nullptr;
84 0 : mPort->Dispatch();
85 :
86 0 : return rv;
87 : }
88 :
89 : nsresult
90 0 : Cancel() override
91 : {
92 0 : NS_ASSERT_OWNINGTHREAD(Runnable);
93 :
94 0 : mPort = nullptr;
95 0 : mData = nullptr;
96 0 : return NS_OK;
97 : }
98 :
99 : private:
100 : nsresult
101 0 : DispatchMessage() const
102 : {
103 0 : NS_ASSERT_OWNINGTHREAD(Runnable);
104 :
105 0 : nsCOMPtr<nsIGlobalObject> globalObject = mPort->GetParentObject();
106 :
107 0 : AutoJSAPI jsapi;
108 0 : if (!globalObject || !jsapi.Init(globalObject)) {
109 0 : NS_WARNING("Failed to initialize AutoJSAPI object.");
110 0 : return NS_ERROR_FAILURE;
111 : }
112 :
113 0 : JSContext* cx = jsapi.cx();
114 :
115 0 : ErrorResult rv;
116 0 : JS::Rooted<JS::Value> value(cx);
117 :
118 0 : UniquePtr<AbstractTimelineMarker> start;
119 0 : UniquePtr<AbstractTimelineMarker> end;
120 0 : RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
121 0 : bool isTimelineRecording = timelines && !timelines->IsEmpty();
122 :
123 0 : if (isTimelineRecording) {
124 0 : start = MakeUnique<MessagePortTimelineMarker>(
125 : ProfileTimelineMessagePortOperationType::DeserializeData,
126 0 : MarkerTracingType::START);
127 : }
128 :
129 0 : mData->Read(cx, &value, rv);
130 :
131 0 : if (isTimelineRecording) {
132 0 : end = MakeUnique<MessagePortTimelineMarker>(
133 : ProfileTimelineMessagePortOperationType::DeserializeData,
134 0 : MarkerTracingType::END);
135 0 : timelines->AddMarkerForAllObservedDocShells(start);
136 0 : timelines->AddMarkerForAllObservedDocShells(end);
137 : }
138 :
139 0 : if (NS_WARN_IF(rv.Failed())) {
140 0 : return rv.StealNSResult();
141 : }
142 :
143 : // Create the event
144 : nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
145 0 : do_QueryInterface(mPort->GetOwner());
146 : RefPtr<MessageEvent> event =
147 0 : new MessageEvent(eventTarget, nullptr, nullptr);
148 :
149 0 : Sequence<OwningNonNull<MessagePort>> ports;
150 0 : if (!mData->TakeTransferredPortsAsSequence(ports)) {
151 0 : return NS_ERROR_OUT_OF_MEMORY;
152 : }
153 :
154 0 : event->InitMessageEvent(nullptr, NS_LITERAL_STRING("message"),
155 : false /* non-bubbling */,
156 0 : false /* cancelable */, value, EmptyString(),
157 0 : EmptyString(), nullptr, ports);
158 0 : event->SetTrusted(true);
159 :
160 : bool dummy;
161 0 : mPort->DispatchEvent(static_cast<dom::Event*>(event.get()), &dummy);
162 :
163 0 : return NS_OK;
164 : }
165 :
166 : private:
167 0 : ~PostMessageRunnable()
168 0 : {}
169 :
170 : RefPtr<MessagePort> mPort;
171 : RefPtr<SharedMessagePortMessage> mData;
172 : };
173 :
174 : NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
175 :
176 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
177 : DOMEventTargetHelper)
178 0 : if (tmp->mPostMessageRunnable) {
179 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mPostMessageRunnable->mPort);
180 : }
181 :
182 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessages);
183 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagesForTheOtherPort);
184 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mUnshippedEntangledPort);
185 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
186 :
187 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
188 : DOMEventTargetHelper)
189 0 : if (tmp->mPostMessageRunnable) {
190 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPostMessageRunnable->mPort);
191 : }
192 :
193 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnshippedEntangledPort);
194 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
195 :
196 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessagePort)
197 0 : NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
198 0 : NS_INTERFACE_MAP_ENTRY(nsIObserver)
199 0 : NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
200 :
201 0 : NS_IMPL_ADDREF_INHERITED(MessagePort, DOMEventTargetHelper)
202 0 : NS_IMPL_RELEASE_INHERITED(MessagePort, DOMEventTargetHelper)
203 :
204 : namespace {
205 :
206 : class MessagePortWorkerHolder final : public workers::WorkerHolder
207 : {
208 : MessagePort* mPort;
209 :
210 : public:
211 0 : explicit MessagePortWorkerHolder(MessagePort* aPort)
212 0 : : mPort(aPort)
213 : {
214 0 : MOZ_ASSERT(aPort);
215 0 : MOZ_COUNT_CTOR(MessagePortWorkerHolder);
216 0 : }
217 :
218 0 : virtual bool Notify(workers::Status aStatus) override
219 : {
220 0 : if (aStatus > Running) {
221 : // We cannot process messages anymore because we cannot dispatch new
222 : // runnables. Let's force a Close().
223 0 : mPort->CloseForced();
224 : }
225 :
226 0 : return true;
227 : }
228 :
229 : private:
230 0 : ~MessagePortWorkerHolder()
231 0 : {
232 0 : MOZ_COUNT_DTOR(MessagePortWorkerHolder);
233 0 : }
234 : };
235 :
236 : class ForceCloseHelper final : public nsIIPCBackgroundChildCreateCallback
237 : {
238 : public:
239 : NS_DECL_ISUPPORTS
240 :
241 0 : static void ForceClose(const MessagePortIdentifier& aIdentifier)
242 : {
243 : PBackgroundChild* actor =
244 0 : mozilla::ipc::BackgroundChild::GetForCurrentThread();
245 0 : if (actor) {
246 0 : Unused << actor->SendMessagePortForceClose(aIdentifier.uuid(),
247 : aIdentifier.destinationUuid(),
248 : aIdentifier.sequenceId());
249 0 : return;
250 : }
251 :
252 0 : RefPtr<ForceCloseHelper> helper = new ForceCloseHelper(aIdentifier);
253 0 : if (NS_WARN_IF(!mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(helper))) {
254 0 : MOZ_CRASH();
255 : }
256 : }
257 :
258 : private:
259 0 : explicit ForceCloseHelper(const MessagePortIdentifier& aIdentifier)
260 0 : : mIdentifier(aIdentifier)
261 0 : {}
262 :
263 0 : ~ForceCloseHelper() {}
264 :
265 0 : void ActorFailed() override
266 : {
267 0 : MOZ_CRASH("Failed to create a PBackgroundChild actor!");
268 : }
269 :
270 0 : void ActorCreated(mozilla::ipc::PBackgroundChild* aActor) override
271 : {
272 0 : ForceClose(mIdentifier);
273 0 : }
274 :
275 : const MessagePortIdentifier mIdentifier;
276 : };
277 :
278 0 : NS_IMPL_ISUPPORTS(ForceCloseHelper, nsIIPCBackgroundChildCreateCallback)
279 :
280 : } // namespace
281 :
282 0 : MessagePort::MessagePort(nsIGlobalObject* aGlobal)
283 : : DOMEventTargetHelper(aGlobal)
284 : , mInnerID(0)
285 : , mMessageQueueEnabled(false)
286 0 : , mIsKeptAlive(false)
287 : {
288 0 : MOZ_ASSERT(aGlobal);
289 :
290 0 : mIdentifier = new MessagePortIdentifier();
291 0 : mIdentifier->neutered() = true;
292 0 : mIdentifier->sequenceId() = 0;
293 0 : }
294 :
295 0 : MessagePort::~MessagePort()
296 : {
297 0 : CloseForced();
298 0 : MOZ_ASSERT(!mWorkerHolder);
299 0 : }
300 :
301 : /* static */ already_AddRefed<MessagePort>
302 0 : MessagePort::Create(nsIGlobalObject* aGlobal, const nsID& aUUID,
303 : const nsID& aDestinationUUID, ErrorResult& aRv)
304 : {
305 0 : MOZ_ASSERT(aGlobal);
306 :
307 0 : RefPtr<MessagePort> mp = new MessagePort(aGlobal);
308 0 : mp->Initialize(aUUID, aDestinationUUID, 1 /* 0 is an invalid sequence ID */,
309 0 : false /* Neutered */, eStateUnshippedEntangled, aRv);
310 0 : return mp.forget();
311 : }
312 :
313 : /* static */ already_AddRefed<MessagePort>
314 0 : MessagePort::Create(nsIGlobalObject* aGlobal,
315 : const MessagePortIdentifier& aIdentifier,
316 : ErrorResult& aRv)
317 : {
318 0 : MOZ_ASSERT(aGlobal);
319 :
320 0 : RefPtr<MessagePort> mp = new MessagePort(aGlobal);
321 0 : mp->Initialize(aIdentifier.uuid(), aIdentifier.destinationUuid(),
322 0 : aIdentifier.sequenceId(), aIdentifier.neutered(),
323 0 : eStateEntangling, aRv);
324 0 : return mp.forget();
325 : }
326 :
327 : void
328 0 : MessagePort::UnshippedEntangle(MessagePort* aEntangledPort)
329 : {
330 0 : MOZ_ASSERT(aEntangledPort);
331 0 : MOZ_ASSERT(!mUnshippedEntangledPort);
332 :
333 0 : mUnshippedEntangledPort = aEntangledPort;
334 0 : }
335 :
336 : void
337 0 : MessagePort::Initialize(const nsID& aUUID,
338 : const nsID& aDestinationUUID,
339 : uint32_t aSequenceID, bool mNeutered,
340 : State aState, ErrorResult& aRv)
341 : {
342 0 : MOZ_ASSERT(mIdentifier);
343 0 : mIdentifier->uuid() = aUUID;
344 0 : mIdentifier->destinationUuid() = aDestinationUUID;
345 0 : mIdentifier->sequenceId() = aSequenceID;
346 :
347 0 : mState = aState;
348 :
349 0 : if (mNeutered) {
350 : // If this port is neutered we don't want to keep it alive artificially nor
351 : // we want to add listeners or workerWorkerHolders.
352 0 : mState = eStateDisentangled;
353 0 : return;
354 : }
355 :
356 0 : if (mState == eStateEntangling) {
357 0 : ConnectToPBackground();
358 : } else {
359 0 : MOZ_ASSERT(mState == eStateUnshippedEntangled);
360 : }
361 :
362 : // The port has to keep itself alive until it's entangled.
363 0 : UpdateMustKeepAlive();
364 :
365 0 : if (!NS_IsMainThread()) {
366 0 : WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
367 0 : MOZ_ASSERT(workerPrivate);
368 0 : MOZ_ASSERT(!mWorkerHolder);
369 :
370 0 : nsAutoPtr<WorkerHolder> workerHolder(new MessagePortWorkerHolder(this));
371 0 : if (NS_WARN_IF(!workerHolder->HoldWorker(workerPrivate, Closing))) {
372 0 : aRv.Throw(NS_ERROR_FAILURE);
373 0 : return;
374 : }
375 :
376 0 : mWorkerHolder = Move(workerHolder);
377 0 : } else if (GetOwner()) {
378 0 : MOZ_ASSERT(NS_IsMainThread());
379 0 : MOZ_ASSERT(GetOwner()->IsInnerWindow());
380 0 : mInnerID = GetOwner()->WindowID();
381 :
382 0 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
383 0 : if (obs) {
384 0 : obs->AddObserver(this, "inner-window-destroyed", false);
385 : }
386 : }
387 : }
388 :
389 : JSObject*
390 0 : MessagePort::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
391 : {
392 0 : return MessagePortBinding::Wrap(aCx, this, aGivenProto);
393 : }
394 :
395 : void
396 0 : MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
397 : const Sequence<JSObject*>& aTransferable,
398 : ErrorResult& aRv)
399 : {
400 : // We *must* clone the data here, or the JS::Value could be modified
401 : // by script
402 :
403 : // Here we want to check if the transerable object list contains
404 : // this port.
405 0 : for (uint32_t i = 0; i < aTransferable.Length(); ++i) {
406 0 : JS::Rooted<JSObject*> object(aCx, aTransferable[i]);
407 0 : if (!object) {
408 0 : continue;
409 : }
410 :
411 0 : MessagePort* port = nullptr;
412 0 : nsresult rv = UNWRAP_OBJECT(MessagePort, &object, port);
413 0 : if (NS_SUCCEEDED(rv) && port == this) {
414 0 : aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
415 0 : return;
416 : }
417 : }
418 :
419 0 : JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
420 :
421 0 : aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
422 0 : &transferable);
423 0 : if (NS_WARN_IF(aRv.Failed())) {
424 0 : return;
425 : }
426 :
427 0 : RefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
428 :
429 0 : UniquePtr<AbstractTimelineMarker> start;
430 0 : UniquePtr<AbstractTimelineMarker> end;
431 0 : RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
432 0 : bool isTimelineRecording = timelines && !timelines->IsEmpty();
433 :
434 0 : if (isTimelineRecording) {
435 0 : start = MakeUnique<MessagePortTimelineMarker>(
436 : ProfileTimelineMessagePortOperationType::SerializeData,
437 0 : MarkerTracingType::START);
438 : }
439 :
440 0 : data->Write(aCx, aMessage, transferable, aRv);
441 :
442 0 : if (isTimelineRecording) {
443 0 : end = MakeUnique<MessagePortTimelineMarker>(
444 : ProfileTimelineMessagePortOperationType::SerializeData,
445 0 : MarkerTracingType::END);
446 0 : timelines->AddMarkerForAllObservedDocShells(start);
447 0 : timelines->AddMarkerForAllObservedDocShells(end);
448 : }
449 :
450 0 : if (NS_WARN_IF(aRv.Failed())) {
451 0 : return;
452 : }
453 :
454 : // This message has to be ignored.
455 0 : if (mState > eStateEntangled) {
456 0 : return;
457 : }
458 :
459 : // If we are unshipped we are connected to the other port on the same thread.
460 0 : if (mState == eStateUnshippedEntangled) {
461 0 : MOZ_ASSERT(mUnshippedEntangledPort);
462 0 : mUnshippedEntangledPort->mMessages.AppendElement(data);
463 0 : mUnshippedEntangledPort->Dispatch();
464 0 : return;
465 : }
466 :
467 : // Not entangled yet, but already closed/disentangled.
468 0 : if (mState == eStateEntanglingForDisentangle ||
469 0 : mState == eStateEntanglingForClose) {
470 0 : return;
471 : }
472 :
473 0 : RemoveDocFromBFCache();
474 :
475 : // Not entangled yet.
476 0 : if (mState == eStateEntangling) {
477 0 : mMessagesForTheOtherPort.AppendElement(data);
478 0 : return;
479 : }
480 :
481 0 : MOZ_ASSERT(mActor);
482 0 : MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
483 :
484 0 : AutoTArray<RefPtr<SharedMessagePortMessage>, 1> array;
485 0 : array.AppendElement(data);
486 :
487 0 : AutoTArray<ClonedMessageData, 1> messages;
488 : // note: `messages` will borrow the underlying buffer, but this is okay
489 : // because reverse destruction order means `messages` will be destroyed prior
490 : // to `array`/`data`.
491 0 : SharedMessagePortMessage::FromSharedToMessagesChild(mActor, array, messages);
492 0 : mActor->SendPostMessages(messages);
493 : }
494 :
495 : void
496 0 : MessagePort::Start()
497 : {
498 0 : if (mMessageQueueEnabled) {
499 0 : return;
500 : }
501 :
502 0 : mMessageQueueEnabled = true;
503 0 : Dispatch();
504 : }
505 :
506 : void
507 0 : MessagePort::Dispatch()
508 : {
509 0 : if (!mMessageQueueEnabled || mMessages.IsEmpty() || mPostMessageRunnable) {
510 0 : return;
511 : }
512 :
513 0 : switch (mState) {
514 : case eStateUnshippedEntangled:
515 : // Everything is fine here. We have messages because the other
516 : // port populates our queue directly.
517 0 : break;
518 :
519 : case eStateEntangling:
520 : // Everything is fine here as well. We have messages because the other
521 : // port populated our queue directly when we were in the
522 : // eStateUnshippedEntangled state.
523 0 : break;
524 :
525 : case eStateEntanglingForDisentangle:
526 : // Here we don't want to ship messages because these messages must be
527 : // delivered by the cloned version of this one. They will be sent in the
528 : // SendDisentangle().
529 0 : return;
530 :
531 : case eStateEntanglingForClose:
532 : // We still want to deliver messages if we are closing. These messages
533 : // are here from the previous eStateUnshippedEntangled state.
534 0 : break;
535 :
536 : case eStateEntangled:
537 : // This port is up and running.
538 0 : break;
539 :
540 : case eStateDisentangling:
541 : // If we are in the process to disentangle the port, we cannot dispatch
542 : // messages. They will be sent to the cloned version of this port via
543 : // SendDisentangle();
544 0 : return;
545 :
546 : case eStateDisentangled:
547 0 : MOZ_CRASH("This cannot happen.");
548 : // It cannot happen because Disentangle should take off all the pending
549 : // messages.
550 : break;
551 :
552 : case eStateDisentangledForClose:
553 : // If we are here is because the port has been closed. We can still
554 : // process the pending messages.
555 0 : break;
556 : }
557 :
558 0 : RefPtr<SharedMessagePortMessage> data = mMessages.ElementAt(0);
559 0 : mMessages.RemoveElementAt(0);
560 :
561 0 : mPostMessageRunnable = new PostMessageRunnable(this, data);
562 :
563 0 : nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
564 0 : if (NS_IsMainThread() && global) {
565 0 : MOZ_ALWAYS_SUCCEEDS(global->Dispatch("MessagePortMessage", TaskCategory::Other, do_AddRef(mPostMessageRunnable)));
566 0 : return;
567 : }
568 :
569 0 : MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mPostMessageRunnable));
570 : }
571 :
572 : void
573 0 : MessagePort::Close()
574 : {
575 0 : CloseInternal(true /* aSoftly */);
576 0 : }
577 :
578 : void
579 0 : MessagePort::CloseForced()
580 : {
581 0 : CloseInternal(false /* aSoftly */);
582 0 : }
583 :
584 : void
585 0 : MessagePort::CloseInternal(bool aSoftly)
586 : {
587 : // If we have some messages to send but we don't want a 'soft' close, we have
588 : // to flush them now.
589 0 : if (!aSoftly) {
590 0 : mMessages.Clear();
591 : }
592 :
593 0 : if (mState == eStateUnshippedEntangled) {
594 0 : MOZ_ASSERT(mUnshippedEntangledPort);
595 :
596 : // This avoids loops.
597 0 : RefPtr<MessagePort> port = Move(mUnshippedEntangledPort);
598 0 : MOZ_ASSERT(mUnshippedEntangledPort == nullptr);
599 :
600 0 : mState = eStateDisentangledForClose;
601 0 : port->CloseInternal(aSoftly);
602 :
603 0 : UpdateMustKeepAlive();
604 0 : return;
605 : }
606 :
607 : // Not entangled yet, we have to wait.
608 0 : if (mState == eStateEntangling) {
609 0 : mState = eStateEntanglingForClose;
610 0 : return;
611 : }
612 :
613 : // Not entangled but already cloned or closed
614 0 : if (mState == eStateEntanglingForDisentangle ||
615 0 : mState == eStateEntanglingForClose) {
616 0 : return;
617 : }
618 :
619 : // Maybe we were already closing the port but softly. In this case we call
620 : // UpdateMustKeepAlive() to consider the empty pending message queue.
621 0 : if (mState == eStateDisentangledForClose && !aSoftly) {
622 0 : UpdateMustKeepAlive();
623 0 : return;
624 : }
625 :
626 0 : if (mState > eStateEntangled) {
627 0 : return;
628 : }
629 :
630 : // We don't care about stopping the sending of messages because from now all
631 : // the incoming messages will be ignored.
632 0 : mState = eStateDisentangledForClose;
633 :
634 0 : MOZ_ASSERT(mActor);
635 :
636 0 : mActor->SendClose();
637 0 : mActor->SetPort(nullptr);
638 0 : mActor = nullptr;
639 :
640 0 : UpdateMustKeepAlive();
641 : }
642 :
643 : EventHandlerNonNull*
644 0 : MessagePort::GetOnmessage()
645 : {
646 0 : if (NS_IsMainThread()) {
647 0 : return GetEventHandler(nsGkAtoms::onmessage, EmptyString());
648 : }
649 0 : return GetEventHandler(nullptr, NS_LITERAL_STRING("message"));
650 : }
651 :
652 : void
653 0 : MessagePort::SetOnmessage(EventHandlerNonNull* aCallback)
654 : {
655 0 : if (NS_IsMainThread()) {
656 0 : SetEventHandler(nsGkAtoms::onmessage, EmptyString(), aCallback);
657 : } else {
658 0 : SetEventHandler(nullptr, NS_LITERAL_STRING("message"), aCallback);
659 : }
660 :
661 : // When using onmessage, the call to start() is implied.
662 0 : Start();
663 0 : }
664 :
665 : // This method is called when the PMessagePortChild actor is entangled to
666 : // another actor. It receives a list of messages to be dispatch. It can be that
667 : // we were waiting for this entangling step in order to disentangle the port or
668 : // to close it.
669 : void
670 0 : MessagePort::Entangled(nsTArray<ClonedMessageData>& aMessages)
671 : {
672 0 : MOZ_ASSERT(mState == eStateEntangling ||
673 : mState == eStateEntanglingForDisentangle ||
674 : mState == eStateEntanglingForClose);
675 :
676 0 : State oldState = mState;
677 0 : mState = eStateEntangled;
678 :
679 : // If we have pending messages, these have to be sent.
680 0 : if (!mMessagesForTheOtherPort.IsEmpty()) {
681 : {
682 0 : nsTArray<ClonedMessageData> messages;
683 0 : SharedMessagePortMessage::FromSharedToMessagesChild(mActor,
684 : mMessagesForTheOtherPort,
685 0 : messages);
686 0 : mActor->SendPostMessages(messages);
687 : }
688 : // Because `messages` borrow the underlying JSStructuredCloneData buffers,
689 : // only clear after `messages` have gone out of scope.
690 0 : mMessagesForTheOtherPort.Clear();
691 : }
692 :
693 : // We must convert the messages into SharedMessagePortMessages to avoid leaks.
694 0 : FallibleTArray<RefPtr<SharedMessagePortMessage>> data;
695 0 : if (NS_WARN_IF(!SharedMessagePortMessage::FromMessagesToSharedChild(aMessages,
696 : data))) {
697 : // OOM, we cannot continue.
698 0 : return;
699 : }
700 :
701 : // If the next step is to close the port, we do it ignoring the received
702 : // messages.
703 0 : if (oldState == eStateEntanglingForClose) {
704 0 : CloseForced();
705 0 : return;
706 : }
707 :
708 0 : mMessages.AppendElements(data);
709 :
710 : // We were waiting for the entangling callback in order to disentangle this
711 : // port immediately after.
712 0 : if (oldState == eStateEntanglingForDisentangle) {
713 0 : StartDisentangling();
714 0 : return;
715 : }
716 :
717 0 : Dispatch();
718 : }
719 :
720 : void
721 0 : MessagePort::StartDisentangling()
722 : {
723 0 : MOZ_ASSERT(mActor);
724 0 : MOZ_ASSERT(mState == eStateEntangled);
725 :
726 0 : mState = eStateDisentangling;
727 :
728 : // Sending this message we communicate to the parent actor that we don't want
729 : // to receive any new messages. It is possible that a message has been
730 : // already sent but not received yet. So we have to collect all of them and
731 : // we send them in the SendDispatch() request.
732 0 : mActor->SendStopSendingData();
733 0 : }
734 :
735 : void
736 0 : MessagePort::MessagesReceived(nsTArray<ClonedMessageData>& aMessages)
737 : {
738 0 : MOZ_ASSERT(mState == eStateEntangled ||
739 : mState == eStateDisentangling ||
740 : // This last step can happen only if Close() has been called
741 : // manually. At this point SendClose() is sent but we can still
742 : // receive something until the Closing request is processed.
743 : mState == eStateDisentangledForClose);
744 0 : MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
745 :
746 0 : RemoveDocFromBFCache();
747 :
748 0 : FallibleTArray<RefPtr<SharedMessagePortMessage>> data;
749 0 : if (NS_WARN_IF(!SharedMessagePortMessage::FromMessagesToSharedChild(aMessages,
750 : data))) {
751 : // OOM, We cannot continue.
752 0 : return;
753 : }
754 :
755 0 : mMessages.AppendElements(data);
756 :
757 0 : if (mState == eStateEntangled) {
758 0 : Dispatch();
759 : }
760 : }
761 :
762 : void
763 0 : MessagePort::StopSendingDataConfirmed()
764 : {
765 0 : MOZ_ASSERT(mState == eStateDisentangling);
766 0 : MOZ_ASSERT(mActor);
767 :
768 0 : Disentangle();
769 0 : }
770 :
771 : void
772 0 : MessagePort::Disentangle()
773 : {
774 0 : MOZ_ASSERT(mState == eStateDisentangling);
775 0 : MOZ_ASSERT(mActor);
776 :
777 0 : mState = eStateDisentangled;
778 :
779 : {
780 0 : nsTArray<ClonedMessageData> messages;
781 0 : SharedMessagePortMessage::FromSharedToMessagesChild(mActor, mMessages,
782 0 : messages);
783 0 : mActor->SendDisentangle(messages);
784 : }
785 : // Only clear mMessages after the ClonedMessageData instances have gone out of
786 : // scope because they borrow mMessages' underlying JSStructuredCloneDatas.
787 0 : mMessages.Clear();
788 :
789 0 : mActor->SetPort(nullptr);
790 0 : mActor = nullptr;
791 :
792 0 : UpdateMustKeepAlive();
793 0 : }
794 :
795 : void
796 0 : MessagePort::CloneAndDisentangle(MessagePortIdentifier& aIdentifier)
797 : {
798 0 : MOZ_ASSERT(mIdentifier);
799 :
800 : // We can clone a port that has already been transfered. In this case, on the
801 : // otherside will have a neutered port. Here we set neutered to true so that
802 : // we are safe in case a early return.
803 0 : aIdentifier.neutered() = true;
804 :
805 0 : if (mState > eStateEntangled) {
806 0 : return;
807 : }
808 :
809 : // We already have a 'next step'. We have to consider this port as already
810 : // cloned/closed/disentangled.
811 0 : if (mState == eStateEntanglingForDisentangle ||
812 0 : mState == eStateEntanglingForClose) {
813 0 : return;
814 : }
815 :
816 0 : aIdentifier.uuid() = mIdentifier->uuid();
817 0 : aIdentifier.destinationUuid() = mIdentifier->destinationUuid();
818 0 : aIdentifier.sequenceId() = mIdentifier->sequenceId() + 1;
819 0 : aIdentifier.neutered() = false;
820 :
821 : // We have to entangle first.
822 0 : if (mState == eStateUnshippedEntangled) {
823 0 : MOZ_ASSERT(mUnshippedEntangledPort);
824 0 : MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
825 :
826 : // Disconnect the entangled port and connect it to PBackground.
827 0 : mUnshippedEntangledPort->ConnectToPBackground();
828 0 : mUnshippedEntangledPort = nullptr;
829 :
830 : // In this case, we don't need to be connected to the PBackground service.
831 0 : if (mMessages.IsEmpty()) {
832 0 : aIdentifier.sequenceId() = mIdentifier->sequenceId();
833 :
834 0 : mState = eStateDisentangled;
835 0 : UpdateMustKeepAlive();
836 0 : return;
837 : }
838 :
839 : // Register this component to PBackground.
840 0 : ConnectToPBackground();
841 :
842 0 : mState = eStateEntanglingForDisentangle;
843 0 : return;
844 : }
845 :
846 : // Not entangled yet, we have to wait.
847 0 : if (mState == eStateEntangling) {
848 0 : mState = eStateEntanglingForDisentangle;
849 0 : return;
850 : }
851 :
852 0 : MOZ_ASSERT(mState == eStateEntangled);
853 0 : StartDisentangling();
854 : }
855 :
856 : void
857 0 : MessagePort::Closed()
858 : {
859 0 : if (mState >= eStateDisentangled) {
860 0 : return;
861 : }
862 :
863 0 : mState = eStateDisentangledForClose;
864 :
865 0 : if (mActor) {
866 0 : mActor->SetPort(nullptr);
867 0 : mActor = nullptr;
868 : }
869 :
870 0 : UpdateMustKeepAlive();
871 : }
872 :
873 : void
874 0 : MessagePort::ConnectToPBackground()
875 : {
876 0 : mState = eStateEntangling;
877 :
878 : PBackgroundChild* actor =
879 0 : mozilla::ipc::BackgroundChild::GetForCurrentThread();
880 0 : if (actor) {
881 0 : ActorCreated(actor);
882 : } else {
883 0 : if (NS_WARN_IF(
884 : !mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(this))) {
885 0 : MOZ_CRASH();
886 : }
887 : }
888 0 : }
889 :
890 : void
891 0 : MessagePort::ActorFailed()
892 : {
893 0 : MOZ_CRASH("Failed to create a PBackgroundChild actor!");
894 : }
895 :
896 : void
897 0 : MessagePort::ActorCreated(mozilla::ipc::PBackgroundChild* aActor)
898 : {
899 0 : MOZ_ASSERT(aActor);
900 0 : MOZ_ASSERT(!mActor);
901 0 : MOZ_ASSERT(mIdentifier);
902 0 : MOZ_ASSERT(mState == eStateEntangling ||
903 : mState == eStateEntanglingForDisentangle ||
904 : mState == eStateEntanglingForClose);
905 :
906 : PMessagePortChild* actor =
907 0 : aActor->SendPMessagePortConstructor(mIdentifier->uuid(),
908 0 : mIdentifier->destinationUuid(),
909 0 : mIdentifier->sequenceId());
910 :
911 0 : mActor = static_cast<MessagePortChild*>(actor);
912 0 : MOZ_ASSERT(mActor);
913 :
914 0 : mActor->SetPort(this);
915 0 : }
916 :
917 : void
918 0 : MessagePort::UpdateMustKeepAlive()
919 : {
920 0 : if (mState >= eStateDisentangled &&
921 0 : mMessages.IsEmpty() &&
922 0 : mIsKeptAlive) {
923 0 : mIsKeptAlive = false;
924 :
925 : // The DTOR of this WorkerHolder will release the worker for us.
926 0 : mWorkerHolder = nullptr;
927 :
928 0 : if (NS_IsMainThread()) {
929 : nsCOMPtr<nsIObserverService> obs =
930 0 : do_GetService("@mozilla.org/observer-service;1");
931 0 : if (obs) {
932 0 : obs->RemoveObserver(this, "inner-window-destroyed");
933 : }
934 : }
935 :
936 0 : Release();
937 0 : return;
938 : }
939 :
940 0 : if (mState < eStateDisentangled && !mIsKeptAlive) {
941 0 : mIsKeptAlive = true;
942 0 : AddRef();
943 : }
944 : }
945 :
946 : NS_IMETHODIMP
947 0 : MessagePort::Observe(nsISupports* aSubject, const char* aTopic,
948 : const char16_t* aData)
949 : {
950 0 : MOZ_ASSERT(NS_IsMainThread());
951 :
952 0 : if (strcmp(aTopic, "inner-window-destroyed")) {
953 0 : return NS_OK;
954 : }
955 :
956 : // If the window id destroyed we have to release the reference that we are
957 : // keeping.
958 0 : if (!mIsKeptAlive) {
959 0 : return NS_OK;
960 : }
961 :
962 0 : nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
963 0 : NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
964 :
965 : uint64_t innerID;
966 0 : nsresult rv = wrapper->GetData(&innerID);
967 0 : NS_ENSURE_SUCCESS(rv, rv);
968 :
969 0 : if (innerID == mInnerID) {
970 0 : CloseForced();
971 : }
972 :
973 0 : return NS_OK;
974 : }
975 :
976 : void
977 0 : MessagePort::RemoveDocFromBFCache()
978 : {
979 0 : if (!NS_IsMainThread()) {
980 0 : return;
981 : }
982 :
983 0 : nsPIDOMWindowInner* window = GetOwner();
984 0 : if (!window) {
985 0 : return;
986 : }
987 :
988 0 : nsIDocument* doc = window->GetExtantDoc();
989 0 : if (!doc) {
990 0 : return;
991 : }
992 :
993 0 : nsCOMPtr<nsIBFCacheEntry> bfCacheEntry = doc->GetBFCacheEntry();
994 0 : if (!bfCacheEntry) {
995 0 : return;
996 : }
997 :
998 0 : bfCacheEntry->RemoveFromBFCacheSync();
999 : }
1000 :
1001 : /* static */ void
1002 0 : MessagePort::ForceClose(const MessagePortIdentifier& aIdentifier)
1003 : {
1004 0 : ForceCloseHelper::ForceClose(aIdentifier);
1005 0 : }
1006 :
1007 : } // namespace dom
1008 : } // namespace mozilla
|