webkit 扩展 JavaScript 对象

转载请注明出处:http://blog.csdn.net/awebkit


我在 上一讲 中说了对于浏览器开发者需要能提供自定义 JavaScript 接口的能力,就像 android 平台的 addJavaScriptInterface 把 java 对象和 JavaScript 对象联系起来。上一讲只讲了加入自定义 JavaScript 对象的时机,即在 FrameLoaderClient 的 dispatchDidClearWindowObjectInWorld 接口中实现自定义的 JavaScript 对象。那么,关键来了,如何实现自定义 JavaScript 对象呢?

我们知道 JavaScript Engine 只认识对象,而这个对象就是 JSObject 。
JavaScriptCore/API 下面有 JavaScript Engine 提供的接口。主要的数据类型有 JSContextRef , JSObjectRef , JSStringRef , JSValueRef 

JavaScriptCore/API/tests 下面有个例子(minidom.c)说明了如何扩展一个函数(print)以及一个对象(Node)。

如何扩展一个函数

从扩展 print 函数的例子开讲。代码如下
    JSGlobalContextRef context = JSGlobalContextCreateInGroup(NULL, NULL);
    JSObjectRef globalObject = JSContextGetGlobalObject(context);
        
    JSStringRef printIString = JSStringCreateWithUTF8CString("print");
    JSObjectSetProperty(context, globalObject, printIString, JSObjectMakeFunctionWithCallback(context, printIString, print), kJSPropertyAttributeNone, NULL);
    JSStringRelease(printIString);

 首先创建 JavaScript 执行环境 JSGlobalContextRef 。关于 JSGlobalContextRef 和  JSContextRef ,可以认为他们是一样的。定义如下。
/*! @typedef JSContextRef A JavaScript execution context. Holds the global object and other execution state. */
typedef const struct OpaqueJSContext* JSContextRef;

/*! @typedef JSGlobalContextRef A global JavaScript execution context. A JSGlobalContext is a JSContext. */
typedef struct OpaqueJSContext* JSGlobalContextRef;

别去找 OpaqueJSContext 的定义了,没有!当时我搜了好久。。。只要知道这是指针即可。

然后在这个 context 中建立了一个 JSObjectRef 对象。我前面说了,JavaScript Engine 只认识 JSObject 对象,顾名思义,JSObjectRef 和 JSObject 之间有联系,但是我也说不太清,我觉得都是指针。

然后就定义了一个 JSStringRef ,这个对应 JavaScript 函数名。记得用完要释放哦

关键的一步:JSObjectSetProperty 我们看看定义
/*!
@function
@abstract Sets a property on an object.
@param ctx The execution context to use.
@param object The JSObject whose property you want to set.
@param propertyName A JSString containing the property's name.
@param value A JSValue to use as the property's value.
@param exception A pointer to a JSValueRef in which to store an exception, if any. Pass NULL if you do not care to store an exception.
@param attributes A logically ORed set of JSPropertyAttributes to give to the property.
*/
JS_EXPORT void JSObjectSetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSPropertyAttributes attributes, JSValueRef* exception);

所以,这句话的意思就是,在全局对象上加一个属性,属性名叫 print ,值对应  JSObjectMakeFunctionWithCallback(context, printIString, print) 。这也是一个关键函数。
JSObjectMakeFunctionWithCallback 定义如下,写的比较清楚,就是通过一个回调函数实现一个 JavaScript 函数
/*!
@function
@abstract Convenience method for creating a JavaScript function with a given callback as its implementation.
@param ctx The execution context to use.
@param name A JSString containing the function's name. This will be used when converting the function to string. Pass NULL to create an anonymous function.
@param callAsFunction The JSObjectCallAsFunctionCallback to invoke when the function is called.
@result A JSObject that is a function. The object's prototype will be the default function prototype.
*/
JS_EXPORT JSObjectRef JSObjectMakeFunctionWithCallback(JSContextRef ctx, JSStringRef name, JSObjectCallAsFunctionCallback callAsFunction);

JSObjectCallAsFunctionCallback 的定义
/*! 
@typedef JSObjectCallAsFunctionCallback
@abstract The callback invoked when an object is called as a function.
@param ctx The execution context to use.
@param function A JSObject that is the function being called.
@param thisObject A JSObject that is the 'this' variable in the function's scope.
@param argumentCount An integer count of the number of arguments in arguments.
@param arguments A JSValue array of the  arguments passed to the function.
@param exception A pointer to a JSValueRef in which to return an exception, if any.
@result A JSValue that is the function's return value.
@discussion If you named your function CallAsFunction, you would declare it like this:

JSValueRef CallAsFunction(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);

If your callback were invoked by the JavaScript expression 'myObject.myFunction()', function would be set to myFunction, and thisObject would be set to myObject.

If this callback is NULL, calling your object as a function will throw an exception.
*/
typedef JSValueRef
(*JSObjectCallAsFunctionCallback) (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception);

