読者です 読者をやめる 読者になる 読者になる

Android 7.0 Nougat に追加した API

github.com

  • View#dispatchFinishTemporaryDetach()
  • View#dispatchStartTemporaryDetach()
  • View#isTemporarilyDetached()

github.com

  • InputConnection#closeConnection()
  • InputConnectionWrapper#closeConnection()
  • BaseInputConnection#closeConnection()

github.com

  • InputConnection#getHandler()
  • InputConnectionWrapper#getHandler()
  • BaseInputConnection#getHandler()

github.com

  • Settings#ACTION_KEYBOARD_LAYOUT_SETTINGS

github.com

  • TextView#getImeHintLocales()
  • TextView#setImeHintLocales()

github.com

  • LocaleSpan#LocaleSpan(LocaleList)
  • LocaleSpan#getLocales()

github.com

  • SuggestionSpan#getLocaleObject()

github.com

  • InputConnection#deleteSurroundingTextInCodePoints(int, int)

github.com

  • InputMethodManager#dispatchKeyEventFromInputMethod(View, KeyEvent)

github.com

  • InputMethodSubtype#getLanguageTag()
  • InputMethodSubtype.InputMethodSubtypeBuilder#setLanguageTag(String)
  • SpellCheckerSubtype#getLanguageTag()

github.com

  • EditorInfo#locales()

github.com

  • LocaleList#describeContents()
  • LocaleList#writeToParcel(Parcel, int)
  • LocaleList#CREATOR

Windows 10 Insider Preview Build 14332 実験ノート: ブラウザと IME のプライベートモード連携

Windows 10 Preview ビルド 14328 でお試しいただける日本語 IME の変更点について – Windows & Devices 開発統括部

入力履歴の管理が便利になりました。人に見られたくない履歴が候補リストに表示されて困った経験はありませんか?
プライベートモード(仮称)を使えば、その間に蓄積された入力履歴はモードの終了時には削除され、その後意図しない場面で表示されてしまうことを防ぐことができます。
プライベートモード(仮称)はIME のアイコンをクリックして表示されるメニューから「プライベートモード」を選択することでオン・オフすることができます。Edge や Internet Explorer で InPrivate ブラウズ機能をお使いの場合は、本機能が自動的に オン・オフ されます。
プライベートモード(仮称)で蓄積された入力履歴はプライベートモードの間だけ有効となり、モードの終了時には削除されます。それまでにプライベートモード以外で蓄積されていた入力履歴のデータには影響ありません。

ブラウザと IME のプライベートモード連携は,自分にとっても時間があったらやれたらいいなぁと思っていた機能だったこともあって,週末を利用して少し調べてみました,がどうも時間切れっぽいのでメモだけでも残しておきます.想定読者はブラウザ開発者,IME 開発者,および IME API 開発者という感じですが,正直に言えば自分用のメモです.

この機能,名前に (仮称) が付いているようにまだまだフィードバックしだいで色々変わりうる段階と思われます.というわけでまだちょっと調べたりないのですが,実験したい人向けにメモでも残しておく次第です.

疑問点

  1. そもそも Internet Explorer / Edge と MS-IME はどのようなプロトコルでこの動作を行っているのか?
  2. サードパーティ製のブラウザと MS-IME の組み合わせで同じことができるか?
  3. Internet Explorer / Edge とサードパーティー製の IME で同じことができるか?

今回の調査結果

そもそも Internet Explorer / Edge と MS-IME はどのようなプロトコルでこの動作を行っているのか?

時間内に絞り込み切れませんでした.無念……

サードパーティ製のブラウザと MS-IME の組み合わせで同じことができるか?

上の結果が確定しなかったため確たる結論に至れず.残念……

Internet Explorer / Edge とサードパーティー製の IME で同じことができるか?

公開 API のみで,InPrivate モードの検出が可能であることまでは確認.少なくとも見かけ上は可能.

調べたこと

  • ITfTextInputProcessorEx::ActivateEx に指定される flags は,InPrivate かどうかで変化するか?
  • ITfThreadMgr::GetGlobalCompartmentITfCompartmentMgr::EnumCompartments の結果は InPrivate かどうかで変化するか?
  • ITfThreadMgr::QueryInterfaceITfCompartmentMgr::EnumCompartments の結果は InPrivate かどうかで変化するか?
  • ITfDocumentMgr::QueryInterfaceITfCompartmentMgr::EnumCompartments の結果は InPrivate かどうかで変化するか?
  • ITfContext::QueryInterfaceITfCompartmentMgr::EnumCompartments の結果は InPrivate かどうかで変化するか?
  • ITfContext::EnumProperties で列挙される ITfProperty に対応しそうなものはあるか?
  • Edge の設定する InputScope は InPrivate かどうかで変化するか?
  • InputScope の一種 IS_PRIVATE (== 61) で MS-IME は動作を変化させるか?
  • Windows 10 Anniversary SDK Preview Build 14332 の API ヘッダの差分および InPrivate と付いた API に関連しそうなものはあるか?

得られた知見

  • Build 14332 時点で,正式な InPrivate 連携 API が Text Services Framework (TSF) に存在するかどうかは疑わしい.InputScope を含め,TSF の API で観測可能な範囲内に InPrivate モードの有効無効を示唆するものは一切見つけられなかった.
  • Build 14332 時点で,InputScope の一種 IS_PRIVATE によって MS-IME をプライベートモードにすることはできなかった.
  • Internet Explorer であれば,ieframe.dll のエクスポート関数 IEIsInPrivateBrowsing が依然として利用可能.ただしこの API は Edge のプロセスには読み込まれない.
  • Windows 10 SDK では,ProcessInPrivateInfo という列挙値が processthreadsapi.h に追加されている (少なくとも 10.0.10586.0 SDK の時点で既に存在する).
typedef enum _PROCESS_INFORMATION_CLASS {
    ProcessMemoryPriority,
    ProcessMemoryExhaustionInfo,
    ProcessAppMemoryInfo,
    ProcessInPrivateInfo,
    ProcessInformationClassMax
} PROCESS_INFORMATION_CLASS;

Build 14332 時点で,以下のコードは Internet Explorer および Edge 共に InPrivate モードのみ true を返す.

