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/. */
7 #include "SDBConnection.h"
10 #include "ActorsChild.h"
11 #include "SDBRequest.h"
12 #include "SimpleDBCommon.h"
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"
37 #include "nsISDBCallbacks.h"
38 #include "nsISupportsUtils.h"
39 #include "nsStringFwd.h"
42 namespace mozilla::dom
{
44 using namespace mozilla::ipc
;
48 nsresult
GetWriteData(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
50 if (aValue
.isObject()) {
51 JS::Rooted
<JSObject
*> obj(aCx
, &aValue
.toObject());
54 if (JS::IsArrayBufferObject(obj
) ||
55 (isView
= JS_IsArrayBufferViewObject(obj
))) {
60 JS_GetObjectAsArrayBufferView(obj
, &length
, &unused
, &data
);
62 JS::GetObjectAsArrayBuffer(obj
, &length
, &data
);
65 // Throw for large buffers to prevent truncation.
66 if (length
> INT32_MAX
) {
67 return NS_ERROR_ILLEGAL_VALUE
;
70 if (NS_WARN_IF(!aData
.Assign(reinterpret_cast<char*>(data
), length
,
72 return NS_ERROR_OUT_OF_MEMORY
;
79 return NS_ERROR_NOT_IMPLEMENTED
;
84 SDBConnection::SDBConnection()
85 : mBackgroundActor(nullptr),
86 mPersistenceType(quota::PERSISTENCE_TYPE_INVALID
),
87 mRunningRequest(false),
89 mAllowedToClose(false) {
90 AssertIsOnOwningThread();
93 SDBConnection::~SDBConnection() {
94 AssertIsOnOwningThread();
96 if (mBackgroundActor
) {
97 mBackgroundActor
->SendDeleteMeInternal();
98 MOZ_ASSERT(!mBackgroundActor
, "SendDeleteMeInternal should have cleared!");
103 nsresult
SDBConnection::Create(REFNSIID aIID
, void** aResult
) {
106 if (NS_WARN_IF(!Preferences::GetBool(kPrefSimpleDBEnabled
, false))) {
107 return NS_ERROR_NOT_AVAILABLE
;
110 RefPtr
<SDBConnection
> connection
= new SDBConnection();
112 nsresult rv
= connection
->QueryInterface(aIID
, aResult
);
113 if (NS_WARN_IF(NS_FAILED(rv
))) {
120 void SDBConnection::ClearBackgroundActor() {
121 AssertIsOnOwningThread();
123 mBackgroundActor
= nullptr;
126 void SDBConnection::OnNewRequest() {
127 AssertIsOnOwningThread();
128 MOZ_ASSERT(!mRunningRequest
);
130 mRunningRequest
= true;
133 void SDBConnection::OnRequestFinished() {
134 AssertIsOnOwningThread();
135 MOZ_ASSERT(mRunningRequest
);
137 mRunningRequest
= false;
140 void SDBConnection::OnOpen() {
141 AssertIsOnOwningThread();
147 void SDBConnection::OnClose(bool aAbnormal
) {
148 AssertIsOnOwningThread();
154 MOZ_ASSERT(mAllowedToClose
);
156 if (mCloseCallback
) {
157 mCloseCallback
->OnClose(this);
162 void SDBConnection::AllowToClose() {
163 AssertIsOnOwningThread();
165 mAllowedToClose
= true;
168 nsresult
SDBConnection::CheckState() {
169 AssertIsOnOwningThread();
171 if (mAllowedToClose
) {
172 return NS_ERROR_ABORT
;
175 if (mRunningRequest
) {
176 return NS_ERROR_NOT_AVAILABLE
;
182 nsresult
SDBConnection::EnsureBackgroundActor() {
183 AssertIsOnOwningThread();
185 if (mBackgroundActor
) {
189 PBackgroundChild
* backgroundActor
=
190 BackgroundChild::GetOrCreateForCurrentThread();
191 if (NS_WARN_IF(!backgroundActor
)) {
192 return NS_ERROR_FAILURE
;
195 SDBConnectionChild
* actor
= new SDBConnectionChild(this);
197 mBackgroundActor
= static_cast<SDBConnectionChild
*>(
198 backgroundActor
->SendPBackgroundSDBConnectionConstructor(
199 actor
, mPersistenceType
, *mPrincipalInfo
));
200 if (NS_WARN_IF(!mBackgroundActor
)) {
201 return NS_ERROR_FAILURE
;
207 nsresult
SDBConnection::InitiateRequest(SDBRequest
* aRequest
,
208 const SDBRequestParams
& aParams
) {
209 AssertIsOnOwningThread();
210 MOZ_ASSERT(aRequest
);
211 MOZ_ASSERT(mBackgroundActor
);
213 auto actor
= new SDBRequestChild(aRequest
);
215 if (!mBackgroundActor
->SendPBackgroundSDBRequestConstructor(actor
, aParams
)) {
216 return NS_ERROR_FAILURE
;
219 // Balanced in SDBRequestChild::Recv__delete__().
225 NS_IMPL_ISUPPORTS(SDBConnection
, nsISDBConnection
)
228 SDBConnection::Init(nsIPrincipal
* aPrincipal
,
229 const nsACString
& aPersistenceType
) {
230 MOZ_ASSERT(NS_IsMainThread());
231 MOZ_ASSERT(aPrincipal
);
233 UniquePtr
<PrincipalInfo
> principalInfo(new PrincipalInfo());
234 nsresult rv
= PrincipalToPrincipalInfo(aPrincipal
, principalInfo
.get());
235 if (NS_WARN_IF(NS_FAILED(rv
))) {
239 if (principalInfo
->type() != PrincipalInfo::TContentPrincipalInfo
&&
240 principalInfo
->type() != PrincipalInfo::TSystemPrincipalInfo
) {
241 NS_WARNING("Simpledb not allowed for this principal!");
242 return NS_ERROR_INVALID_ARG
;
245 if (NS_WARN_IF(!quota::QuotaManager::IsPrincipalInfoValid(*principalInfo
))) {
246 return NS_ERROR_INVALID_ARG
;
249 PersistenceType persistenceType
;
250 if (aPersistenceType
.IsVoid()) {
251 persistenceType
= quota::PERSISTENCE_TYPE_DEFAULT
;
253 const auto maybePersistenceType
=
254 quota::PersistenceTypeFromString(aPersistenceType
, fallible
);
255 if (NS_WARN_IF(maybePersistenceType
.isNothing())) {
256 return NS_ERROR_INVALID_ARG
;
259 persistenceType
= maybePersistenceType
.value();
262 mPrincipalInfo
= std::move(principalInfo
);
263 mPersistenceType
= persistenceType
;
269 SDBConnection::Open(const nsAString
& aName
, nsISDBRequest
** _retval
) {
270 AssertIsOnOwningThread();
272 nsresult rv
= CheckState();
273 if (NS_WARN_IF(NS_FAILED(rv
))) {
278 return NS_ERROR_ALREADY_INITIALIZED
;
281 SDBRequestOpenParams params
;
282 params
.name() = aName
;
284 RefPtr
<SDBRequest
> request
= new SDBRequest(this);
286 rv
= EnsureBackgroundActor();
287 if (NS_WARN_IF(NS_FAILED(rv
))) {
291 rv
= InitiateRequest(request
, params
);
292 if (NS_WARN_IF(NS_FAILED(rv
))) {
296 request
.forget(_retval
);
301 SDBConnection::Seek(uint64_t aOffset
, nsISDBRequest
** _retval
) {
302 AssertIsOnOwningThread();
304 nsresult rv
= CheckState();
305 if (NS_WARN_IF(NS_FAILED(rv
))) {
310 return NS_BASE_STREAM_CLOSED
;
313 SDBRequestSeekParams params
;
314 params
.offset() = aOffset
;
316 RefPtr
<SDBRequest
> request
= new SDBRequest(this);
318 rv
= InitiateRequest(request
, params
);
319 if (NS_WARN_IF(NS_FAILED(rv
))) {
323 request
.forget(_retval
);
328 SDBConnection::Read(uint64_t aSize
, nsISDBRequest
** _retval
) {
329 AssertIsOnOwningThread();
331 nsresult rv
= CheckState();
332 if (NS_WARN_IF(NS_FAILED(rv
))) {
337 return NS_BASE_STREAM_CLOSED
;
340 SDBRequestReadParams params
;
341 params
.size() = aSize
;
343 RefPtr
<SDBRequest
> request
= new SDBRequest(this);
345 rv
= InitiateRequest(request
, params
);
346 if (NS_WARN_IF(NS_FAILED(rv
))) {
350 request
.forget(_retval
);
355 SDBConnection::Write(JS::HandleValue aValue
, JSContext
* aCx
,
356 nsISDBRequest
** _retval
) {
357 AssertIsOnOwningThread();
359 nsresult rv
= CheckState();
360 if (NS_WARN_IF(NS_FAILED(rv
))) {
365 return NS_BASE_STREAM_CLOSED
;
368 JS::Rooted
<JS::Value
> value(aCx
, aValue
);
371 rv
= GetWriteData(aCx
, value
, data
);
372 if (NS_WARN_IF(NS_FAILED(rv
))) {
376 SDBRequestWriteParams params
;
377 params
.data() = data
;
379 RefPtr
<SDBRequest
> request
= new SDBRequest(this);
381 rv
= InitiateRequest(request
, params
);
382 if (NS_WARN_IF(NS_FAILED(rv
))) {
386 request
.forget(_retval
);
391 SDBConnection::Close(nsISDBRequest
** _retval
) {
392 AssertIsOnOwningThread();
394 nsresult rv
= CheckState();
395 if (NS_WARN_IF(NS_FAILED(rv
))) {
400 return NS_BASE_STREAM_CLOSED
;
403 SDBRequestCloseParams params
;
405 RefPtr
<SDBRequest
> request
= new SDBRequest(this);
407 rv
= InitiateRequest(request
, params
);
408 if (NS_WARN_IF(NS_FAILED(rv
))) {
412 request
.forget(_retval
);
417 SDBConnection::GetCloseCallback(nsISDBCloseCallback
** aCloseCallback
) {
418 AssertIsOnOwningThread();
419 MOZ_ASSERT(aCloseCallback
);
421 NS_IF_ADDREF(*aCloseCallback
= mCloseCallback
);
426 SDBConnection::SetCloseCallback(nsISDBCloseCallback
* aCloseCallback
) {
427 AssertIsOnOwningThread();
429 mCloseCallback
= aCloseCallback
;
433 } // namespace mozilla::dom