Worklet: Add WPT tests for ServiceWorker interception on addModule()

WPT Tests for ServiceWorker interception on dynamic import will be added in a
following CL.

Bug: 773778
Change-Id: I636b39f3e377adb36a664bac9cf2f50516a90ca0
Reviewed-on: https://chromium-review.googlesource.com/754422
Commit-Queue: Hiroki Nakagawa <nhiroki@chromium.org>
Reviewed-by: Kinuko Yasuda <kinuko@chromium.org>
Cr-Commit-Position: refs/heads/master@{#514332}
diff --git a/worklets/animation-worklet-service-worker-interception.https.html b/worklets/animation-worklet-service-worker-interception.https.html
new file mode 100644
index 0000000..33000ee
--- /dev/null
+++ b/worklets/animation-worklet-service-worker-interception.https.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="resources/worklet-test-utils.js"></script>
+    <script src="resources/service-worker-interception-tests.js"></script>
+    <script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+</head>
+<body>
+<script>
+    runServiceWorkerInterceptionTests("animation");
+</script>
+</body>
+</html>
diff --git a/worklets/paint-worklet-service-worker-interception.https.html b/worklets/paint-worklet-service-worker-interception.https.html
new file mode 100644
index 0000000..fdce9ca
--- /dev/null
+++ b/worklets/paint-worklet-service-worker-interception.https.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="resources/worklet-test-utils.js"></script>
+    <script src="resources/service-worker-interception-tests.js"></script>
+    <script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+</head>
+<body>
+<script>
+    runServiceWorkerInterceptionTests("paint");
+</script>
+</body>
+</html>
diff --git a/worklets/resources/addmodule-window.html b/worklets/resources/addmodule-window.html
new file mode 100644
index 0000000..6263e6e
--- /dev/null
+++ b/worklets/resources/addmodule-window.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Worklet: Referrer</title>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="worklet-test-utils.js"></script>
+</head>
+<body>
+<script>
+// Calls addModule() on a given worklet type with a script url.
+//
+// [Message Format]
+//   - type: 'paint' (worklet types defined in get_worklet())
+//   - script_url: 'worklet-script.js'
+window.onmessage = e => {
+  const worklet_type = e.data.type;
+  const script_url = e.data.script_url;
+  get_worklet(worklet_type).addModule(script_url)
+      .then(() => window.opener.postMessage('RESOLVED', '*'))
+      .catch(e => window.opener.postMessage('REJECTED', '*'));
+};
+
+window.opener.postMessage('LOADED', '*');
+</script>
+</body>
+</html>
diff --git a/worklets/resources/service-worker-interception-tests.js b/worklets/resources/service-worker-interception-tests.js
new file mode 100644
index 0000000..001707f
--- /dev/null
+++ b/worklets/resources/service-worker-interception-tests.js
@@ -0,0 +1,91 @@
+function openWindow(url) {
+  return new Promise(resolve => {
+      let win = window.open(url, '_blank');
+      add_result_callback(() => win.close());
+      window.onmessage = e => {
+        assert_equals(e.data, 'LOADED');
+        resolve(win);
+      };
+    });
+}
+
+// Runs a series of tests related to service worker interception for a worklet.
+//
+// Usage:
+// runServiceWorkerInterceptionTests("paint");
+function runServiceWorkerInterceptionTests(worklet_type) {
+    const worklet = get_worklet(worklet_type);
+
+    // Tests that a worklet should be controlled by the owner document's service
+    // worker.
+    //
+    // [Current document] registers a service worker for Window's URL.
+    // --(open)--> [Window] should be controlled by the service worker.
+    //   --(addModule)--> [Worklet] should be controlled by the service worker.
+    promise_test(t => {
+        const kWindowURL = 'resources/addmodule-window.html';
+        const kServiceWorkerScriptURL = 'resources/service-worker.js';
+        // This doesn't contain the 'resources/' prefix because this will be
+        // imported from a html file under resources/.
+        const kWorkletScriptURL = 'non-existent-worklet-script.js';
+
+        return service_worker_unregister_and_register(
+            t, kServiceWorkerScriptURL, kWindowURL)
+          .then(r => {
+              add_result_callback(() => r.unregister());
+              return wait_for_state(t, r.installing, 'activated');
+            })
+          .then(() => openWindow(kWindowURL))
+          .then(win => {
+              assert_not_equals(win.navigator.serviceWorker.controller, null,
+                                'The document should be controlled.');
+              const promise = new Promise(r => window.onmessage = r);
+              // The worklet script on kWorkletScriptURL doesn't exist but the
+              // service worker serves it, so the addModule() should succeed.
+              win.postMessage({ type: worklet_type,
+                                script_url: kWorkletScriptURL }, '*');
+              return promise;
+            })
+          .then(msg_event => assert_equals(msg_event.data, 'RESOLVED'));
+    }, 'Importing a script from a controlled document should be intercepted ' +
+       'by a service worker.');
+
+    // Tests that a worklet should not be controlled by a service worker other
+    // than the owner document's service worker.
+    //
+    // [Current document] registers a service worker for Worklet's URL.
+    // --(open)--> [Window] should not be controlled by the service worker.
+    //   --(addModule)--> [Worklet] should not be controlled by the service
+    //                    worker.
+    promise_test(t => {
+        const kWindowURL = 'resources/addmodule-window.html';
+        const kServiceWorkerScriptURL = 'resources/service-worker.js';
+        // This doesn't contain the 'resources/' prefix because this will be
+        // imported from a html file under resources/.
+        const kWorkletScriptURL = 'non-existent-worklet-script.js';
+
+        return service_worker_unregister_and_register(
+            t, kServiceWorkerScriptURL, 'resources/' + kWorkletScriptURL)
+          .then(r =>  {
+              add_result_callback(() => r.unregister());
+              return wait_for_state(t, r.installing, 'activated');
+            })
+          .then(() => openWindow(kWindowURL))
+          .then(win => {
+              assert_equals(win.navigator.serviceWorker.controller, null,
+                            'The document should not be controlled.');
+              const promise = new Promise(r => window.onmessage = r);
+              // The worklet script on kWorkletScriptURL doesn't exist and the
+              // service worker doesn't serve it, so the addModule() should
+              // fail.
+              win.postMessage({ type: worklet_type,
+                                script_url: kWorkletScriptURL }, '*');
+              return promise;
+            })
+          .then(msg_event => assert_equals(msg_event.data, 'REJECTED'));
+    }, 'Importing a script from a non-controlled document should not be ' +
+       'intercepted by a service worker even if the script is under the ' +
+       'service worker scope.');
+
+    // TODO(nhiroki): Add tests for dynamic import.
+}
diff --git a/worklets/resources/service-worker.js b/worklets/resources/service-worker.js
new file mode 100644
index 0000000..89765ca
--- /dev/null
+++ b/worklets/resources/service-worker.js
@@ -0,0 +1,4 @@
+self.addEventListener('fetch', e => {
+  if (e.request.url.indexOf('non-existent-worklet-script.js') != -1)
+    e.respondWith(fetch('empty-worklet-script.js'));
+});