Bug 1633611 [wpt PR 23285] - Idle: Add notification permission check, a=testonly
authorAyu Ishii <ayui@chromium.org>
Wed, 13 May 2020 09:44:16 +0000
changeset 531108 d318fb86b3225bc87c4faee9c4c0f7fcc0eafd5e
parent 531107 877e73caedb35f2dca41703f3fb84eac82cec06d
child 531109 9f3527240d8041d1b6ae5acbdf4f92f5a740d158
push id37435
push userapavel@mozilla.com
push dateWed, 20 May 2020 15:28:23 +0000
treeherdermozilla-central@5415da14ec9a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1633611, 23285, 1074049, 2163638, 764754
milestone78.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1633611 [wpt PR 23285] - Idle: Add notification permission check, a=testonly Automatic update from web-platform-tests Idle: Add notification permission check This change checks for notification permission before allowing idle detection. tested locally / wpt / unittest Bug: 1074049 Change-Id: I5d12efaf5cb68020ed10eccf577d38319592f4f2 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2163638 Commit-Queue: Ayu Ishii <ayui@chromium.org> Reviewed-by: Victor Costan <pwnall@chromium.org> Reviewed-by: Ken Buchanan <kenrb@chromium.org> Reviewed-by: Reilly Grant <reillyg@chromium.org> Reviewed-by: Matt Falkenhagen <falken@chromium.org> Cr-Commit-Position: refs/heads/master@{#764754} -- wpt-commits: 6d1a21bee19885b49dddb5cd234beea4e162b306 wpt-pr: 23285
testing/web-platform/tests/idle-detection/basics.tentative.https.any.js
testing/web-platform/tests/idle-detection/basics.tentative.https.window.js
testing/web-platform/tests/idle-detection/idle-detection-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html
testing/web-platform/tests/idle-detection/idle-detection-allowed-by-feature-policy-attribute.https.sub.html
testing/web-platform/tests/idle-detection/idle-detection-allowed-by-feature-policy.https.sub.html
testing/web-platform/tests/idle-detection/idle-detection-default-feature-policy.https.sub.html
testing/web-platform/tests/idle-detection/idle-permission.tentative.https.window.js
testing/web-platform/tests/idle-detection/idlharness-worker.https.window.js
testing/web-platform/tests/idle-detection/idlharness.https.any.js
testing/web-platform/tests/idle-detection/idlharness.https.window.js
testing/web-platform/tests/idle-detection/interceptor.https.html
testing/web-platform/tests/idle-detection/resources/idlharness-worker.js
deleted file mode 100644
--- a/testing/web-platform/tests/idle-detection/basics.tentative.https.any.js
+++ /dev/null
@@ -1,94 +0,0 @@
-// META: title=Idle Detection API: Basics
-
-'use strict';
-
-promise_test(async t => {
-  let status = new IdleDetector();
-
-  let watcher = new EventWatcher(t, status, ["change"]);
-
-  await status.start();
-
-  await watcher.wait_for("change");
-
-  assert_true(['active', 'idle'].includes(status.state.user),
-                'status has a valid user state');
-  assert_true(['locked', 'unlocked'].includes(status.state.screen),
-                'status has a valid screen state');
-
-}, 'start() basics');
-
-promise_test(async t => {
-  let used = false;
-
-  new IdleDetector({
-    get threshold() {
-      used = true;
-      return 60;
-    }
-  });
-
-  assert_true(used, 'constructor options "threshold" member was used');
-}, 'constructor uses threshold property');
-
-promise_test(async t => {
-  try {
-    new IdleDetector({threshold: 0});
-    assert_unreached('Threshold under 60 should reject');
-  } catch (error) {
-    assert_equals(error.name, 'TypeError');
-  }
-}, 'constructor throws with invalid threshold (0)');
-
-promise_test(async t => {
-  try {
-    new IdleDetector({threshold: 59});
-    assert_unreached('Threshold under 60 should reject');
-  } catch (error) {
-    assert_equals(error.name, 'TypeError');
-  }
-}, 'constructor throws with threshold below minimum (59)');
-
-promise_test(async t => {
-  new IdleDetector({threshold: 60});
-}, 'constructor allows threshold (60)');
-
-promise_test(async t => {
-  new IdleDetector({threshold: 61});
-}, 'constructor allows threshold (61)');
-
-promise_test(async t => {
-  try {
-    new IdleDetector({threshold: null});
-    assert_unreached('Threshold of null should reject');
-  } catch (error) {
-    assert_equals(error.name, 'TypeError');
-  }
-}, 'constructor throws with invalid threshold (null)');
-
-promise_test(async t => {
-  try {
-    new IdleDetector({threshold: -1});
-    assert_unreached('Threshold of negative numbers should reject');
-  } catch (error) {
-    assert_equals(error.name, 'TypeError');
-  }
-}, 'constructor throws with invalid threshold (-1)');
-
-promise_test(async t => {
-  try {
-    new IdleDetector({threshold: NaN});
-    assert_unreached('Threshold of NaN should reject');
-  } catch (error) {
-    assert_equals(error.name, 'TypeError');
-  }
-}, 'constructor throws with invalid threshold (NaN)');
-
-promise_test(async t => {
-  new IdleDetector();
-}, 'constructor uses a default value for the threshold when none is passed');
-
-promise_test(async t => {
-  new IdleDetector({threshold: undefined});
-}, 'constructor uses a default value for the threshold');
-
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/idle-detection/basics.tentative.https.window.js
@@ -0,0 +1,97 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+// META: title=Idle Detection API: Basics
+
+'use strict';
+
+promise_setup(async t => {
+  await test_driver.set_permission({ name: 'notifications' }, 'granted', false);
+})
+
+promise_test(async t => {
+  let status = new IdleDetector();
+  let watcher = new EventWatcher(t, status, ["change"]);
+  let initial_state = watcher.wait_for("change");
+
+  await status.start();
+  await initial_state;
+
+  assert_true(['active', 'idle'].includes(status.state.user),
+                'status has a valid user state');
+  assert_true(['locked', 'unlocked'].includes(status.state.screen),
+                'status has a valid screen state');
+}, 'start() basics');
+
+promise_test(async t => {
+  let used = false;
+
+  new IdleDetector({
+    get threshold() {
+      used = true;
+      return 60;
+    }
+  });
+
+  assert_true(used, 'constructor options "threshold" member was used');
+}, 'constructor uses threshold property');
+
+promise_test(async t => {
+  try {
+    new IdleDetector({threshold: 0});
+    assert_unreached('Threshold under 60 should reject');
+  } catch (error) {
+    assert_equals(error.name, 'TypeError');
+  }
+}, 'constructor throws with invalid threshold (0)');
+
+promise_test(async t => {
+  try {
+    new IdleDetector({threshold: 59});
+    assert_unreached('Threshold under 60 should reject');
+  } catch (error) {
+    assert_equals(error.name, 'TypeError');
+  }
+}, 'constructor throws with threshold below minimum (59)');
+
+promise_test(async t => {
+  new IdleDetector({threshold: 60});
+}, 'constructor allows threshold (60)');
+
+promise_test(async t => {
+  new IdleDetector({threshold: 61});
+}, 'constructor allows threshold (61)');
+
+promise_test(async t => {
+  try {
+    new IdleDetector({threshold: null});
+    assert_unreached('Threshold of null should reject');
+  } catch (error) {
+    assert_equals(error.name, 'TypeError');
+  }
+}, 'constructor throws with invalid threshold (null)');
+
+promise_test(async t => {
+  try {
+    new IdleDetector({threshold: -1});
+    assert_unreached('Threshold of negative numbers should reject');
+  } catch (error) {
+    assert_equals(error.name, 'TypeError');
+  }
+}, 'constructor throws with invalid threshold (-1)');
+
+promise_test(async t => {
+  try {
+    new IdleDetector({threshold: NaN});
+    assert_unreached('Threshold of NaN should reject');
+  } catch (error) {
+    assert_equals(error.name, 'TypeError');
+  }
+}, 'constructor throws with invalid threshold (NaN)');
+
+promise_test(async t => {
+  new IdleDetector();
+}, 'constructor uses a default value for the threshold when none is passed');
+
+promise_test(async t => {
+  new IdleDetector({threshold: undefined});
+}, 'constructor uses a default value for the threshold');
--- a/testing/web-platform/tests/idle-detection/idle-detection-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html
+++ b/testing/web-platform/tests/idle-detection/idle-detection-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html
@@ -1,10 +1,12 @@
 <!DOCTYPE html>
 <body>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-vendor.js></script>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
 <script src=/feature-policy/resources/featurepolicy.js></script>
 <script>
 'use strict';
 
 const base_src = '/feature-policy/resources/redirect-on-load.html#';
 const sub = 'https://{{domains[www]}}:{{ports[https][0]}}';
@@ -13,34 +15,38 @@ const relative_path =
 const relative_worker_frame_path =
   '/feature-policy/resources/feature-policy-idle-detection-worker.html';
 const same_origin_src = base_src + relative_path;
 const same_origin_worker_frame_src = base_src + relative_worker_frame_path;
 const cross_origin_src = base_src + sub + relative_path;
 const cross_origin_worker_frame_src = base_src + sub +
   relative_worker_frame_path;
 
-async_test(t => {
+promise_setup(async () => {
+  await test_driver.set_permission({ name: 'notifications' }, 'granted', false);
+});
+
+promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, same_origin_src,
       expect_feature_available_default, 'idle-detection');
 }, 'Attribute allow="idle-detection" in top-level frame ' +
    'allows same-origin relocation.');
 
