Implement attribute and property lookup for Trusted Types (#36422)

These algorithms are used to check whether an attribute/property can
accept a Trusted Type.

Part of #36258

Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
This commit is contained in:
Tim van der Lippe 2025-04-11 10:38:00 +02:00 committed by GitHub
parent 2c7aeca404
commit 0aa08042d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 143 additions and 192 deletions

View file

@ -4,6 +4,7 @@
use std::cell::RefCell;
use dom_struct::dom_struct;
use html5ever::{LocalName, Namespace, QualName, local_name, namespace_url, ns};
use js::rust::HandleValue;
use crate::dom::bindings::codegen::Bindings::TrustedTypePolicyFactoryBinding::{
@ -85,6 +86,49 @@ impl TrustedTypePolicyFactory {
// Step 9: Return policy.
Ok(policy)
}
/// <https://w3c.github.io/trusted-types/dist/spec/#abstract-opdef-get-trusted-type-data-for-attribute>
#[allow(clippy::if_same_then_else)]
fn get_trusted_type_data_for_attribute(
element: QualName,
attribute: String,
attribute_namespace: Option<Namespace>,
) -> Option<DOMString> {
// Step 1: Let data be null.
let mut data = None;
// Step 2: If attributeNs is null, and attribute is the name of an event handler content attribute, then:
// TODO(36258): look up event handlers
// Step 3: Find the row in the following table, where element is in the first column,
// attributeNs is in the second column, and attribute is in the third column.
// If a matching row is found, set data to that row.
if element.ns == ns!(html) &&
element.local == local_name!("iframe") &&
attribute_namespace.is_none() &&
attribute == "srcdoc"
{
data = Some(DOMString::from("TrustedHTML"))
} else if element.ns == ns!(html) &&
element.local == local_name!("script") &&
attribute_namespace.is_none() &&
attribute == "src"
{
data = Some(DOMString::from("TrustedScriptURL"))
} else if element.ns == ns!(svg) &&
element.local == local_name!("script") &&
attribute_namespace.is_none() &&
attribute == "href"
{
data = Some(DOMString::from("TrustedScriptURL"))
} else if element.ns == ns!(svg) &&
element.local == local_name!("script") &&
attribute_namespace == Some(ns!(xlink)) &&
attribute == "href"
{
data = Some(DOMString::from("TrustedScriptURL"))
}
// Step 4: Return data.
data
}
}
impl TrustedTypePolicyFactoryMethods<crate::DomTypeHolder> for TrustedTypePolicyFactory {
@ -135,23 +179,99 @@ impl TrustedTypePolicyFactoryMethods<crate::DomTypeHolder> for TrustedTypePolicy
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicyfactory-getattributetype>
fn GetAttributeType(
&self,
_: DOMString,
_: DOMString,
_: Option<DOMString>,
_: Option<DOMString>,
tag_name: DOMString,
attribute: DOMString,
element_namespace: Option<DOMString>,
attribute_namespace: Option<DOMString>,
) -> Option<DOMString> {
// TODO(36258): implement algorithm
Some(DOMString::from("".to_string()))
// Step 1: Set localName to tagName in ASCII lowercase.
let local_name = tag_name.to_ascii_lowercase();
// Step 2: Set attribute to attribute in ASCII lowercase.
let attribute = attribute.to_ascii_lowercase();
// Step 3: If elementNs is null or an empty string, set elementNs to HTML namespace.
let element_namespace = match element_namespace {
Some(namespace) if !namespace.is_empty() => Namespace::from(namespace),
Some(_) | None => ns!(html),
};
// Step 4: If attrNs is an empty string, set attrNs to null.
let attribute_namespace = match attribute_namespace {
Some(namespace) if !namespace.is_empty() => Some(Namespace::from(namespace)),
Some(_) | None => None,
};
// Step 5: Let interface be the element interface for localName and elementNs.
let interface = QualName::new(None, element_namespace, LocalName::from(local_name));
// Step 6: Let expectedType be null.
let mut expected_type = None;
// Step 7: Set attributeData to the result of Get Trusted Type data for attribute algorithm,
// with the following arguments: interface as element, attribute, attrNs
let attribute_data = TrustedTypePolicyFactory::get_trusted_type_data_for_attribute(
interface,
attribute,
attribute_namespace,
);
// Step 8: If attributeData is not null, then set expectedType to the interfaces name of
// the value of the fourth member of attributeData.
if let Some(trusted_type) = attribute_data {
expected_type = Some(trusted_type)
}
// Step 9: Return expectedType.
expected_type
}
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicyfactory-getpropertytype>
#[allow(clippy::if_same_then_else)]
fn GetPropertyType(
&self,
_: DOMString,
_: DOMString,
_: Option<DOMString>,
tag_name: DOMString,
property: DOMString,
element_namespace: Option<DOMString>,
) -> Option<DOMString> {
// TODO(36258): implement algorithm
Some(DOMString::from("".to_string()))
// Step 1: Set localName to tagName in ASCII lowercase.
let local_name = tag_name.to_ascii_lowercase();
// Step 2: If elementNs is null or an empty string, set elementNs to HTML namespace.
let element_namespace = match element_namespace {
Some(namespace) if !namespace.is_empty() => Namespace::from(namespace),
Some(_) | None => ns!(html),
};
// Step 3: Let interface be the element interface for localName and elementNs.
let interface = QualName::new(None, element_namespace, LocalName::from(local_name));
// Step 4: Let expectedType be null.
let mut expected_type = None;
// Step 5: Find the row in the following table, where the first column is "*" or interfaces name,
// and property is in the second column. If a matching row is found, set expectedType to
// the interfaces name of the value of the third column.
let property = property.str();
if interface.ns == ns!(html) &&
interface.local == local_name!("iframe") &&
property == "srcdoc"
{
expected_type = Some(DOMString::from("TrustedHTML"))
} else if interface.ns == ns!(html) &&
interface.local == local_name!("script") &&
property == "innerText"
{
expected_type = Some(DOMString::from("TrustedScript"))
} else if interface.ns == ns!(html) &&
interface.local == local_name!("script") &&
property == "src"
{
expected_type = Some(DOMString::from("TrustedScriptURL"))
} else if interface.ns == ns!(html) &&
interface.local == local_name!("script") &&
property == "text"
{
expected_type = Some(DOMString::from("TrustedScript"))
} else if interface.ns == ns!(html) &&
interface.local == local_name!("script") &&
property == "textContent"
{
expected_type = Some(DOMString::from("TrustedScript"))
} else if property == "innerHTML" {
expected_type = Some(DOMString::from("TrustedHTML"))
} else if property == "outerHTML" {
expected_type = Some(DOMString::from("TrustedHTML"))
}
// Step 6: Return expectedType.
expected_type
}
/// <https://www.w3.org/TR/trusted-types/#dom-trustedtypepolicyfactory-defaultpolicy>
fn GetDefaultPolicy(&self) -> Option<DomRoot<TrustedTypePolicy>> {