Bug 1876754 - Add Permissions::ParseSetParameters for webdriver r=webdriver-reviewers,webidl,smaug,whimboo

Differential Revision: https://phabricator.services.mozilla.com/D200941
This commit is contained in:
Kagami Sascha Rosylight 2024-02-12 16:08:03 +00:00
Родитель cbb35c98d7
Коммит 466dba5633
11 изменённых файлов: 96 добавлений и 40 удалений

Просмотреть файл

@ -0,0 +1,18 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*
* The origin of this IDL file is
* https://w3c.github.io/permissions/#permissions-interface
*
* This file is in chrome-webidl as:
* 1. This is for webdriver and is not directly exposed to web
* 2. Putting this to webidl/Permissions.webidl causes header inclusion conflict
*/
[GenerateInit]
dictionary PermissionSetParameters {
required object descriptor;
required PermissionState state;
};

Просмотреть файл

@ -82,6 +82,7 @@ WEBIDL_FILES = [
"MozStorageStatementRow.webidl",
"NetDashboard.webidl",
"PathUtils.webidl",
"PermissionSetParameters.webidl",
"PrecompiledScript.webidl",
"PromiseDebugging.webidl",
"SessionStoreUtils.webidl",

Просмотреть файл

@ -167,4 +167,8 @@ void PermissionStatus::DisconnectFromOwner() {
DOMEventTargetHelper::DisconnectFromOwner();
}
void PermissionStatus::GetType(nsACString& aName) const {
aName.Assign(GetPermissionType());
}
} // namespace mozilla::dom

Просмотреть файл

