以前的文章可以与本文互为参考:脚本化接口、插件接口脚本化。
本文的例子代码可以下载:无boost需安装CMake、原来的scriptable demo。
function newNPObject(mimetype) { var obj = document.createElement("object"); obj.type = mimetype; document.body.appendChild(obj); return obj; }
if (variable==NPPVpluginScriptableNPObject) { *(NPObject **)value = plugin->GetScriptableObject(); }
NPObject* CPlugin::GetScriptableObject() { if (!m_pScriptableObject) { m_pScriptableObject = NPN_CreateObject(m_pNPInstance, &CScriptObject::nsScriptObjectClass); } if (m_pScriptableObject) { NPN_RetainObject(m_pScriptableObject); } return m_pScriptableObject; }
NPObject *NPN_CreateObject(NPP npp, NPClass *aClass); The function has the following parameters: npp The NPP indicating which plugin wants to instantiate the object. aClass The class to instantiate an object of.第一个参数很好搞定,第二个参数比较费解,创建时传入的&CScriptObject::nsScriptObjectClass实际上是基类nsScriptObjectBase的NPClass变量,结合说明可以知道,NPN_CreateObject是根据所传入的NPClass类创建一个NPObject并返回这个对象的指针。NPN_CreateObject中调用NPClass类的NPAllocateFunctionPtr成员来为NPObject分配内存,看到NPClass的NPAllocateFunctionPtr成员是nsScriptObjectBase::_Allocate函数,该函数则是调用nsScriptObjectBase::AllocateScriptPluginObject来实现的,AllocateScriptPluginObject的实现在Plugincpp中,可以看到其实现代码就是return (NPObject*)new CScriptObject(npp);也就是创建一个新的CScriptObject对象,这里绕过来绕过去这么复杂,其实就是要做这样一件事情:我们设计scriptableobject类时会新建一个类,而基类nsScriptObjectBase却需要用我们设计的scriptableobject类的构造函数来分配内存并转换成NPObject,最终由NPP_GetValue返回给浏览器,JS实际上就是与浏览器获取到的这个对象来交互的。
static NPIdentifier foo_id;
NPIdentifier CScriptObject::foo_id;
int m_vfoo;
CScriptObject::foo_id = NPN_GetStringIdentifier("foo");
bool CScriptObject::HasProperty(NPIdentifier name) { return name == foo_id; }
bool CScriptObject::SetProperty(NPIdentifier name, const NPVariant *value) { if(name==foo_id){ if(value->type == NPVariantType_Int32) m_vfoo = NPVARIANT_TO_INT32(*value); return true; } }
bool CScriptObject::GetProperty(NPIdentifier name, NPVariant *result) { if (name == foo_id) { INT32_TO_NPVARIANT(m_vfoo,*result); m_vfoo++; return true; } }
property test: int property<input id = "fooinput" value="0"></input> <button onclick = "btnsetfoo();">set FOO</button> <button onclick = "btngetfoo();">get FOO</button><br />
function btnsetfoo() { var val = document.getElementById("fooinput"); obj.foo = parseInt(val.value) ; } function btngetfoo() { alert(obj.foo); }
static NPIdentifier func_id;初始化:
NPIdentifier CScriptObject::func_id;接下来将这个id与要设置的函数名关联起来:
CScriptObject::func_id = NPN_GetStringIdentifier("func");浏览器会调用CScriptObject类的HasMethod来判断是否具有某个函数,那么我们在HasMethod中如下实现:
bool CScriptObject::HasMethod(NPIdentifier name) { return name == func_id; }JS中调用obj.func()时,会执行到Invoke,其中我们利用messagebox弹出一个消息框,实现如下:
bool CScriptObject::Invoke(NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result) { if (name == func_id) { MessageBox(NULL,_T("func"),_T(""),0); return true; } return false; }在JS中调用func函数,HTML中相应代码为:
<button onclick ="btnclick();" >FUNC</button>JS代码(片段)如下:
function btnclick() { obj.func(); }
function objJSfunc() { alert("JS function called!"); }
在JS中,函数其实也是一个object,那么如何将这个object传递给插件,并在插件中执行呢?我们可以将这个object作为插件的一个属性,在执行的时候利用NPN_InvokeDefault来执行,以下是完整过程:
首先需要一个变量来保存这个JS函数或者函数的标识,JS函数作为一个对象,因此可以将其保存为一个NPObject对象,可以用全局变量也可以将其作为某个类的成员变量,我将这个NPObject作为CPlugin类的一个成员,声明成员 变量:
NPObject* m_jsObj;在构造函数中初始化为NULL:
m_jsObj = NULL;使用完毕之后需要释放这个对象,我们在析构函数中执行:
if (m_jsObj!=NULL) NPN_ReleaseObject(m_jsObj);接下来,将JS函数作为一个属性,与前文设置一般属性是一样的,先声明一个标识:
static NPIdentifier jsfunc_id;初始化:
NPIdentifier CScriptObject::jsfunc_id;与要设置的属性名称关联起来:
CScriptObject::jsfunc_id = NPN_GetStringIdentifier("OnJsFunc");接下来在HasProperty中:
bool CScriptObject::HasProperty(NPIdentifier name) { return name == jsfunc_id; }然后SetProperty:
bool CScriptObject::SetProperty(NPIdentifier name, const NPVariant *value) { if (name == jsfunc_id) { CPlugin * plugin = (CPlugin*) m_npp->pdata; if (plugin->m_jsObj == NULL) { plugin->m_jsObj = NPN_RetainObject(NPVARIANT_TO_OBJECT(*value)); } return true; } }当然这个就不需要实现GetProperty了。这样就实现了JS回调函数的设置,只需要在JS中使用obj.OnJsFunc = objJSfunc;为其设置好需要调用的JS函数就可以了。
bool CScriptObject::Invoke(NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result) { if (name == func_id) { MessageBox(NULL,_T("func"),_T(""),0); CPlugin* plugin = (CPlugin*) m_npp->pdata; if (!(!plugin->m_jsObj)) { NPVariant result; NPN_InvokeDefault(m_npp,plugin->m_jsObj,NULL,0,&result); NPN_ReleaseVariantValue(&result); } return true; } return false; }以上就是设置JS回调的完整过程,与JS交互有关的话题可能还包括编码的转换,在遇到中文时处理不好可能会导致乱码,只要记住一个原则就是JS处理的字符都是UTF-8编码的,而C++中的字符可能是unicode的也可能是ansi的,因此需要根据实际情况进行编码的转换就可以解决中文乱码的问题。我给出的scriptdemo还包含:str属性可以设置字符串类型的属性,funci2i处理输入为int输出为int的函数,funcs2s处理输入为字符串输出为字符串的函数。目前没有发现有乱码的问题,因此这里就不再对编码转换的话题做过多的介绍了,如果有朋友发现scriptdemo中有乱码的问题,请及时反馈给我,需要的话以后再来补充。
class JsArrayObject : public NPObject { private: vector<std::wstring> array_; static NPIdentifier method_At; static NPIdentifier method_Size; public: static NPClass nsJsObject; JsArrayObject(); ~JsArrayObject(){} public: std::wstring At(size_t idx); size_t Size(); void Clear(); bool Empty(); void PushBack(std::wstring val); static NPObject *_Allocate(NPP npp, NPClass *aClass); static void _Deallocate(NPObject *npobj); static void _Invalidate(NPObject *npobj); static bool _HasMethod(NPObject *npobj, NPIdentifier name); static bool _Invoke(NPObject *npobj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result); static bool _InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result); static bool _HasProperty(NPObject *npobj, NPIdentifier name); static bool _GetProperty(NPObject *npobj, NPIdentifier name, NPVariant *result); static bool _SetProperty(NPObject *npobj, NPIdentifier name, const NPVariant *value); static bool _RemoveProperty(NPObject *npobj, NPIdentifier name); static bool _Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count); static bool _Construct(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result); };
NPIdentifier JsArrayObject::method_At; NPIdentifier JsArrayObject::method_Size; JsArrayObject::JsArrayObject() { method_At = NPN_GetStringIdentifier("At"); method_Size = NPN_GetStringIdentifier("Size"); } NPObject* JsArrayObject::_Allocate(NPP npp, NPClass *aClass) { return (NPObject*)new JsArrayObject(); } void JsArrayObject::_Deallocate(NPObject *npobj) { delete (JsArrayObject*)npobj; } void JsArrayObject::_Invalidate(NPObject *npobj) { } bool JsArrayObject::_HasMethod(NPObject *npobj, NPIdentifier name) { return name == method_At || name ==method_Size ; } bool JsArrayObject::_Invoke(NPObject *npobj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result) { if (name == method_At) { if(argCount < 1) return false; int val = args[0].value.intValue; std::wstring str = ((JsArrayObject*)npobj)->At(val); std::string pstr = ult::UnicodeToUtf8(str); char* npOutString = (char*) NPN_MemAlloc(pstr.length() + 1); if (!npOutString) return false; strcpy(npOutString, pstr.c_str()); STRINGZ_TO_NPVARIANT(npOutString,*result); return true; } if (name == method_Size) { int val = ((JsArrayObject*)npobj)->Size(); INT32_TO_NPVARIANT(val, *result); return true; } return false; } bool JsArrayObject::_InvokeDefault(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result) { return false; } bool JsArrayObject::_HasProperty(NPObject *npobj, NPIdentifier name) { return false; } bool JsArrayObject::_GetProperty(NPObject *npobj, NPIdentifier name, NPVariant *result) { return false; } bool JsArrayObject::_SetProperty(NPObject *npobj, NPIdentifier name, const NPVariant *value) { return false; } bool JsArrayObject::_RemoveProperty(NPObject *npobj, NPIdentifier name) { return false; } bool JsArrayObject::_Enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count) { return false; } bool JsArrayObject::_Construct(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result) { return false; } NPClass JsArrayObject::nsJsObject = { NP_CLASS_STRUCT_VERSION_CTOR, JsArrayObject::_Allocate, JsArrayObject::_Deallocate, JsArrayObject::_Invalidate, JsArrayObject::_HasMethod, JsArrayObject::_Invoke, JsArrayObject::_InvokeDefault, JsArrayObject::_HasProperty, JsArrayObject::_GetProperty, JsArrayObject::_SetProperty, JsArrayObject::_RemoveProperty, JsArrayObject::_Enumerate, JsArrayObject::_Construct }; std::wstring JsArrayObject::At(size_t idx) { return array_.at(idx); }