NPAPI & NPRuntime 簡介

原帖: http://wenku.baidu.com/link?url=dNdAGHq6ty0HQxO6iA8BpD1IzVv2-eP6R_zCdfsm2tfugvGwyxlzmlePjF8mfgYCP6vhxouLHNt4KfKFCEZVPCnSOY6Cx5f9b9hE9U-8RA7




NPAPI &NPRuntime 簡介

Netscape PluginApplication Programming Interface (NPAPI)

NPAPI 原本是由 Netscape 所制定的一組單純的 C Plugin API,起初是無法支援 Scriptability;於是到了 2004 年底時,各家 Browser (IE,Opera, Mozilla ) 都同意支援 NPRuntime 延伸 API 以支援 Scriptability,所以目前若是想寫 Plugin 則應該以 NPRuntime API 才能跨不同的 Browsers

Plugin LifeCycle



上面的 SequenceDiagram 說明了 Browser Plugin 之間的運作過程:

1.     Browser lookup Plugin (.so, .dll) andload it.

2.     Browser 呼叫 Plugin NP_Initialize()來交換彼此所需的 API Function Pointers

1.      Browser Side NPN_API functiontable (NPNetscapeFuncs *aNPNFuncs) 傳給 Plugin(Binding)

2.     Plugin 應將其自身所定義好的 NPP API functions填入 NPPluginFuncs *aNPPFuncs 中,好讓 Browser 得到 Plugin Side Functionpointers

             Browser 呼叫 Plugin NP_GetValue() 來得到 Plugin 的資訊,例如:版本資訊與是否支援 Scriptability 等。

             Browser 在網頁中發現 Plugin 所支援的 Mime Type 時,呼叫 Plugin NPP_New() 來建立新的 Plugin instance來處理。

             當網頁被 Unload 前,Browser 則會呼叫 PluginNPP_Destroy() 來通知 Plugin Destroy 所對應的 Plugin instance

              Browser 程式結束前會呼叫 Plugin NP_Shutdown() Destruction,結束整個 Plugin LifeCycle 

 

以下為  API 宣告:

char*NP_GetMIMEDescription() // Unix only NPError NP_GetValue(void*, NPPVariable,void* out) NPError NP_Initialize(NPNetscapeFuncs*, NPPluginFuncs*) NPErrorOSCALL NP_Shutdown()

 

NPNetscapeFuncs(NPN_XXXXX API)

NPNetscapeFuncs 是一個 Functionpointer table,是 Browser 傳給 Plugin 使用的 NPN_XXXXX API

宣告如下:

