Changeset 267837 in webkit


Ignore:
Timestamp:
Oct 1, 2020, 10:05:41 AM (5 years ago)
Author:
achristensen@apple.com
Message:

Non-special URLs are not idempotent
https://bugs.webkit.org/show_bug.cgi?id=215762

Reviewed by Tim Horton.

LayoutTests/imported/w3c:

  • web-platform-tests/url/a-element-expected.txt:
  • web-platform-tests/url/a-element-xhtml-expected.txt:
  • web-platform-tests/url/url-constructor-expected.txt:
  • web-platform-tests/url/url-setters-expected.txt:

Source/WTF:

https://github.com/whatwg/url/pull/505 added an interesting edge case to the URL serialization:
"If url’s host is null, url’s path’s size is greater than 1, and url’s path[0] is the empty string, then append U+002F (/) followed by U+002E (.) to output."
The problem was that URLs like "a:/a/..//a" would be parsed into "a://a" with a pathname of "a" and an empty host. If "a://a" was then reparsed, it would again have an href of "a://a"
but its host would be "a" and it would have an empty path. There is consensus that URL parsing should be idempotent, so we need to do something different here.
According to https://github.com/whatwg/url/issues/415#issuecomment-419197290 this follows what Edge did (and then subsequently abandoned when they switched to Chromium)
to make URL parsing idempotent by adding "/." before the path in the edge case of a URL with a non-special scheme (not http, https, wss, etc.) and a null host and a non-empty path that
has an empty first segment. All the members of the URL remain unchanged except the full serialization (href). This is not important in practice, but important in theory.

Our URL parser tries very hard to use the exact same WTF::String object given as input if it can. However, this step is better implemented as a post-processing step that will almost never happen
because otherwise we would have to parse the entire path twice to find out if we need to add "./" or if the "./" that may have already been there needs to stay. This is illustrated with the test URL
"t:/.//p/../../../..//x" which does need the "./".

In the common case, this adds one well-predicted branch to URL parsing, so I expect performance to be unaffected. Since this is such a rare edge case of URLs, I expect no compatibility problems.

  • wtf/URL.cpp:

(WTF::URL::pathStart const):

  • wtf/URL.h:

(WTF::URL::pathStart const): Deleted.

  • wtf/URLParser.cpp:

(WTF::URLParser::copyURLPartsUntil):
(WTF::URLParser::URLParser):
(WTF::URLParser::needsNonSpecialDotSlash const):
(WTF::URLParser::addNonSpecialDotSlash):

  • wtf/URLParser.h:

Tools:

  • TestWebKitAPI/Tests/WTF/URLParser.cpp:

(TestWebKitAPI::TEST_F):