-async_test(t => {
+promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, same_origin_worker_frame_src,
       expect_feature_available_default, 'idle-detection');
 }, 'Attribute allow="idle-detection" in top-level frame ' +
    'allows workers in same-origin relocation.');
 
-async_test(t => {
+promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, cross_origin_src,
       expect_feature_unavailable_default, 'idle-detection');
 }, 'Attribute allow="idle-detection" in top-level frame ' +
    'disallows cross-origin relocation.');
 
-async_test(t => {
+promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, cross_origin_worker_frame_src,
       expect_feature_unavailable_default, 'idle-detection');
 }, 'Attribute allow="idle-detection" in top-level frame ' +
    'disallows workers in cross-origin relocation.');
 
 </script>
 </body>
--- a/testing/web-platform/tests/idle-detection/idle-detection-allowed-by-feature-policy-attribute.https.sub.html
+++ b/testing/web-platform/tests/idle-detection/idle-detection-allowed-by-feature-policy-attribute.https.sub.html
@@ -1,42 +1,48 @@
 <!DOCTYPE html>
 <body>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-vendor.js></script>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
 <script src=/feature-policy/resources/featurepolicy.js></script>
 <script>
 'use strict';
 
 const sub = 'https://{{domains[www]}}:{{ports[https][0]}}';
 const same_origin_src =
   '/feature-policy/resources/feature-policy-idle-detection.html'
 const same_origin_worker_frame_src =
     '/feature-policy/resources/feature-policy-idle-detection-worker.html';
 const cross_origin_src = sub + same_origin_src;
 const cross_origin_worker_frame_src = sub + same_origin_worker_frame_src;
 
