利用InjectedBundle定制自己的Webkit(一)

Webkit是一个多进程构架,内核WebCore和JS引擎JavaScriptCore都处在WebProcess进程中,而用户界面相关的处理则处在UIProcess进程中。(详见Webkit客户端进程解析

Webkit提供了大量的API供客户程序调用,但是这些API都是在客户进程中调用的,我们无法访问到内核部分的数据结构并处理,如DOM树、Render树、加载的Web资源等等。为了解决这一问题,Webkit提供了一个运行在内核进程的InjectedBundle来提供对内核数据的操作。

InjectedBundle类似于一个插件,单独编译成一个动态库,在内核进程运行到特定情况时会调用InjectedBundle中注册的对应函数来实现自定义操作。每个WebProcess只能加载一个InjectedBundle,用户可以在创建WebProcess的时候指定使用哪个InjectedBundle。

接下来我们就动手制作一个自己的InjectedBundle然后用Webkit加载它。

 

1. 准备工作

我采用的编译环境是VC2005

(1)首先需要下载并编译Webkit(详见Windows平台编译Webkit

(2)然后创建一个空项目,修改项目属性

  a. 配置类型:动态库(.dll)  

  b. 添加附加包含目录:Webkit生成文件路径\inlude 和 Webkit生成文件路径\include\include(一定要加两个,第二个是windows平台缺少的第三方库头文件)

  c. 添加附加库目录:Webkit生成文件路径\lib

好,项目配置完毕!接下来实现Webkit所需的接口

 

2. 编写InjectedBundle

先上代码

#include <WebKit2/WKBundleInitialize.h>

#pragma comment(lib, "WebKit.lib"

extern "C" __declspec(dllexport)

void WKBundleInitialize(WKBundleRef bundle, WKTypeRef initializationUserData)

  // 初始化代码

}

InjectedBundle只需要实现这一个函数即可(是不是很简单),其中参数bundle是Webkit给你的InjectedBundle分配的标志(identifier),可以用它来调用一些InjectedBundle的API,所以存下来为妙;参数initializationUserData是用户利用Webkit加载该InjectedBundle时传入的一些数据(我们可以用它来传启动参数)。

 

接下来我们要做的就是在该初始化函数中注册我们需要的回调函数

  在Webkit中以Client结尾的结构体都是一个回调函数组,比如WKBundlePageLoaderClient就是一组处理页面加载的回调函数,每个Client都有一个版本号和clientInfo,clientInfo是用来在回调函数中传递用户参数。利用对应的WKBundleSetXXXClient就能够注册某一个回调函数组。

  我们需要注册的第一组回调函数是WKBundleClient,内容如下

struct WKBundleClient {

  int version;                              // 版本号

  const void * clientInfo;                        // 用户参数

  WKBundleDidCreatePageCallback didCreatePage;           // page创建完毕

  WKBundleWillDestroyPageCallback willDestroyPage;           // page将要销毁

  WKBundleDidInitializePageGroupCallback didInitializePageGroup;   // page组初始化完毕

  WKBundleDidReceiveMessageCallback didReceiveMessage;       // 收到用户消息

};

  前两个已经说过了,这里主要讲didCreatePage和didReceiveMessage

 

(1)didCreatePage

  该函数是在创建一个Page对象之后被调用的,原型是

typedef void (*WKBundleDidCreatePageCallback)(WKBundleRef bundle, WKBundlePageRef page, const void* clientInfo);

  在回调里我们能获得所创建Page的引用page,利用这个引用可以调用一些和page相关的API。其中比较重要的是WKBundlePageSetXXXClient,利用这一组函数可以设置该页面的一些回调,比如之前说过的WKBundlePageLoaderClient,包含了didReceiveTitleForFrame(当收到Frame的标题后调用),didFinishLoadForFrame(当一个Frame对象加载完毕后被调用)等回调函数。利用这些回调函数就能在不同阶段实现我们想要的功能了。

 

(2)didReceiveMessage

  该函数是在收到客户进程的消息后调用的。Webkit设计了一个消息队列机制使得两个进程之间能够通信,客户进程通过调用WKContextSetInjectedBundleClient给InjectedBundle发消息,InjectedBundle通过WKBundlePostMessage向客户进程发消息。我们利用这个方法就能在两个进程间交换数据。例如把InjectedBundle处理的结果传给客户进程,或者客户进程向InjectedBundle发指令。

消息的格式是messageName + messageBody

messageName是WKStringRef,即一个字符串

messageBody是任意的WKType,如:WKDictionaryRef, WKDataRef, WKArrayRef等等。

 

在InjectedBundle写完之后编译生成一个.dll文件就可以拿来使用了。

下一篇中将介绍怎样在Webkit中加载InjectedBundle,待续...

posted @ 2012-12-02 20:01  有来 有去  阅读(4604)  评论(2编辑  收藏  举报
Copyright ©2013 Jiajun. All Rights Reserved.