Shiki’s Weblog

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

2012/01/07 #ESウェブブラウザ通信

あけましておめでとうございます。新年もESウェブブラウザの開発を進めています。このブログも合わせて引き続きよろしくお願いいたします。
昨年の最後のブログでは、より複雑なフローティングボックスのレイアウトについて対応を進めていきました。今回は、CSS 2.1の仕様書から12.5 Listsの実装をある程度進めて、そのあとで4章の最初から4.3.4まで主にCSSの字句解析・構文解析時のエラーからの復帰処理についてCSS 2.1テストスイートを使って実装とテストを進めていきます。

リスト

CSS 2.1ではリストの詳細については一部、

CSS 2.1 does not define how the list numbering is reset and incremented. This is expected to be defined in the CSS List Module [CSS3LIST] .

といった具合に、細かい規程をCSS 3に先送りしている箇所があります。その背景については、現在のCSS 3 Listsのエディタ、Tab Atkins Jr.さんのブログでまとめられているのが参考になります。
ESウェブブラウザでは、CSS 3 Listsの規程を一部先取りしながら、まずはCSS 2.1として期待通りに動くような実装を目指していきます。進め方はいつも通り、まず下記の修正でクリティカルパスを実装して、詳細はCSSのテストスイートを動かしながら直していきます。
r2218,r2219: ::marker擬似エレメントを導入。
r2220: 'list-style-position'プロパティに対応。
r2221: ''list-style'ショートハンドに対応。
r2222: カウンタの詳細実装を追加。
CSS 3 Listsでおもしろいのは::marker擬似エレメントの導入です。::beforeとほとんど同様の実装で、リストのマーカーに特化した処理を記述できるようになっています。CSS 2.1の仕様をまとめていく過程で::beforeに機能を詰め込みすぎて収拾がつかなくなって、CSS 2.1では詳細の規程をCSS 3に先送りした、といった経緯があったりするみたいですね。
c561-list-displ-000
ふつうに<ol>, <li>を使った場合には、r2222でリストの表示ができるようになっているのですが、このテストでは、div要素のdisplayに'list-item'を指定することでリストを表示させようとしているので未対応になっていました。

r2223r2223

CSS 3 Listsでは、 displayに'list-item'を指定されていた場合には、::marker擬似要素を生成して、自動的にカウンタ'list-item'を増加させるように規程されています。

r2224r2224

r2224でその処理を実装しています。
c563-list-type-001
decimal以外のリストのタイプもテストしています。

r2224r2224

r2225で対応しました。CSS 3 Listsでは、'@counter-style'ルールを使ってかなり自由にカウンタのスタイルを設定することができるのですが、ESウェブブラウザではいまのところCSS 2.1で必要なスタイルをハードコーディングしています。

r2225r2225

c564-list-img-000
'list-style-image'のテストです。

r2225r2225

r2226でひとまず'list-style-image'に対応するようにしました。イメージが無効な場合は'list-style-type'を使わないといけないのですが、その辺は今後実装していきます。

r2226r2226

c566-list-stl-001

CSS 2.1では、

CSS 2.1 does not specify the precise location of the marker box -12.5.1

となっていて、マーカーの位置は具体的には指定されていないのですが、このテストでは、CSS 3 Listsで規定されている、

the horizontal static position of the marker is such that the marker's "end" edge is placed against the "start" edge of the list item's parent. -4. Marker Position

という規則に則っているようです。

r2226r2226

"end" edgeなどもCSS 2.1にはない概念だったりしますが、r2227でひとまず対応しています。メジャーなブラウザでは、さらにマーカーの直後にスペースが入るようなのですが、その辺は最新の仕様の状況など見ながら対応していければと思います。

r2227r2227

counter-reset-increment-002
カウンタのインクリメントのテストです。

r2227r2227

ひとまずr2228で細かなカウンタまわりのバグをまとめて修正しています。

r2228r2228

これでWebKit系のブラウザと同じ崩れ方になりました。というのも、CSS 3 Listsの仕様を導入していると、 list-itemという名前のデフォルトのカウンタがあらかじめ予約されているのですが、このテストでもlist-itemという名前のカウンタを使っているので、CSS 3 Listsの規程の動作とテストのCSSの設定が両方とも動作してカウンタが想定以上に増加していってしまうのです。

r2228 (カウンタの名前をlist-item以外に変更)r2228 (カウンタの名前をlist-item以外に変更)

