1// Copyright 2013 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/history/url_utils.h"
6
7#include <algorithm>
8
9#include "url/gurl.h"
10
11namespace history {
12
13namespace {
14
15// Comparator to enforce '\0' < '?' < '#' < '/' < other characters.
16int GetURLCharPriority(char ch) {
17  switch (ch) {
18    case '\0': return 0;
19    case '?': return 1;
20    case '#': return 2;
21    case '/': return 3;
22  }
23  return 4;
24}
25
26}  // namespace
27
28// Instead of splitting URLs and extract path components, we can implement
29// CanonicalURLStringCompare() using string operations only. The key idea is,
30// treating '/' to be less than any valid path characters would make it behave
31// as a separator, so e.g., "test" < "test-case" would be enforced by
32// "test/..." < "test-case/...". We also force "?" < "/", so "test?query" <
33// "test/stuff". Since the routine is merely lexicographical string comparison
34// with remapping of chracter ordering, so it is a valid strict-weak ordering.
35bool CanonicalURLStringCompare(const std::string& s1, const std::string& s2) {
36  const std::string::value_type* ch1 = s1.c_str();
37  const std::string::value_type* ch2 = s2.c_str();
38  while (*ch1 && *ch2 && *ch1 == *ch2) {
39    ++ch1;
40    ++ch2;
41  }
42  int pri_diff = GetURLCharPriority(*ch1) - GetURLCharPriority(*ch2);
43  // We want false to be returned if |pri_diff| > 0.
44  return (pri_diff != 0) ? pri_diff < 0 : *ch1 < *ch2;
45}
46
47bool HaveSameSchemeHostAndPort(const GURL&url1, const GURL& url2) {
48  return url1.scheme() == url2.scheme() && url1.host() == url2.host() &&
49      url1.port() == url2.port();
50}
51
52bool IsPathPrefix(const std::string& p1, const std::string& p2) {
53  if (p1.length() > p2.length())
54    return false;
55  std::pair<std::string::const_iterator, std::string::const_iterator>
56      first_diff = std::mismatch(p1.begin(), p1.end(), p2.begin());
57  // Necessary condition: |p1| is a string prefix of |p2|.
58  if (first_diff.first != p1.end())
59    return false;  // E.g.: (|p1| = "/test", |p2| = "/exam") => false.
60
61  // |p1| is string prefix.
62  if (first_diff.second == p2.end())  // Is exact match?
63    return true;  // E.g.: ("/test", "/test") => true.
64  // |p1| is strict string prefix, check full match of last path component.
65  if (!p1.empty() && *p1.rbegin() == '/')  // Ends in '/'?
66    return true;  // E.g.: ("/test/", "/test/stuff") => true.
67
68  // Finally, |p1| does not end in "/": check first extra character in |p2|.
69  // E.g.: ("/test", "/test/stuff") => true; ("/test", "/testing") => false.
70  return *(first_diff.second) == '/';
71}
72
73GURL ToggleHTTPAndHTTPS(const GURL& url) {
74  std::string new_scheme;
75  if (url.SchemeIs("http"))
76    new_scheme = "https";
77  else if (url.SchemeIs("https"))
78    new_scheme = "http";
79  else
80    return GURL::EmptyGURL();
81  url::Component comp;
82  comp.len = new_scheme.length();
83  GURL::Replacements replacement;
84  replacement.SetScheme(new_scheme.c_str(), comp);
85  return url.ReplaceComponents(replacement);
86}
87
88}  // namespace history
89