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
37namespace {
38
39string16 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
53class OmniboxApiTest : public ExtensionApiTest {
54 protected:
55  LocationBar* GetLocationBar() const {
56    return browser()->window()->GetLocationBar();
57  }
58
59  AutocompleteController* GetAutocompleteController() const {
60    return GetLocationBar()->location_entry()->model()->popup_model()->
61        autocomplete_controller();
62  }
63
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
74  void WaitForAutocompleteDone(AutocompleteController* controller) {
75    while (!controller->done()) {
76      ui_test_utils::WaitForNotification(
77          NotificationType::AUTOCOMPLETE_CONTROLLER_RESULT_READY);
78    }
79  }
80};
81
82IN_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