Bug 1950636 - Implement MarionetteWebExtensionsProtocolPart r=whimboo,jgraham,webdriver-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D244400
This commit is contained in:
Tomislav Jovanovic
2025-05-20 14:37:33 +00:00
committed by tjovanovic@mozilla.com
parent b6efacd0f5
commit ac257fd33f
3 changed files with 57 additions and 12 deletions

View File

@@ -31,28 +31,34 @@ class Addons:
def __init__(self, marionette): def __init__(self, marionette):
self._mn = marionette self._mn = marionette
def install(self, path, temp=False): def install(self, path=None, temp=False, data=None):
"""Install a Firefox addon. """Install a Firefox addon, which can be used right away.
If the addon is restartless, it can be used right away. Otherwise
a restart using :func:`~marionette_driver.marionette.Marionette.restart`
will be needed.
:param path: A file path to the extension to be installed. :param path: A file path to the extension to be installed.
:param temp: Install a temporary addon. Temporary addons will :param temp: Install a temporary addon. Temporary addons will
automatically be uninstalled on shutdown and do not need automatically be uninstalled on shutdown and do not need
to be signed, though they must be restartless. to be signed.
:param data: A base64-encoded string of a zip-packed addon.
:returns: The addon ID string of the newly installed addon. :returns: The addon ID string of the newly installed addon.
:raises: :exc:`AddonInstallException` :raises: :exc:`AddonInstallException`
""" """
# On windows we can end up with a path with mixed \ and /
# which Firefox doesn't like
path = path.replace("/", os.path.sep)
body = {"path": path, "temporary": temp} if (path and data) or (not path and not data):
raise AddonInstallException("Must use either path or data argument.")
body = {"temporary": temp}
if path:
# On windows we can end up with a path with mixed \ and /
# which Firefox doesn't like
path = path.replace("/", os.path.sep)
body.update({"path": path})
if data:
body.update({"addon": data})
try: try:
return self._mn._send_message("Addon:Install", body, key="value") return self._mn._send_message("Addon:Install", body, key="value")
except errors.UnknownException as e: except errors.UnknownException as e:

View File

@@ -47,6 +47,7 @@ from .protocol import (AccessibilityProtocolPart,
DevicePostureProtocolPart, DevicePostureProtocolPart,
VirtualPressureSourceProtocolPart, VirtualPressureSourceProtocolPart,
DisplayFeaturesProtocolPart, DisplayFeaturesProtocolPart,
WebExtensionsProtocolPart,
merge_dicts) merge_dicts)
@@ -738,6 +739,24 @@ class MarionetteDisplayFeaturesProtocolPart(DisplayFeaturesProtocolPart):
def clear_display_features(self): def clear_display_features(self):
raise NotImplementedError("clear_display_features not yet implemented") raise NotImplementedError("clear_display_features not yet implemented")
class MarionetteWebExtensionsProtocolPart(WebExtensionsProtocolPart):
def setup(self):
self.addons = Addons(self.parent.marionette)
def install_web_extension(self, extension):
if extension["type"] == "base64":
extension_id = self.addons.install(data=extension["value"], temp=True)
else:
path = self.parent.test_dir + extension["path"]
extension_id = self.addons.install(path, temp=True)
return {'extension': extension_id}
def uninstall_web_extension(self, extension_id):
return self.addons.uninstall(extension_id)
class MarionetteProtocol(Protocol): class MarionetteProtocol(Protocol):
implements = [MarionetteBaseProtocolPart, implements = [MarionetteBaseProtocolPart,
MarionetteTestharnessProtocolPart, MarionetteTestharnessProtocolPart,
@@ -761,7 +780,8 @@ class MarionetteProtocol(Protocol):
MarionetteVirtualSensorProtocolPart, MarionetteVirtualSensorProtocolPart,
MarionetteDevicePostureProtocolPart, MarionetteDevicePostureProtocolPart,
MarionetteVirtualPressureSourceProtocolPart, MarionetteVirtualPressureSourceProtocolPart,
MarionetteDisplayFeaturesProtocolPart] MarionetteDisplayFeaturesProtocolPart,
MarionetteWebExtensionsProtocolPart]
def __init__(self, executor, browser, capabilities=None, timeout_multiplier=1, e10s=True, ccov=False): def __init__(self, executor, browser, capabilities=None, timeout_multiplier=1, e10s=True, ccov=False):
do_delayed_imports() do_delayed_imports()
@@ -955,6 +975,10 @@ class MarionetteTestharnessExecutor(TestharnessExecutor):
self.protocol.testharness.load_runner(new_environment["protocol"]) self.protocol.testharness.load_runner(new_environment["protocol"])
def do_test(self, test): def do_test(self, test):
# TODO: followup to do this properly, pass test as state on the CallbackHandler:
# https://phabricator.services.mozilla.com/D244400?id=1030480#inline-1369921
self.protocol.test_dir = os.path.dirname(test.path)
timeout = (test.timeout * self.timeout_multiplier if self.debug_info is None timeout = (test.timeout * self.timeout_multiplier if self.debug_info is None
else None) else None)

View File

@@ -335,6 +335,21 @@ class AccessibilityProtocolPart(ProtocolPart):
pass pass
class WebExtensionsProtocolPart(ProtocolPart):
"""Protocol part for managing WebExtensions"""
__metaclass__ = ABCMeta
name = "web_extensions"
@abstractmethod
def install_web_extension(self, extension):
pass
@abstractmethod
def uninstall_web_extension(self, extension_id):
pass
class BidiBluetoothProtocolPart(ProtocolPart): class BidiBluetoothProtocolPart(ProtocolPart):
"""Protocol part for managing BiDi events""" """Protocol part for managing BiDi events"""
__metaclass__ = ABCMeta __metaclass__ = ABCMeta