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 "LocalStorageManager2.h"
8 
9 #include "LSObject.h"
10 #include "mozilla/dom/Promise.h"
11 #include "mozilla/UniquePtr.h"
12 
13 namespace mozilla {
14 namespace dom {
15 
16 namespace {
17 
18 class AsyncRequestHelper final : public Runnable,
19                                  public LSRequestChildCallback {
20   enum class State {
21     /**
22      * The AsyncRequestHelper has been created and dispatched to the DOM File
23      * Thread.
24      */
25     Initial,
26     /**
27      * Start() has been invoked on the DOM File Thread and
28      * LocalStorageManager2::StartRequest has been invoked from there, sending
29      * an IPC message to PBackground to service the request.  We stay in this
30      * state until a response is received.
31      */
32     ResponsePending,
33     /**
34      * A response has been received and AsyncRequestHelper has been dispatched
35      * back to the owning event target to call Finish().
36      */
37     Finishing,
38     /**
39      * Finish() has been called on the main thread. The promise will be resolved
40      * according to the received response.
41      */
42     Complete
43   };
44 
45   // The object we are issuing a request on behalf of.  Present because of the
46   // need to invoke LocalStorageManager2::StartRequest off the main thread.
47   // Dropped on return to the main-thread in Finish().
48   RefPtr<LocalStorageManager2> mManager;
49   // The thread the AsyncRequestHelper was created on.  This should be the main
50   // thread.
51   nsCOMPtr<nsIEventTarget> mOwningEventTarget;
52   // The IPC actor handling the request with standard IPC allocation rules.
53   // Our reference is nulled in OnResponse which corresponds to the actor's
54   // __destroy__ method.
55   LSRequestChild* mActor;
56   RefPtr<Promise> mPromise;
57   const LSRequestParams mParams;
58   LSRequestResponse mResponse;
59   nsresult mResultCode;
60   State mState;
61 
62  public:
AsyncRequestHelper(LocalStorageManager2 * aManager,Promise * aPromise,const LSRequestParams & aParams)63   AsyncRequestHelper(LocalStorageManager2* aManager, Promise* aPromise,
64                      const LSRequestParams& aParams)
65       : Runnable("dom::LocalStorageManager2::AsyncRequestHelper"),
66         mManager(aManager),
67         mOwningEventTarget(GetCurrentThreadEventTarget()),
68         mActor(nullptr),
69         mPromise(aPromise),
70         mParams(aParams),
71         mResultCode(NS_OK),
72         mState(State::Initial) {}
73 
IsOnOwningThread() const74   bool IsOnOwningThread() const {
75     MOZ_ASSERT(mOwningEventTarget);
76 
77     bool current;
78     return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) &&
79            current;
80   }
81 
AssertIsOnOwningThread() const82   void AssertIsOnOwningThread() const {
83     MOZ_ASSERT(NS_IsMainThread());
84     MOZ_ASSERT(IsOnOwningThread());
85   }
86 
87   nsresult Dispatch();
88 
89  private:
90   ~AsyncRequestHelper() = default;
91 
92   nsresult Start();
93 
94   void Finish();
95 
96   NS_DECL_ISUPPORTS_INHERITED
97 
98   NS_DECL_NSIRUNNABLE
99 
100   // LSRequestChildCallback
101   void OnResponse(const LSRequestResponse& aResponse) override;
102 };
103 
104 class SimpleRequestResolver final : public LSSimpleRequestChildCallback {
105   RefPtr<Promise> mPromise;
106 
107  public:
SimpleRequestResolver(Promise * aPromise)108   explicit SimpleRequestResolver(Promise* aPromise) : mPromise(aPromise) {}
109 
110   NS_INLINE_DECL_REFCOUNTING(SimpleRequestResolver, override);
111 
112  private:
113   ~SimpleRequestResolver() = default;
114 
115   void HandleResponse(nsresult aResponse);
116 
117   void HandleResponse(bool aResponse);
118 
119   // LSRequestChildCallback
120   void OnResponse(const LSSimpleRequestResponse& aResponse) override;
121 };
122 
CreatePromise(JSContext * aContext,Promise ** aPromise)123 nsresult CreatePromise(JSContext* aContext, Promise** aPromise) {
124   MOZ_ASSERT(NS_IsMainThread());
125   MOZ_ASSERT(aContext);
126 
127   nsIGlobalObject* global =
128       xpc::NativeGlobal(JS::CurrentGlobalOrNull(aContext));
129   if (NS_WARN_IF(!global)) {
130     return NS_ERROR_FAILURE;
131   }
132 
133   ErrorResult result;
134   RefPtr<Promise> promise = Promise::Create(global, result);
135   if (result.Failed()) {
136     return result.StealNSResult();
137   }
138 
139   promise.forget(aPromise);
140   return NS_OK;
141 }
142 
CheckedPrincipalToPrincipalInfo(nsIPrincipal * aPrincipal,PrincipalInfo & aPrincipalInfo)143 nsresult CheckedPrincipalToPrincipalInfo(nsIPrincipal* aPrincipal,
144                                          PrincipalInfo& aPrincipalInfo) {
145   MOZ_ASSERT(NS_IsMainThread());
146   MOZ_ASSERT(aPrincipal);
147 
148   nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &aPrincipalInfo);
149   if (NS_WARN_IF(NS_FAILED(rv))) {
150     return rv;
151   }
152 
153   if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) {
154     return NS_ERROR_FAILURE;
155   }
156 
157   if (aPrincipalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
158       aPrincipalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
159     return NS_ERROR_UNEXPECTED;
160   }
161 
162   return NS_OK;
163 }
164 
165 }  // namespace
166 
LocalStorageManager2()167 LocalStorageManager2::LocalStorageManager2() {
168   MOZ_ASSERT(NS_IsMainThread());
169   MOZ_ASSERT(NextGenLocalStorageEnabled());
170 }
171 
~LocalStorageManager2()172 LocalStorageManager2::~LocalStorageManager2() { MOZ_ASSERT(NS_IsMainThread()); }
173 
NS_IMPL_ISUPPORTS(LocalStorageManager2,nsIDOMStorageManager,nsILocalStorageManager)174 NS_IMPL_ISUPPORTS(LocalStorageManager2, nsIDOMStorageManager,
175                   nsILocalStorageManager)
176 
177 NS_IMETHODIMP
178 LocalStorageManager2::PrecacheStorage(nsIPrincipal* aPrincipal,
179                                       nsIPrincipal* aStoragePrincipal,
180                                       Storage** _retval) {
181   MOZ_ASSERT(NS_IsMainThread());
182   MOZ_ASSERT(aPrincipal);
183   MOZ_ASSERT(aStoragePrincipal);
184   MOZ_ASSERT(_retval);
185 
186   // This method was created as part of the e10s-ification of the old LS
187   // implementation to perform a preload in the content/current process.  That's
188   // not how things work in LSNG.  Instead everything happens in the parent
189   // process, triggered by the official preloading spot,
190   // ContentParent::AboutToLoadHttpFtpDocumentForChild.
191   return NS_ERROR_NOT_IMPLEMENTED;
192 }
193 
194 NS_IMETHODIMP
CreateStorage(mozIDOMWindow * aWindow,nsIPrincipal * aPrincipal,nsIPrincipal * aStoragePrincipal,const nsAString & aDocumentURI,bool aPrivate,Storage ** _retval)195 LocalStorageManager2::CreateStorage(mozIDOMWindow* aWindow,
196                                     nsIPrincipal* aPrincipal,
197                                     nsIPrincipal* aStoragePrincipal,
198                                     const nsAString& aDocumentURI,
199                                     bool aPrivate, Storage** _retval) {
200   MOZ_ASSERT(NS_IsMainThread());
201   MOZ_ASSERT(aPrincipal);
202   MOZ_ASSERT(aStoragePrincipal);
203   MOZ_ASSERT(_retval);
204 
205   nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow);
206 
207   RefPtr<LSObject> object;
208   nsresult rv = LSObject::CreateForPrincipal(inner, aPrincipal,
209                                              aStoragePrincipal, aDocumentURI,
210                                              aPrivate, getter_AddRefs(object));
211   if (NS_WARN_IF(NS_FAILED(rv))) {
212     return rv;
213   }
214 
215   object.forget(_retval);
216   return NS_OK;
217 }
218 
219 NS_IMETHODIMP
GetStorage(mozIDOMWindow * aWindow,nsIPrincipal * aPrincipal,nsIPrincipal * aStoragePrincipal,bool aPrivate,Storage ** _retval)220 LocalStorageManager2::GetStorage(mozIDOMWindow* aWindow,
221                                  nsIPrincipal* aPrincipal,
222                                  nsIPrincipal* aStoragePrincipal, bool aPrivate,
223                                  Storage** _retval) {
224   MOZ_ASSERT(NS_IsMainThread());
225   MOZ_ASSERT(aPrincipal);
226   MOZ_ASSERT(aStoragePrincipal);
227   MOZ_ASSERT(_retval);
228 
229   return NS_ERROR_NOT_IMPLEMENTED;
230 }
231 
232 NS_IMETHODIMP
CloneStorage(Storage * aStorageToCloneFrom)233 LocalStorageManager2::CloneStorage(Storage* aStorageToCloneFrom) {
234   MOZ_ASSERT(NS_IsMainThread());
235   MOZ_ASSERT(aStorageToCloneFrom);
236 
237   // Cloning is specific to sessionStorage; state is forked when a new tab is
238   // opened from an existing tab.
239   return NS_ERROR_NOT_IMPLEMENTED;
240 }
241 
242 NS_IMETHODIMP
CheckStorage(nsIPrincipal * aPrincipal,Storage * aStorage,bool * _retval)243 LocalStorageManager2::CheckStorage(nsIPrincipal* aPrincipal, Storage* aStorage,
244                                    bool* _retval) {
245   MOZ_ASSERT(NS_IsMainThread());
246   MOZ_ASSERT(aPrincipal);
247   MOZ_ASSERT(aStorage);
248   MOZ_ASSERT(_retval);
249 
250   // Only used by sessionStorage.
251   return NS_ERROR_NOT_IMPLEMENTED;
252 }
253 
254 NS_IMETHODIMP
GetNextGenLocalStorageEnabled(bool * aResult)255 LocalStorageManager2::GetNextGenLocalStorageEnabled(bool* aResult) {
256   MOZ_ASSERT(NS_IsMainThread());
257   MOZ_ASSERT(aResult);
258 
259   *aResult = NextGenLocalStorageEnabled();
260   return NS_OK;
261 }
262 
263 NS_IMETHODIMP
Preload(nsIPrincipal * aPrincipal,JSContext * aContext,nsISupports ** _retval)264 LocalStorageManager2::Preload(nsIPrincipal* aPrincipal, JSContext* aContext,
265                               nsISupports** _retval) {
266   MOZ_ASSERT(NS_IsMainThread());
267   MOZ_ASSERT(aPrincipal);
268   MOZ_ASSERT(_retval);
269 
270   nsCString originAttrSuffix;
271   nsCString originKey;
272   nsresult rv = aPrincipal->GetStorageOriginKey(originKey);
273   aPrincipal->OriginAttributesRef().CreateSuffix(originAttrSuffix);
274   if (NS_FAILED(rv)) {
275     return NS_ERROR_NOT_AVAILABLE;
276   }
277 
278   PrincipalInfo principalInfo;
279   rv = CheckedPrincipalToPrincipalInfo(aPrincipal, principalInfo);
280   if (NS_WARN_IF(NS_FAILED(rv))) {
281     return rv;
282   }
283 
284   RefPtr<Promise> promise;
285 
286   if (aContext) {
287     rv = CreatePromise(aContext, getter_AddRefs(promise));
288     if (NS_WARN_IF(NS_FAILED(rv))) {
289       return rv;
290     }
291   }
292 
293   LSRequestCommonParams commonParams;
294   commonParams.principalInfo() = principalInfo;
295   commonParams.storagePrincipalInfo() = principalInfo;
296   commonParams.originKey() = originKey;
297 
298   LSRequestPreloadDatastoreParams params(commonParams);
299 
300   RefPtr<AsyncRequestHelper> helper =
301       new AsyncRequestHelper(this, promise, params);
302 
303   // This will start and finish the async request on the DOM File thread.
304   // This must be done on DOM File Thread because it's very likely that a
305   // content process will issue a prepare datastore request for the same
306   // principal while blocking the content process on the main thread.
307   // There would be a potential for deadlock if the preloading was initialized
308   // from the main thread of the parent process and a11y issued a synchronous
309   // message from the parent process to the content process (approximately at
310   // the same time) because the preload request wouldn't be able to respond
311   // to the Ready message by sending the Finish message which is needed to
312   // finish the preload request and unblock the prepare datastore request.
313   rv = helper->Dispatch();
314   if (NS_WARN_IF(NS_FAILED(rv))) {
315     return rv;
316   }
317 
318   promise.forget(_retval);
319   return NS_OK;
320 }
321 
322 NS_IMETHODIMP
IsPreloaded(nsIPrincipal * aPrincipal,JSContext * aContext,nsISupports ** _retval)323 LocalStorageManager2::IsPreloaded(nsIPrincipal* aPrincipal, JSContext* aContext,
324                                   nsISupports** _retval) {
325   MOZ_ASSERT(NS_IsMainThread());
326   MOZ_ASSERT(aPrincipal);
327   MOZ_ASSERT(_retval);
328 
329   RefPtr<Promise> promise;
330   nsresult rv = CreatePromise(aContext, getter_AddRefs(promise));
331   if (NS_WARN_IF(NS_FAILED(rv))) {
332     return rv;
333   }
334 
335   LSSimpleRequestPreloadedParams params;
336 
337   rv = CheckedPrincipalToPrincipalInfo(aPrincipal, params.principalInfo());
338   if (NS_WARN_IF(NS_FAILED(rv))) {
339     return rv;
340   }
341 
342   params.storagePrincipalInfo() = params.principalInfo();
343 
344   rv = StartSimpleRequest(promise, params);
345   if (NS_WARN_IF(NS_FAILED(rv))) {
346     return rv;
347   }
348 
349   promise.forget(_retval);
350   return NS_OK;
351 }
352 
StartRequest(const LSRequestParams & aParams,LSRequestChildCallback * aCallback)353 LSRequestChild* LocalStorageManager2::StartRequest(
354     const LSRequestParams& aParams, LSRequestChildCallback* aCallback) {
355   AssertIsOnDOMFileThread();
356 
357   PBackgroundChild* backgroundActor =
358       BackgroundChild::GetOrCreateForCurrentThread();
359   if (NS_WARN_IF(!backgroundActor)) {
360     return nullptr;
361   }
362 
363   auto actor = new LSRequestChild();
364 
365   if (!backgroundActor->SendPBackgroundLSRequestConstructor(actor, aParams)) {
366     return nullptr;
367   }
368 
369   // Must set callback after calling SendPBackgroundLSRequestConstructor since
370   // it can be called synchronously when SendPBackgroundLSRequestConstructor
371   // fails.
372   actor->SetCallback(aCallback);
373 
374   return actor;
375 }
376 
StartSimpleRequest(Promise * aPromise,const LSSimpleRequestParams & aParams)377 nsresult LocalStorageManager2::StartSimpleRequest(
378     Promise* aPromise, const LSSimpleRequestParams& aParams) {
379   MOZ_ASSERT(NS_IsMainThread());
380   MOZ_ASSERT(aPromise);
381 
382   PBackgroundChild* backgroundActor =
383       BackgroundChild::GetOrCreateForCurrentThread();
384   if (NS_WARN_IF(!backgroundActor)) {
385     return NS_ERROR_FAILURE;
386   }
387 
388   auto actor = new LSSimpleRequestChild();
389 
390   if (!backgroundActor->SendPBackgroundLSSimpleRequestConstructor(actor,
391                                                                   aParams)) {
392     return NS_ERROR_FAILURE;
393   }
394 
395   RefPtr<SimpleRequestResolver> resolver = new SimpleRequestResolver(aPromise);
396 
397   // Must set callback after calling SendPBackgroundLSRequestConstructor since
398   // it can be called synchronously when SendPBackgroundLSRequestConstructor
399   // fails.
400   actor->SetCallback(resolver);
401 
402   return NS_OK;
403 }
404 
Dispatch()405 nsresult AsyncRequestHelper::Dispatch() {
406   AssertIsOnOwningThread();
407 
408   nsCOMPtr<nsIEventTarget> domFileThread =
409       IPCBlobInputStreamThread::GetOrCreate();
410   if (NS_WARN_IF(!domFileThread)) {
411     return NS_ERROR_FAILURE;
412   }
413 
414   nsresult rv = domFileThread->Dispatch(this, NS_DISPATCH_NORMAL);
415   if (NS_WARN_IF(NS_FAILED(rv))) {
416     return rv;
417   }
418 
419   return NS_OK;
420 }
421 
Start()422 nsresult AsyncRequestHelper::Start() {
423   AssertIsOnDOMFileThread();
424   MOZ_ASSERT(mState == State::Initial);
425 
426   mState = State::ResponsePending;
427 
428   LSRequestChild* actor = mManager->StartRequest(mParams, this);
429   if (NS_WARN_IF(!actor)) {
430     return NS_ERROR_FAILURE;
431   }
432 
433   mActor = actor;
434 
435   return NS_OK;
436 }
437 
Finish()438 void AsyncRequestHelper::Finish() {
439   AssertIsOnOwningThread();
440   MOZ_ASSERT(mState == State::Finishing);
441 
442   if (NS_WARN_IF(NS_FAILED(mResultCode))) {
443     if (mPromise) {
444       mPromise->MaybeReject(mResultCode);
445     }
446   } else {
447     switch (mResponse.type()) {
448       case LSRequestResponse::Tnsresult:
449         if (mPromise) {
450           mPromise->MaybeReject(mResponse.get_nsresult());
451         }
452         break;
453 
454       case LSRequestResponse::TLSRequestPreloadDatastoreResponse:
455         if (mPromise) {
456           mPromise->MaybeResolveWithUndefined();
457         }
458         break;
459       default:
460         MOZ_CRASH("Unknown response type!");
461     }
462   }
463 
464   mManager = nullptr;
465 
466   mState = State::Complete;
467 }
468 
NS_IMPL_ISUPPORTS_INHERITED0(AsyncRequestHelper,Runnable)469 NS_IMPL_ISUPPORTS_INHERITED0(AsyncRequestHelper, Runnable)
470 
471 NS_IMETHODIMP
472 AsyncRequestHelper::Run() {
473   nsresult rv;
474 
475   switch (mState) {
476     case State::Initial:
477       rv = Start();
478       break;
479 
480     case State::Finishing:
481       Finish();
482       return NS_OK;
483 
484     default:
485       MOZ_CRASH("Bad state!");
486   }
487 
488   if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::Finishing) {
489     if (NS_SUCCEEDED(mResultCode)) {
490       mResultCode = rv;
491     }
492 
493     mState = State::Finishing;
494 
495     if (IsOnOwningThread()) {
496       Finish();
497     } else {
498       MOZ_ALWAYS_SUCCEEDS(
499           mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
500     }
501   }
502 
503   return NS_OK;
504 }
505 
OnResponse(const LSRequestResponse & aResponse)506 void AsyncRequestHelper::OnResponse(const LSRequestResponse& aResponse) {
507   AssertIsOnDOMFileThread();
508   MOZ_ASSERT(mState == State::ResponsePending);
509 
510   mActor = nullptr;
511 
512   mResponse = aResponse;
513 
514   mState = State::Finishing;
515 
516   MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
517 }
518 
HandleResponse(nsresult aResponse)519 void SimpleRequestResolver::HandleResponse(nsresult aResponse) {
520   MOZ_ASSERT(NS_IsMainThread());
521   MOZ_ASSERT(mPromise);
522 
523   mPromise->MaybeReject(aResponse);
524 }
525 
HandleResponse(bool aResponse)526 void SimpleRequestResolver::HandleResponse(bool aResponse) {
527   MOZ_ASSERT(NS_IsMainThread());
528   MOZ_ASSERT(mPromise);
529 
530   mPromise->MaybeResolve(aResponse);
531 }
532 
OnResponse(const LSSimpleRequestResponse & aResponse)533 void SimpleRequestResolver::OnResponse(
534     const LSSimpleRequestResponse& aResponse) {
535   MOZ_ASSERT(NS_IsMainThread());
536 
537   switch (aResponse.type()) {
538     case LSSimpleRequestResponse::Tnsresult:
539       HandleResponse(aResponse.get_nsresult());
540       break;
541 
542     case LSSimpleRequestResponse::TLSSimpleRequestPreloadedResponse:
543       HandleResponse(
544           aResponse.get_LSSimpleRequestPreloadedResponse().preloaded());
545       break;
546 
547     default:
548       MOZ_CRASH("Unknown response type!");
549   }
550 }
551 
552 }  // namespace dom
553 }  // namespace mozilla
554