OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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/android/contextualsearch/contextual_search_delegate.h" | 5 #include "chrome/browser/android/contextualsearch/contextual_search_delegate.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <utility> | 8 #include <utility> |
9 | 9 |
10 #include "base/base64.h" | 10 #include "base/base64.h" |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
91 template_url_service_(template_url_service), | 91 template_url_service_(template_url_service), |
92 search_term_callback_(search_term_callback), | 92 search_term_callback_(search_term_callback), |
93 surrounding_callback_(surrounding_callback), | 93 surrounding_callback_(surrounding_callback), |
94 icing_callback_(icing_callback) { | 94 icing_callback_(icing_callback) { |
95 field_trial_.reset(new ContextualSearchFieldTrial()); | 95 field_trial_.reset(new ContextualSearchFieldTrial()); |
96 } | 96 } |
97 | 97 |
98 ContextualSearchDelegate::~ContextualSearchDelegate() { | 98 ContextualSearchDelegate::~ContextualSearchDelegate() { |
99 } | 99 } |
100 | 100 |
101 void ContextualSearchDelegate::StartSearchTermResolutionRequest( | 101 void ContextualSearchDelegate::GatherAndSaveSurroundingText( |
102 const std::string& selection, | 102 ContextualSearchContext* contextual_search_context, |
103 const std::string& home_country, | 103 content::WebContents* web_contents) { |
104 content::WebContents* web_contents, | 104 DCHECK(web_contents); |
105 bool may_send_base_page_url) { | 105 RenderFrameHost::TextSurroundingSelectionCallback callback = |
106 GatherSurroundingTextWithCallback( | 106 base::Bind(&ContextualSearchDelegate::OnTextSurroundingSelectionAvailable, |
107 selection, home_country, web_contents, may_send_base_page_url, | 107 AsWeakPtr()); |
108 base::Bind(&ContextualSearchDelegate::StartSearchTermRequestFromSelection, | 108 context_ = contextual_search_context; |
109 AsWeakPtr())); | 109 context_->SetBasePageEncoding(web_contents->GetEncoding()); |
110 int surroundingTextSize = context_->CanResolve() | |
111 ? field_trial_->GetSurroundingSize() | |
Theresa
2017/03/09 22:46:37
Not in this CL, but in a follow up one, it seems t
Donn Denman
2017/03/10 01:29:43
Acknowledged.
| |
112 : field_trial_->GetIcingSurroundingSize(); | |
113 RenderFrameHost* focused_frame = web_contents->GetFocusedFrame(); | |
114 if (focused_frame) { | |
115 focused_frame->RequestTextSurroundingSelection(callback, | |
116 surroundingTextSize); | |
117 } else { | |
118 callback.Run(base::string16(), 0, 0); | |
119 } | |
110 } | 120 } |
111 | 121 |
112 void ContextualSearchDelegate::GatherAndSaveSurroundingText( | 122 void ContextualSearchDelegate::StartSearchTermResolutionRequest( |
113 const std::string& selection, | 123 ContextualSearchContext* contextual_search_context, |
114 const std::string& home_country, | 124 content::WebContents* web_contents) { |
115 content::WebContents* web_contents, | 125 DCHECK(web_contents); |
116 bool may_send_base_page_url) { | 126 DCHECK(context_ == contextual_search_context); |
117 GatherSurroundingTextWithCallback( | 127 DCHECK(context_->CanResolve()); |
118 selection, home_country, web_contents, may_send_base_page_url, | 128 |
119 base::Bind(&ContextualSearchDelegate::SaveSurroundingText, AsWeakPtr())); | 129 // Immediately cancel any request that's in flight, since we're building a new |
120 // TODO(donnd): clear the context here, since we're done with it (but risky). | 130 // context (and the response disposes of any existing context). |
131 search_term_fetcher_.reset(); | |
132 | |
133 // Decide if the URL should be sent with the context. | |
134 GURL page_url(web_contents->GetURL()); | |
135 if (context_->CanSendBasePageUrl() && | |
136 CanSendPageURL(page_url, ProfileManager::GetActiveUserProfile(), | |
137 template_url_service_)) { | |
138 context_->SetBasePageUrl(page_url); | |
139 } | |
140 ResolveSearchTermFromContext(); | |
121 } | 141 } |
122 | 142 |
123 void ContextualSearchDelegate::ContinueSearchTermResolutionRequest() { | 143 void ContextualSearchDelegate::ResolveSearchTermFromContext() { |
124 DCHECK(context_.get()); | 144 DCHECK(context_); |
125 if (!context_.get()) | 145 if (!context_) |
126 return; | 146 return; |
127 GURL request_url(BuildRequestUrl(context_->home_country)); | 147 GURL request_url(BuildRequestUrl(context_->GetHomeCountry())); |
128 DCHECK(request_url.is_valid()); | 148 DCHECK(request_url.is_valid()); |
129 | 149 |
130 // Reset will delete any previous fetcher, and we won't get any callback. | 150 // Reset will delete any previous fetcher, and we won't get any callback. |
131 search_term_fetcher_.reset( | 151 search_term_fetcher_.reset( |
132 net::URLFetcher::Create(kContextualSearchURLFetcherID, request_url, | 152 net::URLFetcher::Create(kContextualSearchURLFetcherID, request_url, |
133 net::URLFetcher::GET, this).release()); | 153 net::URLFetcher::GET, this).release()); |
134 search_term_fetcher_->SetRequestContext(url_request_context_); | 154 search_term_fetcher_->SetRequestContext(url_request_context_); |
135 | 155 |
136 // Add Chrome experiment state to the request headers. | 156 // Add Chrome experiment state to the request headers. |
137 net::HttpRequestHeaders headers; | 157 net::HttpRequestHeaders headers; |
(...skipping 21 matching lines...) Expand all Loading... | |
159 if (source->GetStatus().is_success() && response_code == net::HTTP_OK) { | 179 if (source->GetStatus().is_success() && response_code == net::HTTP_OK) { |
160 std::string response; | 180 std::string response; |
161 bool has_string_response = source->GetResponseAsString(&response); | 181 bool has_string_response = source->GetResponseAsString(&response); |
162 DCHECK(has_string_response); | 182 DCHECK(has_string_response); |
163 if (has_string_response) { | 183 if (has_string_response) { |
164 resolved_search_term = | 184 resolved_search_term = |
165 GetResolvedSearchTermFromJson(response_code, response); | 185 GetResolvedSearchTermFromJson(response_code, response); |
166 } | 186 } |
167 } | 187 } |
168 search_term_callback_.Run(*resolved_search_term); | 188 search_term_callback_.Run(*resolved_search_term); |
169 | |
170 // The ContextualSearchContext is consumed once the request has completed. | |
171 context_.reset(); | |
172 } | 189 } |
173 | 190 |
174 std::unique_ptr<ResolvedSearchTerm> | 191 std::unique_ptr<ResolvedSearchTerm> |
175 ContextualSearchDelegate::GetResolvedSearchTermFromJson( | 192 ContextualSearchDelegate::GetResolvedSearchTermFromJson( |
176 int response_code, | 193 int response_code, |
177 const std::string& json_string) { | 194 const std::string& json_string) { |
178 std::string search_term; | 195 std::string search_term; |
179 std::string display_text; | 196 std::string display_text; |
180 std::string alternate_term; | 197 std::string alternate_term; |
181 std::string mid; | 198 std::string mid; |
(...skipping 12 matching lines...) Expand all Loading... | |
194 json_string, &search_term, &display_text, &alternate_term, &mid, | 211 json_string, &search_term, &display_text, &alternate_term, &mid, |
195 &prevent_preload, &mention_start, &mention_end, &context_language, | 212 &prevent_preload, &mention_start, &mention_end, &context_language, |
196 &thumbnail_url, &caption, &quick_action_uri, &quick_action_category); | 213 &thumbnail_url, &caption, &quick_action_uri, &quick_action_category); |
197 if (mention_start != 0 || mention_end != 0) { | 214 if (mention_start != 0 || mention_end != 0) { |
198 // Sanity check that our selection is non-zero and it is less than | 215 // Sanity check that our selection is non-zero and it is less than |
199 // 100 characters as that would make contextual search bar hide. | 216 // 100 characters as that would make contextual search bar hide. |
200 // We also check that there is at least one character overlap between | 217 // We also check that there is at least one character overlap between |
201 // the new and old selection. | 218 // the new and old selection. |
202 if (mention_start >= mention_end || | 219 if (mention_start >= mention_end || |
203 (mention_end - mention_start) > kContextualSearchMaxSelection || | 220 (mention_end - mention_start) > kContextualSearchMaxSelection || |
204 mention_end <= context_->start_offset || | 221 mention_end <= context_->GetStartOffset() || |
205 mention_start >= context_->end_offset) { | 222 mention_start >= context_->GetEndOffset()) { |
206 start_adjust = 0; | 223 start_adjust = 0; |
207 end_adjust = 0; | 224 end_adjust = 0; |
208 } else { | 225 } else { |
209 start_adjust = mention_start - context_->start_offset; | 226 start_adjust = mention_start - context_->GetStartOffset(); |
210 end_adjust = mention_end - context_->end_offset; | 227 end_adjust = mention_end - context_->GetEndOffset(); |
211 } | 228 } |
212 } | 229 } |
213 bool is_invalid = response_code == net::URLFetcher::RESPONSE_CODE_INVALID; | 230 bool is_invalid = response_code == net::URLFetcher::RESPONSE_CODE_INVALID; |
214 return std::unique_ptr<ResolvedSearchTerm>(new ResolvedSearchTerm( | 231 return std::unique_ptr<ResolvedSearchTerm>(new ResolvedSearchTerm( |
215 is_invalid, response_code, search_term, display_text, alternate_term, mid, | 232 is_invalid, response_code, search_term, display_text, alternate_term, mid, |
216 prevent_preload == kDoPreventPreloadValue, start_adjust, end_adjust, | 233 prevent_preload == kDoPreventPreloadValue, start_adjust, end_adjust, |
217 context_language, thumbnail_url, caption, quick_action_uri, | 234 context_language, thumbnail_url, caption, quick_action_uri, |
218 quick_action_category)); | 235 quick_action_category)); |
219 } | 236 } |
220 | 237 |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
263 if (!replacement_url.empty()) { | 280 if (!replacement_url.empty()) { |
264 size_t pos = request.find(kContextualSearchServerEndpoint); | 281 size_t pos = request.find(kContextualSearchServerEndpoint); |
265 if (pos != std::string::npos) { | 282 if (pos != std::string::npos) { |
266 request.replace(0, pos + strlen(kContextualSearchServerEndpoint), | 283 request.replace(0, pos + strlen(kContextualSearchServerEndpoint), |
267 replacement_url); | 284 replacement_url); |
268 } | 285 } |
269 } | 286 } |
270 return request; | 287 return request; |
271 } | 288 } |
272 | 289 |
273 void ContextualSearchDelegate::GatherSurroundingTextWithCallback( | 290 void ContextualSearchDelegate::OnTextSurroundingSelectionAvailable( |
274 const std::string& selection, | |
275 const std::string& home_country, | |
276 content::WebContents* web_contents, | |
277 bool may_send_base_page_url, | |
278 HandleSurroundingsCallback callback) { | |
279 // Immediately cancel any request that's in flight, since we're building a new | |
280 // context (and the response disposes of any existing context). | |
281 search_term_fetcher_.reset(); | |
282 BuildContext(selection, home_country, web_contents, may_send_base_page_url); | |
283 DCHECK(web_contents); | |
284 RenderFrameHost* focused_frame = web_contents->GetFocusedFrame(); | |
285 if (focused_frame) { | |
286 focused_frame->RequestTextSurroundingSelection( | |
287 callback, field_trial_->GetSurroundingSize()); | |
288 } else { | |
289 callback.Run(base::string16(), 0, 0); | |
290 } | |
291 } | |
292 | |
293 void ContextualSearchDelegate::BuildContext(const std::string& selection, | |
294 const std::string& home_country, | |
295 content::WebContents* web_contents, | |
296 bool may_send_base_page_url) { | |
297 // Decide if the URL should be sent with the context. | |
298 GURL page_url(web_contents->GetURL()); | |
299 GURL url_to_send; | |
300 if (may_send_base_page_url && | |
301 CanSendPageURL(page_url, ProfileManager::GetActiveUserProfile(), | |
302 template_url_service_)) { | |
303 url_to_send = page_url; | |
304 } | |
305 std::string encoding(web_contents->GetEncoding()); | |
306 context_.reset(new ContextualSearchContext(selection, home_country, | |
307 url_to_send, encoding)); | |
308 } | |
309 | |
310 void ContextualSearchDelegate::StartSearchTermRequestFromSelection( | |
311 const base::string16& surrounding_text, | 291 const base::string16& surrounding_text, |
312 int start_offset, | 292 int start_offset, |
313 int end_offset) { | 293 int end_offset) { |
314 // TODO(donnd): figure out how to gather text surrounding the selection | 294 SaveSurroundingText(surrounding_text, start_offset, end_offset); |
315 // for other purposes too: e.g. to determine if we should select the | 295 if (context_->CanResolve()) |
316 // word where the user tapped. | |
317 if (context_.get()) { | |
318 SaveSurroundingText(surrounding_text, start_offset, end_offset); | |
319 SendSurroundingText(kSurroundingSizeForUI); | 296 SendSurroundingText(kSurroundingSizeForUI); |
320 ContinueSearchTermResolutionRequest(); | |
321 } else { | |
322 DVLOG(1) << "ctxs: Null context, ignored!"; | |
323 } | |
324 } | 297 } |
325 | 298 |
326 void ContextualSearchDelegate::SaveSurroundingText( | 299 void ContextualSearchDelegate::SaveSurroundingText( |
327 const base::string16& surrounding_text, | 300 const base::string16& surrounding_text, |
328 int start_offset, | 301 int start_offset, |
329 int end_offset) { | 302 int end_offset) { |
330 DCHECK(context_.get()); | 303 DCHECK(context_); |
331 // Sometimes the surroundings are 0, 0, '', so fall back on the selection. | 304 // Sometimes the surroundings are 0, 0, '', so fall back on the selection. |
332 // See crbug.com/393100. | 305 // See crbug.com/393100. |
306 bool use_selection = false; | |
333 if (start_offset == 0 && end_offset == 0 && surrounding_text.length() == 0) { | 307 if (start_offset == 0 && end_offset == 0 && surrounding_text.length() == 0) { |
334 context_->surrounding_text = base::UTF8ToUTF16(context_->selected_text); | 308 use_selection = true; |
335 context_->start_offset = 0; | 309 end_offset = context_->GetOriginalSelectedText().length(); |
336 context_->end_offset = context_->selected_text.length(); | |
337 } else { | |
338 context_->surrounding_text = surrounding_text; | |
339 context_->start_offset = start_offset; | |
340 context_->end_offset = end_offset; | |
341 } | 310 } |
311 const base::string16& surrounding_text_or_selection( | |
312 use_selection ? base::UTF8ToUTF16(context_->GetOriginalSelectedText()) | |
313 : surrounding_text); | |
342 | 314 |
343 // Pin the start and end offsets to ensure they point within the string. | 315 // Pin the start and end offsets to ensure they point within the string. |
344 int surrounding_length = context_->surrounding_text.length(); | 316 int surrounding_length = surrounding_text_or_selection.length(); |
345 context_->start_offset = | 317 start_offset = std::min(surrounding_length, std::max(0, start_offset)); |
346 std::min(surrounding_length, std::max(0, context_->start_offset)); | 318 end_offset = std::min(surrounding_length, std::max(0, end_offset)); |
347 context_->end_offset = | 319 |
348 std::min(surrounding_length, std::max(0, context_->end_offset)); | 320 context_->SetSelectionSurroundings(start_offset, end_offset, |
321 surrounding_text_or_selection); | |
349 | 322 |
350 // Call the Icing callback with a shortened copy of the surroundings. | 323 // Call the Icing callback with a shortened copy of the surroundings. |
351 int icing_surrounding_size = field_trial_->GetIcingSurroundingSize(); | 324 int icing_surrounding_size = field_trial_->GetIcingSurroundingSize(); |
352 size_t selection_start = context_->start_offset; | 325 size_t selection_start = start_offset; |
353 size_t selection_end = context_->end_offset; | 326 size_t selection_end = end_offset; |
354 if (icing_surrounding_size >= 0 && selection_start < selection_end) { | 327 if (icing_surrounding_size >= 0 && selection_start <= selection_end) { |
355 int icing_padding_each_side = icing_surrounding_size / 2; | 328 int icing_padding_each_side = icing_surrounding_size / 2; |
356 base::string16 icing_surrounding_text = SurroundingTextForIcing( | 329 base::string16 icing_surrounding_text = SurroundingTextForIcing( |
357 context_->surrounding_text, icing_padding_each_side, &selection_start, | 330 surrounding_text_or_selection, icing_padding_each_side, |
358 &selection_end); | 331 &selection_start, &selection_end); |
359 if (selection_start < selection_end) | 332 if (selection_start <= selection_end) |
360 icing_callback_.Run(context_->encoding, icing_surrounding_text, | 333 icing_callback_.Run(context_->GetBasePageEncoding(), |
361 selection_start, selection_end); | 334 icing_surrounding_text, selection_start, |
335 selection_end); | |
362 } | 336 } |
363 } | 337 } |
364 | 338 |
365 void ContextualSearchDelegate::SendSurroundingText(int max_surrounding_chars) { | 339 void ContextualSearchDelegate::SendSurroundingText(int max_surrounding_chars) { |
366 const base::string16& surrounding = context_->surrounding_text; | 340 const base::string16& surrounding = context_->GetSurroundingText(); |
367 | 341 |
368 // Determine the text after the selection. | 342 // Determine the text after the selection. |
369 int surrounding_length = surrounding.length(); // Cast to int. | 343 int surrounding_length = surrounding.length(); // Cast to int. |
370 int num_after_characters = std::min( | 344 int num_after_characters = std::min( |
371 surrounding_length - context_->end_offset, max_surrounding_chars); | 345 surrounding_length - context_->GetEndOffset(), max_surrounding_chars); |
372 base::string16 after_text = surrounding.substr( | 346 base::string16 after_text = |
373 context_->end_offset, num_after_characters); | 347 surrounding.substr(context_->GetEndOffset(), num_after_characters); |
374 | 348 |
375 base::TrimWhitespace(after_text, base::TRIM_ALL, &after_text); | 349 base::TrimWhitespace(after_text, base::TRIM_ALL, &after_text); |
376 surrounding_callback_.Run(UTF16ToUTF8(after_text)); | 350 surrounding_callback_.Run(UTF16ToUTF8(after_text)); |
377 } | 351 } |
378 | 352 |
379 void ContextualSearchDelegate::SetDiscourseContextAndAddToHeader( | 353 void ContextualSearchDelegate::SetDiscourseContextAndAddToHeader( |
380 const ContextualSearchContext& context) { | 354 const ContextualSearchContext& context) { |
381 search_term_fetcher_->AddExtraRequestHeader(GetDiscourseContext(context)); | 355 search_term_fetcher_->AddExtraRequestHeader(GetDiscourseContext(context)); |
382 } | 356 } |
383 | 357 |
384 std::string ContextualSearchDelegate::GetDiscourseContext( | 358 std::string ContextualSearchDelegate::GetDiscourseContext( |
385 const ContextualSearchContext& context) { | 359 const ContextualSearchContext& context) { |
386 discourse_context::ClientDiscourseContext proto; | 360 discourse_context::ClientDiscourseContext proto; |
387 discourse_context::Display* display = proto.add_display(); | 361 discourse_context::Display* display = proto.add_display(); |
388 display->set_uri(context.page_url.spec()); | 362 display->set_uri(context.GetBasePageUrl().spec()); |
389 | 363 |
390 discourse_context::Media* media = display->mutable_media(); | 364 discourse_context::Media* media = display->mutable_media(); |
391 media->set_mime_type(context.encoding); | 365 media->set_mime_type(context.GetBasePageEncoding()); |
392 | 366 |
393 discourse_context::Selection* selection = display->mutable_selection(); | 367 discourse_context::Selection* selection = display->mutable_selection(); |
394 selection->set_content(UTF16ToUTF8(context.surrounding_text)); | 368 selection->set_content(UTF16ToUTF8(context.GetSurroundingText())); |
395 selection->set_start(context.start_offset); | 369 selection->set_start(context.GetStartOffset()); |
396 selection->set_end(context.end_offset); | 370 selection->set_end(context.GetEndOffset()); |
397 selection->set_is_uri_encoded(false); | 371 selection->set_is_uri_encoded(false); |
398 | 372 |
399 std::string serialized; | 373 std::string serialized; |
400 proto.SerializeToString(&serialized); | 374 proto.SerializeToString(&serialized); |
401 | 375 |
402 std::string encoded_context; | 376 std::string encoded_context; |
403 base::Base64Encode(serialized, &encoded_context); | 377 base::Base64Encode(serialized, &encoded_context); |
404 // The server memoizer expects a web-safe encoding. | 378 // The server memoizer expects a web-safe encoding. |
405 std::replace(encoded_context.begin(), encoded_context.end(), '+', '-'); | 379 std::replace(encoded_context.begin(), encoded_context.end(), '+', '-'); |
406 std::replace(encoded_context.begin(), encoded_context.end(), '/', '_'); | 380 std::replace(encoded_context.begin(), encoded_context.end(), '/', '_'); |
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
599 end_offset -= trim; | 573 end_offset -= trim; |
600 } | 574 } |
601 if (result_text.length() > end_offset + padding_each_side_pinned) { | 575 if (result_text.length() > end_offset + padding_each_side_pinned) { |
602 // Trim the end. | 576 // Trim the end. |
603 result_text = result_text.substr(0, end_offset + padding_each_side_pinned); | 577 result_text = result_text.substr(0, end_offset + padding_each_side_pinned); |
604 } | 578 } |
605 *start = start_offset; | 579 *start = start_offset; |
606 *end = end_offset; | 580 *end = end_offset; |
607 return result_text; | 581 return result_text; |
608 } | 582 } |
OLD | NEW |