summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/webappapis/system-state-and-capabilities
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/html/webappapis/system-state-and-capabilities
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/html/webappapis/system-state-and-capabilities')
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/clientinformation.window.js8
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/get-navigatorlanguage-manual.html16
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/historical.https.window.js16
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-indexed.html28
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-window-controls-overlay.html41
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator.any.js106
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator_user_agent.https.html53
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator_user_agent.tentative.html8
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorcookies-cookieenabled-false-manual.html17
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorcookies-cookieenabled-true.html13
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorlanguage.html19
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/per-global.window.js4
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/plugins-and-mimetypes.html83
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-manual.https.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-nosw-manual.https.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-nosw.https.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment.https.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-path-manual.https.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-path.https.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-manual.https.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-nosw-manual.https.html20
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-nosw.https.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query.https.html25
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol.https.html218
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol.tentative.https.html30
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-sw.js3
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-tools.js53
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler.html23
-rw-r--r--testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/secure_context.html18
29 files changed, 982 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/clientinformation.window.js b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/clientinformation.window.js
new file mode 100644
index 0000000000..197a3b13e9
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/clientinformation.window.js
@@ -0,0 +1,8 @@
+test(() => {
+ assert_equals(window.clientInformation, window.navigator);
+}, "window.clientInformation exists and equals window.navigator");
+
+test(() => {
+ window.clientInformation = 1;
+ assert_equals(window.clientInformation, 1);
+}, "window.clientInformation is Replaceable");
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/get-navigatorlanguage-manual.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/get-navigatorlanguage-manual.html
new file mode 100644
index 0000000000..4bdab91121
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/get-navigatorlanguage-manual.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>NavigatorLanguage: navigator.language returns the user's preferred language</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#navigatorlanguage">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<h2>Precondition</h2>
+<p>The user agent's preferred language is set as English (en).</p>
+<div id="log"></div>
+<script>
+ test(function() {
+ assert_equals(navigator.language, "en");
+ });
+</script>
+
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/historical.https.window.js b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/historical.https.window.js
new file mode 100644
index 0000000000..f6b9db078e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/historical.https.window.js
@@ -0,0 +1,16 @@
+[
+ "registerContentHandler",
+ "isProtocolHandlerRegistered",
+ "isContentHandlerRegistered",
+ "unregisterContentHandler"
+].forEach(method => {
+ test(() => {
+ assert_false(method in self.navigator);
+ }, method + "() is removed");
+});
+
+test(() => {
+ let called = false;
+ self.navigator.registerProtocolHandler("web+test", "%s", { toString: () => called = true });
+ assert_false(called);
+}, "registerProtocolHandler has no third argument");
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-indexed.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-indexed.html
new file mode 100644
index 0000000000..a971fe9d1c
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-indexed.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for lack of indexed getter on Navigator</title>
+<link rel="author" title="Ms2ger" href="mailto:Ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-navigator-object">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_false("0" in window.navigator);
+ assert_equals(window.navigator[0], undefined);
+}, "window.navigator[0] should not exist");
+test(function() {
+ window.navigator[0] = "pass";
+ assert_true("0" in window.navigator);
+ assert_equals(window.navigator[0], "pass");
+}, "window.navigator[0] should be settable");
+test(function() {
+ assert_false("-1" in window.navigator);
+ assert_equals(window.navigator[-1], undefined);
+}, "window.navigator[-1] should not exist");
+test(function() {
+ window.navigator[-1] = "pass";
+ assert_true("-1" in window.navigator);
+ assert_equals(window.navigator[-1], "pass");
+}, "window.navigator[-1] should be settable");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-window-controls-overlay.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-window-controls-overlay.html
new file mode 100644
index 0000000000..9cff8d3163
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-window-controls-overlay.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset='utf-8'>
+<title>navigator.windowControlsOverlay</title>
+
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+
+<script>
+ test(function(){
+ assert_idl_attribute(navigator, 'windowControlsOverlay');
+ }, 'the windowControlsOverlay object should exist on the navigator object');
+
+ test(function(){
+ assert_idl_attribute(navigator.windowControlsOverlay, 'visible');
+ }, 'visible should be a member of the windowControlsOverlay object');
+
+ test(function(){
+ assert_false(navigator.windowControlsOverlay.visible);
+ }, 'visible should be false');
+
+ test(function(){
+ assert_idl_attribute(navigator.windowControlsOverlay, 'getTitlebarAreaRect');
+ }, 'getTitlebarAreaRect should be a method of the windowControlsOverlay object');
+
+ test(function(){
+ var rect = navigator.windowControlsOverlay.getTitlebarAreaRect();
+ assert_true(rect instanceof DOMRect);
+ }, 'getTitlebarAreaRect return type should be DOMRect');
+
+ test(function(){
+ var rect = navigator.windowControlsOverlay.getTitlebarAreaRect();
+ assert_equals(rect.x, 0);
+ assert_equals(rect.y, 0);
+ assert_equals(rect.width, 0);
+ assert_equals(rect.height, 0);
+ }, 'getTitlebarAreaRect should return a empty DOMRect');
+
+ test(function(){
+ assert_idl_attribute(navigator.windowControlsOverlay, 'ongeometrychange');
+ }, 'ongeometrychange should be a member of the windowControlsOverlay object');
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator.any.js b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator.any.js
new file mode 100644
index 0000000000..07bccb7880
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator.any.js
@@ -0,0 +1,106 @@
+ var compatibilityMode;
+ if (navigator.userAgent.includes("Chrome")) {
+ compatibilityMode = "Chrome";
+ } else if (navigator.userAgent.includes("WebKit")) {
+ compatibilityMode = "WebKit";
+ } else {
+ compatibilityMode = "Gecko";
+ }
+
+ test(function() {
+ assert_equals(navigator.appCodeName, "Mozilla");
+ }, "appCodeName");
+
+ test(function() {
+ assert_equals(navigator.appName, "Netscape");
+ }, "appName");
+
+ test(function() {
+ assert_equals(typeof navigator.appVersion, "string",
+ "navigator.appVersion should be a string");
+ }, "appVersion");
+
+ test(function() {
+ assert_equals(typeof navigator.platform, "string",
+ "navigator.platform should be a string");
+ }, "platform");
+
+ test(function() {
+ assert_equals(navigator.product, "Gecko");
+ }, "product");
+
+ test(function() {
+ if ("window" in self) {
+ if (compatibilityMode == "Gecko") {
+ assert_equals(navigator.productSub, "20100101");
+ } else {
+ assert_equals(navigator.productSub, "20030107");
+ }
+ } else {
+ assert_false("productSub" in navigator);
+ }
+ }, "productSub");
+
+ test(function() {
+ assert_equals(typeof navigator.userAgent, "string",
+ "navigator.userAgent should be a string");
+ }, "userAgent type");
+
+ async_test(function() {
+ var request = new XMLHttpRequest();
+ request.onload = this.step_func_done(function() {
+ assert_equals("User-Agent: " + navigator.userAgent + "\n",
+ request.response,
+ "userAgent should return the value sent in the " +
+ "User-Agent header");
+ });
+ request.open("GET", "/xhr/resources/inspect-headers.py?" +
+ "filter_name=User-Agent");
+ request.send();
+ }, "userAgent value");
+
+ test(function() {
+ if ("window" in self) {
+ if (compatibilityMode == "Chrome") {
+ assert_equals(navigator.vendor, "Google Inc.");
+ } else if (compatibilityMode == "WebKit") {
+ assert_equals(navigator.vendor, "Apple Computer, Inc.");
+ } else {
+ assert_equals(navigator.vendor, "");
+ }
+ } else {
+ assert_false("vendor" in navigator);
+ }
+ }, "vendor");
+
+ test(function() {
+ if ("window" in self) {
+ assert_equals(navigator.vendorSub, "");
+ } else {
+ assert_false("vendorSub" in navigator);
+ }
+ }, "vendorSub");
+
+ // "If the navigator compatibility mode is Gecko, then the user agent must
+ // also support the following partial interface" (taintEnabled() and oscpu)
+ // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=22555 and
+ // https://www.w3.org/Bugs/Public/show_bug.cgi?id=27820
+
+ test(function() {
+ if ("window" in self && compatibilityMode == "Gecko") {
+ assert_false(navigator.taintEnabled());
+ } else {
+ assert_false("taintEnabled" in navigator);
+ }
+ }, "taintEnabled");
+
+ test(function() {
+ if ("window" in self && compatibilityMode == "Gecko") {
+ assert_equals(typeof navigator.oscpu, "string",
+ "navigator.oscpu should be a string");
+ } else {
+ assert_false("oscpu" in navigator);
+ }
+ }, "oscpu");
+
+done()
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator_user_agent.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator_user_agent.https.html
new file mode 100644
index 0000000000..10b3dd2249
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator_user_agent.https.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(t => {
+ assert_true("userAgentData" in navigator);
+ assert_true("NavigatorUAData" in window);
+ assert_equals(typeof self.NavigatorUAData, "function")
+ }, "navigator.userAgentData is exposed.");
+
+ promise_test(async t => {
+ let didMicrotaskRun = false;
+ const uaData = navigator.userAgentData;
+ const brandRegex = /^[a-zA-Z ()\-.\/:;=?_]+$/;
+ for (brandVersionPair of uaData.brands) {
+ assert_equals(typeof brandVersionPair.brand, "string", "brand should be a string");
+ assert_regexp_match(brandVersionPair.brand, brandRegex, "brand should not contain unexpected characters");
+ assert_equals(typeof brandVersionPair.version, "string", "version should be a string");
+ }
+ assert_equals(typeof uaData.mobile, "boolean", "mobile should be a boolean");
+
+ const highEntropyData = await uaData.getHighEntropyValues([
+ "platformVersion", "architecture", "model", "uaFullVersion", "fullVersionList", "formFactor"]);
+ assert_equals(typeof highEntropyData["platform"], "string", "Platform brand should be a string");
+ assert_equals(typeof highEntropyData["platformVersion"], "string", "Platform version should be a string");
+ assert_equals(typeof highEntropyData["architecture"], "string", "Architecture should be a string");
+ assert_equals(typeof highEntropyData["model"], "string", "Model should be a string");
+ assert_equals(typeof highEntropyData["uaFullVersion"], "string", "UAFullVersion should be a string");
+ for (formFactor of highEntropyData['formFactor']) {
+ assert_equals(typeof formFactor, "string", "Each FormFactor should be a string");
+ }
+ for (brandVersionPair of highEntropyData['fullVersionList']) {
+ assert_equals(typeof brandVersionPair.brand, "string", "brand should be a string");
+ assert_regexp_match(brandVersionPair.brand, brandRegex, "brand should not contain unexpected characters");
+ assert_equals(typeof brandVersionPair.version, "string", "version should be a string");
+ }
+ const highEntropyData2 = await uaData.getHighEntropyValues([]);
+ assert_equals(typeof highEntropyData["platform"], "string", "Platform brand should be a string");
+ assert_false("platformVersion" in highEntropyData2, "Platform version should be an empty string");
+ assert_false("architecture" in highEntropyData2, "Architecture should be an empty string");
+ assert_false("model" in highEntropyData2, "Model should be an empty string");
+ assert_false("uaFullVersion" in highEntropyData2, "UAFullVersion should be an empty string");
+ assert_false("formFactor" in highEntropyData2, "FormFactor should be an empty string");
+ assert_false("fullVersionList" in highEntropyData2, "fullVersionList should not be present");
+ let finalPromise = uaData.getHighEntropyValues([]).then(() => {
+ assert_true(didMicrotaskRun, "getHighEntropyValues queued on a task");
+ });
+ await Promise.resolve().then(function() {
+ didMicrotaskRun = true;
+ });
+ return finalPromise;
+ }, "navigator.userAgentData returns a UserAgentMetadata object.");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator_user_agent.tentative.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator_user_agent.tentative.html
new file mode 100644
index 0000000000..dd4c531070
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator_user_agent.tentative.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(t => {
+ assert_false("getUserAgent" in navigator);
+ }, "navigator.getUserAgent() is not available in non-secure contexts.");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorcookies-cookieenabled-false-manual.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorcookies-cookieenabled-false-manual.html
new file mode 100644
index 0000000000..adcea90a36
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorcookies-cookieenabled-false-manual.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>navigator.cookieEnabled false</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/#dom-navigator-cookieenabled">
+<meta name="flags" content="interact">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<h2>Preconditions</h2>
+<p>Disable cookies in browser settings.</p>
+
+<script>
+ test(() => {
+ assert_false(navigator.cookieEnabled);
+ }, "navigator.cookieEnabled is false when cookies are disabled");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorcookies-cookieenabled-true.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorcookies-cookieenabled-true.html
new file mode 100644
index 0000000000..fc7f49d143
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorcookies-cookieenabled-true.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>navigator.cookieEnabled true</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/#dom-navigator-cookieenabled">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+ test(() => {
+ assert_true(navigator.cookieEnabled);
+ }, "navigator.cookieEnabled is true when cookies are enabled");
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorlanguage.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorlanguage.html
new file mode 100644
index 0000000000..d56df8a3d8
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigatorlanguage.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>NavigatorLanguage: the most preferred language is the one returned by navigator.language</title>
+<link rel="author" title="Intel" href="http://www.intel.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#navigatorlanguage">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ test(function() {
+ assert_true("language" in navigator);
+ assert_true("languages" in navigator);
+
+ assert_equals(navigator.languages[0], navigator.language,
+ "navigator.languages is the most preferred language first");
+
+ });
+</script>
+
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/per-global.window.js b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/per-global.window.js
new file mode 100644
index 0000000000..016aef8c4e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/per-global.window.js
@@ -0,0 +1,4 @@
+// META: script=/common/object-association.js
+
+testIsPerWindow("navigator");
+testIsPerWindow("clientInformation");
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/plugins-and-mimetypes.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/plugins-and-mimetypes.html
new file mode 100644
index 0000000000..af05015801
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/plugins-and-mimetypes.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Navigator.plugins and navigator.mimeTypes behavior</title>
+<link rel="author" href="mailto:domenic@chromium.org">
+<link rel="help" href="https://github.com/whatwg/html/pull/6738">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body>
+<script>
+test(() => {
+ assert_true('pdfViewerEnabled' in navigator, "property exists");
+ assert_equals(typeof navigator.pdfViewerEnabled, 'boolean', "property is boolean");
+}, "navigator.pdfViewerEnabled exists");
+
+const pluginNames = [
+ "PDF Viewer",
+ "Chrome PDF Viewer",
+ "Chromium PDF Viewer",
+ "Microsoft Edge PDF Viewer",
+ "WebKit built-in PDF"
+];
+
+const mimeTypes = [
+ "application/pdf",
+ "text/pdf"
+];
+
+if (navigator.pdfViewerEnabled) {
+ test(() => {
+ assert_equals(navigator.mimeTypes.length, mimeTypes.length, "length");
+
+ for (let i = 0; i < mimeTypes.length; ++i) {
+ const mimeType = mimeTypes[i];
+ const mimeTypeObject = navigator.mimeTypes.item(i);
+
+ assert_equals(mimeTypeObject.type, mimeType, `${i}th type`);
+ assert_equals(mimeTypeObject.description, "Portable Document Format", `${i}th description`);
+ assert_equals(mimeTypeObject.suffixes, "pdf", `${i}th suffixes`);
+ assert_equals(mimeTypeObject, navigator.mimeTypes.namedItem(mimeType), `mimeTypes.item(${i}) matches namedItem("${mimeType}")`);
+ assert_equals(mimeTypeObject.enabledPlugin, navigator.plugins[0], `${i}th enabledPlugin matches 0th Plugin`)
+ }
+ }, "navigator.mimeTypes contains the hard-coded list");
+
+ test(() => {
+ assert_equals(navigator.plugins.length, pluginNames.length, "length");
+
+ for (let i = 0; i < pluginNames.length; ++i) {
+ const pluginName = pluginNames[i];
+ const pluginObject = navigator.plugins.item(i);
+
+ assert_equals(pluginObject.name, pluginName, `${i}th name`);
+ assert_equals(pluginObject.description, "Portable Document Format", `${i}th description`);
+ assert_equals(pluginObject.filename, "internal-pdf-viewer", `${i}th filename`);
+ assert_equals(pluginObject, navigator.plugins.namedItem(pluginName), `plugins.item(${i}) matches namedItem("${pluginName}")`);
+
+ for (let j = 0; j < mimeTypes.length; ++j) {
+ const mimeType = mimeTypes[j];
+ assert_equals(pluginObject.item(j).type, navigator.mimeTypes[j].type, `item(${j}) on plugin(${i}) (${pluginObject.name})`);
+ assert_equals(pluginObject.namedItem(mimeType).type, navigator.mimeTypes.item(j).type, `namedItem("${mimeType}") on plugin(${i})`);
+ }
+ }
+ }, "navigator.plugins contains the hard-coded list");
+} else {
+ test(() => {
+ assert_equals(navigator.mimeTypes.length, 0, "length");
+ assert_equals(navigator.mimeTypes.item(0), null, "item");
+
+ for (const mimeType of mimeTypes) {
+ assert_equals(navigator.mimeTypes.namedItem(mimeType), null, `namedItem("${mimeType}")`);
+ }
+ }, "navigator.mimeTypes is empty");
+
+ test(() => {
+ assert_equals(navigator.plugins.length, 0, "length");
+ assert_equals(navigator.plugins.item(0), null, "item");
+
+ for (const pluginName of pluginNames) {
+ assert_equals(navigator.plugins.namedItem(pluginName), null, `namedItem("${pluginName}")`);
+ }
+ }, "navigator.plugins is empty");
+}
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-manual.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-manual.https.html
new file mode 100644
index 0000000000..1561786569
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-manual.https.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed -->
+<meta charset=windows-1254>
+<meta name=timeout content=long>
+<title>registerProtocolHandler() and a handler with %s in the fragment</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script>
+// Configure expectations for individual test
+window.type = "fragment";
+window.noSW = false;
+</script>
+<script src=resources/handler-tools.js></script>
+<ol>
+ <li><p>First, register the handler: <button onclick='register()'>Register</button>.
+ <li><p>Then, run the test: <button onclick='runTest()'>Run</button>.
+ <li><p>Or, run the test with U+0000 NULL: <button onclick='runTest({ includeNull: true })'>Run NULL</button>.
+</ol>
+<div id=log></div>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-nosw-manual.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-nosw-manual.https.html
new file mode 100644
index 0000000000..be3a6be666
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-nosw-manual.https.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed -->
+<meta charset=windows-1254>
+<meta name=timeout content=long>
+<title>registerProtocolHandler() and a handler with %s in the fragment (does not use a service worker)</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script>
+// Configure expectations for individual test
+window.type = "fragment";
+window.noSW = true;
+</script>
+<script src=resources/handler-tools.js></script>
+<ol>
+ <li><p>First, register the handler: <button onclick='register()'>Register</button>.
+ <li><p>Then, run the test: <button onclick='runTest()'>Run</button>.
+ <li><p>Or, run the test with U+0000 NULL: <button onclick='runTest({ includeNull: true })'>Run NULL</button>.
+</ol>
+<div id=log></div>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-nosw.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-nosw.https.html
new file mode 100644
index 0000000000..61b9722ec2
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-nosw.https.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed -->
+<meta charset=windows-1254>
+<meta name=timeout content=long>
+<title>registerProtocolHandler() and a handler with %s in the fragment (does not use a service worker)</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=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script>
+'use strict';
+promise_setup(async () => {
+ await test_driver.set_rph_registration_mode("autoAccept");
+ await test_driver.bless('handler registration');
+ register();
+});
+// Configure expectations for individual test
+window.type = "fragment";
+window.noSW = true;
+</script>
+<script src=resources/handler-tools.js></script>
+<script>
+runTest();
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment.https.html
new file mode 100644
index 0000000000..1eaefaa45f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment.https.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed -->
+<meta charset=windows-1254>
+<meta name=timeout content=long>
+<title>registerProtocolHandler() and a handler with %s in the fragment</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=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script>
+'use strict';
+promise_setup(async () => {
+ await test_driver.set_rph_registration_mode("autoAccept");
+ await test_driver.bless('handler registration');
+ register();
+});
+// Configure expectations for individual test
+window.type = "fragment";
+window.noSW = false;
+</script>
+<script src=resources/handler-tools.js></script>
+<script>
+runTest();
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-path-manual.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-path-manual.https.html
new file mode 100644
index 0000000000..085c5723ec
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-path-manual.https.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed -->
+<meta charset=windows-1254>
+<meta name=timeout content=long>
+<title>registerProtocolHandler() and a handler with %s in the path</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script>
+// Configure expectations for individual test
+window.type = "path";
+window.noSW = false;
+</script>
+<script src=resources/handler-tools.js></script>
+<ol>
+ <li><p>First, register the handler: <button onclick='register()'>Register</button>.
+ <li><p>Then, run the test: <button onclick='runTest()'>Run</button>.
+ <li><p>Or, run the test with U+0000 NULL: <button onclick='runTest({ includeNull: true })'>Run NULL</button>.
+</ol>
+<div id=log></div>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-path.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-path.https.html
new file mode 100644
index 0000000000..a86e79e42e
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-path.https.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed -->
+<meta charset=windows-1254>
+<meta name=timeout content=long>
+<title>registerProtocolHandler() and a handler with %s in the path</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=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script>
+'use strict';
+promise_setup(async () => {
+ await test_driver.set_rph_registration_mode("autoAccept");
+ await test_driver.bless('handler registration');
+ register();
+});
+// Configure expectations for individual test
+window.type = "path";
+window.noSW = false;
+</script>
+<script src=resources/handler-tools.js></script>
+<script>
+runTest();
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-manual.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-manual.https.html
new file mode 100644
index 0000000000..8ce65a5bad
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-manual.https.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed -->
+<meta charset=windows-1254>
+<meta name=timeout content=long>
+<title>registerProtocolHandler() and a handler with %s in the query</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script>
+// Configure expectations for individual test
+window.type = "query";
+window.noSW = false;
+</script>
+<script src=resources/handler-tools.js></script>
+<ol>
+ <li><p>First, register the handler: <button onclick='register()'>Register</button>.
+ <li><p>Then, run the test: <button onclick='runTest()'>Run</button>.
+ <li><p>Or, run the test with U+0000 NULL: <button onclick='runTest({ includeNull: true })'>Run NULL</button>.
+</ol>
+<div id=log></div>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-nosw-manual.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-nosw-manual.https.html
new file mode 100644
index 0000000000..9b4473fb89
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-nosw-manual.https.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed -->
+<meta charset=windows-1254>
+<meta name=timeout content=long>
+<title>registerProtocolHandler() and a handler with %s in the query (does not use a service worker)</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script>
+// Configure expectations for individual test
+window.type = "query";
+window.noSW = true;
+</script>
+<script src=resources/handler-tools.js></script>
+<ol>
+ <li><p>First, register the handler: <button onclick='register()'>Register</button>.
+ <li><p>Then, run the test: <button onclick='runTest()'>Run</button>.
+ <li><p>Or, run the test with U+0000 NULL: <button onclick='runTest({ includeNull: true })'>Run NULL</button>.
+</ol>
+<div id=log></div>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-nosw.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-nosw.https.html
new file mode 100644
index 0000000000..97f67e94ec
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query-nosw.https.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed -->
+<meta charset=windows-1254>
+<meta name=timeout content=long>
+<title>registerProtocolHandler() and a handler with %s in the query (does not use a service worker)</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=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script>
+'use strict';
+promise_setup(async () => {
+ await test_driver.set_rph_registration_mode("autoAccept");
+ await test_driver.bless('handler registration');
+ register();
+});
+// Configure expectations for individual test
+window.type = "query";
+window.noSW = true;
+</script>
+<script src=resources/handler-tools.js></script>
+<script>
+runTest();
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query.https.html
new file mode 100644
index 0000000000..8c7b2e1cfd
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-query.https.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<!-- Use a non-UTF-8 encoding to see how the handler URL is parsed -->
+<meta charset=windows-1254>
+<meta name=timeout content=long>
+<title>registerProtocolHandler() and a handler with %s in the query</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=/service-workers/service-worker/resources/test-helpers.sub.js></script>
+<script>
+'use strict';
+promise_setup(async () => {
+ await test_driver.set_rph_registration_mode("autoAccept");
+ await test_driver.bless('handler registration');
+ register();
+});
+// Configure expectations for individual test
+window.type = "query";
+window.noSW = false;
+</script>
+<script src=resources/handler-tools.js></script>
+<script>
+runTest();
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol.https.html
new file mode 100644
index 0000000000..add78d2fd4
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol.https.html
@@ -0,0 +1,218 @@
+<!DOCTYPE html>
+<meta charset='utf-8'>
+<title>protocol handlers</title>
+
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+
+<noscript><p>Enable JavaScript and reload.</p></noscript>
+
+<p><strong>Note:</strong> If your browser limits the number of handler
+registration requests on a page, you might need to disable or significantly
+increase that limit for the tests below to run.</p>
+
+<script>
+test(() => {
+ assert_idl_attribute(navigator, 'registerProtocolHandler');
+}, 'the registerProtocolHandler method should exist on the navigator object');
+
+test(() => {
+ assert_idl_attribute(navigator, 'unregisterProtocolHandler');
+}, 'the unregisterProtocolHandler method should exist on the navigator object');
+
+/* URL argument */
+[
+ '%s',
+ 'foo/%s',
+ `%s${location.href}`,
+ location.href.replace(location.protocol,
+ `${location.protocol[0]}%s${location.protocol.substring(1)}`),
+ location.href.replace(location.protocol, `${location.protocol}%s`),
+ location.href + '/%s',
+ location.href + '#%s',
+ location.href + '?foo=%s',
+ location.href + '?foo=%s&bar',
+ location.href + '/%s/bar/baz/',
+ location.href + '/%s/bar/baz/?foo=1337&bar#baz',
+ location.href + '/%s/foo/%s/',
+].forEach(url => {
+ test(() => {
+ navigator.registerProtocolHandler('tel', url, 'foo');
+ }, 'registerProtocolHandler: Valid URL "' + url + '" should work.');
+
+ test(() => {
+ navigator.unregisterProtocolHandler('tel', url);
+ }, 'unregisterProtocolHandler: Valid URL "' + url + '" should work.');
+});
+
+/* Invalid URLs */
+[
+ '',
+ '%S',
+ 'http://%s.com',
+ 'http://%s.example.com',
+ location.href.replace(location.hostname, `%s${location.hostname}`),
+ location.href.replace(location.port, `%s${location.port}`),
+ location.href + '',
+ location.href + '/%',
+ location.href + '/%a',
+ 'http://example.com',
+ 'http://[v8.:::]//url=%s',
+ 'https://test:test/',
+].forEach(url => {
+ test(() => {
+ assert_throws_dom('SYNTAX_ERR', () => { navigator.registerProtocolHandler('mailto', url, 'foo'); });
+ assert_throws_dom('SECURITY_ERR', () => { navigator.registerProtocolHandler('x', url, 'foo'); });
+ }, `registerProtocolHandler: Invalid URL "${url}" should throw (but after scheme)`);
+
+ test(() => {
+ assert_throws_dom('SYNTAX_ERR', () => { navigator.unregisterProtocolHandler('mailto', url); });
+ assert_throws_dom('SECURITY_ERR', () => { navigator.unregisterProtocolHandler('x', url, 'foo'); });
+ }, `unregisterProtocolHandler: Invalid URL "${url}" should throw (but after scheme)`);
+});
+
+[
+ 'http://example.com/%s',
+ 'https://example.com/%s',
+ 'http://foobar.example.com/%s',
+ 'mailto:%s@example.com',
+ 'mailto:%s',
+ `chrome://${location.host}/%s`,
+ `foo://${location.host}/%s`,
+ URL.createObjectURL(new Blob()) + "#%s",
+].forEach(url => {
+ const title = url.startsWith("blob:") ? "blob: URL" : url;
+ test(() => {
+ assert_throws_dom('SECURITY_ERR', () => { navigator.registerProtocolHandler('mailto', url, 'foo'); });
+ }, `registerProtocolHandler: Invalid URL "${title}" should throw SECURITY_ERR.`);
+
+ test(() => {
+ assert_throws_dom('SECURITY_ERR', () => { navigator.unregisterProtocolHandler('mailto', url); });
+ }, `unregisterProtocolHandler: Invalid URL "${title}" should throw SECURITY_ERR.`);
+});
+
+/* Protocol argument */
+
+/* Overriding any of the following protocols must never be allowed. That would
+ * break the browser. */
+[
+ 'about',
+ 'attachment',
+ 'blob',
+ 'chrome',
+ 'cid',
+ 'data',
+ 'file',
+ 'http',
+ 'https',
+ 'javascript',
+ 'livescript',
+ 'mid',
+ 'mocha',
+ 'moz-icon',
+ 'opera',
+ 'operamail',
+ 'res',
+ 'resource',
+ 'shttp',
+ 'tcl',
+ 'vbscript',
+ 'view-source',
+ 'ws',
+ 'wss',
+ 'wyciwyg',
+ /* other invalid schemes */
+ 'unrecognized',
+ 'mаilto', /* a cyrillic "а" */
+ 'mailto:',
+ 'mailto://',
+ 'mailto' + String.fromCharCode(0),
+ 'mailtoo' + String.fromCharCode(8),
+ 'mailto' + String.fromCharCode(10),
+ 'http://',
+ 'ssh:/',
+ 'magnet:+',
+ 'tel:sip',
+ 'foo',
+ 'fweb+oo',
+ /* web+ prefixed schemes must be followed by 1+ ascii alphas */
+ 'web+',
+ 'web+1',
+ 'web+namewithid123',
+ 'web+namewithtrailingspace ',
+ 'web+préfixewithaccent', // é is not ascii alpha
+ 'web+Kelvinsign', // ASCII-lower KELVIN SIGN is not k
+ 'web+latinsmallletterlongſ', // ASCII-lower LATIN SMALL LETTER LONG S is not s
+ 'web+dots.are.forbidden',
+ 'web+dashes-are-forbidden',
+ 'web+underscores_are_forbidden',
+ 'web+spaces are forbidden',
+ 'web+non*alpha*are*forbidden',
+ 'web+digits123areforbidden',
+].forEach(scheme => {
+ test(() => {
+ // https://test:test/ does not parse and does not contain %s, but the scheme check happens first
+ assert_throws_dom('SECURITY_ERR', () => { navigator.registerProtocolHandler(scheme, 'https://test:test/', 'foo'); });
+ }, 'registerProtocolHandler: Attempting to override the "' + scheme + '" protocol should throw SECURITY_ERR.');
+
+ test(() => {
+ assert_throws_dom('SECURITY_ERR', () => { navigator.unregisterProtocolHandler(scheme, 'https://test:test/'); });
+ }, 'unregisterProtocolHandler: Attempting to override the "' + scheme + '" protocol should throw SECURITY_ERR.');
+});
+
+/* The following protocols must be possible to override.
+ * We're just testing that the call goes through here. Whether or not they
+ * actually work as handlers is covered by the interactive tests. */
+
+[
+ /* safelisted schemes listed in
+ * https://html.spec.whatwg.org/multipage/system-state.html#safelisted-scheme */
+ 'bitcoin',
+ 'ftp',
+ 'ftps',
+ 'geo',
+ 'im',
+ 'irc',
+ 'ircs',
+ 'magnet',
+ 'mailto',
+ 'matrix',
+ 'mms',
+ 'news',
+ 'nntp',
+ 'openpgp4fpr',
+ 'sftp',
+ 'sip',
+ 'sms',
+ 'smsto',
+ 'ssh',
+ 'tel',
+ 'urn',
+ 'webcal',
+ 'wtai',
+ 'xmpp',
+ /* other valid schemes */
+ 'BitcoIn',
+ 'Irc',
+ 'MagneT',
+ 'Matrix',
+ 'SmsTo',
+ 'TEL',
+ 'teL',
+ 'WebCAL',
+ 'WTAI',
+ 'web+myprotocol',
+ 'web+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', // all alphas
+ 'web+UpperCasedIsLowercased',
+ 'WEB+seeabove',
+ 'WeB+SeEaBoVe'
+].forEach(scheme => {
+ test(() => {
+ navigator.registerProtocolHandler(scheme, location.href + '/%s', "foo");
+ }, 'registerProtocolHandler: overriding the "' + scheme + '" protocol should work');
+
+ test(() => {
+ navigator.unregisterProtocolHandler(scheme, location.href + '/%s');
+ }, 'unregisterProtocolHandler: overriding the "' + scheme + '" protocol should work');
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol.tentative.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol.tentative.https.html
new file mode 100644
index 0000000000..0120aaa12f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol.tentative.https.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset='utf-8'>
+<title>protocol handlers</title>
+
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+
+<script>
+// This should be merged into protocol.https.html when/if
+// https://github.com/whatwg/html/pull/5482 is approved.
+[
+ 'cabal',
+ 'dat',
+ 'did',
+ 'dweb',
+ 'ethereum',
+ 'hyper',
+ 'ipfs',
+ 'ipns',
+ 'ssb',
+].forEach(scheme => {
+ test(() => {
+ navigator.registerProtocolHandler(scheme, location.href + '/%s', "foo");
+ }, 'registerProtocolHandler: overriding the "' + scheme + '" protocol should work');
+
+ test(() => {
+ navigator.unregisterProtocolHandler(scheme, location.href + '/%s');
+ }, 'unregisterProtocolHandler: overriding the "' + scheme + '" protocol should work');
+});
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-sw.js b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-sw.js
new file mode 100644
index 0000000000..5fd915d17f
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-sw.js
@@ -0,0 +1,3 @@
+onfetch = e => {
+ e.respondWith(fetch("handler.html"));
+}
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-tools.js b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-tools.js
new file mode 100644
index 0000000000..88c62ec373
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler-tools.js
@@ -0,0 +1,53 @@
+// These can be used in an environment that has these global variables defined:
+// * type (one of "path", "query", or "fragment")
+// * noSW (a boolean)
+
+if (type === "path" && noSW) {
+ throw new Error("There is no support for a path handler without a service worker.");
+}
+
+const swString = noSW ? "" : "sw";
+const handler = {
+ "path": "PSS%sPSE/?QES\u2020QEE#FES\u2020FEE",
+ "query": "?QES\u2020QEEPSS%sPSE#FES\u2020FEE",
+ "fragment": "?QES\u2020QEE#FES\u2020FEEPSS%sPSE"
+}[type];
+const scheme = `web+wpt${type}${swString}`;
+
+function register() {
+ const handlerURL = noSW ? `resources/handler.html${handler}${type}` : `resources/handler/${type}/${handler}`;
+ navigator.registerProtocolHandler(scheme, handlerURL, `WPT ${type} handler${noSW ? ", without service worker" : ""}`);
+}
+
+function runTest({ includeNull = false } = {}) {
+ promise_test(async t => {
+ const bc = new BroadcastChannel(`protocol-handler-${type}${swString}`);
+ if (!noSW) {
+ const reg = await service_worker_unregister_and_register(t, "resources/handler-sw.js", "resources/handler/");
+ t.add_cleanup(async () => await reg.unregister());
+ await wait_for_state(t, reg.installing, 'activated');
+ }
+ const a = document.body.appendChild(document.createElement("a"));
+ const codePoints = [];
+ let i = includeNull ? 0 : 1;
+ for (; i < 0x82; i++) {
+ codePoints.push(String.fromCharCode(i));
+ }
+ a.href = `${scheme}:${codePoints.join("")}`;
+ a.target = "_blank";
+ a.click();
+ await new Promise(resolve => {
+ bc.onmessage = t.step_func(e => {
+ resultingURL = e.data;
+ assert_equals(stringBetweenMarkers(resultingURL, "QES", "QEE"), "%86", "query baseline");
+ assert_equals(stringBetweenMarkers(resultingURL, "FES", "FEE"), "%E2%80%A0", "fragment baseline");
+ assert_equals(stringBetweenMarkers(resultingURL, "PSS", "PSE"), `${encodeURIComponent(scheme)}%3A${includeNull ? "%2500" : ""}%2501%2502%2503%2504%2505%2506%2507%2508%250B%250C%250E%250F%2510%2511%2512%2513%2514%2515%2516%2517%2518%2519%251A%251B%251C%251D%251E%251F%20!%22%23%24%25%26${type === "query" ? "%27" : "'"}()*%2B%2C-.%2F0123456789%3A%3B%253C%3D%253E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%2560abcdefghijklmnopqrstuvwxyz%7B%7C%7D~%257F%25C2%2580%25C2%2581`, "actual test");
+ resolve();
+ });
+ });
+ });
+}
+
+function stringBetweenMarkers(string, start, end) {
+ return string.substring(string.indexOf(start) + start.length, string.indexOf(end));
+}
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler.html
new file mode 100644
index 0000000000..552e541784
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<p>This popup can be closed if it does not close itself.
+<p>
+<script>
+// This resource either gets navigated to through a service worker as a result of a URL that looks
+// like:
+// https://.../html/webappapis/system-state-and-capabilities/the-navigator-object/resources/handler/{type}/...
+// (the host is excluded to not upset the lint tool)
+// or it gets navigated to directly with the type appended to the end of the URL. In that case type
+// can only be fragment or query.
+
+let type = null;
+let swString = null;
+if (new URL(document.URL).pathname.endsWith("handler.html")) {
+ swString = "";
+ type = (document.URL.endsWith("fragment")) ? "fragment" : "query";
+} else {
+ type = document.URL.split("/")[9];
+ swString = "sw";
+}
+new BroadcastChannel(`protocol-handler-${type}${swString}`).postMessage(document.URL);
+window.close();
+</script>
diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/secure_context.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/secure_context.html
new file mode 100644
index 0000000000..685f5d19d7
--- /dev/null
+++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/secure_context.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ test(t => {
+ assert_false('registerProtocolHandler' in navigator);
+ assert_equals(navigator.registerProtocolHandler, undefined);
+ }, "navigator.registerProtocolHandler does not exist in non-secure contexts.");
+
+ test(t => {
+ assert_false('unregisterProtocolHandler' in navigator);
+ assert_equals(navigator.unregisterProtocolHandler, undefined);
+ }, "navigator.unregisterProtocolHandler does not exist in non-secure contexts.");
+ </script>
+</head>
+</html>