Bug 1583142 - Remove third-party "persistent-storage" prompting support r=baku

Differential Revision: https://phabricator.services.mozilla.com/D50908

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Thomas Nguyen 2019-11-07 11:56:28 +00:00
parent dcda6f6b3d
commit f431604a62
13 changed files with 255 additions and 42 deletions

View File

@ -423,6 +423,12 @@ pref("permissions.postPrompt.animate", true);
pref("permissions.eventTelemetry.enabled", false);
#endif
#ifdef NIGHTLY_BUILD
pref("permissions.delegation.enable", true);
#else
pref("permissions.delegation.enable", false);
#endif
// handle links targeting new windows
// 1=current window/tab, 2=new window, 3=new tab in most recent window
pref("browser.link.open_newwindow", 3);

View File

@ -15147,6 +15147,11 @@ PermissionDelegateHandler* Document::GetPermissionDelegateHandler() {
mPermissionDelegateHandler =
mozilla::MakeAndAddRef<PermissionDelegateHandler>(this);
}
if (!mPermissionDelegateHandler->Initialize()) {
mPermissionDelegateHandler = nullptr;
}
return mPermissionDelegateHandler;
}

View File

@ -4596,7 +4596,7 @@ class Document : public nsINode,
UniquePtr<ResizeObserverController> mResizeObserverController;
// Permission Delegate Handler, lazily-initialized in
// PermissionDelegateHandler
// GetPermissionDelegateHandler
RefPtr<PermissionDelegateHandler> mPermissionDelegateHandler;
// True if BIDI is enabled.

View File

