From a59b1825ce3ba5d6e6adf51ab2745c7212789c31 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Wed, 3 Feb 2021 22:47:50 +0100 Subject: [PATCH] LibWeb: Basic implementation of global event handlers :^) Document and HTMLElement now inherit from HTML::GlobalEventHandlers which allows them to support "onfoo" event handler attributes. These are assignable both via IDL attributes and content attributes. Event listeners constructed this way get a special "attribute" flag on them so we know which one to replace if you reassign them. This also allows them to coexist with EventTarget.addEventListener(). This is all a bit sloppy, but it works decently for a first cut. The Window object should also inherit GlobalEventHandlers, but since we don't generate it from IDL, I haven't taken that step here. Also this would be a lot nicer if we supported IDL mixins. --- Userland/Libraries/LibWeb/CMakeLists.txt | 1 + .../CodeGenerators/WrapperGenerator.cpp | 17 ++++ Userland/Libraries/LibWeb/DOM/Document.h | 5 + Userland/Libraries/LibWeb/DOM/Document.idl | 71 +++++++++++++ Userland/Libraries/LibWeb/DOM/EventListener.h | 6 +- Userland/Libraries/LibWeb/DOM/EventTarget.h | 1 + Userland/Libraries/LibWeb/Forward.h | 1 + Userland/Libraries/LibWeb/HTML/EventHandler.h | 54 ++++++++++ .../LibWeb/HTML/GlobalEventHandlers.cpp | 99 +++++++++++++++++++ .../LibWeb/HTML/GlobalEventHandlers.h | 25 +++++ .../Libraries/LibWeb/HTML/HTMLBodyElement.cpp | 7 ++ .../Libraries/LibWeb/HTML/HTMLBodyElement.h | 3 + .../Libraries/LibWeb/HTML/HTMLElement.cpp | 19 ++++ Userland/Libraries/LibWeb/HTML/HTMLElement.h | 12 ++- .../Libraries/LibWeb/HTML/HTMLElement.idl | 72 ++++++++++++++ 15 files changed, 391 insertions(+), 2 deletions(-) create mode 100644 Userland/Libraries/LibWeb/HTML/EventHandler.h create mode 100644 Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.cpp diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index e61f263f26..14b458640c 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -55,6 +55,7 @@ set(SOURCES HTML/AttributeNames.cpp HTML/CanvasRenderingContext2D.cpp HTML/EventNames.cpp + HTML/GlobalEventHandlers.cpp HTML/HTMLAnchorElement.cpp HTML/HTMLAreaElement.cpp HTML/HTMLAudioElement.cpp diff --git a/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp b/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp index 39e8ac2bdd..06213342ad 100644 --- a/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp +++ b/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp @@ -904,6 +904,7 @@ void generate_prototype_implementation(const IDL::Interface& interface) #include #include #include +#include #include #include #include @@ -1096,6 +1097,18 @@ static @fully_qualified_name@* impl_from(JS::VM& vm, JS::GlobalObject& global_ob auto @cpp_name@ = @js_name@@js_suffix@.to_u32(global_object); if (vm.exception()) @return_statement@ +)~~~"); + } else if (parameter.type.name == "EventHandler") { + // x.onfoo = function() { ... } + scoped_generator.append(R"~~~( + HTML::EventHandler @cpp_name@; + if (@js_name@@js_suffix@.is_function()) { + @cpp_name@.callback = JS::make_handle(&@js_name@@js_suffix@.as_function()); + } else if (@js_name@@js_suffix@.is_string()) { + @cpp_name@.string = @js_name@@js_suffix@.as_string().string(); + } else { + @return_statement@ + } )~~~"); } else { dbgln("Unimplemented JS-to-C++ conversion: {}", parameter.type.name); @@ -1173,6 +1186,10 @@ static @fully_qualified_name@* impl_from(JS::VM& vm, JS::GlobalObject& global_ob } else if (return_type.name == "Uint8ClampedArray") { scoped_generator.append(R"~~~( return retval; +)~~~"); + } else if (return_type.name == "EventHandler") { + scoped_generator.append(R"~~~( + return retval.callback.cell(); )~~~"); } else { scoped_generator.append(R"~~~( diff --git a/Userland/Libraries/LibWeb/DOM/Document.h b/Userland/Libraries/LibWeb/DOM/Document.h index be7050b2cc..0f19fefdfd 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.h +++ b/Userland/Libraries/LibWeb/DOM/Document.h @@ -55,6 +55,7 @@ enum class QuirksMode { class Document : public ParentNode , public NonElementParentNode + , public HTML::GlobalEventHandlers , public Bindings::ScriptExecutionContext { public: using WrapperType = Bindings::DocumentWrapper; @@ -233,8 +234,12 @@ public: private: explicit Document(const URL&); + // ^DOM::Node virtual RefPtr create_layout_node() override; + // ^HTML::GlobalEventHandlers + virtual EventTarget& global_event_handlers_to_event_target() final { return *this; } + void tear_down_layout_tree(); void increment_referencing_node_count() diff --git a/Userland/Libraries/LibWeb/DOM/Document.idl b/Userland/Libraries/LibWeb/DOM/Document.idl index 9aa2878d62..bd405a48bb 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.idl +++ b/Userland/Libraries/LibWeb/DOM/Document.idl @@ -36,4 +36,75 @@ interface Document : Node { attribute DOMString title; + // FIXME: These should all come from a GlobalEventHandlers mixin + attribute EventHandler onabort; + attribute EventHandler onauxclick; + attribute EventHandler onblur; + attribute EventHandler oncancel; + attribute EventHandler oncanplay; + attribute EventHandler oncanplaythrough; + attribute EventHandler onchange; + attribute EventHandler onclick; + attribute EventHandler onclose; + attribute EventHandler oncontextmenu; + attribute EventHandler oncuechange; + attribute EventHandler ondblclick; + attribute EventHandler ondrag; + attribute EventHandler ondragend; + attribute EventHandler ondragenter; + attribute EventHandler ondragleave; + attribute EventHandler ondragover; + attribute EventHandler ondragstart; + attribute EventHandler ondrop; + attribute EventHandler ondurationchange; + attribute EventHandler onemptied; + attribute EventHandler onended; + + // FIXME: Should be an OnErrorEventHandler + attribute EventHandler onerror; + + attribute EventHandler onfocus; + attribute EventHandler onformdata; + attribute EventHandler oninput; + attribute EventHandler oninvalid; + attribute EventHandler onkeydown; + attribute EventHandler onkeypress; + attribute EventHandler onkeyup; + attribute EventHandler onload; + attribute EventHandler onloadeddata; + attribute EventHandler onloadedmetadata; + attribute EventHandler onloadstart; + attribute EventHandler onmousedown; + [LegacyLenientThis] attribute EventHandler onmouseenter; + [LegacyLenientThis] attribute EventHandler onmouseleave; + attribute EventHandler onmousemove; + attribute EventHandler onmouseout; + attribute EventHandler onmouseover; + attribute EventHandler onmouseup; + attribute EventHandler onpause; + attribute EventHandler onplay; + attribute EventHandler onplaying; + attribute EventHandler onprogress; + attribute EventHandler onratechange; + attribute EventHandler onreset; + attribute EventHandler onresize; + attribute EventHandler onscroll; + attribute EventHandler onsecuritypolicyviolation; + attribute EventHandler onseeked; + attribute EventHandler onseeking; + attribute EventHandler onselect; + attribute EventHandler onslotchange; + attribute EventHandler onstalled; + attribute EventHandler onsubmit; + attribute EventHandler onsuspend; + attribute EventHandler ontimeupdate; + attribute EventHandler ontoggle; + attribute EventHandler onvolumechange; + attribute EventHandler onwaiting; + attribute EventHandler onwebkitanimationend; + attribute EventHandler onwebkitanimationiteration; + attribute EventHandler onwebkitanimationstart; + attribute EventHandler onwebkittransitionend; + attribute EventHandler onwheel; + }; diff --git a/Userland/Libraries/LibWeb/DOM/EventListener.h b/Userland/Libraries/LibWeb/DOM/EventListener.h index 1d5705c2b6..377d3899be 100644 --- a/Userland/Libraries/LibWeb/DOM/EventListener.h +++ b/Userland/Libraries/LibWeb/DOM/EventListener.h @@ -38,8 +38,9 @@ class EventListener public: using WrapperType = Bindings::EventListenerWrapper; - explicit EventListener(JS::Handle function) + explicit EventListener(JS::Handle function, bool is_attribute = false) : m_function(move(function)) + , m_attribute(is_attribute) { } @@ -60,6 +61,8 @@ public: bool removed() const { return m_removed; } void set_removed(bool removed) { m_removed = removed; } + bool is_attribute() const { return m_attribute; } + private: FlyString m_type; JS::Handle m_function; @@ -67,6 +70,7 @@ private: bool m_passive { false }; bool m_once { false }; bool m_removed { false }; + bool m_attribute { false }; }; } diff --git a/Userland/Libraries/LibWeb/DOM/EventTarget.h b/Userland/Libraries/LibWeb/DOM/EventTarget.h index f03d4a4fb2..af1a1d7b0c 100644 --- a/Userland/Libraries/LibWeb/DOM/EventTarget.h +++ b/Userland/Libraries/LibWeb/DOM/EventTarget.h @@ -61,6 +61,7 @@ public: NonnullRefPtr listener; }; + Vector& listeners() { return m_listeners; } const Vector& listeners() const { return m_listeners; } Function activation_behaviour; diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 7e8ca5b9da..32ed362c1f 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -62,6 +62,7 @@ enum class QuirksMode; namespace Web::HTML { class CanvasRenderingContext2D; +class EventHandler; class HTMLAnchorElement; class HTMLAreaElement; class HTMLAudioElement; diff --git a/Userland/Libraries/LibWeb/HTML/EventHandler.h b/Userland/Libraries/LibWeb/HTML/EventHandler.h new file mode 100644 index 0000000000..1a6229a2ae --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/EventHandler.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include +#include + +namespace Web::HTML { + +struct EventHandler { + EventHandler() + { + } + + EventHandler(String s) + : string(move(s)) + { + } + + EventHandler(JS::Handle c) + : callback(move(c)) + { + } + + String string; + JS::Handle callback; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.cpp b/Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.cpp new file mode 100644 index 0000000000..46585e80d9 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Web::HTML { + +#undef __ENUMERATE +#define __ENUMERATE(attribute_name, event_name) \ + void GlobalEventHandlers::set_##attribute_name(HTML::EventHandler value) \ + { \ + set_event_handler_attribute(event_name, move(value)); \ + } \ + HTML::EventHandler GlobalEventHandlers::attribute_name() \ + { \ + return get_event_handler_attribute(event_name); \ + } +ENUMERATE_GLOBAL_EVENT_HANDLERS(__ENUMERATE) +#undef __ENUMERATE + +GlobalEventHandlers::~GlobalEventHandlers() +{ +} + +void GlobalEventHandlers::set_event_handler_attribute(const FlyString& name, HTML::EventHandler value) +{ + auto& self = global_event_handlers_to_event_target(); + + RefPtr listener; + if (!value.callback.is_null()) { + listener = adopt(*new DOM::EventListener(move(value.callback))); + } else { + StringBuilder builder; + builder.appendff("function {}(event) {{\n{}\n}}", name, value.string); + auto parser = JS::Parser(JS::Lexer(builder.string_view())); + auto program = parser.parse_function_node(); + if (parser.has_errors()) { + dbgln("Failed to parse script in event handler attribute '{}'", name); + return; + } + auto* function = JS::ScriptFunction::create(self.script_execution_context()->interpreter().global_object(), name, program->body(), program->parameters(), program->function_length(), nullptr, false, false); + ASSERT(function); + listener = adopt(*new DOM::EventListener(JS::make_handle(static_cast(function)))); + } + if (listener) { + for (auto& registered_listener : self.listeners()) { + if (registered_listener.event_name == name && registered_listener.listener->is_attribute()) { + self.remove_event_listener(name, registered_listener.listener); + break; + } + } + self.add_event_listener(name, listener.release_nonnull()); + } +} + +HTML::EventHandler GlobalEventHandlers::get_event_handler_attribute(const FlyString& name) +{ + auto& self = global_event_handlers_to_event_target(); + for (auto& listener : self.listeners()) { + if (listener.event_name == name && listener.listener->is_attribute()) { + return HTML::EventHandler { JS::make_handle(&listener.listener->function()) }; + } + } + + return {}; +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.h b/Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.h index 7cf05ecf4e..cb272d249b 100644 --- a/Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.h +++ b/Userland/Libraries/LibWeb/HTML/GlobalEventHandlers.h @@ -26,6 +26,9 @@ #pragma once +#include +#include + #define ENUMERATE_GLOBAL_EVENT_HANDLERS(E) \ E(onabort, HTML::EventNames::abort) \ E(onauxclick, "auxclick") \ @@ -93,3 +96,25 @@ E(onwebkitanimationstart, "webkitanimationstart") \ E(onwebkittransitionend, "webkittransitionend") \ E(onwheel, "wheel") + +namespace Web::HTML { + +class GlobalEventHandlers { +public: + virtual ~GlobalEventHandlers(); + +#undef __ENUMERATE +#define __ENUMERATE(attribute_name, event_name) \ + void set_##attribute_name(HTML::EventHandler); \ + HTML::EventHandler attribute_name(); + ENUMERATE_GLOBAL_EVENT_HANDLERS(__ENUMERATE) +#undef __ENUMERATE + + void set_event_handler_attribute(const FlyString& name, HTML::EventHandler); + HTML::EventHandler get_event_handler_attribute(const FlyString& name); + +protected: + virtual DOM::EventTarget& global_event_handlers_to_event_target() = 0; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.cpp index e6b7272f8d..4e3277f396 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include namespace Web::HTML { @@ -78,4 +79,10 @@ void HTMLBodyElement::parse_attribute(const FlyString& name, const String& value } } +DOM::EventTarget& HTMLBodyElement::global_event_handlers_to_event_target() +{ + // NOTE: This is a little weird, but IIUC document.body.onload actually refers to window.onload + return document().window(); +} + } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.h b/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.h index d8d41cbb7b..e5e169621a 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLBodyElement.h @@ -41,6 +41,9 @@ public: virtual void apply_presentational_hints(CSS::StyleProperties&) const override; private: + // ^HTML::GlobalEventHandlers + virtual EventTarget& global_event_handlers_to_event_target() override; + RefPtr m_background_style_value; }; diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp index eb5356a594..ac09cb1d7a 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp @@ -25,11 +25,17 @@ */ #include +#include +#include +#include #include +#include +#include #include #include #include #include +#include namespace Web::HTML { @@ -138,4 +144,17 @@ bool HTMLElement::cannot_navigate() const return !is(this) && !is_connected(); } +void HTMLElement::parse_attribute(const FlyString& name, const String& value) +{ + Element::parse_attribute(name, value); + +#undef __ENUMERATE +#define __ENUMERATE(attribute_name, event_name) \ + if (name == HTML::AttributeNames::attribute_name) { \ + set_event_handler_attribute(event_name, EventHandler { value }); \ + } + ENUMERATE_GLOBAL_EVENT_HANDLERS(__ENUMERATE) +#undef __ENUMERATE +} + } diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.h b/Userland/Libraries/LibWeb/HTML/HTMLElement.h index ee602a62b5..b070c13e49 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.h @@ -27,10 +27,14 @@ #pragma once #include +#include +#include namespace Web::HTML { -class HTMLElement : public DOM::Element { +class HTMLElement + : public DOM::Element + , public HTML::GlobalEventHandlers { public: using WrapperType = Bindings::HTMLElementWrapper; @@ -48,7 +52,13 @@ public: bool cannot_navigate() const; +protected: + virtual void parse_attribute(const FlyString& name, const String& value) override; + private: + // ^HTML::GlobalEventHandlers + virtual EventTarget& global_event_handlers_to_event_target() override { return *this; } + enum class ContentEditableState { True, False, diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLElement.idl index 6b5de34cdb..5369c9a3cd 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLElement.idl +++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.idl @@ -8,4 +8,76 @@ interface HTMLElement : Element { attribute DOMString contentEditable; [LegacyNullToEmptyString] attribute DOMString innerText; + + // FIXME: These should all come from a GlobalEventHandlers mixin + attribute EventHandler onabort; + attribute EventHandler onauxclick; + attribute EventHandler onblur; + attribute EventHandler oncancel; + attribute EventHandler oncanplay; + attribute EventHandler oncanplaythrough; + attribute EventHandler onchange; + attribute EventHandler onclick; + attribute EventHandler onclose; + attribute EventHandler oncontextmenu; + attribute EventHandler oncuechange; + attribute EventHandler ondblclick; + attribute EventHandler ondrag; + attribute EventHandler ondragend; + attribute EventHandler ondragenter; + attribute EventHandler ondragleave; + attribute EventHandler ondragover; + attribute EventHandler ondragstart; + attribute EventHandler ondrop; + attribute EventHandler ondurationchange; + attribute EventHandler onemptied; + attribute EventHandler onended; + + // FIXME: Should be an OnErrorEventHandler + attribute EventHandler onerror; + + attribute EventHandler onfocus; + attribute EventHandler onformdata; + attribute EventHandler oninput; + attribute EventHandler oninvalid; + attribute EventHandler onkeydown; + attribute EventHandler onkeypress; + attribute EventHandler onkeyup; + attribute EventHandler onload; + attribute EventHandler onloadeddata; + attribute EventHandler onloadedmetadata; + attribute EventHandler onloadstart; + attribute EventHandler onmousedown; + [LegacyLenientThis] attribute EventHandler onmouseenter; + [LegacyLenientThis] attribute EventHandler onmouseleave; + attribute EventHandler onmousemove; + attribute EventHandler onmouseout; + attribute EventHandler onmouseover; + attribute EventHandler onmouseup; + attribute EventHandler onpause; + attribute EventHandler onplay; + attribute EventHandler onplaying; + attribute EventHandler onprogress; + attribute EventHandler onratechange; + attribute EventHandler onreset; + attribute EventHandler onresize; + attribute EventHandler onscroll; + attribute EventHandler onsecuritypolicyviolation; + attribute EventHandler onseeked; + attribute EventHandler onseeking; + attribute EventHandler onselect; + attribute EventHandler onslotchange; + attribute EventHandler onstalled; + attribute EventHandler onsubmit; + attribute EventHandler onsuspend; + attribute EventHandler ontimeupdate; + attribute EventHandler ontoggle; + attribute EventHandler onvolumechange; + attribute EventHandler onwaiting; + attribute EventHandler onwebkitanimationend; + attribute EventHandler onwebkitanimationiteration; + attribute EventHandler onwebkitanimationstart; + attribute EventHandler onwebkittransitionend; + attribute EventHandler onwheel; + };