blob: 69bd7b2bf7a4e74beaf2daa41772fd3047119530 [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_UI_LENS_LENS_OVERLAY_CONTROLLER_H_
#define CHROME_BROWSER_UI_LENS_LENS_OVERLAY_CONTROLLER_H_
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "chrome/browser/lens/core/mojom/geometry.mojom.h"
#include "chrome/browser/lens/core/mojom/lens.mojom.h"
#include "chrome/browser/lens/core/mojom/text.mojom.h"
#include "chrome/browser/resources/lens/server/proto/lens_overlay_response.pb.h"
#include "chrome/browser/ui/tabs/tab_model_observer.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
#include "chrome/browser/ui/webui/searchbox/lens_searchbox_client.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/views/widget/unique_widget_ptr.h"
class TabStripModel;
namespace lens {
class LensOverlaySidePanelCoordinator;
class LensOverlayQueryController;
} // namespace lens
namespace tabs {
class TabModel;
} // namespace tabs
namespace views {
class View;
class WebView;
} // namespace views
namespace content {
class WebUI;
} // namespace content
// Manages all state associated with the lens overlay.
// This class is not thread safe. It should only be used from the browser
// thread.
class LensOverlayController : public TabStripModelObserver,
public LensSearchboxClient,
public lens::mojom::LensPageHandler,
public lens::mojom::LensSidePanelPageHandler,
public tabs::TabModelObserver {
public:
explicit LensOverlayController(tabs::TabModel* tab_model);
~LensOverlayController() override;
DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kOverlayId);
DECLARE_CLASS_ELEMENT_IDENTIFIER_VALUE(kOverlaySidePanelWebViewId);
// Returns whether the lens overlay feature is enabled. This value is
// guaranteed not to change over the lifetime of a LensOverlayController.
bool Enabled();
// This is entry point for showing the overlay UI. This has no effect if state
// is not kOff. This has no effect if the tab is not in the foreground.
void ShowUI();
// Closes the overlay UI and sets state to kOff. This method should be
// idempotent. This synchronously destroys any associated WebUIs, so should
// not be invoked in callbacks from those WebUIs.
void CloseUI();
// Given an instance of `web_ui` created by the LensOverlayController, returns
// the LensOverlayController. This method is necessary because WebUIController
// is created by //content with no context or references to the owning
// controller.
static LensOverlayController* GetController(content::WebUI* web_ui);
// Given a `content::WebContents` associated with a tab, returns the
// associated controller. Returns `nullptr` if there is no controller (e.g.
// the WebContents is not a tab).
static LensOverlayController* GetController(
content::WebContents* tab_contents);
// This method is used to set up communication between this instance and the
// overlay WebUI. This is called by the WebUIController when the WebUI is
// executing javascript and ready to bind.
void BindOverlay(mojo::PendingReceiver<lens::mojom::LensPageHandler> receiver,
mojo::PendingRemote<lens::mojom::LensPage> page);
// This method is used to set up communication between this instance and the
// side panel WebUI. This is called by the WebUIController when the WebUI is
// executing javascript and ready to bind.
void BindSidePanel(
mojo::PendingReceiver<lens::mojom::LensSidePanelPageHandler> receiver,
mojo::PendingRemote<lens::mojom::LensSidePanelPage> page);
// Internal state machine. States are mutually exclusive. Exposed for testing.
enum class State {
// This is the default state. There should be no performance overhead as
// this state will apply to all tabs.
kOff,
// In the process of taking a screenshot to transition to kOverlay.
kScreenshot,
// In the process of starting the overlay WebUI.
kStartingWebUI,
// Showing an overlay without results.
kOverlay,
// Showing an overlay with results.
kOverlayAndResults,
// Will be kOff soon.
kClosing,
};
State state() { return state_; }
// Returns the screenshot currently being displayed on this overlay. If no
// screenshot is showing, will return nullptr.
const SkBitmap& current_screenshot() { return current_screenshot_; }
// Returns the side panel coordinator
lens::LensOverlaySidePanelCoordinator* side_panel_coordinator() {
return results_side_panel_coordinator_.get();
}
// Testing helper method for checking widget.
views::Widget* GetOverlayWidgetForTesting();
// Resizes the overlay UI. Used when the window size changes.
void ResetUIBounds();
// Creates the glue that allows the WebUIController for a WebView to look up
// the LensOverlayController.
void CreateGlueForWebView(views::WebView* web_view);
// Removes the glue that allows the WebUIController for a WebView to look up
// the LensOverlayController. Used by the side panel coordinator when it is
// closed when the overlay is still open. This is a no-op if the provided web
// view is not glued.
void RemoveGlueForWebView(views::WebView* web_view);
// Send text data to the WebUI.
void SendText(lens::mojom::TextPtr text);
// Returns true if the overlay is open and covering the current active tab.
bool IsOverlayShowing();
// Handles when the side panel has been deregistered to do any required
// cleanup.
void OnSidePanelEntryDeregistered();
// Testing function to issue a text request.
// TODO(b/328294794): Remove this function when connecting the mojo call.
void IssueTextRequestForTesting(const std::string& text_query);
private:
class UnderlyingWebContentsObserver;
// Called once a screenshot has been captured. This should trigger transition
// to kOverlay. As this process is asynchronous, there are edge cases that can
// result in multiple in-flight screenshot attempts. We record the
// `attempt_id` for each attempt so we can ignore all but the most recent
// attempt.
void DidCaptureScreenshot(int attempt_id, const SkBitmap& bitmap);
// Called when the UI needs to create the overlay widget.
void ShowOverlayWidget();
// Creates InitParams for the overlay widget based on the window bounds.
views::Widget::InitParams CreateWidgetInitParams();
// Called when the UI needs to create the view to show in the overlay.
std::unique_ptr<views::View> CreateViewForOverlay();
// Overridden from TabStripModelObserver:
void OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) override;
// Overridden from LensSearchboxClient:
const GURL& GetPageURL() const override;
metrics::OmniboxEventProto::PageClassification GetPageClassification()
const override;
void OnSuggestionAccepted(const GURL& destination_url) override;
// Called when the associated tab enters the foreground.
void TabForegrounded();
// Called when the associated tab enters the background.
void TabBackgrounded();
// lens::mojom::LensPageHandler overrides.
void CloseRequestedByOverlay() override;
void IssueLensRequest(lens::mojom::CenterRotatedBoxPtr region) override;
// Handles a text query by constructing a search URL and loading it into the
// results frame.
void IssueTextRequest(const std::string& text_query);
// Calls CloseUI() asynchronously.
void CloseUIAsync();
// Handles the response to the Lens start query request.
void HandleStartQueryResponse(
lens::proto::LensOverlayFullImageResponse response);
// Handles the URL response to the Lens interaction request.
void HandleInteractionURLResponse(
lens::proto::LensOverlayUrlResponse response);
// Handles the suggest signals response to the Lens interaction request.
void HandleInteractionDataResponse(
lens::proto::LensOverlayInteractionResponse response);
// tabs::TabModelObserver overrides:
void WillRemoveContents(tabs::TabModel* tab,
content::WebContents* contents) override;
void DidAddContents(tabs::TabModel* tab,
content::WebContents* contents) override;
// Owns this class.
raw_ptr<tabs::TabModel> tab_model_;
// A monotonically increasing id. This is used to differentiate between
// different screenshot attempts.
int screenshot_attempt_id_ = 0;
// Tracks the internal state machine.
State state_ = State::kOff;
// Pointer to the overlay widget.
views::UniqueWidgetPtr overlay_widget_;
// Pointer to the WebViews that are being glued by this class. Only used to
// clean up stale pointers. Only valid while `overlay_widget_` is showing.
std::vector<views::WebView*> glued_webviews_;
// The screenshot that is currently being rendered by the WebUI.
SkBitmap current_screenshot_;
// A pending text query to be loaded in the side panel. Needed when the side
// panel is not bound at the time of a text request.
std::optional<std::string> pending_text_query_ = std::nullopt;
// Connections to and from the overlay WebUI. Only valid while
// `overlay_widget_` is showing, and after the WebUI has started executing JS
// and has bound the connection.
mojo::Receiver<lens::mojom::LensPageHandler> receiver_{this};
mojo::Remote<lens::mojom::LensPage> page_;
// Connections to and from the side panel WebUI. Only valid when the side
// panel is currently open and after the WebUI has started executing JS and
// has bound the connection.
mojo::Receiver<lens::mojom::LensSidePanelPageHandler> side_panel_receiver_{
this};
mojo::Remote<lens::mojom::LensSidePanelPage> side_panel_page_;
// Side panel coordinator for showing results in the panel.
std::unique_ptr<lens::LensOverlaySidePanelCoordinator>
results_side_panel_coordinator_;
// Observer for the WebContents of the associated tab. Only valid while the
// overlay widget is showing.
std::unique_ptr<UnderlyingWebContentsObserver> tab_contents_observer_;
// Query controller.
std::unique_ptr<lens::LensOverlayQueryController>
lens_overlay_query_controller_;
base::ScopedObservation<tabs::TabModel, tabs::TabModelObserver>
tab_model_observer_{this};
// Must be the last member.
base::WeakPtrFactory<LensOverlayController> weak_factory_{this};
};
#endif // CHROME_BROWSER_UI_LENS_LENS_OVERLAY_CONTROLLER_H_