This specification extends the High Resolution Time specification [[!HR-TIME-2]] by providing methods to store and retrieve high resolution performance metric data.

Performance Timeline Level 2 replaces the first version of [[PERFORMANCE-TIMELINE]] and includes:

Introduction

Accurately measuring performance characteristics of web applications is an important aspect of making web applications faster. This specification defines the necessary Performance Timeline primitives that enable web developers to access, instrument, and retrieve various performance metrics from the full lifecycle of a web application.

[[NAVIGATION-TIMING-2]], [[RESOURCE-TIMING]], and [[USER-TIMING]] are examples of specifications that define timing information related to the navigation of the document, resources on the page, and developer scripts, respectively. Together these and other performance interfaces define performance metrics that describe the Performance Timeline of a web application. For example, the following script shows how a developer can access the Performance Timeline to obtain performance metrics related to the navigation of the document, resources on the page, and developer scripts:

<!doctype html>
<html>
  <head>
  </head>
  <body onload="init()">
    <img id="image0" src="https://www.w3.org/Icons/w3c_main.png" />
    <script>
       function init()
       {
            performance.mark("startWork"); // see [USER-TIMING]
            doWork(); // Some developer code
            performance.mark("endWork");
            measurePerf();
       }
       function measurePerf()
       {
           var perfEntries = performance.getEntries();
           for (var i = 0; i < perfEntries.length; i++)
           {
                 if (window.console) {
                   console.log("Name: "        + perfEntries[i].name      +
                               " Entry Type: " + perfEntries[i].entryType +
                               " Start Time: " + perfEntries[i].startTime +
                               " Duration: "   + perfEntries[i].duration  + "\n");
                 }
           }
       }
    </script>
  </body>
</html>

Alternatively, the developer can observe the Performance Timeline and be notified of new performance metrics via the PerformanceObserver interface:

<!doctype html>
<html>
  <head>
  </head>
  <body>
    <img id="image0" src="https://www.w3.org/Icons/w3c_main.png" />
    <script>
    var doneObservingEvents = false;
    var observer = new PerformanceObserver(function(list) {
      var perfEntries = list.getEntries();
      for (var i = 0; i < perfEntries.length; i++)
      {
           if (window.console) {
             console.log("Name: "        + perfEntries[i].name      +
                         " Entry Type: " + perfEntries[i].entryType +
                         " Start Time: " + perfEntries[i].startTime +
                         " Duration: "   + perfEntries[i].duration  + "\n");
           }
      }
      // maybe disconnect after processing the events.
      if (doneObservingEvents) {
           observer.disconnect();
      }
    });
    // subscribe to Resource-Timing and User-Timing events
    observer.observe({entryTypes: ['resource', 'mark', 'measure']});
    </script>
  </body>
</html>

The PerformanceObserver interface was added in Performance Timeline Level 2 and is designed to address limitations of the buffer-based approach shown in the first example. By using the PerformanceObserver interface, the application can:

The developer is encouraged to use PerformanceObserver where possible. Further, new performance API's and metrics may only be available through the PerformanceObserver interface.

Conformance requirements phrased as algorithms or specific steps may be implemented in any manner, so long as the end result is equivalent. (In particular, the algorithms defined in this specification are intended to be easy to follow, and not intended to be performant.)

The IDL fragments in this specification must be interpreted as required for [conforming IDL fragments], as described in the Web IDL specification. [[!WebIDL]]

Performance Timeline

Each [ECMAScript global environment][es-global] has:

