mirror of https://github.com/nodejs/node.git
perf_hooks: fix webperf idlharness
1. Enforce receiver checks on IDL interfaces. 2. Avoid prototype manipulation on constructing IDL interfaces with `ReflectConstruct`. 3. `defineReplaceableAttribute` should create IDL getter/setter. 4. Corrected `PerformanceResourceTiming` to inherit the public interface `PerformanceEntry` instead of the internal interface `InternalPerformanceResourceTiming`. 5. `detail` is not a specified attribute on `PerfomanceEntry`. Node.js specific extensions are moved to a subclass of `PerformanceEntry` as `PerformanceNodeEntry`. PR-URL: https://github.com/nodejs/node/pull/44483 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Minwoo Jung <nodecorelab@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
f529f73bd7
commit
364c0e196c
|
@ -49,6 +49,11 @@ Node.js instance. It is similar to [`window.performance`][] in browsers.
|
|||
|
||||
<!-- YAML
|
||||
added: v8.5.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This method must be called with the `performance` object as
|
||||
the receiver.
|
||||
-->
|
||||
|
||||
* `name` {string}
|
||||
|
@ -60,6 +65,11 @@ Performance Timeline. If `name` is provided, removes only the named mark.
|
|||
|
||||
<!-- YAML
|
||||
added: v16.7.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This method must be called with the `performance` object as
|
||||
the receiver.
|
||||
-->
|
||||
|
||||
* `name` {string}
|
||||
|
@ -73,6 +83,11 @@ Performance Timeline. If `name` is provided, removes only the named measure.
|
|||
added:
|
||||
- v18.2.0
|
||||
- v16.17.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This method must be called with the `performance` object as
|
||||
the receiver.
|
||||
-->
|
||||
|
||||
* `name` {string}
|
||||
|
@ -147,6 +162,11 @@ are not guaranteed to reflect any correct state of the event loop.
|
|||
|
||||
<!-- YAML
|
||||
added: v16.7.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This method must be called with the `performance` object as
|
||||
the receiver.
|
||||
-->
|
||||
|
||||
* Returns: {PerformanceEntry\[]}
|
||||
|
@ -160,6 +180,11 @@ performance entries of certain types or that have certain names, see
|
|||
|
||||
<!-- YAML
|
||||
added: v16.7.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This method must be called with the `performance` object as
|
||||
the receiver.
|
||||
-->
|
||||
|
||||
* `name` {string}
|
||||
|
@ -175,6 +200,11 @@ equal to `name`, and optionally, whose `performanceEntry.entryType` is equal to
|
|||
|
||||
<!-- YAML
|
||||
added: v16.7.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This method must be called with the `performance` object as
|
||||
the receiver.
|
||||
-->
|
||||
|
||||
* `type` {string}
|
||||
|
@ -189,6 +219,10 @@ is equal to `type`.
|
|||
<!-- YAML
|
||||
added: v8.5.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This method must be called with the `performance` object as
|
||||
the receiver.
|
||||
- version: v16.0.0
|
||||
pr-url: https://github.com/nodejs/node/pull/37136
|
||||
description: Updated to conform to the User Timing Level 3 specification.
|
||||
|
@ -244,6 +278,10 @@ Performance Timeline manually with `performance.clearResourceTimings`.
|
|||
<!-- YAML
|
||||
added: v8.5.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This method must be called with the `performance` object as
|
||||
the receiver.
|
||||
- version: v16.0.0
|
||||
pr-url: https://github.com/nodejs/node/pull/37136
|
||||
description: Updated to conform to the User Timing Level 3 specification.
|
||||
|
@ -305,6 +343,11 @@ metrics for specific Node.js operational milestones.
|
|||
|
||||
<!-- YAML
|
||||
added: v8.5.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This method must be called with the `performance` object as
|
||||
the receiver.
|
||||
-->
|
||||
|
||||
* Returns: {number}
|
||||
|
@ -316,6 +359,11 @@ the start of the current `node` process.
|
|||
|
||||
<!-- YAML
|
||||
added: v18.8.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This method must be called with the `performance` object as
|
||||
the receiver.
|
||||
-->
|
||||
|
||||
Sets the global performance resource timing buffer size to the specified number
|
||||
|
@ -393,6 +441,11 @@ invoked.
|
|||
|
||||
<!-- YAML
|
||||
added: v16.1.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This method must be called with the `performance` object as
|
||||
the receiver.
|
||||
-->
|
||||
|
||||
An object which is JSON representation of the `performance` object. It
|
||||
|
@ -416,20 +469,17 @@ more entries to be added to the performance timeline buffer.
|
|||
added: v8.5.0
|
||||
-->
|
||||
|
||||
### `performanceEntry.detail`
|
||||
|
||||
<!-- YAML
|
||||
added: v16.0.0
|
||||
-->
|
||||
|
||||
* {any}
|
||||
|
||||
Additional detail specific to the `entryType`.
|
||||
The constructor of this class is not exposed to users directly.
|
||||
|
||||
### `performanceEntry.duration`
|
||||
|
||||
<!-- YAML
|
||||
added: v8.5.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceEntry` object as the receiver.
|
||||
-->
|
||||
|
||||
* {number}
|
||||
|
@ -441,6 +491,11 @@ be meaningful for all Performance Entry types.
|
|||
|
||||
<!-- YAML
|
||||
added: v8.5.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceEntry` object as the receiver.
|
||||
-->
|
||||
|
||||
* {string}
|
||||
|
@ -455,7 +510,123 @@ The type of the performance entry. It may be one of:
|
|||
* `'http2'` (Node.js only)
|
||||
* `'http'` (Node.js only)
|
||||
|
||||
### `performanceEntry.flags`
|
||||
### `performanceEntry.name`
|
||||
|
||||
<!-- YAML
|
||||
added: v8.5.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceEntry` object as the receiver.
|
||||
-->
|
||||
|
||||
* {string}
|
||||
|
||||
The name of the performance entry.
|
||||
|
||||
### `performanceEntry.startTime`
|
||||
|
||||
<!-- YAML
|
||||
added: v8.5.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceEntry` object as the receiver.
|
||||
-->
|
||||
|
||||
* {number}
|
||||
|
||||
The high resolution millisecond timestamp marking the starting time of the
|
||||
Performance Entry.
|
||||
|
||||
## Class: `PerformanceMark`
|
||||
|
||||
<!-- YAML
|
||||
added:
|
||||
- v18.2.0
|
||||
- v16.17.0
|
||||
-->
|
||||
|
||||
* Extends: {PerformanceEntry}
|
||||
|
||||
Exposes marks created via the `Performance.mark()` method.
|
||||
|
||||
### `performanceMark.detail`
|
||||
|
||||
<!-- YAML
|
||||
added: v16.0.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceMark` object as the receiver.
|
||||
-->
|
||||
|
||||
* {any}
|
||||
|
||||
Additional detail specified when creating with `Performance.mark()` method.
|
||||
|
||||
## Class: `PerformanceMeasure`
|
||||
|
||||
<!-- YAML
|
||||
added:
|
||||
- v18.2.0
|
||||
- v16.17.0
|
||||
-->
|
||||
|
||||
* Extends: {PerformanceEntry}
|
||||
|
||||
Exposes measures created via the `Performance.measure()` method.
|
||||
|
||||
The constructor of this class is not exposed to users directly.
|
||||
|
||||
### `performanceMeasure.detail`
|
||||
|
||||
<!-- YAML
|
||||
added: v16.0.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceMeasure` object as the receiver.
|
||||
-->
|
||||
|
||||
* {any}
|
||||
|
||||
Additional detail specified when creating with `Performance.measure()` method.
|
||||
|
||||
## Class: `PerformanceNodeEntry`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* Extends: {PerformanceEntry}
|
||||
|
||||
_This class is an extension by Node.js. It is not available in Web browsers._
|
||||
|
||||
Provides detailed Node.js timing data.
|
||||
|
||||
The constructor of this class is not exposed to users directly.
|
||||
|
||||
### `performanceNodeEntry.detail`
|
||||
|
||||
<!-- YAML
|
||||
added: v16.0.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceNodeEntry` object as the receiver.
|
||||
-->
|
||||
|
||||
* {any}
|
||||
|
||||
Additional detail specific to the `entryType`.
|
||||
|
||||
### `performanceNodeEntry.flags`
|
||||
|
||||
<!-- YAML
|
||||
added:
|
||||
|
@ -468,9 +639,9 @@ changes:
|
|||
when entryType is 'gc'.
|
||||
-->
|
||||
|
||||
* {number}
|
||||
> Stability: 0 - Deprecated: Use `performanceNodeEntry.detail` instead.
|
||||
|
||||
_This property is an extension by Node.js. It is not available in Web browsers._
|
||||
* {number}
|
||||
|
||||
When `performanceEntry.entryType` is equal to `'gc'`, the `performance.flags`
|
||||
property contains additional information about garbage collection operation.
|
||||
|
@ -484,17 +655,7 @@ The value may be one of:
|
|||
* `perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_ALL_EXTERNAL_MEMORY`
|
||||
* `perf_hooks.constants.NODE_PERFORMANCE_GC_FLAGS_SCHEDULE_IDLE`
|
||||
|
||||
### `performanceEntry.name`
|
||||
|
||||
<!-- YAML
|
||||
added: v8.5.0
|
||||
-->
|
||||
|
||||
* {string}
|
||||
|
||||
The name of the performance entry.
|
||||
|
||||
### `performanceEntry.kind`
|
||||
### `performanceNodeEntry.kind`
|
||||
|
||||
<!-- YAML
|
||||
added: v8.5.0
|
||||
|
@ -505,9 +666,9 @@ changes:
|
|||
when entryType is 'gc'.
|
||||
-->
|
||||
|
||||
* {number}
|
||||
> Stability: 0 - Deprecated: Use `performanceNodeEntry.detail` instead.
|
||||
|
||||
_This property is an extension by Node.js. It is not available in Web browsers._
|
||||
* {number}
|
||||
|
||||
When `performanceEntry.entryType` is equal to `'gc'`, the `performance.kind`
|
||||
property identifies the type of garbage collection operation that occurred.
|
||||
|
@ -518,21 +679,10 @@ The value may be one of:
|
|||
* `perf_hooks.constants.NODE_PERFORMANCE_GC_INCREMENTAL`
|
||||
* `perf_hooks.constants.NODE_PERFORMANCE_GC_WEAKCB`
|
||||
|
||||
### `performanceEntry.startTime`
|
||||
|
||||
<!-- YAML
|
||||
added: v8.5.0
|
||||
-->
|
||||
|
||||
* {number}
|
||||
|
||||
The high resolution millisecond timestamp marking the starting time of the
|
||||
Performance Entry.
|
||||
|
||||
### Garbage Collection ('gc') Details
|
||||
|
||||
When `performanceEntry.type` is equal to `'gc'`, the `performanceEntry.detail`
|
||||
property will be an {Object} with two properties:
|
||||
When `performanceEntry.type` is equal to `'gc'`, the
|
||||
`performanceNodeEntry.detail` property will be an {Object} with two properties:
|
||||
|
||||
* `kind` {number} One of:
|
||||
* `perf_hooks.constants.NODE_PERFORMANCE_GC_MAJOR`
|
||||
|
@ -550,8 +700,9 @@ property will be an {Object} with two properties:
|
|||
|
||||
### HTTP ('http') Details
|
||||
|
||||
When `performanceEntry.type` is equal to `'http'`, the `performanceEntry.detail`
|
||||
property will be an {Object} containing additional information.
|
||||
When `performanceEntry.type` is equal to `'http'`, the
|
||||
`performanceNodeEntry.detail` property will be an {Object} containing
|
||||
additional information.
|
||||
|
||||
If `performanceEntry.name` is equal to `HttpClient`, the `detail`
|
||||
will contain the following properties: `req`, `res`. And the `req` property
|
||||
|
@ -569,7 +720,7 @@ diagnostic purposes, not left turned on in production by default.
|
|||
### HTTP/2 ('http2') Details
|
||||
|
||||
When `performanceEntry.type` is equal to `'http2'`, the
|
||||
`performanceEntry.detail` property will be an {Object} containing
|
||||
`performanceNodeEntry.detail` property will be an {Object} containing
|
||||
additional performance information.
|
||||
|
||||
If `performanceEntry.name` is equal to `Http2Stream`, the `detail`
|
||||
|
@ -610,13 +761,13 @@ contain the following properties:
|
|||
### Timerify ('function') Details
|
||||
|
||||
When `performanceEntry.type` is equal to `'function'`, the
|
||||
`performanceEntry.detail` property will be an {Array} listing
|
||||
`performanceNodeEntry.detail` property will be an {Array} listing
|
||||
the input arguments to the timed function.
|
||||
|
||||
### Net ('net') Details
|
||||
|
||||
When `performanceEntry.type` is equal to `'net'`, the
|
||||
`performanceEntry.detail` property will be an {Object} containing
|
||||
`performanceNodeEntry.detail` property will be an {Object} containing
|
||||
additional information.
|
||||
|
||||
If `performanceEntry.name` is equal to `connect`, the `detail`
|
||||
|
@ -625,7 +776,7 @@ will contain the following properties: `host`, `port`.
|
|||
### DNS ('dns') Details
|
||||
|
||||
When `performanceEntry.type` is equal to `'dns'`, the
|
||||
`performanceEntry.detail` property will be an {Object} containing
|
||||
`performanceNodeEntry.detail` property will be an {Object} containing
|
||||
additional information.
|
||||
|
||||
If `performanceEntry.name` is equal to `lookup`, the `detail`
|
||||
|
@ -758,6 +909,11 @@ The constructor of this class is not exposed to users directly.
|
|||
added:
|
||||
- v18.2.0
|
||||
- v16.17.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceResourceTiming` object as the receiver.
|
||||
-->
|
||||
|
||||
* {number}
|
||||
|
@ -772,6 +928,11 @@ will always return 0.
|
|||
added:
|
||||
- v18.2.0
|
||||
- v16.17.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceResourceTiming` object as the receiver.
|
||||
-->
|
||||
|
||||
* {number}
|
||||
|
@ -785,6 +946,11 @@ of the fetch which initiates the redirect.
|
|||
added:
|
||||
- v18.2.0
|
||||
- v16.17.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceResourceTiming` object as the receiver.
|
||||
-->
|
||||
|
||||
* {number}
|
||||
|
@ -798,6 +964,11 @@ receiving the last byte of the response of the last redirect.
|
|||
added:
|
||||
- v18.2.0
|
||||
- v16.17.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceResourceTiming` object as the receiver.
|
||||
-->
|
||||
|
||||
* {number}
|
||||
|
@ -811,6 +982,11 @@ to fetch the resource.
|
|||
added:
|
||||
- v18.2.0
|
||||
- v16.17.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceResourceTiming` object as the receiver.
|
||||
-->
|
||||
|
||||
* {number}
|
||||
|
@ -824,6 +1000,11 @@ the domain name lookup for the resource.
|
|||
added:
|
||||
- v18.2.0
|
||||
- v16.17.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceResourceTiming` object as the receiver.
|
||||
-->
|
||||
|
||||
* {number}
|
||||
|
@ -837,6 +1018,11 @@ after the Node.js finished the domain name lookup for the resource.
|
|||
added:
|
||||
- v18.2.0
|
||||
- v16.17.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceResourceTiming` object as the receiver.
|
||||
-->
|
||||
|
||||
* {number}
|
||||
|
@ -851,6 +1037,11 @@ the resource.
|
|||
added:
|
||||
- v18.2.0
|
||||
- v16.17.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceResourceTiming` object as the receiver.
|
||||
-->
|
||||
|
||||
* {number}
|
||||
|
@ -865,6 +1056,11 @@ the resource.
|
|||
added:
|
||||
- v18.2.0
|
||||
- v16.17.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceResourceTiming` object as the receiver.
|
||||
-->
|
||||
|
||||
* {number}
|
||||
|
@ -878,6 +1074,11 @@ before Node.js starts the handshake process to secure the current connection.
|
|||
added:
|
||||
- v18.2.0
|
||||
- v16.17.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceResourceTiming` object as the receiver.
|
||||
-->
|
||||
|
||||
* {number}
|
||||
|
@ -891,6 +1092,11 @@ before Node.js receives the first byte of the response from the server.
|
|||
added:
|
||||
- v18.2.0
|
||||
- v16.17.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceResourceTiming` object as the receiver.
|
||||
-->
|
||||
|
||||
* {number}
|
||||
|
@ -905,6 +1111,11 @@ the transport connection is closed, whichever comes first.
|
|||
added:
|
||||
- v18.2.0
|
||||
- v16.17.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceResourceTiming` object as the receiver.
|
||||
-->
|
||||
|
||||
* {number}
|
||||
|
@ -918,6 +1129,11 @@ includes the response header fields plus the response payload body.
|
|||
added:
|
||||
- v18.2.0
|
||||
- v16.17.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceResourceTiming` object as the receiver.
|
||||
-->
|
||||
|
||||
* {number}
|
||||
|
@ -932,6 +1148,11 @@ content-codings.
|
|||
added:
|
||||
- v18.2.0
|
||||
- v16.17.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This property getter must be called with the
|
||||
`PerformanceResourceTiming` object as the receiver.
|
||||
-->
|
||||
|
||||
* {number}
|
||||
|
@ -946,12 +1167,17 @@ content-codings.
|
|||
added:
|
||||
- v18.2.0
|
||||
- v16.17.0
|
||||
changes:
|
||||
- version: REPLACEME
|
||||
pr-url: https://github.com/nodejs/node/pull/44483
|
||||
description: This method must be called with the
|
||||
`PerformanceResourceTiming` object as the receiver.
|
||||
-->
|
||||
|
||||
Returns a `object` that is the JSON representation of the
|
||||
`PerformanceResourceTiming` object
|
||||
|
||||
## Class: `perf_hooks.PerformanceObserver`
|
||||
## Class: `PerformanceObserver`
|
||||
|
||||
### `new PerformanceObserver(callback)`
|
||||
|
||||
|
|
|
@ -75,8 +75,8 @@ exposeInterface(globalThis, 'Blob', buffer.Blob);
|
|||
// https://www.w3.org/TR/hr-time-2/#the-performance-attribute
|
||||
const perf_hooks = require('perf_hooks');
|
||||
exposeInterface(globalThis, 'Performance', perf_hooks.Performance);
|
||||
defineReplacableAttribute(globalThis, 'performance',
|
||||
perf_hooks.performance);
|
||||
defineReplaceableAttribute(globalThis, 'performance',
|
||||
perf_hooks.performance);
|
||||
|
||||
function createGlobalConsole() {
|
||||
const consoleFromNode =
|
||||
|
@ -114,14 +114,33 @@ function exposeGetterAndSetter(target, name, getter, setter = undefined) {
|
|||
});
|
||||
}
|
||||
|
||||
// https://heycam.github.io/webidl/#Replaceable
|
||||
function defineReplacableAttribute(target, name, value) {
|
||||
// https://webidl.spec.whatwg.org/#Replaceable
|
||||
function defineReplaceableAttribute(target, name, value) {
|
||||
let slot = value;
|
||||
|
||||
// https://webidl.spec.whatwg.org/#dfn-attribute-getter
|
||||
function get() {
|
||||
return slot;
|
||||
}
|
||||
ObjectDefineProperty(get, 'name', {
|
||||
__proto__: null,
|
||||
value: `get ${name}`,
|
||||
});
|
||||
|
||||
function set(value) {
|
||||
slot = value;
|
||||
}
|
||||
ObjectDefineProperty(set, 'name', {
|
||||
__proto__: null,
|
||||
value: `set ${name}`,
|
||||
});
|
||||
|
||||
ObjectDefineProperty(target, name, {
|
||||
__proto__: null,
|
||||
writable: true,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
value,
|
||||
get,
|
||||
set,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -16,9 +16,11 @@ const {
|
|||
ObjectDefineProperties,
|
||||
ObjectFreeze,
|
||||
ObjectKeys,
|
||||
ReflectConstruct,
|
||||
SafeMap,
|
||||
SafeSet,
|
||||
Symbol,
|
||||
SymbolToStringTag,
|
||||
} = primordials;
|
||||
|
||||
const {
|
||||
|
@ -36,12 +38,13 @@ const {
|
|||
} = internalBinding('performance');
|
||||
|
||||
const {
|
||||
InternalPerformanceEntry,
|
||||
isPerformanceEntry,
|
||||
createPerformanceNodeEntry,
|
||||
} = require('internal/perf/performance_entry');
|
||||
|
||||
const {
|
||||
codes: {
|
||||
ERR_ILLEGAL_CONSTRUCTOR,
|
||||
ERR_INVALID_ARG_VALUE,
|
||||
ERR_INVALID_ARG_TYPE,
|
||||
ERR_MISSING_ARGS,
|
||||
|
@ -51,6 +54,7 @@ const {
|
|||
const {
|
||||
validateFunction,
|
||||
validateObject,
|
||||
validateInternalField,
|
||||
} = require('internal/validators');
|
||||
|
||||
const {
|
||||
|
@ -58,6 +62,7 @@ const {
|
|||
deprecate,
|
||||
lazyDOMException,
|
||||
kEmptyObject,
|
||||
kEnumerableProperty,
|
||||
} = require('internal/util');
|
||||
|
||||
const {
|
||||
|
@ -68,6 +73,7 @@ const { inspect } = require('util');
|
|||
|
||||
const { now } = require('internal/perf/utils');
|
||||
|
||||
const kBuffer = Symbol('kBuffer');
|
||||
const kDispatch = Symbol('kDispatch');
|
||||
const kMaybeBuffer = Symbol('kMaybeBuffer');
|
||||
const kDeprecatedFields = Symbol('kDeprecatedFields');
|
||||
|
@ -167,34 +173,39 @@ function maybeIncrementObserverCount(type) {
|
|||
}
|
||||
|
||||
class PerformanceObserverEntryList {
|
||||
#buffer = [];
|
||||
|
||||
constructor(entries) {
|
||||
this.#buffer = ArrayPrototypeSort(entries, (first, second) => {
|
||||
return first.startTime - second.startTime;
|
||||
});
|
||||
constructor() {
|
||||
throw new ERR_ILLEGAL_CONSTRUCTOR();
|
||||
}
|
||||
|
||||
getEntries() {
|
||||
return ArrayPrototypeSlice(this.#buffer);
|
||||
validateInternalField(this, kBuffer, 'PerformanceObserverEntryList');
|
||||
return ArrayPrototypeSlice(this[kBuffer]);
|
||||
}
|
||||
|
||||
getEntriesByType(type) {
|
||||
validateInternalField(this, kBuffer, 'PerformanceObserverEntryList');
|
||||
if (arguments.length === 0) {
|
||||
throw new ERR_MISSING_ARGS('type');
|
||||
}
|
||||
type = `${type}`;
|
||||
return ArrayPrototypeFilter(
|
||||
this.#buffer,
|
||||
this[kBuffer],
|
||||
(entry) => entry.entryType === type);
|
||||
}
|
||||
|
||||
getEntriesByName(name, type) {
|
||||
getEntriesByName(name, type = undefined) {
|
||||
validateInternalField(this, kBuffer, 'PerformanceObserverEntryList');
|
||||
if (arguments.length === 0) {
|
||||
throw new ERR_MISSING_ARGS('name');
|
||||
}
|
||||
name = `${name}`;
|
||||
if (type != null /** not nullish */) {
|
||||
return ArrayPrototypeFilter(
|
||||
this.#buffer,
|
||||
this[kBuffer],
|
||||
(entry) => entry.name === name && entry.entryType === type);
|
||||
}
|
||||
return ArrayPrototypeFilter(
|
||||
this.#buffer,
|
||||
this[kBuffer],
|
||||
(entry) => entry.name === name);
|
||||
}
|
||||
|
||||
|
@ -206,9 +217,29 @@ class PerformanceObserverEntryList {
|
|||
depth: options.depth == null ? null : options.depth - 1
|
||||
};
|
||||
|
||||
return `PerformanceObserverEntryList ${inspect(this.#buffer, opts)}`;
|
||||
return `PerformanceObserverEntryList ${inspect(this[kBuffer], opts)}`;
|
||||
}
|
||||
}
|
||||
ObjectDefineProperties(PerformanceObserverEntryList.prototype, {
|
||||
getEntries: kEnumerableProperty,
|
||||
getEntriesByType: kEnumerableProperty,
|
||||
getEntriesByName: kEnumerableProperty,
|
||||
[SymbolToStringTag]: {
|
||||
__proto__: null,
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: 'PerformanceObserverEntryList',
|
||||
},
|
||||
});
|
||||
|
||||
function createPerformanceObserverEntryList(entries) {
|
||||
return ReflectConstruct(function PerformanceObserverEntryList() {
|
||||
this[kBuffer] = ArrayPrototypeSort(entries, (first, second) => {
|
||||
return first.startTime - second.startTime;
|
||||
});
|
||||
}, [], PerformanceObserverEntryList);
|
||||
}
|
||||
|
||||
class PerformanceObserver {
|
||||
#buffer = [];
|
||||
|
@ -319,7 +350,7 @@ class PerformanceObserver {
|
|||
}
|
||||
|
||||
[kDispatch]() {
|
||||
this.#callback(new PerformanceObserverEntryList(this.takeRecords()),
|
||||
this.#callback(createPerformanceObserverEntryList(this.takeRecords()),
|
||||
this);
|
||||
}
|
||||
|
||||
|
@ -339,6 +370,18 @@ class PerformanceObserver {
|
|||
}, opts)}`;
|
||||
}
|
||||
}
|
||||
ObjectDefineProperties(PerformanceObserver.prototype, {
|
||||
observe: kEnumerableProperty,
|
||||
disconnect: kEnumerableProperty,
|
||||
takeRecords: kEnumerableProperty,
|
||||
[SymbolToStringTag]: {
|
||||
__proto__: null,
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
value: 'PerformanceObserver',
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* https://www.w3.org/TR/performance-timeline/#dfn-queue-a-performanceentry
|
||||
|
@ -485,7 +528,7 @@ function filterBufferMapByNameAndType(name, type) {
|
|||
|
||||
function observerCallback(name, type, startTime, duration, details) {
|
||||
const entry =
|
||||
new InternalPerformanceEntry(
|
||||
createPerformanceNodeEntry(
|
||||
name,
|
||||
type,
|
||||
startTime,
|
||||
|
@ -542,7 +585,7 @@ function stopPerf(target, key, context = {}) {
|
|||
return;
|
||||
}
|
||||
const startTime = ctx.startTime;
|
||||
const entry = new InternalPerformanceEntry(
|
||||
const entry = createPerformanceNodeEntry(
|
||||
ctx.name,
|
||||
ctx.type,
|
||||
startTime,
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
'use strict';
|
||||
|
||||
const {
|
||||
ObjectDefineProperty,
|
||||
ObjectDefineProperties,
|
||||
ObjectSetPrototypeOf,
|
||||
ReflectConstruct,
|
||||
Symbol,
|
||||
SymbolToStringTag,
|
||||
} = primordials;
|
||||
|
||||
const {
|
||||
|
@ -17,6 +18,8 @@ const {
|
|||
EventTarget,
|
||||
Event,
|
||||
kTrustEvent,
|
||||
initEventTarget,
|
||||
defineEventHandler,
|
||||
} = require('internal/event_target');
|
||||
|
||||
const { now } = require('internal/perf/utils');
|
||||
|
@ -38,13 +41,16 @@ const {
|
|||
const { eventLoopUtilization } = require('internal/perf/event_loop_utilization');
|
||||
const nodeTiming = require('internal/perf/nodetiming');
|
||||
const timerify = require('internal/perf/timerify');
|
||||
const { customInspectSymbol: kInspect } = require('internal/util');
|
||||
const { customInspectSymbol: kInspect, kEnumerableProperty, kEmptyObject } = require('internal/util');
|
||||
const { inspect } = require('util');
|
||||
const { validateInternalField } = require('internal/validators');
|
||||
|
||||
const {
|
||||
getTimeOriginTimestamp
|
||||
} = internalBinding('performance');
|
||||
|
||||
const kPerformanceBrand = Symbol('performance');
|
||||
|
||||
class Performance extends EventTarget {
|
||||
constructor() {
|
||||
throw new ERR_ILLEGAL_CONSTRUCTOR();
|
||||
|
@ -63,121 +69,135 @@ class Performance extends EventTarget {
|
|||
timeOrigin: this.timeOrigin,
|
||||
}, opts)}`;
|
||||
}
|
||||
}
|
||||
|
||||
function toJSON() {
|
||||
return {
|
||||
nodeTiming: this.nodeTiming,
|
||||
timeOrigin: this.timeOrigin,
|
||||
eventLoopUtilization: this.eventLoopUtilization()
|
||||
};
|
||||
}
|
||||
clearMarks(name = undefined) {
|
||||
validateInternalField(this, kPerformanceBrand, 'Performance');
|
||||
if (name !== undefined) {
|
||||
name = `${name}`;
|
||||
}
|
||||
clearMarkTimings(name);
|
||||
clearEntriesFromBuffer('mark', name);
|
||||
}
|
||||
|
||||
function clearMarks(name) {
|
||||
if (name !== undefined) {
|
||||
clearMeasures(name = undefined) {
|
||||
validateInternalField(this, kPerformanceBrand, 'Performance');
|
||||
if (name !== undefined) {
|
||||
name = `${name}`;
|
||||
}
|
||||
clearEntriesFromBuffer('measure', name);
|
||||
}
|
||||
|
||||
clearResourceTimings(name = undefined) {
|
||||
validateInternalField(this, kPerformanceBrand, 'Performance');
|
||||
if (name !== undefined) {
|
||||
name = `${name}`;
|
||||
}
|
||||
clearEntriesFromBuffer('resource', name);
|
||||
}
|
||||
|
||||
getEntries() {
|
||||
validateInternalField(this, kPerformanceBrand, 'Performance');
|
||||
return filterBufferMapByNameAndType();
|
||||
}
|
||||
|
||||
getEntriesByName(name) {
|
||||
validateInternalField(this, kPerformanceBrand, 'Performance');
|
||||
if (arguments.length === 0) {
|
||||
throw new ERR_MISSING_ARGS('name');
|
||||
}
|
||||
name = `${name}`;
|
||||
return filterBufferMapByNameAndType(name, undefined);
|
||||
}
|
||||
clearMarkTimings(name);
|
||||
clearEntriesFromBuffer('mark', name);
|
||||
}
|
||||
|
||||
function clearMeasures(name) {
|
||||
if (name !== undefined) {
|
||||
name = `${name}`;
|
||||
getEntriesByType(type) {
|
||||
validateInternalField(this, kPerformanceBrand, 'Performance');
|
||||
if (arguments.length === 0) {
|
||||
throw new ERR_MISSING_ARGS('type');
|
||||
}
|
||||
type = `${type}`;
|
||||
return filterBufferMapByNameAndType(undefined, type);
|
||||
}
|
||||
clearEntriesFromBuffer('measure', name);
|
||||
}
|
||||
|
||||
function clearResourceTimings(name) {
|
||||
if (name !== undefined) {
|
||||
name = `${name}`;
|
||||
mark(name, options = kEmptyObject) {
|
||||
validateInternalField(this, kPerformanceBrand, 'Performance');
|
||||
if (arguments.length === 0) {
|
||||
throw new ERR_MISSING_ARGS('name');
|
||||
}
|
||||
return mark(name, options);
|
||||
}
|
||||
clearEntriesFromBuffer('resource', name);
|
||||
}
|
||||
|
||||
function getEntries() {
|
||||
return filterBufferMapByNameAndType();
|
||||
}
|
||||
|
||||
function getEntriesByName(name) {
|
||||
if (arguments.length === 0) {
|
||||
throw new ERR_MISSING_ARGS('name');
|
||||
measure(name, startOrMeasureOptions = kEmptyObject, endMark = undefined) {
|
||||
validateInternalField(this, kPerformanceBrand, 'Performance');
|
||||
if (arguments.length === 0) {
|
||||
throw new ERR_MISSING_ARGS('name');
|
||||
}
|
||||
return measure(name, startOrMeasureOptions, endMark);
|
||||
}
|
||||
name = `${name}`;
|
||||
return filterBufferMapByNameAndType(name, undefined);
|
||||
}
|
||||
|
||||
function getEntriesByType(type) {
|
||||
if (arguments.length === 0) {
|
||||
throw new ERR_MISSING_ARGS('type');
|
||||
now() {
|
||||
validateInternalField(this, kPerformanceBrand, 'Performance');
|
||||
return now();
|
||||
}
|
||||
type = `${type}`;
|
||||
return filterBufferMapByNameAndType(undefined, type);
|
||||
}
|
||||
|
||||
class InternalPerformance extends EventTarget {}
|
||||
InternalPerformance.prototype.constructor = Performance.prototype.constructor;
|
||||
ObjectSetPrototypeOf(InternalPerformance.prototype, Performance.prototype);
|
||||
setResourceTimingBufferSize(maxSize) {
|
||||
validateInternalField(this, kPerformanceBrand, 'Performance');
|
||||
if (arguments.length === 0) {
|
||||
throw new ERR_MISSING_ARGS('maxSize');
|
||||
}
|
||||
return setResourceTimingBufferSize(maxSize);
|
||||
}
|
||||
|
||||
get timeOrigin() {
|
||||
validateInternalField(this, kPerformanceBrand, 'Performance');
|
||||
return getTimeOriginTimestamp();
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
validateInternalField(this, kPerformanceBrand, 'Performance');
|
||||
return {
|
||||
nodeTiming: this.nodeTiming,
|
||||
timeOrigin: this.timeOrigin,
|
||||
eventLoopUtilization: this.eventLoopUtilization(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
ObjectDefineProperties(Performance.prototype, {
|
||||
clearMarks: {
|
||||
clearMarks: kEnumerableProperty,
|
||||
clearMeasures: kEnumerableProperty,
|
||||
clearResourceTimings: kEnumerableProperty,
|
||||
getEntries: kEnumerableProperty,
|
||||
getEntriesByName: kEnumerableProperty,
|
||||
getEntriesByType: kEnumerableProperty,
|
||||
mark: kEnumerableProperty,
|
||||
measure: kEnumerableProperty,
|
||||
now: kEnumerableProperty,
|
||||
timeOrigin: kEnumerableProperty,
|
||||
toJSON: kEnumerableProperty,
|
||||
setResourceTimingBufferSize: kEnumerableProperty,
|
||||
[SymbolToStringTag]: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
value: clearMarks,
|
||||
},
|
||||
clearMeasures: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: clearMeasures,
|
||||
},
|
||||
clearResourceTimings: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: clearResourceTimings,
|
||||
value: 'Performance',
|
||||
},
|
||||
|
||||
// Node.js specific extensions.
|
||||
eventLoopUtilization: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
// Node.js specific extensions.
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
value: eventLoopUtilization,
|
||||
},
|
||||
getEntries: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: getEntries,
|
||||
},
|
||||
getEntriesByName: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: getEntriesByName,
|
||||
},
|
||||
getEntriesByType: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: getEntriesByType,
|
||||
},
|
||||
mark: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: mark,
|
||||
},
|
||||
measure: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: measure,
|
||||
},
|
||||
nodeTiming: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
// Node.js specific extensions.
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
value: nodeTiming,
|
||||
},
|
||||
// In the browser, this function is not public. However, it must be used inside fetch
|
||||
|
@ -185,55 +205,30 @@ ObjectDefineProperties(Performance.prototype, {
|
|||
markResourceTiming: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
// Node.js specific extensions.
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
value: markResourceTiming,
|
||||
},
|
||||
now: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: now,
|
||||
},
|
||||
setResourceTimingBufferSize: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: setResourceTimingBufferSize
|
||||
},
|
||||
timerify: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
// Node.js specific extensions.
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
value: timerify,
|
||||
},
|
||||
// This would be updated during pre-execution in case
|
||||
// the process is launched from a snapshot.
|
||||
// TODO(joyeecheung): we may want to warn about access to
|
||||
// this during snapshot building.
|
||||
timeOrigin: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
value: getTimeOriginTimestamp(),
|
||||
},
|
||||
toJSON: {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
value: toJSON,
|
||||
}
|
||||
});
|
||||
defineEventHandler(Performance.prototype, 'resourcetimingbufferfull');
|
||||
|
||||
function refreshTimeOrigin() {
|
||||
ObjectDefineProperty(Performance.prototype, 'timeOrigin', {
|
||||
__proto__: null,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
value: getTimeOriginTimestamp(),
|
||||
});
|
||||
function createPerformance() {
|
||||
return ReflectConstruct(function Performance() {
|
||||
initEventTarget(this);
|
||||
this[kPerformanceBrand] = true;
|
||||
}, [], Performance);
|
||||
}
|
||||
|
||||
const performance = new InternalPerformance();
|
||||
const performance = createPerformance();
|
||||
|
||||
function dispatchBufferFull(type) {
|
||||
const event = new Event(type, {
|
||||
|
@ -246,5 +241,4 @@ setDispatchBufferFull(dispatchBufferFull);
|
|||
module.exports = {
|
||||
Performance,
|
||||
performance,
|
||||
refreshTimeOrigin
|
||||
};
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
'use strict';
|
||||
|
||||
const {
|
||||
ObjectSetPrototypeOf,
|
||||
ObjectDefineProperties,
|
||||
ReflectConstruct,
|
||||
Symbol,
|
||||
} = primordials;
|
||||
|
||||
|
@ -13,15 +14,17 @@ const {
|
|||
|
||||
const {
|
||||
customInspectSymbol: kInspect,
|
||||
kEnumerableProperty,
|
||||
} = require('internal/util');
|
||||
const { validateInternalField } = require('internal/validators');
|
||||
|
||||
const { inspect } = require('util');
|
||||
|
||||
const kName = Symbol('kName');
|
||||
const kType = Symbol('kType');
|
||||
const kStart = Symbol('kStart');
|
||||
const kDuration = Symbol('kDuration');
|
||||
const kDetail = Symbol('kDetail');
|
||||
const kName = Symbol('PerformanceEntry.Name');
|
||||
const kEntryType = Symbol('PerformanceEntry.EntryType');
|
||||
const kStartTime = Symbol('PerformanceEntry.StartTime');
|
||||
const kDuration = Symbol('PerformanceEntry.Duration');
|
||||
const kDetail = Symbol('NodePerformanceEntry.Detail');
|
||||
|
||||
function isPerformanceEntry(obj) {
|
||||
return obj?.[kName] !== undefined;
|
||||
|
@ -32,15 +35,25 @@ class PerformanceEntry {
|
|||
throw new ERR_ILLEGAL_CONSTRUCTOR();
|
||||
}
|
||||
|
||||
get name() { return this[kName]; }
|
||||
get name() {
|
||||
validateInternalField(this, kName, 'PerformanceEntry');
|
||||
return this[kName];
|
||||
}
|
||||
|
||||
get entryType() { return this[kType]; }
|
||||
get entryType() {
|
||||
validateInternalField(this, kEntryType, 'PerformanceEntry');
|
||||
return this[kEntryType];
|
||||
}
|
||||
|
||||
get startTime() { return this[kStart]; }
|
||||
get startTime() {
|
||||
validateInternalField(this, kStartTime, 'PerformanceEntry');
|
||||
return this[kStartTime];
|
||||
}
|
||||
|
||||
get duration() { return this[kDuration]; }
|
||||
|
||||
get detail() { return this[kDetail]; }
|
||||
get duration() {
|
||||
validateInternalField(this, kDuration, 'PerformanceEntry');
|
||||
return this[kDuration];
|
||||
}
|
||||
|
||||
[kInspect](depth, options) {
|
||||
if (depth < 0) return this;
|
||||
|
@ -54,33 +67,69 @@ class PerformanceEntry {
|
|||
}
|
||||
|
||||
toJSON() {
|
||||
validateInternalField(this, kName, 'PerformanceEntry');
|
||||
return {
|
||||
name: this.name,
|
||||
entryType: this.entryType,
|
||||
startTime: this.startTime,
|
||||
duration: this.duration,
|
||||
detail: this.detail,
|
||||
name: this[kName],
|
||||
entryType: this[kEntryType],
|
||||
startTime: this[kStartTime],
|
||||
duration: this[kDuration],
|
||||
};
|
||||
}
|
||||
}
|
||||
ObjectDefineProperties(PerformanceEntry.prototype, {
|
||||
name: kEnumerableProperty,
|
||||
entryType: kEnumerableProperty,
|
||||
startTime: kEnumerableProperty,
|
||||
duration: kEnumerableProperty,
|
||||
toJSON: kEnumerableProperty,
|
||||
});
|
||||
|
||||
function initPerformanceEntry(entry, name, type, start, duration) {
|
||||
entry[kName] = name;
|
||||
entry[kEntryType] = type;
|
||||
entry[kStartTime] = start;
|
||||
entry[kDuration] = duration;
|
||||
}
|
||||
|
||||
function createPerformanceEntry(name, type, start, duration) {
|
||||
return ReflectConstruct(function PerformanceEntry() {
|
||||
initPerformanceEntry(this, name, type, start, duration);
|
||||
}, [], PerformanceEntry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Node.js specific extension to PerformanceEntry.
|
||||
*/
|
||||
class PerformanceNodeEntry extends PerformanceEntry {
|
||||
get detail() {
|
||||
validateInternalField(this, kDetail, 'NodePerformanceEntry');
|
||||
return this[kDetail];
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
validateInternalField(this, kName, 'PerformanceEntry');
|
||||
return {
|
||||
name: this[kName],
|
||||
entryType: this[kEntryType],
|
||||
startTime: this[kStartTime],
|
||||
duration: this[kDuration],
|
||||
detail: this[kDetail],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class InternalPerformanceEntry {
|
||||
constructor(name, type, start, duration, detail) {
|
||||
this[kName] = name;
|
||||
this[kType] = type;
|
||||
this[kStart] = start;
|
||||
this[kDuration] = duration;
|
||||
function createPerformanceNodeEntry(name, type, start, duration, detail) {
|
||||
return ReflectConstruct(function PerformanceNodeEntry() {
|
||||
initPerformanceEntry(this, name, type, start, duration);
|
||||
this[kDetail] = detail;
|
||||
}
|
||||
}, [], PerformanceNodeEntry);
|
||||
}
|
||||
|
||||
InternalPerformanceEntry.prototype.constructor = PerformanceEntry;
|
||||
ObjectSetPrototypeOf(
|
||||
InternalPerformanceEntry.prototype,
|
||||
PerformanceEntry.prototype);
|
||||
|
||||
module.exports = {
|
||||
InternalPerformanceEntry,
|
||||
initPerformanceEntry,
|
||||
createPerformanceEntry,
|
||||
PerformanceEntry,
|
||||
isPerformanceEntry,
|
||||
PerformanceNodeEntry,
|
||||
createPerformanceNodeEntry,
|
||||
};
|
||||
|
|
|
@ -1,33 +1,32 @@
|
|||
'use strict';
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming
|
||||
|
||||
const { InternalPerformanceEntry } = require('internal/perf/performance_entry');
|
||||
const { SymbolToStringTag } = primordials;
|
||||
const {
|
||||
ObjectDefineProperties,
|
||||
ObjectSetPrototypeOf,
|
||||
ReflectConstruct,
|
||||
Symbol,
|
||||
SymbolToStringTag,
|
||||
} = primordials;
|
||||
const { initPerformanceEntry, PerformanceEntry } = require('internal/perf/performance_entry');
|
||||
const assert = require('internal/assert');
|
||||
const { enqueue, bufferResourceTiming } = require('internal/perf/observe');
|
||||
const { Symbol, ObjectSetPrototypeOf } = primordials;
|
||||
const {
|
||||
codes: {
|
||||
ERR_ILLEGAL_CONSTRUCTOR,
|
||||
}
|
||||
} = require('internal/errors');
|
||||
const { validateInternalField } = require('internal/validators');
|
||||
const { kEnumerableProperty } = require('internal/util');
|
||||
|
||||
const kCacheMode = Symbol('kCacheMode');
|
||||
const kRequestedUrl = Symbol('kRequestedUrl');
|
||||
const kTimingInfo = Symbol('kTimingInfo');
|
||||
const kInitiatorType = Symbol('kInitiatorType');
|
||||
|
||||
const {
|
||||
codes: {
|
||||
ERR_ILLEGAL_CONSTRUCTOR,
|
||||
}
|
||||
} = require('internal/errors');
|
||||
|
||||
class InternalPerformanceResourceTiming extends InternalPerformanceEntry {
|
||||
constructor(requestedUrl, initiatorType, timingInfo, cacheMode = '') {
|
||||
super(requestedUrl, 'resource');
|
||||
this[kInitiatorType] = initiatorType;
|
||||
this[kRequestedUrl] = requestedUrl;
|
||||
// https://fetch.spec.whatwg.org/#fetch-timing-info
|
||||
// This class is using timingInfo assuming it's already validated.
|
||||
// The spec doesn't say to validate it in the class construction.
|
||||
this[kTimingInfo] = timingInfo;
|
||||
this[kCacheMode] = cacheMode;
|
||||
class PerformanceResourceTiming extends PerformanceEntry {
|
||||
constructor() {
|
||||
throw new ERR_ILLEGAL_CONSTRUCTOR();
|
||||
}
|
||||
|
||||
get [SymbolToStringTag]() {
|
||||
|
@ -35,84 +34,104 @@ class InternalPerformanceResourceTiming extends InternalPerformanceEntry {
|
|||
}
|
||||
|
||||
get name() {
|
||||
validateInternalField(this, kRequestedUrl, 'PerformanceResourceTiming');
|
||||
return this[kRequestedUrl];
|
||||
}
|
||||
|
||||
get startTime() {
|
||||
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
|
||||
return this[kTimingInfo].startTime;
|
||||
}
|
||||
|
||||
get duration() {
|
||||
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
|
||||
return this[kTimingInfo].endTime - this[kTimingInfo].startTime;
|
||||
}
|
||||
|
||||
get initiatorType() {
|
||||
validateInternalField(this, kInitiatorType, 'PerformanceResourceTiming');
|
||||
return this[kInitiatorType];
|
||||
}
|
||||
|
||||
get workerStart() {
|
||||
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
|
||||
return this[kTimingInfo].finalServiceWorkerStartTime;
|
||||
}
|
||||
|
||||
get redirectStart() {
|
||||
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
|
||||
return this[kTimingInfo].redirectStartTime;
|
||||
}
|
||||
|
||||
get redirectEnd() {
|
||||
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
|
||||
return this[kTimingInfo].redirectEndTime;
|
||||
}
|
||||
|
||||
get fetchStart() {
|
||||
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
|
||||
return this[kTimingInfo].postRedirectStartTime;
|
||||
}
|
||||
|
||||
get domainLookupStart() {
|
||||
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
|
||||
return this[kTimingInfo].finalConnectionTimingInfo?.domainLookupStartTime;
|
||||
}
|
||||
|
||||
get domainLookupEnd() {
|
||||
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
|
||||
return this[kTimingInfo].finalConnectionTimingInfo?.domainLookupEndTime;
|
||||
}
|
||||
|
||||
get connectStart() {
|
||||
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
|
||||
return this[kTimingInfo].finalConnectionTimingInfo?.connectionStartTime;
|
||||
}
|
||||
|
||||
get connectEnd() {
|
||||
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
|
||||
return this[kTimingInfo].finalConnectionTimingInfo?.connectionEndTime;
|
||||
}
|
||||
|
||||
get secureConnectionStart() {
|
||||
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
|
||||
return this[kTimingInfo]
|
||||
.finalConnectionTimingInfo?.secureConnectionStartTime;
|
||||
}
|
||||
|
||||
get nextHopProtocol() {
|
||||
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
|
||||
return this[kTimingInfo]
|
||||
.finalConnectionTimingInfo?.ALPNNegotiatedProtocol;
|
||||
}
|
||||
|
||||
get requestStart() {
|
||||
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
|
||||
return this[kTimingInfo].finalNetworkRequestStartTime;
|
||||
}
|
||||
|
||||
get responseStart() {
|
||||
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
|
||||
return this[kTimingInfo].finalNetworkResponseStartTime;
|
||||
}
|
||||
|
||||
get responseEnd() {
|
||||
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
|
||||
return this[kTimingInfo].endTime;
|
||||
}
|
||||
|
||||
get encodedBodySize() {
|
||||
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
|
||||
return this[kTimingInfo].encodedBodySize;
|
||||
}
|
||||
|
||||
get decodedBodySize() {
|
||||
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
|
||||
return this[kTimingInfo].decodedBodySize;
|
||||
}
|
||||
|
||||
get transferSize() {
|
||||
validateInternalField(this, kTimingInfo, 'PerformanceResourceTiming');
|
||||
if (this[kCacheMode] === 'local') return 0;
|
||||
if (this[kCacheMode] === 'validated') return 300;
|
||||
|
||||
|
@ -120,6 +139,7 @@ class InternalPerformanceResourceTiming extends InternalPerformanceEntry {
|
|||
}
|
||||
|
||||
toJSON() {
|
||||
validateInternalField(this, kInitiatorType, 'PerformanceResourceTiming');
|
||||
return {
|
||||
name: this.name,
|
||||
entryType: this.entryType,
|
||||
|
@ -146,10 +166,38 @@ class InternalPerformanceResourceTiming extends InternalPerformanceEntry {
|
|||
}
|
||||
}
|
||||
|
||||
class PerformanceResourceTiming extends InternalPerformanceResourceTiming {
|
||||
constructor() {
|
||||
throw new ERR_ILLEGAL_CONSTRUCTOR();
|
||||
}
|
||||
ObjectDefineProperties(PerformanceResourceTiming.prototype, {
|
||||
initiatorType: kEnumerableProperty,
|
||||
nextHopProtocol: kEnumerableProperty,
|
||||
workerStart: kEnumerableProperty,
|
||||
redirectStart: kEnumerableProperty,
|
||||
redirectEnd: kEnumerableProperty,
|
||||
fetchStart: kEnumerableProperty,
|
||||
domainLookupStart: kEnumerableProperty,
|
||||
domainLookupEnd: kEnumerableProperty,
|
||||
connectStart: kEnumerableProperty,
|
||||
connectEnd: kEnumerableProperty,
|
||||
secureConnectionStart: kEnumerableProperty,
|
||||
requestStart: kEnumerableProperty,
|
||||
responseStart: kEnumerableProperty,
|
||||
responseEnd: kEnumerableProperty,
|
||||
transferSize: kEnumerableProperty,
|
||||
encodedBodySize: kEnumerableProperty,
|
||||
decodedBodySize: kEnumerableProperty,
|
||||
toJSON: kEnumerableProperty,
|
||||
});
|
||||
|
||||
function createPerformanceResourceTiming(requestedUrl, initiatorType, timingInfo, cacheMode = '') {
|
||||
return ReflectConstruct(function PerformanceResourceTiming() {
|
||||
initPerformanceEntry(this, requestedUrl, 'resource');
|
||||
this[kInitiatorType] = initiatorType;
|
||||
this[kRequestedUrl] = requestedUrl;
|
||||
// https://fetch.spec.whatwg.org/#fetch-timing-info
|
||||
// This class is using timingInfo assuming it's already validated.
|
||||
// The spec doesn't say to validate it in the class construction.
|
||||
this[kTimingInfo] = timingInfo;
|
||||
this[kCacheMode] = cacheMode;
|
||||
}, [], PerformanceResourceTiming);
|
||||
}
|
||||
|
||||
// https://w3c.github.io/resource-timing/#dfn-mark-resource-timing
|
||||
|
@ -165,7 +213,7 @@ function markResourceTiming(
|
|||
cacheMode === '' || cacheMode === 'local',
|
||||
'cache must be an empty string or \'local\'',
|
||||
);
|
||||
const resource = new InternalPerformanceResourceTiming(
|
||||
const resource = createPerformanceResourceTiming(
|
||||
requestedUrl,
|
||||
initiatorType,
|
||||
timingInfo,
|
||||
|
|
|
@ -8,7 +8,7 @@ const {
|
|||
ReflectConstruct,
|
||||
} = primordials;
|
||||
|
||||
const { InternalPerformanceEntry } = require('internal/perf/performance_entry');
|
||||
const { createPerformanceNodeEntry } = require('internal/perf/performance_entry');
|
||||
const { now } = require('internal/perf/utils');
|
||||
|
||||
const {
|
||||
|
@ -39,7 +39,7 @@ function processComplete(name, start, args, histogram) {
|
|||
if (histogram !== undefined)
|
||||
histogram.record(MathCeil(duration * 1e6));
|
||||
const entry =
|
||||
new InternalPerformanceEntry(
|
||||
createPerformanceNodeEntry(
|
||||
name,
|
||||
'function',
|
||||
start,
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
'use strict';
|
||||
|
||||
const {
|
||||
ObjectDefineProperties,
|
||||
ObjectSetPrototypeOf,
|
||||
SafeMap,
|
||||
SafeSet,
|
||||
SafeArrayIterator,
|
||||
Symbol,
|
||||
SymbolToStringTag,
|
||||
ReflectConstruct,
|
||||
} = primordials;
|
||||
|
||||
const { InternalPerformanceEntry } = require('internal/perf/performance_entry');
|
||||
const { initPerformanceEntry, PerformanceEntry } = require('internal/perf/performance_entry');
|
||||
const { now } = require('internal/perf/utils');
|
||||
const { enqueue, bufferUserTiming } = require('internal/perf/observe');
|
||||
const nodeTiming = require('internal/perf/nodetiming');
|
||||
|
@ -16,11 +20,14 @@ const {
|
|||
validateNumber,
|
||||
validateObject,
|
||||
validateString,
|
||||
validateInternalField,
|
||||
} = require('internal/validators');
|
||||
|
||||
const {
|
||||
codes: {
|
||||
ERR_ILLEGAL_CONSTRUCTOR,
|
||||
ERR_INVALID_ARG_VALUE,
|
||||
ERR_MISSING_ARGS,
|
||||
ERR_PERFORMANCE_INVALID_TIMESTAMP,
|
||||
ERR_PERFORMANCE_MEASURE_INVALID_OPTIONS,
|
||||
},
|
||||
|
@ -30,8 +37,11 @@ const { structuredClone } = require('internal/structured_clone');
|
|||
const {
|
||||
kEmptyObject,
|
||||
lazyDOMException,
|
||||
kEnumerableProperty,
|
||||
} = require('internal/util');
|
||||
|
||||
const kDetail = Symbol('kDetail');
|
||||
|
||||
const markTimings = new SafeMap();
|
||||
|
||||
const nodeTimingReadOnlyAttributes = new SafeSet(new SafeArrayIterator([
|
||||
|
@ -59,12 +69,15 @@ function getMark(name) {
|
|||
return ts;
|
||||
}
|
||||
|
||||
class PerformanceMark extends InternalPerformanceEntry {
|
||||
constructor(name, options) {
|
||||
class PerformanceMark {
|
||||
constructor(name, options = kEmptyObject) {
|
||||
if (arguments.length === 0) {
|
||||
throw new ERR_MISSING_ARGS('name');
|
||||
}
|
||||
name = `${name}`;
|
||||
options ??= kEmptyObject;
|
||||
if (nodeTimingReadOnlyAttributes.has(name))
|
||||
throw new ERR_INVALID_ARG_VALUE('name', name);
|
||||
options ??= kEmptyObject;
|
||||
validateObject(options, 'options');
|
||||
const startTime = options.startTime ?? now();
|
||||
validateNumber(startTime, 'startTime');
|
||||
|
@ -76,25 +89,61 @@ class PerformanceMark extends InternalPerformanceEntry {
|
|||
detail = detail != null ?
|
||||
structuredClone(detail) :
|
||||
null;
|
||||
super(name, 'mark', startTime, 0, detail);
|
||||
initPerformanceEntry(this, name, 'mark', startTime, 0);
|
||||
this[kDetail] = detail;
|
||||
}
|
||||
|
||||
get detail() {
|
||||
validateInternalField(this, kDetail, 'PerformanceMark');
|
||||
return this[kDetail];
|
||||
}
|
||||
|
||||
get [SymbolToStringTag]() {
|
||||
return 'PerformanceMark';
|
||||
}
|
||||
}
|
||||
|
||||
class PerformanceMeasure extends InternalPerformanceEntry {
|
||||
constructor(name, start, duration, detail) {
|
||||
super(name, 'measure', start, duration, detail);
|
||||
toJSON() {
|
||||
return {
|
||||
name: this.name,
|
||||
entryType: this.entryType,
|
||||
startTime: this.startTime,
|
||||
duration: this.duration,
|
||||
detail: this[kDetail],
|
||||
};
|
||||
}
|
||||
}
|
||||
ObjectSetPrototypeOf(PerformanceMark, PerformanceEntry);
|
||||
ObjectSetPrototypeOf(PerformanceMark.prototype, PerformanceEntry.prototype);
|
||||
ObjectDefineProperties(PerformanceMark.prototype, {
|
||||
detail: kEnumerableProperty,
|
||||
});
|
||||
|
||||
class PerformanceMeasure extends PerformanceEntry {
|
||||
constructor() {
|
||||
throw new ERR_ILLEGAL_CONSTRUCTOR();
|
||||
}
|
||||
|
||||
get detail() {
|
||||
validateInternalField(this, kDetail, 'PerformanceMeasure');
|
||||
return this[kDetail];
|
||||
}
|
||||
|
||||
get [SymbolToStringTag]() {
|
||||
return 'PerformanceMeasure';
|
||||
}
|
||||
}
|
||||
ObjectDefineProperties(PerformanceMeasure.prototype, {
|
||||
detail: kEnumerableProperty,
|
||||
});
|
||||
|
||||
function mark(name, options = kEmptyObject) {
|
||||
function createPerformanceMeasure(name, start, duration, detail) {
|
||||
return ReflectConstruct(function PerformanceMeasure() {
|
||||
initPerformanceEntry(this, name, 'measure', start, duration);
|
||||
this[kDetail] = detail;
|
||||
}, [], PerformanceMeasure);
|
||||
}
|
||||
|
||||
function mark(name, options) {
|
||||
const mark = new PerformanceMark(name, options);
|
||||
enqueue(mark);
|
||||
bufferUserTiming(mark);
|
||||
|
@ -160,7 +209,7 @@ function measure(name, startOrMeasureOptions, endMark) {
|
|||
} = calculateStartDuration(startOrMeasureOptions, endMark);
|
||||
let detail = startOrMeasureOptions?.detail;
|
||||
detail = detail != null ? structuredClone(detail) : null;
|
||||
const measure = new PerformanceMeasure(name, start, duration, detail);
|
||||
const measure = createPerformanceMeasure(name, start, duration, detail);
|
||||
enqueue(measure);
|
||||
bufferUserTiming(measure);
|
||||
return measure;
|
||||
|
|
|
@ -372,7 +372,6 @@ function setupTraceCategoryState() {
|
|||
}
|
||||
|
||||
function setupPerfHooks() {
|
||||
require('internal/perf/performance').refreshTimeOrigin();
|
||||
require('internal/perf/utils').refreshTimeOrigin();
|
||||
}
|
||||
|
||||
|
|
|
@ -298,7 +298,7 @@ class WPTRunner {
|
|||
this.resource = new ResourceLoader(path);
|
||||
|
||||
this.flags = [];
|
||||
this.dummyGlobalThisScript = null;
|
||||
this.globalThisInitScripts = [];
|
||||
this.initScript = null;
|
||||
|
||||
this.status = new StatusLoader(path);
|
||||
|
@ -340,17 +340,17 @@ class WPTRunner {
|
|||
}
|
||||
|
||||
get fullInitScript() {
|
||||
if (this.initScript === null && this.dummyGlobalThisScript === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.initScript === null) {
|
||||
return this.dummyGlobalThisScript;
|
||||
} else if (this.dummyGlobalThisScript === null) {
|
||||
if (this.globalThisInitScripts.length === null) {
|
||||
return this.initScript;
|
||||
}
|
||||
|
||||
return `${this.dummyGlobalThisScript}\n\n//===\n${this.initScript}`;
|
||||
const globalThisInitScript = this.globalThisInitScripts.join('\n\n//===\n');
|
||||
|
||||
if (this.initScript === null) {
|
||||
return globalThisInitScript;
|
||||
}
|
||||
|
||||
return `${globalThisInitScript}\n\n//===\n${this.initScript}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -361,8 +361,9 @@ class WPTRunner {
|
|||
pretendGlobalThisAs(name) {
|
||||
switch (name) {
|
||||
case 'Window': {
|
||||
this.dummyGlobalThisScript =
|
||||
'global.Window = Object.getPrototypeOf(globalThis).constructor;';
|
||||
this.globalThisInitScripts.push(
|
||||
`global.Window = Object.getPrototypeOf(globalThis).constructor;
|
||||
self.GLOBAL.isWorker = () => false;`);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -376,6 +377,36 @@ class WPTRunner {
|
|||
}
|
||||
}
|
||||
|
||||
brandCheckGlobalScopeAttribute(name) {
|
||||
// TODO(legendecas): idlharness GlobalScope attribute receiver validation.
|
||||
const script = `
|
||||
const desc = Object.getOwnPropertyDescriptor(globalThis, '${name}');
|
||||
function getter() {
|
||||
// Mimic GlobalScope instance brand check.
|
||||
if (this !== globalThis) {
|
||||
throw new TypeError('Illegal invocation');
|
||||
}
|
||||
return desc.get();
|
||||
}
|
||||
Object.defineProperty(getter, 'name', { value: 'get ${name}' });
|
||||
|
||||
function setter(value) {
|
||||
// Mimic GlobalScope instance brand check.
|
||||
if (this !== globalThis) {
|
||||
throw new TypeError('Illegal invocation');
|
||||
}
|
||||
desc.set(value);
|
||||
}
|
||||
Object.defineProperty(setter, 'name', { value: 'set ${name}' });
|
||||
|
||||
Object.defineProperty(globalThis, '${name}', {
|
||||
get: getter,
|
||||
set: setter,
|
||||
});
|
||||
`;
|
||||
this.globalThisInitScripts.push(script);
|
||||
}
|
||||
|
||||
// TODO(joyeecheung): work with the upstream to port more tests in .html
|
||||
// to .js.
|
||||
async runJsTests() {
|
||||
|
|
|
@ -21,8 +21,9 @@ Last update:
|
|||
- html/webappapis/microtask-queuing: https://github.com/web-platform-tests/wpt/tree/2c5c3c4c27/html/webappapis/microtask-queuing
|
||||
- html/webappapis/structured-clone: https://github.com/web-platform-tests/wpt/tree/47d3fb280c/html/webappapis/structured-clone
|
||||
- html/webappapis/timers: https://github.com/web-platform-tests/wpt/tree/5873f2d8f1/html/webappapis/timers
|
||||
- interfaces: https://github.com/web-platform-tests/wpt/tree/fc086c82d5/interfaces
|
||||
- interfaces: https://github.com/web-platform-tests/wpt/tree/df731dab88/interfaces
|
||||
- performance-timeline: https://github.com/web-platform-tests/wpt/tree/17ebc3aea0/performance-timeline
|
||||
- resource-timing: https://github.com/web-platform-tests/wpt/tree/22d38586d0/resource-timing
|
||||
- resources: https://github.com/web-platform-tests/wpt/tree/c5b428f15a/resources
|
||||
- streams: https://github.com/web-platform-tests/wpt/tree/9e5ef42bd3/streams
|
||||
- url: https://github.com/web-platform-tests/wpt/tree/0e5b126cd0/url
|
||||
|
|
|
@ -11,6 +11,7 @@ partial interface mixin WindowOrWorkerGlobalScope {
|
|||
interface Crypto {
|
||||
[SecureContext] readonly attribute SubtleCrypto subtle;
|
||||
ArrayBufferView getRandomValues(ArrayBufferView array);
|
||||
[SecureContext] DOMString randomUUID();
|
||||
};
|
||||
|
||||
typedef (object or DOMString) AlgorithmIdentifier;
|
||||
|
@ -29,7 +30,7 @@ enum KeyType { "public", "private", "secret" };
|
|||
|
||||
enum KeyUsage { "encrypt", "decrypt", "sign", "verify", "deriveKey", "deriveBits", "wrapKey", "unwrapKey" };
|
||||
|
||||
[SecureContext,Exposed=(Window,Worker)]
|
||||
[SecureContext,Exposed=(Window,Worker),Serializable]
|
||||
interface CryptoKey {
|
||||
readonly attribute KeyType type;
|
||||
readonly attribute boolean extractable;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// (https://github.com/w3c/webref)
|
||||
// Source: Console Standard (https://console.spec.whatwg.org/)
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
[Exposed=*]
|
||||
namespace console { // but see namespace object requirements below
|
||||
// Logging
|
||||
undefined assert(optional boolean condition = false, any... data);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// (https://github.com/w3c/webref)
|
||||
// Source: DOM Standard (https://dom.spec.whatwg.org/)
|
||||
|
||||
[Exposed=(Window,Worker,AudioWorklet)]
|
||||
[Exposed=*]
|
||||
interface Event {
|
||||
constructor(DOMString type, optional EventInit eventInitDict = {});
|
||||
|
||||
|
@ -46,7 +46,7 @@ partial interface Window {
|
|||
[Replaceable] readonly attribute (Event or undefined) event; // legacy
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
[Exposed=*]
|
||||
interface CustomEvent : Event {
|
||||
constructor(DOMString type, optional CustomEventInit eventInitDict = {});
|
||||
|
||||
|
@ -59,7 +59,7 @@ dictionary CustomEventInit : EventInit {
|
|||
any detail = null;
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker,AudioWorklet)]
|
||||
[Exposed=*]
|
||||
interface EventTarget {
|
||||
constructor();
|
||||
|
||||
|
@ -77,25 +77,28 @@ dictionary EventListenerOptions {
|
|||
};
|
||||
|
||||
dictionary AddEventListenerOptions : EventListenerOptions {
|
||||
boolean passive = false;
|
||||
boolean passive;
|
||||
boolean once = false;
|
||||
AbortSignal signal;
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
[Exposed=*]
|
||||
interface AbortController {
|
||||
constructor();
|
||||
|
||||
[SameObject] readonly attribute AbortSignal signal;
|
||||
|
||||
undefined abort();
|
||||
undefined abort(optional any reason);
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
[Exposed=*]
|
||||
interface AbortSignal : EventTarget {
|
||||
[NewObject] static AbortSignal abort();
|
||||
[NewObject] static AbortSignal abort(optional any reason);
|
||||
[Exposed=(Window,Worker), NewObject] static AbortSignal timeout([EnforceRange] unsigned long long milliseconds);
|
||||
|
||||
readonly attribute boolean aborted;
|
||||
readonly attribute any reason;
|
||||
undefined throwIfAborted();
|
||||
|
||||
attribute EventHandler onabort;
|
||||
};
|
||||
|
|
|
@ -18,7 +18,7 @@ dictionary TextDecodeOptions {
|
|||
boolean stream = false;
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
[Exposed=*]
|
||||
interface TextDecoder {
|
||||
constructor(optional DOMString label = "utf-8", optional TextDecoderOptions options = {});
|
||||
|
||||
|
@ -35,7 +35,7 @@ dictionary TextEncoderEncodeIntoResult {
|
|||
unsigned long long written;
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
[Exposed=*]
|
||||
interface TextEncoder {
|
||||
constructor();
|
||||
|
||||
|
@ -44,14 +44,14 @@ interface TextEncoder {
|
|||
};
|
||||
TextEncoder includes TextEncoderCommon;
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
[Exposed=*]
|
||||
interface TextDecoderStream {
|
||||
constructor(optional DOMString label = "utf-8", optional TextDecoderOptions options = {});
|
||||
};
|
||||
TextDecoderStream includes TextDecoderCommon;
|
||||
TextDecoderStream includes GenericTransformStream;
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
[Exposed=*]
|
||||
interface TextEncoderStream {
|
||||
constructor();
|
||||
};
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
|
||||
typedef double DOMHighResTimeStamp;
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
typedef unsigned long long EpochTimeStamp;
|
||||
|
||||
[Exposed=*]
|
||||
interface Performance : EventTarget {
|
||||
DOMHighResTimeStamp now();
|
||||
readonly attribute DOMHighResTimeStamp timeOrigin;
|
||||
|
|
|
@ -43,6 +43,7 @@ interface DOMStringList {
|
|||
};
|
||||
|
||||
enum DocumentReadyState { "loading", "interactive", "complete" };
|
||||
enum DocumentVisibilityState { "visible", "hidden" };
|
||||
typedef (HTMLScriptElement or SVGScriptElement) HTMLOrSVGScriptElement;
|
||||
|
||||
[LegacyOverrideBuiltIns]
|
||||
|
@ -87,9 +88,12 @@ partial interface Document {
|
|||
boolean queryCommandState(DOMString commandId);
|
||||
boolean queryCommandSupported(DOMString commandId);
|
||||
DOMString queryCommandValue(DOMString commandId);
|
||||
readonly attribute boolean hidden;
|
||||
readonly attribute DocumentVisibilityState visibilityState;
|
||||
|
||||
// special event handler IDL attributes that only apply to Document objects
|
||||
[LegacyLenientThis] attribute EventHandler onreadystatechange;
|
||||
attribute EventHandler onvisibilitychange;
|
||||
|
||||
// also has obsolete members
|
||||
};
|
||||
|
@ -111,7 +115,8 @@ interface HTMLElement : Element {
|
|||
[CEReactions] attribute DOMString dir;
|
||||
|
||||
// user interaction
|
||||
[CEReactions] attribute boolean hidden;
|
||||
[CEReactions] attribute (boolean or unrestricted double or DOMString)? hidden;
|
||||
[CEReactions] attribute boolean inert;
|
||||
undefined click();
|
||||
[CEReactions] attribute DOMString accessKey;
|
||||
readonly attribute DOMString accessKeyLabel;
|
||||
|
@ -187,7 +192,7 @@ interface HTMLLinkElement : HTMLElement {
|
|||
[CEReactions] attribute USVString href;
|
||||
[CEReactions] attribute DOMString? crossOrigin;
|
||||
[CEReactions] attribute DOMString rel;
|
||||
[CEReactions] attribute DOMString as; // (default "")
|
||||
[CEReactions] attribute DOMString as;
|
||||
[SameObject, PutForwards=value] readonly attribute DOMTokenList relList;
|
||||
[CEReactions] attribute DOMString media;
|
||||
[CEReactions] attribute DOMString integrity;
|
||||
|
@ -197,6 +202,7 @@ interface HTMLLinkElement : HTMLElement {
|
|||
[CEReactions] attribute USVString imageSrcset;
|
||||
[CEReactions] attribute DOMString imageSizes;
|
||||
[CEReactions] attribute DOMString referrerPolicy;
|
||||
[SameObject, PutForwards=value] readonly attribute DOMTokenList blocking;
|
||||
[CEReactions] attribute boolean disabled;
|
||||
|
||||
// also has obsolete members
|
||||
|
@ -210,6 +216,7 @@ interface HTMLMetaElement : HTMLElement {
|
|||
[CEReactions] attribute DOMString name;
|
||||
[CEReactions] attribute DOMString httpEquiv;
|
||||
[CEReactions] attribute DOMString content;
|
||||
[CEReactions] attribute DOMString media;
|
||||
|
||||
// also has obsolete members
|
||||
};
|
||||
|
@ -218,7 +225,9 @@ interface HTMLMetaElement : HTMLElement {
|
|||
interface HTMLStyleElement : HTMLElement {
|
||||
[HTMLConstructor] constructor();
|
||||
|
||||
attribute boolean disabled;
|
||||
[CEReactions] attribute DOMString media;
|
||||
[SameObject, PutForwards=value] readonly attribute DOMTokenList blocking;
|
||||
|
||||
// also has obsolete members
|
||||
};
|
||||
|
@ -487,16 +496,6 @@ interface HTMLObjectElement : HTMLElement {
|
|||
// also has obsolete members
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface HTMLParamElement : HTMLElement {
|
||||
[HTMLConstructor] constructor();
|
||||
|
||||
[CEReactions] attribute DOMString name;
|
||||
[CEReactions] attribute DOMString value;
|
||||
|
||||
// also has obsolete members
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface HTMLVideoElement : HTMLMediaElement {
|
||||
[HTMLConstructor] constructor();
|
||||
|
@ -928,6 +927,8 @@ interface HTMLInputElement : HTMLElement {
|
|||
undefined setRangeText(DOMString replacement, unsigned long start, unsigned long end, optional SelectionMode selectionMode = "preserve");
|
||||
undefined setSelectionRange(unsigned long start, unsigned long end, optional DOMString direction);
|
||||
|
||||
undefined showPicker();
|
||||
|
||||
// also has obsolete members
|
||||
};
|
||||
|
||||
|
@ -972,7 +973,7 @@ interface HTMLSelectElement : HTMLElement {
|
|||
|
||||
[SameObject] readonly attribute HTMLOptionsCollection options;
|
||||
[CEReactions] attribute unsigned long length;
|
||||
getter Element? item(unsigned long index);
|
||||
getter HTMLOptionElement? item(unsigned long index);
|
||||
HTMLOptionElement? namedItem(DOMString name);
|
||||
[CEReactions] undefined add((HTMLOptionElement or HTMLOptGroupElement) element, optional (HTMLElement or long)? before = null);
|
||||
[CEReactions] undefined remove(); // ChildNode overload
|
||||
|
@ -1214,6 +1215,9 @@ interface HTMLScriptElement : HTMLElement {
|
|||
[CEReactions] attribute DOMString text;
|
||||
[CEReactions] attribute DOMString integrity;
|
||||
[CEReactions] attribute DOMString referrerPolicy;
|
||||
[SameObject, PutForwards=value] readonly attribute DOMTokenList blocking;
|
||||
|
||||
static boolean supports(DOMString type);
|
||||
|
||||
// also has obsolete members
|
||||
};
|
||||
|
@ -1239,7 +1243,7 @@ dictionary AssignedNodesOptions {
|
|||
boolean flatten = false;
|
||||
};
|
||||
|
||||
typedef (CanvasRenderingContext2D or ImageBitmapRenderingContext or WebGLRenderingContext or WebGL2RenderingContext) RenderingContext;
|
||||
typedef (CanvasRenderingContext2D or ImageBitmapRenderingContext or WebGLRenderingContext or WebGL2RenderingContext or GPUCanvasContext) RenderingContext;
|
||||
|
||||
[Exposed=Window]
|
||||
interface HTMLCanvasElement : HTMLElement {
|
||||
|
@ -1264,7 +1268,8 @@ typedef (HTMLOrSVGImageElement or
|
|||
HTMLVideoElement or
|
||||
HTMLCanvasElement or
|
||||
ImageBitmap or
|
||||
OffscreenCanvas) CanvasImageSource;
|
||||
OffscreenCanvas or
|
||||
VideoFrame) CanvasImageSource;
|
||||
|
||||
enum PredefinedColorSpace { "srgb", "display-p3" };
|
||||
|
||||
|
@ -1274,6 +1279,7 @@ dictionary CanvasRenderingContext2DSettings {
|
|||
boolean alpha = true;
|
||||
boolean desynchronized = false;
|
||||
PredefinedColorSpace colorSpace = "srgb";
|
||||
boolean willReadFrequently = false;
|
||||
};
|
||||
|
||||
enum ImageSmoothingQuality { "low", "medium", "high" };
|
||||
|
@ -1307,6 +1313,7 @@ interface mixin CanvasState {
|
|||
undefined save(); // push state on state stack
|
||||
undefined restore(); // pop state stack and restore state
|
||||
undefined reset(); // reset the rendering context to its default state
|
||||
boolean isContextLost(); // return whether context is lost
|
||||
};
|
||||
|
||||
interface mixin CanvasTransform {
|
||||
|
@ -1326,7 +1333,7 @@ interface mixin CanvasTransform {
|
|||
interface mixin CanvasCompositing {
|
||||
// compositing
|
||||
attribute unrestricted double globalAlpha; // (default 1.0)
|
||||
attribute DOMString globalCompositeOperation; // (default source-over)
|
||||
attribute DOMString globalCompositeOperation; // (default "source-over")
|
||||
};
|
||||
|
||||
interface mixin CanvasImageSmoothing {
|
||||
|
@ -1357,7 +1364,14 @@ interface mixin CanvasShadowStyles {
|
|||
|
||||
interface mixin CanvasFilters {
|
||||
// filters
|
||||
attribute DOMString filter; // (default "none")
|
||||
attribute (DOMString or CanvasFilter) filter; // (default "none")
|
||||
};
|
||||
|
||||
typedef record<DOMString, any> CanvasFilterInput;
|
||||
|
||||
[Exposed=(Window,Worker,PaintWorklet)]
|
||||
interface CanvasFilter {
|
||||
constructor(optional (CanvasFilterInput or sequence<CanvasFilterInput>) filters);
|
||||
};
|
||||
|
||||
interface mixin CanvasRect {
|
||||
|
@ -1441,12 +1455,12 @@ interface mixin CanvasTextDrawingStyles {
|
|||
attribute CanvasTextAlign textAlign; // (default: "start")
|
||||
attribute CanvasTextBaseline textBaseline; // (default: "alphabetic")
|
||||
attribute CanvasDirection direction; // (default: "inherit")
|
||||
attribute double textLetterSpacing; // (default: 0)
|
||||
attribute double textWordSpacing; // (default: 0)
|
||||
attribute DOMString letterSpacing; // (default: "0px")
|
||||
attribute CanvasFontKerning fontKerning; // (default: "auto")
|
||||
attribute CanvasFontStretch fontStretch; // (default: "normal")
|
||||
attribute CanvasFontVariantCaps fontVariantCaps; // (default: "normal")
|
||||
attribute CanvasTextRendering textRendering; // (default: "normal")
|
||||
attribute CanvasTextRendering textRendering; // (default: "auto")
|
||||
attribute DOMString wordSpacing; // (default: "0px")
|
||||
};
|
||||
|
||||
interface mixin CanvasPath {
|
||||
|
@ -1458,6 +1472,7 @@ interface mixin CanvasPath {
|
|||
undefined bezierCurveTo(unrestricted double cp1x, unrestricted double cp1y, unrestricted double cp2x, unrestricted double cp2y, unrestricted double x, unrestricted double y);
|
||||
undefined arcTo(unrestricted double x1, unrestricted double y1, unrestricted double x2, unrestricted double y2, unrestricted double radius);
|
||||
undefined rect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h);
|
||||
undefined roundRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h, optional (unrestricted double or DOMPointInit or sequence<(unrestricted double or DOMPointInit)>) radii = 0);
|
||||
undefined arc(unrestricted double x, unrestricted double y, unrestricted double radius, unrestricted double startAngle, unrestricted double endAngle, optional boolean counterclockwise = false);
|
||||
undefined ellipse(unrestricted double x, unrestricted double y, unrestricted double radiusX, unrestricted double radiusY, unrestricted double rotation, unrestricted double startAngle, unrestricted double endAngle, optional boolean counterclockwise = false);
|
||||
};
|
||||
|
@ -1527,14 +1542,14 @@ dictionary ImageBitmapRenderingContextSettings {
|
|||
boolean alpha = true;
|
||||
};
|
||||
|
||||
typedef (OffscreenCanvasRenderingContext2D or ImageBitmapRenderingContext or WebGLRenderingContext or WebGL2RenderingContext) OffscreenRenderingContext;
|
||||
typedef (OffscreenCanvasRenderingContext2D or ImageBitmapRenderingContext or WebGLRenderingContext or WebGL2RenderingContext or GPUCanvasContext) OffscreenRenderingContext;
|
||||
|
||||
dictionary ImageEncodeOptions {
|
||||
DOMString type = "image/png";
|
||||
unrestricted double quality;
|
||||
};
|
||||
|
||||
enum OffscreenRenderingContextId { "2d", "bitmaprenderer", "webgl", "webgl2" };
|
||||
enum OffscreenRenderingContextId { "2d", "bitmaprenderer", "webgl", "webgl2", "webgpu" };
|
||||
|
||||
[Exposed=(Window,Worker), Transferable]
|
||||
interface OffscreenCanvas : EventTarget {
|
||||
|
@ -1546,6 +1561,9 @@ interface OffscreenCanvas : EventTarget {
|
|||
OffscreenRenderingContext? getContext(OffscreenRenderingContextId contextId, optional any options = null);
|
||||
ImageBitmap transferToImageBitmap();
|
||||
Promise<Blob> convertToBlob(optional ImageEncodeOptions options = {});
|
||||
|
||||
attribute EventHandler oncontextlost;
|
||||
attribute EventHandler oncontextrestored;
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
|
@ -1724,6 +1742,7 @@ interface Window : EventTarget {
|
|||
|
||||
// the user agent
|
||||
readonly attribute Navigator navigator;
|
||||
readonly attribute Navigator clientInformation; // legacy alias of .navigator
|
||||
readonly attribute boolean originAgentCluster;
|
||||
|
||||
// user prompts
|
||||
|
@ -1741,7 +1760,7 @@ interface Window : EventTarget {
|
|||
Window includes GlobalEventHandlers;
|
||||
Window includes WindowEventHandlers;
|
||||
|
||||
dictionary WindowPostMessageOptions : PostMessageOptions {
|
||||
dictionary WindowPostMessageOptions : StructuredSerializeOptions {
|
||||
USVString targetOrigin = "/";
|
||||
};
|
||||
|
||||
|
@ -1823,7 +1842,7 @@ interface BeforeUnloadEvent : Event {
|
|||
attribute DOMString returnValue;
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
[Exposed=*]
|
||||
interface ErrorEvent : Event {
|
||||
constructor(DOMString type, optional ErrorEventInit eventInitDict = {});
|
||||
|
||||
|
@ -1839,10 +1858,10 @@ dictionary ErrorEventInit : EventInit {
|
|||
USVString filename = "";
|
||||
unsigned long lineno = 0;
|
||||
unsigned long colno = 0;
|
||||
any error = null;
|
||||
any error;
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
[Exposed=*]
|
||||
interface PromiseRejectionEvent : Event {
|
||||
constructor(DOMString type, PromiseRejectionEventInit eventInitDict);
|
||||
|
||||
|
@ -1870,6 +1889,8 @@ typedef OnBeforeUnloadEventHandlerNonNull? OnBeforeUnloadEventHandler;
|
|||
interface mixin GlobalEventHandlers {
|
||||
attribute EventHandler onabort;
|
||||
attribute EventHandler onauxclick;
|
||||
attribute EventHandler onbeforeinput;
|
||||
attribute EventHandler onbeforematch;
|
||||
attribute EventHandler onblur;
|
||||
attribute EventHandler oncancel;
|
||||
attribute EventHandler oncanplay;
|
||||
|
@ -1877,7 +1898,9 @@ interface mixin GlobalEventHandlers {
|
|||
attribute EventHandler onchange;
|
||||
attribute EventHandler onclick;
|
||||
attribute EventHandler onclose;
|
||||
attribute EventHandler oncontextlost;
|
||||
attribute EventHandler oncontextmenu;
|
||||
attribute EventHandler oncontextrestored;
|
||||
attribute EventHandler oncuechange;
|
||||
attribute EventHandler ondblclick;
|
||||
attribute EventHandler ondrag;
|
||||
|
@ -1968,15 +1991,17 @@ interface mixin WindowOrWorkerGlobalScope {
|
|||
readonly attribute boolean isSecureContext;
|
||||
readonly attribute boolean crossOriginIsolated;
|
||||
|
||||
undefined reportError(any e);
|
||||
|
||||
// base64 utility methods
|
||||
DOMString btoa(DOMString data);
|
||||
ByteString atob(DOMString data);
|
||||
|
||||
// timers
|
||||
long setTimeout(TimerHandler handler, optional long timeout = 0, any... arguments);
|
||||
undefined clearTimeout(optional long handle = 0);
|
||||
undefined clearTimeout(optional long id = 0);
|
||||
long setInterval(TimerHandler handler, optional long timeout = 0, any... arguments);
|
||||
undefined clearInterval(optional long handle = 0);
|
||||
undefined clearInterval(optional long id = 0);
|
||||
|
||||
// microtask queuing
|
||||
undefined queueMicrotask(VoidFunction callback);
|
||||
|
@ -1984,6 +2009,9 @@ interface mixin WindowOrWorkerGlobalScope {
|
|||
// ImageBitmap
|
||||
Promise<ImageBitmap> createImageBitmap(ImageBitmapSource image, optional ImageBitmapOptions options = {});
|
||||
Promise<ImageBitmap> createImageBitmap(ImageBitmapSource image, long sx, long sy, long sw, long sh, optional ImageBitmapOptions options = {});
|
||||
|
||||
// structured cloning
|
||||
any structuredClone(any value, optional StructuredSerializeOptions options = {});
|
||||
};
|
||||
Window includes WindowOrWorkerGlobalScope;
|
||||
WorkerGlobalScope includes WindowOrWorkerGlobalScope;
|
||||
|
@ -2050,6 +2078,49 @@ interface mixin NavigatorCookies {
|
|||
readonly attribute boolean cookieEnabled;
|
||||
};
|
||||
|
||||
interface mixin NavigatorPlugins {
|
||||
[SameObject] readonly attribute PluginArray plugins;
|
||||
[SameObject] readonly attribute MimeTypeArray mimeTypes;
|
||||
boolean javaEnabled();
|
||||
readonly attribute boolean pdfViewerEnabled;
|
||||
};
|
||||
|
||||
[Exposed=Window,
|
||||
LegacyUnenumerableNamedProperties]
|
||||
interface PluginArray {
|
||||
undefined refresh();
|
||||
readonly attribute unsigned long length;
|
||||
getter Plugin? item(unsigned long index);
|
||||
getter Plugin? namedItem(DOMString name);
|
||||
};
|
||||
|
||||
[Exposed=Window,
|
||||
LegacyUnenumerableNamedProperties]
|
||||
interface MimeTypeArray {
|
||||
readonly attribute unsigned long length;
|
||||
getter MimeType? item(unsigned long index);
|
||||
getter MimeType? namedItem(DOMString name);
|
||||
};
|
||||
|
||||
[Exposed=Window,
|
||||
LegacyUnenumerableNamedProperties]
|
||||
interface Plugin {
|
||||
readonly attribute DOMString name;
|
||||
readonly attribute DOMString description;
|
||||
readonly attribute DOMString filename;
|
||||
readonly attribute unsigned long length;
|
||||
getter MimeType? item(unsigned long index);
|
||||
getter MimeType? namedItem(DOMString name);
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface MimeType {
|
||||
readonly attribute DOMString type;
|
||||
readonly attribute DOMString description;
|
||||
readonly attribute DOMString suffixes;
|
||||
readonly attribute Plugin enabledPlugin;
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker), Serializable, Transferable]
|
||||
interface ImageBitmap {
|
||||
readonly attribute unsigned long width;
|
||||
|
@ -2131,53 +2202,6 @@ dictionary EventSourceInit {
|
|||
boolean withCredentials = false;
|
||||
};
|
||||
|
||||
enum BinaryType { "blob", "arraybuffer" };
|
||||
[Exposed=(Window,Worker)]
|
||||
interface WebSocket : EventTarget {
|
||||
constructor(USVString url, optional (DOMString or sequence<DOMString>) protocols = []);
|
||||
|
||||
readonly attribute USVString url;
|
||||
|
||||
// ready state
|
||||
const unsigned short CONNECTING = 0;
|
||||
const unsigned short OPEN = 1;
|
||||
const unsigned short CLOSING = 2;
|
||||
const unsigned short CLOSED = 3;
|
||||
readonly attribute unsigned short readyState;
|
||||
readonly attribute unsigned long long bufferedAmount;
|
||||
|
||||
// networking
|
||||
attribute EventHandler onopen;
|
||||
attribute EventHandler onerror;
|
||||
attribute EventHandler onclose;
|
||||
readonly attribute DOMString extensions;
|
||||
readonly attribute DOMString protocol;
|
||||
undefined close(optional [Clamp] unsigned short code, optional USVString reason);
|
||||
|
||||
// messaging
|
||||
attribute EventHandler onmessage;
|
||||
attribute BinaryType binaryType;
|
||||
undefined send(USVString data);
|
||||
undefined send(Blob data);
|
||||
undefined send(ArrayBuffer data);
|
||||
undefined send(ArrayBufferView data);
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
interface CloseEvent : Event {
|
||||
constructor(DOMString type, optional CloseEventInit eventInitDict = {});
|
||||
|
||||
readonly attribute boolean wasClean;
|
||||
readonly attribute unsigned short code;
|
||||
readonly attribute USVString reason;
|
||||
};
|
||||
|
||||
dictionary CloseEventInit : EventInit {
|
||||
boolean wasClean = false;
|
||||
unsigned short code = 0;
|
||||
USVString reason = "";
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
interface MessageChannel {
|
||||
constructor();
|
||||
|
@ -2189,7 +2213,7 @@ interface MessageChannel {
|
|||
[Exposed=(Window,Worker,AudioWorklet), Transferable]
|
||||
interface MessagePort : EventTarget {
|
||||
undefined postMessage(any message, sequence<object> transfer);
|
||||
undefined postMessage(any message, optional PostMessageOptions options = {});
|
||||
undefined postMessage(any message, optional StructuredSerializeOptions options = {});
|
||||
undefined start();
|
||||
undefined close();
|
||||
|
||||
|
@ -2198,7 +2222,7 @@ interface MessagePort : EventTarget {
|
|||
attribute EventHandler onmessageerror;
|
||||
};
|
||||
|
||||
dictionary PostMessageOptions {
|
||||
dictionary StructuredSerializeOptions {
|
||||
sequence<object> transfer = [];
|
||||
};
|
||||
|
||||
|
@ -2233,7 +2257,7 @@ interface DedicatedWorkerGlobalScope : WorkerGlobalScope {
|
|||
[Replaceable] readonly attribute DOMString name;
|
||||
|
||||
undefined postMessage(any message, sequence<object> transfer);
|
||||
undefined postMessage(any message, optional PostMessageOptions options = {});
|
||||
undefined postMessage(any message, optional StructuredSerializeOptions options = {});
|
||||
|
||||
undefined close();
|
||||
|
||||
|
@ -2261,7 +2285,7 @@ interface Worker : EventTarget {
|
|||
undefined terminate();
|
||||
|
||||
undefined postMessage(any message, sequence<object> transfer);
|
||||
undefined postMessage(any message, optional PostMessageOptions options = {});
|
||||
undefined postMessage(any message, optional StructuredSerializeOptions options = {});
|
||||
attribute EventHandler onmessage;
|
||||
attribute EventHandler onmessageerror;
|
||||
};
|
||||
|
@ -2560,7 +2584,12 @@ partial interface HTMLParagraphElement {
|
|||
[CEReactions] attribute DOMString align;
|
||||
};
|
||||
|
||||
partial interface HTMLParamElement {
|
||||
[Exposed=Window]
|
||||
interface HTMLParamElement : HTMLElement {
|
||||
[HTMLConstructor] constructor();
|
||||
|
||||
[CEReactions] attribute DOMString name;
|
||||
[CEReactions] attribute DOMString value;
|
||||
[CEReactions] attribute DOMString type;
|
||||
[CEReactions] attribute DOMString valueType;
|
||||
};
|
||||
|
@ -2656,42 +2685,3 @@ interface External {
|
|||
undefined AddSearchProvider();
|
||||
undefined IsSearchProviderInstalled();
|
||||
};
|
||||
|
||||
interface mixin NavigatorPlugins {
|
||||
[SameObject] readonly attribute PluginArray plugins;
|
||||
[SameObject] readonly attribute MimeTypeArray mimeTypes;
|
||||
boolean javaEnabled();
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface PluginArray {
|
||||
undefined refresh();
|
||||
readonly attribute unsigned long length;
|
||||
getter object? item(unsigned long index);
|
||||
object? namedItem(DOMString name);
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface MimeTypeArray {
|
||||
readonly attribute unsigned long length;
|
||||
getter object? item(unsigned long index);
|
||||
object? namedItem(DOMString name);
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface Plugin {
|
||||
readonly attribute undefined name;
|
||||
readonly attribute undefined description;
|
||||
readonly attribute undefined filename;
|
||||
readonly attribute undefined length;
|
||||
getter undefined item(unsigned long index);
|
||||
undefined namedItem(DOMString name);
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface MimeType {
|
||||
readonly attribute undefined type;
|
||||
readonly attribute undefined description;
|
||||
readonly attribute undefined suffixes;
|
||||
readonly attribute undefined enabledPlugin;
|
||||
};
|
||||
|
|
|
@ -10,7 +10,7 @@ partial interface Performance {
|
|||
};
|
||||
typedef sequence<PerformanceEntry> PerformanceEntryList;
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
[Exposed=*]
|
||||
interface PerformanceEntry {
|
||||
readonly attribute DOMString name;
|
||||
readonly attribute DOMString entryType;
|
||||
|
@ -22,7 +22,7 @@ interface PerformanceEntry {
|
|||
callback PerformanceObserverCallback = undefined (PerformanceObserverEntryList entries,
|
||||
PerformanceObserver observer,
|
||||
optional PerformanceObserverCallbackOptions options = {});
|
||||
[Exposed=(Window,Worker)]
|
||||
[Exposed=*]
|
||||
interface PerformanceObserver {
|
||||
constructor(PerformanceObserverCallback callback);
|
||||
undefined observe (optional PerformanceObserverInit options = {});
|
||||
|
@ -41,7 +41,7 @@ dictionary PerformanceObserverInit {
|
|||
boolean buffered;
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
[Exposed=*]
|
||||
interface PerformanceObserverEntryList {
|
||||
PerformanceEntryList getEntries();
|
||||
PerformanceEntryList getEntriesByType (DOMString type);
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
// GENERATED CONTENT - DO NOT EDIT
|
||||
// Content was automatically extracted by Reffy into webref
|
||||
// (https://github.com/w3c/webref)
|
||||
// Source: Resource Timing Level 2 (https://w3c.github.io/resource-timing/)
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
interface PerformanceResourceTiming : PerformanceEntry {
|
||||
readonly attribute DOMString initiatorType;
|
||||
readonly attribute ByteString nextHopProtocol;
|
||||
readonly attribute DOMHighResTimeStamp workerStart;
|
||||
readonly attribute DOMHighResTimeStamp redirectStart;
|
||||
readonly attribute DOMHighResTimeStamp redirectEnd;
|
||||
readonly attribute DOMHighResTimeStamp fetchStart;
|
||||
readonly attribute DOMHighResTimeStamp domainLookupStart;
|
||||
readonly attribute DOMHighResTimeStamp domainLookupEnd;
|
||||
readonly attribute DOMHighResTimeStamp connectStart;
|
||||
readonly attribute DOMHighResTimeStamp connectEnd;
|
||||
readonly attribute DOMHighResTimeStamp secureConnectionStart;
|
||||
readonly attribute DOMHighResTimeStamp requestStart;
|
||||
readonly attribute DOMHighResTimeStamp responseStart;
|
||||
readonly attribute DOMHighResTimeStamp responseEnd;
|
||||
readonly attribute unsigned long long transferSize;
|
||||
readonly attribute unsigned long long encodedBodySize;
|
||||
readonly attribute unsigned long long decodedBodySize;
|
||||
[Default] object toJSON();
|
||||
};
|
||||
|
||||
partial interface Performance {
|
||||
undefined clearResourceTimings ();
|
||||
undefined setResourceTimingBufferSize (unsigned long maxSize);
|
||||
attribute EventHandler onresourcetimingbufferfull;
|
||||
};
|
|
@ -3,7 +3,7 @@
|
|||
// (https://github.com/w3c/webref)
|
||||
// Source: Streams Standard (https://streams.spec.whatwg.org/)
|
||||
|
||||
[Exposed=(Window,Worker,Worklet), Transferable]
|
||||
[Exposed=*, Transferable]
|
||||
interface ReadableStream {
|
||||
constructor(optional object underlyingSource, optional QueuingStrategy strategy = {});
|
||||
|
||||
|
@ -64,35 +64,30 @@ interface mixin ReadableStreamGenericReader {
|
|||
Promise<undefined> cancel(optional any reason);
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
[Exposed=*]
|
||||
interface ReadableStreamDefaultReader {
|
||||
constructor(ReadableStream stream);
|
||||
|
||||
Promise<ReadableStreamDefaultReadResult> read();
|
||||
Promise<ReadableStreamReadResult> read();
|
||||
undefined releaseLock();
|
||||
};
|
||||
ReadableStreamDefaultReader includes ReadableStreamGenericReader;
|
||||
|
||||
dictionary ReadableStreamDefaultReadResult {
|
||||
any value;
|
||||
boolean done;
|
||||
dictionary ReadableStreamReadResult {
|
||||
any value;
|
||||
boolean done;
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
[Exposed=*]
|
||||
interface ReadableStreamBYOBReader {
|
||||
constructor(ReadableStream stream);
|
||||
|
||||
Promise<ReadableStreamBYOBReadResult> read(ArrayBufferView view);
|
||||
Promise<ReadableStreamReadResult> read(ArrayBufferView view);
|
||||
undefined releaseLock();
|
||||
};
|
||||
ReadableStreamBYOBReader includes ReadableStreamGenericReader;
|
||||
|
||||
dictionary ReadableStreamBYOBReadResult {
|
||||
ArrayBufferView value;
|
||||
boolean done;
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
[Exposed=*]
|
||||
interface ReadableStreamDefaultController {
|
||||
readonly attribute unrestricted double? desiredSize;
|
||||
|
||||
|
@ -101,7 +96,7 @@ interface ReadableStreamDefaultController {
|
|||
undefined error(optional any e);
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
[Exposed=*]
|
||||
interface ReadableByteStreamController {
|
||||
readonly attribute ReadableStreamBYOBRequest? byobRequest;
|
||||
readonly attribute unrestricted double? desiredSize;
|
||||
|
@ -111,7 +106,7 @@ interface ReadableByteStreamController {
|
|||
undefined error(optional any e);
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
[Exposed=*]
|
||||
interface ReadableStreamBYOBRequest {
|
||||
readonly attribute ArrayBufferView? view;
|
||||
|
||||
|
@ -119,7 +114,7 @@ interface ReadableStreamBYOBRequest {
|
|||
undefined respondWithNewView(ArrayBufferView view);
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker,Worklet), Transferable]
|
||||
[Exposed=*, Transferable]
|
||||
interface WritableStream {
|
||||
constructor(optional object underlyingSink, optional QueuingStrategy strategy = {});
|
||||
|
||||
|
@ -143,7 +138,7 @@ callback UnderlyingSinkWriteCallback = Promise<undefined> (any chunk, WritableSt
|
|||
callback UnderlyingSinkCloseCallback = Promise<undefined> ();
|
||||
callback UnderlyingSinkAbortCallback = Promise<undefined> (optional any reason);
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
[Exposed=*]
|
||||
interface WritableStreamDefaultWriter {
|
||||
constructor(WritableStream stream);
|
||||
|
||||
|
@ -157,12 +152,13 @@ interface WritableStreamDefaultWriter {
|
|||
Promise<undefined> write(optional any chunk);
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
[Exposed=*]
|
||||
interface WritableStreamDefaultController {
|
||||
readonly attribute AbortSignal signal;
|
||||
undefined error(optional any e);
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker,Worklet), Transferable]
|
||||
[Exposed=*, Transferable]
|
||||
interface TransformStream {
|
||||
constructor(optional object transformer,
|
||||
optional QueuingStrategy writableStrategy = {},
|
||||
|
@ -184,7 +180,7 @@ callback TransformerStartCallback = any (TransformStreamDefaultController contro
|
|||
callback TransformerFlushCallback = Promise<undefined> (TransformStreamDefaultController controller);
|
||||
callback TransformerTransformCallback = Promise<undefined> (any chunk, TransformStreamDefaultController controller);
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
[Exposed=*]
|
||||
interface TransformStreamDefaultController {
|
||||
readonly attribute unrestricted double? desiredSize;
|
||||
|
||||
|
@ -198,13 +194,13 @@ dictionary QueuingStrategy {
|
|||
QueuingStrategySize size;
|
||||
};
|
||||
|
||||
callback QueuingStrategySize = unrestricted double (optional any chunk);
|
||||
callback QueuingStrategySize = unrestricted double (any chunk);
|
||||
|
||||
dictionary QueuingStrategyInit {
|
||||
required unrestricted double highWaterMark;
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
[Exposed=*]
|
||||
interface ByteLengthQueuingStrategy {
|
||||
constructor(QueuingStrategyInit init);
|
||||
|
||||
|
@ -212,7 +208,7 @@ interface ByteLengthQueuingStrategy {
|
|||
readonly attribute Function size;
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker,Worklet)]
|
||||
[Exposed=*]
|
||||
interface CountQueuingStrategy {
|
||||
constructor(QueuingStrategyInit init);
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// (https://github.com/w3c/webref)
|
||||
// Source: URL Standard (https://url.spec.whatwg.org/)
|
||||
|
||||
[Exposed=(Window,Worker),
|
||||
[Exposed=*,
|
||||
LegacyWindowAlias=webkitURL]
|
||||
interface URL {
|
||||
constructor(USVString url, optional USVString base);
|
||||
|
@ -24,7 +24,7 @@ interface URL {
|
|||
USVString toJSON();
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
[Exposed=*]
|
||||
interface URLSearchParams {
|
||||
constructor(optional (sequence<sequence<USVString>> or record<USVString, USVString> or USVString) init = "");
|
||||
|
||||
|
|
|
@ -22,13 +22,13 @@ partial interface Performance {
|
|||
undefined clearMeasures(optional DOMString measureName);
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
[Exposed=*]
|
||||
interface PerformanceMark : PerformanceEntry {
|
||||
constructor(DOMString markName, optional PerformanceMarkOptions markOptions = {});
|
||||
readonly attribute any detail;
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
[Exposed=*]
|
||||
interface PerformanceMeasure : PerformanceEntry {
|
||||
readonly attribute any detail;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
// GENERATED CONTENT - DO NOT EDIT
|
||||
// Content was automatically extracted by Reffy into webref
|
||||
// (https://github.com/w3c/webref)
|
||||
// Source: Web IDL Standard (https://webidl.spec.whatwg.org/)
|
||||
|
||||
typedef (Int8Array or Int16Array or Int32Array or
|
||||
Uint8Array or Uint16Array or Uint32Array or Uint8ClampedArray or
|
||||
BigInt64Array or BigUint64Array or
|
||||
Float32Array or Float64Array or DataView) ArrayBufferView;
|
||||
|
||||
typedef (ArrayBufferView or ArrayBuffer) BufferSource;
|
||||
[Exposed=(Window,Worker),
|
||||
Serializable]
|
||||
interface DOMException { // but see below note about ECMAScript binding
|
||||
constructor(optional DOMString message = "", optional DOMString name = "Error");
|
||||
readonly attribute DOMString name;
|
||||
readonly attribute DOMString message;
|
||||
readonly attribute unsigned short code;
|
||||
|
||||
const unsigned short INDEX_SIZE_ERR = 1;
|
||||
const unsigned short DOMSTRING_SIZE_ERR = 2;
|
||||
const unsigned short HIERARCHY_REQUEST_ERR = 3;
|
||||
const unsigned short WRONG_DOCUMENT_ERR = 4;
|
||||
const unsigned short INVALID_CHARACTER_ERR = 5;
|
||||
const unsigned short NO_DATA_ALLOWED_ERR = 6;
|
||||
const unsigned short NO_MODIFICATION_ALLOWED_ERR = 7;
|
||||
const unsigned short NOT_FOUND_ERR = 8;
|
||||
const unsigned short NOT_SUPPORTED_ERR = 9;
|
||||
const unsigned short INUSE_ATTRIBUTE_ERR = 10;
|
||||
const unsigned short INVALID_STATE_ERR = 11;
|
||||
const unsigned short SYNTAX_ERR = 12;
|
||||
const unsigned short INVALID_MODIFICATION_ERR = 13;
|
||||
const unsigned short NAMESPACE_ERR = 14;
|
||||
const unsigned short INVALID_ACCESS_ERR = 15;
|
||||
const unsigned short VALIDATION_ERR = 16;
|
||||
const unsigned short TYPE_MISMATCH_ERR = 17;
|
||||
const unsigned short SECURITY_ERR = 18;
|
||||
const unsigned short NETWORK_ERR = 19;
|
||||
const unsigned short ABORT_ERR = 20;
|
||||
const unsigned short URL_MISMATCH_ERR = 21;
|
||||
const unsigned short QUOTA_EXCEEDED_ERR = 22;
|
||||
const unsigned short TIMEOUT_ERR = 23;
|
||||
const unsigned short INVALID_NODE_TYPE_ERR = 24;
|
||||
const unsigned short DATA_CLONE_ERR = 25;
|
||||
};
|
||||
|
||||
callback Function = any (any... arguments);
|
||||
callback VoidFunction = undefined ();
|
||||
|
||||
typedef unsigned long long DOMTimeStamp;
|
|
@ -0,0 +1,53 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing - cached resources generate performance entries</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help"
|
||||
href="https://www.w3.org/TR/resource-timing-2/#resources-included-in-the-performanceresourcetiming-interface"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/entry-invariants.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Description</h1>
|
||||
<p>This test validates that a 304 Not Modified resource appears in the
|
||||
Performance Timeline.</p>
|
||||
<script>
|
||||
// Need to fetch the same resource twice; the first will get a 200 response but
|
||||
// the second request should be cached and get a 304.
|
||||
promise_test(async () => {
|
||||
performance.clearResourceTimings();
|
||||
|
||||
const unique = Math.random();
|
||||
const path = `resources/fake_responses.py?tag=${unique}`;
|
||||
|
||||
await load.xhr_sync(path);
|
||||
await load.xhr_sync(path, {"If-None-Match": `${unique}`});
|
||||
const entries = await new Promise(resolve => {
|
||||
const accumulator = [];
|
||||
new PerformanceObserver(entry_list => {
|
||||
entry_list.getEntries().forEach(entry => {
|
||||
accumulator.push(entry);
|
||||
});
|
||||
if (accumulator.length >= 2) {
|
||||
resolve(accumulator);
|
||||
}
|
||||
}).observe({'type': 'resource', 'buffered': true});
|
||||
});
|
||||
|
||||
|
||||
if (entries.length != 2) {
|
||||
throw new Error(`Expecting 2 but got ${entries.length} entries`);
|
||||
}
|
||||
|
||||
assert_equals(entries[0].name, entries[1].name,
|
||||
"Both entries should have the same name");
|
||||
invariants.assert_tao_pass_no_redirect_http(entries[0]);
|
||||
invariants.assert_tao_pass_304_not_modified_http(entries[1]);
|
||||
}, "304 responses should still show up in the PerformanceTimeline");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,77 @@
|
|||
For [Resource Timing][1] tests, we want to have a consistent and clear coding
|
||||
style. The goals of this style are to:
|
||||
* Make it easier for new contributors to find their way around
|
||||
* Help improve readability and maintainability
|
||||
* Help us understand which parts of the spec are tested or not
|
||||
Lots of the following rules are arbitrary but the value is realized in
|
||||
consistency instead of adhering to the 'perfect' style.
|
||||
|
||||
We want the test suite to be navigable. Developers should be able to easily
|
||||
find the file or test that is relevant to their work.
|
||||
* Tests should be arranged in files according to which piece of the spec they
|
||||
test
|
||||
* Files should be named using a consistent pattern
|
||||
* HTML files should include useful meta tags
|
||||
* `<title>` for controlling labels in results pages
|
||||
* `<link rel="help">` to point at the relevant piece of the spec
|
||||
|
||||
We want the test suite to run consistently. Flaky tests are counterproductive.
|
||||
* Prefer `promise_test` to `async_test`
|
||||
* Note that there’s [still potential for some concurrency][2]; use
|
||||
`add_cleanup()` if needed
|
||||
|
||||
We want the tests to be readable. Tests should be written in a modern style
|
||||
with recurring patterns.
|
||||
* 80 character line limits where we can
|
||||
* Consistent use of anonymous functions
|
||||
* prefer
|
||||
```
|
||||
const func1 = param1 => {
|
||||
body();
|
||||
}
|
||||
const func2 = (param1, param2) => {
|
||||
body();
|
||||
}
|
||||
fn(param => {
|
||||
body();
|
||||
});
|
||||
```
|
||||
|
||||
over
|
||||
|
||||
```
|
||||
function func1(param1) {
|
||||
body();
|
||||
}
|
||||
function func2(param1, param2) {
|
||||
body();
|
||||
}
|
||||
fn(function(param) {
|
||||
body();
|
||||
});
|
||||
```
|
||||
|
||||
* Prefer `const` (or, if needed, `let`) to `var`
|
||||
* Contain use of ‘.sub’ in filenames to known helper utilities where possible
|
||||
* E.g. prefer use of get-host-info.sub.js to `{{host}}` or `{{ports[0]}}`
|
||||
expressions
|
||||
* Avoid use of webperftestharness[extension].js as it’s a layer of cognitive
|
||||
overhead between test content and test intent
|
||||
* Helper .js files are still encouraged where it makes sense but we want
|
||||
to avoid a testing framework that is specific to Resource Timing (or
|
||||
web performance APIs, in general).
|
||||
* Prefer [`fetch_tests_from_window`][3] to collect test results from embedded
|
||||
iframes instead of hand-rolled `postMessage` approaches
|
||||
* Use the [`assert_*`][4] family of functions to check conformance to the spec
|
||||
but throw exceptions explicitly when the test itself is broken.
|
||||
* A failed assert indicates "the implementation doesn't conform to the
|
||||
spec"
|
||||
* Other uncaught exceptions indicate "the test case itself has a bug"
|
||||
* Where possible, we want tests to be scalable - adding another test case
|
||||
should be as simple as calling the tests with new parameters, rather than
|
||||
copying an existing test and modifying it.
|
||||
|
||||
[1]: https://www.w3.org/TR/resource-timing-2/
|
||||
[2]: https://web-platform-tests.org/writing-tests/testharness-api.html#promise-tests
|
||||
[3]: https://web-platform-tests.org/writing-tests/testharness-api.html#consolidating-tests-from-other-documents
|
||||
[4]: https://web-platform-tests.org/writing-tests/testharness-api.html#list-of-assertions
|
|
@ -0,0 +1,6 @@
|
|||
spec: https://w3c.github.io/resource-timing/
|
||||
suggested_reviewers:
|
||||
- plehegar
|
||||
- zqzhang
|
||||
- igrigorik
|
||||
- yoavweiss
|
|
@ -0,0 +1,64 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates resource timing information for a same-origin=>cross-origin=>same-origin redirect chain without Timing-Allow-Origin.</title>
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-cross-origin-resources"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/entry-invariants.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const {HTTPS_REMOTE_ORIGIN} = get_host_info();
|
||||
const SAME_ORIGIN = location.origin;
|
||||
// Same-Origin => Cross-Origin => Same-Origin => Same-Origin redirect chain
|
||||
let destUrl = `${SAME_ORIGIN}/resource-timing/resources/multi_redirect.py?`;
|
||||
destUrl += `page_origin=${SAME_ORIGIN}`;
|
||||
destUrl += `&cross_origin=${HTTPS_REMOTE_ORIGIN}`;
|
||||
destUrl += `&final_resource=/resource-timing/resources/blank_page_green.htm`;
|
||||
|
||||
// No TAO in the redirect chain
|
||||
attribute_test(
|
||||
load.iframe, destUrl,
|
||||
invariants.assert_cross_origin_redirected_resource,
|
||||
"Verify that cross origin resources' timings are not exposed when " +
|
||||
"same-origin=>cross-origin=>same-origin redirects have no " +
|
||||
"`Timing-Allow-Origin:` headers.");
|
||||
|
||||
// Partial TAO in the redirect chain
|
||||
destUrl += '&tao_steps=2';
|
||||
attribute_test(
|
||||
load.iframe, destUrl,
|
||||
invariants.assert_cross_origin_redirected_resource,
|
||||
"Verify that cross origin resources' timings are not exposed when " +
|
||||
"same-origin=>cross-origin=>same-origin redirects have " +
|
||||
"`Timing-Allow-Origin:` headers only on some of the responses.");
|
||||
|
||||
// Cross-origin => Cross-Origin => Same-Origin => Same-Origin redirect chain.
|
||||
destUrl = `${HTTPS_REMOTE_ORIGIN}/resource-timing/resources/multi_redirect.py?`;
|
||||
destUrl += `page_origin=${SAME_ORIGIN}`;
|
||||
destUrl += `&cross_origin=${HTTPS_REMOTE_ORIGIN}`;
|
||||
destUrl += `&final_resource=/resource-timing/resources/blue-with-tao.png`;
|
||||
destUrl += `&tao_steps=3`;
|
||||
|
||||
// Full redirect chain with `TAO: *`.
|
||||
attribute_test(
|
||||
load.image, destUrl,
|
||||
invariants.assert_tao_enabled_cross_origin_redirected_resource,
|
||||
"Verify that cross origin resources' timings are exposed when cross-origin " +
|
||||
"redirects have `Timing-Allow-Origin: *` headers");
|
||||
|
||||
// TAO with a specific origin
|
||||
destUrl += `&tao_value=${SAME_ORIGIN}`;
|
||||
attribute_test(
|
||||
load.image, destUrl,
|
||||
invariants.assert_cross_origin_redirected_resource,
|
||||
"Verify that cross origin resources' timings are not exposed when " +
|
||||
"same-origin=>cross-origin=>same-origin redirects have " +
|
||||
"`Timing-Allow-Origin:` headers with a specific origin.");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,82 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing TAO tests</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help"
|
||||
href="https://www.w3.org/TR/resource-timing-2/#timing-allow-origin"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="/common/custom-cors-response.js"></script>
|
||||
<script src="resources/entry-invariants.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/tao-response.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
const {ORIGIN, REMOTE_ORIGIN} = get_host_info();
|
||||
|
||||
const run_test = (loader, resource_type) => {
|
||||
attribute_test(loader, remote_tao_response(ORIGIN),
|
||||
invariants.assert_tao_pass_no_redirect_http,
|
||||
`The timing allow check algorithm will pass when the Timing-Allow-Origin ` +
|
||||
`header value contains only the origin. (${resource_type})`);
|
||||
|
||||
attribute_test(loader, remote_tao_response('*'),
|
||||
invariants.assert_tao_pass_no_redirect_http,
|
||||
`The timing allow check algorithm will pass when the Timing-Allow-Origin ` +
|
||||
`header value contains only a wildcard. (${resource_type})`);
|
||||
|
||||
attribute_test(loader, remote_tao_response(`${ORIGIN},fake`),
|
||||
invariants.assert_tao_pass_no_redirect_http,
|
||||
`The timing allow check algorithm will pass when the Timing-Allow-Origin ` +
|
||||
`header value list contains a case-sensitive match. (${resource_type})`);
|
||||
|
||||
attribute_test(loader, remote_tao_response(`${ORIGIN},*`),
|
||||
invariants.assert_tao_pass_no_redirect_http,
|
||||
`The timing allow check algorithm will pass when the Timing-Allow-Origin ` +
|
||||
`header value list contains the origin and a wildcard. (${resource_type})`);
|
||||
|
||||
attribute_test(loader, remote_tao_response('fake,*'),
|
||||
invariants.assert_tao_pass_no_redirect_http,
|
||||
`The timing allow check algorithm will pass when the Timing-Allow-Origin ` +
|
||||
`header value list contains a wildcard. (${resource_type})`);
|
||||
|
||||
attribute_test(loader, remote_tao_response('null'),
|
||||
invariants.assert_tao_failure_resource,
|
||||
`The timing allow check algorithm will fail when the Timing-Allow-Origin ` +
|
||||
`header value list contains a null origin. (${resource_type})`);
|
||||
|
||||
attribute_test(loader, remote_tao_response('*,*'),
|
||||
invariants.assert_tao_pass_no_redirect_http,
|
||||
`The timing allow check algorithm will pass when the Timing-Allow-Origin ` +
|
||||
`header value list contains multiple wildcards. (${resource_type})`);
|
||||
|
||||
attribute_test(loader, remote_tao_response(ORIGIN.toUpperCase()),
|
||||
invariants.assert_tao_failure_resource,
|
||||
`The timing allow check algorithm will fail when the Timing-Allow-Origin ` +
|
||||
`header value contains only the uppercased origin. (${resource_type})`);
|
||||
|
||||
attribute_test(loader, remote_tao_response(`${ORIGIN} *`),
|
||||
invariants.assert_tao_failure_resource,
|
||||
`The timing allow check algorithm will fail when the Timing-Allow-Origin ` +
|
||||
`header value contains the origin, a space, then a wildcard. ` +
|
||||
`(${resource_type})`);
|
||||
|
||||
attribute_test(loader, custom_cors_response({}, REMOTE_ORIGIN),
|
||||
invariants.assert_tao_failure_resource,
|
||||
`The timing allow check algorithm will fail when the Timing-Allow-Origin ` +
|
||||
`header is not present. (${resource_type})`);
|
||||
};
|
||||
|
||||
run_test(load.font, "font");
|
||||
run_test(load.iframe, "iframe");
|
||||
run_test(load.image, "image");
|
||||
run_test(load.script, "script");
|
||||
run_test(load.stylesheet, "stylesheet");
|
||||
run_test(load.xhr_sync, "XMLHttpRequest");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,46 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>TAO - port mismatch must fail the check</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-timing-allow-origin"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="resources/entry-invariants.js"></script>
|
||||
<script>
|
||||
|
||||
const {ORIGINAL_HOST, PORT, PORT2} = get_host_info();
|
||||
|
||||
// The main page is being requested on the default port (PORT), while the
|
||||
// subresource will be requested on a separate port (PORT2). The response will
|
||||
// have a Timing-Allow-Origin header value with the second port so this page's
|
||||
// origin should not be a match.
|
||||
const port_mismatch_url = `${location.protocol}//${ORIGINAL_HOST}:${PORT2}` +
|
||||
`/resource-timing/resources/TAOResponse.py?` +
|
||||
`tao=origin_port_${PORT2}`;
|
||||
attribute_test(
|
||||
fetch, port_mismatch_url, invariants.assert_tao_failure_resource,
|
||||
"A port mismatch must fail the TAO check");
|
||||
|
||||
// The same URL as above except the Timing-Allow-Origin header will have the
|
||||
// same port as this page's origin. Therefore, this page's origin will match
|
||||
// the Timing-Allow-Origin header's value. Therefore, the subresource's timings
|
||||
// must be exposed.
|
||||
const port_match_url = `${location.protocol}//${ORIGINAL_HOST}:${PORT2}` +
|
||||
`/resource-timing/resources/TAOResponse.py?` +
|
||||
`tao=origin_port_${PORT}`;
|
||||
attribute_test(
|
||||
fetch, port_match_url, invariants.assert_tao_pass_no_redirect_http,
|
||||
"An identical port must pass the TAO check");
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Description</h1>
|
||||
<p>This test validates that for a cross origin resource with different ports,
|
||||
the timing allow check algorithm will fail when the value of
|
||||
Timing-Allow-Origin value has the right host but the wrong port in it.</p>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performance-setresourcetimingbuffersize">
|
||||
<title>This test validates that setResourceTimingBufferFull behaves appropriately when set to the current buffer level.</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/buffer-full-utilities.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(async t => {
|
||||
await forceBufferFullEvent();
|
||||
performance.clearResourceTimings();
|
||||
return new Promise(resolve => {
|
||||
new PerformanceObserver(t.step_func(() => {
|
||||
assert_equals(performance.getEntriesByType('resource').length, 1,
|
||||
'The entry should be available in the performance timeline!');
|
||||
resolve();
|
||||
})).observe({type: 'resource'});
|
||||
load.script(scriptResources[2]);
|
||||
});
|
||||
}, "Test that entry was added to the buffer after a buffer full event");
|
||||
</script>
|
29
test/fixtures/wpt/resource-timing/buffer-full-add-entries-during-callback-that-drop.html
vendored
Normal file
29
test/fixtures/wpt/resource-timing/buffer-full-add-entries-during-callback-that-drop.html
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head onload>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates that synchronously adding entries in onresourcetimingbufferfull callback results in these entries being properly handled.</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performance-onresourcetimingbufferfull"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/buffer-full-utilities.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(async () => {
|
||||
await fillUpTheBufferWithSingleResource();
|
||||
performance.addEventListener('resourcetimingbufferfull', () => {
|
||||
performance.setResourceTimingBufferSize(2);
|
||||
// The sync entry is added to the secondary buffer, so will be the last one there and eventually dropped.
|
||||
load.xhr_sync(scriptResources[2]);
|
||||
});
|
||||
// This resource overflows the entry buffer, and goes into the secondary buffer.
|
||||
load.script(scriptResources[1]);
|
||||
await bufferFullFirePromise;
|
||||
checkEntries(2);
|
||||
}, "Test that entries synchronously added to the buffer during the callback are dropped");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
28
test/fixtures/wpt/resource-timing/buffer-full-add-entries-during-callback.html
vendored
Normal file
28
test/fixtures/wpt/resource-timing/buffer-full-add-entries-during-callback.html
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head onload>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates that synchronously adding entries in onresourcetimingbufferfull callback results in these entries being properly handled.</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performance-onresourcetimingbufferfull"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/buffer-full-utilities.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(async () => {
|
||||
await fillUpTheBufferWithSingleResource();
|
||||
performance.addEventListener('resourcetimingbufferfull', () => {
|
||||
performance.setResourceTimingBufferSize(3);
|
||||
load.xhr_sync(scriptResources[2]);
|
||||
});
|
||||
// This resource overflows the entry buffer, and goes into the secondary buffer.
|
||||
load.script(scriptResources[1]);
|
||||
await bufferFullFirePromise;
|
||||
checkEntries(3);
|
||||
}, "Test that entries synchronously added to the buffer during the callback don't get dropped if the buffer is increased");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,31 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head onload>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates that synchronously adding entries in onresourcetimingbufferfull callback results in these entries being properly handled.</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performance-onresourcetimingbufferfull"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/buffer-full-utilities.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(async t => {
|
||||
addAssertUnreachedBufferFull(t);
|
||||
await fillUpTheBufferWithSingleResource('resources/empty.js?willbelost');
|
||||
// These resources overflow the entry buffer, and go into the secondary buffer.
|
||||
load.xhr_sync(scriptResources[0]);
|
||||
load.xhr_sync(scriptResources[1]);
|
||||
performance.clearResourceTimings();
|
||||
performance.setResourceTimingBufferSize(3);
|
||||
load.xhr_sync(scriptResources[2]);
|
||||
const entriesAfterAddition = performance.getEntriesByType('resource');
|
||||
await waitForNextTask();
|
||||
checkEntries(3);
|
||||
assert_equals(entriesAfterAddition.length, 0, "No entries should have been added to the primary buffer before the task to 'fire a buffer full event'.");
|
||||
}, "Test that if the buffer is cleared after entries were added to the secondary buffer, those entries make it into the primary one");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
26
test/fixtures/wpt/resource-timing/buffer-full-decrease-buffer-during-callback.html
vendored
Normal file
26
test/fixtures/wpt/resource-timing/buffer-full-decrease-buffer-during-callback.html
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head onload>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates that decreasing the buffer size in onresourcetimingbufferfull callback does not result in extra entries being dropped.</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performance-onresourcetimingbufferfull"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/buffer-full-utilities.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(async () => {
|
||||
performance.addEventListener('resourcetimingbufferfull', () => {
|
||||
performance.setResourceTimingBufferSize(1);
|
||||
});
|
||||
await fillUpTheBufferWithTwoResources();
|
||||
load.script(scriptResources[2]);
|
||||
await bufferFullFirePromise;
|
||||
checkEntries(2);
|
||||
}, "Test that decreasing the buffer limit during the callback does not drop entries");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,31 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="help"
|
||||
href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
|
||||
<title>This test validates that resource timing implementations have a finite
|
||||
number of entries in their buffer.</title>
|
||||
<meta name="timeout" content="long">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(t => {
|
||||
return new Promise(resolve => {
|
||||
let counter = 0;
|
||||
performance.onresourcetimingbufferfull = resolve;
|
||||
const loadImagesRecursively = () => {
|
||||
// Load an image.
|
||||
(new Image()).src = "resources/blue.png?" + counter;
|
||||
++counter;
|
||||
// Yield to enable queueing an entry, then recursively load another image.
|
||||
t.step_timeout(loadImagesRecursively, 0);
|
||||
};
|
||||
loadImagesRecursively();
|
||||
});
|
||||
}, "Finite resource timing entries buffer size");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
26
test/fixtures/wpt/resource-timing/buffer-full-increase-buffer-during-callback.html
vendored
Normal file
26
test/fixtures/wpt/resource-timing/buffer-full-increase-buffer-during-callback.html
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head onload>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates increasing the buffer size in onresourcetimingbufferfull callback of resource timing.</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performance-onresourcetimingbufferfull"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/buffer-full-utilities.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(async () => {
|
||||
await fillUpTheBufferWithSingleResource();
|
||||
performance.addEventListener('resourcetimingbufferfull', () => {
|
||||
performance.setResourceTimingBufferSize(2);
|
||||
});
|
||||
await load.script(scriptResources[1]);
|
||||
await bufferFullFirePromise;
|
||||
checkEntries(2);
|
||||
}, "Test that increasing the buffer during the callback is enough for entries not to be dropped");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
30
test/fixtures/wpt/resource-timing/buffer-full-inspect-buffer-during-callback.html
vendored
Normal file
30
test/fixtures/wpt/resource-timing/buffer-full-inspect-buffer-during-callback.html
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head onload>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates the buffer doesn't contain more entries than it should inside onresourcetimingbufferfull callback.</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performance-setresourcetimingbuffersize"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/buffer-full-utilities.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(async t => {
|
||||
performance.addEventListener('resourcetimingbufferfull', t.step_func(() => {
|
||||
assert_equals(performance.getEntriesByType("resource").length, 1,
|
||||
"resource timing buffer in resourcetimingbufferfull is the size of the limit");
|
||||
load.xhr_sync(scriptResources[2]);
|
||||
performance.setResourceTimingBufferSize(3);
|
||||
assert_equals(performance.getEntriesByType("resource").length, 1,
|
||||
"A sync request must not be added to the primary buffer just yet, because it is full");
|
||||
}));
|
||||
await forceBufferFullEvent();
|
||||
await waitForNextTask();
|
||||
checkEntries(3);
|
||||
}, "Test that entries in the secondary buffer are not exposed during the callback and before they are copied to the primary buffer");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>This test validates that setResourceTimingBufferFull behaves appropriately when set to the current buffer level.</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performance-onresourcetimingbufferfull">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/buffer-full-utilities.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(async () => {
|
||||
let result = '';
|
||||
performance.addEventListener('resourcetimingbufferfull', () => {
|
||||
result += 'Event Fired with ' +
|
||||
performance.getEntriesByType('resource').length + ' entries.';
|
||||
performance.clearResourceTimings();
|
||||
});
|
||||
result += 'Before adding entries. ';
|
||||
await fillUpTheBufferWithTwoResources();
|
||||
result += 'After adding entries. ';
|
||||
load.script(scriptResources[2]);
|
||||
await bufferFullFirePromise;
|
||||
assert_equals(result, 'Before adding entries. After adding entries. Event Fired with 2 entries.');
|
||||
const entries = performance.getEntriesByType('resource');
|
||||
assert_equals(entries.length, 1,
|
||||
'Number of entries in resource timing buffer is unexpected');
|
||||
assert_true(entries[0].name.includes(scriptResources[2]),
|
||||
'The entry must correspond to the last resource loaded.')
|
||||
}, "Test that adding entries and firing the buffer full event happen in the right order.");
|
||||
</script>
|
36
test/fixtures/wpt/resource-timing/buffer-full-store-and-clear-during-callback.html
vendored
Normal file
36
test/fixtures/wpt/resource-timing/buffer-full-store-and-clear-during-callback.html
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head onload>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates the behavior of read and clear operation in onresourcetimingbufferfull callback of resource timing.</title>
|
||||
<link rel="author" title="Intel" href="http://www.intel.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performance-onresourcetimingbufferfull"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/buffer-full-utilities.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(async () => {
|
||||
await fillUpTheBufferWithSingleResource();
|
||||
const entryBuffer = [];
|
||||
performance.addEventListener('resourcetimingbufferfull', () => {
|
||||
entryBuffer.push(...performance.getEntriesByType('resource'));
|
||||
performance.clearResourceTimings();
|
||||
});
|
||||
load.script(scriptResources[1]);
|
||||
await bufferFullFirePromise;
|
||||
const entries = performance.getEntriesByType('resource');
|
||||
assert_equals(entries.length, 1,
|
||||
"Only the last entry should be stored in resource timing buffer since it's cleared once it overflows.");
|
||||
assert_true(entries[0].name.includes(scriptResources[1]),
|
||||
scriptResources[1] + " is in the entries buffer");
|
||||
assert_equals(entryBuffer.length, 1,
|
||||
'1 resource timing entry should be moved to entryBuffer.');
|
||||
assert_true(entryBuffer[0].name.includes(scriptResources[0]),
|
||||
scriptResources[0] + ' is in the entryBuffer');
|
||||
}, "Test that entries overflowing the buffer trigger the buffer full event, can be stored, and make their way to the primary buffer after it's cleared in the buffer full event.");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates that reducing the buffer size after entries were
|
||||
queued does not drop those entries, nor does it call the
|
||||
resourcetimingbufferfull event callback.</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help"
|
||||
href="https://www.w3.org/TR/resource-timing-2/#dom-performance-onresourcetimingbufferfull"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/buffer-full-utilities.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(async t => {
|
||||
addAssertUnreachedBufferFull(t);
|
||||
await fillUpTheBufferWithTwoResources();
|
||||
performance.setResourceTimingBufferSize(1);
|
||||
await waitForNextTask();
|
||||
checkEntries(2);
|
||||
}, "Test that if the buffer is reduced after entries were added to it, those" +
|
||||
" entries don't get cleared, nor is the resourcetimingbufferfull event" +
|
||||
" being called.");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head onload>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates that synchronously adding entries in onresourcetimingbufferfull callback results in these entries being properly handled.</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performance-onresourcetimingbufferfull"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/buffer-full-utilities.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(async t => {
|
||||
addAssertUnreachedBufferFull(t);
|
||||
await fillUpTheBufferWithSingleResource();
|
||||
// These resources overflow the entry buffer, and go into the secondary buffer.
|
||||
load.xhr_sync(scriptResources[1]);
|
||||
load.xhr_sync(scriptResources[2]);
|
||||
// Immediately increase the size: the bufferfull event should not be fired.
|
||||
performance.setResourceTimingBufferSize(3);
|
||||
await waitForNextTask();
|
||||
checkEntries(3);
|
||||
}, "Test that overflowing the buffer and immediately increasing its limit does not trigger the resourcetimingbufferfull event");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates the functionality of onresourcetimingbufferfull in resource timing.</title>
|
||||
<link rel="author" title="Intel" href="http://www.intel.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performance-onresourcetimingbufferfull"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/buffer-full-utilities.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(async () => {
|
||||
let bufferFullCount = 0;
|
||||
performance.addEventListener('resourcetimingbufferfull', e => {
|
||||
assert_equals(e.bubbles, false, "Event bubbles attribute is false");
|
||||
bufferFullCount++;
|
||||
});
|
||||
await fillUpTheBufferWithTwoResources();
|
||||
// Overflow the buffer
|
||||
await load.script(scriptResources[2]);
|
||||
await waitForNextTask();
|
||||
checkEntries(2);
|
||||
assert_equals(bufferFullCount, 1, 'onresourcetimingbufferfull should have been invoked once.');
|
||||
}, "Test that a buffer full event does not bubble and that resourcetimingbufferfull is called only once per overflow");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,18 @@
|
|||
async_test(t => {
|
||||
performance.clearResourceTimings();
|
||||
// First observer creates second in callback to ensure the entry has been dispatched by the time
|
||||
// the second observer begins observing.
|
||||
new PerformanceObserver(() => {
|
||||
// Second observer requires 'buffered: true' to see an entry.
|
||||
new PerformanceObserver(t.step_func_done(list => {
|
||||
const entries = list.getEntries();
|
||||
assert_equals(entries.length, 1, 'There should be 1 resource entry.');
|
||||
assert_equals(entries[0].entryType, 'resource');
|
||||
assert_greater_than(entries[0].startTime, 0);
|
||||
assert_greater_than(entries[0].responseEnd, entries[0].startTime);
|
||||
assert_greater_than(entries[0].duration, 0);
|
||||
assert_true(entries[0].name.endsWith('resources/empty.js'));
|
||||
})).observe({'type': 'resource', buffered: true});
|
||||
}).observe({'entryTypes': ['resource']});
|
||||
fetch('resources/empty.js');
|
||||
}, 'PerformanceObserver with buffered flag sees previous resource entries.');
|
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing: test behavior for cached resources</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/observe-entry.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Description</h1>
|
||||
<p>Test that a reused resource only appears in the buffer once.</p>
|
||||
<script>
|
||||
// Need our own image loading helper because the one in resource-loaders.js
|
||||
// is desgined to always side-step the HTTP cache but this test relies on the
|
||||
// second request being resolved from the cache.
|
||||
const load_image = path => new Promise(resolve => {
|
||||
const img = document.createElement('img');
|
||||
img.onload = img.onerror = () => resolve();
|
||||
img.src = path;
|
||||
document.body.append(img);
|
||||
});
|
||||
|
||||
promise_test(async () => {
|
||||
const blue = "resources/blue.png";
|
||||
|
||||
// First request. Should appear in the timeline.
|
||||
await load_image(blue + "?cacheable");
|
||||
|
||||
// Second request. Should not appear in the timeline.
|
||||
await load_image(blue + "?cacheable");
|
||||
|
||||
// Third request. When this request shows up in the timeline, we know that, if
|
||||
// the second request would generate an entry, that entry would have already
|
||||
// shown up in the timeline. Without this, we'd need to guess at how long to
|
||||
// wait which tends to be flaky.
|
||||
await load_image(blue + "?avoid-cache");
|
||||
|
||||
const entries = await new Promise(resolve => {
|
||||
const accumulator = [];
|
||||
new PerformanceObserver(entry_list => {
|
||||
entry_list.getEntries().forEach(entry => {
|
||||
if (!entry.name.includes("blue.png")) {
|
||||
// Ignore resources other than blue images.
|
||||
return;
|
||||
}
|
||||
accumulator.push(entry);
|
||||
|
||||
// Once we see the 'canary' resource, we don't need to wait anymore.
|
||||
if (entry.name.endsWith('avoid-cache')) {
|
||||
resolve(accumulator);
|
||||
}
|
||||
});
|
||||
}).observe({'type': 'resource', 'buffered': true});
|
||||
});
|
||||
|
||||
assert_equals(entries.length, 2, "There must be exactly 2 entries in the " +
|
||||
"Performance Timeline");
|
||||
assert_true(entries[0].name.endsWith("blue.png?cacheable"));
|
||||
assert_true(entries[1].name.endsWith("blue.png?avoid-cache"));
|
||||
}, "When a resource is resolved from cache, there must not be a " +
|
||||
"corresponding entry in the Performance Timeline");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates the functionality of clearResourceTimings method
|
||||
in resource timing.</title>
|
||||
<link rel="author" title="Intel" href="http://www.intel.com/" />
|
||||
<link rel="help"
|
||||
href="https://www.w3.org/TR/resource-timing-2/#dom-performance-clearresourcetimings">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script>
|
||||
test(() => {
|
||||
assert_equals(performance.getEntriesByType("resource").length, 2,
|
||||
"Resource timing entries exist");
|
||||
performance.clearResourceTimings();
|
||||
assert_equals(performance.getEntriesByType("resource").length, 0,
|
||||
"Resource timing entries are cleared");
|
||||
}, "Test that clearResourceTimings() clears the performance timeline buffer");
|
||||
</script>
|
||||
</head>
|
||||
</html>
|
|
@ -0,0 +1,56 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing connection reuse</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="resources/entry-invariants.js"></script>
|
||||
<script src="resources/connection-reuse-test.js"></script>
|
||||
<script>
|
||||
const {HTTPS_ORIGIN} = get_host_info();
|
||||
|
||||
// Fetches the given subresource a couple times with the same connection.
|
||||
const http_path = "resources/fake_responses.py";
|
||||
connection_reuse_test(http_path,
|
||||
{
|
||||
'on_200': invariants.assert_tao_pass_no_redirect_http,
|
||||
'on_304': invariants.assert_tao_pass_304_not_modified_http,
|
||||
}, "Reuse HTTP connection");
|
||||
|
||||
// Like above, but the subresource is fetched over HTTPS while this page is
|
||||
// fetched over HTTP.
|
||||
const https_url = `${HTTPS_ORIGIN}/resource-timing/${http_path}`;
|
||||
connection_reuse_test(https_url,
|
||||
{
|
||||
'on_200': invariants.assert_tao_pass_no_redirect_https,
|
||||
'on_304': invariants.assert_tao_pass_304_not_modified_https,
|
||||
}, "Reuse HTTPS connection from HTTP page");
|
||||
|
||||
// Like the above mixed-content test but the final resource is behind an HTTP
|
||||
// redirect response.
|
||||
const redirect_path = (() => {
|
||||
// The resource behind the redirect is the same fake_responses.py handler
|
||||
// on the HTTPS origin. Pass it through encodeURIComponent so that it can
|
||||
// be passed through a query-parameter.
|
||||
const redirect_url = encodeURIComponent(https_url)
|
||||
// The request is made to the HTTPS origin with a query parameter that will
|
||||
// cause a 302 response.
|
||||
return `${https_url}?redirect=${redirect_url}`;
|
||||
})();
|
||||
connection_reuse_test(redirect_path,
|
||||
{
|
||||
'on_200': invariants.assert_tao_enabled_cross_origin_redirected_resource,
|
||||
'on_304': invariants.assert_tao_enabled_cross_origin_redirected_resource,
|
||||
}, "Reuse HTTPS connection with redirects from an HTTP page");
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Description</h1>
|
||||
<p>See <a href="resources/connection-reuse-test.js">the included test
|
||||
script</a></p>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,25 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing connection reuse</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/entry-invariants.js"></script>
|
||||
<script src="resources/connection-reuse-test.js"></script>
|
||||
<script>
|
||||
connection_reuse_test("resources/fake_responses.py",
|
||||
{
|
||||
'on_200': invariants.assert_tao_pass_no_redirect_https,
|
||||
'on_304': invariants.assert_tao_pass_304_not_modified_https,
|
||||
}, "Reuse an HTTPS connection");
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Description</h1>
|
||||
<p>See <a href="resources/connection-reuse-test.js">the included test
|
||||
script</a></p>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,49 @@
|
|||
// META: script=/common/utils.js
|
||||
// META: script=/common/get-host-info.sub.js
|
||||
|
||||
// Because apache decrements the Keep-Alive max value on each request, the
|
||||
// transferSize will vary slightly between requests for the same resource.
|
||||
const fuzzFactor = 3; // bytes
|
||||
|
||||
const {HTTP_REMOTE_ORIGIN} = get_host_info();
|
||||
const url = new URL('/resource-timing/resources/preflight.py',
|
||||
HTTP_REMOTE_ORIGIN).href;
|
||||
|
||||
// The header bytes are expected to be > |minHeaderSize| and
|
||||
// < |maxHeaderSize|. If they are outside this range the test will fail.
|
||||
const minHeaderSize = 100;
|
||||
const maxHeaderSize = 1024;
|
||||
|
||||
promise_test(async () => {
|
||||
const checkCorsAllowed = response => response.arrayBuffer();
|
||||
const requirePreflight = {headers: {'X-Require-Preflight' : '1'}};
|
||||
const collectEntries = new Promise(resolve => {
|
||||
let entriesSeen = [];
|
||||
new PerformanceObserver(entryList => {
|
||||
entriesSeen = entriesSeen.concat(entryList.getEntries());
|
||||
if (entriesSeen.length > 2) {
|
||||
throw new Error(`Saw too many PerformanceResourceTiming entries ` +
|
||||
`(${entriesSeen.length})`);
|
||||
}
|
||||
if (entriesSeen.length == 2) {
|
||||
resolve(entriesSeen);
|
||||
}
|
||||
}).observe({"type": "resource"});
|
||||
});
|
||||
|
||||
// Although this fetch doesn't send a pre-flight request, the server response
|
||||
// will allow cross-origin requests explicitly with the
|
||||
// Access-Control-Allow-Origin header.
|
||||
await fetch(url).then(checkCorsAllowed);
|
||||
|
||||
// This fetch will send a pre-flight request to do the CORS handshake
|
||||
// explicitly.
|
||||
await fetch(url, requirePreflight).then(checkCorsAllowed);
|
||||
|
||||
const entries = await collectEntries;
|
||||
assert_greater_than(entries[0].transferSize, 0, 'No-preflight transferSize');
|
||||
const lowerBound = entries[0].transferSize - fuzzFactor;
|
||||
const upperBound = entries[0].transferSize + fuzzFactor;
|
||||
assert_between_exclusive(entries[1].transferSize, lowerBound, upperBound,
|
||||
'Preflighted transferSize');
|
||||
}, 'PerformanceResourceTiming sizes fetch with preflight test');
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Test ResourceTiming reporting for cross-origin iframe.</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="resources/entry-invariants.js"></script>
|
||||
<script src="resources/observe-entry.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<body>
|
||||
<script>
|
||||
const {REMOTE_ORIGIN} = get_host_info();
|
||||
|
||||
promise_test(async t => {
|
||||
const iframe = document.createElement('iframe');
|
||||
t.add_cleanup(() => iframe.remove());
|
||||
iframe.src = `${REMOTE_ORIGIN}/resource-timing/resources/green.html`;
|
||||
document.body.appendChild(iframe);
|
||||
const entry = await observe_entry(iframe.src);
|
||||
invariants.assert_tao_failure_resource(entry);
|
||||
}, "A cross-origin iframe should report an opaque RT entry");
|
||||
|
||||
promise_test(async t => {
|
||||
const iframe = document.createElement('iframe');
|
||||
t.add_cleanup(() => iframe.remove());
|
||||
iframe.src = `${REMOTE_ORIGIN}/resource-timing/resources/TAOResponse.py?tao=wildcard`;
|
||||
document.body.appendChild(iframe);
|
||||
const entry = await observe_entry(iframe.src);
|
||||
invariants.assert_tao_pass_no_redirect_http(entry);
|
||||
}, "A cross-origin iframe with TAO enabled should report a full RT entry");
|
||||
|
||||
</script>
|
|
@ -0,0 +1,102 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates the values in resource timing for cross-origin
|
||||
redirects.</title>
|
||||
<link rel="author" title="Intel" href="http://www.intel.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/custom-cors-response.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="resources/entry-invariants.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/tao-response.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const {ORIGIN, REMOTE_ORIGIN} = get_host_info();
|
||||
|
||||
const HTTP_SO_to_XO_redirect_url = url => {
|
||||
// Make an initial request to a same-domain resource that will return a 302
|
||||
// redirect to the given (possibly cross-origin) url.
|
||||
return `/resource-timing/resources/redirect-cors.py?location=${url}`;
|
||||
};
|
||||
|
||||
const HTTP_SO_resource = () => {
|
||||
if (location.protocol != "http:") {
|
||||
throw new Error("Can only make an HTTP SO request if this page was " +
|
||||
"served over HTTP.");
|
||||
}
|
||||
return tao_response("*", ORIGIN);
|
||||
};
|
||||
|
||||
const HTTP_XO_redirect = (url, tao) => {
|
||||
const ret = new URL(
|
||||
`${REMOTE_ORIGIN}/resource-timing/resources/redirect-cors.py`);
|
||||
ret.searchParams.append("location", url);
|
||||
ret.searchParams.append("allow_origin", "*");
|
||||
ret.searchParams.append("timing_allow_origin", tao);
|
||||
return ret.href;
|
||||
};
|
||||
|
||||
attribute_test(
|
||||
load.iframe, HTTP_SO_to_XO_redirect_url(custom_cors_response({},
|
||||
REMOTE_ORIGIN)),
|
||||
invariants.assert_http_to_cross_origin_redirected_resource,
|
||||
"Verify that cross-origin resources' timings aren't exposed through HTTP " +
|
||||
"redirects.");
|
||||
|
||||
attribute_test(
|
||||
load.iframe, HTTP_SO_to_XO_redirect_url(remote_tao_response("no-match")),
|
||||
invariants.assert_cross_origin_redirected_resource,
|
||||
"Verify that a redirected cross-origin resources' timings aren't exposed " +
|
||||
"when the TAO check fails.");
|
||||
|
||||
attribute_test(
|
||||
load.iframe, HTTP_SO_to_XO_redirect_url(remote_tao_response("*")),
|
||||
invariants.assert_http_to_tao_enabled_cross_origin_https_redirected_resource,
|
||||
"Verify that cross-origin resources' timings are exposed when the TAO " +
|
||||
"check succeeds. Also verify that secureConnectionStart is 0 since the " +
|
||||
"original request was over HTTP.");
|
||||
|
||||
attribute_test(
|
||||
load.iframe, HTTP_XO_redirect(HTTP_XO_redirect(HTTP_SO_resource(), "*"), "*"),
|
||||
invariants.assert_http_to_tao_enabled_cross_origin_https_redirected_resource,
|
||||
"Verify that a redirect chain through cross-origin resources have their " +
|
||||
"timings exposed when all TAO checks succeed. Also verify that " +
|
||||
"secureConnectionStart is 0 since the original request was over HTTP.");
|
||||
|
||||
const failure_permutations = [
|
||||
["fail", "fail", "fail"],
|
||||
["fail", "fail", "*" ],
|
||||
["fail", "*", "fail"],
|
||||
["fail", "*", "*" ],
|
||||
["*", "fail", "fail"],
|
||||
["*", "fail", "*" ],
|
||||
["*", "*", "fail"],
|
||||
];
|
||||
const test_case = (so_tao, xo1_tao, xo2_tao) => {
|
||||
return HTTP_XO_redirect(HTTP_XO_redirect(HTTP_SO_resource(
|
||||
so_tao), xo2_tao), xo1_tao);
|
||||
};
|
||||
const test_label = perm => {
|
||||
return perm.map(x => {
|
||||
if (x == "*" ) return "PASS";
|
||||
if (x == "fail" ) return "FAIL";
|
||||
throw new Error(`unexpected element ${x}`);
|
||||
}).join(" -> ");
|
||||
};
|
||||
for (const permutation of failure_permutations) {
|
||||
attribute_test(
|
||||
load.iframe, test_case.apply(permutation),
|
||||
invariants.assert_tao_failure_resource,
|
||||
`Verify that a redirect chain through cross-origin resources do not have ` +
|
||||
`their timings exposed when any of the TAO checks fail. ` +
|
||||
`(${test_label(permutation)})`);
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
32
test/fixtures/wpt/resource-timing/cross-origin-start-end-time-with-redirects.html
vendored
Normal file
32
test/fixtures/wpt/resource-timing/cross-origin-start-end-time-with-redirects.html
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates the values in resource timing for cross-origin
|
||||
redirects.</title>
|
||||
<link rel="author" title="Noam Rosenthal" href="noam@webkit.org">
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/entry-invariants.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const {REMOTE_ORIGIN} = get_host_info();
|
||||
const delay = 2
|
||||
const blank_page = `/resource-timing/resources/blank_page_green.htm`;
|
||||
const destUrl = `/common/slow-redirect.py?delay=${delay}&location=${REMOTE_ORIGIN}/${blank_page}`;
|
||||
|
||||
const timeBefore = performance.now()
|
||||
attribute_test(load.iframe, destUrl, entry => {
|
||||
assert_equals(entry.startTime, entry.fetchStart, 'startTime and fetchStart should be equal');
|
||||
assert_greater_than(entry.startTime, timeBefore, 'startTime and fetchStart should be greater than the time before fetching');
|
||||
// See https://github.com/w3c/resource-timing/issues/264
|
||||
assert_less_than(Math.round(entry.startTime - timeBefore), delay * 1000, 'startTime should not expose redirect delays');
|
||||
}, "Verify that cross-origin resources don't implicitly expose their redirect timings")
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,70 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Resource Timing: PerformanceResourceTiming attributes shouldn't change
|
||||
if the HTTP status code changes</title>
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src=/common/get-host-info.sub.js></script>
|
||||
</head>
|
||||
<body>
|
||||
<img id="img_200">
|
||||
<img id="img_307">
|
||||
<img id="img_404">
|
||||
<img id="img_502">
|
||||
<script id="script_200"></script>
|
||||
<script id="script_307"></script>
|
||||
<script id="script_404"></script>
|
||||
<script id="script_502"></script>
|
||||
<script>
|
||||
|
||||
const listenForPerformanceEntries = num_expected => {
|
||||
return new Promise(resolve => {
|
||||
let results = [];
|
||||
new PerformanceObserver(entryList => {
|
||||
entryList.getEntries().forEach(entry => {
|
||||
if (!entry.name.includes("status-code"))
|
||||
return;
|
||||
|
||||
results.push(entry);
|
||||
if (results.length == num_expected) {
|
||||
resolve(results);
|
||||
}
|
||||
});
|
||||
}).observe({entryTypes: ['resource']});
|
||||
});
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
const destUrl = get_host_info().HTTP_REMOTE_ORIGIN + '/resource-timing/resources/';
|
||||
const statusCodes = ['200', '307', '404', '502'];
|
||||
|
||||
let expected_entry_count = 0;
|
||||
statusCodes.forEach(status => {
|
||||
document.getElementById(`img_${status}`).src = `${destUrl}status-code.py?status=${status}`;
|
||||
document.getElementById(`script_${status}`).src = `${destUrl}status-code.py?status=${status}&script=1`;
|
||||
expected_entry_count += 2;
|
||||
});
|
||||
|
||||
const entries = await listenForPerformanceEntries(expected_entry_count);
|
||||
|
||||
// We will check that the non-timestamp values of the entry match for all
|
||||
// entries.
|
||||
const keys = [
|
||||
'entryType',
|
||||
'nextHopProtocol',
|
||||
'transferSize',
|
||||
'encodedBodySize',
|
||||
'decodedBodySize',
|
||||
];
|
||||
|
||||
const first = entries[0];
|
||||
entries.slice(1).forEach(entry => {
|
||||
keys.forEach(attribute => {
|
||||
assert_equals(entry[attribute], first[attribute],
|
||||
`There must be no discernible difference for the ${attribute} ` +
|
||||
`attribute but found a difference for the ${entry.name} resource.`);
|
||||
})});
|
||||
}, "Make sure cross origin resource fetch failures with different status codes are indistinguishable");
|
||||
</script>
|
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script>
|
||||
// Open a document on one of hosts on the web-platform test domain, so that
|
||||
// document.domain will set a valid domain, turning the frame into a
|
||||
// cross-origin frame.
|
||||
const {OTHER_ORIGIN} = get_host_info();
|
||||
const openee = window.open(OTHER_ORIGIN +
|
||||
"/resource-timing/resources/document-domain-no-impact.html");
|
||||
fetch_tests_from_window(openee);
|
||||
</script>
|
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates that a failed cross-origin fetch creates an opaque network timing entry.
|
||||
</title>
|
||||
<link rel="author" title="Noam Rosenthal" href="noam@webkit.org">
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="resources/entry-invariants.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const validDataURL = 'data:,Hello%2C%20World%21'
|
||||
const {REMOTE_ORIGIN, ORIGINAL_HOST, HTTP_PORT} = get_host_info();
|
||||
const validXmlUrl = '/common/dummy.xml';
|
||||
|
||||
network_error_entry_test(
|
||||
`${REMOTE_ORIGIN}${validXmlUrl}`, null, `failed cross-origin requests`);
|
||||
|
||||
network_error_entry_test(`/common/redirect.py?location=${validDataURL}`, null, "non-HTTP redirect");
|
||||
network_error_entry_test('//{{hosts[][nonexistent]}}/common/dummy.xml', null, "DNS failure");
|
||||
network_error_entry_test(`http://${ORIGINAL_HOST}:${HTTP_PORT}/commo/dummy.xml`, null, "Mixed content");
|
||||
|
||||
network_error_entry_test('/common/dummy.xml', {cache: 'only-if-cached', mode: 'same-origin'},
|
||||
"only-if-cached resource that was not cached");
|
||||
|
||||
network_error_entry_test(
|
||||
`/element-timing/resources/multiple-redirects.py?redirect_count=22&final_resource=${validXmlUrl}`,
|
||||
null, "too many redirects");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,39 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing: PerformanceResourceTiming attributes</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help"
|
||||
href="https://www.w3.org/TR/resource-timing-2/#timing-allow-origin"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/entry-invariants.js"></script>
|
||||
<script>
|
||||
attribute_test(
|
||||
load.image, "resources/fake_responses.py#hash=1",
|
||||
entry => {
|
||||
assert_true(entry.name.includes('#hash=1'),
|
||||
"There should be a hash in the resource name");
|
||||
invariants.assert_tao_pass_no_redirect_http(entry);
|
||||
},
|
||||
"Image resources should generate conformant entries");
|
||||
|
||||
attribute_test(
|
||||
load.font, "/fonts/Ahem.ttf",
|
||||
invariants.assert_tao_pass_no_redirect_http,
|
||||
"Font resources should generate conformant entries");
|
||||
|
||||
attribute_test(
|
||||
load.image, "/common/redirect.py?location=resources/fake_responses.py",
|
||||
invariants.assert_same_origin_redirected_resource,
|
||||
"Same-origin redirects should populate redirectStart/redirectEnd");
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Description</h1>
|
||||
<p>This test validates that PerformanceResourceTiming entries' attributes are
|
||||
populated with the correct values.</p>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="timeout" content="long">
|
||||
<title>Resource Timing: EventSource timing behavior</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resource-timing/resources/observe-entry.js"></script>
|
||||
</head>
|
||||
</script>
|
||||
<script>
|
||||
async_test(t => {
|
||||
const repetitions = 2;
|
||||
const url = new URL(`/eventsource/resources/message.py`, location.href);
|
||||
const eventSource = new EventSource(url);
|
||||
let messages = 0;
|
||||
t.add_cleanup(() => eventSource.close());
|
||||
eventSource.addEventListener('message', () => {
|
||||
++messages;
|
||||
})
|
||||
|
||||
new PerformanceObserver(() => {
|
||||
const entries = performance.getEntriesByName(url);
|
||||
assert_greater_than_equal(entries.length, messages - 1);
|
||||
if (entries.length === repetitions)
|
||||
t.done();
|
||||
}).observe({type: 'resource'});
|
||||
}, "ResourceTiming for EventSource should reflect number of re-connections to source");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Test cross-origin fetch redirects have the right values.</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="resources/entry-invariants.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script>
|
||||
|
||||
const {REMOTE_ORIGIN, ORIGIN} = get_host_info();
|
||||
const redirect = "/common/redirect.py?" +
|
||||
"location=/resource-timing/resources/green.html";
|
||||
const cross_origin_redirect = REMOTE_ORIGIN + redirect;
|
||||
const same_origin_redirect = ORIGIN + redirect;
|
||||
|
||||
attribute_test(
|
||||
url => fetch(url, {mode: "no-cors", credentials: "include"}),
|
||||
new URL(cross_origin_redirect).href,
|
||||
invariants.assert_cross_origin_redirected_resource,
|
||||
"Test fetching through a cross-origin redirect URL"
|
||||
);
|
||||
|
||||
attribute_test(
|
||||
url => fetch(url, {mode: "no-cors", credentials: "include"}),
|
||||
new URL(same_origin_redirect).href,
|
||||
invariants.assert_same_origin_redirected_resource,
|
||||
"Test fetching through a same-origin redirect URL"
|
||||
);
|
||||
|
||||
</script>
|
|
@ -0,0 +1,62 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Test cross-origin fetch redirects have the right values.</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
const load_font = url => {
|
||||
document.body.innerHTML = `
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: ahem;
|
||||
src: url('${url}');
|
||||
}
|
||||
</style>
|
||||
<div style="font-family: ahem;">This fetches ahem font.</div>
|
||||
`;
|
||||
return document.fonts.ready;
|
||||
};
|
||||
|
||||
const run_test = async (t, url) => {
|
||||
// Set up PerformanceObserver
|
||||
const href = new URL(url).href;
|
||||
const setPerformanceObserver = new Promise(resolve => {
|
||||
const po = new PerformanceObserver(resolve);
|
||||
po.observe({type: "resource"});
|
||||
});
|
||||
|
||||
// Load the font resource and wait for it to be fetched.
|
||||
await load_font(href);
|
||||
|
||||
// Wait for an entry
|
||||
const timeout = new Promise(resolve => t.step_timeout(resolve, 3000));
|
||||
const list = await Promise.race([setPerformanceObserver, timeout]);
|
||||
assert_equals(typeof(list), "object", "No iframe entry was fired");
|
||||
const entries = list.getEntriesByName(url);
|
||||
assert_equals(entries.length, 1);
|
||||
|
||||
// Test entry values
|
||||
const entry = entries[0];
|
||||
assert_greater_than(entry.fetchStart, 0, "fetchStart should be greater than 0 in redirects.");
|
||||
assert_greater_than_equal(entry.domainLookupStart, entry.fetchStart, "domainLookupStart should be more than 0 in same-origin redirect.");
|
||||
assert_greater_than_equal(entry.domainLookupEnd, entry.domainLookupStart, "domainLookupEnd should be more than 0 in same-origin redirect.");
|
||||
assert_greater_than_equal(entry.connectStart, entry.domainLookupEnd, "connectStart should be more than 0 in same-origin redirect.");
|
||||
assert_greater_than_equal(entry.secureConnectionStart, entry.connectStart, "secureConnectionStart should be more than 0 in same-origin redirect.");
|
||||
assert_greater_than_equal(entry.connectEnd, entry.secureConnectionStart, "connectEnd should be more than 0 in same-origin redirect.");
|
||||
assert_greater_than_equal(entry.requestStart, entry.connectEnd, "requestStart should be more than 0 in same-origin redirect.");
|
||||
assert_greater_than_equal(entry.responseStart, entry.requestStart, "responseStart should be more than 0 in same-origin redirect.");
|
||||
assert_greater_than_equal(entry.responseEnd, entry.responseStart, "responseEnd should be greater than 0 in redirects.");
|
||||
assert_greater_than_equal(entry.duration, 0, "duration should be greater than 0 in redirects.");
|
||||
}
|
||||
|
||||
const {HTTPS_REMOTE_ORIGIN} = get_host_info();
|
||||
promise_test(t => {
|
||||
return run_test(t, HTTPS_REMOTE_ORIGIN + "/fonts/Ahem.ttf");
|
||||
}, "Test a font's timestamps");
|
||||
|
||||
promise_test(t => {
|
||||
return run_test(t, HTTPS_REMOTE_ORIGIN + "/resource-timing/resources/cors-ahem.py?pipe=trickle(d1)");
|
||||
}, "Test a font's timestamps with delays");
|
||||
</script>
|
|
@ -0,0 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Test the sequence of events when reporting timing for frames.</title>
|
||||
<frameset>
|
||||
<frame src="resources/frameset-timing-frame.html" />
|
||||
</frameset>
|
|
@ -0,0 +1,24 @@
|
|||
// META: script=/resources/WebIDLParser.js
|
||||
// META: script=/resources/idlharness.js
|
||||
// META: timeout=long
|
||||
|
||||
'use strict';
|
||||
|
||||
// https://w3c.github.io/resource-timing/
|
||||
|
||||
idl_test(
|
||||
['resource-timing'],
|
||||
['performance-timeline', 'hr-time', 'dom', 'html'],
|
||||
idl_array => {
|
||||
try {
|
||||
self.resource = performance.getEntriesByType('resource')[0];
|
||||
} catch (e) {
|
||||
// Will be surfaced when resource is undefined below.
|
||||
}
|
||||
|
||||
idl_array.add_objects({
|
||||
Performance: ['performance'],
|
||||
PerformanceResourceTiming: ['resource']
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,108 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing - test that unsuccessful iframes create entries</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href=
|
||||
"https://www.w3.org/TR/resource-timing-2/#resources-included-in-the-performanceresourcetiming-interface"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="resources/entry-invariants.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
|
||||
// Like load.iframe but fetches the iframe under a "default-src 'none'"
|
||||
// Content-Security-Policy.
|
||||
const load_iframe_with_csp = async path => {
|
||||
return load.iframe_with_attrs(path, {"csp": "default-src 'none'"});
|
||||
};
|
||||
|
||||
// Runs a test (labeled by the given label) to verify that loading an iframe
|
||||
// with the given URL generates a PerformanceResourceTiming entry and that the
|
||||
// entry does not expose sensitive timing attributes.
|
||||
const masked_entry_test = (url, label) => {
|
||||
return attribute_test(load.iframe, url,
|
||||
invariants.assert_tao_failure_resource, label);
|
||||
};
|
||||
|
||||
// Runs a test (labeled by the given label) to verify that loading an iframe
|
||||
// with the given URL generates a PerformanceResourceTiming entry and that the
|
||||
// entry does expose sensitive timing attributes.
|
||||
const unmasked_entry_with_csp_test = (url, label) => {
|
||||
return attribute_test(load_iframe_with_csp, url,
|
||||
invariants.assert_tao_pass_no_redirect_http, label);
|
||||
};
|
||||
|
||||
// Runs a test (labeled by the given label) to verify that loading an iframe
|
||||
// with the given URL under a "default-src 'none' Content-Security-Policy
|
||||
// generates a PerformanceResourceTiming entry and that the entry does not
|
||||
// expose sensitive timing attributes.
|
||||
const masked_entry_with_csp_test = (url, label) => {
|
||||
return attribute_test(load_iframe_with_csp, url,
|
||||
invariants.assert_tao_failure_resource, label);
|
||||
};
|
||||
|
||||
// Runs a test (labeled by the given label) to verify that loading an iframe
|
||||
// with the given URL, an empty response body and under a "default-src 'none'
|
||||
// Content-Security-Policy generates a PerformanceResourceTiming entry and that
|
||||
// the entry does expose sensitive timing attributes.
|
||||
const empty_unmasked_entry_with_csp_test = (url, label) => {
|
||||
return attribute_test(load_iframe_with_csp, url,
|
||||
invariants.assert_tao_pass_no_redirect_http_empty, label);
|
||||
};
|
||||
|
||||
const {REMOTE_ORIGIN, ORIGINAL_HOST, HTTPS_PORT} = get_host_info();
|
||||
const unhosted_url = `https://nonexistent.${ORIGINAL_HOST}:${HTTPS_PORT}/`;
|
||||
|
||||
masked_entry_test(
|
||||
unhosted_url,
|
||||
"Test iframe from non-existent host gets reported");
|
||||
|
||||
masked_entry_test(
|
||||
"/resource-timing/resources/fake_responses.py?redirect=" + unhosted_url,
|
||||
"Test iframe redirecting to non-existent host gets reported");
|
||||
|
||||
unmasked_entry_with_csp_test("/resource-timing/resources/csp-default-none.html",
|
||||
"Same-origin iframe that complies with CSP attribute gets reported");
|
||||
|
||||
unmasked_entry_with_csp_test("/resource-timing/resources/green-frame.html",
|
||||
"Same-origin iframe that doesn't comply with CSP attribute gets reported");
|
||||
|
||||
masked_entry_with_csp_test(
|
||||
new URL("/resource-timing/resources/csp-default-none.html", REMOTE_ORIGIN),
|
||||
"Cross-origin iframe that complies with CSP attribute gets reported");
|
||||
|
||||
masked_entry_with_csp_test(
|
||||
new URL("/resource-timing/resources/green-frame.html", REMOTE_ORIGIN),
|
||||
"Cross-origin iframe that doesn't comply with CSP attribute gets reported");
|
||||
|
||||
empty_unmasked_entry_with_csp_test(
|
||||
"/resource-timing/resources/200_empty.asis",
|
||||
"Same-origin empty iframe with a 200 status gets reported");
|
||||
|
||||
masked_entry_with_csp_test(
|
||||
new URL("/resource-timing/resources/200_empty.asis", REMOTE_ORIGIN),
|
||||
"Cross-origin empty iframe with a 200 status gets reported");
|
||||
|
||||
unmasked_entry_with_csp_test(
|
||||
new URL("/resource-timing/resources/204_empty.asis"),
|
||||
"Same-origin empty iframe with a 204 status gets reported");
|
||||
|
||||
unmasked_entry_with_csp_test(
|
||||
new URL("/resource-timing/resources/205_empty.asis"),
|
||||
"Same-origin empty iframe with a 205 status gets reported");
|
||||
|
||||
masked_entry_with_csp_test(
|
||||
new URL("/resource-timing/resources/204_empty.asis", REMOTE_ORIGIN),
|
||||
"Cross-origin empty iframe with a 204 status gets reported");
|
||||
|
||||
masked_entry_with_csp_test(
|
||||
new URL("/resource-timing/resources/205_empty.asis", REMOTE_ORIGIN),
|
||||
"Cross-origin empty iframe with a 205 status gets reported");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Test the sequence of events when reporting iframe timing.</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
function test(href, type) {
|
||||
promise_test(async t => {
|
||||
await load.iframe(href);
|
||||
const entries = performance.getEntriesByType('resource').filter(({name}) => name.includes(href));
|
||||
assert_equals(entries.length, 1);
|
||||
assert_equals(entries[0].initiatorType, 'iframe');
|
||||
}, `Iframes should report resource timing for ${type} iframes`);
|
||||
}
|
||||
|
||||
test('/common/square.png', 'image');
|
||||
test('/common/dummy.xhtml', 'xhtml');
|
||||
test('/common/dummy.xml', 'xml');
|
||||
test('/common/text-plain.txt', 'text');
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Test the sequence of events when reporting iframe timing.</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(async t => {
|
||||
const href = new URL('resources/redirect-without-location.py', location.href);
|
||||
await load.iframe(href);
|
||||
const entries = performance.getEntriesByType('resource').filter(({name}) => name.startsWith(href));
|
||||
assert_equals(entries.length, 1);
|
||||
assert_equals(entries[0].initiatorType, 'iframe');
|
||||
}, 'Iframes should report resource timing for redirect responses without a location');
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Test the sequence of events when reporting iframe timing.</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/frame-timing.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
test_frame_timing_before_load_event('iframe');
|
||||
test_frame_timing_change_src('iframe');
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,24 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Test the sequence of events when reporting iframe timing.</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(async t => {
|
||||
const href = new URL('resources/download.asis', location.href);
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.src = href;
|
||||
const errored = new Promise(resolve => iframe.addEventListener('error', resolve));
|
||||
const loaded = new Promise(resolve => iframe.addEventListener('load', resolve));
|
||||
document.body.appendChild(iframe);
|
||||
const timeout = 2000;
|
||||
t.add_cleanup(() => iframe.remove());
|
||||
const expired = new Promise(resolve => t.step_timeout(resolve, timeout));
|
||||
await Promise.any([loaded, expired, errored]);
|
||||
const entries = performance.getEntriesByType('resource').filter(({name}) => name.startsWith(href));
|
||||
assert_equals(entries.length, 0);
|
||||
}, 'Iframes should not report resource timing for non-handled mime-types (downloads)');
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Test the sequence of events when reporting image timing.</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
function test_image_sequence(src, event, t) {
|
||||
const image = document.createElement('img');
|
||||
const absoluteURL = new URL(src, location.href).toString();
|
||||
document.body.appendChild(image);
|
||||
t.add_cleanup(() => image.remove());
|
||||
return new Promise(resolve => {
|
||||
image.addEventListener(event, t.step_func(() => {
|
||||
assert_equals(performance.getEntriesByName(absoluteURL).length, 1);
|
||||
resolve();
|
||||
}));
|
||||
image.src = src;
|
||||
});
|
||||
}
|
||||
promise_test(t => test_image_sequence('resources/blue.png', 'load', t),
|
||||
"An image should receive its load event after the ResourceTiming entry is available");
|
||||
|
||||
promise_test(t => test_image_sequence('resources/nothing-at-all.png', 'error', t),
|
||||
"A non-existent (404) image should receive its error event after the ResourceTiming entry is available");
|
||||
|
||||
promise_test(t => test_image_sequence('resources/invalid.png', 'error', t),
|
||||
"An invalid image should receive its error event after the ResourceTiming entry is available");
|
||||
</script>
|
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates that the initiatorType information for various
|
||||
Resource Timing entries is accurate for scripts.</title>
|
||||
<link rel="help"
|
||||
href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/entry-invariants.js"></script>
|
||||
<!-- Tested resources -->
|
||||
<script src="resources/empty_script.js?id=blocking"></script>
|
||||
<script src="resources/empty_script.js?id=async" async></script>
|
||||
<script src="resources/empty_script.js?id=async_false" async=false></script>
|
||||
<script src="resources/empty_script.js?id=defer" defer></script>
|
||||
<script>
|
||||
document.write("<script src='resources/empty_script.js?id=doc_written'></scr"
|
||||
+ "ipt>");
|
||||
|
||||
const head = document.getElementsByTagName("head")[0];
|
||||
const s1 = document.createElement("script");
|
||||
s1.src = "empty_script.js?id=appended";
|
||||
head.appendChild(s1);
|
||||
|
||||
const s2 = document.createElement("script");
|
||||
s2.src = "empty_script.js?id=appended_async";
|
||||
s2.async = true;
|
||||
head.appendChild(s2);
|
||||
|
||||
const s3 = document.createElement("script");
|
||||
s3.src = "empty_script.js?id=appended_aync_false";
|
||||
s3.async = false;
|
||||
head.appendChild(s3);
|
||||
|
||||
const s4 = document.createElement("script");
|
||||
s4.src = "empty_script.js?id=appended_defer";
|
||||
s4.defer = true;
|
||||
head.appendChild(s4);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
|
||||
const wait_for_onload = () => {
|
||||
return new Promise(resolve => {
|
||||
window.addEventListener("load", resolve);
|
||||
})};
|
||||
|
||||
promise_test(
|
||||
async () => {
|
||||
await wait_for_onload();
|
||||
|
||||
const entry_list = performance.getEntriesByType("resource");
|
||||
for (entry of entry_list) {
|
||||
if (entry.name.includes("empty_script.js")) {
|
||||
assert_equals(entry.initiatorType, "script",
|
||||
"initiatorType should be 'script' for " + entry.name);
|
||||
}
|
||||
}
|
||||
}, "Validate initiatorType for scripts is 'script'");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing initiator type: audio</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resource-timing/resources/observe-entry.js"></script>
|
||||
<script src="resources/initiator-type-test.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<audio src="/resource-timing/resources/empty.py?id=src"></audio>
|
||||
<audio>
|
||||
<source src="/resource-timing/resources/empty.py?id=source-wav"
|
||||
type="audio/wav" />
|
||||
</audio>
|
||||
<audio>
|
||||
<source src="/resource-timing/resources/empty.py?id=source-mpeg"
|
||||
type="audio/mpeg" />
|
||||
</audio>
|
||||
<audio>
|
||||
<source src="/resource-timing/resources/empty.py?id=source-ogg"
|
||||
type="audio/ogg" />
|
||||
</audio>
|
||||
<script>
|
||||
initiator_type_test("empty.py?id=src", "audio", "<audio src> without 'type' attribute");
|
||||
initiator_type_test("empty.py?id=source-wav", "audio", "<source src> with type 'audio/wav'");
|
||||
initiator_type_test("empty.py?id=source-mpeg", "audio", "<source src> with type 'audio/mpeg'");
|
||||
initiator_type_test("empty.py?id=source-ogg", "audio", "<source src> with type 'audio/ogg'");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,41 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing - initiatorType with dynamic insertion</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resource-timing/resources/entry-invariants.js"></script>
|
||||
<script src="/resource-timing/resources/resource-loaders.js"></script>
|
||||
<script>
|
||||
const dynamic_initiator_type_test = (loader, path, expected_type,
|
||||
resource_type) => {
|
||||
attribute_test(loader, path, entry => {
|
||||
assert_equals(entry.initiatorType, expected_type);
|
||||
}, `A ${resource_type} should have the '${expected_type}' initiator type.`);
|
||||
};
|
||||
|
||||
dynamic_initiator_type_test(load.image, "resources/resource_timing_test0.png",
|
||||
"img", "image");
|
||||
// Note that, to download a font, 'load.font' uses a <style> element to
|
||||
// construct a font-face that is then applied to a <div>. Since it's a <style>
|
||||
// element requesting the resource, the initiator type is 'css', not 'font'.
|
||||
dynamic_initiator_type_test(load.font, "/fonts/Ahem.ttf", "css", "font");
|
||||
dynamic_initiator_type_test(load.stylesheet,
|
||||
"resources/resource_timing_test0.css", "link", "stylesheet");
|
||||
dynamic_initiator_type_test(load.iframe, "resources/green.html", "iframe",
|
||||
"iframe");
|
||||
dynamic_initiator_type_test(load.script, "resources/empty.js", "script",
|
||||
"script");
|
||||
dynamic_initiator_type_test(load.xhr_sync, "resources/empty.py",
|
||||
"xmlhttprequest", "XMLHttpRequest");
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Description</h1>
|
||||
<p>This test validates that the initiatorType field is correct even when an
|
||||
element is dynamically inserted.</p>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing initiator type: embed</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resource-timing/resources/observe-entry.js"></script>
|
||||
<script src="resources/initiator-type-test.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<embed src="/resource-timing/resources/resource_timing_test0.css"
|
||||
type="text/css">
|
||||
<script>
|
||||
initiator_type_test("resource_timing_test0.css", "embed", "<embed>");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing initiator type: frameset</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resource-timing/resources/observe-entry.js"></script>
|
||||
<script src="resources/initiator-type-test.js"></script>
|
||||
<script>
|
||||
initiator_type_test("green.html", "frame", "<frame> in a <frameset>");
|
||||
</script>
|
||||
</head>
|
||||
<!-- Although framesets were deprecated in HTML5, we still want to make sure
|
||||
Resource Timing is emitting entries for the underlying resources' requests.
|
||||
-->
|
||||
<frameset>
|
||||
<frame src="/resource-timing/resources/green.html">
|
||||
</frameset>
|
||||
</html>
|
|
@ -0,0 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing initiator type: iframe</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resource-timing/resources/observe-entry.js"></script>
|
||||
<script src="resources/initiator-type-test.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<iframe src="/resource-timing/resources/green.html"></iframe>
|
||||
<script>
|
||||
initiator_type_test("green.html", "iframe", "<iframe>");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,21 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing initiator type: img with srcset attribute</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resource-timing/resources/observe-entry.js"></script>
|
||||
<script src="resources/initiator-type-test.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<img src="/resource-timing/resources/resource_timing_test0.png"
|
||||
srcset="/resource-timing/resources/resource_timing_test0.png?id=srcset 67w"
|
||||
sizes="67px"></img>
|
||||
<script>
|
||||
initiator_type_test("resource_timing_test0.png?id=srcset", "img", "<img srcset>");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing initiator type: img</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resource-timing/resources/observe-entry.js"></script>
|
||||
<script src="resources/initiator-type-test.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<img src="/resource-timing/resources/resource_timing_test0.png"></img>
|
||||
<script>
|
||||
initiator_type_test("resource_timing_test0.png", "img", "<img>");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing initiator type: input</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resource-timing/resources/observe-entry.js"></script>
|
||||
<script src="resources/initiator-type-test.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<input type="image" src="/resource-timing/resources/resource_timing_test0.png">
|
||||
<script>
|
||||
initiator_type_test("resource_timing_test0.png", "input", "<input type=image>");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing initiator type: link</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resource-timing/resources/observe-entry.js"></script>
|
||||
<script src="resources/initiator-type-test.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<link rel="stylesheet" href="/resource-timing/resources/nested.css">
|
||||
<link rel="prefetch"
|
||||
href="/resource-timing/resources/resource_timing_test0.css?id=prefetch">
|
||||
<link rel="preload" as="style"
|
||||
href="/resource-timing/resources/resource_timing_test0.css?id=preload">
|
||||
<link rel="prerender" href="/resource-timing/resources/green.html?id=prerender">
|
||||
<link rel="manifest" href="/resource-timing/resources/manifest.json">
|
||||
<link rel="modulepreload" href="resources/empty.js?id=modulePreload">
|
||||
<script>
|
||||
initiator_type_test("nested.css", "link", "<link>");
|
||||
|
||||
// Verify there are enries for each of nested.css' nested resources.
|
||||
initiator_type_test("resource_timing_test0.css?id=n1", "css", "css resources embedded in css");
|
||||
initiator_type_test("fonts/Ahem.ttf?id=n1", "css", "font resources embedded in css");
|
||||
initiator_type_test("blue.png?id=n1", "css", "image resources embedded in css");
|
||||
|
||||
initiator_type_test("resource_timing_test0.css?id=prefetch", "link", "<link prefetch>");
|
||||
initiator_type_test("resource_timing_test0.css?id=preload", "link", "<link preload>");
|
||||
initiator_type_test("green.html?id=prerender", "link", "<link prerender>");
|
||||
initiator_type_test("manifest.json", "link", "<link manifest>");
|
||||
initiator_type_test("resources/empty.js?id=modulePreload", "other", "module preload");
|
||||
</script>
|
||||
<ol>This content forces a font to get fetched</ol>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,31 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing initiator type: miscellaneous elements</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resource-timing/resources/observe-entry.js"></script>
|
||||
<script src="resources/initiator-type-test.js"></script>
|
||||
</head>
|
||||
<body background="/resource-timing/resources/blue.png?id=body">
|
||||
<input type="image" src="/resource-timing/resources/blue.png?id=input">
|
||||
<object type="image/png" data="/resource-timing/resources/blue.png?id=object">
|
||||
</object>
|
||||
<script>
|
||||
navigator.sendBeacon('/resource-timing/resources/empty.py?id=beacon');
|
||||
fetch('/resource-timing/resources/empty.py?id=fetch');
|
||||
const evtSource = new EventSource('/resource-timing/resources/eventsource.py?id=eventsource');
|
||||
</script>
|
||||
<script>
|
||||
initiator_type_test("blue.png?id=body", "body", "<body background>");
|
||||
initiator_type_test("blue.png?id=input", "input", "<input type='image'>");
|
||||
initiator_type_test("blue.png?id=object", "object", "<object type='image/png'>");
|
||||
initiator_type_test("empty.py?id=beacon", "beacon", "sendBeacon()");
|
||||
initiator_type_test("empty.py?id=fetch", "fetch", "for fetch()");
|
||||
initiator_type_test("eventsource.py?id=eventsource", "other", "new EventSource()");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,39 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing initiator type: picture</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resource-timing/resources/observe-entry.js"></script>
|
||||
<script src="resources/initiator-type-test.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<picture>
|
||||
<source srcset="blue.png?id=picture-source" type="image/png" />
|
||||
<img src="blue.png?id=picture-img" />
|
||||
</picture>
|
||||
<picture>
|
||||
<source srcset="blue.png?id=picture-notsupported-source" type="image/notsupported" />
|
||||
<img src="blue.png?id=picture-notsupported-img" />
|
||||
</picture>
|
||||
<picture>
|
||||
<img src="blue.png?id=picture-img-src"
|
||||
srcset="blue.png?id=picture-img-srcset"
|
||||
sizes="67px"></img>
|
||||
</picture>
|
||||
<picture>
|
||||
<img src="blue.png?id=picture-99x-img-src"
|
||||
srcset="blue.png?id=picture-99x-img-srcset 99x"
|
||||
sizes="67px"></img>
|
||||
</picture>
|
||||
<script>
|
||||
initiator_type_test("blue.png?id=picture-source", "img", "<source> in a <picture>");
|
||||
initiator_type_test("blue.png?id=picture-notsupported-img", "img", "<img> in a <picture>");
|
||||
initiator_type_test("blue.png?id=picture-img-srcset", "img", "<img srcset> in a <picture>");
|
||||
initiator_type_test("blue.png?id=picture-99x-img-src", "img", "<img src> in a <picture>");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
15
test/fixtures/wpt/resource-timing/initiator-type/resources/initiator-type-test.js
vendored
Normal file
15
test/fixtures/wpt/resource-timing/initiator-type/resources/initiator-type-test.js
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
|
||||
if (observe_entry === undefined) {
|
||||
throw new Error("You must include resource-timing/resources/observe-entry.js "
|
||||
+ "before including this script.");
|
||||
}
|
||||
|
||||
// Asserts that, for the given name, there is/will-be a
|
||||
// PerformanceResourceTiming entry that has the given 'initiatorType'. The test
|
||||
// is labeled according to the given descriptor.
|
||||
const initiator_type_test = (entry_name, expected_initiator, descriptor) => {
|
||||
promise_test(async () => {
|
||||
const entry = await observe_entry(entry_name);
|
||||
assert_equals(entry.initiatorType, expected_initiator);
|
||||
}, `The initiator type for ${descriptor} must be '${expected_initiator}'`);
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing initiator type: script</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resource-timing/resources/observe-entry.js"></script>
|
||||
<script src="resources/initiator-type-test.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script src="/resource-timing/resources/empty_script.js"></script>
|
||||
<script>
|
||||
const async_xhr = new XMLHttpRequest;
|
||||
async_xhr.open('GET', '/resource-timing/resources/blue.png?id=async_xhr',
|
||||
true);
|
||||
async_xhr.send();
|
||||
</script>
|
||||
<script>
|
||||
initiator_type_test("empty_script.js", "script", "<script>");
|
||||
initiator_type_test("blue.png?id=async_xhr", "xmlhttprequest", "an asynchronous XmlHTTPRequest");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing initiator type: style</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resource-timing/resources/observe-entry.js"></script>
|
||||
<script src="resources/initiator-type-test.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
iframe {
|
||||
background: url('/resource-timing/resources/blue.png?id=background');
|
||||
}
|
||||
body {
|
||||
cursor: url('/resource-timing/resources/blue.png?id=cursor'), pointer;
|
||||
}
|
||||
ul {
|
||||
list-style-image: url('/resource-timing/resources/blue.png?id=list-style');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: remoteFontAhem;
|
||||
src: url('/fonts/Ahem.ttf');
|
||||
}
|
||||
.ahem {
|
||||
font-family: remoteFontAhem;
|
||||
}
|
||||
</style>
|
||||
<iframe>This iframe forces the 'background' resource to be fetched.</iframe>
|
||||
<ul>
|
||||
<li>This content forces the 'list-style-image' resource to be fetched.</li>
|
||||
</ul>
|
||||
<div class="ahem">This content forces the '@font-face' resource to be fetched.</div>
|
||||
<script>
|
||||
initiator_type_test("blue.png?id=background", "css", "'background' attributes in <style> elements");
|
||||
initiator_type_test("blue.png?id=cursor", "css", "'cursor' attributes in <style> elements");
|
||||
initiator_type_test("blue.png?id=list-style", "css", "'list-style-image' attributes in <style> elements");
|
||||
initiator_type_test("fonts/Ahem.ttf", "css", "'@font-face' resources");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing initiator type: svg</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resource-timing/resources/observe-entry.js"></script>
|
||||
<script src="resources/initiator-type-test.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<svg width=200 height=200
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<image href="/resource-timing/resources/blue.png" height="200" width="200"/>
|
||||
</svg>
|
||||
<script>
|
||||
initiator_type_test("blue.png", "image", "<image> in an <svg>");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing initiator type: video</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resource-timing/resources/observe-entry.js"></script>
|
||||
<script src="resources/initiator-type-test.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<video poster="/resource-timing/resources/blue.png?id=poster"></video>
|
||||
<video src="/media/test.mp4?id=src" autoplay="true"></video>
|
||||
<video autoplay="true">
|
||||
<source src="/media/test.mp4?id=source-mp4" type="video/mp4">
|
||||
<track kind="subtitles" srclang="en" default
|
||||
src="/resource-timing/resources/empty.py?id=track">
|
||||
</video>
|
||||
<video autoplay="true">
|
||||
<source src="/media/test.ogv?id=source-ogv" type="video/ogg">
|
||||
</video>
|
||||
<script>
|
||||
initiator_type_test("blue.png?id=poster", "video", "<video poster>");
|
||||
initiator_type_test("media/test.mp4?id=src", "video", "<video src>");
|
||||
initiator_type_test("media/test.mp4?id=source-mp4", "video", "<source src> with type=\"video/mp4\"");
|
||||
initiator_type_test("empty.py?id=track", "track", "<track src>");
|
||||
initiator_type_test("media/test.ogv?id=source-ogv", "video", "<source src> with type=\"video/ogg\"");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing initiatorType: worker resources</title>
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#dom-performanceresourcetiming-initiatortype"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resource-timing/resources/observe-entry.js"></script>
|
||||
<script src="resources/initiator-type-test.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<script>
|
||||
const moduleWorkerURL = 'resources/empty.js?moduleWorker';
|
||||
const workerURL = 'resources/empty.js?worker';
|
||||
new Worker(moduleWorkerURL, {type: "module"});
|
||||
new Worker(workerURL, {type: "classic"});
|
||||
initiator_type_test(workerURL, "other", "classic worker");
|
||||
initiator_type_test(moduleWorkerURL, "other", "module worker");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,21 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Test the sequence of events when reporting input timing.</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
async_test(t => {
|
||||
const input = document.createElement('input');
|
||||
input.type = "image";
|
||||
const absoluteURL = new URL('resources/blue.png', location.href).toString();
|
||||
t.add_cleanup(() => input.remove());
|
||||
input.addEventListener('load', t.step_func(() => {
|
||||
assert_equals(performance.getEntriesByName(absoluteURL).length, 1);
|
||||
t.done();
|
||||
}));
|
||||
input.src = absoluteURL;
|
||||
document.body.appendChild(input);
|
||||
}, "An image input element should receive its load event after the ResourceTiming entry is available");
|
||||
|
||||
</script>
|
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Test the sequence of events when reporting link timing.</title>
|
||||
<meta name="timeout" content="long">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(async t => {
|
||||
const link = document.createElement('link');
|
||||
const delay = 500;
|
||||
const src = `./resources/import.sub.css?delay=${delay}`
|
||||
const absoluteURL = new URL(src, location.href).toString();
|
||||
new PerformanceObserver(t.step_func(() => {
|
||||
const allPerformanceEntries = performance.getEntriesByType('resource');
|
||||
const linkEntry = allPerformanceEntries.find(e => e.name.includes('import.sub.css'));
|
||||
const importEntry = allPerformanceEntries.find(e => e.name.includes('delay-css'));
|
||||
if (!linkEntry || !importEntry)
|
||||
return;
|
||||
const linkEndTime = linkEntry.startTime + linkEntry.duration;
|
||||
const importEndTime = importEntry.startTime + importEntry.duration;
|
||||
assert_greater_than_equal(importEndTime, linkEndTime + delay, "link load should be done before import load");
|
||||
t.done();
|
||||
|
||||
})).observe({type: 'resource'});
|
||||
link.href = src;
|
||||
link.rel = 'stylesheet';
|
||||
document.head.appendChild(link);
|
||||
}, "test that @imports don't affect link resource timings");
|
||||
</script>
|
|
@ -0,0 +1,65 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>This tests transfer size of resource timing when loaded from memory cache.</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/entry-invariants.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
function getScript(url) {
|
||||
const script = document.createElement("script");
|
||||
const loaded = new Promise(resolve => {
|
||||
script.onload = script.onerror = resolve;
|
||||
});
|
||||
script.src = url;
|
||||
document.body.appendChild(script);
|
||||
return loaded;
|
||||
}
|
||||
function add_iframe(url) {
|
||||
return new Promise(function (resolve) {
|
||||
var frame = document.createElement('iframe');
|
||||
frame.src = url;
|
||||
frame.onload = function () { resolve(frame); };
|
||||
document.body.appendChild(frame);
|
||||
});
|
||||
}
|
||||
promise_test(async t => {
|
||||
// Add unique token to url so that each run the url is different to avoid
|
||||
// flakiness.
|
||||
let url = 'resources/resource_timing_test0.js?unique=' +
|
||||
Math.random().toString().substr(2);
|
||||
let frame;
|
||||
return add_iframe('resources/iframe-load-from-mem-cache-transfer-size.html')
|
||||
.then((f) => {
|
||||
frame = f;
|
||||
// Load script onto iframe in order to get it into the memory cache.
|
||||
return frame.contentWindow.getScript(url.split('/')[1])
|
||||
})
|
||||
.then(() => {
|
||||
// Verify that the transferSize in case of normal load is greater than
|
||||
// 0.
|
||||
assert_positive_(
|
||||
frame.contentWindow.performance.getEntriesByType('resource')
|
||||
.filter(e => e.name.includes(url))[0], ['transferSize']);
|
||||
|
||||
// Load the same script onto the parent document. This time the script
|
||||
// is coming from memory cache.
|
||||
return getScript(url);
|
||||
})
|
||||
.then(() => {
|
||||
// Verify that the transferSize in case of memory cache load is 0.
|
||||
assert_zeroed_(
|
||||
window.performance.getEntriesByType('resource')
|
||||
.filter(e => e.name.includes(url))[0], ['transferSize']);
|
||||
});
|
||||
}, "The transferSize of resource timing entries should be 0 when resource \
|
||||
is loaded from memory cache.");
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,36 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name=timeout content=long>
|
||||
<title>Resource Timing embed navigate - back button navigation</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="resources/nested-contexts.js"></script>
|
||||
<script>
|
||||
open_test_window("resources/embed-navigate-back.html",
|
||||
"Test that embed navigations are not observable by the parent, even " +
|
||||
"after history navigations by the parent");
|
||||
open_test_window("resources/embed-navigate-back.html?crossorigin",
|
||||
"Test that crossorigin embed navigations are not observable by the " +
|
||||
"parent, even after history navigations by the parent");
|
||||
open_test_window("resources/embed-navigate-back.html?cross-site",
|
||||
"Test that cross-site embed navigations are not observable by the " +
|
||||
"parent, even after history navigations by the parent");
|
||||
|
||||
open_test_window("resources/embed-navigate.html",
|
||||
"Test that embed navigations are not observable by the parent");
|
||||
open_test_window("resources/embed-navigate.html?crossorigin",
|
||||
"Test that crossorigin embed navigations are not observable by the parent");
|
||||
open_test_window("resources/embed-navigate.html?cross-site",
|
||||
"Test that cross-site embed navigations are not observable by the parent");
|
||||
open_test_window("resources/embed-refresh.html",
|
||||
"Test that embed refreshes are not observable by the parent");
|
||||
open_test_window("resources/embed-refresh.html?crossorigin",
|
||||
"Test that crossorigin embed refreshes are not observable by the parent");
|
||||
open_test_window("resources/embed-refresh.html?cross-site",
|
||||
"Test that cross-site embed refreshes are not observable by the parent");
|
||||
|
||||
</script>
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name=timeout content=long>
|
||||
<title>Resource Timing embed navigate - back button navigation</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="resources/nested-contexts.js"></script>
|
||||
<script>
|
||||
open_test_window("resources/iframe-navigate-back.html",
|
||||
"Test that iframe navigations are not observable by the parent, even after history navigations by the parent");
|
||||
open_test_window("resources/iframe-navigate-back.html?crossorigin",
|
||||
"Test that crossorigin iframe navigations are not observable by the parent, even after history navigations by the parent");
|
||||
open_test_window("resources/iframe-navigate-back.html?cross-site",
|
||||
"Test that cross-site iframe navigations are not observable by the parent, even after history navigations by the parent");
|
||||
open_test_window("resources/iframe-navigate.html",
|
||||
"Test that iframe navigations are not observable by the parent");
|
||||
open_test_window("resources/iframe-navigate.html?crossorigin",
|
||||
"Test that crossorigin iframe navigations are not observable by the parent");
|
||||
open_test_window("resources/iframe-navigate.html?cross-site",
|
||||
"Test that cross-site iframe navigations are not observable by the parent");
|
||||
open_test_window("resources/iframe-refresh.html",
|
||||
"Test that iframe refreshes are not observable by the parent");
|
||||
open_test_window("resources/iframe-refresh.html?crossorigin",
|
||||
"Test that crossorigin iframe refreshes are not observable by the parent");
|
||||
open_test_window("resources/iframe-refresh.html?cross-site",
|
||||
"Test that cross-site iframe refreshes are not observable by the parent");
|
||||
|
||||
</script>
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name=timeout content=long>
|
||||
<title>Resource Timing embed navigate - back button navigation</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="resources/nested-contexts.js"></script>
|
||||
<script>
|
||||
open_test_window("resources/object-navigate-back.html",
|
||||
"Test that object navigations are not observable by the parent, even " +
|
||||
"after history navigations by the parent");
|
||||
open_test_window("resources/object-navigate-back.html?crossorigin",
|
||||
"Test that crossorigin object navigations are not observable by the " +
|
||||
"parent, even after history navigations by the parent");
|
||||
open_test_window("resources/object-navigate-back.html?cross-site",
|
||||
"Test that cross-site object navigations are not observable by the " +
|
||||
"parent, even after history navigations by the parent");
|
||||
open_test_window("resources/object-navigate.html",
|
||||
"Test that object navigations are not observable by the parent");
|
||||
open_test_window("resources/object-navigate.html?crossorigin",
|
||||
"Test that crossorigin object navigations are not observable by the " +
|
||||
"parent");
|
||||
open_test_window("resources/object-navigate.html?cross-site",
|
||||
"Test that cross-site object navigations are not observable by the " +
|
||||
"parent");
|
||||
open_test_window("resources/object-refresh.html",
|
||||
"Test that object refreshes are not observable by the parent");
|
||||
open_test_window("resources/object-refresh.html?crossorigin",
|
||||
"Test that crossorigin object refreshes are not observable by the parent");
|
||||
open_test_window("resources/object-refresh.html?cross-site",
|
||||
"Test that cross-site object refreshes are not observable by the parent");
|
||||
|
||||
</script>
|
||||
|
49
test/fixtures/wpt/resource-timing/nextHopProtocol-is-tao-protected.https.html
vendored
Normal file
49
test/fixtures/wpt/resource-timing/nextHopProtocol-is-tao-protected.https.html
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing - Check that nextHopProtocol is TAO protected</title>
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/custom-cors-response.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="resources/entry-invariants.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/tao-response.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
|
||||
const {HTTPS_REMOTE_ORIGIN} = get_host_info();
|
||||
|
||||
const tao_protected_next_hop_test = (loader, item) => {
|
||||
attribute_test(
|
||||
loader, custom_cors_response({}, HTTPS_REMOTE_ORIGIN),
|
||||
entry => assert_equals(entry.nextHopProtocol, "",
|
||||
"nextHopProtocol should be the empty string."),
|
||||
`Fetch TAO-less ${item} from remote origin. Make sure nextHopProtocol ` +
|
||||
"is the empty string."
|
||||
);
|
||||
|
||||
attribute_test(
|
||||
loader, remote_tao_response('*'),
|
||||
entry => assert_not_equals(entry.nextHopProtocol, "",
|
||||
"nextHopProtocol should not be the empty string."),
|
||||
`Fetch TAO'd ${item} from remote origin. Make sure nextHopProtocol ` +
|
||||
"is not the empty string."
|
||||
);
|
||||
}
|
||||
|
||||
tao_protected_next_hop_test(load.font, "font");
|
||||
tao_protected_next_hop_test(load.iframe, "iframe");
|
||||
tao_protected_next_hop_test(load.image, "image");
|
||||
tao_protected_next_hop_test(path => load.object(path, "text/plain"), "object");
|
||||
tao_protected_next_hop_test(load.script, "script");
|
||||
tao_protected_next_hop_test(load.stylesheet, "stylesheet");
|
||||
tao_protected_next_hop_test(load.xhr_sync, "synchronous xhr");
|
||||
tao_protected_next_hop_test(load.xhr_async, "asynchronous xhr");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
39
test/fixtures/wpt/resource-timing/no-entries-for-cross-origin-css-fetched.sub.html
vendored
Normal file
39
test/fixtures/wpt/resource-timing/no-entries-for-cross-origin-css-fetched.sub.html
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
<!DOCTYPE HTML>
|
||||
<meta charset=utf-8>
|
||||
<title>Make sure that resources fetched by cross origin CSS are not in the timeline.</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
const host = get_host_info();
|
||||
|
||||
const link = document.createElement("LINK");
|
||||
link.rel = "stylesheet";
|
||||
link.id = "cross_origin_style";
|
||||
|
||||
/*
|
||||
This stylesheet is fetched from one of:
|
||||
//www1.web–platform.test:64941/resource-timing/resources/nested.css
|
||||
//127.0.0.1:64941/resource-timing/resources/nested.css
|
||||
*/
|
||||
link.href = "//" + host.REMOTE_HOST + ":{{ports[http][1]}}{{location[path]}}/../resources/nested.css"
|
||||
document.currentScript.parentNode.insertBefore(link, document.currentScript);
|
||||
</script>
|
||||
<script>
|
||||
const t = async_test("Make sure that resources fetched by cross origin CSS are not in the timeline.");
|
||||
window.addEventListener("load", function() {
|
||||
// A timeout is needed as entries are not guaranteed to be in the timeline before onload triggers.
|
||||
t.step_timeout(function() {
|
||||
const url = (new URL(document.getElementById("cross_origin_style").href));
|
||||
const prefix = url.protocol + "//" + url.host;
|
||||
assert_equals(performance.getEntriesByName(prefix + "/resource-timing/resources/resource_timing_test0.css?id=n1").length, 0, "Import should not be in timeline");
|
||||
assert_equals(performance.getEntriesByName(prefix + "/fonts/Ahem.ttf?id=n1").length, 0, "Font should not be in timeline");
|
||||
assert_equals(performance.getEntriesByName(prefix + "/resource-timing/resources/blue.png?id=n1").length, 0, "Image should not be in timeline");
|
||||
t.done();
|
||||
}, 200);
|
||||
});
|
||||
</script>
|
||||
<ol>Some content</ol>
|
||||
</body>
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates that object resource emit resource timing entries.</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/entry-invariants.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const load_image_object = async path => {
|
||||
return load.object(path, "image/png");
|
||||
}
|
||||
|
||||
const load_null_object = async path => {
|
||||
return load.object(path, null);
|
||||
}
|
||||
|
||||
attribute_test(
|
||||
load_null_object, "resources/status-code.py?status=200&type=none",
|
||||
invariants.assert_tao_pass_no_redirect_http,
|
||||
"Verify that a 200 null-typed object emits an entry.");
|
||||
|
||||
attribute_test(
|
||||
load_null_object, "resources/status-code.py?status=404&type=none",
|
||||
invariants.assert_tao_pass_no_redirect_http,
|
||||
"Verify that a 404 null-typed object emits an entry.");
|
||||
|
||||
attribute_test(
|
||||
load_image_object, "resources/status-code.py?status=404&type=img",
|
||||
invariants.assert_tao_pass_no_redirect_http,
|
||||
"Verify that a 404 img-typed object emits an entry.");
|
||||
</script>
|
47
test/fixtures/wpt/resource-timing/object-not-found-after-TAO-cross-origin-redirect.html
vendored
Normal file
47
test/fixtures/wpt/resource-timing/object-not-found-after-TAO-cross-origin-redirect.html
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates resource timing information for a timing allowed cross-origin redirect chain.</title>
|
||||
<link rel="help" href="http://www.w3.org/TR/resource-timing/#performanceresourcetiming"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src=/common/get-host-info.sub.js></script>
|
||||
<script src="resources/webperftestharness.js"></script>
|
||||
<script src="resources/webperftestharnessextension.js"></script>
|
||||
|
||||
<script>
|
||||
setup({explicit_done: true});
|
||||
test_namespace('getEntriesByName');
|
||||
|
||||
function onload_test()
|
||||
{
|
||||
const context = new PerformanceContext(performance);
|
||||
const entries = context.getEntriesByName(document.querySelector('object').data, 'resource');
|
||||
test_equals(entries.length, 1, 'There should be one entry.');
|
||||
const entry = entries[0];
|
||||
|
||||
test_greater_than(entry.redirectStart, 0, 'redirectStart > 0 in timing allowed cross-origin redirect.');
|
||||
test_equals(entry.redirectStart, entry.startTime, 'redirectStart == startTime in timing allowed cross-origin redirect.');
|
||||
test_greater_than(entry.redirectEnd, entry.redirectStart, 'redirectEnd > redirectStart in timing allowed cross-origin redirect.');
|
||||
test_greater_or_equals(entry.fetchStart, entry.redirectEnd, 'fetchStart >= redirectEnd in timing allowed cross-origin redirect.');
|
||||
done();
|
||||
}
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
let destUrl = get_host_info().HTTP_REMOTE_ORIGIN + '/resource-timing/resources/multi_redirect.py?';
|
||||
destUrl += 'page_origin=' + 'http://' + document.location.host;
|
||||
destUrl += '&cross_origin=' + get_host_info().HTTP_REMOTE_ORIGIN;
|
||||
destUrl += '&final_resource=' + encodeURIComponent("/resource-timing/resources/status-code.py?status=404&tao_value=*");
|
||||
destUrl += '&tao_steps=3';
|
||||
const objElement = document.createElement('object');
|
||||
objElement.style = 'width: 0px; height: 0px;';
|
||||
objElement.data = destUrl;
|
||||
objElement.onerror = onload_test;
|
||||
document.body.appendChild(objElement);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
35
test/fixtures/wpt/resource-timing/object-not-found-after-cross-origin-redirect.html
vendored
Normal file
35
test/fixtures/wpt/resource-timing/object-not-found-after-cross-origin-redirect.html
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates the values in resource timing for cross-origin
|
||||
redirects.</title>
|
||||
<link rel="author" title="Noam Rosenthal" href="noam@webkit.org">
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/entry-invariants.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
const {REMOTE_ORIGIN} = get_host_info();
|
||||
const delay = 2
|
||||
const not_found_page = encodeURIComponent("/resource-timing/resources/status-code.py?status=404");
|
||||
const load_null_object = async path => {
|
||||
return load.object(path, null);
|
||||
}
|
||||
const destUrl = `/common/slow-redirect.py?delay=${delay}&location=${REMOTE_ORIGIN}/${not_found_page}`;
|
||||
|
||||
const timeBefore = performance.now()
|
||||
attribute_test(load_null_object, destUrl, entry => {
|
||||
assert_equals(entry.startTime, entry.fetchStart, 'startTime and fetchStart should be equal');
|
||||
assert_greater_than(entry.startTime, timeBefore, 'startTime and fetchStart should be greater than the time before fetching');
|
||||
// See https://github.com/w3c/resource-timing/issues/264
|
||||
assert_less_than(Math.round(entry.startTime - timeBefore), delay * 1000, 'startTime should not expose redirect delays');
|
||||
}, "Verify that cross-origin object resources don't implicitly expose their redirect timings")
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,46 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing TAO - "null" and opaque origin</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#timing-allow-origin"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Description</h1>
|
||||
<p>This test validates that, for a cross origin resource, the timing allow
|
||||
check algorithm will correctly distinguish between 'null' and 'Null' values in
|
||||
the Timing-Allow-Origin header. An opaque origin's serialization is the string
|
||||
"null" and the timing allow origin check needs to do a case-sensitive comparison
|
||||
to the Timing-Allow-Origin header.
|
||||
</p>
|
||||
<iframe id="frameContext"></iframe>
|
||||
<script>
|
||||
const {ORIGIN} = get_host_info();
|
||||
const url = `${ORIGIN}/resource-timing/resources/TAOResponse.py`;
|
||||
const frame_content = `data:text/html;utf8,<body>
|
||||
<script src="${ORIGIN}/resources/testharness.js"></` + `script>
|
||||
<script src="${ORIGIN}/resource-timing/resources/entry-invariants.js">
|
||||
</` + `script>
|
||||
<script>
|
||||
attribute_test(fetch, "${url}?tao=null",
|
||||
invariants.assert_tao_pass_no_redirect_http,
|
||||
"An opaque origin should be authorized to see resource timings when the" +
|
||||
"TAO header is the string 'null'");
|
||||
attribute_test(fetch, "${url}?tao=Null",
|
||||
invariants.assert_tao_failure_resource,
|
||||
"An opaque origin must not be authorized to see resource timings when " +
|
||||
"the TAO header is the string 'Null'. (The check for 'null' must be " +
|
||||
"case-sensitive)");
|
||||
</` + `script>
|
||||
</body>`;
|
||||
|
||||
frameContext.style = "display:none";
|
||||
frameContext.src = frame_content;
|
||||
fetch_tests_from_window(frameContext.contentWindow);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing Entry For hyperlink audit (ping)</title>
|
||||
<link rel="help" href="https://w3c.github.io/resource-timing/"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resource-timing/resources/observe-entry.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(async t => {
|
||||
const link = document.createElement('a');
|
||||
const delay = 500;
|
||||
const ping = `/xhr/resources/delay.py?ms=${delay}`;
|
||||
link.setAttribute('href', 'resources/close.html');
|
||||
link.setAttribute('target', '_blank');
|
||||
link.setAttribute('ping', ping);
|
||||
link.innerText = 'Link';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
const entry = await observe_entry(ping);
|
||||
assert_equals(entry.initiatorType, 'ping');
|
||||
assert_greater_than(entry.duration, delay);
|
||||
}, "Hyperlink auditing (<a ping>) should have a resource timing entry");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,61 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Resource Timing: resources fetched through same-origin redirects</title>
|
||||
<link rel="author" title="Google" href="http://www.google.com/" />
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="resources/resource-loaders.js"></script>
|
||||
<script src="resources/entry-invariants.js"></script>
|
||||
<script>
|
||||
const {HTTPS_NOTSAMESITE_ORIGIN} = get_host_info();
|
||||
const redirect_url = `/common/redirect.py`;
|
||||
const url_prefix = `${redirect_url}?location=/resource-timing/resources/`;
|
||||
const https_url_prefix = `${redirect_url}?location=${HTTPS_NOTSAMESITE_ORIGIN}/resource-timing/resources/`;
|
||||
|
||||
attribute_test(
|
||||
load.stylesheet, url_prefix + "resource_timing_test0.css",
|
||||
invariants.assert_same_origin_redirected_resource,
|
||||
"Verify attributes of a redirected stylesheet's PerformanceResourceTiming");
|
||||
|
||||
attribute_test(
|
||||
load.image, url_prefix + "blue.png",
|
||||
invariants.assert_same_origin_redirected_resource,
|
||||
"Verify attributes of a redirected image's PerformanceResourceTiming");
|
||||
|
||||
attribute_test(
|
||||
load.iframe, url_prefix + "green.html",
|
||||
invariants.assert_same_origin_redirected_resource,
|
||||
"Verify attributes of a redirected iframe's PerformanceResourceTiming");
|
||||
|
||||
attribute_test(
|
||||
load.script, url_prefix + "empty_script.js",
|
||||
invariants.assert_same_origin_redirected_resource,
|
||||
"Verify attributes of a redirected script's PerformanceResourceTiming");
|
||||
|
||||
attribute_test(
|
||||
load.xhr_sync, url_prefix + "green.html?id=xhr",
|
||||
invariants.assert_same_origin_redirected_resource,
|
||||
"Verify attributes of a redirected synchronous XMLHttpRequest's " +
|
||||
"PerformanceResourceTiming");
|
||||
|
||||
attribute_test(
|
||||
load.xhr_sync, https_url_prefix + "green.html?id=xhr",
|
||||
invariants.assert_cross_origin_redirected_resource,
|
||||
"Verify attributes of a synchronous XMLHttpRequest's " +
|
||||
"PerformanceResourceTiming where the initial HTTP request is redirected " +
|
||||
"to a cross-origin HTTPS resource."
|
||||
);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Description</h1>
|
||||
<p>This test validates that, when a fetching resources that encounter
|
||||
same-origin redirects, attributes of the PerformanceResourceTiming entry
|
||||
conform to the specification.</p>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,222 @@
|
|||
<!DOCTYPE html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates the render blocking status of resources.</title>
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
|
||||
<!-- Start of test cases -->
|
||||
<script>
|
||||
// Dynamic style using document.write in head
|
||||
document.write(`
|
||||
<link rel=stylesheet
|
||||
href='resources/empty_style.css?stylesheet-head-dynamic-docWrite'>
|
||||
`);
|
||||
document.write(`
|
||||
<link rel=stylesheet
|
||||
href='resources/empty_style.css?stylesheet-head-dynamic-docWrite-print'
|
||||
media=print>
|
||||
`);
|
||||
</script>
|
||||
|
||||
<link rel=stylesheet href="resources/empty_style.css?stylesheet-head">
|
||||
<link rel=stylesheet href="resources/empty_style.css?stylesheet-head-media-print"
|
||||
media=print>
|
||||
<link rel="alternate stylesheet"
|
||||
href="resources/empty_style.css?stylesheet-head-alternate">
|
||||
<link rel=preload as=style href="resources/empty_style.css?link-style-head-preload">
|
||||
<link rel=preload as=style href="resources/empty_style.css?link-style-preload-used">
|
||||
<link rel=stylesheet href="resources/importer.css?stylesheet-importer-head">
|
||||
<link rel=stylesheet id="link-head-remove-attr" blocking="render"
|
||||
href="resources/empty_style.css?stylesheet-head-blocking-render-remove-attr">
|
||||
<link rel=modulepreload href="resources/empty_script.js?link-head-modulepreload">
|
||||
|
||||
<style>@import url(resources/empty_style.css?stylesheet-inline-imported);</style>
|
||||
<style media=print>
|
||||
@import url(resources/empty_style.css?stylesheet-inline-imported-print);
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<link rel=stylesheet href="resources/empty_style.css?stylesheet-body">
|
||||
<link rel=stylesheet href="resources/importer.css?stylesheet-importer-body">
|
||||
<link rel=stylesheet href="resources/empty_style.css?stylesheet-body-media-print"
|
||||
media=print>
|
||||
<link rel=stylesheet blocking="render"
|
||||
href="resources/empty_style.css?stylesheet-body-blocking-render">
|
||||
|
||||
<!-- https://html.spec.whatwg.org/multipage/urls-and-fetching.html#blocking-attributes
|
||||
mentions that an element is potentially render-blocking if its blocking
|
||||
tokens set contains "render", or if it is implicitly potentially
|
||||
render-blocking. By default, an element is not implicitly potentially
|
||||
render-blocking.
|
||||
https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet
|
||||
specifies that a link element of type stylesheet is implicitly potentially
|
||||
render-blocking only if the element was created by its node document's parser. -->
|
||||
<script>
|
||||
// Dynamic style using document.write in body
|
||||
document.write(`
|
||||
<link rel=stylesheet
|
||||
href='resources/empty_style.css?stylesheet-body-dynamic-docWrite'>
|
||||
`);
|
||||
document.write(`
|
||||
<link rel=stylesheet
|
||||
href='resources/empty_style.css?stylesheet-body-dynamic-docWrite-print'
|
||||
media=print>
|
||||
`);
|
||||
|
||||
// Dynamic style using innerHTML
|
||||
document.head.innerHTML += `
|
||||
<link rel=stylesheet
|
||||
href='resources/empty_style.css?stylesheet-head-dynamic-innerHTML'>
|
||||
`;
|
||||
document.head.innerHTML += `
|
||||
<link rel=stylesheet
|
||||
href='resources/empty_style.css?stylesheet-head-dynamic-innerHTML-print'
|
||||
media=print>
|
||||
`;
|
||||
document.head.innerHTML += `
|
||||
<link rel=stylesheet blocking=render
|
||||
href='resources/empty_style.css?stylesheet-head-blocking-render-dynamic-innerHTML'>
|
||||
`;
|
||||
|
||||
// Dynamic style using DOM API
|
||||
var link = document.createElement("link");
|
||||
link.href = "resources/empty_style.css?stylesheet-head-dynamic-dom";
|
||||
link.rel = "stylesheet";
|
||||
document.head.appendChild(link);
|
||||
|
||||
// Add a dynamic render-blocking style with DOM API
|
||||
link = document.createElement("link");
|
||||
link.href = "resources/empty_style.css?stylesheet-head-blocking-render-dynamic-dom";
|
||||
link.rel = "stylesheet";
|
||||
link.blocking = "render";
|
||||
document.head.appendChild(link);
|
||||
|
||||
// Dynamic style preload using DOM API
|
||||
link = document.createElement("link");
|
||||
link.href = "resources/empty_style.css?link-style-head-preload-dynamic-dom";
|
||||
link.rel = "preload";
|
||||
link.as = "style";
|
||||
document.head.appendChild(link);
|
||||
|
||||
// Dynamic module via modulepreload using DOM API
|
||||
link = document.createElement("link");
|
||||
link.href = "resources/empty_script.js?link-head-modulepreload-dynamic-dom";
|
||||
link.rel = "modulepreload";
|
||||
document.head.appendChild(link);
|
||||
|
||||
// Add a style preload with DOM API to be used later
|
||||
link = document.createElement("link");
|
||||
link.href = "resources/empty_style.css?link-style-preload-used-dynamic";
|
||||
link.rel = "preload";
|
||||
link.as = "style";
|
||||
document.head.appendChild(link);
|
||||
// Use the preload
|
||||
link = document.createElement("link");
|
||||
link.href = "resources/empty_style.css?link-style-preload-used-dynamic";
|
||||
link.rel = "stylesheet";
|
||||
document.head.appendChild(link);
|
||||
|
||||
// Dynamic inline CSS
|
||||
// Add an inline CSS importer
|
||||
document.write(`
|
||||
<style>
|
||||
@import url('resources/empty_style.css?stylesheet-inline-imported-dynamic-docwrite')
|
||||
</style>
|
||||
`);
|
||||
document.write(`
|
||||
<style media=print>
|
||||
@import url('resources/empty_style.css?stylesheet-inline-imported-dynamic-docwrite-print')
|
||||
</style>
|
||||
`);
|
||||
|
||||
// Add a dynamic inline CSS importer using DOM API
|
||||
let style = document.createElement("style");
|
||||
style.textContent = "@import url('resources/empty_style.css?stylesheet-inline-imported-dynamic-dom')";
|
||||
document.head.appendChild(style);
|
||||
|
||||
// Add a dynamic render-blocking inline CSS importer
|
||||
style = document.createElement("style");
|
||||
style.textContent = "@import url('resources/empty_style.css?stylesheet-inline-imported-blocking-render-dynamic-dom')";
|
||||
style.blocking = "render";
|
||||
document.head.appendChild(style);
|
||||
|
||||
// Dynamic CSS importer
|
||||
document.write(`
|
||||
<link rel=stylesheet href='resources/importer_dynamic.css'>
|
||||
`);
|
||||
document.write(`
|
||||
<link rel=stylesheet href='resources/importer_print.css' media=print>
|
||||
`);
|
||||
|
||||
// Removing blocking render attribute after request is made
|
||||
const sheet = document.getElementById("link-head-remove-attr");
|
||||
sheet.blocking = "";
|
||||
</script>
|
||||
|
||||
<link rel=stylesheet href="resources/empty_style.css?link-style-preload-used">
|
||||
|
||||
<script>
|
||||
|
||||
const wait_for_onload = () => {
|
||||
return new Promise(resolve => {
|
||||
window.addEventListener("load", resolve);
|
||||
})};
|
||||
|
||||
promise_test(
|
||||
async () => {
|
||||
const expectedRenderBlockingStatus = {
|
||||
'stylesheet-head-dynamic-docWrite': 'blocking',
|
||||
'stylesheet-head-dynamic-docWrite-print': 'non-blocking',
|
||||
'stylesheet-head': 'blocking',
|
||||
'stylesheet-head-media-print' : 'non-blocking',
|
||||
'stylesheet-head-alternate' : 'non-blocking',
|
||||
'link-style-head-preload' : 'non-blocking',
|
||||
'stylesheet-importer-head' : 'blocking',
|
||||
'stylesheet-head-blocking-render-remove-attr' : 'blocking',
|
||||
'link-head-modulepreload' : 'non-blocking',
|
||||
'stylesheet-inline-imported' : 'blocking',
|
||||
'stylesheet-inline-imported-print' : 'non-blocking',
|
||||
'stylesheet-body': 'non-blocking',
|
||||
'stylesheet-importer-body' : 'non-blocking',
|
||||
'stylesheet-body-media-print' : 'non-blocking',
|
||||
'stylesheet-body-blocking-render' : 'non-blocking',
|
||||
'stylesheet-body-dynamic-docWrite' : 'non-blocking',
|
||||
'stylesheet-body-dynamic-docWrite-print': 'non-blocking',
|
||||
'stylesheet-head-dynamic-innerHTML' : 'non-blocking',
|
||||
'stylesheet-head-dynamic-innerHTML-print' : 'non-blocking',
|
||||
'stylesheet-head-blocking-render-dynamic-innerHTML' : 'blocking',
|
||||
'stylesheet-head-dynamic-dom' : 'non-blocking',
|
||||
'stylesheet-head-blocking-render-dynamic-dom' : 'blocking',
|
||||
'link-style-head-preload-dynamic-dom' : 'non-blocking',
|
||||
'link-head-modulepreload-dynamic-dom' : 'non-blocking',
|
||||
'link-style-preload-used' : 'non-blocking',
|
||||
'link-style-preload-used-dynamic' : 'non-blocking',
|
||||
'stylesheet-inline-imported-dynamic-docwrite': 'blocking',
|
||||
'stylesheet-inline-imported-dynamic-docwrite-print' : 'non-blocking',
|
||||
'stylesheet-inline-imported-dynamic-dom' : 'non-blocking',
|
||||
'stylesheet-inline-imported-blocking-render-dynamic-dom' : 'blocking',
|
||||
'stylesheet-imported' : 'blocking',
|
||||
'stylesheet-imported-print' : 'non-blocking',
|
||||
'stylesheet-imported-dynamic' : 'non-blocking'
|
||||
};
|
||||
|
||||
await wait_for_onload();
|
||||
|
||||
const entry_list = performance.getEntriesByType("resource");
|
||||
for (entry of entry_list) {
|
||||
if (entry.name.includes("empty_style.css") ||
|
||||
entry.name.includes("importer.css") ||
|
||||
entry.name.includes("empty_script.js")) {
|
||||
key = entry.name.split("?").pop();
|
||||
expectedStatus = expectedRenderBlockingStatus[key];
|
||||
assert_equals(entry.renderBlockingStatus, expectedStatus,
|
||||
`render blocking status for ${entry.name} should be ${expectedStatus}`);
|
||||
}
|
||||
}
|
||||
}, "Validate render blocking status of link resources in PerformanceResourceTiming");
|
||||
|
||||
</script>
|
|
@ -0,0 +1,196 @@
|
|||
<!DOCTYPE html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>This test validates the render blocking status of resources.</title>
|
||||
<link rel="help" href="https://www.w3.org/TR/resource-timing-2/#sec-performanceresourcetiming"/>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
|
||||
<!-- Start of test cases -->
|
||||
<script src="resources/empty_script.js?script-head"></script>
|
||||
<script type="module" src="resources/empty_script.js?script-head-module"></script>
|
||||
<script async type=module
|
||||
src="resources/empty_script.js?script-head-async-module">
|
||||
</script>
|
||||
<script async src="resources/empty_script.js?script-head-async"></script>
|
||||
<script defer src="resources/empty_script.js?script-head-defer"></script>
|
||||
<script blocking=render
|
||||
src="resources/empty_script.js?script-head-blocking-render">
|
||||
</script>
|
||||
<script async blocking=render
|
||||
src="resources/empty_script.js?script-head-async-blocking-render">
|
||||
</script>
|
||||
<script type=module blocking=render
|
||||
src="resources/empty_script.js?script-head-module-blocking-render">
|
||||
</script>
|
||||
<script async type=module blocking=render
|
||||
src="resources/empty_script.js?script-head-async-module-blocking-render">
|
||||
</script>
|
||||
<script defer blocking=render
|
||||
src="resources/empty_script.js?script-head-defer-blocking-render">
|
||||
</script>
|
||||
|
||||
<script id="script-head-remove-attr" blocking=render
|
||||
src="resources/empty_script.js?script-head-blocking-render-remove-attr">
|
||||
</script>
|
||||
|
||||
<script>
|
||||
document.write(`
|
||||
<script defer
|
||||
src="resources/empty_script.js?script-head-defer-dynamic-docwrite">
|
||||
<\/script>`);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<script src="resources/empty_script.js?script-body"></script>
|
||||
<script type="module" src="resources/empty_script.js?script-body-module"></script>
|
||||
<script async type=module
|
||||
src="resources/empty_script.js?script-body-async-module">
|
||||
</script>
|
||||
<script async src="resources/empty_script.js?script-body-async"></script>
|
||||
<script defer src="resources/empty_script.js?script-body-defer"></script>
|
||||
|
||||
<script>
|
||||
const script = document.createElement("script");
|
||||
script.src = "resources/empty_script.js?script-head-dynamic-dom";
|
||||
document.head.appendChild(script);
|
||||
|
||||
// Dynamic explicitly async script
|
||||
const async_script = document.createElement("script");
|
||||
async_script.src = "resources/empty_script.js?script-head-async-dynamic-dom";
|
||||
async_script.async = true;
|
||||
document.head.appendChild(async_script);
|
||||
|
||||
// Dynamic non-async script
|
||||
// https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model
|
||||
// mentions that a script element has to be parser-inserted to be
|
||||
// implicitly potentially render-blocking
|
||||
const non_async_script = document.createElement("script");
|
||||
non_async_script.src = "resources/empty_script.js?script-head-non-async-dynamic-dom";
|
||||
non_async_script.async = false;
|
||||
document.head.appendChild(non_async_script);
|
||||
|
||||
// Dynamic defer script
|
||||
const defer_script = document.createElement("script");
|
||||
defer_script.src = "resources/empty_script.js?script-head-defer-dynamic-dom";
|
||||
defer_script.defer = true;
|
||||
document.head.appendChild(defer_script);
|
||||
|
||||
// Dynamic explicitly render-blocking script
|
||||
const blocking_script = document.createElement("script");
|
||||
blocking_script.src = "resources/empty_script.js?script-head-blocking-render-dynamic-dom";
|
||||
blocking_script.blocking = "render";
|
||||
document.head.appendChild(blocking_script);
|
||||
|
||||
// Dynamic explicitly render-blocking module script
|
||||
const blocking_module_script = document.createElement("script");
|
||||
blocking_module_script.src = "resources/empty_script.js?script-head-module-blocking-render-dynamic-dom";
|
||||
blocking_module_script.type = "module";
|
||||
blocking_module_script.blocking = "render";
|
||||
document.head.appendChild(blocking_module_script);
|
||||
|
||||
// Dynamic async module script
|
||||
const async_module_script = document.createElement("script");
|
||||
async_module_script.src = "resources/empty_script.js?script-head-async-module-dynamic-dom";
|
||||
async_module_script.type = "module";
|
||||
async_module_script.async = true;
|
||||
document.head.appendChild(async_module_script);
|
||||
|
||||
// Dynamic async render-blocking module script
|
||||
const async_blocking_module_script = document.createElement("script");
|
||||
async_blocking_module_script.src = "resources/empty_script.js?script-head-async-module-blocking-render-dynamic-dom";
|
||||
async_blocking_module_script.type = "module";
|
||||
async_blocking_module_script.async = true;
|
||||
async_blocking_module_script.blocking = "render"
|
||||
document.head.appendChild(async_blocking_module_script);
|
||||
|
||||
// Add a module that imports more modules
|
||||
const importer_script = document.createElement("script");
|
||||
importer_script.src = "resources/fake_responses.py?url=importer.js";
|
||||
importer_script.type = "module";
|
||||
document.head.appendChild(importer_script);
|
||||
|
||||
// Add an async module that imports more modules
|
||||
const importer_async_script = document.createElement("script");
|
||||
importer_async_script.src = "resources/fake_responses.py?url=importer_async.js";
|
||||
importer_async_script.type = "module";
|
||||
importer_async_script.async = true;
|
||||
document.head.appendChild(importer_async_script);
|
||||
|
||||
// Removing blocking render attribute after request is made
|
||||
const script_element = document.getElementById("script-head-remove-attr");
|
||||
script_element.blocking = "";
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
const wait_for_onload = () => {
|
||||
return new Promise(resolve => {
|
||||
window.addEventListener("load", resolve);
|
||||
})};
|
||||
|
||||
promise_test(
|
||||
async () => {
|
||||
const expectedRenderBlockingStatus = {
|
||||
'script-head': 'blocking',
|
||||
'script-head-module' : 'non-blocking',
|
||||
'script-head-async-module' : 'non-blocking',
|
||||
'script-head-async' : 'non-blocking',
|
||||
'script-head-defer' : 'non-blocking',
|
||||
'script-head-blocking-render' : 'blocking',
|
||||
'script-head-async-blocking-render' : 'blocking',
|
||||
'script-head-module-blocking-render' : 'blocking',
|
||||
'script-head-async-module-blocking-render' : 'blocking',
|
||||
'script-head-defer-blocking-render' : 'blocking',
|
||||
'script-head-blocking-render-remove-attr' : 'blocking',
|
||||
'script-head-defer-dynamic-docwrite' : 'non-blocking',
|
||||
'script-body' : 'non-blocking',
|
||||
'script-body-module' : 'non-blocking',
|
||||
'script-body-async-module' : 'non-blocking',
|
||||
'script-body-async' : 'non-blocking',
|
||||
'script-body-defer' : 'non-blocking',
|
||||
'script-head-dynamic-dom': 'non-blocking',
|
||||
'script-head-async-dynamic-dom' : 'non-blocking',
|
||||
'script-head-non-async-dynamic-dom': 'non-blocking',
|
||||
'script-head-defer-dynamic-dom' : 'non-blocking',
|
||||
'script-head-blocking-render-dynamic-dom' : 'blocking',
|
||||
'script-head-module-blocking-render-dynamic-dom' : 'blocking',
|
||||
'script-head-async-module-dynamic-dom' : 'non-blocking',
|
||||
'script-head-async-module-blocking-render-dynamic-dom' : 'blocking',
|
||||
'script-head-import-defer' : 'non-blocking',
|
||||
'script-head-import-defer-dynamic' : 'non-blocking',
|
||||
'script-head-import-async' : 'non-blocking',
|
||||
'script-head-import-async-dynamic' : 'non-blocking',
|
||||
'script-importer' : 'non-blocking',
|
||||
'script-importer-async' : 'non-blocking'
|
||||
};
|
||||
|
||||
await wait_for_onload();
|
||||
|
||||
const entry_list = performance.getEntriesByType("resource");
|
||||
for (entry of entry_list) {
|
||||
if (entry.name.includes("empty_script.js")) {
|
||||
key = entry.name.split("?").pop();
|
||||
expectedStatus = expectedRenderBlockingStatus[key];
|
||||
assert_equals(entry.renderBlockingStatus, expectedStatus,
|
||||
`render blocking status for ${entry.name} should be ${expectedStatus}`);
|
||||
}
|
||||
else if (entry.name.includes("importer.js")){
|
||||
key = 'script-importer';
|
||||
expectedStatus = expectedRenderBlockingStatus[key];
|
||||
assert_equals(entry.renderBlockingStatus, expectedStatus,
|
||||
`render blocking status for ${entry.name} should be ${expectedStatus}`);
|
||||
}
|
||||
else if (entry.name.includes("importer_async.js")){
|
||||
key = 'script-importer-async';
|
||||
expectedStatus = expectedRenderBlockingStatus[key];
|
||||
assert_equals(entry.renderBlockingStatus, expectedStatus,
|
||||
`render blocking status for ${entry.name} should be ${expectedStatus}`);
|
||||
}
|
||||
}
|
||||
}, "Validate render blocking status of script resources in PerformanceResourceTiming");
|
||||
|
||||
</script>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue