首页 > 代码库 > WebKit JavaScript Binding添加新DOM对象的三种方式

WebKit JavaScript Binding添加新DOM对象的三种方式

一.基础知识

首先WebKit IDL并非完全遵循Web IDL,只是借鉴使用。WebKit官网提供了一份说明(WebKitIDL),比如Web IDL称"operation”(操作), 而WebKitIDL称为"method"(方法), 另外Web IDL也不像WebKitIDL那样将属性和参数分开。

 

为JavaScript绑定对象,可以使用module来定义所从属的模组。

典型的module包括: core, window, event, traversal, ranges, html, storage. 其中一部分是HTML5定义的。虽然定义的模组不同,并不影响该对象在DOM Tree位置。关于DOM的规格定义,参考以下文档:

    DOM 1 Core and HTML: LINK

    DOM2 HTML:  LINK

    DOM2 Core:  LINK

    DOM2 Traversal and Range: LINK

 

DOM对象有三种型态:

   a.      寄生于现有对象,单实例。

     从属于某个全局对象,访问时透过宿主对象完成。如document.object或window.object, 也可以直接调用object.method.这个实现最为简单,按需分配,并且随着宿主对象释放就可以了。主要参考window的Navigator实现。

   b.      和window、document一样成为全局对象,单实例。

       这个实现最为复杂,且还没有看到相关文档说明。关键要将对象在合适的位置创建,并更新到JSC的堆中才能达到功能。可以参考document的实现,并且需要考虑执行流程,必须对代码做过一些了解,才可能知道相关的改动量。

   c.      多实例对象。可以在脚本中使用new创建。

      myObj = new Object();

      较第一类需要指定自定义建构函数,主要参考DOM中的Image和Float32Array实现。

     提示:1.如果尝试这样使用单例对象,会出现类似下面的错误。不是一个建构对象:

                  
              2.将新对象作为一个新属性寄生到window中提供扩展服务,更符合DOM的框架,它代表的是和浏览器相关的属性。如果需要扩展页面功能,为document增加属性或方法即可。

 

三种型态下的类的基本代码相同,最大的差异在于是如何引用及实例的管理。难点是在合适的位置绑定给JavaScript Core使用。

 

二.实作

下面从最简单的开始,进行三种型态的实作。

1. 寄生于现有对象,单例

这一段的实现,可以参考DOMWindow中的Navigator。(参考:LINK)

  a. 添加新的代码

   为了和DOMWindow就近,将新增的文件放到WebCore/page下。

HorkyWebObject.h

 1 #ifndef WebCore_Horky_WebObject_h 2  3 #define WebCore_Horky_WebObject _h 4  5   6  7 #include <wtf/Forward.h> 8  9 #include <wtf/PassRefPtr.h>10 11 #include <wtf/RefCounted.h>12 13 #include <wtf/HashMap.h>14 15 #include <wtf/RefPtr.h>16 17  18 19 #include "PlatformString.h"20 21  22 23 namespace WebCore {24 25    26 27     class HorkyWebObject :publicRefCounted<HorkyWebObject> {28 29     public:30 31         staticPassRefPtr<HorkyWebObject> create()32 33         {34 35             returnadoptRef(newHorkyWebObject());36 37         }38 39        40 41         String description()const;42 43     private:44 45         HorkyWebObject();46 47     };48 49    50 51 } // namespace WebCore52 53  54 55 #endif

 

HorkyWebObject.cpp

 1 #include "config.h" 2  3 #include "HorkyWebObject.h" 4  5   6  7 namespace WebCore { 8  9     HorkyWebObject::HorkyWebObject()10 11     {12 13     }14 15    16 17     StringHorkyWebObject::description()const18 19     {20 21         return"Hello World!";22 23     }24 25    26 27 } // namespace WebCore28 29  30 31     HorkyWebObject.idl32 33    module window {34 35  36 37     interface [38 39         OmitConstructor  <---不提供getConstructor接口40 41     ] HorkyWebObject {42 43         readonly attribute DOMString description;44 45     };46 47 }

 

    IDL定义中的module将此对象归属于window模块之下。在JavaScript下可以使用window.xxxx来使用新的对象, 也可以直接引用新对象。

