まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。
この広告は、90日以上更新していないブログに表示しています。
Firefox拡張機能を開発する時に、Firefox のツールバー周りのことがよくわからなかったので色々調べてみた。 ドキュメントを調べただけじゃなくて、実際の動作を見たりFirefox のソースコード (Firefox 20 beta 5 のソースコード) を見たりして調べたこともあるので、全部が全部ここに書かれていることが正しいとは限らない。 個人用メモ程度。 指摘等歓迎。
toolbar の currentSet プロパティをいじるなら、document の readyState が "complete" になったあとにしないとおかしくなるので注意。
そういうわけなので、ブートストラップ型の拡張機能を開発している場合で、ツールバー項目を追加する場合には、Chrome ウィンドウの readyState が "complete" になるまでに toolbarpalette にツールバー項目を追加しておくとよい。 そうするとFirefox 側の機能で自動的にツールバー項目が適切な位置に配置される。 (それより遅いタイミングでツールバー項目を追加する場合は、拡張機能側でどこに追加するかを指定しなければならない。)
ちなみにブートストラップ型の拡張機能のインストール直後には既にはChrome ウィンドウは存在しているはずなので、そのタイミングでは適切な位置に拡張機能が挿入する必要がある。 アップデート時にはアップデート前と同じ位置に挿入する必要がある。 上記の 「readyState が "complete" になるまでに...」 というのはあくまでFirefox 再起動後の拡張機能の setup 時の話である。
toolbarpalette 要素にツールバー項目を追加する方法は次の節を参照のこと。
なので、id="BrowserToolbarPalette" の toolbarpalette 要素に子要素を追加するには、条件分岐しなければならない。
var browserToolbarPaletteElem =document.getElementById("BrowserToolbarPalette") ||document.getElementById("navigator-toolbox").palette;browserToolbarPaletteElem.appendChild(newToolbarItem);
基本的にはXUL オーバーレイが使える場合は特に上記のようなことをする必要は無いと思うが、動的にツールバー項目を追加する場合や、XUL オーバーレイが使えないブートストラップ型の拡張機能を開発する場合等は上のようにすれば良いと思われる。
toolbar の collapsed プロパティをいじることで、ツールバーを非表示にしたり表示したりできる。 アドオンバーは初期状態で非表示なので、拡張機能の初回実行時にアドオンバーにツールバー項目を追加する場合は、ついでにアドオンバーを表示するようにするのが良さそう。
toolbar の collapsed プロパティを false にするだけだと永続化されないので collapased 属性も書き換えて、document.persist メソッドを使って永続化するのが良い。
// 拡張機能の初回起動時の処理; これらは document.readyState が "complete" になったあとに実行すべき (?)toolbar.collapsed =false;toolbar.setAttribute("collapsed","false");// プロパティを変更したら属性も変更されるのかもしれないが未確認document.persist(toolbar.id,"collapsed");
piroor さんにより公開されているブートストラップ型のXUL ベースの拡張機能のテンプレート (restartless) の中に、ToolbarItem というモジュールがあるので、ブートストラップ型の拡張機能でツールバー項目を使いたい場合はこれを使うとか参考にすると良さそう。
最初にこのモジュールを読んだときは何をしているのか全然分からなかったけど、前提知識としてこの記事に書いたような内容を知っていれば大体読めそうな気がする。
まず、以下のようにオーバーレイを指定するXML ファイルで、id="BrowserToolbarPalette" の toolbarpalette 要素に toolbarbutton 要素を追加するように書いておく。
<?xml version="1.0"?><overlayid="Screenshot-xulOverlay"xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"><toolbarpaletteid="BrowserToolbarPalette"><toolbarbuttonid="sample-toolbar-button"label="サンプルツールバーボタン"image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="tooltiptext="サンプルです"></toolbarbutton></toolbarpalette><scripttype="text/javascript; version=1.8"src="overlay.js"charset="utf-8" /></overlay>
上のXML ファイルで読み込まれる overlay.js として以下のようなファイルを作っておく。
(function (){"use strict";// https://developer.mozilla.org/en-US/docs/Code_snippets/Toolbar のサンプルコードをベースに少し書き換えた関数function installButton(toolbarId, id, afterId){var doc =window.document;// 既にどこかに配置されている場合は位置変更しないif (!doc.getElementById(id)){var toolbar = doc.getElementById(toolbarId);// If no afterId is given, then append the item to the toolbarvar before =null;if (afterId){var elem = doc.getElementById(afterId);if (elem && elem.parentNode == toolbar) before = elem.nextElementSibling;}// 1 回実行すれば, 次に別のウィンドウを開くときにはこの設定が使われる toolbar.insertItem(id, before); toolbar.setAttribute("currentset", toolbar.currentSet); doc.persist(toolbar.id,"currentset");// 属性値の永続化// 追加先のツールバーが非表示なっている可能性があるので, 表示する toolbar.setAttribute("collapsed","false"); doc.persist(toolbar.id,"collapsed");// 属性値の永続化}}window.addEventListener("load",function el(evt){if (evt.target !==window.document)return;window.removeEventListener("load", el,false);// オーバーレイされたときに毎回この関数を呼び出すと、ユーザーがボタンを// 表示したくない場合にもアドオンバーに追加されてしまう。 実際の拡張機能では// Preferences などで管理して、最初に 1 回だけ実行するようにすること。 installButton("addon-bar","sample-toolbar-button");},false);}).call(this);
そうすると、XUL オーバーレイされるたびに (つまり、新しいFirefox アプリケーションウィンドウが開かれるたびに) JS ファイルが実行され、この拡張機能のツールバーボタンがまだツールバーに表示されていない場合は、アドオンバーに追加されるようになる。 コメント中にも書いているように、実際のFirefox 拡張では、初回起動時のみに実行する様にするなどの配慮が必要である。
ブートストラップ型のXUL ベースの拡張機能では、XUL オーバーレイが使えない。 なので、ツールバー項目を追加するには JS 側から追加してやる必要がある。XUL オーバーレイで の toolbarpalette 要素にツールバー項目を追加するのと大体同じことをしようと思うと、以下のような感じにすれば良さそう。
下記は bootstrap.js のサンプルコードである。 インストール時や無効になっていた拡張機能が有効にされたときなど、Firefox のアプリケーションウィンドウが既に存在する状態で startup 関数が実行されたときには、既に表示されているウィンドウの toolbarpalette 要素にツールバー項目を追加する。 また、ウィンドウの新規立ち上げを監視し、必要に応じて新しいウィンドウの toolbarpalette 要素にツールバー項目を追加する。 初期状態でどこかのツールバー上に項目を表示したりはしない。 ユーザーによる項目移動がなされた場合はFirefox アプリケーション側で位置が保存されるので、拡張機能側ではどこに表示するかを覚えておくことはしていない。
ブートストラップ型のXUL ベースの拡張機能でツールバーボタンを追加する方法を調べてると 「Firefox 拡張側でツールバーボタンの挿入位置を覚えておくようにして、ツールバーボタン挿入時に適切な位置に挿入するようにすればよい」 と書かれているものがほとんどだったが、Firefox 拡張側で位置を保存しておく必要は必ずしもないんじゃないかなーと思う。
"use strict";var Cc = Components.classes;var Ci = Components.interfaces;var PromptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci.nsIPromptService);var windowWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci.nsIWindowWatcher);var windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);// window ごとのツールバー項目管理var ToolbarItemManager =function (win){this._win = win;};ToolbarItemManager.prototype.setupToolbarItems =function (){var doc =this._win.document;if (doc.readyState ==="complete"){this._addToolbarItemsToPalette();this._reinitializeToolbars();}else{this._addToolbarItemsToPalette();// 配置は Firefox がやってくれる}};ToolbarItemManager.prototype._addToolbarItemsToPalette =function (){var doc =this._win.document;var toolbarItemElems =this._createToolbarItemElems();this._toolbarItemElems = toolbarItemElems;var paletteElem = doc.getElementById("BrowserToolbarPalette") || doc.getElementById("navigator-toolbox").palette;if (!paletteElem){throw"デフォルトのツールバーボタンパレットが見つかりません";} toolbarItemElems.forEach(function (toolbarItemElem){ paletteElem.appendChild(toolbarItemElem);});};ToolbarItemManager.prototype._reinitializeToolbars =function (){var win =this._win;var doc =this._win.document;var navToolboxElem = doc.getElementById("navigator-toolbox");var NS ="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";var toolbarElems =Array.prototype.slice.call(doc.getElementsByTagNameNS(NS,"toolbar")); toolbarElems.forEach(function (toolbarElem){if (toolbarElem.toolbox !== navToolboxElem)return;var curset = toolbarElem.getAttribute("currentset"); toolbarElem.currentSet = curset;});};// パレットに追加するツールバー項目の配列を返す// ブートストラップ型でない拡張機能における id="BrowserToolbarPalette" の// toolbarpalette 要素へのオーバーレイに相当ToolbarItemManager.prototype._createToolbarItemElems =function (){var doc =this._win.document;var NS ="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";var e = doc.createElementNS(NS,"toolbarbutton");var id ="toolbar"; e.setAttribute("label","Test Button! " +Date.now()); e.setAttribute("id", id);// 小さな赤丸 e.setAttribute("image","data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAU" +"AAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO" +"9TXL0Y4OHwAAAABJRU5ErkJggg==");return[e];};// 終了処理ToolbarItemManager.prototype.releaseToolbarItems =function (){var toolbarItemElems =this._toolbarItemElems;if (!toolbarItemElems)return; toolbarItemElems.forEach(function (e){var pe = e.parentNode;if (pe){ pe.removeChild(e);}});};// ToolbarItemManager を使って全ウィンドウのツールバー項目を管理var ToolbarItemManagerForAllWindows =function (){this._finalizerSet =new Set();};var finalizerSet =new Set();ToolbarItemManagerForAllWindows.prototype.initForWindow =function (win){var finalizerSet =this._finalizerSet;var toolbarButtonManager =new ToolbarItemManager(win);var finalizer =function (){ win.removeEventListener("unload", unloadEventListener,false); toolbarButtonManager.releaseToolbarItems(); finalizerSet.delete(finalizer); finalizer = void 0; unloadEventListener = void 0;};var unloadEventListener =function (evt){if (evt.target !== win.document)return; finalizer.call(null);}; win.addEventListener("unload", unloadEventListener,false); finalizerSet.add(finalizer); toolbarButtonManager.setupToolbarItems();};ToolbarItemManagerForAllWindows.prototype.finalizeForAllWindows =function (){var finalizerSet =this._finalizerSet;for (var finalizer of finalizerSet){ finalizer.call(null);}};var toolbarItemManagerForAllWindows =new ToolbarItemManagerForAllWindows();// 既に表示されているウィンドウに対する初期化処理function _initForWindows(){var type ="navigator:browser";var enumerator = windowMediator.getEnumerator(type);while(enumerator.hasMoreElements()){// |win| は [Object ChromeWindow] である (|window| と同等)。これに何かをするvar win = enumerator.getNext(); toolbarItemManagerForAllWindows.initForWindow(win);}}// 新たに開かれたウィンドウに対する初期化処理を行うためのオブザーバvar windowObserver ={ observe:function (aSubject, aTopic, aData){var win = aSubject.QueryInterface(Components.interfaces.nsIDOMWindow);var targetLoc ="chrome://browser/content/browser.xul";if (aTopic ==="domwindowopened"){ win.addEventListener("DOMContentLoaded",function el(evt){// DOMContentLoaded 前に確認すると win.location.href が "about:blank" だったりしたif (win.location.href !== targetLoc){// 対象とする window でない場合はイベントリスナを解除して終了 win.removeEventListener("DOMContentLoaded", el,false);return;}// DOMContentLoaded は何回か発生するので, 対象のものでない場合は何もしないif (evt.target !== win.document)return; win.removeEventListener("DOMContentLoaded", el,false); toolbarItemManagerForAllWindows.initForWindow(win);},false);}}};function install(aData, aReason){ PromptService.alert(null,"Bootstrapped Extension Sample","Installed");}function startup(aData, aReason){ PromptService.alert(null,"Bootstrapped Extension Sample","Startup!"); _initForWindows(); windowWatcher.registerNotification(windowObserver);}function shutdown(aData, aReason){ PromptService.alert(null,"Bootstrapped Extension Sample","Shutdown!"); windowWatcher.unregisterNotification(windowObserver); toolbarItemManagerForAllWindows.finalizeForAllWindows();}function uninstall(aData, aReason){ PromptService.alert(null,"Bootstrapped Extension Sample","Uninstall!");}
引用をストックしました
引用するにはまずログインしてください
引用をストックできませんでした。再度お試しください
限定公開記事のため引用できません。