這篇主要來說明一下, VIL 的 Auto-Update 是怎麼實作的. 要實作Userscript 的 Auto-Update 功能, 要解決的問題有以下兩個:
- 如何比較遠端版本和已安裝的版本?
- 如何下載並更新本機的Userscript?
首先來看第一個問題. 在 greasemonkey userscript 中, 要取得遠端網站上的資訊可以透過 GM_xmlhttpRequest 來進行, 因此只要遠端的 server 可以提供版本資訊, 或者 Last-Modified, 我們便可以拿來做為判斷依據. 不過以 Last-Modified 來判斷, 比較容易產生誤差. 所幸 VIL 目前是放在 Opensvn上, 而 SVN 會在 HTTP Response Header 的 ETag 中包含 SVN revision number:
---response begin---
HTTP/1.1 200 OK
Date: Sat, 19 Apr 2008 06:24:47 GMT
Server: Apache
Last-Modified: Sat, 19 Apr 2008 05:38:26 GMT
ETag: "55//userscripts/view.image.links.user.js"
Accept-Ranges: bytes
Content-Length: 44055
Connection: close
Content-Type: text/plain
X-Pad: avoid browser bug
所以我們可以比較這個值即可. 但是另一個問題是, 要如何取得已安裝程式的版本呢? 我原本有想過利用 SVN Keyword 的方式, 自動 replace 程式中的 $Revision$ keyword, 不過後來發現 SVN 是由 client 來進行 Keyword replace, 所以使用 HTTP 方式取得的內容Keyword並不會被代換掉, 因此只好用 GM_getValue/GM_setValue 來處理了. 以下便是 check update 的程式碼片斷:
if('200' != rspDtls.status){
GM_log('check failed, response HTTP status: '+rspDtls.status);
return;
}else{
var myrev = parseInt(GM_getValue(revtag,'0'));
var remoterev = parseInt(rspDtls.responseHeaders.match(/Etag:\s+"([0-9]+).*"/)[1]);
GM_log('remote rev is ' + remoterev + ', current rev is ' + myrev);
if(myrev < remoterev){
return updateFunction(rspDtls, remoterev);
}else{
if(showOptMessage){
alert('remote rev is ' + remoterev + ', my rev is ' + myrev + ', no need to update.');
}
return true;
}
}
至於第二點, "如何下載及更新本機Userscript", 我一開始的想法是直接 window.open() 到 userscript 的 URL, Greasemonkey 便會自動出現安裝提示. 但後來發現, 這樣的安裝方法會清掉現有的 Include/Exclude page 設定, 所以便改用直接覆蓋已安裝檔案的方法. 而問題又來了: 要如何取得現有的安裝路徑及檔名呢? 這時候就要直接去挖 greasemonkey extension 的 source 了. 在 greasemonkey 的 util.js 中可以看到這一段:
function getScriptDir() {
var dir = getNewScriptDir();
if (dir.exists()) {
return dir;
} else {
var oldDir = getOldScriptDir();
if (oldDir.exists()) {
return oldDir;
} else {
// if we called this function, we want a script dir.
// but, at this branch, neither the old nor new exists, so create one
return GM_createScriptsDir(dir);
}
}
}
function getNewScriptDir() {
var file = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties)
.get("ProfD", Components.interfaces.nsILocalFile);
file.append("gm_scripts");
return file;
}
function getOldScriptDir() {
var file = getContentDir();
file.append("scripts");
return file;
}
直接 copy paste 到 VIL 中就 ok 了. 取得 script install dir, 再用 DOMParser 取得安裝後的檔名(修改自 greasemonkey 的 config.js):
function getInstalledFileName(){
var installedFilename = '';
var configContents = getContents(getScriptFileURI("config.xml"));
var domParser = new DOMParser();
var doc = domParser.parseFromString(configContents, "text/xml");
var nodes = doc.evaluate("/UserScriptConfig/Script", doc, null, 0, null);
for (var node = null; (node = nodes.iterateNext()); ) {
var fname = node.getAttribute("filename");
var name = node.getAttribute("name");
var namespace = node.getAttribute("namespace");
if(name == MY_NAME && namespace == MY_NAMESPACE){
installedFilename = fname;
break;
}
}
return installedFilename;
}
之後就直接 overwite 掉現有的 userscript, 再 alert 提示使用者重新整理頁面即可.
解決這兩個問題之後, userscript 便可以享有 auto-update 的功能了. 有在寫 userscript 的朋友可以參考看看.
Hi Horance,
Just a little bug report.
I got the following error from auto-updating the scripts at diggirl:
update failed! reason:Error: Permission denied to get property XPCComponents.classes
I am using Firefox 3 RC2, greasemonkey 0.8.20080609 and refcontrol 0.8.11. I have already clicked “Allow” when it asked for permission.
Also, although I have already set “signed.applets.codebase_principal_support” = true, I am getting the same “Permission denied” error from launching PicLens from VIL. The exception apparently was thrown from the following lines:
// get home directory
fileObj= Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties).get(“Home”, Components.interfaces.nsILocalFile);
Sorry that my only knowledge about javascript is that it is not java. Can’t help you trace the problem further.
BTW, I didn’t encounter those problems with Firefox 2.
That’s all. Keep up the great work!
Hi Convil,
Thanks a lot for your report.
Actually, It’s a known-issue for VIL on FF3.0, and seems no solution for now. I guess we might need to stay on 2.0 for a while.
I’m working one a extension version of VIL, I believe that will solve the security-related issue. But I’m quite busy recently, so it will take more time to complete. please wait for the good news
Thanks again!