blob: 82a50b7e22dfe085f9a40623fb48b4d467b5336a [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include "base/ranges/algorithm.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/permission_bubble/permission_prompt.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
#include "chrome/browser/ui/views/permissions/permission_prompt_bubble.h"
#include "chrome/browser/ui/views/permissions/permission_prompt_chip.h"
#include "chrome/browser/ui/views/permissions/permission_prompt_quiet_icon.h"
#include "chrome/browser/ui/web_applications/app_browser_controller.h"
#include "chrome/common/webui_url_constants.h"
#include "components/permissions/features.h"
#include "components/permissions/permission_request.h"
#include "components/permissions/permission_uma_util.h"
#include "components/permissions/request_type.h"
#include "content/public/browser/web_contents.h"
namespace {
bool IsFullScreenMode(Browser* browser) {
DCHECK(browser);
// PWA uses the title bar as a substitute for LocationBarView.
if (web_app::AppBrowserController::IsWebApp(browser))
return false;
BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
if (!browser_view)
return false;
LocationBarView* location_bar = browser_view->GetLocationBarView();
return !location_bar || !location_bar->IsDrawn() ||
location_bar->GetWidget()->IsFullscreen();
}
LocationBarView* GetLocationBarView(Browser* browser) {
BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
return browser_view ? browser_view->GetLocationBarView() : nullptr;
}
// A permission request should be auto-ignored if a user interacts with the
// LocationBar. The only exception is the NTP page where the user needs to press
// on a microphone icon to get a permission request.
bool ShouldIgnorePermissionRequest(
content::WebContents* web_contents,
Browser* browser,
permissions::PermissionPrompt::Delegate* delegate) {
DCHECK(web_contents);
DCHECK(browser);
// In case of the NTP, `WebContents::GetVisibleURL()` is equal to
// `chrome://newtab/`, but the `LocationBarView` will be empty.
if (web_contents->GetVisibleURL() == GURL(chrome::kChromeUINewTabURL)) {
return false;
}
LocationBarView* location_bar = GetLocationBarView(browser);
bool can_display_prompt = location_bar && location_bar->IsEditingOrEmpty();
permissions::PermissionUmaUtil::RecordPermissionPromptAttempt(
delegate->Requests(), can_display_prompt);
return can_display_prompt;
}
bool ShouldUseChip(permissions::PermissionPrompt::Delegate* delegate) {
if (!base::FeatureList::IsEnabled(permissions::features::kPermissionChip))
return false;
// Permission request chip should not be shown if `delegate->Requests()` were
// requested without a user gesture.
if (!permissions::PermissionUtil::HasUserGesture(delegate))
return false;
std::vector<permissions::PermissionRequest*> requests = delegate->Requests();
return base::ranges::all_of(
requests, [](permissions::PermissionRequest* request) {
return request
->GetRequestChipText(
permissions::PermissionRequest::ChipTextType::LOUD_REQUEST)
.has_value();
});
}
bool IsLocationBarDisplayed(Browser* browser) {
LocationBarView* lbv = GetLocationBarView(browser);
return lbv && lbv->IsDrawn() && !lbv->GetWidget()->IsFullscreen();
}
bool ShouldCurrentRequestUseQuietChip(
permissions::PermissionPrompt::Delegate* delegate) {
if (!base::FeatureList::IsEnabled(
permissions::features::kPermissionQuietChip)) {
return false;
}
std::vector<permissions::PermissionRequest*> requests = delegate->Requests();
return base::ranges::all_of(
requests, [](permissions::PermissionRequest* request) {
return request->request_type() ==
permissions::RequestType::kNotifications ||
request->request_type() ==
permissions::RequestType::kGeolocation;
});
}
std::unique_ptr<permissions::PermissionPrompt> CreatePwaPrompt(
Browser* browser,
content::WebContents* web_contents,
permissions::PermissionPrompt::Delegate* delegate) {
if (delegate->ShouldCurrentRequestUseQuietUI()) {
return std::make_unique<PermissionPromptQuietIcon>(browser, web_contents,
delegate);
} else {
return std::make_unique<PermissionPromptBubble>(browser, web_contents,
delegate);
}
}
std::unique_ptr<permissions::PermissionPrompt> CreateNormalPrompt(
Browser* browser,
content::WebContents* web_contents,
permissions::PermissionPrompt::Delegate* delegate) {
DCHECK(!delegate->ShouldCurrentRequestUseQuietUI());
if (ShouldUseChip(delegate) && IsLocationBarDisplayed(browser)) {
return std::make_unique<PermissionPromptChip>(browser, web_contents,
delegate);
} else {
return std::make_unique<PermissionPromptBubble>(browser, web_contents,
delegate);
}
}
std::unique_ptr<permissions::PermissionPrompt> CreateQuietPrompt(
Browser* browser,
content::WebContents* web_contents,
permissions::PermissionPrompt::Delegate* delegate) {
if (ShouldCurrentRequestUseQuietChip(delegate)) {
if (IsLocationBarDisplayed(browser)) {
return std::make_unique<PermissionPromptChip>(browser, web_contents,
delegate);
} else {
// If LocationBar is not displayed (Fullscreen mode), display a default
// bubble only for non-abusive origins.
DCHECK(!delegate->ShouldDropCurrentRequestIfCannotShowQuietly());
return std::make_unique<PermissionPromptBubble>(browser, web_contents,
delegate);
}
} else {
return std::make_unique<PermissionPromptQuietIcon>(browser, web_contents,
delegate);
}
}
} // namespace
std::unique_ptr<permissions::PermissionPrompt> CreatePermissionPrompt(
content::WebContents* web_contents,
permissions::PermissionPrompt::Delegate* delegate) {
Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
if (!browser) {
DLOG(WARNING) << "Permission prompt suppressed because the WebContents is "
"not attached to any Browser window.";
return nullptr;
}
if (delegate->ShouldDropCurrentRequestIfCannotShowQuietly() &&
IsFullScreenMode(browser)) {
return nullptr;
}
// Auto-ignore the permission request if a user is typing into location bar.
if (ShouldIgnorePermissionRequest(web_contents, browser, delegate)) {
return nullptr;
}
if (web_app::AppBrowserController::IsWebApp(browser)) {
return CreatePwaPrompt(browser, web_contents, delegate);
} else if (delegate->ShouldCurrentRequestUseQuietUI()) {
return CreateQuietPrompt(browser, web_contents, delegate);
}
return CreateNormalPrompt(browser, web_contents, delegate);
}