コンテナータイミング API

コミュニティグループ報告書草案,

このバージョン:
https://WICG.github.io/container-timing/
テストスイート:
https://github.com/web-platform-tests/wpt/tree/master/container-timing
課題追跡:
GitHub
編集者:
Jason Williams (Bloomberg)
(Igalia)

概要

この仕様は、DOM の注釈付けされたセクションが画面に表示され、その初回描画が完了した時点を監視できる API を定義します。

この文書のステータス

この仕様は、Web Platform Incubator Community Group によって公開されました。 これは W3C 標準ではなく、W3C 標準化過程上のものでもありません。 W3C Community Contributor License Agreement (CLA) の下では、限定的なオプトアウトおよびその他の条件が適用されることに注意してください。 W3C Community and Business Groups について詳しく学ぶ。

1. はじめに

Container Timing API により、DOM の注釈されたセクションが画面に表示され、 初回ペイントが完了した時点を監視できます。開発者は containerTiming 属性(Element Timing API の elementtiming と同様)を使用して DOM のサブセクションをマークし、そのセクションが 初めてペイントされたときにパフォーマンスエントリを受け取ることができます。

この API により、開発者はページ内のさまざまなコンポーネントのタイミングを測定できます。開発者が アプリケーションをコンポーネントとして構成することが増えるにつれ、アプリケーションまたは Web ページの サブセクションにおけるパフォーマンスを測定したいという需要が高まっています。

Element Timing とは異なり、DOM のセクションが描画を完了した時点をレンダラーが知ることはできません (将来の変更、新しい画像に対する非同期リクエスト、読み込みの遅いボタンなどが存在し得るため)。 そのため、この API は、更新があったときに PerformanceEntry オブジェクトの形式で候補を発行します。

2. 動機

開発者は、テーブル、ウィジェット、その他のコンポーネントなど、DOM のサブセクションがいつ描画されたかを 測定し、描画時間を追跡して分析に送信できるようにしたいと考えています。現在の Web API はこれを十分には サポートしていません:

Web 作者は自分のドメインを誰よりもよく理解しており、自分たちのコンテンツブロックのパフォーマンスを、 ユーザーや組織が理解できる方法で伝えたいと考えています(例: "time to first tweet")。

2.1. ライフサイクル

このライフサイクルの例では、コンポーネントは異なる時点で複数のコンテンツ片を描画し、それぞれの時点で 更新された情報を含む新しい PerformanceContainerTiming entry が生成されます。

ただし、ある領域が一度描画されると、その同じ領域の以後の描画によって新しいエントリが生成されることはありません。

ユーザーがページをスクロールまたは操作した後は、それ以降の描画は記録されず、新しいエントリも 生成されません。 Container Timing ライフサイクル図

3. 使用例

次の例は、コンテナールートを登録し、その描画タイミングを監視する方法を示します。

登録は containerTiming 属性を使用して要素ごとに行われます:
<div containertiming="foobar">
  <main>...</main>
  <aside>...</aside>
</div>

<script>
  const observer = new PerformanceObserver((list) => {
    let perfEntries = list.getEntries();
    for (const entry of perfEntries) {
      console.log('コンテナーが描画されました:', entry.identifier,
                  '時刻', entry.startTime,
                  'サイズ:', entry.size);
    }
  });
  observer.observe({ entryTypes: ["container"] });
</script>

この属性は、要素が文書に追加される前に設定する必要があります(HTML 内で設定するか、 JavaScript で設定する場合は、文書に追加する前に設定します)。この属性を遡及的に設定した場合は、 それ以降のイベントと将来の描画のみが取得されます。

3.1. サブツリーの無視

DOM ツリーの一部は、containerTimingIgnore 属性を使用して無視できます:
<div containertiming="foobar">
  <main>...</main>
  <!-- aside の更新はコンテナータイミングイベントを発生させません -->
  <aside containertimingignore>...</aside>
</div>

4. 用語

コンテナ・ルートとは、 HTMLElement であり、containerTiming 属性を持つものです。

無視されるサブツリーとは、 HTMLElement をルートとし、その要素が containerTimingIgnore 属性を持つサブツリーです。

ペイント済み領域とは、 コンテナ・ルートでペイントされたすべての部分を、その要素が最初に 観測されてから累積して表す領域(矩形の集合)であり、CSS ピクセルで表現されます。ペイント済み領域はビューポート座標空間で維持されます。 コンテナ・ルート要素が移動した場合 (たとえば、レイアウトの変更や moveBefore() によって)、 以前に累積された矩形は調整されません — 領域はビューポート座標内で拡大し続けます。

