Home | History | Annotate | Download | only in search_engines
      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 "chrome/browser/ui/search_engines/search_engine_tab_helper.h"
      6 
      7 #include "chrome/browser/profiles/profile.h"
      8 #include "chrome/browser/search_engines/template_url.h"
      9 #include "chrome/browser/search_engines/template_url_fetcher.h"
     10 #include "chrome/browser/search_engines/template_url_fetcher_factory.h"
     11 #include "chrome/browser/search_engines/template_url_service.h"
     12 #include "chrome/browser/search_engines/template_url_service_factory.h"
     13 #include "chrome/browser/ui/search_engines/template_url_fetcher_ui_callbacks.h"
     14 #include "chrome/common/render_messages.h"
     15 #include "chrome/common/url_constants.h"
     16 #include "content/public/browser/favicon_status.h"
     17 #include "content/public/browser/navigation_controller.h"
     18 #include "content/public/browser/navigation_entry.h"
     19 #include "content/public/browser/web_contents.h"
     20 #include "content/public/common/frame_navigate_params.h"
     21 
     22 using content::NavigationController;
     23 using content::NavigationEntry;
     24 using content::WebContents;
     25 
     26 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SearchEngineTabHelper);
     27 
     28 namespace {
     29 
     30 // Returns true if the entry's transition type is FORM_SUBMIT.
     31 bool IsFormSubmit(const NavigationEntry* entry) {
     32   return (content::PageTransitionStripQualifier(entry->GetTransitionType()) ==
     33           content::PAGE_TRANSITION_FORM_SUBMIT);
     34 }
     35 
     36 base::string16 GenerateKeywordFromNavigationEntry(
     37     const NavigationEntry* entry) {
     38   // Don't autogenerate keywords for pages that are the result of form
     39   // submissions.
     40   if (IsFormSubmit(entry))
     41     return base::string16();
     42 
     43   // We want to use the user typed URL if available since that represents what
     44   // the user typed to get here, and fall back on the regular URL if not.
     45   GURL url = entry->GetUserTypedURL();
     46   if (!url.is_valid()) {
     47     url = entry->GetURL();
     48     if (!url.is_valid())
     49       return base::string16();
     50   }
     51 
     52   // Don't autogenerate keywords for referrers that are anything other than HTTP
     53   // or have a path.
     54   //
     55   // If we relax the path constraint, we need to be sure to sanitize the path
     56   // elements and update AutocompletePopup to look for keywords using the path.
     57   // See http://b/issue?id=863583.
     58   if (!url.SchemeIs(url::kHttpScheme) || (url.path().length() > 1))
     59     return base::string16();
     60 
     61   return TemplateURL::GenerateKeyword(url);
     62 }
     63 
     64 }  // namespace
     65 
     66 SearchEngineTabHelper::~SearchEngineTabHelper() {
     67 }
     68 
     69 void SearchEngineTabHelper::DidNavigateMainFrame(
     70     const content::LoadCommittedDetails& /*details*/,
     71     const content::FrameNavigateParams& params) {
     72   GenerateKeywordIfNecessary(params);
     73 }
     74 
     75 bool SearchEngineTabHelper::OnMessageReceived(const IPC::Message& message) {
     76   bool handled = true;
     77   IPC_BEGIN_MESSAGE_MAP(SearchEngineTabHelper, message)
     78     IPC_MESSAGE_HANDLER(ChromeViewHostMsg_PageHasOSDD, OnPageHasOSDD)
     79     IPC_MESSAGE_UNHANDLED(handled = false)
     80   IPC_END_MESSAGE_MAP()
     81 
     82   return handled;
     83 }
     84 
     85 SearchEngineTabHelper::SearchEngineTabHelper(WebContents* web_contents)
     86     : content::WebContentsObserver(web_contents) {
     87   DCHECK(web_contents);
     88 }
     89 
     90 void SearchEngineTabHelper::OnPageHasOSDD(
     91     const GURL& page_url,
     92     const GURL& osdd_url,
     93     const search_provider::OSDDType& msg_provider_type) {
     94   // Checks to see if we should generate a keyword based on the OSDD, and if
     95   // necessary uses TemplateURLFetcher to download the OSDD and create a
     96   // keyword.
     97 
     98   // Make sure that the page is the current page and other basic checks.
     99   if (!osdd_url.is_valid())
    100     return;
    101   Profile* profile =
    102       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
    103   if (page_url != web_contents()->GetLastCommittedURL() ||
    104       !TemplateURLFetcherFactory::GetForProfile(profile) ||
    105       profile->IsOffTheRecord())
    106     return;
    107 
    108   TemplateURLFetcher::ProviderType provider_type =
    109       (msg_provider_type == search_provider::AUTODETECTED_PROVIDER) ?
    110           TemplateURLFetcher::AUTODETECTED_PROVIDER :
    111           TemplateURLFetcher::EXPLICIT_PROVIDER;
    112 
    113   // If the current page is a form submit, find the last page that was not a
    114   // form submit and use its url to generate the keyword from.
    115   const NavigationController& controller = web_contents()->GetController();
    116   const NavigationEntry* entry = controller.GetLastCommittedEntry();
    117   for (int index = controller.GetLastCommittedEntryIndex();
    118        (index > 0) && IsFormSubmit(entry);
    119        entry = controller.GetEntryAtIndex(index))
    120     --index;
    121   if (IsFormSubmit(entry))
    122     return;
    123 
    124   // Autogenerate a keyword for the autodetected case; in the other cases we'll
    125   // generate a keyword later after fetching the OSDD.
    126   base::string16 keyword;
    127   if (provider_type == TemplateURLFetcher::AUTODETECTED_PROVIDER) {
    128     keyword = GenerateKeywordFromNavigationEntry(entry);
    129     if (keyword.empty())
    130       return;
    131   }
    132 
    133   // Download the OpenSearch description document. If this is successful, a
    134   // new keyword will be created when done.
    135   TemplateURLFetcherFactory::GetForProfile(profile)->ScheduleDownload(
    136       keyword, osdd_url, entry->GetFavicon().url, web_contents(),
    137       new TemplateURLFetcherUICallbacks(this, web_contents()), provider_type);
    138 }
    139 
    140 void SearchEngineTabHelper::GenerateKeywordIfNecessary(
    141     const content::FrameNavigateParams& params) {
    142   if (!params.searchable_form_url.is_valid())
    143     return;
    144 
    145   Profile* profile =
    146       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
    147   if (profile->IsOffTheRecord())
    148     return;
    149 
    150   const NavigationController& controller = web_contents()->GetController();
    151   int last_index = controller.GetLastCommittedEntryIndex();
    152   // When there was no previous page, the last index will be 0. This is
    153   // normally due to a form submit that opened in a new tab.
    154   // TODO(brettw) bug 916126: we should support keywords when form submits
    155   //              happen in new tabs.
    156   if (last_index <= 0)
    157     return;
    158 
    159   base::string16 keyword(GenerateKeywordFromNavigationEntry(
    160       controller.GetEntryAtIndex(last_index - 1)));
    161   if (keyword.empty())
    162     return;
    163 
    164   TemplateURLService* url_service =
    165       TemplateURLServiceFactory::GetForProfile(profile);
    166   if (!url_service)
    167     return;
    168 
    169   if (!url_service->loaded()) {
    170     url_service->Load();
    171     return;
    172   }
    173 
    174   TemplateURL* current_url;
    175   GURL url = params.searchable_form_url;
    176   if (!url_service->CanReplaceKeyword(keyword, url, &current_url))
    177     return;
    178 
    179   if (current_url) {
    180     if (current_url->originating_url().is_valid()) {
    181       // The existing keyword was generated from an OpenSearch description
    182       // document, don't regenerate.
    183       return;
    184     }
    185     url_service->Remove(current_url);
    186   }
    187 
    188   TemplateURLData data;
    189   data.short_name = keyword;
    190   data.SetKeyword(keyword);
    191   data.SetURL(url.spec());
    192   DCHECK(controller.GetLastCommittedEntry());
    193   const GURL& current_favicon =
    194       controller.GetLastCommittedEntry()->GetFavicon().url;
    195   // If the favicon url isn't valid, it means there really isn't a favicon, or
    196   // the favicon url wasn't obtained before the load started. This assumes the
    197   // latter.
    198   // TODO(sky): Need a way to set the favicon that doesn't involve generating
    199   // its url.
    200   data.favicon_url = current_favicon.is_valid() ?
    201       current_favicon : TemplateURL::GenerateFaviconURL(params.referrer.url);
    202   data.safe_for_autoreplace = true;
    203   data.input_encodings.push_back(params.searchable_form_encoding);
    204   url_service->Add(new TemplateURL(data));
    205 }
    206