| // 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. |
| |
| #ifndef CHROME_BROWSER_UI_VIEWS_PERMISSIONS_CHIP_CHIP_CONTROLLER_H_ |
| #define CHROME_BROWSER_UI_VIEWS_PERMISSIONS_CHIP_CHIP_CONTROLLER_H_ |
| |
| #include <cstddef> |
| #include <memory> |
| |
| #include "base/check_is_test.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/observer_list.h" |
| #include "base/observer_list_types.h" |
| #include "base/timer/timer.h" |
| #include "chrome/browser/ui/views/permissions/chip/permission_chip_view.h" |
| #include "components/permissions/permission_prompt.h" |
| #include "components/permissions/permission_request_manager.h" |
| #include "components/permissions/permission_util.h" |
| #include "ui/views/widget/widget_observer.h" |
| |
| class PermissionPromptChipModel; |
| class LocationBarView; |
| class PermissionDashboardView; |
| class PermissionDashboardController; |
| class PermissionPromptBubbleBaseView; |
| class ContentSettingBubbleContents; |
| |
| // ButtonController that NotifyClick from being called when the |
| // BubbleOwnerDelegate's bubble is showing. Otherwise the bubble will show again |
| // immediately after being closed via losing focus. |
| class BubbleOwnerDelegate { |
| public: |
| virtual bool IsBubbleShowing() = 0; |
| virtual bool IsAnimating() const = 0; |
| virtual void RestartTimersOnMouseHover() = 0; |
| }; |
| |
| // This class controls a chip UI view to surface permission related information |
| // and prompts. For its creation, the controller expects an object of type |
| // PermissionChipView which should be a child view of another view. No ownership |
| // is transferred through the creation, and the controller will never destruct |
| // the PermissionChipView object. The controller and it's view are intended to |
| // be long-lived. |
| class ChipController : public permissions::PermissionRequestManager::Observer, |
| public views::WidgetObserver, |
| public BubbleOwnerDelegate, |
| public PermissionChipView::Observer { |
| public: |
| class Observer : public base::CheckedObserver { |
| public: |
| // Triggered when the permission prompt shows. |
| virtual void OnPermissionPromptShown() = 0; |
| |
| // Triggered when the permission prompt hides. |
| virtual void OnPermissionPromptHidden() = 0; |
| }; |
| |
| ChipController( |
| LocationBarView* location_bar_view, |
| PermissionChipView* chip_view, |
| PermissionDashboardView* permission_dashboard_view = nullptr, |
| PermissionDashboardController* permission_dashboard_controller = nullptr); |
| |
| ~ChipController() override; |
| ChipController(const ChipController&) = delete; |
| ChipController& operator=(const ChipController&) = delete; |
| |
| // PermissionRequestManager::Observer: |
| void OnPermissionRequestManagerDestructed() override; |
| void OnTabActiveChanged(bool is_active) override; |
| // Called when the currently active permission request was finalized. That |
| // could be called independently of `OnRequestDecided`. |
| void OnRequestsFinalized() override; |
| // Called when currently visible permission prompt was removed. That is called |
| // independently from `OnRequestsFinalized` and `OnRequestDecided`. |
| void OnPromptRemoved() override; |
| |
| // OnBubbleRemoved only triggers when a request chip (bubble) is removed, when |
| // the user navigates while a confirmation chip is showing, the request is |
| // already finished and hence OnBubbleRemoved is not triggered. Thus we need |
| // to handle chip cleanup on navigation events separately. |
| void OnNavigation(content::NavigationHandle* navigation_handle) override; |
| |
| // Called when there is a decision for a permission request. |
| void OnRequestDecided(permissions::PermissionAction permissions) override; |
| |
| // BubbleOwnerDelegate: |
| bool IsBubbleShowing() override; |
| bool IsAnimating() const override; |
| void RestartTimersOnMouseHover() override; |
| |
| // WidgetObserver: |
| void OnWidgetDestroying(views::Widget* widget) override; |
| void OnWidgetDestroyed(views::Widget* widget) override; |
| void OnWidgetActivationChanged(views::Widget* widget, bool active) override; |
| |
| // PermissionChipView::Observer |
| void OnChipVisibilityChanged(bool is_visible) override; |
| void OnExpandAnimationEnded() override; |
| void OnCollapseAnimationEnded() override; |
| |
| void AddObserver(Observer* observer); |
| void RemoveObserver(Observer* observer); |
| |
| // Initializes the permission prompt model as well as the permission request |
| // manager and observes the prompt bubble. |
| void InitializePermissionPrompt( |
| base::WeakPtr<permissions::PermissionPrompt::Delegate> delegate, |
| base::OnceCallback<void()> = base::DoNothing()); |
| |
| // Displays a permission chip. Call ShowPermissionPrompt() instead to show |
| // both a chip and a prompt. |
| void ShowPermissionChip( |
| base::WeakPtr<permissions::PermissionPrompt::Delegate> delegate); |
| |
| // Displays a permission prompt using the chip UI. |
| void ShowPermissionPrompt( |
| base::WeakPtr<permissions::PermissionPrompt::Delegate> delegate); |
| |
| // Chip View. |
| PermissionChipView* chip() { return chip_; } |
| |
| // Hide and clean up the chip. |
| void ResetPermissionPromptChip(); |
| |
| // Hide and clean up only if the chip displays a permission request. That |
| // method is no-op if the confirmation chip is displayed. |
| void ResetPermissionRequestChip(); |
| |
| bool IsPermissionPromptChipVisible() { |
| return chip_ && chip_->GetVisible() && permission_prompt_model_; |
| } |
| |
| views::Widget* GetBubbleWidget(); |
| |
| // Returns the currently active permission prompt bubble, specifically when a |
| // non quiet chip UI is expected. This method CHECKs that the prompt style is |
| // `kChip`. |
| PermissionPromptBubbleBaseView* GetPromptBubbleView(); |
| // Returns the currently active permission prompt bubble, specifically when a |
| // quiet chip UI is expected. This method CHECKs that the prompt style is |
| // `kQuietChip`. |
| ContentSettingBubbleContents* GetContentSettingBubbleContentsForTesting(); |
| |
| void ClosePermissionPrompt(); |
| void PromptDecided(permissions::PermissionAction action); |
| |
| PermissionPromptChipModel* permission_prompt_model() { |
| return permission_prompt_model_.get(); |
| } |
| |
| bool should_expand_for_testing(); |
| |
| bool is_collapse_timer_running_for_testing() const { |
| CHECK_IS_TEST(); |
| return collapse_timer_.IsRunning(); |
| } |
| |
| void fire_collapse_timer_for_testing() { |
| CHECK_IS_TEST(); |
| collapse_timer_.FireNow(); |
| } |
| |
| bool is_dismiss_timer_running_for_testing() const { |
| CHECK_IS_TEST(); |
| return dismiss_timer_.IsRunning(); |
| } |
| |
| void stop_animation_for_test() { |
| CHECK_IS_TEST(); |
| chip_->animation_for_testing()->Stop(); |
| OnExpandAnimationEnded(); |
| } |
| |
| views::View* get_prompt_bubble_view_for_testing() { |
| CHECK_IS_TEST(); |
| return bubble_tracker_.view(); |
| } |
| |
| std::optional<permissions::PermissionRequestManager*> |
| active_permission_request_manager() { |
| return active_chip_permission_request_manager_; |
| } |
| |
| bool is_confirmation_showing() const { return is_confirmation_showing_; } |
| |
| bool is_waiting_for_confirmation_collapse_for_testing() const { |
| CHECK_IS_TEST(); |
| return is_waiting_for_confirmation_collapse_; |
| } |
| |
| void set_is_bubble_suppressed(bool is_bubble_suppressed) { |
| is_bubble_suppressed_ = is_bubble_suppressed; |
| } |
| |
| void DoNotCollapseForTesting() { do_no_collapse_for_testing_ = true; } |
| |
| private: |
| FRIEND_TEST_ALL_PREFIXES(PermissionChipUnitTest, AccessibleName); |
| |
| bool ShouldWaitForConfirmationToComplete() const; |
| bool ShouldWaitForLHSIndicatorToCollapse() const; |
| void AnimateExpand(); |
| |
| // Confirmation chip. |
| void HandleConfirmation(permissions::PermissionAction permission_action); |
| void CollapseConfirmation(); |
| |
| // Permission prompt chip functionality. |
| void AnnouncePermissionRequestForAccessibility(const std::u16string& text); |
| void CollapsePrompt(bool allow_restart); |
| |
| // Hides the chip and invalidates the layout. |
| void HideChip(); |
| |
| void ShowPermissionUi( |
| base::WeakPtr<permissions::PermissionPrompt::Delegate> delegate); |
| |
| // Permission prompt bubble functiontionality. |
| void OpenPermissionPromptBubble(); |
| void ClosePermissionPromptBubbleWithReason( |
| views::Widget::ClosedReason reason); |
| |
| void RecordRequestChipButtonPressed(const char* recordKey); |
| |
| // Event handling. |
| void ObservePromptBubble(); |
| void OnPromptBubbleDismissed(); |
| void OnPromptExpired(); |
| void OnRequestChipButtonPressed(); |
| |
| // Updates chip icon, text and theme with model. |
| void SyncChipWithModel(); |
| |
| // Opens the Page Info Dialog. |
| void ShowPageInfoDialog(); |
| |
| // Actions executed when the user closes the page info dialog. |
| void OnPageInfoBubbleClosed(views::Widget::ClosedReason closed_reason, |
| bool reload_prompt); |
| |
| // Clean up utility. |
| void RemoveBubbleObserverAndResetTimersAndChipCallbacks(); |
| |
| // Timer functionality. |
| void StartCollapseTimer(); |
| void StartDismissTimer(); |
| void ResetTimers(); |
| |
| // The location bar view to which the chip is attached. |
| LocationBarView* GetLocationBarView() { return location_bar_view_; } |
| |
| bool is_confirmation_showing_ = false; |
| bool is_waiting_for_confirmation_collapse_ = false; |
| |
| // `LocationBarView` owns this. |
| raw_ptr<LocationBarView> location_bar_view_ = nullptr; |
| // The chip view this controller modifies. |
| raw_ptr<PermissionChipView> chip_; |
| |
| // `PermissionDashboardView` is an owner of PermissionChipView. |
| raw_ptr<PermissionDashboardView> permission_dashboard_view_; |
| // `PermissionDashboardController` is an owner of this. |
| raw_ptr<PermissionDashboardController> permission_dashboard_controller_; |
| |
| // A timer used to dismiss the permission request after it's been collapsed |
| // for a while. |
| base::OneShotTimer dismiss_timer_; |
| |
| // A timer used to collapse the chip after a delay. |
| base::OneShotTimer collapse_timer_; |
| |
| // A timer used to delay showing a prompt if a confirmation chip is being |
| // displayed. |
| base::OneShotTimer delay_prompt_timer_; |
| |
| bool parent_was_visible_when_activation_changed_ = false; |
| |
| // The model of a permission prompt if one is present. |
| std::unique_ptr<PermissionPromptChipModel> permission_prompt_model_; |
| |
| std::optional<permissions::PermissionRequestManager*> |
| active_chip_permission_request_manager_; |
| |
| views::ViewTracker bubble_tracker_; |
| bool is_bubble_suppressed_ = false; |
| |
| bool do_no_collapse_for_testing_ = false; |
| |
| // Keep prompt's decision until the prompt's widget is removed. |
| std::optional<permissions::PermissionAction> prompt_decision_; |
| |
| base::ScopedClosureRunner disallowed_custom_cursors_scope_; |
| |
| base::ScopedObservation<PermissionChipView, PermissionChipView::Observer> |
| observation_{this}; |
| |
| base::ObserverList<Observer> permission_prompt_observers_; |
| |
| base::WeakPtrFactory<ChipController> weak_factory_{this}; |
| }; |
| |
| #endif // CHROME_BROWSER_UI_VIEWS_PERMISSIONS_CHIP_CHIP_CONTROLLER_H_ |