EventTarget.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /*
  2. * Copyright (c) 2020-2022, 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/EventNames.h>
  19. #include <LibWeb/HTML/HTMLBodyElement.h>
  20. #include <LibWeb/HTML/HTMLFrameSetElement.h>
  21. #include <LibWeb/UIEvents/EventNames.h>
  22. namespace Web::DOM {
  23. EventTarget::EventTarget(Bindings::ScriptExecutionContext& script_execution_context)
  24. : m_script_execution_context(&script_execution_context)
  25. {
  26. }
  27. EventTarget::~EventTarget()
  28. {
  29. }
  30. // https://dom.spec.whatwg.org/#add-an-event-listener
  31. void EventTarget::add_event_listener(const FlyString& event_name, RefPtr<EventListener> listener)
  32. {
  33. if (listener.is_null())
  34. return;
  35. auto existing_listener = m_listeners.first_matching([&](auto& entry) {
  36. return entry.listener->type() == event_name && &entry.listener->function() == &listener->function() && entry.listener->capture() == listener->capture();
  37. });
  38. if (existing_listener.has_value())
  39. return;
  40. listener->set_type(event_name);
  41. m_listeners.append({ event_name, listener.release_nonnull() });
  42. }
  43. // https://dom.spec.whatwg.org/#remove-an-event-listener
  44. void EventTarget::remove_event_listener(const FlyString& event_name, RefPtr<EventListener> listener)
  45. {
  46. if (listener.is_null())
  47. return;
  48. m_listeners.remove_first_matching([&](auto& entry) {
  49. auto matches = entry.event_name == event_name && &entry.listener->function() == &listener->function() && entry.listener->capture() == listener->capture();
  50. if (matches)
  51. entry.listener->set_removed(true);
  52. return matches;
  53. });
  54. }
  55. void EventTarget::remove_from_event_listener_list(NonnullRefPtr<EventListener> listener)
  56. {
  57. m_listeners.remove_first_matching([&](auto& entry) {
  58. return entry.listener->type() == listener->type() && &entry.listener->function() == &listener->function() && entry.listener->capture() == listener->capture();
  59. });
  60. }
  61. // https://dom.spec.whatwg.org/#dom-eventtarget-dispatchevent
  62. ExceptionOr<bool> EventTarget::dispatch_event_binding(NonnullRefPtr<Event> event)
  63. {
  64. if (event->dispatched())
  65. return DOM::InvalidStateError::create("The event is already being dispatched.");
  66. if (!event->initialized())
  67. return DOM::InvalidStateError::create("Cannot dispatch an uninitialized event.");
  68. event->set_is_trusted(false);
  69. return dispatch_event(event);
  70. }
  71. // https://html.spec.whatwg.org/multipage/webappapis.html#window-reflecting-body-element-event-handler-set
  72. static bool is_window_reflecting_body_element_event_handler(FlyString const& name)
  73. {
  74. return name.is_one_of(
  75. HTML::EventNames::blur,
  76. HTML::EventNames::error,
  77. HTML::EventNames::focus,
  78. HTML::EventNames::load,
  79. UIEvents::EventNames::resize,
  80. "scroll");
  81. }
  82. // https://html.spec.whatwg.org/multipage/webappapis.html#windoweventhandlers
  83. static bool is_window_event_handler(FlyString const& name)
  84. {
  85. return name.is_one_of(
  86. HTML::EventNames::afterprint,
  87. HTML::EventNames::beforeprint,
  88. HTML::EventNames::beforeunload,
  89. HTML::EventNames::hashchange,
  90. HTML::EventNames::languagechange,
  91. HTML::EventNames::message,
  92. HTML::EventNames::messageerror,
  93. HTML::EventNames::offline,
  94. HTML::EventNames::online,
  95. HTML::EventNames::pagehide,
  96. HTML::EventNames::pageshow,
  97. HTML::EventNames::popstate,
  98. HTML::EventNames::rejectionhandled,
  99. HTML::EventNames::storage,
  100. HTML::EventNames::unhandledrejection,
  101. HTML::EventNames::unload);
  102. }
  103. // https://html.spec.whatwg.org/multipage/webappapis.html#determining-the-target-of-an-event-handler
  104. static EventTarget* determine_target_of_event_handler(EventTarget& event_target, FlyString const& name)
  105. {
  106. // To determine the target of an event handler, given an EventTarget object eventTarget on which the event handler is exposed,
  107. // and an event handler name name, the following steps are taken:
  108. // 1. If eventTarget is not a body element or a frameset element, then return eventTarget.
  109. if (!is<HTML::HTMLBodyElement>(event_target) && !is<HTML::HTMLFrameSetElement>(event_target))
  110. return &event_target;
  111. auto& event_target_element = static_cast<HTML::HTMLElement&>(event_target);
  112. // 2. If name is not the name of an attribute member of the WindowEventHandlers interface mixin and the Window-reflecting
  113. // body element event handler set does not contain name, then return eventTarget.
  114. if (!is_window_event_handler(name) && !is_window_reflecting_body_element_event_handler(name))
  115. return &event_target;
  116. // 3. If eventTarget's node document is not an active document, then return null.
  117. if (!event_target_element.document().is_active())
  118. return nullptr;
  119. // Return eventTarget's node document's relevant global object.
  120. return &event_target_element.document().window();
  121. }
  122. HTML::EventHandler EventTarget::event_handler_attribute(FlyString const& name)
  123. {
  124. auto target = determine_target_of_event_handler(*this, name);
  125. if (!target)
  126. return {};
  127. for (auto& listener : target->listeners()) {
  128. if (listener.event_name == name && listener.listener->is_attribute()) {
  129. return HTML::EventHandler { JS::make_handle(&listener.listener->function()) };
  130. }
  131. }
  132. return {};
  133. }
  134. void EventTarget::set_event_handler_attribute(FlyString const& name, HTML::EventHandler value)
  135. {
  136. auto target = determine_target_of_event_handler(*this, name);
  137. if (!target)
  138. return;
  139. RefPtr<DOM::EventListener> listener;
  140. if (!value.callback.is_null()) {
  141. listener = adopt_ref(*new DOM::EventListener(move(value.callback), true));
  142. } else {
  143. StringBuilder builder;
  144. builder.appendff("function {}(event) {{\n{}\n}}", name, value.string);
  145. auto parser = JS::Parser(JS::Lexer(builder.string_view()));
  146. auto program = parser.parse_function_node<JS::FunctionExpression>();
  147. if (parser.has_errors()) {
  148. dbgln("Failed to parse script in event handler attribute '{}'", name);
  149. return;
  150. }
  151. auto* function = JS::ECMAScriptFunctionObject::create(target->script_execution_context()->realm().global_object(), name, program->source_text(), program->body(), program->parameters(), program->function_length(), nullptr, nullptr, JS::FunctionKind::Normal, false, false);
  152. VERIFY(function);
  153. listener = adopt_ref(*new DOM::EventListener(JS::make_handle(static_cast<JS::FunctionObject*>(function)), true));
  154. }
  155. if (listener) {
  156. for (auto& registered_listener : target->listeners()) {
  157. if (registered_listener.event_name == name && registered_listener.listener->is_attribute()) {
  158. target->remove_event_listener(name, registered_listener.listener);
  159. break;
  160. }
  161. }
  162. target->add_event_listener(name, listener.release_nonnull());
  163. }
  164. }
  165. bool EventTarget::dispatch_event(NonnullRefPtr<Event> event)
  166. {
  167. return EventDispatcher::dispatch(*this, move(event));
  168. }
  169. }