-async_test(t => {
+promise_setup(async () => {
+  await test_driver.set_permission({ name: 'notifications' }, 'granted', false);
+});
+
+promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, same_origin_src,
       expect_feature_available_default, 'idle-detection');
 }, 'Attribute allow="idle-detection" in top-level frame can be enabled ' +
    'in same-origin iframe using Feature policy "idle-detection".');
 
-async_test(t => {
+promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, same_origin_worker_frame_src,
       expect_feature_available_default, 'idle-detection');
 }, 'Attribute allow="idle-detection" in top-level frame can be enabled ' +
    'in a worker in same-origin iframe using Feature policy "idle-detection".');
 
-async_test(t => {
+promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, cross_origin_src,
       expect_feature_available_default, 'idle-detection');
 }, 'Attribute allow="idle-detection" in top-level frame can be enabled ' +
    'in cross-origin iframe using Feature policy "idle-detection".');
 
-async_test(t => {
+promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, cross_origin_worker_frame_src,
       expect_feature_available_default, 'idle-detection');
 }, 'Attribute allow="idle-detection" in top-level frame can be enabled ' +
    'in a worker in cross-origin iframe using Feature policy "idle-detection".');
 
 </script>
 </body>
--- a/testing/web-platform/tests/idle-detection/idle-detection-allowed-by-feature-policy.https.sub.html
+++ b/testing/web-platform/tests/idle-detection/idle-detection-allowed-by-feature-policy.https.sub.html
@@ -1,49 +1,55 @@
 <!DOCTYPE html>
 <body>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-vendor.js></script>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
 <script src=/feature-policy/resources/featurepolicy.js></script>
 <script>
 'use strict';
 
 const sub = 'https://{{domains[www]}}:{{ports[https][0]}}';
 const same_origin_src =
   '/feature-policy/resources/feature-policy-idle-detection.html'
 const same_origin_worker_frame_src =
     '/feature-policy/resources/feature-policy-idle-detection-worker.html';
 const cross_origin_src = sub + same_origin_src;
 const cross_origin_worker_frame_src = sub + same_origin_worker_frame_src;
 
