HTML 解释器

article/2025/9/15 11:46:26
解释过程

HTML 解释器的工作就是将网络或者本地磁盘获取的 HTML 网页和资源从字节流解释成 DOM 树结构。这一过程大致可以理解成下图
在这里插入图片描述
WebKit 中这一过程如下:首先是字节流,经过解码之后是字符流,然后通过词法分析器把字符流解释成词语(Tokens),之后经过语法分析器构建成节点,最后这些节点被组建成一棵 DOM 树。

词法分析

在进行词法分析之前,解释器首先要做的事情就是检查该网页内容使用的编码格式,以便后面使用合适的解码器。如果解释器在 HTML 网页中找到了设置的编码格式, WebKit 会使用相应的解码器来将字节流转换成特定格式的字符串。如果没有特殊格式,词法分析器 HTMLTokenizer 类可以直接进行词法分析

词法分析的工作都是由 HTMLTokenizer 来完成 ,简单来说,它就是一个状态机---输入的是字符串,输出的是一个个词语。因为字节流可能是分段的,所以输入的字符串可能也是分段的,但是这对词法分析器来说没有什么特别之处,它会自己维护内部的状态信息。

XSSAuditor 验证词语

当词语生成之后,WebKit 需要使用 XSSAuditor 来验证词语流(Token Stream)。XSS 指的是 Cross Site Security , 主要是针对安全方面的考虑。

根据 XSS 的安全机制,对于解析出来的这些词语,可能会阻碍某些内容的进一步执行,所以 XSSAuditor 类主要负责过滤这些被阻止的内容,只有通过的词语才会作后面的处理

词语到节点

经过词法分析器解释之后的词语随之被 XSSAuditor 过滤并且在没有被阻止之后,将被 WebKit 用来构建 DOM 节点。从词语到构建节点的步骤是由 HTMLDocumentParser 类调用 HTMLTreeBuilder 类的 “constructTree” 函数来实现。

节点到 DOM 树

从节点到构建 DOM 树,包括为树中的元素节点创建属性节点等工作由 HTMLConstructionSite 类来完成。正如前面介绍的,该类包含一个 DOM 树的根节点 ——HTMLDocument 对象,其他的元素节点都是它的后代。

因为 HTML 文档的 Tag 标签是有开始和结束标记的,所以构建这一过程可以使用栈结构来帮忙。HTMLConstructionSite 类中包含一个 “HTMLElementStack” 变量,它是一个保存元素节点的栈,其中的元素节点是当前有开始标记但是还没有结束标记的元素节点。想象一下 HTML 文档的特点,例如一个片段 <body><div><img></img></div></body>当解释到 img 元素的开始标记时,栈中的元素就是 body 、div 和 img ,当遇到 img 的结束标记时,img 退栈, img 是 div 元素的子女;当遇到 div 的结束标记时,div 退栈,表明 div 和它的子女都已处理完,以此类推。

JavaScript 的执行

在 HTML 解释器的工作过程中,可能会有 JavaScript 代码(全局作用域的代码)需要执行,它发生在将字符串解释成词语之后、创建各种节点的时候。这也是全局执行的 JavaScript 代码不能访问 DOM 树的原因——因为 DOM 树还没有被创建完

所以建议 JavaScript 的使用如下
1、将 “script” 元素加上 “async” 属性,表明这是一个可以异步执行的 JavaScript 代码。

2、将 “script” 元素放在 “body” 元素的最后,这样它不会阻碍其他资源的并发下载。

但是不这样做的时候,WebKit 使用预扫描和预加载机制来实现资源的并发下载而不被 JavaScript 的执行所阻碍。

具体做法是:当遇到需要执行 JavaScript 代码的时候,WebKit 先暂停当前 JavaScript 代码的执行,使用预先扫描器 HTMLPreloadScanner 类来扫描后面的词语。如果 WebKit 发现它们需要使用其他资源,那么使用预资源加载器 HTMLPreloadScanner 类来发送请求,在这之后,才执行 JavaScript代码。预先扫描器本身并不创建节点对象,也不会构建 DOM 树,所以速度比较快。

当 DOM 树构建完之后,WebKit 触发 “DOMContentLoaded” 事件,注册在该事件上的 JavaScript 函数会被调用。当所在资源都被加载完之后,WebKit 触发 “onload” 事件

