From 92392398a22d5dfde6099fd21d14b5cadcbfd969 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 8 Jun 2020 20:31:49 +0200 Subject: [PATCH] LibWeb: Add Page abstraction between PageView and main Frame * A PageView is a view onto a Page object. * A Page always has a main Frame (root of Frame tree.) * Page has a PageClient. PageView is a PageClient. The goal here is to allow building another kind of view onto a Page while keeping the rest of LibWeb intact. --- Applications/Browser/Tab.cpp | 4 +- Libraries/LibWeb/CMakeLists.txt | 1 + Libraries/LibWeb/CSS/StyleValue.cpp | 2 +- Libraries/LibWeb/DOM/Document.cpp | 6 +- Libraries/LibWeb/DOM/HTMLFormElement.cpp | 2 +- Libraries/LibWeb/DOM/HTMLInputElement.cpp | 3 +- Libraries/LibWeb/DOM/Window.cpp | 10 +- Libraries/LibWeb/Forward.h | 2 + Libraries/LibWeb/Frame/EventHandler.cpp | 25 +-- Libraries/LibWeb/Frame/Frame.cpp | 39 ++-- Libraries/LibWeb/Frame/Frame.h | 13 +- Libraries/LibWeb/Layout/LayoutWidget.cpp | 4 +- Libraries/LibWeb/Page.cpp | 78 ++++++++ Libraries/LibWeb/Page.h | 87 +++++++++ Libraries/LibWeb/PageView.cpp | 210 ++++++++++------------ Libraries/LibWeb/PageView.h | 44 +++-- 16 files changed, 351 insertions(+), 179 deletions(-) create mode 100644 Libraries/LibWeb/Page.cpp create mode 100644 Libraries/LibWeb/Page.h diff --git a/Applications/Browser/Tab.cpp b/Applications/Browser/Tab.cpp index a5e40d6c10..c3f7ddba06 100644 --- a/Applications/Browser/Tab.cpp +++ b/Applications/Browser/Tab.cpp @@ -130,7 +130,7 @@ Tab::Tab() m_bookmark_button->set_preferred_size(22, 22); m_bookmark_button->on_click = [this](auto) { - auto url = m_page_view->main_frame().document()->url().to_string(); + auto url = m_page_view->document()->url().to_string(); if (BookmarksBarWidget::the().contains_bookmark(url)) { BookmarksBarWidget::the().remove_bookmark(url); } else { @@ -195,7 +195,7 @@ Tab::Tab() m_page_view->on_title_change = [this](auto& title) { if (title.is_null()) { - m_title = m_page_view->main_frame().document()->url().to_string(); + m_title = m_page_view->document()->url().to_string(); } else { m_title = title; } diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index f1b926ca9d..ae553f6d18 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -96,6 +96,7 @@ set(SOURCES Loader/ImageResource.cpp Loader/Resource.cpp Loader/ResourceLoader.cpp + Page.cpp PageView.cpp Parser/CSSParser.cpp Parser/Entities.cpp diff --git a/Libraries/LibWeb/CSS/StyleValue.cpp b/Libraries/LibWeb/CSS/StyleValue.cpp index f088274ea1..fc1abe924a 100644 --- a/Libraries/LibWeb/CSS/StyleValue.cpp +++ b/Libraries/LibWeb/CSS/StyleValue.cpp @@ -171,7 +171,7 @@ Color IdentifierStyleValue::to_color(const Document& document) const if (id() == CSS::ValueID::VendorSpecificLink) return document.link_color(); - auto palette = document.frame()->page_view()->palette(); + auto palette = document.frame()->page().palette(); switch (id()) { case CSS::ValueID::VendorSpecificPaletteDesktopBackground: return palette.color(ColorRole::DesktopBackground); diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 9973310dd9..1d06433c7d 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -376,7 +376,7 @@ Color Document::link_color() const return m_link_color.value(); if (!frame()) return Color::Blue; - return frame()->page_view()->palette().link(); + return frame()->page().palette().link(); } Color Document::active_link_color() const @@ -385,7 +385,7 @@ Color Document::active_link_color() const return m_active_link_color.value(); if (!frame()) return Color::Red; - return frame()->page_view()->palette().active_link(); + return frame()->page().palette().active_link(); } Color Document::visited_link_color() const @@ -394,7 +394,7 @@ Color Document::visited_link_color() const return m_visited_link_color.value(); if (!frame()) return Color::Magenta; - return frame()->page_view()->palette().visited_link(); + return frame()->page().palette().visited_link(); } JS::Interpreter& Document::interpreter() diff --git a/Libraries/LibWeb/DOM/HTMLFormElement.cpp b/Libraries/LibWeb/DOM/HTMLFormElement.cpp index d714fe8130..5028ac0db3 100644 --- a/Libraries/LibWeb/DOM/HTMLFormElement.cpp +++ b/Libraries/LibWeb/DOM/HTMLFormElement.cpp @@ -72,7 +72,7 @@ void HTMLFormElement::submit(RefPtr submitter) url.set_query(url_encode(parameters)); // FIXME: We shouldn't let the form just do this willy-nilly. - document().frame()->page_view()->load(url); + document().frame()->page().load(url); } } diff --git a/Libraries/LibWeb/DOM/HTMLInputElement.cpp b/Libraries/LibWeb/DOM/HTMLInputElement.cpp index e34f9cb752..45b30f61c9 100644 --- a/Libraries/LibWeb/DOM/HTMLInputElement.cpp +++ b/Libraries/LibWeb/DOM/HTMLInputElement.cpp @@ -50,8 +50,7 @@ RefPtr HTMLInputElement::create_layout_node(const StyleProperties*) { ASSERT(document().frame()); auto& frame = *document().frame(); - ASSERT(frame.page_view()); - auto& page_view = const_cast(*frame.page_view()); + auto& page_view = const_cast(static_cast(frame.page().client())); if (type() == "hidden") return nullptr; diff --git a/Libraries/LibWeb/DOM/Window.cpp b/Libraries/LibWeb/DOM/Window.cpp index 10a70c5305..af22ec934e 100644 --- a/Libraries/LibWeb/DOM/Window.cpp +++ b/Libraries/LibWeb/DOM/Window.cpp @@ -115,10 +115,7 @@ void Window::did_set_location_href(Badge, const String auto* frame = document().frame(); if (!frame) return; - auto* view = frame->page_view(); - if (!view) - return; - view->load(new_href); + frame->loader().load(new_href); } void Window::did_call_location_reload(Badge) @@ -126,10 +123,7 @@ void Window::did_call_location_reload(Badge) auto* frame = document().frame(); if (!frame) return; - auto* view = frame->page_view(); - if (!view) - return; - view->reload(); + frame->loader().load(document().url()); } } diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index bae5ccb696..dd53caf084 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -53,6 +53,8 @@ class LoadRequest; class MouseEvent; class Node; class Origin; +class Page; +class PageClient; class Resource; class ResourceLoader; class Selector; diff --git a/Libraries/LibWeb/Frame/EventHandler.cpp b/Libraries/LibWeb/Frame/EventHandler.cpp index c9c39ada0b..5145cc43e4 100644 --- a/Libraries/LibWeb/Frame/EventHandler.cpp +++ b/Libraries/LibWeb/Frame/EventHandler.cpp @@ -105,7 +105,7 @@ bool EventHandler::handle_mousedown(const Gfx::Point& position, unsigned button, return false; auto& layout_root = *layout_root_ptr; auto& document = *m_frame.document(); - auto& page_view = *m_frame.page_view(); + auto& page_client = m_frame.page().client(); auto result = layout_root.hit_test(position); if (!result.layout_node) @@ -133,16 +133,16 @@ bool EventHandler::handle_mousedown(const Gfx::Point& position, unsigned button, document.run_javascript(href.substring_view(11, href.length() - 11)); } else { if (m_frame.is_main_frame()) { - page_view.notify_link_click({}, m_frame, link->href(), link->target(), modifiers); + page_client.page_did_click_link(link->href(), link->target(), modifiers); } else { // FIXME: Handle different targets! m_frame.loader().load(document.complete_url(link->href())); } } } else if (button == GUI::MouseButton::Right) { - page_view.notify_link_context_menu_request({}, m_frame, position, link->href(), link->target(), modifiers); + page_client.page_did_request_link_context_menu(m_frame.to_main_frame_position(position), link->href(), link->target(), modifiers); } else if (button == GUI::MouseButton::Middle) { - page_view.notify_link_middle_click({}, m_frame, link->href(), link->target(), modifiers); + page_client.page_did_middle_click_link(link->href(), link->target(), modifiers); } } else { if (button == GUI::MouseButton::Left) { @@ -161,7 +161,7 @@ bool EventHandler::handle_mousemove(const Gfx::Point& position, unsigned buttons return false; auto& layout_root = *layout_root_ptr; auto& document = *m_frame.document(); - auto& page_view = *m_frame.page_view(); + auto& page_client = m_frame.page().client(); bool hovered_node_changed = false; bool is_hovering_link = false; @@ -193,22 +193,23 @@ bool EventHandler::handle_mousemove(const Gfx::Point& position, unsigned buttons if (m_in_mouse_selection) { layout_root.selection().set_end({ result.layout_node, result.index_in_node }); dump_selection("MouseMove"); - page_view.update(); + page_client.page_did_change_selection(); } } - if (page_view.window()) - page_view.window()->set_override_cursor(is_hovering_link ? GUI::StandardCursor::Hand : GUI::StandardCursor::None); + page_client.page_did_request_cursor_change(is_hovering_link ? GUI::StandardCursor::Hand : GUI::StandardCursor::None); if (hovered_node_changed) { - page_view.update(); RefPtr hovered_html_element = document.hovered_node() ? document.hovered_node()->enclosing_html_element() : nullptr; if (hovered_html_element && !hovered_html_element->title().is_null()) { - page_view.notify_tooltip_area_enter({}, m_frame, position, hovered_html_element->title()); + page_client.page_did_enter_tooltip_area(m_frame.to_main_frame_position(position), hovered_html_element->title()); } else { - page_view.notify_tooltip_area_leave({}, m_frame); + page_client.page_did_leave_tooltip_area(); } } if (is_hovering_link != was_hovering_link) { - page_view.notify_link_hover({}, m_frame, hovered_link_element ? document.complete_url(hovered_link_element->href()).to_string() : String()); + if (is_hovering_link) + page_client.page_did_hover_link(document.complete_url(hovered_link_element->href())); + else + page_client.page_did_unhover_link(); } return true; } diff --git a/Libraries/LibWeb/Frame/Frame.cpp b/Libraries/LibWeb/Frame/Frame.cpp index dbebd6f6c3..0393e667dc 100644 --- a/Libraries/LibWeb/Frame/Frame.cpp +++ b/Libraries/LibWeb/Frame/Frame.cpp @@ -33,18 +33,19 @@ namespace Web { Frame::Frame(Element& host_element, Frame& main_frame) - : m_main_frame(main_frame) + : m_page(main_frame.page()) + , m_main_frame(main_frame) , m_loader(*this) , m_event_handler({}, *this) , m_host_element(host_element.make_weak_ptr()) { } -Frame::Frame(PageView& page_view) - : m_main_frame(*this) +Frame::Frame(Page& page) + : m_page(page) + , m_main_frame(*this) , m_loader(*this) , m_event_handler({}, *this) - , m_page_view(page_view.make_weak_ptr()) { } @@ -94,8 +95,7 @@ void Frame::set_needs_display(const Gfx::Rect& rect) return; if (is_main_frame()) { - if (page_view()) - page_view()->notify_needs_display({}, *this, rect); + page().client().page_did_invalidate(to_main_frame_rect(rect)); return; } @@ -117,12 +117,31 @@ void Frame::did_scroll(Badge) void Frame::scroll_to_anchor(const String& fragment) { - // FIXME: We should be able to scroll iframes to an anchor, too! - if (!m_page_view) - return; // FIXME: This logic is backwards, the work should be done in here, // and then we just request that the "view" scrolls to a certain content offset. - m_page_view->scroll_to_anchor(fragment); + page().client().page_did_request_scroll_to_anchor(fragment); +} + +Gfx::Rect Frame::to_main_frame_rect(const Gfx::Rect& a_rect) +{ + auto rect = a_rect; + rect.set_location(to_main_frame_position(a_rect.location())); + return rect; +} + +Gfx::Point Frame::to_main_frame_position(const Gfx::Point& a_position) +{ + auto position = a_position; + for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) { + if (ancestor->is_main_frame()) + break; + if (!ancestor->host_element()) + return {}; + if (!ancestor->host_element()->layout_node()) + return {}; + position.move_by(ancestor->host_element()->layout_node()->box_type_agnostic_position().to_int_point()); + } + return position; } } diff --git a/Libraries/LibWeb/Frame/Frame.h b/Libraries/LibWeb/Frame/Frame.h index 57458cc575..e89f3063f6 100644 --- a/Libraries/LibWeb/Frame/Frame.h +++ b/Libraries/LibWeb/Frame/Frame.h @@ -42,7 +42,7 @@ namespace Web { class Frame : public TreeNode { public: static NonnullRefPtr create_subframe(Element& host_element, Frame& main_frame) { return adopt(*new Frame(host_element, main_frame)); } - static NonnullRefPtr create(PageView& page_view) { return adopt(*new Frame(page_view)); } + static NonnullRefPtr create(Page& page) { return adopt(*new Frame(page)); } ~Frame(); bool is_main_frame() const { return this == &m_main_frame; } @@ -52,8 +52,8 @@ public: void set_document(Document*); - PageView* page_view() { return is_main_frame() ? m_page_view : main_frame().m_page_view; } - const PageView* page_view() const { return is_main_frame() ? m_page_view : main_frame().m_page_view; } + Page& page() { return m_page; } + const Page& page() const { return m_page; } const Gfx::Size& size() const { return m_size; } void set_size(const Gfx::Size&); @@ -84,17 +84,20 @@ public: Element* host_element() { return m_host_element; } const Element* host_element() const { return m_host_element; } + Gfx::Point to_main_frame_position(const Gfx::Point&); + Gfx::Rect to_main_frame_rect(const Gfx::Rect&); + private: explicit Frame(Element& host_element, Frame& main_frame); - explicit Frame(PageView&); + explicit Frame(Page&); + Page& m_page; Frame& m_main_frame; FrameLoader m_loader; EventHandler m_event_handler; WeakPtr m_host_element; - WeakPtr m_page_view; RefPtr m_document; Gfx::Size m_size; Gfx::Rect m_viewport_rect; diff --git a/Libraries/LibWeb/Layout/LayoutWidget.cpp b/Libraries/LibWeb/Layout/LayoutWidget.cpp index e8f68bf4b2..852a36d218 100644 --- a/Libraries/LibWeb/Layout/LayoutWidget.cpp +++ b/Libraries/LibWeb/Layout/LayoutWidget.cpp @@ -60,8 +60,8 @@ void LayoutWidget::did_set_rect() void LayoutWidget::update_widget() { auto adjusted_widget_position = rect().location().to_int_point(); - if (auto* page_view = document().frame()->page_view()) - adjusted_widget_position.move_by(-page_view->horizontal_scrollbar().value(), -page_view->vertical_scrollbar().value()); + auto& page_view = static_cast(document().frame()->page().client()); + adjusted_widget_position.move_by(-page_view.horizontal_scrollbar().value(), -page_view.vertical_scrollbar().value()); widget().move_to(adjusted_widget_position); } diff --git a/Libraries/LibWeb/Page.cpp b/Libraries/LibWeb/Page.cpp new file mode 100644 index 0000000000..2d49245386 --- /dev/null +++ b/Libraries/LibWeb/Page.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +namespace Web { + +Page::Page(PageClient& client) + : m_client(client) +{ + m_main_frame = Frame::create(*this); + + main_frame().on_set_document = [this](auto* document) { + m_client.page_did_set_document_in_main_frame(document); + }; + main_frame().on_title_change = [this](auto& title) { + m_client.page_did_change_title(title); + }; + main_frame().on_load_start = [this](auto& url) { + m_client.page_did_start_loading(url); + }; +} + +Page::~Page() +{ +} + +void Page::load(const URL& url) +{ + main_frame().loader().load(url); +} + +Gfx::Palette Page::palette() const +{ + return static_cast(m_client).palette(); +} + +bool Page::handle_mouseup(const Gfx::Point& position, unsigned button, unsigned modifiers) +{ + return main_frame().event_handler().handle_mouseup(position, button, modifiers); +} + +bool Page::handle_mousedown(const Gfx::Point& position, unsigned button, unsigned modifiers) +{ + return main_frame().event_handler().handle_mousedown(position, button, modifiers); +} + +bool Page::handle_mousemove(const Gfx::Point& position, unsigned buttons, unsigned modifiers) +{ + return main_frame().event_handler().handle_mousemove(position, buttons, modifiers); +} + +} diff --git a/Libraries/LibWeb/Page.h b/Libraries/LibWeb/Page.h new file mode 100644 index 0000000000..9dd7bf14f5 --- /dev/null +++ b/Libraries/LibWeb/Page.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace Web { + +class PageClient; + +class Page { + AK_MAKE_NONCOPYABLE(Page); + AK_MAKE_NONMOVABLE(Page); + +public: + explicit Page(PageClient&); + ~Page(); + + PageClient& client() { return m_client; } + const PageClient& client() const { return m_client; } + + Web::Frame& main_frame() { return *m_main_frame; } + const Web::Frame& main_frame() const { return *m_main_frame; } + + void load(const URL&); + + bool handle_mouseup(const Gfx::Point&, unsigned button, unsigned modifiers); + bool handle_mousedown(const Gfx::Point&, unsigned button, unsigned modifiers); + bool handle_mousemove(const Gfx::Point&, unsigned buttons, unsigned modifiers); + + Gfx::Palette palette() const; + +private: + PageClient& m_client; + + RefPtr m_main_frame; +}; + +class PageClient { +public: + virtual void page_did_set_document_in_main_frame(Document*) { } + virtual void page_did_change_title(const String&) { } + virtual void page_did_start_loading(const URL&) { } + virtual void page_did_change_selection() { } + virtual void page_did_request_cursor_change(GUI::StandardCursor) { } + virtual void page_did_request_link_context_menu(const Gfx::Point&, [[maybe_unused]] const String& href, [[maybe_unused]] const String& target, [[maybe_unused]] unsigned modifiers) { } + virtual void page_did_click_link([[maybe_unused]] const String& href, [[maybe_unused]] const String& target, [[maybe_unused]] unsigned modifiers) { } + virtual void page_did_middle_click_link([[maybe_unused]] const String& href, [[maybe_unused]] const String& target, [[maybe_unused]] unsigned modifiers) { } + virtual void page_did_enter_tooltip_area(const Gfx::Point&, const String&) { } + virtual void page_did_leave_tooltip_area() { } + virtual void page_did_hover_link(const URL&) { } + virtual void page_did_unhover_link() { } + virtual void page_did_request_scroll_to_anchor([[maybe_unused]] const String& fragment) { } + virtual void page_did_invalidate(const Gfx::Rect&) { } +}; + +} diff --git a/Libraries/LibWeb/PageView.cpp b/Libraries/LibWeb/PageView.cpp index e1c9a74a4d..1a5dbafeda 100644 --- a/Libraries/LibWeb/PageView.cpp +++ b/Libraries/LibWeb/PageView.cpp @@ -55,24 +55,8 @@ namespace Web { PageView::PageView() - : m_main_frame(Web::Frame::create(*this)) + : m_page(make(*this)) { - main_frame().on_set_document = [this](auto* document) { - if (on_set_document) - on_set_document(document); - layout_and_sync_size(); - scroll_to_top(); - update(); - }; - main_frame().on_title_change = [this](auto& title) { - if (on_title_change) - on_title_change(title); - }; - main_frame().on_load_start = [this](auto& url) { - if (on_load_start) - on_load_start(url); - }; - set_should_hide_unnecessary_scrollbars(true); set_background_role(ColorRole::Base); } @@ -81,6 +65,86 @@ PageView::~PageView() { } +void PageView::page_did_change_title(const String& title) +{ + if (on_title_change) + on_title_change(title); +} + +void PageView::page_did_set_document_in_main_frame(Document* document) +{ + if (on_set_document) + on_set_document(document); + layout_and_sync_size(); + scroll_to_top(); + update(); +} + +void PageView::page_did_start_loading(const URL& url) +{ + if (on_load_start) + on_load_start(url); +} + +void PageView::page_did_change_selection() +{ + update(); +} + +void PageView::page_did_request_cursor_change(GUI::StandardCursor cursor) +{ + if (window()) + window()->set_override_cursor(cursor); +} + +void PageView::page_did_request_link_context_menu(const Gfx::Point& content_position, const String& href, [[maybe_unused]] const String& target, [[maybe_unused]] unsigned modifiers) +{ + if (on_link_context_menu_request) + on_link_context_menu_request(href, screen_relative_rect().location().translated(to_widget_position(content_position))); +} + +void PageView::page_did_click_link(const String& href, const String& target, unsigned modifiers) +{ + if (on_link_click) + on_link_click(href, target, modifiers); +} + +void PageView::page_did_middle_click_link(const String& href, [[maybe_unused]] const String& target, [[maybe_unused]] unsigned modifiers) +{ + if (on_link_middle_click) + on_link_middle_click(href); +} + +void PageView::page_did_enter_tooltip_area(const Gfx::Point& content_position, const String& title) +{ + GUI::Application::the().show_tooltip(title, screen_relative_rect().location().translated(to_widget_position(content_position))); +} + +void PageView::page_did_leave_tooltip_area() +{ + GUI::Application::the().hide_tooltip(); +} + +void PageView::page_did_hover_link(const URL& url) +{ + if (on_link_hover) + on_link_hover(url.to_string()); +} + +void PageView::page_did_unhover_link() +{ +} + +void PageView::page_did_request_scroll_to_anchor(const String& fragment) +{ + scroll_to_anchor(fragment); +} + +void PageView::page_did_invalidate(const Gfx::Rect&) +{ + update(); +} + void PageView::layout_and_sync_size() { if (!document()) @@ -89,19 +153,19 @@ void PageView::layout_and_sync_size() bool had_vertical_scrollbar = vertical_scrollbar().is_visible(); bool had_horizontal_scrollbar = horizontal_scrollbar().is_visible(); - main_frame().set_size(available_size()); + page().main_frame().set_size(available_size()); document()->layout(); set_content_size(enclosing_int_rect(layout_root()->rect()).size()); // NOTE: If layout caused us to gain or lose scrollbars, we have to lay out again // since the scrollbars now take up some of the available space. if (had_vertical_scrollbar != vertical_scrollbar().is_visible() || had_horizontal_scrollbar != horizontal_scrollbar().is_visible()) { - main_frame().set_size(available_size()); + page().main_frame().set_size(available_size()); document()->layout(); set_content_size(enclosing_int_rect(layout_root()->rect()).size()); } - main_frame().set_viewport_rect(viewport_rect_in_content_coordinates()); + page().main_frame().set_viewport_rect(viewport_rect_in_content_coordinates()); #ifdef HTML_DEBUG dbgprintf("\033[33;1mLayout tree after layout:\033[0m\n"); @@ -145,28 +209,19 @@ void PageView::paint_event(GUI::PaintEvent& event) void PageView::mousemove_event(GUI::MouseEvent& event) { - if (main_frame().event_handler().handle_mousemove(to_content_position(event.position()), event.buttons(), event.modifiers())) { - event.accept(); - return; - } + page().handle_mousemove(to_content_position(event.position()), event.buttons(), event.modifiers()); GUI::ScrollableWidget::mousemove_event(event); } void PageView::mousedown_event(GUI::MouseEvent& event) { - if (main_frame().event_handler().handle_mousedown(to_content_position(event.position()), event.button(), event.modifiers())) { - event.accept(); - return; - } + page().handle_mousedown(to_content_position(event.position()), event.button(), event.modifiers()); GUI::ScrollableWidget::mousedown_event(event); } void PageView::mouseup_event(GUI::MouseEvent& event) { - if (main_frame().event_handler().handle_mouseup(to_content_position(event.position()), event.button(), event.modifiers())) { - event.accept(); - return; - } + page().handle_mouseup(to_content_position(event.position()), event.button(), event.modifiers()); GUI::ScrollableWidget::mouseup_event(event); } @@ -208,7 +263,7 @@ void PageView::keydown_event(GUI::KeyEvent& event) void PageView::reload() { - load(main_frame().document()->url()); + load(page().main_frame().document()->url()); } bool PageView::load(const URL& url) @@ -216,7 +271,7 @@ bool PageView::load(const URL& url) if (window()) window()->set_override_cursor(GUI::StandardCursor::None); - return main_frame().loader().load(url); + return page().main_frame().loader().load(url); } const LayoutDocument* PageView::layout_root() const @@ -263,33 +318,33 @@ void PageView::scroll_to_anchor(const StringView& name) void PageView::set_use_old_parser(bool use_old_parser) { - main_frame().loader().set_use_old_parser(use_old_parser); + page().main_frame().loader().set_use_old_parser(use_old_parser); } void PageView::load_empty_document() { - main_frame().set_document(nullptr); + page().main_frame().set_document(nullptr); } Document* PageView::document() { - return main_frame().document(); + return page().main_frame().document(); } const Document* PageView::document() const { - return main_frame().document(); + return page().main_frame().document(); } void PageView::set_document(Document* document) { - main_frame().set_document(document); + page().main_frame().set_document(document); } void PageView::did_scroll() { - main_frame().set_viewport_rect(viewport_rect_in_content_coordinates()); - main_frame().did_scroll({}); + page().main_frame().set_viewport_rect(viewport_rect_in_content_coordinates()); + page().main_frame().did_scroll({}); } void PageView::drop_event(GUI::DropEvent& event) @@ -303,77 +358,4 @@ void PageView::drop_event(GUI::DropEvent& event) ScrollableWidget::drop_event(event); } -void PageView::notify_link_click(Badge, Web::Frame&, const String& href, const String& target, unsigned modifiers) -{ - if (on_link_click) - on_link_click(href, target, modifiers); -} - -void PageView::notify_link_middle_click(Badge, Web::Frame&, const String& href, const String&, unsigned) -{ - if (on_link_middle_click) - on_link_middle_click(href); -} - -Gfx::Point PageView::to_screen_position(const Web::Frame& frame, const Gfx::Point& frame_position) const -{ - Gfx::Point offset; - for (auto* f = &frame; f; f = f->parent()) { - if (f->is_main_frame()) - break; - auto f_position = f->host_element()->layout_node()->box_type_agnostic_position().to_int_point(); - offset.move_by(f_position); - } - return screen_relative_rect().location().translated(offset).translated(frame_position); -} - -Gfx::Rect PageView::to_widget_rect(const Web::Frame& frame, const Gfx::Rect& frame_rect) const -{ - Gfx::Point offset; - for (auto* f = &frame; f; f = f->parent()) { - if (f->is_main_frame()) - break; - if (!f->host_element()) - return {}; - if (!f->host_element()->layout_node()) - return {}; - auto f_position = f->host_element()->layout_node()->box_type_agnostic_position().to_int_point(); - offset.move_by(f_position); - } - - auto content_position = frame_rect.location().translated(offset); - return { to_widget_position(content_position), frame_rect.size() }; -} - -void PageView::notify_link_context_menu_request(Badge, Web::Frame& frame, const Gfx::Point& content_position, const String& href, const String&, unsigned) -{ - if (on_link_context_menu_request) - on_link_context_menu_request(href, to_screen_position(frame, content_position)); -} - -void PageView::notify_link_hover(Badge, Web::Frame&, const String& href) -{ - if (on_link_hover) - on_link_hover(href); -} - -void PageView::notify_tooltip_area_enter(Badge, Web::Frame& frame, const Gfx::Point& content_position, const String& title) -{ - GUI::Application::the().show_tooltip(title, to_screen_position(frame, content_position)); -} - -void PageView::notify_tooltip_area_leave(Badge, Web::Frame&) -{ - GUI::Application::the().hide_tooltip(); -} - -void PageView::notify_needs_display(Badge, Web::Frame& frame, const Gfx::Rect& rect) -{ - update(to_widget_rect(frame, rect)); - - // FIXME: This is a total hack that forces a full repaint every time. - // We shouldn't have to do this, but until the ICB is actually viewport-sized, we have no choice. - update(); -} - } diff --git a/Libraries/LibWeb/PageView.h b/Libraries/LibWeb/PageView.h index f91f097044..bf646fd849 100644 --- a/Libraries/LibWeb/PageView.h +++ b/Libraries/LibWeb/PageView.h @@ -29,11 +29,13 @@ #include #include #include -#include +#include namespace Web { -class PageView : public GUI::ScrollableWidget { +class PageView final + : public GUI::ScrollableWidget + , public PageClient { C_OBJECT(PageView); public: @@ -52,9 +54,6 @@ public: const LayoutDocument* layout_root() const; LayoutDocument* layout_root(); - Web::Frame& main_frame() { return *m_main_frame; } - const Web::Frame& main_frame() const { return *m_main_frame; } - void reload(); bool load(const URL&); void scroll_to_anchor(const StringView&); @@ -75,17 +74,12 @@ public: virtual bool accepts_focus() const override { return true; } - void notify_link_click(Badge, Web::Frame&, const String& href, const String& target, unsigned modifiers); - void notify_link_middle_click(Badge, Web::Frame&, const String& href, const String& target, unsigned modifiers); - void notify_link_context_menu_request(Badge, Web::Frame&, const Gfx::Point& content_position, const String& href, const String& target, unsigned modifiers); - void notify_link_hover(Badge, Web::Frame&, const String& href); - void notify_tooltip_area_enter(Badge, Web::Frame&, const Gfx::Point& content_position, const String& title); - void notify_tooltip_area_leave(Badge, Web::Frame&); - void notify_needs_display(Badge, Web::Frame&, const Gfx::Rect&); - -protected: +private: PageView(); + Page& page() { return *m_page; } + const Page& page() const { return *m_page; } + virtual void resize_event(GUI::ResizeEvent&) override; virtual void paint_event(GUI::PaintEvent&) override; virtual void mousemove_event(GUI::MouseEvent&) override; @@ -94,17 +88,29 @@ protected: virtual void keydown_event(GUI::KeyEvent&) override; virtual void drop_event(GUI::DropEvent&) override; -private: virtual void did_scroll() override; - Gfx::Point to_screen_position(const Web::Frame&, const Gfx::Point&) const; - Gfx::Rect to_widget_rect(const Web::Frame&, const Gfx::Rect&) const; + // ^Web::PageClient + virtual void page_did_change_title(const String&) override; + virtual void page_did_set_document_in_main_frame(Document*) override; + virtual void page_did_start_loading(const URL&) override; + virtual void page_did_change_selection() override; + virtual void page_did_request_cursor_change(GUI::StandardCursor) override; + virtual void page_did_request_link_context_menu(const Gfx::Point&, const String& href, const String& target, unsigned modifiers) override; + virtual void page_did_click_link(const String& href, const String& target, unsigned modifiers) override; + virtual void page_did_middle_click_link(const String& href, const String& target, unsigned modifiers) override; + virtual void page_did_enter_tooltip_area(const Gfx::Point&, const String&) override; + virtual void page_did_leave_tooltip_area() override; + virtual void page_did_hover_link(const URL&) override; + virtual void page_did_unhover_link() override; + virtual void page_did_request_scroll_to_anchor(const String& fragment) override; + virtual void page_did_invalidate(const Gfx::Rect&) override; void layout_and_sync_size(); - RefPtr m_main_frame; - bool m_should_show_line_box_borders { false }; + + NonnullOwnPtr m_page; }; }