bool is_process_in_private() {
  BYTE value = 0;
  if (!::GetProcessInformation(GetCurrentProcess(),
                               ProcessInPrivateInfo,
                               &value,
                               sizeof(value)) {
    return false;
  }
  return value != 0;
}

フィードバックアイディア

ちょっと裏付け不足でまだ未登録なのですが,概ね以下の点についてもうちょい調べてみないとなーと思っております.

  • プライベートモード連携に関しては,TSF の公開 API 内で完結させ,サードパーティー製ブラウザおよびサードパーティー製 IME との相互運用性に配慮して欲しい.ファーストパーティ製ブラウザおよびファーストパーティ製 IME の間のみで使われるプライベートプロトコルは好ましくない.
  • ProcessInPrivateInfo は,現状取得のみが可能なように見える.これに該当する特殊プロセスの作成に非公開 API が必要なのであれば,ファーストパーティ製ブラウザのみがそれを使用可能という状況は好ましくない.

関連事例

(同一プロセスで動く IME で使うことを前提とした) Accessibility API を利用したプライベートモード検出

書くいう自分も,2013 年末ごろには MSAA を使ってプライベートモードの検出ができるかどうか実験したりしていました,がチームを移ったあとのごたごたで結局プロダクションでは使われてなかったりします.その辺のコードがいまもここに.

mozc/src/win32/base/browser_info.cc BrowserInfo::IsInIncognitoMode

sufix は typo ですすみません.あとで直します……

Chromium Bug 311180: Chrome OS: Incognito window is not really incognito for IME users

Issue 311180 - chromium - Chrome OS: Incognito window is not really incognito for IME users - Monorail

Chromium OS での同様の機能リクエスト.長いこと放置されていて辛い感じです.

Chromium Bug 601951: Android keyboard suggestions leak information typed in incognito window

Issue 601951 - chromium - Android keyboard suggestions leak information typed in incognito window - Monorail

Android 版 Chromium での同様の機能リクエスト.

この件については SwiftKey のサポートページにも以下のように書かれていたり.

Can I turn off learning in incognito mode with SwiftKey Keyboard for Android? – SwiftKey Support

Unfortunately, the keyboard actually does not know that you are in this special mode in the Chrome app because that is not something Google passes on to other apps.

改定履歴

  • 2016-05-02: ProcessInPrivateInfo の追加時期について表現を変更.少なくとも 10.0.10586.0 SDK の時点で存在する.

My OSS Wish List

f:id:NyaRuRu:20150914052437p:plain

業務外にパッチを書いたりしている OSS プロジェクト,だいぶやりたいことが溜まっているので,優先順位をつける意味でもまとめてみた. 業務と基本無関係なので,代わりにやっていただくのは大歓迎.なので以下は Wish List ということにしておきたい.

OSS Mozc 関係

[Mozc] Windows 版向けのインストーラーをビルドできるようにする

2013 年に Windows 版のコードを概ねオープンソース化したときに,面倒で後回しにしたのがこの部分. プロダクト版の WiX スクリプトは実際公開済みではあるのだけど,自動アップデート関係の設定等もここで行っているため OSS 版のインストーラにそのまま転用できなくなっている. 技術的な障害は何もなく,OSS 版向きでない処理を取り除くだけ,なんだけどずるずるとはや 2 年半ぐらい.

やること.

  • OSS Mozc 向けに,余計なものを取り除いた WiX スクリプトを書く
  • ビルドに必要な WiX のインストール方法を決める.OSS Mozc のリポジトリにチェックインしてしまうか,choco install wixtoolset あたりを使う.

[Mozc] DirectWrite を使うと真っ白になる問題を再現させる

候補ウィンドウの DirectWrite で表示するというコードを実装したのがだいぶ前の 2013 年 12 月ごろ.先日こいつが安定版に取り込まれ,それなりの数のユーザーにリリースされたところ,文字が全く表示されなくなるという問題が各所で起こることが判明.急遽該当コードを無効化するという事態に. 報告を読む限り Windows 7 環境限定っぽいのですが手元で再現できていないので詳しいことはよくわからず.

やること.

  • 再現環境を入手する
  • Mozc の実装に問題があるかどうかを調査する.問題があれば修正する.
  • もし環境側の問題であれば,それを検出する方法があるか調査する.
  • DirectWrite を用いた表示を無効化する設定を設定ダイアログに追加するべきか検討する.必要があればその実装.

[Mozc] ソースコードのチェックアウトプロセスから Subversion 依存を取り除く

Mozc Issue #299 参照. 技術的な障害は何もなく,淡々と手を動かせば終わるはず.

[Mozc] 依存する外部リポジトリの管理を gclient から Git submodule に変更

Subversion 依存が取り除かれれば,あとは機械的に対応可能.

[Mozc] ibus-mozc の削除

Mozc Issue #287 及び Mozc Issue #194 参照. これは割と自分の都合でお願いして削除することを決めてもらった話なので,早いところ何とかしたい.

ibus-mozc に関しては主に 2 つの理由からサポートをやめたいと思っていた.ひとつはメンテナー不在という問題,もうひとつは互換性問題の多発による周辺プロジェクトの疲弊がある.

メンテナー不在に関しては単純で,自分の空き時間の使い道としては優先順位が高くない,というのが理由である. 自分が OSS Mozc のメンテナンスを引き受け始めたころには,既に Chromium OS は IBus の使用をやめており,ibus-mozc を業務でメンテナンスする人間はいなかった. 自分の場合 OSS 関係のプロジェクトに関しては基本的に業務外時間で面倒を見ていて,そのため土日にできる範囲で作業するというスタンスを取っている. 日本語入力については,Windows と Android であれば日常的に使っているという意味もあってまだ興味が持てるものの,Linux Desktop 上で日本語入力することは基本的にない. そのため,既知のバグを概ね直しきったところで手を引くべきだと考えていた. Mozc Issue #194 という Issue を立てメンテナーを社内外から募集してみたのが 2 年ほど前. 残念ながら特に引き取り手は現れなかったので,まだ作業する余裕があるうちにコードを削除してしまおうというのが現在の考えである. というものの実際ここ数ヶ月は時間がとれずに作業が進んでいなかったので,もう少し早く始めていれば良かったのかもしれない.

互換性問題に関しては,複雑だ. IBus 1.4 あたりから,IBus の様々な処理が非同期プロセス間呼び出しとして実装されているのだけど,自分が普段使っているアプリケーションを中心にこれが何度も互換性問題を引き起こしている.

自分の理解が正しければ,もともと Chromium OS でのパフォーマンスを稼ぐために寄せられたパッチ群がもとで ibus が非同期処理を多用し始めた,という歴史だったように思う.とすれば不幸としか言いようがない. とはいえ同僚が今もこの件で時間を消費していることを考えると,多くのユーザーには IBus 以外の実装に移って欲しいという気持ちもある.心境としては複雑ではあるのだけど.

やること.

  • 社内リポジトリで変更済みの ibus-mozc 関係の更新を全て OSS 化
  • ibus-mozc 関係のコードを全削除
  • fcitx-mozc 等の代替ソフトウェアに足りない機能があれば,可能な範囲でそちらへの協力.
  • IBus の非同期モードが問題を引き起こしている周辺プロジェクトへのできる限りの協力.

[Mozc] tsf-mozc の Windows 10 関係の互換性問題の調査

やること.

  • Windows 10 環境を作る
  • 色々と言われている問題が再現するか調査.再現するなら直す.

[Mozc] Windows 環境での縦書きサポート

ずっと言われているのでどこかで時間を取って何とかしたい.候補ウィンドウ側に関してはハッカソンで一回動くところまで作ったことがあって,2 日もあればそこまではできることは確認済み. 問題は変換エンジン側で,縦書き時にカーソルキーの意味が変わるという動作をきちんとプロトコルレベルで定義する必要がある.

やること.

  • 候補ウィンドウの縦書きを実装.テストも追加.
  • 縦書きと横書きでキーマップを入れ替えるためのプロトコルを定義する

[Mozc] Qt 5.x への移行

Mozc では依然として Qt 4.8.x 系を使っているのだけど,いくつかの理由から 5.x へのアップグレードを急ぐ必要がある.

  • Qt 4.8.x では Windows の HiDPI 環境をサポートできない.これはだいぶ深刻.
  • 様々な Linux ディストリビューションが Qt 4.x のサポートを打ち切りたがっている.

基本的には Qt のアップグレードでビルドが通らなくなるところをひたすら直していく以外にないのだけど,Qt にパッチを当てずに移行可能かはまだ未知数. また,ビルドが通っても特定ユーザー環境で問題が発生する可能性もあり,先は長い.

[Mozc] Linux および Android 向け Continuous Build / Test 環境の整備.

現在の OSS Mozc は,Windows と OS X に関してある程度の Continuous Build / Test 環境が有効化されているものの,Travis CI が長らく Linux 12.04 にとどまっているため Linux および Android に関しては有効化できていなかった. 最近 Travis CI が Docker をサポートを追加したらしく,そろそろ何とかなりそうな気がしている.

[Mozc] その他溜まっている OSS Mozc の更新

Mozc の開発は現在も社内リポジトリが master. OSS リポジトリの更新タイミングは,基本的に誰かの手が空いているときであって,ここ 1 年半ぐらいは自分が土日及び休暇の空き時間を利用して行っている. で,ここのところ時間がとりにくかったのもあって,OSS 化すべき更新が 7 ヶ月分ぐらい溜まっている.

やること.

  • 作業のさらなる自動化.
  • ビルド確認の手間を減らすべく CI の拡充.

gclient から git submodule に移行すれば,GitHub 界隈のエコシステムをもうちょっとうまいこと活用できるんではないかと期待.

GYP 関係

[GYP] Revert された 540e4b1e665 の再有効化

これは完全に巻き込まれ案件なのだけど,個人的に気になっていて何とかしたい.以下時系列.

  1. いくつかのビルドツールで問題を起こすことから,GYP は同一ビルドターゲット内に (ディレクトリだけ異なる) 同名ファイルが存在することを許さない.一方,社内外からこの制限が不便だという声があがっていた.
  2. 数年前,社内リポジトリにとりこまれたバージョンの GYP に,このファイル名チェックを回避するオプションを追加するローカルパッチが提案され,メンテナンスを担当していた自分はうかつにもこれを受け入れてしまう.以後,アップデートのたびにパッチを当てなおす必要がうまれメンテナンスコストが増大.
  3. 自分が中心となって,GYP upstream に同等のオプションを実装 GYP r1947
  4. 上記機能が実装された GYP を社内にインポート.いくつかのプロジェクトがそのオプションを有効にする.
  5. とある Chromium 開発者,この制限を完全に取り除くパッチを提案.受け入れられる.GYP r1993
  6. 上記機能が実装された GYP を社内にインポート.不要になったオプションは削除される.
  7. GYP r1993 が,Chromium のビルドで警告を引き起こすことが分かり revert される.GYP c0cf1f22eb

というわけで,最新の GYP をそのまま社内にインポートすると,いくつかのプロジェクトでビルドが壊れることが分かっている.何とかしたい. この問題,自分の理解では OS X の libtools の制限によるもので,XCode では技巧的な方法で回避済み.同じことを GYP の Ninja ジェネレータでも実装すれば済む話,なんだけど時間が取れずまだ何もできていない.

が,こうやって書いてみると,自分的には最新の GYP がインポートできれば十分なので,もう一回社内の該当プロジェクトにオプション追加すれば良いだけのような気もしてきた……

やること.

  • 自分で直すまたは代わりにやってくれる人をみつける.

Chromium 関係

[Chromium] Web MIDI の実装に Windows 10 の新 MIDI API が使えるか調査

Chromium Issue #512433 参照. Windows 10 には MIDI API が追加されていて,Windows 版 Chromium の Web MIDI がもっと使いやすくなる可能性がある.

やること.

  • 自分でやるまたは代わりにやってくれる人をみつける.
  • 自分でやる場合,まずは Windows 10 環境を作る.

[Chromium] その他 Chromium の Web MIDI サポートで担当者募集中バグの修正

2015 年 9 月 13 日現在 24 個の担当者募集中バグがある. しかしこれはちょっと分量的につらいかも.

やること.

  • 自分でやるまたは代わりにやってくれる人をみつける.
  • MIDI の meetup 等で協力者を募る.

[Chromium] Android Lollipop で追加された新 IME API のサポート

Chromium Issue #424866 参照. 去年 Android Lollipop に追加した CursorAnchorInfo API,Chromium でまだサポートされていないのでなんとかしたい. 実はパッチまでは既にあるのだけど,Chromium 特有のレビューコスト・パフォーマンスに関する敷居の高さに阻まれ中断中. これはひょっとしたら業務時間中にやりきる口実を見つけられる可能性もゼロではないのだけど,その時間を Android 本体に使ったほうがどう考えても合理的なので早いところ担当してくれる Chromium 関係者を見つけるべきだと思っている.

やること.

  • 代わりにやってくれる人をみつける.あるいは気合を入れて自分でやりきる.

Firefox 関係

[Firefox] Android Lollipop で追加された新 IME API のサポート

Mozilla Issue #1094729 参照. 去年 Android Lollipop に追加した CursorAnchorInfo API,Firefox でまだサポートされていないのでサポートされて欲しい. 新 IME API が自社製品でしか使われないというシナリオは避けたい.心の底から.それが Microsoft ウォッチャーとして Text Services Framework から得た教訓なのですな.

やること.

Docker 上に IPython Notebook 実行環境を作って DeepDream を動かす

DeepDream を動かしてみようと思ったら 9 割ぐらい IPython Notebook と必要なライブラリの環境整備だったのでそのまとめ.

  • 作業日
    • 2015 年 7 月 4 日
  • 作業前の筆者の状況及びそこから導かれる想定読者
    • IPython Notebook を使ったことがない.環境構築もしたことがない.
      • Mathematica は使ったことがあるかもしれない.
    • docker コマンドが動く Linux Desktop 環境を,実機,または VM 内 (Windows にインストールした VMWare 等) にもっている.
    • Deep Neural Network と呼ばれる技術について,特に知らないし,特に自分で手を動かして何かしたこもなく,業務でも当面使う予定はない.
    • インターネットからの数 GB 程度のデータダウンロードが可能である.
  • 目標
    • docker コマンドが動く環境ならどこでも DeepDream のチュートリアルを実行できるようになる.
  • 目標ではない
    • Deep Neural Network と呼ばれる技術について,何らかの知見を得る.

Deep Neural Network 素人であるところの筆者が理解できた範囲の話の背景

6 月中旬ごろ,不思議な画像を生み出す画像処理アルゴリズムが Google Brain Project に所属する研究者たちによって公表されました.

Inceptionism の背景ですが,たとえば 10 段から 30 段にもなるニューラルネットワークが存在したとき,各段の振る舞いが実際に何をやっているのかは自明な話ではありません.この疑問に対する実験の 1 つとして,特定の段のニューロンの特性を使って入力画像を強調してみたらどうなるか,というのがどうも Inceptionism の意義というか立ち位置のようです.

実際,ランダムノイズ画像に上記強調処理を行った結果何やら物体めいたものが見えてきたというのが,ブログの前半で述べられていることです.

So here’s one surprise: neural networks that were trained to discriminate between different kinds of images have quite a bit of the information needed to generate images too.

さて,異なる画像を識別するように訓練されたニューラルネットワークは,画像を生成するために必要な情報をある程度持っているのであれば,これを使って面白画像が生成できないかというのが後半パートです.

Inceptionism ブログ記事では,実際の画像強調処理の具体的なアルゴリズムは記述されていませんでしたが,7月1日になって,IPython Notebook というインタラクティブな Python 実行環境を使ってチュートリアル的に行う方法が公開されました. この GitHub リポジトリの名前が DeepDream です.

ちなみに DeepDream として新たに公開されたのは,数十行程度の反復処理アルゴリズムだけで,Inceptionism ブログ記事の画像を再現するために公開が必要だった本当にこの反復処理アルゴリズムだけであったことが分かります. DeepDream では,GoogLeNet という訓練済みニューラルネットワークを用いており,このデータは 2014 年 12 月に公開済みでした.

さて問題は,普段この種の処理と無縁な生活をしている人間にとって,まずこの IPython Notebook の実行環境を (DeepDream に必要なライブラリを揃えつつ) 整えるのが大変面倒だ,ということです. というわけで Docker の出番です.

なぜ Docker を使うのか

まず最初になぜ Docker を選んだのかについて書いておきます.筆者の気づいた範囲で,DeepDream の実行環境を整えるにあたって面倒なのは次のような点です:

  • Caffe というライブラリを正しくインストール・設定する必要がある
    • GoogLeNet のデータセットが含まれていなければならない
    • 特定 GPU / ドライバへの依存を避けるために,Caffe は CPU 処理モードになっていて欲しい
  • pip コマンドで複数の Python ライブラリをインストールする必要がある
  • IPython Notebook はブラウザをフロントエンドに使用するため,実行環境にはブラウザがインストールされている必要がある

これらの処理を,普段使用している Linux Desktop 環境を汚さず,かつ (なるべく) 再現可能な形で,Linux ディストリビューション間の非互換性に煩わされることなく行いたいというのが Docker を使用する目的です. Docker を用いることで,仮に IPython Notebook 実行環境を予期せず壊してしまっても,すぐに最初からやり直すことが可能です. また,いったんコンテナイメージを作ってしまえば,ホスト環境が変わってもすぐに同じ実験を行うことが可能です.

ほんと,Docker みたいなのが自分の学生時代にも欲しかったものです……

(Docker) ホスト Linux

以下では Ubuntu 14.04.1 64-bit 版を想定していますが,必要な環境は Docker コンテナ内に用意するため,ホスト環境に要求するのは以下の 2 点です.

  • Docker が動く
  • (表示用のブラウザを X11 経由でホスト画面に表示するため) GUI ログイン可能である

Docker のセットアップについては詳しく述べませんので,Supported installation - Docker から辿ってインストールしてみてください.

なお,以下では sudo なしで docker コマンドが実行可能なように設定されていることを前提に書かれています.その設定を望まない場合は各自 sudo を付けて読み替えてください.

Docker コンテナのダウンロードとインストール

必要なコンテナは DockerHub にアップロード済みです.

docker pull nyaruru/deepdream-ipython-notebook

セキュリティが気になる人・自分で再現を試みたい人は,deepdream-ipython-notebook/dockerfile/ で Dockerfile を確認することができます.

何か問題・改善点がありましたら GitHub 上のNyaRuRu/deepdream-ipython-notebook までお知らせください. また,今回は github.com/visionai/clouddream をベースイメージに使わせていただいており,Caffe のセットアップ等大部分の処理はそちらで行われています.

Docker コンテナの実行

まず,コンテナ内からホストへの X11 接続を許可しておきます.

xhost +local:

DeepDream を一通り実行するだけであれば,以下のように docker コマンドを実行するだけで十分です.

docker run -i -t --rm -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix:ro nyaruru/deepdream-ipython-notebook

上手くいけば,コンテナ内で Firefox が起動され,そのウィンドウがホスト GUI 環境に表示されるはずです. 終了するには,ターミナル上で Ctrl-C を実行すれば OK です.

docker コンテナ起動時に --rm オプションを付けていることに注意してください.この結果,コンテナ終了時に作業内容は全て失われますが,もう一度実行しなおすことで確実に動く状態からやり直すことが可能です.

DeepDream IPython Notebook チュートリアルの実行

Docker コンテナを起動すると,すでに IPython Notebook が起動しているはずです.DeepDream のディレクトリが表示されているはずですので,そこから dream.ipynb を選択します.

f:id:NyaRuRu:20150705091726p:plain

IPython Notebook が表示されます.Mathematica 経験者であれば,あの Notebook みたいなもの,と思えばとっつきやすいかも.

上の方に ▸ ボタンがありますので,それを押していくとチュートリアルの内容が上から順に実行されてきます.

f:id:NyaRuRu:20150705091711p:plain

以下の箇所を実行することで,チュートリアルのサンプル設定での DeepDream が画像を強調処理していく様子をリアルタイムに見ていくことができます.

_=deepdream(net, img)

デフォルトのパラメータは,関数定義に書いてあるとおり以下のようになります.

  • ter_n=10
  • octave_n=4
  • octave_scale=1.4
  • end='inception_4c/output'

さらに DeepDream を楽しむために

パラメータを変える

こちらについても詳細についてはチュートリアルに書かれています.たとえば,

net.blobs.keys()

を実行することでニューロン層のリストが表示されます.強調処理に使う層を inception_3b/5x5_reduce に変更してみるというのが

_=deepdream(net, img, end='inception_3b/5x5_reduce')

の行っていることです.

ホスト環境のディレクトリを Docker コンテナ内に見せる

ここまで来ると,実際に色々な画像で実験してみたくなります. Docker のホストとコンテナでファイルをやり取りする方法はいくつかあるかと思いますが,ここでは -v オプションでホストの特定ディレクトリをコンテナ内に公開することにします.

docker run -i -t --rm -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix:ro -v $HOME/Pictures:/opt/pictures nyaruru/deepdream-ipython-notebook

これで,ホスト環境の $HOME/Pictures がコンテナ内の /opt/pictures と共有されます. たとえば foobar.jpg を $HOME/Pictures に置いたとします.IPython Notebook のデータを読み込んでいる次の行を探してください.

img = np.float32(PIL.Image.open('sky1024px.jpg'))
showarray(img)

これを,

img = np.float32(PIL.Image.open('/opt/pictures/foobar.jpg'))
showarray(img)

に書き換え,フォーカスを合わせた状態で実行 ▸ ボタンを押します. これで画像が読み込めれば成功です. あとは改めて deepdream を行っている行を実行します. 結果は,ブラウザの「画像に名前を付けて保存」で保存してもいいでしょうし,Python で

frame=deepdream(net, img)
PIL.Image.fromarray(np.uint8(frame)).save("/opt/pictures/foobare_result.jpb")

と書いても良いでしょう.

その他の実装

github.com/jcjohnson/cnn-vis にて,独立実装による Inceptionism の再現実験が試みられています.こちらでは,MIT Places データセットに対して訓練された GoogLeNet のデータを用いることで,建物に反応する (と思われる) ニューロン層を使った実験も行われています.

Future Work

今回の Dockerfile はあくまで IPython Notebook チュートリアルの実行を目標としているので,DeepDream の処理を動画に対して行ったり,GPU を使って高速化したりはまた別リポジトリで行いたいと思っています.

そのうえで,いくつか気になっている点としては,

  • Caffe が CPU モードで動くのはいいんだけど,CPU 1 コアしか使っていない気がする.
  • ホスト環境のファイル受け渡しが若干面倒なので何とかしたい.
  • ベースにしている Docker イメージから引き継いだ特性として,コンテナ内のユーザーが root な のが若干気にいらない.出来れば root でブラウザを動かしたくない.

あたりでしょうか.この辺はまた時間が取れたら見ていきたいところです,

Docker とか Caffe とか普段触ることがないので,詳しい方ぜひおすすめテクニック等教えてください.

2014 年やったこと - Mozc 編

いつもどおりコードが公開されている範囲で.

実は 2013 年にやっていたこと

OSS Mozc のアップデートタイミングの関係上,2013年やったこと - Mozc 編 - NyaRuRuが地球にいたころ に載っていなかった内容です.概ね 2013 年末休暇シーズンに余暇としてやっていました.

Windows 版での候補ウィンドウのカラー絵文字対応

Mozc チームを離れる直前,休暇シーズンということもあって久しぶりに自分の書きたいコードでも書くかと言うことでやってみたのがこのカラー絵文字対応です.
f:id:NyaRuRu:20150211174200p:plain
作業のほとんどは mozc_renderer (独立したプロセスとして動く,候補ウィンドウ表示用プロセス) 内の文字表示処理を DirectWrite に対応させることでした.カラー絵文字専用に追加したコードは数行です.
r192 にてリリース.
変更の大半は renderer/win32/text_renderer.cc にまとまっています.

候補ウィンドウの DirectWrite 対応

思い起こせば 2009 年,Mozc の候補ウィンドウ表示を別プロセス化したころから DirectWrite を使うというアイディア自体はありました.緊急性と重要性の高いタスクは他にいくらでもあったことからこのアイディアは長らく放置されていたのですが,カラー絵文字表示の副産物として 4 年越しの実現となります.Windows 8 以前の OS でも,高 DPI 環境ではそれなりにメリットがあるかもしれません.
r192 にてリリース.

ITfFnReconversion::GetReconversion 対応

tsf-mozc が ITfFnReconversion::GetReconversion をサポートしていなかったので実装してみた,というものです.ITfFnReconversion::GetReconversion を使用する一部アプリケーション以外でユーザーの目に見える変化はありません.

一番わかりやすいのは Microsoft Word で,文字を選択したあと右クリックすると,コンテキストメニュー内に再変換候補が表示されるようになります.
f:id:NyaRuRu:20150211163902p:plain:h600
Mozc 内部のプロトコル的に再変換は対応済みだったので,あとは単純に実装するだけという内容でした.

2014 年にやったこと

2013 年 12 月末をもって所属的には Mozc チームを離れたこともあり,休日・休暇に作業できる内容であることというのを特に重視していた 2014 年でした.そんなわけで注目してみたのが,OSS 版周りの自主オープンソース活動でした.OSS としてみた Mozc プロジェクトは,特に対外的な締め切りもなく,本当に時間的に余裕があるときだけ作業するのに適しています.また,OSS 化後のソースコードについては個人の PC 環境で作業できるので,喫茶店や旅行先でコードを見たり変更したりしながらあれこれ試せるのも嬉しいポイントでした.

OSS Mozc を選んだもうひとつの理由としては,最近モバイル OS 向けに OSS Mozc をベースにした IME を作っていただけるケースが増えていて,少し応援してみたくなったというのもあります.数字的にも合計数百万インストールという規模で,OSS としてはまずまずの成果.間接的にモバイル IME 開発の活発化に貢献できるのであれば,休日の使い方としてはまずまず Mobile First かなと思った次第.

さて,2014 年のテーマとして個人的に興味があったのは,社内リポジトリから OSS リポジトリへに変更を移動するとき,そのコミット粒度を小さくするのは可能か,というものでした.

これまで OSS Mozc といえば,いわゆる "code drop" 方式でリリースされていました.開発自体は社内のリポジトリで行い,しかるべきタイミングに (たいていはプロプライタリな製品として社外にリリースされたあと),開発されたソースコードを OSS として公開する,というものです.このとき問題になるのがコミットログで,OSS Mozc は様々な理由から変更は 1 つの SVN コミットにまとめられ,数千行から多いときは数万行の変更になることもありました.この巨大なコミットに含まれる変更点全てを把握するは開発者自身にとっても難しく,コミットログやリリースノートを書くときに苦労したのを憶えています.

そこで試してみることにしたのが,OSS 化して問題ない変更については,確認の上,個別のコミットとして OSS リポジトリに適宜反映させていくというものでした.作業自体は休日等の空き時間を利用して行っています.2014年5月24日から12月25日の間に,222 個のコミットを行いました (うち 81 コミットは 2014年12月6日から12月31日までの間,有給消化の年末休暇中に行っています).

残念ながら r467 だけは従来通りの巨大コミットになってしまいましたが,これは Android 向け新機能が多数含まれるため分けきれなかったというのが理由です.それ以前の 218 個のコミットはコミットメッセージと変更が (概ね) 対応した通常のコミットに見えるようになっています.変更の粒度を下げたことで,最後の巨大コミットを待つことなく,27 個のバグを閉じることができました.

この作業,個人的に何が新鮮に感じたかというと,それがある種の <コミット後レビュー> だったことです (このあたりの話,コードレビューいろいろ - steps to phantasien を先に読むことをおすすめ).会社に入って以来,<コミット前レビュー> は 6 年ぐらいやってきたものの,<コミット後レビュー> を継続的にやったのはこれが初めてでした.
感想 1.既にコミット済みのコードに対するレビューなので,気になるところを修正するとそれが別コミットになってしまう.
感想 2.ある程度の期間のコミットの差分をまとめて見たことで,無駄なコードが増えたのに気付きやすくなる.以下典型的なケース.

  1. ある問題に,A という方法で対処する
  2. A という方法では完全に問題が解決できないことが分かり B という方法に切り替える

複数のコミットが数日から数週間の間に加えられていたとき.あとで全体の差分を見てみると不要な変更が残ってしまっているケースを数件発見.これは確かに <コミット前レビュー> で見つけにくいパターンかも.

まあそんなわけで新鮮でもあった細切れ OSS 化作業ですが,休日の時間を結構吸い取られる作業であったのも確かで,今後どうするかはまだ決めていません.

一方,OSS 版リポジトリを活用し,実際にバグを直したり技術的負債を返済したりといったことも行っていました.以下は,そんな休日自主オープンソース活動についてです.

TF_E_NOLAYOUT 問題

Mozilla Japan の中野さんから報告していただいた問題です.調査の結果,以下のような OS 側の問題であることが分かりました.

  1. TSF で書かれた IME (TIP) が ITfContextView::GetTextExt() を呼び出す.
  2. TSF ランタイムがアプリケーションの実装する ITextStoreACP::GetTextExt() を呼び出す
  3. アプリケーションが ITextStoreACP::GetTextExt() に対して TS_E_NOLAYOUT を返す
  4. TSF ランタイムが戻り値を E_FAIL に書き換える
  5. TSF で書かれた IME (TIP) が ITfContextView::GetTextExt() の結果として E_FAIL を受け取る

開発元のサポートに相談したところ,ITfContextView::GetACPFromPoint() と ITfContextView:: GetRangeFromPoint() にはこの問題がないことが分かり,TS_E_NOLAYOUT が返されているかどうかの判定をこれらの API で行うという回避策をおすすめされました.その方針で tsf-mozc 側にて対処してみた,というのが一連の流れとなります.
r192 にてリリース.コード的には以下となります.
https://code.google.com/p/mozc/source/browse/trunk/src/win32/tip/tip_range_util.cc?r=192#231

HRESULT TipRangeUtil::GetTextExt(ITfContextView *context_view,
                                 TfEditCookie read_cookie,
                                 ITfRange *range,
                                 RECT *rect,
                                 bool *clipped) {
  BOOL clipped_result = FALSE;
  HRESULT hr = context_view->GetTextExt(
      read_cookie, range, rect, &clipped_result);
  if (clipped != nullptr) {
    *clipped = (clipped_result != FALSE);
  }
  // Due to a bug of TSF subsystem, as of Windows 8.1 and prior,
  // ITfContextView::GetTextExt never returns TF_E_NOLAYOUT even when an
  // application returns TS_E_NOLAYOUT in ITextStoreACP::GetTextExt. Since this
  // bug is specific to ITfContextView::GetTextExt, a possible workaround is
  // to also see the returned value of ITfContextView::GetRangeFromPoint.
  // If the attached application implements ITextStoreACP::GetACPFromPoint
  // consistently with ITfContextView::GetTextExt,, we can suspect if E_FAIL
  // returned from ITfContextView::GetTextExt comes from TF_E_NOLAYOUT.
  if (hr == E_FAIL) {
    // Depending on the internal design of an application, the implementation of
    // ITextStoreACP::GetACPFromPoint can easily be computationally expensive.
    // This is why we should carefully choose parameters passed to
    // ITfContextView::GetRangeFromPoint here.
    const POINT dummy_point = {
       numeric_limits<LONG>::min(), numeric_limits<LONG>::min()
    };
    CComPtr<ITfRange> dummy_range;
    const HRESULT next_hr = context_view->GetRangeFromPoint(
         read_cookie, &dummy_point, 0, &dummy_range);
    if (next_hr == TF_E_NOLAYOUT) {
      hr = TF_E_NOLAYOUT;
    }
  }
  return hr;
}

ちなみにこの問題,現在ベータテスト中の Windows 10 では OS 側で無事修正されている模様です.


TSF を有効にした Firefox がフォーカスを失ったとき候補ウィンドウが表示されたままになる

tsf-mozc と TSF-aware Firefox の組み合わせで,文字入力中に別アプリケーションにフォーカスを移すと,Mozc の候補ウィンドウが表示されたままになるという問題です.
tsf-mozc 側で ITfThreadFocusSink::OnSetThreadFocus() と ITfThreadFocusSink::OnKillThreadFocus() を処理していなかったのが問題でした.
r192 にてリリース.

Issue 206: 特定ロケールの Windows 環境で prediction/dictionary_predictor.cc をビルドすると C2065 エラーで失敗する

https://code.google.com/p/mozc/issues/detail?id=206
Mozcの C++ で書かれたソースコードは基本的に BOM なし UTF-8 で統一されているのですが,過去何度もVisual C++ が C2065 エラーでコンパイルに失敗するというトラブルに見舞われて来ました.そこで発明された対処法が,とりあえず non-ASCII 文字は "" で囲む,というものです.
OSS Mozc の方に C2065 の報告が来たわけですが,今回も "" で囲って対処となりました.
r192 にてリリース.

Issue 215: mozc::Encryptor 実装のポータブル化

https://code.google.com/p/mozc/issues/detail?id=215
mozc::Encryptor というクラスの実装から,プラットフォーム別の処理を極力取り除いたというものです.
Mozc は,確定履歴などいくつかのローカルファイルを保存時に AES256 CBC モードで暗号化します (Design Doc 参照)
当時この実装はプラットフォームごとに異なっていて, Mac 版, NaCl 版,および Linux デスクトップ版 Mozc では OpenSSL が,Windows 版では Win32 Crypto APIが,そしてAndroid 版では javax.crypto.Cipher クラスが使われていました.
さて,話は 2014 年 2月22日にさかのぼります.当日 Mozilla Japan 東京オフィスで開かれていた FxOS コードリーディングミートアップ #4 にふらりと参加した筆者は,Mozc を Emscripten でビルドできないものかとあれこれ試し始めました.Mozc のビルドは概ね「近寄るべきではない程度に大変」の部類に属しますが,そうはいってもクロスコンパイルの必要な Android 対応と NaCl 対応は既にできているわけです.数時間もあれば最初の一歩ぐらいはなんとかなるだろうと軽い気持ちで始めたものの,結果的に最初の一歩すら踏み出せずに敗退することとなります.

このとき最初の一歩として立ちふさがったのが,この OpenSSL 依存でした.以下に,当時私が参考にしようとしていた nacl-mozc の OpenSSL 依存部分を示します.
https://code.google.com/p/mozc/source/browse/trunk/src/base/base.gyp?r=185#508

    ['target_platform=="NaCl"', {
      'targets': [
        {
          'target_name': 'pepper_file_system_mock',
          'type': 'static_library',
          'sources': [
            'pepper_file_system_mock.cc',
          ],
          'dependencies': [
            'base.gyp:base',
          ],
        },
        {
          'target_name': 'openssl_crypto_aes',
          'type': 'static_library',
          'variables': {
            'openssl_dir%': '<(DEPTH)/third_party/openssl/openssl',
          },
          'direct_dependent_settings': {
            'defines': [
              'HAVE_OPENSSL=1',
            ],
            'include_dirs': [
              '<(gen_out_dir)/openssl/include',
              '<(openssl_dir)/include',
            ],
          },
          'cflags': [
            # Suppresses warnings in third_party codes.
            '-Wno-unused-value',
          ],
          'include_dirs': [
            '<(gen_out_dir)/openssl/include',
            '<(openssl_dir)',
            '<(openssl_dir)/crypto',
            '<(openssl_dir)/include',
          ],
          'hard_dependency': 1,
          'sources': [
            # The following files are the minumum set for 'encryptor.cc'
            '<(openssl_dir)/crypto/aes/aes_cbc.c',
            '<(openssl_dir)/crypto/aes/aes_core.c',
            '<(openssl_dir)/crypto/aes/aes_misc.c',
            '<(openssl_dir)/crypto/mem_clr.c',
            '<(openssl_dir)/crypto/modes/cbc128.c',
            '<(openssl_dir)/crypto/sha/sha1dgst.c',
            '<(openssl_dir)/crypto/sha/sha1_one.c',
            '<(openssl_dir)/crypto/sha/sha256.c',
            '<(openssl_dir)/crypto/sha/sha512.c',
            '<(openssl_dir)/crypto/sha/sha_dgst.c',
            '<(openssl_dir)/crypto/sha/sha_one.c',
          ],
          'dependencies': [
            'openssl_config',
          ],
          'export_dependent_settings': [
            'openssl_config',
          ],
        },
        {
          'target_name': 'openssl_config',
          'type': 'none',
          'actions': [
            {
              'action_name': 'openssl_config',
              'inputs': [
                'openssl_config.sh',
              ],
              'outputs': [
                '<(gen_out_dir)/openssl/include/openssl/opensslconf.h',
              ],
              'action': [
                'bash', 'openssl_config.sh', '<(gen_out_dir)', '$(abspath ./)',
              ],
            },
          ],
        },
      ]},
    ],
  ],
}

