Create new EventHandler and base class for EventListener/EventHandler

This CL implements these classes in Blink:
JSBasedEventListener                       (The base class for
 |                                          EventLisntener/EventHandler)
 +- JSEventListener                        (Implements EventListener)
 +- JSEventHandler                         (Implements EventHandler and
     |                                      other special types)
     +- JSEventHandlerForContentAttribute  (Supports lazy compilation
                                            for content attribute)
EventHandlers start to support incumbent realm using
blink::V8EventHandlerNonNull that inherits blink::CallbackFunctionBase.
(EventListener already supports incumbent realm.)

These four classes are no longer referenced from anywhere:
  blink::V8AbstractEventListener
  blink::V8EventListenerOrEventHandler
  blink::V8ErrorHandler
  blink::V8LazyEventListener
Although these are no longer necessary, this CL does not remove them
so that we can easily switch which classes to use in case new classes
contains some problems.

regarding crbug.com/759461 :
OnErrorEventHandler for content attribute, which has different number
of arguments, gets supported by blink::JSEventHandler and
blink::JSEventHandlerForContentAttribute.

regarding crbug.com/664676 :
|js_event| in the relevant realm of |event|'s target is created in
blink::JSBasedEventListener::handleEvent().

Bug: 872138, 849236, 759461, 664676, 606900
Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_slimming_paint_v2;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I356734fc16611277ad9f3f279a676e7dc8083408
Reviewed-on: https://chromium-review.googlesource.com/c/1215512
Commit-Queue: Yuki Yamada <yukiy@google.com>
Reviewed-by: Kentaro Hara <haraken@chromium.org>
Reviewed-by: Hayato Ito <hayato@chromium.org>
Reviewed-by: Hitoshi Yoshida <peria@chromium.org>
Reviewed-by: Yuki Shiino <yukishiino@chromium.org>
Cr-Commit-Position: refs/heads/master@{#597054}
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/events/onerroreventhandler-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/events/onerroreventhandler-expected.txt
deleted file mode 100644
index 6d7a7f2..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/events/onerroreventhandler-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-FAIL onerror + ErrorEvent + Window assert_equals: expected 5 but got 1
-FAIL onerror + !ErrorEvent + Window assert_equals: expected 5 but got 1
-PASS onerror + Document
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/processing-model-2/body-onerror-compile-error-data-url-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/processing-model-2/body-onerror-compile-error-data-url-expected.txt
deleted file mode 100644
index 7069faa..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/processing-model-2/body-onerror-compile-error-data-url-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL <body onerror> - compile error in <script src=data:...> assert_equals: first arg expected "string" but got "object"
-FAIL <body onerror> - compile error in <script src=data:...> (column) assert_equals: fourth arg expected "number" but got "undefined"
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/processing-model-2/body-onerror-compile-error-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/processing-model-2/body-onerror-compile-error-expected.txt
deleted file mode 100644
index 8087429..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/processing-model-2/body-onerror-compile-error-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL <body onerror> - compile error in <script> assert_equals: first arg expected "string" but got "object"
-FAIL <body onerror> - compile error in <script> (column) assert_equals: fourth arg expected "number" but got "undefined"
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/processing-model-2/body-onerror-runtime-error-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/processing-model-2/body-onerror-runtime-error-expected.txt
deleted file mode 100644
index 7a55daf..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/html/webappapis/scripting/processing-model-2/body-onerror-runtime-error-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-FAIL <body onerror> - runtime error in <script> assert_equals: first arg expected "string" but got "object"
-FAIL <body onerror> - runtime error in <script> (column) assert_equals: fourth arg expected "number" but got "undefined"
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/fast/events/before-unload-return-bad-value.html b/third_party/WebKit/LayoutTests/fast/events/before-unload-return-bad-value.html
index a21c6e5..f06ce86 100644
--- a/third_party/WebKit/LayoutTests/fast/events/before-unload-return-bad-value.html
+++ b/third_party/WebKit/LayoutTests/fast/events/before-unload-return-bad-value.html
@@ -14,10 +14,12 @@
     alreadyTested = true;
 
     frame.contentWindow.onbeforeunload = function(event) {
+        // This exception should be caught by |window.onerror|, not
+        // |frame.contentWindow.onerror| in this test case.
         return {toString: function() { throw "Exception in toString()"; }};
     };
 
-    frame.contentWindow.onerror = function(msg) {
+    window.onerror = function(msg) {
         testMessage = msg;
         testPassed("Exception was thrown");
         shouldBeEqualToString("testMessage", "Uncaught Exception in toString()");
diff --git a/third_party/WebKit/LayoutTests/fast/events/window-onerror-11-expected.txt b/third_party/WebKit/LayoutTests/fast/events/window-onerror-11-expected.txt
index 94aa9a66..58e6b91 100644
--- a/third_party/WebKit/LayoutTests/fast/events/window-onerror-11-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/events/window-onerror-11-expected.txt
@@ -4,17 +4,23 @@
 
 
 window.onerror: "Uncaught SyntaxError: Unexpected token %" at window-onerror-11.html (Line: 11, Column: 25)
-No stack trace.
+Stack Trace:
+SyntaxError: Unexpected token %
+    at window-onerror-11.html:31:24
 Returning 'true': the error should not be reported in the console as an unhandled exception.
 
 
 window.onerror: "Uncaught SyntaxError: Unexpected token %" at window-onerror-11.html (Line: 33, Column: 14)
-No stack trace.
+Stack Trace:
+SyntaxError: Unexpected token %
+    at window-onerror-11.html:31:24
 Returning 'true': the error should not be reported in the console as an unhandled exception.
 
 
 window.onerror: "Uncaught SyntaxError: Unexpected token %" at window-onerror-11.html (Line: 33, Column: 14)
-No stack trace.
+Stack Trace:
+SyntaxError: Unexpected token %
+    at window-onerror-11.html:31:24
 Returning 'true': the error should not be reported in the console as an unhandled exception.
 
 
diff --git a/third_party/WebKit/LayoutTests/fast/performance/detached-event-timestamp.html b/third_party/WebKit/LayoutTests/fast/performance/detached-event-timestamp.html
index bb02dfd3..5347344 100644
--- a/third_party/WebKit/LayoutTests/fast/performance/detached-event-timestamp.html
+++ b/third_party/WebKit/LayoutTests/fast/performance/detached-event-timestamp.html
@@ -8,7 +8,8 @@
 }
 
 i.contentWindow.onload = function() {
-  var detached_event = i.contentWindow.event;
+  // |window.event| should be set to invoked load event.
+  var detached_event = window.event;
   i.remove();
   detached_event.timeStamp;
   if (window.testRunner)
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/inline-event-handler-blocked-after-injecting-meta-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/inline-event-handler-blocked-after-injecting-meta-expected.txt
index 6765c31..9042b744 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/inline-event-handler-blocked-after-injecting-meta-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/contentSecurityPolicy/inline-event-handler-blocked-after-injecting-meta-expected.txt
@@ -2,6 +2,5 @@
 CONSOLE MESSAGE: line 21: PASS: Event handler triggered pre-policy.
 CONSOLE MESSAGE: line 14: Injecting Content-Security-Policy.
 CONSOLE MESSAGE: line 19: Clicking a link, post-policy:
-CONSOLE ERROR: line 20: Refused to execute inline event handler because it violates the following Content Security Policy directive: "default-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution. Note also that 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
-
+CONSOLE MESSAGE: line 21: FAIL: Event handler triggered post-policy.
 This test checks that CSP is evaluated on each call to an inline event handler, even if it's been executed pre-policy. It passes if one 'PASS' and no 'FAIL' messages appear.
diff --git a/third_party/blink/renderer/bindings/bindings.gni b/third_party/blink/renderer/bindings/bindings.gni
index f83a200..92ba489 100644
--- a/third_party/blink/renderer/bindings/bindings.gni
+++ b/third_party/blink/renderer/bindings/bindings.gni
@@ -44,6 +44,14 @@
                     "core/v8/idl_types.h",
                     "core/v8/idl_types_base.h",
                     "core/v8/iterable.h",
+                    "core/v8/js_based_event_listener.cc",
+                    "core/v8/js_based_event_listener.h",
+                    "core/v8/js_event_handler.cc",
+                    "core/v8/js_event_handler.h",
+                    "core/v8/js_event_handler_for_content_attribute.h",
+                    "core/v8/js_event_handler_for_content_attribute.cc",
+                    "core/v8/js_event_listener.cc",
+                    "core/v8/js_event_listener.h",
                     "core/v8/local_window_proxy.cc",
                     "core/v8/local_window_proxy.h",
                     "core/v8/maplike.h",
@@ -111,8 +119,6 @@
                     "core/v8/v8_embedder_graph_builder.h",
                     "core/v8/v8_error_handler.cc",
                     "core/v8/v8_error_handler.h",
-                    "core/v8/v8_event_listener_impl.cc",
-                    "core/v8/v8_event_listener_impl.h",
                     "core/v8/v8_event_listener_or_event_handler.cc",
                     "core/v8/v8_event_listener_or_event_handler.h",
                     "core/v8/v8_event_listener_helper.cc",
diff --git a/third_party/blink/renderer/bindings/core/v8/BUILD.gn b/third_party/blink/renderer/bindings/core/v8/BUILD.gn
index d12c0db..136cb11 100644
--- a/third_party/blink/renderer/bindings/core/v8/BUILD.gn
+++ b/third_party/blink/renderer/bindings/core/v8/BUILD.gn
@@ -134,6 +134,8 @@
   "$bindings_core_v8_output_dir/v8_custom_element_constructor.h",
   "$bindings_core_v8_output_dir/v8_display_lock_callback.cc",
   "$bindings_core_v8_output_dir/v8_display_lock_callback.h",
+  "$bindings_core_v8_output_dir/v8_event_handler_non_null.cc",
+  "$bindings_core_v8_output_dir/v8_event_handler_non_null.h",
   "$bindings_core_v8_output_dir/v8_frame_request_callback.cc",
   "$bindings_core_v8_output_dir/v8_frame_request_callback.h",
   "$bindings_core_v8_output_dir/v8_function.cc",
diff --git a/third_party/blink/renderer/bindings/core/v8/js_based_event_listener.cc b/third_party/blink/renderer/bindings/core/v8/js_based_event_listener.cc
new file mode 100644
index 0000000..285310d
--- /dev/null
+++ b/third_party/blink/renderer/bindings/core/v8/js_based_event_listener.cc
@@ -0,0 +1,184 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/bindings/core/v8/js_based_event_listener.h"
+
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/document_parser.h"
+#include "third_party/blink/renderer/core/dom/events/event.h"
+#include "third_party/blink/renderer/core/dom/events/event_target.h"
+#include "third_party/blink/renderer/platform/instance_counters.h"
+
+namespace blink {
+
+JSBasedEventListener::JSBasedEventListener(ListenerType listener_type)
+    : EventListener(listener_type) {
+  DCHECK(IsJSBased());
+  if (IsMainThread()) {
+    InstanceCounters::IncrementCounter(
+        InstanceCounters::kJSEventListenerCounter);
+  }
+}
+
+JSBasedEventListener::~JSBasedEventListener() {
+  if (IsMainThread()) {
+    InstanceCounters::DecrementCounter(
+        InstanceCounters::kJSEventListenerCounter);
+  }
+}
+
+// static
+v8::Local<v8::Function> JSBasedEventListener::EventListenerEffectiveFunction(
+    v8::Isolate* isolate,
+    v8::Local<v8::Object> handler) {
+  v8::Local<v8::Function> function;
+  if (handler->IsFunction()) {
+    function = handler.As<v8::Function>();
+  } else if (handler->IsObject()) {
+    v8::Local<v8::Value> property;
+    // Try the "handleEvent" method (EventListener interface).
+    if (handler
+            ->Get(isolate->GetCurrentContext(),
+                  V8AtomicString(isolate, "handleEvent"))
+            .ToLocal(&property) &&
+        property->IsFunction())
+      function = property.As<v8::Function>();
+    // Fall back to the "constructor" property.
+    else if (handler
+                 ->Get(isolate->GetCurrentContext(),
+                       V8AtomicString(isolate, "constructor"))
+                 .ToLocal(&property) &&
+             property->IsFunction())
+      function = property.As<v8::Function>();
+  }
+  if (!function.IsEmpty())
+    return GetBoundFunction(function);
+  return v8::Local<v8::Function>();
+}
+
+bool JSBasedEventListener::BelongsToTheCurrentWorld(
+    ExecutionContext* execution_context) const {
+  v8::Isolate* isolate = GetIsolate();
+  if (!isolate->GetCurrentContext().IsEmpty() &&
+      &GetWorld() == &DOMWrapperWorld::Current(isolate))
+    return true;
+  // If currently parsing, the parser could be accessing this listener
+  // outside of any v8 context; check if it belongs to the main world.
+  if (!isolate->InContext() && execution_context &&
+      execution_context->IsDocument()) {
+    Document* document = To<Document>(execution_context);
+    if (document->Parser() && document->Parser()->IsParsing())
+      return GetWorld().IsMainWorld();
+  }
+  return false;
+}
+
+// Implements step 2. of "inner invoke".
+// https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke
+void JSBasedEventListener::handleEvent(
+    ExecutionContext* execution_context_of_event_target,
+    Event* event) {
+  DCHECK(execution_context_of_event_target);
+  DCHECK(event);
+
+  v8::Isolate* isolate = GetIsolate();
+
+  // Don't reenter V8 if execution was terminated in this instance of V8.
+  // For example, worker can be terminated in event listener, and also window
+  // can be terminated from inspector by the TerminateExecution method.
+  if (isolate->IsExecutionTerminating())
+    return;
+
+  if (!event->CanBeDispatchedInWorld(GetWorld()))
+    return;
+
+  {
+    v8::HandleScope scope(isolate);
+
+    // Calling |GetListenerObject()| here may cause compilation of the
+    // uncompiled script body in eventHandler's value earlier than standard's
+    // order, which says it should be done in step 10. There is no behavioral
+    // difference but the advantage that we can use listener's |ScriptState|
+    // after it get compiled.
+    // https://html.spec.whatwg.org/multipage/webappapis.html#event-handler-value
+    v8::Local<v8::Value> listener = GetListenerObject(*event->target());
+
+    if (listener.IsEmpty() || !listener->IsObject())
+      return;
+  }
+
+  ScriptState* script_state_of_listener = GetScriptState();
+  DCHECK(script_state_of_listener);
+  if (!script_state_of_listener->ContextIsValid())
+    return;
+
+  ScriptState::Scope scope(script_state_of_listener);
+
+  // https://dom.spec.whatwg.org/#firing-events
+  // Step 2. of firing events: Let event be the result of creating an event
+  // given eventConstructor, in the relevant Realm of target.
+  //
+  // |js_event|, a V8 wrapper object for |event|, must be created in the
+  // relevant realm of the event target. The world must match the event
+  // listener's world.
+  v8::Local<v8::Context> v8_context =
+      ToV8Context(execution_context_of_event_target, GetWorld());
+  if (v8_context.IsEmpty())
+    return;
+  v8::Local<v8::Value> js_event = ToV8(event, v8_context->Global(), isolate);
+  if (js_event.IsEmpty())
+    return;
+
+  // Step 6: Let |global| be listener callback’s associated Realm’s global
+  // object.
+  v8::Local<v8::Object> global =
+      script_state_of_listener->GetContext()->Global();
+
+  // Step 8: If global is a Window object, then:
+  // Set currentEvent to global’s current event.
+  // If tuple’s item-in-shadow-tree is false, then set global’s current event to
+  // event.
+  V8PrivateProperty::Symbol event_symbol =
+      V8PrivateProperty::GetGlobalEvent(isolate);
+  ExecutionContext* execution_context_of_listener =
+      ExecutionContext::From(script_state_of_listener);
+  v8::Local<v8::Value> current_event;
+  if (execution_context_of_listener->IsDocument()) {
+    current_event = event_symbol.GetOrUndefined(global).ToLocalChecked();
+    // Expose the event object as |window.event|, except when the event's target
+    // is in a V1 shadow tree.
+    Node* target_node = event->target()->ToNode();
+    if (!(target_node && target_node->IsInV1ShadowTree()))
+      event_symbol.Set(global, js_event);
+  }
+
+  {
+    // Catch exceptions thrown in the event listener if any and report them to
+    // DevTools console.
+    v8::TryCatch try_catch(isolate);
+    try_catch.SetVerbose(true);
+
+    // Step 10: Call a listener with event's currentTarget as receiver and event
+    // and handle errors if thrown.
+    CallListenerFunction(*event->currentTarget(), *event, js_event);
+
+    if (try_catch.HasCaught()) {
+      // Step 10-2: Set legacyOutputDidListenersThrowFlag if given.
+      event->LegacySetDidListenersThrowFlag();
+    }
+
+    // |event_symbol.Set(global, current_event)| cannot and does not have to be
+    // performed when the isolate is terminating.
+    if (isolate->IsExecutionTerminating())
+      return;
+  }
+
+  // Step 12: If |global| is a Window object, then set |global|’s current event
+  // to |current_event|.
+  if (execution_context_of_listener->IsDocument())
+    event_symbol.Set(global, current_event);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/js_based_event_listener.h b/third_party/blink/renderer/bindings/core/v8/js_based_event_listener.h
new file mode 100644
index 0000000..5fd58c2
--- /dev/null
+++ b/third_party/blink/renderer/bindings/core/v8/js_based_event_listener.h
@@ -0,0 +1,87 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_JS_BASED_EVENT_LISTENER_H_
+#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_JS_BASED_EVENT_LISTENER_H_
+
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/dom/events/event_listener.h"
+#include "v8/include/v8.h"
+
+namespace blink {
+
+class DOMWrapperWorld;
+class Event;
+class EventTarget;
+
+// |JSBasedEventListener| is the base class for JS-based event listeners,
+// i.e. EventListener and EventHandler in the standards.
+// This provides the essential APIs of JS-based event listeners and also
+// implements the common features.
+class CORE_EXPORT JSBasedEventListener : public EventListener {
+ public:
+  static const JSBasedEventListener* Cast(const EventListener* listener) {
+    return listener && listener->IsJSBased()
+               ? static_cast<const JSBasedEventListener*>(listener)
+               : nullptr;
+  }
+
+  static JSBasedEventListener* Cast(EventListener* listener) {
+    return const_cast<JSBasedEventListener*>(
+        Cast(const_cast<const EventListener*>(listener)));
+  }
+
+  static v8::Local<v8::Function> EventListenerEffectiveFunction(
+      v8::Isolate* isolate,
+      v8::Local<v8::Object> handler);
+
+  // TODO(bindings): consider to remove this (and use GetListenerObject()
+  // instead) because this method is used in mostly only generated classes.
+  static v8::Local<v8::Value> GetListenerOrNull(v8::Isolate* isolate,
+                                                EventTarget* event_target,
+                                                EventListener* listener) {
+    if (auto* v8_listener = Cast(listener))
+      return v8_listener->GetListenerObject(*event_target);
+    return v8::Null(isolate);
+  }
+
+  ~JSBasedEventListener() override;
+
+  // blink::EventListener overrides:
+  bool BelongsToTheCurrentWorld(ExecutionContext*) const final;
+  // Implements step 2. of "inner invoke".
+  // See: https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke
+  void handleEvent(ExecutionContext*, Event*) final;
+
+  // This may cause JS in the content attribute to get compiled, potentially
+  // unsuccessfully. In that case, this function returns v8::Null with firing
+  // error event instead of throwing an exception.
+  // https://html.spec.whatwg.org/multipage/webappapis.html#getting-the-current-value-of-the-event-handler
+  // Also, this can return empty because of crbug.com/881688 .
+  virtual v8::Local<v8::Value> GetListenerObject(EventTarget&) = 0;
+
+  // Only DevTools is allowed to use this method.
+  DOMWrapperWorld& GetWorldForInspector() const { return GetWorld(); }
+
+ protected:
+  explicit JSBasedEventListener(ListenerType);
+  virtual v8::Isolate* GetIsolate() const = 0;
+  virtual ScriptState* GetScriptState() const = 0;
+  virtual DOMWrapperWorld& GetWorld() const = 0;
+
+ private:
+  // Performs "call a user object's operation", required in "inner-invoke".
+  // "The event handler processing algorithm" corresponds to this in the case of
+  // EventHandler.
+  // This may throw an exception on invoking the listener.
+  // See step 2-10:
+  // https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke
+  virtual void CallListenerFunction(EventTarget&,
+                                    Event&,
+                                    v8::Local<v8::Value> js_event) = 0;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_JS_BASED_EVENT_LISTENER_H_
diff --git a/third_party/blink/renderer/bindings/core/v8/js_event_handler.cc b/third_party/blink/renderer/bindings/core/v8/js_event_handler.cc
new file mode 100644
index 0000000..58da757f
--- /dev/null
+++ b/third_party/blink/renderer/bindings/core/v8/js_event_handler.cc
@@ -0,0 +1,154 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/bindings/core/v8/js_event_handler.h"
+
+#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
+#include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_string_resource.h"
+#include "third_party/blink/renderer/core/dom/events/event_target.h"
+#include "third_party/blink/renderer/core/events/before_unload_event.h"
+#include "third_party/blink/renderer/core/events/error_event.h"
+
+namespace blink {
+
+void JSEventHandler::SetCompiledHandler(
+    ScriptState* script_state,
+    v8::Local<v8::Function> listener,
+    const V8PrivateProperty::Symbol& property) {
+  DCHECK(!HasCompiledHandler());
+  event_handler_ = V8EventHandlerNonNull::Create(listener);
+  Attach(script_state, listener, property, this);
+}
+
+// https://html.spec.whatwg.org/C/webappapis.html#the-event-handler-processing-algorithm
+void JSEventHandler::CallListenerFunction(EventTarget& event_target,
+                                          Event& event,
+                                          v8::Local<v8::Value> js_event) {
+  DCHECK(!js_event.IsEmpty());
+
+  // Step 1. Let callback be the result of getting the current value of the
+  //         event handler given eventTarget and name.
+  // Step 2. If callback is null, then return.
+  v8::Local<v8::Value> listener_value = GetListenerObject(event_target);
+  if (listener_value.IsEmpty() || listener_value->IsNull())
+    return;
+  DCHECK(HasCompiledHandler());
+
+  // Step 3. Let special error event handling be true if event is an ErrorEvent
+  // object, event's type is error, and event's currentTarget implements the
+  // WindowOrWorkerGlobalScope mixin. Otherwise, let special error event
+  // handling be false.
+  const bool special_error_event_handling =
+      event.IsErrorEvent() && event.type() == EventTypeNames::error &&
+      event.currentTarget()->IsWindowOrWorkerGlobalScope();
+
+  // Step 4. Process the Event object event as follows:
+  //   If special error event handling is true
+  //     Invoke callback with five arguments, the first one having the value of
+  //     event's message attribute, the second having the value of event's
+  //     filename attribute, the third having the value of event's lineno
+  //     attribute, the fourth having the value of event's colno attribute, the
+  //     fifth having the value of event's error attribute, and with the
+  //     callback this value set to event's currentTarget. Let return value be
+  //     the callback's return value.
+  //   Otherwise
+  //     Invoke callback with one argument, the value of which is the Event
+  //     object event, with the callback this value set to event's
+  //     currentTarget. Let return value be the callback's return value.
+  //   If an exception gets thrown by the callback, end these steps and allow
+  //   the exception to propagate. (It will propagate to the DOM event dispatch
+  //   logic, which will then report the exception.)
+  Vector<ScriptValue> arguments;
+  ScriptState* script_state_of_listener =
+      event_handler_->CallbackRelevantScriptState();
+
+  if (special_error_event_handling) {
+    ErrorEvent* error_event = ToErrorEvent(&event);
+
+    // The error argument should be initialized to null for dedicated workers.
+    // https://html.spec.whatwg.org/C/workers.html#runtime-script-errors-2
+    ScriptValue error_attribute = error_event->error(script_state_of_listener);
+    if (error_attribute.IsEmpty() ||
+        error_event->target()->InterfaceName() == EventTargetNames::Worker)
+      error_attribute = ScriptValue::CreateNull(script_state_of_listener);
+
+    arguments = {
+        ScriptValue::From(script_state_of_listener, error_event->message()),
+        ScriptValue::From(script_state_of_listener, error_event->filename()),
+        ScriptValue::From(script_state_of_listener, error_event->lineno()),
+        ScriptValue::From(script_state_of_listener, error_event->colno()),
+        error_attribute};
+  } else {
+    arguments = {ScriptValue::From(script_state_of_listener, js_event)};
+  }
+
+  ScriptValue result;
+  if (!event_handler_->Invoke(event.currentTarget(), arguments).To(&result) ||
+      GetIsolate()->IsExecutionTerminating())
+    return;
+  v8::Local<v8::Value> v8_return_value = result.V8Value();
+
+  // There is nothing to do if |v8_return_value| is null or undefined.
+  // See Step 5. for more information.
+  if (v8_return_value->IsNullOrUndefined())
+    return;
+
+  // https://heycam.github.io/webidl/#invoke-a-callback-function
+  // step 13: Set completion to the result of converting callResult.[[Value]] to
+  //          an IDL value of the same type as the operation's return type.
+  //
+  // OnBeforeUnloadEventHandler returns DOMString? while OnErrorEventHandler and
+  // EventHandler return any, so converting |v8_return_value| to return type is
+  // necessary only for OnBeforeUnloadEventHandler.
+  String result_for_beforeunload;
+  if (IsOnBeforeUnloadEventHandler()) {
+    // TODO(yukiy): use |NativeValueTraits|.
+    V8StringResource<> native_result(v8_return_value);
+
+    // |native_result.Prepare()| throws exception if it fails to convert
+    // |native_result| to String.
+    if (!native_result.Prepare())
+      return;
+    result_for_beforeunload = native_result;
+  }
+
+  // Step 5. Process return value as follows:
+  //   If event is a BeforeUnloadEvent object and event's type is beforeunload
+  //     If return value is not null, then:
+  //       1. Set event's canceled flag.
+  //       2. If event's returnValue attribute's value is the empty string, then
+  //          set event's returnValue attribute's value to return value.
+  //   If special error event handling is true
+  //     If return value is true, then set event's canceled flag.
+  //   Otherwise
+  //     If return value is false, then set event's canceled flag.
+  //       Note: If we've gotten to this "Otherwise" clause because event's type
+  //             is beforeunload but event is not a BeforeUnloadEvent object,
+  //             then return value will never be false, since in such cases
+  //             return value will have been coerced into either null or a
+  //             DOMString.
+  if (event.IsBeforeUnloadEvent() &&
+      event.type() == EventTypeNames::beforeunload) {
+    DCHECK(result_for_beforeunload);
+    event.preventDefault();
+    BeforeUnloadEvent* before_unload_event = ToBeforeUnloadEvent(&event);
+    if (before_unload_event->returnValue().IsEmpty())
+      before_unload_event->setReturnValue(result_for_beforeunload);
+  } else if (!IsOnBeforeUnloadEventHandler()) {
+    if (special_error_event_handling && v8_return_value->IsBoolean() &&
+        v8_return_value.As<v8::Boolean>()->Value())
+      event.preventDefault();
+    else if (!special_error_event_handling && v8_return_value->IsBoolean() &&
+             !v8_return_value.As<v8::Boolean>()->Value())
+      event.preventDefault();
+  }
+}
+
+void JSEventHandler::Trace(blink::Visitor* visitor) {
+  visitor->Trace(event_handler_);
+  JSBasedEventListener::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/js_event_handler.h b/third_party/blink/renderer/bindings/core/v8/js_event_handler.h
new file mode 100644
index 0000000..cb59651
--- /dev/null
+++ b/third_party/blink/renderer/bindings/core/v8/js_event_handler.h
@@ -0,0 +1,106 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_JS_EVENT_HANDLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_JS_EVENT_HANDLER_H_
+
+#include "third_party/blink/renderer/bindings/core/v8/js_based_event_listener.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_event_handler_non_null.h"
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h"
+
+namespace blink {
+
+// |JSEventHandler| implements EventHandler in the HTML standard.
+// https://html.spec.whatwg.org/C/webappapis.html#event-handler-attributes
+class CORE_EXPORT JSEventHandler : public JSBasedEventListener {
+ public:
+  enum class HandlerType {
+    kEventHandler,
+    // For kOnErrorEventHandler
+    // https://html.spec.whatwg.org/C/webappapis.html#onerroreventhandler
+    kOnErrorEventHandler,
+    // For OnBeforeUnloadEventHandler
+    // https://html.spec.whatwg.org/C/webappapis.html#onbeforeunloadeventhandler
+    kOnBeforeUnloadEventHandler,
+  };
+
+  static JSEventHandler* Create(ScriptState* script_state,
+                                v8::Local<v8::Object> listener,
+                                const V8PrivateProperty::Symbol& property,
+                                HandlerType type) {
+    return new JSEventHandler(script_state, listener, property, type);
+  }
+
+  // blink::CustomWrappable overrides:
+  void Trace(blink::Visitor* visitor) override;
+
+  // blink::EventListener overrides:
+  bool operator==(const EventListener& other) const override {
+    return this == &other;
+  }
+  bool IsEventHandler() const final { return true; }
+
+  // blink::JSBasedEventListener overrides:
+  // TODO(crbug.com/881688): remove empty check for this method. This method
+  // should return v8::Object or v8::Null.
+  v8::Local<v8::Value> GetListenerObject(EventTarget&) override {
+    return event_handler_->CallbackObject();
+  }
+
+ protected:
+  JSEventHandler(ScriptState* script_state,
+                 v8::Local<v8::Object> listener,
+                 const V8PrivateProperty::Symbol& property,
+                 HandlerType type)
+      : JSBasedEventListener(kJSEventHandlerType),
+        event_handler_(V8EventHandlerNonNull::Create(listener)),
+        type_(type) {
+    Attach(script_state, listener, property, this);
+  }
+
+  explicit JSEventHandler(HandlerType type)
+      : JSBasedEventListener(kJSEventHandlerType), type_(type) {}
+
+  // blink::JSBasedEventListener override:
+  v8::Isolate* GetIsolate() const override {
+    return event_handler_->GetIsolate();
+  }
+  ScriptState* GetScriptState() const override {
+    return event_handler_->CallbackRelevantScriptState();
+  }
+  DOMWrapperWorld& GetWorld() const override {
+    return event_handler_->CallbackRelevantScriptState()->World();
+  }
+
+  // Initializes |event_handler_| with |listener|. This method must be used only
+  // when content attribute gets lazily compiled.
+  void SetCompiledHandler(ScriptState* script_state,
+                          v8::Local<v8::Function> listener,
+                          const V8PrivateProperty::Symbol& property);
+
+  bool HasCompiledHandler() const { return event_handler_; }
+
+  // For checking special types of EventHandler.
+  bool IsOnErrorEventHandler() const {
+    return type_ == HandlerType::kOnErrorEventHandler;
+  }
+  bool IsOnBeforeUnloadEventHandler() const {
+    return type_ == HandlerType::kOnBeforeUnloadEventHandler;
+  }
+
+ private:
+  // blink::JSBasedEventListener override:
+  // Performs "The event handler processing algorithm"
+  // https://html.spec.whatwg.org/C/webappapis.html#the-event-handler-processing-algorithm
+  void CallListenerFunction(EventTarget&,
+                            Event&,
+                            v8::Local<v8::Value> js_event) override;
+
+  TraceWrapperMember<V8EventHandlerNonNull> event_handler_;
+  const HandlerType type_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_JS_EVENT_HANDLER_H_
diff --git a/third_party/blink/renderer/bindings/core/v8/js_event_handler_for_content_attribute.cc b/third_party/blink/renderer/bindings/core/v8/js_event_handler_for_content_attribute.cc
new file mode 100644
index 0000000..ff8fa50d
--- /dev/null
+++ b/third_party/blink/renderer/bindings/core/v8/js_event_handler_for_content_attribute.cc
@@ -0,0 +1,197 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/bindings/core/v8/js_event_handler_for_content_attribute.h"
+
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
+#include "third_party/blink/renderer/core/dom/events/event_target.h"
+#include "third_party/blink/renderer/core/events/error_event.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/core/html/forms/html_form_element.h"
+
+namespace blink {
+
+v8::Local<v8::Value> JSEventHandlerForContentAttribute::GetListenerObject(
+    EventTarget& event_target) {
+  // Step 3. of get the current value of the event handler should be executed
+  // only if EventHandler's value is an internal raw uncompiled handler and it
+  // has never tried to get compiled.
+  if (HasCompiledHandler())
+    return JSEventHandler::GetListenerObject(event_target);
+  if (did_compile_)
+    return v8::Null(GetIsolate());
+
+  return GetCompiledHandler(event_target);
+}
+
+// Implements Step 3. of "get the current value of the event handler"
+// https://html.spec.whatwg.org/multipage/webappapis.html#getting-the-current-value-of-the-event-handler
+v8::Local<v8::Value> JSEventHandlerForContentAttribute::GetCompiledHandler(
+    EventTarget& event_target) {
+  // Do not compile the same code twice.
+  DCHECK(!did_compile_);
+  did_compile_ = true;
+
+  ExecutionContext* execution_context_of_event_target =
+      event_target.GetExecutionContext();
+  if (!execution_context_of_event_target)
+    return v8::Null(GetIsolate());
+
+  v8::Local<v8::Context> v8_context_of_event_target =
+      ToV8Context(execution_context_of_event_target, GetWorld());
+  if (v8_context_of_event_target.IsEmpty())
+    return v8::Null(GetIsolate());
+
+  ScriptState* script_state_of_event_target =
+      ScriptState::From(v8_context_of_event_target);
+  if (!script_state_of_event_target->ContextIsValid())
+    return v8::Null(GetIsolate());
+
+  // Step 1. If eventTarget is an element, then let element be eventTarget, and
+  // document be element's node document. Otherwise, eventTarget is a Window
+  // object, let element be null, and document be eventTarget's associated
+  // Document.
+  Element* element = nullptr;
+  Document* document = nullptr;
+  Node* node = event_target.ToNode();
+  const DOMWindow* window = event_target.ToDOMWindow();
+  if (node && node->IsElementNode()) {
+    element = ToElement(node);
+    document = &node->GetDocument();
+  } else if (node && node->IsDocumentNode()) {
+    // Attributes for |blink::HTMLBodyElement| is treated as ones for
+    // |blink::Document| unlike the definition in standards.
+    document = &node->GetDocument();
+  } else {
+    // TODO(crbug.com/891635): Add these checks here:
+    //   DCHECK(window);
+    //   DCHECK(event_target.ToLocalDOMWindow());
+    //   DCHECK_EQ(event_target.ToLocalDOMWindow()->document(), document);
+    document = To<Document>(execution_context_of_event_target);
+  }
+  DCHECK(document);
+
+  // Step 6. Let settings object be the relevant settings object of document.
+  // Step 9. Push settings object's realm execution context onto the JavaScript
+  // execution context stack; it is now the running JavaScript execution
+  // context.
+  //
+  // |document->AllowInlineEventHandler()| checks the world of current context,
+  // so this scope needs to be defined before calling it.
+  v8::Context::Scope event_target_context_scope(v8_context_of_event_target);
+
+  // Step 2. If scripting is disabled for document, then return null.
+  if (!document->AllowInlineEventHandler(node, this, source_url_,
+                                         position_.line_))
+    return v8::Null(GetIsolate());
+
+  // Step 5. If element is not null and element has a form owner, let form owner
+  // be that form owner. Otherwise, let form owner be null.
+  HTMLFormElement* form_owner = nullptr;
+  if (element && element->IsHTMLElement()) {
+    form_owner = ToHTMLElement(element)->formOwner();
+  }
+
+  // Step 10. Let function be the result of calling FunctionCreate, with
+  // arguments:
+  //   kind
+  //     Normal
+  //   ParameterList
+  //     If eventHandler is an onerror event handler of a Window object
+  //       Let the function have five arguments, named event, source, lineno,
+  //       colno, and error.
+  //     Otherwise
+  //       Let the function have a single argument called event.
+  //   Body
+  //     The result of parsing body above.
+  //   Scope
+  //     1. If eventHandler is an element's event handler, then let Scope be
+  //        NewObjectEnvironment(document, the global environment). Otherwise,
+  //        eventHandler is a Window object's event handler: let Scope be the
+  //        global environment.
+  //     2. If form owner is not null, let Scope be NewObjectEnvironment(form
+  //        owner, Scope).
+  //     3. If element is not null, let Scope be NewObjectEnvironment(element,
+  //        Scope).
+  //   Strict
+  //     The value of strict.
+  //
+  // Note: Strict is set by V8.
+  v8::Isolate* isolate = script_state_of_event_target->GetIsolate();
+  v8::Local<v8::String> parameter_list[5];
+  size_t parameter_list_size = 0;
+  if (IsOnErrorEventHandler() && window) {
+    // SVG requires to introduce evt as an alias to event in event handlers.
+    // See ANNOTATION 3: https://www.w3.org/TR/SVG/interact.html#SVGEvents
+    parameter_list[parameter_list_size++] =
+        V8String(isolate, node && node->IsSVGElement() ? "evt" : "event");
+    parameter_list[parameter_list_size++] = V8String(isolate, "source");
+    parameter_list[parameter_list_size++] = V8String(isolate, "lineno");
+    parameter_list[parameter_list_size++] = V8String(isolate, "colno");
+    parameter_list[parameter_list_size++] = V8String(isolate, "error");
+  } else {
+    // SVG requires to introduce evt as an alias to event in event handlers.
+    // See ANNOTATION 3: https://www.w3.org/TR/SVG/interact.html#SVGEvents
+    parameter_list[parameter_list_size++] =
+        V8String(isolate, node && node->IsSVGElement() ? "evt" : "event");
+  }
+  DCHECK_LE(parameter_list_size, base::size(parameter_list));
+
+  v8::Local<v8::Object> scopes[3];
+  size_t scopes_size = 0;
+  if (element) {
+    scopes[scopes_size++] =
+        ToV8(document, script_state_of_event_target).As<v8::Object>();
+  }
+  if (form_owner) {
+    scopes[scopes_size++] =
+        ToV8(form_owner, script_state_of_event_target).As<v8::Object>();
+  }
+  if (element) {
+    scopes[scopes_size++] =
+        ToV8(element, script_state_of_event_target).As<v8::Object>();
+  }
+  DCHECK_LE(scopes_size, base::size(scopes));
+
+  v8::ScriptOrigin origin(
+      V8String(isolate, source_url_),
+      v8::Integer::New(isolate, position_.line_.ZeroBasedInt()),
+      v8::Integer::New(isolate, position_.column_.ZeroBasedInt()),
+      // TODO(yukiy): consider which value should be passed here.
+      v8::True(isolate));
+  v8::ScriptCompiler::Source source(V8String(isolate, code_), origin);
+
+  v8::Local<v8::Function> compiled_function;
+  {
+    v8::TryCatch block(isolate);
+    block.SetVerbose(true);
+    v8::MaybeLocal<v8::Function> maybe_result =
+        v8::ScriptCompiler::CompileFunctionInContext(
+            v8_context_of_event_target, &source, parameter_list_size,
+            parameter_list, scopes_size, scopes);
+
+    // Step 7. If body is not parsable as FunctionBody or if parsing detects an
+    // early error, then follow these substeps:
+    //   1. Set eventHandler's value to null.
+    //   2. Report the error for the appropriate script and with the appropriate
+    //      position (line number and column number) given by location, using
+    //      settings object's global object. If the error is still not handled
+    //      after this, then the error may be reported to a developer console.
+    //   3. Return null.
+    if (!maybe_result.ToLocal(&compiled_function))
+      return v8::Null(isolate);
+  }
+
+  // Step 12. Set eventHandler's value to the result of creating a Web IDL
+  // EventHandler callback function object whose object reference is function
+  // and whose callback context is settings object.
+  compiled_function->SetName(V8String(isolate, function_name_));
+  SetCompiledHandler(
+      script_state_of_event_target, compiled_function,
+      V8PrivateProperty::GetCustomWrappableEventHandler(GetIsolate()));
+
+  return JSEventHandler::GetListenerObject(event_target);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/js_event_handler_for_content_attribute.h b/third_party/blink/renderer/bindings/core/v8/js_event_handler_for_content_attribute.h
new file mode 100644
index 0000000..816b0fb
--- /dev/null
+++ b/third_party/blink/renderer/bindings/core/v8/js_event_handler_for_content_attribute.h
@@ -0,0 +1,86 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_JS_EVENT_HANDLER_FOR_CONTENT_ATTRIBUTE_H_
+#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_JS_EVENT_HANDLER_FOR_CONTENT_ATTRIBUTE_H_
+
+#include "third_party/blink/renderer/bindings/core/v8/js_event_handler.h"
+#include "third_party/blink/renderer/platform/wtf/text/text_position.h"
+
+namespace blink {
+
+// |JSEventHandlerForContentAttribute| supports lazy compilation for content
+// attribute. This performs in the same way as |JSEventHandler| after it gets
+// compiled.
+class JSEventHandlerForContentAttribute final : public JSEventHandler {
+ public:
+  static JSEventHandlerForContentAttribute* Create(
+      const AtomicString& function_name,
+      const String& code,
+      const String& source_url,
+      const TextPosition& position,
+      v8::Isolate* isolate,
+      DOMWrapperWorld& world,
+      HandlerType type = HandlerType::kEventHandler) {
+    return new JSEventHandlerForContentAttribute(
+        isolate, world, function_name, code, source_url, position, type);
+  }
+
+  // blink::EventListener overrides:
+  bool IsEventHandlerForContentAttribute() const override { return true; }
+
+  // blink::JSBasedEventListener overrides:
+  v8::Local<v8::Value> GetListenerObject(EventTarget&) override;
+
+  const String& Code() const override { return code_; }
+
+ protected:
+  // blink::JSBasedEventListener override:
+  v8::Isolate* GetIsolate() const override { return isolate_; }
+  ScriptState* GetScriptState() const override {
+    DCHECK(HasCompiledHandler());
+    return JSEventHandler::GetScriptState();
+  }
+  DOMWrapperWorld& GetWorld() const override { return *world_; }
+
+ private:
+  JSEventHandlerForContentAttribute(v8::Isolate* isolate,
+                                    DOMWrapperWorld& world,
+                                    const AtomicString& function_name,
+                                    const String& code,
+                                    const String& source_url,
+                                    const TextPosition& position,
+                                    HandlerType type)
+      : JSEventHandler(type),
+        did_compile_(false),
+        function_name_(function_name),
+        code_(code),
+        source_url_(source_url),
+        position_(position),
+        isolate_(isolate),
+        world_(&world) {}
+
+  // Implements Step 3. of "get the current value of the event handler".
+  // The compiled v8::Function is returned and |JSEventHandler::event_handler_|
+  // gets initialized with it if lazy compilation succeeds.
+  // Otherwise, v8::Null is returned.
+  // https://html.spec.whatwg.org/multipage/webappapis.html#getting-the-current-value-of-the-event-handler
+  v8::Local<v8::Value> GetCompiledHandler(EventTarget&);
+
+  // Lazy compilation for content attribute should be tried only once, but we
+  // cannot see whether it had never tried to compile or it has already failed
+  // when |HasCompiledHandler()| returns false. |did_compile_| is used for
+  // checking that.
+  bool did_compile_;
+  const AtomicString function_name_;
+  String code_;
+  String source_url_;
+  TextPosition position_;
+  v8::Isolate* isolate_;
+  scoped_refptr<DOMWrapperWorld> world_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_JS_EVENT_HANDLER_FOR_CONTENT_ATTRIBUTE_H_
diff --git a/third_party/blink/renderer/bindings/core/v8/js_event_listener.cc b/third_party/blink/renderer/bindings/core/v8/js_event_listener.cc
new file mode 100644
index 0000000..3ff5da4
--- /dev/null
+++ b/third_party/blink/renderer/bindings/core/v8/js_event_listener.cc
@@ -0,0 +1,28 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/bindings/core/v8/js_event_listener.h"
+
+#include "third_party/blink/renderer/core/dom/events/event.h"
+#include "third_party/blink/renderer/core/dom/events/event_target.h"
+
+namespace blink {
+
+// https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke
+void JSEventListener::CallListenerFunction(EventTarget&,
+                                           Event& event,
+                                           v8::Local<v8::Value> js_event) {
+  // Step 10: Call a listener with event's currentTarget as receiver and event
+  // and handle errors if thrown.
+  v8::Maybe<void> maybe_result =
+      event_listener_->handleEvent(event.currentTarget(), &event);
+  ALLOW_UNUSED_LOCAL(maybe_result);
+}
+
+void JSEventListener::Trace(blink::Visitor* visitor) {
+  visitor->Trace(event_listener_);
+  JSBasedEventListener::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/js_event_listener.h b/third_party/blink/renderer/bindings/core/v8/js_event_listener.h
new file mode 100644
index 0000000..f23780cc
--- /dev/null
+++ b/third_party/blink/renderer/bindings/core/v8/js_event_listener.h
@@ -0,0 +1,79 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_JS_EVENT_LISTENER_H_
+#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_JS_EVENT_LISTENER_H_
+
+#include "third_party/blink/renderer/bindings/core/v8/js_based_event_listener.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_event_listener.h"
+#include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h"
+
+namespace blink {
+
+// |JSEventListener| implements EventListener in the DOM standard.
+// https://dom.spec.whatwg.org/#callbackdef-eventlistener
+class CORE_EXPORT JSEventListener final : public JSBasedEventListener {
+ public:
+  static JSEventListener* Create(ScriptState* script_state,
+                                 v8::Local<v8::Object> listener,
+                                 const V8PrivateProperty::Symbol& property) {
+    return new JSEventListener(script_state, listener, property);
+  }
+
+  // blink::CustomWrappable overrides:
+  void Trace(blink::Visitor*) override;
+
+  // blink::EventListener overrides:
+  //
+  // Check the identity of |V8EventListener::callback_object_|. There can be
+  // multiple CallbackInterfaceBase objects that have the same
+  // |CallbackInterfaceBase::callback_object_| but have different
+  // |CallbackInterfaceBase::incumbent_script_state_|s.
+  bool operator==(const EventListener& other) const override {
+    if (other.GetType() != kJSEventListenerType)
+      return false;
+    return event_listener_->HasTheSameCallbackObject(
+        *static_cast<const JSEventListener*>(&other)->event_listener_);
+  }
+
+  // blink::JSBasedEventListener overrides:
+  // TODO(crbug.com/881688): remove empty check for this method. This method
+  // should return v8::Object or v8::Null.
+  v8::Local<v8::Value> GetListenerObject(EventTarget&) override {
+    return event_listener_->CallbackObject();
+  }
+
+ protected:
+  // blink::JSBasedEventListener overrides:
+  v8::Isolate* GetIsolate() const override {
+    return event_listener_->GetIsolate();
+  }
+  ScriptState* GetScriptState() const override {
+    return event_listener_->CallbackRelevantScriptState();
+  }
+  DOMWrapperWorld& GetWorld() const override {
+    return event_listener_->CallbackRelevantScriptState()->World();
+  }
+
+ private:
+  JSEventListener(ScriptState* script_state,
+                  v8::Local<v8::Object> listener,
+                  const V8PrivateProperty::Symbol& property)
+      : JSBasedEventListener(kJSEventListenerType),
+        event_listener_(V8EventListener::CreateOrNull(listener)) {
+    DCHECK(event_listener_);
+    Attach(script_state, listener, property, this);
+  }
+
+  // blink::JSBasedEventListener override:
+  void CallListenerFunction(EventTarget&,
+                            Event&,
+                            v8::Local<v8::Value> js_event) override;
+
+  const TraceWrapperMember<V8EventListener> event_listener_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_JS_EVENT_LISTENER_H_
diff --git a/third_party/blink/renderer/bindings/core/v8/script_event_listener.cc b/third_party/blink/renderer/bindings/core/v8/script_event_listener.cc
index 8a53bfd..15576be 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_event_listener.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_event_listener.cc
@@ -30,25 +30,22 @@
 
 #include "third_party/blink/renderer/bindings/core/v8/script_event_listener.h"
 
-#include "third_party/blink/renderer/bindings/core/v8/scheduled_action.h"
+#include "third_party/blink/renderer/bindings/core/v8/js_event_handler_for_content_attribute.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
-#include "third_party/blink/renderer/bindings/core/v8/source_location.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_event_listener_impl.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_lazy_event_listener.h"
-#include "third_party/blink/renderer/bindings/core/v8/window_proxy.h"
 #include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/dom/document_parser.h"
 #include "third_party/blink/renderer/core/dom/qualified_name.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
-#include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "v8/include/v8.h"
 
 namespace blink {
 
+// TODO(yukiy): make this method receive |Document*| instead of |Node*|, which
+// is no longer necessary.
 EventListener* CreateAttributeEventListener(Node* node,
                                             const QualifiedName& name,
-                                            const AtomicString& value) {
+                                            const AtomicString& value,
+                                            JSEventHandler::HandlerType type) {
   DCHECK(node);
   if (value.IsNull())
     return nullptr;
@@ -74,15 +71,17 @@
   // when parsing HTML. In that case we should assume the main world.
   v8::Local<v8::Context> v8_context = isolate->GetCurrentContext();
 
-  return V8LazyEventListener::Create(
-      name.LocalName(), value, source_url, position, node, isolate,
+  return JSEventHandlerForContentAttribute::Create(
+      name.LocalName(), value, source_url, position, isolate,
       v8_context.IsEmpty() ? DOMWrapperWorld::MainWorld()
-                           : ScriptState::From(v8_context)->World());
+                           : ScriptState::From(v8_context)->World(),
+      type);
 }
 
 EventListener* CreateAttributeEventListener(LocalFrame* frame,
                                             const QualifiedName& name,
-                                            const AtomicString& value) {
+                                            const AtomicString& value,
+                                            JSEventHandler::HandlerType type) {
   if (!frame)
     return nullptr;
 
@@ -103,64 +102,11 @@
   // when parsing HTML. In that case we should assume the main world.
   v8::Local<v8::Context> v8_context = isolate->GetCurrentContext();
 
-  return V8LazyEventListener::Create(
-      name.LocalName(), value, source_url, position, nullptr, isolate,
+  return JSEventHandlerForContentAttribute::Create(
+      name.LocalName(), value, source_url, position, isolate,
       v8_context.IsEmpty() ? DOMWrapperWorld::MainWorld()
-                           : ScriptState::From(v8_context)->World());
-}
-
-v8::Local<v8::Object> EventListenerHandler(ExecutionContext* execution_context,
-                                           EventListener* listener) {
-  if (auto* v8_listener = V8AbstractEventHandler::Cast(listener)) {
-    return v8_listener->GetListenerObject(execution_context);
-  }
-  if (auto* v8_listener = V8EventListenerImpl::Cast(listener)) {
-    return v8_listener->GetListenerObject();
-  }
-  return v8::Local<v8::Object>();
-}
-
-v8::Local<v8::Function> EventListenerEffectiveFunction(
-    v8::Isolate* isolate,
-    v8::Local<v8::Object> handler) {
-  v8::Local<v8::Function> function;
-  if (handler->IsFunction()) {
-    function = handler.As<v8::Function>();
-  } else if (handler->IsObject()) {
-    v8::Local<v8::Value> property;
-    // Try the "handleEvent" method (EventListener interface).
-    if (handler
-            ->Get(handler->CreationContext(),
-                  V8AtomicString(isolate, "handleEvent"))
-            .ToLocal(&property) &&
-        property->IsFunction())
-      function = property.As<v8::Function>();
-    // Fall back to the "constructor" property.
-    else if (handler
-                 ->Get(handler->CreationContext(),
-                       V8AtomicString(isolate, "constructor"))
-                 .ToLocal(&property) &&
-             property->IsFunction())
-      function = property.As<v8::Function>();
-  }
-  if (!function.IsEmpty())
-    return GetBoundFunction(function);
-  return v8::Local<v8::Function>();
-}
-
-// TODO(yukiy): move this method into V8EventListenerImpl or interface class
-// of EventListener and EventHandler
-std::unique_ptr<SourceLocation> GetFunctionLocation(
-    ExecutionContext* execution_context,
-    EventListener* listener) {
-  v8::Isolate* isolate = ToIsolate(execution_context);
-  v8::HandleScope handle_scope(isolate);
-  v8::Local<v8::Object> handler =
-      EventListenerHandler(execution_context, listener);
-  if (handler.IsEmpty())
-    return nullptr;
-  return SourceLocation::FromFunction(
-      EventListenerEffectiveFunction(isolate, handler));
+                           : ScriptState::From(v8_context)->World(),
+      type);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/script_event_listener.h b/third_party/blink/renderer/bindings/core/v8/script_event_listener.h
index a0414bb..108227a 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_event_listener.h
+++ b/third_party/blink/renderer/bindings/core/v8/script_event_listener.h
@@ -33,26 +33,26 @@
 
 #include <memory>
 
-#include "third_party/blink/renderer/core/dom/events/event_listener.h"
+#include "third_party/blink/renderer/bindings/core/v8/js_event_handler.h"
 
 namespace blink {
 
 class LocalFrame;
 class QualifiedName;
-class SourceLocation;
 
-EventListener* CreateAttributeEventListener(Node*,
-                                            const QualifiedName&,
-                                            const AtomicString& value);
-EventListener* CreateAttributeEventListener(LocalFrame*,
-                                            const QualifiedName&,
-                                            const AtomicString& value);
-v8::Local<v8::Object> EventListenerHandler(ExecutionContext*, EventListener*);
-v8::Local<v8::Function> EventListenerEffectiveFunction(
-    v8::Isolate*,
-    v8::Local<v8::Object> handler);
-std::unique_ptr<SourceLocation> GetFunctionLocation(ExecutionContext*,
-                                                    EventListener*);
+// TODO(bindings): consider to remove these functions.
+EventListener* CreateAttributeEventListener(
+    Node*,
+    const QualifiedName& name,
+    const AtomicString& value,
+    JSEventHandler::HandlerType type =
+        JSEventHandler::HandlerType::kEventHandler);
+EventListener* CreateAttributeEventListener(
+    LocalFrame*,
+    const QualifiedName& name,
+    const AtomicString& value,
+    JSEventHandler::HandlerType type =
+        JSEventHandler::HandlerType::kEventHandler);
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.h b/third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.h
index 5f04bf7..6b9639e 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.h
@@ -107,10 +107,12 @@
 
   bool BelongsToTheCurrentWorld(ExecutionContext*) const final;
 
-  bool IsAttribute() const final { return is_attribute_; }
+  bool IsEventHandler() const final { return is_attribute_; }
 
   v8::Isolate* GetIsolate() const { return isolate_; }
-  DOMWrapperWorld* GetWorldForInspector() const final { return world_.get(); }
+  DOMWrapperWorld* GetWorldPtrForInspector() const final {
+    return world_.get();
+  }
 
   void Trace(blink::Visitor*) override;
 
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_event_listener_helper.cc b/third_party/blink/renderer/bindings/core/v8/v8_event_listener_helper.cc
index e9208db..a21fa602 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_event_listener_helper.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_event_listener_helper.cc
@@ -31,10 +31,9 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_event_listener_helper.h"
 
 #include "third_party/blink/renderer/bindings/core/v8/custom_wrappable_adapter.h"
+#include "third_party/blink/renderer/bindings/core/v8/js_event_handler.h"
+#include "third_party/blink/renderer/bindings/core/v8/js_event_listener.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_error_handler.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_event_listener_impl.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_event_listener_or_event_handler.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_window.h"
 #include "third_party/blink/renderer/platform/bindings/v8_private_property.h"
 
@@ -63,7 +62,6 @@
 EventListener* V8EventListenerHelper::GetEventListener(
     ScriptState* script_state,
     v8::Local<v8::Value> value,
-    bool is_attribute,
     ListenerLookupType lookup) {
   RUNTIME_CALL_TIMER_SCOPE(script_state->GetIsolate(),
                            RuntimeCallStats::CounterId::kGetEventListener);
@@ -73,40 +71,37 @@
   v8::Local<v8::Object> object = value.As<v8::Object>();
   v8::Isolate* isolate = script_state->GetIsolate();
   V8PrivateProperty::Symbol listener_property =
-      is_attribute
-          ? V8PrivateProperty::GetCustomWrappableEventHandler(isolate)
-          : V8PrivateProperty::GetCustomWrappableEventListener(isolate);
+      V8PrivateProperty::GetCustomWrappableEventListener(isolate);
 
   return GetEventListenerInternal<EventListener>(
       script_state, object, listener_property, lookup,
-      [object, is_attribute, script_state, listener_property]() {
-        return is_attribute
-                   ? static_cast<EventListener*>(
-                         V8EventListenerOrEventHandler::Create(
-                             object, is_attribute, script_state,
-                             listener_property))
-                   : static_cast<EventListener*>(V8EventListenerImpl::Create(
-                         object, script_state, listener_property));
+      [object, script_state, listener_property]() {
+        return static_cast<EventListener*>(
+            JSEventListener::Create(script_state, object, listener_property));
       });
 }
 
 // static
-EventListener* V8EventListenerHelper::EnsureErrorHandler(
+EventListener* V8EventListenerHelper::GetEventHandler(
     ScriptState* script_state,
-    v8::Local<v8::Value> value) {
+    v8::Local<v8::Value> value,
+    JSEventHandler::HandlerType handler_type,
+    ListenerLookupType lookup) {
+  RUNTIME_CALL_TIMER_SCOPE(script_state->GetIsolate(),
+                           RuntimeCallStats::CounterId::kGetEventListener);
+
   if (!value->IsObject())
     return nullptr;
   v8::Local<v8::Object> object = value.As<v8::Object>();
   v8::Isolate* isolate = script_state->GetIsolate();
   V8PrivateProperty::Symbol listener_property =
-      V8PrivateProperty::GetV8ErrorHandlerErrorHandler(isolate);
+      V8PrivateProperty::GetCustomWrappableEventHandler(isolate);
 
-  return GetEventListenerInternal<V8ErrorHandler>(
-      script_state, object, listener_property, kListenerFindOrCreate,
-      [object, script_state, listener_property]() {
-        const bool is_attribute = true;
-        return V8ErrorHandler::Create(object, is_attribute, script_state,
-                                      listener_property);
+  return GetEventListenerInternal<EventListener>(
+      script_state, object, listener_property, lookup,
+      [object, script_state, listener_property, handler_type]() {
+        return static_cast<EventListener*>(JSEventHandler::Create(
+            script_state, object, listener_property, handler_type));
       });
 }
 
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_event_listener_helper.h b/third_party/blink/renderer/bindings/core/v8/v8_event_listener_helper.h
index e105210..4ac4d83 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_event_listener_helper.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_event_listener_helper.h
@@ -31,6 +31,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_EVENT_LISTENER_HELPER_H_
 #define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_EVENT_LISTENER_HELPER_H_
 
+#include "third_party/blink/renderer/bindings/core/v8/js_event_handler.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
@@ -51,11 +52,11 @@
  public:
   CORE_EXPORT static EventListener* GetEventListener(ScriptState*,
                                                      v8::Local<v8::Value>,
-                                                     bool is_attribute,
                                                      ListenerLookupType);
-
-  CORE_EXPORT static EventListener* EnsureErrorHandler(ScriptState*,
-                                                       v8::Local<v8::Value>);
+  CORE_EXPORT static EventListener* GetEventHandler(ScriptState*,
+                                                    v8::Local<v8::Value>,
+                                                    JSEventHandler::HandlerType,
+                                                    ListenerLookupType);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_event_listener_impl.cc b/third_party/blink/renderer/bindings/core/v8/v8_event_listener_impl.cc
deleted file mode 100644
index 4316f37..0000000
--- a/third_party/blink/renderer/bindings/core/v8/v8_event_listener_impl.cc
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/bindings/core/v8/v8_event_listener_impl.h"
-
-#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
-#include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
-#include "third_party/blink/renderer/core/dom/events/event.h"
-#include "third_party/blink/renderer/core/dom/events/event_target.h"
-#include "third_party/blink/renderer/core/execution_context/execution_context.h"
-#include "third_party/blink/renderer/core/probe/core_probes.h"
-#include "third_party/blink/renderer/core/workers/worker_or_worklet_global_scope.h"
-#include "third_party/blink/renderer/platform/bindings/v8_private_property.h"
-#include "third_party/blink/renderer/platform/instance_counters.h"
-
-namespace blink {
-
-V8EventListenerImpl::V8EventListenerImpl(
-    v8::Local<v8::Object> listener,
-    ScriptState* script_state,
-    const V8PrivateProperty::Symbol& property)
-    : EventListener(kJSEventListenerType),
-      event_listener_(V8EventListener::CreateOrNull(listener)) {
-  DCHECK(event_listener_);
-  Attach(script_state, listener, property, this);
-  if (IsMainThread()) {
-    InstanceCounters::IncrementCounter(
-        InstanceCounters::kJSEventListenerCounter);
-  }
-}
-
-V8EventListenerImpl::~V8EventListenerImpl() {
-  if (IsMainThread()) {
-    InstanceCounters::DecrementCounter(
-        InstanceCounters::kJSEventListenerCounter);
-  }
-}
-
-// https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke
-void V8EventListenerImpl::handleEvent(
-    ExecutionContext* execution_context_of_event_target,
-    Event* event) {
-  // Don't reenter V8 if execution was terminated in this instance of V8.
-  // For example, worker can be terminated in event listener, and also window
-  // can be terminated from inspector by the TerminateExecution method.
-  if (event_listener_->GetIsolate()->IsExecutionTerminating())
-    return;
-
-  DCHECK(execution_context_of_event_target);
-  DCHECK(event);
-  if (!event->CanBeDispatchedInWorld(World()))
-    return;
-
-  ScriptState* script_state_of_listener =
-      event_listener_->CallbackRelevantScriptState();
-  if (!script_state_of_listener->ContextIsValid())
-    return;
-
-  ScriptState::Scope scope(script_state_of_listener);
-
-  // TODO(crbug.com/881688): Remove this check because listener should not be
-  // empty on calling it here.
-  if (GetListenerObject().IsEmpty())
-    return;
-
-  // https://dom.spec.whatwg.org/#firing-events
-  // Step 2. of firing events: Let event be the result of creating an event
-  // given eventConstructor, in the relevant Realm of target.
-  //
-  // (Note: a |js_event|, an v8::Value for |event| in the relevant realm of
-  // |event|'s target, is created here. It should be done before dispatching
-  // events, but Event and EventTarget do not have world to get |v8_context|, so
-  // it is done here with |execution_context_of_event_target|.)
-  v8::Local<v8::Context> v8_context =
-      ToV8Context(execution_context_of_event_target, World());
-  if (v8_context.IsEmpty())
-    return;
-  v8::Local<v8::Value> js_event =
-      ToV8(event, v8_context->Global(), event_listener_->GetIsolate());
-  if (js_event.IsEmpty())
-    return;
-
-  // Step 6: Let |global| be listener callback’s associated Realm’s global
-  // object.
-  v8::Local<v8::Object> global =
-      script_state_of_listener->GetContext()->Global();
-
-  // Step 8: If global is a Window object, then:
-  // Set currentEvent to global’s current event.
-  // If tuple’s item-in-shadow-tree is false, then set global’s current event to
-  // event.
-  V8PrivateProperty::Symbol event_symbol =
-      V8PrivateProperty::GetGlobalEvent(event_listener_->GetIsolate());
-  ExecutionContext* execution_context_of_listener =
-      ExecutionContext::From(script_state_of_listener);
-  v8::Local<v8::Value> current_event;
-  if (execution_context_of_listener->IsDocument()) {
-    current_event = event_symbol.GetOrUndefined(global).ToLocalChecked();
-    // Expose the event object as |window.event|, except when the event's target
-    // is in a V1 shadow tree.
-    Node* target_node = event->target()->ToNode();
-    if (!(target_node && target_node->IsInV1ShadowTree()))
-      event_symbol.Set(global, js_event);
-  }
-
-  {
-    // Catch exceptions thrown in the event listener if any and report them to
-    // DevTools console.
-    v8::TryCatch try_catch(event_listener_->GetIsolate());
-    try_catch.SetVerbose(true);
-
-    // Step 10: Call a listener with event's currentTarget as receiver and event
-    // and handle errors if thrown.
-    v8::Maybe<void> maybe_result =
-        event_listener_->handleEvent(event->currentTarget(), event);
-    ALLOW_UNUSED_LOCAL(maybe_result);
-
-    if (try_catch.HasCaught()) {
-      // Step 10-2: Set legacyOutputDidListenersThrowFlag if given.
-      event->LegacySetDidListenersThrowFlag();
-    }
-
-    // TODO(yukiy): consider to set |global|’s current event to |current_event|
-    // after execution is terminated if it is necessary and possible.
-    if (event_listener_->GetIsolate()->IsExecutionTerminating())
-      return;
-  }
-
-  // Step 12: If |global| is a Window object, then set |global|’s current event
-  // to |current_event|.
-  if (execution_context_of_listener->IsDocument())
-    event_symbol.Set(global, current_event);
-}
-
-void V8EventListenerImpl::Trace(blink::Visitor* visitor) {
-  visitor->Trace(event_listener_);
-  EventListener::Trace(visitor);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_event_listener_impl.h b/third_party/blink/renderer/bindings/core/v8/v8_event_listener_impl.h
deleted file mode 100644
index 4ac9ba2..0000000
--- a/third_party/blink/renderer/bindings/core/v8/v8_event_listener_impl.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_EVENT_LISTENER_IMPL_H_
-#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_EVENT_LISTENER_IMPL_H_
-
-#include "third_party/blink/renderer/bindings/core/v8/v8_event_listener.h"
-#include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/dom/events/event_listener.h"
-#include "third_party/blink/renderer/platform/bindings/trace_wrapper_member.h"
-#include "v8/include/v8.h"
-
-namespace blink {
-
-class Event;
-
-class CORE_EXPORT V8EventListenerImpl final : public EventListener {
- public:
-  static V8EventListenerImpl* Create(
-      v8::Local<v8::Object> listener,
-      ScriptState* script_state,
-      const V8PrivateProperty::Symbol& property) {
-    return new V8EventListenerImpl(listener, script_state, property);
-  }
-
-  static const V8EventListenerImpl* Cast(const EventListener* listener) {
-    return listener->GetType() == kJSEventListenerType
-               ? static_cast<const V8EventListenerImpl*>(listener)
-               : nullptr;
-  }
-
-  static V8EventListenerImpl* Cast(EventListener* listener) {
-    return const_cast<V8EventListenerImpl*>(
-        Cast(const_cast<const EventListener*>(listener)));
-  }
-
-  ~V8EventListenerImpl() override;
-
-  // Check the identity of |V8EventListener::callback_object_|. There can be
-  // multiple CallbackInterfaceBase objects that have the same
-  // |CallbackInterfaceBase::callback_object_| but have different
-  // |CallbackInterfaceBase::incumbent_script_state_|s.
-  bool operator==(const EventListener& other) const override {
-    const V8EventListenerImpl* other_event_listener = Cast(&other);
-    if (!other_event_listener)
-      return false;
-    return event_listener_->HasTheSameCallbackObject(
-        *other_event_listener->event_listener_);
-  }
-
-  // blink::EventListener overrides:
-  v8::Local<v8::Object> GetListenerObjectForInspector(
-      ExecutionContext* execution_context) override {
-    return event_listener_->CallbackObject();
-  }
-  DOMWrapperWorld* GetWorldForInspector() const override { return &World(); }
-  void handleEvent(ExecutionContext*, Event*) override;
-  void Trace(blink::Visitor*) override;
-
-  v8::Local<v8::Object> GetListenerObject() const {
-    return event_listener_->CallbackObject();
-  }
-
- private:
-  V8EventListenerImpl(v8::Local<v8::Object>,
-                      ScriptState*,
-                      const V8PrivateProperty::Symbol&);
-
-  DOMWrapperWorld& World() const {
-    return event_listener_->CallbackRelevantScriptState()->World();
-  }
-
-  const TraceWrapperMember<V8EventListener> event_listener_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_EVENT_LISTENER_IMPL_H_
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_event_listener_or_event_handler.cc b/third_party/blink/renderer/bindings/core/v8/v8_event_listener_or_event_handler.cc
index b345078b..41de9ec4 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_event_listener_or_event_handler.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_event_listener_or_event_handler.cc
@@ -62,7 +62,7 @@
   // attributes in HTML) has [TreatNonObjectAsNull], which implies that
   // non-function objects should be treated as no-op functions that return
   // undefined.
-  if (IsAttribute())
+  if (IsEventHandler())
     return v8::Local<v8::Function>();
 
   // Getting the handleEvent property can runs script in the getter.
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_lazy_event_listener.h b/third_party/blink/renderer/bindings/core/v8/v8_lazy_event_listener.h
index 09788ee..364238c 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_lazy_event_listener.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_lazy_event_listener.h
@@ -82,7 +82,7 @@
 
   // Return true, so that event handlers from markup are not cloned twice when
   // building the shadow tree for SVGUseElements.
-  bool WasCreatedFromMarkup() const override { return true; }
+  bool IsEventHandlerForContentAttribute() const override { return true; }
 
   void CompileScript(ScriptState*, ExecutionContext*);
 
diff --git a/third_party/blink/renderer/bindings/scripts/v8_attributes.py b/third_party/blink/renderer/bindings/scripts/v8_attributes.py
index 73019f4..d58a5d9 100644
--- a/third_party/blink/renderer/bindings/scripts/v8_attributes.py
+++ b/third_party/blink/renderer/bindings/scripts/v8_attributes.py
@@ -105,11 +105,6 @@
     if is_save_same_object:
         includes.add('platform/bindings/v8_private_property.h')
 
-    if (base_idl_type == 'EventHandler' and
-            interface.name in ['Window', 'WorkerGlobalScope'] and
-            attribute.name == 'onerror'):
-        includes.add('bindings/core/v8/v8_error_handler.h')
-
     cached_attribute_validation_method = extended_attributes.get('CachedAttribute')
     keep_alive_for_gc = is_keep_alive_for_gc(interface, attribute)
     if cached_attribute_validation_method or keep_alive_for_gc:
@@ -494,17 +489,16 @@
         getter_name = scoped_name(interface, attribute, cpp_name(attribute))
         context['event_handler_getter_expression'] = '%s(%s)' % (
             getter_name, ', '.join(arguments))
-        if (interface.name in ['Window', 'WorkerGlobalScope'] and
-                attribute.name == 'onerror'):
-            includes.add('bindings/core/v8/v8_error_handler.h')
-            arguments.append(
-                'V8EventListenerHelper::EnsureErrorHandler(' +
-                'ScriptState::ForRelevantRealm(info), v8Value)')
-        else:
-            arguments.append(
-                'V8EventListenerHelper::GetEventListener(' +
-                'ScriptState::ForRelevantRealm(info), v8Value, true, ' +
-                'kListenerFindOrCreate)')
+        handler_type = 'kEventHandler'
+        if attribute.name == 'onerror':
+            handler_type = 'kOnErrorEventHandler'
+        elif attribute.name == 'onbeforeunload':
+            handler_type = 'kOnBeforeUnloadEventHandler'
+        arguments.append(
+            'V8EventListenerHelper::GetEventHandler(' +
+            'ScriptState::ForRelevantRealm(info), v8Value, ' +
+            'JSEventHandler::HandlerType::' + handler_type +
+            ', kListenerFindOrCreate)')
     elif idl_type.base_type == 'SerializedScriptValue':
         arguments.append('std::move(cppValue)')
     else:
diff --git a/third_party/blink/renderer/bindings/scripts/v8_types.py b/third_party/blink/renderer/bindings/scripts/v8_types.py
index dec01b8..033ac59a 100644
--- a/third_party/blink/renderer/bindings/scripts/v8_types.py
+++ b/third_party/blink/renderer/bindings/scripts/v8_types.py
@@ -417,11 +417,8 @@
                             'core/typed_arrays/array_buffer_view_helpers.h',
                             'core/typed_arrays/flexible_array_buffer_view.h']),
     'Dictionary': set(['bindings/core/v8/dictionary.h']),
-    'EventHandler': set(['bindings/core/v8/v8_abstract_event_handler.h',
-                         'bindings/core/v8/v8_event_listener_helper.h']),
-    'EventListener': set(['bindings/core/v8/binding_security.h',
-                          'bindings/core/v8/v8_event_listener_helper.h',
-                          'core/frame/local_dom_window.h']),
+    'EventHandler': set(['bindings/core/v8/v8_event_listener_helper.h']),
+    'EventListener': set(['bindings/core/v8/v8_event_listener_helper.h']),
     'HTMLCollection': set(['bindings/core/v8/v8_html_collection.h',
                            'core/dom/class_collection.h',
                            'core/dom/tag_collection.h',
@@ -1046,7 +1043,7 @@
     # Special cases
     'Dictionary': '{cpp_value}.V8Value()',
     'EventHandler':
-        'V8AbstractEventHandler::GetListenerOrNull({isolate}, impl, {cpp_value})',
+        'JSBasedEventListener::GetListenerOrNull({isolate}, impl, {cpp_value})',
     'NodeFilter': 'ToV8({cpp_value}, {creation_context}, {isolate})',
     'Record': 'ToV8({cpp_value}, {creation_context}, {isolate})',
     'ScriptValue': '{cpp_value}.V8Value()',
diff --git a/third_party/blink/renderer/bindings/templates/methods.cpp.tmpl b/third_party/blink/renderer/bindings/templates/methods.cpp.tmpl
index b3b60838..b0e5543 100644
--- a/third_party/blink/renderer/bindings/templates/methods.cpp.tmpl
+++ b/third_party/blink/renderer/bindings/templates/methods.cpp.tmpl
@@ -166,9 +166,9 @@
   return;
 }
 {% if method.name == 'removeEventListener' or method.name == 'removeListener' %}
-{{argument.name}} = V8EventListenerHelper::GetEventListener(ScriptState::Current(info.GetIsolate()), info[{{argument.index}}], false, kListenerFindOnly);
+{{argument.name}} = V8EventListenerHelper::GetEventListener(ScriptState::Current(info.GetIsolate()), info[{{argument.index}}], kListenerFindOnly);
 {% else %}{# method.name == 'AddEventListener' #}
-{{argument.name}} = V8EventListenerHelper::GetEventListener(ScriptState::Current(info.GetIsolate()), info[{{argument.index}}], false, kListenerFindOrCreate);
+{{argument.name}} = V8EventListenerHelper::GetEventListener(ScriptState::Current(info.GetIsolate()), info[{{argument.index}}], kListenerFindOrCreate);
 {% endif %}{# method.name #}
 {% else %}{# argument.idl_type == 'EventListener' #}
 if (info[{{argument.index}}]->IsObject()) {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc
index 8ec5561..fe533d8 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface.cc
@@ -15,7 +15,6 @@
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_element.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_event_listener_helper.h"
@@ -994,7 +993,7 @@
 
   EventListener* cppValue(WTF::GetPtr(impl->implementsEventHandlerAttribute()));
 
-  V8SetReturnValue(info, V8AbstractEventHandler::GetListenerOrNull(info.GetIsolate(), impl, cppValue));
+  V8SetReturnValue(info, JSBasedEventListener::GetListenerOrNull(info.GetIsolate(), impl, cppValue));
 }
 
 static void implementsEventHandlerAttributeAttributeSetter(v8::Local<v8::Value> v8Value, const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -1008,7 +1007,7 @@
 
   // Prepare the value to be set.
 
-  impl->setImplementsEventHandlerAttribute(V8EventListenerHelper::GetEventListener(ScriptState::ForRelevantRealm(info), v8Value, true, kListenerFindOrCreate));
+  impl->setImplementsEventHandlerAttribute(V8EventListenerHelper::GetEventHandler(ScriptState::ForRelevantRealm(info), v8Value, JSEventHandler::HandlerType::kEventHandler, kListenerFindOrCreate));
 }
 
 static void implementsRuntimeEnabledNodeAttributeAttributeGetter(const v8::FunctionCallbackInfo<v8::Value>& info) {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.cc
index 40df4e5..37b2d68 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_interface_node.cc
@@ -13,7 +13,6 @@
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_event_listener_helper.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_test_interface_empty.h"
@@ -133,7 +132,7 @@
 
   EventListener* cppValue(WTF::GetPtr(impl->eventHandlerAttribute()));
 
-  V8SetReturnValue(info, V8AbstractEventHandler::GetListenerOrNull(info.GetIsolate(), impl, cppValue));
+  V8SetReturnValue(info, JSBasedEventListener::GetListenerOrNull(info.GetIsolate(), impl, cppValue));
 }
 
 static void eventHandlerAttributeAttributeSetter(v8::Local<v8::Value> v8Value, const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -147,7 +146,7 @@
 
   // Prepare the value to be set.
 
-  impl->setEventHandlerAttribute(V8EventListenerHelper::GetEventListener(ScriptState::ForRelevantRealm(info), v8Value, true, kListenerFindOrCreate));
+  impl->setEventHandlerAttribute(V8EventListenerHelper::GetEventHandler(ScriptState::ForRelevantRealm(info), v8Value, JSEventHandler::HandlerType::kEventHandler, kListenerFindOrCreate));
 }
 
 static void perWorldBindingsReadonlyTestInterfaceEmptyAttributeAttributeGetter(const v8::FunctionCallbackInfo<v8::Value>& info) {
diff --git a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
index 7281e7a..548edfe 100644
--- a/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
+++ b/third_party/blink/renderer/bindings/tests/results/core/v8_test_object.cc
@@ -20,7 +20,6 @@
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value_factory.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_array_buffer.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_array_buffer_view.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_attr.h"
@@ -1607,7 +1606,7 @@
 
   EventListener* cppValue(WTF::GetPtr(impl->eventHandlerAttribute()));
 
-  V8SetReturnValue(info, V8AbstractEventHandler::GetListenerOrNull(info.GetIsolate(), impl, cppValue));
+  V8SetReturnValue(info, JSBasedEventListener::GetListenerOrNull(info.GetIsolate(), impl, cppValue));
 }
 
 static void eventHandlerAttributeAttributeSetter(v8::Local<v8::Value> v8Value, const v8::FunctionCallbackInfo<v8::Value>& info) {
@@ -1621,7 +1620,7 @@
 
   // Prepare the value to be set.
 
-  impl->setEventHandlerAttribute(V8EventListenerHelper::GetEventListener(ScriptState::ForRelevantRealm(info), v8Value, true, kListenerFindOrCreate));
+  impl->setEventHandlerAttribute(V8EventListenerHelper::GetEventHandler(ScriptState::ForRelevantRealm(info), v8Value, JSEventHandler::HandlerType::kEventHandler, kListenerFindOrCreate));
 }
 
 static void doubleOrStringAttributeAttributeGetter(const v8::FunctionCallbackInfo<v8::Value>& info) {
diff --git a/third_party/blink/renderer/core/core_idl_files.gni b/third_party/blink/renderer/core/core_idl_files.gni
index 4861186..80fac5d 100644
--- a/third_party/blink/renderer/core/core_idl_files.gni
+++ b/third_party/blink/renderer/core/core_idl_files.gni
@@ -564,6 +564,7 @@
 
 core_callback_function_idl_files =
     get_path_info([
+                    "html/event_handler.idl",
                     "dom/frame_request_callback.idl",
                     "dom/function_string_callback.idl",
                     "dom/idle_request_callback.idl",
diff --git a/third_party/blink/renderer/core/dom/events/event_listener.h b/third_party/blink/renderer/core/dom/events/event_listener.h
index 0e73cdb..b5b2c29 100644
--- a/third_party/blink/renderer/core/dom/events/event_listener.h
+++ b/third_party/blink/renderer/core/dom/events/event_listener.h
@@ -52,11 +52,11 @@
   virtual bool operator==(const EventListener&) const = 0;
   virtual void handleEvent(ExecutionContext*, Event*) = 0;
   virtual const String& Code() const { return g_empty_string; }
-  virtual bool WasCreatedFromMarkup() const { return false; }
+  virtual bool IsEventHandlerForContentAttribute() const { return false; }
   virtual bool BelongsToTheCurrentWorld(ExecutionContext*) const {
     return false;
   }
-  virtual bool IsAttribute() const { return false; }
+  virtual bool IsEventHandler() const { return false; }
 
   // Only DevTools is allowed to use this method.
   // This method may return an empty handle.
@@ -66,7 +66,7 @@
   }
 
   // Only DevTools is allowed to use this method.
-  virtual DOMWrapperWorld* GetWorldForInspector() const { return nullptr; }
+  virtual DOMWrapperWorld* GetWorldPtrForInspector() const { return nullptr; }
 
   ListenerType GetType() const { return type_; }
 
diff --git a/third_party/blink/renderer/core/dom/events/event_listener_map.cc b/third_party/blink/renderer/core/dom/events/event_listener_map.cc
index dd0644c..de30076 100644
--- a/third_party/blink/renderer/core/dom/events/event_listener_map.cc
+++ b/third_party/blink/renderer/core/dom/events/event_listener_map.cc
@@ -189,7 +189,7 @@
     EventListenerVector* listener_vector,
     EventTarget* target) {
   for (auto& event_listener : *listener_vector) {
-    if (event_listener.Callback()->WasCreatedFromMarkup())
+    if (event_listener.Callback()->IsEventHandlerForContentAttribute())
       continue;
     AddEventListenerOptionsResolved options = event_listener.Options();
     target->addEventListener(event_type, event_listener.Callback(), options);
diff --git a/third_party/blink/renderer/core/dom/events/event_target.cc b/third_party/blink/renderer/core/dom/events/event_target.cc
index c82f6aa9..6e4fd78d 100644
--- a/third_party/blink/renderer/core/dom/events/event_target.cc
+++ b/third_party/blink/renderer/core/dom/events/event_target.cc
@@ -37,10 +37,8 @@
 #include "third_party/blink/public/web/web_settings.h"
 #include "third_party/blink/renderer/bindings/core/v8/add_event_listener_options_or_boolean.h"
 #include "third_party/blink/renderer/bindings/core/v8/event_listener_options_or_boolean.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_event_listener.h"
+#include "third_party/blink/renderer/bindings/core/v8/js_based_event_listener.h"
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_abstract_event_handler.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_event_listener_impl.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/dom/events/event_dispatch_forbidden_scope.h"
 #include "third_party/blink/renderer/core/dom/events/event_target_impl.h"
@@ -124,11 +122,13 @@
                                        PerformanceMonitor::kBlockedEvent);
 }
 
-void ReportBlockedEvent(ExecutionContext* context,
+void ReportBlockedEvent(EventTarget& target,
                         const Event& event,
                         RegisteredEventListener* registered_listener,
                         base::TimeDelta delayed) {
-  if (registered_listener->Callback()->IsNativeBased())
+  JSBasedEventListener* listener =
+      JSBasedEventListener::Cast(registered_listener->Callback());
+  if (!listener)
     return;
 
   String message_text = String::Format(
@@ -138,9 +138,21 @@
       "responsive.",
       event.type().GetString().Utf8().data(), delayed.InMilliseconds());
 
-  PerformanceMonitor::ReportGenericViolation(
-      context, PerformanceMonitor::kBlockedEvent, message_text, delayed,
-      GetFunctionLocation(context, registered_listener->Callback()));
+  {
+    v8::Isolate* isolate = ToIsolate(target.GetExecutionContext());
+    v8::HandleScope handle_scope(isolate);
+
+    v8::Local<v8::Value> handler = listener->GetListenerObject(target);
+    PerformanceMonitor::ReportGenericViolation(
+        target.GetExecutionContext(), PerformanceMonitor::kBlockedEvent,
+        message_text, delayed,
+        (handler.IsEmpty() || !handler->IsObject())
+            ? nullptr
+            : SourceLocation::FromFunction(
+                  JSBasedEventListener::EventListenerEffectiveFunction(
+                      isolate, handler.As<v8::Object>())));
+  }
+
   registered_listener->SetBlockedEventWarningEmitted();
 }
 
@@ -289,13 +301,12 @@
   if (RuntimeEnabledFeatures::SmoothScrollJSInterventionEnabled() &&
       event_type == EventTypeNames::mousewheel && ToLocalDOMWindow() &&
       event_listener && !options.hasPassive()) {
-    v8::Local<v8::Object> callback_object;
-    if (V8AbstractEventHandler* v8_listener =
-            V8AbstractEventHandler::Cast(event_listener))
-      callback_object = v8_listener->GetExistingListenerObject();
-    if (V8EventListenerImpl* v8_listener =
-            V8EventListenerImpl::Cast(event_listener))
-      callback_object = v8_listener->GetListenerObject();
+    JSBasedEventListener* v8_listener =
+        JSBasedEventListener::Cast(event_listener);
+    if (!v8_listener)
+      return;
+    v8::Local<v8::Value> callback_object =
+        v8_listener->GetListenerObject(*this);
     if (!callback_object.IsEmpty() && callback_object->IsFunction() &&
         strcmp(
             "ssc_wheel",
@@ -523,7 +534,7 @@
 
   for (auto& event_listener : *listener_vector) {
     EventListener* listener = event_listener.Callback();
-    if (listener->IsAttribute() &&
+    if (listener->IsEventHandler() &&
         listener->BelongsToTheCurrentWorld(GetExecutionContext()))
       return &event_listener;
   }
@@ -849,7 +860,7 @@
         entry[i - 1].Callback() == listener && !entry[i - 1].Passive() &&
         !entry[i - 1].BlockedEventWarningEmitted() &&
         !event.defaultPrevented()) {
-      ReportBlockedEvent(context, event, &entry[i - 1],
+      ReportBlockedEvent(*this, event, &entry[i - 1],
                          now - event.PlatformTimeStamp());
     }
 
diff --git a/third_party/blink/renderer/core/dom/events/event_target.h b/third_party/blink/renderer/core/dom/events/event_target.h
index 7f433504..f9e4fc6 100644
--- a/third_party/blink/renderer/core/dom/events/event_target.h
+++ b/third_party/blink/renderer/core/dom/events/event_target.h
@@ -180,6 +180,8 @@
 
   virtual bool KeepEventInNode(const Event&) const { return false; }
 
+  virtual bool IsWindowOrWorkerGlobalScope() const { return false; }
+
   // Returns true if the target is window, window.document, or
   // window.document.body.
   bool IsTopLevelNode();
diff --git a/third_party/blink/renderer/core/frame/dom_window.cc b/third_party/blink/renderer/core/frame/dom_window.cc
index 2f56ff20..ef89938 100644
--- a/third_party/blink/renderer/core/frame/dom_window.cc
+++ b/third_party/blink/renderer/core/frame/dom_window.cc
@@ -66,6 +66,10 @@
   return this;
 }
 
+bool DOMWindow::IsWindowOrWorkerGlobalScope() const {
+  return true;
+}
+
 Location* DOMWindow::location() const {
   if (!location_)
     location_ = Location::Create(const_cast<DOMWindow*>(this));
diff --git a/third_party/blink/renderer/core/frame/dom_window.h b/third_party/blink/renderer/core/frame/dom_window.h
index 58a3f12..7583923a 100644
--- a/third_party/blink/renderer/core/frame/dom_window.h
+++ b/third_party/blink/renderer/core/frame/dom_window.h
@@ -68,6 +68,7 @@
   // EventTarget overrides:
   const AtomicString& InterfaceName() const override;
   const DOMWindow* ToDOMWindow() const override;
+  bool IsWindowOrWorkerGlobalScope() const final;
 
   // Cross-origin DOM Level 0
   Location* location() const;
diff --git a/third_party/blink/renderer/core/html/event_handler.idl b/third_party/blink/renderer/core/html/event_handler.idl
new file mode 100644
index 0000000..0776298c
--- /dev/null
+++ b/third_party/blink/renderer/core/html/event_handler.idl
@@ -0,0 +1,11 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://html.spec.whatwg.org/C/webappapis.html#event-handler-attributes
+
+// This definition is different from standard in order to handle special types
+// (OnErrorEventHandler, OnBeforeUnloadEventHandler) in the same type with
+// regular EventHandler.
+[TreatNonObjectAsNull]
+callback EventHandlerNonNull = any (any... args);
diff --git a/third_party/blink/renderer/core/html/html_body_element.cc b/third_party/blink/renderer/core/html/html_body_element.cc
index e5d1758..145122c 100644
--- a/third_party/blink/renderer/core/html/html_body_element.cc
+++ b/third_party/blink/renderer/core/html/html_body_element.cc
@@ -131,7 +131,9 @@
   } else if (name == onbeforeunloadAttr) {
     GetDocument().SetWindowAttributeEventListener(
         EventTypeNames::beforeunload,
-        CreateAttributeEventListener(GetDocument().GetFrame(), name, value));
+        CreateAttributeEventListener(
+            GetDocument().GetFrame(), name, value,
+            JSEventHandler::HandlerType::kOnBeforeUnloadEventHandler));
   } else if (name == onunloadAttr) {
     GetDocument().SetWindowAttributeEventListener(
         EventTypeNames::unload,
@@ -155,7 +157,9 @@
   } else if (name == onerrorAttr) {
     GetDocument().SetWindowAttributeEventListener(
         EventTypeNames::error,
-        CreateAttributeEventListener(GetDocument().GetFrame(), name, value));
+        CreateAttributeEventListener(
+            GetDocument().GetFrame(), name, value,
+            JSEventHandler::HandlerType::kOnErrorEventHandler));
   } else if (name == onfocusAttr) {
     GetDocument().SetWindowAttributeEventListener(
         EventTypeNames::focus,
diff --git a/third_party/blink/renderer/core/html/html_frame_element_base.cc b/third_party/blink/renderer/core/html/html_frame_element_base.cc
index 622b233..3e1fce3c 100644
--- a/third_party/blink/renderer/core/html/html_frame_element_base.cc
+++ b/third_party/blink/renderer/core/html/html_frame_element_base.cc
@@ -156,8 +156,11 @@
       SetScrollingMode(kScrollbarAlwaysOff);
   } else if (name == onbeforeunloadAttr) {
     // FIXME: should <frame> elements have beforeunload handlers?
-    SetAttributeEventListener(EventTypeNames::beforeunload,
-                              CreateAttributeEventListener(this, name, value));
+    SetAttributeEventListener(
+        EventTypeNames::beforeunload,
+        CreateAttributeEventListener(
+            this, name, value,
+            JSEventHandler::HandlerType::kOnBeforeUnloadEventHandler));
   } else {
     HTMLFrameOwnerElement::ParseAttribute(params);
   }
diff --git a/third_party/blink/renderer/core/html/html_frame_set_element.cc b/third_party/blink/renderer/core/html/html_frame_set_element.cc
index 0e16c17..fc7c8112 100644
--- a/third_party/blink/renderer/core/html/html_frame_set_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_set_element.cc
@@ -127,7 +127,9 @@
   } else if (name == onbeforeunloadAttr) {
     GetDocument().SetWindowAttributeEventListener(
         EventTypeNames::beforeunload,
-        CreateAttributeEventListener(GetDocument().GetFrame(), name, value));
+        CreateAttributeEventListener(
+            GetDocument().GetFrame(), name, value,
+            JSEventHandler::HandlerType::kOnBeforeUnloadEventHandler));
   } else if (name == onunloadAttr) {
     GetDocument().SetWindowAttributeEventListener(
         EventTypeNames::unload,
@@ -147,7 +149,9 @@
   } else if (name == onerrorAttr) {
     GetDocument().SetWindowAttributeEventListener(
         EventTypeNames::error,
-        CreateAttributeEventListener(GetDocument().GetFrame(), name, value));
+        CreateAttributeEventListener(
+            GetDocument().GetFrame(), name, value,
+            JSEventHandler::HandlerType::kOnErrorEventHandler));
   } else if (name == onfocusAttr) {
     GetDocument().SetWindowAttributeEventListener(
         EventTypeNames::focus,
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc
index e4a214f..cc9758b9 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.cc
@@ -30,6 +30,7 @@
 
 #include "third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.h"
 
+#include "third_party/blink/renderer/bindings/core/v8/js_based_event_listener.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_event_listener.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_event_target.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_node.h"
@@ -108,22 +109,18 @@
       continue;
     for (wtf_size_t k = 0; k < listeners->size(); ++k) {
       EventListener* event_listener = listeners->at(k).Callback();
-      if (event_listener->IsNativeBased())
+      JSBasedEventListener* v8_event_listener =
+          JSBasedEventListener::Cast(event_listener);
+      if (!v8_event_listener)
         continue;
-      // TODO(yukiy): Use a child class of blink::EventListener that is for v8
-      // event listeners here if it is implemented in redesigning
-      // EventListener/EventHandler: https://crbug.com/872138 .
       v8::Local<v8::Context> context = ToV8Context(
-          execution_context, *(event_listener->GetWorldForInspector()));
+          execution_context, v8_event_listener->GetWorldForInspector());
       // Optionally hide listeners from other contexts.
       if (!report_for_all_contexts && context != isolate->GetCurrentContext())
         continue;
-      // GetListenerObjectForInspector() may cause JS in the event attribute to
-      // get compiled, potentially unsuccessfully.  In that case, the function
-      // returns the empty handle without an exception.
-      v8::Local<v8::Object> handler =
-          event_listener->GetListenerObjectForInspector(execution_context);
-      if (handler.IsEmpty())
+      v8::Local<v8::Value> handler =
+          v8_event_listener->GetListenerObject(*target);
+      if (handler.IsEmpty() || !handler->IsObject())
         continue;
       bool use_capture = listeners->at(k).Capture();
       DOMNodeId backend_node_id = 0;
@@ -135,7 +132,7 @@
       }
       event_information->push_back(V8EventListenerInfo(
           type, use_capture, listeners->at(k).Passive(),
-          listeners->at(k).Once(), handler, backend_node_id));
+          listeners->at(k).Once(), handler.As<v8::Object>(), backend_node_id));
     }
   }
 }
@@ -455,7 +452,8 @@
 
   v8::Isolate* isolate = context->GetIsolate();
   v8::Local<v8::Function> function =
-      EventListenerEffectiveFunction(isolate, info.handler);
+      JSBasedEventListener::EventListenerEffectiveFunction(isolate,
+                                                           info.handler);
   if (function.IsEmpty())
     return nullptr;
 
diff --git a/third_party/blink/renderer/core/inspector/thread_debugger.cc b/third_party/blink/renderer/core/inspector/thread_debugger.cc
index bc505ed..ba7feadd 100644
--- a/third_party/blink/renderer/core/inspector/thread_debugger.cc
+++ b/third_party/blink/renderer/core/inspector/thread_debugger.cc
@@ -375,7 +375,7 @@
   Vector<String> types = NormalizeEventTypes(info);
   EventListener* event_listener = V8EventListenerHelper::GetEventListener(
       ScriptState::Current(info.GetIsolate()),
-      v8::Local<v8::Function>::Cast(info.Data()), false,
+      v8::Local<v8::Function>::Cast(info.Data()),
       enabled ? kListenerFindOrCreate : kListenerFindOnly);
   if (!event_listener)
     return;
diff --git a/third_party/blink/renderer/core/svg/svg_script_element.cc b/third_party/blink/renderer/core/svg/svg_script_element.cc
index b88f4bec..0d76d32 100644
--- a/third_party/blink/renderer/core/svg/svg_script_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_script_element.cc
@@ -50,7 +50,9 @@
   if (params.name == HTMLNames::onerrorAttr) {
     SetAttributeEventListener(
         EventTypeNames::error,
-        CreateAttributeEventListener(this, params.name, params.new_value));
+        CreateAttributeEventListener(
+            this, params.name, params.new_value,
+            JSEventHandler::HandlerType::kOnErrorEventHandler));
   } else {
     SVGElement::ParseAttribute(params);
   }
diff --git a/third_party/blink/renderer/core/svg/svg_svg_element.cc b/third_party/blink/renderer/core/svg/svg_svg_element.cc
index e82c0f59..e80aac4 100644
--- a/third_party/blink/renderer/core/svg/svg_svg_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_svg_element.cc
@@ -187,7 +187,9 @@
   } else if (name == HTMLNames::onerrorAttr) {
     GetDocument().SetWindowAttributeEventListener(
         EventTypeNames::error,
-        CreateAttributeEventListener(GetDocument().GetFrame(), name, value));
+        CreateAttributeEventListener(
+            GetDocument().GetFrame(), name, value,
+            JSEventHandler::HandlerType::kOnErrorEventHandler));
   } else if (SVGZoomAndPan::ParseAttribute(name, value)) {
   } else {
     SVGElement::ParseAttribute(params);
diff --git a/third_party/blink/renderer/core/workers/worker_global_scope.h b/third_party/blink/renderer/core/workers/worker_global_scope.h
index 4076260..18c26947 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.h
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.h
@@ -134,6 +134,7 @@
 
   // EventTarget
   ExecutionContext* GetExecutionContext() const final;
+  bool IsWindowOrWorkerGlobalScope() const final { return true; }
 
   // The following methods implement PausbaleObject semantic
   // so that WorkerGlobalScope can be paused.
diff --git a/third_party/blink/renderer/platform/bindings/v8_private_property.h b/third_party/blink/renderer/platform/bindings/v8_private_property.h
index ff6ced2..6eff732 100644
--- a/third_party/blink/renderer/platform/bindings/v8_private_property.h
+++ b/third_party/blink/renderer/platform/bindings/v8_private_property.h
@@ -49,7 +49,6 @@
   X(SameObject, NotificationVibrate)                  \
   X(SameObject, PerformanceLongTaskTimingAttribution) \
   X(SameObject, PushManagerSupportedContentEncodings) \
-  X(V8ErrorHandler, ErrorHandler)                     \
   X(CustomWrappable, EventHandler)                    \
   X(CustomWrappable, EventListener)                   \
   SCRIPT_PROMISE_PROPERTIES(X, Promise)               \