Copyright © 2024 World Wide Web Consortium. W3C® liability, trademark and permissive document license rules apply.
本仕様は、タイムオリジンと現在時刻をサブミリ秒精度で提供するAPIを定義します。これにより、システムクロックのズレや調整の影響を受けません。
このセクションは、公開時点での文書のステータスについて説明します。現行のW3C 公開文書および本技術レポートの最新改訂は、W3C技術レポート一覧(https://www.w3.org/TR/)で確認できます。
本文書は、Webパフォーマンス作業グループが 勧告プロセスに従い、作業草案として公開したものです。
作業草案として公開されたことは、W3Cおよびそのメンバーによる承認を意味するものではありません。
本文書はドラフトであり、随時更新、差替え、廃止される可能性があります。進行中の作業以外のものとして引用することは不適切です。
本文書は、 W3C特許ポリシー の下で運営されるグループによって作成されました。 W3Cは、 グループ成果物に関連する特許開示の公開リスト を管理しています。このページでは特許開示の方法も案内しています。個人が、必須クレームを含むと考える特許を実際に知っている場合、 W3C特許ポリシー第6節に従って情報を開示する必要があります。
本文書は 2023年11月03日 W3Cプロセス文書 に従って作成されています。
このセクションは規範的ではありません。
ECMAScript言語仕様[ECMA-262]では、
Date
オブジェクトは1970年1月1日UTCからのミリ秒単位の時刻値を表すと定義されています。ほとんどの場合、この時刻の定義は十分であり、約285,616年間の任意の瞬間についてミリ秒精度で時刻を表すことができます。
実際には、これらの時刻の定義はクロックのずれやシステムクロックの調整の影響を受けます。時刻値は必ずしも単調増加するとは限らず、後続の値が減少したり、同じ値になることもあります。
例えば、以下のスクリプトでは、計算されたdurationが正数、負数、またはゼロとなる場合があります。
var mark_start = Date.now();
doTask(); // タスクを実行
var duration = Date.now() - mark_start;
特定のタスクにおいて、この時刻の定義では十分でない場合があります。
本仕様はDate.now()
[ECMA-262]の挙動を変更することを提案しません。これは現在のカレンダー時刻の値を取得するのに有用であり、長い利用実績があります。
DOMHighResTimeStamp型、
Performance.now()メソッド、
Performance.timeOrigin属性は、
Performanceインターフェイスの一部であり、上記の課題を解決します。これらはサブミリ秒分解能で単調増加する時刻値を提供します。
サブミリ秒分解能の提供は本仕様の必須要件ではありません。実装は、プライバシーやセキュリティの理由でタイマー分解能を制限し、サブミリ秒タイマーを公開しない選択をすることも可能です。サブミリ秒分解能に依存するユースケースは、その場合満たされない可能性があります。
このセクションは規範的ではありません。
本仕様はいくつかの機能を定義しています。安定した単調クロックに基づくタイムスタンプを、文脈をまたいで比較可能にし、サブミリ秒分解能も可能です。
パフォーマンス測定において安定した単調クロックが必要となる理由は、無関係なクロックのずれが測定値を歪めてしまい、役に立たなくなるためです。例えば、Documentへのナビゲーションやリソース取得、スクリプトの実行時間を正確に測定する際には、サブミリ秒分解能かつ単調増加するクロックが望ましいです。
文脈間でタイムスタンプを比較することは、例えばWorkerとメインスレッド間で作業を同期したり、イベントタイムラインの統一ビューを作成する際に不可欠です。
最後に、サブミリ秒タイマーの必要性は以下のユースケースに関連します。
このセクションは規範的ではありません。
開発者は、WorkerやSharedWorkerなど、
異なるタイムオリジンを持つイベントも含め、アプリケーション全体のタイムラインを構築したい場合があります。そのようなイベントを同じタイムライン上に表示するには、
DOMHighResTimeStampを
Performance.timeOrigin属性を使って変換できます。
// ---- worker.js -----------------------------
// 共有ワーカースクリプト
onconnect = function(e) {
var port = e.ports[0];
port.onmessage = function(e) {
// ワーカーでの実行時間計測
var task_start = performance.now();
result = runSomeWorkerTask();
var task_end = performance.now();
}
// 他のコンテキストに結果とエポック基準のタイムスタンプを送信
port.postMessage({
'task': 'ワーカーのタスク',
'start_time': task_start + performance.timeOrigin,
'end_time': task_end + performance.timeOrigin,
'result': result
});
}
// ---- application.js ------------------------
// ドキュメント内のタスク計測
var task_start = performance.now();
runSomeApplicationTask();
var task_end = performance.now();
// 実行時パフォーマンスデータをアップロードする開発者提供メソッド
reportEventToAnalytics({
'task': 'ドキュメントのタスク',
'start_time': task_start,
'duration': task_end - task_start
});
// ワーカーのタイムスタンプをドキュメントのタイムオリジンに変換
var worker = new SharedWorker('worker.js');
worker.port.onmessage = function (event) {
var msg = event.data;
// エポック基準のタイムスタンプをドキュメントのタイムオリジンに変換
msg.start_time = msg.start_time - performance.timeOrigin;
msg.end_time = msg.end_time - performance.timeOrigin;
reportEventToAnalytics(msg);
}
クロックは、時間の経過を追跡し、アルゴリズムステップが実行されている 安全でない現在時刻を報告できます。 クロックには様々な種類があります。Webプラットフォーム上のすべてのクロックは、1ミリ秒のクロック時間が実世界の1ミリ秒と一致するように設計されていますが、正確に一致できない場合の扱い方が異なります。
単調クロックの安全でない現在時刻は、決して減少せず、システムクロックの調整によって変化しません。 単調クロックは ユーザーエージェントの1回の実行内にのみ存在し、 異なる実行間で発生するイベントを比較するためには使用できません。
単調クロックはユーザーの時間感覚に合わせて調整できないため、計測用として使用し、ユーザー向けの時刻には壁時計を使用してください。
ユーザーエージェントは、ブラウザ再起動時、分離されたブラウジングセッション(例:シークレットモード等)開始時、または既存の設定オブジェクトと通信できない
環境設定オブジェクト作成時などに
Unixエポックの推定単調時刻を選択できます。
そのため開発者は、共有タイムスタンプを絶対時刻として、すべての過去・現在・未来の文脈間で単調性が維持されるとは考えないでください。実際には、単調性はメッセージ交換が可能な文脈間でのみ適用されます。
例:postMessage(message, options),
BroadcastChannelなど。
ある状況(例:タブがバックグラウンドになった場合)では、ユーザーエージェントがその文脈でタイマーや定期コールバックの実行を抑制、あるいは完全に停止することがあります。 このような抑制は単調クロックが返す時刻の分解能や精度には影響しないようにすべきです。
各クロックの安全でない現在時刻は、 安全でないモーメントを返します。 時刻の粗化はこれらの安全でないモーメントを 粗化されたモーメントまたは単に モーメントに変換します。 安全でないモーメントと モーメントは異なるクロック間では比較できません。
モーメントおよび安全でないモーメントは時点を表し、直接数値として保存できません。 実装では通常、モーメントを他の固定時点からの 持続時間 として表現しますが、仕様書ではモーメント自体を扱うべきです。
持続時間は、同じクロック上の モーメント同士の距離です。どちらの端点も 安全でないモーメントであってはならず、 これにより持続時間やその差分は 9.1 クロックの分解能での懸念を軽減します。 持続時間はミリ秒、秒などで測定されます。 すべてのクロックが同じ速度でカウントしようとするため、 持続時間には関連するクロックがありません。 1つのクロック上の2つのモーメントから算出した 持続時間は、 2番目のクロック上のモーメントに加えることで、 さらに新しいモーメントを生成できます。
持続時間(duration from)aからbまで は次のアルゴリズムによって求めます:
持続時間は、暗黙的に
DOMHighResTimeStampとして利用できます。
持続時間をタイムスタンプに暗黙的に変換するには、
持続時間dを与え、dに含まれるミリ秒数を返します。
1ページ内(1つの環境設定オブジェクトの範囲)で時間を測定する場合は、settingsObjectの現在の相対タイムスタンプを使用します。
これはsettingsObjectのタイムオリジンからsettingsObjectの現在の単調時刻までの持続時間として定義されます。
この値は持続時間をタイムスタンプへの暗黙的変換によって
JavaScriptのDOMHighResTimeStampとして直接公開できます。
UAの1回の実行内で時間を測定する場合、環境設定オブジェクトのタイムオリジンが比較の基準として適切でない場合は、 モーメントを環境設定オブジェクトの 現在の単調時刻を使って作成します。 環境設定オブジェクト settingsObjectの現在の単調時刻 は次の手順で求めます:
モーメントは、モノトニッククロックから直接JavaScriptやHTTPで表現することはできません。代わりに、2つのこのようなモーメント間の持続時間を公開します。
複数回のUA実行にまたがる時間測定には、現在の粗化された壁時計時刻、または(クロスオリジン分離機能のある文脈でより高精度が必要な場合は) 環境設定オブジェクトの現在の壁時計時刻を使ってモーメントを作成します。 現在の粗化された壁時計時刻は、時刻の粗化を壁時計の安全でない現在時刻に対して呼び出した結果です。
環境設定オブジェクト settingsObjectの現在の壁時計時刻 は次の手順で求めます:
壁時計からのモーメントを使用する際は、ユーザーが時計を前後に調整する場合があることを設計に反映してください。
壁時計からのモーメントは、Unixエポックからそのモーメントまでのミリ秒数をJavaScriptの
コンストラクターに渡すか、
Unixエポックからそのモーメントまでのナノ秒数を
Temporal.Instant
コンストラクターに渡すことで表現できます。
Date
類似の表現をコンピューター間で送信することは避けてください。そうするとユーザーの時計のズレが漏洩し、 トラッキングベクトルとなります。 代わりに、単調クロックのモーメントのように、2つのモーメント間の持続時間を送信する方法を使ってください。
DOMイベントが発生した時刻は以下のように報告できます:
timeStamp属性を、
thisの関連設定オブジェクトの現在の相対タイムスタンプで初期化する。
エラーレポートの経過時間は以下で計算できます:
後で:
複数日間のアトリビューションレポートの有効期限は以下のように扱えます:
value["expiry"]を解釈したもの
数日後:
Unixエポックは、 壁時計上で1970年1月1日 00:00:00 UTCに対応する モーメントです。
何らかの方法で通信可能な環境設定オブジェクト群ごとに、 Unixエポックの推定単調時刻があり、 これは単調クロック上の モーメントであり、以下の手順で初期化されます:
monotonic time - (wall time - Unixエポック)
とする。
Workerも含みます。
パフォーマンス測定では、関連する環境設定オブジェクトの初期化の早い段階での モーメントからの 持続時間を報告します。 そのモーメントは、その設定オブジェクトの タイムオリジンに格納されます。
タイムオリジンタイムスタンプを取得するには、 グローバルオブジェクト globalを与え、次の手順を実行します。この手順は 持続時間を返します。
timeOriginを globalの 関連設定オブジェクトの タイムオリジンとする。
Window文脈では、
この値は
ナビゲーション開始時刻を表します。
Workerや
ServiceWorkerでは、
ワーカーが実行された時刻を表します。
[service-workers]
タイムオリジンタイムスタンプを取得するで返される値は、
globalのタイムオリジンが発生した
Unixエポックからの時間にほぼ一致します。
ただしこれは、Date.now()をタイムオリジンで実行したときの値とは異なる場合があります。
前者はシステムやユーザーによるクロック調整、クロックのずれなどの影響を受けない
単調クロックに基づいて記録されるためです。
現在の高分解能時刻は、 グローバルオブジェクト current globalを与え、 安全でない共有現在時刻と current globalで 相対高分解能時刻アルゴリズムの結果を返します。
粗化された共有現在時刻は、 オプションの真偽値crossOriginIsolatedCapability(デフォルトfalse)を与え、 時刻の粗化アルゴリズムを 安全でない共有現在時刻と crossOriginIsolatedCapabilityで呼び出し、その結果を返します。
DOMHighResTimeStamp型は、
ミリ秒単位の持続時間を格納するために使用されます。文脈によっては、この型は
持続時間後のモーメントを表す場合があります。
例えばタイムオリジンや
Unixエポックなどの基準となるモーメントからの持続時間です。
WebIDLtypedef double DOMHighResTimeStamp;
DOMHighResTimeStampは、
計測が可能な精度でミリ秒単位の時刻を表すべきSHOULDであり、タイミング攻撃を防ぐ必要があります。
詳細は9.1 クロックの分解能を参照してください。
DOMHighResTimeStampは
double型であり、
エポック基準の時刻(Unixエポックから
モーメントまでのミリ秒数)を有限の分解能で表すことができます。
2023年のモーメントでは、その分解能は約0.2マイクロ秒です。
WebIDLtypedef unsigned long long EpochTimeStamp;
EpochTimeStampは、
Unixエポックから
モーメントまでのミリ秒数(整数値)を
壁時計上で表します(うるう秒は除外)。
これを利用する仕様書は、ミリ秒数の解釈方法を定義する必要があります。
WebIDL[Exposed=(Window,Worker)]
interface Performance : EventTarget {
DOMHighResTimeStamp now();
readonly attribute DOMHighResTimeStamp timeOrigin;
[Default] object toJSON();
};
now()メソッドはMUST
現在の高分解能時刻により
thisの関連グローバルオブジェクト(持続時間)を与え、ミリ秒数を返さなければならない。
now()
メソッドをPerformanceオブジェクトに対し、
同じタイムオリジンで呼び出した場合は、
同じ単調クロックを利用しなければならないMUST。
now()メソッドから
取得した時刻値のうち、時系列的に記録された2つの値の差分は、同じ
タイムオリジンであれば
決して負になってはならないMUST。
timeOrigin属性はMUST
持続時間として
タイムオリジンタイムスタンプを取得するで
関連グローバルオブジェクトに対して返されるミリ秒数を返さなければならない。
Performance.timeOriginで返される時刻値は
単調クロック(タイムオリジンで共有される)を利用しなければならないMUST。
参照点は[ECMA-262]
time
定義―詳細は9.
セキュリティに関する考慮事項を参照。
toJSON()が呼ばれた場合は、
[WEBIDL]の
default toJSON stepsを実行する。
インターフェースミックスインWindowOrWorkerGlobalScope上の
performance属性は、
グローバルオブジェクトから
パフォーマンス関連の属性やメソッドへのアクセスを可能にします。
WebIDLpartial interface mixin WindowOrWorkerGlobalScope {
[Replaceable] readonly attribute Performance performance;
};
測定やスケジューリング目的で正確なタイミング情報にアクセスすることは、多くのアプリケーションにとって一般的な要求事項です。 例えば、アニメーションや音声などページ上のアクティビティを調整するには、高分解能時刻へのアクセスが良好なユーザー体験を提供するために必要です。 また、測定により重要なコードコンポーネントのパフォーマンスを追跡したり、リグレッションを検出したりできます。
しかし、同じ正確なタイミング情報へのアクセスは、攻撃者によって見えないデータやアクセスできないデータを推測・推論するための悪意ある用途にも使われる可能性があります。 例えばキャッシュ攻撃、統計的フィンガープリンティングやマイクロアーキテクチャ攻撃は、プライバシー・セキュリティ上の懸念点であり、 悪意あるウェブサイトが様々なブラウザやアプリケーションの高分解能タイミングデータを利用してユーザーの区別、特定、または関連しない同一プロセスのユーザーデータを露呈する場合があります。 詳細は [CACHE-ATTACKS]、 [SPECTRE] も参照してください。
本仕様は、従来のEpochTimeStampで公開されていたミリ秒分解能よりも高精度な
サブミリ秒分解能を提供するAPIを定義しています。
ただし、この新しいAPIがなくても攻撃者は繰り返し実行や統計分析によって高分解能の推定値を取得できる可能性があります。
この新APIがそのような攻撃の精度や速度を大幅に向上させないようにするため、
DOMHighResTimeStamp型の
最小分解能は攻撃を防ぐのに十分不正確であるべきです。
必要に応じて、ユーザーエージェントは時刻の粗化の処理モデルにおいて 時間分解能をより高い値に設定し、アーキテクチャやソフトウェアの制約、その他の事情による プライバシー・セキュリティの懸念に対処するべきです。
このような攻撃を軽減するために、ユーザーエージェントは必要と思われる技術を自由に導入できます。 その導入はブラウザーのアーキテクチャやユーザーのデバイス、コンテンツ、クロスオリジンデータの悪用可能性、その他現実的な事情により異なる場合があります。
こうした技術には以下が含まれます:
タイミングのサイドチャネル攻撃を完全に防ぐのは実質的に不可能です。 全ての操作が機密情報の値に依存しない時間で実行されるか、アプリケーションがタイム関連のプリミティブ(クロック、タイマー、カウンター等)から完全隔離される必要があります。 いずれもブラウザーやアプリ開発者の負担や、アプリのパフォーマンス・応答性への悪影響を考えると現実的ではありません。
本仕様は、タイムオリジンのゼロ時刻のサブミリ秒分解能も提供するAPIを定義しており、 これはアプリケーションに単調クロックを公開し、すべてのブラウザーコンテキスト間で共有されていなければなりません。 単調クロックは物理時間に結びつける必要はありませんが、 [ECMA-262]の time定義を基準に設定することが推奨されます。 これはユーザーに新しいフィンガープリントエントロピーを露呈しないためであり、 例えばこの時刻はアプリケーション側で容易に取得できますが、新たな論理クロックを公開すると新情報となるためです。
ただし上記の仕組みがあっても単調クロックは
追加的なクロックドリフト分解能を提供し得ます。
今日では、アプリケーションは時刻(日時)と単調時刻(Date.now()、
now())を
同一コンテキスト内で複数回記録し、それらの間のドリフト(自動やユーザーによる時計調整による)を観測できます。
timeOrigin属性により、
攻撃者はタイムオリジン
(単調クロックによる報告値)と
タイムオリジンの現在の日時推定値
(例:performance.timeOrigin と Date.now() - performance.now() の差分)を比較し、
長期間にわたり両クロックのドリフトを観測することが可能となります。
実際には、同様の時刻ドリフトはアプリケーションが複数回ナビゲーションすることで観測できます。 各コンテキストで論理時刻を記録し、クライアントやサーバーの時刻同期機構を使ってユーザーの時計の変化を推測できます。 同様に、TCPタイムスタンプなど下位レイヤーの仕組みでも、複数回の訪問不要でサーバー側に高分解能情報が露呈する場合があります。 したがって、このAPIで提供される情報はユーザーについて新たな、または従来は利用できなかったエントロピーを 露呈しないようにすべきです。
タイムオリジンの現行定義では、
Documentに対して、
ドキュメントのオリジンへリクエストが到達する前の
クロスオリジンリダイレクトの合計時間を公開しています。これはクロスオリジン情報の漏洩につながりますが、
パフォーマンス指標の大きな破壊を起こさずにどう緩和するかは未決定です。
この議論を追うには、Navigation Timing Issue 160を参照してください。
非規範的と明記されたセクションのほか、本仕様書中のすべての執筆ガイドライン、図、例、補足も非規範的です。 それ以外はすべて規範的です。
本書におけるMUSTおよびSHOULDというキーワードは、 BCP 14 [RFC2119] [RFC8174] で示されている通り、全て大文字で表記されている場合のみ、ここで定める意味で解釈されます。
一部の適合性要件は属性、メソッド、オブジェクトへの要件として記述されていますが、 それらはユーザーエージェントへの要件として解釈されます。
DOMHighResTimeStamp
§5.
EpochTimeStamp
§6.
now()メソッド(Performance用)
§7.1
performance
属性(WindowOrWorkerGlobalScope用)
§8.1
Performanceインターフェイス
§7.
timeOrigin属性(Performance用)
§7.2
toJSON()メソッド(Performance用)
§7.3
Documentインターフェイス
EventTargetインターフェイス
timeStamp属性(Event用)
BroadcastChannelインターフェイス
realm用)
postMessage(message, options)(Window用)
Windowインターフェイス
WindowOrWorkerGlobalScopeインターフェイス
Workerインターフェイス
ServiceWorkerインターフェイス
[Default]拡張属性
double型
[Exposed]拡張属性
object型
[Replaceable]拡張属性
unsigned long long型
WebIDLtypedef double DOMHighResTimeStamp;
typedef unsigned long long EpochTimeStamp;
[Exposed=(Window,Worker)]
interface Performance : EventTarget {
DOMHighResTimeStamp now();
readonly attribute DOMHighResTimeStamp timeOrigin;
[Default] object toJSON();
};
partial interface mixin WindowOrWorkerGlobalScope {
[Replaceable] readonly attribute Performance performance;
};
この文書の執筆・貢献にあたり、Arvind Jain、Angelos D. Keromytis、Boris Zbarsky、Jason Weber、Karen Anderson、Nat Duca、Philippe Le Hegaret、Ryosuke Niwa、Simha Sethumadhavan、Todd Reifsteck、Tony Gentilcore、Vasileios P. Kemerlis、Yoav Weiss、Yossef Oren 各氏に感謝します。
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in: