Thunderbird
タブ拡張(WAT)

Mozilla 勉強会@東京 2010-04-03

by teramako teramako@gmail.com

自己紹介

teramako

Webサイト

はてな id:teramako
http://d.hatena.ne.jp/teramako/
http://vimperator.g.hatena.ne.jp/teramako/
Twitter
http://twitter.com/teramako/
GitHub
http://github.com/teramako/

WAT

WebApplicationTab

What is WAT

Thunderbird 3 でWebページをタブに開くことを可能にする拡張機能

Screenshots

capture_1 capture_2

きっかけ

Thunderbirdを立ち上げたら、ツールメニューからエラーコンソールを開き、以下のコードを追加します。

Components.classes['@mozilla.org/appshell/window-mediator;1']
	.getService(Components.interfaces.nsIWindowMediator)
	.getMostRecentWindow("mail:3pane")
	.document.getElementById("tabmail")
	.openTab("contentTab", {
		contentPage: "https://wave.google.com/wave/?nouacheck"
	});

Thunderbird 3の新しいタブ機能でGoogle Waveの受信箱が開けるみたいな初心者に優しくないやり方が紹介されているのを見て、腹立たしく思い作った。

機能

タブ機能において、Thunderbrid3が持つべき機能を追加しているだけ

WATはFirefoxの車輪の再発明で出来ています

拡張なしのThunderbird 3

出来ないことだらけ (version 3.0.3時点)

WATがあれば全部できる!

WATの制限事項

Thunderbirdタブ実装

Firefoxとの比較を交えて

tabmail要素

Thunderbird3のタブ実装形態はFirefoxとは異なり、tabbrowser要素はない

tabmailという要素がtabbrowserの代わり

タブの種類

Firefoxは1種類だが、Thunderbirdには(デフォルトでは)6つの種類がある

tabmail.openTabメソッドの第一引数で指定

種類

"contentTab"
通常のWebコンテンツ
"chromeTab"
chrome URI, about URI等のThunderbird内部コンテンツ
contentTabとの違いは、browser要素にdisablesecurity="true"の属性
"folder"
メッセージフォルダ
"message"
メッセージ
"glodaFacet"
検索結果
"glodaList"
検索結果から開いたメッセージリストとメッセージ内容

タブに開く

tabmail.openTabメソッドを使用する

var tabmail = document.getElementById("tabmail");
tabmail.openTab(aTabModeName, aArgs);

参照: comm-central comm-central/mail/base/content/tabmail.xml

主な処理

  1. tabmail.tabModes[aTabModeName]の取得
  2. 新たなタブを作らず、タブの選択のみにするか判断
    • aTabModeNamecontentTabの場合、既に同じURLを開いているか等
  3. tab要素の生成
  4. tabmail.tabModes[aTabModeName].openTab または tabmail.tabModes[aTabModeName].tabType.openTab に aArgs を渡して実行

引数

  1. aTabModeName (String)
  2. aArgs (Object)

aTabModeName

参照:前項

aArgs

aTabModeNameによって異なるプロパティをもつオブジェクト

全てに共通するプロパティ

background
バックグラウンドに開くか (Boolean)

タブに開く - contentTab

Webページを開くモード

contentPage
URL (String)
必須
clickHandler
onclick属性の値 (String)
省略時、specialTabs.defaultClickHandler(event)が使用される

参照: comm-central comm-central/mail/base/content/specialTabs.js#221

Example

var tabmail = document.getElementById("tabmail");
tabmail.openTab("contentTab", {
  contentPage: "http://example.com",
  background: true, 
  clickHandler: "specialTabs.siteClickHandler(event, /^https?:\\/\\/example\\.com\\//)"
});

Note

タブに開く - chromeTab

chrome URI, about URIを開くモード

chromePage
URL (String)
必須
clickHandler
onclick属性の値 (String)
省略時、specialTabs.defaultClickHandler(event)が使用される

参照: comm-central comm-central/mail/base/content/specialTabs.js#652

Example

var tabmail = document.getElementById("tabmail");
tabmail.openTab("chromeTab", {
  chromePage: "chrome://global/content/console.xul"
});

Note

タブに開く - folder

メッセージフォルダを開くモード

folder
folderオブジェクト (nsIMsgFolder)
必須
folderPaneVisible
フォルダツリーを表示するか否か (Boolean)
省略時は最初のタブ設定を流用
messagePaneVisible
メッセージを表示するか否か (Boolean)
省略時は最初のタブ設定を流用
msgHdr
messageオブジェクト (nsIMsgDBHdr)
選択するメッセージ
forceSelectMessage
msgHdrと組み合わせて使う (Boolean)
searchMode
検索モード
  • "global" (すべてのメッセージを検索)
  • 0 (件名または差出人、受信者)
  • 1 (件名または差出人)
  • 2 (件名または宛先、Cc)
  • 3 (件名)
  • 4 (差出人)
  • 5 (宛先または Cc)
  • 6 (メッセージ本文)

参照: comm-central comm-central/mail/base/content/mailTabs.js

Example

var tabmail = document.getElemenetById("tabmail");
tabmail.openTab("folder", {
  folder: gFolderTreeView.getSelectedFolders()[0],
  msgHdr: gFolderDisplay.selectedMessage,
  folderPaneVisible: false
});

タブに開く - message

メッセージを開くモード

msgHdr
メッセージオブジェクト (nsIMsgDBHdr)
必須
viewWrapperToClone
メッセージビューオブジェクト (Object)

参照: comm-central comm-central/mail/base/content/mailTabs.js

Example

var tabmail = document.getElmenetById("tabmail");
tabmail.openTab("message", {
  msgHdr: gFolderDisplay.selectedMessage,
  viewWrapperToClone: gFolderDisplay.view,
  background: true
});

Note

WATでのタブとブラウザ実装

Firefoxからいろいろ拝借

タブに開くメソッド実装

WAT.openTab(aURL)

参照: chrome/content/wat.js at version-0.5 from teramako's wat - GitHub

  1. aURLnsIURIに変換
  2. URIスキームでcontentTabchromeTabかを判定
    • chrome,aboutスキームならchromeTab
    • それ以外はcontentTab
  3. tabmail.openTabに渡す引数設定
    • contentTabならば、{ conentPage, clickHandler }の生成
    • chromeTabならば、{ chromePage, clickHandler }の生成
  4. tabmail.openTabの実行
  5. contentTabの場合、履歴を有効にする

履歴(戻る、進む)の有効化

デフォルトでは履歴が無効化(disablehistory=true)されている

有効にするにはnsISHistoryインスタンスを生成してbrowser.webNavigation.sessionHistoryに代入する

function enableSessionHistory(browser){
  if (!browser.hasAttribute("disablehistory"))
    return;
  browser.removeAttribute("disablehistory");
  try {
    os.addObserver(browser, "browser:purge-session-history", false);
    browser.webNavigation.sessionHistory =
      Cc["@mozilla.org/browser/shistory;1"].createInstance(Ci.nsISHistory);
  } catch(e){
    Components.utils.reportError(e);
  }  
}

browser.goBack(),browser.goForward()で「戻る」、「進む」が可能

Note

通常(disablehistory=trueが無ければ)、履歴は有効なので特に何もする必要はない
参照: mozilla-central mozilla/toolkit/content/widgets/browser.xml

タブに表示するアイコン

5種類のアイコン

当初

  1. ドキュメントのDOMContentLoadedイベントを待つ
  2. link[rel~=icon]があれば採用
  3. XMLHttpRequestのHEADメソッドをドメイン/favicon.icoへ投げてレスポンスヘッダがContent-Type: image/x-iconであれば、採用

参照: http://github.com/teramako/wat/commit/cac931e0a728664051bd3324ed192daa248151bb

Firefoxでは

DOMLinkAddedイベントとnsIWebProgressListenerの組み合わせ

DOMLinkAddedイベント処理部分
mozilla1.9.2 mozilla/browser/base/content/browser.js
nsIWebProgressListener実装部分
mozilla1.9.2 mozilla/browser/base/content/tabbrowser.xml

DOMLinkAdded イベント

link要素が追加されたときにイベント発生

  1. rel属性値に"icon"があれば処理
  2. href属性値のセキュリティチェック
  3. browser要素のmIconURLプロパティにURLを代入

nsIWebProgressListener インターフェース

コンテンツの読み込み状況に応じて処理

参照: https://developer.mozilla.org/en/nsIWebProgressListener

  • 読み込み中ならば、インジケータの設定
  • 読み込みが終了していれば、アイコン設定
    • browser要素のmIconURLに値があれば、それを使用
    • なければ、ドメイン/favicon.icoを設定
    • 画像読み込みに失敗した場合はデフォルトのアイコン

metaタグのリダイレクト

<meta http-equiv="refresh" content="10; URL=http://example.com" />

browser要素のbrowser.docShell.allowMetaRedirecttrueになっているのに、リダイレクトされない!
何故?

当初

  1. ドキュメントのDOMContentLoadedイベントを待つ
  2. meta[http-equiv=refresh]があればパースしてloadURI

参照: http://github.com/teramako/wat/commit/844b0c7962b251f02577226337184cface7e8ede#L0L339

Firefoxでは

参照: https://developer.mozilla.org/en/XPCOM_Interface_Reference/NsIWebProgressListener2#onRefreshAttempted()

nsIWebProgressListener2onRefreshAttemptedtrueを返してリダイレクトを許可している

ThunderbirdのonRefreshAttemptedは定義のみで中身がないため、リダイレクトされない

RSSフィードの検出

<link rel="alternate" type="application/rss+xml" title="RSS" href="..."/>

Firefoxではアイコンと同じくDOMLinkAddedで検出

開発手法

FirefoxもThunderbirdも大して変わらない

開発に使っているAddon等

Vimperator, Muttator

真の価値はvimキーバインドではなく、コマンドライン機能拡張性

DOM Inspector

お馴染みのツール

Vimperartor + DOM Inspector

JavaScript実行結果をDOM Inspectorに表示するVimperatorプラグイン

個人的なJavaScriptの書き方

let WAT = (function(){
  // --------------------------
  // private
  // --------------------------
  const Cc = Components.classes;
  const Ci = Components.interfaces;
  function init(){
    window.removeEventListener("load", init, false);
    self.tabMail = document.getElementById("tabmail");
  }
  function fooFunc(){
    Application.console.log(arguments.callee.name + "\n"+ Error("DEBUG").stack);
  }
  // --------------------------
  // public
  // --------------------------
  let self = {
    tabMail: null,
    openTab: function WAT_openTab(aURL){
      // ...;
      fooFunc();
    },
  };
  window.addEventListener("load", init, false);
  return self;
})();

Firefox, ThunderbirdのXUL,JavaScriptコード

Firefox, THunderbirdのソースコードの参照方法

Mozilla Cross-Reference

インストールディレクトリ/chrome/*.jar

Windows
C:\Program Files\Mozilla Firefox\chrome\*.jar
Linux
/usr/lib/firefox-3.6/chrome/*.jar(Ubuntuの場合)
Mac OS X
/Applications/Firefox.app/Contents/MacOS/chrome/*.jar

*.jarファイルはzip形式で圧縮されている。展開しておくと、ローカルから手早く参照可能