Home > Latest topics

Latest topics > 安全なタブの取得方法

宣伝1。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能! シス管系女子って何!? - 「シス管系女子」特設サイト

宣伝2。Firefox Hacks Rebooted発売中。本書の1/3を使って、再起動不要なアドオンの作り方のテクニックや非同期処理の効率のいい書き方などを解説しています。既刊のFirefox 3 Hacks拡張機能開発チュートリアルと併せてどうぞ。

Firefox Hacks Rebooted ―Mozillaテクノロジ徹底活用テクニック
浅井 智也 池田 譲治 小山田 昌史 五味渕 大賀 下田 洋志 寺田 真 松澤 太郎
オライリージャパン

安全なタブの取得方法 - Jan 24, 2009

前のエントリでも触れてるけど、Firefox 3.1ではタブでない要素がタブバーに入る可能性が出てきたので、安全なタブの取得方法を色々考えてみた。

まず、すべてのタブのリスト。

function getTabs(aTabBrowser) {
  return aTabBrowser.ownerDocument.evaluate(
    'descendant::*[local-name()="tab"]',
    aTabBrowser.mTabContainer,
    null,
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
    null
  );
}

var tabs = getTabs(gBrowser);
for (var i = 0, maxi = tabs.snapshotLength; i < maxi; i++) {
  alert(tabs.snapshotItem(i).label);
}

配列でないと困る時はこう。

function getTabsArray(aTabBrowser) {
  var tabs = getTabs(aTabBrowser);
  var array = [];
  for (var i = 0, maxi = tabs.snapshotLength; i < maxi; i++) {
    array.push(tabs.snapshotItem(i).label);
  }
  return array;
}

特定のタブの「次のタブ」。

function getNextTab(aTab) {
  return aTab.ownerDocument.evaluate(
    'following-sibling::*[local-name()="tab"][1]',
    aTab,
    null,
    XPathResult.FIRST_ORDERED_NODE_TYPE,
    null
  ).singleNodeValue;
}

特定のタブの「前のタブ」。

function getPreviousTab(aTab) {
  var tabs = aTab.ownerDocument.evaluate(
    'preceding-sibling::*[local-name()="tab"]',
    aTab,
    null,
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
    null
  );
  return tabs.snapshotItem(tabs.snapshotLength-1);
}

preceding-sibling軸を使った時の返り値のノードの並びは軸方向の出現順ではなく文書順になるので、注意がいる。

function getPreviousTab(aTab) {
  return aTab.ownerDocument.evaluate(
    'preceding-sibling::*[local-name()="tab"][1]',
    aTab,
    null,
    XPathResult.FIRST_ORDERED_NODE_TYPE,
    null
  ).singleNodeValue;
}

タブの個数、最初のタブ、最後のタブも。

function getTabCount(aTabBowser) {
  return aTabBrowser.ownerDocument.evaluate(
    'count(descendant::*[local-name()="tab"])',
    aTabBrowser.mTabContainer,
    null,
    XPathResult.NUMBER_TYPE,
    null
  ).numberValue;
}

function getFirstTab(aTabBowser) {
  return aTabBrowser.ownerDocument.evaluate(
    'descendant::*[local-name()="tab"][1]',
    aTabBrowser.mTabContainer,
    null,
    XPathResult.FIRST_ORDERED_NODE_TYPE,
    null
  ).singleNodeValue;
}

function getLastTab(aTabBowser) {
  return aTabBrowser.ownerDocument.evaluate(
    'descendant::*[local-name()="tab"][last()]',
    aTabBrowser.mTabContainer,
    null,
    XPathResult.FIRST_ORDERED_NODE_TYPE,
    null
  ).singleNodeValue;
}

こういう時自分はついついXPathを使ってしまうんだけど、速度的にはどうなんだろう。ちゃんと計測してないから分からない。Array.slice(gBrowser.mTabContainer.childNodes).filter(function(aNode) { return aNode.localName == 'tab'; })とかの方が普通に速かったらごめんなさい。

childNodesを参照してるコードがヤバイというのはホントに酷い話だなあと思うけど、Google ChromeやIE7以降と同じユーザ体験を実現しようと思ったらどっかの時点でこれには手を出さなきゃいけなかった訳で、もうみんな腹括って粛々と受け入れるしかないんじゃないかと思うんだなあ僕は。

