2011年11月20日日曜日

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

前回8.3.1のマージンのつぶしのテストを行いました。今回は10章までのテストでもうひとつまるごとスキップした10.8のline-heightのテストを進めていきます。

単純なline-heightのテスト

・ line-height-001

line-heightに不正な負の値を指定した場合に、指定を無視しているかどうかテストしています。Microsoft社から提供されたテストにはこういった値域のテストが必ず入っていますね。

r2131
r2132で修正しています。

r2132
・ line-height-002

このテストでは、インラインレベルボックスのline-heightに0を設定して値がそのまま使用されるかどうか確認しています。line-heightはブロックコンテナに指定された場合はラインボックスの高さの最小値の指定になりますが、インラインレベルボックスについてはその値がそのまま計算値になります。

r2132
r2132では、line-heightをすべて最小値扱いにしていたために、左側のAhemフォントの"X X"の部分で改行が入ったときにフォントの高さ分だけ2つめの'X'が下にずれてしまっていました。

r2133

r2133で修正しています。このテストの場合、高さ0のラインボックスを含むブロックレベルボックスでもcollapse throughはできない、という状況になっているので、前回修正したmargin collapseまわりの隠れていたバグも合わせて修正しています。

ESウェブブラウザでは、インラインボックスにほぼ対応するInlineLevelBoxクラスのheightにはコンテントエリアの高さ(基本的にはフォントの高さ、AD)を格納しています。line-heightはラインボックスの高さとしてだけ残ります。

line-height, AD, and L
すこし分かりにくいので図でまとめておくと、ライム色の線で示したラインボックスの高さはline-heightの設定が使われるのに対して、アクア色で示したコンテントエリアの高さは使用しているフォントのAD(Ascent-Decent)で与えられます。レディング(L)は、L='line-height'-ADで計算されるので、ADをコンテントエリアの高さとした場合、コンテントエリアの高さにLは一切含まれないことになります。また、インラインボックスのパディング、ボーダー、マージンは(line-heightとは無関係で)アクア色で示したコンテントエリアの外側からはじまります。

特に、このテストでは、line-heightが0なので、Lは-ADとなり、インラインボックスはAD/2だけラインボックスの上側の位置からはじまることになります。

line-height = 0 のとき

このあたりCSS 2.1の仕様はわりと未定義で、10.6.1で、
The height of the content area should be based on the font, but this specification does not specify how. 
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'.
という記述があって、line-heightとcontent areaを区別しているものの、10.8.1でも、
CSS 2.1 does not define what the content area of an inline box is and thus different UAs may draw the backgrounds and borders in different places.
と改めてコンテントエリアの詳細は未定義としているのです。その分、実際のウェブブラウザの実装ではどう実装するかこのように決めておく必要がある、というわけです。(ADの定義はどうなっているの?という話もあるのですが、それは今回後の方でまた触れます。)

ひとまず、このr2133の修正でMicrosoft社で開発されたline-height-xxxシリーズのテストはすべてパスするようになりました。

単純なvertical-alignのテスト

より複雑なline-heightのテストに進む前に、同様にMicrosoft社で開発されたシンプルなvertical-align-xxxのテストを進めておきます。

この一連のテストに関してはr2133の段階ではかなりのテストでブラウザ自体がassertで落ちていました。'vertical-align'の計算値を求める際に'line-height'が'normal'だとパーセンテージの計算が不可能、というのが理由でした。r2134, r2135では計算値と解決値の2段階に分けて解決するようにしましたが、こういう場合、'vertical-align'の設定をinvalidとして初期値の'baseline'扱いにしたいいのかもしれません(未テストです)。

なお、vertical-alignの処理では、インライン要素の親にさらに別のインライン要素がある場合など少し複雑な処理が発生するのですが、今回はそういった複雑なパターンはスキップして、ラインボックスの中に単純にインラインボックスがある場合にだけ対応していきます。(入れ子のインライン要素の処理については、テキストまわり処理と一緒に修正していく予定です。)

・ vertical-align-baseline-004

r2136
こちらはテスト本来の意図であるインラインボックスのアラインの処理ではなくて、ラインボックスの高さの計算にバグがありました。

r2137
r2137で修正しています。薄い縦線はAhemフォントの描画によるものです。

・ vertical-align-baseline-005

このテストではインラインブロックのoverflowに'hidden'が指定されている場合のベースラインの計算をチェックしています。標準に従っていれば、ベースラインは下マージンのエッジと同じ位置なので、下マージンのあるオレンジのボックスは青いボックスよりも上側に配置されます。

r2137
r2138で修正しています。

r2138
本当は左右の青いボックスの間も青く塗られるべきなのですが、現状は入れ子になっているインラインボックスでは外側のインラインボックスのスタイルは画面に描画できない、というバグがあります。

・ vertical-align-baseline-006

このテストもインラインブロックのベースラインの計算に関するテストです。先ほどのvertical-align-baseline-005とは違って、インラインブロックがvisibleなのでベースラインはインラインブロック中の最後のラインボックス(もしあれば)のベースラインに合わせないといけません。

r2138
r2139で修正しています。

r2139
・ vertical-align-121

このテストは成功すれば十マークが表示されます。

r1240
ラインボックスで保持するベースラインの計算にバグがあったのでr2141で修正しています。

r1241
ここまでで、vertical-alignの基本的なテストにはパスするようになりました。入れ子のインライン要素の対応などが必要な sub, super, text-bottom, text-top 等はテキストまわりの処理を改善するときに合わせて対応する予定です。

line-heightのより複雑なテスト

・ line-box-height-001

このテストは結構ややこしいものがあります。こちらでも指摘されているように、HTMLファイル中に記載されているテストの説明は実際の動作とはほとんど無関係に思います。ただ、成功した場合は左右のボックスの高さが同じになります。
r2139
左側の青いボックスは実際はAhemフォントの"X"が縦に2文字並んだものです。r2139では青いボックスが低くなってしまっていますが、これは2文字目の"X"の開始位置がレディングが計算式'line-height'-ADに従うと0-96pxで-96pxとなって、その半分の-48px分だけ上にずれているのです。

今回2番目にテストしたline-height-002もやはりline-heightが0で文字の位置はフォントの高さの半分だけ上にずれていました。このテストの場合には上にズレない、という点をどう仕様から解釈するのかがちょっと謎なのです。

line-height-002との違いはインライン要素の方はどちらも'line-height'が0なのですが、ブロックコンテナの方がline-height-002は0、今回のline-box-height-001は'normal'という点です。そこでレディングの計算式を、
L = max(インライン要素のline-height, ブロックコンテナのline-height) - AD
のように変更すると、他のテストも崩すことなく、うまく行くようです。(このあたりCSSの仕様書でもうすこしクリアになっているといいと思いますが、どうでしょう?)

r2140
・ c548-ln-ht-000

r2142
このテストに関しては上側の'line-height'を使っている方ではなくて、下側のpre要素の方で改行を含んだテキストの行の折り返しと描画処理(改行コードを描画してしまっている)にバグがありました。r2143で修正しています。

r2143
・ line-height-bleed-003

このテストはインラインレベルボックスのline-heightは0だけれど、ボーダーはline-heightは関係なくてコンテントエリアの回りに引いているかどうかをテストしています。

r2144

r2145で描画まわりを修正しています。なお、ラインボックスの高さはコンテナのline-heightが'normal'なので、BR要素でふつうに1文字の高さ分だけ改行されています。

r2145
・ c44-ln-box-003

このテストは緑色の枠の中が真っ白になればOKです。

r2143
r2144で1行にいろいろなボックスが入っている場合のレディングの計算まわりのバグを主に修正しています。r2143で赤色の横長の線になって見えている部分です。

このテストではインラインのimg要素にマイナスの水平方向のマージンを使っているので、赤い縦長の線はマージンの処理のバグのように思えたのですが、実際にはwhite-spaceの処理にバグがあってimgの直前のテキストの最後の空白を余計に消去してしまっていたのでした。r2147r2150でwhite-spaceのバグを修正しています。

なお、この前後でボックスの水平マージンの処理も見直して、r2148でだいたい落ち着きました。通常のブロックでは右マージンがマイナスだとボックスの幅が広がるのに対して、インラインでは次のインラインブロックの開始位置がその分左にずれるだけなので、少し場合分けが必要な場面が残っていました。

r2148

・ margin-inline-002

このあたりで10.8のline-heightのテストで今回対応しておきたかったものはパスするようになりました。ただ以前も一度修正したことがあるこの8章のテストがまた崩れてしまっていたので修正します。

r2151
このテストで暗黙に仮定されていることは、line-heightが'normal'のときはコンテントエリアの高さとラインボックスの高さは一致しないといけない、というものです。

