2020-01-18 09:38:21 +01:00
|
|
|
|
/*
|
2024-10-04 13:19:50 +02:00
|
|
|
|
* Copyright (c) 2018-2022, Andreas Kling <andreas@ladybird.org>
|
2025-05-14 12:56:03 +02:00
|
|
|
|
* Copyright (c) 2025, Jelle Raaijmakers <jelle@ladybird.org>
|
2020-01-18 09:38:21 +01:00
|
|
|
|
*
|
2021-04-22 01:24:48 -07:00
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-01-18 09:38:21 +01:00
|
|
|
|
*/
|
|
|
|
|
|
2024-06-23 09:14:27 -04:00
|
|
|
|
#include <LibUnicode/Segmenter.h>
|
2022-08-28 13:42:07 +02:00
|
|
|
|
#include <LibWeb/Bindings/CharacterDataPrototype.h>
|
2020-03-07 10:32:51 +01:00
|
|
|
|
#include <LibWeb/DOM/CharacterData.h>
|
2021-02-10 18:32:16 +01:00
|
|
|
|
#include <LibWeb/DOM/Document.h>
|
2022-07-11 16:39:32 +01:00
|
|
|
|
#include <LibWeb/DOM/MutationType.h>
|
2022-03-21 20:05:25 +01:00
|
|
|
|
#include <LibWeb/DOM/Range.h>
|
2023-08-16 11:03:00 +02:00
|
|
|
|
#include <LibWeb/Layout/TextNode.h>
|
2019-10-12 23:26:47 +02:00
|
|
|
|
|
2020-07-26 19:37:56 +02:00
|
|
|
|
namespace Web::DOM {
|
2020-03-07 10:27:02 +01:00
|
|
|
|
|
2024-11-15 04:01:23 +13:00
|
|
|
|
GC_DEFINE_ALLOCATOR(CharacterData);
|
2023-11-19 19:47:52 +01:00
|
|
|
|
|
2025-07-24 12:05:52 -04:00
|
|
|
|
CharacterData::CharacterData(Document& document, NodeType type, Utf16String data)
|
2019-10-12 23:26:47 +02:00
|
|
|
|
: Node(document, type)
|
2025-07-24 12:05:52 -04:00
|
|
|
|
, m_data(move(data))
|
2019-10-12 23:26:47 +02:00
|
|
|
|
{
|
2023-01-10 06:28:20 -05:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-19 09:02:21 -04:00
|
|
|
|
CharacterData::~CharacterData() = default;
|
|
|
|
|
|
2023-08-07 08:41:28 +02:00
|
|
|
|
void CharacterData::initialize(JS::Realm& realm)
|
2023-01-10 06:28:20 -05:00
|
|
|
|
{
|
2024-03-16 13:13:08 +01:00
|
|
|
|
WEB_SET_PROTOTYPE_FOR_INTERFACE(CharacterData);
|
2025-04-20 16:22:57 +02:00
|
|
|
|
Base::initialize(realm);
|
2019-10-12 23:26:47 +02:00
|
|
|
|
}
|
|
|
|
|
|
2022-07-11 16:13:16 +01:00
|
|
|
|
// https://dom.spec.whatwg.org/#dom-characterdata-data
|
2025-07-24 12:05:52 -04:00
|
|
|
|
void CharacterData::set_data(Utf16String const& data)
|
2021-02-10 18:32:16 +01:00
|
|
|
|
{
|
2022-07-11 16:13:16 +01:00
|
|
|
|
// [The data] setter must replace data with node this, offset 0, count this’s length, and data new value.
|
|
|
|
|
// NOTE: Since the offset is 0, it can never be above data's length, so this can never throw.
|
|
|
|
|
// NOTE: Setting the data to the same value as the current data still causes a mutation observer callback.
|
|
|
|
|
// FIXME: Figure out a way to make this a no-op again if the passed in data is the same as the current data.
|
2023-12-22 20:41:34 +13:00
|
|
|
|
MUST(replace_data(0, this->length_in_utf16_code_units(), data));
|
2021-02-10 18:32:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-21 17:20:42 +01:00
|
|
|
|
// https://dom.spec.whatwg.org/#concept-cd-substring
|
2025-07-24 12:05:52 -04:00
|
|
|
|
WebIDL::ExceptionOr<Utf16String> CharacterData::substring_data(size_t offset, size_t count) const
|
2022-03-21 17:20:42 +01:00
|
|
|
|
{
|
|
|
|
|
// 1. Let length be node’s length.
|
2025-07-24 12:05:52 -04:00
|
|
|
|
auto length = m_data.length_in_code_units();
|
2022-03-21 17:20:42 +01:00
|
|
|
|
|
|
|
|
|
// 2. If offset is greater than length, then throw an "IndexSizeError" DOMException.
|
|
|
|
|
if (offset > length)
|
2025-08-07 19:31:52 -04:00
|
|
|
|
return WebIDL::IndexSizeError::create(realm(), "Substring offset out of range."_utf16);
|
2022-03-21 17:20:42 +01:00
|
|
|
|
|
|
|
|
|
// 3. If offset plus count is greater than length, return a string whose value is the code units from the offsetth code unit
|
|
|
|
|
// to the end of node’s data, and then return.
|
|
|
|
|
if (offset + count > length)
|
2025-08-08 16:32:26 -04:00
|
|
|
|
return Utf16String::from_utf16(m_data.substring_view(offset));
|
2022-03-21 17:20:42 +01:00
|
|
|
|
|
|
|
|
|
// 4. Return a string whose value is the code units from the offsetth code unit to the offset+countth code unit in node’s data.
|
2025-08-08 16:32:26 -04:00
|
|
|
|
return Utf16String::from_utf16(m_data.substring_view(offset, count));
|
2022-03-21 17:20:42 +01:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-21 18:05:20 +01:00
|
|
|
|
// https://dom.spec.whatwg.org/#concept-cd-replace
|
2025-07-24 12:05:52 -04:00
|
|
|
|
WebIDL::ExceptionOr<void> CharacterData::replace_data(size_t offset, size_t count, Utf16View const& data)
|
2022-03-21 18:05:20 +01:00
|
|
|
|
{
|
|
|
|
|
// 1. Let length be node’s length.
|
2025-07-24 12:05:52 -04:00
|
|
|
|
auto length = m_data.length_in_code_units();
|
2022-03-21 18:05:20 +01:00
|
|
|
|
|
|
|
|
|
// 2. If offset is greater than length, then throw an "IndexSizeError" DOMException.
|
|
|
|
|
if (offset > length)
|
2025-08-07 19:31:52 -04:00
|
|
|
|
return WebIDL::IndexSizeError::create(realm(), "Replacement offset out of range."_utf16);
|
2022-03-21 18:05:20 +01:00
|
|
|
|
|
|
|
|
|
// 3. If offset plus count is greater than length, then set count to length minus offset.
|
|
|
|
|
if (offset + count > length)
|
|
|
|
|
count = length - offset;
|
|
|
|
|
|
|
|
|
|
// 5. Insert data into node’s data after offset code units.
|
|
|
|
|
// 6. Let delete offset be offset + data’s length.
|
|
|
|
|
// 7. Starting from delete offset code units, remove count code units from node’s data.
|
2025-07-24 12:05:52 -04:00
|
|
|
|
auto before_data = m_data.substring_view(0, offset);
|
|
|
|
|
auto after_data = m_data.substring_view(offset + count);
|
2025-06-26 12:52:23 -04:00
|
|
|
|
|
2025-07-24 12:05:52 -04:00
|
|
|
|
StringBuilder full_data(StringBuilder::Mode::UTF16, before_data.length_in_code_units() + data.length_in_code_units() + after_data.length_in_code_units());
|
2025-06-17 16:08:30 -04:00
|
|
|
|
full_data.append(before_data);
|
2025-07-24 12:05:52 -04:00
|
|
|
|
full_data.append(data);
|
2025-06-17 16:08:30 -04:00
|
|
|
|
full_data.append(after_data);
|
2025-02-19 16:05:43 +01:00
|
|
|
|
|
2025-03-06 17:17:20 -05:00
|
|
|
|
auto old_data = m_data;
|
2025-07-24 12:05:52 -04:00
|
|
|
|
m_data = full_data.to_utf16_string();
|
2022-03-21 18:05:20 +01:00
|
|
|
|
|
2025-03-06 17:17:20 -05:00
|
|
|
|
// 4. Queue a mutation record of "characterData" for node with null, null, node’s data, « », « », null, and null.
|
|
|
|
|
// NOTE: We do this later so that the mutation observer may notify UI clients of this node's new value.
|
2025-07-24 12:05:52 -04:00
|
|
|
|
queue_mutation_record(MutationType::characterData, {}, {}, old_data.to_utf8_but_should_be_ported_to_utf16(), {}, {}, nullptr, nullptr);
|
2025-03-06 17:17:20 -05:00
|
|
|
|
|
2025-05-14 12:56:03 +02:00
|
|
|
|
// 8. For each live range whose start node is node and start offset is greater than offset but less than or equal to
|
|
|
|
|
// offset plus count, set its start offset to offset.
|
|
|
|
|
for (auto* range : Range::live_ranges()) {
|
2022-03-21 20:05:25 +01:00
|
|
|
|
if (range->start_container() == this && range->start_offset() > offset && range->start_offset() <= (offset + count))
|
2025-05-14 12:56:03 +02:00
|
|
|
|
range->set_start_offset(offset);
|
2022-03-21 20:05:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2025-05-14 12:56:03 +02:00
|
|
|
|
// 9. For each live range whose end node is node and end offset is greater than offset but less than or equal to
|
|
|
|
|
// offset plus count, set its end offset to offset.
|
|
|
|
|
for (auto* range : Range::live_ranges()) {
|
2022-03-21 20:05:25 +01:00
|
|
|
|
if (range->end_container() == this && range->end_offset() > offset && range->end_offset() <= (offset + count))
|
2025-05-14 12:56:03 +02:00
|
|
|
|
range->set_end_offset(offset);
|
2022-03-21 20:05:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2025-05-14 12:56:03 +02:00
|
|
|
|
// 10. For each live range whose start node is node and start offset is greater than offset plus count, increase its
|
|
|
|
|
// start offset by data’s length and decrease it by count.
|
|
|
|
|
for (auto* range : Range::live_ranges()) {
|
2022-03-21 20:05:25 +01:00
|
|
|
|
if (range->start_container() == this && range->start_offset() > (offset + count))
|
2025-07-24 12:05:52 -04:00
|
|
|
|
range->set_start_offset(range->start_offset() + data.length_in_code_units() - count);
|
2022-03-21 20:05:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2025-05-14 12:56:03 +02:00
|
|
|
|
// 11. For each live range whose end node is node and end offset is greater than offset plus count, increase its end
|
|
|
|
|
// offset by data’s length and decrease it by count.
|
|
|
|
|
for (auto* range : Range::live_ranges()) {
|
|
|
|
|
if (range->end_container() == this && range->end_offset() > (offset + count))
|
2025-07-24 12:05:52 -04:00
|
|
|
|
range->set_end_offset(range->end_offset() + data.length_in_code_units() - count);
|
2022-03-21 20:05:25 +01:00
|
|
|
|
}
|
2022-03-21 18:05:20 +01:00
|
|
|
|
|
|
|
|
|
// 12. If node’s parent is non-null, then run the children changed steps for node’s parent.
|
|
|
|
|
if (parent())
|
2025-01-27 01:16:33 +13:00
|
|
|
|
parent()->children_changed(nullptr);
|
2022-03-21 18:05:20 +01:00
|
|
|
|
|
2025-02-21 11:05:59 +01:00
|
|
|
|
// OPTIMIZATION: If the characters are the same, we can skip the remainder of this function.
|
2025-07-24 12:05:52 -04:00
|
|
|
|
if (m_data == old_data)
|
2025-02-21 11:05:59 +01:00
|
|
|
|
return {};
|
|
|
|
|
|
2025-03-07 21:10:16 +01:00
|
|
|
|
if (auto* layout_node = this->layout_node(); layout_node && layout_node->is_text_node()) {
|
|
|
|
|
// NOTE: Since the text node's data has changed, we need to invalidate the text for rendering.
|
|
|
|
|
// This ensures that the new text is reflected in layout, even if we don't end up
|
|
|
|
|
// doing a full layout tree rebuild.
|
2023-08-16 11:03:00 +02:00
|
|
|
|
static_cast<Layout::TextNode&>(*layout_node).invalidate_text_for_rendering();
|
|
|
|
|
|
2025-03-07 21:10:16 +01:00
|
|
|
|
// We also need to relayout.
|
2025-04-18 20:40:14 +02:00
|
|
|
|
layout_node->set_needs_layout_update(SetNeedsLayoutReason::CharacterDataReplaceData);
|
2025-03-07 21:10:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
2025-01-23 10:50:00 +01:00
|
|
|
|
document().bump_character_data_version();
|
2024-06-19 09:02:21 -04:00
|
|
|
|
|
2024-09-05 09:59:59 -04:00
|
|
|
|
if (m_grapheme_segmenter)
|
|
|
|
|
m_grapheme_segmenter->set_segmented_text(m_data);
|
2024-09-05 12:10:25 -04:00
|
|
|
|
if (m_word_segmenter)
|
|
|
|
|
m_word_segmenter->set_segmented_text(m_data);
|
2024-06-19 09:02:21 -04:00
|
|
|
|
|
2022-03-21 18:05:20 +01:00
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-11 16:23:50 +01:00
|
|
|
|
// https://dom.spec.whatwg.org/#dom-characterdata-appenddata
|
2025-07-24 12:05:52 -04:00
|
|
|
|
WebIDL::ExceptionOr<void> CharacterData::append_data(Utf16View const& data)
|
2022-07-11 16:23:50 +01:00
|
|
|
|
{
|
|
|
|
|
// The appendData(data) method steps are to replace data with node this, offset this’s length, count 0, and data data.
|
2023-12-22 20:41:34 +13:00
|
|
|
|
return replace_data(this->length_in_utf16_code_units(), 0, data);
|
2022-07-11 16:23:50 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://dom.spec.whatwg.org/#dom-characterdata-insertdata
|
2025-07-24 12:05:52 -04:00
|
|
|
|
WebIDL::ExceptionOr<void> CharacterData::insert_data(size_t offset, Utf16View const& data)
|
2022-07-11 16:23:50 +01:00
|
|
|
|
{
|
|
|
|
|
// The insertData(offset, data) method steps are to replace data with node this, offset offset, count 0, and data data.
|
|
|
|
|
return replace_data(offset, 0, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://dom.spec.whatwg.org/#dom-characterdata-deletedata
|
2022-09-25 17:03:42 +01:00
|
|
|
|
WebIDL::ExceptionOr<void> CharacterData::delete_data(size_t offset, size_t count)
|
2022-07-11 16:23:50 +01:00
|
|
|
|
{
|
|
|
|
|
// The deleteData(offset, count) method steps are to replace data with node this, offset offset, count count, and data the empty string.
|
2025-07-24 12:05:52 -04:00
|
|
|
|
return replace_data(offset, count, {});
|
2022-07-11 16:23:50 +01:00
|
|
|
|
}
|
|
|
|
|
|
2024-09-22 10:03:23 -04:00
|
|
|
|
Unicode::Segmenter& CharacterData::grapheme_segmenter() const
|
2024-06-19 09:02:21 -04:00
|
|
|
|
{
|
2024-09-05 09:59:59 -04:00
|
|
|
|
if (!m_grapheme_segmenter) {
|
2024-09-22 10:03:23 -04:00
|
|
|
|
m_grapheme_segmenter = document().grapheme_segmenter().clone();
|
2024-09-05 09:59:59 -04:00
|
|
|
|
m_grapheme_segmenter->set_segmented_text(m_data);
|
2024-06-19 09:02:21 -04:00
|
|
|
|
}
|
|
|
|
|
|
2024-09-05 09:59:59 -04:00
|
|
|
|
return *m_grapheme_segmenter;
|
2024-06-19 09:02:21 -04:00
|
|
|
|
}
|
|
|
|
|
|
2024-09-22 10:03:23 -04:00
|
|
|
|
Unicode::Segmenter& CharacterData::word_segmenter() const
|
2024-09-05 12:10:25 -04:00
|
|
|
|
{
|
|
|
|
|
if (!m_word_segmenter) {
|
2024-09-22 10:03:23 -04:00
|
|
|
|
m_word_segmenter = document().word_segmenter().clone();
|
2024-09-05 12:10:25 -04:00
|
|
|
|
m_word_segmenter->set_segmented_text(m_data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return *m_word_segmenter;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-07 10:27:02 +01:00
|
|
|
|
}
|