株式会社クリアコード > ククログ

ククログ


GNU/Linux版Firefoxでime-modeを動作させるアドオン

ime-modeとは

CSSにはime-modeというプロパティがあり、これによりinput要素やtext要素でインプットメソッドの入力モードを制御することができます。この仕様は廃止されることが決まっているため積極的に利用すべきものではありませんが、一方で代替となる仕様や実装が無いこともまた事実です。日本の業務システムでは、入力業務効率化の必要性から、今なおこの機能を利用している事例も多いようです。

Firefoxでのime-modeサポート状況

この仕様はInternet Explorer由来のものですが、Firefoxも同仕様をサポートしています。ただし、GNU/Linux版ではサポートされている機能が限定的で、activeinactiveはサポートされていません。これは、GNU/Linux版Firefoxがプラットフォーム層として利用しているGTK側の制約に依るものです。

弊社でも「GNU/Linux版Firefoxでime-modeを使用することはできないか?」というお問い合わせを受けたことが過去に何度かあります。

ime-mode-we

最近、とあるお客様から「どうしてもこの機能をGNU/Linux版Firefoxで実現したい」という強いご要望があったため、新たにime-mode-weというアドオンを作成してお応えしました。

同アドオンは、今のところ多言語入力フレームワークとしてIBusを、入力エンジンとしてAnthyKKCMozcSKKのみをサポートしています。

インストール方法

アドオン自体は、通常のアドオンと同様に、同ページから「Firefoxへ追加」ボタンを押すだけでインストールすることができます。ただし、このアドオンはシステムネイティブのコマンドを呼び出す形で実装されているため、このネイティブコマンドについても別途インストールする必要があります。同ページにも記載していますが、以下の手順でネイティブコマンドをインストールして下さい

  1. "ime-mode-we-host.tar.gz"を https://github.com/clear-code/ime-mode-we/releases からダウンロードします。
  2. 以下のコマンドで、同アーカイブ内のファイルをシステムに展開します:
sudo tar xvf ime-mode-we-host.tar.gz -C /

アドオンの方をネイティブコマンドよりも先にインストールしてしまっている場合は、Firefoxを再起動して有効化して下さい。

なお、同コマンドの実行にはPythonが必要です。

利用方法

問題なくインストールが完了していれば、ime-mode: inactiveがセットされた入力エリアにフォーカスを合わせると自動的に入力モードが直接入力モードになり、ime-mode: activeがセットされた入力エリアにフォーカスを合わせると自動的に入力モードがひらがな入力モードに切り替わるようになります。後者については、実装上の都合で、直前の入力モードではなくひらがなモードに固定化されています(直前の入力モードに変更するためには、各IBusエンジン側の対応が必要です)。

ただし前述の通り対応している入力システムは限られますし、IBus側の設定をデフォルト状態から変更している場合には、対応済みのIBusエンジンでも正しく動作しないこともあるかもしれません。

まとめ

GNU/Linux版Firefoxでime-modeを動作させるアドオンime-mode-weを紹介しました。現在はIBusの特定のIMエンジンのみ対応していますが、Fcitxなどにも対応させることは難しくないでしょう。実際、上記のお客様向けには、非公開のプロプライエタリIMエンジンに対応させる形で提供していますし、IMエンジン側に手を入れることでひらがな固定といった制約も排除しています。また、ime-modeの仕様に拘らなければ、IMEのON/OFFだけでなく、初期状態をカタカナや半角カタカナなどに切り替えるといった機能も実現できるかと思います。

もしこういったカスタマイズのご要望がある場合は、ぜひ弊社お問い合わせフォームよりご連絡下さい。

2019-06-24

OSSへのフィードバックには何をどう書けばいいのか、どのプロジェクトに報告すればいいのか

※注:この記事の対象読者は、「OSSを使用していてトラブルに遭遇しているか、改善の提案があり、その情報を開発元のイシュートラッカーに伝えようとしているが、どんな内容を書けばよいかわからない」という人です。そもそも「どこに報告すればよいかわからない」「イシュートラッカーに書き込んでいいかどうか不安がある」という方は、1つ前の記事をご参照下さい。

結城です。

1つ前の記事では、フォーラムとイシュートラッカーのどちらに書き込めばよいかわからない場合の考え方について詳しく語りました。その際、両者の目的の違いを以下のようにまとめました。

  • フォーラム:個人の問題の解決を図る場所。
  • イシュートラッカー:ソフトウェアの問題の解決、品質の向上を図る場所。

この記事ではその続編として、イシュートラッカーの目的から導かれる「適切な、書くべき報告の内容」を語ってみます。

書くべき内容、避けるべき内容

フォーラムとイシュートラッカーの目的の違いは、「投稿する内容をどうまとめればよいのか」という事を考える時の指針にもなります。「ソフトウェアの問題の解決」を目的とするイシュートラッカーに報告する内容は、その目的に沿って、「ソフトウェアの開発に関わる人達が、このソフトウェアの問題を解決するには、どのような情報が必要か?」という視点で整理する事になります。

例えば、以下の3項目は典型的な「書くべき内容」です。

  • Steps to reproduce:現象の発生条件、再現に必要な手順
    • 他の人は、どうすればその現象が起こるかを知らないし、あなたが普段どのようにその機能を使っているのかも知らないので、あなたが詳しく説明するしかありません。
    • メニューの選び方、機能の呼び出し順序など、あなたが無意識でしている操作の仕方がその現象を引き起こす決定的なきっかけとなっているかもしれません。
  • Actual result:実際に起こる結果
    • 他の人は、自分の環境で得られた結果が、あなたの見ている物と同じかどうか分からないので、あなたが詳しく説明するしかありません。
  • Expected result:本来期待される結果
    • 他の人は、あなたほどにはその機能や使い方の事を詳しく知らないかもしれないので、あなたが詳しく説明した方が効率がよいです。

その問題のせいで自分がいかに困っているか? という訴えは報告のメインの内容とはなり得ません。プロジェクト全体の中でその問題の重要度の高さを認識してもらうための材料として、問題の影響度・深刻さの説明としてそれらの訴えを盛り込む場合はありますが、そのような訴え自体が問題の原因究明や修正箇所の特定に役立つという事は、基本的には無いからです。

重複する報告も、場違いな報告も、無駄じゃないのでしていい

しかし、いざ報告しようとしても、

  • 既に他の人が報告しているにもかかわらず、自分の探し方が下手なだけで、既存の報告を見つけられていないだけかもしれない。
  • この問題をこのプロジェクトに報告するのが適切かどうか分からない。もっと適したプロジェクトがあるかもしれない。場違いな報告をしたら迷惑かもしれない。

という心配から報告をためらう、という事例はワークショップの中でもよくあります。筆者はそのような場合には、「不安を感じるくらいなら、自分が思う内容・表現のままでそのプロジェクトに報告してしまってよい」とアドバイスする事が多いです。

既知の報告がないか全く調べていない、というのはさすがにまずいですが、自分の思いつく表現で検索して報告が見つからないなら、その表現で報告して大抵は問題ありません。なぜなら、仮にその後既知の他の報告と重複していると分かって、そちらに誘導された上でクローズされたとしても、その報告内容自体が、同じ探し方をする人のための今後の誘導になるからです。

適切な報告先プロジェクトかどうか分からない、という場合も同様です。その問題が根本的には別のソフトウェアの問題だったと後から分かっても、同様の現象に遭遇した人のための誘導になるので、報告は無駄にはなりません。例えば1つ前の記事で紹介した事例では、今後同様の現象に見舞われて検索して辿り着いた人は、今後はMicrosoft IMEの動向に注意するとよい、という情報を得られる事になります。

また、根本的な原因については既に報告があったとしても、その報告で触れられている以外の現象の情報があると、それは問題の影響度合いを示す情報になります。当初の報告では些細な問題だから後回しにしようと判断されていた物が、実はユーザーレベルで大きな影響を受ける物だったと分かって、対応の重要度・緊急度が引き上げられるという事は度々あります。

却下された報告や提案にも価値がある

明らかな不具合というよりは「こうなっているとより良い」という提案の性質が強い報告の場合に多いのですが、「それは対応しない」「やらない」と判断され却下される場合もあります。そのような場合も、「無駄だった」「失敗した」という考え方をする必要はありません。というのも、「ある提案が却下された」という事そのものにも意義があるからです。

OSSのプロジェクトのスコープ*1は、必ずしもすべて明確化されているとは限りません。ある人にとっては「当然あるべき」と思える機能が無かったり、逆に、「無駄にしか思えない」という機能があったり、特に、それについて何の説明も無いという事はざらにあります。大規模なプロジェクトでは、すでに参加している人の間ですら解釈が別れていたりもします。

しかし、例えスコープが明文化されていなくても、一貫性を持った数々の判断の結果、その輪郭は段々と浮かび上がってきます。新たな判断の事例が1つ増えるという事は、不明瞭だった基準がより明確化されたという事になり、後から来た人にとって有用な情報となります。また、判断と共にその根拠が示されていれば、後になって状況が変わり根拠が薄れた時などに、それを理由として再考を求める事もできます。

そのような意義ある判断を引き出すためにも、議論は「自分の意見を押し通すため」ではなく「プロジェクトの目的をより良く達成するため」という点を念頭に置いて、「相手(敵) v.s. 自分」ではなく「問題 v.s. プロジェクト」という方向で行いましょう。反論も、主張の材料を補ったり、説明しきれていなかった理由を追加で説明したりと、意味のある反論をする事が肝要です。感情的に同じフレーズを繰り返すだけや、明確な根拠無しに強弁するだけのような不誠実な態度、他の参加者の共感を得られないゴリ押しは、百害あって一利無しです。

ただし、実際に手を動かす人が足りないから労力的に対応できないという理由で却下される場合は、より深く関わるチャンスです。自分で実装してパッチ(プルリクエスト)を提出し、その後もメンテナンスを引き受け続ければ、あなたも立派に開発者の仲間入りという訳です。

「対案」無しでも報告・提案していい

  • 報告だけでは無責任なのではないか? 対案まで考えてから出さないと駄目なんじゃないか?
  • パッチ(プルリクエスト)まで書いて初めてOSS開発への参加と言えるんじゃないか?

といった点で悩んで報告をためらう、というケースもあります。筆者はこのような場合、「対案は無くてもいいから、まず報告してみよう」とアドバイスします。

もちろん、コードのレベルで修正の仕方まではっきり分かっているのなら、パッチやプルリクエストの形でフィードバックできるに越した事はないです。しかし、そうできないならフィードバックしない方がよい、という事はありません。

むしろ、自分の考えつく解決策が最良であるとは必ずしも限りません。詳しい事情が分かっていない段階で自分の思い込みだけで独りよがりに実装を作り込んでしまうと、その労力がまるっきり無駄になるという事もあり得ます。例えば、ある機能の不具合を見つけて、その問題を解消するための複雑な変更を持ち込んだが、単にその機能全体を既存の別ライブラリで置き換えれば済む事だった、というような事はよくあります。筆者も、過去に強引な実装のパッチを代理で投稿してもらった事がありましたが、実装の筋が悪すぎたためか、それ以上話が進む事はありませんでした。

