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(nsISupports
* aOuter
, REFNSIID aIID
,
107 if (NS_WARN_IF(!Preferences::GetBool(kPrefSimpleDBEnabled
, false))) {
108 return NS_ERROR_NOT_AVAILABLE
;
112 return NS_ERROR_NO_AGGREGATION
;
115 RefPtr
<SDBConnection
> connection
= new SDBConnection();
117 nsresult rv
= connection
->QueryInterface(aIID
, aResult
);
118 if (NS_WARN_IF(NS_FAILED(rv
))) {
125 void SDBConnection::ClearBackgroundActor() {
126 AssertIsOnOwningThread();
128 mBackgroundActor
= nullptr;
131 void SDBConnection::OnNewRequest() {
132 AssertIsOnOwningThread();
133 MOZ_ASSERT(!mRunningRequest
);
135 mRunningRequest
= true;
138 void SDBConnection::OnRequestFinished() {
139 AssertIsOnOwningThread();
140 MOZ_ASSERT(mRunningRequest
);
142 mRunningRequest
= false;
145 void SDBConnection::OnOpen() {
146 AssertIsOnOwningThread();
152 void SDBConnection::OnClose(bool aAbnormal
) {
153 AssertIsOnOwningThread();
159 MOZ_ASSERT(mAllowedToClose
);
161 if (mCloseCallback
) {
162 mCloseCallback
->OnClose(this);
167 void SDBConnection::AllowToClose() {
168 AssertIsOnOwningThread();
170 mAllowedToClose
= true;
173 nsresult
SDBConnection::CheckState() {
174 AssertIsOnOwningThread();
176 if (mAllowedToClose
) {
177 return NS_ERROR_ABORT
;
180 if (mRunningRequest
) {
181 return NS_ERROR_NOT_AVAILABLE
;
187 nsresult
SDBConnection::EnsureBackgroundActor() {
188 AssertIsOnOwningThread();
190 if (mBackgroundActor
) {
194 PBackgroundChild
* backgroundActor
=
195 BackgroundChild::GetOrCreateForCurrentThread();
196 if (NS_WARN_IF(!backgroundActor
)) {
197 return NS_ERROR_FAILURE
;
200 SDBConnectionChild
* actor
= new SDBConnectionChild(this);
202 mBackgroundActor
= static_cast<SDBConnectionChild
*>(
203 backgroundActor
->SendPBackgroundSDBConnectionConstructor(
204 actor
, mPersistenceType
, *mPrincipalInfo
));
205 if (NS_WARN_IF(!mBackgroundActor
)) {
206 return NS_ERROR_FAILURE
;
212 nsresult
SDBConnection::InitiateRequest(SDBRequest
* aRequest
,
213 const SDBRequestParams
& aParams
) {
214 AssertIsOnOwningThread();
215 MOZ_ASSERT(aRequest
);
216 MOZ_ASSERT(mBackgroundActor
);
218 auto actor
= new SDBRequestChild(aRequest
);
220 if (!mBackgroundActor
->SendPBackgroundSDBRequestConstructor(actor
, aParams
)) {
221 return NS_ERROR_FAILURE
;
224 // Balanced in SDBRequestChild::Recv__delete__().
230 NS_IMPL_ISUPPORTS(SDBConnection
, nsISDBConnection
)
233 SDBConnection::Init(nsIPrincipal
* aPrincipal
,
234 const nsACString
& aPersistenceType
) {
235 MOZ_ASSERT(NS_IsMainThread());
236 MOZ_ASSERT(aPrincipal
);
238 UniquePtr
<PrincipalInfo
> principalInfo(new PrincipalInfo());
239 nsresult rv
= PrincipalToPrincipalInfo(aPrincipal
, principalInfo
.get());
240 if (NS_WARN_IF(NS_FAILED(rv
))) {
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
;
250 if (NS_WARN_IF(!quota::QuotaManager::IsPrincipalInfoValid(*principalInfo
))) {
251 return NS_ERROR_INVALID_ARG
;
254 PersistenceType persistenceType
;
255 if (aPersistenceType
.IsVoid()) {
256 persistenceType
= quota::PERSISTENCE_TYPE_DEFAULT
;
258 const auto maybePersistenceType
=
259 quota::PersistenceTypeFromString(aPersistenceType
, fallible
);
260 if (NS_WARN_IF(maybePersistenceType
.isNothing())) {
261 return NS_ERROR_INVALID_ARG
;
264 persistenceType
= maybePersistenceType
.value();
267 mPrincipalInfo
= std::move(principalInfo
);
268 mPersistenceType
= persistenceType
;
274 SDBConnection::Open(const nsAString
& aName
, nsISDBRequest
** _retval
) {
275 AssertIsOnOwningThread();
277 nsresult rv
= CheckState();
278 if (NS_WARN_IF(NS_FAILED(rv
))) {
283 return NS_ERROR_ALREADY_INITIALIZED
;
286 SDBRequestOpenParams params
;
287 params
.name() = aName
;
289 RefPtr
<SDBRequest
> request
= new SDBRequest(this);
291 rv
= EnsureBackgroundActor();
292 if (NS_WARN_IF(NS_FAILED(rv
))) {
296 rv
= InitiateRequest(request
, params
);
297 if (NS_WARN_IF(NS_FAILED(rv
))) {
301 request
.forget(_retval
);
306 SDBConnection::Seek(uint64_t aOffset
, nsISDBRequest
** _retval
) {
307 AssertIsOnOwningThread();
309 nsresult rv
= CheckState();
310 if (NS_WARN_IF(NS_FAILED(rv
))) {
315 return NS_BASE_STREAM_CLOSED
;
318 SDBRequestSeekParams params
;
319 params
.offset() = aOffset
;
321 RefPtr
<SDBRequest
> request
= new SDBRequest(this);
323 rv
= InitiateRequest(request
, params
);
324 if (NS_WARN_IF(NS_FAILED(rv
))) {
328 request
.forget(_retval
);
333 SDBConnection::Read(uint64_t aSize
, nsISDBRequest
** _retval
) {
334 AssertIsOnOwningThread();
336 nsresult rv
= CheckState();
337 if (NS_WARN_IF(NS_FAILED(rv
))) {
342 return NS_BASE_STREAM_CLOSED
;
345 SDBRequestReadParams params
;
346 params
.size() = aSize
;
348 RefPtr
<SDBRequest
> request
= new SDBRequest(this);
350 rv
= InitiateRequest(request
, params
);
351 if (NS_WARN_IF(NS_FAILED(rv
))) {
355 request
.forget(_retval
);
360 SDBConnection::Write(JS::HandleValue aValue
, JSContext
* aCx
,
361 nsISDBRequest
** _retval
) {
362 AssertIsOnOwningThread();
364 nsresult rv
= CheckState();
365 if (NS_WARN_IF(NS_FAILED(rv
))) {
370 return NS_BASE_STREAM_CLOSED
;
373 JS::Rooted
<JS::Value
> value(aCx
, aValue
);
376 rv
= GetWriteData(aCx
, value
, data
);
377 if (NS_WARN_IF(NS_FAILED(rv
))) {
381 SDBRequestWriteParams params
;
382 params
.data() = data
;
384 RefPtr
<SDBRequest
> request
= new SDBRequest(this);
386 rv
= InitiateRequest(request
, params
);
387 if (NS_WARN_IF(NS_FAILED(rv
))) {
391 request
.forget(_retval
);
396 SDBConnection::Close(nsISDBRequest
** _retval
) {
397 AssertIsOnOwningThread();
399 nsresult rv
= CheckState();
400 if (NS_WARN_IF(NS_FAILED(rv
))) {
405 return NS_BASE_STREAM_CLOSED
;
408 SDBRequestCloseParams params
;
410 RefPtr
<SDBRequest
> request
= new SDBRequest(this);
412 rv
= InitiateRequest(request
, params
);
413 if (NS_WARN_IF(NS_FAILED(rv
))) {
417 request
.forget(_retval
);
422 SDBConnection::GetCloseCallback(nsISDBCloseCallback
** aCloseCallback
) {
423 AssertIsOnOwningThread();
424 MOZ_ASSERT(aCloseCallback
);
426 NS_IF_ADDREF(*aCloseCallback
= mCloseCallback
);
431 SDBConnection::SetCloseCallback(nsISDBCloseCallback
* aCloseCallback
) {
432 AssertIsOnOwningThread();
434 mCloseCallback
= aCloseCallback
;
438 } // namespace mozilla::dom