Home > Latest topics

Latest topics 近況報告

たまに18歳未満の人や心臓の弱い人にはお勧めできない情報が含まれることもあるかもしれない、甘くなくて酸っぱくてしょっぱいチラシの裏。RSSによる簡単な更新情報を利用したりすると、ハッピーになるかも知れませんしそうでないかも知れません。

萌えるふぉくす子さんだば子本制作プロジェクトの動向はもえじら組ブログで。

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

Page 6/243: « 2 3 4 5 6 7 8 9 10 »

アドオンのナイトリービルドのようなものを置くようにした - Nov 24, 2012

リリースにまつわる諸々の作業が面倒くさくて、about:newtabには機能ができてすぐ対応したのに、リリースしてなかったせいで今頃「about:newtabを空のタブとして認識してくれないんだが」系の「不具合報告」が届くようになってきていて、他にも障害報告に対して「それmasterのHEADではもう直ってます」な場合が時々あって、あと時々「ナイトリービルドを提供してくれ」っていう声も見かけてはいたので、ちょっと頑張って/xul/xpi/nightly/でそれっぽい物を提供するようにしてみた。

  • 毎日定期的にリポジトリの変更をpullして、変更があったらXPIを作りなおしてアップロードする、ということを自動的にやってみてます。
  • 中身はmasterブランチのHEADをそのままパッケージングしただけの物で、typoで動かなくなってるような場合でもそのままアップロードしてしまうから、常用はお勧めしないでおきます。(とはいえ、この状態は僕自身の環境に極めて近いので、僕が常用しているアドオンについては、僕が不具合に遭遇し次第すぐに直すとは思いますが……)
  • 自動アップデートについては、リリース版とは別にこのナイトリービルド専用のupdate.rdfを提供するようにしてます。

どうやってるかというと、自宅にあるUbuntuマシン(NASも兼ねていて24時間動きっぱなしの自宅サーバ)上でcronjobを動かしているだけ。1点だけ工夫が必要だったのは、update.rdfに署名するという部分。update.rdfのデジタル署名にはMcCoyを使うんだけど、これだとcronjobとして自動実行させるのには都合が悪いので、MDNのページで紹介されてたMX-Toolsというのを使うようにした。

前提はこんな感じ。

  • アドオンのリポジトリはgitで管理している。
    • gitリポジトリには、パスワード無しの秘密鍵でSSH接続できる。もしくは、手元にリポジトリをパスワード無しでcloneしている(GitHubなら、httpsのURLを使ってcloneした場合がこれにあたる)。
    • すべてのリポジトリを、1つのディレクトリ(ベースディレクトリ)の中に、プロジェクト名と同じ名前のサブディレクトリでcloneしてある。
  • WWWサーバにはパスワード無しの秘密鍵でSSH接続できる。
  • RDFファイルの署名用にOpenSSLの鍵ペアを作成してある。
    • openssl genrsa -out keyfile.pem 1024
    • openssl rsa -in keyfile.pem -pubout -out keyfile.pub
    • 作成された秘密鍵・公開鍵はどちらもベースディレクトリ直下に置いてある。

特定の1つのアドオンについて「masterのHEADをpullして、変更があったらXPIにして、update.rdfを作って、アップロードする」というスクリプトは、試行錯誤の結果、こんな感じになった。

upload_nightly_xpi.sh:

#!/bin/bash

# MX-Tools等を同じディレクトリに置いているという前提で、
# このファイルのパスを基準にして、他のツールの位置を
# 指定するため、ディレクトリのフルパスを最初に得ておく。
tools_dir=$(cd $(dirname $0) && pwd)

remote_user=piro
remote_host=piro.sakura.ne.jp
remote_dist_dir=~/www/xul/xpi/nightly
actual_dist_dir=http://piro.sakura.ne.jp/xul/xpi/nightly

# ファイルの置き場所等はオプションで得る事にする。
while getopts ab:d:i: OPT
do
  case $OPT in
    # 変更が無くても強制的にアップロードする指定。テスト用。
    "a" ) always=1 ;;
    # keyfile.pem等を置いているディレクトリのパス。
    "b" ) base_dir="$OPTARG" ;;
    # リポジトリをcloneした先のディレクトリ。
    "d" ) project_dir="$OPTARG" ;;
    # SSH接続に使う秘密鍵のパス。
    "i" ) secret_key="$OPTARG" ;; 
  esac
done

if [ "$project_dir" = '' ]; then
  echo "no project directory specified"
  exit 1
fi

if [ "$base_dir" = '' ]; then
  echo "no base directory specified"
  exit 1
fi

if [ "$secret_key" = '' ]; then
  echo "no secret key specified"
  exit 1
fi

# sedのオプションの違いを吸収しておく。
case $(uname) in
  Darwin|*BSD|CYGWIN*) sed="sed -E" ;;
  *)                   sed="sed -r" ;;
esac

# pullした時のメッセージを見て、変更があったかどうかを調べる。
cd $project_dir
pull_result=$(git pull --rebase)
updated=$(if [ "$pull_result" != "Already up-to-date." -a \
               "$pull_result" != "Current branch master is up to date." ];\
          then echo "1"; fi)
