diff --git a/docs/writing-tests/testharness.md b/docs/writing-tests/testharness.md
index 7cf7f73a868e0a..49060bdd8e6cd7 100644
--- a/docs/writing-tests/testharness.md
+++ b/docs/writing-tests/testharness.md
@@ -140,6 +140,8 @@ It is possible to customize the set of scopes with a metadata comment, such as
// ==> would only run in the window and service worker scope
// META: global=dedicatedworker
// ==> would run in the default dedicated worker scope
+// META: global=dedicatedworker-module
+// ==> would run in the dedicated worker scope as a module
// META: global=worker
// ==> would run in the dedicated, shared, and service worker scopes
```
@@ -149,8 +151,13 @@ are:
* `window` (default): to be run at x.any.html
* `dedicatedworker` (default): to be run at x.any.worker.html
-* `serviceworker`: to be run at x.any.serviceworker.html
(`.https` is implied)
+* `dedicatedworker-module` to be run at x.any.worker-module.html
+* `serviceworker`: to be run at x.any.serviceworker.html
(`.https` is
+ implied)
+* `serviceworker-module`: to be run at x.any.serviceworker-module.html
+ (`.https` is implied)
* `sharedworker`: to be run at x.any.sharedworker.html
+* `sharedworker-module`: to be run at x.any.sharedworker-module.html
* `jsshell`: to be run in a JavaScript shell, without access to the DOM
(currently only supported in SpiderMonkey, and skipped in wptrunner)
* `worker`: shorthand for the dedicated, shared, and service worker scopes
@@ -183,6 +190,10 @@ Use `// META: script=link/to/resource.js` at the beginning of the resource. For
can be used to include both the global and a local `utils.js` in a test.
+In window environments, the script will be included using a classic `
-
-
-
-
-
-
diff --git a/service-workers/service-worker/no-dynamic-import-in-module.any.js b/service-workers/service-worker/no-dynamic-import-in-module.any.js
new file mode 100644
index 00000000000000..84b17629ccddc9
--- /dev/null
+++ b/service-workers/service-worker/no-dynamic-import-in-module.any.js
@@ -0,0 +1,7 @@
+// META: global=serviceworker-module
+
+// This is imported to ensure import('./basic-module-2.js') fails even if
+// it has been previously statically imported.
+import './basic-module-2.js';
+
+import './resources/no-dynamic-import.js';
diff --git a/service-workers/service-worker/no-dynamic-import.any.js b/service-workers/service-worker/no-dynamic-import.any.js
new file mode 100644
index 00000000000000..25b370b7094bf8
--- /dev/null
+++ b/service-workers/service-worker/no-dynamic-import.any.js
@@ -0,0 +1,3 @@
+// META: global=serviceworker
+
+importScripts('resources/no-dynamic-import.js');
diff --git a/service-workers/service-worker/resources/basic-module-2.js b/service-workers/service-worker/resources/basic-module-2.js
new file mode 100644
index 00000000000000..189b1c87fee75c
--- /dev/null
+++ b/service-workers/service-worker/resources/basic-module-2.js
@@ -0,0 +1 @@
+export default 'hello again!';
diff --git a/service-workers/service-worker/resources/basic-module.js b/service-workers/service-worker/resources/basic-module.js
new file mode 100644
index 00000000000000..789a89bc630847
--- /dev/null
+++ b/service-workers/service-worker/resources/basic-module.js
@@ -0,0 +1 @@
+export default 'hello!';
diff --git a/service-workers/service-worker/resources/no-dynamic-import.js b/service-workers/service-worker/resources/no-dynamic-import.js
new file mode 100644
index 00000000000000..ecedd6c5d75c7d
--- /dev/null
+++ b/service-workers/service-worker/resources/no-dynamic-import.js
@@ -0,0 +1,18 @@
+/** @type {[name: string, url: string][]} */
+const importUrlTests = [
+ ["Module URL", "./basic-module.js"],
+ // In no-dynamic-import-in-module.any.js, this module is also statically imported
+ ["Another module URL", "./basic-module-2.js"],
+ [
+ "Module data: URL",
+ "data:text/javascript;charset=utf-8," +
+ encodeURIComponent(`export default 'hello!';`),
+ ],
+];
+
+for (const [name, url] of importUrlTests) {
+ promise_test(
+ (t) => promise_rejects_js(t, TypeError, import(url), "Import must reject"),
+ name
+ );
+}
diff --git a/tools/manifest/sourcefile.py b/tools/manifest/sourcefile.py
index 5b99f90e458a29..34e679eb8b73b7 100644
--- a/tools/manifest/sourcefile.py
+++ b/tools/manifest/sourcefile.py
@@ -84,9 +84,13 @@ def read_script_metadata(f, regexp):
_any_variants = {
"window": {"suffix": ".any.html"},
"serviceworker": {"force_https": True},
+ "serviceworker-module": {"force_https": True},
"sharedworker": {},
+ "sharedworker-module": {},
"dedicatedworker": {"suffix": ".any.worker.html"},
+ "dedicatedworker-module": {"suffix": ".any.worker-module.html"},
"worker": {"longhand": {"dedicatedworker", "sharedworker", "serviceworker"}},
+ "worker-module": {},
"jsshell": {"suffix": ".any.js"},
} # type: Dict[Text, Dict[Text, Any]]
diff --git a/tools/serve/serve.py b/tools/serve/serve.py
index 644a8a93f704f0..8e1aaa8dc73c1a 100644
--- a/tools/serve/serve.py
+++ b/tools/serve/serve.py
@@ -227,6 +227,22 @@ class WorkersHandler(HtmlWrapperHandler):
"""
+class WorkerModulesHandler(HtmlWrapperHandler):
+ global_type = "dedicatedworker-module"
+ path_replace = [(".any.worker-module.html", ".any.js", ".any.worker-module.js"),
+ (".worker.html", ".worker.js")]
+ wrapper = """
+
+%(meta)s
+
+
+
+
+"""
+
+
class WindowHandler(HtmlWrapperHandler):
path_replace = [(".window.html", ".window.js")]
wrapper = """
@@ -275,6 +291,21 @@ class SharedWorkersHandler(HtmlWrapperHandler):
"""
+class SharedWorkerModulesHandler(HtmlWrapperHandler):
+ global_type = "sharedworker-module"
+ path_replace = [(".any.sharedworker-module.html", ".any.js", ".any.worker-module.js")]
+ wrapper = """
+
+%(meta)s
+
+
+
+
+"""
+
+
class ServiceWorkersHandler(HtmlWrapperHandler):
global_type = "serviceworker"
path_replace = [(".any.serviceworker.html", ".any.js", ".any.worker.js")]
@@ -296,8 +327,54 @@ class ServiceWorkersHandler(HtmlWrapperHandler):
"""
-class AnyWorkerHandler(WrapperHandler):
+class ServiceWorkerModulesHandler(HtmlWrapperHandler):
+ global_type = "serviceworker-module"
+ path_replace = [(".any.serviceworker-module.html",
+ ".any.js", ".any.worker-module.js")]
+ wrapper = """
+
+%(meta)s
+
+
+
+
+"""
+
+
+class BaseWorkerHandler(WrapperHandler):
headers = [('Content-Type', 'text/javascript')]
+
+ def _meta_replacement(self, key, value):
+ return None
+
+ @abc.abstractmethod
+ def _create_script_import(self, attribute):
+ # Take attribute (a string URL to a JS script) and return JS source to import the script
+ # into the worker.
+ pass
+
+ def _script_replacement(self, key, value):
+ if key == "script":
+ attribute = value.replace("\\", "\\\\").replace('"', '\\"')
+ return self._create_script_import(attribute)
+ if key == "title":
+ value = value.replace("\\", "\\\\").replace('"', '\\"')
+ return 'self.META_TITLE = "%s";' % value
+ return None
+
+
+class ClassicWorkerHandler(BaseWorkerHandler):
path_replace = [(".any.worker.js", ".any.js")]
wrapper = """%(meta)s
self.GLOBAL = {
@@ -310,17 +387,25 @@ class AnyWorkerHandler(WrapperHandler):
done();
"""
- def _meta_replacement(self, key, value):
- return None
+ def _create_script_import(self, attribute):
+ return 'importScripts("%s")' % attribute
- def _script_replacement(self, key, value):
- if key == "script":
- attribute = value.replace("\\", "\\\\").replace('"', '\\"')
- return 'importScripts("%s")' % attribute
- if key == "title":
- value = value.replace("\\", "\\\\").replace('"', '\\"')
- return 'self.META_TITLE = "%s";' % value
- return None
+
+class ModuleWorkerHandler(BaseWorkerHandler):
+ path_replace = [(".any.worker-module.js", ".any.js")]
+ wrapper = """%(meta)s
+self.GLOBAL = {
+ isWindow: function() { return false; },
+ isWorker: function() { return true; },
+};
+import "/resources/testharness.js";
+%(script)s
+import "%(path)s";
+done();
+"""
+
+ def _create_script_import(self, attribute):
+ return 'import "%s";' % attribute
rewrites = [("GET", "/resources/WebIDLParser.js", "/resources/webidl2/lib/webidl2.js")]
@@ -368,11 +453,15 @@ def add_mount_point(self, url_base, path):
routes = [
("GET", "*.worker.html", WorkersHandler),
+ ("GET", "*.worker-module.html", WorkerModulesHandler),
("GET", "*.window.html", WindowHandler),
("GET", "*.any.html", AnyHtmlHandler),
("GET", "*.any.sharedworker.html", SharedWorkersHandler),
+ ("GET", "*.any.sharedworker-module.html", SharedWorkerModulesHandler),
("GET", "*.any.serviceworker.html", ServiceWorkersHandler),
- ("GET", "*.any.worker.js", AnyWorkerHandler),
+ ("GET", "*.any.serviceworker-module.html", ServiceWorkerModulesHandler),
+ ("GET", "*.any.worker.js", ClassicWorkerHandler),
+ ("GET", "*.any.worker-module.js", ModuleWorkerHandler),
("GET", "*.asis", handlers.AsIsHandler),
("GET", "/.well-known/origin-policy", handlers.PythonScriptHandler),
("*", "*.py", handlers.PythonScriptHandler),
diff --git a/tools/wptserve/tests/functional/test_handlers.py b/tools/wptserve/tests/functional/test_handlers.py
index 0acfac4d1945f7..1dc2a9ba7bb3e5 100644
--- a/tools/wptserve/tests/functional/test_handlers.py
+++ b/tools/wptserve/tests/functional/test_handlers.py
@@ -436,12 +436,12 @@ def test_serviceworker_html(self):
'text/html', serve.ServiceWorkersHandler)
-class TestAnyWorkerHandler(TestWrapperHandlerUsingServer):
+class TestClassicWorkerHandler(TestWrapperHandlerUsingServer):
dummy_files = {'bar.any.js': b''}
def test_any_work_js(self):
self.run_wrapper_test('bar.any.worker.js', 'text/javascript',
- serve.AnyWorkerHandler)
+ serve.ClassicWorkerHandler)
if __name__ == '__main__':
diff --git a/workers/examples/general.any.js b/workers/examples/general.any.js
index 8906350a7e8d4a..cb5d61eafbece7 100644
--- a/workers/examples/general.any.js
+++ b/workers/examples/general.any.js
@@ -7,7 +7,7 @@
// testharness.js is imported (via importScripts()) by generated glue code by
// WPT server.
-// See AnyWorkerHandler in
+// See ClassicWorkerHandler in
// https://github.com/web-platform-tests/wpt/blob/master/tools/serve/serve.py.
// ============================================================================
@@ -30,5 +30,5 @@ test(() => {
// done() is NOT needed in .any.js tests, as it is called by generated
// glue code by the WPT server.
-// See AnyWorkerHandler in
+// See ClassicWorkerHandler in
// https://github.com/web-platform-tests/wpt/blob/master/tools/serve/serve.py.