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 "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_model.h"
11 #include "chrome/browser/ui/search_engines/template_url_fetcher_ui_callbacks.h"
12 #include "chrome/common/render_messages.h"
13 #include "content/common/view_messages.h"
14 #include "content/browser/tab_contents/tab_contents.h"
15
16 namespace {
17
18 // Returns true if the entry's transition type is FORM_SUBMIT.
IsFormSubmit(const NavigationEntry * entry)19 bool IsFormSubmit(const NavigationEntry* entry) {
20 return (PageTransition::StripQualifier(entry->transition_type()) ==
21 PageTransition::FORM_SUBMIT);
22 }
23
24 } // namespace
25
SearchEngineTabHelper(TabContents * tab_contents)26 SearchEngineTabHelper::SearchEngineTabHelper(TabContents* tab_contents)
27 : TabContentsObserver(tab_contents) {
28 DCHECK(tab_contents);
29 }
30
~SearchEngineTabHelper()31 SearchEngineTabHelper::~SearchEngineTabHelper() {
32 }
33
DidNavigateMainFramePostCommit(const NavigationController::LoadCommittedDetails &,const ViewHostMsg_FrameNavigate_Params & params)34 void SearchEngineTabHelper::DidNavigateMainFramePostCommit(
35 const NavigationController::LoadCommittedDetails& /*details*/,
36 const ViewHostMsg_FrameNavigate_Params& params) {
37 GenerateKeywordIfNecessary(params);
38 }
39
OnMessageReceived(const IPC::Message & message)40 bool SearchEngineTabHelper::OnMessageReceived(const IPC::Message& message) {
41 bool handled = true;
42 IPC_BEGIN_MESSAGE_MAP(SearchEngineTabHelper, message)
43 IPC_MESSAGE_HANDLER(ViewHostMsg_PageHasOSDD, OnPageHasOSDD)
44 IPC_MESSAGE_UNHANDLED(handled = false)
45 IPC_END_MESSAGE_MAP()
46
47 return handled;
48 }
49
OnPageHasOSDD(int32 page_id,const GURL & doc_url,const search_provider::OSDDType & msg_provider_type)50 void SearchEngineTabHelper::OnPageHasOSDD(
51 int32 page_id,
52 const GURL& doc_url,
53 const search_provider::OSDDType& msg_provider_type) {
54 // Checks to see if we should generate a keyword based on the OSDD, and if
55 // necessary uses TemplateURLFetcher to download the OSDD and create a
56 // keyword.
57
58 // Make sure page_id is the current page and other basic checks.
59 DCHECK(doc_url.is_valid());
60 if (!tab_contents()->IsActiveEntry(page_id))
61 return;
62 if (!tab_contents()->profile()->GetTemplateURLFetcher())
63 return;
64 if (tab_contents()->profile()->IsOffTheRecord())
65 return;
66
67 TemplateURLFetcher::ProviderType provider_type;
68 switch (msg_provider_type) {
69 case search_provider::AUTODETECTED_PROVIDER:
70 provider_type = TemplateURLFetcher::AUTODETECTED_PROVIDER;
71 break;
72
73 case search_provider::EXPLICIT_DEFAULT_PROVIDER:
74 provider_type = TemplateURLFetcher::EXPLICIT_DEFAULT_PROVIDER;
75 break;
76
77 case search_provider::EXPLICIT_PROVIDER:
78 provider_type = TemplateURLFetcher::EXPLICIT_PROVIDER;
79 break;
80
81 default:
82 NOTREACHED();
83 return;
84 }
85
86 const NavigationController& controller = tab_contents()->controller();
87 const NavigationEntry* entry = controller.GetLastCommittedEntry();
88 DCHECK(entry);
89
90 const NavigationEntry* base_entry = entry;
91 if (IsFormSubmit(base_entry)) {
92 // If the current page is a form submit, find the last page that was not
93 // a form submit and use its url to generate the keyword from.
94 int index = controller.last_committed_entry_index() - 1;
95 while (index >= 0 && IsFormSubmit(controller.GetEntryAtIndex(index)))
96 index--;
97 if (index >= 0)
98 base_entry = controller.GetEntryAtIndex(index);
99 else
100 base_entry = NULL;
101 }
102
103 // We want to use the user typed URL if available since that represents what
104 // the user typed to get here, and fall back on the regular URL if not.
105 if (!base_entry)
106 return;
107 GURL keyword_url = base_entry->user_typed_url().is_valid() ?
108 base_entry->user_typed_url() : base_entry->url();
109 if (!keyword_url.is_valid())
110 return;
111
112 string16 keyword = TemplateURLModel::GenerateKeyword(
113 keyword_url,
114 provider_type == TemplateURLFetcher::AUTODETECTED_PROVIDER);
115
116 // Download the OpenSearch description document. If this is successful, a
117 // new keyword will be created when done.
118 tab_contents()->profile()->GetTemplateURLFetcher()->ScheduleDownload(
119 keyword,
120 doc_url,
121 base_entry->favicon().url(),
122 new TemplateURLFetcherUICallbacks(this, tab_contents()),
123 provider_type);
124 }
125
GenerateKeywordIfNecessary(const ViewHostMsg_FrameNavigate_Params & params)126 void SearchEngineTabHelper::GenerateKeywordIfNecessary(
127 const ViewHostMsg_FrameNavigate_Params& params) {
128 if (!params.searchable_form_url.is_valid())
129 return;
130
131 if (tab_contents()->profile()->IsOffTheRecord())
132 return;
133
134 const NavigationController& controller = tab_contents()->controller();
135 int last_index = controller.last_committed_entry_index();
136 // When there was no previous page, the last index will be 0. This is
137 // normally due to a form submit that opened in a new tab.
138 // TODO(brettw) bug 916126: we should support keywords when form submits
139 // happen in new tabs.
140 if (last_index <= 0)
141 return;
142 const NavigationEntry* previous_entry =
143 controller.GetEntryAtIndex(last_index - 1);
144 if (IsFormSubmit(previous_entry)) {
145 // Only generate a keyword if the previous page wasn't itself a form
146 // submit.
147 return;
148 }
149
150 GURL keyword_url = previous_entry->user_typed_url().is_valid() ?
151 previous_entry->user_typed_url() : previous_entry->url();
152 string16 keyword =
153 TemplateURLModel::GenerateKeyword(keyword_url, true); // autodetected
154 if (keyword.empty())
155 return;
156
157 TemplateURLModel* url_model =
158 tab_contents()->profile()->GetTemplateURLModel();
159 if (!url_model)
160 return;
161
162 if (!url_model->loaded()) {
163 url_model->Load();
164 return;
165 }
166
167 const TemplateURL* current_url;
168 GURL url = params.searchable_form_url;
169 if (!url_model->CanReplaceKeyword(keyword, url, ¤t_url))
170 return;
171
172 if (current_url) {
173 if (current_url->originating_url().is_valid()) {
174 // The existing keyword was generated from an OpenSearch description
175 // document, don't regenerate.
176 return;
177 }
178 url_model->Remove(current_url);
179 }
180 TemplateURL* new_url = new TemplateURL();
181 new_url->set_keyword(keyword);
182 new_url->set_short_name(keyword);
183 new_url->SetURL(url.spec(), 0, 0);
184 new_url->add_input_encoding(params.searchable_form_encoding);
185 DCHECK(controller.GetLastCommittedEntry());
186 const GURL& favicon_url =
187 controller.GetLastCommittedEntry()->favicon().url();
188 if (favicon_url.is_valid()) {
189 new_url->SetFaviconURL(favicon_url);
190 } else {
191 // The favicon url isn't valid. This means there really isn't a favicon,
192 // or the favicon url wasn't obtained before the load started. This assumes
193 // the later.
194 // TODO(sky): Need a way to set the favicon that doesn't involve generating
195 // its url.
196 new_url->SetFaviconURL(TemplateURL::GenerateFaviconURL(params.referrer));
197 }
198 new_url->set_safe_for_autoreplace(true);
199 url_model->Add(new_url);
200 }
201