if [ "$updated" = "1" -o "$always" = "1" ]; then
  package_name=$(cat $project_dir/Makefile | \
                 grep "PACKAGE_NAME" | \
                 head -n 1 | cut -d "=" -f 2 | \
                 $sed -e "s/\\s*//#")
  public_key=$(cat $base_dir/keyfile.pub | \
               grep -v -E "^--" \
               tr -d "\r" | tr -d "\n")

  # ナイトリービルド用として、install.rdfを書き換える。
  # バージョン番号の末尾に今日の日付を付ける。
  $sed -e "s/(em:version=\"[^\"]+)\\.[0-9]{10}/\\1/" \
       -i install.rdf
  $sed -e "s/(em:version=\"[^\"]+)/\\1.$(date +%Y%m%d)00.$(date +%H%M%S)/" \
       -i install.rdf
  update_rdf=${package_name}.update.rdf
  # 古いupdate.rdfが残っていたら消す。
  rm $update_rdf
  # update.rdfの参照先と、公開鍵を書き換える。
  $sed -e "s#/xul/update.rdf#/xul/xpi/nightly/updateinfo/${update_rdf}#" \
       -i install.rdf
  $sed -e "s#([^/]em:updateKey[=>\"]+)[^\"<]+#\\1${public_key}#" \
       -i install.rdf

  # 自動生成されたバージョン番号は、後で使うので控えておく。
  version=$(cat install.rdf | \
            grep "em:version" | head -n 1 | \
            $sed -e "s#[^\">]*[\">]([^\"<]+).*#\\1#")

  # ビルドした後、リポジトリの内容を元に戻しておく。
  # (次にpullする時に、未コミットの変更があるという
  # 警告が出ないようにする。)
  make
  git reset --hard

  file=$(ls *.xpi | grep -v "_noupdate" | head -n 1)
  # キャッシュが使われる事の無いように、
  # ユニークなダウンロード用URLにする。
  update_link=${actual_dist_dir}/${file}?version=${version}

  if [ "$file" != "" -a -f $file ]; then
    ssh_remote=${remote_user}@${remote_host}
    # 公開先のディレクトリを作っておく。
    ssh -i $secret_key ${ssh_remote} \
        mkdir -p ${remote_dist_dir}/updateinfo
    # XPIをアップロードする。
    scp -i $secret_key ./$file \
        ${ssh_remote}:${remote_dist_dir}/
    # XPIからupdate.rdfを自動生成し、それもアップロードする。
    $tools_dir/mxtools/uhura -o $update_rdf \
      -k $base_dir/keyfile.pem $file $update_link
    scp -i $secret_key ./$update_rdf \
        ${ssh_remote}:${remote_dist_dir}/updateinfo/
  fi
fi

exit 0

uhuraを使うには、依存パッケージを入れておく必要がある。

$ sudo apt-get install openssl unzip libexpat1-dev
$ cpan
(色々セットアップ。基本的には全部YesでOKだと思う。)
$ sudo cpan install  XML::Parser RDF::Core Digest::SHA1 Convert::ASN1

アドオン1つだけでテストしてみた。

$ ./upload_nightly_xpi.sh -b ~piro/xul \
                          -d ~piro/xul/autodisableime \
                          -i ~/.ssh/id_rsa.nopassword \
                          -a

これでうまく動くことを確認できたので、複数リポジトリを全部処理するスクリプトを作って、そちらをcronで走らせるようにした。

upload_nightly_xpis.sh

#!/bin/bash

tools_dir=$(cd $(dirname $0) && pwd)

# 鍵の指定などを丸ごと引き渡すようにしたいので、
# このスクリプト自身では使わないオプションについても
# 受け付けるようにしてる。
while getopts ab:d:i: OPT
do
  case $OPT in
    "b" ) base_dir="$OPTARG" ;;
  esac
done

if [ "$base_dir" = '' ]; then
  echo "no base directory specified"
  exit 1
fi

cd $base_dir
for dirname in *
do
  if [ -d $dirname ]; then
    ${tools_dir}/upload_nightly_xpi.sh -d $base_dir/$dirname "$@"
  fi
done

で、これを日に2回動かすよう、crontabに 0 7,19 * * * upload_nightly_xpis.sh -b ~piro/xul -i ~/.ssh/id_rsa.nopassword とかいう感じで書いてる。

仮想マシンでも何でもいいけど、Linuxな環境があると自動化がはかどるな……と思いました(手前味噌)。

2012年11月26日追記:スクリプトにミスがあってinstall.rdfに埋め込む公開鍵が置き換わっていなかったので修正した。

FirefoxとThunderbirdの導入案件で使ってるソフトウェアを公開し(て)た - Nov 09, 2012

2年前のブラウザー勉強会で、FirefoxやThunderbirdの一括導入と企業での導入事例についての話をやったけど、その時に「こういう物を使ってます」と話だけ出した「メタインストーラ(FirefoxやThunderbirdのインストーラをキックしてサイレントインストールした上で、アドオンも一緒にインストールする物。設定を作り込んだ状態のFirefoxやThunderbirdを一括導入するために使う)」だけど、実はちょっと前からGitHubでひっそりと公開してた。導入案件の相談があってもリソース配分の関係で受けられないという事が何度かあったので、ツール公開しとくから自分でできる人は自分でやってねという話です。

