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