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

        虽然目前的浏览器的功能很强 ,但仍然有其局限性。早期的浏览器能力十分有限,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.mfbz.cn/a/341009.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

transformer优化(二)-DETR 学习笔记

论文地址&#xff1a;https://arxiv.org/pdf/2005.12872.pdf 代码地址&#xff1a;https://github.com/bubbliiiing/detr-pytorch https://github.com/facebookresearch/detr 1.是什么&#xff1f; DETR&#xff08;Detection Transformer&#xff09;是一种基于Transforme…

rocketmq4.9.4有bug,生产勿用

周一早上,领导发来消息:xxxx用户:您有服务器因攻击被限制访问部分目的端口&#xff0c;详细信息请看https 排查了服务器上的异常进程后,与阿里云沟通得知,被人利用rocketmq的漏洞攻击了 服务器中毒的原因 您服务器中运行的 rocketmq 是 4.9.4 版本&#xff0c;该版本存在远程…

【.NET Core】多线程之线程池(ThreadPool)详解(一)

【.NET Core】多线程之线程池&#xff08;ThreadPool&#xff09;详解&#xff08;一&#xff09; 文章目录 【.NET Core】多线程之线程池&#xff08;ThreadPool&#xff09;详解&#xff08;一&#xff09;一、概述二、线程池的应用范围三、线程池特性3.1 线程池线程中的异常…

旅游项目day14

其他模块数据初始化 搜索实现 请求一样&#xff0c;但是参数不一样&#xff0c;根据type划分。 后台需要提供一个搜索接口。 请求分发器&#xff1a; 全部搜索 目的地搜索 精确搜索、无高亮展示 攻略搜索 全文搜索、高亮显示、分页 游记搜搜 用户搜索 丝袜哥

Python实现Lasso回归模型

• Tibshirani(1996)提出了Lasso(The Least Absolute Shrinkage and Selectionator operator)算法。 • 通过构造一个一阶惩罚函数获得一个精炼的模型&#xff1b;通过最终确定一些指标&#xff08;变量&#xff09;的系数为零&#xff08;岭回归估计系数等于0的机会微乎其微&a…

Mysql--创建数据库(1)

我们可以在登陆 MySQL 服务后&#xff0c;使用 create 命令创建数据库&#xff0c;语法如下: CREATE DATABASE 数据库名;以下命令简单的演示了创建数据库的过程&#xff0c;数据名为 R OB: [roothost]# mysql -u root -p Enter password:****** # 登录后进入终端mysql>…

什么是美颜SDK:解密视频美颜SDK背后的图像处理算法

美颜SDK是一种集成到应用程序中的工具包&#xff0c;能够通过图像处理算法实时改善或修改用户的面部外观。在本文中&#xff0c;我们将深入探讨美颜SDK的基本概念&#xff0c;并解密其背后的图像处理算法。 一、美颜SDK的基本概念 美颜SDK的应用范围广泛&#xff0c;涵盖了视…

《统计学习方法:李航》笔记 从原理到实现(基于python)-- 第1章 统计学习方法概论

文章目录 第1章 统计学习方法概论1.1 统计学习1&#xff0e;统计学习的特点2&#xff0e;统计学习的对象3&#xff0e;统计学习的目的4&#xff0e;统计学习的方法1.2.1 基本概念1.2.2 问题的形式化 1.3 统计学习三要素1.3.1 模型1.3.2 策略1.3.3 算法 1.4 模型评估与模型选择1…

助力焊接场景下自动化缺陷检测识别,基于YOLOv8【n/s/m/l/x】全系列参数模型开发构建工件表面焊接裂纹缺陷检测识别分析系统

焊接是一个不陌生但是对于开发来说相对小众的场景&#xff0c;在工件表面焊接场景下常常有对工件表面缺陷智能自动化检测识别的需求&#xff0c;工业AI结合落地是一个比较有潜力的场景&#xff0c;在我们前面的博文开发实践中也有一些相关的实践&#xff0c;感兴趣的话可以自行…

SpringBoot3.1.7集成Kafka和Kafka安装

