《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.rhkb.cn/news/243618.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

决策树的基本构建流程

决策树的基本构建流程 决策树的本质是挖掘有效的分类规则&#xff0c;然后以树的形式呈现。 这里有两个重点&#xff1a; 有效的分类规则&#xff1b;树的形式。 有效的分类规则&#xff1a;叶子节点纯度越高越好&#xff0c;就像我们分红豆和黄豆一样&#xff0c;我们当然…

表单的总数据为什么可以写成一个空对象,不用具体的写表单中绑定的值,vue3

<el-form :model"form" label-width"120px"><el-form-item label"Activity name"><el-input v-model"form.name" /></el-form-item> </el-form> const form ref({})from为空对象 在v-model里写form…

Python 猎户星空Orion-14B,截止到目前为止,各评测指标均名列前茅,综合指标最强;Orion-14B表现强大,LLMs大模型

1.简介 Orion-14B-Base是一个具有140亿参数的多语种大模型&#xff0c;该模型在一个包含2.5万亿token的多样化数据集上进行了训练&#xff0c;涵盖了中文、英语、日语、韩语等多种语言。在多语言环境下的一系列任务中展现出卓越的性能。在主流的公开基准评测中&#xff0c;Orio…

Tensorflow2.0笔记 - tensor的合并和分割

主要记录concat,stack,unstack和split相关操作的作用 import tensorflow as tf import numpy as nptf.__version__#concat对某个维度进行连接 #假设下面的tensor0和tensor1分别表示4个班级35名同学的8门成绩和两个班级35个同学8门成绩 tensor0 tf.ones([4,35,8]) tensor1 tf…

centos安装:node.js、npm及pm2

前言 Node.js发布于2009年5月&#xff0c;由Ryan Dahl开发&#xff0c;是一个基于Chrome V8引擎的JavaScript运行环境&#xff0c;使用了一个事件驱动、非阻塞式I/O模型&#xff0c;让JavaScript 运行在服务端的开发平台&#xff0c;它让JavaScript成为与PHP、Python、Perl、Ru…

20.云原生之GitLab CICD实战

云原生专栏大纲 文章目录 GitLab RunnerGitLab Runner 介绍Gitlab Runner工作流程 Gitlab集成Gitlab RunnerGitLab Runner 版本选择Gitlab Runner部署docker-compose方式安装kubesphere中可视化方式安装helm方式安装 配置gitlab-runner配置gitlab-ci.ymlgitlab-ci.yml 介绍编写…

SpringCloud Alibaba 深入源码 - Nacos 分级存储模型、支撑百万服务注册压力、解决并发读写问题(CopyOnWrite)

目录 一、SpringCloudAlibaba 源码分析 1.1、SpringCloud & SpringCloudAlibaba 常用组件 1.2、Nacos的服务注册表结构是怎样的&#xff1f; 1.2.1、Nacos的分级存储模型&#xff08;理论层&#xff09; 1.2.2、Nacos 源码启动&#xff08;准备工作&#xff09; 1.2.…

Linux编辑器---vim

目录 1、vim的基本概念 2正常/普通/命令模式(Normal mode) 2、1命令模式下一些命令&#xff08;不用进入插入模式&#xff09; 3插入模式(Insert mode) 4末行/底行模式(last line mode) 4、1底行模式下的一些命令 5、普通用户无法进行sudo提权的解决方案 6、vim配置问题 6、1配…

超优秀的三维模型轻量化、格式转换、可视化部署平台!

1、基于 HTML5 和 WebGL 技术&#xff0c;可在主流浏览器上进行快速浏览和调试&#xff0c;支持PC端和移动端 2、自主研发 AMRT 展示框架和9大核心技术&#xff0c;支持3D模型全网多端流畅展示与交互 3、提供格式转换、减面展UV、烘焙等多项单模型和倾斜摄影模型轻量化服务 4、…

uniapp 链接跳转(内部跳转和外部跳转)

使用uniapp的超链接跳转在微信小程序中会出现复制链接在外面在跳转如图 这样的客户体验感不好 我们需要可以直接跳转查看 思路&#xff1a;webview 1.先在自己uniapp项目pages.json建一个内部页面webview.vue 在page.json里面指向我们跳转的这个内部路径(这个创建页面会自动…

