2011年9月28日水曜日

ESウェブブラウザ通信 - CSS 2.1 Test Suite #1

前回はESウェブブラウザがAcid1テストをパスするようになったところまで進めました。その後は、CSS オフィシャル W3C テスト スイートを使って、ESウェブブラウザでこれまで実装してきた部分のテストを進めています。今回は、テストスイートのChapter 1, Chapter 2と、Chapter 8の8.3.1 Collapsing marginsの手前までで、ESブラウザで実装が入っている部分のテストについてまとめていきます。

CSSの実装にあたっては、CSSの仕様書を読みながら進めてきたわけですが、仕様書の文章だけからではどうしても解釈違い、文章の見落としなどが重なって、実装が仕様通りになっていない、という問題が発生しがちです。(こういった問題はCSSに限らずTCP/IPプロトコルなどでも緊急ポインタの位置の解釈などで問題を起こしていましたよね。)

CSS オフィシャル W3C テスト スイートは、こういった問題がなるべく起きないように、仕様書の編集者やブラウザの開発者などが作成したテストページをまとめたものです。特にCSS 2.1に関してはマイクロソフト社から提供されたページが7,000ページ以上になるとのことで、ESブラウザの開発にあたっても有用なテストスイートになっているように思います。
The only way to know if a browser has correctly implemented a specification is to develop a comprehensive set of tests for the specification.
Jason Upton, Test Manager, Internet Explorer

テストスイートの一番最初のテストです。shand-border-000は、border-colorの計算値(computed value)は、border-colorが未指定の場合は要素のcolorプロパティの計算値を使用する、という規則になっているのを実装できているかどうかテストしています。

正しくブラウザが実装されてれば、テキストのまわりのボーダーは緑色で表示されるはずなのですが、r1963では以下のとおり黒色で表示してしまっていました。(このセクションの見出しの部分を最近のCSS 2.1に準拠しているブラウザで表示すれば正しい結果が見られるはずです。)

r1963
r1963では、border-colorは文法的には通常のcolorと同じなので、実装でもcolor用CSSColorValueImpクラスを使っていたために上記のような結果になっていました。
r1964では、新たにCSSBorderColorValueImpクラスを導入して、この問題を修正しました。

r1964


このテストはHTMLのhtml要素とbody要素に別々に'background'の指定をした場合の動作をテストしています。

r1965

このr1965の状態でもテストが本来テストしている部分に関してはokなのですが、テキストの上下に紺色の部分があるのは正常ではないので、ついでに修正していきます。

r1966では、 CSSの"White space content that would subsequently be collapsed away according to the 'white-space' property does not generate any anonymous inline boxes."という規則の実装が入っていなかったのを追加しました。

r1966

テキストの下側の紺色の部分がなくなっています。HTMLソースファイル中の以下の部分、
 page. Around it on all sides should be purple.</p>
</body>
は、DOMツリーに展開されると、改行だけ含んだテキストノードがbodyの最後の子ノードとして追加されます。本来は、このDOMツリーがCSSのレンダーツリーに変換されるときに、'white-space'の設定に従って改行文字は消されるので、これを含んだ匿名ボックスも生成されないという具合になります。

ESウェブブラウザの実装では、匿名、非匿名に関わらずブロックレベルボックスだけを先にすべて展開してしまって、後から個々のブロックレベルボックス内のラインボックスなどをレイアウトしていく、という具合になっています。そのため、r1965ではこの不要な匿名ボックスの分だけ下側に紺色の部分が表示されていたのでした。そこで、1966では先に生成してしまった不要な匿名ボックスを後から削除するという処理を行っています。

r1968では、 マージンのつぶし処理を修正して、なるべく外側(ツリーのルート側)のボックスにマージンを残すようにしました。ボックスの座標配置自体はそれまででもよかったのですが、マージンを内側のボックスにまとめてしまっていたので、本来透明の筈の背景部分を違う色で塗ってしまっていたのでした。

r1968
これでこのテストは大丈夫そうです。

margin-backgrounds-001

続いて、3章から飛ばして、8章'Box model'の方に進みました。CSSのボックスモデルは一番基本的なところなので、この部分の実装でおかしい箇所はなるべく早めにとっておきたい、というのが理由です。

このテストは単純にマージンが背景色で塗られることなく表示されるかどうかテストするだけなのですが、r1968では、テキストの下側のマージンがなくなってしまっています。(ちなみにこのテストも最初に書いたマイクロソフト社から提供されたテストのようです。各テストとも、ソースには"author"の記載があるのでわかります。)

r1968
r1966では、不要な匿名ボックスを削除する様にしたわけですが、この不要な匿名ボックスと直前のテキストを含んだブロックボックスとの間でマージンのつぶし処理が先に行われていて、匿名ボックスを削除した時に一緒にマージンまでなくなっていたのでした。
r1970では、匿名ボックスを削除する際につぶしたマージンを元に戻すように修正しました。

