Skip to content

Commit 48958a1

Browse files
committed
Implement trusted types for setAttribute
Callers now call `set_attribute` directly, to avoid the trusted types machinery, as well as skip validation. That's not required by spec as well. This implements part of the DOM integration from whatwg/dom#1268 Part of servo#36258 Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
1 parent 046dbd8 commit 48958a1

18 files changed

+217
-240
lines changed

components/script/devtools.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ use devtools_traits::{
1111
AttrModification, AutoMargins, ComputedNodeLayout, CssDatabaseProperty, EvaluateJSReply,
1212
NodeInfo, NodeStyle, RuleModification, TimelineMarker, TimelineMarkerType,
1313
};
14+
use html5ever::LocalName;
1415
use ipc_channel::ipc::IpcSender;
1516
use js::conversions::jsstr_to_string;
1617
use js::jsval::UndefinedValue;
1718
use js::rust::ToString;
1819
use servo_config::pref;
20+
use style::attr::AttrValue;
1921
use uuid::Uuid;
2022

2123
use crate::document_collection::DocumentCollection;
@@ -435,9 +437,9 @@ pub(crate) fn handle_modify_attribute(
435437
for modification in modifications {
436438
match modification.new_value {
437439
Some(string) => {
438-
let _ = elem.SetAttribute(
439-
DOMString::from(modification.attribute_name),
440-
DOMString::from(string),
440+
elem.set_attribute(
441+
&LocalName::from(modification.attribute_name),
442+
AttrValue::String(string),
441443
can_gc,
442444
);
443445
},

components/script/dom/element.rs

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,9 @@ use crate::dom::bindings::codegen::Bindings::WindowBinding::{
8787
};
8888
use crate::dom::bindings::codegen::UnionTypes::{
8989
BooleanOrScrollIntoViewOptions, NodeOrString, TrustedHTMLOrNullIsEmptyString,
90-
TrustedHTMLOrString, TrustedScriptURLOrUSVString,
90+
TrustedHTMLOrString,
91+
TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString as TrustedTypeOrString,
92+
TrustedScriptURLOrUSVString,
9193
};
9294
use crate::dom::bindings::conversions::DerivedFrom;
9395
use crate::dom::bindings::domname::{
@@ -161,6 +163,7 @@ use crate::dom::servoparser::ServoParser;
161163
use crate::dom::shadowroot::{IsUserAgentWidget, ShadowRoot};
162164
use crate::dom::text::Text;
163165
use crate::dom::trustedhtml::TrustedHTML;
166+
use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory;
164167
use crate::dom::validation::Validatable;
165168
use crate::dom::validitystate::ValidationFlags;
166169
use crate::dom::virtualmethods::{VirtualMethods, vtable_for};
@@ -752,7 +755,7 @@ impl Element {
752755

753756
// https://html.spec.whatwg.org/multipage/#translation-mode
754757
pub(crate) fn is_translate_enabled(&self) -> bool {
755-
let name = &html5ever::local_name!("translate");
758+
let name = &local_name!("translate");
756759
if self.has_attribute(name) {
757760
match_ignore_ascii_case! { &*self.get_string_attribute(name),
758761
"yes" | "" => return true,
@@ -3250,17 +3253,39 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
32503253
}
32513254

32523255
/// <https://dom.spec.whatwg.org/#dom-element-setattribute>
3253-
fn SetAttribute(&self, name: DOMString, value: DOMString, can_gc: CanGc) -> ErrorResult {
3254-
// Step 1. If qualifiedName is not a valid attribute local name,
3255-
// then throw an "InvalidCharacterError" DOMException.
3256+
fn SetAttribute(
3257+
&self,
3258+
name: DOMString,
3259+
value: TrustedTypeOrString,
3260+
can_gc: CanGc,
3261+
) -> ErrorResult {
3262+
// Step 1. If qualifiedName does not match the Name production in XML,
3263+
// then throw an "InvalidCharacterError" DOMException.
32563264
if !is_valid_attribute_local_name(&name) {
32573265
return Err(Error::InvalidCharacter);
32583266
}
32593267

3260-
// Step 2.
3268+
// Step 2. If this is in the HTML namespace and its node document is an HTML document,
3269+
// then set qualifiedName to qualifiedName in ASCII lowercase.
32613270
let name = self.parsed_name(name);
32623271

3263-
// Step 3-5.
3272+
// Step 3. Let verifiedValue be the result of calling get
3273+
// Trusted Types-compliant attribute value with qualifiedName, null,
3274+
// this, and value. [TRUSTED-TYPES]
3275+
let value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
3276+
self.namespace(),
3277+
self.local_name(),
3278+
&name,
3279+
None,
3280+
value,
3281+
&self.owner_global(),
3282+
can_gc,
3283+
)?;
3284+
3285+
// Step 4. Let attribute be the first attribute in this’s attribute list whose qualified name is qualifiedName, and null otherwise.
3286+
// Step 5. If attribute is null, create an attribute whose local name is qualifiedName, value is verifiedValue, and node document
3287+
// is this’s node document, then append this attribute to this, and then return.
3288+
// Step 6. Change attribute to verifiedValue.
32643289
let value = self.parse_attribute(&ns!(), &name, value);
32653290
self.set_first_matching_attribute(
32663291
name.clone(),
@@ -3279,20 +3304,29 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
32793304
&self,
32803305
namespace: Option<DOMString>,
32813306
qualified_name: DOMString,
3282-
value: DOMString,
3307+
value: TrustedTypeOrString,
32833308
can_gc: CanGc,
32843309
) -> ErrorResult {
3285-
// Step 1. Let (namespace, prefix, localName) be the result of validating and
3286-
// extracting namespace and qualifiedName given "element".
3287-
let context = domname::Context::Element;
3310+
// Step 1. Let namespace, prefix, and localName be the result of passing namespace and qualifiedName to validate and extract.
32883311
let (namespace, prefix, local_name) =
3289-
domname::validate_and_extract(namespace, &qualified_name, context)?;
3290-
let qualified_name = LocalName::from(qualified_name);
3312+
domname::validate_and_extract(namespace, &qualified_name, domname::Context::Element)?;
3313+
// Step 2. Let verifiedValue be the result of calling get
3314+
// Trusted Types-compliant attribute value with localName, namespace, element, and value. [TRUSTED-TYPES]
3315+
let value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
3316+
self.namespace(),
3317+
self.local_name(),
3318+
&local_name,
3319+
Some(&namespace),
3320+
value,
3321+
&self.owner_global(),
3322+
can_gc,
3323+
)?;
3324+
// Step 3. Set an attribute value for this using localName, verifiedValue, and also prefix and namespace.
32913325
let value = self.parse_attribute(&namespace, &local_name, value);
32923326
self.set_first_matching_attribute(
32933327
local_name.clone(),
32943328
value,
3295-
qualified_name,
3329+
LocalName::from(qualified_name),
32963330
namespace.clone(),
32973331
prefix,
32983332
|attr| *attr.local_name() == local_name && *attr.namespace() == namespace,

components/script/dom/htmlaudioelement.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
use dom_struct::dom_struct;
66
use html5ever::{LocalName, Prefix, QualName, local_name, ns};
77
use js::rust::HandleObject;
8+
use style::attr::AttrValue;
89

9-
use crate::dom::bindings::codegen::Bindings::ElementBinding::Element_Binding::ElementMethods;
1010
use crate::dom::bindings::codegen::Bindings::HTMLAudioElementBinding::HTMLAudioElementMethods;
1111
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
1212
use crate::dom::bindings::error::Fallible;
@@ -75,15 +75,17 @@ impl HTMLAudioElementMethods<crate::DomTypeHolder> for HTMLAudioElement {
7575

7676
let audio = DomRoot::downcast::<HTMLAudioElement>(element).unwrap();
7777

78-
audio
79-
.upcast::<Element>()
80-
.SetAttribute(DOMString::from("preload"), DOMString::from("auto"), can_gc)
81-
.expect("should be infallible");
78+
audio.upcast::<Element>().set_attribute(
79+
&local_name!("preload"),
80+
AttrValue::String("auto".to_owned()),
81+
can_gc,
82+
);
8283
if let Some(s) = src {
83-
audio
84-
.upcast::<Element>()
85-
.SetAttribute(DOMString::from("src"), s, can_gc)
86-
.expect("should be infallible");
84+
audio.upcast::<Element>().set_attribute(
85+
&local_name!("src"),
86+
AttrValue::String(s.into()),
87+
can_gc,
88+
);
8789
}
8890

8991
Ok(audio)

components/script/dom/trustedhtml.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::dom::bindings::root::DomRoot;
1717
use crate::dom::bindings::str::DOMString;
1818
use crate::dom::globalscope::GlobalScope;
1919
use crate::dom::trustedtypepolicy::TrustedType;
20-
use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory;
20+
use crate::dom::trustedtypepolicyfactory::{DEFAULT_SCRIPT_SINK_GROUP, TrustedTypePolicyFactory};
2121
use crate::script_runtime::CanGc;
2222

2323
#[dom_struct]
@@ -53,14 +53,18 @@ impl TrustedHTML {
5353
global,
5454
value,
5555
sink,
56-
"'script'",
56+
DEFAULT_SCRIPT_SINK_GROUP,
5757
can_gc,
5858
)
5959
},
6060

6161
TrustedHTMLOrString::TrustedHTML(trusted_html) => Ok(trusted_html.data.clone()),
6262
}
6363
}
64+
65+
pub(crate) fn data(&self) -> DOMString {
66+
self.data.clone()
67+
}
6468
}
6569

6670
impl fmt::Display for TrustedHTML {

components/script/dom/trustedscript.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::dom::bindings::str::DOMString;
1616
use crate::dom::csp::CspReporting;
1717
use crate::dom::globalscope::GlobalScope;
1818
use crate::dom::trustedtypepolicy::TrustedType;
19-
use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory;
19+
use crate::dom::trustedtypepolicyfactory::{DEFAULT_SCRIPT_SINK_GROUP, TrustedTypePolicyFactory};
2020
use crate::script_runtime::{CanGc, JSContext};
2121

2222
#[dom_struct]
@@ -52,7 +52,7 @@ impl TrustedScript {
5252
global,
5353
value,
5454
sink,
55-
"'script'",
55+
DEFAULT_SCRIPT_SINK_GROUP,
5656
can_gc,
5757
)
5858
},

components/script/dom/trustedscripturl.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::dom::bindings::root::DomRoot;
1414
use crate::dom::bindings::str::DOMString;
1515
use crate::dom::globalscope::GlobalScope;
1616
use crate::dom::trustedtypepolicy::TrustedType;
17-
use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory;
17+
use crate::dom::trustedtypepolicyfactory::{DEFAULT_SCRIPT_SINK_GROUP, TrustedTypePolicyFactory};
1818
use crate::script_runtime::CanGc;
1919

2020
#[dom_struct]
@@ -52,7 +52,7 @@ impl TrustedScriptURL {
5252
global,
5353
value.as_ref().into(),
5454
&sink,
55-
"'script'",
55+
DEFAULT_SCRIPT_SINK_GROUP,
5656
can_gc,
5757
)
5858
},
@@ -61,6 +61,10 @@ impl TrustedScriptURL {
6161
},
6262
}
6363
}
64+
65+
pub(crate) fn data(&self) -> DOMString {
66+
self.data.clone()
67+
}
6468
}
6569

6670
impl fmt::Display for TrustedScriptURL {

components/script/dom/trustedtypepolicy.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ use std::rc::Rc;
66

77
use dom_struct::dom_struct;
88
use js::rust::HandleValue;
9-
use strum_macros::IntoStaticStr;
9+
use strum_macros::AsRefStr;
1010

1111
use crate::dom::bindings::callback::ExceptionHandling;
1212
use crate::dom::bindings::codegen::Bindings::TrustedTypePolicyBinding::TrustedTypePolicyMethods;
1313
use crate::dom::bindings::codegen::Bindings::TrustedTypePolicyFactoryBinding::{
1414
CreateHTMLCallback, CreateScriptCallback, CreateScriptURLCallback, TrustedTypePolicyOptions,
1515
};
16+
use crate::dom::bindings::codegen::UnionTypes::TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString as TrustedTypeOrString;
1617
use crate::dom::bindings::error::Error::Type;
1718
use crate::dom::bindings::error::Fallible;
1819
use crate::dom::bindings::reflector::{DomGlobal, DomObject, Reflector, reflect_dom_object};
@@ -38,13 +39,29 @@ pub struct TrustedTypePolicy {
3839
create_script_url: Option<Rc<CreateScriptURLCallback>>,
3940
}
4041

41-
#[derive(Clone, IntoStaticStr)]
42+
#[derive(Clone, Debug, AsRefStr)]
4243
pub(crate) enum TrustedType {
4344
TrustedHTML,
4445
TrustedScript,
4546
TrustedScriptURL,
4647
}
4748

49+
impl TrustedType {
50+
pub(crate) fn matches_idl_trusted_type(&self, idl_trusted_type: &TrustedTypeOrString) -> bool {
51+
match self {
52+
TrustedType::TrustedHTML => {
53+
matches!(idl_trusted_type, TrustedTypeOrString::TrustedHTML(_))
54+
},
55+
TrustedType::TrustedScript => {
56+
matches!(idl_trusted_type, TrustedTypeOrString::TrustedScript(_))
57+
},
58+
TrustedType::TrustedScriptURL => {
59+
matches!(idl_trusted_type, TrustedTypeOrString::TrustedScriptURL(_))
60+
},
61+
}
62+
}
63+
}
64+
4865
impl TrustedTypePolicy {
4966
fn new_inherited(name: String, options: &TrustedTypePolicyOptions) -> Self {
5067
Self {

0 commit comments

Comments
 (0)