たまに18歳未満の人や心臓の弱い人にはお勧めできない情報が含まれることもあるかもしれない、甘くなくて酸っぱくてしょっぱいチラシの裏。RSSによる簡単な更新情報を利用したりすると、ハッピーになるかも知れませんしそうでないかも知れません。
の動向はもえじら組ブログで。
宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能!
Firefoxのマルチプロセス設計への移行がいよいよ目前に迫っている。
Firefoxにおけるマルチプロセス化のための仕組みそのものはElectrolysis、略してe10sと呼ばれており、Firefox 4の頃から既に入っていて、Firefox Hacks Rebootedでも詳しく解説してたんだけど、ブラウザのUIを動かすプロセスとコンテンツ領域のプロセスを分けるというのはFirefoxではかなりの大ごとだった。一時は「こんなん無理!」っつって計画が凍結されてたほどだったと記憶してる。
e10s移行が困難だった理由は、多分、Firefoxというアプリケーションそのものの設計にあったのだと思う。FirefoxというWebブラウザは、「GeckoというHTML・XMLレンダリングエンジンの上で動作するJavaScript製のローカルWebアプリケーション」といった感じの設計になっている。なのでコンテンツ領域内の操作のためのコードとUI部分の操作のためのコードが非常に似通っていて、シームレスに連携しやすかった、否、「連携しやすすぎた」と言える。あまりにシームレスに連携できたせいで、ブラウザのUIに関わるコードとコンテンツ領域内の操作に関わるコードが渾然一体の密結合になってしまい、それが「UI部分とコンテンツ領域内との間はテキストメッセージだけを非同期に通信しあう」というe10sベースの設計への移行を阻んでいた、という事なのではないか……と僕は思っている。
どういう理由があったのかは知らないけど、最近のバージョンのFirefoxではe10sベースの設計への本格的な以降のための準備が着々と進められている。上記のような密結合だった部分が「UI領域専用」「コンテンツ領域専用」「両方で共通」といった感じに分離されてきているし、同期処理前提だった部分が非同期処理前提に改められている。 セッション保存の仕組み(ページ内のテキストボックスへの入力内容を保存する所とか)、リンクのクリック操作のハンドリング、その他多数の部分が大幅に書き直されている。アドオンから見た時のAPI的な互換性は可能な限り残すようにしてあるようで、その努力の様子が非常に興味深い。
で、アドオン側での対応をどう進めるかなんだけど、実際にいくつかのアドオンをe10s対応に改修してみて、アドオンの性質によって典型的なパターンがあるようだという事が分かった。
browser
のcontentWindow
やcontentDocument
に一切触れず、コンテンツ領域内からBubblingしてくるイベントも捕捉しないタイプのアドオンは、何も考えなくてもそのままe10sで動く。var linkURIs = Array.map(gBrowser.contentDocument.links, function(aLink) { return aLink.href; });
と書けたけれども、こういう事ができなくなる。
UI領域とコンテンツ領域の境界をまたぐ時に非同期処理を挟まないといけないので、処理を「UI領域からコンテンツ領域へ、リンクを収集する指示のメッセージを送る」「コンテンツ領域で指示のメッセージを受け取って、リンクを収集し、UI領域へ結果を報告するメッセージを送る」「UI領域で報告のメッセージを受け取って、次の処理を行う」という3つの段階に分けなくてはならない。
マルチプルタブハンドラの場合、「選択したタブの情報をクリップボードにコピーする」という機能のために、タブ(で開いているページ)の情報からクリップボードにコピーするための文字列を得る処理をコンテンツ領域側に移動した上で、UI領域からコンテンツ領域へ・コンテンツ領域からUI領域への橋渡しを行うためのコードを追加し、前後の処理をPromiseで繋ぐようにした。上記の5パターンの最初の方の物ほど実装が容易で、後の方の物ほど実装が面倒になる(その上、メッセージの往復を待たないといけないのでオーバーヘッドも大きくなる)。 特に深い意味もなく後の方のパターンで実装していた機能は、どうにかして前の方のパターンで実装できないか検討した方がいい(Firefox本体の設計変更も、おそらくそういう風に進められたのではないかと思う)。 実際に、情報化タブではWebページのサムネイル画像を取得する処理について、元々はUI領域で「サムネイルが必要だ」となったタイミングでその都度同期処理していたのだけれども、まずサムネイル取得の処理はコンテンツ領域に移し、処理が走るタイミングについて、コンテンツ領域側で発生したイベントをトリガーとしてサムネイル画像をUI領域側にpushで送りつけるように改めた。 これは上のリストで言うと、5番目のパターンから4番目のパターンに設計変更した、ということになる。
それでもどうしても5番目のパターンで実装しないといけないケースというのはあって、前後の処理とどうやって繋ぐか(同期処理だった物をどう非同期化するか)というのが問題になる。 自分の場合は、こういう時はPromiseを使うのがいいと思ってる(以前ならJSDeferredを使ってたんだけど、Mozilla Add-onsのレビューが通らないため、最近になってPromise.jsmのES6 Promise互換APIを使うようになった)。 ツリー型タブでも、コンテンツ領域内にプラグイン(Flashなど)で描画されている領域があるかどうかを調べるための処理で、このパターンが残っている。 Promiseを使うコードはUI領域側のブリッジにまとめてあり、こんな感じになってる。
sendAsyncCommand : function CB_sendAsyncCommand(aCommandType, aCommandParams)
{
var manager = this.mTab.linkedBrowser.messageManager;
manager.sendAsyncMessage(this.MESSAGE_TYPE, {
command : aCommandType,
params : aCommandParams || {}
});
},
checkPluginAreaExistence : function CB_checkPluginAreaExistence()
{
return new Promise((function(aResolve, aReject) {
var id = Date.now() + '-' + Math.floor(Math.random() * 65000);
this.sendAsyncCommand(this.COMMAND_REQUEST_PLUGIN_AREA_EXISTENCE, {
id : id
});
return this.checkPluginAreaExistenceResolvers[id] = aResolve;
}).bind(this));
},
handleMessage : function CB_handleMessage(aMessage)
{
// dump(JSON.stringify(aMessage.json)+'\n');
switch (aMessage.json.command)
{
...
case this.COMMAND_REPORT_PLUGIN_AREA_EXISTENCE:
var id = aMessage.json.id;
if (id in this.checkPluginAreaExistenceResolvers) {
let resolver = this.checkPluginAreaExistenceResolvers[id];
delete this.checkPluginAreaExistenceResolvers[id];
resolver(aMessage.json.existence);
}
return;
}
},
checkPluginAreaExistence
メソッドは、その実行を示すユニークなidを伴ってコンテンツ領域に指示のメッセージを送ると同時に、新たに生成したPromiseのリゾルバ関数を、idと対応付けて保持する。メソッドの戻り値はPromiseとする。という風にする事で、checkPluginAreaExistence
メソッドをthenableなメソッドとして他の処理の中に無理なく組み込める(次のコールバックには、コンテンツ領域側での処理で得られた結果が渡る)ようになってる。
UI領域→コンテンツ領域→UI領域 と境界を2回以上またぐ処理を実装する時は、多少の差異はあれどだいたいこんな感じになるんじゃないだろうか。
必要かどうかで言うと、このパターンでPromiseは必須ではなくて、Promiseのリゾルバ関数を保持・実行する代わりに、単にcheckPluginAreaExistence
メソッドでコールバック関数を受け取って、それを保持・実行するようにしてもいいんだけど。
非同期の処理同士を何度も書き連ねる時のコールバック地獄は見たくない(今はそういう連携をする必要がないとしても、今後いつ必要になるとも限らない)ので、僕は自分で書く非同期処理は、インターフェースとしては基本的にPromiseを使う方向で統一するように考えてる。
When the Multiple Tab Handler addon is installed, I cannot drag a tab to move it. Even if I start dragging on a tab, mouseover-ed tabs are selected (highlighted) instead of moving the dragged one tab.
マルチプルタブハンドラがインストールされているとタブをドラッグで移動できません。タブをドラッグしようとしても、そのタブが移動する代わりに、ポインタが上に載ったタブが選択(ハイライト表示)されるだけという結果になってしまいます。
The addon provides ability to select multiple tabs and do some actions for them. Long-press of your left mouse button on a tab starts the selection, moving mouse cursor selects mouseover-ed tabs, and releasing the button shows the menu for selected tabs.
On the other hand, if you move your mouse immediately after you press the left mouse button on a tab, then the tab is just dragged and moved as you expected. Remember, long-press selects tabs but short-press starts dragging of the tab. To change the threshold, see the configuration dialog of the Multiple Tab Handler.
By the way, you can select tabs without dragging. When you do ctrl-left-click on a tab, it toggles the selection state of the tab. Shift-left-click on a tab selects tabs between the clicked tab and the current tab. This behaviour is same to the one of Microsoft Excel, Windows Explorer, and others. After that, you can open the menu for selected tabs with right-click on a selected tab. If you do short-press of the left mouse button on a selected tab and start dragging immediately, then selected tabs are dragged and moved together.
このアドオンは、選択された複数のタブにまとめて同じ操作を行う機能を提供します。タブの上でマウスの左ボタンを長押しするとタブの選択が始まり、マウスカーソルと動かすとカーソルが上に載ったタブが選択され、ボタンを放すと選択されたタブのためのメニューが開かれます。
他方で、タブの上でマウスの左ボタンを押下してすぐにマウスを動かすと、タブの選択ではなく、通常通りのタブのドラッグ(移動)操作となります。憶えておいてください、長押しすると選択が始まって、長押ししないですぐにマウスを動かすとタブのドラッグです。長押しと判定する時間の閾値はマルチプルタブハンドラの設定ダイアログで変更できます。
ちなみに、長押しからのドラッグ以外の方法でもタブは選択できます。タブの上でCtrl-左クリックすると、タブの選択状態が反転されます。また、Shift-左クリックすると、クリックされたタブと現在のタブまでの間がまとめて選択されます。これはMicrosoft ExcelやWindows Explorerなどの他のアプリケーションと同じ挙動です。そうしてタブを選択した後に、選択されたタブの上でマウスの右ボタンを押すと、選択されたタブのための操作のメニューが表示されます。選択済みのタブの上でマウスの左ボタンを押下してすぐにマウスを動かすと、選択されたタブをまとめてドラッグ(移動)する事もできます。
On Firefox 29 and later, I cannot hide the title bar anymore if Tree Style Tab addon is installed. Is this a bug? How can I hide the title bar?
Firefox 29以降のバージョンでTree Style Tabを使うとタイトルバーを非表示にできません。これはバグではないのですか? どうすればタイトルバーを非表示にできますか?
Install Tree Style Tab 0.14.2014050601 (or later) and another addon Tabs on Bottom 0.5 (or later) together. If both addons are installed, the title bar becomes hidden.
This is not a bug but a designed behavior of TST, because that affects ability hiding the title bar: "is the tab bar on top of the window or not?" Old Firefox (Firefox 28 or older) had the "Tabs on Bottom" mode, and the navigation toolbar worked like the title bar - draggable to move the window itself. Because TST moves the tab bar away from the top of the window, TST always chose the mode automatically, for the usability. However, the "Tabs on Bottom" mode has been removed on Firefox 29. If TST moves the tab bar away, you cannot move the window by dragging of the navigation toolbar anymore. To avoid such an inconvenience, now TST forces to Firefox to show its title bar.
On the other hand, Tabs on Bottom addon re-introduce the "tabs on bottom" mode to Firefox 29 and later. Because it is not a "tree" feature, and there may be some people who just want to use "tabs on bottom" mode without "tree" features, I decided to keep it as an independent addon. See also another FAQ.
ツリー型タブ 0.14.2014050601(またはそれ以降)と、別のアドオンであるTabs on Bottom 0.5(またはそれ以降)をインストールしてください。両方のアドオンが同時に有効になっていれば、タブバーが非表示になります。
これはバグではなくツリー型タブの設計上意図された挙動です。何故なら、タイトルバーを隠せるかどうかは、タブバーがウィンドウの最上部にあるかどうかに依存するからです。Firefox 28またはそれ以前の古いFirefoxには「タブを下部に表示」というモードがあり、そのモードではナビゲーションツールバーがタイトルバーのように働くようになっていました(例えば、ドラッグするとウィンドウ自体を移動できるなど)。ツリー型タブはタブバーをウィンドウの最上部から取り除いてしまうので、利便性のために、自動的に「タブを下部に表示」モードを有効にするようになっていました。しかしながら、Firefox 29ではそのモード自体が削除されてしまったため、単にタブバーをウィンドウの最上部から取り除いてしまうと、ナビゲーションツールバーのドラッグ操作ではウィンドウを移動できなくなってしまいました。このような不便を回避するために、現在、TSTはFirefoxに対して常にタイトルバーを表示するように強制する設計となっています。
他方で、Tabs on BottomアドオンはFirefox 29以降のバージョンに対し再び「タブを下部に表示」機能を提供します。その機能は「ツリー」と全く関係がなく、また、単にその機能だけを使いたい(ツリー機能は必要ない)という人がいるであろうという事から、その機能はTSTに統合せずに別のアドオンとしてリリースしています。別のFAQも参照してください。
On Firefox 29 (and later), I cannot open a new tab by double-click on the empty area of the tab bar, with Tree Style Tab. Is this a bug?
ツリー型タブを使っているとき、Firefox 29以降のバージョンではタブバー上の余白をダブルクリックしても新しいタブを開けません。これはバグではないですか?
No, it's not a bug of Tree Style Tab. In old Firefox (Firefox 28 or older), double-click on the empty area of the tab bar opened a new tab actually, but it was a feature of Firefox itself. The action was available only when you choose "Tabs on Bottom" mode, and Tree Style Tab always chose the mode automatically. In the "Tabs on Top" mode, your actions on the empty area of the tab bar worked as on the title bar - for example, it maximized the window itself on Windows. Because the "Tabs on Bottom" mode has been removed on Firefox 29, now double-click on the empty area always works as on the title bar.
Because it is not a feature related to "tree of tabs", I won't include it to TST in the future. Certainly TST already has some features not related to "tree of tabs", but there is a criterion for those exceptions: is the feature provided by Firefox itself, and does TST break it? Then, now Firefox 29 itself doesn't open a new tab by double-click on the tab bar, so TST also never do it. Sorry, but I have to draw a line somewhere.
Instead, you should use another addon who provides the feature: double-click on the empty area on the tab bar to open a new tab, for Firefox 29 and later. For example, my another addon Tabs on Bottom.
いいえ、それはツリー型タブのバグではありません。古いバージョン(Firefox 28以前)のFirefoxでは確かにタブバーの余白をダブルクリックすると新しいタブが開かれましたが、それは「タブを下部に表示」モードの時だけ使える、Firefox自体の機能です。ツリー型タブは単に、「タブを下部に表示」モードを常時有効にしていただけです。元々、「タブを上部に表示」モードでは、タブバーの余白での操作はタイトルバー上での操作と同等に扱われていました(例えば、ダブルクリックでウィンドウを最大化するなど)。「タブを下部に表示」モードがFirefox 29で削除されてしまったため、タブバーの余白でのダブルクリックは常にタイトルバー上での操作と見なされます。
この機能は「タブのツリー」というコンセプトに直接は関係しないため、今後もツリー型タブにこの機能を含めるつもりはありません。ツリー型タブはFirefox本体に元々備わっている機能をなるべく阻害しないことを目標にしており、そのために実際にツリーと関係無い機能を入れている部分もありますが、この機能については現在はFirefox本体にそもそも含まれていないので、そのような特別な対応をする予定も残念ですがありません。
なので、どうしても必要な場合は、代わりに、タブバーの余白のダブルクリックで新しいタブを開く機能を提供している別のアドオン(例えばTabs on Bottom)を併用してください。
Mozilla Corporationの人から、「なんでツリー型タブはFull Reviewを受けてないんだい? about:addonsからの検索にヒットしないから、ユーザがインストールするのに面倒だよ。You申請しちゃいなよ!(意訳)」的なメールを頂いた。ありがたいことだ。
Mozillaのアドオンポータルサイトである所のMozilla Add-onsでは、アドオンを登録するにあたってPreliminary Review(事前審査)とFull Review(完全審査)という2段階の審査がある。登録したアドオンはまずPreliminary Reviewでセキュリティ面などの大雑把な審査を必ず受けて、通過できなければそのアドオンはAMOへの掲載すらかなわない。ここで問題なしとなれば晴れて「実験的アドオン」としてサイトに掲載されるようになるけど、Firefoxのアドオンマネージャからの検索にはヒットせず、おすすめアドオンとしてもノミネートされないので、これではまだ単に「載っただけ」。そこで一定の支持を得たら、次の段階としてFull Reviewを申請して品質面その他を審査してもらえる。Full Reviewを無事通過すれば、Firefoxのアドオンマネージャからの検索にヒットするようになったり、おすすめとして紹介して貰えたりするようになる。定番アドオンと言われるような物は、代替はこのFull Reviewを通過した状態になってる。
前から見てる人は把握してるかもしれないけど、僕が作ってるアドオンのうちいくつかは、Preliminary Reviewのみ通過していて実験的アドオンのままになっている。新しく作って登録した物は当然なんだけど、それだけでなく、過去にはFull Reviewを通っていた物が、ある時を境に審査にパスしなくなってそれっきりになっている物もある。ツリー型タブもその1つだ。
何故かというと、簡単に言えば、最初のFull Reviewの頃には審査が甘かったから通過できていたのが、その後の審査基準見直しで通らなくなったということ。ただ、僕としてはこの時の裁定にはあまり納得がいってなくて、evalが危険でそれ以外の方法が安全だと思ってる人へ( 英訳)というエントリでタラタラ不満をぶちまけたりもしてた。
そういう経緯があったから、大人げないオッサンとしては「いやーFull Reviewしたいのは山々なんですけどねーeval()
使ってるから駄目っておたくんとこの人達が言うもんですからねー」と嫌味全開で返したくもなったんだけど、そこは我慢して、審査基準に合わないと言われたからFull Reviewしてないんですよ、審査基準が変わったなら再申請するのはやぶさかでないけどそうでないなら多分通過しないと思いますよ、でもまあ確かにeval()
使ってるとレビューしにくいだろうししょうがないっすね、と、そんな感じで返信するに留め……られるほどにはやっぱり大人になりきれておらず、嫌みったらしく上記のエントリ(英語の方)のURIを貼り付けてメール送信してしまいました。ああ、なんとも底の浅い人間である事よ……
でもまあ、その時のレビュワーの人とは無関係であるにせよ、公式サイドの人から「Full Reviewに値する」という評価を貰えた事には「やってやったぜ」という胸のすく思いで、4年越しで溜飲を下げたのでした。
Scrollbar Like Scrollerを更新した。
1.0/1.1での大きな変更点として、仮想的な「つまみ」を導入して、それ以外の場所では反応しないようにした。使い勝手が悪くなったと見る向きもあるかもしれないけど、前の挙動に戻すつもりはないです。
というのも、僕が自分で実際に使ってた感想として、何も無いところでスワイプやパンスクロールしようとして急に画面が飛んでしてしまう(スクロールバー風操作の開始と判断して、指があった位置から計算したスクロール位置まで強制的にスクロールしてしまう)という誤爆が何度かあってイラッときてしまったのです。
スクロール位置を示すインジケーター自体はFirefoxも提供してるんだけど、これは注意深く見ないと気がつかないようなさりげなさで表示されているので、UIとしてこれを目印にするのは辛い。と思ったというのもあって、ぶっとい「つまみ」に相当する物を自前で表示するようにした。これをなるべく正確な位置に表示したくてああだこうだと座標をいじくり回した結果、指のある位置からのスクロール位置の算出が前よりグッと正確になったので、そういう意味でも成果はあったと思う。
もう1つの変更として、画面の上端や左端もスクロールバーとして動作するようにした。右手でスマホを持って親指でスクロールしてる時は右にスクロールバーが出てていいんだけど、左手で持って親指でスクロールしてると、右端に出るつまみまで指が届かなかったので。最後にパンスクロールした時のタッチ位置が画面の1/3より左だったら左につまみが出るようになってるけど、つまみが出てない時でも、つまみがあるであろう位置を指で操作すればスクロール操作は開始できます。
Nexus 7を買って以降、Firefox for Androidをエンドユーザとして使うようになってそれなりに経つんだけど、布団の中でWebブラウズする時間がだんだん長くなってきて(病気で……とかじゃなくて、単にPCの前に座って作業するのが億劫になってきたという事です)、触る時間・頻度が増えてくると、色々細かい所で「イラッ」と来るようになってきた。
アドオンを作れば解決できるんだろうなあと思ってはいたんだけど、既に何十とリポジトリを作っていてメンテナンスしきれてない僕がまた新しく抱え込むのは世界に不幸を増やす事になるのではないか……と思って、「他の誰かがやってくれないかなあ」と淡い期待を抱きながら待ってたんだけど、僕の不満が解消されるような物が出てくる気配が一向になかったので、カッとなった勢いで2つほど作ってAMOに登録してみた。ああ、またつまらぬ物を作ってしまった……
Scrollbar Like Scroller(スクロールバー風スクロール)は、いくつかのAndroidアプリ(具体的にはJota Text Editorとか)で実装されていた「基本的にはパンスクロールだけど、必要に応じてスクロールバーも使える」的な挙動を実現する物。はい、どこからどう見てもパクリです。
ビューポートの大きさの解像度とタッチイベントの座標の解像度が違うという点でドツボに填って難儀したけど、タッチイベントの座標を現在のズーム率とかけ算した値がどうやらビューポートのサイズと同じ解像度になるようだったので、どうにかそれらしい動きを実現できた。
縦にクソ長い2chまとめだとかTogetterまとめだとかを見た後で最初の方までスクロールするのにパンスクロールのためのスワイプ操作で画面をひたすらこするのにウンザリしていて、このままじゃ画面か指が削れて無くなっちまうよ!!!という悩みはこれで解消されると思う。
Open Local File(ローカルファイルを開く)は、名前の通りで、なぜかAndroid版Firefoxに標準で付いてない「ローカルファイルを選択して開く」機能を加えるだけの物。一旦ダウンロードして保存したXPIファイルをファイルブラウザからFirefoxを指定して開こうと思ったら、関連付けでJota Text Editor固定になってしまっててどう頑張っても開けず、関連付けを設定し直すのもイヤだったので、作った。
nsIFilePickerでやってるんだけど、modeGetFolderが未実装だったり最後に開いたディレクトリを記録・反映できなかったりと、PC版と同じように作るのはやはり難しいのだなあ。という事を改めて思い知らされた感じです。
あと選択範囲のリンクをまとめて保存する機能なんかも欲しかったんだけどゼロから作るのはさすがに辛かったので、これはSave Link Menusを改造して作った。
デスクトップ版FirefoxとAndroid版FirefoxではAPIが違う部分が多いので(低レベルのAPIは共通でもUIに近い部分は全然違う)、まだまだおぼつかない感じだし、ちょっと凝ったことをやろうとするとドキュメントが無いというかつてのMozilla Application Suite時代のアドオン開発を想起させられる手探りっぷりだけど、必要になったらさっと作れるくらいにはなれるといいなあ。
This is the English translation of my another entry.
I decided to reject a pull request for TST, adding new secret preference to disable animation effects around drag and drop of tabs, because it contradicts the principle of the TST project. This entry describes why I rejected the pull request.
Because it is introduced by Firefox itself. After the animation effect is activated on Firefox, I updated TST to follow it. However, it was unwelcome update for some people and I got many requests like "it is hard to operate tabs with drag-and-drop", "I want an option to disable animation effects during tabs are dragged." Actually, I can see two issues on the issue tracker:
The patch of the pull request adds a secret preference to do it. It is enough small and clear. If I added the option, I wrote just same patch.
But I disagree to merge the pull request, because I think that the approach of the patch is similar to a story: "Firefox's Gecko engine is too buggy and less compatibility to WebKit, so why don't you delete all codes of Gecko and introduce WebKit with Firefox-like UI?" In other words, it is very easy to add new option which is requested by people, however, I'm extremely reluctant to do it beacuse it is opposed to my polify on Tree Style Tab project.
Basically, this project depends on Mozilla Firefox project --which is very large and uncontrollable by me-- and it is unavoidable to be tossed up and down by the storm of changes in Firefox. Actually, on my another project, I had to rewrite the addon for new versions of Firefox again and again. I learned through the bitter experience that I should have some strict policies on my addon projects:
Don't re-implement a feature included in Firefox itself. For example, Tab Mix Plus has its own session management mechanism, because TMP project is started before the session management feature is introduced by Firefox itself. If the TMP project was started after that, they project team didn't implement such a custom session manager. There is no merit to implement a custom feature which conflicts to Firefox's.
To reduce maintenance cost, and to keep better compatibility with other addons which are developed based on Firefox's APIs, I think I should update and re-construct my addons for new APIs introduced by Firefox. It is better than I struggle to keep old custom implementations against Firefox's changes.
However, sometimes I decide to keep my custom implementations, when it is hard to rewrite codes for Firefox's new API for me.
For example, TST uses a library "JSDeferred" to process asynchronous operations easily. On the other side, lately Firefox uses Promise and Task for the same purpose. I know I should rewrite TST based on them instead of JSDeferred, but I still don't do it because: 1) TST is strongly designed based on JSDeferred. 2) Promise/Task are not available on the current ESR (Firefox 17). (In other words, I'll merge pull requests to do such a reconstruction, if there is no disadvantage about compatibility with other addons.)
When the API of Firefox's library is too untrustworthy for me, I decide to use my custom library based on very low stable APIs.
For example, Firefox has a system named "preference" which can save/restore users' configurations. Because it is not developer-friendly (ex. there are three deferent types --boolean, integer, and string--, and hard to observe changed configurations dynamically), Firefox provides some libraries like FUEL. But, such libraries are untrustworthy and risky for me because Firefox team sometimes changes those APIs despite they promised those APIs are developer-friendly --obviously they should be stable and safe--. I don't want to spend time to update my codes for such unstable APIs, so I actually use my custom library modules/lib/prefs.js which is based only on very stable low APIs. It is one of reasons why I don't update my codes for Promise/Task yet.
Anyway, I think that FUEL APIs are untrustworthy because Firefox team created FUEL just for third party add-on developers, not themselves. Because Promise/Task are used everywhere in Firefox, Firefox team will keep them stable for themselves.
Don't include features not related to the main concept. The basic concept is: what I want to use, one feature per one addon, and, as minimum as possible.
In my old blog entry (note: written in Japanese), I told that: features which I never use or unrelated to the main concept may satisfy users in the short term, but it will shorten life of the project in the long term. Basically I develop and publish my addons on GitHub because I need it and I want to keep it available for me. So I don't want to introduce changes which can disrupt the concept.
Provide higihly compatible, natural look-and-feel for Firefox's built-in features and other addons. For example, Firefox has a feature "auto hide toolbox" for the fullscreen mode started by F11 key. And, TST also provides "auto hide tab bar" feature. Yes, it is not related to "tree" feature. But if TST doesn't have the feature, you'll see unexpected vertical tab bar in the fullscreen mode. You press F11, you expect that the web page becomes fullscreened, then the vertical tab bar should not appear on the scene.
This is the main reason why I took much time to update TST to support drag-and-drop animation effects. Before the animation effect is introduced to Firefox, I respected behaviors of drag-and-drop around layers and objects on Adobe Illustrator. This behavior is still available when you drag a link to the tab bar.
Based on the above policies, I disagree to merge this pull request to TST's master, because:
Because there is no option to enable/disable animation effects, I think that Firefox team basically designs Firefox to do drag-and-drop operations with animations. Then, I should keep TST along the design of Firefox itself.
Actually disabling animation effects by userChrome.css or other ways sometimes break Firefox itself. For example, the internal operation to finalize closed tabs uses "TransitionEnd" DOM event to trigger itself, but it didn't workcorrectly because the event was never fired if the animation efect was disabled by userChrome.css. (I don't kwnow the bug is already fixed or not.)
I don't think my addon should be specially hospitable for people who live without animation effects, because Firefox itself disfavors them.
This is just my personal, current opinion. Of course I don't think this is the final truth of the topic. If you have information which can solve my worrying, or if you explain compelling reasons that I should do it, then I possibly merge such a change.
Otherwise, I'm sorry but I never merge such pull requests to my master repository. Then please fork this project, extend, maintain, and release it for people who have same distress - it is my stance on this project. To keep my codes forkable -- this is one of reasons why I distribute all codes of TST under OSS licenses.
English version of this entry is available.
ツリー型タブに頂いたプルリクエストについて、ポリシー上の理由から取り込めないなあと思ったのだけれども、これまで、参照しやすい形でまとまった文章としてポリシーを表明していなかった気がするで、何故この変更を取り込まないか(タブをドラッグ&ドロップするときのアニメーション効果をOFFにできるようにしないのか)の判断理由を頑張って文章にしてみました。アーカイブとして、少し手直し&追記してこちらにも転記しておきます。
Firefox本体の挙動としてタブのドラッグ&ドロップ操作にアニメーション効果が適用されるようになって以後、ツリー型タブにも、その挙動に合わせるための変更を随時行ってきました。
しかしながら、この変更は万人にとって望ましい結果をもたらしたとは言えず、「タブをドラッグ&ドロップで操作しにくい」「タブのドラッグ&ドロップ操作時のアニメーション効果を無効にできるようにして欲しい」といった意見・要望が何度か寄せられています。今すぐにパッと挙げられるだけでも、Issue Tracker上には以下の2つのIssueがありました。
メールを通じて寄せられた意見も合わせると、無視できない数の人がこの件に関心を持っているようだ、という印象を僕は持っています。
冒頭に挙げたプルリクエストは、ドラッグ&ドロップ時のアニメーションを無効化する設定をツリー型タブに加える、という物でした。変更箇所は最小限で、内容もおかしなことをしている部分はなく、コードとしてはそのまま取り込んで全く違和感のないパッチです。
ですが、自分の認識としては、このパッチで示されている方向での対応というのは言わば、「FirefoxのGeckoエンジンはバグだらけだしWebkitと互換性が低いから、Geckoを全部消して、WebkitにFirefox風のUIを付けた物を次のバージョンのFirefoxにしますよ」というような物なのではないか、と考えています。……という例えは極端なのでもう少し普通の言葉で言い直すと、「確かに皆が望む通りに対処することは簡単なのだけれども、プロジェクトの運営ポリシー的に、この点で皆が望む通りの対処の仕方をする事には非常に慎重である」ということになります。
大前提として、このプロジェクト(ツリー型タブというアドオンの開発プロジェクト)は、Mozilla Firefoxという自分には制御のできない別のプロジェクトに依存している・振り回される事を避けられないプロジェクトである、という事をまず押さえておく必要があります。
ツリー型タブ以外も含めた自作のFirefoxアドオン全体で、Firefoxの仕様変更に追従するために大きな変更が必要だったことが過去に何度かあり、その時の苦い経験から、現在のところ、自分はこれらのアドオン開発のプロジェクトにおいて以下のような方針を立てるようにしています。
以上を踏まえてこのプルリクエストで触れている問題を考えると、自分は以下の理由から、この変更を行わない方が良いという考えを持っています。
以上の事は、あくまで現時点での自分の考えがそうであるというだけで、絶対普遍の真理であるとは考えていません。ここに挙げた数々の懸念点を払拭できる材料があったり、あるいは、そのような懸念があってもなおこのようにするべきであるという強い動機があって、僕が同意できるのであれば、変更を取り込むことにはやぶさかでないです。
でも、そうでない限りは、この種の変更については「フォーク版を作ってそっちで自由に開発していって下さい。そして、その機能を必要とする人自身の手でメンテナンスしていって下さい。」というのがこのプロジェクトの方針ということになります。僕がこれらのアドオン群にオープンソースライセンスを適用しているのには、そのような自由を保障しておきたかったからという理由もあります。
冷たいようですが、どこで線を引くのか・何を基準に線を引くのかを熟慮した結果、このような線引きに落ち着いたということで、同様の不満を抱いている方々にはどうかご理解を頂ければ幸いです。
1つ前のエントリで、Rubyでバッククォートで実行した外部コマンドの標準出力を何故か受け取れないと書いてたんだけど、追記した通り、これは「RubyスクリプトがCGIで実行されているせいで標準入出力がCGI用に使われており、バッククォートで起動した子プロセスはその標準入出力を引き継ぐから、子プロセスから標準出力に出した内容が文字列として呼び出し元のスクリプトに返される代わりに、CGI経由でクライアント(GitHubのService Hookのエージェント)に返されてしまっている」ということだった(すとうさんに教えていただいた)。
で、対策として spawn()
と Process.waitpid()
を使うと良いというアドバイスを頂いて、以下のように直してみた。
#!/usr/bin/ruby1.9.1
require "cgi"
require "shellwords"
require "json"
require "logger"
require "stringio"
SYNC_SCRIPT = "/home/piro/shared/or/tools/upload_nightly_xpi.sh"
RELEASE_SCRIPT = "/home/piro/shared/or/tools/release_addon.sh"
SSH_KEY = "/path/to/secret_key"
BASE_DIR = "/home/piro/shared/xul"
USER = "piro"
logger = Logger.new("/home/piro/shared/post-receiver.log")
#logger = Logger.new("/dev/null")
logger.level = Logger::INFO
# 子プロセスの実行結果(標準出力)を常に文字列で受け取るユーティリティ
def run(command_line)
pipe_in, pipe_out = IO.pipe
Process.waitpid(spawn(command_line, [:out, :err] => pipe_out))
pipe_out.close
pipe_in.read
end
cgi = CGI::new
puts "Content-Type: text/plain\n\n"
begin
payload, = cgi.params["payload"]
payload = JSON.parse(payload)
project = payload["repository"]["name"]
project_dir = File.join(BASE_DIR, Shellwords.escape(project))
makefile = File.join(project_dir, "Makefile")
if File.exist?(project_dir) and File.exist?(makefile)
logger.info "build #{project}"
sudo = "sudo -u #{USER} -H"
command_line = "#{sudo} #{SYNC_SCRIPT} -i #{SSH_KEY} -b #{BASE_DIR} -d #{project_dir}"
logger.info command_line
logger.info run(command_line)
command_line = "cd #{project_dir}; git describe"
logger.info command_line
current_commit = run(command_line).strip
logger.info current_commit
# 現在のコミットがタグを打ったまさにそのコミットである場合、
# git describeの結果はタグ名だけになる。
# なので、それをトリガーにしてリリース処理を走らせる。
if !current_commit.empty? && !current_commit.match(/\A.+-[0-9]+-g[0-9a-f]+\z/)
logger.info "=> release commit"
command_line = "#{sudo} #{RELEASE_SCRIPT} -i #{SSH_KEY} -b #{BASE_DIR} -n #{project}"
logger.info run(command_line)
else
logger.info "=> regular commit"
end
end
logger.info "ok"
p "ok"
rescue Exception => error
logger.error error
logger.info "ng"
p "ng"
end
run()
というのを定義していて、ここでパイプを作って spawn()
の子プロセスの標準入出力に設定して、実行が終わるまで待ってパイプから実行結果を文字列として読み出す、ということをしている。(パイプを使う方法もすとうさんに教えていただいた。)
あと、このスクリプトは ~/public_html 以下に置いてるんだけど、apacheユーザで実行されてしまってファイルのアクセス権が……という事にも地味に悩まされていて、それでsudoを使ってたんだけど、suexecというApacheモジュールを使うと良いと教えてもらって sudo a2enmod suexec; sudo service apache2 restart としてみた。でもこの状態で git pull とかさせるとどういうわけか「error: cannot open .git/FETCH_HEAD: Permission denied」と言われてしまって(whoの結果ではapacheユーザじゃなくpublic_htmlがあるユーザになってるのに、何故だ……)、それで結局相変わらずsudoしている。