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