| Title | HTML spec single page doesn't load fully |
|---|---|
| Author | Rodrigo Arias Mallo |
| Created | Sat, 27 Jun 2026 11:03:54 +0200 |
| State | open |
When loading the full HTML WHATWG spec in Dillo, only a subset of the page seems to be rendered.
https://html.spec.whatwg.org/ (warning: huge page)
We seem to have the whole thing in the cache entry:
% dilloc dump | wc -c
15563880
% curl -s https://html.spec.whatwg.org/ | wc -c
15563880
% dilloc dump | md5sum
00af5d9e18d6e98a0879b4ea45c9baed -
% curl -s https://html.spec.whatwg.org/ | md5sum
00af5d9e18d6e98a0879b4ea45c9baed -
Which ends with the license:
% dilloc dump | xq | tail
International License</a>. To the extent portions of it are incorporated into source code, such
portions in the source code are licensed under the
<a rel="license" href="https://opensource.org/licenses/BSD-3-Clause">BSD 3-Clause License</a> instead.
</p>
<p>This is the Living Standard. Those interested in the patent-review version
should view the
<a href="/review-drafts/2026-01/">Living Standard Review Draft</a>.
</p>
</body>
</html>
But it doesn't reach the widget tree, the last section is 2.5.4 then it adds garbage:
...
<dw::Textblock::Line>
<dw::Textblock x=50 y=136006 w=695 h=101>
<dw::Textblock::Line>
<word w=42 h=18>2.5.4</word>
<word w=47 h=18>CORS</word>
<word w=69 h=18>settings</word>
<word w=84 h=18>attributes</word>
</dw::Textblock::Line>
</dw::Textblock>
<break/>
</dw::Textblock::Line>
<dw::Textblock::Line>
<dw::Textblock x=50 y=136107 w=695 h=68>
<dw::Textblock::Line>
<unknown type=32/>
<break/>
</dw::Textblock::Line>
<dw::Textblock::Line>
<dw::Textblock x=54 y=136112 w=687 h=34>
<dw::Textblock::Line>
<dw::Textblock x=54 y=136126 w=687 h=20>
<dw::Textblock::Line>
<word w=4 h=14>,</word> <--- hmm?
<word w=4 h=14>,</word>
<word w=4 h=14>,</word>
</dw::Textblock::Line>
</dw::Textblock>
<break/>
</dw::Textblock::Line>
</dw::Textblock>
<break/>
</dw::Textblock::Line>
</dw::Textblock>
<break/>
</dw::Textblock::Line>
</dw::Textblock>
</resize>
Let's see if the sanitizer catches something. Nope.
Adding a print line to the open section shows that we are stopping at the 2.5.4, so the parser is stopping:
Html_tag_open_h(tag=<h4 id="content-type-sniffing"><span class="secno">2.5.2</span>)
Html_tag_open_h(tag=<h4 id="extracting-character-encodings-from-meta-elements"><spa)
Html_tag_open_h(tag=<h4 id="cors-settings-attributes"><span class="secno">2.5.4</sp)
Also, html->stop_parser is false, so it must be stopped by another
condition. It seems it doesn't go past the 736782 byte:
Html_tag_open_h(tag=<h4 id="extracting-character-encodings-from-meta-elements"><spa)
Html_tag_open_h(tag=<h4 id="cors-settings-attributes"><span class="secno">2.5.4</sp)
DilloHtml::write BufSize=1535770 Start_Ofs=736782 EOF=0
DilloHtml::write BufSize=2093431 Start_Ofs=736782 EOF=0
DilloHtml::write BufSize=2224847 Start_Ofs=736782 EOF=0
DilloHtml::write BufSize=4387518 Start_Ofs=736782 EOF=0
DilloHtml::write BufSize=4909458 Start_Ofs=736782 EOF=0
DilloHtml::write BufSize=8756788 Start_Ofs=736782 EOF=0
DilloHtml::write BufSize=9124319 Start_Ofs=736782 EOF=0
DilloHtml::write BufSize=14841875 Start_Ofs=736782 EOF=0
DilloHtml::write BufSize=15563880 Start_Ofs=736782 EOF=0
DilloHtml::write BufSize=15563880 Start_Ofs=736782 EOF=0
DilloHtml::write BufSize=15563880 Start_Ofs=736782 EOF=1
We can dump what is at that location, and a bit before:
% dilloc dump | dd skip=736782 count=64 bs=1 status=none
, and <video> elements, provides support for CORS, defining how
% dilloc dump | dd skip=736762 count=64 bs=1 status=none
g>, <link>, <script>, and <video> elements, provides support for
^^^^^^^^
here seems to be the problem
This seems to be coming from the title attribute of a link:
<a
href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin"
title="
The crossorigin attribute, valid on the <audio>,
<img>, <link>, <script>, and <video> elements, provides
support for CORS, defining how the element handles
cross-origin requests, thereby enabling the configuration
of the CORS requests for the element's fetched data.
Depending on the element, the attribute can be a CORS
settings attribute.
"
>Attributes/crossorigin</a>
Following https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 it
seems to be acceptable for quoted attribute values to contain < or >.
However, this breaks the tag parser as it stops in <audio> instead of
continuing until the </a> is found:
Html_tag_open_a(tag=<a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin" title="The crossorigin attribute, valid on the <audio>)
Here seems to be the problem:
if ((ch = buf[buf_index]) == '>') {
break;
} else if (ch == '"' || ch == '\'') {
/* Skip over quoted string */
buf_index++;
buf_index += strcspn(buf + buf_index,
(ch == '"') ? "\">" : "'>");
if (buf[buf_index] == '>') {
/* Unterminated string value? Let's look ahead and test:
* (<: unterminated, closing-quote: terminated) */
int offset = buf_index + 1;
offset += strcspn(buf + offset,
(ch == '"') ? "\"<" : "'<");
if (buf[offset] == ch || !buf[offset]) {
buf_index = offset;
} else {
BUG_MSG("Attribute lacks closing quote.");
break;
}
}
} else if (ch == '<') {
...
The HTML spec from WHATWG seems to indicate that we should not treat < or >
characters inside a quoted string differently:
https://html.spec.whatwg.org/multipage/parsing.html#attribute-value-(double-quoted)-state
In XML, we cannot have < inside an attribute value:
https://www.w3.org/TR/REC-xml/#NT-AttValue
For HTML 4.01 it is not very clear.
Removing the special handling for < allows Dillo to parse the whole HTML spec
with no truncation.