タブブラウザ用コード

このページには Firefox のタブブラウザを扱うのに有用なコードがリストされています。あなた自身のコードを挿入すべき場所には通常コメントが入れられています。

通常、それぞれのコードには初期化の際に実行するべきコードが含まれています。これらは load リスナーで実行するのがいいでしょう。コードはブラウザウィンドウのコンテキストの中で実行されることを想定しています。もし、ブラウザでないウィンドウでタブを操作したい場合は、ブラウザウィンドウへの参照を最初に取得する必要があります。詳細は chrome コードでウィンドウを操作する を参照してください。

「ブラウザ」という単語の意味

「ブラウザ」という単語はいろいろな使い方をされます。当然、Firefoxのアプリケーション全体は“ブラウザ”と呼ばれます。Firefoxブラウザの中にはタブがあり、一般のウェブページブラウザのイメージからしても、browser要素というXUL的なイメージからしても、内側のそれぞれのタブもブラウザです。さらに、この文書中や一部のFirefoxのソースに現れる「ブラウザ」には、FirefoxのXULウィンドウ中の“tabbrowser要素”を意味するものもあります。

ブラウザへのアクセス方法

メインウィンドウから

browser.xul にオーバーレイするような拡張機能では普通ですが、FirefoxのグローバルなChromeWindowで実行されるコードならば、大域変数 gBrowser を使ってtabbrowser要素にアクセスできます。

// gBrowser はブラウザウィンドウ (browser.xul) のスコープからのみアクセス可能
gBrowser.addTab(...);

gBrowser が未定義ならば、あなたのコードはブラウザウィンドウのスコープで実行されていないか、もしくは早すぎる段階で実行されています。gBrowser にはブラウザウィンドウが完全にロードされた後でのみアクセス出来ます。ウィンドウが開かれたすぐ後に gBrowser に対して何かしたければ、 load イベントをリッスン し、イベントリスナの中で gBrowser を使ってください。

サイドバーから

基本的には、拡張機能がサイドバーで動作しているなら以下のようにできます。

var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                   .getInterface(Components.interfaces.nsIWebNavigation)
                   .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
                   .rootTreeItem
                   .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                   .getInterface(Components.interfaces.nsIDOMWindow);

mainWindow.gBrowser.addTab(...);

ダイアログから

もし、コードがブラウザウィンドウから直接立ち上げられたダイアログで実行されているなら、以下のようにできます。

window.opener.gBrowser.addTab(...);

もし、window.opener が動作しないなら、このコードによって最も最近使われたブラウザウィンドウを取得できます。

var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
                   .getService(Components.interfaces.nsIWindowMediator);
var mainWindow = wm.getMostRecentWindow("navigator:browser");
mainWindow.gBrowser.addTab(...);

新しいタブを開く

// タブの追加
gBrowser.addTab("http://www.google.com/");

// タブの追加とアクティブ化
gBrowser.selectedTab = gBrowser.addTab("http://www.google.com/");

新しいタブのコンテンツの操作

新しく開かれたタブのコンテンツについて仕事をしたいときは、内容がロードされ終わるまで待つ必要があります。

// 間違った方法(ページがロードし終わってない)
var newTab = gBrowser.getBrowserForTab(gBrowser.addTab("http://www.google.com/"));
alert(newTab.contentDocument.body.innerHTML);

// よい方法
var newTab = gBrowser.getBrowserForTab(gBrowser.addTab("http://www.google.com/"));
newTab.addEventListener("load", function() {
  newTab.contentDocument.body.innerHTML = "<div>hello world</div>";
}, true);

(このonLoadハンドラのイベントターゲットはXULの'tab'要素になります。) 詳しくはtabbrowser の getBrowserForTab() を見てください。

URL を適切なウィンドウやタブで開く

簡単にURLをタブで開くことができるメソッドが chrome://browser/content/utilityOverlay.js にあります。openUILinkInopenUILink です。

openUILinkIn( url, where, allowThirdPartyFixup, postData, referrerUrl )
where:
  • "current" 現在のタブ (もしブラウザウィンドウがなければ、代わりに新しいウィンドウで開く)
  • "tab" 新しいタブ (もしブラウザウィンドウがなければ、代わりに新しいウィンドウで開く)
  • "tabshifted" "tab"と同じだが、デフォルトが「新しいタブを選択する」ならバックグラウンドで開く。逆も同様。
  • "window" 新しいウィンドウ
  • "save" ディスクに保存する (ファイル名は指定できない!)
openUILink( url, e, ignoreButton, ignoreAlt, allowKeywordFixup, postData, referrerUrl )

