前言: Chromium浏览器采用的是V8引擎解析javascript代码, V8引擎相对于传统的JS引擎效率上有很大的提高,主要是因为它将js代码直接编译成了目标机器代码. V8引擎的编译过程主要是 js代码->抽象代码树->目标机器代码. 而传统的js编译过程是 js代码->抽象代码树->中间代码->解释执行. V8引擎在执行需要重复调用函数的js代码中效率有显著提升,但是在执行代码量庞大,函数一般是单次调用的JS代码效率上却并不明显,原因是重复调用的函数只需要被编译成一次机器码就能重复执行,而传统的解释执行方式却需要每次都去解释执行.
在做Chromium浏览器定制化需求时常常需要扩展JS对象以增加js的功能. 在javascript这门编程语言的概念里,一切皆为对象,变量,函数等等一切皆为对象,没有类的概念,javascript是一门动态语言,它的主要特点是对象的类型和内容是在运行时决定的,是可以不断变化的. 在javascript的世界里,根对象是global对象,所有的一切对象皆为global的子孙对象.在浏览器中,这个global对象的表现为window对象. 在webkit中已实现的对象称为javascript内部对象,内部对象又分为本地对象和内置对象. 本地对象需要由用户在js代码中使用new方法初化始对象之后才能使用,内置对象是在webkit中已初始化好了可以直接使用. 例如
Test为js本地对象,使用方法为: Test test = new Test(); alert(test.value);
Test为js内置对象,使用方法为: alert(Test.value);
一: 现在以扩展一个javascript Test本地对象为例子说明如何扩展webkit js对象(本方法试验的chorium版本为53):
实现目标: 以下js代码能够顺利执行
var test = new Test(“aaa”,”bbb”,”ccc”);
test.setValue(“value”);
alert(“ip = “ + test.address + “,mask = “ + test.mask + “,gateway =”+test.gateway + “,value = “ + test.value);
实现步骤:
1.在webkit 的core目录(浏览器根目录/src/wibkit/Source/core)相应的位置分别新建Test.idl,Test.h,Test.cpp文件.并在core.gypi中相应的位置将.idl和c文件包含进去.
2..idl文件是连接js对象和c++对象的媒介,首先根据你要设计的js对象的属性和方法来确定.idl对象,再根据.idl文件来写相应的.h 和 .cpp文件.
现在要新增一个 本地js对象,对象名称为Test,它的构造方法是传入三个字符串参数,它有三个可读写字符串属性adress,mask,gateway,一个只读字符串属性value,一个方法为setValue.
那么Test.idl文件的内容为.
[
Constructor(DOMString ip,DOMString mask,DOMString gateway),
] interface Test {
readonly attribute DOMString value;
attribute DOMString address;
attribute DOMString mask;
attribute DOMString gateway;
void setValue(DOMString value);
};
在window.idl中加上:
attribute TestConstructor Test; //注意这里的Test对应的javascript中的Test,如果这里是myTest,那么js代码就是 new myTest(...)
现在写Test 对应的c++类,它需要实现 static Test * create(String & str1,String &str2,String& str3)供外部调用. 只读属性 value 对应的方法 String value() const; 可读写属性对应的方法 String address() const; void setAddress(const String& ip); //(注意这里的函数名要与前面.idl文件声明的属性名称一致,设置属性的函数需要在前面加set并将第一个字母大写,如setAdress函数名一致),mask , gateway属性如上. .idl中声明的方法在c++类中的方法一致,只需要照旧声明一个方法 void setValue(String &value);就行. 好了,只需要把这些接口实现所有工作就完成了. 接下来就可以编译运行测试了.
类的实现代码如下所示:
#ifndef TEST_H
#define TEST_H
#include "wtf/PassRefPtr.h"
#include "core/CoreExport.h"
#include "bindings/core/v8/ScriptWrappable.h"
#include "wtf/RefCounted.h"
#include "wtf/text/WTFString.h"
namespace blink {
class Test : public RefCountedWillBeGarbageCollectedFinalized
DEFINE_WRAPPERTYPEINFO();
public:
virtual ~Test();
static PassRefPtrWillBeRawPtr
return create( String(""), String(""), String(""));
}
static PassRefPtrWillBeRawPtr
String address() const;
String mask() const;
String gateway() const;
void setAddress(const String& ip){mIp=ip;}
void setMask(const String& mask){mMask=mask;}
void setGateway(const String& gateway){mGateWay=gateway;}
String value() const;
void setValue(const String& value){mValue=value;}
private:
explicit Test(String,String,String);
String mValue;
String mIp;
String mMask;
String mGateWay;
};
}
#endif
二 webkit扩展js内置对象的方法
还是以上面的 Test为例:
1.在Test.idl中将[
Constructor(DOMString ip,DOMString mask,DOMString gateway),
] 去除.
2. 在 window.idl 将原来的声明改为 [Replaceable] readonly attribute Test Test;
3. DomWindow.h中增加Test的虚函数构造方法声明 virtual Test* test() const {return NULL;}
4.在LocalDOMWindow.h中声明方法 Test* test() const override,新增变量mutable PersistentWillBeMember
5.在LocalDomWindow.cpp中对 test方法做实现
Test* LocalDOMWindow::test() const
{
if (!m_test)
m_test = Test::create();
return m_test.get();
}
Chromium编译系统会首先去解析test.idl,通过python脚本解析.idl文件生成相应的v8test.cpp,v8test.h. v8test对象 会调用到我们手动写的test对象. v8test.cpp对象绑定了js对象相应的属性和方法,所以在运行时V8解析javascript代码时能够找到找到关联的c++对象上. 运行Test 的js代码相应的调用步骤是: V8引擎解析js代码-> v8Test类->Test类.
在webkit中扩展js对象的方法比较简单,实现过程中出现出错一般也能通过参照原有的代码找到解决方法. 本文是对chorium浏览器如何扩展js对象作了大体概述,但是对于 python脚本如何解析.idl文件生成对应代码,v8引擎如何解析javascript代码这些内部原理本文未涉及.