2011年10月23日日曜日

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

今回からW3C CSS 2.1テストスイートの10章のテストに進みます。今回は、replaced element, line-height, margin collapsing, list, table などを除いた基本的な実装の修正についてのまとめです。

anonymous-boxes-001

このテストは、パーセンテージによる長さの解決に匿名ボックスは影響しない、という点をチェックしています。r2054では、テスト内容とはまったく別に絶対配置されたボックスの位置が間違っていて赤い部分が見えてしまっていました。

r2054
r2032で絶対配置されたボックスのスタティック ポジションの計算を単純化した際に修正ミスがあったので、r2055で修正しています。

r2055
abspos-containing-block-005

絶対配置されたボックスの包含ブロックが初期包含ブロックになっている場合のテストです。r2055では、8.3.1で禁止されているルート要素のマージンまでつぶしてしまっていて、赤い部分が見えてしまっています。

r2055
r2056でルート要素のマージンはつぶさないように修正しています。

r2056
・ containing-block-031

このテストはインラインレベルボックスを起点に絶対位置を指定してボックスを配置するテストです。r2056では、絶対配置の処理ではなくて、また行末の空白処理のバグのために赤い文字(Ahemフォント)が見えてしまっています。

r2056
今回は1文字もラインボックスに収まらなかったわけではなくて、テキストの1部分は収まってその次の単語も実は空白を削除すれば収まった、というケースです。CSSモジュールの実装側で対応しているとこれからもこぼれがいろいろ出てきそうなので、r2057ではFontTextureクラスの実装から修正しました。

r2057
・ abspos-containing-block-initial-004a

このテストはHTML要素自体が絶対配置されている場合のテストです。r2057では、HTMLパーサーのバグでhtml要素に直接指定されたstyle属性がDOMツリーに登録されていなかったために単に空のHTMLドキュメントのような表示になってしまっていました。

r2057
ESウェブブラウザのHTMLパーサーのテストには、html5libテストデータを使っているのですが、最後にテストを行ってから割と時間も経っているので、また一度テストしておいた方が良さそうです。

r2058
r2058でまずHTMLパーサーのバグを修正して、本来のテストをできるようにしました。r2058では、左上部分が白色のままで背景色を適用できていませんでした。

r2059
r2059でHTML要素自体が絶対配置されている場合でも背景を正しく塗りつぶせるように修正しました。

・ abspos-containing-block-initial-009b

このテストもHTML要素自体が絶対配置されている場合のテストで、サイズを初期包含ブロックからのパーセンテージで計算するバージョンです。

r2059
r2059までは、ルート要素が絶対配置されているときにも、絶対配置されていない場合の要素のようにレイアウトしてしまっていておかしなことになっていました。

r2060
r2060で修正しています。

float-non-replaced-width-008

このテストの詳細については、HTMLファイルの中に詳しい説明が書かれています。フローティング ボックスにshrink-to-fitを適用した場合、そのあとで改めてmax-widthをチェックする必要があるというわけです。

r2060
r2060まではそのようなチェックをしていなかったので、r2061で修正しています。

r2061
・ block-non-replaced-width-001

前回、インラインレベル中で絶対配置されたブロック要素のスタティック ポジションの修正を行いましたが、絶対配置された要素がブロックならどのみちスタティック ポジションは改行した位置でないといけません。

r2061
r2062でr2061のバグも合わせて修正しています。

r2062
・ block-non-replaced-width-002

このテストの本来のポイントはオレンジとブルーのボックスの幅が同じになるかどうか、という点なのですが、r2062では、その点ではなくてr2061の修正で匿名ボックスについてまで不要なスタイルの計算してしまっていたバグがあって垂直方向の位置が揃っていません。

r2062
r2063で修正しています。

r2063
・ abspos-008

このテストも本来の目的と無関係に、テキストがボックスの中に収まっていませんでした。

r2063
line-heightの計算値と解決値の取扱いに間違いがあって、解決値を継承するようになってしまっていました。

r2064
r2064で修正しています。line-heightについては、10章でテストスイートが用意されているので次回以降でより詳しくテストしていきます。

・ inline-block-002