DOM 事件机制

事件在工作过程中使用两个主体,第一个是事件(event),第二个是事件目标(EventTarget)。WebKit 中用 EventTarget 类来表示 DOM 规范中 Events 部分定义的事件目标

每个 事件都有属性来标记该事件的事件目标。当事件到达事件目标(如一个元素节点)的时候,在这个目标上注册的监听者(Event Listeners)都会有触发调用,而这些监听者的调用顺序不是固定的,所以不能依赖监听者注册的顺序来决定你的代码逻辑

事件处理最重要就是事件捕获(Event capture)和事件冒泡(Event bubbling)这两种机制。图 5-18 是事件捕获和事件冒泡的过程。

在这里插入图片描述
当渲染引擎接收到一个事件的时候,它会通过 HitTest(WebKit 中的一种检查触发gkwrd哪个区域的算法)检查哪个元素是直接的事件目标。在图 5-18 中,以 “img” 为例,假设它是事件的直接目标,这样,事件会经过自顶向下和自底向上的两个过程。

事件的捕获是自顶向下,事件先是到 document 节点,然后一路到达目标节点。在图 5-18 中,顺序就是 “#document” -> “HTML” -> “body” -> “img” 这样一个顺序。事件可以在这一传递过程中被捕获,只需要在注册监听者的时候设置相应参数即可。默认情况下,其他节点不捕获这样的事件。如果网页注册了这样的监听者,那么监听者的回调函数会被调用,函数可以通过事件的 “stopPropagation” 函数来阻止事件向下传递

事件的冒泡过程是从下向上的顺序,它的默认行为是不冒泡,但是是事件包含一个是否冒泡的属性。当这一属性为真的时候,渲染引擎会将该事件首先传递给事件的目标节点的父亲,然后是父亲的父亲,以此类推。同捕获动作一样,这此监听函数也可以使用 “stopPropagation” 函数来阻止事件向上传递

WebKit 的事件处理机制

DOM 的事件分为很多种,与用户相关的只是其中的一种,称为 UIEvent ,其他的包括 CustomEvent、MutationEvent 等。UIEvent 又可以分为很多种,包括但是不限于 FocusEvent、MouseEvent、KeyboardEvent、Composition 等

基于 WebKit 的浏览器事件处理过程,首先是做 HitTest ,查找事件发生处的元素,检查该元素有无监听者。如果网页的相关节点注册了事件的监听者,那么浏览器会把事件派发给 WebKit 内核来处理。同时,浏览器也可能需要理解和处理这样的事件。这主要是因为,有些事件浏览器必须响应从而对网页作默认处理

影子(Shadow)DOM

影子 DOM 是一个新东西,主要解决了一个文档中可能需要大量交互的多个 DOM 树建立和维护各自的功能边界的问题。

当网页的开发者需要访问网页 DOM 树的时候,这些控件内部的 DOM 子树都会暴露出来,这些暴露的节点不仅可能给 DOM 树的遍历带来很多麻烦,而且也可能给 CSS 的样式选择带来问题,因为选择器无意中可能会改变这些内部节点的样式,从而导致很奇怪的控件界面。

HTML5 支持了很多新的特性,例如对视频、音频的支持,读者会发现这些元素其实是由很复杂的控制界面组成,这些界面也是使用 HTML 元素编写,但是在 DOM 树中,你无法找到相应的节点,这其实也是使用了影子 DOM 的思想。

因为影子 DOM 的子树在整个网页的 DOM 树中不可见,那么事件是如何处理的呢 ?事件中需要包含事件目标,这个目标当然不能是不可见的 DOM 节点,所以事件目标其实就是包含影子 DOM 子树的节点对象。事件捕获的逻辑没有发生变化,在影子 DOM 子树内也会继续传递。当影子 DOM 子树中的事件向上冒泡的时候, WebKit 会同时向整个文档的 DOM 上传递该事件,以避免一些很奇怪的行为。

