EventTarget.cpp 5.8 KB

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