ここでやっているのは,OpenSSL のソースコードがダウンロード済みと仮定して,configure スクリプトを走らせ,ターゲットの toolchain でビルドする,というものです.asm.js 版 Mozc を作るにあたって,これとほぼ同様の作業を行う必要がありました.一方 Android 版も, JNI を使って javax.crypto.Cipher クラスの一部を C++ に公開するという,処理的にもビルド設定的にも非常に複雑な実装になっていることが分かりました.
実は Mozc がローカルにファイルを保存するときの暗号化処理は,"カジュアルな" 情報漏洩対策という何とも微妙な目的で導入されたという過去があります.デスクトップ版のみであった最初期は,この機能による複雑さの増加もまだ許容できたのかもしれませんが, Android 版や NaCl 版開発時に「がんばって移植すべき機能」のひとつになってしまったのは不幸でした.これらの環境では,システムの OpenSSL を単純に呼び出すといった方法が使えなかったため,ビルド時に OpenSSL 自体をビルドしたり,JNI コードを追加したりといった方法で対処されることとなりました.このがんばりは,予定されたスケジュールまでに製品をリリースするという点では有効でしたが,残ることになった複雑さはまさに技術的負債でした.
さて,よくよく処理内容を調べてみると,SHA1 と AES256 CBC の計算で,きちんと認証を受けた OS 固有実装を利用しようとした結果,物事が複雑化しまっていることがわかります.しかしながら,もともと "カジュアルな" 情報漏洩対策でしかない暗号化処理で,FIPS 認定にこだわるというのも変な話です.だったら自前で SHA1 と AES256 CBC を実装してしまえば,大幅にビルドが単純になるだろう,と始めてみたのがこの Issue 215 となります.
基本実装は r192 に含まれていますが,この時点では旧実装も残されていました.その後,r208r209r497 として旧実装は削除されています.
規格を読みながら有名アルゴリズムを実装するのは楽しい体験でした.計算結果の比較のために見ていた Chromium の SHA1 実装にバグを見つけるというおまけもありました (Chromium Issue 348333) .4 月に OpenSSL の Heartbleed が盛り上がったのも,面白い偶然の一致です.
まとめると,以下のようなメリットがあったものと考えています.

  • 実行時に依存するライブラリが少なくなる
  • ビルドが単純化される
  • プラットフォーム共通コードが増える
  • (OSS Mozc に GPL コードを組み合わせたい人にとっての),潜在的なライセンス非互換問題のリスクが減る

