Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ window.addEventListener('load', collectAndSendTrace);

## Markers Extensions

See [markers](markers.md) for detailed description of the proposal.
The API supports optional markers that identify browser activity during sampling. Markers are conditionally exposed based on security context - all markers are available in cross-origin isolated contexts, while only safe markers (`style`, `layout`) are available in regular contexts.

See [markers](markers.md) for detailed description of the proposal and [Conditional Markers Exposure](https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/ConditionalMarkersExposure/explainer.md) for technical implementation details.

## Privacy and Security

Expand Down
44 changes: 44 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,37 @@ <h2>Processing Model</h2>
<li>Set the <a>ProfilerSample.timestamp</a> property of <var>sample</var> to the <dfn data-cite="!hr-time-2#dfn-current-high-resolution-time">current high resolution time</dfn> relative to the <a>profiling session</a>'s <a>time origin</a>.</li>
<li>Let <var>stack</var> be the <dfn data-cite="!ECMA-262#execution-context-stack">execution context stack</dfn> associated with the profiling session's <a>agent</a>.</li>
<li>Set the <a>ProfilerSample.stackId</a> property of <var>sample</var> to the result of the <a>get a stack ID</a> algorithm on <var>stack</var>.</li>
<li>Let <var>marker</var> be the result of running the <a>capture a marker</a> algorithm given the profiling session's associated <a data-cite="HTML5#environment-settings-object">environment settings object</a>.</li>
<li>If <var>marker</var> is not null, set the <a>ProfilerSample.marker</a> property of <var>sample</var> to <var>marker</var>.</li>
<li>Add <var>sample</var> to the <a>ProfilerTrace.samples</a> associated with the session's <a>ProfilerTrace</a>.</li>
</ol>
<p>
To <dfn>capture a marker</dfn> given an <a data-cite="HTML5#environment-settings-object">environment settings object</a> <var>settings</var>, perform the following steps:
</p>
<ol>
<li>Let <var>candidateMarker</var> be the <a>ProfilerMarker</a> value that best describes the top-level work the user agent was performing on the profiling session's <a>agent</a> when the sample was taken, or null if none applies.</li>
<li>If <var>candidateMarker</var> is null, return null.</li>
<li>If the work described by <var>candidateMarker</var> was performed for a specific document that is not [= same origin =] with the <a data-cite="HTML5#concept-settings-object-origin">origin</a> of <var>settings</var>, return null.</li>
<li>Return the result of running <a>filter a marker</a> given <var>candidateMarker</var> and <var>settings</var>.</li>
</ol>
<p class="note">
The mapping from a user agent's internal activity to a <a>ProfilerMarker</a> value is intentionally implementation-defined, as engine architectures differ. As non-normative guidance, a user agent is expected to report <code>script</code> while executing author script, <code>gc</code> during garbage collection, <code>style</code> while recalculating computed style, <code>layout</code> while computing box geometry (i.e. reflow), and <code>paint</code> while rasterizing or compositing document content. The <code>other</code> marker is used for user-agent work that does not fall into one of the preceding categories. When no category applies (for example, when the agent is idle), the <a>capture a marker</a> algorithm returns null and the <a>ProfilerSample.marker</a> attribute is left absent on the resulting <a>ProfilerSample</a>.
</p>
<p class="note">
The same-origin check ensures a marker only describes work performed for a document that is [= same origin =] with the profiler, consistent with the requirement that the API not reveal cross-origin activity. Work that is not attributable to a single document &mdash; for example, engine-wide garbage collection &mdash; is not constrained by that check; such work is instead exposed only when the profiler is cross-origin isolated, as enforced by <a>filter a marker</a>.
</p>
<p>
To <dfn>filter a marker</dfn> given a <a>ProfilerMarker</a> <var>marker</var> and an <a data-cite="HTML5#environment-settings-object">environment settings object</a> <var>settings</var>, perform the following steps:
</p>
<ol>
<li>If <var>settings</var>' <a data-cite="HTML5#concept-settings-object-cross-origin-isolated-capability">cross-origin isolated capability</a> is true, return <var>marker</var>.</li>
<li>If <var>marker</var> is <code>"style"</code> or <code>"layout"</code>, return <var>marker</var>.</li>
<li>Return null.</li>
</ol>
<p class="note">
The <a>filter a marker</a> algorithm produces the observable behavior that in non-cross-origin-isolated contexts, only the <code>style</code> and <code>layout</code> markers appear on <a>ProfilerSample</a>; the <code>script</code>, <code>gc</code>, <code>paint</code>, and <code>other</code> markers are suppressed. See <a href="#conditional-markers">Conditional marker exposure</a> for the rationale behind this behavior. User agents MAY offer opt-in mechanisms (for example, an origin-trial token or a document policy) that widen the set returned by <a>filter a marker</a> for a specific <a data-cite="HTML5#environment-settings-object">environment settings object</a>; such opt-ins MUST NOT expose markers cross-origin.
</p>
<p>
To <dfn>get a stack ID</dfn> given an <a>execution context stack</a> bound to <var>stack</var>, perform the following steps:
<ol>
<li>If <var>stack</var> is empty, return <code>undefined</code>.</li>
Expand Down Expand Up @@ -307,13 +335,20 @@ <h2>The <dfn>ProfilerTrace</dfn> Dictionary</h2>
<section data-dfn-for="ProfilerSample" data-link-for="ProfilerSample">
<h2>The <dfn>ProfilerSample</dfn> Dictionary</h2>
<pre class="idl">
enum ProfilerMarker { "script", "gc", "style", "layout", "paint", "other" };

