| // 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. |
| |
| // This class runs functional tests for lens overlay. These tests spin up a full |
| // web browser, but allow for inspection and modification of internal state of |
| // LensOverlayController and other business-logic classes. |
| |
| #include "chrome/browser/ui/lens/lens_overlay_controller.h" |
| |
| #include <memory> |
| |
| #include "base/base64url.h" |
| #include "base/compiler_specific.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/memory/raw_ptr_exclusion.h" |
| #include "base/path_service.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/test/bind.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/metrics/user_action_tester.h" |
| #include "base/test/protobuf_matchers.h" |
| #include "base/test/run_until.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/with_feature_override.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/companion/text_finder/text_highlighter.h" |
| #include "chrome/browser/companion/text_finder/text_highlighter_manager.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/lens_side_panel.mojom.h" |
| #include "chrome/browser/lens/core/mojom/overlay_object.mojom.h" |
| #include "chrome/browser/lens/core/mojom/page_content_type.mojom.h" |
| #include "chrome/browser/lens/core/mojom/polygon.mojom.h" |
| #include "chrome/browser/lens/core/mojom/text.mojom-forward.h" |
| #include "chrome/browser/lens/core/mojom/text.mojom.h" |
| #include "chrome/browser/pdf/pdf_extension_test_base.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.h" |
| #include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h" |
| #include "chrome/browser/signin/identity_manager_factory.h" |
| #include "chrome/browser/sync/sync_service_factory.h" |
| #include "chrome/browser/themes/theme_service.h" |
| #include "chrome/browser/themes/theme_service_factory.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_actions.h" |
| #include "chrome/browser/ui/browser_command_controller.h" |
| #include "chrome/browser/ui/browser_commands.h" |
| #include "chrome/browser/ui/browser_element_identifiers.h" |
| #include "chrome/browser/ui/browser_tabstrip.h" |
| #include "chrome/browser/ui/browser_window/public/browser_window_features.h" |
| #include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h" |
| #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h" |
| #include "chrome/browser/ui/find_bar/find_bar_controller.h" |
| #include "chrome/browser/ui/hats/hats_service_factory.h" |
| #include "chrome/browser/ui/hats/mock_hats_service.h" |
| #include "chrome/browser/ui/lens/lens_overlay_colors.h" |
| #include "chrome/browser/ui/lens/lens_overlay_entry_point_controller.h" |
| #include "chrome/browser/ui/lens/lens_overlay_gen204_controller.h" |
| #include "chrome/browser/ui/lens/lens_overlay_query_controller.h" |
| #include "chrome/browser/ui/lens/lens_overlay_side_panel_coordinator.h" |
| #include "chrome/browser/ui/lens/lens_overlay_untrusted_ui.h" |
| #include "chrome/browser/ui/lens/lens_overlay_url_builder.h" |
| #include "chrome/browser/ui/lens/lens_permission_bubble_controller.h" |
| #include "chrome/browser/ui/lens/lens_search_controller.h" |
| #include "chrome/browser/ui/lens/lens_side_panel_untrusted_ui.h" |
| #include "chrome/browser/ui/lens/test_lens_overlay_controller.h" |
| #include "chrome/browser/ui/lens/test_lens_overlay_query_controller.h" |
| #include "chrome/browser/ui/lens/test_lens_overlay_side_panel_coordinator.h" |
| #include "chrome/browser/ui/lens/test_lens_search_contextualization_controller.h" |
| #include "chrome/browser/ui/lens/test_lens_search_controller.h" |
| #include "chrome/browser/ui/location_bar/location_bar.h" |
| #include "chrome/browser/ui/tabs/public/tab_features.h" |
| #include "chrome/browser/ui/tabs/split_tab_metrics.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/browser/ui/ui_features.h" |
| #include "chrome/browser/ui/views/frame/browser_view.h" |
| #include "chrome/browser/ui/views/location_bar/location_bar_view.h" |
| #include "chrome/browser/ui/views/omnibox/omnibox_view_views.h" |
| #include "chrome/browser/ui/views/page_action/page_action_container_view.h" |
| #include "chrome/browser/ui/views/page_action/page_action_icon_controller.h" |
| #include "chrome/browser/ui/views/page_action/page_action_view.h" |
| #include "chrome/browser/ui/views/side_panel/side_panel.h" |
| #include "chrome/browser/ui/views/side_panel/side_panel_coordinator.h" |
| #include "chrome/browser/ui/views/side_panel/side_panel_entry_id.h" |
| #include "chrome/browser/ui/views/side_panel/side_panel_util.h" |
| #include "chrome/browser/ui/webui/feedback/feedback_dialog.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/extensions/api/pdf_viewer_private.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/base32/base32.h" |
| #include "components/constrained_window/constrained_window_views.h" |
| #include "components/feature_engagement/public/feature_constants.h" |
| #include "components/lens/lens_features.h" |
| #include "components/lens/lens_overlay_dismissal_source.h" |
| #include "components/lens/lens_overlay_invocation_source.h" |
| #include "components/lens/lens_overlay_mime_type.h" |
| #include "components/lens/lens_overlay_permission_utils.h" |
| #include "components/lens/lens_overlay_side_panel_menu_option.h" |
| #include "components/lens/lens_overlay_side_panel_result.h" |
| #include "components/lens/proto/server/lens_overlay_response.pb.h" |
| #include "components/omnibox/common/omnibox_features.h" |
| #include "components/optimization_guide/content/browser/page_context_eligibility.h" |
| #include "components/optimization_guide/content/browser/page_context_eligibility_api.h" |
| #include "components/permissions/test/permission_request_observer.h" |
| #include "components/policy/core/browser/browser_policy_connector.h" |
| #include "components/policy/core/common/mock_configuration_policy_provider.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/sessions/content/session_tab_helper.h" |
| #include "components/tabs/public/tab_interface.h" |
| #include "components/ukm/test_ukm_recorder.h" |
| #include "content/public/browser/context_menu_params.h" |
| #include "content/public/browser/navigation_controller.h" |
| #include "content/public/browser/page_navigator.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/render_widget_host_view.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/referrer.h" |
| #include "content/public/common/result_codes.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/hit_test_region_observer.h" |
| #include "content/public/test/network_connection_change_simulator.h" |
| #include "content/public/test/no_renderer_crashes_assertion.h" |
| #include "content/public/test/test_navigation_observer.h" |
| #include "extensions/browser/test_event_router_observer.h" |
| #include "mojo/public/cpp/bindings/receiver.h" |
| #include "net/base/mock_network_change_notifier.h" |
| #include "net/base/network_change_notifier.h" |
| #include "net/base/url_util.h" |
| #include "net/test/embedded_test_server/http_response.h" |
| #include "pdf/pdf_features.h" |
| #include "services/metrics/public/cpp/ukm_builders.h" |
| #include "third_party/blink/public/mojom/context_menu/context_menu.mojom.h" |
| #include "third_party/lens_server_proto/lens_overlay_selection_type.pb.h" |
| #include "third_party/lens_server_proto/lens_overlay_server.pb.h" |
| #include "third_party/lens_server_proto/lens_overlay_service_deps.pb.h" |
| #include "ui/base/page_transition_types.h" |
| #include "ui/base/unowned_user_data/user_data_factory.h" |
| #include "ui/base/window_open_disposition.h" |
| #include "ui/compositor/compositor_switches.h" |
| #include "ui/events/base_event_utils.h" |
| #include "ui/events/event_constants.h" |
| #include "ui/views/accessibility/view_accessibility.h" |
| #include "ui/views/controls/webview/webview.h" |
| #include "ui/views/interaction/element_tracker_views.h" |
| #include "ui/views/test/widget_test.h" |
| #include "ui/views/view_utils.h" |
| #include "ui/views/widget/any_widget_observer.h" |
| #include "url/origin.h" |
| |
| namespace { |
| |
| constexpr char kDocumentWithNamedElement[] = "/select.html"; |
| constexpr char kDocumentWithNamedElementWithFragment[] = |
| "/select.html#fragment"; |
| constexpr char kDocumentWithImage[] = "/test_visual.html"; |
| constexpr char kDocumentWithDynamicColor[] = "/lens/dynamic_color.html"; |
| constexpr char kPdfDocument[] = "/pdf/test.pdf"; |
| constexpr char kMultiPagePdf[] = "/pdf/test-bookmarks.pdf"; |
| constexpr char kPdfDocumentWithForm[] = "/pdf/submit_form.pdf"; |
| constexpr char kDocumentWithNonAsciiCharacters[] = "/non-ascii.html"; |
| constexpr char kImageFile[] = "/handbag.png"; |
| constexpr char kVideoFile[] = "/media/bear-640x360-a_frag-cenc.mp4"; |
| constexpr char kAudioFile[] = "/media/pink_noise_140ms.wav"; |
| constexpr char kJsonFile[] = "/web_apps/basic.json"; |
| |
| constexpr char kPdfDocument12KbFileName[] = "pdf/test-title.pdf"; |
| |
| constexpr char kHelloWorldDataUri[] = |
| "data:text/html,%3Ch1%3EHello%2C%20World%21%3C%2Fh1%3E"; |
| |
| using ::base::test::EqualsProto; |
| using ::testing::_; |
| using State = LensOverlayController::State; |
| using LensOverlayInvocationSource = lens::LensOverlayInvocationSource; |
| using LensOverlayDismissalSource = lens::LensOverlayDismissalSource; |
| |
| // The fake server session id. |
| constexpr char kTestServerSessionId[] = "server_session_id"; |
| |
| // The fake search session id. |
| constexpr char kTestSearchSessionId[] = "search_session_id"; |
| |
| constexpr char kNewTabLinkClickScript[] = |
| "(function() {const anchor = document.createElement('a');anchor.href = " |
| "$1;anchor.target = " |
| "'_blank';document.body.appendChild(anchor);anchor.click();})();"; |
| |
| constexpr char kSameTabLinkClickScript[] = |
| "(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;" |
| "const searchboxInputLoaded = " |
| " " |
| "root.getElementById('searchbox').shadowRoot.getElementById('input').value " |
| " === $1; return searchboxInputLoaded;})();"; |
| |
| constexpr char kRequestNotificationsScript[] = R"( |
| new Promise(resolve => { |
| Notification.requestPermission().then(function (permission) { |
| resolve(permission); |
| }); |
| }) |
| )"; |
| |
| constexpr char kCheckSidePanelResultsLoadedScript[] = |
| "(function() {const root = " |
| "document.getElementsByTagName('lens-side-panel-app')[0].shadowRoot; " |
| "const iframeSrcLoaded = " |
| " root.getElementById('results').src.includes('q=' + $1);" |
| "const searchboxInputLoaded = " |
| " " |
| "root.getElementById('searchbox').shadowRoot.getElementById('input').value " |
| " === $1; return iframeSrcLoaded && searchboxInputLoaded;})();"; |
| |
| constexpr char kCheckSidePanelTranslateResultsLoadedScript[] = |
| "(function() {const root = " |
| "document.getElementsByTagName('lens-side-panel-app')[0].shadowRoot; " |
| "const iframeSrcLoaded = " |
| " root.getElementById('results').src.includes('q=' + $1);" |
| "const stickPresent = " |
| " root.getElementById('results').src.includes('stick=');" |
| "const searchboxInputLoaded = " |
| " " |
| "root.getElementById('searchbox').shadowRoot.getElementById('input').value " |
| " === $1; return iframeSrcLoaded && stickPresent && " |
| " searchboxInputLoaded;})();"; |
| |
| constexpr char kCheckSidePanelThumbnailShownScript[] = |
| "(function() {const appRoot = " |
| "document.getElementsByTagName('lens-side-panel-app')[0].shadowRoot;" |
| "const searchboxRoot = appRoot.getElementById('searchbox').shadowRoot;" |
| "const thumbContainer = searchboxRoot.getElementById('thumbnailContainer');" |
| "const thumbnailRoot = " |
| "searchboxRoot.getElementById('thumbnail').shadowRoot;" |
| "const imageSrc = thumbnailRoot.getElementById('image').src;" |
| "return window.getComputedStyle(thumbContainer).display !== 'none' && " |
| " imageSrc.startsWith('data:image/jpeg');})();"; |
| |
| constexpr char kHistoryStateScript[] = |
| "(function() {history.replaceState({'test':1}, 'test'); " |
| "history.pushState({'test':1}, 'test'); history.back();})();"; |
| |
| // `content::ExecJs` can handle promises, so queue a promise that only succeeds |
| // after the contents have been rendered. |
| constexpr char kPaintWorkaroundFunction[] = |
| "() => new Promise(resolve => requestAnimationFrame(() => resolve(true)))"; |
| |
| constexpr char kTestSuggestSignals[] = "encoded_image_signals"; |
| |
| constexpr char kQuerySubmissionTimeQueryParameter[] = "qsubts"; |
| constexpr char kClientUploadDurationQueryParameter[] = "cud"; |
| constexpr char kViewportWidthQueryParamKey[] = "biw"; |
| constexpr char kViewportHeightQueryParamKey[] = "bih"; |
| constexpr char kTextQueryParamKey[] = "q"; |
| constexpr char kChromeSidePanelParameterKey[] = "gsc"; |
| constexpr char kLensRequestQueryParameter[] = "vsrid"; |
| |
| constexpr char kResultsSearchBaseUrl[] = "https://www.google.com/search"; |
| |
| // The test time. |
| constexpr base::Time kTestTime = base::Time::FromSecondsSinceUnixEpoch(1000); |
| |
| std::string EncodeRequestId(const lens::LensOverlayRequestId& request_id) { |
| std::string serialized_request_id; |
| CHECK(request_id.SerializeToString(&serialized_request_id)); |
| std::string encoded_request_id; |
| base::Base64UrlEncode(serialized_request_id, |
| base::Base64UrlEncodePolicy::OMIT_PADDING, |
| &encoded_request_id); |
| return encoded_request_id; |
| } |
| |
| // Opens the given URL in the given browser and waits for the first paint to |
| // complete. |
| void WaitForPaintImpl( |
| Browser* browser, |
| const GURL& url, |
| WindowOpenDisposition disposition = WindowOpenDisposition::CURRENT_TAB, |
| int browser_test_flags = ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP) { |
| ASSERT_TRUE(ui_test_utils::NavigateToURLWithDisposition( |
| browser, url, disposition, browser_test_flags)); |
| const bool first_paint_completed = |
| browser->tab_strip_model() |
| ->GetActiveTab() |
| ->GetContents() |
| ->CompletedFirstVisuallyNonEmptyPaint(); |
| |
| // Return early if first paint is already completed. |
| if (first_paint_completed) { |
| return; |
| } |
| // Wait for the first paint to complete. The below code works for a majority |
| // of cases, but loading non-html files can lead to the workaround failing, so |
| // this check is still needed. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return browser->tab_strip_model() |
| ->GetActiveTab() |
| ->GetContents() |
| ->CompletedFirstVisuallyNonEmptyPaint(); |
| })); |
| // If the first paint was not mark as completed by the WebContents, use a |
| // workaround to request a frame on the WebContents. This function will only |
| // return when the promise is resolved and thus there is content painted on |
| // the WebContents to allow screenshotting. See crbug.com/334747109 for |
| // details on this possible race condition and the workaround used in |
| // interactive tests. |
| ASSERT_TRUE( |
| content::ExecJs(browser->tab_strip_model()->GetActiveTab()->GetContents(), |
| kPaintWorkaroundFunction)); |
| } |
| |
| void ClickBubbleDialogButton( |
| views::BubbleDialogDelegate* bubble_widget_delegate, |
| views::View* button) { |
| // Reset the timer so that the test click isn't discarded as unintended. |
| bubble_widget_delegate->ResetViewShownTimeStampForTesting(); |
| gfx::Point center(button->width() / 2, button->height() / 2); |
| const ui::MouseEvent event(ui::EventType::kMousePressed, center, center, |
| ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, |
| ui::EF_LEFT_MOUSE_BUTTON); |
| button->OnMousePressed(event); |
| button->OnMouseReleased(event); |
| } |
| |
| std::optional<int64_t> GetFileSizeForTestDataFile(std::string_view file_name) { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| base::FilePath path = base::PathService::CheckedGet(chrome::DIR_TEST_DATA) |
| .AppendASCII(file_name); |
| return base::GetFileSize(path); |
| } |
| |
| const lens::mojom::GeometryPtr kTestGeometry = lens::mojom::Geometry::New( |
| lens::mojom::CenterRotatedBox::New( |
| gfx::RectF(0.5, 0.5, 0.8, 0.8), |
| 0.1, |
| lens::mojom::CenterRotatedBox_CoordinateType::kNormalized), |
| std::vector<lens::mojom::PolygonPtr>()); |
| const lens::mojom::OverlayObjectPtr kTestOverlayObject = |
| lens::mojom::OverlayObject::New("unique_id", kTestGeometry->Clone()); |
| const lens::mojom::TextPtr kTestText = |
| lens::mojom::Text::New(lens::mojom::TextLayout::New(), "en"); |
| const lens::mojom::CenterRotatedBoxPtr kTestRegion = |
| lens::mojom::CenterRotatedBox::New( |
| gfx::RectF(0.5, 0.5, 0.8, 0.8), |
| 0.0, |
| lens::mojom::CenterRotatedBox_CoordinateType::kNormalized); |
| |
| lens::LensOverlayObjectsResponse CreateTestObjectsResponse( |
| bool is_translate, |
| std::vector<std::string> words = {}) { |
| lens::LensOverlayObjectsResponse objects_response; |
| auto* overlay_object = objects_response.add_overlay_objects(); |
| overlay_object->set_id("unique_id"); |
| overlay_object->mutable_geometry()->mutable_bounding_box()->set_center_x( |
| kTestGeometry->bounding_box->box.x()); |
| overlay_object->mutable_geometry()->mutable_bounding_box()->set_center_y( |
| kTestGeometry->bounding_box->box.y()); |
| overlay_object->mutable_geometry()->mutable_bounding_box()->set_width( |
| kTestGeometry->bounding_box->box.width()); |
| overlay_object->mutable_geometry()->mutable_bounding_box()->set_height( |
| kTestGeometry->bounding_box->box.height()); |
| overlay_object->mutable_geometry() |
| ->mutable_bounding_box() |
| ->set_coordinate_type(lens::CoordinateType::NORMALIZED); |
| overlay_object->mutable_geometry()->mutable_bounding_box()->set_rotation_z( |
| kTestGeometry->bounding_box->rotation); |
| |
| // The interaction properties must be present or else the proto converter |
| // will ignore the object. |
| overlay_object->mutable_interaction_properties()->set_select_on_tap(true); |
| |
| // Create the test text object. |
| lens::Text* text = objects_response.mutable_text(); |
| text->set_content_language(is_translate ? "fr" : "en"); |
| |
| // Create a paragraph. |
| lens::TextLayout::Paragraph* paragraph = |
| text->mutable_text_layout()->add_paragraphs(); |
| |
| // Create a line. |
| lens::TextLayout::Line* line = paragraph->add_lines(); |
| |
| for (size_t i = 0; i < words.size(); ++i) { |
| lens::TextLayout::Word* word = line->add_words(); |
| word->set_plain_text(words[i]); |
| word->set_text_separator(" "); |
| word->mutable_geometry()->mutable_bounding_box()->set_center_x(0.1 * i); |
| word->mutable_geometry()->mutable_bounding_box()->set_center_y(0.1); |
| word->mutable_geometry()->mutable_bounding_box()->set_width(0.1); |
| word->mutable_geometry()->mutable_bounding_box()->set_height(0.1); |
| word->mutable_geometry()->mutable_bounding_box()->set_coordinate_type( |
| lens::NORMALIZED); |
| } |
| |
| objects_response.mutable_cluster_info()->set_server_session_id( |
| kTestServerSessionId); |
| objects_response.mutable_cluster_info()->set_search_session_id( |
| kTestSearchSessionId); |
| return objects_response; |
| } |
| |
| class LensOverlayPageFake : public lens::mojom::LensPage { |
| public: |
| void ScreenshotDataReceived(const SkBitmap& screenshot) override { |
| last_received_screenshot_ = screenshot; |
| // Do the real call on the open WebUI we intercepted. |
| overlay_page_->ScreenshotDataReceived(screenshot); |
| } |
| |
| void ObjectsReceived( |
| std::vector<lens::mojom::OverlayObjectPtr> objects) override { |
| last_received_objects_ = std::move(objects); |
| } |
| |
| void TextReceived(lens::mojom::TextPtr text) override { |
| last_received_text_ = std::move(text); |
| } |
| |
| void RegionTextReceived(lens::mojom::TextPtr text, |
| bool is_injected_image) override { |
| last_received_text_ = std::move(text); |
| } |
| |
| void ThemeReceived(lens::mojom::OverlayThemePtr theme) override { |
| last_received_theme_ = std::move(theme); |
| } |
| |
| void ShouldShowContextualSearchBox(bool should_show) override { |
| last_received_should_show_contextual_searchbox_ = should_show; |
| } |
| |
| void NotifyHandshakeComplete() override {} |
| |
| void NotifyResultsPanelOpened() override { |
| did_notify_results_opened_ = true; |
| } |
| |
| void NotifyOverlayClosing() override { did_notify_overlay_closing_ = true; } |
| |
| void SetPostRegionSelection( |
| lens::mojom::CenterRotatedBoxPtr region) override { |
| post_region_selection_ = std::move(region); |
| } |
| |
| void SetTextSelection(int selection_start_index, |
| int selection_end_index) override { |
| text_selection_indexes_ = |
| std::make_pair(selection_start_index, selection_end_index); |
| } |
| |
| void SetTranslateMode(const std::string& source_language, |
| const std::string& target_language) override { |
| source_language_ = source_language; |
| target_language_ = target_language; |
| } |
| |
| void ClearRegionSelection() override { did_clear_region_selection_ = true; } |
| |
| void ClearTextSelection() override { did_clear_text_selection_ = true; } |
| |
| void ClearAllSelections() override { |
| did_clear_region_selection_ = true; |
| did_clear_text_selection_ = true; |
| } |
| |
| void OnCopyCommand() override { did_trigger_copy = true; } |
| |
| void SuppressGhostLoader() override {} |
| |
| void PageContentTypeChanged( |
| lens::mojom::PageContentType new_page_content_type) override {} |
| |
| void Reset() { |
| last_received_screenshot_.reset(); |
| last_received_theme_->reset(); |
| last_received_objects_ = std::vector<lens::mojom::OverlayObjectPtr>(); |
| last_received_text_.reset(); |
| post_region_selection_.reset(); |
| source_language_.clear(); |
| target_language_.clear(); |
| last_received_should_show_contextual_searchbox_ = false; |
| did_notify_results_opened_ = false; |
| did_notify_overlay_closing_ = false; |
| did_clear_region_selection_ = false; |
| did_clear_text_selection_ = false; |
| did_trigger_copy = false; |
| } |
| |
| // The real side panel page that was opened by the lens overlay. Needed to |
| // call real functions on the WebUI. |
| mojo::Remote<lens::mojom::LensPage> overlay_page_; |
| |
| SkBitmap last_received_screenshot_; |
| std::optional<lens::mojom::OverlayThemePtr> last_received_theme_; |
| std::vector<lens::mojom::OverlayObjectPtr> last_received_objects_; |
| bool last_received_should_show_contextual_searchbox_ = false; |
| std::string source_language_; |
| std::string target_language_; |
| lens::mojom::TextPtr last_received_text_; |
| bool did_notify_results_opened_ = false; |
| bool did_notify_overlay_closing_ = false; |
| lens::mojom::CenterRotatedBoxPtr post_region_selection_; |
| std::pair<int, int> text_selection_indexes_; |
| bool did_clear_region_selection_ = false; |
| bool did_clear_text_selection_ = false; |
| bool did_trigger_copy = false; |
| }; |
| |
| // Stubs out network requests and mojo calls. |
| class LensOverlayControllerFake : public lens::TestLensOverlayController { |
| public: |
| LensOverlayControllerFake(tabs::TabInterface* tab, |
| LensSearchController* lens_search_controller, |
| variations::VariationsClient* variations_client, |
| signin::IdentityManager* identity_manager, |
| PrefService* pref_service, |
| syncer::SyncService* sync_service, |
| ThemeService* theme_service) |
| : lens::TestLensOverlayController(tab, |
| lens_search_controller, |
| variations_client, |
| identity_manager, |
| pref_service, |
| sync_service, |
| theme_service) {} |
| |
| void BindOverlay(mojo::PendingReceiver<lens::mojom::LensPageHandler> receiver, |
| mojo::PendingRemote<lens::mojom::LensPage> page) override { |
| // Reset the receiver to close any existing connection. |
| fake_overlay_page_receiver_.reset(); |
| fake_overlay_page_.overlay_page_.reset(); |
| |
| // Set up the fake overlay page to intercept the mojo call. |
| fake_overlay_page_.overlay_page_.Bind(std::move(page)); |
| LensOverlayController::BindOverlay( |
| std::move(receiver), |
| fake_overlay_page_receiver_.BindNewPipeAndPassRemote()); |
| } |
| |
| bool IsScreenshotPossible(content::RenderWidgetHostView*) override { |
| return is_screenshot_possible_; |
| } |
| |
| void FlushForTesting() { fake_overlay_page_receiver_.FlushForTesting(); } |
| |
| LensOverlayPageFake fake_overlay_page_; |
| bool is_screenshot_possible_ = true; |
| mojo::Receiver<lens::mojom::LensPage> fake_overlay_page_receiver_{ |
| &fake_overlay_page_}; |
| }; |
| |
| class LensSearchControllerFake : public lens::TestLensSearchController { |
| public: |
| explicit LensSearchControllerFake(tabs::TabInterface* tab) |
| : lens::TestLensSearchController(tab) {} |
| |
| ~LensSearchControllerFake() override = default; |
| |
| // Helper function to force the fake query controller to return errors in its |
| // responses to full image requests. This should be called before ShowUI. |
| void SetFullImageRequestShouldReturnError() { |
| full_image_request_should_return_error_ = true; |
| } |
| |
| void SetOcrResponseWords(const std::vector<std::string>& words) { |
| ocr_response_words_ = words; |
| } |
| |
| std::string GetLastSearchUrl() { return last_search_url_; } |
| |
| protected: |
| std::unique_ptr<LensOverlayController> CreateLensOverlayController( |
| tabs::TabInterface* tab, |
| LensSearchController* lens_search_controller, |
| variations::VariationsClient* variations_client, |
| signin::IdentityManager* identity_manager, |
| PrefService* pref_service, |
| syncer::SyncService* sync_service, |
| ThemeService* theme_service) override { |
| // Set browser color scheme to light mode for consistency. |
| theme_service->SetBrowserColorScheme( |
| ThemeService::BrowserColorScheme::kLight); |
| |
| return std::make_unique<LensOverlayControllerFake>( |
| tab, lens_search_controller, variations_client, identity_manager, |
| pref_service, sync_service, theme_service); |
| } |
| |
| std::unique_ptr<lens::LensOverlayQueryController> CreateLensQueryController( |
| lens::LensOverlayFullImageResponseCallback full_image_callback, |
| lens::LensOverlayUrlResponseCallback url_callback, |
| lens::LensOverlayInteractionResponseCallback interaction_callback, |
| lens::LensOverlaySuggestInputsCallback suggest_inputs_callback, |
| lens::LensOverlayThumbnailCreatedCallback thumbnail_created_callback, |
| lens::UploadProgressCallback upload_progress_callback, |
| variations::VariationsClient* variations_client, |
| signin::IdentityManager* identity_manager, |
| Profile* profile, |
| lens::LensOverlayInvocationSource invocation_source, |
| bool use_dark_mode, |
| lens::LensOverlayGen204Controller* gen204_controller) override { |
| url_callback_ = url_callback; |
| auto fake_query_controller = |
| std::make_unique<lens::TestLensOverlayQueryController>( |
| full_image_callback, |
| base::BindRepeating( |
| &LensSearchControllerFake::RecordUrlResponseCallback, |
| base::Unretained(this)), |
| interaction_callback, suggest_inputs_callback, |
| thumbnail_created_callback, upload_progress_callback, |
| variations_client, identity_manager, profile, invocation_source, |
| use_dark_mode, gen204_controller); |
| // Set up the fake responses for the query controller. |
| fake_query_controller->set_next_full_image_request_should_return_error( |
| full_image_request_should_return_error_); |
| |
| lens::LensOverlayServerClusterInfoResponse cluster_info_response; |
| cluster_info_response.set_server_session_id(kTestServerSessionId); |
| cluster_info_response.set_search_session_id(kTestSearchSessionId); |
| fake_query_controller->set_fake_cluster_info_response( |
| cluster_info_response); |
| |
| fake_query_controller->set_fake_objects_response( |
| CreateTestObjectsResponse(/*is_translate=*/false, ocr_response_words_)); |
| |
| lens::LensOverlayInteractionResponse interaction_response; |
| interaction_response.set_encoded_response(kTestSuggestSignals); |
| fake_query_controller->set_fake_interaction_response(interaction_response); |
| return fake_query_controller; |
| } |
| |
| std::unique_ptr<lens::LensOverlaySidePanelCoordinator> |
| CreateLensOverlaySidePanelCoordinator() override { |
| return std::make_unique<lens::TestLensOverlaySidePanelCoordinator>(this); |
| } |
| |
| private: |
| // A url response callback that records the url sent to the callback. |
| void RecordUrlResponseCallback(lens::proto::LensOverlayUrlResponse response) { |
| last_search_url_ = response.url(); |
| if (!url_callback_.is_null()) { |
| url_callback_.Run(response); |
| } |
| } |
| |
| std::vector<std::string> ocr_response_words_; |
| std::string last_search_url_; |
| lens::LensOverlayUrlResponseCallback url_callback_; |
| bool full_image_request_should_return_error_ = false; |
| }; |
| |
| namespace { |
| |
| ui::UserDataFactory::ScopedOverride UseFakeLensSearchController() { |
| return tabs::TabFeatures::GetUserDataFactoryForTesting() |
| .AddOverrideForTesting(base::BindRepeating([](tabs::TabInterface& tab) { |
| return std::make_unique<LensSearchControllerFake>(&tab); |
| })); |
| } |
| |
| } // namespace |
| |
| class LensOverlayControllerBrowserTest : public InProcessBrowserTest { |
| protected: |
| LensOverlayControllerBrowserTest() { |
| lens_search_controller_override_ = UseFakeLensSearchController(); |
| } |
| |
| void SetUp() override { |
| ASSERT_TRUE(embedded_test_server()->InitializeAndListen()); |
| SetupFeatureList(); |
| policy_provider_.SetDefaultReturns( |
| /*is_initialization_complete_return=*/true, |
| /*is_first_policy_load_complete_return=*/true); |
| policy::BrowserPolicyConnector::SetPolicyProviderForTesting( |
| &policy_provider_); |
| InProcessBrowserTest::SetUp(); |
| } |
| |
| void SetUpOnMainThread() override { |
| InProcessBrowserTest::SetUpOnMainThread(); |
| embedded_test_server()->StartAcceptingConnections(); |
| |
| // Permits sharing the page screenshot by default. |
| PrefService* prefs = browser()->profile()->GetPrefs(); |
| prefs->SetBoolean(lens::prefs::kLensSharingPageScreenshotEnabled, true); |
| prefs->SetBoolean(lens::prefs::kLensSharingPageContentEnabled, true); |
| |
| mock_hats_service_ = static_cast<MockHatsService*>( |
| HatsServiceFactory::GetInstance()->SetTestingFactoryAndUse( |
| browser()->profile(), base::BindRepeating(&BuildMockHatsService))); |
| } |
| |
| void TearDownOnMainThread() override { |
| EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete()); |
| InProcessBrowserTest::TearDownOnMainThread(); |
| |
| // Disallow sharing the page screenshot by default. |
| PrefService* prefs = browser()->profile()->GetPrefs(); |
| prefs->SetBoolean(lens::prefs::kLensSharingPageScreenshotEnabled, false); |
| prefs->SetBoolean(lens::prefs::kLensSharingPageContentEnabled, false); |
| |
| mock_hats_service_ = nullptr; |
| } |
| |
| virtual void SetupFeatureList() { |
| feature_list_.InitWithFeaturesAndParameters( |
| {{lens::features::kLensOverlay, |
| {{"results-search-url", kResultsSearchBaseUrl}, |
| {"use-dynamic-theme", "true"}, |
| {"use-dynamic-theme-min-population-pct", "0.002"}, |
| {"use-dynamic-theme-min-chroma", "3.0"}}}, |
| {lens::features::kLensOverlayContextualSearchbox, |
| { |
| {"send-page-url-for-contextualization", "true"}, |
| {"use-inner-text-as-context", "true"}, |
| {"update-viewport-each-query", "true"}, |
| }}, |
| {lens::features::kLensOverlayLatencyOptimizations, |
| {{"enable-early-start-query-flow-optimization", "true"}}}, |
| {lens::features::kLensOverlaySurvey, {}}, |
| {lens::features::kLensOverlaySidePanelOpenInNewTab, {}}, |
| {lens::features::kLensOverlayBackToPage, {}}}, |
| /*disabled_features=*/{ |
| lens::features::kLensOverlaySimplifiedSelection}); |
| } |
| |
| const SkBitmap CreateNonEmptyBitmap(int width, int height) { |
| SkBitmap bitmap; |
| bitmap.allocN32Pixels(width, height); |
| bitmap.eraseColor(SK_ColorGREEN); |
| return bitmap; |
| } |
| |
| LensSearchController* GetLensSearchController() { |
| return LensSearchController::From(browser()->GetActiveTabInterface()); |
| } |
| |
| LensOverlayController* GetLensOverlayController() { |
| return browser() |
| ->tab_strip_model() |
| ->GetActiveTab() |
| ->GetTabFeatures() |
| ->lens_overlay_controller(); |
| } |
| |
| content::WebContents* GetOverlayWebContents() { |
| auto* controller = GetLensOverlayController(); |
| return controller->GetOverlayWebViewForTesting()->GetWebContents(); |
| } |
| |
| const std::optional<lens::SearchQuery> GetLoadedSearchQuery() { |
| auto* controller = GetLensOverlayController(); |
| return controller->results_side_panel_coordinator() |
| ->get_loaded_search_query_for_testing(); |
| } |
| |
| const std::vector<lens::SearchQuery>& GetSearchQueryHistory() { |
| auto* controller = GetLensOverlayController(); |
| return controller->results_side_panel_coordinator() |
| ->get_search_query_history_for_testing(); |
| } |
| |
| void OpenLensOverlay(lens::LensOverlayInvocationSource invocation_source) { |
| GetLensSearchController()->OpenLensOverlay(invocation_source); |
| } |
| |
| void OpenLensOverlayWithPendingRegion( |
| lens::LensOverlayInvocationSource invocation_source, |
| lens::mojom::CenterRotatedBoxPtr region, |
| const SkBitmap& region_bitmap) { |
| GetLensSearchController()->OpenLensOverlayWithPendingRegion( |
| invocation_source, std::move(region), region_bitmap); |
| } |
| |
| void SimulateLeftClickDrag(gfx::Point from, gfx::Point to) { |
| auto* overlay_web_contents = GetOverlayWebContents(); |
| |
| // We should wait for the main frame's hit-test data to be ready before |
| // sending the click event below to avoid flakiness. |
| content::WaitForHitTestData(overlay_web_contents->GetPrimaryMainFrame()); |
| content::SimulateMouseEvent(overlay_web_contents, |
| blink::WebInputEvent::Type::kMouseDown, |
| blink::WebMouseEvent::Button::kLeft, from); |
| content::SimulateMouseEvent(overlay_web_contents, |
| blink::WebInputEvent::Type::kMouseMove, |
| blink::WebMouseEvent::Button::kLeft, to); |
| content::SimulateMouseEvent(overlay_web_contents, |
| blink::WebInputEvent::Type::kMouseUp, |
| blink::WebMouseEvent::Button::kLeft, to); |
| content::RunUntilInputProcessed( |
| overlay_web_contents->GetRenderWidgetHostView()->GetRenderWidgetHost()); |
| } |
| |
| // Unable to use `content::SimulateKeyPress()` helper function since it sets |
| // `event.skip_if_unhandled` to true which stops the propagation of the event |
| // to the delegate web view. |
| void SimulateCtrlCKeyPress(content::WebContents* web_content) { |
| // Create the escape key press event. |
| input::NativeWebKeyboardEvent event(blink::WebKeyboardEvent::Type::kChar, |
| blink::WebInputEvent::kControlKey, |
| base::TimeTicks::Now()); |
| event.windows_key_code = ui::VKEY_C; |
| event.dom_key = ui::DomKey::FromCharacter('C'); |
| event.dom_code = static_cast<int>(ui::DomCode::US_C); |
| |
| // Send the event to the Web Contents. |
| web_content->GetPrimaryMainFrame() |
| ->GetRenderViewHost() |
| ->GetWidget() |
| ->ForwardKeyboardEvent(event); |
| } |
| |
| // Lens overlay takes a screenshot of the tab. In order to take a screenshot |
| // the tab must not be about:blank and must be painted. By default opens in |
| // the current tab. |
| void WaitForPaint( |
| std::string_view relative_url = kDocumentWithNamedElement, |
| WindowOpenDisposition disposition = WindowOpenDisposition::CURRENT_TAB, |
| int browser_test_flags = ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP) { |
| const GURL url = embedded_test_server()->GetURL(relative_url); |
| WaitForPaintImpl(browser(), url, disposition, browser_test_flags); |
| } |
| |
| // Helper to remove the start time, client upload duration, and viewport size |
| // query params from the url. |
| GURL RemoveStartTimeAndSizeParams(const GURL& url_to_process) { |
| GURL processed_url = url_to_process; |
| std::string actual_client_upload_duration; |
| bool has_client_upload_duration = net::GetValueForKeyInQuery( |
| GURL(url_to_process), kClientUploadDurationQueryParameter, |
| &actual_client_upload_duration); |
| EXPECT_TRUE(has_client_upload_duration); |
| processed_url = net::AppendOrReplaceQueryParameter( |
| processed_url, kClientUploadDurationQueryParameter, std::nullopt); |
| std::string actual_submission_time; |
| bool has_submission_time = net::GetValueForKeyInQuery( |
| GURL(url_to_process), kQuerySubmissionTimeQueryParameter, |
| &actual_submission_time); |
| EXPECT_TRUE(has_submission_time); |
| processed_url = net::AppendOrReplaceQueryParameter( |
| processed_url, kQuerySubmissionTimeQueryParameter, std::nullopt); |
| std::string actual_viewport_width; |
| bool has_viewport_width = net::GetValueForKeyInQuery( |
| GURL(url_to_process), kViewportWidthQueryParamKey, |
| &actual_viewport_width); |
| std::string actual_viewport_height; |
| bool has_viewport_height = net::GetValueForKeyInQuery( |
| GURL(url_to_process), kViewportHeightQueryParamKey, |
| &actual_viewport_height); |
| EXPECT_TRUE(has_viewport_width); |
| EXPECT_TRUE(has_viewport_height); |
| EXPECT_NE(actual_viewport_width, "0"); |
| EXPECT_NE(actual_viewport_height, "0"); |
| processed_url = net::AppendOrReplaceQueryParameter( |
| processed_url, kViewportWidthQueryParamKey, std::nullopt); |
| processed_url = net::AppendOrReplaceQueryParameter( |
| processed_url, kViewportHeightQueryParamKey, std::nullopt); |
| return processed_url; |
| } |
| |
| void VerifyTextQueriesAreEqual(const GURL& url, const GURL& url_to_compare) { |
| std::string text_query; |
| bool has_text_query = |
| net::GetValueForKeyInQuery(GURL(url), kTextQueryParamKey, &text_query); |
| EXPECT_TRUE(has_text_query); |
| |
| std::string query_to_compare; |
| bool has_query_to_compare = net::GetValueForKeyInQuery( |
| GURL(url_to_compare), kTextQueryParamKey, &query_to_compare); |
| EXPECT_TRUE(has_query_to_compare); |
| |
| EXPECT_EQ(query_to_compare, text_query); |
| } |
| |
| void VerifySearchQueryParameters(const GURL& url_to_process) { |
| std::string gsc_value; |
| bool has_gsc_value = |
| net::GetValueForKeyInQuery(url_to_process, "gsc", &gsc_value); |
| EXPECT_TRUE(has_gsc_value); |
| |
| std::string hl_value; |
| bool has_hl_value = |
| net::GetValueForKeyInQuery(url_to_process, "hl", &hl_value); |
| EXPECT_TRUE(has_hl_value); |
| |
| std::string q_value; |
| bool has_q_value = |
| net::GetValueForKeyInQuery(url_to_process, "q", &q_value); |
| EXPECT_TRUE(has_q_value); |
| } |
| |
| void CloseOverlayAndWaitForOff(LensOverlayController* controller, |
| LensOverlayDismissalSource dismissal_source) { |
| // TODO(crbug.com/404941800): This uses a roundabout way to close the UI. |
| // It has to go through the LensOverlayController because the search |
| // controller doesn't have proper state management. Use search controller |
| // directly once it has its own state for properly determining kOff. |
| LensSearchController::From(controller->GetTabInterface()) |
| ->CloseLensAsync(dismissal_source); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOff; })); |
| } |
| |
| // Helper to get a test context menu on the active tab. |
| std::unique_ptr<TestRenderViewContextMenu> GetContextMenu() { |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| return TestRenderViewContextMenu::Create(web_contents, |
| web_contents->GetURL()); |
| } |
| |
| policy::MockConfigurationPolicyProvider* policy_provider() { |
| return &policy_provider_; |
| } |
| |
| protected: |
| base::test::ScopedFeatureList feature_list_; |
| raw_ptr<MockHatsService> mock_hats_service_ = nullptr; |
| // The words returned by the mock objects response. |
| std::vector<std::string> ocr_response_words_; |
| testing::NiceMock<policy::MockConfigurationPolicyProvider> policy_provider_; |
| |
| private: |
| ui::UserDataFactory::ScopedOverride lens_search_controller_override_; |
| }; |
| |
| } // namespace |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| PermissionBubbleAccept_ScreenshotAndCSBPrefDisabled) { |
| WaitForPaint(); |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Allow sharing the page screenshot but not other page content. |
| PrefService* prefs = browser()->profile()->GetPrefs(); |
| prefs->SetBoolean(lens::prefs::kLensSharingPageScreenshotEnabled, false); |
| prefs->SetBoolean(lens::prefs::kLensSharingPageContentEnabled, false); |
| ASSERT_FALSE(lens::CanSharePageScreenshotWithLensOverlay(prefs)); |
| ASSERT_FALSE(lens::CanSharePageContentWithLensOverlay(prefs)); |
| |
| // Verify attempting to show the UI will still show the permission bubble |
| // with the contextual searchbox enabled. |
| views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{}, |
| lens::kLensPermissionDialogName); |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| // State should remain off. |
| ASSERT_EQ(controller->state(), State::kOff); |
| auto* bubble_widget = waiter.WaitIfNeededAndGet(); |
| // Wait for the bubble to become visible. |
| views::test::WidgetVisibleWaiter(bubble_widget).Wait(); |
| ASSERT_TRUE(bubble_widget->IsVisible()); |
| |
| auto* search_controller = GetLensSearchController(); |
| ASSERT_TRUE( |
| search_controller->get_lens_permission_bubble_controller_for_testing() |
| ->HasOpenDialogWidget()); |
| |
| // Verify attempting to show the UI again does not close the bubble widget. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| // State should remain off. |
| ASSERT_EQ(controller->state(), State::kOff); |
| ASSERT_TRUE(bubble_widget->IsVisible()); |
| ASSERT_TRUE( |
| search_controller->get_lens_permission_bubble_controller_for_testing() |
| ->HasOpenDialogWidget()); |
| |
| // Simulate click on the accept button. |
| auto* bubble_widget_delegate = |
| bubble_widget->widget_delegate()->AsBubbleDialogDelegate(); |
| ClickBubbleDialogButton(bubble_widget_delegate, |
| bubble_widget_delegate->GetOkButton()); |
| ASSERT_FALSE( |
| search_controller->get_lens_permission_bubble_controller_for_testing() |
| ->HasOpenDialogWidget()); |
| |
| // Verify sharing the page content and screenshot are now permitted. |
| ASSERT_TRUE(lens::CanSharePageContentWithLensOverlay(prefs)); |
| ASSERT_TRUE(lens::CanSharePageScreenshotWithLensOverlay(prefs)); |
| |
| // Verify accepting the permission bubble will eventually result in the |
| // overlay state. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify screenshot was captured and stored. |
| auto screenshot_bitmap = controller->initial_screenshot(); |
| EXPECT_FALSE(screenshot_bitmap.empty()); |
| screenshot_bitmap = controller->updated_screenshot(); |
| EXPECT_FALSE(screenshot_bitmap.empty()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| LensOverlayControllerBrowserTest, |
| PermissionBubbleAccept_ScreenshotPrefEnabledCSBPrefDisabled) { |
| WaitForPaint(); |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Allow sharing the page screenshot but not other page content. |
| PrefService* prefs = browser()->profile()->GetPrefs(); |
| prefs->SetBoolean(lens::prefs::kLensSharingPageScreenshotEnabled, true); |
| prefs->SetBoolean(lens::prefs::kLensSharingPageContentEnabled, false); |
| ASSERT_TRUE(lens::CanSharePageScreenshotWithLensOverlay(prefs)); |
| ASSERT_FALSE(lens::CanSharePageContentWithLensOverlay(prefs)); |
| |
| // Verify attempting to show the UI will still show the permission bubble |
| // with the contextual searchbox enabled. |
| views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{}, |
| lens::kLensPermissionDialogName); |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| // State should remain off. |
| ASSERT_EQ(controller->state(), State::kOff); |
| auto* bubble_widget = waiter.WaitIfNeededAndGet(); |
| // Wait for the bubble to become visible. |
| views::test::WidgetVisibleWaiter(bubble_widget).Wait(); |
| ASSERT_TRUE(bubble_widget->IsVisible()); |
| |
| auto* search_controller = GetLensSearchController(); |
| ASSERT_TRUE( |
| search_controller->get_lens_permission_bubble_controller_for_testing() |
| ->HasOpenDialogWidget()); |
| |
| // Verify attempting to show the UI again does not close the bubble widget. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| // State should remain off. |
| ASSERT_EQ(controller->state(), State::kOff); |
| ASSERT_TRUE(bubble_widget->IsVisible()); |
| ASSERT_TRUE( |
| search_controller->get_lens_permission_bubble_controller_for_testing() |
| ->HasOpenDialogWidget()); |
| |
| // Simulate click on the accept button. |
| auto* bubble_widget_delegate = |
| bubble_widget->widget_delegate()->AsBubbleDialogDelegate(); |
| ClickBubbleDialogButton(bubble_widget_delegate, |
| bubble_widget_delegate->GetOkButton()); |
| ASSERT_FALSE( |
| search_controller->get_lens_permission_bubble_controller_for_testing() |
| ->HasOpenDialogWidget()); |
| |
| // Verify sharing the page content and screenshot are now permitted. |
| ASSERT_TRUE(lens::CanSharePageContentWithLensOverlay(prefs)); |
| ASSERT_TRUE(lens::CanSharePageScreenshotWithLensOverlay(prefs)); |
| |
| // Verify accepting the permission bubble will eventually result in the |
| // overlay state. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify screenshot was captured and stored. |
| auto screenshot_bitmap = controller->initial_screenshot(); |
| EXPECT_FALSE(screenshot_bitmap.empty()); |
| screenshot_bitmap = controller->updated_screenshot(); |
| EXPECT_FALSE(screenshot_bitmap.empty()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| PermissionBubble_CSBPrefReject) { |
| WaitForPaint(); |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Allow sharing the page screenshot but not other page content. |
| PrefService* prefs = browser()->profile()->GetPrefs(); |
| prefs->SetBoolean(lens::prefs::kLensSharingPageScreenshotEnabled, true); |
| prefs->SetBoolean(lens::prefs::kLensSharingPageContentEnabled, false); |
| ASSERT_TRUE(lens::CanSharePageScreenshotWithLensOverlay(prefs)); |
| ASSERT_FALSE(lens::CanSharePageContentWithLensOverlay(prefs)); |
| |
| // Verify attempting to show the UI will show the permission bubble. |
| views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{}, |
| lens::kLensPermissionDialogName); |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| // State should remain off. |
| ASSERT_EQ(controller->state(), State::kOff); |
| auto* bubble_widget = waiter.WaitIfNeededAndGet(); |
| // Wait for the bubble to become visible. |
| views::test::WidgetVisibleWaiter(bubble_widget).Wait(); |
| ASSERT_TRUE(bubble_widget->IsVisible()); |
| |
| auto* search_controller = GetLensSearchController(); |
| ASSERT_TRUE( |
| search_controller->get_lens_permission_bubble_controller_for_testing() |
| ->HasOpenDialogWidget()); |
| |
| // Verify attempting to show the UI again does not close the bubble widget. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| // State should remain off. |
| ASSERT_EQ(controller->state(), State::kOff); |
| ASSERT_TRUE(bubble_widget->IsVisible()); |
| ASSERT_TRUE( |
| search_controller->get_lens_permission_bubble_controller_for_testing() |
| ->HasOpenDialogWidget()); |
| |
| // Simulate click on the reject button. |
| auto* bubble_widget_delegate = |
| bubble_widget->widget_delegate()->AsBubbleDialogDelegate(); |
| ClickBubbleDialogButton(bubble_widget_delegate, |
| bubble_widget_delegate->GetCancelButton()); |
| ASSERT_FALSE( |
| search_controller->get_lens_permission_bubble_controller_for_testing() |
| ->HasOpenDialogWidget()); |
| |
| // Verify sharing the page screenshot is still permitted. |
| ASSERT_TRUE(lens::CanSharePageScreenshotWithLensOverlay(prefs)); |
| // Verify sharing the page content is still not permitted. |
| ASSERT_FALSE(lens::CanSharePageContentWithLensOverlay(prefs)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| DoesNotOpenOnCrashedWebContents) { |
| WaitForPaint(); |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Force the live page renderer to terminate. |
| content::WebContents* tab_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| content::RenderProcessHost* process = |
| tab_contents->GetPrimaryMainFrame()->GetProcess(); |
| content::ScopedAllowRendererCrashes allow_renderer_crashes(process); |
| process->Shutdown(content::RESULT_CODE_KILLED); |
| |
| EXPECT_TRUE( |
| base::test::RunUntil([&]() { return tab_contents->IsCrashed(); })); |
| |
| // Showing UI should be a no-op and remain in state off. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kOff); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, CaptureScreenshot) { |
| WaitForPaint(); |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify screenshot was captured and stored. |
| auto screenshot_bitmap = controller->initial_screenshot(); |
| EXPECT_FALSE(screenshot_bitmap.empty()); |
| screenshot_bitmap = controller->updated_screenshot(); |
| EXPECT_FALSE(screenshot_bitmap.empty()); |
| |
| // Verify screenshot was encoded and passed to WebUI. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| EXPECT_FALSE( |
| fake_controller->fake_overlay_page_.last_received_screenshot_.empty()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, CreateAndLoadWebUI) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Assert that the web view was created and loaded WebUI. |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| ASSERT_EQ(GetOverlayWebContents()->GetLastCommittedURL(), |
| GURL(chrome::kChromeUILensOverlayUntrustedURL)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, ShowSidePanel) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Before showing the results panel, there should be no notification sent to |
| // WebUI. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| EXPECT_FALSE(fake_controller->fake_overlay_page_.did_notify_results_opened_); |
| |
| // Now show the side panel. |
| controller->OpenSidePanelForTesting(); |
| |
| // Prevent flakiness by flushing the tasks. |
| fake_controller->FlushForTesting(); |
| |
| auto* coordinator = browser()->GetFeatures().side_panel_coordinator(); |
| EXPECT_TRUE(coordinator->IsSidePanelShowing()); |
| EXPECT_EQ(coordinator->GetCurrentEntryId(), |
| SidePanelEntry::Id::kLensOverlayResults); |
| EXPECT_TRUE(fake_controller->fake_overlay_page_.did_notify_results_opened_); |
| } |
| |
| class TestWebModalDialog : public views::DialogDelegateView { |
| public: |
| TestWebModalDialog() { |
| SetFocusBehavior(FocusBehavior::ALWAYS); |
| SetModalType(ui::mojom::ModalType::kChild); |
| // Dialogs that take focus must have a name and role to pass accessibility |
| // checks. |
| GetViewAccessibility().SetRole(ax::mojom::Role::kDialog); |
| GetViewAccessibility().SetName("Test dialog", |
| ax::mojom::NameFrom::kAttribute); |
| } |
| |
| TestWebModalDialog(const TestWebModalDialog&) = delete; |
| TestWebModalDialog& operator=(const TestWebModalDialog&) = delete; |
| |
| ~TestWebModalDialog() override = default; |
| |
| views::View* GetInitiallyFocusedView() override { return this; } |
| }; |
| |
| namespace { |
| |
| // Show a web modal dialog hosted by `host_contents`. |
| views::Widget* ShowTestWebModalDialog(content::WebContents* host_contents) { |
| return constrained_window::ShowWebModalDialogViews(new TestWebModalDialog, |
| host_contents); |
| } |
| |
| } // namespace |
| |
| // Regression test for crbug.com/375224885. The result side panel can open modal |
| // dialogs (e.g., screen-sharing permission request dialog). If lens overlay |
| // dies (e.g., due to tab refresh) before the side panel web view, the modal |
| // should close normally without crashing. |
| // TODO(crbug.com/413042395): This test is not testing overlay logic, but |
| // instead the side panel logic. Therefore, this test should be moved to a side |
| // panel browsertest file. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, SidePanelModalDialog) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Open the side panel. |
| controller->OpenSidePanelForTesting(); |
| |
| // Prevent flakiness by flushing the tasks. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| fake_controller->FlushForTesting(); |
| |
| // Wait for open animation to progress. This is important, otherwise when we |
| // close the lens overlay at a later time the side panel will be closed |
| // together synchronously. |
| SidePanel* side_panel = |
| BrowserView::GetBrowserViewForBrowser(browser())->unified_side_panel(); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return side_panel->GetAnimationValue() > 0; })); |
| |
| // Open a web modal dialog. |
| views::Widget* modal_widget = ShowTestWebModalDialog( |
| controller->results_side_panel_coordinator()->GetSidePanelWebContents()); |
| views::test::WidgetDestroyedWaiter modal_widget_destroy_waiter(modal_widget); |
| |
| // Close the lens overlay. |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kPageChanged); |
| |
| // Modal dialog should close without crashing. |
| modal_widget_destroy_waiter.Wait(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| ShowSidePanelWithPendingRegion) { |
| EXPECT_CALL(*mock_hats_service_, LaunchDelayedSurveyForWebContents( |
| kHatsSurveyTriggerLensOverlayResults, _, |
| _, _, _, _, _, _, _, _)); |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| SkBitmap initial_bitmap = CreateNonEmptyBitmap(100, 100); |
| OpenLensOverlayWithPendingRegion(LensOverlayInvocationSource::kAppMenu, |
| kTestRegion->Clone(), initial_bitmap); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlayAndResults; })); |
| auto* coordinator = browser()->GetFeatures().side_panel_coordinator(); |
| // Expect the Lens Overlay results panel to open. |
| ASSERT_TRUE(coordinator->IsSidePanelShowing()); |
| EXPECT_EQ(coordinator->GetCurrentEntryId(), |
| SidePanelEntry::Id::kLensOverlayResults); |
| |
| // Verify region was passed to WebUI. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| EXPECT_EQ(kTestRegion, |
| fake_controller->fake_overlay_page_.post_region_selection_); |
| |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| EXPECT_TRUE(fake_query_controller->last_queried_region_bytes()); |
| UNSAFE_TODO(EXPECT_TRUE( |
| memcmp(fake_query_controller->last_queried_region_bytes()->getPixels(), |
| initial_bitmap.getPixels(), |
| initial_bitmap.computeByteSize()) == 0)); |
| EXPECT_EQ(fake_query_controller->last_queried_region_bytes()->width(), 100); |
| EXPECT_EQ(fake_query_controller->last_queried_region_bytes()->height(), 100); |
| EXPECT_EQ(fake_query_controller->last_lens_selection_type(), |
| lens::INJECTED_IMAGE); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, CloseSidePanel) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| // Tab contents web view should be enabled. |
| ASSERT_TRUE(browser()->GetWebView()->GetEnabled()); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| // Tab contents web view should be disabled. |
| ASSERT_FALSE(browser()->GetWebView()->GetEnabled()); |
| |
| // Grab fake controller to test if notify the overlay of being closed. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| EXPECT_FALSE(fake_controller->fake_overlay_page_.did_notify_overlay_closing_); |
| |
| // Open the side panel. |
| controller->OpenSidePanelForTesting(); |
| |
| // Ensure the side panel is showing. |
| auto* coordinator = browser()->GetFeatures().side_panel_coordinator(); |
| EXPECT_TRUE(coordinator->IsSidePanelShowing()); |
| // Tab contents web view should be disabled. |
| ASSERT_FALSE(browser()->GetWebView()->GetEnabled()); |
| |
| // Close the side panel. |
| coordinator->Close(); |
| |
| // Ensure the overlay closes too. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOff; })); |
| // Tab contents web view should be enabled. |
| ASSERT_TRUE(browser()->GetWebView()->GetEnabled()); |
| |
| // The overlay should have been notified of the closing. |
| EXPECT_TRUE(fake_controller->fake_overlay_page_.did_notify_overlay_closing_); |
| } |
| |
| // TODO(crbug.com/341383805): Enable once flakiness is fixed on all platforms. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| DISABLED_DelayPermissionsPrompt) { |
| // Navigate to a page so we can request permissions |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| content::WebContents* contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(contents); |
| |
| permissions::PermissionRequestObserver observer(contents); |
| |
| // Request permission in tab under overlay. |
| EXPECT_TRUE(content::ExecJs( |
| contents->GetPrimaryMainFrame(), kRequestNotificationsScript, |
| content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES)); |
| |
| // Verify no prompt was shown |
| observer.Wait(); |
| EXPECT_FALSE(observer.request_shown()); |
| |
| // Close overlay |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| |
| // Verify a prompt was shown |
| ASSERT_TRUE(base::test::RunUntil([&]() { return observer.request_shown(); })); |
| } |
| |
| // TODO(b/335801964): Test flaky on Mac. |
| #if BUILDFLAG(IS_MAC) |
| #define MAYBE_SidePanelInteractionsAfterRegionSelection \ |
| DISABLED_SidePanelInteractionsAfterRegionSelection |
| #else |
| #define MAYBE_SidePanelInteractionsAfterRegionSelection \ |
| SidePanelInteractionsAfterRegionSelection |
| #endif |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| MAYBE_SidePanelInteractionsAfterRegionSelection) { |
| EXPECT_CALL(*mock_hats_service_, LaunchDelayedSurveyForWebContents( |
| kHatsSurveyTriggerLensOverlayResults, _, |
| _, _, _, _, _, _, _, _)); |
| WaitForPaint(); |
| |
| std::string text_query = "Apples"; |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| EXPECT_TRUE(controller->GetThumbnailForTesting().empty()); |
| EXPECT_EQ(controller->GetPageClassificationForTesting(), |
| metrics::OmniboxEventProto::SEARCH_SIDE_PANEL_SEARCHBOX); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| // We need to flush the mojo receiver calls to make sure the screenshot was |
| // passed back to the WebUI or else the region selection UI will not render. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| fake_controller->FlushForTesting(); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Simulate mouse events on the overlay for drawing a manual region. |
| gfx::Point center = |
| GetOverlayWebContents()->GetContainerBounds().CenterPoint(); |
| gfx::Point off_center = gfx::Point(center); |
| off_center.Offset(100, 100); |
| SimulateLeftClickDrag(center, off_center); |
| |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlayAndResults; })); |
| auto* coordinator = browser()->GetFeatures().side_panel_coordinator(); |
| // Expect the Lens Overlay results panel to open. |
| ASSERT_TRUE(coordinator->IsSidePanelShowing()); |
| EXPECT_EQ(coordinator->GetCurrentEntryId(), |
| SidePanelEntry::Id::kLensOverlayResults); |
| |
| // Verify that the side panel searchbox displays a thumbnail and that the |
| // controller has a copy. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return true == content::EvalJs( |
| controller->GetSidePanelWebContentsForTesting(), |
| content::JsReplace(kCheckSidePanelThumbnailShownScript)); |
| })); |
| EXPECT_FALSE(controller->get_selected_text_for_region().has_value()); |
| EXPECT_FALSE(controller->get_selected_region_for_testing().is_null()); |
| EXPECT_TRUE(base::StartsWith(controller->GetThumbnailForTesting(), "data:")); |
| EXPECT_EQ(controller->GetPageClassificationForTesting(), |
| metrics::OmniboxEventProto::LENS_SIDE_PANEL_SEARCHBOX); |
| |
| // Verify that after text selection, the controller has a copy of the text, |
| // the thumbnail is no longer shown and the controller's copy of the |
| // thumbnail is empty. |
| controller->IssueTextSelectionRequestForTesting(text_query, |
| /*selection_start_index=*/10, |
| /*selection_end_index=*/16); |
| EXPECT_TRUE(content::EvalJs( |
| controller->GetSidePanelWebContentsForTesting() |
| ->GetPrimaryMainFrame(), |
| content::JsReplace(kCheckSearchboxInput, text_query), |
| content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES) |
| .ExtractBool()); |
| EXPECT_TRUE(controller->get_selected_text_for_region().has_value()); |
| EXPECT_TRUE(controller->get_selected_region_for_testing().is_null()); |
| EXPECT_TRUE(controller->GetThumbnailForTesting().empty()); |
| EXPECT_EQ(controller->GetPageClassificationForTesting(), |
| metrics::OmniboxEventProto::SEARCH_SIDE_PANEL_SEARCHBOX); |
| |
| // Verify that after a signal from the searchbox that the text was modified, |
| // no text selection is present. |
| EXPECT_FALSE(fake_controller->fake_overlay_page_.did_clear_text_selection_); |
| controller->OnTextModifiedForTesting(); |
| EXPECT_FALSE(controller->get_selected_text_for_region().has_value()); |
| fake_controller->FlushForTesting(); |
| EXPECT_TRUE(fake_controller->fake_overlay_page_.did_clear_text_selection_); |
| } |
| |
| // TODO(b/350991033): Test flaky on Mac. |
| #if BUILDFLAG(IS_MAC) |
| #define MAYBE_ShowSidePanelAfterTextSelectionRequest \ |
| DISABLED_ShowSidePanelAfterTextSelectionRequest |
| #else |
| #define MAYBE_ShowSidePanelAfterTextSelectionRequest \ |
| ShowSidePanelAfterTextSelectionRequest |
| #endif |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| MAYBE_ShowSidePanelAfterTextSelectionRequest) { |
| EXPECT_CALL(*mock_hats_service_, LaunchDelayedSurveyForWebContents( |
| kHatsSurveyTriggerLensOverlayResults, _, |
| _, _, _, _, _, _, _, _)); |
| WaitForPaint(); |
| |
| std::string text_query = "Apples"; |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| controller->IssueTextSelectionRequestForTesting(text_query, |
| /*selection_start_index=*/0, |
| /*selection_end_index=*/0); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlayAndResults; })); |
| |
| // Expect the Lens Overlay results panel to open. |
| auto* coordinator = browser()->GetFeatures().side_panel_coordinator(); |
| EXPECT_TRUE(coordinator->IsSidePanelEntryShowing( |
| SidePanelEntryKey(SidePanelEntry::Id::kLensOverlayResults))); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| auto search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(search_query); |
| EXPECT_EQ(search_query->search_query_text_, text_query); |
| EXPECT_EQ(search_query->lens_selection_type_, lens::SELECT_TEXT_HIGHLIGHT); |
| |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| EXPECT_EQ(fake_query_controller->last_queried_text(), text_query); |
| EXPECT_EQ(fake_query_controller->last_lens_selection_type(), |
| lens::SELECT_TEXT_HIGHLIGHT); |
| |
| // Verify that the side panel displays our query. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return true == |
| content::EvalJs(controller->GetSidePanelWebContentsForTesting(), |
| content::JsReplace( |
| kCheckSidePanelResultsLoadedScript, text_query)); |
| })); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| SelectionTypeForTranslateTextSelection) { |
| WaitForPaint(); |
| |
| std::string text_query = "Apples"; |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| controller->IssueTextSelectionRequestForTesting(text_query, |
| /*selection_start_index=*/0, |
| /*selection_end_index=*/0, |
| /*is_translate=*/true); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlayAndResults; })); |
| |
| // Expect the Lens Overlay results panel to open. |
| auto* coordinator = browser()->GetFeatures().side_panel_coordinator(); |
| EXPECT_TRUE(coordinator->IsSidePanelEntryShowing( |
| SidePanelEntryKey(SidePanelEntry::Id::kLensOverlayResults))); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| auto search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(search_query); |
| EXPECT_EQ(search_query->search_query_text_, text_query); |
| EXPECT_EQ(search_query->lens_selection_type_, lens::SELECT_TRANSLATED_TEXT); |
| |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| EXPECT_EQ(fake_query_controller->last_queried_text(), text_query); |
| EXPECT_EQ(fake_query_controller->last_lens_selection_type(), |
| lens::SELECT_TRANSLATED_TEXT); |
| } |
| |
| // TODO(b/335028577): Test flaky on Mac. |
| #if BUILDFLAG(IS_MAC) |
| #define MAYBE_ShowSidePanelAfterTranslateSelectionRequest \ |
| DISABLED_ShowSidePanelAfterTranslateSelectionRequest |
| #else |
| #define MAYBE_ShowSidePanelAfterTranslateSelectionRequest \ |
| ShowSidePanelAfterTranslateSelectionRequest |
| #endif |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| MAYBE_ShowSidePanelAfterTranslateSelectionRequest) { |
| EXPECT_CALL(*mock_hats_service_, LaunchDelayedSurveyForWebContents( |
| kHatsSurveyTriggerLensOverlayResults, _, |
| _, _, _, _, _, _, _, _)); |
| WaitForPaint(); |
| |
| std::string text_query = "Manzanas"; |
| std::string content_language = "es"; |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| controller->IssueTranslateSelectionRequestForTesting( |
| text_query, content_language, |
| /*selection_start_index=*/0, |
| /*selection_end_index=*/0); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlayAndResults; })); |
| |
| // Expect the Lens Overlay results panel to open. |
| auto* coordinator = browser()->GetFeatures().side_panel_coordinator(); |
| EXPECT_TRUE(coordinator->IsSidePanelEntryShowing( |
| SidePanelEntryKey(SidePanelEntry::Id::kLensOverlayResults))); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| auto search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(search_query); |
| EXPECT_EQ(search_query->search_query_text_, text_query); |
| EXPECT_EQ(search_query->lens_selection_type_, lens::TRANSLATE_CHIP); |
| |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| EXPECT_EQ(fake_query_controller->last_queried_text(), text_query); |
| EXPECT_EQ(fake_query_controller->last_lens_selection_type(), |
| lens::TRANSLATE_CHIP); |
| |
| // Verify that the side panel displays our query. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return true == |
| content::EvalJs( |
| controller->GetSidePanelWebContentsForTesting(), |
| content::JsReplace(kCheckSidePanelTranslateResultsLoadedScript, |
| text_query)); |
| })); |
| } |
| |
| // TODO(crbug.com/335028577): Test flaky on Mac. |
| #if BUILDFLAG(IS_MAC) |
| #define MAYBE_ShowSidePanelAfterMathSelectionRequest \ |
| DISABLED_ShowSidePanelAfterMathSelectionRequest |
| #else |
| #define MAYBE_ShowSidePanelAfterMathSelectionRequest \ |
| ShowSidePanelAfterMathSelectionRequest |
| #endif |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| MAYBE_ShowSidePanelAfterMathSelectionRequest) { |
| EXPECT_CALL(*mock_hats_service_, LaunchDelayedSurveyForWebContents( |
| kHatsSurveyTriggerLensOverlayResults, _, |
| _, _, _, _, _, _, _, _)); |
| WaitForPaint(); |
| |
| std::string query = "query"; |
| std::string formula = "\\frac{x + 2}{4} = 4"; |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| controller->IssueMathSelectionRequestForTesting(query, formula, |
| /*selection_start_index=*/0, |
| /*selection_end_index=*/0); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlayAndResults; })); |
| |
| // Expect the Lens Overlay results panel to open. |
| auto* coordinator = browser()->GetFeatures().side_panel_coordinator(); |
| EXPECT_TRUE(coordinator->IsSidePanelEntryShowing( |
| SidePanelEntryKey(SidePanelEntry::Id::kLensOverlayResults))); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| auto search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(search_query); |
| EXPECT_EQ(search_query->search_query_text_, query); |
| EXPECT_EQ(search_query->lens_selection_type_, lens::SYMBOLIC_MATH_OBJECT); |
| |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| EXPECT_EQ(fake_query_controller->last_queried_text(), query); |
| EXPECT_EQ(fake_query_controller->last_lens_selection_type(), |
| lens::SYMBOLIC_MATH_OBJECT); |
| |
| // Verify that the side panel displays our query. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return true == content::EvalJs( |
| controller->GetSidePanelWebContentsForTesting(), |
| content::JsReplace( |
| kCheckSidePanelTranslateResultsLoadedScript, query)); |
| })); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| IssueTranslateFullPageRequest) { |
| EXPECT_CALL(*mock_hats_service_, LaunchDelayedSurveyForWebContents( |
| kHatsSurveyTriggerLensOverlayResults, _, |
| _, _, _, _, _, _, _, _)); |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Before sending the requests, we need to reset the fake controller.. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| fake_controller->FlushForTesting(); |
| fake_controller->fake_overlay_page_.Reset(); |
| EXPECT_TRUE( |
| fake_controller->fake_overlay_page_.last_received_objects_.empty()); |
| EXPECT_FALSE(fake_controller->fake_overlay_page_.last_received_text_); |
| |
| std::string source = "auto"; |
| std::string target = "fr"; |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| fake_query_controller->set_fake_objects_response( |
| CreateTestObjectsResponse(/*is_translate=*/true)); |
| controller->IssueTranslateFullPageRequestForTesting(source, target); |
| |
| // Prevent flakiness by flushing the tasks. |
| fake_controller->FlushForTesting(); |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return !fake_controller->fake_overlay_page_.last_received_text_.is_null() && |
| target == |
| fake_controller->fake_overlay_page_.last_received_text_.get() |
| ->content_language.value(); |
| })); |
| |
| // Expect the Lens Overlay results panel to remain closed. |
| auto* coordinator = browser()->GetFeatures().side_panel_coordinator(); |
| EXPECT_FALSE(coordinator->IsSidePanelEntryShowing( |
| SidePanelEntryKey(SidePanelEntry::Id::kLensOverlayResults))); |
| |
| // After flushing the mojo calls, the data should be present. |
| EXPECT_FALSE( |
| fake_controller->fake_overlay_page_.last_received_objects_.empty()); |
| |
| auto* translate_object = |
| fake_controller->fake_overlay_page_.last_received_objects_[0].get(); |
| auto* translated_text = |
| fake_controller->fake_overlay_page_.last_received_text_.get(); |
| EXPECT_TRUE(translate_object); |
| EXPECT_TRUE(translated_text); |
| EXPECT_TRUE(kTestOverlayObject->Equals(*translate_object)); |
| |
| // Now disable translate mode. |
| fake_query_controller->set_fake_objects_response( |
| CreateTestObjectsResponse(/*is_translate=*/false)); |
| controller->IssueEndTranslateModeRequestForTesting(); |
| |
| // Prevent flakiness by flushing the tasks. |
| fake_controller->FlushForTesting(); |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return !fake_controller->fake_overlay_page_.last_received_text_.is_null() && |
| kTestText->content_language.value() == |
| fake_controller->fake_overlay_page_.last_received_text_.get() |
| ->content_language.value(); |
| })); |
| |
| auto* non_translate_object = |
| fake_controller->fake_overlay_page_.last_received_objects_[0].get(); |
| auto* non_translated_text = |
| fake_controller->fake_overlay_page_.last_received_text_.get(); |
| EXPECT_TRUE(non_translate_object); |
| EXPECT_TRUE(non_translated_text); |
| EXPECT_TRUE(kTestOverlayObject->Equals(*non_translate_object)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| IssueTranslateFullPageRequestWithSelectedRegion) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Before sending the requests, we need to reset the fake controller.. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| fake_controller->FlushForTesting(); |
| fake_controller->fake_overlay_page_.Reset(); |
| EXPECT_TRUE( |
| fake_controller->fake_overlay_page_.last_received_objects_.empty()); |
| EXPECT_FALSE(fake_controller->fake_overlay_page_.last_received_text_); |
| |
| // Issuing a region selection request should update the results page. |
| const GURL first_search_url( |
| "https://www.google.com/" |
| "search?source=chrome.cr.menu&vsint=KgwKAggHEgIIAxgBIAI&q=&lns_fp=1" |
| "&lns_mode=un&gsc=2&hl=en-US&cs=0"); |
| controller->IssueLensRegionRequestForTesting(kTestRegion->Clone(), |
| /*is_click=*/false); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| EXPECT_EQ(controller->get_selected_region_for_testing(), kTestRegion); |
| |
| std::string source = "auto"; |
| std::string target = "fr"; |
| controller->IssueTranslateFullPageRequestForTesting(source, target); |
| |
| // Prevent flakiness by flushing the tasks. |
| fake_controller->FlushForTesting(); |
| |
| // The selected region should be null now, as a result of turning on |
| // translate mode. |
| EXPECT_TRUE(controller->get_selected_region_for_testing().is_null()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| HandleStartQueryResponse) { |
| // TODO(crbug.com/398040980): After launching simplified selection, this can |
| // be removed as it will be replaced with the |
| // `HandleStartQueryResponse` test in the |
| // `LensOverlayControllerBrowserSimplifiedSelection` suite. |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Before showing the UI, there should be no set objects or text as |
| // no query flow has started. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| EXPECT_TRUE( |
| fake_controller->fake_overlay_page_.last_received_objects_.empty()); |
| EXPECT_FALSE(fake_controller->fake_overlay_page_.last_received_text_); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| // When the overlay is bound, it should start the query flow which returns a |
| // response for the full image callback. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Prevent flakiness by flushing the tasks. |
| fake_controller->FlushForTesting(); |
| |
| // After flushing the mojo calls, the data should be present. |
| EXPECT_FALSE( |
| fake_controller->fake_overlay_page_.last_received_objects_.empty()); |
| |
| auto* object = |
| fake_controller->fake_overlay_page_.last_received_objects_[0].get(); |
| auto* text = fake_controller->fake_overlay_page_.last_received_text_.get(); |
| EXPECT_TRUE(object); |
| EXPECT_TRUE(text); |
| EXPECT_TRUE(kTestOverlayObject->Equals(*object)); |
| EXPECT_EQ(kTestText->content_language, text->content_language); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| HandleStartQueryResponseError) { |
| base::HistogramTester histogram_tester; |
| WaitForPaint(); |
| |
| // There should be no histograms logged. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.SidePanelResultStatus", |
| /*expected_count=*/0); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Set the full image request to return an error via the search controller. |
| auto* fake_controller = |
| static_cast<LensSearchControllerFake*>(GetLensSearchController()); |
| ASSERT_TRUE(fake_controller); |
| fake_controller->SetFullImageRequestShouldReturnError(); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| // When the overlay is bound, it should start the query flow which returns a |
| // response for the full image callback. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Verify the error page histogram was not recorded since the result panel is |
| // not open. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.SidePanelResultStatus", |
| /*expected_count=*/0); |
| |
| // Side panel is not showing at first. |
| auto* coordinator = browser()->GetFeatures().side_panel_coordinator(); |
| EXPECT_FALSE(coordinator->IsSidePanelShowing()); |
| EXPECT_FALSE(controller->GetSidePanelWebContentsForTesting()); |
| |
| // Issuing a request should show the side panel even if navigation is expected |
| // to fail. |
| controller->IssueTextSelectionRequestForTesting("test query", |
| /*selection_start_index=*/0, |
| /*selection_end_index=*/0); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // Expect the Lens Overlay results panel to open. |
| ASSERT_TRUE(coordinator->IsSidePanelShowing()); |
| EXPECT_EQ(coordinator->GetCurrentEntryId(), |
| SidePanelEntry::Id::kLensOverlayResults); |
| |
| // The recorded histogram should be the start query error. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.SidePanelResultStatus", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.SidePanelResultStatus", |
| lens::SidePanelResultStatus::kErrorPageShownStartQueryError, |
| /*expected_count=*/1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| HandleStartQueryResponseError_Offline) { |
| base::HistogramTester histogram_tester; |
| WaitForPaint(); |
| |
| // There should be no histograms logged. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.SidePanelResultStatus", |
| /*expected_count=*/0); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Set the full image request to return an error via the search controller. |
| auto* fake_controller = |
| static_cast<LensSearchControllerFake*>(GetLensSearchController()); |
| fake_controller->SetFullImageRequestShouldReturnError(); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| // When the overlay is bound, it should start the query flow which returns a |
| // response for the full image callback. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Verify the error page histogram was not recorded since the result panel is |
| // not open. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.SidePanelResultStatus", |
| /*expected_count=*/0); |
| |
| // Set the network connection type to being offline. |
| auto scoped_mock_network_change_notifier = |
| std::make_unique<net::test::ScopedMockNetworkChangeNotifier>(); |
| scoped_mock_network_change_notifier->mock_network_change_notifier() |
| ->SetConnectionType(net::NetworkChangeNotifier::CONNECTION_NONE); |
| |
| // Side panel is not showing at first. |
| auto* coordinator = browser()->GetFeatures().side_panel_coordinator(); |
| EXPECT_FALSE(coordinator->IsSidePanelShowing()); |
| EXPECT_FALSE(controller->GetSidePanelWebContentsForTesting()); |
| |
| // Issuing a request should show the side panel even if navigation is expected |
| // to fail. |
| controller->IssueTextSelectionRequestForTesting("test query", |
| /*selection_start_index=*/0, |
| /*selection_end_index=*/0); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // Expect the Lens Overlay results panel to open. |
| ASSERT_TRUE(coordinator->IsSidePanelShowing()); |
| EXPECT_EQ(coordinator->GetCurrentEntryId(), |
| SidePanelEntry::Id::kLensOverlayResults); |
| |
| // The recorded histogram should still be the start query error rather than |
| // the network being offline. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.SidePanelResultStatus", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.SidePanelResultStatus", |
| lens::SidePanelResultStatus::kErrorPageShownStartQueryError, |
| /*expected_count=*/1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| HandleInteractionDataResponse) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Before showing the UI, there should be no suggest signals as no query flow |
| // has started. |
| EXPECT_FALSE( |
| controller->GetLensSuggestInputsForTesting().has_encoded_image_signals()); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| // When the overlay is bound, it should start the query flow which returns a |
| // response for the full image callback. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // An after an interaction, the image suggest signals should be set. |
| controller->IssueLensRegionRequestForTesting(kTestRegion->Clone(), |
| /*is_click=*/false); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // The lens response should have been correctly set for use by the searchbox. |
| EXPECT_TRUE( |
| controller->GetLensSuggestInputsForTesting().has_encoded_image_signals()); |
| EXPECT_EQ( |
| controller->GetLensSuggestInputsForTesting().encoded_image_signals(), |
| kTestSuggestSignals); |
| |
| // The tab ID should have been correctly set for use by the searchbox. |
| content::WebContents* tab_web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| SessionID tab_id = sessions::SessionTabHelper::IdForTab(tab_web_contents); |
| EXPECT_EQ(controller->GetTabIdForTesting(), tab_id); |
| |
| EXPECT_TRUE(!controller->GetPageURLForTesting().is_empty()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| BackgroundAndForegroundUI) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| // Tab contents web view should be enabled. |
| ASSERT_TRUE(browser()->GetWebView()->GetEnabled()); |
| |
| // Grab the index of the currently active tab so we can return to it later. |
| int active_controller_tab_index = |
| browser()->tab_strip_model()->active_index(); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible()); |
| EXPECT_TRUE(controller->GetOverlayWebViewForTesting()->GetVisible()); |
| EXPECT_TRUE(controller->GetOverlayViewForTesting()->Contains( |
| controller->GetOverlayWebViewForTesting())); |
| // Tab contents web view should be disabled. |
| ASSERT_FALSE(browser()->GetWebView()->GetEnabled()); |
| |
| // Open a side panel to test that the side panel persists between tab |
| // switches. |
| controller->IssueTextSelectionRequestForTesting("test query", |
| /*selection_start_index=*/0, |
| /*selection_end_index=*/0); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlayAndResults; })); |
| |
| // Verify the side panel is showing. |
| auto* coordinator = browser()->GetFeatures().side_panel_coordinator(); |
| EXPECT_TRUE(coordinator->IsSidePanelShowing()); |
| EXPECT_EQ(coordinator->GetCurrentEntryId(), |
| SidePanelEntry::Id::kLensOverlayResults); |
| |
| // Opening a new tab should background the overlay UI. |
| WaitForPaint(kDocumentWithNamedElement, |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB | |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| EXPECT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kBackground; })); |
| // Overlay view should never be invisible since it is used across tabs. |
| EXPECT_FALSE(controller->GetOverlayViewForTesting()->GetVisible()); |
| EXPECT_FALSE(controller->GetOverlayWebViewForTesting()->GetVisible()); |
| EXPECT_TRUE(base::test::RunUntil( |
| [&]() { return !coordinator->IsSidePanelShowing(); })); |
| // Tab contents web view should be enabled. |
| ASSERT_TRUE(browser()->GetWebView()->GetEnabled()); |
| |
| // Returning back to the previous tab should show the overlay UI again. |
| browser()->tab_strip_model()->ActivateTabAt(active_controller_tab_index); |
| EXPECT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlayAndResults; })); |
| EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible()); |
| EXPECT_TRUE(controller->GetOverlayWebViewForTesting()->GetVisible()); |
| EXPECT_TRUE(controller->GetOverlayViewForTesting()->Contains( |
| controller->GetOverlayWebViewForTesting())); |
| // Side panel should come back when returning to previous tab. |
| EXPECT_TRUE(base::test::RunUntil( |
| [&]() { return coordinator->IsSidePanelShowing(); })); |
| EXPECT_EQ(coordinator->GetCurrentEntryId(), |
| SidePanelEntry::Id::kLensOverlayResults); |
| // Tab contents web view should be disabled. |
| ASSERT_FALSE(browser()->GetWebView()->GetEnabled()); |
| } |
| |
| // TODO(crbug.com/413042395): This test is not testing overlay logic, but |
| // instead the side panel logic. Therefore, this test should be moved to a side |
| // panel browsertest file. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| LoadURLInResultsFrame) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible()); |
| |
| // Side panel is not showing at first. |
| auto* coordinator = browser()->GetFeatures().side_panel_coordinator(); |
| EXPECT_FALSE(coordinator->IsSidePanelShowing()); |
| |
| // Open the side panel. |
| controller->OpenSidePanelForTesting(); |
| |
| // Loading a url in the side panel should show the results page. |
| const GURL search_url("https://www.google.com/search"); |
| controller->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting( |
| search_url); |
| |
| // Expect the Lens Overlay results panel to open. |
| ASSERT_TRUE(coordinator->IsSidePanelShowing()); |
| EXPECT_EQ(coordinator->GetCurrentEntryId(), |
| SidePanelEntry::Id::kLensOverlayResults); |
| } |
| |
| // TODO(crbug.com/413042395): This test is not testing overlay logic, but |
| // instead the side panel logic. Therefore, this test should be moved to a side |
| // panel browsertest file. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| SidePanelResultStatusHistogram_ResultShown) { |
| base::HistogramTester histogram_tester; |
| WaitForPaint(); |
| |
| // There should be no histograms logged. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.SidePanelResultStatus", |
| /*expected_count=*/0); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible()); |
| |
| // Side panel is not showing at first. |
| auto* coordinator = browser()->GetFeatures().side_panel_coordinator(); |
| EXPECT_FALSE(coordinator->IsSidePanelShowing()); |
| EXPECT_FALSE(controller->GetSidePanelWebContentsForTesting()); |
| |
| // Open the side panel. |
| controller->OpenSidePanelForTesting(); |
| |
| // Loading a url in the side panel should show the side panel even if we |
| // expect the navigation to fail. |
| const GURL search_url("https://www.google.com/search"); |
| controller->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting( |
| search_url); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // Expect the Lens Overlay results panel to open. |
| ASSERT_TRUE(coordinator->IsSidePanelShowing()); |
| EXPECT_EQ(coordinator->GetCurrentEntryId(), |
| SidePanelEntry::Id::kLensOverlayResults); |
| |
| // Verify the histogram was set correctly to `kResultShown`. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.SidePanelResultStatus", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount("Lens.Overlay.SidePanelResultStatus", |
| lens::SidePanelResultStatus::kResultShown, |
| /*expected_count=*/1); |
| } |
| |
| // TODO(crbug.com/413042395): This test is not testing overlay logic, but |
| // instead the side panel logic. Therefore, this test should be moved to a side |
| // panel browsertest file. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| OfflineErrorPageInSidePanel) { |
| base::HistogramTester histogram_tester; |
| WaitForPaint(); |
| |
| // There should be no histograms logged. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.SidePanelResultStatus", |
| /*expected_count=*/0); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Set the network connection type to being offline. |
| auto scoped_mock_network_change_notifier = |
| std::make_unique<net::test::ScopedMockNetworkChangeNotifier>(); |
| scoped_mock_network_change_notifier->mock_network_change_notifier() |
| ->SetConnectionType(net::NetworkChangeNotifier::CONNECTION_NONE); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible()); |
| |
| // Side panel is not showing at first. |
| auto* coordinator = browser()->GetFeatures().side_panel_coordinator(); |
| EXPECT_FALSE(coordinator->IsSidePanelShowing()); |
| EXPECT_FALSE(controller->GetSidePanelWebContentsForTesting()); |
| |
| // Issuing a request should show the side panel even if navigation is expected |
| // to fail. |
| controller->IssueTextSelectionRequestForTesting("test query", |
| /*selection_start_index=*/0, |
| /*selection_end_index=*/0); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // Expect the Lens Overlay results panel to open. |
| ASSERT_TRUE(coordinator->IsSidePanelShowing()); |
| EXPECT_EQ(coordinator->GetCurrentEntryId(), |
| SidePanelEntry::Id::kLensOverlayResults); |
| |
| // Verify the error page was set correctly. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.SidePanelResultStatus", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.SidePanelResultStatus", |
| lens::SidePanelResultStatus::kErrorPageShownOffline, |
| /*expected_count=*/1); |
| |
| // Set the network connection type to being online. |
| scoped_mock_network_change_notifier->mock_network_change_notifier() |
| ->SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI); |
| |
| // Issuing a new request after the network connection is back should show the |
| // results page. |
| content::TestNavigationObserver observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->IssueTextSelectionRequestForTesting("test query", |
| /*selection_start_index=*/0, |
| /*selection_end_index=*/0); |
| observer.WaitForNavigationFinished(); |
| |
| // Verify the error page was set correctly. It should be hidden after a |
| // successful navigation. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.SidePanelResultStatus", |
| /*expected_count=*/2); |
| histogram_tester.ExpectBucketCount("Lens.Overlay.SidePanelResultStatus", |
| lens::SidePanelResultStatus::kResultShown, |
| /*expected_count=*/1); |
| } |
| |
| // TODO(crbug.com/413042395): This test is not testing overlay logic, but |
| // instead the side panel logic. Therefore, this test should be moved to a side |
| // panel browsertest file. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| SidePanel_SameTabSameOriginLinkClick) { |
| 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. |
| OpenLensOverlay(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()); |
| |
| // Open the side panel. |
| controller->OpenSidePanelForTesting(); |
| |
| // 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->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting( |
| 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(); |
| |
| // Verify the fake controller exists and reset any loading that was done |
| // before as part of setup. |
| auto* test_side_panel_coordinator = |
| static_cast<lens::TestLensOverlaySidePanelCoordinator*>( |
| controller->results_side_panel_coordinator()); |
| ASSERT_TRUE(test_side_panel_coordinator); |
| test_side_panel_coordinator->ResetSidePanelTracking(); |
| |
| // 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); |
| EXPECT_TRUE(results_frame); |
| |
| // Simulate a same-origin navigation on the results frame. |
| const GURL nav_url("https://www.google.com/search?q=apples"); |
| content::TestNavigationObserver observer( |
| controller->GetSidePanelWebContentsForTesting(), |
| /*expected_number_of_navigations=*/2); |
| EXPECT_TRUE(content::ExecJs( |
| results_frame, content::JsReplace(kSameTabLinkClickScript, nav_url), |
| content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES)); |
| |
| // Wait for the navigation to finish and the page to finish loading. |
| observer.Wait(); |
| |
| // 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. |
| // Loading is set to true twice because the URL is originally malformed. |
| EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_true_, 2); |
| EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_false_, 1); |
| |
| // 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()); |
| } |
| |
| // TODO(crbug.com/413042395): This test is not testing overlay logic, but |
| // instead the side panel logic. Therefore, this test should be moved to a side |
| // panel browsertest file. |
| IN_PROC_BROWSER_TEST_F( |
| LensOverlayControllerBrowserTest, |
| SidePanel_UnsupportedSearchLinkClick_ShouldOpenSearchURLInNewTab) { |
| 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. |
| OpenLensOverlay(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()); |
| |
| // Open the side panel. |
| controller->OpenSidePanelForTesting(); |
| |
| // 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->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting( |
| 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(); |
| |
| // Verify the fake controller exists and reset any loading that was done |
| // before as part of setup. |
| auto* test_side_panel_coordinator = |
| static_cast<lens::TestLensOverlaySidePanelCoordinator*>( |
| controller->results_side_panel_coordinator()); |
| ASSERT_TRUE(test_side_panel_coordinator); |
| test_side_panel_coordinator->ResetSidePanelTracking(); |
| |
| // 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); |
| EXPECT_TRUE(results_frame); |
| |
| // Simulate a same-origin navigation that should open in a new tab on the |
| // results frame. |
| ui_test_utils::AllBrowserTabAddedWaiter add_tab; |
| const GURL nav_url("https://www.google.com/search?q=apples&udm=28"); |
| EXPECT_TRUE(content::ExecJs( |
| results_frame, content::JsReplace(kSameTabLinkClickScript, nav_url), |
| content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES)); |
| |
| // Verify the new tab has the URL. |
| content::WebContents* new_tab = add_tab.Wait(); |
| content::WaitForLoadStop(new_tab); |
| EXPECT_EQ(new_tab->GetLastCommittedURL(), nav_url); |
| // It should open a new tab as this is a an unsupported search URL for the |
| // side panel. |
| EXPECT_EQ(tabs + 1, browser()->tab_strip_model()->count()); |
| |
| // Verify the loading state was not set. |
| EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_true_, 0); |
| EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_false_, 0); |
| } |
| |
| // TODO(crbug.com/413042395): This test is not testing overlay logic, but |
| // instead the side panel logic. Therefore, this test should be moved to a side |
| // panel browsertest file. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| SidePanel_SameTabCrossOriginLinkClick) { |
| 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. |
| OpenLensOverlay(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()); |
| |
| // Open the side panel. |
| controller->OpenSidePanelForTesting(); |
| |
| // 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->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting( |
| 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())); |
| |
| // 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); |
| EXPECT_TRUE(results_frame); |
| |
| // Verify the fake controller exists and reset any loading that was done |
| // before as part of setup. |
| auto* test_side_panel_coordinator = |
| static_cast<lens::TestLensOverlaySidePanelCoordinator*>( |
| controller->results_side_panel_coordinator()); |
| ASSERT_TRUE(test_side_panel_coordinator); |
| test_side_panel_coordinator->ResetSidePanelTracking(); |
| |
| ui_test_utils::AllBrowserTabAddedWaiter add_tab; |
| const GURL nav_url("https://new.domain.com/"); |
| // Simulate a cross-origin navigation on the results frame. |
| EXPECT_TRUE(content::ExecJs( |
| results_frame, content::JsReplace(kSameTabLinkClickScript, nav_url), |
| content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES)); |
| |
| // Verify the new tab has the URL. |
| content::WebContents* new_tab = add_tab.Wait(); |
| content::WaitForLoadStop(new_tab); |
| EXPECT_EQ(new_tab->GetLastCommittedURL(), nav_url); |
| |
| // Verify the loading state was never set. |
| EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_true_, 0); |
| EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_false_, 0); |
| } |
| |
| // TODO(crbug.com/413042395): This test is not testing overlay logic, but |
| // instead the side panel logic. Therefore, this test should be moved to a side |
| // panel browsertest file. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| SidePanel_SearchURLClickWithTextDirective) { |
| base::HistogramTester histogram_tester; |
| 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. |
| OpenLensOverlay(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()); |
| |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "green", AutocompleteMatchType::Type::SEARCH_WHAT_YOU_TYPED, |
| /*is_zero_prefix_suggestion=*/false, |
| std::map<std::string, std::string>()); |
| |
| // Issuing a searchbox request when the controller is in kOverlay state |
| // should result in the state being kLivePageAndResults. This shouldn't |
| // change the CONTEXTUAL_SEARCHBOX page classification. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| EXPECT_EQ(controller->GetPageClassificationForTesting(), |
| metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX); |
| |
| // 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())); |
| |
| // 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); |
| EXPECT_TRUE(results_frame); |
| |
| // Verify the fake controller exists and reset any loading that was done |
| // before as part of setup. |
| auto* test_side_panel_coordinator = |
| static_cast<lens::TestLensOverlaySidePanelCoordinator*>( |
| controller->results_side_panel_coordinator()); |
| ASSERT_TRUE(test_side_panel_coordinator); |
| test_side_panel_coordinator->ResetSidePanelTracking(); |
| |
| // Simulate a same-origin navigation on the results frame. |
| ui_test_utils::AllBrowserTabAddedWaiter add_tab; |
| const GURL nav_url("https://www.google.com/search?q=apples#:~:text=apple"); |
| EXPECT_TRUE(content::ExecJs( |
| results_frame, content::JsReplace(kSameTabLinkClickScript, nav_url), |
| content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES)); |
| |
| // Verify the new tab has the URL. |
| content::WebContents* new_tab = add_tab.Wait(); |
| content::WaitForLoadStop(new_tab); |
| EXPECT_EQ(new_tab->GetLastCommittedURL(), nav_url); |
| |
| // Verify the loading state was never set. |
| EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_true_, 0); |
| EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_false_, 0); |
| |
| // Record the text directive result. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.TextDirectiveResult", 1); |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.TextDirectiveResult", |
| lens::LensOverlayTextDirectiveResult::kOpenedInNewTab, 1); |
| } |
| |
| // TODO(crbug.com/413042395): This test is not testing overlay logic, but |
| // instead the side panel logic. Therefore, this test should be moved to a side |
| // panel browsertest file. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| SidePanel_LinkClickWithTextDirective_TextIsPresent) { |
| base::HistogramTester histogram_tester; |
| 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. |
| OpenLensOverlay(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()); |
| int tabs = browser()->tab_strip_model()->count(); |
| |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "green", AutocompleteMatchType::Type::SEARCH_WHAT_YOU_TYPED, |
| /*is_zero_prefix_suggestion=*/false, |
| std::map<std::string, std::string>()); |
| |
| // Issuing a searchbox request when the controller is in kOverlay state |
| // should result in the state being kLivePageAndResults. This shouldn't |
| // change the CONTEXTUAL_SEARCHBOX page classification. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| EXPECT_EQ(controller->GetPageClassificationForTesting(), |
| metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX); |
| |
| // 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())); |
| |
| // 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); |
| EXPECT_TRUE(results_frame); |
| |
| // Verify the fake controller exists and reset any loading that was done |
| // before as part of setup. |
| auto* test_side_panel_coordinator = |
| static_cast<lens::TestLensOverlaySidePanelCoordinator*>( |
| controller->results_side_panel_coordinator()); |
| ASSERT_TRUE(test_side_panel_coordinator); |
| test_side_panel_coordinator->ResetSidePanelTracking(); |
| |
| std::string relative_url = |
| std::string(kDocumentWithNamedElement) + "#:~:text=select&text=element"; |
| const GURL nav_url = embedded_test_server()->GetURL(relative_url); |
| |
| // There should be no text highlighter manager for the main web contents at |
| // this point. |
| companion::TextHighlighterManager* manager = |
| companion::TextHighlighterManager::GetForPage(browser() |
| ->tab_strip_model() |
| ->GetActiveTab() |
| ->GetContents() |
| ->GetPrimaryPage()); |
| EXPECT_FALSE(manager); |
| |
| // Simulate a cross-origin navigation on the results frame. |
| EXPECT_TRUE(content::ExecJs( |
| results_frame, content::JsReplace(kSameTabLinkClickScript, nav_url), |
| content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES)); |
| |
| // Wait for the TextHighlighterManager to be created. |
| EXPECT_TRUE(base::test::RunUntil([&]() { |
| manager = |
| companion::TextHighlighterManager::GetForPage(browser() |
| ->tab_strip_model() |
| ->GetActiveTab() |
| ->GetContents() |
| ->GetPrimaryPage()); |
| return manager; |
| })); |
| |
| // It should not open a new tab as this only renders text highlights. |
| EXPECT_EQ(tabs, browser()->tab_strip_model()->count()); |
| EXPECT_TRUE(manager); |
| EXPECT_FALSE(manager->get_text_highlighters_for_testing().empty()); |
| for (const auto& highlighter : manager->get_text_highlighters_for_testing()) { |
| EXPECT_TRUE(highlighter->GetTextDirective() == "select" || |
| highlighter->GetTextDirective() == "element"); |
| } |
| EXPECT_TRUE(base::test::RunUntil([&]() { |
| return histogram_tester.GetBucketCount( |
| "Lens.Overlay.TextDirectiveResult", |
| lens::LensOverlayTextDirectiveResult::kFoundOnPage) == 1; |
| })); |
| histogram_tester.ExpectTotalCount("Lens.Overlay.TextDirectiveResult", 1); |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.TextDirectiveResult", |
| lens::LensOverlayTextDirectiveResult::kFoundOnPage, 1); |
| } |
| |
| // TODO(crbug.com/413042395): This test is not testing overlay logic, but |
| // instead the side panel logic. Therefore, this test should be moved to a side |
| // panel browsertest file. |
| // TODO(crbug.com/399899383): Disabled due to flakiness on Mac. |
| #if BUILDFLAG(IS_MAC) |
| #define MAYBE_SidePanel_LinkClickWithTextDirective_TextIsMissing \ |
| DISABLED_SidePanel_LinkClickWithTextDirective_TextIsMissing |
| #else |
| #define MAYBE_SidePanel_LinkClickWithTextDirective_TextIsMissing \ |
| SidePanel_LinkClickWithTextDirective_TextIsMissing |
| #endif |
| IN_PROC_BROWSER_TEST_F( |
| LensOverlayControllerBrowserTest, |
| MAYBE_SidePanel_LinkClickWithTextDirective_TextIsMissing) { |
| base::HistogramTester histogram_tester; |
| 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. |
| OpenLensOverlay(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()); |
| |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "green", AutocompleteMatchType::Type::SEARCH_WHAT_YOU_TYPED, |
| /*is_zero_prefix_suggestion=*/false, |
| std::map<std::string, std::string>()); |
| |
| // Issuing a searchbox request when the controller is in kOverlay state |
| // should result in the state being kLivePageAndResults. This shouldn't |
| // change the CONTEXTUAL_SEARCHBOX page classification. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| EXPECT_EQ(controller->GetPageClassificationForTesting(), |
| metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX); |
| |
| // 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())); |
| |
| // 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); |
| EXPECT_TRUE(results_frame); |
| |
| // Verify the fake controller exists and reset any loading that was done |
| // before as part of setup. |
| auto* test_side_panel_coordinator = |
| static_cast<lens::TestLensOverlaySidePanelCoordinator*>( |
| controller->results_side_panel_coordinator()); |
| ASSERT_TRUE(test_side_panel_coordinator); |
| test_side_panel_coordinator->ResetSidePanelTracking(); |
| |
| std::string relative_url = |
| std::string(kDocumentWithNamedElement) + "#:~:text=javascript"; |
| const GURL nav_url = embedded_test_server()->GetURL(relative_url); |
| |
| // Simulate a cross-origin navigation on the results frame. |
| ui_test_utils::AllBrowserTabAddedWaiter add_tab; |
| EXPECT_TRUE(content::ExecJs( |
| results_frame, content::JsReplace(kSameTabLinkClickScript, nav_url), |
| content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES)); |
| |
| // There should be no text highlighter manager for the main web contents at |
| // this point. |
| companion::TextHighlighterManager* manager = |
| companion::TextHighlighterManager::GetForPage(browser() |
| ->tab_strip_model() |
| ->GetActiveTab() |
| ->GetContents() |
| ->GetPrimaryPage()); |
| EXPECT_FALSE(manager); |
| |
| // Verify the new tab has the URL. |
| content::WebContents* new_tab = add_tab.Wait(); |
| EXPECT_TRUE(content::WaitForLoadStop(new_tab)); |
| EXPECT_EQ(new_tab->GetLastCommittedURL(), nav_url); |
| |
| // Verify the loading state was never set. |
| EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_true_, 0); |
| EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_false_, 0); |
| |
| // Record the text directive result. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.TextDirectiveResult", 1); |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.TextDirectiveResult", |
| lens::LensOverlayTextDirectiveResult::kOpenedInNewTab, 1); |
| } |
| |
| // TODO(crbug.com/413042395): This test is not testing overlay logic, but |
| // instead the side panel logic. Therefore, this test should be moved to a side |
| // panel browsertest file. |
| // TODO(crbug.com/399899383): Disabled due to flakiness on Mac. |
| #if BUILDFLAG(IS_MAC) |
| #define MAYBE_SidePanel_LinkClickWithTextDirective_TextIsIncomplete \ |
| DISABLED_SidePanel_LinkClickWithTextDirective_TextIsIncomplete |
| #else |
| #define MAYBE_SidePanel_LinkClickWithTextDirective_TextIsIncomplete \ |
| SidePanel_LinkClickWithTextDirective_TextIsIncomplete |
| #endif |
| IN_PROC_BROWSER_TEST_F( |
| LensOverlayControllerBrowserTest, |
| MAYBE_SidePanel_LinkClickWithTextDirective_TextIsIncomplete) { |
| base::HistogramTester histogram_tester; |
| 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. |
| OpenLensOverlay(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()); |
| |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "green", AutocompleteMatchType::Type::SEARCH_WHAT_YOU_TYPED, |
| /*is_zero_prefix_suggestion=*/false, |
| std::map<std::string, std::string>()); |
| |
| // Issuing a searchbox request when the controller is in kOverlay state |
| // should result in the state being kLivePageAndResults. This shouldn't |
| // change the CONTEXTUAL_SEARCHBOX page classification. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| EXPECT_EQ(controller->GetPageClassificationForTesting(), |
| metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX); |
| |
| // 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())); |
| |
| // 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); |
| EXPECT_TRUE(results_frame); |
| |
| // Verify the fake controller exists and reset any loading that was done |
| // before as part of setup. |
| auto* test_side_panel_coordinator = |
| static_cast<lens::TestLensOverlaySidePanelCoordinator*>( |
| controller->results_side_panel_coordinator()); |
| ASSERT_TRUE(test_side_panel_coordinator); |
| test_side_panel_coordinator->ResetSidePanelTracking(); |
| |
| std::string relative_url = std::string(kDocumentWithNamedElement) + |
| "#:~:text=select&text=element&text=javascript"; |
| const GURL nav_url = embedded_test_server()->GetURL(relative_url); |
| |
| // Simulate a cross-origin navigation on the results frame. |
| ui_test_utils::AllBrowserTabAddedWaiter add_tab; |
| EXPECT_TRUE(content::ExecJs( |
| results_frame, content::JsReplace(kSameTabLinkClickScript, nav_url), |
| content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES)); |
| |
| // There should be no text highlighter manager for the main web contents at |
| // this point. |
| companion::TextHighlighterManager* manager = |
| companion::TextHighlighterManager::GetForPage(browser() |
| ->tab_strip_model() |
| ->GetActiveTab() |
| ->GetContents() |
| ->GetPrimaryPage()); |
| EXPECT_FALSE(manager); |
| |
| // Verify the new tab has the URL. |
| content::WebContents* new_tab = add_tab.Wait(); |
| EXPECT_TRUE(content::WaitForLoadStop(new_tab)); |
| EXPECT_EQ(new_tab->GetLastCommittedURL(), nav_url); |
| |
| // Verify the loading state was never set. |
| EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_true_, 0); |
| EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_false_, 0); |
| |
| // Record the text directive result. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.TextDirectiveResult", 1); |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.TextDirectiveResult", |
| lens::LensOverlayTextDirectiveResult::kOpenedInNewTab, 1); |
| } |
| |
| // TODO(crbug.com/413042395): This test is not testing overlay logic, but |
| // instead the side panel logic. Therefore, this test should be moved to a side |
| // panel browsertest file. |
| 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. |
| OpenLensOverlay(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()); |
| |
| // Open the side panel. |
| controller->OpenSidePanelForTesting(); |
| |
| // 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->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting( |
| 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* test_side_panel_coordinator = |
| static_cast<lens::TestLensOverlaySidePanelCoordinator*>( |
| controller->results_side_panel_coordinator()); |
| ASSERT_TRUE(test_side_panel_coordinator); |
| test_side_panel_coordinator->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(test_side_panel_coordinator->side_panel_loading_set_to_true_, 1); |
| EXPECT_EQ(test_side_panel_coordinator->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()); |
| } |
| |
| // TODO(crbug.com/413042395): This test is not testing overlay logic, but |
| // instead the side panel logic. Therefore, this test should be moved to a side |
| // panel browsertest file. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| SidePanel_NewTabCrossOriginLinkClick) { |
| 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. |
| OpenLensOverlay(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()); |
| |
| // Open the side panel. |
| controller->OpenSidePanelForTesting(); |
| |
| // 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->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting( |
| 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())); |
| |
| // 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://new.domain.com/"); |
| 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* test_side_panel_coordinator = |
| static_cast<lens::TestLensOverlaySidePanelCoordinator*>( |
| controller->results_side_panel_coordinator()); |
| ASSERT_TRUE(test_side_panel_coordinator); |
| test_side_panel_coordinator->ResetSidePanelTracking(); |
| |
| // Simulate a cross-origin navigation on the results frame. |
| ui_test_utils::AllBrowserTabAddedWaiter add_tab; |
| EXPECT_TRUE(content::ExecJs( |
| results_frame, content::JsReplace(kNewTabLinkClickScript, nav_url), |
| content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES)); |
| |
| // Verify the new tab has the URL. |
| content::WebContents* new_tab = add_tab.Wait(); |
| content::WaitForLoadStop(new_tab); |
| EXPECT_EQ(new_tab->GetLastCommittedURL(), nav_url); |
| // Verify the loading state was never set. |
| EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_true_, 0); |
| EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_false_, 0); |
| } |
| |
| // TODO(crbug.com/413042395): This test is not testing overlay logic, but |
| // instead the side panel logic. Therefore, this test should be moved to a side |
| // panel browsertest file. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| SidePanel_NewTabCrossOriginLinkClickFromUntrustedSite) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| EXPECT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should eventually result in overlay state. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| EXPECT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| EXPECT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible()); |
| |
| // Open the side panel. |
| controller->OpenSidePanelForTesting(); |
| |
| // 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->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting( |
| 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://new.domain.com/"); |
| content::OverrideLastCommittedOrigin(results_frame, |
| url::Origin::Create(nav_url)); |
| EXPECT_TRUE(results_frame); |
| |
| // Verify the fake controller exists and reset any loading that was done |
| // before as part of setup. |
| auto* test_side_panel_coordinator = |
| static_cast<lens::TestLensOverlaySidePanelCoordinator*>( |
| controller->results_side_panel_coordinator()); |
| ASSERT_TRUE(test_side_panel_coordinator); |
| test_side_panel_coordinator->ResetSidePanelTracking(); |
| |
| // Simulate a cross-origin navigation on the results frame. |
| EXPECT_TRUE(content::ExecJs( |
| results_frame, content::JsReplace(kNewTabLinkClickScript, nav_url), |
| content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES)); |
| |
| // It should not open a new tab as the initatior origin should not be |
| // considered "trusted". |
| EXPECT_EQ(tabs, browser()->tab_strip_model()->count()); |
| // Verify the loading state was never set. |
| EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_true_, 0); |
| EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_false_, 0); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| CsbLivePageAndResults) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should eventually result in overlay state. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| EXPECT_EQ(controller->GetPageClassificationForTesting(), |
| metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX); |
| |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "green", AutocompleteMatchType::Type::SEARCH_WHAT_YOU_TYPED, |
| /*is_zero_prefix_suggestion=*/false, |
| std::map<std::string, std::string>()); |
| |
| // Issuing a searchbox request when the controller is in kOverlay state |
| // should result in the state being kLivePageAndResults. This shouldn't |
| // change the CONTEXTUAL_SEARCHBOX page classification. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| EXPECT_EQ(controller->GetPageClassificationForTesting(), |
| metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX); |
| } |
| |
| // TODO(crbug.com/413042395): This test is not testing overlay logic, but |
| // instead the side panel logic. Therefore, this test should be moved to a side |
| // panel browsertest file. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| SidePanel_OpenInNewTab) { |
| base::HistogramTester histogram_tester; |
| base::UserActionTester user_action_tester; |
| 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. |
| OpenLensOverlay(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()); |
| |
| // Open the side panel. |
| controller->OpenSidePanelForTesting(); |
| |
| // 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?gsc=2&vsrid=12345"); |
| controller->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting( |
| 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())); |
| |
| // Verify the fake controller exists and reset any loading that was done |
| // before as part of setup. |
| auto* test_side_panel_coordinator = |
| static_cast<lens::TestLensOverlaySidePanelCoordinator*>( |
| controller->results_side_panel_coordinator()); |
| ASSERT_TRUE(test_side_panel_coordinator); |
| test_side_panel_coordinator->ResetSidePanelTracking(); |
| |
| ui_test_utils::AllBrowserTabAddedWaiter add_tab; |
| |
| // Simulate clicking the open in new tab option. |
| coordinator->OpenInNewTab(); |
| |
| // Verify the new tab opens to a URL with the same path, no gsc param, and a |
| // different vsrid. Other params may be changed or unchanged. |
| content::WebContents* new_tab = add_tab.Wait(); |
| content::WaitForLoadStop(new_tab); |
| EXPECT_EQ(new_tab->GetLastCommittedURL().path(), search_url.path()); |
| std::string gsc_value; |
| EXPECT_FALSE(net::GetValueForKeyInQuery(new_tab->GetLastCommittedURL(), |
| kChromeSidePanelParameterKey, |
| &gsc_value)); |
| std::string original_vsrid_value; |
| net::GetValueForKeyInQuery(search_url, kLensRequestQueryParameter, |
| &original_vsrid_value); |
| std::string new_vsrid_value; |
| net::GetValueForKeyInQuery(new_tab->GetLastCommittedURL(), |
| kLensRequestQueryParameter, &new_vsrid_value); |
| EXPECT_NE(original_vsrid_value, new_vsrid_value); |
| |
| // Verify action recorded. |
| EXPECT_EQ(1, user_action_tester.GetActionCount( |
| "SidePanel.LensOverlayResults.NewTabButtonClicked")); |
| |
| // Verify the loading state was never set. |
| EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_true_, 0); |
| EXPECT_EQ(test_side_panel_coordinator->side_panel_loading_set_to_false_, 0); |
| } |
| |
| // TODO(crbug.com/413042395): This test is not testing overlay logic, but |
| // instead the side panel logic. Therefore, this test should be moved to a side |
| // panel browsertest file. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| SidePanel_OpenInNewTabDisabledForContextualQueries) { |
| base::UserActionTester user_action_tester; |
| 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. |
| OpenLensOverlay(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()); |
| |
| // Verify page content was included as bytes in the query. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data() |
| .size() != 0; |
| })); |
| |
| // Verify searchbox is in contextual mode. |
| EXPECT_EQ(controller->GetPageClassificationForTesting(), |
| metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX); |
| |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "hello", AutocompleteMatchType::Type::SEARCH_WHAT_YOU_TYPED, |
| /*is_zero_prefix_suggestion=*/false, |
| std::map<std::string, std::string>()); |
| |
| // Wait for URL to load in side panel. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // Verify the fake controller exists and reset any loading that was done |
| // before as part of setup. |
| auto* test_side_panel_coordinator = |
| static_cast<lens::TestLensOverlaySidePanelCoordinator*>( |
| controller->results_side_panel_coordinator()); |
| ASSERT_TRUE(test_side_panel_coordinator); |
| test_side_panel_coordinator->ResetSidePanelTracking(); |
| |
| // Should do nothing. |
| auto* coordinator = browser()->GetFeatures().side_panel_coordinator(); |
| coordinator->OpenInNewTab(); |
| |
| // Verify no action recorded. |
| EXPECT_EQ(0, user_action_tester.GetActionCount( |
| "SidePanel.LensOverlayResults.NewTabButtonClicked")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| CsbInputTypeSetsLensSelectionType) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should eventually result in overlay state. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| EXPECT_EQ(controller->GetPageClassificationForTesting(), |
| metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX); |
| |
| // Issue a regular searchbox request. |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "green", AutocompleteMatchType::Type::SEARCH_WHAT_YOU_TYPED, |
| /*is_zero_prefix_suggestion=*/false, |
| std::map<std::string, std::string>()); |
| |
| // Wait for URL to load in side panel. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // Verify the query and params are set. |
| auto first_search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(first_search_query); |
| EXPECT_EQ(first_search_query->search_query_text_, "green"); |
| |
| EXPECT_EQ(first_search_query->lens_selection_type_, lens::MULTIMODAL_SEARCH); |
| |
| // Issue a zero prefix suggest searchbox request. |
| content::TestNavigationObserver second_searchbox_query_observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "red", AutocompleteMatchType::Type::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/true, std::map<std::string, std::string>()); |
| |
| // We can't use content::WaitForLoadStop here since the last navigation is |
| // successful. |
| second_searchbox_query_observer.WaitForNavigationFinished(); |
| |
| // Verify the query and params are set. |
| auto second_search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(second_search_query); |
| EXPECT_EQ(second_search_query->search_query_text_, "red"); |
| |
| EXPECT_EQ(second_search_query->lens_selection_type_, |
| lens::MULTIMODAL_SUGGEST_ZERO_PREFIX); |
| |
| // Issue a typeahead suggest searchbox request. |
| content::TestNavigationObserver third_searchbox_query_observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "blue", AutocompleteMatchType::Type::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| std::map<std::string, std::string>()); |
| |
| third_searchbox_query_observer.WaitForNavigationFinished(); |
| |
| // Verify the query and params are set. |
| auto third_search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(third_search_query); |
| EXPECT_EQ(third_search_query->search_query_text_, "blue"); |
| |
| EXPECT_EQ(third_search_query->lens_selection_type_, |
| lens::MULTIMODAL_SUGGEST_TYPEAHEAD); |
| } |
| |
| // TODO THIS SHOULD NOT BE HERE |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| PopAndLoadQueryFromHistory) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should eventually result in overlay state. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| EXPECT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| EXPECT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible()); |
| |
| // Open the side panel. |
| controller->OpenSidePanelForTesting(); |
| |
| // Loading a url in the side panel should show the results page. |
| const GURL first_search_url( |
| "https://www.google.com/" |
| "search?source=chrome.cr.menu&q=oranges&lns_fp=1&lns_mode=text" |
| "&gsc=2&hl=en-US&cs=0"); |
| controller->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting( |
| first_search_url); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // The search query history stack should be empty and the currently loaded |
| // query should be set. |
| EXPECT_TRUE(GetSearchQueryHistory().empty()); |
| auto loaded_search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(loaded_search_query); |
| EXPECT_EQ(loaded_search_query->search_query_text_, "oranges"); |
| VerifySearchQueryParameters(loaded_search_query->search_query_url_); |
| VerifyTextQueriesAreEqual(loaded_search_query->search_query_url_, |
| first_search_url); |
| EXPECT_FALSE(loaded_search_query->selected_region_); |
| EXPECT_FALSE(loaded_search_query->selected_text_); |
| EXPECT_FALSE(loaded_search_query->translate_options_); |
| EXPECT_EQ(loaded_search_query->lens_selection_type_, |
| lens::UNKNOWN_SELECTION_TYPE); |
| |
| // Loading a second url in the side panel should show the results page. |
| const GURL second_search_url( |
| "https://www.google.com/" |
| "search?source=chrome.cr.menu&q=kiwi&lns_fp=1&lns_mode=text&gsc=2" |
| "&hl=en-US&cs=0"); |
| // We can't use content::WaitForLoadStop here since the last navigation is |
| // successful. |
| content::TestNavigationObserver observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting( |
| second_search_url); |
| observer.Wait(); |
| |
| // The search query history stack should have 1 entry and the currently loaded |
| // query should be set to the new query |
| EXPECT_EQ(GetSearchQueryHistory().size(), 1UL); |
| loaded_search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(loaded_search_query); |
| EXPECT_EQ(loaded_search_query->search_query_text_, "kiwi"); |
| VerifySearchQueryParameters(loaded_search_query->search_query_url_); |
| VerifyTextQueriesAreEqual(loaded_search_query->search_query_url_, |
| second_search_url); |
| EXPECT_FALSE(loaded_search_query->selected_region_); |
| EXPECT_FALSE(loaded_search_query->selected_text_); |
| EXPECT_FALSE(loaded_search_query->translate_options_); |
| EXPECT_EQ(loaded_search_query->lens_selection_type_, |
| lens::UNKNOWN_SELECTION_TYPE); |
| VerifySearchQueryParameters(observer.last_navigation_url()); |
| VerifyTextQueriesAreEqual(observer.last_navigation_url(), second_search_url); |
| // Popping the query should load the previous query into the results frame. |
| content::TestNavigationObserver pop_observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->results_side_panel_coordinator()->PopAndLoadQueryFromHistory(); |
| pop_observer.Wait(); |
| |
| // The search query history stack should be empty and the currently loaded |
| // query should be set to the previous query. |
| EXPECT_TRUE(GetSearchQueryHistory().empty()); |
| loaded_search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(loaded_search_query); |
| EXPECT_EQ(loaded_search_query->search_query_text_, "oranges"); |
| VerifySearchQueryParameters(loaded_search_query->search_query_url_); |
| VerifyTextQueriesAreEqual(loaded_search_query->search_query_url_, |
| first_search_url); |
| EXPECT_FALSE(loaded_search_query->selected_region_); |
| EXPECT_FALSE(loaded_search_query->selected_text_); |
| EXPECT_FALSE(loaded_search_query->translate_options_); |
| EXPECT_EQ(loaded_search_query->lens_selection_type_, |
| lens::UNKNOWN_SELECTION_TYPE); |
| VerifySearchQueryParameters(pop_observer.last_navigation_url()); |
| VerifyTextQueriesAreEqual(pop_observer.last_navigation_url(), |
| first_search_url); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| PopAndLoadQueryFromHistoryWithRegionAndTextSelection) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should eventually result in overlay state. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| EXPECT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| EXPECT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible()); |
| |
| // Issuing a text selection request should show the results page. |
| const GURL first_search_url( |
| "https://www.google.com/" |
| "search?source=chrome.cr.menu&vsint=CAMqDAoCCAcSAggDGAEgAg&q=oranges" |
| "&lns_fp=1&lns_mode=text&lns_surface=42&gsc=2&hl=en-US&cs=0"); |
| controller->IssueTextSelectionRequestForTesting("oranges", 20, 200); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // The search query history stack should be empty and the currently loaded |
| // query should be set. |
| EXPECT_TRUE(GetSearchQueryHistory().empty()); |
| auto loaded_search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(loaded_search_query); |
| EXPECT_EQ(loaded_search_query->search_query_text_, "oranges"); |
| GURL url_without_start_time_or_size = |
| RemoveStartTimeAndSizeParams(loaded_search_query->search_query_url_); |
| EXPECT_EQ(url_without_start_time_or_size, first_search_url); |
| EXPECT_TRUE(loaded_search_query->selected_text_); |
| EXPECT_EQ(loaded_search_query->selected_text_->first, 20); |
| EXPECT_EQ(loaded_search_query->selected_text_->second, 200); |
| EXPECT_TRUE(loaded_search_query->selected_region_thumbnail_uri_.empty()); |
| EXPECT_FALSE(loaded_search_query->selected_region_); |
| EXPECT_EQ(loaded_search_query->lens_selection_type_, |
| lens::SELECT_TEXT_HIGHLIGHT); |
| |
| // Issuing a region selection request should update the results page. |
| const GURL second_search_url( |
| "https://www.google.com/" |
| "search?source=chrome.cr.menu&vsint=KgwKAggHEgIIAxgBIAI&q=&lns_fp=1" |
| "&lns_mode=un&gsc=2&hl=en-US&cs=0"); |
| // We can't use content::WaitForLoadStop here and below since the last |
| // navigation was already successful. |
| content::TestNavigationObserver second_search_observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->IssueLensRegionRequestForTesting(kTestRegion->Clone(), |
| /*is_click=*/false); |
| second_search_observer.WaitForNavigationFinished(); |
| |
| // The search query history stack should have 1 entry and the currently loaded |
| // region should be set. |
| EXPECT_EQ(GetSearchQueryHistory().size(), 1UL); |
| loaded_search_query.reset(); |
| loaded_search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(loaded_search_query); |
| EXPECT_EQ(loaded_search_query->search_query_text_, std::string()); |
| EXPECT_FALSE(loaded_search_query->selected_text_); |
| EXPECT_FALSE(loaded_search_query->selected_region_thumbnail_uri_.empty()); |
| EXPECT_TRUE(loaded_search_query->selected_region_); |
| EXPECT_EQ(loaded_search_query->lens_selection_type_, lens::REGION_SEARCH); |
| |
| // Loading another url in the side panel should update the results page. |
| const GURL third_search_url( |
| "https://www.google.com/" |
| "search?source=chrome.cr.menu&vsint=CAMqCgoCCAcSAggDIAI&q=kiwi&lns_fp=1" |
| "&lns_mode=text&lns_surface=42&gsc=2&hl=en-US&cs=0"); |
| content::TestNavigationObserver third_search_observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->IssueTextSelectionRequestForTesting("kiwi", 1, 100); |
| third_search_observer.WaitForNavigationFinished(); |
| |
| // The search query history stack should have 2 entries and the currently |
| // loaded query should be set to the new query |
| EXPECT_EQ(GetSearchQueryHistory().size(), 2UL); |
| loaded_search_query.reset(); |
| loaded_search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(loaded_search_query); |
| EXPECT_EQ(loaded_search_query->search_query_text_, "kiwi"); |
| url_without_start_time_or_size = |
| RemoveStartTimeAndSizeParams(loaded_search_query->search_query_url_); |
| EXPECT_EQ(url_without_start_time_or_size, third_search_url); |
| EXPECT_TRUE(loaded_search_query->selected_text_); |
| EXPECT_EQ(loaded_search_query->selected_text_->first, 1); |
| EXPECT_EQ(loaded_search_query->selected_text_->second, 100); |
| EXPECT_TRUE(loaded_search_query->selected_region_thumbnail_uri_.empty()); |
| EXPECT_FALSE(loaded_search_query->selected_region_); |
| EXPECT_EQ(loaded_search_query->lens_selection_type_, |
| lens::SELECT_TEXT_HIGHLIGHT); |
| url_without_start_time_or_size = |
| RemoveStartTimeAndSizeParams(third_search_observer.last_navigation_url()); |
| EXPECT_EQ(url_without_start_time_or_size, third_search_url); |
| |
| // Popping a query with a region should resend a region search request. |
| content::TestNavigationObserver first_pop_observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| fake_query_controller->ResetTestingState(); |
| controller->results_side_panel_coordinator()->PopAndLoadQueryFromHistory(); |
| |
| // Verify the new interaction request was sent. |
| EXPECT_EQ(controller->get_selected_region_for_testing(), kTestRegion); |
| EXPECT_FALSE(controller->get_selected_text_for_region()); |
| EXPECT_EQ(fake_query_controller->last_queried_region(), kTestRegion); |
| EXPECT_TRUE(fake_query_controller->last_queried_region_bytes().has_value()); |
| EXPECT_EQ(fake_query_controller->last_lens_selection_type(), |
| lens::REGION_SEARCH); |
| |
| first_pop_observer.WaitForNavigationFinished(); |
| |
| // The search query history stack should have 1 entry and the previously |
| // loaded region should be present. |
| EXPECT_EQ(GetSearchQueryHistory().size(), 1UL); |
| loaded_search_query.reset(); |
| loaded_search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(loaded_search_query); |
| EXPECT_EQ(loaded_search_query->search_query_text_, std::string()); |
| EXPECT_FALSE(loaded_search_query->selected_text_); |
| EXPECT_FALSE(loaded_search_query->selected_region_thumbnail_uri_.empty()); |
| EXPECT_EQ(loaded_search_query->selected_region_, kTestRegion); |
| EXPECT_EQ(loaded_search_query->lens_selection_type_, lens::REGION_SEARCH); |
| |
| // Popping another query should load the original query into the results |
| // frame. |
| content::TestNavigationObserver second_pop_observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->results_side_panel_coordinator()->PopAndLoadQueryFromHistory(); |
| second_pop_observer.WaitForNavigationFinished(); |
| |
| // The search query history stack should be empty and the currently loaded |
| // query should be set to the original query. |
| EXPECT_TRUE(GetSearchQueryHistory().empty()); |
| loaded_search_query.reset(); |
| loaded_search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(loaded_search_query); |
| EXPECT_EQ(loaded_search_query->search_query_text_, "oranges"); |
| url_without_start_time_or_size = |
| RemoveStartTimeAndSizeParams(loaded_search_query->search_query_url_); |
| VerifySearchQueryParameters(loaded_search_query->search_query_url_); |
| VerifyTextQueriesAreEqual(loaded_search_query->search_query_url_, |
| first_search_url); |
| EXPECT_TRUE(loaded_search_query->selected_region_thumbnail_uri_.empty()); |
| EXPECT_FALSE(loaded_search_query->selected_region_); |
| EXPECT_TRUE(loaded_search_query->selected_text_); |
| EXPECT_EQ(loaded_search_query->lens_selection_type_, |
| lens::SELECT_TEXT_HIGHLIGHT); |
| EXPECT_EQ(loaded_search_query->selected_text_->first, 20); |
| EXPECT_EQ(loaded_search_query->selected_text_->second, 200); |
| VerifySearchQueryParameters(second_pop_observer.last_navigation_url()); |
| VerifyTextQueriesAreEqual(second_pop_observer.last_navigation_url(), |
| first_search_url); |
| // Verify the text selection was sent back to mojo and any old selections |
| // were cleared. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| EXPECT_TRUE(fake_controller->fake_overlay_page_.did_clear_text_selection_); |
| EXPECT_TRUE(fake_controller->fake_overlay_page_.did_clear_region_selection_); |
| EXPECT_EQ(fake_controller->fake_overlay_page_.text_selection_indexes_, |
| loaded_search_query->selected_text_); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| PopAndLoadQueryFromHistoryWithInitialImageBytes) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlayWithPendingRegion( |
| LensOverlayInvocationSource::kContentAreaContextMenuImage, |
| kTestRegion->Clone(), CreateNonEmptyBitmap(100, 100)); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlayAndResults; })); |
| EXPECT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible()); |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| EXPECT_EQ(fake_query_controller->last_lens_selection_type(), |
| lens::INJECTED_IMAGE); |
| |
| // Loading a url in the side panel should show the results page. |
| const GURL first_search_url( |
| "https://www.google.com/" |
| "search?source=chrome.cr.ctxi&q=&lns_fp=1&lns_mode=un" |
| "&gsc=2&hl=en-US&cs=0"); |
| |
| // The search query history stack should be empty and the currently loaded |
| // query should be set. |
| EXPECT_TRUE(GetSearchQueryHistory().empty()); |
| auto loaded_search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(loaded_search_query); |
| EXPECT_TRUE(loaded_search_query->search_query_text_.empty()); |
| VerifySearchQueryParameters(loaded_search_query->search_query_url_); |
| VerifyTextQueriesAreEqual(loaded_search_query->search_query_url_, |
| first_search_url); |
| EXPECT_FALSE(loaded_search_query->selected_region_thumbnail_uri_.empty()); |
| EXPECT_EQ(loaded_search_query->selected_region_, kTestRegion); |
| EXPECT_FALSE(loaded_search_query->selected_region_bitmap_.drawsNothing()); |
| EXPECT_EQ(loaded_search_query->selected_region_bitmap_.width(), 100); |
| EXPECT_EQ(loaded_search_query->selected_region_bitmap_.height(), 100); |
| EXPECT_FALSE(loaded_search_query->selected_text_); |
| EXPECT_EQ(loaded_search_query->lens_selection_type_, lens::INJECTED_IMAGE); |
| |
| // Loading a second url in the side panel should show the results page. |
| const GURL second_search_url( |
| "https://www.google.com/" |
| "search?source=chrome.cr.ctxi&vsint=CAMqCgoCCAcSAggDIAI&q=kiwi&lns_fp=" |
| "1&lns_mode=text&lns_surface=42&gsc=2&hl=en-US&cs=0"); |
| content::TestNavigationObserver second_observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->IssueTextSelectionRequestForTesting("kiwi", 1, 100); |
| second_observer.Wait(); |
| |
| // The search query history stack should have 1 entry and the currently loaded |
| // query should be set to the new query |
| EXPECT_EQ(GetSearchQueryHistory().size(), 1UL); |
| loaded_search_query.reset(); |
| loaded_search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(loaded_search_query); |
| EXPECT_EQ(loaded_search_query->search_query_text_, "kiwi"); |
| VerifySearchQueryParameters(loaded_search_query->search_query_url_); |
| VerifyTextQueriesAreEqual(loaded_search_query->search_query_url_, |
| second_search_url); |
| EXPECT_TRUE(loaded_search_query->selected_region_thumbnail_uri_.empty()); |
| EXPECT_FALSE(loaded_search_query->selected_region_); |
| EXPECT_TRUE(loaded_search_query->selected_region_bitmap_.drawsNothing()); |
| EXPECT_TRUE(loaded_search_query->selected_text_); |
| EXPECT_EQ(loaded_search_query->lens_selection_type_, |
| lens::SELECT_TEXT_HIGHLIGHT); |
| GURL url_without_start_time_or_size = |
| RemoveStartTimeAndSizeParams(second_observer.last_navigation_url()); |
| EXPECT_EQ(url_without_start_time_or_size, second_search_url); |
| |
| // Popping a query with a region should resend a region search request. |
| fake_query_controller->ResetTestingState(); |
| |
| content::TestNavigationObserver third_observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->results_side_panel_coordinator()->PopAndLoadQueryFromHistory(); |
| third_observer.Wait(); |
| |
| // Verify the new interaction request was sent. |
| EXPECT_EQ(controller->get_selected_region_for_testing(), kTestRegion); |
| EXPECT_FALSE(controller->get_selected_text_for_region()); |
| EXPECT_EQ(fake_query_controller->last_queried_region(), kTestRegion); |
| EXPECT_TRUE(fake_query_controller->last_queried_region_bytes()); |
| EXPECT_EQ(fake_query_controller->last_queried_region_bytes()->width(), 100); |
| EXPECT_EQ(fake_query_controller->last_queried_region_bytes()->height(), 100); |
| EXPECT_EQ(fake_query_controller->last_lens_selection_type(), |
| lens::INJECTED_IMAGE); |
| |
| // The search query history stack should be empty and the currently loaded |
| // query should be set to the original query. |
| EXPECT_EQ(GetSearchQueryHistory().size(), 0UL); |
| loaded_search_query.reset(); |
| loaded_search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(loaded_search_query); |
| EXPECT_TRUE(loaded_search_query->search_query_text_.empty()); |
| VerifySearchQueryParameters(loaded_search_query->search_query_url_); |
| VerifyTextQueriesAreEqual(loaded_search_query->search_query_url_, |
| first_search_url); |
| EXPECT_FALSE(loaded_search_query->selected_region_thumbnail_uri_.empty()); |
| EXPECT_EQ(loaded_search_query->selected_region_, kTestRegion); |
| EXPECT_FALSE(loaded_search_query->selected_region_bitmap_.drawsNothing()); |
| EXPECT_EQ(loaded_search_query->selected_region_bitmap_.width(), 100); |
| EXPECT_EQ(loaded_search_query->selected_region_bitmap_.height(), 100); |
| EXPECT_FALSE(loaded_search_query->selected_text_); |
| EXPECT_EQ(loaded_search_query->lens_selection_type_, lens::INJECTED_IMAGE); |
| VerifySearchQueryParameters(third_observer.last_navigation_url()); |
| VerifyTextQueriesAreEqual(third_observer.last_navigation_url(), |
| first_search_url); |
| } |
| |
| // TODO(https://crbug.com/397600510): Disabled due to excessive flakiness. |
| IN_PROC_BROWSER_TEST_F( |
| LensOverlayControllerBrowserTest, |
| DISABLED_PopAndLoadQueryFromHistoryWithMultimodalRequest) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| SkBitmap initial_bitmap = CreateNonEmptyBitmap(100, 100); |
| OpenLensOverlayWithPendingRegion( |
| LensOverlayInvocationSource::kContentAreaContextMenuImage, |
| kTestRegion->Clone(), initial_bitmap); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlayAndResults; })); |
| EXPECT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible()); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return GetLoadedSearchQuery().has_value(); })); |
| |
| // Loading a url in the side panel should show the results page. |
| const GURL first_search_url( |
| "https://www.google.com/" |
| "search?source=chrome.cr.ctxi&q=&lns_fp=1&lns_mode=un" |
| "&gsc=2&hl=en-US&cs=0"); |
| |
| // The search query history stack should be empty and the currently loaded |
| // query should be set. |
| EXPECT_TRUE(GetSearchQueryHistory().empty()); |
| auto loaded_search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(loaded_search_query); |
| EXPECT_TRUE(loaded_search_query->search_query_text_.empty()); |
| VerifySearchQueryParameters(loaded_search_query->search_query_url_); |
| VerifyTextQueriesAreEqual(loaded_search_query->search_query_url_, |
| first_search_url); |
| EXPECT_FALSE(loaded_search_query->selected_region_thumbnail_uri_.empty()); |
| EXPECT_EQ(loaded_search_query->selected_region_, kTestRegion); |
| EXPECT_FALSE(loaded_search_query->selected_region_bitmap_.drawsNothing()); |
| EXPECT_EQ(loaded_search_query->selected_region_bitmap_.width(), 100); |
| EXPECT_EQ(loaded_search_query->selected_region_bitmap_.height(), 100); |
| EXPECT_FALSE(loaded_search_query->selected_text_); |
| EXPECT_EQ(loaded_search_query->lens_selection_type_, lens::INJECTED_IMAGE); |
| |
| // Loading a second url in the side panel should show the results page. |
| const GURL second_search_url( |
| "https://www.google.com/" |
| "search?source=chrome.gsc&ie=UTF-8&oq=green&vsint=KgwKAggHEgIIEhgAIAI&" |
| "gsc=2&hl=en-US&cs=0&q=green&lns_mode=mu&lns_fp=1&udm=24"); |
| // We can't use content::WaitForLoadStop here since the last navigation is |
| // successful. |
| content::TestNavigationObserver first_searchbox_query_observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "green", AutocompleteMatchType::Type::SEARCH_WHAT_YOU_TYPED, |
| /*is_zero_prefix_suggestion=*/false, |
| std::map<std::string, std::string>()); |
| first_searchbox_query_observer.Wait(); |
| |
| // The search query history stack should have 1 entry and the currently loaded |
| // query should be set to the new query |
| EXPECT_EQ(GetSearchQueryHistory().size(), 1UL); |
| loaded_search_query.reset(); |
| loaded_search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(loaded_search_query); |
| EXPECT_EQ(loaded_search_query->search_query_text_, "green"); |
| VerifySearchQueryParameters(loaded_search_query->search_query_url_); |
| VerifyTextQueriesAreEqual(loaded_search_query->search_query_url_, |
| second_search_url); |
| EXPECT_FALSE(loaded_search_query->selected_region_thumbnail_uri_.empty()); |
| EXPECT_TRUE(loaded_search_query->selected_region_); |
| EXPECT_FALSE(loaded_search_query->selected_text_); |
| EXPECT_EQ(loaded_search_query->lens_selection_type_, lens::MULTIMODAL_SEARCH); |
| |
| // Loading a third search url in the side panel should show the results page. |
| const GURL third_search_url( |
| "https://www.google.com/" |
| "search?source=chrome.gsc&ie=UTF-8&oq=red&vsint=KgwKAggHEgIIEhgAIAI&" |
| "gsc=2&hl=en-US&cs=0&q=red&lns_mode=mu&lns_fp=1&udm=24"); |
| // We can't use content::WaitForLoadStop here since the last navigation is |
| // successful. |
| content::TestNavigationObserver second_searchbox_query_observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "red", AutocompleteMatchType::Type::SEARCH_WHAT_YOU_TYPED, |
| /*is_zero_prefix_suggestion=*/true, std::map<std::string, std::string>()); |
| second_searchbox_query_observer.Wait(); |
| |
| // The search query history stack should have 2 entries and the currently |
| // loaded query should be set to the new query |
| EXPECT_EQ(GetSearchQueryHistory().size(), 2UL); |
| loaded_search_query.reset(); |
| loaded_search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(loaded_search_query); |
| EXPECT_EQ(loaded_search_query->search_query_text_, "red"); |
| VerifySearchQueryParameters(loaded_search_query->search_query_url_); |
| VerifyTextQueriesAreEqual(loaded_search_query->search_query_url_, |
| third_search_url); |
| EXPECT_FALSE(loaded_search_query->selected_region_thumbnail_uri_.empty()); |
| EXPECT_TRUE(loaded_search_query->selected_region_); |
| EXPECT_FALSE(loaded_search_query->selected_text_); |
| EXPECT_EQ(loaded_search_query->lens_selection_type_, |
| lens::MULTIMODAL_SUGGEST_ZERO_PREFIX); |
| |
| // Popping a query with a region should resend a multimodal request. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| fake_query_controller->ResetTestingState(); |
| |
| content::TestNavigationObserver pop_observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->results_side_panel_coordinator()->PopAndLoadQueryFromHistory(); |
| |
| // Verify the new interaction request was sent. |
| EXPECT_EQ(controller->get_selected_region_for_testing(), kTestRegion); |
| EXPECT_FALSE(controller->get_selected_text_for_region()); |
| EXPECT_EQ(fake_query_controller->last_queried_region(), kTestRegion); |
| EXPECT_TRUE(fake_query_controller->last_queried_region_bytes()); |
| UNSAFE_TODO(EXPECT_TRUE( |
| memcmp(fake_query_controller->last_queried_region_bytes()->getPixels(), |
| initial_bitmap.getPixels(), |
| initial_bitmap.computeByteSize()) == 0)); |
| EXPECT_EQ(fake_query_controller->last_queried_region_bytes()->width(), 100); |
| EXPECT_EQ(fake_query_controller->last_queried_region_bytes()->height(), 100); |
| EXPECT_EQ(fake_query_controller->last_queried_text(), "green"); |
| EXPECT_EQ(fake_query_controller->last_lens_selection_type(), |
| lens::MULTIMODAL_SEARCH); |
| |
| pop_observer.Wait(); |
| |
| // Popping the query stack again should show the initial query. |
| fake_query_controller->ResetTestingState(); |
| controller->results_side_panel_coordinator()->PopAndLoadQueryFromHistory(); |
| |
| // Verify that the last queried data did not contain any query text. |
| EXPECT_EQ(controller->get_selected_region_for_testing(), kTestRegion); |
| EXPECT_FALSE(controller->get_selected_text_for_region()); |
| EXPECT_EQ(fake_query_controller->last_queried_region(), kTestRegion); |
| EXPECT_TRUE(fake_query_controller->last_queried_region_bytes()); |
| UNSAFE_TODO(EXPECT_TRUE( |
| memcmp(fake_query_controller->last_queried_region_bytes()->getPixels(), |
| initial_bitmap.getPixels(), |
| initial_bitmap.computeByteSize()) == 0)); |
| EXPECT_EQ(fake_query_controller->last_queried_region_bytes()->width(), 100); |
| EXPECT_EQ(fake_query_controller->last_queried_region_bytes()->height(), 100); |
| EXPECT_TRUE(fake_query_controller->last_queried_text().empty()); |
| EXPECT_EQ(fake_query_controller->last_lens_selection_type(), |
| lens::INJECTED_IMAGE); |
| } |
| |
| // TODO(crbug.com/413042395): This test is not testing overlay logic, but |
| // instead the side panel logic. Therefore, this test should be moved to a side |
| // panel browsertest file. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| AddQueryToHistoryAfterResize) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should eventually result in overlay state. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| EXPECT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| EXPECT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible()); |
| |
| // Open the side panel. |
| controller->OpenSidePanelForTesting(); |
| |
| // Loading a url in the side panel should show the results page. |
| const GURL first_search_url( |
| "https://www.google.com/search?q=oranges&gsc=2&hl=en-US"); |
| controller->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting( |
| first_search_url); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // Loading a second url in the side panel should show the results page. |
| const GURL second_search_url( |
| "https://www.google.com/search?q=kiwi&gsc=2&hl=en-US"); |
| // We can't use content::WaitForLoadStop here since the last navigation is |
| // successful. |
| content::TestNavigationObserver observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting( |
| second_search_url); |
| observer.WaitForNavigationFinished(); |
| |
| // Make the side panel larger. |
| const int increment = -50; |
| BrowserView::GetBrowserViewForBrowser(browser()) |
| ->unified_side_panel() |
| ->OnResize(increment, true); |
| // Popping the query should load the previous query into the results frame. |
| content::TestNavigationObserver pop_observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->results_side_panel_coordinator()->PopAndLoadQueryFromHistory(); |
| pop_observer.WaitForNavigationFinished(); |
| // The search query history stack should be empty and the currently loaded |
| // query should be set to the previous query. |
| EXPECT_TRUE(GetSearchQueryHistory().empty()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| RecordHistogramsShowAndClose) { |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| base::HistogramTester histogram_tester; |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // No metrics should be emitted before anything happens. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.Invoked", |
| /*expected_count=*/0); |
| histogram_tester.ExpectTotalCount("Lens.Overlay.Dismissed", |
| /*expected_count=*/0); |
| histogram_tester.ExpectTotalCount("Lens.Overlay.InvocationResultedInSearch", |
| /*expected_count=*/0); |
| histogram_tester.ExpectTotalCount("Lens.Overlay.SessionDuration", |
| /*expected_count=*/0); |
| auto entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_SessionEnd::kEntryName); |
| EXPECT_EQ(0u, entries.size()); |
| |
| // Showing the UI and then closing it should record an entry in the |
| // appropriate buckets and the total count of invocations, dismissals, |
| // "resulted in search" and session duration should each be 1. In particular, |
| // the "resulted in search" metric should have an entry in the false bucket. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| histogram_tester.ExpectBucketCount("Lens.Overlay.Invoked", |
| LensOverlayInvocationSource::kAppMenu, |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount("Lens.Overlay.Invoked", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.Dismissed", LensOverlayDismissalSource::kOverlayCloseButton, |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount("Lens.Overlay.Dismissed", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount("Lens.Overlay.InvocationResultedInSearch", |
| false, /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount("Lens.Overlay.InvocationResultedInSearch", |
| true, /*expected_count=*/0); |
| histogram_tester.ExpectTotalCount("Lens.Overlay.InvocationResultedInSearch", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ByInvocationSource.AppMenu.InvocationResultedInSearch", |
| false, /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ByInvocationSource.AppMenu.InvocationResultedInSearch", |
| true, /*expected_count=*/0); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ByInvocationSource.AppMenu.InvocationResultedInSearch", |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount("Lens.Overlay.SessionDuration", |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ByInvocationSource.AppMenu.SessionDuration", |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount("Lens.Overlay.ByDocumentType.Html.Invoked", |
| /*expected_count=*/1); |
| entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_SessionEnd::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| test_ukm_recorder.ExpectEntryMetric( |
| entries[0], ukm::builders::Lens_Overlay_SessionEnd::kInvocationSourceName, |
| static_cast<int64_t>(LensOverlayInvocationSource::kAppMenu)); |
| test_ukm_recorder.ExpectEntryMetric( |
| entries[0], |
| ukm::builders::Lens_Overlay_SessionEnd::kInvocationResultedInSearchName, |
| false); |
| test_ukm_recorder.ExpectEntryMetric( |
| entries[0], |
| ukm::builders::Lens_Overlay_SessionEnd::kInvocationDocumentTypeName, |
| static_cast<int64_t>(lens::MimeType::kHtml)); |
| const char kSessionDuration[] = "SessionDuration"; |
| EXPECT_TRUE( |
| ukm::TestUkmRecorder::EntryHasMetric(entries[0].get(), kSessionDuration)); |
| } |
| |
| // TODO(346840584): Disabled due to flakiness on Mac. |
| #if BUILDFLAG(IS_MAC) |
| #define MAYBE_RecordHistogramsShowSearchAndClose \ |
| DISABLED_RecordHistogramsShowSearchAndClose |
| #else |
| #define MAYBE_RecordHistogramsShowSearchAndClose \ |
| RecordHistogramsShowSearchAndClose |
| #endif |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| MAYBE_RecordHistogramsShowSearchAndClose) { |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| base::HistogramTester histogram_tester; |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // No metrics should be emitted before anything happens. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.Invoked", |
| /*expected_count=*/0); |
| histogram_tester.ExpectTotalCount("Lens.Overlay.Dismissed", |
| /*expected_count=*/0); |
| histogram_tester.ExpectTotalCount("Lens.Overlay.InvocationResultedInSearch", |
| /*expected_count=*/0); |
| histogram_tester.ExpectTotalCount("Lens.Overlay.SessionDuration", |
| /*expected_count=*/0); |
| auto entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_SessionEnd::kEntryName); |
| EXPECT_EQ(0u, entries.size()); |
| |
| // Showing the UI, issuing a search and then closing it should record |
| // an entry in the true bucket of the "resulted in search" metric. |
| OpenLensOverlay(LensOverlayInvocationSource::kToolbar); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Issue a search. |
| controller->IssueTextSelectionRequestForTesting("oranges", 20, 200); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // Close the overlay and verify that a successful session was recorded. |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| histogram_tester.ExpectBucketCount("Lens.Overlay.Invoked", |
| LensOverlayInvocationSource::kToolbar, |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount("Lens.Overlay.Invoked", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount("Lens.Overlay.InvocationResultedInSearch", |
| false, /*expected_count=*/0); |
| histogram_tester.ExpectBucketCount("Lens.Overlay.InvocationResultedInSearch", |
| true, /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ByInvocationSource.Toolbar.InvocationResultedInSearch", |
| false, /*expected_count=*/0); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ByInvocationSource.Toolbar.InvocationResultedInSearch", |
| true, /*expected_count=*/1); |
| entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_SessionEnd::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| test_ukm_recorder.ExpectEntryMetric( |
| entries[0], ukm::builders::Lens_Overlay_SessionEnd::kInvocationSourceName, |
| static_cast<int64_t>(LensOverlayInvocationSource::kToolbar)); |
| test_ukm_recorder.ExpectEntryMetric( |
| entries[0], |
| ukm::builders::Lens_Overlay_SessionEnd::kInvocationResultedInSearchName, |
| true); |
| const char kSessionDuration[] = "SessionDuration"; |
| EXPECT_TRUE( |
| ukm::TestUkmRecorder::EntryHasMetric(entries[0].get(), kSessionDuration)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| RecordHistogramsDoubleOpenClose) { |
| base::HistogramTester histogram_tester; |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Attempting to invoke the overlay twice without closing it in between |
| // should record only a single new entry. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Attempting to close the overlay twice without opening it in between should |
| // only record a single entry. |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| histogram_tester.ExpectBucketCount("Lens.Overlay.Invoked", |
| LensOverlayInvocationSource::kAppMenu, |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount("Lens.Overlay.Invoked", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.Dismissed", LensOverlayDismissalSource::kOverlayCloseButton, |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount("Lens.Overlay.Dismissed", |
| /*expected_count=*/1); |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.Dismissed", LensOverlayDismissalSource::kOverlayCloseButton, |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount("Lens.Overlay.Dismissed", |
| /*expected_count=*/1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| RecordUkmAndTaskCompletionForLensOverlayInteraction) { |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // No metrics should be emitted before anything happens. |
| auto entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_Overlay_UserAction::kEntryName); |
| EXPECT_EQ(0u, entries.size()); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // We need to flush the mojo receiver calls to make sure the screenshot was |
| // passed back to the WebUI or else the region selection UI will not render. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| fake_controller->FlushForTesting(); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Test that the RecordUkmAndTaskCompletionForLensOverlayInteraction function |
| // which is called from the WebUI side, records the entry successfully. |
| controller->RecordUkmAndTaskCompletionForLensOverlayInteractionForTesting( |
| lens::mojom::UserAction::kRegionSelection); |
| entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_Overlay_UserAction::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| auto* entry = entries[0].get(); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, ukm::builders::Lens_Overlay_Overlay_UserAction::kUserActionName, |
| static_cast<int64_t>(lens::mojom::UserAction::kRegionSelection)); |
| |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| EXPECT_THAT(fake_query_controller->last_user_action(), |
| testing::Optional(lens::mojom::UserAction::kRegionSelection)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| OverlayClosesIfTabUrlPathChanges) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the Overlay |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Navigate the main tab URL to a different path. |
| WaitForPaint(kDocumentWithImage); |
| |
| // Overlay should close |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOff; })); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| OverlayClosesIfTabUrlFragmentChanges) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the Overlay |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Navigate the main tab URL to a different path. |
| WaitForPaint(kDocumentWithNamedElementWithFragment); |
| |
| // Overlay should close |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOff; })); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| OverlayClosesOnReload) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the Overlay |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Simulate user pressing the reload button. |
| chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB); |
| |
| // Overlay should close |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOff; })); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| OverlayStaysOpenWithHistoryState) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the Overlay |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| content::WebContents* contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(contents); |
| |
| // Call replaceState, pushState, and back on the underlying page. |
| EXPECT_TRUE(content::ExecJs( |
| contents->GetPrimaryMainFrame(), kHistoryStateScript, |
| content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES)); |
| |
| // Overlay should have stayed open. |
| ASSERT_TRUE(controller->state() == State::kOverlay); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| OverlayClosesSidePanelBeforeOpening) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the side panel |
| auto* side_panel_coordinator = |
| browser()->GetFeatures().side_panel_coordinator(); |
| side_panel_coordinator->Show(SidePanelEntry::Id::kBookmarks); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return side_panel_coordinator->IsSidePanelShowing(); })); |
| |
| // Showing UI should eventually result in overlay state. When the overlay is |
| // bound, it should start the query flow which returns a response for the |
| // interaction data callback. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Side panel should now be closed. |
| EXPECT_FALSE(side_panel_coordinator->IsSidePanelShowing()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| OverlayClosesIfSidePanelIsOpened) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should eventually result in overlay state. When the overlay is |
| // bound, it should start the query flow which returns a response for the |
| // interaction data callback. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Grab fake controller to test if notify the overlay of being closed. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| EXPECT_FALSE(fake_controller->fake_overlay_page_.did_notify_overlay_closing_); |
| |
| // Open the side panel |
| auto* side_panel_coordinator = |
| browser()->GetFeatures().side_panel_coordinator(); |
| side_panel_coordinator->Show(SidePanelEntry::Id::kBookmarks); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return side_panel_coordinator->IsSidePanelShowing(); })); |
| |
| // Overlay should close. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOff; })); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| OverlayClosesIfNewSidePanelEntryAppears) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should eventually result in overlay state. When the overlay is |
| // bound, it should start the query flow which returns a response for the |
| // interaction data callback. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Grab fake controller to test if notify the overlay of being closed. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| EXPECT_FALSE(fake_controller->fake_overlay_page_.did_notify_overlay_closing_); |
| |
| // Open our results panel |
| controller->IssueTextSelectionRequestForTesting("test query", |
| /*selection_start_index=*/0, |
| /*selection_end_index=*/0); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlayAndResults; })); |
| |
| // Open a different side panel |
| auto* side_panel_coordinator = |
| browser()->GetFeatures().side_panel_coordinator(); |
| |
| // Verify the side panel is open |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return side_panel_coordinator->IsSidePanelShowing(); })); |
| |
| side_panel_coordinator->Show(SidePanelEntry::Id::kBookmarks); |
| |
| // Overlay should close. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOff; })); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, SidePanelOpen) { |
| WaitForPaint(); |
| |
| // Wait for side panel to fully open. |
| auto* side_panel_coordinator = |
| browser()->GetFeatures().side_panel_coordinator(); |
| side_panel_coordinator->Show(SidePanelEntry::Id::kBookmarks); |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return browser()->GetBrowserView().unified_side_panel()->state() == |
| SidePanel::State::kOpen; |
| })); |
| |
| auto* controller = GetLensOverlayController(); |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| |
| // Overlay should eventually show. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // And the side-panel should be hidden. |
| EXPECT_EQ(browser()->GetBrowserView().unified_side_panel()->state(), |
| SidePanel::State::kClosed); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, FindBarClosesOverlay) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Open the find bar. |
| browser()->GetFeatures().GetFindBarController()->Show(); |
| |
| // Verify the overlay turns off. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOff; })); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, EnterprisePolicy) { |
| // The default policy is to allow the feature to be enabled. |
| EXPECT_TRUE(browser() |
| ->GetFeatures() |
| .lens_overlay_entry_point_controller() |
| ->IsEnabled()); |
| |
| // If GenAiDefaultSettings is set, the feature enablement should |
| // fallback to GenAiDefaultSettings setting. |
| policy::PolicyMap policies; |
| policies.Set("GenAiDefaultSettings", policy::POLICY_LEVEL_MANDATORY, |
| policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD, |
| base::Value(2), nullptr); |
| policy_provider()->UpdateChromePolicy(policies); |
| EXPECT_FALSE(browser() |
| ->GetFeatures() |
| .lens_overlay_entry_point_controller() |
| ->IsEnabled()); |
| |
| policies.Set("LensOverlaySettings", policy::POLICY_LEVEL_MANDATORY, |
| policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD, |
| base::Value(1), nullptr); |
| policy_provider()->UpdateChromePolicy(policies); |
| EXPECT_FALSE(browser() |
| ->GetFeatures() |
| .lens_overlay_entry_point_controller() |
| ->IsEnabled()); |
| |
| policies.Set("LensOverlaySettings", policy::POLICY_LEVEL_MANDATORY, |
| policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD, |
| base::Value(0), nullptr); |
| policy_provider()->UpdateChromePolicy(policies); |
| EXPECT_TRUE(browser() |
| ->GetFeatures() |
| .lens_overlay_entry_point_controller() |
| ->IsEnabled()); |
| } |
| |
| class LensOverlayControllerEntrypointsBrowserTest |
| : public LensOverlayControllerBrowserTest, |
| public ::testing::WithParamInterface<bool> { |
| public: |
| LensOverlayControllerEntrypointsBrowserTest() = default; |
| ~LensOverlayControllerEntrypointsBrowserTest() override = default; |
| |
| void SetupFeatureList() override { |
| std::vector<base::test::FeatureRefAndParams> enabled_features = { |
| {lens::features::kLensOverlay, {}}, |
| {lens::features::kLensOverlayContextualSearchbox, {}}, |
| {lens::features::kLensOverlayOmniboxEntryPoint, {}}, |
| {lens::features::kLensOverlaySurvey, {}}, |
| {lens::features::kLensOverlaySidePanelOpenInNewTab, {}}}; |
| if (IsPageActionsMigrationEnabled()) { |
| enabled_features.push_back( |
| {::features::kPageActionsMigration, |
| { |
| {::features::kPageActionsMigrationLensOverlay.name, "true"}, |
| }}); |
| } |
| // TODO(crbug.com/441102004): Update OverlayHidesEntrypoints to support |
| // kAiModeOmniboxEntryPoint. |
| feature_list_.InitWithFeaturesAndParameters( |
| enabled_features, |
| /*disabled_features=*/{lens::features::kLensOverlaySimplifiedSelection, |
| omnibox::kAiModeOmniboxEntryPoint}); |
| } |
| |
| void VerifyEntrypoints(bool expected_visible) { |
| // Verify context menu entrypoint matches expected visibility. |
| EXPECT_EQ(expected_visible, GetContextMenu()->IsItemPresent( |
| IDC_CONTENT_CONTEXT_LENS_REGION_SEARCH)); |
| |
| // Verify omnibox (location bar) icon matches expected visibility. |
| auto* location_bar = |
| BrowserView::GetBrowserViewForBrowser(browser())->GetLocationBarView(); |
| location_bar->omnibox_view()->RequestFocus(); |
| views::View* omnibox_entrypoint; |
| if (IsPageActionMigrated(PageActionIconType::kLensOverlay)) { |
| omnibox_entrypoint = |
| location_bar->page_action_container()->GetPageActionView( |
| kActionSidePanelShowLensOverlayResults); |
| } else { |
| location_bar->page_action_icon_controller()->UpdateAll(); |
| omnibox_entrypoint = |
| location_bar->page_action_icon_controller()->GetIconView( |
| PageActionIconType::kLensOverlay); |
| } |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return omnibox_entrypoint->GetVisible() == expected_visible; |
| })); |
| |
| // Verify three dot menu entrypoint matches expected visibility. |
| EXPECT_EQ(expected_visible, |
| browser()->command_controller()->IsCommandEnabled( |
| IDC_CONTENT_CONTEXT_LENS_OVERLAY)); |
| |
| // Verify toolbar entrypoint is always enabled and visible. |
| actions::ActionItem* toolbar_entry_point = |
| actions::ActionManager::Get().FindAction( |
| kActionSidePanelShowLensOverlayResults, |
| browser()->browser_actions()->root_action_item()); |
| EXPECT_TRUE(toolbar_entry_point->GetVisible()); |
| EXPECT_TRUE(toolbar_entry_point->GetEnabled()); |
| } |
| |
| private: |
| bool IsPageActionsMigrationEnabled() const { return GetParam(); } |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| LensOverlayControllerEntrypointsBrowserTest, |
| ::testing::Values(false, true), |
| [](const ::testing::TestParamInfo<bool>& info) { |
| return info.param ? "PageActionsMigrationEnabled" |
| : "PageActionsMigrationDisabled"; |
| }); |
| |
| IN_PROC_BROWSER_TEST_P(LensOverlayControllerEntrypointsBrowserTest, |
| OverlayHidesEntrypoints) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Verify the entrypoints are enabled. |
| VerifyEntrypoints(/*expected_visible=*/true); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify the entrypoints are hidden. |
| VerifyEntrypoints(/*expected_visible=*/false); |
| |
| // Grab the index of the currently active tab so we can return to it later. |
| int active_controller_tab_index = |
| browser()->tab_strip_model()->active_index(); |
| |
| // Switch to a new tab. |
| WaitForPaint(kDocumentWithNamedElement, |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB | |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kBackground; })); |
| |
| // Verify the entrypoints are visible again. |
| VerifyEntrypoints(/*expected_visible=*/true); |
| |
| // Switch back to the original tab. |
| browser()->tab_strip_model()->ActivateTabAt(active_controller_tab_index); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify the entrypoints are hidden again. |
| VerifyEntrypoints(/*expected_visible=*/false); |
| |
| // Close the overlay. |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| |
| // Verify the entrypoints are visible again. |
| VerifyEntrypoints(/*expected_visible=*/true); |
| } |
| |
| // TODO(crbug.com/350292135): Flaky on all platforms. Re-enable once flakiness |
| // is fixed. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| DISABLED_OverlayCopyShortcut) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the Overlay and wait for the WebUI to be ready. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Before showing the results panel, there should be no OnCopyCommand sent to |
| // the overlay. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| EXPECT_FALSE(fake_controller->fake_overlay_page_.did_trigger_copy); |
| |
| // Send CTRL+C to overlay |
| SimulateCtrlCKeyPress(GetOverlayWebContents()); |
| fake_controller->FlushForTesting(); |
| |
| // Verify that OnCopyCommand was sent. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return fake_controller->fake_overlay_page_.did_trigger_copy; })); |
| |
| // Reset did_trigger_copy. |
| fake_controller->fake_overlay_page_.did_trigger_copy = false; |
| |
| // Open side panel. |
| controller->IssueTextSelectionRequestForTesting("test query", |
| /*selection_start_index=*/0, |
| /*selection_end_index=*/0); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlayAndResults; })); |
| |
| // Send CTRL+C to side panel |
| SimulateCtrlCKeyPress(controller->GetSidePanelWebContentsForTesting()); |
| fake_controller->FlushForTesting(); |
| |
| // Verify that OnCopyCommand was sent. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return fake_controller->fake_overlay_page_.did_trigger_copy; })); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| OverlayClosesIfRendererExits) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the Overlay |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Force the renderer to crash. |
| content::RenderProcessHost* process = |
| controller->GetOverlayWebViewForTesting() |
| ->GetWebContents() |
| ->GetPrimaryMainFrame() |
| ->GetProcess(); |
| content::ScopedAllowRendererCrashes allow_renderer_crashes(process); |
| process->ForceCrash(); |
| |
| // Overlay should close |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOff; })); |
| // Tab contents web view should be enabled. |
| ASSERT_TRUE(browser()->GetWebView()->GetEnabled()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| OverlayClosesIfRendererNavigates) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the Overlay |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Force the renderer to navigate cross-origin to change the renderer process. |
| // This was previously known to cause a crash (crbug.com/371643466). |
| controller->GetOverlayWebViewForTesting() |
| ->GetWebContents() |
| ->GetController() |
| .LoadURL(GURL(kHelloWorldDataUri), content::Referrer(), |
| ui::PAGE_TRANSITION_LINK, std::string()); |
| |
| // Overlay should close |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOff; })); |
| } |
| |
| // TODO(crbug.com/422501416): Re-enable this test on Windows. |
| #if BUILDFLAG(IS_WIN) |
| #define MAYBE_OverlayInBackgroundClosesIfRendererExits \ |
| DISABLED_OverlayInBackgroundClosesIfRendererExits |
| #else |
| #define MAYBE_OverlayInBackgroundClosesIfRendererExits \ |
| OverlayInBackgroundClosesIfRendererExits |
| #endif |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| MAYBE_OverlayInBackgroundClosesIfRendererExits) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Get the underlying tab before we open a new tab. |
| content::WebContents* underlying_tab_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| // Open the Overlay |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Opening a new tab to background the overlay UI. |
| WaitForPaint(kDocumentWithNamedElement, |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB | |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| EXPECT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kBackground; })); |
| |
| // Force the old tab renderer to crash. |
| content::RenderProcessHost* process = |
| underlying_tab_contents->GetPrimaryMainFrame()->GetProcess(); |
| content::ScopedAllowRendererCrashes allow_renderer_crashes(process); |
| process->ForceCrash(); |
| |
| // Overlay should close |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOff; })); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| OverlayInBackgroundClosesIfPageNavigates) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Get the underlying tab before we open a new tab. |
| content::WebContents* underlying_tab_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| // Open the Overlay |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Opening a new tab to background the overlay UI. |
| WaitForPaint(kDocumentWithNamedElement, |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB | |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| EXPECT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kBackground; })); |
| |
| // Navigate the old tab to a new URL. |
| const GURL new_url = embedded_test_server()->GetURL(kDocumentWithImage); |
| ASSERT_TRUE(content::NavigateToURL(underlying_tab_contents, new_url)); |
| |
| // Overlay should close |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOff; })); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| CorrectAnalyticsAndRequestIdSentWithGen204s) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| auto* fake_search_controller = |
| static_cast<LensSearchControllerFake*>(GetLensSearchController()); |
| ASSERT_TRUE(fake_search_controller); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| // When the overlay is bound, it should start the query flow which returns a |
| // response for the full image callback. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| |
| EXPECT_EQ(fake_query_controller->latency_gen_204_counter( |
| lens::LensOverlayGen204Controller::LatencyType:: |
| kFullPageObjectsRequestFetchLatency), |
| 1); |
| |
| // Objects request latency should not have an analytics id associated with it. |
| EXPECT_FALSE( |
| fake_query_controller->last_latency_gen204_analytics_id().has_value()); |
| |
| // Objects request latency gen204 should have the same vsrid as the actual |
| // request. |
| EXPECT_THAT( |
| fake_query_controller->sent_full_image_request_id(), |
| EqualsProto( |
| fake_query_controller->last_latency_gen204_request_id().value())); |
| |
| std::string encoded_sent_objects_analytics_id = base32::Base32Encode( |
| base::as_byte_span( |
| fake_query_controller->sent_full_image_request_id().analytics_id()), |
| base32::Base32EncodePolicy::OMIT_PADDING); |
| |
| // Log a copy text user task completion event. |
| controller->RecordUkmAndTaskCompletionForLensOverlayInteractionForTesting( |
| lens::mojom::UserAction::kCopyText); |
| |
| // The objects request and the task completion gen204 should have the same |
| // analytics id and request id. |
| EXPECT_EQ(fake_query_controller->last_user_action(), |
| lens::mojom::UserAction::kCopyText); |
| EXPECT_TRUE(fake_query_controller->last_task_completion_gen204_analytics_id() |
| .has_value()); |
| EXPECT_EQ( |
| fake_query_controller->last_task_completion_gen204_analytics_id().value(), |
| encoded_sent_objects_analytics_id); |
| EXPECT_THAT( |
| fake_query_controller->last_task_completion_gen204_request_id().value(), |
| EqualsProto(fake_query_controller->sent_full_image_request_id())); |
| |
| // Issue a text selection request and record the task completion. |
| controller->IssueTextSelectionRequestForTesting("oranges", 20, 200); |
| controller->RecordUkmAndTaskCompletionForLensOverlayInteractionForTesting( |
| lens::mojom::UserAction::kTextSelection); |
| |
| // The text selection request should trigger the side panel to load new |
| // search query. |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // The objects request and the task completion gen204 should have the same |
| // analytics id and request id. |
| EXPECT_EQ(fake_query_controller->last_user_action(), |
| lens::mojom::UserAction::kTextSelection); |
| EXPECT_TRUE(fake_query_controller->last_task_completion_gen204_analytics_id() |
| .has_value()); |
| EXPECT_EQ( |
| fake_query_controller->last_task_completion_gen204_analytics_id().value(), |
| encoded_sent_objects_analytics_id); |
| |
| // Issue a region search request, which should trigger an interaction request. |
| // Use a navigation observer to wait for the side panel to load, since |
| // WaitForLoadStop only works once. |
| content::TestNavigationObserver region_search_observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->IssueLensRegionRequestForTesting(kTestRegion->Clone(), |
| /*is_click=*/false); |
| region_search_observer.Wait(); |
| |
| // The interaction request should have a different analytics id than the |
| // objects request. |
| std::string encoded_sent_interaction_analytics_id = base32::Base32Encode( |
| base::as_byte_span( |
| fake_query_controller->sent_interaction_request_id().analytics_id()), |
| base32::Base32EncodePolicy::OMIT_PADDING); |
| EXPECT_NE(encoded_sent_interaction_analytics_id, |
| encoded_sent_objects_analytics_id); |
| |
| // Issue a delayed text gleams view start event. Normally, this would be sent |
| // as soon as the full image response is received, but it is possible that an |
| // interaction request and search page request is sent before the full image |
| // response is received, such as for the INJECTED_IMAGE case. |
| controller->RecordSemanticEventForTesting( |
| lens::mojom::SemanticEvent::kTextGleamsViewStart); |
| |
| // There should be a semantic action for text view start, with a different |
| // request id than the objects or interaction request, as it instead |
| // corresponds to the search request. |
| EXPECT_EQ(fake_query_controller->last_semantic_event().value(), |
| lens::mojom::SemanticEvent::kTextGleamsViewStart); |
| EXPECT_THAT( |
| fake_query_controller->last_semantic_event_gen204_request_id().value(), |
| testing::Not( |
| EqualsProto(fake_query_controller->sent_full_image_request_id()))); |
| EXPECT_THAT( |
| fake_query_controller->last_semantic_event_gen204_request_id().value(), |
| testing::Not( |
| EqualsProto(fake_query_controller->sent_interaction_request_id()))); |
| |
| std::string search_url_vsrid; |
| EXPECT_TRUE(net::GetValueForKeyInQuery( |
| GURL(fake_search_controller->GetLastSearchUrl()), |
| kLensRequestQueryParameter, &search_url_vsrid)); |
| EXPECT_EQ(EncodeRequestId( |
| fake_query_controller->last_semantic_event_gen204_request_id() |
| .value()), |
| search_url_vsrid); |
| |
| // The interaction request and latency gen204 should have the same analytics |
| // and request id. |
| EXPECT_EQ(fake_query_controller->latency_gen_204_counter( |
| lens::LensOverlayGen204Controller::LatencyType:: |
| kInteractionRequestFetchLatency), |
| 1); |
| EXPECT_EQ(encoded_sent_interaction_analytics_id, |
| fake_query_controller->last_latency_gen204_analytics_id().value()); |
| EXPECT_THAT( |
| fake_query_controller->sent_interaction_request_id(), |
| EqualsProto( |
| fake_query_controller->last_latency_gen204_request_id().value())); |
| |
| // Log a copy text user task completion event. |
| controller->RecordUkmAndTaskCompletionForLensOverlayInteractionForTesting( |
| lens::mojom::UserAction::kCopyText); |
| |
| // The encoded vsrid in the task completion gen204 should match that of |
| // the search request. |
| EXPECT_EQ(EncodeRequestId( |
| fake_query_controller->last_semantic_event_gen204_request_id() |
| .value()), |
| search_url_vsrid); |
| |
| // Issue a text selection request and record the task completion. |
| content::TestNavigationObserver text_selection_observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->IssueTextSelectionRequestForTesting("oranges", 20, 200); |
| controller->RecordUkmAndTaskCompletionForLensOverlayInteractionForTesting( |
| lens::mojom::UserAction::kTextSelection); |
| |
| // The text selection request should trigger the side panel to load new |
| // search query. |
| text_selection_observer.Wait(); |
| |
| // The encoded vsrid in the task completion gen204 should match that of |
| // the search request. |
| EXPECT_EQ(fake_query_controller->last_user_action(), |
| lens::mojom::UserAction::kTextSelection); |
| EXPECT_TRUE(fake_query_controller->last_task_completion_gen204_analytics_id() |
| .has_value()); |
| EXPECT_EQ( |
| fake_query_controller->last_task_completion_gen204_analytics_id().value(), |
| encoded_sent_interaction_analytics_id); |
| EXPECT_THAT( |
| fake_query_controller->sent_interaction_request_id(), |
| testing::Not(EqualsProto( |
| fake_query_controller->last_task_completion_gen204_request_id() |
| .value()))); |
| EXPECT_EQ(EncodeRequestId( |
| fake_query_controller->last_semantic_event_gen204_request_id() |
| .value()), |
| search_url_vsrid); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| OnScrollToMessage_NonPDF) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Set up the test extension event observer to monitor is the extension event |
| // is sent correctly. |
| extensions::TestEventRouterObserver observer( |
| extensions::EventRouter::Get(browser()->profile())); |
| |
| // Call OnScrollToMessage. |
| std::vector<std::string> text_fragments = {"text1", "text2"}; |
| uint32_t page_number = 3; |
| int tabs = browser()->tab_strip_model()->count(); |
| controller->results_side_panel_coordinator()->SetLatestPageUrlWithResponse( |
| GURL("file:///test.pdf")); |
| controller->results_side_panel_coordinator()->OnScrollToMessage( |
| text_fragments, page_number); |
| |
| // Expect a new tab to be opened. |
| EXPECT_EQ(tabs + 1, browser()->tab_strip_model()->count()); |
| EXPECT_EQ(0u, observer.dispatched_events().size()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| FeedbackRequestedOpensFeedbackUI) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Open the side panel. |
| controller->OpenSidePanelForTesting(); |
| ASSERT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // Get the coordinator. |
| auto* coordinator = controller->results_side_panel_coordinator(); |
| ASSERT_TRUE(coordinator); |
| |
| base::HistogramTester histogram_tester; |
| |
| ASSERT_FALSE(FeedbackDialog::GetInstanceForTest()); |
| coordinator->RequestSendFeedback(); |
| |
| // ChromeOS opens its own feedback dialog. |
| #if !BUILDFLAG(IS_CHROMEOS) |
| // Wait for the feedback dialog to appear instead of a new tab. |
| ASSERT_TRUE(base::test::RunUntil( |
| []() { return FeedbackDialog::GetInstanceForTest() != nullptr; })); |
| #endif // !BUILDFLAG(IS_CHROMEOS) |
| |
| histogram_tester.ExpectTotalCount("Feedback.RequestSource", 1); |
| } |
| |
| class LensOverlayControllerBrowserStartQueryFlowOptimization |
| : public LensOverlayControllerBrowserTest { |
| protected: |
| void SetupFeatureList() override { |
| feature_list_.InitWithFeaturesAndParameters( |
| {{lens::features::kLensOverlay, |
| {{"results-search-url", kResultsSearchBaseUrl}, |
| {"use-dynamic-theme", "true"}, |
| {"use-dynamic-theme-min-population-pct", "0.002"}, |
| {"use-dynamic-theme-min-chroma", "3.0"}}}, |
| {lens::features::kLensOverlayContextualSearchbox, |
| { |
| {"use-inner-text-as-context", "true"}, |
| {"use-webpage-vit-param", "true"}, |
| }}, |
| {lens::features::kLensOverlaySurvey, {}}, |
| {lens::features::kLensOverlayLatencyOptimizations, |
| {{"enable-early-start-query-flow-optimization", "true"}}}}, |
| /*disabled_features=*/{}); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserStartQueryFlowOptimization, |
| CsbPageContentsAreStillUploaded) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should eventually result in overlay state. This also tests that |
| // the start query flow optimization doesn't break the overlay initialization |
| // flow. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| |
| // With the start query flow optimization, the page contents will not be |
| // uploaded with the full image request. This checks that that the page |
| // contents are still uploaded at some point after the overlay is |
| // initialized. |
| EXPECT_FALSE( |
| fake_query_controller->sent_full_image_objects_request().has_payload()); |
| EXPECT_EQ(fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data()[0] |
| .content_type(), |
| lens::ContentData::CONTENT_TYPE_INNER_TEXT); |
| EXPECT_EQ(fake_query_controller->sent_full_image_request_id().sequence_id(), |
| 1); |
| EXPECT_EQ(fake_query_controller->sent_page_content_request_id().sequence_id(), |
| 1); |
| EXPECT_FALSE( |
| controller->GetLensSuggestInputsForTesting().has_encoded_image_signals()); |
| EXPECT_FALSE(controller->GetLensSuggestInputsForTesting() |
| .has_encoded_visual_search_interaction_log_data()); |
| EXPECT_EQ(controller->GetLensSuggestInputsForTesting() |
| .contextual_visual_input_type(), |
| "wp"); |
| EXPECT_TRUE( |
| controller->GetLensSuggestInputsForTesting().has_encoded_request_id()); |
| EXPECT_TRUE( |
| controller->GetLensSuggestInputsForTesting().has_search_session_id()); |
| } |
| |
| class LensOverlayControllerBrowserFullscreenDisabled |
| : public LensOverlayControllerBrowserTest { |
| protected: |
| void SetupFeatureList() override { |
| feature_list_.InitAndEnableFeatureWithParameters( |
| lens::features::kLensOverlay, { |
| {"enable-in-fullscreen", "false"}, |
| }); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserFullscreenDisabled, |
| FullscreenClosesOverlay) { |
| WaitForPaint(); |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Enter into fullscreen mode. |
| FullscreenController* fullscreen_controller = browser() |
| ->GetFeatures() |
| .exclusive_access_manager() |
| ->fullscreen_controller(); |
| content::WebContents* tab_web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| fullscreen_controller->EnterFullscreenModeForTab( |
| tab_web_contents->GetPrimaryMainFrame()); |
| |
| // Verify the overlay turns off. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOff; })); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserFullscreenDisabled, |
| ToolbarEntryPointState) { |
| WaitForPaint(); |
| |
| // Assert entry points are enabled. |
| actions::ActionItem* toolbar_entry_point = |
| actions::ActionManager::Get().FindAction( |
| kActionSidePanelShowLensOverlayResults, |
| browser()->browser_actions()->root_action_item()); |
| EXPECT_TRUE(toolbar_entry_point->GetEnabled()); |
| EXPECT_TRUE(browser()->command_controller()->IsCommandEnabled( |
| IDC_CONTENT_CONTEXT_LENS_OVERLAY)); |
| |
| // Enter into fullscreen mode. |
| FullscreenController* fullscreen_controller = browser() |
| ->GetFeatures() |
| .exclusive_access_manager() |
| ->fullscreen_controller(); |
| content::WebContents* tab_web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| fullscreen_controller->EnterFullscreenModeForTab( |
| tab_web_contents->GetPrimaryMainFrame()); |
| |
| // Assert entry points become disabled. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return !toolbar_entry_point->GetEnabled(); })); |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return !browser()->command_controller()->IsCommandEnabled( |
| IDC_CONTENT_CONTEXT_LENS_OVERLAY); |
| })); |
| |
| // Exit fullscreen. |
| fullscreen_controller->ExitFullscreenModeForTab(tab_web_contents); |
| |
| // Verify the entry points become re-enabled. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return toolbar_entry_point->GetEnabled(); })); |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return browser()->command_controller()->IsCommandEnabled( |
| IDC_CONTENT_CONTEXT_LENS_OVERLAY); |
| })); |
| } |
| |
| class LensOverlayControllerBrowserPDFTest |
| : public base::test::WithFeatureOverride, |
| public PDFExtensionTestBase { |
| public: |
| LensOverlayControllerBrowserPDFTest() |
| : base::test::WithFeatureOverride(chrome_pdf::features::kPdfOopif) { |
| lens_search_controller_override_ = UseFakeLensSearchController(); |
| } |
| |
| void SetUpOnMainThread() override { |
| PDFExtensionTestBase::SetUpOnMainThread(); |
| |
| // Permits sharing the page screenshot by default. This disables the |
| // permission dialog. |
| PrefService* prefs = browser()->profile()->GetPrefs(); |
| prefs->SetBoolean(lens::prefs::kLensSharingPageScreenshotEnabled, true); |
| prefs->SetBoolean(lens::prefs::kLensSharingPageContentEnabled, true); |
| } |
| |
| bool UseOopif() const override { return GetParam(); } |
| |
| std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() |
| const override { |
| auto enabled = PDFExtensionTestBase::GetEnabledFeatures(); |
| enabled.push_back({lens::features::kLensOverlay, {}}); |
| return enabled; |
| } |
| |
| std::vector<base::test::FeatureRef> GetDisabledFeatures() const override { |
| auto disabled = PDFExtensionTestBase::GetDisabledFeatures(); |
| disabled.emplace_back(lens::features::kLensOverlayContextualSearchbox); |
| disabled.emplace_back(lens::features::kLensOverlayKeyboardSelection); |
| return disabled; |
| } |
| |
| LensSearchController* GetLensSearchController() { |
| return LensSearchController::From(browser()->GetActiveTabInterface()); |
| } |
| |
| LensOverlayController* GetLensOverlayController() { |
| return browser() |
| ->tab_strip_model() |
| ->GetActiveTab() |
| ->GetTabFeatures() |
| ->lens_overlay_controller(); |
| } |
| |
| void OpenLensOverlay(LensOverlayInvocationSource invocation_source) { |
| GetLensSearchController()->OpenLensOverlay(invocation_source); |
| } |
| |
| void CloseOverlayAndWaitForOff(LensOverlayController* controller, |
| LensOverlayDismissalSource dismissal_source) { |
| // TODO(crbug.com/404941800): This uses a roundabout way to close the UI. |
| // It has to go through the LensOverlayController because the search |
| // controller doesn't have proper state management. Use search controller |
| // directly once it has its own state for properly determining kOff. |
| LensSearchController::From(controller->GetTabInterface()) |
| ->CloseLensAsync(dismissal_source); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOff; })); |
| } |
| |
| private: |
| ui::UserDataFactory::ScopedOverride lens_search_controller_override_; |
| }; |
| |
| // Regression test for crbug.com/360710001. Asserts the overlay lens page will |
| // load in a tab without crashing. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| OverlayWebUILoadsInTab) { |
| content::WebContents* active_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| // Navigate to the lens overlay WebUI and wait for load to finish. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), GURL(chrome::kChromeUILensOverlayUntrustedURL))); |
| EXPECT_TRUE(active_contents->GetWebUI() |
| ->GetController() |
| ->GetAs<lens::LensOverlayUntrustedUI>()); |
| } |
| |
| // Regression test for crbug.com/360710001. Asserts the side panel lens page |
| // will load in a tab without crashing. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| SidePanelWebUILoadsInTab) { |
| content::WebContents* active_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| |
| // Navigate to the lens overlay WebUI and wait for load to finish. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), GURL(chrome::kChromeUILensUntrustedSidePanelURL))); |
| EXPECT_TRUE(active_contents->GetWebUI() |
| ->GetController() |
| ->GetAs<lens::LensSidePanelUntrustedUI>()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFTest, |
| ContextMenuOpensOverlay) { |
| // Open the PDF document and wait for it to finish loading. |
| const GURL url = embedded_test_server()->GetURL(kPdfDocument); |
| content::RenderFrameHost* extension_host = LoadPdfGetExtensionHost(url); |
| ASSERT_TRUE(extension_host); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay on the PDF using the context menu. |
| bool run_observed = false; |
| auto menu_observer = std::make_unique<ContextMenuNotificationObserver>( |
| IDC_CONTENT_CONTEXT_LENS_REGION_SEARCH, ui::EF_MOUSE_BUTTON, |
| base::BindLambdaForTesting([&](RenderViewContextMenu* menu) { |
| // Verify the overlay activates. |
| run_observed = true; |
| })); |
| |
| content::WebContents* tab = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| content::SimulateMouseClick(tab, 0, blink::WebMouseEvent::Button::kRight); |
| |
| // Verify the overlay eventually opens. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return run_observed && controller->state() == State::kOverlay; |
| })); |
| } |
| |
| // TODO(crbug.com/440876016): Re-enable this test |
| IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFTest, |
| DISABLED_PdfBytesExcludedInRequest) { |
| base::HistogramTester histogram_tester; |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| // Open the PDF document and wait for it to finish loading. |
| const GURL url = embedded_test_server()->GetURL(kPdfDocument); |
| content::RenderFrameHost* extension_host = LoadPdfGetExtensionHost(url); |
| ASSERT_TRUE(extension_host); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify PDF bytes were excluded from the query. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| EXPECT_THAT( |
| lens::Payload(), |
| EqualsProto(fake_query_controller->last_sent_page_content_payload())); |
| |
| // Histograms shouldn't be recorded if CSB isn't shown in session. |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSearchBox.FocusedInSession", |
| /*expected_count=*/0); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.ShownInSession", |
| /*expected_count=*/0); |
| auto entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_ContextualSearchbox_FocusedInSession:: |
| kEntryName); |
| EXPECT_EQ(0u, entries.size()); |
| entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_ContextualSuggest_ZPS_ShownInSession:: |
| kEntryName); |
| EXPECT_EQ(0u, entries.size()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFTest, |
| OnScrollToMessage_PDFNotOnMainTab) { |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| const GURL expected_file_url = |
| GURL("file:///test.pdf#page=3:~:text=text1&text=text2"); |
| |
| // Open the PDF document and wait for it to finish loading. |
| const GURL url = embedded_test_server()->GetURL(kPdfDocument); |
| content::RenderFrameHost* extension_host = LoadPdfGetExtensionHost(url); |
| ASSERT_TRUE(extension_host); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Set up the test extension event observer to monitor is the extension event |
| // is sent correctly. |
| extensions::TestEventRouterObserver observer( |
| extensions::EventRouter::Get(browser()->profile())); |
| |
| // Call OnScrollToMessage. |
| std::vector<std::string> text_fragments = {"text1", "text2"}; |
| uint32_t page_number = 3; |
| int tabs = browser()->tab_strip_model()->count(); |
| controller->results_side_panel_coordinator()->SetLatestPageUrlWithResponse( |
| expected_file_url); |
| ui_test_utils::AllBrowserTabAddedWaiter add_tab; |
| controller->results_side_panel_coordinator()->OnScrollToMessage( |
| text_fragments, page_number); |
| |
| // Verify the new tab has the URL. |
| content::WebContents* new_tab = add_tab.Wait(); |
| content::WaitForLoadStop(new_tab); |
| EXPECT_EQ(new_tab->GetLastCommittedURL(), expected_file_url); |
| |
| // Expect one new tab to have opened. |
| EXPECT_EQ(tabs + 1, browser()->tab_strip_model()->count()); |
| } |
| |
| // This test is wrapped in this BUILDFLAG block because the fallback region |
| // search functionality will not be enabled if the flag is unset. |
| #if BUILDFLAG(ENABLE_LENS_DESKTOP_GOOGLE_BRANDED_FEATURES) |
| IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFTest, |
| ContextMenuViaKeyboardDoesNotOpenOverlay) { |
| // Open the PDF document and wait for it to finish loading. |
| const GURL url = embedded_test_server()->GetURL(kPdfDocument); |
| content::RenderFrameHost* extension_host = LoadPdfGetExtensionHost(url); |
| ASSERT_TRUE(extension_host); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| bool run_observed = false; |
| // Using EF_NONE event type represents a keyboard action. |
| auto menu_observer = std::make_unique<ContextMenuNotificationObserver>( |
| IDC_CONTENT_CONTEXT_LENS_REGION_SEARCH, ui::EF_NONE, |
| base::BindLambdaForTesting([&](RenderViewContextMenu* menu) { |
| // Verify the normal region search flow activates. |
| ASSERT_TRUE(menu->lens_region_search_controller_started_for_testing()); |
| run_observed = true; |
| })); |
| |
| content::WebContents* tab = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| content::SimulateMouseClick(tab, 0, blink::WebMouseEvent::Button::kRight); |
| |
| // Verify the region search flow eventually opens. |
| ASSERT_TRUE(base::test::RunUntil([&]() { return run_observed; })); |
| ASSERT_EQ(controller->state(), State::kOff); |
| } |
| #endif // BUILDFLAG(ENABLE_LENS_DESKTOP_GOOGLE_BRANDED_FEATURES) |
| |
| class LensOverlayControllerBrowserPDFContextualizationTest |
| : public LensOverlayControllerBrowserPDFTest { |
| public: |
| std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() |
| const override { |
| auto enabled = PDFExtensionTestBase::GetEnabledFeatures(); |
| enabled.push_back({lens::features::kLensOverlayContextualSearchbox, |
| {{"send-page-url-for-contextualization", "true"}, |
| {"characters-per-page-heuristic", "1"}, |
| {"use-pdfs-as-context", "true"}, |
| {"use-inner-html-as-context", "true"}, |
| {"file-upload-limit-bytes", |
| base::NumberToString(kFileSizeLimitBytes)}}}); |
| return enabled; |
| } |
| |
| std::vector<base::test::FeatureRef> GetDisabledFeatures() const override { |
| return {}; |
| } |
| |
| protected: |
| static constexpr uint32_t kFileSizeLimitBytes = 10000; |
| }; |
| |
| IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFContextualizationTest, |
| PdfBytesIncludedInRequest) { |
| // Open the PDF document and wait for it to finish loading. |
| const GURL url = embedded_test_server()->GetURL(kPdfDocument); |
| content::RenderFrameHost* extension_host = LoadPdfGetExtensionHost(url); |
| ASSERT_TRUE(extension_host); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_TRUE(controller); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify PDF bytes were included in the query. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data() |
| .size() == 1; |
| })); |
| auto content_data = fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data()[0]; |
| ASSERT_EQ(content_data.content_type(), lens::ContentData::CONTENT_TYPE_PDF); |
| |
| // Verify the searchbox was shown. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| EXPECT_TRUE(fake_controller->fake_overlay_page_ |
| .last_received_should_show_contextual_searchbox_); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFContextualizationTest, |
| PartialPdfIncludedInRequest) { |
| // Open the PDF document and wait for it to finish loading. |
| const GURL url = embedded_test_server()->GetURL(kPdfDocument); |
| content::RenderFrameHost* extension_host = LoadPdfGetExtensionHost(url); |
| ASSERT_TRUE(extension_host); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_TRUE(controller); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&] { return controller->state() == State::kOverlay; })); |
| |
| // Verify pdf content was included in the query. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| ASSERT_TRUE(base::test::RunUntil([&] { |
| return fake_query_controller->last_sent_partial_content().pages_size() == 1; |
| })); |
| ASSERT_EQ( |
| "this is some text\r\nsome more text", |
| fake_query_controller->last_sent_partial_content().pages(0).text_segments( |
| 0)); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFContextualizationTest, |
| PageUrlIncludedInRequest) { |
| // Open the PDF document and wait for it to finish loading. |
| const GURL url = embedded_test_server()->GetURL(kPdfDocument); |
| content::RenderFrameHost* extension_host = LoadPdfGetExtensionHost(url); |
| ASSERT_TRUE(extension_host); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_TRUE(controller); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify PDF bytes were included in the query. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return fake_query_controller->last_sent_page_url() == url; })); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFContextualizationTest, |
| CurrentPageIncludedInRequest) { |
| // Open the PDF document and wait for it to finish loading. |
| const GURL url = embedded_test_server()->GetURL(kPdfDocument); |
| content::RenderFrameHost* extension_host = LoadPdfGetExtensionHost(url); |
| ASSERT_TRUE(extension_host); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_TRUE(controller); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify PDF page number was included in the query. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| ASSERT_EQ(0, fake_query_controller->sent_full_image_objects_request() |
| .viewport_request_context() |
| .pdf_page_number()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFContextualizationTest, |
| PdfBytesInFollowUpRequest) { |
| base::HistogramTester histogram_tester; |
| |
| const GURL url = embedded_test_server()->GetURL(kPdfDocument); |
| LoadPdfGetExtensionHost(url); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Make a searchbox query. |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "oranges", AutocompleteMatchType::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| /*additional_query_params=*/{}); |
| |
| // Verify transitions to live page. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| |
| // Reset mock query controller so we can verify the new request. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| fake_query_controller->ResetTestingState(); |
| |
| // Change page to new PDF. |
| const GURL new_url = embedded_test_server()->GetURL(kPdfDocumentWithForm); |
| LoadPdfGetExtensionHost(new_url); |
| |
| // Focus the searchbox. |
| controller->OnFocusChangedForTesting(true); |
| |
| // Issue a new searchbox query. |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "oranges", AutocompleteMatchType::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| /*additional_query_params=*/{}); |
| |
| // Verify bytes was updated. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data() |
| .size() != 0; |
| })); |
| ASSERT_EQ(lens::ContentData::CONTENT_TYPE_PDF, |
| fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data()[0] |
| .content_type()); |
| |
| // Recording the histograms is async, so need to wait for it to be recorded |
| // before continuing to prevent flakiness. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return histogram_tester.GetBucketCount( |
| "Lens.Overlay.ByPageContentType.Pdf.PageCount", 1) == 2; |
| })); |
| |
| // Verify the histogram recorded the new byte size. |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ByPageContentType.Pdf.DocumentSize2", |
| /*expected_count=*/2); |
| // Verify the histogram two documents of one page. |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ByPageContentType.Pdf.PageCount", /*sample*/ 1, |
| /*expected_count=*/2); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSearchBox.ByPageContentType.Pdf." |
| "TimeFromNavigationToFirstInteraction", |
| /*expected_count=*/1); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFContextualizationTest, |
| LargePdfNotIncludedInRequest) { |
| base::HistogramTester histogram_tester; |
| |
| // Verify the document is over the size limit. |
| auto file_size = GetFileSizeForTestDataFile(kPdfDocument12KbFileName); |
| ASSERT_TRUE(file_size.has_value()); |
| ASSERT_GT(file_size.value(), kFileSizeLimitBytes); |
| |
| // Open the PDF document that is over our size limit and wait for it to finish |
| // loading. |
| const GURL url = embedded_test_server()->GetURL( |
| base::StrCat({"/", kPdfDocument12KbFileName})); |
| content::RenderFrameHost* extension_host = LoadPdfGetExtensionHost(url); |
| ASSERT_TRUE(extension_host); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_TRUE(controller); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify PDF bytes were not included in the query. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| EXPECT_THAT( |
| lens::Payload(), |
| EqualsProto(fake_query_controller->last_sent_page_content_payload())); |
| |
| // Verify the searchbox was shown. The CSB should be shown for all PDFs |
| // regardless of size. The server will handle what should be returned for |
| // large PDFs. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| EXPECT_TRUE(fake_controller->fake_overlay_page_ |
| .last_received_should_show_contextual_searchbox_); |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| |
| // Verify the histogram recorded the searchbox was shown. |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.ContextualSearchBox.ByPageContentType.Pdf.ShownInSession", |
| /*sample*/ true, |
| /*expected_bucket_count=*/1); |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.ContextualSearchBox.ByDocumentType.Pdf.ShownInSession", |
| /*sample*/ true, |
| /*expected_bucket_count=*/1); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFContextualizationTest, |
| QueryFlowRestartsOnSearchboxFocus) { |
| base::HistogramTester histogram_tester; |
| |
| const GURL url = embedded_test_server()->GetURL(kPdfDocument); |
| LoadPdfGetExtensionHost(url); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Grab the fake query controller. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| |
| // Verify the current number of requests is as expected. Both requests happen |
| // async, so need to wait for both to be sent before continuing to prevent |
| // flakiness. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->num_full_image_requests_sent() == 1 && |
| fake_query_controller->num_page_content_update_requests_sent() == 1; |
| })); |
| |
| // Cause cluster info to expire. |
| fake_query_controller->ResetRequestClusterInfoStateForTesting(); |
| |
| // Reset the fake query controller testing state to prevent dangling the |
| // stored page content bytes. |
| fake_query_controller->ResetTestingState(); |
| |
| // Focus the searchbox. |
| controller->OnFocusChangedForTesting(true); |
| |
| // Issue a query. |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "red", AutocompleteMatchType::Type::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/true, std::map<std::string, std::string>()); |
| |
| // Verify transitions to live page. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| |
| // Cause cluster info to expire again. |
| fake_query_controller->ResetRequestClusterInfoStateForTesting(); |
| |
| // Reset the fake query controller testing state to prevent dangling the |
| // stored page content bytes. |
| fake_query_controller->ResetTestingState(); |
| |
| // Focus the searchbox again. |
| controller->OnFocusChangedForTesting(true); |
| |
| // Verify a new full image and page content request was sent in the live page |
| // state. Both requests happen async, so need to wait for both to be sent |
| // before continuing to prevent flakiness. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->num_full_image_requests_sent() == 2 && |
| fake_query_controller->num_page_content_update_requests_sent() == 2; |
| })); |
| } |
| |
| // TODO(crbug.com/423881729): Flaky on Win ASAN |
| #if BUILDFLAG(IS_WIN) && defined(ADDRESS_SANITIZER) |
| #define MAYBE_Histograms DISABLED_Histograms |
| #else |
| #define MAYBE_Histograms Histograms |
| #endif |
| IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFContextualizationTest, |
| MAYBE_Histograms) { |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| base::HistogramTester histogram_tester; |
| |
| const GURL url = embedded_test_server()->GetURL(kPdfDocument); |
| LoadPdfGetExtensionHost(url); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| controller->OnZeroSuggestShownForTesting(); |
| |
| // Simulate a zero suggest suggestion being chosen. |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "red", AutocompleteMatchType::Type::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/true, std::map<std::string, std::string>()); |
| |
| // Issuing a search from the overlay state can only be done through the |
| // contextual searchbox and should result in a live page with results. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| |
| // Recording the histograms is async, so need to wait for it to be recorded |
| // before continuing to prevent flakiness. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return histogram_tester.GetBucketCount( |
| "Lens.Overlay.ByPageContentType.Pdf.PageCount", 1) == 1; |
| })); |
| |
| histogram_tester.ExpectUniqueSample("Lens.Overlay.ByDocumentType.Pdf.Invoked", |
| /*sample*/ true, |
| /*expected_bucket_count=*/1); |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.ContextualSearchBox.ShownInSession", |
| /*sample*/ true, |
| /*expected_bucket_count=*/1); |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.ContextualSearchBox.ByPageContentType.Pdf.ShownInSession", |
| /*sample*/ true, |
| /*expected_bucket_count=*/1); |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.ContextualSearchBox.ByDocumentType.Pdf.ShownInSession", |
| /*sample*/ true, |
| /*expected_bucket_count=*/1); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ByPageContentType.Pdf.DocumentSize2", |
| /*expected_count=*/1); |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.ByPageContentType.Pdf.PageCount", /*sample*/ 1, |
| /*expected_bucket_count=*/1); |
| |
| // Verify UKM metrics were recorded. |
| auto entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_ContextualSearchBox_ShownInSession:: |
| kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| auto* entry = entries[0].get(); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSearchBox_ShownInSession:: |
| kWasShownName, |
| true); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSearchBox_ShownInSession:: |
| kPageContentTypeName, |
| static_cast<int64_t>(lens::MimeType::kPdf)); |
| |
| // Assert zps used in session metrics get recorded. |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.SuggestionUsedInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.SuggestionUsedInSession", true, |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.ByPageContentType.Pdf." |
| "SuggestionUsedInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.ByPageContentType.Pdf." |
| "SuggestionUsedInSession", |
| true, /*expected_count=*/1); |
| entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders:: |
| Lens_Overlay_ContextualSuggest_ZPS_SuggestionUsedInSession:: |
| kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| entry = entries[0].get(); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders:: |
| Lens_Overlay_ContextualSuggest_ZPS_SuggestionUsedInSession:: |
| kSuggestionUsedInSessionName, |
| true); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders:: |
| Lens_Overlay_ContextualSuggest_ZPS_SuggestionUsedInSession:: |
| kPageContentTypeName, |
| static_cast<int64_t>(lens::MimeType::kPdf)); |
| |
| // Assert query issued in session metrics get recorded. |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.QueryIssuedInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.QueryIssuedInSession", true, |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.ByPageContentType.Pdf." |
| "QueryIssuedInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.ByPageContentType.Pdf." |
| "QueryIssuedInSession", |
| true, /*expected_count=*/1); |
| entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_ContextualSuggest_QueryIssuedInSession:: |
| kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| entry = entries[0].get(); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSuggest_QueryIssuedInSession:: |
| kQueryIssuedInSessionName, |
| true); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSuggest_QueryIssuedInSession:: |
| kPageContentTypeName, |
| static_cast<int64_t>(lens::MimeType::kPdf)); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFContextualizationTest, |
| RecordSearchboxFocusedInSessionNavigationHistograms) { |
| base::HistogramTester histogram_tester; |
| |
| // Load a non PDF. |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| WaitForPaintImpl(browser(), url); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| controller->OnFocusChangedForTesting(true); |
| |
| // Make a searchbox query. |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "oranges", AutocompleteMatchType::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| /*additional_query_params=*/{}); |
| |
| // Verify transitions to live page. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| fake_query_controller->ResetTestingState(); |
| |
| // Change page to a PDF. |
| const GURL pdf_url = embedded_test_server()->GetURL(kPdfDocumentWithForm); |
| LoadPdfGetExtensionHost(pdf_url); |
| |
| // Focus the searchbox. |
| controller->OnFocusChangedForTesting(true); |
| |
| // Issue a new searchbox query. |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "oranges", AutocompleteMatchType::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| /*additional_query_params=*/{}); |
| |
| // Verify transitions to live page. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data() |
| .size() == 1; |
| })); |
| auto content_data = fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data(); |
| ASSERT_EQ(content_data.size(), 1); |
| ASSERT_EQ(content_data[0].content_type(), |
| lens::ContentData::CONTENT_TYPE_PDF); |
| |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| // Even after navigation, histograms should still be recorded. |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSearchBox.FocusedInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSearchBox.FocusedInSession", true, |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSearchBox.ByPageContentType.AnnotatedPageContent." |
| "FocusedInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSearchBox.ByPageContentType.AnnotatedPageContent." |
| "FocusedInSession", |
| true, /*expected_count=*/1); |
| } |
| |
| // TODO(crbug.com/413042395): This test is not testing overlay logic, but |
| // instead the side panel logic. Therefore, this test should be moved to a side |
| // panel browsertest file. |
| IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFContextualizationTest, |
| SidePanel_SameTabCrossOriginLinkClick_PdfWithFragment) { |
| const GURL pdf_url = embedded_test_server()->GetURL(kMultiPagePdf); |
| LoadPdfGetExtensionHost(pdf_url); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| EXPECT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| int tab_count = browser()->tab_strip_model()->count(); |
| |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "green", AutocompleteMatchType::Type::SEARCH_WHAT_YOU_TYPED, |
| /*is_zero_prefix_suggestion=*/false, |
| std::map<std::string, std::string>()); |
| |
| // Issuing a searchbox request when the controller is in kOverlay state |
| // should result in the state being kLivePageAndResults. This shouldn't |
| // change the CONTEXTUAL_SEARCHBOX page classification. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| EXPECT_EQ(controller->GetPageClassificationForTesting(), |
| metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX); |
| |
| // 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())); |
| |
| // Set up the test extension event observer to monitor is the extension event |
| // is sent correctly. |
| extensions::TestEventRouterObserver observer( |
| extensions::EventRouter::Get(browser()->profile())); |
| |
| // 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); |
| EXPECT_TRUE(results_frame); |
| |
| std::string pdf_url_with_fragment = std::string(kMultiPagePdf) + "#page=3"; |
| const GURL nav_url = embedded_test_server()->GetURL(pdf_url_with_fragment); |
| |
| // Simulate a cross-origin navigation on the results frame. |
| EXPECT_TRUE(content::ExecJs( |
| results_frame, content::JsReplace(kSameTabLinkClickScript, nav_url), |
| content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES)); |
| |
| // Expect an extension event to be sent to the PDF viewer. |
| observer.WaitForEventWithName( |
| extensions::api::pdf_viewer_private::OnShouldUpdateViewport::kEventName); |
| EXPECT_EQ(1u, observer.dispatched_events().size()); |
| EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
| } |
| |
| class LensOverlayControllerBrowserPDFUpdatedContentFieldsTest |
| : public LensOverlayControllerBrowserPDFContextualizationTest { |
| public: |
| std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() |
| const override { |
| auto enabled = PDFExtensionTestBase::GetEnabledFeatures(); |
| enabled.push_back({lens::features::kLensOverlayContextualSearchbox, |
| {{"send-page-url-for-contextualization", "true"}, |
| {"characters-per-page-heuristic", "1"}, |
| {"use-pdfs-as-context", "true"}, |
| {"use-inner-html-as-context", "true"}, |
| {"file-upload-limit-bytes", |
| base::NumberToString(kFileSizeLimitBytes)}, |
| {"use-updated-content-fields", "true"}}}); |
| return enabled; |
| } |
| |
| std::vector<base::test::FeatureRef> GetDisabledFeatures() const override { |
| return {}; |
| } |
| |
| protected: |
| static constexpr uint32_t kFileSizeLimitBytes = 10000; |
| }; |
| |
| IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFUpdatedContentFieldsTest, |
| PartialPdfIncludedInRequest) { |
| // Open the PDF document and wait for it to finish loading. |
| const GURL url = embedded_test_server()->GetURL(kPdfDocument); |
| content::RenderFrameHost* extension_host = LoadPdfGetExtensionHost(url); |
| ASSERT_TRUE(extension_host); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_TRUE(controller); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&] { return controller->state() == State::kOverlay; })); |
| |
| // Verify pdf content was included in the query. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| ASSERT_TRUE(base::test::RunUntil([&] { |
| return fake_query_controller->last_sent_partial_content().pages_size() == 1; |
| })); |
| ASSERT_EQ( |
| "this is some text\r\nsome more text", |
| fake_query_controller->last_sent_partial_content().pages(0).text_segments( |
| 0)); |
| } |
| |
| class LensOverlayControllerBrowserPDFIncreaseLimitTest |
| : public LensOverlayControllerBrowserPDFContextualizationTest { |
| public: |
| std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() |
| const override { |
| auto enabled = PDFExtensionTestBase::GetEnabledFeatures(); |
| enabled.push_back({lens::features::kLensOverlayContextualSearchbox, |
| {{"use-pdfs-as-context", "true"}, |
| {"characters-per-page-heuristic", "1"}, |
| {"use-inner-html-as-context", "true"}, |
| {"pdf-text-character-limit", "50"}, |
| {"file-upload-limit-bytes", |
| base::NumberToString(kFileSizeLimitBytes)}}}); |
| return enabled; |
| } |
| |
| std::vector<base::test::FeatureRef> GetDisabledFeatures() const override { |
| return {}; |
| } |
| |
| protected: |
| static constexpr uint32_t kFileSizeLimitBytes = 200000; |
| }; |
| |
| IN_PROC_BROWSER_TEST_P(LensOverlayControllerBrowserPDFIncreaseLimitTest, |
| PartialPdfCharacterLimitReached_IncludedInRequest) { |
| // Open the PDF document and wait for it to finish loading. |
| const GURL url = embedded_test_server()->GetURL(kMultiPagePdf); |
| content::RenderFrameHost* extension_host = LoadPdfGetExtensionHost(url); |
| ASSERT_TRUE(extension_host); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_TRUE(controller); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&] { return controller->state() == State::kOverlay; })); |
| |
| // Verify the first two pages were sent, excluding the last page of the PDF. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| |
| ASSERT_TRUE(base::test::RunUntil([&] { |
| return 2 == fake_query_controller->last_sent_partial_content().pages_size(); |
| })); |
| ASSERT_EQ( |
| "1 First Section\r\nThis is the first section.\r\n1", |
| fake_query_controller->last_sent_partial_content().pages(0).text_segments( |
| 0)); |
| ASSERT_EQ( |
| "1.1 First Subsection\r\nThis is the first subsection.\r\n2", |
| fake_query_controller->last_sent_partial_content().pages(1).text_segments( |
| 0)); |
| } |
| |
| // TODO(crbug.com/40268279): Stop testing both modes after OOPIF PDF viewer |
| // launches. |
| INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE(LensOverlayControllerBrowserPDFTest); |
| INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE( |
| LensOverlayControllerBrowserPDFContextualizationTest); |
| INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE( |
| LensOverlayControllerBrowserPDFUpdatedContentFieldsTest); |
| INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE( |
| LensOverlayControllerBrowserPDFIncreaseLimitTest); |
| |
| // Test with --enable-pixel-output-in-tests enabled, required to actually grab |
| // screenshots for color extraction. |
| class LensOverlayControllerBrowserWithPixelsTest |
| : public LensOverlayControllerBrowserTest { |
| protected: |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| command_line->AppendSwitch(::switches::kEnablePixelOutputInTests); |
| InProcessBrowserTest::SetUpCommandLine(command_line); |
| } |
| |
| void SetupFeatureList() override { |
| feature_list_.InitWithFeatures( |
| /*enabled_features=*/{}, /*disabled_features=*/{ |
| lens::features::kLensOverlayVisualSelectionUpdates}); |
| } |
| |
| bool IsNotEmptyAndNotTransparentBlack(SkBitmap bitmap) { |
| if (!bitmap.empty()) { |
| for (int x = 0; x < bitmap.width(); x++) { |
| for (int y = 0; y < bitmap.height(); y++) { |
| if (bitmap.getColor(x, y) != SK_ColorTRANSPARENT) { |
| return true; |
| } |
| } |
| } |
| } |
| return false; |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserWithPixelsTest, |
| DynamicTheme_Fallback) { |
| WaitForPaint(); |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify screenshot was captured and stored. |
| auto screenshot_bitmap = controller->initial_screenshot(); |
| EXPECT_TRUE(IsNotEmptyAndNotTransparentBlack(screenshot_bitmap)); |
| screenshot_bitmap = controller->updated_screenshot(); |
| EXPECT_TRUE(IsNotEmptyAndNotTransparentBlack(screenshot_bitmap)); |
| |
| // Verify screenshot was encoded and passed to WebUI. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| EXPECT_FALSE( |
| fake_controller->fake_overlay_page_.last_received_screenshot_.empty()); |
| |
| // Verify expected color palette was identified, fallback expected |
| // with the page being mostly colorless. |
| ASSERT_EQ(lens::PaletteId::kFallback, controller->color_palette()); |
| // Verify expected theme color were passed to WebUI. |
| auto expected_theme = lens::mojom::OverlayTheme::New(); |
| expected_theme->primary = lens::kColorFallbackPrimary; |
| expected_theme->shader_layer_1 = lens::kColorFallbackShaderLayer1; |
| expected_theme->shader_layer_2 = lens::kColorFallbackShaderLayer2; |
| expected_theme->shader_layer_3 = lens::kColorFallbackShaderLayer3; |
| expected_theme->shader_layer_4 = lens::kColorFallbackShaderLayer4; |
| expected_theme->shader_layer_5 = lens::kColorFallbackShaderLayer5; |
| expected_theme->scrim = lens::kColorFallbackScrim; |
| expected_theme->surface_container_highest_light = |
| lens::kColorFallbackSurfaceContainerHighestLight; |
| expected_theme->surface_container_highest_dark = |
| lens::kColorFallbackSurfaceContainerHighestDark; |
| expected_theme->selection_element = lens::kColorFallbackSelectionElement; |
| EXPECT_TRUE( |
| fake_controller->fake_overlay_page_.last_received_theme_.has_value()); |
| const auto& received_theme = |
| fake_controller->fake_overlay_page_.last_received_theme_.value(); |
| EXPECT_EQ(received_theme->primary, expected_theme->primary); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserWithPixelsTest, |
| DynamicTheme_DynamicColorTangerine) { |
| WaitForPaint(kDocumentWithDynamicColor); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| EXPECT_TRUE( |
| fake_controller->fake_overlay_page_.last_received_screenshot_.empty()); |
| EXPECT_FALSE( |
| fake_controller->fake_overlay_page_.last_received_theme_.has_value()); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify screenshot was captured and stored. |
| auto screenshot_bitmap = controller->initial_screenshot(); |
| EXPECT_TRUE(IsNotEmptyAndNotTransparentBlack(screenshot_bitmap)); |
| screenshot_bitmap = controller->updated_screenshot(); |
| EXPECT_TRUE(IsNotEmptyAndNotTransparentBlack(screenshot_bitmap)); |
| |
| // Verify screenshot was encoded and passed to WebUI. |
| EXPECT_FALSE( |
| fake_controller->fake_overlay_page_.last_received_screenshot_.empty()); |
| |
| // Verify expected color palette was identified. |
| ASSERT_EQ(lens::PaletteId::kTangerine, controller->color_palette()); |
| // Verify expected theme color were passed to WebUI. |
| auto expected_theme = controller->CreateTheme(lens::PaletteId::kTangerine); |
| EXPECT_TRUE( |
| fake_controller->fake_overlay_page_.last_received_theme_.has_value()); |
| const auto& received_theme = |
| fake_controller->fake_overlay_page_.last_received_theme_.value(); |
| EXPECT_EQ(received_theme->primary, expected_theme->primary); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserWithPixelsTest, |
| ViewportImageBoundingBoxes) { |
| WaitForPaint(kDocumentWithImage); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| EXPECT_TRUE( |
| fake_controller->fake_overlay_page_.last_received_screenshot_.empty()); |
| |
| // Showing UI should change the state to screenshot and eventually to starting |
| // WebUI. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify screenshot was captured and stored. |
| auto screenshot_bitmap = controller->initial_screenshot(); |
| EXPECT_TRUE(IsNotEmptyAndNotTransparentBlack(screenshot_bitmap)); |
| |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| |
| auto& boxes = fake_query_controller->last_sent_significant_region_boxes(); |
| EXPECT_EQ(boxes.size(), 1UL); |
| EXPECT_GT(boxes[0]->box.x(), 0); |
| EXPECT_LT(boxes[0]->box.x(), 1); |
| EXPECT_GT(boxes[0]->box.y(), 0); |
| EXPECT_LT(boxes[0]->box.y(), 1); |
| EXPECT_GT(boxes[0]->box.width(), 0); |
| EXPECT_LT(boxes[0]->box.width(), 1); |
| EXPECT_GT(boxes[0]->box.height(), 0); |
| EXPECT_LT(boxes[0]->box.height(), 1); |
| EXPECT_EQ(boxes[0]->rotation, 0); |
| EXPECT_EQ(boxes[0]->coordinate_type, |
| lens::mojom::CenterRotatedBox_CoordinateType::kNormalized); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| RecordTimeToFirstInteraction) { |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| base::HistogramTester histogram_tester; |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // No metrics should be emitted before anything happens. |
| auto entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_TimeToFirstInteraction::kEntryName); |
| EXPECT_EQ(0u, entries.size()); |
| histogram_tester.ExpectTotalCount("Lens.Overlay.TimeToFirstInteraction", |
| /*expected_count=*/0); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ByInvocationSource.AppMenu.TimeToFirstInteraction", |
| /*expected_count=*/0); |
| |
| // Issue a search. |
| controller->IssueTextSelectionRequestForTesting("oranges", 20, 200); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| histogram_tester.ExpectTotalCount("Lens.Overlay.TimeToFirstInteraction", |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ByInvocationSource.AppMenu.TimeToFirstInteraction", |
| /*expected_count=*/1); |
| entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_TimeToFirstInteraction::kEntryName); |
| EXPECT_EQ(2u, entries.size()); |
| const char kAllEntryPoints[] = "AllEntryPoints"; |
| const char kAppMenu[] = "AppMenu"; |
| EXPECT_TRUE( |
| ukm::TestUkmRecorder::EntryHasMetric(entries[0].get(), kAllEntryPoints)); |
| EXPECT_TRUE(ukm::TestUkmRecorder::EntryHasMetric(entries[1].get(), kAppMenu)); |
| |
| // Issue another search. |
| controller->IssueTextSelectionRequestForTesting("apples", 30, 250); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // Another search should not log another time to first interaction metric. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.TimeToFirstInteraction", |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ByInvocationSource.AppMenu.TimeToFirstInteraction", |
| /*expected_count=*/1); |
| entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_TimeToFirstInteraction::kEntryName); |
| EXPECT_EQ(2u, entries.size()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| RecordTimeToFirstInteractionPendingRegion) { |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| base::HistogramTester histogram_tester; |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlayWithPendingRegion( |
| LensOverlayInvocationSource::kContentAreaContextMenuImage, |
| kTestRegion->Clone(), CreateNonEmptyBitmap(100, 100)); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlayAndResults; })); |
| EXPECT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| EXPECT_TRUE(controller->GetOverlayViewForTesting()->GetVisible()); |
| |
| // No metrics should be emitted before anything happens. |
| auto entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_TimeToFirstInteraction::kEntryName); |
| EXPECT_EQ(0u, entries.size()); |
| histogram_tester.ExpectTotalCount("Lens.Overlay.TimeToFirstInteraction", |
| /*expected_count=*/0); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ByInvocationSource.ContentAreaContextMenuImage." |
| "TimeToFirstInteraction", |
| /*expected_count=*/0); |
| |
| // Issue a search. |
| controller->IssueTextSelectionRequestForTesting("oranges", 20, 200); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // When a lens overlay instance was invoked with an initial region selected, |
| // we shouldn't record TimeToFirstInteraction. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.TimeToFirstInteraction", |
| /*expected_count=*/0); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ByInvocationSource.ContentAreaContextMenuImage." |
| "TimeToFirstInteraction", |
| /*expected_count=*/0); |
| entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_TimeToFirstInteraction::kEntryName); |
| EXPECT_EQ(0u, entries.size()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| InnerTextBytesInRequest) { |
| WaitForPaint(kDocumentWithNonAsciiCharacters); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify inner text was incluced as bytes in the the query. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data() |
| .size() == 2; |
| })); |
| auto content_data = fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data(); |
| ASSERT_EQ(content_data[0].content_type(), |
| lens::ContentData::CONTENT_TYPE_INNER_TEXT); |
| |
| // Verify the bytes are actually what we expect them to be. |
| ASSERT_EQ("The below are non-ascii characters.\n\nこんにちは thêrē 🐶 ©", |
| std::string(content_data[0].data().begin(), |
| content_data[0].data().end())); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| InnerTextBytesInFollowUpRequest) { |
| base::HistogramTester histogram_tester; |
| WaitForPaint(kDocumentWithNonAsciiCharacters); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Setup fake text in the OCR response. Included 0 words from the DOM to |
| // ensure the similarity score is still recorded when its 0. |
| auto* fake_controller = |
| static_cast<LensSearchControllerFake*>(GetLensSearchController()); |
| fake_controller->SetOcrResponseWords( |
| {"BLAH.", " random - ", " ,no] ", "RANDOM", "\n\npuppies.\n"}); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Make a searchbox query. |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "oranges", AutocompleteMatchType::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| /*additional_query_params=*/{}); |
| |
| // Verify transitions to live page. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| |
| // Reset mock query controller so we can verify the new request. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| fake_query_controller->ResetTestingState(); |
| |
| // Change page. |
| WaitForPaint(kDocumentWithNamedElement); |
| |
| // Focus the searchbox. |
| controller->OnFocusChangedForTesting(true); |
| |
| // Issue a new searchbox query. |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "oranges", AutocompleteMatchType::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| /*additional_query_params=*/{}); |
| |
| // Verify inner text was updated. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data() |
| .size() != 0; |
| })); |
| ASSERT_EQ(lens::ContentData::CONTENT_TYPE_INNER_TEXT, |
| fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data()[0] |
| .content_type()); |
| |
| // The size of the PlainText is retrieved when issuing the searchbox query. |
| // Getting the size of the HTML is retrieved afterwards, and therefore is |
| // async at this point. RunUntil the bytes are retrieved and the histogram is |
| // recorded to prevent flakiness. To check if the histogram is recorded, grab |
| // the bucket count for the HTML document size of 0, since the HTML we are |
| // testing on is so small it rounds to 0 KB. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return histogram_tester.GetBucketCount( |
| "Lens.Overlay.ByPageContentType.PlainText.DocumentSize2", 0) == |
| 2; |
| })); |
| |
| // Verify the size histograms recorded the new bytes. |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ByPageContentType.PlainText.DocumentSize2", |
| /*expected_count=*/2); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSearchBox.ByPageContentType.AnnotatedPageContent." |
| "TimeFromNavigationToFirstInteraction", |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ByPageContentType.Pdf.PageCount", |
| /*expected_count=*/0); |
| |
| // This histogram is async so run until it is recorded. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return histogram_tester.GetBucketCount("Lens.Overlay.OcrDomSimilarity", |
| 0) == 1; |
| })); |
| // Verify the similarity score was only recorded once for the session. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.OcrDomSimilarity", 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| UpdateScreenshotOnSearchboxFocus) { |
| base::HistogramTester histogram_tester; |
| WaitForPaint(kDocumentWithNonAsciiCharacters); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Make a searchbox query. |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "oranges", AutocompleteMatchType::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| /*additional_query_params=*/{}); |
| |
| // Verify transitions to live page. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| |
| // Reset mock query controller so we can verify the new request. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| fake_query_controller->ResetTestingState(); |
| |
| // Change page. |
| WaitForPaint(kDocumentWithNamedElement); |
| |
| // Focus the searchbox. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| fake_controller->OnFocusChangedForTesting(/*focused=*/true); |
| |
| // Verify a new full image and page content request was sent in the live page |
| // state. Both requests happen async, so need to wait for both to be sent |
| // before continuing to prevent flakiness. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->num_full_image_requests_sent() == 2 && |
| fake_query_controller->num_page_content_update_requests_sent() == 2; |
| })); |
| |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data() |
| .size() != 0; |
| })); |
| ASSERT_EQ(lens::ContentData::CONTENT_TYPE_INNER_TEXT, |
| fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data()[0] |
| .content_type()); |
| ASSERT_TRUE(fake_query_controller->sent_full_image_objects_request() |
| .has_image_data()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| WebDocumentTypeHistograms) { |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| base::HistogramTester histogram_tester; |
| |
| WaitForPaint(kDocumentWithNonAsciiCharacters); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Setup fake text in the OCR response. Included 4 words on the DOM, and 1 |
| // not, to make a similarity score of 0.8. Also include some random characters |
| // to make sure they are ignored. |
| auto* fake_controller = |
| static_cast<LensSearchControllerFake*>(GetLensSearchController()); |
| fake_controller->SetOcrResponseWords( |
| {"The.", " below - ", " ,are] ", "RANDOM", "\n\n\nCharacters.\n"}); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify page content was included as bytes in the query. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data() |
| .size() != 0; |
| })); |
| |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.ByDocumentType.Html.Invoked", |
| /*sample*/ true, |
| /*expected_bucket_count=*/1); |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.ContextualSearchBox.ShownInSession", |
| /*sample*/ true, |
| /*expected_bucket_count=*/1); |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.ContextualSearchBox.ByPageContentType.AnnotatedPageContent." |
| "ShownInSession", |
| /*sample*/ true, |
| /*expected_bucket_count=*/1); |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.ContextualSearchBox.ByDocumentType.Html." |
| "ShownInSession", |
| /*sample*/ true, |
| /*expected_bucket_count=*/1); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ByPageContentType.PlainText.DocumentSize2", |
| /*expected_count=*/1); |
| |
| // Verify UKM metrics were recorded. |
| auto entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_ContextualSearchBox_ShownInSession:: |
| kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| auto* entry = entries[0].get(); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSearchBox_ShownInSession:: |
| kWasShownName, |
| true); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSearchBox_ShownInSession:: |
| kPageContentTypeName, |
| static_cast<int64_t>(lens::MimeType::kAnnotatedPageContent)); |
| |
| // This histogram is async so run until it is recorded. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return histogram_tester.GetBucketCount("Lens.Overlay.OcrDomSimilarity", |
| 80) == 1; |
| })); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| RecordSearchboxFocusedInSessionHistograms) { |
| base::HistogramTester histogram_tester; |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| WaitForPaint(kDocumentWithNonAsciiCharacters); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify inner HTML was included as bytes in the the query. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data() |
| .size() == 2; |
| })); |
| |
| // Simulate the searchbox being focused. |
| controller->OnFocusChangedForTesting(true); |
| |
| // Close the overlay and assert that each histogram was recorded once and |
| // that the searchbox was focused in the session. |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSearchBox.FocusedInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSearchBox.FocusedInSession", true, |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSearchBox.ByPageContentType.AnnotatedPageContent." |
| "FocusedInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSearchBox.ByPageContentType.AnnotatedPageContent." |
| "FocusedInSession", |
| true, /*expected_count=*/1); |
| auto entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_ContextualSearchbox_FocusedInSession:: |
| kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| auto* entry = entries[0].get(); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSearchbox_FocusedInSession:: |
| kFocusedInSessionName, |
| true); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSearchbox_FocusedInSession:: |
| kPageContentTypeName, |
| static_cast<int64_t>(lens::MimeType::kAnnotatedPageContent)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| RecordSearchboxNotFocusedInSessionHistograms) { |
| base::HistogramTester histogram_tester; |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| WaitForPaint(kDocumentWithNonAsciiCharacters); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify inner HTML was included as bytes in the the query. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data() |
| .size() == 2; |
| })); |
| |
| // Close the overlay and assert that the histogram was recorded once and |
| // that the searchbox was not focused in the session. |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSearchBox.FocusedInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSearchBox.FocusedInSession", false, |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSearchBox.ByPageContentType.AnnotatedPageContent." |
| "FocusedInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSearchBox.ByPageContentType.AnnotatedPageContent." |
| "FocusedInSession", |
| false, /*expected_count=*/1); |
| auto entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_ContextualSearchbox_FocusedInSession:: |
| kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| auto* entry = entries[0].get(); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSearchbox_FocusedInSession:: |
| kFocusedInSessionName, |
| false); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSearchbox_FocusedInSession:: |
| kPageContentTypeName, |
| static_cast<int64_t>(lens::MimeType::kAnnotatedPageContent)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| RecordContextualZpsSessionHistograms) { |
| base::HistogramTester histogram_tester; |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| WaitForPaint(kDocumentWithNonAsciiCharacters); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify inner HTML was included as bytes in the the query. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data() |
| .size() == 2; |
| })); |
| |
| controller->OnZeroSuggestShownForTesting(); |
| |
| // Simulate a zero suggest suggestion being chosen. |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "red", AutocompleteMatchType::Type::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/true, std::map<std::string, std::string>()); |
| |
| // Issuing a search from the overlay state can only be done through the |
| // contextual searchbox and should result in a live page with results. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| |
| // Close the overlay and assert that the histogram was recorded once and |
| // that zps was shown in the session. |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| |
| // Assert zps shown in session metrics get recorded. |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.ShownInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.ShownInSession", true, |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.ByPageContentType." |
| "AnnotatedPageContent." |
| "ShownInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.ByPageContentType." |
| "AnnotatedPageContent." |
| "ShownInSession", |
| true, /*expected_count=*/1); |
| auto entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_ContextualSuggest_ZPS_ShownInSession:: |
| kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| auto* entry = entries[0].get(); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSuggest_ZPS_ShownInSession:: |
| kShownInSessionName, |
| true); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSuggest_ZPS_ShownInSession:: |
| kPageContentTypeName, |
| static_cast<int64_t>(lens::MimeType::kAnnotatedPageContent)); |
| |
| // Assert zps used in session metrics get recorded. |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.SuggestionUsedInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.SuggestionUsedInSession", true, |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.ByPageContentType." |
| "AnnotatedPageContent." |
| "SuggestionUsedInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.ByPageContentType." |
| "AnnotatedPageContent." |
| "SuggestionUsedInSession", |
| true, /*expected_count=*/1); |
| entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders:: |
| Lens_Overlay_ContextualSuggest_ZPS_SuggestionUsedInSession:: |
| kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| entry = entries[0].get(); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders:: |
| Lens_Overlay_ContextualSuggest_ZPS_SuggestionUsedInSession:: |
| kSuggestionUsedInSessionName, |
| true); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders:: |
| Lens_Overlay_ContextualSuggest_ZPS_SuggestionUsedInSession:: |
| kPageContentTypeName, |
| static_cast<int64_t>(lens::MimeType::kAnnotatedPageContent)); |
| |
| // Assert query issued in session metrics get recorded. |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.QueryIssuedInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.QueryIssuedInSession", true, |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.ByPageContentType.AnnotatedPageContent." |
| "QueryIssuedInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.ByPageContentType.AnnotatedPageContent." |
| "QueryIssuedInSession", |
| true, /*expected_count=*/1); |
| entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_ContextualSuggest_QueryIssuedInSession:: |
| kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| entry = entries[0].get(); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSuggest_QueryIssuedInSession:: |
| kQueryIssuedInSessionName, |
| true); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSuggest_QueryIssuedInSession:: |
| kPageContentTypeName, |
| static_cast<int64_t>(lens::MimeType::kAnnotatedPageContent)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| RecordContextualZpsNotSelectedInSessionHistograms) { |
| base::HistogramTester histogram_tester; |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| WaitForPaint(kDocumentWithNonAsciiCharacters); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify inner HTML was included as bytes in the the query. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data() |
| .size() == 2; |
| })); |
| |
| controller->OnZeroSuggestShownForTesting(); |
| |
| // Simulate a manual typed suggestion being entered. |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "red", AutocompleteMatchType::Type::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| std::map<std::string, std::string>()); |
| |
| // Issuing a search from the overlay state can only be done through the |
| // contextual searchbox and should result in a live page with results. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| |
| // Close the overlay and assert that the histogram was recorded once and |
| // that zps was shown in the session. |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| |
| // Assert used in session metrics get recorded. |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.SuggestionUsedInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.SuggestionUsedInSession", false, |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.ByPageContentType." |
| "AnnotatedPageContent." |
| "SuggestionUsedInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.ByPageContentType." |
| "AnnotatedPageContent." |
| "SuggestionUsedInSession", |
| false, /*expected_count=*/1); |
| auto entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders:: |
| Lens_Overlay_ContextualSuggest_ZPS_SuggestionUsedInSession:: |
| kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| auto* entry = entries[0].get(); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders:: |
| Lens_Overlay_ContextualSuggest_ZPS_SuggestionUsedInSession:: |
| kSuggestionUsedInSessionName, |
| false); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders:: |
| Lens_Overlay_ContextualSuggest_ZPS_SuggestionUsedInSession:: |
| kPageContentTypeName, |
| static_cast<int64_t>(lens::MimeType::kAnnotatedPageContent)); |
| |
| // Assert query issued in session metrics get recorded. |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.QueryIssuedInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.QueryIssuedInSession", true, |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.ByPageContentType.AnnotatedPageContent." |
| "QueryIssuedInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.ByPageContentType.AnnotatedPageContent." |
| "QueryIssuedInSession", |
| true, /*expected_count=*/1); |
| entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_ContextualSuggest_QueryIssuedInSession:: |
| kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| entry = entries[0].get(); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSuggest_QueryIssuedInSession:: |
| kQueryIssuedInSessionName, |
| true); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSuggest_QueryIssuedInSession:: |
| kPageContentTypeName, |
| static_cast<int64_t>(lens::MimeType::kAnnotatedPageContent)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| RecordContextualZpsNotShownInSessionHistograms) { |
| base::HistogramTester histogram_tester; |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| WaitForPaint(kDocumentWithNonAsciiCharacters); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify inner HTML was included as bytes in the the query. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data() |
| .size() == 2; |
| })); |
| |
| // Close the overlay and assert that the histogram was recorded once and |
| // that zps was not shown in the session. |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.ShownInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.ShownInSession", false, |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.ByPageContentType." |
| "AnnotatedPageContent.ShownInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.ZPS.ByPageContentType." |
| "AnnotatedPageContent." |
| "ShownInSession", |
| false, /*expected_count=*/1); |
| auto entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_ContextualSuggest_ZPS_ShownInSession:: |
| kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| auto* entry = entries[0].get(); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSuggest_ZPS_ShownInSession:: |
| kShownInSessionName, |
| false); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSuggest_ZPS_ShownInSession:: |
| kPageContentTypeName, |
| static_cast<int64_t>(lens::MimeType::kAnnotatedPageContent)); |
| |
| // Assert query issued in session metrics get recorded. |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.QueryIssuedInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.QueryIssuedInSession", false, |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.ByPageContentType.AnnotatedPageContent." |
| "QueryIssuedInSession", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.ByPageContentType.AnnotatedPageContent." |
| "QueryIssuedInSession", |
| false, /*expected_count=*/1); |
| entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_ContextualSuggest_QueryIssuedInSession:: |
| kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| entry = entries[0].get(); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSuggest_QueryIssuedInSession:: |
| kQueryIssuedInSessionName, |
| false); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSuggest_QueryIssuedInSession:: |
| kPageContentTypeName, |
| static_cast<int64_t>(lens::MimeType::kAnnotatedPageContent)); |
| } |
| |
| // TODO - crbug.com/400650442: Deflake and re-enable this test. |
| IN_PROC_BROWSER_TEST_F( |
| LensOverlayControllerBrowserTest, |
| DISABLED_RecordQueryIssuesBeforeZpsShownInSessionHistograms) { |
| base::HistogramTester histogram_tester; |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| WaitForPaint(kDocumentWithNonAsciiCharacters); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Show ZPS and issue a query. |
| controller->OnZeroSuggestShownForTesting(); |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "red", AutocompleteMatchType::Type::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| std::map<std::string, std::string>()); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| |
| // Close the overlay. |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| |
| // Verify the initial histogram was recorded. |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.InitialQuery." |
| "QueryIssuedInSessionBeforeSuggestShown", |
| false, |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.InitialQuery.ByPageContentType." |
| "AnnotatedPageContent." |
| "QueryIssuedInSessionBeforeSuggestShown", |
| false, |
| /*expected_count=*/1); |
| |
| // Verify the follow up histogram was not recorded. |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.FollowUpQuery." |
| "QueryIssuedInSessionBeforeSuggestShown", |
| /*expected_count=*/0); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSuggest.FollowUpQuery.ByPageContentType." |
| "AnnotatedPageContent.QueryIssuedInSessionBeforeSuggestShown", |
| /*expected_count=*/0); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Issue a search query before ZPS is shown. |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "red", AutocompleteMatchType::Type::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| std::map<std::string, std::string>()); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| |
| // Issue a follow up after ZPS is shown. |
| auto* test_side_panel_coordinator = |
| static_cast<lens::TestLensOverlaySidePanelCoordinator*>( |
| controller->results_side_panel_coordinator()); |
| int follow_up_query_issued_count = |
| test_side_panel_coordinator->side_panel_loading_set_to_true_; |
| |
| controller->OnZeroSuggestShownForTesting(); |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "red", AutocompleteMatchType::Type::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| std::map<std::string, std::string>()); |
| |
| // Run until the follow up query is issued. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return test_side_panel_coordinator->side_panel_loading_set_to_true_ == |
| follow_up_query_issued_count + 1; |
| })); |
| |
| // Close the overlay. |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| |
| // Verify the initial histogram was recorded. |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.InitialQuery." |
| "QueryIssuedInSessionBeforeSuggestShown", |
| true, |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.InitialQuery.ByPageContentType." |
| "AnnotatedPageContent." |
| "QueryIssuedInSessionBeforeSuggestShown", |
| true, |
| /*expected_count=*/1); |
| |
| // Verify the follow up histogram was recorded. |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.FollowUpQuery." |
| "QueryIssuedInSessionBeforeSuggestShown", |
| false, |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.FollowUpQuery.ByPageContentType." |
| "AnnotatedPageContent.QueryIssuedInSessionBeforeSuggestShown", |
| false, |
| /*expected_count=*/1); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Issue a search query. |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "red", AutocompleteMatchType::Type::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| std::map<std::string, std::string>()); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| |
| // Re-grab the new results side panel coordinator since it was destroyed after |
| // the overlay was turned off. |
| test_side_panel_coordinator = |
| static_cast<lens::TestLensOverlaySidePanelCoordinator*>( |
| controller->results_side_panel_coordinator()); |
| |
| // Issue a follow up before ZPS is shown. |
| follow_up_query_issued_count = |
| test_side_panel_coordinator->side_panel_loading_set_to_true_; |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "red", AutocompleteMatchType::Type::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| std::map<std::string, std::string>()); |
| |
| // Run until the follow up query is issued. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return test_side_panel_coordinator->side_panel_loading_set_to_true_ == |
| follow_up_query_issued_count + 1; |
| })); |
| |
| // Close the overlay. |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| |
| // Verify the follow up histogram was recorded. |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.FollowUpQuery." |
| "QueryIssuedInSessionBeforeSuggestShown", |
| true, |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.ContextualSuggest.FollowUpQuery.ByPageContentType." |
| "AnnotatedPageContent.QueryIssuedInSessionBeforeSuggestShown", |
| true, |
| /*expected_count=*/1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| ContextualQueryInBackStackRequest) { |
| WaitForPaint(kDocumentWithNonAsciiCharacters); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Make a searchbox query. |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "oranges", AutocompleteMatchType::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| /*additional_query_params=*/{}); |
| |
| // Wait for the side panel to load. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->GetSidePanelWebContentsForTesting(); })); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // Verify we entered the contextual searchbox flow. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| |
| // Make another query to build the history stack. |
| content::TestNavigationObserver observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "apples", AutocompleteMatchType::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| /*additional_query_params=*/{}); |
| |
| // Wait for the side panel to load. |
| observer.WaitForNavigationFinished(); |
| |
| // Reset mock query controller so we can verify the new request. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| fake_query_controller->ResetTestingState(); |
| |
| // Issue a new searchbox query. |
| controller->results_side_panel_coordinator()->PopAndLoadQueryFromHistory(); |
| |
| // Verify we entered the contextual searchbox flow. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->num_interaction_requests_sent() == 1; |
| })); |
| ASSERT_EQ(fake_query_controller->num_interaction_requests_sent(), 1); |
| ASSERT_EQ(fake_query_controller->last_queried_text(), "oranges"); |
| } |
| |
| // TODO(crbug.com/413042395): This test is not testing overlay logic, but |
| // instead the side panel logic. Therefore, this test should be moved to a side |
| // panel browsertest file. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| IssueTextSearchRequest_Contextualizes) { |
| WaitForPaint(kDocumentWithNonAsciiCharacters); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay by issuing a text search request without suppressing |
| // contextualization. |
| GetLensSearchController()->IssueTextSearchRequest( |
| LensOverlayInvocationSource::kContentAreaContextMenuText, "test", |
| /*additional_query_parameters=*/{}, |
| AutocompleteMatchType::Type::SEARCH_WHAT_YOU_TYPED, |
| /*is_zero_prefix_suggestion=*/false, |
| /*suppress_contextualization=*/false); |
| |
| // Wait for the side panel to load. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->GetSidePanelWebContentsForTesting(); })); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // Verify we entered the live page state. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| |
| // Make another query to build the history stack. |
| content::TestNavigationObserver observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "apples", AutocompleteMatchType::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| /*additional_query_params=*/{}); |
| |
| // Wait for the side panel to load. |
| observer.WaitForNavigationFinished(); |
| |
| // Pop last query from history. |
| controller->results_side_panel_coordinator()->PopAndLoadQueryFromHistory(); |
| observer.WaitForNavigationFinished(); |
| |
| // An interaction request should have been sent for each contextualized query. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->num_interaction_requests_sent() == 3; |
| })); |
| } |
| |
| // TODO(crbug.com/413042395): This test is not testing overlay logic, but |
| // instead the side panel logic. Therefore, this test should be moved to a side |
| // panel browsertest file. |
| // TODO(crbug.com/439622878): Test is flaky on Windows. |
| #if BUILDFLAG(IS_WIN) |
| #define MAYBE_IssueTextSearchRequest_SuppressesContextualization \ |
| DISABLED_IssueTextSearchRequest_SuppressesContextualization |
| #else |
| #define MAYBE_IssueTextSearchRequest_SuppressesContextualization \ |
| IssueTextSearchRequest_SuppressesContextualization |
| #endif |
| IN_PROC_BROWSER_TEST_F( |
| LensOverlayControllerBrowserTest, |
| MAYBE_IssueTextSearchRequest_SuppressesContextualization) { |
| WaitForPaint(kDocumentWithNonAsciiCharacters); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay by issuing a text search request and suppressing |
| // contextualization. |
| GetLensSearchController()->IssueTextSearchRequest( |
| LensOverlayInvocationSource::kContentAreaContextMenuText, "test", |
| /*additional_query_parameters=*/{}, |
| AutocompleteMatchType::Type::SEARCH_WHAT_YOU_TYPED, |
| /*is_zero_prefix_suggestion=*/false, |
| /*suppress_contextualization=*/true); |
| |
| // Wait for the side panel to load. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->GetSidePanelWebContentsForTesting(); })); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // Verify we entered the live page state. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| |
| // Make another query to build the history stack. |
| content::TestNavigationObserver observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "apples", AutocompleteMatchType::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| /*additional_query_params=*/{}); |
| |
| // Wait for the side panel to load. |
| observer.WaitForNavigationFinished(); |
| |
| // Pop last query from history. |
| controller->results_side_panel_coordinator()->PopAndLoadQueryFromHistory(); |
| observer.WaitForNavigationFinished(); |
| |
| // No interaction requests should have been sent as contextualization was |
| // suppressed. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| ASSERT_EQ(fake_query_controller->num_interaction_requests_sent(), 0); |
| } |
| |
| // This test checks that there is no crash when showing lens overlay when |
| // screenshot is not available. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| ScreenshotUnavailable) { |
| // Wait for the real page to finish painting. |
| WaitForPaint(); |
| |
| // Pretend like a screenshot isn't available. |
| auto* fake_controller = |
| static_cast<LensOverlayControllerFake*>(GetLensOverlayController()); |
| fake_controller->is_screenshot_possible_ = false; |
| |
| // Show the overlay. State should still be off. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(fake_controller->state(), State::kOff); |
| |
| // Background the tab. |
| chrome::AddTabAt(browser(), GURL(url::kAboutBlankURL), /*index=*/-1, |
| /*foreground=*/true); |
| |
| // There should be two tabs. Close the first tab. |
| // Regression testing for crbug.com/373767988. Ensure no crash when |
| // closing a tab that wasn't screenshotable. |
| ASSERT_EQ(browser()->tab_strip_model()->count(), 2); |
| browser()->tab_strip_model()->DetachAndDeleteWebContentsAt(/*index=*/0); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| PageUrlIncludedInFollowUpRequest) { |
| WaitForPaint(kDocumentWithNonAsciiCharacters); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Make a searchbox query. |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "oranges", AutocompleteMatchType::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| /*additional_query_params=*/{}); |
| |
| // Verify transitions to live page. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| |
| // Reset mock query controller so we can verify the new request. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| fake_query_controller->ResetTestingState(); |
| |
| // Change page. |
| WaitForPaint(kDocumentWithNamedElement); |
| |
| // Focus the searchbox. |
| controller->OnFocusChangedForTesting(true); |
| |
| // Issue a new searchbox query. |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "oranges", AutocompleteMatchType::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| /*additional_query_params=*/{}); |
| |
| // Verify inner text was updated. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return !fake_query_controller->last_sent_page_url().is_empty(); |
| })); |
| const GURL expected_url = |
| embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| ASSERT_EQ(expected_url, fake_query_controller->last_sent_page_url()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| NoBytesInNonCsbFollowUpRequest) { |
| WaitForPaint(kDocumentWithNonAsciiCharacters); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Issue a text seleciton request to get out of CSB flow. |
| controller->IssueTextSelectionRequestForTesting("testing", |
| /*selection_start_index=*/10, |
| /*selection_end_index=*/16); |
| |
| // Verify transitions to live page. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlayAndResults; })); |
| |
| // Reset mock query controller so we can verify the new request. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| fake_query_controller->ResetTestingState(); |
| |
| // Issue a new searchbox query. |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "oranges", AutocompleteMatchType::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| /*additional_query_params=*/{}); |
| |
| // Verify inner text was not updated, signified by the payload being empty. |
| EXPECT_THAT( |
| lens::Payload(), |
| EqualsProto(fake_query_controller->last_sent_page_content_payload())); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, ProtectedPageShows) { |
| base::HistogramTester histogram_tester; |
| WaitForPaint(); |
| |
| // There should be no histograms logged. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.SidePanelResultStatus", |
| /*expected_count=*/0); |
| |
| // Set the contextualization controller to return the page as not context |
| // eligible. |
| auto* fake_contextualization_controller = |
| static_cast<lens::TestLensSearchContextualizationController*>( |
| GetLensSearchController() |
| ->lens_search_contextualization_controller()); |
| ASSERT_TRUE(fake_contextualization_controller); |
| fake_contextualization_controller->SetContextEligible(true); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| // When the overlay is bound, it should start the query flow which returns a |
| // response for the full image callback. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Verify the error page histogram was not recorded since the result panel is |
| // not open. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.SidePanelResultStatus", |
| /*expected_count=*/0); |
| |
| // Side panel is not showing at first. |
| auto* coordinator = browser()->GetFeatures().side_panel_coordinator(); |
| EXPECT_FALSE(coordinator->IsSidePanelShowing()); |
| EXPECT_FALSE(controller->GetSidePanelWebContentsForTesting()); |
| |
| // Issuing a request should show the side panel even if navigation is expected |
| // to fail. |
| controller->IssueTextSelectionRequestForTesting("test query", |
| /*selection_start_index=*/0, |
| /*selection_end_index=*/0); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // Expect the Lens Overlay results panel to open. |
| ASSERT_TRUE(coordinator->IsSidePanelShowing()); |
| EXPECT_EQ(coordinator->GetCurrentEntryId(), |
| SidePanelEntry::Id::kLensOverlayResults); |
| |
| // The recorded histogram should be a normal result shown. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.SidePanelResultStatus", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount("Lens.Overlay.SidePanelResultStatus", |
| lens::SidePanelResultStatus::kResultShown, |
| /*expected_count=*/1); |
| } |
| |
| class LensOverlayControllerIframeBrowserTest |
| : public LensOverlayControllerBrowserTest { |
| void SetUp() override { |
| // Register a request handler to close the socket. This should result in an |
| // ERR_EMPTY_RESPONSE, which is not a special-cased error. Used for the |
| // SidePanelIframeLoadOtherError test case. The `test` query parameter is |
| // used to trigger this handler. |
| embedded_test_server()->RegisterRequestHandler(base::BindRepeating( |
| [](const net::test_server::HttpRequest& request) |
| -> std::unique_ptr<net::test_server::HttpResponse> { |
| if (base::StartsWith(request.relative_url, kDocumentWithNamedElement, |
| base::CompareCase::SENSITIVE)) { |
| std::string fail_query_param; |
| net::GetValueForKeyInQuery(request.GetURL(), "fail", |
| &fail_query_param); |
| if (fail_query_param == "invalid-headers") { |
| return std::make_unique<net::test_server::RawHttpResponse>( |
| "invalid-headers", ""); |
| } |
| } |
| return nullptr; |
| })); |
| |
| LensOverlayControllerBrowserTest::SetUp(); |
| } |
| |
| protected: |
| void SetupFeatureList() override { |
| // Set the results search URL to the test server URL so that the iframe |
| // navigations are allowed by the iframe CORS policy and the navigation |
| // throttle. |
| feature_list_.InitAndEnableFeatureWithParameters( |
| lens::features::kLensOverlay, |
| { |
| {"results-search-url", |
| embedded_test_server()->GetURL(kDocumentWithNamedElement).spec()}, |
| }); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerIframeBrowserTest, |
| SidePanelIframeLoadSuccess) { |
| base::HistogramTester histogram_tester; |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Open the side panel. |
| controller->OpenSidePanelForTesting(); |
| ASSERT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // Navigate the iframe to a successful URL. |
| GURL url(embedded_test_server()->GetURL(kDocumentWithNamedElement)); |
| content::TestNavigationObserver navigation_observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting( |
| url); |
| navigation_observer.WaitForNavigationFinished(); |
| |
| // Check histogram. The enum is defined in the .cc file so we can't reference |
| // it directly. |
| histogram_tester.ExpectUniqueSample("Lens.Overlay.SidePanel.IframeLoadStatus", |
| /*IframeLoadStatus::kSuccess=*/0, 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerIframeBrowserTest, |
| SidePanelIframeLoadConnectionRefused) { |
| base::HistogramTester histogram_tester; |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Open the side panel. |
| controller->OpenSidePanelForTesting(); |
| ASSERT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // Create a URL and then shut down the server to force a connection refused |
| // error when the iframe tries to load the URL. |
| GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete()); |
| |
| // Navigate the iframe to the connection refused URL. |
| content::TestNavigationObserver navigation_observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting( |
| url); |
| navigation_observer.WaitForNavigationFinished(); |
| |
| // Check histogram. |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.SidePanel.IframeLoadStatus", |
| /*IframeLoadStatus::kFailedConnectionRefused=*/1, 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerIframeBrowserTest, |
| SidePanelIframeLoadOtherError) { |
| base::HistogramTester histogram_tester; |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Open the side panel. |
| controller->OpenSidePanelForTesting(); |
| ASSERT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // Navigate the iframe to the URL with a query parameter. This will trigger |
| // the request handler that will close the socket. |
| GURL url = net::AppendOrReplaceQueryParameter( |
| embedded_test_server()->GetURL(kDocumentWithNamedElement), /*key=*/"fail", |
| /*value=*/"invalid-headers"); |
| content::TestNavigationObserver navigation_observer( |
| controller->GetSidePanelWebContentsForTesting()); |
| controller->results_side_panel_coordinator()->LoadURLInResultsFrameForTesting( |
| url); |
| navigation_observer.WaitForNavigationFinished(); |
| |
| // Check histogram. The enum is defined in the .cc file so we can't reference |
| // it directly. |
| histogram_tester.ExpectUniqueSample("Lens.Overlay.SidePanel.IframeLoadStatus", |
| /*IframeLoadStatus::kFailedOther=*/6, 1); |
| } |
| |
| class LensOverlayControllerInnerTextEnabledSmallByteLimitTest |
| : public LensOverlayControllerBrowserTest { |
| protected: |
| void SetupFeatureList() override { |
| feature_list_.InitAndEnableFeatureWithParameters( |
| lens::features::kLensOverlayContextualSearchbox, |
| { |
| {"use-inner-text-as-context", "true"}, |
| {"use-apc-as-context", "false"}, |
| {"file-upload-limit-bytes", "10"}, |
| }); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerInnerTextEnabledSmallByteLimitTest, |
| InnerTextOverLimitNotIncludedInRequest) { |
| base::HistogramTester histogram_tester; |
| |
| WaitForPaint(kDocumentWithNonAsciiCharacters); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify innerText bytes were not included in the query. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| EXPECT_THAT( |
| lens::Payload(), |
| EqualsProto(fake_query_controller->last_sent_page_content_payload())); |
| |
| // Verify the searchbox was shown. The CSB should be shown even if the inner |
| // text is not included in the request. The server will handle what to show in |
| // the searchbox based on the page content that is received. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| EXPECT_TRUE(fake_controller->fake_overlay_page_ |
| .last_received_should_show_contextual_searchbox_); |
| |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| |
| // Verify the histogram recorded the searchbox was shown. |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.ContextualSearchBox.ByPageContentType.PlainText." |
| "ShownInSession", |
| /*sample*/ true, |
| /*expected_bucket_count=*/1); |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.ContextualSearchBox.ByDocumentType.Html." |
| "ShownInSession", |
| /*sample*/ true, |
| /*expected_bucket_count=*/1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| PageUrlIncludedInRequest) { |
| WaitForPaint(kDocumentWithNonAsciiCharacters); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify the page url was included as bytes in the the query. |
| const GURL expected_url = |
| embedded_test_server()->GetURL(kDocumentWithNonAsciiCharacters); |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->last_sent_page_url() == expected_url; |
| })); |
| } |
| |
| class LensOverlayControllerApcOnlyTest |
| : public LensOverlayControllerBrowserTest { |
| protected: |
| void SetupFeatureList() override { |
| feature_list_.InitAndEnableFeatureWithParameters( |
| lens::features::kLensOverlayContextualSearchbox, |
| { |
| {"use-inner-text-as-context", "false"}, |
| {"use-apc-as-context", "true"}, |
| }); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerApcOnlyTest, |
| RecordSearchboxShownInSessionHistogramsByDocumentType) { |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| base::HistogramTester histogram_tester; |
| |
| // Navigate to an image, and invoke then close the overlay. |
| WaitForPaint(kImageFile); |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| |
| // Navigate to a audio file, and invoke then close the overlay. |
| WaitForPaint(kAudioFile); |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| |
| // Navigate to a video file, and invoke then close the overlay. |
| WaitForPaint(kVideoFile); |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| |
| // Navigate to a JSON file, and invoke then close the overlay. |
| WaitForPaint(kJsonFile); |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| |
| // Verify histograms were recorded. |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.ContextualSearchBox.ByDocumentType.Image.ShownInSession", |
| /*sample*/ true, |
| /*expected_bucket_count=*/1); |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.ContextualSearchBox.ByDocumentType.Video.ShownInSession", |
| /*sample*/ true, |
| /*expected_bucket_count=*/1); |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.ContextualSearchBox.ByDocumentType.Audio.ShownInSession", |
| /*sample*/ true, |
| /*expected_bucket_count=*/1); |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.ContextualSearchBox.ByDocumentType.Json.ShownInSession", |
| /*sample*/ true, |
| /*expected_bucket_count=*/1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserTest, |
| RecordSearchboxFocusHistograms) { |
| base::HistogramTester histogram_tester; |
| |
| // Navigate to a webpage, and invoke then close the overlay. |
| WaitForPaint(); |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Focus the searchbox. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| fake_controller->OnFocusChangedForTesting(/*focused=*/true); |
| |
| // Verify histogram was recorded. |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSearchBox.ByPageContentType.AnnotatedPageContent." |
| "TimeFromInvocationToFirstFocus", |
| /*expected_count=*/1); |
| |
| // Focusing the searchbox again should not record another histogram. |
| fake_controller->OnFocusChangedForTesting(/*focused=*/true); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSearchBox.ByPageContentType.AnnotatedPageContent." |
| "TimeFromInvocationToFirstFocus", |
| /*expected_count=*/1); |
| |
| // Make a searchbox query to open the live page and side panel. |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "oranges", AutocompleteMatchType::SEARCH_SUGGEST, |
| /*is_zero_prefix_suggestion=*/false, |
| /*additional_query_params=*/{}); |
| |
| // Verify transitions to live page. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| |
| // Change page to new URL. |
| WaitForPaint(kDocumentWithImage); |
| |
| // Focus the searchbox again and verify histogram was recorded. |
| fake_controller->OnFocusChangedForTesting(/*focused=*/true); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSearchBox.ByPageContentType.AnnotatedPageContent." |
| "TimeFromNavigationToFirstFocus", |
| /*expected_count=*/1); |
| |
| // Focusing the searchbox again should not record another histogram. |
| fake_controller->OnFocusChangedForTesting(/*focused=*/true); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSearchBox.ByPageContentType.AnnotatedPageContent." |
| "TimeFromNavigationToFirstFocus", |
| /*expected_count=*/1); |
| |
| // Navigate to a new page and verify histogram was recorded. |
| WaitForPaint(kImageFile); |
| fake_controller->OnFocusChangedForTesting(/*focused=*/true); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ContextualSearchBox.ByPageContentType.AnnotatedPageContent." |
| "TimeFromNavigationToFirstFocus", |
| /*expected_count=*/2); |
| } |
| |
| class LensOverlayControllerInnerTextAndApc |
| : public LensOverlayControllerBrowserTest { |
| protected: |
| void SetupFeatureList() override { |
| feature_list_.InitWithFeaturesAndParameters( |
| {{lens::features::kLensOverlayContextualSearchbox, |
| { |
| {"send-page-url-for-contextualization", "true"}, |
| {"use-inner-text-as-context", "true"}, |
| {"use-apc-as-context", "true"}, |
| {"use-updated-content-fields", "true"}, |
| }}, |
| {lens::features::kLensSearchProtectedPage, {}}}, |
| {}); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerInnerTextAndApc, |
| AllBytesInRequest) { |
| WaitForPaint(kDocumentWithNonAsciiCharacters); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify inner HTML was included as bytes in the the query. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| // Run until the page content is sent. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data() |
| .size() == 2; |
| })); |
| |
| // Expect the old content data fields to be empty. |
| EXPECT_TRUE(fake_query_controller->last_sent_page_content_payload() |
| .content_data() |
| .empty()); |
| EXPECT_TRUE(fake_query_controller->last_sent_page_content_payload() |
| .content_type() |
| .empty()); |
| |
| // There should be 2 fields of content data. |
| const auto last_sent_content = |
| fake_query_controller->last_sent_page_content_payload().content(); |
| ASSERT_EQ(last_sent_content.content_data().size(), 2); |
| |
| // Verify innerText is what we expect it to be. |
| EXPECT_EQ(lens::ContentData::CONTENT_TYPE_INNER_TEXT, |
| last_sent_content.content_data(0).content_type()); |
| const auto last_sent_text_bytes = last_sent_content.content_data(0).data(); |
| ASSERT_EQ( |
| "The below are non-ascii characters.\n\nこんにちは thêrē 🐶 ©", |
| std::string(last_sent_text_bytes.begin(), last_sent_text_bytes.end())); |
| |
| // Verify APC is what we expect it to be. |
| EXPECT_EQ(lens::ContentData::CONTENT_TYPE_ANNOTATED_PAGE_CONTENT, |
| last_sent_content.content_data(1).content_type()); |
| const auto last_sent_apc_bytes = last_sent_content.content_data(1).data(); |
| optimization_guide::proto::AnnotatedPageContent apc; |
| ASSERT_TRUE(apc.ParseFromString( |
| std::string(last_sent_apc_bytes.begin(), last_sent_apc_bytes.end()))); |
| EXPECT_EQ("The below are non-ascii characters.", apc.root_node() |
| .children_nodes(0) |
| .children_nodes(0) |
| .content_attributes() |
| .text_data() |
| .text_content()); |
| EXPECT_EQ("こんにちは thêrē 🐶 ©", apc.root_node() |
| .children_nodes(1) |
| .children_nodes(0) |
| .content_attributes() |
| .text_data() |
| .text_content()); |
| |
| // Verify the searchbox was shown. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| EXPECT_TRUE(fake_controller->fake_overlay_page_ |
| .last_received_should_show_contextual_searchbox_); |
| } |
| |
| // TODO(crbug.com/422479353): This test seems to be too slow on Windows ASAN. |
| #if BUILDFLAG(IS_WIN) && defined(ADDRESS_SANITIZER) |
| #define MAYBE_PageContentTypeHistograms DISABLED_PageContentTypeHistograms |
| #else |
| #define MAYBE_PageContentTypeHistograms PageContentTypeHistograms |
| #endif |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerInnerTextAndApc, |
| MAYBE_PageContentTypeHistograms) { |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder; |
| base::HistogramTester histogram_tester; |
| |
| WaitForPaint(kDocumentWithNonAsciiCharacters); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Setup fake text in the OCR response. Included 4 words on the DOM, and 1 |
| // not, to make a similarity score of 0.8. Also include some random characters |
| // to make sure they are ignored. |
| auto* fake_controller = |
| static_cast<LensSearchControllerFake*>(GetLensSearchController()); |
| fake_controller->SetOcrResponseWords( |
| {"The.", " below - ", " ,are] ", "RANDOM", "\n\n\nCharacters.\n"}); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| CloseOverlayAndWaitForOff(controller, |
| LensOverlayDismissalSource::kOverlayCloseButton); |
| |
| // This histogram is async so run until it is recorded. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return histogram_tester.GetBucketCount( |
| "Lens.Overlay.ByPageContentType.AnnotatedPageContent." |
| "DocumentSize2", |
| 0) == 1; |
| })); |
| |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.ByDocumentType.Html.Invoked", |
| /*sample*/ true, |
| /*expected_bucket_count=*/1); |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.ContextualSearchBox.ShownInSession", |
| /*sample*/ true, |
| /*expected_bucket_count=*/1); |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.ContextualSearchBox.ByPageContentType.AnnotatedPageContent." |
| "ShownInSession", |
| /*sample*/ true, |
| /*expected_bucket_count=*/1); |
| histogram_tester.ExpectUniqueSample( |
| "Lens.Overlay.ContextualSearchBox.ByDocumentType.Html." |
| "ShownInSession", |
| /*sample*/ true, |
| /*expected_bucket_count=*/1); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ByPageContentType.AnnotatedPageContent.DocumentSize2", |
| /*expected_count=*/1); |
| histogram_tester.ExpectTotalCount( |
| "Lens.Overlay.ByPageContentType.PlainText.DocumentSize2", |
| /*expected_count=*/1); |
| |
| // Verify UKM metrics were recorded. |
| auto entries = test_ukm_recorder.GetEntriesByName( |
| ukm::builders::Lens_Overlay_ContextualSearchBox_ShownInSession:: |
| kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| auto* entry = entries[0].get(); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSearchBox_ShownInSession:: |
| kWasShownName, |
| true); |
| test_ukm_recorder.ExpectEntryMetric( |
| entry, |
| ukm::builders::Lens_Overlay_ContextualSearchBox_ShownInSession:: |
| kPageContentTypeName, |
| static_cast<int64_t>(lens::MimeType::kAnnotatedPageContent)); |
| |
| // This histogram is async so run until it is recorded. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return histogram_tester.GetBucketCount("Lens.Overlay.OcrDomSimilarity", |
| 80) == 1; |
| })); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerInnerTextAndApc, |
| PageNotContextEligibleError) { |
| base::HistogramTester histogram_tester; |
| WaitForPaint(kDocumentWithNonAsciiCharacters); |
| |
| // There should be no histograms logged. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.SidePanelResultStatus", |
| /*expected_count=*/0); |
| |
| // Set the contextualization controller to return the page as not context |
| // eligible. |
| auto* fake_contextualization_controller = |
| static_cast<lens::TestLensSearchContextualizationController*>( |
| GetLensSearchController() |
| ->lens_search_contextualization_controller()); |
| ASSERT_TRUE(fake_contextualization_controller); |
| fake_contextualization_controller->SetContextEligible(false); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| // When the overlay is bound, it should start the query flow which returns a |
| // response for the full image callback. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Verify the error page histogram was not recorded since the result panel is |
| // not open. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.SidePanelResultStatus", |
| /*expected_count=*/0); |
| |
| // Side panel is not showing at first. |
| auto* coordinator = browser()->GetFeatures().side_panel_coordinator(); |
| EXPECT_FALSE(coordinator->IsSidePanelShowing()); |
| EXPECT_FALSE(controller->GetSidePanelWebContentsForTesting()); |
| |
| // Issuing a request should show the side panel even if navigation is expected |
| // to fail. |
| controller->IssueTextSelectionRequestForTesting("test query", |
| /*selection_start_index=*/0, |
| /*selection_end_index=*/0); |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // Expect the Lens Overlay results panel to open. |
| ASSERT_TRUE(coordinator->IsSidePanelShowing()); |
| EXPECT_EQ(coordinator->GetCurrentEntryId(), |
| SidePanelEntry::Id::kLensOverlayResults); |
| |
| // No page data or screenshot should have been sent. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| const auto last_sent_content = |
| fake_query_controller->last_sent_page_content_payload().content(); |
| EXPECT_EQ(last_sent_content.content_data().size(), 0); |
| EXPECT_TRUE(last_sent_content.webpage_url().empty()); |
| EXPECT_TRUE(last_sent_content.webpage_title().empty()); |
| |
| // The recorded histogram should be a protected error page being shown. |
| histogram_tester.ExpectTotalCount("Lens.Overlay.SidePanelResultStatus", |
| /*expected_count=*/1); |
| histogram_tester.ExpectBucketCount( |
| "Lens.Overlay.SidePanelResultStatus", |
| lens::SidePanelResultStatus::kErrorPageShownProtected, |
| /*expected_count=*/1); |
| } |
| |
| class LensOverlayControllerContextualFeaturesDisabledTest |
| : public LensOverlayControllerBrowserTest { |
| protected: |
| void SetupFeatureList() override { |
| feature_list_.InitWithFeatures( |
| /*enabled_features=*/{}, |
| /*disabled_features=*/{ |
| lens::features::kLensOverlayContextualSearchbox}); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerContextualFeaturesDisabledTest, |
| PreselectionToastShows) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Preselection toast should be visible when the overlay is showing and is in |
| // the kOverlay state. |
| auto* preselection_widget = controller->get_preselection_widget_for_testing(); |
| ASSERT_TRUE(preselection_widget->IsVisible()); |
| } |
| |
| // TODO(crbug.com/360161233): This test is flaky. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerContextualFeaturesDisabledTest, |
| PreselectionToastDisappearsOnSelection) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // We need to flush the mojo receiver calls to make sure the screenshot was |
| // passed back to the WebUI or else the region selection UI will not render. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| fake_controller->FlushForTesting(); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Preselection toast should be visible when the overlay is showing and is in |
| // the kOverlay state. |
| auto* preselection_widget = controller->get_preselection_widget_for_testing(); |
| ASSERT_TRUE(preselection_widget->IsVisible()); |
| |
| // Simulate mouse events on the overlay for drawing a manual region. |
| gfx::Point center = |
| GetOverlayWebContents()->GetContainerBounds().CenterPoint(); |
| gfx::Point off_center = gfx::Point(center); |
| off_center.Offset(100, 100); |
| SimulateLeftClickDrag(center, off_center); |
| |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlayAndResults; })); |
| // Must explicitly get preselection bubble from controller. |
| ASSERT_EQ(controller->get_preselection_widget_for_testing(), nullptr); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerContextualFeaturesDisabledTest, |
| PreselectionToastOmniboxFocusState) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // We need to flush the mojo receiver calls to make sure the screenshot was |
| // passed back to the WebUI or else the region selection UI will not render. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| fake_controller->FlushForTesting(); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Preselection toast should be visible when the overlay is showing and is in |
| // the kOverlay state. |
| auto* preselection_widget = controller->get_preselection_widget_for_testing(); |
| ASSERT_TRUE(preselection_widget->IsVisible()); |
| |
| // Focus the location bar. |
| browser()->window()->GetLocationBar()->FocusLocation(false); |
| |
| // Must explicitly get preselection bubble from controller. Widget should be |
| // hidden when omnibox has focus. |
| ASSERT_FALSE(controller->get_preselection_widget_for_testing()->IsVisible()); |
| |
| // Move focus away from omnibox to the overlay web view. |
| controller->GetOverlayWebViewForTesting()->RequestFocus(); |
| |
| // Widget should be visible when web view receives focus and overlay is open. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return controller->get_preselection_widget_for_testing()->IsVisible(); |
| })); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerContextualFeaturesDisabledTest, |
| NoUnderlyingContentBytesInRequest) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Open the overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify no bytes were excluded from the query. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| EXPECT_THAT( |
| lens::Payload(), |
| EqualsProto(fake_query_controller->last_sent_page_content_payload())); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerContextualFeaturesDisabledTest, |
| PermissionBubble_Accept) { |
| WaitForPaint(); |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Disallow sharing the page screenshot. |
| PrefService* prefs = browser()->profile()->GetPrefs(); |
| prefs->SetBoolean(lens::prefs::kLensSharingPageScreenshotEnabled, false); |
| prefs->SetBoolean(lens::prefs::kLensSharingPageContentEnabled, false); |
| ASSERT_FALSE(lens::CanSharePageScreenshotWithLensOverlay(prefs)); |
| ASSERT_FALSE(lens::CanSharePageContentWithLensOverlay(prefs)); |
| |
| // Verify attempting to show the UI will show the permission bubble. |
| views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{}, |
| lens::kLensPermissionDialogName); |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| // State should remain off. |
| ASSERT_EQ(controller->state(), State::kOff); |
| auto* bubble_widget = waiter.WaitIfNeededAndGet(); |
| // Wait for the bubble to become visible. |
| views::test::WidgetVisibleWaiter(bubble_widget).Wait(); |
| ASSERT_TRUE(bubble_widget->IsVisible()); |
| |
| auto* search_controller = GetLensSearchController(); |
| ASSERT_TRUE( |
| search_controller->get_lens_permission_bubble_controller_for_testing() |
| ->HasOpenDialogWidget()); |
| |
| // Verify attempting to show the UI again does not close the bubble widget. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| // State should remain off. |
| ASSERT_EQ(controller->state(), State::kOff); |
| ASSERT_TRUE(bubble_widget->IsVisible()); |
| ASSERT_TRUE( |
| search_controller->get_lens_permission_bubble_controller_for_testing() |
| ->HasOpenDialogWidget()); |
| |
| // Simulate click on the accept button. |
| auto* bubble_widget_delegate = |
| bubble_widget->widget_delegate()->AsBubbleDialogDelegate(); |
| ClickBubbleDialogButton(bubble_widget_delegate, |
| bubble_widget_delegate->GetOkButton()); |
| ASSERT_FALSE( |
| search_controller->get_lens_permission_bubble_controller_for_testing() |
| ->HasOpenDialogWidget()); |
| |
| // Verify sharing the page screenshot is now permitted. |
| ASSERT_TRUE(lens::CanSharePageScreenshotWithLensOverlay(prefs)); |
| // Page content should still not be able to be shared when CSB isn't enabled. |
| ASSERT_FALSE(lens::CanSharePageContentWithLensOverlay(prefs)); |
| |
| // Verify accepting the permission bubble will eventually result in the |
| // overlay state. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Verify screenshot was captured and stored. |
| auto screenshot_bitmap = controller->initial_screenshot(); |
| EXPECT_FALSE(screenshot_bitmap.empty()); |
| screenshot_bitmap = controller->updated_screenshot(); |
| EXPECT_FALSE(screenshot_bitmap.empty()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerContextualFeaturesDisabledTest, |
| PermissionBubble_Reject) { |
| WaitForPaint(); |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Disallow sharing the page screenshot. |
| PrefService* prefs = browser()->profile()->GetPrefs(); |
| prefs->SetBoolean(lens::prefs::kLensSharingPageScreenshotEnabled, false); |
| ASSERT_FALSE(lens::CanSharePageScreenshotWithLensOverlay(prefs)); |
| |
| // Verify attempting to show the UI will show the permission bubble. |
| views::NamedWidgetShownWaiter waiter(views::test::AnyWidgetTestPasskey{}, |
| lens::kLensPermissionDialogName); |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| // State should remain off. |
| ASSERT_EQ(controller->state(), State::kOff); |
| auto* bubble_widget = waiter.WaitIfNeededAndGet(); |
| // Wait for the bubble to become visible. |
| views::test::WidgetVisibleWaiter(bubble_widget).Wait(); |
| ASSERT_TRUE(bubble_widget->IsVisible()); |
| |
| auto* search_controller = GetLensSearchController(); |
| ASSERT_TRUE( |
| search_controller->get_lens_permission_bubble_controller_for_testing() |
| ->HasOpenDialogWidget()); |
| |
| // Verify attempting to show the UI again does not close the bubble widget. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| // State should remain off. |
| ASSERT_EQ(controller->state(), State::kOff); |
| ASSERT_TRUE(bubble_widget->IsVisible()); |
| ASSERT_TRUE( |
| search_controller->get_lens_permission_bubble_controller_for_testing() |
| ->HasOpenDialogWidget()); |
| |
| // Simulate click on the reject button. |
| auto* bubble_widget_delegate = |
| bubble_widget->widget_delegate()->AsBubbleDialogDelegate(); |
| ClickBubbleDialogButton(bubble_widget_delegate, |
| bubble_widget_delegate->GetCancelButton()); |
| ASSERT_FALSE( |
| search_controller->get_lens_permission_bubble_controller_for_testing() |
| ->HasOpenDialogWidget()); |
| |
| // Verify sharing the page screenshot is still not permitted. |
| ASSERT_FALSE(lens::CanSharePageScreenshotWithLensOverlay(prefs)); |
| } |
| |
| class LensOverlayControllerOverlaySearchbox |
| : public LensOverlayControllerBrowserTest { |
| protected: |
| void SetupFeatureList() override { |
| feature_list_.InitWithFeatures( |
| /*enabled_features=*/{lens::features::kLensOverlay, |
| lens::features::kLensOverlayContextualSearchbox}, |
| /*disabled_features=*/{}); |
| } |
| |
| void VerifyContextualSearchQueryParameters(const GURL& url_to_process) { |
| EXPECT_THAT(url_to_process.spec(), |
| testing::MatchesRegex(std::string(kResultsSearchBaseUrl) + |
| ".*source=chrome.cr.menu.*&vit=.*&gsc=2&" |
| "hl=.*&q=.*&biw=\\d+&bih=\\d+")); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerOverlaySearchbox, |
| OverlaySearchboxPageClassificationAndState) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| // Searchbox should start in contextual mode if we are in the overlay state. |
| EXPECT_EQ(controller->GetPageClassificationForTesting(), |
| metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX); |
| |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "hello", AutocompleteMatchType::Type::SEARCH_WHAT_YOU_TYPED, |
| /*is_zero_prefix_suggestion=*/false, |
| std::map<std::string, std::string>()); |
| |
| // Issuing a search from the overlay state can only be done through the |
| // contextual searchbox and should result in a live page with results. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| EXPECT_EQ(controller->GetPageClassificationForTesting(), |
| metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerOverlaySearchbox, |
| OverlaySearchboxCorrectResultsUrl) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Wait for the bytes to be uploaded before issuing the request. |
| auto* fake_query_controller = |
| static_cast<lens::TestLensOverlayQueryController*>( |
| controller->get_lens_overlay_query_controller_for_testing()); |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return fake_query_controller->last_sent_page_content_payload() |
| .content() |
| .content_data() |
| .size() != 0; |
| })); |
| |
| // Verify searchbox is in contextual mode. |
| EXPECT_EQ(controller->GetPageClassificationForTesting(), |
| metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX); |
| |
| controller->IssueSearchBoxRequestForTesting( |
| kTestTime, "hello", AutocompleteMatchType::Type::SEARCH_WHAT_YOU_TYPED, |
| /*is_zero_prefix_suggestion=*/false, |
| std::map<std::string, std::string>()); |
| |
| // Wait for the side panel to open. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kLivePageAndResults; })); |
| EXPECT_EQ(controller->GetPageClassificationForTesting(), |
| metrics::OmniboxEventProto::CONTEXTUAL_SEARCHBOX); |
| |
| // Wait for URL to load in side panel. |
| EXPECT_TRUE(content::WaitForLoadStop( |
| controller->GetSidePanelWebContentsForTesting())); |
| |
| // Verify the query and params are set. |
| auto loaded_search_query = GetLoadedSearchQuery(); |
| EXPECT_TRUE(loaded_search_query); |
| EXPECT_EQ(loaded_search_query->search_query_text_, "hello"); |
| VerifyContextualSearchQueryParameters(loaded_search_query->search_query_url_); |
| } |
| |
| class LensOverlayControllerIPHBrowserTest |
| : public LensOverlayControllerBrowserTest { |
| protected: |
| void SetupFeatureList() override { |
| feature_list_.InitWithFeaturesAndParameters( |
| {{lens::features::kLensOverlay, {}}, |
| {feature_engagement::kIPHLensOverlayFeature, |
| { |
| {"x_url_allow_filters", |
| "[\"a.com\",\"b.com\",\"c.com/path\",\"d.com/path\"]"}, |
| {"x_url_block_filters", "[\"a.com/login\",\"d.com\"]"}, |
| }}}, |
| /*disabled_features=*/{}); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerIPHBrowserTest, |
| IsUrlEligibleForTutorialIPH) { |
| WaitForPaint(); |
| |
| auto* controller = GetLensOverlayController(); |
| EXPECT_TRUE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.a.com/"))); |
| EXPECT_FALSE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.a.com/login/path?key=param"))); |
| EXPECT_TRUE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.b.com/page?key=param"))); |
| EXPECT_FALSE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.c.com/"))); |
| EXPECT_TRUE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.c.com/path/path"))); |
| // Blocks override allows. |
| EXPECT_FALSE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.d.com/path"))); |
| } |
| |
| class LensOverlayControllerIPHWithPathMatchBrowserTest |
| : public LensOverlayControllerBrowserTest { |
| protected: |
| void SetupFeatureList() override { |
| feature_list_.InitWithFeaturesAndParameters( |
| {{lens::features::kLensOverlay, {}}, |
| {feature_engagement::kIPHLensOverlayFeature, |
| { |
| {"x_url_allow_filters", "[\"*\"]"}, |
| {"x_url_block_filters", "[\"a.com/login\",\"d.com\",\"e.edu\"]"}, |
| {"x_url_path_match_allow_patterns", |
| "[\"assignment\",\"homework\"]"}, |
| {"x_url_path_match_block_patterns", "[\"tutor\"]"}, |
| {"x_url_forced_allowed_match_patterns", "[\"edu/.+\"]"}, |
| }}}, |
| /*disabled_features=*/{}); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerIPHWithPathMatchBrowserTest, |
| IsUrlEligibleForTutorialIPH) { |
| WaitForPaint(); |
| |
| auto* controller = GetLensOverlayController(); |
| // Path must match. |
| EXPECT_FALSE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.a.com/"))); |
| EXPECT_TRUE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.a.com/assignment"))); |
| EXPECT_TRUE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.a.com/homework"))); |
| // Match can be in any part of path. |
| EXPECT_TRUE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.b.com/1/anassignmentpage/2"))); |
| EXPECT_TRUE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.c.com/your-homework-problem"))); |
| // Match is on path, not on domain. |
| EXPECT_FALSE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.homework.com/"))); |
| // Match is on path, not on query. |
| EXPECT_FALSE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.c.com/path?assignment=1"))); |
| EXPECT_FALSE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.c.com/path?query=homework"))); |
| // Match is on path, not on fragment. |
| EXPECT_FALSE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.c.com/path#assignment1"))); |
| // Block patterns take precedence over allow patterns. |
| EXPECT_FALSE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.a.com/tutor/assignment"))); |
| // x_url_block_filters takes precedence over path matches. |
| EXPECT_FALSE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.a.com/login/assignments"))); |
| EXPECT_FALSE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.d.com/homework"))); |
| |
| // x_url_forced_allowed_match_patterns is blocked by the block url filters. |
| EXPECT_FALSE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.e.edu/tutor"))); |
| // x_url_forced_allowed_match_patterns is blocked by the block path filters. |
| EXPECT_FALSE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.d.edu/tutor"))); |
| // x_url_forced_allowed_match_patterns skips the allowed path filters. |
| EXPECT_TRUE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.d.edu/something"))); |
| // URL not in x_url_forced_allowed_match_patterns and not in allowed path |
| // filters. |
| EXPECT_FALSE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.d.edu/"))); |
| // URL in x_url_forced_allowed_match_patterns and in allowed path filters. |
| EXPECT_TRUE(controller->IsUrlEligibleForTutorialIPHForTesting( |
| GURL("https://www.d.edu/homework"))); |
| } |
| |
| class LensOverlayControllerBrowserSimplifiedSelectionTest |
| : public LensOverlayControllerBrowserTest { |
| protected: |
| void SetupFeatureList() override { |
| feature_list_.InitWithFeaturesAndParameters( |
| {{lens::features::kLensOverlay, |
| {{"results-search-url", kResultsSearchBaseUrl}, |
| {"use-dynamic-theme", "true"}, |
| {"use-dynamic-theme-min-population-pct", "0.002"}, |
| {"use-dynamic-theme-min-chroma", "3.0"}}}, |
| {lens::features::kLensOverlayContextualSearchbox, |
| { |
| {"send-page-url-for-contextualization", "true"}, |
| {"use-inner-text-as-context", "true"}, |
| }}, |
| {lens::features::kLensOverlaySurvey, {}}, |
| {lens::features::kLensOverlaySidePanelOpenInNewTab, {}}, |
| {lens::features::kLensOverlaySimplifiedSelection, {}}}, |
| /*disabled_features=*/{}); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerBrowserSimplifiedSelectionTest, |
| HandleStartQueryResponse) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Before showing the UI, there should be no set objects or text as |
| // no query flow has started. |
| auto* fake_controller = static_cast<LensOverlayControllerFake*>(controller); |
| ASSERT_TRUE(fake_controller); |
| EXPECT_TRUE( |
| fake_controller->fake_overlay_page_.last_received_objects_.empty()); |
| EXPECT_FALSE(fake_controller->fake_overlay_page_.last_received_text_); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| // When the overlay is bound, it should start the query flow which returns a |
| // response for the full image callback. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Prevent flakiness by flushing the tasks. |
| fake_controller->FlushForTesting(); |
| |
| // After flushing the mojo calls, the data should be present. |
| EXPECT_FALSE( |
| fake_controller->fake_overlay_page_.last_received_objects_.empty()); |
| |
| // Only objects should have been sent to the overlay from the full image |
| // response. |
| auto* object = |
| fake_controller->fake_overlay_page_.last_received_objects_[0].get(); |
| auto* text = fake_controller->fake_overlay_page_.last_received_text_.get(); |
| EXPECT_TRUE(object); |
| EXPECT_TRUE(text); |
| EXPECT_TRUE(kTestOverlayObject->Equals(*object)); |
| EXPECT_EQ(kTestText->content_language, text->content_language); |
| } |
| |
| class LensOverlayControllerSideBySideBrowserTest |
| : public LensOverlayControllerBrowserTest { |
| protected: |
| void SetupFeatureList() override { |
| feature_list_.InitWithFeaturesAndParameters( |
| {{lens::features::kLensOverlay, {{"use-blur", "true"}}}, |
| {features::kSideBySide, {}}}, |
| {}); |
| } |
| |
| bool AreAnyRoundedCornersShowing() { |
| const ui::ElementContext context = |
| views::ElementTrackerViews::GetContextForView( |
| BrowserView::GetBrowserViewForBrowser(browser())); |
| views::View* start_corner = |
| views::ElementTrackerViews::GetInstance()->GetFirstMatchingView( |
| kContentsSeparatorLeadingTopCornerElementId, context); |
| views::View* end_corner = |
| views::ElementTrackerViews::GetInstance()->GetFirstMatchingView( |
| kContentsSeparatorTrailingTopCornerElementId, context); |
| return (start_corner && start_corner->GetVisible()) || |
| (end_corner && end_corner->GetVisible()); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerSideBySideBrowserTest, |
| BackgroundBlurNotLiveInitially) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Wait until AddBackgroundBlur is called. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->GetOverlayWebViewForTesting()->layer(); })); |
| |
| // In a normal tab, the screenshot is not resized initially, so background |
| // image capturing should not have been started. |
| EXPECT_FALSE(controller->GetLensOverlayBlurLayerDelegateForTesting() |
| ->IsCapturingBackgroundImageForTesting()); |
| } |
| |
| // Regression test for crbug.com/6893132. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerSideBySideBrowserTest, |
| BackgroundBlurWhenSwitchTabs) { |
| // Load the initial page and ensure it has finished painting. |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Grab the index of the currently active tab so we can return to it later. |
| int active_controller_tab_index = |
| browser()->tab_strip_model()->active_index(); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kStartingWebUI; })); |
| |
| // Quickly switch to a new tab. |
| WaitForPaint(kDocumentWithNamedElement, |
| WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB | |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| |
| // The original tab is now in the background, which should cause the |
| // overlay to transition to kBackground. |
| // Wait for this transition to complete. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kBackground; })); |
| |
| // Switch back to the original tab. |
| browser()->tab_strip_model()->ActivateTabAt(active_controller_tab_index); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // The blur layer delegate should exist now, but it should not be actively |
| // capturing since the blur is applied to a static screenshot in a normal tab. |
| ASSERT_TRUE(controller->GetLensOverlayBlurLayerDelegateForTesting()); |
| EXPECT_FALSE(controller->GetLensOverlayBlurLayerDelegateForTesting() |
| ->IsCapturingBackgroundImageForTesting()); |
| } |
| |
| // TODO(crbug.com/422479353): Flaky on Linux MSan. |
| #if BUILDFLAG(IS_LINUX) && defined(MEMORY_SANITIZER) |
| #define MAYBE_BackgroundBlurLiveInitiallyInSplitTab \ |
| DISABLED_BackgroundBlurLiveInitiallyInSplitTab |
| #else |
| #define MAYBE_BackgroundBlurLiveInitiallyInSplitTab \ |
| BackgroundBlurLiveInitiallyInSplitTab |
| #endif |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerSideBySideBrowserTest, |
| MAYBE_BackgroundBlurLiveInitiallyInSplitTab) { |
| chrome::NewSplitTab(browser(), |
| split_tabs::SplitTabCreatedSource::kToolbarButton); |
| |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| |
| // Wait until AddBackgroundBlur is called. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->GetOverlayWebViewForTesting()->layer(); })); |
| |
| // In a split tab, the screenshot is initially resized, so background image |
| // capturing should have been started. |
| EXPECT_TRUE(controller->GetLensOverlayBlurLayerDelegateForTesting() |
| ->IsCapturingBackgroundImageForTesting()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerSideBySideBrowserTest, |
| SidePanelRoundedCornerRegularTab) { |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Open a side panel. |
| controller->IssueTextSelectionRequestForTesting(/*text_query=*/"Apples", |
| /*selection_start_index=*/0, |
| /*selection_end_index=*/0); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlayAndResults; })); |
| |
| // Expect the side panel rounded corner to exist. |
| ASSERT_TRUE(controller->GetOverlayViewForTesting()->layer()); |
| EXPECT_TRUE(controller->GetOverlayViewForTesting() |
| ->layer() |
| ->GetTargetRoundedCornerRadius() |
| .upper_right() > 0); |
| EXPECT_TRUE(AreAnyRoundedCornersShowing()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerSideBySideBrowserTest, |
| SidePanelRoundedCornerSplitTab) { |
| // Create a new split, after which the second tab should be activated. |
| chrome::NewSplitTab(browser(), |
| split_tabs::SplitTabCreatedSource::kToolbarButton); |
| |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller_1 = GetLensOverlayController(); |
| ASSERT_EQ(controller_1->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller_1->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller_1->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Open a side panel. |
| controller_1->IssueTextSelectionRequestForTesting(/*text_query=*/"Apples", |
| /*selection_start_index=*/0, |
| /*selection_end_index=*/0); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller_1->state() == State::kOverlayAndResults; })); |
| |
| // Expect overlay view's corners to be rounded. |
| EXPECT_TRUE(controller_1->GetOverlayViewForTesting()->layer() && |
| controller_1->GetOverlayViewForTesting() |
| ->layer() |
| ->GetTargetRoundedCornerRadius() |
| .upper_right() > 0); |
| EXPECT_FALSE(AreAnyRoundedCornersShowing()); |
| |
| // Switch to the first tab. |
| browser()->tab_strip_model()->ActivateTabAt(0); |
| |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller_0 = GetLensOverlayController(); |
| ASSERT_EQ(controller_0->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller_0->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller_0->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Expect the corners to not be rounded. |
| EXPECT_FALSE(controller_0->GetOverlayViewForTesting()->layer() && |
| controller_0->GetOverlayViewForTesting() |
| ->layer() |
| ->GetTargetRoundedCornerRadius() |
| .upper_right() > 0); |
| EXPECT_FALSE(AreAnyRoundedCornersShowing()); |
| |
| // Switch back to the second tab. |
| browser()->tab_strip_model()->ActivateTabAt(1); |
| |
| // Wait for backgrounded state to be restored. |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller_1->state() == State::kOverlayAndResults; })); |
| |
| // Expect overlay view's corners to be rounded. |
| EXPECT_TRUE(controller_1->GetOverlayViewForTesting()->layer() && |
| controller_1->GetOverlayViewForTesting() |
| ->layer() |
| ->GetTargetRoundedCornerRadius() |
| .upper_right() > 0); |
| EXPECT_FALSE(AreAnyRoundedCornersShowing()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerSideBySideBrowserTest, |
| SidePanelAlignmentChanged) { |
| chrome::NewSplitTab(browser(), |
| split_tabs::SplitTabCreatedSource::kToolbarButton); |
| |
| WaitForPaint(); |
| |
| // State should start in off. |
| auto* controller = GetLensOverlayController(); |
| ASSERT_EQ(controller->state(), State::kOff); |
| |
| // Showing UI should change the state to screenshot and eventually to overlay. |
| OpenLensOverlay(LensOverlayInvocationSource::kAppMenu); |
| ASSERT_EQ(controller->state(), State::kScreenshot); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlay; })); |
| ASSERT_TRUE(content::WaitForLoadStop(GetOverlayWebContents())); |
| |
| // Open a side panel. |
| controller->IssueTextSelectionRequestForTesting(/*text_query=*/"Apples", |
| /*selection_start_index=*/0, |
| /*selection_end_index=*/0); |
| ASSERT_TRUE(base::test::RunUntil( |
| [&]() { return controller->state() == State::kOverlayAndResults; })); |
| |
| // Expect overlay view's top right corner to be rounded. |
| gfx::RoundedCornersF rounded_corners = controller->GetOverlayViewForTesting() |
| ->layer() |
| ->GetTargetRoundedCornerRadius(); |
| EXPECT_TRUE(rounded_corners.upper_right() > 0); |
| EXPECT_TRUE(rounded_corners.upper_left() == 0); |
| |
| // Change side panel to be left aligned. |
| browser()->profile()->GetPrefs()->SetBoolean( |
| prefs::kSidePanelHorizontalAlignment, false); |
| |
| // Expect overlay view's top left corner to be rounded. |
| rounded_corners = controller->GetOverlayViewForTesting() |
| ->layer() |
| ->GetTargetRoundedCornerRadius(); |
| EXPECT_TRUE(rounded_corners.upper_right() == 0); |
| EXPECT_TRUE(rounded_corners.upper_left() > 0); |
| } |