このバグはこれまでもどこかみたような感じです。#4のfloats-108と、#3のinlines-003で修正をしたのと同様の問題がインラインブロックの処理の方でも起きていました。

r2064
r2065でインラインブロックもフロートと同じように単純に包含ブロックから利用可能な幅を計算するように修正しています。

r2065
続いて、以前もインラインレベルボックスで似たようなバグがありましたが、途中で改行できないバグをr2066で修正しました。

r2066
・ max-width-089

このテストでは、max-widthに不正な負の値を指定したときのチェックをしています。

r2066
r2067で修正しました。

r2067
補足: こういった負の値といった不正な値に関して、現在のESウェブブラウザではCSSパーサーの段階ではエラーとしていなくて、プロパティを評価する際にチェックするようになっています。この方法だと、height-089のように"height: 0; height: -1%;"のような書き方をされた場合に対応できない、という問題があります。ただ、height-089には"invalid"フラグ("test is invalid; report error to public-css-testsuite"という意味のようです)が立てられているので、それほど緊急な問題でもないのかな、と。

・ inline-block-non-replaced-height-002

このテストでは、インラインブロックのマージンの処理をテストしています。

r2067
r2067では、インラインブロックのマージントップをラインボックスのマージンとつぶしてしまうという状態になってしまっていました。

r2068
r2068で修正しています。より詳しいマージン関連の処理は次回以降で8.3.1 Collapsing marginsのテストスイートに戻ってテストを進めていきます。

・ block-formatting-context-height-001

絶対配置されたボックスもフォーマッティング コンテキストを作っているので(CSS 3のボックスモデルで言うところのフロールートなので)、最後に子孫のフロートのクリアランスを確保しておく必要があります。このテストはその点をテストしています。

r2068
絶対配置されたボックスはふつうのブロックレベルボックスと違う関数でレイアウトしているので、こういう個別のテストがあって助かりました。BlockLevelBox::layOut()には入っていたクリアランスの処理がBlockLevelBox::layOutAbsolute()では抜けていたのをr2069で修正しています。

r2069
この次のテストから10.4のテストに進んでいます。max-heightはいままでサポートしていなかったので簡単な実装をr2070で追加しています。

・ max-height-percentage-002

このテストのHTMLファイル中にも記載されていますが、min-height、max-heightをパーセンテージで指定した場合には、
If the height of the containing block is not specified explicitly (i.e., it depends on content height), and this element is not absolutely positioned, the percentage value is treated as '0' (for 'min-height') or 'none' (for 'max-height'). 
という規則(10.7)があります。このテストはこの点についてテストしています。

r2070
r2071r2074でこの点について修正しています。

r2071
通常のブロックボックスのmax-heightの処理ができてきたので、r2072では絶対配置されたボックスについてもmin-height, max-heightを適用するように修正しています。

・ absolute-non-replaced-max-height-003

このテストでは、絶対配置されたボックスのheightがmin-height, max-heightの適用で変わったあとで、きちんとマージンやy座標を更新できているかどうかをテストしています。

r2072
r2072では後処理がなかったので青いボックスが少しずれてしまっています。 r2073で修正しています。すこしごちゃごちゃした修正ですが基本的には、BlockLevelBox::resolveAbsoluteWidth と同じ処理を改めて適用させているだけです。

r2073
ここまで絶対配置の処理をちょこちょこと修正してきので、9章の絶対配置に関連したテストをもう一度見直しておきました。

・ abspos-001

前回はこのテストは問題なかったのですが、r2075では緑色の絶対配置されているボックスの位置がずれて赤いボックスが見えてしまっていました。

r2075
これは、block-non-replaced-width-001のときの修正で隠れていたバグ(スタティック ポジションの計算が正しくない)が露見した感じです。r2076で修正しています。

r2076
以上で、replaced element, line-height, margin collapsing, list, table, rtl, bidi を除くと、8章から10章まで一通りテストができました。max-width, shrink-to-fit関連でまだ少しエラーが残っているのですが、次回からはここまでテストをスキップしてきた部分についても実装とテストを進めていく予定です。

2011年10月18日火曜日

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

