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.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"
22 using content::NavigationController
;
23 using content::NavigationEntry
;
24 using content::WebContents
;
26 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SearchEngineTabHelper
);
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
);
36 base::string16
GenerateKeywordFromNavigationEntry(
37 const NavigationEntry
* entry
) {
38 // Don't autogenerate keywords for pages that are the result of form
40 if (IsFormSubmit(entry
))
41 return base::string16();
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();
49 return base::string16();
52 // Don't autogenerate keywords for referrers that are anything other than HTTP
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(content::kHttpScheme
) || (url
.path().length() > 1))
59 return base::string16();
61 return TemplateURLService::GenerateKeyword(url
);
66 SearchEngineTabHelper::~SearchEngineTabHelper() {
69 void SearchEngineTabHelper::DidNavigateMainFrame(
70 const content::LoadCommittedDetails
& /*details*/,
71 const content::FrameNavigateParams
& params
) {
72 GenerateKeywordIfNecessary(params
);
75 bool SearchEngineTabHelper::OnMessageReceived(const IPC::Message
& message
) {
77 IPC_BEGIN_MESSAGE_MAP(SearchEngineTabHelper
, message
)
78 IPC_MESSAGE_HANDLER(ChromeViewHostMsg_PageHasOSDD
, OnPageHasOSDD
)
79 IPC_MESSAGE_UNHANDLED(handled
= false)
85 SearchEngineTabHelper::SearchEngineTabHelper(WebContents
* web_contents
)
86 : content::WebContentsObserver(web_contents
) {
90 void SearchEngineTabHelper::OnPageHasOSDD(
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
98 // Make sure page_id is the current page and other basic checks.
99 if (!doc_url
.is_valid())
102 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
103 if (!web_contents()->IsActiveEntry(page_id
) ||
104 !TemplateURLFetcherFactory::GetForProfile(profile
) ||
105 profile
->IsOffTheRecord())
108 TemplateURLFetcher::ProviderType provider_type
=
109 (msg_provider_type
== search_provider::AUTODETECTED_PROVIDER
) ?
110 TemplateURLFetcher::AUTODETECTED_PROVIDER
:
111 TemplateURLFetcher::EXPLICIT_PROVIDER
;
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
))
121 if (IsFormSubmit(entry
))
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
);
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
, doc_url
, entry
->GetFavicon().url
, web_contents(),
137 new TemplateURLFetcherUICallbacks(this, web_contents()), provider_type
);
140 void SearchEngineTabHelper::GenerateKeywordIfNecessary(
141 const content::FrameNavigateParams
& params
) {
142 if (!params
.searchable_form_url
.is_valid())
146 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
147 if (profile
->IsOffTheRecord())
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.
159 base::string16
keyword(GenerateKeywordFromNavigationEntry(
160 controller
.GetEntryAtIndex(last_index
- 1)));
164 TemplateURLService
* url_service
=
165 TemplateURLServiceFactory::GetForProfile(profile
);
169 if (!url_service
->loaded()) {
174 TemplateURL
* current_url
;
175 GURL url
= params
.searchable_form_url
;
176 if (!url_service
->CanReplaceKeyword(keyword
, url
, ¤t_url
))
180 if (current_url
->originating_url().is_valid()) {
181 // The existing keyword was generated from an OpenSearch description
182 // document, don't regenerate.
185 url_service
->Remove(current_url
);
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
198 // TODO(sky): Need a way to set the favicon that doesn't involve generating
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(profile
, data
));