イシュートラッカーは「問題の解決」に取り組むための場であって、「特定の解決策の実現」に取り組む場ではありません。解決策は関わる人みんなで考えてよく、一人で抱え込む必要はないのです。よく「文句を言うなら対案を示すべき」という言い方がされますが、だからといって「対案が無いなら文句を言ってはならない」は真ではありません。問題解決に取り組む1つのチームの一員として、対案を考える事も含めて皆で取り組む、という姿勢・考え方をするのがポイントです。

同じ問題に取り組む仲間と思って接しよう

真摯に議論しようという話とも共通しますが、「問題を解決する開発者達 v.s. 外部から善意で協力しているただのユーザーの自分」という壁を自ら作る事は避けた方がよいです。対等な関係ではないという事を過剰に意識してしまうと、実際以上に大きな上下関係を自分の中に作り上げてしまい、コミュニケーションの妨げになります。

1つ前の記事ではOSSに新たに関わる人を新入社員に例えましたが、そう考えれば、なぜ自分とプロジェクトの間に過度に壁を作るべきでないのかが分かるのではないでしょうか。新メンバーがお客様気分で上げ膳据え膳に期待してふんぞり返るのはおかしいですし、かといってへりくだりすぎるのもおかしいですよね。

無論、雇用されフルタイムで開発に従事している人と、外部から余力の範囲で関わっている人の間では、立場も権限も異なります。しかし、そのプロジェクトの目的、あるいは報告した1つの問題の解決に向けて一緒に取り組む仲間という点では差は無いはずですから、お互いに敬意を持って接する事が大事です。

特に、「自分は問題で損をした被害者なんだ。その被害者自身がわざわざ協力してやってるんだ。だからお膳立てはあいつらの方で整えてくれて然るべき」というような捉え方は、問題の解決を遠のけさせこそすれ、近づけさせは決してしません。このような意識を強く持ってしまうと、相手の無理解を責めたり相手をやり込めたりする事にばかり意識が向いてしまい、ひいては特定個人に対する罵倒や中傷にまでエスカレートし得ます*2

まとめ

以上、OSSにフィードバックする事に不慣れな場合に悩みがち・判断に迷いがちと思われる点について、イシュートラッカーの目的から導かれる「適切な考え方」を、筆者の感覚から語ってみました。

表現を変えながら繰り返し何度か述べていますが、OSS開発に関わる際は基本的に「問題 v.s. それに立ち向かうプロジェクト参加者達(そして、その一員としての新人の自分)」という構図で考え取り組む事が大事だというのが、筆者の考えです。かつては思ったように成果を出せなかった自分も、そのように考えられるようになって以降は、より有用な報告ができ、提出したパッチやプルリクエストを取り込んでもらえる機会も増えたように思います。まだOSSの開発への参加に不慣れで、報告がうまくいかなくて悩んでいるという方は、この記事で述べた点を意識してみてはいかがでしょうか?

また、この記事で述べた事は実際にはOSSに特有の話ではなく、一般的に企業内でバグ票をやり取りするような場合にも共通して言える事です。1つ前の記事ではOSSのイシュートラッカーを出入り自由の社屋や工場に例えましたが、OSS開発の場で広く共有されているノウハウの中には、普通に仕事の上で有用な物も多く含まれています。特に、OSS開発では「多くの人を雇って人海戦術で乗り切る」というような力業での解決が難しいため、いかに少ないコストで最大の効用を得るかという点で工夫がなされている事も多いです。当社のOSS開発支援サービスでは、OSS開発に参加していきたいという企業さまのお手伝いだけでなく、OSSコミュニティでの開発ノウハウを企業内での開発に活かすお手伝いも承っています。そういった事に関心をお持ちの担当者さまがいらっしゃいましたら、ぜひお問い合わせフォームよりご連絡下さい。

*1 プロジェクトとして取り組む事の範囲。

*2 お恥ずかしい話ですが、筆者も何度かこれで失敗しています。

2019-06-19

OSSへのフィードバックはユーザーフォーラムとイシュートラッカーのどちらに書くべきか?

※注:この記事の対象読者は、「OSSを使用していてトラブルに遭遇しているか、改善の提案があり、その情報を開発元に伝えたいが、どこで伝えればよいかわからない」という人です。「どういう体裁で報告すればよいか分からない」「何を報告すればよいか分からない」という人向けの話はまた日を改めて書くつもりです。

結城です。

OSS Gateワークショップで、初めてフィードバックをしようとしているビギナー参加者のサポートをしていると、ビギナー参加者から以下のような質問を受ける事があります。

  • こんな簡単な・くだらないレベルの事を報告してもいいんでしょうか?
  • ユーザーフォーラムとイシュートラッカー(バグトラッキングシステム)*1のどちらに報告すればいいんでしょうか?
  • どのプロジェクト(開発元)に報告すればいいんでしょうか?

これらの点に対する筆者の回答は、端的には以下のようになります。

  • 簡単でも些細でも何でも、あらゆる未解決の問題は報告していい。
  • これは自分個人の問題ではなく、そのソフトウェアの全ユーザー向けに解決されるべき問題だ、と思えるならイシュートラッカーに報告していい。
  • 自分で確かめられる範囲ではこのプロダクトの上でしか問題に遭遇しない、他のプロダクトでは問題が再現しない、と言えるならとりあえずその開発元に報告していい。

奇しくも最近、これらの事を考える上で非常に興味深い事例に出会いました。以下、それを題材として上記の点をどのように判断しているのかを語ってみます。

報告者からフォーラムへ、フォーラムからイシュートラッカーへ、MozillaからMicrosoftへ

当社の法人向けのThunderbirdサポートにおいて、数ヶ月前から複数のお客さまより「メール作成時に日本語入力で漢字への変換ができなくなる」という現象のお問い合わせを頂くという事がありました。

お問い合わせの中にはMozillaZineフォーラムに同様の事例の報告がある旨をお書き添え頂いた物もあり、参照してみると、スレッドの大まかな流れは以下のようになっていました。

  1. このような現象が起こっている、既知の解決策はどれも有効でなかった、という情報が投稿される。(2018年12月末)
  2. 現象の再現条件の特定が複数人によって進められる。(2018年12月末~1月頭)
  3. Thunderbirdに対して行われた変更のうち、どの変更以降からこの現象が起こるようになったのかが特定される。(2019年1月)
  4. 暫定的な回避作として、このアドオンをインストールすると現象が起こらなくなる、という情報が寄せられる。(2019年1月末~2月)

当社の検証環境でも、条件が整うと確かに現象が再現するという事を確認できました。そこで、Mozillaが製品開発のために運用しているイシュートラッカーであるBugzillaに何か情報が無いかと検索してみたのですが、思いつく限りのキーワードで検索しても該当するBugを見つけられませんでした。そのため、ひとまず筆者自身で新しいBugとして報告を行いました。これが2019年5月の事です。

もし既に報告されている問題であれば、そちらへの参照情報が追加され、この新しいBugは「重複する報告」としてクローズされます。しかし実際にはこれが最初の報告だったようで、最終的にはFirefoxとThunderbirdの共通基盤であるGeckoエンジンの日本語入力部分の担当であるMozillaの中野さんにまで連絡が行くという事にまでなりました*2

ただ、話はそれで終わりませんでした。仮にこれがGeckoの明白なミスに起因する現象だったのであれば、Gecko側で修正されてBugはクローズされます。しかし実際には、Mozillaの中野さんによる詳細な調査ではGecko側に問題は見当たらなかったため、Microsoft IME(MS-IME)側の不具合である可能性があるとして、中野さんからMicrosoftのフィードバック窓口へ2019年5月末に報告されたそうです。

以上が、この件の2019年5月末時点の状況です。

ずっと以前から現象が確認されていても全く進展が無かったのが、イシュートラッカーに報告された途端に事態が動き出す、という事は度々あります。上記の経緯のうち、フォーラムからBugzillaへの報告までの流れはまさにそのような推移に見えます。

しかし最終的な結果を見ると、Thunderbirdの問題でなくMicrosoft IMEの問題である可能性があるなら、MozillaのイシュートラッカーであるBugzillaではなくMicrosoftに報告するのが正しかったのではないか? という見方もできます。もし仮に本当にMicrosoft IMEの問題だったのであれば、現象の確認から適切な場へ情報が伝わるまでに半年もの時間がかかってしまったという事になります。

さて、この問題は一体どのように取り扱われるべきだったのでしょうか?

誰の問題か、何をしたいかという観点で持ち込み先は変わる

ここまでの経緯には多数の論点が入り交じっていますが、目的・動機という点では大まかに以下の2つにまとめられます。

  • 報告者にとっての「どうすれば自分が今困っているこの状況を脱せるのか?」という視点=個人の問題を解決したいという視点
  • 開発者にとっての「そのソフトウェアにどんな問題があるのか、解決されるべき問題はどんな内容なのか」という視点=ソフトウェアの問題を解決したいという視点

ソフトウェアの問題が解決された結果として個人の問題が解決されるという事も、個人の問題を解決するためにした事が結果的にソフトウェアの改善にもなるという事もあります。しかし、それらはあくまで偶然の結果そうなっただけで、最初から偶然に期待するというのは効率が悪くお薦めできません。

OSSでは、というよりもこれは人間社会で一般的に言える事ですが、報告・投稿を持ち込む先は目的によって決めるものです。問題がいつまでも解決されない場合、最大の原因は問題を持ち込んだ先が適切でなかったからだった、という事がよくあります。

フォーラムが解決できる問題

上記の「フォーラムに報告がなされた例」では、関わった人達の期待とその結果は以下のように言えます。

  • トラブルに遭遇した本人(フォーラムに最初に投稿した人)にとって:
    • 期待:
      • 自分が遭遇した問題の解決。
    • 結果:
      • フォーラムに集う人達に調べて貰えて、発生条件を絞り込めた。
      • その人固有の状況で有効な暫定的回避策が判明し、当座をしのげるようになった。
  • フォーラムに集う人達にとって:
    • 期待:
      • 相談者によって道混まれた問題の解決の手助け。
    • 結果:
      • 助けを求めてやってきた人の問題の解決に力を貸せた。
  • その後同様の問題に躓いた人達にとって:
    • 期待:
      • 問題にそもそも遭遇しない事
    • 結果:
      • ソフトウェアの問題は未解決のままだったので、同じ現象に後から遭遇してしまった
      • フォーラムの投稿者と状況が一致していれば、暫定的解決策で現象を回避できる。
      • フォーラムの投稿者と状況が一致しないと、暫定的解決策では現象を回避できない
  • ソフトウェアの開発・品質向上に関わる人達にとって:
    • 期待:
      • ソフトウェアの開発が進む事、品質が向上する事。
    • 結果:
      • 普段見ていない場所で話が進行していたので、問題の存在自体を把握できなかった
      • 問題が把握されなかったため、対応もなされなかった

このように、個々のトラブルの解決や、同様のトラブルに遭遇した人の解決のための情報源にはなるものの、同様のトラブルが繰り返される事そのものの解決にまではならないのが、ユーザーフォーラムの性質と言えます。

また、ユーザーフォーラムは「ユーザー同士の相互扶助の場所」であるという点にも注意が必要です。基本的にはボランティア運営なので、分かる人がいなければ反応を得られないという事もあります。そこに集まる人は全く無関係の人よりは詳しい人である可能性が高いですが、開発者ほどには詳しくない場合も多いです*3

イシュートラッカーが解決できる問題

