OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/ui/app_list/search/answer_card/answer_card_search_provi
der.h" | 5 #include "chrome/browser/ui/app_list/search/answer_card/answer_card_search_provi
der.h" |
6 | 6 |
7 #include <utility> | 7 #include <utility> |
8 | 8 |
9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
10 #include "base/metrics/histogram_macros.h" | 10 #include "base/metrics/histogram_macros.h" |
11 #include "base/metrics/user_metrics.h" | 11 #include "base/metrics/user_metrics.h" |
12 #include "base/strings/utf_string_conversions.h" | 12 #include "base/strings/utf_string_conversions.h" |
13 #include "chrome/browser/search_engines/template_url_service_factory.h" | 13 #include "chrome/browser/search_engines/template_url_service_factory.h" |
14 #include "chrome/browser/ui/app_list/search/answer_card/answer_card_result.h" | 14 #include "chrome/browser/ui/app_list/search/answer_card/answer_card_result.h" |
15 #include "chrome/browser/ui/browser_navigator.h" | 15 #include "chrome/browser/ui/browser_navigator.h" |
16 #include "chrome/browser/ui/browser_navigator_params.h" | 16 #include "chrome/browser/ui/browser_navigator_params.h" |
17 #include "components/omnibox/browser/autocomplete_input.h" | 17 #include "components/omnibox/browser/autocomplete_input.h" |
18 #include "components/omnibox/browser/autocomplete_match.h" | 18 #include "components/omnibox/browser/autocomplete_match.h" |
19 #include "components/search_engines/template_url_service.h" | 19 #include "components/search_engines/template_url_service.h" |
20 #include "content/public/browser/navigation_handle.h" | |
21 #include "content/public/browser/page_navigator.h" | |
22 #include "net/http/http_response_headers.h" | |
23 #include "net/http/http_status_code.h" | |
24 #include "ui/app_list/app_list_features.h" | 20 #include "ui/app_list/app_list_features.h" |
25 #include "ui/app_list/app_list_model.h" | 21 #include "ui/app_list/app_list_model.h" |
26 #include "ui/app_list/search_box_model.h" | 22 #include "ui/app_list/search_box_model.h" |
27 | 23 |
28 namespace app_list { | 24 namespace app_list { |
29 | 25 |
30 namespace { | 26 namespace { |
31 | 27 |
32 enum class SearchAnswerRequestResult { | 28 enum class SearchAnswerRequestResult { |
33 REQUEST_RESULT_ANOTHER_REQUEST_STARTED = 0, | 29 REQUEST_RESULT_ANOTHER_REQUEST_STARTED = 0, |
34 REQUEST_RESULT_REQUEST_FAILED = 1, | 30 REQUEST_RESULT_REQUEST_FAILED = 1, |
35 REQUEST_RESULT_NO_ANSWER = 2, | 31 REQUEST_RESULT_NO_ANSWER = 2, |
36 REQUEST_RESULT_RECEIVED_ANSWER = 3, | 32 REQUEST_RESULT_RECEIVED_ANSWER = 3, |
37 REQUEST_RESULT_RECEIVED_ANSWER_TOO_LARGE = 4, | 33 REQUEST_RESULT_RECEIVED_ANSWER_TOO_LARGE = 4, |
38 REQUEST_RESULT_MAX = 5 | 34 REQUEST_RESULT_MAX = 5 |
39 }; | 35 }; |
40 | 36 |
41 constexpr char kSearchAnswerHasResult[] = "SearchAnswer-HasResult"; | |
42 constexpr char kSearchAnswerIssuedQuery[] = "SearchAnswer-IssuedQuery"; | |
43 constexpr char kSearchAnswerTitle[] = "SearchAnswer-Title"; | |
44 | |
45 void RecordRequestResult(SearchAnswerRequestResult request_result) { | 37 void RecordRequestResult(SearchAnswerRequestResult request_result) { |
46 UMA_HISTOGRAM_ENUMERATION("SearchAnswer.RequestResult", request_result, | 38 UMA_HISTOGRAM_ENUMERATION("SearchAnswer.RequestResult", request_result, |
47 SearchAnswerRequestResult::REQUEST_RESULT_MAX); | 39 SearchAnswerRequestResult::REQUEST_RESULT_MAX); |
48 } | 40 } |
49 | 41 |
50 } // namespace | 42 } // namespace |
51 | 43 |
52 AnswerCardSearchProvider::AnswerCardSearchProvider( | 44 AnswerCardSearchProvider::AnswerCardSearchProvider( |
53 Profile* profile, | 45 Profile* profile, |
54 app_list::AppListModel* model, | 46 app_list::AppListModel* model, |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
109 void AnswerCardSearchProvider::UpdatePreferredSize(const gfx::Size& pref_size) { | 101 void AnswerCardSearchProvider::UpdatePreferredSize(const gfx::Size& pref_size) { |
110 preferred_size_ = pref_size; | 102 preferred_size_ = pref_size; |
111 OnResultAvailable(received_answer_ && IsCardSizeOk() && | 103 OnResultAvailable(received_answer_ && IsCardSizeOk() && |
112 !contents_->IsLoading()); | 104 !contents_->IsLoading()); |
113 if (!answer_loaded_time_.is_null()) { | 105 if (!answer_loaded_time_.is_null()) { |
114 UMA_HISTOGRAM_TIMES("SearchAnswer.ResizeAfterLoadTime", | 106 UMA_HISTOGRAM_TIMES("SearchAnswer.ResizeAfterLoadTime", |
115 base::TimeTicks::Now() - answer_loaded_time_); | 107 base::TimeTicks::Now() - answer_loaded_time_); |
116 } | 108 } |
117 } | 109 } |
118 | 110 |
119 content::WebContents* AnswerCardSearchProvider::OpenURLFromTab( | |
120 const content::OpenURLParams& params) { | |
121 // Open the user-clicked link in the browser taking into account the requested | |
122 // disposition. | |
123 chrome::NavigateParams new_tab_params(profile_, params.url, | |
124 params.transition); | |
125 | |
126 new_tab_params.disposition = params.disposition; | |
127 | |
128 if (params.disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB) { | |
129 // When the user asks to open a link as a background tab, we show an | |
130 // activated window with the new activated tab after the user closes the | |
131 // launcher. So it's "background" relative to the launcher itself. | |
132 new_tab_params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; | |
133 new_tab_params.window_action = chrome::NavigateParams::SHOW_WINDOW_INACTIVE; | |
134 } | |
135 | |
136 chrome::Navigate(&new_tab_params); | |
137 | |
138 base::RecordAction(base::UserMetricsAction("SearchAnswer_OpenedUrl")); | |
139 | |
140 return new_tab_params.target_contents; | |
141 } | |
142 | |
143 void AnswerCardSearchProvider::DidFinishNavigation( | 111 void AnswerCardSearchProvider::DidFinishNavigation( |
144 content::NavigationHandle* navigation_handle) { | 112 const GURL& url, |
145 if (navigation_handle->GetURL() != current_request_url_) { | 113 bool has_error, |
| 114 bool has_answer_card, |
| 115 const std::string& result_title, |
| 116 const std::string& issued_query) { |
| 117 if (url != current_request_url_) { |
146 // TODO(vadimt): Remove this and similar logging once testing is complete if | 118 // TODO(vadimt): Remove this and similar logging once testing is complete if |
147 // we think this is not useful after release or happens too frequently. | 119 // we think this is not useful after release or happens too frequently. |
148 VLOG(1) << "DidFinishNavigation: Another request started"; | 120 VLOG(1) << "DidFinishNavigation: Another request started"; |
149 RecordRequestResult( | 121 RecordRequestResult( |
150 SearchAnswerRequestResult::REQUEST_RESULT_ANOTHER_REQUEST_STARTED); | 122 SearchAnswerRequestResult::REQUEST_RESULT_ANOTHER_REQUEST_STARTED); |
151 return; | 123 return; |
152 } | 124 } |
153 | 125 |
154 VLOG(1) << "DidFinishNavigation: Latest request completed"; | 126 VLOG(1) << "DidFinishNavigation: Latest request completed"; |
155 if (!navigation_handle->HasCommitted() || navigation_handle->IsErrorPage() || | 127 if (has_error) { |
156 !navigation_handle->IsInMainFrame()) { | |
157 LOG(ERROR) << "Failed to navigate: HasCommitted=" | |
158 << navigation_handle->HasCommitted() | |
159 << ", IsErrorPage=" << navigation_handle->IsErrorPage() | |
160 << ", IsInMainFrame=" << navigation_handle->IsInMainFrame(); | |
161 RecordRequestResult( | 128 RecordRequestResult( |
162 SearchAnswerRequestResult::REQUEST_RESULT_REQUEST_FAILED); | 129 SearchAnswerRequestResult::REQUEST_RESULT_REQUEST_FAILED); |
163 return; | 130 return; |
164 } | 131 } |
165 | 132 |
166 if (!features::IsAnswerCardDarkRunEnabled()) { | 133 if (!features::IsAnswerCardDarkRunEnabled()) { |
167 if (!ParseResponseHeaders(navigation_handle->GetResponseHeaders())) { | 134 if (!has_answer_card || result_title.empty()) { |
168 RecordRequestResult(SearchAnswerRequestResult::REQUEST_RESULT_NO_ANSWER); | 135 RecordRequestResult(SearchAnswerRequestResult::REQUEST_RESULT_NO_ANSWER); |
169 return; | 136 return; |
170 } | 137 } |
| 138 result_title_ = result_title; |
| 139 if (!issued_query.empty()) |
| 140 result_url_ = GetResultUrl(base::UTF8ToUTF16(issued_query)); |
171 } else { | 141 } else { |
172 // In the dark run mode, every other "server response" contains a card. | 142 // In the dark run mode, every other "server response" contains a card. |
173 dark_run_received_answer_ = !dark_run_received_answer_; | 143 dark_run_received_answer_ = !dark_run_received_answer_; |
174 if (!dark_run_received_answer_) | 144 if (!dark_run_received_answer_) |
175 return; | 145 return; |
176 // SearchResult requires a non-empty id. This "url" will never be opened. | 146 // SearchResult requires a non-empty id. This "url" will never be opened. |
177 result_url_ = "some string"; | 147 result_url_ = "https://www.google.com/?q=something"; |
178 } | 148 } |
179 | 149 |
180 received_answer_ = true; | 150 received_answer_ = true; |
181 UMA_HISTOGRAM_TIMES("SearchAnswer.NavigationTime", | 151 UMA_HISTOGRAM_TIMES("SearchAnswer.NavigationTime", |
182 base::TimeTicks::Now() - server_request_start_time_); | 152 base::TimeTicks::Now() - server_request_start_time_); |
183 } | 153 } |
184 | 154 |
185 void AnswerCardSearchProvider::DidStopLoading() { | 155 void AnswerCardSearchProvider::DidStopLoading() { |
186 if (!received_answer_) | 156 if (!received_answer_) |
187 return; | 157 return; |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
231 GURL(result_url_), AutocompleteInput(), template_url_service_, | 201 GURL(result_url_), AutocompleteInput(), template_url_service_, |
232 base::string16() /* keyword */); | 202 base::string16() /* keyword */); |
233 | 203 |
234 results.emplace_back(base::MakeUnique<AnswerCardResult>( | 204 results.emplace_back(base::MakeUnique<AnswerCardResult>( |
235 profile_, list_controller_, result_url_, stripped_result_url.spec(), | 205 profile_, list_controller_, result_url_, stripped_result_url.spec(), |
236 base::UTF8ToUTF16(result_title_), contents_.get())); | 206 base::UTF8ToUTF16(result_title_), contents_.get())); |
237 } | 207 } |
238 SwapResults(&results); | 208 SwapResults(&results); |
239 } | 209 } |
240 | 210 |
241 bool AnswerCardSearchProvider::ParseResponseHeaders( | |
242 const net::HttpResponseHeaders* headers) { | |
243 if (!headers) { | |
244 LOG(ERROR) << "Failed to parse response headers: no headers"; | |
245 return false; | |
246 } | |
247 if (headers->response_code() != net::HTTP_OK) { | |
248 LOG(ERROR) << "Failed to parse response headers: response code=" | |
249 << headers->response_code(); | |
250 return false; | |
251 } | |
252 if (!headers->HasHeaderValue(kSearchAnswerHasResult, "true")) { | |
253 LOG(ERROR) << "Failed to parse response headers: " << kSearchAnswerHasResult | |
254 << " header != true"; | |
255 return false; | |
256 } | |
257 if (!headers->GetNormalizedHeader(kSearchAnswerTitle, &result_title_)) { | |
258 LOG(ERROR) << "Failed to parse response headers: " << kSearchAnswerTitle | |
259 << " header is not present"; | |
260 return false; | |
261 } | |
262 | |
263 std::string issued_query; | |
264 // TODO(749320): Make the header mandatory once the production server starts | |
265 // serving it. | |
266 if (headers->GetNormalizedHeader(kSearchAnswerIssuedQuery, &issued_query)) { | |
267 // The header contains the autocompleted query that corresponds to the card | |
268 // contents. Use it for the open-URL, and for deduplication with the omnibox | |
269 // search results. | |
270 result_url_ = GetResultUrl(base::UTF8ToUTF16(issued_query)); | |
271 } else { | |
272 VLOG(1) << "Warning: " << kSearchAnswerIssuedQuery | |
273 << " header is not present"; | |
274 } | |
275 | |
276 return true; | |
277 } | |
278 | |
279 std::string AnswerCardSearchProvider::GetResultUrl( | 211 std::string AnswerCardSearchProvider::GetResultUrl( |
280 const base::string16& query) const { | 212 const base::string16& query) const { |
281 return template_url_service_->GetDefaultSearchProvider() | 213 return template_url_service_->GetDefaultSearchProvider() |
282 ->url_ref() | 214 ->url_ref() |
283 .ReplaceSearchTerms(TemplateURLRef::SearchTermsArgs(query), | 215 .ReplaceSearchTerms(TemplateURLRef::SearchTermsArgs(query), |
284 template_url_service_->search_terms_data()); | 216 template_url_service_->search_terms_data()); |
285 } | 217 } |
286 | 218 |
287 } // namespace app_list | 219 } // namespace app_list |
OLD | NEW |