Skip to content

Commit a2e63b2

Browse files
TimvdLippePotatoCP
authored andcommitted
Implement trusted types for setAttribute (servo#38700)
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 ff2b399 commit a2e63b2

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,
@@ -3155,17 +3158,39 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
31553158
}
31563159

31573160
/// <https://dom.spec.whatwg.org/#dom-element-setattribute>
3158-
fn SetAttribute(&self, name: DOMString, value: DOMString, can_gc: CanGc) -> ErrorResult {
3159-
// Step 1. If qualifiedName is not a valid attribute local name,
3160-
// then throw an "InvalidCharacterError" DOMException.
3161+
fn SetAttribute(
3162+
&self,
3163+
name: DOMString,
3164+
value: TrustedTypeOrString,
3165+
can_gc: CanGc,
3166+
) -> ErrorResult {
3167+
// Step 1. If qualifiedName does not match the Name production in XML,
3168+
// then throw an "InvalidCharacterError" DOMException.
31613169
if !is_valid_attribute_local_name(&name) {
31623170
return Err(Error::InvalidCharacter);
31633171
}
31643172

3165-
// Step 2.
3173+
// Step 2. If this is in the HTML namespace and its node document is an HTML document,
3174+
// then set qualifiedName to qualifiedName in ASCII lowercase.
31663175
let name = self.parsed_name(name);
31673176

3168-
// Step 3-5.
3177+
// Step 3. Let verifiedValue be the result of calling get
3178+
// Trusted Types-compliant attribute value with qualifiedName, null,
3179+
// this, and value. [TRUSTED-TYPES]
3180+
let value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
3181+
self.namespace(),
3182+
self.local_name(),
3183+
&name,
3184+
None,
3185+
value,
3186+
&self.owner_global(),
3187+
can_gc,
3188+
)?;
3189+
3190+
// Step 4. Let attribute be the first attribute in this’s attribute list whose qualified name is qualifiedName, and null otherwise.
3191+
// Step 5. If attribute is null, create an attribute whose local name is qualifiedName, value is verifiedValue, and node document
3192+
// is this’s node document, then append this attribute to this, and then return.
3193+
// Step 6. Change attribute to verifiedValue.
31693194
let value = self.parse_attribute(&ns!(), &name, value);
31703195
self.set_first_matching_attribute(
31713196
name.clone(),
@@ -3184,20 +3209,29 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
31843209
&self,
31853210
namespace: Option<DOMString>,
31863211
qualified_name: DOMString,
3187-
value: DOMString,
3212+
value: TrustedTypeOrString,
31883213
can_gc: CanGc,
31893214
) -> ErrorResult {
3190-
// Step 1. Let (namespace, prefix, localName) be the result of validating and
3191-
// extracting namespace and qualifiedName given "element".
3192-
let context = domname::Context::Element;
3215+
// Step 1. Let namespace, prefix, and localName be the result of passing namespace and qualifiedName to validate and extract.
31933216
let (namespace, prefix, local_name) =
3194-
domname::validate_and_extract(namespace, &qualified_name, context)?;
3195-
let qualified_name = LocalName::from(qualified_name);
3217+
domname::validate_and_extract(namespace, &qualified_name, domname::Context::Element)?;
3218+
// Step 2. Let verifiedValue be the result of calling get
3219+
// Trusted Types-compliant attribute value with localName, namespace, element, and value. [TRUSTED-TYPES]
3220+
let value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
3221+
self.namespace(),
3222+
self.local_name(),
3223+
&local_name,
3224+
Some(&namespace),
3225+
value,
3226+
&self.owner_global(),
3227+
can_gc,
3228+
)?;
3229+
// Step 3. Set an attribute value for this using localName, verifiedValue, and also prefix and namespace.
31963230
let value = self.parse_attribute(&namespace, &local_name, value);
31973231
self.set_first_matching_attribute(
31983232
local_name.clone(),
31993233
value,
3200-
qualified_name,
3234+
LocalName::from(qualified_name),
32013235
namespace.clone(),
32023236
prefix,
32033237
|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(AsRefStr, Clone)]
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)