浏览器是怎样工作的二:渲染引擎 HTML解析(4)(转)

本文作者:hfliu

文章来源:携程UED

解析结束后的动作

在这一阶段浏览器会把文档标记为交互模式,并开始解析deferred模式的script。"deferred"意味着脚本应该在文档解析完成后执行。脚本处理完成后将进入"complete"状态,"load"事件发生。

HTML5规范中包含了完整的算法: http://www.w3.org/TR/html5/syntax.html#html-parser

浏览器的容错

你永远不会看到HTML页面语法错误。浏览器会修正错误并继续。看看下面的例子:

  1. <html>  
  2.     <mytag>  
  3.     mytag>  
  4.     <div>  
  5.           <p>    
  6.     div>  
  7.         Really lousy HTML   
  8.     p>  
  9.  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>。代码如下:

  1. if (t->isCloseTag(brTag) && m_document->inCompatMode()) {   
  2.      reportError(MalformedBRError);   
  3.      t->beginTag = true;   
  4. }  

注意,这里的错误处理是内部的,并不会显示给用户。

迷失的表格

像下面的例子这样,一个表格包含在另外一个表格的内容中,但不是在外部表格的单元格里:

  1. <table>  
  2.     <table>  
  3.         <tr><td>inner tabletd>tr>  
  4.          table>  
  5.     <tr><td>outer tabletd>tr>  
  6.  table>  

Webkit会改变层级关系,把它们处理成两个相临的表格:

  1. <table>  
  2.     <tr><td>outer tabletd>tr>  
  3.  table>  
  4.  <table>  
  5.     <tr><td>inner tabletd>tr>  
  6.  table>  

代码:

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

Webkit用一个堆栈保存当前元素,它会把里面的表格弹出到外部表格堆栈,使它们成为兄弟表格。

元素嵌套

为防止一表单的嵌套,第二个表单会被忽略。代码:

  1. if (!m_currentFormElement) {   
  2.         m_currentFormElement = new HTMLFormElement(formTag,    m_document);   
  3. }  

过深的元素层级

注释不言自喻:

www.liceo.edu.mx是一个层级过深的典型,它用大量的嵌套到1500个标签的深度。我们只允许同一标签连续出现20次,超过的话,所有此标签都会被忽略。

  1. bool HTMLParser::allowNestedRedundantTag(const AtomicString& tagName)   
  2. {   
  3. unsigned i = 0;   
  4.  for (HTMLStackElem* curr = m_blockStack;   
  5.          i < cMaxRedundantTagDepth && curr && curr->tagName == tagName;   
  6.      curr = curr->next, i++) { }   
  7.  return i != cMaxRedundantTagDepth;   
  8. }  

错误的html或body结束标签位置

注释仍然很明了:

支持真正的错误html 我们永远不关闭tag,因为有些愚蠢的网页在文档真正结束之前就关闭了它。 让我们用end()来关闭标签。
  1. if (t->tagName == htmlTag || t->tagName == bodyTag )   
  2.         return;  

所以网页作者们小心了,除非你想写一个Webkit容错的示例代码,否则请按正确格式书写HTML。

posted @ 2013-01-16 14:49  碎念的风  阅读(241)  评论(0编辑  收藏  举报