1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=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 file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "SDBConnection.h"
8 
9 // Local includes
10 #include "ActorsChild.h"
11 #include "SDBRequest.h"
12 #include "SimpleDBCommon.h"
13 
14 // Global includes
15 #include <stdint.h>
16 #include <utility>
17 #include "MainThreadUtils.h"
18 #include "js/ArrayBuffer.h"
19 #include "js/RootingAPI.h"
20 #include "js/TypeDecls.h"
21 #include "js/experimental/TypedData.h"
22 #include "mozilla/Assertions.h"
23 #include "mozilla/MacroForEach.h"
24 #include "mozilla/Maybe.h"
25 #include "mozilla/Preferences.h"
26 #include "mozilla/RefPtr.h"
27 #include "mozilla/Variant.h"
28 #include "mozilla/dom/PBackgroundSDBConnection.h"
29 #include "mozilla/dom/quota/QuotaManager.h"
30 #include "mozilla/fallible.h"
31 #include "mozilla/ipc/BackgroundChild.h"
32 #include "mozilla/ipc/BackgroundUtils.h"
33 #include "mozilla/ipc/PBackgroundChild.h"
34 #include "mozilla/ipc/PBackgroundSharedTypes.h"
35 #include "nsDebug.h"
36 #include "nsError.h"
37 #include "nsISDBCallbacks.h"
38 #include "nsISupportsUtils.h"
39 #include "nsStringFwd.h"
40 #include "nscore.h"
41 
42 namespace mozilla::dom {
43 
44 using namespace mozilla::ipc;
45 
46 namespace {
47 
GetWriteData(JSContext * aCx,JS::Handle<JS::Value> aValue,nsCString & aData)48 nsresult GetWriteData(JSContext* aCx, JS::Handle<JS::Value> aValue,
49                       nsCString& aData) {
50   if (aValue.isObject()) {
51     JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
52 
53     bool isView = false;
54     if (JS::IsArrayBufferObject(obj) ||
55         (isView = JS_IsArrayBufferViewObject(obj))) {
56       uint8_t* data;
57       size_t length;
58       bool unused;
59       if (isView) {
60         JS_GetObjectAsArrayBufferView(obj, &length, &unused, &data);
61       } else {
62         JS::GetObjectAsArrayBuffer(obj, &length, &data);
63       }
64 
65       // Throw for large buffers to prevent truncation.
66       if (length > INT32_MAX) {
67         return NS_ERROR_ILLEGAL_VALUE;
68       }
69 
70       if (NS_WARN_IF(!aData.Assign(reinterpret_cast<char*>(data), length,
71                                    fallible_t()))) {
72         return NS_ERROR_OUT_OF_MEMORY;
73       }
74 
75       return NS_OK;
76     }
77   }
78 
79   return NS_ERROR_NOT_IMPLEMENTED;
80 }
81 
82 }  // namespace
83 
SDBConnection()84 SDBConnection::SDBConnection()
85     : mBackgroundActor(nullptr),
86       mPersistenceType(quota::PERSISTENCE_TYPE_INVALID),
87       mRunningRequest(false),
88       mOpen(false),
89       mAllowedToClose(false) {
90   AssertIsOnOwningThread();
91 }
92 
~SDBConnection()93 SDBConnection::~SDBConnection() {
94   AssertIsOnOwningThread();
95 
96   if (mBackgroundActor) {
97     mBackgroundActor->SendDeleteMeInternal();
98     MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
99   }
100 }
101 
102 // static
Create(nsISupports * aOuter,REFNSIID aIID,void ** aResult)103 nsresult SDBConnection::Create(nsISupports* aOuter, REFNSIID aIID,
104                                void** aResult) {
105   MOZ_ASSERT(aResult);
106 
107   if (NS_WARN_IF(!Preferences::GetBool(kPrefSimpleDBEnabled, false))) {
108     return NS_ERROR_NOT_AVAILABLE;
109   }
110 
111   if (aOuter) {
112     return NS_ERROR_NO_AGGREGATION;
113   }
114 
115   RefPtr<SDBConnection> connection = new SDBConnection();
116 
117   nsresult rv = connection->QueryInterface(aIID, aResult);
118   if (NS_WARN_IF(NS_FAILED(rv))) {
119     return rv;
120   }
121 
122   return NS_OK;
123 }
124 
ClearBackgroundActor()125 void SDBConnection::ClearBackgroundActor() {
126   AssertIsOnOwningThread();
127 
128   mBackgroundActor = nullptr;
129 }
130 
OnNewRequest()131 void SDBConnection::OnNewRequest() {
132   AssertIsOnOwningThread();
133   MOZ_ASSERT(!mRunningRequest);
134 
135   mRunningRequest = true;
136 }
137 
OnRequestFinished()138 void SDBConnection::OnRequestFinished() {
139   AssertIsOnOwningThread();
140   MOZ_ASSERT(mRunningRequest);
141 
142   mRunningRequest = false;
143 }
144 
OnOpen()145 void SDBConnection::OnOpen() {
146   AssertIsOnOwningThread();
147   MOZ_ASSERT(!mOpen);
148 
149   mOpen = true;
150 }
151 
OnClose(bool aAbnormal)152 void SDBConnection::OnClose(bool aAbnormal) {
153   AssertIsOnOwningThread();
154   MOZ_ASSERT(mOpen);
155 
156   mOpen = false;
157 
158   if (aAbnormal) {
159     MOZ_ASSERT(mAllowedToClose);
160 
161     if (mCloseCallback) {
162       mCloseCallback->OnClose(this);
163     }
164   }
165 }
166 
AllowToClose()167 void SDBConnection::AllowToClose() {
168   AssertIsOnOwningThread();
169 
170   mAllowedToClose = true;
171 }
172 
CheckState()173 nsresult SDBConnection::CheckState() {
174   AssertIsOnOwningThread();
175 
176   if (mAllowedToClose) {
177     return NS_ERROR_ABORT;
178   }
179 
180   if (mRunningRequest) {
181     return NS_ERROR_NOT_AVAILABLE;
182   }
183 
184   return NS_OK;
185 }
186 
EnsureBackgroundActor()187 nsresult SDBConnection::EnsureBackgroundActor() {
188   AssertIsOnOwningThread();
189 
190   if (mBackgroundActor) {
191     return NS_OK;
192   }
193 
194   PBackgroundChild* backgroundActor =
195       BackgroundChild::GetOrCreateForCurrentThread();
196   if (NS_WARN_IF(!backgroundActor)) {
197     return NS_ERROR_FAILURE;
198   }
199 
200   SDBConnectionChild* actor = new SDBConnectionChild(this);
201 
202   mBackgroundActor = static_cast<SDBConnectionChild*>(
203       backgroundActor->SendPBackgroundSDBConnectionConstructor(
204           actor, mPersistenceType, *mPrincipalInfo));
205   if (NS_WARN_IF(!mBackgroundActor)) {
206     return NS_ERROR_FAILURE;
207   }
208 
209   return NS_OK;
210 }
211 
InitiateRequest(SDBRequest * aRequest,const SDBRequestParams & aParams)212 nsresult SDBConnection::InitiateRequest(SDBRequest* aRequest,
213                                         const SDBRequestParams& aParams) {
214   AssertIsOnOwningThread();
215   MOZ_ASSERT(aRequest);
216   MOZ_ASSERT(mBackgroundActor);
217 
218   auto actor = new SDBRequestChild(aRequest);
219 
220   if (!mBackgroundActor->SendPBackgroundSDBRequestConstructor(actor, aParams)) {
221     return NS_ERROR_FAILURE;
222   }
223 
224   // Balanced in SDBRequestChild::Recv__delete__().
225   OnNewRequest();
226 
227   return NS_OK;
228 }
229 
NS_IMPL_ISUPPORTS(SDBConnection,nsISDBConnection)230 NS_IMPL_ISUPPORTS(SDBConnection, nsISDBConnection)
231 
232 NS_IMETHODIMP
233 SDBConnection::Init(nsIPrincipal* aPrincipal,
234                     const nsACString& aPersistenceType) {
235   MOZ_ASSERT(NS_IsMainThread());
236   MOZ_ASSERT(aPrincipal);
237 
238   UniquePtr<PrincipalInfo> principalInfo(new PrincipalInfo());
239   nsresult rv = PrincipalToPrincipalInfo(aPrincipal, principalInfo.get());
240   if (NS_WARN_IF(NS_FAILED(rv))) {
241     return rv;
242   }
243 
244   if (principalInfo->type() != PrincipalInfo::TContentPrincipalInfo &&
245       principalInfo->type() != PrincipalInfo::TSystemPrincipalInfo) {
246     NS_WARNING("Simpledb not allowed for this principal!");
247     return NS_ERROR_INVALID_ARG;
248   }
249 
250   if (NS_WARN_IF(!quota::QuotaManager::IsPrincipalInfoValid(*principalInfo))) {
251     return NS_ERROR_INVALID_ARG;
252   }
253 
254   PersistenceType persistenceType;
255   if (aPersistenceType.IsVoid()) {
256     persistenceType = quota::PERSISTENCE_TYPE_DEFAULT;
257   } else {
258     const auto maybePersistenceType =
259         quota::PersistenceTypeFromString(aPersistenceType, fallible);
260     if (NS_WARN_IF(maybePersistenceType.isNothing())) {
261       return NS_ERROR_INVALID_ARG;
262     }
263 
264     persistenceType = maybePersistenceType.value();
265   }
266 
267   mPrincipalInfo = std::move(principalInfo);
268   mPersistenceType = persistenceType;
269 
270   return NS_OK;
271 }
272 
273 NS_IMETHODIMP
Open(const nsAString & aName,nsISDBRequest ** _retval)274 SDBConnection::Open(const nsAString& aName, nsISDBRequest** _retval) {
275   AssertIsOnOwningThread();
276 
277   nsresult rv = CheckState();
278   if (NS_WARN_IF(NS_FAILED(rv))) {
279     return rv;
280   }
281 
282   if (mOpen) {
283     return NS_ERROR_ALREADY_INITIALIZED;
284   }
285 
286   SDBRequestOpenParams params;
287   params.name() = aName;
288 
289   RefPtr<SDBRequest> request = new SDBRequest(this);
290 
291   rv = EnsureBackgroundActor();
292   if (NS_WARN_IF(NS_FAILED(rv))) {
293     return rv;
294   }
295 
296   rv = InitiateRequest(request, params);
297   if (NS_WARN_IF(NS_FAILED(rv))) {
298     return rv;
299   }
300 
301   request.forget(_retval);
302   return NS_OK;
303 }
304 
305 NS_IMETHODIMP
Seek(uint64_t aOffset,nsISDBRequest ** _retval)306 SDBConnection::Seek(uint64_t aOffset, nsISDBRequest** _retval) {
307   AssertIsOnOwningThread();
308 
309   nsresult rv = CheckState();
310   if (NS_WARN_IF(NS_FAILED(rv))) {
311     return rv;
312   }
313 
314   if (!mOpen) {
315     return NS_BASE_STREAM_CLOSED;
316   }
317 
318   SDBRequestSeekParams params;
319   params.offset() = aOffset;
320 
321   RefPtr<SDBRequest> request = new SDBRequest(this);
322 
323   rv = InitiateRequest(request, params);
324   if (NS_WARN_IF(NS_FAILED(rv))) {
325     return rv;
326   }
327 
328   request.forget(_retval);
329   return NS_OK;
330 }
331 
332 NS_IMETHODIMP
Read(uint64_t aSize,nsISDBRequest ** _retval)333 SDBConnection::Read(uint64_t aSize, nsISDBRequest** _retval) {
334   AssertIsOnOwningThread();
335 
336   nsresult rv = CheckState();
337   if (NS_WARN_IF(NS_FAILED(rv))) {
338     return rv;
339   }
340 
341   if (!mOpen) {
342     return NS_BASE_STREAM_CLOSED;
343   }
344 
345   SDBRequestReadParams params;
346   params.size() = aSize;
347 
348   RefPtr<SDBRequest> request = new SDBRequest(this);
349 
350   rv = InitiateRequest(request, params);
351   if (NS_WARN_IF(NS_FAILED(rv))) {
352     return rv;
353   }
354 
355   request.forget(_retval);
356   return NS_OK;
357 }
358 
359 NS_IMETHODIMP
Write(JS::HandleValue aValue,JSContext * aCx,nsISDBRequest ** _retval)360 SDBConnection::Write(JS::HandleValue aValue, JSContext* aCx,
361                      nsISDBRequest** _retval) {
362   AssertIsOnOwningThread();
363 
364   nsresult rv = CheckState();
365   if (NS_WARN_IF(NS_FAILED(rv))) {
366     return rv;
367   }
368 
369   if (!mOpen) {
370     return NS_BASE_STREAM_CLOSED;
371   }
372 
373   JS::Rooted<JS::Value> value(aCx, aValue);
374 
375   nsCString data;
376   rv = GetWriteData(aCx, value, data);
377   if (NS_WARN_IF(NS_FAILED(rv))) {
378     return rv;
379   }
380 
381   SDBRequestWriteParams params;
382   params.data() = data;
383 
384   RefPtr<SDBRequest> request = new SDBRequest(this);
385 
386   rv = InitiateRequest(request, params);
387   if (NS_WARN_IF(NS_FAILED(rv))) {
388     return rv;
389   }
390 
391   request.forget(_retval);
392   return NS_OK;
393 }
394 
395 NS_IMETHODIMP
Close(nsISDBRequest ** _retval)396 SDBConnection::Close(nsISDBRequest** _retval) {
397   AssertIsOnOwningThread();
398 
399   nsresult rv = CheckState();
400   if (NS_WARN_IF(NS_FAILED(rv))) {
401     return rv;
402   }
403 
404   if (!mOpen) {
405     return NS_BASE_STREAM_CLOSED;
406   }
407 
408   SDBRequestCloseParams params;
409 
410   RefPtr<SDBRequest> request = new SDBRequest(this);
411 
412   rv = InitiateRequest(request, params);
413   if (NS_WARN_IF(NS_FAILED(rv))) {
414     return rv;
415   }
416 
417   request.forget(_retval);
418   return NS_OK;
419 }
420 
421 NS_IMETHODIMP
GetCloseCallback(nsISDBCloseCallback ** aCloseCallback)422 SDBConnection::GetCloseCallback(nsISDBCloseCallback** aCloseCallback) {
423   AssertIsOnOwningThread();
424   MOZ_ASSERT(aCloseCallback);
425 
426   NS_IF_ADDREF(*aCloseCallback = mCloseCallback);
427   return NS_OK;
428 }
429 
430 NS_IMETHODIMP
SetCloseCallback(nsISDBCloseCallback * aCloseCallback)431 SDBConnection::SetCloseCallback(nsISDBCloseCallback* aCloseCallback) {
432   AssertIsOnOwningThread();
433 
434   mCloseCallback = aCloseCallback;
435   return NS_OK;
436 }
437 
438 }  // namespace mozilla::dom
439