r2151では仕様書の推奨にならってコンテントの高さに使用するADをTrueTypeフォントのsTypoAscender - sTypoDescenderとしていたわけですが、以前#4で、position-relative-035をパスするためには'normal'のラインの高さはラインギャップを含んだusWinAscent + usWinDescentとしなければいけない、ということがありました。

で、ESウェブブラウザではコンテントエリアの高さをADとするように決めたわけですから、仕様書の推奨はここでは無視してADはusWinAscent + usWinDescentに合わせておくようにします。修正はr2152になります。

r2152

ちなみにESウェブブラウザでよく使用しているIPAGothicフォントとAhemフォントのOS/2テーブルの内容は以下のような感じになっています。

IPAGothic
units_per_EM 2048
sTypoAscender 1802
sTypoDescender -246
sTypoLineGap 0
usWinAscent 1802
usWinDescent 401
sxHeight 1040

Ahem
units_per_EM 1000
sTypoAscender 800
sTypoDescender -200
sTypoLineGap 0
usWinAscent 800
usWinDescent 200
sxHeight 800

sTypoLineGapの値はまったく当てにならなくて、フォント デザイナの想定しているラインギャップはusWinAscent+usWinDescent-units_per_EMで計算しないといけない、Ahemフォントにはラインギャップがない、といったことがわかります。この辺はラインギャップありのフォントも別途用意してテストケースを作っていった方がいい感じがしますね。

まとめ

今回はここまでです。9月下旬から進めてきたCSS 2.1のテストスイートによるテストも、ようやく1/3強のテストにパスするようになりました。複雑なテストで個別にスキップしているものは残っているのですが、テキストが絡まなければボックスのレイアウトはだいたい標準通りになってきたように思います。大物のテーブルとテキストがまだ残っていますが、ESブラウザ全体としてみるとCSSモジュールの開発だけが一歩進んだ感じになってきているので、今後はCSS以外の部分の開発もペースを上げていきたいところです。

2011年11月13日日曜日

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

前回まででW3C CSS 2.1テストスイート8章から10章まで一通り確認を行ってきて、マージンをつぶす前のボックスの配置については、だいたい正しくレイアウトできるようになってきたように思います。そこで今回は、これまでテストをスキップしてきた8.3.1 Collapsing marginsのテストの確認と修正を進めます。仕様書中ではたった1セクションなのですが、きちんと実装するのが難しい部分で、現状ではWebKit系の最新のウェブ ブラウザなどでもまだ全テストにパスするようにはなっていなかったりする部分でもあります。

Ahemフォントの処理

・ containing-block-031

マージンのつぶしのテストの前に、Ahemフォントのベースラインのずれの問題がだんだん煩わしい感じになってきているので修正することにしました。

r2095
ESウェブブラウザで利用しているFree Type 2では、TrueTypeフォントからフォントのビットマップを生成する際に、フォントの線が綺麗にディスプレイのグリッドにフィットするように処理してくれます。ただESウェブブラウザでは、このようにして展開した32pxのフォントをOpenGLで拡大・縮小して表示しているので、実際に描画されるフォントはほとんどの場合グリッドにはフィットしていません。

さてAhemフォントの場合、フォントのアセントとディセントの比は8:2なので、これを32pxに割り当てると、25.6px:6.4pxとなるのですが、Free Type 2ではグリッド フィット処理が入るので26px:6pxに丸められて展開されるようです。この26-25.6=0.4の差が画面上でちらっと赤い部分が見えたりする原因になっていたようです。

r2096
r2096では、この差を計算してOpenGLで描画する際はアセントを理論値で処理するように修正しました。Ahemフォントを利用しているcontaining-block-031では赤い部分は完全に見えなくなりました。ただOpenGLの処理と合わさって、テストによってはフォントの端の部分が背景を完全には隠せなかったり、という場合が残ってしまうことがあります。

マージンのつぶしのテスト

前準備はここまでで、8.3.1のテストに移ります。r2096の段階では、8.3.1のテスト結果は次のような具合でした。

