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