blob: 46799fe9998ee3862c4f7e30c457e0c479214529 [file] [log] [blame]
// Copyright (c) 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/omnibox/omnibox_view_views.h"
#include <stddef.h>
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/i18n/rtl.h"
#include "base/macros.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_tick_clock.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
#include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
#include "chrome/browser/command_updater.h"
#include "chrome/browser/command_updater_impl.h"
#include "chrome/browser/search_engines/template_url_service_factory_test_util.h"
#include "chrome/browser/ui/omnibox/chrome_omnibox_client.h"
#include "chrome/browser/ui/omnibox/chrome_omnibox_edit_controller.h"
#include "chrome/browser/ui/omnibox/omnibox_theme.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/views/chrome_views_test_base.h"
#include "components/omnibox/browser/omnibox_edit_model.h"
#include "components/omnibox/browser/test_location_bar_model.h"
#include "components/omnibox/common/omnibox_features.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/mock_navigation_handle.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/input/web_input_event.h"
#include "third_party/blink/public/common/input/web_keyboard_event.h"
#include "third_party/blink/public/common/input/web_mouse_event.h"
#include "third_party/metrics_proto/omnibox_event.pb.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/ime/input_method.h"
#include "ui/base/ime/text_edit_commands.h"
#include "ui/events/event_utils.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/gfx/animation/animation_container_element.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/render_text.h"
#include "ui/gfx/render_text_test_api.h"
#include "ui/views/controls/textfield/textfield_test_api.h"
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/input_method/input_method_configuration.h"
#include "chrome/browser/chromeos/input_method/mock_input_method_manager_impl.h"
#endif
using gfx::Range;
using metrics::OmniboxEventProto;
namespace {
// TestingOmniboxView ---------------------------------------------------------
class TestingOmniboxView : public OmniboxViewViews {
public:
TestingOmniboxView(OmniboxEditController* controller,
std::unique_ptr<OmniboxClient> client);
using views::Textfield::GetRenderText;
void ResetEmphasisTestState();
void CheckUpdatePopupCallInfo(size_t call_count,
const base::string16& text,
const Range& selection_range);
void CheckUpdatePopupNotCalled();
Range scheme_range() const { return scheme_range_; }
Range emphasis_range() const { return emphasis_range_; }
bool base_text_emphasis() const { return base_text_emphasis_; }
// Returns the latest color applied to |range| via ApplyColor(), or
// base::nullopt if no color has been applied to |range|.
base::Optional<SkColor> GetLatestColorForRange(const gfx::Range& range);
// OmniboxViewViews:
void GetAccessibleNodeData(ui::AXNodeData* node_data) override {}
void OnThemeChanged() override;
using OmniboxView::OnInlineAutocompleteTextMaybeChanged;
private:
// OmniboxViewViews:
// There is no popup and it doesn't actually matter whether we change the
// visual style of the text, so these methods are all overridden merely to
// capture relevant state at the time of the call, to be checked by test code.
void UpdatePopup() override;
void SetEmphasis(bool emphasize, const Range& range) override;
void UpdateSchemeStyle(const Range& range) override;
void ApplyColor(SkColor color, const gfx::Range& range) override;
size_t update_popup_call_count_ = 0;
base::string16 update_popup_text_;
Range update_popup_selection_range_;
// Range of the last scheme logged by UpdateSchemeStyle().
Range scheme_range_;
// Range of the last text emphasized by SetEmphasis().
Range emphasis_range_;
// Stores the colors applied to ranges via ApplyColor(), in chronological
// order.
std::vector<std::pair<SkColor, gfx::Range>> range_colors_;
// SetEmphasis() logs whether the base color of the text is emphasized.
bool base_text_emphasis_;
DISALLOW_COPY_AND_ASSIGN(TestingOmniboxView);
};
TestingOmniboxView::TestingOmniboxView(OmniboxEditController* controller,
std::unique_ptr<OmniboxClient> client)
: OmniboxViewViews(controller,
std::move(client),
false,
nullptr,
gfx::FontList()) {}
void TestingOmniboxView::ResetEmphasisTestState() {
base_text_emphasis_ = false;
emphasis_range_ = Range::InvalidRange();
scheme_range_ = Range::InvalidRange();
}
void TestingOmniboxView::CheckUpdatePopupCallInfo(
size_t call_count,
const base::string16& text,
const Range& selection_range) {
EXPECT_EQ(call_count, update_popup_call_count_);
EXPECT_EQ(text, update_popup_text_);
EXPECT_EQ(selection_range, update_popup_selection_range_);
}
void TestingOmniboxView::CheckUpdatePopupNotCalled() {
EXPECT_EQ(update_popup_call_count_, 0U);
}
base::Optional<SkColor> TestingOmniboxView::GetLatestColorForRange(
const gfx::Range& range) {
// Iterate backwards to get the most recently applied color for |range|.
for (auto range_color = range_colors_.rbegin();
range_color != range_colors_.rend(); range_color++) {
if (range == range_color->second)
return range_color->first;
}
return base::nullopt;
}
void TestingOmniboxView::OnThemeChanged() {
// This method is overridden simply to expose this protected method for tests
// to call.
OmniboxViewViews::OnThemeChanged();
}
void TestingOmniboxView::UpdatePopup() {
++update_popup_call_count_;
update_popup_text_ = GetText();
update_popup_selection_range_ = GetSelectedRange();
// The real view calls OmniboxEditModel::UpdateInput(), which sets input in
// progress and starts autocomplete. Triggering autocomplete and the popup is
// beyond the scope of this test, but setting input in progress is important
// for making some sequences (e.g. uneliding on taking an action) behave
// correctly.
model()->SetInputInProgress(true);
}
void TestingOmniboxView::SetEmphasis(bool emphasize, const Range& range) {
if (range == Range::InvalidRange()) {
base_text_emphasis_ = emphasize;
return;
}
EXPECT_TRUE(emphasize);
emphasis_range_ = range;
}
void TestingOmniboxView::UpdateSchemeStyle(const Range& range) {
scheme_range_ = range;
}
void TestingOmniboxView::ApplyColor(SkColor color, const gfx::Range& range) {
range_colors_.emplace_back(std::pair<SkColor, gfx::Range>(color, range));
}
// TestingOmniboxEditController -----------------------------------------------
class TestingOmniboxEditController : public ChromeOmniboxEditController {
public:
TestingOmniboxEditController(CommandUpdater* command_updater,
LocationBarModel* location_bar_model)
: ChromeOmniboxEditController(command_updater),
location_bar_model_(location_bar_model) {}
void set_omnibox_view(OmniboxViewViews* view) { omnibox_view_ = view; }
private:
// ChromeOmniboxEditController:
LocationBarModel* GetLocationBarModel() override {
return location_bar_model_;
}
const LocationBarModel* GetLocationBarModel() const override {
return location_bar_model_;
}
void UpdateWithoutTabRestore() override {
// This is a minimal amount of what LocationBarView does. Not all tests
// set |omnibox_view_|.
if (omnibox_view_)
omnibox_view_->Update();
}
LocationBarModel* location_bar_model_;
OmniboxViewViews* omnibox_view_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(TestingOmniboxEditController);
};
// TODO(crbug.com/1112536): With RTL UI, the URL is sometimes off by one pixel
// of the right edge. Investigate if this is expected, otherwise replace this
// with equality checks in tests that use it. Checks |a| is within 1 of |b|.
void CheckEqualsWithMarginOne(int a, int b) {
EXPECT_LE(std::abs(a - b), 1);
}
} // namespace
// OmniboxViewViewsTest -------------------------------------------------------
// Base class that ensures ScopedFeatureList is initialized first.
class OmniboxViewViewsTestBase : public ChromeViewsTestBase {
public:
OmniboxViewViewsTestBase(const std::vector<base::Feature>& enabled_features,
const std::vector<base::Feature>& disabled_features,
bool is_rtl_ui_test = false) {
scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
base::i18n::SetRTLForTesting(is_rtl_ui_test);
}
protected:
base::test::ScopedFeatureList scoped_feature_list_;
};
// The display URL used in simplified domain display tests.
const base::string16 kSimplifiedDomainDisplayUrl =
base::ASCIIToUTF16("https://foo.example.test/bar");
const base::string16 kSimplifiedDomainDisplayUrlHostnameAndScheme =
base::ASCIIToUTF16("https://foo.example.test");
const base::string16 kSimplifiedDomainDisplayUrlSubdomainAndScheme =
base::ASCIIToUTF16("https://foo.");
const base::string16 kSimplifiedDomainDisplayUrlSubdomain =
base::ASCIIToUTF16("foo.");
const base::string16 kSimplifiedDomainDisplayUrlPath =
base::ASCIIToUTF16("/bar");
const base::string16 kSimplifiedDomainDisplayUrlScheme =
base::ASCIIToUTF16("https://");
class OmniboxViewViewsTest : public OmniboxViewViewsTestBase {
public:
OmniboxViewViewsTest(const std::vector<base::Feature>& enabled_features,
const std::vector<base::Feature>& disabled_features,
bool is_rtl_ui_test = false);
OmniboxViewViewsTest()
: OmniboxViewViewsTest(std::vector<base::Feature>(),
std::vector<base::Feature>()) {}
TestLocationBarModel* location_bar_model() { return &location_bar_model_; }
CommandUpdaterImpl* command_updater() { return &command_updater_; }
TestingOmniboxView* omnibox_view() const { return omnibox_view_; }
// TODO(tommycli): These base class accessors exist because Textfield and
// OmniboxView both hide member functions that were public in base classes.
// Remove these after we stop doing that.
views::Textfield* omnibox_textfield() const { return omnibox_view(); }
views::View* omnibox_textfield_view() const { return omnibox_view(); }
views::TextfieldTestApi* textfield_test_api() { return test_api_.get(); }
// Sets |new_text| as the omnibox text, and emphasizes it appropriately. If
// |accept_input| is true, pretends that the user has accepted this input
// (i.e. it's been navigated to).
void SetAndEmphasizeText(const std::string& new_text, bool accept_input);
bool IsCursorEnabled() const {
return test_api_->GetRenderText()->cursor_enabled();
}
ui::MouseEvent CreateMouseEvent(ui::EventType type,
const gfx::Point& point,
int event_flags = ui::EF_LEFT_MOUSE_BUTTON) {
return ui::MouseEvent(type, point, point, ui::EventTimeForNow(),
event_flags, event_flags);
}
protected:
Profile* profile() { return profile_.get(); }
TestingOmniboxEditController* omnibox_edit_controller() {
return &omnibox_edit_controller_;
}
// Sets up tests for the simplified domain field trials.
void SetUpSimplifiedDomainTest() {
location_bar_model()->set_url(GURL(kSimplifiedDomainDisplayUrl));
location_bar_model()->set_url_for_display(kSimplifiedDomainDisplayUrl);
omnibox_view()->model()->ResetDisplayTexts();
omnibox_view()->RevertAll();
// Call OnThemeChanged() to create the animations.
omnibox_view()->OnThemeChanged();
}
// testing::Test:
void SetUp() override;
void TearDown() override;
ui::MouseEvent CreateEvent(ui::EventType type, int flags) {
return ui::MouseEvent(type, gfx::Point(0, 0), gfx::Point(),
ui::EventTimeForNow(), flags, 0);
}
private:
std::unique_ptr<TestingProfile> profile_;
std::unique_ptr<TemplateURLServiceFactoryTestUtil> util_;
CommandUpdaterImpl command_updater_;
TestLocationBarModel location_bar_model_;
TestingOmniboxEditController omnibox_edit_controller_;
content::RenderViewHostTestEnabler rvh_test_enabler_;
std::unique_ptr<views::Widget> widget_;
// Owned by |widget_|.
TestingOmniboxView* omnibox_view_;
std::unique_ptr<views::TextfieldTestApi> test_api_;
DISALLOW_COPY_AND_ASSIGN(OmniboxViewViewsTest);
};
OmniboxViewViewsTest::OmniboxViewViewsTest(
const std::vector<base::Feature>& enabled_features,
const std::vector<base::Feature>& disabled_features,
bool is_rtl_ui_test)
: OmniboxViewViewsTestBase(enabled_features,
disabled_features,
is_rtl_ui_test),
command_updater_(nullptr),
omnibox_edit_controller_(&command_updater_, &location_bar_model_) {}
void OmniboxViewViewsTest::SetAndEmphasizeText(const std::string& new_text,
bool accept_input) {
omnibox_view()->ResetEmphasisTestState();
omnibox_view()->SetUserText(base::ASCIIToUTF16(new_text));
if (accept_input) {
// We don't need to actually navigate in this case (and doing so in a test
// would be difficult); it's sufficient to mark input as "no longer in
// progress", and the edit model will assume the current text is a URL.
omnibox_view()->model()->SetInputInProgress(false);
}
omnibox_view()->EmphasizeURLComponents();
}
void OmniboxViewViewsTest::SetUp() {
ChromeViewsTestBase::SetUp();
profile_ = std::make_unique<TestingProfile>();
util_ = std::make_unique<TemplateURLServiceFactoryTestUtil>(profile_.get());
// We need a widget so OmniboxView can be correctly focused and unfocused.
widget_ = CreateTestWidget();
widget_->Show();
#if defined(OS_CHROMEOS)
chromeos::input_method::InitializeForTesting(
new chromeos::input_method::MockInputMethodManagerImpl);
#endif
AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse(
profile_.get(),
base::BindRepeating(&AutocompleteClassifierFactory::BuildInstanceFor));
auto omnibox_view = std::make_unique<TestingOmniboxView>(
&omnibox_edit_controller_,
std::make_unique<ChromeOmniboxClient>(&omnibox_edit_controller_,
profile_.get()));
test_api_ = std::make_unique<views::TextfieldTestApi>(omnibox_view.get());
omnibox_view->Init();
omnibox_view_ = widget_->SetContentsView(std::move(omnibox_view));
}
void OmniboxViewViewsTest::TearDown() {
// Clean ourselves up as the text input client.
if (omnibox_view_->GetInputMethod())
omnibox_view_->GetInputMethod()->DetachTextInputClient(omnibox_view_);
widget_.reset();
util_.reset();
profile_.reset();
#if defined(OS_CHROMEOS)
chromeos::input_method::Shutdown();
#endif
ChromeViewsTestBase::TearDown();
}
// Actual tests ---------------------------------------------------------------
// Checks that a single change of the text in the omnibox invokes
// only one call to OmniboxViewViews::UpdatePopup().
TEST_F(OmniboxViewViewsTest, UpdatePopupCall) {
ui::KeyEvent char_event(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::US_A, 0,
ui::DomKey::FromCharacter('a'),
ui::EventTimeForNow());
omnibox_textfield()->InsertChar(char_event);
omnibox_view()->CheckUpdatePopupCallInfo(1, base::ASCIIToUTF16("a"),
Range(1));
char_event =
ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_B, ui::DomCode::US_B, 0,
ui::DomKey::FromCharacter('b'), ui::EventTimeForNow());
omnibox_textfield()->InsertChar(char_event);
omnibox_view()->CheckUpdatePopupCallInfo(2, base::ASCIIToUTF16("ab"),
Range(2));
ui::KeyEvent pressed(ui::ET_KEY_PRESSED, ui::VKEY_BACK, 0);
omnibox_textfield()->OnKeyEvent(&pressed);
omnibox_view()->CheckUpdatePopupCallInfo(3, base::ASCIIToUTF16("a"),
Range(1));
}
// Test that text cursor is shown in the omnibox after entering any single
// character in NTP 'Search box'. Test for crbug.com/698172.
TEST_F(OmniboxViewViewsTest, EditTextfield) {
omnibox_textfield()->SetCursorEnabled(false);
ui::KeyEvent char_event(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::US_A, 0,
ui::DomKey::FromCharacter('a'),
ui::EventTimeForNow());
omnibox_textfield()->InsertChar(char_event);
EXPECT_TRUE(IsCursorEnabled());
}
// Test that the scheduled text edit command is cleared when Textfield receives
// a key press event. This ensures that the scheduled text edit command property
// is always in the correct state. Test for http://crbug.com/613948.
TEST_F(OmniboxViewViewsTest, ScheduledTextEditCommand) {
omnibox_textfield()->SetTextEditCommandForNextKeyEvent(
ui::TextEditCommand::MOVE_UP);
EXPECT_EQ(ui::TextEditCommand::MOVE_UP,
textfield_test_api()->scheduled_text_edit_command());
ui::KeyEvent up_pressed(ui::ET_KEY_PRESSED, ui::VKEY_UP, 0);
omnibox_textfield()->OnKeyEvent(&up_pressed);
EXPECT_EQ(ui::TextEditCommand::INVALID_COMMAND,
textfield_test_api()->scheduled_text_edit_command());
}
// Test that Shift+Up and Shift+Down are not captured and let selection mode
// take over. Test for crbug.com/863543 and crbug.com/892216.
TEST_F(OmniboxViewViewsTest, SelectWithShift_863543) {
location_bar_model()->set_url(GURL("http://www.example.com/?query=1"));
const base::string16 text =
base::ASCIIToUTF16("http://www.example.com/?query=1");
omnibox_view()->SetWindowTextAndCaretPos(text, 23U, false, false);
ui::KeyEvent shift_up_pressed(ui::ET_KEY_PRESSED, ui::VKEY_UP,
ui::EF_SHIFT_DOWN);
omnibox_textfield()->OnKeyEvent(&shift_up_pressed);
size_t start, end;
omnibox_view()->GetSelectionBounds(&start, &end);
EXPECT_EQ(23U, start);
EXPECT_EQ(0U, end);
omnibox_view()->CheckUpdatePopupNotCalled();
omnibox_view()->SetWindowTextAndCaretPos(text, 18U, false, false);
ui::KeyEvent shift_down_pressed(ui::ET_KEY_PRESSED, ui::VKEY_DOWN,
ui::EF_SHIFT_DOWN);
omnibox_textfield()->OnKeyEvent(&shift_down_pressed);
omnibox_view()->GetSelectionBounds(&start, &end);
EXPECT_EQ(18U, start);
EXPECT_EQ(31U, end);
omnibox_view()->CheckUpdatePopupNotCalled();
}
TEST_F(OmniboxViewViewsTest, OnBlur) {
// Make the Omnibox very narrow (so it couldn't fit the whole string).
int kOmniboxWidth = 60;
gfx::RenderText* render_text = omnibox_view()->GetRenderText();
render_text->SetDisplayRect(gfx::Rect(0, 0, kOmniboxWidth, 10));
render_text->SetHorizontalAlignment(gfx::ALIGN_LEFT);
// (In this example, uppercase Latin letters represent Hebrew letters.)
// The string |kContentsRtl| is equivalent to:
// RA.QWM/0123/abcd
// This is displayed as:
// 0123/MWQ.AR/abcd
// Enter focused mode, where the text should *not* be elided, and we expect
// SetWindowTextAndCaretPos to scroll such that the start of the string is
// on-screen. Because the domain is RTL, this scrolls to an offset greater
// than 0.
omnibox_textfield()->OnFocus();
const base::string16 kContentsRtl =
base::WideToUTF16(L"\x05e8\x05e2.\x05e7\x05d5\x05dd/0123/abcd");
omnibox_view()->SetWindowTextAndCaretPos(kContentsRtl, 0, false, false);
EXPECT_EQ(gfx::NO_ELIDE, render_text->elide_behavior());
// TODO(https://crbug.com/1094386): this assertion fails because
// EmphasizeURLComponents() sets the textfield's directionality to
// DIRECTIONALITY_AS_URL. This should be either fixed or the assertion
// removed.
//
// NOTE: Technically (depending on the font), this expectation could fail if
// the entire domain fits in 60 pixels. However, 60px is so small it should
// never happen with any font.
// EXPECT_GT(0, render_text->GetUpdatedDisplayOffset().x());
omnibox_view()->SelectAll(false);
EXPECT_TRUE(omnibox_view()->IsSelectAll());
// Now enter blurred mode, where the text should be elided to 60px. This means
// the string itself is truncated. Scrolling would therefore mean the text is
// off-screen. Ensure that the horizontal scrolling has been reset to 0.
omnibox_textfield()->OnBlur();
EXPECT_EQ(gfx::ELIDE_TAIL, render_text->elide_behavior());
EXPECT_EQ(0, render_text->GetUpdatedDisplayOffset().x());
EXPECT_FALSE(omnibox_view()->IsSelectAll());
}
TEST_F(OmniboxViewViewsTest, Emphasis) {
constexpr struct {
const char* input;
bool expected_base_text_emphasized;
Range expected_emphasis_range;
Range expected_scheme_range;
} test_cases[] = {
{"data:text/html,Hello%20World", false, Range(0, 4), Range(0, 4)},
{"http://www.example.com/path/file.htm", false, Range(7, 22),
Range(0, 4)},
{"https://www.example.com/path/file.htm", false, Range(8, 23),
Range(0, 5)},
{"chrome-extension://ldfbacdbackkjhclmhnjabngnppnkagl", false,
Range::InvalidRange(), Range(0, 16)},
{"nosuchscheme://opaque/string", true, Range::InvalidRange(),
Range(0, 12)},
{"nosuchscheme:opaquestring", true, Range::InvalidRange(), Range(0, 12)},
{"host.com/path/file", false, Range(0, 8), Range::InvalidRange()},
{"This is plain text", true, Range::InvalidRange(),
Range::InvalidRange()},
};
for (const auto& test_case : test_cases) {
SCOPED_TRACE(test_case.input);
SetAndEmphasizeText(test_case.input, false);
EXPECT_EQ(test_case.expected_base_text_emphasized,
omnibox_view()->base_text_emphasis());
EXPECT_EQ(test_case.expected_emphasis_range,
omnibox_view()->emphasis_range());
EXPECT_FALSE(omnibox_view()->scheme_range().IsValid());
if (test_case.expected_scheme_range.IsValid()) {
SetAndEmphasizeText(test_case.input, true);
EXPECT_EQ(test_case.expected_base_text_emphasized,
omnibox_view()->base_text_emphasis());
EXPECT_EQ(test_case.expected_emphasis_range,
omnibox_view()->emphasis_range());
EXPECT_EQ(test_case.expected_scheme_range,
omnibox_view()->scheme_range());
}
}
}
TEST_F(OmniboxViewViewsTest, RevertOnBlur) {
location_bar_model()->set_url(GURL("https://example.com/"));
omnibox_view()->model()->ResetDisplayTexts();
omnibox_view()->RevertAll();
EXPECT_EQ(base::ASCIIToUTF16("https://example.com/"),
omnibox_view()->GetText());
EXPECT_FALSE(omnibox_view()->model()->user_input_in_progress());
// Set the view text without updating the model's user text. This usually
// occurs when the omnibox unapplies Steady State Elisions to temporarily show
// the full URL to the user.
omnibox_view()->SetWindowTextAndCaretPos(base::ASCIIToUTF16("view text"), 0,
false, false);
EXPECT_EQ(base::ASCIIToUTF16("view text"), omnibox_view()->GetText());
EXPECT_FALSE(omnibox_view()->model()->user_input_in_progress());
// Expect that on blur, we revert to the original text and are not in user
// input mode.
omnibox_textfield()->OnBlur();
EXPECT_EQ(base::ASCIIToUTF16("https://example.com/"),
omnibox_view()->GetText());
EXPECT_FALSE(omnibox_view()->model()->user_input_in_progress());
// Now set user text, which is reflected into the model as well.
omnibox_view()->SetUserText(base::ASCIIToUTF16("user text"));
EXPECT_EQ(base::ASCIIToUTF16("user text"), omnibox_view()->GetText());
EXPECT_TRUE(omnibox_view()->model()->user_input_in_progress());
// Expect that on blur, if the text has been edited, stay in user input mode.
omnibox_textfield()->OnBlur();
EXPECT_EQ(base::ASCIIToUTF16("user text"), omnibox_view()->GetText());
EXPECT_TRUE(omnibox_view()->model()->user_input_in_progress());
}
TEST_F(OmniboxViewViewsTest, RevertOnEscape) {
location_bar_model()->set_url(GURL("https://permanent-text.com/"));
omnibox_view()->model()->ResetDisplayTexts();
omnibox_view()->RevertAll();
EXPECT_EQ(base::ASCIIToUTF16("https://permanent-text.com/"),
omnibox_view()->GetText());
EXPECT_FALSE(omnibox_view()->model()->user_input_in_progress());
omnibox_view()->SetUserText(base::ASCIIToUTF16("user text"));
EXPECT_EQ(base::ASCIIToUTF16("user text"), omnibox_view()->GetText());
EXPECT_TRUE(omnibox_view()->model()->user_input_in_progress());
// Expect that on Escape, the text is reverted to the permanent URL.
ui::KeyEvent escape(ui::ET_KEY_PRESSED, ui::VKEY_ESCAPE, 0);
omnibox_textfield()->OnKeyEvent(&escape);
EXPECT_EQ(base::ASCIIToUTF16("https://permanent-text.com/"),
omnibox_view()->GetText());
EXPECT_FALSE(omnibox_view()->model()->user_input_in_progress());
}
TEST_F(OmniboxViewViewsTest, BackspaceExitsKeywordMode) {
omnibox_view()->SetUserText(base::UTF8ToUTF16("user text"));
omnibox_view()->model()->EnterKeywordModeForDefaultSearchProvider(
OmniboxEventProto::KEYBOARD_SHORTCUT);
ASSERT_EQ(base::UTF8ToUTF16("user text"), omnibox_view()->GetText());
ASSERT_TRUE(omnibox_view()->IsSelectAll());
ASSERT_FALSE(omnibox_view()->model()->keyword().empty());
// First backspace should clear the user text but not exit keyword mode.
ui::KeyEvent backspace(ui::ET_KEY_PRESSED, ui::VKEY_BACK, 0);
omnibox_textfield()->OnKeyEvent(&backspace);
EXPECT_TRUE(omnibox_view()->GetText().empty());
EXPECT_FALSE(omnibox_view()->model()->keyword().empty());
// Second backspace should exit keyword mode.
omnibox_textfield()->OnKeyEvent(&backspace);
EXPECT_TRUE(omnibox_view()->GetText().empty());
EXPECT_TRUE(omnibox_view()->model()->keyword().empty());
}
TEST_F(OmniboxViewViewsTest, BlurNeverExitsKeywordMode) {
location_bar_model()->set_url(GURL());
omnibox_view()->model()->ResetDisplayTexts();
omnibox_view()->RevertAll();
// Enter keyword mode, but with no user text.
omnibox_view()->model()->EnterKeywordModeForDefaultSearchProvider(
OmniboxEventProto::KEYBOARD_SHORTCUT);
EXPECT_TRUE(omnibox_view()->GetText().empty());
EXPECT_FALSE(omnibox_view()->model()->keyword().empty());
// Expect that on blur, stay in keyword mode.
omnibox_textfield()->OnBlur();
EXPECT_TRUE(omnibox_view()->GetText().empty());
EXPECT_FALSE(omnibox_view()->model()->keyword().empty());
}
TEST_F(OmniboxViewViewsTest, PasteAndGoToUrlOrSearchCommand) {
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
ui::ClipboardBuffer clipboard_buffer = ui::ClipboardBuffer::kCopyPaste;
command_updater()->UpdateCommandEnabled(IDC_OPEN_CURRENT_URL, true);
// Test command is disabled for an empty clipboard.
clipboard->Clear(clipboard_buffer);
EXPECT_FALSE(omnibox_view()->IsCommandIdEnabled(IDC_PASTE_AND_GO));
// Test input that's a valid URL.
base::string16 expected_text =
#if defined(OS_MAC)
base::ASCIIToUTF16("Pa&ste and Go to https://test.com");
#else
base::ASCIIToUTF16("Pa&ste and go to https://test.com");
#endif
ui::ScopedClipboardWriter(clipboard_buffer)
.WriteText(base::ASCIIToUTF16("https://test.com/"));
base::string16 returned_text =
omnibox_view()->GetLabelForCommandId(IDC_PASTE_AND_GO);
EXPECT_TRUE(omnibox_view()->IsCommandIdEnabled(IDC_PASTE_AND_GO));
EXPECT_EQ(expected_text, returned_text);
// Test input that's URL-like. (crbug.com/980002).
expected_text =
#if defined(OS_MAC)
base::ASCIIToUTF16("Pa&ste and Go to test.com");
#else
base::ASCIIToUTF16("Pa&ste and go to test.com");
#endif
ui::ScopedClipboardWriter(clipboard_buffer)
.WriteText(base::ASCIIToUTF16("test.com"));
returned_text = omnibox_view()->GetLabelForCommandId(IDC_PASTE_AND_GO);
EXPECT_TRUE(omnibox_view()->IsCommandIdEnabled(IDC_PASTE_AND_GO));
EXPECT_EQ(expected_text, returned_text);
// Test input that's search-like.
expected_text =
#if defined(OS_MAC)
base::WideToUTF16(
L"Pa&ste and Search for \x201Cthis is a test sentence\x201D");
#else
base::WideToUTF16(
L"Pa&ste and search for \x201Cthis is a test sentence\x201D");
#endif
ui::ScopedClipboardWriter(clipboard_buffer)
.WriteText(base::ASCIIToUTF16("this is a test sentence"));
returned_text = omnibox_view()->GetLabelForCommandId(IDC_PASTE_AND_GO);
EXPECT_TRUE(omnibox_view()->IsCommandIdEnabled(IDC_PASTE_AND_GO));
EXPECT_EQ(expected_text, returned_text);
}
// Verifies |OmniboxEditModel::State::needs_revert_and_select_all|, and verifies
// a recent regression in this logic (see https://crbug.com/923290).
TEST_F(OmniboxViewViewsTest, SelectAllOnReactivateTabAfterDeleteAll) {
omnibox_edit_controller()->set_omnibox_view(omnibox_view());
auto web_contents1 =
content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
// Simulate a new tab with "about:blank".
const GURL url_1("about:blank/");
location_bar_model()->set_url(url_1);
omnibox_view()->model()->ResetDisplayTexts();
omnibox_view()->RevertAll();
omnibox_view()->SaveStateToTab(web_contents1.get());
// Simulate creating another tab at "chrome://history". The second url should
// be longer than the first (to trigger the bug).
auto web_contents2 =
content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
const GURL url_2("chrome://history/");
EXPECT_GT(url_2.spec().size(), url_1.spec().size());
// Notice the url is set before ResetDisplayTexts(), this matches what
// actually happens in code.
location_bar_model()->set_url(url_2);
omnibox_view()->model()->ResetDisplayTexts();
omnibox_view()->RevertAll();
// Delete all the text.
omnibox_view()->SetUserText(base::string16());
EXPECT_TRUE(omnibox_view()->model()->user_input_in_progress());
// Switch back to the first url.
location_bar_model()->set_url(url_1);
omnibox_view()->SaveStateToTab(web_contents2.get());
omnibox_view()->OnTabChanged(web_contents1.get());
// Switch back to the second url. Even though the text was deleted earlier,
// the previous text (|url_2|) should be restored *and* all the text selected.
location_bar_model()->set_url(url_2);
omnibox_view()->SaveStateToTab(web_contents1.get());
omnibox_view()->OnTabChanged(web_contents2.get());
EXPECT_EQ(url_2, GURL(base::UTF16ToUTF8(omnibox_view()->GetText())));
EXPECT_TRUE(omnibox_view()->IsSelectAll());
}
TEST_F(OmniboxViewViewsTest, SelectAllDuringMouseDown) {
omnibox_textfield()->OnMousePressed(
CreateMouseEvent(ui::ET_MOUSE_PRESSED, {0, 0}));
omnibox_view()->SetUserText(base::ASCIIToUTF16("abc"));
ui::KeyEvent event_a(ui::ET_KEY_PRESSED, ui::VKEY_A, 0);
EXPECT_FALSE(omnibox_view()->IsSelectAll());
omnibox_textfield_view()->OnKeyPressed(event_a);
// Normally SelectAll happens after OnMouseRelease. Verifying this happens
// during OnKeyPress when the mouse is down.
EXPECT_TRUE(omnibox_view()->IsSelectAll());
}
TEST_F(OmniboxViewViewsTest, SetWindowTextAndCaretPos) {
// googl|e.com
omnibox_view()->SetWindowTextAndCaretPos(base::UTF8ToUTF16("google.com"), 5,
false, false);
EXPECT_EQ(base::ASCIIToUTF16("google.com"), omnibox_view()->GetText());
EXPECT_EQ(omnibox_view()->GetRenderText()->GetAllSelections(),
(std::vector<Range>{{5, 5}}));
}
TEST_F(OmniboxViewViewsTest, OnInlineAutocompleteTextMaybeChanged) {
// No selection, google.com|
omnibox_view()->OnInlineAutocompleteTextMaybeChanged(
base::UTF8ToUTF16("google.com"), 0, 10);
EXPECT_EQ(base::ASCIIToUTF16("google.com"), omnibox_view()->GetText());
EXPECT_EQ(omnibox_view()->GetRenderText()->GetAllSelections(),
(std::vector<Range>{{10, 10}}));
// Single selection, gmai[l.com]
omnibox_view()->OnInlineAutocompleteTextMaybeChanged(
base::UTF8ToUTF16("gmail.com"), 0, 4);
EXPECT_EQ(base::ASCIIToUTF16("gmail.com"), omnibox_view()->GetText());
EXPECT_EQ(omnibox_view()->GetRenderText()->GetAllSelections(),
(std::vector<Range>{{9, 4}}));
// Multiselection, [go]ogl[e.com]
omnibox_view()->OnInlineAutocompleteTextMaybeChanged(
base::UTF8ToUTF16("google.com"), 2, 3);
EXPECT_EQ(base::ASCIIToUTF16("google.com"), omnibox_view()->GetText());
EXPECT_EQ(omnibox_view()->GetRenderText()->GetAllSelections(),
(std::vector<Range>{{10, 5}, {0, 2}}));
}
TEST_F(OmniboxViewViewsTest, OverflowingAutocompleteText) {
// Make the Omnibox narrow so it can't fit the entire string (~650px), but
// wide enough to fit the user text (~65px).
int kOmniboxWidth = 100;
gfx::RenderText* render_text = omnibox_view()->GetRenderText();
render_text->SetDisplayRect(gfx::Rect(0, 0, kOmniboxWidth, 10));
omnibox_textfield()->OnFocus();
omnibox_view()->OnInlineAutocompleteTextMaybeChanged(
base::ASCIIToUTF16("user text. Followed by very long autocompleted text "
"that is unlikely to fit in |kOmniboxWidth|"),
0, 10);
// NOTE: Technically (depending on the font), this expectation could fail if
// 'user text' doesn't fit in 100px or the entire string fits in 100px.
EXPECT_EQ(render_text->GetUpdatedDisplayOffset().x(), 0);
EXPECT_FALSE(omnibox_view()->IsSelectAll());
// On blur, the display should remain to the start of the text.
omnibox_textfield()->OnBlur();
EXPECT_EQ(render_text->GetUpdatedDisplayOffset().x(), 0);
EXPECT_FALSE(omnibox_view()->IsSelectAll());
}
TEST_F(OmniboxViewViewsTest, ElideAnimationDoesntStartIfNoVisibleChange) {
SetUpSimplifiedDomainTest();
gfx::RenderText* render_text = omnibox_view()->GetRenderText();
OmniboxViewViews::ElideAnimation elide_animation(omnibox_view(), render_text);
// Before any animation runs, the elide from rectangle is considered to be
// render_text's DisplayRect, so set it manually to be the current URL length.
gfx::Rect full_url_bounds;
for (auto rect : render_text->GetSubstringBounds(
gfx::Range(0, omnibox_view()->GetOmniboxTextLength()))) {
full_url_bounds.Union(rect);
}
render_text->SetDisplayRect(full_url_bounds);
// Start the animation, and have it animate to the current state.
elide_animation.Start(
gfx::Range(0,
omnibox_view()->GetOmniboxTextLength()), /* elide_to_bounds */
0, /* delay_ms */
{gfx::Range(0, 0)}, /* ranges_surrounding_simplified_domain */
SK_ColorBLACK, /* starting_color */
SK_ColorBLACK); /* ending_color */
// Animation shouldn't have been started.
EXPECT_FALSE(elide_animation.IsAnimating());
}
class OmniboxViewViewsClipboardTest
: public OmniboxViewViewsTest,
public ::testing::WithParamInterface<ui::TextEditCommand> {
public:
void SetUp() override {
OmniboxViewViewsTest::SetUp();
location_bar_model()->set_url(GURL("https://test.com/"));
omnibox_view()->model()->ResetDisplayTexts();
omnibox_view()->RevertAll();
}
};
TEST_P(OmniboxViewViewsClipboardTest, ClipboardCopyOrCutURL) {
omnibox_view()->SelectAll(false);
ASSERT_TRUE(omnibox_view()->IsSelectAll());
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
ui::ClipboardBuffer clipboard_buffer = ui::ClipboardBuffer::kCopyPaste;
clipboard->Clear(clipboard_buffer);
ui::TextEditCommand clipboard_command = GetParam();
textfield_test_api()->ExecuteTextEditCommand(clipboard_command);
base::string16 expected_text;
if (clipboard_command == ui::TextEditCommand::COPY)
expected_text = base::ASCIIToUTF16("https://test.com/");
EXPECT_EQ(expected_text, omnibox_view()->GetText());
// Make sure the plain text format is available, but the HTML one isn't.
EXPECT_TRUE(
clipboard->IsFormatAvailable(ui::ClipboardFormatType::GetPlainTextType(),
clipboard_buffer, /* data_dst = */ nullptr));
EXPECT_FALSE(
clipboard->IsFormatAvailable(ui::ClipboardFormatType::GetHtmlType(),
clipboard_buffer, /* data_dst = */ nullptr));
// Windows clipboard only supports text URLs.
// Mac clipboard not reporting URL format available for some reason.
// crbug.com/751031
#if defined(OS_LINUX)
EXPECT_TRUE(
clipboard->IsFormatAvailable(ui::ClipboardFormatType::GetUrlType(),
clipboard_buffer, /* data_dst = */ nullptr));
#endif
std::string read_from_clipboard;
clipboard->ReadAsciiText(clipboard_buffer, /* data_dst = */ nullptr,
&read_from_clipboard);
EXPECT_EQ("https://test.com/", read_from_clipboard);
}
TEST_P(OmniboxViewViewsClipboardTest, ClipboardCopyOrCutUserText) {
omnibox_view()->SetUserText(base::ASCIIToUTF16("user text"));
omnibox_view()->SelectAll(false);
ASSERT_TRUE(omnibox_view()->IsSelectAll());
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
ui::ClipboardBuffer clipboard_buffer = ui::ClipboardBuffer::kCopyPaste;
clipboard->Clear(clipboard_buffer);
ui::TextEditCommand clipboard_command = GetParam();
textfield_test_api()->ExecuteTextEditCommand(clipboard_command);
if (clipboard_command == ui::TextEditCommand::CUT)
EXPECT_EQ(base::string16(), omnibox_view()->GetText());
// Make sure HTML format isn't written. See
// BookmarkNodeData::WriteToClipboard() for details.
EXPECT_TRUE(
clipboard->IsFormatAvailable(ui::ClipboardFormatType::GetPlainTextType(),
clipboard_buffer, /* data_dst = */ nullptr));
EXPECT_FALSE(
clipboard->IsFormatAvailable(ui::ClipboardFormatType::GetHtmlType(),
clipboard_buffer, /* data_dst = */ nullptr));
std::string read_from_clipboard;
clipboard->ReadAsciiText(clipboard_buffer, /* data_dst = */ nullptr,
&read_from_clipboard);
EXPECT_EQ("user text", read_from_clipboard);
}
INSTANTIATE_TEST_SUITE_P(OmniboxViewViewsClipboardTest,
OmniboxViewViewsClipboardTest,
::testing::Values(ui::TextEditCommand::COPY,
ui::TextEditCommand::CUT));
class OmniboxViewViewsSteadyStateElisionsTest : public OmniboxViewViewsTest {
public:
OmniboxViewViewsSteadyStateElisionsTest() : OmniboxViewViewsTest({}, {}) {}
protected:
const int kCharacterWidth = 10;
const GURL kFullUrl = GURL("https://www.example.com/");
void SetUp() override {
OmniboxViewViewsTest::SetUp();
// Advance 5 seconds from epoch so the time is not considered null.
clock_.Advance(base::TimeDelta::FromSeconds(5));
ui::SetEventTickClockForTesting(&clock_);
location_bar_model()->set_url(kFullUrl);
location_bar_model()->set_url_for_display(
base::ASCIIToUTF16("example.com"));
gfx::test::RenderTextTestApi render_text_test_api(
omnibox_view()->GetRenderText());
render_text_test_api.SetGlyphWidth(kCharacterWidth);
omnibox_view()->model()->ResetDisplayTexts();
omnibox_view()->RevertAll();
}
void TearDown() override {
ui::SetEventTickClockForTesting(nullptr);
OmniboxViewViewsTest::TearDown();
}
void BlurOmnibox() {
ASSERT_TRUE(omnibox_view()->HasFocus());
omnibox_view()->GetFocusManager()->ClearFocus();
ASSERT_FALSE(omnibox_view()->HasFocus());
}
void ExpectFullUrlDisplayed() {
EXPECT_EQ(base::UTF8ToUTF16(kFullUrl.spec()), omnibox_view()->GetText());
EXPECT_FALSE(omnibox_view()->model()->user_input_in_progress());
}
bool IsElidedUrlDisplayed() {
return omnibox_view()->GetText() == base::ASCIIToUTF16("example.com") &&
!omnibox_view()->model()->user_input_in_progress();
}
// Gets a point at |x_offset| from the beginning of the RenderText.
gfx::Point GetPointInTextAtXOffset(int x_offset) {
gfx::Rect bounds = omnibox_view()->GetRenderText()->display_rect();
return gfx::Point(bounds.x() + x_offset, bounds.y() + bounds.height() / 2);
}
// Sends a mouse down and mouse up event at |x_offset| pixels from the
// beginning of the RenderText.
void SendMouseClick(int x_offset) {
gfx::Point point = GetPointInTextAtXOffset(x_offset);
SendMouseClickAtPoint(point, 1);
}
// Sends a mouse down and mouse up event at a point
// beginning of the RenderText.
void SendMouseClickAtPoint(gfx::Point point,
int click_count,
int event_flags = ui::EF_LEFT_MOUSE_BUTTON) {
auto mouse_pressed =
CreateMouseEvent(ui::ET_MOUSE_PRESSED, point, event_flags);
mouse_pressed.SetClickCount(click_count);
omnibox_textfield()->OnMousePressed(mouse_pressed);
auto mouse_released =
CreateMouseEvent(ui::ET_MOUSE_RELEASED, point, event_flags);
mouse_released.SetClickCount(click_count);
omnibox_textfield()->OnMouseReleased(mouse_released);
}
// Used to access members that are marked private in views::TextField.
base::SimpleTestTickClock* clock() { return &clock_; }
private:
base::SimpleTestTickClock clock_;
};
TEST_F(OmniboxViewViewsSteadyStateElisionsTest, UrlStartsInElidedState) {
EXPECT_TRUE(IsElidedUrlDisplayed());
}
TEST_F(OmniboxViewViewsSteadyStateElisionsTest, UnelideOnArrowKey) {
SendMouseClick(0);
// Right key should unelide and move the cursor to the end.
omnibox_textfield_view()->OnKeyPressed(
ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_RIGHT, 0));
ExpectFullUrlDisplayed();
size_t start, end;
omnibox_view()->GetSelectionBounds(&start, &end);
EXPECT_EQ(23U, start);
EXPECT_EQ(23U, end);
// Blur to restore the elided URL, then click on the Omnibox again to refocus.
BlurOmnibox();
SendMouseClick(0);
// Left key should unelide and move the cursor to the beginning of the elided
// part.
omnibox_textfield_view()->OnKeyPressed(
ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_LEFT, 0));
ExpectFullUrlDisplayed();
omnibox_view()->GetSelectionBounds(&start, &end);
EXPECT_EQ(12U, start);
EXPECT_EQ(12U, end);
}
TEST_F(OmniboxViewViewsSteadyStateElisionsTest, UnelideOnHomeKey) {
SendMouseClick(0);
// Home key should unelide and move the cursor to the beginning of the full
// unelided URL.
omnibox_textfield_view()->OnKeyPressed(
ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_HOME, 0));
ExpectFullUrlDisplayed();
size_t start, end;
omnibox_view()->GetSelectionBounds(&start, &end);
EXPECT_EQ(0U, start);
EXPECT_EQ(0U, end);
}
TEST_F(OmniboxViewViewsSteadyStateElisionsTest,
UnelideViaEndKeyWorksWithIntranetUrls) {
location_bar_model()->set_url(GURL("https://foobar/"));
location_bar_model()->set_formatted_full_url(
base::ASCIIToUTF16("https://foobar"));
location_bar_model()->set_url_for_display(base::ASCIIToUTF16("foobar/"));
omnibox_view()->model()->ResetDisplayTexts();
omnibox_view()->RevertAll();
SendMouseClick(0);
// End key should unelide and move the cursor to the end of the full URL.
omnibox_textfield_view()->OnKeyPressed(
ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_END, 0));
EXPECT_EQ(base::ASCIIToUTF16("https://foobar"), omnibox_view()->GetText());
EXPECT_FALSE(omnibox_view()->model()->user_input_in_progress());
size_t start, end;
omnibox_view()->GetSelectionBounds(&start, &end);
EXPECT_EQ(14U, start);
EXPECT_EQ(14U, end);
}
TEST_F(OmniboxViewViewsSteadyStateElisionsTest, GestureTaps) {
ui::GestureEvent tap_down(0, 0, 0, ui::EventTimeForNow(),
ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN));
omnibox_textfield()->OnGestureEvent(&tap_down);
// Select all on first tap.
ui::GestureEventDetails tap_details(ui::ET_GESTURE_TAP);
tap_details.set_tap_count(1);
ui::GestureEvent tap(0, 0, 0, ui::EventTimeForNow(), tap_details);
omnibox_textfield()->OnGestureEvent(&tap);
EXPECT_TRUE(omnibox_view()->IsSelectAll());
EXPECT_TRUE(IsElidedUrlDisplayed());
// Unelide on second tap (cursor placement).
omnibox_textfield()->OnGestureEvent(&tap);
ExpectFullUrlDisplayed();
}
TEST_F(OmniboxViewViewsSteadyStateElisionsTest, FirstMouseClickFocusesOnly) {
EXPECT_FALSE(omnibox_view()->IsSelectAll());
SendMouseClick(0);
EXPECT_TRUE(IsElidedUrlDisplayed());
EXPECT_TRUE(omnibox_view()->IsSelectAll());
EXPECT_TRUE(omnibox_view()->HasFocus());
}
TEST_F(OmniboxViewViewsSteadyStateElisionsTest, NegligibleDragKeepsElisions) {
gfx::Point click_point = GetPointInTextAtXOffset(2 * kCharacterWidth);
omnibox_textfield()->OnMousePressed(
CreateMouseEvent(ui::ET_MOUSE_PRESSED, click_point));
// Offset the drag and release point by an insignificant 2 px.
gfx::Point drag_point = click_point;
drag_point.Offset(2, 0);
omnibox_textfield()->OnMouseDragged(
CreateMouseEvent(ui::ET_MOUSE_DRAGGED, drag_point));
omnibox_textfield()->OnMouseReleased(
CreateMouseEvent(ui::ET_MOUSE_RELEASED, drag_point));
// Expect that after a negligible drag and release, everything is selected.
EXPECT_TRUE(IsElidedUrlDisplayed());
EXPECT_TRUE(omnibox_view()->IsSelectAll());
EXPECT_TRUE(omnibox_view()->HasFocus());
}
TEST_F(OmniboxViewViewsSteadyStateElisionsTest, CaretPlacementByMouse) {
SendMouseClick(0);
// Advance the clock 5 seconds so the second click is not interpreted as a
// double click.
clock()->Advance(base::TimeDelta::FromSeconds(5));
// Second click should unelide only on mouse release.
omnibox_textfield()->OnMousePressed(CreateMouseEvent(
ui::ET_MOUSE_PRESSED, GetPointInTextAtXOffset(2 * kCharacterWidth)));
EXPECT_TRUE(IsElidedUrlDisplayed());
omnibox_textfield()->OnMouseReleased(CreateMouseEvent(
ui::ET_MOUSE_RELEASED, GetPointInTextAtXOffset(2 * kCharacterWidth)));
ExpectFullUrlDisplayed();
// Verify the cursor position is https://www.ex|ample.com. It should be
// between 'x' and 'a', because the click was after the second character of
// the unelided text "example.com".
size_t start, end;
omnibox_view()->GetSelectionBounds(&start, &end);
EXPECT_EQ(14U, start);
EXPECT_EQ(14U, end);
}
TEST_F(OmniboxViewViewsSteadyStateElisionsTest, MouseDoubleClick) {
SendMouseClick(4 * kCharacterWidth);
// Second click without advancing the clock should be a double-click, which
// should do a single word selection and unelide the text on mousedown.
omnibox_textfield()->OnMousePressed(CreateMouseEvent(
ui::ET_MOUSE_PRESSED, GetPointInTextAtXOffset(4 * kCharacterWidth)));
ExpectFullUrlDisplayed();
// Verify that the selection is https://www.|example|.com, since the
// double-click after the fourth character of the unelided text "example.com".
size_t start, end;
omnibox_view()->GetSelectionBounds(&start, &end);
EXPECT_EQ(12U, start);
EXPECT_EQ(19U, end);
}
TEST_F(OmniboxViewViewsSteadyStateElisionsTest, MouseSingleThenDoubleClick) {
EXPECT_TRUE(IsElidedUrlDisplayed());
auto point = GetPointInTextAtXOffset(4 * kCharacterWidth);
SendMouseClickAtPoint(point, 1);
EXPECT_TRUE(IsElidedUrlDisplayed());
EXPECT_EQ(base::ASCIIToUTF16("example.com"), omnibox_view()->GetText());
// Verify that the whole full URL is selected.
EXPECT_TRUE(omnibox_view()->IsSelectAll());
// Advance the clock 5 seconds so the next click is not interpreted as a
// double click.
clock()->Advance(base::TimeDelta::FromSeconds(5));
// Double click
SendMouseClickAtPoint(point, 1);
ExpectFullUrlDisplayed();
SendMouseClickAtPoint(point, 2);
ExpectFullUrlDisplayed();
// Verify that the selection is https://www.|example|.com, since the
// double-click after the fourth character of the unelided text "example.com".
size_t start, end;
omnibox_view()->GetSelectionBounds(&start, &end);
EXPECT_EQ(12U, start);
EXPECT_EQ(19U, end);
}
TEST_F(OmniboxViewViewsSteadyStateElisionsTest, MouseSingleThenRightClick) {
EXPECT_TRUE(IsElidedUrlDisplayed());
auto point = GetPointInTextAtXOffset(4 * kCharacterWidth);
SendMouseClickAtPoint(point, 1);
EXPECT_TRUE(IsElidedUrlDisplayed());
EXPECT_EQ(base::ASCIIToUTF16("example.com"), omnibox_view()->GetText());
// Verify that the whole full URL is selected.
EXPECT_TRUE(omnibox_view()->IsSelectAll());
// Advance the clock 5 seconds so the next click is not interpreted as a
// double click.
clock()->Advance(base::TimeDelta::FromSeconds(5));
// Right click
SendMouseClickAtPoint(point, 1, ui::EF_RIGHT_MOUSE_BUTTON);
EXPECT_TRUE(IsElidedUrlDisplayed());
EXPECT_TRUE(omnibox_view()->IsSelectAll());
EXPECT_TRUE(omnibox_view()->HasFocus());
}
TEST_F(OmniboxViewViewsSteadyStateElisionsTest, MouseTripleClick) {
auto point = GetPointInTextAtXOffset(4 * kCharacterWidth);
SendMouseClickAtPoint(point, 1);
SendMouseClickAtPoint(point, 2);
SendMouseClickAtPoint(point, 3);
ExpectFullUrlDisplayed();
// Verify that the whole full URL is selected.
EXPECT_TRUE(omnibox_view()->IsSelectAll());
size_t start, end;
omnibox_view()->GetSelectionBounds(&start, &end);
EXPECT_EQ(0U, start);
EXPECT_EQ(24U, end);
}
TEST_F(OmniboxViewViewsSteadyStateElisionsTest, MouseClickDrag) {
omnibox_textfield()->OnMousePressed(CreateMouseEvent(
ui::ET_MOUSE_PRESSED, GetPointInTextAtXOffset(2 * kCharacterWidth)));
EXPECT_TRUE(IsElidedUrlDisplayed());
// Expect that during the drag, the URL is still elided.
omnibox_textfield()->OnMouseDragged(CreateMouseEvent(
ui::ET_MOUSE_DRAGGED, GetPointInTextAtXOffset(4 * kCharacterWidth)));
EXPECT_TRUE(IsElidedUrlDisplayed());
// Expect that ex|am|ple.com is the drag selected portion while dragging.
size_t start, end;
omnibox_view()->GetSelectionBounds(&start, &end);
EXPECT_EQ(2U, start);
EXPECT_EQ(4U, end);
omnibox_textfield()->OnMouseReleased(CreateMouseEvent(
ui::ET_MOUSE_RELEASED, GetPointInTextAtXOffset(4 * kCharacterWidth)));
ExpectFullUrlDisplayed();
// Expect that https://www.ex|am|ple.com is the selected portion after the
// user releases the mouse.
omnibox_view()->GetSelectionBounds(&start, &end);
EXPECT_EQ(14U, start);
EXPECT_EQ(16U, end);
}
TEST_F(OmniboxViewViewsSteadyStateElisionsTest,
MouseClickDragToBeginningSelectingText) {
// Backwards drag-select this portion of the elided URL: |exam|ple.com
omnibox_textfield()->OnMousePressed(CreateMouseEvent(
ui::ET_MOUSE_PRESSED, GetPointInTextAtXOffset(4 * kCharacterWidth)));
omnibox_textfield()->OnMouseDragged(CreateMouseEvent(
ui::ET_MOUSE_DRAGGED, GetPointInTextAtXOffset(0 * kCharacterWidth)));
omnibox_textfield()->OnMouseReleased(CreateMouseEvent(
ui::ET_MOUSE_RELEASED, GetPointInTextAtXOffset(0 * kCharacterWidth)));
ExpectFullUrlDisplayed();
// Since the selection did not look like a URL, expect the following selected
// selected portion after the user releases the mouse:
// https://www.|exam|ple.com
size_t start, end;
omnibox_view()->GetSelectionBounds(&start, &end);
EXPECT_EQ(16U, start);
EXPECT_EQ(12U, end);
}
TEST_F(OmniboxViewViewsSteadyStateElisionsTest,
MouseClickDragToBeginningSelectingURL) {
// Backwards drag-select this portion of the elided URL: |example.co|m
omnibox_textfield()->OnMousePressed(CreateMouseEvent(
ui::ET_MOUSE_PRESSED, GetPointInTextAtXOffset(10 * kCharacterWidth)));
omnibox_textfield()->OnMouseDragged(CreateMouseEvent(
ui::ET_MOUSE_DRAGGED, GetPointInTextAtXOffset(0 * kCharacterWidth)));
omnibox_textfield()->OnMouseReleased(CreateMouseEvent(
ui::ET_MOUSE_RELEASED, GetPointInTextAtXOffset(0 * kCharacterWidth)));
ExpectFullUrlDisplayed();
// Since the selection does look like a URL, expect the following selected
// selected portion after the user releases the mouse:
// |https://www.example.co|m
size_t start, end;
omnibox_view()->GetSelectionBounds(&start, &end);
EXPECT_EQ(22U, start);
EXPECT_EQ(0U, end);
}
TEST_F(OmniboxViewViewsSteadyStateElisionsTest, MouseDoubleClickDrag) {
// Expect that after a double-click after the third character of the elided
// text, the text is unelided, and https://www.|example|.com is selected.
SendMouseClick(4 * kCharacterWidth);
omnibox_textfield()->OnMousePressed(CreateMouseEvent(
ui::ET_MOUSE_PRESSED, GetPointInTextAtXOffset(4 * kCharacterWidth)));
ExpectFullUrlDisplayed();
size_t start, end;
omnibox_view()->GetSelectionBounds(&start, &end);
EXPECT_EQ(12U, start);
EXPECT_EQ(19U, end);
// Expect that negligible drags are ignored immediately after unelision, as
// the text has likely shifted, and we don't want to accidentally change the
// selection.
gfx::Point drag_point = GetPointInTextAtXOffset(4 * kCharacterWidth);
drag_point.Offset(1, 1); // Offset test point one pixel in each dimension.
omnibox_textfield()->OnMouseDragged(
CreateMouseEvent(ui::ET_MOUSE_DRAGGED, drag_point));
omnibox_view()->GetSelectionBounds(&start, &end);
EXPECT_EQ(12U, start);
EXPECT_EQ(19U, end);
// Expect that dragging to the fourth character of the full URL (between the
// the 'p' and the 's' of https), will word-select the scheme, subdomain, and
// domain, so the new selection will be |https://www.example|.com. The
// expected selection is backwards, since we are dragging the mouse from the
// domain to the scheme.
omnibox_textfield()->OnMouseDragged(CreateMouseEvent(
ui::ET_MOUSE_DRAGGED, GetPointInTextAtXOffset(2 * kCharacterWidth)));
ExpectFullUrlDisplayed();
omnibox_view()->GetSelectionBounds(&start, &end);
EXPECT_EQ(19U, start);
EXPECT_EQ(0U, end);
// Expect the selection to stay the same after mouse-release.
omnibox_textfield()->OnMouseReleased(CreateMouseEvent(
ui::ET_MOUSE_RELEASED, GetPointInTextAtXOffset(2 * kCharacterWidth)));
ExpectFullUrlDisplayed();
omnibox_view()->GetSelectionBounds(&start, &end);
EXPECT_EQ(19U, start);
EXPECT_EQ(0U, end);
}
TEST_F(OmniboxViewViewsSteadyStateElisionsTest, ReelideOnBlur) {
// Double-click should unelide the URL by making a partial selection.
SendMouseClick(4 * kCharacterWidth);
SendMouseClick(4 * kCharacterWidth);
ExpectFullUrlDisplayed();
BlurOmnibox();
EXPECT_TRUE(IsElidedUrlDisplayed());
}
TEST_F(OmniboxViewViewsSteadyStateElisionsTest, DontReelideOnBlurIfEdited) {
// Double-click should unelide the URL by making a partial selection.
SendMouseClick(4 * kCharacterWidth);
SendMouseClick(4 * kCharacterWidth);
ExpectFullUrlDisplayed();
// Since the domain word is selected, pressing 'a' should replace the domain.
ui::KeyEvent char_event(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::DomCode::US_A, 0,
ui::DomKey::FromCharacter('a'),
ui::EventTimeForNow());
omnibox_textfield()->InsertChar(char_event);
EXPECT_EQ(base::ASCIIToUTF16("https://www.a.com/"),
omnibox_view()->GetText());
EXPECT_TRUE(omnibox_view()->model()->user_input_in_progress());
// Now that we've edited the text, blurring should not re-elide the URL.
BlurOmnibox();
EXPECT_EQ(base::ASCIIToUTF16("https://www.a.com/"),
omnibox_view()->GetText());
EXPECT_TRUE(omnibox_view()->model()->user_input_in_progress());
}
TEST_F(OmniboxViewViewsSteadyStateElisionsTest,
DontReelideOnBlurIfWidgetDeactivated) {
SendMouseClick(0);
SendMouseClick(0);
ExpectFullUrlDisplayed();
// Create a different Widget that will take focus away from the test widget
// containing our test Omnibox.
std::unique_ptr<views::Widget> other_widget = CreateTestWidget();
other_widget->Show();
ExpectFullUrlDisplayed();
omnibox_view()->GetWidget()->Activate();
ExpectFullUrlDisplayed();
}
TEST_F(OmniboxViewViewsSteadyStateElisionsTest, SaveSelectAllOnBlurAndRefocus) {
SendMouseClick(0);
EXPECT_TRUE(IsElidedUrlDisplayed());
EXPECT_TRUE(omnibox_view()->IsSelectAll());
// Blurring and refocusing should preserve a select-all state.
BlurOmnibox();
omnibox_view()->RequestFocus();
EXPECT_TRUE(omnibox_view()->HasFocus());
EXPECT_TRUE(IsElidedUrlDisplayed());
EXPECT_TRUE(omnibox_view()->IsSelectAll());
}
TEST_F(OmniboxViewViewsSteadyStateElisionsTest, UnelideFromModel) {
EXPECT_TRUE(IsElidedUrlDisplayed());
omnibox_view()->model()->Unelide();
EXPECT_TRUE(omnibox_view()->IsSelectAll());
size_t start, end;
omnibox_view()->GetSelectionBounds(&start, &end);
EXPECT_EQ(24U, start);
EXPECT_EQ(0U, end);
ExpectFullUrlDisplayed();
}
// Checks that |view|'s current display rect and offset does not display
// |path|, and also does not display |subdomain_and_scheme| if
// |should_elide_to_registrable_domain| is true.
//
// |subdomain_and_scheme| is assumed to be a prefix of |hostname_and_scheme|.
// |subdomain_and_scheme| and |subdomain| should include a trailing ".", and
// |path| should include a leading "/".
void ExpectElidedToSimplifiedDomain(TestingOmniboxView* view,
const base::string16& scheme,
const base::string16& subdomain,
const base::string16& hostname_and_scheme,
const base::string16& path,
bool should_elide_to_registrable_domain) {
gfx::RenderText* render_text = view->GetRenderText();
gfx::Rect subdomain_and_scheme_rect;
for (const auto& rect : render_text->GetSubstringBounds(
gfx::Range(0, scheme.size() + subdomain.size()))) {
subdomain_and_scheme_rect.Union(rect);
}
gfx::Rect path_rect;
for (const auto& rect : render_text->GetSubstringBounds(
gfx::Range(hostname_and_scheme.size(),
hostname_and_scheme.size() + path.size()))) {
path_rect.Union(rect);
}
EXPECT_FALSE(render_text->display_rect().Contains(path_rect));
if (should_elide_to_registrable_domain) {
EXPECT_FALSE(
render_text->display_rect().Contains(subdomain_and_scheme_rect));
gfx::Rect registrable_domain_rect;
for (const auto& rect : render_text->GetSubstringBounds(gfx::Range(
scheme.size() + subdomain.size(), hostname_and_scheme.size()))) {
registrable_domain_rect.Union(rect);
}
EXPECT_TRUE(render_text->display_rect().Contains(registrable_domain_rect));
// The text should be scrolled to push the scheme and subdomain offscreen,
// so that the text starts at the registrable domain. Note that this code
// computes the expected offset by comparing x() values rather than
// comparing based on widths (for example, it wouldn't work to check that
// the display offset is equal to |subdomain_and_scheme_rect|'s width). This
// is because GetSubstringBounds() rounds outward, so the width of
// |subdomain_and_scheme_rect| could slightly overlap
// |registrable_domain_rect|.
// In the RTL UI case, the offset instead has to push the path offscreen to
// the right, so we check offset equals the width of the path rectangle.
if (base::i18n::IsRTL()) {
CheckEqualsWithMarginOne(path_rect.width(),
render_text->GetUpdatedDisplayOffset().x());
} else {
EXPECT_EQ(registrable_domain_rect.x() - subdomain_and_scheme_rect.x(),
-1 * render_text->GetUpdatedDisplayOffset().x());
}
// The scheme and subdomain should be transparent.
EXPECT_EQ(SK_ColorTRANSPARENT, view->GetLatestColorForRange(gfx::Range(
0, scheme.size() + subdomain.size())));
} else {
// When elision to registrable domain is disabled, the scheme should be
// hidden but the subdomain should not be.
EXPECT_FALSE(
render_text->display_rect().Contains(subdomain_and_scheme_rect));
gfx::Rect hostname_rect;
for (const auto& rect : render_text->GetSubstringBounds(
gfx::Range(scheme.size(), hostname_and_scheme.size()))) {
hostname_rect.Union(rect);
}
// The text should be scrolled to push the scheme offscreen, so that the
// text starts at the subdomain. As above, it's important to compute the
// expected offset with x() values instead of width()s, since the width()s
// of different adjacent substring bounds could overlap.
// In the RTL UI case, the offset instead has to push the path offscreen to
// the right, so we check offset equals the width of the path rectangle.
if (base::i18n::IsRTL()) {
CheckEqualsWithMarginOne(path_rect.width(),
render_text->GetUpdatedDisplayOffset().x());
} else {
EXPECT_EQ(hostname_rect.x() - subdomain_and_scheme_rect.x(),
-1 * render_text->GetUpdatedDisplayOffset().x());
}
// The scheme should be transparent.
EXPECT_EQ(SK_ColorTRANSPARENT,
view->GetLatestColorForRange(gfx::Range(0, scheme.size())));
}
// The path should be transparent.
EXPECT_EQ(SK_ColorTRANSPARENT,
view->GetLatestColorForRange(
gfx::Range(hostname_and_scheme.size(),
hostname_and_scheme.size() + path.size())));
}
// Checks that |render_text|'s current display rect and offset displays all of
// |display_url|, starting at the leading edge.
void ExpectUnelidedFromSimplifiedDomain(gfx::RenderText* render_text,
const gfx::Range& display_url) {
gfx::Rect unelided_rect;
for (const auto& rect : render_text->GetSubstringBounds(display_url)) {
unelided_rect.Union(rect);
}
EXPECT_TRUE(render_text->display_rect().Contains(unelided_rect));
// |display_url| should be at the leading edge of |render_text|'s display
// rect for LTR UI, or at the rightmost side of the omnibox for RTL UI.
if (base::i18n::IsRTL()) {
CheckEqualsWithMarginOne(
unelided_rect.x(),
render_text->display_rect().right() - unelided_rect.width());
} else {
EXPECT_EQ(unelided_rect.x(), render_text->display_rect().x());
}
}
// Returns true if |render_text|'s current display rect and offset display at
// least part of |path_bounds|, but not the full |display_url|. This is useful
// for checking the displayed text partway through an animation.
bool IsPartlyThroughSimplifiedDomainElision(gfx::RenderText* render_text,
const base::string16& display_url,
const gfx::Range& path_bounds) {
// First check if all of |display_url| is showing; if it is, we aren't partly
// elided.
gfx::Rect unelided_rect;
for (const auto& rect :
render_text->GetSubstringBounds(gfx::Range(0, display_url.size()))) {
unelided_rect.Union(rect);
}
if (render_text->display_rect().Contains(unelided_rect) &&
render_text->GetUpdatedDisplayOffset().x() == 0) {
return false;
}
// Now check if at least some of |path| is visible.
gfx::Rect path_rect;
for (const auto& rect : render_text->GetSubstringBounds(path_bounds)) {
path_rect.Union(rect);
}
return render_text->display_rect().Intersects(path_rect);
}
class OmniboxViewViewsNoSimplifiedDomainTest : public OmniboxViewViewsTest {
public:
OmniboxViewViewsNoSimplifiedDomainTest()
: OmniboxViewViewsTest(
{},
{omnibox::kHideSteadyStateUrlPathQueryAndRefOnInteraction,
omnibox::kRevealSteadyStateUrlPathQueryAndRefOnHover}) {}
OmniboxViewViewsNoSimplifiedDomainTest(
const OmniboxViewViewsNoSimplifiedDomainTest&) = delete;
OmniboxViewViewsNoSimplifiedDomainTest& operator=(
const OmniboxViewViewsNoSimplifiedDomainTest&) = delete;
};
// Tests that when no simplified domain field trials are enabled, URL components
// are not hidden. Regression test for https://crbug.com/1093748.
TEST_F(OmniboxViewViewsNoSimplifiedDomainTest, UrlNotSimplifiedByDefault) {
SetUpSimplifiedDomainTest();
omnibox_view()->EmphasizeURLComponents();
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
omnibox_view()->GetRenderText(),
gfx::Range(0, kSimplifiedDomainDisplayUrl.size())));
}
class OmniboxViewViewsRevealOnHoverTest
: public OmniboxViewViewsTest,
public ::testing::WithParamInterface<std::pair<bool, bool>> {
public:
OmniboxViewViewsRevealOnHoverTest()
: OmniboxViewViewsTest(
GetParam().first
? std::vector<base::Feature>(
{omnibox::kRevealSteadyStateUrlPathQueryAndRefOnHover,
omnibox::kElideToRegistrableDomain})
: std::vector<base::Feature>(
{omnibox::kRevealSteadyStateUrlPathQueryAndRefOnHover}),
{},
GetParam().second) {}
OmniboxViewViewsRevealOnHoverTest(const OmniboxViewViewsRevealOnHoverTest&) =
delete;
OmniboxViewViewsRevealOnHoverTest& operator=(
const OmniboxViewViewsRevealOnHoverTest&) = delete;
protected:
bool ShouldElideToRegistrableDomain() { return GetParam().first; }
};
INSTANTIATE_TEST_SUITE_P(OmniboxViewViewsRevealOnHoverTest,
OmniboxViewViewsRevealOnHoverTest,
::testing::ValuesIn({std::make_pair(true, false),
std::make_pair(false, false),
std::make_pair(true, true),
std::make_pair(false, true)}));
// Tests the field trial variation that shows a simplified domain by default and
// reveals the unsimplified URL on hover.
TEST_P(OmniboxViewViewsRevealOnHoverTest, HoverAndExit) {
SetUpSimplifiedDomainTest();
gfx::RenderText* render_text = omnibox_view()->GetRenderText();
ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
omnibox_view(), kSimplifiedDomainDisplayUrlScheme,
kSimplifiedDomainDisplayUrlSubdomain,
kSimplifiedDomainDisplayUrlHostnameAndScheme,
kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
// As soon as the mouse hovers over the omnibox, the unelide animation should
// start running.
omnibox_view()->OnMouseMoved(CreateMouseEvent(ui::ET_MOUSE_MOVED, {0, 0}));
OmniboxViewViews::ElideAnimation* hover_animation =
omnibox_view()->GetHoverElideOrUnelideAnimationForTesting();
ASSERT_TRUE(hover_animation);
EXPECT_TRUE(hover_animation->IsAnimating());
// Advance the clock through the animation.
gfx::AnimationContainerElement* hover_animation_as_element =
static_cast<gfx::AnimationContainerElement*>(
hover_animation->GetAnimationForTesting());
hover_animation_as_element->SetStartTime(base::TimeTicks());
hover_animation_as_element->Step(
base::TimeTicks() +
base::TimeDelta::FromMilliseconds(
OmniboxFieldTrial::UnelideURLOnHoverThresholdMs()));
// After the extended hover threshold has elapsed, the display text shouldn't
// have changed yet.
ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
omnibox_view(), kSimplifiedDomainDisplayUrlScheme,
kSimplifiedDomainDisplayUrlSubdomain,
kSimplifiedDomainDisplayUrlHostnameAndScheme,
kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
// Now advance through the unelision and check the display text. We assume
// that the animation takes less than 1 second.
hover_animation_as_element->Step(base::TimeTicks() +
base::TimeDelta::FromSeconds(1));
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
render_text, gfx::Range(0, kSimplifiedDomainDisplayUrl.size())));
EXPECT_FALSE(hover_animation->IsAnimating());
// Check that the path and subdomain are not transparent.
EXPECT_NE(SK_ColorTRANSPARENT,
omnibox_view()->GetLatestColorForRange(
gfx::Range(kSimplifiedDomainDisplayUrlHostnameAndScheme.size(),
kSimplifiedDomainDisplayUrl.size())));
EXPECT_NE(SK_ColorTRANSPARENT,
omnibox_view()->GetLatestColorForRange(gfx::Range(
0, kSimplifiedDomainDisplayUrlSubdomainAndScheme.size())));
// Now exit the mouse. At this point the elision animation should run.
omnibox_view()->OnMouseExited(CreateMouseEvent(ui::ET_MOUSE_EXITED, {0, 0}));
EXPECT_TRUE(hover_animation->IsAnimating());
hover_animation_as_element = static_cast<gfx::AnimationContainerElement*>(
hover_animation->GetAnimationForTesting());
hover_animation_as_element->SetStartTime(base::TimeTicks());
// We assume that the animation takes less than 1 second.
hover_animation_as_element->Step(base::TimeTicks() +
base::TimeDelta::FromSeconds(1));
ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
omnibox_view(), kSimplifiedDomainDisplayUrlScheme,
kSimplifiedDomainDisplayUrlSubdomain,
kSimplifiedDomainDisplayUrlHostnameAndScheme,
kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
}
class OmniboxViewViewsHideOnInteractionAndRevealOnHoverTest
: public OmniboxViewViewsTest,
public ::testing::WithParamInterface<std::pair<bool, bool>> {
public:
OmniboxViewViewsHideOnInteractionAndRevealOnHoverTest()
: OmniboxViewViewsTest(
GetParam().first
? std::vector<base::Feature>(
{omnibox::kRevealSteadyStateUrlPathQueryAndRefOnHover,
omnibox::kHideSteadyStateUrlPathQueryAndRefOnInteraction,
omnibox::kElideToRegistrableDomain})
: std::vector<base::Feature>(
{omnibox::kRevealSteadyStateUrlPathQueryAndRefOnHover,
omnibox::
kHideSteadyStateUrlPathQueryAndRefOnInteraction}),
{},
GetParam().second) {}
OmniboxViewViewsHideOnInteractionAndRevealOnHoverTest(
const OmniboxViewViewsHideOnInteractionAndRevealOnHoverTest&) = delete;
OmniboxViewViewsHideOnInteractionAndRevealOnHoverTest& operator=(
const OmniboxViewViewsHideOnInteractionAndRevealOnHoverTest&) = delete;
protected:
bool ShouldElideToRegistrableDomain() { return GetParam().first; }
};
INSTANTIATE_TEST_SUITE_P(OmniboxViewViewsHideOnInteractionAndRevealOnHoverTest,
OmniboxViewViewsHideOnInteractionAndRevealOnHoverTest,
::testing::ValuesIn({std::make_pair(true, false),
std::make_pair(false, false),
std::make_pair(true, true),
std::make_pair(false, true)}));
// Tests the field trial variation that shows the simplified domain when the
// user interacts with the page and brings back the URL when the user hovers
// over the omnibox.
TEST_P(OmniboxViewViewsHideOnInteractionAndRevealOnHoverTest,
UserInteractionAndHover) {
SetUpSimplifiedDomainTest();
content::MockNavigationHandle navigation;
navigation.set_is_same_document(false);
omnibox_view()->DidFinishNavigation(&navigation);
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
omnibox_view()->GetRenderText(),
gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrl.size())));
// Simulate a user interaction and check that the fade-out animation runs.
omnibox_view()->DidGetUserInteraction(blink::WebKeyboardEvent());
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
EXPECT_TRUE(elide_animation->IsAnimating());
// Advance the clock through the fade-out animation; we assume that it takes
// less than 1s.
gfx::AnimationContainerElement* elide_as_element =
static_cast<gfx::AnimationContainerElement*>(
elide_animation->GetAnimationForTesting());
elide_as_element->SetStartTime(base::TimeTicks());
elide_as_element->Step(base::TimeTicks() + base::TimeDelta::FromSeconds(1));
ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
omnibox_view(), kSimplifiedDomainDisplayUrlScheme,
kSimplifiedDomainDisplayUrlSubdomain,
kSimplifiedDomainDisplayUrlHostnameAndScheme,
kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
// A second user interaction should not run the animation again.
omnibox_view()->DidGetUserInteraction(blink::WebKeyboardEvent());
EXPECT_FALSE(omnibox_view()
->GetElideAfterInteractionAnimationForTesting()
->IsAnimating());
// The URL should come back on hover.
omnibox_view()->OnMouseMoved(CreateMouseEvent(ui::ET_MOUSE_MOVED, {0, 0}));
OmniboxViewViews::ElideAnimation* unelide_animation =
omnibox_view()->GetHoverElideOrUnelideAnimationForTesting();
ASSERT_TRUE(unelide_animation);
EXPECT_TRUE(unelide_animation->IsAnimating());
gfx::AnimationContainerElement* unelide_as_element =
static_cast<gfx::AnimationContainerElement*>(
unelide_animation->GetAnimationForTesting());
unelide_as_element->SetStartTime(base::TimeTicks());
// Assume that the extended hover time + fade-in time is less than 2 seconds.
unelide_as_element->Step(base::TimeTicks() + base::TimeDelta::FromSeconds(2));
// The hover should bring back the full URL, including scheme and trivial
// subdomains.
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
omnibox_view()->GetRenderText(),
gfx::Range(0, kSimplifiedDomainDisplayUrl.size())));
// The path and scheme/subdomain should not be transparent.
EXPECT_NE(SK_ColorTRANSPARENT,
omnibox_view()->GetLatestColorForRange(
gfx::Range(kSimplifiedDomainDisplayUrlHostnameAndScheme.size(),
kSimplifiedDomainDisplayUrl.size())));
EXPECT_NE(SK_ColorTRANSPARENT,
omnibox_view()->GetLatestColorForRange(gfx::Range(
0, kSimplifiedDomainDisplayUrlSubdomainAndScheme.size())));
}
// Tests that mouse clicks do not count as user interactions and elide the URL.
TEST_P(OmniboxViewViewsHideOnInteractionAndRevealOnHoverTest, MouseClick) {
SetUpSimplifiedDomainTest();
content::MockNavigationHandle navigation;
navigation.set_is_same_document(false);
omnibox_view()->DidFinishNavigation(&navigation);
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
omnibox_view()->GetRenderText(),
gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrl.size())));
// Simulate a mouse click and check that the fade-out animation does not run.
blink::WebMouseEvent event;
event.SetType(blink::WebInputEvent::Type::kMouseDown);
omnibox_view()->DidGetUserInteraction(event);
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
EXPECT_FALSE(elide_animation);
}
// Tests that simplified domain elisions are re-applied when the omnibox's
// bounds change.
TEST_P(OmniboxViewViewsHideOnInteractionAndRevealOnHoverTest, BoundsChanged) {
SetUpSimplifiedDomainTest();
content::MockNavigationHandle navigation;
navigation.set_is_same_document(false);
omnibox_view()->DidFinishNavigation(&navigation);
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
omnibox_view()->GetRenderText(),
gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrl.size())));
// After the bounds change, the URL should remain unelided.
omnibox_view()->OnBoundsChanged(gfx::Rect());
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
omnibox_view()->GetRenderText(),
gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrl.size())));
// Hover over the omnibox and change the bounds during the animation. The
// animation should be cancelled and immediately transition back to the
// unelided URL.
omnibox_view()->OnMouseMoved(CreateMouseEvent(ui::ET_MOUSE_MOVED, {0, 0}));
OmniboxViewViews::ElideAnimation* unelide_animation =
omnibox_view()->GetHoverElideOrUnelideAnimationForTesting();
ASSERT_TRUE(unelide_animation);
EXPECT_TRUE(unelide_animation->IsAnimating());
omnibox_view()->OnBoundsChanged(gfx::Rect());
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
omnibox_view()->GetRenderText(),
gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrl.size())));
// Simulate a user interaction and change the bounds during the animation. The
// animation should be cancelled and immediately transition to the animation's
// end state (simplified domain).
omnibox_view()->DidGetUserInteraction(blink::WebKeyboardEvent());
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
ASSERT_TRUE(elide_animation);
EXPECT_TRUE(elide_animation->IsAnimating());
omnibox_view()->OnBoundsChanged(gfx::Rect());
ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
omnibox_view(), kSimplifiedDomainDisplayUrlScheme,
kSimplifiedDomainDisplayUrlSubdomain,
kSimplifiedDomainDisplayUrlHostnameAndScheme,
kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
}
// Tests that simplified domain elisions are re-applied when the omnibox's
// bounds change when only reveal-on-hover is enabled.
TEST_P(OmniboxViewViewsRevealOnHoverTest, BoundsChanged) {
SetUpSimplifiedDomainTest();
ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
omnibox_view(), kSimplifiedDomainDisplayUrlScheme,
kSimplifiedDomainDisplayUrlSubdomain,
kSimplifiedDomainDisplayUrlHostnameAndScheme,
kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
// After the bounds change, the URL should remain elided.
omnibox_view()->OnBoundsChanged(gfx::Rect());
ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
omnibox_view(), kSimplifiedDomainDisplayUrlScheme,
kSimplifiedDomainDisplayUrlSubdomain,
kSimplifiedDomainDisplayUrlHostnameAndScheme,
kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
// Hover over the omnibox and change the bounds during the animation. The
// animation should be cancelled and immediately transition back to the
// simplified domain.
omnibox_view()->OnMouseMoved(CreateMouseEvent(ui::ET_MOUSE_MOVED, {0, 0}));
OmniboxViewViews::ElideAnimation* unelide_animation =
omnibox_view()->GetHoverElideOrUnelideAnimationForTesting();
ASSERT_TRUE(unelide_animation);
EXPECT_TRUE(unelide_animation->IsAnimating());
omnibox_view()->OnBoundsChanged(gfx::Rect());
ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
omnibox_view(), kSimplifiedDomainDisplayUrlScheme,
kSimplifiedDomainDisplayUrlSubdomain,
kSimplifiedDomainDisplayUrlHostnameAndScheme,
kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
}
// Tests scheme and trivial subdomain elision when simplified domain field
// trials are enabled.
TEST_P(OmniboxViewViewsHideOnInteractionAndRevealOnHoverTest,
SchemeAndTrivialSubdomainElision) {
// Use custom setup code instead of SetUpSimplifiedDomainTest() to use a URL
// with a "www." prefix (a trivial subdomain).
const base::string16 kFullUrl =
base::ASCIIToUTF16("https://www.example.test/foo");
constexpr size_t kSchemeAndSubdomainSize = 12; // "https://www."
location_bar_model()->set_url(GURL("https://www.example.test/foo"));
location_bar_model()->set_url_for_display(kFullUrl);
omnibox_view()->model()->ResetDisplayTexts();
omnibox_view()->RevertAll();
omnibox_view()->OnThemeChanged();
content::MockNavigationHandle navigation;
navigation.set_is_same_document(false);
omnibox_view()->DidFinishNavigation(&navigation);
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
omnibox_view()->GetRenderText(),
gfx::Range(kSchemeAndSubdomainSize, kFullUrl.size())));
EXPECT_EQ(SK_ColorTRANSPARENT, omnibox_view()->GetLatestColorForRange(
gfx::Range(0, kSchemeAndSubdomainSize)));
// Hovering before user interaction should bring back the scheme and trivial
// subdomain.
omnibox_view()->OnMouseMoved(CreateMouseEvent(ui::ET_MOUSE_MOVED, {0, 0}));
OmniboxViewViews::ElideAnimation* unelide_animation =
omnibox_view()->GetHoverElideOrUnelideAnimationForTesting();
ASSERT_TRUE(unelide_animation);
EXPECT_TRUE(unelide_animation->IsAnimating());
gfx::AnimationContainerElement* unelide_as_element =
static_cast<gfx::AnimationContainerElement*>(
unelide_animation->GetAnimationForTesting());
unelide_as_element->SetStartTime(base::TimeTicks());
unelide_as_element->Step(base::TimeTicks() + base::TimeDelta::FromSeconds(2));
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
omnibox_view()->GetRenderText(), gfx::Range(0, kFullUrl.size())));
EXPECT_NE(SK_ColorTRANSPARENT, omnibox_view()->GetLatestColorForRange(
gfx::Range(0, kSchemeAndSubdomainSize)));
// After mousing out, the scheme should fade out again.
omnibox_view()->OnMouseExited(CreateMouseEvent(ui::ET_MOUSE_MOVED, {0, 0}));
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetHoverElideOrUnelideAnimationForTesting();
ASSERT_TRUE(elide_animation);
EXPECT_TRUE(elide_animation->IsAnimating());
gfx::AnimationContainerElement* elide_as_element =
static_cast<gfx::AnimationContainerElement*>(
elide_animation->GetAnimationForTesting());
elide_as_element->SetStartTime(base::TimeTicks());
elide_as_element->Step(base::TimeTicks() + base::TimeDelta::FromSeconds(2));
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
omnibox_view()->GetRenderText(),
gfx::Range(kSchemeAndSubdomainSize, kSimplifiedDomainDisplayUrl.size())));
EXPECT_EQ(SK_ColorTRANSPARENT, omnibox_view()->GetLatestColorForRange(
gfx::Range(0, kSchemeAndSubdomainSize)));
// Simulate a user interaction and check that the URL gets elided to the
// simplified domain.
omnibox_view()->DidGetUserInteraction(blink::WebKeyboardEvent());
elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
EXPECT_TRUE(elide_animation->IsAnimating());
elide_as_element = static_cast<gfx::AnimationContainerElement*>(
elide_animation->GetAnimationForTesting());
elide_as_element->SetStartTime(base::TimeTicks());
elide_as_element->Step(base::TimeTicks() + base::TimeDelta::FromSeconds(2));
// Use should_elide_to_registrable_domain=true here regardless of how the
// field trial is set because the "www." should be elided as a trivial
// subdomain.
ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
omnibox_view(), base::ASCIIToUTF16("https://"),
base::ASCIIToUTF16("www."),
base::ASCIIToUTF16("https://www.example.test"),
base::ASCIIToUTF16("/foo"),
/* should_elide_to_registrable_domain=*/true));
// Do another hover and check that the URL gets unelided to the full URL.
omnibox_view()->OnMouseMoved(CreateMouseEvent(ui::ET_MOUSE_MOVED, {0, 0}));
elide_animation = omnibox_view()->GetHoverElideOrUnelideAnimationForTesting();
ASSERT_TRUE(unelide_animation);
EXPECT_TRUE(unelide_animation->IsAnimating());
unelide_as_element = static_cast<gfx::AnimationContainerElement*>(
unelide_animation->GetAnimationForTesting());
unelide_as_element->SetStartTime(base::TimeTicks());
unelide_as_element->Step(base::TimeTicks() + base::TimeDelta::FromSeconds(2));
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
omnibox_view()->GetRenderText(), gfx::Range(0, kFullUrl.size())));
EXPECT_NE(SK_ColorTRANSPARENT, omnibox_view()->GetLatestColorForRange(
gfx::Range(0, kSchemeAndSubdomainSize)));
// And after another mouse exit, the URL should go back to the simplified
// domain.
omnibox_view()->OnMouseExited(CreateMouseEvent(ui::ET_MOUSE_MOVED, {0, 0}));
elide_animation = omnibox_view()->GetHoverElideOrUnelideAnimationForTesting();
ASSERT_TRUE(elide_animation);
EXPECT_TRUE(elide_animation->IsAnimating());
elide_as_element = static_cast<gfx::AnimationContainerElement*>(
elide_animation->GetAnimationForTesting());
elide_as_element->SetStartTime(base::TimeTicks());
elide_as_element->Step(base::TimeTicks() + base::TimeDelta::FromSeconds(2));
ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
omnibox_view(), base::ASCIIToUTF16("https://"),
base::ASCIIToUTF16("www."),
base::ASCIIToUTF16("https://www.example.test"),
base::ASCIIToUTF16("/foo"),
/* should_elide_to_registrable_domain=*/true));
EXPECT_EQ(SK_ColorTRANSPARENT, omnibox_view()->GetLatestColorForRange(
gfx::Range(0, kSchemeAndSubdomainSize)));
}
class OmniboxViewViewsHideOnInteractionTest
: public OmniboxViewViewsTest,
public ::testing::WithParamInterface<std::pair<bool, bool>> {
public:
OmniboxViewViewsHideOnInteractionTest()
: OmniboxViewViewsTest(
GetParam().first
? std::vector<base::Feature>(
{omnibox::kHideSteadyStateUrlPathQueryAndRefOnInteraction,
omnibox::kElideToRegistrableDomain})
: std::vector<base::Feature>(
{omnibox::
kHideSteadyStateUrlPathQueryAndRefOnInteraction}),
{},
GetParam().second) {}
OmniboxViewViewsHideOnInteractionTest(
const OmniboxViewViewsHideOnInteractionTest&) = delete;
OmniboxViewViewsHideOnInteractionTest& operator=(
const OmniboxViewViewsHideOnInteractionTest&) = delete;
protected:
bool ShouldElideToRegistrableDomain() { return GetParam().first; }
};
INSTANTIATE_TEST_SUITE_P(OmniboxViewViewsHideOnInteractionTest,
OmniboxViewViewsHideOnInteractionTest,
::testing::ValuesIn({std::make_pair(true, false),
std::make_pair(false, false),
std::make_pair(true, true),
std::make_pair(false, true)}));
// Tests the the "Always Show Full URLs" option works with the field trial
// variation that shows a simplified domain when the user interacts with the
// page.
TEST_P(OmniboxViewViewsHideOnInteractionTest, AlwaysShowFullURLs) {
// This test does setup itself and doesn't call SetUpSimplifiedDomainTest()
// because SetUpSimplifiedDomainTest() uses a URL with a foo.example.test
// hostname, and in this test we want to use a "www." subdomain to test that
// the URL is displayed properly when trivial subdomain elision is disabled.
const base::string16 kFullUrl =
base::ASCIIToUTF16("https://www.example.test/foo");
location_bar_model()->set_url(GURL(kFullUrl));
location_bar_model()->set_url_for_display(kFullUrl);
omnibox_view()->model()->ResetDisplayTexts();
omnibox_view()->RevertAll();
omnibox_view()->OnThemeChanged();
// Enable the "Always show full URLs" setting.
location_bar_model()->set_should_prevent_elision(true);
omnibox_view()->OnShouldPreventElisionChanged();
std::unique_ptr<content::WebContents> web_contents =
content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
omnibox_view()->OnTabChanged(web_contents.get());
EXPECT_EQ(kFullUrl, omnibox_view()->GetText());
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
omnibox_view()->GetRenderText(), gfx::Range(0, kFullUrl.size())));
// When the Always Show Full URLs pref is enabled, the omnibox view won't
// observe user interactions and elide the URL.
EXPECT_FALSE(omnibox_view()->web_contents());
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
EXPECT_FALSE(elide_animation);
}
// Tests the the "Always Show Full URLs" option works with the field trial
// variation that shows a simplified domain until the user hovers over the
// omnibox.
TEST_P(OmniboxViewViewsRevealOnHoverTest, AlwaysShowFullURLs) {
SetUpSimplifiedDomainTest();
// Enable the "Always show full URLs" setting.
location_bar_model()->set_should_prevent_elision(true);
omnibox_view()->OnShouldPreventElisionChanged();
// After a hover, there should be no animations running.
omnibox_view()->OnMouseMoved(CreateMouseEvent(ui::ET_MOUSE_MOVED, {0, 0}));
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetHoverElideOrUnelideAnimationForTesting();
EXPECT_FALSE(elide_animation);
omnibox_view()->OnMouseExited(CreateMouseEvent(ui::ET_MOUSE_MOVED, {0, 0}));
elide_animation = omnibox_view()->GetHoverElideOrUnelideAnimationForTesting();
EXPECT_FALSE(elide_animation);
}
// This test fixture enables the reveal-on-hover simplified domain field trial,
// and the hide-on-interaction variation when the parameter is true.
class OmniboxViewViewsRevealOnHoverAndMaybeHideOnInteractionTest
: public OmniboxViewViewsTest,
public ::testing::WithParamInterface<std::pair<bool, bool>> {
public:
OmniboxViewViewsRevealOnHoverAndMaybeHideOnInteractionTest()
: OmniboxViewViewsTest(
GetParam().first
? std::vector<base::Feature>(
{omnibox::kOmniboxContextMenuShowFullUrls,
omnibox::kRevealSteadyStateUrlPathQueryAndRefOnHover,
omnibox::
kHideSteadyStateUrlPathQueryAndRefOnInteraction})
: std::vector<base::Feature>(
{omnibox::kOmniboxContextMenuShowFullUrls,
omnibox::kRevealSteadyStateUrlPathQueryAndRefOnHover}),
{omnibox::kElideToRegistrableDomain},
GetParam().second) {}
OmniboxViewViewsRevealOnHoverAndMaybeHideOnInteractionTest(
const OmniboxViewViewsRevealOnHoverAndMaybeHideOnInteractionTest&) =
delete;
OmniboxViewViewsRevealOnHoverAndMaybeHideOnInteractionTest& operator=(
const OmniboxViewViewsRevealOnHoverAndMaybeHideOnInteractionTest&) =
delete;
protected:
bool IsHideOnInteractionEnabled() { return GetParam().first; }
};
INSTANTIATE_TEST_SUITE_P(
OmniboxViewViewsRevealOnHoverAndMaybeHideOnInteractionTest,
OmniboxViewViewsRevealOnHoverAndMaybeHideOnInteractionTest,
::testing::ValuesIn({std::make_pair(true, false),
std::make_pair(false, false),
std::make_pair(true, true),
std::make_pair(false, true)}));
// Tests that unsetting the "Always show full URLs" option begins showing/hiding
// the full URL appropriately when simplified domain field trials are enabled.
// This test has kElideToRegistrableDomain disabled so that we can check that
// www is elided when the option is unset but other subdomains are not.
TEST_P(OmniboxViewViewsRevealOnHoverAndMaybeHideOnInteractionTest,
UnsetAlwaysShowFullURLs) {
// This test does setup itself and doesn't call SetUpSimplifiedDomainTest()
// because SetUpSimplifiedDomainTest() uses a URL with a foo.example.test
// hostname, and in this test we want to use a "www." subdomain to test that
// the URL is displayed properly when trivial subdomain elision is disabled.
const base::string16 kFullUrl =
base::ASCIIToUTF16("https://www.example.test/foo");
location_bar_model()->set_url(GURL(kFullUrl));
location_bar_model()->set_url_for_display(kFullUrl);
omnibox_view()->model()->ResetDisplayTexts();
omnibox_view()->RevertAll();
omnibox_view()->OnThemeChanged();
gfx::RenderText* render_text = omnibox_view()->GetRenderText();
// Enable the "Always show full URLs" setting.
location_bar_model()->set_should_prevent_elision(true);
omnibox_view()->OnShouldPreventElisionChanged();
std::unique_ptr<content::WebContents> web_contents =
content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
omnibox_view()->OnTabChanged(web_contents.get());
EXPECT_EQ(base::ASCIIToUTF16("https://www.example.test/foo"),
omnibox_view()->GetText());
// Now toggle the preference and check that the animations run as expected.
location_bar_model()->set_should_prevent_elision(false);
location_bar_model()->set_url_for_display(
base::ASCIIToUTF16("https://www.example.test/foo"));
omnibox_view()->OnShouldPreventElisionChanged();
// When simplified domain field trials are enabled, LocationBarModelImpl
// doesn't do any elision, leaving it all up to OmniboxViewViews, so the text
// returned from LocationBarModelImpl is the same even though the preference
// has changed.
EXPECT_EQ(base::ASCIIToUTF16("https://www.example.test/foo"),
omnibox_view()->GetText());
if (IsHideOnInteractionEnabled()) {
ExpectUnelidedFromSimplifiedDomain(
render_text,
gfx::Range(std::string("https://www.").size(), kFullUrl.size()));
// Simulate a user interaction and check the fade-out animation.
omnibox_view()->DidGetUserInteraction(blink::WebKeyboardEvent());
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
ASSERT_TRUE(elide_animation);
EXPECT_TRUE(elide_animation->IsAnimating());
} else {
// Even though kElideToRegistrableDomain is disabled, we expect to be elided
// to the registrable domain because the www subdomain is considered
// trivial.
ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
omnibox_view(), base::ASCIIToUTF16("https://"),
base::ASCIIToUTF16("www."),
base::ASCIIToUTF16("https://www.example.test"),
base::ASCIIToUTF16("/foo"),
true /* should elide to registrable domain */));
}
// Simulate a hover event and check the elide/unelide animations. This
// should happen the same regardless of whether hide-on-interaction is
// enabled.
omnibox_view()->OnMouseMoved(CreateMouseEvent(ui::ET_MOUSE_MOVED, {0, 0}));
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetHoverElideOrUnelideAnimationForTesting();
ASSERT_TRUE(elide_animation);
EXPECT_TRUE(elide_animation->IsAnimating());
// Advance the animation, so the visible URL changes.
gfx::AnimationContainerElement* elide_as_element =
elide_animation->GetAnimationForTesting();
elide_as_element->SetStartTime(base::TimeTicks());
elide_as_element->Step(base::TimeTicks() + base::TimeDelta::FromSeconds(1));
omnibox_view()->OnMouseExited(CreateMouseEvent(ui::ET_MOUSE_MOVED, {0, 0}));
elide_animation = omnibox_view()->GetHoverElideOrUnelideAnimationForTesting();
ASSERT_TRUE(elide_animation);
EXPECT_TRUE(elide_animation->IsAnimating());
}
// Tests that in the hide-on-interaction field trial, when the path changes
// while being elided, the animation is stopped.
TEST_P(OmniboxViewViewsHideOnInteractionAndRevealOnHoverTest,
PathChangeDuringAnimation) {
SetUpSimplifiedDomainTest();
content::MockNavigationHandle navigation;
navigation.set_is_same_document(false);
omnibox_view()->DidFinishNavigation(&navigation);
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
omnibox_view()->GetRenderText(),
gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrl.size())));
// Simulate a user interaction and check that the fade-out animation runs.
omnibox_view()->DidGetUserInteraction(blink::WebKeyboardEvent());
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
EXPECT_TRUE(elide_animation->IsAnimating());
// Change the path and check that the animation is cancelled.
location_bar_model()->set_url(GURL("https://foo.example.test/bar#bar"));
location_bar_model()->set_url_for_display(
base::ASCIIToUTF16("foo.example.test/bar#bar"));
omnibox_view()->model()->ResetDisplayTexts();
omnibox_view()->RevertAll();
EXPECT_FALSE(elide_animation->IsAnimating());
}
// Tests that vertical and horizontal positioning doesn't change when eliding
// to/from simplified domain. Regression test for https://crbug.com/1101674.
TEST_P(OmniboxViewViewsHideOnInteractionAndRevealOnHoverTest,
VerticalAndHorizontalPosition) {
SetUpSimplifiedDomainTest();
gfx::RenderText* render_text = omnibox_view()->GetRenderText();
const gfx::Rect& original_display_rect = render_text->display_rect();
content::MockNavigationHandle navigation;
navigation.set_is_same_document(false);
omnibox_view()->DidFinishNavigation(&navigation);
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
omnibox_view()->GetRenderText(),
gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrl.size())));
// After a navigation, the URL should not be elided to the simplified domain,
// and the display rect (including vertical and horizontal position) should be
// unchanged.
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
omnibox_view()->GetRenderText(),
gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrl.size())));
EXPECT_EQ(original_display_rect, render_text->display_rect());
// Simulate a user interaction to elide to simplified domain and advance
// through the animation; the vertical position should still be unchanged, and
// the text should still start at the some position (the same x value).
omnibox_view()->DidGetUserInteraction(blink::WebKeyboardEvent());
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
gfx::AnimationContainerElement* elide_as_element =
elide_animation->GetAnimationForTesting();
elide_as_element->SetStartTime(base::TimeTicks());
elide_as_element->Step(base::TimeTicks() + base::TimeDelta::FromSeconds(1));
const gfx::Rect& elided_display_rect = render_text->display_rect();
EXPECT_EQ(original_display_rect.y(), elided_display_rect.y());
EXPECT_EQ(original_display_rect.height(), elided_display_rect.height());
EXPECT_EQ(original_display_rect.x(), elided_display_rect.x());
// Now hover over the omnibox to trigger an unelide and check that the display
// rect (including vertical and horizontal position) is back to what it was
// originally.
omnibox_view()->OnMouseMoved(CreateMouseEvent(ui::ET_MOUSE_MOVED, {0, 0}));
OmniboxViewViews::ElideAnimation* unelide_animation =
omnibox_view()->GetHoverElideOrUnelideAnimationForTesting();
ASSERT_TRUE(unelide_animation);
gfx::AnimationContainerElement* unelide_as_element =
static_cast<gfx::AnimationContainerElement*>(
unelide_animation->GetAnimationForTesting());
unelide_as_element->SetStartTime(base::TimeTicks());
unelide_as_element->Step(base::TimeTicks() + base::TimeDelta::FromSeconds(1));
const gfx::Rect& unelided_display_rect = render_text->display_rect();
EXPECT_EQ(original_display_rect, unelided_display_rect);
}
// Tests that modifier keys don't count as user interactions in the
// hide-on-interaction field trial.
TEST_P(OmniboxViewViewsHideOnInteractionTest, ModifierKeys) {
SetUpSimplifiedDomainTest();
gfx::RenderText* render_text = omnibox_view()->GetRenderText();
content::MockNavigationHandle navigation;
navigation.set_is_same_document(false);
omnibox_view()->DidFinishNavigation(&navigation);
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
render_text, gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrl.size())));
// Simulate a user interaction with a modifier key and check that the elide
// animation doesn't run.
blink::WebKeyboardEvent event(
blink::WebInputEvent::Type::kRawKeyDown,
blink::WebInputEvent::kControlKey,
blink::WebInputEvent::GetStaticTimeStampForTests());
omnibox_view()->DidGetUserInteraction(event);
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
render_text, gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrl.size())));
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
EXPECT_FALSE(elide_animation);
}
// Tests that in the hide-on-interaction field trial, the URL is simplified on
// cross-document main-frame navigations, but not on same-document navigations.
TEST_P(OmniboxViewViewsHideOnInteractionTest, SameDocNavigations) {
SetUpSimplifiedDomainTest();
gfx::RenderText* render_text = omnibox_view()->GetRenderText();
{
content::MockNavigationHandle navigation;
navigation.set_is_same_document(false);
omnibox_view()->DidFinishNavigation(&navigation);
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
render_text, gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrl.size())));
}
// On a same-document navigation before the URL has been simplified, the URL
// should remain unsimplified.
{
// Set a longer URL to ensure that the full URL stays visible even if it's
// longer than the previous URL.
const base::string16 kUrlSuffix = base::ASCIIToUTF16("/foobar");
location_bar_model()->set_url(GURL(base::ASCIIToUTF16("https://") +
kSimplifiedDomainDisplayUrl +
kUrlSuffix));
location_bar_model()->set_url_for_display(kSimplifiedDomainDisplayUrl +
kUrlSuffix);
omnibox_view()->model()->ResetDisplayTexts();
omnibox_view()->RevertAll();
content::MockNavigationHandle navigation;
navigation.set_is_same_document(true);
omnibox_view()->DidFinishNavigation(&navigation);
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
render_text,
gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrl.size() + kUrlSuffix.size())));
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
EXPECT_FALSE(elide_animation);
}
// Simulate a user interaction to elide to the simplified domain.
omnibox_view()->DidGetUserInteraction(blink::WebKeyboardEvent());
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
ASSERT_TRUE(elide_animation);
EXPECT_TRUE(elide_animation->IsAnimating());
// On a cross-document main-frame navigation, the unsimplified URL should
// remain visible.
{
location_bar_model()->set_url(
GURL(base::ASCIIToUTF16("https://") + kSimplifiedDomainDisplayUrl));
location_bar_model()->set_url_for_display(kSimplifiedDomainDisplayUrl);
omnibox_view()->model()->ResetDisplayTexts();
omnibox_view()->RevertAll();
content::MockNavigationHandle navigation;
navigation.set_is_same_document(false);
omnibox_view()->DidFinishNavigation(&navigation);
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
render_text, gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrl.size())));
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
EXPECT_FALSE(elide_animation);
}
// Simulate another user interaction to elide to the simplified domain, and
// advance the clock all the way through the animation.
omnibox_view()->DidGetUserInteraction(blink::WebKeyboardEvent());
elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
ASSERT_TRUE(elide_animation);
EXPECT_TRUE(elide_animation->IsAnimating());
gfx::AnimationContainerElement* elide_as_element =
elide_animation->GetAnimationForTesting();
elide_as_element->SetStartTime(base::TimeTicks());
elide_as_element->Step(base::TimeTicks() + base::TimeDelta::FromSeconds(1));
ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
omnibox_view(), kSimplifiedDomainDisplayUrlScheme,
kSimplifiedDomainDisplayUrlSubdomain,
kSimplifiedDomainDisplayUrlHostnameAndScheme,
kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
// On a subsequent same-document main-frame navigation, the URL should remain
// elided to the simplified domain.
{
content::MockNavigationHandle navigation;
navigation.set_is_same_document(true);
omnibox_view()->DidFinishNavigation(&navigation);
ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
omnibox_view(), kSimplifiedDomainDisplayUrlScheme,
kSimplifiedDomainDisplayUrlSubdomain,
kSimplifiedDomainDisplayUrlHostnameAndScheme,
kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
ASSERT_TRUE(elide_animation);
EXPECT_FALSE(elide_animation->IsAnimating());
}
// On same-document main-frame fragment navigation, the URL should remain
// elided to the simplified domain.
{
content::MockNavigationHandle navigation;
navigation.set_is_same_document(true);
navigation.set_previous_url(GURL(kSimplifiedDomainDisplayUrl));
navigation.set_url(
GURL(kSimplifiedDomainDisplayUrl + base::ASCIIToUTF16("#foobar")));
omnibox_view()->DidFinishNavigation(&navigation);
ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
omnibox_view(), kSimplifiedDomainDisplayUrlScheme,
kSimplifiedDomainDisplayUrlSubdomain,
kSimplifiedDomainDisplayUrlHostnameAndScheme,
kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
ASSERT_TRUE(elide_animation);
EXPECT_FALSE(elide_animation->IsAnimating());
}
// On same-document main-frame non-fragment navigation, the URL shouldn't
// remain elided to the simplified domain.
{
content::MockNavigationHandle navigation;
navigation.set_is_same_document(true);
navigation.set_previous_url(GURL(kSimplifiedDomainDisplayUrl));
navigation.set_url(
GURL(kSimplifiedDomainDisplayUrl + base::ASCIIToUTF16("/foobar")));
omnibox_view()->DidFinishNavigation(&navigation);
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
render_text, gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrl.size())));
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
EXPECT_FALSE(elide_animation);
}
}
// Tests that in the hide-on-interaction field trial, a same-document navigation
// does not interfere with an animation that is currently running.
TEST_P(OmniboxViewViewsHideOnInteractionTest,
SameDocNavigationDuringAnimation) {
SetUpSimplifiedDomainTest();
gfx::RenderText* render_text = omnibox_view()->GetRenderText();
gfx::Range path_bounds(
kSimplifiedDomainDisplayUrl.find(kSimplifiedDomainDisplayUrlPath),
kSimplifiedDomainDisplayUrl.size());
content::MockNavigationHandle navigation;
navigation.set_is_same_document(false);
omnibox_view()->DidFinishNavigation(&navigation);
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
render_text, gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrl.size())));
// Simulate a user interaction to begin animating to the simplified domain.
omnibox_view()->DidGetUserInteraction(blink::WebKeyboardEvent());
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
ASSERT_TRUE(elide_animation);
EXPECT_TRUE(elide_animation->IsAnimating());
// Advance the clock by 1ms until the full URL is no longer showing, but we
// haven't finished eliding to the simplified domain yet. After a
// same-document navigation, we check that the URL is still in the same state
// (midway through elision) and that the animation is still running
// undisturbed. In other words, a same-document navigation shouldn't change
// anything when an animation is in progress.
gfx::AnimationContainerElement* elide_as_element =
elide_animation->GetAnimationForTesting();
elide_as_element->SetStartTime(base::TimeTicks());
bool is_midway_through_elision = false;
uint32_t step = 1;
while (!is_midway_through_elision) {
elide_as_element->Step(base::TimeTicks() +
base::TimeDelta::FromMilliseconds(++step));
is_midway_through_elision = IsPartlyThroughSimplifiedDomainElision(
render_text, kSimplifiedDomainDisplayUrl, path_bounds);
}
double animation_value =
elide_animation->GetAnimationForTesting()->GetCurrentValue();
content::MockNavigationHandle same_doc_navigation;
same_doc_navigation.set_is_same_document(true);
omnibox_view()->DidFinishNavigation(&same_doc_navigation);
elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
ASSERT_TRUE(elide_animation);
EXPECT_TRUE(elide_animation->IsAnimating());
EXPECT_EQ(animation_value,
elide_animation->GetAnimationForTesting()->GetCurrentValue());
// The current display text should reflect that the animation in progress: the
// full display URL shouldn't be still visible, but we haven't necessarily
// reached the end state (just the simplified domain visible) yet.
EXPECT_TRUE(IsPartlyThroughSimplifiedDomainElision(
render_text, kSimplifiedDomainDisplayUrl, path_bounds));
}
// Tests that gradient mask is set correctly.
TEST_P(OmniboxViewViewsHideOnInteractionTest, GradientMask) {
if (base::i18n::IsRTL()) {
// TODO(crbug.com/1101472): Re-enable this test once gradient mask is
// implemented for RTL UI.
return;
}
SetUpSimplifiedDomainTest();
gfx::RenderText* render_text = omnibox_view()->GetRenderText();
content::MockNavigationHandle navigation;
navigation.set_is_same_document(false);
omnibox_view()->DidFinishNavigation(&navigation);
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
render_text, gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrl.size())));
// Simulate a user interaction to begin animating to the simplified domain.
omnibox_view()->DidGetUserInteraction(blink::WebKeyboardEvent());
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
ASSERT_TRUE(elide_animation);
EXPECT_TRUE(elide_animation->IsAnimating());
// Advance the clock by 1ms until the full size gradient has been added.
gfx::AnimationContainerElement* elide_as_element =
elide_animation->GetAnimationForTesting();
elide_as_element->SetStartTime(base::TimeTicks());
uint32_t step = 1;
int max_gradient_width = OmniboxViewViews::kSmoothingGradientMaxWidth;
while (omnibox_view()->elide_animation_smoothing_rect_right_.width() <
max_gradient_width) {
elide_as_element->Step(base::TimeTicks() +
base::TimeDelta::FromMilliseconds(++step));
}
// If we are eliding from the left, the other side gradient should also be
// full size at this point, otherwise it should be 0.
if (ShouldElideToRegistrableDomain()) {
EXPECT_EQ(omnibox_view()->elide_animation_smoothing_rect_left_.width(),
max_gradient_width);
} else {
EXPECT_EQ(omnibox_view()->elide_animation_smoothing_rect_left_.width(), 0);
}
// Get a bounding box for the unelided section of the URL.
std::vector<gfx::Range> ranges_surrounding_simplified_domain;
gfx::Range simplified_range = omnibox_view()->GetSimplifiedDomainBounds(
&ranges_surrounding_simplified_domain);
gfx::Rect simplified_rect;
for (auto rect : render_text->GetSubstringBounds(simplified_range)) {
simplified_rect.Union(rect - render_text->GetLineOffset(0));
}
// Advance the animation until both gradients start shrinking.
while (omnibox_view()->elide_animation_smoothing_rect_left_.width() ==
max_gradient_width ||
omnibox_view()->elide_animation_smoothing_rect_right_.width() ==
max_gradient_width) {
elide_as_element->Step(base::TimeTicks() +
base::TimeDelta::FromMilliseconds(++step));
}
int offset = elide_animation->GetCurrentOffsetForTesting();
gfx::Rect display_rect = render_text->display_rect();
// Check the expected size and positions for both gradients.
EXPECT_EQ(omnibox_view()->elide_animation_smoothing_rect_left_.width(),
simplified_rect.x() + offset);
EXPECT_EQ(omnibox_view()->elide_animation_smoothing_rect_left_.x(),
display_rect.x());
EXPECT_EQ(omnibox_view()->elide_animation_smoothing_rect_right_.width(),
display_rect.right() - (simplified_rect.right() + offset));
EXPECT_EQ(omnibox_view()->elide_animation_smoothing_rect_right_.x(),
simplified_rect.right() + offset);
}
// Tests that in the hide-on-interaction field trial, a second user interaction
// does not interfere with an animation that is currently running. This is
// similar to SameDocNavigationDuringAnimation except that this test checks that
// a second user interaction (rather than a same-doc navigation) lets the
// animation proceed undisturbed.
TEST_P(OmniboxViewViewsHideOnInteractionTest, UserInteractionDuringAnimation) {
SetUpSimplifiedDomainTest();
gfx::RenderText* render_text = omnibox_view()->GetRenderText();
gfx::Range path_bounds(
kSimplifiedDomainDisplayUrl.find(kSimplifiedDomainDisplayUrlPath),
kSimplifiedDomainDisplayUrl.size());
content::MockNavigationHandle navigation;
navigation.set_is_same_document(false);
omnibox_view()->DidFinishNavigation(&navigation);
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
render_text, gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrl.size())));
// Simulate a user interaction to begin animating to the simplified domain.
omnibox_view()->DidGetUserInteraction(blink::WebKeyboardEvent());
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
ASSERT_TRUE(elide_animation);
EXPECT_TRUE(elide_animation->IsAnimating());
// Advance the clock by 1ms until the full URL is no longer showing, but we
// haven't finished eliding to the simplified domain yet. After a subsequent
// user interaction, we check that the URL is still in the same state (midway
// through elision) and that the animation is still running undisturbed. In
// other words, a second user interaction shouldn't change anything when an
// animation is in progress.
gfx::AnimationContainerElement* elide_as_element =
elide_animation->GetAnimationForTesting();
elide_as_element->SetStartTime(base::TimeTicks());
bool is_midway_through_elision = false;
uint32_t step = 1;
while (!is_midway_through_elision) {
elide_as_element->Step(base::TimeTicks() +
base::TimeDelta::FromMilliseconds(++step));
is_midway_through_elision = IsPartlyThroughSimplifiedDomainElision(
render_text, kSimplifiedDomainDisplayUrl, path_bounds);
}
double animation_value =
elide_animation->GetAnimationForTesting()->GetCurrentValue();
omnibox_view()->DidGetUserInteraction(blink::WebKeyboardEvent());
elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
ASSERT_TRUE(elide_animation);
EXPECT_TRUE(elide_animation->IsAnimating());
EXPECT_EQ(animation_value,
elide_animation->GetAnimationForTesting()->GetCurrentValue());
// The current display text should reflect that the animation in progress: the
// full display URL shouldn't be still visible, but we haven't necessarily
// reached the end state (just the simplified domain visible) yet.
EXPECT_TRUE(IsPartlyThroughSimplifiedDomainElision(
render_text, kSimplifiedDomainDisplayUrl, path_bounds));
}
// Tests that in the hide-on-interaction field trial, the path is not re-shown
// on subframe navigations.
TEST_P(OmniboxViewViewsHideOnInteractionTest, SubframeNavigations) {
SetUpSimplifiedDomainTest();
gfx::RenderText* render_text = omnibox_view()->GetRenderText();
{
content::MockNavigationHandle navigation;
navigation.set_is_same_document(false);
omnibox_view()->DidFinishNavigation(&navigation);
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
render_text, gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrl.size())));
}
// Simulate a user interaction to elide to the simplified domain, and advance
// the clock all the way through the animation.
omnibox_view()->DidGetUserInteraction(blink::WebKeyboardEvent());
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
ASSERT_TRUE(elide_animation);
EXPECT_TRUE(elide_animation->IsAnimating());
gfx::AnimationContainerElement* elide_as_element =
elide_animation->GetAnimationForTesting();
elide_as_element->SetStartTime(base::TimeTicks());
elide_as_element->Step(base::TimeTicks() + base::TimeDelta::FromSeconds(1));
ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
omnibox_view(), kSimplifiedDomainDisplayUrlScheme,
kSimplifiedDomainDisplayUrlSubdomain,
kSimplifiedDomainDisplayUrlHostnameAndScheme,
kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
// On a subframe navigation, the URL should remain elided to a simplified
// domain.
{
content::MockNavigationHandle navigation;
navigation.set_is_same_document(false);
std::unique_ptr<content::WebContents> web_contents =
content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
content::RenderFrameHostTester::For(web_contents->GetMainFrame())
->InitializeRenderFrameIfNeeded();
content::RenderFrameHost* subframe =
content::RenderFrameHostTester::For(web_contents->GetMainFrame())
->AppendChild("subframe");
navigation.set_render_frame_host(subframe);
omnibox_view()->DidFinishNavigation(&navigation);
ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
omnibox_view(), kSimplifiedDomainDisplayUrlScheme,
kSimplifiedDomainDisplayUrlSubdomain,
kSimplifiedDomainDisplayUrlHostnameAndScheme,
kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
}
}
// Tests that in the hide-on-interaction field trial variation, the path is
// faded out after omnibox focus and blur.
TEST_P(OmniboxViewViewsHideOnInteractionAndRevealOnHoverTest,
HideOnInteractionAfterFocusAndBlur) {
SetUpSimplifiedDomainTest();
gfx::RenderText* render_text = omnibox_view()->GetRenderText();
content::MockNavigationHandle navigation;
navigation.set_is_same_document(false);
omnibox_view()->DidFinishNavigation(&navigation);
// Simulate a user interaction to fade out the path.
omnibox_view()->DidGetUserInteraction(blink::WebKeyboardEvent());
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
gfx::AnimationContainerElement* elide_as_element =
elide_animation->GetAnimationForTesting();
elide_as_element->SetStartTime(base::TimeTicks());
elide_as_element->Step(base::TimeTicks() + base::TimeDelta::FromSeconds(1));
ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
omnibox_view(), kSimplifiedDomainDisplayUrlScheme,
kSimplifiedDomainDisplayUrlSubdomain,
kSimplifiedDomainDisplayUrlHostnameAndScheme,
kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
// After focus, the URL should be fully unelided.
omnibox_view()->OnFocus();
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
render_text, gfx::Range(0, kSimplifiedDomainDisplayUrl.size())));
EXPECT_NE(SK_ColorTRANSPARENT,
omnibox_view()->GetLatestColorForRange(
gfx::Range(0, omnibox_view()->GetText().size())));
EXPECT_EQ(gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrlHostnameAndScheme.size()),
omnibox_view()->emphasis_range());
// After blur, the URL should return to the same state as page load: only
// scheme and trivial subdomains elided.
omnibox_view()->OnBlur();
ASSERT_NO_FATAL_FAILURE(ExpectUnelidedFromSimplifiedDomain(
render_text, gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrl.size())));
EXPECT_NE(SK_ColorTRANSPARENT,
omnibox_view()->GetLatestColorForRange(
gfx::Range(0, omnibox_view()->GetText().size())));
EXPECT_EQ(gfx::Range(kSimplifiedDomainDisplayUrlScheme.size(),
kSimplifiedDomainDisplayUrlHostnameAndScheme.size()),
omnibox_view()->emphasis_range());
// After a post-blur user interaction, the URL should animate to the
// simplified domain.
omnibox_view()->DidGetUserInteraction(blink::WebKeyboardEvent());
elide_animation =
omnibox_view()->GetElideAfterInteractionAnimationForTesting();
EXPECT_TRUE(elide_animation->IsAnimating());
}
// Tests that in the reveal-on-hover field trial variation (without
// hide-on-interaction), the path is faded back in after focus, then blur, then
// hover.
TEST_P(OmniboxViewViewsRevealOnHoverTest, AfterBlur) {
SetUpSimplifiedDomainTest();
// Focus and blur the omnibox, then hover over it. The URL should unelide.
omnibox_view()->OnFocus();
omnibox_view()->OnBlur();
ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
omnibox_view(), kSimplifiedDomainDisplayUrlScheme,
kSimplifiedDomainDisplayUrlSubdomain,
kSimplifiedDomainDisplayUrlHostnameAndScheme,
kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
omnibox_view()->OnMouseMoved(CreateMouseEvent(ui::ET_MOUSE_MOVED, {0, 0}));
OmniboxViewViews::ElideAnimation* elide_animation =
omnibox_view()->GetHoverElideOrUnelideAnimationForTesting();
ASSERT_TRUE(elide_animation);
EXPECT_TRUE(elide_animation->IsAnimating());
}