To queue a PerformanceEntry (new entry), run these steps:

  1. Let interested observers be an initially empty set of PerformanceObserver objects.
  2. For each registered performance observer (observer):
    1. If observer's PerformanceObserverInit entryTypes includes new entry’s entryType value, append observer to interested observers.
  3. For each observer in interested observers:
    1. Append new entry to observer buffer.
  4. If the performance observer task queued flag is set, terminate these steps.
  5. Set performance observer task queued flag.
  6. [Queue a task] that consists of running the following substeps. The [task source] for the queued task is the performance timeline task source.
    1. Unset performance observer task queued flag.
    2. Let notify list be a copy of [ECMAScript global environment][es-global]'s list of registered performance observer objects.
    3. For each PerformanceObserver object po in notify list, run these steps:
      1. Let entries be a copy of po’s observer buffer.
      2. Empty po’s observer buffer.
      3. If entries is non-empty, call po’s callback with entries as first argument and [callback this value]. If this throws an exception, [report the exception].

The performance timeline [task queue] is a low priority queue that, if possible, should be processed by the user agent during idle periods to minimize impact of performance monitoring code.

The PerformanceEntry interface

The PerformanceEntry interface hosts the performance data of various metrics.

[Exposed=(Window,Worker)]
interface PerformanceEntry {
    readonly    attribute DOMString           name;
    readonly    attribute DOMString           entryType;
    readonly    attribute DOMHighResTimeStamp startTime;
    readonly    attribute DOMHighResTimeStamp duration;
    serializer = {attribute};
};

The name attribute MUST return an identifier for this PerformanceEntry object. This identifier does not have to be unique.

The entryType attribute MUST return the type of the interface represented by this PerformanceEntry object.

Valid entryType values are: "mark [[USER-TIMING]], "measure" [[USER-TIMING]], "navigation" [[NAVIGATION-TIMING-2]], "resource" [[RESOURCE-TIMING]].

The startTime attribute MUST return the time value of the first recorded timestamp of this performance metric.

The duration attribute MUST return the time value of the duration of the entire event being recorded by this PerformanceEntry. Typically, this would be the time difference between the last recorded timestamp and the first recorded timestamp of this PerformanceEntry. If the duration concept doesn't apply, a performance metric may choose to return a `duration` of 0.

Extensions to the Performance interface

This extends the [Performance] interface [[!HR-TIME-2]] and hosts performance related attributes and methods used to retrieve the performance metric data from the Performance Timeline.

partial interface Performance {
    PerformanceEntryList getEntries ();
    PerformanceEntryList getEntriesByType (DOMString type);
    PerformanceEntryList getEntriesByName (DOMString name, optional DOMString type);
};
typedef sequence<PerformanceEntry> PerformanceEntryList;

The getEntries method returns a PerformanceEntryList object returned by algorithm with name and type set to `null`.

The getEntriesByType method returns a PerformanceEntryList object returned by algorithm with name set to `null` and type set to `type`.

The getEntriesByName method returns a PerformanceEntryList object returned by algorithm with name set to `name` and type set to `null` if optional `entryType` is omitted, and type set to `type` otherwise.

The PerformanceObserver interface

The PerformanceObserver interface can be used to observe the Performance Timeline and be notified of new performance entries as they are recorded.

Each PerformanceObserver has these associated concepts:

The `PerformanceObserver(callback)` constructor must create a new PerformanceObserver object with callback set to callback and then return it.

A registered performance observer consists of an observer (a PerformanceObserver object) and options (a PerformanceObserverInit dictionary).

dictionary PerformanceObserverInit {
  required sequence<DOMString> entryTypes;
};

[Exposed=(Window,Worker)]
interface PerformanceObserverEntryList {
  PerformanceEntryList getEntries ();
  PerformanceEntryList getEntriesByType (DOMString type);
  PerformanceEntryList getEntriesByName (DOMString name, optional DOMString type);
};

callback PerformanceObserverCallback = void (PerformanceObserverEntryList entries,
                                             PerformanceObserver observer);

[Constructor(PerformanceObserverCallback callback), Exposed=(Window,Worker)]
interface PerformanceObserver {
  void observe (PerformanceObserverInit options);
  void disconnect ();
};

PerformanceObserverInit.entryTypes is a list of valid entryType names to be observed. The list MUST NOT be empty and types not recognized by the user agent MUST be ignored.