-promise_test(async () => {
+promise_setup(async () => {
+  await test_driver.set_permission({ name: 'notifications' }, 'granted', false);
+});
+
+promise_test(async t => {
   await new IdleDetector().start();
 },
   'Feature-Policy {"idle-detection" : ["*"]} explicity set by top-level ' +
   'frame allows the top-level document.');
 
-async_test(t => {
+promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, same_origin_src,
       expect_feature_available_default);
 }, 'Feature-Policy {"idle-detection" : ["*"]} explicity set by top-level ' +
    'frame allows same-origin iframes.');
 
-async_test(t => {
+promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, same_origin_worker_frame_src,
       expect_feature_available_default);
 }, 'Feature-Policy {"idle-detection" : ["*"]} explicity set by top-level ' +
    'frame allows workers in same-origin iframes.');
 
-async_test(t => {
+promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, cross_origin_src,
       expect_feature_available_default);
 }, 'Feature-Policy {"idle-detection" : ["*"]} explicity set by top-level ' +
    'frame allows cross-origin iframes.');
 
-async_test(t => {
+promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, cross_origin_worker_frame_src,
       expect_feature_available_default);
 }, 'Feature-Policy {"idle-detection" : ["*"]} explicity set by top-level ' +
    'frame allows workers in cross-origin iframes.');
 
 fetch_tests_from_worker(new Worker(
   'resources/idle-detection-allowed-by-feature-policy-worker.js'))
 
--- a/testing/web-platform/tests/idle-detection/idle-detection-default-feature-policy.https.sub.html
+++ b/testing/web-platform/tests/idle-detection/idle-detection-default-feature-policy.https.sub.html
@@ -1,33 +1,39 @@
 <!DOCTYPE html>
 <body>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-vendor.js></script>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
 <script src=/feature-policy/resources/featurepolicy.js></script>
 <script>
 'use strict';
 
 const same_origin_src =
   '/feature-policy/resources/feature-policy-idle-detection.html'
 const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
   same_origin_src;
 