CSS 2.1 Test Suite #2から続けてきたW3C CSS 2.1テストスイートの9章も今回でひとまず最後です。以下、相対配置、絶対配置、そしてフロートの順に失敗していたテストの内容とその修正についてまとめていきます。

position-relative-035

このテストはサイズ16pxのフォントには1px以上のラインギャップがある、ということでないと成功しません。

r2027
r2027では、Ahemフォントに対応した描画をするようにした際にラインギャップの処理を入れていなかったので真っ黒になってしまっています(Ahemフォントはラインギャップの値が0なのでした)。

r2028
r2028で、ラインギャップも考慮してline-heightを決定するように修正しています。TrueTypeフォントのラインギャップをどう計算するか、というのには思ったよりも紆余虚曲折があったみたいなのですが、ここではOS/2テーブルから'usWinAscent + usWinDescent - unitsPerEm'で計算しています。

・ abspos-007

このテストでは、インライン コンテキストの中にブロックレベルのボックスが絶対配置されたときのスタティック ポジションが次の行の先頭になっているかどうかチェックしています。

r2028
r2028まではスタティック ポジションは常にインライン レベルで計算してしまっていたのでした。r2029で修正しています。

r2029

このテストでは、相対配置されているインライン要素の中にインライン要素が絶対配置されている場合のレイアウトをチェックしています。

r2030
"PASS"と表示されているので良さそうに見えますが、その直前の"The test has "というテキストが表示されていません。

r2031
"This test..."ではじまる相対配置されたインライン要素からは、2つのインライン ボックス("This test has "と".")が生成されるのですが、スタッキング コンテキストの中には最後のボックスしか記録していなかったために、"This test has "の部分が描画されていませんでした。r2031で修正しています。

abspos-inline-003

r2031では、このテストでassertに引っかかっていました。ソース中、

<span class="test"><span class="absolute">PASSED</span><span class="fail">FAILED</span></span>

となっている部分で、外側の'span class="test"'に直接対応するインラインレベル ボックスがないので、"PASSED"のインライン要素の絶対位置を決めるために参照するボックス自体がない、という状態になっていたのでした。

r2032
r2032では、インライン要素内のインライン ボックスについては各要素からその子孫の要素によって生成されたインラインボックスも参照できるように修正しています。

この辺りで9.6, 9.7, 9.8, 9.9 と実装済みの部分に付いては割りと落ち着いてきたので、また9.4に戻ります。

・ empty-inline-001

このテストは空のインラインのテストで、ラインボックスの高さが0になることをテストしています。

r2033
前回inlines-020で空のインライン要素にも高さがある、ということをテストしたばかりですが、9.4.2には、
Line boxes that contain no text, no preserved white space, no inline elements with non-zero margins, padding, or borders, and no other in-flowcontent (such as images, inline blocks or inline tables), and do not end with a preserved newline must be treated as zero-height line boxes...
と書かれているので、このテストは正しいわけです。ただ、10.8には、
Empty inline elements generate empty inline boxes, but these boxes still have margins, padding, borders and a line height, and thus influence these calculations just like elements with content.
とも書かれているので、ややこしい感じになってます。

r2034
r2034では、マージン、ボーダー、パディングがすべて0(左右の設定も含む)の空のインライン要素が生成するインラインレベルボックスの高さは0にするように修正しました。

・ inline-formatting-context-002

r2034
このテストではバグが2つ露見していて、ひとつはインラインボックスの余白が効いていないというバグ。もうひとつは、入れ子のインライン要素のスタイルの重ね合わせに対応していないというバグです。

r2035
r2035では、ひとまずインライン要素の余白を適用するように修正しています。入れ子のインライン要素のスタイルの重ね合わせについては、大きめの修正になる可能性もあるので、9章のテストではひとまず保留にしておきます。

・ inline-formatting-context-015

このテストでは、前回スタッキング コンテキストの実装を改善したときにサボっていた箇所を見つけられてしまいました。

r2035
本来表示するはずのフロートの青い枠の後に、背景がオレンジ色のブロックボックスを描画しているので青い枠が見えなくなってしまっています。

