《WebKit 技术内幕》学习之十(1): 插件与JavaScript扩展

news2024/12/22 13:14:14

        虽然目前的浏览器的功能很强 ,但仍然有其局限性。早期的浏览器能力十分有限,Web前端开发者希望能够通过一定的机制来扩展浏览器的能力。早期的方法就是插件机制,现在流行次啊用混合编程(Hybird Programming)模式。插件一直伴随着浏览器的发展,最著名莫过于Adobe公司的Flash插件。对于插件的接口定义,差别也很大,比较著名的是微软公司的ActiveX插件机制和网景公司 的NPAPI产检。随后,Chromium项目考虑到性能引入PPAPI插件机制,同时为了安全方面的考虑,引入NativeClient机制。这些插件机制扩展了浏览器的能力,极大地丰富了网页的应用场景,同时,随着HTML5的发展,很多HTML5功能同样需要扩展JavaScript的编程接口,以便开发者可以使用JavaScript代码来调用,而这样的扩展就需要相应的机制来实现。

1 NPAPI插件

1.1 NPAPI简介

        NPAPI(Netscape Plugin Application Programming Interface)的全称是网景插件应用程序编程接口,最早是由网景公司提出的,用于让浏览器执行外部程序,以支持网页中各种格式的文件,典型的例子是视频、音频和PDF文件等(通过内容类型来区分)。对于这些网络资源或者文件,浏览器本身并不支持它们。但是,经过第三方开发者开发的插件程序,浏览器就可以做到支持了。图10-1是Chrome浏览器使用NPAPI插件的列表中一个示例(在地址栏中输入chrome://plugins/就可以查看到所有插件)。当遇到上述格式PDF文件的时候,Chrome浏览器会调用该阅读器插件,通过NPAPI规范定义的接口使浏览器同插件之间得以交互。

                     图10-1 Chrome浏览器中Adobe阅读器插件

        现实中,NPAPI机制被广泛地应用,很多厂商或者开发者基于该接口规范编写了数量众多的插件实现,因而Chromium项目也必须对它提供支持,不过Chromium还有自己独特的插件架构,后面会详细介绍。使用插件的方法也非常简单,在网页中申明如下语句即可,它表示使用上述插件来打开一个PDF文件并显示在网页中:

    <embed id="plugin" type="application/pdf" src="src/abc.pdf">

        NPAPI提供两组接口,一类以NPP开始,由插件来实现,被浏览器调用,主要包括一些插件创建、初始化、关闭、销毁、信息查询及事件处理、数据流、窗口设置、URL等。另一类以NPN开始,由浏览器来实现,被插件所调用,主要包括图形绘制、数据流处理、浏览器信息查询、内存分配和释放、浏览器的插件设置、URL等。这两类接口足够满足大多数双方交互的需求。

        原始的NPAPI接口使用起来不是很方便,因而有开发者对其进行了封装以便于其使用。一个比较著名的开源项目是Firebreath。它将原始C风格的NPAPI接口封装成C++风格的接口,非常方便用户使用,而且有针对Windows和X Window的移植,用户无须对底层接口特别了解。更为有趣的是,Firebreath也有对ActiveX接口规范的封装,因而对于现在主流的两种插件接口,开发者都可以基于Firebreath的接口进行编程,极大地增强了移植性和通用性。详情请参考Firebreath项目的主页,网址如下所示:http://www.firebreath.org/display/documentation/FireBreath+Home。

        下面主要介绍WebKit和Chromium中是如何支持插件机制的,所以上面的使用Firebreath等项目开发插件的实现不在本书的范围内,有兴趣的读者请自行学习。

1.2 WebKit和Chromium的实现

1.2.1 WebKit基础设施

NPAPI插件获得了WebKit的支持,因为它的广泛使用性。在HTML网页中,可以通过两种类型的元素“embed”和“object”来使用插件。两者都可以用来在网页中内嵌插件,看起来“embed”元素更老一些,之前的一些浏览器只支持“embed”而不支持“object”,不过在WebKit中,两者都得到了支持,一个简单的例子如“<embed src='webkit.pdf'/>”。那么,WebKit中是如何支持它们的呢?

        图10-2给出WebKit中支持插件机制所使用的类及其结构,初看起来比较复杂和杂乱无章,那么就分成左、中、右三个部分分别介绍它们。左边部分就是表示插件元素在DOM树和RenderObject树中的节点类,因为有两种HTML元素可以表示插件,所以为它们抽象出来了一个基类。对于插件元素在DOM树中的对应节点,RenderObject树中对应就是RenderWidget对象,用于表示这是个可视化的元素。在某些WebKit移植中,甚至引入了硬件加速机制来加速插件的绘制,例如WebKit的Qt移植。它的基本思想是将插件元素作为单独的一个层(PlatformLayer)来处理,插件的实例将绘制所有内容在这一层上,就像视频元素一样。

                                        图10-2 WebKit中支持插件的相关类

图中右侧部分表示的是WebKit如何管理插件库,主要使用两个类:

  • PluginDatabase :注册和管理所有的插件实现,一个插件通常是一个动态库,插件的信息包括名字、描述、版本,还有最重要的MIME类型和文件的扩展名(File extensions),例如图10-1中的PDF插件能够支持MIME类型“application/pdf”和扩展名“.pdf”。当然,一个插件也可以支持多种类型的文件。同时,它能够根据MIME类型和文件扩展名来查找相应的插件库。
  • PluginPackage :表示一个插件库,也就是PluginDatabase类管理的对象。它包含两个非常重要的变量,就是m_pluginFuncs和m_browserFuncs,对应的就是前面介绍的NPP开头的函数组和NPN开头的函数组。

        在中间部分的表示是插件的视图部分,它和DOM元素或者RenderWidget对象一一对应,其作用当然是绘制插件的可视化结果,同时它需要调用最右侧的类来获取插件。

  • NPP :使用PluginPackage的接口来创建的插件实例。
  • PluginViewBase :抽象类,主要是定义一些接口,这些接口会被HTMLPlugIn- Element类调用,用来处理视图方面的一些操作,如鼠标、聚焦(focus)等。
  • PluginView :表示的是一个插件的视图,它非常重要,连接了插件库和网页中DOM接口和可视化RenderObject节点,包含所需的插件库和插件实例。
  • NPObject :表示的是插件和浏览器(这里是WebKit)之间数据的交互类型,因为插件能够访问DOM树和JavaScript对象,所以JavaScript中的基本类型和JavaScript对象都会包装成NPObject来在两者之间传递。

        对于PluginDataBase、PluginPackage和Pluginview类,在不同的移植中,它们可能会需要一些不同的实现,所以移植通常可能会扩展它们,当然主要工作逻辑可以共享。对于WebKit的Chromium移植来说,它的实现更为复杂,在下一节详细介绍。

        对于插件机制,有几个问题要回答,第一是插件库的注册、查找等管理机制。第二是WebKit中的插件节点的处理,包括DOM树和RenderObject树如何支持插件。第三是如何使用注册的插件来创建插件示例并绘制需要的结果到网页最终结果中去。下面我们来具体说明一下。

        首先是插件库管理机制。管理的基础是MIME类型和文件扩展名,例如对于“<embed src='webkit.pdf'/>”这样的例子,PluginView类会将“.pdf”文件扩展名当作参数传递给PluginDatabase并期待返回一个PluginPackage对象。对于某个MIME类型,当出现多个插件支持的时候,管理机制需要决定如何选择它们。

        第二是插件节点的处理。当网页中出现“embed”和“object”元素的时候,WebKit会首先创建HTMLPlugInElement(应该是它的子类)对象,之后需要创建RenderWidget节点,当出现硬件加速机制的时候,可能还需要创建相应的RenderLayer节点。同时,还要创建PluginView对象,并根据DOM元素的属性来查找并创建相应的实例。

        第三是绘图工作。本身NPAPI没有提供绘图的接口,只是让插件将绘制完的结果传给浏览器或者提供一个绘制的目标存储结构,从而让插件直接在它上面绘制,这就是插件的Window和Windowless模式,关于这两种模式,后面还会做介绍。另外一个方面是跟浏览器交互以通知某些区域需要重绘等消息。

        虽然插件机制是用来支持“object”或者“embed”元素,但是,该机制也能够扩展JavaScript中对象和对象的方法,例如希望在JavaScript中增加W3C组织定义的一些标准接口,如设备相关的对象和方法。

        NPAPI插件虽然功能强大,但是,通常它是浏览器不稳定的重要原因之一,这是因为插件由各个厂家自行维护,质量和稳定性也千差万别,插件的不稳定通常会导致浏览器的不稳定,这在现在多页面同时浏览的模式下会带来非常差的用户体验。同时,NPAPI的性能不是很高效,而且存在一些局限性,特别是绘图方面。最后,NPAPI插件拥有访问任何本地资源的能力,这会带来安全性问题,所有未经过认证的插件都非常危险,随意使用第三方插件的网页也不无可能对系统造成灾难性的后果。这与ActiveX插件很像,它同样也是很多病毒攻击的对象。因为插件通常是网络攻击的对象,一旦这些插件被攻击成功,那么攻击者就能够随意访问本地资源。

        在WebKit的这种插件设计架构中,渲染引擎同插件的运行通常在同一进程中,这一设计将会带来稳定性和安全性方面的灾难性后果。为了避免这些方面的问题,Chromium在WebKit/Blink插件架构的基础上引入了跨进程的插件机制,这为浏览器的稳定性提供了保证,下一小节将详细介绍。同时,考虑到性能方面的问题,Google提出了新的PPAPI插件机制,考虑到安全性和支持本地代码的问题,Chromium引入了Native Client机制,为安全性提供了保证,这在后面也会作详细介绍。

1.2.2 Chromium的插件架构

        为了解决插件的稳定性问题,同时因为Chromium的沙箱模型机制(第12章会介绍,它会限制插件访问本地资源的能力),插件实例不能够在Renderer进程中运行,因为除了访问IO之外,没有访问其他接口和资源的能力,所以在Chromium中,插件是被放在单独的进程中来执行,这就是Chromium的插件多进程模型。图10-3显示的是Chromium的插件进程示例图。

                              图10-3 Chromium的插件多进程模型

        在Chromium中,每一个插件库只会有一个进程,这就是说,如果有两个或者多个Renderer进程同时使用同一个插件库,那么这些Renderer进程会共享同一个插件进程。因为多个Renderer进程共享同一种的Plugin进程,那么Plugin进程如何为它们服务呢?答案是Chromium在加载插件库后为每个插件使用点在plugin进程中创建一个对应插件实例(PluginInstance)。

        值得注意的是,插件进程是由Browser进程来负责创建和销毁,而不是Renderer进程。原因在于Renderer进程没有创建的权限,而且Plugin进程也应该由Browser进程来统一管理,这样也更方便。当Plugin进程创建成功时,Browser进程会返回进程间通信的句柄,用于创建和Plugin进程通讯的PluginChannelHost。那它什么时候被销毁呢?当没有任何插件实例并且空闲一段事件后,它才会被销毁,这样做的好处是避免频繁地创建和销毁Plugin进程。

        图10-4描述了Browser进程和Plugin进程间的通信机制及其所涉及的相关的模块(类)。Browser进程通过PluginProcessHost发送消息调用Plugin进程的函数,响应动作由PluginThread完成。而Plugin进程则是通过WebPluginProxy发送消息调用Browser进程的响应函数,响应动作由PluginProcessHost完成。

                                图10-4 Brower进程和Plugin进程的交互过程

        Browser进程和Plugin进程仅有较少的消息传递,用于插件的创建等管理工作。其实,主要的工作在Renderer进程和Plugin进程之间,机制也相对更为复杂一些。根据前面介绍,HTMLPluginElement节点是DOM树中的一个节点,在Chromium的实现中会包含一个WebPluginContainerImpl,该节点是WebKit::Widget的子类,也就是Chromium中的一个对PluginView的具体实现类,而它包含一个WebPluginImpl,对plugin的调用有WebPluginDelegateProxy负责中转。在Plugin进程中,由WebPluginDelegateStub处理所有Renderer进程发送过来的请求,并由WebPlugin-DelegateImpl调用创建好的PluginInstance对象。PluginInstance最终调用PluginLib读取的插件库(libxxx.so)的各个函数入口地址,最终完成对插件库实现的调用。而对插件实现中对NPN开头函数的调用,则是通过PluginHost来完成。

        PluginHost主要负责实现NPN开头的函数,如前面所描述,这些函数被plugin进程所调用。可以在plugin和renderer进程被调用。当在plugin进程调用这些函数时,chromium会覆盖PluginHost的部分函数,而这些新的callback函数会调用NPObjectProxy来通过IPC发送请求到renderer进程。

        PluginInstance实现了NPP开头的函数,被Renderer进程所调用(WebPluginImpl通过WebPluginDelegateImpl来调用),PluginInstance通过PluginLib获得了插件库中这些函数的地址,从而把实际的调用桥接到具体的插件中。具体的如图10-5所示,主要结构来源于Chromium项目的官方网站,略有修改。

                         图10-5 Chromium的跨进程插件和Renderer进程交互过程

        对于NPObject相关的函数调用,有专门的类来处理。NPObject的调用或者访问是双向的(renderer进程<->plugin进程),他们的具体实现是通过NPObjectProxy和NPObjectStub来完成。NPObjectProxy接受来自对方的访问请求,转发给NPObjectStub,最后NPObjectStub调用真正的NPObject并返回结果,如图10-6所示。

                                        图10-6 NPObject对象的跨进程使用

1.2.3 Chromium插件的工作过程

        插件工作过程主要是创建并完成插件和浏览器的交互过程。首先来看一下插件实例是如何被创建的,图10-7给出一个插件如何被Renderer进程触发创建的过程。

                        图10-7 Renderer进程创建插件实例的过程

        如果页面中包含一个“embed”或者“object”元素,Renderer进程会创建一个HTMLEmbedElement元素,当该元素被JavaScript代码或者其他地方使用的时候,会触发创建相应的插件。HTMLEmbedElement对象会请求创建自己对应的RenderWidget(WebPluginContainerImpl),进而创建WebPluginImpl和WebPluginDelegate-Proxy。如果该插件的进程还不存在,WebPluginDelegateProxy会发送消息到Browser进程,请求该进程来创建Plugin进程。Plugin进程被Browser进程创建后,会响应Renderer进程的请求来创建PluginInstance并将它初始化,这样它们之间的联系就建立好了。注意,图中的WebPluginDelegateProxy类调用的操作“创建和初始化PluginInstance”,是通过进程间通信发送消息到Plugin进程,最终由该进程完成的。

        接下来的工作主要是浏览器和插件通过NPP和NPN接口进行互相调用,这些调用在Chromium浏览器中都通过IPC机制来完成,具体的过程就是使用图10-5所描述类的调用过程。

1.2.4 Window和WindowIess插件

         根据规范,可以通过设置“embed”或者“object”元素的属性来让浏览器来决定如何提供绘制结果的存储方式。Window模式插件由Renderer进程提供一个窗口(window),插件直接在该窗口上进行绘制,所以它不需要和网页的内容再进行合并,而是一个独立的绘制目标。而Windowless模式的插件则不同,插件将绘制的结果(如Pixmap)通过共享内存的方式(如Transport DIB)传递给Renderer进程,Renderer进程然后绘制该内容到自己内部的存储结构(Backing Store)上。

         从上面的论述不难看出,Window模式的性能是要高于Windowless的。但是,对于Window模式的插件来说,它不能跟网页的内部内容构成很好的前后关系,例如在网页的某些元素之后,某些元素之前,这不得不说是一个局限。而对于Windowless模式的插件来说,性能较差的问题带来的好处是,可以把插件绘制的结构和网页上的其他内容做各种形式的合成。

        跨进程带来稳定性的同时,由于访问对象和操作都需要经过进程间通信,所以额外的负担也比较重。为此,Chromium的PPAPI插件机制诞生。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1403564.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小微企业科技创新之策略:人才、投入、模式、技术、支持四管齐下

对于小微企业来说&#xff0c;搞科技创新需要从多个方面入手。以下是一些可供参考的方法&#xff1a; 明确创新方向&#xff1a;首先&#xff0c;企业需要明确自己的创新方向和目标&#xff0c;这有助于聚焦资源&#xff0c;避免盲目投入。同时&#xff0c;企业需要对市场进行…

charles使用指南

一、什么是charles Charles 是一个http代理、管理以及反向代理工具&#xff0c;它允许开发者查看本地机器和互联网之间的关于http、https的所有通信&#xff0c;包含请求、响应以及他们的请求头、响应头。 它的主要功能包含&#xff1a; 1、SSL代理 2、带宽限制 3、ajax断…

如何查看Linux CPU占有率

目录 1、top 2、htop 3、vmstat 4、mpstat 5、iostat 查看嵌入式设备CPU占有率是评估系统资源使用情况的重要方式。 在Linux系统中&#xff0c;有多种方法可以查看CPU占有率&#xff0c;这里介绍几种常用的命令行工具。 1、top 这是最常用的命令之一&#xff0c;它提供了…

go语言(十三)-----interface

一、Interface 通用万能类型 空接口int&#xff0c;string&#xff0c;float&#xff0c;struct都实现了interface都可以用interface{}类型,引用任意的数据类型 package mainimport "fmt"//interface()是万能数据类型 func myFunc(arg interface{}) {fmt.Println(&…

[娱乐]索尼电视安装Kodi

索尼电视不能直接apk安装kodi应用 android studio安装后附带 abd&#xff0c; 路径 C:\Users\[yourname]\AppuoData\Local\Android\Sdk\platform-tools\adb.exe安卓电视点击内部版本号&#xff0c;启用开发者模式 adb 连接索尼安卓电视&#xff0c;记得电视上运行调试 abi选…

函数传参数组时,使用数组形参的本质

c语言中函数的调用分为两种方式&#xff1a;传址调用、传值调用。 传值调用我们都知道就是将实参的值传送给被调函数&#xff0c;让被调函数的形参接收这个值&#xff0c;从而形参内存中的数据就变成了实参的一份拷贝。 而传址调用则是将实参的地址传送过去&#xff0c;然后令…

IaC基础设施即代码:Terraform 创建 docker 网络与容器资源

目录 一、实验 1.环境 2.Terraform查看版本 3.Linux主机安装Docker 4.Terraform使用本地编译&#xff08;In-house&#xff09;的Providers 5.Docker-CE 开启远程API 6. Linux主机拉取镜像 7.Terraform 创建docker 网络资源 8.Terraform 创建docker 容器资源 一、实验 …

8.3 Springboot整合Redis 之Jedis方式

文章目录 前言一、Maven依赖二、新增子Module:tg-book-redis三、Jedis配置类3.1 Jedis连接池核心配置说明四、Jedis 工具类五、新增controller测试前言 Jedis是Redis官方推荐的Java客户端连接工具,用法非常简单,Jedis的API与Redis的API可以说是一模一样,所以非常有利于熟悉…

【K8S 云原生】K8S的包包管理器-helm

目录 一、helm概念 1、什么是helm 2、helm的概念&#xff1a; 二、实验部署&#xff1a; 1、安装helm&#xff1a; 2、对chart仓库的基本使用&#xff1a; 2.1、查看和更新chart仓库 2.2、安装chart 2.3、卸载chart&#xff1a; 3、helm自定义模版&#xff1a; 3.1、…

未来趋势:视频美颜SDK与增强现实(AR)的融合

当下&#xff0c;视频美颜SDK不断演化&#xff0c;成为用户记录和分享生活时不可或缺的一部分。同时&#xff0c;增强现实技术也以其独特的沉浸感和交互性受到青睐&#xff0c;被广泛应用于游戏、教育、医疗等领域。 一、视频美颜与AR的结合 1.实时美颜的AR增值体验 借助AR的…

【Springboot】日志

1.日志的使用 日志主要用于记录程序运行的情况。我们从学习javase的时候就使用System.out.println();打印日志了&#xff0c;通过打印的日志来发现和定位问题&#xff0c;或根据日志来分析程序运行的过程。在Spring的学习中,也经常根据控制台的⽇志来分析和定位问题 。 日志除…

《WebKit 技术内幕》学习之七(1): 渲染基础

《WebKit 技术内幕》之七&#xff08;1&#xff09;&#xff1a; 渲染基础 WebKit的布局计算使用 RenderObject 树并保存计算结果到 RenderObject 树。 RenderObject 树同其他树&#xff08;如 RenderLayer 树等&#xff09;&#xff0c;构成了 WebKit 渲染的为要基础设施。 1…

w23靶场安装

一、实验环境 服务器&#xff1a;phpstudyv8.1.13 靶场&#xff1a;Bees二、实验目的 提供一个靶场环境 三、实验步骤 bees靶场安装 1.启动小皮的apache和mysql 2.在小皮V8.1.1.3版本上创建bees网站&#xff0c;选择的php版本最好在5.x&#xff0c;不然会有php解析错误。…

Windows系统如何修改Nginx配置实现远程访问多个本地站点

文章目录 1. 下载windows版Nginx2. 配置Nginx3. 测试局域网访问4. cpolar内网穿透5. 测试公网访问6. 配置固定二级子域名7. 测试访问公网固定二级子域名 1. 下载windows版Nginx 进入官方网站(http://nginx.org/en/download.html)下载windows版的nginx 下载好后解压进入nginx目…

samba服务搭建,并将共享目录映射到windows

系统版本&#xff1a;centos7 1、centos 安装samba yum -y install samba 2、查看安装信息 rpm -qa |grep samba 3、设置开机自启动 systemctl enable smb.service systemctl enable nmb.service 4、设置samba服务器配置文件 sudo vi /etc/samba/smb.conf 注意&#…

mysql进阶-执行计划

目录 1. 概念 2. 使用 3. 具体相关字段含义 3.1 id 3.2 select_type 3.3 table 3.4 partition 3.5 type 3.6 possible_key 3.7 key 3.8 key_len 3.9 ref 3.10 row 3.11 filtered 3.12 extra 1. 概念 一条语句通过优化器之后&#xff0c;会生成具体的执行计划用…

qt-c++多窗口编程

1、QMessageBox 消息对话框 QMessageBox继承自QDialog&#xff0c;显示一个模态对话框。用于用户前台信息通知或询问用户问题&#xff0c;并且接收问题答案。 QDialog再Qt源码中&#xff0c;派生类往往都是一些在特定场合下使用的预设好的对话框窗口。这些窗口无需创建对象&…

PaddleDetection学习3——使用Paddle-Lite在 Android 上部署PicoDet模型(fp16)

使用Paddle-Lite在 Android 上运行PicoDet模型&#xff08;fp16&#xff09; 1. 环境准备2. 部署步骤2.1 下载Paddle-Lite-Demo2.2 打开 picodet_detection_demo项目2.2.1 修改build.gradle&#xff0c;配置国内镜像仓库2.2.2 NDK 配置错误问题2.2.3 gradle.properties文件配置…

PaddleOCR实现对表格的提取

1、背景 PaddleOCR: 基于飞桨的OCR工具库&#xff0c;包含总模型仅8.6M的超轻量级中文OCR&#xff0c;单模型支持中英文数字组合识别、竖排文本识别、长文本识别。同时支持多种文本检测、文本识别的训练算法。对pdf/word里的表格中识别出有效信息具有重要意义&#xff0c;比如…

OpenSource - 文件在线预览模块(多格式转 PDF 文件)

文章目录 文件在线预览模块&#xff08;多格式转PDF文件&#xff09;现已支持格式如下界面展示运行方式接口介绍文件上传文件转 PDF文件转图片文件转SVG 参数配置其他说明项目关联关键词文档转换预览技术说明同步转换异步转换 主要技术乱码问题处理帮助文档 前端预览弹出层用法…