@ -563,6 +563,8 @@ ContentPermissionRequestBase::ContentPermissionRequestBase(
return;
}
mPermissionHandler = doc->GetPermissionDelegateHandler();
mUserHadInteractedWithDocument = doc->UserHasInteracted();
nsDOMNavigationTiming* navTiming = doc->GetNavigationTiming();
@ -655,8 +657,25 @@ ContentPermissionRequestBase::CheckPromptPrefs() {
return PromptResult::Pending;
}
bool ContentPermissionRequestBase::CheckPermissionDelegate() {
// There is case that ContentPermissionRequestBase is constructed without
// window, then mPermissionHandler will be null. So we only check permission
// delegate if we have non-null mPermissionHandler
if (mPermissionHandler &&
!mPermissionHandler->HasPermissionDelegated(mType)) {
return false;
}
return true;
}
nsresult ContentPermissionRequestBase::ShowPrompt(
ContentPermissionRequestBase::PromptResult& aResult) {
if (!CheckPermissionDelegate()) {
aResult = PromptResult::Denied;
return NS_OK;
}
aResult = CheckPromptPrefs();
if (aResult != PromptResult::Pending) {

View File

@ -13,6 +13,7 @@
#include "mozilla/dom/PContentPermissionRequestChild.h"
#include "mozilla/dom/ipc/IdType.h"
#include "nsIDOMEventListener.h"
#include "PermissionDelegateHandler.h"
// Microsoft's API Name hackery sucks
// XXXbz Doing this in a header is a gigantic footgun. See
@ -140,6 +141,9 @@ class ContentPermissionRequestBase : public nsIContentPermissionRequest {
PromptResult CheckPromptPrefs();
// Check if the permission has an opportunity to request.
bool CheckPermissionDelegate();
enum class DelayedTaskType {
Allow,
Deny,
@ -158,6 +162,7 @@ class ContentPermissionRequestBase : public nsIContentPermissionRequest {
nsCOMPtr<nsIPrincipal> mTopLevelPrincipal;
nsCOMPtr<nsPIDOMWindowInner> mWindow;
nsCOMPtr<nsIContentPermissionRequester> mRequester;
RefPtr<PermissionDelegateHandler> mPermissionHandler;
nsCString mPrefName;
nsCString mType;
bool mIsHandlingUserInput;

View File

@ -79,6 +79,10 @@ nsresult PermissionStatus::UpdateState() {
PermissionDelegateHandler* permissionHandler =
document->GetPermissionDelegateHandler();
if (NS_WARN_IF(!permissionHandler)) {
return NS_ERROR_FAILURE;
}
nsresult rv = permissionHandler->GetPermissionForPermissionsAPI(
PermissionNameToType(mName), &action);
if (NS_WARN_IF(NS_FAILED(rv))) {

View File

@ -92,7 +92,7 @@
top: UNKNOWN_ACTION,
name: 'persistent-storage',
type: 'persistent-storage',
expected: 'prompt',
expected: 'denied',
},
{
id: 'query navigation top prompt',
@ -120,7 +120,7 @@
top: PROMPT_ACTION,
name: 'persistent-storage',
type: 'persistent-storage',
expected: 'prompt',
expected: 'denied',
},
{
id: 'query navigation top denied',
@ -148,7 +148,7 @@
top: DENY_ACTION,
name: 'persistent-storage',
type: 'persistent-storage',
expected: 'prompt',
expected: 'denied',
},
{
id: 'query navigation top granted',
@ -176,7 +176,7 @@
top: ALLOW_ACTION,
name: 'persistent-storage',
type: 'persistent-storage',
expected: 'prompt',
expected: 'denied',
},
{
id: 'query navigation top denied, iframe has allow attribute',

View File

@ -286,6 +286,8 @@ already_AddRefed<Promise> ExecuteOpOnMainOrWorkerThread(
// In private browsing mode, no permission prompt.
if (nsContentUtils::IsInPrivateBrowsing(doc)) {
aRv = request->Cancel();
} else if (!request->CheckPermissionDelegate()) {
aRv = request->Cancel();
} else {
aRv = request->Start();
}

View File

@ -5,7 +5,9 @@ support-files =
browser_permissionsPrompt.html
head-shared.js
head.js
empty.html
[browser_permissionsCrossOrigin.js]
[browser_permissionsPromptAllow.js]
skip-if = fission
[browser_permissionsPromptDeny.js]

View File

@ -0,0 +1,56 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
const emptyURL = "https://example.com/browser/dom/quota/test/empty.html";
add_task(async function testNoPermissionPrompt() {
registerPopupEventHandler("popupshowing", function() {
ok(false, "Shouldn't show a popup this time");
});
registerPopupEventHandler("popupshown", function() {
ok(false, "Shouldn't show a popup this time");
});
registerPopupEventHandler("popuphidden", function() {
ok(false, "Shouldn't show a popup this time");
});
info("Creating tab");
await BrowserTestUtils.withNewTab(emptyURL, async function(browser) {
await new Promise(r => {
SpecialPowers.pushPrefEnv(
{
set: [
["dom.security.featurePolicy.enabled", true],
["permissions.delegation.enable", true],
["dom.security.featurePolicy.header.enabled", true],
["dom.security.featurePolicy.webidl.enabled", true],
],
},
r
);
});
await ContentTask.spawn(browser, null, async function(host0) {
let frame = content.document.createElement("iframe");
// Cross origin src
frame.src = "https://example.org/browser/dom/quota/test/empty.html";
content.document.body.appendChild(frame);
await ContentTaskUtils.waitForEvent(frame, "load");
await content.SpecialPowers.spawn(frame, [], async function() {
// Request a permission.
const persistAllowed = await this.content.navigator.storage.persist();
Assert.ok(
!persistAllowed,
"navigator.storage.persist() has been denied"
);
});
content.document.body.removeChild(frame);
});
});
unregisterAllPopupEventHandlers();
});

View File

@ -0,0 +1,8 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Empty file</title>
</head>
<body>
</body>
</html>

View File

@ -7,10 +7,11 @@
#include "nsGlobalWindowInner.h"
#include "PermissionDelegateHandler.h"
#include "nsPIDOMWindow.h"
#include "nsIPermissionManager.h"
#include "nsPermissionManager.h"
#include "nsIPrincipal.h"
#include "nsContentPermissionHelper.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticPrefs_permissions.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/FeaturePolicyUtils.h"
@ -24,9 +25,12 @@ typedef PermissionDelegateHandler::PermissionDelegateInfo DelegateInfo;
static const DelegateInfo sPermissionsMap[] = {
// Permissions API map
{"geo", u"geolocation", DelegatePolicy::eDelegateUseFeaturePolicy},
// The same with geo, but we support both to save some conversions between
// "geo" and "geolocation"
{"geolocation", u"geolocation", DelegatePolicy::eDelegateUseFeaturePolicy},
{"desktop-notification", nullptr,
DelegatePolicy::ePersistDeniedCrossOrigin},
{"persistent-storage", nullptr, DelegatePolicy::eDelegateUseIframeOrigin},
{"persistent-storage", nullptr, DelegatePolicy::ePersistDeniedCrossOrigin},
};
NS_IMPL_CYCLE_COLLECTION(PermissionDelegateHandler)
@ -56,48 +60,87 @@ const DelegateInfo* PermissionDelegateHandler::GetPermissionDelegateInfo(
return nullptr;
}
nsresult PermissionDelegateHandler::GetPermissionForPermissionsAPI(
const nsACString& aType, uint32_t* aPermission) {
bool PermissionDelegateHandler::Initialize() {
MOZ_ASSERT(mDocument);
mPermissionManager = nsPermissionManager::GetInstance();
if (!mPermissionManager) {
return false;
}
mPrincipal = mDocument->NodePrincipal();
nsPIDOMWindowInner* window = mDocument->GetInnerWindow();
nsGlobalWindowInner* innerWindow = nsGlobalWindowInner::Cast(window);
if (innerWindow) {
mTopLevelPrincipal = innerWindow->GetTopLevelAntiTrackingPrincipal();
}
return true;
}
static bool IsTopWindowContent(Document* aDocument) {
MOZ_ASSERT(aDocument);
BrowsingContext* browsingContext = aDocument->GetBrowsingContext();
return browsingContext && browsingContext->IsTopContent();
}
bool PermissionDelegateHandler::HasPermissionDelegated(
const nsACString& aType) {
MOZ_ASSERT(mDocument);
if (!StaticPrefs::permissions_delegation_enable()) {
return true;
}
// System principal should have right to make permission request
if (mPrincipal->IsSystemPrincipal()) {
return true;
}
const DelegateInfo* info =
GetPermissionDelegateInfo(NS_ConvertUTF8toUTF16(aType));
// If the type is not in the supported list, auto denied
if (!info) {
*aPermission = nsIPermissionManager::DENY_ACTION;
return false;
}
if (info->mPolicy == DelegatePolicy::eDelegateUseFeaturePolicy &&
info->mFeatureName) {
nsAutoString featureName(info->mFeatureName);
// Default allowlist for a feature used in permissions delegate should be
// set to eSelf, to ensure that permission is denied by default and only
// have the opportunity to request permission with allow attribute.
if (!FeaturePolicyUtils::IsFeatureAllowed(mDocument, featureName)) {
return false;
}
}
if (info->mPolicy == DelegatePolicy::ePersistDeniedCrossOrigin &&
!IsTopWindowContent(mDocument) &&
!mPrincipal->Subsumes(mTopLevelPrincipal)) {
return false;
}
return true;
}
nsresult PermissionDelegateHandler::GetPermission(const nsACString& aType,
uint32_t* aPermission,
bool aExactHostMatch) {
MOZ_ASSERT(mDocument);
if (mPrincipal->IsSystemPrincipal()) {
*aPermission = nsIPermissionManager::ALLOW_ACTION;
return NS_OK;
}
nsresult rv;
nsCOMPtr<nsIPermissionManager> permMgr =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
*aPermission = nsIPermissionManager::DENY_ACTION;
return rv;
}
const DelegateInfo* info =
GetPermissionDelegateInfo(NS_ConvertUTF8toUTF16(aType));
nsCOMPtr<nsIPrincipal> principal = mDocument->NodePrincipal();
if (!Preferences::GetBool("permissions.delegation.enable", false)) {
return permMgr->TestPermissionFromPrincipal(principal, aType, aPermission);
}
if (mDocument->GetWindow()->IsTopLevelWindow() ||
info->mPolicy == DelegatePolicy::eDelegateUseIframeOrigin) {
return permMgr->TestPermissionFromPrincipal(principal, aType, aPermission);
}
nsPIDOMWindowInner* window = mDocument->GetInnerWindow();
nsGlobalWindowInner* innerWindow = nsGlobalWindowInner::Cast(window);
nsIPrincipal* topPrincipal = innerWindow->GetTopLevelAntiTrackingPrincipal();
// Permission is delegated in same origin
if (principal->Subsumes(topPrincipal)) {
return permMgr->TestPermissionFromPrincipal(topPrincipal, aType,
aPermission);
}
if (info->mPolicy == DelegatePolicy::ePersistDeniedCrossOrigin) {
// If the type is not in the supported list, auto denied
if (!info) {
*aPermission = nsIPermissionManager::DENY_ACTION;
return NS_OK;
}
@ -114,5 +157,35 @@ nsresult PermissionDelegateHandler::GetPermissionForPermissionsAPI(
}
}
return permMgr->TestPermissionFromPrincipal(topPrincipal, aType, aPermission);
if (info->mPolicy == DelegatePolicy::ePersistDeniedCrossOrigin &&
!IsTopWindowContent(mDocument) &&
!mPrincipal->Subsumes(mTopLevelPrincipal)) {
*aPermission = nsIPermissionManager::DENY_ACTION;
return NS_OK;
}
nsresult (nsIPermissionManager::*testPermission)(
nsIPrincipal*, const nsACString&, uint32_t*) =
aExactHostMatch ? &nsIPermissionManager::TestExactPermissionFromPrincipal
: &nsIPermissionManager::TestPermissionFromPrincipal;
if (!StaticPrefs::permissions_delegation_enable()) {
return (mPermissionManager->*testPermission)(mPrincipal, aType,
aPermission);
}
nsIPrincipal* principal = mPrincipal;
if (mTopLevelPrincipal &&
(info->mPolicy == DelegatePolicy::eDelegateUseTopOrigin ||
(info->mPolicy == DelegatePolicy::eDelegateUseFeaturePolicy &&
StaticPrefs::dom_security_featurePolicy_enabled()))) {
principal = mTopLevelPrincipal;
}
return (mPermissionManager->*testPermission)(principal, aType, aPermission);
}
nsresult PermissionDelegateHandler::GetPermissionForPermissionsAPI(
const nsACString& aType, uint32_t* aPermission) {
return GetPermission(aType, aPermission, false);
}

View File

@ -28,6 +28,9 @@
#include "nsISupports.h"
class nsIPrincipal;
class nsIContentPermissionRequest;
namespace mozilla {
namespace dom {
class Document;
@ -41,9 +44,34 @@ class PermissionDelegateHandler final : nsISupports {
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(PermissionDelegateHandler)
bool Initialize();
/*
* Get permission state for permission api with aType, which applied
* Indicates if we has the right to make permission request with aType
*/
bool HasPermissionDelegated(const nsACString& aType);
/*
* Get permission state, which applied permission delegate policy.
*
* @param aType the permission type to get
* @param aPermission out argument which will be a permission type that we
* will return from this function.
* @param aExactHostMatch whether to look for the exact host name or also for
* subdomains that can have the same permission.
*/
nsresult GetPermission(const nsACString& aType, uint32_t* aPermission,
bool aExactHostMatch);
/*
* Get permission state for permission api, which applied
* permission delegate policy.
*
* @param aType the permission type to get
* @param aExactHostMatch whether to look for the exact host name or also for
* subdomains that can have the same permission.
* @param aPermission out argument which will be a permission type that we
* will return from this function.
*/
nsresult GetPermissionForPermissionsAPI(const nsACString& aType,
uint32_t* aPermission);
@ -91,13 +119,18 @@ class PermissionDelegateHandler final : nsISupports {
virtual ~PermissionDelegateHandler() = default;
/*
* Helper function to return the delegate info value for aPermissionName.
* Helper function to return the delegate info value for aPermissionName.
* @param aPermissionName the permission name to get
*/
const PermissionDelegateInfo* GetPermissionDelegateInfo(
const nsAString& aPermissionName) const;
// A weak pointer to our document. Nulled out by DropDocumentReference.
mozilla::dom::Document* mDocument;
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMPtr<nsIPrincipal> mTopLevelPrincipal;
RefPtr<nsIPermissionManager> mPermissionManager;
};
#endif // PermissionDelegateHandler_h__