Serenity Operating System
at master 261 lines 10 kB view raw
1/* 2 * Copyright (c) 2020, the SerenityOS developers. 3 * Copyright (c) 2021, Luke Wilde <lukew@serenityos.org> 4 * Copyright (c) 2022, Andreas Kling <kling@serenityos.org> 5 * 6 * SPDX-License-Identifier: BSD-2-Clause 7 */ 8 9#include <AK/TypeCasts.h> 10#include <LibWeb/Bindings/Intrinsics.h> 11#include <LibWeb/DOM/Event.h> 12#include <LibWeb/DOM/Node.h> 13#include <LibWeb/DOM/ShadowRoot.h> 14 15namespace Web::DOM { 16 17WebIDL::ExceptionOr<JS::NonnullGCPtr<Event>> Event::create(JS::Realm& realm, DeprecatedFlyString const& event_name, EventInit const& event_init) 18{ 19 return MUST_OR_THROW_OOM(realm.heap().allocate<Event>(realm, realm, event_name, event_init)); 20} 21 22WebIDL::ExceptionOr<JS::NonnullGCPtr<Event>> Event::construct_impl(JS::Realm& realm, DeprecatedFlyString const& event_name, EventInit const& event_init) 23{ 24 return create(realm, event_name, event_init); 25} 26 27Event::Event(JS::Realm& realm, DeprecatedFlyString const& type) 28 : PlatformObject(realm) 29 , m_type(type) 30 , m_initialized(true) 31{ 32} 33 34Event::Event(JS::Realm& realm, DeprecatedFlyString const& type, EventInit const& event_init) 35 : PlatformObject(realm) 36 , m_type(type) 37 , m_bubbles(event_init.bubbles) 38 , m_cancelable(event_init.cancelable) 39 , m_composed(event_init.composed) 40 , m_initialized(true) 41{ 42} 43 44JS::ThrowCompletionOr<void> Event::initialize(JS::Realm& realm) 45{ 46 MUST_OR_THROW_OOM(Base::initialize(realm)); 47 set_prototype(&Bindings::ensure_web_prototype<Bindings::EventPrototype>(realm, "Event")); 48 49 return {}; 50} 51 52void Event::visit_edges(Visitor& visitor) 53{ 54 Base::visit_edges(visitor); 55 visitor.visit(m_target.ptr()); 56 visitor.visit(m_related_target.ptr()); 57 visitor.visit(m_current_target.ptr()); 58 for (auto& it : m_path) { 59 visitor.visit(it.invocation_target.ptr()); 60 visitor.visit(it.shadow_adjusted_target.ptr()); 61 visitor.visit(it.related_target.ptr()); 62 for (auto& itit : it.touch_target_list) 63 visitor.visit(itit.ptr()); 64 } 65 for (auto& it : m_touch_target_list) 66 visitor.visit(it.ptr()); 67} 68 69// https://dom.spec.whatwg.org/#concept-event-path-append 70void Event::append_to_path(EventTarget& invocation_target, JS::GCPtr<EventTarget> shadow_adjusted_target, JS::GCPtr<EventTarget> related_target, TouchTargetList& touch_targets, bool slot_in_closed_tree) 71{ 72 // 1. Let invocationTargetInShadowTree be false. 73 bool invocation_target_in_shadow_tree = false; 74 75 // 3. Let root-of-closed-tree be false. 76 bool root_of_closed_tree = false; 77 78 // 2. If invocationTarget is a node and its root is a shadow root, then set invocationTargetInShadowTree to true. 79 if (is<Node>(invocation_target)) { 80 auto& invocation_target_node = verify_cast<Node>(invocation_target); 81 if (is<ShadowRoot>(invocation_target_node.root())) 82 invocation_target_in_shadow_tree = true; 83 if (is<ShadowRoot>(invocation_target_node)) { 84 auto& invocation_target_shadow_root = verify_cast<ShadowRoot>(invocation_target_node); 85 // 4. If invocationTarget is a shadow root whose mode is "closed", then set root-of-closed-tree to true. 86 root_of_closed_tree = invocation_target_shadow_root.mode() == Bindings::ShadowRootMode::Closed; 87 } 88 } 89 90 // 5. Append a new struct to event’s path whose invocation target is invocationTarget, invocation-target-in-shadow-tree is invocationTargetInShadowTree, 91 // shadow-adjusted target is shadowAdjustedTarget, relatedTarget is relatedTarget, touch target list is touchTargets, root-of-closed-tree is root-of-closed-tree, 92 // and slot-in-closed-tree is slot-in-closed-tree. 93 m_path.append({ invocation_target, invocation_target_in_shadow_tree, shadow_adjusted_target, related_target, touch_targets, root_of_closed_tree, slot_in_closed_tree, m_path.size() }); 94} 95 96void Event::set_cancelled_flag() 97{ 98 if (m_cancelable && !m_in_passive_listener) 99 m_cancelled = true; 100} 101 102// https://dom.spec.whatwg.org/#concept-event-initialize 103void Event::initialize_event(DeprecatedString const& type, bool bubbles, bool cancelable) 104{ 105 // 1. Set event’s initialized flag. 106 m_initialized = true; 107 108 // 2. Unset event’s stop propagation flag, stop immediate propagation flag, and canceled flag. 109 m_stop_propagation = false; 110 m_stop_immediate_propagation = false; 111 m_cancelled = false; 112 113 // 3. Set event’s isTrusted attribute to false. 114 m_is_trusted = false; 115 116 // 4. Set event’s target to null. 117 m_target = nullptr; 118 119 // 5. Set event’s type attribute to type. 120 m_type = type; 121 122 // 6. Set event’s bubbles attribute to bubbles. 123 m_bubbles = bubbles; 124 125 // 8. Set event’s cancelable attribute to cancelable. 126 m_cancelable = cancelable; 127} 128 129// https://dom.spec.whatwg.org/#dom-event-initevent 130void Event::init_event(DeprecatedString const& type, bool bubbles, bool cancelable) 131{ 132 // 1. If this’s dispatch flag is set, then return. 133 if (m_dispatch) 134 return; 135 136 // 2. Initialize this with type, bubbles, and cancelable. 137 initialize_event(type, bubbles, cancelable); 138} 139 140// https://dom.spec.whatwg.org/#dom-event-timestamp 141double Event::time_stamp() const 142{ 143 return m_time_stamp; 144} 145 146// https://dom.spec.whatwg.org/#dom-event-composedpath 147Vector<JS::Handle<EventTarget>> Event::composed_path() const 148{ 149 // 1. Let composedPath be an empty list. 150 Vector<JS::Handle<EventTarget>> composed_path; 151 152 // 2. Let path be this’s path. (NOTE: Not necessary) 153 154 // 3. If path is empty, then return composedPath. 155 if (m_path.is_empty()) 156 return composed_path; 157 158 // 4. Let currentTarget be this’s currentTarget attribute value. (NOTE: Not necessary) 159 160 // 5. Append currentTarget to composedPath. 161 // NOTE: If path is not empty, then the event is being dispatched and will have a currentTarget. 162 VERIFY(m_current_target); 163 composed_path.append(const_cast<EventTarget*>(m_current_target.ptr())); 164 165 // 6. Let currentTargetIndex be 0. 166 size_t current_target_index = 0; 167 168 // 7. Let currentTargetHiddenSubtreeLevel be 0. 169 size_t current_target_hidden_subtree_level = 0; 170 171 // 8. Let index be path’s size − 1. 172 // 9. While index is greater than or equal to 0: 173 for (ssize_t index = m_path.size() - 1; index >= 0; --index) { 174 auto& path_entry = m_path.at(index); 175 176 // 1. If path[index]'s root-of-closed-tree is true, then increase currentTargetHiddenSubtreeLevel by 1. 177 if (path_entry.root_of_closed_tree) 178 ++current_target_hidden_subtree_level; 179 180 // 2. If path[index]'s invocation target is currentTarget, then set currentTargetIndex to index and break. 181 if (path_entry.invocation_target == m_current_target) { 182 current_target_index = index; 183 break; 184 } 185 186 // 3. If path[index]'s slot-in-closed-tree is true, then decrease currentTargetHiddenSubtreeLevel by 1. 187 if (path_entry.slot_in_closed_tree) 188 --current_target_hidden_subtree_level; 189 190 // 4. Decrease index by 1. 191 } 192 193 // 10. Let currentHiddenLevel and maxHiddenLevel be currentTargetHiddenSubtreeLevel. 194 size_t current_hidden_level = current_target_hidden_subtree_level; 195 size_t max_hidden_level = current_target_hidden_subtree_level; 196 197 // 11. Set index to currentTargetIndex − 1. 198 // 12. While index is greater than or equal to 0: 199 for (ssize_t index = current_target_index - 1; index >= 0; --index) { 200 auto& path_entry = m_path.at(index); 201 202 // 1. If path[index]'s root-of-closed-tree is true, then increase currentHiddenLevel by 1. 203 if (path_entry.root_of_closed_tree) 204 ++current_hidden_level; 205 206 // 2. If currentHiddenLevel is less than or equal to maxHiddenLevel, then prepend path[index]'s invocation target to composedPath. 207 if (current_hidden_level <= max_hidden_level) { 208 VERIFY(path_entry.invocation_target); 209 composed_path.prepend(const_cast<EventTarget*>(path_entry.invocation_target.ptr())); 210 } 211 212 // 3. If path[index]'s slot-in-closed-tree is true, then: 213 if (path_entry.slot_in_closed_tree) { 214 // 1. Decrease currentHiddenLevel by 1. 215 --current_hidden_level; 216 217 // 2. If currentHiddenLevel is less than maxHiddenLevel, then set maxHiddenLevel to currentHiddenLevel. 218 if (current_hidden_level < max_hidden_level) 219 max_hidden_level = current_hidden_level; 220 } 221 222 // 4. Decrease index by 1. 223 } 224 225 // 13. Set currentHiddenLevel and maxHiddenLevel to currentTargetHiddenSubtreeLevel. 226 current_hidden_level = current_target_hidden_subtree_level; 227 max_hidden_level = current_target_hidden_subtree_level; 228 229 // 14. Set index to currentTargetIndex + 1. 230 // 15. While index is less than path’s size: 231 for (size_t index = current_target_index + 1; index < m_path.size(); ++index) { 232 auto& path_entry = m_path.at(index); 233 234 // 1. If path[index]'s slot-in-closed-tree is true, then increase currentHiddenLevel by 1. 235 if (path_entry.slot_in_closed_tree) 236 ++current_hidden_level; 237 238 // 2. If currentHiddenLevel is less than or equal to maxHiddenLevel, then append path[index]'s invocation target to composedPath. 239 if (current_hidden_level <= max_hidden_level) { 240 VERIFY(path_entry.invocation_target); 241 composed_path.append(const_cast<EventTarget*>(path_entry.invocation_target.ptr())); 242 } 243 244 // 3. If path[index]'s root-of-closed-tree is true, then: 245 if (path_entry.root_of_closed_tree) { 246 // 1. Decrease currentHiddenLevel by 1. 247 --current_hidden_level; 248 249 // 2. If currentHiddenLevel is less than maxHiddenLevel, then set maxHiddenLevel to currentHiddenLevel. 250 if (current_hidden_level < max_hidden_level) 251 max_hidden_level = current_hidden_level; 252 } 253 254 // 4. Increase index by 1. 255 } 256 257 // 16. Return composedPath. 258 return composed_path; 259} 260 261}