LibWeb: Stop crashing when textarea element is modified before insertion

If an element is created from JS, it might have its contents modified
before it is inserted into the document. In this case, we don't have a
shadow tree yet and so trying to set m_text_node's text content would
cause a null dereference. So let's not do that. :^)

That case also means that by the time we do create the shadow tree, we
have the text content already, so we can set it there.

Added a test to verify that we don't crash, and that the text content
appears in the textarea whether it was inserted by JS or by the HTML
parser.
This commit is contained in:
Sam Atkins 2023-09-09 11:40:43 +01:00 committed by Sam Atkins
parent 883f44d397
commit b1a569c1c5
3 changed files with 52 additions and 3 deletions

View file

@ -0,0 +1,38 @@
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
BlockContainer <html> at (0,0) content-size 800x56.9375 [BFC] children: not-inline
BlockContainer <body> at (8,8) content-size 784x40.9375 children: inline
line 0 width: 403.75, height: 40.9375, bottom: 40.9375, baseline: 13.53125
frag 0 from BlockContainer start: 0, length: 0, rect: [11,11 191.875x34.9375]
frag 1 from TextNode start: 0, length: 1, rect: [206,8 8x17.46875]
" "
frag 2 from BlockContainer start: 0, length: 0, rect: [217,11 191.875x34.9375]
TextNode <#text>
BlockContainer <textarea> at (11,11) content-size 191.875x34.9375 inline-block [BFC] children: not-inline
BlockContainer <div> at (11,11) content-size 191.875x17.46875 children: not-inline
BlockContainer <div> at (11,11) content-size 191.875x17.46875 children: inline
line 0 width: 152.203125, height: 17.46875, bottom: 17.46875, baseline: 13.53125
frag 0 from TextNode start: 0, length: 17, rect: [11,11 152.203125x17.46875]
"Bonjour mon amis!"
TextNode <#text>
TextNode <#text>
BlockContainer <textarea> at (217,11) content-size 191.875x34.9375 inline-block [BFC] children: not-inline
BlockContainer <div> at (217,11) content-size 191.875x17.46875 children: not-inline
BlockContainer <div> at (217,11) content-size 191.875x17.46875 children: inline
line 0 width: 142.140625, height: 17.46875, bottom: 17.46875, baseline: 13.53125
frag 0 from TextNode start: 0, length: 19, rect: [217,11 142.140625x17.46875]
"Well hello friends!"
TextNode <#text>
TextNode <#text>
ViewportPaintable (Viewport<#document>) [0,0 800x600]
PaintableWithLines (BlockContainer<HTML>) [0,0 800x56.9375]
PaintableWithLines (BlockContainer<BODY>) [8,8 784x40.9375]
PaintableWithLines (BlockContainer<TEXTAREA>) [8,8 197.875x40.9375]
PaintableWithLines (BlockContainer<DIV>) [11,11 191.875x17.46875]
PaintableWithLines (BlockContainer<DIV>) [11,11 191.875x17.46875]
TextPaintable (TextNode<#text>)
TextPaintable (TextNode<#text>)
PaintableWithLines (BlockContainer<TEXTAREA>) [214,8 197.875x40.9375]
PaintableWithLines (BlockContainer<DIV>) [217,11 191.875x17.46875]
PaintableWithLines (BlockContainer<DIV>) [217,11 191.875x17.46875]
TextPaintable (TextNode<#text>)

View file

@ -0,0 +1,9 @@
<!doctype html>
<body>
<textarea>Bonjour mon amis!</textarea>
<script>
const textarea = document.createElement("textarea");
textarea.innerText = "Well hello friends!";
document.body.appendChild(textarea);
</script>
</body>

View file

@ -101,11 +101,12 @@ void HTMLTextAreaElement::create_shadow_tree_if_needed()
m_inner_text_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
// NOTE: The text content of the <textarea> element is not available to us yet.
// It gets filled in by `children_changed()`.
m_text_node = heap().allocate<DOM::Text>(realm(), document(), String {});
m_text_node->set_always_editable(true);
m_text_node->set_editable_text_node_owner(Badge<HTMLTextAreaElement> {}, *this);
// NOTE: If `children_changed()` was called before now, `m_raw_value` will hold the text content.
// Otherwise, it will get filled in whenever that does get called.
m_text_node->set_text_content(m_raw_value);
MUST(m_inner_text_element->append_child(*m_text_node));
MUST(element->append_child(*m_inner_text_element));
@ -120,7 +121,8 @@ void HTMLTextAreaElement::children_changed()
// set the element's raw value to its child text content.
if (!m_dirty) {
m_raw_value = child_text_content();
m_text_node->set_text_content(m_raw_value);
if (m_text_node)
m_text_node->set_text_content(m_raw_value);
}
}