1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=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 "PermissionRequestBase.h"
8 
9 #include "IndexedDBCommon.h"
10 
11 #include "MainThreadUtils.h"
12 #include "mozilla/Assertions.h"
13 #include "mozilla/Services.h"
14 #include "mozilla/dom/Element.h"
15 #include "nsIObserverService.h"
16 #include "nsIPrincipal.h"
17 #include "nsXULAppAPI.h"
18 
19 namespace mozilla::dom::indexedDB {
20 
21 using namespace mozilla::services;
22 
23 namespace {
24 
25 #define IDB_PREFIX "indexedDB"
26 #define TOPIC_PREFIX IDB_PREFIX "-permissions-"
27 
28 const nsLiteralCString kPermissionString = nsLiteralCString(IDB_PREFIX);
29 
30 const char kPermissionPromptTopic[] = TOPIC_PREFIX "prompt";
31 
32 #ifdef DEBUG
33 const char kPermissionResponseTopic[] = TOPIC_PREFIX "response";
34 #endif
35 
36 #undef TOPIC_PREFIX
37 #undef IDB_PREFIX
38 
39 const uint32_t kPermissionDefault = nsIPermissionManager::UNKNOWN_ACTION;
40 
AssertSanity()41 void AssertSanity() {
42   MOZ_ASSERT(XRE_IsParentProcess());
43   MOZ_ASSERT(NS_IsMainThread());
44 }
45 
46 }  // namespace
47 
PermissionRequestBase(Element * aOwnerElement,nsIPrincipal * aPrincipal)48 PermissionRequestBase::PermissionRequestBase(Element* aOwnerElement,
49                                              nsIPrincipal* aPrincipal)
50     : mOwnerElement(aOwnerElement), mPrincipal(aPrincipal) {
51   AssertSanity();
52   MOZ_ASSERT(aOwnerElement);
53   MOZ_ASSERT(aPrincipal);
54 }
55 
~PermissionRequestBase()56 PermissionRequestBase::~PermissionRequestBase() { AssertSanity(); }
57 
58 // static
59 Result<PermissionRequestBase::PermissionValue, nsresult>
GetCurrentPermission(nsIPrincipal & aPrincipal)60 PermissionRequestBase::GetCurrentPermission(nsIPrincipal& aPrincipal) {
61   AssertSanity();
62 
63   const nsCOMPtr<nsIPermissionManager> permMan = GetPermissionManager();
64   QM_TRY(OkIf(permMan), Err(NS_ERROR_FAILURE));
65 
66   QM_TRY_INSPECT(const uint32_t& intPermission,
67                  MOZ_TO_RESULT_INVOKE(permMan, TestExactPermissionFromPrincipal,
68                                       &aPrincipal, kPermissionString));
69 
70   const PermissionValue permission =
71       PermissionValueForIntPermission(intPermission);
72 
73   MOZ_ASSERT(permission == kPermissionAllowed ||
74              permission == kPermissionDenied ||
75              permission == kPermissionPrompt);
76 
77   return permission;
78 }
79 
80 // static
PermissionValueForIntPermission(uint32_t aIntPermission)81 auto PermissionRequestBase::PermissionValueForIntPermission(
82     uint32_t aIntPermission) -> PermissionValue {
83   AssertSanity();
84 
85   switch (aIntPermission) {
86     case kPermissionDefault:
87       return kPermissionPrompt;
88     case kPermissionAllowed:
89       return kPermissionAllowed;
90     case kPermissionDenied:
91       return kPermissionDenied;
92     default:
93       MOZ_CRASH("Bad permission!");
94   }
95 
96   MOZ_CRASH("Should never get here!");
97 }
98 
99 Result<PermissionRequestBase::PermissionValue, nsresult>
PromptIfNeeded()100 PermissionRequestBase::PromptIfNeeded() {
101   AssertSanity();
102   MOZ_ASSERT(mPrincipal);
103 
104   // Tricky, we want to release the window and principal in all cases except
105   // when we successfully prompt.
106   nsCOMPtr<Element> element = std::move(mOwnerElement);
107   nsCOMPtr<nsIPrincipal> principal = std::move(mPrincipal);
108 
109   QM_TRY_INSPECT(const PermissionValue& currentValue,
110                  GetCurrentPermission(*principal));
111   MOZ_ASSERT(currentValue != kPermissionDefault);
112 
113   if (currentValue == kPermissionPrompt) {
114     nsCOMPtr<nsIObserverService> obsSvc = GetObserverService();
115     QM_TRY(OkIf(obsSvc), Err(NS_ERROR_FAILURE));
116 
117     // We're about to prompt so move the members back.
118     mOwnerElement = std::move(element);
119     mPrincipal = std::move(principal);
120 
121     QM_TRY(obsSvc->NotifyObservers(static_cast<nsIObserver*>(this),
122                                    kPermissionPromptTopic, nullptr),
123            QM_PROPAGATE, [this](const auto&) {
124              // Finally release if we failed the prompt.
125              mOwnerElement = nullptr;
126              mPrincipal = nullptr;
127            });
128   }
129 
130   return currentValue;
131 }
132 
SetExplicitPermission(nsIPrincipal * aPrincipal,uint32_t aIntPermission)133 void PermissionRequestBase::SetExplicitPermission(nsIPrincipal* aPrincipal,
134                                                   uint32_t aIntPermission) {
135   AssertSanity();
136   MOZ_ASSERT(aPrincipal);
137   MOZ_ASSERT(aIntPermission == kPermissionAllowed ||
138              aIntPermission == kPermissionDenied);
139 
140   nsCOMPtr<nsIPermissionManager> permMan = GetPermissionManager();
141   if (NS_WARN_IF(!permMan)) {
142     return;
143   }
144 
145   nsresult rv =
146       permMan->AddFromPrincipal(aPrincipal, kPermissionString, aIntPermission,
147                                 nsIPermissionManager::EXPIRE_NEVER,
148                                 /* aExpireTime */ 0);
149   if (NS_WARN_IF(NS_FAILED(rv))) {
150     return;
151   }
152 }
153 
NS_IMPL_ISUPPORTS(PermissionRequestBase,nsIObserver,nsIIDBPermissionsRequest)154 NS_IMPL_ISUPPORTS(PermissionRequestBase, nsIObserver, nsIIDBPermissionsRequest)
155 
156 NS_IMETHODIMP
157 PermissionRequestBase::GetBrowserElement(Element** aElement) {
158   AssertSanity();
159   *aElement = do_AddRef(mOwnerElement).take();
160   return NS_OK;
161 }
162 
163 NS_IMETHODIMP
GetResponseObserver(nsIObserver ** aObserver)164 PermissionRequestBase::GetResponseObserver(nsIObserver** aObserver) {
165   AssertSanity();
166   *aObserver = do_AddRef(this).take();
167   return NS_OK;
168 }
169 
170 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)171 PermissionRequestBase::Observe(nsISupports* aSubject, const char* aTopic,
172                                const char16_t* aData) {
173   AssertSanity();
174   MOZ_ASSERT(!strcmp(aTopic, kPermissionResponseTopic));
175   MOZ_ASSERT(mOwnerElement);
176   MOZ_ASSERT(mPrincipal);
177 
178   const nsCOMPtr<Element> element = std::move(mOwnerElement);
179   Unused << element;
180   const nsCOMPtr<nsIPrincipal> principal = std::move(mPrincipal);
181 
182   nsresult rv;
183   uint32_t promptResult = nsDependentString(aData).ToInteger(&rv);
184   MOZ_ALWAYS_SUCCEEDS(rv);
185 
186   // The UI prompt code will only return one of these three values. We have to
187   // transform it to our values.
188   MOZ_ASSERT(promptResult == kPermissionDefault ||
189              promptResult == kPermissionAllowed ||
190              promptResult == kPermissionDenied);
191 
192   if (promptResult != kPermissionDefault) {
193     // Save explicitly allowed or denied permissions now.
194     SetExplicitPermission(principal, promptResult);
195   }
196 
197   PermissionValue permission;
198   switch (promptResult) {
199     case kPermissionDefault:
200       permission = kPermissionPrompt;
201       break;
202 
203     case kPermissionAllowed:
204       permission = kPermissionAllowed;
205       break;
206 
207     case kPermissionDenied:
208       permission = kPermissionDenied;
209       break;
210 
211     default:
212       MOZ_CRASH("Bad prompt result!");
213   }
214 
215   OnPromptComplete(permission);
216   return NS_OK;
217 }
218 
219 }  // namespace mozilla::dom::indexedDB
220