summaryrefslogtreecommitdiffstats
path: root/dom/ipc/RefMessageBodyService.h
blob: 6cca2a836efa0f7173e162020ad1737ad1757978 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_dom_RefMessageBodyService_h
#define mozilla_dom_RefMessageBodyService_h

#include <cstdint>
#include "js/TypeDecls.h"
#include "mozilla/Maybe.h"
#include "mozilla/Mutex.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/UniquePtr.h"
#include "nsHashKeys.h"
#include "nsID.h"
#include "nsISupports.h"
#include "nsRefPtrHashtable.h"

namespace JS {
class CloneDataPolicy;
}  // namespace JS

namespace mozilla {

class ErrorResult;
template <class T>
class OwningNonNull;

namespace dom {

class MessagePort;
template <typename T>
class Sequence;

namespace ipc {
class StructuredCloneData;
}

/**
 * At the time a BroadcastChannel or MessagePort sends messages, we don't know
 * which process is going to receive it. Because of this, we need to check if
 * the message is able to cross the process boundary.
 * If the message contains objects such as SharedArrayBuffers, WASM modules or
 * ImageBitmaps, it can be delivered on the current process only.
 * Instead of sending the whole message via IPC, we send a unique ID, while the
 * message is kept alive by RefMessageBodyService, on the current process using
 * a ref-counted RefMessageBody object.
 * When the receiver obtains the message ID, it checks if the local
 * RefMessageBodyService knows that ID. If yes, the sender and the receiver are
 * on the same process and the delivery can be completed; if not, a
 * messageerror event has to be dispatched instead.
 *
 * For MessagePort communication is 1-to-1 and because of this, the
 * receiver takes ownership of the message (RefMessageBodyService::Steal()).
 * If the receiver port is on a different process, RefMessageBodyService will
 * return a nullptr and a messageerror event will be dispatched.

 * For BroadcastChannel, the life-time of a message is a bit different than for
 * MessagePort. It has a 1-to-many communication and we could have multiple
 * receivers. Each one needs to deliver the message without taking full
 * ownership of it.
 * In order to support this feature, BroadcastChannel needs to call
 * RefMessageBodyService::SetMaxCount() to inform how many ports are allowed to
 * retrieve the current message, on the current process. Receivers on other
 * processes are not kept in consideration because they will not be able to
 * retrieve the message from RefMessageBodyService. When the last allowed
 * port has called RefMessageBodyService::GetAndCount(), the message is
 * released.
 */
class RefMessageBody final {
  friend class RefMessageBodyService;

 public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefMessageBody)

  RefMessageBody(const nsID& aPortID,
                 UniquePtr<ipc::StructuredCloneData>&& aCloneData);

  const nsID& PortID() const { return mPortID; }

  void Read(JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
            const JS::CloneDataPolicy& aCloneDataPolicy, ErrorResult& aRv);

  // This method can be called only if the RefMessageBody is not supposed to be
  // ref-counted (see mMaxCount).
  bool TakeTransferredPortsAsSequence(
      Sequence<OwningNonNull<mozilla::dom::MessagePort>>& aPorts);

 private:
  ~RefMessageBody();

  const nsID mPortID;

  // In case the RefMessageBody is shared and refcounted (see mCount/mMaxCount),
  // we must enforce that the reading does not happen simultaneously on
  // different threads.
  Mutex mMutex MOZ_UNANNOTATED;

  UniquePtr<ipc::StructuredCloneData> mCloneData;

  // When mCount reaches mMaxCount, this object is released by the service.
  Maybe<uint32_t> mMaxCount;
  uint32_t mCount;
};

class RefMessageBodyService final {
 public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefMessageBodyService)

  static already_AddRefed<RefMessageBodyService> GetOrCreate();

  void ForgetPort(const nsID& aPortID);

  const nsID Register(already_AddRefed<RefMessageBody> aBody, ErrorResult& aRv);

  already_AddRefed<RefMessageBody> Steal(const nsID& aID);

  already_AddRefed<RefMessageBody> GetAndCount(const nsID& aID);

  void SetMaxCount(const nsID& aID, uint32_t aMaxCount);

 private:
  explicit RefMessageBodyService(const StaticMutexAutoLock& aProofOfLock);
  ~RefMessageBodyService();

  static RefMessageBodyService* GetOrCreateInternal(
      const StaticMutexAutoLock& aProofOfLock);

  nsRefPtrHashtable<nsIDHashKey, RefMessageBody> mMessages;
};

}  // namespace dom
}  // namespace mozilla

#endif  // mozilla_dom_RefMessageBodyService_h