たまに18歳未満の人や心臓の弱い人にはお勧めできない情報が含まれることもあるかもしれない、甘くなくて酸っぱくてしょっぱいチラシの裏。RSSによる簡単な更新情報を利用したりすると、ハッピーになるかも知れませんしそうでないかも知れません。
の動向はもえじら組ブログで。
宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能!
※今(2009年11月24日03:22)見たらリンク先エントリおよびWikiのページからTree Style Tabの項目が消されていた。このエントリおよびリンク先のコメントを見た人にはさっぱり意味不明でワケが分からないと思うので説明しておくと、リンク先のページにはどちらもタブ系のAll-in-One型アドオンが列挙されていて、初出時はその中にツリー型タブ(Tree Style Tab)もあったのです。
多機能オールインワンを否定して、タブをツリー表示するという事に特化した単機能のアドオンとして作ってきたつもりなのに、こういう所で俎上に載せられることが非常に心外且つショックで、でも実態からそう見られてしまう部分があるのは事実なので、悲しい思いをした。
ツリー型タブにあるツリーと関係ない機能は、開発当時にその機能だけを提供する単機能アドオンを見つけられなかったからだったり、あるいは、ツリーとの緊密な連携を考慮しないと上手く働かなさそうだったりという理由からやむなく加えた物なので、外せるものならどんどん外していきたいというのが僕の正直な考えです。
で、今日探してみたら選択範囲のリンクをタブで開く機能に特化したアドオン「Selection Links」(紹介記事)を見つけて、このアドオンによってツリー型タブの持つ機能は完全に代替できる(実際にはTSTはリファラを渡すけどこのアドオンはリファラを渡さない等の違いがあるんだけどそこは気にしない)事が分かったので、当該機能をツリー型タブの次のバージョンから廃止することにしました。
あとついでに「新しいタブを開く」ボタンの表示・非表示を制御するオプションも廃止します。これはuserChrome.cssに以下のように書けば簡単に代替できるので。
.tabs-newtab-button {
display: none !important;
}
追記。意に反する紹介のされ方をした僕がキレて当てつけのために機能の削除を決意した、という風に解釈されるぜと忠告を受けたのでここにも明記しておきますが、今回の決定はそーいう下らない目的のためでは決してないです。機能の削除は「ユーザを困らせるため」ではなく、「何のためのアドオンなのかというコンセプトをはっきりさせることによってユーザ体験を向上するため(あと、コードを減らしてメンテナンス性を高めるため)」という、前向きな理由に基づいています。
確かに事実として関係ない機能まで取り込んで肥大化してしまっている場合はありますが、今回言及した表のようにそれを気付かせてくれる存在に出会えば、肥大化した部分はちゃんと切り捨てて本来あるべき姿に戻したい、と思っています。これは昨日今日で決めたようなことではなく、ツリー型タブ(およびその他の「TBE代替」のためのアドオン)を開発した当初からの方針です。
その辺の話に触れた過去のエントリがいくつかありますので、併せてご覧頂ければ幸いです。
テキストリンクに続いてClipboard ObserverもJetpackに移植してみた。
テキストリンク同様に、普通のアドオンのClipboard ObserverのコードをJetpackで動くように手直しして、最後にJetpack用のコードを付け加えただけ……という感じ。今回は、テキストリンクでは使わなかったステータスバーへの項目追加のAPIを使ってる。
以下、詰まった点。
jetpack.storage
の使い方が分からない、というか、Web上に書かれてる情報が古くて役に立たない。APIリファレンスに書いてある書き方を試したら「この方法は古いです、もうすぐサポートしなくなります」なんてメッセージが出る。
とにかくドキュメント不足が深刻です。
追記。アウトラインリスト生成のやつでstorageの使い方がやっと分かったので、チェック状態を保存するようにした。
いわゆる軽量アドオンであるところのJetpackについて今度の日曜のFirefox Developers ConferenceでAza氏とトークショーじゃなかったトークセッションを行うにあたって、「全くJetpackを触らないまま行くのはさすがに失礼すぎるだろ常識的に考えて」と思ったので、テキストリンクをJetpack featureとして移植してみた。
以下、詰まった点。
==
で比較すると、ラップされてるノードが同じノードでもfalse
になる。===
や!==
で比較すればちゃんと意図通りに判定される。これは盲点だった。(普通にアドオンを作る時だと==
で期待通りに判定されるので)やっつけ移植なので、URLっぽい文字列をダブルクリックしたら新しいタブを開く、という機能しかないです。そのくせ25KBもありやがる。
スクリプトの中身はぶっちゃけ普通のテキストリンクのコードのコピペです。というか、Jetpackで動かないであろう部分を削っていって残ったのがコレなんで。最後の方にちょこっとだけ、JetpackのAPIを使ってページの読み込み完了を監視する処理が入ってて、そこが本題です。
いいな、と思った点。
I would like to see a shortcut assigned to show/hide the tab bar with the next update. That would be very useful since I reckon, since I have to click every time I want to show/hide it, which every time I want to read some thing on the web, which is way too frequent.
次のアップデートで、タブバーの表示・非表示の切り替えのためのキーボードショートカットを追加して欲しいです。私が思うにそれはきっととても便利でしょう。Webで何かを読もうと思う度に、タブバーの表示・非表示を切り替えるために今は(タブバーを?)その都度クリックしていますが、この操作が頻繁すぎて面倒です。
I'm very sorry, currently I have no idea to add new shortcut to show/hide tab bar. Instead, if you press "Ctrl" key for a while, collapsed tab bar will be expanded while you press the key. I hope it helps you.
If you like, you can call internal methods to show/hide tab bar from keyboard shortcuts defined by keyconfig or KeySnail. For example:
// toggle mode shown <=> hidden (shown <=> shrunken)
TreeStyleTabBrowserAutoHide.toggleMode();
// set to "shown"
TreeStyleTabService.setTreePref('tabbar.autoHide.mode',
TreeStyleTabBrowserAutoHide.prototype.kMODE_DISABLED);
// set to "hidden"
TreeStyleTabService.setTreePref('tabbar.autoHide.mode',
TreeStyleTabBrowserAutoHide.prototype.kMODE_HIDE);
// set to "shrunken"
TreeStyleTabService.setTreePref('tabbar.autoHide.mode',
TreeStyleTabBrowserAutoHide.prototype.kMODE_SHRINK);
Note: These examples work on Tree Style Tab 0.8.2009100101 or later.
すみません、今の所タブバーの表示・非表示を切り替えるショートカットを加える予定はないです。その代わり、Ctrlキーを長押しすると、非表示になっていたタブバーがCtrlキーを押している間表示されるという機能があります。これが助けになることを願っています。
お好みで、タブバーの表示・非表示を切り替える内部的な機能をkeyconfigやKeySnailで定義されたショートカットから呼ぶこともできます。例は上記の通りです(ツリー型タブ0.8.2009100101以降で動作します)。
In Tree Style Tab extension, would it be possible to make the background (dark gray blank area) display an image? Maybe with a userstyle or script...
ツリー型タブで、タブの背景(暗いグレーの空白の領域)に画像を表示できませんか? ユーザースタイルシートかスクリプトを使えばできると思うんですが……
On Firefox 3.6, you'll be able to make the background color of the tabbar transparent, like following CSS in the userChrome.css:
tabbrowser[treestyletab-style][treestyletab-mode]
.tabs-stack,
tabbrowser[treestyletab-style][treestyletab-mode]
.tabbrowser-tabs,
tabbrowser[treestyletab-style][treestyletab-tabbar-position]
.tabbrowser-tabs {
background: transparent !important;
}
On Firefox 4:
:root[treestyletab-style][treestyletab-tabbar-position]
#appcontent,
:root[treestyletab-style][treestyletab-tabbar-position]
tabbrowser,
:root[treestyletab-style][treestyletab-tabbar-position]
.tabbrowser-strip {
background: transparent !important;
}
Another way, you can get transparent tab bar with a secret preference "extensions.treestyletab.tabbar.style.aero". Go to "about:config" and turn it to "true".
上記のようなCSSをuserChrome.cssに書くと、タブバーの背景色を透明にできます。1つ目はFirefox 3.6用、2つ目はFirefox 4用の指定です。
また別の方法として、隠し設定「extensions.treestyletab.tabbar.style.aero」でもタブバーを透明にできます。「about:config」を開いて、この設定の値を「true」に変更して下さい。
XUL/Migemo 0.12.xで、機能を他のアドオンとかから呼び出すためのAPIを刷新してみたよ。
古いAPI(はてなブックマーク拡張とかが使ってくれてるやつ)は僕自身が色々よく分からないまま作った物だったために、引数を文字列で渡さないといけないとか戻り値も正規表現オブジェクトではなく文字列だとか、色々と使い勝手が悪かったと思う。メインウィンドウ以外では呼び出す時にいちいちXPConnect使わないといかんし。
新しいAPIは、それに比べたらものっそシンプルになった。XPConnect使わなくてもmigemo.getRegExp('hoge')
とか書くだけで使えるし、戻り値もすぐに使える正規表現オブジェクトが返ってくるし。互換性を保つために旧APIはそのまま残してあるので、旧APIを使ってるアドオンが動かなくなるということはないけど、今後は使うなら新APIの方を使うのがお勧めです。
で、このAPIはWebページ内のスクリプトでもCPUの使用率を取得できるようにするAPIを提供する例のアドオンの技術の応用なので、Webページ内のスクリプトからもXUL/Migemoの機能を利用できてしまいます。
ただしスクリプトの実行権限の関係で、上記のmigemo.getRegExp('hoge')
のような、正規表現オブジェクトを直に受け取る機能は使えません。代わりにJavaScript/Migemo互換の、正規表現のソース文字列を返すAPI migemo.query('hoge')
などを使う必要があります。
XUL/Migemo 0.12.2以降が入ってる環境でこのページを表示してれば、以下のデモを試せるはず。
ページ内検索系のメソッドもあるんだけど、多分使いでがなさそうなので解説は用意してないです。
すでに上でもリンクしてるけど、エンジンごとページ内に埋め込めるJavaScript/Migemoという実装もあるから、XUL/Migemoを入れてるFirefoxユーザでないと使えないこのAPIってなんか意味あんの?と言われそう。深くはツッコまないでください。
以下、補足事項。
当初このエントリでは、「Webページ上のスクリプトからもmigemo.getRegExp('hoge')
のようにして正規表現を取得して利用できる」という風な書き方をしてたけど、これは大間違いだった。戻り値の正規表現オブジェクトが生成された実行コンテキストがUniversalXPConnect特権のある場所なのに対して、呼び出し元は特権のない普通のWebページ上であるため、それぞれの権限が違うので本来ならその正規表現の各メソッドは実行できないのが正しい。
ただ、Gecko 1.9.1(Firefox 3.5)以前のバージョンにはバグがあって、上記のようなセキュリティのチェックが働かないために、戻り値の正規表現の各メソッドを呼べてしまう状態になっていた。
このバグはGecko 1.9.2(Firefox 3.6)以降ではすでに修正済みで、実際、Trunk等で戻り値の正規表現オブジェクトのメソッドを呼ぼうとすると、その場で処理が中断されて Error: RegExp.prototype.toString called on incompatible ChromeObjectWrapper というエラーがエラーコンソール上に出力される。
ということで、JavaScript/Migemo互換のAPIとして正規表現オブジェクトのソース文字列だけを返すような機能を0.12.2で新たに加えた。文字列や数値などのプリミティブ値に対してはセキュリティのチェックが行われないようなので、こっちはGecko 1.9.2以降でもWeb上で使える。
Tab Progress BarのプログレスバーがFirefox 3.7のモックアップ風なのを見て「羨ましい!」と思ったのでInformational Tabのデフォルトスタイルをそのように変えてみた。一応、設定で今まで通り(ラベルの下に表示)にも戻せる。
で、やるならとことんやってみっか!と一念発起して、モックアップにあるような光るプログレスバーを再現しようと頑張ってみた。 伸びるバーの部分は背景画像で、ぽわーんと光った感じは-moz-box-shadowを使ってるので、Firefox 3.5じゃないと期待通りには見えない。
表題の件について、どーも実際に表示されてる内容とセッション情報とが食い違ってるケースがあるようだ。
Firefoxのタブとかのセッション情報はJSONっぽい文字列で保存されてて、最初はJSON整形で読みやすい形にして調べようと思ってたけど、めんどすぎたので、以下のようなスクリプトでツリー構造の所だけ可視化してみた。
var sv = gBrowser.treeStyleTab;
var session = sv.SessionStore.getWindowState(window);
eval('session = '+session);
var result = [];
session.windows[0].tabs.forEach(function(aInfo) {
var entry = aInfo.entries[aInfo.entries.length-1];
var item = {
label : entry.title+' / '+entry.url,
id : aInfo.extData[sv.kID],
children : (aInfo.extData[sv.kCHILDREN] || '').split('|'),
parent : (aInfo.extData[sv.kPARENT] || ''),
items : []
};
var bullet = '*';
var tab = sv.getTabById(item.id);
if (tab.getAttribute(sv.kPARENT) != item.parent) {
item.label += '\n<WRONG PARENT>';
bullet = '?';
}
if (tab.getAttribute(sv.kCHILDREN) != item.children.join('|')) {
item.label += '\n<WRONG CHILDREN>';
bullet = '?';
}
item.label = item.label.replace(/^/gm, ' ').replace(/^./, bullet);
var current, index;
if (result.some(function(aItem) {
if (!aItem) return false;
if (aItem.items.some(arguments.callee)) return true;
current = aItem;
index = aItem.children.indexOf(item.id);
return index > -1;
})) {
if (current.items.length <= index) {
while (current.items.length < index) current.items.push(null);
current.items.push(item);
}
else {
current.items[index] = item;
}
}
else if (result.some(function(aItem) {
if (!aItem) return false;
if (aItem.items.some(arguments.callee)) return true;
current = aItem;
return aItem.id == item.id;
})) {
current.items.push(item);
}
else {
result.push(item);
}
});
var string = result.map(function(aItem) {
var children = aItem.items.map(arguments.callee).join('\n');
return children ?
aItem.label+'\n'+children.replace(/^/gm, ' ') :
aItem.label ;
}).join('\n')+'\n';
alert(string);
で、調べてみたら、やっぱりツリー構造がおかしい。ツリー表示はタブの属性値の方に基づいて行われてて、その属性値をnsISessionStoreのsetTabValue()
とdeleteTabValue()
でセッションの方にミラーしてるんだけど、ミラーされてるはずの値が期待通りにミラーされてないようだ。
追記。
……nsSessionStore.jsを読んでたら原因が分かった。
setTabValue()
では内部で最後にsaveStateDelayed()
を呼んでいるため、変更がファイル(プロファイルフォルダ内のsessionstore.js)にすぐ書き出される。deleteTabValue()
ではsaveStateDelayed()
が呼ばれてないために、他の処理の中でsaveStateDelayed()
が呼ばれるまでは変更がファイルに書き出されない。deleteTabValue()
だけで情報をミラーしたつもりでいると、ゴミ情報が残ったままになってしまうことが多々ある。そのゴミ情報が邪魔をして、期待通りにツリー構造が復元されなくなってしまっている。deleteTabValue()
する前にsetTabValue()
で空の値をセットして強制的にセッション情報を書き出させるようにしてみたところ、上記のスクリプトで調査してもセッション情報との間でのツリー構造の不整合は検出されなくなった。
結論:deleteTabValue()
マジ使えねえ……
追記。
それでも全然駄目だった。Firefoxがセッション情報をファイルに書き出す時、読み込み中であるというフラグが立っている(Firefox 3.5以前ではtab.linkedBrowser.parentNode.__SS_data._tabStillLoading
、Firefox 3.6以降ではtab.linkedBrowser.__SS_data._tabStillLoading
がtrue
である)タブについてはキャッシュされた情報を書き出すようになってるのに、このフラグがタブの内容の読み込み完了後も立ちっぱなしになってるせいで、常にキャッシュされた古い情報が書き出されてしまい、setTabValue()
で設定された新しい値が無視されてしまう。
結論:nsISessionStore/nsSessionStore.jsは腐ってる……
まとめると、以下のようなメソッドを使うようにしてやれば色々と幸せになれそうです。
setTabValue : function(aTab, aKey, aValue) {
if (!aValue) return this.deleteTabValue(aTab, aKey);
try {
this.checkCachedSessionDataExpiration(aTab);
this.SessionStore.setTabValue(aTab, aKey, aValue);
}
catch(e) {
}
return aValue;
},
deleteTabValue : function(aTab, aKey) {
try {
this.checkCachedSessionDataExpiration(aTab);
this.SessionStore.setTabValue(aTab, aKey, '');
this.SessionStore.deleteTabValue(aTab, aKey);
}
catch(e) {
}
},
checkCachedSessionDataExpiration : function(aTab) {
var data = aTab.linkedBrowser.__SS_data || // Firefox 3.6-
aTab.linkedBrowser.parentNode.__SS_data; // -Firefox 3.5
if (data &&
data._tabStillLoading &&
aTab.getAttribute('busy') != 'true' &&
aTab.linkedBrowser.__SS_restoreState != 1)
data._tabStillLoading = false;
},
2010年1月29日追記。Firefox 3.6以降とFirefox 3.5以前でフラグが保存される場所が少し違っていたので、その旨を修正した。
2010年9月27日追記。Firefox 4 の最適セッションリストア(原文)の影響によって、まだ実際にはセッションが復元されていないタブなのに、ビジー状態でなくなっているというケースがあり得るようになった。そのため、aTab.getAttribute('busy')
だけでビジー状態を判別すると、これからセッションを復元して欲しい・読み込み中のタブであるにも関わらず_tabStillLoading
をfalseにしてしまい、セッションが復元されなくなってしまうという問題が起こっていた。なので、タブの属性値と併せてaTab.linkedBrowser.__SS_restoring
も確認するようにサンプルコードを修正した。
2010年12月6日追記。aTab.linkedBrowser.__SS_restoring
が廃止されてaTab.linkedBrowser.__SS_restoreState
というプロパティが使われるようになっていたので、それにあわせてサンプルコードを修正した。
History Tree :: Firefox Add-ons
履歴をビジュアルに見たい、グラフ化して見たいという考え自体はやっぱりよくある話なのかなー。自分も過去にWeb Mapなんてものをやったしなあ。
Web Map、チャンスがあったら作りなおしてみたいところではある。今だったらHTML Canvas使ってもっと軽く作れるだろうから。(必要な道具はある、というだけで、自分の方にその技能があるわけではないんだけど)
その名もズバリ「Open Bookmarks in New Tab(ブックマークを新しいタブで開く)」。何のひねりもない。
ツリー型タブにこの機能を付けれという要望が何度も何度もいろんな人から寄せられていて、しかしどうも調べてみると、Tab Mix Plusの一機能としてはこういう機能があるものの、これだけを実現してくれるアドオンが実は存在してなかったらしい(userChrome.jsを使える人はそっちで解決してしまうから、アドオンでなければ使えないというレベルのユーザには行き渡っていない?)、ということで作った次第です。userChrome.jsでやる人が多いんだろうなあということからも分かるように、メインの実装はたったこんだけ。これ以上機能を追加するつもりはないです。全く。
工夫?というか、実際使ってみて感じたことをフィードバックした点としては、ブックマークを中クリックした時にも常にタブで開くようにしてる、というあたりでしょうか。
作り手としてバカ正直に考えると、「普通の左クリックと中クリックの挙動をそれぞれ反転させればいいんじゃね?(そうすれば新しいタブで開きたい時と現在のタブに読み込ませたい時に使い分けれて便利じゃね?)」ということでそうしてしまいそう(事実、最初はそうしてた)なんだけど、実際に使ってみると自分の場合はブックマークを中クリックすることが癖になってて、タブで開きたいのに現在のタブに読み込まれて「ムキー!!!」となってしまった。
それに、こういう要望を出す人というのは多分、ミドルクリックでタブで開けるということをそもそも知らない(ミドルクリックという操作がある事自体を知らない)か、2ボタンマウスを使ってるかで、ハナから操作を使い分ける気なんか無いんだろうなあ。とも考えられる。
つまり「操作によって挙動を変えるという自由」が、混乱の元であったり、そもそも誰もそんな自由を欲してないんじゃないか、と。なので、「左クリックでも中クリックでもとにかくブックマークは新しいタブで開く」という挙動を初期設定としておいた。設定を変更すれば、中クリックした時は現在のタブに読み込ませるという挙動にもできるけど、作者の推奨設定はあくまでこうですよってこと。