aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorSimon Wülker <simon.wuelker@arcor.de>2024-11-22 18:07:01 +0100
committerGitHub <noreply@github.com>2024-11-22 17:07:01 +0000
commit1198b26ec99af88210090fb958233181f83a0ba0 (patch)
tree2d6c60380b47c214538d70444511b0a9bfee7864 /components
parent44ed111c0adec7e4ebaadde2baaf44185ccc72ef (diff)
downloadservo-1198b26ec99af88210090fb958233181f83a0ba0.tar.gz
servo-1198b26ec99af88210090fb958233181f83a0ba0.zip
Implement `ShadowRoot.innerHtml` attribute (#34335)
* Implement DocumentFragment::fragment_serialization_algorithm Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Implement ShadowRoot innerHtml attribute Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Update WPT expectations Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * cargo-clippy Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Reuse existing serialization code and move helpers into Node Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> * Fix typo Signed-off-by: Simon Wülker <simon.wuelker@arcor.de> --------- Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
Diffstat (limited to 'components')
-rw-r--r--components/script/dom/bindings/codegen/Bindings.conf2
-rw-r--r--components/script/dom/element.rs68
-rw-r--r--components/script/dom/node.rs49
-rw-r--r--components/script/dom/shadowroot.rs29
-rw-r--r--components/script/dom/webidls/Element.webidl6
-rw-r--r--components/script/dom/webidls/ShadowRoot.webidl9
6 files changed, 110 insertions, 53 deletions
diff --git a/components/script/dom/bindings/codegen/Bindings.conf b/components/script/dom/bindings/codegen/Bindings.conf
index 27bbc202f22..48d9392465f 100644
--- a/components/script/dom/bindings/codegen/Bindings.conf
+++ b/components/script/dom/bindings/codegen/Bindings.conf
@@ -420,7 +420,7 @@ DOMInterfaces = {
},
'ShadowRoot': {
- 'canGc': ['ElementFromPoint', 'ElementsFromPoint'],
+ 'canGc': ['ElementFromPoint', 'ElementsFromPoint', 'SetInnerHTML'],
},
'StaticRange': {
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index 1c6d22c979a..41aec11ca90 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -18,10 +18,8 @@ use dom_struct::dom_struct;
use embedder_traits::InputMethodType;
use euclid::default::{Rect, Size2D};
use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode};
-use html5ever::serialize::{SerializeOpts, TraversalScope};
use html5ever::{
- local_name, namespace_prefix, namespace_url, ns, serialize, LocalName, Namespace, Prefix,
- QualName,
+ local_name, namespace_prefix, namespace_url, ns, LocalName, Namespace, Prefix, QualName,
};
use js::jsapi::Heap;
use js::jsval::JSVal;
@@ -61,11 +59,9 @@ use style::values::generics::NonNegative;
use style::values::{computed, specified, AtomIdent, AtomString, CSSFloat};
use style::{dom_apis, thread_state, ArcSlice, CaseSensitivityExt};
use style_dom::ElementState;
-use xml5ever::serialize as xmlSerialize;
use xml5ever::serialize::TraversalScope::{
ChildrenOnly as XmlChildrenOnly, IncludeNode as XmlIncludeNode,
};
-use xml5ever::serialize::{SerializeOpts as XmlSerializeOpts, TraversalScope as XmlTraversalScope};
use super::htmltablecolelement::{HTMLTableColElement, HTMLTableColElementLayoutHelpers};
use crate::dom::activation::Activatable;
@@ -1327,35 +1323,6 @@ impl Element {
}
}
- pub fn serialize(&self, traversal_scope: TraversalScope) -> Fallible<DOMString> {
- let mut writer = vec![];
- match serialize(
- &mut writer,
- &self.upcast::<Node>(),
- SerializeOpts {
- traversal_scope,
- ..Default::default()
- },
- ) {
- // FIXME(ajeffrey): Directly convert UTF8 to DOMString
- Ok(()) => Ok(DOMString::from(String::from_utf8(writer).unwrap())),
- Err(_) => panic!("Cannot serialize element"),
- }
- }
-
- #[allow(non_snake_case)]
- pub fn xmlSerialize(&self, traversal_scope: XmlTraversalScope) -> Fallible<DOMString> {
- let mut writer = vec![];
- match xmlSerialize::serialize(
- &mut writer,
- &self.upcast::<Node>(),
- XmlSerializeOpts { traversal_scope },
- ) {
- Ok(()) => Ok(DOMString::from(String::from_utf8(writer).unwrap())),
- Err(_) => panic!("Cannot serialize element"),
- }
- }
-
pub fn root_element(&self) -> DomRoot<Element> {
if self.node.is_in_doc() {
self.upcast::<Node>()
@@ -1959,7 +1926,7 @@ impl Element {
win.scroll_node(node, x, y, behavior, can_gc);
}
- // https://w3c.github.io/DOM-Parsing/#parsing
+ /// <https://html.spec.whatwg.org/multipage/#fragment-parsing-algorithm-steps>
pub fn parse_fragment(
&self,
markup: DOMString,
@@ -2743,21 +2710,26 @@ impl ElementMethods for Element {
self.client_rect(can_gc).size.height
}
- /// <https://w3c.github.io/DOM-Parsing/#widl-Element-innerHTML>
+ /// <https://html.spec.whatwg.org/multipage/#dom-element-innerhtml>
fn GetInnerHTML(&self) -> Fallible<DOMString> {
let qname = QualName::new(
self.prefix().clone(),
self.namespace().clone(),
self.local_name().clone(),
);
- if document_from_node(self).is_html_document() {
- self.serialize(ChildrenOnly(Some(qname)))
+
+ let result = if document_from_node(self).is_html_document() {
+ self.upcast::<Node>()
+ .html_serialize(ChildrenOnly(Some(qname)))
} else {
- self.xmlSerialize(XmlChildrenOnly(Some(qname)))
- }
+ self.upcast::<Node>()
+ .xml_serialize(XmlChildrenOnly(Some(qname)))
+ };
+
+ Ok(result)
}
- /// <https://w3c.github.io/DOM-Parsing/#widl-Element-innerHTML>
+ /// <https://html.spec.whatwg.org/multipage/#dom-element-innerhtml>
fn SetInnerHTML(&self, value: DOMString, can_gc: CanGc) -> ErrorResult {
// Step 2.
// https://github.com/w3c/DOM-Parsing/issues/1
@@ -2787,16 +2759,18 @@ impl ElementMethods for Element {
Ok(())
}
- // https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#widl-Element-outerHTML
+ /// <https://html.spec.whatwg.org/multipage/#dom-element-outerhtml>
fn GetOuterHTML(&self) -> Fallible<DOMString> {
- if document_from_node(self).is_html_document() {
- self.serialize(IncludeNode)
+ let result = if document_from_node(self).is_html_document() {
+ self.upcast::<Node>().html_serialize(IncludeNode)
} else {
- self.xmlSerialize(XmlIncludeNode)
- }
+ self.upcast::<Node>().xml_serialize(XmlIncludeNode)
+ };
+
+ Ok(result)
}
- // https://w3c.github.io/DOM-Parsing/#dom-element-outerhtml
+ /// <https://html.spec.whatwg.org/multipage/#dom-element-outerhtml>
fn SetOuterHTML(&self, value: DOMString, can_gc: CanGc) -> ErrorResult {
let context_document = document_from_node(self);
let context_node = self.upcast::<Node>();
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs
index 3267204fda4..a102682d8c6 100644
--- a/components/script/dom/node.rs
+++ b/components/script/dom/node.rs
@@ -18,7 +18,7 @@ use bitflags::bitflags;
use devtools_traits::NodeInfo;
use dom_struct::dom_struct;
use euclid::default::{Rect, Size2D, Vector2D};
-use html5ever::{namespace_url, ns, Namespace, Prefix, QualName};
+use html5ever::{namespace_url, ns, serialize as html_serialize, Namespace, Prefix, QualName};
use js::jsapi::JSObject;
use js::rust::HandleObject;
use libc::{self, c_void, uintptr_t};
@@ -43,6 +43,7 @@ use style::properties::ComputedValues;
use style::selector_parser::{SelectorImpl, SelectorParser};
use style::stylesheets::{Stylesheet, UrlExtraData};
use uuid::Uuid;
+use xml5ever::serialize as xml_serialize;
use crate::document_loader::DocumentLoader;
use crate::dom::attr::Attr;
@@ -2448,6 +2449,52 @@ impl Node {
}
&*(conversions::private_from_object(object) as *const Self)
}
+
+ pub fn html_serialize(&self, traversal_scope: html_serialize::TraversalScope) -> DOMString {
+ let mut writer = vec![];
+ html_serialize::serialize(
+ &mut writer,
+ &self,
+ html_serialize::SerializeOpts {
+ traversal_scope,
+ ..Default::default()
+ },
+ )
+ .expect("Cannot serialize node");
+
+ // FIXME(ajeffrey): Directly convert UTF8 to DOMString
+ DOMString::from(String::from_utf8(writer).unwrap())
+ }
+
+ pub fn xml_serialize(&self, traversal_scope: xml_serialize::TraversalScope) -> DOMString {
+ let mut writer = vec![];
+ xml_serialize::serialize(
+ &mut writer,
+ &self,
+ xml_serialize::SerializeOpts { traversal_scope },
+ )
+ .expect("Cannot serialize node");
+
+ // FIXME(ajeffrey): Directly convert UTF8 to DOMString
+ DOMString::from(String::from_utf8(writer).unwrap())
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#fragment-serializing-algorithm-steps>
+ pub fn fragment_serialization_algorithm(&self, require_well_formed: bool) -> DOMString {
+ // Step 1. Let context document be node's node document.
+ let context_document = document_from_node(self);
+
+ // Step 2. If context document is an HTML document, return the result of HTML fragment serialization algorithm
+ // with node, false, and « ».
+ if context_document.is_html_document() {
+ return self.html_serialize(html_serialize::TraversalScope::ChildrenOnly(None));
+ }
+
+ // Step 3. Return the XML serialization of node given require well-formed.
+ // TODO: xml5ever doesn't seem to want require_well_formed
+ let _ = require_well_formed;
+ self.xml_serialize(xml_serialize::TraversalScope::ChildrenOnly(None))
+ }
}
impl NodeMethods for Node {
diff --git a/components/script/dom/shadowroot.rs b/components/script/dom/shadowroot.rs
index 22967bb5ad2..d7810806864 100644
--- a/components/script/dom/shadowroot.rs
+++ b/components/script/dom/shadowroot.rs
@@ -18,6 +18,7 @@ use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::num::Finite;
use crate::dom::bindings::reflector::reflect_dom_object;
use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
+use crate::dom::bindings::str::DOMString;
use crate::dom::cssstylesheet::CSSStyleSheet;
use crate::dom::document::Document;
use crate::dom::documentfragment::DocumentFragment;
@@ -249,6 +250,34 @@ impl ShadowRootMethods for ShadowRoot {
)
})
}
+
+ /// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-innerhtml>
+ fn InnerHTML(&self) -> DOMString {
+ // ShadowRoot's innerHTML getter steps are to return the result of running fragment serializing
+ // algorithm steps with this and true.
+ self.upcast::<Node>().fragment_serialization_algorithm(true)
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#dom-shadowroot-innerhtml>
+ fn SetInnerHTML(&self, value: DOMString, can_gc: CanGc) {
+ // TODO Step 1. Let compliantString be the result of invoking the Get Trusted Type compliant string algorithm
+ // with TrustedHTML, this's relevant global object, the given value, "ShadowRoot innerHTML", and "script".
+ let compliant_string = value;
+
+ // Step 2. Let context be this's host.
+ let context = self.Host();
+
+ // Step 3. Let fragment be the result of invoking the fragment parsing algorithm steps with context and
+ // compliantString.
+ let Ok(frag) = context.parse_fragment(compliant_string, can_gc) else {
+ // NOTE: The spec doesn't strictly tell us to bail out here, but
+ // we can't continue if parsing failed
+ return;
+ };
+
+ // Step 4. Replace all with fragment within this.
+ Node::replace_all(Some(frag.upcast()), self.upcast());
+ }
}
#[allow(unsafe_code)]
diff --git a/components/script/dom/webidls/Element.webidl b/components/script/dom/webidls/Element.webidl
index 4525e4ae9c8..99d17117221 100644
--- a/components/script/dom/webidls/Element.webidl
+++ b/components/script/dom/webidls/Element.webidl
@@ -121,10 +121,8 @@ partial interface Element {
// https://w3c.github.io/DOM-Parsing/#extensions-to-the-element-interface
partial interface Element {
- [CEReactions, Throws]
- attribute [LegacyNullToEmptyString] DOMString innerHTML;
- [CEReactions, Throws]
- attribute [LegacyNullToEmptyString] DOMString outerHTML;
+ [CEReactions, Throws] attribute [LegacyNullToEmptyString] DOMString innerHTML;
+ [CEReactions, Throws] attribute [LegacyNullToEmptyString] DOMString outerHTML;
};
// https://fullscreen.spec.whatwg.org/#api
diff --git a/components/script/dom/webidls/ShadowRoot.webidl b/components/script/dom/webidls/ShadowRoot.webidl
index c8f3e754016..a1ecdc5c10f 100644
--- a/components/script/dom/webidls/ShadowRoot.webidl
+++ b/components/script/dom/webidls/ShadowRoot.webidl
@@ -16,3 +16,12 @@ enum ShadowRootMode { "open", "closed"};
// enum SlotAssignmentMode { "manual", "named" };
ShadowRoot includes DocumentOrShadowRoot;
+
+// https://html.spec.whatwg.org/multipage/#dom-parsing-and-serialization
+partial interface ShadowRoot {
+ // [CEReactions] undefined setHTMLUnsafe((TrustedHTML or DOMString) html);
+ // DOMString getHTML(optional GetHTMLOptions options = {});
+
+ // [CEReactions] attribute (TrustedHTML or [LegacyNullToEmptyString] DOMString) innerHTML;
+ [CEReactions] attribute [LegacyNullToEmptyString] DOMString innerHTML;
+};