diff options
Diffstat (limited to 'chrome/browser/autocomplete/autocomplete_edit_view_browsertest.cc')
-rw-r--r-- | chrome/browser/autocomplete/autocomplete_edit_view_browsertest.cc | 551 |
1 files changed, 551 insertions, 0 deletions
diff --git a/chrome/browser/autocomplete/autocomplete_edit_view_browsertest.cc b/chrome/browser/autocomplete/autocomplete_edit_view_browsertest.cc new file mode 100644 index 0000000..e267f3c --- /dev/null +++ b/chrome/browser/autocomplete/autocomplete_edit_view_browsertest.cc @@ -0,0 +1,551 @@ +// Copyright (c) 2010 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 <stdio.h> + +#include "base/file_path.h" +#include "base/keyboard_codes.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/platform_thread.h" +#include "base/ref_counted.h" +#include "base/string_util.h" +#include "base/time.h" +#include "base/utf_string_conversions.h" +#include "chrome/app/chrome_dll_resource.h" +#include "chrome/browser/autocomplete/autocomplete.h" +#include "chrome/browser/autocomplete/autocomplete_edit.h" +#include "chrome/browser/autocomplete/autocomplete_popup_model.h" +#include "chrome/browser/autocomplete/autocomplete_edit_view.h" +#include "chrome/browser/automation/ui_controls.h" +#include "chrome/browser/bookmarks/bookmark_model.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_window.h" +#include "chrome/browser/history/history.h" +#include "chrome/browser/location_bar.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/search_engines/template_url.h" +#include "chrome/browser/search_engines/template_url_model.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/url_constants.h" +#include "chrome/test/in_process_browser_test.h" +#include "chrome/test/ui_test_utils.h" +#include "net/base/mock_host_resolver.h" +#include "views/event.h" + +using base::Time; +using base::TimeDelta; + +namespace { + +const char kSearchKeyword[] = "foo"; +const wchar_t kSearchKeywordKeys[] = { + base::VKEY_F, base::VKEY_O, base::VKEY_O, 0 +}; +const char kSearchURL[] = "http://www.foo.com/search?q={searchTerms}"; +const char kSearchShortName[] = "foo"; +const char kSearchText[] = "abc"; +const wchar_t kSearchTextKeys[] = { + base::VKEY_A, base::VKEY_B, base::VKEY_C, 0 +}; +const char kSearchTextURL[] = "http://www.foo.com/search?q=abc"; +const char kSearchSingleChar[] = "z"; +const wchar_t kSearchSingleCharKeys[] = { base::VKEY_Z, 0 }; +const char kSearchSingleCharURL[] = "http://www.foo.com/search?q=z"; + +const char kHistoryPageURL[] = "chrome://history/#q=abc"; + +const char kDesiredTLDHostname[] = "www.bar.com"; +const wchar_t kDesiredTLDKeys[] = { + base::VKEY_B, base::VKEY_A, base::VKEY_R, 0 +}; + +// Hostnames that shall be blocked by host resolver. +const char *kBlockedHostnames[] = { + "foo", + "*.foo.com", + "bar", + "*.bar.com", + "abc", + "*.abc.com", + "history", + "z" +}; + +const struct TestHistoryEntry { + const char* url; + const char* title; + const char* body; + int visit_count; + int typed_count; + bool starred; +} kHistoryEntries[] = { + {"http://www.bar.com/1", "Page 1", kSearchText, 1, 1, false }, + {"http://www.bar.com/2", "Page 2", kSearchText, 1, 1, false }, + {"http://www.bar.com/3", "Page 3", kSearchText, 1, 1, false }, + {"http://www.bar.com/4", "Page 4", kSearchText, 1, 1, false }, + {"http://www.bar.com/5", "Page 5", kSearchText, 1, 1, false }, + {"http://www.bar.com/6", "Page 6", kSearchText, 1, 1, false }, + {"http://www.bar.com/7", "Page 7", kSearchText, 1, 1, false }, + {"http://www.bar.com/8", "Page 8", kSearchText, 1, 1, false }, + {"http://www.bar.com/9", "Page 9", kSearchText, 1, 1, false }, + + // To trigger inline autocomplete. + {"http://www.abc.com", "Page abc", kSearchText, 10000, 10000, true }, +}; + +} // namespace + +class AutocompleteEditViewTest : public InProcessBrowserTest, + public NotificationObserver { + protected: + AutocompleteEditViewTest() { + set_show_window(true); + } + + void GetNativeWindow(gfx::NativeWindow* native_window) { + BrowserWindow* window = browser()->window(); + ASSERT_TRUE(window); + *native_window = window->GetNativeHandle(); + ASSERT_TRUE(*native_window); + } + + void GetAutocompleteEditView(AutocompleteEditView** edit_view) { + BrowserWindow* window = browser()->window(); + ASSERT_TRUE(window); + LocationBar* loc_bar = window->GetLocationBar(); + ASSERT_TRUE(loc_bar); + *edit_view = loc_bar->location_entry(); + ASSERT_TRUE(*edit_view); + } + + void SendKey(base::KeyboardCode key, bool control, bool shift, bool alt) { + gfx::NativeWindow window = NULL; + ASSERT_NO_FATAL_FAILURE(GetNativeWindow(&window)); + ui_controls::SendKeyPressNotifyWhenDone(window, key, control, shift, alt, + false /* command */, + new MessageLoop::QuitTask()); + ui_test_utils::RunMessageLoop(); + } + + void SendKeySequence(const wchar_t* keys) { + for (; *keys; ++keys) + ASSERT_NO_FATAL_FAILURE(SendKey(static_cast<base::KeyboardCode>(*keys), + false, false, false)); + } + + void WaitForTabOpenOrClose(int expected_tab_count) { + int tab_count = browser()->tab_count(); + if (tab_count == expected_tab_count) + return; + + NotificationRegistrar registrar; + registrar.Add(this, + (tab_count < expected_tab_count ? + NotificationType::TAB_PARENTED : + NotificationType::TAB_CLOSED), + NotificationService::AllSources()); + + while (!HasFailure() && browser()->tab_count() != expected_tab_count) + ui_test_utils::RunMessageLoop(); + + ASSERT_EQ(expected_tab_count, browser()->tab_count()); + } + + void WaitForAutocompleteControllerDone() { + AutocompleteEditView* edit_view = NULL; + ASSERT_NO_FATAL_FAILURE(GetAutocompleteEditView(&edit_view)); + + AutocompleteController* controller = + edit_view->model()->popup_model()->autocomplete_controller(); + ASSERT_TRUE(controller); + + if (controller->done()) + return; + + NotificationRegistrar registrar; + registrar.Add(this, + NotificationType::AUTOCOMPLETE_CONTROLLER_RESULT_UPDATED, + Source<AutocompleteController>(controller)); + + while (!HasFailure() && !controller->done()) + ui_test_utils::RunMessageLoop(); + + ASSERT_TRUE(controller->done()); + } + + void SetupSearchEngine() { + TemplateURLModel* model = browser()->profile()->GetTemplateURLModel(); + ASSERT_TRUE(model); + + if (!model->loaded()) { + NotificationRegistrar registrar; + registrar.Add(this, NotificationType::TEMPLATE_URL_MODEL_LOADED, + Source<TemplateURLModel>(model)); + model->Load(); + ui_test_utils::RunMessageLoop(); + } + + ASSERT_TRUE(model->loaded()); + + TemplateURL* template_url = new TemplateURL(); + template_url->SetURL(kSearchURL, 0, 0); + template_url->set_keyword(UTF8ToWide(kSearchKeyword)); + template_url->set_short_name(UTF8ToWide(kSearchShortName)); + + model->Add(template_url); + model->SetDefaultSearchProvider(template_url); + } + + void SetupHistory() { + Profile* profile = browser()->profile(); + HistoryService* history_service = + profile->GetHistoryService(Profile::EXPLICIT_ACCESS); + ASSERT_TRUE(history_service); + + if (!history_service->BackendLoaded()) { + NotificationRegistrar registrar; + registrar.Add(this, NotificationType::HISTORY_LOADED, + Source<Profile>(profile)); + ui_test_utils::RunMessageLoop(); + } + + BookmarkModel* bookmark_model = profile->GetBookmarkModel(); + ASSERT_TRUE(bookmark_model); + + if (!bookmark_model->IsLoaded()) { + NotificationRegistrar registrar; + registrar.Add(this, NotificationType::BOOKMARK_MODEL_LOADED, + Source<Profile>(profile)); + ui_test_utils::RunMessageLoop(); + } + + // Add enough history pages containing |kSearchText| to trigger open history + // page url in autocomplete result. + for (size_t i = 0; i < arraysize(kHistoryEntries); i++) { + const TestHistoryEntry& cur = kHistoryEntries[i]; + GURL url(cur.url); + // Add everything in order of time. We don't want to have a time that + // is "right now" or it will nondeterministically appear in the results. + Time t = Time::Now() - TimeDelta::FromHours(i + 1); + history_service->AddPageWithDetails(url, UTF8ToUTF16(cur.title), + cur.visit_count, + cur.typed_count, t, false); + history_service->SetPageContents(url, UTF8ToUTF16(cur.body)); + if (cur.starred) { + bookmark_model->SetURLStarred(url, std::wstring(), true); + } + } + } + + void SetupHostResolver() { + for (size_t i = 0; i < arraysize(kBlockedHostnames); ++i) + host_resolver()->AddSimulatedFailure(kBlockedHostnames[i]); + } + + void SetupComponents() { + ASSERT_NO_FATAL_FAILURE(SetupHostResolver()); + ASSERT_NO_FATAL_FAILURE(SetupSearchEngine()); + ASSERT_NO_FATAL_FAILURE(SetupHistory()); + } + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::TAB_PARENTED: + case NotificationType::TAB_CLOSED: + case NotificationType::TEMPLATE_URL_MODEL_LOADED: + case NotificationType::AUTOCOMPLETE_CONTROLLER_RESULT_UPDATED: + case NotificationType::HISTORY_LOADED: + case NotificationType::BOOKMARK_MODEL_LOADED: + break; + default: + FAIL() << "Unexpected notification type"; + } + MessageLoopForUI::current()->Quit(); + } + +#if defined(OS_WIN) + virtual void CleanUpOnMainThread() { + // A hack to avoid hitting issue http://crbug.com/18372 and + // http://crbug.com/18373 + // Weird that it only happens on Windows. + // TODO(suzhe): Remove this hack as soon as these bugs are fixed. + MessageLoop::current()->PostDelayedTask(FROM_HERE, + new MessageLoop::QuitTask(), + 2000); + ui_test_utils::RunMessageLoop(); + } +#endif +}; + +// Test if ctrl-* accelerators are workable in omnibox. +// See http://crbug.com/19193: omnibox blocks ctrl-* commands +// This test is disabled. See bug 23213. +IN_PROC_BROWSER_TEST_F(AutocompleteEditViewTest, BrowserAccelerators) { + ASSERT_NO_FATAL_FAILURE(SetupComponents()); + browser()->FocusLocationBar(); + AutocompleteEditView* edit_view = NULL; + ASSERT_NO_FATAL_FAILURE(GetAutocompleteEditView(&edit_view)); + + int tab_count = browser()->tab_count(); + + // Create a new Tab. + browser()->NewTab(); + ASSERT_NO_FATAL_FAILURE(WaitForTabOpenOrClose(tab_count + 1)); + + // Select the first Tab. + ASSERT_NO_FATAL_FAILURE(SendKey(base::VKEY_1, true, false, false)); + ASSERT_EQ(0, browser()->selected_index()); + + browser()->FocusLocationBar(); + + // Select the second Tab. + ASSERT_NO_FATAL_FAILURE(SendKey(base::VKEY_2, true, false, false)); + ASSERT_EQ(1, browser()->selected_index()); + + browser()->FocusLocationBar(); + + // Close a Tab. + ASSERT_NO_FATAL_FAILURE(SendKey(base::VKEY_W, true, false, false)); + ASSERT_NO_FATAL_FAILURE(WaitForTabOpenOrClose(tab_count)); + + // Try ctrl-l to focus location bar. + edit_view->SetUserText(L"Hello world"); + EXPECT_FALSE(edit_view->IsSelectAll()); + ASSERT_NO_FATAL_FAILURE(SendKey(base::VKEY_L, true, false, false)); + EXPECT_TRUE(edit_view->IsSelectAll()); +} + +IN_PROC_BROWSER_TEST_F(AutocompleteEditViewTest, BackspaceInKeywordMode) { + ASSERT_NO_FATAL_FAILURE(SetupComponents()); + browser()->FocusLocationBar(); + + AutocompleteEditView* edit_view = NULL; + ASSERT_NO_FATAL_FAILURE(GetAutocompleteEditView(&edit_view)); + + // Trigger keyword hint mode. + ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchKeywordKeys)); + ASSERT_TRUE(edit_view->model()->is_keyword_hint()); + ASSERT_EQ(kSearchKeyword, WideToUTF8(edit_view->model()->keyword())); + + // Trigger keyword mode. + ASSERT_NO_FATAL_FAILURE(SendKey(base::VKEY_TAB, false, false, false)); + ASSERT_FALSE(edit_view->model()->is_keyword_hint()); + ASSERT_EQ(kSearchKeyword, WideToUTF8(edit_view->model()->keyword())); + + // Backspace without search text should bring back keyword hint mode. + ASSERT_NO_FATAL_FAILURE(SendKey(base::VKEY_BACK, false, false, false)); + ASSERT_TRUE(edit_view->model()->is_keyword_hint()); + ASSERT_EQ(kSearchKeyword, WideToUTF8(edit_view->model()->keyword())); + + // Trigger keyword mode again. + ASSERT_NO_FATAL_FAILURE(SendKey(base::VKEY_TAB, false, false, false)); + ASSERT_FALSE(edit_view->model()->is_keyword_hint()); + ASSERT_EQ(kSearchKeyword, WideToUTF8(edit_view->model()->keyword())); + + // Input something as search text. + ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchTextKeys)); + + // Should stay in keyword mode while deleting search text by pressing + // backspace. + for (size_t i = 0; i < arraysize(kSearchText) - 1; ++i) { + ASSERT_NO_FATAL_FAILURE(SendKey(base::VKEY_BACK, false, false, false)); + ASSERT_FALSE(edit_view->model()->is_keyword_hint()); + ASSERT_EQ(kSearchKeyword, WideToUTF8(edit_view->model()->keyword())); + } + + // Input something as search text. + ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchTextKeys)); + + // Move cursor to the beginning of the search text. + ASSERT_NO_FATAL_FAILURE(SendKey(base::VKEY_HOME, false, false, false)); + // Backspace at the beginning of the search text shall turn off + // the keyword mode. + ASSERT_NO_FATAL_FAILURE(SendKey(base::VKEY_BACK, false, false, false)); + ASSERT_FALSE(edit_view->model()->is_keyword_hint()); + ASSERT_EQ(std::string(), WideToUTF8(edit_view->model()->keyword())); + ASSERT_EQ(std::string(kSearchKeyword) + kSearchText, + WideToUTF8(edit_view->GetText())); +} + +IN_PROC_BROWSER_TEST_F(AutocompleteEditViewTest, Escape) { + ASSERT_NO_FATAL_FAILURE(SetupComponents()); + ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIHistoryURL)); + browser()->FocusLocationBar(); + + AutocompleteEditView* edit_view = NULL; + ASSERT_NO_FATAL_FAILURE(GetAutocompleteEditView(&edit_view)); + + std::wstring old_text = edit_view->GetText(); + EXPECT_FALSE(old_text.empty()); + EXPECT_TRUE(edit_view->IsSelectAll()); + + // Delete all text in omnibox. + ASSERT_NO_FATAL_FAILURE(SendKey(base::VKEY_BACK, false, false, false)); + EXPECT_TRUE(edit_view->GetText().empty()); + + // Escape shall revert the text in omnibox. + ASSERT_NO_FATAL_FAILURE(SendKey(base::VKEY_ESCAPE, false, false, false)); + EXPECT_EQ(old_text, edit_view->GetText()); + EXPECT_TRUE(edit_view->IsSelectAll()); +} + +IN_PROC_BROWSER_TEST_F(AutocompleteEditViewTest, DesiredTLD) { + ASSERT_NO_FATAL_FAILURE(SetupComponents()); + browser()->FocusLocationBar(); + + AutocompleteEditView* edit_view = NULL; + ASSERT_NO_FATAL_FAILURE(GetAutocompleteEditView(&edit_view)); + AutocompletePopupModel* popup_model = edit_view->model()->popup_model(); + ASSERT_TRUE(popup_model); + + // Test ctrl-Enter. + ASSERT_NO_FATAL_FAILURE(SendKeySequence(kDesiredTLDKeys)); + ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone()); + ASSERT_TRUE(popup_model->IsOpen()); + // ctrl-Enter triggers desired_tld feature, thus www.bar.com shall be opened. + ASSERT_NO_FATAL_FAILURE(SendKey(base::VKEY_RETURN, true, false, false)); + + GURL url = browser()->GetSelectedTabContents()->GetURL(); + EXPECT_STREQ(kDesiredTLDHostname, url.host().c_str()); +} + +// This test is disabled. See bug 23213. +IN_PROC_BROWSER_TEST_F(AutocompleteEditViewTest, AltEnter) { + ASSERT_NO_FATAL_FAILURE(SetupComponents()); + browser()->FocusLocationBar(); + + AutocompleteEditView* edit_view = NULL; + ASSERT_NO_FATAL_FAILURE(GetAutocompleteEditView(&edit_view)); + + edit_view->SetUserText(ASCIIToWide(chrome::kChromeUIHistoryURL)); + int tab_count = browser()->tab_count(); + // alt-Enter opens a new tab. + ASSERT_NO_FATAL_FAILURE(SendKey(base::VKEY_RETURN, false, false, true)); + ASSERT_NO_FATAL_FAILURE(WaitForTabOpenOrClose(tab_count + 1)); +} + +IN_PROC_BROWSER_TEST_F(AutocompleteEditViewTest, EnterToSearch) { + ASSERT_NO_FATAL_FAILURE(SetupHostResolver()); + ASSERT_NO_FATAL_FAILURE(SetupSearchEngine()); + browser()->FocusLocationBar(); + + AutocompleteEditView* edit_view = NULL; + ASSERT_NO_FATAL_FAILURE(GetAutocompleteEditView(&edit_view)); + AutocompletePopupModel* popup_model = edit_view->model()->popup_model(); + ASSERT_TRUE(popup_model); + + // Test Enter to search. + ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchTextKeys)); + ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone()); + ASSERT_TRUE(popup_model->IsOpen()); + + // Check if the default match result is Search Primary Provider. + ASSERT_EQ(AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, + popup_model->result().default_match()->type); + + // Open the default match. + ASSERT_NO_FATAL_FAILURE(SendKey(base::VKEY_RETURN, false, false, false)); + GURL url = browser()->GetSelectedTabContents()->GetURL(); + EXPECT_STREQ(kSearchTextURL, url.spec().c_str()); + + // Test that entering a single character then Enter performs a search. + browser()->FocusLocationBar(); + EXPECT_TRUE(edit_view->IsSelectAll()); + ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchSingleCharKeys)); + ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone()); + ASSERT_TRUE(popup_model->IsOpen()); + EXPECT_EQ(kSearchSingleChar, WideToUTF8(edit_view->GetText())); + + // Check if the default match result is Search Primary Provider. + ASSERT_EQ(AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, + popup_model->result().default_match()->type); + + // Open the default match. + ASSERT_NO_FATAL_FAILURE(SendKey(base::VKEY_RETURN, false, false, false)); + url = browser()->GetSelectedTabContents()->GetURL(); + EXPECT_STREQ(kSearchSingleCharURL, url.spec().c_str()); +} + +// See http://crbug.com/20934: Omnibox keyboard behavior wrong for +// "See recent pages in history" +IN_PROC_BROWSER_TEST_F(AutocompleteEditViewTest, EnterToOpenHistoryPage) { + ASSERT_NO_FATAL_FAILURE(SetupComponents()); + browser()->FocusLocationBar(); + + AutocompleteEditView* edit_view = NULL; + ASSERT_NO_FATAL_FAILURE(GetAutocompleteEditView(&edit_view)); + AutocompletePopupModel* popup_model = edit_view->model()->popup_model(); + ASSERT_TRUE(popup_model); + + ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchTextKeys)); + ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone()); + ASSERT_TRUE(popup_model->IsOpen()); + EXPECT_EQ(0U, popup_model->selected_line()); + + // Move to the history page item. + size_t size = popup_model->result().size(); + while (true) { + if (popup_model->result().match_at(popup_model->selected_line()).type == + AutocompleteMatch::OPEN_HISTORY_PAGE) + break; + size_t old_selected_line = popup_model->selected_line(); + ASSERT_NO_FATAL_FAILURE(SendKey(base::VKEY_DOWN, false, false, false)); + ASSERT_EQ(old_selected_line + 1, popup_model->selected_line()); + if (popup_model->selected_line() == size - 1) + break; + } + + // Make sure the history page item is selected. + ASSERT_EQ(AutocompleteMatch::OPEN_HISTORY_PAGE, + popup_model->result().match_at(popup_model->selected_line()).type); + + // Open the history page item. + ASSERT_NO_FATAL_FAILURE(SendKey(base::VKEY_RETURN, false, false, false)); + GURL url = browser()->GetSelectedTabContents()->GetURL(); + EXPECT_STREQ(kHistoryPageURL, url.spec().c_str()); +} + +IN_PROC_BROWSER_TEST_F(AutocompleteEditViewTest, EscapeToDefaultMatch) { + ASSERT_NO_FATAL_FAILURE(SetupComponents()); + browser()->FocusLocationBar(); + + AutocompleteEditView* edit_view = NULL; + ASSERT_NO_FATAL_FAILURE(GetAutocompleteEditView(&edit_view)); + AutocompletePopupModel* popup_model = edit_view->model()->popup_model(); + ASSERT_TRUE(popup_model); + + // Input something to trigger inline autocomplete. + ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchTextKeys)); + ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone()); + ASSERT_TRUE(popup_model->IsOpen()); + + std::wstring old_text = edit_view->GetText(); + + // Make sure inline autocomplete is triggerred. + EXPECT_GT(old_text.length(), arraysize(kSearchText) - 1); + + size_t old_selected_line = popup_model->selected_line(); + EXPECT_EQ(0U, old_selected_line); + + // Move to another line with different text. + size_t size = popup_model->result().size(); + while (popup_model->selected_line() < size - 1) { + ASSERT_NO_FATAL_FAILURE(SendKey(base::VKEY_DOWN, false, false, false)); + ASSERT_NE(old_selected_line, popup_model->selected_line()); + if (old_text != edit_view->GetText()) + break; + } + + EXPECT_NE(old_text, edit_view->GetText()); + + // Escape shall revert back to the default match item. + ASSERT_NO_FATAL_FAILURE(SendKey(base::VKEY_ESCAPE, false, false, false)); + EXPECT_EQ(old_text, edit_view->GetText()); + EXPECT_EQ(old_selected_line, popup_model->selected_line()); +} |