次のコードでは、押されたマウスのボタンや、押されている Ctrl などのホットキーによって、新しいタブ、現在のタブ、現在のウィンドウのどこに URL が開かれるかが決まります。このコードは menuitem 用のものですが、他の XUL 要素でも同じように動作します。このコードは browser.xul のオーバーレイでのみ動作します。

XUL:

<menuitem oncommand="myExtension.foo(event)" onclick="checkForMiddleClick(this, event)" label="Click me"/>

JS:

var myExtension = {
  foo: function(event) {
    openUILink("http://www.example.com", event, false, true);
  }
}

タブの再利用

毎回必要なときに新しいブラウザウィンドウやタブを開くより、もしあれば必要としている URL をすでに表示している既存のタブを再利用することを考える方がよいでしょう。この方法をとれば、あなたの拡張機能が作成するブラウザやタブを最小にすることができます。

URL/URI により再利用する

さまざまな拡張機能で共通する機能として、拡張機能のボタンやリンクをユーザがクリックしたときに、ブラウザウィンドウで chrome:// URI (ヘルプや about 情報など) や外部の (オンラインの http(s)://) HTML 文書を開くようにしています。次のコードは、すでに必要な URL や URI を表示しているタブを再利用する方法を示しています。もし、存在しなければ、指定された URL や URI を新しいタブで開きます。

function openAndReuseOneTabPerURL(url) {
  var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
                     .getService(Components.interfaces.nsIWindowMediator);
  var browserEnumerator = wm.getEnumerator("navigator:browser");

  // 必要としている URL を開いている browser が無いか確認する
  var found = false;
  while (!found && browserEnumerator.hasMoreElements()) {
    var browserWin = browserEnumerator.getNext();
    var tabbrowser = browserWin.gBrowser;

    // browser インスタンスの全てのタブを確認する
    var numTabs = tabbrowser.browsers.length;
    for (var index = 0; index < numTabs; index++) {
      var currentBrowser = tabbrowser.getBrowserAtIndex(index);
      if (url == currentBrowser.currentURI.spec) {

        // URL はすでに開いています。タブを選択します。
        tabbrowser.selectedTab = tabbrowser.tabContainer.childNodes[index];

        // *この*ブラウザウィンドウにフォーカスを移す
        browserInstance.focus();
        found = true;
        break;
      }
    }
  }

  // URL が開かれてないので新たに開く
  if (!found) {
    var recentWindow = wm.getMostRecentWindow("navigator:browser");
    if (recentWindow) {
      // 既存のブラウザウィンドウを利用する
      recentWindow.delayedOpenTab(url, null, null, null, null);
    }
    else {
      // すでに開いているブラウザウィンドウがないので、新たに開く
      window.open(url);
    }
  }
}

その他の条件によって再利用する

すでにどのような URL/URI を開いているかにかかわらず、既存のタブを再利用したいということがあるでしょう。そのタブがほかのブラウザコンポーネントにでなく、あなたの拡張機能により開かれたとします。最初にタブを開くときに独自の属性を付加しておくことで、タブを再利用できます。後々そのタブを再利用したい時には、開かれているすべてのタブからその独自の属性をもったタブを探します。そのようなタブがあれば、そのタブのURL/URIを変更し、そのタブを選択してフォーカスします。そうでなければ (ユーザがタブを閉じたか、そもそもそのようなタブを開いていないかでしょう)、独自の属性をもった新しいタブを作ります。

function openAndReuseOneTabPerAttribute(attrName, url) {
  var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
                     .getService(Components.interfaces.nsIWindowMediator);
  for (var found = false, index = 0, tabbrowser = wm.getEnumerator('navigator:browser').getNext().gBrowser;
       index < tabbrowser.tabContainer.childNodes.length && !found;
       index++) {

    // 次のタブを取得
    var currentTab = tabbrowser.tabContainer.childNodes[index];
  
    // このタブは独自の属性をもっているかな?
    if (currentTab.hasAttribute(attrName)) {

      // Yes -- それを選択・フォーカス
      tabbrowser.selectedTab = currentTab;

      // 他のブラウザウィンドウがフォーカスされている場合、*この*ウィンドウをフォーカス
      tabbrowser.ownerDocument.defaultView.focus();
      found = true;
    }
  }

  if (!found) {
    // ここから開かれたタブはない。新規に開く。
    var browserEnumerator = wm.getEnumerator("navigator:browser");
    var tabbrowser = browserEnumerator.getNext().gBrowser;
  
    // タブを作成
    var newTab = tabbrowser.addTab(url);
    newTab.setAttribute(attrName, "xyz");
  
    // タブを選択・フォーカス
    tabbrowser.selectedTab = newTab;
    
    // 他のブラウザウィンドウがフォーカスされている場合、*この*ウィンドウをフォーカス
    tabbrowser.ownerDocument.defaultView.focus();
  }
}

