« userChrome.js についてのアンケート | マウスジェスチャについてのアンケート結果 » |
ブックマークツリーの右クリックメニュー実装方式
Trunkへ正式に搭載されたことだし、そろそろPlacesの仕組みとかを勉強していこうかと思ったが、まず最初に気になるツリーの右クリックメニューの実装方式を見て一安心した。 chrome://browser/content/places/placesOverlay.xul を見ればわかるとおり、右クリックメニューが普通の popup 要素と menuitem 要素の集まりで実装されているのだ。これなら拡張機能が右クリックメニューへ独自のメニューを追加したければ、通常の XUL のオーバーレイで実現できる。
というのも、 Firefox 2 でのブックマークツリーの右クリックメニューは、右クリックでしたときの popupshowing イベント発生時に BookmarksCommand.createContextMenu という JavaScript 関数がすべての menuitem 要素を document.createElementNS で動的に生成してメニューを表示するという実装方式なのだ。これが大変厄介で、拡張機能から右クリックメニューへ新たなメニューを追加しようとしても、以下のような醜いやり方をせざるを得ない(もしかすると、別のもっと美しいやり方もあるかも?)。
方法1 BookmarksCommand.createContextMenu を、本来の関数を内包する独自の関数へ置き換える
これは Locate in Bookmark Folders という拡張機能で見かけたやり方だが、 menuitem 要素を動的に生成している関数を包含した独自の関数へ置き換えてしまい、ひとつの popupshowing イベントハンドラ内で Firefox 本来のメニューと拡張機能独自のメニューの両方を動的に生成する方法だ。
BookmarksCommand.originalCreateContextMenu = BookmarksCommand.createContextMenu; BookmarksCommand.createContextMenu = function(aEvent, aSelection, aDS) { // まずはじめにオリジナルの関数を呼び出してから... this.originalCreateContextMenu(aEvent, aSelection, aDS); // document.createElement で menuitem 要素を生成し、 menupopup 要素 (aEvent.target) へ appendChild する。 // 右クリックメニューの末尾ではなく特定位置へメニューを追加したければ insertBefore する。 };
方法2 右クリックメニューに対する popupshowing イベントを追加し、 JavaScript によって menuitem 要素をさらに追加する
まず、 chrome://browser/content/bookmarks/bookmarksTree.xml#bookmarks-tree-name を見るとわかるように、ブックマークツリーの右クリックメニューのポップアップ (menupopup 要素) は XBL で定義された bookmarks-tree 要素内の匿名要素であり、イベントハンドラ追加のために要素へアクセスするには、
var bmtree = document.getElementById("bookmarks-view"); var bmpopup = document.getAnonymousNodes(bmtree)[1]; // ちなみに[0]は[Object comment]
あるいは、
var bmtree = document.getElementById("bookmarks-view"); var bmpopup = document.getAnonymousElementByAttribute(bmtree, "onclick", "event.stopPropagation();");
なんていうややトリッキーなやり方をするしかないようだ。こうして得られたポップアップに対して、イベントハンドラを追加する。
function injectMenuItems(event) { // document.createElement で menuitem 要素を生成し、 menupopup 要素 (event.target) へ appendChild する。 // 右クリックメニューの末尾ではなく特定位置へメニューを追加したければ insertBefore する。 } bmpopup.addEventListener("popupshowing", injectMenuItems, false);