浏览器如何准确解析脚本标签?
Posted
技术标签:
【中文标题】浏览器如何准确解析脚本标签?【英文标题】:How do browsers parse a script tag exactly? 【发布时间】:2013-01-12 12:36:50 【问题描述】:我刚刚遇到了一个关于 html 解析的病态案例。我一直认为<script>
标签会一直运行到第一个关闭</script>
标签。但事实证明,并非总是如此。
这是有效的:
<script><!--
alert('<script></script>');
--></script>
即使这是有效的:
<script><!--
alert('<script></script>');
</script>
但这不是:
<script><!--
alert('</script>');
--></script>
这也不是:
<script>
alert('<script></script>');
</script>
这种行为在 Firefox 和 Chrome 中是一致的。因此,令人难以置信的是,浏览器似乎接受脚本标签内的 html 注释内的打开+关闭脚本标签。那么问题来了,浏览器really是如何解析脚本标签的呢? 这很重要,因为我使用的 HTML 解析库 Nokogiri 假定了明显的(但不正确的)直到第一个关闭标记规则并且没有处理这种边缘情况。我想大多数其他库也不会处理它。
【问题讨论】:
将您的脚本放在一个单独的文件中。 问题与html解析有关,也就是说我在处理别人的网页。 HTML 解析的边缘案例......我现在要哭着睡觉了,祝你好运:D @Daniel 是的,我的意思是通过将实际的 javascript 代码放在一个引用的 JS 文件中,将其从 HTML 解析中取出。 这是对浏览器应该使用的状态机的详细描述:whatwg.org/specs/web-apps/current-work/multipage/…。 TBH,我不确定它是否支持您所看到的。 【参考方案1】:在仔细研究了given by Timand Jukka的链接后,我得到了以下答案:
在打开<script>
标签之后,解析器进入data1状态
如果在 data1 状态下遇到<!--
,则切换到 data2 状态
如果在任何状态下遇到-->
,则切换到data1状态
如果在 data2 状态下遇到<script[\s/>]
,则切换到 data3 状态
如果在 data3 状态下遇到</script[\s/>]
,则切换到 data2 状态
如果在任何其他状态下遇到</script[\s/>]
,则停止解析
【讨论】:
换句话说,如果您要嵌入 JSON,则在将其字符串化后,您只需将所有<
替换为 \074
,您就安全了吗?因为那只能出现在字符串中,他们会再次将该转义字符串读取为<
?
呃,JSON不喜欢八进制,必须是unicode,所以\u003c
或用“”替换“”【参考方案2】:
根据 HTML 4.01 规范,所有示例都是无效的:script
的内容声明为CDATA
,CDATA
的description 表示:
“尽管 STYLE 和 SCRIPT 元素使用 CDATA 作为其数据模型,但对于这些元素,用户代理必须以不同方式处理 CDATA。标记和实体必须被视为原始文本并按原样传递给应用程序。字符序列“
</
”(结束标记打开分隔符)的第一次出现被视为终止元素内容的结尾。在有效文档中,这将是元素的结束标记。”
正如您所观察到的,在某些情况下,浏览器可能不会强制执行此规则,而是识别成对的开始和结束标记。从规范的角度来看,这是对无效文档的处理,即错误处理。目前尚不清楚他们到底在做什么以及为什么。这似乎取决于<!--
的存在,这对HTML 4.01 解析应该没有任何影响(它不是CDATA
内容中的评论开启器)。
在 XHTML 中,应用部分不同的规则,因为在 XHTML 中,<!--
在 script
元素的内容中打开注释。
顺便说一句,由于script
中缺少type
属性,所有示例都是无效的HTML 4.01 和无效的XHTML。该属性不是必需的(浏览器默认将内容视为 JavaScript),但这些规范要求它。
在 HTML5 中,其他规则适用。它们相当复杂,它们应该描述浏览器的行为。除了强加restrictions on content(禁止例如<!--
而不匹配-->
),HTML5还指定parsing rules。
【讨论】:
【参考方案3】:标签的内容仍然是 HTML,除非您将其标记为不是 HTML。在 HTML 中,<word>
被认为是一个标签,&lt;
需要写成&lt;
以避免这种行为。或者,您想让<script>
的内容成为文本节点;使用这个公式:
<script type="text/javascript">
//<![CDATA[
// your code, with < and & and "", woohoo!
//]]>
</script>
<![CDATA[ ... ]]>
将文档的一部分描述为纯文本,没有标记。斜线是为了让 JavaScript 不会混淆;第一组斜线在 CDATA 之外,但它们是 HTML 安全的,所以没有问题。
编辑:刚刚意识到问题是关于解析,而不是编写 HTML。哎呀。
【讨论】:
你在 XHTML 甚至 HTML 4 中都是对的,但是 HTML 5 handlesscript
tags specially.【参考方案4】:
假设,如果先解析标签,然后再解析 cmets,则 HTML 解析器会为您提供这些结果。
(我并不是说一定是这样,只是一种可能的解释。)
第一种情况
<script><!--
alert('<script></script>');
--></script>
在另一个<script></script>
中有一组<script></script>
。解析器可能会首先忽略标签的名称,只检查这些标签的正确打开和关闭。然后解析 cmets。
<script><!--
--></script>
所以这是有效的。
第二种情况
<script><!--
alert('<script></script>');
</script>
在另一个<script></script>
中有一组<script></script>
。然后解析 cmets。
<script><!--
注释一直延伸到文档的末尾。这不是严格有效的,但浏览器会正确处理它。
第三种情况
<script><!--
alert('</script>');
--></script>
<script></script>
集合中有一个结束标记。在将 </script>
解析为 cmets 之前,它已失效。
第四种情况
<script>
alert('<script></script>');
</script>
在另一个<script></script>
中有一组<script></script>
,并且没有cmets。第一遍是有效的,但它会真正查看标签以查看它们是什么。它可能不接受另一个标签中的一对<script>
标签,因此它会使案例无效。
【讨论】:
我认为在第 4 种情况下,脚本标签过早关闭,第一个</script>
之后的所有内容都转到同级节点,而第二个“不匹配”</script>
被忽略。跨度>
第三种情况在 Chrome 中会出现 SyntaxError(可能第一个 </script>
也关闭了脚本,这里不确定)。以上是关于浏览器如何准确解析脚本标签?的主要内容,如果未能解决你的问题,请参考以下文章