Movatterモバイル変換


[0]ホーム

URL:


Skip to content.Skip to navigation
Advanced Search…

ありえるえりあ

Sections
Personal tools
You are here:Home原稿・資料ワークス、アリエル共同勉強会Firefox拡張機能(extension)の作り方
Document Actions
  • Send this page to somebody
  • Print this page
  • Toggle full screen mode

Firefox拡張機能(extension)の作り方

Firefox拡張機能(extension)の作り方を説明します。

Firefox 拡張機能(extension)の作り方

Firefox 拡張機能とは

Firefox add-onの一種です。

add-onは次のように分類できます。

  • plugin ...実体はexeやdll。C++で作成。素人にはお勧めしません
  • 検索バー ...実体はXMLの設定ファイルのみ。見れば分かるので説明はしません
  • スペルチェッカ ...日本語には無縁なので未調査(たぶんファイルを作るだけ)
  • 拡張機能 ...実体はXML、JavaScriptとCSS。必要なら、C++で書くXPCOM。これから説明します
  • テーマ ...拡張機能のサブセット。CSSのみの場合をテーマと呼びます

開発環境

  • firefox2
  • firebug
  • 普通に使えるエディタ

以下、MS-Windowsを仮定していますが、他のOSでもファイルパス以外は同じです。

前提となる概念の説明

  • chrome(クローム)
  • XUL(ズール。"pronounced zool and it rhymes with cool")

chromeとは(1)

参考サイト

上記サイトの説明によれば「chromeはアプリケーションウィンドウのUI要素のセット」です。しかし、この説明で意味が分かる人は奇跡的に勘が良い人でしょう。

chromeが何かを知るには、Mozillaアーキテクチャを理解する必要があります。

chromeとは(2)

Mozillaアーキテクチャはコア機能の上に各種chrome(とXPCOM実装コードのセット)が載ることで、様々なアプリケーションを実装しています。