この関数は以下のように呼びます。

openAndReuseOneTabPerAttribute("myextension-myattribute", "http://developer.mozilla.org/").

タブを閉じる

このサンプルは現在選択されているタブを閉じます。

gBrowser.removeCurrentTab();

また、引数として XUL の tab 要素を一つだけ取る、より汎用的な removeTab というメソッドもあります。

選択されているタブを変更する

タブを一つ右へ移動します。

gBrowser.tabContainer.advanceSelectedTab(1, true);

こちらは左へ移動します。

gBrowser.tabContainer.advanceSelectedTab(-1, true);

ページ読み込みの検出

Code snippets:On page load も参照してください。

function examplePageLoad(event) {
  if (event.originalTarget instanceof HTMLDocument) {
    var win = event.originalTarget.defaultView;
    if (win.frameElement) {
      // タブにフレームが読み込まれました。winはフレームセットのtop windowで
      // なければなりません。もし、このウェブページに frame/iframe が
      // 読み込まれたときに何もしないなら、次の行のコメントアウトを外してください
      // return;
      // ルートドキュメントを探索する
      win = win.top;
    }
  }
}

// ブラウザウィンドウが初期化されるまでコールバック関数を追加しようとしないで
// ください。タブブラウザへのコールバックの追加はブラウザウィンドウが
// 読み込まれたあとにする必要があります。
window.addEventListener("load", function () {
  // ドキュメントが読み込まれるたびに実行されるコールバック関数を追加する
  // ドキュメント内部の frame/iframe にも適用されるので注意が必要
  gBrowser.addEventListener("load", examplePageLoad, true);
}, false);

...
// もし、必要なくなれば
gBrowser.removeEventListener("load", examplePageLoad, true);
...

タブが追加もしくは削除されたときに通知する

function exampleTabAdded(event) {
  var browser = gBrowser.getBrowserForTab(event.target);
  // browser は追加された browser をさす XUL 要素です
}

function exampleTabMoved(event) {
  var browser = gBrowser.getBrowserForTab(event.target);
  // browser は移動した browser をさす XUL 要素です
}

function exampleTabRemoved(event) {
  var browser = gBrowser.getBrowserForTab(event.target);
  // browser は削除された browser をさす XUL 要素です
}

// 初期化中に
var container = gBrowser.tabContainer;
container.addEventListener("TabOpen", exampleTabAdded, false);
container.addEventListener("TabMove", exampleTabMoved, false);
container.addEventListener("TabClose", exampleTabRemoved, false);

// 必要なくなれば
container.removeEventListener("TabOpen", exampleTabAdded, false);
container.removeEventListener("TabMove", exampleTabMoved, false);
container.removeEventListener("TabClose", exampleTabRemoved, false);

Gecko 1.9.1 note
(Firefox 3.5 / Thunderbird 3.0 / SeaMonkey 2.0)

Gecko 1.9.1 (Firefox 3.5 / Thunderbird 3.0 / SeaMonkey 2.0) から、簡単に すべてのタブのイベントをlistenする 方法が使えます。

Gecko 2.0 が必要(Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1)

タブの属性が変更されたときに通知する

Gecko 2.0 以降では、TabAttrModified をlistenすることでタブの属性の変更を検知することができます。以下の属性が変更されると、このイベントが送られます。

function exampleTabAttrModified(event) {
  var tab = event.target;
  // ここで、タブに何の変更があったのかチェックする
}

// 初期化時に
var container = gBrowser.tabContainer;
container.addEventListener("TabAttrModified", exampleTabAttrModified, false);

// 必要なくなったとき
container.removeEventListener("TabAttrModified", exampleTabAttrModified, false);

Gecko 2.0 が必要(Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1)

タブがアイコン化したとき、解除されたときに通知する

Gecko 2.0 以降では、タブはアイコン化(pin)できます。すなわち、タブは特別なアプリケーションタブ (アイコンタブ) となって、タブバーの先頭に固定され、ファビコンだけを表示します。TabPinnedTabUnpinned イベントを監視することで、タブがアイコン化したり解除されたりしたときに検知できます。

function exampleTabPinned(event) {
  var browser = gBrowser.getBrowserForTab(event.target);
  // browser はアイコン化したブラウザのXUL要素
}

function exampleTabUnpinned(event) {
  var browser = gBrowser.getBrowserForTab(event.target);
  // browser はアイコン化したブラウザのXUL要素
}