Container Timing APIは、コンテナ・ルートが画面にペイントされるタイミングに関する情報を提供します。

5. PerformanceContainerTiming インターフェイス

[Exposed=Window]
interface PerformanceContainerTiming : PerformanceEntry {
    readonly attribute DOMString identifier;
    readonly attribute DOMRectReadOnly intersectionRect;
    readonly attribute unsigned long long size;
    readonly attribute DOMHighResTimeStamp firstRenderTime;
    readonly attribute HTMLElement? lastPaintedElement;
    readonly attribute HTMLElement? rootElement;

    [Default] object toJSON();
};

PerformanceContainerTiming includes PaintTimingMixin;

注記: intersectionRectの 座標およびsizeは CSS ピクセル(sizeの場合は平方 CSS ピクセル)で表現され、 ペイント済み 領域の座標空間および交差矩形アルゴリズムによって生成される単位と一致する。

各 PerformanceContainerTiming オブジェクトには、次の関連付けられた概念がある。

entryType 属性のゲッターは、DOMString "container" を返さなければならない。

name 属性のゲッターは、空文字列を返さなければならない。

duration 属性は 0 を返さなければならない。

startTime 属性のゲッターは、this の renderTime の値を返さなければならない。

identifier 属性は、 this の identifier の値を返さなければならない。

intersectionRect 属性は、this の intersectionRect の値を返さなければならない。

size 属性は、 this の size の値を返さなければならない。

firstRenderTime 属性は、this の firstRenderTime の値を返さなければならない。

lastPaintedElement 属性は、this の lastPaintedElement の値を返さなければならない。

rootElement 属性は、 this の rootElement の値を返さなければならない。

toJSON() が呼び出されたとき、デフォルトの toJSON 手順を実行します。

注記: ユーザーエージェントは、削除されたコンテンツによって メモリリークが発生しないように、コンテナルートレコードマップを維持する必要がある。特に、エントリーの存続期間を HTMLElement への弱いポインターに 結び付けることで、HTMLElement が 削除された後のいずれかの時点で、それらをクリーンアップできる。このマップはウェブ開発者に公開されないため、 ガベージコレクションのタイミングが公開されることはない。

6. 処理モデル

注: Container Timing API を実装するユーザーエージェントは、 "container" を supportedEntryTypes に含める必要があります。これは Window コンテキスト向けです。 これにより、開発者は container timing のサポートを検出できます。

6.1. 文書単位の状態

各 Document について、ユーザーエージェントは、コンテナ・ルート HTMLElement を Container Timing レコード オブジェクトにマップする コンテナ・ルート・レコード・マップを維持しなければなりません。

各 Window は、初期値が false に設定されたブール値である スクロールイベントをディスパッチ済みを 持ちます。

各 Window は、初期値が false に設定されたブール値である 入力イベントをディスパッチ済みを 持ちます。

6.2. DOM 仕様の変更

この節は、[DOM] 仕様が変更されると削除されます。

イベント・ディスパッチ・アルゴリズムを次のように変更します。

ステップ 1 の直後に、次のステップを追加します。

  1. target の 関連するグローバルオブジェクトが Window オブジェクトであり、event の isTrusted が true の場合:

    1. event の type が scroll の場合、target の 関連するグローバルオブジェクトの スクロールイベントをディスパッチ済みを true に設定します。

    2. event の type が keydown、mousedown、pointerdown、または click のいずれかの場合、target の 関連するグローバルオブジェクトの 入力イベントをディスパッチ済みを true に設定します。

6.3. HTMLElement インターフェイスの拡張

この節は、[DOM] 仕様が変更されると削除されます。

HTMLElement インターフェイスを次のように拡張します。

partial interface HTMLElement {

    [CEReactions, Reflect] attribute DOMString containerTiming;

    [CEReactions, Reflect] attribute boolean containerTimingIgnore;

};

containerTiming 属性は、要素を コンテナ・ルートとして識別する DOMString です。この値は、対応する PerformanceContainerTiming エントリ内の identifier になります。

containerTimingIgnore 属性は、 存在する場合、その要素とその子孫を、祖先の コンテナ・ルートのコンテナ・タイミング 測定に寄与すべきでない 無視されるサブツリーとしてマークします。

