2012年12月23日日曜日

ESウェブブラウザ通信 - escort バージョン 0.2.4 公開

前回、『年内に次の0.2.4のリリースを』とお知らせしていた通りescort バージョン 0.2.4 (alpha)を先ほどリリースしました。

escort バージョン 0.2.4 (alpha)の主な改良点は以下の通りです:
  • DOM Living Standardから、NodeElementインターフェイスで未実装だった基本的な属性やオペレーション(getElementsByClassNameなど)の実装を追加。
  • script要素のdefer, async属性のサポート。
  • テーブルやフロールートになるボックスを可能ならフローティング ボックスの隣にレイアウトできるように更新。
その他にも細かなバグの修正が含まれています。CSS2.1 Conformance Test Suiteの達成率は約91.6%に向上しています。

Fedora 17とUbuntu 12.10用のパッケージは、こちらからダウンロードできます。

で、終わってしまうと、ただのリリースノートになってしまいますね。今回は実際のところ何をどうして、どんな風に変えたのか、といったところをまとめてみます。

ウィキペディアの忠犬ハチ公のページ


ウィキペディアはシンプルなページ構成になっていて、現状のescortでもきちんと表示できるようになっていてほしいサイトの代表格です。0.2.3では下のスクリーンショットのように「目次」の部分が下に行きすぎてしまっている、というような問題が残っていました。

escort 0.2.3

これはCSSのフロートの仕様で、テーブルなどフロールートになるボックスについては、
implementations should clear the said element by placing it below any preceding floats, but may place it adjacent to such floats if there is sufficient space.
と規定されている部分のMAYの部分が未実装だったためでした。この部分、文面からはoptionalというニュアンスで受け取っていたのですが、実際にはCSS2.1 Conformance Test Suiteでも、MAYの方の動作を積極的にテストしている部分があって(floats-wrap-bfc-001-left-overflowなど)、実用上はMUSTになっているようです。

0.2.4ではこの仕様に対応して、想定通りの表示ができるようになりました。

escort 0.2.4

はてなさんのトップページ


はてなさんのトップページも、比較的シンプルなのに0.2.3ではうまく動いていなかったページのひとつでした。

escort 0.2.3
間違い探しのようですが0.2.3では、
  • 598usersのような表示が最初のひとつしか出ていない、
  • 検索ボックスの位置が下にずれている、
  • 『一般 | 社会 | 政経 | 生活・人生 | 芸能 | 科学』というタブをクリックしても効かない、
といった問題がありました。それぞれ原因は、
  • HTTPプロトコル処理の実装の問題
  • デフォルト スタイルシートの設定の問題、
  • script要素の処理がHTTPパーサーと同期していない; 未実装のNode, Elementインターフェイスのオペレーションが使われている、
といったものでした。このあたりの問題も0.2.4では改善されています。

escort 0.2.4

改良点のより詳細については、ESプロジェクトのGoogle+コミュニティで随時報告していますので、あわせてご参考にしてください。

Dogfooding


ところで今回、最初に書いたようなリリースノート的な書き方をすると、
『これこれを直したので、このページも表示できるようになりました』、
という雰囲気になるのですが、実際のところは、ウィキペディアやはてなさんのページを見てみて、
『このページが表示できないので、これこれを直しました』、
という流れになっています。

DOMも、HTMLも最新の仕様はLiving Standardモデルに移行して、日々修正が加えられ続けています。こういう状況になると、仕様書の1章から順に実装していく、という手法はすこし無理がありそうです。CSS 2.1の方は、仕様書がW3Cの勧告になって、テストスイートも十分に揃っているという状況でしたので、escortでは比較的しらみつぶし的な開発手法が採れたのとは対照的です。

そういう点を踏まえて、先日David Baronさんがブログに書かれていた、
Perhaps most importantly, dogfooding is a way to shift the development cycle away from the (often endless) task of building items on abstract requirement lists and instead focus on fixing the things that are needed to make the product usable for real users.
という文章を読んでみると、とても深みがあります(David BaraonさんはCSS 2.1が勧告としてまとまるより、ずっと前からCSSなどの開発を進められていたという点に注意)。

とにかく使ってみる(Dogfooding) ことで明らかになった、簡単な問題からつぶしていく、というのがソフトウェアを実用レベルに持っていくのには効果的、ということなのだと思います。

まとめ


次のescort 0.2.5のリリースは年が明けてからになります。0.2.5では、まずescortで利用しているWeb IDLによるインターフェイスの定義を先日、勧告候補(CR)が発行されたHTML5Canvas 2Dのものにアップデートする予定です。Web IDLコンパイラesidlも必要に応じてアップデートしていきます。

また年が明けると少し遅れていたFedora 18がリリースされそうです。これに合わせてfc18のescort 0.2.4とesidl 0.2.2のパッケージを可能であればリリースしていきます。何か問題が発生してしまった場合は、それぞれ0.2.5, 0.2.3までfc17ベースで進めていく予定でいます。

ESウェブブラウザは、HTML5がW3Cの勧告になる予定の2014年Q4までには"broadly usable"な状態にもっていくのを目標に開発を進めています。膨大な量の仕様書をただ見ていると先に進まなくなってしまいそうなので、そこはdogfoodingしながら、というわけなのでした。

今回はここまでです。みなさま、よいクリスマスを。

2012年12月15日土曜日

ESウェブブラウザ通信 - escort バージョン 0.2.3 公開

前回は、そろそろescort 0.2.3を出す予定です、ということを書いたところで終えていたのですが、今月初めにescort 0.2.3をリリースしました。今回はすこし遅くなってしまいましたが、そのインストール手順の紹介と、最近開設したESプロジェクトのGoogle+コミュニティを紹介します。

インストール手順


escort 0.2.3からFedora 17に加えてUbuntu 12.10用のパッケージ レポジトリも用意しました。レポジトリのトップページにもインストール手順の説明をしてありますが、ここでも簡単に紹介しておきます。

Fedora 17


Fedoraでの手順は0.2.1をリリースした際に紹介した方法とまったく同じです。ただ、0.2.3からパッケージに署名をするようになっていますので、0.2.2以前からレポジトリを追加してくださっていた場合は、下記のように一度古いesrille.repoファイルを削除してください

sudo rm /etc/yum.repos.d/esrille.repo

古いesrille.repoファイルがなければ、以下のコマンドでエスリルで用意しているパッケージ レポジトリの情報を追加できます。

sudo yum-config-manager --add-repo http://download.esrille.org/fedora/esrille.repo

ここまでできれば、

sudo yum install escort

と実行すれば、escortをインストールできます。yum以外にもApper (KDEの場合)のようなGUIツールを使ってインストールすることもできます。
Apper

Ubuntu 12.10


Ubuntuの場合は、まず/etc/apt/sources.listの中に以下の2行を追加して、エスリルで用意しているパッケージ レポジトリの情報を追加します。

deb http://download.esrille.org/ubuntu quantal main
deb-src http://download.esrille.org/ubuntu quantal main


sudoeditで直接編集してもよいですし、GUIからも次の手順で追加できます:
  1. System Settingsを開く。
  2. Software Sourcesを開く。
  3. Other Softwareタブを選択する。
  4. Add...ボタンを押して、software-properties-gtkダイアログボックスを開く。
  5. APT line:テキストボックスに、
    deb http://download.esrille.org/ubuntu quantal main
    と入力して、Add Sourceボタンを押す。
  6. Authenticateダイアログが表示されたら、管理者のパスワードを入力して承認する。
 ここまでで下のような画面になっていれば大丈夫です:
Software Sources ウィンドウ
続いてパッケージの署名に使っているエスリルの公開鍵(esrille-pubkey.asc)をシステムに取り込んでください。
  1. Software SourcesウィンドウのAuthenticationタブを選択する。
  2. Import Key File...ボタンを押して、 Import keyダイアログボックスを開く。
  3. ダウンロードしたesrille-pubkey.ascを選択する。
  4. Authenticateダイアログが表示されたら、管理者のパスワードを入力して承認する。
 下のような画面(マジェンタ色の中)になれば設定完了です:
エスリルの鍵の取り込み
鍵の取り込みは、コマンドラインから次のようにして実行することもできます:

sudo apt-key add esrille-pubkey.asc

以上でレポジトリ情報の追加ができました。ここまでできれば、apt-getや、Synapticパッケージ マネージャーを使ってescortをインストールできます。

apt-getを使う場合は、

sudo apt-get update
sudo apt-get install escort

のようにしてインストールできます。

Synapticを使う場合は、Reloadボタンを押してから、World Wide Webグループのescortを選択してインストールしてください。

