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 の書字 モードを基準とする)を計算した結果とする。
-
box の ブロックサイズが auto の場合、fixedBlockSize を null とし、それ以外の場合は、 ブロックコンテナの場合とまったく同様に box の border-box ブロックサイズを計算した 結果とする。
-
次の設定を持つ新しい
LayoutConstraintsオブジェクトを返す:-
fixedInlineSizeおよびavailableInlineSizeを fixedInlineSize に設定する。 -
percentageInlineSizeを internalLayoutConstraints の インライン軸におけるパーセンテージ解決サイズ(box の 書字モードを基準とする)に設定する。 -
fixedBlockSizeを fixedBlockSize に設定する。 -
availableBlockSizeを、fixedBlockSize が null でない場合はそれに設定し、 それ以外の場合は 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
に記録された地点から生成することができます。
この例では、LayoutFragment
の以前の breakToken
を使用して、LayoutChild
の次のフラグメントを生成する方法も示す。
また、LayoutConstraints
の
blockFragmentationType
に従うために BreakToken
を使用する方法も示しており、以前の BreakToken
からレイアウトを再開する。
レイアウトの再開に使用される breakToken
を持つ FragmentResultOptions
を返す。
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 { // フラグメントにブレークトークンがない場合、 // 次の子に進む。 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として現れない。 -
inlineOffsetおよびblockOffsetは、 位置指定および変形が行われる前のフラグメントの位置を表す。 -
静的位置は、Layout API コンテナの絶対位置指定された子について、 インライン始端、ブロック始端のパディング辺、すなわちLayout API コンテナのものに 設定される。子の自動マージンはゼロとして扱われる。
-
"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(複数の場合もある)が
生成されると、ユーザーエージェントは、結果として得られた 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、必要に応じて並列で実行)を実行する。
注: ユーザーエージェントがintrinsic sizes コールバックを呼び出す処理を スレッド上で並列に 実行する場合、そのスレッドで使用できるレイアウトワークレットグローバルスコープを 選択するべきである。
-
definition を、name および workletGlobalScope を指定して レイアウト 定義を取得する処理の結果とする。
レイアウト定義を取得する処理が失敗を返した場合、 box を フロー レイアウトにフォールバックさせ、これらの手順をすべて中止する。
-
layoutInstance を、box、definition、 workletGlobalScope を指定して レイアウト クラスインスタンスを取得する処理の結果とする。
レイアウトクラスインスタンスを取得する処理が失敗を返した場合、 box を フロー レイアウトにフォールバックさせ、これらの手順をすべて中止する。
-
inputProperties を definition の 入力プロパティとする。
-
children を新しいリストとする。
-
childBoxes 内の各 childBox についてそれぞれ次のサブステップを 実行する:
-
layoutChild を、workletGlobalScope、name、および childBox を指定して レイアウト子を取得する 処理の結果とする。
-
layoutChild を children に付加する。
-
-
edges を、box のすべてのボックス モデルの辺についての算出値を設定した新しい
LayoutEdgeSizesとする。 -
styleMap を、box について inputProperties に列挙されたプロパティの算出値のみを設定した新しい
StylePropertyMapReadOnlyとする。 -
この段階で、children、styleMap が前回の呼び出しと同等である場合、 ユーザーエージェントは前回の呼び出しによる内在サイズを再利用してもよい。その場合、 内在サイズをキャッシュされた内在サイズとし、これらの手順をすべて中止する。
-
intrinsicSizesGeneratorFunction を definition の 内在サイズジェネレーター 関数とする。
-
intrinsicSizesGenerator を 呼び出す(intrinsicSizesGeneratorFunction, layoutInstance, «children, edges, styleMap») の結果とする。
-
intrinsicSizesValue を、intrinsicSizesGenerator および
"intrinsic-sizes"を指定して ジェネレーターを実行する処理の結果とする。ジェネレーターを 実行する処理が失敗を返した場合、box を フロー レイアウトにフォールバックさせ、これらの手順をすべて中止する。
-
intrinsicSizes を、intrinsicSizesValue を
IntrinsicSizesResultOptionsに変換する処理の結果とする。 例外がスローされた場合、box を フローレイアウトにフォールバックさせ、これらの手順をすべて中止する。 -
box の内在サイズを設定する:
-
intrinsicSizes の
minContentSizeを box の min-content サイズとする。 -
intrinsicSizes の
maxContentSizeを box の max-content サイズとする。
-
5.6.2. フラグメント生成
フラグメント生成アルゴリズムは、ユーザーエージェントが著者定義レイアウト用の box の fragment を生成する方法を定義します。
注: フラグメント生成アルゴリズムも、ユーザーエージェントが過去の呼び出し結果を任意の数キャッシュして再利用できるようになっています。
-
layoutFunction を、box に対する <display-inside> の 算出値の layout() とする。
-
layoutFunction の レイアウト 有効フラグが レイアウト有効である場合、ユーザー エージェントは前回の呼び出しによる内在サイズを使用してもよい。その場合、これらの手順をすべて 中止し、内在サイズに前回の値を使用してもよい。
-
layoutFunction の レイアウト 有効フラグを レイアウト有効に設定する。
-
name を layoutFunction の第 1 引数とする。
-
documentDefinition を、name を指定して 文書 レイアウト定義を取得する処理の結果とする。
文書レイアウト定義を取得する処理が failure を返した場合、または documentDefinition が
"invalid"である場合、 box を フロー レイアウトにフォールバックさせ、これらの手順をすべて 中止する。 -
workletGlobalScope を、レイアウト
Workletの ワークレットの WorkletGlobalScopes のリストから選択したLayoutWorkletGlobalScopeとする。ユーザーエージェントは、メモリ制約下にある場合を除き、ワークレットの WorkletGlobalScopes の リスト内に、 少なくとも 2 つの
LayoutWorkletGlobalScopeを 保持し、その中から選択しなければならない。注: これは、作者がグローバル オブジェクトに状態を保存できること、またはクラスに再生成不可能な状態を保存できることに依存しないようにするためである。
ユーザーエージェントは、この時点で、レイアウト
Workletを指定して、WorkletGlobalScope を作成することもできる。 -
name、 box、childBoxes、internalLayoutConstraints、 internalBreakToken、および workletGlobalScope を指定して、必要に応じて 並列に レイアウトコールバックを呼び出す処理を実行する。
注: ユーザーエージェントがintrinsic sizes コールバックを呼び出す処理を スレッド上で並列に 実行する場合、そのスレッドで使用できるレイアウトワークレットグローバルスコープを 選択するべきである。
-
definition を、name および workletGlobalScope を指定して レイアウト 定義を取得する処理の結果とする。
レイアウト定義を取得する処理が failure を返した場合、 box を フロー レイアウトにフォールバックさせ、これらの手順をすべて中止する。
-
layoutInstance を、box、definition、 workletGlobalScope を指定して レイアウト クラスインスタンスを取得する処理の結果とする。
レイアウトクラスインスタンスを取得する処理が failure を返した場合、 box を フロー レイアウトにフォールバックさせ、これらの手順をすべて中止する。
-
sizingMode を definition の レイアウトオプションの
sizingプロパティとする。 -
inputProperties を definition の 入力プロパティとする。
-
children を新しいリストとする。
-
childBoxes 内の各 childBox についてそれぞれ次のサブステップを実行する:
-
layoutChild を、workletGlobalScope、name、および childBox を指定して レイアウト子を取得する 処理の結果とする。
-
layoutChild を children に付加する。
-
-
edges を、box のすべての ボックス モデルの辺についての 算出値を設定した新しい
LayoutEdgeSizesとする。 -
layoutConstraints を、internalLayoutConstraints、box、および sizingMode を指定して レイアウト制約オブジェクトを作成する処理の結果とする。
-
styleMap を、box について inputProperties に列挙されたプロパティの算出値のみを設定した、新しい
StylePropertyMapReadOnlyとする。 -
breakToken を、internalBreakToken から適切な情報を設定した新しい
BreakTokenとする。internalBreakToken が null の場合、breakToken を null とする。
-
この段階で、children、styleMap、layoutConstraints、 breakToken が以前の呼び出しと同等である場合、ユーザーエージェントは以前の呼び出しによる フラグメントを 再利用してもよい。その場合、フラグメント出力をそのキャッシュされたフラグメントとし、これらの手順をすべて中止する。
-
layoutGeneratorFunction を definition の レイアウトジェネレーター関数とする。
-
layoutGenerator を 呼び出す(layoutGeneratorFunction, layoutInstance, «children, edges, layoutConstraints, styleMap, breakToken») の結果とする。
-
fragmentValue を、layoutGenerator および
"layout"を指定して ジェネレーターを実行する処理の結果とする。ジェネレーターを 実行する処理が failure を返した場合、box を フロー レイアウトにフォールバックさせ、これらの手順をすべて中止する。
-
fragment を、fragmentValue を
FragmentResultOptionsに変換する処理の結果とする。 例外がスローされた場合、box を フローレイアウトにフォールバックさせ、これらの手順をすべて中止する。 -
fragment の
childFragments内の各 childFragment についてそれぞれ次のサブステップを実行する:-
childFragment の
[[generator]]内部スロットが layoutGenerator と等しくない場合、 box を フローレイアウトにフォールバックさせ、これらの手順をすべて中止する。
-
-
sizingMode が
"block-like"の場合:-
次のようにする:
-
inlineSize を layoutConstraints の
fixedInlineSizeとする。("block-like"サイズ指定を使用する場合、この 値は設定されていなければならない)。 -
blockSize を、fragment の
autoBlockSizeを「内在 高さ」として、ブロックコンテナの場合とまったく同様に box の border-box ブロックサイズ(box の 書字モードを基準とする)を計算した結果とする。
-
-
それ以外の場合(sizingMode が
"manual"の場合):-
inlineSize を fragment の
inlineSizeとする。 -
blockSize を fragment の
blockSizeとする。
-
-
-
box に対して、次の内容を持つ フラグメントを返す:
-
インラインサイズを inlineSize に設定する。
-
ブロックサイズを blockSize に設定する。
-
子フラグメントを fragment の
childFragmentsリストに設定する。この順序はペイント順を決定するため重要である (§2 Layout API コンテナで説明する)。fragment の border box に対するそれらの位置は、作者が指定したinlineOffsetおよびblockOffsetに基づくべきである。 -
断片化ブレーク情報を fragment の
breakTokenに設定する。 -
clonedData を、fragment の
dataに対して StructuredSerializeForStorage を呼び出した結果とする。ユーザーエージェントは、clonedData を フラグメントとともに保存しなければならない。
-
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. プライバシー考慮事項
これらの機能によって新たなプライバシー問題は知られていません。