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