Chrome是一个多进程(multi-process)架构的浏览器,相比起Firefox之类的单进程浏览器,其提供额外的完全特性。 Chrome包含一个称为“browser”的主进程,其负责运行UI界面、管理标签页(Tabs)和插件进程,具有当前用户所有的访问权限(文件、网络等),并且能够fork出子进程。
标签页被分配到单独的“renderer”进程中,通常每个domain会共享一个进程。展示进程运行在沙盒(Sandbox)中,不能打开文件、网络。展示进程负责渲染HTML布局、处理页面展示。
插件指外部的二进制文件,插件为浏览器添加额外的功能。插件可以随着浏览器一起安装,例如 Adobe Flash、Adobe Reader,也可以是用户自行安装的。一般来说,现有的大部分插件由于需要网络、文件系统的访问权限,而不能在沙盒内运行,因此Chrome提供了进程外插件机制(out of process plugins),在独立进程中以完整访问权限运行插件,并且使用进程间通信(IPC)机制来与browser、renderer进程通信。
Chrome也支持进程内插件(in process plugins),其运行于renderer进程内部,不需要使用IPC机制进行通信。
NPAPI是一个跨浏览器的插件框架,除了微软使用ActiveX技术以外,其他主要浏览器均支持。Chrome使用进程外插件的方式来支持NPAPI。Chrome 目前已经不再支持NPAPI。
PPAPI是谷歌提出的开源、跨平台插件框架,其主要目的是解决NPAPI的性能、可移植性问题。PPAPI的初期设计曾考虑尽可能保持与NPAPI的接口风格一致性,但是这一设计原则逐渐被放弃。
PPAPI允许C/C++模块与浏览器交互、以安全可移植方式来访问系统级函数。PPAPI不支持操作系统调用,但是它提供了类似的API。
PPAPI支持以下操作:
Google没有提供自动化安装第三方的PPAPI插件的方式。使用类似下面的命令行参数启动Chrome,可以手工加载PPAPI插件:
chrome --disable-sandbox --register-pepper-plugins="<plugin_dll_path>;mime/type"
NaCl是一种在浏览器中安全执行平台无关的、不受信任代码的开源沙盒技术,它允许计算密集型、交互性的实时Web应用有效的利用机器的物理性能,并同时保证安全性。NaCl本质上是一个进程内PPAPI插件。
一个NaCl应用程序包含:JavaScript、HTML、CSS、以及一个NaCl模块。目前NaCl SDK支持的语言主要是C和C++。
NaCl SDK是用于开发NaCl可执行文件(nexe)的工具包。由一系列GNU工具链组成,包括 gcc, binutils、gdb等。
Portable Native Client(PNacl)则是在NaCl的基础上支持硬件架构的无关性,可以在任何支持AOT(ahead-of-time)的平台下运行,必须在Chrome 31+才能支持。Chrome负责把pexe格式的中间代码翻译成客户端本地代码。PNacl可以在不依赖Google Web Store的情况下进行部署。在Chrome://flags中开启Native Client标记可以运行任意NaCl。
应当尽可能使用PNacl,而不是NaCl,除非:


