diff --git a/infrastructure/metadata/infrastructure/webdriver/bidi/subscription.html.ini b/infrastructure/metadata/infrastructure/webdriver/bidi/subscription.html.ini
index 7c3127d167a740c..1a5ac47c67d942a 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 000000000000000..1a5ac47c67d942a
--- /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/webdriver/bidi/subscription.html b/infrastructure/webdriver/bidi/subscription.html
index 056c2e5f77fb959..f1144b72fd56352 100644
--- a/infrastructure/webdriver/bidi/subscription.html
+++ b/infrastructure/webdriver/bidi/subscription.html
@@ -3,7 +3,7 @@
Test console log are present
-
+
+
+ """.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 b39b8deb76c958e..d9d3ed166e10c5c 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 fa593ce40231069..c46b8e978c4d269 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 2e3fd974d4d43ee..2646fc4449868ab 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,