Interface后所带的属性并不是在Web IDL定义的,而由WebKit自行定义,相关的属性列在WebCore/bindings/scripts/IDLAttributes.txt中,相应的对生成头文件及代码的影响需要对照CodeGeneratorJS.pm来理解。参考WebkitIDL说明:LINK .

 

b. 修改DOMWindow.h,添加如下代码:

      

 1   class HorkyWebObject; 2  3         …… 4  5         public: 6  7             HorkyWebObject* horkyWebObject()const; 8  9          private:10 11             mutableRefPtr<HorkyWebObject> m_horkyWebObject;

 

 

c. 修改DOMWindow.cpp,添加如下代码

    i. 在头文件添加:

       #include "HorkyWebObject.h"

    ii. 在Clear函数中,添加m_horkyWebObject =0;

    iii. 添加函数:

 

 1 HorkyWebObject*DOMWindow::HorkyWebObject()const 2  3 { 4  5     if (!m_horkyWebObject) 6  7        m_horkyWebObject =HorkyWebObject::create(); 8  9     returnm_horkyWebObject.get();10 11 }

 

 

d. 修改DOMWindow.idl,添加如下一行 :

    1 attribute [Replaceable] HorkyWebObject horkyWebObject; 

 

e. 修改DerivedSources.make, 参考Navigator.idl添加如下代码:

    1 $(WebCore)/page/HorkyWebObject.idl \ 

 

f.修改CMakeLists.txt和WebCore.gypi,参考Navigator.idl, Navigator.h, Navigator.cpp以及JSNavigator.cpp添加相应的文件。

 

g. 对于XCode Project, 先将HorkyWebObject.cpp添加到项目中。待编译一次后,将生成的JSHorkyWebObject.cpp拖到项目中。

 

最后使用下面的JavaScript可以进行测试:

1 <script type=”text/javascript”>2 3     document.write(horkyWebObject.description);4 5     document.write("<br />");6 7     document.write(window. horkyWebObject.description);8 9   </script>

 

2. 根对象,单例

使用最直接的方式,在JSDOMWindowBase有finishCreation原本有一个动作将window和空的document对象加入到JSC堆中,在这里加入新的对象。

   前三个步骤和第一个实作相似, 只是将类名称改为HorkyGlobalWebObject。新的流程设计如下:

a. 添加新的代码

     因为是DOM有一个新全局对象,将新增的文件放到WebCore/dom下。代码和HorkyWebObject相同。

     HorkyGlobalWebObject.h:

 1 #ifndef WebCore_Horky_GlobalWebObject_h 2  3 #define WebCore_Horky_GlobalWebObject_h 4  5   6  7 #include <wtf/Forward.h> 8  9 #include <wtf/PassRefPtr.h>10 11 #include <wtf/RefCounted.h>12 13 #include <wtf/HashMap.h>14 15 #include <wtf/RefPtr.h>16 17  18 19 #include "PlatformString.h"20 21  22 23 namespace WebCore {24 25    26 27     class HorkyGlobalWebObject :publicRefCounted<HorkyGlobalWebObject> {28 29     public:30 31         staticPassRefPtr<HorkyGlobalWebObject> create()32 33         {34 35             returnadoptRef(newHorkyGlobalWebObject());36 37         }38 39        40 41         String description()const;42 43     private:44 45         HorkyGlobalWebObject();46 47     };48 49    50 51 } // namespace WebCore52 53 #endif54 55  56 57      HorkyGlobalWebObject.cpp:58 59 #include "config.h"60 61 #include "HorkyGlobalWebObject.h"62 63  64 65 namespace WebCore {66 67    68 69     HorkyGlobalWebObject::HorkyGlobalWebObject()70 71     {72 73     }74 75    76 77     StringHorkyGlobalWebObject::description()const78 79     {80 81         return"Hello World from Global Object!";82 83     }84 85    86 87 } // namespace WebCore

 

     HorkyGlobalWebObject.idl:

    

 1 module core { 2  3     interface [ 4  5         OmitConstructor 6  7     ] HorkyGlobalWebObject { 8  9         readonly attribute DOMString description;10 11     };12 13 }

 

 

    b. 修改DOMWindow.h,添加如下代码:

       

 1 class HorkyGlobalWebObject; 2  3         …… 4  5         public: 6  7             HorkyGlobalWebObject* horkyGlobalWebObject()const; 8  9          private:10 11            mutableRefPtr<HorkyGlobalWebObject> m_horkyGlobalWebObject;

 

 

    c. 修改DOMWindow.h,添加如下代码:

    i. 在头文件添加:

      

