Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / localstorage / LocalStorageManager2.cpp
blobcd2cd9eca06fe99b4ae51c65ac8f6daf176700d9
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/QuotaManager.h"
29 #include "mozilla/ipc/BackgroundChild.h"
30 #include "mozilla/ipc/BackgroundUtils.h"
31 #include "mozilla/ipc/PBackgroundChild.h"
32 #include "mozilla/ipc/PBackgroundSharedTypes.h"
33 #include "nsCOMPtr.h"
34 #include "nsDebug.h"
35 #include "nsError.h"
36 #include "nsIEventTarget.h"
37 #include "nsILocalStorageManager.h"
38 #include "nsIPrincipal.h"
39 #include "nsIRunnable.h"
40 #include "nsPIDOMWindow.h"
41 #include "nsStringFwd.h"
42 #include "nsThreadUtils.h"
43 #include "nscore.h"
44 #include "xpcpublic.h"
46 namespace mozilla::dom {
48 namespace {
50 class AsyncRequestHelper final : public Runnable,
51 public LSRequestChildCallback {
52 enum class State {
53 /**
54 * The AsyncRequestHelper has been created and dispatched to the
55 * RemoteLazyInputStream Thread.
57 Initial,
58 /**
59 * Start() has been invoked on the RemoteLazyInputStream Thread and
60 * LocalStorageManager2::StartRequest has been invoked from there, sending
61 * an IPC message to PBackground to service the request. We stay in this
62 * state until a response is received.
64 ResponsePending,
65 /**
66 * A response has been received and AsyncRequestHelper has been dispatched
67 * back to the owning event target to call Finish().
69 Finishing,
70 /**
71 * Finish() has been called on the main thread. The promise will be resolved
72 * according to the received response.
74 Complete
77 // The object we are issuing a request on behalf of. Present because of the
78 // need to invoke LocalStorageManager2::StartRequest off the main thread.
79 // Dropped on return to the main-thread in Finish().
80 RefPtr<LocalStorageManager2> mManager;
81 // The thread the AsyncRequestHelper was created on. This should be the main
82 // thread.
83 nsCOMPtr<nsIEventTarget> mOwningEventTarget;
84 // The IPC actor handling the request with standard IPC allocation rules.
85 // Our reference is nulled in OnResponse which corresponds to the actor's
86 // __destroy__ method.
87 LSRequestChild* mActor;
88 RefPtr<Promise> mPromise;
89 const LSRequestParams mParams;
90 LSRequestResponse mResponse;
91 nsresult mResultCode;
92 State mState;
94 public:
95 AsyncRequestHelper(LocalStorageManager2* aManager, Promise* aPromise,
96 const LSRequestParams& aParams)
97 : Runnable("dom::LocalStorageManager2::AsyncRequestHelper"),
98 mManager(aManager),
99 mOwningEventTarget(GetCurrentSerialEventTarget()),
100 mActor(nullptr),
101 mPromise(aPromise),
102 mParams(aParams),
103 mResultCode(NS_OK),
104 mState(State::Initial) {}
106 bool IsOnOwningThread() const {
107 MOZ_ASSERT(mOwningEventTarget);
109 bool current;
110 return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) &&
111 current;
114 void AssertIsOnOwningThread() const {
115 MOZ_ASSERT(NS_IsMainThread());
116 MOZ_ASSERT(IsOnOwningThread());
119 nsresult Dispatch();
121 private:
122 ~AsyncRequestHelper() = default;
124 nsresult Start();
126 void Finish();
128 NS_DECL_ISUPPORTS_INHERITED
130 NS_DECL_NSIRUNNABLE
132 // LSRequestChildCallback
133 void OnResponse(const LSRequestResponse& aResponse) override;
136 class SimpleRequestResolver final : public LSSimpleRequestChildCallback {
137 RefPtr<Promise> mPromise;
139 public:
140 explicit SimpleRequestResolver(Promise* aPromise) : mPromise(aPromise) {}
142 NS_INLINE_DECL_REFCOUNTING(SimpleRequestResolver, override);
144 private:
145 ~SimpleRequestResolver() = default;
147 void HandleResponse(nsresult aResponse);
149 void HandleResponse(bool aResponse);
151 void HandleResponse(const nsTArray<LSItemInfo>& aResponse);
153 // LSRequestChildCallback
154 void OnResponse(const LSSimpleRequestResponse& aResponse) override;
157 nsresult CreatePromise(JSContext* aContext, Promise** aPromise) {
158 MOZ_ASSERT(NS_IsMainThread());
159 MOZ_ASSERT(aContext);
161 nsIGlobalObject* global =
162 xpc::NativeGlobal(JS::CurrentGlobalOrNull(aContext));
163 if (NS_WARN_IF(!global)) {
164 return NS_ERROR_FAILURE;
167 ErrorResult result;
168 RefPtr<Promise> promise = Promise::Create(global, result);
169 if (result.Failed()) {
170 return result.StealNSResult();
173 promise.forget(aPromise);
174 return NS_OK;
177 nsresult CheckedPrincipalToPrincipalInfo(
178 nsIPrincipal* aPrincipal, mozilla::ipc::PrincipalInfo& aPrincipalInfo) {
179 MOZ_ASSERT(NS_IsMainThread());
180 MOZ_ASSERT(aPrincipal);
182 nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &aPrincipalInfo);
183 if (NS_WARN_IF(NS_FAILED(rv))) {
184 return rv;
187 if (NS_WARN_IF(!quota::QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) {
188 return NS_ERROR_FAILURE;
191 if (aPrincipalInfo.type() !=
192 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo &&
193 aPrincipalInfo.type() !=
194 mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) {
195 return NS_ERROR_UNEXPECTED;
198 return NS_OK;
201 } // namespace
203 LocalStorageManager2::LocalStorageManager2() {
204 MOZ_ASSERT(NS_IsMainThread());
205 MOZ_ASSERT(NextGenLocalStorageEnabled());
208 LocalStorageManager2::~LocalStorageManager2() { MOZ_ASSERT(NS_IsMainThread()); }
210 NS_IMPL_ISUPPORTS(LocalStorageManager2, nsIDOMStorageManager,
211 nsILocalStorageManager)
213 NS_IMETHODIMP
214 LocalStorageManager2::PrecacheStorage(nsIPrincipal* aPrincipal,
215 nsIPrincipal* aStoragePrincipal,
216 Storage** _retval) {
217 MOZ_ASSERT(NS_IsMainThread());
218 MOZ_ASSERT(aPrincipal);
219 MOZ_ASSERT(aStoragePrincipal);
220 MOZ_ASSERT(_retval);
222 // This method was created as part of the e10s-ification of the old LS
223 // implementation to perform a preload in the content/current process. That's
224 // not how things work in LSNG. Instead everything happens in the parent
225 // process, triggered by the official preloading spot,
226 // ContentParent::AboutToLoadHttpDocumentForChild.
227 return NS_ERROR_NOT_IMPLEMENTED;
230 NS_IMETHODIMP
231 LocalStorageManager2::CreateStorage(mozIDOMWindow* aWindow,
232 nsIPrincipal* aPrincipal,
233 nsIPrincipal* aStoragePrincipal,
234 const nsAString& aDocumentURI,
235 bool aPrivate, Storage** _retval) {
236 MOZ_ASSERT(NS_IsMainThread());
237 MOZ_ASSERT(aPrincipal);
238 MOZ_ASSERT(aStoragePrincipal);
239 MOZ_ASSERT(_retval);
241 nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow);
243 RefPtr<LSObject> object;
244 nsresult rv = LSObject::CreateForPrincipal(inner, aPrincipal,
245 aStoragePrincipal, aDocumentURI,
246 aPrivate, getter_AddRefs(object));
247 if (NS_WARN_IF(NS_FAILED(rv))) {
248 return rv;
251 object.forget(_retval);
252 return NS_OK;
255 NS_IMETHODIMP
256 LocalStorageManager2::GetStorage(mozIDOMWindow* aWindow,
257 nsIPrincipal* aPrincipal,
258 nsIPrincipal* aStoragePrincipal, bool aPrivate,
259 Storage** _retval) {
260 MOZ_ASSERT(NS_IsMainThread());
261 MOZ_ASSERT(aPrincipal);
262 MOZ_ASSERT(aStoragePrincipal);
263 MOZ_ASSERT(_retval);
265 return NS_ERROR_NOT_IMPLEMENTED;
268 NS_IMETHODIMP
269 LocalStorageManager2::CloneStorage(Storage* aStorageToCloneFrom) {
270 MOZ_ASSERT(NS_IsMainThread());
271 MOZ_ASSERT(aStorageToCloneFrom);
273 // Cloning is specific to sessionStorage; state is forked when a new tab is
274 // opened from an existing tab.
275 return NS_ERROR_NOT_IMPLEMENTED;
278 NS_IMETHODIMP
279 LocalStorageManager2::CheckStorage(nsIPrincipal* aPrincipal, Storage* aStorage,
280 bool* _retval) {
281 MOZ_ASSERT(NS_IsMainThread());
282 MOZ_ASSERT(aPrincipal);
283 MOZ_ASSERT(aStorage);
284 MOZ_ASSERT(_retval);
286 // Only used by sessionStorage.
287 return NS_ERROR_NOT_IMPLEMENTED;
290 NS_IMETHODIMP
291 LocalStorageManager2::GetNextGenLocalStorageEnabled(bool* aResult) {
292 MOZ_ASSERT(NS_IsMainThread());
293 MOZ_ASSERT(aResult);
295 *aResult = NextGenLocalStorageEnabled();
296 return NS_OK;
299 NS_IMETHODIMP
300 LocalStorageManager2::Preload(nsIPrincipal* aPrincipal, JSContext* aContext,
301 Promise** _retval) {
302 MOZ_ASSERT(NS_IsMainThread());
303 MOZ_ASSERT(aPrincipal);
304 MOZ_ASSERT(_retval);
306 nsCString originAttrSuffix;
307 nsCString originKey;
308 nsresult rv = aPrincipal->GetStorageOriginKey(originKey);
309 aPrincipal->OriginAttributesRef().CreateSuffix(originAttrSuffix);
310 if (NS_FAILED(rv)) {
311 return NS_ERROR_NOT_AVAILABLE;
314 mozilla::ipc::PrincipalInfo principalInfo;
315 rv = CheckedPrincipalToPrincipalInfo(aPrincipal, principalInfo);
316 if (NS_WARN_IF(NS_FAILED(rv))) {
317 return rv;
320 RefPtr<Promise> promise;
322 if (aContext) {
323 rv = CreatePromise(aContext, getter_AddRefs(promise));
324 if (NS_WARN_IF(NS_FAILED(rv))) {
325 return rv;
329 LSRequestCommonParams commonParams;
330 commonParams.principalInfo() = principalInfo;
331 commonParams.storagePrincipalInfo() = principalInfo;
332 commonParams.originKey() = originKey;
334 LSRequestPreloadDatastoreParams params(commonParams);
336 RefPtr<AsyncRequestHelper> helper =
337 new AsyncRequestHelper(this, promise, params);
339 // This will start and finish the async request on the RemoteLazyInputStream
340 // thread.
341 // This must be done on RemoteLazyInputStream Thread because it's very likely
342 // that a content process will issue a prepare datastore request for the same
343 // principal while blocking the content process on the main thread.
344 // There would be a potential for deadlock if the preloading was initialized
345 // from the main thread of the parent process and a11y issued a synchronous
346 // message from the parent process to the content process (approximately at
347 // the same time) because the preload request wouldn't be able to respond
348 // to the Ready message by sending the Finish message which is needed to
349 // finish the preload request and unblock the prepare datastore request.
350 rv = helper->Dispatch();
351 if (NS_WARN_IF(NS_FAILED(rv))) {
352 return rv;
355 promise.forget(_retval);
356 return NS_OK;
359 NS_IMETHODIMP
360 LocalStorageManager2::IsPreloaded(nsIPrincipal* aPrincipal, JSContext* aContext,
361 Promise** _retval) {
362 MOZ_ASSERT(NS_IsMainThread());
363 MOZ_ASSERT(aPrincipal);
364 MOZ_ASSERT(_retval);
366 RefPtr<Promise> promise;
367 nsresult rv = CreatePromise(aContext, getter_AddRefs(promise));
368 if (NS_WARN_IF(NS_FAILED(rv))) {
369 return rv;
372 LSSimpleRequestPreloadedParams params;
374 rv = CheckedPrincipalToPrincipalInfo(aPrincipal, params.principalInfo());
375 if (NS_WARN_IF(NS_FAILED(rv))) {
376 return rv;
379 params.storagePrincipalInfo() = params.principalInfo();
381 rv = StartSimpleRequest(promise, params);
382 if (NS_WARN_IF(NS_FAILED(rv))) {
383 return rv;
386 promise.forget(_retval);
387 return NS_OK;
390 NS_IMETHODIMP
391 LocalStorageManager2::GetState(nsIPrincipal* aPrincipal, JSContext* aContext,
392 Promise** _retval) {
393 MOZ_ASSERT(NS_IsMainThread());
394 MOZ_ASSERT(aPrincipal);
395 MOZ_ASSERT(_retval);
397 RefPtr<Promise> promise;
398 nsresult rv = CreatePromise(aContext, getter_AddRefs(promise));
399 if (NS_WARN_IF(NS_FAILED(rv))) {
400 return rv;
403 LSSimpleRequestGetStateParams params;
405 rv = CheckedPrincipalToPrincipalInfo(aPrincipal, params.principalInfo());
406 if (NS_WARN_IF(NS_FAILED(rv))) {
407 return rv;
410 params.storagePrincipalInfo() = params.principalInfo();
412 rv = StartSimpleRequest(promise, params);
413 if (NS_WARN_IF(NS_FAILED(rv))) {
414 return rv;
417 promise.forget(_retval);
418 return NS_OK;
421 LSRequestChild* LocalStorageManager2::StartRequest(
422 const LSRequestParams& aParams, LSRequestChildCallback* aCallback) {
423 AssertIsOnDOMFileThread();
425 mozilla::ipc::PBackgroundChild* backgroundActor =
426 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
427 if (NS_WARN_IF(!backgroundActor)) {
428 return nullptr;
431 auto actor = new LSRequestChild();
433 if (!backgroundActor->SendPBackgroundLSRequestConstructor(actor, aParams)) {
434 return nullptr;
437 // Must set callback after calling SendPBackgroundLSRequestConstructor since
438 // it can be called synchronously when SendPBackgroundLSRequestConstructor
439 // fails.
440 actor->SetCallback(aCallback);
442 return actor;
445 nsresult LocalStorageManager2::StartSimpleRequest(
446 Promise* aPromise, const LSSimpleRequestParams& aParams) {
447 MOZ_ASSERT(NS_IsMainThread());
448 MOZ_ASSERT(aPromise);
450 mozilla::ipc::PBackgroundChild* backgroundActor =
451 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
452 if (NS_WARN_IF(!backgroundActor)) {
453 return NS_ERROR_FAILURE;
456 auto actor = new LSSimpleRequestChild();
458 if (!backgroundActor->SendPBackgroundLSSimpleRequestConstructor(actor,
459 aParams)) {
460 return NS_ERROR_FAILURE;
463 RefPtr<SimpleRequestResolver> resolver = new SimpleRequestResolver(aPromise);
465 // Must set callback after calling SendPBackgroundLSRequestConstructor since
466 // it can be called synchronously when SendPBackgroundLSRequestConstructor
467 // fails.
468 actor->SetCallback(resolver);
470 return NS_OK;
473 nsresult AsyncRequestHelper::Dispatch() {
474 AssertIsOnOwningThread();
476 nsCOMPtr<nsIEventTarget> domFileThread =
477 RemoteLazyInputStreamThread::GetOrCreate();
478 if (NS_WARN_IF(!domFileThread)) {
479 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
482 nsresult rv = domFileThread->Dispatch(this, NS_DISPATCH_NORMAL);
483 if (NS_WARN_IF(NS_FAILED(rv))) {
484 return rv;
487 return NS_OK;
490 nsresult AsyncRequestHelper::Start() {
491 AssertIsOnDOMFileThread();
492 MOZ_ASSERT(mState == State::Initial);
494 mState = State::ResponsePending;
496 LSRequestChild* actor = mManager->StartRequest(mParams, this);
497 if (NS_WARN_IF(!actor)) {
498 return NS_ERROR_FAILURE;
501 mActor = actor;
503 return NS_OK;
506 void AsyncRequestHelper::Finish() {
507 AssertIsOnOwningThread();
508 MOZ_ASSERT(mState == State::Finishing);
510 if (NS_WARN_IF(NS_FAILED(mResultCode))) {
511 if (mPromise) {
512 mPromise->MaybeReject(mResultCode);
514 } else {
515 switch (mResponse.type()) {
516 case LSRequestResponse::Tnsresult:
517 if (mPromise) {
518 mPromise->MaybeReject(mResponse.get_nsresult());
520 break;
522 case LSRequestResponse::TLSRequestPreloadDatastoreResponse:
523 if (mPromise) {
524 mPromise->MaybeResolveWithUndefined();
526 break;
527 default:
528 MOZ_CRASH("Unknown response type!");
532 mManager = nullptr;
534 mState = State::Complete;
537 NS_IMPL_ISUPPORTS_INHERITED0(AsyncRequestHelper, Runnable)
539 NS_IMETHODIMP
540 AsyncRequestHelper::Run() {
541 nsresult rv;
543 switch (mState) {
544 case State::Initial:
545 rv = Start();
546 break;
548 case State::Finishing:
549 Finish();
550 return NS_OK;
552 default:
553 MOZ_CRASH("Bad state!");
556 if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::Finishing) {
557 if (NS_SUCCEEDED(mResultCode)) {
558 mResultCode = rv;
561 mState = State::Finishing;
563 if (IsOnOwningThread()) {
564 Finish();
565 } else {
566 MOZ_ALWAYS_SUCCEEDS(
567 mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
571 return NS_OK;
574 void AsyncRequestHelper::OnResponse(const LSRequestResponse& aResponse) {
575 AssertIsOnDOMFileThread();
576 MOZ_ASSERT(mState == State::ResponsePending);
578 mActor = nullptr;
580 mResponse = aResponse;
582 mState = State::Finishing;
584 MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
587 void SimpleRequestResolver::HandleResponse(nsresult aResponse) {
588 MOZ_ASSERT(NS_IsMainThread());
589 MOZ_ASSERT(mPromise);
591 mPromise->MaybeReject(aResponse);
594 void SimpleRequestResolver::HandleResponse(bool aResponse) {
595 MOZ_ASSERT(NS_IsMainThread());
596 MOZ_ASSERT(mPromise);
598 mPromise->MaybeResolve(aResponse);
601 [[nodiscard]] static bool ToJSValue(JSContext* aCx,
602 const nsTArray<LSItemInfo>& aArgument,
603 JS::MutableHandle<JS::Value> aValue) {
604 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
605 if (!obj) {
606 return false;
609 for (size_t i = 0; i < aArgument.Length(); ++i) {
610 const LSItemInfo& itemInfo = aArgument[i];
612 const nsString& key = itemInfo.key();
614 JS::Rooted<JS::Value> value(aCx);
615 if (!ToJSValue(aCx, itemInfo.value().AsString(), &value)) {
616 return false;
619 if (!JS_DefineUCProperty(aCx, obj, key.BeginReading(), key.Length(), value,
620 JSPROP_ENUMERATE)) {
621 return false;
625 aValue.setObject(*obj);
626 return true;
629 void SimpleRequestResolver::HandleResponse(
630 const nsTArray<LSItemInfo>& aResponse) {
631 MOZ_ASSERT(NS_IsMainThread());
632 MOZ_ASSERT(mPromise);
634 mPromise->MaybeResolve(aResponse);
637 void SimpleRequestResolver::OnResponse(
638 const LSSimpleRequestResponse& aResponse) {
639 MOZ_ASSERT(NS_IsMainThread());
641 switch (aResponse.type()) {
642 case LSSimpleRequestResponse::Tnsresult:
643 HandleResponse(aResponse.get_nsresult());
644 break;
646 case LSSimpleRequestResponse::TLSSimpleRequestPreloadedResponse:
647 HandleResponse(
648 aResponse.get_LSSimpleRequestPreloadedResponse().preloaded());
649 break;
651 case LSSimpleRequestResponse::TLSSimpleRequestGetStateResponse:
652 HandleResponse(
653 aResponse.get_LSSimpleRequestGetStateResponse().itemInfos());
654 break;
656 default:
657 MOZ_CRASH("Unknown response type!");
661 } // namespace mozilla::dom