では仮に、最初から問題をイシュートラッカーに投稿しようとしていたら何が起こったでしょうか。筆者の見立てでは、最悪の場合の結果は以下のようになったのではないかと考えています。

  • トラブルに遭遇した本人や、同様の問題に躓いた人達にとって:
    • 問題の原因調査の方法に詳しくないと、有用な情報を提供できない。
    • そもそも技術的な知識の有無以前に、英語が不得意だと、自分の置かれた状況(問題の発生条件)を上手く説明できない。
    • 原因が特定され、次のバージョンで修正されても、今使っているバージョンでの問題は解決しない
    • 調査の過程で暫定的な解決策がたまたま見つからなければ、今この瞬間のトラブルは解決しない
  • ソフトウェアの開発・品質向上に関わる人達にとって:
    • 調査方法を知らない・開発者レベルの知識が無い人に調査の仕方を説明するために、時間と手間を使う必要がある。
    • 有効な情報を得られないと、スッキリしないものを抱えたままになる。

一方、もし理想的に推移したとすると、以下のようになるでしょう。

  • トラブルに遭遇した本人や、同様の問題に躓いた人達にとって:
    • 原因が特定され、次のバージョンで修正される事になれば、そのバージョンのリリースを待てば問題は解決する。
    • しかし、今使っているバージョンでは問題は解決しない
    • 調査の過程で暫定的な解決策がたまたま見つからなければ、今この瞬間のトラブルは解決しない
  • ソフトウェアの開発・品質向上に関わる人達にとって:
    • 問題の原因を特定できれば、根本的解決や回避策を検討できる。
    • その成果を次のバージョンに盛り込めれば、同様のトラブルの発生を防げる。
    • 根本的な原因がそのソフトウェアに無いとしても、より適切な場所へ、開発者レベルでの詳細な調査結果を報告できる。

いずれの場合も、今この瞬間に起こっている問題の解決には必ずしもつながらないし、また、その優先順位も低い、という事には注意が必要です。

イシュートラッカーに関わる人には、ある程度の知識が要求されます。報告者に全く知識が無いという場合、問題自体の深刻度がよほど高いと判断されれば、開発者もなんとか原因究明に必要な情報を報告者から引き出そうとして手厚く手助けする事もあります。しかし、それだけの手間をかけるのは割に合わないとなれば、残念ながらそれ以上の手助けはなされません。

このような事になるのは、OSSプロジェクトのイシュートラッカーは「個人の問題を解決する場所」ではなく、あくまで「ソフトウェアの開発を進め品質を向上する事を目的とする場所」だからです。

ソフトウェアの改善・ソフトウェアの問題の解決に関わりたいならイシュートラッカーへ

例えて言えば、これは企業の社屋や工場に誰でも出入り自由になっている状況のようなものです。社会科見学では見学しかできないのに対し、見学ルートを外れて「中の人達」同士の議論に混ざって発言できる、参加の機会が開かれているのがOSSです。そう考えれば、そこで行われている議論や開発に誰でも気軽についていける訳ではなく、機会があるかどうかとその中で一人前の参加者として振る舞えるかどうかは別の話だ、という事も納得しやすいでしょう。

しかし、これは言い換えれば、開発に関わりたい・品質向上に寄与したいという積極的な意志を持つ人にとっての門戸が開かれているという事です。開発の拠点が海外でも、普段の仕事がソフトウェア開発と無関係でも*4、あるいはライバル製品の開発元に所属していてすらも*5、やる気次第で自分から参入できます。

  • 「ここの所に穴が開いてるみたいなんだけど、大丈夫かなあ……塞がれないのかなあ……? 気になる……」「この機能のこの部分、これだと不十分に見えるんだけどなあ……ソワソワする……」という風に、ソフトウェアのちょっとした不具合が気になって仕方ない人。
  • 「ああもう! この人達(開発者)はまるで分かってない! 俺の方がずっとスマートにこの問題を解決できるのに!」「ああもう! なんでここの所はこんな風にできてるんだ? 使いにくい! 俺ならこんな作り方にしないのに!」という風に、問題が放置されている事に我慢がならない人*6

こういったモヤモヤを感じた時に、「でも、手を出せないんだからしょうがない」と諦めないで根本的解決できる可能性が開かれているのがOSSなのです。

ですから、「OSS開発に関わってみたい、ソフトウェアの改善に関わりたい、その最初の一歩を踏み出してみたい」と思ってOSS Gateワークショップに参加を考えた人であれば、もうその時点で「イシュートラッカーに報告する」という選択をして全く問題ない訳です。

また、その際には最初から理想的に振る舞える必要はありません。OSSプロジェクトに初めて関わる人というのは会社の新入社員と同じですから、当然要領は良くありませんし、最初は簡単な仕事からしかこなせませんし、あるいはミスもします。分からない事があれば、会社の先輩に質問するのと同じように、先に参加していた人を捕まえて質問するだけの事です。

「フォーラムに書くべきか、イシュートラッカーに書くべきか」「こんな些細な問題を報告してもいいのか」という問いへの筆者の答えが「個人の問題でないならイシュートラッカーに書いてよい」「些細でも報告してよい」となるのは、以上のような理由からです。

まとめ

以上、OSS Gateワークショップの中で度々聞かれる「フォーラムとイシュートラッカーのどちらに書くべきか判断できない」という悩みについて、フォーラムとイシュートラッカーの目的を把握した上で、「ソフトウェアの問題を解決する場所」というイシュートラッカーの目的に沿った報告をすればよい、という事を語ってみました。

イシュートラッカーへの報告は、問題が解決されればより多くの人に恩恵がもたらされます*7。OSSのイシュートラッカーはソフトウェア開発の現場ですので、ソフトウェア開発の知識がある人はよりよい報告ができる可能性がありますし、パッチやプルリクエストを通じて直接的に問題解決を図る事もできます。一歩進んだ関わり方として、皆さんもイシュートラッカーへの報告にぜひ挑戦してみて下さい。

ところで、先の事例にはもう1つ気になる点が残っています。それは、Microsoft IMEの問題だった可能性があるのにThunderbirdの問題として報告してしまってよかったのか、自分では適切な判断ができない時は一体どこに報告すればよいのか、という点です。次の記事では、それも含めて「どんな報告の仕方をすればいいか、報告の内容はどう書けばよいか」について語ってみます。

なお、当社ではOSS開発を推進したい企業さまのお手伝いをするOSS開発支援サービスを行っています(直近のアカツキさまでの事例の紹介)。社内のITエンジニアのOSS開発者としての活躍を促進したいとお考えの担当者さまがいらっしゃいましたら、ぜひお問い合わせフォームよりご連絡下さい。

*1 「ソフトウェアの不具合」を「バグ」と呼ぶことから、バグの修正状況の追跡をする物として「バグトラッキングシステム(Bug Tracking System、BTS)」という呼び方が以前はよく使われていました。しかし、この呼び方だと「不具合ではないただの要望はバグとして取り扱うべきなのかどうなのか?」という点で混乱が生じる場合があります。Mozillaが運用するバグトラッキングシステム「Bugzilla」では、不具合報告だけでなく要望もすべてひっくるめて「バグ」と呼ぶという運用ルールにする事でこの混乱を回避していますが、運用ルールを知らない人にとっては違和感が残ります。そのため現在は一般的には、バグや要望などを「解決されるべき課題=issue」と捉えて「イシュートラッキングシステム(Issue Tracking System、ITS)」や「イシュートラッカー」と呼んだり、あるいは、どんな課題であってもシステム上で管理される時はひとつの「チケット」になるからという事で「チケットトラッキングシステム」と呼んだりします。本稿では、ひとまず「イシュートラッカー」で統一しています。

*2 またその過程で、`intl.tsf.enable`という設定の値を`false`に変更すると現象が解消されるという、別の暫定的解決策も紹介されていました。これはWindows XP以降で標準となっている文字入力の仕組みを使うかどうかを制御する設定で、`false`に設定すると従来の文字入力の仕組みを使うようになります。音声からの文字入力などTSFで初めて可能になった事はできなくなりますが、その状態でも、キーボードからの一般的な文字入力操作に関しては特に支障なく行えます。

*3 これに対し、当社の法人向け有償サポートサービスは、ソースコードレベルの調査を実施したり、一定の期限内で回答したり、という事をお約束する事をもって対価を頂いているという事になります。

*4 ソフトウェア開発ではなく運用に従事しているという人や、まだ就職していなくて学生で開発に参加する人もいます。

*5 実際に、Mozillaに雇用されている人達もChromiumにバグ報告をしています。

*6 「プログラマーの三大美徳」の1つの「傲慢」とは、このような事(自分にはこの問題を解決できるはず、という考え方をすること)を指します。

*7 当社の法人向けサポートサービスは、お客様の問題(個別の問題)について原因や回避方法を調査してその解決を図ると同時に、その問題がソフトウェアの問題であった場合はソフトウェアの品質向上のためアップストリームへのフィードバックも行う事を心がけています。

2019-06-18

代表取締役の須藤がApache Arrowの開発でGoogle Open Source Peer Bonusを受賞

Apache Arrowの開発に参加している代表取締役の須藤です。

もう2ヶ月前の話なのですが、2019年のGoogle Open Source Peer Bonusの1人に選ばれました。選ばれるまでこんな取り組みがあることを知らなかったのですが、2011年からやっているそうです。オープンソースの開発に関わっている人を支援する取り組みだそうです。賞金(?)として250ドルもらいました。ありがとうございます。

Apache Arrowの開発をがんばっているということで選んでもらいました。現時点で私は2番目にコミット数が多いのでそのあたりを評価してくれたのかもしれません。ちなみに、1番コミット数が多いWesさんはpandasの開発で選ばれていました。

私の自由なソフトウェアの開発を支援したいけどどう支援したらよいかわからないという方はご相談ください。

2019-06-17

Firefox 67以降のユーザープロファイルの仕様の詳細

Firefox 67の新機能の1つに、インストール先ごとにプロファイルが分けられるようになったという物があります。

この記事では、Firefoxのユーザープロファイルの仕組みの歴史的な経緯や、プロファイル切り替えの仕組み自体がどのように使われてきたかという点を踏まえながら、Firefox 67での新機能の性質を解説します。

Firefoxの「ユーザープロファイル」

Firefoxのユーザープロファイルの仕組みは、元を辿ると、その前身となったNetscape Communicatorのユーザープロファイルの仕組みに行き着きます。

Netscape Communicatorが現役だった頃に広く使われていたWindows 95やWindows 98は、原則としてユーザーは1人だけであるという前提で設計されていたため、複数人で同じPCを共用する場合にユーザーを使い分けるという事ができませんでした。そのためNetscape Communicator独自の概念として「使用者ごとにユーザープロファイルを作り分けて、設定値やブックマークなどはその中で管理する」という仕組みが用意されました。

(図:Netscape Communicatorのユーザープロファイルをユーザーごとに使い分ける様子)

その一方で、Windows XP以降のバージョン*1ではOS自体が複数ユーザーを想定した設計になっています。アプリケーション側で個別にユーザープロファイルを管理する必要は無く、アプリケーションの設定を使用者ごとに使い分けたい場合、単純にWindowsのユーザーを使い分ければよいという事になっています。

(図:Windowsのプロファイルをユーザーごとに使い分ける様子)

以上のような歴史的な経緯があり、Firefoxのユーザープロファイルは「Windowsのユーザープロファイルごとに、複数のFirefoxのプロファイルが存在しうる」という少々複雑な状況となっています。

