|
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 |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "CheckPermissionsHelper.h" |
|
8 |
|
9 #include "nsIDOMWindow.h" |
|
10 #include "nsILoadContext.h" |
|
11 #include "nsIWebNavigation.h" |
|
12 #include "nsIObserverService.h" |
|
13 #include "nsIPermissionManager.h" |
|
14 #include "nsIPrincipal.h" |
|
15 #include "nsIScriptObjectPrincipal.h" |
|
16 #include "nsIURI.h" |
|
17 |
|
18 #include "CheckQuotaHelper.h" |
|
19 #include "nsContentUtils.h" |
|
20 #include "nsNetUtil.h" |
|
21 #include "nsThreadUtils.h" |
|
22 #include "mozilla/Services.h" |
|
23 |
|
24 #include "IndexedDatabaseManager.h" |
|
25 |
|
26 #define PERMISSION_INDEXEDDB "indexedDB" |
|
27 #define TOPIC_PERMISSIONS_PROMPT "indexedDB-permissions-prompt" |
|
28 #define TOPIC_PERMISSIONS_RESPONSE "indexedDB-permissions-response" |
|
29 |
|
30 // This is a little confusing, but our default behavior (UNKNOWN_ACTION) is to |
|
31 // allow access without a prompt. If the "indexedDB" permission is set to |
|
32 // ALLOW_ACTION then we will issue a prompt before allowing access. Otherwise |
|
33 // (DENY_ACTION) we deny access. |
|
34 #define PERMISSION_ALLOWED nsIPermissionManager::UNKNOWN_ACTION |
|
35 #define PERMISSION_DENIED nsIPermissionManager::DENY_ACTION |
|
36 #define PERMISSION_PROMPT nsIPermissionManager::ALLOW_ACTION |
|
37 |
|
38 USING_INDEXEDDB_NAMESPACE |
|
39 using namespace mozilla::services; |
|
40 using mozilla::dom::quota::CheckQuotaHelper; |
|
41 |
|
42 namespace { |
|
43 |
|
44 inline |
|
45 uint32_t |
|
46 GetIndexedDBPermissions(nsIDOMWindow* aWindow) |
|
47 { |
|
48 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
49 |
|
50 NS_ASSERTION(aWindow, "Chrome shouldn't check the permission!"); |
|
51 |
|
52 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(aWindow)); |
|
53 NS_ENSURE_TRUE(sop, nsIPermissionManager::DENY_ACTION); |
|
54 |
|
55 NS_ASSERTION(!nsContentUtils::IsSystemPrincipal(sop->GetPrincipal()), |
|
56 "Chrome windows shouldn't check the permission!"); |
|
57 |
|
58 nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow); |
|
59 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav); |
|
60 if (loadContext && loadContext->UsePrivateBrowsing()) { |
|
61 // TODO Support private browsing indexedDB? |
|
62 NS_WARNING("IndexedDB may not be used while in private browsing mode!"); |
|
63 return PERMISSION_DENIED; |
|
64 } |
|
65 |
|
66 nsCOMPtr<nsIPermissionManager> permissionManager = |
|
67 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); |
|
68 NS_ENSURE_TRUE(permissionManager, PERMISSION_DENIED); |
|
69 |
|
70 uint32_t permission; |
|
71 nsresult rv = |
|
72 permissionManager->TestPermissionFromPrincipal(sop->GetPrincipal(), |
|
73 PERMISSION_INDEXEDDB, |
|
74 &permission); |
|
75 NS_ENSURE_SUCCESS(rv, PERMISSION_DENIED); |
|
76 |
|
77 return permission; |
|
78 } |
|
79 |
|
80 } // anonymous namespace |
|
81 |
|
82 NS_IMPL_ISUPPORTS(CheckPermissionsHelper, nsIRunnable, |
|
83 nsIInterfaceRequestor, |
|
84 nsIObserver) |
|
85 |
|
86 NS_IMETHODIMP |
|
87 CheckPermissionsHelper::Run() |
|
88 { |
|
89 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
90 |
|
91 uint32_t permission = mHasPrompted ? |
|
92 mPromptResult : |
|
93 GetIndexedDBPermissions(mWindow); |
|
94 |
|
95 nsresult rv; |
|
96 if (mHasPrompted) { |
|
97 // Add permissions to the database, but only if we are in the parent |
|
98 // process (if we are in the child process, we have already |
|
99 // set the permission when the prompt was shown in the parent, as |
|
100 // we cannot set the permission from the child). |
|
101 if (permission != PERMISSION_PROMPT && |
|
102 IndexedDatabaseManager::IsMainProcess()) { |
|
103 NS_ASSERTION(mWindow, "Null window!"); |
|
104 |
|
105 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mWindow); |
|
106 NS_ASSERTION(sop, "Window didn't QI to nsIScriptObjectPrincipal!"); |
|
107 |
|
108 nsIPrincipal* windowPrincipal = sop->GetPrincipal(); |
|
109 NS_ASSERTION(windowPrincipal, "Null principal!"); |
|
110 |
|
111 nsCOMPtr<nsIPermissionManager> permissionManager = |
|
112 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); |
|
113 NS_ENSURE_STATE(permissionManager); |
|
114 |
|
115 rv = permissionManager->AddFromPrincipal(windowPrincipal, |
|
116 PERMISSION_INDEXEDDB, permission, |
|
117 nsIPermissionManager::EXPIRE_NEVER, |
|
118 0); |
|
119 NS_ENSURE_SUCCESS(rv, rv); |
|
120 } |
|
121 } |
|
122 else if (permission == PERMISSION_PROMPT && mPromptAllowed) { |
|
123 nsCOMPtr<nsIObserverService> obs = GetObserverService(); |
|
124 rv = obs->NotifyObservers(static_cast<nsIRunnable*>(this), |
|
125 TOPIC_PERMISSIONS_PROMPT, nullptr); |
|
126 NS_ENSURE_SUCCESS(rv, rv); |
|
127 |
|
128 return NS_OK; |
|
129 } |
|
130 |
|
131 nsRefPtr<OpenDatabaseHelper> helper; |
|
132 helper.swap(mHelper); |
|
133 |
|
134 nsCOMPtr<nsIDOMWindow> window; |
|
135 window.swap(mWindow); |
|
136 |
|
137 if (permission == PERMISSION_ALLOWED) { |
|
138 // If we're running from a window then we should check the quota permission |
|
139 // as well. If we don't have a window then we're opening a chrome database |
|
140 // and the quota will be unlimited already. |
|
141 if (window) { |
|
142 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(window); |
|
143 NS_ASSERTION(sop, "Window didn't QI to nsIScriptObjectPrincipal!"); |
|
144 |
|
145 nsIPrincipal* windowPrincipal = sop->GetPrincipal(); |
|
146 NS_ASSERTION(windowPrincipal, "Null principal!"); |
|
147 |
|
148 uint32_t quotaPermission = |
|
149 CheckQuotaHelper::GetQuotaPermission(windowPrincipal); |
|
150 |
|
151 if (quotaPermission == nsIPermissionManager::ALLOW_ACTION) { |
|
152 helper->SetUnlimitedQuotaAllowed(); |
|
153 } |
|
154 } |
|
155 |
|
156 return helper->DispatchToIOThread(); |
|
157 } |
|
158 |
|
159 NS_ASSERTION(permission == PERMISSION_PROMPT || |
|
160 permission == PERMISSION_DENIED, |
|
161 "Unknown permission!"); |
|
162 |
|
163 helper->SetError(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); |
|
164 |
|
165 return helper->RunImmediately(); |
|
166 } |
|
167 |
|
168 NS_IMETHODIMP |
|
169 CheckPermissionsHelper::GetInterface(const nsIID& aIID, |
|
170 void** aResult) |
|
171 { |
|
172 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
173 if (aIID.Equals(NS_GET_IID(nsIObserver))) { |
|
174 return QueryInterface(aIID, aResult); |
|
175 } |
|
176 |
|
177 if (aIID.Equals(NS_GET_IID(nsIDOMWindow))) { |
|
178 return mWindow->QueryInterface(aIID, aResult); |
|
179 } |
|
180 |
|
181 *aResult = nullptr; |
|
182 return NS_ERROR_NOT_AVAILABLE; |
|
183 } |
|
184 |
|
185 NS_IMETHODIMP |
|
186 CheckPermissionsHelper::Observe(nsISupports* aSubject, |
|
187 const char* aTopic, |
|
188 const char16_t* aData) |
|
189 { |
|
190 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
191 NS_ASSERTION(!strcmp(aTopic, TOPIC_PERMISSIONS_RESPONSE), "Bad topic!"); |
|
192 NS_ASSERTION(mPromptAllowed, "How did we get here?"); |
|
193 |
|
194 mHasPrompted = true; |
|
195 |
|
196 nsresult rv; |
|
197 uint32_t promptResult = nsDependentString(aData).ToInteger(&rv); |
|
198 NS_ENSURE_SUCCESS(rv, rv); |
|
199 |
|
200 // Have to convert the permission we got from the user to our weird reversed |
|
201 // permission type. |
|
202 switch (promptResult) { |
|
203 case nsIPermissionManager::ALLOW_ACTION: |
|
204 mPromptResult = PERMISSION_ALLOWED; |
|
205 break; |
|
206 case nsIPermissionManager::DENY_ACTION: |
|
207 mPromptResult = PERMISSION_DENIED; |
|
208 break; |
|
209 case nsIPermissionManager::UNKNOWN_ACTION: |
|
210 mPromptResult = PERMISSION_PROMPT; |
|
211 break; |
|
212 |
|
213 default: |
|
214 NS_NOTREACHED("Unknown permission type!"); |
|
215 mPromptResult = PERMISSION_DENIED; |
|
216 } |
|
217 |
|
218 rv = NS_DispatchToCurrentThread(this); |
|
219 NS_ENSURE_SUCCESS(rv, rv); |
|
220 |
|
221 return NS_OK; |
|
222 } |