が、過去の案件で要望が上がる度に場当たり的に機能追加や修正を繰り返してきて、それなのに目の前の導入案件をこなす事に手一杯でドキュメントが整備できてなかったせいで、公開したはいいけど結局弊社(というか僕)でないと使えない状態だった。ドキュメントを書こうと思っても、設定の仕方とかが煩雑ですっきり解説できないものだから、ドキュメント整備もなかなか進んでなかった。

それがこのところ、Mozilla関係の作業が少し落ち着いてて時間を確保しやすかったので、じゃあやるか!と頑張って、ドキュメント整備と設定方法の改善をちまちま進めてた(解説しにくかったのは実装が宜しくなかったからで、実装がすっきりしていれば解説もしやすいわけで、ドキュメントを書く事が契機となって実装が改善されるというのはよくある話なのです)。その締めくくりとして、簡単なチュートリアル込みの使い方解説エントリを会社のブログで公開した。アドオンを同梱して導入するだけのインストーラなら多分そんなに迷わないで作れるんじゃないかなと思う。

このFx Meta Installerは、FirefoxのWindows版インストーラと同じくNSISスクリプトで開発してるんだけど、思い返してみれば、僕のNSISとの付き合いはPortable Firefox(Firefox for Androidではなく、USBメモリで実行ファイルとプロファイルを持ち運ぶアレ)からだった。NSISはインストーラを作るための物なんだけど、使いよう次第ではPortable Firefoxみたいなこと(プロファイルをテンポラリフォルダにコピーして、Firefoxを起動して、Firefoxが終了したらプロファイルをUSBメモリに書き戻す)もできる。ずーっと前にMozilla Japanが秋葉原と渋谷でFirefoxの入ったCD-ROMを配るというプロモーションをやった事があったけど、あの時配ってたCDの「ディスクを入れたらautorunでFirefoxがお試し用に起動する」っていうやつは、実は、Portable Firefoxを改造したか参考にしたかして僕が作ってたのですよね。それがなかったら今のFx Meta Installerはなかったかもしれないと思うと、ちょっと感慨深い。

あと、Fx Meta Installerの設定項目だったり機能だったりを見てると、ああこんな事もあったなあ……と過去の案件の事を色々思い出させられる。Linuxでもビルドできるようにしたのは、普段使ってるUbuntu入りのLet's noteだけ持ってお客さんの所で動作を試して直してビルドしてまた試して……というのをやるためだったし、解説エントリでは触れてないけど、メタインストーラのバイナリに電子署名するための設定を見てると、グローバルサインで証明書を買う手続きをしたなあという事を思い出すし。いまFx Meta Installerに実装されてる機能は、お客さんの要望だったり状況だったりの必要に迫られて追加したものがほとんどなので、ある意味でこいつは僕の仕事(の1つ)の履歴そのものとも言えるのかもしれない。

Tabs Outlinerとツリー型タブ - Sep 21, 2012

Tabs OutlinerというGoogle Chrome/Chromium用拡張機能がある。説明をよく読むと、Firefoxでツリー型タブを使っていた人が、Google Chromeに乗り換えるにあたって作った物らしい。

以前試した時は、リンクからタブを開いてもツリーが構築されないというのが「えっ」という感じであんまりよく試さないでアンインストールしちゃったんだけど、今日なんとなくまたインストールしてオプションを開いてみたら「Tree Style Tabs」ってチェックボックスがあって、そこに書いてあった説明を見たら「これONにしたら自動でツリーになるようになる」的な感じっぽかったので試してみたら確かにリンクから開いたタブが勝手に子タブになってくれた。

ということで、ツリー型タブにロックインされてしまってるせいでFirefoxからGoogle Chromeに乗り換えられないという人(時々そういう話を目にするのです)にとってこれはマジに救世主になるかもしれないなと思ってもうちょっとだけ試してみてた。タブの切り替えがダブルクリックじゃないとだめだったり、他の拡張機能との連携はさすがにできないっぽかったり、Google Chrome本体のメニューから終了したらツリー構造が保存されなかったりで、僕自身が乗り換える先としては厳しいなあ……とは感じたけど、そういう所が気にならない人なら、普通に使えるんだと思う。

そんな「Tree Style Tabs」モードなんだけど、チェックボックスの下にすごく長い注意書きが付いてた。僕が読み取れた大意としては、「そういう要望がものっそ沢山寄せられるから機能を付けてるけど、自分(作者)はこのモードはお薦めしない。試してみてもいいけど、このモードだけで使うんじゃなくて、このモードをOFFにしたTabs Outliner本来の状態でも是非使ってみて欲しい。」ということが書いてあるようだった。同様のことがTabs Outlinerの配布ページにも書いてあって、作者の人によると、ツリー表示は情報の一覧性が悪くて、フラットなタブのリストの方が可読性が高いぞ、と。ツリー、ずいぶん嫌われたもんですね……

この見解の違いは、タブとタブのツリーという物をその人がどう捉えているのか? という所から生じているのだと思う。

僕にとって、タブのツリーは(生存期間は短いけれども)ある情報を起点にしてあちこちさまよい歩いた足跡そのものであって、どのページからどのタブへ辿り着いたのかが視覚化されているということが非常に重要だ。僕はその関係性を目で追って「分かれ道になったノードはどれだったっけ」と探している(ちなみに、この時見ているのは主にfaviconと情報化タブが提供するサムネイルで、タブのラベル文字列は注視していない)。1階層のグループ化しかできないPanoramaだけでは不十分なのは、「新しいタブを開いて分かれ道になったノードがどれだったか」という情報が欠落してしまうからだ。

