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 "RefMessageBodyService.h"
8
9 #include <cstdint>
10 #include <cstdlib>
11 #include "mozilla/ErrorResult.h"
12 #include "mozilla/RefPtr.h"
13 #include "mozilla/dom/ipc/StructuredCloneData.h"
14 #include "nsBaseHashtable.h"
15 #include "nsContentUtils.h"
16 #include "nsDebug.h"
17
18 namespace mozilla::dom {
19
20 // Guards sService and its members.
21 StaticMutex sRefMessageBodyServiceMutex;
22
23 // Raw pointer because the service is kept alive by other objects.
24 // See the CTOR and the DTOR of this object.
25 RefMessageBodyService* sService;
26
27 // static
GetOrCreate()28 already_AddRefed<RefMessageBodyService> RefMessageBodyService::GetOrCreate() {
29 StaticMutexAutoLock lock(sRefMessageBodyServiceMutex);
30
31 RefPtr<RefMessageBodyService> service = GetOrCreateInternal(lock);
32 return service.forget();
33 }
34
35 // static
GetOrCreateInternal(const StaticMutexAutoLock & aProofOfLock)36 RefMessageBodyService* RefMessageBodyService::GetOrCreateInternal(
37 const StaticMutexAutoLock& aProofOfLock) {
38 if (!sService) {
39 sService = new RefMessageBodyService(aProofOfLock);
40 }
41 return sService;
42 }
43
RefMessageBodyService(const StaticMutexAutoLock & aProofOfLock)44 RefMessageBodyService::RefMessageBodyService(
45 const StaticMutexAutoLock& aProofOfLock) {
46 MOZ_DIAGNOSTIC_ASSERT(sService == nullptr);
47 }
48
~RefMessageBodyService()49 RefMessageBodyService::~RefMessageBodyService() {
50 StaticMutexAutoLock lock(sRefMessageBodyServiceMutex);
51 MOZ_DIAGNOSTIC_ASSERT(sService == this);
52 sService = nullptr;
53 }
54
Register(already_AddRefed<RefMessageBody> aBody,ErrorResult & aRv)55 const nsID RefMessageBodyService::Register(
56 already_AddRefed<RefMessageBody> aBody, ErrorResult& aRv) {
57 RefPtr<RefMessageBody> body = aBody;
58 MOZ_ASSERT(body);
59
60 nsID uuid = {};
61 aRv = nsContentUtils::GenerateUUIDInPlace(uuid);
62 if (NS_WARN_IF(aRv.Failed())) {
63 return nsID();
64 }
65
66 StaticMutexAutoLock lock(sRefMessageBodyServiceMutex);
67 GetOrCreateInternal(lock)->mMessages.InsertOrUpdate(uuid, std::move(body));
68 return uuid;
69 }
70
Steal(const nsID & aID)71 already_AddRefed<RefMessageBody> RefMessageBodyService::Steal(const nsID& aID) {
72 StaticMutexAutoLock lock(sRefMessageBodyServiceMutex);
73 if (!sService) {
74 return nullptr;
75 }
76
77 RefPtr<RefMessageBody> body;
78 sService->mMessages.Remove(aID, getter_AddRefs(body));
79
80 return body.forget();
81 }
82
GetAndCount(const nsID & aID)83 already_AddRefed<RefMessageBody> RefMessageBodyService::GetAndCount(
84 const nsID& aID) {
85 StaticMutexAutoLock lock(sRefMessageBodyServiceMutex);
86 if (!sService) {
87 return nullptr;
88 }
89
90 RefPtr<RefMessageBody> body = sService->mMessages.Get(aID);
91 if (!body) {
92 return nullptr;
93 }
94
95 ++body->mCount;
96
97 MOZ_ASSERT_IF(body->mMaxCount.isSome(),
98 body->mCount <= body->mMaxCount.value());
99 if (body->mMaxCount.isSome() && body->mCount >= body->mMaxCount.value()) {
100 sService->mMessages.Remove(aID);
101 }
102
103 return body.forget();
104 }
105
SetMaxCount(const nsID & aID,uint32_t aMaxCount)106 void RefMessageBodyService::SetMaxCount(const nsID& aID, uint32_t aMaxCount) {
107 StaticMutexAutoLock lock(sRefMessageBodyServiceMutex);
108 if (!sService) {
109 return;
110 }
111
112 RefPtr<RefMessageBody> body = sService->mMessages.Get(aID);
113 if (!body) {
114 return;
115 }
116
117 MOZ_ASSERT(body->mMaxCount.isNothing());
118 body->mMaxCount.emplace(aMaxCount);
119
120 MOZ_ASSERT(body->mCount <= body->mMaxCount.value());
121 if (body->mCount >= body->mMaxCount.value()) {
122 sService->mMessages.Remove(aID);
123 }
124 }
125
ForgetPort(const nsID & aPortID)126 void RefMessageBodyService::ForgetPort(const nsID& aPortID) {
127 StaticMutexAutoLock lock(sRefMessageBodyServiceMutex);
128 if (!sService) {
129 return;
130 }
131
132 for (auto iter = sService->mMessages.Iter(); !iter.Done(); iter.Next()) {
133 if (iter.UserData()->PortID() == aPortID) {
134 iter.Remove();
135 }
136 }
137 }
138
RefMessageBody(const nsID & aPortID,UniquePtr<ipc::StructuredCloneData> && aCloneData)139 RefMessageBody::RefMessageBody(const nsID& aPortID,
140 UniquePtr<ipc::StructuredCloneData>&& aCloneData)
141 : mPortID(aPortID),
142 mMutex("RefMessageBody::mMutex"),
143 mCloneData(std::move(aCloneData)),
144 mMaxCount(Nothing()),
145 mCount(0) {}
146
147 RefMessageBody::~RefMessageBody() = default;
148
Read(JSContext * aCx,JS::MutableHandle<JS::Value> aValue,const JS::CloneDataPolicy & aCloneDataPolicy,ErrorResult & aRv)149 void RefMessageBody::Read(JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
150 const JS::CloneDataPolicy& aCloneDataPolicy,
151 ErrorResult& aRv) {
152 MutexAutoLock lock(mMutex);
153 mCloneData->Read(aCx, aValue, aCloneDataPolicy, aRv);
154 }
155
TakeTransferredPortsAsSequence(Sequence<OwningNonNull<mozilla::dom::MessagePort>> & aPorts)156 bool RefMessageBody::TakeTransferredPortsAsSequence(
157 Sequence<OwningNonNull<mozilla::dom::MessagePort>>& aPorts) {
158 MOZ_ASSERT(mMaxCount.isNothing());
159 return mCloneData->TakeTransferredPortsAsSequence(aPorts);
160 }
161
162 } // namespace mozilla::dom
163