(図:Windowsのプロファイル上に複数のFirefoxのプロファイルが存在する様子)

実際には、Windowsのユーザーを使い分ければよい現状においては、使用者ごとにFirefoxのプロファイルを使い分けないといけないという状況はまず発生しません。よって、Firefoxのユーザープロファイルは今では1人のユーザーが、「仕事用」「開発用」「趣味用」といった何らかの用途ごとに使い分けるという用途が主流になっています。

(図:Windowsのプロファイル上で、用途ごとにFirefoxのプロファイルが使い分けられている様子)

「ユーザープロファイルの使い分け」から「コンテキストやコンテナーの使い分け」への変化

ただ、一般的なユーザーが普段使いにおいてFirefoxのユーザープロファイルを使い分けるということは、実際にはあまり無いものと思われます。その最大の理由は、運用の面倒さです。

Firefoxのユーザープロファイルを使い分けるには、

  • Firefoxの起動オプションに-pオプションを指定してプロファイルマネージャを起動し、そこでプロファイルを管理する。
  • about:profilesでプロファイルを管理する。

このいずれかの方法でプロファイルを作成し、さらに、

  • "C:\Program Files\Mozilla Firefox\firefox.exe" -p private(個人的な使用)
  • "C:\Program Files\Mozilla Firefox\firefox.exe" -p work(仕事での使用)
  • "C:\Program Files\Mozilla Firefox\firefox.exe" -p test(検証用)

のように起動オプションを変えたショートカットを複数用意する必要があります。

また、これだけでは「個人用のFirefox」「仕事用のFirefox」を並行して起動することができません。というのも、Firefoxのプロセスが既に起動している場合、別のプロセスで新たにFirefoxを起動しようとすると、既に起動している方のプロセスの方にプロセスが吸収されてしまう(そちらでウィンドウやタブが開かれる)という結果になるからです。これを回避するためには、

  • "C:\Program Files\Mozilla Firefox\firefox.exe" -p private
  • "C:\Program Files\Mozilla Firefox\firefox.exe" -p work -no-remote
  • "C:\Program Files\Mozilla Firefox\firefox.exe" -p test -no-remote

のように、普段使いの(URLなどの関連付けからFirefoxが起動された時に使う)プロファイルを除いて他のショートカットには-no-remoteオプションを指定し、前述の「既存プロセスに吸収される」挙動を無効化する必要があります。

また別の方法として、プロファイルマネージャを使用せずに以下のようにする方法もあります。

  • "C:\Program Files\Mozilla Firefox\firefox.exe" -profile "C:\Users\username\private-profile" -no-remote

-profileオプションで任意のフォルダをフルパスで指定すると、プロファイルマネージャの管理下にないフォルダであっても、それをプロファイルとしてFirefoxを起動できます。実験や検証のためのプロファイルをいくつも併用したり使い捨てにしたりという使い方をしたい場合、プロファイルマネージャを使わずともフォルダを用意するだけで済むため、この方法は非常に便利です。ただしこの場合も、-no-remoteを付けないと「既に起動している方のプロセスの方にプロセスが吸収される」という動作になってしまう点には注意が必要です。

これだけ面倒な手順が必要なわりに、一般的なユーザーが普段使いで得られるユーザー体験はあまり良い物ではありません。というのも、実際の利用局面では「仕事中に見つけた面白そうな記事を個人用にブックマークしておく」「個人的な使用中に見つけた仕事に関係する記事を、仕事中に履歴から探す」といった場面が生じやすいのに対し、履歴やブックマークなどがプロファイルごとに別々に保存されていると、そういった「用途をまたいだ情報の共有」ができないからです。

この問題の解決策として現在のFirefoxには、「1つのユーザープロファイルで、閲覧履歴やブックマークなどは共有しつつ、起動中の1つのプロセスの中でログイン状態やCookieなどの認証情報は分離する」という事を可能にする、以下の機能が備わっています。

  • プライベートウィンドウ(そのウィンドウの中で閲覧したページには、通常モードで閲覧した時のCookieなどが通知されない)
  • コンテナータブ(用途ごとに認証情報を切り替える)

これらの機能は、「諸々の不便を被ってでも厳密にプロファイルを分けたい程ではない」「しかし、ログイン状態やCookieなどの認証情報は用途ごとに切り替えたい」という、実際の使用局面でよく見られるニーズに応えるものとなっています。

(図:コンテナーを使い分けている様子)

ユーザープロファイルの使い分けよりもはるかに手軽に使えるため、これらの機能で充分に用を果たせる場面では、ユーザープロファイルの使い分けのニーズはほぼ無くなったと言ってよいでしょう。

それでも残った「ユーザープロファイルを使い分ける必要がある場面」

以上のような経緯から、現在のFirefoxで「ユーザープロファイルを使い分けなければその目的を達成できない」場面は非常に限られています。その代表的な例が、複数バージョンのFirefoxの並行起動です。

(図:複数バージョンのFirefoxを専用プロファイルで使い分けている様子)

例えば、前述の手順を用いて複数バージョンのFirefoxを並行起動する場合、以下のように起動オプションでプロファイルを指定する事になります。

  • "C:\Program Files\Mozilla Firefox\firefox.exe" -p default(64bit版Firefox)
  • "C:\Program Files (x86)\Mozilla Firefox\firefox.exe" -p 32bit -no-remote(32bit版Firefox)
  • "C:\Program Files\Mozilla Firefox Beta\firefox.exe" -p beta -no-remote(ベータ版)

Firefox 67からの変更は、まさにこの用途のためのものです。言い換えると、Firefox 67以降のバージョンでは、「インストール先が異なるFirefoxの実行ファイルごとに、専用のユーザープロファイルを使用する」使い方が、特にユーザー側での工夫無しに自動的に行われるようになっているという事になります。

Firefoxが自身のインストール先を識別する処理の流れ

では、このような挙動は一体どのようにして実現されているのでしょうか。特に、「特定のインストール先のFirefox」と「特定のユーザープロファイル」とはどのように紐付けられているのでしょうか。鍵になるのは、インストール先のパスに基づくCity Hash値です。

Firefox 67以降のバージョンでは、Firefoxはプロセスの起動時に、自身の実行ファイルの位置からCity Hashのアルゴリズムに基づいてハッシュ値を求めます。例えばFirefoxがC:\Program Files\Mozilla Firefox\にインストールされていた場合、値は308046B0AF4A39CBのようになります。内部的にはこれは「install hash」と名付けられている模様です。

次にFirefoxは、ユーザープロファイルが置かれている%AppData%\Mozilla\Firefox(具体的には C:\Users\ユーザー名\Appdata\Roaming\Mozilla\Firefox)の位置にあるprofiles.iniから[Install(install hash)]という名前のセクションを探します。これは通常、以下のような内容になっています*2

; こちらは従来からあるセクション
[Profile0]
Name=default
IsRelative=1
Path=Profiles/xxxxx.default

; 自動的に追加されたセクション
[Install308046B0AF4A39CB]
Default=Profiles/xxxxx.default
Locked=1

このDefaultの値がprofiles.iniから見た相対パスとなっており、このパスを解決することで、特定のインストール先のFirefoxに対して紐付けられたプロファイルフォルダを特定できるというわけです。この仕組みがあるお陰で、関連付け等からFirefoxが起動された場合などで起動オプションでプロファイルが明示されていなくても、適切なプロファイルが自動的に選択されるようになっています。

なお、この機能について「Firefoxのバージョンごとにプロファイルが別れる」というような説明がなされている場合がありますが、以上の仕組みを見て分かる通り、プロファイルはあくまでFirefoxのインストール先のパスに紐付いています。インストール済みのFirefoxに別のバージョンのFirefoxを上書きインストールしたり、Firefox自身の自動更新機能で新しいバージョンに更新したりした場合であっても、Firefoxのインストール先が変わらなければ、プロファイルは以前の物が引き続き使われます。「Firefoxを更新したらその度に新しいプロファイルが作られる」という事はありません。(逆に言うと、この機能を使ってプロファイルを使い分けるためには、必ずそれぞれのバージョンのFirefoxを別々の位置にインストールする必要があります。)

Windowsのレジストリ上での取り扱い

上記のinstall hashはWindowsのレジストリ上でも使われています。

従来は、Windowsのレジストリ上はFirefoxはあくまで「Firefox」という名前の1つのアプリケーションとして扱われており、複数のバージョンを異なるフォルダにインストールした場合は最後にインストールした物(最後にインストーラがレジストリに書き込みを行ったバージョン)がその代表として扱われるようになっていました。例えばシステム標準のブラウザの選択肢の項目は

  • HKEY_LOCAL_MACHINE\SOFTWARE\Clients\StartMenuInternet\Firefox
  • HKEY_CURRENT_USER\Software\Clients\StartMenuInternet\Firefox

とその配下に記録された情報に基づいて表示されていました。

Firefox 67以降のバージョンでは、この情報がインストール先ごとに書き込まれるようになっています。

  • HKEY_LOCAL_MACHINE\SOFTWARE\Clients\StartMenuInternet\Firefox-(install hash)
  • HKEY_CURRENT_USER\Software\Clients\StartMenuInternet\Firefox-(install hash)

同様に、関連付け対象のファイルやデータの型も

  • HKEY_CLASSES_ROOT\FirefoxHTML-(install hash)
  • HKEY_CLASSES_ROOT\FirefoxURL-(install hash)

のようにそれぞれインストール先ごとに登録されるようになりました。このように、Windowsのレジストリ上はインストール先ごとのFirefoxはそれぞれ別々のアプリケーションとして認識されています

アプリケーションの一覧上でそれぞれのインストール先を識別できない問題の回避策

以上の通り、Windowsのシステム上は各インストール先のFirefoxは別々の物として認識されているわけですが、ここで1つ問題があります。内部的にはそれぞれ別々のinstall hashを伴って認識されているものの、表示されるアプリケーション名としてはどれも「Firefox」となるため、標準のブラウザや関連付けの対象を選択する場面で同じ名前の項目がいくつも並んでしまうという事が起こってしまうのです。

(複数のバージョンのFirefoxが同じ名前で並んでいる様子)

現在の所、この問題を解決するには手動でのレジストリの編集が必要です。この一覧に表示されるアプリケーション名は HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\Shell\MuiCache というキーの値が参照されており、

  • C:\Program Files\Mozilla Firefox\firefox.exe.FriendlyAppName のデータ→Firefox Release
  • C:\Program Files\Mozilla Firefox ESR\firefox.exe.FriendlyAppName のデータ→Firefox ESR

のように実行ファイルのパス.FriendlyAppNameという名前の値のデータをそれぞれ編集しておくと、インストール先ごとの項目が分かりやすい名前で表示されるようになります。

(複数のバージョンのFirefoxにそれぞれ別の名前が付いている様子)

起動オプションの作用はどう変わったか

上記の通り、Firefox 67以降のバージョンは自分自身のインストール先に基づいて適切なプロファイルを自動的に選択するようになったわけですが、では、今まで使われていた「起動オプションでプロファイルを明示する」機能はどのような影響を受けたのでしょうか。

-p プロファイル名

前述した通り、Firefox 67以降のバージョンでは%AppData%\Mozilla\Firefox\profiles.iniに以下のように[Install(install hash)]という名前のセクションが追加されます。