そのくらい憶えておけよって言われるだろうけど、僕はほんとに頭が悪いというか物覚えが悪いというかバカなので、「そのくらい」がもう悲しいくらい絶望的なほどに覚えられない。覚えられないから、そのまま視覚化して置いておく。僕にとってタブのツリーは、脳に収まりきらない情報を置いておく外部記憶になっていると言える。メインメモリの中に情報が乗り切らなくて、画面の中にしか置いておけないのだから、多少アクセス速度が遅いとしても、そこに置いてある情報を毎回取りに行く。そういう使い方をしていると思う。

でも、「そのくらい」を覚えるのが苦にならない人にとっては、オーバーヘッドが大きすぎてまどろっこしいのかもしれない。タブ同士の関係はこぼれることなくメインメモリの上に載りきっているから、わざわざ画面の中にまで同じ情報を残しておく必要性が無い。そういうことなんじゃあないだろうか、と思う。

極端なことを言えば、ツリー型タブというのは脳味噌の性能が極めて低い障碍者がフツーの人に後れを取らないように、フツーの人に合わせて作られた社会の中で日常生活を送るために不足分を補うために使う、義肢とか車椅子とかそういう物に相当するのかもしれない。電動車椅子に乗れば確かに誰でもラクに移動できるけど、自分の足で立って歩ける人は、車椅子を降りて歩いた方が自由にもっといろんな所に行ける。また、電動車椅子に慣れきってしまうと、脚がひなびて自力で立てなくなってしまうかもしれない。使わなくても生きていけるなら、使わない方が健康でいられるのかもしれない。ツリー型タブという物については。

ツリー型タブがあると遅くなるとかツリー型タブが重いとかその辺の話について - Sep 17, 2012

ツリー型タブが重いという声をたまに見かけるんだけど、そういうのを見かける度に「ほんとかなあ?」とついつい思ってしまう。

もちろん、素のFirefoxに対して余計な処理を加えるわけだから、そりゃあ、元の状態より重くはなると思う。でも、「重い」って言ってる人の言ってる「重い」は、なんというか、そういうレベルのことを言ってるわけではないって気がする。もっと鈍重な、全体的に動作がヤバイくらい緩慢になる、そういうのを指してる気がしてる。

で、そういう現象が起こるとして、本当にそれがツリー型タブのせいなのかどうか。ツリー型タブを使っている時と使っていない時とで、同じ数のタブを開いていて、同じ時間だけブラウジングした状態で、ツリー型タブがある場合だけ顕著に性能が劣化するのかどうか。というのが、この件で自分が一番気になっているポイントなのです。

何故僕はこうまで頑なに「ツリー型タブが原因で遅くなっているのだ」と素直に認めようとしないのかというと、基本的にツリー型タブはブラウザのコンテンツ領域や履歴にはタッチしないように設計しているつもりなので、「コンテンツ領域に起因するメモリリークがある」とか「履歴を消去したら軽くなった」とかの報告があるという事自体が、どうにも不可解なのです。

実際、about:memoryではタブ毎にメモリの使用状況を確認できますが、「about:memoryを見ると、メモリが解放されていないことが分かる」と言われた再現条件をこちらで試行しても問題が再現せず、その後報告者の環境でもツリー型タブだけをインストールした状況では問題が再現しなくて、どうやら他のアドオンがこの問題の原因になっているのではないかという話になったこともありました。

ツリー型タブがあるとタブを開く数が増える&1つ1つのタブの生存期間が長くなる傾向があると思うので、他のアドオンやFirefox自身が潜在的に抱えてはいるものの普段の利用では無視できる程度だったという問題があったとしたら、それらがより顕在化しやすい状態になるとは思います。例えばあるアドオンを入れていると全体の動作が0.1秒遅くなるとして、5つくらいタブを開いている状況ではそれが気にならなかった。でも、ツリー型タブを入れてタブを50とか開きっぱなしにしていると、些細な重さでもそのまま×50されるから、シャレにならない重さになる。こういう事は十分にあり得ます。でもツリー型タブ単体でそういう事が起こるとはどうしても自分には考えにくいのです。

「コンテンツ領域への参照を残しているせいでメモリリークが発生する」というのは自分もアドオン開発初期にはよくやらかしていたミスなので、かなり後の方になって開発したツリー型タブの頃にはそこらへんのことは想定に入れられるようになっていて、多少速度を犠牲にしてでも安全になるように、だいぶ気をつけて設計したという認識があります。「そこまでやらんでもええんちゃうん」って所まで偏執的にやってることすらあります。Firefox本体のコードでもaddEventListenerした物をremoveEventListenerせずに放りっぱなし(多分ガーベジコレクション任せ?)になってるのとか見ると、怖いなーって思ってしまうくらいです。

その一方で、僕は「これこれこのアドオンと衝突してるんだけど」という報告を受ける度にそのアドオンのソースを見るという事がこれまで結構あったのですが、ソース見てげんなりすることが割とありました。これメモリリークするやろ、とか。で、そういう問題を解決しようと思ったら、根本的なアーキテクチャの変更が必要だったりして。そういうとこまで見だすときりが無いし、大体それは僕の領分でもないので、ツリー型タブとの衝突の原因になってるとこだけ見てあとは見て見ぬふりしてるんですけども。