不能在一个应用里混合使用多个工具链。下表列出这些工具链使用的C标准库、目录名称,其中platform可能是win或者linux
| 目标架构 | C标准库 | 工具链目录 |
| x86 | newlib | toolchain/<platform>_x86_newlib |
| x86 | glibc | toolchain/<platform>_x86_glibc |
| ARM | newlib | toolchain/<platform>_arm_newlib |
| PNaCl | newlib | toolchain/<platform>_pnacl |
至少包含HTML代码、Manifest文件和NaCl模块3部分内容。
HTML代码中需要包含embed标签,用于定位manifest文件:
<embed width="300" height="150" type="application/x-pnacl" src="helloworld.nmf" name="mygame"> </embed>
Manifest文件以nmf为扩展名,定义了需要加载的NaCl模块以及选项,形式如下:
{
"url": "helloworld.pexe"
}
NaCl模块是编译好的pexe文件,其使用Pepper API作为本地代码与JavaScript、其它浏览器资源的桥梁。
NaCl具有以下缺点:
PPAPI (without NaCL)
本文上面已经提到过,PPAPI插件无法方便的安装,只能使用命令行参数
Google Native Messaging
该技术允许Chrome插件与本地程序进行消息交互。
优势:
劣势:
cd nacl_sdk naclsdk update --force
sudo apt-get install libc6:i386 cd ~/CPP/tools/Chromium/nacl_sdk ./naclsdk update vim ~/.bashrc #添加以下内容 export PATH="$PATH":/home/alex/CPP/tools/Chromium/depot_tools export NACL_SDK_ROOT=/home/alex/Chromium/nacl_sdk/pepper_39
环境变量:
NACL_SDK_ROOT=D:\CPP\tools\nacl_sdk\pepper_39
如果安装了Cygwin,需要把Cygwin的bin目录从PATH中排除掉(可以通过Eclipse工程右键,Properties - C/C++ Build Environment设置),否则构建时可能收到类似“/x86_64-nacl-ld: cannot find -lppapi_gles2”的错误
IDE设置(Eclipse CDT):
@%NACL_SDK_ROOT%\tools\make.exe %*
CONFIG = Debug VALID_TOOLCHAINS := newlib glibc pnacl win NACL_SDK_ROOT ?= $(abspath $(CURDIR)/../../..) include $(NACL_SDK_ROOT)/tools/common.mk TARGET = hello_world LIBS = ppapi_cpp ppapi CFLAGS = -Wall SOURCES = hello_world.cc $(foreach src,$(SOURCES),$(eval $(call COMPILE_RULE,$(src),$(CFLAGS)))) ifneq (,$(or $(findstring pnacl,$(TOOLCHAIN)),$(findstring Release,$(CONFIG)))) $(eval $(call LINK_RULE,$(TARGET)_unstripped,$(SOURCES),$(LIBS),$(DEPS))) $(eval $(call STRIP_RULE,$(TARGET),$(TARGET)_unstripped)) else $(eval $(call LINK_RULE,$(TARGET),$(SOURCES),$(LIBS),$(DEPS))) endif $(eval $(call NMF_RULE,$(TARGET),))
#include <string>
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"
/**
* NaCl实例类
* 浏览器端的每一个embed标签对应了一个实例
* 指定embed标签的属性:
* src="hello_world.nmf"
* type="application/x-pnacl"
*/
class HelloWorldInstance : public pp::Instance
{
public:
explicit HelloWorldInstance( PP_Instance instance ) :
pp::Instance( instance )
{
}
virtual ~HelloWorldInstance()
{
}
/**
* 处理浏览器端通过postMessage()调用传入的消息
* @param var_message 浏览器传入的消息
*/
virtual void HandleMessage( const pp::Var& var_message )
{
// 处理传入消息
if ( !var_message.is_string() ) return;
std::string message = var_message.AsString();
pp::Var var_reply;
if ( message == "hello" )
{
var_reply = pp::Var( "hello from NaCl" );
PostMessage( var_reply );
}
}
};
/**
* NaCl模块类
*/
class HelloWorldModule : public pp::Module
{
public:
HelloWorldModule() :
pp::Module()
{
}
virtual ~HelloWorldModule()
{
}
/**
* 在模块加载完成后,浏览器即调用此函数创建实例。
* @param in 浏览器端的实例对象句柄
* @return 插件端的实例对象
*/
virtual pp::Instance* CreateInstance( PP_Instance instance )
{
return new HelloWorldInstance( instance );
}
};
namespace pp
{
/**
* 该工厂函数在NaCl模块在加载时被浏览器调用一次,不会多次调用
* 浏览器维持模块的单例
*
*/
Module* CreateModule()
{
return new HelloWorldModule();
}
}
{
"program": {
"portable": {
"pnacl-translate": {
"url": "hello_world.pexe"
}
}
}
}
<!DOCTYPE html>
<html>
<head>
<title>hello World</title>
<script type="text/javascript">
HelloWorldModule = null; // 全局模块对象
statusText = 'NO-STATUS';
function moduleDidLoad()
{
HelloWorldModule = document.getElementById( 'hello_world' );
updateStatus( 'SUCCESS' );
HelloWorldModule.postMessage('hello');
}
//处理NaCl模块发送的消息
function handleMessage( message_event )
{
alert( message_event.data );
}
function pageDidLoad()
{
if ( HelloWorldModule == null )
{
updateStatus( 'LOADING...' );
}
else
{
updateStatus();
}
}
function updateStatus( opt_message )
{
if ( opt_message ) statusText = opt_message;
var statusField = document.getElementById( 'statusField' );
if ( statusField )
{
statusField.innerHTML = statusText;
}
}
</script>
</head>
<body onload="pageDidLoad()">
<div id="listener">
<script type="text/javascript">
var listener = document.getElementById( 'listener' );
listener.addEventListener( 'load', moduleDidLoad, true );
listener.addEventListener( 'message', handleMessage, true );
</script>
<embed id="hello_world" width=0 height=0 src="newlib/Debug/hello_world.nmf" type="application/x-nacl" />
</div>
<div>
Status <code id="statusField">NO-STATUS</code>
</div>
</body>
</html>
CONFIG = Debug VALID_TOOLCHAINS := newlib glibc pnacl win
rem 重定向stderr、stdout set NACL_EXE_STDERR=F:\Temp\Chrome\nacl_stderr.log set NACL_EXE_STDOUT=F:\Temp\Chrome\nacl_stdout.log rem 重定向NaCl内部生成的消息,默认至stderr set NACLLOG=F:\Temp\Chrome\nacl.log rem 控制警告、错误信息的显示 set NACL_PLUGIN_DEBUG=1 rem 1-255,越高则信息越详细 set NACL_SRPC_DEBUG=255 set NACLVERBOSITY=255
[GoogleChromePortable] ;--vmodule=ppb*=4 --enable-logging=stderr用于记录NaCl模块对Pepper API的调用 AdditionalParameters=--no-sandbox --enable-nacl-debug --disable-hang-monitor --vmodule=ppb*=4 --enable-logging=stderr
NaCl提供了一种跨平台的类POSIX环境,很多Linux下开源项目都可以在此环境下运行,但是需要特殊的编译过程(port to nacl),在Windows下,需要Cygwin才能编译ports。
sudo apt-get install curl sed git cmake texinfo gettext pkg-config autoconf automake libtool xsltproc sudo apt-get install libglib2.0-dev
cd ~/CPP/tools/Chromium git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git vim ~/.bashrc #添加 export PATH=`pwd`/depot_tools:"$PATH"
cd ~/CPP/tools/Chromium mkdir naclports cd naclports gclient config --name=src https://chromium.googlesource.com/external/naclports.git gclient sync
naclports install <package_dir>
注意NaCl模块可以在四种架构下构建(i686,x86_64, arm, pnacl),每次构建只会使用其中一种架构。某些架构下有多个Toolchain(例如x86有newlib、glibc),可以通过环境变量选择:
NACL_ARCH=i686 TOOLCHAIN=glibc make ffmpeg
如果要为某个port编译所有架构下所有工具链的版本,可以运行:
./make_all.sh ffmpeg
头文件、库被安装到对应工具链的目录下,使用这些库不需要额外的-I、-L参数。源码和编译的输出文件位于out/build/<PACKAGE_NAME>目录下。默认的,所有编译都是以RELEASE方式进行的,可以设置环境变量NACL_DEBUG=1来改变此行为,或者给naclports 传递 --debug参数。naclports会优先尝试直接从Google下载二进制包,如果要强制从源代码编译,可以传递--from-source参数
cd ~/CPP/tools/Chromium/naclports/src NACL_ARCH=pnacl make ffmpeg
可以下载便携版(Portable)的Chromium:http://sourceforge.net/projects/crportable/
类似的,便携版的Chrome也有,可以到网上搜索并下载。
Leave a Reply to Alex Cancel reply