diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index d7bd5e995e3..108e9a6b8e7 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -4982,7 +4982,7 @@ impl DocumentMethods for Document { None => return, }; - let elem = if root.namespace() == &ns!(svg) && root.local_name() == &local_name!("svg") { + let node = if root.namespace() == &ns!(svg) && root.local_name() == &local_name!("svg") { let elem = root.upcast::().child_elements().find(|node| { node.namespace() == &ns!(svg) && node.local_name() == &local_name!("title") }); @@ -5036,7 +5036,7 @@ impl DocumentMethods for Document { return; }; - elem.SetTextContent(Some(title), can_gc); + node.set_text_content_for_element(Some(title), can_gc); } // https://html.spec.whatwg.org/multipage/#dom-document-head diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index d2d04d13fd8..4b2e26602cd 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -3876,8 +3876,7 @@ impl ElementMethods for Element { .iter() .any(|c| matches!(*c, b'&' | b'\0' | b'<' | b'\r')) { - Node::SetTextContent(&target, Some(value), can_gc); - return Ok(()); + return Node::SetTextContent(&target, Some(value), can_gc); } // Step 3: Let fragment be the result of invoking the fragment parsing algorithm steps diff --git a/components/script/dom/htmlanchorelement.rs b/components/script/dom/htmlanchorelement.rs index c26d4911154..8763aae9961 100644 --- a/components/script/dom/htmlanchorelement.rs +++ b/components/script/dom/htmlanchorelement.rs @@ -132,7 +132,8 @@ impl HTMLAnchorElementMethods for HTMLAnchorElement { // https://html.spec.whatwg.org/multipage/#dom-a-text fn SetText(&self, value: DOMString, can_gc: CanGc) { - self.upcast::().SetTextContent(Some(value), can_gc) + self.upcast::() + .set_text_content_for_element(Some(value), can_gc) } // https://html.spec.whatwg.org/multipage/#dom-a-rel diff --git a/components/script/dom/htmldetailselement.rs b/components/script/dom/htmldetailselement.rs index 17cdc9f966b..703c37f28b1 100644 --- a/components/script/dom/htmldetailselement.rs +++ b/components/script/dom/htmldetailselement.rs @@ -114,7 +114,7 @@ impl HTMLDetailsElement { HTMLElement::new(local_name!("summary"), None, &document, None, can_gc); fallback_summary .upcast::() - .SetTextContent(Some(DEFAULT_SUMMARY.into()), can_gc); + .set_text_content_for_element(Some(DEFAULT_SUMMARY.into()), can_gc); summary .upcast::() .AppendChild(fallback_summary.upcast::(), can_gc) diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index c1ac1bf7ed2..096377dd198 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -2055,7 +2055,7 @@ impl HTMLMediaElement { *self.media_controls_id.borrow_mut() = Some(id); script .upcast::() - .SetTextContent(Some(DOMString::from(media_controls_script)), can_gc); + .set_text_content_for_element(Some(DOMString::from(media_controls_script)), can_gc); if let Err(e) = shadow_root .upcast::() .AppendChild(script.upcast::(), can_gc) @@ -2074,7 +2074,7 @@ impl HTMLMediaElement { ); style .upcast::() - .SetTextContent(Some(DOMString::from(MEDIA_CONTROL_CSS)), can_gc); + .set_text_content_for_element(Some(DOMString::from(MEDIA_CONTROL_CSS)), can_gc); if let Err(e) = shadow_root .upcast::() diff --git a/components/script/dom/htmloptionelement.rs b/components/script/dom/htmloptionelement.rs index a61564d3c5b..0fe4fc44087 100644 --- a/components/script/dom/htmloptionelement.rs +++ b/components/script/dom/htmloptionelement.rs @@ -176,7 +176,9 @@ impl HTMLOptionElementMethods for HTMLOptionElement { let option = DomRoot::downcast::(element).unwrap(); if !text.is_empty() { - option.upcast::().SetTextContent(Some(text), can_gc) + option + .upcast::() + .set_text_content_for_element(Some(text), can_gc) } if let Some(val) = value { @@ -224,7 +226,8 @@ impl HTMLOptionElementMethods for HTMLOptionElement { /// fn SetText(&self, value: DOMString, can_gc: CanGc) { - self.upcast::().SetTextContent(Some(value), can_gc) + self.upcast::() + .set_text_content_for_element(Some(value), can_gc) } /// diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 8917b8c92bf..a467b005c19 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -1523,7 +1523,8 @@ impl HTMLScriptElementMethods for HTMLScriptElement { // Step 2: Set this's script text value to value. *self.script_text.borrow_mut() = value.clone(); // Step 3: Run set text content with this and value. - self.upcast::().SetTextContent(Some(value), can_gc); + self.upcast::() + .set_text_content_for_element(Some(value), can_gc); Ok(()) } diff --git a/components/script/dom/htmlselectelement.rs b/components/script/dom/htmlselectelement.rs index aa30daeef79..43d9c8e4f12 100644 --- a/components/script/dom/htmlselectelement.rs +++ b/components/script/dom/htmlselectelement.rs @@ -294,7 +294,7 @@ impl HTMLSelectElement { ); chevron_container .upcast::() - .SetTextContent(Some("▾".into()), can_gc); + .set_text_content_for_element(Some("▾".into()), can_gc); select_box .upcast::() .AppendChild(chevron_container.upcast::(), can_gc) diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs index 43b5ec5a0ab..91bcf365fdb 100644 --- a/components/script/dom/htmltextareaelement.rs +++ b/components/script/dom/htmltextareaelement.rs @@ -311,7 +311,8 @@ impl HTMLTextAreaElementMethods for HTMLTextAreaElement { // https://html.spec.whatwg.org/multipage/#dom-textarea-defaultvalue fn SetDefaultValue(&self, value: DOMString, can_gc: CanGc) { - self.upcast::().SetTextContent(Some(value), can_gc); + self.upcast::() + .set_text_content_for_element(Some(value), can_gc); // if the element's dirty value flag is false, then the element's // raw value must be set to the value of the element's textContent IDL attribute diff --git a/components/script/dom/htmltitleelement.rs b/components/script/dom/htmltitleelement.rs index 33ce749ab6d..6a8a2af8f3d 100644 --- a/components/script/dom/htmltitleelement.rs +++ b/components/script/dom/htmltitleelement.rs @@ -9,7 +9,6 @@ use html5ever::{LocalName, Prefix}; use js::rust::HandleObject; use crate::dom::bindings::codegen::Bindings::HTMLTitleElementBinding::HTMLTitleElementMethods; -use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; @@ -71,7 +70,8 @@ impl HTMLTitleElementMethods for HTMLTitleElement { // https://html.spec.whatwg.org/multipage/#dom-title-text fn SetText(&self, value: DOMString, can_gc: CanGc) { - self.upcast::().SetTextContent(Some(value), can_gc) + self.upcast::() + .set_text_content_for_element(Some(value), can_gc) } } diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index e72ba493121..2d91b07850d 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -3067,6 +3067,30 @@ impl Node { DOMString::from(content) } + /// + pub(crate) fn set_text_content_for_element(&self, value: Option, can_gc: CanGc) { + // This should only be called for elements and document fragments when setting the + // text content: https://dom.spec.whatwg.org/#set-text-content + assert!(matches!( + self.type_id(), + NodeTypeId::DocumentFragment(_) | NodeTypeId::Element(..) + )); + let value = value.unwrap_or_default(); + let node = if value.is_empty() { + // Step 1. Let node be null. + None + } else { + // Step 2. If string is not the empty string, then set node to + // a new Text node whose data is string and node document is parent’s node document. + Some(DomRoot::upcast( + self.owner_doc().CreateTextNode(value, can_gc), + )) + }; + + // Step 3. Replace all with node within parent. + Self::replace_all(node.as_deref(), self, can_gc); + } + pub(crate) fn namespace_to_string(namespace: Namespace) -> Option { match namespace { ns!() => None, @@ -3372,34 +3396,23 @@ impl NodeMethods for Node { } } - /// - fn SetTextContent(&self, value: Option, can_gc: CanGc) { - let value = value.unwrap_or_default(); + /// + fn SetTextContent(&self, value: Option, can_gc: CanGc) -> Fallible<()> { match self.type_id() { NodeTypeId::DocumentFragment(_) | NodeTypeId::Element(..) => { - // Step 1-2. - let node = if value.is_empty() { - None - } else { - Some(DomRoot::upcast( - self.owner_doc().CreateTextNode(value, can_gc), - )) - }; - - // Step 3. - Node::replace_all(node.as_deref(), self, can_gc); + self.set_text_content_for_element(value, can_gc); }, NodeTypeId::Attr => { let attr = self.downcast::().unwrap(); - // TODO(#36258): Propagate failure to callers - let _ = attr.SetValue(value, can_gc); + attr.SetValue(value.unwrap_or_default(), can_gc)?; }, NodeTypeId::CharacterData(..) => { let characterdata = self.downcast::().unwrap(); - characterdata.SetData(value); + characterdata.SetData(value.unwrap_or_default()); }, NodeTypeId::DocumentType | NodeTypeId::Document(_) => {}, - } + }; + Ok(()) } /// diff --git a/components/script_bindings/webidls/Node.webidl b/components/script_bindings/webidls/Node.webidl index 8d0afd4a610..1ab4329cad9 100644 --- a/components/script_bindings/webidls/Node.webidl +++ b/components/script_bindings/webidls/Node.webidl @@ -56,7 +56,7 @@ interface Node : EventTarget { [CEReactions, Pure, SetterThrows] attribute DOMString? nodeValue; - [CEReactions, Pure] + [CEReactions, Pure, SetterThrows] attribute DOMString? textContent; [CEReactions] undefined normalize(); diff --git a/tests/wpt/meta/trusted-types/block-string-assignment-to-attribute-via-attribute-node.html.ini b/tests/wpt/meta/trusted-types/block-string-assignment-to-attribute-via-attribute-node.html.ini deleted file mode 100644 index 929ff98e38a..00000000000 --- a/tests/wpt/meta/trusted-types/block-string-assignment-to-attribute-via-attribute-node.html.ini +++ /dev/null @@ -1,9 +0,0 @@ -[block-string-assignment-to-attribute-via-attribute-node.html] - [Set script.src via textContent] - expected: FAIL - - [Set iframe.srcdoc via textContent] - expected: FAIL - - [Set div.onclick via textContent] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/set-attributes-require-trusted-types-no-default-policy.html.ini b/tests/wpt/meta/trusted-types/set-attributes-require-trusted-types-no-default-policy.html.ini deleted file mode 100644 index 64383823f40..00000000000 --- a/tests/wpt/meta/trusted-types/set-attributes-require-trusted-types-no-default-policy.html.ini +++ /dev/null @@ -1,42 +0,0 @@ -[set-attributes-require-trusted-types-no-default-policy.html] - [Node.textContent throws for elementNS=http://www.w3.org/1999/xhtml, element=DIV, attrName=onclick with a plain string] - expected: FAIL - - [Node.textContent throws for elementNS=http://www.w3.org/2000/svg, element=g, attrName=ondblclick with a plain string] - expected: FAIL - - [Node.textContent throws for elementNS=http://www.w3.org/1998/Math/MathML, element=mrow, attrName=onmousedown with a plain string] - expected: FAIL - - [Node.textContent throws for elementNS=http://www.w3.org/1999/xhtml, element=IFRAME, attrName=srcdoc with a plain string] - expected: FAIL - - [Node.textContent throws for elementNS=http://www.w3.org/1999/xhtml, element=SCRIPT, attrName=src with a plain string] - expected: FAIL - - [Node.textContent throws for elementNS=http://www.w3.org/2000/svg, element=script, attrName=href with a plain string] - expected: FAIL - - [Node.textContent throws for elementNS=http://www.w3.org/2000/svg, element=script, attrNS=http://www.w3.org/1999/xlink, attrName=href with a plain string] - expected: FAIL - - [Node.textContent throws for elementNS=http://www.w3.org/1999/xhtml, element=DIV, attrName=onclick with a TrustedScript input.] - expected: FAIL - - [Node.textContent throws for elementNS=http://www.w3.org/2000/svg, element=g, attrName=ondblclick with a TrustedScript input.] - expected: FAIL - - [Node.textContent throws for elementNS=http://www.w3.org/1998/Math/MathML, element=mrow, attrName=onmousedown with a TrustedScript input.] - expected: FAIL - - [Node.textContent throws for elementNS=http://www.w3.org/1999/xhtml, element=IFRAME, attrName=srcdoc with a TrustedHTML input.] - expected: FAIL - - [Node.textContent throws for elementNS=http://www.w3.org/1999/xhtml, element=SCRIPT, attrName=src with a TrustedScriptURL input.] - expected: FAIL - - [Node.textContent throws for elementNS=http://www.w3.org/2000/svg, element=script, attrName=href with a TrustedScriptURL input.] - expected: FAIL - - [Node.textContent throws for elementNS=http://www.w3.org/2000/svg, element=script, attrNS=http://www.w3.org/1999/xlink, attrName=href with a TrustedScriptURL input.] - expected: FAIL