diff --git a/test/fixtures/wpt/README.md b/test/fixtures/wpt/README.md
index dcf42abe7925f7..421b5fdcb77fc7 100644
--- a/test/fixtures/wpt/README.md
+++ b/test/fixtures/wpt/README.md
@@ -17,6 +17,7 @@ Last update:
- interfaces: https://github.com/web-platform-tests/wpt/tree/712c9f275e/interfaces
- html/webappapis/microtask-queuing: https://github.com/web-platform-tests/wpt/tree/0c3bed38df/html/webappapis/microtask-queuing
- html/webappapis/timers: https://github.com/web-platform-tests/wpt/tree/ddfe9c089b/html/webappapis/timers
+- hr-time: https://github.com/web-platform-tests/wpt/tree/a5d1774ecf/hr-time
[Web Platform Tests]: https://github.com/web-platform-tests/wpt
[`git node wpt`]: https://github.com/nodejs/node-core-utils/blob/master/docs/git-node.md#git-node-wpt
diff --git a/test/fixtures/wpt/hr-time/META.yml b/test/fixtures/wpt/hr-time/META.yml
new file mode 100644
index 00000000000000..779d5b4af08428
--- /dev/null
+++ b/test/fixtures/wpt/hr-time/META.yml
@@ -0,0 +1,4 @@
+spec: https://w3c.github.io/hr-time/
+suggested_reviewers:
+ - plehegar
+ - igrigorik
diff --git a/test/fixtures/wpt/hr-time/basic.any.js b/test/fixtures/wpt/hr-time/basic.any.js
new file mode 100644
index 00000000000000..364dd81a344818
--- /dev/null
+++ b/test/fixtures/wpt/hr-time/basic.any.js
@@ -0,0 +1,28 @@
+test(function() {
+ assert_true((self.performance !== undefined), "self.performance exists");
+ assert_equals(typeof self.performance, "object", "self.performance is an object");
+ assert_equals((typeof self.performance.now), "function", "self.performance.now() is a function");
+ assert_equals(typeof self.performance.now(), "number", "self.performance.now() returns a number");
+}, "self.performance.now() is a function that returns a number");
+
+test(function() {
+ assert_true(self.performance.now() > 0);
+}, "self.performance.now() returns a positive number");
+
+test(function() {
+ var now1 = self.performance.now();
+ var now2 = self.performance.now();
+ assert_true((now2-now1) >= 0);
+ }, "self.performance.now() difference is not negative");
+
+async_test(function() {
+ // Check whether the performance.now() method is close to Date() within 30ms (due to inaccuracies)
+ var initial_hrt = self.performance.now();
+ var initial_date = Date.now();
+ this.step_timeout(function() {
+ var final_hrt = self.performance.now();
+ var final_date = Date.now();
+ assert_approx_equals(final_hrt - initial_hrt, final_date - initial_date, 30, 'High resolution time value increased by approximately the same amount as time from date object');
+ this.done();
+ }, 2000);
+}, 'High resolution time has approximately the right relative magnitude');
diff --git a/test/fixtures/wpt/hr-time/idlharness.any.js b/test/fixtures/wpt/hr-time/idlharness.any.js
new file mode 100644
index 00000000000000..6676b001a8b816
--- /dev/null
+++ b/test/fixtures/wpt/hr-time/idlharness.any.js
@@ -0,0 +1,23 @@
+// META: global=window,worker
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+// META: timeout=long
+
+'use strict';
+
+// https://w3c.github.io/hr-time/
+
+idl_test(
+ ['hr-time'],
+ ['html', 'dom'],
+ async idl_array => {
+ if (self.GLOBAL.isWorker()) {
+ idl_array.add_objects({ WorkerGlobalScope: ['self'] });
+ } else {
+ idl_array.add_objects({ Window: ['self'] });
+ }
+ idl_array.add_objects({
+ Performance: ['performance'],
+ });
+ }
+);
diff --git a/test/fixtures/wpt/hr-time/monotonic-clock.any.js b/test/fixtures/wpt/hr-time/monotonic-clock.any.js
new file mode 100644
index 00000000000000..c53b04d844e4f5
--- /dev/null
+++ b/test/fixtures/wpt/hr-time/monotonic-clock.any.js
@@ -0,0 +1,13 @@
+// The time values returned when calling the now method MUST be monotonically increasing and not subject to system clock adjustments or system clock skew.
+test(function() {
+ assert_true(self.performance.now() > 0, "self.performance.now() returns positive numbers");
+}, "self.performance.now() returns a positive number");
+
+// The difference between any two chronologically recorded time values returned from the now method MUST never be negative.
+test(function() {
+ var now1 = self.performance.now();
+ var now2 = self.performance.now();
+ assert_true((now2-now1) >= 0, "self.performance.now() difference is not negative");
+ },
+ "self.performance.now() difference is not negative"
+);
diff --git a/test/fixtures/wpt/hr-time/performance-tojson.html b/test/fixtures/wpt/hr-time/performance-tojson.html
new file mode 100644
index 00000000000000..fd8049cb9a1eee
--- /dev/null
+++ b/test/fixtures/wpt/hr-time/performance-tojson.html
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/fixtures/wpt/hr-time/resources/now_frame.html b/test/fixtures/wpt/hr-time/resources/now_frame.html
new file mode 100644
index 00000000000000..5bec688af9cd3c
--- /dev/null
+++ b/test/fixtures/wpt/hr-time/resources/now_frame.html
@@ -0,0 +1,9 @@
+
+
+
+
+ window.performance.now frame
+
+
+
+
diff --git a/test/fixtures/wpt/hr-time/resources/unload-a.html b/test/fixtures/wpt/hr-time/resources/unload-a.html
new file mode 100644
index 00000000000000..40c1d061830e6f
--- /dev/null
+++ b/test/fixtures/wpt/hr-time/resources/unload-a.html
@@ -0,0 +1,13 @@
+
+
+
+ Helper page for ../unload-manual.html
+
+
+
+
+
+
+
diff --git a/test/fixtures/wpt/hr-time/resources/unload-b.html b/test/fixtures/wpt/hr-time/resources/unload-b.html
new file mode 100644
index 00000000000000..7c2d90df275fbc
--- /dev/null
+++ b/test/fixtures/wpt/hr-time/resources/unload-b.html
@@ -0,0 +1,13 @@
+
+
+
+ Helper page for ../unload-manual.html
+
+
+
+
+
+
+
diff --git a/test/fixtures/wpt/hr-time/resources/unload-c.html b/test/fixtures/wpt/hr-time/resources/unload-c.html
new file mode 100644
index 00000000000000..731da9db758ba5
--- /dev/null
+++ b/test/fixtures/wpt/hr-time/resources/unload-c.html
@@ -0,0 +1,13 @@
+
+
+
+ Helper page for ../unload-manual.html
+
+
+
+
+
+
+
diff --git a/test/fixtures/wpt/hr-time/resources/unload.js b/test/fixtures/wpt/hr-time/resources/unload.js
new file mode 100644
index 00000000000000..06b0bc7da78b4b
--- /dev/null
+++ b/test/fixtures/wpt/hr-time/resources/unload.js
@@ -0,0 +1,50 @@
+const syncDelay = ms => {
+ const start = performance.now();
+ let elapsedTime;
+ do {
+ elapsedTime = performance.now() - start;
+ } while (elapsedTime < ms);
+};
+
+const markTime = (docName, lifecycleEventName) => {
+ // Calculating these values before the below `mark` invocation ensures that delays in
+ // reaching across to the other window object doesn't interfere with the correctness
+ // of the test.
+ const dateNow = Date.now();
+ const performanceNow = performance.now();
+
+ window.opener.mark({
+ docName,
+ lifecycleEventName,
+ performanceNow: performanceNow,
+ dateNow: dateNow
+ });
+};
+
+const setupUnloadPrompt = (docName, msg) => {
+ window.addEventListener("beforeunload", ev => {
+ markTime(docName, "beforeunload");
+ return ev.returnValue = msg || "Click OK to continue test."
+ });
+};
+
+const setupListeners = (docName, nextDocument) => {
+ window.addEventListener("load", () => {
+ markTime(docName, "load");
+ document.getElementById("proceed").addEventListener("click", ev => {
+ ev.preventDefault();
+ if (nextDocument) {
+ document.location = nextDocument;
+ } else {
+ window.close();
+ }
+ })
+ });
+
+ setupUnloadPrompt(docName);
+
+ window.addEventListener("unload", () => {
+ markTime(docName, "unload");
+ if (docName !== "c") { syncDelay(1000); }
+ });
+};
diff --git a/test/fixtures/wpt/hr-time/test_cross_frame_start.html b/test/fixtures/wpt/hr-time/test_cross_frame_start.html
new file mode 100644
index 00000000000000..30e804bd735031
--- /dev/null
+++ b/test/fixtures/wpt/hr-time/test_cross_frame_start.html
@@ -0,0 +1,59 @@
+
+
+
+
+ window.performance.now across frames
+
+
+
+
+
+
+
+
+
+
+ Description
+ This test validates the values of the window.performance.now() are based on the current document's navigationStart.
+
+
+
diff --git a/test/fixtures/wpt/hr-time/timeOrigin.html b/test/fixtures/wpt/hr-time/timeOrigin.html
new file mode 100644
index 00000000000000..20aea75084515a
--- /dev/null
+++ b/test/fixtures/wpt/hr-time/timeOrigin.html
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/fixtures/wpt/hr-time/timing-attack.html b/test/fixtures/wpt/hr-time/timing-attack.html
new file mode 100644
index 00000000000000..71ade4a8c48ce2
--- /dev/null
+++ b/test/fixtures/wpt/hr-time/timing-attack.html
@@ -0,0 +1,55 @@
+
+
+
+
+window.performance.now should not enable timing attacks
+
+
+
+
+
+
+
+Description
+The recommended minimum resolution of the Performance interface should be set to 5 microseconds.
+
+
+
+
+
diff --git a/test/fixtures/wpt/hr-time/unload-manual.html b/test/fixtures/wpt/hr-time/unload-manual.html
new file mode 100644
index 00000000000000..18c4e0dc327919
--- /dev/null
+++ b/test/fixtures/wpt/hr-time/unload-manual.html
@@ -0,0 +1,73 @@
+
+
+
+ time origin value manual test
+
+
+
+
+
+
+
+
+
+
+ Description
+ This test validates the behavior of performance.now()
with respect to its time origin.
+
+
+
diff --git a/test/fixtures/wpt/hr-time/window-worker-timeOrigin.window.js b/test/fixtures/wpt/hr-time/window-worker-timeOrigin.window.js
new file mode 100644
index 00000000000000..1e5ef1cdffecf6
--- /dev/null
+++ b/test/fixtures/wpt/hr-time/window-worker-timeOrigin.window.js
@@ -0,0 +1,30 @@
+"use strict"
+// https://w3c.github.io/hr-time/#time-origin
+
+async_test(function(test) {
+ // Cache global time before starting worker
+ const globalTimeOrigin = performance.timeOrigin;
+ const globalNowBeforeWorkerStart = performance.now();
+
+ // Start worker and retrieve time
+ const workerScript = "postMessage({timeOrigin: performance.timeOrigin, now: performance.now()})";
+ const blob = new Blob([workerScript]);
+ let worker = new Worker(URL.createObjectURL(blob));
+
+ worker.addEventListener("message", test.step_func_done(function(event) {
+ const workerTimeOrigin = event.data.timeOrigin;
+ const workerNow = event.data.now;
+
+ assert_not_equals(workerTimeOrigin, 0, "worker timeOrigin must not be 0");
+ assert_not_equals(performance.timeOrigin, 0, "Document timeOrigin must not be 0");
+
+ assert_equals(globalTimeOrigin, performance.timeOrigin, "timeOrigin should not be changed in same document mode");
+ assert_less_than(globalTimeOrigin, workerTimeOrigin, "Document timeOrigin must be earlier than worker timeOrigin");
+
+ // Document and worker's now() start from their respective timeOrigins.
+ const timeDiff = workerTimeOrigin - globalTimeOrigin; // convert worker's time to Document time.
+ assert_less_than(globalTimeOrigin + globalNowBeforeWorkerStart, globalTimeOrigin + timeDiff + workerNow, "Document old now is earlier than worker now.");
+
+ // Comparing timing between Document and worker threads could be delicate as it relies on the thread implementation and could be subject to race conditions.
+ }));
+}, 'timeOrigin and now() should be correctly ordered between window and worker');
diff --git a/test/fixtures/wpt/versions.json b/test/fixtures/wpt/versions.json
index c560f6844e9e3d..5fdc09c4f4f8c5 100644
--- a/test/fixtures/wpt/versions.json
+++ b/test/fixtures/wpt/versions.json
@@ -26,5 +26,9 @@
"html/webappapis/timers": {
"commit": "ddfe9c089bab565a9d3aa37bdef63d8012c1a94c",
"path": "html/webappapis/timers"
+ },
+ "hr-time": {
+ "commit": "a5d1774ecf41751d1c9357c27c709ee33bf3e279",
+ "path": "hr-time"
}
}
\ No newline at end of file
diff --git a/test/wpt/status/hr-time.json b/test/wpt/status/hr-time.json
new file mode 100644
index 00000000000000..0967ef424bce67
--- /dev/null
+++ b/test/wpt/status/hr-time.json
@@ -0,0 +1 @@
+{}
diff --git a/test/wpt/test-hr-time.js b/test/wpt/test-hr-time.js
new file mode 100644
index 00000000000000..eb9c68797dfcbf
--- /dev/null
+++ b/test/wpt/test-hr-time.js
@@ -0,0 +1,27 @@
+'use strict';
+
+// Flags: --expose-internals
+
+require('../common');
+const { WPTRunner } = require('../common/wpt');
+const { performance, PerformanceObserver } = require('perf_hooks');
+
+const runner = new WPTRunner('hr-time');
+
+runner.copyGlobalsFromObject(global, [
+ 'setInterval',
+ 'clearInterval',
+ 'setTimeout',
+ 'clearTimeout'
+]);
+
+runner.defineGlobal('performance', {
+ get() {
+ return performance;
+ }
+});
+runner.defineGlobal('PerformanceObserver', {
+ value: PerformanceObserver
+});
+
+runner.runJsTests();