diff --git a/docs/writing-tests/testdriver.md b/docs/writing-tests/testdriver.md index ce5aa6af545448..e3f0ba3ea4058b 100644 --- a/docs/writing-tests/testdriver.md +++ b/docs/writing-tests/testdriver.md @@ -192,6 +192,14 @@ the global scope. .. js:autofunction:: test_driver.clear_display_features ``` +### Global Privacy Control ### + +```eval_rst +.. js:autofunction:: test_driver.set_global_privacy_control +.. js:autofunction:: test_driver.set_global_privacy_control +``` + + ### Using test_driver in other browsing contexts ### Testdriver can be used in browsing contexts (i.e. windows or frames) diff --git a/resources/testdriver.js b/resources/testdriver.js index 5b390dedeb72bb..0366e476c1a6b7 100644 --- a/resources/testdriver.js +++ b/resources/testdriver.js @@ -2169,6 +2169,32 @@ */ clear_display_features: function(context=null) { return window.test_driver_internal.clear_display_features(context); + }, + + /** + * Gets the current globally-applied privacy control status + * + * @returns {Promise} Fulfils with an object with boolean property `gpc` + * that encodes the current "do not sell or share" + * signal the browser is configured to convey. + */ + get_global_privacy_control: function() { + return window.test_driver_internal.get_global_privacy_control(); + }, + + /** + * Gets the current globally-applied privacy control status + * + * @param {bool} newValue - The a boolean that is true if the browers + * should convey a "do not sell or share" signal + * and false otherwise + * + * @returns {Promise} Fulfils with an object with boolean property `gpc` + * that encodes the new "do not sell or share" + * after applying the new value. + */ + set_global_privacy_control: function(newValue) { + return window.test_driver_internal.set_global_privacy_control(newValue); } }; @@ -2486,6 +2512,14 @@ async clear_display_features(context=null) { throw new Error("clear_display_features() is not implemented by testdriver-vendor.js"); + }, + + async set_global_privacy_control(newValue) { + throw new Error("set_global_privacy_control() is not implemented by testdriver-vendor.js"); + }, + + async get_global_privacy_control() { + throw new Error("get_global_privacy_control() is not implemented by testdriver-vendor.js"); } }; })(); diff --git a/tools/webdriver/webdriver/client.py b/tools/webdriver/webdriver/client.py index f4e6259d547a66..24803b1917db7d 100644 --- a/tools/webdriver/webdriver/client.py +++ b/tools/webdriver/webdriver/client.py @@ -654,6 +654,20 @@ def delete_cookie(self, name=None): #[...] + + def set_global_privacy_control(self, gpc): + body = { + "gpc": gpc, + } + return self.send_session_command("POST", "privacy", body) + + + def get_global_privacy_control(self): + return self.send_session_command("GET", "privacy") + + #[...] + + def execute_script(self, script, args=None): if args is None: args = [] diff --git a/tools/wptrunner/wptrunner/executors/actions.py b/tools/wptrunner/wptrunner/executors/actions.py index b093b0355d357f..086923e4f6c44a 100644 --- a/tools/wptrunner/wptrunner/executors/actions.py +++ b/tools/wptrunner/wptrunner/executors/actions.py @@ -183,6 +183,30 @@ def __call__(self, payload): self.logger.debug("Setting permission %s to %s" % (name, state)) self.protocol.set_permission.set_permission(descriptor, state) + +class SetGlobalPrivacyControlAction: + name = "set_global_privacy_control" + + def __init__(self, logger, protocol): + self.logger = logger + self.protocol = protocol + + def __call__(self, payload): + gpc = payload["gpc"] + return self.protocol.global_privacy_control.set_global_privacy_control(gpc) + +class GetGlobalPrivacyControlAction: + name = "get_global_privacy_control" + + def __init__(self, logger, protocol): + self.logger = logger + self.protocol = protocol + + def __call__(self, payload): + return self.protocol.global_privacy_control.get_global_privacy_control() + + + class AddVirtualAuthenticatorAction: name = "add_virtual_authenticator" @@ -586,4 +610,6 @@ def __call__(self, payload): RemoveVirtualPressureSourceAction, SetProtectedAudienceKAnonymityAction, SetDisplayFeaturesAction, - ClearDisplayFeaturesAction] + ClearDisplayFeaturesAction, + GetGlobalPrivacyControlAction, + SetGlobalPrivacyControlAction] diff --git a/tools/wptrunner/wptrunner/executors/executormarionette.py b/tools/wptrunner/wptrunner/executors/executormarionette.py index 37c64e1745b3de..efc5aaabf53a34 100644 --- a/tools/wptrunner/wptrunner/executors/executormarionette.py +++ b/tools/wptrunner/wptrunner/executors/executormarionette.py @@ -34,6 +34,7 @@ DevicePostureProtocolPart, DisplayFeaturesProtocolPart, GenerateTestReportProtocolPart, + GlobalPrivacyControlProtocolPart, PrefsProtocolPart, PrintProtocolPart, Protocol, @@ -620,6 +621,29 @@ def set_permission(self, descriptor, state): raise NotImplementedError("set_permission not yet implemented") from e +class MarionetteGlobalPrivacyControlProtocolPart(GlobalPrivacyControlProtocolPart): + def setup(self): + self.marionette = self.parent.marionette + + def set_global_privacy_control(self, gpc): + body = { + "gpc": gpc, + } + try: + return self.marionette._send_message("WebDriver:SetGlobalPrivacyControl", body)["value"] + except errors.UnsupportedOperationException as e: + raise NotImplementedError("set_global_privacy_control not yet implemented") from e + + def get_global_privacy_control(self): + try: + return self.marionette._send_message("WebDriver:GetGlobalPrivacyControl")["value"] + except errors.UnsupportedOperationException as e: + raise NotImplementedError("get_global_privacy_control not yet implemented") from e + + + + + class MarionettePrintProtocolPart(PrintProtocolPart): def setup(self): self.marionette = self.parent.marionette @@ -795,6 +819,7 @@ class MarionetteProtocol(Protocol): MarionetteGenerateTestReportProtocolPart, MarionetteVirtualAuthenticatorProtocolPart, MarionetteSetPermissionProtocolPart, + MarionetteGlobalPrivacyControlProtocolPart, MarionettePrintProtocolPart, MarionetteDebugProtocolPart, MarionetteAccessibilityProtocolPart, diff --git a/tools/wptrunner/wptrunner/executors/executorwebdriver.py b/tools/wptrunner/wptrunner/executors/executorwebdriver.py index e4127f130ba32d..9cba20f2dfa885 100644 --- a/tools/wptrunner/wptrunner/executors/executorwebdriver.py +++ b/tools/wptrunner/wptrunner/executors/executorwebdriver.py @@ -48,6 +48,7 @@ VirtualPressureSourceProtocolPart, ProtectedAudienceProtocolPart, DisplayFeaturesProtocolPart, + GlobalPrivacyControlProtocolPart, merge_dicts) from typing import Any, List, Dict, Optional, Tuple @@ -943,6 +944,17 @@ def set_display_features(self, features): def clear_display_features(self): return self.webdriver.send_session_command("DELETE", "displayfeatures") +class WebDriverGlobalPrivacyControlProtocolPart(GlobalPrivacyControlProtocolPart): + def setup(self): + self.webdriver = self.parent.webdriver + + def set_global_privacy_control(self, gpc): + return self.webdriver.set_global_privacy_control(gpc) + + def get_global_privacy_control(self): + return self.webdriver.get_global_privacy_control() + + class WebDriverProtocol(Protocol): enable_bidi = False implements = [WebDriverBaseProtocolPart, @@ -968,7 +980,8 @@ class WebDriverProtocol(Protocol): WebDriverStorageProtocolPart, WebDriverVirtualPressureSourceProtocolPart, WebDriverProtectedAudienceProtocolPart, - WebDriverDisplayFeaturesProtocolPart] + WebDriverDisplayFeaturesProtocolPart, + WebDriverGlobalPrivacyControlProtocolPart] def __init__(self, executor, browser, capabilities, **kwargs): super().__init__(executor, browser) diff --git a/tools/wptrunner/wptrunner/executors/protocol.py b/tools/wptrunner/wptrunner/executors/protocol.py index 8af313d27d6d21..afb553053cd735 100644 --- a/tools/wptrunner/wptrunner/executors/protocol.py +++ b/tools/wptrunner/wptrunner/executors/protocol.py @@ -736,6 +736,20 @@ def set_permission(self, descriptor, state): pass +class GlobalPrivacyControlProtocolPart(ProtocolPart): + """Protocol part for reading and writing the GPC signal""" + __metaclass__ = ABCMeta + + name = "global_privacy_control" + + @abstractmethod + def set_global_privacy_control(self, value): + pass + + @abstractmethod + def get_global_privacy_control(self): + pass + class ActionSequenceProtocolPart(ProtocolPart): """Protocol part for performing trusted clicks""" __metaclass__ = ABCMeta diff --git a/tools/wptrunner/wptrunner/testdriver-extra.js b/tools/wptrunner/wptrunner/testdriver-extra.js index 4d26a14097f530..15022ae5c0c618 100644 --- a/tools/wptrunner/wptrunner/testdriver-extra.js +++ b/tools/wptrunner/wptrunner/testdriver-extra.js @@ -657,4 +657,12 @@ window.test_driver_internal.clear_display_features = function(context=null) { return create_context_action("clear_display_features", context, {}); } + + window.test_driver_internal.get_global_privacy_control = function(context=null) { + return create_action("get_global_privacy_control", {}); + }; + + window.test_driver_internal.set_global_privacy_control = function(gpc, context=null) { + return create_action("set_global_privacy_control", {gpc}); + }; })();