Issue 211: 2 段階ビルドを廃止

https://code.google.com/p/mozc/issues/detail?id=211
従来,たとえば Android 版 Mozc の APK をビルドするときには,以下のようにコマンドを 3 回実行する必要がありました.

python build_mozc.py gyp --target_platform=Android
python build_mozc.py build_tools -c Release
python build_mozc.py build -c Debug_Android android/android.gyp:apk

これが,以下のようにコマンド 2 回でビルドできるようになった,というものです.

python build_mozc.py gyp --target_platform=Android
python build_mozc.py build -c Debug android/android.gyp:apk

Mozc のビルドが build_tools というコマンドと build の 2 段階に分けられたのは,実に 2010 年,Mozc を OSS 化するにあたってビルドシステムを GYP に移行するさなかのことでした.当初の動機は,時間のかかるバイナリ辞書圧縮処理を開発中何度も実行したくないという理由でした.状況が変わったのは,Android 版や NaCl 版の必要がでてきてからです.これらのプラットフォームはクロスコンパイルを必要とし,そのための基盤として 2 段階ビルドが「利用」されました.ビルドの複雑さは一時制御不能な規模までふくれあがり,一見正しく見える変更が,なぜか Android ビルドだけを壊すという問題が多発しました.記憶が確かなら 2011 年から 2012 年頃が最も酷かったように思います.
このひどい状況は,2013 年末,Android 版ビルドが GYP の Android.mk ジェネレータから一般的な make ジェネレータに切り替えられることで大幅に改善され,今回 FxOS コードリーディングミートアップ #4 に参加したのを切っ掛けに取り払ってみた感じです.
ちなみに 2010 年ごろ懸念されたビルド時間の問題も,今時のワークステーションで作業する分には概ね問題になりません.
基本実装は r192 にて.その後,r211およびr214にて有効化されました.
2 段階ビルドに使用されたコードはその後 r371 および r507 として完全に削除されます.