HTMLファイルを修正して、カウンタ名を'list-item'以外に変更すると上図のように想定通りの動作になります。CSS 3リストで予約するカウンタ名が本当にlist-itemで大丈夫なのかという疑問はちょっと残りますね。
リストの処理自体はまだ作り込んでいかないといけない部分が残っているのですが、基本的な処理は入ったので、今はここでいったん終わりにして、CSS 2.1の仕様書の4 Syntax and basic data typesのテストに進んでいきます。

CSSの構文とデータ型のテスト

これまで他の章のテストを進めて来れたことからもわかるように、エラーもなく丁寧に記述されているCSSスタイルシートのパース処理については、ここまでの段階でも大きな問題はありません。今回テストを進めていくのは文法エラーからの復帰処理など、一部誤りがあるようなスタイルシートの処理などが中心になります。
ESウェブブラウザでは字句解析にRE2C、構文解析にbisonを使用しています。修正箇所もこれらのツールのソースコードの修正がかなりあるので、そういった部分の詳細に興味のあるひとはいわゆるドラゴンブックが定番の教科書だと思うので参考にしてみてください。
補足: 字句解析はlex(flex)の方が定番だったと思うのですが、ESウェブブラウザの内部処理コード(char16_t)との親和性などでより扱いやすいRE2Cを使っています。
core-syntax-009
@importルールの基本的なテストを行っています。

r2231r2231

r2232で@mediaルールにも対応しました。

r2232r2232

list-style-020
最後のblue circleの行のlist-styleの設定(.seven)はすべて無効なのでデフォルトのcircleが表示されるはず、というテストです。

r2235r2235

r2236で無効なプロパティの設定を無視する枠組みを実装して、無効なlist-styleを除外できるようにしました。

r2236r2236

case-sensitive-003
:first-child擬似クラスセレクタと:lang擬似クラスセレクタに未対応でしたので、赤い行が残っています。

r2236r2236

r2237で:first-child擬似クラスセレクタに、r2238で:lang擬似クラスセレクタにそれぞれ対応しました。

r2238r2238

case-sensitive-004
langセレクタの比較で大文字・小文字を区別し処理してしまっていないかテストしています。

r2238r2238

r2239でlangセレクタの比較では大文字・小文字を区別しなように修正しました。

r2239r2239

case-sensitive-005
カウンタの名前の大文字、小文字を区別しているかどうかテストしています。

r2239r2239

r2240でカウンタ名の大文字と小文字を区別するように修正しました。

r2240r2240

escapes-005
\00006Cのようなユニコードのエスケープ表記のテストです。

r2240r2240

r2240では、"back\000047round"を"backGround"までは変換できていたのですが、さらにプロパティ名は大文字・小文字を区別しないので内部的にはさらに"background"のようにすべて小文字に変換しておく必要がありました。

r2241r2241

r2241で修正しています。
ident-003
CSS 2.1の仕様書に載せられているyacc/lexの文法では、#ではじまるRGBカラーと、#ではじまるIDセレクタなどを区別していないのですが、それをそのまま利用していたので'#-1ident'という文字列が有効なIDセレクタとして処理されたりしてしまっていました。

r2241r2241

ランタイムでチェックしてもよいのですが、r2242では字句解析のレベルでRGBカラーとIDセレクタを区別するように修正しています。

r2242r2242

at-import-010
@importルールは、 @charsetを除いて、ほかのどのルールよりも先に来ていないといけないのですが、無効なルールセットに関してはその限りではない、という点をテストしています。スタイルシート中最初の2行は無効なので、3行目の@importルールが有効になるというテストになっています。

r2043r2043

r2044で不正な擬似セレクタを無視できるように修正しました。

r2044r2044

ident-020
スタイルシートの中に.および#と識別子の間に'-'が1文字入っている無効なセレクタが含まれているのですが、それによってパーサーが混乱しないで続く有効なセレクタを処理できているかどうかテストしています。

r2245r2245

r2246でエラー処理を追加しています。

r2246r2246

at-rules-004
このテストのスタイルシートの

p { color: green; }
@foo {};
p { color: red; }

という部分を見ていると、一瞬3行目のpルールは有効なような気がしてしまいますが、実際には@fooは}で終わっていて、";"だけの文は許されておらず、"; p"というセレクタもないので、最初のpルールだけが生き残る、という具合になっています。

r2246r2246

r2247では、ひとまずbisonのstatement_listのルールに以下の3種類のエラー対策のルールを追加しています。

| statement_list '@' error '{' error '}' optional_sgml
| statement_list '@' error ';' optional_sgml
| statement_list error '{' error '}' optional_sgml