r2036
r2036で位置指定されていないフロートについては、子孫のボックスを描画したあとに描画するように修正しています。本当はさらにその後で子孫のインラインボックスを描画しないといけないのですが、その修正は9章のテストでは保留しておきます。

・ float-002

単純なテストなのですが、よく見ると青いボックスがオレンジの枠に接していません。

r2036
#1r1984で行末の空白を削除するようにした際に、インラインボックスの幅だけ調整していて、合わせてラインボックスの幅を調整するのを忘れていたのでした。

r2037
r2037で修正しています。

・ floats-placement-vertical-003

このテストはモジラから提供されたテストのようですが、良くできているテストでした。

r2037
青いフローティングボックスは1行目には収まらないのですが、その判定がきちんとできているかどうか、さらに、青いボックスが収まらなかったときに続く黄色いフローティングボックスを先に収めてしまわないかどうかをチェックしています。

ひとまず1行に収まりきらなかったフロートをきちんと包含ボックス内で処理していなかったバグを修正した段階では下図のようになりました。黄色いフローティングボックスは幅だけ考えれば2つとも1行目に収まってしまうのですが、このように順番を入れ替えたレイアウトはNGというわけです。

r2037 修正中
r2038でこれらの問題を修正しています。

r2038
モジラのテストは画面中にテストの評価方法を記載しないようで意図を解釈するのにHTMLのソースファイルを見ないといけないのが少し難点ですが、コメントの中には、Check that we don't allow floats to reorderとあって、あぁ、なるほど、という感じです。

・ clear-002

フローティング要素にclearプロパティが一緒に設定されている、というパターンのテストです。
r2038
r2038までは、まったく考慮していなかったのでした。r2039で修正しています。

r2039
・ floats-014

正常なら黒い枠の中にブルーのフローティングボックスが4つとも収まります。

r2040
これはr2038で一部修正ミスがあったのでr2041で修正しています。

r2041
・ floats-015

このテストも、正常なら黒い枠の中にブルーのフローティングボックスが4つとも収まります。
r2041
フロートの高さを消費していく分の計算にバグがあったのをr2042で修正しました。

r2042
・ float-006

このテストは9.5の、
Note: this means that floats with zero outer height or negative outer height do not shorten line boxes.
という部分のテストです。配置を計算するときに高さ0のフロートの幅は0として扱うというわけです。

r2039
r2040r2043で、高さ0のフロートを考慮するコードの追加と、幅0フロートをラインボックスに入れることができなかった、というバグを修正しています。

r2043
floats-005

このテストは正常なら縦長のグリーンの長方形がひとつ表示されます。

r2043
まずidセレクタの大文字・小文字の区別の処理が壊れていたのをr2044で修正しました。

r2044
r2044までは、最後までラインボックスに収まりきらなかったフロートは強制的にclearしながら順にレイアウトしていっていたのですが、それだとクリアしなくても収まった筈のフロートの前でも強制的に改行してしまう場面が上図のように出てきてしまいます。この場合だと、最初の赤い部分に最下行の左側の緑の部分が収まる筈だったのです。

r2045, r2046で最後まで残ったフロートも通常通りにレイアウトしていくように修正しました。
r2045
・ floats-006

画面の説明だけ読むとパスしているように見えてしまうのが難点なのですが、このテストはWebKit系のブラウザも下のr2046と同じように崩れています。

r2046
正しくはFirefoxなどのように3つのボックスが横1列に並びます(オレンジが一番右側)。

r2047
このテストでは、ふつうにレイアウト処理を進めていくと、改行処理を行う直前に行末に空白が1文字残っているのですが、その空白は改行処理で'white-space'の設定に従って取り除かれます。したがって、その分だけラインボックスにスペースが増えるのですが、その増えたスペースをちゃんと利用しないとr2046のように青いボックスがひとつ次の行に回ってしまうのです。

r2047でこの問題を修正しています(微妙にずれているように見えるのは既知のAhemフォントのレンダリング側の問題です)。

・ clear-float-002

このテスト自体はごく単純に'clear: right;'を使っているテストです。