Issue 224: enable_gtk_renderer=0 指定時にビルドが失敗する

https://code.google.com/p/mozc/issues/detail?id=224
Linux 版 Mozc で, mozc_renderer を無効にした状態でビルドしようとするとコンパイルエラーになるという問題.単純なミスだったので即修正となりました.
r215 にて修正.

Unicode 絵文字変換を NaCl 環境および Linux デスクトップ環境でもデフォルト有効化

今までは特定環境のみ Unicode 絵文字をデフォルト有効化というロジックだったのですが,コードがだいぶ複雑になってしまっていたので,逆に特定環境のみデフォルト無効化というロジックに変更しました.このときの簡略化の一環で, NaCl 環境および Linux デスクトップ環境でも Unicode 絵文字変換はデフォルト有効化となりました.
r229r230r231 の 3 つのコミットにより有効化.

Issue 226: ibus-mozc の周辺文字列取得コードが正しく動作していなかった

https://code.google.com/p/mozc/issues/detail?id=226
ibus-mozc の周辺文字列の取得コードにバグがあって,UTF-8 文字列のバイトオフセットを使って部分文字列を切り出すべきところに,間違って文字数を使って部分文字列を切り出していたというもの.結果的に,ibus-mozc で周辺文字列を考慮した変換が意図しない形で動作してしまっていました.
r232 および r233 にて修正.