r1970

このテストでは赤い線と緑の線がぴったり重ならないといけないのですが、r1970では右辺がずれています。

r1970
これは仕様の読み違いでした。左右のマージンと幅がすべて'auto'以外に設定されていると、"If all of the above have a computed value other than 'auto', the values are said to be "over-constrained" and one of the used values will have to be different from its computed value."という条件に当てはまると想定して、右のマージンの設定を無視するようになっていたのでした。
実際には、 "over-constrained"でも、包含ボックスの幅が足りないときは右マージンの値はそのままでよくて、はみ出る部分の表示は'visibility'の設定に任せればよいわけですね。こういった仕様書の読み違いもテストが用意されていればちゃんとチェックできる、という良い例だと思います。
r1972でこの問題を修正しています(r1978でもさらにこの問題の修正が追加されています)。

r1972

このテストはマージンに負の値を指定した場合の動作をテストするものです。r1972ではまだ負のマージンに対応していなかったので下記の通り表示が崩れていました。

r1972

r1973では負のマージンが指定された場合のシンプルなケースについて対応するコードを追加しました。より複雑なケースはテストスイートの§8.3.1でテストするのでここではとりあえずの修正になっています。

r1973
マージンのテストとしてはこれで良いのですが、r1973では赤い線の幅が広くなりすぎています。これはグーグルのページを表示するためにとりあえず入れておいた(正しくない)修正の所為でしたので、再度r1974で修正しています。

r1974

このテストはwidthがauto以外で包含ボックスの幅が足りないときに、"any 'auto' values for 'margin-left' or 'margin-right' are, for the following rules, treated as zero."というルールのテストになっています。r1976ではその実装が入っていなかったので、以下のような具合でした。
r1976
 r1977, r1978で対応して下記の様に正常になりました。

r1977


このテストはインラインボックスのマージンのテストです。r1979ではかなり崩れていました。
r1979
これは仕様書のどこと対応付いているのか微妙な感じもするのですが、10.6.1の"The vertical padding, border and margin of an inline, non-replaced box start at the top and bottom of the content area, and has nothing to do with the 'line-height'."でしょうか。当たり前のことを書いてあるだけのような気もしますが、ESウェブブラウザの実装であれば、ボックスを並べるときに普通は原点はマージンエッジの左上角にあるものが、インラインのボックスだとコンテンツエッジの上端とマージンエッジの左端の交点に移る感じで考えると良さそうです(Firefoxのfirebugで要素を調査したときの表示が分かりやすいと思います)。
ふつうのボックスの原点

インラインボックスの原点
r1980, r1982でこの修正を入れています。

r1980
このテストで調査している範囲としてはこれでも良いのですが、見た感じ気持ち悪いので、行末の空白を'white-space'の設定に応じて削除する処理をr1984で実装しています。

r1984


このテストは親要素でmarginをパーセントで指定しているときに、それをinheritで継承した場合の処理をチェックしています。r1984では以下の様に崩れていました。

r1984

これも仕様書の読み違いで、inheritで継承する値を計算値ではなくて、決定値(resolved value)を継承する様になっていたのでした。r1985では、この問題を修正しました。

r1985
この辺でESウェブブラウザで実装が入っている部分に関して、Microsoft社から提供された8.3.1 Collapsing marginsの手前までのマージン関連のテストについてはほぼ通るようになってきました。


CSSのテストスイートの方にはまだ難しめのテストが残っています。このテストは、HTMLのスペック エディタとしても有名なHixieが作成したテストのひとつです。

r1985
左右で同じパターンが表示されればOKなのですが、下から3ブロック目の水色の矩形の位置がずれているのと、幅が半分の白い矩形(p.ten)が表示されていないのがエラーのある部分です。(一番下側の矩形の色が左右で違うのはHTMLのtableのセルの高さの処理が完全でないためのものでマージンの処理とは無関係です。)

このテストでは白い矩形(p.ten)がfloat要素でこれを含んだブロックボックスの高さは0になります。高さ0のブロックボックスの上端と下端のマージンはつぶし合うことができるのですが、その処理が未実装でしたので、r1986でその実装をしています。

r1986
 
ただこういったマージンのつぶしのテストは 8.3.1 Collapsing margins のテストスイートに個別のものがあるような感じなので、こういった複合的なテストは後回しにしても良さそうな感じですね。

今回はここまでです。かなり基本的な部分でも仕様書を読み違えていたりして、改めてこういったテストスイートの重要性を感じます。次回も基本的なテストを中心にいま既に実装がある部分のバグ修正を中心に進めていきます。

2011年9月21日水曜日

ESウェブブラウザ通信 - Acid1テスト