そういう惨状を見てるから、僕の心情としてはついつい、まず最初に他の原因の方を疑ってしまいます。それらの可能性がなくてちゃんと明らかに「ツリー型タブが原因だ」と断言できる、という所にまで絞り込まれてないと(はい。ほんとにツリー型タブが原因で問題が起こってる可能性も、もちろんあるとは思ってます。そこまで完全否定はできないです。)、調べようという気になかなかなれないです。これって、思い上がりすぎですかね?

あと、それとは全然別の話として、ツリー構造が何らかの理由で壊れてしまうせいで、無限ループが発生してフリーズしてしまう……という事は時々起こるみたいなので、そういうのはもう完全にツリー型タブの責任なので今後も粛々と直していきたいとは思ってます。

いずれの場合にしても、メンテナンスに十分に時間を割けない現状では「ツリー型タブのせいで問題が起こってる、かもしれない」という段階では動けなくて、「間違いなくツリー型タブのせいで問題が起こってて、この手順で100%再現できる」という所まで絞り込んでもらってることが、こっちで対処できるためには絶対に欠かせない条件という感じです。できれば「この部分が問題になっている」という所まで明らかにしてもらえてると嬉しいし、もっと言えば修正パッチをpull requestでもらえるのがベストなんですが。

それから、何と言おうとツリー型タブを入れてFirefoxの動作がおかしくなり、ツリー型タブを削除したことでこれらが解決されたことは事実なのです。 という風な話はもう何度あったか覚えてられないくらいにあって、GitHubのIssue Trackerを探してもゴロゴロでてくるんだけど、詳しく聞いてみると大概は他のアドオンとの衝突です(全部がそうだというわけではないですが、感覚的には、そうである場合の方が多かったという印象です)。こういうのも、じゃあどのアドオンと衝突してるのかという所まで明らかにさえしてもらえれば、何か手の打ちようが出てきくる可能性がありますので、より快適な生活を望む場合には、衝突の解消に協力してもらえたらなーって思います。

Mozilla勉強会@東京 7th 発表資料を公開しました - Aug 19, 2012

Mozilla勉強会@東京 7thでの発表資料やサンプル実装を公開しました

発表時は準備不足のため時間の配分に失敗し、満足な説明を行えませんでした。すみません。発表時に喋ろうと思っていた内容のメモも併せて公開していますので、そちらもご覧頂ければと思います。

続きを表示する ...

セッションの復元とツリー構造の維持 - Aug 07, 2012

ツリー型タブを3ヶ月ぶりに更新した。

地味な修正が多い中で特に地味な修正だけど、セッション復元でツリー構造が壊れるという問題について一定の改善を見られたのではないかと思ってる。実際に効果があったのかどうかは、今後同様のバグ報告が減るかどうかで見るしかない。

何故ツリーが壊れるか、という事の前にそもそもツリーが壊れるってどういう事やねん、という話なんだけど、ツリー型タブを使っててごく希に、「リンクから新しい子タブを開いても子タブにならない」とか「親にあたるタブには折り畳みのためのつまみが表示されているのに、子になったはずのタブはインデントされておらず、つまみをクリックしても子になったはずのタブが折り畳まれない」とかそういう怪しい挙動になる事があった。

何故そういう事が起こるのか。これはタブの管理方法に理由がある。

ツリー型タブでは、個々のタブに一意なIDをあらかじめふっておいて、tab.setAttribute(TreeStyleTabService.KPARENT, parentTab.getAttribute(TreeStyleTabService.kID)) とか tab.setAttribute(TreeStyleTabService.kCHILDREN, childTabs.map(function(aChild) { return aChild.getAttribute(TreeStyleTabService.kID); }).join('|')) とかいう感じにIDの文字列をキーにして互いの関係を保持している。親のタブや子のタブを取得する時は、TreeStyleTabService.getParentTab(tab) とした時にその中で tab.getAttribute(TreeStyleTabService.KPARENT) して取得したIDを使って(DOM3 XPathなどで)実際の要素を改めて取得するという風になってる。何故こうしたかというと、

  • タブの要素ノードに直接 tab.parentTab = parentTab のようにしてしまうと、うっかり参照を消し忘れたらいつまで経ってもメモリが解放されないんじゃないか、という心配があった。(意図しない事態の発生を防ぐため)
  • 実際には setAttribute() するのと同じタイミングで SessionStore.setTabValue(tab, TreeStyleTabService.kPARENT, parentTabId) のようにしてセッションに情報を複製しており、クラッシュ時のツリーの復元などに役立てている。(ツリー構造の永続的な保存のため)

といった理由があってのことだった。

これはそれなりに堅牢なやり方なはずだと思ってたんだけど、実際にできあがってからJavaScriptデバッガでプロファイリングしてみたら、IDの文字列からタブの要素ノードを探すという処理が呼ばれる回数が突出して多くて、それが結構無駄な待ち時間を増やす元になっているらしかった。それで、もう少し高速に動作するような仕組みを後から加える事にした。setAttribute(TreeStyleTabService.kID, id) するのと同じタイミングで、gBrowser.treeStyleTab.tabsHash[id] = tab; としてハッシュテーブルを作っておき、それ以後はそちらだけを参照するという、ごくシンプルな物だ。