CSSの文は@ルール文か、ルールセット文のどちらかですが、最初の2つは無効な@ルール文、最後の1つが無効なルールセットに対応します。(なお、修正自体はまだ不十分であとのat-rule-001のテストなどでさらに修正していきます。)

r2247r2247

at-rule-003
CSSでは、{}, [], ()といったペアはまとめてエラー処理するよう指定されています(4.2)。

r2247r2247

r2248,r2249でひとまず{}のマッチング処理を追加しています。

r2248r2248

at-rule-001
このテストのスタイルシートの、

@ import "support/at-rule-red.css";
div
{
color: red;
}

という部分では、@とimportの間に空白が1文字入ってしまっています。一見、これも@ルールで行末の;(セミコロン)でルールが終わって次のdivルールが有効になるように思えます。しかし実は@ルールとして処理して構わないのは@の直後に空白を含まず識別子が来た場合だけなので、上のルールは'@ import "support/at-rule-red.css"; div'という長いセレクタ相当の部分がある不正な1つのルールセットということになります。

r2249r2249

先ほどのr2247では、@ではじまる文を@ルール文として処理するように修正しましたが、それではダメで、r2250では、@記号の直後に空白などをはさまずに識別子がある場合のみ@ルール文として処理するように修正しています:

| statement_list '@' IDENT error invalid_block optional_sgml
| statement_list '@' IDENT error ';' optional_sgml
| statement_list error invalid_block optional_sgml

r2250r2250

なお、このテストに関しては詳しい解説がマイクロソフト社のArron Eicholzさんからメーリングリストに流れていたのが参考になりました。
core-syntax-004

CSSでは、スタイルシートの終わりまできて、開いたままの{ではじまるブロックがあれば、強制的に閉じるように規程していますが、そのテストになっています。

r2251r2251

r2252でこの仕様に対応しています。

r2252r2252

core-syntax-006

これは正しい宣言と;(セミコロン)の間に不正な文字列が入っている場合のテストです。このような場合、パーサーは不正な部分を飛ばして処理を続けていくこともできるのですが、CSSの場合には正しかった宣言も無効なものとして扱うように規程しています。

r2252r2252

r2254で宣言のあとの;(セミコロン)あるいは}の前に不正な文字があった場合にはその宣言を取り消すように修正しています。

r2254r2254

font-family-invalid-characters-005

CSSでは、文字列が対応する"(ダブルクォート)もしくは'(シングルクォート)で閉じられずに行末まで来てしまった場合は、その文字列を強制的に閉じて、かつその文字列を使用しているルールを無効することになっていますが、そのテストになっています。

r2254r2254

r2255で対応しています。

r2255r2255

blocks-001
{}、[]、()のペアはまとめてエラー処理する必要があるのですが、そのテストになっています。

r2255r2255

r2248での{}に続いて、r2256で[]と()にも対応しました。

r2256r2256

