diff --git a/docs/writing-tests/testdriver-extension-tutorial.md b/docs/writing-tests/testdriver-extension-tutorial.md
index ca867c392da70e..6a1da4e0077de1 100644
--- a/docs/writing-tests/testdriver-extension-tutorial.md
+++ b/docs/writing-tests/testdriver-extension-tutorial.md
@@ -90,8 +90,16 @@ We will leave this unimplemented and override it in another file. Lets do that n
#### WebDriver BiDi
-For commands using WebDriver BiDi, add the methods to `window.test_driver.bidi`. Parameters are passed as a single object `params`.
-
+For commands using WebDriver BiDi, add the methods to `test_driver.bidi`. Parameters are passed as a single object `params`. For example [`test_driver.bidi.permissions.set_permission`](https://github.com/web-platform-tests/wpt/blob/5ec8ba6d68f27d49a056cbf940e3bc9a8324c538/resources/testdriver.js#L183).
+
+Before calling `test_driver_internal` method, assert the `bidi` testdriver feature is enabled.
+```javascript
+set_permission: function (params) {
+ assertBidiIsEnabled();
+ return window.test_driver_internal.bidi.permissions.set_permission(
+ params);
+}
+```
### [tools/wptrunner/wptrunner/testdriver-extra.js](https://github.com/web-platform-tests/wpt/blob/master/tools/wptrunner/wptrunner/testdriver-extra.js)
diff --git a/docs/writing-tests/testdriver.md b/docs/writing-tests/testdriver.md
index 462c7b85503cb9..42a862fbe1e139 100644
--- a/docs/writing-tests/testdriver.md
+++ b/docs/writing-tests/testdriver.md
@@ -32,6 +32,20 @@ document when using testdriver from a different context):
The api in `test_driver.bidi` provides access to the
[WebDriver BiDi](https://w3c.github.io/webdriver-bidi) protocol.
+### Markup ###
+
+To use WebDriver BiDi, enable the `bidi` feature in `testdriver.js` by adding
+`feature=bidi` query string parameter. Details are in [RFC 214: Add testdriver features](https://github.com/web-platform-tests/rfcs/blob/master/rfcs/testdriver-features.md).
+```html
+
+```
+
+```javascript
+// META: script=/resources/testdriver.js?feature=bidi
+```
+
+[Example](https://github.com/web-platform-tests/wpt/blob/aae46926b1fdccd460e1c6eaaf01ca20b941fbce/infrastructure/webdriver/bidi/subscription.html#L6).
+
### Context ###
A WebDriver BiDi "browsing context" is equivalent to an
diff --git a/infrastructure/metadata/infrastructure/testdriver/bidi/bluetooth/simulate_adapter.https.html.ini b/infrastructure/metadata/infrastructure/testdriver/bidi/bluetooth/simulate_adapter.https.html.ini
index 7c3127d167a740..a40a56bcdb9bd8 100644
--- a/infrastructure/metadata/infrastructure/testdriver/bidi/bluetooth/simulate_adapter.https.html.ini
+++ b/infrastructure/metadata/infrastructure/testdriver/bidi/bluetooth/simulate_adapter.https.html.ini
@@ -1 +1,2 @@
-disabled: https://github.com/web-platform-tests/wpt/issues/47544
+disabled :
+ if product != "chrome": @True
diff --git a/infrastructure/metadata/infrastructure/testdriver/bidi/permissions/set_permission.https.html.ini b/infrastructure/metadata/infrastructure/testdriver/bidi/permissions/set_permission.https.html.ini
index 7c3127d167a740..1a5ac47c67d942 100644
--- a/infrastructure/metadata/infrastructure/testdriver/bidi/permissions/set_permission.https.html.ini
+++ b/infrastructure/metadata/infrastructure/testdriver/bidi/permissions/set_permission.https.html.ini
@@ -1 +1,2 @@
-disabled: https://github.com/web-platform-tests/wpt/issues/47544
+disabled:
+ if product != "chrome": @True
diff --git a/infrastructure/metadata/infrastructure/webdriver/bidi/subscription.html.ini b/infrastructure/metadata/infrastructure/webdriver/bidi/subscription.html.ini
index 7c3127d167a740..1a5ac47c67d942 100644
--- a/infrastructure/metadata/infrastructure/webdriver/bidi/subscription.html.ini
+++ b/infrastructure/metadata/infrastructure/webdriver/bidi/subscription.html.ini
@@ -1 +1,2 @@
-disabled: https://github.com/web-platform-tests/wpt/issues/47544
+disabled:
+ if product != "chrome": @True
diff --git a/infrastructure/metadata/infrastructure/webdriver/bidi/subscription.window.js.ini b/infrastructure/metadata/infrastructure/webdriver/bidi/subscription.window.js.ini
new file mode 100644
index 00000000000000..1a5ac47c67d942
--- /dev/null
+++ b/infrastructure/metadata/infrastructure/webdriver/bidi/subscription.window.js.ini
@@ -0,0 +1,2 @@
+disabled:
+ if product != "chrome": @True
diff --git a/infrastructure/testdriver/bidi/bluetooth/simulate_adapter.https.html b/infrastructure/testdriver/bidi/bluetooth/simulate_adapter.https.html
index 3850fa26974a32..ed60285c41fabe 100644
--- a/infrastructure/testdriver/bidi/bluetooth/simulate_adapter.https.html
+++ b/infrastructure/testdriver/bidi/bluetooth/simulate_adapter.https.html
@@ -3,7 +3,7 @@
TestDriver bidi.bluetooth.simulate_adapter method
-
+
-
+
-
+
+
+ """.encode("utf-8")
+
+ s = create("html/test.html", contents=contents)
+ s.testdriver_features == None
+
+
+@pytest.mark.parametrize("features",
+ [[], ['feature_1'], ['feature_1', 'feature_2']])
+def test_html_testdriver_features(features):
+ contents = f"""
+
+
+
+ """.encode("utf-8")
+
+ s = create("html/test.html", contents=contents)
+ s.testdriver_features == ['bidi']
diff --git a/tools/wptrunner/wptrunner/browsers/chrome.py b/tools/wptrunner/wptrunner/browsers/chrome.py
index b39b8deb76c958..d9d3ed166e10c5 100644
--- a/tools/wptrunner/wptrunner/browsers/chrome.py
+++ b/tools/wptrunner/wptrunner/browsers/chrome.py
@@ -6,7 +6,7 @@
from mozlog.structuredlog import StructuredLogger
from . import chrome_spki_certs
-from .base import BrowserError
+from .base import BrowserError, BrowserSettings
from .base import WebDriverBrowser, require_arg
from .base import NullBrowser # noqa: F401
from .base import OutputHandler
@@ -41,6 +41,8 @@
"update_properties": "update_properties",
"timeout_multiplier": "get_timeout_multiplier",}
+from ..wpttest import Test
+
def debug_args(debug_info):
if debug_info.interactive:
@@ -214,6 +216,7 @@ def __init__(self,
super().__init__(logger, **kwargs)
self._leak_check = leak_check
self._actual_port = None
+ self._require_webdriver_bidi = None
def restart_on_test_type_change(self, new_test_type: str, old_test_type: str) -> bool:
# Restart the test runner when switch from/to wdspec tests. Wdspec test
@@ -262,6 +265,20 @@ def executor_browser(self):
browser_cls, browser_kwargs = super().executor_browser()
return browser_cls, {**browser_kwargs, "leak_check": self._leak_check}
+ @property
+ def require_webdriver_bidi(self) -> Optional[bool]:
+ return self._require_webdriver_bidi
+
+ def settings(self, test: Test) -> BrowserSettings:
+ """ Required to store `require_webdriver_bidi` in browser settings."""
+ settings = super().settings(test)
+ self._require_webdriver_bidi = test.testdriver_features is not None and 'bidi' in test.testdriver_features
+
+ return {
+ **settings,
+ "require_webdriver_bidi": self._require_webdriver_bidi
+ }
+
class ChromeDriverOutputHandler(OutputHandler):
PORT_RE = re.compile(rb'.*was started successfully on port (\d+)\.')
diff --git a/tools/wptrunner/wptrunner/executors/executorchrome.py b/tools/wptrunner/wptrunner/executors/executorchrome.py
index fa593ce4023106..c46b8e978c4d26 100644
--- a/tools/wptrunner/wptrunner/executors/executorchrome.py
+++ b/tools/wptrunner/wptrunner/executors/executorchrome.py
@@ -20,6 +20,7 @@
WebDriverFedCMProtocolPart,
WebDriverPrintRefTestExecutor,
WebDriverProtocol,
+ WebDriverBidiProtocol,
WebDriverRefTestExecutor,
WebDriverTestharnessExecutor,
WebDriverTestharnessProtocolPart,
@@ -211,6 +212,27 @@ def __init__(self, executor, browser, capabilities, **kwargs):
super().__init__(executor, browser, capabilities, **kwargs)
+class ChromeDriverBidiProtocol(WebDriverBidiProtocol):
+ implements = [
+ ChromeDriverBaseProtocolPart,
+ ChromeDriverDevToolsProtocolPart,
+ ChromeDriverFedCMProtocolPart,
+ ChromeDriverTestharnessProtocolPart,
+ ]
+ for base_part in WebDriverBidiProtocol.implements:
+ if base_part.name not in {part.name for part in implements}:
+ implements.append(base_part)
+
+ # Prefix to apply to vendor-specific WebDriver extension commands.
+ vendor_prefix = "goog"
+
+ def __init__(self, executor, browser, capabilities, **kwargs):
+ self.implements = list(ChromeDriverBidiProtocol.implements)
+ if getattr(browser, "leak_check", False):
+ self.implements.append(ChromeDriverLeakProtocolPart)
+ super().__init__(executor, browser, capabilities, **kwargs)
+
+
def _evaluate_leaks(executor_cls):
if hasattr(executor_cls, "base_convert_result"):
# Don't wrap more than once, which can cause unbounded recursion.
@@ -244,9 +266,15 @@ class ChromeDriverRefTestExecutor(WebDriverRefTestExecutor, _SanitizerMixin): #
@_evaluate_leaks
class ChromeDriverTestharnessExecutor(WebDriverTestharnessExecutor, _SanitizerMixin): # type: ignore
- protocol_cls = ChromeDriverProtocol
-
def __init__(self, *args, reuse_window=False, **kwargs):
+ require_webdriver_bidi = kwargs.get("browser_settings", {}).get(
+ "require_webdriver_bidi", None)
+
+ if require_webdriver_bidi:
+ self.protocol_cls = ChromeDriverBidiProtocol
+ else:
+ self.protocol_cls = ChromeDriverProtocol
+
super().__init__(*args, **kwargs)
self.reuse_window = reuse_window
diff --git a/tools/wptrunner/wptrunner/wpttest.py b/tools/wptrunner/wptrunner/wpttest.py
index 2e3fd974d4d43e..2646fc4449868a 100644
--- a/tools/wptrunner/wptrunner/wpttest.py
+++ b/tools/wptrunner/wptrunner/wpttest.py
@@ -216,7 +216,8 @@ class Test(ABC):
long_timeout = 60 # seconds
def __init__(self, url_base, tests_root, url, inherit_metadata, test_metadata,
- timeout=None, path=None, protocol="http", subdomain=False, pac=None):
+ timeout=None, path=None, protocol="http", subdomain=False, pac=None,
+ testdriver_features=None):
self.url_base = url_base
self.tests_root = tests_root
self.url = url
@@ -224,6 +225,7 @@ def __init__(self, url_base, tests_root, url, inherit_metadata, test_metadata,
self._test_metadata = test_metadata
self.timeout = timeout if timeout is not None else self.default_timeout
self.path = path
+ self.testdriver_features = testdriver_features
self.subdomain = subdomain
self.environment = {"url_base": url_base,
"protocol": protocol,
@@ -482,9 +484,10 @@ class TestharnessTest(Test):
def __init__(self, url_base, tests_root, url, inherit_metadata, test_metadata,
timeout=None, path=None, protocol="http", testdriver=False,
- jsshell=False, scripts=None, subdomain=False, pac=None):
+ jsshell=False, scripts=None, subdomain=False, pac=None,
+ testdriver_features=None):
Test.__init__(self, url_base, tests_root, url, inherit_metadata, test_metadata, timeout,
- path, protocol, subdomain, pac)
+ path, protocol, subdomain, pac, testdriver_features)
self.testdriver = testdriver
self.jsshell = jsshell
@@ -494,6 +497,7 @@ def __init__(self, url_base, tests_root, url, inherit_metadata, test_metadata,
def from_manifest(cls, manifest_file, manifest_item, inherit_metadata, test_metadata):
timeout = cls.long_timeout if manifest_item.timeout == "long" else cls.default_timeout
pac = manifest_item.pac
+ testdriver_features = manifest_item.testdriver_features
testdriver = manifest_item.testdriver if hasattr(manifest_item, "testdriver") else False
jsshell = manifest_item.jsshell if hasattr(manifest_item, "jsshell") else False
script_metadata = manifest_item.script_metadata or []
@@ -506,6 +510,7 @@ def from_manifest(cls, manifest_file, manifest_item, inherit_metadata, test_meta
test_metadata,
timeout=timeout,
pac=pac,
+ testdriver_features=testdriver_features,
path=os.path.join(manifest_file.tests_root, manifest_item.path),
protocol=server_protocol(manifest_item),
testdriver=testdriver,