# 8.3.1
html4/abspos-022.htm pass
html4/background-bg-pos-205.htm fail
html4/blocks-017.htm fail
html4/c411-vt-mrgn-000.htm fail
html4/margin-collapse-001.htm pass
html4/margin-collapse-002.htm pass
html4/margin-collapse-003.htm pass
html4/margin-collapse-004.htm pass
html4/margin-collapse-005.htm pass
html4/margin-collapse-006.htm pass
html4/margin-collapse-007.htm pass
html4/margin-collapse-008.htm pass
html4/margin-collapse-009.htm pass
html4/margin-collapse-010.htm pass
html4/margin-collapse-011.htm pass
html4/margin-collapse-012.htm pass
html4/margin-collapse-013.htm pass
html4/margin-collapse-014.htm pass
html4/margin-collapse-015.htm pass
html4/margin-collapse-016.htm pass
html4/margin-collapse-017.htm pass
html4/margin-collapse-018.htm pass
html4/margin-collapse-019.htm fail
html4/margin-collapse-020.htm pass
html4/margin-collapse-021.htm pass
html4/margin-collapse-022.htm pass
html4/margin-collapse-023.htm pass
html4/margin-collapse-024.htm pass
html4/margin-collapse-025.htm pass
html4/margin-collapse-026.htm pass
html4/margin-collapse-027.htm pass
html4/margin-collapse-028.htm pass
html4/margin-collapse-029.htm pass
html4/margin-collapse-030.htm pass
html4/margin-collapse-031.htm pass
html4/margin-collapse-032.htm pass
html4/margin-collapse-033.htm pass
html4/margin-collapse-034.htm pass
html4/margin-collapse-035.htm pass
html4/margin-collapse-037.htm pass
html4/margin-collapse-038.htm pass
html4/margin-collapse-101.htm fail
html4/margin-collapse-102.htm pass
html4/margin-collapse-103.htm pass
html4/margin-collapse-104.htm pass
html4/margin-collapse-105.htm fail
html4/margin-collapse-106.htm pass
html4/margin-collapse-107.htm fail
html4/margin-collapse-108.htm fail
html4/margin-collapse-109.htm pass
html4/margin-collapse-110.htm fail
html4/margin-collapse-111.htm fail
html4/margin-collapse-112.htm pass
html4/margin-collapse-113.htm fail
html4/margin-collapse-114.htm fail
html4/margin-collapse-115.htm fail
html4/margin-collapse-116.htm fail
html4/margin-collapse-117.htm fail
html4/margin-collapse-118.htm pass
html4/margin-collapse-119.htm fail
html4/margin-collapse-120.htm fail
html4/margin-collapse-121.htm fail
html4/margin-collapse-122.htm fail
html4/margin-collapse-123.htm fail
html4/margin-collapse-125.htm fail
html4/margin-collapse-126.htm pass
html4/margin-collapse-127.htm pass
html4/margin-collapse-128.htm fail
html4/margin-collapse-129.htm fail
html4/margin-collapse-130.htm pass
html4/margin-collapse-131.htm pass
html4/margin-collapse-132.htm fail
html4/margin-collapse-133.htm fail
html4/margin-collapse-134.htm fail
html4/margin-collapse-135.htm fail
html4/margin-collapse-137.htm fail
html4/margin-collapse-138.htm fail
html4/margin-collapse-139.htm pass
html4/margin-collapse-140.htm pass
html4/margin-collapse-141.htm fail
html4/margin-collapse-142.htm fail
html4/margin-collapse-143.htm fail
html4/margin-collapse-145.htm fail
html4/margin-collapse-146.htm fail
html4/margin-collapse-147.htm fail
html4/margin-collapse-148.htm fail
html4/margin-collapse-151.htm pass
html4/margin-collapse-154.htm fail
html4/margin-collapse-155.htm pass
html4/margin-collapse-156.htm pass
html4/margin-collapse-157.htm fail
html4/margin-collapse-158.htm fail
html4/margin-collapse-159.htm fail
html4/margin-collapse-160.htm skip (page)
html4/margin-collapse-162.htm fail
html4/margin-collapse-163.htm fail
html4/margin-collapse-164.htm pass
html4/margin-collapse-165.htm pass
html4/margin-collapse-166.htm pass
html4/margin-collapse-clear-000.htm pass
html4/margin-collapse-clear-001.htm pass
html4/margin-collapse-clear-002.htm pass
html4/margin-collapse-clear-003.htm pass
html4/margin-collapse-clear-004.htm fail
html4/margin-collapse-clear-005.htm fail
html4/margin-collapse-clear-006.htm pass
html4/margin-collapse-clear-007.htm pass
html4/margin-collapse-clear-008.htm pass
html4/margin-collapse-clear-009.htm fail
html4/margin-collapse-clear-010.htm fail
html4/margin-collapse-clear-011.htm pass
html4/margin-collapse-clear-012.htm fail
html4/margin-collapse-clear-013.htm fail
html4/margin-collapse-clear-014.htm pass
html4/margin-collapse-clear-015.htm pass
html4/margin-collapse-clear-016.htm pass
html4/margin-collapse-clear-017.htm pass
html4/table-margin-004.htm skip (table)