@ -29,6 +29,7 @@ class PermissionStatus : public DOMEventTargetHelper {
JS::Handle<JSObject*> aGivenProto) override;
PermissionState State() const { return mState; }
void SetState(PermissionState aState) { mState = aState; }
IMPL_EVENT_HANDLER(change)
@ -36,6 +37,8 @@ class PermissionStatus : public DOMEventTargetHelper {
PermissionName Name() const { return mName; }
void GetType(nsACString& aName) const;
RefPtr<SimplePromise> Init();
protected:

Просмотреть файл

@ -6,16 +6,14 @@
#include "mozilla/dom/Permissions.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/MidiPermissionStatus.h"
#include "mozilla/dom/PermissionMessageUtils.h"
#include "mozilla/dom/PermissionSetParametersBinding.h"
#include "mozilla/dom/PermissionStatus.h"
#include "mozilla/dom/PermissionsBinding.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/RootedDictionary.h"
#include "mozilla/dom/StorageAccessPermissionStatus.h"
#include "mozilla/Components.h"
#include "nsIPermissionManager.h"
#include "PermissionUtils.h"
namespace mozilla::dom {
@ -143,4 +141,42 @@ already_AddRefed<Promise> Permissions::Query(JSContext* aCx,
return promise.forget();
}
already_AddRefed<PermissionStatus> Permissions::ParseSetParameters(
JSContext* aCx, JS::Handle<JSObject*> aParameters, ErrorResult& aRv) {
// Step 1: Let parametersDict be the parameters argument, converted to an IDL
// value of type PermissionSetParameters. If this throws an exception,
// return an invalid argument error.
// (The error type should be handled by the caller)
JS::Rooted<JS::Value> parameters(aCx, JS::ObjectValue(*aParameters));
RootedDictionary<PermissionSetParameters> parametersDict(aCx);
if (!parametersDict.Init(aCx, parameters)) {
aRv.MightThrowJSException();
aRv.StealExceptionFromJSContext(aCx);
return nullptr;
}
// Step 2: Let rootDesc be parameters.descriptor.
JS::Rooted<JSObject*> rootDesc(aCx, parametersDict.mDescriptor);
// Step 3: If parameters.state is an inappropriate permission state for any
// implementation-defined reason, return a invalid argument error.
// (We don't do this)
// Step 4: Let typedDescriptor be the object rootDesc refers to, converted
// to an IDL value of rootDesc.name's permission descriptor type. If this
// throws an exception, return a invalid argument error.
//
// We use PermissionStatus as the typed object.
RefPtr<PermissionStatus> status =
CreatePermissionStatus(aCx, rootDesc, nullptr, aRv);
if (aRv.Failed()) {
return nullptr;
}
// Set the state too so that the caller can use it for step 5.
status->SetState(parametersDict.mState);
return status.forget();
}
} // namespace mozilla::dom

Просмотреть файл

@ -18,6 +18,7 @@ class ErrorResult;
namespace dom {
class Promise;
class PermissionStatus;
class Permissions final : public nsISupports, public nsWrapperCache {
public:
@ -35,6 +36,11 @@ class Permissions final : public nsISupports, public nsWrapperCache {
JS::Handle<JSObject*> aPermission,
ErrorResult& aRv);
// The IDL conversion steps of
// https://w3c.github.io/permissions/#webdriver-command-set-permission
already_AddRefed<PermissionStatus> ParseSetParameters(
JSContext* aCx, JS::Handle<JSObject*> aParameters, ErrorResult& aRv);
private:
~Permissions();

Просмотреть файл

@ -11,6 +11,7 @@ EXPORTS.mozilla.dom += [
"MidiPermissionStatus.h",
"Permissions.h",
"PermissionStatus.h",
"PermissionUtils.h",
"StorageAccessPermissionStatus.h",
]

Просмотреть файл

@ -18,4 +18,7 @@ interface PermissionStatus : EventTarget {
readonly attribute PermissionName name;
readonly attribute PermissionState state;
attribute EventHandler onchange;
[ChromeOnly]
readonly attribute UTF8String type;
};

Просмотреть файл

@ -35,4 +35,10 @@ dictionary MidiPermissionDescriptor : PermissionDescriptor {
interface Permissions {
[NewObject]
Promise<PermissionStatus> query(object permission);
// http://w3c.github.io/permissions/#webdriver-command-set-permission
// We use `object` instead of PermissionSetParameters here to retain the access
// to the extra descriptor members from e.g. MidiPermissionDescriptor.
[ChromeOnly, Throws]
PermissionStatus parseSetParameters(object parameters);
};

Просмотреть файл

@ -3382,28 +3382,20 @@ GeckoDriver.prototype.setPermission = async function (cmd) {
);
}
// This abuses permissions.query() to do the IDL conversion in step 1 in the spec:
// https://w3c.github.io/permissions/#webdriver-command-set-permission
// If this does not throw then the descriptor is valid.
//
// TODO: Currently we consume the original JS object later on, but we should
// consume the IDL-converted dictionary instead.
// For WPT purpose the current state is fine, but for general webdriver extension
// this is not ideal as the script might get access to fields that are not in IDL.
// See bug 1876754.
let params;
try {
await this.curBrowser.window.navigator.permissions.query(descriptor);
params =
await this.curBrowser.window.navigator.permissions.parseSetParameters({
descriptor,
state,
});
} catch (err) {
throw new lazy.error.InvalidArgumentError(`setPermission: ${err.message}`);
}
lazy.assert.boolean(oneRealm);
lazy.assert.that(
state => ["granted", "denied", "prompt"].includes(state),
`state is ${state}, expected "granted", "denied", or "prompt"`
)(state);
lazy.permissions.set(descriptor, state, oneRealm, browsingContext);
lazy.permissions.set(params.type, params.state, oneRealm, browsingContext);
};
/**

Просмотреть файл

@ -12,18 +12,11 @@ ChromeUtils.defineESModuleGetters(lazy, {
/** @namespace */
export const permissions = {};
const specialPermissionNameMap = {
geolocation: "geo",
notifications: "desktop-notification",
};
function mapToInternalPermissionParameters(browsingContext, descriptor) {
function mapToInternalPermissionParameters(browsingContext, permissionType) {
const currentURI = browsingContext.currentWindowGlobal.documentURI;
const { name } = descriptor;
// storage-access is quite special...
if (name === "storage-access") {
if (permissionType === "storage-access") {
const thirdPartyPrincipalSite = Services.eTLD.getSite(currentURI);
const topLevelURI = browsingContext.top.currentWindowGlobal.documentURI;
@ -39,15 +32,8 @@ function mapToInternalPermissionParameters(browsingContext, descriptor) {
const currentPrincipal =
Services.scriptSecurityManager.createContentPrincipal(currentURI, {});
if (name === "midi" && descriptor.sysex) {
return {
name: "midi-sysex",
principal: currentPrincipal,
};
}
return {
name: specialPermissionNameMap[name] ?? name,
name: permissionType,
principal: currentPrincipal,
};
}
@ -56,8 +42,8 @@ function mapToInternalPermissionParameters(browsingContext, descriptor) {
* Set a permission's state.
* Note: Currently just a shim to support testdriver's set_permission.
*
* @param {object} descriptor
* Descriptor with the `name` property.
* @param {object} permissionType
* The Gecko internal permission type
* @param {string} state
* State of the permission. It can be `granted`, `denied` or `prompt`.
* @param {boolean} oneRealm
@ -68,7 +54,7 @@ function mapToInternalPermissionParameters(browsingContext, descriptor) {
* If `marionette.setpermission.enabled` is not set or
* an unsupported permission is used.
*/
permissions.set = function (descriptor, state, oneRealm, browsingContext) {
permissions.set = function (permissionType, state, oneRealm, browsingContext) {
if (!lazy.MarionettePrefs.setPermissionEnabled) {
throw new lazy.error.UnsupportedOperationError(
"'Set Permission' is not available"
@ -77,7 +63,7 @@ permissions.set = function (descriptor, state, oneRealm, browsingContext) {
const { name, principal } = mapToInternalPermissionParameters(
browsingContext,
descriptor
permissionType
);
switch (state) {