diff --git a/remote/marionette/driver.sys.mjs b/remote/marionette/driver.sys.mjs index 2ffe17e84279..95b1352ab351 100644 --- a/remote/marionette/driver.sys.mjs +++ b/remote/marionette/driver.sys.mjs @@ -3357,22 +3357,16 @@ GeckoDriver.prototype.setPermission = async function (cmd) { const { descriptor, state, oneRealm = false } = cmd.parameters; const browsingContext = lazy.assert.open(this.getBrowsingContext()); - // XXX: We currently depend on camera/microphone tests throwing UnsupportedOperationError, - // the fix is ongoing in bug 1609427. - if (["camera", "microphone"].includes(descriptor.name)) { - throw new lazy.error.UnsupportedOperationError( - "setPermission: camera and microphone permissions are currently unsupported" - ); - } + lazy.permissions.validatePermission(descriptor.name); - // XXX: Allowing this permission causes timing related Android crash, see also bug 1878741 + // Bug 1878741: Allowing this permission causes timing related Android crash. if (descriptor.name === "notifications") { if (Services.prefs.getBoolPref("notification.prompt.testing", false)) { // Okay, do nothing. The notifications module will work without permission. return; } throw new lazy.error.UnsupportedOperationError( - "setPermission: expected notification.prompt.testing to be set" + `Setting "descriptor.name" "notifications" expected "notification.prompt.testing" preference to be set` ); } @@ -3389,7 +3383,26 @@ GeckoDriver.prototype.setPermission = async function (cmd) { lazy.assert.boolean(oneRealm); - lazy.permissions.set(params.type, params.state, oneRealm, browsingContext); + if (!lazy.MarionettePrefs.setPermissionEnabled) { + throw new lazy.error.UnsupportedOperationError( + "'Set Permission' is not available" + ); + } + + let origin = browsingContext.currentURI.prePath; + + // storage-access is a special case. + if (descriptor.name === "storage-access") { + origin = browsingContext.top.currentURI.prePath; + + params = { + type: lazy.permissions.getStorageAccessPermissionsType( + browsingContext.currentWindowGlobal.documentURI + ), + }; + } + + lazy.permissions.set(params, state, origin); }; /** diff --git a/remote/shared/Permissions.sys.mjs b/remote/shared/Permissions.sys.mjs index 5238bf834700..50996bf7019a 100644 --- a/remote/shared/Permissions.sys.mjs +++ b/remote/shared/Permissions.sys.mjs @@ -6,71 +6,48 @@ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs", - MarionettePrefs: "chrome://remote/content/marionette/prefs.sys.mjs", }); /** @namespace */ export const permissions = {}; -function mapToInternalPermissionParameters(browsingContext, permissionType) { - const currentURI = browsingContext.currentWindowGlobal.documentURI; - - // storage-access is quite special... - if (permissionType === "storage-access") { - const thirdPartyPrincipalSite = Services.eTLD.getSite(currentURI); - - const topLevelURI = browsingContext.top.currentWindowGlobal.documentURI; - const topLevelPrincipal = - Services.scriptSecurityManager.createContentPrincipal(topLevelURI, {}); - - return { - name: "3rdPartyFrameStorage^" + thirdPartyPrincipalSite, - principal: topLevelPrincipal, - }; - } - - const currentPrincipal = - Services.scriptSecurityManager.createContentPrincipal(currentURI, {}); - - return { - name: permissionType, - principal: currentPrincipal, - }; -} +/** + * Get a permission type for the "storage-access" permission. + * + * @param {nsIURI} uri + * The URI to use for building the permission type. + * + * @returns {string} permissionType + * The permission type for the "storage-access" permission. + */ +permissions.getStorageAccessPermissionsType = function (uri) { + const thirdPartyPrincipalSite = Services.eTLD.getSite(uri); + return "3rdPartyFrameStorage^" + thirdPartyPrincipalSite; +}; /** - * Set a permission's state. - * Note: Currently just a shim to support testdriver's set_permission. + * Set a permission given a permission descriptor, a permission state, + * an origin. * - * @param {object} permissionType - * The Gecko internal permission type + * @param {PermissionDescriptor} descriptor + * The descriptor of the permission which will be updated. * @param {string} state * State of the permission. It can be `granted`, `denied` or `prompt`. - * @param {boolean} oneRealm - * Currently ignored - * @param {browsingContext=} browsingContext - * Current browsing context object + * @param {string} origin + * The origin which is used as a target for permission update. + * * @throws {UnsupportedOperationError} - * If `marionette.setpermission.enabled` is not set or - * an unsupported permission is used. + * If state has unsupported value. */ -permissions.set = function (permissionType, state, oneRealm, browsingContext) { - if (!lazy.MarionettePrefs.setPermissionEnabled) { - throw new lazy.error.UnsupportedOperationError( - "'Set Permission' is not available" - ); - } - - const { name, principal } = mapToInternalPermissionParameters( - browsingContext, - permissionType - ); +permissions.set = function (descriptor, state, origin) { + const principal = + Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin); switch (state) { case "granted": { Services.perms.addFromPrincipal( principal, - name, + descriptor.type, Services.perms.ALLOW_ACTION ); return; @@ -78,13 +55,13 @@ permissions.set = function (permissionType, state, oneRealm, browsingContext) { case "denied": { Services.perms.addFromPrincipal( principal, - name, + descriptor.type, Services.perms.DENY_ACTION ); return; } case "prompt": { - Services.perms.removeFromPrincipal(principal, name); + Services.perms.removeFromPrincipal(principal, descriptor.type); return; } default: @@ -93,3 +70,21 @@ permissions.set = function (permissionType, state, oneRealm, browsingContext) { ); } }; + +/** + * Validate the permission. + * + * @param {string} permissionName + * The name of the permission which will be validated. + * + * @throws {UnsupportedOperationError} + * If permissionName is not supported. + */ +permissions.validatePermission = function (permissionName) { + // Bug 1609427: PermissionDescriptor for "camera" and "microphone" are not yet implemented. + if (["camera", "microphone"].includes(permissionName)) { + throw new lazy.error.UnsupportedOperationError( + `"descriptor.name" "${permissionName}" is currently unsupported` + ); + } +}; diff --git a/remote/test/puppeteer/test/TestExpectations.json b/remote/test/puppeteer/test/TestExpectations.json index 008a15fd1303..9f021882f556 100644 --- a/remote/test/puppeteer/test/TestExpectations.json +++ b/remote/test/puppeteer/test/TestExpectations.json @@ -1028,6 +1028,13 @@ "expectations": ["FAIL"], "comment": "TODO: add a comment explaining why this expectation is required (include links to issues)" }, + { + "testIdPattern": "[browsercontext.spec] BrowserContext BrowserContext.overridePermissions should deny permission when not listed", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["firefox", "webDriverBiDi"], + "expectations": ["FAIL"], + "comment": "https://bugzilla.mozilla.org/show_bug.cgi?id=1894217" + }, { "testIdPattern": "[browsercontext.spec] BrowserContext BrowserContext.overridePermissions should fail when bad permission is given", "platforms": ["darwin", "linux", "win32"], @@ -1041,6 +1048,13 @@ "expectations": ["FAIL"], "comment": "TODO: add a comment explaining why this expectation is required (include links to issues)" }, + { + "testIdPattern": "[browsercontext.spec] BrowserContext BrowserContext.overridePermissions should grant permission when listed", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["firefox", "webDriverBiDi"], + "expectations": ["FAIL"], + "comment": "https://bugzilla.mozilla.org/show_bug.cgi?id=1894217" + }, { "testIdPattern": "[browsercontext.spec] BrowserContext BrowserContext.overridePermissions should grant persistent-storage", "platforms": ["darwin", "linux", "win32"], @@ -1048,6 +1062,13 @@ "expectations": ["FAIL"], "comment": "TODO: add a comment explaining why this expectation is required (include links to issues)" }, + { + "testIdPattern": "[browsercontext.spec] BrowserContext BrowserContext.overridePermissions should grant persistent-storage", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["firefox", "webDriverBiDi"], + "expectations": ["FAIL"], + "comment": "https://bugzilla.mozilla.org/show_bug.cgi?id=1894217" + }, { "testIdPattern": "[browsercontext.spec] BrowserContext BrowserContext.overridePermissions should isolate permissions between browser contexts", "platforms": ["darwin", "linux", "win32"], @@ -1055,6 +1076,13 @@ "expectations": ["FAIL"], "comment": "TODO: add a comment explaining why this expectation is required (include links to issues)" }, + { + "testIdPattern": "[browsercontext.spec] BrowserContext BrowserContext.overridePermissions should isolate permissions between browser contexts", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["firefox", "webDriverBiDi"], + "expectations": ["FAIL"], + "comment": "https://bugzilla.mozilla.org/show_bug.cgi?id=1894217" + }, { "testIdPattern": "[browsercontext.spec] BrowserContext BrowserContext.overridePermissions should reset permissions", "platforms": ["darwin", "linux", "win32"], @@ -1062,12 +1090,26 @@ "expectations": ["FAIL"], "comment": "TODO: add a comment explaining why this expectation is required (include links to issues)" }, + { + "testIdPattern": "[browsercontext.spec] BrowserContext BrowserContext.overridePermissions should reset permissions", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["firefox", "webDriverBiDi"], + "expectations": ["FAIL"], + "comment": "https://bugzilla.mozilla.org/show_bug.cgi?id=1894217" + }, + { + "testIdPattern": "[browsercontext.spec] BrowserContext BrowserContext.overridePermissions should trigger permission onchange", + "platforms": ["darwin", "linux", "win32"], + "parameters": ["cdp", "firefox"], + "expectations": ["FAIL"], + "comment": "TODO: add a comment explaining why this expectation is required (include links to issues)" + }, { "testIdPattern": "[browsercontext.spec] BrowserContext BrowserContext.overridePermissions should trigger permission onchange", "platforms": ["darwin", "linux", "win32"], - "parameters": ["cdp", "firefox"], + "parameters": ["firefox", "webDriverBiDi"], "expectations": ["FAIL"], - "comment": "TODO: add a comment explaining why this expectation is required (include links to issues)" + "comment": "https://bugzilla.mozilla.org/show_bug.cgi?id=1894217" }, { "testIdPattern": "[browsercontext.spec] BrowserContext should fire target events", diff --git a/remote/webdriver-bidi/jar.mn b/remote/webdriver-bidi/jar.mn index 6f0b2493d8b2..ed28fda6ba66 100644 --- a/remote/webdriver-bidi/jar.mn +++ b/remote/webdriver-bidi/jar.mn @@ -21,6 +21,7 @@ remote.jar: content/webdriver-bidi/modules/root/input.sys.mjs (modules/root/input.sys.mjs) content/webdriver-bidi/modules/root/log.sys.mjs (modules/root/log.sys.mjs) content/webdriver-bidi/modules/root/network.sys.mjs (modules/root/network.sys.mjs) + content/webdriver-bidi/modules/root/permissions.sys.mjs (modules/root/permissions.sys.mjs) content/webdriver-bidi/modules/root/script.sys.mjs (modules/root/script.sys.mjs) content/webdriver-bidi/modules/root/session.sys.mjs (modules/root/session.sys.mjs) content/webdriver-bidi/modules/root/storage.sys.mjs (modules/root/storage.sys.mjs) diff --git a/remote/webdriver-bidi/modules/ModuleRegistry.sys.mjs b/remote/webdriver-bidi/modules/ModuleRegistry.sys.mjs index 63713f1f02a9..145e227fc751 100644 --- a/remote/webdriver-bidi/modules/ModuleRegistry.sys.mjs +++ b/remote/webdriver-bidi/modules/ModuleRegistry.sys.mjs @@ -18,6 +18,8 @@ ChromeUtils.defineESModuleGetters(modules.root, { log: "chrome://remote/content/webdriver-bidi/modules/root/log.sys.mjs", network: "chrome://remote/content/webdriver-bidi/modules/root/network.sys.mjs", + permissions: + "chrome://remote/content/webdriver-bidi/modules/root/permissions.sys.mjs", script: "chrome://remote/content/webdriver-bidi/modules/root/script.sys.mjs", session: "chrome://remote/content/webdriver-bidi/modules/root/session.sys.mjs", diff --git a/remote/webdriver-bidi/modules/root/permissions.sys.mjs b/remote/webdriver-bidi/modules/root/permissions.sys.mjs new file mode 100644 index 000000000000..7c66b113b035 --- /dev/null +++ b/remote/webdriver-bidi/modules/root/permissions.sys.mjs @@ -0,0 +1,140 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + assert: "chrome://remote/content/shared/webdriver/Assert.sys.mjs", + error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs", + permissions: "chrome://remote/content/shared/Permissions.sys.mjs", +}); + +export const PermissionState = { + denied: "denied", + granted: "granted", + prompt: "prompt", +}; + +class PermissionsModule extends Module { + constructor(messageHandler) { + super(messageHandler); + } + + destroy() {} + + /** + * An object that holds the information about permission descriptor + * for Webdriver BiDi permissions.setPermission command. + * + * @typedef PermissionDescriptor + * + * @property {string} name + * The name of the permission. + */ + + /** + * Set to a given permission descriptor a given state on a provided origin. + * + * @param {object=} options + * @param {PermissionDescriptor} options.descriptor + * The descriptor of the permission which will be updated. + * @param {PermissionState} options.state + * The state which will be set to the permission. + * @param {string} options.origin + * The origin which is used as a target for permission update. + * @param {string=} options.userContext [unsupported] + * The id of the user context which should be used as a target + * for permission update. + * + * @throws {InvalidArgumentError} + * Raised if an argument is of an invalid type or value. + * @throws {UnsupportedOperationError} + * Raised when unsupported permissions are set or userContext + * argument is used. + */ + async setPermission(options = {}) { + const { + descriptor, + state, + origin, + userContext: userContextId = null, + } = options; + + lazy.assert.object( + descriptor, + `Expected "descriptor" to be an object, got ${descriptor}` + ); + const permissionName = descriptor.name; + lazy.assert.string( + permissionName, + `Expected "descriptor.name" to be a string, got ${permissionName}` + ); + + lazy.permissions.validatePermission(permissionName); + + // Bug 1878741: Allowing this permission causes timing related Android crash. + if (descriptor.name === "notifications") { + if (Services.prefs.getBoolPref("notification.prompt.testing", false)) { + // Okay, do nothing. The notifications module will work without permission. + return; + } + throw new lazy.error.UnsupportedOperationError( + `Setting "descriptor.name" "notifications" expected "notification.prompt.testing" preference to be set` + ); + } + + if (permissionName === "storage-access") { + // TODO: Bug 1895457. Add support for "storage-access" permission. + throw new lazy.error.UnsupportedOperationError( + `"descriptor.name" "${permissionName}" is currently unsupported` + ); + } + + const permissionStateTypes = Object.keys(PermissionState); + lazy.assert.that( + state => permissionStateTypes.includes(state), + `Expected "state" to be one of ${permissionStateTypes}, got ${state}` + )(state); + + lazy.assert.string( + origin, + `Expected "origin" to be a string, got ${origin}` + ); + lazy.assert.that( + origin => URL.canParse(origin), + `Expected "origin" to be a valid URL, got ${origin}` + )(origin); + + if (userContextId !== null) { + lazy.assert.string( + userContextId, + `Expected "userContext" to be a string, got ${userContextId}` + ); + + // TODO: Bug 1894217. Add support for "userContext" argument. + throw new lazy.error.UnsupportedOperationError( + `"userContext" is not supported yet` + ); + } + + const activeWindow = Services.wm.getMostRecentBrowserWindow(); + let typedDescriptor; + try { + typedDescriptor = activeWindow.navigator.permissions.parseSetParameters({ + descriptor, + state, + }); + } catch (err) { + throw new lazy.error.InvalidArgumentError( + `The conversion of "descriptor" was not successful: ${err.message}` + ); + } + + lazy.permissions.set(typedDescriptor, state, origin); + } +} + +export const permissions = PermissionsModule; diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_set_permission.py b/testing/marionette/harness/marionette_harness/tests/unit/test_set_permission.py index 476d9c5729b3..ba35bd6b990b 100644 --- a/testing/marionette/harness/marionette_harness/tests/unit/test_set_permission.py +++ b/testing/marionette/harness/marionette_harness/tests/unit/test_set_permission.py @@ -48,3 +48,7 @@ class TestSetPermission(MarionetteTestCase): self.assertEqual( self.query_permission({"name": "midi", "sysex": True}), "prompt" ) + + def test_storage_access(self): + self.marionette.set_permission({"name": "storage-access"}, "granted") + self.assertEqual(self.query_permission({"name": "storage-access"}), "granted") diff --git a/testing/web-platform/meta/webdriver/tests/bidi/external/permissions/set_permission/invalid.py.ini b/testing/web-platform/meta/webdriver/tests/bidi/external/permissions/set_permission/invalid.py.ini deleted file mode 100644 index 4c807f188ff0..000000000000 --- a/testing/web-platform/meta/webdriver/tests/bidi/external/permissions/set_permission/invalid.py.ini +++ /dev/null @@ -1,68 +0,0 @@ -[invalid.py] - expected: - if (processor == "x86") and not debug: [OK, TIMEOUT] - [test_params_descriptor_invalid_type[False\]] - expected: FAIL - - [test_params_descriptor_invalid_type[SOME_STRING\]] - expected: FAIL - - [test_params_descriptor_invalid_type[42\]] - expected: FAIL - - [test_params_descriptor_invalid_type[descriptor3\]] - expected: FAIL - - [test_params_descriptor_invalid_type[descriptor4\]] - expected: FAIL - - [test_params_descriptor_invalid_type[descriptor5\]] - expected: FAIL - - [test_params_descriptor_invalid_type[None\]] - expected: FAIL - - [test_params_descriptor_invalid_type[descriptor7\]] - expected: FAIL - - [test_params_descriptor_invalid_value[descriptor0\]] - expected: FAIL - - [test_params_state_invalid_type[False\]] - expected: FAIL - - [test_params_state_invalid_type[42\]] - expected: FAIL - - [test_params_state_invalid_type[state2\]] - expected: FAIL - - [test_params_state_invalid_type[state3\]] - expected: FAIL - - [test_params_state_invalid_type[None\]] - expected: FAIL - - [test_params_state_invalid_type[state5\]] - expected: FAIL - - [test_params_state_invalid_value[Granted\]] - expected: FAIL - - [test_params_origin_invalid_type[False\]] - expected: FAIL - - [test_params_origin_invalid_type[42\]] - expected: FAIL - - [test_params_origin_invalid_type[None\]] - expected: FAIL - - [test_params_state_invalid_value[UNKNOWN\]] - expected: FAIL - - [test_params_origin_invalid_type[user_context2\]] - expected: FAIL - - [test_params_origin_invalid_type[user_context3\]] - expected: FAIL diff --git a/testing/web-platform/meta/webdriver/tests/bidi/external/permissions/set_permission/set_permission.py.ini b/testing/web-platform/meta/webdriver/tests/bidi/external/permissions/set_permission/set_permission.py.ini index 789600d42655..1de0c86fe07e 100644 --- a/testing/web-platform/meta/webdriver/tests/bidi/external/permissions/set_permission/set_permission.py.ini +++ b/testing/web-platform/meta/webdriver/tests/bidi/external/permissions/set_permission/set_permission.py.ini @@ -1,13 +1,4 @@ [set_permission.py] - [test_set_permission] - expected: FAIL - - [test_set_permission_insecure_context] - expected: FAIL - - [test_set_permission_new_context] - expected: FAIL - [test_set_permission_origin_unknown[UNKNOWN\]] expected: FAIL @@ -16,3 +7,6 @@ [test_set_permission_user_context] expected: FAIL + bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1894217 + disabled: + if os == "android": bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1877953