<html>
    <head>
      <base href="https://bugs.webkit.org/">
    </head>
    <body><table border="1" cellspacing="0" cellpadding="8">
        <tr>
          <th>Bug ID</th>
          <td><a class="bz_bug_link 
          bz_status_NEW "
   title="NEW - TextCodec should treat lone surrogates as the replacement character"
   href="https://bugs.webkit.org/show_bug.cgi?id=235307">235307</a>
          </td>
        </tr>

        <tr>
          <th>Summary</th>
          <td>TextCodec should treat lone surrogates as the replacement character
          </td>
        </tr>

        <tr>
          <th>Product</th>
          <td>WebKit
          </td>
        </tr>

        <tr>
          <th>Version</th>
          <td>WebKit Nightly Build
          </td>
        </tr>

        <tr>
          <th>Hardware</th>
          <td>Unspecified
          </td>
        </tr>

        <tr>
          <th>OS</th>
          <td>Unspecified
          </td>
        </tr>

        <tr>
          <th>Status</th>
          <td>NEW
          </td>
        </tr>

        <tr>
          <th>Severity</th>
          <td>Normal
          </td>
        </tr>

        <tr>
          <th>Priority</th>
          <td>P2
          </td>
        </tr>

        <tr>
          <th>Component</th>
          <td>WebCore Misc.
          </td>
        </tr>

        <tr>
          <th>Assignee</th>
          <td>webkit-unassigned@lists.webkit.org
          </td>
        </tr>

        <tr>
          <th>Reporter</th>
          <td>abotella@igalia.com
          </td>
        </tr></table>
      <p>
        <div>
        <pre>Created <span class=""><a href="attachment.cgi?id=449360" name="attach_449360" title="Test case for lone surrogate character entities in URL parsing">attachment 449360</a> <a href="attachment.cgi?id=449360&action=edit" title="Test case for lone surrogate character entities in URL parsing">[details]</a></span>
Test case for lone surrogate character entities in URL parsing

WebCore uses TextCodec as its implementation of the encoding standard, and the "encode" algorithm in it (<a href="https://encoding.spec.whatwg.org/#encode">https://encoding.spec.whatwg.org/#encode</a>) corresponds to calling TextCodec::encode with UnencodableHandling::Entities. However, the encoding standard algorithm only allows a scalar value string as its input (or anything else which can be converted to an I/O queue of scalar values; <a href="https://encoding.spec.whatwg.org/#to-i-o-queue-convert">https://encoding.spec.whatwg.org/#to-i-o-queue-convert</a>), whereas TextCodec::encode is called with arbitrary `StringView`s, which can contain lone surrogates. This would be fine if the codecs handled lone surrogates as if they were the replacement character, but they don't: the UTF-8 encoder emits invalid UTF-8 (for example, U+D800 encodes to 0xED 0xA0 0x80, which is invalid UTF-8; see <a href="https://simonsapin.github.io/wtf-8">https://simonsapin.github.io/wtf-8</a>), and the rest of codecs emit a character entity for the lone surrogate (&#55296; for example).

The non-UTF-8 case can be observed through URL parsing; see the attached test case. The spec here is a bit convoluted, but when the URL parser is in the query state (<a href="https://url.spec.whatwg.org/#query-state">https://url.spec.whatwg.org/#query-state</a>), it calls into "percent-encode after encoding", which itself calls the Encoding Standard's "encode or fail", and if that fails, it emits a URL-encoded character entity for the unencodable code point. The input to "encode or fail" must also be a scalar value string, and therefore the unencodable code point in the error thrown by that algorithm can't be a surrogate code point. This bug doesn't affect URL parsing with the UTF-8 encoding because it uses a different code path.

Observing the UTF-8 case in web content is not easy, but in a Windows system you can create files with lone surrogates, and in the Windows WebKit port, uploading those in a multipart/form-data form will result in the form payload containing invalid UTF-8 (or surrogate character entities, if the page uses some other encoding) – see <a href="https://github.com/whatwg/html/issues/7413">https://github.com/whatwg/html/issues/7413</a>. Both of these bugs could be fixed by converting the strings in the URL parsing and form submission code before passing them to TextCodec, but it arguably should be fixed in the TextCodec implementations in any case to prevent regressions.

However, there does seem to be one case where surrogate character entities are not necessarily a bug, and that is saving a page to disk. Right click / Save As will serialize the DOM and any associated resources as MHTML, with the intent that opening it will round-trip the DOM. If there is a lone surrogate in the DOM, and the page's encoding isn't UTF-8, the serialization will emit a surrogate character entity which will correctly round-trip as a surrogate in the DOM. However, if the page's encoding is UTF-8, the surrogate will serialize as (quoted-printable) invalid UTF-8. If this is a case worth keeping –which it might not be–, there should be a new variant in the UnencodableHandling enum to have lone surrogates serialize as surrogate character entities in all encodings.

See also the corresponding Chromium bug: <a href="https://crbug.com/1285987">https://crbug.com/1285987</a></pre>
        </div>
      </p>


      <hr>
      <span>You are receiving this mail because:</span>

      <ul>
          <li>You are the assignee for the bug.</li>
      </ul>
    </body>
</html>