Bug 1919778 - Flag EnumeratedArray variable from dom/media/ipc/RemoteDecoderManagerCh...
[gecko.git] / dom / localstorage / LocalStorageManager2.cpp
blob8906047c8a59bd94c8c224b2c9be64267ec56f25
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 "LocalStorageManager2.h"
9 // Local includes
10 #include "ActorsChild.h"
11 #include "LSObject.h"
13 // Global includes
14 #include <utility>
15 #include "MainThreadUtils.h"
16 #include "jsapi.h"
17 #include "mozilla/Assertions.h"
18 #include "mozilla/ErrorResult.h"
19 #include "mozilla/MacroForEach.h"
20 #include "mozilla/OriginAttributes.h"
21 #include "mozilla/RefPtr.h"
22 #include "mozilla/RemoteLazyInputStreamThread.h"
23 #include "mozilla/dom/LocalStorageCommon.h"
24 #include "mozilla/dom/PBackgroundLSRequest.h"
25 #include "mozilla/dom/PBackgroundLSSharedTypes.h"
26 #include "mozilla/dom/PBackgroundLSSimpleRequest.h"
27 #include "mozilla/dom/Promise.h"
28 #include "mozilla/dom/quota/PromiseUtils.h"
29 #include "mozilla/dom/quota/QuotaManager.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 "nsCOMPtr.h"
35 #include "nsDebug.h"
36 #include "nsError.h"
37 #include "nsIEventTarget.h"
38 #include "nsILocalStorageManager.h"
39 #include "nsIPrincipal.h"
40 #include "nsIRunnable.h"
41 #include "nsPIDOMWindow.h"
42 #include "nsStringFwd.h"
43 #include "nsThreadUtils.h"
44 #include "nscore.h"
45 #include "xpcpublic.h"
47 namespace mozilla::dom {
49 namespace {
51 class AsyncRequestHelper final : public Runnable,
52 public LSRequestChildCallback {
53 enum class State {
54 /**
55 * The AsyncRequestHelper has been created and dispatched to the
56 * RemoteLazyInputStream Thread.
58 Initial,
59 /**
60 * Start() has been invoked on the RemoteLazyInputStream Thread and
61 * LocalStorageManager2::StartRequest has been invoked from there, sending
62 * an IPC message to PBackground to service the request. We stay in this
63 * state until a response is received.
65 ResponsePending,
66 /**
67 * A response has been received and AsyncRequestHelper has been dispatched
68 * back to the owning event target to call Finish().
70 Finishing,
71 /**
72 * Finish() has been called on the main thread. The promise will be resolved
73 * according to the received response.
75 Complete
78 // The object we are issuing a request on behalf of. Present because of the
79 // need to invoke LocalStorageManager2::StartRequest off the main thread.
80 // Dropped on return to the main-thread in Finish().
81 RefPtr<LocalStorageManager2> mManager;
82 // The thread the AsyncRequestHelper was created on. This should be the main
83 // thread.
84 nsCOMPtr<nsIEventTarget> mOwningEventTarget;
85 // The IPC actor handling the request with standard IPC allocation rules.
86 // Our reference is nulled in OnResponse which corresponds to the actor's
87 // __destroy__ method.
88 LSRequestChild* mActor;
89 RefPtr<Promise> mPromise;
90 const LSRequestParams mParams;
91 LSRequestResponse mResponse;
92 nsresult mResultCode;
93 State mState;
95 public:
96 AsyncRequestHelper(LocalStorageManager2* aManager, Promise* aPromise,
97 const LSRequestParams& aParams)
98 : Runnable("dom::LocalStorageManager2::AsyncRequestHelper"),
99 mManager(aManager),
100 mOwningEventTarget(GetCurrentSerialEventTarget()),
101 mActor(nullptr),
102 mPromise(aPromise),
103 mParams(aParams),
104 mResultCode(NS_OK),
105 mState(State::Initial) {}
107 bool IsOnOwningThread() const {
108 MOZ_ASSERT(mOwningEventTarget);
110 bool current;
111 return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) &&
112 current;
115 void AssertIsOnOwningThread() const {
116 MOZ_ASSERT(NS_IsMainThread());
117 MOZ_ASSERT(IsOnOwningThread());
120 nsresult Dispatch();
122 private:
123 ~AsyncRequestHelper() = default;
125 nsresult Start();
127 void Finish();
129 NS_DECL_ISUPPORTS_INHERITED
131 NS_DECL_NSIRUNNABLE
133 // LSRequestChildCallback
134 void OnResponse(const LSRequestResponse& aResponse) override;
137 class SimpleRequestResolver final : public LSSimpleRequestChildCallback {
138 RefPtr<Promise> mPromise;
140 public:
141 explicit SimpleRequestResolver(Promise* aPromise) : mPromise(aPromise) {}
143 NS_INLINE_DECL_REFCOUNTING(SimpleRequestResolver, override);
145 private:
146 ~SimpleRequestResolver() = default;
148 void HandleResponse(nsresult aResponse);
150 void HandleResponse(bool aResponse);
152 void HandleResponse(const nsTArray<LSItemInfo>& aResponse);
154 // LSRequestChildCallback
155 void OnResponse(const LSSimpleRequestResponse& aResponse) override;
158 nsresult CheckedPrincipalToPrincipalInfo(
159 nsIPrincipal* aPrincipal, mozilla::ipc::PrincipalInfo& aPrincipalInfo) {
160 MOZ_ASSERT(NS_IsMainThread());
161 MOZ_ASSERT(aPrincipal);
163 nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &aPrincipalInfo);
164 if (NS_WARN_IF(NS_FAILED(rv))) {
165 return rv;
168 if (NS_WARN_IF(!quota::QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) {
169 return NS_ERROR_FAILURE;
172 if (aPrincipalInfo.type() !=
173 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo &&
174 aPrincipalInfo.type() !=
175 mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) {
176 return NS_ERROR_UNEXPECTED;
179 return NS_OK;
182 } // namespace
184 LocalStorageManager2::LocalStorageManager2() {
185 MOZ_ASSERT(NS_IsMainThread());
186 MOZ_ASSERT(NextGenLocalStorageEnabled());
189 LocalStorageManager2::~LocalStorageManager2() { MOZ_ASSERT(NS_IsMainThread()); }
191 NS_IMPL_ISUPPORTS(LocalStorageManager2, nsIDOMStorageManager,
192 nsILocalStorageManager)
194 NS_IMETHODIMP
195 LocalStorageManager2::PrecacheStorage(nsIPrincipal* aPrincipal,
196 nsIPrincipal* aStoragePrincipal,
197 Storage** _retval) {
198 MOZ_ASSERT(NS_IsMainThread());
199 MOZ_ASSERT(aPrincipal);
200 MOZ_ASSERT(aStoragePrincipal);
201 MOZ_ASSERT(_retval);
203 // This method was created as part of the e10s-ification of the old LS
204 // implementation to perform a preload in the content/current process. That's
205 // not how things work in LSNG. Instead everything happens in the parent
206 // process, triggered by the official preloading spot,
207 // ContentParent::AboutToLoadHttpDocumentForChild.
208 return NS_ERROR_NOT_IMPLEMENTED;
211 NS_IMETHODIMP
212 LocalStorageManager2::CreateStorage(mozIDOMWindow* aWindow,
213 nsIPrincipal* aPrincipal,
214 nsIPrincipal* aStoragePrincipal,
215 const nsAString& aDocumentURI,
216 bool aPrivate, Storage** _retval) {
217 MOZ_ASSERT(NS_IsMainThread());
218 MOZ_ASSERT(aPrincipal);
219 MOZ_ASSERT(aStoragePrincipal);
220 MOZ_ASSERT(_retval);
222 nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow);
224 RefPtr<LSObject> object;
225 nsresult rv = LSObject::CreateForPrincipal(inner, aPrincipal,
226 aStoragePrincipal, aDocumentURI,
227 aPrivate, getter_AddRefs(object));
228 if (NS_WARN_IF(NS_FAILED(rv))) {
229 return rv;
232 object.forget(_retval);
233 return NS_OK;
236 NS_IMETHODIMP
237 LocalStorageManager2::GetStorage(mozIDOMWindow* aWindow,
238 nsIPrincipal* aPrincipal,
239 nsIPrincipal* aStoragePrincipal, bool aPrivate,
240 Storage** _retval) {
241 MOZ_ASSERT(NS_IsMainThread());
242 MOZ_ASSERT(aPrincipal);
243 MOZ_ASSERT(aStoragePrincipal);
244 MOZ_ASSERT(_retval);
246 return NS_ERROR_NOT_IMPLEMENTED;
249 NS_IMETHODIMP
250 LocalStorageManager2::CloneStorage(Storage* aStorageToCloneFrom) {
251 MOZ_ASSERT(NS_IsMainThread());
252 MOZ_ASSERT(aStorageToCloneFrom);
254 // Cloning is specific to sessionStorage; state is forked when a new tab is
255 // opened from an existing tab.
256 return NS_ERROR_NOT_IMPLEMENTED;
259 NS_IMETHODIMP
260 LocalStorageManager2::CheckStorage(nsIPrincipal* aPrincipal, Storage* aStorage,
261 bool* _retval) {
262 MOZ_ASSERT(NS_IsMainThread());
263 MOZ_ASSERT(aPrincipal);
264 MOZ_ASSERT(aStorage);
265 MOZ_ASSERT(_retval);
267 // Only used by sessionStorage.
268 return NS_ERROR_NOT_IMPLEMENTED;
271 NS_IMETHODIMP
272 LocalStorageManager2::GetNextGenLocalStorageEnabled(bool* aResult) {
273 MOZ_ASSERT(NS_IsMainThread());
274 MOZ_ASSERT(aResult);
276 *aResult = NextGenLocalStorageEnabled();
277 return NS_OK;
280 NS_IMETHODIMP
281 LocalStorageManager2::Preload(nsIPrincipal* aPrincipal, JSContext* aContext,
282 Promise** _retval) {
283 MOZ_ASSERT(NS_IsMainThread());
284 MOZ_ASSERT(aPrincipal);
285 MOZ_ASSERT(_retval);
287 nsCString originAttrSuffix;
288 nsCString originKey;
289 nsresult rv = aPrincipal->GetStorageOriginKey(originKey);
290 aPrincipal->OriginAttributesRef().CreateSuffix(originAttrSuffix);
291 if (NS_FAILED(rv)) {
292 return NS_ERROR_NOT_AVAILABLE;
295 mozilla::ipc::PrincipalInfo principalInfo;
296 rv = CheckedPrincipalToPrincipalInfo(aPrincipal, principalInfo);
297 if (NS_WARN_IF(NS_FAILED(rv))) {
298 return rv;
301 RefPtr<Promise> promise;
303 if (aContext) {
304 rv = quota::CreatePromise(aContext, getter_AddRefs(promise));
305 if (NS_WARN_IF(NS_FAILED(rv))) {
306 return rv;
310 LSRequestCommonParams commonParams;
311 commonParams.principalInfo() = principalInfo;
312 commonParams.storagePrincipalInfo() = principalInfo;
313 commonParams.originKey() = originKey;
315 LSRequestPreloadDatastoreParams params(commonParams);
317 RefPtr<AsyncRequestHelper> helper =
318 new AsyncRequestHelper(this, promise, params);
320 // This will start and finish the async request on the RemoteLazyInputStream
321 // thread.
322 // This must be done on RemoteLazyInputStream Thread because it's very likely
323 // that a content process will issue a prepare datastore request for the same
324 // principal while blocking the content process on the main thread.
325 // There would be a potential for deadlock if the preloading was initialized
326 // from the main thread of the parent process and a11y issued a synchronous
327 // message from the parent process to the content process (approximately at
328 // the same time) because the preload request wouldn't be able to respond
329 // to the Ready message by sending the Finish message which is needed to
330 // finish the preload request and unblock the prepare datastore request.
331 rv = helper->Dispatch();
332 if (NS_WARN_IF(NS_FAILED(rv))) {
333 return rv;
336 promise.forget(_retval);
337 return NS_OK;
340 NS_IMETHODIMP
341 LocalStorageManager2::IsPreloaded(nsIPrincipal* aPrincipal, JSContext* aContext,
342 Promise** _retval) {
343 MOZ_ASSERT(NS_IsMainThread());
344 MOZ_ASSERT(aPrincipal);
345 MOZ_ASSERT(_retval);
347 RefPtr<Promise> promise;
348 nsresult rv = quota::CreatePromise(aContext, getter_AddRefs(promise));
349 if (NS_WARN_IF(NS_FAILED(rv))) {
350 return rv;
353 LSSimpleRequestPreloadedParams params;
355 rv = CheckedPrincipalToPrincipalInfo(aPrincipal, params.principalInfo());
356 if (NS_WARN_IF(NS_FAILED(rv))) {
357 return rv;
360 params.storagePrincipalInfo() = params.principalInfo();
362 rv = StartSimpleRequest(promise, params);
363 if (NS_WARN_IF(NS_FAILED(rv))) {
364 return rv;
367 promise.forget(_retval);
368 return NS_OK;
371 NS_IMETHODIMP
372 LocalStorageManager2::GetState(nsIPrincipal* aPrincipal, JSContext* aContext,
373 Promise** _retval) {
374 MOZ_ASSERT(NS_IsMainThread());
375 MOZ_ASSERT(aPrincipal);
376 MOZ_ASSERT(_retval);
378 RefPtr<Promise> promise;
379 nsresult rv = quota::CreatePromise(aContext, getter_AddRefs(promise));
380 if (NS_WARN_IF(NS_FAILED(rv))) {
381 return rv;
384 LSSimpleRequestGetStateParams params;
386 rv = CheckedPrincipalToPrincipalInfo(aPrincipal, params.principalInfo());
387 if (NS_WARN_IF(NS_FAILED(rv))) {
388 return rv;
391 params.storagePrincipalInfo() = params.principalInfo();
393 rv = StartSimpleRequest(promise, params);
394 if (NS_WARN_IF(NS_FAILED(rv))) {
395 return rv;
398 promise.forget(_retval);
399 return NS_OK;
402 LSRequestChild* LocalStorageManager2::StartRequest(
403 const LSRequestParams& aParams, LSRequestChildCallback* aCallback) {
404 AssertIsOnDOMFileThread();
406 mozilla::ipc::PBackgroundChild* backgroundActor =
407 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
408 if (NS_WARN_IF(!backgroundActor)) {
409 return nullptr;
412 auto actor = new LSRequestChild();
414 if (!backgroundActor->SendPBackgroundLSRequestConstructor(actor, aParams)) {
415 return nullptr;
418 // Must set callback after calling SendPBackgroundLSRequestConstructor since
419 // it can be called synchronously when SendPBackgroundLSRequestConstructor
420 // fails.
421 actor->SetCallback(aCallback);
423 return actor;
426 nsresult LocalStorageManager2::StartSimpleRequest(
427 Promise* aPromise, const LSSimpleRequestParams& aParams) {
428 MOZ_ASSERT(NS_IsMainThread());
429 MOZ_ASSERT(aPromise);
431 mozilla::ipc::PBackgroundChild* backgroundActor =
432 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
433 if (NS_WARN_IF(!backgroundActor)) {
434 return NS_ERROR_FAILURE;
437 auto actor = new LSSimpleRequestChild();
439 if (!backgroundActor->SendPBackgroundLSSimpleRequestConstructor(actor,
440 aParams)) {
441 return NS_ERROR_FAILURE;
444 RefPtr<SimpleRequestResolver> resolver = new SimpleRequestResolver(aPromise);
446 // Must set callback after calling SendPBackgroundLSRequestConstructor since
447 // it can be called synchronously when SendPBackgroundLSRequestConstructor
448 // fails.
449 actor->SetCallback(resolver);
451 return NS_OK;
454 nsresult AsyncRequestHelper::Dispatch() {
455 AssertIsOnOwningThread();
457 nsCOMPtr<nsIEventTarget> domFileThread =
458 RemoteLazyInputStreamThread::GetOrCreate();
459 if (NS_WARN_IF(!domFileThread)) {
460 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
463 nsresult rv = domFileThread->Dispatch(this, NS_DISPATCH_NORMAL);
464 if (NS_WARN_IF(NS_FAILED(rv))) {
465 return rv;
468 return NS_OK;
471 nsresult AsyncRequestHelper::Start() {
472 AssertIsOnDOMFileThread();
473 MOZ_ASSERT(mState == State::Initial);
475 mState = State::ResponsePending;
477 LSRequestChild* actor = mManager->StartRequest(mParams, this);
478 if (NS_WARN_IF(!actor)) {
479 return NS_ERROR_FAILURE;
482 mActor = actor;
484 return NS_OK;
487 void AsyncRequestHelper::Finish() {
488 AssertIsOnOwningThread();
489 MOZ_ASSERT(mState == State::Finishing);
491 if (NS_WARN_IF(NS_FAILED(mResultCode))) {
492 if (mPromise) {
493 mPromise->MaybeReject(mResultCode);
495 } else {
496 switch (mResponse.type()) {
497 case LSRequestResponse::Tnsresult:
498 if (mPromise) {
499 mPromise->MaybeReject(mResponse.get_nsresult());
501 break;
503 case LSRequestResponse::TLSRequestPreloadDatastoreResponse:
504 if (mPromise) {
505 mPromise->MaybeResolveWithUndefined();
507 break;
508 default:
509 MOZ_CRASH("Unknown response type!");
513 mManager = nullptr;
514 mPromise = nullptr;
516 mState = State::Complete;
519 NS_IMPL_ISUPPORTS_INHERITED0(AsyncRequestHelper, Runnable)
521 NS_IMETHODIMP
522 AsyncRequestHelper::Run() {
523 nsresult rv;
525 switch (mState) {
526 case State::Initial:
527 rv = Start();
528 break;
530 case State::Finishing:
531 Finish();
532 return NS_OK;
534 default:
535 MOZ_CRASH("Bad state!");
538 if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::Finishing) {
539 if (NS_SUCCEEDED(mResultCode)) {
540 mResultCode = rv;
543 mState = State::Finishing;
545 if (IsOnOwningThread()) {
546 Finish();
547 } else {
548 MOZ_ALWAYS_SUCCEEDS(
549 mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
553 return NS_OK;
556 void AsyncRequestHelper::OnResponse(const LSRequestResponse& aResponse) {
557 AssertIsOnDOMFileThread();
558 MOZ_ASSERT(mState == State::ResponsePending);
560 mActor = nullptr;
562 mResponse = aResponse;
564 mState = State::Finishing;
566 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
569 void SimpleRequestResolver::HandleResponse(nsresult aResponse) {
570 MOZ_ASSERT(NS_IsMainThread());
571 MOZ_ASSERT(mPromise);
573 mPromise->MaybeReject(aResponse);
576 void SimpleRequestResolver::HandleResponse(bool aResponse) {
577 MOZ_ASSERT(NS_IsMainThread());
578 MOZ_ASSERT(mPromise);
580 mPromise->MaybeResolve(aResponse);
583 [[nodiscard]] static bool ToJSValue(JSContext* aCx,
584 const nsTArray<LSItemInfo>& aArgument,
585 JS::MutableHandle<JS::Value> aValue) {
586 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
587 if (!obj) {
588 return false;
591 for (size_t i = 0; i < aArgument.Length(); ++i) {
592 const LSItemInfo& itemInfo = aArgument[i];
594 const nsString& key = itemInfo.key();
596 JS::Rooted<JS::Value> value(aCx);
597 if (!ToJSValue(aCx, itemInfo.value().AsString(), &value)) {
598 return false;
601 if (!JS_DefineUCProperty(aCx, obj, key.BeginReading(), key.Length(), value,
602 JSPROP_ENUMERATE)) {
603 return false;
607 aValue.setObject(*obj);
608 return true;
611 void SimpleRequestResolver::HandleResponse(
612 const nsTArray<LSItemInfo>& aResponse) {
613 MOZ_ASSERT(NS_IsMainThread());
614 MOZ_ASSERT(mPromise);
616 mPromise->MaybeResolve(aResponse);
619 void SimpleRequestResolver::OnResponse(
620 const LSSimpleRequestResponse& aResponse) {
621 MOZ_ASSERT(NS_IsMainThread());
623 switch (aResponse.type()) {
624 case LSSimpleRequestResponse::Tnsresult:
625 HandleResponse(aResponse.get_nsresult());
626 break;
628 case LSSimpleRequestResponse::TLSSimpleRequestPreloadedResponse:
629 HandleResponse(
630 aResponse.get_LSSimpleRequestPreloadedResponse().preloaded());
631 break;
633 case LSSimpleRequestResponse::TLSSimpleRequestGetStateResponse:
634 HandleResponse(
635 aResponse.get_LSSimpleRequestGetStateResponse().itemInfos());
636 break;
638 default:
639 MOZ_CRASH("Unknown response type!");
643 } // namespace mozilla::dom