From c5d416ef96e8fe37bca6272046fa426a4c497a59 Mon Sep 17 00:00:00 2001 From: Andreas Pehrson Date: Thu, 5 Mar 2026 12:09:07 +0100 Subject: [PATCH 1/2] Download and install OpenH264 GMP plugin when installing Firefox This patch adds a step to download the public OpenH264 GMP plugin from the Cisco CDN during Firefox installation . The correct version is parsed from openh264.json, which is downloaded for the corresponding revision from the Firefox github. The plugin is extracted into the correct folder structure "gmp-gmpopenh264/{version}" inside "browsers/{channel}". In Firefox.setup_kwargs, the presence of this folder is detected and used to set the environment variable MOZ_GMP_PATH down the line. This makes OpenH264 available to WebRTC tests, allowing those reliant on H264 to pass. --- tools/wpt/browser.py | 93 +++++++++++++++++++++++++++++++++++++++++++- tools/wpt/run.py | 19 +++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/tools/wpt/browser.py b/tools/wpt/browser.py index 4265ad06f9b020..7fae46a5e8886f 100644 --- a/tools/wpt/browser.py +++ b/tools/wpt/browser.py @@ -452,6 +452,16 @@ class Firefox(Browser): "nightly": "Firefox Nightly.app" } + openh264_platform = { + ("linux", "x86_64"): "Linux_x86_64-gcc3", + ("linux", "x86"): "Linux_x86-gcc3", + ("linux", "aarch64"): "Linux_aarch64-gcc3", + ("win", "AMD64"): "WINNT_x86_64-msvc", + ("win", "x86"): "WINNT_x86-msvc", + ("macos", "x86_64"): "Darwin_x86_64-gcc3", + ("macos", "arm64"): "Darwin_aarch64-gcc3", + } + def platform_string_geckodriver(self): if self.platform is None: raise ValueError("Unable to construct a valid Geckodriver package name for current platform") @@ -529,7 +539,88 @@ def install(self, dest=None, channel="nightly"): raise os.remove(installer_path) - return self.find_binary_path(dest) + binary = self.find_binary_path(dest) + self.install_openh264(binary_dir=dest, binary=binary, channel=channel) + return binary + + def install_openh264(self, binary_dir, binary, channel="nightly"): + import hashlib + import io + + platform_key = self.openh264_platform.get((self.platform, uname.machine)) + if platform_key is None: + self.logger.warning( + "OpenH264: unsupported platform %s %s, skipping" + % (self.platform, uname.machine) + ) + return None + + prefs = FirefoxPrefs(self.logger) + version, channel_, rev = ( + prefs.get_version_and_channel(binary) if binary else (None, channel, None) + ) + ref = prefs.get_git_ref(version, channel_ if channel is None else channel, rev) + + self.logger.info("Downloading openh264.json from git ref %s" % ref) + try: + openh264_json = json.loads( + get_file_github( + "mozilla-firefox/firefox", + ref, + "toolkit/content/gmp-sources/openh264.json", + ) + ) + except Exception as e: + self.logger.warning("Failed to download openh264.json: %s" % e) + return None + + version = openh264_json["vendors"]["gmp-gmpopenh264"]["version"] + if version is None: + self.logger.warning("OpenH264: no entry for version in openh264.json") + return None + + openh264_dir = os.path.join(binary_dir, "gmp-gmpopenh264", version) + if os.path.isdir(openh264_dir): + self.logger.info("Using cached OpenH264 plugin from %s" % openh264_dir) + return openh264_dir + + platforms = openh264_json["vendors"]["gmp-gmpopenh264"]["platforms"] + platform_data = platforms.get(platform_key) + if platform_data is None: + self.logger.warning( + "OpenH264: no entry for platform %s in openh264.json" % platform_key + ) + return None + if "alias" in platform_data: + platform_data = platforms.get(platform_data["alias"]) + if platform_data is None: + self.logger.warning("OpenH264: alias target missing in openh264.json") + return None + + file_url = platform_data["fileUrl"] + expected_hash = platform_data["hashValue"] + hash_function = openh264_json.get("hashFunction", "sha512") + + self.logger.info("Downloading OpenH264 plugin from %s" % file_url) + try: + resp = get(file_url) + except Exception as e: + self.logger.warning("Failed to download OpenH264 plugin: %s" % e) + return None + + data = resp.content + actual_hash = getattr(hashlib, hash_function)(data).hexdigest() + if actual_hash != expected_hash: + self.logger.warning( + "OpenH264 hash mismatch: expected %s, got %s" + % (expected_hash, actual_hash) + ) + return None + + os.makedirs(openh264_dir, exist_ok=True) + unzip(io.BytesIO(data), dest=openh264_dir) + self.logger.info("OpenH264 plugin installed to %s" % openh264_dir) + return openh264_dir def install_prefs(self, binary, dest=None, channel=None): return FirefoxPrefs(self.logger).install_prefs(binary, dest, channel) diff --git a/tools/wpt/run.py b/tools/wpt/run.py index 7bc9572ad73c9f..0c45549cec7c89 100644 --- a/tools/wpt/run.py +++ b/tools/wpt/run.py @@ -335,6 +335,25 @@ def setup_kwargs(self, kwargs): # Allow WebRTC tests to call getUserMedia. kwargs["extra_prefs"].append("media.navigator.streams.fake=true") + if kwargs.get("gmp_path") is None and kwargs["browser_channel"] is not None: + openh264_dir = os.path.join( + self.browser._get_browser_binary_dir( + self.venv.path, kwargs["browser_channel"] + ), + "gmp-gmpopenh264", + ) + if os.path.isdir(openh264_dir): + dirs = os.listdir(openh264_dir) + openh264_dir = os.path.join(openh264_dir, dirs[0]) if dirs else None + if len(dirs) > 1: + logger.warning( + "More than one version of OpenH264 found. Using %s" % dirs[0] + ) + if openh264_dir and os.path.isdir(openh264_dir): + logger.info("Using OpenH264 plugin in %s" % openh264_dir) + kwargs["gmp_path"] = openh264_dir + else: + logger.warning("OpenH264 is not installed. Some tests may fail.") class FirefoxAndroid(BrowserSetup): name = "firefox_android" From ec5881b927d30f3ff104056136069aeb63e7c075 Mon Sep 17 00:00:00 2001 From: Andreas Pehrson Date: Thu, 5 Mar 2026 13:01:44 +0100 Subject: [PATCH 2/2] Allow installing OpenH264 plugin on the fly Like for webdriver this patch adds a path to prompt to allow downloading the OpenH264 plugin for Firefox if not already installed. --- tools/wpt/run.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tools/wpt/run.py b/tools/wpt/run.py index 0c45549cec7c89..09620c52cb3b83 100644 --- a/tools/wpt/run.py +++ b/tools/wpt/run.py @@ -336,12 +336,20 @@ def setup_kwargs(self, kwargs): kwargs["extra_prefs"].append("media.navigator.streams.fake=true") if kwargs.get("gmp_path") is None and kwargs["browser_channel"] is not None: - openh264_dir = os.path.join( - self.browser._get_browser_binary_dir( - self.venv.path, kwargs["browser_channel"] - ), - "gmp-gmpopenh264", + binary_dir = self.browser._get_browser_binary_dir( + self.venv.path, kwargs["browser_channel"] ) + openh264_dir = os.path.join(binary_dir, "gmp-gmpopenh264") + + if not os.path.isdir(openh264_dir): + if self.prompt_install("OpenH264 GMP plugin"): + logger.info("Downloading OpenH264 plugin") + self.browser.install_openh264( + binary_dir=binary_dir, + binary=kwargs["binary"], + channel=kwargs["browser_channel"], + ) + if os.path.isdir(openh264_dir): dirs = os.listdir(openh264_dir) openh264_dir = os.path.join(openh264_dir, dirs[0]) if dirs else None