前回はESウェブブラウザを使ってグーグルで検索できるようするところまで進めました。今回はESウェブブラウザがAcid1テストをパスするようになったので、その報告です。

AcidテストはウェブブラウザのWeb標準への準拠具合を調べる目安として使われているテストページです。最近では先週最新のAcid3テストが改訂されて、Internet Explorer 9.0とFirefox 6.0.2もAcid3をパスした、というニュースが流れていました。

今回ESウェブブラウザがパスするようになったAcid1は、今から13年前に作られた最初のAcidテストだそうです。ウェブブラウザがWeb標準を正しく実装していれば、Acid1テストのページは以下の様に表示されます。

リファレンス レンダリング(http://style.cleverchimp.com/boxacidtest/vd/layout.gif)

特に一番下の段落より上側の部分については、フォントとラジオボタンを除いて、ピクセル レベルでこのスクリーンショットとまったく同じように表示できなければいけません。

前回の最後に使用したr1952でAcid1を実行すると以下の様になりました。 かなり表示が崩れています。右側の"the way..."からはじまるフロート ボックスはどこへ消えたのか、テキストが左に寄ってしまっているのはなぜか、等々。

r1952
r1953では、まずフロートのボックスをレンダーツリーに入れ損ねてしまうバグを修正しました。これで最後のテキストの部分がどうして左側に寄ってしまっていたのか謎が解けました。フロートの幅の計算が間違っていて、最初の行に収まりきっていないんですね。

r1953
r1954r1955では、フロートの幅を初期包含ボックスからの割合で計算してしまっていたバグ(正しくはフロートの包含ボックス)と、右寄りのフロートの位置決めのバグをそれぞれ修正しました。

r1955

r1956では、フロートの高さを包含ボックスのheightに足し込んでしまっていたバグの修正し忘れ(7月r1811で修正した箇所)があったのを修正しています。

r1956
r1957r1959では、ラインボックスを描画する時に左側のフロートのスペース分を空けていなかったバグと、フォーマッティング コンテキストを閉じるときに完全に表示しきれていないフロートを表示するために必要な高さを考慮できていなかったバグをそれぞれ修正しました。パッと見はこれで良さそうなのですが、実はこの段階ではCSSのfontプロパティをサポートしていなくて、正しい大きさより1.6倍ほど大きく表示してしまっています。

r1959
r1960ではfontプロパティに対応しました。現状ではESウェブブラウザはフォントのテクスチャは1サイズ分だけ展開していて、画面上ではOpenGLに任せて拡大縮小して描画しています。そのため、Acid1で使われているような特に小さいフォントだとかなり読みにくくなってしまっていますが、その点以外はほぼ大丈夫そうです。

r1960
r1961では、本当にとりあえずですがラジオボタンのbindingを展開して表示する様にしました。

r1961

この結果と最初のリファレンスのレンダリングを重ね合わせてみると、以下の様にぴったり重なり合いました。

リファレンスとの照合
現在ではAcid1テストに失敗するメジャーなブラウザはないと思いますが、ESウェブブラウザのようにスクラッチから開発しているものでは、現在でもひとつのマイルストーンかな、と思います。CSS 2.1でもAcid1はW3Cの公式テストスイートひとつとして含まれています。これだけ簡単なページでも、こんな具合にいろいろなバグをあぶり出してくれるところが、アシッドテストと呼ばれた理由なのでしょうね(アシッドテストは歴史的には酸で金以外のものを溶かすことで金の純度を調べる厳密な検査のことだそうです)。

さて、r1961で改めてグーグルの検索結果ページを表示してみたのが以下のスクリーンショットです。Acid1テストにパスするように修正を加えていった結果、また隠れていたバグが見えてきてしまいました。

r1961. 水色で示した部分がおかしい
Everything, Images, Videos, ... と続く検索ツールの部分が本来のグーグルのロゴマークの下ではなくて、検索結果の中に重なって表示されてしまっています。これはr1957でフロートボックス用のスペースをラインボックス中にちゃんと空けておくように修正したので、グーグルロゴの部分のフロートボックス(水色に塗った部分)を避けてこのようになってしまっていました。(今まではフロートのスペースを空けてなかったので正常に見えていただけだったのでした。)問題はグーグルロゴのフロートボックスを、本来の高さよりも大きいボックスとして扱ってしまっている点です。

r1963では、table wrapper boxのように独自にフォーマッティング コンテキストを構築するブロックボックスの高さ分がフロートボックスの高さを消費していく要素として考慮されていなかったバグを修正しました。グーグルの検索結果ページの場合だと検索用のテキストフィールドを囲っているテーブルの部分の高さが考慮されていなかったわけです。

r1963

検索ツールの位置は無事に直りました。検索用のテキストフィールドまわりの描画はこれまでもずっと崩れたままですが、こちらもおいおい直して行きます。

というわけで今回はここまでです。レイアウトテストもちょこちょこ試せるようになってきたことですので、もうしばらくCSSまわりの開発を進めていきます。DOM, HTML, HTTPの方はやや放置状態になってしまっていますけれども。

2011年9月15日木曜日

ESウェブブラウザ通信(9/15)

前回のESウェブブラウザ通信では、www.google.co.jpのページで検索用のテキストフィールドに文字を入力できるようになるところまで進めました。twitterでもときどきレポートしていますが、その後の進捗をまとめてみます。

r1914

r1925では、HTMLFormElementのsubmitオペレーションの実装を少し進めて、[Google Search]というsubmitボタンをクリックした時に、HTTPのGETメソッドを使って結果を取り出すことができるようになりました(GET以外のアクションはまだサポートしていません)。

r1925
注: 検索結果のうち、ESプロジェクトやエスリルのサイト以外の部分はぼかしています。

r1929では、絶対配置されたimg要素も表示できるようにしました。この段階ではCSSのoverflowのhidden設定に未対応だったので画像がクリッピングされずに全部でてしまっています。(いろいろなアイコンをひとつのビットマップにまとめておくのはお約束でしょうか。)

r1929
r1930では、overflowのhidden設定でクリッピングするようにしました。クリッピング処理はOpenGLの視空間(viewing volume)を小さくすることで実現しているので、修正している箇所はOpenGLのバックエンド内だけです。(☆マークがたくさん表示されてる部分はクリッピングの問題ではなくて、HTMLのtableに未対応なのと、ボックス幅の調整関連のバグの所為です。)

r1930
r1931では、Windowのスクロールをできるようにしました(スクロールはW3C/WHATWGの仕様書のHTMLではなくてCSSOM Viewの方で規定されています)。マウスホイールで操作できます(キーボードからの操作は未実装です)。Goooooooooogle▶の部分が縦に表示されて崩れてしまっていますが、これもr1930と同様の理由です。

r1931
r1938で、ボックスのautoで設定していない幅まで調整してしまっていたバグをとりあえず修正しました(r1945でもう一回修正しなおしています)。

r1938
r1945では、基本的な処理だけですが、HTMLのtableを表形式で表示できるようになりました。Goooooooooogle▶の部分も横一列に並んで表示されるようになりました。実装では、CSSのtable wrapper boxに対応するクラスとしてTableWrapperBoxクラスを、また特に規程されていませんがセルに対応するクラスとしてCellBoxクラスを導入しています。修正内容としてはtable関連の実装よりもTableWrapperBoxクラスを導出できるようにするためのBlockLevelBoxクラスのメンバ関数の整理の方が大きい感じです。(テーブルを構成するためのCSSのanonymous table objectの考え方と、HTMLでのHTMLパーサーと連携した方法はすこし違いますが、今の実装で使っているのはHTMLの方です。CSS 2.1でも"HTML tables may be rendered using other algorithms intended for backwards compatible rendering."と記載されています。)

r1945
ただグーグルのトップページでtableでレイアウトされている箇所がまだ崩れてしまっています。

r1945
r1948ではinline-boxに対してshrink-to-fitが働いていなかったバグを修正しました。Advanced searchリンクなどが検索テキストフィールドの右側に表示されるようになりました。td要素のalignやwidth属性の設定などには未対応なので、細かい部分ではレイアウトが崩れていますが、それ以外はほぼ大丈夫に見えます。(td要素のalignやwidth属性とかHTML5でobsoleteにされた機能がメジャーサイトで本当に使われなくなるのにはどれくらいかかるのでしょうね。現在は、"entirely obsolete, and must not be used by authors" とのこと。)

r1948
r1949では、bindingが設定されている、inline-blockとして表示される要素が、displayやfloatの設定次第でblockとして表示しないといけないときにbindingを展開できていなかったバグを修正しました。tableの幅の設定が効いていないので[Search]ボタンにほとんど隠されてしまっていますが、検索テキストフィールドに検索した文字列"esrille"の'e'だけ表示されているのがわかるでしょうか?(この検索ボックスの部分はtd要素にwidth=100%が指定されていて、他にもセルがあるので実際には100%には絶対にできないのだけれど、100%に近づくように可能な限り表の幅が広くなるようになっていて、その上限がtable要素を含んでいるdiv要素の'max-width: 711px'で設定されている、という込み入った感じになっているようです。)

r1949
r1952では、ブロックレベルボックスのmin-widthとmax-width設定が効くようにしました。検索結果の部分に設定されているmin-widthが効いて、結果がより幅広に表示されるようになりました。

r1952

今回はここまでです。tableのレイアウトはまだだいぶ直していかないといけませんが、次回以降はもっといろいろなサイトを表示できるように進めていきます。