Unity中URP下的SimpleLit的 BlinnPhong高光反射计算

文章目录 前言一、回顾Blinn-Phong光照模型1、Blinn-Phong模型&#xff1a; 二、URP下的SimpleLit的 BlinnPhong1、输入参数2、程序体计算 前言 在上篇文章中&#xff0c;我们分析了 URP下的SimpleLit的 Lambert漫反射计算。 Unity中URP下的SimpleLit的 Lambert漫反射计算 我…

别再因为React、Vue吵了,真的毫无新意!

最近尤大的一个推文引起了不小热议&#xff0c;大概经过是&#xff1a; 有人在推上夸React文档写的好&#xff0c;把可能的坑点都列出来尤看到后批评道&#xff1a;框架应该自己处理这些坑点&#xff0c;而不是把他们暴露给用户 尤大在推上的发言一直比较耿直&#xff0c;这次…

2024-01-22(MongoDB)

1.Mongodb使用的业务场景&#xff1a; 传统的关系型数据库/mysql在“三高”需求以及应对web2.0的网站需求面前&#xff0c;有点力不从心&#xff0c;什么是“三高”需求&#xff1a; a. 对数据库高并发的读写需求 b. 对海量数据的高效率存储和访问需求 c. 对数据库的高可扩…

Redis应用(1)缓存(1.2)------Redis三种缓存问题

一、 缓存穿透&#xff1a; 1、定义&#xff1a; 缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在&#xff0c;这样缓存永远不会生效&#xff0c;这些请求都会打到数据库。所谓穿透&#xff0c;就是直接透过了redis&#xff0c;直接透到数据库 2、原因&#xff1a;…

Spring Boot3整合Druid(监控功能)

目录 1.前置条件 2.导依赖 错误依赖&#xff1a; 正确依赖&#xff1a; 3.配置 1.前置条件 已经初始化好一个spring boot项目且版本为3X&#xff0c;项目可正常启动。 作者版本为3.2.2最新版 2.导依赖 错误依赖&#xff1a; 这个依赖对于spring boot 3的支持不够&#…

【学习】FPN特征金字塔

论文&#xff1a;Feature Pyramid Networks for Object Detection &#xff08;CVPR 2016) 参考blog&#xff1a;https://blog.csdn.net/weixin_55073640/article/details/122627966 参考视频讲解&#xff1a;添加链接描述 卷积网络中&#xff0c;深层网络容易响应语义特征&am…

芯驰E3340软件编译以及更新步骤

打开已有工程File->Open Solution: 东南项目&#xff1a;e3340\boards\e3_324_ref_display\proj\jetour-t1n-fl3\sf\SES 编译&#xff1a;build->build sf 增加头文件和宏定义&#xff1a; 编译完成sf后&#xff0c;进行编译bootloader 东南项目&#xff1a;e3340\boa…

k8s-kubectl常用命令

一、基础命令 1.1 get 查询集群所有资源的详细信息&#xff0c;resource包括集群节点、运行的Pod、Deployment、Service等。 1.1.1 查询Pod kubectl get po -o wid 1.1.2 查询所有NameSpace kubectl get namespace 1.1.3 查询NameSpace下Pod kubectl get po --all-namespaces…

机器人学论文——智能施药机器人调研报告

目录 摘 要 Abstract 第一章&#xff1a;引言 1.1研究背景 1.2 研究意义 1.3文章架构 第二章&#xff1a;智能施药机器人发展现状 2.1引言 2.2 大田智能施药机器人发展现状 2.3 果园智能施药机器人发展现状 2.4 设施农业智能施药机器人发展现状 第三章&#xff1a;智能施药机器…

【Spring Boot 3】【Redis】基本数据类型操作

【Spring Boot 3】【Redis】基本数据类型操作 背景介绍开发环境开发步骤及源码工程目录结构 背景 软件开发是一门实践性科学&#xff0c;对大多数人来说&#xff0c;学习一种新技术不是一开始就去深究其原理&#xff0c;而是先从做出一个可工作的DEMO入手。但在我个人学习和工…