; 従来からあるセクション
[Profile0]
Name=default
IsRelative=1
Path=Profiles/xxxxx.default

; 自動的に追加されたセクション
[Install308046B0AF4A39CB]
Default=Profiles/xxxxx.default
Locked=1

この時、2つのセクションで同じパスが参照されている点に注目して下さい。Firefoxは参照先のパスが一致する2つのセクションを関連付けて認識しています。そのため、以下の2つのコマンド列は同等として扱われます

  • "C:\Program Files\Mozilla Firefox\firefox.exe" (install hashに基づいて自動的にプロファイルを選択)
  • "C:\Program Files\Mozilla Firefox\firefox.exe" -p default-pオプションでプロファイルを明示)

引数無しで起動したFirefoxのプロセスがある状態でプロファイルを明示してFirefoxを起動しても、逆に、プロファイルを明示して起動したプロセスがある状態で引数無しでFirefoxを起動しても、既に起動している方のプロセスに処理が吸収される(既存プロセスの方でウィンドウまたはタブが開かれる)という、従来の-no-remoteオプション無しの動作と似た結果になります。

同じバージョン・同じインストール先のFirefoxを、別プロセス・別プロファイルで起動する

別のプロファイルを指定して別のプロセスを並行動作させたい場合、従来は-p 別のプロファイル名と指定するだけでは不十分で、-p 別のプロファイル名 -no-remoteと指定する必要がありました。Firefox 67以降のバージョンではこの点が改善され、-p 別のプロファイル名と指定するだけで別プロセスのFirefoxを並行動作させられるようになりました

従来は-no-remoteオプションが必須だったため、必然的に、これらのオプションを指定したショートカットからFirefoxを多重起動しようとすると、「既存プロセスに処理が吸収されない」「しかし、新しいプロセスで使おうとしているプロファイルは、別のプロセスによって既に使用中」ということで、「Firefoxは起動していますが応答しません。新しいウィンドウを開くには既存のFirefoxをプロセスを終了させなければなりません。」というメッセージが表示されてしまうという結果になっていました。Firefox 67以降では、この制限に煩わされる事はもうありません。

-profile プロファイルのフルパス

プロファイルをフルパスで指定して起動する場合も同様に、-no-remoteオプション無しでの別プロセス起動が可能となりました。以下のように起動オプションを指定すると、それぞれのプロファイルでFirefoxのプロセスが並行して動作する状態になります。

  • "C:\Program Files\Mozilla Firefox\firefox.exe" -profile "C:\Users\username\profile1"
  • "C:\Program Files\Mozilla Firefox\firefox.exe" -profile "C:\Users\username\profile2"

この場合、同じ起動オプションでFirefoxを多重起動すると、既に起動していてそのプロファイルを使用しているプロセスに処理が吸収されます。

また、条件を整えると、「起動オプションでプロファイル名を明示する場合」と「フルパスで明示する場合」を等価に扱うことや、あるいは、「プロファイル名を明示」「フルパスで明示」「プロファイルを明示しない」の3つを等価に扱うという事も可能になっています。

重要なのは以下の点です。

  • 当該プロファイルがプロファイルマネージャの管理下に入っている(profiles.iniにそのフォルダの情報が記載されている)。
  • 当該プロファイルのプロファイルマネージャ上の名前と、フォルダ名とが一致している。

以下は、profiles.iniを編集して上記の条件を満たすように設定したプロファイルの例です。

[Profile0]
;Name=default
Name=xxxxx.default
IsRelative=1
Path=Profiles/xxxxx.default

Firefoxのプロファイルマネージャはプロファイルを作成する際、表示用の名前の前にランダムな文字列を付与してフォルダを作成します*3この表示名が記載されているName=の行を編集し、表示名がプロファイルのフォルダ名と一致する状態にします。この状態であれば、以下の2つのコマンド列は等価に扱われるようになります。

  • "C:\Program Files\Mozilla Firefox\firefox.exe" -profile "%AppData%\Mozilla\Firefox\Profiles\xxxxx.default"
  • "C:\Program Files\Mozilla Firefox\firefox.exe" -p xxxxx.default

また、install hashとの紐付けが

[Profile0]
Name=xxxxx.default
IsRelative=1
Path=Profiles/xxxxx.default

[Install308046B0AF4A39CB]
Default=Profiles/xxxxx.default
Locked=1

のように行われていれば、以下の3パターンがすべて等価に扱われるようになります。

  • "C:\Program Files\Mozilla Firefox\firefox.exe" -profile "%AppData%\Mozilla\Firefox\Profiles\xxxxx.default"
  • "C:\Program Files\Mozilla Firefox\firefox.exe" -p xxxxx.default
  • "C:\Program Files\Mozilla Firefox\firefox.exe"

ただ、前述した通り、Firefox 67以降での新機能の恩恵を受けるためには、プロファイルをプロファイルマネージャの管理下に置く必要がある、という点に注意が必要です。「フルパスでプロファイルを明示したい場面」と「プロファイル指定無しでプロファイルを自動認識させたい場面」が重なる事はあまりありませんが、この両方のニーズを同時に満たしたいという場合*4には、見落としが生じないようにくれぐれも気をつけて下さい。

まとめ

以上、Firefox 67以降での「Firefoxのインストール先ごとのユーザープロファイルを使用する挙動」の詳細について解説しました。

Google Chromeが覇権を取った現在においては、Firefoxのニーズは一般ユーザーよりも(おそらくは検証用として)Web制作・Webアプリ開発を業としている人の方に偏っている模様です。「複数バージョンを並行して起動して検証する」という場面で特に有用なこの機能が導入されたのも、そのような背景があったからという事ではないかと考えられます。筆者ほど多くのバージョンを使い分ける事はそうそう無いと思われますが、通常リリース版・ベータ版・ESR版の3つを並行起動できるだけでも検証の負担は下がります。皆さんもこれを機に試してみてはいかがでしょうか。

また、今回の調査は現在Firefox ESR60を運用中のお客様にFirefox ESR68をご案内するにあたって、変更点の詳細を把握するために行いました。当社の法人向けFirefoxサポートでは、詳細な技術情報が提供されていない新機能の詳細の調査も有償にて承っております。「SIの業務において、お客さまの承認を得るために詳細なエビデンスの提出が求められているが、どこを対象に調査すればよいのか分からない」といった問題でお困りの方は、メールフォームよりぜひお問い合わせ下さい。

*1 正確には、Windows 2000やそれ以前のWindows NTなども含みます。

*2 Firefox 67以降のバージョンを初めて起動した時に、このような情報が追記されます。

*3 これは、ユーザープロファイルの位置を決め打ちで特定しにくいようにして、悪意の攻撃者の被害を受けにくくするための工夫です。

*4 例えば、web-ext経由でFirefoxを起動して常用しているというような場合。

タグ: Mozilla
2019-06-14

MXEを使ってUNIX系フリーソフトウェアをGNU/Linux上でWindows向けにクロスビルドする

はじめに

UNIX系のフリーソフトウェアMinGW-w64でビルドすることでWindows上で使えるものも多いですが、Windows上でのMinGW-w64およびMSYS2を使ったビルドは、UNIX系OS上でのセルフビルドと比べて非常に遅いという問題があります。こういった問題はUNIX系OS上でWindows向けにクロスビルドすることで改善することができますが、様々な環境変数やオプションを指定する必要があり、ビルド方法が煩雑になります。また、どちらでビルドするにしても、依存するライブラリについて一つ一つWindows用SDKを用意する必要があり、それらを手動で用意するのも骨が折れます。特に全ての依存ライブラリを自分でビルドしたい場合、ソースコードに手を入れなければならないことも多く、非常に手間がかかります。GNOMEプロジェクトで開発されているJHBuildというビルドシステムを使うことである程度は解決することができますが、Windowsについてはサポートが十分ではなく、素直にビルドが通らないことも多いようです。

これまでは真面目に調べたことがなかったこともあり、なかなか定番と言えるWindows向けクロスビルドシステムが存在しないなと思っていたのですが、最近になってMXE(M cross environment)というクロスビルドシステムの存在を知ったので、紹介致します。

MXEの概要

MXE(M cross environment)はUNIX系OS上からWindowsへのクロスビルドに特化したビルドシステムのようです。このようなビルド環境に特化しているため、サポートされているホストおよびターゲットであれば、ほぼノートラブルでビルドを完了できます。

ビルドシステムはGNU Makefile形式のみで記述されているためシンプルで理解しやすく、新たなパッケージを追加するのも比較的容易です。標準で収録されているソフトウェアはGTKやQt、SDLといったGUI系のものがメインで、非GUI系のものは手薄いという印象です。詳細はMXE: List of Packagesに記載されています。

ビルド環境の準備

MXEを動かすのに必要なソフトウェア一式はMXE: Requirementsに記載されています。

例えばDebianやUbuntuの場合には以下のコマンドで必要なソフトウェア一式をインストールできます。

$ sudo apt install \
    autoconf \
    automake \
    autopoint \
    bash \
    bison \
    bzip2 \
    flex \
    g++ \
    g++-multilib \
    gettext \
    git \
    gperf \
    intltool \
    libc6-dev-i386 \
    libgdk-pixbuf2.0-dev \
    libltdl-dev \
    libssl-dev \
    libtool-bin \
    libxml-parser-perl \
    lzip \
    make \
    openssl \
    p7zip-full \
    patch \
    perl \
    pkg-config \
    python \
    ruby \
    sed \
    unzip \
    wget \
    xz-utils

DebianやUbuntuではMinGW-w64もaptコマンドでインストールできますが、MXEの場合はMXEでビルドしたMinGW-w64が使用されるため、インストールする必要は無いようです。

次に、MXEをcloneします。

$ git clone https://github.com/mxe/mxe.git

cloneしたディレクトリに移動し、以下のコマンドを実行することで、要求されるソフトウェアが揃っているか否かをチェックすることができます。

$ make check-requirements

この際、トップディレクトリにビルド設定が記載された settings.mk というファイルが生成されます。
必要に応じて、ビルド前に編集しておくと良いでしょう。

# This is a template of configuration file for MXE. See
# docs/index.html for more extensive documentations.

# This variable controls the number of compilation processes
# within one package ("intra-package parallelism").
#JOBS := 

# This variable controls where intermediate files are created
# this is necessary when compiling inside a virtualbox shared
# directory. Some commands like strip fail in there with Protocol error
# default is the current directory
#MXE_TMP := /tmp

# This variable controls the targets that will build.
#MXE_TARGETS := i686-w64-mingw32.static i686-w64-mingw32.shared x86_64-w64-mingw32.static x86_64-w64-mingw32.shared

# This variable controls which plugins are in use.
# See plugins/README.md for further information.
#override MXE_PLUGIN_DIRS += plugins/apps plugins/native

# This variable controls the download mirror for SourceForge,
# when it is used. Enabling the value below means auto.
#SOURCEFORGE_MIRROR := downloads.sourceforge.net

# The three lines below makes `make` build these "local
# packages" instead of all packages.
#LOCAL_PKG_LIST := boost curl file flac lzo pthreads vorbis wxwidgets
#.DEFAULT_GOAL  := local-pkg-list
#local-pkg-list: $(LOCAL_PKG_LIST)

