1c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Copyright 2007, Google Inc.
2c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// All rights reserved.
3c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//
4c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Redistribution and use in source and binary forms, with or without
5c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// modification, are permitted provided that the following conditions are
6c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// met:
7c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//
8c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//     * Redistributions of source code must retain the above copyright
9c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// notice, this list of conditions and the following disclaimer.
10c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//     * Redistributions in binary form must reproduce the above
11c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// copyright notice, this list of conditions and the following disclaimer
12c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// in the documentation and/or other materials provided with the
13c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// distribution.
14c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//     * Neither the name of Google Inc. nor the names of its
15c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// contributors may be used to endorse or promote products derived from
16c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// this software without specific prior written permission.
17c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//
18c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
30c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Canonicalizer functions for working with and resolving relative URLs.
31c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
32c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/logging.h"
33c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "googleurl/src/url_canon.h"
34c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "googleurl/src/url_canon_internal.h"
35c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "googleurl/src/url_file.h"
36c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "googleurl/src/url_parse_internal.h"
37c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
38c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottnamespace url_canon {
39c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
40c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottnamespace {
41c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
42c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Firefox does a case-sensitive compare (which is probably wrong--Mozilla bug
43c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// 379034), whereas IE is case-insensetive.
44c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//
45c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// We choose to be more permissive like IE. We don't need to worry about
46c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// unescaping or anything here: neither IE or Firefox allow this. We also
47c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// don't have to worry about invalid scheme characters since we are comparing
48c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// against the canonical scheme of the base.
49c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//
50c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// The base URL should always be canonical, therefore is ASCII.
51c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scotttemplate<typename CHAR>
52c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool AreSchemesEqual(const char* base,
53c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     const url_parse::Component& base_scheme,
54c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     const CHAR* cmp,
55c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     const url_parse::Component& cmp_scheme) {
56c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (base_scheme.len != cmp_scheme.len)
57c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
58c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  for (int i = 0; i < base_scheme.len; i++) {
59c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // We assume the base is already canonical, so we don't have to
60c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // canonicalize it.
61c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (CanonicalSchemeChar(cmp[cmp_scheme.begin + i]) !=
62c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott        base[base_scheme.begin + i])
63c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return false;
64c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
65c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return true;
66c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
67c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
68c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#ifdef WIN32
69c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
70c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Here, we also allow Windows paths to be represented as "/C:/" so we can be
71c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// consistent about URL paths beginning with slashes. This function is like
72c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// DoesBeginWindowsDrivePath except that it also requires a slash at the
73c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// beginning.
74c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scotttemplate<typename CHAR>
75c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool DoesBeginSlashWindowsDriveSpec(const CHAR* spec, int start_offset,
76c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                    int spec_len) {
77c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (start_offset >= spec_len)
78c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
79c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return url_parse::IsURLSlash(spec[start_offset]) &&
80c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      url_parse::DoesBeginWindowsDriveSpec(spec, start_offset + 1, spec_len);
81c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
82c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
83c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#endif  // WIN32
84c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
85c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// See IsRelativeURL in the header file for usage.
86c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scotttemplate<typename CHAR>
87c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool DoIsRelativeURL(const char* base,
88c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     const url_parse::Parsed& base_parsed,
89c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     const CHAR* url,
90c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     int url_len,
91c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     bool is_base_hierarchical,
92c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     bool* is_relative,
93c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     url_parse::Component* relative_component) {
94c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  *is_relative = false;  // So we can default later to not relative.
95c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
96c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Trim whitespace and construct a new range for the substring.
97c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  int begin = 0;
98c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  url_parse::TrimURL(url, &begin, &url_len);
99c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (begin >= url_len) {
100c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Empty URLs are relative, but do nothing.
101c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    *relative_component = url_parse::Component(begin, 0);
102c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    *is_relative = true;
103c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return true;
104c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
105c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
106c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#ifdef WIN32
107c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // We special case paths like "C:\foo" so they can link directly to the
108c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // file on Windows (IE compatability). The security domain stuff should
109c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // prevent a link like this from actually being followed if its on a
110c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // web page.
111c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  //
112c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // We treat "C:/foo" as an absolute URL. We can go ahead and treat "/c:/"
113c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // as relative, as this will just replace the path when the base scheme
114c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // is a file and the answer will still be correct.
115c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  //
116c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // We require strict backslashes when detecting UNC since two forward
117c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // shashes should be treated a a relative URL with a hostname.
118c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (url_parse::DoesBeginWindowsDriveSpec(url, begin, url_len) ||
119c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      url_parse::DoesBeginUNCPath(url, begin, url_len, true))
120c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return true;
121c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#endif  // WIN32
122c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
123c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // See if we've got a scheme, if not, we know this is a relative URL.
124c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // BUT: Just because we have a scheme, doesn't make it absolute.
125c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // "http:foo.html" is a relative URL with path "foo.html". If the scheme is
126c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // empty, we treat it as relative (":foo") like IE does.
127c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  url_parse::Component scheme;
128c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!url_parse::ExtractScheme(url, url_len, &scheme) || scheme.len == 0) {
129c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Don't allow relative URLs if the base scheme doesn't support it.
130c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (!is_base_hierarchical)
131c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return false;
132c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
133c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    *relative_component = url_parse::MakeRange(begin, url_len);
134c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    *is_relative = true;
135c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return true;
136c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
137c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
138c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // If the scheme isn't valid, then it's relative.
139c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  int scheme_end = scheme.end();
140c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  for (int i = scheme.begin; i < scheme_end; i++) {
141c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (!CanonicalSchemeChar(url[i])) {
142c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      *relative_component = url_parse::MakeRange(begin, url_len);
143c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      *is_relative = true;
144c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return true;
145c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
146c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
147c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
148c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // If the scheme is not the same, then we can't count it as relative.
149c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!AreSchemesEqual(base, base_parsed.scheme, url, scheme))
150c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return true;
151c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
152c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // When the scheme that they both share is not hierarchical, treat the
153c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // incoming scheme as absolute (this way with the base of "data:foo",
154c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // "data:bar" will be reported as absolute.
155c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!is_base_hierarchical)
156c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return true;
157c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
158c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // ExtractScheme guarantees that the colon immediately follows what it
159c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // considers to be the scheme. CountConsecutiveSlashes will handle the
160c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // case where the begin offset is the end of the input.
161c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  int colon_offset = scheme.end();
162c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  int num_slashes = url_parse::CountConsecutiveSlashes(url, colon_offset + 1,
163c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                                       url_len);
164c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
165c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (num_slashes == 0 || num_slashes == 1) {
166c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // No slashes means it's a relative path like "http:foo.html". One slash
167c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // is an absolute path. "http:/home/foo.html"
168c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    *is_relative = true;
169c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    *relative_component = url_parse::MakeRange(colon_offset + 1, url_len);
170c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return true;
171c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
172c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
173c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Two or more slashes after the scheme we treat as absolute.
174c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return true;
175c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
176c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
177c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Copies all characters in the range [begin, end) of |spec| to the output,
178c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// up until and including the last slash. There should be a slash in the
179c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// range, if not, nothing will be copied.
180c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//
181c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// The input is assumed to be canonical, so we search only for exact slashes
182c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// and not backslashes as well. We also know that it's ASCII.
183c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid CopyToLastSlash(const char* spec,
184c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     int begin,
185c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     int end,
186c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                     CanonOutput* output) {
187c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Find the last slash.
188c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  int last_slash = -1;
189c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  for (int i = end - 1; i >= begin; i--) {
190c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (spec[i] == '/') {
191c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      last_slash = i;
192c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      break;
193c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
194c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
195c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (last_slash < 0)
196c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;  // No slash.
197c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
198c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Copy.
199c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  for (int i = begin; i <= last_slash; i++)
200c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    output->push_back(spec[i]);
201c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
202c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
203c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Copies a single component from the source to the output. This is used
204c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// when resolving relative URLs and a given component is unchanged. Since the
205c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// source should already be canonical, we don't have to do anything special,
206c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// and the input is ASCII.
207c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid CopyOneComponent(const char* source,
208c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                      const url_parse::Component& source_component,
209c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                      CanonOutput* output,
210c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                      url_parse::Component* output_component) {
211c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (source_component.len < 0) {
212c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // This component is not present.
213c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    *output_component = url_parse::Component();
214c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;
215c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
216c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
217c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  output_component->begin = output->length();
218c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  int source_end = source_component.end();
219c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  for (int i = source_component.begin; i < source_end; i++)
220c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    output->push_back(source[i]);
221c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  output_component->len = output->length() - output_component->begin;
222c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
223c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
224c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#ifdef WIN32
225c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
226c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Called on Windows when the base URL is a file URL, this will copy the "C:"
227c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// to the output, if there is a drive letter and if that drive letter is not
228c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// being overridden by the relative URL. Otherwise, do nothing.
229c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//
230c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// It will return the index of the beginning of the next character in the
231c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// base to be processed: if there is a "C:", the slash after it, or if
232c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// there is no drive letter, the slash at the beginning of the path, or
233c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// the end of the base. This can be used as the starting offset for further
234c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// path processing.
235c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scotttemplate<typename CHAR>
236c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottint CopyBaseDriveSpecIfNecessary(const char* base_url,
237c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                 int base_path_begin,
238c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                 int base_path_end,
239c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                 const CHAR* relative_url,
240c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                 int path_start,
241c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                 int relative_url_len,
242c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                 CanonOutput* output) {
243c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (base_path_begin >= base_path_end)
244c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return base_path_begin;  // No path.
245c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
246c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // If the relative begins with a drive spec, don't do anything. The existing
247c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // drive spec in the base will be replaced.
248c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (url_parse::DoesBeginWindowsDriveSpec(relative_url,
249c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                           path_start, relative_url_len)) {
250c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return base_path_begin;  // Relative URL path is "C:/foo"
251c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
252c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
253c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // The path should begin with a slash (as all canonical paths do). We check
254c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // if it is followed by a drive letter and copy it.
255c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (DoesBeginSlashWindowsDriveSpec(base_url,
256c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                     base_path_begin,
257c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                     base_path_end)) {
258c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Copy the two-character drive spec to the output. It will now look like
259c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // "file:///C:" so the rest of it can be treated like a standard path.
260c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    output->push_back('/');
261c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    output->push_back(base_url[base_path_begin + 1]);
262c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    output->push_back(base_url[base_path_begin + 2]);
263c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return base_path_begin + 3;
264c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
265c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
266c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return base_path_begin;
267c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
268c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
269c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#endif  // WIN32
270c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
271c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// A subroutine of DoResolveRelativeURL, this resolves the URL knowning that
272c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// the input is a relative path or less (qyuery or ref).
273c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scotttemplate<typename CHAR>
274c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool DoResolveRelativePath(const char* base_url,
275c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           const url_parse::Parsed& base_parsed,
276c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           bool base_is_file,
277c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           const CHAR* relative_url,
278c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           const url_parse::Component& relative_component,
279c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           CharsetConverter* query_converter,
280c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           CanonOutput* output,
281c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           url_parse::Parsed* out_parsed) {
282c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  bool success = true;
283c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
284c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // We know the authority section didn't change, copy it to the output. We
285c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // also know we have a path so can copy up to there.
286c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  url_parse::Component path, query, ref;
287c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  url_parse::ParsePathInternal(relative_url,
288c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                               relative_component,
289c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                               &path,
290c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                               &query,
291c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                               &ref);
292c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Canonical URLs always have a path, so we can use that offset.
293c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  output->Append(base_url, base_parsed.path.begin);
294c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
295c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (path.len > 0) {
296c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // The path is replaced or modified.
297c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    int true_path_begin = output->length();
298c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
299c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // For file: URLs on Windows, we don't want to treat the drive letter and
300c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // colon as part of the path for relative file resolution when the
301c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // incoming URL does not provide a drive spec. We save the true path
302c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // beginning so we can fix it up after we are done.
303c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    int base_path_begin = base_parsed.path.begin;
304c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#ifdef WIN32
305c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (base_is_file) {
306c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      base_path_begin = CopyBaseDriveSpecIfNecessary(
307c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott          base_url, base_parsed.path.begin, base_parsed.path.end(),
308c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott          relative_url, relative_component.begin, relative_component.end(),
309c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott          output);
310c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // Now the output looks like either "file://" or "file:///C:"
311c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // and we can start appending the rest of the path. |base_path_begin|
312c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // points to the character in the base that comes next.
313c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
314c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#endif  // WIN32
315c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
316c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (url_parse::IsURLSlash(relative_url[path.begin])) {
317c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // Easy case: the path is an absolute path on the server, so we can
318c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // just replace everything from the path on with the new versions.
319c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // Since the input should be canonical hierarchical URL, we should
320c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // always have a path.
321c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      success &= CanonicalizePath(relative_url, path,
322c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                  output, &out_parsed->path);
323c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    } else {
324c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // Relative path, replace the query, and reference. We take the
325c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // original path with the file part stripped, and append the new path.
326c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // The canonicalizer will take care of resolving ".." and "."
327c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      int path_begin = output->length();
328c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      CopyToLastSlash(base_url, base_path_begin, base_parsed.path.end(),
329c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                      output);
330c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      success &= CanonicalizePartialPath(relative_url, path, path_begin,
331c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                         output);
332c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      out_parsed->path = url_parse::MakeRange(path_begin, output->length());
333c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
334c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // Copy the rest of the stuff after the path from the relative path.
335c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
336c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
337c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Finish with the query and reference part (these can't fail).
338c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    CanonicalizeQuery(relative_url, query, query_converter,
339c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                      output, &out_parsed->query);
340c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    CanonicalizeRef(relative_url, ref, output, &out_parsed->ref);
341c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
342c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Fix the path beginning to add back the "C:" we may have written above.
343c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    out_parsed->path = url_parse::MakeRange(true_path_begin,
344c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                            out_parsed->path.end());
345c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return success;
346c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
347c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
348c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // If we get here, the path is unchanged: copy to output.
349c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  CopyOneComponent(base_url, base_parsed.path, output, &out_parsed->path);
350c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
351c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (query.is_valid()) {
352c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Just the query specified, replace the query and reference (ignore
353c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // failures for refs)
354c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    CanonicalizeQuery(relative_url, query, query_converter,
355c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                      output, &out_parsed->query);
356c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    CanonicalizeRef(relative_url, ref, output, &out_parsed->ref);
357c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return success;
358c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
359c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
360c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // If we get here, the query is unchanged: copy to output. Note that the
361c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // range of the query parameter doesn't include the question mark, so we
362c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // have to add it manually if there is a component.
363c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (base_parsed.query.is_valid())
364c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    output->push_back('?');
365c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  CopyOneComponent(base_url, base_parsed.query, output, &out_parsed->query);
366c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
367c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (ref.is_valid()) {
368c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Just the reference specified: replace it (ignoring failures).
369c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    CanonicalizeRef(relative_url, ref, output, &out_parsed->ref);
370c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return success;
371c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
372c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
373c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // We should always have something to do in this function, the caller checks
374c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // that some component is being replaced.
375c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DCHECK(false) << "Not reached";
376c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return success;
377c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
378c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
379c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Resolves a relative URL that contains a host. Typically, these will
380c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// be of the form "//www.google.com/foo/bar?baz#ref" and the only thing which
381c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// should be kept from the original URL is the scheme.
382c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scotttemplate<typename CHAR>
383c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool DoResolveRelativeHost(const char* base_url,
384c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           const url_parse::Parsed& base_parsed,
385c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           const CHAR* relative_url,
386c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           const url_parse::Component& relative_component,
387c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           CharsetConverter* query_converter,
388c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           CanonOutput* output,
389c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           url_parse::Parsed* out_parsed) {
390c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Parse the relative URL, just like we would for anything following a
391c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // scheme.
392c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  url_parse::Parsed relative_parsed;  // Everything but the scheme is valid.
393c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  url_parse::ParseAfterScheme(&relative_url[relative_component.begin],
394c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                              relative_component.len, relative_component.begin,
395c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                              &relative_parsed);
396c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
397c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Now we can just use the replacement function to replace all the necessary
398c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // parts of the old URL with the new one.
399c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  Replacements<CHAR> replacements;
400c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  replacements.SetUsername(relative_url, relative_parsed.username);
401c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  replacements.SetPassword(relative_url, relative_parsed.password);
402c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  replacements.SetHost(relative_url, relative_parsed.host);
403c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  replacements.SetPort(relative_url, relative_parsed.port);
404c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  replacements.SetPath(relative_url, relative_parsed.path);
405c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  replacements.SetQuery(relative_url, relative_parsed.query);
406c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  replacements.SetRef(relative_url, relative_parsed.ref);
407c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
408c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return ReplaceStandardURL(base_url, base_parsed, replacements,
409c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                            query_converter, output, out_parsed);
410c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
411c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
412c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Resolves a relative URL that happens to be an absolute file path.  Examples
413c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// include: "//hostname/path", "/c:/foo", and "//hostname/c:/foo".
414c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scotttemplate<typename CHAR>
415c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool DoResolveAbsoluteFile(const CHAR* relative_url,
416c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           const url_parse::Component& relative_component,
417c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           CharsetConverter* query_converter,
418c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           CanonOutput* output,
419c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                           url_parse::Parsed* out_parsed) {
420c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Parse the file URL. The file URl parsing function uses the same logic
421c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // as we do for determining if the file is absolute, in which case it will
422c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // not bother to look for a scheme.
423c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  url_parse::Parsed relative_parsed;
424c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  url_parse::ParseFileURL(&relative_url[relative_component.begin],
425c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                          relative_component.len, &relative_parsed);
426c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
427c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return CanonicalizeFileURL(&relative_url[relative_component.begin],
428c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                             relative_component.len, relative_parsed,
429c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                             query_converter, output, out_parsed);
430c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
431c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
432c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// TODO(brettw) treat two slashes as root like Mozilla for FTP?
433c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scotttemplate<typename CHAR>
434c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool DoResolveRelativeURL(const char* base_url,
435c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                          const url_parse::Parsed& base_parsed,
436c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                          bool base_is_file,
437c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                          const CHAR* relative_url,
438c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                          const url_parse::Component& relative_component,
439c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                          CharsetConverter* query_converter,
440c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                          CanonOutput* output,
441c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                          url_parse::Parsed* out_parsed) {
442c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Starting point for our output parsed. We'll fix what we change.
443c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  *out_parsed = base_parsed;
444c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
445c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Sanity check: the input should have a host or we'll break badly below.
446c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // We can only resolve relative URLs with base URLs that have hosts and
447c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // paths (even the default path of "/" is OK).
448c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  //
449c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // We allow hosts with no length so we can handle file URLs, for example.
450c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (base_parsed.path.len <= 0) {
451c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // On error, return the input (resolving a relative URL on a non-relative
452c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // base = the base).
453c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    int base_len = base_parsed.Length();
454c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    for (int i = 0; i < base_len; i++)
455c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      output->push_back(base_url[i]);
456c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return false;
457c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
458c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
459c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (relative_component.len <= 0) {
460c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Empty relative URL, leave unchanged, only removing the ref component.
461c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    int base_len = base_parsed.Length();
462c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    base_len -= base_parsed.ref.len + 1;
463c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    out_parsed->ref.reset();
464c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    output->Append(base_url, base_len);
465c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return true;
466c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
467c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
468c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  int num_slashes = url_parse::CountConsecutiveSlashes(
469c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      relative_url, relative_component.begin, relative_component.end());
470c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
471c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#ifdef WIN32
472c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // On Windows, two slashes for a file path (regardless of which direction
473c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // they are) means that it's UNC. Two backslashes on any base scheme mean
474c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // that it's an absolute UNC path (we use the base_is_file flag to control
475c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // how strict the UNC finder is).
476c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  //
477c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // We also allow Windows absolute drive specs on any scheme (for example
478c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // "c:\foo") like IE does. There must be no preceeding slashes in this
479c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // case (we reject anything like "/c:/foo") because that should be treated
480c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // as a path. For file URLs, we allow any number of slashes since that would
481c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // be setting the path.
482c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  //
483c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // This assumes the absolute path resolver handles absolute URLs like this
484c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // properly. url_util::DoCanonicalize does this.
485c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  int after_slashes = relative_component.begin + num_slashes;
486c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (url_parse::DoesBeginUNCPath(relative_url, relative_component.begin,
487c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                  relative_component.end(), !base_is_file) ||
488c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      ((num_slashes == 0 || base_is_file) &&
489c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott       url_parse::DoesBeginWindowsDriveSpec(relative_url, after_slashes,
490c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                            relative_component.end()))) {
491c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return DoResolveAbsoluteFile(relative_url, relative_component,
492c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                 query_converter, output, out_parsed);
493c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
494c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#else
495c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Other platforms need explicit handling for file: URLs with multiple
496c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // slashes because the generic scheme parsing always extracts a host, but a
497c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // file: URL only has a host if it has exactly 2 slashes. This also
498c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // handles the special case where the URL is only slashes, since that
499c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // doesn't have a host part either.
500c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (base_is_file &&
501c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      (num_slashes > 2 || num_slashes == relative_component.len)) {
502c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return DoResolveAbsoluteFile(relative_url, relative_component,
503c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                 query_converter, output, out_parsed);
504c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
505c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#endif
506c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
507c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Any other double-slashes mean that this is relative to the scheme.
508c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (num_slashes >= 2) {
509c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return DoResolveRelativeHost(base_url, base_parsed,
510c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                 relative_url, relative_component,
511c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                 query_converter, output, out_parsed);
512c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
513c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
514c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // When we get here, we know that the relative URL is on the same host.
515c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return DoResolveRelativePath(base_url, base_parsed, base_is_file,
516c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                               relative_url, relative_component,
517c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                               query_converter, output, out_parsed);
518c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
519c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
520c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}  // namespace
521c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
522c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool IsRelativeURL(const char* base,
523c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                   const url_parse::Parsed& base_parsed,
524c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                   const char* fragment,
525c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                   int fragment_len,
526c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                   bool is_base_hierarchical,
527c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                   bool* is_relative,
528c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                   url_parse::Component* relative_component) {
529c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return DoIsRelativeURL<char>(
530c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      base, base_parsed, fragment, fragment_len, is_base_hierarchical,
531c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      is_relative, relative_component);
532c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
533c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
534c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool IsRelativeURL(const char* base,
535c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                   const url_parse::Parsed& base_parsed,
536c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                   const char16* fragment,
537c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                   int fragment_len,
538c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                   bool is_base_hierarchical,
539c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                   bool* is_relative,
540c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                   url_parse::Component* relative_component) {
541c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return DoIsRelativeURL<char16>(
542c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      base, base_parsed, fragment, fragment_len, is_base_hierarchical,
543c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      is_relative, relative_component);
544c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
545c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
546c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool ResolveRelativeURL(const char* base_url,
547c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                        const url_parse::Parsed& base_parsed,
548c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                        bool base_is_file,
549c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                        const char* relative_url,
550c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                        const url_parse::Component& relative_component,
551c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                        CharsetConverter* query_converter,
552c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                        CanonOutput* output,
553c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                        url_parse::Parsed* out_parsed) {
554c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return DoResolveRelativeURL<char>(
555c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      base_url, base_parsed, base_is_file, relative_url,
556c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      relative_component, query_converter, output, out_parsed);
557c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
558c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
559c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool ResolveRelativeURL(const char* base_url,
560c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                        const url_parse::Parsed& base_parsed,
561c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                        bool base_is_file,
562c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                        const char16* relative_url,
563c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                        const url_parse::Component& relative_component,
564c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                        CharsetConverter* query_converter,
565c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                        CanonOutput* output,
566c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                        url_parse::Parsed* out_parsed) {
567c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return DoResolveRelativeURL<char16>(
568c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      base_url, base_parsed, base_is_file, relative_url,
569c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      relative_component, query_converter, output, out_parsed);
570c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
571c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
572c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}  // namespace url_canon
573