追記。例によってきっとガン無視されるだろうけどバグ立ててみた

追記。どうやら僕が立てたバグに関しては杞憂だったみたい……そもそもこのエントリに書いた話も今回の修正以前から潜在的にはあった問題のようだし。むしろヤバイのはalice0775氏が着目してる問題で、実際のノードの並び順と表示される順番とが食い違うことがあるようだ。この手の問題は僕も過去にXBLで色々試してた時に遭遇して、諦めて「こういう事はしない」という風に自分ルールで決めた記憶がある。バックアウトするのもいいけど、そもそもこれはXBLの匿名内容のレイアウトに絡む根深い問題に起因してるようなので、ここらで誰かが本気出して低層の所を直してくれないことにはどうにもならん気がする。

26日追記。「実際のノードの並び順と表示される順番とが食い違う」件は、新しいNightlyを試したら再現しなくなった。僕の全面的な勘違いだったのか、それとも別の所で修正されたのか…… あとgetBrowser().mTabContainer.selectedIndex プロパティと getItemAtIndex メソッドじゃあダメなのかなと言われたので調べてみたけど、現状のgetItemAtIndex()childNodes.item()のエイリアスに過ぎないようなので、やはり万全を期すなら別の手(このエントリに書いたような)を考えておいた方がいい気はする。

分類:Mozilla > XUL, , , , , 時刻:15:19 | Comments/Trackbacks (4) | Edit

Comments/Trackbacks

no title

昔みたくタブ群の右に.tabs-rightなスペーサを置く方式のほうが、トラブルが起こる可能性が低くて、今回発生しているバグを修理しやすいということですか?あの方式だとタブバーのスクロール関係とかで問題が起きるとか、なにか問題があるんですかね?

Commented by くでん at 2009/01/24 (Sat) 19:17:34

no title

>昔みたくタブ群の右に.tabs-rightなスペーサを置く方式のほうが、トラブルが起こる可能性が低くて、今回発生しているバグを修理しやすいということですか?

いえ、多分その逆です。

Commented by Piro at 2009/01/24 (Sat) 19:23:08

no title

「前のタブ」もXPath式を
preceding-sibling::*[local-name()="tab"][1]
とすれば、「次のタブ」と同じくsingleNodeValueで取得できませんか。

それから、せっかくArray Generics (と式クロージャ)があるのだから、
Array.slice(gBrowser.mTabContainer.childNodes).filter(function(aNode) { return aNode.localName == 'tab'; })

Array.filter(gBrowser.mTabContainer.childNodes, function (aNode) aNode.localName == 'tab')
でいい気がします。

あと、Firefox 3.1以降でしか使わないコードならquerySelectorAllメソッドを使うのもありかと思います。

Commented by nanto_vi at 2009/01/24 (Sat) 23:27:33

no title

XPathを使う時に、何事もORDERED_NODE_SNAPSHOT_TYPEで考える癖が付いてしまってました……他にもXPath式の方を工夫すればもっとスマートにできますね。ということでサンプルコードをちょっと書き直し(足し)ました。

あとArrayの機能を使う例については、JS1.6以降の機能は部分的にしか把握してなかったので、半端な書き方になってしまってました。お恥ずかしいです。

こういう事例ではXPathとquerySelectorAllのどっちが速いんでしょう。結果を簡単にArrayとして扱えるquerySelectorAllの方がコーディングする側としては明らかに楽そうなんですけど。XPathの結果は配列にしにくくてめんどくてたまらんです……

Commented by Piro at 2009/01/26 (Mon) 15:28:35

TrackBack ping me at


の末尾に2014年1月19日時点の日本の首相のファミリーネーム(ローマ字で回答)を繋げて下さい。例えば「noda」なら、「2009-01-24_tab.trackbacknoda」です。これは機械的なトラックバックスパムを防止するための措置です。

Post a comment

writeback message: Ready to post a comment.

2014年1月19日時点の日本の首相のファミリーネーム(ひらがなで回答)

Powered by blosxom 2.0 + starter kit
Home

カテゴリ一覧

過去の記事

1999.2~2005.8

最近のつぶやき

オススメ

Mozilla Firefox ブラウザ無料ダウンロード