Location:
trunk
Files:
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/imported/w3c/ChangeLog

    r267833 r267837  
     12020-10-01  Alex Christensen  <achristensen@webkit.org>
     2
     3        Non-special URLs are not idempotent
     4        https://bugs.webkit.org/show_bug.cgi?id=215762
     5
     6        Reviewed by Tim Horton.
     7
     8        * web-platform-tests/url/a-element-expected.txt:
     9        * web-platform-tests/url/a-element-xhtml-expected.txt:
     10        * web-platform-tests/url/url-constructor-expected.txt:
     11        * web-platform-tests/url/url-setters-expected.txt:
     12
    1132020-10-01  Youenn Fablet  <youenn@apple.com>
    214
  • trunk/LayoutTests/imported/w3c/web-platform-tests/url/a-element-expected.txt

    r267647 r267837  
    512512PASS Parsing: <urn:ietf:rfc:2648> against <about:blank>
    513513PASS Parsing: <tag:joe@example.org,2001:foo/bar> against <about:blank>
    514 FAIL Parsing: <non-spec:/.//> against <about:blank> assert_equals: href expected "non-spec:/.//" but got "non-spec://"
    515 FAIL Parsing: <non-spec:/..//> against <about:blank> assert_equals: href expected "non-spec:/.//" but got "non-spec://"
    516 FAIL Parsing: <non-spec:/a/..//> against <about:blank> assert_equals: href expected "non-spec:/.//" but got "non-spec://"
    517 FAIL Parsing: <non-spec:/.//path> against <about:blank> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    518 FAIL Parsing: <non-spec:/..//path> against <about:blank> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    519 FAIL Parsing: <non-spec:/a/..//path> against <about:blank> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    520 FAIL Parsing: </.//path> against <non-spec:/p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    521 FAIL Parsing: </..//path> against <non-spec:/p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    522 FAIL Parsing: <..//path> against <non-spec:/p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    523 FAIL Parsing: <a/..//path> against <non-spec:/p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    524 FAIL Parsing: <> against <non-spec:/..//p> assert_equals: href expected "non-spec:/.//p" but got "non-spec://p"
    525 FAIL Parsing: <path> against <non-spec:/..//p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
     514PASS Parsing: <non-spec:/.//> against <about:blank>
     515PASS Parsing: <non-spec:/..//> against <about:blank>
     516PASS Parsing: <non-spec:/a/..//> against <about:blank>
     517PASS Parsing: <non-spec:/.//path> against <about:blank>
     518PASS Parsing: <non-spec:/..//path> against <about:blank>
     519PASS Parsing: <non-spec:/a/..//path> against <about:blank>
     520PASS Parsing: </.//path> against <non-spec:/p>
     521PASS Parsing: </..//path> against <non-spec:/p>
     522PASS Parsing: <..//path> against <non-spec:/p>
     523PASS Parsing: <a/..//path> against <non-spec:/p>
     524PASS Parsing: <> against <non-spec:/..//p>
     525PASS Parsing: <path> against <non-spec:/..//p>
    526526PASS Parsing: <../path> against <non-spec:/.//p>
    527527PASS Parsing: <non-special://%E2%80%A0/> against <about:blank>
  • trunk/LayoutTests/imported/w3c/web-platform-tests/url/a-element-xhtml-expected.txt

    r267647 r267837  
    512512PASS Parsing: <urn:ietf:rfc:2648> against <about:blank>
    513513PASS Parsing: <tag:joe@example.org,2001:foo/bar> against <about:blank>
    514 FAIL Parsing: <non-spec:/.//> against <about:blank> assert_equals: href expected "non-spec:/.//" but got "non-spec://"
    515 FAIL Parsing: <non-spec:/..//> against <about:blank> assert_equals: href expected "non-spec:/.//" but got "non-spec://"
    516 FAIL Parsing: <non-spec:/a/..//> against <about:blank> assert_equals: href expected "non-spec:/.//" but got "non-spec://"
    517 FAIL Parsing: <non-spec:/.//path> against <about:blank> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    518 FAIL Parsing: <non-spec:/..//path> against <about:blank> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    519 FAIL Parsing: <non-spec:/a/..//path> against <about:blank> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    520 FAIL Parsing: </.//path> against <non-spec:/p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    521 FAIL Parsing: </..//path> against <non-spec:/p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    522 FAIL Parsing: <..//path> against <non-spec:/p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    523 FAIL Parsing: <a/..//path> against <non-spec:/p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    524 FAIL Parsing: <> against <non-spec:/..//p> assert_equals: href expected "non-spec:/.//p" but got "non-spec://p"
    525 FAIL Parsing: <path> against <non-spec:/..//p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
     514PASS Parsing: <non-spec:/.//> against <about:blank>
     515PASS Parsing: <non-spec:/..//> against <about:blank>
     516PASS Parsing: <non-spec:/a/..//> against <about:blank>
     517PASS Parsing: <non-spec:/.//path> against <about:blank>
     518PASS Parsing: <non-spec:/..//path> against <about:blank>
     519PASS Parsing: <non-spec:/a/..//path> against <about:blank>
     520PASS Parsing: </.//path> against <non-spec:/p>
     521PASS Parsing: </..//path> against <non-spec:/p>
     522PASS Parsing: <..//path> against <non-spec:/p>
     523PASS Parsing: <a/..//path> against <non-spec:/p>
     524PASS Parsing: <> against <non-spec:/..//p>
     525PASS Parsing: <path> against <non-spec:/..//p>
    526526PASS Parsing: <../path> against <non-spec:/.//p>
    527527PASS Parsing: <non-special://%E2%80%A0/> against <about:blank>
  • trunk/LayoutTests/imported/w3c/web-platform-tests/url/url-constructor-expected.txt

    r267647 r267837  
    515515PASS Parsing: <urn:ietf:rfc:2648> against <about:blank>
    516516PASS Parsing: <tag:joe@example.org,2001:foo/bar> against <about:blank>
    517 FAIL Parsing: <non-spec:/.//> against <about:blank> assert_equals: href expected "non-spec:/.//" but got "non-spec://"
    518 FAIL Parsing: <non-spec:/..//> against <about:blank> assert_equals: href expected "non-spec:/.//" but got "non-spec://"
    519 FAIL Parsing: <non-spec:/a/..//> against <about:blank> assert_equals: href expected "non-spec:/.//" but got "non-spec://"
    520 FAIL Parsing: <non-spec:/.//path> against <about:blank> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    521 FAIL Parsing: <non-spec:/..//path> against <about:blank> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    522 FAIL Parsing: <non-spec:/a/..//path> against <about:blank> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    523 FAIL Parsing: </.//path> against <non-spec:/p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    524 FAIL Parsing: </..//path> against <non-spec:/p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    525 FAIL Parsing: <..//path> against <non-spec:/p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    526 FAIL Parsing: <a/..//path> against <non-spec:/p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
    527 FAIL Parsing: <> against <non-spec:/..//p> assert_equals: href expected "non-spec:/.//p" but got "non-spec://p"
    528 FAIL Parsing: <path> against <non-spec:/..//p> assert_equals: href expected "non-spec:/.//path" but got "non-spec://path"
     517PASS Parsing: <non-spec:/.//> against <about:blank>
     518PASS Parsing: <non-spec:/..//> against <about:blank>
     519PASS Parsing: <non-spec:/a/..//> against <about:blank>
     520PASS Parsing: <non-spec:/.//path> against <about:blank>
     521PASS Parsing: <non-spec:/..//path> against <about:blank>
     522PASS Parsing: <non-spec:/a/..//path> against <about:blank>
     523PASS Parsing: </.//path> against <non-spec:/p>
     524PASS Parsing: </..//path> against <non-spec:/p>
     525PASS Parsing: <..//path> against <non-spec:/p>
     526PASS Parsing: <a/..//path> against <non-spec:/p>
     527PASS Parsing: <> against <non-spec:/..//p>
     528PASS Parsing: <path> against <non-spec:/..//p>
    529529PASS Parsing: <../path> against <non-spec:/.//p>
    530530PASS Parsing: <non-special://%E2%80%A0/> against <about:blank>
  • trunk/LayoutTests/imported/w3c/web-platform-tests/url/url-setters-expected.txt

    r267647 r267837  
    430430PASS <a>: Setting <non-spec:/.//p>.hostname = 'h' Drop /. from path
    431431PASS <area>: Setting <non-spec:/.//p>.hostname = 'h' Drop /. from path
    432 FAIL URL: Setting <non-spec:/.//p>.hostname = '' assert_equals: expected "non-spec:////p" but got "non-spec://p"
    433 FAIL <a>: Setting <non-spec:/.//p>.hostname = '' assert_equals: expected "non-spec:////p" but got "non-spec://p"
    434 FAIL <area>: Setting <non-spec:/.//p>.hostname = '' assert_equals: expected "non-spec:////p" but got "non-spec://p"
     432FAIL URL: Setting <non-spec:/.//p>.hostname = '' assert_equals: expected "non-spec:////p" but got "non-spec:/.//p"
     433FAIL <a>: Setting <non-spec:/.//p>.hostname = '' assert_equals: expected "non-spec:////p" but got "non-spec:/.//p"
     434FAIL <area>: Setting <non-spec:/.//p>.hostname = '' assert_equals: expected "non-spec:////p" but got "non-spec:/.//p"
    435435PASS URL: Setting <http://example.net>.port = '8080'
    436436PASS <a>: Setting <http://example.net>.port = '8080'
     
    544544FAIL <a>: Setting <file:///unicorn>.pathname = '//monkey/..//' File URLs and (back)slashes assert_equals: expected "file:///" but got "file://///"
    545545FAIL <area>: Setting <file:///unicorn>.pathname = '//monkey/..//' File URLs and (back)slashes assert_equals: expected "file:///" but got "file://///"
    546 FAIL URL: Setting <non-spec:/>.pathname = '/.//p' Serialize /. in path assert_equals: expected "non-spec:/.//p" but got "non-spec://p"
    547 FAIL <a>: Setting <non-spec:/>.pathname = '/.//p' Serialize /. in path assert_equals: expected "non-spec:/.//p" but got "non-spec://p"
    548 FAIL <area>: Setting <non-spec:/>.pathname = '/.//p' Serialize /. in path assert_equals: expected "non-spec:/.//p" but got "non-spec://p"
    549 FAIL URL: Setting <non-spec:/>.pathname = '/..//p' assert_equals: expected "non-spec:/.//p" but got "non-spec://p"
    550 FAIL <a>: Setting <non-spec:/>.pathname = '/..//p' assert_equals: expected "non-spec:/.//p" but got "non-spec://p"
    551 FAIL <area>: Setting <non-spec:/>.pathname = '/..//p' assert_equals: expected "non-spec:/.//p" but got "non-spec://p"
     546PASS URL: Setting <non-spec:/>.pathname = '/.//p' Serialize /. in path
     547PASS <a>: Setting <non-spec:/>.pathname = '/.//p' Serialize /. in path
     548PASS <area>: Setting <non-spec:/>.pathname = '/.//p' Serialize /. in path
     549PASS URL: Setting <non-spec:/>.pathname = '/..//p'
     550PASS <a>: Setting <non-spec:/>.pathname = '/..//p'
     551PASS <area>: Setting <non-spec:/>.pathname = '/..//p'
    552552FAIL URL: Setting <non-spec:/>.pathname = '//p' assert_equals: expected "non-spec:/.//p" but got "non-spec://p"
    553553FAIL <a>: Setting <non-spec:/>.pathname = '//p' assert_equals: expected "non-spec:/.//p" but got "non-spec://p"
  • trunk/Source/WTF/ChangeLog

    r267797 r267837  
     12020-10-01  Alex Christensen  <achristensen@webkit.org>
     2
     3        Non-special URLs are not idempotent
     4        https://bugs.webkit.org/show_bug.cgi?id=215762
     5
     6        Reviewed by Tim Horton.
     7
     8        https://github.com/whatwg/url/pull/505 added an interesting edge case to the URL serialization:
     9        "If url’s host is null, url’s path’s size is greater than 1, and url’s path[0] is the empty string, then append U+002F (/) followed by U+002E (.) to output."
     10        The problem was that URLs like "a:/a/..//a" would be parsed into "a://a" with a pathname of "//a" and an empty host.  If "a://a" was then reparsed, it would again have an href of "a://a"
     11        but its host would be "a" and it would have an empty path.  There is consensus that URL parsing should be idempotent, so we need to do something different here.
     12        According to https://github.com/whatwg/url/issues/415#issuecomment-419197290 this follows what Edge did (and then subsequently abandoned when they switched to Chromium)
     13        to make URL parsing idempotent by adding "/." before the path in the edge case of a URL with a non-special scheme (not http, https, wss, etc.) and a null host and a non-empty path that
     14        has an empty first segment.  All the members of the URL remain unchanged except the full serialization (href).  This is not important in practice, but important in theory.
     15
     16        Our URL parser tries very hard to use the exact same WTF::String object given as input if it can.  However, this step is better implemented as a post-processing step that will almost never happen
     17        because otherwise we would have to parse the entire path twice to find out if we need to add "./" or if the "./" that may have already been there needs to stay.  This is illustrated with the test URL
     18        "t:/.//p/../../../..//x" which does need the "./".
     19
     20        In the common case, this adds one well-predicted branch to URL parsing, so I expect performance to be unaffected.  Since this is such a rare edge case of URLs, I expect no compatibility problems.
     21
     22        * wtf/URL.cpp:
     23        (WTF::URL::pathStart const):
     24        * wtf/URL.h:
     25        (WTF::URL::pathStart const): Deleted.
     26        * wtf/URLParser.cpp:
     27        (WTF::URLParser::copyURLPartsUntil):
     28        (WTF::URLParser::URLParser):
     29        (WTF::URLParser::needsNonSpecialDotSlash const):
     30        (WTF::URLParser::addNonSpecialDotSlash):
     31        * wtf/URLParser.h:
     32
    1332020-09-30  Commit Queue  <commit-queue@webkit.org>
    234
  • trunk/Source/WTF/wtf/URL.cpp

    r265735 r267837  
    9292
    9393    return StringView(m_string).substring(start, end - start + 1);
     94}
     95
     96unsigned URL::pathStart() const
     97{
     98    unsigned start = m_hostEnd + m_portLength;
     99    if (start == m_schemeEnd + 1
     100        && start + 1 < m_string.length()
     101        && m_string[start] == '/' && m_string[start + 1] == '.')
     102        start += 2;
     103    return start;
    94104}
    95105
  • trunk/Source/WTF/wtf/URL.h

    r264304 r267837  
    168168    WTF_EXPORT_PRIVATE static bool hostIsIPAddress(StringView);
    169169
    170     unsigned pathStart() const;
     170    WTF_EXPORT_PRIVATE unsigned pathStart() const;
    171171    unsigned pathEnd() const;
    172172    unsigned pathAfterLastSlash() const;
     
    371371}
    372372
    373 inline unsigned URL::pathStart() const
    374 {
    375     return m_hostEnd + m_portLength;
    376 }
    377 
    378373inline unsigned URL::pathEnd() const
    379374{
  • trunk/Source/WTF/wtf/URLParser.cpp

    r266399 r267837  
    11/*
    2  * Copyright (C) 2016-2019 Apple Inc. All rights reserved.
     2 * Copyright (C) 2016-2020 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    808808        m_url.m_schemeEnd = base.m_schemeEnd;
    809809    }
     810
    810811    switch (scheme(StringView(m_asciiBuffer.data(), m_url.m_schemeEnd))) {
    811812    case Scheme::WS:
     
    825826        m_urlIsSpecial = false;
    826827        nonUTF8QueryEncoding = nullptr;
     828        auto pathStart = m_url.m_hostEnd + m_url.m_portLength;
     829        if (pathStart + 2 < m_asciiBuffer.size()
     830            && m_asciiBuffer[pathStart] == '/'
     831            && m_asciiBuffer[pathStart + 1] == '.'
     832            && m_asciiBuffer[pathStart + 2] == '/') {
     833            m_asciiBuffer.remove(pathStart + 1, 2);
     834            m_url.m_pathAfterLastSlash = std::max(2u, m_url.m_pathAfterLastSlash) - 2;
     835            m_url.m_pathEnd = std::max(2u, m_url.m_pathEnd) - 2;
     836            m_url.m_queryEnd = std::max(2u, m_url.m_queryEnd) - 2;
     837        }
    827838        return;
    828839    }
     
    10761087    }
    10771088#endif // ASSERT_ENABLED
     1089
     1090    if (UNLIKELY(needsNonSpecialDotSlash()))
     1091        addNonSpecialDotSlash();
    10781092}
    10791093
     
    24352449}
    24362450
     2451bool URLParser::needsNonSpecialDotSlash() const
     2452{
     2453    auto pathStart = m_url.m_hostEnd + m_url.m_portLength;
     2454    return !m_urlIsSpecial
     2455        && pathStart == m_url.m_schemeEnd + 1
     2456        && pathStart + 1 < m_url.m_string.length()
     2457        && m_url.m_string[pathStart] == '/'
     2458        && m_url.m_string[pathStart + 1] == '/';
     2459}
     2460
     2461void URLParser::addNonSpecialDotSlash()
     2462{
     2463    auto oldPathStart = m_url.m_hostEnd + m_url.m_portLength;
     2464    auto& oldString = m_url.m_string;
     2465    m_url.m_string = makeString(oldString.substring(0, oldPathStart + 1), "./", oldString.substring(oldPathStart + 1));
     2466    m_url.m_pathAfterLastSlash += 2;
     2467    m_url.m_pathEnd += 2;
     2468    m_url.m_queryEnd += 2;
     2469}
     2470
    24372471template<typename CharacterType> Optional<URLParser::LCharBuffer> URLParser::domainToASCII(StringImpl& domain, const CodePointIterator<CharacterType>& iteratorForSyntaxViolationPosition)
    24382472{
  • trunk/Source/WTF/wtf/URLParser.h

    r248546 r267837  
    115115    UChar parsedDataView(size_t position);
    116116
     117    bool needsNonSpecialDotSlash() const;
     118    void addNonSpecialDotSlash();
     119
    117120    using IPv4Address = uint32_t;
    118121    void serializeIPv4(IPv4Address);
  • trunk/Tools/ChangeLog

    r267831 r267837  
     12020-10-01  Alex Christensen  <achristensen@webkit.org>
     2
     3        Non-special URLs are not idempotent
     4        https://bugs.webkit.org/show_bug.cgi?id=215762
     5
     6        Reviewed by Tim Horton.
     7
     8        * TestWebKitAPI/Tests/WTF/URLParser.cpp:
     9        (TestWebKitAPI::TEST_F):
     10
    1112020-10-01  Carlos Garcia Campos  <cgarcia@igalia.com>
    212
  • trunk/Tools/TestWebKitAPI/Tests/WTF/URLParser.cpp

    r266399 r267837  
    208208{
    209209    checkRelativeURL(urlString, baseString, {"", "", "", "", 0, "", "", "", urlString});
     210}
     211
     212TEST_F(WTF_URLParser, Idempotence)
     213{
     214    checkURL("a://", {"a", "", "", "", 0, "", "", "", "a://"});
     215    checkURL("b:///", {"b", "", "", "", 0, "/", "", "", "b:///"});
     216    checkURL("c:/.//", {"c", "", "", "", 0, "//", "", "", "c:/.//"});
     217    checkURL("d:/..//", {"d", "", "", "", 0, "//", "", "", "d:/.//"});
     218    checkURL("e:/../..//", {"e", "", "", "", 0, "//", "", "", "e:/.//"});
     219    checkURL("f:/../../", {"f", "", "", "", 0, "/", "", "", "f:/"});
     220    checkURL("g:/././", {"g", "", "", "", 0, "/", "", "", "g:/"});
     221    checkURL("h:/./../", {"h", "", "", "", 0, "/", "", "", "h:/"});
     222    checkURL("i:/.././", {"i", "", "", "", 0, "/", "", "", "i:/"});
     223    checkURL("j:/./..//", {"j", "", "", "", 0, "//", "", "", "j:/.//"});
     224    checkURL("k:/.././/", {"k", "", "", "", 0, "//", "", "", "k:/.//"});
     225    checkURL("l:/.?", {"l", "", "", "", 0, "/", "", "", "l:/?"});
     226    checkURL("m:/./?", {"m", "", "", "", 0, "/", "", "", "m:/?"});
     227    checkURL("n:/.//?", {"n", "", "", "", 0, "//", "", "", "n:/.//?"});
     228    checkURL("o:/.#", {"o", "", "", "", 0, "/", "", "", "o:/#"});
     229    checkURL("p:/%2e//", {"p", "", "", "", 0, "//", "", "", "p:/.//"});
     230    checkURL("q:/%2e%2e//", {"q", "", "", "", 0, "//", "", "", "q:/.//"});
     231    checkURL("r:/%2e%2e/", {"r", "", "", "", 0, "/", "", "", "r:/"});
     232    checkURL("s:/%2e/", {"s", "", "", "", 0, "/", "", "", "s:/"});
     233    checkURL("t:/.//p/../../../..//x", {"t", "", "", "", 0, "//x", "", "", "t:/.//x"});
     234    checkRelativeURL("../path", "u:/.//p", {"u", "", "", "", 0, "/path", "", "", "u:/path"});
     235    checkURL("v:/.//..", {"v", "", "", "", 0, "/", "", "", "v:/"});
     236    checkURL("w:/.//..//", {"w", "", "", "", 0, "//", "", "", "w:/.//"});
     237    checkURL("x:/.//../a", {"x", "", "", "", 0, "/a", "", "", "x:/a"});
     238    checkURL("http://host/./", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
     239    checkURL("http://host/../", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
     240    checkURL("http://host/.../", {"http", "", "", "host", 0, "/.../", "", "", "http://host/.../"});
     241    checkURL("http://host/..", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
     242    checkURL("http://host/.", {"http", "", "", "host", 0, "/", "", "", "http://host/"});
    210243}
    211244
Note: See TracChangeset for help on using the changeset viewer.