例えば、MXE_TARGETSを省略すると、デフォルトはi686-w64-mingw32.staticになるようですので、ターゲットをx86_64にしたい場合や、各種ライブラリを共有ライブラリとしてビルドしたい場合などは事前の設定が必要です。

例:

MXE_TARGETS := x86_64-w64-mingw32.shared

ビルド方法

ビルドは単純で、

$ make レシピ名

でビルドできます。例えばGTK3をビルドした場合は

$ make gtk3

とするだけで、依存ライブラリを含めたソースパッケージのダウンロードやビルドが自動的に実行されます。ビルドされたソフトウェアはデフォルトでは./usr/[ターゲット名]以下にインストールされるようです。

新たなソフトウェアの追加方法

MXEの各パッケージ向けMakefileはsrcディレクトリ下に[パッケージ名].mkの名前で用意されています。
拡張子が.mkのファイルは自動的に認識されるので、新たなソフトウェアは同様の方法で追加することができます。

また、前述のsetting.mkに以下のような設定があることからもわかるように

# This variable controls which plugins are in use.
# See plugins/README.md for further information.
#override MXE_PLUGIN_DIRS += plugins/apps plugins/native

MEXにはpluginという仕組みがあり、この仕組みを使って新たなパッケージを追加することもできます。プラグインとは言っても、単に.mkファイルを読み込むディレクトリを追加するだけですので、src以下の.mkファイルと同様に変数やマクロを追加(あるいは上書き)するだけです。詳細は、上記のコメントにもある通りplugins/README.mdに記載されています。

まとめ

UNIX系OS上からWindowsへのクロスビルドに特化したビルドシステムMXE(M cross environment)を紹介しました。

クリアコードが過去に関わった案件で、GTKを使用してマルチプラットフォームアプリケーションを開発されているお客様がおられましたが、ビルドシステムは独自に構築されており、そのメンテナンスに多大なコストがかかっていました。当時MXEのことを知っていれば、開発コスト低減の提案が出来ていたかもしれないな、ということが悔やまれてなりません。

2019-06-06

株式会社アカツキさまでの新人研修の一環としてOSS Gateワークショップを実施しました

結城です。

当社のOSS開発支援サービスではこれまで、SpeeeさんでのOSS開発支援やご相談窓口の設置、Supershipさまでの研修の実施などに携わって参りました。この記事ではその新たな事例として、株式会社アカツキさまで昨年および本年の新人研修の一環として開催した OSS Gate のワークショップを社内にて複数回開催させていただいた件をご紹介します。

アカツキさまへのクリアコードの関わり方

株式会社アカツキさまの事業の柱の1つはゲームの開発・運営です。お話を伺ったところ、ゲーム業界では(少なくとも、ファミコンに端を発するコンソールゲームの業界では)伝統的に、会社ごとに自社開発のライブラリやノウハウを抱え込む事が多かった事から、開発の成果を公開したり社外に開発の中心があるOSSの開発に参加したりという事はそれほど積極的には行われない傾向があるとのことでした。

しかし、現代のゲーム開発では技術が1社だけに閉じないケースが多くなっています。オンラインゲームではサーバー側の技術基盤は一般的なWebサービスと変わりませんし、クライアント側においてもUnityのスクリプトエンジンが.NET FrameworkのOSS実装のMonoであったりします。また、ゲーム本体から周囲に目を向ければ、そもそも開発に使用するGitやVisualStudio CodeなどのツールもOSSです。このように、サービス提供も開発も今やOSS無しではままならないというのが実情です。こういった背景があり、アカツキさまでは「会社としてOSS活動を推進していきたい」という意気が高まっていたそうです。

これを踏まえて、アカツキさまの技術アドバイザーSpeeeさまの技術顧問を兼任されている井原さんから当社をご紹介頂き、アカツキさまのOSS活動推進の取り組みをお手伝いさせて頂くこととなりました。その際、具体的な取り組み方について相談した結果、まだOSS活動に関わったことがない人が多い・(今後の進め方を考えるために)当社が現状を把握したいという理由から、まずは当社メンバーが案内役を務める形で OSS Gate ワークショップをアカツキさま社内で開催することとしました。

OSS Gate ワークショップ in アカツキ

OSS Gate ワークショップは、参加者を「まだOSS開発に関わった事が無い人=ビギナー」と「OSS開発に関わった経験がある人=サポーター」に分けて、ビギナーが主体的に作業を進めるのをサポーターが手助けする*1という形で、バグ報告やパッチの提供などの「フィードバック」をする体験をして頂くイベントです。今回は、ビギナーとしては主に新入社員の方や中途採用されて日が浅い方に多くご参加頂く結果となりました。また、通常の公開で行う OSS Gate ワークショップではサポーターは広くボランティアを募っていますが、今回はサポーターも皆アカツキさま社内の方に務めて頂きたいという事で、こちらも社内で希望者を募ったり、アカツキ内でこの取り組みの主担当をされているシニアエンジニアの島村さんから指名して頂いたりしました。これは、サポートする側を経験して頂く事により、普段から業務の中で他の方のOSS開発への参加を手助けして頂きやすくなればという目論見があったためです。

開催形態は東京で開催している OSS Gate ワークショップの形態を踏襲し、1人のサポーターが1~2人のビギナーと組む形としました*2。参加希望者のスケジュールが合う日がバラバラだったため複数の日に分けて開催することとし、2018年には4回、2019年には3回に分けて実施しました。

ワークショップは通常は1日がかりで行いますが、今回はアカツキさまの業務の都合もあり、内容を一部省略して所要時間を2/3~半分程度に短縮しての実施としました*3

(写真:2019年開催分の様子)

各回の参加者アンケートは OSS Gate ワークショップのリポジトリで公開されており、以下から閲覧可能です。

OSS Gate ワークショップはフィードバックの過程を体験して頂くという趣旨のイベントなので、バグ報告やパッチ提出などの形に残る結果を出すことを必須には設定していません。そのため、通常の OSS Gate ワークショップはフィードバックに至る手前で時間切れになる方も多いです*4。ただ、アカツキさまでの開催分については研修で前提知識が共有されている部分が多いためか躓きはあまり発生せず、ほとんどの方が時間内に実際にフィードバックを行う段階まで到達されていました。

筆者が印象的だった出来事としては、ビギナーの方が選択を検討されていたソフトウェアが実はOSSではなかった、という事例がありました。ゲーム業界ということでUnityを使われている方が多く、その方は「ユニティちゃんライセンス」が設定されたプロジェクト(unity3d-jp/unitychan-crsなど)へのフィードバックを検討されていたのですが、調べてみると、当該ライセンスはOSIが認定済みのOSSライセンス一覧には掲載されていませんでした*5。この事例のように、GitHubにリポジトリがある・ソースコードが閲覧可能な状態になっているからといってOSSであるとは限らない、という点は意外と見落としてしまいがちなので、皆さんもくれぐれもご注意下さい*6

なお、この一連のワークショップが契機となってか、後に参加者の方が新たにOSSを公開されたという事を後からお知らせ頂きました*7。OSS Gate ワークショップではフィードバックをする際の注意点として「作者(プロジェクト運営者)にとって分かりやすくなるように報告内容を整理する」という事を度々伝えていますが、自分自身が作者としてOSSを公開する側になると、実際にフィードバックを受ける際にどのようなフィードバックであれば助かるかという事を、より実感を持って考えられるようになるはずです。得るものは多いと思われますので、開発したソフトウェアをOSSとして公開する事にも、皆さんには是非チャレンジして頂きたいです。

継続的なOSS活動の推進のためのふりかえり

アカツキさま社内での OSS Gate ワークショップ開催は、ゴールではなく最初の一歩という位置付けです。そのため、毎回のワークショップ終了後にはアンケートの回答を見ながら参加者全員で「どうすれば、社内にOSS活動が根付くか?」という事を考えるふりかえりの時間を設けました。

ワークショップ後も継続してフィードバックしていく事が根付くかどうかが課題であるという点は、公開で開催されている OSS Gate ワークショップと共通している様子でした。アカツキさまのケースでは特に、「業務と並行してフィードバックも行う」という事を全体でどう習慣化していくかが課題という事になります。この点について、各回のアンケートの回答からは、現在は「普段の業務」と「OSS活動」を全く別の物として捉えている方が多いようで、「普段の業務をしていると、OSS活動をする時間がない」と認識されている様子がうかがえました。

業務上使用しているツールやライブラリの不具合を見つけた場合、仮にそれが自社開発の物であれば、不具合を伝えて修正してもらうという事は、業務の中の出来事として受け入れやすいでしょう。その際にはもちろん、修正する側にとってわかりやすい形の障害報告にするという事も求められるはずです。業務で使用するOSSへのフィードバックも、報告先が公開のイシュートラッカーであったり、報告に使う言語が英語であったり*8という差異はあるものの、基本的にはそれと同じ事と言えます。よって、心理的な障壁をどのようにして取り除くか、いかにして「業務の活動をしていたら、それが自然とOSS活動になっていた」と言える状況を作るか、という事が重要なのではないかと思われます。

ただ、業務での開発では「ライブラリやツールのバージョンは開発時点の物で固定し、最新版へは更新しない」といった事も行われがちです。その場合、フィードバックの結果が業務に反映されることが無いという事になってしまうので、開発元へのフィードバックに時間を使う事に対して「業務と無関係な事をしている」感覚が強くなってしまう、という側面はありそうです。この種の問題については個人でどうにもなりませんので、むしろ、古いバージョンのソフトウェアを使い続ける事にはセキュリティなどの面でリスクがあるという認識を全員で持って、適宜ソフトウェアのバージョンを上げていく健全な開発・メンテナンスのスタイルを取り入れていくという、普段の開発そのものの見直しも必要になってくるでしょう。

こういった事を全体で一気に推し進めるのは困難が伴います。アカツキさまでは元々、日常的にフィードバックをされている方の周囲ではスポット的にそのような動きが見られるとのことですので、全体を一気に改革するという事には拘らず、周囲の人に影響を与えるリーダーとなる人を増やした上で部分部分のできる所から進めていく、というのが無理のない進め方なのかもしれません。

また、この取り組み全体を担当されているシニアエンジニアの島村さんと一緒に行った、全体を通してのふりかえりの中では、OSSへのフィードバックで困った人が表れた時の気軽な相談先として当社をお使い頂く、というような方向でのOSS推進もご検討頂きました。

まとめ

以上、アカツキさまでのOSS推進の取り組みの一環として実施した OSS Gate ワークショップの様子をご紹介しました。

当社では「自社内にOSS開発者を育てたい」「OSSへのコントリビュートを行いたい」といった企業さまのご依頼を受けて、人材育成や社内文化の改善といった観点からご協力させて頂くというOSS開発支援サービスを行っています。今回のアカツキさまでの事例のように、社内事情に合わせて規模や内容をカスタマイズしての OSS Gate ワークショップ開催のお手伝いも承っております。社内で旗振り役を務められる人が少ないためにOSS活動をうまく推進できていない、などのお悩みをお持ちの方がいらっしゃいましたら、是非メールフォームよりお気軽にお問い合わせ下さい。

なお、近い日程では2019年6月8日にOSS Gate 東京ワークショップが開催される予定です。公開のOSS Gateワークショップはサポーター側が不足しがちなため、ビギナー側での参加は枠がすぐ一杯になってキャンセル待ちになりやすいのですが、「バグ報告をした事がある」という方はサポーターとして登録して頂ければ、多くの場合はそのまま参加できます。会社としてOSS活動を奨励していきたいという方は、実際にワークショップに参加して頂くと雰囲気や要領を参考にして頂けると思いますので、是非ご検討下さい。