6.4. Container Timing レコード

この仕様では、処理モデルによって使用される内部データ構造を定義します。

Container Timing レコードには、次の関連する 概念があります。
ペイント・タイミング情報 paintTimingInfo と DOMString identifier を与えて Container Timing レコードを作成するには、次のステップを実行します。
  1. record を新しい Container Timing レコードとします。

  2. record の paintTimingInfo を paintTimingInfo に設定します。

  3. record の identifier を identifier に設定します。

  4. record を返します。

6.5. コンテナ・ルートの登録

HTMLElement が containerTiming コンテンツ属性を持った状態で文書に接続された場合:

  1. ユーザーエージェントは、その要素を コンテナ・ルートとして登録しなければなりません。

  2. ユーザーエージェントは、コンテナ・ルート用に空の 描画済み領域を初期化しなければなりません。

  3. ユーザーエージェントは、コンテナ・ルートのサブツリー内のすべての 描画操作を追跡しなければなりません。ただし、無視されるサブツリーは除きます。

6.6. containertiming 属性の削除

containerTiming コンテンツ属性が HTMLElement element から削除された場合、次のステップを実行します。
  1. document を element の ノード文書とします。

  2. document の コンテナ・ルート・レコード・マップに element のエントリが含まれている場合、そのエントリを削除します。

注: containerTiming 属性が後で同じ要素に再び追加された場合、次の描画時に新しい Container Timing レコードが 作成されます。描画済み領域は新しい状態から始まり、以前の描画データは保持されません。

6.7. コンテナ・ルートの切断

コンテナ・ルート HTMLElement element が文書から切断された場合、次のステップを実行します。
  1. document を element の ノード文書とします。

  2. document の コンテナ・ルート・レコード・マップに element のエントリが含まれている場合、そのエントリを削除します。

注: 要素が containerTiming 属性を引き続き持ったまま文書に再接続された場合、それは新しい コンテナ・ルート登録として扱われます。次の描画時に、新しい Container Timing レコードが、 新しい 描画済み領域とともに作成されます。

6.8. Container Timing 用の要素描画の処理

要素が描画され、ユーザーエージェントがコンテナ・タイミングの更新を処理する必要がある場合、Document document、ペイント・タイミング情報 paintTimingInfo、HTMLElement element、および DOMRectReadOnly intersectionRect を与えて、次のステップを実行します。
  1. window を document の 関連するグローバルオブジェクトとします。

  2. window の スクロールイベントをディスパッチ済みまたは 入力イベントをディスパッチ済みのいずれかが true の場合、 戻ります。

  3. element が コンテナ・タイミングに寄与するものでない場合、戻ります。

  4. containerRoot を、element を与えて コンテナ・ ルート要素を取得する結果とします。

  5. containerRoot が null の場合、戻ります。

  6. record を、document の コンテナ・ルート・ レコード・マップ内の containerRoot のエントリとします。そのようなエントリが存在しない場合、record を、paintTimingInfo と containerRoot の containerTiming コンテンツ属性の値を与えて Container Timing レコードを作成する結果に 設定し、次に (containerRoot → record) を document の コンテナ・ルート・レコード・マップに追加します。

  7. enclosingRect を intersectionRect を囲む最小の矩形とします。

  8. 最後の新しい描画領域を必要に応じて更新 する処理を、record に対して document、containerRoot、 element、enclosingRect、および paintTimingInfo を与えて行います。

  9. Document を、保留中のコンテナ・タイミング変更があるものとしてマークします。

注: このアルゴリズムは、描画される各画像またはテキスト ノードについて呼び出されます。交差矩形は、要素をターゲット、ビューポートをルートとして 交差矩形アルゴリズムを使用して計算し、 ビジュアル・ビューポートと交差させる必要があります。テキストノードの場合、交差 矩形は、所有されるテキストノードの集合内のすべてのテキストノードのボーダーボックスを含む最小の矩形であり、ビジュアル・ビューポートと交差させたものです。

注: Container Timing は、コンテンツの 初期レンダリングを測定することを目的としています。ユーザーが文書をスクロールまたは操作した後は、描画は 記録されなくなります。これは、それらがページの読み込みではなく、そのスクロールまたは操作の結果(たとえば、 ビュー内にスクロールされたコンテンツやクリックによって表示されたコンテンツ)である可能性が高いためです。現在の フレームですでに保留中のエントリは、引き続き発行されます。

