Now browsing the archives for the 'XPCOM' category.
「nsIRDFObserver を使ってブックマークのデータソースの動きを調べる」の使用例
「nsIRDFObserver を使ってブックマークのデータソースの動きを調べる」の使用例。
(1) ブックマークのプロパティを開いて新しくキーワードを設定してやると、以下のようなメッセージがエラーコンソールへ出力される。
onAssert rdf:bookmarks rdf:#$NQocU2 http://home.netscape.com/NC-rdf#ShortcutURL test --- onChange rdf:bookmarks rdf:#$NQocU2 http://home.netscape.com/WEB-rdf#LastModifiedDate 1159195753515707 1159195758372691
これはすなわち、リソース「rdf:#$NQocU2」へ新たに値が「test」であるノードを指し示すアーク「http://home.netscape.com/NC-rdf#ShortcutURL」が生成されたことを意味する。さらに、リソース「rdf:#$NQocU2」から発するアーク「http://home.netscape.com/WEB-rdf#LastModifiedDate」が指し示す先のノードの値が「1159195753515707」から「1159195758372691」に変わったことを意味する。
以上のことから、ブックマークの名前やキーワードを変更すると、それに伴って最終更新日時も変更されることがわかる。
ちなみになぜ「キーワード」プロパティに相当するアークのURIが「http:// … #ShortcutURL」なんていうわかりにくいものであるのかは不明。おそらく昔の名残?
(2) ブックマークをブラウザで開くと、ページのロードが完了したタイミングで、以下のようなメッセージがエラーコンソールへ出力される。
onChange rdf:bookmarks rdf:#$JzBPv1 http://home.netscape.com/WEB-rdf#LastVisitDate 1159194536946366 1159196268746572 --- onChange rdf:bookmarks rdf:#$JzBPv1 http://home.netscape.com/WEB-rdf#LastCharset UTF-8 UTF-8
これはすなわち、リソース「rdf:#$JzBPv1」から発するアーク「http://home.netscape.com/WEB-rdf#LastVisitDate」が指し示す先のノードの値「1159194536946366」が「1159196268746572」に変更され、同様にアーク「http://home.netscape.com/WEB-rdf#LastCharset」が指し示す先のノードの値「UTF-8」が「UTF-8」に変更された(実質変化なし)ことを意味する。
以上のことから、ブックマークをブラウザで開くと、最終訪問日時とページの文字コードの2つのプロパティが更新されることがわかる。これについてもう少し詳しく調べると、以下のような処理の流れであることがわかる。
gBrowser上で何らかのページがロードされる
↓
pageShowEventHandlers が呼ばれる
↓
nsIBookmarksService の updateLastVisitedDate が呼ばれ、もしロードされたページの URL がブックマークされたものであれば、最終訪問日時とページの文字コードの2つのプロパティを更新する
Inspecting Bookmarks data with nsIRDFObserver
Japanese version of this post is also available.
nsIRDFObserver enables us to observe various events occurred at nsIRDFDataSource. The Firefox’s Bookmarks is managed internally as nsIRDFDataSource, so we can inspect it with nsIRDFObserver described below and Error Console (formally known as JavaScript Console).
First, you should define nsIRDFObserver object which observes the datasource. The two methods, “_targetToString” and “_log” are defined as original private methods.
var rdfObserver = { onAssert : function(aData, aRes, aProp, aTarget) { this._log(["onAssert", aData.URI, aRes.Value, aProp.Value, this._targetToString(aTarget)].join(" ")); }, onBeginUpdateBatch : function(aData) { this._log(["onBeginUpdateBatch", aData.URI].join(" ")); }, onChange : function(aData, aRes, aProp, aOldTarget, aNewTarget) { this._log(["onChange", aData.URI, aRes.Value, aProp.Value, this._targetToString(aOldTarget), this._targetToString(aNewTarget)].join(" ")); }, onEndUpdateBatch : function(aData) { this._log(["onEndUpdateBatch", aData.URI].join(" ")); }, onMove : function(aData, aOldRes, aNewRes, aProp, aTarget) { this._log(["onMove", aData.URI, aOldRes.Value, aNewRes.Value, aProp.Value, this._targetToString(aTarget)].join(" ")); }, onUnassert : function(aData, aRes, aProp, aTarget) { this._log(["onUnassert", aData.URI, aRes.Value, aProp.Value, this._targetToString(aTarget)].join(" ")); }, /** * refer to an appropriate interface for nsIRDFNode and get the string-type value */ _targetToString : function(aTarget) { const Ci = Components.interfaces; if ( aTarget instanceof Ci.nsIRDFLiteral ) // String type return aTarget.QueryInterface(Ci.nsIRDFLiteral).Value; else if ( aTarget instanceof Ci.nsIRDFInt ) // Number type return aTarget.QueryInterface(Ci.nsIRDFInt).Value; else if ( aTarget instanceof Ci.nsIRDFDate ) // Date type return aTarget.QueryInterface(Ci.nsIRDFDate).Value; else // And more...? return ""; }, /** * output string to Error Console */ _log : function(aMsg) { const CONSOLE_SERVICE = Components.classes['@mozilla.org/consoleservice;1'].getService(Components.interfaces.nsIConsoleService); CONSOLE_SERVICE.logStringMessage(aMsg); }, };
Then, the only thing you have to do is adding the observer to Bookmarks datasource.
// get the datasource of Bookmarks const RDF_SERVICE = Components.classes['@mozilla.org/rdf/rdf-service;1'].getService(Components.interfaces.nsIRDFService); var BMDS = RDF_SERVICE.GetDataSource("rdf:bookmarks"); // add the observer to datasource BMDS.RemoveObserver(rdfObserver); BMDS.AddObserver(rdfObserver);
References:
Interface Reference – nsIRDFObserver
Interface Reference – nsIRDFDataSource
nsIRDFObserver を使ってブックマークのデータソースの動きを調べる
English version of this post is also available.
nsIRDFObserver によってRDFデータソースに起こった様々な変化を監視することができる。ブックマークのデータも Firefox の内部ではRDFデータソースとして管理されているので、後述するような nsIRDFObserver によってブックマークのデータがどのように管理されているかを、エラーコンソール(JavaScript コンソール)を使って調べることができる。
まずは、データソースを監視するための nsIRDFObserver オブジェクトを定義する。なお、 _targetToString と _log は、独自に定義したプライベートなメソッドである。
var rdfObserver = { onAssert : function(aData, aRes, aProp, aTarget) { this._log(["onAssert", aData.URI, aRes.Value, aProp.Value, this._targetToString(aTarget)].join(" ")); }, onBeginUpdateBatch : function(aData) { this._log(["onBeginUpdateBatch", aData.URI].join(" ")); }, onChange : function(aData, aRes, aProp, aOldTarget, aNewTarget) { this._log(["onChange", aData.URI, aRes.Value, aProp.Value, this._targetToString(aOldTarget), this._targetToString(aNewTarget)].join(" ")); }, onEndUpdateBatch : function(aData) { this._log(["onEndUpdateBatch", aData.URI].join(" ")); }, onMove : function(aData, aOldRes, aNewRes, aProp, aTarget) { this._log(["onMove", aData.URI, aOldRes.Value, aNewRes.Value, aProp.Value, this._targetToString(aTarget)].join(" ")); }, onUnassert : function(aData, aRes, aProp, aTarget) { this._log(["onUnassert", aData.URI, aRes.Value, aProp.Value, this._targetToString(aTarget)].join(" ")); }, /** * nsIRDFNode から適切なインタフェースを参照し、String型の値を取得する */ _targetToString : function(aTarget) { const Ci = Components.interfaces; if ( aTarget instanceof Ci.nsIRDFLiteral ) // 文字列 return aTarget.QueryInterface(Ci.nsIRDFLiteral).Value; else if ( aTarget instanceof Ci.nsIRDFInt ) // 数値 return aTarget.QueryInterface(Ci.nsIRDFInt).Value; else if ( aTarget instanceof Ci.nsIRDFDate ) // 日時 return aTarget.QueryInterface(Ci.nsIRDFDate).Value; else // 他にもあったっけ? return ""; }, /** * 文字列をエラーコンソールへ出力 */ _log : function(aMsg) { var consoleSvc = Components.classes['@mozilla.org/consoleservice;1'].getService(Components.interfaces.nsIConsoleService); consoleSvc.logStringMessage(aMsg); }, };
あとは、ブックマークのデータソースに対して先ほどのオブザーバを追加してやるだけで良い。
// ブックマークのデータソースを取得 var rdfSvc = Components.classes['@mozilla.org/rdf/rdf-service;1'].getService(Components.interfaces.nsIRDFService); var bmds = rdfSvc.GetDataSource("rdf:bookmarks"); // データソースへオブザーバを追加 bmds.RemoveObserver(rdfObserver); bmds.AddObserver(rdfObserver);
リファレンス:
Interface Reference – nsIRDFObserver
Interface Reference – nsIRDFDataSource
nsIZipWriter @ Google Summer of Code 2006
4月に行われた Mozilla Party にて、ファイルを解凍したりするためのXPCOMである nsIZipReader はあるけど、逆にファイルを圧縮して書庫を作るためのXPCOM(つまり nsIZipWriter)が今のところ無く、将来的にこれが toolkit の一部として Firefox 本体に実装されたら、拡張機能でできることが広がって大変喜ばしいのであるが…という話をしたところ、Darin Fisher 氏からサマーなんたらにて発表?されるという情報をいただいた。
これについて調べたところ、 Mozilla Wiki に Google Summer of Code ついてのページがあり “Create a scriptable jar writer or zip writer” としてアイデアがエントリされているのを発見した。
Community:SummerOfCode06 – MozillaWiki
Summer of Code とはどうやら学生参加型のオープンソース開発プログラムのようである。この Wiki のページを見ると他にも色々と面白そうなアイデアが書いてある。
ユーザ名とパスワードを入力するプロンプト
ユーザ名とパスワードを入力するプロンプトはわざわざ自前で作らなくても nsIPromptService で可能。なお、 promptUsernameAndPassword メソッドの6,7番目の引数を利用してチェックボックス付きのプロンプトにすることもできる。
var user = { value : "" }, pass = { value : "" }; var promptSvc = Cc["@mozilla.org/embedcomp/prompt-service;1"] .getService(Ci.nsIPromptService); var ret = promptSvc.promptUsernameAndPassword( window, "Authorization", "Enter username and password.", user, pass, null, {} ); if (ret) alert("user:" + user.value + " pass:" + pass.value); else alert("canceled.");
パスワード管理
Webサービスを利用するXULアプリでは、たいていの場合、ログインするためのユーザ名やパスワードが付きものだ。入力したユーザ名やパスワードを記憶するために、 nsIPrefService などを利用して pref.js へ保存する?それは簡単だが好ましい方法ではない。
XULにはURIに関連付けてユーザ名やパスワードを管理する仕組みがきちんと提供されているのである。パスワードを保存したり削除したりするには nsIPasswordManager を、保存されたパスワードを問い合わせるには nsIPasswordManagerInternal を利用する。
const PM = Cc["@mozilla.org/passwordmanager;1"].getService(Ci.nsIPasswordManager); const PMI = Cc["@mozilla.org/passwordmanager;1"].getService(Ci.nsIPasswordManagerInternal);
例えば、 http://www.example.com に関連付けられたパスワードを問い合わせるには、以下のようにする。
var host = { value : "" }, user = { value : "" }, pass = { value : ""}; try { PMI.findPasswordEntry("http://www.example.com", null, null, host, user, pass); } catch (ex) { // 該当するユーザ名やパスワードが見つからない } alert("host:" + host.value + " user:" + user.value + " pass:" + pass.value);
http://www.example.com のユーザ名 foo のパスワード bar を保存するには、以下のようにする。どうやら同じユーザ名で別のパスワードを保存しようとしてもうまくいかないようなので、一度 removeUser で削除した後、 addUser で追加するという方法である。
try { PM.removeUser("http://www.example.com", "foo"); } catch (ex) { // ユーザ名 foo が存在しない場合、例外発生 } try { PM.addUser("http://www.example.com", "foo", "bar"); } catch (ex) { // 何らかの例外 }
なお、Firefox では保存されたユーザ名やパスワードが ツール > オプション > プライバシー > パスワード > 保存されているパスワードを表示 でパスワードマネージャを開くことができる。
リファレンス:
Interface Reference – nsIPasswordManager
Interface Reference – nsIPasswordManagerInternal
textboxのundo/redo履歴をクリアする
Firefox 2.0 以降であれば、 textbox 要素の editor プロパティから nsIEditor にアクセスし、 nsIEditor の transactionManager プロパティからアンドゥやリドゥについての色々な操作が可能である。
例えばアンドゥ/リドゥの履歴をクリアするには以下のようにすればよい。
document.getElementById("myTextbox").editor.transactionManager.clear();
ちなみに chrome://browser/content/sanitize.js を見ると、
searchBar.textbox.editor.enableUndo(false); searchBar.textbox.editor.enableUndo(true);
というやり方をしている。これでも同じ結果が得られるようだ。
リファレンス:
Interface Reference – nsIEditor
Interface Reference – nsITransactionManager
ScrapBookのバグBug 13244 – Undo works throughout multiple notes がめでたくFixできそうだ。