From 4dde36844b26de285abdd289a2b0443bfc54188c Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 21 Mar 2020 18:17:18 +0100 Subject: [PATCH] LibWeb: Add a DOM Event class (instead of events being simple strings) This patch adds the Event base class, along with a MouseEvent subclass. We now dispatch MouseEvent objects for mousedown, mouseup and mousemove and these objects have the .offsetX and .offsetY properties. Both of those properties are hard-coded at the moment. This will be fixed in the next patch. :^) --- Libraries/LibWeb/Bindings/EventWrapper.cpp | 51 ++++++++++++++ Libraries/LibWeb/Bindings/EventWrapper.h | 51 ++++++++++++++ .../LibWeb/Bindings/MouseEventWrapper.cpp | 67 +++++++++++++++++++ Libraries/LibWeb/Bindings/MouseEventWrapper.h | 47 +++++++++++++ Libraries/LibWeb/DOM/Event.cpp | 0 Libraries/LibWeb/DOM/Event.h | 36 ++++++++++ Libraries/LibWeb/DOM/EventTarget.h | 2 +- Libraries/LibWeb/DOM/MouseEvent.cpp | 0 Libraries/LibWeb/DOM/MouseEvent.h | 36 ++++++++++ Libraries/LibWeb/DOM/Node.cpp | 11 +-- Libraries/LibWeb/DOM/Node.h | 2 +- Libraries/LibWeb/Forward.h | 4 ++ Libraries/LibWeb/HtmlView.cpp | 7 +- Libraries/LibWeb/Makefile | 4 ++ Libraries/LibWeb/Parser/HTMLParser.cpp | 3 +- 15 files changed, 311 insertions(+), 10 deletions(-) create mode 100644 Libraries/LibWeb/Bindings/EventWrapper.cpp create mode 100644 Libraries/LibWeb/Bindings/EventWrapper.h create mode 100644 Libraries/LibWeb/Bindings/MouseEventWrapper.cpp create mode 100644 Libraries/LibWeb/Bindings/MouseEventWrapper.h create mode 100644 Libraries/LibWeb/DOM/Event.cpp create mode 100644 Libraries/LibWeb/DOM/Event.h create mode 100644 Libraries/LibWeb/DOM/MouseEvent.cpp create mode 100644 Libraries/LibWeb/DOM/MouseEvent.h diff --git a/Libraries/LibWeb/Bindings/EventWrapper.cpp b/Libraries/LibWeb/Bindings/EventWrapper.cpp new file mode 100644 index 0000000000..86648a8061 --- /dev/null +++ b/Libraries/LibWeb/Bindings/EventWrapper.cpp @@ -0,0 +1,51 @@ +/* + * 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 { +namespace Bindings { + +EventWrapper* wrap(JS::Heap& heap, Event& event) +{ + if (event.is_mouse_event()) + return static_cast(wrap_impl(heap, static_cast(event))); + return static_cast(wrap_impl(heap, event)); +} + +EventWrapper::EventWrapper(Event& event) + : m_event(event) +{ +} + +EventWrapper::~EventWrapper() +{ +} + +} +} diff --git a/Libraries/LibWeb/Bindings/EventWrapper.h b/Libraries/LibWeb/Bindings/EventWrapper.h new file mode 100644 index 0000000000..28f9e7b9b1 --- /dev/null +++ b/Libraries/LibWeb/Bindings/EventWrapper.h @@ -0,0 +1,51 @@ +/* + * 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 + +namespace Web { +namespace Bindings { + +class EventWrapper : public Wrapper { +public: + explicit EventWrapper(Event&); + virtual ~EventWrapper() override; + + Event& event() { return m_event; } + const Event& event() const { return m_event; } + +private: + virtual const char* class_name() const override { return "EventWrapper"; } + + NonnullRefPtr m_event; +}; + +EventWrapper* wrap(JS::Heap&, Event&); + +} +} diff --git a/Libraries/LibWeb/Bindings/MouseEventWrapper.cpp b/Libraries/LibWeb/Bindings/MouseEventWrapper.cpp new file mode 100644 index 0000000000..c3d652589a --- /dev/null +++ b/Libraries/LibWeb/Bindings/MouseEventWrapper.cpp @@ -0,0 +1,67 @@ +/* + * 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 +#include + +namespace Web { +namespace Bindings { + +MouseEventWrapper::MouseEventWrapper(MouseEvent& event) + : EventWrapper(event) +{ + put_native_property( + "offsetX", + [this](JS::Object*) { + return JS::Value(this->event().offset_x()); + }, + nullptr); + put_native_property( + "offsetY", + [this](JS::Object*) { + return JS::Value(this->event().offset_y()); + }, + nullptr); +} + +MouseEventWrapper::~MouseEventWrapper() +{ +} + +const MouseEvent& MouseEventWrapper::event() const +{ + return static_cast(EventWrapper::event()); +} + +MouseEvent& MouseEventWrapper::event() +{ + return static_cast(EventWrapper::event()); +} + +} +} diff --git a/Libraries/LibWeb/Bindings/MouseEventWrapper.h b/Libraries/LibWeb/Bindings/MouseEventWrapper.h new file mode 100644 index 0000000000..e5f0a1e693 --- /dev/null +++ b/Libraries/LibWeb/Bindings/MouseEventWrapper.h @@ -0,0 +1,47 @@ +/* + * 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 + +namespace Web { +namespace Bindings { + +class MouseEventWrapper : public EventWrapper { +public: + explicit MouseEventWrapper(MouseEvent&); + virtual ~MouseEventWrapper() override; + + MouseEvent& event(); + const MouseEvent& event() const; + +private: + virtual const char* class_name() const override { return "MouseEventWrapper"; } +}; + +} +} diff --git a/Libraries/LibWeb/DOM/Event.cpp b/Libraries/LibWeb/DOM/Event.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Libraries/LibWeb/DOM/Event.h b/Libraries/LibWeb/DOM/Event.h new file mode 100644 index 0000000000..ca2ca845db --- /dev/null +++ b/Libraries/LibWeb/DOM/Event.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +namespace Web { + +class Event + : public RefCounted + , public Bindings::Wrappable { +public: + using WrapperType = Bindings::EventWrapper; + + static NonnullRefPtr create(String event_name) + { + return adopt(*new Event(move(event_name))); + } + + virtual ~Event() {} + + const String& name() const { return m_event_name; } + + virtual bool is_mouse_event() const { return false; } + +protected: + Event(String event_name) + : m_event_name(move(event_name)) + { + } + +private: + String m_event_name; +}; + +} diff --git a/Libraries/LibWeb/DOM/EventTarget.h b/Libraries/LibWeb/DOM/EventTarget.h index bf61df0d9f..d23ebca60d 100644 --- a/Libraries/LibWeb/DOM/EventTarget.h +++ b/Libraries/LibWeb/DOM/EventTarget.h @@ -45,7 +45,7 @@ public: void add_event_listener(String event_name, NonnullRefPtr); - virtual void dispatch_event(String event_name) = 0; + virtual void dispatch_event(NonnullRefPtr) = 0; struct EventListenerRegistration { String event_name; diff --git a/Libraries/LibWeb/DOM/MouseEvent.cpp b/Libraries/LibWeb/DOM/MouseEvent.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Libraries/LibWeb/DOM/MouseEvent.h b/Libraries/LibWeb/DOM/MouseEvent.h new file mode 100644 index 0000000000..5b9c39f44d --- /dev/null +++ b/Libraries/LibWeb/DOM/MouseEvent.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +namespace Web { + +class MouseEvent final : public Event { +public: + using WrapperType = Bindings::MouseEventWrapper; + + static NonnullRefPtr create(String event_name, i32 offset_x, i32 offset_y) + { + return adopt(*new MouseEvent(move(event_name), offset_x, offset_y)); + } + + virtual ~MouseEvent() override {} + + i32 offset_x() const { return m_offset_x; } + i32 offset_y() const { return m_offset_y; } + +protected: + MouseEvent(String event_name, i32 offset_x, i32 offset_y) + : Event(move(event_name)) + , m_offset_x(offset_x) + , m_offset_y(offset_y) + { + } + +private: + virtual bool is_mouse_event() const override { return true; } + + i32 m_offset_x { 0 }; + i32 m_offset_y { 0 }; +}; + +} diff --git a/Libraries/LibWeb/DOM/Node.cpp b/Libraries/LibWeb/DOM/Node.cpp index 293ffba1ea..315aec5f3b 100644 --- a/Libraries/LibWeb/DOM/Node.cpp +++ b/Libraries/LibWeb/DOM/Node.cpp @@ -29,9 +29,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -123,21 +125,22 @@ bool Node::is_link() const return enclosing_link->has_attribute("href"); } -void Node::dispatch_event(String event_name) +void Node::dispatch_event(NonnullRefPtr event) { for (auto& listener : listeners()) { - if (listener.event_name == event_name) { + if (listener.event_name == event->name()) { auto* function = const_cast(*listener.listener).function(); static_cast(function)->body().dump(0); auto* this_value = wrap(function->heap(), *this); dbg() << "calling event listener with this=" << this_value; - document().interpreter().call(function, this_value, {}); + auto* event_wrapper = wrap(function->heap(), *event); + document().interpreter().call(function, this_value, { event_wrapper }); } } // FIXME: This is a hack. We should follow the real rules of event bubbling. if (parent()) - parent()->dispatch_event(move(event_name)); + parent()->dispatch_event(move(event)); } } diff --git a/Libraries/LibWeb/DOM/Node.h b/Libraries/LibWeb/DOM/Node.h index abd6e1d084..cfb56c0fe3 100644 --- a/Libraries/LibWeb/DOM/Node.h +++ b/Libraries/LibWeb/DOM/Node.h @@ -68,7 +68,7 @@ public: // ^EventTarget virtual void ref_event_target() final { ref(); } virtual void unref_event_target() final { unref(); } - virtual void dispatch_event(String event_name) final; + virtual void dispatch_event(NonnullRefPtr) final; virtual ~Node(); diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index 2e2e5f437a..623511e311 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -31,21 +31,25 @@ namespace Web { class CanvasRenderingContext2D; class Document; class Element; +class Event; class EventListener; class EventTarget; class Frame; class HTMLElement; class HTMLCanvasElement; class HtmlView; +class MouseEvent; class Node; namespace Bindings { class CanvasRenderingContext2DWrapper; class DocumentWrapper; +class EventWrapper; class EventListenerWrapper; class EventTargetWrapper; class HTMLCanvasElementWrapper; +class MouseEventWrapper; class NodeWrapper; class Wrappable; class Wrapper; diff --git a/Libraries/LibWeb/HtmlView.cpp b/Libraries/LibWeb/HtmlView.cpp index 5a2ddb9dfe..b46f0d9c48 100644 --- a/Libraries/LibWeb/HtmlView.cpp +++ b/Libraries/LibWeb/HtmlView.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -184,7 +185,7 @@ void HtmlView::mousemove_event(GUI::MouseEvent& event) #endif is_hovering_link = true; } - const_cast(node)->dispatch_event("mousemove"); + const_cast(node)->dispatch_event(MouseEvent::create("mousemove", 2, 3)); } if (m_in_mouse_selection) { layout_root()->selection().set_end({ result.layout_node, result.index_in_node }); @@ -235,7 +236,7 @@ void HtmlView::mousedown_event(GUI::MouseEvent& event) m_in_mouse_selection = true; } } - const_cast(node)->dispatch_event("mousedown"); + const_cast(node)->dispatch_event(MouseEvent::create("mousedown", 2, 3)); } } if (hovered_node_changed) @@ -251,7 +252,7 @@ void HtmlView::mouseup_event(GUI::MouseEvent& event) auto result = layout_root()->hit_test(to_content_position(event.position())); if (result.layout_node) { if (auto* node = result.layout_node->node()) - const_cast(node)->dispatch_event("mouseup"); + const_cast(node)->dispatch_event(MouseEvent::create("mouseup", 2, 3)); } if (event.button() == GUI::MouseButton::Left) { diff --git a/Libraries/LibWeb/Makefile b/Libraries/LibWeb/Makefile index 2b6a5f9d9a..bc36ffeda1 100644 --- a/Libraries/LibWeb/Makefile +++ b/Libraries/LibWeb/Makefile @@ -1,9 +1,11 @@ LIBWEB_OBJS = \ Bindings/CanvasRenderingContext2DWrapper.o \ Bindings/DocumentWrapper.o \ + Bindings/EventWrapper.o \ Bindings/EventListenerWrapper.o \ Bindings/EventTargetWrapper.o \ Bindings/HTMLCanvasElementWrapper.o \ + Bindings/MouseEventWrapper.o \ Bindings/NodeWrapper.o \ Bindings/Wrappable.o \ CSS/DefaultStyleSheetSource.o \ @@ -23,6 +25,7 @@ LIBWEB_OBJS = \ DOM/DocumentType.o \ DOM/Element.o \ DOM/ElementFactory.o \ + DOM/Event.o \ DOM/EventListener.o \ DOM/EventTarget.o \ DOM/HTMLAnchorElement.o \ @@ -43,6 +46,7 @@ LIBWEB_OBJS = \ DOM/HTMLScriptElement.o \ DOM/HTMLStyleElement.o \ DOM/HTMLTitleElement.o \ + DOM/MouseEvent.o \ DOM/Node.o \ DOM/ParentNode.o \ DOM/Text.o \ diff --git a/Libraries/LibWeb/Parser/HTMLParser.cpp b/Libraries/LibWeb/Parser/HTMLParser.cpp index b9690f360f..af945af006 100644 --- a/Libraries/LibWeb/Parser/HTMLParser.cpp +++ b/Libraries/LibWeb/Parser/HTMLParser.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -378,7 +379,7 @@ RefPtr parse_html_document(const StringView& html, const URL& url) }; fire_insertion_callbacks(document); - document->dispatch_event("DOMContentLoaded"); + document->dispatch_event(Event::create("DOMContentLoaded")); return document; }