-promise_test(async () => {
+promise_setup(async () => {
+  await test_driver.set_permission({ name: 'notifications' }, 'granted', false);
+});
+
+promise_test(async t => {
   await new IdleDetector().start()
 },
   'Default "idle-detection" feature policy ["self"] ' +
   'allows the top-level document.');
 
-async_test(t => {
+promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, same_origin_src,
       expect_feature_available_default);
 }, 'Default "idle-detection" feature policy ["self"] ' +
    'allows same-origin iframes.');
 
-async_test(t => {
+promise_test(async t => {
   test_feature_availability('new IdleDetector().start()', t, cross_origin_src,
       expect_feature_unavailable_default);
 }, 'Default "idle-detection" feature policy ["self"] ' +
    'disallows cross-origin iframes.');
 
 </script>
 </body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/idle-detection/idle-permission.tentative.https.window.js
@@ -0,0 +1,24 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+'use strict';
+
+promise_test(async t => {
+    await test_driver.set_permission(
+        { name: 'notifications' }, 'denied', false);
+
+    let status = new IdleDetector();
+    await promise_rejects_dom(t, 'NotAllowedError', status.start());
+}, "Deny notifications permission should work.");
+
+promise_test(async t => {
+    await test_driver.set_permission(
+        { name: 'notifications' }, 'granted', false);
+
+    let status = new IdleDetector();
+    await status.start();
+
+    assert_true(['active', 'idle'].includes(status.state.user),
+                  'status has a valid user state');
+    assert_true(['locked', 'unlocked'].includes(status.state.screen),
+                  'status has a valid screen state');
+}, "Grant notifications permission should work.");
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/idle-detection/idlharness-worker.https.window.js
@@ -0,0 +1,10 @@
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+
+'use strict';
+
+promise_test(async t => {
+  await test_driver.set_permission({ name: 'notifications' }, 'granted', false);
+
+  await fetch_tests_from_worker(new Worker('resources/idlharness-worker.js'));
+}, 'Run idlharness tests in a worker.');
deleted file mode 100644
--- a/testing/web-platform/tests/idle-detection/idlharness.https.any.js
+++ /dev/null
@@ -1,36 +0,0 @@
-// META: script=/resources/WebIDLParser.js
-// META: script=/resources/idlharness.js
-
-// https://github.com/samuelgoto/idle-detection
-
-'use strict';
-
-promise_test(async (t) => {
-  const srcs = ['./idle-detection.idl',
-                '/interfaces/dom.idl',
-                '/interfaces/html.idl'];
-
-  const [idle, dom, html] = await Promise.all(
-    srcs.map(i => fetch(i).then(r => r.text()))
-  );
-
-  const idl_array = new IdlArray();
-  idl_array.add_idls(idle);
-  idl_array.add_dependency_idls(dom);
-  idl_array.add_dependency_idls(html);
-
-  self.idle = new IdleDetector({threshold: 60});
-
-  let watcher = new EventWatcher(t, self.idle, ["change"]);
-
-  self.idle.start();
-
-  await watcher.wait_for("change");
-
-  idl_array.add_objects({
-    IdleDetector: ['idle'],
-    IdleState: ['idle.state']
-  });
-
-  idl_array.test();
-}, 'Test IDL implementation of Idle Detection API');
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/idle-detection/idlharness.https.window.js
@@ -0,0 +1,38 @@
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+// META: script=/resources/testdriver.js
+// META: script=/resources/testdriver-vendor.js
+
+// https://github.com/samuelgoto/idle-detection
+
+'use strict';
+
+promise_test(async (t) => {
+  await test_driver.set_permission({ name: 'notifications' }, 'granted', false);
+
+  const srcs = ['./idle-detection.idl',
+                '/interfaces/dom.idl',
+                '/interfaces/html.idl'];
+
+  const [idle, dom, html] = await Promise.all(
+    srcs.map(i => fetch(i).then(r => r.text()))
+  );
+
+  const idl_array = new IdlArray();
+  idl_array.add_idls(idle);
+  idl_array.add_dependency_idls(dom);
+  idl_array.add_dependency_idls(html);
+
+  self.idle = new IdleDetector({threshold: 60});
+  let watcher = new EventWatcher(t, self.idle, ["change"]);
+  let initial_state = watcher.wait_for("change");
+  await self.idle.start();
+  await initial_state;
+
+  idl_array.add_objects({
+    IdleDetector: ['idle'],
+    IdleState: ['idle.state']
+  });
+
+  idl_array.test();
+}, 'Test IDL implementation of Idle Detection API');
--- a/testing/web-platform/tests/idle-detection/interceptor.https.html
+++ b/testing/web-platform/tests/idle-detection/interceptor.https.html
@@ -1,47 +1,51 @@
 <!DOCTYPE html>
 <link rel="help" href="https://github.com/samuelgoto/idle-detection">
 <title>Tests the Idle Detection API</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
 <script src="/gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
 <script src="/gen/mojo/public/mojom/base/string16.mojom.js"></script>
 <script src="/gen/mojo/public/mojom/base/time.mojom.js"></script>
 <script src="/gen/third_party/blink/public/mojom/idle/idle_manager.mojom.js"></script>
 <script src="mock.js"></script>
 <script>
 'use strict';
 
+promise_setup(async t => {
+  await test_driver.set_permission({ name: 'notifications' }, 'granted', false);
+})
+
 promise_test(async t => {
   // Basic test that expects start() to call internally
   // addMonitor, which in turn return an ACTIVE state.
   expect(addMonitor).andReturn((threshold, monitorPtr) => {
       return Promise.resolve({
         state: {
           user: UserIdleState.ACTIVE,
           screen: ScreenIdleState.LOCKED
         }
       });
   });
 
   let detector = new IdleDetector({threshold: 60});
-
   let watcher = new EventWatcher(t, detector, ["change"]);
+  let initial_state = watcher.wait_for("change");
 
   await detector.start();
-
-  // Waits for the first event.
-  await watcher.wait_for("change");
+  await initial_state;
 
   assert_equals(detector.state.user, "active");
   assert_equals(detector.state.screen, "locked");
 
   detector.stop();
-}, 'query()');
+}, 'start()');
 
 promise_test(async t => {
   // Verifies that an event is thrown when a change of state from IDLE to ACTIVE
   // is detected.
   expect(addMonitor).andReturn((threshold, monitorPtr) => {
       let first = Promise.resolve({
         state: {
           user: UserIdleState.ACTIVE,
@@ -55,23 +59,21 @@ promise_test(async t => {
           screen: ScreenIdleState.UNLOCKED
         });
       }, 0);
 
       return first;
     });
 
   let detector = new IdleDetector({threshold: 60});
-
   let watcher = new EventWatcher(t, detector, ["change"]);
+  let initial_state = watcher.wait_for("change");
 
   await detector.start();
-
-  // Wait for the initial state.
-  await watcher.wait_for("change");
+  await initial_state;
 
   // Wait for the first change in state.
   await watcher.wait_for("change");
 
   assert_equals(detector.state.user, "idle");
   assert_equals(detector.state.screen, "unlocked");
 
   detector.stop();
@@ -102,23 +104,21 @@ promise_test(async t => {
           user: UserIdleState.ACTIVE,
           screen: ScreenIdleState.UNLOCKED
         });
       }, 1);
       return first;
     });
 
   let detector = new IdleDetector({threshold: 60});
