1// Copyright (c) 2012 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/strings/utf_string_conversions.h"
6#include "chrome/browser/autocomplete/autocomplete_classifier_factory.h"
7#include "chrome/browser/search_engines/template_url_service_factory.h"
8#include "chrome/browser/ui/omnibox/omnibox_edit_controller.h"
9#include "chrome/browser/ui/omnibox/omnibox_edit_model.h"
10#include "chrome/browser/ui/omnibox/omnibox_view.h"
11#include "chrome/browser/ui/toolbar/test_toolbar_model.h"
12#include "chrome/test/base/testing_profile.h"
13#include "testing/gtest/include/gtest/gtest.h"
14
15using base::ASCIIToUTF16;
16using base::UTF8ToUTF16;
17using content::WebContents;
18
19namespace {
20
21class TestingOmniboxView : public OmniboxView {
22 public:
23  explicit TestingOmniboxView(OmniboxEditController* controller)
24      : OmniboxView(NULL, controller, NULL) {}
25
26  // OmniboxView:
27  virtual void SaveStateToTab(WebContents* tab) OVERRIDE {}
28  virtual void OnTabChanged(const WebContents* web_contents) OVERRIDE {}
29  virtual void Update() OVERRIDE {}
30  virtual void UpdatePlaceholderText() OVERRIDE {}
31  virtual void OpenMatch(const AutocompleteMatch& match,
32                         WindowOpenDisposition disposition,
33                         const GURL& alternate_nav_url,
34                         const base::string16& pasted_text,
35                         size_t selected_line) OVERRIDE {}
36  virtual base::string16 GetText() const OVERRIDE { return text_; }
37  virtual void SetUserText(const base::string16& text,
38                           const base::string16& display_text,
39                           bool update_popup) OVERRIDE {
40    text_ = display_text;
41  }
42  virtual void SetWindowTextAndCaretPos(const base::string16& text,
43                                        size_t caret_pos,
44                                        bool update_popup,
45                                        bool notify_text_changed) OVERRIDE {
46    text_ = text;
47  }
48  virtual void SetForcedQuery() OVERRIDE {}
49  virtual bool IsSelectAll() const OVERRIDE { return false; }
50  virtual bool DeleteAtEndPressed() OVERRIDE { return false; }
51  virtual void GetSelectionBounds(size_t* start, size_t* end) const OVERRIDE {}
52  virtual void SelectAll(bool reversed) OVERRIDE {}
53  virtual void RevertAll() OVERRIDE {}
54  virtual void UpdatePopup() OVERRIDE {}
55  virtual void SetFocus() OVERRIDE {}
56  virtual void ApplyCaretVisibility() OVERRIDE {}
57  virtual void OnTemporaryTextMaybeChanged(
58      const base::string16& display_text,
59      bool save_original_selection,
60      bool notify_text_changed) OVERRIDE {
61    text_ = display_text;
62  }
63  virtual bool OnInlineAutocompleteTextMaybeChanged(
64      const base::string16& display_text, size_t user_text_length) OVERRIDE {
65    const bool text_changed = text_ != display_text;
66    text_ = display_text;
67    inline_autocomplete_text_ = display_text.substr(user_text_length);
68    return text_changed;
69  }
70  virtual void OnInlineAutocompleteTextCleared() OVERRIDE {
71    inline_autocomplete_text_.clear();
72  }
73  virtual void OnRevertTemporaryText() OVERRIDE {}
74  virtual void OnBeforePossibleChange() OVERRIDE {}
75  virtual bool OnAfterPossibleChange() OVERRIDE { return false; }
76  virtual gfx::NativeView GetNativeView() const OVERRIDE { return NULL; }
77  virtual gfx::NativeView GetRelativeWindowForPopup() const OVERRIDE {
78    return NULL;
79  }
80  virtual void SetGrayTextAutocompletion(
81      const base::string16& input) OVERRIDE {}
82  virtual base::string16 GetGrayTextAutocompletion() const OVERRIDE {
83    return base::string16();
84  }
85  virtual int GetTextWidth() const OVERRIDE { return 0; }
86  virtual int GetWidth() const OVERRIDE { return 0; }
87  virtual bool IsImeComposing() const OVERRIDE { return false; }
88  virtual int GetOmniboxTextLength() const OVERRIDE { return 0; }
89  virtual void EmphasizeURLComponents() OVERRIDE { }
90
91  const base::string16& inline_autocomplete_text() const {
92    return inline_autocomplete_text_;
93  }
94
95 private:
96  base::string16 text_;
97  base::string16 inline_autocomplete_text_;
98
99  DISALLOW_COPY_AND_ASSIGN(TestingOmniboxView);
100};
101
102class TestingOmniboxEditController : public OmniboxEditController {
103 public:
104  explicit TestingOmniboxEditController(ToolbarModel* toolbar_model)
105      : OmniboxEditController(NULL),
106        toolbar_model_(toolbar_model) {
107  }
108
109 protected:
110  // OmniboxEditController:
111  virtual void Update(const content::WebContents* contents) OVERRIDE {}
112  virtual void OnChanged() OVERRIDE {}
113  virtual void OnSetFocus() OVERRIDE {}
114  virtual void ShowURL() OVERRIDE {}
115  virtual void HideURL() OVERRIDE {}
116  virtual void EndOriginChipAnimations(bool cancel_fade) OVERRIDE {}
117  virtual InstantController* GetInstant() OVERRIDE { return NULL; }
118  virtual WebContents* GetWebContents() OVERRIDE { return NULL; }
119  virtual ToolbarModel* GetToolbarModel() OVERRIDE { return toolbar_model_; }
120  virtual const ToolbarModel* GetToolbarModel() const OVERRIDE {
121    return toolbar_model_;
122  }
123
124 private:
125  ToolbarModel* toolbar_model_;
126
127  DISALLOW_COPY_AND_ASSIGN(TestingOmniboxEditController);
128};
129
130}  // namespace
131
132class AutocompleteEditTest : public ::testing::Test {
133 public:
134  TestToolbarModel* toolbar_model() { return &toolbar_model_; }
135
136 private:
137  TestToolbarModel toolbar_model_;
138};
139
140// Tests various permutations of AutocompleteModel::AdjustTextForCopy.
141TEST_F(AutocompleteEditTest, AdjustTextForCopy) {
142  struct Data {
143    const char* perm_text;
144    const int sel_start;
145    const bool is_all_selected;
146    const char* input;
147    const char* expected_output;
148    const bool write_url;
149    const char* expected_url;
150    const bool extracted_search_terms;
151  } input[] = {
152    // Test that http:// is inserted if all text is selected.
153    { "a.de/b", 0, true, "a.de/b", "http://a.de/b", true, "http://a.de/b",
154      false },
155
156    // Test that http:// is inserted if the host is selected.
157    { "a.de/b", 0, false, "a.de/", "http://a.de/", true, "http://a.de/",
158      false },
159
160    // Tests that http:// is inserted if the path is modified.
161    { "a.de/b", 0, false, "a.de/c", "http://a.de/c", true, "http://a.de/c",
162      false },
163
164    // Tests that http:// isn't inserted if the host is modified.
165    { "a.de/b", 0, false, "a.com/b", "a.com/b", false, "", false },
166
167    // Tests that http:// isn't inserted if the start of the selection is 1.
168    { "a.de/b", 1, false, "a.de/b", "a.de/b", false, "", false },
169
170    // Tests that http:// isn't inserted if a portion of the host is selected.
171    { "a.de/", 0, false, "a.d", "a.d", false, "", false },
172
173    // Tests that http:// isn't inserted for an https url after the user nukes
174    // https.
175    { "https://a.com/", 0, false, "a.com/", "a.com/", false, "", false },
176
177    // Tests that http:// isn't inserted if the user adds to the host.
178    { "a.de/", 0, false, "a.de.com/", "a.de.com/", false, "", false },
179
180    // Tests that we don't get double http if the user manually inserts http.
181    { "a.de/", 0, false, "http://a.de/", "http://a.de/", true, "http://a.de/",
182      false },
183
184    // Makes sure intranet urls get 'http://' prefixed to them.
185    { "b/foo", 0, true, "b/foo", "http://b/foo", true, "http://b/foo", false },
186
187    // Verifies a search term 'foo' doesn't end up with http.
188    { "www.google.com/search?", 0, false, "foo", "foo", false, "", false },
189
190    // Makes sure extracted search terms are not modified.
191    { "www.google.com/webhp?", 0, true, "hello world", "hello world", false,
192      "", true },
193  };
194  TestingOmniboxEditController controller(toolbar_model());
195  TestingOmniboxView view(&controller);
196  TestingProfile profile;
197  // NOTE: The TemplateURLService must be created before the
198  // AutocompleteClassifier so that the SearchProvider gets a non-NULL
199  // TemplateURLService at construction time.
200  TemplateURLServiceFactory::GetInstance()->SetTestingFactory(
201      &profile, &TemplateURLServiceFactory::BuildInstanceFor);
202  AutocompleteClassifierFactory::GetInstance()->SetTestingFactory(
203      &profile, &AutocompleteClassifierFactory::BuildInstanceFor);
204  OmniboxEditModel model(&view, &controller, &profile);
205
206  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(input); ++i) {
207    toolbar_model()->set_text(ASCIIToUTF16(input[i].perm_text));
208    model.UpdatePermanentText();
209
210    toolbar_model()->set_perform_search_term_replacement(
211        input[i].extracted_search_terms);
212
213    base::string16 result = ASCIIToUTF16(input[i].input);
214    GURL url;
215    bool write_url;
216    model.AdjustTextForCopy(input[i].sel_start, input[i].is_all_selected,
217                            &result, &url, &write_url);
218    EXPECT_EQ(ASCIIToUTF16(input[i].expected_output), result) << "@: " << i;
219    EXPECT_EQ(input[i].write_url, write_url) << " @" << i;
220    if (write_url)
221      EXPECT_EQ(input[i].expected_url, url.spec()) << " @" << i;
222  }
223}
224
225TEST_F(AutocompleteEditTest, InlineAutocompleteText) {
226  TestingOmniboxEditController controller(toolbar_model());
227  TestingOmniboxView view(&controller);
228  TestingProfile profile;
229  // NOTE: The TemplateURLService must be created before the
230  // AutocompleteClassifier so that the SearchProvider gets a non-NULL
231  // TemplateURLService at construction time.
232  TemplateURLServiceFactory::GetInstance()->SetTestingFactory(
233      &profile, &TemplateURLServiceFactory::BuildInstanceFor);
234  AutocompleteClassifierFactory::GetInstance()->SetTestingFactory(
235      &profile, &AutocompleteClassifierFactory::BuildInstanceFor);
236  OmniboxEditModel model(&view, &controller, &profile);
237
238  // Test if the model updates the inline autocomplete text in the view.
239  EXPECT_EQ(base::string16(), view.inline_autocomplete_text());
240  model.SetUserText(UTF8ToUTF16("he"));
241  model.OnPopupDataChanged(UTF8ToUTF16("llo"), NULL, base::string16(), false);
242  EXPECT_EQ(UTF8ToUTF16("hello"), view.GetText());
243  EXPECT_EQ(UTF8ToUTF16("llo"), view.inline_autocomplete_text());
244
245  model.OnAfterPossibleChange(UTF8ToUTF16("he"), UTF8ToUTF16("hel"), 3, 3,
246                              false, true, false, true);
247  EXPECT_EQ(base::string16(), view.inline_autocomplete_text());
248  model.OnPopupDataChanged(UTF8ToUTF16("lo"), NULL, base::string16(), false);
249  EXPECT_EQ(UTF8ToUTF16("hello"), view.GetText());
250  EXPECT_EQ(UTF8ToUTF16("lo"), view.inline_autocomplete_text());
251
252  model.Revert();
253  EXPECT_EQ(base::string16(), view.GetText());
254  EXPECT_EQ(base::string16(), view.inline_autocomplete_text());
255
256  model.SetUserText(UTF8ToUTF16("he"));
257  model.OnPopupDataChanged(UTF8ToUTF16("llo"), NULL, base::string16(), false);
258  EXPECT_EQ(UTF8ToUTF16("hello"), view.GetText());
259  EXPECT_EQ(UTF8ToUTF16("llo"), view.inline_autocomplete_text());
260
261  model.AcceptTemporaryTextAsUserText();
262  EXPECT_EQ(UTF8ToUTF16("hello"), view.GetText());
263  EXPECT_EQ(base::string16(), view.inline_autocomplete_text());
264}
265