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