r2047
float-006では、外側の高さが0のフロートは幅も0として扱うという、という修正をしました。そのとき単純にマージンを足し込んで高さを計算していたので、このテストでは高さが -96 (margin-top) + 96 (height) = 0 となってしまって、幅を0として扱ってしまっていたのでした。r2048でこの問題を修正しています。

2012/2/17追記: r2048の修正は誤りで、stack-floats-001など他のテストにPASSできなくなってしまいます。フローティング ボックスの外側の高さは9.5に"floats with zero outer height or negative outer height"という記述がある通り、0未満となっても構いません。そうではなくて、フローティング ボックスの有効な幅を計算する場合は、クリアランスも込みで高さ計算する、という具合に解釈すれば、ほかのフローティング ボックスのテストを崩さずにこのテストも成功するようになるようです。r2419で修正しています。

r2048

このテストは、フローティング ボックスを子孫に含んだボックスのshrink-to-fitのテストになっています。

r2048
本来は左側のボックスの中に右側にあるボックスが入っていないといけません。shrink-to-fitをoffにしてみると以下のように描画されて、shrink-to-fitの実装が子孫のフロートを考慮できていないことがわかります。

r2048 (shirik-to-fitをオフ)
r2049でこの問題を修正しました。r2048までは、右のフローティングボックスと右端のインライン要素との間にあるギャップをフローティングボックスの左マージンに足し込んでいたのですが、それだと処理がやっかいになってしまうので、LineBoxクラスにgapというメンバー変数を追加しています。

r2049
floats-122

このテストでは、包含ボックスに2文字分の幅があって、最初に1文字分の幅のフロート(白いボックス)、続いて"X "という2文字からなる緑色のテキストがくるのですが、行末の空白が削除されて、最終的には1行の中にフロートと"X"というテキストが収まり、緑色の正方形だけが見える、というテストです。floats-006では最後にフロートを挿入できたのに対して、このテストではフロートは最初に入れられている点が少し違います。

r2049
r2049では、ふたつバグがあって、ひとつはテキストが1行に収まらないときに1フロート分垂直方向の位置をシフトダウンするときにラインボックスに未挿入のフロートを消失してしまう、というバグ(それで白いボックスが見えていません)。もうひとつは、シフトダウン前に行末の空白が削除できないかどうかチェックしていないというバグです。r2050で前者をまず修正しました。

r2050
ひとまずフロートを消失してしまう問題は解決して、左上に白いフローティングボックスが表示されるようになりました。そして、"X "の2文字分のために改行したけれども、行末の空白が削除されて結局は1行に収まる筈だった、というのが見てわかります。

r2051
r2051でシフトダウン前の行末の処理を修正しました(薄く赤い線が残っているのは例のAhemフォントの描画側の問題です)。

floats-138

このテストでは、正常ならLine 1とLine 2は別の行に表示されます。

r2051
これはまだ修正を進めていないマージンのつぶし処理関連のバグで、すべての場合には対応できていないのですが、r2052でひとまず修正をいれてあります。

r2052
・ floats-108

このテストはどちらかというと10.3.5のテストかと思います。正常であれば、青いボックスはすべて同じ大きさで描画されなければなりません。

r2052
フロートを配置するときに計算する利用可能な幅(available width)は、単純に包含ブロックの幅から余白等の幅を引いて計算すればよかったのですが、r2052ではわざわざラインボックスに残っている幅を使おうとして崩れていました。

CSSのフローティング ボックスは隙間があればなんでもかんでも詰め込む、というわけではなくて、フロートの望ましい幅(preferred width)が収まるだけの幅が包含ブロックにあれば無理はしないで改行しながら詰め込んでいくわけですね。

r2053
r2053でこの問題を修正しています。実際、変に窮屈にボックスを詰め込むよりも、r2053のように表示できた方が読みやすいですね。

というわけで、実装済みの機能の範囲では9章のテストについてはだいたいパスするようになりました。そこで9章はひとまず完了にして、次回からは10章のテストスイートに進んでいきます。実際には特定のテストに合わせて加えた修正によって、今までパスしていたテストが失敗するようになってしまったりすることもあります。今の段階では一通りテストスイートをみていって、改めて最初からテストをやり直してみるときに9章の残りのテストも詳しく見ていくことにします。