1. 苏葳的备忘录首页
  2. 因特网

浏览器的HTML解析器(二)

浏览器 HTML 解析在浏览器的HTML解析完成时,会执行一些动作。在这个阶段浏览器会把文档标记为可交互的,并且将会开始处理设定为“延迟”模式的脚本—它们应该在文档处理之后执行。文档状态将被设置为“完成”,“load”事件将会触发。你可以在HTML5规范里看到标记化和树构造的完整算法—http://www.w3.org/TR/html5/syntax.html#html-parser。然而这还不是全部,浏览器还需要处理HTML的容错问题。

浏览器的容错度

你从不会在HTML页面上得到“无效语法”错误。浏览器修正一个无效内容然后继续处理。

例如下面的HTML:

<html>
<mytag>
</mytag>
<div>
<p>
</div>
    Really lousy HTML
</p>
</html>

我几乎违反了所有规则(“mytag”不是个标准标签,“p”和“div”有错误嵌套,还有其它错误)但是浏览器仍然正确显示,并没有抱怨。所以有大量的解析器代码用于修正HTML作者的错误。

浏览器的错误处理非常一致,但令人惊奇的是这并不是当前HTML规范的一部份。象书签和向后/向前按钮一样,这只是多年对浏览器的开发形成的东西。有些著名的非法HTML结构在许多网站上反复出现,于是一种浏览器就试图跟别的浏览器一样以一致的方式纠正它们。

HTML5规范定义了一些这样的需求,Webkit在HTML解析类的开始处的注释里给予了恰当的总结。

“解析器把标记化的输入解析成文档,建立文档树。如果文档组织良好,一直往下处理就行了。不幸的是,我们必须处理许多结构混乱的HTML文档,因此解析器不得不提供容错能力。我们必须考虑至少以下错误状况:

1 被增加的元素明确的不允许添加到某些外标签中。

在这种情况下,我们应该关闭所有到这个标签(拒绝添加这个元素的)之前的标签,把它添到后面。

2 不允许我们直接添加这个元素。

可能是因为写这个文档的人忘了里面的一些标签(或其中的一些标签是可选的)。

这可能发生在后面的一些标签里:HTML HEAD BODY TBODY TR TD LI (我忘了什么吗?)

3 我们希望增加一个块元素到一个内联元素里。关闭到下一个更高层的块元素为止的所有内联元素。

4 如果这些都没用,关闭元素,直到允许我们添加这个元素或忽略这个标签。”

我们看一些Webkit容错性的例子:

</br>替换<br>

一些站点用</br>代替<br>。为了兼容IE和Firefox,Webkit象处理<br>一样处理它们。代码:

if (t->isCloseTag(brTag) && m_document->inCompatMode()) {
reportError(MalformedBRError);
t->beginTag = true;

注意—错误处理只在内部—不会被展现给用户。

零散的表格

零散的表格是一个在另一个表格内容里的表格,但却没有处于一个单元格里面。

象这样的例子:

<table>
<table>
<tr><td>inner table</td></tr>
</table>
<tr><td>outer table</td></tr>
</table>

Webkit将更正这种层级关系为两个兄弟表:

<table>
<tr><td>outer table</td></tr>
</table>
<table>
<tr><td>inner table</td></tr>
</table>

代码:

if (m_inStrayTableContent && localName == tableTag)
    popBlock(tableTag);

Webkit为当前元素内容使用了一个栈—它会把内部表从外部表栈里弹出。表格就变成兄弟表了。

嵌套的form元素

在用户把一个form放进另一个form的情况下,第二个form会被忽略掉。

代码:

if (!m_currentFormElement) {
    m_currentFormElement = new HTMLFormElement(formTag, m_document);
}

过深的标签层次

注释里自己说道:

www.liceo.edu.mx是一个站点的例子,嵌套居然达到了大约1500个标签,一长串<b>标签。

我们只允许最多20个相同类型的标签嵌套,否则将全部忽略它们。

bool HTMLParser::allowNestedRedundantTag(const AtomicString& tagName)
{
    unsigned i = 0;
    for (HTMLStackElem* curr = m_blockStack;i < cMaxRedundantTagDepth && curr && curr->tagName == tagName;curr = curr->next, i++)
    { }
    return i != cMaxRedundantTagDepth;
}

html和body结束标签放置错误

再一次—注释里自己提到:

“支持未结束的html。

我们从未关闭body标签,因为一些愚蠢的网页在实际文档结束之前就关闭了它。我们依靠end()调用来关闭一切。”

if (t->tagName == htmlTag || t->tagName == bodyTag )
    return;

所以网页编写者们可得小心了—除非你愿意出现在Webkit的容错性例子里—所以记得要编写结构良好的HTML。

(待续)

原创文章,作者:苏葳,如需转载,请注明出处:https://www.swmemo.com/2038.html

发表评论

邮箱地址不会被公开。 必填项已用*标注