比較的単純なmargin-collapse-0xx以外のテストについては、ほとんど失敗していました。今回は、これらのうちテーブルと印刷用メディアに関連した2つのテストをスキップする以外はすべてパスするように実装を修正するのが目標です。

・ margin-collapse-019

ボックスの高さが0で、ボックスの上下のマージン同士をつぶせる場合のテストです。

r2096

r2096では、上下のマージンを親のボックスおよび次のボックスとつぶす処理が個別に実行されてしまって、上下のマージン同士はつぶせずに表示してしまっていました。

r2097
r2097では、ひとまず上下のマージンをつぶし合える(collapse through)ボックスの処理を別個導入して、レイアウト上はマージンをつぶしたように見えるようにしています。ただし、この修正は一時的なものでr2107で大きく実装を変えています。

・ margin-collapse-101

このテスト自体は単純なマージンのつぶし処理のテストで左右のボックスが同じようになればOKです。

r2097
r2098で、最後の子がcollapse throughする場合の処理を追加しました。

r2098
・ margin-collapse-102

このテストも101と同様に子のボックスがcollapse throughするのですが、親のボックスが絶対配置されたボックスになっています。

r2098

r2099で、絶対配置されたボックスでも、最後の子がcollapse throughだった場合の処理を行うように修正しました。

r2099
・ margin-collapse-117

このテストでは親のボックスのheightが'auto'でないときに、最後の子の下マージンをつぶしてしまわないかどうかチェックしています。仕様書の、
bottom margin of a last in-flow child and bottom margin of its parent if the parent has 'auto' computed height
という部分です。    

r2100
r2101で修正しています。

r2101
・ margin-collapse-121

このテストはマージンが0の場合のテストで、マージンのつぶし処理は発生しないのですが、それはさておいて、フロートをクリアするときに、ボックスにボーダーが設定されているとクリアランスの計算を間違えるというバグが残っていました。

r2101
r2102で修正しています。(一番下の行が赤いのはテーブルの実装のバグなのでここでは無視して進めます。)

r2102
・ margin-collapse-130

このテストでは、赤くなっている部分にheightが0のボックスがあるのですが、中にテキストを含んだラインボックスがあってオーバーフローしているので、仕様にしたがうとこのボックスではcollapse throughの処理をしてはいけません。

r2103

r2104で修正しています。ESウェブブラウザでは、フローティングボックスなどが高さ0のラインボックスに入るので、collapse throughするかどうかの判定が少し細かくなっています。

r2104
・ margin-collapse-132

このあたりのテストからは、collapsed-throughしたボックスが連続しているときに、それらのマージンをまとめて親や兄のボックスに戻す、という処理のテストを行っています。

r2106r2107でそのための前準備として、クリアランスがあるときは仕様通りマージンをつぶし合わない用にしています(結構コードが変わっています)。

このテストでは、 collapse throughするマージンの処理に関する、
If the element's margins are collapsed with its parent's top margin, the top border edge of the box is defined to be the same as the parent's.
というルールをテストしています。

通常はcollapse throughするボックスの子の開始位置はそのボックスをレイアウトする時点の上マージンの値の分だけ下にずれるのですが、親と上マージンをつぶし合った場合には、この開始位置のズレを0にする、というわけです。(なお、collapse throughしたボックスの次の弟のボックスの開始位置はこのズレの影響は受けません。)

r2107
r2108で、親と上マージンをつぶしたときは子孫の開始位置を保存しないようにしました。(r2108では子孫の開始位置をclearaceに保存していたのですが、紛らわしいのでr2109で、BlockLevelBox::topBorderEdgeに保存するように修正しています。)

r2108

・ margin-collapse-143

このテストもmargin-collapse-132と同様にcollapse throughしたマージンが親の上マージンともつぶし合う場合のテストです。ただその経路が複雑で、一度下マージンが親の下マージンにつぶされるのですが、その親のボックス自体もcollapse throughしていて親の下マージンが親の上マージンにつぶされる、という具合になっています。

r2109
r2110で、collapsed-throughしたボックスの下マージンを上マージンに移動させたときにも、topBorderEdgeをクリアするようにしました。

r2110
・ margin-collapse-142

このテストでは、マージンのテストをする以前に右側の参照パターンの色が崩れていました。

