1. はじめに
このセクションは規定的ではありません。
CSSのレイアウト段階は、フラグメントをボックスツリーから生成および配置する役割を持ちます。
この仕様は、開発者が計算済みスタイルやボックスツリーの変更に応じて ボックスツリーをレイアウトできるAPIについて記述します。
2. レイアウトAPIコンテナー
新しいalternative値が
<display-inside>生成式に追加されます:
layout(<ident>)
。
- layout()
- この値は要素にレイアウトAPIコンテナボックスを生成させます。
レイアウトAPIコンテナは、<display-inside>の計算値としてlayout()を持つ要素によって生成されるボックスです。
レイアウトAPIコンテナはその内容に対して新しいレイアウトAPI整形コンテキストを確立します。これはブロック整形コンテキストを確立するのと同様ですが、ブロックレイアウトの代わりに著者提供のレイアウトが使用されます。 例えば、フロートはレイアウトAPIコンテナに侵入せず、コンテナのマージンは内容のマージンと折り畳まれません。
レイアウトAPIコンテナの流れの中のすべての子はレイアウトAPI子要素と呼ばれ、著者定義のレイアウトによってレイアウトされます。
レイアウトAPIコンテナは内容に対してブロックコンテナと全く同じように包含ブロックを形成します。[CSS21]
注: 将来の仕様レベルでは包含ブロックの挙動を上書きする方法が追加される可能性があります。
overflowプロパティはレイアウトAPIコンテナに適用されます。詳細は§4.3 オーバーフローで説明します。
レイアウトが完全に著者の裁量であるため、他のレイアウトモード(例:フレックス・ブロック)で使用されるプロパティは適用されない場合があります。例えば、著者が子要素のmarginプロパティを尊重しない場合があります。
2.1. レイアウトAPIコンテナのペインティング
レイアウトAPIコンテナの子要素は、インラインブロック[CSS21]と全く同じように描画されますが、
レイアウトメソッドから返される順序(childFragments
経由)が生の文書順の代わりに使用され、
z-indexがauto以外の場合、positionがstaticでもスタッキングコンテキストが作成されます。
2.2. ボックスツリーの変換
レイアウトAPI子要素は、layout
options'のchildDisplay
(クラスのlayoutOptions
で設定)値によって異なる振る舞いをします。
layout options'のchildDisplay
の値が"block"
の場合、その子のdisplay値はブロック化されます。これはフレックスコンテナやグリッドコンテナの子と似ています。
詳細は[css3-display]を参照してください。
layout options'のchildDisplay
の値が"normal"
の場合、ブロック化は行われません。
代わりに、<display-outside>の計算値がinline(ルートインラインボックス)の場合、
LayoutFragment
が各行を表す形で
layoutNextFragment()
呼び出し時に生成されます。
注: これにより著者は各行の利用可能なインラインサイズを調整し、個別に行を配置できます。
LayoutChild
が表すルートインラインボックスの子にも追加の変換があります。
-
ブロックレベルボックスがインラインレベルボックス内にある場合、 インライン化されます。つまり、<display-outside>がinlineに設定されます。
-
floatがインラインレベルボックス内にある場合、 フローから除外されません。代わりに、流れ内として扱われ、インライン化されます。
上記いずれの場合も、子要素はアトミックインラインとなります。
注: ユーザーエージェントはブロックレベルボックスに遭遇した際、インライン分割や断片化は行いません。
LayoutChild
として表され、
"block"と"float"は両方ともアトミックインラインとなります。
<span id="inline-span"> Text <div id="block"></div> <div id="float"></div> Text </span>
3. レイアウトAPIのモデルと用語
このセクションでは著者が利用できるレイアウトAPIの概要を説明します。
現在のレイアウトは、現在レイアウト処理を行っているボックスのレイアウトアルゴリズムです。
親レイアウトは、ボックスの直接の親(現在のレイアウトの実行を要求しているレイアウトアルゴリズム)です。
子レイアウトは、LayoutChild
のレイアウトアルゴリズムです。
3.1. レイアウトの子要素
[Exposed=LayoutWorklet] interfaceLayoutChild
{ readonly attribute StylePropertyMapReadOnly styleMap; IntrinsicSizesRequest intrinsicSizes(); LayoutFragmentRequest layoutNextFragment(LayoutConstraintsconstraints
, ChildBreakTokenbreakToken
); };
LayoutChild
は内部スロットを持ちます:
-
[[box]]
CSSボックス。 -
[[styleMap]]
StylePropertyMapReadOnly
で、 子要素の計算済みスタイルです。これはchildInputProperties
に記載されているプロパティのみで構成されます。
LayoutChild
は、CSSによって生成されたボックス(レイアウト前)を表します。(ボックスはすべてdisplayの計算値がnone以外です)。
LayoutChild
は自体にレイアウト情報(インラインサイズやブロックサイズなど)は持たず、LayoutFragment
(レイアウト情報を持つ)を生成するために使われます。
著者はこのAPIでLayoutChild
を構築することはできません。これはレンダリングエンジンの別の段階(スタイル解決後)で行われます。
LayoutChild
は以下によって生成されます:
-
要素。
-
注: ::first-letterや::first-lineのような他の疑似要素は、レイアウト目的の
LayoutChild
を生成しません。これらはテキストノードへの追加スタイル情報です。 -
匿名ボックス。例えば、匿名ボックスは以下の結果として挿入される場合があります:
-
ブロック化されたテキストノード。(より一般的には、ルートインラインボックスがブロック化された場合)。
-
親にdisplay: tableを持たないdisplay: table-cellの要素。
-
LayoutChild
に分割されます:
<style> #box::before { content: 'hello!'; } </style> <div id="box">A block level box with text.</div> <img src="..." />
LayoutChild
にまとめられます(共通のルートインラインボックスを共有しているため):
This is a next node, <span>with some additional styling, that may</span> break over<br>multiple lines.
複数の非アトミックインラインは同じLayoutChild
にまとめられ、レンダリングエンジンが要素間でテキストシェーピングを行えるようにします。
現在レイアウト中のボックスの子要素を表すLayoutChild
の配列がレイアウトメソッドに渡されます。
styleMap
は、LayoutChild
thisから取得する場合、ユーザーエージェントは以下の手順を実行しなければなりません:
-
thisの
StylePropertyMapReadOnly
([[styleMap]]
内部スロット)を返す。
layoutNextFragment(constraints, breakToken)
メソッドがLayoutChild
thisで呼び出された場合、ユーザーエージェントは以下の手順を実行しなければなりません:
-
requestを新しい
LayoutFragmentRequest
として生成し、内部スロットを持つ:-
[[layoutChild]]
にthisを設定。 -
[[layoutConstraints]]
にconstraintsを設定。 -
[[breakToken]]
にbreakTokenを設定。
-
-
requestを返す。
intrinsicSizes()
メソッドがLayoutChild
thisで呼び出された場合、ユーザーエージェントは以下の手順を実行しなければなりません:
-
requestを新しい
IntrinsicSizesRequest
として生成し、内部スロットを持つ:-
[[layoutChild]]
にthisを設定。
-
-
requestを返す。
注: layoutNextFragment()
および intrinsicSizes()
は同期的に実行されません。詳細は§5.5.1 リクエストオブジェクトを参照してください。
3.1.1. LayoutChildrenとボックスツリー
各boxは、[[layoutChildMap]]
という内部スロットを持ちます。これは、マップであり、LayoutWorkletGlobalScope
からLayoutChild
への対応関係を保持します。
-
以下を確認する:
-
boxが現在ボックスツリーに接続されている。
-
boxの包含ブロックがレイアウトAPIコンテナである。
-
-
layoutChildMapをboxの
[[layoutChildMap]]
とする。 -
もしlayoutChildMap[workletGlobalScope]が存在しない場合、次の手順を実行する:
-
definitionをレイアウト定義の取得でnameとworkletGlobalScopeを渡して得る。
レイアウト定義の取得が成功し、definitionが
"invalid"
でないことを確認する。 -
childInputPropertiesをdefinitionの子入力プロパティとする。
-
layoutChildを新しい
LayoutChild
として、以下の内部スロットを持たせて生成する:-
[[box]]
にboxを設定。 -
[[styleMap]]
に新しいStylePropertyMapReadOnly
を設定し、childInputPropertiesに記載されたプロパティの計算値のみで初期化する。
-
-
セット layoutChildMap[workletGlobalScope]にlayoutChildを設定する。
-
-
取得したlayoutChildMap[workletGlobalScope]を返す。
ユーザーエージェントは、boxがボックスツリーに挿入された際に、すべてのLayoutWorkletGlobalScope
に対して[[layoutChildMap]]
を事前に生成してもよい。
ユーザーエージェントは、boxがボックスツリーから削除された際、[[layoutChildMap]]
を必ずクリアしなければなりません。
-
以下を確認する:
-
boxが現在ボックスツリーに接続されている。
-
-
もしboxの包含ブロックがレイアウトAPIコンテナでない場合、すべての手順を中止する。
-
layoutChildMapをboxの
[[layoutChildMap]]
とする。 -
全てのlayoutChildについてlayoutChildMap内を反復する:
-
styleMapをlayoutChildの
[[styleMap]]
とする。 -
styleMapの宣言をboxの新しい計算済みスタイルに基づき更新する。
-
boxの計算済みスタイルが変更されたとき、ユーザーエージェントはレイアウト子要素のスタイル更新アルゴリズムを実行しなければなりません。
3.2. レイアウトフラグメント
[Exposed=LayoutWorklet] interfaceLayoutFragment
{ readonly attribute doubleinlineSize
; readonly attribute doubleblockSize
; attribute doubleinlineOffset
; attribute doubleblockOffset
; readonly attribute anydata
; readonly attribute ChildBreakToken?breakToken
; };
LayoutFragment
は内部スロットを持ちます:
-
[[layoutFragmentRequest]]
LayoutFragmentRequest
。このフラグメントを生成したフラグメントリクエストです。 -
[[generator]]
このフラグメントを生成したジェネレーター。
LayoutFragment
は、LayoutChild
のレイアウト後のCSSフラグメントを表します。これはlayoutNextFragment()
メソッドによって生成されます。
LayoutFragment
には、inlineSize
とblockSize
属性があり、それぞれ子要素のレイアウトアルゴリズムによって設定されます。これらはCSSフラグメントのborder boxサイズであり、現在のレイアウトの書字モードに相対的です。
inlineSize
とblockSize
属性は変更できません。
現在のレイアウトで異なるinlineSize
またはblockSize
が必要な場合、著者は異なる引数でlayoutNextFragment()
を再度実行しなければなりません。
現在のレイアウト内の著者は、生成されたLayoutFragment
のinlineOffset
とblockOffset
属性を設定することで位置を指定できます。著者が設定しない場合はデフォルトでゼロになります。inlineOffset
とblockOffset
属性は、LayoutFragment
の親のborder
boxに対する位置を表し、transformやpositioning(例えばフラグメントが相対配置された場合)を適用する前の値です。
registerLayout('block-like', class { *intrinsicSizes(children, edges, styleMap) { const childrenSizes = yield children.map((child) => { return child.intrinsicSizes(); }); const maxContentSize = childrenSizes.reduce((max, childSizes) => { return Math.max(max, childSizes.maxContentSize); }, 0) + edges.all.inline; const minContentSize = childrenSizes.reduce((max, childSizes) => { return Math.max(max, childSizes.minContentSize); }, 0) + edges.all.inline; return {maxContentSize, minContentSize}; } *layout(children, edges, constraints, styleMap) { const availableInlineSize = constraints.fixedInlineSize - edges.all.inline; const availableBlockSize = (constraints.fixedBlockSize || Infinity) - edges.all.block; const childFragments = []; const childConstraints = { availableInlineSize, availableBlockSize }; const childFragments = yield children.map((child) => { return child.layoutNextFragment(childConstraints); }); let blockOffset = edges.all.blockStart; for (let fragment of childFragments) { // フラグメントをブロック方向に配置し、インライン方向で中央揃えする fragment.blockOffset = blockOffset; fragment.inlineOffset = Math.max( edges.all.inlineStart, (availableInlineSize - fragment.inlineSize) / 2); blockOffset += fragment.blockSize; } const autoBlockSize = blockOffset + edges.all.blockEnd; return { autoBlockSize, childFragments, }; } });
レイアウトAPIコンテナは、data
属性を利用して他のレイアウトAPIコンテナと通信できます。これはdata
メンバーでFragmentResultOptions
辞書にセットされます。
LayoutFragment
のbreakToken
は、LayoutChild
が最後に断片化された場所を指定します。breakToken
がnullの場合、そのLayoutChild
はそのトークンチェーンに対してこれ以上LayoutFragment
を生成しません。breakToken
は、layoutNextFragment()
関数に渡すことで、特定の子要素の次のLayoutFragment
を生成できます。breakToken
は変更できません。
現在のレイアウトで異なるbreakToken
が必要な場合は、著者は異なる引数でlayoutNextFragment()
を再度実行する必要があります。
-
targetRealmをgeneratorのRealmとする。
-
fragmentを新しい
LayoutFragment
として、以下の設定で生成する:-
[[layoutFragmentRequest]]
内部スロットはlayoutFragmentRequest。 -
[[generator]]
内部スロットはgenerator。 -
inlineSize
はinternalFragmentのインラインサイズ(現在のレイアウトの書字モードに対する相対値)。 -
inlineOffset
の初期値は0。 -
blockOffset
の初期値は0。 -
breakToken
は、internalFragmentの内部ブレークトークンに対応する新しいChildBreakToken
。 -
もしinternalFragmentにclonedDataオブジェクトが格納されていれば、
data
はStructuredDeserialize(clonedData,targetRealm)の結果。なければnull。
-
-
fragmentを返す。
3.3. 本来サイズ
[Exposed=LayoutWorklet] interfaceIntrinsicSizes
{ readonly attribute doubleminContentSize
; readonly attribute doublemaxContentSize
; };
IntrinsicSizes
オブジェクトは内部スロットを持ちます:
-
[[intrinsicSizesRequest]]
IntrinsicSizesRequest
。これは本来サイズリクエストであり、この本来サイズ群を生成したものです。
IntrinsicSizes
オブジェクトは、CSSのmin-contentサイズおよびmax-contentサイズを表します。これは、minContentSize
属性とmaxContentSize
属性を持ち、LayoutChild
のborder
boxのmin/max-content寄与を現在のレイアウトについて表します。これらの属性は、現在のレイアウトの書字方向のインライン方向に対する相対値です。
minContentSize
とmaxContentSize
は変更できません。
これらは、現在のレイアウトパス内でLayoutChild
について変化しないものとします。
<style> .child-0 { width: 380px; border: solid 10px; } .child-1 { border: solid 5px; } .box { display: layout(intrinsic-sizes-example); font: 25px/1 Ahem; } </style> <div class="box"> <div class="child-0"></div> <div class="child-1">XXX XXXX</div> </div>
registerLayout('intrinsic-sizes-example', class { *intrinsicSizes(children, edges, styleMap) { const childrenSizes = yield children.map((child) => { return child.intrinsicSizes(); }); childrenSizes[0].minContentSize; // 400, (380+10+10) 固定サイズの子。 childrenSizes[0].maxContentSize; // 400, (380+10+10) 固定サイズの子。 childrenSizes[1].minContentSize; // 100, "XXXX"のサイズ。 childrenSizes[1].maxContentSize; // 200, "XXX XXXX"のサイズ。 } *layout() {} });
-
intrinsicSizesを新しい
IntrinsicSizes
として、以下の設定で生成する:-
[[intrinsicSizesRequest]]
内部スロットはintrinsicSizesRequest。 -
minContentSize
はinternalIntrinsicSizesのborder boxmin-content寄与サイズ(現在のレイアウトの書字モードに対する相対値)。 -
maxContentSize
はinternalIntrinsicSizesのborder boxmax-content寄与サイズ(現在のレイアウトの書字モードに対する相対値)。
-
-
intrinsicSizesを返す。
3.4. レイアウト制約
[Constructor
(optional LayoutConstraintsOptionsoptions
),Exposed=LayoutWorklet] interfaceLayoutConstraints
{ readonly attribute doubleavailableInlineSize
; readonly attribute doubleavailableBlockSize
; readonly attribute double?fixedInlineSize
; readonly attribute double?fixedBlockSize
; readonly attribute doublepercentageInlineSize
; readonly attribute doublepercentageBlockSize
; readonly attribute double?blockFragmentationOffset
; readonly attribute BlockFragmentationTypeblockFragmentationType
; readonly attribute anydata
; }; dictionaryLayoutConstraintsOptions
{ doubleavailableInlineSize
= 0; doubleavailableBlockSize
= 0; doublefixedInlineSize
; doublefixedBlockSize
; doublepercentageInlineSize
; doublepercentageBlockSize
; doubleblockFragmentationOffset
; BlockFragmentationTypeblockFragmentationType
= "none"; anydata
; }; enumBlockFragmentationType
{"none"
,"page"
,"column"
,"region"
};
LayoutConstraints
オブジェクトが layout メソッドに渡され、現在のレイアウトがレイアウトを実行するためのすべての制約を表します。また、このオブジェクトは利用可能なスペースの情報を 子レイアウトに渡すためにも使用されます。
LayoutConstraints
オブジェクトには availableInlineSize
と availableBlockSize
属性があります。これは、レイアウトが尊重すべき 利用可能なスペースを LayoutFragment
のために表します。
注: 一部のレイアウトでは、このサイズを超える LayoutFragment
を生成する必要があります。例えば 置換要素 などです。親レイアウトはこれが発生することを想定し、適切に対応する必要があります。
親レイアウトは 現在のレイアウトが正確に特定のサイズであることを要求する場合があります。fixedInlineSize
や fixedBlockSize
が指定されている場合、現在のレイアウトは指定された方向に指定されたサイズの LayoutFragment
を生成する必要があります。
registerLayout('flex-distribution-like', class { *intrinsicSizes(children, edges, styleMap) { const childrenSizes = yield children.map((child) => { return child.intrinsicSizes(); }); const maxContentSize = childrenSizes.reduce((sum, childSizes) => { return sum + childSizes.maxContentSize; }, 0) + edges.all.inline; const minContentSize = childrenSizes.reduce((max, childSizes) => { return sum + childSizes.minContentSize; }, 0) + edges.all.inline; return {maxContentSize, minContentSize}; } *layout(children, edges, constraints, styleMap) { const availableInlineSize = constraints.fixedInlineSize - edges.all.inline; const availableBlockSize = (constraints.fixedInlineSize || Infinity) - edges.all.block; const childConstraints = { availableInlineSize, availableBlockSize }; const unconstrainedChildFragments = yield children.map((child) => { return child.layoutNextFragment(childConstraints); }); const unconstrainedSizes = []; const totalSize = unconstrainedChildFragments.reduce((sum, fragment, i) => { unconstrainedSizes[i] = fragment.inlineSize; return sum + fragment.inlineSize; }, 0); // 余分なスペースを子要素間で分配する。 const remainingSpace = Math.max(0, inlineSize - totalSize); const extraSpace = remainingSpace / children.length; const childFragments = yield children.map((child, i) => { return child.layoutNextFragment({ fixedInlineSize: unconstrainedSizes[i] + extraSpace, availableBlockSize }); }); // フラグメントの位置決め。 let inlineOffset = 0; let maxChildBlockSize = 0; for (let fragment of childFragments) { fragment.inlineOffset = inlineOffset; fragment.blockOffset = edges.all.blockStart; inlineOffset += fragment.inlineSize; maxChildBlockSize = Math.max(maxChildBlockSize, fragment.blockSize); } return { autoBlockSize: maxChildBlockSize + edges.all.block, childFragments, }; } });
LayoutConstraints
オブジェクトには percentageInlineSize
と percentageBlockSize
属性があります。これらは、レイアウトのパーセンテージがレイアウト実行時にどのサイズを基準に解決されるかを表します。
LayoutConstraints
には blockFragmentationType
属性があります。現在のレイアウトは、可能であれば LayoutFragment
を blockFragmentationOffset
で断片化する必要があります。
現在のレイアウトは、LayoutChild
を
blockFragmentationType
に基づき断片化しない選択をすることができます。例えば、子に break-inside:
avoid-page; のようなプロパティがある場合です。
-
sizingMode が
"block-like"
の場合:-
fixedInlineSize を box の border-box インラインサイズ(box の書字モードに対して)を block コンテナと同様に計算した結果とする。
-
fixedBlockSize を box の ブロックサイズ が auto の場合は null、そうでなければ block コンテナと同様に計算した box の border-box ブロックサイズ とする。
-
次の内容で新しい
LayoutConstraints
オブジェクトを返す:-
fixedInlineSize
とavailableInlineSize
を fixedInlineSize に設定。 -
percentageInlineSize
を internalLayoutConstraints のインライン軸(box の書字モードに対して)のパーセンテージ解決サイズに設定。 -
fixedBlockSize
を fixedBlockSize に設定。 -
availableBlockSize
を null でなければ fixedBlockSize に、そうでなければ internalLayoutConstraints のブロック軸(box の書字モードに対して)の 利用可能なスペース に設定。 -
percentageBlockSize
を internalLayoutConstraints のブロック軸(box の書字モードに対して)のパーセンテージ解決サイズに設定。
-
-
-
sizingMode が
"manual"
の場合:-
次の内容で新しい
LayoutConstraints
オブジェクトを返す:-
fixedInlineSize
/fixedBlockSize
を internalLayoutConstraints の親レイアウトによって強制された(box の書字モードに対しての)固定インライン/ブロックサイズに設定。どちらも null の場合がある。注: この場合が発生する様々なシナリオについては §4.1 サイズ指定 を参照してください。
-
availableInlineSize
/availableBlockSize
を internalLayoutConstraints の 利用可能なスペース に設定。 -
percentageInlineSize
/percentageBlockSize
を internalLayoutConstraints のパーセンテージ解決サイズに設定。
-
-
3.5. 分割と断片化
[Exposed=LayoutWorklet] interfaceChildBreakToken
{ readonly attribute BreakTypebreakType
; readonly attribute LayoutChildchild
; }; [Exposed=LayoutWorklet] interfaceBreakToken
{ readonly attribute sequence<ChildBreakToken>childBreakTokens
; readonly attribute anydata
; }; dictionaryBreakTokenOptions
{ sequence<ChildBreakToken>childBreakTokens
; anydata
= null; }; enumBreakType
{"none"
,"line"
,"column"
,"page"
,"region"
};
LayoutChild
は複数の LayoutFragment
を生成することができます。
LayoutChild
は、blockFragmentationType
が none でなければブロック方向に断片化する場合があります。さらに、LayoutChild
が インラインレベル コンテンツを表す場合、レイアウトオプション の childDisplay
(layoutOptions
により設定)が "normal"
の場合、行ごとに断片化することがあります。
後続の LayoutFragment
は、前の LayoutFragment
の breakToken
を使用して生成されます。これによって、子レイアウトは LayoutFragment
を ChildBreakToken
に記録された地点から生成することができます。
この例はまた、breakToken
を用いて LayoutFragment
の次のフラグメントを生成する方法を示しています。
さらに、BreakToken
を使用し、LayoutConstraints
の blockFragmentationType
を尊重して、前回の BreakToken
からレイアウトを再開しています。
FragmentResultOptions
と breakToken
を返し、
レイアウトの再開に利用します。
registerLayout('indent-lines', class { static layoutOptions = {childDisplay: 'normal'}; static inputProperties = ['--indent', '--indent-lines']; *layout(children, edges, constraints, styleMap, breakToken) { // (内側の)利用可能サイズを決定する。 const availableInlineSize = constraints.fixedInlineSize - edges.all.inline; const availableBlockSize = (constraints.fixedBlockSize || Infinity) - edges.all.block; // インデントする行数とインデント量を決定する。 const indent = resolveLength(constraints, styleMap.get('--indent')); let lines = styleMap.get('--indent-lines').value; const childFragments = []; let childBreakToken = null; if (breakToken) { childBreakToken = breakToken.childBreakTokens[0]; // すでにフラグメントを生成した子要素をすべて削除する。 children.splice(0, children.indexOf(childBreakToken.child)); } let blockOffset = edges.all.blockStart; let child = children.shift(); while (child) { const shouldIndent = lines-- > 0; // インデント分インラインサイズを調整する。 const childAvailableInlineSize = shouldIndent ? availableInlineSize - indent : availableInlineSize; const childConstraints = { availableInlineSize: childAvailableInlineSize, availableBlockSize, percentageInlineSize: availableInlineSize, blockFragmentationType: constraints.blockFragmentationType, }; const fragment = yield child.layoutNextFragment(childConstraints, childBreakToken); childFragments.push(fragment); // フラグメントの位置決め。 fragment.inlineOffset = shouldIndent ? edges.all.inlineStart + indent : edges.all.inlineStart; fragment.blockOffset = blockOffset; blockOffset += fragment.blockSize; // ブロック断片化の制限を超えたかどうかを確認する。 if (constraints.blockFragmentationType != 'none' && blockOffset > constraints.blockSize) { break; } if (fragment.breakToken) { childBreakToken = fragment.breakToken; } else { // フラグメントに breakToken が無い場合、次の子要素に移る。 child = children.shift(); childBreakToken = null; } } const autoBlockSize = blockOffset + edges.all.blockEnd; // フラグメントを返す。 const result = { autoBlockSize, childFragments: childFragments, } if (childBreakToken) { result.breakToken = { childBreakTokens: [childBreakToken], }; } return result; } });
3.6. エッジ
[Exposed=LayoutWorklet] interfaceLayoutEdgeSizes
{ readonly attribute doubleinlineStart
; readonly attribute doubleinlineEnd
; readonly attribute doubleblockStart
; readonly attribute doubleblockEnd
; // Convenience attributes for the sum in one direction. readonly attribute doubleinline
; readonly attribute doubleblock
; }; [Exposed=LayoutWorklet] interfaceLayoutEdges
{ readonly attribute LayoutEdgeSizesborder
; readonly attribute LayoutEdgeSizesscrollbar
; readonly attribute LayoutEdgeSizespadding
; readonly attribute LayoutEdgeSizesall
; };
LayoutEdges
オブジェクトは layout メソッドに渡されます。これは、現在レイアウトされているボックスの ボックスモデルのエッジのサイズを表します。
LayoutEdges
には
border
、
scrollbar
、
padding
属性があります。これらはそれぞれのエッジの幅を表します。
LayoutEdges
には
all
属性があります。これは
border
、
scrollbar
、
padding
エッジの合計値を表す便利な属性です。
LayoutEdgeSizes
オブジェクトは、各 抽象的な寸法
(inlineStart
、
inlineEnd
、
blockStart
、
blockEnd
)
のエッジの幅を CSS ピクセル単位で表します。
inline
と block
は LayoutEdgeSizes
オブジェクト上の便利な属性で、その方向の合計値を表します。
LayoutEdges
の内容を示します。
<style> .container { width: 50px; height: 50px; } .box { display: layout(box-edges); padding: 10%; border: solid 2px; overflow-y: scroll; } </style> <div class="container"> <div class="box"></div> </div>
registerLayout('box-edges', class { *layout(children, edges, constraints, styleMap, breakToken) { edges.padding.inlineStart; // 5 (10% * 50px = 5px) edges.border.blockEnd; // 2 edges.scrollbar.inlineEnd; // UA依存、0か16など edges.all.block; // 14 (2 + 5 + 5 + 2) } }
4. 他のモジュールとの相互作用
このセクションでは、他の CSS モジュールが CSS Layout API とどのように相互作用するかを説明します。
4.1. サイズ指定
ユーザーエージェントは LayoutConstraints
オブジェクトを使って、現在のレイアウトにフラグメントの希望サイズを伝えなければなりません。
ユーザーエージェントがボックスにサイズを強制したい場合、fixedInlineSize
と fixedBlockSize
属性を使います。
レイアウト API コンテナは、レイアウトオプション の sizing
の値によって、様々な方法でサイズ情報を受け取ることができます(クラスの layoutOptions
で設定)。
レイアウトオプション の sizing
の値が "block-like"
の場合、レイアウト API コンテナに渡される LayoutConstraints
は:
-
必ず
fixedInlineSize
を [css-sizing-3] で指定されたルールと参加している整形コンテキストに基づいて計算・設定しなければなりません。例:-
ブロックレベルボックスが ブロック整形コンテキスト内にある場合、ブロックボックスとして整形コンテキストを確立し、auto インラインサイズは非置換ブロックボックスと同様に計算されます。
-
インラインレベルボックスが インライン整形コンテキスト内にある場合、インラインブロック等の原子的インラインレベルボックスとしてサイズ指定されます。
-
-
必ず
fixedBlockSize
を [css-sizing-3] で指定されたルールと整形コンテキストに基づいて計算・設定しなければなりません。レイアウト API コンテナ が auto ブロックサイズで事前に決定できない場合、fixedBlockSize
はnull
に設定します。
レイアウトオプション の sizing
の値が "manual"
の場合、ユーザーエージェントは fixedInlineSize
や fixedBlockSize
を事前に計算してはなりません。
ただし、参加している整形コンテキストによって特定のサイズが強制される場合は例外です。例えば:
-
レイアウト API コンテナが ブロック整形コンテキスト内、inflowで、auto インラインサイズの場合、ユーザーエージェントは
fixedInlineSize
を stretch-fit インラインサイズに設定しなければなりません。
<style> #container { width: 100px; height: 100px; box-sizing: border-box; padding: 5px; } #layout-api { display: layout(foo); margin: 0 20px; } </style> <div id="container"> <div id="layout-api"></div> </div>
4.1.1. 配置されたレイアウトのサイズ指定
レイアウト API
コンテナがフロー外で配置されている場合、ユーザーエージェントは
配置サイズの方程式(CSS Positioned Layout 3 §8.1
絶対/固定配置非置換要素の幅、
CSS Positioned Layout 3 §8.3
絶対/固定配置非置換要素の高さ)を解決し、
適切な fixedInlineSize
と fixedBlockSize
を設定しなければなりません。
<style> #container { position: relative; width: 100px; height: 100px; } #layout-api { display: layout(foo); top: 10px; bottom: 10px; left: 10px; right: 10px; position: absolute; } </style> <div id="container"> <div id="layout-api"></div> </div>
4.2. 配置
この仕様レベルにおけるすべての配置はユーザーエージェントによって処理されます。
その結果:
-
フロー外の子要素は
LayoutChild
として現れません。 -
レイアウト API コンテナは 包含ブロックを確立します。ブロックコンテナと同様に。[CSS21]
-
inlineOffset
とblockOffset
は、 配置や変形が行われる前のフラグメントの位置を表します。 -
静的位置は、 レイアウト API コンテナの絶対配置子要素の場合、 inline-start、block-start のパディングエッジになります。自動マージンは子要素に対してゼロとして扱われます。
-
"child-relative" はレイアウト作者に渡される唯一の子要素です。もし (
inlineOffset
= 20
,blockOffset
= 30
) に配置された場合、最終的な位置は (25
,40
) となり、相対配置はユーザーエージェントによって処理されます。 -
"child-absolute" は
LayoutChild
としては現れず、 ユーザーエージェントによってレイアウト・配置されます。 -
上記の例は、sticky や固定配置の子要素にも同様に適用されます。
<style> #container { display: layout(foo); position: relative; /* container は包含ブロック */ width: 100px; height: 100px; } #child-relative { position: relative; left: 5px; top: 10px; } </style> <div id="container"> <div id="child-relative"></div> <div id="child-absolute"></div> </div>
4.3. オーバーフロー
スクロール可能オーバーフローは、レイアウト API コンテナの場合、この仕様レベルではユーザーエージェントによって処理されます。
著者の レイアウト API コンテナ は、ブロックコンテナと同様にスクロール可能オーバーフローを計算するべきです。
たとえ著者の レイアウト API コンテナ がフラグメントを スクロール可能オーバーフロー領域に配置しても、相対位置や変形によってフラグメントが移動し、スクロール可能オーバーフロー領域となり、オーバーフローが発生しない場合もあります。
4.4. 断片化
親レイアウトは、現在のレイアウトに 断片化を要求するために、
blockFragmentationType
と blockFragmentationOffset
を設定できます。
例:[css-multicol-1] のレイアウトでは、blockFragmentationType
に "column"
を設定し、blockFragmentationOffset
に子要素を断片化したい位置を指定します。
4.5. アラインメント
レイアウト API
コンテナの最初/最後のベースラインセットは、ブロックコンテナと同様に生成されます(CSS Box
Alignment 3 §9.1 Determining the Baselines of a Box参照)。ただし、インフロー子要素の順序は、document order ではなく layout
メソッド(childFragments
経由)で返された順序で決定されるべきです。
LayoutChild
からベースライン情報を取得する場合:
const fragment = yield child.layoutNextFragment({ fixedInlineSize: availableInlineSize, baselineRequests: ['alphabetic', 'middle'], }); fragment.baselines.get('alphabetic') === 25 /* など */;
親レイアウト向けにベースライン情報を生成する場合:
registerLayout('baseline-producing', class { *layout(children, edges, constraints, styleMap) { const result = {baselines: {}}; for (let baselineRequest of constraints.baselineRequests) { // baselineRequest === 'alphabetic' など result.baselines[baselineRequest] = 25; } return result; } });
5. レイアウト
このセクションでは、CSS Layout API がユーザーエージェントのレイアウトエンジンとどのように相互作用するかを説明します。
5.1. 概念
レイアウト定義とは、著者が定義したレイアウトについて、LayoutWorkletGlobalScope
が必要とする情報を記述する構造体です(layout()関数で参照できます)。次の要素から成ります:
-
クラスコンストラクター(クラスのコンストラクター)
-
レイアウトジェネレーター関数(レイアウトジェネレーター関数コールバック)
-
内在サイズジェネレーター関数(内在サイズジェネレーター関数コールバック)
-
コンストラクター有効フラグ
-
入力プロパティ(リスト形式の
DOMStrings
) -
子入力プロパティ(リスト形式の
DOMStrings
) -
レイアウトオプション(
LayoutOptions
)
文書レイアウト定義とは、著者が定義したレイアウトについて、文書が必要とする情報を記述する構造体です(layout()関数で参照できます)。次の要素から成ります:
-
入力プロパティ(リスト形式の
DOMStrings
) -
子入力プロパティ(リスト形式の
DOMStrings
) -
レイアウトオプション(
LayoutOptions
)
5.2. レイアウトの無効化
各ボックスには、関連付けられたレイアウト有効フラグがあります。これはlayout-validまたはlayout-invalidのいずれかになります。初期値はlayout-invalidです。
各ボックスには、関連付けられた内在サイズ有効フラグがあります。これはintrinsic-sizes-validまたはintrinsic-sizes-invalidのいずれかになります。初期値はintrinsic-sizes-invalidです。
-
layoutFunction を box の計算スタイルの display プロパティの layout() 関数とする(存在する場合)。他の型の値(例:grid)の場合は、これらの手順を中止する。
-
name を layoutFunction の最初の引数とする。
-
documentDefinition を 文書レイアウト定義を取得(nameを指定)した結果とする。
文書レイアウト定義を取得が失敗した場合、またはdocumentDefinition が
"invalid"
の場合は、これらの手順を中止する。 -
inputProperties を documentDefinition の 入力プロパティとする。
-
childInputProperties を documentDefinition の 子入力プロパティとする。
-
inputProperties の各 property について、property の 計算値が変化していれば、レイアウト有効フラグをboxに対してlayout-invalidに設定し、内在サイズ有効フラグをintrinsic-sizes-invalidに設定する。
-
childInputProperties の各 property について、property の 計算値が変化していれば、レイアウト有効フラグをboxに対してlayout-invalidに設定し、内在サイズ有効フラグをintrinsic-sizes-invalidに設定する。
レイアウト関数の無効化は、ユーザーエージェントがボックスの計算スタイルを再計算したとき、またはそのボックスの子の計算スタイルが再計算されたときに必ず実行されます。
LayoutChild
で表現される子ボックスがボックスツリーに追加・削除された場合、または計算スタイルの変更や子孫の変更によりレイアウトが無効化された場合、かつその無効化がボックスツリー上位に伝播すべき場合、現在のレイアウト有効フラグをlayout-invalidに、現在の内在サイズ有効フラグをintrinsic-sizes-invalidに設定します。
レイアウト API
コンテナの計算スタイルが変更され、その変更がLayoutEdges
オブジェクト内部の値に影響する場合、ボックスのレイアウト有効フラグをlayout-invalidに、ボックスの内在サイズ有効フラグをintrinsic-sizes-invalidに設定します。
計算スタイルの変更がLayoutConstraints
オブジェクト内部の値に影響する場合は、ボックスの内在サイズ有効フラグのみintrinsic-sizes-invalidに設定します。
注: LayoutConstraints
オブジェクトはレイアウト関数だけに渡されるので、内在サイズを無効化する必要はありません。
LayoutEdges
オブジェクトを変更する可能性があります:
また、以下のプロパティはLayoutConstraints
オブジェクトを変更する可能性があります:
注: これは CSS Layout API に関連するレイアウト無効化のみを説明しています。すべてのボックスは概念的にレイアウト有効フラグを持ち、これらの変更はボックスツリーを通して伝播します。
5.3. レイアウトワークレット
layoutWorklet
属性は、レイアウトに関連するすべてのクラスを管理するWorklet
へアクセスすることができます。
layoutWorklet
のワークレットグローバルスコープ型はLayoutWorkletGlobalScope
です。
partial interface CSS {
[SameObject] readonly attribute Worklet layoutWorklet
;
};
LayoutWorkletGlobalScope
は、layoutWorklet
のグローバル実行コンテキストです。
[Global=(Worklet,LayoutWorklet),Exposed=LayoutWorklet] interfaceLayoutWorkletGlobalScope
: WorkletGlobalScope { void registerLayout(DOMStringname
, VoidFunctionlayoutCtor
); };
5.4. レイアウトの登録
[Exposed=LayoutWorklet] dictionaryLayoutOptions
{ ChildDisplayTypechildDisplay
= "block"; LayoutSizingModesizing
= "block-like"; }; [Exposed=LayoutWorklet] enumChildDisplayType
{"block"
,"normal"
, }; [Exposed=LayoutWorklet] enumLayoutSizingMode
{"block-like"
,"manual"
, };
document は map 型の 文書レイアウト定義 を持ちます。
最初はこの map は空ですが、registerLayout(name, layoutCtor)
が呼び出されたときに値が追加されます。
LayoutWorkletGlobalScope
は map 型の
レイアウト定義 を持ちます。
最初はこの map は空ですが、registerLayout(name, layoutCtor)
が呼び出されたときに値が追加されます。
box のうち layout API container に該当するものは、 map 型の レイアウトクラスインスタンス を持ちます。最初はこの map は空ですが、ユーザーエージェントが 内在サイズの決定 または フラグメント生成 を行う際に値が追加されます。
registerLayout(name, layoutCtor)
メソッドが呼び出されたとき、ユーザーエージェントは以下の手順を必ず実行します:
-
layoutDefinitionMap を
LayoutWorkletGlobalScope
の レイアウト定義 map とする。 -
layoutDefinitionMap[name] が 存在する場合、throw で "InvalidModificationError" DOMException を投げて、以降の手順を中止する。
-
inputProperties を空の
sequence<DOMString>
とする。 -
inputPropertiesIterable を Get(layoutCtor, "inputProperties") の結果とする。
-
inputPropertiesIterable が undefined でなければ、inputProperties に 変換した値(
sequence<DOMString>
)を格納する。例外が thrown された場合は例外を投げて以降の手順を中止する。注: input properties getter が返す CSS プロパティのリストは、カスタムもしくはネイティブ CSS プロパティどちらでもよい。
注: CSS プロパティのリストにはショートハンドも含まれる可能性がある。
注: レイアウトクラスを将来互換にするには、ユーザーエージェントで現在無効な CSS プロパティも含めることができる(例:
margin-bikeshed-property
)。 -
childInputProperties を空の
sequence<DOMString>
とする。 -
childInputPropertiesIterable を Get(layoutCtor, "childInputProperties") の結果とする。
-
childInputPropertiesIterable が undefined でなければ、childInputProperties に 変換した値(
sequence<DOMString>
)を格納する。例外が thrown された場合は例外を投げて以降の手順を中止する。 -
layoutOptionsValue を Get(layoutCtor, "layoutOptions") の結果とする。
-
layoutOptions を 変換した
LayoutOptions
とする。 例外が thrown された場合は例外を投げて以降の手順を中止する。 -
IsConstructor(layoutCtor) が false なら、throw で TypeError を投げて以降の手順を中止する。
-
prototype を Get(layoutCtor, "prototype") の結果とする。
-
Type(prototype) が Object でないなら throw で TypeError を投げて以降の手順を中止する。
-
intrinsicSizes を Get(prototype,
"intrinsicSizes"
) の結果とする。 -
IsCallable(intrinsicSizes) が false なら throw で TypeError を投げて以降の手順を中止する。
-
intrinsicSizes の
[[FunctionKind]]
内部スロットが"generator"
でないなら throw で TypeError を投げて以降の手順を中止する。 -
layout を Get(prototype,
"layout"
) の結果とする。 -
IsCallable(layout) が false なら throw で TypeError を投げて以降の手順を中止する。
-
layout の
[[FunctionKind]]
内部スロットが"generator"
でないなら throw で TypeError を投げて以降の手順を中止する。 -
definition を新しい レイアウト定義として以下の内容で生成する:
-
クラスコンストラクターは layoutCtor。
-
レイアウトジェネレーター関数は layout。
-
内在サイズジェネレーター関数は intrinsicSizes。
-
コンストラクター有効フラグは true。
-
入力プロパティは inputProperties。
-
子入力プロパティは childInputProperties。
-
レイアウトオプションは layoutOptions。
-
-
Set layoutDefinitionMap[name] に definition を設定する。
-
Queue a task で以下を実行:
-
documentLayoutDefinitionMap を関連づけられた document の 文書レイアウト定義 map とする。
-
documentDefinition を新しい 文書レイアウト定義として以下の内容で生成する:
-
入力プロパティは inputProperties。
-
子入力プロパティは childInputProperties。
-
レイアウトオプションは layoutOptions。
-
-
documentLayoutDefinitionMap[name] が 存在する場合、以下を実行:
-
existingDocumentDefinition を get(documentLayoutDefinitionMap[name]) の結果とする。
-
existingDocumentDefinition が
"invalid"
なら以降の手順を中止する。 -
existingDocumentDefinition と documentDefinition が等価でない場合 (入力プロパティ、子入力プロパティ、レイアウトオプション が異なる場合)、次を行う:
Set documentLayoutDefinitionMap[name] に
"invalid"
を設定する。同じクラスが異なる
inputProperties
、childInputProperties
、layoutOptions
で登録された旨のエラーをデバッグコンソールに出力する。
-
-
それ以外の場合は、set documentLayoutDefinitionMap[name] に documentDefinition を設定する。
-
class MyLayout { static get inputProperties() { return ['--foo']; } static get childrenInputProperties() { return ['--bar']; } static get layoutOptions() { return {childDisplay: 'normal', sizing: 'block-like'} } *intrinsicSizes(children, edges, styleMap) { // 内在サイズの処理。 } *layout(children, edges, constraints, styleMap, breakToken) { // レイアウト処理。 } }
5.5. レイアウトエンジン
Promises
-
エラー報告がより良い。
-
開発者体験が向上する可能性。
Generator
-
より「厳格」— レイアウト操作のみを実行できる。各呼び出しでどの promise API が動作するか制限不要。
-
より効率的なバインディング overhead の可能性。
5.5.1. リクエストオブジェクト
[Exposed=LayoutWorklet] interfaceIntrinsicSizesRequest
{ }; [Exposed=LayoutWorklet] interfaceLayoutFragmentRequest
{ }; typedef (IntrinsicSizesRequest or LayoutFragmentRequest)LayoutFragmentRequestOrIntrinsicSizesRequest
;
IntrinsicSizesRequest
は内部スロット:
-
[[layoutChild]]
LayoutChild
。内在サイズ計算対象の子。
LayoutFragmentRequest
は内部スロット:
-
[[layoutChild]]
LayoutChild
。フラグメント生成対象の子。 -
[[layoutConstraints]]
LayoutConstraintsOptions
辞書。LayoutChild
のレイアウトアルゴリズムへの入力制約。 -
[[breakToken]]
ChildBreakToken
オブジェクト。レイアウト再開時の break token。
著者定義の layout メソッドと intrinsic sizes メソッドは、通常の javascript 関数ではなく generator 関数となっています。これはユーザーエージェントが非同期・並列レイアウトエンジンをサポートするためです。
著者が layoutNextFragment()
を LayoutChild
に呼び出すと、ユーザーエージェントは LayoutFragment
を同期的に生成して返すのではなく、LayoutFragmentRequest
を返します。これは著者には完全に不透明なオブジェクトですが、内部スロットとして layoutNextFragment()
の呼び出し情報を持ちます。
LayoutFragmentRequest
がレイアウトジェネレーターオブジェクトから yield された場合、ユーザーエージェントのレイアウトエンジンは他の処理と並行して、または別スレッドでこのアルゴリズムを非同期に実行することがあります。エンジンが LayoutFragment
を生成したら、その結果でジェネレーターオブジェクトを「tick」します。
intrinsicSizes()
メソッドも同様です。
class LayoutEngine { // rootBox、LayoutConstraints、BreakToken を受け取って LayoutFragment を生成する。 layoutEntry(rootBox, rootPageConstraints, breakToken) { return layoutFragment({ box: rootBox, layoutConstraints: rootPageConstraints, breakToken: breakToken, }); } // LayoutFragmentRequest を受け取り、対応するレイアウトアルゴリズムを呼び出して LayoutFragment を生成する。 layoutFragment(fragmentRequest) { const box = fragmentRequest.layoutChild; const algorithm = selectLayoutAlgorithmForBox(box); const fragmentRequestGenerator = algorithm.layout( fragmentRequest.layoutConstraints, box.children, box.styleMap, fragmentRequest.breakToken); let nextFragmentRequest = fragmentRequestGenerator.next(); while (!nextFragmentRequest.done) { // UA はフラグメント生成を並列(別スレッド)で行うこともできる。例は同期処理のみ。 let fragments = nextFragmentRequest.value.map(layoutFragment); // UA は他の処理(GC等)で yield することもできる。例は同期で進める。 nextFragmentRequest = fragmentRequestGenerator.next(fragments); } return nextFragmentRequest.value; // 最終的な LayoutFragment を返す。 } }
5.6. レイアウトの実行
// 著者定義 layout() メソッドの最終返却値。 dictionaryFragmentResultOptions
{ doubleinlineSize
= 0; doubleblockSize
= 0; doubleautoBlockSize
= 0; sequence<LayoutFragment>childFragments
= []; anydata
= null; BreakTokenOptionsbreakToken
= null; }; dictionaryIntrinsicSizesResultOptions
{ doublemaxContentSize
; doubleminContentSize
; };
5.6.1. 内在サイズの決定
内在サイズの決定アルゴリズムは、ユーザーエージェントが著者定義レイアウトから box の 内在サイズ情報を取得する方法を定義します。
注: 内在サイズの決定アルゴリズムは、ユーザーエージェントが過去の呼び出し結果を任意の数キャッシュして再利用できるようにしています。
-
layoutFunction を box の layout()(計算値の <display-inside>)とする。
-
layoutFunction の 内在サイズ有効フラグ が intrinsic-sizes-valid なら、ユーザーエージェントは前回の内在サイズ値を再利用してもよい。その場合は以降の手順を中止してキャッシュ値を使う。
-
layoutFunction の 内在サイズ有効フラグ を intrinsic-sizes-valid に設定する。
-
name を layoutFunction の第1引数とする。
-
documentDefinition を 文書レイアウト定義取得(name指定)した結果とする。
もし 文書レイアウト定義取得 が失敗、または documentDefinition が
"invalid"
なら、 box を flow layout にフォールバックし、以降の手順を中止。 -
workletGlobalScope をレイアウト
LayoutWorkletGlobalScope
(WorkletGlobalScopesリストから取得)とする。ユーザーエージェントは、リストに少なくとも2つの
LayoutWorkletGlobalScope
を持ち、選択しなければならない(メモリ制約下を除く)。注: グローバルオブジェクトやクラス上の再生成不可能な状態に依存しないための仕様です。
また、ユーザーエージェントはこのタイミングでレイアウト
Worklet
に対して WorkletGlobalScope を作成してもよい。 -
内在サイズコールバック呼び出し(name, box, childBoxes, workletGlobalScope、必要に応じて並列で実行)を実行する。
注: 並列実行する場合、そのスレッドで使える layout worklet global scope を選ぶべき。
-
definition を レイアウト定義取得(name, workletGlobalScope)の結果とする。
失敗なら box を flow layout にフォールバックし、以降中止。
-
layoutInstance を レイアウトクラスインスタンス取得(box, definition, workletGlobalScope)の結果とする。
失敗なら box を flow layout にフォールバックし、以降中止。
-
inputProperties を definition の 入力プロパティとする。
-
children を新しいリストとする。
-
各 childBox について:
-
edges を
LayoutEdgeSizes
(boxの計算値をもとに生成)とする。 -
styleMap を
StylePropertyMapReadOnly
(boxのinputPropertiesにあるプロパティのみの計算値で生成)とする。 -
この段階で、childrenとstyleMapが前回呼び出しと同じなら、キャッシュされた内在サイズを利用して以降の手順を中止してもよい。
-
intrinsicSizesGeneratorFunction を definition の 内在サイズジェネレーター関数とする。
-
intrinsicSizesGenerator を Invoke(intrinsicSizesGeneratorFunction, layoutInstance, «children, edges, styleMap»)の結果とする。
例外発生時は box を flow layout にフォールバックし、以降中止。
-
intrinsicSizesValue を run a generator(intrinsicSizesGenerator,
"intrinsic-sizes"
)の結果とする。失敗時は box を flow layout にフォールバックし、以降中止。
-
intrinsicSizes を 変換(intrinsicSizesValueを
IntrinsicSizesResultOptions
へ)したものとする。 例外時は box を flow layout にフォールバックし、以降中止。 -
box の 内在サイズを次のように設定:
-
intrinsicSizes の
minContentSize
を min-content sizeとする。 -
intrinsicSizes の
maxContentSize
を max-content sizeとする。
-
5.6.2. フラグメント生成
フラグメント生成アルゴリズムは、ユーザーエージェントが著者定義レイアウト用の box の fragment を生成する方法を定義します。
注: フラグメント生成アルゴリズムも、ユーザーエージェントが過去の呼び出し結果を任意の数キャッシュして再利用できるようになっています。
-
layoutFunction を box の layout()(計算値の <display-inside>)とする。
-
layoutFunction の レイアウト有効フラグ が layout-valid なら、ユーザーエージェントは前回の値を再利用してもよい。その場合は以降の手順を中止してキャッシュ値を使う。
-
layoutFunction の レイアウト有効フラグ を layout-valid に設定する。
-
name を layoutFunction の第1引数とする。
-
documentDefinition を 文書レイアウト定義取得(name指定)した結果とする。
失敗、または documentDefinition が
"invalid"
なら、box を flow layout にフォールバックし、以降中止。 -
workletGlobalScope をレイアウト
LayoutWorkletGlobalScope
(WorkletGlobalScopesリストから取得)とする。ユーザーエージェントは、リストに少なくとも2つの
LayoutWorkletGlobalScope
を持ち、選択しなければならない(メモリ制約下を除く)。注: グローバルオブジェクトやクラス上の再生成不可能な状態に依存しないための仕様です。
また、ユーザーエージェントはこのタイミングでレイアウト
Worklet
に対して WorkletGlobalScope を作成してもよい。 -
レイアウトコールバック呼び出し(name, box, childBoxes, internalLayoutConstraints, internalBreakToken, workletGlobalScope、必要に応じて並列で実行)を実行する。
注: 並列実行する場合、そのスレッドで使える layout worklet global scope を選ぶべき。
-
definition を レイアウト定義取得(name, workletGlobalScope)の結果とする。
失敗なら box を flow layout にフォールバックし、以降中止。
-
layoutInstance を レイアウトクラスインスタンス取得(box, definition, workletGlobalScope)の結果とする。
失敗なら box を flow layout にフォールバックし、以降中止。
-
sizingMode を definition の layout options の
sizing
プロパティとする。 -
inputProperties を definition の 入力プロパティとする。
-
children を新しいリストとする。
-
各 childBox について:
-
edges を
LayoutEdgeSizes
(boxの計算値をもとに生成)とする。 -
layoutConstraints を レイアウト制約オブジェクト生成(internalLayoutConstraints, box, sizingMode)の結果とする。
-
styleMap を
StylePropertyMapReadOnly
(boxのinputPropertiesにあるプロパティのみの計算値で生成)とする。 -
breakToken を
BreakToken
(internalBreakTokenから適切な情報で生成)とする。もし internalBreakToken が null なら breakToken も null。
-
この段階で、children, styleMap, layoutConstraints, breakToken が前回呼び出しと同じなら、キャッシュされたフラグメントを利用して以降の手順を中止してもよい。
-
layoutGeneratorFunction を definition の レイアウトジェネレーター関数とする。
-
layoutGenerator を Invoke(layoutGeneratorFunction, layoutInstance, «children, edges, layoutConstraints, styleMap, breakToken»)の結果とする。
例外発生時は box を flow layout にフォールバックし、以降中止。
-
fragmentValue を run a generator(layoutGenerator,
"layout"
)の結果とする。失敗時は box を flow layout にフォールバックし、以降中止。
-
fragment を 変換(fragmentValueを
FragmentResultOptions
へ)したものとする。 例外時は box を flow layout にフォールバックし、以降中止。 -
各 childFragment (fragment の
childFragments
)について次を実行:-
childFragment の
[[generator]]
内部スロットが layoutGenerator と異なる場合、 box を flow layout にフォールバックし、以降中止。
-
-
sizingMode が
"block-like"
の場合:-
次の通り:
-
inlineSize を layoutConstraints の
fixedInlineSize
(必須)とする。 -
blockSize を box の border-box block size(書字モードに対し、fragment の
autoBlockSize
を「内在高さ」として)とする。
-
-
それ以外(sizingModeが
"manual"
):-
inlineSize を fragment の
inlineSize
とする。 -
blockSize を fragment の
blockSize
とする。
-
-
-
box の fragment を次の内容で返す:
-
inline size は inlineSize。
-
block size は blockSize。
-
子フラグメントは fragment の
childFragments
リストとする。その順序は描画順に影響する(§2 Layout API Containers参照)。border box基準の位置は、著者指定のinlineOffset
とblockOffset
に基づく。 -
断片化ブレーク情報は fragment の
breakToken
。 -
clonedData を StructuredSerializeForStorage(fragment の
data
)で生成。ユーザーエージェントは clonedData を fragmentとともに保存する必要がある。
-
5.6.3. ユーティリティアルゴリズム
このセクションでは、内在サイズの決定アルゴリズムとフラグメント生成アルゴリズムで共通して使われるアルゴリズムを示します。
-
layoutDefinitionMapをworkletGlobalScope のレイアウト定義mapとする。
-
layoutDefinitionMap[name]が存在しない場合、以下を実行:
-
Queue a taskで以下を実行:
-
Set documentLayoutDefinitionMap[name]に
"invalid"
を設定する。 -
ユーザーエージェントは、すべての
LayoutWorkletGlobalScope
でクラスが登録されていない旨のエラーをデバッグコンソールに推奨で出力するべきです。
-
失敗を返して、以降の手順を中止する。
-
-
get layoutDefinitionMap[name]の結果を返す。
-
layoutClassInstanceMapをboxのレイアウトクラスインスタンスmapとする。
-
layoutInstanceをget layoutClassInstanceMap[workletGlobalScope]の結果とする。layoutInstanceがnullの場合、以下を実行:
-
definitionのコンストラクター有効フラグがfalseの場合、失敗を返して以降の手順を中止する。
-
layoutCtorをdefinitionのクラスコンストラクターとする。
-
layoutInstanceをConstruct(layoutCtor)の結果とする。
constructが例外を投げた場合はdefinitionのコンストラクター有効フラグをfalseに設定し、失敗を返して以降の手順を中止する。
-
Set layoutClassInstanceMap[workletGlobalScope]にlayoutInstanceを設定する。
-
-
layoutInstanceを返す。
-
doneを
false
で初期化したbooleanとする。 -
nextValueをundefinedとする。
-
doneが
true
になるまで以下を繰り返す:-
nextFunctionをGet(generator,
"next"
)の結果とする。 -
IsCallable(nextFunction)がfalseならthrowでTypeErrorを投げて以降の手順を中止する。
-
nextResultをInvoke(nextFunction, generator, «nextValue»)の結果とする。
例外発生時は失敗を返して以降の手順を中止する。
-
requestOrRequestsをGet(nextResult,
"value"
)の結果とする。 -
doneをGet(nextResult,
"done"
)の結果とする。 -
GetMethod(requestOrRequests,
@@iterable
)が存在する場合: -
requestを変換(requestOrRequestsを
LayoutFragmentRequestOrIntrinsicSizesRequest
へ)したものとする。例外発生時は例外を投げて以降の手順を中止する。
-
resultをジェネレーター結果生成(request, generator, generatorType)したものとする。
ジェネレーター結果生成が失敗を返した場合、失敗を返して以降の手順を中止する。
-
nextValueをresultに設定する。
-
続行。
ユーザーエージェントは上記ループを順不同・並列で実行してもよい。ただしrequestsとresultsの順序は必ず一貫性が必要。
注: これはユーザーエージェントが適切なレイアウトアルゴリズムを別スレッドや非同期(例えば他の処理とタイムスライス)で実行可能にするためです。並列実行の場合、外側ループは全スレッドの完了を待ってからジェネレーターを再度呼び出す必要があります。著者に部分結果を返してはいけません。
-
-
Get(nextResult,
"value"
)の結果を返す。
-
requestが
IntrinsicSizesRequest
なら:-
layoutChildをrequestの内部スロット
[[layoutChild]]
から取得。 -
boxをlayoutChildの内部スロット
[[box]]
から取得。 -
boxがbox treeに接続されていないなら、失敗を返して中止する。
注: 著者は前の呼び出しから
LayoutChild
を保持する場合がありますが、boxは一度だけbox-treeに接続され、再利用されないことが想定されています。 -
internalIntrinsicSizesをユーザーエージェントがboxのborder boxのmin/max content contributionを計算した結果とする。
-
内在サイズオブジェクト生成(request, internalIntrinsicSizes)の結果を返す。
-
-
requestが
LayoutFragmentRequest
かつgeneratorTypeが"layout"
なら:-
layoutChildをrequestの内部スロット
[[layoutChild]]
から取得。 -
boxをlayoutChildの内部スロット
[[box]]
から取得。 -
boxがbox treeに接続されていないなら、失敗を返して中止する。
注: 著者は前の呼び出しから
LayoutChild
を保持する場合がありますが、boxは一度だけbox-treeに接続され、再利用されないことが想定されています。 -
childLayoutConstraintsをrequestの内部スロット
[[layoutConstraints]]
から取得。 -
childBreakTokenをrequestの内部スロット
[[breakToken]]
から取得。 -
internalFragmentをユーザーエージェントがbox、childLayoutConstraints、childBreakTokenから生成したfragmentとする。
-
レイアウトフラグメント生成(generator, request, internalFragment)の結果を返す。
-
-
(上記分岐に該当しない場合)失敗を返す。
6. セキュリティ考慮事項
これらの機能によって新たなセキュリティ問題は知られていません。
7. プライバシー考慮事項
これらの機能によって新たなプライバシー問題は知られていません。