1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
|
// 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/raw_ptr.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_window_config.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/display/display_observer.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;
// 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 display::DisplayObserver,
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;
void MaybeSetWidgetCanResize() 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;
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;
// display::DisplayObserver implementation
void OnDisplayMetricsChanged(const display::Display& display,
uint32_t changed_metrics) 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(bool user_modified);
// 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};
// Observes the display configuration.
display::ScopedOptionalDisplayObserver display_observer_{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_;
// Contains the size of the draggable area zone for the glic widget.
// This value gets sent from the web client; if it is ever null, the draggable
// area will be set to a default value.
std::optional<gfx::Rect> draggable_area_ = std::nullopt;
// 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;
GlicWindowConfig window_config_;
// 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<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_
|