1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/format_macros.h"
6 #include "base/string_util.h"
7 #include "base/stringprintf.h"
8 #include "base/utf_string_conversions.h"
9 #include "chrome/browser/autocomplete/autocomplete.h"
10 #include "chrome/browser/autocomplete/autocomplete_edit.h"
11 #include "chrome/browser/autocomplete/autocomplete_edit_view.h"
12 #include "chrome/browser/autocomplete/autocomplete_match.h"
13 #include "chrome/browser/autocomplete/autocomplete_popup_model.h"
14 #include "chrome/browser/extensions/extension_apitest.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/search_engines/template_url.h"
17 #include "chrome/browser/search_engines/template_url_model.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/browser/ui/browser_window.h"
20 #include "chrome/browser/ui/omnibox/location_bar.h"
21 #include "chrome/common/url_constants.h"
22 #include "chrome/test/ui_test_utils.h"
23 #include "content/common/notification_type.h"
24
25 #if defined(TOOLKIT_GTK)
26 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
27 #endif
28
29 // Basic test is flaky on ChromeOS.
30 // http://crbug.com/52929
31 #if defined(OS_CHROMEOS)
32 #define MAYBE_Basic FLAKY_Basic
33 #else
34 #define MAYBE_Basic Basic
35 #endif
36
37 namespace {
38
AutocompleteResultAsString(const AutocompleteResult & result)39 string16 AutocompleteResultAsString(const AutocompleteResult& result) {
40 std::string output(base::StringPrintf("{%" PRIuS "} ", result.size()));
41 for (size_t i = 0; i < result.size(); ++i) {
42 AutocompleteMatch match = result.match_at(i);
43 std::string provider_name = match.provider->name();
44 output.append(base::StringPrintf("[\"%s\" by \"%s\"] ",
45 UTF16ToUTF8(match.contents).c_str(),
46 provider_name.c_str()));
47 }
48 return UTF8ToUTF16(output);
49 }
50
51 } // namespace
52
53 class OmniboxApiTest : public ExtensionApiTest {
54 protected:
GetLocationBar() const55 LocationBar* GetLocationBar() const {
56 return browser()->window()->GetLocationBar();
57 }
58
GetAutocompleteController() const59 AutocompleteController* GetAutocompleteController() const {
60 return GetLocationBar()->location_entry()->model()->popup_model()->
61 autocomplete_controller();
62 }
63
WaitForTemplateURLModelToLoad()64 void WaitForTemplateURLModelToLoad() {
65 TemplateURLModel* model =
66 browser()->profile()->GetTemplateURLModel();
67 model->Load();
68 if (!model->loaded()) {
69 ui_test_utils::WaitForNotification(
70 NotificationType::TEMPLATE_URL_MODEL_LOADED);
71 }
72 }
73
WaitForAutocompleteDone(AutocompleteController * controller)74 void WaitForAutocompleteDone(AutocompleteController* controller) {
75 while (!controller->done()) {
76 ui_test_utils::WaitForNotification(
77 NotificationType::AUTOCOMPLETE_CONTROLLER_RESULT_READY);
78 }
79 }
80 };
81
IN_PROC_BROWSER_TEST_F(OmniboxApiTest,MAYBE_Basic)82 IN_PROC_BROWSER_TEST_F(OmniboxApiTest, MAYBE_Basic) {
83 #if defined(TOOLKIT_GTK)
84 // Disable the timer because, on Lucid at least, it triggers resize/move
85 // behavior in the browser window, which dismisses the autocomplete popup
86 // before the results can be read.
87 static_cast<BrowserWindowGtk*>(
88 browser()->window())->DisableDebounceTimerForTests(true);
89 #endif
90
91 ASSERT_TRUE(test_server()->Start());
92 ASSERT_TRUE(RunExtensionTest("omnibox")) << message_;
93
94 // The results depend on the TemplateURLModel being loaded. Make sure it is
95 // loaded so that the autocomplete results are consistent.
96 WaitForTemplateURLModelToLoad();
97
98 LocationBar* location_bar = GetLocationBar();
99 AutocompleteController* autocomplete_controller = GetAutocompleteController();
100
101 // Test that our extension's keyword is suggested to us when we partially type
102 // it.
103 {
104 autocomplete_controller->Start(
105 ASCIIToUTF16("keywor"), string16(), true, false, true,
106 AutocompleteInput::ALL_MATCHES);
107
108 WaitForAutocompleteDone(autocomplete_controller);
109 EXPECT_TRUE(autocomplete_controller->done());
110 EXPECT_EQ(std::wstring(), location_bar->GetInputString());
111 EXPECT_EQ(string16(), location_bar->location_entry()->GetText());
112 EXPECT_TRUE(location_bar->location_entry()->IsSelectAll());
113
114 // First result should be to search for what was typed, second should be to
115 // enter "extension keyword" mode.
116 const AutocompleteResult& result = autocomplete_controller->result();
117 ASSERT_EQ(2U, result.size()) << AutocompleteResultAsString(result);
118 AutocompleteMatch match = result.match_at(0);
119 EXPECT_EQ(AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, match.type);
120 EXPECT_FALSE(match.deletable);
121
122 match = result.match_at(1);
123 ASSERT_TRUE(match.template_url);
124 EXPECT_TRUE(match.template_url->IsExtensionKeyword());
125 EXPECT_EQ(ASCIIToUTF16("keyword"), match.template_url->keyword());
126 }
127
128 // Test that our extension can send suggestions back to us.
129 {
130 autocomplete_controller->Start(
131 ASCIIToUTF16("keyword suggestio"), string16(), true, false, true,
132 AutocompleteInput::ALL_MATCHES);
133
134 WaitForAutocompleteDone(autocomplete_controller);
135 EXPECT_TRUE(autocomplete_controller->done());
136
137 // First result should be to invoke the keyword with what we typed, 2-4
138 // should be to invoke with suggestions from the extension, and the last
139 // should be to search for what we typed.
140 const AutocompleteResult& result = autocomplete_controller->result();
141 ASSERT_EQ(5U, result.size()) << AutocompleteResultAsString(result);
142
143 ASSERT_TRUE(result.match_at(0).template_url);
144 EXPECT_EQ(ASCIIToUTF16("keyword suggestio"),
145 result.match_at(0).fill_into_edit);
146 EXPECT_EQ(ASCIIToUTF16("keyword suggestion1"),
147 result.match_at(1).fill_into_edit);
148 EXPECT_EQ(ASCIIToUTF16("keyword suggestion2"),
149 result.match_at(2).fill_into_edit);
150 EXPECT_EQ(ASCIIToUTF16("keyword suggestion3"),
151 result.match_at(3).fill_into_edit);
152
153 string16 description =
154 ASCIIToUTF16("Description with style: <match>, [dim], (url till end)");
155 EXPECT_EQ(description, result.match_at(1).contents);
156 ASSERT_EQ(6u, result.match_at(1).contents_class.size());
157
158 EXPECT_EQ(0u,
159 result.match_at(1).contents_class[0].offset);
160 EXPECT_EQ(ACMatchClassification::NONE,
161 result.match_at(1).contents_class[0].style);
162
163 EXPECT_EQ(description.find('<'),
164 result.match_at(1).contents_class[1].offset);
165 EXPECT_EQ(ACMatchClassification::MATCH,
166 result.match_at(1).contents_class[1].style);
167
168 EXPECT_EQ(description.find('>') + 1u,
169 result.match_at(1).contents_class[2].offset);
170 EXPECT_EQ(ACMatchClassification::NONE,
171 result.match_at(1).contents_class[2].style);
172
173 EXPECT_EQ(description.find('['),
174 result.match_at(1).contents_class[3].offset);
175 EXPECT_EQ(ACMatchClassification::DIM,
176 result.match_at(1).contents_class[3].style);
177
178 EXPECT_EQ(description.find(']') + 1u,
179 result.match_at(1).contents_class[4].offset);
180 EXPECT_EQ(ACMatchClassification::NONE,
181 result.match_at(1).contents_class[4].style);
182
183 EXPECT_EQ(description.find('('),
184 result.match_at(1).contents_class[5].offset);
185 EXPECT_EQ(ACMatchClassification::URL,
186 result.match_at(1).contents_class[5].style);
187
188 AutocompleteMatch match = result.match_at(4);
189 EXPECT_EQ(AutocompleteMatch::SEARCH_WHAT_YOU_TYPED, match.type);
190 EXPECT_FALSE(match.deletable);
191 }
192
193 {
194 ResultCatcher catcher;
195 autocomplete_controller->Start(
196 ASCIIToUTF16("keyword command"), string16(), true, false, true,
197 AutocompleteInput::ALL_MATCHES);
198 location_bar->AcceptInput();
199 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
200 }
201 }
202