EventTarget.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /*
  2. * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/StringBuilder.h>
  7. #include <LibJS/Interpreter.h>
  8. #include <LibJS/Parser.h>
  9. #include <LibJS/Runtime/ECMAScriptFunctionObject.h>
  10. #include <LibWeb/Bindings/ScriptExecutionContext.h>
  11. #include <LibWeb/DOM/Document.h>
  12. #include <LibWeb/DOM/Event.h>
  13. #include <LibWeb/DOM/EventDispatcher.h>
  14. #include <LibWeb/DOM/EventListener.h>
  15. #include <LibWeb/DOM/EventTarget.h>
  16. #include <LibWeb/DOM/Window.h>
  17. #include <LibWeb/HTML/EventHandler.h>
  18. #include <LibWeb/HTML/HTMLBodyElement.h>
  19. #include <LibWeb/HTML/HTMLFrameSetElement.h>
  20. namespace Web::DOM {
  21. EventTarget::EventTarget(Bindings::ScriptExecutionContext& script_execution_context)
  22. : m_script_execution_context(&script_execution_context)
  23. {
  24. }
  25. EventTarget::~EventTarget()
  26. {
  27. }
  28. // https://dom.spec.whatwg.org/#add-an-event-listener
  29. void EventTarget::add_event_listener(const FlyString& event_name, RefPtr<EventListener> listener)
  30. {
  31. if (listener.is_null())
  32. return;
  33. auto existing_listener = m_listeners.first_matching([&](auto& entry) {
  34. return entry.listener->type() == event_name && &entry.listener->function() == &listener->function() && entry.listener->capture() == listener->capture();
  35. });
  36. if (existing_listener.has_value())
  37. return;
  38. listener->set_type(event_name);
  39. m_listeners.append({ event_name, listener.release_nonnull() });
  40. }
  41. // https://dom.spec.whatwg.org/#remove-an-event-listener
  42. void EventTarget::remove_event_listener(const FlyString& event_name, RefPtr<EventListener> listener)
  43. {
  44. if (listener.is_null())
  45. return;
  46. m_listeners.remove_first_matching([&](auto& entry) {
  47. auto matches = entry.event_name == event_name && &entry.listener->function() == &listener->function() && entry.listener->capture() == listener->capture();
  48. if (matches)
  49. entry.listener->set_removed(true);
  50. return matches;
  51. });
  52. }
  53. void EventTarget::remove_from_event_listener_list(NonnullRefPtr<EventListener> listener)
  54. {
  55. m_listeners.remove_first_matching([&](auto& entry) {
  56. return entry.listener->type() == listener->type() && &entry.listener->function() == &listener->function() && entry.listener->capture() == listener->capture();
  57. });
  58. }
  59. // https://dom.spec.whatwg.org/#dom-eventtarget-dispatchevent
  60. ExceptionOr<bool> EventTarget::dispatch_event_binding(NonnullRefPtr<Event> event)
  61. {
  62. if (event->dispatched())
  63. return DOM::InvalidStateError::create("The event is already being dispatched.");
  64. if (!event->initialized())
  65. return DOM::InvalidStateError::create("Cannot dispatch an uninitialized event.");
  66. event->set_is_trusted(false);
  67. return dispatch_event(event);
  68. }
  69. // https://html.spec.whatwg.org/multipage/webappapis.html#determining-the-target-of-an-event-handler
  70. static EventTarget* determine_target_of_event_handler(EventTarget& event_target, FlyString const& name)
  71. {
  72. // To determine the target of an event handler, given an EventTarget object eventTarget on which the event handler is exposed,
  73. // and an event handler name name, the following steps are taken:
  74. // 1. If eventTarget is not a body element or a frameset element, then return eventTarget.
  75. if (!is<HTML::HTMLBodyElement>(event_target) && !is<HTML::HTMLFrameSetElement>(event_target))
  76. return &event_target;
  77. auto& event_target_element = static_cast<HTML::HTMLElement&>(event_target);
  78. // FIXME: 2. If name is not the name of an attribute member of the WindowEventHandlers interface mixin and the Window-reflecting
  79. // body element event handler set does not contain name, then return eventTarget.
  80. (void)name;
  81. // 3. If eventTarget's node document is not an active document, then return null.
  82. if (!event_target_element.document().is_active())
  83. return nullptr;
  84. // Return eventTarget's node document's relevant global object.
  85. return &event_target_element.document().window();
  86. }
  87. HTML::EventHandler EventTarget::event_handler_attribute(FlyString const& name)
  88. {
  89. auto target = determine_target_of_event_handler(*this, name);
  90. if (!target)
  91. return {};
  92. for (auto& listener : target->listeners()) {
  93. if (listener.event_name == name && listener.listener->is_attribute()) {
  94. return HTML::EventHandler { JS::make_handle(&listener.listener->function()) };
  95. }
  96. }
  97. return {};
  98. }
  99. void EventTarget::set_event_handler_attribute(FlyString const& name, HTML::EventHandler value)
  100. {
  101. auto target = determine_target_of_event_handler(*this, name);
  102. if (!target)
  103. return;
  104. RefPtr<DOM::EventListener> listener;
  105. if (!value.callback.is_null()) {
  106. listener = adopt_ref(*new DOM::EventListener(move(value.callback), true));
  107. } else {
  108. StringBuilder builder;
  109. builder.appendff("function {}(event) {{\n{}\n}}", name, value.string);
  110. auto parser = JS::Parser(JS::Lexer(builder.string_view()));
  111. auto program = parser.parse_function_node<JS::FunctionExpression>();
  112. if (parser.has_errors()) {
  113. dbgln("Failed to parse script in event handler attribute '{}'", name);
  114. return;
  115. }
  116. auto* function = JS::ECMAScriptFunctionObject::create(target->script_execution_context()->realm().global_object(), name, program->body(), program->parameters(), program->function_length(), nullptr, nullptr, JS::FunctionKind::Normal, false, false);
  117. VERIFY(function);
  118. listener = adopt_ref(*new DOM::EventListener(JS::make_handle(static_cast<JS::FunctionObject*>(function)), true));
  119. }
  120. if (listener) {
  121. for (auto& registered_listener : target->listeners()) {
  122. if (registered_listener.event_name == name && registered_listener.listener->is_attribute()) {
  123. target->remove_event_listener(name, registered_listener.listener);
  124. break;
  125. }
  126. }
  127. target->add_event_listener(name, listener.release_nonnull());
  128. }
  129. }
  130. bool EventTarget::dispatch_event(NonnullRefPtr<Event> event)
  131. {
  132. return EventDispatcher::dispatch(*this, move(event));
  133. }
  134. }