Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "GMPStorageChild.h"
7 : #include "GMPChild.h"
8 : #include "gmp-storage.h"
9 : #include "base/task.h"
10 :
11 : #define ON_GMP_THREAD() (mPlugin->GMPMessageLoop() == MessageLoop::current())
12 :
13 : #define CALL_ON_GMP_THREAD(_func, ...) \
14 : do { \
15 : if (ON_GMP_THREAD()) { \
16 : _func(__VA_ARGS__); \
17 : } else { \
18 : mPlugin->GMPMessageLoop()->PostTask( \
19 : dont_add_new_uses_of_this::NewRunnableMethod(this, &GMPStorageChild::_func, ##__VA_ARGS__) \
20 : ); \
21 : } \
22 : } while(false)
23 :
24 : static nsTArray<uint8_t>
25 0 : ToArray(const uint8_t* aData, uint32_t aDataSize)
26 : {
27 0 : nsTArray<uint8_t> data;
28 0 : data.AppendElements(aData, aDataSize);
29 0 : return mozilla::Move(data);
30 : }
31 :
32 : namespace mozilla {
33 : namespace gmp {
34 :
35 0 : GMPRecordImpl::GMPRecordImpl(GMPStorageChild* aOwner,
36 : const nsCString& aName,
37 0 : GMPRecordClient* aClient)
38 : : mName(aName)
39 : , mClient(aClient)
40 0 : , mOwner(aOwner)
41 : {
42 0 : }
43 :
44 : GMPErr
45 0 : GMPRecordImpl::Open()
46 : {
47 0 : return mOwner->Open(this);
48 : }
49 :
50 : void
51 0 : GMPRecordImpl::OpenComplete(GMPErr aStatus)
52 : {
53 0 : mClient->OpenComplete(aStatus);
54 0 : }
55 :
56 : GMPErr
57 0 : GMPRecordImpl::Read()
58 : {
59 0 : return mOwner->Read(this);
60 : }
61 :
62 : void
63 0 : GMPRecordImpl::ReadComplete(GMPErr aStatus,
64 : const uint8_t* aBytes,
65 : uint32_t aLength)
66 : {
67 0 : mClient->ReadComplete(aStatus, aBytes, aLength);
68 0 : }
69 :
70 : GMPErr
71 0 : GMPRecordImpl::Write(const uint8_t* aData, uint32_t aDataSize)
72 : {
73 0 : return mOwner->Write(this, aData, aDataSize);
74 : }
75 :
76 : void
77 0 : GMPRecordImpl::WriteComplete(GMPErr aStatus)
78 : {
79 0 : mClient->WriteComplete(aStatus);
80 0 : }
81 :
82 : GMPErr
83 0 : GMPRecordImpl::Close()
84 : {
85 0 : RefPtr<GMPRecordImpl> kungfuDeathGrip(this);
86 : // Delete our self reference.
87 0 : Release();
88 0 : mOwner->Close(this->Name());
89 0 : return GMPNoErr;
90 : }
91 :
92 0 : GMPStorageChild::GMPStorageChild(GMPChild* aPlugin)
93 : : mMonitor("GMPStorageChild")
94 : , mPlugin(aPlugin)
95 0 : , mShutdown(false)
96 : {
97 0 : MOZ_ASSERT(ON_GMP_THREAD());
98 0 : }
99 :
100 : GMPErr
101 0 : GMPStorageChild::CreateRecord(const nsCString& aRecordName,
102 : GMPRecord** aOutRecord,
103 : GMPRecordClient* aClient)
104 : {
105 0 : MonitorAutoLock lock(mMonitor);
106 :
107 0 : if (mShutdown) {
108 0 : NS_WARNING("GMPStorage used after it's been shutdown!");
109 0 : return GMPClosedErr;
110 : }
111 :
112 0 : MOZ_ASSERT(aRecordName.Length() && aOutRecord);
113 :
114 0 : if (HasRecord(aRecordName)) {
115 0 : return GMPRecordInUse;
116 : }
117 :
118 0 : RefPtr<GMPRecordImpl> record(new GMPRecordImpl(this, aRecordName, aClient));
119 0 : mRecords.Put(aRecordName, record); // Addrefs
120 :
121 : // The GMPRecord holds a self reference until the GMP calls Close() on
122 : // it. This means the object is always valid (even if neutered) while
123 : // the GMP expects it to be.
124 0 : record.forget(aOutRecord);
125 :
126 0 : return GMPNoErr;
127 : }
128 :
129 : bool
130 0 : GMPStorageChild::HasRecord(const nsCString& aRecordName)
131 : {
132 0 : mMonitor.AssertCurrentThreadOwns();
133 0 : return mRecords.Contains(aRecordName);
134 : }
135 :
136 : already_AddRefed<GMPRecordImpl>
137 0 : GMPStorageChild::GetRecord(const nsCString& aRecordName)
138 : {
139 0 : MonitorAutoLock lock(mMonitor);
140 0 : RefPtr<GMPRecordImpl> record;
141 0 : mRecords.Get(aRecordName, getter_AddRefs(record));
142 0 : return record.forget();
143 : }
144 :
145 : GMPErr
146 0 : GMPStorageChild::Open(GMPRecordImpl* aRecord)
147 : {
148 0 : MonitorAutoLock lock(mMonitor);
149 :
150 0 : if (mShutdown) {
151 0 : NS_WARNING("GMPStorage used after it's been shutdown!");
152 0 : return GMPClosedErr;
153 : }
154 :
155 0 : if (!HasRecord(aRecord->Name())) {
156 : // Trying to re-open a record that has already been closed.
157 0 : return GMPClosedErr;
158 : }
159 :
160 0 : CALL_ON_GMP_THREAD(SendOpen, aRecord->Name());
161 :
162 0 : return GMPNoErr;
163 : }
164 :
165 : GMPErr
166 0 : GMPStorageChild::Read(GMPRecordImpl* aRecord)
167 : {
168 0 : MonitorAutoLock lock(mMonitor);
169 :
170 0 : if (mShutdown) {
171 0 : NS_WARNING("GMPStorage used after it's been shutdown!");
172 0 : return GMPClosedErr;
173 : }
174 :
175 0 : if (!HasRecord(aRecord->Name())) {
176 : // Record not opened.
177 0 : return GMPClosedErr;
178 : }
179 :
180 0 : CALL_ON_GMP_THREAD(SendRead, aRecord->Name());
181 :
182 0 : return GMPNoErr;
183 : }
184 :
185 : GMPErr
186 0 : GMPStorageChild::Write(GMPRecordImpl* aRecord,
187 : const uint8_t* aData,
188 : uint32_t aDataSize)
189 : {
190 0 : if (aDataSize > GMP_MAX_RECORD_SIZE) {
191 0 : return GMPQuotaExceededErr;
192 : }
193 :
194 0 : MonitorAutoLock lock(mMonitor);
195 :
196 0 : if (mShutdown) {
197 0 : NS_WARNING("GMPStorage used after it's been shutdown!");
198 0 : return GMPClosedErr;
199 : }
200 :
201 0 : if (!HasRecord(aRecord->Name())) {
202 : // Record not opened.
203 0 : return GMPClosedErr;
204 : }
205 :
206 0 : CALL_ON_GMP_THREAD(SendWrite, aRecord->Name(), ToArray(aData, aDataSize));
207 :
208 0 : return GMPNoErr;
209 : }
210 :
211 : GMPErr
212 0 : GMPStorageChild::Close(const nsCString& aRecordName)
213 : {
214 0 : MonitorAutoLock lock(mMonitor);
215 :
216 0 : if (!HasRecord(aRecordName)) {
217 : // Already closed.
218 0 : return GMPClosedErr;
219 : }
220 :
221 0 : mRecords.Remove(aRecordName);
222 :
223 0 : if (!mShutdown) {
224 0 : CALL_ON_GMP_THREAD(SendClose, aRecordName);
225 : }
226 :
227 0 : return GMPNoErr;
228 : }
229 :
230 : mozilla::ipc::IPCResult
231 0 : GMPStorageChild::RecvOpenComplete(const nsCString& aRecordName,
232 : const GMPErr& aStatus)
233 : {
234 : // We don't need a lock to read |mShutdown| since it is only changed in
235 : // the GMP thread.
236 0 : if (mShutdown) {
237 0 : return IPC_OK();
238 : }
239 0 : RefPtr<GMPRecordImpl> record = GetRecord(aRecordName);
240 0 : if (!record) {
241 : // Not fatal.
242 0 : return IPC_OK();
243 : }
244 0 : record->OpenComplete(aStatus);
245 0 : return IPC_OK();
246 : }
247 :
248 : mozilla::ipc::IPCResult
249 0 : GMPStorageChild::RecvReadComplete(const nsCString& aRecordName,
250 : const GMPErr& aStatus,
251 : InfallibleTArray<uint8_t>&& aBytes)
252 : {
253 0 : if (mShutdown) {
254 0 : return IPC_OK();
255 : }
256 0 : RefPtr<GMPRecordImpl> record = GetRecord(aRecordName);
257 0 : if (!record) {
258 : // Not fatal.
259 0 : return IPC_OK();
260 : }
261 0 : record->ReadComplete(aStatus, aBytes.Elements(), aBytes.Length());
262 0 : return IPC_OK();
263 : }
264 :
265 : mozilla::ipc::IPCResult
266 0 : GMPStorageChild::RecvWriteComplete(const nsCString& aRecordName,
267 : const GMPErr& aStatus)
268 : {
269 0 : if (mShutdown) {
270 0 : return IPC_OK();
271 : }
272 0 : RefPtr<GMPRecordImpl> record = GetRecord(aRecordName);
273 0 : if (!record) {
274 : // Not fatal.
275 0 : return IPC_OK();
276 : }
277 0 : record->WriteComplete(aStatus);
278 0 : return IPC_OK();
279 : }
280 :
281 : mozilla::ipc::IPCResult
282 0 : GMPStorageChild::RecvShutdown()
283 : {
284 : // Block any new storage requests, and thus any messages back to the
285 : // parent. We don't delete any objects here, as that may invalidate
286 : // GMPRecord pointers held by the GMP.
287 0 : MonitorAutoLock lock(mMonitor);
288 0 : mShutdown = true;
289 0 : return IPC_OK();
290 : }
291 :
292 : } // namespace gmp
293 : } // namespace mozilla
294 :
295 : // avoid redefined macro in unified build
296 : #undef ON_GMP_THREAD
297 : #undef CALL_ON_GMP_THREAD
|