diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 5cfb030bde..94d7e04a62 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -36,6 +36,7 @@ set(SOURCES DOM/Element.cpp DOM/ElementFactory.cpp DOM/Event.cpp + DOM/Range.cpp DOM/EventDispatcher.cpp DOM/EventListener.cpp DOM/EventTarget.cpp diff --git a/Libraries/LibWeb/DOM/Position.cpp b/Libraries/LibWeb/DOM/Position.cpp index 90de24663c..aa80758f62 100644 --- a/Libraries/LibWeb/DOM/Position.cpp +++ b/Libraries/LibWeb/DOM/Position.cpp @@ -39,24 +39,6 @@ Position::~Position() { } -Range Range::normalized() const -{ - if (!is_valid()) - return {}; - - if (m_start.node() == m_end.node()) { - if (m_start.offset() <= m_end.offset()) - return *this; - - return { m_end, m_start }; - } - - if (m_start.node()->is_before(*m_end.node())) - return *this; - - return { m_end, m_start }; -} - const LogStream& operator<<(const LogStream& stream, const Position& position) { if (!position.node()) diff --git a/Libraries/LibWeb/DOM/Position.h b/Libraries/LibWeb/DOM/Position.h index 13ad332a1b..75ef1a2f88 100644 --- a/Libraries/LibWeb/DOM/Position.h +++ b/Libraries/LibWeb/DOM/Position.h @@ -62,38 +62,6 @@ private: unsigned m_offset { 0 }; }; -class Range { -public: - Range() = default; - Range(const Position& start, const Position& end) - : m_start(start) - , m_end(end) - { - } - - bool is_valid() const { return m_start.is_valid() && m_end.is_valid(); } - - void set(const Position& start, const Position& end) - { - m_start = start; - m_end = end; - } - - void set_start(const Position& start) { m_start = start; } - void set_end(const Position& end) { m_end = end; } - - const Position& start() const { return m_start; } - Position& start() { return m_start; } - - const Position& end() const { return m_end; } - Position& end() { return m_end; } - - Range normalized() const; - -private: - Position m_start, m_end; -}; - const LogStream& operator<<(const LogStream&, const Position&); } diff --git a/Libraries/LibWeb/DOM/Range.cpp b/Libraries/LibWeb/DOM/Range.cpp new file mode 100644 index 0000000000..94e8162b5a --- /dev/null +++ b/Libraries/LibWeb/DOM/Range.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +namespace Web::DOM { + +Range::Range(Document& document) + : m_start_container(document) + , m_start_offset(0) + , m_end_container(document) + , m_end_offset(0) +{ +} + +Range::Range(Node& start_container, size_t start_offset, Node& end_container, size_t end_offset) + : m_start_container(start_container) + , m_start_offset(start_offset) + , m_end_container(end_container) + , m_end_offset(end_offset) +{ +} + +NonnullRefPtr Range::clone_range() const +{ + return adopt(*new Range(const_cast(*m_start_container), m_start_offset, const_cast(*m_end_container), m_end_offset)); +} + +NonnullRefPtr Range::inverted() const +{ + return adopt(*new Range(const_cast(*m_end_container), m_end_offset, const_cast(*m_start_container), m_start_offset)); +} + +NonnullRefPtr Range::normalized() const +{ + if (m_start_container.ptr() == m_end_container.ptr()) { + if (m_start_offset <= m_end_offset) + return clone_range(); + + return inverted(); + } + + if (m_start_container->is_before(m_end_container)) + return clone_range(); + + return inverted(); +} + +} diff --git a/Libraries/LibWeb/DOM/Range.h b/Libraries/LibWeb/DOM/Range.h new file mode 100644 index 0000000000..c720af6c8f --- /dev/null +++ b/Libraries/LibWeb/DOM/Range.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include + +namespace Web::DOM { + +class Range final + : public RefCounted + , public Bindings::Wrappable { +public: + // using WrapperType = Bindings::RangeWrapper; + + static NonnullRefPtr create(Document& document) + { + return adopt(*new Range(document)); + } + static NonnullRefPtr create(Node& start_container, size_t start_offset, Node& end_container, size_t end_offset) + { + return adopt(*new Range(start_container, start_offset, end_container, end_offset)); + } + + Node* start_container() { return m_start_container; } + unsigned start_offset() { return m_start_offset; } + + Node* end_container() { return m_end_container; } + unsigned end_offset() { return m_end_offset; } + + bool collapsed() + { + return start_container() == end_container() && start_offset() == end_offset(); + } + + void set_start(Node& container, JS::Value& offset) + { + m_start_container = container; + m_start_offset = (unsigned)offset.as_i32(); + } + + void set_end(Node& container, JS::Value& offset) + { + m_end_container = container; + m_end_offset = (unsigned)offset.as_i32(); + } + + NonnullRefPtr inverted() const; + NonnullRefPtr normalized() const; + NonnullRefPtr clone_range() const; + +private: + explicit Range(Document&); + Range(Node& start_container, size_t start_offset, Node& end_container, size_t end_offset); + + NonnullRefPtr m_start_container; + unsigned m_start_offset; + + NonnullRefPtr m_end_container; + unsigned m_end_offset; +}; + +} diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index 17674ecba4..d525f91dfa 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -51,10 +51,10 @@ class MouseEvent; class Node; class ParentNode; class Position; -class Range; class Text; class Timer; class Window; +class Range; enum class QuirksMode; } diff --git a/Libraries/LibWeb/Layout/LayoutPosition.cpp b/Libraries/LibWeb/Layout/LayoutPosition.cpp index 811e9d5067..307c81f9f7 100644 --- a/Libraries/LibWeb/Layout/LayoutPosition.cpp +++ b/Libraries/LibWeb/Layout/LayoutPosition.cpp @@ -25,6 +25,7 @@ */ #include +#include #include #include @@ -53,12 +54,14 @@ LayoutRange LayoutRange::normalized() const return { m_end, m_start }; } -DOM::Range LayoutRange::to_dom_range() const +NonnullRefPtr LayoutRange::to_dom_range() const { - if (!is_valid()) - return {}; + ASSERT(is_valid()); - return { m_start.to_dom_position(), m_end.to_dom_position() }; + auto start = m_start.to_dom_position(); + auto end = m_end.to_dom_position(); + + return DOM::Range::create(*start.node(), start.offset(), *end.node(), end.offset()); } } diff --git a/Libraries/LibWeb/Layout/LayoutPosition.h b/Libraries/LibWeb/Layout/LayoutPosition.h index 0f19b1208f..840a9c388b 100644 --- a/Libraries/LibWeb/Layout/LayoutPosition.h +++ b/Libraries/LibWeb/Layout/LayoutPosition.h @@ -68,7 +68,7 @@ public: LayoutRange normalized() const; - DOM::Range to_dom_range() const; + NonnullRefPtr to_dom_range() const; private: LayoutPosition m_start; diff --git a/Libraries/LibWeb/Page/EditEventHandler.cpp b/Libraries/LibWeb/Page/EditEventHandler.cpp index 5e8267faa2..7b1c47a416 100644 --- a/Libraries/LibWeb/Page/EditEventHandler.cpp +++ b/Libraries/LibWeb/Page/EditEventHandler.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -39,15 +40,15 @@ namespace Web { // This method is quite convoluted but this is necessary to make editing feel intuitive. -void EditEventHandler::handle_delete(DOM::Range range) +void EditEventHandler::handle_delete(DOM::Range& range) { - auto* start = downcast(range.start().node()); - auto* end = downcast(range.end().node()); + auto* start = downcast(range.start_container()); + auto* end = downcast(range.end_container()); if (start == end) { StringBuilder builder; - builder.append(start->data().substring_view(0, range.start().offset())); - builder.append(end->data().substring_view(range.end().offset())); + builder.append(start->data().substring_view(0, range.start_offset())); + builder.append(end->data().substring_view(range.end_offset())); start->set_data(builder.to_string()); } else { @@ -84,8 +85,8 @@ void EditEventHandler::handle_delete(DOM::Range range) // Join the start and end nodes. StringBuilder builder; - builder.append(start->data().substring_view(0, range.start().offset())); - builder.append(end->data().substring_view(range.end().offset())); + builder.append(start->data().substring_view(0, range.start_offset())); + builder.append(end->data().substring_view(range.end_offset())); start->set_data(builder.to_string()); start->parent()->remove_child(*end); diff --git a/Libraries/LibWeb/Page/EditEventHandler.h b/Libraries/LibWeb/Page/EditEventHandler.h index dbcc04e7a1..67edb9eff0 100644 --- a/Libraries/LibWeb/Page/EditEventHandler.h +++ b/Libraries/LibWeb/Page/EditEventHandler.h @@ -39,7 +39,7 @@ public: virtual ~EditEventHandler() = default; - virtual void handle_delete(DOM::Range); + virtual void handle_delete(DOM::Range&); virtual void handle_insert(DOM::Position, u32 code_point); private: diff --git a/Libraries/LibWeb/Page/EventHandler.cpp b/Libraries/LibWeb/Page/EventHandler.cpp index 9f3f80b697..3170c7122a 100644 --- a/Libraries/LibWeb/Page/EventHandler.cpp +++ b/Libraries/LibWeb/Page/EventHandler.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -346,15 +347,15 @@ bool EventHandler::handle_keydown(KeyCode key, unsigned modifiers, u32 code_poin } if (layout_root()->selection().is_valid()) { - auto range = layout_root()->selection().to_dom_range().normalized(); + auto range = layout_root()->selection().to_dom_range()->normalized(); m_frame.document()->layout_node()->set_selection({}); // FIXME: This doesn't work for some reason? - m_frame.set_cursor_position(range.start()); + m_frame.set_cursor_position({ *range->start_container(), range->start_offset() }); if (key == KeyCode::Key_Backspace || key == KeyCode::Key_Delete) { - if (range.start().node()->is_editable()) { + if (range->start_container()->is_editable()) { m_edit_event_handler->handle_delete(range); return true; } @@ -375,7 +376,7 @@ bool EventHandler::handle_keydown(KeyCode key, unsigned modifiers, u32 code_poin TODO(); m_frame.cursor_position().set_offset(position.offset() - 1); - m_edit_event_handler->handle_delete({ { *position.node(), position.offset() - 1 }, position }); + m_edit_event_handler->handle_delete(DOM::Range::create(*position.node(), position.offset() - 1, *position.node(), position.offset())); return true; } else if (key == KeyCode::Key_Delete) { @@ -384,7 +385,7 @@ bool EventHandler::handle_keydown(KeyCode key, unsigned modifiers, u32 code_poin if (position.offset() >= downcast(position.node())->data().length()) TODO(); - m_edit_event_handler->handle_delete({ position, { *position.node(), position.offset() + 1 } }); + m_edit_event_handler->handle_delete(DOM::Range::create(*position.node(), position.offset(), *position.node(), position.offset() + 1)); return true; } else if (key == KeyCode::Key_Right) {