Skip to content

Commit cc4cf08

Browse files
committed
Implement trusted types for setAttributeNode
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. 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 cc4cf08

File tree

1 file changed

+144
-97
lines changed

1 file changed

+144
-97
lines changed

components/script/dom/element.rs

Lines changed: 144 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -2140,31 +2140,69 @@ impl Element {
21402140
self.push_attribute(&attr, can_gc);
21412141
}
21422142

2143-
pub(crate) fn push_attribute(&self, attr: &Attr, can_gc: CanGc) {
2143+
/// <https://dom.spec.whatwg.org/#handle-attribute-changes>
2144+
fn handle_attribute_changes(
2145+
&self,
2146+
attr: &Attr,
2147+
old_value: Option<&AttrValue>,
2148+
new_value: Option<DOMString>,
2149+
can_gc: CanGc,
2150+
) {
2151+
let old_value_string = old_value.map(|old_value| DOMString::from(&**old_value));
2152+
// Step 1. Queue a mutation record of "attributes" for element with attribute’s local name,
2153+
// attribute’s namespace, oldValue, « », « », null, and null.
21442154
let name = attr.local_name().clone();
21452155
let namespace = attr.namespace().clone();
21462156
let mutation = LazyCell::new(|| Mutation::Attribute {
21472157
name: name.clone(),
21482158
namespace: namespace.clone(),
2149-
old_value: None,
2159+
old_value: old_value_string.clone(),
21502160
});
2151-
21522161
MutationObserver::queue_a_mutation_record(&self.node, mutation);
21532162

2163+
// Avoid double borrow
2164+
let has_new_value = new_value.is_none();
2165+
2166+
// Step 2. If element is custom, then enqueue a custom element callback reaction with element,
2167+
// callback name "attributeChangedCallback", and « attribute’s local name, oldValue, newValue, attribute’s namespace ».
21542168
if self.is_custom() {
2155-
let value = DOMString::from(&**attr.value());
2156-
let reaction = CallbackReaction::AttributeChanged(name, None, Some(value), namespace);
2169+
let reaction = CallbackReaction::AttributeChanged(
2170+
attr.local_name().clone(),
2171+
old_value_string,
2172+
new_value,
2173+
attr.namespace().clone(),
2174+
);
21572175
ScriptThread::enqueue_callback_reaction(self, reaction, None);
21582176
}
21592177

2160-
assert!(attr.GetOwnerElement().as_deref() == Some(self));
2178+
// Step 3. Run the attribute change steps with element, attribute’s local name, oldValue, newValue, and attribute’s namespace.
21612179
self.will_mutate_attr(attr);
2162-
self.attrs.borrow_mut().push(Dom::from_ref(attr));
21632180
if is_relevant_attribute(attr.namespace(), attr.local_name()) {
2164-
vtable_for(self.upcast()).attribute_mutated(attr, AttributeMutation::Set(None), can_gc);
2181+
let attribute_mutation = if has_new_value {
2182+
AttributeMutation::Removed
2183+
} else {
2184+
AttributeMutation::Set(old_value)
2185+
};
2186+
vtable_for(self.upcast()).attribute_mutated(attr, attribute_mutation, can_gc);
21652187
}
21662188
}
21672189

2190+
/// <https://dom.spec.whatwg.org/#concept-element-attributes-append>
2191+
pub(crate) fn push_attribute(&self, attr: &Attr, can_gc: CanGc) {
2192+
// Step 2. Set attribute’s element to element.
2193+
//
2194+
// Handled by callers of this function and asserted here.
2195+
assert!(attr.GetOwnerElement().as_deref() == Some(self));
2196+
// Step 3. Set attribute’s node document to element’s node document.
2197+
//
2198+
// Handled by callers of this function and asserted here.
2199+
assert!(attr.upcast::<Node>().owner_doc() == self.node.owner_doc());
2200+
// Step 1. Append attribute to element’s attribute list.
2201+
self.attrs.borrow_mut().push(Dom::from_ref(attr));
2202+
// Step 4. Handle attribute changes for attribute with element, null, and attribute’s value.
2203+
self.handle_attribute_changes(attr, None, Some(DOMString::from(&**attr.value())), can_gc);
2204+
}
2205+
21682206
pub(crate) fn get_attribute(
21692207
&self,
21702208
namespace: &Namespace,
@@ -2329,41 +2367,22 @@ impl Element {
23292367
self.remove_first_matching_attribute(|attr| attr.name() == name, can_gc)
23302368
}
23312369

2370+
/// <https://dom.spec.whatwg.org/#concept-element-attributes-remove>
23322371
fn remove_first_matching_attribute<F>(&self, find: F, can_gc: CanGc) -> Option<DomRoot<Attr>>
23332372
where
23342373
F: Fn(&Attr) -> bool,
23352374
{
23362375
let idx = self.attrs.borrow().iter().position(|attr| find(attr));
23372376
idx.map(|idx| {
23382377
let attr = DomRoot::from_ref(&*(*self.attrs.borrow())[idx]);
2339-
self.will_mutate_attr(&attr);
2340-
2341-
let name = attr.local_name().clone();
2342-
let namespace = attr.namespace().clone();
2343-
let old_value = DOMString::from(&**attr.value());
2344-
let mutation = LazyCell::new(|| Mutation::Attribute {
2345-
name: name.clone(),
2346-
namespace: namespace.clone(),
2347-
old_value: Some(old_value.clone()),
2348-
});
2349-
2350-
MutationObserver::queue_a_mutation_record(&self.node, mutation);
2351-
2352-
if self.is_custom() {
2353-
let reaction =
2354-
CallbackReaction::AttributeChanged(name, Some(old_value), None, namespace);
2355-
ScriptThread::enqueue_callback_reaction(self, reaction, None);
2356-
}
23572378

2379+
// Step 2. Remove attribute from element’s attribute list.
23582380
self.attrs.borrow_mut().remove(idx);
2381+
// Step 3. Set attribute’s element to null.
23592382
attr.set_owner(None);
2360-
if is_relevant_attribute(attr.namespace(), attr.local_name()) {
2361-
vtable_for(self.upcast()).attribute_mutated(
2362-
&attr,
2363-
AttributeMutation::Removed,
2364-
can_gc,
2365-
);
2366-
}
2383+
// Step 4. Handle attribute changes for attribute with element, attribute’s value, and null.
2384+
self.handle_attribute_changes(&attr, Some(&attr.value()), None, can_gc);
2385+
23672386
attr
23682387
})
23692388
}
@@ -2657,6 +2676,93 @@ impl Element {
26572676
};
26582677
}
26592678

2679+
/// <https://dom.spec.whatwg.org/#concept-element-attributes-set>
2680+
/// including steps of
2681+
/// <https://dom.spec.whatwg.org/#concept-element-attributes-replace>
2682+
fn set_attribute_node(&self, attr: &Attr, can_gc: CanGc) -> Fallible<Option<DomRoot<Attr>>> {
2683+
// Step 1. Let verifiedValue be the result of calling
2684+
// get Trusted Types-compliant attribute value with attr’s local name,
2685+
// attr’s namespace, element, and attr’s value. [TRUSTED-TYPES]
2686+
let verified_value = TrustedTypePolicyFactory::get_trusted_types_compliant_attribute_value(
2687+
self.namespace(),
2688+
self.local_name(),
2689+
attr.local_name(),
2690+
Some(attr.namespace()),
2691+
TrustedTypeOrString::String(attr.Value()),
2692+
&self.owner_global(),
2693+
can_gc,
2694+
)?;
2695+
2696+
// Step 2. If attr’s element is neither null nor element,
2697+
// throw an "InUseAttributeError" DOMException.
2698+
if let Some(owner) = attr.GetOwnerElement() {
2699+
if &*owner != self {
2700+
return Err(Error::InUseAttribute);
2701+
}
2702+
}
2703+
2704+
let vtable = vtable_for(self.upcast());
2705+
2706+
// Step 5. Set attr’s value to verifiedValue.
2707+
//
2708+
// This ensures that the attribute is of the expected kind for this
2709+
// specific element. This is inefficient and should probably be done
2710+
// differently.
2711+
attr.swap_value(
2712+
&mut vtable.parse_plain_attribute(attr.local_name(), verified_value.clone()),
2713+
);
2714+
2715+
// Step 3. Let oldAttr be the result of getting an attribute given attr’s namespace, attr’s local name, and element.
2716+
let position = self.attrs.borrow().iter().position(|old_attr| {
2717+
attr.namespace() == old_attr.namespace() && attr.local_name() == old_attr.local_name()
2718+
});
2719+
2720+
let old_attr = if let Some(position) = position {
2721+
let old_attr = DomRoot::from_ref(&*self.attrs.borrow()[position]);
2722+
2723+
// Step 4. If oldAttr is attr, return attr.
2724+
if &*old_attr == attr {
2725+
return Ok(Some(DomRoot::from_ref(attr)));
2726+
}
2727+
2728+
// Step 6. If oldAttr is non-null, then replace oldAttr with attr.
2729+
//
2730+
// Start of steps for https://dom.spec.whatwg.org/#concept-element-attributes-replace
2731+
2732+
// Step 1. Let element be oldAttribute’s element.
2733+
//
2734+
// Skipped, as that points to self.
2735+
2736+
// Step 2. Replace oldAttribute by newAttribute in element’s attribute list.
2737+
self.attrs.borrow_mut()[position] = Dom::from_ref(attr);
2738+
// Step 3. Set newAttribute’s element to element.
2739+
attr.set_owner(Some(self));
2740+
// Step 4. Set newAttribute’s node document to element’s node document.
2741+
attr.upcast::<Node>().set_owner_doc(&self.node.owner_doc());
2742+
// Step 5. Set oldAttribute’s element to null.
2743+
old_attr.set_owner(None);
2744+
// Step 6. Handle attribute changes for oldAttribute with element, oldAttribute’s value, and newAttribute’s value.
2745+
self.handle_attribute_changes(
2746+
attr,
2747+
Some(&old_attr.value()),
2748+
Some(verified_value),
2749+
can_gc,
2750+
);
2751+
2752+
Some(old_attr)
2753+
} else {
2754+
// Step 7. Otherwise, append attr to element.
2755+
attr.set_owner(Some(self));
2756+
attr.upcast::<Node>().set_owner_doc(&self.node.owner_doc());
2757+
self.push_attribute(attr, can_gc);
2758+
2759+
None
2760+
};
2761+
2762+
// Step 8. Return oldAttr.
2763+
Ok(old_attr)
2764+
}
2765+
26602766
/// <https://html.spec.whatwg.org/multipage/#nonce-attributes>
26612767
pub(crate) fn update_nonce_internal_slot(&self, nonce: String) {
26622768
self.ensure_rare_data().cryptographic_nonce = nonce;
@@ -3241,74 +3347,12 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
32413347

32423348
// https://dom.spec.whatwg.org/#dom-element-setattributenode
32433349
fn SetAttributeNode(&self, attr: &Attr, can_gc: CanGc) -> Fallible<Option<DomRoot<Attr>>> {
3244-
// Step 1.
3245-
if let Some(owner) = attr.GetOwnerElement() {
3246-
if &*owner != self {
3247-
return Err(Error::InUseAttribute);
3248-
}
3249-
}
3250-
3251-
let vtable = vtable_for(self.upcast());
3252-
3253-
// This ensures that the attribute is of the expected kind for this
3254-
// specific element. This is inefficient and should probably be done
3255-
// differently.
3256-
attr.swap_value(&mut vtable.parse_plain_attribute(attr.local_name(), attr.Value()));
3257-
3258-
// Step 2.
3259-
let position = self.attrs.borrow().iter().position(|old_attr| {
3260-
attr.namespace() == old_attr.namespace() && attr.local_name() == old_attr.local_name()
3261-
});
3262-
3263-
if let Some(position) = position {
3264-
let old_attr = DomRoot::from_ref(&*self.attrs.borrow()[position]);
3265-
3266-
// Step 3.
3267-
if &*old_attr == attr {
3268-
return Ok(Some(DomRoot::from_ref(attr)));
3269-
}
3270-
3271-
// Step 4.
3272-
if self.is_custom() {
3273-
let old_name = old_attr.local_name().clone();
3274-
let old_value = DOMString::from(&**old_attr.value());
3275-
let new_value = DOMString::from(&**attr.value());
3276-
let namespace = old_attr.namespace().clone();
3277-
let reaction = CallbackReaction::AttributeChanged(
3278-
old_name,
3279-
Some(old_value),
3280-
Some(new_value),
3281-
namespace,
3282-
);
3283-
ScriptThread::enqueue_callback_reaction(self, reaction, None);
3284-
}
3285-
self.will_mutate_attr(attr);
3286-
attr.set_owner(Some(self));
3287-
self.attrs.borrow_mut()[position] = Dom::from_ref(attr);
3288-
old_attr.set_owner(None);
3289-
if is_relevant_attribute(attr.namespace(), attr.local_name()) {
3290-
vtable.attribute_mutated(
3291-
attr,
3292-
AttributeMutation::Set(Some(&old_attr.value())),
3293-
can_gc,
3294-
);
3295-
}
3296-
3297-
// Step 6.
3298-
Ok(Some(old_attr))
3299-
} else {
3300-
// Step 5.
3301-
attr.set_owner(Some(self));
3302-
self.push_attribute(attr, can_gc);
3303-
3304-
// Step 6.
3305-
Ok(None)
3306-
}
3350+
self.set_attribute_node(attr, can_gc)
33073351
}
33083352

33093353
// https://dom.spec.whatwg.org/#dom-element-setattributenodens
33103354
fn SetAttributeNodeNS(&self, attr: &Attr, can_gc: CanGc) -> Fallible<Option<DomRoot<Attr>>> {
3311-
self.SetAttributeNode(attr, can_gc)
3355+
self.set_attribute_node(attr, can_gc)
33123356
}
33133357

33143358
// https://dom.spec.whatwg.org/#dom-element-removeattribute
@@ -4563,11 +4607,14 @@ impl VirtualMethods for Element {
45634607
},
45644608
&local_name!("style") => self.update_style_attribute(attr, mutation),
45654609
&local_name!("id") => {
4610+
// https://dom.spec.whatwg.org/#ref-for-concept-element-attributes-change-ext%E2%91%A2
45664611
*self.id_attribute.borrow_mut() = mutation.new_value(attr).and_then(|value| {
45674612
let value = value.as_atom();
45684613
if value != &atom!("") {
4614+
// Step 2. Otherwise, if localName is id, namespace is null, then set element’s ID to value.
45694615
Some(value.clone())
45704616
} else {
4617+
// Step 1. If localName is id, namespace is null, and value is null or the empty string, then unset element’s ID.
45714618
None
45724619
}
45734620
});

0 commit comments

Comments
 (0)