MSEdgeExplainers

JavaScript Self-Profiling API: Conditional Marker Exposure

Table of Contents

  1. Introduction
  2. Problem Statement
  3. Proposed Solution
  4. Behavioral Changes
  5. Security Analysis
  6. API Impact
  7. Examples
  8. Compatibility

Introduction

This explainer describes a proposed modification to the JavaScript Self-Profiling API that enables conditional exposure of ProfilerMarker values based on context isolation status. The change allows selective disclosure of markers based on their security sensitivity, improving developer experience while maintaining security boundaries.

Problem Statement

Current Limitation

The current JavaScript Self-Profiling API markers feature has some limitations:

Goals

Proposed Solution

Key Changes

  1. Graduated disclosure: Allow certain safe markers (layout, style) in non-isolated contexts
  2. Security-based filtering: Continue to restrict sensitive markers (gc, paint, script) to Cross-Origin Isolated contexts
  3. Transparent operation: No changes to the API surface; filtering happens internally

Rationale

Layout and style state information is already exposed through other browser APIs (DOM APIs and CSSOM), making these markers safe for broader availability without introducing new security risks.

Behavioral Changes

Context Type Feature Flag Available Markers
Cross-Origin Isolated Enabled All markers (gc, layout, paint, script, style)
Cross-Origin Isolated Disabled None
Non-Isolated Enabled Limited (layout, style only)
Non-Isolated Disabled None

Security Analysis

Safe Markers in Non-Isolated Contexts

Restricted Markers

The following markers remain restricted to Cross-Origin Isolated contexts due to potential security implications:

Security Model

This approach follows the principle of graduated disclosure, where:

  1. Information already available through other APIs can be safely exposed
  2. Sensitive timing information requires explicit Cross-Origin Isolation opt-in
  3. The overall feature remains controlled by user agent implementation

API Impact

For Developers

The API surface remains unchanged from a developer perspective. The conditional exposure happens transparently:

// Cross-origin isolated context
const profiler = new Profiler({sampleInterval: 10, maxBufferSize: 1000});
const trace = await profiler.stop();
// trace.samples may contain all marker types

// Non-isolated context  
const profiler = new Profiler({sampleInterval: 10, maxBufferSize: 1000});
const trace = await profiler.stop();
// trace.samples may contain layout/style markers only

Trace Format

No changes to trace structure. The marker field in samples will be:

Examples

Cross-Origin Isolated Context

// Set required headers for Cross-Origin Isolation:
// Cross-Origin-Embedder-Policy: require-corp
// Cross-Origin-Opener-Policy: same-origin

const profiler = new Profiler({sampleInterval: 5, maxBufferSize: 1000});

// Trigger various browser activities
document.body.style.color = 'red';  // Style recalculation
document.body.offsetHeight;         // Layout
new Array(1000000);                 // Potential GC

const trace = await profiler.stop();

// Sample output may include all marker types:
console.log(trace.samples);
// [
//   { timestamp: 1234.5, stackId: 2, marker: "gc" },
//   { timestamp: 1235.0, stackId: 3, marker: "layout" },
//   { timestamp: 1235.5, stackId: 1, marker: "style" },
//   { timestamp: 1236.0, stackId: 4, marker: "paint" }
// ]

Non-Isolated Context

// Regular context (no special headers required)

const profiler = new Profiler({sampleInterval: 5, maxBufferSize: 1000});

// Trigger various browser activities
document.body.style.color = 'blue';  // Style recalculation  
document.body.offsetHeight;          // Layout
new Array(1000000);                  // Potential GC (won't be exposed)

const trace = await profiler.stop();

// Sample output with filtered markers:
console.log(trace.samples);
// [
//   { timestamp: 1234.5, stackId: 2 },                    // gc marker filtered out
//   { timestamp: 1235.0, stackId: 3, marker: "layout" },  // allowed
//   { timestamp: 1235.5, stackId: 1, marker: "style" },   // allowed
//   { timestamp: 1236.0, stackId: 4 }                     // paint marker filtered out
// ]

Feature Disabled

const profiler = new Profiler({sampleInterval: 5, maxBufferSize: 1000});
const trace = await profiler.stop();

// No markers regardless of context isolation status:
console.log(trace.samples);
// [
//   { timestamp: 1234.5, stackId: 2 },
//   { timestamp: 1235.0, stackId: 3 },
//   { timestamp: 1235.5, stackId: 1 }
// ]

Compatibility

Backward Compatibility

Forward Compatibility

References