typedef struct_NPNetscapeFuncs { uint16 size; uint16 version; NPN_GetURLProcPtr geturl;NPN_PostURLProcPtr posturl; NPN_RequestReadProcPtr requestread;NPN_NewStreamProcPtr newstream; NPN_WriteProcPtr write;NPN_DestroyStreamProcPtr destroystream; NPN_StatusProcPtr status;NPN_UserAgentProcPtr uagent; NPN_MemAllocProcPtr memalloc; NPN_MemFreeProcPtrmemfree; NPN_MemFlushProcPtr memflush; NPN_ReloadPluginsProcPtr reloadplugins;NPN_GetJavaEnvProcPtr getJavaEnv; NPN_GetJavaPeerProcPtr getJavaPeer;NPN_GetURLNotifyProcPtr geturlnotify; NPN_PostURLNotifyProcPtr posturlnotify;NPN_GetValueProcPtr getvalue; NPN_SetValueProcPtr setvalue; NPN_InvalidateRectProcPtrinvalidaterect; NPN_InvalidateRegionProcPtr invalidateregion;NPN_ForceRedrawProcPtr forceredraw; NPN_GetStringIdentifierProcPtrgetstringidentifier; NPN_GetStringIdentifiersProcPtr getstringidentifiers;NPN_GetIntIdentifierProcPtr getintidentifier; NPN_IdentifierIsStringProcPtridentifierisstring; NPN_UTF8FromIdentifierProcPtr utf8fromidentifier;NPN_IntFromIdentifierProcPtrintfromidentifier; NPN_CreateObjectProcPtrcreateobject; NPN_RetainObjectProcPtr retainobject; NPN_ReleaseObjectProcPtrreleaseobject; NPN_InvokeProcPtr invoke; NPN_InvokeDefaultProcPtrinvokeDefault; NPN_EvaluateProcPtr evaluate; NPN_GetPropertyProcPtrgetproperty; NPN_SetPropertyProcPtr setproperty; NPN_RemovePropertyProcPtrremoveproperty; NPN_HasPropertyProcPtr hasproperty; NPN_HasMethodProcPtrhasmethod; NPN_ReleaseVariantValueProcPtr releasevariantvalue;NPN_SetExceptionProcPtr setexception; NPN_PushPopupsEnabledStateProcPtrpushpopupsenabledstate; NPN_PopPopupsEnabledStateProcPtr poppopupsenabledstate;NPN_EnumerateProcPtr enumerate; NPN_PluginThreadAsyncCallProcPtrpluginthreadasynccall; NPN_ConstructProcPtr construct; NPN_ScheduleTimerProcPtrscheduletimer; NPN_UnscheduleTimerProcPtr unscheduletimer;NPN_PopUpContextMenuProcPtr popupcontextmenu; } NPNetscapeFuncs;

 

NPPluginFuncs(NPP_XXXX API)

NPPluginFuncs 也是一個 Functionpointer table,是由 Plugin 傳回給 Browser 使用的 NPP_XXXXX API

宣告如下:

typedef struct_NPPluginFuncs { uint16 size; uint16 version; NPP_NewProcPtr newp;NPP_DestroyProcPtr destroy; NPP_SetWindowProcPtr setwindow;NPP_NewStreamProcPtr newstream; NPP_DestroyStreamProcPtr destroystream;NPP_StreamAsFileProcPtr asfile; NPP_WriteReadyProcPtr writeready;NPP_WriteProcPtr write; NPP_PrintProcPtr print; NPP_HandleEventProcPtr event;NPP_URLNotifyProcPtr urlnotify; JRIGlobalRef javaClass; NPP_GetValueProcPtrgetvalue; NPP_SetValueProcPtr setvalue; } NPPluginFuncs;

 

Plugin InstanceConstruction and Destruction

Browser HTML 中發現 Plugin 所對應的 Mime Type 時,會呼叫 NPP_New() 來向 Plugin 要求一個 Plugin Instace 服務。

NPP_New() 定義如下:

#include<npapi.h> NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved); NPErrorNPP_Destroy(NPP instance, NPSavedData **save);

NPP 即為 Plugin Instance資料結構,由 Browser 所建立,透過 NPP_New() 傳送給 Plugin

NPP 資料結構很簡單,僅包含兩個 void pointer

1.     void *pdata : Plugin Private Data

2.     void *ndata : Browser Private Data

宣告如下:

typedef struct_NPP { void* pdata; void* ndata; } NPP_t; typedef NPP_t* NPP;

Browser 在某個 Page Unload 之前,則會呼叫 NPP_Destroy() 來通知 Plugin 結束所對應的 Plugin Instance

Scriptability

Scriptability 就是讓 JavaScript 可以將 Plugin 當作 JavaScriptObject 來使用,而 NPRuntime 定義了 NPObject NPClass 兩個結構來建立 Browser 能夠了解的 ScriptableObject

Multiple NPObjectInstances

該注意的一點是,NPObject 本身也是需要支援 MultipleInstance,原因很簡單,因為 Plugin Instance 都應該擁有自己的 NPObject,若是 NPObject 不設計成 MultipleInstance,就得所有 Plugin Instance 「共用」一組 NPObject,將會帶來很多擴充性上的困難。

 

Scriptable ObjectModel (NPObject & NPClass design with UML)

以下是個人從 NPRuntime 設計中理解出的 ScriptableObject Model (名字取不好,多見諒。)


 

What is NPClass?

NPClass 是一組 Interface(function pointer table),代表某個 NPObject 在建立 Instance 時所需要的動作(ex:Constructor/Desctructor),也就是說 Browser 只透過 NPClass 所指定的 Methods 來建立新的 NPObjectInstance。舉例來說,當 Browser 透過 NPP_GetValue() 來向 Plugin 要一個 Property 時,Plugin 可以傳回一個 NPObject Browser ,讓 Browser 知道其實這個 Plugin Property其實是一個 Scriptable Object

NPClass 其實就是所謂的 Marshaling Functions,這個原本由 RPC 發展出來的方法已經在很多地方都可以看到,幾乎只要是 Virtual Machine相關的系統都會用這個方式來達到模擬 CallingConvention 的目的。
不過也有例外的,像是 Mozilla XPCOM  
xptcall 就是直接從 register/stack 來做 Marshaling 的動作。

PluginObject 是我們實際上想建立的 Object,它應該具有我們想要的 CustomeProperties Methods,而 PluginObject 所擁有的 NPClass 其實就是 PluginClass ,也是由我們設計的,因為只有設計者才知道 Plugin Object 應該如何construct/destruct/etc…)

Browser 需要建立 NPObjectInstance 時,會呼叫 NPObject→NPClass→allocate(),也就會呼叫到PluginClass→pluginAllocate(),我們就可以 newPluginObject() 傳回給 Browser 了。簡單的說,Browser 想要建立或是存取任何 PluginObject,都得透過 PluginClass 中的 APIBrowser 是無法直接存取 PluginObject CustomProperty/Methods

雖然 NPRuntime 的呼叫都是 C API,但是實際上這組 API 想完成的事就是上面的 ScriptableObject Model。若只是了解 Call Flow 是不夠的,要能「讀出」原來設計這組 API 的人在「想」的是什麼。

NPClass NPObject C 宣告如下:

struct NPClass {uint32_t structVersion; NPAllocateFunctionPtr allocate; NPDeallocateFunctionPtrdeallocate; NPInvalidateFunctionPtr invalidate; NPHasMethodFunctionPtrhasMethod; NPInvokeFunctionPtr invoke; NPInvokeDefaultFunctionPtrinvokeDefault; NPHasPropertyFunctionPtr hasProperty;NPGetPropertyFunctionPtrgetProperty; NPSetPropertyFunctionPtr setProperty;NPRemovePropertyFunctionPtr removeProperty; NPEnumerationFunctionPtr enumerate;}; struct NPObject { NPClass *_class; uint32_t referenceCount; // Additionalspace may be allocated here by types of NPObjects }

 

When shouldNPObject be created?

NPP_New() 要求建立 Plugin Instance,就需要建立我們的 PluginObject(which is a NPObject) Instance,在此同時,應該直接以NPN_CreateObject() 來建立相對應的 NPObject,因為 NPObject 是由 Browser 來主動 Allocate/FreeMemory,所以 reference count 也會紀錄在 NPObject 中。

以下為使用 NPN_CreateObject() 來建立 NPObject 的範例:

#include<npruntime.h> NPObject *NPN_CreateObject(NPP npp, NPClass *aClass);NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc,char* argn[], char* argv[], NPSavedData* saved) { // Scripting functionsappeared in NPAPI version 14 if (browser->version >= 14) instance->pdata= NPN_CreateObject ((NPP) instance, (NP_Class *) PluginClass); }

NPN_CreateObject()是由 Plugin Browser 要求建立一個 NPObject,此 NPObject NPClass 就是我們所指定的 PluginClass

若是 PluginClass 中有指定 allocate(),則 Browser 會使用 PluginClass-> allocate() 來做來替NPObject allocate Memory ,否則 Browser 會以 malloc() AllocateNPObject。傳回的 NPObject reference count會變成 1

這裡有一件很重要的技巧,PluginClass 中的pluginAllocate() 應該要 newPluginObject 傳回給 Browser,因為 PluginObject 「繼承自」 NPObject Browser 的角度來看,PluginObject 就只是個 NPObject;但是對於 Plugin 來說,instance→pdata 所存放的其實是 PluginObject;之後 Browser 呼叫 PluginClass 中的 Methods 時,我們只需要取得 instance→pdata 就可以當做是 PluginObject,並直接存由 Plugin 自己定義的 CustomProperties/Methods 了。如此一來,就不用一堆 Variable 指來指去的了,這是簡化支援 MultipleInstance 的一個重點。

這樣的技巧在 AppleObjective-C Object-OrientedLanguage (ex: C++ vtable) 裡是很常見的,可惜的是我們目前的實作完全沒有 OO 的思考。
instance->pdata
反正是個 void *,可以任意 casting 成任一種 typeBrowser Plugin 就像一個中國各自表述啦~

 

Browser ask forNPObject

Browser NPP_New() 建立 Plugin Instance之後,還會以

NPP_GetValue(npp,NPPVpluginScriptableNPObject, void* value);

來詢問 Plugin 是否支援 Scriptable,此時再把我們先前建立好的 PluginObject(stored in instance->pdata) 透過 value 傳回給 Browser 即可。

範立如下:

NPErrorNPP_GetValue(NPP instance, NPPVariable variable, void *value) { if (variable ==NPPVpluginScriptableNPObject) { void **v = (void **)value; PluginObject *obj =instance->pdata; if (obj) NPN_RetainObject((NPObject*)obj); *v = obj; returnNPERR_NO_ERROR; } return NPERR_GENERIC_ERROR; }

NPRuntime API 中規定,在傳回 NPObject 之前,應該先以 NPN_RetainObject()來增加 NPObject.refCount,這對 JavaScriptEngine Garbage Collection 機制很重要,千萬別忘了。

 

How NPObject wasused by JavaScript?

 

2 Types ofMarshaling Functions (Method/Property)

在上面建立完 ScriptableNPObject 後,等於是建立了一個相對應的 JavaScript Object 。於是 JavaScript 可以對 JavaScriptObject 做其它的存取動作,而這些動作則被對應到 NPObject 的兩類 MarshalingFunctions

(提醒一下:NPObject->_class就是 NPClass )

1.     Method 呼叫

 

typedef bool(*NPHasMethodFunctionPtr)(NPObject *obj, NPIdentifier name); typedef bool(*NPInvokeFunctionPtr)(NPObject *obj, NPIdentifier name, const NPVariant *args,uint32_t argCount, NPVariant *result); typedef bool(*NPInvokeDefaultFunctionPtr)(NPObject *npobj, const NPVariant *args, uint32_targCount, NPVariant *result); struct NPClass {     ...    NPHasMethodFunctionPtr hasMethod;     NPInvokeFunctionPtrinvoke;     NPInvokeDefaultFunctionPtr invokeDefault;    ... };

2.     Property 存取

 

typedef bool(*NPHasPropertyFunctionPtr)(NPObject *obj, NPIdentifier name); typedef bool(*NPGetPropertyFunctionPtr)(NPObject *obj, NPIdentifier name, NPVariant*result); typedef bool (*NPSetPropertyFunctionPtr)(NPObject *obj, NPIdentifiername, const NPVariant *value); typedef bool(*NPRemovePropertyFunctionPtr)(NPObject *npobj, NPIdentifier name); structNPClass {     ...   NPHasPropertyFunctionPtr hasProperty;     NPGetPropertyFunctionPtr getProperty;    NPSetPropertyFunctionPtrsetProperty;     NPRemovePropertyFunctionPtrremoveProperty;     ... };

從以下範例說明會比較清楚:

<script>var myPlugin = document.getElementByIdx_x_x_x_x("FooPlugin");myPlugin.fooMethod(); myPlugin.fooProperty = "hello world";</script>

ECMAScript 的角度說明如下:

·        myPlugin : "myPlugin" 是一個 JavaScriptObject 的名字(Identifier),之後可以將 myPlugin 想像成 NPObject(實際上 JavaScript VM 內部的對應要複雜許多)

·        fooMethod : "fooMethod" 是我們 Plugin 所提供的 Method 的名字(Identifier),則 myPlugin.fooMethod();會轉換成對 NPObject NPClass function call ,動作如下:
1.
透過 hasMethod(NPObject, NPIdentifier of"fooMethod") 詢問 Plugin 是否提供名稱為"fooMethod" Method,若有則到 2.
2.
透過 invokeMethod(NPObject, NPIdentifier of"fooMethod", …) 來傳送參數給 Plugin,而 Plugin 則可由 NPIdentifier 得知 Browser 希望呼叫的 method 為何,再去執行所對應的功能,最後再傳回值 (result)

·        fooProperty : "fooProperty" 是我們 Plugin 所提供的 Property 的名字(Identifier),則myPlugin.fooProperty = "hello world"; 會轉換成為以下動作:
1.
透過 hasProperty(NPObject, NPIdentifier of"fooProperty"); 詢問 Plugin 是否有提供名稱為"fooProperty" Property,若有則到 2.
2.
透過 setProperty(NPObject, NPIdentifier of"fooProperty", "hello world"); 要求 Plugin 執行將 fooProperty 的值更改為 "helloworld" 的動作。

以上說明著動在流程上,細節上並非完全正確,因為 JavaScript(ECMAScript) 內部有許多針對 Objects,Properties, Attributes 等細節,可以說上三天三夜了吧!

 

NPVariant(Parameters Serialization between JavaScript and C)

MarshalingFunctions 中,會以 NPVariant 來傳送真正的參數資料。

NPVariant 就是參數的 Serialized DataType

typedef struct_NPVariant { NPVariantType type; union { bool boolValue; int32_t intValue;double doubleValue; NPString stringValue; NPObject *objectValue; } value; }NPVariant; typedef enum { NPVariantType_Void, NPVariantType_Null,NPVariantType_Bool, NPVariantType_Int32, NPVariantType_Double,NPVariantType_String, NPVariantType_Object } NPVariantType;

 

Data Type MappingBetween JavaScript and NPVariant

NPVariant 所封裝的資料型態會對應到 JavaScript 資料型態。

對應如下:

JavaScript

C (NPVariant with type:)

undefined

NPVariantType_Void

null

NPVariantType_Null

Boolean

NPVariantType_Bool

Number

NPVariantType_Double or NPVariantType_Int32

String

NPVariantType_String

Object

NPVariantType_Object

 

Marshaling Macro

為了方便 NPVariant JavaScript 間的資料轉換, NPRuntime 也定義了一組轉換的 Macro 方便程式設計。

#defineNPVARIANT_IS_VOID(_v) ((_v).type == NPVariantType_Void) #defineNPVARIANT_IS_NULL(_v) ((_v).type == NPVariantType_Null) #defineNPVARIANT_IS_BOOLEAN(_v) ((_v).type == NPVariantType_Bool) #defineNPVARIANT_IS_INT32(_v) ((_v).type == NPVariantType_Int32) #defineNPVARIANT_IS_DOUBLE(_v) ((_v).type == NPVariantType_Double) #defineNPVARIANT_IS_STRING(_v) ((_v).type == NPVariantType_String) #defineNPVARIANT_IS_OBJECT(_v) ((_v).type == NPVariantType_Object) #defineNPVARIANT_TO_BOOLEAN(_v) ((_v).value.boolValue) #define NPVARIANT_TO_INT32(_v)((_v).value.intValue) #define NPVARIANT_TO_DOUBLE(_v) ((_v).value.doubleValue)#define NPVARIANT_TO_STRING(_v) ((_v).value.stringValue) #defineNPVARIANT_TO_OBJECT(_v) ((_v).value.objectValue) #define NP_BEGIN_MACRO do {#define NP_END_MACRO } while (0) #define VOID_TO_NPVARIANT(_v) NP_BEGIN_MACRO(_v).type = NPVariantType_Void; (_v).value.objectValue = NULL; NP_END_MACRO#define NULL_TO_NPVARIANT(_v) NP_BEGIN_MACRO (_v).type = NPVariantType_Null;(_v).value.objectValue = NULL; NP_END_MACRO #define BOOLEAN_TO_NPVARIANT(_val,_v) NP_BEGIN_MACRO (_v).type = NPVariantType_Bool; (_v).value.boolValue =!!(_val); NP_END_MACRO #define INT32_TO_NPVARIANT(_val, _v) NP_BEGIN_MACRO(_v).type = NPVariantType_Int32; (_v).value.intValue = _val; NP_END_MACRO#define DOUBLE_TO_NPVARIANT(_val, _v) NP_BEGIN_MACRO (_v).type =NPVariantType_Double; (_v).value.doubleValue = _val; NP_END_MACRO #defineSTRINGZ_TO_NPVARIANT(_val, _v) NP_BEGIN_MACRO (_v).type = NPVariantType_String;NPString str = { _val, strlen(_val) }; (_v).value.stringValue = str;NP_END_MACRO #define STRINGN_TO_NPVARIANT(_val, _len, _v) NP_BEGIN_MACRO(_v).type = NPVariantType_String; NPString str = { _val, _len };(_v).value.stringValue = str; NP_END_MACRO #define OBJECT_TO_NPVARIANT(_val,_v) NP_BEGIN_MACRO (_v).type = NPVariantType_Object; (_v).value.objectValue =_val; NP_END_MACRO

 

Why need NPVariant(Serialization) ?

JavaScript 中,使用者可以任意撰寫任何 function,而這些 function 的參數個數,型態,排列順序等,都是任意的;我們不可能寫出一個 C function 來對應到所有的 JavaScriptfunctionC function compile time 時就必須決定參數個數,型態與順序;因此必須要透過 Marshaling(Serialization) 的方式來取得 JavaScript 的參數後,再轉換成 C 語言中相對應的資料型態來處理。

NPIdentifier

NPObject Method Property 的皆是由 NPIdentifier 來指定,NPIdentifier 對於相同名稱的 Method 或是 Property 會有一個 Unique 值。而 NPIdentifier 的值是由 Browser 所提供,也就是說 Browser 內部有一個 (Hash) Table 來儲存所有的 NPIdentifier

typedef void*NPIdentifier; NPIdentifier NPN_GetStringIdentifier(const NPUTF8 *name); voidNPN_GetStringIdentifiers(const NPUTF8 **names, int32_t nameCount, NPIdentifier*identifiers); NPIdentifier NPN_GetIntIdentifier(int32_t intid); boolNPN_IdentifierIsString(NPIdentifier identifier); NPUTF8*NPN_UTF8FromIdentifier(NPIdentifier identifier); int32_tNPN_IntFromIdentifier(NPIdentifier identifier);

Browser 另外還提供了 NPIdentifier 的轉換函數,供 Plugin 方便使用。

Why useNPIdentifier?

NPIdentifier 這樣的設計主要有兩個原因:

1.     Less Memory Cost
對於許多 Object 來說都有相同名稱的 Method 或是 Property,若是將這些「名稱字串」全都儲存在 Object Instance 中,對於 Memory 的消耗實在是一種浪費。

2.     Fast Lookup (ECMAScript IdentiferResolution)
對於 Browser 或是 Plugin JavaScript 執行時在 Lookup Object 的動作時,能夠以 LookupNPIdentifier 來取代 Name String Compare,可以大大增加 Lookup 的速度。

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值