<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[206592] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/206592">206592</a></dd>
<dt>Author</dt> <dd>achristensen@apple.com</dd>
<dt>Date</dt> <dd>2016-09-29 11:18:04 -0700 (Thu, 29 Sep 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>URLParser should ignore tabs at all possible locations
https://bugs.webkit.org/show_bug.cgi?id=162711

Reviewed by Tim Horton.

Source/WebCore:

The URL spec says to remove all tabs and newlines before parsing a URL.
To reduce passes on the URL and copies of data, I chose to just ignore them every time I increment the iterator.
This is fragile, but faster.  It can be completely tested, though.  That is what this patch does.

Covered by an addition to the API tests that tries inserting one tab at each location of each test.

* platform/URLParser.cpp:
(WebCore::URLParser::advance):
(WebCore::URLParser::isWindowsDriveLetter):
(WebCore::URLParser::appendWindowsDriveLetter):
(WebCore::URLParser::isPercentEncodedDot):
(WebCore::URLParser::isSingleDotPathSegment):
(WebCore::URLParser::isDoubleDotPathSegment):
(WebCore::URLParser::consumeSingleDotPathSegment):
(WebCore::URLParser::consumeDoubleDotPathSegment):
(WebCore::URLParser::checkLocalhostCodePoint):
(WebCore::URLParser::isAtLocalhost):
(WebCore::URLParser::isLocalhost):
(WebCore::URLParser::URLParser):
(WebCore::URLParser::parse):
(WebCore::isPercentEncodedDot): Deleted.
(WebCore::isSingleDotPathSegment): Deleted.
(WebCore::isDoubleDotPathSegment): Deleted.
(WebCore::consumeSingleDotPathSegment): Deleted.
(WebCore::consumeDoubleDotPathSegment): Deleted.
* platform/URLParser.h:
(WebCore::URLParser::advance):

Tools:

* TestWebKitAPI/Tests/WebCore/URLParser.cpp:
(TestWebKitAPI::ExpectedParts::isInvalid):
(TestWebKitAPI::checkURL):
(TestWebKitAPI::TEST_F):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreplatformURLParsercpp">trunk/Source/WebCore/platform/URLParser.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformURLParserh">trunk/Source/WebCore/platform/URLParser.h</a></li>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebCoreURLParsercpp">trunk/Tools/TestWebKitAPI/Tests/WebCore/URLParser.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (206591 => 206592)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-09-29 18:09:59 UTC (rev 206591)
+++ trunk/Source/WebCore/ChangeLog        2016-09-29 18:18:04 UTC (rev 206592)
</span><span class="lines">@@ -1,3 +1,38 @@
</span><ins>+2016-09-29  Alex Christensen  &lt;achristensen@webkit.org&gt;
+
+        URLParser should ignore tabs at all possible locations
+        https://bugs.webkit.org/show_bug.cgi?id=162711
+
+        Reviewed by Tim Horton.
+
+        The URL spec says to remove all tabs and newlines before parsing a URL.
+        To reduce passes on the URL and copies of data, I chose to just ignore them every time I increment the iterator.
+        This is fragile, but faster.  It can be completely tested, though.  That is what this patch does.
+
+        Covered by an addition to the API tests that tries inserting one tab at each location of each test.
+
+        * platform/URLParser.cpp:
+        (WebCore::URLParser::advance):
+        (WebCore::URLParser::isWindowsDriveLetter):
+        (WebCore::URLParser::appendWindowsDriveLetter):
+        (WebCore::URLParser::isPercentEncodedDot):
+        (WebCore::URLParser::isSingleDotPathSegment):
+        (WebCore::URLParser::isDoubleDotPathSegment):
+        (WebCore::URLParser::consumeSingleDotPathSegment):
+        (WebCore::URLParser::consumeDoubleDotPathSegment):
+        (WebCore::URLParser::checkLocalhostCodePoint):
+        (WebCore::URLParser::isAtLocalhost):
+        (WebCore::URLParser::isLocalhost):
+        (WebCore::URLParser::URLParser):
+        (WebCore::URLParser::parse):
+        (WebCore::isPercentEncodedDot): Deleted.
+        (WebCore::isSingleDotPathSegment): Deleted.
+        (WebCore::isDoubleDotPathSegment): Deleted.
+        (WebCore::consumeSingleDotPathSegment): Deleted.
+        (WebCore::consumeDoubleDotPathSegment): Deleted.
+        * platform/URLParser.h:
+        (WebCore::URLParser::advance):
+
</ins><span class="cx"> 2016-09-29  Simon Fraser  &lt;simon.fraser@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Fix hit testing on display:block &lt;svg&gt; elements
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformURLParsercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/URLParser.cpp (206591 => 206592)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/URLParser.cpp        2016-09-29 18:09:59 UTC (rev 206591)
+++ trunk/Source/WebCore/platform/URLParser.cpp        2016-09-29 18:18:04 UTC (rev 206592)
</span><span class="lines">@@ -414,12 +414,13 @@
</span><span class="cx"> template&lt;typename CharacterType&gt; ALWAYS_INLINE static bool isValidSchemeCharacter(CharacterType character) { return character &lt;= 'z' &amp;&amp; characterClassTable[character] &amp; Scheme; }
</span><span class="cx"> static bool shouldPercentEncodeQueryByte(uint8_t byte) { return characterClassTable[byte] &amp; QueryPercent; }
</span><span class="cx"> 
</span><del>-template&lt;typename CharacterType&gt;
</del><ins>+template&lt;typename CharacterType, URLParser::ReportSyntaxViolation reportSyntaxViolation&gt;
</ins><span class="cx"> ALWAYS_INLINE void URLParser::advance(CodePointIterator&lt;CharacterType&gt;&amp; iterator, const CodePointIterator&lt;CharacterType&gt;&amp; iteratorForSyntaxViolationPosition)
</span><span class="cx"> {
</span><span class="cx">     ++iterator;
</span><span class="cx">     while (UNLIKELY(!iterator.atEnd() &amp;&amp; isTabOrNewline(*iterator))) {
</span><del>-        syntaxViolation(iteratorForSyntaxViolationPosition);
</del><ins>+        if (reportSyntaxViolation == ReportSyntaxViolation::Yes)
+            syntaxViolation(iteratorForSyntaxViolationPosition);
</ins><span class="cx">         ++iterator;
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="lines">@@ -429,15 +430,13 @@
</span><span class="cx"> {
</span><span class="cx">     if (iterator.atEnd() || !isASCIIAlpha(*iterator))
</span><span class="cx">         return false;
</span><del>-    advance(iterator);
</del><ins>+    advance&lt;CharacterType, ReportSyntaxViolation::No&gt;(iterator);
</ins><span class="cx">     if (iterator.atEnd())
</span><span class="cx">         return false;
</span><span class="cx">     if (*iterator == ':')
</span><span class="cx">         return true;
</span><del>-    if (UNLIKELY(*iterator == '|')) {
-        syntaxViolation(iterator);
</del><ins>+    if (UNLIKELY(*iterator == '|'))
</ins><span class="cx">         return true;
</span><del>-    }
</del><span class="cx">     return false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -464,6 +463,8 @@
</span><span class="cx">     advance(iterator);
</span><span class="cx">     ASSERT(!iterator.atEnd());
</span><span class="cx">     ASSERT(*iterator == ':' || *iterator == '|');
</span><ins>+    if (*iterator == '|')
+        syntaxViolation(iterator);
</ins><span class="cx">     appendToASCIIBuffer(':');
</span><span class="cx">     advance(iterator);
</span><span class="cx"> }
</span><span class="lines">@@ -818,18 +819,18 @@
</span><span class="cx"> static const char* dotASCIICode = &quot;2e&quot;;
</span><span class="cx"> 
</span><span class="cx"> template&lt;typename CharacterType&gt;
</span><del>-ALWAYS_INLINE static bool isPercentEncodedDot(CodePointIterator&lt;CharacterType&gt; c)
</del><ins>+ALWAYS_INLINE bool URLParser::isPercentEncodedDot(CodePointIterator&lt;CharacterType&gt; c)
</ins><span class="cx"> {
</span><span class="cx">     if (c.atEnd())
</span><span class="cx">         return false;
</span><span class="cx">     if (*c != '%')
</span><span class="cx">         return false;
</span><del>-    ++c;
</del><ins>+    advance&lt;CharacterType, ReportSyntaxViolation::No&gt;(c);
</ins><span class="cx">     if (c.atEnd())
</span><span class="cx">         return false;
</span><span class="cx">     if (*c != dotASCIICode[0])
</span><span class="cx">         return false;
</span><del>-    ++c;
</del><ins>+    advance&lt;CharacterType, ReportSyntaxViolation::No&gt;(c);
</ins><span class="cx">     if (c.atEnd())
</span><span class="cx">         return false;
</span><span class="cx">     return toASCIILower(*c) == dotASCIICode[1];
</span><span class="lines">@@ -836,24 +837,24 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> template&lt;typename CharacterType&gt;
</span><del>-ALWAYS_INLINE static bool isSingleDotPathSegment(CodePointIterator&lt;CharacterType&gt; c)
</del><ins>+ALWAYS_INLINE bool URLParser::isSingleDotPathSegment(CodePointIterator&lt;CharacterType&gt; c)
</ins><span class="cx"> {
</span><span class="cx">     if (c.atEnd())
</span><span class="cx">         return false;
</span><span class="cx">     if (*c == '.') {
</span><del>-        ++c;
</del><ins>+        advance&lt;CharacterType, ReportSyntaxViolation::No&gt;(c);
</ins><span class="cx">         return c.atEnd() || isSlashQuestionOrHash(*c);
</span><span class="cx">     }
</span><span class="cx">     if (*c != '%')
</span><span class="cx">         return false;
</span><del>-    ++c;
</del><ins>+    advance&lt;CharacterType, ReportSyntaxViolation::No&gt;(c);
</ins><span class="cx">     if (c.atEnd() || *c != dotASCIICode[0])
</span><span class="cx">         return false;
</span><del>-    ++c;
</del><ins>+    advance&lt;CharacterType, ReportSyntaxViolation::No&gt;(c);
</ins><span class="cx">     if (c.atEnd())
</span><span class="cx">         return false;
</span><span class="cx">     if (toASCIILower(*c) == dotASCIICode[1]) {
</span><del>-        ++c;
</del><ins>+        advance&lt;CharacterType, ReportSyntaxViolation::No&gt;(c);
</ins><span class="cx">         return c.atEnd() || isSlashQuestionOrHash(*c);
</span><span class="cx">     }
</span><span class="cx">     return false;
</span><span class="lines">@@ -860,24 +861,24 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> template&lt;typename CharacterType&gt;
</span><del>-ALWAYS_INLINE static bool isDoubleDotPathSegment(CodePointIterator&lt;CharacterType&gt; c)
</del><ins>+ALWAYS_INLINE bool URLParser::isDoubleDotPathSegment(CodePointIterator&lt;CharacterType&gt; c)
</ins><span class="cx"> {
</span><span class="cx">     if (c.atEnd())
</span><span class="cx">         return false;
</span><span class="cx">     if (*c == '.') {
</span><del>-        ++c;
</del><ins>+        advance&lt;CharacterType, ReportSyntaxViolation::No&gt;(c);
</ins><span class="cx">         return isSingleDotPathSegment(c);
</span><span class="cx">     }
</span><span class="cx">     if (*c != '%')
</span><span class="cx">         return false;
</span><del>-    ++c;
</del><ins>+    advance&lt;CharacterType, ReportSyntaxViolation::No&gt;(c);
</ins><span class="cx">     if (c.atEnd() || *c != dotASCIICode[0])
</span><span class="cx">         return false;
</span><del>-    ++c;
</del><ins>+    advance&lt;CharacterType, ReportSyntaxViolation::No&gt;(c);
</ins><span class="cx">     if (c.atEnd())
</span><span class="cx">         return false;
</span><span class="cx">     if (toASCIILower(*c) == dotASCIICode[1]) {
</span><del>-        ++c;
</del><ins>+        advance&lt;CharacterType, ReportSyntaxViolation::No&gt;(c);
</ins><span class="cx">         return isSingleDotPathSegment(c);
</span><span class="cx">     }
</span><span class="cx">     return false;
</span><span class="lines">@@ -884,27 +885,27 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> template&lt;typename CharacterType&gt;
</span><del>-static void consumeSingleDotPathSegment(CodePointIterator&lt;CharacterType&gt;&amp; c)
</del><ins>+void URLParser::consumeSingleDotPathSegment(CodePointIterator&lt;CharacterType&gt;&amp; c)
</ins><span class="cx"> {
</span><span class="cx">     ASSERT(isSingleDotPathSegment(c));
</span><span class="cx">     if (*c == '.') {
</span><del>-        ++c;
</del><ins>+        advance(c);
</ins><span class="cx">         if (!c.atEnd()) {
</span><span class="cx">             if (*c == '/' || *c == '\\')
</span><del>-                ++c;
</del><ins>+                advance(c);
</ins><span class="cx">             else
</span><span class="cx">                 ASSERT(*c == '?' || *c == '#');
</span><span class="cx">         }
</span><span class="cx">     } else {
</span><span class="cx">         ASSERT(*c == '%');
</span><del>-        ++c;
</del><ins>+        advance(c);
</ins><span class="cx">         ASSERT(*c == dotASCIICode[0]);
</span><del>-        ++c;
</del><ins>+        advance(c);
</ins><span class="cx">         ASSERT(toASCIILower(*c) == dotASCIICode[1]);
</span><del>-        ++c;
</del><ins>+        advance(c);
</ins><span class="cx">         if (!c.atEnd()) {
</span><span class="cx">             if (*c == '/' || *c == '\\')
</span><del>-                ++c;
</del><ins>+                advance(c);
</ins><span class="cx">             else
</span><span class="cx">                 ASSERT(*c == '?' || *c == '#');
</span><span class="cx">         }
</span><span class="lines">@@ -912,18 +913,18 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> template&lt;typename CharacterType&gt;
</span><del>-static void consumeDoubleDotPathSegment(CodePointIterator&lt;CharacterType&gt;&amp; c)
</del><ins>+void URLParser::consumeDoubleDotPathSegment(CodePointIterator&lt;CharacterType&gt;&amp; c)
</ins><span class="cx"> {
</span><span class="cx">     ASSERT(isDoubleDotPathSegment(c));
</span><span class="cx">     if (*c == '.')
</span><del>-        ++c;
</del><ins>+        advance(c);
</ins><span class="cx">     else {
</span><span class="cx">         ASSERT(*c == '%');
</span><del>-        ++c;
</del><ins>+        advance(c);
</ins><span class="cx">         ASSERT(*c == dotASCIICode[0]);
</span><del>-        ++c;
</del><ins>+        advance(c);
</ins><span class="cx">         ASSERT(toASCIILower(*c) == dotASCIICode[1]);
</span><del>-        ++c;
</del><ins>+        advance(c);
</ins><span class="cx">     }
</span><span class="cx">     consumeSingleDotPathSegment(c);
</span><span class="cx"> }
</span><span class="lines">@@ -991,6 +992,46 @@
</span><span class="cx">     m_url.m_string = m_inputString;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+template&lt;typename CharacterType&gt;
+bool URLParser::checkLocalhostCodePoint(CodePointIterator&lt;CharacterType&gt;&amp; iterator, UChar32 codePoint)
+{
+    if (iterator.atEnd() || toASCIILower(*iterator) != codePoint)
+        return false;
+    advance&lt;CharacterType, ReportSyntaxViolation::No&gt;(iterator);
+    return true;
+}
+
+template&lt;typename CharacterType&gt;
+bool URLParser::isAtLocalhost(CodePointIterator&lt;CharacterType&gt; iterator)
+{
+    if (!checkLocalhostCodePoint(iterator, 'l'))
+        return false;
+    if (!checkLocalhostCodePoint(iterator, 'o'))
+        return false;
+    if (!checkLocalhostCodePoint(iterator, 'c'))
+        return false;
+    if (!checkLocalhostCodePoint(iterator, 'a'))
+        return false;
+    if (!checkLocalhostCodePoint(iterator, 'l'))
+        return false;
+    if (!checkLocalhostCodePoint(iterator, 'h'))
+        return false;
+    if (!checkLocalhostCodePoint(iterator, 'o'))
+        return false;
+    if (!checkLocalhostCodePoint(iterator, 's'))
+        return false;
+    if (!checkLocalhostCodePoint(iterator, 't'))
+        return false;
+    return iterator.atEnd();
+}
+
+bool URLParser::isLocalhost(StringView view)
+{
+    if (view.is8Bit())
+        return isAtLocalhost(CodePointIterator&lt;LChar&gt;(view.characters8(), view.characters8() + view.length()));
+    return isAtLocalhost(CodePointIterator&lt;UChar&gt;(view.characters16(), view.characters16() + view.length()));
+}
+
</ins><span class="cx"> ALWAYS_INLINE StringView URLParser::parsedDataView(size_t start, size_t length)
</span><span class="cx"> {
</span><span class="cx">     if (UNLIKELY(m_didSeeSyntaxViolation)) {
</span><span class="lines">@@ -1028,9 +1069,20 @@
</span><span class="cx">         m_inputBegin = input.characters16();
</span><span class="cx">         parse(input.characters16(), input.length(), base, encoding);
</span><span class="cx">     }
</span><ins>+
</ins><span class="cx">     ASSERT(!m_url.m_isValid
</span><span class="cx">         || m_didSeeSyntaxViolation == (m_url.string() != input)
</span><span class="cx">         || (input.isEmpty() &amp;&amp; m_url.m_string == base.m_string));
</span><ins>+    ASSERT(internalValuesConsistent(m_url));
+#if !ASSERT_DISABLED
+    if (!m_didSeeSyntaxViolation) {
+        // Force a syntax violation at the beginning to make sure we get the same result.
+        URLParser parser(makeString(&quot; &quot;, input), base, encoding);
+        URL parsed = parser.result();
+        if (parsed.isValid())
+            ASSERT(allValuesEqual(parser.result(), m_url));
+    }
+#endif
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> template&lt;typename CharacterType&gt;
</span><span class="lines">@@ -1211,7 +1263,7 @@
</span><span class="cx">             if (*c == '/') {
</span><span class="cx">                 appendToASCIIBuffer('/');
</span><span class="cx">                 state = State::AuthorityOrHost;
</span><del>-                ++c;
</del><ins>+                advance(c);
</ins><span class="cx">                 m_url.m_userStart = currentPosition(c);
</span><span class="cx">                 authorityOrHostBegin = c;
</span><span class="cx">             } else {
</span><span class="lines">@@ -1305,6 +1357,7 @@
</span><span class="cx">                     auto lastAt = c;
</span><span class="cx">                     auto findLastAt = c;
</span><span class="cx">                     while (!findLastAt.atEnd()) {
</span><ins>+                        LOG(URLParser, &quot;Finding last @: %c&quot;, *findLastAt);
</ins><span class="cx">                         if (*findLastAt == '@')
</span><span class="cx">                             lastAt = findLastAt;
</span><span class="cx">                         bool isSlash = *findLastAt == '/' || (m_urlIsSpecial &amp;&amp; *findLastAt == '\\');
</span><span class="lines">@@ -1342,23 +1395,25 @@
</span><span class="cx">             } while (!c.atEnd());
</span><span class="cx">             break;
</span><span class="cx">         case State::Host:
</span><del>-            LOG_STATE(&quot;Host&quot;);
-            if (*c == '/' || *c == '?' || *c == '#') {
-                if (!parseHostAndPort(CodePointIterator&lt;CharacterType&gt;(authorityOrHostBegin, c))) {
-                    failure();
-                    return;
</del><ins>+            do {
+                LOG_STATE(&quot;Host&quot;);
+                if (*c == '/' || *c == '?' || *c == '#') {
+                    if (!parseHostAndPort(CodePointIterator&lt;CharacterType&gt;(authorityOrHostBegin, c))) {
+                        failure();
+                        return;
+                    }
+                    if (*c == '?' || *c == '#') {
+                        syntaxViolation(c);
+                        appendToASCIIBuffer('/');
+                        m_url.m_pathAfterLastSlash = currentPosition(c);
+                    }
+                    state = State::Path;
+                    break;
</ins><span class="cx">                 }
</span><del>-                if (*c == '?' || *c == '#') {
-                    syntaxViolation(c);
-                    appendToASCIIBuffer('/');
-                    m_url.m_pathAfterLastSlash = currentPosition(c);
-                }
-                state = State::Path;
-                break;
-            }
-            if (isPercentOrNonASCII(*c))
-                m_hostHasPercentOrNonASCII = true;
-            ++c;
</del><ins>+                if (isPercentOrNonASCII(*c))
+                    m_hostHasPercentOrNonASCII = true;
+                ++c;
+            } while (!c.atEnd());
</ins><span class="cx">             break;
</span><span class="cx">         case State::File:
</span><span class="cx">             LOG_STATE(&quot;File&quot;);
</span><span class="lines">@@ -1426,8 +1481,8 @@
</span><span class="cx">             if (LIKELY(*c == '/' || *c == '\\')) {
</span><span class="cx">                 if (UNLIKELY(*c == '\\'))
</span><span class="cx">                     syntaxViolation(c);
</span><del>-                ++c;
</del><span class="cx">                 appendToASCIIBuffer('/');
</span><ins>+                advance(c);
</ins><span class="cx">                 m_url.m_userStart = currentPosition(c);
</span><span class="cx">                 m_url.m_userEnd = m_url.m_userStart;
</span><span class="cx">                 m_url.m_passwordEnd = m_url.m_userStart;
</span><span class="lines">@@ -1463,55 +1518,57 @@
</span><span class="cx">             state = State::Path;
</span><span class="cx">             break;
</span><span class="cx">         case State::FileHost:
</span><del>-            LOG_STATE(&quot;FileHost&quot;);
-            if (isSlashQuestionOrHash(*c)) {
-                bool windowsQuirk = c.codeUnitsSince(authorityOrHostBegin) == 2 &amp;&amp; isWindowsDriveLetter(authorityOrHostBegin);
-                if (windowsQuirk) {
-                    syntaxViolation(authorityOrHostBegin);
-                    appendToASCIIBuffer('/');
-                    appendWindowsDriveLetter(authorityOrHostBegin);
-                }
-                if (windowsQuirk || authorityOrHostBegin == c) {
-                    ASSERT(windowsQuirk || parsedDataView(currentPosition(c) - 1, 1) == &quot;/&quot;);
-                    if (UNLIKELY(*c == '?')) {
-                        syntaxViolation(c);
-                        appendToASCIIBuffer(&quot;/?&quot;, 2);
-                        ++c;
-                        m_url.m_pathAfterLastSlash = currentPosition(c) - 1;
-                        m_url.m_pathEnd = m_url.m_pathAfterLastSlash;
-                        state = State::Query;
</del><ins>+            do {
+                LOG_STATE(&quot;FileHost&quot;);
+                if (isSlashQuestionOrHash(*c)) {
+                    bool windowsQuirk = c.codeUnitsSince(authorityOrHostBegin) == 2 &amp;&amp; isWindowsDriveLetter(authorityOrHostBegin);
+                    if (windowsQuirk) {
+                        syntaxViolation(authorityOrHostBegin);
+                        appendToASCIIBuffer('/');
+                        appendWindowsDriveLetter(authorityOrHostBegin);
+                    }
+                    if (windowsQuirk || authorityOrHostBegin == c) {
+                        ASSERT(windowsQuirk || parsedDataView(currentPosition(c) - 1, 1) == &quot;/&quot;);
+                        if (UNLIKELY(*c == '?')) {
+                            syntaxViolation(c);
+                            appendToASCIIBuffer(&quot;/?&quot;, 2);
+                            ++c;
+                            m_url.m_pathAfterLastSlash = currentPosition(c) - 1;
+                            m_url.m_pathEnd = m_url.m_pathAfterLastSlash;
+                            state = State::Query;
+                            break;
+                        }
+                        if (UNLIKELY(*c == '#')) {
+                            syntaxViolation(c);
+                            appendToASCIIBuffer(&quot;/#&quot;, 2);
+                            ++c;
+                            m_url.m_pathAfterLastSlash = currentPosition(c) - 1;
+                            m_url.m_pathEnd = m_url.m_pathAfterLastSlash;
+                            m_url.m_queryEnd = m_url.m_pathAfterLastSlash;
+                            state = State::Fragment;
+                            break;
+                        }
+                        state = State::Path;
</ins><span class="cx">                         break;
</span><span class="cx">                     }
</span><del>-                    if (UNLIKELY(*c == '#')) {
</del><ins>+                    if (!parseHostAndPort(CodePointIterator&lt;CharacterType&gt;(authorityOrHostBegin, c))) {
+                        failure();
+                        return;
+                    }
+                    if (UNLIKELY(isLocalhost(parsedDataView(m_url.m_passwordEnd, currentPosition(c) - m_url.m_passwordEnd)))) {
</ins><span class="cx">                         syntaxViolation(c);
</span><del>-                        appendToASCIIBuffer(&quot;/#&quot;, 2);
-                        ++c;
-                        m_url.m_pathAfterLastSlash = currentPosition(c) - 1;
-                        m_url.m_pathEnd = m_url.m_pathAfterLastSlash;
-                        m_url.m_queryEnd = m_url.m_pathAfterLastSlash;
-                        state = State::Fragment;
-                        break;
</del><ins>+                        m_asciiBuffer.shrink(m_url.m_passwordEnd);
+                        m_url.m_hostEnd = currentPosition(c);
+                        m_url.m_portEnd = m_url.m_hostEnd;
</ins><span class="cx">                     }
</span><del>-                    state = State::Path;
</del><ins>+                    
+                    state = State::PathStart;
</ins><span class="cx">                     break;
</span><span class="cx">                 }
</span><del>-                if (!parseHostAndPort(CodePointIterator&lt;CharacterType&gt;(authorityOrHostBegin, c))) {
-                    failure();
-                    return;
-                }
-                if (UNLIKELY(equalLettersIgnoringASCIICase(parsedDataView(m_url.m_passwordEnd, currentPosition(c) - m_url.m_passwordEnd), &quot;localhost&quot;))) {
-                    syntaxViolation(c);
-                    m_asciiBuffer.shrink(m_url.m_passwordEnd);
-                    m_url.m_hostEnd = currentPosition(c);
-                    m_url.m_portEnd = m_url.m_hostEnd;
-                }
-                
-                state = State::PathStart;
-                break;
-            }
-            if (isPercentOrNonASCII(*c))
-                m_hostHasPercentOrNonASCII = true;
-            ++c;
</del><ins>+                if (isPercentOrNonASCII(*c))
+                    m_hostHasPercentOrNonASCII = true;
+                ++c;
+            } while (!c.atEnd());
</ins><span class="cx">             break;
</span><span class="cx">         case State::PathStart:
</span><span class="cx">             LOG_STATE(&quot;PathStart&quot;);
</span><span class="lines">@@ -1553,16 +1610,15 @@
</span><span class="cx">                 state = State::Fragment;
</span><span class="cx">                 break;
</span><span class="cx">             }
</span><del>-            if (isPercentEncodedDot(c)) {
-                if (UNLIKELY(*c != '.'))
-                    syntaxViolation(c);
</del><ins>+            if (UNLIKELY(isPercentEncodedDot(c))) {
+                syntaxViolation(c);
</ins><span class="cx">                 appendToASCIIBuffer('.');
</span><span class="cx">                 ASSERT(*c == '%');
</span><del>-                ++c;
</del><ins>+                advance(c);
</ins><span class="cx">                 ASSERT(*c == dotASCIICode[0]);
</span><del>-                ++c;
</del><ins>+                advance(c);
</ins><span class="cx">                 ASSERT(toASCIILower(*c) == dotASCIICode[1]);
</span><del>-                ++c;
</del><ins>+                advance(c);
</ins><span class="cx">                 break;
</span><span class="cx">             }
</span><span class="cx">             utf8PercentEncode&lt;isInDefaultEncodeSet&gt;(c);
</span><span class="lines">@@ -1777,7 +1833,7 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         syntaxViolation(c);
</span><del>-        if (equalLettersIgnoringASCIICase(parsedDataView(m_url.m_passwordEnd, currentPosition(c) - m_url.m_passwordEnd), &quot;localhost&quot;)) {
</del><ins>+        if (isLocalhost(parsedDataView(m_url.m_passwordEnd, currentPosition(c) - m_url.m_passwordEnd))) {
</ins><span class="cx">             m_asciiBuffer.shrink(m_url.m_passwordEnd);
</span><span class="cx">             m_url.m_hostEnd = currentPosition(c);
</span><span class="cx">             m_url.m_portEnd = m_url.m_hostEnd;
</span><span class="lines">@@ -1835,7 +1891,6 @@
</span><span class="cx">     }
</span><span class="cx">     m_url.m_isValid = true;
</span><span class="cx">     LOG(URLParser, &quot;Parsed URL &lt;%s&gt;&quot;, m_url.m_string.utf8().data());
</span><del>-    ASSERT(internalValuesConsistent(m_url));
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> template&lt;typename CharacterType&gt;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformURLParserh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/URLParser.h (206591 => 206592)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/URLParser.h        2016-09-29 18:09:59 UTC (rev 206591)
+++ trunk/Source/WebCore/platform/URLParser.h        2016-09-29 18:18:04 UTC (rev 206592)
</span><span class="lines">@@ -66,12 +66,23 @@
</span><span class="cx">     template&lt;typename CharacterType&gt; bool parsePort(CodePointIterator&lt;CharacterType&gt;&amp;);
</span><span class="cx"> 
</span><span class="cx">     void failure();
</span><del>-    template&lt;typename CharacterType&gt; void advance(CodePointIterator&lt;CharacterType&gt;&amp; iterator) { advance(iterator, iterator); }
-    template&lt;typename CharacterType&gt; void advance(CodePointIterator&lt;CharacterType&gt;&amp;, const CodePointIterator&lt;CharacterType&gt;&amp; iteratorForSyntaxViolationPosition);
</del><ins>+    enum class ReportSyntaxViolation { No, Yes };
+    template&lt;typename CharacterType, ReportSyntaxViolation reportSyntaxViolation = ReportSyntaxViolation::Yes&gt;
+    void advance(CodePointIterator&lt;CharacterType&gt;&amp; iterator) { advance&lt;CharacterType, reportSyntaxViolation&gt;(iterator, iterator); }
+    template&lt;typename CharacterType, ReportSyntaxViolation = ReportSyntaxViolation::Yes&gt;
+    void advance(CodePointIterator&lt;CharacterType&gt;&amp;, const CodePointIterator&lt;CharacterType&gt;&amp; iteratorForSyntaxViolationPosition);
</ins><span class="cx">     template&lt;typename CharacterType&gt; void syntaxViolation(const CodePointIterator&lt;CharacterType&gt;&amp;);
</span><span class="cx">     template&lt;typename CharacterType&gt; void fragmentSyntaxViolation(const CodePointIterator&lt;CharacterType&gt;&amp;);
</span><ins>+    template&lt;typename CharacterType&gt; bool isPercentEncodedDot(CodePointIterator&lt;CharacterType&gt;);
</ins><span class="cx">     template&lt;typename CharacterType&gt; bool isWindowsDriveLetter(CodePointIterator&lt;CharacterType&gt;);
</span><ins>+    template&lt;typename CharacterType&gt; bool isSingleDotPathSegment(CodePointIterator&lt;CharacterType&gt;);
+    template&lt;typename CharacterType&gt; bool isDoubleDotPathSegment(CodePointIterator&lt;CharacterType&gt;);
</ins><span class="cx">     template&lt;typename CharacterType&gt; bool shouldCopyFileURL(CodePointIterator&lt;CharacterType&gt;);
</span><ins>+    template&lt;typename CharacterType&gt; bool checkLocalhostCodePoint(CodePointIterator&lt;CharacterType&gt;&amp;, UChar32);
+    template&lt;typename CharacterType&gt; bool isAtLocalhost(CodePointIterator&lt;CharacterType&gt;);
+    bool isLocalhost(StringView);
+    template&lt;typename CharacterType&gt; void consumeSingleDotPathSegment(CodePointIterator&lt;CharacterType&gt;&amp;);
+    template&lt;typename CharacterType&gt; void consumeDoubleDotPathSegment(CodePointIterator&lt;CharacterType&gt;&amp;);
</ins><span class="cx">     template&lt;typename CharacterType&gt; void appendWindowsDriveLetter(CodePointIterator&lt;CharacterType&gt;&amp;);
</span><span class="cx">     template&lt;typename CharacterType&gt; size_t currentPosition(const CodePointIterator&lt;CharacterType&gt;&amp;);
</span><span class="cx">     template&lt;typename UnsignedIntegerType&gt; void appendNumberToASCIIBuffer(UnsignedIntegerType);
</span><span class="lines">@@ -92,7 +103,7 @@
</span><span class="cx">     using IPv6Address = std::array&lt;uint16_t, 8&gt;;
</span><span class="cx">     template&lt;typename CharacterType&gt; Optional&lt;IPv6Address&gt; parseIPv6Host(CodePointIterator&lt;CharacterType&gt;);
</span><span class="cx">     void serializeIPv6Piece(uint16_t piece);
</span><del>-    void serializeIPv6(URLParser::IPv6Address);
</del><ins>+    void serializeIPv6(IPv6Address);
</ins><span class="cx"> 
</span><span class="cx">     enum class URLPart;
</span><span class="cx">     template&lt;typename CharacterType&gt; void copyURLPartsUntil(const URL&amp; base, URLPart, const CodePointIterator&lt;CharacterType&gt;&amp;);
</span></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (206591 => 206592)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog        2016-09-29 18:09:59 UTC (rev 206591)
+++ trunk/Tools/ChangeLog        2016-09-29 18:18:04 UTC (rev 206592)
</span><span class="lines">@@ -1,3 +1,15 @@
</span><ins>+2016-09-29  Alex Christensen  &lt;achristensen@webkit.org&gt;
+
+        URLParser should ignore tabs at all possible locations
+        https://bugs.webkit.org/show_bug.cgi?id=162711
+
+        Reviewed by Tim Horton.
+
+        * TestWebKitAPI/Tests/WebCore/URLParser.cpp:
+        (TestWebKitAPI::ExpectedParts::isInvalid):
+        (TestWebKitAPI::checkURL):
+        (TestWebKitAPI::TEST_F):
+
</ins><span class="cx"> 2016-09-29  Gyuyoung Kim  &lt;gyuyoung.kim@navercorp.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [EFL] Add search button to url bar in MiniBrowser
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebCoreURLParsercpp"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/URLParser.cpp (206591 => 206592)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebCore/URLParser.cpp        2016-09-29 18:09:59 UTC (rev 206591)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/URLParser.cpp        2016-09-29 18:18:04 UTC (rev 206592)
</span><span class="lines">@@ -49,6 +49,18 @@
</span><span class="cx">     String query;
</span><span class="cx">     String fragment;
</span><span class="cx">     String string;
</span><ins>+
+    bool isInvalid() const
+    {
+        return protocol.isEmpty()
+            &amp;&amp; user.isEmpty()
+            &amp;&amp; password.isEmpty()
+            &amp;&amp; host.isEmpty()
+            &amp;&amp; !port
+            &amp;&amp; path.isEmpty()
+            &amp;&amp; query.isEmpty()
+            &amp;&amp; fragment.isEmpty();
+    }
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> static bool eq(const String&amp; s1, const String&amp; s2)
</span><span class="lines">@@ -57,7 +69,7 @@
</span><span class="cx">     return s1.utf8() == s2.utf8();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static void checkURL(const String&amp; urlString, const ExpectedParts&amp; parts)
</del><ins>+static void checkURL(const String&amp; urlString, const ExpectedParts&amp; parts, bool checkTabs = true)
</ins><span class="cx"> {
</span><span class="cx">     bool wasEnabled = URLParser::enabled();
</span><span class="cx">     URLParser::setEnabled(true);
</span><span class="lines">@@ -89,6 +101,14 @@
</span><span class="cx">     EXPECT_TRUE(URLParser::allValuesEqual(url, oldURL));
</span><span class="cx">     EXPECT_TRUE(URLParser::internalValuesConsistent(url));
</span><span class="cx">     EXPECT_TRUE(URLParser::internalValuesConsistent(oldURL));
</span><ins>+
+    if (checkTabs) {
+        for (size_t i = 0; i &lt; urlString.length(); ++i) {
+            String urlStringWithTab = makeString(urlString.substring(0, i), &quot;\t&quot;, urlString.substring(i));
+            ExpectedParts invalidPartsWithTab = {&quot;&quot;, &quot;&quot;, &quot;&quot;, &quot;&quot;, 0, &quot;&quot; , &quot;&quot;, &quot;&quot;, urlStringWithTab};
+            checkURL(urlStringWithTab, parts.isInvalid() ? invalidPartsWithTab : parts, false);
+        }
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> template&lt;size_t length&gt;
</span><span class="lines">@@ -275,7 +295,7 @@
</span><span class="cx">     checkURL(&quot;http://:@host&quot;, {&quot;http&quot;, &quot;&quot;, &quot;&quot;, &quot;host&quot;, 0, &quot;/&quot;, &quot;&quot;, &quot;&quot;, &quot;http://host/&quot;});
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static void checkRelativeURL(const String&amp; urlString, const String&amp; baseURLString, const ExpectedParts&amp; parts)
</del><ins>+static void checkRelativeURL(const String&amp; urlString, const String&amp; baseURLString, const ExpectedParts&amp; parts, bool checkTabs = true)
</ins><span class="cx"> {
</span><span class="cx">     bool wasEnabled = URLParser::enabled();
</span><span class="cx">     URLParser::setEnabled(true);
</span><span class="lines">@@ -307,6 +327,14 @@
</span><span class="cx">     EXPECT_TRUE(URLParser::allValuesEqual(url, oldURL));
</span><span class="cx">     EXPECT_TRUE(URLParser::internalValuesConsistent(url));
</span><span class="cx">     EXPECT_TRUE(URLParser::internalValuesConsistent(oldURL));
</span><ins>+    
+    if (checkTabs) {
+        for (size_t i = 0; i &lt; urlString.length(); ++i) {
+            String urlStringWithTab = makeString(urlString.substring(0, i), &quot;\t&quot;, urlString.substring(i));
+            ExpectedParts invalidPartsWithTab = {&quot;&quot;, &quot;&quot;, &quot;&quot;, &quot;&quot;, 0, &quot;&quot; , &quot;&quot;, &quot;&quot;, urlStringWithTab};
+            checkRelativeURL(urlStringWithTab, baseURLString, parts.isInvalid() ? invalidPartsWithTab : parts, false);
+        }
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> TEST_F(URLParserTest, ParseRelative)
</span><span class="lines">@@ -403,6 +431,8 @@
</span><span class="cx">     EXPECT_FALSE(URLParser::allValuesEqual(url, oldURL));
</span><span class="cx">     EXPECT_TRUE(URLParser::internalValuesConsistent(url));
</span><span class="cx">     EXPECT_TRUE(URLParser::internalValuesConsistent(oldURL));
</span><ins>+    
+    // FIXME: check tabs here like we do for checkURL and checkRelativeURL.
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static void checkRelativeURLDifferences(const String&amp; urlString, const String&amp; baseURLString, const ExpectedParts&amp; partsNew, const ExpectedParts&amp; partsOld)
</span><span class="lines">@@ -437,6 +467,8 @@
</span><span class="cx">     EXPECT_FALSE(URLParser::allValuesEqual(url, oldURL));
</span><span class="cx">     EXPECT_TRUE(URLParser::internalValuesConsistent(url));
</span><span class="cx">     EXPECT_TRUE(URLParser::internalValuesConsistent(oldURL));
</span><ins>+
+    // FIXME: check tabs here like we do for checkURL and checkRelativeURL.
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> // These are differences between the new URLParser and the old URL::parse which make URLParser more standards compliant.
</span><span class="lines">@@ -859,13 +891,13 @@
</span><span class="cx">         {&quot;ws&quot;, &quot;&quot;, &quot;&quot;, &quot;&quot;, 0, &quot;&quot;, &quot;&quot;, &quot;&quot;, &quot;ws:&quot;},
</span><span class="cx">         {&quot;ws&quot;, &quot;&quot;, &quot;&quot;, &quot;&quot;, 0, &quot;s:&quot;, &quot;&quot;, &quot;&quot;, &quot;ws:s:&quot;});
</span><span class="cx">     checkRelativeURL(&quot;notspecial:&quot;, &quot;http://example.org/foo/bar&quot;, {&quot;notspecial&quot;, &quot;&quot;, &quot;&quot;, &quot;&quot;, 0, &quot;&quot;, &quot;&quot;, &quot;&quot;, &quot;notspecial:&quot;});
</span><del>-    
</del><ins>+
</ins><span class="cx">     const wchar_t surrogateBegin = 0xD800;
</span><span class="cx">     const wchar_t validSurrogateEnd = 0xDD55;
</span><span class="cx">     const wchar_t invalidSurrogateEnd = 'A';
</span><span class="cx">     checkURL(utf16String&lt;12&gt;({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', surrogateBegin, validSurrogateEnd, '\0'}),
</span><del>-        {&quot;http&quot;, &quot;&quot;, &quot;&quot;, &quot;w&quot;, 0, &quot;/%F0%90%85%95&quot;, &quot;&quot;, &quot;&quot;, &quot;http://w/%F0%90%85%95&quot;});
-    
</del><ins>+        {&quot;http&quot;, &quot;&quot;, &quot;&quot;, &quot;w&quot;, 0, &quot;/%F0%90%85%95&quot;, &quot;&quot;, &quot;&quot;, &quot;http://w/%F0%90%85%95&quot;}, false);
+
</ins><span class="cx">     // URLParser matches Chrome and Firefox but not URL::parse.
</span><span class="cx">     checkURLDifferences(utf16String&lt;12&gt;({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', surrogateBegin, invalidSurrogateEnd}),
</span><span class="cx">         {&quot;http&quot;, &quot;&quot;, &quot;&quot;, &quot;w&quot;, 0, &quot;/%EF%BF%BDA&quot;, &quot;&quot;, &quot;&quot;, &quot;http://w/%EF%BF%BDA&quot;},
</span><span class="lines">@@ -897,6 +929,8 @@
</span><span class="cx">     EXPECT_TRUE(eq(parts.query, url.query()));
</span><span class="cx">     EXPECT_TRUE(eq(parts.fragment, url.fragmentIdentifier()));
</span><span class="cx">     EXPECT_TRUE(eq(parts.string, url.string()));
</span><ins>+
+    // FIXME: check tabs here like we do for checkURL and checkRelativeURL.
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> TEST_F(URLParserTest, QueryEncoding)
</span></span></pre>
</div>
</div>

</body>
</html>