r2110
クラスセレクタの大文字・小文字の区別の処理が崩れていたのをr2111で修正しました。

r2111
ここからマージンのテストです。このテストもHixieが作成したテストなのですが、もともと難しめのテストの多いHixieのテストの中で、さらにタイトルに"CSS Test: Margin Collapsing: clear (hard)"とわざわざhardと記載がされています。

黄色いボックスにはclear: leftと一緒に上マージンとして64px設定されているのですが、シアン色のフローティング ボックスの高さもちょうど64pxなのです。なのでこれまでの処理ですと黄色いボックスにはclearanceは不要なので上マージンは親とつぶして大丈夫、という判定をしてしまって描画した結果がr2111です。

何が難しいのかというと、clear: leftの意図からしてr2111がおかしいのは自明なのですが、仕様書の文言から上記の動作がいけない、ということをどう読みとるか、という点だったりします。

先ほどの考え方では、まずクリアランスの計算をして、それに基づいてマージンのつぶしを行っていました。でもcollapse throughしているボックスをclearするときは、そのマージンをさら上にthroughさせてしまうとr2111のようになってしまいます。こういう場合、collapse throughするボックスをclearするときは元のマージンがどうであろうと必ずクリアランスを導入する、と解釈すると整合がとれます。

このテストの場合だと、
  • 黄色のボックスのclearance = 0px
  • 黄色のボックスのmargin-top = 64px
としておけば意図通りの動作になります。r2114でその処理を実装しています。

r2114

ちなみに、r2114の実装では黄色のボックスのマージンが80pxだった場合には、
  • 黄色のclearance = -16px
  • 黄色のmargin-top = 80px
という具合になって、黄色とシアン色のボックスは接したままになります。

r2114 (黄色のマージンが80pxの場合)
画面上ではマージンは指定した値より小さく見えるので不思議な感じもしますが、WebKit系もFirefoxもIEも現状みんなこう動きます。

