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