Commit ed2c2176 authored by Balazs Engedy's avatar Balazs Engedy Committed by Commit Bot

Add ContextualNotificationPermissionUiSelector.

ContextualNotificationPermissionUiSelector determines if the quiet prompt UI
should be used to display a notification permission request on a given site.

This is the case when:
  1) the quiet UI is enabled in prefs for all sites, either directly by the
     user in settings, or by the AdaptiveQuietNotificationPermissionUiEnabler.
  2) the quiet UI is triggered by crowd deny, either through:
     a) CrowdDenyPreloadData, that is, the component updater, or
     b) CrowdDenySafeBrowsingRequest, that is, on-demand Safe Browsing pings.

The ContextualNotificationPermissionUiSelector is also wired up to the
PermissionRequestManager instead NotificationPermissionUiSelectorBasedOnPrefs.

Bug: 1028642
Change-Id: Ibb44ec1e0f0fe95d4fb350e9c2211199719df103
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1944395
Commit-Queue: Balazs Engedy <engedy@chromium.org>
Reviewed-by: default avatarAndy Paicu <andypaicu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#721748}
parent c530ad90
......@@ -1175,6 +1175,8 @@ jumbo_static_library("browser") {
"permissions/adaptive_quiet_notification_permission_ui_enabler.h",
"permissions/chooser_context_base.cc",
"permissions/chooser_context_base.h",
"permissions/contextual_notification_permission_ui_selector.cc",
"permissions/contextual_notification_permission_ui_selector.h",
"permissions/crowd_deny_preload_data.cc",
"permissions/crowd_deny_preload_data.h",
"permissions/crowd_deny_safe_browsing_request.cc",
......
......@@ -1313,13 +1313,15 @@ const FeatureEntry::FeatureVariation
const FeatureEntry::FeatureParam
kQuietNotificationPromptsWithAdaptiveActivation[] = {
{QuietNotificationPermissionUiConfig::kEnableAdaptiveActivation,
"true"},
{QuietNotificationPermissionUiConfig::kEnableCrowdDenyTriggering,
"true"}};
// The default "Enabled" option has the semantics of showing the quiet UI
// (animated location bar indicator on Desktop, and mini-infobars on Android),
// but only when the user directly turns it on in Settings. In addition to that,
// expose an option to also enable adaptively turning on the quiet UI after
// three consecutive denies.
// three consecutive denies or based on crowd deny verdicts.
const FeatureEntry::FeatureVariation kQuietNotificationPromptsVariations[] = {
{"(with adaptive activation)",
kQuietNotificationPromptsWithAdaptiveActivation,
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/permissions/contextual_notification_permission_ui_selector.h"
#include <utility>
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/task/post_task.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/permissions/crowd_deny_preload_data.h"
#include "chrome/browser/permissions/permission_request.h"
#include "chrome/browser/permissions/quiet_notification_permission_ui_config.h"
#include "chrome/browser/permissions/quiet_notification_permission_ui_state.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "components/safe_browsing/db/database_manager.h"
namespace {
using UiToUse = ContextualNotificationPermissionUiSelector::UiToUse;
using QuietUiReason = ContextualNotificationPermissionUiSelector::QuietUiReason;
// Attempts to decide which UI to use based on preloaded site reputation data,
// or returns base::nullopt if not possible. |site_reputation| can be nullptr.
base::Optional<UiToUse> GetUiToUseBasedOnSiteReputation(
const CrowdDenyPreloadData::SiteReputation* site_reputation) {
if (!site_reputation)
return base::nullopt;
switch (site_reputation->notification_ux_quality()) {
case CrowdDenyPreloadData::SiteReputation::ACCEPTABLE:
return UiToUse::kNormalUi;
case CrowdDenyPreloadData::SiteReputation::UNSOLICITED_PROMPTS:
return UiToUse::kQuietUi;
case CrowdDenyPreloadData::SiteReputation::UNKNOWN:
return base::nullopt;
}
NOTREACHED();
return base::nullopt;
}
// Decides which UI to use based on the Safe Browsing verdict.
UiToUse GetUiToUseFromSafeBrowsingVerdict(
CrowdDenySafeBrowsingRequest::Verdict verdict) {
using Verdict = CrowdDenySafeBrowsingRequest::Verdict;
switch (verdict) {
case Verdict::kAcceptable:
return UiToUse::kNormalUi;
case Verdict::kKnownToShowUnsolicitedNotificationPermissionRequests:
return UiToUse::kQuietUi;
}
NOTREACHED();
return UiToUse::kNormalUi;
}
} // namespace
ContextualNotificationPermissionUiSelector::
ContextualNotificationPermissionUiSelector(Profile* profile)
: profile_(profile) {}
void ContextualNotificationPermissionUiSelector::SelectUiToUse(
PermissionRequest* request,
DecisionMadeCallback callback) {
callback_ = std::move(callback);
DCHECK(callback_);
if (QuietNotificationPermissionUiConfig::UiFlavorToUse() ==
QuietNotificationPermissionUiConfig::NONE) {
Notify(UiToUse::kNormalUi, base::nullopt);
return;
}
if (QuietNotificationPermissionUiState::IsQuietUiEnabledInPrefs(profile_)) {
Notify(UiToUse::kQuietUi, QuietUiReason::kEnabledInPrefs);
return;
}
if (!QuietNotificationPermissionUiConfig::IsCrowdDenyTriggeringEnabled()) {
Notify(UiToUse::kNormalUi, base::nullopt);
return;
}
const auto origin = url::Origin::Create(request->GetOrigin());
base::Optional<UiToUse> ui_to_use = GetUiToUseBasedOnSiteReputation(
CrowdDenyPreloadData::GetInstance()->GetReputationDataForSite(origin));
if (ui_to_use) {
Notify(*ui_to_use, QuietUiReason::kTriggeredByCrowdDeny);
return;
}
DCHECK(!safe_browsing_request_);
DCHECK(g_browser_process->safe_browsing_service());
// It is fine to use base::Unretained() here, as |safe_browsing_request_|
// guarantees not to fire the callback after its destruction.
safe_browsing_request_.emplace(
g_browser_process->safe_browsing_service()->database_manager(), origin,
base::BindOnce(&ContextualNotificationPermissionUiSelector::
OnSafeBrowsingVerdictReceived,
base::Unretained(this)));
}
void ContextualNotificationPermissionUiSelector::Cancel() {
// The computation either finishes synchronously above, or is waiting on the
// Safe Browsing check.
safe_browsing_request_.reset();
}
ContextualNotificationPermissionUiSelector::
~ContextualNotificationPermissionUiSelector() = default;
void ContextualNotificationPermissionUiSelector::OnSafeBrowsingVerdictReceived(
CrowdDenySafeBrowsingRequest::Verdict verdict) {
DCHECK(safe_browsing_request_);
DCHECK(callback_);
safe_browsing_request_.reset();
Notify(GetUiToUseFromSafeBrowsingVerdict(verdict),
QuietUiReason::kTriggeredByCrowdDeny);
}
void ContextualNotificationPermissionUiSelector::Notify(
UiToUse ui_to_use,
base::Optional<QuietUiReason> quiet_ui_reason) {
if (ui_to_use != UiToUse::kQuietUi)
quiet_ui_reason = base::nullopt;
std::move(callback_).Run(ui_to_use, quiet_ui_reason);
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_PERMISSIONS_CONTEXTUAL_NOTIFICATION_PERMISSION_UI_SELECTOR_H_
#define CHROME_BROWSER_PERMISSIONS_CONTEXTUAL_NOTIFICATION_PERMISSION_UI_SELECTOR_H_
#include "base/callback.h"
#include "base/optional.h"
#include "chrome/browser/permissions/crowd_deny_safe_browsing_request.h"
#include "chrome/browser/permissions/notification_permission_ui_selector.h"
class Profile;
class PermissionRequest;
// Determines if the quiet prompt UI should be used to display a notification
// permission request on a given site. This is the case when:
// 1) the quiet UI is enabled in prefs for all sites, either directly by the
// user in settings, or by the AdaptiveQuietNotificationPermissionUiEnabler.
// 2) the quiet UI is triggered by crowd deny, either through:
// a) CrowdDenyPreloadData, that is, the component updater, or
// b) CrowdDenySafeBrowsingRequest, that is, on-demand Safe Browsing pings.
//
// Each instance of this class is long-lived and can support multiple requests,
// but only one at a time.
class ContextualNotificationPermissionUiSelector
: public NotificationPermissionUiSelector {
public:
// Constructs an instance in the context of the given |profile|.
explicit ContextualNotificationPermissionUiSelector(Profile* profile);
~ContextualNotificationPermissionUiSelector() override;
// NotificationPermissionUiSelector:
void SelectUiToUse(PermissionRequest* request,
DecisionMadeCallback callback) override;
void Cancel() override;
private:
ContextualNotificationPermissionUiSelector(
const ContextualNotificationPermissionUiSelector&) = delete;
ContextualNotificationPermissionUiSelector& operator=(
const ContextualNotificationPermissionUiSelector&) = delete;
void OnSafeBrowsingVerdictReceived(
CrowdDenySafeBrowsingRequest::Verdict verdict);
void Notify(UiToUse ui_to_use, base::Optional<QuietUiReason> quiet_ui_reason);
Profile* profile_;
base::Optional<CrowdDenySafeBrowsingRequest> safe_browsing_request_;
DecisionMadeCallback callback_;
};
#endif // CHROME_BROWSER_PERMISSIONS_CONTEXTUAL_NOTIFICATION_PERMISSION_UI_SELECTOR_H_
......@@ -16,6 +16,7 @@
#include "base/task/post_task.h"
#include "build/build_config.h"
#include "chrome/browser/permissions/adaptive_quiet_notification_permission_ui_enabler.h"
#include "chrome/browser/permissions/contextual_notification_permission_ui_selector.h"
#include "chrome/browser/permissions/notification_permission_ui_selector.h"
#include "chrome/browser/permissions/permission_decision_auto_blocker.h"
#include "chrome/browser/permissions/permission_request.h"
......@@ -41,35 +42,6 @@
namespace {
class NotificationPermissionUiSelectorBasedOnPrefs
: public NotificationPermissionUiSelector {
public:
explicit NotificationPermissionUiSelectorBasedOnPrefs(Profile* profile)
: profile_(profile) {}
~NotificationPermissionUiSelectorBasedOnPrefs() override = default;
// NotificationPermissionUiSelector:
void SelectUiToUse(PermissionRequest* request,
DecisionMadeCallback callback) override {
if (QuietNotificationPermissionUiConfig::UiFlavorToUse() !=
QuietNotificationPermissionUiConfig::NONE &&
QuietNotificationPermissionUiState::IsQuietUiEnabledInPrefs(profile_)) {
std::move(callback).Run(UiToUse::kQuietUi,
QuietUiReason::kEnabledInPrefs);
} else {
std::move(callback).Run(UiToUse::kNormalUi, base::nullopt);
}
}
private:
NotificationPermissionUiSelectorBasedOnPrefs(
const NotificationPermissionUiSelectorBasedOnPrefs&) = delete;
const NotificationPermissionUiSelectorBasedOnPrefs& operator=(
NotificationPermissionUiSelectorBasedOnPrefs&) = delete;
Profile* profile_;
};
bool IsMessageTextEqual(PermissionRequest* a,
PermissionRequest* b) {
if (a == b)
......@@ -383,10 +355,10 @@ PermissionRequestManager::PermissionRequestManager(
view_(nullptr),
tab_is_hidden_(web_contents->GetVisibility() ==
content::Visibility::HIDDEN),
auto_response_for_test_(NONE) {
notification_permission_ui_selector_ =
std::make_unique<NotificationPermissionUiSelectorBasedOnPrefs>(
Profile::FromBrowserContext(web_contents->GetBrowserContext()));
auto_response_for_test_(NONE),
notification_permission_ui_selector_(
std::make_unique<ContextualNotificationPermissionUiSelector>(
Profile::FromBrowserContext(web_contents->GetBrowserContext()))) {
}
void PermissionRequestManager::ScheduleShowBubble() {
......
......@@ -11,6 +11,10 @@
const char QuietNotificationPermissionUiConfig::kEnableAdaptiveActivation[] =
"enable_adaptive_activation";
// static
const char QuietNotificationPermissionUiConfig::kEnableCrowdDenyTriggering[] =
"enable_crowd_deny_triggering";
QuietNotificationPermissionUiConfig::UiFlavor
QuietNotificationPermissionUiConfig::UiFlavorToUse() {
if (!base::FeatureList::IsEnabled(features::kQuietNotificationPrompts))
......@@ -32,3 +36,13 @@ bool QuietNotificationPermissionUiConfig::IsAdaptiveActivationEnabled() {
features::kQuietNotificationPrompts, kEnableAdaptiveActivation,
false /* default */);
}
// static
bool QuietNotificationPermissionUiConfig::IsCrowdDenyTriggeringEnabled() {
if (!base::FeatureList::IsEnabled(features::kQuietNotificationPrompts))
return false;
return base::GetFieldTrialParamByFeatureAsBool(
features::kQuietNotificationPrompts, kEnableCrowdDenyTriggering,
false /* default */);
}
......@@ -27,6 +27,12 @@ class QuietNotificationPermissionUiConfig {
// consecutive prompt denies.
static const char kEnableAdaptiveActivation[];
// Name of the boolean variation parameter that determines if the quiet
// notification permission prompt UI should be enabled as a one-off based on
// crowd deny data, that is, on sites with a low notification permission grant
// rate.
static const char kEnableCrowdDenyTriggering[];
// Which flavor of the quiet UI to use.
//
// Deprecated: The UI flavor is now hardcoded on each platform and no longer
......@@ -37,6 +43,11 @@ class QuietNotificationPermissionUiConfig {
// that quiet notifications permission prompts will be turned on after three
// consecutive prompt denies.
static bool IsAdaptiveActivationEnabled();
// Whether or not triggering via crowd deny is enabled. This means that on
// sites with a low notification permission grant rate, the quiet UI will be
// shown as a one-off, even when it is not turned on for all sites in prefs.
static bool IsCrowdDenyTriggeringEnabled();
};
#endif // CHROME_BROWSER_PERMISSIONS_QUIET_NOTIFICATION_PERMISSION_UI_CONFIG_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment