1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Dirk Mueller (mueller@kde.org)
5 * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
6 * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
7 * (C) 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
22 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 */
31
32 #include "third_party/blink/renderer/core/dom/events/event_target.h"
33
34 #include <memory>
35
36 #include "base/format_macros.h"
37 #include "third_party/blink/public/web/web_settings.h"
38 #include "third_party/blink/renderer/bindings/core/v8/add_event_listener_options_or_boolean.h"
39 #include "third_party/blink/renderer/bindings/core/v8/event_listener_options_or_boolean.h"
40 #include "third_party/blink/renderer/bindings/core/v8/js_based_event_listener.h"
41 #include "third_party/blink/renderer/bindings/core/v8/js_event_listener.h"
42 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
43 #include "third_party/blink/renderer/core/dom/abort_signal.h"
44 #include "third_party/blink/renderer/core/dom/events/add_event_listener_options_resolved.h"
45 #include "third_party/blink/renderer/core/dom/events/event.h"
46 #include "third_party/blink/renderer/core/dom/events/event_dispatch_forbidden_scope.h"
47 #include "third_party/blink/renderer/core/dom/events/event_target_impl.h"
48 #include "third_party/blink/renderer/core/editing/editor.h"
49 #include "third_party/blink/renderer/core/events/event_util.h"
50 #include "third_party/blink/renderer/core/events/pointer_event.h"
51 #include "third_party/blink/renderer/core/frame/frame_console.h"
52 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
53 #include "third_party/blink/renderer/core/frame/performance_monitor.h"
54 #include "third_party/blink/renderer/core/frame/settings.h"
55 #include "third_party/blink/renderer/core/frame/web_feature.h"
56 #include "third_party/blink/renderer/core/inspector/console_message.h"
57 #include "third_party/blink/renderer/core/probe/core_probes.h"
58 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
59 #include "third_party/blink/renderer/platform/bindings/v8_dom_activity_logger.h"
60 #include "third_party/blink/renderer/platform/instrumentation/histogram.h"
61 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
62 #include "third_party/blink/renderer/platform/wtf/assertions.h"
63 #include "third_party/blink/renderer/platform/wtf/functional.h"
64 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
65 #include "third_party/blink/renderer/platform/wtf/threading.h"
66 #include "third_party/blink/renderer/platform/wtf/vector.h"
67
68 namespace blink {
69 namespace {
70
71 enum PassiveForcedListenerResultType {
72 kPreventDefaultNotCalled,
73 kDocumentLevelTouchPreventDefaultCalled,
74 kPassiveForcedListenerResultTypeMax
75 };
76
EventPassiveMode(const RegisteredEventListener & event_listener)77 Event::PassiveMode EventPassiveMode(
78 const RegisteredEventListener& event_listener) {
79 if (!event_listener.Passive()) {
80 if (event_listener.PassiveSpecified())
81 return Event::PassiveMode::kNotPassive;
82 return Event::PassiveMode::kNotPassiveDefault;
83 }
84 if (event_listener.PassiveForcedForDocumentTarget())
85 return Event::PassiveMode::kPassiveForcedDocumentLevel;
86 if (event_listener.PassiveSpecified())
87 return Event::PassiveMode::kPassive;
88 return Event::PassiveMode::kPassiveDefault;
89 }
90
WindowSettings(LocalDOMWindow * executing_window)91 Settings* WindowSettings(LocalDOMWindow* executing_window) {
92 if (executing_window) {
93 if (LocalFrame* frame = executing_window->GetFrame()) {
94 return frame->GetSettings();
95 }
96 }
97 return nullptr;
98 }
99
IsTouchScrollBlockingEvent(const AtomicString & event_type)100 bool IsTouchScrollBlockingEvent(const AtomicString& event_type) {
101 return event_type == event_type_names::kTouchstart ||
102 event_type == event_type_names::kTouchmove;
103 }
104
IsWheelScrollBlockingEvent(const AtomicString & event_type)105 bool IsWheelScrollBlockingEvent(const AtomicString& event_type) {
106 return event_type == event_type_names::kMousewheel ||
107 event_type == event_type_names::kWheel;
108 }
109
IsScrollBlockingEvent(const AtomicString & event_type)110 bool IsScrollBlockingEvent(const AtomicString& event_type) {
111 return IsTouchScrollBlockingEvent(event_type) ||
112 IsWheelScrollBlockingEvent(event_type);
113 }
114
IsInstrumentedForAsyncStack(const AtomicString & event_type)115 bool IsInstrumentedForAsyncStack(const AtomicString& event_type) {
116 return event_type == event_type_names::kLoad ||
117 event_type == event_type_names::kError;
118 }
119
BlockedEventsWarningThreshold(ExecutionContext * context,const Event & event)120 base::TimeDelta BlockedEventsWarningThreshold(ExecutionContext* context,
121 const Event& event) {
122 if (!event.cancelable())
123 return base::TimeDelta();
124 if (!IsScrollBlockingEvent(event.type()))
125 return base::TimeDelta();
126 return PerformanceMonitor::Threshold(context,
127 PerformanceMonitor::kBlockedEvent);
128 }
129
ReportBlockedEvent(EventTarget & target,const Event & event,RegisteredEventListener * registered_listener,base::TimeDelta delayed)130 void ReportBlockedEvent(EventTarget& target,
131 const Event& event,
132 RegisteredEventListener* registered_listener,
133 base::TimeDelta delayed) {
134 JSBasedEventListener* listener =
135 DynamicTo<JSBasedEventListener>(registered_listener->Callback());
136 if (!listener)
137 return;
138
139 String message_text = String::Format(
140 "Handling of '%s' input event was delayed for %" PRId64
141 " ms due to main thread being busy. "
142 "Consider marking event handler as 'passive' to make the page more "
143 "responsive.",
144 event.type().GetString().Utf8().c_str(), delayed.InMilliseconds());
145 PerformanceMonitor::ReportGenericViolation(
146 target.GetExecutionContext(), PerformanceMonitor::kBlockedEvent,
147 message_text, delayed, listener->GetSourceLocation(target));
148 registered_listener->SetBlockedEventWarningEmitted();
149 }
150
151 // UseCounts the event if it has the specified type. Returns true iff the event
152 // type matches.
CheckTypeThenUseCount(const Event & event,const AtomicString & event_type_to_count,const WebFeature feature,Document & document)153 bool CheckTypeThenUseCount(const Event& event,
154 const AtomicString& event_type_to_count,
155 const WebFeature feature,
156 Document& document) {
157 if (event.type() != event_type_to_count)
158 return false;
159 UseCounter::Count(document, feature);
160 return true;
161 }
162
CountFiringEventListeners(const Event & event,const LocalDOMWindow * executing_window)163 void CountFiringEventListeners(const Event& event,
164 const LocalDOMWindow* executing_window) {
165 if (!executing_window)
166 return;
167 if (!executing_window->document())
168 return;
169 Document& document = *executing_window->document();
170
171 if (event.type() == event_type_names::kToggle &&
172 document.ToggleDuringParsing()) {
173 UseCounter::Count(document, WebFeature::kToggleEventHandlerDuringParsing);
174 return;
175 }
176 if (CheckTypeThenUseCount(event, event_type_names::kBeforeunload,
177 WebFeature::kDocumentBeforeUnloadFired, document)) {
178 if (executing_window != executing_window->top())
179 UseCounter::Count(document, WebFeature::kSubFrameBeforeUnloadFired);
180 return;
181 }
182 if (CheckTypeThenUseCount(event, event_type_names::kPointerdown,
183 WebFeature::kPointerDownFired, document)) {
184 if (IsA<PointerEvent>(event) &&
185 static_cast<const PointerEvent&>(event).pointerType() == "touch") {
186 UseCounter::Count(document, WebFeature::kPointerDownFiredForTouch);
187 }
188 return;
189 }
190
191 struct CountedEvent {
192 const AtomicString& event_type;
193 const WebFeature feature;
194 };
195 static const CountedEvent counted_events[] = {
196 {event_type_names::kUnload, WebFeature::kDocumentUnloadFired},
197 {event_type_names::kPagehide, WebFeature::kDocumentPageHideFired},
198 {event_type_names::kPageshow, WebFeature::kDocumentPageShowFired},
199 {event_type_names::kDOMFocusIn, WebFeature::kDOMFocusInOutEvent},
200 {event_type_names::kDOMFocusOut, WebFeature::kDOMFocusInOutEvent},
201 {event_type_names::kFocusin, WebFeature::kFocusInOutEvent},
202 {event_type_names::kFocusout, WebFeature::kFocusInOutEvent},
203 {event_type_names::kTextInput, WebFeature::kTextInputFired},
204 {event_type_names::kTouchstart, WebFeature::kTouchStartFired},
205 {event_type_names::kMousedown, WebFeature::kMouseDownFired},
206 {event_type_names::kPointerenter, WebFeature::kPointerEnterLeaveFired},
207 {event_type_names::kPointerleave, WebFeature::kPointerEnterLeaveFired},
208 {event_type_names::kPointerover, WebFeature::kPointerOverOutFired},
209 {event_type_names::kPointerout, WebFeature::kPointerOverOutFired},
210 {event_type_names::kSearch, WebFeature::kSearchEventFired},
211 {event_type_names::kWebkitprerenderstart,
212 WebFeature::kWebkitPrerenderStartEventFired},
213 {event_type_names::kWebkitprerenderstop,
214 WebFeature::kWebkitPrerenderStopEventFired},
215 {event_type_names::kWebkitprerenderload,
216 WebFeature::kWebkitPrerenderLoadEventFired},
217 {event_type_names::kWebkitprerenderdomcontentloaded,
218 WebFeature::kWebkitPrerenderDOMContentLoadedEventFired},
219 };
220 for (const auto& counted_event : counted_events) {
221 if (CheckTypeThenUseCount(event, counted_event.event_type,
222 counted_event.feature, document))
223 return;
224 }
225 }
226
RegisterWithScheduler(ExecutionContext * execution_context,const AtomicString & event_type)227 void RegisterWithScheduler(ExecutionContext* execution_context,
228 const AtomicString& event_type) {
229 if (!execution_context || !execution_context->GetScheduler())
230 return;
231 // TODO(altimin): Ideally we would also support tracking unregistration of
232 // event listeners, but we don't do this for performance reasons.
233 base::Optional<SchedulingPolicy::Feature> feature_for_scheduler;
234 if (event_type == event_type_names::kPageshow) {
235 feature_for_scheduler = SchedulingPolicy::Feature::kPageShowEventListener;
236 } else if (event_type == event_type_names::kPagehide) {
237 feature_for_scheduler = SchedulingPolicy::Feature::kPageHideEventListener;
238 } else if (event_type == event_type_names::kBeforeunload) {
239 feature_for_scheduler =
240 SchedulingPolicy::Feature::kBeforeUnloadEventListener;
241 } else if (event_type == event_type_names::kUnload) {
242 feature_for_scheduler = SchedulingPolicy::Feature::kUnloadEventListener;
243 } else if (event_type == event_type_names::kFreeze) {
244 feature_for_scheduler = SchedulingPolicy::Feature::kFreezeEventListener;
245 } else if (event_type == event_type_names::kResume) {
246 feature_for_scheduler = SchedulingPolicy::Feature::kResumeEventListener;
247 }
248 if (feature_for_scheduler) {
249 execution_context->GetScheduler()->RegisterStickyFeature(
250 feature_for_scheduler.value(),
251 {SchedulingPolicy::RecordMetricsForBackForwardCache()});
252 }
253 }
254
255 } // namespace
256
257 EventTargetData::EventTargetData() = default;
258
259 EventTargetData::~EventTargetData() = default;
260
Trace(Visitor * visitor) const261 void EventTargetData::Trace(Visitor* visitor) const {
262 visitor->Trace(event_listener_map);
263 }
264
265 EventTarget::EventTarget() = default;
266
267 EventTarget::~EventTarget() = default;
268
ToNode()269 Node* EventTarget::ToNode() {
270 return nullptr;
271 }
272
ToDOMWindow() const273 const DOMWindow* EventTarget::ToDOMWindow() const {
274 return nullptr;
275 }
276
ToLocalDOMWindow() const277 const LocalDOMWindow* EventTarget::ToLocalDOMWindow() const {
278 return nullptr;
279 }
280
ToLocalDOMWindow()281 LocalDOMWindow* EventTarget::ToLocalDOMWindow() {
282 return nullptr;
283 }
284
ToMessagePort()285 MessagePort* EventTarget::ToMessagePort() {
286 return nullptr;
287 }
288
ToServiceWorker()289 ServiceWorker* EventTarget::ToServiceWorker() {
290 return nullptr;
291 }
292
ToPortalHost()293 PortalHost* EventTarget::ToPortalHost() {
294 return nullptr;
295 }
296
297 // An instance of EventTargetImpl is returned because EventTarget
298 // is an abstract class, and making it non-abstract is unfavorable
299 // because it will increase the size of EventTarget and all of its
300 // subclasses with code that are mostly unnecessary for them,
301 // resulting in a performance decrease.
302 // We also don't use ImplementedAs=EventTargetImpl in event_target.idl
303 // because it will result in some complications with classes that are
304 // currently derived from EventTarget.
305 // Spec: https://dom.spec.whatwg.org/#dom-eventtarget-eventtarget
Create(ScriptState * script_state)306 EventTarget* EventTarget::Create(ScriptState* script_state) {
307 return MakeGarbageCollected<EventTargetImpl>(script_state);
308 }
309
ExecutingWindow()310 inline LocalDOMWindow* EventTarget::ExecutingWindow() {
311 return DynamicTo<LocalDOMWindow>(GetExecutionContext());
312 }
313
IsTopLevelNode()314 bool EventTarget::IsTopLevelNode() {
315 if (ToLocalDOMWindow())
316 return true;
317
318 Node* node = ToNode();
319 if (!node)
320 return false;
321
322 if (node->IsDocumentNode() || node->GetDocument().documentElement() == node ||
323 node->GetDocument().body() == node) {
324 return true;
325 }
326
327 return false;
328 }
329
SetDefaultAddEventListenerOptions(const AtomicString & event_type,EventListener * event_listener,AddEventListenerOptionsResolved * options)330 void EventTarget::SetDefaultAddEventListenerOptions(
331 const AtomicString& event_type,
332 EventListener* event_listener,
333 AddEventListenerOptionsResolved* options) {
334 options->SetPassiveSpecified(options->hasPassive());
335
336 if (!IsScrollBlockingEvent(event_type)) {
337 if (!options->hasPassive())
338 options->setPassive(false);
339 return;
340 }
341
342 LocalDOMWindow* executing_window = ExecutingWindow();
343 if (executing_window) {
344 if (options->hasPassive()) {
345 UseCounter::Count(executing_window->document(),
346 options->passive()
347 ? WebFeature::kAddEventListenerPassiveTrue
348 : WebFeature::kAddEventListenerPassiveFalse);
349 }
350 }
351
352 if (IsTouchScrollBlockingEvent(event_type)) {
353 if (!options->hasPassive() && IsTopLevelNode()) {
354 options->setPassive(true);
355 options->SetPassiveForcedForDocumentTarget(true);
356 return;
357 }
358 }
359
360 if (IsWheelScrollBlockingEvent(event_type) && IsTopLevelNode()) {
361 if (options->hasPassive()) {
362 if (executing_window) {
363 UseCounter::Count(
364 executing_window->document(),
365 options->passive()
366 ? WebFeature::kAddDocumentLevelPassiveTrueWheelEventListener
367 : WebFeature::kAddDocumentLevelPassiveFalseWheelEventListener);
368 }
369 } else { // !options->hasPassive()
370 if (executing_window) {
371 UseCounter::Count(
372 executing_window->document(),
373 WebFeature::kAddDocumentLevelPassiveDefaultWheelEventListener);
374 }
375 options->setPassive(true);
376 options->SetPassiveForcedForDocumentTarget(true);
377 return;
378 }
379 }
380
381 // For mousewheel event listeners that have the target as the window and
382 // a bound function name of "ssc_wheel" treat and no passive value default
383 // passive to true. See crbug.com/501568.
384 if (event_type == event_type_names::kMousewheel && ToLocalDOMWindow() &&
385 event_listener && !options->hasPassive()) {
386 JSBasedEventListener* v8_listener =
387 DynamicTo<JSBasedEventListener>(event_listener);
388 if (!v8_listener)
389 return;
390 v8::Local<v8::Value> callback_object =
391 v8_listener->GetListenerObject(*this);
392 if (!callback_object.IsEmpty() && callback_object->IsFunction() &&
393 strcmp(
394 "ssc_wheel",
395 *v8::String::Utf8Value(
396 v8::Isolate::GetCurrent(),
397 v8::Local<v8::Function>::Cast(callback_object)->GetName())) ==
398 0) {
399 options->setPassive(true);
400 if (executing_window) {
401 UseCounter::Count(executing_window->document(),
402 WebFeature::kSmoothScrollJSInterventionActivated);
403
404 executing_window->GetFrame()->Console().AddMessage(
405 MakeGarbageCollected<ConsoleMessage>(
406 mojom::ConsoleMessageSource::kIntervention,
407 mojom::ConsoleMessageLevel::kWarning,
408 "Registering mousewheel event as passive due to "
409 "smoothscroll.js usage. The smoothscroll.js library is "
410 "buggy, no longer necessary and degrades performance. See "
411 "https://www.chromestatus.com/feature/5749447073988608"));
412 }
413 return;
414 }
415 }
416
417 if (Settings* settings = WindowSettings(ExecutingWindow())) {
418 switch (settings->GetPassiveListenerDefault()) {
419 case PassiveListenerDefault::kFalse:
420 if (!options->hasPassive())
421 options->setPassive(false);
422 break;
423 case PassiveListenerDefault::kTrue:
424 if (!options->hasPassive())
425 options->setPassive(true);
426 break;
427 case PassiveListenerDefault::kForceAllTrue:
428 options->setPassive(true);
429 break;
430 }
431 } else {
432 if (!options->hasPassive())
433 options->setPassive(false);
434 }
435
436 if (!options->passive() && !options->PassiveSpecified()) {
437 String message_text = String::Format(
438 "Added non-passive event listener to a scroll-blocking '%s' event. "
439 "Consider marking event handler as 'passive' to make the page more "
440 "responsive. See "
441 "https://www.chromestatus.com/feature/5745543795965952",
442 event_type.GetString().Utf8().c_str());
443
444 PerformanceMonitor::ReportGenericViolation(
445 GetExecutionContext(), PerformanceMonitor::kDiscouragedAPIUse,
446 message_text, base::TimeDelta(), nullptr);
447 }
448 }
449
addEventListener(const AtomicString & event_type,V8EventListener * listener)450 bool EventTarget::addEventListener(const AtomicString& event_type,
451 V8EventListener* listener) {
452 EventListener* event_listener = JSEventListener::CreateOrNull(listener);
453 return addEventListener(event_type, event_listener);
454 }
455
addEventListener(const AtomicString & event_type,V8EventListener * listener,const AddEventListenerOptionsOrBoolean & options_union)456 bool EventTarget::addEventListener(
457 const AtomicString& event_type,
458 V8EventListener* listener,
459 const AddEventListenerOptionsOrBoolean& options_union) {
460 EventListener* event_listener = JSEventListener::CreateOrNull(listener);
461
462 if (options_union.IsBoolean()) {
463 return addEventListener(event_type, event_listener,
464 options_union.GetAsBoolean());
465 }
466
467 if (options_union.IsAddEventListenerOptions()) {
468 auto* resolved_options =
469 MakeGarbageCollected<AddEventListenerOptionsResolved>();
470 AddEventListenerOptions* options =
471 options_union.GetAsAddEventListenerOptions();
472 if (options->hasPassive())
473 resolved_options->setPassive(options->passive());
474 if (options->hasOnce())
475 resolved_options->setOnce(options->once());
476 if (options->hasCapture())
477 resolved_options->setCapture(options->capture());
478 if (options->hasSignal())
479 resolved_options->setSignal(options->signal());
480 return addEventListener(event_type, event_listener, resolved_options);
481 }
482
483 return addEventListener(event_type, event_listener);
484 }
485
addEventListener(const AtomicString & event_type,EventListener * listener,bool use_capture)486 bool EventTarget::addEventListener(const AtomicString& event_type,
487 EventListener* listener,
488 bool use_capture) {
489 auto* options = MakeGarbageCollected<AddEventListenerOptionsResolved>();
490 options->setCapture(use_capture);
491 SetDefaultAddEventListenerOptions(event_type, listener, options);
492 return AddEventListenerInternal(event_type, listener, options);
493 }
494
addEventListener(const AtomicString & event_type,EventListener * listener,AddEventListenerOptionsResolved * options)495 bool EventTarget::addEventListener(const AtomicString& event_type,
496 EventListener* listener,
497 AddEventListenerOptionsResolved* options) {
498 SetDefaultAddEventListenerOptions(event_type, listener, options);
499 return AddEventListenerInternal(event_type, listener, options);
500 }
501
AddEventListenerInternal(const AtomicString & event_type,EventListener * listener,const AddEventListenerOptionsResolved * options)502 bool EventTarget::AddEventListenerInternal(
503 const AtomicString& event_type,
504 EventListener* listener,
505 const AddEventListenerOptionsResolved* options) {
506 if (!listener)
507 return false;
508
509 if (event_type == event_type_names::kTouchcancel ||
510 event_type == event_type_names::kTouchend ||
511 event_type == event_type_names::kTouchmove ||
512 event_type == event_type_names::kTouchstart) {
513 if (const LocalDOMWindow* executing_window = ExecutingWindow()) {
514 if (const Document* document = executing_window->document()) {
515 document->CountUse(options->passive()
516 ? WebFeature::kPassiveTouchEventListener
517 : WebFeature::kNonPassiveTouchEventListener);
518 }
519 }
520 }
521
522 V8DOMActivityLogger* activity_logger =
523 V8DOMActivityLogger::CurrentActivityLoggerIfIsolatedWorld();
524 if (activity_logger) {
525 Vector<String> argv;
526 argv.push_back(ToNode() ? ToNode()->nodeName() : InterfaceName());
527 argv.push_back(event_type);
528 activity_logger->LogEvent("blinkAddEventListener", argv.size(),
529 argv.data());
530 }
531
532 RegisteredEventListener registered_listener;
533 bool added = EnsureEventTargetData().event_listener_map.Add(
534 event_type, listener, options, ®istered_listener);
535 if (added) {
536 if (options->signal()) {
537 options->signal()->AddAlgorithm(WTF::Bind(
538 [](EventTarget* event_target, const AtomicString& event_type,
539 const EventListener* listener) {
540 event_target->removeEventListener(event_type, listener);
541 },
542 WrapWeakPersistent(this), event_type, WrapWeakPersistent(listener)));
543 if (const LocalDOMWindow* executing_window = ExecutingWindow()) {
544 if (const Document* document = executing_window->document()) {
545 document->CountUse(WebFeature::kAddEventListenerWithAbortSignal);
546 }
547 }
548 }
549
550 AddedEventListener(event_type, registered_listener);
551 if (IsA<JSBasedEventListener>(listener) &&
552 IsInstrumentedForAsyncStack(event_type)) {
553 probe::AsyncTaskScheduled(GetExecutionContext(), event_type,
554 listener->async_task_id());
555 }
556 }
557 return added;
558 }
559
AddedEventListener(const AtomicString & event_type,RegisteredEventListener & registered_listener)560 void EventTarget::AddedEventListener(
561 const AtomicString& event_type,
562 RegisteredEventListener& registered_listener) {
563 if (const LocalDOMWindow* executing_window = ExecutingWindow()) {
564 if (Document* document = executing_window->document()) {
565 if (event_type == event_type_names::kAuxclick) {
566 UseCounter::Count(*document, WebFeature::kAuxclickAddListenerCount);
567 } else if (event_type == event_type_names::kAppinstalled) {
568 UseCounter::Count(*document, WebFeature::kAppInstalledEventAddListener);
569 } else if (event_util::IsPointerEventType(event_type)) {
570 UseCounter::Count(*document, WebFeature::kPointerEventAddListenerCount);
571 } else if (event_type == event_type_names::kSlotchange) {
572 UseCounter::Count(*document, WebFeature::kSlotChangeEventAddListener);
573 } else if (event_type == event_type_names::kBeforematch) {
574 UseCounter::Count(*document, WebFeature::kBeforematchHandlerRegistered);
575 }
576 }
577 }
578
579 RegisterWithScheduler(GetExecutionContext(), event_type);
580
581 if (event_util::IsDOMMutationEventType(event_type)) {
582 if (ExecutionContext* context = GetExecutionContext()) {
583 String message_text = String::Format(
584 "Added synchronous DOM mutation listener to a '%s' event. "
585 "Consider using MutationObserver to make the page more responsive.",
586 event_type.GetString().Utf8().c_str());
587 PerformanceMonitor::ReportGenericViolation(
588 context, PerformanceMonitor::kDiscouragedAPIUse, message_text,
589 base::TimeDelta(), nullptr);
590 }
591 }
592 }
593
removeEventListener(const AtomicString & event_type,V8EventListener * listener)594 bool EventTarget::removeEventListener(const AtomicString& event_type,
595 V8EventListener* listener) {
596 EventListener* event_listener = JSEventListener::CreateOrNull(listener);
597 return removeEventListener(event_type, event_listener);
598 }
599
removeEventListener(const AtomicString & event_type,V8EventListener * listener,const EventListenerOptionsOrBoolean & options_union)600 bool EventTarget::removeEventListener(
601 const AtomicString& event_type,
602 V8EventListener* listener,
603 const EventListenerOptionsOrBoolean& options_union) {
604 EventListener* event_listener = JSEventListener::CreateOrNull(listener);
605
606 if (options_union.IsBoolean()) {
607 return removeEventListener(event_type, event_listener,
608 options_union.GetAsBoolean());
609 }
610
611 if (options_union.IsEventListenerOptions()) {
612 EventListenerOptions* options = options_union.GetAsEventListenerOptions();
613 return removeEventListener(event_type, event_listener, options);
614 }
615
616 return removeEventListener(event_type, event_listener);
617 }
618
removeEventListener(const AtomicString & event_type,const EventListener * listener,bool use_capture)619 bool EventTarget::removeEventListener(const AtomicString& event_type,
620 const EventListener* listener,
621 bool use_capture) {
622 EventListenerOptions* options = EventListenerOptions::Create();
623 options->setCapture(use_capture);
624 return RemoveEventListenerInternal(event_type, listener, options);
625 }
626
removeEventListener(const AtomicString & event_type,const EventListener * listener,EventListenerOptions * options)627 bool EventTarget::removeEventListener(const AtomicString& event_type,
628 const EventListener* listener,
629 EventListenerOptions* options) {
630 return RemoveEventListenerInternal(event_type, listener, options);
631 }
632
RemoveEventListenerInternal(const AtomicString & event_type,const EventListener * listener,const EventListenerOptions * options)633 bool EventTarget::RemoveEventListenerInternal(
634 const AtomicString& event_type,
635 const EventListener* listener,
636 const EventListenerOptions* options) {
637 if (!listener)
638 return false;
639
640 EventTargetData* d = GetEventTargetData();
641 if (!d)
642 return false;
643
644 wtf_size_t index_of_removed_listener;
645 RegisteredEventListener registered_listener;
646
647 if (!d->event_listener_map.Remove(event_type, listener, options,
648 &index_of_removed_listener,
649 ®istered_listener))
650 return false;
651
652 // Notify firing events planning to invoke the listener at 'index' that
653 // they have one less listener to invoke.
654 if (d->firing_event_iterators) {
655 for (const auto& firing_iterator : *d->firing_event_iterators) {
656 if (event_type != firing_iterator.event_type)
657 continue;
658
659 if (index_of_removed_listener >= firing_iterator.end)
660 continue;
661
662 --firing_iterator.end;
663 // Note that when firing an event listener,
664 // firingIterator.iterator indicates the next event listener
665 // that would fire, not the currently firing event
666 // listener. See EventTarget::fireEventListeners.
667 if (index_of_removed_listener < firing_iterator.iterator)
668 --firing_iterator.iterator;
669 }
670 }
671 RemovedEventListener(event_type, registered_listener);
672 return true;
673 }
674
RemovedEventListener(const AtomicString & event_type,const RegisteredEventListener & registered_listener)675 void EventTarget::RemovedEventListener(
676 const AtomicString& event_type,
677 const RegisteredEventListener& registered_listener) {}
678
GetAttributeRegisteredEventListener(const AtomicString & event_type)679 RegisteredEventListener* EventTarget::GetAttributeRegisteredEventListener(
680 const AtomicString& event_type) {
681 EventListenerVector* listener_vector = GetEventListeners(event_type);
682 if (!listener_vector)
683 return nullptr;
684
685 for (auto& event_listener : *listener_vector) {
686 EventListener* listener = event_listener.Callback();
687 if (GetExecutionContext() && listener->IsEventHandler() &&
688 listener->BelongsToTheCurrentWorld(GetExecutionContext()))
689 return &event_listener;
690 }
691 return nullptr;
692 }
693
SetAttributeEventListener(const AtomicString & event_type,EventListener * listener)694 bool EventTarget::SetAttributeEventListener(const AtomicString& event_type,
695 EventListener* listener) {
696 RegisteredEventListener* registered_listener =
697 GetAttributeRegisteredEventListener(event_type);
698 if (!listener) {
699 if (registered_listener)
700 removeEventListener(event_type, registered_listener->Callback(), false);
701 return false;
702 }
703 if (registered_listener) {
704 if (IsA<JSBasedEventListener>(listener) &&
705 IsInstrumentedForAsyncStack(event_type)) {
706 probe::AsyncTaskScheduled(GetExecutionContext(), event_type,
707 listener->async_task_id());
708 }
709 registered_listener->SetCallback(listener);
710 return true;
711 }
712 return addEventListener(event_type, listener, false);
713 }
714
GetAttributeEventListener(const AtomicString & event_type)715 EventListener* EventTarget::GetAttributeEventListener(
716 const AtomicString& event_type) {
717 RegisteredEventListener* registered_listener =
718 GetAttributeRegisteredEventListener(event_type);
719 if (registered_listener)
720 return registered_listener->Callback();
721 return nullptr;
722 }
723
dispatchEventForBindings(Event * event,ExceptionState & exception_state)724 bool EventTarget::dispatchEventForBindings(Event* event,
725 ExceptionState& exception_state) {
726 if (!event->WasInitialized()) {
727 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
728 "The event provided is uninitialized.");
729 return false;
730 }
731 if (event->IsBeingDispatched()) {
732 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
733 "The event is already being dispatched.");
734 return false;
735 }
736
737 if (!GetExecutionContext())
738 return false;
739
740 event->SetTrusted(false);
741
742 // Return whether the event was cancelled or not to JS not that it
743 // might have actually been default handled; so check only against
744 // CanceledByEventHandler.
745 return DispatchEventInternal(*event) !=
746 DispatchEventResult::kCanceledByEventHandler;
747 }
748
DispatchEvent(Event & event)749 DispatchEventResult EventTarget::DispatchEvent(Event& event) {
750 event.SetTrusted(true);
751 return DispatchEventInternal(event);
752 }
753
DispatchEventInternal(Event & event)754 DispatchEventResult EventTarget::DispatchEventInternal(Event& event) {
755 event.SetTarget(this);
756 event.SetCurrentTarget(this);
757 event.SetEventPhase(Event::kAtTarget);
758 DispatchEventResult dispatch_result = FireEventListeners(event);
759 event.SetEventPhase(0);
760 return dispatch_result;
761 }
762
LegacyType(const Event & event)763 static const AtomicString& LegacyType(const Event& event) {
764 if (event.type() == event_type_names::kTransitionend)
765 return event_type_names::kWebkitTransitionEnd;
766
767 if (event.type() == event_type_names::kAnimationstart)
768 return event_type_names::kWebkitAnimationStart;
769
770 if (event.type() == event_type_names::kAnimationend)
771 return event_type_names::kWebkitAnimationEnd;
772
773 if (event.type() == event_type_names::kAnimationiteration)
774 return event_type_names::kWebkitAnimationIteration;
775
776 if (event.type() == event_type_names::kWheel)
777 return event_type_names::kMousewheel;
778
779 return g_empty_atom;
780 }
781
CountLegacyEvents(const AtomicString & legacy_type_name,EventListenerVector * listeners_vector,EventListenerVector * legacy_listeners_vector)782 void EventTarget::CountLegacyEvents(
783 const AtomicString& legacy_type_name,
784 EventListenerVector* listeners_vector,
785 EventListenerVector* legacy_listeners_vector) {
786 WebFeature unprefixed_feature;
787 WebFeature prefixed_feature;
788 WebFeature prefixed_and_unprefixed_feature;
789 if (legacy_type_name == event_type_names::kWebkitTransitionEnd) {
790 prefixed_feature = WebFeature::kPrefixedTransitionEndEvent;
791 unprefixed_feature = WebFeature::kUnprefixedTransitionEndEvent;
792 prefixed_and_unprefixed_feature =
793 WebFeature::kPrefixedAndUnprefixedTransitionEndEvent;
794 } else if (legacy_type_name == event_type_names::kWebkitAnimationEnd) {
795 prefixed_feature = WebFeature::kPrefixedAnimationEndEvent;
796 unprefixed_feature = WebFeature::kUnprefixedAnimationEndEvent;
797 prefixed_and_unprefixed_feature =
798 WebFeature::kPrefixedAndUnprefixedAnimationEndEvent;
799 } else if (legacy_type_name == event_type_names::kWebkitAnimationStart) {
800 prefixed_feature = WebFeature::kPrefixedAnimationStartEvent;
801 unprefixed_feature = WebFeature::kUnprefixedAnimationStartEvent;
802 prefixed_and_unprefixed_feature =
803 WebFeature::kPrefixedAndUnprefixedAnimationStartEvent;
804 } else if (legacy_type_name == event_type_names::kWebkitAnimationIteration) {
805 prefixed_feature = WebFeature::kPrefixedAnimationIterationEvent;
806 unprefixed_feature = WebFeature::kUnprefixedAnimationIterationEvent;
807 prefixed_and_unprefixed_feature =
808 WebFeature::kPrefixedAndUnprefixedAnimationIterationEvent;
809 } else if (legacy_type_name == event_type_names::kMousewheel) {
810 prefixed_feature = WebFeature::kMouseWheelEvent;
811 unprefixed_feature = WebFeature::kWheelEvent;
812 prefixed_and_unprefixed_feature = WebFeature::kMouseWheelAndWheelEvent;
813 } else {
814 return;
815 }
816
817 if (const LocalDOMWindow* executing_window = ExecutingWindow()) {
818 if (Document* document = executing_window->document()) {
819 if (legacy_listeners_vector) {
820 if (listeners_vector)
821 UseCounter::Count(*document, prefixed_and_unprefixed_feature);
822 else
823 UseCounter::Count(*document, prefixed_feature);
824 } else if (listeners_vector) {
825 UseCounter::Count(*document, unprefixed_feature);
826 }
827 }
828 }
829 }
830
FireEventListeners(Event & event)831 DispatchEventResult EventTarget::FireEventListeners(Event& event) {
832 #if DCHECK_IS_ON()
833 DCHECK(!EventDispatchForbiddenScope::IsEventDispatchForbidden());
834 #endif
835 DCHECK(event.WasInitialized());
836
837 EventTargetData* d = GetEventTargetData();
838 if (!d)
839 return DispatchEventResult::kNotCanceled;
840
841 EventListenerVector* legacy_listeners_vector = nullptr;
842 AtomicString legacy_type_name = LegacyType(event);
843 if (!legacy_type_name.IsEmpty())
844 legacy_listeners_vector = d->event_listener_map.Find(legacy_type_name);
845
846 EventListenerVector* listeners_vector =
847 d->event_listener_map.Find(event.type());
848
849 bool fired_event_listeners = false;
850 if (listeners_vector) {
851 fired_event_listeners = FireEventListeners(event, d, *listeners_vector);
852 } else if (event.isTrusted() && legacy_listeners_vector) {
853 AtomicString unprefixed_type_name = event.type();
854 event.SetType(legacy_type_name);
855 fired_event_listeners =
856 FireEventListeners(event, d, *legacy_listeners_vector);
857 event.SetType(unprefixed_type_name);
858 }
859
860 // Only invoke the callback if event listeners were fired for this phase.
861 if (fired_event_listeners) {
862 event.DoneDispatchingEventAtCurrentTarget();
863
864 // Only count uma metrics if we really fired an event listener.
865 Editor::CountEvent(GetExecutionContext(), event);
866 CountLegacyEvents(legacy_type_name, listeners_vector,
867 legacy_listeners_vector);
868 }
869 return GetDispatchEventResult(event);
870 }
871
FireEventListeners(Event & event,EventTargetData * d,EventListenerVector & entry)872 bool EventTarget::FireEventListeners(Event& event,
873 EventTargetData* d,
874 EventListenerVector& entry) {
875 // Fire all listeners registered for this event. Don't fire listeners removed
876 // during event dispatch. Also, don't fire event listeners added during event
877 // dispatch. Conveniently, all new event listeners will be added after or at
878 // index |size|, so iterating up to (but not including) |size| naturally
879 // excludes new event listeners.
880
881 ExecutionContext* context = GetExecutionContext();
882 if (!context)
883 return false;
884
885 CountFiringEventListeners(event, ExecutingWindow());
886
887 wtf_size_t i = 0;
888 wtf_size_t size = entry.size();
889 if (!d->firing_event_iterators)
890 d->firing_event_iterators = std::make_unique<FiringEventIteratorVector>();
891 d->firing_event_iterators->push_back(
892 FiringEventIterator(event.type(), i, size));
893
894 base::TimeDelta blocked_event_threshold =
895 BlockedEventsWarningThreshold(context, event);
896 base::TimeTicks now;
897 bool should_report_blocked_event = false;
898 if (!blocked_event_threshold.is_zero()) {
899 now = base::TimeTicks::Now();
900 should_report_blocked_event =
901 now - event.PlatformTimeStamp() > blocked_event_threshold;
902 }
903 bool fired_listener = false;
904
905 while (i < size) {
906 // If stopImmediatePropagation has been called, we just break out
907 // immediately, without handling any more events on this target.
908 if (event.ImmediatePropagationStopped())
909 break;
910
911 RegisteredEventListener registered_listener = entry[i];
912
913 // Move the iterator past this event listener. This must match
914 // the handling of the FiringEventIterator::iterator in
915 // EventTarget::removeEventListener.
916 ++i;
917
918 if (!registered_listener.ShouldFire(event))
919 continue;
920
921 EventListener* listener = registered_listener.Callback();
922 // The listener will be retained by Member<EventListener> in the
923 // registeredListener, i and size are updated with the firing event iterator
924 // in case the listener is removed from the listener vector below.
925 if (registered_listener.Once())
926 removeEventListener(event.type(), listener,
927 registered_listener.Capture());
928
929 event.SetHandlingPassive(EventPassiveMode(registered_listener));
930
931 probe::UserCallback probe(context, nullptr, event.type(), false, this);
932 probe::AsyncTask async_task(context, listener->async_task_id(), "event",
933 IsInstrumentedForAsyncStack(event.type()));
934
935 // To match Mozilla, the AT_TARGET phase fires both capturing and bubbling
936 // event listeners, even though that violates some versions of the DOM spec.
937 listener->Invoke(context, &event);
938 fired_listener = true;
939
940 // If we're about to report this event listener as blocking, make sure it
941 // wasn't removed while handling the event.
942 if (should_report_blocked_event && i > 0 &&
943 entry[i - 1].Callback() == listener && !entry[i - 1].Passive() &&
944 !entry[i - 1].BlockedEventWarningEmitted() &&
945 !event.defaultPrevented()) {
946 ReportBlockedEvent(*this, event, &entry[i - 1],
947 now - event.PlatformTimeStamp());
948 }
949
950 event.SetHandlingPassive(Event::PassiveMode::kNotPassive);
951
952 CHECK_LE(i, size);
953 }
954 d->firing_event_iterators->pop_back();
955 return fired_listener;
956 }
957
GetDispatchEventResult(const Event & event)958 DispatchEventResult EventTarget::GetDispatchEventResult(const Event& event) {
959 if (event.defaultPrevented())
960 return DispatchEventResult::kCanceledByEventHandler;
961 if (event.DefaultHandled())
962 return DispatchEventResult::kCanceledByDefaultEventHandler;
963 return DispatchEventResult::kNotCanceled;
964 }
965
GetEventListeners(const AtomicString & event_type)966 EventListenerVector* EventTarget::GetEventListeners(
967 const AtomicString& event_type) {
968 EventTargetData* data = GetEventTargetData();
969 if (!data)
970 return nullptr;
971 return data->event_listener_map.Find(event_type);
972 }
973
NumberOfEventListeners(const AtomicString & event_type) const974 int EventTarget::NumberOfEventListeners(const AtomicString& event_type) const {
975 EventListenerVector* listeners =
976 const_cast<EventTarget*>(this)->GetEventListeners(event_type);
977 return listeners ? listeners->size() : 0;
978 }
979
EventTypes()980 Vector<AtomicString> EventTarget::EventTypes() {
981 EventTargetData* d = GetEventTargetData();
982 return d ? d->event_listener_map.EventTypes() : Vector<AtomicString>();
983 }
984
RemoveAllEventListeners()985 void EventTarget::RemoveAllEventListeners() {
986 EventTargetData* d = GetEventTargetData();
987 if (!d)
988 return;
989 d->event_listener_map.Clear();
990
991 // Notify firing events planning to invoke the listener at 'index' that
992 // they have one less listener to invoke.
993 if (d->firing_event_iterators) {
994 for (const auto& iterator : *d->firing_event_iterators) {
995 iterator.iterator = 0;
996 iterator.end = 0;
997 }
998 }
999 }
1000
EnqueueEvent(Event & event,TaskType task_type)1001 void EventTarget::EnqueueEvent(Event& event, TaskType task_type) {
1002 ExecutionContext* context = GetExecutionContext();
1003 if (!context)
1004 return;
1005 probe::AsyncTaskScheduled(context, event.type(), event.async_task_id());
1006 context->GetTaskRunner(task_type)->PostTask(
1007 FROM_HERE,
1008 WTF::Bind(&EventTarget::DispatchEnqueuedEvent, WrapPersistent(this),
1009 WrapPersistent(&event), WrapPersistent(context)));
1010 }
1011
DispatchEnqueuedEvent(Event * event,ExecutionContext * context)1012 void EventTarget::DispatchEnqueuedEvent(Event* event,
1013 ExecutionContext* context) {
1014 if (!GetExecutionContext()) {
1015 probe::AsyncTaskCanceled(context, event->async_task_id());
1016 return;
1017 }
1018 probe::AsyncTask async_task(context, event->async_task_id());
1019 DispatchEvent(*event);
1020 }
1021
1022 STATIC_ASSERT_ENUM(WebSettings::PassiveEventListenerDefault::kFalse,
1023 PassiveListenerDefault::kFalse);
1024 STATIC_ASSERT_ENUM(WebSettings::PassiveEventListenerDefault::kTrue,
1025 PassiveListenerDefault::kTrue);
1026 STATIC_ASSERT_ENUM(WebSettings::PassiveEventListenerDefault::kForceAllTrue,
1027 PassiveListenerDefault::kForceAllTrue);
1028
1029 } // namespace blink
1030