Webブラウザのchrome(firefox)    メーラのchrome(thunderbird)  カレンダーのchrome(sunbird)  その他(#4)               \                     |                       /       コア機能(ネットワーク機能(#1)、レンダリング機能(#2)、JavaScriptインタプリタ(#3)、etc.)                            [based on XPCOM(#5)、NSPR、etc.]
  • (#1) Necko
  • (#2) Gecko
  • (#3) SpiderMonkey
  • (#4) ChatZilla, Composer, etc.
  • (#5) (偉大なる)MS COMのパクリ技術

chromeとは(3)

chromeの実体は主に次の要素からなります。

  • XUL
  • JavaScript
  • CSS

chromeとは(4)

それぞれの主な役割は

  • XUL ...UIコンポーネント(ボタン、メニュー、ラベルなど)の配置を定義
  • JavaScript ...UIコンポーネントのイベントハンドラを実装
  • CSS ...UIコンポーネントのデザイン(レイアウト)を定義

chromeとは(5)

結局chromeは、Gecko(XULとCSSのインタプリタ)とSpiderMonkey(JavaScriptインタプリタ)を実行環境と見なした場合

  • アプリケーションプログラムのGUI部分そのもの

です。

firefox拡張機能は、アプリケーションプログラムのひとつであるfirefoxに手をいれることです。

chrome URI

chrome://browser/content/browser.xul
  • 'browser'の部分; パッケージ名(ローカルPC上で一意)
  • 'content'の部分; 定義済みキーワード('content'、'locale'、'skin'のいずれか)
  • browser.xulの部分; chromeパッケージ内でのファイル名(*)
  • (*)パッケージ名とファイル名の対応はchrome.manifestファイルで宣言します(後述)

XULとは(1)

  • XMLベースのGUI記述言語

HTMLで、GUIコントロールと呼べるものは、ボタン、テキストボックス、プルダウンメニューなど色々あります。しかし、一般的なウィンドウシステム(MS-WindowsやGNOMEなど)が提供するGUIコントロールと比較すると、質、量ともに劣ります(元々の目的が異なるので当然ですが)。

XULは一般的なウィンドウシステムが提供するGUIコントロールと同等のGUIをXMLで記述することを目的としています。

XULとは(2)

XULで定義されたGUIコントロール一覧
http://developer.mozilla.org/en/docs/XUL_Reference

ウィンドウプログラミングに馴染みのある人が見れば、雰囲気が分かると思います。

XULとは(3)

  • 以下の内容のmy.xulファイルを作成してfirefoxで開いてみます。
<?xml version="1.0"?><?xml-stylesheet href="chrome://browser/skin/" type="text/css"?> <window id="my-xul"        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">  <hbox>    <label value="this is a XUL page" id="my-label"/>    <colorpicker type="button"/>  </hbox></window>

XULとは(4)

  • firebugでmy.xulのDOM要素を操作してみます。
var l = $('my-label');l.value += '...';l.onclick = function () {alert('label clicked');}

XULとは(5)

GeckoはHTMLレンダリングエンジンと呼ばれることがありますが、正確にはXULレンダリングエンジンと呼ぶべきです。

Firefoxのメニューバー、ツールバー、ステータスバー、各種ダイアログボックス、すべてXULで記述して、Geckoがレンダリングしています。

XULとは(6)

FirefoxのXULファイルはbrowser.xulです。

MS-Windowsの場合のパスの例(jarを展開するとbrowser.xulファイルがあります)

c:/Program Files/Mozilla Firefox/chrome/browser.jar

XULとは(7)

  • browser.xulの中身をエディタで見ます
  • chrome://browser/content/browser.xul を開くとfirefoxがbrowser.xulをレンダリングします

XULとは(8)

chromeを新規に書き起こせば、GUIアプリケーションを作成可能です。(MS-Windowsとの対比で言えば、VBやDelphiでGUIプログラミングをすることと等価の作業です。GUIコントロールの配置をXULとCSSで記述し、ロジック(イベントハンドラ)の記述をJavaScriptで行います)

Firefox拡張機能と言った場合、既存のchrome(browser.xulがメインファイル)の書き換えを意味します。これを実現するのがXULのoverlay機能です(詳細は後述)。

拡張機能開発の準備(2)

開発用のfirefoxプロファイルを作成します。

  1. firefoxを終了
  2. firefox -ProfileManager を起動
  3. 適当な名前でプロファイルを作成 (e.g. dev)
  4. firefox -no-remote -P dev で起動します(次のbatファイルを作成すると楽)
@echo off: batch file for firefox extension development"C:\Program Files\Mozilla Firefox\firefox" -no-remote -P dev

拡張機能開発の準備(3)

about:configを開いて、以下の4つの値をtrueにします。

  • javascript.options.showInConsole
  • javascript.options.strict
  • extensions.firebug.showChromeErrors
  • extensions.firebug.showChromeMessages

拡張機能開発の準備(4)

about:configを開いて、次を追加します(右クリックメニュー/新規作成/真偽値)

  • nglayout.debug.disable_xul_cache => true

Firefoxを再起動せずに拡張機能を試せます。ただしFirefoxが異常に重くなるので、速いPCで無いと辛いです。

何もしない拡張機能を作って動かしてみる(1)

作業ディレクトリ(e.g. c:/cygwin/home/inoue/src/firefox/)の下に次のようなディレクトリ構成を作成します。(このようなディレクトリをtemplateとして用意して、再利用することを勧めます)

以下、作業ディレクトリのベースを${WORK}と記述します。

${WORK}/my/chrome.manifest          /install.rdf          /chrome/content/my.xul

何もしない拡張機能を作って動かしてみる(2)

chrome.manifestの中身

content     sample    chrome/content/overlay chrome://browser/content/browser.xul chrome://sample/content/my.xul

先頭カラムは定義済みキーワードです('content','locale','skin','overlay','style','override')。先頭カラムによって、後続カラムの文法が決定します。

参照
http://developer.mozilla.org/en/docs/Chrome_Registration

何もしない拡張機能を作って動かしてみる(3)

chrome.manifestの中身の説明

content     sample    chrome/content/

chromeパッケージ名(上記例では'sample')と、ファイルシステム(相対パス指定)の対応を定義します。Firefoxにこのchromeパッケージをインストールすると(インストール方法は後述)、chrome://sample/content/my.xulがファイルシステム上の${WORK}/my/chrome/content/my.xulを指します。

overlay chrome://browser/content/browser.xul chrome://sample/content/my.xul

chrome://sample/content/my.xulでchrome://browser/content/browser.xul(Firefoxのchrome)をoverlayするように指示します(overlayの詳細は後述)。

何もしない拡張機能を作って動かしてみる(4)

install.rdfの中身 (コメントの無い部分はあまり気にしないで下さい)

<?xml version="1.0"?><RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"     xmlns:em="http://www.mozilla.org/2004/em-rdf#">  <Description about="urn:mozilla:install-manifest">    <em:id>{a58fa49f-acb8-43f8-b3b5-e69f552f6a7d}</em:id>   <!-- 拡張機能ごとにUUIDを生成(*) -->    <em:version>1.0</em:version>  <!-- 拡張機能のバージョン -->    <em:type>2</em:type>  <!-- 2:拡張機能、4:テーマ、8:ロケール、16:プラグイン、32:... -->       <!-- Target Application this extension can install into,          with minimum and maximum supported versions. -->     <em:targetApplication>      <Description>        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>  <!-- Firefox固有のUUID -->        <em:minVersion>1.5</em:minVersion>        <em:maxVersion>2.0.0.*</em:maxVersion>      </Description>    </em:targetApplication>       <!-- Front End MetaData -->    <!-- 拡張機能の説明(人間用) -->    <em:name>my sample</em:name>    <em:description>A test extension</em:description>    <em:creator>inoue@ariel-networks.com</em:creator>    <!--em:homepageURL>http://dev.ariel-networks.com/</em:homepageURL-->  </Description>      </RDF>
  • (*)名前が被らなければ任意名でも可
参照
http://developer.mozilla.org/ja/docs/install.rdf

何もしない拡張機能を作って動かしてみる(5)

my.xulの中身

<?xml version="1.0"?><overlay id="sample"        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"></overlay>

何もしない拡張機能を作って動かしてみる(6)

動かす方法

次のディレクトリ(ユーザ名と${id}の部分は環境依存します)

c:/Documents and Settings/inoue/Application Data/Mozilla/Firefox/Profiles/${id}/extensions/

に、次の名前のファイルを作成します。

c:/Documents and Settings/inoue/Application Data/Mozilla/Firefox/Profiles/${id}/extensions/{a58fa49f-acb8-43f8-b3b5-e69f552f6a7d}

カッコ内のuuidはinstall.rdfの/RDF/Description/em:idの値に対応します。

このファイルの中身に拡張機能の実体へのパスを書きます。

c:\cygwin\home\inoue\src\firefox\my\

Firefoxを起動すると、sample拡張機能(chrome://sample/content/my.xul)が有効になります。(nglayout.debug.disable_xul_cacheがtrueであれば、新規ウィンドウを開くだけでOK)

XULのoverlay機能(1)

Firefox拡張機能の最初の一歩は、browser.xulをoverlayで書き換えることです。

XULのXML要素のid属性の値をキーにして、上書きを指定する場所を指定します。

browser.xulの調べ方

  • browser.xulファイルの中身を読む
  • chrome://browser/content/browser.xulを開いて、firebugで調査

XULのoverlayの実例(1)

例えば、browser.xul内のステータスバー定義は次のようになっています。

<statusbar class="chromeclass-status" id="status-bar"           ondragdrop="nsDragAndDrop.drop(event, contentAreaDNDObserver);">  <statusbarpanel id="statusbar-display" flex="1"/>  ...省略  </statusbarpanel></statusbar>

my.xulでoverlayするには次のように書きます。

<overlay id="sample"          xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <statusbar id="status-bar">  <statusbarpanel id="my-panel" label="Hello, Firefox extension"/> </statusbar></overlay>

XULのoverlayの実例(2)

メニューバーへのメニュー追加をoverlayで書く例

<overlay id="sample"          xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">  <menupopup id="menu_ToolsPopup">    <menuitem label='my menu' oncommand='alert("foobar")'/>  </menupopup></overlay>

メニューからコマンド実行の実例(1)

<menuitem label='my menu' oncommand='my_func()'/>  <!-- JavaScript関数呼び出し -->

JavaScript関数定義の参照方法(1)

<overlay id="sample"          xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">  <script type="application/x-javascript"><![CDATA[  function my_func() {    alert('my-func');  }  ]]></script></overlay>

JavaScript関数定義の参照方法(2)

<overlay id="sample"          xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">  <script type="application/x-javascript"  src="my.js"/>  <!-- 別ファイル --></overlay>

メニューからコマンド実行の実例(2)

メニュー、ツールバー、キーボードショートカットから同じコマンドを呼ぶ場合、次のような方法が良いでしょう。

<overlay id="sample"          xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">  <commandset id="mainCommandSet">    <command id='MyCommand' oncommand='alert("my command")'/>  </commandset>  <menupopup id="menu_ToolsPopup">    <menuitem label='my menu' command='MyCommand'/>  </menupopup></overlay>

拡張機能の(私的)分類

  • ユーザ操作で動く拡張機能(メニュー、ツールバー、サイドバーから実行)
  • 文書オープン時に動く拡張機能
  • タイマードリブンで動く拡張機能

ユーザ操作で動く拡張機能の実例

  • メニューから実行して画像を削除する拡張機能
<overlay id="sample"          xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">  <menupopup id="menu_ToolsPopup">    <menuitem label='hide images' oncommand='hideImages()'/>  </menupopup>  <script type="application/x-javascript"><![CDATA[  function hideImages() {    var imgs = window.content.document.images;    for (var i = 0, len = imgs.length; i < len; i++) {      imgs[i].style.display = 'none';    }  }  ]]></script></overlay>
  • # この程度ならbookmarkletで充分ですが...

文書オープン時に動く拡張機能の雛型

<overlay id="sample"        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">  <script type="application/x-javascript" src="my.js" /></overlay>
// my.js templatevar MyContLoad = {  init: function() {    window.removeEventListener("load", MyContLoad.init, false);    // global initialization code        window.addEventListener("DOMContentLoaded", MyContLoad.onContentLoad, false);//新規文書ごとに挙がるイベント  },  onContentLoad: function() {    // alert('load');  }};window.addEventListener('load', MyContLoad.init, false);  //新規ウィンドウごとに挙がるイベント
  • このパターンはgreasemonkeyを使うのがお勧め(greasemonkey: このパターンのディスパッチ機能を提供する拡張機能)

タイマードリブンで動く拡張機能の雛型

<overlay id="sample"        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">  <script type="application/x-javascript" src="my.js" /></overlay>
// my.js templatevar MyTimer = {  init: function() {    window.removeEventListener("load", MyTimer.init, false);    setInterval(MyTimer.onTimer, 60 * 1000); // 1 min.  },  onTimer: function() {    // alert('load');  }};window.addEventListener('load', MyTimer.init, false);

タイマードリブンで動く拡張機能の実例

  • XMLHttpRequestでJSONデータを取得してステータスバーに結果を表示
var MyAirOne = {  init: function() {    MyAirOne.getFreqMinute();    window.removeEventListener("load", MyAirOne.init, false);    MyAirOne.getHeadline();    setInterval(MyAirOne.getHeadline, MyAirOne.getFreqMinute() * 60 * 1000);  },  getHeadline: function() {    var req = new XMLHttpRequest();    req.open('get', 'http://localhost:6809/1/aircafe/get-headline-num', true);    req.onreadystatechange = function(ev) {      if (req.readyState == 4 && req.status == 200) {        var data = eval(req.responseText); // ({count:$room-number, $room-id1:$count1, $room-id2:$count2, ... })        var count = data.count;        if (count == 0) {          document.getElementById('airone-statusbar-image').src = 'chrome://my-airone/content/airone-notice1.ico';        } else {          document.getElementById('airone-statusbar-image').src = 'chrome://my-airone/content/airone-notice2.ico';        }      }    }    req.send(null);  },  getFreqMinute: function() { // default is 3min    try {      var prefManager = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);      var freq = prefManager.getIntPref('extensions.airone.checkfreq');      return freq > 0 ? freq : 3;     } catch (e) {      return 3;    }  },  openPrefs: function() {    window.openDialog("chrome://my-airone/content/prefs.xul", 'Preferences', 'chrome,titlebar,toolbar,centerscreen,modal');  }};window.addEventListener('load', MyAirOne.init, false);

その他の話題

  • 国際化(メッセージ翻訳)
  • XBL
  • Preference
  • ポップアップウィンドウ

国際化(メッセージ翻訳)

  • JavaScript
  • XUL

# 文字コードはutf8

国際化(chrome.manifest)

locale my ja-JP chrome/locale/ja-JP/

myというパッケージ名と相対ファイルパス(chrome/locale/ja-JP/)を結び付けます。

  • ${WORK}/my/chrome/locale/ja-JP/my.propertiesをchrome://my/locale/my.propertiesで参照可能にします。
  • ${WORK}/my/chrome/locale/ja-JP/my.dtdをchrome://my/locale/my.dtdで参照可能にします。

JavaScriptのメッセージ翻訳(1)

${WORK}/my/chrome/locale/ja-JP/my.properties
msg.hello=こんにちは

JavaScriptのメッセージ翻訳(2)

${WORK}/my/chrome/content/my.xul 抜粋
<script type="application/x-javascript" src="my.js" /><stringbundleset id="stringbundleset">  <stringbundle id="my-msg-bundle" src="chrome://my/locale/my.properties" /></stringbundleset>

JavaScriptのメッセージ翻訳(3)

${WORK}/my/chrome/content/my.js 抜粋
alert(document.getElementById('my-msg-bundle').getString('msg.hello'));

XULのメッセージ翻訳(1)

${WORK}/my/chrome/locale/ja-JP/my.dtd
<!ENTITY my.msg.hello "こんにちは">

XULのメッセージ翻訳(2)

${WORK}/my/chrome/content/my.xul
<?xml version="1.0"?><!DOCTYPE window SYSTEM "chrome://my/locale/my.dtd"><overlay id="sample"          xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">  <menupopup id="menu_ToolsPopup">    <menuitem label='&my.msg.hello;' oncommand='alert("&my.msg.hello;")'/>  </menupopup></overlay>

XBL(1)

  • cssを仲介することで、XULのメタ言語のように振舞う(XULにadvice)
${WORK}/my/chrome/content/my.xul
<?xml version="1.0"?><?xml-stylesheet href="my.css" type="text/css"?><overlay id="sample"          xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">  <menupopup id="menu_ToolsPopup">    <menuitem class="my"/>  </menupopup></overlay>

XBL(2)

${WORK}/my/chrome/content/my.xml
<?xml version="1.0"?><bindings id="my-xbl"      xmlns="http://www.mozilla.org/xbl"      xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"      xmlns:xbl="http://www.mozilla.org/xbl">  <binding id="my-binding">    <content>      <children/>      <label value='my xbl'/>    </content>  </binding></bindings>

XBL(3)

${WORK}/my/chrome/content/my.css
menuitem.my { -moz-binding: url('chrome://my-xbl/content/my.xml#my-binding'); }

Preference(1)

// プリファレンス値の取得例var prefManager = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);var freq = prefManager.getIntPref('extensions.my.checkfreq');  //名前が被らないように extensions prefixで始めるのが良い// プリファレンス設定画面を出す例window.openDialog("chrome://my/content/prefs.xul", 'Preferences', 'chrome,titlebar,toolbar,centerscreen,modal');

Preference(2)

${WORK}/my/chrome/content/prefs.xul
<?xml version="1.0"?><?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?><prefwindow  id="prefs" title="preferences"  buttons="accept,cancel"  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">  <prefpane id="my-prefpane">    <preferences>      <preference id="my-checkfreq" name="extensions.my.checkfreq" type="int" /> <!-- id属性が後から参照される -->    </preferences>    <hbox align="center">      <label control="checkfreq-field" value="frequency" />      <textbox id="checkfreq-field" preference="my-checkfreq" size="2" cols="2" /> <!-- ユーザに入力させるテキストボックス。preference属性の値が上記のid値を参照して -->    </hbox>  </prefpane></prefwindow>

ポップアップウィンドウ

var alertsService = Components.classes["@mozilla.org/alerts-service;1"]  .getService(Components.interfaces.nsIAlertsService);alertsService.showAlertNotification('chrome://my/content/my.png',                                    'popup title', 'popup content',                                    true, '', null);

Copyright(C) 2001 - 2006Ariel Networks, Inc. All rights reserved.

[8]ページ先頭

©2009-2025 Movatter.jp