diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp index 436bf956beb..89cc66fcfc3 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp @@ -1223,6 +1223,7 @@ void generate_prototype_implementation(IDL::Interface const& interface) #include #include #include +#include #include #include #include diff --git a/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h b/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h index d39375510ed..002008b6972 100644 --- a/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h +++ b/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h @@ -8,6 +8,10 @@ // FIXME: Find a way to generate all of this +#include +#include +#include +#include #include #include #include @@ -239,6 +243,8 @@ #define ADD_WINDOW_OBJECT_INTERFACES \ auto& vm = this->vm(); \ + ADD_WINDOW_OBJECT_INTERFACE(AbortController) \ + ADD_WINDOW_OBJECT_INTERFACE(AbortSignal) \ ADD_WINDOW_OBJECT_INTERFACE(CanvasRenderingContext2D) \ ADD_WINDOW_OBJECT_INTERFACE(CharacterData) \ ADD_WINDOW_OBJECT_INTERFACE(CloseEvent) \ diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 0009eb7c58f..4afd6dba2ae 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -38,6 +38,8 @@ set(SOURCES CSS/ValueID.cpp CSS/ValueID.h Cookie/ParsedCookie.cpp + DOM/AbortController.cpp + DOM/AbortSignal.cpp DOM/CharacterData.cpp DOM/CharacterData.idl DOM/Comment.cpp @@ -299,6 +301,8 @@ libweb_js_wrapper(CSS/CSSStyleSheet) libweb_js_wrapper(CSS/Screen) libweb_js_wrapper(CSS/StyleSheet) libweb_js_wrapper(CSS/StyleSheetList) +libweb_js_wrapper(DOM/AbortController) +libweb_js_wrapper(DOM/AbortSignal) libweb_js_wrapper(DOM/CharacterData) libweb_js_wrapper(DOM/Comment) libweb_js_wrapper(DOM/Document) diff --git a/Userland/Libraries/LibWeb/DOM/AbortController.cpp b/Userland/Libraries/LibWeb/DOM/AbortController.cpp new file mode 100644 index 00000000000..4f3450c4a8b --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/AbortController.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Web::DOM { + +// https://dom.spec.whatwg.org/#dom-abortcontroller-abortcontroller +AbortController::AbortController(Document& document) + : m_signal(AbortSignal::create(document)) +{ +} + +AbortController::~AbortController() +{ +} + +// https://dom.spec.whatwg.org/#dom-abortcontroller-abort +void AbortController::abort() +{ + m_signal->signal_abort(); +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/AbortController.h b/Userland/Libraries/LibWeb/DOM/AbortController.h new file mode 100644 index 00000000000..3e9d919978c --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/AbortController.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace Web::DOM { + +// https://dom.spec.whatwg.org/#abortcontroller +class AbortController final + : public RefCounted + , public Weakable + , public Bindings::Wrappable { +public: + using WrapperType = Bindings::AbortControllerWrapper; + + static NonnullRefPtr create(Document& document) + { + return adopt_ref(*new AbortController(document)); + } + + static NonnullRefPtr create_with_global_object(Bindings::WindowObject& window_object) + { + return AbortController::create(window_object.impl().document()); + } + + virtual ~AbortController() override; + + // https://dom.spec.whatwg.org/#dom-abortcontroller-signal + NonnullRefPtr signal() const { return m_signal; } + + void abort(); + +private: + AbortController(Document& document); + + // https://dom.spec.whatwg.org/#abortcontroller-signal + NonnullRefPtr m_signal; +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/AbortController.idl b/Userland/Libraries/LibWeb/DOM/AbortController.idl new file mode 100644 index 00000000000..4877c9295e8 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/AbortController.idl @@ -0,0 +1,8 @@ +[Exposed=(Window,Worker)] +interface AbortController { + constructor(); + + [SameObject] readonly attribute AbortSignal signal; + + undefined abort(); +}; diff --git a/Userland/Libraries/LibWeb/DOM/AbortSignal.cpp b/Userland/Libraries/LibWeb/DOM/AbortSignal.cpp new file mode 100644 index 00000000000..deda4a93e8e --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/AbortSignal.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace Web::DOM { + +AbortSignal::AbortSignal(Document& document) + : EventTarget(static_cast(document)) +{ +} + +AbortSignal::~AbortSignal() +{ +} + +bool AbortSignal::dispatch_event(NonnullRefPtr event) +{ + return EventDispatcher::dispatch(*this, event); +} + +JS::Object* AbortSignal::create_wrapper(JS::GlobalObject& global_object) +{ + return wrap(global_object, *this); +} + +// https://dom.spec.whatwg.org/#abortsignal-add +void AbortSignal::add_abort_algorithm(Function abort_algorithm) +{ + if (m_aborted) + return; + + m_abort_algorithms.append(move(abort_algorithm)); +} + +// https://dom.spec.whatwg.org/#abortsignal-signal-abort +void AbortSignal::signal_abort() +{ + if (m_aborted) + return; + + m_aborted = true; + + for (auto& algorithm : m_abort_algorithms) + algorithm(); + + m_abort_algorithms.clear(); + + dispatch_event(Event::create(HTML::EventNames::abort)); +} + +} diff --git a/Userland/Libraries/LibWeb/DOM/AbortSignal.h b/Userland/Libraries/LibWeb/DOM/AbortSignal.h new file mode 100644 index 00000000000..273d28981ab --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/AbortSignal.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2021, Luke Wilde + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Web::DOM { + +// https://dom.spec.whatwg.org/#abortsignal +class AbortSignal final + : public RefCounted + , public Weakable + , public EventTarget + , public Bindings::Wrappable { +public: + using WrapperType = Bindings::AbortSignalWrapper; + + using RefCounted::ref; + using RefCounted::unref; + + static NonnullRefPtr create(Document& document) + { + return adopt_ref(*new AbortSignal(document)); + } + + static NonnullRefPtr create_with_global_object(Bindings::WindowObject& window_object) + { + return AbortSignal::create(window_object.impl().document()); + } + + virtual ~AbortSignal() override; + + void add_abort_algorithm(Function); + + // https://dom.spec.whatwg.org/#dom-abortsignal-aborted + bool aborted() const { return m_aborted; } + + void signal_abort(); + + // ^EventTarget + virtual void ref_event_target() override { ref(); } + virtual void unref_event_target() override { unref(); } + virtual bool dispatch_event(NonnullRefPtr) override; + virtual JS::Object* create_wrapper(JS::GlobalObject&) override; + +private: + AbortSignal(Document& document); + + // https://dom.spec.whatwg.org/#abortsignal-aborted-flag + bool m_aborted { false }; + + // https://dom.spec.whatwg.org/#abortsignal-abort-algorithms + // FIXME: This should be a set. + Vector> m_abort_algorithms; +}; + +} diff --git a/Userland/Libraries/LibWeb/DOM/AbortSignal.idl b/Userland/Libraries/LibWeb/DOM/AbortSignal.idl new file mode 100644 index 00000000000..6bb9cd81a59 --- /dev/null +++ b/Userland/Libraries/LibWeb/DOM/AbortSignal.idl @@ -0,0 +1,8 @@ +[Exposed=(Window,Worker)] +interface AbortSignal : EventTarget { + // FIXME: [NewObject] static AbortSignal abort(); + + readonly attribute boolean aborted; + + // FIXME: attribute EventHandler onabort; +}; diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 3954b2dcfe2..160de93cf6d 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -32,6 +32,8 @@ enum class Display; } namespace Web::DOM { +class AbortController; +class AbortSignal; class CharacterData; class Comment; class Document; @@ -203,6 +205,8 @@ class XMLHttpRequestEventTarget; } namespace Web::Bindings { +class AbortControllerWrapper; +class AbortSignalWrapper; class CSSStyleDeclarationWrapper; class CSSStyleSheetWrapper; class CanvasRenderingContext2DWrapper; diff --git a/Userland/Libraries/LibWeb/Tests/DOM/AbortController.js b/Userland/Libraries/LibWeb/Tests/DOM/AbortController.js new file mode 100644 index 00000000000..44c77443a62 --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/DOM/AbortController.js @@ -0,0 +1,20 @@ +describe("AbortController", () => { + loadLocalPage("/res/html/misc/blank.html"); + + afterInitialPageLoad(page => { + test("Basic functionality", () => { + const abortController = new page.AbortController(); + let timesCallbackCalled = 0; + abortController.signal.addEventListener("abort", () => { + timesCallbackCalled++; + }); + + abortController.abort(); + expect(abortController.signal.aborted).toBeTrue(); + + abortController.abort(); + expect(timesCallbackCalled).toBe(1); + }); + }); + waitForPageToLoad(); +});