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