1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 18:28:12 +00:00

LibWeb: Start working on DOM event support

This patch adds the EventTarget class and makes Node inherit from it.

You can register event listeners on an EventTarget, and when you call
dispatch_event() on it, the event listeners will get invoked.

An event listener is basically a wrapper around a JS::Function*.

This is pretty far from how DOM events should eventually work, but it's
a place to start and we'll build more on top of this. :^)
This commit is contained in:
Andreas Kling 2020-03-18 15:22:31 +01:00
parent 97674da502
commit f39e5352f0
17 changed files with 310 additions and 11 deletions

View file

View file

@ -0,0 +1,26 @@
#pragma once
#include <AK/RefCounted.h>
#include <LibJS/Forward.h>
#include <LibWeb/Bindings/Wrappable.h>
namespace Web {
class EventListener
: public RefCounted<EventListener>
, public Bindings::Wrappable {
public:
using WrapperType = Bindings::EventListenerWrapper;
explicit EventListener(JS::Function* function)
: m_function(function)
{
}
JS::Function* function() { return m_function; }
private:
JS::Function* m_function { nullptr };
};
}

View file

@ -0,0 +1,19 @@
#include <LibWeb/DOM/EventListener.h>
#include <LibWeb/DOM/EventTarget.h>
namespace Web {
EventTarget::EventTarget()
{
}
EventTarget::~EventTarget()
{
}
void EventTarget::add_event_listener(String event_name, NonnullRefPtr<EventListener> listener)
{
m_listeners.append({ move(event_name), move(listener) });
}
}

View file

@ -0,0 +1,41 @@
#pragma once
#include <AK/Noncopyable.h>
#include <AK/String.h>
#include <AK/Vector.h>
#include <LibWeb/Forward.h>
namespace Web {
class EventTarget {
AK_MAKE_NONCOPYABLE(EventTarget);
AK_MAKE_NONMOVABLE(EventTarget);
public:
virtual ~EventTarget();
void ref() { ref_event_target(); }
void unref() { unref_event_target(); }
void add_event_listener(String event_name, NonnullRefPtr<EventListener>);
virtual void dispatch_event(String event_name) = 0;
struct EventListenerRegistration {
String event_name;
NonnullRefPtr<EventListener> listener;
};
const Vector<EventListenerRegistration>& listeners() const { return m_listeners; }
protected:
EventTarget();
virtual void ref_event_target() = 0;
virtual void unref_event_target() = 0;
private:
Vector<EventListenerRegistration> m_listeners;
};
}

View file

@ -25,8 +25,14 @@
*/
#include <AK/StringBuilder.h>
#include <LibJS/AST.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/Function.h>
#include <LibJS/Runtime/ScriptFunction.h>
#include <LibWeb/Bindings/NodeWrapper.h>
#include <LibWeb/CSS/StyleResolver.h>
#include <LibWeb/DOM/Element.h>
#include <LibWeb/DOM/EventListener.h>
#include <LibWeb/DOM/HTMLAnchorElement.h>
#include <LibWeb/DOM/Node.h>
#include <LibWeb/Layout/LayoutBlock.h>
@ -117,4 +123,21 @@ bool Node::is_link() const
return enclosing_link->has_attribute("href");
}
void Node::dispatch_event(String event_name)
{
for (auto& listener : listeners()) {
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, {});
}
}
// FIXME: This is a hack. We should follow the real rules of event bubbling.
if (parent())
parent()->dispatch_event(move(event_name));
}
}

View file

@ -31,6 +31,7 @@
#include <AK/String.h>
#include <AK/Vector.h>
#include <LibWeb/Bindings/Wrappable.h>
#include <LibWeb/DOM/EventTarget.h>
#include <LibWeb/TreeNode.h>
namespace Web {
@ -56,10 +57,19 @@ class StyleProperties;
class Node
: public TreeNode<Node>
, public EventTarget
, public Bindings::Wrappable {
public:
using WrapperType = Bindings::NodeWrapper;
using TreeNode<Node>::ref;
using TreeNode<Node>::unref;
// ^EventTarget
virtual void ref_event_target() final { ref(); }
virtual void unref_event_target() final { unref(); }
virtual void dispatch_event(String event_name) final;
virtual ~Node();
NodeType type() const { return m_type; }