Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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/. */
7 #include "CheckPermissionsHelper.h"
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"
18 #include "CheckQuotaHelper.h"
19 #include "nsContentUtils.h"
20 #include "nsNetUtil.h"
21 #include "nsThreadUtils.h"
22 #include "mozilla/Services.h"
24 #include "IndexedDatabaseManager.h"
26 #define PERMISSION_INDEXEDDB "indexedDB"
27 #define TOPIC_PERMISSIONS_PROMPT "indexedDB-permissions-prompt"
28 #define TOPIC_PERMISSIONS_RESPONSE "indexedDB-permissions-response"
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
38 USING_INDEXEDDB_NAMESPACE
39 using namespace mozilla::services;
40 using mozilla::dom::quota::CheckQuotaHelper;
42 namespace {
44 inline
45 uint32_t
46 GetIndexedDBPermissions(nsIDOMWindow* aWindow)
47 {
48 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
50 NS_ASSERTION(aWindow, "Chrome shouldn't check the permission!");
52 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(aWindow));
53 NS_ENSURE_TRUE(sop, nsIPermissionManager::DENY_ACTION);
55 NS_ASSERTION(!nsContentUtils::IsSystemPrincipal(sop->GetPrincipal()),
56 "Chrome windows shouldn't check the permission!");
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 }
66 nsCOMPtr<nsIPermissionManager> permissionManager =
67 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
68 NS_ENSURE_TRUE(permissionManager, PERMISSION_DENIED);
70 uint32_t permission;
71 nsresult rv =
72 permissionManager->TestPermissionFromPrincipal(sop->GetPrincipal(),
73 PERMISSION_INDEXEDDB,
74 &permission);
75 NS_ENSURE_SUCCESS(rv, PERMISSION_DENIED);
77 return permission;
78 }
80 } // anonymous namespace
82 NS_IMPL_ISUPPORTS(CheckPermissionsHelper, nsIRunnable,
83 nsIInterfaceRequestor,
84 nsIObserver)
86 NS_IMETHODIMP
87 CheckPermissionsHelper::Run()
88 {
89 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
91 uint32_t permission = mHasPrompted ?
92 mPromptResult :
93 GetIndexedDBPermissions(mWindow);
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!");
105 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mWindow);
106 NS_ASSERTION(sop, "Window didn't QI to nsIScriptObjectPrincipal!");
108 nsIPrincipal* windowPrincipal = sop->GetPrincipal();
109 NS_ASSERTION(windowPrincipal, "Null principal!");
111 nsCOMPtr<nsIPermissionManager> permissionManager =
112 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
113 NS_ENSURE_STATE(permissionManager);
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);
128 return NS_OK;
129 }
131 nsRefPtr<OpenDatabaseHelper> helper;
132 helper.swap(mHelper);
134 nsCOMPtr<nsIDOMWindow> window;
135 window.swap(mWindow);
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!");
145 nsIPrincipal* windowPrincipal = sop->GetPrincipal();
146 NS_ASSERTION(windowPrincipal, "Null principal!");
148 uint32_t quotaPermission =
149 CheckQuotaHelper::GetQuotaPermission(windowPrincipal);
151 if (quotaPermission == nsIPermissionManager::ALLOW_ACTION) {
152 helper->SetUnlimitedQuotaAllowed();
153 }
154 }
156 return helper->DispatchToIOThread();
157 }
159 NS_ASSERTION(permission == PERMISSION_PROMPT ||
160 permission == PERMISSION_DENIED,
161 "Unknown permission!");
163 helper->SetError(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
165 return helper->RunImmediately();
166 }
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 }
177 if (aIID.Equals(NS_GET_IID(nsIDOMWindow))) {
178 return mWindow->QueryInterface(aIID, aResult);
179 }
181 *aResult = nullptr;
182 return NS_ERROR_NOT_AVAILABLE;
183 }
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?");
194 mHasPrompted = true;
196 nsresult rv;
197 uint32_t promptResult = nsDependentString(aData).ToInteger(&rv);
198 NS_ENSURE_SUCCESS(rv, rv);
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;
213 default:
214 NS_NOTREACHED("Unknown permission type!");
215 mPromptResult = PERMISSION_DENIED;
216 }
218 rv = NS_DispatchToCurrentThread(this);
219 NS_ENSURE_SUCCESS(rv, rv);
221 return NS_OK;
222 }