*1 詰まった時にヒントを出す、迷ったときにアドバイスするなど。

*2 東京開催分は、現在は基本的に1人のビギナーに1人以上のサポーターが付く形態とし、サポーターの人数以上のビギナーを集めない方針を取っています。アカツキさま社内での開催に関してはビギナー参加者を可能な限り多く受け入れる必要があったため、進行役・サポートメンターを加えればマンツーマンに相当する人数比は維持できそうという事から、この人数に設定しました。

*3 過去には、PHPカンファレンスの中で一室を借りて実施した際にもこの形態を取りました。

*4 Gitの使い方に手間取ったり、課題選びに悩んだり、選んだ課題の規模が大きすぎて準備に時間がかかったり、という例が多いようです。

*5 OSIでは、オープンソースの定義に合致していて、且つOSIで認定されているライセンスに基づくソフトウェアのみをOSSと呼ぶ事を推奨しています。また、よくある質問の回答として、オープンソースの定義に合致していても一覧に掲載されていない物をOSSライセンスと称する事は混乱を招くため、(承認され一覧に掲載されるまでは)「独自のOSSライセンス」を無闇に策定・自称しない事が求められています。なお、筆者がユニティちゃんライセンスの文面を確認した限りでは、営利目的での利用の禁止などの条件がオープンソースの定義に合致しないようでした。

*6 OSSでなくとも、ライセンスの条件に従って利用する限りにおいては何ら問題ありません。また、OSSライセンスでないライセンスを設定する事もここでは否定していません。ただ、OSSライセンス同士であっても条件が互いに矛盾するために成果物を組み合わせられない場合はあり、OSSライセンスとOSSでないライセンスとの組み合わせでは余計にそのような状況が発生しやすい、という事は言えます。

*7 s-luna/AndroidReplayRecorder

*8 作者が日本語話者のOSSでは、日本語でフィードバックするという事もありますが、多くの場合は英語が使われています。

2019-05-29

Thunderbirdアドオン「CardBook(連絡先)」でローカルに保存されるデータの暗号化に対応しました

CardBookと企業利用

皆さんはCardDAVという仕様をご存じでしょうか? CardDAVはWebDAVのプロトコルを使ってLDIF形式のアドレス帳をやり取りするという物で、これを用いると「読み書き両方を行えて、内容が複数PC間で同期される」という種類のリモートアドレス帳を汎用の物として実現することができます。CardDAVサーバーとして振る舞える製品にはownCloudやDAViCalなどがあり、読み取り専用に設定したリモートアドレス帳を複数人で共有するという事もできますので、企業利用では重宝する場面がありそうです。

このように便利なCardDAVですが、残念ながらThunderbirdは本体の機能としては対応していません。CardDAVベースのリモートアドレス帳を使うにはアドオンをインストールする必要があります。CardBookは、そのようなCardDAV対応のためのアドオンの一例です。

ところで、企業によっては個人情報の取り扱い方について、「顧客や取引先の個人情報をローカルに保存する際は必ず暗号化する」といったプライバシーポリシーを定めている場合があります。前出のCardBookはリモートアドレス帳のデータをIndexedDBを使用してローカルにキャッシュする設計で、この時のデータは暗号化されないため、そのままでは前述のポリシーに抵触するので採用できないという事になります。

そのような背景から、「CardBookでローカルに保存されるデータを暗号化したい」というご相談を頂き、成果を開発元に還元する前提で先行して作業を進めていたのですが、残念ながら受注には至らず、手元には実現可能性の調査のために行った試験的な実装が残るという結果になりました。しかしせっかく実装した物をそのまま放置しておくのも勿体なかったので、CardBookプロジェクトに還元したところ、標準機能の1つとして取り込まれるに至りました。現在リリース済みのCardBook 33.9以降のバージョンでは、設定画面でチェックボックスをONにすればローカルデータの暗号化が有効になるようになっています。

以下、CardBookのローカルデータベースの暗号化を実現するにあたって行った具体的な内容をご紹介します。

IndexedDBに格納するデータの暗号化と復号

幸い、Thunderbirdの基盤であるGeckoには、暗号化のための汎用APIであるWeb Crypto APIが実装されています。あるのなら使わない理由はありませんので、CardBookでもデータの暗号化はWeb Crypto APIによる共通鍵暗号で行う事にしました。何故公開鍵暗号ではなく共通鍵暗号なのかについては別項で詳しく述べていますので、そちらも併せてご覧下さい。

実装は、まず暗号化・復号を行う専用のモジュールを追加した上で、IndexedDBの読み書きを行うモジュールの書き込み用のデータを用意する箇所に暗号化処理を読み込んだデータを検証する箇所に復号処理を仕掛けることで、他のモジュールに影響を与えず透過的に動作するような組み込み方としました。

この時気をつけなくてはならないポイントとして、暗号化をどのタイミングで行うかという点が挙げられます。以下、暗号化を行っている実際の箇所を抜粋しながら説明します。

元々の設計では、IndexedDBへのデータ書き込みは以下の要領で行われていました。

addCard: unction (aDirPrefName, aCard, aMode) {
  var db = cardbookRepository.cardbookDatabase.db;
  // トランザクション開始
  var transaction = db.transaction(["cards"], "readwrite");
  var store = transaction.objectStore("cards");
  var storedCard = aCard;
  // データの書き込み
  var cursorRequest = store.put(storedCard);

  // 以下、成功時・エラー時の処理
}

ここに暗号化処理を組み込むのですが、Web Crypto APIは暗号化したデータがPromiseで返されるため、値を使うには.then()のコールバック関数で受け取るか、awaitで値の解決を待つ必要があります。コールバック関数を使うスタイルで実装するにはこのメソッドの書き方を大幅に変えなくてはなりませんが、asyncキーワードとawaitを使うと、この同期処理の関数を容易に非同期処理に対応させることができます。

addCard: async function (aDirPrefName, aCard, aMode) { // asyncキーワードを追加
  var db = cardbookRepository.cardbookDatabase.db;
  // トランザクション開始
  var transaction = db.transaction(["cards"], "readwrite");
  var store = transaction.objectStore("cards");
  // 暗号化処理を追加
  var storedCard = cardbookIndexedDB.encryptionEnabled ? (await cardbookEncryptor.encryptCard(aCard)) : aCard;
  // データの書き込み
  var cursorRequest = store.put(storedCard);

  // 以下、成功時・エラー時の処理
}

当初はこの例のように、書き込みを行う直前で暗号化を行うようにしていました。しかし実際に動作させてみると、これではIndexedDBでのデータ書き込みに失敗するという結果になりました。何故でしょうか?

実は、IndexedDBでのデータ書き込みはトランザクション開始から書き込みまでを同期的に(同じイベントループ内で)行う必要があります。この例ではトランザクション開始後にawaitを使ってしまっているせいで、store.put(storedCard)が次のイベントループでの実行となってしまい、そのせいで書き込みに失敗してしまうという訳です。

そのため最終的な実装では、以下の例のようにトランザクション開始前に暗号化を終えておくようにしました。

addCard: async function (aDirPrefName, aCard, aMode) {
  // 暗号化
  var storedCard = cardbookIndexedDB.encryptionEnabled ? (await cardbookEncryptor.encryptCard(aCard)) : aCard;
  var db = cardbookRepository.cardbookDatabase.db;
  // トランザクション開始
  var transaction = db.transaction(["cards"], "readwrite");
  var store = transaction.objectStore("cards");
  // データの書き込み
  var cursorRequest = store.put(storedCard);

  // 以下、成功時・エラー時の処理
}

これなら、トランザクション開始から書き込みまでが同期的に行われるため問題ありません。

復号時には、特にこのような注意は必要ありません。また、元々IndexedDBからのデータ読み取りは結果が非同期で返されるので、CardBookのデータ読み込み処理もその前提で設計されていました。そのため、IndexedDBから返ってきたデータを非同期で復号した上で返却するという処理を挟み込んでも、CardBookのデータ読み込み処理全体としてはインターフェースを変えずに済んだのでした。

パスワード入力を求める方式にしなかった理由

CardBookのローカルデータ暗号化では、暗号化・復号に使う共通鍵は、バックグラウンドで自動生成した物を暗黙的に使い、鍵自体をユーザープロファイル内に保存する形としました。

「暗号化されたデータと鍵を同じ場所に置いておくのでは、暗号化の意味が無いじゃないか」と思うでしょうか? 実際、変更をフィードバックした際にもCardBookプロジェクトの開発者の方からも「パスワード入力を求める方式にした方がいいのではないか?」という質問がありました。Web Crypto APIの機能を使うとユーザーが入力したパスワードから秘密鍵を作る事もできる(Web Crypto APIの解説記事の「パスワードを鍵に変換する」の項をご参照下さい)のに、そうしなかったのは何故でしょうか。

ここで一旦、パスワードの安全な運用という事を考えてみましょう。パスワードを自分で記憶しておきその都度入力するという方式は、一見すると安全なように思えます。しかしながら、運用の仕方によっては却って危険になる場合があります。

  • パスワード入力には、肩越しに入力の様子を覗き見るショルダーハックや、キーの入力を監視するキーロガーなどによってパスワードを盗み取られるリスクがあります。パスワード入力の頻度が多ければ多いほど、このリスクは高まります。
  • 人間の記憶容量には限りがあるため、あまり複雑なパスワードを複数覚えるという事はできません。そのために「同じパスワードを何度も使い回す」「推測が容易なパスワードにする」といった事が行われてしまい、そうなると却って危険な状態となります。
  • 定期的なパスワード変更にも、同様の問題があります

これらの理由から、パスワードの入力は「複雑で憶えにくいパスワードを1つだけ覚える」「それをマスターパスワードとして使い、それ以外はパスワードマネージャに憶えさせる」という運用にするのが比較的安全だというのが現在の定説となっています*1

「企業でThunderbirdを使う」というシチュエーションでは、「PCのログオン」「受信メールサーバーの認証」「送信メールサーバーの認証」などでそれぞれパスワードの入力が発生する可能性があります。という事は、ここにさらに「ローカルデータの復号」のためのパスワードが加わるというのは、さすがに実運用を妨げるレベルの煩わしさでしょう。かといって、他の部分ではパスワードを使用していないのにここでだけパスワードの入力を求める、というアンバランスな運用も考えにくいです。そういった事を考慮した結果として、CardBookのローカルデータ暗号化は現在比較的安全とされている運用を想定し、

  • 秘密鍵は、自動生成した物をJWK形式でエクスポートし、「長いパスワード文字列」の一種としてThunderbirdのパスワードマネージャに記憶させる。
  • 秘密鍵の保護が必要な場合は、使用者が任意でThunderbird本体のマスターパスワード機能を有効化する。

というポリシーを採用する事にしたのでした。

まとめ

Thunderbird用アドオンのCardBookに対して行った、ローカルデータの暗号化対応の概要をご紹介しました。

当社では、一般に公開されているFirefox用アドオン・Thunderbird用アドオンをはじめとした様々なフリーソフトウェア・OSSについて機能追加・改造のご依頼を承っております。また、成果をアップストリームに還元しても差し支えがないケースでは、積極的に還元を行うようにしています。自社でフリーソフトウェア・OSSを採用したいが少しだけ要件に合わない、という事でお悩みの場合には、メールフォームからお問い合わせ下さい。

