michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "CheckPermissionsHelper.h" michael@0: michael@0: #include "nsIDOMWindow.h" michael@0: #include "nsILoadContext.h" michael@0: #include "nsIWebNavigation.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIPermissionManager.h" michael@0: #include "nsIPrincipal.h" michael@0: #include "nsIScriptObjectPrincipal.h" michael@0: #include "nsIURI.h" michael@0: michael@0: #include "CheckQuotaHelper.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "mozilla/Services.h" michael@0: michael@0: #include "IndexedDatabaseManager.h" michael@0: michael@0: #define PERMISSION_INDEXEDDB "indexedDB" michael@0: #define TOPIC_PERMISSIONS_PROMPT "indexedDB-permissions-prompt" michael@0: #define TOPIC_PERMISSIONS_RESPONSE "indexedDB-permissions-response" michael@0: michael@0: // This is a little confusing, but our default behavior (UNKNOWN_ACTION) is to michael@0: // allow access without a prompt. If the "indexedDB" permission is set to michael@0: // ALLOW_ACTION then we will issue a prompt before allowing access. Otherwise michael@0: // (DENY_ACTION) we deny access. michael@0: #define PERMISSION_ALLOWED nsIPermissionManager::UNKNOWN_ACTION michael@0: #define PERMISSION_DENIED nsIPermissionManager::DENY_ACTION michael@0: #define PERMISSION_PROMPT nsIPermissionManager::ALLOW_ACTION michael@0: michael@0: USING_INDEXEDDB_NAMESPACE michael@0: using namespace mozilla::services; michael@0: using mozilla::dom::quota::CheckQuotaHelper; michael@0: michael@0: namespace { michael@0: michael@0: inline michael@0: uint32_t michael@0: GetIndexedDBPermissions(nsIDOMWindow* aWindow) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: NS_ASSERTION(aWindow, "Chrome shouldn't check the permission!"); michael@0: michael@0: nsCOMPtr sop(do_QueryInterface(aWindow)); michael@0: NS_ENSURE_TRUE(sop, nsIPermissionManager::DENY_ACTION); michael@0: michael@0: NS_ASSERTION(!nsContentUtils::IsSystemPrincipal(sop->GetPrincipal()), michael@0: "Chrome windows shouldn't check the permission!"); michael@0: michael@0: nsCOMPtr webNav = do_GetInterface(aWindow); michael@0: nsCOMPtr loadContext = do_QueryInterface(webNav); michael@0: if (loadContext && loadContext->UsePrivateBrowsing()) { michael@0: // TODO Support private browsing indexedDB? michael@0: NS_WARNING("IndexedDB may not be used while in private browsing mode!"); michael@0: return PERMISSION_DENIED; michael@0: } michael@0: michael@0: nsCOMPtr permissionManager = michael@0: do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); michael@0: NS_ENSURE_TRUE(permissionManager, PERMISSION_DENIED); michael@0: michael@0: uint32_t permission; michael@0: nsresult rv = michael@0: permissionManager->TestPermissionFromPrincipal(sop->GetPrincipal(), michael@0: PERMISSION_INDEXEDDB, michael@0: &permission); michael@0: NS_ENSURE_SUCCESS(rv, PERMISSION_DENIED); michael@0: michael@0: return permission; michael@0: } michael@0: michael@0: } // anonymous namespace michael@0: michael@0: NS_IMPL_ISUPPORTS(CheckPermissionsHelper, nsIRunnable, michael@0: nsIInterfaceRequestor, michael@0: nsIObserver) michael@0: michael@0: NS_IMETHODIMP michael@0: CheckPermissionsHelper::Run() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: uint32_t permission = mHasPrompted ? michael@0: mPromptResult : michael@0: GetIndexedDBPermissions(mWindow); michael@0: michael@0: nsresult rv; michael@0: if (mHasPrompted) { michael@0: // Add permissions to the database, but only if we are in the parent michael@0: // process (if we are in the child process, we have already michael@0: // set the permission when the prompt was shown in the parent, as michael@0: // we cannot set the permission from the child). michael@0: if (permission != PERMISSION_PROMPT && michael@0: IndexedDatabaseManager::IsMainProcess()) { michael@0: NS_ASSERTION(mWindow, "Null window!"); michael@0: michael@0: nsCOMPtr sop = do_QueryInterface(mWindow); michael@0: NS_ASSERTION(sop, "Window didn't QI to nsIScriptObjectPrincipal!"); michael@0: michael@0: nsIPrincipal* windowPrincipal = sop->GetPrincipal(); michael@0: NS_ASSERTION(windowPrincipal, "Null principal!"); michael@0: michael@0: nsCOMPtr permissionManager = michael@0: do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); michael@0: NS_ENSURE_STATE(permissionManager); michael@0: michael@0: rv = permissionManager->AddFromPrincipal(windowPrincipal, michael@0: PERMISSION_INDEXEDDB, permission, michael@0: nsIPermissionManager::EXPIRE_NEVER, michael@0: 0); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: else if (permission == PERMISSION_PROMPT && mPromptAllowed) { michael@0: nsCOMPtr obs = GetObserverService(); michael@0: rv = obs->NotifyObservers(static_cast(this), michael@0: TOPIC_PERMISSIONS_PROMPT, nullptr); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsRefPtr helper; michael@0: helper.swap(mHelper); michael@0: michael@0: nsCOMPtr window; michael@0: window.swap(mWindow); michael@0: michael@0: if (permission == PERMISSION_ALLOWED) { michael@0: // If we're running from a window then we should check the quota permission michael@0: // as well. If we don't have a window then we're opening a chrome database michael@0: // and the quota will be unlimited already. michael@0: if (window) { michael@0: nsCOMPtr sop = do_QueryInterface(window); michael@0: NS_ASSERTION(sop, "Window didn't QI to nsIScriptObjectPrincipal!"); michael@0: michael@0: nsIPrincipal* windowPrincipal = sop->GetPrincipal(); michael@0: NS_ASSERTION(windowPrincipal, "Null principal!"); michael@0: michael@0: uint32_t quotaPermission = michael@0: CheckQuotaHelper::GetQuotaPermission(windowPrincipal); michael@0: michael@0: if (quotaPermission == nsIPermissionManager::ALLOW_ACTION) { michael@0: helper->SetUnlimitedQuotaAllowed(); michael@0: } michael@0: } michael@0: michael@0: return helper->DispatchToIOThread(); michael@0: } michael@0: michael@0: NS_ASSERTION(permission == PERMISSION_PROMPT || michael@0: permission == PERMISSION_DENIED, michael@0: "Unknown permission!"); michael@0: michael@0: helper->SetError(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR); michael@0: michael@0: return helper->RunImmediately(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: CheckPermissionsHelper::GetInterface(const nsIID& aIID, michael@0: void** aResult) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: if (aIID.Equals(NS_GET_IID(nsIObserver))) { michael@0: return QueryInterface(aIID, aResult); michael@0: } michael@0: michael@0: if (aIID.Equals(NS_GET_IID(nsIDOMWindow))) { michael@0: return mWindow->QueryInterface(aIID, aResult); michael@0: } michael@0: michael@0: *aResult = nullptr; michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: CheckPermissionsHelper::Observe(nsISupports* aSubject, michael@0: const char* aTopic, michael@0: const char16_t* aData) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: NS_ASSERTION(!strcmp(aTopic, TOPIC_PERMISSIONS_RESPONSE), "Bad topic!"); michael@0: NS_ASSERTION(mPromptAllowed, "How did we get here?"); michael@0: michael@0: mHasPrompted = true; michael@0: michael@0: nsresult rv; michael@0: uint32_t promptResult = nsDependentString(aData).ToInteger(&rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Have to convert the permission we got from the user to our weird reversed michael@0: // permission type. michael@0: switch (promptResult) { michael@0: case nsIPermissionManager::ALLOW_ACTION: michael@0: mPromptResult = PERMISSION_ALLOWED; michael@0: break; michael@0: case nsIPermissionManager::DENY_ACTION: michael@0: mPromptResult = PERMISSION_DENIED; michael@0: break; michael@0: case nsIPermissionManager::UNKNOWN_ACTION: michael@0: mPromptResult = PERMISSION_PROMPT; michael@0: break; michael@0: michael@0: default: michael@0: NS_NOTREACHED("Unknown permission type!"); michael@0: mPromptResult = PERMISSION_DENIED; michael@0: } michael@0: michael@0: rv = NS_DispatchToCurrentThread(this); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: }