上記のおかしな挙動は、このハッシュテーブルが原因だっていた。このハッシュテーブルの仕組みが上手く働くためには、「ハッシュテーブルに入っていないタブはない(すべてのタブがハッシュテーブルで管理されている)」という前提が必要になる。もし万が一何らかの理由でハッシュテーブルの管理から漏れてしまうと、そのタブはAPI経由では永久に見つけられないことになってしまう。そのタブを親として子タブを登録する、という風な事はできるのに、その子タブに保存された情報から親のタブを探そうとしても、ハッシュテーブルに無いタブだから見つけられない。こういう不整合の発生は考慮に入っていなかったので、親子関係に基づくインデント幅の調整などの処理が中途半端な所で止まってしまって、上記のような色々と不可思議な現象が発生していた。

色々調べた結果、大量のタブが一気にセッション復元された時に「現在のタブ」になっていたタブが、このハッシュテーブルの管理から漏れてしまうことがあるようだという事が分かった。なので、そのような場合もちゃんとハッシュテーブルに入るようにした。これによって、手元で再現性100%だったケースについては問題が起こらないようになった。これまでに報告があった全ての問題がこれと同一の原因かどうかは不明だけれども、今までよりは問題が起こりにくくなったという事は言えると思う。

あと似たような問題で、タブの復元のタイミングによっては「ツリー構造だけの高速な復元」ができなくなるという現象も起こっていた。ツリー型タブが完全に初期化されるよりも前にFirefoxのセッション復元の仕組みによってタブが復元されていた、という想定外の状況が発生していて、それで色々な前提が崩れてしまっていた。理屈から言ってなんでそうなるのかがてんで分からなくて、どうにかしてタブが復元されるよりも前のタイミングに割り込みたかったんだけど、どうも無理っぽかったので、既にタブが復元されてしまっていた場合でもちゃんとそれらのタブを「後から初期化」するようにした。

そういう地味な修正ばかりが入っている版です。

Unityのランチャーに沢山Firefoxの項目を追加する代わりにサブコマンド?にした - Jul 29, 2012

複数のプロファイル、複数のバージョンを検証用に用意しないといけなくて、簡単に使い分けられるような環境を作る必要があるのです。

  1. Unityのランチャー上にある「Firefox ウェブ・ブラウザ」を右クリックして、「ランチャーに常に表示」のチェックを外す。
  2. /usr/share/applications/firefox-custom.desktopとして以下の内容でファイルを作成する。

    [Desktop Entry]
    Version=1.0
    Name=Firefox Web Browser (custom)
    Name[ja]=Firefox ウェブ・ブラウザ (custom)
    Comment[ja]=ウェブを閲覧します
    GenericName=Web Browser
    GenericName[ja]=ウェブ・ブラウザ
    Exec=firefox %u
    Terminal=false
    X-MultipleArgs=false
    Type=Application
    Icon=firefox
    Categories=GNOME;GTK;Network;WebBrowser;
    MimeType=text/html;text/xml;application/xhtml+xml;application/xml;application/rss+xml;application/rdf+xml;image/gif;image/jpeg;image/png;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/ftp;x-scheme-handler/chrome;video/webm;application/x-xpinstall;
    StartupWMClass=Firefox
    StartupNotify=true
    X-Ayatana-Desktop-Shortcuts=NewWindow;Sub;ESR;Stable;Beta;Nightly;
    
    
    [NewWindow Shortcut Group]
    Name=Open a New Window
    Name[ja]=新しいウィンドウを開く
    Exec=firefox -new-window
    TargetEnvironment=Unity
    
    
    [Sub Shortcut Group]
    Name=Sub
    Exec=firefox -no-remote -p sub
    TargetEnvironment=Unity
    
    
    [ESR Shortcut Group]
    Name=ESR
    Exec=/home/piro/opt/firefox-esr/firefox -no-remote -p esr
    TargetEnvironment=Unity
    
    
    [Stable Shortcut Group]
    Name=Stable
    Exec=/home/piro/opt/firefox-stable/firefox -no-remote -p stable
    TargetEnvironment=Unity
    
    
    [Beta Shortcut Group]
    Name=Beta
    Exec=/home/piro/opt/firefox-beta/firefox -no-remote -p beta
    TargetEnvironment=Unity
    
    
    [Nightly Shortcut Group]
    Name=Nightly
    Exec=/home/piro/opt/firefox-trunk/firefox -no-remote -p trunk
    TargetEnvironment=Unity
    
  3. ファイルに実行権限を設定する。
  4. Dashホームから「Firefox ウェブ・ブラウザ(custom)」を探して、実行する。
  5. ランチャーに表示されたアイコンを右クリックして「ランチャーに常に表示」にチェックを入れる。

これで、アイコンを右クリックして各プロファイルで起動できるようになった。

ユーザのホーム以下にファイルを作成して複数のアイコンを登録してみたりとか色々試みてたんだけど、自分で付けた名前と表示名が一致しないとか色々動作が怪しくて、結局このような所(グローバルな設定ファイルとして作成)に落ち着いた。

2014年8月21日追記。Ubuntu 14.04LTSで、Thunderbird用の物も作った。

