1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 19:37:34 +00:00

LibWeb: Make Node.textContent more spec compliant

The current implementation felt a bit ad-hoc and notably allowed
textContent to operate on all node types. It also only returned the
child text content of the Node instead of the descendant text content.
This commit is contained in:
Luke Wilde 2021-09-06 00:21:59 +01:00 committed by Andreas Kling
parent d36838d050
commit 5e61382849
4 changed files with 40 additions and 14 deletions

View file

@ -25,8 +25,6 @@ public:
unsigned length() const { return m_data.length(); }
virtual String text_content() const override { return m_data; }
protected:
explicit CharacterData(Document&, NodeType, const String&);

View file

@ -98,22 +98,45 @@ const HTML::HTMLElement* Node::enclosing_html_element_with_attribute(const FlySt
return nullptr;
}
String Node::text_content() const
// https://dom.spec.whatwg.org/#concept-descendant-text-content
String Node::descendant_text_content() const
{
StringBuilder builder;
for (auto* child = first_child(); child; child = child->next_sibling()) {
builder.append(child->text_content());
}
for_each_in_subtree_of_type<Text>([&](auto& text_node) {
builder.append(text_node.data());
return IterationDecision::Continue;
});
return builder.to_string();
}
void Node::set_text_content(const String& content)
// https://dom.spec.whatwg.org/#dom-node-textcontent
String Node::text_content() const
{
if (is_text()) {
verify_cast<Text>(this)->set_data(content);
if (is<DocumentFragment>(this) || is<Element>(this))
return descendant_text_content();
else if (is<CharacterData>(this))
return verify_cast<CharacterData>(this)->data();
// FIXME: Else if this is an Attr node, return this's value.
return {};
}
// https://dom.spec.whatwg.org/#ref-for-dom-node-textcontent%E2%91%A0
void Node::set_text_content(String const& content)
{
if (is<DocumentFragment>(this) || is<Element>(this)) {
string_replace_all(content);
} else if (is<CharacterData>(this)) {
// FIXME: CharacterData::set_data is not spec compliant. Make this match the spec when set_data becomes spec compliant.
// Do note that this will make this function able to throw an exception.
auto* character_data_node = verify_cast<CharacterData>(this);
character_data_node->set_data(content);
} else {
remove_all_children();
append_child(document().create_text_node(content));
// FIXME: Else if this is an Attr node, set an existing attribute value with this and the given value.
return;
}
set_needs_style_update(true);

View file

@ -99,8 +99,9 @@ public:
virtual FlyString node_name() const = 0;
virtual String text_content() const;
void set_text_content(const String&);
String descendant_text_content() const;
String text_content() const;
void set_text_content(String const&);
Document& document() { return *m_document; }
const Document& document() const { return *m_document; }

View file

@ -14,7 +14,11 @@ interface Node : EventTarget {
readonly attribute Element? parentElement;
readonly attribute boolean isConnected;
readonly attribute Document? ownerDocument;
attribute DOMString textContent;
// FIXME: [LegacyNullToEmptyString] is not allowed on nullable types as per the Web IDL spec.
// However, we only apply it to setters, so this works as a stop gap.
// Replace this with something like a special cased [LegacyNullToEmptyString].
[LegacyNullToEmptyString] attribute DOMString? textContent;
Node appendChild(Node node);
[ImplementedAs=pre_insert] Node insertBefore(Node node, Node? child);