Issue 227: IBus 1.5 環境下で確定取り消しが動作しない

https://code.google.com/p/mozc/issues/detail?id=227
Issue 201 修正の副作用で,ibus-mozc を IBus 1.5 環境下で使用しているとき,確定取り消し (MS-IME キー設定で言うところの Ctrl+BS) が動作しなくなっていた,という問題の修正です.
r234 にて修正.

Issue 233: ibus-mozc 使用時,MozcTool に対応するテキストラベルが存在しない

https://code.google.com/p/mozc/issues/detail?id=233
MozcTool に対応するテキストラベルが存在せず,空白のエントリが表示されるというものです. IBus 1.4 環境までは言語バー的な UI が標準で表示されていたため問題になりませんでしたが,IBus 1.5 環境で言語バー的な UI が廃止されたためこの問題が表面化する可能性が高くなりました.
f:id:NyaRuRu:20150211174258p:plain
ちょうど社内のワークステーションが Ubuntu 14.04 に切り替わるタイミングだったので,同僚から言われる前に IBus 1.5 関係のやり残しタスクの一環で直してみた感じです.
r251 にて修正.

Issue 236: Windows 8.1 update1 環境で Explorer がクラッシュすることがある

https://code.google.com/p/mozc/issues/detail?id=236
安定性の確認もかねて,家でときどき tsf-mozc を使っていたところ,Windows 8.1 update1 環境で Explorer がクラッシュする確実な手順を発見したので,デバッグ・対処してみたというものです.再現率 100% なので, Visual Studio で簡単にデバッグできるかと思いきや,windbg で WinRT の内部まで潜る必要があって大変でした.Ntdebugging Blog の記事を見つけられなければデバッグは無理だったように思います.WinRT で言語共通の実行時例外がどのように実装されているか興味がある人には,この記事面白いかもしれません.
r252 にて対処.

Issue 222: Linux デスクトップ, NaCl, および Android ビルドで Ninja を使用する

https://code.google.com/p/mozc/issues/detail?id=222
2012年他にやったこと - Mozc 編 - NyaRuRuが地球にいたころ で書いたように,2012 年末から Windows 版 Mozc は Ninja でビルドされるようになっていたのですが,これを Linux デスクトップ, NaCl, および Android ビルドでも使うようにしたというものです.
やってみて分かったのですが,どうも GYP の Ninja ジェネレータとmake_global_settings という GYP の設定の組み合わせで Android 向けクロスビルドを行ったのは Mozc が最初らしく,結局自分で GYP 側も直す必要がありました.以下のようにせっせと GYP 側にパッチを送っていたりします.たまに役立つ GYP コミット権.

Mozc 側では,r265, r266, r267, r268 の 4 回に分けて有効化されました.

Issue 240: Linux デスクトップ, NaCl, および Android ビルド環境構築のための Dockerfile の提供開始

https://code.google.com/p/mozc/issues/detail?id=240
OSS Mozc の公式ビルド環境を Docker で定義してみよう,と Docker の勉強がてらやってみました.特に Android 版 Mozcのビルド環境を Docker 化できたおかげで,その後のビルドの検証がだいぶ楽になりました.
r271 にて Ubuntu 12.04 ベースのコンテナを提供開始.
r330 にて Ubuntu 14.04 ベースのコンテナの試験提供を開始しました.
なお, r486 で Ubuntu 12.04 ベースのコンテナは役目を終了し,以後は Ubuntu 14.04 に一本化しています.

Ninja の Console Pool 機能を有効化

https://code.google.com/p/mozc/source/detail?r=314
Ninja 1.5 の新機能に Console Pool というのがあって,これが Mozc の Android ビルドのときに使えるかなと試してみたものです.
r314 にて有効化.