dictionary ProfilerSample {
required DOMHighResTimeStamp timestamp;
unsigned long long stackId;
ProfilerMarker? marker;
};
</pre>
<p><dfn>timestamp</dfn> MUST return the value it was initialized to.</p>
<p><dfn>stackId</dfn> MUST return the value it was initialized to.</p>
<p><dfn>marker</dfn> MUST return the value it was initialized to by the <a>take a sample</a> algorithm, or be absent if no marker was set. The set of marker values that may be exposed is governed by the <a>filter a marker</a> algorithm.</p>
<p class="note">
In cross-origin isolated contexts the <a>filter a marker</a> algorithm returns every value in the <a>ProfilerMarker</a> enumeration. In non-isolated contexts it returns only <code>style</code> and <code>layout</code>, so any other marker is suppressed (the <a>marker</a> attribute is absent on the corresponding <a>ProfilerSample</a>).
</p>
</section>
<section data-dfn-for="ProfilerStack" data-link-for="ProfilerStack">
<h2>The <dfn>ProfilerStack</dfn> Dictionary</h2>
Expand Down Expand Up @@ -437,6 +472,15 @@ <h2>Timing attacks</h2>
See [[?HR-Time]]'s discussion on <a href="https://www.w3.org/TR/hr-time-2/#clock-resolution">clock resolution</a>.
</p>
</section>
<section id="conditional-markers">
<h2>Conditional marker exposure</h2>
<p>
By default, the <a>filter a marker</a> algorithm exposes the <code>script</code>, <code>gc</code>, <code>paint</code>, and <code>other</code> markers only when the profiler's <a data-cite="HTML5#environment-settings-object">environment settings object</a> has its <a data-cite="HTML5#concept-settings-object-cross-origin-isolated-capability">cross-origin isolated capability</a> set to true. These markers can reflect engine work that cannot reliably be attributed to a single origin &mdash; for example, garbage collection and other cross-context engine activity can correlate work across contexts, and painting may composite content from other frames. Exposing the timing of such work without isolation could therefore contribute to a cross-origin side channel of the kind that <a data-cite="HTML5#concept-settings-object-cross-origin-isolated-capability">cross-origin isolation</a> is designed to mitigate, so it is gated behind it. A user agent MAY widen the exposed set through an explicit opt-in (as described in the <a>filter a marker</a> note above), but such opt-ins MUST NOT expose markers cross-origin.
</p>
<p>
The <code>style</code> and <code>layout</code> markers are exposed without cross-origin isolation because the timing they reveal is already same-origin observable. A document can already synchronously flush, and time, its own style and layout using existing APIs &mdash; for example, <code>getBoundingClientRect()</code> forces layout, and <code>getComputedStyle()</code> flushes style (and layout where required). To preserve this property, the <a>capture a marker</a> algorithm reports a marker only for work performed for a document that is same origin with the profiler; work that cannot be attributed to a same-origin document is not reported through these markers. As a result, they expose no timing information that a same-origin document could not already obtain for itself.
</p>
</section>
</section>
</body>
</html>
92 changes: 89 additions & 3 deletions markers.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,25 @@ enum ProfilerMarker { "script", "gc", "style", "layout", "paint", "other" };
dictionary ProfilerSample {
required DOMHighResTimeStamp timestamp;
unsigned long long stackId;
[CrossOriginIsolated] ProfilerMarker? marker;
ProfilerMarker? marker;
};
```

## Conditional Marker Exposure

Markers are conditionally exposed based on the security context to balance developer utility with privacy protection:

**Cross-Origin Isolated contexts** (with `Cross-Origin-Embedder-Policy: require-corp` and `Cross-Origin-Opener-Policy: same-origin` headers):
- All marker types are available: `script`, `gc`, `style`, `layout`, `paint`, `other`

**Non-isolated contexts** (regular browsing contexts):
- Only safe markers are available: `style`, `layout`
- Sensitive markers (`gc`, `paint`, `script`) are filtered out for security reasons

This graduated approach allows developers to access layout and style timing information (already available through DOM APIs and CSSOM) in regular contexts, while requiring explicit Cross-Origin Isolation for more sensitive timing data.

For detailed technical specification, see the [Conditional Markers Exposure explainer](https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/ConditionalMarkersExposure/explainer.md).

## Key scenarios

### Browser activity
Expand Down Expand Up @@ -240,14 +255,85 @@ Trace with markers:
"marker": "script"
},
{
"timestamp" :150
"timestamp": 150
}
```

