mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-01 13:49:16 +00:00
LibWeb: Move event handling & cursor from BrowsingContext to Navigable
This was a long standing FIXME since the introduction of navigables.
This commit is contained in:
parent
9cd4a65071
commit
0ebfc0a4c4
Notes:
sideshowbarker
2024-07-17 01:46:43 +09:00
Author: https://github.com/awesomekling
Commit: 0ebfc0a4c4
Pull-request: https://github.com/SerenityOS/serenity/pull/24123
26 changed files with 364 additions and 360 deletions
|
@ -15,7 +15,7 @@ namespace Web::DOM {
|
||||||
class EditableTextNodeOwner {
|
class EditableTextNodeOwner {
|
||||||
public:
|
public:
|
||||||
virtual ~EditableTextNodeOwner() = default;
|
virtual ~EditableTextNodeOwner() = default;
|
||||||
virtual void did_edit_text_node(Badge<HTML::BrowsingContext>) = 0;
|
virtual void did_edit_text_node(Badge<HTML::Navigable>) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Text
|
class Text
|
||||||
|
|
|
@ -276,22 +276,7 @@ WebIDL::ExceptionOr<BrowsingContext::BrowsingContextAndDocument> BrowsingContext
|
||||||
|
|
||||||
BrowsingContext::BrowsingContext(JS::NonnullGCPtr<Page> page)
|
BrowsingContext::BrowsingContext(JS::NonnullGCPtr<Page> page)
|
||||||
: m_page(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;
|
BrowsingContext::~BrowsingContext() = default;
|
||||||
|
@ -301,7 +286,6 @@ void BrowsingContext::visit_edges(Cell::Visitor& visitor)
|
||||||
Base::visit_edges(visitor);
|
Base::visit_edges(visitor);
|
||||||
|
|
||||||
visitor.visit(m_page);
|
visitor.visit(m_page);
|
||||||
visitor.visit(m_cursor_position);
|
|
||||||
visitor.visit(m_window_proxy);
|
visitor.visit(m_window_proxy);
|
||||||
visitor.visit(m_group);
|
visitor.visit(m_group);
|
||||||
visitor.visit(m_parent);
|
visitor.visit(m_parent);
|
||||||
|
@ -310,8 +294,6 @@ void BrowsingContext::visit_edges(Cell::Visitor& visitor)
|
||||||
visitor.visit(m_next_sibling);
|
visitor.visit(m_next_sibling);
|
||||||
visitor.visit(m_previous_sibling);
|
visitor.visit(m_previous_sibling);
|
||||||
visitor.visit(m_opener_browsing_context);
|
visitor.visit(m_opener_browsing_context);
|
||||||
|
|
||||||
m_event_handler.visit_edges(visitor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/document-sequences.html#bc-traversable
|
// https://html.spec.whatwg.org/multipage/document-sequences.html#bc-traversable
|
||||||
|
@ -324,25 +306,6 @@ JS::NonnullGCPtr<HTML::TraversableNavigable> BrowsingContext::top_level_traversa
|
||||||
return *traversable;
|
return *traversable;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowsingContext::did_edit(Badge<EditEventHandler>)
|
|
||||||
{
|
|
||||||
reset_cursor_blink_cycle();
|
|
||||||
|
|
||||||
if (m_cursor_position && is<DOM::Text>(*m_cursor_position->node())) {
|
|
||||||
auto& text_node = static_cast<DOM::Text&>(*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
|
// https://html.spec.whatwg.org/multipage/browsers.html#top-level-browsing-context
|
||||||
bool BrowsingContext::is_top_level() const
|
bool BrowsingContext::is_top_level() const
|
||||||
{
|
{
|
||||||
|
@ -350,11 +313,6 @@ bool BrowsingContext::is_top_level() const
|
||||||
return !parent();
|
return !parent();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BrowsingContext::is_focused_context() const
|
|
||||||
{
|
|
||||||
return &m_page->focused_context() == this;
|
|
||||||
}
|
|
||||||
|
|
||||||
JS::GCPtr<BrowsingContext> BrowsingContext::top_level_browsing_context() const
|
JS::GCPtr<BrowsingContext> BrowsingContext::top_level_browsing_context() const
|
||||||
{
|
{
|
||||||
auto const* start = this;
|
auto const* start = this;
|
||||||
|
@ -376,127 +334,6 @@ JS::GCPtr<BrowsingContext> BrowsingContext::top_level_browsing_context() const
|
||||||
return navigable->active_browsing_context();
|
return navigable->active_browsing_context();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowsingContext::set_cursor_position(JS::NonnullGCPtr<DOM::Position> 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<DOM::Text>(*range.start_container())) {
|
|
||||||
if (!range.start_container()->layout_node())
|
|
||||||
return String {};
|
|
||||||
return MUST(static_cast<DOM::Text const&>(*range.start_container()).data().substring_from_byte_offset(range.start_offset(), range.end_offset() - range.start_offset()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is<DOM::Text>(*range.start_container()) && range.start_container()->layout_node())
|
|
||||||
builder.append(static_cast<DOM::Text const&>(*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<DOM::Text>(*node) && range.contains_node(*node) && node->layout_node())
|
|
||||||
builder.append(static_cast<DOM::Text const&>(*node).data());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is<DOM::Text>(*range.end_container()) && range.end_container()->layout_node())
|
|
||||||
builder.append(static_cast<DOM::Text const&>(*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<DOM::Document&>(*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<DOM::Node> 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<HTML::NavigableContainer>(candidate->focused_element())
|
|
||||||
&& static_cast<HTML::NavigableContainer&>(*candidate->focused_element()).nested_browsing_context()) {
|
|
||||||
candidate = static_cast<HTML::NavigableContainer&>(*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
|
DOM::Document const* BrowsingContext::active_document() const
|
||||||
{
|
{
|
||||||
auto* window = active_window();
|
auto* window = active_window();
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
#include <LibWeb/HTML/SessionHistoryEntry.h>
|
#include <LibWeb/HTML/SessionHistoryEntry.h>
|
||||||
#include <LibWeb/HTML/TokenizedFeatures.h>
|
#include <LibWeb/HTML/TokenizedFeatures.h>
|
||||||
#include <LibWeb/HTML/VisibilityState.h>
|
#include <LibWeb/HTML/VisibilityState.h>
|
||||||
#include <LibWeb/Page/EventHandler.h>
|
|
||||||
#include <LibWeb/Platform/Timer.h>
|
#include <LibWeb/Platform/Timer.h>
|
||||||
#include <LibWeb/TreeNode.h>
|
#include <LibWeb/TreeNode.h>
|
||||||
|
|
||||||
|
@ -99,7 +98,6 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_top_level() const;
|
bool is_top_level() const;
|
||||||
bool is_focused_context() const;
|
|
||||||
|
|
||||||
DOM::Document const* active_document() const;
|
DOM::Document const* active_document() const;
|
||||||
DOM::Document* active_document();
|
DOM::Document* active_document();
|
||||||
|
@ -115,26 +113,8 @@ public:
|
||||||
Page& page() { return m_page; }
|
Page& page() { return m_page; }
|
||||||
Page const& page() const { 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<BrowsingContext> top_level_browsing_context() const;
|
JS::GCPtr<BrowsingContext> top_level_browsing_context() const;
|
||||||
|
|
||||||
JS::GCPtr<DOM::Position> cursor_position() const { return m_cursor_position; }
|
|
||||||
void set_cursor_position(JS::NonnullGCPtr<DOM::Position>);
|
|
||||||
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<EditEventHandler>);
|
|
||||||
|
|
||||||
JS::GCPtr<DOM::Node> currently_focused_area();
|
|
||||||
|
|
||||||
BrowsingContextGroup* group();
|
BrowsingContextGroup* group();
|
||||||
void set_group(BrowsingContextGroup*);
|
void set_group(BrowsingContextGroup*);
|
||||||
|
|
||||||
|
@ -156,13 +136,8 @@ private:
|
||||||
|
|
||||||
virtual void visit_edges(Cell::Visitor&) override;
|
virtual void visit_edges(Cell::Visitor&) override;
|
||||||
|
|
||||||
void reset_cursor_blink_cycle();
|
|
||||||
|
|
||||||
JS::NonnullGCPtr<Page> m_page;
|
JS::NonnullGCPtr<Page> m_page;
|
||||||
|
|
||||||
// FIXME: Move EventHandler to Navigable
|
|
||||||
Web::EventHandler m_event_handler;
|
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/document-sequences.html#browsing-context
|
// https://html.spec.whatwg.org/multipage/document-sequences.html#browsing-context
|
||||||
JS::GCPtr<HTML::WindowProxy> m_window_proxy;
|
JS::GCPtr<HTML::WindowProxy> m_window_proxy;
|
||||||
|
|
||||||
|
@ -184,11 +159,6 @@ private:
|
||||||
// https://html.spec.whatwg.org/multipage/document-sequences.html#virtual-browsing-context-group-id
|
// https://html.spec.whatwg.org/multipage/document-sequences.html#virtual-browsing-context-group-id
|
||||||
u64 m_virtual_browsing_context_group_id = { 0 };
|
u64 m_virtual_browsing_context_group_id = { 0 };
|
||||||
|
|
||||||
// FIXME: Move cursor tracking to Navigable
|
|
||||||
JS::GCPtr<DOM::Position> m_cursor_position;
|
|
||||||
RefPtr<Core::Timer> m_cursor_blink_timer;
|
|
||||||
bool m_cursor_blink_state { false };
|
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/browsers.html#tlbc-group
|
// https://html.spec.whatwg.org/multipage/browsers.html#tlbc-group
|
||||||
JS::GCPtr<BrowsingContextGroup> m_group;
|
JS::GCPtr<BrowsingContextGroup> m_group;
|
||||||
|
|
||||||
|
|
|
@ -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.
|
// 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())
|
if (!new_focus_target->document().browsing_context())
|
||||||
return;
|
return;
|
||||||
auto top_level_browsing_context = new_focus_target->document().browsing_context()->top_level_browsing_context();
|
auto top_level_traversable = new_focus_target->document().browsing_context()->top_level_traversable();
|
||||||
if (new_focus_target == top_level_browsing_context->currently_focused_area().ptr())
|
if (new_focus_target == top_level_traversable->currently_focused_area().ptr())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 6. Let old chain be the current focus chain of the top-level browsing context in which
|
// 6. Let old chain be the current focus chain of the top-level browsing context in which
|
||||||
// new focus target finds itself.
|
// 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.
|
// 7. Let new chain be the focus chain of new focus target.
|
||||||
auto new_chain = focus_chain(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)) {
|
if (is_shadow_host(old_focus_target)) {
|
||||||
auto* shadow_root = static_cast<DOM::Element*>(old_focus_target)->shadow_root_internal();
|
auto* shadow_root = static_cast<DOM::Element*>(old_focus_target)->shadow_root_internal();
|
||||||
if (shadow_root->delegates_focus()) {
|
if (shadow_root->delegates_focus()) {
|
||||||
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();
|
||||||
if (auto currently_focused_area = top_level_browsing_context->currently_focused_area()) {
|
if (auto currently_focused_area = top_level_traversable->currently_focused_area()) {
|
||||||
if (shadow_root->is_shadow_including_ancestor_of(*currently_focused_area)) {
|
if (shadow_root->is_shadow_including_ancestor_of(*currently_focused_area)) {
|
||||||
old_focus_target = 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
|
// 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.
|
// 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.
|
// 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; });
|
auto it = old_chain.find_if([&](auto const& node) { return old_focus_target == node; });
|
||||||
|
|
|
@ -589,10 +589,10 @@ void HTMLElement::did_receive_focus()
|
||||||
{
|
{
|
||||||
if (m_content_editable_state != ContentEditableState::True)
|
if (m_content_editable_state != ContentEditableState::True)
|
||||||
return;
|
return;
|
||||||
auto* browsing_context = document().browsing_context();
|
auto navigable = document().navigable();
|
||||||
if (!browsing_context)
|
if (!navigable)
|
||||||
return;
|
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
|
// https://html.spec.whatwg.org/multipage/interaction.html#dom-accesskeylabel
|
||||||
|
|
|
@ -379,7 +379,7 @@ WebIDL::ExceptionOr<void> HTMLInputElement::run_input_activation_behavior(DOM::E
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTMLInputElement::did_edit_text_node(Badge<BrowsingContext>)
|
void HTMLInputElement::did_edit_text_node(Badge<Navigable>)
|
||||||
{
|
{
|
||||||
// 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.
|
// 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());
|
m_value = value_sanitization_algorithm(m_text_node->data());
|
||||||
|
@ -543,8 +543,8 @@ WebIDL::ExceptionOr<void> HTMLInputElement::set_value(String const& value)
|
||||||
m_text_node->set_data(m_value);
|
m_text_node->set_data(m_value);
|
||||||
update_placeholder_visibility();
|
update_placeholder_visibility();
|
||||||
|
|
||||||
if (auto* browsing_context = document().browsing_context())
|
if (auto navigable = document().navigable())
|
||||||
browsing_context->set_cursor_position(DOM::Position::create(realm, *m_text_node, m_text_node->data().bytes().size()));
|
navigable->set_cursor_position(DOM::Position::create(realm, *m_text_node, m_text_node->data().bytes().size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
update_shadow_tree();
|
update_shadow_tree();
|
||||||
|
@ -1053,12 +1053,12 @@ void HTMLInputElement::update_slider_thumb_element()
|
||||||
|
|
||||||
void HTMLInputElement::did_receive_focus()
|
void HTMLInputElement::did_receive_focus()
|
||||||
{
|
{
|
||||||
auto* browsing_context = document().browsing_context();
|
auto navigable = document().navigable();
|
||||||
if (!browsing_context)
|
if (!navigable)
|
||||||
return;
|
return;
|
||||||
if (!m_text_node)
|
if (!m_text_node)
|
||||||
return;
|
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()
|
void HTMLInputElement::did_lose_focus()
|
||||||
|
|
|
@ -143,7 +143,7 @@ public:
|
||||||
WebIDL::ExceptionOr<void> show_picker();
|
WebIDL::ExceptionOr<void> show_picker();
|
||||||
|
|
||||||
// ^DOM::EditableTextNodeOwner
|
// ^DOM::EditableTextNodeOwner
|
||||||
virtual void did_edit_text_node(Badge<BrowsingContext>) override;
|
virtual void did_edit_text_node(Badge<Navigable>) override;
|
||||||
|
|
||||||
// ^EventTarget
|
// ^EventTarget
|
||||||
// https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute:the-input-element
|
// https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute:the-input-element
|
||||||
|
|
|
@ -66,12 +66,12 @@ void HTMLTextAreaElement::visit_edges(Cell::Visitor& visitor)
|
||||||
|
|
||||||
void HTMLTextAreaElement::did_receive_focus()
|
void HTMLTextAreaElement::did_receive_focus()
|
||||||
{
|
{
|
||||||
auto* browsing_context = document().browsing_context();
|
auto navigable = document().navigable();
|
||||||
if (!browsing_context)
|
if (!navigable)
|
||||||
return;
|
return;
|
||||||
if (!m_text_node)
|
if (!m_text_node)
|
||||||
return;
|
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()
|
void HTMLTextAreaElement::did_lose_focus()
|
||||||
|
@ -158,8 +158,8 @@ void HTMLTextAreaElement::set_value(String const& value)
|
||||||
m_text_node->set_data(m_raw_value);
|
m_text_node->set_data(m_raw_value);
|
||||||
update_placeholder_visibility();
|
update_placeholder_visibility();
|
||||||
|
|
||||||
if (auto* browsing_context = document().browsing_context())
|
if (auto navigable = document().navigable())
|
||||||
browsing_context->set_cursor_position(DOM::Position::create(realm, *m_text_node, m_text_node->data().bytes().size()));
|
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
|
// 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.
|
// immediately follows the text entry cursor.
|
||||||
if (auto const* browsing_context = document().browsing_context()) {
|
if (auto navigable = document().navigable()) {
|
||||||
if (auto cursor = browsing_context->cursor_position())
|
if (auto cursor = navigable->cursor_position())
|
||||||
return cursor->offset();
|
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
|
// 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.
|
// immediately follows the text entry cursor.
|
||||||
if (auto const* browsing_context = document().browsing_context()) {
|
if (auto navigable = document().navigable()) {
|
||||||
if (auto cursor = browsing_context->cursor_position())
|
if (auto cursor = navigable->cursor_position())
|
||||||
return cursor->offset();
|
return cursor->offset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,7 +430,7 @@ void HTMLTextAreaElement::form_associated_element_attribute_changed(FlyString co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTMLTextAreaElement::did_edit_text_node(Badge<Web::HTML::BrowsingContext>)
|
void HTMLTextAreaElement::did_edit_text_node(Badge<Navigable>)
|
||||||
{
|
{
|
||||||
VERIFY(m_text_node);
|
VERIFY(m_text_node);
|
||||||
set_raw_value(m_text_node->data());
|
set_raw_value(m_text_node->data());
|
||||||
|
|
|
@ -37,7 +37,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// ^DOM::EditableTextNodeOwner
|
// ^DOM::EditableTextNodeOwner
|
||||||
virtual void did_edit_text_node(Badge<BrowsingContext>) override;
|
virtual void did_edit_text_node(Badge<Navigable>) override;
|
||||||
|
|
||||||
// ^EventTarget
|
// ^EventTarget
|
||||||
// https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute:the-textarea-element
|
// https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute:the-textarea-element
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2022-2024, Andreas Kling <kling@serenityos.org>
|
||||||
* Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
* Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
@ -9,6 +9,7 @@
|
||||||
#include <LibWeb/DOM/Document.h>
|
#include <LibWeb/DOM/Document.h>
|
||||||
#include <LibWeb/DOM/DocumentLoading.h>
|
#include <LibWeb/DOM/DocumentLoading.h>
|
||||||
#include <LibWeb/DOM/Event.h>
|
#include <LibWeb/DOM/Event.h>
|
||||||
|
#include <LibWeb/DOM/Range.h>
|
||||||
#include <LibWeb/Fetch/Fetching/Fetching.h>
|
#include <LibWeb/Fetch/Fetching/Fetching.h>
|
||||||
#include <LibWeb/Fetch/Infrastructure/FetchAlgorithms.h>
|
#include <LibWeb/Fetch/Infrastructure/FetchAlgorithms.h>
|
||||||
#include <LibWeb/Fetch/Infrastructure/FetchController.h>
|
#include <LibWeb/Fetch/Infrastructure/FetchController.h>
|
||||||
|
@ -36,6 +37,7 @@
|
||||||
#include <LibWeb/Painting/Paintable.h>
|
#include <LibWeb/Painting/Paintable.h>
|
||||||
#include <LibWeb/Painting/ViewportPaintable.h>
|
#include <LibWeb/Painting/ViewportPaintable.h>
|
||||||
#include <LibWeb/Platform/EventLoopPlugin.h>
|
#include <LibWeb/Platform/EventLoopPlugin.h>
|
||||||
|
#include <LibWeb/Selection/Selection.h>
|
||||||
#include <LibWeb/XHR/FormData.h>
|
#include <LibWeb/XHR/FormData.h>
|
||||||
|
|
||||||
namespace Web::HTML {
|
namespace Web::HTML {
|
||||||
|
@ -103,8 +105,24 @@ bool Navigable::is_ancestor_of(JS::NonnullGCPtr<Navigable> other) const
|
||||||
|
|
||||||
Navigable::Navigable(JS::NonnullGCPtr<Page> page)
|
Navigable::Navigable(JS::NonnullGCPtr<Page> page)
|
||||||
: m_page(page)
|
: m_page(page)
|
||||||
|
, m_event_handler({}, *this)
|
||||||
{
|
{
|
||||||
all_navigables().set(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()
|
Navigable::~Navigable()
|
||||||
|
@ -120,6 +138,8 @@ void Navigable::visit_edges(Cell::Visitor& visitor)
|
||||||
visitor.visit(m_current_session_history_entry);
|
visitor.visit(m_current_session_history_entry);
|
||||||
visitor.visit(m_active_session_history_entry);
|
visitor.visit(m_active_session_history_entry);
|
||||||
visitor.visit(m_container);
|
visitor.visit(m_container);
|
||||||
|
visitor.visit(m_cursor_position);
|
||||||
|
m_event_handler.visit_edges(visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Navigable::set_delaying_load_events(bool value)
|
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;
|
return event.is_trusted() ? UserNavigationInvolvement::Activation : UserNavigationInvolvement::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Navigable::did_edit(Badge<EditEventHandler>)
|
||||||
|
{
|
||||||
|
reset_cursor_blink_cycle();
|
||||||
|
|
||||||
|
if (m_cursor_position && is<DOM::Text>(*m_cursor_position->node())) {
|
||||||
|
auto& text_node = static_cast<DOM::Text&>(*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<DOM::Position> 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<DOM::Text>(*range.start_container())) {
|
||||||
|
if (!range.start_container()->layout_node())
|
||||||
|
return String {};
|
||||||
|
return MUST(static_cast<DOM::Text const&>(*range.start_container()).data().substring_from_byte_offset(range.start_offset(), range.end_offset() - range.start_offset()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is<DOM::Text>(*range.start_container()) && range.start_container()->layout_node())
|
||||||
|
builder.append(static_cast<DOM::Text const&>(*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<DOM::Text>(*node) && range.contains_node(*node) && node->layout_node())
|
||||||
|
builder.append(static_cast<DOM::Text const&>(*node).data());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is<DOM::Text>(*range.end_container()) && range.end_container()->layout_node())
|
||||||
|
builder.append(static_cast<DOM::Text const&>(*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<Navigable*>(this)->active_document();
|
||||||
|
if (!document)
|
||||||
|
return String {};
|
||||||
|
auto selection = const_cast<DOM::Document&>(*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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <LibWeb/HTML/SourceSnapshotParams.h>
|
#include <LibWeb/HTML/SourceSnapshotParams.h>
|
||||||
#include <LibWeb/HTML/StructuredSerialize.h>
|
#include <LibWeb/HTML/StructuredSerialize.h>
|
||||||
#include <LibWeb/HTML/TokenizedFeatures.h>
|
#include <LibWeb/HTML/TokenizedFeatures.h>
|
||||||
|
#include <LibWeb/Page/EventHandler.h>
|
||||||
#include <LibWeb/PixelUnits.h>
|
#include <LibWeb/PixelUnits.h>
|
||||||
#include <LibWeb/XHR/FormDataEntry.h>
|
#include <LibWeb/XHR/FormDataEntry.h>
|
||||||
|
|
||||||
|
@ -44,7 +45,9 @@ struct TargetSnapshotParams {
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/document-sequences.html#navigable
|
// https://html.spec.whatwg.org/multipage/document-sequences.html#navigable
|
||||||
class Navigable : public JS::Cell {
|
class Navigable
|
||||||
|
: public JS::Cell
|
||||||
|
, public Weakable<Navigable> {
|
||||||
JS_CELL(Navigable, JS::Cell);
|
JS_CELL(Navigable, JS::Cell);
|
||||||
JS_DECLARE_ALLOCATOR(Navigable);
|
JS_DECLARE_ALLOCATOR(Navigable);
|
||||||
|
|
||||||
|
@ -93,6 +96,8 @@ public:
|
||||||
|
|
||||||
virtual bool is_top_level_traversable() const { return false; }
|
virtual bool is_top_level_traversable() const { return false; }
|
||||||
|
|
||||||
|
[[nodiscard]] bool is_focused() const;
|
||||||
|
|
||||||
enum class WindowType {
|
enum class WindowType {
|
||||||
ExistingOrNone,
|
ExistingOrNone,
|
||||||
NewAndUnrestricted,
|
NewAndUnrestricted,
|
||||||
|
@ -186,6 +191,22 @@ public:
|
||||||
Page& page() { return m_page; }
|
Page& page() { return m_page; }
|
||||||
Page const& page() const { 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<EditEventHandler>);
|
||||||
|
|
||||||
|
JS::GCPtr<DOM::Position> cursor_position() const { return m_cursor_position; }
|
||||||
|
void set_cursor_position(JS::NonnullGCPtr<DOM::Position>);
|
||||||
|
bool increment_cursor_position_offset();
|
||||||
|
bool decrement_cursor_position_offset();
|
||||||
|
|
||||||
|
bool cursor_blink_state() const { return m_cursor_blink_state; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit Navigable(JS::NonnullGCPtr<Page>);
|
explicit Navigable(JS::NonnullGCPtr<Page>);
|
||||||
|
|
||||||
|
@ -198,6 +219,8 @@ protected:
|
||||||
TokenizedFeature::Popup m_is_popup { TokenizedFeature::Popup::No };
|
TokenizedFeature::Popup m_is_popup { TokenizedFeature::Popup::No };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void reset_cursor_blink_cycle();
|
||||||
|
|
||||||
void scroll_offset_did_change();
|
void scroll_offset_did_change();
|
||||||
|
|
||||||
void inform_the_navigation_api_about_aborting_navigation();
|
void inform_the_navigation_api_about_aborting_navigation();
|
||||||
|
@ -231,6 +254,12 @@ private:
|
||||||
CSSPixelPoint m_viewport_scroll_offset;
|
CSSPixelPoint m_viewport_scroll_offset;
|
||||||
|
|
||||||
bool m_needs_repaint { false };
|
bool m_needs_repaint { false };
|
||||||
|
|
||||||
|
Web::EventHandler m_event_handler;
|
||||||
|
|
||||||
|
JS::GCPtr<DOM::Position> m_cursor_position;
|
||||||
|
RefPtr<Core::Timer> m_cursor_blink_timer;
|
||||||
|
bool m_cursor_blink_state { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
HashTable<Navigable*>& all_navigables();
|
HashTable<Navigable*>& all_navigables();
|
||||||
|
|
|
@ -95,7 +95,7 @@ WebIDL::ExceptionOr<void> NavigableContainer::create_new_child_navigable(JS::Saf
|
||||||
document_state->set_about_base_url(document->about_base_url());
|
document_state->set_about_base_url(document->about_base_url());
|
||||||
|
|
||||||
// 7. Let navigable be a new navigable.
|
// 7. Let navigable be a new navigable.
|
||||||
JS::NonnullGCPtr<Navigable> navigable = *heap().allocate_without_realm<Navigable>();
|
JS::NonnullGCPtr<Navigable> navigable = *heap().allocate_without_realm<Navigable>(page);
|
||||||
|
|
||||||
// 8. Initialize the navigable navigable given documentState and parentNavigable.
|
// 8. Initialize the navigable navigable given documentState and parentNavigable.
|
||||||
TRY_OR_THROW_OOM(vm(), navigable->initialize_navigable(document_state, parent_navigable));
|
TRY_OR_THROW_OOM(vm(), navigable->initialize_navigable(document_state, parent_navigable));
|
||||||
|
|
|
@ -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<DOM::Node> 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<HTML::NavigableContainer>(candidate->focused_element())
|
||||||
|
&& static_cast<HTML::NavigableContainer&>(*candidate->focused_element()).content_navigable()) {
|
||||||
|
candidate = static_cast<HTML::NavigableContainer&>(*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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,8 @@ public:
|
||||||
String window_handle() const { return m_window_handle; }
|
String window_handle() const { return m_window_handle; }
|
||||||
void set_window_handle(String window_handle) { m_window_handle = move(window_handle); }
|
void set_window_handle(String window_handle) { m_window_handle = move(window_handle); }
|
||||||
|
|
||||||
|
[[nodiscard]] JS::GCPtr<DOM::Node> currently_focused_area();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TraversableNavigable(JS::NonnullGCPtr<Page>);
|
TraversableNavigable(JS::NonnullGCPtr<Page>);
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ void EditEventHandler::handle_delete_character_after(JS::NonnullGCPtr<DOM::Posit
|
||||||
builder.append(text.bytes_as_string_view().substring_view(*next_grapheme_offset));
|
builder.append(text.bytes_as_string_view().substring_view(*next_grapheme_offset));
|
||||||
node.set_data(MUST(builder.to_string()));
|
node.set_data(MUST(builder.to_string()));
|
||||||
|
|
||||||
m_browsing_context->did_edit({});
|
m_navigable->did_edit({});
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method is quite convoluted but this is necessary to make editing feel intuitive.
|
// 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();
|
end->remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_browsing_context->did_edit({});
|
m_navigable->did_edit({});
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditEventHandler::handle_insert(JS::NonnullGCPtr<DOM::Position> position, u32 code_point)
|
void EditEventHandler::handle_insert(JS::NonnullGCPtr<DOM::Position> position, u32 code_point)
|
||||||
|
@ -126,6 +126,6 @@ void EditEventHandler::handle_insert(JS::NonnullGCPtr<DOM::Position> position, S
|
||||||
position->set_offset(1);
|
position->set_offset(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_browsing_context->did_edit({});
|
m_navigable->did_edit({});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,15 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
|
#include <LibJS/Forward.h>
|
||||||
#include <LibWeb/Forward.h>
|
#include <LibWeb/Forward.h>
|
||||||
|
|
||||||
namespace Web {
|
namespace Web {
|
||||||
|
|
||||||
class EditEventHandler {
|
class EditEventHandler {
|
||||||
public:
|
public:
|
||||||
explicit EditEventHandler(HTML::BrowsingContext& browsing_context)
|
explicit EditEventHandler(HTML::Navigable& navigable)
|
||||||
: m_browsing_context(browsing_context)
|
: m_navigable(navigable)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ public:
|
||||||
virtual void handle_insert(JS::NonnullGCPtr<DOM::Position>, String);
|
virtual void handle_insert(JS::NonnullGCPtr<DOM::Position>, String);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
JS::NonnullGCPtr<HTML::BrowsingContext> m_browsing_context;
|
JS::NonnullGCPtr<HTML::Navigable> m_navigable;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,9 +129,9 @@ static CSSPixelPoint compute_mouse_event_offset(CSSPixelPoint position, Layout::
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
EventHandler::EventHandler(Badge<HTML::BrowsingContext>, HTML::BrowsingContext& browsing_context)
|
EventHandler::EventHandler(Badge<HTML::Navigable>, HTML::Navigable& navigable)
|
||||||
: m_browsing_context(browsing_context)
|
: m_navigable(navigable)
|
||||||
, m_edit_event_handler(make<EditEventHandler>(browsing_context))
|
, m_edit_event_handler(make<EditEventHandler>(navigable))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,26 +139,26 @@ EventHandler::~EventHandler() = default;
|
||||||
|
|
||||||
Painting::PaintableBox* EventHandler::paint_root()
|
Painting::PaintableBox* EventHandler::paint_root()
|
||||||
{
|
{
|
||||||
if (!m_browsing_context->active_document())
|
if (!m_navigable->active_document())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return m_browsing_context->active_document()->paintable_box();
|
return m_navigable->active_document()->paintable_box();
|
||||||
}
|
}
|
||||||
|
|
||||||
Painting::PaintableBox const* EventHandler::paint_root() const
|
Painting::PaintableBox const* EventHandler::paint_root() const
|
||||||
{
|
{
|
||||||
if (!m_browsing_context->active_document())
|
if (!m_navigable->active_document())
|
||||||
return nullptr;
|
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)
|
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;
|
return false;
|
||||||
if (!m_browsing_context->active_document()->is_fully_active())
|
if (!m_navigable->active_document()->is_fully_active())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_browsing_context->active_document()->update_layout();
|
m_navigable->active_document()->update_layout();
|
||||||
|
|
||||||
if (!paint_root())
|
if (!paint_root())
|
||||||
return false;
|
return false;
|
||||||
|
@ -191,7 +191,7 @@ bool EventHandler::handle_mousewheel(CSSPixelPoint position, CSSPixelPoint scree
|
||||||
if (is<HTML::HTMLIFrameElement>(*node)) {
|
if (is<HTML::HTMLIFrameElement>(*node)) {
|
||||||
auto& iframe = static_cast<HTML::HTMLIFrameElement&>(*node);
|
auto& iframe = static_cast<HTML::HTMLIFrameElement&>(*node);
|
||||||
auto position_in_iframe = position.translated(compute_mouse_event_offset({}, paintable->layout_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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,7 +204,7 @@ bool EventHandler::handle_mousewheel(CSSPixelPoint position, CSSPixelPoint scree
|
||||||
auto client_offset = compute_mouse_event_client_offset(position);
|
auto client_offset = compute_mouse_event_client_offset(position);
|
||||||
auto page_offset = compute_mouse_event_page_offset(client_offset);
|
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())) {
|
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;
|
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)
|
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;
|
return false;
|
||||||
if (!m_browsing_context->active_document()->is_fully_active())
|
if (!m_navigable->active_document()->is_fully_active())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_browsing_context->active_document()->update_layout();
|
m_navigable->active_document()->update_layout();
|
||||||
|
|
||||||
if (!paint_root())
|
if (!paint_root())
|
||||||
return false;
|
return false;
|
||||||
|
@ -248,8 +248,8 @@ bool EventHandler::handle_mouseup(CSSPixelPoint position, CSSPixelPoint screen_p
|
||||||
|
|
||||||
if (node) {
|
if (node) {
|
||||||
if (is<HTML::HTMLIFrameElement>(*node)) {
|
if (is<HTML::HTMLIFrameElement>(*node)) {
|
||||||
if (auto* nested_browsing_context = static_cast<HTML::HTMLIFrameElement&>(*node).nested_browsing_context())
|
if (auto content_navigable = static_cast<HTML::HTMLIFrameElement&>(*node).content_navigable())
|
||||||
return nested_browsing_context->event_handler().handle_mouseup(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, button, buttons, modifiers);
|
return content_navigable->event_handler().handle_mouseup(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, button, buttons, modifiers);
|
||||||
return false;
|
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
|
// 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<HTML::HTMLAnchorElement const> link = node->enclosing_link_element()) {
|
if (JS::GCPtr<HTML::HTMLAnchorElement const> link = node->enclosing_link_element()) {
|
||||||
JS::NonnullGCPtr<DOM::Document> document = *m_browsing_context->active_document();
|
JS::NonnullGCPtr<DOM::Document> document = *m_navigable->active_document();
|
||||||
auto href = link->href();
|
auto href = link->href();
|
||||||
auto url = document->parse_url(href);
|
auto url = document->parse_url(href);
|
||||||
dbgln("Web::EventHandler: Clicking on a link to {}", url);
|
dbgln("Web::EventHandler: Clicking on a link to {}", url);
|
||||||
if (button == GUI::MouseButton::Middle) {
|
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) {
|
} 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) {
|
} else if (button == GUI::MouseButton::Secondary) {
|
||||||
if (is<HTML::HTMLImageElement>(*node)) {
|
if (is<HTML::HTMLImageElement>(*node)) {
|
||||||
auto& image_element = verify_cast<HTML::HTMLImageElement>(*node);
|
auto& image_element = verify_cast<HTML::HTMLImageElement>(*node);
|
||||||
auto image_url = image_element.document().parse_url(image_element.src());
|
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<HTML::HTMLMediaElement>(*node)) {
|
} else if (is<HTML::HTMLMediaElement>(*node)) {
|
||||||
auto& media_element = verify_cast<HTML::HTMLMediaElement>(*node);
|
auto& media_element = verify_cast<HTML::HTMLMediaElement>(*node);
|
||||||
|
|
||||||
|
@ -321,9 +321,9 @@ bool EventHandler::handle_mouseup(CSSPixelPoint position, CSSPixelPoint screen_p
|
||||||
.is_looping = media_element.has_attribute(HTML::AttributeNames::loop),
|
.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 {
|
} 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)
|
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;
|
return false;
|
||||||
if (!m_browsing_context->active_document()->is_fully_active())
|
if (!m_navigable->active_document()->is_fully_active())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_browsing_context->active_document()->update_layout();
|
m_navigable->active_document()->update_layout();
|
||||||
|
|
||||||
if (!paint_root())
|
if (!paint_root())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
JS::NonnullGCPtr<DOM::Document> document = *m_browsing_context->active_document();
|
JS::NonnullGCPtr<DOM::Document> document = *m_navigable->active_document();
|
||||||
JS::GCPtr<DOM::Node> node;
|
JS::GCPtr<DOM::Node> node;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -374,12 +374,12 @@ bool EventHandler::handle_mousedown(CSSPixelPoint position, CSSPixelPoint screen
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (is<HTML::HTMLIFrameElement>(*node)) {
|
if (is<HTML::HTMLIFrameElement>(*node)) {
|
||||||
if (auto* nested_browsing_context = static_cast<HTML::HTMLIFrameElement&>(*node).nested_browsing_context())
|
if (auto content_navigable = static_cast<HTML::HTMLIFrameElement&>(*node).content_navigable())
|
||||||
return nested_browsing_context->event_handler().handle_mousedown(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, button, buttons, modifiers);
|
return content_navigable->event_handler().handle_mousedown(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, button, buttons, modifiers);
|
||||||
return false;
|
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.
|
// 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)
|
// "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.
|
// FIXME: This is all rather strange. Find a better solution.
|
||||||
if (!did_focus_something || paintable->dom_node()->is_editable()) {
|
if (!did_focus_something || paintable->dom_node()->is_editable()) {
|
||||||
auto& realm = document->realm();
|
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()) {
|
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);
|
(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)
|
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;
|
return false;
|
||||||
if (!m_browsing_context->active_document()->is_fully_active())
|
if (!m_navigable->active_document()->is_fully_active())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_browsing_context->active_document()->update_layout();
|
m_navigable->active_document()->update_layout();
|
||||||
|
|
||||||
if (!paint_root())
|
if (!paint_root())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto& document = *m_browsing_context->active_document();
|
auto& document = *m_navigable->active_document();
|
||||||
auto& realm = document.realm();
|
auto& realm = document.realm();
|
||||||
|
|
||||||
bool hovered_node_changed = false;
|
bool hovered_node_changed = false;
|
||||||
|
@ -471,14 +471,14 @@ bool EventHandler::handle_mousemove(CSSPixelPoint position, CSSPixelPoint screen
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// FIXME: It feels a bit aggressive to always update the cursor like this.
|
// 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);
|
auto node = dom_node_for_event_dispatch(*paintable);
|
||||||
|
|
||||||
if (node && is<HTML::HTMLIFrameElement>(*node)) {
|
if (node && is<HTML::HTMLIFrameElement>(*node)) {
|
||||||
if (auto* nested_browsing_context = static_cast<HTML::HTMLIFrameElement&>(*node).nested_browsing_context())
|
if (auto content_navigable = static_cast<HTML::HTMLIFrameElement&>(*node).content_navigable())
|
||||||
return nested_browsing_context->event_handler().handle_mousemove(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, buttons, modifiers);
|
return content_navigable->event_handler().handle_mousemove(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, buttons, modifiers);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,7 +529,7 @@ bool EventHandler::handle_mousemove(CSSPixelPoint position, CSSPixelPoint screen
|
||||||
if (m_in_mouse_selection) {
|
if (m_in_mouse_selection) {
|
||||||
auto hit = paint_root()->hit_test(position, Painting::HitTestType::TextCursor);
|
auto hit = paint_root()->hit_test(position, Painting::HitTestType::TextCursor);
|
||||||
if (start_index.has_value() && hit.has_value() && hit->dom_node()) {
|
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()) {
|
if (auto selection = document.get_selection()) {
|
||||||
auto anchor_node = selection->anchor_node();
|
auto anchor_node = selection->anchor_node();
|
||||||
if (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);
|
page.client().page_did_request_cursor_change(hovered_node_cursor);
|
||||||
|
|
||||||
if (hovered_node_changed) {
|
if (hovered_node_changed) {
|
||||||
JS::GCPtr<HTML::HTMLElement const> hovered_html_element = document.hovered_node() ? document.hovered_node()->enclosing_html_element_with_attribute(HTML::AttributeNames::title) : nullptr;
|
JS::GCPtr<HTML::HTMLElement const> 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()) {
|
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 {
|
} else {
|
||||||
page.client().page_did_leave_tooltip_area();
|
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)
|
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;
|
return false;
|
||||||
if (!m_browsing_context->active_document()->is_fully_active())
|
if (!m_navigable->active_document()->is_fully_active())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_browsing_context->active_document()->update_layout();
|
m_navigable->active_document()->update_layout();
|
||||||
|
|
||||||
if (!paint_root())
|
if (!paint_root())
|
||||||
return false;
|
return false;
|
||||||
|
@ -595,8 +595,8 @@ bool EventHandler::handle_doubleclick(CSSPixelPoint position, CSSPixelPoint scre
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (is<HTML::HTMLIFrameElement>(*node)) {
|
if (is<HTML::HTMLIFrameElement>(*node)) {
|
||||||
if (auto* nested_browsing_context = static_cast<HTML::HTMLIFrameElement&>(*node).nested_browsing_context())
|
if (auto content_navigable = static_cast<HTML::HTMLIFrameElement&>(*node).content_navigable())
|
||||||
return nested_browsing_context->event_handler().handle_doubleclick(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, button, buttons, modifiers);
|
return content_navigable->event_handler().handle_doubleclick(position.translated(compute_mouse_event_offset({}, paintable->layout_node())), screen_position, button, buttons, modifiers);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -648,7 +648,7 @@ bool EventHandler::handle_doubleclick(CSSPixelPoint position, CSSPixelPoint scre
|
||||||
}();
|
}();
|
||||||
|
|
||||||
auto& realm = node->document().realm();
|
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()) {
|
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);
|
(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()
|
bool EventHandler::focus_next_element()
|
||||||
{
|
{
|
||||||
if (!m_browsing_context->active_document())
|
if (!m_navigable->active_document())
|
||||||
return false;
|
return false;
|
||||||
if (!m_browsing_context->active_document()->is_fully_active())
|
if (!m_navigable->active_document()->is_fully_active())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto* element = m_browsing_context->active_document()->focused_element();
|
auto* element = m_navigable->active_document()->focused_element();
|
||||||
if (!element) {
|
if (!element) {
|
||||||
element = m_browsing_context->active_document()->first_child_of_type<DOM::Element>();
|
element = m_navigable->active_document()->first_child_of_type<DOM::Element>();
|
||||||
if (element && element->is_focusable()) {
|
if (element && element->is_focusable()) {
|
||||||
m_browsing_context->active_document()->set_focused_element(element);
|
m_navigable->active_document()->set_focused_element(element);
|
||||||
return true;
|
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())
|
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;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EventHandler::focus_previous_element()
|
bool EventHandler::focus_previous_element()
|
||||||
{
|
{
|
||||||
if (!m_browsing_context->active_document())
|
if (!m_navigable->active_document())
|
||||||
return false;
|
return false;
|
||||||
if (!m_browsing_context->active_document()->is_fully_active())
|
if (!m_navigable->active_document()->is_fully_active())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto* element = m_browsing_context->active_document()->focused_element();
|
auto* element = m_navigable->active_document()->focused_element();
|
||||||
if (!element) {
|
if (!element) {
|
||||||
element = m_browsing_context->active_document()->last_child_of_type<DOM::Element>();
|
element = m_navigable->active_document()->last_child_of_type<DOM::Element>();
|
||||||
if (element && element->is_focusable()) {
|
if (element && element->is_focusable()) {
|
||||||
m_browsing_context->active_document()->set_focused_element(element);
|
m_navigable->active_document()->set_focused_element(element);
|
||||||
return true;
|
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())
|
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;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -713,9 +713,9 @@ constexpr bool should_ignore_keydown_event(u32 code_point, u32 modifiers)
|
||||||
return code_point == 0 || code_point == 27;
|
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<DOM::Document> document = browsing_context.active_document();
|
JS::GCPtr<DOM::Document> document = navigable.active_document();
|
||||||
if (!document)
|
if (!document)
|
||||||
return false;
|
return false;
|
||||||
if (!document->is_fully_active())
|
if (!document->is_fully_active())
|
||||||
|
@ -724,8 +724,8 @@ bool EventHandler::fire_keyboard_event(FlyString const& event_name, HTML::Browsi
|
||||||
if (JS::GCPtr<DOM::Element> focused_element = document->focused_element()) {
|
if (JS::GCPtr<DOM::Element> focused_element = document->focused_element()) {
|
||||||
if (is<HTML::NavigableContainer>(*focused_element)) {
|
if (is<HTML::NavigableContainer>(*focused_element)) {
|
||||||
auto& navigable_container = verify_cast<HTML::NavigableContainer>(*focused_element);
|
auto& navigable_container = verify_cast<HTML::NavigableContainer>(*focused_element);
|
||||||
if (navigable_container.nested_browsing_context())
|
if (navigable_container.content_navigable())
|
||||||
return fire_keyboard_event(event_name, *navigable_container.nested_browsing_context(), key, modifiers, code_point);
|
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);
|
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)
|
bool EventHandler::handle_keydown(KeyCode key, u32 modifiers, u32 code_point)
|
||||||
{
|
{
|
||||||
if (!m_browsing_context->active_document())
|
if (!m_navigable->active_document())
|
||||||
return false;
|
return false;
|
||||||
if (!m_browsing_context->active_document()->is_fully_active())
|
if (!m_navigable->active_document()->is_fully_active())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
JS::NonnullGCPtr<DOM::Document> document = *m_browsing_context->active_document();
|
JS::NonnullGCPtr<DOM::Document> document = *m_navigable->active_document();
|
||||||
if (!document->layout_node())
|
if (!document->layout_node())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -766,7 +766,7 @@ bool EventHandler::handle_keydown(KeyCode key, u32 modifiers, u32 code_point)
|
||||||
selection->remove_all_ranges();
|
selection->remove_all_ranges();
|
||||||
|
|
||||||
// FIXME: This doesn't work for some reason?
|
// 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) {
|
if (key == KeyCode::Key_Backspace || key == KeyCode::Key_Delete) {
|
||||||
m_edit_event_handler->handle_delete(*range);
|
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.
|
// FIXME: Text editing shortcut keys (copy/paste etc.) should be handled here.
|
||||||
if (!should_ignore_keydown_event(code_point, modifiers)) {
|
if (!should_ignore_keydown_event(code_point, modifiers)) {
|
||||||
m_edit_event_handler->handle_delete(*range);
|
m_edit_event_handler->handle_delete(*range);
|
||||||
m_edit_event_handler->handle_insert(JS::NonnullGCPtr { *m_browsing_context->cursor_position() }, code_point);
|
m_edit_event_handler->handle_insert(JS::NonnullGCPtr { *m_navigable->cursor_position() }, code_point);
|
||||||
m_browsing_context->increment_cursor_position_offset();
|
m_navigable->increment_cursor_position_offset();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto* element = m_browsing_context->active_document()->focused_element(); is<HTML::HTMLMediaElement>(element)) {
|
if (auto* element = m_navigable->active_document()->focused_element(); is<HTML::HTMLMediaElement>(element)) {
|
||||||
auto& media_element = static_cast<HTML::HTMLMediaElement&>(*element);
|
auto& media_element = static_cast<HTML::HTMLMediaElement&>(*element);
|
||||||
media_element.handle_keydown({}, key).release_value_but_fixme_should_propagate_errors();
|
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_)
|
if (!continue_)
|
||||||
return false;
|
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 (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.
|
// FIXME: Move to the previous node and delete the last character there.
|
||||||
return true;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
if (key == KeyCode::Key_Delete) {
|
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.
|
// FIXME: Move to the next node and delete the first character there.
|
||||||
return true;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
if (key == KeyCode::Key_Right) {
|
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.
|
// FIXME: Move to the next node.
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (key == KeyCode::Key_Left) {
|
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.
|
// FIXME: Move to the previous node.
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (key == KeyCode::Key_Home) {
|
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())
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
if (key == KeyCode::Key_End) {
|
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()) {
|
if (cursor_position_node.is_text()) {
|
||||||
auto& text_node = static_cast<DOM::Text&>(cursor_position_node);
|
auto& text_node = static_cast<DOM::Text&>(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;
|
return true;
|
||||||
}
|
}
|
||||||
if (key == KeyCode::Key_Return) {
|
if (key == KeyCode::Key_Return) {
|
||||||
HTML::HTMLInputElement* input_element = nullptr;
|
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()) {
|
if (node->is_text()) {
|
||||||
auto& text_node = static_cast<DOM::Text&>(*node);
|
auto& text_node = static_cast<DOM::Text&>(*node);
|
||||||
if (is<HTML::HTMLInputElement>(text_node.editable_text_node_owner()))
|
if (is<HTML::HTMLInputElement>(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.
|
// FIXME: Text editing shortcut keys (copy/paste etc.) should be handled here.
|
||||||
if (!should_ignore_keydown_event(code_point, modifiers)) {
|
if (!should_ignore_keydown_event(code_point, modifiers)) {
|
||||||
m_edit_event_handler->handle_insert(JS::NonnullGCPtr { *m_browsing_context->cursor_position() }, code_point);
|
m_edit_event_handler->handle_insert(JS::NonnullGCPtr { *m_navigable->cursor_position() }, code_point);
|
||||||
m_browsing_context->increment_cursor_position_offset();
|
m_navigable->increment_cursor_position_offset();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Work out and implement the difference between this and keydown.
|
// 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)
|
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)
|
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)
|
if (!active_document)
|
||||||
return;
|
return;
|
||||||
if (!active_document->is_fully_active())
|
if (!active_document->is_fully_active())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (auto cursor_position = m_browsing_context->cursor_position()) {
|
if (auto cursor_position = m_navigable->cursor_position()) {
|
||||||
active_document->update_layout();
|
active_document->update_layout();
|
||||||
m_edit_event_handler->handle_insert(*cursor_position, text);
|
m_edit_event_handler->handle_insert(*cursor_position, text);
|
||||||
cursor_position->set_offset(cursor_position->offset() + text.code_points().length());
|
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
|
// 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.
|
// 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);
|
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.
|
// 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.
|
// 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.
|
// 3. Return the sum of offset and the value of the event’s clientX attribute.
|
||||||
return event_client_offset.translated(scroll_offset);
|
return event_client_offset.translated(scroll_offset);
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace Web {
|
||||||
|
|
||||||
class EventHandler {
|
class EventHandler {
|
||||||
public:
|
public:
|
||||||
explicit EventHandler(Badge<HTML::BrowsingContext>, HTML::BrowsingContext&);
|
explicit EventHandler(Badge<HTML::Navigable>, HTML::Navigable&);
|
||||||
~EventHandler();
|
~EventHandler();
|
||||||
|
|
||||||
bool handle_mouseup(CSSPixelPoint, CSSPixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers);
|
bool handle_mouseup(CSSPixelPoint, CSSPixelPoint screen_position, unsigned button, unsigned buttons, unsigned modifiers);
|
||||||
|
@ -46,7 +46,7 @@ private:
|
||||||
bool focus_next_element();
|
bool focus_next_element();
|
||||||
bool focus_previous_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_client_offset(CSSPixelPoint event_page_position) const;
|
||||||
CSSPixelPoint compute_mouse_event_page_offset(CSSPixelPoint event_client_offset) const;
|
CSSPixelPoint compute_mouse_event_page_offset(CSSPixelPoint event_client_offset) const;
|
||||||
CSSPixelPoint compute_mouse_event_movement(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* paint_root();
|
||||||
Painting::PaintableBox const* paint_root() const;
|
Painting::PaintableBox const* paint_root() const;
|
||||||
|
|
||||||
JS::NonnullGCPtr<HTML::BrowsingContext> m_browsing_context;
|
JS::NonnullGCPtr<HTML::Navigable> m_navigable;
|
||||||
|
|
||||||
bool m_in_mouse_selection { false };
|
bool m_in_mouse_selection { false };
|
||||||
|
|
||||||
|
|
|
@ -46,16 +46,16 @@ void Page::visit_edges(JS::Cell::Visitor& visitor)
|
||||||
visitor.visit(m_client);
|
visitor.visit(m_client);
|
||||||
}
|
}
|
||||||
|
|
||||||
HTML::BrowsingContext& Page::focused_context()
|
HTML::Navigable& Page::focused_navigable()
|
||||||
{
|
{
|
||||||
if (m_focused_context)
|
if (m_focused_navigable)
|
||||||
return *m_focused_context;
|
return *m_focused_navigable;
|
||||||
return top_level_browsing_context();
|
return top_level_traversable();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Page::set_focused_browsing_context(Badge<EventHandler>, HTML::BrowsingContext& browsing_context)
|
void Page::set_focused_navigable(Badge<EventHandler>, 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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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<HTML::TraversableNavigable> navigable)
|
void Page::set_top_level_traversable(JS::NonnullGCPtr<HTML::TraversableNavigable> navigable)
|
||||||
|
|
|
@ -65,10 +65,10 @@ public:
|
||||||
|
|
||||||
JS::NonnullGCPtr<HTML::TraversableNavigable> top_level_traversable() const;
|
JS::NonnullGCPtr<HTML::TraversableNavigable> top_level_traversable() const;
|
||||||
|
|
||||||
HTML::BrowsingContext& focused_context();
|
HTML::Navigable& focused_navigable();
|
||||||
HTML::BrowsingContext const& focused_context() const { return const_cast<Page*>(this)->focused_context(); }
|
HTML::Navigable const& focused_navigable() const { return const_cast<Page*>(this)->focused_navigable(); }
|
||||||
|
|
||||||
void set_focused_browsing_context(Badge<EventHandler>, HTML::BrowsingContext&);
|
void set_focused_navigable(Badge<EventHandler>, HTML::Navigable&);
|
||||||
|
|
||||||
void load(URL::URL const&);
|
void load(URL::URL const&);
|
||||||
|
|
||||||
|
@ -186,7 +186,7 @@ private:
|
||||||
|
|
||||||
JS::NonnullGCPtr<PageClient> m_client;
|
JS::NonnullGCPtr<PageClient> m_client;
|
||||||
|
|
||||||
WeakPtr<HTML::BrowsingContext> m_focused_context;
|
WeakPtr<HTML::Navigable> m_focused_navigable;
|
||||||
|
|
||||||
JS::GCPtr<HTML::TraversableNavigable> m_top_level_traversable;
|
JS::GCPtr<HTML::TraversableNavigable> m_top_level_traversable;
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ LabelablePaintable::DispatchEventOfSameName LabelablePaintable::handle_mousedown
|
||||||
|
|
||||||
set_being_pressed(true);
|
set_being_pressed(true);
|
||||||
m_tracking_mouse = 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;
|
return DispatchEventOfSameName::Yes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ LabelablePaintable::DispatchEventOfSameName LabelablePaintable::handle_mouseup(B
|
||||||
|
|
||||||
set_being_pressed(false);
|
set_being_pressed(false);
|
||||||
m_tracking_mouse = 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;
|
return DispatchEventOfSameName::Yes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -284,7 +284,7 @@ MediaPaintable::DispatchEventOfSameName MediaPaintable::handle_mousedown(Badge<E
|
||||||
}
|
}
|
||||||
|
|
||||||
if (media_element.layout_mouse_tracking_component({}).has_value())
|
if (media_element.layout_mouse_tracking_component({}).has_value())
|
||||||
const_cast<HTML::BrowsingContext&>(browsing_context()).event_handler().set_mouse_event_tracking_paintable(this);
|
const_cast<HTML::Navigable&>(*navigable()).event_handler().set_mouse_event_tracking_paintable(this);
|
||||||
|
|
||||||
return DispatchEventOfSameName::Yes;
|
return DispatchEventOfSameName::Yes;
|
||||||
}
|
}
|
||||||
|
@ -306,7 +306,7 @@ MediaPaintable::DispatchEventOfSameName MediaPaintable::handle_mouseup(Badge<Eve
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const_cast<HTML::BrowsingContext&>(browsing_context()).event_handler().set_mouse_event_tracking_paintable(nullptr);
|
const_cast<HTML::Navigable&>(*navigable()).event_handler().set_mouse_event_tracking_paintable(nullptr);
|
||||||
media_element.set_layout_mouse_tracking_component({}, {});
|
media_element.set_layout_mouse_tracking_component({}, {});
|
||||||
|
|
||||||
return DispatchEventOfSameName::Yes;
|
return DispatchEventOfSameName::Yes;
|
||||||
|
|
|
@ -65,7 +65,7 @@ void NestedBrowsingContextPaintable::paint(PaintContext& context, PaintPhase pha
|
||||||
context.recording_painter().restore();
|
context.recording_painter().restore();
|
||||||
|
|
||||||
if constexpr (HIGHLIGHT_FOCUSED_FRAME_DEBUG) {
|
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<int>(), Color::Cyan);
|
context.recording_painter().draw_rect(clip_rect.to_type<int>(), Color::Cyan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
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;
|
return;
|
||||||
|
|
||||||
if (!browsing_context.cursor_blink_state())
|
if (!navigable.cursor_blink_state())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (browsing_context.cursor_position()->node() != paintable.dom_node())
|
if (navigable.cursor_position()->node() != paintable.dom_node())
|
||||||
return;
|
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.
|
// 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;
|
return;
|
||||||
|
|
||||||
if (!fragment.layout_node().dom_node() || !fragment.layout_node().dom_node()->is_editable())
|
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();
|
auto text = fragment.string_view();
|
||||||
CSSPixelRect cursor_rect {
|
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(),
|
fragment_rect.top(),
|
||||||
1,
|
1,
|
||||||
fragment_rect.height()
|
fragment_rect.height()
|
||||||
|
|
|
@ -43,7 +43,7 @@ TextPaintable::DispatchEventOfSameName TextPaintable::handle_mousedown(Badge<Eve
|
||||||
if (!label)
|
if (!label)
|
||||||
return DispatchEventOfSameName::No;
|
return DispatchEventOfSameName::No;
|
||||||
const_cast<Layout::Label*>(label)->handle_mousedown_on_label({}, position, button);
|
const_cast<Layout::Label*>(label)->handle_mousedown_on_label({}, position, button);
|
||||||
const_cast<HTML::BrowsingContext&>(browsing_context()).event_handler().set_mouse_event_tracking_paintable(this);
|
const_cast<HTML::Navigable&>(*navigable()).event_handler().set_mouse_event_tracking_paintable(this);
|
||||||
return DispatchEventOfSameName::Yes;
|
return DispatchEventOfSameName::Yes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ TextPaintable::DispatchEventOfSameName TextPaintable::handle_mouseup(Badge<Event
|
||||||
return DispatchEventOfSameName::No;
|
return DispatchEventOfSameName::No;
|
||||||
|
|
||||||
const_cast<Layout::Label*>(label)->handle_mouseup_on_label({}, position, button);
|
const_cast<Layout::Label*>(label)->handle_mouseup_on_label({}, position, button);
|
||||||
const_cast<HTML::BrowsingContext&>(browsing_context()).event_handler().set_mouse_event_tracking_paintable(nullptr);
|
const_cast<HTML::Navigable&>(*navigable()).event_handler().set_mouse_event_tracking_paintable(nullptr);
|
||||||
return DispatchEventOfSameName::Yes;
|
return DispatchEventOfSameName::Yes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -552,7 +552,7 @@ void ConnectionFromClient::inspect_dom_node(u64 page_id, i32 node_id, Optional<W
|
||||||
// FIXME: Pseudo-elements only exist as Layout::Nodes, which don't have style information
|
// FIXME: Pseudo-elements only exist as Layout::Nodes, which don't have style information
|
||||||
// in a format we can use. So, we run the StyleComputer again to get the specified
|
// in a format we can use. So, we run the StyleComputer again to get the specified
|
||||||
// values, and have to ignore the computed values and custom properties.
|
// values, and have to ignore the computed values and custom properties.
|
||||||
auto pseudo_element_style = page->page().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 computed_values = serialize_json(pseudo_element_style);
|
||||||
ByteString resolved_values = "{}";
|
ByteString resolved_values = "{}";
|
||||||
ByteString custom_properties_json = serialize_custom_properties_json(element, pseudo_element);
|
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)
|
Messages::WebContentServer::GetSelectedTextResponse ConnectionFromClient::get_selected_text(u64 page_id)
|
||||||
{
|
{
|
||||||
if (auto page = this->page(page_id); page.has_value())
|
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 {};
|
return ByteString {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectionFromClient::select_all(u64 page_id)
|
void ConnectionFromClient::select_all(u64 page_id)
|
||||||
{
|
{
|
||||||
if (auto page = this->page(page_id); page.has_value())
|
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)
|
void ConnectionFromClient::paste(u64 page_id, String const& text)
|
||||||
{
|
{
|
||||||
if (auto page = this->page(page_id); page.has_value())
|
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)
|
Messages::WebContentServer::DumpLayoutTreeResponse ConnectionFromClient::dump_layout_tree(u64 page_id)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue