NPAPIをざっくりやってみる (1)
多分続編はあると思います。まだ色々やってない事も沢山あるので
お題
最近色んな界隈で賑わっている、APIを使ってCDドライブを開くってやつをNPAPIを使ってChrome拡張でブラウザアクションをクリックしたら開くような感じな事やってみる
一応、これ環境によっては動かないはずなので。ちなみに検証はUbuntu12.04とChrome(v23)
manifest.json
{
"name": "test",
"version": "0.1",
"manifest_version": 2,
"browser_action": {
"default_icon": "icon.png"
},
"background": {
"page": "background.html"
},
"plugins": [
{
"path": "plugin/libhoge.so"
}
]
}
background.htmlはembedでプラグイン読み込みとそれに伴うJavaScriptを読み込むだけなので省略します。JavaScriptの方は書きますけど
background.js
(function(undefined) {
chrome.browserAction.onClicked.addListener(function() {
var plugin = document.getElementById("plugin");
plugin.eject();
});
})();
まぁブラウザアクションをクリックしたらプラグインのejectメソッドを呼び出すって感じ。あとはNPAPIなプラグインを作るだけとなる
NPAPIを作るにあたって必要な物
NPAPIのヘッダーファイルが必要になるんですが、 http://code.google.com/p/npapi-sdk でチェックアウトする事が出来るのでチェックアウトしておく。でこのnpapi-sdkを/opt/npapiとして移動させておく。あとは-I/opt/npapi/headersで参照可能
で作成するのはプラグインのヘッダーとプラグインのソースとなるC++を書くだけ
plugin.h
#include "npfunctions.h"
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/cdrom.h>
NPNetscapeFuncs* ppFuncs;
plugin.cc
#include <iostream>
#include "plugin.h"
using namespace std;
void loga(const char* action) {
cerr << "[" << action << "]" << endl;
}
void eject() {
int fd;
fd = open("/dev/cdrom", O_RDONLY | O_NONBLOCK);
if (fd > 0) {
loga("EJECT");
ioctl(fd, CDROMEJECT, 0);
close(fd);
}
}
class HogeNPObject {
public:
static NPClass npClass;
static bool HasMethod(NPObject *pObj, NPIdentifier name) {
NPUTF8 *methodName = ppFuncs->utf8fromidentifier(name);
bool result = strcmp(methodName, "eject") == 0;
ppFuncs->memfree(methodName);
return result;
}
static bool Invoke(NPObject* pObj, NPIdentifier name, const NPVariant *args, uint32_t argc, NPVariant* result) {
loga("Invoke");
NPUTF8 *methodName = ppFuncs->utf8fromidentifier(name);
bool isMatchMethodName = strcmp(methodName, "eject") == 0;
if (isMatchMethodName) {
eject();
}
ppFuncs->memfree(methodName);
return isMatchMethodName;
}
};
NPClass HogeNPObject::npClass = {
NP_CLASS_STRUCT_VERSION,
NULL,
NULL,
NULL,
HogeNPObject::HasMethod,
HogeNPObject::Invoke,
NULL,
NULL,
NULL,
NULL,
NULL
};
const char* NP_GetMIMEDescription() {
loga("NP_GetMIMEDescription");
return (const char*)"application/hoge-plugin::hoge plugin";
}
NPError NP_GetValue(NPP instance, NPPVariable variable, void *value) {
loga("NP_GetValue");
NPError rv = NPERR_NO_ERROR;
switch (variable) {
case NPPVpluginNameString:
*static_cast<const char**>(value) = "hoge";
break;
case NPPVpluginDescriptionString:
*static_cast<const char**>(value) = "hoge plugin";
break;
case NPPVpluginNeedsXEmbed:
*static_cast<bool*>(value) = true;
break;
case NPPVpluginScriptableNPObject: {
*static_cast<NPObject**>(value) = ppFuncs->createobject(instance, &HogeNPObject::npClass);
break;
}
default:
rv = NPERR_INVALID_PARAM;
break;
}
return rv;
}
NPError NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs) {
loga("NP_Initialize");
ppFuncs = bFuncs;
pFuncs->newp = NPP_New;
pFuncs->getvalue = NP_GetValue;
return NPERR_NO_ERROR;
}
NPError NP_Shutdown() {
loga("NP_Shutdown");
return NPERR_NO_ERROR;
}
NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* saved) {
loga("NPP_New");
return NPERR_NO_ERROR;
}
<embed>とかで読み込んだプラグインをJavaScriptから参照してメソッドを実行する場合、NPClassで指定されているHasMethodが働く。でそこからtrueが返されたらInvokeが動く模様。メソッド実行から値を返したい場合とかにはNPVariantな引数(result)とかに値ぶち込んでやれば良い模様。まだそこら辺はやってない
コンパイル
g++ -g -I/opt/npapi/headers -I. -Wall -DXP_UNIX=1 -fPIC -c plugin.cc -o plugin.o
g++ -shared plugin.o -o plugin/libhoge.so
な感じでコンパイルする。で問題なければおっけーかと
あとは拡張機能のcrx作るとか、開発プロジェクト読み込むかでインストールした後にブラウザアクションにあるアイコンをクリックするとCDドライブが開く