Issue 246: Linux, NaCl, および Android 環境で,'Set input mode to X' コマンドが直接入力モードで動作しない

https://code.google.com/p/mozc/issues/detail?id=246
https://bugzilla.redhat.com/show_bug.cgi?id=1119048
Red Hat Bugzilla で ibus-mozc 向けに報告されていた問題で,キーマップエディタで直接入力モード時に「ひらがなモードに切り替え」というコマンドを設定したとしても,実際には動作しないという問題です.
Windows では問題なく動作するのですが,動作するプラットフォームが実はホワイトリスト方式になっていて,Windows 以外では意図的に受け付けないようになっていました.実際には Mac 以外で許可することに問題はないので,そのように対処した,というものです.
r315 にて修正.

Issue 244: Visual C++ 2013 対応

https://code.google.com/p/mozc/issues/detail?id=244
OSS Mozc を Visual Studio 2013 でビルドできるようにしてみた,というものです.
r311 以降で対応.
ちなみにこの後 Visual Studio Community がリリースされたおかげで,無償版の Visual Studio を利用して OSS Mozc Windows 版をビルドできるようになっています.
なお, r509 以降では Visual Studio 2013 にサポートするコンパイラを一本化しています.

About ダイアログ上の Copyright 表記の更新

2014 年も半分以上過ぎているのに,About Dialog 上の Copyright 表記が 2013 年のままだったので直したというものです.
r324 にて修正.

アプリケーションマニフェストに Windows 10 対応宣言を追加

Windows 版の実行ファイル全てに,Windows 10 対応という GUID をセットするというものです.
r344 にて対処.

UserHistoryStorage の 2038 年問題

Mozc の入力履歴ファイル内で使用されるタイムスタンプが 2038 年以降不正な値になるという問題です.たまたまコードを眺めていて発見したので直してみました.修正自体はほぼ Protocol Buffers で定義されたデータ型の型を uint32 から uint64 に変更するだけといえます.Protocol Buffers の特徴として,この変更に関しては互換性と保存されるデータサイズ的に影響を及ぼさないと分かっていたため,割と気軽に直すことができました.
r346 にて対処.

SafeStrToDouble/SafeStrToFloat を Android 環境で再度有効化

https://code.google.com/p/mozc/source/detail?r=370
特定バージョンの Android NDK でビルドすると,strtod に 16 進数表記の文字列を渡したときの挙動に非互換の変更があったため,Android 環境で一部のテストを無効化されていたのですが,Android NDK r10c でこの問題が修正されたためテストを再度有効化したというものです.
r370 にて対処.

Issue 249: Windows 8 以降の環境でスタート画面に統合された IME 候補表示が正しく動作しない

https://code.google.com/p/mozc/issues/detail?id=249
これも家で tsf-mozc を使っていて発見した問題です.当時実験的に tsf-mozc でITfFnSearchCandidateProvider をサポートしていたのですが,どうも実装が不完全だったため逆に問題のある挙動になってしまっていたらしく,該当機能を無効化することで対処となりました.
r329 にて修正.

Issue 248: Android 環境に物理キーボードを接続し,デッドキーを使用するとクラッシュする

https://code.google.com/p/mozc/issues/detail?id=248
OSS Mozc の Issue Tracker に寄せられた報告です.Android 環境での Mozc のデッドキーイベントの扱いに問題があり,特定キーボードレイアウトで特定キーシーケンスを入力すると必ず Mozc がクラッシュしてしまっていました.
r463 にて修正.

Issue 255: ibus-mozc を XIM プロトコルで使用すると,フォーカスロスト時にプリエディットがクリアされない

https://code.google.com/p/mozc/issues/detail?id=255
OSS Mozc の Issue Tracker に寄せられた報告です.元々は日本 IBM の人から SUSE Linux に報告が寄せられ,さらに openSUSE の Issue Tracker を経ての報告だそうです.
ibus-mozc を XIM プロトコルで使用し,gedit 上でプリエディットが残っている状態で,別アプリケーションにフォーカスを移し,再度 gedit にフォーカスを戻すと,以前のプリエディットは既に確定されているのに,ibus-mozc は以前のプリエディットの内容が残ったままであるかのように振る舞う,という問題でした.
調べてみると Mozc 1.2.809.102 より前のバージョンではこのケースでも正しく動いていたものの, Chromium OS (当時はまだ IBus を使用していた)でのフォーカス周りのバグに対処すべくあれこれ変更するうちに現在の挙動になってしまったということが分かりました.
r386 にて修正.

2015 年の話とか

今年は休日の空き時間があまり読めないので,分からないというのが正直なところです.少なくとも OSS 化作業については,Android 5.0 向けの OSS 化が一息ついたので,多少は時間が稼げたんではなかろうかと.

もし何かひとつ優先するすれば,今年こそ不要コードの削除に注力したいかなと.Windows XP 対応のために入れていたコードや,もうあまり意味の無さそうなパフォーマンスチューニング,古いコンパイラのために必要だったコードなどなど.書いた本人ならすぐに判断がつくものの,もし仮に誰かが引き継いでしまうと消していいかの判断がつかなくなりそうなものは今のうちに消してしまいたいと思う次第.

次点としては,Windows 版向けにずっとやりたかったことあれこれ,たとえば OSS 版のインストーラーを動くようにするとか per-monitor DPI-aware 対応とか.とはいえ,すべては今年の休日具合次第なんですけどね.あとどんなミートアップに参加するかも.

2014 年やったこと - Chromium 編

Chromium 関係,コードはあまり書いていない一年でした.数字としてはコミット 17 回,コードレビュー 13 回となります.一方 Issue Tracker 上での活動はまあそれなりにで,Issue 登録 19 回,コメントを書き込んだり状態を変更したりといった Issue の数は 173 件となりました.

自分でやったこと

Windows 版 Chromium の Aura 移行完了後不要になった IME 関連コードの削除

2014 年 1 月から 2 月にかけて,Aura + Ash 移行に伴い不要になった IME 関連コードの削除をやっていました.既にチームを移っていたこともあり,最後の後片付けという感じです.書くだけ書いて後片付けを次の人に任せるのは気が引けたというのもありますし,Chromium に関わり始めた 2012 年ごろ,おっかなびっくり書いたやっつけコードを自らの手で消し去る良い機会だったというのもあります.

Windows 版 Chromium で HiDPI 有効化時に IME の候補ウィンドウの位置がずれる問題の修正

f:id:NyaRuRu:20150104153618p:plain
2013 年に自分が登録したバグですが,普段使っている Windows ノート PC がこの影響を受けていてあまりにも不便だったので,土日の空き時間を使って自分で直してみたものです.

Android 5.0 の新 IME API への対応 (未完)

後で改めて書くかと思いますが,2014 年のメインプロジェクトの仕事のひとつで Android 5.0 にいくつか IME API を追加,というのをやっておりました.追加した API は InputMethodManager.html#updateCursor API の置き換えを狙ったもので,変換中の文字列のスクリーン座標を IME に伝えられるようにするものです.さて,この種の新 API の宿命として,IME だけでなくテキスト入力 widget 側にも新 API 対応が必要になります.Android の標準 TextView を使っているアプリケーションであれば何もしなくてよいのですが,独自にテキスト描画を行っているアプリケーションではそうはいきません.Chromium も当然ながらこのカテゴリに属し,Android 5.0 の SDK を使用して割と面倒なコードを追加する必要がありました.この仕事,当初は Chromium Android 版チームの人に頼もうかと漠然と考えていたのですが,Android 5.0 の開発期間中にとりあえず動く検証用コードを書き上げてしまったこともあり,なし崩し的に自分で対応コードを追加することになったという感じです.
とはいえコードレビューでのリクエストに色々応えていたらだいぶ規模が大きくなり,そのまま年末休暇シーズンに突入してしまったため,実現は 2015 年に持ち越しとなりました.

Issue Tracker 上で情報提供しつつ直してもらったものからいくつか

base::SHA1HashBytes に 4 GB 以上のデータを渡すと誤ったハッシュ値を返す

別件で SHA1 Hash の計算式を調べていたときに,参考に見てみた Chromium の内部関数に問題を見つけてしまったというものです.ざっと見てみた範囲では問題になるケースは見つけられませんでしたが,念のためセキュリティバグとして報告,修正してもらいました.

Chrome Remote Desktop (Chromoting) のウィンドウ上ではクライアント環境の IME は常にオフにすべき

