1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
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
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "StorageAccessPermissionRequest.h"
8 #include "nsGlobalWindowInner.h"
9 #include "mozilla/StaticPrefs_dom.h"
10 #include <cstdlib>
11 
12 namespace mozilla::dom {
13 
NS_IMPL_CYCLE_COLLECTION_INHERITED(StorageAccessPermissionRequest,ContentPermissionRequestBase)14 NS_IMPL_CYCLE_COLLECTION_INHERITED(StorageAccessPermissionRequest,
15                                    ContentPermissionRequestBase)
16 
17 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(StorageAccessPermissionRequest,
18                                                ContentPermissionRequestBase)
19 
20 StorageAccessPermissionRequest::StorageAccessPermissionRequest(
21     nsPIDOMWindowInner* aWindow, nsIPrincipal* aNodePrincipal,
22     AllowCallback&& aAllowCallback, CancelCallback&& aCancelCallback)
23     : ContentPermissionRequestBase(aNodePrincipal, aWindow,
24                                    "dom.storage_access"_ns,
25                                    "storage-access"_ns),
26       mAllowCallback(std::move(aAllowCallback)),
27       mCancelCallback(std::move(aCancelCallback)),
28       mCallbackCalled(false) {
29   mPermissionRequests.AppendElement(
30       PermissionRequest(mType, nsTArray<nsString>()));
31 }
32 
33 NS_IMETHODIMP
Cancel()34 StorageAccessPermissionRequest::Cancel() {
35   if (!mCallbackCalled) {
36     mCallbackCalled = true;
37     mCancelCallback();
38   }
39   return NS_OK;
40 }
41 
42 NS_IMETHODIMP
Allow(JS::HandleValue aChoices)43 StorageAccessPermissionRequest::Allow(JS::HandleValue aChoices) {
44   nsTArray<PermissionChoice> choices;
45   nsresult rv = TranslateChoices(aChoices, mPermissionRequests, choices);
46   if (NS_FAILED(rv)) {
47     return rv;
48   }
49 
50   // There is no support to allow grants automatically from the prompting code
51   // path.
52 
53   if (!mCallbackCalled) {
54     mCallbackCalled = true;
55     if (choices.Length() == 1 && choices[0].choice().EqualsLiteral("allow")) {
56       mAllowCallback();
57     }
58   }
59   return NS_OK;
60 }
61 
62 RefPtr<StorageAccessPermissionRequest::AutoGrantDelayPromise>
MaybeDelayAutomaticGrants()63 StorageAccessPermissionRequest::MaybeDelayAutomaticGrants() {
64   RefPtr<AutoGrantDelayPromise::Private> p =
65       new AutoGrantDelayPromise::Private(__func__);
66 
67   unsigned simulatedDelay = CalculateSimulatedDelay();
68   if (simulatedDelay) {
69     nsCOMPtr<nsITimer> timer;
70     RefPtr<AutoGrantDelayPromise::Private> promise = p;
71     nsresult rv = NS_NewTimerWithFuncCallback(
72         getter_AddRefs(timer),
73         [](nsITimer* aTimer, void* aClosure) -> void {
74           auto* promise =
75               static_cast<AutoGrantDelayPromise::Private*>(aClosure);
76           promise->Resolve(true, __func__);
77           NS_RELEASE(aTimer);
78           NS_RELEASE(promise);
79         },
80         promise, simulatedDelay, nsITimer::TYPE_ONE_SHOT,
81         "DelayedAllowAutoGrantCallback");
82     if (NS_WARN_IF(NS_FAILED(rv))) {
83       p->Reject(false, __func__);
84     } else {
85       // Leak the references here! We'll release them inside the callback.
86       Unused << timer.forget();
87       Unused << promise.forget();
88     }
89   } else {
90     p->Resolve(false, __func__);
91   }
92   return p;
93 }
94 
95 already_AddRefed<StorageAccessPermissionRequest>
Create(nsPIDOMWindowInner * aWindow,AllowCallback && aAllowCallback,CancelCallback && aCancelCallback)96 StorageAccessPermissionRequest::Create(nsPIDOMWindowInner* aWindow,
97                                        AllowCallback&& aAllowCallback,
98                                        CancelCallback&& aCancelCallback) {
99   if (!aWindow) {
100     return nullptr;
101   }
102   nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(aWindow);
103   if (!win->GetPrincipal()) {
104     return nullptr;
105   }
106   RefPtr<StorageAccessPermissionRequest> request =
107       new StorageAccessPermissionRequest(aWindow, win->GetPrincipal(),
108                                          std::move(aAllowCallback),
109                                          std::move(aCancelCallback));
110   return request.forget();
111 }
112 
CalculateSimulatedDelay()113 unsigned StorageAccessPermissionRequest::CalculateSimulatedDelay() {
114   if (!StaticPrefs::dom_storage_access_auto_grants_delayed()) {
115     return 0;
116   }
117 
118   // Generate a random time value that is at least 0 and and most 3 seconds.
119   std::srand(static_cast<unsigned>(PR_Now()));
120 
121   const unsigned kMin = 0;
122   const unsigned kMax = 3000;
123   const unsigned random = std::abs(std::rand());
124 
125   return kMin + random % (kMax - kMin);
126 }
127 
128 }  // namespace mozilla::dom
129