关于什么是浏览器,强大的wiki已经做了比较完善的解释http://en.wikipedia.org/wiki/Web_browser。相关浏览器的比较参考:http://en.wikipedia.org/wiki/Comparison_of_web_browsers#Vulnerabilities
浏览器的核心是layout engine,基本上各浏览器只是包装,主要layout engine参考:http://en.wikipedia.org/wiki/Comparison_of_layout_engines
目前主要的浏览器为Internet Explorer, Firefox, Chrome, Safari和Opera,其余浏览器多为在此基础上的二次开发,如搜狗浏览器,Maxthon等。
浏览器基本都支持针对性的开发,具体的开发支持分为三个层次:主题开发,只能定制浏览器的皮肤界面等;扩展开发,可以定制浏览器的一些功能响应;插件开发,真正的对于浏览器的新功能开发。常见的flash就是使用最广的浏览器插件。目前主要的插件有两种,ActiveX控件和NPAPI插件。
微软的技术基本是COM和ActiveX的天下,IE浏览器插件只支持ActiveX作为插件。具体过程比较简单,使用ATL或MFC开发一个标准的支持IE的ActiveX控件即可。
除微软外,其它浏览器核心基本都是支持NPAPI插件。NPAPI较ActiveX控件更简单,但是只针对浏览器插件。什么是NPAPI,可以参考:http://en.wikipedia.org/wiki/NPAPI
下面主要介绍一个windows32 NPAPI插件的开发实例。
(一)下载所需的头文件或SDK
可以下载GeckoPluginSDK(https://developer.mozilla.org/en/Gecko_SDK),如果觉得麻烦的话可以下载一个只包含头文件的精简版http://code.google.com/p/npapi-headers/(其中函数结构体的大小好像需要点修改)。下面使用精简版;
(二)使用Visual Studio C++建立一个DLL项目(建议选择纯空项目),添加上面下载的头文件;
添加一个Plugin.cpp作为导出函数的源代码文件,添加一个资源文件(这个必须,并且要全部填写下面的资源,否则不会被加载),添加一个.def文件。
(三)填写.def文件(windows下最少有这三个函数)
LIBRARY "NPPlugin" EXPORTS NP_GetEntryPoints @1 NP_Initialize @2 NP_Shutdown @3
(四)填写资源文件(windows下此资源文件必须包括所有字符资源)
BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" BEGIN VALUE "Comments", "simple plugin of NPAPI./0" VALUE "CompanyName", "FSE Team/0" VALUE "FileDescription", "npsimple/0" VALUE "FileExtents", "npHello/0" VALUE "FileOpenName", "npsimple/0" VALUE "FileVersion", "1, 0, 0, 1/0" VALUE "InternalName", "npHello/0" VALUE "LegalCopyright", "Copyright ?2011/0" VALUE "LegalTrademarks", "FSE Team./0" VALUE "MIMEType", "application/npHello/0" VALUE "OriginalFilename", "npPlugin.dll/0" VALUE "PrivateBuild", "/0" VALUE "ProductName", "Simple Example Plugin for Mozilla/0" VALUE "ProductVersion", "1, 0, 0, 1/0" VALUE "SpecialBuild", "/0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0409, 1252 END END
注意:其中很多资源项必须存在,否则浏览器将无法确认其为一个NPAPI插件。
(五)实现基本的插件接口函数
#define EXPORT_API EXTERN_C static NPNetscapeFuncs NPNFuncs; static WNDPROC lpOldProc = NULL; static NPWindow* s_Window = NULL; EXPORT_API NPError OSCALL NP_GetEntryPoints(NPPluginFuncs* pFuncs) { if(pFuncs == NULL) return NPERR_INVALID_FUNCTABLE_ERROR; if(pFuncs->size < sizeof(NPPluginFuncs)) return NPERR_INVALID_FUNCTABLE_ERROR; pFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; pFuncs->size = sizeof(NPPluginFuncs); pFuncs->newp = NPP_New; pFuncs->destroy = NPP_Destroy; pFuncs->setwindow = NPP_SetWindow; pFuncs->newstream = NULL; //NPP_NewStream; pFuncs->destroystream = NULL; //NPP_DestroyStream; pFuncs->asfile = NULL; //NPP_StreamAsFile; pFuncs->writeready = NULL; //NPP_WriteReady; pFuncs->write = NULL; //NPP_Write; pFuncs->print = NULL; //NPP_Print; pFuncs->event = NULL; //NPP_HandleEvent; pFuncs->urlnotify = NULL; //NPP_URLNotify; pFuncs->getvalue = NPP_GetValue; pFuncs->setvalue = NULL; //NPP_SetValue; pFuncs->javaClass = NULL; return NPERR_NO_ERROR; } EXPORT_API NPError OSCALL NP_Initialize( NPNetscapeFuncs* pFuncs #ifdef XP_UNIX , NPPluginFuncs* pluginFuncs #endif ) { if(pFuncs == NULL) return NPERR_INVALID_FUNCTABLE_ERROR; if(HIBYTE(pFuncs->version) > NP_VERSION_MAJOR) return NPERR_INCOMPATIBLE_VERSION_ERROR; if(pFuncs->size < sizeof(NPNetscapeFuncs)) return NPERR_INVALID_FUNCTABLE_ERROR; NPNFuncs.size = pFuncs->size; NPNFuncs.version = pFuncs->version; NPNFuncs.geturlnotify = pFuncs->geturlnotify; NPNFuncs.geturl = pFuncs->geturl; NPNFuncs.posturlnotify = pFuncs->posturlnotify; NPNFuncs.posturl = pFuncs->posturl; NPNFuncs.requestread = pFuncs->requestread; NPNFuncs.newstream = pFuncs->newstream; NPNFuncs.write = pFuncs->write; NPNFuncs.destroystream = pFuncs->destroystream; NPNFuncs.status = pFuncs->status; NPNFuncs.uagent = pFuncs->uagent; NPNFuncs.memalloc = pFuncs->memalloc; NPNFuncs.memfree = pFuncs->memfree; NPNFuncs.memflush = pFuncs->memflush; NPNFuncs.reloadplugins = pFuncs->reloadplugins; NPNFuncs.getJavaEnv = NULL; NPNFuncs.getJavaPeer = NULL; NPNFuncs.getvalue = pFuncs->getvalue; NPNFuncs.setvalue = pFuncs->setvalue; NPNFuncs.invalidaterect = pFuncs->invalidaterect; NPNFuncs.invalidateregion = pFuncs->invalidateregion; NPNFuncs.forceredraw = pFuncs->forceredraw; #ifndef WIN32 NPNFuncs.getstringidentifier = pFuncs->getstringidentifier; NPNFuncs.getstringidentifiers = pFuncs->getstringidentifiers; NPNFuncs.getintidentifier = pFuncs->getintidentifier; NPNFuncs.identifierisstring = pFuncs->identifierisstring; NPNFuncs.utf8fromidentifier = pFuncs->utf8fromidentifier; NPNFuncs.intfromidentifier = pFuncs->intfromidentifier; NPNFuncs.createobject = pFuncs->createobject; NPNFuncs.retainobject = pFuncs->retainobject; NPNFuncs.releaseobject = pFuncs->releaseobject; NPNFuncs.invoke = pFuncs->invoke; NPNFuncs.invokeDefault = pFuncs->invokeDefault; NPNFuncs.evaluate = pFuncs->evaluate; NPNFuncs.getproperty = pFuncs->getproperty; NPNFuncs.setproperty = pFuncs->setproperty; NPNFuncs.removeproperty = pFuncs->removeproperty; NPNFuncs.hasproperty = pFuncs->hasproperty; NPNFuncs.hasmethod = pFuncs->hasmethod; NPNFuncs.releasevariantvalue = pFuncs->releasevariantvalue; NPNFuncs.setexception = pFuncs->setexception; #endif return NPERR_NO_ERROR; } EXPORT_API NPError OSCALL NP_Shutdown(void) { return NPERR_NO_ERROR; } EXPORT_API char* NP_GetMIMEDescription(void) { return "application/nphello: nphello: np plugin"; } NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* saved) { if(instance == NULL) return NPERR_INVALID_INSTANCE_ERROR; NPError rv = NPERR_NO_ERROR; return rv; } NPError NPP_Destroy(NPP instance, NPSavedData** save) { // 释放对于窗口的子类化,还原原来的消息处理函数 if( s_Window && s_Window->window ) { if( lpOldProc ) { SetWindowLong( HWND(s_Window->window), GWL_WNDPROC, (LONG)lpOldProc ); } } s_Window = NULL; lpOldProc = NULL; return NPERR_NO_ERROR; } static LRESULT CALLBACK PluginWinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_PAINT: { // draw a frame and display the string PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); RECT rc; GetClientRect(hWnd, &rc); FrameRect(hdc, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH)); // get our plugin instance object and ask it for the version string //nsPluginInstance *plugin = (nsPluginInstance *)GetWindowLong(hWnd, GWL_USERDATA); DrawText(hdc, "IGA laucher", strlen("IGA laucher"), &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER); EndPaint(hWnd, &ps); } break; default: break; } return DefWindowProc(hWnd, msg, wParam, lParam); } NPError NPP_SetWindow(NPP instance, NPWindow *window) { s_Window = window; if( !(window->window) ) { if( lpOldProc ) { SetWindowLong( HWND(window->window), GWL_WNDPROC, (LONG)lpOldProc ); } return NPERR_GENERIC_ERROR; } HWND hwnd = HWND(window->window); // subclass window so we can intercept window messages and // do our drawing to it lpOldProc = (WNDPROC)SetWindowLong( hwnd, GWL_WNDPROC, (LONG)PluginWinProc ); //// associate window with our nsPluginInstance object so we can access //// it in the window procedure //SetWindowLong(hwnd, GWL_USERDATA, (LONG)this); return NPERR_NO_ERROR; } NPError NP_LOADDS NPP_GetValue(NPP instance, NPPVariable variable, void *value) { NPError err = NPERR_NO_ERROR; switch (variable) { case NPPVpluginNameString: *((char **)value) = "application/nphello"; break; case NPPVpluginDescriptionString: *((char **)value) = "writen by freecnjet-at-gmail.com"; break; default: err = NPERR_GENERIC_ERROR; } return err; }
其中如果需要实现插件的现实,NPP_SetWindow部分将插件和一个容器窗口关联。
(六)插件的安装和使用
插件编译完成后为一个标准的DLL(此处仅讨论windows下的情况)。将此DLL,以np*.dll形式命名并保存在firefox的plugin目录,
在firefox中输入about:plugins;即可看到插件已经安装完成。另外一种更加通用的方式为添加注册表项,注册插件为一个标准插件。
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE/SOFTWARE/MozillaPlugins/@fse.org/NPP Plugin,version=1.0] "Description"="NPP plugin" "Path"="D://Projects//BrownsPlugin//Release//npPlugin.dll" "ProductName"="NPP plugin" "Vendor"="fse team." "Version"="1.0.0.0" [HKEY_LOCAL_MACHINE/SOFTWARE/MozillaPlugins/@fse.org/NPP Plugin,version=1.0/MimeTypes] [HKEY_LOCAL_MACHINE/SOFTWARE/MozillaPlugins/@fse.org/NPP Plugin,version=1.0/MimeTypes/application/nphello] "Description"="NPP plugin(*.nphello)" "Suffixes"="nphello" [HKEY_LOCAL_MACHINE/SOFTWARE/MozillaPlugins/@fse.org/NPP Plugin,version=1.0/Suffixes] "nphello"=""
具体关于NPAPI插件的PLID参考http://www-archive.mozilla.org/projects/plugins/plugin-identifier.html。这样chrome,
opera和safari等都可以支持此插件了,具体可以在地址栏输入about:plugins;即可查看到安装的新插件(注意需要关闭所有
浏览器窗口之后再重新打开浏览器才能确保浏览器会加载新的插件)
最后编辑一个网页,内容如下:
使用浏览器打开此网页即可发现结果。
浏览器插件开发作为对于浏览器本身功能的扩展应用广泛,NPAPI插件更是当今浏览器所支持的标准插件,结构简单功能强大。