本文作者:hfliu
文章来源:携程UED
解析结束后的动作
在这一阶段浏览器会把文档标记为交互模式,并开始解析deferred模式的script。"deferred"意味着脚本应该在文档解析完成后执行。脚本处理完成后将进入"complete"状态,"load"事件发生。
HTML5规范中包含了完整的算法: http://www.w3.org/TR/html5/syntax.html#html-parser
浏览器的容错
你永远不会看到HTML页面语法错误。浏览器会修正错误并继续。看看下面的例子:
- <html>
- <mytag>
- mytag>
- <div>
- <p>
- div>
- Really lousy HTML
- p>
- html>
我一定违背了几百万条规则("my tag"是非法标签,"p"与"p"元素嵌套错误等等),但浏览器仍然正确地显示,没有任何抱怨。所以很多解析器代码在修正这些HTML作者的错误。
浏览器的错误处理相当统一,惊人的是这并不是当前HTML规范的一部分,就像书签、前进、后退,只是多年以来在浏览器中开发出来的。有些无效的HTML结构出现在许多网站,浏览器会尝试用和其它各种浏览器一致的方式修复这些错误。
HTML5规范中应这一需求定义了一些东西,Webkit在它的HTML解析器类开头的注释中很好的做了摘要:
解析器分析输入符号生成文档,并构建文档树。如果文档格式良好,解析工作会很简单。
不幸的是,我们要处理很多格式不良的HTML文档,解析器需要宽容这些错误。
我们至少需要照顾下列错误:
1. 元素必需被插入在正确的位置。未关闭的标签应该一一关闭,直到可以添加新元素。
2. 不允许直接添加元素。用户可能会漏掉一些标签,比如:HTML HEAD BODY TBODY TR TD LI(我遗漏了什么?)。
3. 在inline元素里添加block元素时,应关闭所有inline元素,再添加block元素。
4. 如果以上不起作用,关闭所有元素,直到可以添加,或者忽略此标签。
让我们来看一些Webkit容错的例子:
代替
而不是
。为了更好的与IE和Firefox兼容,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 tabletd>tr>
- table>
- <tr><td>outer tabletd>tr>
- table>
Webkit会改变层级关系,把它们处理成两个相临的表格:
- <table>
- <tr><td>outer tabletd>tr>
- table>
- <table>
- <tr><td>inner tabletd>tr>
- table>
代码:
if (m_inStrayTableContent && localName == tableTag) popBlock(tableTag);
Webkit用一个堆栈保存当前元素,它会把里面的表格弹出到外部表格堆栈,使它们成为兄弟表格。
元素嵌套
为防止一表单的嵌套,第二个表单会被忽略。代码:
- if (!m_currentFormElement) {
- m_currentFormElement = new HTMLFormElement(formTag, m_document);
- }
过深的元素层级
注释不言自喻:
www.liceo.edu.mx是一个层级过深的典型,它用大量的嵌套到1500个标签的深度。我们只允许同一标签连续出现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 我们永远不关闭tag,因为有些愚蠢的网页在文档真正结束之前就关闭了它。 让我们用end()来关闭标签。
- if (t->tagName == htmlTag || t->tagName == bodyTag )
- return;
所以网页作者们小心了,除非你想写一个Webkit容错的示例代码,否则请按正确格式书写HTML。