Firefox 拡張機能開発ワークショップ in OSC Kansai 2009

nanto_vi, 2009-07-11

自己紹介

[図: はてなブックマーク Firefox 拡張]

実際に拡張を作ってみる

[図: Context History]

拡張を作るにあたって

[図: Mozilla プラットフォーム]

準備

<?xml version="1.0" encoding="utf-8"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns:em="http://www.mozilla.org/2004/em-rdf#">
  <rdf:Description rdf:about="urn:mozilla:install-manifest">
    <em:id>contexthistory@extdev.org</em:id>
    <em:type>2</em:type>
    <em:name>Context History</em:name>
    <em:version>0.1</em:version>
    <em:description>Show recent pages in the context menu.</em:description>
    <em:creator>nanto_vi</em:creator>
    <em:homepageURL>http://example.org/context-history</em:homepageURL>
    <em:targetApplication>
      <rdf:Description>
        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
        <em:minVersion>3.5</em:minVersion>
        <em:maxVersion>3.5.*</em:maxVersion>
      </rdf:Description>
    </em:targetApplication>
  </rdf:Description>
</rdf:RDF>

オーバーレイの作成

<?xml version="1.0" encoding="utf-8"?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

</overlay>
content contexthistory chrome/content/
overlay chrome://browser/content/browser.xul chrome://contexthistory/content/contexthistory.xul

現在のファイル構成

コンテキストメニューへの項目の追加

[図: 「最近表示したページ」メニューの追加]

スクリプトの作成

var ContextHistory = {
  menu: null,
  popup: null,

  init: function CH_init() {
    this.menu = document.getElementById("ctxhst-recent-pages");
    this.popup = this.menu.menupopup;
  },
};

window.addEventListener("load", function () ContextHistory.init(), false);

式クロージャ

JavaScript 1.8 (Firfox 3) の新機能。次の 2 つは同じ意味。

function f() { return expr; }
function f() expr

オブジェクトリテラルの末尾要素のカンマ

ECMAScript 3 では不正だが、JavaScript では妥当。ECMAScript 5 でも妥当になる予定。

var o = { foo: 42, bar: 23, }

メニューの動的な作成: タイミング

[図: イベントの伝播]

document.addEventListener("popupshowing", function () 1, true);
document.addEventListener("popupshowing", function () 4, false);
menupopup.addEventListener("popupshowing", function () 2, true);
menupopup.addEventListener("popupshowing", function () 3, false);
// Web アプリケーション

function addEvent(target, type, listener) {
  if (target.addEventListener)
    target.addEventListener(type, listener, false);
  else if (target.attachEvent)
    target.attachEvent("on" + type, listener);
}

addEvent(target, function (event) { ... });
// XUL アプリケーション

var listener = {
  doSomething: function () { ... },
  handleEvent: function (event) {
    this.doSomething();
  },
};

target.addEventListener(type, listener, useCapture);
var ContextHistory = {
  ...

  init: function CH_init() {
    ...
    this.popup.addEventListener("popupshowing", this, false);
  },

  createMenu: function CH_createMenu() {
    ...
  },

  handleEvent: function CH_handleEvent(event) {
    switch (event.type) {
    case "popupshowing":
      this.createMenu();
      break;
    }
  },
};

メニューの動的な作成: 要素の作成

var ContextHistory = {
  XUL_NS: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",

  ...

  createMenu: function CH_createMenu() {
    let xulElement = document.createElementNS(this.XUL_NS, tagName);
  },

  ...
};

let 定義

JavaScript 1.7 (Firefox 2) の新機能。ブロックスコープの変数を宣言する。

var x = 42; { var x = 23; } x; // => 23
let x = 42; { let x = 23; } x; // => 42

メニューの動的な作成: 履歴の取得

while (this.popup.firstChild)
  this.popup.removeChild(this.popup.firstChild);

let history = gBrowser.sessionHistory;
for (let i = 0, n = history.count; i < n; i++) {
  let entry = history.getEntryAtIndex(i, false);
  let menu = document.createElementNS(this.XUL_NS, "menuitem");
  menu.setAttribute("label", entry.title);
  menu.setAttribute("index", i);
  this.popup.insertBefore(menu, this.popup.firstChild);
}

Mozilla Cross-Reference

履歴の移動

gotoHistory: function CH_gotoHistory(event) {
  let index = +event.target.getAttribute("index");
  gBrowser.webNavigation.gotoIndex(index);
},

ラベルの国際化

[図: 英語版の履歴ドロップダウンのツールチップ]

発展