eof-002
スタイルシートの最後でまだ閉じられていない(と[についても仕様通りに強制的に閉じているかどうかテストしています。

r2256r2256

r2257で対応しています。

r2257r2257

eof-003
CSSでは、文字列が閉じられないままスタイルシートの終わりに来たときは、行末の場合とは違って、文字列を閉じてかつそれを有効な文字列として処理を継続するように規程されています。そのような場合のテストになっています。

r2257r2257

r2258で対応しています。

r2258r2258

補足:re2cは、

re2c:yyfill:enable = 0;

のようにyyfillを無効にしている場合には、終端のチェックがかからないので、自前でチェックするようにr2258で合わせて修正しています:

Set this to zero to suppress generation of YYFILL(n). When using this be sure to verify that the generated scanner does not read behind input. Allowing this behavior might introduce sever security issues to you programs. -Manpage of RE2C

eof-005
;(セミコロン)で閉じる@ルールも、スタイルシートの最後まで閉じられなかった場合は強制的に閉じて、有効なルールとして処理する必要があります。そのテストになっています。

r2258r2258

r2259で対応しています。

r2259r2259

font-family-invalid-characters-002
ファンクション中の式にエラーがある場合、()は対応づけてエラー処理をしないといけません。そのテストになっています。

r2259r2259

r2260でひとまず対応しています。

r2260r2260

comments-003
/*ではじまるCSSのコメントも、スタイルシートの最後まで閉じられなかった場合は強制的に閉じる必要があります。

r2260r2260

r2261で対応しています。

r2261r2261

matching-brackets-001
@ルールの処理ではブロックなのか、ブロックを含まない;(セミコロン)で終端されるルールなのか明確に区別しておく必要があります。

r2261r2261

r2262で、{}を含んだerror_blockと{}を含まないerror_non_blockにエラー箇所を分けるようにしました。

r2262r2262

uri-005
このテストではCSSの実装の問題はなくて、URIのパーセント エンコードのバグと、RFC 3986のHTML5による拡張が未実装のために正常に動作していませんでした。

r2262r2262

r2263,r2264でそれぞれ修正しています。

r2264r2264

uri-012
このテストの解釈はすこし難しいのですが、4.1.1のコアシンタックスまで戻って見ると、()内にはブロックは現れない、と考えられるので、url( { test )の{は行末の}とマッチングされることはなく、直後の;(セミコロン)でこの不正な関数は打ち止められる、という理解になるのだと思います。

r2264r2264

r2265で対応しています。

r2265r2265

uri-017
url(ではじまるURIも、スタイルシートの最後まで閉じられなかった場合は強制的に閉じる必要があります。

r2266r2266

r2267およびr2276で修正しています。

r2267r2267

at-import-009
先ほどのat-import-010で修正したのと似たパターンで、@importルールの前にある 他の@ルールが無効なので、 @importルールが有効になる、というテストです。

r2267r2267

r2268で対応しています。

r2268r2268

import-001
このテストは一瞬@importルールの位置のテストのように見えますが、単純に@mediaルール内の構文エラーに対処しているかどうかのチェックです。

r2268r2268

r2269で対応しています。

r2269r2269

at-rule-013
このテストもimport-001に続いて@mediaルール内の構文エラーに対処しているかどうかのチェックです。

r2269r2269

r2270で対応しています。

r2270r2270

counters-010
これは字句・構文解析まわりでなくてカウンターの処理のバグでした。

r2272r2272

r2273,r2280でひとまず修正しました。カウンタまわりは次回以降、さらに修正していきます。

r2273r2273

z-index-016
このテストは以前も取り上げたことがありますが、整数しか受け付けないCSSプロパティに2.0のような表記の浮動小数点で値を指定した場合にエラーにできない、という問題が残っていました。

r2273r2273

r2274,r2275でそのような場合もエラーにできるように改善しました。

r2275r2275

border-width-009
マイナスのボーダー幅をパース時にエラーとして無視しているかどうかのテストです。

r2276r2276

こちらも以前に一度触れたことがありますが、これまではマイナスの値を受け付けないプロパティについてもパースには成功して計算値として既定値を返すような実装になっていました。r2277で、マイナスのボーダー幅をパース時にエラーとして無視できるようにしました。

r2277r2277

ほかのプロパティについても同様の修正をr2278,r2279で行っています。
なお宣言の式の部分については、何かのツール用のコードのように見えるかもしれませんが、bisonで生成したパーサーが生成した木構造をC++のプログラムで解析するようになっています。
background-position-001
このテストでは、body要素の背景画像を使って描かれているライム色の線が200pxの位置にくればOKです。

r2281r2281

body要素のbackgroundの設定はhtml要素の方に移るわけですが、r2281ではbackground-positionの計算値を計算していなかったために、body要素のfont-sizeではなくて、html要素のfont-sizeから解決値として位置を決定するようになってしまっていました。

r2282r2282

r2282,r2284で修正しています。
以上で4章の4.3.4までの構文処理のテストについてはuri-013の#twelve、およびuri-015を除いて成功するようになりました。(未対応のプロパティのためにFAILしているテストは一部残っています。)
ちなみに4章のここで取り上げたテストに関して、執筆時点でのメジャーなブラウザのPASS率を見てみると、
Firefox 9
100%
Internet Explorer 9
100%
Internet Explorer 8
87.5%
(このページを見る限りIE9もほぼ同様のようです)
Opera 11
75%
Chrome 16
62.5%
2010/1/13 追記 : IE9では100%PASSするようです。Test Suite Failuresの個別のページに"IE9 Mode"という記載がなければFAILするのはIE 8でだけということのようです。(参考)
といった具合になっています。エラーからの回復処理などは、まだ足並みが揃っていない様子です。CSSに関してはまだきちんと文法はチェックして書いておいた方が無難かもしれないですね。

まとめ

今回はここまでです。ESウェブブラウザのCSS 2.1テストスイート全体のPASS率は約64%になりました。次回は4.3.5 Countersの部分からテストと開発を続けていく予定です。