To keep the performance overhead to minimum the application should only subscribe to event types that it is interested in, and disconnect the observer once it no longer needs to observe the performance data. Filtering by name is not supported, as it would implicitly require a subscription for all event types — this is possible, but discouraged, as it will generate a significant volume of events.

The PerformanceObserverEntryList interface provides the same getEntries, getEntriesByType, getEntriesByName methods as the Performance interface, except that PerformanceObserverEntryList operates on the observed emitted list of events instead of the global timeline.

The observe method instructs the user agent to register the observer and must run these steps:

  1. If _options'_ entryTypes attribute is not present, [throw] a JavaScript `TypeError`.
  2. Filter unsupported entryType entryType names within the `entryTypes` sequence, and replace the `entryTypes` sequence with the new filtered sequence.
  3. If the _options'_ entryTypes attribute is an empty sequence, [throw] a JavaScript `TypeError`.
  4. If the list of registered performance observer objects associated with the [ECMAScript global environment of the interface object][es-global]'s contains a registered performance observer that is the [context object][], replace the [context object][]'s `options` with options.
  5. Otherwise, append a new registered performance observer object to the list of registered performance observer objects associated with the [ECMAScript global environment of the interface object][es-global], with the [context object][] as `observer` and options as the `options`.

The disconnect method must remove the [context object] from the list of PerformanceObserver objects associated with the [ECMAScript global environment of the interface object][es-global], and also empty [context object]'s observer buffer.

Processing

Filter performance entry buffer by name and type

Given optional name and type string values this algorithm returns a PerformanceEntryList object that contains a list of PerformanceEntry objects, sorted in chronological order with respect to startTime.

  1. Let the list of entry objects be the empty PerformanceEntryList.
  2. For each PerformanceEntry object (entryObject) in the performance entry buffer, in chronological order with respect to startTime:
    1. If name is not `null` and entryObject's `name` attribute does not match name in a [case-sensitive] manner, go to next entryObject.
    2. If type is not `null` and entryObject's `type` attribute does not match type in a [case-sensitive] manner, go to next entryObject.
    3. Add entryObject to the list of entry objects.
  3. Return the list of entry objects.

Privacy and Security

This specification extends the Performance interface defined by [[HR-TIME-2]] and provides methods to queue and retrieve entries from the performance timeline. Please refer to [[HR-TIME-2]] for privacy and security considerations of exposing high-resoluting timing information.

Acknowledgments

Thanks to Arvind Jain, Boris Zbarsky, Jatinder Mann, Nat Duca, Philippe Le Hegaret, Ryosuke Niwa, Shubhie Panicker, Todd Reifsteck, Yoav Weiss, and Zhiheng Wang, for their contributions to this work.

[context object]: https://dom.spec.whatwg.org/#context-object [task source]: https://html.spec.whatwg.org/multipage/webappapis.html#task-source [queue a task]: https://html.spec.whatwg.org/multipage/webappapis.html#queue-a-task [task queue]: https://html.spec.whatwg.org/multipage/webappapis.html#task-queue [report the exception]: https://html.spec.whatwg.org/multipage/webappapis.html#report-the-exception [case-sensitive]: https://html.spec.whatwg.org/multipage/infrastructure.html#case-sensitive [conforming IDL fragments]: https://heycam.github.io/webidl/#dfn-conforming-idl-fragment [es-global]: https://heycam.github.io/webidl/#es-environment [dictionary member]: https://heycam.github.io/webidl/#dfn-dictionary-member [throw]: http://heycam.github.io/webidl/#dfn-throw [callback this value]: https://heycam.github.io/webidl/#dfn-callback-this-value [Performance]: https://w3c.github.io/hr-time/#idl-def-Performance [initiatorType]: https://w3c.github.io/resource-timing/#widl-PerformanceResourceTiming-initiatorType [PerformanceResourceTiming]: https://w3c.github.io/resource-timing/#idl-def-PerformanceResourceTiming