6.9. Container Timing エントリの発行

Document document の Container Timing エントリを発行するよう要求された場合、次のステップを実行します。これは、すべての描画 操作が処理された後、フレームごとに一度呼び出される必要があります。
  1. document に保留中のコンテナ・タイミング変更がない場合、戻ります。

  2. document の コンテナ・ルート・レコード・マップの 各 containerRoot → record について それぞれ:

    1. record の hasPendingChanges が false の場合、続行します。

    2. record と containerRoot を与えて Container Timing エントリを作成する 処理を行います。

    3. record の hasPendingChanges を false に設定します。

    4. record の lastNewPaintedAreaElement を null に設定します。

    5. record の lastNewPaintedAreaSize を 0 に設定します。

  3. Document を、保留中のコンテナ・タイミング変更がなくなったものとしてマークします。

注: 描画された要素ごとに複数の エントリを発行する場合がある他の一部のペイント・タイミング API とは異なり、Container Timing は各 コンテナ・ルートについて描画済み領域を蓄積し、 フレームごと、コンテナ・ルートごとに最大 1 つの PerformanceContainerTiming エントリを発行します。このバッチ処理方式はより効率的であり、 コンテナの描画状態を包括的に把握できます。

6.10. 親コンテナ・ルート Element の取得

HTMLElement element を与えて 親 コンテナ・ルート要素を取得するには、次のステップを実行します。
  1. parent を element の parentElement とします。

  2. parent が null の場合、null を返します。

  3. parent を与えて コンテナ・ルート要素を取得する結果を返します。

6.11. Container Timing への寄与

HTMLElement は、次のすべてが true の場合、コンテナ・ルートの コンテナ・タイミングに 寄与します。

HTMLElement element が コンテナ・ルート containerRoot のコンテナ・タイミングに寄与するかどうかを判定するには:
  1. element が null の場合、false を返します。

  2. element がシャドーツリー内にある場合、false を返します。

  3. element が containerRoot の子孫でない場合、false を返します。

  4. element が 無視されるサブツリー内にある場合、false を返します。

  5. true を返します。

6.12. コンテナ・ルート Element の取得

HTMLElement element を与えて コンテナ・ ルート要素を取得するには、次のステップを実行します。
  1. element が null の場合、null を返します。

  2. element の containerTiming コンテンツ属性が存在する場合、element を返します。

  3. element の parentElement が null でない場合、element の parentElement を与えて コンテナ・ ルート要素を取得する結果を返します。

  4. null を返します。

6.13. 最後の新しい描画領域を必要に応じて更新

Container Timing レコード record について、Document document、コンテナ・ルート HTMLElement containerRoot、HTMLElement element、DOMRectReadOnly enclosingRect、および ペイント・タイミング情報 paintTimingInfo を与えて 最後の新しい描画領域を必要に応じて更新するには、 次のステップを実行します。
  1. paintedRegion を record の paintedRegion とします。

  2. paintedRegion が enclosingRect を完全に含む場合、戻ります。

  3. newPaintedArea を、paintedRegion にまだ 含まれていない enclosingRect の面積とします。

  4. record の paintedRegion を、 paintedRegion と enclosingRect の和集合に設定します。

  5. record の lastNewPaintedAreaPaintTimingInfo を paintTimingInfo に設定します。

  6. newPaintedArea が record の lastNewPaintedAreaSize より大きい場合:

    1. record の lastNewPaintedAreaElement を element に設定します。

    2. record の lastNewPaintedAreaSize を newPaintedArea に設定します。

  7. record の hasPendingChanges を true に設定します。

  8. containerRoot の containerTimingIgnore コンテンツ属性が存在する場合、戻ります。

  9. parentContainerRoot を、containerRoot を与えて 親コンテナ・ルート要素を取得する 結果とします。

  10. parentContainerRoot が null の場合、戻ります。

  11. parentRecord を、document の コンテナ・ルート・ レコード・マップ内の parentContainerRoot のエントリとします。そのようなエントリが存在しない場合、 parentRecord を、paintTimingInfo と parentContainerRoot の containerTiming コンテンツ属性の値を与えて Container Timing レコードを作成する結果に設定し、 次に (parentContainerRoot → parentRecord) を document の コンテナ・ルート・レコード・マップに追加します。

  12. 最後の新しい描画 領域を必要に応じて更新する処理を、parentRecord に対して document、 parentContainerRoot、element、enclosingRect、および paintTimingInfo を与えて行います。