Synaptic

    起動


    インストールが完了したら、コマンドラインからは、

    escort

    で起動できます。GUIからの起動方法も7月のこのブログで紹介しているので参考にしてください。

    escort 0.2.3


    Fedora, Ubuntuどちらも、レポジトリ情報の追加さえ終わっていれば、今後新しいバージョンをリリースした場合も、yumやapt-getなどで簡単に新しいバージョンへアップデートできるようになるので、開発の進捗の確認などにもご利用ください。

    まとめ


    escortの開発がCSS 2.1の部分については割と落ち着いてきたこともあって、このブログの方は更新頻度が下がっていますが、開発寄りの最新の情報は最近スタートしたGoogle+のコミュニティを使ってこちら(ES Web Browser/Operating System)でお知らせしています。何かありましたら、コミュニティにも気軽にコメントなど寄せてください(一部分でも開発に参加してみたい、みたいなことがありましたら、ぜひ使ってください!)。

    また今回はescort 0.2.3のインストール手順を紹介してきましたが、年内に次の0.2.4のリリースを予定しています。escort 0.2.4では、DOMやHTMLの基本的なAPIの実装が進んでいるので、単純にHTMLページを表示できるだけではなくて、マウスで操作できる部分も少し増えてくる予定です。

    escort 0.2.4 (開発中のもの。画面はHatenaさんのトップページ)

    というわけで今回はここまでです。

    2012年11月7日水曜日

    ESウェブブラウザ通信 - インクリメンタル リフロー #2

    現在インクリメンタル リフローの実装まっ最中ということで、前回から少し時間があいてしまいました。今回は前回端折ってしまったところもあったので、前段をすこし掘り下げてから、インクリメンタル リフローの処理の続きについてまとめていきます。

    ドキュメントとビューとウィンドウ


    ESウェブブラウザ内部は大きく分けると、
    1. DOMツリーを保持しているドキュメント
    2. 各DOM要素に対応するCSSのスタイルとレンダーツリーを保持しているビュー
    3. ドキュメントとビューを管理しているウィンドウ
    の3つの部品で構成されています。感覚としてはMVCモデルに近いものがありますが、MVCモデル全般について話しだすと議論が飛びやすくなりそうな気がするので、ここでは基本線として:
    The interesting thing to me is that the DOM is what's meant to be the model originally, as far as I can tell, with the CSS presentation being the view.... - Boris Zbarsky
    ということで進めていきます。DOMがモデルで、CSSがビュー、というわけです。

    現在はobsoleteになっていますが、歴史的にはW3Cの仕様 "Document Object Model (DOM) Level 2 Views Specification" の中で、ドキュメントに対応するビューの抽象インターフェイスとしてAbstractViewインターフェイスが定義されて、CSSによるAbstractViewの実現が、 "Document Object Model CSS" の中で、ViewCSSインターフェイスとして規定されていました。

    これらを図にしたものが下図になります:

    Document, ViewCSS, and Window

    最新の仕様ではViewCSSは独立したインターフェイスとしてはもう規定されていなくて、CSSOMの中でWindowインターフェイスの拡張として定義されています。そういう流れがあるので、最初に紹介したBorisさんの含みのあるコメントにつながっていくのだろうと思います。

    そういうわけで仕様書の中からは消えてしまったViewCSSですが、ESウェブブラウザの実装にあたっては独立したクラスViewCSSImpとして残っています。そしてブラウザ内部でのこの3つのオブジェクトのインターラクションは依然としてMVCモデル的な動きになっています:
    • ドキュメント内で要素を追加・変更・削除したりすると、Documentはミューテーション イベントを発行します。ESウェブブラウザはこのミューテーション イベントをViewCSSでも拾うようにしています。ミューテーション イベントを通知されたら、ViewCSSはレンダー ツリーを構成しなおして、Windowに画面の再描画が可能なことを通知します。
    •  ウィンドウ内をマウスでクリックすると、WindowはViewCSSからマウス カーソルの位置にあるドキュメント要素を問い合わせて、マウス イベントをドキュメント内のその要素に向けてディスパッチします。

    レイアウト処理の流れ


    前回説明したようなViewCSS内部でのレイアウト処理の流れを図にしたものが下図になります:

    CSSのレンダリング ステージ - (右側は対応するViewCSSImpクラスのメソッド名)

    5月のこのブログで紹介した図からは前回説明した"block construction"ステージが増えています。また:hoverの処理の高速化のために追加した"partial style recalculation"ステージが削除されています。これはインクリメンタル リフローが入ると、どのステージも更新が必要な部分だけを更新することができるようになっているためです。

    hoverする要素が変わった場合には、r3096以降からは、hover効果が有効になった要素と無効になった要素についてstyle recalculationまで戻って処理をします。それでもインクリメンタルに必要な部分だけで処理が進んでいくので、単に文字の色だけを変えるということであれば不要なblock constructionやreflowはスキップされて処理時間はほとんど変わりません。

    インクリメンタル リフロー (続き)


    前回、ViewCSSが考慮しないといけないミューテーション イベントには以下のものがあることを説明しました:
    1. テキスト ノードの内容が変更された
    2. テキスト ノードが追加・削除された
    3. DOM要素が追加・削除された
    4. DOM要素の属性が変更された
    5. DOM要素のスタイルのプロパティの値が変更された
    1.から3.の場合の対応については前回説明した通りで、今回は4.から説明していきます。

    4.の場合は、要素にマッチするIDセレクタ、属性セレクタ、およびクラス セレクタが変わる可能性があります()。r3092で、その要素についてはセレクタ マッチングからやり直すようにしています。属性の変更を頻繁に使うということであればスタイル再計算からのやり直しで対応するような実装も考えられそうですが、いまとのころそこまでは、という理解です。

    5.の場合は、 まず変更を加えた要素に対応するスタイルの再計算を実行します。その結果、値が変化したプロパティーの種類によって、ブロックの再組み立てが必要になる場合(display, float, positionなど)、リフローが必要になる場合(width, heightなど)、リペイントが必要になる場合(colorなど)に分類して処理を進めていきます。r3039以降で実装が進んでいます。

    なお、位置づけされた要素のz-indexの変更にともなうスタッキング コンテキストの順番の入れ替えは、r3088以降でスタイル再計算中に処理するようにしています。

    またテーブルについてはレイアウトの考え方がまったく異なるのでいつものように別処理で対応を進めています。現在はr3071でセル内部でリフローが発生しても、セルのサイズが変わらなければテーブル全体の再レイアウトはしないようにする処理が入っています。それ以外にも行の追加・削除といった最適化が適用可能な場面が考えられますが、いまのところテーブル全体を再レイアウトするようになっています。このあたりについては改善できたらまた報告していきます。

    インクリメンタル リフローの評価


    インクリメンタル リフローの効果としてよく例に挙げられているのが、インプット要素のテキストボックスに1文字挿入するという処理です。この例は、テキストボックスにOSのコントロールを使っている場合には当てはまりませんが、ESウェブブラウザのようにフォーム コントロールもHTML(XBL 2.0)で作られている場合には、インクリメンタル リフローがないとページ全体を再レイアウトしないといけないことになってしまいます。

    r3121の段階では、インプット要素に1文字挿入した際にレイアウト処理を終えるまでに使用するCPU時間は、表示しているHTMLページの複雑さには影響をほとんど受けずに3.4ミリ秒程(Core i7 3770, -O2)になっています。いままではページ全体をリフローしないといけなかったので、例えばESオペレーティング システムのページですと20倍くらい速くなったような感じになっています。
    r3096 (indroductionまでマウス カーソルを移動させると、¶ 記号が表示されます)

    まとめ


    今回はインクリメンタル リフローの処理の残りの実装について報告しました。まだ実装が入っていない部分が残っていますが、考え方としては今回説明したとおりで対応できるはずです。

    CSS 2.1に関しては、エスリル内部のソースコード レポジトリを確認してみると実装を開始したのは2010年の12月からなので、2年近くかけてようやく骨格が固まってきたというところです。CSS 2.1適合テストスイートのチェックを後回しにすればもっと早くここまではきた可能性もあります。ただそれだとインクリメンタルな処理と一緒にテストスイートの達成率を上げていかないといけないので、トータルではさらに時間がかかることになったんじゃないかなと思います。

    次回はescort 0.2.3のリリースを行なってからの予定です。Fedora 18が12月11日リリース予定、Ubuntuも12.10が先月リリース済みということで、リリース作業が少しややこしくなりそうな予感もしますが、年内に0.2.3、可能なら0.2.4まで、というようなつもりでいます。というわけで今回はここまでです。

    2012年9月29日土曜日

    ESウェブブラウザ通信 - インクリメンタル リフロー #1

    ESウェブブラウザ(escort)が先月からCSSレンダリング エンジンをインクリメンタル リフロー(DOMツリーを部分的に操作したときなどのブラウザの応答性を良くするために必要最小限の部分だけリフローを行う手法)に対応したものに改良中ということで、ブログの方は前回から少し時間があいてしまいました。今回は実装中のインクリメンタル リフローの話題のほかに、レイアウト処理の基本的な事項も合わせて紹介していこうと思います。

    5月にこのブログでは、CSSのレンダリング処理は一般的に以下の4ステージで進んでいくことを紹介しました:
    1. セレクター マッチング (selector matching)
    2. スタイル再計算 (style recalculation)
    3. リフロー (reflow)
    4. リペイント (repaint)
    escortのスタイル マッチングとスタイル再計算の実装については、そこでまとめておいたので、今回はリフローとリペイントについて簡単にまとめておきます。また現在実装が進んでいるインクリメンタル リフローについても説明していきます。

    リフロー


    DOMツリーからレンダー ツリー(あるいはボックス ツリー、レンダリング ツリーとも)を構築して、ボックスの位置を決めていく処理は一般的にレイアウトあるいはリフローと呼ばれています。

    レンダー ツリーはテーブルを除くと、以下の3タイプのボックスから構成されている木構造です:
    CSS 2.1の仕様書では、ブロック ボックス、ブロック レベル ボックス、ブロック コンテナ ボックスと3種類のブロック系ボックスの用語を導入していますが、特に明確に区別する必要がない場合は単に「ブロック」と呼ぶ、ということになっています(9.2.1)。(ブロック ボックスはブロック レベル ボックスでもあり、通常ブロック レベル ボックスはブロック コンテナ ボックスなので、実際に区別しないといけない場合はそんなにありません。)

    これら3種類のボックスは自由に親子関係になれるわけではありません:
    • ブロックの子ノードはすべてブロックかすべてライン ボックスかのどちらかでないといけません(これはCSSの仕様)。
    • ライン ボックスの子ノードは通常はインライン ボックスになります。escortの実装では、フローティング ボックスや絶対配置ボックスに対応するブロックもライン ボックスの子ノードとして配置されます。
    • インライン ボックスはテキストに相当する部分なので通常は子ノードを持つことはありません。escortの実装では、inline-blockに対応するブロックはインライン ボックスの子ノードとして管理されています(これは将来いきなりライン ボックスがブロックを保持するように変更するかもしれません)。
    ですので、ひとつのブロック要素の中にブロック要素とインライン要素が混在していた場合には、インライン要素に対応するインライン ボックス(とそれらをまとめるライン ボックス)をまとめて保持する匿名のブロック ボックスを生成する、というのがCSSの仕様です。

    またCSS 2.1の仕様書では「匿名のインライン要素」という言葉が9.2.2.1で出てきますが、このあたりは難しく考えなくても、DOMツリー中のテキスト ノードはインライン ボックスに変換される、と考えた方が分かりやすいように思います。そしてインライン要素に設定されたスタイルはそれぞれのインライン ボックスに対して効果をもちます。実装においては、white-spaceプロパティの指定にともなう空白の除去や、空のインライン要素(ボーダーはあるかもしれない)の処理や、ネストしたインライン要素(スタイルが重複していく)の処理などがあるので、実際には多少複雑なところもあります。

    escortでは、リフローの処理はさらに内部で3段階に分かれています:
    1. ブロックを組み立てる,
    2. 絶対配置ボックス以外のブロックを、ブロック中のライン ボックス、インライン ボックスを生成しながら、レイアウトしていく,
    3. スタッキング コンテキストを辿りながら、絶対配置ボックスを、ブロック中のライン ボックス、インライン ボックスを生成しながら、レイアウトしていく。
    狭義でリフローと言った場合には2.と3.の処理を指す場合があるようです。1.の処理のようなボックスの配置を決める前にボックスをまず生成するステップのことをMozillaのDavid Baronさんは"Frame construction"と呼んでいます(CSS 2.1で言うボックスのことをGeckoではFrameと呼んでいます。ただこれはBad nameとのこと)。CSS 2.1の仕様書の用語に合わせると、1.は"Block construction"といったところでしょうか。

    画面上のウィンドウのサイズを変更したりしただけでは、ブロックの木構造には何も変化はおきないので、そういった場合は2.の手順から処理をはじめることによって全体の処理を軽くすることができます。そのためブロックの実装では、それぞれのブロックごとにその中にレイアウトするインライン レベルのDOMノードのリストを保持しています。

    補足: 1.のステップで以前はブロック レベル ボックスと匿名ブロック ボックスだけを生成していたのですが、r2985, r2988の変更によって、現在は文字通りインライン ブロックやフローティング ボックスなどブロックになるものをすべて生成しています。

    リペイント


    リペイントは、従来はWindowオブジェクトごとにレンダー ツリーを描画用と作業用に2つずつ保持して、描画用のレンダー ツリーを元にOpenGLの命令を発行して画面に描画する、という方法を使っていました。

    インクリメンタル リフローをサポートするようになると、レンダー ツリーが一旦構築されたあともレンダー ツリーは部分的に更新され続けていく事、加えてescortではリフローはメイン スレッドとは別のスレッドで実行されているので、レンダー ツリーを元にOpenGLの命令を発行しながらペイントする、という手法は簡単に使うことができなくなりました(スレッド間の排他制御などで複雑な実装になってしまうことが予想されます)。

    そこで、r2934からr2936の変更によって、レンダー ツリーは作業用に1つだけ持つようにして、リペイントのためには描画用のレンダー ツリーを保持する代わりに描画済みのビットマップ(内部的にはOpenGLのテクスチャ)を保持するようにしました。

    リペイントまでの手順としては、レンダー ツリーの更新が完了したら、いきなり画面上のウィンドウに描画するのではなく、まずはOpenGLのテクスチャにオフラインでレンダリングしていきます(この際、OpenGLのARB_framebuffer_object拡張を使っています)。実際のリペイントの際には単にこのテクスチャを画面上のウィンドウに貼り付けていきます。

    これでGPUやCPUのリペイントに関する処理量も減ることになるのですが、代わりにメモリ(GPU側)の消費量は多くなっています。デスクトップPCではエントリークラスのGPUを搭載したビデオカードにも2GBといったメモリが搭載されるような時代になってきていますが、モバイルに関してはfacebookから、
    Given the size of GPU buffers relative to the size of content consumed on devices nowadays, I doubt well get to a place where managing GPU can be left strictly to the browser in a reasonable amount of time.
    というフィードバックが先日流れていましたね。

    インクリメンタル リフロー


    escortの場合、DOMツリーからレンダー ツリーを構築してペイントするまでの処理は、以前紹介した4ステージではなくて、以下のような5ステージとして捉えるのがわかりやすいと思います。
    1. セレクター マッチング (selector matching)
    2. スタイル再計算 (style recalculation)
    3. ブロックの組み立て (block construction)
    4. リフロー (reflow)
    5. リペイント (repaint)
    いずれにしても、何もないところからDOMツリーを元にレンダー ツリーを生成してペイントするまでには秒単位の時間がかかる場合も少なくありません。ですので動的にDOMツリーに変更を加えるようなダイナミックなHTMLページでは、毎回レンダー ツリーを再構築したりしていると実用的な応答速度を得ることはとても難しくなります。

    実用的な応答速度を得るためには、構築済みのレンダー ツリーをできる限りそのまま利用して、必要な部分だけボックスを更新したり、追加したり、あるいは削除したりするインクリメンタル リフロー(参考: Geckoでの実装)と呼ばれている処理が重要になります。

    余談: リンク先のモジラのページを見てみると、インクリメンタル リフローの考え方はDOMの登場よりも前から、モデムを使ってインターネットに接続していた頃のようにネットワーク接続が非常に低速だった時代に、サーバーから読み込めた部分から先に画面上に表示していく、といったテクニックの延長にできてきたもののようにも見えます。escortではそこまでアグレッシブなことはしていなくて、HTMLファイルや画像ファイルなど個々のファイルについては完全にロードが完了してからはじめて必要な処理を開始するようになっています。

    DOMツリーに加えられる変更は、いまのところescortではmutation eventを使って捕捉しています(いずれDOM4のMutationObserverに置き換えないといけませんが)。実際に考慮しないといけない事象には次のようなものがあります。
    1. テキスト ノードの内容が変更された
    2. テキスト ノードが追加・削除された
    3. DOM要素が追加・削除された
    4. DOM要素の属性が変更された
    5. スタイルのプロパティの値が変更された
    1., 2.の場合のインクリメンタル リフローは比較的簡単で、一番単純な場合であれば、そのノードを含むブロックだけリフローして、あとはそれ以降のブロックの座標だけ更新すれば終わりになります()。 より複雑な場合になると、リフローしたブロック以降のブロックについてもリフローしたり(フローティング ボックスの高さの消費量が変わった場合など。)、それが絶対配置ボックスの包含ブロックの大きさに影響していれば、関連する絶対配置ボックスについてもリフローを行ったり()と、芋づる式にリフローが連鎖していく場合もあります。

    1., 2.に関する実装については、r2968からブロックごとにdirtyビットを使って、リフローしたりしなかったりを制御しています。ややこしいのはマージンのつぶしやフローティング ボックスの高さの消費の途中経過など、フォーマッティング  コンテキストをリフローを行うボックスできちんと回復できるようになっていないといけない、といった点です。r2976では絶対配置されているボックスのコンテント エリアの大きさが変わっていなければリフローを抑制するようなっています。さらにr2990ではインライン ブロックやフローティング ボックスについてもインクリメンタルにリフローできるようになっています。

    3.の場合、インクリメンタル リフローは、追加された要素についてはセレクター マッチングから処理をはじめないといけません。このときはマッチング済みの要素についてはセレクター マッチングの処理を省略するといった工夫が必要になります(セレクター マッチングがCSSの処理の中で一番重い処理だという点を忘れると大変です)。r3002でそのような基本的な処理が実装されています。また追加された要素がCSSカウンタを操作していたりする場合には、それ以降のボックスで使われている連番の値が変わって、これまたリフローが芋づる式に必要になる場合があります()。その制御がr3013で入っています。

    補足: 長大なドキュメントでカウンタによる連番が使われてた場合、連番の振り直しとそれにともなうリフローにはそれなりの時間がかかる場合も想定できます。ワードプロセッサなどでは、連番はユーザーから指示があるまであえて振り直さない、アイドル タイムを使ってバックグラウンドで振り直してしばらくたってから正しく振り直して表示する、といった工夫がされていることがあります。escortでも、このあたりは今後もうひと工夫して実装を改良してみるかもしれません。

    4., 5.の場合については、また次回以降でソースコードがレポジトリにコミットできてから紹介していきます。

    まとめ


    今回は現在escortで実装中のインクリメンタル リフローの話題のほかに、レイアウト処理の基本的な事項も合わせて紹介しました。次回は、インクリメンタル リフローの処理の続きを報告していく予定です。escort 0.2.3のリリースはそのあとになる予定です。

    2012年8月19日日曜日

    ESウェブブラウザ通信 - テストハーネスの使い方

    今回はESウェブブラウザでのCSS 2.1 Conformance Test Suiteのヘッドレステスト方法について紹介します。

    CSS 2.1 Conformance Test Suiteは、CSS 2.1の実装が仕様通りになされているかどうか判断するためのテスト ページを集めたものです。テストスイートには仕様書の編集者が作成したものだけなく、Internet Explorere, Firefox, WebKitなどブラウザ ベンダーから提供したされたものも含まれています。そのテストページ数はhtml4だけでも9,365ページに及びます。

    escortウェブ ブラウザでは、このテストスイートを使ってレンダリング エンジンを開発してきました。その内容については21回にわけてこのブログで報告してきました。仕様書の文面だけでは解釈が分かれそうな箇所についても、テストスイートで確認することによって解釈違いを修正できた場面なども少なくありません。以前も紹介したように、IEBlogでJason Uptonさんが、
    The only way to know if a browser has correctly implemented a specification is to develop a comprehensive set of tests for the specification.
    と述べられている通り、テストスイートなしに仕様書だけを見て各ベンダー間で互換性のあるウェブブラウザを実装することは現実的には不可能かもしれません。

    テスト ハーネス


    CSSレンダリング エンジンには高速化や新機能の実装といった形で日々修正が加えられています。そういったときに修正に何か問題があると、それまでパスしていたテストページのテストに失敗して表示が崩れたりすることがあります(リグレッションと呼ばれています)。ですので、原則としては何か修正を行ったらテストスイートのすべてのテストページを再確認しておくべき、ということになります。

    一方、CSS 2.1 Conformance Test Suiteの個々のテスト ページは基本的には実際にブラウザで表示して目で見て成否を確認するように作られています。しかし何か修正を加えるたびに9,370ページものページをひとつひとつ目視で確認していたのでは実装ペースを上げることができません。そこでいちいち目視せずに自動でテストを行うためのソフトウェア、テストハーネス、を利用することが重要になってきます。

    ESウェブブラウザでは、レンダーツリーのテキスト形式のダンプにpass, failといったタグをつけて記録したログファイルと、実際の実行結果を比較して判定するharnessというプログラムを用意しています。以下ではこのharnessの使ったテスト方法について説明していきます。

    テスト環境の構築


    1) Apache HTTPサーバーのセットアップ


    CSS 2.1 Conformance Test SuiteではApache HTTPサーバー用の.htaccessファイルを利用して、特定のキャラクターエンコードでHTTPファイルを送信するといった指定がされている箇所があります。そのためテスト用のHTTPサーバーとしてはApacheを利用するのが簡単です。

    harnessは、いまのところテスト用PC上でローカルに起動されているHTTPサーバーにアクセスするようになっています。Fedoraであれば、

    $ sudo yum install httpd

    でApacheをインストールして、/etc/httpd/conf/httpd.confを開いて、ServerNameを、

    ServerName localhost:80

    に、<Directory "/var/www/html">の部分のAllowOverrideを、

        AllowOverride All

    と変更して、.htaccessを利用できるようにします。あとは、

    # systemctl start httpd.service
    # systemctl enable httpd.service

    のようにすれば、Apacheをいつも起動した状態に設定できます。

    2) テストスイートの展開


    W3CのサイトからCSS 2.1 Conformance Test Suiteを取得してきます(現在の最新版は20110323.zipです)。これを、/var/www/html/suites/css2.1に展開します。

    # cd /var/www/html/suites/css2.1
    # wget http://test.csswg.org/suites/css2.1/20110323.zip
    # unzip 20110323.zip

    3) テスト フォントの展開


    CSS 2.1 Conformance Test Suiteを利用すために必要なフォントが、やはりW3Cのサイトから提供されています。これを、/var/www/html/Style/CSS/Test/Fontsに展開します。

    # cd /var/www/html/Style/CSS/Test/Fonts
    # wget http://www.w3.org/Style/CSS/Test/Fonts/css-testsuite-fonts-v2.zip
    # unzip css-testsuite-fonts-v2.zip

    今のところ、harnessでテスト中は、ユーザースタイルシート等のサポートファイルに関しては、http経由ではなくて、上記のディレクトリに直接アクセスしています。

    これでテストスイートの準備ができました。試しに、http://localhost/suites/css2.1/20110323/ にアクセスしてみてください。下のような画面が表示されれば、正しくセットアップできています。
    CSS 2.1 Conformance Test Suite

    テストハーネスを使ったテスト方法


    テスト結果のレポート形式


    CSS 2.1 Conformance Test Suiteのテスト結果のレポート形式については、W3CのCSSWG(CSS ワーキンググループ)で決められていて、下記のサイトにまとめられています:

    http://wiki.csswg.org/test/implementation-report

    escortウェブ ブラウザでは、今のところhtml4のテストのみ行なっています。そのテスト結果は以下のページから確認することができます:

    http://code.google.com/p/es-operating-system/source/browse/trunk/escort/testlog/css2.1/20110323/implement-report-Escort-Fedora.data

    r2923の段階ではFedora 17上でescortは約90.8%のテストにパスしていることがわかります。

    自動テスト用のログファイルの準備


    自動テストを行うためには、まず各テストのタグ付きのログファイルを用意します。はじめてテストするときは目視でひとつひとつテストをチェックしていかないといけません。一方、テスト済みのテストに関しては、implement-report-Escort-Fedora.dataの結果を元にログファイルを自動で生成する機能がharnessにあります。

    ログを生成するには、implement-report-Escort-Fedora.dataに対応するリビジョンのビルド ディレクトリで以下のようにコマンドを実行します:

    $ mkdir html4
    $ ./harness -j16 -g ~/es/trunk/escort/testlog/css2.1/20110323/implement-report-Escort-Fedora.data ./Script.test ~/es/trunk/escort/testdata/default.css --v=2 -geometry +0+0

    -g オプションがログファイルの生成を指示しています。-j16 はテストを16プロセスで並行して実行するように指示しています(プロセス数はお使いのCPUに合わせて調整してください)。

    ./Script.test以降が各テストプロセスで実行するコマンドになります。--vはログレベルの指定です(harnessでは2を指定します)。-geometry以降はglutInitが処理する引数になります。

    2012/8/24 補足: r2948以降、ログレベル1だとharnessに対応しなくなっています。2を指定してくださいい。

    harnessの実行が終わると(Core i7 3770のPCでは5分前後かかります)、ログファイルがディレクトリhtml4の下に生成されています。

    $ ls html4
    absolute-non-replaced-height-001.log
    absolute-non-replaced-height-002.log
    absolute-non-replaced-height-003.log
    ・・・snip・・・

    ちなみにログファイルの中身はこのような感じになっています:

    $ cat html4/absolute-non-replaced-height-001.log
    # html4/absolute-non-replaced-height-001.htm    pass
    ## render tree
    * block-level box [html] (0, 0) w:816 h:17.6783 (0, 0) m:0:0:0:0 p:0:0:0:0 b:0:0:0:0 transparent
      * block-level box [body] (0, 0) w:816 h:17.6783 (0, 0) m:0:0:0:0 p:0:0:0:0 b:0:0:0:0 transparent
        * block-level box [p] (0, 0) w:816 h:17.6783 (0, 0) m:0:0:0:0 p:0:0:0:0 b:0:0:0:0 transparent
          * line box (0, 0) w:581.614 h:17.6783 (0, 0) m:0:0:0:0
            * inline-level box (0, 0) w:581.614 h:17.6783 m:0:0:0:0 p:0:0:0:0 b:0:0:0:0 "Test passes the a filled blue square touches the upper-left corner of the black box." #000000
        * block-level box [anonymous] (0, 17.6783) w:816 h:0 (0, 0) t:0 m:0:0:0:0 p:0:0:0:0 b:0:0:0:0 transparent
          * line box (0, 17.6783) w:0 h:0 (0, 0) m:0:0:0:0
            * block-level box [div] (0, 96) w:288 h:192 (0, 0) m:0:0:0:0 p:0:0:0:0 b:3:3:3:3 transparent
              * line box (3, 99) w:0 h:0 (0, 0) m:0:0:0:0
                * block-level box [div] (3, 99) w:96 h:96 (0, 0) m:0:0:0:0 p:0:0:0:0 b:0:0:0:0 #0000ff

    1 行目に実装レポートの形式で結果が記述されていて、2行目以降にScript.testから出力されたレンダー ツリーのダンプが書き込まれています。

    なおharnessが保存したり比較したりするレンダー ツリーはloadイベントを処理した直後のツリーになっています。ですので、ページのロードが完了した後でさらにマウスで何か操作したりしないといけないようなテストについてはharnessで完全にテストできているわけではない、という点は注意が必要です。

    自動テストの実行と確認


    ログファイルの準備ができたので、実際にテストしてみます:

    $ ./harness -j16 ~/es/trunk/escort/testlog/css2.1/20110323/implement-report-Escort-Fedora.data ./Script.test ~/es/trunk/escort/testdata/default.css --v=2 -geometry +0+0

    harnessをテスト プロセス数を指定する-j16以外のオプションなしで実行すると、比較テストを行います。harnessの戻り値は、何も変化がなければ0に、変化があれば1になります。

    $ echo $?
    0

    またテスト結果は、report.dataというファイルに記録されています。何か変化があったテストでは結果が"uncertain"に、実行中にクラッシュしたりタイムアウトしたりしたテストでは結果が"fatal"と記されています。

    "uncertain"となったテストを確認したい場合は、まず

    $ grep '#\|uncertain' report.data > uncertain.data

    のようにして、 表示に変化のあったテストだけを抜き出します。

    補足: grepで#も含めているのは、harnessは#からはじまる行をディレクテブ(命令)として処理していて、指定されたユーザースタイル シートを読み込んだり、テスト用のフォントの有効・無効を切り替えたりしているためです。

    続いて以下のように、

    $ ./harness -i uncertain.data ./Script.test ~/es/trunk/escort/testdata/default.css --v=2 -geometry +0+0

    harnessを -i オプションをつけて実行すると、テストがひとつひとつ表示されて、コマンド プロンプトから結果を再度入力していくことができます。

    [html4/absolute-non-replaced-height-001.htm] pass?

    このとき単に[Enter]を押せば、ログファイル中に記録されてい判定(この例ではpass)のままログのレンダーツリーだけが更新されます。それ以外の判定を入力する場合は、判定内容を直接タイプするか、以下のような短縮形が使えます:

    p   pass    成功
    f   fail    失敗
    i   invalid テスト自体が無効
    n   na      テストしている機能が未実装
    q   quit    harnessを終了
    z   undo    ひとつ前のテストに戻ってテストをやり直す

    また未実装の機能を利用しているためにテストが失敗しているのであれば、短縮形ではなく、

    [html4/replaced-intrinsic-001.htm] ? na (svg)

    といった具合に入力しておくとあとで調査しやすくなります。

    ブラウザの修正中は、このようにいくつかのテストをピックアップして、それらを集中的にテストして進めていくことが多いのですが、それによって思わぬ他のテストが崩れていたりする場合もあるので要注意です。もっと大規模なプロジェクトの場合は、テスト結果が(悪い方向に) 変わるようなパッチをコミットできないようにしたりすることもあると思いますが、ESプロジェクトではいまのところそこまで厳格なことはしていなくて、リグレッションはリグレッションで別パッチで後でまとめて修正したりしているのはこれまで報告してきた通りです。

    付録: harnessのコマンドライン オプション


    harnessコマンドには、-g, -i オプションといったオプション以外にも、次のようなオプションがあります。

    -g   レポートファイルの判定を元にログファイルを生成します。
    -i   テストをひとつひとつ実行して、結果を入力していきます。
    -r   ログファイルからreport.dataを再生成します。
    -u   ログファイル中のレンダーツリーのダンプを指定したScript.testからの出力に置換します。

    -u は、レンダーツリーのダンプ内容自体を変更したりした場合に利用します。

    まとめ


    今回はescortウェブ ブラウザのテストを自動化するテスト ハーネスについて紹介しました。harnessプログラム自体もプロジェクトの進展とともに進化している部分もあるので、また何か新しいことがあればこのブログからもお知らせしていきます(データセンターがあればテストに16プロセスなんてケチなことは言わずに、1,024プロセスでテスト時間5秒みたいな感じになりますよね)。

    それから一般的にはいわゆる標準規格のテスト スイートは、時間と労力をかけて独自に開発したり、あるいはお金を払ってテスト スイートごと購入したりしなければならないことが少なくありません(数百万円〜/年、数千万円〜/年のようなイメージです)。CSS 2.1 Conformance Test Suiteのような規模なものが無償で手に入るというのは実はすごいことです。もし仮に一律1ページ5,000円だったとしたら、・・・やめときませう f^_^;;

    次回は計画中のCSSレンダリング エンジンの改良などについて報告していく予定です。

    2012年8月18日土曜日

    ESウェブブラウザ通信 - Ubuntu版のリリースに向けて

    これまでescortウェブ ブラウザのバイナリ パッケージはFedora版のみ公開してきました。次のバージョン0.2.3からはUbuntu用のdebパッケージも公開する予定です。今回は、今後の参考のためにdebパッケージを自分で作成して公開する際の手順をまとめておきます。

    FedoraとUbuntuの違いは、いろいろあると思いますが、まず利用しているパッケージの形式がFedoraはrpm、Ubuntuはdebになります。それからファイルシステムの構成方法は、どちらもFilesystem Hierarchy Standard (FHS)に基づいているのですが、Ubuntuが標準のまま利用しようとしている感じなのに対して、Fedoraは、
    The FHS document is the authoritative reference to any FHS-compliant file system, but the standard leaves many areas undefined or extensible.
    としていて多少違いがあります。escortブラウザではr2930から、Fedoraだと/usr/libexecディレクトリを利用している部分は、Ubuntuでは/usr/libを利用するように修正しています。

    注意: これまでにUbuntu上でescortをソースからビルド、インストールされていて、もし/usr/libexecの中にesrilleというディレクトリだけがある場合は、/usr/libexecごと削除してr2930以降にアップデートしてください。

    この記事では、これ以降はご自分でdebパッケージを作成する必要がなければ、特にescortについての新しい情報は含まれていません。

    escortのdebパッケージの作成手順


    ツールのインストール


    Fedoraではrpmbuildやmockというツールを使ってパッケージを作成しましたが、Ubuntuではdebhelperpbuilderといったツールを使いますので、予めインストールしておきます:

    $ sudo apt-get install ubuntu-dev-tools  # pbuilder
    $ sudo apt-get install debhelper         # dh

    debianディレクトリの準備(dh_make コマンド)


    rpmを構築するためにはSPECファイルを作成しましたが、debの構築ではdebianというディレクトリの下にいくつかの設定ファイルが必要になります。r2932以降で必要なファイルがソースツリーに予めコミットしてあります。

    はじめてdebianディレクトリを準備する場合は、make distで作成したtarballを使って、そのひな形をdh_makeコマンドで用意することができます:

    $ sudo apt-get install dh-make
    $ tar zxvf esidl-0.2.0.tar.gz
    $ cd esidl-0.2.0
    $ dh_make -e info@esrille.org --copyright apache \
    -f ../esidl-0.2.0.tar.gz --native

    上記のdh_makeコマンドの引数の内容は以下のとおりです:

    -e info@esrille.org # パッケージをesrille.orgが提供していることを示します
    --copyright apache  # apacheライセンスのオープンソースプログラムであることを示します
    --native            # esidl-0.2.0.tar.gzがはじめからdebianに対応したtarballであることを示します

    debianディレクトリ内のファイルはrpmのSPECファイルで記述したのと同様の内容をいくつかのファイルに分けて記述したような感じになっています。詳細は、『Debian 新メンテナーガイド』などを参考にしてください。SPECファイルもそうでしたが、esidlやescortのようにautotoolsを使っているプロジェクトであれば、特に複雑な設定は必要なさそうです。

    パッケージに署名するための鍵の準備


    注意: 自分でパッケージに署名する必要がなければ、キーリストへの鍵の登録だけを行います。

    pbuilderを利用するときにはパッケージに署名をしておかないとその他の設定がいろいろと面倒になるところがあるので、GnuPGを使って鍵を生成しておきます。

    $ gpg --gen-key

    GnuPGの詳しい使い方は『GnuPGの使い方』などを参考にしてください。esrille.orgから配布するパッケージにはバージョン0.2.3からは、
    Esrille Inc. (0.2) <info@esrille.org>
    の鍵で署名していく予定です。ニックネームは今のところ(メージャー バージョン.マイナー バージョン)表記としています。

    なお、1箇所注意しておいたほうがよいのは、 署名に使うためにgpgで作成した鍵とdebian/changelogファイル中の、
    Firstname Lastname (nickname) <user@domain.tld>
    の部分はまったく同じでないといけない、という点です(参考: debsign: clearsing failed: secret key not available)。

    鍵を作り終わったら、作成した鍵を使って署名したパッケージをaptコマンドで認証できるように、 公開鍵をaptのキーリストに登録しておきます。公開鍵を、

    $ gpg -a --export userid

    で表示して、それを、

    $ apt-key add -
    ...公開鍵のテキストを貼り付ける...
    ^D  # EOFを入力

    のようにすれば、キーリストに鍵を登録できます。登録されている鍵は、

    $ apt-key list

    で確認できます。

    補足: ソースパッケージの認証は~/.gnupg/trustedkeys.gpgを参照するようで、

    $ gpg --no-default-keyring --keyring trustedkeys.gpg \
    --import

    で登録できます。

    ローカルのdebレポジトリの準備


    escortのパッケージのビルドで少し面倒な所は、escortの前にesidl Web IDLコンパイラのパッケージをビルドして、さらにそれを利用してescortのパッケージをビルドしないといけない点です。

    こういった場合、pbuilderでは、ローカルにdebパッケージのレポジトリを用意しておいて、そこにesidlのパッケージを登録しておき、escortをビルドするときにはローカルのレポジトリからesidlを取得してくる、という方法がPbuilderTricksに紹介されています。

    そこで、pbuilderのセットアップを行う前にロカールのdebレポジトリを準備しておきます。
    HowToSetupADebianRepositoryではいろいろなdebレポジトリの構築方法が説明されていて迷ってしまうのですが、ここではapt-ftparchiveを使った方法をまとめておきます。

    まずはこちらなども参考にレポジトリのルート ディレクトリを決めます。 ここでは、
    ~/ftparchive
    を使うことにしておきます。そうしたら、その下にさらに以下のようなディレクトリ構造を作ります:

    ~/ftparchive
      + conf
      + db
      + dists
      + hook  # このディレクトリはpbuilderが参照します
      + pool
          + precise   # 12.04 LTS (Precise Pangolin)用のプール

    ここでは、Ubuntu 12.04(Precise Pangolin)用のパッケージだけを準備しています。confの中には以下の2つの設定ファイルを用意しておきます。

    $ cat ~/ftparchive/conf/apt-ftparchive.conf
    Dir {
        ArchiveDir       ".";
        CacheDir         "db";
    }

    Tree "dists/precise" {
        Sections         "main";
        Architectures    "i386 amd64 source";
        Directory        "pool/precise";
        SrcDirectory     "pool/precise";
    }

    Default {
        Packages {
            Extensions   ".deb";
        };
    };

    $ cat ~/ftparchive/conf/precise-release.conf
    APT::FTPArchive::Release::Origin "Esrille Inc.";
    APT::FTPArchive::Release::Label "ES operating system";
    APT::FTPArchive::Release::Suite "unstable";
    APT::FTPArchive::Release::Codename "precise";
    APT::FTPArchive::Release::Architectures "i386 amd64 source";
    APT::FTPArchive::Release::Components "main";
    APT::FTPArchive::Release::Description "Packages for Ubuntu 12.04 from Esrille";

    ここまで準備ができたら、あとはpbuilderが生成したファイルをpool/preciseの中にコピーして、以下のようなスクリプトを実行すると、~/ftparchive/distsの中にレポジトリに必要なファイルが生成されます:

    $ cat  ~/ftparchive/update-archive.sh
    #!/bin/sh
    rm -rf db
    mkdir -p db
    rm -rf dists
    mkdir -p dists/precise/main/binary-amd64
    mkdir -p dists/precise/main/binary-i386
    mkdir -p dists/precise/main/source
    apt-ftparchive generate conf/apt-ftparchive.conf
    apt-ftparchive -c conf/precise-release.conf release dists/precise > dists/precise/Release
    gpg -abs -o dists/precise/Release.gpg dists/precise/Release

    このupdate-archive.shスクリプトはあとで利用するのでここで作成しておきます。

    pbuilderのセットアップ


    pbuilderはFedoraのmockのUbuntu版のような感じで、やはりchroot環境でビルドすることでパッケージの依存関係の誤りなどを発見しやすくしてくれるものです。まずは、chroot環境を構築します。

    $ pbuilder-dist precise create

    'precise'はUbuntu 12.04 (Precise Pangolin) 用のパッケージをビルドすることを指定しています。ホストのOSが64ビット版(amd64)であれば、以下のような具合に32ビット(i386)のchroot環境も操作できます。

    $ pbuilder-dist precise i386 create

    続いて先ほど作成したロカールのdebレポジトリにpbuilderがアクセスできるように設定していきます。まず、以下のような~/.pbuilderrcファイルを作成します(userとなっている箇所は適当に置き換えてください)。

    $ cat ~/.pbuilderrc
    OTHERMIRROR="deb file:///home/user/ftparchive precise main"
    BINDMOUNTS="/home/user/ftparchive"
    HOOKDIR="/home/user/ftparchive/hook"
    EXTRAPACKAGES="apt-utils"

    また$HOOKDIRに以下のようなファイルD05deps作成します(変わったファイル名ですが名前の付け方に規則があるので変更しないように):

    $ cat  ~/ftparchive/hook/D05deps
    #!/bin/sh
    apt-get update

    以上の設定をpbuilderに反映させます:

    $ pbuilder-dist precise update --override-config \
    --configfile ~/.pbuilderrc

    更にesidlパッケージの署名をpbuilder内でも認証できるようにしておかないといけないので、署名に使う鍵の公開鍵をpbuilderの環境にも保存しておきます(参考):

    $ pbuilder-dist precise login --save-after-login
    # apt-key add -
    ...公開鍵のテキストを貼り付ける...
    ^D  # EOFを入力
    # もう一度^Dを押してログアウト

    先ほどホスト側で行ったのと同じ処理をchroot環境内でも予め実行しておくわけです。

    これでpbuilderのセットアップは完了です。

    ソースパッケージの作成 (esidl)


    次に、esidlのtarballを展開して、ソースパッケージだけを作成します。

    $ cd esidl-0.2.3
    $ dpkg-buildpackage -S
    $ cd ..
    $ ls -F
    esidl-0.2.3/
    esidl_0.2.3.dsc
    esidl_0.2.3_source.changes
    esidl_0.2.3.tar.gz
    esidl-0.2.3.tar.gz

    esidl_0.2.3(途中はアンダースコア)ではじまるファイルがdpkg-buildpackageコマンドの作成したファイルで、pbuilderが参照するファイルになります。(mockでもrpmbuildでソースパッケージだけ先に作ったのと似ています。)

    なお、ソースツリーでdebianディレクトリ内のファイルを変更したときは、この手順から実行し直す必要があります。

    debパッケージの作成 (esidl)


    続けて、pbuilderを使ってesidlのdebパッケージを構築します。

    $ pbuilder-dist precise build esidl_0.2.3.dsc

    実行が完了すると、~/pbuilder/precise_result の中にdebパッケージが作成されています:

    $ cd ~/pbuilder/precise_result
    $ ls
    esidl_0.2.3.dsc
    esidl_0.2.3_amd64.changes
    esidl_0.2.3_amd64.deb
    esidl_0.2.3.tar.gz

    この状態ではまだパッケージに署名がされていないので、署名しておきます。

    $ debsign esidl_0.2.3_i386.changes

    ローカルdebレポジトリの更新


    次に生成されたesidlのdebパッケージをローカルレポジトリに登録します:

    $ cd ~/ftparchive
    $ cp ~/pbuilder/precise_result/esidl_0.2.3* pool/precise
    $./update-archive.sh
     pool/precise: New 1,347 B 1 files 202 kB 0s
     pool/precise: New 20 B 0 files 0 B 0s
     pool/precise:  esidl has no source override entry
      esidl has no binary override entry either
     New 1,324 B 1 pkgs in 0s
    Packages done, Starting contents.
     dists/precise/Contents-amd64: New 220 B 1 files 202 kB 0s
     dists/precise/Contents-i386: New 20 B 0 files 0 B 0s
    Done. 202 kB in 1 archives. Took 0s

    update-archive.shの最後のステップでReleaseに署名しておくのがポイントです(参考: SecureApt - All about secure apt)。こうしておくと、あとでescortのdebパッケージをビルドする際に、pbuilderがesidlのパッケージを無事にインストールできます。ちなみに署名がないと、

    Untrusted packages could compromise your system's security.
    You should only proceed with the installation if you are certain that
    this is what you want to do.

      esidl

    Do you want to ignore this warning and proceed anyway?
    To continue, enter "Yes"; to abort, enter "No": Abort.
    E: pbuilder-satisfydepends failed.

    となって、pbulderが強制終了されてしまいます(これはこれで直す方法があるようですが、署名してしまっておいた方が簡単な気がします)。

    これでようやくescortのdebパッケージをビルドする準備ができました。今後、esidlには変更がなくて、escortだけパッケージを構築し直したい場合には次のステップから始めれば大丈夫です。

    debパッケージの作成 (escort)


    いよいよescortのdebパッケージをビルドしていきます。手順はesidlの場合と同じです。

    $ tar zxvf escort-0.2.3.tar.gz
    $ cd escort-0.2.3
    $ dpkg-buildpackage -S
    $ cd ..
    $ ls -F
    escort-0.2.3/
    escort_0.2.3.dsc
    escort_0.2.3_source.changes
    escort_0.2.3.tar.gz
    escort-0.2.3.tar.gz
    $ pbuilder-dist precise build escort_0.2.3.dsc
    $ cd ~/pbuilder/precise_result
    $ ls
    escort_0.2.3.dsc
    escort_0.2.3_amd64.changes
    escort_0.2.3_amd64.deb
    escort_0.2.3.tar.gz
    esidl_0.2.3.dsc
    esidl_0.2.3_amd64.changes
    esidl_0.2.3_amd64.deb
    esidl_0.2.3.tar.gz
    $ debsign escort_0.2.3_amd64.changes

    これでパッケージがすべて準備できました。

    実際にHTTPサーバーから公開する場合には、escortのファイルもpoolに入れて、~/ftparchiveから~/ftparchive/distsと~/ftparchive/poolの部分を公開することになります。

    まとめ


    今回はUbuntu用のdebパッケージの作成方法をまとめておきました。それから前回、次回紹介しますと書いてしまったテスト ハーネスの使い方についてはまた次回に、ということで、今回はここまでです。

    2012年8月11日土曜日

    ESウェブブラウザ通信 - テーブルの自動レイアウトの改善等

    今回は、今日リリースしたescort 0.2.2で新たに実装した機能について紹介します。escort 0.2.2では、CSS 2.1の実装の改善を主な目的として開発を進めきました。CSS2.1 Conformance Test Suiteの達成率は90.8%とescortではこれまででベストの結果となっています。

    link要素のmedia属性


    r2870では、link要素のmedia属性が未サポートだったので、
    &lt;link href="/includes/deed3-print.css" media="print" rel="stylesheet" type="text/css">
    といった印刷メディア用のスタイルシートも一緒にスクリーン上で処理してしまっていました。 下図は r2870でCreative Commons のライセンスの要約のページを表示した際のスクリーンショットです。
    r2870 (link要素のmedia属性未対応)
    r2871, r2872でlink要素のmedia属性に対応しています。
    r2871 (対応済み)
    なお、今回の実装は CSS 2.1 のメディア リストの仕様に基づいています。メディア リストは CSS 3では、メディア クエリーとなって強化されている部分ですが、escortではいまのところ未対応です。

    テーブルの自動レイアウト


    テーブルの自動レイアウト アルゴリズムは、CSS 2.1の仕様書17.5.2.2では、
    UAs are not required to implement this algorithm to determine the table layout in the case that 'table-layout' is 'auto'; they can use any other algorithm even if it results in different behavior.
    となっています(≒未定義)。現状ではCSS標準のレイアウト アルゴリズムがないので、何か特定の動作に期待して'table-layout'プロパティが'auto'に設定されている表をレイアウトに使わないこと、というのがよく言われているお約束の趣旨かな、と思います。

    そのためこれまでのescortのテーブルの自動レイアウト アルゴリズムはかなり手を抜いたものになっていて、Google CodeのES operating systemのページはr2872では以下のような描画になってしまっていました。
    r2872
    Google Codeのこのページでは、左側のリンクばかりのカラムの幅はautoに、右側のカラムの幅は100%(表全体の幅に対して100%という意味)に設定されています。ですのでnon-normative(参考)としてCSS 2.1の仕様書に載っているアルゴリズムに倣うと、左側のカラムは最小限必要な幅だけを取って、残りを右側のカラムが(幅がなるべく100%に近づくように)全部専有する感じになります。

    このとき『最小限必要な幅』というのはとりあえずレイアウトしてみないことにはわからないので、参考アルゴリズムではレイアウト処理が1パスでは終わらなくて、仕様書にも"This algorithm may be inefficient"と記載されています。

    escort 0.2.2では、CSS 2.1の参考アルゴリズムを参考に、Google Codeのページくらいはそれなりにレイアウトできるように実装を改良することにしました。
    r2873
    r2873では、自動レイアウトのテーブルに2パスのレイアウト処理を取り入れました。

    1パス目では従来と同じように、テーブルを包含ブロックとして各セルをレイアウトしつつ(このとき算出されたセルの幅を"maximum" cell widthとしています)、参考アルゴリズムに記述されている各セルのminimum content width (MCW)を合わせて計算しています。MCWは行の途中で折り返すことのできない単語の最大幅などから計算できます。

    2パス目では、まずMCWをもとにMIN(最低限必要なテーブル全体の幅)を計算します。テーブル自体に指定された幅(W)がautoの場合は、簡略レイアウトで求めた幅から計算される値から参考アルゴリズムのMAXを計算しています(r2875)。

    またMIN &lt; Wの場合などには余った幅を参考アイルごリズムでは、
    If the used width is greater than MIN, the extra width should be distributed over the columns.
    と、各カラムに分配するように記述されています。ところがこの1文の解釈が実は結構大変なところでした。r2873では、ひとまず幅が明示的に固定値で指定されているカラムには分配しないようにしてありました。

    このようにして各カラムの幅がきまったら、各セルをカラムの幅に合わせて再度レイアウトしていきます。

    すこしリビジョンが進んで、r2888ではinput要素のsize属性に対応しています(右上の方にあるテクストボックスの幅が広がっています)。
    r2888
    r2888では、テキストボックスの幅が広がった分、es-operating-systemというテキストを含んでいるセルの幅が不足してテキストが途中で折り返されるようになってしまいました。
    r2889
    r2889では、自動テーブルレイアウトのアルゴリズムを改良して、余った幅は、まだmaximum column widthに達していないカラムに優先して分配して、それでも余っていたら各カラムに均等に分配、という2段階で分配するようにしました。これで"es-operating-system"の部分も無駄に改行されずに済むようになりました。

    ただr2889では『各カラムに均等に分配』という処理のために、Summary Peopleというリンクがあるセルの右隣にあるMCWが0のセルの幅も増やしてしまっていました。この点については、幅0のセルは0のままにしておく、というのが各メジャー ブラウザでの暗黙のルールの様子です。
    r2890
    r2890では、幅0のセルは0のままにしておくように修正しました。Summary Peopleというリンクがあるセルの下線がウィンドウ幅いっぱいに広がっているのに気づくでしょうか?

    CSS 3では、 標準のテーブル自動レイアウト アルゴリズムの策定が期待されるところですが(*)、escortブラウザでは、当面は更に凝ったテーブルの自動レイアウト処理が必要かどうか他のサイトなども見ながら確認していきたいと思います(David Baronさんも詳細を検討されている途中のようです)。

    (*) CSS3 BOXに、
    [CSS3TBL]
    Bert Bos; David Hyatt. CSS3 Tables Module. (forthcoming). W3C Working Draft. (Work in progress.)
    という記載が見られます。

    ドキュメント タイトルの空白のつぶし


    r2873のウィンドウのタイトルバーを見てみると、"es-operating-system -     ES operating system..." のように余分な空白が見えます。
    r2873
    HTMLファイル中ではこの部分は改行コードなどが入っていたりするのですが、それがそのまま表示されてしまっていました。

    HTML Living Standardの3.1.4では、documentからtitleを取得するときは、
    Replace any sequence of one or more consecutive space characters in value with a single U+0020 SPACE character.
    と規定されているので、r2874で対応しています。
    r2874
    ちなみに、HTMLTitleElement要素のtext属性を取得する場合は、文字列の前後以外の空白のつぶしは行わないのが仕様のようです。

    テーブルの絶対配置


    escort 0.2.1以前はテーブルの絶対配置に未対応だったのですが、r2879からr2884にかけてCSS2.1 Conformance Test Suiteを使ってテストしつつ対応しました。
    table-percent-width-001 (r2879)
    r2879ではテーブルの高さの処理に問題があって、テストスイートのabspos-006などで問題がありました。 PASS という文字はページの右下に表示されないといけないのですが、テーブルの高さがビューポートの高さに設定されてしまっていました。
    abspos-006 (r2879)
     r2880で問題点を修正しています。
    abspos-006 (r2880)
    r2881では、仕様書14.2のキャンバス全体をルート要素の背景色で塗りつぶすという処理にバグがありました。
    abspos-containing-block-initial-004c (r2881)
    r2882で修正しています。
    abspos-containing-block-initial-004c (r2882)
    abspos-containing-block-initial-004eは、ルート要素のスタイルに"display:table"が設定されているという嫌な感じのテストです。
    abspos-containing-block-initial-004e (r2882)
    r2882の段階では、匿名のセルに誤ってスタイルを設定してしまっていたので、セルが絶対配置されているようなおかしな状態になってしまっていました。
    r2883
    r2883でその点を修正していますが、背景色が黄色になっていません。キャンバスの色はbody要素から来ているので、rootで再計算した値をそのまま使ってしまうとデフォルトの色になってしまっていたのでした。
    r2884
    r2884でひとまず修正しましたが、この処理は想像よりも面倒でr2912でようやく実装が落ちつきました。

    リグレッションの修正


    テーブルの自動レイアウトでは各セルについて2パスでリフローを実行したり、といったこともあってリグレッションがいろいろと発生して、一時的にCSS 2.1 Conformance Test Suiteの達成率も3月末の90.1%から84%くらいまで落ちてしまっていました。r2890以降r2919までリグレッションの修正を続けて達成率を90.8%まで復旧させました。修正内容については、escort 0.2.2のTODOリストChangeLogを参考にしてください。

    まとめ


    今回は、escort 0.2.2で新規に実装した下記の機能について紹介しました。
    • link要素のmedia属性の処理
    • 自動テーブルレイアウトアルゴリズムの実装
    • 絶対配置されたテーブルのレイアウト機能の実装
    • Document要素のtitle属性取得時の空白のつぶしの実装
    特に自動テーブルレイアウトアルゴリズムの実装では、まだ標準仕様がないために実装にあたっては他のブラウザのレイアウトと見比べながら改良していくということで結構手間取りました。

    自動テーブルレイアウトはもうあまりにも多く(レイアウト目的に)使われすぎてしまっていて、今後は標準仕様を詰めていくしかないのだろうと思います。けれどもロンドン オリンピックの開会式にも登場されていたTim Berners-Leeさんの"Principles of Design"でも引用されているKISSの原則
    "Keep it simple, stupid!"
    には自動テーブルレイアウトは合ってない感じがします。特定のブラウザだけに非常に複雑な機能を実装してみて、他のベンダーも合わせて、と言うのではなくて、ものすごい単純だけどこれ便利だね、ということで他のベンダーもすぐにコピーしていくような発展の仕方の方がウェブらしいかな、と。

    次回のESウェブブラウザ通信では、harnessコマンドを使ったescortブラウザのテスト方法について紹介したいと思います。またescortの方は、今後レンダーツリーの部分リフロー処理の実装をはじめて行く予定です(今回も実はテーブルの各セルについては部分リフローとほぼ同じ処理を行なっているわけですけれども)。部分リフローまでできるようになれば、escortのCSSレイアウトエンジンの骨格部分は固まってくる感じになると思います。

    というわけで、今回はここまでです。

    2012年7月24日火曜日

    ESウェブブラウザ通信 - escort バージョン 0.2.1 公開


    escort バージョン 0.2.1 を Google Code から公開しました。このバージョンは先日公開したバージョン 0.2.0 のバグフィックス版です。また今回から Fedora 用の yum レポジトリを合わせて用意しました。Fedora に関しては、これまでよりも簡単にインストールしたりアンインストールしたりできるようになっていると思います。

    修正内容


    主な修正内容は、
    • URL, URIの処理周りのバグ修正(r2854, r2857)
    • HTTPキャッシュシステムのバグ修正(r2858, r2861-r2865)
    • 子ウィンドウのクリップ処理のバグ修正(r2859)
    です。

    r2859では、iframe 中のクリッピング処理が正しく行えていなかった問題を修正しました。これによって、Google の検索結果のページの表示が崩れたりしていた問題などが修正されています。
    r2858 (子ウィンドウのクリッピング処理が崩れていました)
    r2859 (修正済)
    r2864では、HTTPリダイレクトに対応しています。いままで日本から www.google.com にアクセスすると、302 Movedの画面が表示されて自分でリンクをクリックして www.google.co.jp にアクセスし直さないといけなかったのですが、r2864ではescortブラウザが自動的にリダイレクトするようになりました。
    r2863 (HTTPリダイレクトの未対応)
    r2864 (自動的に www.google.co.jp にジャンプします)

    yum レポジトリの利用方法


    escort ブラウザ等、ES オペレーティング システム プロジェクト用のレポジトリ サイト http://download.esrille.org/ を用意しました。現在は Fedora のみの対応ですが、今後他のディストリビューションにも対応していければと考えています。

    この yum レポジトリを利用する場合は、root で、

    # yum-config-manager --add-repo http://download.esrille.org/fedora/esrille.repo

    のように実行してレポジトリをお使いのコンピューターに追加してください。

    一度、レポジトリを追加しておけば、コマンドラインから yum を使って、

    # yum install escort


    のように escort ブラウザをインストールしたりアップデートしたりすることができます。GUI版の Apper (KDEの場合)のようなツールを使ってインストールすることもできます。
    Apper から escort をインストール

    yum レポジトリからパッケージを公開するまでの作業手順 (参考)


    参考までに Fedora 用のパッケージを yum レポジトリから公開するまでの作業手順を紹介しておきます。

    前々回の ES ウェブブラウザ通信では、一般的な rpm パッケージを作る方法についてまとめましたが、Fedora の場合は直接 rpmbuild コマンドを使うのではなくて、Mock というツールを利用することができます。Mock を使うと、SPEC ファイルに記述した依存関係に誤りがないかをチェックしたり、利用している PC とは別のアーキテクチャ用のパッケージを簡単に作ったりすることができます。

    1) tarball を作る

    ビルドディレクトリで、

    % make dist

    を実行します。ビルドディレクトリ中に escort-0.2.1.tar.gz のような tarball が作られます。escort の tarball には SPEC ファイルが含まれているので、この tarball を使って RPM ファイルを作れる、というところまでは前々回説明した通りです。

    2) ソース RPM パッケージを作る

    Mock を使う場合はソース RPM パッケージが必要になります(その他のパッケージは Mock が作成します)。

    % rpmbuild -ts escort-0.2.1.tar.gz

    -ts オプションを指定して rpmbuild コマンドを実行すると、~/rpmbuild/SRPMS の中に escort-0.2.1-1.fc17.src.rpm のようなソース RPM パッケージが作られます。ソース RPM パッケージを作るだけなのでこのコマンドの実行はすぐに完了します。

    3) mock レポジトリの初期化

    % mock --init -r fedora-17-i386

    x86_64用のパッケージを作る場合は、 fedora-17-i386 の部分を fedora-17-x86_64 に置き換えます。これで /var/lib/mock/fedora-17-i386/root の中にまっさらな Fedora 17 の chroot が作られます。(はじめて実行する場合は、すこし時間がかかることがあります。)

    4) chroot に esidl をインストールする

    % mock -r fedora-17-i386 --install esidl-0.2.0-1.fc17.i686.rpm

    escort のビルドには esidl が必要になるので、これだけはあらかじめインストールしておきます。(esidl-0.2.0-1.fc17.i686.rpm は現在配布中のものです。)

    補足: esidl もソース RPM からビルドする場合は以下のようにします:

    % mock -r fedora-17-i386 rebuild esidl-0.2.0-1.fc17.src.rpm

    ディレクトリ /var/lib/mock/fedora-17-i386/result の中に esidl の RPM パッケージが生成されています。

    5) escort をビルドする

    % mock -r fedora-17-i386 --no-clean rebuild escort-0.2.1-1.fc17.src.rpm

    --no-clean オプションを使って、esidl を残したまま、escort をビルドします。 mock が完了すると、ディレクトリ /var/lib/mock/fedora-17-i386/result の中に escort の RPM パッケージが生成されています。

    % ls /var/lib/mock/fedora-17-i386/result
    build.log
    escort-0.2.1-1.fc17.i686.rpm
    escort-0.2.1-1.fc17.src.rpm
    escort-debuginfo-0.2.1-1.fc17.i686.rpm
    root.log
    state.log

    依存しているパッケージが増えているのに SPEC ファイルがそれに合わせて更新されていなかったりするとビルドに失敗するので、その場合は SPEC ファイルを修正して 1) のステップからやり直します。

    6) ローカルに yum レポジトリを作る

    インターネットからパッケージを公開する前に、ローカルに yum レポジトリを作っておきます。

    % cp /var/lib/mock/fedora-17-i386/result/*.rpm /var/yum
    % cp /var/lib/mock/fedora-17-x86_64/result/*.rpm /var/yum
    (/var/yum は一例です。ローカルなルールに従って置き換えてください。)

    createrepo コマンドを実行して、レポジトリのメタデータを更新します。

    % createrepo /var/yum

    /var/yum/repodata の中にメタデータが生成されます。

    補足: 下のように local.repo のようなファイルを作成して、/etc/yum.repos.d ディレクトリに置いておくと、この /var/yum レポジトリを使ってテストすることもできます。

    [local]
    name=local yum
    baseurl=file:///var/yum
    enable=1
    gpgcheck=0

    7) ローカルの yum レポジトリをウェブ サーバーにコピーする。

    この手順はお使いのウェブ サーバーの管理マニュアル等を参考にしてください。download.esrille.org は今のところ Google AppsGoogle Sites を使って運営しています。Google Sites を使ったファイルのアップロード方法はこちらを参考に(ファイル キャビネットを使うのが簡単だと思います)。

    8) repo ファイルを公開する。(はじめて公開するときなど)

    yum レポジトリの中に .repo ファイルを合わせて置いておくと、ユーザーが yum-config-managerコマンドを使って簡単にレポジトリを追加することができて便利です。download.esrille.org では以下のような esrille.repo ファイルを置いてあります。

    [esrille]
    name=Esrille Inc.
    baseurl=http://download.esrille.org/fedora
    enabled=1
    gpgcheck=0 

    参考: 4.3.5, Adding, Enabling, and Disabling a Yum Repository, Fedora Documentation.

    まとめ


    escort バージョン 0.2.1では 0.2.0 版に残っていた基本的なバグをひとまず修正しました。次回からはバージョン 0.2.2 に向けて CSS の実装の改善などを進めていく予定です。