### Conditional marker availability example

**In a Cross-Origin Isolated context** (all markers available):

```json
{
"samples": [
{
"timestamp": 100,
"stackId": 2,
"marker": "script"
},
{
"timestamp": 110,
"marker": "gc"
},
{
"timestamp": 120,
"marker": "style"
},
{
"timestamp": 130,
"marker": "layout"
},
{
"timestamp": 140,
"marker": "paint"
}
]
}
```

**In a regular (non-isolated) context** (only safe markers available):

```json
{
"samples": [
{
"timestamp": 100,
"stackId": 2
},
{
"timestamp": 110
},
{
"timestamp": 120,
"marker": "style"
},
{
"timestamp": 130,
"marker": "layout"
},
{
"timestamp": 140
}
]
}
```

Note how `gc`, `script`, and `paint` markers are filtered out in non-isolated contexts for security reasons.

## Privacy and Security

Careful consideration must be taken to avoid leaking top level UA work performed on a cross-origin document. UAs must only expose a marker if the responsible document for the work is same-origin with the profiler.

There is a risk to introduce a new source of side channel information through this API. Specifically, the timings of cross-origin opaque resources owned by the document that do not pass a Timing-Allow-Origin check or the timings of cross-origin documents hosted by the same process. To mitigate this risk, a Sample's marker attribute may only be accessible when the current Realm's settings objects's cross-origin isolated capability boolean is set to true.
To balance developer utility with privacy protection, markers are conditionally exposed based on security context:

**Safe markers** (`style`, `layout`): Available in all contexts as this information is already accessible through existing web APIs (DOM APIs, CSSOM) and does not pose additional security risks.

**Sensitive markers** (`gc`, `paint`, `script`): Only available in Cross-Origin Isolated contexts to prevent potential timing attacks and cross-origin information leakage. These markers could potentially be used for side-channel attacks or to infer information about cross-origin content.

There is a risk to introduce a new source of side channel information through this API. The conditional exposure model mitigates this risk by:
- Requiring Cross-Origin Isolation for sensitive timing information
- Only exposing safe markers that reveal information already available through other APIs in regular contexts
- Ensuring all markers are only exposed when the responsible document is same-origin with the profiler

Additional checks may also be required by user agents to implement this feature.
## Considered alternatives
Expand Down