注: lastPaintedElement は、単一のレンダリング・フレーム内で新たに描画された面積が最大となるよう寄与した要素です。これにより、 実装固有の描画順序への依存を回避します。lastPaintedElement は、大規模または複雑な コンテナ・ルート(たとえば テーブル)の更新を何が引き起こしているかを調査する開発者向けのデバッグ支援として意図されており、それ自体が独立したパフォーマンス指標ではありません。lastNewPaintedAreaSize は、 エントリが発行された後にリセットされるため、各フレームの比較は新しい状態から始まります。

注: このアルゴリズムは、サイズに関係なく描画済み 領域へのあらゆる変更を報告します。描画領域に 1 ピクセルだけの変更があった場合でも、新しい PerformanceContainerTiming エントリがキューに入れられます。小さな変更を除外したい開発者は、エントリ間の size 値を比較することで除外できます。

6.14. Container Timing エントリの作成

Container Timing レコード record と コンテナ・ルート HTMLElement containerRoot を与えて Container Timing エントリを作成するために、 ユーザーエージェントは次のステップを実行しなければなりません。
  1. entry を、次の値を持つ新しい PerformanceContainerTiming エントリとします。

  2. entry PerformanceEntry をキューに入れます。

7. セキュリティとプライバシーに関する考慮事項

7.1. クロスオリジン制限

この API はクロスオリジン境界を尊重します:

7.2. 情報の露出

この API によって提供される情報の大部分は、既存の API によってすでに推定できます:

この API は次のものを公開しません:

7.3. タイミング攻撃

この API は DOMHighResTimeStamp を使用します。これは他の Performance API と同様に、セキュリティ目的で分解能の制限を受ける場合があります。

7.4. プライバシーに関する考慮事項

この API は次のことを行いません:

8. 謝辞

貴重なフィードバックと助言をくださった以下の方々に深く感謝します:

適合性

文書の 規約

適合要件は、 記述的な表明 および RFC 2119 用語の組み合わせによって表現されます。 規範的な部分におけるキーワード “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” は、RFC 2119 で説明されているとおりに解釈されます。 ただし、読みやすさのため、 この仕様では、これらの語がすべて大文字で現れるとは限りません。

この仕様のテキストはすべて規範的です。 ただし、明示的に非規範的と示されたセクション、例、および注記は除きます。 [RFC2119]

この仕様の例は、“for example” という語で導入されるか、 規範的なテキストから分離され、 class="example" によって次のように示されます:

これは参考例の例です。

参考注記は “Note” という語で始まり、 規範的なテキストから分離され、 class="note" によって次のように示されます:

注: これは参考注記です。

索引

この仕様で定義される 用語

参照により定義される 用語

参照文献

規範的参照文献

[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[GEOMETRY-1]
Sebastian Zartner; Yehonatan Daniv. Geometry Interfaces Module Level 1. URL: https://drafts.csswg.org/geometry/
[HR-TIME-3]
Yoav Weiss. High Resolution Time. URL: https://w3c.github.io/hr-time/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[PAINT-TIMING]
Ian Clelland; Noam Rosenthal. Paint Timing. URL: https://w3c.github.io/paint-timing/
[PERFORMANCE-TIMELINE]
Nicolas Pena Moreno. Performance Timeline. URL: https://w3c.github.io/performance-timeline/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. 1997年3月. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

参考参照文献

[INTERSECTION-OBSERVER]
Stefan Zager; Emilio Cobos Álvarez; Traian Captan. Intersection Observer. URL: https://w3c.github.io/IntersectionObserver/

IDL 索引

[Exposed=Window]
interface PerformanceContainerTiming : PerformanceEntry {
    readonly attribute DOMString identifier;
    readonly attribute DOMRectReadOnly intersectionRect;
    readonly attribute unsigned long long size;
    readonly attribute DOMHighResTimeStamp firstRenderTime;
    readonly attribute HTMLElement? lastPaintedElement;
    readonly attribute HTMLElement? rootElement;

    [Default] object toJSON();
};

PerformanceContainerTiming includes PaintTimingMixin;

partial interface HTMLElement {
    [CEReactions, Reflect] attribute DOMString containerTiming;
    [CEReactions, Reflect] attribute boolean containerTimingIgnore;
};