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, &registered_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                                     &registered_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