使用shadowDOM
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Document</title>
</head>
<body> <div id="div"></div><script>window.onload = function(){let adiv = document.getElementById('div');let root = adiv.createShadowRoot();let shadowImg = document.createElement("img");shadowImg.src = "/render.png";shadowImg.width= 100;shadowImg.height= 200;root.appendChild(shadowImg);let shadowDiv = document.createElement('div');shadowDiv.innerHTML = 'this is div from shadow dom!';root.appendChild(shadowDiv);}</script>
</body>
</html>

网页只包含了一个 “div” 元素,JavaScript 代码使用该元素创建了一个影子 DOM 子树的根节点,然后该根节点下加入了两个子女,第一个是图片元素,第二个是 “div” 元素,该元素内部包含了一些文本。

如此解决上述问题


http://chatgpt.dhexx.cn/article/9bCxBHhO.shtml

相关文章

Pycharm python解释器的选择与使用

python解释器的使用 和Pycharm有关的东西&#xff1a; 首先自己电脑之前在学习软件里面已经有了pycharm&#xff0c;但是那个目录是在中文目录的&#xff0c;很多项目运行是没有问题的&#xff0c;但部分可能会出现因为中文目录而导致的报错&#xff0c;所以只能用中文目录来弄…

PHPstorm解释器配置

最近使用PHP打BUUCTF用到了PHPstorm进行PHP语言的部分改写&#xff0c;其中安装解释器部分遇到了一些坑。如下&#xff1a; 首先官网下载PHPstorm进行安装&#xff0c;破解方式网上也有许多&#xff0c;可以直接百度&#xff0c;我是在《不迷路资源库》下载的。 安装后进行进入…

配置Python解释器

在运行python项目时,如果代码目录中没有venv目录(即别人已经配置好的解释器),往往不能直接运行,因此需要自己添加项目对应的解释器,即使有别人配好的解释器,也会因为安装的python位置差异导致解释器不可用,本文将对这两种情况分别解决。(以我曾经碰到过的情况为例,不一定全面) …

python解释器配置_Python解释器的配置

1、准备工作 安装好Pycharm2017版本 电脑上安装好Python解释器 2、本地解释器配置 配置本地解释器的步骤相对简洁直观&#xff1a; (1)单击工具栏中的设置按钮。 (2)在Settings/Preferences对话框中选中 Project Interpreter页面&#xff0c;在Project Interpreter对应的下拉列…

学习笔记——解释器

解释器(英语:Interpreter)&#xff0c;又译为直译器&#xff0c;是一种电脑程序&#xff0c;能够把高级编程语言一行一行直接转译运行。解释器不会一次把整个程序转译出来&#xff0c;只像一位"中间人"&#xff0c;每次运行程序时都要先转成另一种语言再作运行&#…

【设计模式】解释器模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )

文章目录 一、解释器模式简介二、解释器模式适用场景三、解释器模式优缺点四、解释器模式与适配器模式五、解释器模式代码示例1、解释器接口2、加法解释器3、乘法解释器4、整型解释器5、语法解析类6、工具类7、测试类 一、解释器模式简介 解释器模式 : 给定一个 语言 , 定义它的…

linux每周2 4 6执行定时任务,linux计划任务crontab例子

/root/send_msg.sh #要自动执行的脚本程序路径 chmod +x /root/send_msg.sh #对脚本文件添加执行权限,否则不能执行 vim /etc/crontab #编辑配置文件,在最后一行添加内容 30 1 * * * root /root/send_msg.sh #表示每天凌晨1点30执行备份 :wq! #保存退出 service crond restar…

Crontab 在线生成器 - Linux计划任务

Linux系统里有一个很方便的程序「例行性计划任务」&#xff08;Crontab&#xff09;&#xff0c;接触过的朋友一定不陌生。Crontab 主要是让系统去执行一些固定时间要自动进行的例行性工作&#xff0c;最常用的例如备份资料、移除暂存文件、更新或重新启动等等。如果将某个周期…

Linux计划任务以及进程检测与控制

安装第三方环境变量 /etc/profile PATH export PATH $home /.bashrc 当前用户的bash信息&#xff08;aliase&#xff0c;umask&#xff09; $home/.bash_profile 当前用户的环境变量的信息 $home /.bash_logout /ect/bashrc 使用bash shell用户全局变量 /etc/profile 系统和…

Linux服务器查看任务计划,Linux的任务计划

