blob: 5f809a94c34ba331ef759ba75f9d6285b4ba3498 [file] [log] [blame]
// Copyright 2024 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_GLIC_WIDGET_GLIC_WINDOW_CONTROLLER_IMPL_H_
#define CHROME_BROWSER_GLIC_WIDGET_GLIC_WINDOW_CONTROLLER_IMPL_H_
#include <optional>
#include <vector>
#include "base/callback_list.h"
#include "base/functional/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/scoped_observation.h"
#include "base/scoped_observation_traits.h"
#include "chrome/browser/glic/glic_enabling.h"
#include "chrome/browser/glic/host/glic.mojom.h"
#include "chrome/browser/glic/host/glic_web_client_access.h"
#include "chrome/browser/glic/host/host.h"
#include "chrome/browser/glic/widget/application_hotkey_delegate.h"
#include "chrome/browser/glic/widget/glic_modal_manager.h"
#include "chrome/browser/glic/widget/glic_window_controller.h"
#include "chrome/browser/glic/widget/glic_window_hotkey_delegate.h"
#include "chrome/browser/glic/widget/local_hotkey_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "components/web_modal/web_contents_modal_dialog_host.h"
#include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/interaction/element_tracker.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"
class Browser;
class WindowFinder;
namespace gfx {
class Size;
class Point;
} // namespace gfx
namespace glic {
class GlicEnabling;
class ScopedGlicButtonIndicator;
class GlicButton;
class GlicModalManager;
// This class owns and manages the glic window. This class has the same lifetime
// as the GlicKeyedService, so it exists if and only if the profile exists.
//
// See the |State| enum below for the lifecycle of the window. When the glic
// window is open |attached_browser_| indicates if the window is attached or
// standalone. See |IsAttached|
class GlicWindowControllerImpl
: public GlicWindowController,
public views::WidgetObserver,
public Host::Observer,
public web_modal::WebContentsModalDialogManagerDelegate,
public web_modal::WebContentsModalDialogHost {
public:
GlicWindowControllerImpl(const GlicWindowControllerImpl&) = delete;
GlicWindowControllerImpl& operator=(const GlicWindowControllerImpl&) = delete;
GlicWindowControllerImpl(Profile* profile,
signin::IdentityManager* identity_manager,
GlicKeyedService* service,
GlicEnabling* enabling);
~GlicWindowControllerImpl() override;
// GlicWindowController implementation
void Toggle(BrowserWindowInterface* browser,
bool prevent_close,
mojom::InvocationSource source) override;
void ShowAfterSignIn(base::WeakPtr<Browser> browser) override;
void ToggleWhenNotAlwaysDetached(Browser* new_attached_browser,
bool prevent_close,
mojom::InvocationSource source) override;
void FocusIfOpen() override;
void Attach() override;
void Detach() override;
void Shutdown() override;
void Resize(const gfx::Size& size,
base::TimeDelta duration,
base::OnceClosure callback) override;
void EnableDragResize(bool enabled) override;
gfx::Size GetSize() override;
void SetDraggableAreas(
const std::vector<gfx::Rect>& draggable_areas) override;
void SetMinimumWidgetSize(const gfx::Size& size) override;
void Close() override;
void CloseWithReason(views::Widget::ClosedReason reason) override;
void ShowTitleBarContextMenuAt(gfx::Point event_loc) override;
bool ShouldStartDrag(const gfx::Point& initial_press_loc,
const gfx::Point& mouse_location) override;
void HandleWindowDragWithOffset(gfx::Vector2d mouse_offset) override;
const mojom::PanelState& GetPanelState() const override;
void AddStateObserver(StateObserver* observer) override;
void RemoveStateObserver(StateObserver* observer) override;
bool IsActive() override;
bool IsShowing() const override;
bool IsPanelOrFreShowing() const override;
bool IsAttached() const override;
bool IsDetached() const override;
base::CallbackListSubscription AddWindowActivationChangedCallback(
WindowActivationChangedCallback callback) override;
void Preload() override;
void PreloadFre() override;
void Reload() override;
bool IsWarmed() const override;
base::WeakPtr<GlicWindowController> GetWeakPtr() override;
GlicView* GetGlicView() override;
base::WeakPtr<views::View> GetGlicViewAsView() override;
GlicWidget* GetGlicWidget() override;
content::WebContents* GetFreWebContents() override;
Browser* attached_browser() override;
State state() const override;
GlicFreController* fre_controller() override;
GlicWindowAnimator* window_animator() override;
Profile* profile() override;
bool IsDragging() override;
void ShowGlicModal(std::u16string label) override;
gfx::Rect GetInitialBounds(Browser* browser) override;
void ShowDetachedForTesting() override;
void SetPreviousPositionForTesting(gfx::Point position) override;
// views::WidgetObserver implementation, monitoring the glic window widget.
void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
void OnWidgetDestroyed(views::Widget* widget) override;
void OnWidgetBoundsChanged(views::Widget* widget,
const gfx::Rect& new_bounds) override;
void OnWidgetUserResizeStarted() override;
void OnWidgetUserResizeEnded() override;
private:
Host& host() const;
// Sets the floating attributes of the glic window.
//
// When set to true, the glic window is set to have a `kFloatingWindow`
// z-order, and on the Mac is set to be "activation independent" (to allow the
// user to interact with it without causing Chromium to be activated), and
// visible on every space (including fullscreen ones).
//
// When set to false, the glic window is set to have a `kNormal` z-order, and
// on the Mac, all special activation and visibility properties are cleared.
void SetGlicWindowToFloatingMode(bool floating);
// Return the default detached bounds which are just below the tab strip
// button on the active browser.
std::optional<gfx::Rect> GetInitialDetachedBoundsFromBrowser(
Browser* browser,
const gfx::Size& target_size);
// Return the default detached bounds when there is no active browser. The
// position is relative to the top right of the current display.
gfx::Rect GetInitialDetachedBoundsNoBrowser(const gfx::Size& target_size);
// Return the default bounds when attached to the browser which cover the tab
// strip button on the active browser.
gfx::Rect GetInitialAttachedBounds(Browser& browser);
// Creates the glic view, waits for the web client to initialize, and then
// shows the glic window. If `browser` is non-nullptr then glic will be
// attached to the browser. Otherwise glic will be detached.
void Show(Browser* browser, mojom::InvocationSource source);
void SetupGlicWidget(Browser* browser);
void SetupGlicWidgetAccessibilityText();
// Host::Observer implementation.
void WebClientInitializeFailed() override;
void LoginPageCommitted() override;
void ClientReadyToShow(const mojom::OpenPanelInfo& open_info) override;
// Called once glic is completely loaded and any animations have finished.
// This is the end of the opening process and |state_| will be set to kOpen.
void GlicLoadedAndReadyToDisplay();
void SetDraggingAreasAndWatchForMouseEvents();
// Called when the Detach() animation ends.
void DetachFinished();
// Save the top-right corner position for re-opening.
void SaveWidgetPosition();
// Clear the previous position if the widget would not be on an existing
// display when shown.
void MaybeResetPreviousPosition(const gfx::Size& target_size);
// Determines the correct position for the glic window when attached to a
// browser window. The top right of the widget should be placed here.
gfx::Point GetTopRightPositionForAttachedGlicWindow(GlicButton* glic_button);
// Runs an animation to move glic to its target position.
// TODO(crbug.com/410629338): Reimplement attachment.
void AttachToBrowser(Browser& browser, AttachChangeReason reason);
// Keep part of glic window within the visible region.
void AdjustPositionIfNeeded();
// Handles end-of-drag:
// - If glic is within attachment distance of a browser window's glic button,
// attach the glic window to the button's position.
// - If glic is still detached and has moved to a display with a different
// work area size, possibly resize the window.
void OnDragComplete();
// Finds a browser within attachment distance of glic to toggle the attachment
// indicator.
void HandleGlicButtonIndicator();
// Find and return a browser within attachment distance. Returns nullptr if no
// browsers are within attachment distance.
Browser* FindBrowserForAttachment();
// Updates the position of the glic window to that of the glic button of
// `browser`'s window. This position change is animated if `animate` is true.
void MovePositionToBrowserGlicButton(const Browser& browser, bool animate);
// Called when the move animation finishes when attaching.
void AttachAnimationFinished();
// This method should be called anytime:
// * state_ transitions to or from kClosed.
// * attached_browser_ changes.
void NotifyIfPanelStateChanged();
mojom::PanelState ComputePanelState() const;
// When the attached browser is closed, this is invoked so we can clean up.
void AttachedBrowserDidClose(BrowserWindowInterface* browser);
// Returns true if a browser is occluded at point in screen coordinates.
bool IsBrowserOccludedAtPoint(Browser* browser, gfx::Point point);
// Return the last size Resize() was called with, or the default initial size
// if Resize() hasn't been called. The return value is clamped to fit between
// the minimum and maximum sizes.
gfx::Size GetLastRequestedSizeClamped() const;
// Possibly adjusts the size of the window appropriate for the current
// display workspace, but only if it's different than the current target size.
void MaybeAdjustSizeForDisplay(bool animate);
// Modifies `state_` to the given new state.
void SetWindowState(State new_state);
// Returns true of the window is showing and the content is loaded.
bool IsWindowOpenAndReady();
// web_modal::WebContentsModalDialogManagerDelegate:
web_modal::WebContentsModalDialogHost* GetWebContentsModalDialogHost()
override;
// web_modal::WebContentsModalDialogHost:
gfx::Size GetMaximumDialogSize() override;
gfx::NativeView GetHostView() const override;
gfx::Point GetDialogPosition(const gfx::Size& dialog_size) override;
bool ShouldDialogBoundsConstrainedByHost() override;
void AddObserver(web_modal::ModalDialogHostObserver* observer) override;
void RemoveObserver(web_modal::ModalDialogHostObserver* observer) override;
// Observes the glic widget.
base::ScopedObservation<views::Widget, views::WidgetObserver>
glic_widget_observation_{this};
// Used for observing closing of the pinned browser.
std::optional<base::CallbackListSubscription> browser_close_subscription_;
// List of callbacks to be notified when window activation has changed.
base::RepeatingCallbackList<void(bool)> window_activation_callback_list_;
const raw_ptr<Profile> profile_;
// Contains the glic webview.
std::unique_ptr<GlicWidget> glic_widget_;
std::unique_ptr<GlicWindowAnimator> glic_window_animator_;
// True if we've hit a login page (and have not yet shown).
bool login_page_committed_ = false;
// This member contains the last size that glic requested. This should be
// reset every time glic is closed but is currently cached.
std::optional<gfx::Size> glic_size_;
// Whether the widget should be user resizable, kept here in case it's
// specified before the widget is created.
bool user_resizable_ = true;
// Used to monitor key and mouse events from native window.
class WindowEventObserver;
std::unique_ptr<WindowEventObserver> window_event_observer_;
// True while RunMoveLoop() has been called on a widget.
bool in_move_loop_ = false;
// This is the last panel state sent to observers. It should only be updated
// in `NotifyIfPanelStateChanged`.
mojom::PanelState panel_state_;
raw_ptr<GlicWebClientAccess> web_client_;
// Modified only by calling `SetWindowState`.
State state_ = State::kClosed;
// If State != kClosed, then the UI must either be associated with a browser
// window, or standalone. That is tracked by this member.
raw_ptr<Browser> attached_browser_ = nullptr;
base::ObserverList<StateObserver> state_observers_;
// Used by web modals to listens for glic window events, e.g. size change or
// window close.
base::ObserverList<web_modal::ModalDialogHostObserver>::Unchecked
modal_dialog_host_observers_;
// The announcement should happen the first time focus is lost after the FRE.
bool do_focus_loss_announcement_ = false;
// Whether the user is currently drag-resizing the widget.
bool user_resizing_ = false;
// The invocation source requesting the opening of the web client. Note that
// this value is retained until it is consumed by the web client. Because
// opening the glic window may not actually load the client, there's no
// guarantee that this value is sent to the web client.
std::optional<mojom::InvocationSource> opening_source_;
std::optional<gfx::Point> previous_position_ = std::nullopt;
std::unique_ptr<ScopedGlicButtonIndicator> scoped_glic_button_indicator_;
std::unique_ptr<GlicFreController> fre_controller_;
std::unique_ptr<WindowFinder> window_finder_;
std::unique_ptr<GlicModalManager> glic_modal_manager_;
std::unique_ptr<LocalHotkeyManager> application_hotkey_manager_;
std::unique_ptr<LocalHotkeyManager> glic_window_hotkey_manager_;
raw_ptr<GlicKeyedService> glic_service_; // Owns this.
raw_ptr<GlicEnabling> enabling_;
base::ScopedObservation<Host, Host::Observer> host_observation_{this};
base::WeakPtrFactory<GlicWindowControllerImpl> weak_ptr_factory_{this};
};
} // namespace glic
#endif // CHROME_BROWSER_GLIC_WIDGET_GLIC_WINDOW_CONTROLLER_IMPL_H_