[Desktop Entry]
Encoding=UTF-8
Name=Thunderbird Mail (custom)
Name[ja]=Thunderbird電子メールクライアント (custom)
Comment=Send and receive mail with Thunderbird
Comment[ja]=メールの読み書き
GenericName=Mail Client
GenericName[ja]=電子メールクライアント
Keywords=Email;E-mail;Newsgroup;Feed;RSS
Keywords[ja]=Eメール;イーメール;mail;e-mail;email;メール;電子メール;ニュースグループ;ネットニュース;RSS;フ>ィードリーダー;書く;読む;Mozilla
Exec=thunderbird %u
Terminal=false
X-MultipleArgs=false
Type=Application
Icon=thunderbird
Categories=Application;Network;Email;
MimeType=x-scheme-handler/mailto;application/x-xpinstall;
StartupNotify=true
Actions=Compose;Contacts;AnotherProfile

[Desktop Action Compose]
Name=Compose New Message
Name[ja]=新しいメッセージの作成
Exec=thunderbird -compose
OnlyShowIn=Messaging Menu;Unity;

[Desktop Action Contacts]
Name=Contacts
Name[ja]=連絡先
Exec=thunderbird -addressbook
OnlyShowIn=Messaging Menu;Unity;

[Desktop Action AnotherProfile]
Name=Start with Another Profile
Exec=thunderbird -no-remote -p sub
OnlyShowIn=Messaging Menu;Unity;

Firefoxの物を作った時と微妙に様式が変わっているようだ。

The priority policy of my addon projects - May 10, 2012

In recent days, I discussed on the issue about scrolling of the vertical tab bar. It made the priority policy about my addon projects clear, again. So, let's sum up them.

  • I assign the highest priority to issues I actually annoyed. Most projects were started because I wanted to use such addons. This is the main motivation to continue developing for me. In other words, when I suffer from a bug in my daily use, I'll fix it prior to other bugs, even if the mine is just trivial and the yours is very serious. After that, if I have time good enough, I'll research and fix your issues. If I've never used the feature (and I never use it in the future), then there is extremely low feasibility rating.
  • I'm negative about adding new features (especially digressive features) to existing addons. Because I currently have no plan to switch to other browsers (Google Chrome, etc.), I want to keep codes easy to follow up to future releases of Firefox. Even if the new feature looks simple, it can cost much time for maintenance in the future chronically. Long term sustainability is prior to short term convenience, for my projects. I'll reject requests which can lose maintainability or independency of the project, even if it is very useful feature, fixing serious bug, and so on.
  • I think that any addon shouldn't break features of Firefox itself without any apparent reason. For example, because the tab bar is automatically hidden in the full-screen mode (F11 key) by default, the vertical tab bar also should be done. If my addon breaks Firefox's default behavior unexpectedly, then I'll make effort on fixing it. However, if the issue is not introduced by my addon, then I often step back from it.

Because currently I have less time to develop addons, I have to apply these policies strictly. Actually, in recent days, I spend just a few days of my time per a month for my private development. Sadly I have to keep many requests pending.

However, codes of my addons are licensed under open source licenses. For example, Tree Style Tab is licensed under GPL/LGPL/MPL. You can fork, develop, or re-distribute any project based on my codes, by your hand. Your version can be better alternative for people who suffered from the issue ignored by me. (And, if it is enough reasonable, I possibly merge your changes to my repository.)

setTabState()を使わないでタブのサスペンドと復元をやる実装のサンプル - Apr 25, 2012

ツリー型タブに「バックグラウンドにあるタブをアンロードする機能を付けてくれ」って要望が来てて、そんなもんDormancyやらBarTabやらSaveMemoryやらUnloadTabやらなんぼでもあるがな、と思ったんだけど調べてみたらツリー型タブと併用できなかったり(Dormancy, UnloadTab)併用できても今のFirefoxで動かなくなってて開発止まってたり(BarTab)併用できないわ開発止まってるわだったり(SaveMemory)と軒並み全滅だった。

そもそもこの手の奴がなんでツリー型タブと併用できないのかっていうと、

  • ツリー型タブは、ツリー構造の情報をタブのセッション情報の一部として保存している。
  • タブをアンロードする系のアドオンは、セッションストアAPIを使ってgetTabState()でタブの現在のセッション情報を吸い出した後、タブを閉じて、新しい空のタブを開いて、そのタブが選択された時にさっき吸い出したセッション情報をsetTabState()で適用する、という事をしている。

ということで、アンロードする系のアドオンが元のタブを閉じてしまうからツリー構造が破壊されてしまうわけです。BarTabは互換性のためにツリー型タブのAPIを呼んでツリー構造をわざわざ復元するようにしてくれてたから併用できてただけで、本質的にはやっぱり相性が悪い。

っていうかべつに元のタブを閉じる必要なんてなくね? nsISHistoryのPurgeHistory()でセッションヒストリを空にするだけやったらあかんの? あと状態を復元する時もタブの属性とかまで含めて全部上書きせんでもいくない? と思って、「アンロードする時はセッションヒストリを消すだけ」「復元する時はセッションヒストリを復元するだけ」という実装のサンプルを作ってみた。

めんどくさかったから、nsSessionStore.jsをそのまま読み込んで流用するという乱暴なことをやってる。