ちょっとややこしいですよね。今年の2月でもMozillaの開発者のひとりBoris Zbarskyさんは、
In the area of clear + margin-collapse interaction, pretty much no one understands the details. :(
とW3Cのメーリングリストに書かれているくらいです。

またCSS 2.1の仕様では、上記の動作の他に、
  • 黄色のclearance = 0px
  • 黄色のmargin-top = 80px
という、より直感的な次のようなレイアウトをとってもいいことになっています。

黄色のマージンが80pxの場合のもうひとつの有効な解釈
この仕様の曖昧さの理由はCSS Wg Blogに説明されていました:
The preferred behavior is the latter, since it doesn’t mysteriously eat margins and make clear to make things move up. But we need to evaluate web compat since Acid2 and therefore all browsers do the former
後者の直感的なレイアウトの方が望ましいけれど、Acid2が前者を想定していてどのブラウザも前者で実装しちゃっているから互換性の問題が……と。

この曖昧さが実際にあとのテストケースでは問題になってくるのですが、そのはそのときに。

・ margin-collapse-146

このテストはmargin-collapse-143のより複雑なパターンで、連続したcollapse-throughしたボックスのマージンが親のボトムマージンとつぶしあって、さらにそのマージンが親のボトムからトップに移る、というパターンになっています。

r2114
r2115で対応しています。

r2115
・ margin-collapse-148

このテストは先頭からcollapsed-throughしている子のボックスが連なっていてそのつぶし合ったマージンが親の上マージンに移るパターンのテストです。


r2115
r2116で、上マージンが親に移るときに、連なっている子のボックスtopBorderEdgeをまとめてクリアするように修正しました。

r2116
・ margin-collapse-164

このテストはWebKit系, FireFox, IEどのブラウザも同じように失敗しているテストで、まだInvalidマークは付けられていないのですが、このテスト自体がInvalidだと思います。

r2116
margin-collapse-142の修正の時に、CSS 2.1ではクリアランスの計算方法として2通りの方法が許されていて、r2114ではほかのブラウザと同じ動きの方を選択しましたよ、ということを書きました。

このテストは、許されているもう一方のクリアランスの計算方法を使うとパスするようになります。それで、どちらのクリアランスの計算方法を採用するか、という話なのですが、このテストにパスする方の計算方法を使うと、ESウェブブラウザは165, 166のテストも同時にパスするようになることもあって、今は後者を採用することにします。それだとAcid 2にパスしないのでは、という話がもしかすると出てきたりするのかもしれませんが、それはまたそのときに、ということで。修正はr2117になります。

r2117
補足: IE 9に関するマイクロソフト社の次のページにも詳しくこのテストの問題点が説明されています: 3.1.50 CSS 2.1 Test: margin-collapse-164.htm

・ margin-collapse-128

このテストは2つ以上のマージンがつぶし合うときに、その中に負のマージンが混ざっていた場合のテストしています。いままではこういった負のマージンが混ざる複雑なケースには対応してこなかったのでr2119では以下の通り赤い部分が見えてしまっていました。

r2119
CSS 2.1では、複数のマージンをまとめてつぶす場合は、正のマージンと負のマージンはそれぞれ別個に最大値、最小値を計算して、最後につぶすときにその差を残ったマージンの値とする、という規則があります。

r2120
r2120で、FormattingContextに正のマージンと負のマージンの最大値、最小値を格納してマージンの値が確定したときに有効なフロートの高さを消費するような実装にしています。マージンの処理は有効なフローティング ボックスの高さを消費していく処理とも連動しないといけないので結構やっかいなのです。

・ margin-collapse-123

r2120ではやはりフローティング ボックスの高さを消費していく処理にバグが残っていました。

r2120
r2121で修正しています。collapse throughするボックス内で新たに生成したフローティング ボックスと、それ以前のボックスから引き継いできたフローティング ボックスとでちょっと違う処理を行う必要があるのでした。

r2121
・ margin-collapse-clear-006

マージンをつぶせるかどうかの判定方法は、ブロックレベルボックスの内側と外側ですこし違います。このテストではオレンジ色のボックスのoverflowに'hidden'が設定されています。この場合、オレンジ色のボックスのマージンは外側のボックスのマージンとはつぶし合うことができます。

r2121
r2121までは単純にフロールートならマージンをつぶさない、という実装をしてしまっていたので、r2122で修正しています。

r2122
・ margin-collapse-clear-009

このテストはmargin-collapse-clear-006の続きのようなテストで、overflowに'hidden'が設定されているボックスでもclearがちゃんと機能するかどうかテストしています。

r2122
overflowが'visible'以外の値を取ると、そのボックスは新しいフォーマッティング コンテキストを作ります。新しいフォーマッティング コンテキストからはそれまでのコンテキストの中のフローティング ボックスは見えません。r2122では新しいフォーマッティング コンテキストを使い始めてからクリアの処理をしていたので、r2122のような結果になってしまっていました。

r2123
r2123で修正しています。

・ margin-collapse-clear-012

このテストも赤い部分が見えたら失敗です。

r2123
スクリーンショットからはわからないのですが、ブルーのボックスのあとに2つ、collapse-throughする透明のボックスがあって、最初の方のボックスにはclearが設定されていてクリアランスが60pxになるように設定されています。

さて、この2つのcollapse-throughする透明のボックスのマージンをつぶしていくと、140pxになるのですが、r2123ではこのマージンをライム色の親のボックスの下マージンに移してしまったので、ライム色の部分の高さが減って赤い部分が見えています。

仕様書をよく読むと、クリアランスがあるcollapse-throughしているボックスに連なってつぶしたマージンは、親の下マージンに移してはいけない、と書いてあります:
If the top and bottom margins of an element with clearance are adjoining, its margins collapse with the adjoining margins of following siblings but that resulting margin does not collapse with the bottom margin of the parent block.
r2123では最後の子のボックスがクリアランスがあるcollapse-throughしているボックスの場合のみ親の下マージンに移さないようなコードになっていて、このテストの用に2つ以上ボックスが連なっている場合に対処できていなかったのでした。

r2124
r2124でこのマージンの処理の問題を修正しています。マージンのテストとしてはこれでOKだと思いますが、r2123で赤い部分が半分しかないことからもわかるように、テスト本来の意図はライム色の部分は幅が50%になることを期待しています。

前回r2094では、パーセンテージで指定した幅の目安とする包含ブロックの幅が不明の時は幅を'auto'として扱う、という修正を入れました。r2123でライム色のボックスの幅が大きいままなのは、その判定がこのテストでもかかって幅が50%から'auto'扱いに変わってしまっているためです。

ということは、こういう場合、包含ブロックの幅は不明ではない、と考えるべき、ということですよね。r2125では包含ブロックの幅が'auto'というだけではなくて、包含ブロックの幅がshrink-to-fitし得る場合に限って包含ブロックの幅は不明と判断するように修正しました。ソースコードのコメントにも記載しましたが、'auto'扱いにするということ自体がCSS 2.1で決められているわけではないので、このあたりの処理は一番もっともらしいところに合わせていくしかない感じです。

r2125
margin-collapse-clear-015, margin-collapse-157

r2125までは、clearanceはマージンとつぶし合わない、という理解で実装を進めてきていました(clearanceを挟んだ上下のボックスに関しては当然そうですよね)。ところが、この2つのテストに関してはボックスのクリアランスとそのボックスの先頭の子のボックスの上マージンはつぶし合えると考えないとうまく動かないと思われるテストになっています。

r2125 (margin-collapse-clear-015)
r2125 (margin-collapse-157)
margin-collapse-157では、左下の黒枠内が崩れています。黄色い下ボーダーのある高さ0のボックスにclear: leftが指定されていて、さらにその子として上下左右1emずつマージンのある高さ0のボックスが配置されています。この場合の解決値は、

アクア色のフロートの高さ: 64px
・黄色のクリアランス: 64px
・黄色の上マージン: 0px

のようになるので、クリアランスと子のボックスのマージン16pxがつぶし合えないとすると、r2125のような結果になります。

margin-collapse-157はGeckoでも下の段のテストは3つともinvalidじゃない?という意見が出されていますが、 margin-collapse-clear-015はパスしているところを見ると、先頭の子の上マージンはクリアランスでつぶせると考えないといけない気がします。

r2126では、負のマージンが絡んだときの動作がどうなるかちょっとわからないのですが、その処理を推定して実装しています。

r2126 (margin-collapse-clear-015)

r2126 (margin-collapse-157)
・ background-bg-pos-205

このテストではマージンの値が負のときにスクロールが正しい範囲で動くかどうかテストしています。単純に足し合わせて可動幅を決めてしまうと、本来の値より小さくなってしまいますよね。

正常なら右下までスクロールすると黄色や青のボックスが見えるのですがr2126では水平方向に付いてはスクロールバーさえ表示されない状態になっていました。

r2126
r2127で修正しています。本当はさらにCSSのbackground属性によって黄色いボックスの上に小さなオレンジのボックスが表示されたりしないといけないのですが、今回は8章のマージンのテストということで進めているので、14章のテストを行う際に改めて調査したいと思います。

r2127
・ c411-vt-mrgn-000

tableに未対応な部分があって若干表示が崩れている部分は残っていますですが、この段階で8.3.1のマージンの処理に関するテストについては(最初にskip扱いとした2つを除いて)すべてパスするようになりました。

このテストではマージンを使っている左側の列ではなくて、絶対配置で同じパターンを構築している右側の列の白いボックスの位置がおかしくなっていました。

r2128
topとheightが'auto'で、bottomが'auto'以外のときに、heightを最終的に解決したらtopの値も調整しなおさないといけなかったのですが、その処理が落ちてしまっていました。10章のテストスイートで発見できないといけないバグだと思うのですが見落としがあったかもしれません。r2129で修正しています。

r2129
最下行の色違いはtableの処理のバグによるものです。

・  www.esrille.com

ここでテストスイートではないのですが、esrilleのページの表示がr2108あたりから崩れるようになってしまっていたのですが、テストスイートの最後まで終わらせても崩れたままなのでした。

r2129
本来、タブの上側にあるはずのマージンが、タブと用紙の間に入ってしまっています。

r2130
クリアランスが入ったときに、そこでマージンのつぶしは途切れるわけですが、そのときに上側のマージンをcollapse-throughしたボックス間でまとめる処理が抜け落ちていました。テストスイートで動いているように見えるからといって、安心というわけではない、という変な例になっていまいました。r2130で修正しています。

まとめ

CSSの仕様書中の短さとは反対に修正にはけっこう手間取りましたが、マージンのつぶしもようやくテスト完了です。マージンのつぶし処理の修正中はどれかのテストをパスするように直すと、それまでパスしていた他のテストが失敗しだす、というもぐら叩き状態が続いたこともあって、少し前から使っているharnessというプログラムをr2119で修正して(ようやく)自動でテストも行えるようにしました。1回は目視で成功・失敗を判定しないといけないのは同じなのですが、レンダーツリーのテキスト形式のログを残しておくことで、2回目移行は成功したままか、失敗したままか、あるいは何か変化が起きたか、ということを自動でテストできるようになっています。

次回はもうひとつテストをまるごとスキップしてきた10.8 line-heightのテストに進む予定です。