blob: 8aa8c90ef7b2107a27ccf73998175258d39c0dfe [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 "chrome/browser/ui/views/permissions/permission_prompt_bubble.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
#include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
#include "chrome/browser/ui/views/permissions/permission_prompt_bubble_base_view.h"
#include "chrome/browser/ui/views/permissions/permission_prompt_bubble_view_factory.h"
#include "chrome/browser/ui/views/permissions/permission_prompt_style.h"
#include "components/permissions/features.h"
#include "content/public/browser/web_contents.h"
#include "ui/display/types/display_constants.h"
PermissionPromptBubble::PermissionPromptBubble(
Browser* browser,
content::WebContents* web_contents,
Delegate* delegate)
: PermissionPromptDesktop(browser, web_contents, delegate) {
LocationBarView* lbv = GetLocationBarView();
if (lbv && lbv->IsDrawn() &&
delegate->Requests()[0]->IsConfirmationChipSupported()) {
lbv->GetChipController()->InitializePermissionPrompt(
delegate->GetWeakPtr(),
base::BindOnce(&PermissionPromptBubble::ShowBubble,
weak_factory_.GetWeakPtr()));
} else {
ShowBubble();
}
}
PermissionPromptBubble::~PermissionPromptBubble() {
CleanUpPromptBubble();
CHECK(!IsInObserverList());
}
void PermissionPromptBubble::ShowBubble() {
FullscreenController* fullscreen_controller = browser()
->GetFeatures()
.exclusive_access_manager()
->fullscreen_controller();
CHECK(fullscreen_controller);
if (fullscreen_controller->IsTabFullscreen()) {
fullscreen_blocker_ =
web_contents()->ForSecurityDropFullscreen(display::kInvalidDisplayId);
}
raw_ptr<PermissionPromptBubbleBaseView> prompt_bubble =
CreatePermissionPromptBubbleView(browser(), delegate()->GetWeakPtr(),
PermissionPromptStyle::kBubbleOnly);
prompt_bubble_tracker_.SetView(prompt_bubble);
prompt_bubble->Show();
prompt_bubble->GetWidget()->AddObserver(this);
parent_was_visible_when_activation_changed_ =
prompt_bubble->GetWidget()->GetPrimaryWindowWidget()->IsVisible();
disallowed_custom_cursors_scope_ =
delegate()->GetAssociatedWebContents()->CreateDisallowCustomCursorScope(
/*max_dimension_dips=*/0);
}
void PermissionPromptBubble::CleanUpPromptBubble() {
if (GetPromptBubble()) {
views::Widget* widget = GetPromptBubble()->GetWidget();
widget->RemoveObserver(this);
widget->CloseWithReason(views::Widget::ClosedReason::kUnspecified);
prompt_bubble_tracker_.SetView(nullptr);
disallowed_custom_cursors_scope_.RunAndReset();
}
}
void PermissionPromptBubble::OnWidgetDestroying(views::Widget* widget) {
widget->RemoveObserver(this);
prompt_bubble_tracker_.SetView(nullptr);
}
void PermissionPromptBubble::OnWidgetActivationChanged(views::Widget* widget,
bool active) {
// This logic prevents clickjacking. See https://crbug.com/1160485
if (active && !parent_was_visible_when_activation_changed_) {
// If the widget is active and the primary window wasn't active the last
// time activation changed, we know that the window just came to the
// foreground and trigger input protection.
GetPromptBubble()->AsDialogDelegate()->TriggerInputProtection(
/*force_early=*/true);
}
parent_was_visible_when_activation_changed_ =
GetPromptBubble()->GetWidget()->GetPrimaryWindowWidget()->IsVisible();
}
std::optional<gfx::Rect> PermissionPromptBubble::GetViewBoundsInScreen() const {
return GetPromptBubble()
? std::make_optional<gfx::Rect>(
GetPromptBubble()->GetWidget()->GetWindowBoundsInScreen())
: std::nullopt;
}
bool PermissionPromptBubble::UpdateAnchor() {
bool was_browser_changed = UpdateBrowser();
// TODO(crbug.com/40747230): Investigate why prompt_bubble_ can be null
// here. Early return is preventing the crash from happening but we still
// don't know the reason why it is null here and cannot reproduce it.
if (!GetPromptBubble()) {
return true;
}
// If |browser_| changed, we need to recreate bubble for correct browser.
if (was_browser_changed) {
CleanUpPromptBubble();
return false;
} else {
GetPromptBubble()->UpdateAnchorPosition();
}
if (!delegate()->Requests().empty() &&
delegate()->Requests()[0]->IsConfirmationChipSupported()) {
// If we have a location bar view but the chip_controller_ doesn't exist,
// it means that the we switched from a browser mode that did not have a
// location bar view. In that case we should create the chip in the location
// bar view if required, then obtain a reference to the chip controller and
// finally initialize it with the current permission request.
LocationBarView* lbv = GetLocationBarView();
if (lbv && lbv->IsDrawn() && !lbv->GetWidget()->IsFullscreen() &&
!lbv->IsEditingOrEmpty()) {
auto* chip_controller = lbv->GetChipController();
chip_controller->InitializePermissionPrompt(delegate()->GetWeakPtr());
}
}
return true;
}
permissions::PermissionPromptDisposition
PermissionPromptBubble::GetPromptDisposition() const {
return permissions::PermissionPromptDisposition::ANCHORED_BUBBLE;
}
views::Widget* PermissionPromptBubble::GetPromptBubbleWidgetForTesting() {
return GetPromptBubble() ? GetPromptBubble()->GetWidget() : nullptr;
}
PermissionPromptBubbleBaseView* PermissionPromptBubble::GetPromptBubble() {
return static_cast<PermissionPromptBubbleBaseView*>(
prompt_bubble_tracker_.view());
}
const PermissionPromptBubbleBaseView* PermissionPromptBubble::GetPromptBubble()
const {
return static_cast<const PermissionPromptBubbleBaseView*>(
prompt_bubble_tracker_.view());
}