1  #include "HorkyGlobalWebObject.h"

 

    ii. 在Clear函数中,添加m_horkyGlobalWebObject =0;

    iii. 添加函数:

 1 HorkyGlobalWebObject*DOMWindow::horkyGlobalWebObject()const 2  3 { 4  5     if (!m_horkyGlobalWebObject) 6  7         m_horkyGlobalWebObject =HorkyGlobalWebObject::create(); 8  9     returnm_horkyGlobalWebObject.get();10 11 }

 

  

   d. 修改JSDOMWindowBase.h,在updateDocument下添加一行 :

     

  void updateHorkyGlobalWebObject();

 

 

     e. 修改JSDOMWindowBase.cpp.

       i. 在文件头添加

              

    #include "JSHorkyGlobalWebObject.h"

 

      ii. 添加函数:

 1 voidJSDOMWindowBase::updateHorkyGlobalWebObject() 2  3 { 4  5     ASSERT(m_impl->horkyGlobalWebObject()); 6  7     ExecState* exec = globalExec(); 8  9     symbolTablePutWithAttributes(exec->globalData(), Identifier(exec,"horkyglobalwebobject"),10 11                                  toJS(exec, this, m_impl->horkyGlobalWebObject()), DontDelete | ReadOnly);   12 13 }

 

       iii. 修改finishCreation函数在数组staticGlobals中增加一行:

           

 GlobalPropertyInfo(Identifier(globalExec(),"horkyglobalwebobject"), jsNull(), DontDelete | ReadOnly)

 

     提示:别忘了在上一行尾加个逗点。

 

    e. 修改ScriptController.h,在updateDocument下添加新的函数定义:

    

   void updateHorkyGlobalWebObject();

    f. 修改ScriptController.cpp:

       i. 在头文件添加

                 

#include "HorkyGlobalWebObject.h"

 

      ii. 添加函数(参考updateDocument):

 1 voidScriptController::updateHorkyGlobalWebObject()//20120605    2  3 { 4  5     JSLock lock(SilenceAssertionsOnly); 6  7     for (ShellMap::iterator iter = m_windowShells.begin(); iter != m_windowShells.end(); ++iter) 8  9         iter->second->window()->updateHorkyGlobalWebObject();10 11 }

 

       iii. 修改initScript函数在updateDocument下增加一行:

         

 windowShell->window()->updateHorkyGlobalWebObject();

 

  

  g. 修改Frame.cpp在setDocument函数中呼叫m_script.updateDocument下面加入一行:

   

 m_script.updateHorkyGlobalWebObject();

 

    提示:这一句可以保证文档刷新时,新增的全局对象仍然有效。

  h. 修改DerivedSources.make, 参考HorkyWebObject.idl添加如下代码:

   

 $(WebCore)/page/HorkyGlobalWebObject.idl \

 

 

   i.修改CMakeLists.txt和WebCore.gypi,参考HorkyWebObject.idl, HorkyWebObject.h, HorkyWebObject.cpp以及JSHorkyWebObject.cpp添加相应的文件。对于XCode Project, 先将HorkyGlobalWebObject.cpp添加到项目中。待编译一次后,将生成的JSHorkyGlobalWebObject.cpp拖到项目中。

 

