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. :^)
This commit is contained in:
Andreas Kling 2020-03-21 18:17:18 +01:00
parent b196665131
commit 4dde36844b
Notes: sideshowbarker 2024-07-19 08:11:59 +09:00
15 changed files with 311 additions and 10 deletions

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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 <LibWeb/Bindings/EventWrapper.h>
#include <LibWeb/Bindings/MouseEventWrapper.h>
#include <LibWeb/DOM/MouseEvent.h>
namespace Web {
namespace Bindings {
EventWrapper* wrap(JS::Heap& heap, Event& event)
{
if (event.is_mouse_event())
return static_cast<EventWrapper*>(wrap_impl(heap, static_cast<MouseEvent&>(event)));
return static_cast<EventWrapper*>(wrap_impl(heap, event));
}
EventWrapper::EventWrapper(Event& event)
: m_event(event)
{
}
EventWrapper::~EventWrapper()
{
}
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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 <LibWeb/Bindings/Wrapper.h>
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<Event> m_event;
};
EventWrapper* wrap(JS::Heap&, Event&);
}
}

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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 <AK/Function.h>
#include <LibJS/Runtime/Function.h>
#include <LibWeb/Bindings/MouseEventWrapper.h>
#include <LibWeb/DOM/MouseEvent.h>
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<const MouseEvent&>(EventWrapper::event());
}
MouseEvent& MouseEventWrapper::event()
{
return static_cast<MouseEvent&>(EventWrapper::event());
}
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* 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 <LibWeb/Bindings/EventWrapper.h>
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"; }
};
}
}

View file

View file

@ -0,0 +1,36 @@
#pragma once
#include <AK/RefCounted.h>
#include <AK/String.h>
#include <LibWeb/Bindings/Wrappable.h>
namespace Web {
class Event
: public RefCounted<Event>
, public Bindings::Wrappable {
public:
using WrapperType = Bindings::EventWrapper;
static NonnullRefPtr<Event> 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;
};
}

View file

@ -45,7 +45,7 @@ public:
void add_event_listener(String event_name, NonnullRefPtr<EventListener>);
virtual void dispatch_event(String event_name) = 0;
virtual void dispatch_event(NonnullRefPtr<Event>) = 0;
struct EventListenerRegistration {
String event_name;

View file

View file

@ -0,0 +1,36 @@
#pragma once
#include <LibWeb/DOM/Event.h>
namespace Web {
class MouseEvent final : public Event {
public:
using WrapperType = Bindings::MouseEventWrapper;
static NonnullRefPtr<MouseEvent> 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 };
};
}

View file

@ -29,9 +29,11 @@
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/Function.h>
#include <LibJS/Runtime/ScriptFunction.h>
#include <LibWeb/Bindings/EventWrapper.h>
#include <LibWeb/Bindings/NodeWrapper.h>
#include <LibWeb/CSS/StyleResolver.h>
#include <LibWeb/DOM/Element.h>
#include <LibWeb/DOM/Event.h>
#include <LibWeb/DOM/EventListener.h>
#include <LibWeb/DOM/HTMLAnchorElement.h>
#include <LibWeb/DOM/Node.h>
@ -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> event)
{
for (auto& listener : listeners()) {
if (listener.event_name == event_name) {
if (listener.event_name == event->name()) {
auto* function = const_cast<EventListener&>(*listener.listener).function();
static_cast<const JS::ScriptFunction*>(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));
}
}

View file

@ -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<Event>) final;
virtual ~Node();

View file

@ -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;

View file

@ -36,6 +36,7 @@
#include <LibWeb/DOM/ElementFactory.h>
#include <LibWeb/DOM/HTMLAnchorElement.h>
#include <LibWeb/DOM/HTMLImageElement.h>
#include <LibWeb/DOM/MouseEvent.h>
#include <LibWeb/DOM/Text.h>
#include <LibWeb/Dump.h>
#include <LibWeb/Frame.h>
@ -184,7 +185,7 @@ void HtmlView::mousemove_event(GUI::MouseEvent& event)
#endif
is_hovering_link = true;
}
const_cast<Node*>(node)->dispatch_event("mousemove");
const_cast<Node*>(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*>(node)->dispatch_event("mousedown");
const_cast<Node*>(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*>(node)->dispatch_event("mouseup");
const_cast<Node*>(node)->dispatch_event(MouseEvent::create("mouseup", 2, 3));
}
if (event.button() == GUI::MouseButton::Left) {

View file

@ -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 \

View file

@ -32,6 +32,7 @@
#include <LibWeb/DOM/DocumentType.h>
#include <LibWeb/DOM/Element.h>
#include <LibWeb/DOM/ElementFactory.h>
#include <LibWeb/DOM/Event.h>
#include <LibWeb/DOM/Text.h>
#include <LibWeb/Parser/HTMLParser.h>
#include <ctype.h>
@ -378,7 +379,7 @@ RefPtr<Document> 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;
}