| // 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()); |
| } |