EditEventHandler.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. /*
  2. * Copyright (c) 2020-2021, the SerenityOS developers.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/StringBuilder.h>
  7. #include <AK/Utf8View.h>
  8. #include <LibWeb/DOM/Document.h>
  9. #include <LibWeb/DOM/Position.h>
  10. #include <LibWeb/DOM/Range.h>
  11. #include <LibWeb/DOM/Text.h>
  12. #include <LibWeb/HTML/BrowsingContext.h>
  13. #include <LibWeb/Layout/InitialContainingBlock.h>
  14. #include <LibWeb/Layout/LayoutPosition.h>
  15. #include <LibWeb/Page/EditEventHandler.h>
  16. namespace Web {
  17. void EditEventHandler::handle_delete_character_after(const DOM::Position& cursor_position)
  18. {
  19. if (cursor_position.offset_is_at_end_of_node()) {
  20. // FIXME: Move to the next node and delete the first character there.
  21. return;
  22. }
  23. auto& node = *static_cast<DOM::Text*>(const_cast<DOM::Node*>(cursor_position.node()));
  24. auto& text = node.data();
  25. auto code_point_length = Utf8View(text).iterator_at_byte_offset(cursor_position.offset()).underlying_code_point_length_in_bytes();
  26. StringBuilder builder;
  27. builder.append(text.substring_view(0, cursor_position.offset()));
  28. builder.append(text.substring_view(cursor_position.offset() + code_point_length));
  29. node.set_data(builder.to_string());
  30. m_frame.did_edit({});
  31. }
  32. // This method is quite convoluted but this is necessary to make editing feel intuitive.
  33. void EditEventHandler::handle_delete(DOM::Range& range)
  34. {
  35. auto* start = verify_cast<DOM::Text>(range.start_container());
  36. auto* end = verify_cast<DOM::Text>(range.end_container());
  37. if (start == end) {
  38. StringBuilder builder;
  39. builder.append(start->data().substring_view(0, range.start_offset()));
  40. builder.append(end->data().substring_view(range.end_offset()));
  41. start->set_data(builder.to_string());
  42. } else {
  43. // Remove all the nodes that are fully enclosed in the range.
  44. HashTable<DOM::Node*> queued_for_deletion;
  45. for (auto* node = start->next_in_pre_order(); node; node = node->next_in_pre_order()) {
  46. if (node == end)
  47. break;
  48. queued_for_deletion.set(node);
  49. }
  50. for (auto* parent = start->parent(); parent; parent = parent->parent())
  51. queued_for_deletion.remove(parent);
  52. for (auto* parent = end->parent(); parent; parent = parent->parent())
  53. queued_for_deletion.remove(parent);
  54. for (auto* node : queued_for_deletion)
  55. node->remove();
  56. // Join the parent nodes of start and end.
  57. DOM::Node *insert_after = start, *remove_from = end, *parent_of_end = end->parent();
  58. while (remove_from) {
  59. auto* next_sibling = remove_from->next_sibling();
  60. remove_from->remove();
  61. insert_after->parent()->insert_before(*remove_from, *insert_after);
  62. insert_after = remove_from;
  63. remove_from = next_sibling;
  64. }
  65. if (!parent_of_end->has_children()) {
  66. if (parent_of_end->parent())
  67. parent_of_end->remove();
  68. }
  69. // Join the start and end nodes.
  70. StringBuilder builder;
  71. builder.append(start->data().substring_view(0, range.start_offset()));
  72. builder.append(end->data().substring_view(range.end_offset()));
  73. start->set_data(builder.to_string());
  74. end->remove();
  75. }
  76. // FIXME: When nodes are removed from the DOM, the associated layout nodes become stale and still
  77. // remain in the layout tree. This has to be fixed, this just causes everything to be recomputed
  78. // which really hurts performance.
  79. m_frame.active_document()->force_layout();
  80. m_frame.did_edit({});
  81. }
  82. void EditEventHandler::handle_insert(DOM::Position position, u32 code_point)
  83. {
  84. if (is<DOM::Text>(*position.node())) {
  85. auto& node = verify_cast<DOM::Text>(*position.node());
  86. StringBuilder builder;
  87. builder.append(node.data().substring_view(0, position.offset()));
  88. builder.append_code_point(code_point);
  89. builder.append(node.data().substring_view(position.offset()));
  90. node.set_data(builder.to_string());
  91. node.invalidate_style();
  92. }
  93. // FIXME: When nodes are removed from the DOM, the associated layout nodes become stale and still
  94. // remain in the layout tree. This has to be fixed, this just causes everything to be recomputed
  95. // which really hurts performance.
  96. m_frame.active_document()->force_layout();
  97. m_frame.did_edit({});
  98. }
  99. }