これに「何分以上放置したら勝手にアンロード」「タブが選択されたら復元」みたいな事を加えればDormancyとかBarTabとかの代替になるんだろうけど、これ以上管理対象のアドオンを増やすのもイヤだし、かといってDormancy等にそのまま取り込んでもらえるとも思えないし(やり方があまりにダーティすぎて、開発の継続性を低下させてしまうだろうし、そもそも現状で困ってるのなんてツリー型タブのユーザくらいだろうし……)、どうしたもんでしょうかね。

……とかいってたんだけど結局真面目に作り始めてしまった。30分以上放置してたら勝手にアンロードするとかその程度の事はとりあえずできるようになってはいる。

Fox Splitterを更新した - Apr 23, 2012

バージョン0.6系(旧版)2.x系(現行バージョン)の両方とも更新した。

バージョン0.6系の方は、前に書いた2.x系への自動アップデート機能の追加だけで、他は何もいじってない。AMOからインストールした人が2.x系に自動アップデートされないという問題への対処のためだけに作ったバージョンなので、AMOにしかアップロードしてない。基本的にこっちはもう終了したバージョンということでよろしく。

2.x系の方は、最近になってUbuntu上でヘビーに常用し始めるようになって色々気付いた問題を修正したのと、僕の使い方で「ああこういう機能欲しいやばい(ないとめんどい)」と思った機能を追加した。

修正の方で特に大きいのは、「グループ化されたウィンドウの1つを移動した後に他のウィンドウをそれに併せて移動する」という部分の改善だと思う。

GNOME2のワークスペース切り替えの時はそこまで酷いことになってなかったんだけど、Ubuntu 11.10にアップデートして3×3のワークスペースを使うようにし始めた(これ多分GNOME3の機能ですよね?)ところ、全く使い物にならないレベルでグループの配置が壊れるようになってしまった。GNOMEのワークスペース切り替えは、少なくともFirefoxの中から見た時には、各ウィンドウの座標を固定してビューポートの方だけを移動させるんじゃなくて、ビューポートの座標の方を固定して全部のウィンドウの座標をぐりぐり動かしているように検知されている。なので、ワークスペース切り替えの時に全部のウィンドウのscreenX/screenYの値が変わってしまう。それで、それぞれのウィンドウがてんでバラバラに「グループの中のウィンドウが1個動かされたから他のウィンドウも再配置しよう」という事を始めてしまってシッチャカメッチャカになっていた。

「グループ化されたウィンドウ全部が相対座標を保ったまま一度に移動されたので、再配置の必要なし」とかの判断をちゃんとするようにすればよかったんだけど、僕の頭で思いつくやり方だとどーにもうまくいかんかったので、諦めて安全確実だけどちょっと重い方法でウィンドウの移動→グループ全体の再配置を行うようにした。普通にウィンドウをドラッグで移動した時とかのレスポンスは悪くなったんじゃないかと思うけど、背に腹は代えられない。

新機能は、「グループ化されたウィンドウの1つでPanoramaを使った時に、グループ全体の大きさまでそのウィンドウを一時的に広げる」という機能が元々あったのを、Panoramaの時以外にも使えるようにした(ついでに、詰めが甘かった所を色々直した)という物。何でこういう物が欲しかったかというと、

  1. 僕は今Ubuntuのワークスペースの1つをFox Splitterで3分割したFirefoxのウィンドウ専用に割り当てている。
  2. 分割後のそれぞれのウィンドウには、別々のプロジェクトのRedmineのチケット一覧を表示している。
  3. それぞれのプロジェクトでチケットを追加する時は、その分割されたウィンドウの中でやっている。
  4. Redmineはある程度の大きさのウィンドウで表示される前提でレイアウトが組まれているので、狭いウィンドウの中では非常に使いにくい。
  5. なので、チケットを書いたり詳細を読んだりするときだけそのウィンドウを大きくして、終わったら元の大きさに戻す(そうしないと他のウィンドウのRedmineが見えないから)という事をよくやっていた。

という使い方をしていて、いいかげんいちいち自分でリサイズするのが面倒になったから。

今のFox Splitterは、全体を管理する大きなウィンドウのような実体が無い状態で各ウィンドウをあたかも1つのグループの下に属しているかのように協調して動作させる、ということをやっていて、作ってる側の自分としては、頭を抱える部分も多いけれども「元々Firefoxにある物だけを駆使してどこまでできるか?」ということを追及するある種の挑戦というかパズルに挑んでいるような感覚もあって、そういうことを効率よくやるためには結局2分木でやるのが安全確実なんだなあとか色々と気付かされた所があって、ノウハウ的にも自分のやってきたことの集大成になってるんだけど、エンドユーザとしての使い勝手は多分旧版のFox Splitterに比べると劣化してると受け取られることの方が多そうで、報われないことに一生懸命になってるなー感が自分である。

とはいっても、自分で使う分にはとりあえずこれだけ動いてくれれば十分だし、旧版の設計で今後もずっと自分でメンテナンスし続けるのは絶対に無理だし、もっといいやり方が見つかるまでは、この路線を変えることは多分無いと思う。

Page 6/243: « 2 3 4 5 6 7 8 9 10 »

Powered by blosxom 2.0 + starter kit
Home

カテゴリ一覧

過去の記事

1999.2~2005.8

最近のつぶやき