本文探討在Windows平台下,WebKit如何搜尋外掛程式。
用於測試的網頁代碼如下:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> </head> <body> <script language=javascript> var len = navigator.plugins.length; document.write("你的瀏覽器共支援" + len + "種plug-in:<BR>"); document.write("<TABLE BORDER>"); document.write("<CAPTION>PLUG-IN 清單</CAPTION>"); document.write("<TR><TH>編號</TH><TH>名稱</TH><TH>描述</TH><TH>檔案名稱</TH></TR>"); for (var i=0; i<len; i++){ document.write("<TR><TD>" + i + "</TD>" + "<TD>" + navigator.plugins[i].name + "</TD>" + "<TD>" + navigator.plugins[i].description + "</TD>" + "<TD>" + navigator.plugins[i].filename) + "</TD></TR>"; } document.write("</TABLE>"); </script> </body></html>
這個網頁的作用是列出當前瀏覽器支援的外掛程式名稱、描述和對應的檔案。
WebKit的外掛程式是消極式載入,並非一開啟瀏覽器就載入所有外掛程式。這個例子中,當JS執行到navigator.plugins.length時,才開始初始化外掛程式資訊,此刻的調用棧如下:
PluginDatabase物件建構完後,將會到幾個特定的目錄下尋找外掛程式,這部分程式碼片段如下:
Vector<String> PluginDatabase::defaultPluginDirectories(){ Vector<String> directories; String ourDirectory = safariPluginsDirectory(); if (!ourDirectory.isNull()) directories.append(ourDirectory); addQuickTimePluginDirectory(directories); addAdobeAcrobatPluginDirectory(directories); addMozillaPluginDirectories(directories); addWindowsMediaPlayerPluginDirectory(directories); addMacromediaPluginDirectories(directories);#if PLATFORM(QT) addJavaPluginDirectory(directories);#endif return directories;}
其中:
sarariPluginsDirectory()返回的是WebKit目前的目錄(exe檔案所在的目錄)下的Plugins子目錄;
addQuickTimePluginDirectory()到註冊表HKEY_LOCAL_MACHINE\Software\AppleComputer, Inc.\QuickTime下尋找InstallDir鍵,取它的值;
addAdobeAcrobatPluginDirectory()到註冊表HKEY_LOCAL_MACHINE\Software\Adobe\AcrobatReader下尋找InstallPath鍵,取它的值(同時也會記錄Adobe的版本號碼);
addMozillaPluginDirectories()到註冊表HKEY_LOCAL_MACHINE\Software\Mozilla,遍曆其子目錄,尋找Plugins鍵,取它的值;
addWindowsMediaPlayerPluginDirectory()首先加入“C:\PFiles\Plugins”這個路徑,再加入註冊表HKEY_LOCAL_MACHINE\Software\Microsoft\MediaPlayer下Installation Directory的值;
addMacromediaPluginDirectories()加入了“C:\Windows\system32\macromed\Flash”和“C:\Windows\system32\macromed\Shockwave10”目錄。
接下來,WebKit會遍曆加入的這些目錄,尋找np打頭的dll檔案,或者,如果遍曆的目錄是以“Shockwave 10”結尾,則尋找Plugin.dll,記錄其路徑。做完這些之後,WebKit還會到註冊表HKEY_LOCAL_MACHINE\Software\MozillaPlugins和HKEY_CURRENT_USER\Software\MozillaPlugins下,遍曆子目錄,尋找Path鍵,將值記錄下來。
經過以上步驟,WebKit會得到Windows下可用作外掛程式的dll檔案路徑的集合。WebKit還會記錄這些檔案的最後修改時間,將來如果要重新載入外掛程式,而dll檔案最後修改時間有更新,則可能要重新擷取dll檔案的資訊和載入新的dll檔案。
WebKit擷取dll檔案資訊的邏輯實現在PluginPackageWin.cpp的PluginPackage::fetchInfo()裡,代碼如下:
bool PluginPackage::fetchInfo(){ DWORD versionInfoSize, zeroHandle; versionInfoSize = GetFileVersionInfoSizeW(const_cast<UChar*>(m_path.charactersWithNullTermination()), &zeroHandle); if (versionInfoSize == 0) return false; OwnArrayPtr<char> versionInfoData = adoptArrayPtr(new char[versionInfoSize]); if (!GetFileVersionInfoW(const_cast<UChar*>(m_path.charactersWithNullTermination()), 0, versionInfoSize, versionInfoData.get())) return false; m_name = getVersionInfo(versionInfoData.get(), "ProductName"); m_description = getVersionInfo(versionInfoData.get(), "FileDescription"); if (m_name.isNull() || m_description.isNull()) return false; VS_FIXEDFILEINFO* info; UINT infoSize; if (!VerQueryValueW(versionInfoData.get(), L"\\", (LPVOID*) &info, &infoSize) || infoSize < sizeof(VS_FIXEDFILEINFO)) return false; m_moduleVersion.leastSig = info->dwFileVersionLS; m_moduleVersion.mostSig = info->dwFileVersionMS; if (isPluginBlacklisted()) return false; Vector<String> types; getVersionInfo(versionInfoData.get(), "MIMEType").split('|', types); Vector<String> extensionLists; getVersionInfo(versionInfoData.get(), "FileExtents").split('|', extensionLists); Vector<String> descriptions; getVersionInfo(versionInfoData.get(), "FileOpenName").split('|', descriptions); for (unsigned i = 0; i < types.size(); i++) { String type = types[i].lower(); String description = i < descriptions.size() ? descriptions[i] : ""; String extensionList = i < extensionLists.size() ? extensionLists[i] : ""; Vector<String> extensionsVector; extensionList.split(',', extensionsVector); // Get rid of the extension list that may be at the end of the description string. int pos = description.find("(*"); if (pos != -1) { // There might be a space that we need to get rid of. if (pos > 1 && description[pos - 1] == ' ') pos--; description = description.left(pos); } // Determine the quirks for the MIME types this plug-in supports determineQuirks(type); m_mimeToExtensions.add(type, extensionsVector); m_mimeToDescriptions.add(type, description); } return true;}
這個方法使用Wndows API,擷取dll檔案的ProductName、FileDescription、Version、MIMEType、FileExtents、FileOpenName等資訊(GetFileVersionInfoW(const_cast<UChar*>(m_path.charactersWithNullTermination()), 0, versionInfoSize, versionInfoData.get()),其中m_path儲存了dll檔案的路徑)。我們測試網頁中列出的外掛程式名稱和描述,就是在這個方法中獲得的。這個方法還調用了另一個有意思的方法PluginPackage::isPluginBlacklisted()來判斷外掛程式是否被禁止。具體哪些外掛程式被禁止以及為什麼被禁止,想瞭解的人可以去看看這個方法。
通過以上步驟,PluginInfo擷取完畢。我們的測試網頁,在我的電腦上顯示結果如下: