EditingHostManager.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /*
  2. * Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibUnicode/CharacterTypes.h>
  7. #include <LibUnicode/Segmenter.h>
  8. #include <LibWeb/DOM/EditingHostManager.h>
  9. #include <LibWeb/DOM/Range.h>
  10. #include <LibWeb/DOM/Text.h>
  11. #include <LibWeb/Selection/Selection.h>
  12. namespace Web::DOM {
  13. JS_DEFINE_ALLOCATOR(EditingHostManager);
  14. void EditingHostManager::handle_insert(String const& data)
  15. {
  16. auto selection = m_document->get_selection();
  17. auto selection_range = selection->range();
  18. if (!selection_range) {
  19. return;
  20. }
  21. auto node = selection->anchor_node();
  22. if (!node || !node->is_editable()) {
  23. return;
  24. }
  25. if (!is<DOM::Text>(*node)) {
  26. auto& realm = node->realm();
  27. auto text = realm.heap().allocate<DOM::Text>(realm, node->document(), data);
  28. MUST(node->append_child(*text));
  29. MUST(selection->collapse(*text, 1));
  30. return;
  31. }
  32. auto& text_node = static_cast<DOM::Text&>(*node);
  33. MUST(selection_range->delete_contents());
  34. MUST(text_node.insert_data(selection->anchor_offset(), data));
  35. VERIFY(selection->is_collapsed());
  36. auto utf16_data = MUST(AK::utf8_to_utf16(data));
  37. Utf16View const utf16_view { utf16_data };
  38. auto length = utf16_view.length_in_code_units();
  39. MUST(selection->collapse(*node, selection->anchor_offset() + length));
  40. text_node.invalidate_style(DOM::StyleInvalidationReason::EditingInsertion);
  41. }
  42. void EditingHostManager::select_all()
  43. {
  44. if (!m_active_contenteditable_element) {
  45. return;
  46. }
  47. auto selection = m_document->get_selection();
  48. if (!selection->anchor_node() || !selection->focus_node()) {
  49. return;
  50. }
  51. MUST(selection->set_base_and_extent(*selection->anchor_node(), 0, *selection->focus_node(), selection->focus_node()->length()));
  52. }
  53. void EditingHostManager::set_selection_anchor(JS::NonnullGCPtr<DOM::Node> anchor_node, size_t anchor_offset)
  54. {
  55. auto selection = m_document->get_selection();
  56. MUST(selection->collapse(*anchor_node, anchor_offset));
  57. m_document->reset_cursor_blink_cycle();
  58. }
  59. void EditingHostManager::set_selection_focus(JS::NonnullGCPtr<DOM::Node> focus_node, size_t focus_offset)
  60. {
  61. if (!m_active_contenteditable_element || !m_active_contenteditable_element->is_ancestor_of(*focus_node))
  62. return;
  63. auto selection = m_document->get_selection();
  64. if (!selection->anchor_node())
  65. return;
  66. MUST(selection->set_base_and_extent(*selection->anchor_node(), selection->anchor_offset(), *focus_node, focus_offset));
  67. m_document->reset_cursor_blink_cycle();
  68. }
  69. void EditingHostManager::move_cursor_to_start(CollapseSelection collapse)
  70. {
  71. auto selection = m_document->get_selection();
  72. auto node = selection->anchor_node();
  73. if (!node || !is<DOM::Text>(*node))
  74. return;
  75. if (collapse == CollapseSelection::Yes) {
  76. MUST(selection->collapse(node, 0));
  77. m_document->reset_cursor_blink_cycle();
  78. return;
  79. }
  80. MUST(selection->set_base_and_extent(*node, selection->anchor_offset(), *node, 0));
  81. }
  82. void EditingHostManager::move_cursor_to_end(CollapseSelection collapse)
  83. {
  84. auto selection = m_document->get_selection();
  85. auto node = selection->anchor_node();
  86. if (!node || !is<DOM::Text>(*node))
  87. return;
  88. if (collapse == CollapseSelection::Yes) {
  89. m_document->reset_cursor_blink_cycle();
  90. MUST(selection->collapse(node, node->length()));
  91. return;
  92. }
  93. MUST(selection->set_base_and_extent(*node, selection->anchor_offset(), *node, node->length()));
  94. }
  95. void EditingHostManager::increment_cursor_position_offset(CollapseSelection collapse)
  96. {
  97. auto selection = m_document->get_selection();
  98. auto node = selection->anchor_node();
  99. if (!node || !is<DOM::Text>(*node))
  100. return;
  101. auto& text_node = static_cast<DOM::Text&>(*node);
  102. if (auto offset = text_node.grapheme_segmenter().next_boundary(selection->focus_offset()); offset.has_value()) {
  103. if (collapse == CollapseSelection::Yes) {
  104. MUST(selection->collapse(*node, *offset));
  105. m_document->reset_cursor_blink_cycle();
  106. } else {
  107. MUST(selection->set_base_and_extent(*node, selection->anchor_offset(), *node, *offset));
  108. }
  109. }
  110. }
  111. void EditingHostManager::decrement_cursor_position_offset(CollapseSelection collapse)
  112. {
  113. auto selection = m_document->get_selection();
  114. auto node = selection->anchor_node();
  115. if (!node || !is<DOM::Text>(*node)) {
  116. return;
  117. }
  118. auto& text_node = static_cast<DOM::Text&>(*node);
  119. if (auto offset = text_node.grapheme_segmenter().previous_boundary(selection->focus_offset()); offset.has_value()) {
  120. if (collapse == CollapseSelection::Yes) {
  121. MUST(selection->collapse(*node, *offset));
  122. m_document->reset_cursor_blink_cycle();
  123. } else {
  124. MUST(selection->set_base_and_extent(*node, selection->anchor_offset(), *node, *offset));
  125. }
  126. }
  127. }
  128. static bool should_continue_beyond_word(Utf8View const& word)
  129. {
  130. for (auto code_point : word) {
  131. if (!Unicode::code_point_has_punctuation_general_category(code_point) && !Unicode::code_point_has_separator_general_category(code_point))
  132. return false;
  133. }
  134. return true;
  135. }
  136. void EditingHostManager::increment_cursor_position_to_next_word(CollapseSelection collapse)
  137. {
  138. auto selection = m_document->get_selection();
  139. auto node = selection->anchor_node();
  140. if (!node || !is<DOM::Text>(*node)) {
  141. return;
  142. }
  143. auto& text_node = static_cast<DOM::Text&>(*node);
  144. while (true) {
  145. auto focus_offset = selection->focus_offset();
  146. if (focus_offset == text_node.data().bytes_as_string_view().length()) {
  147. return;
  148. }
  149. if (auto offset = text_node.word_segmenter().next_boundary(focus_offset); offset.has_value()) {
  150. auto word = text_node.data().code_points().substring_view(focus_offset, *offset - focus_offset);
  151. if (collapse == CollapseSelection::Yes) {
  152. MUST(selection->collapse(node, *offset));
  153. m_document->reset_cursor_blink_cycle();
  154. } else {
  155. MUST(selection->set_base_and_extent(*node, selection->anchor_offset(), *node, *offset));
  156. }
  157. if (should_continue_beyond_word(word))
  158. continue;
  159. }
  160. break;
  161. }
  162. }
  163. void EditingHostManager::decrement_cursor_position_to_previous_word(CollapseSelection collapse)
  164. {
  165. auto selection = m_document->get_selection();
  166. auto node = selection->anchor_node();
  167. if (!node || !is<DOM::Text>(*node)) {
  168. return;
  169. }
  170. auto& text_node = static_cast<DOM::Text&>(*node);
  171. while (true) {
  172. auto focus_offset = selection->focus_offset();
  173. if (auto offset = text_node.word_segmenter().previous_boundary(focus_offset); offset.has_value()) {
  174. auto word = text_node.data().code_points().substring_view(focus_offset, focus_offset - *offset);
  175. if (collapse == CollapseSelection::Yes) {
  176. MUST(selection->collapse(node, *offset));
  177. m_document->reset_cursor_blink_cycle();
  178. } else {
  179. MUST(selection->set_base_and_extent(*node, selection->anchor_offset(), *node, *offset));
  180. }
  181. if (should_continue_beyond_word(word))
  182. continue;
  183. }
  184. break;
  185. }
  186. }
  187. void EditingHostManager::handle_delete(DeleteDirection direction)
  188. {
  189. auto selection = m_document->get_selection();
  190. auto selection_range = selection->range();
  191. if (!selection_range) {
  192. return;
  193. }
  194. if (selection->is_collapsed()) {
  195. auto node = selection->anchor_node();
  196. if (!node || !is<DOM::Text>(*node)) {
  197. return;
  198. }
  199. auto& text_node = static_cast<DOM::Text&>(*node);
  200. if (direction == DeleteDirection::Backward) {
  201. if (selection->anchor_offset() > 0) {
  202. MUST(text_node.delete_data(selection->anchor_offset() - 1, 1));
  203. text_node.invalidate_style(DOM::StyleInvalidationReason::EditingInsertion);
  204. }
  205. } else {
  206. if (selection->anchor_offset() < text_node.data().bytes_as_string_view().length()) {
  207. MUST(text_node.delete_data(selection->anchor_offset(), 1));
  208. text_node.invalidate_style(DOM::StyleInvalidationReason::EditingInsertion);
  209. }
  210. }
  211. m_document->reset_cursor_blink_cycle();
  212. return;
  213. }
  214. MUST(selection_range->delete_contents());
  215. }
  216. void EditingHostManager::handle_return_key()
  217. {
  218. dbgln("FIXME: Implement EditingHostManager::handle_return_key()");
  219. }
  220. }