仕事で Chrome Remote Desktop (内部コードネーム Chromoting) を使うことが良くあるのですが,IME 関係で不満があったので変更を提案して対応してもらったというものです.
Windows 環境から Linux 環境に Chrome Remote Desktop で接続するケースを考えてみます.このとき,Chrome Remote Desktop のウィンドウ上でクライアント側 (Windows 側) の IME を有効にすることができてしまっていました.しかし,確定した文字列は単に捨てられてしまうという状況でした.
f:id:NyaRuRu:20150427054704p:plain
これは,特に機能として役立っていないだけではなく,パスワードを意図せず IME に学習させてしまうなど望ましくない動作でした.そこで,クライアント側の IME を完全にオフにするという提案をしてみた次第です.
以前 Pepper SDK の IME 周りを担当されていた kinaba@ さんの的確なアドバイスのおかげで,数日のうちに対応していただくことができました.

Time::EnableHighResolutionTimer(false) が無視されることがある






2014 年 7 月頃,ネットメディアを中心に Windows 版 Chrome の電力消費量が話題になったことがありました.報道に関して色々言いたいことはあったものの,実際うまく動作しないケースが存在することも知っていたので,念のため知っていることを Issue Tracker にコメントしておきました.実際には他にも色々問題があったらしく,しばらくしてまとめて修正していただきました.マルチスレッド絡みバグがたくさん見つかったそうです.

base::PowerMonitor::AddObserver と base::PowerMonitor::RemoveObserver が同じスレッドで呼ばれないことによる use-after-free

たまたま自分の目の前で Google Chrome Canary がクラッシュしたので,クラッシュダンプを頼りに Issue Tracker 上で原因究明に協力したものです.Issue としては以下のものになります.(Chromium ではクラッシュバグは原則セキュリティバグ扱いのため,特定アカウントでログインしていない限り以下のページは表示されません.面倒な仕組みですみません)

仮想関数呼び出しで EXCEPTION_ACCESS_VIOLATION_EXEC という一見不可思議なクラッシュだったのですが,イベントリスナーとして使われている仮想関数だったことから use-after-free を疑って関連コードを読んでいくと,以下のようなコードを発見.これ,AddObserver と RemoveObserver が同じスレッドで呼ばれないと,RemoveObserver はサイレントに何もしないという実装だったという.
http://src.chromium.org/viewvc/chrome/trunk/src/base/observer_list_threadsafe.h?revision=212281#l125

  // Remove an observer from the list if it is in the list.
  // If there are pending notifications in-transit to the observer, they will
  // be aborted.
  // If the observer to be removed is in the list, RemoveObserver MUST
  // be called from the same thread which called AddObserver.
  void RemoveObserver(ObserverType* obs) {

「このせいじゃね?」とコメントしておいたところ,直前にコミットされていた Issue 482333003: Reland r290125: Close all active PeerConnections upon OS suspend - Code Review がまさにこの問題を踏んでいたことを特定していただきました.これにコードレビューで気付けというのはちょっと無理ですかね.ライブラリの設計ミスとでも言うべきか.

Google Cloud Messaging クライアントの exponential backoff が約 7 日でオーバーフローする

利用者からバグ報告に担当者が割り当てられていなかったので,関係しそうな人々を CC しただけ仕事です.Chrome が特定条件で (Google のサーバーの?) port 5228 に大量のパケットを送信する,という問題.原因だけは気になっていたのでその後もやりとりをウォッチしていたのですが,exponential backoff の計算方法に問題があって,約 7 日送信失敗し続けると double の精度を超えてしまっていたというオチでした.

Windows 10 Technical Preview 上で動く Chromium の User Agent が "Windows NT 6.3" と返す

Windows 8.1 のときにもあった,GetVersionEx 嘘つき問題です.マニフェストファイルに GUID を追加すれば解決する話ではあるのですが,実際手を出してみたら大量の yak shaving に付き合わされたのが前回.今回は Issue として登録して直してもらいました.

感想

何となくそうではないかと予想していましたが,Issue Tracker で人に作業を振ったりあれこれ意見を述べるだけってのはだいぶ楽ですねこれ.チームを移ったこともあり,基本的に休日や通勤中の空き時間でできる作業に専念していたのですが,これぐらいの作業なら空き時間との相談で続けてもいいかなというところです.
一方で Chromium のコードを書く人の負担が半端なく大きいという問題は未解決のままと言えます.かなり厳しいコードレビュー,法外に高いクロスプラットフォーム対応コスト,不安定なテスト,コミット後忘れた頃にやってくる regression とクラッシュの報告.実際に問題を修正してくれている人たちは何故そんな面倒な仕事をしてくれているのか,そこをうまくシステム化していかないとブラウザ開発というプロジェクト自体の持続可能性が危ういんではないかなと.Issue Tracker であれこれ意見を言うだけの人が 100 人いても,大変めんどくさいコード変更作業に立ち上がってくれる人が 1 人現れない限り問題は解決しないという構造をプロジェクト運営にどう反映させていくのか.思うことは色々あるのですが,この辺についてはまた改めて別のエントリで書いてみたいと思います.
2015 年ですが,Android 5.0 の新 IME API 対応はさっさと終わせないとですね.その先はあんまり考えていないです.まあなるようになる感じで.

References

コメントした Issue 一覧

2013年やったこと - Firefox, LibreOffice, Gyp 編

2013年やったこと - Firefox, LibreOffice, Gyp 編.

Firefox: TSF 無効時の HTML5 Forms のtype 指定と InputScope の対応作業

866736 – InputScope support for IMM32 with CUAS
f:id:NyaRuRu:20140105122321p:plain
TSF モードの Firefox は InputScope に対応既なのですが,IMM32 モードでは特に何も考慮されていませんでした.実は SetInputScopes API を使えばレガシー IMM32 アプリでも InputScope に対応できるので,Firefox でもやってみた,という変更です.以下のように InputScope 周りを重点的に見ていた時期だったので,タイミングが良かったというのもありました.

  • 同様の方法で InputScope に対応するコードを Chromium 向けに書いていた
  • 同時期に Mozc の InputScope 対応を実装していた

ゴールデンウィークに Mozilla Japan の六本木オフィスで行われたハッカソンで書き上げたものです.パッチの送り方も一緒に教えてもらえたので大変助かりました.
ビルドを壊しちゃったり コードに CRLF が混ざっていたりと,チェックイン後も色々ご迷惑をおかけすることに.中野さんをはじめフォローしていただいた Mozilla の皆様ありがとうございました.

LibreOffice: IMR_QUERYCHARPOSITION 対応

Bug 64298 – Support IMR_QUERYCHARPOSITION for better integration with IMEs
LibreOffice に IMR_QUERYCHARPOSITION サポートを追加して,各種 IME が適切な位置にサジェストウィンドウを表示できるようにするというパッチです.

LibreOffice Writer 4.0 (修正前)

f:id:NyaRuRu:20140105122333p:plain

LibreOffice Writer 4.1 (修正後)

f:id:NyaRuRu:20140105122401p:plain
この問題との付き合いは長く,Mozc リリース直後にOpenOffice.org でサジェストウィンドウの表示位置が変というレポートがたくさん来て以来となります.3 年半のときを経てやっと根本的に対処できました.

IMR_QUERYCHARPOSITION を (直接的または間接的に) 利用している全 IME で表示位置が改善されます.
このパッチもたしかゴールデンウィークを利用して書いています.パッチの送り方からチェックイン後のビルド失敗の後始末まで LibreOffice チームのみなさんにたくさんフォローしていただきました.この場をお借りして改めてお礼申しあげます.

Gyp Issue 385: ninja generator doesn't handle LinkTimeCodeGeneration/ProfileGuidedDatabase for PGO

Issue 385 - gyp - ninja generator doesn't handle LinkTimeCodeGeneration/ProfileGuidedDatabase for PGO - Generate Your Projects - Google Project Hosting
年末休暇で暇つぶしに適当に見つけた Gyp のバグを直してみたというもの.特に困ってはいなかったのですが,たまには Gyp コミッターらしいことでもしておくかという感じ.
Gyp の Ninja 向けジェネレータが PGO 設定の一部をサポートしていなかったので,対応してみました.実装自体は単に対応表を書くだけだったのですが,コードレビューで「実際に PGO ビルドするテストもあるといいね」という流れになってテストを書いてみたら予想を上回る大変さだったという.この体験がもとになって書いたのが次の記事です.

Chromium で PGO が有効化される場合はもしかしたら意味があるかもしれません.もっとも,そのまえに Chromium のビルドシステムが Gyp から GN (Generate Ninja) に移行しちゃうかもしれませんが.

Gyp Issue 389: ninja generator is not aware of VCLinkerTool.GenerateManifest

Issue 389 - gyp - ninja generator is not aware of VCLinkerTool.GenerateManifest - Generate Your Projects - Google Project Hosting
上の Gyp Issue 385 のテストを書くのにどうしても必要になった機能です.

感想

メインプロジェクトと関連があるところを攻めつつ,みんなが便利になる方向の改良にもっていけたように思います.

今年の話とか

今年も隙を見て OSS プロジェクトにパッチを投げていきたいです.