diff --git a/Userland/Libraries/LibWeb/DOM/Text.h b/Userland/Libraries/LibWeb/DOM/Text.h index 4f35a54632e..9d531dd5e82 100644 --- a/Userland/Libraries/LibWeb/DOM/Text.h +++ b/Userland/Libraries/LibWeb/DOM/Text.h @@ -15,7 +15,7 @@ namespace Web::DOM { class EditableTextNodeOwner { public: virtual ~EditableTextNodeOwner() = default; - virtual void did_edit_text_node(Badge) = 0; + virtual void did_edit_text_node(Badge) = 0; }; class Text diff --git a/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp b/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp index bae2f29db49..2bac4c04ab0 100644 --- a/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp +++ b/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp @@ -276,22 +276,7 @@ WebIDL::ExceptionOr BrowsingContext BrowsingContext::BrowsingContext(JS::NonnullGCPtr page) : m_page(page) - , m_event_handler({}, *this) { - m_cursor_blink_timer = Core::Timer::create_repeating(500, [this] { - if (!is_focused_context()) - return; - if (!m_cursor_position) - return; - auto node = m_cursor_position->node(); - if (!node) - return; - node->document().update_layout(); - if (node->paintable()) { - m_cursor_blink_state = !m_cursor_blink_state; - node->paintable()->set_needs_display(); - } - }); } BrowsingContext::~BrowsingContext() = default; @@ -301,7 +286,6 @@ void BrowsingContext::visit_edges(Cell::Visitor& visitor) Base::visit_edges(visitor); visitor.visit(m_page); - visitor.visit(m_cursor_position); visitor.visit(m_window_proxy); visitor.visit(m_group); visitor.visit(m_parent); @@ -310,8 +294,6 @@ void BrowsingContext::visit_edges(Cell::Visitor& visitor) visitor.visit(m_next_sibling); visitor.visit(m_previous_sibling); visitor.visit(m_opener_browsing_context); - - m_event_handler.visit_edges(visitor); } // https://html.spec.whatwg.org/multipage/document-sequences.html#bc-traversable @@ -324,25 +306,6 @@ JS::NonnullGCPtr BrowsingContext::top_level_traversa return *traversable; } -void BrowsingContext::did_edit(Badge) -{ - reset_cursor_blink_cycle(); - - if (m_cursor_position && is(*m_cursor_position->node())) { - auto& text_node = static_cast(*m_cursor_position->node()); - if (auto text_node_owner = text_node.editable_text_node_owner()) - text_node_owner->did_edit_text_node({}); - } -} - -void BrowsingContext::reset_cursor_blink_cycle() -{ - m_cursor_blink_state = true; - m_cursor_blink_timer->restart(); - if (m_cursor_position && m_cursor_position->node()->paintable()) - m_cursor_position->node()->paintable()->set_needs_display(); -} - // https://html.spec.whatwg.org/multipage/browsers.html#top-level-browsing-context bool BrowsingContext::is_top_level() const { @@ -350,11 +313,6 @@ bool BrowsingContext::is_top_level() const return !parent(); } -bool BrowsingContext::is_focused_context() const -{ - return &m_page->focused_context() == this; -} - JS::GCPtr BrowsingContext::top_level_browsing_context() const { auto const* start = this; @@ -376,127 +334,6 @@ JS::GCPtr BrowsingContext::top_level_browsing_context() const return navigable->active_browsing_context(); } -void BrowsingContext::set_cursor_position(JS::NonnullGCPtr position) -{ - if (m_cursor_position && m_cursor_position->equals(position)) - return; - - if (m_cursor_position && m_cursor_position->node()->paintable()) - m_cursor_position->node()->paintable()->set_needs_display(); - - m_cursor_position = position; - - if (m_cursor_position && m_cursor_position->node()->paintable()) - m_cursor_position->node()->paintable()->set_needs_display(); - - reset_cursor_blink_cycle(); -} - -static String visible_text_in_range(DOM::Range const& range) -{ - // NOTE: This is an adaption of Range stringification, but we skip over DOM nodes that don't have a corresponding layout node. - StringBuilder builder; - - if (range.start_container() == range.end_container() && is(*range.start_container())) { - if (!range.start_container()->layout_node()) - return String {}; - return MUST(static_cast(*range.start_container()).data().substring_from_byte_offset(range.start_offset(), range.end_offset() - range.start_offset())); - } - - if (is(*range.start_container()) && range.start_container()->layout_node()) - builder.append(static_cast(*range.start_container()).data().bytes_as_string_view().substring_view(range.start_offset())); - - for (DOM::Node const* node = range.start_container(); node != range.end_container()->next_sibling(); node = node->next_in_pre_order()) { - if (is(*node) && range.contains_node(*node) && node->layout_node()) - builder.append(static_cast(*node).data()); - } - - if (is(*range.end_container()) && range.end_container()->layout_node()) - builder.append(static_cast(*range.end_container()).data().bytes_as_string_view().substring_view(0, range.end_offset())); - - return MUST(builder.to_string()); -} - -String BrowsingContext::selected_text() const -{ - auto const* document = active_document(); - if (!document) - return String {}; - auto selection = const_cast(*document).get_selection(); - auto range = selection->range(); - if (!range) - return String {}; - return visible_text_in_range(*range); -} - -void BrowsingContext::select_all() -{ - auto* document = active_document(); - if (!document) - return; - auto* body = document->body(); - if (!body) - return; - auto selection = document->get_selection(); - if (!selection) - return; - (void)selection->select_all_children(*document->body()); -} - -void BrowsingContext::paste(String const& text) -{ - auto* document = active_document(); - if (!document) - return; - - m_event_handler.handle_paste(text); -} - -bool BrowsingContext::increment_cursor_position_offset() -{ - if (!m_cursor_position->increment_offset()) - return false; - reset_cursor_blink_cycle(); - return true; -} - -bool BrowsingContext::decrement_cursor_position_offset() -{ - if (!m_cursor_position->decrement_offset()) - return false; - reset_cursor_blink_cycle(); - return true; -} - -// https://html.spec.whatwg.org/multipage/interaction.html#currently-focused-area-of-a-top-level-browsing-context -JS::GCPtr BrowsingContext::currently_focused_area() -{ - // 1. If topLevelBC does not have system focus, then return null. - if (!is_focused_context()) - return nullptr; - - // 2. Let candidate be topLevelBC's active document. - auto* candidate = active_document(); - - // 3. While candidate's focused area is a browsing context container with a non-null nested browsing context: - // set candidate to the active document of that browsing context container's nested browsing context. - while (candidate->focused_element() - && is(candidate->focused_element()) - && static_cast(*candidate->focused_element()).nested_browsing_context()) { - candidate = static_cast(*candidate->focused_element()).nested_browsing_context()->active_document(); - } - - // 4. If candidate's focused area is non-null, set candidate to candidate's focused area. - if (candidate->focused_element()) { - // NOTE: We return right away here instead of assigning to candidate, - // since that would require compromising type safety. - return candidate->focused_element(); - } - - // 5. Return candidate. - return candidate; -} - DOM::Document const* BrowsingContext::active_document() const { auto* window = active_window(); diff --git a/Userland/Libraries/LibWeb/HTML/BrowsingContext.h b/Userland/Libraries/LibWeb/HTML/BrowsingContext.h index 694306f1a4d..aa61c6bcc2e 100644 --- a/Userland/Libraries/LibWeb/HTML/BrowsingContext.h +++ b/Userland/Libraries/LibWeb/HTML/BrowsingContext.h @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -99,7 +98,6 @@ public: } bool is_top_level() const; - bool is_focused_context() const; DOM::Document const* active_document() const; DOM::Document* active_document(); @@ -115,26 +113,8 @@ public: Page& page() { return m_page; } Page const& page() const { return m_page; } - Web::EventHandler& event_handler() { return m_event_handler; } - Web::EventHandler const& event_handler() const { return m_event_handler; } - JS::GCPtr top_level_browsing_context() const; - JS::GCPtr cursor_position() const { return m_cursor_position; } - void set_cursor_position(JS::NonnullGCPtr); - bool increment_cursor_position_offset(); - bool decrement_cursor_position_offset(); - - bool cursor_blink_state() const { return m_cursor_blink_state; } - - String selected_text() const; - void select_all(); - void paste(String const&); - - void did_edit(Badge); - - JS::GCPtr currently_focused_area(); - BrowsingContextGroup* group(); void set_group(BrowsingContextGroup*); @@ -156,13 +136,8 @@ private: virtual void visit_edges(Cell::Visitor&) override; - void reset_cursor_blink_cycle(); - JS::NonnullGCPtr m_page; - // FIXME: Move EventHandler to Navigable - Web::EventHandler m_event_handler; - // https://html.spec.whatwg.org/multipage/document-sequences.html#browsing-context JS::GCPtr m_window_proxy; @@ -184,11 +159,6 @@ private: // https://html.spec.whatwg.org/multipage/document-sequences.html#virtual-browsing-context-group-id u64 m_virtual_browsing_context_group_id = { 0 }; - // FIXME: Move cursor tracking to Navigable - JS::GCPtr m_cursor_position; - RefPtr m_cursor_blink_timer; - bool m_cursor_blink_state { false }; - // https://html.spec.whatwg.org/multipage/browsers.html#tlbc-group JS::GCPtr m_group; diff --git a/Userland/Libraries/LibWeb/HTML/Focus.cpp b/Userland/Libraries/LibWeb/HTML/Focus.cpp index b4589ce09eb..6ccb25a908f 100644 --- a/Userland/Libraries/LibWeb/HTML/Focus.cpp +++ b/Userland/Libraries/LibWeb/HTML/Focus.cpp @@ -209,13 +209,13 @@ void run_focusing_steps(DOM::Node* new_focus_target, DOM::Node* fallback_target, // 5. If new focus target is the currently focused area of a top-level browsing context, then return. if (!new_focus_target->document().browsing_context()) return; - auto top_level_browsing_context = new_focus_target->document().browsing_context()->top_level_browsing_context(); - if (new_focus_target == top_level_browsing_context->currently_focused_area().ptr()) + auto top_level_traversable = new_focus_target->document().browsing_context()->top_level_traversable(); + if (new_focus_target == top_level_traversable->currently_focused_area().ptr()) return; // 6. Let old chain be the current focus chain of the top-level browsing context in which // new focus target finds itself. - auto old_chain = focus_chain(top_level_browsing_context->currently_focused_area()); + auto old_chain = focus_chain(top_level_traversable->currently_focused_area()); // 7. Let new chain be the focus chain of new focus target. auto new_chain = focus_chain(new_focus_target); @@ -243,8 +243,8 @@ void run_unfocusing_steps(DOM::Node* old_focus_target) if (is_shadow_host(old_focus_target)) { auto* shadow_root = static_cast(old_focus_target)->shadow_root_internal(); if (shadow_root->delegates_focus()) { - auto top_level_browsing_context = old_focus_target->document().browsing_context()->top_level_browsing_context(); - if (auto currently_focused_area = top_level_browsing_context->currently_focused_area()) { + auto top_level_traversable = old_focus_target->document().browsing_context()->top_level_traversable(); + if (auto currently_focused_area = top_level_traversable->currently_focused_area()) { if (shadow_root->is_shadow_including_ancestor_of(*currently_focused_area)) { old_focus_target = currently_focused_area; } @@ -261,10 +261,10 @@ void run_unfocusing_steps(DOM::Node* old_focus_target) // NOTE: HTMLAreaElement is currently missing the shapes property - auto top_level_browsing_context = old_focus_target->document().browsing_context()->top_level_browsing_context(); + auto top_level_traversable = old_focus_target->document().browsing_context()->top_level_traversable(); // 4. Let old chain be the current focus chain of the top-level browsing context in which old focus target finds itself. - auto old_chain = focus_chain(top_level_browsing_context->currently_focused_area()); + auto old_chain = focus_chain(top_level_traversable->currently_focused_area()); // 5. If old focus target is not one of the entries in old chain, then return. auto it = old_chain.find_if([&](auto const& node) { return old_focus_target == node; }); diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp index 4ad6dc02b87..46837072ee6 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp @@ -589,10 +589,10 @@ void HTMLElement::did_receive_focus() { if (m_content_editable_state != ContentEditableState::True) return; - auto* browsing_context = document().browsing_context(); - if (!browsing_context) + auto navigable = document().navigable(); + if (!navigable) return; - browsing_context->set_cursor_position(DOM::Position::create(realm(), *this, 0)); + navigable->set_cursor_position(DOM::Position::create(realm(), *this, 0)); } // https://html.spec.whatwg.org/multipage/interaction.html#dom-accesskeylabel diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 777fbb05871..e5c03e30a9e 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -379,7 +379,7 @@ WebIDL::ExceptionOr HTMLInputElement::run_input_activation_behavior(DOM::E return {}; } -void HTMLInputElement::did_edit_text_node(Badge) +void HTMLInputElement::did_edit_text_node(Badge) { // An input element's dirty value flag must be set to true whenever the user interacts with the control in a way that changes the value. m_value = value_sanitization_algorithm(m_text_node->data()); @@ -543,8 +543,8 @@ WebIDL::ExceptionOr HTMLInputElement::set_value(String const& value) m_text_node->set_data(m_value); update_placeholder_visibility(); - if (auto* browsing_context = document().browsing_context()) - browsing_context->set_cursor_position(DOM::Position::create(realm, *m_text_node, m_text_node->data().bytes().size())); + if (auto navigable = document().navigable()) + navigable->set_cursor_position(DOM::Position::create(realm, *m_text_node, m_text_node->data().bytes().size())); } update_shadow_tree(); @@ -1053,12 +1053,12 @@ void HTMLInputElement::update_slider_thumb_element() void HTMLInputElement::did_receive_focus() { - auto* browsing_context = document().browsing_context(); - if (!browsing_context) + auto navigable = document().navigable(); + if (!navigable) return; if (!m_text_node) return; - browsing_context->set_cursor_position(DOM::Position::create(realm(), *m_text_node, 0)); + navigable->set_cursor_position(DOM::Position::create(realm(), *m_text_node, 0)); } void HTMLInputElement::did_lose_focus() diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h index 47dd7131748..a9dca6c56c3 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h @@ -143,7 +143,7 @@ public: WebIDL::ExceptionOr show_picker(); // ^DOM::EditableTextNodeOwner - virtual void did_edit_text_node(Badge) override; + virtual void did_edit_text_node(Badge) override; // ^EventTarget // https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute:the-input-element diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp index 81be3f122b4..4e9b5149a2e 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp @@ -66,12 +66,12 @@ void HTMLTextAreaElement::visit_edges(Cell::Visitor& visitor) void HTMLTextAreaElement::did_receive_focus() { - auto* browsing_context = document().browsing_context(); - if (!browsing_context) + auto navigable = document().navigable(); + if (!navigable) return; if (!m_text_node) return; - browsing_context->set_cursor_position(DOM::Position::create(realm(), *m_text_node, 0)); + navigable->set_cursor_position(DOM::Position::create(realm(), *m_text_node, 0)); } void HTMLTextAreaElement::did_lose_focus() @@ -158,8 +158,8 @@ void HTMLTextAreaElement::set_value(String const& value) m_text_node->set_data(m_raw_value); update_placeholder_visibility(); - if (auto* browsing_context = document().browsing_context()) - browsing_context->set_cursor_position(DOM::Position::create(realm, *m_text_node, m_text_node->data().bytes().size())); + if (auto navigable = document().navigable()) + navigable->set_cursor_position(DOM::Position::create(realm, *m_text_node, m_text_node->data().bytes().size())); } } } @@ -215,8 +215,8 @@ WebIDL::UnsignedLong HTMLTextAreaElement::selection_start() const // 2. If there is no selection, return the code unit offset within the relevant value to the character that // immediately follows the text entry cursor. - if (auto const* browsing_context = document().browsing_context()) { - if (auto cursor = browsing_context->cursor_position()) + if (auto navigable = document().navigable()) { + if (auto cursor = navigable->cursor_position()) return cursor->offset(); } @@ -244,8 +244,8 @@ WebIDL::UnsignedLong HTMLTextAreaElement::selection_end() const // 2. If there is no selection, return the code unit offset within the relevant value to the character that // immediately follows the text entry cursor. - if (auto const* browsing_context = document().browsing_context()) { - if (auto cursor = browsing_context->cursor_position()) + if (auto navigable = document().navigable()) { + if (auto cursor = navigable->cursor_position()) return cursor->offset(); } @@ -430,7 +430,7 @@ void HTMLTextAreaElement::form_associated_element_attribute_changed(FlyString co } } -void HTMLTextAreaElement::did_edit_text_node(Badge) +void HTMLTextAreaElement::did_edit_text_node(Badge) { VERIFY(m_text_node); set_raw_value(m_text_node->data()); diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h index 801ec801d8c..d719107a8fc 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h @@ -37,7 +37,7 @@ public: } // ^DOM::EditableTextNodeOwner - virtual void did_edit_text_node(Badge) override; + virtual void did_edit_text_node(Badge) override; // ^EventTarget // https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute:the-textarea-element diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.cpp b/Userland/Libraries/LibWeb/HTML/Navigable.cpp index a89349a302f..8a43a0d56da 100644 --- a/Userland/Libraries/LibWeb/HTML/Navigable.cpp +++ b/Userland/Libraries/LibWeb/HTML/Navigable.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Andreas Kling + * Copyright (c) 2022-2024, Andreas Kling * Copyright (c) 2023, Aliaksandr Kalenik * * SPDX-License-Identifier: BSD-2-Clause @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include namespace Web::HTML { @@ -103,8 +105,24 @@ bool Navigable::is_ancestor_of(JS::NonnullGCPtr other) const Navigable::Navigable(JS::NonnullGCPtr page) : m_page(page) + , m_event_handler({}, *this) { all_navigables().set(this); + + m_cursor_blink_timer = Core::Timer::create_repeating(500, [this] { + if (!is_focused()) + return; + if (!m_cursor_position) + return; + auto node = m_cursor_position->node(); + if (!node) + return; + node->document().update_layout(); + if (node->paintable()) { + m_cursor_blink_state = !m_cursor_blink_state; + node->paintable()->set_needs_display(); + } + }); } Navigable::~Navigable() @@ -120,6 +138,8 @@ void Navigable::visit_edges(Cell::Visitor& visitor) visitor.visit(m_current_session_history_entry); visitor.visit(m_active_session_history_entry); visitor.visit(m_container); + visitor.visit(m_cursor_position); + m_event_handler.visit_edges(visitor); } void Navigable::set_delaying_load_events(bool value) @@ -2159,4 +2179,120 @@ UserNavigationInvolvement user_navigation_involvement(DOM::Event const& event) return event.is_trusted() ? UserNavigationInvolvement::Activation : UserNavigationInvolvement::None; } +void Navigable::did_edit(Badge) +{ + reset_cursor_blink_cycle(); + + if (m_cursor_position && is(*m_cursor_position->node())) { + auto& text_node = static_cast(*m_cursor_position->node()); + if (auto text_node_owner = text_node.editable_text_node_owner()) + text_node_owner->did_edit_text_node({}); + } +} + +void Navigable::reset_cursor_blink_cycle() +{ + m_cursor_blink_state = true; + m_cursor_blink_timer->restart(); + if (m_cursor_position && m_cursor_position->node()->paintable()) + m_cursor_position->node()->paintable()->set_needs_display(); +} + +bool Navigable::is_focused() const +{ + return &m_page->focused_navigable() == this; +} + +void Navigable::set_cursor_position(JS::NonnullGCPtr position) +{ + if (m_cursor_position && m_cursor_position->equals(position)) + return; + + if (m_cursor_position && m_cursor_position->node()->paintable()) + m_cursor_position->node()->paintable()->set_needs_display(); + + m_cursor_position = position; + + if (m_cursor_position && m_cursor_position->node()->paintable()) + m_cursor_position->node()->paintable()->set_needs_display(); + + reset_cursor_blink_cycle(); +} + +static String visible_text_in_range(DOM::Range const& range) +{ + // NOTE: This is an adaption of Range stringification, but we skip over DOM nodes that don't have a corresponding layout node. + StringBuilder builder; + + if (range.start_container() == range.end_container() && is(*range.start_container())) { + if (!range.start_container()->layout_node()) + return String {}; + return MUST(static_cast(*range.start_container()).data().substring_from_byte_offset(range.start_offset(), range.end_offset() - range.start_offset())); + } + + if (is(*range.start_container()) && range.start_container()->layout_node()) + builder.append(static_cast(*range.start_container()).data().bytes_as_string_view().substring_view(range.start_offset())); + + for (DOM::Node const* node = range.start_container(); node != range.end_container()->next_sibling(); node = node->next_in_pre_order()) { + if (is(*node) && range.contains_node(*node) && node->layout_node()) + builder.append(static_cast(*node).data()); + } + + if (is(*range.end_container()) && range.end_container()->layout_node()) + builder.append(static_cast(*range.end_container()).data().bytes_as_string_view().substring_view(0, range.end_offset())); + + return MUST(builder.to_string()); +} + +String Navigable::selected_text() const +{ + auto document = const_cast(this)->active_document(); + if (!document) + return String {}; + auto selection = const_cast(*document).get_selection(); + auto range = selection->range(); + if (!range) + return String {}; + return visible_text_in_range(*range); +} + +void Navigable::select_all() +{ + auto document = active_document(); + if (!document) + return; + auto* body = document->body(); + if (!body) + return; + auto selection = document->get_selection(); + if (!selection) + return; + (void)selection->select_all_children(*document->body()); +} + +void Navigable::paste(String const& text) +{ + auto document = active_document(); + if (!document) + return; + + m_event_handler.handle_paste(text); +} + +bool Navigable::increment_cursor_position_offset() +{ + if (!m_cursor_position->increment_offset()) + return false; + reset_cursor_blink_cycle(); + return true; +} + +bool Navigable::decrement_cursor_position_offset() +{ + if (!m_cursor_position->decrement_offset()) + return false; + reset_cursor_blink_cycle(); + return true; +} + } diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.h b/Userland/Libraries/LibWeb/HTML/Navigable.h index 9ed65de98ba..810ba85f292 100644 --- a/Userland/Libraries/LibWeb/HTML/Navigable.h +++ b/Userland/Libraries/LibWeb/HTML/Navigable.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -44,7 +45,9 @@ struct TargetSnapshotParams { }; // https://html.spec.whatwg.org/multipage/document-sequences.html#navigable -class Navigable : public JS::Cell { +class Navigable + : public JS::Cell + , public Weakable { JS_CELL(Navigable, JS::Cell); JS_DECLARE_ALLOCATOR(Navigable); @@ -93,6 +96,8 @@ public: virtual bool is_top_level_traversable() const { return false; } + [[nodiscard]] bool is_focused() const; + enum class WindowType { ExistingOrNone, NewAndUnrestricted, @@ -186,6 +191,22 @@ public: Page& page() { return m_page; } Page const& page() const { return m_page; } + String selected_text() const; + void select_all(); + void paste(String const&); + + Web::EventHandler& event_handler() { return m_event_handler; } + Web::EventHandler const& event_handler() const { return m_event_handler; } + + void did_edit(Badge); + + JS::GCPtr cursor_position() const { return m_cursor_position; } + void set_cursor_position(JS::NonnullGCPtr); + bool increment_cursor_position_offset(); + bool decrement_cursor_position_offset(); + + bool cursor_blink_state() const { return m_cursor_blink_state; } + protected: explicit Navigable(JS::NonnullGCPtr); @@ -198,6 +219,8 @@ protected: TokenizedFeature::Popup m_is_popup { TokenizedFeature::Popup::No }; private: + void reset_cursor_blink_cycle(); + void scroll_offset_did_change(); void inform_the_navigation_api_about_aborting_navigation(); @@ -231,6 +254,12 @@ private: CSSPixelPoint m_viewport_scroll_offset; bool m_needs_repaint { false }; + + Web::EventHandler m_event_handler; + + JS::GCPtr m_cursor_position; + RefPtr m_cursor_blink_timer; + bool m_cursor_blink_state { false }; }; HashTable& all_navigables(); diff --git a/Userland/Libraries/LibWeb/HTML/NavigableContainer.cpp b/Userland/Libraries/LibWeb/HTML/NavigableContainer.cpp index 8b1f2f5c525..a6cb701efac 100644 --- a/Userland/Libraries/LibWeb/HTML/NavigableContainer.cpp +++ b/Userland/Libraries/LibWeb/HTML/NavigableContainer.cpp @@ -95,7 +95,7 @@ WebIDL::ExceptionOr NavigableContainer::create_new_child_navigable(JS::Saf document_state->set_about_base_url(document->about_base_url()); // 7. Let navigable be a new navigable. - JS::NonnullGCPtr navigable = *heap().allocate_without_realm(); + JS::NonnullGCPtr navigable = *heap().allocate_without_realm(page); // 8. Initialize the navigable navigable given documentState and parentNavigable. TRY_OR_THROW_OOM(vm(), navigable->initialize_navigable(document_state, parent_navigable)); diff --git a/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp b/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp index 6d2e6947716..aec597347a7 100644 --- a/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp +++ b/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp @@ -1138,4 +1138,33 @@ void TraversableNavigable::set_system_visibility_state(VisibilityState visibilit } } +// https://html.spec.whatwg.org/multipage/interaction.html#currently-focused-area-of-a-top-level-traversable +JS::GCPtr TraversableNavigable::currently_focused_area() +{ + // 1. If traversable does not have system focus, then return null. + if (!is_focused()) + return nullptr; + + // 2. Let candidate be traversable's active document. + auto candidate = active_document(); + + // 3. While candidate's focused area is a navigable container with a non-null content navigable: + // set candidate to the active document of that navigable container's content navigable. + while (candidate->focused_element() + && is(candidate->focused_element()) + && static_cast(*candidate->focused_element()).content_navigable()) { + candidate = static_cast(*candidate->focused_element()).content_navigable()->active_document(); + } + + // 4. If candidate's focused area is non-null, set candidate to candidate's focused area. + if (candidate->focused_element()) { + // NOTE: We return right away here instead of assigning to candidate, + // since that would require compromising type safety. + return candidate->focused_element(); + } + + // 5. Return candidate. + return candidate; +} + } diff --git a/Userland/Libraries/LibWeb/HTML/TraversableNavigable.h b/Userland/Libraries/LibWeb/HTML/TraversableNavigable.h index bf2c9df4ee7..f27513dd777 100644 --- a/Userland/Libraries/LibWeb/HTML/TraversableNavigable.h +++ b/Userland/Libraries/LibWeb/HTML/TraversableNavigable.h @@ -82,6 +82,8 @@ public: String window_handle() const { return m_window_handle; } void set_window_handle(String window_handle) { m_window_handle = move(window_handle); } + [[nodiscard]] JS::GCPtr currently_focused_area(); + private: TraversableNavigable(JS::NonnullGCPtr); diff --git a/Userland/Libraries/LibWeb/Page/EditEventHandler.cpp b/Userland/Libraries/LibWeb/Page/EditEventHandler.cpp index 07e7c9ab4b1..e43974f69ba 100644 --- a/Userland/Libraries/LibWeb/Page/EditEventHandler.cpp +++ b/Userland/Libraries/LibWeb/Page/EditEventHandler.cpp @@ -33,7 +33,7 @@ void EditEventHandler::handle_delete_character_after(JS::NonnullGCPtrdid_edit({}); + m_navigable->did_edit({}); } // This method is quite convoluted but this is necessary to make editing feel intuitive. @@ -89,7 +89,7 @@ void EditEventHandler::handle_delete(DOM::Range& range) end->remove(); } - m_browsing_context->did_edit({}); + m_navigable->did_edit({}); } void EditEventHandler::handle_insert(JS::NonnullGCPtr position, u32 code_point) @@ -126,6 +126,6 @@ void EditEventHandler::handle_insert(JS::NonnullGCPtr position, S position->set_offset(1); } - m_browsing_context->did_edit({}); + m_navigable->did_edit({}); } } diff --git a/Userland/Libraries/LibWeb/Page/EditEventHandler.h b/Userland/Libraries/LibWeb/Page/EditEventHandler.h index a3083b42a34..584d371052f 100644 --- a/Userland/Libraries/LibWeb/Page/EditEventHandler.h +++ b/Userland/Libraries/LibWeb/Page/EditEventHandler.h @@ -7,14 +7,15 @@ #pragma once #include +#include #include namespace Web { class EditEventHandler { public: - explicit EditEventHandler(HTML::BrowsingContext& browsing_context) - : m_browsing_context(browsing_context) + explicit EditEventHandler(HTML::Navigable& navigable) + : m_navigable(navigable) { } @@ -26,7 +27,7 @@ public: virtual void handle_insert(JS::NonnullGCPtr, String); private: - JS::NonnullGCPtr m_browsing_context; + JS::NonnullGCPtr m_navigable; }; } diff --git a/Userland/Libraries/LibWeb/Page/EventHandler.cpp b/Userland/Libraries/LibWeb/Page/EventHandler.cpp index 76aacfd4be5..74f7ef41da4 100644 --- a/Userland/Libraries/LibWeb/Page/EventHandler.cpp +++ b/Userland/Libraries/LibWeb/Page/EventHandler.cpp @@ -129,9 +129,9 @@ static CSSPixelPoint compute_mouse_event_offset(CSSPixelPoint position, Layout:: }; } -EventHandler::EventHandler(Badge, HTML::BrowsingContext& browsing_context) - : m_browsing_context(browsing_context) - , m_edit_event_handler(make(browsing_context)) +EventHandler::EventHandler(Badge, HTML::Navigable& navigable) + : m_navigable(navigable) + , m_edit_event_handler(make(navigable)) { } @@ -139,26 +139,26 @@ EventHandler::~EventHandler() = default; Painting::PaintableBox* EventHandler::paint_root() { - if (!m_browsing_context->active_document()) + if (!m_navigable->active_document()) return nullptr; - return m_browsing_context->active_document()->paintable_box(); + return m_navigable->active_document()->paintable_box(); } Painting::PaintableBox const* EventHandler::paint_root() const { - if (!m_browsing_context->active_document()) + if (!m_navigable->active_document()) return nullptr; - return m_browsing_context->active_document()->paintable_box(); + return m_navigable->active_document()->paintable_box(); } bool EventHandler::handle_mousewheel(CSSPixelPoint position, CSSPixelPoint screen_position, u32 button, u32 buttons, u32 modifiers, int wheel_delta_x, int wheel_delta_y) { - if (!m_browsing_context->active_document()) + if (!m_navigable->active_document()) return false; - if (!m_browsing_context->active_document()->is_fully_active()) + if (!m_navigable->active_document()->is_fully_active()) return false; - m_browsing_context->active_document()->update_layout(); + m_navigable->active_document()->update_layout(); if (!paint_root()) return false; @@ -191,7 +191,7 @@ bool EventHandler::handle_mousewheel(CSSPixelPoint position, CSSPixelPoint scree if (is(*node)) { auto& iframe = static_cast(*node); auto position_in_iframe = position.translated(compute_mouse_event_offset({}, paintable->layout_node())); - iframe.nested_browsing_context()->event_handler().handle_mousewheel(position_in_iframe, screen_position, button, buttons, modifiers, wheel_delta_x, wheel_delta_y); + iframe.content_navigable()->event_handler().handle_mousewheel(position_in_iframe, screen_position, button, buttons, modifiers, wheel_delta_x, wheel_delta_y); return false; } @@ -204,7 +204,7 @@ bool EventHandler::handle_mousewheel(CSSPixelPoint position, CSSPixelPoint scree auto client_offset = compute_mouse_event_client_offset(position); auto page_offset = compute_mouse_event_page_offset(client_offset); if (node->dispatch_event(UIEvents::WheelEvent::create_from_platform_event(node->realm(), UIEvents::EventNames::wheel, screen_position, page_offset, client_offset, offset, wheel_delta_x, wheel_delta_y, button, buttons, modifiers).release_value_but_fixme_should_propagate_errors())) { - m_browsing_context->active_window()->scroll_by(wheel_delta_x, wheel_delta_y); + m_navigable->active_window()->scroll_by(wheel_delta_x, wheel_delta_y); } handled_event = true; @@ -216,12 +216,12 @@ bool EventHandler::handle_mousewheel(CSSPixelPoint position, CSSPixelPoint scree bool EventHandler::handle_mouseup(CSSPixelPoint position, CSSPixelPoint screen_position, u32 button, u32 buttons, u32 modifiers) { - if (!m_browsing_context->active_document()) + if (!m_navigable->active_document()) return false; - if (!m_browsing_context->active_document()->is_fully_active()) + if (!m_navigable->active_document()->is_fully_active()) return false; - m_browsing_context->active_document()->update_layout(); + m_navigable->active_document()->update_layout(); if (!paint_root()) return false; @@ -248,8 +248,8 @@ bool EventHandler::handle_mouseup(CSSPixelPoint position, CSSPixelPoint screen_p if (node) { if (is(*node)) { - if (auto* nested_browsing_context = static_cast(*node).nested_browsing_context()) - return nested_browsing_context->event_handler().handle_mouseup(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, button, buttons, modifiers); + if (auto content_navigable = static_cast(*node).content_navigable()) + return content_navigable->event_handler().handle_mouseup(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, button, buttons, modifiers); return false; } @@ -292,23 +292,23 @@ bool EventHandler::handle_mouseup(CSSPixelPoint position, CSSPixelPoint screen_p // // https://html.spec.whatwg.org/multipage/document-sequences.html#the-rules-for-choosing-a-navigable - auto top_level_position = m_browsing_context->active_document()->navigable()->to_top_level_position(position); + auto top_level_position = m_navigable->active_document()->navigable()->to_top_level_position(position); if (JS::GCPtr link = node->enclosing_link_element()) { - JS::NonnullGCPtr document = *m_browsing_context->active_document(); + JS::NonnullGCPtr document = *m_navigable->active_document(); auto href = link->href(); auto url = document->parse_url(href); dbgln("Web::EventHandler: Clicking on a link to {}", url); if (button == GUI::MouseButton::Middle) { - m_browsing_context->page().client().page_did_middle_click_link(url, link->target().to_byte_string(), modifiers); + m_navigable->page().client().page_did_middle_click_link(url, link->target().to_byte_string(), modifiers); } else if (button == GUI::MouseButton::Secondary) { - m_browsing_context->page().client().page_did_request_link_context_menu(top_level_position, url, link->target().to_byte_string(), modifiers); + m_navigable->page().client().page_did_request_link_context_menu(top_level_position, url, link->target().to_byte_string(), modifiers); } } else if (button == GUI::MouseButton::Secondary) { if (is(*node)) { auto& image_element = verify_cast(*node); auto image_url = image_element.document().parse_url(image_element.src()); - m_browsing_context->page().client().page_did_request_image_context_menu(top_level_position, image_url, "", modifiers, image_element.bitmap()); + m_navigable->page().client().page_did_request_image_context_menu(top_level_position, image_url, "", modifiers, image_element.bitmap()); } else if (is(*node)) { auto& media_element = verify_cast(*node); @@ -321,9 +321,9 @@ bool EventHandler::handle_mouseup(CSSPixelPoint position, CSSPixelPoint screen_p .is_looping = media_element.has_attribute(HTML::AttributeNames::loop), }; - m_browsing_context->page().did_request_media_context_menu(media_element.unique_id(), top_level_position, "", modifiers, move(menu)); + m_navigable->page().did_request_media_context_menu(media_element.unique_id(), top_level_position, "", modifiers, move(menu)); } else { - m_browsing_context->page().client().page_did_request_context_menu(top_level_position); + m_navigable->page().client().page_did_request_context_menu(top_level_position); } } } @@ -338,17 +338,17 @@ after_node_use: bool EventHandler::handle_mousedown(CSSPixelPoint position, CSSPixelPoint screen_position, u32 button, u32 buttons, u32 modifiers) { - if (!m_browsing_context->active_document()) + if (!m_navigable->active_document()) return false; - if (!m_browsing_context->active_document()->is_fully_active()) + if (!m_navigable->active_document()->is_fully_active()) return false; - m_browsing_context->active_document()->update_layout(); + m_navigable->active_document()->update_layout(); if (!paint_root()) return false; - JS::NonnullGCPtr document = *m_browsing_context->active_document(); + JS::NonnullGCPtr document = *m_navigable->active_document(); JS::GCPtr node; { @@ -374,12 +374,12 @@ bool EventHandler::handle_mousedown(CSSPixelPoint position, CSSPixelPoint screen return false; if (is(*node)) { - if (auto* nested_browsing_context = static_cast(*node).nested_browsing_context()) - return nested_browsing_context->event_handler().handle_mousedown(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, button, buttons, modifiers); + if (auto content_navigable = static_cast(*node).content_navigable()) + return content_navigable->event_handler().handle_mousedown(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, button, buttons, modifiers); return false; } - m_browsing_context->page().set_focused_browsing_context({}, m_browsing_context); + m_navigable->page().set_focused_navigable({}, m_navigable); // Search for the first parent of the hit target that's an element. // "The click event type MUST be dispatched on the topmost event target indicated by the pointer." (https://www.w3.org/TR/uievents/#event-type-click) @@ -424,7 +424,7 @@ bool EventHandler::handle_mousedown(CSSPixelPoint position, CSSPixelPoint screen // FIXME: This is all rather strange. Find a better solution. if (!did_focus_something || paintable->dom_node()->is_editable()) { auto& realm = document->realm(); - m_browsing_context->set_cursor_position(DOM::Position::create(realm, *paintable->dom_node(), result->index_in_node)); + m_navigable->set_cursor_position(DOM::Position::create(realm, *paintable->dom_node(), result->index_in_node)); if (auto selection = document->get_selection()) { (void)selection->set_base_and_extent(*paintable->dom_node(), result->index_in_node, *paintable->dom_node(), result->index_in_node); } @@ -438,17 +438,17 @@ bool EventHandler::handle_mousedown(CSSPixelPoint position, CSSPixelPoint screen bool EventHandler::handle_mousemove(CSSPixelPoint position, CSSPixelPoint screen_position, u32 buttons, u32 modifiers) { - if (!m_browsing_context->active_document()) + if (!m_navigable->active_document()) return false; - if (!m_browsing_context->active_document()->is_fully_active()) + if (!m_navigable->active_document()->is_fully_active()) return false; - m_browsing_context->active_document()->update_layout(); + m_navigable->active_document()->update_layout(); if (!paint_root()) return false; - auto& document = *m_browsing_context->active_document(); + auto& document = *m_navigable->active_document(); auto& realm = document.realm(); bool hovered_node_changed = false; @@ -471,14 +471,14 @@ bool EventHandler::handle_mousemove(CSSPixelPoint position, CSSPixelPoint screen return false; // FIXME: It feels a bit aggressive to always update the cursor like this. - m_browsing_context->page().client().page_did_request_cursor_change(Gfx::StandardCursor::None); + m_navigable->page().client().page_did_request_cursor_change(Gfx::StandardCursor::None); } auto node = dom_node_for_event_dispatch(*paintable); if (node && is(*node)) { - if (auto* nested_browsing_context = static_cast(*node).nested_browsing_context()) - return nested_browsing_context->event_handler().handle_mousemove(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, buttons, modifiers); + if (auto content_navigable = static_cast(*node).content_navigable()) + return content_navigable->event_handler().handle_mousemove(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, buttons, modifiers); return false; } @@ -529,7 +529,7 @@ bool EventHandler::handle_mousemove(CSSPixelPoint position, CSSPixelPoint screen if (m_in_mouse_selection) { auto hit = paint_root()->hit_test(position, Painting::HitTestType::TextCursor); if (start_index.has_value() && hit.has_value() && hit->dom_node()) { - m_browsing_context->set_cursor_position(DOM::Position::create(realm, *hit->dom_node(), *start_index)); + m_navigable->set_cursor_position(DOM::Position::create(realm, *hit->dom_node(), *start_index)); if (auto selection = document.get_selection()) { auto anchor_node = selection->anchor_node(); if (anchor_node) @@ -542,14 +542,14 @@ bool EventHandler::handle_mousemove(CSSPixelPoint position, CSSPixelPoint screen } } - auto& page = m_browsing_context->page(); + auto& page = m_navigable->page(); page.client().page_did_request_cursor_change(hovered_node_cursor); if (hovered_node_changed) { JS::GCPtr hovered_html_element = document.hovered_node() ? document.hovered_node()->enclosing_html_element_with_attribute(HTML::AttributeNames::title) : nullptr; if (hovered_html_element && hovered_html_element->title().has_value()) { - page.client().page_did_enter_tooltip_area(m_browsing_context->active_document()->navigable()->to_top_level_position(position), hovered_html_element->title()->to_byte_string()); + page.client().page_did_enter_tooltip_area(m_navigable->active_document()->navigable()->to_top_level_position(position), hovered_html_element->title()->to_byte_string()); } else { page.client().page_did_leave_tooltip_area(); } @@ -564,12 +564,12 @@ bool EventHandler::handle_mousemove(CSSPixelPoint position, CSSPixelPoint screen bool EventHandler::handle_doubleclick(CSSPixelPoint position, CSSPixelPoint screen_position, u32 button, u32 buttons, u32 modifiers) { - if (!m_browsing_context->active_document()) + if (!m_navigable->active_document()) return false; - if (!m_browsing_context->active_document()->is_fully_active()) + if (!m_navigable->active_document()->is_fully_active()) return false; - m_browsing_context->active_document()->update_layout(); + m_navigable->active_document()->update_layout(); if (!paint_root()) return false; @@ -595,8 +595,8 @@ bool EventHandler::handle_doubleclick(CSSPixelPoint position, CSSPixelPoint scre return false; if (is(*node)) { - if (auto* nested_browsing_context = static_cast(*node).nested_browsing_context()) - return nested_browsing_context->event_handler().handle_doubleclick(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, button, buttons, modifiers); + if (auto content_navigable = static_cast(*node).content_navigable()) + return content_navigable->event_handler().handle_doubleclick(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, button, buttons, modifiers); return false; } @@ -648,7 +648,7 @@ bool EventHandler::handle_doubleclick(CSSPixelPoint position, CSSPixelPoint scre }(); auto& realm = node->document().realm(); - m_browsing_context->set_cursor_position(DOM::Position::create(realm, hit_dom_node, first_word_break_after)); + m_navigable->set_cursor_position(DOM::Position::create(realm, hit_dom_node, first_word_break_after)); if (auto selection = node->document().get_selection()) { (void)selection->set_base_and_extent(hit_dom_node, first_word_break_before, hit_dom_node, first_word_break_after); } @@ -660,16 +660,16 @@ bool EventHandler::handle_doubleclick(CSSPixelPoint position, CSSPixelPoint scre bool EventHandler::focus_next_element() { - if (!m_browsing_context->active_document()) + if (!m_navigable->active_document()) return false; - if (!m_browsing_context->active_document()->is_fully_active()) + if (!m_navigable->active_document()->is_fully_active()) return false; - auto* element = m_browsing_context->active_document()->focused_element(); + auto* element = m_navigable->active_document()->focused_element(); if (!element) { - element = m_browsing_context->active_document()->first_child_of_type(); + element = m_navigable->active_document()->first_child_of_type(); if (element && element->is_focusable()) { - m_browsing_context->active_document()->set_focused_element(element); + m_navigable->active_document()->set_focused_element(element); return true; } } @@ -677,22 +677,22 @@ bool EventHandler::focus_next_element() for (element = element->next_element_in_pre_order(); element && !element->is_focusable(); element = element->next_element_in_pre_order()) ; - m_browsing_context->active_document()->set_focused_element(element); + m_navigable->active_document()->set_focused_element(element); return element; } bool EventHandler::focus_previous_element() { - if (!m_browsing_context->active_document()) + if (!m_navigable->active_document()) return false; - if (!m_browsing_context->active_document()->is_fully_active()) + if (!m_navigable->active_document()->is_fully_active()) return false; - auto* element = m_browsing_context->active_document()->focused_element(); + auto* element = m_navigable->active_document()->focused_element(); if (!element) { - element = m_browsing_context->active_document()->last_child_of_type(); + element = m_navigable->active_document()->last_child_of_type(); if (element && element->is_focusable()) { - m_browsing_context->active_document()->set_focused_element(element); + m_navigable->active_document()->set_focused_element(element); return true; } } @@ -700,7 +700,7 @@ bool EventHandler::focus_previous_element() for (element = element->previous_element_in_pre_order(); element && !element->is_focusable(); element = element->previous_element_in_pre_order()) ; - m_browsing_context->active_document()->set_focused_element(element); + m_navigable->active_document()->set_focused_element(element); return element; } @@ -713,9 +713,9 @@ constexpr bool should_ignore_keydown_event(u32 code_point, u32 modifiers) return code_point == 0 || code_point == 27; } -bool EventHandler::fire_keyboard_event(FlyString const& event_name, HTML::BrowsingContext& browsing_context, KeyCode key, u32 modifiers, u32 code_point) +bool EventHandler::fire_keyboard_event(FlyString const& event_name, HTML::Navigable& navigable, KeyCode key, u32 modifiers, u32 code_point) { - JS::GCPtr document = browsing_context.active_document(); + JS::GCPtr document = navigable.active_document(); if (!document) return false; if (!document->is_fully_active()) @@ -724,8 +724,8 @@ bool EventHandler::fire_keyboard_event(FlyString const& event_name, HTML::Browsi if (JS::GCPtr focused_element = document->focused_element()) { if (is(*focused_element)) { auto& navigable_container = verify_cast(*focused_element); - if (navigable_container.nested_browsing_context()) - return fire_keyboard_event(event_name, *navigable_container.nested_browsing_context(), key, modifiers, code_point); + if (navigable_container.content_navigable()) + return fire_keyboard_event(event_name, *navigable_container.content_navigable(), key, modifiers, code_point); } auto event = UIEvents::KeyboardEvent::create_from_platform_event(document->realm(), event_name, key, modifiers, code_point); @@ -743,12 +743,12 @@ bool EventHandler::fire_keyboard_event(FlyString const& event_name, HTML::Browsi bool EventHandler::handle_keydown(KeyCode key, u32 modifiers, u32 code_point) { - if (!m_browsing_context->active_document()) + if (!m_navigable->active_document()) return false; - if (!m_browsing_context->active_document()->is_fully_active()) + if (!m_navigable->active_document()->is_fully_active()) return false; - JS::NonnullGCPtr document = *m_browsing_context->active_document(); + JS::NonnullGCPtr document = *m_navigable->active_document(); if (!document->layout_node()) return false; @@ -766,7 +766,7 @@ bool EventHandler::handle_keydown(KeyCode key, u32 modifiers, u32 code_point) selection->remove_all_ranges(); // FIXME: This doesn't work for some reason? - m_browsing_context->set_cursor_position(DOM::Position::create(realm, *range->start_container(), range->start_offset())); + m_navigable->set_cursor_position(DOM::Position::create(realm, *range->start_container(), range->start_offset())); if (key == KeyCode::Key_Backspace || key == KeyCode::Key_Delete) { m_edit_event_handler->handle_delete(*range); @@ -775,69 +775,69 @@ bool EventHandler::handle_keydown(KeyCode key, u32 modifiers, u32 code_point) // FIXME: Text editing shortcut keys (copy/paste etc.) should be handled here. if (!should_ignore_keydown_event(code_point, modifiers)) { m_edit_event_handler->handle_delete(*range); - m_edit_event_handler->handle_insert(JS::NonnullGCPtr { *m_browsing_context->cursor_position() }, code_point); - m_browsing_context->increment_cursor_position_offset(); + m_edit_event_handler->handle_insert(JS::NonnullGCPtr { *m_navigable->cursor_position() }, code_point); + m_navigable->increment_cursor_position_offset(); return true; } } } - if (auto* element = m_browsing_context->active_document()->focused_element(); is(element)) { + if (auto* element = m_navigable->active_document()->focused_element(); is(element)) { auto& media_element = static_cast(*element); media_element.handle_keydown({}, key).release_value_but_fixme_should_propagate_errors(); } - bool continue_ = fire_keyboard_event(UIEvents::EventNames::keydown, m_browsing_context, key, modifiers, code_point); + bool continue_ = fire_keyboard_event(UIEvents::EventNames::keydown, m_navigable, key, modifiers, code_point); if (!continue_) return false; - if (m_browsing_context->cursor_position() && m_browsing_context->cursor_position()->node()->is_editable()) { + if (m_navigable->cursor_position() && m_navigable->cursor_position()->node()->is_editable()) { if (key == KeyCode::Key_Backspace) { - if (!m_browsing_context->decrement_cursor_position_offset()) { + if (!m_navigable->decrement_cursor_position_offset()) { // FIXME: Move to the previous node and delete the last character there. return true; } - m_edit_event_handler->handle_delete_character_after(*m_browsing_context->cursor_position()); + m_edit_event_handler->handle_delete_character_after(*m_navigable->cursor_position()); return true; } if (key == KeyCode::Key_Delete) { - if (m_browsing_context->cursor_position()->offset_is_at_end_of_node()) { + if (m_navigable->cursor_position()->offset_is_at_end_of_node()) { // FIXME: Move to the next node and delete the first character there. return true; } - m_edit_event_handler->handle_delete_character_after(*m_browsing_context->cursor_position()); + m_edit_event_handler->handle_delete_character_after(*m_navigable->cursor_position()); return true; } if (key == KeyCode::Key_Right) { - if (!m_browsing_context->increment_cursor_position_offset()) { + if (!m_navigable->increment_cursor_position_offset()) { // FIXME: Move to the next node. } return true; } if (key == KeyCode::Key_Left) { - if (!m_browsing_context->decrement_cursor_position_offset()) { + if (!m_navigable->decrement_cursor_position_offset()) { // FIXME: Move to the previous node. } return true; } if (key == KeyCode::Key_Home) { - auto& cursor_position_node = *m_browsing_context->cursor_position()->node(); + auto& cursor_position_node = *m_navigable->cursor_position()->node(); if (cursor_position_node.is_text()) - m_browsing_context->set_cursor_position(DOM::Position::create(realm, cursor_position_node, 0)); + m_navigable->set_cursor_position(DOM::Position::create(realm, cursor_position_node, 0)); return true; } if (key == KeyCode::Key_End) { - auto& cursor_position_node = *m_browsing_context->cursor_position()->node(); + auto& cursor_position_node = *m_navigable->cursor_position()->node(); if (cursor_position_node.is_text()) { auto& text_node = static_cast(cursor_position_node); - m_browsing_context->set_cursor_position(DOM::Position::create(realm, text_node, (unsigned)text_node.data().bytes().size())); + m_navigable->set_cursor_position(DOM::Position::create(realm, text_node, (unsigned)text_node.data().bytes().size())); } return true; } if (key == KeyCode::Key_Return) { HTML::HTMLInputElement* input_element = nullptr; - if (auto node = m_browsing_context->cursor_position()->node()) { + if (auto node = m_navigable->cursor_position()->node()) { if (node->is_text()) { auto& text_node = static_cast(*node); if (is(text_node.editable_text_node_owner())) @@ -858,30 +858,30 @@ bool EventHandler::handle_keydown(KeyCode key, u32 modifiers, u32 code_point) } // FIXME: Text editing shortcut keys (copy/paste etc.) should be handled here. if (!should_ignore_keydown_event(code_point, modifiers)) { - m_edit_event_handler->handle_insert(JS::NonnullGCPtr { *m_browsing_context->cursor_position() }, code_point); - m_browsing_context->increment_cursor_position_offset(); + m_edit_event_handler->handle_insert(JS::NonnullGCPtr { *m_navigable->cursor_position() }, code_point); + m_navigable->increment_cursor_position_offset(); return true; } } // FIXME: Work out and implement the difference between this and keydown. - return !fire_keyboard_event(UIEvents::EventNames::keypress, m_browsing_context, key, modifiers, code_point); + return !fire_keyboard_event(UIEvents::EventNames::keypress, m_navigable, key, modifiers, code_point); } bool EventHandler::handle_keyup(KeyCode key, u32 modifiers, u32 code_point) { - return !fire_keyboard_event(UIEvents::EventNames::keyup, m_browsing_context, key, modifiers, code_point); + return !fire_keyboard_event(UIEvents::EventNames::keyup, m_navigable, key, modifiers, code_point); } void EventHandler::handle_paste(String const& text) { - auto* active_document = m_browsing_context->active_document(); + auto active_document = m_navigable->active_document(); if (!active_document) return; if (!active_document->is_fully_active()) return; - if (auto cursor_position = m_browsing_context->cursor_position()) { + if (auto cursor_position = m_navigable->cursor_position()) { active_document->update_layout(); m_edit_event_handler->handle_insert(*cursor_position, text); cursor_position->set_offset(cursor_position->offset() + text.code_points().length()); @@ -898,7 +898,7 @@ CSSPixelPoint EventHandler::compute_mouse_event_client_offset(CSSPixelPoint even // https://w3c.github.io/csswg-drafts/cssom-view/#dom-mouseevent-clientx // The clientX attribute must return the x-coordinate of the position where the event occurred relative to the origin of the viewport. - auto scroll_offset = m_browsing_context->active_document()->navigable()->viewport_scroll_offset(); + auto scroll_offset = m_navigable->active_document()->navigable()->viewport_scroll_offset(); return event_page_position.translated(-scroll_offset); } @@ -908,7 +908,7 @@ CSSPixelPoint EventHandler::compute_mouse_event_page_offset(CSSPixelPoint event_ // FIXME: 1. If the event’s dispatch flag is set, return the horizontal coordinate of the position where the event occurred relative to the origin of the initial containing block and terminate these steps. // 2. Let offset be the value of the scrollX attribute of the event’s associated Window object, if there is one, or zero otherwise. - auto scroll_offset = m_browsing_context->active_document()->navigable()->viewport_scroll_offset(); + auto scroll_offset = m_navigable->active_document()->navigable()->viewport_scroll_offset(); // 3. Return the sum of offset and the value of the event’s clientX attribute. return event_client_offset.translated(scroll_offset); diff --git a/Userland/Libraries/LibWeb/Page/EventHandler.h b/Userland/Libraries/LibWeb/Page/EventHandler.h index 06980b78851..ce9543eb7ba 100644 --- a/Userland/Libraries/LibWeb/Page/EventHandler.h +++ b/Userland/Libraries/LibWeb/Page/EventHandler.h @@ -22,7 +22,7 @@ namespace Web { class EventHandler { public: - explicit EventHandler(Badge, HTML::BrowsingContext&); + explicit EventHandler(Badge, HTML::Navigable&); ~EventHandler(); bool handle_mouseup(CSSPixelPoint, CSSPixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers); @@ -46,7 +46,7 @@ private: bool focus_next_element(); bool focus_previous_element(); - bool fire_keyboard_event(FlyString const& event_name, HTML::BrowsingContext& browsing_context, KeyCode key, unsigned modifiers, u32 code_point); + bool fire_keyboard_event(FlyString const& event_name, HTML::Navigable&, KeyCode, unsigned modifiers, u32 code_point); CSSPixelPoint compute_mouse_event_client_offset(CSSPixelPoint event_page_position) const; CSSPixelPoint compute_mouse_event_page_offset(CSSPixelPoint event_client_offset) const; CSSPixelPoint compute_mouse_event_movement(CSSPixelPoint event_client_offset) const; @@ -60,7 +60,7 @@ private: Painting::PaintableBox* paint_root(); Painting::PaintableBox const* paint_root() const; - JS::NonnullGCPtr m_browsing_context; + JS::NonnullGCPtr m_navigable; bool m_in_mouse_selection { false }; diff --git a/Userland/Libraries/LibWeb/Page/Page.cpp b/Userland/Libraries/LibWeb/Page/Page.cpp index 7cb246af910..c369a568922 100644 --- a/Userland/Libraries/LibWeb/Page/Page.cpp +++ b/Userland/Libraries/LibWeb/Page/Page.cpp @@ -46,16 +46,16 @@ void Page::visit_edges(JS::Cell::Visitor& visitor) visitor.visit(m_client); } -HTML::BrowsingContext& Page::focused_context() +HTML::Navigable& Page::focused_navigable() { - if (m_focused_context) - return *m_focused_context; - return top_level_browsing_context(); + if (m_focused_navigable) + return *m_focused_navigable; + return top_level_traversable(); } -void Page::set_focused_browsing_context(Badge, HTML::BrowsingContext& browsing_context) +void Page::set_focused_navigable(Badge, HTML::Navigable& navigable) { - m_focused_context = browsing_context.make_weak_ptr(); + m_focused_navigable = navigable.make_weak_ptr(); } void Page::load(URL::URL const& url) @@ -165,37 +165,37 @@ DevicePixelRect Page::rounded_device_rect(CSSPixelRect rect) const bool Page::handle_mouseup(DevicePixelPoint position, DevicePixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers) { - return top_level_browsing_context().event_handler().handle_mouseup(device_to_css_point(position), device_to_css_point(screen_position), button, buttons, modifiers); + return top_level_traversable()->event_handler().handle_mouseup(device_to_css_point(position), device_to_css_point(screen_position), button, buttons, modifiers); } bool Page::handle_mousedown(DevicePixelPoint position, DevicePixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers) { - return top_level_browsing_context().event_handler().handle_mousedown(device_to_css_point(position), device_to_css_point(screen_position), button, buttons, modifiers); + return top_level_traversable()->event_handler().handle_mousedown(device_to_css_point(position), device_to_css_point(screen_position), button, buttons, modifiers); } bool Page::handle_mousemove(DevicePixelPoint position, DevicePixelPoint screen_position, unsigned buttons, unsigned modifiers) { - return top_level_browsing_context().event_handler().handle_mousemove(device_to_css_point(position), device_to_css_point(screen_position), buttons, modifiers); + return top_level_traversable()->event_handler().handle_mousemove(device_to_css_point(position), device_to_css_point(screen_position), buttons, modifiers); } bool Page::handle_mousewheel(DevicePixelPoint position, DevicePixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers, DevicePixels wheel_delta_x, DevicePixels wheel_delta_y) { - return top_level_browsing_context().event_handler().handle_mousewheel(device_to_css_point(position), device_to_css_point(screen_position), button, buttons, modifiers, wheel_delta_x.value(), wheel_delta_y.value()); + return top_level_traversable()->event_handler().handle_mousewheel(device_to_css_point(position), device_to_css_point(screen_position), button, buttons, modifiers, wheel_delta_x.value(), wheel_delta_y.value()); } bool Page::handle_doubleclick(DevicePixelPoint position, DevicePixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers) { - return top_level_browsing_context().event_handler().handle_doubleclick(device_to_css_point(position), device_to_css_point(screen_position), button, buttons, modifiers); + return top_level_traversable()->event_handler().handle_doubleclick(device_to_css_point(position), device_to_css_point(screen_position), button, buttons, modifiers); } bool Page::handle_keydown(KeyCode key, unsigned modifiers, u32 code_point) { - return focused_context().event_handler().handle_keydown(key, modifiers, code_point); + return focused_navigable().event_handler().handle_keydown(key, modifiers, code_point); } bool Page::handle_keyup(KeyCode key, unsigned modifiers, u32 code_point) { - return focused_context().event_handler().handle_keyup(key, modifiers, code_point); + return focused_navigable().event_handler().handle_keyup(key, modifiers, code_point); } void Page::set_top_level_traversable(JS::NonnullGCPtr navigable) diff --git a/Userland/Libraries/LibWeb/Page/Page.h b/Userland/Libraries/LibWeb/Page/Page.h index 340c977ad48..5c730b668df 100644 --- a/Userland/Libraries/LibWeb/Page/Page.h +++ b/Userland/Libraries/LibWeb/Page/Page.h @@ -65,10 +65,10 @@ public: JS::NonnullGCPtr top_level_traversable() const; - HTML::BrowsingContext& focused_context(); - HTML::BrowsingContext const& focused_context() const { return const_cast(this)->focused_context(); } + HTML::Navigable& focused_navigable(); + HTML::Navigable const& focused_navigable() const { return const_cast(this)->focused_navigable(); } - void set_focused_browsing_context(Badge, HTML::BrowsingContext&); + void set_focused_navigable(Badge, HTML::Navigable&); void load(URL::URL const&); @@ -186,7 +186,7 @@ private: JS::NonnullGCPtr m_client; - WeakPtr m_focused_context; + WeakPtr m_focused_navigable; JS::GCPtr m_top_level_traversable; diff --git a/Userland/Libraries/LibWeb/Painting/LabelablePaintable.cpp b/Userland/Libraries/LibWeb/Painting/LabelablePaintable.cpp index e013333c670..d6b4207abb5 100644 --- a/Userland/Libraries/LibWeb/Painting/LabelablePaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/LabelablePaintable.cpp @@ -42,7 +42,7 @@ LabelablePaintable::DispatchEventOfSameName LabelablePaintable::handle_mousedown set_being_pressed(true); m_tracking_mouse = true; - browsing_context().event_handler().set_mouse_event_tracking_paintable(this); + navigable()->event_handler().set_mouse_event_tracking_paintable(this); return DispatchEventOfSameName::Yes; } @@ -57,7 +57,7 @@ LabelablePaintable::DispatchEventOfSameName LabelablePaintable::handle_mouseup(B set_being_pressed(false); m_tracking_mouse = false; - browsing_context().event_handler().set_mouse_event_tracking_paintable(nullptr); + navigable()->event_handler().set_mouse_event_tracking_paintable(nullptr); return DispatchEventOfSameName::Yes; } diff --git a/Userland/Libraries/LibWeb/Painting/MediaPaintable.cpp b/Userland/Libraries/LibWeb/Painting/MediaPaintable.cpp index 0123c2a61e4..88081139316 100644 --- a/Userland/Libraries/LibWeb/Painting/MediaPaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/MediaPaintable.cpp @@ -284,7 +284,7 @@ MediaPaintable::DispatchEventOfSameName MediaPaintable::handle_mousedown(Badge(browsing_context()).event_handler().set_mouse_event_tracking_paintable(this); + const_cast(*navigable()).event_handler().set_mouse_event_tracking_paintable(this); return DispatchEventOfSameName::Yes; } @@ -306,7 +306,7 @@ MediaPaintable::DispatchEventOfSameName MediaPaintable::handle_mouseup(Badge(browsing_context()).event_handler().set_mouse_event_tracking_paintable(nullptr); + const_cast(*navigable()).event_handler().set_mouse_event_tracking_paintable(nullptr); media_element.set_layout_mouse_tracking_component({}, {}); return DispatchEventOfSameName::Yes; diff --git a/Userland/Libraries/LibWeb/Painting/NestedBrowsingContextPaintable.cpp b/Userland/Libraries/LibWeb/Painting/NestedBrowsingContextPaintable.cpp index ba799c2167f..314650b3515 100644 --- a/Userland/Libraries/LibWeb/Painting/NestedBrowsingContextPaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/NestedBrowsingContextPaintable.cpp @@ -65,7 +65,7 @@ void NestedBrowsingContextPaintable::paint(PaintContext& context, PaintPhase pha context.recording_painter().restore(); if constexpr (HIGHLIGHT_FOCUSED_FRAME_DEBUG) { - if (layout_box().dom_node().nested_browsing_context()->is_focused_context()) { + if (layout_box().dom_node().content_navigable()->is_focused()) { context.recording_painter().draw_rect(clip_rect.to_type(), Color::Cyan); } } diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp index e22df2a0032..6c7bd259192 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -559,19 +559,19 @@ void PaintableBox::clear_clip_overflow_rect(PaintContext& context, PaintPhase ph void paint_cursor_if_needed(PaintContext& context, TextPaintable const& paintable, PaintableFragment const& fragment) { - auto const& browsing_context = paintable.browsing_context(); + auto const& navigable = *paintable.navigable(); - if (!browsing_context.is_focused_context()) + if (!navigable.is_focused()) return; - if (!browsing_context.cursor_blink_state()) + if (!navigable.cursor_blink_state()) return; - if (browsing_context.cursor_position()->node() != paintable.dom_node()) + if (navigable.cursor_position()->node() != paintable.dom_node()) return; // NOTE: This checks if the cursor is before the start or after the end of the fragment. If it is at the end, after all text, it should still be painted. - if (browsing_context.cursor_position()->offset() < (unsigned)fragment.start() || browsing_context.cursor_position()->offset() > (unsigned)(fragment.start() + fragment.length())) + if (navigable.cursor_position()->offset() < (unsigned)fragment.start() || navigable.cursor_position()->offset() > (unsigned)(fragment.start() + fragment.length())) return; if (!fragment.layout_node().dom_node() || !fragment.layout_node().dom_node()->is_editable()) @@ -581,7 +581,7 @@ void paint_cursor_if_needed(PaintContext& context, TextPaintable const& paintabl auto text = fragment.string_view(); CSSPixelRect cursor_rect { - fragment_rect.x() + CSSPixels::nearest_value_for(paintable.layout_node().first_available_font().width(text.substring_view(0, paintable.browsing_context().cursor_position()->offset() - fragment.start()))), + fragment_rect.x() + CSSPixels::nearest_value_for(paintable.layout_node().first_available_font().width(text.substring_view(0, navigable.cursor_position()->offset() - fragment.start()))), fragment_rect.top(), 1, fragment_rect.height() diff --git a/Userland/Libraries/LibWeb/Painting/TextPaintable.cpp b/Userland/Libraries/LibWeb/Painting/TextPaintable.cpp index c5cfcbe59c5..78bb29ded89 100644 --- a/Userland/Libraries/LibWeb/Painting/TextPaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/TextPaintable.cpp @@ -43,7 +43,7 @@ TextPaintable::DispatchEventOfSameName TextPaintable::handle_mousedown(Badge(label)->handle_mousedown_on_label({}, position, button); - const_cast(browsing_context()).event_handler().set_mouse_event_tracking_paintable(this); + const_cast(*navigable()).event_handler().set_mouse_event_tracking_paintable(this); return DispatchEventOfSameName::Yes; } @@ -54,7 +54,7 @@ TextPaintable::DispatchEventOfSameName TextPaintable::handle_mouseup(Badge(label)->handle_mouseup_on_label({}, position, button); - const_cast(browsing_context()).event_handler().set_mouse_event_tracking_paintable(nullptr); + const_cast(*navigable()).event_handler().set_mouse_event_tracking_paintable(nullptr); return DispatchEventOfSameName::Yes; } diff --git a/Userland/Services/WebContent/ConnectionFromClient.cpp b/Userland/Services/WebContent/ConnectionFromClient.cpp index 2b3f15df094..0ca12863131 100644 --- a/Userland/Services/WebContent/ConnectionFromClient.cpp +++ b/Userland/Services/WebContent/ConnectionFromClient.cpp @@ -552,7 +552,7 @@ void ConnectionFromClient::inspect_dom_node(u64 page_id, i32 node_id, Optionalpage().focused_context().active_document()->style_computer().compute_style(element, pseudo_element); + auto pseudo_element_style = page->page().focused_navigable().active_document()->style_computer().compute_style(element, pseudo_element); ByteString computed_values = serialize_json(pseudo_element_style); ByteString resolved_values = "{}"; ByteString custom_properties_json = serialize_custom_properties_json(element, pseudo_element); @@ -817,20 +817,20 @@ Messages::WebContentServer::DumpGcGraphResponse ConnectionFromClient::dump_gc_gr Messages::WebContentServer::GetSelectedTextResponse ConnectionFromClient::get_selected_text(u64 page_id) { if (auto page = this->page(page_id); page.has_value()) - return page->page().focused_context().selected_text().to_byte_string(); + return page->page().focused_navigable().selected_text().to_byte_string(); return ByteString {}; } void ConnectionFromClient::select_all(u64 page_id) { if (auto page = this->page(page_id); page.has_value()) - page->page().focused_context().select_all(); + page->page().focused_navigable().select_all(); } void ConnectionFromClient::paste(u64 page_id, String const& text) { if (auto page = this->page(page_id); page.has_value()) - page->page().focused_context().paste(text); + page->page().focused_navigable().paste(text); } Messages::WebContentServer::DumpLayoutTreeResponse ConnectionFromClient::dump_layout_tree(u64 page_id)