最后使用下面的JavaScript可以进行测试:

1 <script type=”text/javascript”>2 3     document.write(horkyWebObject.description);4 5     document.write("<br />");6 7     document.write( horkyglobalwebobject.description);8 9 </script>

 

3. 多实例对象

这一类的对象,可以参考DOMWindows中的Image成员以及Float32Array的实现。

 

 相对单例的对象,主要是提供了一个建构类。在IDL的声明时,将OmitConstructor去掉,替换为CustomConstructor, ConstructorParameters=1, Webkit就会自动生成一个Constructor类。然后再补齐constructXXXXX即可。 参考WebKitIDL中的说明:LINK.

以下为新增对象的类图:

经过两个实作,部分步骤不再细述。 为了区分,需然代码相同,类的名称改为HorkyNewObject。

    a. 添加新的代码。

       HorkyNewObject.cpp/.h参上面的实作。

       HorkyNewObject.idl:

   

 1  module window { 2  3     interface [ 4  5       CustomConstructor, 6  7       ConstructorParameters=1 8  9     ] HorkyNewObject {10 11         readonly attribute DOMString description;12 13     };14 15 }

 

   b. 新增JSHorkyNewObjectCustom.cpp

 1 #include "config.h" 2  3 #include "JSHorkyNewObject.h" 4  5 #include "HorkyNewObject.h" 6  7 #include <runtime/Error.h> 8  9  10 11 usingnamespaceJSC;12 13  14 15 namespace WebCore {16 17    18 19     EncodedJSValueJSC_HOST_CALLJSHorkyNewObjectConstructor::constructJSHorkyNewObject(ExecState* exec)20 21     {22 23         JSHorkyNewObjectConstructor* jsConstructor = jsCast<JSHorkyNewObjectConstructor*>(exec->callee());24 25  26 27         RefPtr<HorkyNewObject> horkyNewObject =HorkyNewObject::create();28 29         if (!horkyNewObject.get())30 31             return throwVMError(exec, createReferenceError(exec,"Cannot create the new instance of HorkyNewObject!"));32 33        34 35         return JSValue::encode(asObject(toJS(exec, jsConstructor->globalObject(),horkyNewObject.get())));36 37     }38 39    40 41 } // namespace WebCore

 

  c. 修改DOMWindow.idl, 参考Image增加一行:

   

    attribute [JSCustomGetter, CustomConstructor] HorkyNewObjectConstructor horkyNewObject;

 

        提示:这里在Constructor类名称前不要加JS. 不然就会遇到JSDOMWindow.cpp找到不对应头文件的编译错误。

 

   d. 修改JSDOMWindowCustom.cpp.

    i. 在头文件添加

      

 #include "JSHorkyNewObject.h"

 

   ii. 添加函数:

JSValueJSDOMWindow::horkyNewObject(ExecState* exec)const {     return getDOMConstructor<JSHorkyNewObjectConstructor>(exec,this); }

 

 提示:关于c/d两个步骤,如果不需要提供自定义的绑定函数(在这个实作是不需要的),可以不要attribut后面两个属性,也不需要步骤d.

 

其余的步骤,参考实作一即可。

测试脚本如下:

<script type=”text/javascript”>        document.write("<br />");        var myObj = new horkyNewObject();        document.write(myObj.description);    </script>

 

参考:

   1. WebKit的JavaScript对象扩展  http://mogoweb.net/archives/194 (mogoweb@gmail.com)

 

   2. 使用如下脚本可以直接基于idl文件生成代码:

       ./generate-bindings.pl ./test/Document.idl --generator=JS --outputDir=./test/Test

WebKit JavaScript Binding添加新DOM对象的三种方式