一、背景 我们在很多系统开发都需要用到消息中间件&#xff0c;目前来说Kafka凭借其优秀的性能&#xff0c;使得它的使用率已经是名列前茅了&#xff0c;所以今天我们将它应用到我们的系统 二、版本选择 在使用一个中间件一定要考虑版本的兼容性&#xff0c;否则后面会遇到很…

搜索与图论第五期 拓扑序列

前言 拓扑排序是非常重要的一部分&#xff0c;希望大家都能够手撕代码&#xff01;&#xff01;&#xff01;&#xff08;嘿嘿嘿&#xff09; 一、拓扑排序定义&#xff08;百度须知嘿嘿嘿&#xff09; 拓扑排序 拓扑排序是一种对有向无环图&#xff08;Directed Acyclic Gra…

开始学习vue2基础篇(指令)

一、 内容渲染指令 > {{}} 模板渲染&#xff08;模板引擎&#xff09; 1. {{数据绑定}} 2. {{简单计算}} 3. {{简单逻辑运算}}&#xff08;三元运算&#xff09; 4. {{做简单 js 判断}} 注意&#xff1a;不能写语句、不能解析 html 渲染、不能放在在属性身上 > v-…

菜鸟导入导出assetbundle

因为菜鸟不会用unity c#什么的&#xff0c;所以最后参考贴吧的方法用的是UABE(Unity Assets Bundle Extractor)和UABEA(Unity Assets Bundle Extractor Avalonia) 可以去github上下载 对于txt、xml什么的可以直接改&#xff0c;但是byte文件里还是会有一些类似乱码的东西&…

算法通关村番外篇-面试150题一

大家好我是苏麟 , 今天开始LeetCode面试经典150题 . 大纲 26. 删除有序数组中的重复项80. 删除有序数组中的重复项 II 26. 删除有序数组中的重复项 描述 : 给你一个 非严格递增排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 …

MIT - 线性代数-LU_LDU分解|单位矩阵

麻省理工学院 - MIT - 线性代数 第四讲 https://www.bilibili.com/video/BV1GD4y1x7Za/?spm_id_from333.1007.top_right_bar_window_history.content.click&vd_source54eff91a9ce326df74fd3b06c9fc2be322情况 老师&#xff0c;没讲明白的LU分解&#xff0c;MIT一张图就解…

【QT+QGIS跨平台编译】之五:【curl+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、curl介绍二、curl下载三、文件分析四、pro文件五、编译实践 一、curl介绍 curl&#xff08;CommandLine Uniform Resource Locator&#xff09;主要功能就是用不同的协议连接和沟通不同的服务器&#xff0c;相当封装了的socket。 libcurl支持http, https, ftp, g…

什么叫单位矩阵?

单位矩阵&#xff08;Identity Matrix&#xff09;是一个特殊的方阵&#xff0c;其主对角线上的元素全为1&#xff0c;而其他元素全为0。单位矩阵通常用符号 I 或 E 表示。 一个nn 的单位矩阵的表示形式如下&#xff1a; 其中&#xff0c;主对角线上的元素全为1&#xff0c;…

http网络编程——在ue5中实现文件传输功能

http网络编程在ue5中实现 需求&#xff1a;在unreal中实现下载功能&#xff0c;输入相关url网址&#xff0c;本地文件夹存入相应文件。 一、代码示例 1.Build.cs需要新增Http模块&#xff0c;样例如下。 PublicDependencyModuleNames.AddRange(new string[] { "Core&q…

matlab 交通流量PI和P控制

1、内容简介 略 37-可以交流、咨询、答疑 2、内容说明 略. 题目背景 有一条路&#xff0c;他有一个主干道和一个次干道&#xff0c;现在这条路上有一定的交通流&#xff0c;交通流的情况是第二张图(交通流的程序在那个matlab文件里的做出的figure1里有&#xff09;&#x…

【vue3】GSAP在vue中的使用

一、获取GSAP npm install gsap 二、开始GSAP 导入GSAP&#xff0c;如果需要导入gsap的插件可以参考这里。 import gasp from gsap; 这里用的是选项式&#xff0c;在methods属性中创建一个方法用来写gsap的动画。 gasp_animation(){let tl gasp.timeline({defaults:{ ease:&…
最新文章