1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// found in the LICENSE file.
4c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/logging.h"
6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "url/url_file.h"
7c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "url/url_parse.h"
8c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "url/url_parse_internal.h"
9c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
10c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Interesting IE file:isms...
11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//
12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//  INPUT                      OUTPUT
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//  =========================  ==============================
14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//  file:/foo/bar              file:///foo/bar
15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//      The result here seems totally invalid!?!? This isn't UNC.
16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//
17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//  file:/
18c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//  file:// or any other number of slashes
19c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//      IE6 doesn't do anything at all if you click on this link. No error:
20c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//      nothing. IE6's history system seems to always color this link, so I'm
21c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//      guessing that it maps internally to the empty URL.
22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//
23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//  C:\                        file:///C:/
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//      When on a file: URL source page, this link will work. When over HTTP,
25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//      the file: URL will appear in the status bar but the link will not work
26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//      (security restriction for all file URLs).
27c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//  file:foo/                  file:foo/     (invalid?!?!?)
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//  file:/foo/                 file:///foo/  (invalid?!?!?)
30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//  file://foo/                file://foo/   (UNC to server "foo")
31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//  file:///foo/               file:///foo/  (invalid, seems to be a file)
32c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//  file:////foo/              file://foo/   (UNC to server "foo")
33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//      Any more than four slashes is also treated as UNC.
34c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//
35c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//  file:C:/                   file://C:/
36c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//  file:/C:/                  file://C:/
37c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//      The number of slashes after "file:" don't matter if the thing following
38c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//      it looks like an absolute drive path. Also, slashes and backslashes are
39c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)//      equally valid here.
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
410529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochnamespace url {
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)namespace {
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// A subcomponent of DoInitFileURL, the input of this function should be a UNC
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// path name, with the index of the first character after the slashes following
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// the scheme given in |after_slashes|. This will initialize the host, path,
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// query, and ref, and leave the other output components untouched
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// (DoInitFileURL handles these for us).
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)template<typename CHAR>
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void DoParseUNC(const CHAR* spec,
52c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                int after_slashes,
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                int spec_len,
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)               Parsed* parsed) {
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int next_slash = FindNextSlash(spec, after_slashes, spec_len);
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (next_slash == spec_len) {
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // No additional slash found, as in "file://foo", treat the text as the
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // host with no path (this will end up being UNC to server "foo").
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    int host_len = spec_len - after_slashes;
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (host_len)
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      parsed->host = Component(after_slashes, host_len);
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    else
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      parsed->host.reset();
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    parsed->path.reset();
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#ifdef WIN32
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // See if we have something that looks like a path following the first
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // component. As in "file://localhost/c:/", we get "c:/" out. We want to
71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // treat this as a having no host but the path given. Works on Windows only.
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (DoesBeginWindowsDriveSpec(spec, next_slash + 1, spec_len)) {
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    parsed->host.reset();
74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    ParsePathInternal(spec, MakeRange(next_slash, spec_len),
75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                      &parsed->path, &parsed->query, &parsed->ref);
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#endif
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Otherwise, everything up until that first slash we found is the host name,
81c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // which will end up being the UNC host. For example "file://foo/bar.txt"
82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // will get a server name of "foo" and a path of "/bar". Later, on Windows,
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // this should be treated as the filename "\\foo\bar.txt" in proper UNC
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // notation.
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int host_len = next_slash - after_slashes;
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (host_len)
87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    parsed->host = MakeRange(after_slashes, next_slash);
88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  else
89c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    parsed->host.reset();
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (next_slash < spec_len) {
91c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    ParsePathInternal(spec, MakeRange(next_slash, spec_len),
92c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                      &parsed->path, &parsed->query, &parsed->ref);
93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  } else {
94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    parsed->path.reset();
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// A subcomponent of DoParseFileURL, the input should be a local file, with the
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// beginning of the path indicated by the index in |path_begin|. This will
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// initialize the host, path, query, and ref, and leave the other output
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// components untouched (DoInitFileURL handles these for us).
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)template<typename CHAR>
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void DoParseLocalFile(const CHAR* spec,
104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                      int path_begin,
105c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                      int spec_len,
106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                      Parsed* parsed) {
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  parsed->host.reset();
108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ParsePathInternal(spec, MakeRange(path_begin, spec_len),
109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    &parsed->path, &parsed->query, &parsed->ref);
110c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Backend for the external functions that operates on either char type.
113d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// Handles cases where there is a scheme, but also when handed the first
114d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// character following the "file:" at the beginning of the spec. If so,
115d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)// this is usually a slash, but needn't be; we allow paths like "file:c:\foo".
116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)template<typename CHAR>
117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void DoParseFileURL(const CHAR* spec, int spec_len, Parsed* parsed) {
118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(spec_len >= 0);
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Get the parts we never use for file URLs out of the way.
121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  parsed->username.reset();
122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  parsed->password.reset();
123c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  parsed->port.reset();
124c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
125c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Many of the code paths don't set these, so it's convenient to just clear
126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // them. We'll write them in those cases we need them.
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  parsed->query.reset();
128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  parsed->ref.reset();
129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
130c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Strip leading & trailing spaces and control characters.
131c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int begin = 0;
132c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  TrimURL(spec, &begin, &spec_len);
133c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
134d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Find the scheme, if any.
135d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  int num_slashes = CountConsecutiveSlashes(spec, begin, spec_len);
136c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int after_scheme;
137c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int after_slashes;
138c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#ifdef WIN32
139c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // See how many slashes there are. We want to handle cases like UNC but also
140c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // "/c:/foo". This is when there is no scheme, so we can allow pages to do
141c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // links like "c:/foo/bar" or "//foo/bar". This is also called by the
142c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // relative URL resolver when it determines there is an absolute URL, which
143c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // may give us input like "/c:/foo".
144c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  after_slashes = begin + num_slashes;
145c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (DoesBeginWindowsDriveSpec(spec, after_slashes, spec_len)) {
146c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // Windows path, don't try to extract the scheme (for example, "c:\foo").
147c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    parsed->scheme.reset();
148c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    after_scheme = after_slashes;
149c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  } else if (DoesBeginUNCPath(spec, begin, spec_len, false)) {
150c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // Windows UNC path: don't try to extract the scheme, but keep the slashes.
151c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    parsed->scheme.reset();
152c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    after_scheme = begin;
153c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  } else
154c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#endif
155c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  {
156d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // ExtractScheme doesn't understand the possibility of filenames with
157d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // colons in them, in which case it returns the entire spec up to the
158d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // colon as the scheme. So handle /foo.c:5 as a file but foo.c:5 as
159d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // the foo.c: scheme.
160d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if (!num_slashes &&
161d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        ExtractScheme(&spec[begin], spec_len - begin, &parsed->scheme)) {
162c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // Offset the results since we gave ExtractScheme a substring.
163c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      parsed->scheme.begin += begin;
164c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      after_scheme = parsed->scheme.end() + 1;
165c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    } else {
166c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // No scheme found, remember that.
167c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      parsed->scheme.reset();
168c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      after_scheme = begin;
169c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
170c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
171c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
172c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Handle empty specs ones that contain only whitespace or control chars,
173c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // or that are just the scheme (for example "file:").
174c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (after_scheme == spec_len) {
175c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    parsed->host.reset();
176c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    parsed->path.reset();
177c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
178c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
179c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
180c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  num_slashes = CountConsecutiveSlashes(spec, after_scheme, spec_len);
181c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  after_slashes = after_scheme + num_slashes;
182c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#ifdef WIN32
183c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Check whether the input is a drive again. We checked above for windows
184c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // drive specs, but that's only at the very beginning to see if we have a
185c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // scheme at all. This test will be duplicated in that case, but will
186c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // additionally handle all cases with a real scheme such as "file:///C:/".
187c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!DoesBeginWindowsDriveSpec(spec, after_slashes, spec_len) &&
188c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      num_slashes != 3) {
189c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // Anything not beginning with a drive spec ("c:\") on Windows is treated
190c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // as UNC, with the exception of three slashes which always means a file.
191c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // Even IE7 treats file:///foo/bar as "/foo/bar", which then fails.
192c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    DoParseUNC(spec, after_slashes, spec_len, parsed);
193c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
194c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
195c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#else
196c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // file: URL with exactly 2 slashes is considered to have a host component.
197c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (num_slashes == 2) {
198c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    DoParseUNC(spec, after_slashes, spec_len, parsed);
199c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return;
200c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
201c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#endif  // WIN32
202c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
203c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Easy and common case, the full path immediately follows the scheme
204c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // (modulo slashes), as in "file://c:/foo". Just treat everything from
205c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // there to the end as the path. Empty hosts have 0 length instead of -1.
206c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // We include the last slash as part of the path if there is one.
207c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DoParseLocalFile(spec,
208c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      num_slashes > 0 ? after_scheme + num_slashes - 1 : after_scheme,
209c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      spec_len, parsed);
210c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
211c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
212c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}  // namespace
213c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
214c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void ParseFileURL(const char* url, int url_len, Parsed* parsed) {
215c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DoParseFileURL(url, url_len, parsed);
216c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
217c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2187d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)void ParseFileURL(const base::char16* url, int url_len, Parsed* parsed) {
219c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DoParseFileURL(url, url_len, parsed);
220c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
221c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2220529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch}  // namespace url
223