`

(转)WebKit,鼠标引发的故事

阅读更多

 

原文地址:http://blog.sina.com.cn/s/blog_46d0a3930100dgvi.html

 

WebKit,鼠标引发的故事



 
Figure 1. JavaScript onclick event
Courtesy http://farm4.static.flickr.com/3302/3640149734_3268bf297f_o.jpg

先看一段简单的HTML文件。在浏览器里打开这个文件,将看到两张照片。把鼠标移动到第一张照片,点击鼠标左键,将自动弹出一个窗口,上书“World”。但是当鼠标移动到第二张照片,或者其它任何区域,点击鼠标,却没有反应。关闭“World”窗口,自动弹出第二个窗口,上书“Hello”。

<html>
  <script type="text/javascript">
    function myfunction(v)
    {
      alert(v)
    }
  </script>

  <body onclick="myfunction('Hello')">
    <p>
    <img onclick="myfunction('World')" height="250" width="290" src="http://www.dirjournal.com/info/wp-content/uploads/2009/02/antarctica_mountain_mirrored.jpg">
    <p>
    <img height="206" width="275" src="http://media-cdn.tripadvisor.com/media/photo-s/01/26/f4/eb/hua-shan-hua-mountain.jpg">
  </body>
</html>


这段HTML文件没有什么特别之处,所有略知一点HTML的人,估计都会写。但是耳熟能详,未必等于深入了解。不妨反问自己几个问题,

1. 浏览器如何知道,是否鼠标的位置,在第一个照片的范围内?

2. 假如修改一下HTML文件,把第一张照片替换成另一张照片,前后两张照片的尺寸不同。在浏览器里打开修改后的文件,我们会发现,能够触发弹出窗口事件的区域面积,随着照片的改变而自动改变。浏览器内部,是通过什么样的机制,自动识别事件触发区域的?

3. Onclick 是HTML的元素属性(Element attribute),还是JavaScript的事件侦听器(EventListener)?换而言之,当用户点击鼠标以后,负责处理onclick事件的,是Webkit 还是JavaScript Engine?

4. Alert() 是HTML定义的方法,还是JavaScript提供的函数?谁负责生成那两个弹出的窗口,是Webkit还是JavaScript Engine?

5. 注意到有两个onclick="myfunction(...)",当用户在第一张照片里点击鼠标的时候,为什么是先后弹出,而不是同时弹出?

6. 除了PC上的浏览器以外,手机是否也可以完成同样的事件及其响应?假如手机上没有鼠标,但是有触摸屏,如何把onclick定义成用手指点击屏幕?

7. 为什么需要深入了解这些问题? 除了满足好奇心以外,还有没有其它目的?



 
Figure 2. Event callback stacks
Courtesy http://farm4.static.flickr.com/3611/3640149728_bc64397f60_o.gif

当用户点击鼠标,在OS语汇里,这叫发生了一次中断(interrupt)。系统内核(kernel) 如何侦听以及处理interrupt,不妨参阅“Programming Embedded Systems” 一书,Chapter 8. Interrupts。这里不展开介绍,有两个原因,1. 这些内容很庞杂,而且与本文主题不太相关。2. 从Webkit角度看,它不必关心interrupt 以及interrupt handling 的具体实现,因为Webkit建筑在GUI Toolkit之上,而GUI Toolkit已经把底层的interrupt handling,严密地封装起来。Webkit只需要调用GUI Toolkit 的相关APIs,就可以截获鼠标的点击和移动,键盘的输入等等诸多事件。所以,本文着重讨论Figure 2 中,位于顶部的Webkit和JavaScript两层。

不同的操作系统,有相应的GUI Toolkit。GUI Toolkit提供一系列APIs,方便应用程序去管理各色窗口和控件,以及鼠标和键盘等等UI事件的截获和响应。

1. 微软的Windows操作系统之上的GUI Toolkit,是MFC(Microsoft Fundation Classes)。

2. Linux操作系统GNOME环境的GUI Toolkit,是GTK+.

3. Linux KDE环境的,是QT。

4. Java的GUI Toolkit有两个,一个是Sun Microsystem的Java Swing,另一个是IBM Eclipse的SWT。
  
   Swing对native的依赖较小,它依靠Java 2D来绘制窗口以及控件,而Java 2D对于native的依赖基本上只限于用native library画点画线着色。 SWT对native的依赖较大,很多人把SWT理解为Java通过JNI,对MFC,GTK+和QT进行的封装。这种理解虽然不是百分之百准确,但是大体上也没错。

有了GUI Toolkit,应用程序处理鼠标和键盘等等UI事件的方式,就简化了许多,只需要做两件事情。1. 把事件来源(event source),与事件处理逻辑(event listener) 绑定。2. 解析并执行事件处理逻辑。

Figure 3 显示的是Webkit如何绑定event source和event listener。Figure 4 显示的是Webkit如何调用JavaScript Engine,解析并执行事件处理逻辑。首先看看event source,注意到在HTML文件里有这么一句,
   <img onclick="myfunction('World')" height="250" width="290"  src=".../antarctica_mountain_mirrored.jpg">

这句话里“<img>”标识告诉Webkit,需要在浏览器页面里摆放一张照片,“src”属性明确了照片的来源,“height, width”明确了照片的尺寸。“onclick”属性提醒Webkit,当用户把鼠标移动到照片显示的区域,并点击鼠标时(onclick),需要有所响应。响应的方式定义在“onclick”属性的值里面,也就是“myfunction('World')”。

当Webkit解析这个HTML文件时,它依据这个HTML文件生成一棵DOM Tree,和一棵Render Tree。对应于这一句<img>语句,在DOM Tree里有一个HTMLElement节点,相应地,在Render Tree里有一个RenderImage节点。在layout() 过程结束后,根据<img>语句中规定的height和width,确定了RenderImage的大小和位置。由于 Render Tree的RenderImage节点,与DOM Tree的HTMLElement节点一一对应,所以HTMLElement节点所处的位置和大小也相应确定。

因为onclick事件与这个HTMLElement节点相关联,所以这个HTMLElement节点的位置和大小确定了以后,点击事件的触发区域也就自动确定。假如修改了HTML文件,替换了照片,经过layout() 过程以后,新照片对应的HTMLElement节点,它的位置和大小也自动相应变化,所以,点击事件的触发区域也就相应地自动变化。

在onclick属性的值里,定义了如何处理这个事件的逻辑。有两种处理事件的方式,1. 直接调用HTML DOM method,2. 间接调用外设的Script。onclick="alert('Hello')",是第一种方式。alert()是W3C制订的标准的 HTML DOM methods之一。除此以外,也有稍微复杂一点的methods,譬如可以把这一句改成,<img onclick="document.write('Hello')">。本文的例子,onclick="myfunction('world')",是第二种方式,间接调用外设的Script。

外设的script有多种,最常见的是JavaScript,另外,微软的VBScript和Adobe的ActionScript,在一些浏览器里也能用。即便是JavaScript,也有多种版本,各个版本之间,语法上存在一些差别。为了消弭这些差别,降低JavaScript使用者,以及 JavaScript Engine开发者的负担,ECMA(欧洲电脑产联)试图制订一套标准的JavaScript规范,称为ECMAScript。

各个浏览器使用的JavaScript Engine不同。

1. 微软的IE浏览器,使用的JavaScript Engine是JScript Engine,渲染机是Trident。

2. Firefox浏览器,使用的JavaScript Engine是TraceMonkey,TraceMonkey的前身是SpiderMonkey,渲染机是Gecko。TraceMonkey JavaScript Engine借用了Adobe的Tamarin的部分代码,尤其是Just-In-Time即时编译机的代码。而Tamarin也被用在Adobe Flash的Action Engine中。

3. Opera浏览器,使用的JavaScript Engine是Futhark,它的前身是Linear_b,渲染机是Presto。

4. Apple的Safari浏览器,使用的JavaScript Engine是SquirrelFish,渲染机是Webkit。

5. Google的Chrome浏览器,使用的JavaScript Engine是V8,渲染机也是Webkit。

6. Linux的KDE和GNOME环境中可以使用Konqueror浏览器,这个浏览器使用的JavaScript Engine是JavaScriptCore,前身是KJS,渲染机也是Webkit。

同样是Webkit渲染机,可以调用不同的JavaScript Engine。之所以能做到这一点,是因为Webkit的架构设计,在设置JavaScript Engine的时候,利用代理器,采取了松散的调用方式。



 Figure 3. The listener binding of Webkit
Courtesy http://farm4.static.flickr.com/3659/3640149732_e55446f6b3_b.jpg

Figure 3 详细描绘了Webkit 设置JavaScript Engine 的全过程。在Webkit 解析HTML文件,生成DOM Tree 和Render Tree 的过程中,当解析到 <img onclick="..." src="..."> 这一句的时候,生成DOM Tree中的 HTMLElement 节点,以及Render Tree 中 RenderImage 节点。如前文所述。在生成HTMLElement 节点的过程中,因为注意到有onclick属性,Webkit决定需要给 HTMLElement 节点绑定一个 EventListener,参见Figure 3 中第7步。

Webkit 把所有EventListener 的创建工作,交给Document 统一处理,类似于 Design Patterns中,Singleton 的用法。也就是说,DOM Tree的根节点 Document,掌握着这个网页涉及的所有EventListeners。 有趣的是,当Document 接获请求后,不管针对的是哪一类事件,一律让代理器 (kjsProxy) 生成一个JSLazyEventListener。之所以说这个实现方式有趣,是因为有几个问题需要特别留意,

1. 一个HTMLElement节点,如果有多个类似于onclick的事件属性,那么就需要多个相应的EventListener object instances与之绑定。

2. 每个节点的每个事件属性,都对应一个独立的EventListener object instance。不同节点不共享同一个 EventListener object instance。即便同一个节点中,不同的事件属性,对应的也是不同的EventListener object instances。

   这是一个值得商榷的地方。不同节点不同事件对应彼此独立的EventListener object instances,这种做法给不同节点之间的信息传递,造成了很大障碍。反过来设想一下,如果能够有一种机制,让同一个object instance,穿梭于多个HTMLElement Nodes之间,那么浏览器的表现能力将会大大增强,届时,将会出现大量的前所未有的匪夷所思的应用。

3. DOM Tree的根节点,Document,统一规定了用什么工具,去解析事件属性的值,以及执行这个属性值所定义的事件处理逻辑。如前文所述,事件属性的值,分成HTML DOM methods 和JavaScript 两类。但是不管某个HTMLElement节点的某个事件属性的值属于哪一类,Document一律让 kjsProxy代理器,生成一个 EventListener。

   看看这个代理器的名字就知道,kjsProxy生成的 EventListener,一定是依托JavaScriptCore Engine,也就是以前的KJS JavaScript Engine,来执行事件处理逻辑的。核实一下源代码,这个猜想果然正确。

4. 如果想把JavaScriptCore 替换成其它JavaScript Engine,例如Google的V8,不能简单地更改configuration file,而需要修改一部分源代码。所幸的是,Webkit的架构设计相当清晰,所以需要改动部分不多,关键部位是把Document.{h,cpp} 以及其它少数源代码中,涉及kjsProxy 的部分,改成其它Proxy即可。

5. kjsProxy 生成的EventListener,是JSLazyEventListener。解释一下JSLazyEventListener 命名的寓意,JS容易理解,意思是把事件处理逻辑,交给JavaScript engine 负责。所谓 lazy 指的是,除非用户在照片显示区域点击了鼠标,否则,JavaScript Engine 不主动处理事件属性的值所规定的事件处理逻辑。

   与 lazy做法相对应的是JIT即时编译,譬如有一些JavaScript Engine,在用户尚没有触发任何事件以前,预先编译了所有与该网页相关的JavaScript,这样,当用户触发了一个特定事件,需要调用某些 JavaScript functions时,运行速度就会加快。当然,预先编译会有代价,可能会有一些JavaScript functions,虽然编译过了,但是从来没有被真正执行过。



 Figure 4. The event handling of Webkit
Courtesy http://farm4.static.flickr.com/3390/3640149730_0c98f0218d_b.jpg

当解析完HTML文件,生成了完整的DOM Tree 和Render Tree 以后,Webkit就准备好去响应和处理用户触发的事件了。响应和处理事件的整个流程,如Figure 4所描述。整个流程分成两个阶段,

1. 寻找 EventTargetNode。

   当用户触发某个事件,例如点击鼠标,根据鼠标所在位置,从Render Tree的根节点开始,一路搜索到鼠标所在位置对应的叶子节点。Render Tree根节点对应的是整个浏览器页面,而叶子节点对应的区域面积最小。

   从Render Tree根节点,到叶子节点,沿途每个Render Tree Node,都对应一个DOM Tree Node。这一串DOM Tree Nodes中,有些节点响应用户触发的事件,另一些不响应。例如在本文的例子中,<body> tag 对应的DOM Tree Node,和第一张照片的<img> tag 对应的DOM Tree Node,都对onclick事件有响应。

   第一阶段结束时,Webkit得到一个EventTargetNode,这个节点是一个DOM Tree Node,而且是对事件有响应的DOM Tree Node。如果存在多个DOM Tree Nodes对事件有响应,EventTargetNode是那个最靠近叶子的中间节点。

2. 执行事件处理逻辑。

   如果对于同一个事件,有多个响应节点,那么JavaScript Engine 依次处理这一串节点中,每一个节点定义的事件处理逻辑。事件处理逻辑,以字符串的形式定义在事件属性的值中。在本文的例子中,HTML文件包含<img onclick="myfunction('World')">,和<body onclick="myfunction('Hello')">,这意味着,有两个DOM Tree Nodes 对onclick事件有响应,它们的事件处理逻辑分别是myfunction('World') 和myfunction('Hello'),这两个字符串。

   当JavaScript Engine 获得事件处理逻辑的字符串后,它把这个字符串,根据JavaScript的语法规则,解析为一棵树状结构,称作Parse Tree。有了这棵Parse Tree,JavaScript Engine就可以理解这个字符串中,哪些是函数名,哪些是变量,哪些是变量值。理解清楚以后,JavaScript Engine 就可以执行事件处理逻辑了。本文例子的事件处理过程,如Figure 4中第16步,到第35步所示。

   本文的例子中,“myfunction('World')" 这个字符串本身并没有定义事件处理逻辑,而只是提供了一个JavaScript函数的函数名,以及函数的参数的值。当JavaScript Engine 得到这个字符串以后,解析,执行。执行的结果是得到函数实体的代码。函数实体的代码中,最重要的是alert(v) 这一句。JavaScript Engine 把这一句解析成Parse Tree,然后执行。

   注意到本文例子中,对于同一个事件onclick,有两个不同的DOM Tree Nodes 有响应。处理这两个节点的先后顺序要么由capture path,要么由bubbling path决定,如Figure 5所示。(Figure 5中对应的HTML文件,不是本文所引的例子)。在HTML文件中,可以规定event.bubbles属性。如果没有规定,那就按照bubbling的顺序进行,所以本文的例子,是先执行<img>,弹出“World” 的窗口,关掉“World”窗口后,接着执行<body>,弹出“Hello” 的窗口。



 
Figure 5. The capture and bubbling of event by the DOM tree.
Courtesy http://www.w3.org/TR/DOM-Level-3-Events/images/eventflow.png


这一节比较枯燥,因为涉及了太多的源代码细节。之所以这么不厌其烦地说明细节,是为了解决如何更有效率地处理事件,以及提供更丰富的手段去处理事件。待续。

  • 大小: 10.6 KB
  • 大小: 63.5 KB
  • 大小: 98.4 KB
  • 大小: 116.9 KB
  • 大小: 35 KB
分享到:
评论

相关推荐

    java集成webkit浏览器

    Java集成WebKit浏览器是一种技术实践,它允许Java应用程序利用WebKit渲染引擎来展示网页内容。WebKit是一个开源的Web浏览器引擎,被广泛应用于Safari、Chrome等知名浏览器。在Java中集成WebKit,开发者可以创建具有...

    WebKit.NET-0.5-Chrome的内核WebKit的NET版.zip

    WebKit.NET-0.5-Chrome的内核WebKit的NET版.zip是一个包含WebKit.NET 0.5版本的压缩包,这个项目旨在为.NET开发者提供一个基于Chrome浏览器内核(WebKit)的.NET接口。WebKit是一个开源的网页渲染引擎,广泛应用于...

    基于Webkit的Web开发

    `-webkit-scrollbar`等属性可以自定义浏览器的滚动条样式,`cursor`定义鼠标指针形状,`-webkit-tap-highlight-color`控制元素在触屏设备上被点击时的高亮颜色。 通过这些WebKit特有的CSS扩展,开发者能够实现更为...

    转:webkit开发学习笔记(全面)_第一版

    **WebKit 开发学习笔记概述** WebKit 是一个开源的网页内容渲染引擎,被广泛应用于各种浏览器,包括谷歌的 Chrome、苹果的 Safari、以及许多其他双核浏览器。WebKit 的诞生源自苹果公司在其 Mac OS X 操作系统中对 ...

    WebKit_vbwebkit_VBWebKit_webkit

    **WebKit、VBWebKit与Webkit:核心技术解析** WebKit是一个开源的网页渲染引擎,它负责解释HTML和CSS,处理JavaScript,以及呈现网页内容。这个引擎被广泛应用于许多操作系统和应用程序,包括苹果的Safari浏览器和...

    WebKit技术内幕朱永盛.pdf

    **WebKit技术内幕——深入探索网页渲染引擎** WebKit是一款开源的网页渲染引擎,广泛应用于苹果的Safari、谷歌的Chrome等知名浏览器中。它负责解析HTML、CSS、JavaScript,并将它们转化为用户可见的网页内容。...

    Webkit_webkit浏览器_webkit_android_

    这个"Webkit_webkit浏览器_webkit_android_"的项目是针对初学者设计的,旨在帮助他们理解如何在Android平台上利用Webkit技术构建一个简单的web浏览器应用。 首先,我们要了解Webkit的核心功能。Webkit负责解析HTML...

    WebKit.NET-0.5-bin-cairo

    WebKit.NET-0.5-bin-cairo 是一个专为.NET开发者设计的开源项目,它提供了对WebKit浏览器内核的.NET封装,使得.NET程序员能够方便地在自己的应用程序中集成和使用WebKit来展示和操作网页内容。WebKit是一个强大的、...

    webkit浏览器演示源码 vb.net vs2010

    【webkit浏览器演示源码 vb.net vs2010】是一个基于Visual Studio 2010和VB.NET编写的项目,旨在实现一个简单的WebKit浏览器。WebKit是一个开源的Web渲染引擎,广泛应用于各种操作系统和应用程序中,包括苹果的...

    webkit 内核介绍文档入门

    【Webkit内核探究】Webkit是开源的Web浏览器引擎,它的应用广泛,包括Apple的Safari、Google的Chrome、Nokia S60平台的默认浏览器以及Apple和Android手机的默认浏览器。作为三大主流浏览器内核之一,Webkit与Gecko...

    windows上webkit的编译过程.pdf

    Windows 上 WebKit 的编译过程 在本篇文章中,我们将详细介绍 Windows 上 WebKit 的编译过程。整个过程可以分为 several 部分,包括安装开发环境、下载 WebKit 源代码、编译 WebKit 等。 一、安装开发环境 在开始...

    Webkit内核,含导入WebKit.Interop.dll

    Webkit内核是一种开源的网页渲染引擎,广泛用于各种浏览器和操作系统中,如Apple的Safari和Google的Chrome早期版本。这个内核负责解析HTML、CSS、JavaScript等Web标准,呈现网页内容,并处理用户交互。在.NET环境中...

    WebKit.Interop_浏览器源码_webkit_

    WebKit 是一个开源的浏览器引擎,与之相对应的引擎有Gecko(Mozilla Firefox 等使用),Trident(也称MSHTML,IE 使用)和EdgeHTML(也称Chakra,Edge和其他UWP浏览器使用)。同时WebKit 也是苹果Mac OS X 系统引擎...

    smashing webkit

    **WebKit:Web渲染引擎的深度探索** WebKit 是一个开源的 Web 渲染引擎,它负责解释 HTML 和 CSS、处理 JavaScript 以及渲染网页内容。在 Web 开发领域,WebKit 是一个至关重要的组件,广泛应用于 Safari、Chrome ...

    WebKit源代码结构

    ### WebKit源代码结构解析 #### 一、概述 WebKit是一款开源浏览器引擎,其源代码规模庞大,超过了500万行。尽管如此,WebKit的目录结构却异常清晰,这有助于开发者快速理解其架构和功能模块。本文将详细介绍WebKit...

    c# webkit 内核浏览器简例

    【C# WebKit 内核浏览器简例】 在IT领域,C#是一种广泛使用的编程语言,尤其在构建Windows应用程序和Web服务时。WebKit是开源的网页渲染引擎,它被许多知名浏览器如Safari和Chrome所采用,以提供高效且标准兼容的...

    易语言调webkit内核多页面浏览器

    转webkit内核多页面浏览器,自动填表功能还没实现!请大家测试修改

    C#WinFrom窗体集成Webkit内核 webkit.net包+教程+源码 全包

    C#WinFrom窗体集成Webkit内核 webkit.net包+教程+源码 全包

    WebKit.NET

    WebKit.NET 是一个强大的开源项目,它允许C#开发者在他们的应用程序中嵌入Web浏览器功能。这个框架基于WebKit引擎,这是一个广泛用于苹果Safari和谷歌Chrome等流行浏览器的渲染引擎。WebKit.NET通过提供C#接口,使得...

Global site tag (gtag.js) - Google Analytics