OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 "components/omnibox/autocomplete_match.h" | 5 #include "components/omnibox/autocomplete_match.h" |
6 | 6 |
7 #include "base/i18n/time_formatting.h" | 7 #include "base/i18n/time_formatting.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/strings/string16.h" | 9 #include "base/strings/string16.h" |
10 #include "base/strings/string_number_conversions.h" | 10 #include "base/strings/string_number_conversions.h" |
11 #include "base/strings/string_piece.h" | 11 #include "base/strings/string_piece.h" |
12 #include "base/strings/string_split.h" | 12 #include "base/strings/string_split.h" |
13 #include "base/strings/string_util.h" | 13 #include "base/strings/string_util.h" |
14 #include "base/strings/utf_string_conversions.h" | 14 #include "base/strings/utf_string_conversions.h" |
15 #include "base/time/time.h" | 15 #include "base/time/time.h" |
16 #include "components/omnibox/autocomplete_provider.h" | 16 #include "components/omnibox/autocomplete_provider.h" |
17 #include "components/omnibox/suggestion_answer.h" | 17 #include "components/omnibox/suggestion_answer.h" |
18 #include "components/search_engines/template_url.h" | 18 #include "components/search_engines/template_url.h" |
19 #include "components/search_engines/template_url_service.h" | 19 #include "components/search_engines/template_url_service.h" |
20 #include "grit/components_scaled_resources.h" | 20 #include "grit/components_scaled_resources.h" |
| 21 #include "net/base/net_util.h" |
21 | 22 |
22 namespace { | 23 namespace { |
23 | 24 |
24 bool IsTrivialClassification(const ACMatchClassifications& classifications) { | 25 bool IsTrivialClassification(const ACMatchClassifications& classifications) { |
25 return classifications.empty() || | 26 return classifications.empty() || |
26 ((classifications.size() == 1) && | 27 ((classifications.size() == 1) && |
27 (classifications.back().style == ACMatchClassification::NONE)); | 28 (classifications.back().style == ACMatchClassification::NONE)); |
28 } | 29 } |
29 | 30 |
| 31 // Returns true if one of the |terms_prefixed_by_http_or_https| matches the |
| 32 // beginning of the URL (sans scheme). (Recall that |
| 33 // |terms_prefixed_by_http_or_https|, for the input "http://a b" will be |
| 34 // ["a"].) This suggests that the user wants a particular URL with a scheme |
| 35 // in mind, hence the caller should not consider another URL like this one |
| 36 // but with a different scheme to be a duplicate. |languages| is used to |
| 37 // format punycoded URLs to decide if they match. |
| 38 bool WordMatchesURLContent( |
| 39 const std::vector<base::string16>& terms_prefixed_by_http_or_https, |
| 40 const std::string& languages, |
| 41 const GURL& url) { |
| 42 size_t prefix_length = |
| 43 url.scheme().length() + strlen(url::kStandardSchemeSeparator); |
| 44 DCHECK_GE(url.spec().length(), prefix_length); |
| 45 const base::string16& formatted_url = net::FormatUrl( |
| 46 url, languages, net::kFormatUrlOmitNothing, net::UnescapeRule::NORMAL, |
| 47 NULL, NULL, &prefix_length); |
| 48 if (prefix_length == base::string16::npos) |
| 49 return false; |
| 50 const base::string16& formatted_url_without_scheme = |
| 51 formatted_url.substr(prefix_length); |
| 52 for (const auto& term : terms_prefixed_by_http_or_https) { |
| 53 if (base::StartsWith(formatted_url_without_scheme, term, |
| 54 base::CompareCase::SENSITIVE)) |
| 55 return true; |
| 56 } |
| 57 return false; |
| 58 } |
| 59 |
30 } // namespace | 60 } // namespace |
31 | 61 |
32 // AutocompleteMatch ---------------------------------------------------------- | 62 // AutocompleteMatch ---------------------------------------------------------- |
33 | 63 |
34 // static | 64 // static |
35 const base::char16 AutocompleteMatch::kInvalidChars[] = { | 65 const base::char16 AutocompleteMatch::kInvalidChars[] = { |
36 '\n', '\r', '\t', | 66 '\n', '\r', '\t', |
37 0x2028, // Line separator | 67 0x2028, // Line separator |
38 0x2029, // Paragraph separator | 68 0x2029, // Paragraph separator |
39 0 | 69 0 |
(...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
379 return NULL; | 409 return NULL; |
380 TemplateURL* template_url = keyword.empty() ? | 410 TemplateURL* template_url = keyword.empty() ? |
381 NULL : template_url_service->GetTemplateURLForKeyword(keyword); | 411 NULL : template_url_service->GetTemplateURLForKeyword(keyword); |
382 return (template_url || host.empty()) ? | 412 return (template_url || host.empty()) ? |
383 template_url : template_url_service->GetTemplateURLForHost(host); | 413 template_url : template_url_service->GetTemplateURLForHost(host); |
384 } | 414 } |
385 | 415 |
386 // static | 416 // static |
387 GURL AutocompleteMatch::GURLToStrippedGURL( | 417 GURL AutocompleteMatch::GURLToStrippedGURL( |
388 const GURL& url, | 418 const GURL& url, |
| 419 const AutocompleteInput& input, |
| 420 const std::string& languages, |
389 TemplateURLService* template_url_service, | 421 TemplateURLService* template_url_service, |
390 const base::string16& keyword) { | 422 const base::string16& keyword) { |
391 if (!url.is_valid()) | 423 if (!url.is_valid()) |
392 return url; | 424 return url; |
393 | 425 |
394 GURL stripped_destination_url = url; | 426 GURL stripped_destination_url = url; |
395 | 427 |
396 // If the destination URL looks like it was generated from a TemplateURL, | 428 // If the destination URL looks like it was generated from a TemplateURL, |
397 // remove all substitutions other than the search terms. This allows us | 429 // remove all substitutions other than the search terms. This allows us |
398 // to eliminate cases like past search URLs from history that differ only | 430 // to eliminate cases like past search URLs from history that differ only |
(...skipping 25 matching lines...) Expand all Loading... |
424 | 456 |
425 // Remove the www. prefix from the host. | 457 // Remove the www. prefix from the host. |
426 static const char prefix[] = "www."; | 458 static const char prefix[] = "www."; |
427 static const size_t prefix_len = arraysize(prefix) - 1; | 459 static const size_t prefix_len = arraysize(prefix) - 1; |
428 std::string host = stripped_destination_url.host(); | 460 std::string host = stripped_destination_url.host(); |
429 if (host.compare(0, prefix_len, prefix) == 0) { | 461 if (host.compare(0, prefix_len, prefix) == 0) { |
430 replacements.SetHostStr(base::StringPiece(host).substr(prefix_len)); | 462 replacements.SetHostStr(base::StringPiece(host).substr(prefix_len)); |
431 needs_replacement = true; | 463 needs_replacement = true; |
432 } | 464 } |
433 | 465 |
434 // Replace https protocol with http protocol. | 466 // Remove any trailing slash (if it's not a lone slash), or add a slash (to |
435 if (stripped_destination_url.SchemeIs(url::kHttpsScheme)) { | 467 // make a lone slash) if the path is empty. (We can't unconditionally |
| 468 // remove even lone slashes because for some schemes the path must consist |
| 469 // of at least a slash.) |
| 470 const std::string& path = stripped_destination_url.path(); |
| 471 if ((path.length() > 1) && (path[path.length() - 1] == '/')) { |
| 472 replacements.SetPathStr( |
| 473 base::StringPiece(path).substr(0, path.length() - 1)); |
| 474 needs_replacement = true; |
| 475 } else if (path.empty()) { |
| 476 static const char slash[] = "/"; |
| 477 replacements.SetPathStr(base::StringPiece(slash)); |
| 478 needs_replacement = true; |
| 479 } |
| 480 |
| 481 // Replace https protocol with http, as long as the user didn't explicitly |
| 482 // specify one of the two. |
| 483 if (stripped_destination_url.SchemeIs(url::kHttpsScheme) && |
| 484 (input.terms_prefixed_by_http_or_https().empty() || |
| 485 !WordMatchesURLContent( |
| 486 input.terms_prefixed_by_http_or_https(), languages, url))) { |
436 replacements.SetScheme(url::kHttpScheme, | 487 replacements.SetScheme(url::kHttpScheme, |
437 url::Component(0, strlen(url::kHttpScheme))); | 488 url::Component(0, strlen(url::kHttpScheme))); |
438 needs_replacement = true; | 489 needs_replacement = true; |
439 } | 490 } |
440 | 491 |
441 if (needs_replacement) | 492 if (needs_replacement) |
442 stripped_destination_url = stripped_destination_url.ReplaceComponents( | 493 stripped_destination_url = stripped_destination_url.ReplaceComponents( |
443 replacements); | 494 replacements); |
444 return stripped_destination_url; | 495 return stripped_destination_url; |
445 } | 496 } |
446 | 497 |
447 void AutocompleteMatch::ComputeStrippedDestinationURL( | 498 void AutocompleteMatch::ComputeStrippedDestinationURL( |
| 499 const AutocompleteInput& input, |
| 500 const std::string& languages, |
448 TemplateURLService* template_url_service) { | 501 TemplateURLService* template_url_service) { |
449 stripped_destination_url = | 502 stripped_destination_url = GURLToStrippedGURL( |
450 GURLToStrippedGURL(destination_url, template_url_service, keyword); | 503 destination_url, input, languages, template_url_service, keyword); |
451 } | 504 } |
452 | 505 |
453 void AutocompleteMatch::EnsureUWYTIsAllowedToBeDefault( | 506 void AutocompleteMatch::EnsureUWYTIsAllowedToBeDefault( |
454 const GURL& canonical_input_url, | 507 const AutocompleteInput& input, |
| 508 const std::string& languages, |
455 TemplateURLService* template_url_service) { | 509 TemplateURLService* template_url_service) { |
456 if (!allowed_to_be_default_match) { | 510 if (!allowed_to_be_default_match) { |
457 const GURL& stripped_canonical_input_url = | 511 const GURL& stripped_canonical_input_url = |
458 AutocompleteMatch::GURLToStrippedGURL( | 512 AutocompleteMatch::GURLToStrippedGURL( |
459 canonical_input_url, template_url_service, base::string16()); | 513 input.canonicalized_url(), input, languages, template_url_service, |
460 ComputeStrippedDestinationURL(template_url_service); | 514 base::string16()); |
| 515 ComputeStrippedDestinationURL(input, languages, template_url_service); |
461 allowed_to_be_default_match = | 516 allowed_to_be_default_match = |
462 stripped_canonical_input_url == stripped_destination_url; | 517 stripped_canonical_input_url == stripped_destination_url; |
463 } | 518 } |
464 } | 519 } |
465 | 520 |
466 void AutocompleteMatch::GetKeywordUIState( | 521 void AutocompleteMatch::GetKeywordUIState( |
467 TemplateURLService* template_url_service, | 522 TemplateURLService* template_url_service, |
468 base::string16* keyword, | 523 base::string16* keyword, |
469 bool* is_keyword_hint) const { | 524 bool* is_keyword_hint) const { |
470 *is_keyword_hint = associated_keyword.get() != NULL; | 525 *is_keyword_hint = associated_keyword.get() != NULL; |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
580 << " is unsorted in relation to last offset of " << last_offset | 635 << " is unsorted in relation to last offset of " << last_offset |
581 << ". Provider: " << provider_name << "."; | 636 << ". Provider: " << provider_name << "."; |
582 DCHECK_LT(i->offset, text.length()) | 637 DCHECK_LT(i->offset, text.length()) |
583 << " Classification of [" << i->offset << "," << text.length() | 638 << " Classification of [" << i->offset << "," << text.length() |
584 << "] is out of bounds for \"" << text << "\". Provider: " | 639 << "] is out of bounds for \"" << text << "\". Provider: " |
585 << provider_name << "."; | 640 << provider_name << "."; |
586 last_offset = i->offset; | 641 last_offset = i->offset; |
587 } | 642 } |
588 } | 643 } |
589 #endif | 644 #endif |
OLD | NEW |