// 初期化
var container = gBrowser.tabContainer;
container.addEventListener("TabPinned", exampleTabPinned, false);
container.addEventListener("TabUnpinned", exampleTabUnpinned, false);

// 必要なくなったとき
container.removeEventListener("TabPinned", exampleTabPinned, false);
container.removeEventListener("TabUnpinned", exampleTabUnpinned, false);

タブが選択されたことを検出する

次のコードでブラウザの新たにタブが選択されたことを検出できます。

function exampleTabSelected(event) {
  var browser = gBrowser.selectedBrowser;
  // browser はその時に選択された browser の XUL 要素
}

// 初期化中に
var container = gBrowser.tabContainer;
container.addEventListener("TabSelect", exampleTabSelected, false);

// 必要なくなれば
container.removeEventListener("TabSelect", exampleTabSelected, false);

現在選択されているタブのドキュメントを取得する

次のコードで現在選択されているタブのドキュメントを取得できます。このコードはブラウザウィンドウのスコープで動作します (ブラウザウィンドウへのオーバーレイで動作させる時など) 。

gBrowser.contentDocument;

もしくは

content.document

ブラウザウィンドウから開かれたウィンドウやダイアログで動作させる場合は、このコードを使って、そのウィンドウを開いたブラウザウィンドウの、選択されているタブで表示されているドキュメントを取得できます。

window.opener.content.document

ブラウザウィンドウから開かれたものでないウィンドウやダイアログからは、nsIWindowMediator を使って、一番最近使われたブラウザウィンドウの、選択されているタブで表示されているドキュメントを取得できます。

var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].
         getService(Components.interfaces.nsIWindowMediator);
var recentWindow = wm.getMostRecentWindow("navigator:browser");
return recentWindow ? recentWindow.content.document.location : null;

chrome コードでウィンドウを操作する も参照してください。

タブの列挙

ブラウザで開いている全てのタブを取得するには、最初にブラウザウィンドウへの参照を取得します。もし、Firefox の browser.xul オーバーレイから実行されているコード (たとえば、ツールバーボタンやメニューでの click ハンドラなど) なら、現在のウィンドウにすでに定義されている変数である window でアクセスできます。しかしながら、それ自身のウィンドウから実行されているコード (たとえば、設定やオプションダイアログなど) なら、nsIWindowMediator をブラウザのウィンドウを取得するのに利用する必要があります。

次に、<tabbrowser/>要素を取得します。前の手順で取得したブラウザのウィンドウを win とすると、win.gBrowser によって取得できます。もし、browser.xul オーバーレイの中で動作しているなら、より簡単に window.gBrowser でなく、gBrowser で取得可能です。

最後に、gBrowser.browsers.length により開いているタブの数を取得し、gBrowser.getBrowserAtIndex() により <browser/> 要素を取得します。たとえば、

var num = gBrowser.browsers.length;
for (var i = 0; i < num; i++) {
  var b = gBrowser.getBrowserAtIndex(i);
  try {
    dump(b.currentURI.spec); // 開いている全てのタブの URL をコンソールへ出力
  } catch(e) {
    Components.utils.reportError(e);
  }
}

<browser/><tabbrowser/> 要素でどんなメソッドが利用できるかについてより詳細は、DOM Inspector を利用するか、browser.xmltabbrowser.xml の対応する XBL バインディングを参照してください。

http-on-modify-request 通知が発火されたブラウザを取得する

HTTP リクエストの中にはタブと関係ないものもあることに注意してください。例えば、RSS フィードの更新、拡張機能マネージャのリクエスト、XPCOMからの XMLHttpRequest などです。それらの場合、次のコードはnullを返します。

observe: function (subject, topic, data) {
  if (topic == "http-on-modify-request") {
    subject.QueryInterface(Components.interfaces.nsIHttpChannel);
    var url = subject.URI.spec; /* リクエストされたurl。きっと必要になるでしょう。 */
    var browser = this.getBrowserFromChannel(subject);
    if (browser != null) {
      /* 何かの処理 */
    }
  }
},

getBrowserFromChannel: function (aChannel) {
  try {
    var notificationCallbacks = 
      aChannel.notificationCallbacks ? aChannel.notificationCallbacks : aChannel.loadGroup.notificationCallbacks;

    if (!notificationCallbacks)
      return null;

    var domWin = notificationCallbacks.getInterface(Components.interfaces.nsIDOMWindow);
    return gBrowser.getBrowserForDocument(domWin.top.document);
  }
  catch (e) {
    dump(e + "\n");
    return null;
  }
}

ドキュメントのタグと貢献者

タグ: 
 このページの貢献者: woby, satyr, masahal, Shoot, Mgjbot, Shimono, Taken
 最終更新者: woby,