*1 ただし、これはあくまで現時点での話です。技術の進歩や、この分野での研究が進む事などによって、「最もマシ」なやり方は変わっていく可能性があります。

2019-05-28

「以前のバージョンでは使えていた機能が新バージョンでは使えなくなった」時の原因の調べ方

この記事はQiitaとのクロスポストです

恐らくあまり知られていませんが、Firefoxはインストール先の browser/chrome/icons/default/main-window.ico の位置(Firefoxのインストール先が C:\Program Files\Mozilla Firefox\ であれば、 C:\Program Files\Mozilla Firefox\browser\chrome\icons\default\main-window.ico)にICO形式のアイコン画像を置くと、それがメインウィンドウのアイコンになるという機能がありました*1

(スクリーンショット:実際にアイコンを変えた様子)

この機能について、次のESR版であるESR68でも有効なのかどうかを確かめようとNightly 68で試してみたところ、アイコンが変化しないという結果を得られました。

ということは、Firefox ESR68では機能が削除されたのだな……と決めつけるのは早計です。開発版のNightlyで駄目だったからといって、そのバージョンのFirefoxが正式リリースされた後も駄目だとは限りません*2。たまたまNightlyでは機能していないだけなのか、それともこの方法が使えなくなってしまったのか、きちんと裏付けを取らないとはっきりした事は言えません。

この記事では、フリーソフトウェア・OSSで遭遇したこのような挙動の変化の経緯を実装を辿って調べる方法について、Firefoxでの事例を示します。

調査の取っかかりを探す

この事例では「ウィンドウのアイコン」がポイントになります。Windows上で動くFirefoxはWindowsのアプリケーションなので、ウィンドウのタイトルバーのアイコンを変えるには当然Windowsのアプリケーションの流儀に則った方法を使っているはずです。

Windows window icon win32 といったキーワードで検索してみたところ、Win32アプリケーションでウィンドウのアイコンを変える伝統的なやり方に WM_SETICON という定数で定められたメッセージを使う方法 があるという事が分かりました。

機能が動いているバージョンでどう実装されているかを調べる

確認してみたところ、前述の方法でのアイコン変更はFirefox ESR60やFirefox 66では機能していました。そこで、Firefox ESR60のソースコード中で SM_SETICON という定数が使われている箇所を検索してみたところ、クロスプラットフォームな実装ではなくWindows固有の実装の部分で、この定数が使われている箇所が見つかりました

このメソッドの冒頭で呼ばれているResolveIconName()というメソッドの定義を辿り、さらにその中で呼ばれている ResolveIconNameHelper() というメソッドの定義まで辿ってみたところ、「与えられたファイルハンドラからiconsdefault、とディレクトリを辿り、与えられたファイル名のアイコンファイルに対応するアファイルハンドラを得る」という処理が行われている様子がうかがえました。これはまさしく、前述の「特定の位置に置かれたアイコンファイルを参照する」という処理そのものに見えます。

この箇所は nsWindow クラスの SetIcon() メソッドに含まれていますので、今度はこのメソッドを呼んでいる箇所を探しました。すると、XULのwindow要素のid属性の値を参照してアイコンを設定しているらしい箇所が見つかりました。

  // "id" attribute for icon
  windowElement->GetAttribute(NS_LITERAL_STRING("id"), attr);
  if (attr.IsEmpty()) {
    attr.AssignLiteral("default");
  }
  mWindow->SetIcon(attr);

ということで、先の方法でウィンドウのアイコンを変更するという機能は、この一連の箇所によって実現されている事が分かりました。

機能が動いていないバージョンでどう実装されているかを調べる

機能が動いているバージョンでの実装が分かったので、今度は対応する部分のNightly 68での実装を見てみました。

先ほど見ていった実装のそれぞれについてNightly 68での状況を見ていくと、ほとんどの箇所には変更が見られませんでしたが、nsWindow クラスの SetIcon() メソッドを呼んでいる箇所に違いがありました。こちらでは、XULのwindow要素のicon属性の値を参照して、値があった時にだけアイコンを設定するというコードになっています。

  // "icon" attribute
  windowElement->GetAttribute(NS_LITERAL_STRING("icon"), attr);
  if (!attr.IsEmpty()) {
    mWindow->SetIcon(attr);

    NS_ENSURE_TRUE_VOID(mWindow);
  }

Firefoxのソースコード検索システムでは、そのまま続けてMercurialのblameの結果を見る事ができます。そこから当該箇所の変更が行われたコミットの情報を見ると、バグトラッカー上での対応するbugの番号が記載されており、Bug 1531836 - Each new xul window does a stat call to look for non-existant window specific iconsに辿り着きました。

bugの内容を読むと、どうやら起動処理の高速化の一環として、「まず存在しないファイルを探しに行く」のではなく「明示的にアイコンが指定されている時だけファイルを探しに行く」という方針に改める変更が行われ、その一環として前述の機能が削除されたという事のようです。

まとめ

以上、フリーソフトウェア・OSSで「以前のバージョンでは使えていた機能が新バージョンでは使えなくなった」という事態に遭遇した時の原因の調べ方の、Firefoxでの事例をご紹介しました。

ユーザーの目に触れやすい機能の変更はリリースノートに記載されることが多いですし、また、開発者向けに影響の大きな変更は技術情報が別途まとめられている事もあります。しかし、この例のように影響度の小さい変更は、そのような変更があったという事自体は特に告知されないままになっている事があります。そういった場合でも、フリーソフトウェア・OSSではソースコードやバージョン管理システムの変更履歴を辿って、原因を特定したり回避方法を見つけたりする事ができます。

クリアコードではフリーソフトウェア・OSSに対する技術サポートを有償にて提供しており、お客様からの「今まで使えていた機能が使えなくなった事の理由を調べて欲しい」「回避策があれば、それを教えて欲しい」といったご依頼を受けてこのような調査も行っています。
業務上でのフリーソフトウェア・OSSの利用でお困りの方は、是非メールフォームからご相談下さい。

*1 正確には、`(id文字列).ico` という名前でファイルを置いておくと、ウィンドウの内部的なID(XULの`window`要素の`id`属性の値)が一致するファイルが自動的に使われるという事になっています。

*2 その逆に、Nightlyでは使えていた機能が同じバージョンのリリース版Firefoxで使えなくなる場合もあります。例えば `xpinstall.signatures.required` という設定はビルド時のオプションによって機能するかどうかが切り替わるようになっており、NightlyとDeveloper Editionでは使えますが、ベータ版および正式リリース版のFirefoxでは機能しないようになっています。

タグ: Mozilla
2019-05-14

SciPy Japan Conference 2019 - Apache Arrow #scipyjapan

ゴールデンウィークの前のことになりますが、SciPy Japan Conference 2019Apache Arrowの紹介をした須藤です。

関連リンク:

経緯

まず、ふだんほとんどPythonを書いていない私がどうしてSciPy Japan Conference 2019で話すことになったのか、その経緯を説明します。

もともと、Wes McKinneyさんにオファーがありました。彼はPythonでよく使われているpandasの作者でもあり、Apache Arrowの主要開発者でもあります。SciPy Japan Conference 2019でApache Arrowの紹介をする人としてはうってつけです。しかし、残念ながらSciPy Japan Conference 2019の開催日に日本に来ることができませんでした。そこで、彼が私を紹介しました。私もApache Arrowの主要開発者の1人(2019-04-23時点ではApache Arrowへのコミット数は2位)で、私は東京に住んでいるからです。

ということで、Wesさんからの紹介で私がApache Arrowの紹介をすることになりました。

内容

Pythonのカンファレンスなので、Apache Arrowの一般的な話というより、Pythonユーザーにはどううれしくなりそうかという観点で紹介したつもりです。具体的には近い将来うれしくなりそうな点として次の2点を紹介しました。

  • pikcleの代わりに使って高速化できる
  • データフレームライブラリーが高速化したり大量のデータを扱えるようになる

すでにこれらの点を実現しているプロダクトがあります。

たとえば、Sparkはpickleの代わりにApache Arrowを使うことで100倍以上高速化しています。

参考:Speeding up PySpark with Apache Arrow

たとえば、VaexというデータフレームライブラリーはApache Arrowを(も)使うことでpandasよりも高速に文字列を処理できるようになっています。

参考:Vaex: A DataFrame with super strings

また、どうして高速になるか、大量のデータを扱えるようになるかの理由も説明しました。詳細はスライドやApache Arrowの最新情報(2018年9月版)Apache Arrow東京ミートアップ2018 - Apache Arrowを参照してください。

イベントの内容

SciPy Japan Conferenceは今回が初めての開催ということで参加者は100人いかないくらいでした。PyCon JPと比べると小さな規模です。(PyCon JP 2018は1000人以上の参加者です。)初めての開催ということであまり知られていなかったこととサイエンスに特化した内容ということが影響しているのかと思います。

私は初めてPythonのイベントに参加しましたが、とても国際カンファレンスですごいなぁと思いました。私は海外で開催されている国際カンファレンスには参加したことがなく、参加したことがある国際カンファレンスはRubyKaigiだけなのですが、海外で開催されている国際カンファレンスはこんな感じなのかなぁと思いました。

SciPy Japan Conference 2019の運営をしていた方々から話を聞いたところ、SciPy Japan Conference 2019は本家のSciPy Conferenceを運営している人たちが運営しているということでした。本家のSciPy Conferenceについても教えてもらえました。「今回は機械学習の話題が多かったけど、本家のSciPy Conferenceは機械学習だけでなくサイエンス全般の話題を扱っているんだよ。地球のこととか。SciPy Japan Conferenceもサイエンス全般の話題を扱えるようになるといいな。」みたいな話を聞いてすごくおもしろそうだなぁと思いました。地球のこととかおもしろそう!Rubyでもそんなカンファレンスがあるとおもしろそう!

午前中に3時間のチュートリアルがあるのもおもしろかったです。Rubyのカンファレンスでもやりたいな。(すぐRubyのことを考えてしまう。)

午後はトークでした。知らないことばかりだったので非常に興味深かったです。Pythonではいろんなことが簡単にできるようになっていてワクワクしますね!Rubyでもそうなるといいな。

せっかくいろいろ知れたのでSciPy Japan Conference 2019の最中にこっそりOptunaのRubyバインディングであるRed Optunaを作りました。SciPy Japan Conference 2019に参加してよかったです。Red Optunaのアイディアは初日のレセプションで佐野さんと話しているときにでてきたアイディアです。話せてよかったです。佐野さんは2日目にOptunaのトークをしました。

また参加したいと思ったカンファレンスでした。

まとめ

SciPy Japan Conference 2019で日本のPythonユーザーのみなさんにApache Arrowを紹介しました。Rubyコミュニティー以外にもApache Arrowのことを紹介したいのでApache Arrowのことを知りたくなったら私に声をかけてください!

私はデータ処理ツールの開発という仕事をしたいと思っています。その中にはもちろんApache Arrowの開発も含まれています。一緒に仕事をしたい!(自社サービスをApache Arrow対応したいとか)という方はお問い合わせフォームからご連絡ください。

タグ: Python
2019-05-08

タグ:
年・日ごとに見る
2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|