Home | History | Annotate | Download | only in extensions
      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 
     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:
     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 
     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