From 8ead2f6624b045ee351a9dbac39a07fd3b41ec4c Mon Sep 17 00:00:00 2001 From: Jason Hu Date: Thu, 14 Sep 2023 18:53:15 +0000 Subject: [PATCH] [CSC][Lens]Add network prewarming request on right click Bug: 1479730 Change-Id: I2f4ec7b7ebe946fd961188bba4613aeeedc4f159 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4846723 Reviewed-by: Tarun Bansal Commit-Queue: Jason Hu Reviewed-by: Avi Drissman Cr-Commit-Position: refs/heads/main@{#1196741} --- chrome/browser/companion/core/utils.cc | 16 ++ chrome/browser/companion/core/utils.h | 2 + .../render_view_context_menu.cc | 57 +++++++ .../render_view_context_menu.h | 4 + .../render_view_context_menu_unittest.cc | 142 +++++++++++++++++- components/lens/lens_features.cc | 14 ++ components/lens/lens_features.h | 10 ++ 7 files changed, 244 insertions(+), 1 deletion(-) diff --git a/chrome/browser/companion/core/utils.cc b/chrome/browser/companion/core/utils.cc index 2adcfc3062db05..8c2b53d7b6b014 100644 --- a/chrome/browser/companion/core/utils.cc +++ b/chrome/browser/companion/core/utils.cc @@ -52,6 +52,22 @@ std::string GetImageUploadURLForCompanion() { return url; } +bool GetShouldIssuePreconnectForCompanion() { + return base::GetFieldTrialParamByFeatureAsBool( + *GetFeatureToUse(), "companion-issue-preconnect", true); +} + +std::string GetPreconnectKeyForCompanion() { + // Allow multiple field trials to control the value. This is needed because + // companion may be enabled by any of the field trials. + std::string url = base::GetFieldTrialParamValueByFeature( + *GetFeatureToUse(), "companion-preconnect-key"); + if (url.empty()) { + return std::string("chrome-untrusted://companion-side-panel.top-chrome"); + } + return url; +} + bool ShouldEnableOpenCompanionForImageSearch() { // Allow multiple field trials to control the value. This is needed because // companion may be enabled by any of the field trials. diff --git a/chrome/browser/companion/core/utils.h b/chrome/browser/companion/core/utils.h index 521265f8f88c2e..641adb8f347815 100644 --- a/chrome/browser/companion/core/utils.h +++ b/chrome/browser/companion/core/utils.h @@ -13,6 +13,8 @@ namespace companion { std::string GetHomepageURLForCompanion(); std::string GetImageUploadURLForCompanion(); +bool GetShouldIssuePreconnectForCompanion(); +std::string GetPreconnectKeyForCompanion(); bool ShouldEnableOpenCompanionForImageSearch(); bool ShouldEnableOpenCompanionForWebSearch(); bool ShouldOpenLinksInCurrentTab(); diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc index 17f5149c59651d..1e5cede4bcfe0a 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc +++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc @@ -43,6 +43,7 @@ #include "chrome/browser/browser_features.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/companion/core/features.h" +#include "chrome/browser/companion/core/utils.h" #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h" #include "chrome/browser/devtools/devtools_window.h" #include "chrome/browser/download/download_prefs.h" @@ -51,9 +52,15 @@ #include "chrome/browser/feed/web_feed_ui_util.h" #include "chrome/browser/language/language_model_manager_factory.h" #include "chrome/browser/media/router/media_router_feature.h" +#include "chrome/browser/navigation_predictor/navigation_predictor_features.h" +#include "chrome/browser/navigation_predictor/navigation_predictor_keyed_service.h" +#include "chrome/browser/navigation_predictor/navigation_predictor_keyed_service_factory.h" #include "chrome/browser/password_manager/chrome_password_manager_client.h" #include "chrome/browser/platform_util.h" +#include "chrome/browser/predictors/loading_predictor.h" +#include "chrome/browser/predictors/loading_predictor_factory.h" #include "chrome/browser/prefs/incognito_mode_prefs.h" +#include "chrome/browser/preloading/preloading_prefs.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_attributes_entry.h" #include "chrome/browser/profiles/profile_attributes_storage.h" @@ -181,6 +188,7 @@ #include "extensions/buildflags/buildflags.h" #include "media/base/media_switches.h" #include "mojo/public/cpp/bindings/associated_remote.h" +#include "net/base/network_anonymization_key.h" #include "net/traffic_annotation/network_traffic_annotation.h" #include "pdf/buildflags.h" #include "ppapi/buildflags/buildflags.h" @@ -1002,6 +1010,30 @@ void RenderViewContextMenu::WriteURLToClipboard(const GURL& url) { scw.WriteText(FormatURLForClipboard(url)); } +void RenderViewContextMenu::IssuePreconnectionToUrl( + const std::string& anonymization_key_url, + const std::string& preconnect_url) { + Profile* profile = Profile::FromBrowserContext(browser_context_); + if (prefetch::IsSomePreloadingEnabled(*profile->GetPrefs()) != + content::PreloadingEligibility::kEligible) { + return; + } + + auto* loading_predictor = + predictors::LoadingPredictorFactory::GetForProfile(profile); + if (!loading_predictor) { + return; + } + + GURL anonymization_key_gurl(anonymization_key_url); + net::SchemefulSite anonymization_key_schemeful_site(anonymization_key_gurl); + auto network_anonymziation_key = net::NetworkAnonymizationKey::CreateSameSite( + anonymization_key_schemeful_site); + loading_predictor->PreconnectURLIfAllowed(GURL(preconnect_url), + /*allow_credentials=*/true, + network_anonymziation_key); +} + bool RenderViewContextMenu::IsInProgressiveWebApp() const { const Browser* browser = GetBrowser(); return browser && (browser->is_type_app() || browser->is_type_app_popup()); @@ -1875,6 +1907,18 @@ void RenderViewContextMenu::AppendSearchWebForImageItems() { menu_model_.SetIsNewFeatureAt(menu_model_.GetItemCount() - 1, true); } + if (search::DefaultSearchProviderIsGoogle(GetProfile())) { + if (companion::IsSearchImageInCompanionSidePanelSupported(GetBrowser()) && + companion::GetShouldIssuePreconnectForCompanion()) { + IssuePreconnectionToUrl(companion::GetPreconnectKeyForCompanion(), + companion::GetImageUploadURLForCompanion()); + } else if (base::FeatureList::IsEnabled(lens::features::kLensStandalone) && + lens::features::GetShouldIssuePreconnectForLens()) { + IssuePreconnectionToUrl(lens::features::GetPreconnectKeyForLens(), + lens::features::GetHomepageURLForLens()); + } + } + if (base::FeatureList::IsEnabled(lens::features::kLensStandalone) && base::FeatureList::IsEnabled(lens::features::kEnableImageTranslate) && provider && !provider->image_translate_url().empty() && @@ -2473,6 +2517,19 @@ void RenderViewContextMenu::AppendRegionSearchItem() { companion::IsSearchImageInCompanionSidePanelSupported(GetBrowser())) { menu_model_.SetIsNewFeatureAt(menu_model_.GetItemCount() - 1, true); } + + if (search::DefaultSearchProviderIsGoogle(GetProfile())) { + if (companion::IsSearchImageInCompanionSidePanelSupported(GetBrowser()) && + companion::GetShouldIssuePreconnectForCompanion()) { + IssuePreconnectionToUrl(companion::GetPreconnectKeyForCompanion(), + companion::GetImageUploadURLForCompanion()); + } else if (base::FeatureList::IsEnabled( + lens::features::kLensStandalone) && + lens::features::GetShouldIssuePreconnectForLens()) { + IssuePreconnectionToUrl(lens::features::GetPreconnectKeyForLens(), + lens::features::GetHomepageURLForLens()); + } + } } } diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.h b/chrome/browser/renderer_context_menu/render_view_context_menu.h index 88034ab2348276..b74da355c7b6b9 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu.h +++ b/chrome/browser/renderer_context_menu/render_view_context_menu.h @@ -231,6 +231,10 @@ class RenderViewContextMenu // Writes the specified text/url to the system clipboard. void WriteURLToClipboard(const GURL& url); + // Issues a preconnection request to the given url. + void IssuePreconnectionToUrl(const std::string& anonymization_key_url, + const std::string& preconnect_url); + // RenderViewContextMenuBase: void InitMenu() override; void RecordShownItem(int id, bool is_submenu) override; diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc index c11e41546c7960..31341cd5f76093 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc +++ b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc @@ -22,8 +22,13 @@ #include "chrome/browser/extensions/test_extension_environment.h" #include "chrome/browser/extensions/test_extension_prefs.h" #include "chrome/browser/feed/web_feed_tab_helper.h" +#include "chrome/browser/navigation_predictor/navigation_predictor_keyed_service.h" +#include "chrome/browser/navigation_predictor/navigation_predictor_keyed_service_factory.h" #include "chrome/browser/password_manager/chrome_password_manager_client.h" #include "chrome/browser/password_manager/password_store_factory.h" +#include "chrome/browser/predictors/loading_predictor.h" +#include "chrome/browser/predictors/loading_predictor_factory.h" +#include "chrome/browser/predictors/preconnect_manager.h" #include "chrome/browser/prefs/incognito_mode_prefs.h" #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h" #include "chrome/browser/search_engines/template_url_service_factory.h" @@ -540,7 +545,9 @@ TEST_F(RenderViewContextMenuDeveloperItemsTest, } #endif // BUILDFLAG(IS_CHROMEOS_ASH) -class RenderViewContextMenuPrefsTest : public ChromeRenderViewHostTestHarness { +class RenderViewContextMenuPrefsTest + : public ChromeRenderViewHostTestHarness, + public predictors::PreconnectManager::Observer { public: RenderViewContextMenuPrefsTest() = default; @@ -578,6 +585,24 @@ class RenderViewContextMenuPrefsTest : public ChromeRenderViewHostTestHarness { -> std::unique_ptr { return std::make_unique(); })); + last_preresolved_url_ = GURL(); + } + + // Begins listening for loading preconnections. + void BeginPreresolveListening() { + auto* loading_predictor = + predictors::LoadingPredictorFactory::GetForProfile( + GetBrowser()->profile()); + ASSERT_TRUE(loading_predictor); + loading_predictor->preconnect_manager()->SetObserverForTesting(this); + last_preresolved_url_ = GURL(); + } + + void OnPreresolveFinished( + const GURL& url, + const net::NetworkAnonymizationKey& network_anonymization_key, + bool success) override { + last_preresolved_url_ = url; } void TearDown() override { @@ -652,11 +677,14 @@ class RenderViewContextMenuPrefsTest : public ChromeRenderViewHostTestHarness { return browser_.get(); } + const GURL& last_preresolved_url() const { return last_preresolved_url_; } + private: std::unique_ptr registry_; std::unique_ptr testing_local_state_; raw_ptr template_url_service_; std::unique_ptr browser_; + GURL last_preresolved_url_; }; // Verifies when Incognito Mode is not available (disabled by policy), @@ -1646,6 +1674,118 @@ TEST_F(RenderViewContextMenuPrefsTest, LensRegionSearchChromeUIScheme) { EXPECT_FALSE(menu.IsItemPresent(IDC_CONTENT_CONTEXT_LENS_REGION_SEARCH)); } +// Verify that the adding the companion image search option to the menu +// issues a preconnection request to lens.google.com. +TEST_F(RenderViewContextMenuPrefsTest, + CompanionImageSearchIssuesGoogleLensPreconnect) { + BeginPreresolveListening(); + base::test::ScopedFeatureList features; + features.InitWithFeaturesAndParameters( + {{companion::features::internal::kSidePanelCompanion, + {{"open-companion-for-image-search", "true"}}}}, + {}); + SetUserSelectedDefaultSearchProvider("https://www.google.com", + /*supports_image_search=*/true); + content::ContextMenuParams params = CreateParams(MenuItem::IMAGE); + params.has_image_contents = true; + TestRenderViewContextMenu menu(*web_contents()->GetPrimaryMainFrame(), + params); + menu.SetBrowser(GetBrowser()); + menu.Init(); + + size_t index = 0; + ui::MenuModel* model = nullptr; + + ASSERT_TRUE(menu.GetMenuModelAndItemIndex( + IDC_CONTENT_CONTEXT_SEARCHLENSFORIMAGE, &model, &index)); + + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(last_preresolved_url().spec(), "https://lens.google.com/"); +} + +// Verify that the adding the companion region search option to the menu +// issues a preconnection request to lens.google.com. +TEST_F(RenderViewContextMenuPrefsTest, + CompanionRegionSearchIssuesGoogleLensPreconnect) { + BeginPreresolveListening(); + base::test::ScopedFeatureList features; + features.InitWithFeaturesAndParameters( + {{companion::features::internal::kSidePanelCompanion, + {{"open-companion-for-image-search", "true"}}}}, + {}); + SetUserSelectedDefaultSearchProvider("https://www.google.com", + /*supports_image_search=*/true); + content::ContextMenuParams params = CreateParams(MenuItem::PAGE); + + TestRenderViewContextMenu menu(*web_contents()->GetPrimaryMainFrame(), + params); + menu.SetBrowser(GetBrowser()); + menu.Init(); + + size_t index = 0; + ui::MenuModel* model = nullptr; + + ASSERT_TRUE(menu.GetMenuModelAndItemIndex( + IDC_CONTENT_CONTEXT_LENS_REGION_SEARCH, &model, &index)); + EXPECT_TRUE(menu.IsItemPresent(IDC_CONTENT_CONTEXT_LENS_REGION_SEARCH)); + + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(last_preresolved_url().spec(), "https://lens.google.com/"); +} + +// Verify that the adding the Lens image search option to the menu +// issues a preconnection request to lens.google.com. +TEST_F(RenderViewContextMenuPrefsTest, + LensImageSearchIssuesGoogleLensPreconnect) { + BeginPreresolveListening(); + base::test::ScopedFeatureList features; + features.InitAndEnableFeature(lens::features::kLensStandalone); + SetUserSelectedDefaultSearchProvider("https://www.google.com", + /*supports_image_search=*/true); + content::ContextMenuParams params = CreateParams(MenuItem::IMAGE); + params.has_image_contents = true; + TestRenderViewContextMenu menu(*web_contents()->GetPrimaryMainFrame(), + params); + menu.SetBrowser(GetBrowser()); + menu.Init(); + + size_t index = 0; + ui::MenuModel* model = nullptr; + + ASSERT_TRUE(menu.GetMenuModelAndItemIndex( + IDC_CONTENT_CONTEXT_SEARCHLENSFORIMAGE, &model, &index)); + + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(last_preresolved_url().spec(), "https://lens.google.com/"); +} + +// Verify that the adding the Lens region search option to the menu +// issues a preconnection request to lens.google.com. +TEST_F(RenderViewContextMenuPrefsTest, + LensRegionSearchIssuesGoogleLensPreconnect) { + BeginPreresolveListening(); + base::test::ScopedFeatureList features; + features.InitAndEnableFeature(lens::features::kLensStandalone); + SetUserSelectedDefaultSearchProvider("https://www.google.com", + /*supports_image_search=*/true); + content::ContextMenuParams params = CreateParams(MenuItem::PAGE); + + TestRenderViewContextMenu menu(*web_contents()->GetPrimaryMainFrame(), + params); + menu.SetBrowser(GetBrowser()); + menu.Init(); + + size_t index = 0; + ui::MenuModel* model = nullptr; + + ASSERT_TRUE(menu.GetMenuModelAndItemIndex( + IDC_CONTENT_CONTEXT_LENS_REGION_SEARCH, &model, &index)); + EXPECT_TRUE(menu.IsItemPresent(IDC_CONTENT_CONTEXT_LENS_REGION_SEARCH)); + + base::RunLoop().RunUntilIdle(); + ASSERT_EQ(last_preresolved_url().spec(), "https://lens.google.com/"); +} + // Verify that the new badge is added to region search context menu items if // appropriate feature is enabled. TEST_F(RenderViewContextMenuPrefsTest, diff --git a/components/lens/lens_features.cc b/components/lens/lens_features.cc index 9ecb4ee1a4adba..5bddfca8f54e85 100644 --- a/components/lens/lens_features.cc +++ b/components/lens/lens_features.cc @@ -78,6 +78,12 @@ constexpr base::FeatureParam &kLensStandalone, "dismiss-loading-state-on-navigation-entry-committed", true}; +constexpr base::FeatureParam kShouldIssuePreconnectForLens{ + &kLensStandalone, "lens-issue-preconnect", true}; + +constexpr base::FeatureParam kPreconnectKeyForLens{ + &kLensStandalone, "lens-preconnect-key", "https://google.com"}; + constexpr base::FeatureParam kDismissLoadingStateOnDidFinishLoad{ &kLensStandalone, "dismiss-loading-state-on-did-finish-load", false}; @@ -237,5 +243,13 @@ bool GetLensPingIsSequential() { return kPingLensSequentially.Get(); } +bool GetShouldIssuePreconnectForLens() { + return kShouldIssuePreconnectForLens.Get(); +} + +std::string GetPreconnectKeyForLens() { + return kPreconnectKeyForLens.Get(); +} + } // namespace features } // namespace lens diff --git a/components/lens/lens_features.h b/components/lens/lens_features.h index cc6b65f7076b49..c8176f45f6bb5c 100644 --- a/components/lens/lens_features.h +++ b/components/lens/lens_features.h @@ -257,6 +257,16 @@ extern std::string GetLensPingURL(); // Returns whether or not the Lens ping should be done sequentially. COMPONENT_EXPORT(LENS_FEATURES) extern bool GetLensPingIsSequential(); + +// Returns whether to issue Lens preconnect requests when the +// context menu item is shown. +COMPONENT_EXPORT(LENS_FEATURES) +extern bool GetShouldIssuePreconnectForLens(); + +// Returns the preconnect url to use for when the context menu item +// is shown. +COMPONENT_EXPORT(LENS_FEATURES) +extern std::string GetPreconnectKeyForLens(); } // namespace features } // namespace lens