我们再回到开头,print 对应的 JSObjectCallAsFunctionCallback 是如下定义的,这是 c++ 和 JavaScript 对应的函数。(哦,下面的代码忘记释放JSStringRef 了)
static JSValueRef print(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
    UNUSED_PARAM(object);
    UNUSED_PARAM(thisObject);

    if (argumentCount > 0) {
        JSStringRef string = JSValueToStringCopy(context, arguments[0], exception);
        size_t numChars = JSStringGetMaximumUTF8CStringSize(string);
        char stringUTF8[numChars];
        JSStringGetUTF8CString(string, stringUTF8, numChars);
        printf("%s\n", stringUTF8);
    }       
        
    return JSValueMakeUndefined(context);
}

总结一下,创建 context ,获取全局对象,设置全局对象新的属性和值。这样就把一个函数加入到了 JavaScript Engine

如何扩展一个对象

扩展一个对象的流程和扩展一个函数的流程差不多,只是最后一步不再是创建一个函数而是一个对象,即不再是调用 
JSObjectMakeFunctionWithCallback 而是 JSObjectMakeConstructor 。看代码
    JSStringRef node = JSStringCreateWithUTF8CString("Node");
    JSObjectSetProperty(context, globalObject, node, JSObjectMakeConstructor(context, JSNode_class(context), JSNode_construct), kJSPropertyAttributeNone, NULL);
    JSStringRelease(node);

JSObjectMakeConstructor 的定义如下
/*!
@function
@abstract Convenience method for creating a JavaScript constructor.
@param ctx The execution context to use.
@param jsClass A JSClass that is the class your constructor will assign to the objects its constructs. jsClass will be used to set the constructor's .prototype property, and to evaluate 'instanceof' expressions. Pass NULL to use the default object class.
@param callAsConstructor A JSObjectCallAsConstructorCallback to invoke when your constructor is used in a 'new' expression. Pass NULL to use the default object constructor.
@result A JSObject that is a constructor. The object's prototype will be the default object prototype.
@discussion The default object constructor takes no arguments and constructs an object of class jsClass with no private data.
*/
JS_EXPORT JSObjectRef JSObjectMakeConstructor(JSContextRef ctx, JSClassRef jsClass, JSObjectCallAsConstructorCallback callAsConstructor);

这里, JSClassRef 要复杂很多。如果对插件的 NPAPI 接口熟悉,就知道这个也是一些函数指针需要初始化。我们还回到例子中,看例子里面都实现了哪些函数
JSClassRef JSNode_class(JSContextRef context)
{
    UNUSED_PARAM(context);

    static JSClassRef jsClass;
    if (!jsClass) {
        JSClassDefinition definition = kJSClassDefinitionEmpty;
        definition.staticValues = JSNode_staticValues;
        definition.staticFunctions = JSNode_staticFunctions;
        definition.initialize = JSNode_initialize;
        definition.finalize = JSNode_finalize;

        jsClass = JSClassCreate(&definition);
    }   
    return jsClass;
}

这里只实现了 JSClassRef 的 staticValues(静态属性)  staticFunctions(静态方法) initialize finalize。 initialize 和 finalize 相当于构造函数和析构函数。

静态属性和静态方法看代码,还是比较好理解的
static JSStaticValue JSNode_staticValues[] = {
    { "nodeType", JSNode_getNodeType, NULL, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
    { "childNodes", JSNode_getChildNodes, NULL, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
    { "firstChild", JSNode_getFirstChild, NULL, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly },
    { 0, 0, 0, 0 }
};
static JSStaticFunction JSNode_staticFunctions[] = {
    { "appendChild", JSNode_appendChild, kJSPropertyAttributeDontDelete },
    { "removeChild", JSNode_removeChild, kJSPropertyAttributeDontDelete },
    { "replaceChild", JSNode_replaceChild, kJSPropertyAttributeDontDelete },
    { 0, 0, 0 }
};

好了,对象的绑定大概就这么多吧

理解了函数和对象的绑定,然后在 JavaScript 绑定的入口地方就可以添加对象到 JavaScript Engine 中了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值