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