[Messages] Migrate notification blocked infobar to messages
This Cl only includes the changes of migrating the infobar
to message ui. The changes of introducing the dialog
will be in a followed up CL.
Mock: https://screenshot.googleplex.com/4upv5eUL6sze7ac
Screenshot: https://hsv.googleplex.com/4876989703913472
Bug: 1230927
Change-Id: I88f1ee7fadc8fc294e4813245b3e26719934cf48
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3105005
Commit-Queue: Lijin Shen <[email protected]>
Reviewed-by: Balazs Engedy <[email protected]>
Reviewed-by: Pavel Yatsuk <[email protected]>
Cr-Commit-Position: refs/heads/main@{#941988}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 3b3b56bf..0ebcc0de 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3243,6 +3243,8 @@
"payments/android/service_worker_payment_app_bridge.cc",
"permissions/grouped_permission_infobar_delegate_android.cc",
"permissions/grouped_permission_infobar_delegate_android.h",
+ "permissions/notification_blocked_message_delegate_android.cc",
+ "permissions/notification_blocked_message_delegate_android.h",
"permissions/permission_update_infobar_delegate_android.cc",
"permissions/permission_update_infobar_delegate_android.h",
"permissions/permission_update_message_controller_android.cc",
diff --git a/chrome/browser/permissions/chrome_permissions_client.cc b/chrome/browser/permissions/chrome_permissions_client.cc
index e85b82d..677cd61 100644
--- a/chrome/browser/permissions/chrome_permissions_client.cc
+++ b/chrome/browser/permissions/chrome_permissions_client.cc
@@ -49,10 +49,12 @@
#include "chrome/browser/android/resource_mapper.h"
#include "chrome/browser/android/search_permissions/search_permissions_service.h"
#include "chrome/browser/permissions/grouped_permission_infobar_delegate_android.h"
+#include "chrome/browser/permissions/notification_blocked_message_delegate_android.h"
#include "chrome/browser/permissions/permission_update_infobar_delegate_android.h"
#include "chrome/browser/permissions/permission_update_message_controller_android.h"
#include "components/infobars/content/content_infobar_manager.h"
#include "components/messages/android/messages_feature.h"
+#include "components/permissions/permission_request_manager.h"
#else
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/ui/permission_bubble/permission_prompt.h"
@@ -65,6 +67,20 @@
#include "components/user_manager/user_manager.h"
#endif
+namespace {
+
+#if defined(OS_ANDROID)
+bool ShouldUseQuietUI(content::WebContents* web_contents,
+ ContentSettingsType type) {
+ auto* manager =
+ permissions::PermissionRequestManager::FromWebContents(web_contents);
+ return type == ContentSettingsType::NOTIFICATIONS &&
+ manager->ShouldCurrentRequestUseQuietUI();
+}
+#endif
+
+} // namespace
+
// static
ChromePermissionsClient* ChromePermissionsClient::GetInstance() {
static base::NoDestructor<ChromePermissionsClient> instance;
@@ -389,15 +405,32 @@
base::WeakPtr<permissions::PermissionPromptAndroid> prompt) {
infobars::ContentInfoBarManager* infobar_manager =
infobars::ContentInfoBarManager::FromWebContents(web_contents);
- if (infobar_manager &&
- GroupedPermissionInfoBarDelegate::ShouldShowMiniInfobar(web_contents,
- type)) {
+ if (infobar_manager && ShouldUseQuietUI(web_contents, type)) {
return GroupedPermissionInfoBarDelegate::Create(std::move(prompt),
infobar_manager);
}
return nullptr;
}
+messages::MessageWrapper* ChromePermissionsClient::MaybeCreateMessageUI(
+ content::WebContents* web_contents,
+ ContentSettingsType type,
+ base::WeakPtr<permissions::PermissionPromptAndroid> prompt) {
+ if (messages::IsNotificationBlockedMessagesUiEnabled() &&
+ ShouldUseQuietUI(web_contents, type)) {
+ NotificationBlockedMessageDelegate::CreateForWebContents(web_contents);
+ auto* notification_blocked_message_delegate =
+ NotificationBlockedMessageDelegate::FromWebContents(web_contents);
+ auto delegate =
+ std::make_unique<NotificationBlockedMessageDelegate::Delegate>(
+ std::move(prompt));
+ return notification_blocked_message_delegate->ShowMessage(
+ std::move(delegate));
+ }
+
+ return nullptr;
+}
+
void ChromePermissionsClient::RepromptForAndroidPermissions(
content::WebContents* web_contents,
const std::vector<ContentSettingsType>& content_settings_types,
diff --git a/chrome/browser/permissions/chrome_permissions_client.h b/chrome/browser/permissions/chrome_permissions_client.h
index 61ea22316..3782d53 100644
--- a/chrome/browser/permissions/chrome_permissions_client.h
+++ b/chrome/browser/permissions/chrome_permissions_client.h
@@ -89,6 +89,10 @@
content::WebContents* web_contents,
ContentSettingsType type,
base::WeakPtr<permissions::PermissionPromptAndroid> prompt) override;
+ messages::MessageWrapper* MaybeCreateMessageUI(
+ content::WebContents* web_contents,
+ ContentSettingsType type,
+ base::WeakPtr<permissions::PermissionPromptAndroid> prompt) override;
void RepromptForAndroidPermissions(
content::WebContents* web_contents,
const std::vector<ContentSettingsType>& content_settings_types,
diff --git a/chrome/browser/permissions/grouped_permission_infobar_delegate_android.cc b/chrome/browser/permissions/grouped_permission_infobar_delegate_android.cc
index 622cf8eb..dab833b 100644
--- a/chrome/browser/permissions/grouped_permission_infobar_delegate_android.cc
+++ b/chrome/browser/permissions/grouped_permission_infobar_delegate_android.cc
@@ -20,7 +20,6 @@
#include "components/permissions/permission_util.h"
#include "components/strings/grit/components_strings.h"
#include "components/url_formatter/elide_url.h"
-#include "content/public/browser/web_contents.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/strings/grit/ui_strings.h"
@@ -230,16 +229,6 @@
return true;
}
-// static
-bool GroupedPermissionInfoBarDelegate::ShouldShowMiniInfobar(
- content::WebContents* web_contents,
- ContentSettingsType type) {
- auto* manager =
- permissions::PermissionRequestManager::FromWebContents(web_contents);
- return type == ContentSettingsType::NOTIFICATIONS &&
- manager->ShouldCurrentRequestUseQuietUI();
-}
-
GroupedPermissionInfoBarDelegate::GroupedPermissionInfoBarDelegate(
const base::WeakPtr<permissions::PermissionPromptAndroid>&
permission_prompt,
diff --git a/chrome/browser/permissions/grouped_permission_infobar_delegate_android.h b/chrome/browser/permissions/grouped_permission_infobar_delegate_android.h
index 90efdbb2..10dcfd6 100644
--- a/chrome/browser/permissions/grouped_permission_infobar_delegate_android.h
+++ b/chrome/browser/permissions/grouped_permission_infobar_delegate_android.h
@@ -15,10 +15,6 @@
class ContentInfoBarManager;
}
-namespace content {
-class WebContents;
-}
-
namespace permissions {
class PermissionPromptAndroid;
}
@@ -68,10 +64,6 @@
bool Accept() override;
bool Cancel() override;
- // Returns true if we should show the permission request as a mini-infobar.
- static bool ShouldShowMiniInfobar(content::WebContents* web_contents,
- ContentSettingsType type);
-
private:
GroupedPermissionInfoBarDelegate(
const base::WeakPtr<permissions::PermissionPromptAndroid>&
diff --git a/chrome/browser/permissions/notification_blocked_message_delegate_android.cc b/chrome/browser/permissions/notification_blocked_message_delegate_android.cc
new file mode 100644
index 0000000..e0e6d43
--- /dev/null
+++ b/chrome/browser/permissions/notification_blocked_message_delegate_android.cc
@@ -0,0 +1,133 @@
+// Copyright 2021 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/notification_blocked_message_delegate_android.h"
+
+#include "chrome/browser/android/android_theme_resources.h"
+#include "chrome/browser/android/resource_mapper.h"
+#include "chrome/browser/permissions/quiet_notification_permission_ui_config.h"
+#include "chrome/browser/permissions/quiet_notification_permission_ui_state.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/messages/android/message_dispatcher_bridge.h"
+#include "components/permissions/android/permission_prompt_android.h"
+#include "components/permissions/permission_request.h"
+#include "components/permissions/permission_request_manager.h"
+#include "components/permissions/permission_ui_selector.h"
+#include "components/permissions/permission_util.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/url_formatter/elide_url.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/strings/grit/ui_strings.h"
+
+NotificationBlockedMessageDelegate::~NotificationBlockedMessageDelegate() {
+ DismissInternal();
+}
+
+NotificationBlockedMessageDelegate::NotificationBlockedMessageDelegate(
+ content::WebContents* web_contents)
+ : web_contents_(web_contents) {}
+
+messages::MessageWrapper* NotificationBlockedMessageDelegate::ShowMessage(
+ std::unique_ptr<Delegate> delegate) {
+ if (message_) {
+ // Don't show new message UI until the current message has been destroyed,
+ // such as when prompt dialog has been closed or accpeted.
+ return nullptr;
+ }
+ delegate_ = std::move(delegate);
+ message_ = std::make_unique<messages::MessageWrapper>(
+ messages::MessageIdentifier::NOTIFICATION_BLOCKED,
+ base::BindOnce(
+ &NotificationBlockedMessageDelegate::HandlePrimaryActionClick,
+ base::Unretained(this)),
+ base::BindOnce(&NotificationBlockedMessageDelegate::HandleDismissCallback,
+ base::Unretained(this)));
+ message_->SetTitle(l10n_util::GetStringUTF16(
+ IDS_NOTIFICATION_QUIET_PERMISSION_INFOBAR_TITLE));
+
+ // IDS_OK: notification will still be blocked if primary button is clicked.
+ message_->SetPrimaryButtonText(l10n_util::GetStringUTF16(IDS_OK));
+ message_->SetIconResourceId(ResourceMapper::MapToJavaDrawableId(
+ IDR_ANDROID_INFOBAR_NOTIFICATIONS_OFF));
+ message_->SetSecondaryIconResourceId(
+ ResourceMapper::MapToJavaDrawableId(IDR_ANDROID_MESSAGE_SETTINGS));
+ message_->SetSecondaryButtonMenuText(
+ l10n_util::GetStringUTF16(IDS_NOTIFICATION_BUTTON_MANAGE));
+
+ message_->SetSecondaryActionCallback(
+ base::BindOnce(&NotificationBlockedMessageDelegate::HandleManageClick,
+ base::Unretained(this)));
+ messages::MessageDispatcherBridge::Get()->EnqueueMessage(
+ message_.get(), web_contents_, messages::MessageScopeType::NAVIGATION,
+ messages::MessagePriority::kNormal);
+ return message_.get();
+}
+
+void NotificationBlockedMessageDelegate::HandlePrimaryActionClick() {
+ if (!delegate_ || delegate_->IsPromptDestroyed())
+ return;
+
+ DCHECK(delegate_->ShouldUseQuietUI());
+ delegate_->Deny();
+}
+
+void NotificationBlockedMessageDelegate::HandleManageClick() {
+ // TODO(crbug.com/1230927): Implement the flow of showing dialogs
+}
+
+void NotificationBlockedMessageDelegate::HandleDismissCallback(
+ messages::DismissReason reason) {
+ // When message is dismissed by secondary action, |permission_prompt_| should
+ // be reset when the dialog is dismissed.
+ if (reason != messages::DismissReason::SECONDARY_ACTION && delegate_ &&
+ !delegate_->IsPromptDestroyed()) {
+ delegate_->Closing();
+ }
+ delegate_.reset();
+ message_.reset();
+}
+
+void NotificationBlockedMessageDelegate::DismissInternal() {
+ if (message_) {
+ messages::MessageDispatcherBridge::Get()->DismissMessage(
+ message_.get(), messages::DismissReason::UNKNOWN);
+ }
+}
+
+void NotificationBlockedMessageDelegate::Delegate::Accept() {}
+
+void NotificationBlockedMessageDelegate::Delegate::Deny() {
+ if (!permission_prompt_)
+ return;
+ permission_prompt_->Deny();
+}
+
+void NotificationBlockedMessageDelegate::Delegate::Closing() {
+ if (!permission_prompt_)
+ return;
+ permission_prompt_->Closing();
+ permission_prompt_.reset();
+}
+
+bool NotificationBlockedMessageDelegate::Delegate::IsPromptDestroyed() {
+ return !permission_prompt_;
+}
+
+bool NotificationBlockedMessageDelegate::Delegate::ShouldUseQuietUI() {
+ return permission_prompt_->ShouldCurrentRequestUseQuietUI();
+}
+
+NotificationBlockedMessageDelegate::Delegate::~Delegate() {
+ Closing();
+}
+
+NotificationBlockedMessageDelegate::Delegate::Delegate() {}
+
+NotificationBlockedMessageDelegate::Delegate::Delegate(
+ const base::WeakPtr<permissions::PermissionPromptAndroid>&
+ permission_prompt)
+ : permission_prompt_(permission_prompt) {}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(NotificationBlockedMessageDelegate);
diff --git a/chrome/browser/permissions/notification_blocked_message_delegate_android.h b/chrome/browser/permissions/notification_blocked_message_delegate_android.h
new file mode 100644
index 0000000..2fc17bc
--- /dev/null
+++ b/chrome/browser/permissions/notification_blocked_message_delegate_android.h
@@ -0,0 +1,72 @@
+// Copyright 2021 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_NOTIFICATION_BLOCKED_MESSAGE_DELEGATE_ANDROID_H_
+#define CHROME_BROWSER_PERMISSIONS_NOTIFICATION_BLOCKED_MESSAGE_DELEGATE_ANDROID_H_
+
+#include <memory>
+
+#include "base/callback.h"
+#include "components/content_settings/core/common/content_settings_types.h"
+#include "components/messages/android/message_enums.h"
+#include "components/messages/android/message_wrapper.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace permissions {
+class PermissionPromptAndroid;
+}
+
+// A message ui that displays a notification permission request, which is an
+// alternative ui to the mini infobar.
+class NotificationBlockedMessageDelegate
+ : public content::WebContentsUserData<NotificationBlockedMessageDelegate> {
+ public:
+ // Delegate to mock out the |PermissionPromptAndroid| for testing.
+ class Delegate {
+ public:
+ Delegate();
+ Delegate(const base::WeakPtr<permissions::PermissionPromptAndroid>&
+ permission_prompt);
+ virtual ~Delegate();
+ virtual void Accept();
+ virtual void Deny();
+ virtual void Closing();
+ virtual bool IsPromptDestroyed();
+ virtual bool ShouldUseQuietUI();
+
+ private:
+ base::WeakPtr<permissions::PermissionPromptAndroid> permission_prompt_;
+ };
+
+ ~NotificationBlockedMessageDelegate() override;
+
+ // Returns pointer to the message wrapper if a new message UIs has been
+ // created and will be shown. Returns nullptr if not created.
+ messages::MessageWrapper* ShowMessage(std::unique_ptr<Delegate> delegate);
+
+ private:
+ friend class content::WebContentsUserData<NotificationBlockedMessageDelegate>;
+ friend class NotificationBlockedMessageDelegateAndroidTest;
+
+ explicit NotificationBlockedMessageDelegate(
+ content::WebContents* web_contents);
+
+ void HandlePrimaryActionClick();
+ void HandleDismissCallback(messages::DismissReason reason);
+ void HandleManageClick();
+
+ void DismissInternal();
+
+ std::unique_ptr<messages::MessageWrapper> message_;
+ std::unique_ptr<Delegate> delegate_;
+ content::WebContents* web_contents_ = nullptr;
+
+ WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+#endif // CHROME_BROWSER_PERMISSIONS_NOTIFICATION_BLOCKED_MESSAGE_DELEGATE_ANDROID_H_
diff --git a/chrome/browser/permissions/notification_blocked_message_delegate_android_unittest.cc b/chrome/browser/permissions/notification_blocked_message_delegate_android_unittest.cc
new file mode 100644
index 0000000..b45cad3
--- /dev/null
+++ b/chrome/browser/permissions/notification_blocked_message_delegate_android_unittest.cc
@@ -0,0 +1,139 @@
+// Copyright 2021 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/notification_blocked_message_delegate_android.h"
+
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/messages/android/mock_message_dispatcher_bridge.h"
+#include "components/permissions/permission_prompt.h"
+#include "components/permissions/permission_request_manager.h"
+#include "components/permissions/test/mock_permission_prompt_factory.h"
+
+class MockDelegate : public NotificationBlockedMessageDelegate::Delegate {
+ public:
+ ~MockDelegate() override = default;
+ MockDelegate(const base::WeakPtr<permissions::PermissionPromptAndroid>&
+ permission_prompt) {}
+
+ MOCK_METHOD(void, Accept, (), (override));
+ MOCK_METHOD(void, Deny, (), (override));
+
+ MOCK_METHOD(void, Closing, (), (override));
+ MOCK_METHOD(bool, IsPromptDestroyed, (), (override));
+
+ MOCK_METHOD(bool, ShouldUseQuietUI, (), (override));
+};
+
+class NotificationBlockedMessageDelegateAndroidTest
+ : public ChromeRenderViewHostTestHarness {
+ public:
+ NotificationBlockedMessageDelegateAndroidTest() = default;
+ ~NotificationBlockedMessageDelegateAndroidTest() override = default;
+
+ NotificationBlockedMessageDelegate* GetController() { return controller_; }
+ void ExpectEnqueued() {
+ EXPECT_CALL(message_dispatcher_bridge_, EnqueueMessage);
+ }
+
+ void TriggerDismiss(messages::DismissReason reason) {
+ EXPECT_CALL(message_dispatcher_bridge_, DismissMessage)
+ .WillOnce([&reason](messages::MessageWrapper* message,
+ messages::DismissReason dismiss_reason) {
+ message->HandleDismissCallback(base::android::AttachCurrentThread(),
+ static_cast<int>(reason));
+ });
+ controller_->DismissInternal();
+ EXPECT_EQ(nullptr, controller_->message_.get());
+ }
+
+ void TriggerPrimaryAction() {
+ controller_->HandlePrimaryActionClick();
+ TriggerDismiss(messages::DismissReason::PRIMARY_ACTION);
+ }
+
+ messages::MessageWrapper* GetMessageWrapper() {
+ return controller_->message_.get();
+ }
+
+ std::unique_ptr<MockDelegate> GetMockDelegate() {
+ return std::move(delegate_);
+ }
+
+ protected:
+ void SetUp() override;
+ void TearDown() override;
+
+ private:
+ NotificationBlockedMessageDelegate* controller_;
+ messages::MockMessageDispatcherBridge message_dispatcher_bridge_;
+ std::unique_ptr<MockDelegate> delegate_;
+};
+
+void NotificationBlockedMessageDelegateAndroidTest::SetUp() {
+ content::RenderViewHostTestHarness::SetUp();
+ permissions::PermissionRequestManager::CreateForWebContents(web_contents());
+ NotificationBlockedMessageDelegate::CreateForWebContents(web_contents());
+ controller_ =
+ NotificationBlockedMessageDelegate::FromWebContents(web_contents());
+ messages::MessageDispatcherBridge::SetInstanceForTesting(
+ &message_dispatcher_bridge_);
+ delegate_ = std::make_unique<MockDelegate>(nullptr);
+}
+
+void NotificationBlockedMessageDelegateAndroidTest::TearDown() {
+ messages::MessageDispatcherBridge::SetInstanceForTesting(nullptr);
+ ChromeRenderViewHostTestHarness::TearDown();
+}
+
+TEST_F(NotificationBlockedMessageDelegateAndroidTest, DismissByTimeout) {
+ auto delegate = GetMockDelegate();
+ EXPECT_CALL(*delegate, IsPromptDestroyed)
+ .WillRepeatedly(testing::Return(false));
+
+ EXPECT_CALL(*delegate, Closing);
+ EXPECT_CALL(*delegate, Accept).Times(0);
+ EXPECT_CALL(*delegate, Deny).Times(0);
+
+ ExpectEnqueued();
+
+ GetController()->ShowMessage(std::move(delegate));
+ TriggerDismiss(messages::DismissReason::TIMER);
+ EXPECT_EQ(nullptr, GetMessageWrapper());
+}
+
+TEST_F(NotificationBlockedMessageDelegateAndroidTest, DismissByPrimaryAction) {
+ auto delegate = GetMockDelegate();
+ EXPECT_CALL(*delegate, IsPromptDestroyed)
+ .WillRepeatedly(testing::Return(false));
+ EXPECT_CALL(*delegate, ShouldUseQuietUI)
+ .WillRepeatedly(testing::Return(true));
+
+ EXPECT_CALL(*delegate, Closing);
+ EXPECT_CALL(*delegate, Accept).Times(0);
+ EXPECT_CALL(*delegate, Deny);
+
+ ExpectEnqueued();
+
+ GetController()->ShowMessage(std::move(delegate));
+ TriggerPrimaryAction();
+ EXPECT_EQ(nullptr, GetMessageWrapper());
+}
+
+TEST_F(NotificationBlockedMessageDelegateAndroidTest,
+ DismissByPrimaryActionWhenPromptDestroyed) {
+ auto delegate = GetMockDelegate();
+ EXPECT_CALL(*delegate, IsPromptDestroyed)
+ .WillRepeatedly(testing::Return(true));
+ EXPECT_CALL(*delegate, ShouldUseQuietUI)
+ .WillRepeatedly(testing::Return(true));
+
+ EXPECT_CALL(*delegate, Closing).Times(0);
+ EXPECT_CALL(*delegate, Accept).Times(0);
+ EXPECT_CALL(*delegate, Deny).Times(0);
+
+ ExpectEnqueued();
+ GetController()->ShowMessage(std::move(delegate));
+ TriggerPrimaryAction();
+ EXPECT_EQ(nullptr, GetMessageWrapper());
+}
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 43c6f14a..9a4a84b9 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -4939,6 +4939,7 @@
"../browser/notifications/notification_channels_provider_android_unittest.cc",
"../browser/optimization_guide/android/optimization_guide_tab_url_provider_android_unittest.cc",
"../browser/password_manager/android/password_ui_view_android_unittest.cc",
+ "../browser/permissions/notification_blocked_message_delegate_android_unittest.cc",
"../browser/search/contextual_search_policy_handler_android_unittest.cc",
"../browser/translate/android/translate_bridge_unittest.cc",
"../browser/ui/android/autofill/save_card_message_controller_android_unittest.cc",