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.
5 #include "chrome/browser/ui/search_engines/search_engine_tab_helper.h"
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"
25 using content::NavigationController
;
26 using content::NavigationEntry
;
27 using content::WebContents
;
29 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SearchEngineTabHelper
);
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
);
39 base::string16
GenerateKeywordFromNavigationEntry(
40 const NavigationEntry
* entry
) {
41 // Don't autogenerate keywords for pages that are the result of form
43 if (IsFormSubmit(entry
))
44 return base::string16();
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();
52 return base::string16();
55 // Don't autogenerate keywords for referrers that are anything other than HTTP
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();
64 return TemplateURL::GenerateKeyword(url
);
67 void AssociateURLFetcherWithWebContents(content::WebContents
* web_contents
,
68 net::URLFetcher
* url_fetcher
) {
69 content::AssociateURLFetcherWithRenderFrame(
71 web_contents
->GetURL(),
72 web_contents
->GetRenderProcessHost()->GetID(),
73 web_contents
->GetMainFrame()->GetRoutingID());
78 SearchEngineTabHelper::~SearchEngineTabHelper() {
81 void SearchEngineTabHelper::DidNavigateMainFrame(
82 const content::LoadCommittedDetails
& /*details*/,
83 const content::FrameNavigateParams
& params
) {
84 GenerateKeywordIfNecessary(params
);
87 bool SearchEngineTabHelper::OnMessageReceived(const IPC::Message
& message
) {
89 IPC_BEGIN_MESSAGE_MAP(SearchEngineTabHelper
, message
)
90 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_PageHasOSDD
, OnPageHasOSDD
)
91 IPC_MESSAGE_UNHANDLED(handled
= false)
97 SearchEngineTabHelper::SearchEngineTabHelper(WebContents
* web_contents
)
98 : content::WebContentsObserver(web_contents
),
100 weak_ptr_factory_(this) {
101 DCHECK(web_contents
);
104 void SearchEngineTabHelper::OnPageHasOSDD(
105 const GURL
& page_url
,
106 const GURL
& osdd_url
,
107 const search_provider::OSDDType
& msg_provider_type
) {
108 // Checks to see if we should generate a keyword based on the OSDD, and if
109 // necessary uses TemplateURLFetcher to download the OSDD and create a
112 // Make sure that the page is the current page and other basic checks.
113 // When |page_url| has file: scheme, this method doesn't work because of
114 // http://b/issue?id=863583. For that reason, this doesn't check and allow
115 // urls referring to osdd urls with same schemes.
116 if (!osdd_url
.is_valid() || !osdd_url
.SchemeIsHTTPOrHTTPS())
120 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
121 if (page_url
!= web_contents()->GetLastCommittedURL() ||
122 !TemplateURLFetcherFactory::GetForProfile(profile
) ||
123 profile
->IsOffTheRecord())
126 TemplateURLFetcher::ProviderType provider_type
=
127 (msg_provider_type
== search_provider::AUTODETECTED_PROVIDER
) ?
128 TemplateURLFetcher::AUTODETECTED_PROVIDER
:
129 TemplateURLFetcher::EXPLICIT_PROVIDER
;
131 // If the current page is a form submit, find the last page that was not a
132 // form submit and use its url to generate the keyword from.
133 const NavigationController
& controller
= web_contents()->GetController();
134 const NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
135 for (int index
= controller
.GetLastCommittedEntryIndex();
136 (index
> 0) && IsFormSubmit(entry
);
137 entry
= controller
.GetEntryAtIndex(index
))
139 if (!entry
|| IsFormSubmit(entry
))
142 // Autogenerate a keyword for the autodetected case; in the other cases we'll
143 // generate a keyword later after fetching the OSDD.
144 base::string16 keyword
;
145 if (provider_type
== TemplateURLFetcher::AUTODETECTED_PROVIDER
) {
146 keyword
= GenerateKeywordFromNavigationEntry(entry
);
151 // Download the OpenSearch description document. If this is successful, a
152 // new keyword will be created when done.
153 TemplateURLFetcherFactory::GetForProfile(profile
)->ScheduleDownload(
154 keyword
, osdd_url
, entry
->GetFavicon().url
,
155 base::Bind(&AssociateURLFetcherWithWebContents
, web_contents()),
156 base::Bind(&SearchEngineTabHelper::OnDownloadedOSDD
,
157 weak_ptr_factory_
.GetWeakPtr()),
161 void SearchEngineTabHelper::OnDownloadedOSDD(
162 scoped_ptr
<TemplateURL
> template_url
) {
164 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
165 delegate_
->ConfirmAddSearchProvider(template_url
.release(), profile
);
168 void SearchEngineTabHelper::GenerateKeywordIfNecessary(
169 const content::FrameNavigateParams
& params
) {
170 if (!params
.searchable_form_url
.is_valid())
174 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
175 if (profile
->IsOffTheRecord())
178 const NavigationController
& controller
= web_contents()->GetController();
179 int last_index
= controller
.GetLastCommittedEntryIndex();
180 // When there was no previous page, the last index will be 0. This is
181 // normally due to a form submit that opened in a new tab.
182 // TODO(brettw) bug 916126: we should support keywords when form submits
183 // happen in new tabs.
187 base::string16
keyword(GenerateKeywordFromNavigationEntry(
188 controller
.GetEntryAtIndex(last_index
- 1)));
192 TemplateURLService
* url_service
=
193 TemplateURLServiceFactory::GetForProfile(profile
);
197 if (!url_service
->loaded()) {
202 TemplateURL
* current_url
;
203 GURL url
= params
.searchable_form_url
;
204 if (!url_service
->CanReplaceKeyword(keyword
, url
, ¤t_url
))
208 if (current_url
->originating_url().is_valid()) {
209 // The existing keyword was generated from an OpenSearch description
210 // document, don't regenerate.
213 url_service
->Remove(current_url
);
216 TemplateURLData data
;
217 data
.short_name
= keyword
;
218 data
.SetKeyword(keyword
);
219 data
.SetURL(url
.spec());
220 DCHECK(controller
.GetLastCommittedEntry());
221 const GURL
& current_favicon
=
222 controller
.GetLastCommittedEntry()->GetFavicon().url
;
223 // If the favicon url isn't valid, it means there really isn't a favicon, or
224 // the favicon url wasn't obtained before the load started. This assumes the
226 // TODO(sky): Need a way to set the favicon that doesn't involve generating
228 data
.favicon_url
= current_favicon
.is_valid() ?
229 current_favicon
: TemplateURL::GenerateFaviconURL(params
.referrer
.url
);
230 data
.safe_for_autoreplace
= true;
231 data
.input_encodings
.push_back(params
.searchable_form_encoding
);
232 url_service
->Add(new TemplateURL(data
));