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"
10 #include "ActorsChild.h"
15 #include "MainThreadUtils.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"
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"
44 #include "xpcpublic.h"
46 namespace mozilla::dom
{
50 class AsyncRequestHelper final
: public Runnable
,
51 public LSRequestChildCallback
{
54 * The AsyncRequestHelper has been created and dispatched to the
55 * RemoteLazyInputStream Thread.
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.
66 * A response has been received and AsyncRequestHelper has been dispatched
67 * back to the owning event target to call Finish().
71 * Finish() has been called on the main thread. The promise will be resolved
72 * according to the received response.
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
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
;
95 AsyncRequestHelper(LocalStorageManager2
* aManager
, Promise
* aPromise
,
96 const LSRequestParams
& aParams
)
97 : Runnable("dom::LocalStorageManager2::AsyncRequestHelper"),
99 mOwningEventTarget(GetCurrentEventTarget()),
104 mState(State::Initial
) {}
106 bool IsOnOwningThread() const {
107 MOZ_ASSERT(mOwningEventTarget
);
110 return NS_SUCCEEDED(mOwningEventTarget
->IsOnCurrentThread(¤t
)) &&
114 void AssertIsOnOwningThread() const {
115 MOZ_ASSERT(NS_IsMainThread());
116 MOZ_ASSERT(IsOnOwningThread());
122 ~AsyncRequestHelper() = default;
128 NS_DECL_ISUPPORTS_INHERITED
132 // LSRequestChildCallback
133 void OnResponse(const LSRequestResponse
& aResponse
) override
;
136 class SimpleRequestResolver final
: public LSSimpleRequestChildCallback
{
137 RefPtr
<Promise
> mPromise
;
140 explicit SimpleRequestResolver(Promise
* aPromise
) : mPromise(aPromise
) {}
142 NS_INLINE_DECL_REFCOUNTING(SimpleRequestResolver
, override
);
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
;
168 RefPtr
<Promise
> promise
= Promise::Create(global
, result
);
169 if (result
.Failed()) {
170 return result
.StealNSResult();
173 promise
.forget(aPromise
);
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
))) {
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
;
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
)
214 LocalStorageManager2::PrecacheStorage(nsIPrincipal
* aPrincipal
,
215 nsIPrincipal
* aStoragePrincipal
,
217 MOZ_ASSERT(NS_IsMainThread());
218 MOZ_ASSERT(aPrincipal
);
219 MOZ_ASSERT(aStoragePrincipal
);
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::AboutToLoadHttpFtpDocumentForChild.
227 return NS_ERROR_NOT_IMPLEMENTED
;
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
);
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
))) {
251 object
.forget(_retval
);
256 LocalStorageManager2::GetStorage(mozIDOMWindow
* aWindow
,
257 nsIPrincipal
* aPrincipal
,
258 nsIPrincipal
* aStoragePrincipal
, bool aPrivate
,
260 MOZ_ASSERT(NS_IsMainThread());
261 MOZ_ASSERT(aPrincipal
);
262 MOZ_ASSERT(aStoragePrincipal
);
265 return NS_ERROR_NOT_IMPLEMENTED
;
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
;
279 LocalStorageManager2::CheckStorage(nsIPrincipal
* aPrincipal
, Storage
* aStorage
,
281 MOZ_ASSERT(NS_IsMainThread());
282 MOZ_ASSERT(aPrincipal
);
283 MOZ_ASSERT(aStorage
);
286 // Only used by sessionStorage.
287 return NS_ERROR_NOT_IMPLEMENTED
;
291 LocalStorageManager2::GetNextGenLocalStorageEnabled(bool* aResult
) {
292 MOZ_ASSERT(NS_IsMainThread());
295 *aResult
= NextGenLocalStorageEnabled();
300 LocalStorageManager2::Preload(nsIPrincipal
* aPrincipal
, JSContext
* aContext
,
302 MOZ_ASSERT(NS_IsMainThread());
303 MOZ_ASSERT(aPrincipal
);
306 nsCString originAttrSuffix
;
308 nsresult rv
= aPrincipal
->GetStorageOriginKey(originKey
);
309 aPrincipal
->OriginAttributesRef().CreateSuffix(originAttrSuffix
);
311 return NS_ERROR_NOT_AVAILABLE
;
314 mozilla::ipc::PrincipalInfo principalInfo
;
315 rv
= CheckedPrincipalToPrincipalInfo(aPrincipal
, principalInfo
);
316 if (NS_WARN_IF(NS_FAILED(rv
))) {
320 RefPtr
<Promise
> promise
;
323 rv
= CreatePromise(aContext
, getter_AddRefs(promise
));
324 if (NS_WARN_IF(NS_FAILED(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
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
))) {
355 promise
.forget(_retval
);
360 LocalStorageManager2::IsPreloaded(nsIPrincipal
* aPrincipal
, JSContext
* aContext
,
362 MOZ_ASSERT(NS_IsMainThread());
363 MOZ_ASSERT(aPrincipal
);
366 RefPtr
<Promise
> promise
;
367 nsresult rv
= CreatePromise(aContext
, getter_AddRefs(promise
));
368 if (NS_WARN_IF(NS_FAILED(rv
))) {
372 LSSimpleRequestPreloadedParams params
;
374 rv
= CheckedPrincipalToPrincipalInfo(aPrincipal
, params
.principalInfo());
375 if (NS_WARN_IF(NS_FAILED(rv
))) {
379 params
.storagePrincipalInfo() = params
.principalInfo();
381 rv
= StartSimpleRequest(promise
, params
);
382 if (NS_WARN_IF(NS_FAILED(rv
))) {
386 promise
.forget(_retval
);
391 LocalStorageManager2::GetState(nsIPrincipal
* aPrincipal
, JSContext
* aContext
,
393 MOZ_ASSERT(NS_IsMainThread());
394 MOZ_ASSERT(aPrincipal
);
397 RefPtr
<Promise
> promise
;
398 nsresult rv
= CreatePromise(aContext
, getter_AddRefs(promise
));
399 if (NS_WARN_IF(NS_FAILED(rv
))) {
403 LSSimpleRequestGetStateParams params
;
405 rv
= CheckedPrincipalToPrincipalInfo(aPrincipal
, params
.principalInfo());
406 if (NS_WARN_IF(NS_FAILED(rv
))) {
410 params
.storagePrincipalInfo() = params
.principalInfo();
412 rv
= StartSimpleRequest(promise
, params
);
413 if (NS_WARN_IF(NS_FAILED(rv
))) {
417 promise
.forget(_retval
);
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
)) {
431 auto actor
= new LSRequestChild();
433 if (!backgroundActor
->SendPBackgroundLSRequestConstructor(actor
, aParams
)) {
437 // Must set callback after calling SendPBackgroundLSRequestConstructor since
438 // it can be called synchronously when SendPBackgroundLSRequestConstructor
440 actor
->SetCallback(aCallback
);
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
,
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
468 actor
->SetCallback(resolver
);
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
))) {
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
;
506 void AsyncRequestHelper::Finish() {
507 AssertIsOnOwningThread();
508 MOZ_ASSERT(mState
== State::Finishing
);
510 if (NS_WARN_IF(NS_FAILED(mResultCode
))) {
512 mPromise
->MaybeReject(mResultCode
);
515 switch (mResponse
.type()) {
516 case LSRequestResponse::Tnsresult
:
518 mPromise
->MaybeReject(mResponse
.get_nsresult());
522 case LSRequestResponse::TLSRequestPreloadDatastoreResponse
:
524 mPromise
->MaybeResolveWithUndefined();
528 MOZ_CRASH("Unknown response type!");
534 mState
= State::Complete
;
537 NS_IMPL_ISUPPORTS_INHERITED0(AsyncRequestHelper
, Runnable
)
540 AsyncRequestHelper::Run() {
548 case State::Finishing
:
553 MOZ_CRASH("Bad state!");
556 if (NS_WARN_IF(NS_FAILED(rv
)) && mState
!= State::Finishing
) {
557 if (NS_SUCCEEDED(mResultCode
)) {
561 mState
= State::Finishing
;
563 if (IsOnOwningThread()) {
567 mOwningEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
));
574 void AsyncRequestHelper::OnResponse(const LSRequestResponse
& aResponse
) {
575 AssertIsOnDOMFileThread();
576 MOZ_ASSERT(mState
== State::ResponsePending
);
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
));
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
)) {
619 if (!JS_DefineUCProperty(aCx
, obj
, key
.BeginReading(), key
.Length(), value
,
625 aValue
.setObject(*obj
);
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());
646 case LSSimpleRequestResponse::TLSSimpleRequestPreloadedResponse
:
648 aResponse
.get_LSSimpleRequestPreloadedResponse().preloaded());
651 case LSSimpleRequestResponse::TLSSimpleRequestGetStateResponse
:
653 aResponse
.get_LSSimpleRequestGetStateResponse().itemInfos());
657 MOZ_CRASH("Unknown response type!");
661 } // namespace mozilla::dom