Linux的任务计划 我们在使用系统的过程中&#xff0c;经常需要在以后的某个时间点执行一个任务&#xff0c;而我们又不在机器旁边的时候&#xff0c;比如在半夜时服务器访问的用户很少&#xff0c;宽带资源很充足的时候&#xff0c;要去镜像软件包、重启服务器(虽然Linux很稳定…

Linux 计划任务讲解

目录 计划任务 一次性计划任务 长期性计划任务 计划任务 管理员可以编辑自己的和普通用户的计划任务 普通用户只可以编辑自己的计划任务 计划任务根据执行方式分为一次性计划任务、长期性计划任务 一次性计划任务 此计划只执行一次&#xff0c;执行后或就不会再执行了 通…

Linux计划任务-定时任务

Linux计划任务-定时任务 at单次计划任务at命令格式查看at任务取消at计划任务创建1分钟后的单次任务 定时任务分类cron系统任务调度/etc/crontab文件格式 用户任务调度安装crontab conrtab常用添加计划任务指定用户创建计划任务 查看用户的计划任务计划任务日志存储目录查看所有…

Linux 计划任务

计划任务主要是做一些周期性的任务&#xff0c;目前最主要的用途是定期备份数据。 #分类 ##一次性调度执行 at Schedule one-time tasks with at. 语法格式&#xff1a;at 时间点 例如&#xff1a; [rootBJcoud_computing_hp_hao ~]# at now 2min 2分钟之后 [rootBJcoud_co…

Linux基础之计划任务

在Linux的运维中&#xff0c;我们经常会使用到计划任务&#xff0c;所谓计划任务就是在某个时间让计算机做某件事情。我们可以将计划任务分为一次性的调度执行和循环调度执行。 一、一次性调度执行 一次性调度执行在实际情况中使用少于循环调度执行。在Linux操作系统中使用at &…

linux(计划任务)

一、概念 计划任务就是按照计划定时的去执行任务 二、crond程序--是linux里负责执行计划任务的程序 &#xff08;1&#xff09;使用crontab命令调用crond进程 &#xff08;2&#xff09;crond程序--每隔一分钟去检查是否有计划任务要执行. 2、crond进程的启动、重启、停止 …

Excel2019打开文件时会同时打开一个名为Sheet1的未保存空白工作簿

因为安装了MySQL导致 解决办法&#xff1a; 1、右键EXCEL以管理员身份打开 2、点击“文件”——“选项”——“加载项” 在下方“管理”的下拉框中选择“COM加载项” 点击“转到”&#xff0c;在弹出的界面中取消MySQL For Excel的勾选——确定 再次打开Excel便不会出现打开…

EXCEL每次打开文件都会出现一个空白sheet1窗口

不需要禁用所有加载项&#xff0c;禁用所有的加载项会影响功能&#xff0c;找到COM加载项&#xff0c;转到&#xff0c;取消勾选MySQL的加载项&#xff0c;取消后没有自动创建的sheet1工作簿了&#xff0c;希望有帮助

“EXCEL 文件打开后,显示空白”的解决方法

今天遇到一个很奇怪的问题&#xff0c;一个Excel文件&#xff0c;大小大概50K左右&#xff0c;里面肯定是有内容的。 但打开后&#xff0c;显示的却是空白&#xff0c;如下图&#xff1a; 临时解决方案&#xff1a; 选择打开方式“使用IE打开”&#xff0c;之后弹出的对话框&am…

excel表格打开灰色,没有内容

1、修改注册列表&#xff0c;快捷键 winR,然后输入regedit,回车进入&#xff1b; 2、找 HKEY_CLASSES_ROOT ; Excel.Sheet.12 ; shell ; open ; command ; 3、双击右侧“默认”图标编辑&#xff0c;将数值最后的/dde修改成"%1"&#x1f62d;"%1"与前面的…

已解决:EXCEL每次打开文件都会再出现一个空白sheet1文件

问题描述 安装过MySQL后&#xff0c;它自动关联到excel &#xff0c;以引用其数据源。但是这样一来&#xff0c;每次打开&#xff0c;都会加载一个新的文件sheet1&#xff0c;每次都得手动关闭&#xff1b;在网上搜了一大堆&#xff08;什么禁用相关COM加载项&#xff0c;等等…