| // Copyright 2025 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_FRE_GLIC_FRE_CONTROLLER_H_ |
| #define CHROME_BROWSER_GLIC_FRE_GLIC_FRE_CONTROLLER_H_ |
| |
| #include <memory> |
| |
| #include "base/gtest_prod_util.h" |
| #include "base/time/time.h" |
| #include "base/timer/elapsed_timer.h" |
| #include "chrome/browser/glic/fre/glic_fre.mojom.h" |
| #include "chrome/browser/glic/host/auth_controller.h" |
| #include "chrome/browser/glic/host/glic.mojom.h" |
| #include "chrome/browser/shell_integration.h" |
| #include "components/tabs/public/tab_interface.h" |
| #include "ui/views/widget/widget.h" |
| |
| class Browser; |
| class Profile; |
| |
| namespace content { |
| class WebContents; |
| } |
| |
| namespace version_info { |
| enum class Channel; |
| } |
| |
| namespace views { |
| class Widget; |
| } |
| |
| namespace glic { |
| |
| class GlicFreDialogView; |
| |
| // This enum is used to record the reason for the FRE error state. |
| // These values are persisted to logs. |
| // LINT.IfChange(FreErrorStateReason) |
| enum class FreErrorStateReason { |
| // Sign-in is required. |
| kSignInRequired = 0, |
| // Error while re-syncing cookies before showing FRE. |
| kErrorResyncingCookies = 1, |
| // Timeout exceeded during loading error. |
| kTimeoutExceeded = 2, |
| kMaxValue = kTimeoutExceeded, |
| }; |
| // LINT.ThenChange(tools/metrics/histograms/metadata/glic/enums.xml:FreErrorStateReason) |
| |
| // This enum is used for the Glic.Fre.WidgetClosedReason2 histogram. |
| // It mirrors views::Widget::ClosedReason and adds Glic-specific reasons. |
| // Entries should not be renumbered and numeric values should never be reused. |
| // LINT.IfChange(GlicFreWidgetClosedReason) |
| enum class GlicFreWidgetClosedReason { |
| kUnspecified = 0, |
| kEscKeyPressed = 1, |
| kCloseButtonClicked = 2, |
| kLostFocus = 3, |
| kCancelButtonClicked = 4, |
| kAcceptButtonClicked = 5, |
| kHostTabClosed = 6, |
| kHostTabMoved = 7, |
| kMaxValue = kHostTabMoved, |
| }; |
| // LINT.ThenChange(//tools/metrics/histograms/metadata/glic/enums.xml:GlicFreWidgetClosedReason) |
| |
| // This class owns and manages the glic FRE modal dialog, and is owned by a |
| // GlicWindowController. |
| class GlicFreController { |
| public: |
| GlicFreController(const GlicFreController&) = delete; |
| GlicFreController& operator=(const GlicFreController&) = delete; |
| |
| GlicFreController(Profile* profile, |
| signin::IdentityManager* identity_manager); |
| ~GlicFreController(); |
| |
| mojom::FreWebUiState GetWebUiState() const { return webui_state_; } |
| void WebUiStateChanged(mojom::FreWebUiState new_state); |
| |
| using WebUiStateChangedCallback = |
| base::RepeatingCallback<void(mojom::FreWebUiState new_state)>; |
| |
| // Registers |callback| to be called whenever the WebUi state changes. |
| base::CallbackListSubscription AddWebUiStateChangedCallback( |
| WebUiStateChangedCallback callback); |
| |
| // Close any windows and destroy web contents. |
| void Shutdown(); |
| |
| // Returns whether the FRE dialog should be shown. |
| bool ShouldShowFreDialog(); |
| |
| // Returns whether the FRE dialog can be shown. This function also checks |
| // `TabInterface::CanShowModalUI`, which is a mandatory precondition to |
| // showing the dialog. |
| bool CanShowFreDialog(Browser* browser); |
| |
| // Open the new tab page in the browser and show the FRE in that tab if |
| // possible. |
| void OpenFreDialogInNewTab(BrowserWindowInterface* bwi, |
| mojom::InvocationSource source); |
| |
| // Shows the FRE dialog. This should only be called if `ShouldShowFreDialog` |
| // and `CanShowFreDialog` are both satisfied. |
| void ShowFreDialog(Browser* browser, mojom::InvocationSource source); |
| |
| // Closes the FRE dialog if it is open on the active tab of `browser`. |
| void DismissFreIfOpenOnActiveTab(Browser* browser); |
| |
| // Closes the FRE dialog and immediately opens a glic window attached to |
| // the same browser. |
| void AcceptFre(); |
| |
| // Rejects the FRE dialog. |
| void RejectFre(); |
| |
| // Closes the FRE dialog. |
| void DismissFre(mojom::FreWebUiState panel); |
| |
| // Used when the native window is closed directly. |
| void CloseWithReason(views::Widget::ClosedReason reason); |
| |
| // Re-sync cookies to FRE webview. |
| void PrepareForClient(base::OnceCallback<void(bool)> callback); |
| |
| // Loading timeout was exceeded. |
| void ExceededTimeoutError(); |
| |
| // Notify FRE controller that the user clicked on a link. |
| void OnLinkClicked(const GURL& url); |
| |
| // Attempts to warm the FRE web contents. |
| void TryPreload(); |
| |
| // Returns true if the FRE web contents are loaded (either because it has been |
| // preloaded or because it is visible). |
| bool IsWarmed() const; |
| |
| // Returns the WebContents from the dialog view. |
| content::WebContents* GetWebContents(); |
| |
| // Preconnect to the server that hosts the FRE, so that it loads faster. |
| // Does nothing if the FRE should not be shown. |
| void MaybePreconnect(); |
| |
| bool IsShowingDialog() const; |
| |
| bool IsShowingDialogAndStateInitialized() const; |
| |
| gfx::Size GetFreInitialSize(); |
| |
| void UpdateFreWidgetSize(const gfx::Size& new_size); |
| |
| void LogWebUiLoadComplete(); |
| |
| AuthController& GetAuthControllerForTesting() { return auth_controller_; } |
| |
| Profile* profile() { return profile_; } |
| |
| base::WeakPtr<GlicFreController> GetWeakPtr() { |
| return weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| void SetIsShowingDialogForTesting(bool is_showing) { |
| is_showing_dialog_for_testing_ = is_showing; |
| } |
| |
| private: |
| FRIEND_TEST_ALL_PREFIXES(GlicFreControllerTest, |
| UpdateLauncherOnFreCompletion); |
| void ShowFreDialogAfterAuthCheck(base::WeakPtr<Browser> browser, |
| mojom::InvocationSource source); |
| static void OnCheckIsDefaultBrowserFinished( |
| version_info::Channel channel, |
| shell_integration::DefaultWebClientState state); |
| |
| // Called when the tab showing the FRE dialog is detached. |
| void OnTabShowingModalWillDetach(tabs::TabInterface* tab, |
| tabs::TabInterface::DetachReason reason); |
| |
| void CreateView(); |
| |
| void RecordMetricsIfDialogIsShowingAndReady(); |
| |
| raw_ptr<Profile> const profile_; |
| std::unique_ptr<views::Widget> fre_widget_; |
| std::unique_ptr<GlicFreDialogView> fre_view_; |
| // This is owned by the GlicFreDialogView but we retain a pointer to it so |
| // that we can continue to reference it even after `fre_view_` relinquishes |
| // ownership to the widget. |
| raw_ptr<content::WebContents> web_contents_ = nullptr; |
| AuthController auth_controller_; |
| |
| // The invocation source browser. |
| raw_ptr<Browser> source_browser_ = nullptr; |
| |
| // Tracks the tab that the FRE dialog is shown on. |
| raw_ptr<tabs::TabInterface> tab_showing_modal_; |
| base::CallbackListSubscription will_detach_subscription_; |
| |
| mojom::FreWebUiState webui_state_ = mojom::FreWebUiState::kUninitialized; |
| // List of callbacks to be notified when webui state has changed. |
| base::RepeatingCallbackList<void(mojom::FreWebUiState)> |
| webui_state_callback_list_; |
| |
| // Tracks elapsed time between the request for the FRE to show to the time it |
| // is fully loaded and showing. |
| std::optional<base::ElapsedTimer> presentation_timer_; |
| |
| // Tracks the total time the FRE is open. |
| std::optional<base::ElapsedTimer> open_timer_; |
| |
| // Tracks the total time since the FRE completed loading and has entered the |
| // Ready state. |
| std::optional<base::ElapsedTimer> interaction_timer_; |
| |
| // Tracks elapsed time between the start of the WebUI framework loading and |
| // the moment it's fully loaded. This ends right before the web client begins loading. |
| std::optional<base::ElapsedTimer> webui_framework_load_timer_; |
| |
| // Tracks elapsed time between the start of the web client loading and the |
| // moment it's fully loaded. |
| std::optional<base::ElapsedTimer> web_client_load_timer_; |
| |
| // True if the user has accepted the FRE. |
| bool accepted_ = false; |
| |
| std::optional<bool> is_showing_dialog_for_testing_; |
| |
| base::WeakPtrFactory<GlicFreController> weak_ptr_factory_{this}; |
| }; |
| |
| } // namespace glic |
| |
| #endif // CHROME_BROWSER_GLIC_FRE_GLIC_FRE_CONTROLLER_H_ |