-
   let watcher = new EventWatcher(t, detector, ["change"]);
+  let initial_state = watcher.wait_for("change");
 
   await detector.start();
-
-  // Waits for the initial state.
-  await watcher.wait_for("change");
+  await initial_state;
 
   // Waits for the first event.
   await watcher.wait_for("change");
   assert_equals(detector.state.user, "idle");
 
   // Waits for the second event.
   await watcher.wait_for("change");
   assert_equals(detector.state.user, "active");
@@ -133,23 +133,21 @@ promise_test(async t => {
         state: {
           user: UserIdleState.ACTIVE,
           screen: ScreenIdleState.LOCKED
         }
       });
     });
 
   let detector = new IdleDetector({threshold: 60});
-
   let watcher = new EventWatcher(t, detector, ["change"]);
+  let initial_state = watcher.wait_for("change");
 
   await detector.start();
-
-  // waits for the initial state.
-  await watcher.wait_for("change");
+  await initial_state;
 
   assert_equals(detector.state.screen, "locked");
 
   detector.stop();
 }, 'locked screen');
 
 promise_test(async t => {
   expect(addMonitor).andReturn((threshold, monitorPtr) => {
@@ -186,33 +184,37 @@ promise_test(async t => {
           screen: ScreenIdleState.UNLOCKED
         }
       });
     });
 
   let detector = new IdleDetector({threshold: 60});
 
   let watcher = new EventWatcher(t, detector, ["change"]);
+  let initial_state = watcher.wait_for("change");
 
   // Calling start() multiple times should be safe.
-  await detector.start();
-  await detector.start();
-  await detector.start();
-  await detector.start();
+  await Promise.all([
+    detector.start(),
+    detector.start(),
+    detector.start(),
+    detector.start()
+  ]);
 
-  // waits for the initial state.
-  await watcher.wait_for("change");
+  await initial_state;
   assert_equals(detector.state.user, "active");
   assert_equals(detector.state.screen, "unlocked");
 
   // Calling stop() multiple times should be safe.
-  detector.stop();
-  detector.stop();
-  detector.stop();
-  detector.stop();
+  await Promise.all([
+    detector.stop(),
+    detector.stop(),
+    detector.stop(),
+    detector.stop()
+  ]);
 }, 'Safe to call start() or stop() multiple times');
 
 promise_test(async t => {
   expect(addMonitor).andReturn((threshold, monitorPtr) => {
       return Promise.resolve({
         state: {
           user: UserIdleState.ACTIVE,
           screen: ScreenIdleState.UNLOCKED
@@ -221,21 +223,21 @@ promise_test(async t => {
     });
 
   let detector = new IdleDetector({threshold: 60});
 
   // Calling stop() before start() is a no-op.
   detector.stop();
 
   let watcher = new EventWatcher(t, detector, ["change"]);
+  let initial_state = watcher.wait_for("change");
 
   await detector.start();
+  await initial_state;
 
-  // waits for the initial state.
-  await watcher.wait_for("change");
   assert_equals(detector.state.user, "active");
   assert_equals(detector.state.screen, "unlocked");
 
   detector.stop();
 }, 'Calling stop() after start() is a no-op');
 
 promise_test(async t => {
   expect(addMonitor).andReturn((threshold, monitorPtr) => {
@@ -245,32 +247,35 @@ promise_test(async t => {
           screen: ScreenIdleState.UNLOCKED
         }
       });
     });
 
   let detector = new IdleDetector({threshold: 60});
 
   let watcher = new EventWatcher(t, detector, ["change"]);
+  let initial_state = watcher.wait_for("change");
 
   await detector.start();
-  await watcher.wait_for("change");
+  await initial_state;
+
   detector.stop();
 
   expect(addMonitor).andReturn((threshold, monitorPtr) => {
       return Promise.resolve({
         state: {
           user: UserIdleState.IDLE,
           screen: ScreenIdleState.LOCKED
         }
       });
     });
 
   // Restarting the monitor.
+  initial_state = watcher.wait_for("change");
   await detector.start();
-  await watcher.wait_for("change");
+  await initial_state;
   assert_equals(detector.state.user, "idle");
   assert_equals(detector.state.screen, "locked");
 
   detector.stop();
 }, 'Calling start() after stop(): re-starting monitor.');
 
 </script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/idle-detection/resources/idlharness-worker.js
@@ -0,0 +1,23 @@
+'use strict';
+
+importScripts("/resources/testharness.js");
+importScripts("/resources/WebIDLParser.js", "/resources/idlharness.js");
+
+idl_test(
+    ['../idle-detection/idle-detection'],
+    ['dom', 'html'],
+    async (idl_array, t) => {
+      self.idle = new IdleDetector({threshold: 60});
+      let watcher = new EventWatcher(t, self.idle, ["change"]);
+      let initial_state = watcher.wait_for("change");
+      await self.idle.start();
+      await initial_state;
+
+      idl_array.add_objects({
+        IdleDetector: ['idle'],
+        IdleState: ['idle.state']
+      });
+    }
+);
+
+done();