Skip to content

Commit 4645cf7

Browse files
committed
Implement trusted types for attribute sinks
Additionally, several methods were updated with spec comments. That's because the "adopt the document from the element document" step was missing. By adding these spec comments, I also restructured some code to avoid duplication of mutation records and custom element reaction queueing. Node.textContent doesn't propagate the error yet, as that method has a lot of separate callers of elements that wouldn't fail. I will refactor those in a follow-up PR to keep things manageable. 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 f31edc5 commit 4645cf7

11 files changed

+223
-1343
lines changed

components/script/dom/attr.rs

Lines changed: 41 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
44

55
use std::borrow::ToOwned;
6-
use std::cell::LazyCell;
76
use std::mem;
87
use std::sync::LazyLock;
98

@@ -16,17 +15,15 @@ use stylo_atoms::Atom;
1615

1716
use crate::dom::bindings::cell::{DomRefCell, Ref};
1817
use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
19-
use crate::dom::bindings::inheritance::Castable;
18+
use crate::dom::bindings::codegen::UnionTypes::TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString as TrustedTypeOrString;
19+
use crate::dom::bindings::error::Fallible;
2020
use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
2121
use crate::dom::bindings::str::DOMString;
22-
use crate::dom::customelementregistry::CallbackReaction;
2322
use crate::dom::document::Document;
24-
use crate::dom::element::{AttributeMutation, Element};
25-
use crate::dom::mutationobserver::{Mutation, MutationObserver};
26-
use crate::dom::node::Node;
27-
use crate::dom::virtualmethods::vtable_for;
23+
use crate::dom::element::Element;
24+
use crate::dom::node::{Node, NodeTraits};
25+
use crate::dom::trustedtypepolicyfactory::TrustedTypePolicyFactory;
2826
use crate::script_runtime::CanGc;
29-
use crate::script_thread::ScriptThread;
3027

3128
// https://dom.spec.whatwg.org/#interface-attr
3229
#[dom_struct]
@@ -112,14 +109,41 @@ impl AttrMethods<crate::DomTypeHolder> for Attr {
112109
DOMString::from(&**self.value())
113110
}
114111

115-
// https://dom.spec.whatwg.org/#dom-attr-value
116-
fn SetValue(&self, value: DOMString, can_gc: CanGc) {
112+
/// <https://dom.spec.whatwg.org/#set-an-existing-attribute-value>
113+
fn SetValue(&self, value: DOMString, can_gc: CanGc) -> Fallible<()> {
114+
// Step 2. Otherwise:
117115
if let Some(owner) = self.owner() {
118-
let value = owner.parse_attribute(self.namespace(), self.local_name(), value);
119-
self.set_value(value, &owner, can_gc);
116+
// Step 2.1. Let originalElement be attribute’s element.
117+
let original_element = owner.clone();
118+
// Step 2.2. Let verifiedValue be the result of calling
119+
// get Trusted Types-compliant attribute value with attribute’s local name,
120+
// attribute’s namespace, this, and value. [TRUSTED-TYPES]
121+
let value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
122+
owner.namespace(),
123+
owner.local_name(),
124+
self.local_name(),
125+
Some(self.namespace()),
126+
TrustedTypeOrString::String(value),
127+
&owner.owner_global(),
128+
can_gc,
129+
)?;
130+
if let Some(owner) = self.owner() {
131+
// Step 2.4. If attribute’s element is not originalElement, then return.
132+
if owner != original_element {
133+
return Ok(());
134+
}
135+
// Step 2.5. Change attribute to verifiedValue.
136+
let value = owner.parse_attribute(self.namespace(), self.local_name(), value);
137+
owner.change_attribute(self, value, can_gc);
138+
} else {
139+
// Step 2.3. If attribute’s element is null, then set attribute’s value to verifiedValue, and return.
140+
self.set_value(value);
141+
}
120142
} else {
121-
*self.value.borrow_mut() = AttrValue::String(value.into());
143+
// Step 1. If attribute’s element is null, then set attribute’s value to value.
144+
self.set_value(value);
122145
}
146+
Ok(())
123147
}
124148

125149
// https://dom.spec.whatwg.org/#dom-attr-name
@@ -154,41 +178,6 @@ impl AttrMethods<crate::DomTypeHolder> for Attr {
154178
}
155179

156180
impl Attr {
157-
pub(crate) fn set_value(&self, mut value: AttrValue, owner: &Element, can_gc: CanGc) {
158-
let name = self.local_name().clone();
159-
let namespace = self.namespace().clone();
160-
let old_value = DOMString::from(&**self.value());
161-
let new_value = DOMString::from(&*value);
162-
let mutation = LazyCell::new(|| Mutation::Attribute {
163-
name: name.clone(),
164-
namespace: namespace.clone(),
165-
old_value: Some(old_value.clone()),
166-
});
167-
168-
MutationObserver::queue_a_mutation_record(owner.upcast::<Node>(), mutation);
169-
170-
if owner.is_custom() {
171-
let reaction = CallbackReaction::AttributeChanged(
172-
name,
173-
Some(old_value),
174-
Some(new_value),
175-
namespace,
176-
);
177-
ScriptThread::enqueue_callback_reaction(owner, reaction, None);
178-
}
179-
180-
assert_eq!(Some(owner), self.owner().as_deref());
181-
owner.will_mutate_attr(self);
182-
self.swap_value(&mut value);
183-
if is_relevant_attribute(self.namespace(), self.local_name()) {
184-
vtable_for(owner.upcast()).attribute_mutated(
185-
self,
186-
AttributeMutation::Set(Some(&value)),
187-
can_gc,
188-
);
189-
}
190-
}
191-
192181
/// Used to swap the attribute's value without triggering mutation events
193182
pub(crate) fn swap_value(&self, value: &mut AttrValue) {
194183
mem::swap(&mut *self.value.borrow_mut(), value);
@@ -202,6 +191,10 @@ impl Attr {
202191
self.value.borrow()
203192
}
204193

194+
fn set_value(&self, value: DOMString) {
195+
*self.value.borrow_mut() = AttrValue::String(value.into());
196+
}
197+
205198
pub(crate) fn local_name(&self) -> &LocalName {
206199
&self.identifier.local_name
207200
}

0 commit comments

Comments
 (0)