[lensoverlay] Capture top-level navigations in side panel.
Change-Id: I0ef244df0202d1fe2887365e938036fde6ef78c5
Bug: b:379152175, 379338040
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6024467
Commit-Queue: Juan Mojica <juanmojica@google.com>
Reviewed-by: Duncan Mercer <mercerd@google.com>
Cr-Commit-Position: refs/heads/main@{#1383877}
diff --git a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
index aad412d..575df9c 100644
--- a/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
+++ b/chrome/browser/ui/lens/lens_overlay_controller_browsertest.cc
@@ -150,6 +150,11 @@
"(function() {const anchor = document.createElement('a');anchor.href = "
"$1;document.body.appendChild(anchor);anchor.click();})();";
+constexpr char kTopLevelNavLinkClickScript[] =
+ "(function() {const anchor = document.createElement('a');anchor.href = "
+ "$1;anchor.target='_top';document.body.appendChild(anchor);anchor.click();}"
+ ")();";
+
constexpr char kCheckSearchboxInput[] =
"(function() {const root = "
"document.getElementsByTagName('lens-side-panel-app')[0].shadowRoot;"
@@ -2047,6 +2052,80 @@
}
IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
+ SidePanel_TopLevelSameOriginLinkClick) {
+ WaitForPaint();
+
+ // State should start in off.
+ auto* controller = GetLensOverlayController();
+ EXPECT_EQ(controller->state(), State::kOff);
+
+ // Showing UI should change the state to screenshot and eventually to overlay.
+ controller->ShowUI(LensOverlayInvocationSource::kAppMenu);
+ ASSERT_EQ(controller->state(), State::kScreenshot);
+ EXPECT_TRUE(base::test::RunUntil(
+ [&]() { return controller->state() == State::kOverlay; }));
+ EXPECT_TRUE(content::WaitForLoadStop(GetOverlayWebContents()));
+ EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible());
+
+ // Loading a url in the side panel should show the results page. This needs to
+ // be done to set up the WebContentsObserver.
+ const GURL search_url("https://www.google.com/search");
+ controller->LoadURLInResultsFrame(search_url);
+
+ // Expect the Lens Overlay results panel to open.
+ auto* coordinator = browser()->GetFeatures().side_panel_coordinator();
+ EXPECT_TRUE(coordinator->IsSidePanelShowing());
+ EXPECT_EQ(coordinator->GetCurrentEntryId(),
+ SidePanelEntry::Id::kLensOverlayResults);
+ EXPECT_TRUE(content::WaitForLoadStop(
+ controller->GetSidePanelWebContentsForTesting()));
+ int tabs = browser()->tab_strip_model()->count();
+
+ // The results frame should be the only child frame of the side panel web
+ // contents.
+ content::RenderFrameHost* results_frame = content::ChildFrameAt(
+ controller->GetSidePanelWebContentsForTesting()->GetPrimaryMainFrame(),
+ 0);
+ const GURL nav_url("https://www.google.com/search?q=apples");
+ content::OverrideLastCommittedOrigin(results_frame,
+ url::Origin::Create(search_url));
+ EXPECT_TRUE(results_frame);
+
+ // Verify the fake controller exists and reset any loading that was done
+ // before as part of setup.
+ auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller);
+ ASSERT_TRUE(fake_controller);
+ fake_controller->ResetSidePanelTracking();
+
+ // Simulate a top level same-origin navigation on the results frame.
+ content::TestNavigationObserver observer(
+ controller->GetSidePanelWebContentsForTesting());
+ EXPECT_TRUE(content::ExecJs(
+ results_frame, content::JsReplace(kTopLevelNavLinkClickScript, nav_url),
+ content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
+ observer.WaitForNavigationFinished();
+
+ // It should not open a new tab as this is a same-origin navigation.
+ EXPECT_EQ(tabs, browser()->tab_strip_model()->count());
+
+ VerifySearchQueryParameters(observer.last_navigation_url());
+ VerifyTextQueriesAreEqual(observer.last_navigation_url(), nav_url);
+
+ // Verify the loading state was set correctly.
+ EXPECT_EQ(fake_controller->is_side_panel_loading_set_to_true_, 1);
+ EXPECT_EQ(fake_controller->is_side_panel_loading_set_to_false_, 0);
+
+ // We should find that the input text on the searchbox is the same as the text
+ // query of the nav_url.
+ EXPECT_TRUE(content::EvalJs(
+ controller->GetSidePanelWebContentsForTesting()
+ ->GetPrimaryMainFrame(),
+ content::JsReplace(kCheckSearchboxInput, "apples"),
+ content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES)
+ .ExtractBool());
+}
+
+IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest,
SidePanel_NewTabCrossOriginLinkClick) {
WaitForPaint();
diff --git a/chrome/browser/ui/lens/lens_overlay_side_panel_navigation_throttle.cc b/chrome/browser/ui/lens/lens_overlay_side_panel_navigation_throttle.cc
index 3c13dc6..455f240 100644
--- a/chrome/browser/ui/lens/lens_overlay_side_panel_navigation_throttle.cc
+++ b/chrome/browser/ui/lens/lens_overlay_side_panel_navigation_throttle.cc
@@ -21,12 +21,14 @@
LensOverlaySidePanelNavigationThrottle::MaybeCreateFor(
content::NavigationHandle* handle,
ThemeService* theme_service) {
- // We only want to handle navigations within the side panel results frame, so
- // we can ignore all navigations to a primary main frame. We can also ignore
- // all navigations that don't occur one level down (e.g. children of iframes
- // in the WebUI).
- if (handle->IsInPrimaryMainFrame() || !handle->GetParentFrame() ||
- !handle->GetParentFrame()->IsInPrimaryMainFrame()) {
+ // We only want to handle navigations within the side panel results frame, we
+ // can ignore all navigations that don't occur one level down (e.g. children
+ // of iframes in the WebUI). However, since the top level frame hosts the
+ // WebUI, we should also handle those navigations within this throttle to
+ // prevent breakages.
+ if (!handle->IsInPrimaryMainFrame() &&
+ (!handle->GetParentFrame() ||
+ !handle->GetParentFrame()->IsInPrimaryMainFrame())) {
return nullptr;
}
diff --git a/chrome/browser/ui/lens/lens_overlay_url_builder.cc b/chrome/browser/ui/lens/lens_overlay_url_builder.cc
index 640a4b5..64e50a2 100644
--- a/chrome/browser/ui/lens/lens_overlay_url_builder.cc
+++ b/chrome/browser/ui/lens/lens_overlay_url_builder.cc
@@ -37,6 +37,10 @@
// Query parameter for the mode.
inline constexpr char kModeParameterKey[] = "udm";
+
+// Query parameter for the toolbelt mode.
+inline constexpr char kToolbeltModeParameterKey[] = "tbm";
+
// Query parameter values for the mode.
inline constexpr char kUnimodalModeParameterValue[] = "26";
inline constexpr char kMultimodalModeParameterValue[] = "24";
@@ -78,7 +82,8 @@
// The list of query parameters to ignore when comparing search URLs.
inline constexpr std::string kIgnoredSearchUrlQueryParameters[] = {
kViewportWidthQueryParamKey, kViewportHeightQueryParamKey,
- kXSRFTokenQueryParamKey, kSecActQueryParamKey};
+ kXSRFTokenQueryParamKey, kSecActQueryParamKey,
+ kModeParameterKey, kToolbeltModeParameterKey};
// Query parameter for dark mode.
inline constexpr char kDarkModeParameterKey[] = "cs";