From b216046234560df531e1a32269e5dfa18f8f240c Mon Sep 17 00:00:00 2001 From: Luke Warlow Date: Fri, 21 Jun 2024 22:53:05 +0100 Subject: [PATCH] LibWeb: Implement CloseWatcher API This implements most of the CloseWatcher API from the html spec. AbortSignal support is unimplemented. Integration with dialogs and popovers is also unimplemented. --- .../BindingsGenerator/IDLGenerators.cpp | 1 + .../Userland/Libraries/LibWeb/HTML/BUILD.gn | 2 + .../Userland/Libraries/LibWeb/idl_files.gni | 1 + Userland/Libraries/LibWeb/CMakeLists.txt | 2 + Userland/Libraries/LibWeb/DOM/EventTarget.cpp | 13 +- Userland/Libraries/LibWeb/Forward.h | 2 + .../Libraries/LibWeb/HTML/CloseWatcher.cpp | 167 ++++++++++++++++++ Userland/Libraries/LibWeb/HTML/CloseWatcher.h | 49 +++++ .../Libraries/LibWeb/HTML/CloseWatcher.idl | 19 ++ .../LibWeb/HTML/CloseWatcherManager.cpp | 120 +++++++++++++ .../LibWeb/HTML/CloseWatcherManager.h | 40 +++++ Userland/Libraries/LibWeb/HTML/Window.cpp | 12 ++ Userland/Libraries/LibWeb/HTML/Window.h | 2 + .../Libraries/LibWeb/Page/EventHandler.cpp | 4 + Userland/Libraries/LibWeb/idl_files.cmake | 1 + 15 files changed, 432 insertions(+), 3 deletions(-) create mode 100644 Userland/Libraries/LibWeb/HTML/CloseWatcher.cpp create mode 100644 Userland/Libraries/LibWeb/HTML/CloseWatcher.h create mode 100644 Userland/Libraries/LibWeb/HTML/CloseWatcher.idl create mode 100644 Userland/Libraries/LibWeb/HTML/CloseWatcherManager.cpp create mode 100644 Userland/Libraries/LibWeb/HTML/CloseWatcherManager.h diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp index cd1e767b187..b38e011f586 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp @@ -42,6 +42,7 @@ static bool is_platform_object(Type const& type) "CanvasGradient"sv, "CanvasPattern"sv, "CanvasRenderingContext2D"sv, + "CloseWatcher"sv, "CryptoKey"sv, "Document"sv, "DocumentType"sv, diff --git a/Meta/gn/secondary/Userland/Libraries/LibWeb/HTML/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibWeb/HTML/BUILD.gn index 60c739e390e..357e360fdf4 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibWeb/HTML/BUILD.gn +++ b/Meta/gn/secondary/Userland/Libraries/LibWeb/HTML/BUILD.gn @@ -22,6 +22,8 @@ source_set("HTML") { "CanvasPattern.cpp", "CanvasRenderingContext2D.cpp", "CloseEvent.cpp", + "CloseWatcher.cpp", + "CloseWatcherManager.cpp", "DOMParser.cpp", "DOMStringMap.cpp", "DataTransfer.cpp", diff --git a/Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni b/Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni index ea7b67aa70d..0220a12cd0f 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni +++ b/Meta/gn/secondary/Userland/Libraries/LibWeb/idl_files.gni @@ -114,6 +114,7 @@ standard_idl_files = [ "//Userland/Libraries/LibWeb/HTML/CanvasPattern.idl", "//Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.idl", "//Userland/Libraries/LibWeb/HTML/CloseEvent.idl", + "//Userland/Libraries/LibWeb/HTML/CloseWatcher.idl", "//Userland/Libraries/LibWeb/HTML/CustomElements/CustomElementRegistry.idl", "//Userland/Libraries/LibWeb/HTML/DataTransfer.idl", "//Userland/Libraries/LibWeb/HTML/DOMParser.idl", diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 4ec11ffd603..68a8a1cafa1 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -259,6 +259,8 @@ set(SOURCES HTML/CanvasPattern.cpp HTML/CanvasRenderingContext2D.cpp HTML/CloseEvent.cpp + HTML/CloseWatcher.cpp + HTML/CloseWatcherManager.cpp HTML/CORSSettingAttribute.cpp HTML/CrossOrigin/AbstractOperations.cpp HTML/CrossOrigin/Reporting.cpp diff --git a/Userland/Libraries/LibWeb/DOM/EventTarget.cpp b/Userland/Libraries/LibWeb/DOM/EventTarget.cpp index f157b495eab..44010ef81d7 100644 --- a/Userland/Libraries/LibWeb/DOM/EventTarget.cpp +++ b/Userland/Libraries/LibWeb/DOM/EventTarget.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -796,7 +797,9 @@ bool EventTarget::dispatch_event(Event& event) // FIXME: 3. Extend windows with the active window of each of document's ancestor navigables. // FIXME: 4. Extend windows with the active window of each of document's descendant navigables, // filtered to include only those navigables whose active document's origin is same origin with document's origin. - // FIXME: 5. For each window in windows, set window's last activation timestamp to the current high resolution time. + // FIXME: 5. For each window in windows: + // FIXME: 5.1 Set window's last activation timestamp to the current high resolution time. + // FIXME: 5.2 Notify the close watcher manager about user activation given window. // FIXME: This is ad-hoc, but works for now. if (is_activation_triggering_input_event()) { @@ -804,11 +807,15 @@ bool EventTarget::dispatch_event(Event& event) auto current_time = HighResolutionTime::relative_high_resolution_time(unsafe_shared_time, realm().global_object()); if (is(this)) { - static_cast(this)->set_last_activation_timestamp(current_time); + auto* window = static_cast(this); + window->set_last_activation_timestamp(current_time); + window->close_watcher_manager()->notify_about_user_activation(); } else if (is(this)) { auto const* element = static_cast(this); - if (auto window = element->document().window()) + if (auto window = element->document().window()) { window->set_last_activation_timestamp(current_time); + window->close_watcher_manager()->notify_about_user_activation(); + } } } diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index b17c9ba688c..43ac60b0be6 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -344,6 +344,8 @@ class BrowsingContextGroup; class CanvasRenderingContext2D; class ClassicScript; class CloseEvent; +class CloseWatcher; +class CloseWatcherManager; class CustomElementDefinition; class CustomElementRegistry; class DecodedImageData; diff --git a/Userland/Libraries/LibWeb/HTML/CloseWatcher.cpp b/Userland/Libraries/LibWeb/HTML/CloseWatcher.cpp new file mode 100644 index 00000000000..fbe88e85a08 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/CloseWatcher.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2024, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Web::HTML { + +JS_DEFINE_ALLOCATOR(CloseWatcher); + +// https://html.spec.whatwg.org/multipage/interaction.html#establish-a-close-watcher +JS::NonnullGCPtr CloseWatcher::establish(HTML::Window& window) +{ + // 1. Assert: window's associated Document is fully active. + VERIFY(window.associated_document().is_fully_active()); + + // 2. Let closeWatcher be a new close watcher + auto close_watcher = window.heap().allocate(window.realm(), window.realm()); + + // 3. Let manager be window's associated close watcher manager + auto manager = window.close_watcher_manager(); + + // 4 - 6. Moved to CloseWatcherManager::add + manager->add(close_watcher); + + // 7. Return close_watcher. + return close_watcher; +} + +// https://html.spec.whatwg.org/multipage/interaction.html#dom-closewatcher +WebIDL::ExceptionOr> CloseWatcher::construct_impl(JS::Realm& realm, CloseWatcherOptions const& options) +{ + // 1. If this's relevant global object's associated Document is not fully active, then return an "InvalidStateError" DOMException. + // FIXME: Not in spec explicitly, but this should account for detached iframes too. See /close-watcher/frame-removal.html WPT. + auto& window = verify_cast(realm.global_object()); + if (!window.associated_document().is_fully_active()) + return WebIDL::InvalidStateError::create(realm, "The document is not fully active."_fly_string); + + // 2. Let close_watcher be the result of establishing a close watcher + auto close_watcher = establish(window); + + // 3. If options["signal"] exists, then: + if (options.signal) { + // FIXME: 3.1 If options["signal"]'s aborted, then destroy closeWatcher. + // FIXME: 3.2 Add the following steps to options["signal"]: + } + + return close_watcher; +} + +CloseWatcher::CloseWatcher(JS::Realm& realm) + : DOM::EventTarget(realm) +{ +} + +// https://html.spec.whatwg.org/multipage/interaction.html#close-watcher-request-close +bool CloseWatcher::request_close() +{ + // 1. If closeWatcher is not active, then return. + if (!m_is_active) + return true; + + // 2. If closeWatcher's is running cancel action is true, then return true. + if (m_is_running_cancel_action) + return true; + + // 3. Let window be closeWatcher's window. + auto& window = verify_cast(realm().global_object()); + + // 4. If window's associated Document is not fully active, then return true. + if (!window.associated_document().is_fully_active()) + return true; + + // 5. Let canPreventClose be true if window's close watcher manager's groups's size is less than window's close watcher manager's allowed number of groups, + // and window has history-action activation; otherwise false. + auto manager = window.close_watcher_manager(); + bool can_prevent_close = manager->can_prevent_close() && window.has_history_action_activation(); + // 6. Set closeWatcher's is running cancel action to true. + m_is_running_cancel_action = true; + // 7. Let shouldContinue be the result of running closeWatcher's cancel action given canPreventClose. + bool should_continue = dispatch_event(DOM::Event::create(realm(), HTML::EventNames::cancel, { .cancelable = can_prevent_close })); + // 8. Set closeWatcher's is running cancel action to false. + m_is_running_cancel_action = false; + // 9. If shouldContinue is false, then: + if (!should_continue) { + // 9.1 Assert: canPreventClose is true. + VERIFY(can_prevent_close); + // 9.2 Consume history-action user activation given window. + window.consume_history_action_user_activation(); + return false; + } + + // 10. Close closeWatcher. + close(); + + // 11. Return true. + return true; +} + +// https://html.spec.whatwg.org/multipage/interaction.html#close-watcher-close +void CloseWatcher::close() +{ + // 1. If closeWatcher is not active, then return. + if (!m_is_active) + return; + + // 2. If closeWatcher's window's associated Document is not fully active, then return. + if (!verify_cast(realm().global_object()).associated_document().is_fully_active()) + return; + + // 3. Destroy closeWatcher. + destroy(); + + // 4. Run closeWatcher's close action. + dispatch_event(DOM::Event::create(realm(), HTML::EventNames::close)); +} + +// https://html.spec.whatwg.org/multipage/interaction.html#close-watcher-destroy +void CloseWatcher::destroy() +{ + // 1. Let manager be closeWatcher's window's close watcher manager. + auto manager = verify_cast(realm().global_object()).close_watcher_manager(); + + // 2-3. Moved to CloseWatcherManager::remove + manager->remove(*this); + + m_is_active = false; +} + +void CloseWatcher::initialize(JS::Realm& realm) +{ + Base::initialize(realm); + WEB_SET_PROTOTYPE_FOR_INTERFACE(CloseWatcher); +} + +void CloseWatcher::set_oncancel(WebIDL::CallbackType* event_handler) +{ + set_event_handler_attribute(HTML::EventNames::cancel, event_handler); +} + +WebIDL::CallbackType* CloseWatcher::oncancel() +{ + return event_handler_attribute(HTML::EventNames::cancel); +} + +void CloseWatcher::set_onclose(WebIDL::CallbackType* event_handler) +{ + set_event_handler_attribute(HTML::EventNames::close, event_handler); +} + +WebIDL::CallbackType* CloseWatcher::onclose() +{ + return event_handler_attribute(HTML::EventNames::close); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/CloseWatcher.h b/Userland/Libraries/LibWeb/HTML/CloseWatcher.h new file mode 100644 index 00000000000..4037ad02895 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/CloseWatcher.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Web::HTML { + +// https://html.spec.whatwg.org/multipage/interaction.html#closewatcheroptions +struct CloseWatcherOptions { + JS::GCPtr signal; +}; + +// https://html.spec.whatwg.org/multipage/interaction.html#the-closewatcher-interface +class CloseWatcher final : public DOM::EventTarget { + WEB_PLATFORM_OBJECT(CloseWatcher, DOM::EventTarget); + JS_DECLARE_ALLOCATOR(CloseWatcher); + +public: + static WebIDL::ExceptionOr> construct_impl(JS::Realm&, CloseWatcherOptions const& = {}); + + bool request_close(); + void close(); + void destroy(); + + virtual ~CloseWatcher() override = default; + + void set_oncancel(WebIDL::CallbackType*); + WebIDL::CallbackType* oncancel(); + + void set_onclose(WebIDL::CallbackType*); + WebIDL::CallbackType* onclose(); + +private: + CloseWatcher(JS::Realm&); + [[nodiscard]] static JS::NonnullGCPtr establish(HTML::Window&); + + virtual void initialize(JS::Realm&) override; + + bool m_is_running_cancel_action { false }; + bool m_is_active { true }; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/CloseWatcher.idl b/Userland/Libraries/LibWeb/HTML/CloseWatcher.idl new file mode 100644 index 00000000000..e050f67883a --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/CloseWatcher.idl @@ -0,0 +1,19 @@ +#import +#import + +// https://html.spec.whatwg.org/multipage/interaction.html#closewatcher +[Exposed=Window] +interface CloseWatcher : EventTarget { + constructor(optional CloseWatcherOptions options = {}); + + undefined requestClose(); + undefined close(); + undefined destroy(); + + attribute EventHandler oncancel; + attribute EventHandler onclose; +}; + +dictionary CloseWatcherOptions { + AbortSignal signal; +}; diff --git a/Userland/Libraries/LibWeb/HTML/CloseWatcherManager.cpp b/Userland/Libraries/LibWeb/HTML/CloseWatcherManager.cpp new file mode 100644 index 00000000000..3b1d66e2f6e --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/CloseWatcherManager.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2024, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace Web::HTML { + +JS_DEFINE_ALLOCATOR(CloseWatcherManager); + +JS::NonnullGCPtr CloseWatcherManager::create(JS::Realm& realm) +{ + return realm.heap().allocate(realm, realm); +} + +CloseWatcherManager::CloseWatcherManager(JS::Realm& realm) + : PlatformObject(realm) +{ +} + +void CloseWatcherManager::add(JS::NonnullGCPtr close_watcher) +{ + // If manager's groups's size is less than manager's allowed number of groups + if (m_groups.size() < m_allowed_number_of_groups) { + // then append « closeWatcher » to manager's groups. + JS::MarkedVector> new_group(realm().heap()); + new_group.append(close_watcher); + m_groups.append(move(new_group)); + } else { + // Assert: manager's groups's size is at least 1 in this branch, since manager's allowed number of groups is always at least 1. + VERIFY(!m_groups.is_empty()); + // Append closeWatcher to manager's groups's last item. + m_groups.last().append(close_watcher); + } + + // Set manager's next user interaction allows a new group to true. + m_next_user_interaction_allows_a_new_group = true; +} + +void CloseWatcherManager::remove(CloseWatcher const& close_watcher) +{ + // 2. For each group of manager's groups: remove closeWatcher from group + for (auto& group : m_groups) { + group.remove_first_matching([&close_watcher](JS::NonnullGCPtr& entry) { + return entry.ptr() == &close_watcher; + }); + } + // 3. Remove any item from manager's group that is empty + m_groups.remove_all_matching([](auto& group) { return group.is_empty(); }); +} + +// https://html.spec.whatwg.org/multipage/interaction.html#process-close-watchers +bool CloseWatcherManager::process_close_watchers() +{ + // 1. Let processedACloseWatcher be false. + bool processed_a_close_watcher = false; + // 2. If window's close watcher manager's groups is not empty: + if (!m_groups.is_empty()) { + // 2.1 Let group be the last item in window's close watcher manager's groups. + auto& group = m_groups.last(); + // Ambiguous spec wording. We copy the groups to avoid modifying the original while iterating. + // See https://github.com/whatwg/html/issues/10240 + JS::MarkedVector> group_copy(realm().heap()); + group_copy.ensure_capacity(group.size()); + for (auto& close_watcher : group) { + group_copy.append(close_watcher); + } + // 2.2 For each closeWatcher of group, in reverse order: + for (auto it = group_copy.rbegin(); it != group_copy.rend(); ++it) { + // 2.1.1 Set processedACloseWatcher to true. + processed_a_close_watcher = true; + // 2.1.2 Let shouldProceed be the result of requesting to close closeWatcher. + bool should_proceed = (*it)->request_close(); + // 2.1.3 If shouldProceed is false, then break; + if (!should_proceed) + break; + } + } + // 3. If window's close watcher manager's allowed number of groups is greater than 1, decrement it by 1. + if (m_allowed_number_of_groups > 1) + m_allowed_number_of_groups--; + + // 4. Return processedACloseWatcher. + return processed_a_close_watcher; +} + +// https://html.spec.whatwg.org/multipage/interaction.html#notify-the-close-watcher-manager-about-user-activation +void CloseWatcherManager::notify_about_user_activation() +{ + // 1. Let manager be window's close watcher manager. + // 2. If manager's next user interaction allows a new group is true, then increment manager's allowed number of groups. + if (m_next_user_interaction_allows_a_new_group) + m_allowed_number_of_groups++; + // 3. Set manager's next user interaction allows a new group to false. + m_next_user_interaction_allows_a_new_group = false; +} + +// https://html.spec.whatwg.org/multipage/interaction.html#close-watcher-request-close +bool CloseWatcherManager::can_prevent_close() +{ + // 5. Let canPreventClose be true if window's close watcher manager's groups's size is less than window's close watcher manager's allowed number of groups... + return m_groups.size() < m_allowed_number_of_groups; +} + +void CloseWatcherManager::visit_edges(JS::Cell::Visitor& visitor) +{ + Base::visit_edges(visitor); + + visitor.visit(m_groups); +} + +} diff --git a/Userland/Libraries/LibWeb/HTML/CloseWatcherManager.h b/Userland/Libraries/LibWeb/HTML/CloseWatcherManager.h new file mode 100644 index 00000000000..a08cdd96e61 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/CloseWatcherManager.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024, the Ladybird developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Web::HTML { + +// https://html.spec.whatwg.org/multipage/interaction.html#close-watcher-manager +class CloseWatcherManager final : public Bindings::PlatformObject { + WEB_PLATFORM_OBJECT(CloseWatcherManager, Bindings::PlatformObject); + JS_DECLARE_ALLOCATOR(CloseWatcherManager); + +public: + [[nodiscard]] static JS::NonnullGCPtr create(JS::Realm&); + + void add(JS::NonnullGCPtr); + void remove(CloseWatcher const&); + + bool process_close_watchers(); + + void notify_about_user_activation(); + bool can_prevent_close(); + +private: + explicit CloseWatcherManager(JS::Realm&); + + virtual void visit_edges(Cell::Visitor&) override; + + Vector>> m_groups; + uint32_t m_allowed_number_of_groups { 1 }; + bool m_next_user_interaction_allows_a_new_group { true }; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/Window.cpp b/Userland/Libraries/LibWeb/HTML/Window.cpp index 41a27df7bcf..27989696fd4 100644 --- a/Userland/Libraries/LibWeb/HTML/Window.cpp +++ b/Userland/Libraries/LibWeb/HTML/Window.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -129,6 +130,7 @@ void Window::visit_edges(JS::Cell::Visitor& visitor) visitor.visit(m_pdf_viewer_mime_type_objects); visitor.visit(m_count_queuing_strategy_size_function); visitor.visit(m_byte_length_queuing_strategy_size_function); + visitor.visit(m_close_watcher_manager); } void Window::finalize() @@ -973,6 +975,16 @@ JS::NonnullGCPtr Window::navigator() return JS::NonnullGCPtr { *m_navigator }; } +// https://html.spec.whatwg.org/multipage/interaction.html#close-watcher-manager +JS::NonnullGCPtr Window::close_watcher_manager() +{ + auto& realm = this->realm(); + + if (!m_close_watcher_manager) + m_close_watcher_manager = heap().allocate(realm, realm); + return JS::NonnullGCPtr { *m_close_watcher_manager }; +} + // https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#dom-alert void Window::alert(String const& message) { diff --git a/Userland/Libraries/LibWeb/HTML/Window.h b/Userland/Libraries/LibWeb/HTML/Window.h index 01931ad943e..c6fa403d847 100644 --- a/Userland/Libraries/LibWeb/HTML/Window.h +++ b/Userland/Libraries/LibWeb/HTML/Window.h @@ -162,6 +162,7 @@ public: WebIDL::ExceptionOr> open(Optional const& url, Optional const& target, Optional const& features); [[nodiscard]] JS::NonnullGCPtr navigator(); + [[nodiscard]] JS::NonnullGCPtr close_watcher_manager(); void alert(String const& message = {}); bool confirm(Optional const& message); @@ -269,6 +270,7 @@ private: JS::GCPtr m_screen; JS::GCPtr m_navigator; JS::GCPtr m_location; + JS::GCPtr m_close_watcher_manager; // https://html.spec.whatwg.org/multipage/nav-history-apis.html#window-navigation-api JS::GCPtr m_navigation; diff --git a/Userland/Libraries/LibWeb/Page/EventHandler.cpp b/Userland/Libraries/LibWeb/Page/EventHandler.cpp index 0bebc268313..552d70098bb 100644 --- a/Userland/Libraries/LibWeb/Page/EventHandler.cpp +++ b/Userland/Libraries/LibWeb/Page/EventHandler.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -778,6 +779,9 @@ bool EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u32 code return focus_next_element(); } + if (key == UIEvents::KeyCode::Key_Escape) + return document->window()->close_watcher_manager()->process_close_watchers(); + auto& realm = document->realm(); if (auto selection = document->get_selection()) { diff --git a/Userland/Libraries/LibWeb/idl_files.cmake b/Userland/Libraries/LibWeb/idl_files.cmake index 21b205765e8..f0e42439044 100644 --- a/Userland/Libraries/LibWeb/idl_files.cmake +++ b/Userland/Libraries/LibWeb/idl_files.cmake @@ -98,6 +98,7 @@ libweb_js_bindings(HTML/CanvasGradient) libweb_js_bindings(HTML/CanvasPattern) libweb_js_bindings(HTML/CanvasRenderingContext2D) libweb_js_bindings(HTML/CloseEvent) +libweb_js_bindings(HTML/CloseWatcher) libweb_js_bindings(HTML/CustomElements/CustomElementRegistry) libweb_js_bindings(HTML/DOMParser) libweb_js_bindings(HTML/DOMStringMap)