diff --git a/AK/Platform.h b/AK/Platform.h index 1000e7f4724..2aefdda7122 100644 --- a/AK/Platform.h +++ b/AK/Platform.h @@ -276,11 +276,13 @@ # define ASAN_UNPOISON_MEMORY_REGION(addr, size) __asan_unpoison_memory_region(addr, size) # define LSAN_REGISTER_ROOT_REGION(base, size) __lsan_register_root_region(base, size) # define LSAN_UNREGISTER_ROOT_REGION(base, size) __lsan_unregister_root_region(base, size) +# define LSAN_IGNORE_OBJECT(base) __lsan_ignore_object(base) #else # define ASAN_POISON_MEMORY_REGION(addr, size) # define ASAN_UNPOISON_MEMORY_REGION(addr, size) # define LSAN_REGISTER_ROOT_REGION(base, size) # define LSAN_UNREGISTER_ROOT_REGION(base, size) +# define LSAN_IGNORE_OBJECT(base) #endif #if __has_feature(blocks) diff --git a/Libraries/LibGC/CMakeLists.txt b/Libraries/LibGC/CMakeLists.txt index c89319960ad..378f0790b52 100644 --- a/Libraries/LibGC/CMakeLists.txt +++ b/Libraries/LibGC/CMakeLists.txt @@ -9,6 +9,7 @@ set(SOURCES RootVector.cpp Heap.cpp HeapBlock.cpp + WeakBlock.cpp WeakContainer.cpp ) diff --git a/Libraries/LibGC/Forward.h b/Libraries/LibGC/Forward.h index b531242a68e..ddf12dc528a 100644 --- a/Libraries/LibGC/Forward.h +++ b/Libraries/LibGC/Forward.h @@ -20,6 +20,7 @@ class Heap; class HeapBlock; class NanBoxedValue; class WeakContainer; +class WeakImpl; template class Function; diff --git a/Libraries/LibGC/Heap.cpp b/Libraries/LibGC/Heap.cpp index 0c258537781..fe661af0068 100644 --- a/Libraries/LibGC/Heap.cpp +++ b/Libraries/LibGC/Heap.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022, Andreas Kling + * Copyright (c) 2020-2025, Andreas Kling * Copyright (c) 2023, Aliaksandr Kalenik * * SPDX-License-Identifier: BSD-2-Clause @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #ifdef HAS_ADDRESS_SANITIZER @@ -258,6 +260,7 @@ void Heap::collect_garbage(CollectionType collection_type, bool print_report) mark_live_cells(roots); } finalize_unmarked_cells(); + sweep_weak_blocks(); sweep_dead_cells(print_report, collection_measurement_timer); } @@ -462,6 +465,22 @@ void Heap::finalize_unmarked_cells() }); } +void Heap::sweep_weak_blocks() +{ + for (auto& weak_block : m_usable_weak_blocks) { + weak_block.sweep(); + } + Vector now_usable_weak_blocks; + for (auto& weak_block : m_full_weak_blocks) { + weak_block.sweep(); + if (weak_block.can_allocate()) + now_usable_weak_blocks.append(weak_block); + } + for (auto& weak_block : now_usable_weak_blocks) { + m_usable_weak_blocks.append(weak_block); + } +} + void Heap::sweep_dead_cells(bool print_report, Core::ElapsedTimer const& measurement_timer) { dbgln_if(HEAP_DEBUG, "sweep_dead_cells:"); @@ -559,4 +578,21 @@ void Heap::uproot_cell(Cell* cell) m_uprooted_cells.append(cell); } +WeakImpl* Heap::create_weak_impl(void* ptr) +{ + if (m_usable_weak_blocks.is_empty()) { + // NOTE: These are leaked on Heap destruction, but that's fine since Heap is tied to process lifetime. + auto* weak_block = WeakBlock::create(); + m_usable_weak_blocks.append(*weak_block); + } + + auto* weak_block = m_usable_weak_blocks.first(); + auto* new_weak_impl = weak_block->allocate(static_cast(ptr)); + if (!weak_block->can_allocate()) { + m_full_weak_blocks.append(*weak_block); + } + + return new_weak_impl; +} + } diff --git a/Libraries/LibGC/Heap.h b/Libraries/LibGC/Heap.h index 193ce4ecc13..91b15c4c7fd 100644 --- a/Libraries/LibGC/Heap.h +++ b/Libraries/LibGC/Heap.h @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace GC { @@ -81,6 +82,8 @@ public: void enqueue_post_gc_task(AK::Function); + WeakImpl* create_weak_impl(void*); + private: friend class MarkingVisitor; friend class GraphConstructorVisitor; @@ -113,6 +116,7 @@ private: void mark_live_cells(HashMap const& live_cells); void finalize_unmarked_cells(); void sweep_dead_cells(bool print_report, Core::ElapsedTimer const&); + void sweep_weak_blocks(); ALWAYS_INLINE CellAllocator& allocator_for_size(size_t cell_size) { @@ -159,6 +163,9 @@ private: AK::Function&)> m_gather_embedder_roots; Vector> m_post_gc_tasks; + + WeakBlock::List m_usable_weak_blocks; + WeakBlock::List m_full_weak_blocks; } SWIFT_IMMORTAL_REFERENCE; inline void Heap::did_create_root(Badge, RootImpl& impl) diff --git a/Libraries/LibGC/Weak.h b/Libraries/LibGC/Weak.h new file mode 100644 index 00000000000..b1d9a60eedf --- /dev/null +++ b/Libraries/LibGC/Weak.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2025, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace GC { + +class WeakBlock; + +class WeakImpl { +public: + // NOTE: Null GC::Weaks point at this WeakImpl. This allows Weak to always chase the impl pointer without null-checking it. + static GC_API WeakImpl the_null_weak_impl; + + WeakImpl() = default; + WeakImpl(void* ptr) + : m_ptr(ptr) + { + } + + void* ptr() const { return m_ptr; } + void set_ptr(Badge, void* ptr) { m_ptr = ptr; } + + bool operator==(WeakImpl const& other) const { return m_ptr == other.m_ptr; } + bool operator!=(WeakImpl const& other) const { return m_ptr != other.m_ptr; } + + void ref() const { ++m_ref_count; } + void unref() const + { + VERIFY(m_ref_count); + --m_ref_count; + } + + size_t ref_count() const { return m_ref_count; } + + enum class State { + Allocated, + Freelist, + }; + + void set_state(State state) { m_state = state; } + State state() const { return m_state; } + +private: + mutable size_t m_ref_count { 0 }; + State m_state { State::Allocated }; + void* m_ptr { nullptr }; +}; + +template +class Weak { +public: + constexpr Weak() = default; + Weak(nullptr_t) { } + + Weak(T const* ptr); + Weak(T const& ptr); + + template + Weak(Weak const& other) + requires(IsConvertible); + + Weak(Ref const& other); + + template + Weak(Ref const& other) + requires(IsConvertible); + + template + Weak& operator=(Weak const& other) + requires(IsConvertible) + { + m_impl = other.impl(); + return *this; + } + + Weak& operator=(Ref const& other); + + template + Weak& operator=(Ref const& other) + requires(IsConvertible); + + Weak& operator=(T const& other); + + template + Weak& operator=(U const& other) + requires(IsConvertible); + + Weak& operator=(T const* other); + + template + Weak& operator=(U const* other) + requires(IsConvertible); + + T* operator->() const + { + ASSERT(ptr()); + return ptr(); + } + + [[nodiscard]] T& operator*() const + { + ASSERT(ptr()); + return *ptr(); + } + + Ptr ptr() const { return static_cast(impl().ptr()); } + + explicit operator bool() const { return !!ptr(); } + bool operator!() const { return !ptr(); } + + operator T*() const { return ptr(); } + + Ref as_nonnull() const + { + ASSERT(ptr()); + return *ptr(); + } + + WeakImpl& impl() const { return *m_impl; } + +private: + NonnullRefPtr m_impl { WeakImpl::the_null_weak_impl }; +}; + +template +inline bool operator==(Weak const& a, Ptr const& b) +{ + return a.ptr() == b.ptr(); +} + +template +inline bool operator==(Weak const& a, Ref const& b) +{ + return a.ptr() == b.ptr(); +} + +} + +namespace AK { + +template +struct Traits> : public DefaultTraits> { + static unsigned hash(GC::Weak const& value) + { + return Traits::hash(value.ptr()); + } +}; + +template +struct Formatter> : Formatter { + ErrorOr format(FormatBuilder& builder, GC::Weak const& value) + { + return Formatter::format(builder, value.ptr()); + } +}; + +} diff --git a/Libraries/LibGC/WeakBlock.cpp b/Libraries/LibGC/WeakBlock.cpp new file mode 100644 index 00000000000..5a00dbd7aca --- /dev/null +++ b/Libraries/LibGC/WeakBlock.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2025, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +#if defined(AK_OS_WINDOWS) +# include +# include +#endif + +namespace GC { + +WeakImpl WeakImpl::the_null_weak_impl; + +WeakBlock* WeakBlock::create() +{ +#if !defined(AK_OS_WINDOWS) + auto* block = (HeapBlock*)mmap(nullptr, WeakBlock::BLOCK_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + VERIFY(block != MAP_FAILED); +#else + auto* block = (HeapBlock*)VirtualAlloc(NULL, WeakBlock::BLOCK_SIZE, MEM_COMMIT, PAGE_READWRITE); + VERIFY(block); +#endif + return new (block) WeakBlock; +} + +WeakBlock::WeakBlock() +{ + for (size_t i = 0; i < IMPL_COUNT; ++i) { + m_impls[i].set_ptr({}, i + 1 < IMPL_COUNT ? &m_impls[i + 1] : nullptr); + m_impls[i].set_state(WeakImpl::State::Freelist); + } + m_freelist = &m_impls[0]; +} + +WeakBlock::~WeakBlock() = default; + +WeakImpl* WeakBlock::allocate(Cell* cell) +{ + auto* impl = m_freelist; + if (!impl) + return nullptr; + VERIFY(impl->ref_count() == 0); + m_freelist = impl->ptr() ? static_cast(impl->ptr()) : nullptr; + impl->set_ptr({}, cell); + impl->set_state(WeakImpl::State::Allocated); + return impl; +} + +void WeakBlock::deallocate(WeakImpl* impl) +{ + VERIFY(impl->ref_count() == 0); + impl->set_ptr({}, m_freelist); + impl->set_state(WeakImpl::State::Freelist); + m_freelist = impl; +} + +void WeakBlock::sweep() +{ + for (size_t i = 0; i < IMPL_COUNT; ++i) { + auto& impl = m_impls[i]; + if (impl.state() == WeakImpl::State::Freelist) + continue; + auto* cell = static_cast(impl.ptr()); + if (!cell || !cell->is_marked()) + impl.set_ptr({}, nullptr); + if (impl.ref_count() == 0) + deallocate(&impl); + } +} + +} diff --git a/Libraries/LibGC/WeakBlock.h b/Libraries/LibGC/WeakBlock.h new file mode 100644 index 00000000000..c475ddd0e60 --- /dev/null +++ b/Libraries/LibGC/WeakBlock.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2025, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace GC { + +class GC_API WeakBlock { +public: + static constexpr size_t BLOCK_SIZE = 16 * KiB; + + static WeakBlock* create(); + + WeakImpl* allocate(Cell*); + void deallocate(WeakImpl*); + + bool can_allocate() const { return m_freelist != nullptr; } + + void sweep(); + +private: + WeakBlock(); + ~WeakBlock(); + + IntrusiveListNode m_list_node; + +public: + using List = IntrusiveList<&WeakBlock::m_list_node>; + + WeakImpl* m_freelist { nullptr }; + + static constexpr size_t IMPL_COUNT = (BLOCK_SIZE - sizeof(m_list_node) - sizeof(WeakImpl*)) / sizeof(WeakImpl); + WeakImpl m_impls[IMPL_COUNT]; +}; + +static_assert(sizeof(WeakBlock) <= WeakBlock::BLOCK_SIZE); + +} diff --git a/Libraries/LibGC/WeakInlines.h b/Libraries/LibGC/WeakInlines.h new file mode 100644 index 00000000000..9a8fa00619a --- /dev/null +++ b/Libraries/LibGC/WeakInlines.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2025, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace GC { + +template +Weak::Weak(T const* ptr) + : m_impl(ptr ? *ptr->heap().create_weak_impl(const_cast(static_cast(ptr))) : WeakImpl::the_null_weak_impl) +{ +} + +template +Weak::Weak(T const& ptr) + : m_impl(*ptr.heap().create_weak_impl(const_cast(static_cast(&ptr)))) +{ +} + +template +template +Weak::Weak(Weak const& other) +requires(IsConvertible) + : m_impl(other.impl()) +{ +} + +template +Weak::Weak(Ref const& other) + : m_impl(*other.ptr()->heap().create_weak_impl(other.ptr())) +{ +} + +template +template +Weak::Weak(Ref const& other) +requires(IsConvertible) + : m_impl(*other.ptr()->heap().create_weak_impl(other.ptr())) +{ +} + +template +template +Weak& Weak::operator=(U const& other) +requires(IsConvertible) +{ + if (ptr() != other) { + m_impl = *other.heap().create_weak_impl(const_cast(static_cast(&other))); + } + return *this; +} + +template +Weak& Weak::operator=(Ref const& other) +{ + if (ptr() != other.ptr()) { + m_impl = *other.ptr()->heap().create_weak_impl(other.ptr()); + } + return *this; +} + +template +template +Weak& Weak::operator=(Ref const& other) +requires(IsConvertible) +{ + if (ptr() != other.ptr()) { + m_impl = *other.ptr()->heap().create_weak_impl(other.ptr()); + } + return *this; +} + +template +Weak& Weak::operator=(T const& other) +{ + if (ptr() != &other) { + m_impl = *other.heap().create_weak_impl(const_cast(static_cast(&other))); + } + return *this; +} + +template +Weak& Weak::operator=(T const* other) +{ + if (ptr() != other) { + if (other) + m_impl = *other->heap().create_weak_impl(const_cast(static_cast(other))); + else + m_impl = WeakImpl::the_null_weak_impl; + } + return *this; +} + +template +template +Weak& Weak::operator=(U const* other) +requires(IsConvertible) +{ + if (ptr() != other) { + if (other) + m_impl = *other->heap().create_weak_impl(const_cast(static_cast(other))); + else + m_impl = WeakImpl::the_null_weak_impl; + } + return *this; +} + +} diff --git a/Libraries/LibJS/Bytecode/Executable.h b/Libraries/LibJS/Bytecode/Executable.h index 54e95ee12ab..2dd945fd841 100644 --- a/Libraries/LibJS/Bytecode/Executable.h +++ b/Libraries/LibJS/Bytecode/Executable.h @@ -10,8 +10,9 @@ #include #include #include -#include #include +#include +#include #include #include #include @@ -37,11 +38,11 @@ struct PropertyLookupCache { GetPropertyInPrototypeChain, }; Type type { Type::Empty }; - WeakPtr from_shape; - WeakPtr shape; + GC::Weak from_shape; + GC::Weak shape; Optional property_offset; - WeakPtr prototype; - WeakPtr prototype_chain_validity; + GC::Weak prototype; + GC::Weak prototype_chain_validity; }; AK::Array entries; }; diff --git a/Libraries/LibJS/Bytecode/Interpreter.cpp b/Libraries/LibJS/Bytecode/Interpreter.cpp index 92627013ec7..66bc0d8e65e 100644 --- a/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -1133,20 +1133,22 @@ ThrowCompletionOr put_by_property_key(VM& vm, Value base, Value this_value case PropertyLookupCache::Entry::Type::Empty: break; case PropertyLookupCache::Entry::Type::ChangePropertyInPrototypeChain: { - if (!cache.prototype) [[unlikely]] + auto cached_prototype = cache.prototype.ptr(); + if (!cached_prototype) [[unlikely]] break; // OPTIMIZATION: If the prototype chain hasn't been mutated in a way that would invalidate the cache, we can use it. bool can_use_cache = [&]() -> bool { if (&object->shape() != cache.shape) [[unlikely]] return false; - if (!cache.prototype_chain_validity) [[unlikely]] + auto cached_prototype_chain_validity = cache.prototype_chain_validity.ptr(); + if (!cached_prototype_chain_validity) [[unlikely]] return false; - if (!cache.prototype_chain_validity->is_valid()) [[unlikely]] + if (!cached_prototype_chain_validity->is_valid()) [[unlikely]] return false; return true; }(); - if (can_use_cache) { - auto value_in_prototype = cache.prototype->get_direct(cache.property_offset.value()); + if (can_use_cache) [[likely]] { + auto value_in_prototype = cached_prototype->get_direct(cache.property_offset.value()); if (value_in_prototype.is_accessor()) [[unlikely]] { (void)TRY(call(vm, value_in_prototype.as_accessor().setter(), this_value, value)); return {}; @@ -1170,12 +1172,14 @@ ThrowCompletionOr put_by_property_key(VM& vm, Value base, Value this_value // reuse the resulting shape from the cache. if (cache.from_shape != &object->shape()) [[unlikely]] break; - if (!cache.shape) [[unlikely]] + auto cached_shape = cache.shape.ptr(); + if (!cached_shape) [[unlikely]] break; // The cache is invalid if the prototype chain has been mutated, since such a mutation could have added a setter for the property. - if (cache.prototype_chain_validity && !cache.prototype_chain_validity->is_valid()) [[unlikely]] + auto cached_prototype_chain_validity = cache.prototype_chain_validity.ptr(); + if (cached_prototype_chain_validity && !cached_prototype_chain_validity->is_valid()) [[unlikely]] break; - object->unsafe_set_shape(*cache.shape); + object->unsafe_set_shape(*cached_shape); object->put_direct(*cache.property_offset, value); return {}; } diff --git a/Libraries/LibJS/Bytecode/PropertyAccess.h b/Libraries/LibJS/Bytecode/PropertyAccess.h index a5c9759fe5d..1fb09a0c516 100644 --- a/Libraries/LibJS/Bytecode/PropertyAccess.h +++ b/Libraries/LibJS/Bytecode/PropertyAccess.h @@ -92,19 +92,21 @@ ALWAYS_INLINE ThrowCompletionOr get_by_id(VM& vm, GetBaseIdentifier get_b prototype_chain_validity = shape.prototype()->shape().prototype_chain_validity(); for (auto& cache_entry : cache.entries) { - if (cache_entry.prototype) { + auto cached_prototype = cache_entry.prototype.ptr(); + if (cached_prototype) { // OPTIMIZATION: If the prototype chain hasn't been mutated in a way that would invalidate the cache, we can use it. bool can_use_cache = [&]() -> bool { if (&shape != cache_entry.shape) [[unlikely]] return false; - if (!cache_entry.prototype_chain_validity) [[unlikely]] + auto cached_prototype_chain_validity = cache_entry.prototype_chain_validity.ptr(); + if (!cached_prototype_chain_validity) [[unlikely]] return false; - if (!cache_entry.prototype_chain_validity->is_valid()) [[unlikely]] + if (!cached_prototype_chain_validity->is_valid()) [[unlikely]] return false; return true; }(); if (can_use_cache) [[likely]] { - auto value = cache_entry.prototype->get_direct(cache_entry.property_offset.value()); + auto value = cached_prototype->get_direct(cache_entry.property_offset.value()); if (value.is_accessor()) return TRY(call(vm, value.as_accessor().getter(), this_value)); return value; diff --git a/Libraries/LibJS/Runtime/ExecutionContext.h b/Libraries/LibJS/Runtime/ExecutionContext.h index cb10fbcde97..58d65f59dbe 100644 --- a/Libraries/LibJS/Runtime/ExecutionContext.h +++ b/Libraries/LibJS/Runtime/ExecutionContext.h @@ -9,7 +9,6 @@ #pragma once -#include #include #include #include diff --git a/Libraries/LibJS/Runtime/Object.h b/Libraries/LibJS/Runtime/Object.h index 020ba1e15b4..3de452294b0 100644 --- a/Libraries/LibJS/Runtime/Object.h +++ b/Libraries/LibJS/Runtime/Object.h @@ -64,8 +64,7 @@ struct CacheableSetPropertyMetadata { GC::Ptr prototype; }; -class JS_API Object : public Cell - , public Weakable { +class JS_API Object : public Cell { GC_CELL(Object, Cell); GC_DECLARE_ALLOCATOR(Object); diff --git a/Libraries/LibJS/Runtime/Shape.cpp b/Libraries/LibJS/Runtime/Shape.cpp index ab6327441d9..81a19129a73 100644 --- a/Libraries/LibJS/Runtime/Shape.cpp +++ b/Libraries/LibJS/Runtime/Shape.cpp @@ -110,8 +110,8 @@ GC::Ref Shape::create_put_transition(PropertyKey const& property_key, Pro invalidate_prototype_if_needed_for_new_prototype(new_shape); if (!m_is_prototype_shape) { if (!m_forward_transitions) - m_forward_transitions = make>>(); - m_forward_transitions->set(key, new_shape.ptr()); + m_forward_transitions = make>>(); + m_forward_transitions->set(key, new_shape); } return new_shape; } @@ -125,7 +125,7 @@ GC::Ref Shape::create_configure_transition(PropertyKey const& property_ke invalidate_prototype_if_needed_for_new_prototype(new_shape); if (!m_is_prototype_shape) { if (!m_forward_transitions) - m_forward_transitions = make>>(); + m_forward_transitions = make>>(); m_forward_transitions->set(key, new_shape.ptr()); } return new_shape; @@ -141,7 +141,7 @@ GC::Ref Shape::create_prototype_transition(Object* new_prototype) invalidate_prototype_if_needed_for_new_prototype(new_shape); if (!m_is_prototype_shape) { if (!m_prototype_transitions) - m_prototype_transitions = make, WeakPtr>>(); + m_prototype_transitions = make, GC::Weak>>(); m_prototype_transitions->set(new_prototype, new_shape.ptr()); } return new_shape; @@ -279,7 +279,7 @@ GC::Ref Shape::create_delete_transition(PropertyKey const& property_key) auto new_shape = heap().allocate(*this, property_key, TransitionType::Delete); invalidate_prototype_if_needed_for_new_prototype(new_shape); if (!m_delete_transitions) - m_delete_transitions = make>>(); + m_delete_transitions = make>>(); m_delete_transitions->set(property_key, new_shape.ptr()); return new_shape; } diff --git a/Libraries/LibJS/Runtime/Shape.h b/Libraries/LibJS/Runtime/Shape.h index 454909ebdac..c3fcc4bec8b 100644 --- a/Libraries/LibJS/Runtime/Shape.h +++ b/Libraries/LibJS/Runtime/Shape.h @@ -10,8 +10,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -36,8 +36,7 @@ struct TransitionKey { } }; -class PrototypeChainValidity final : public Cell - , public Weakable { +class PrototypeChainValidity final : public Cell { GC_CELL(PrototypeChainValidity, Cell); GC_DECLARE_ALLOCATOR(PrototypeChainValidity); @@ -50,8 +49,7 @@ private: size_t padding { 0 }; }; -class JS_API Shape final : public Cell - , public Weakable { +class JS_API Shape final : public Cell { GC_CELL(Shape, Cell); GC_DECLARE_ALLOCATOR(Shape); @@ -129,9 +127,9 @@ private: mutable OwnPtr> m_property_table; - OwnPtr>> m_forward_transitions; - OwnPtr, WeakPtr>> m_prototype_transitions; - OwnPtr>> m_delete_transitions; + OwnPtr>> m_forward_transitions; + OwnPtr, GC::Weak>> m_prototype_transitions; + OwnPtr>> m_delete_transitions; GC::Ptr m_previous; Optional m_property_key; GC::Ptr m_prototype; diff --git a/Libraries/LibWeb/ARIA/ARIAMixin.cpp b/Libraries/LibWeb/ARIA/ARIAMixin.cpp index db0559f40c4..180a637c1d9 100644 --- a/Libraries/LibWeb/ARIA/ARIAMixin.cpp +++ b/Libraries/LibWeb/ARIA/ARIAMixin.cpp @@ -247,25 +247,25 @@ Vector ARIAMixin::parse_id_reference_list(Optional const& id_lis ENUMERATE_ARIA_ELEMENT_REFERENCING_ATTRIBUTES #undef __ENUMERATE_ARIA_ATTRIBUTE -#define __ENUMERATE_ARIA_ATTRIBUTE(attribute, referencing_attribute) \ - Optional>> const& ARIAMixin::attribute() const \ - { \ - return m_##attribute; \ - } \ - \ - void ARIAMixin::set_##attribute(Optional>> value) \ - { \ - m_##attribute = move(value); \ - } \ - \ - GC::Ptr ARIAMixin::cached_##attribute() const \ - { \ - return m_cached_##attribute; \ - } \ - \ - void ARIAMixin::set_cached_##attribute(GC::Ptr value) \ - { \ - m_cached_##attribute = value; \ +#define __ENUMERATE_ARIA_ATTRIBUTE(attribute, referencing_attribute) \ + Optional>> const& ARIAMixin::attribute() const \ + { \ + return m_##attribute; \ + } \ + \ + void ARIAMixin::set_##attribute(Optional>> value) \ + { \ + m_##attribute = move(value); \ + } \ + \ + GC::Ptr ARIAMixin::cached_##attribute() const \ + { \ + return m_cached_##attribute; \ + } \ + \ + void ARIAMixin::set_cached_##attribute(GC::Ptr value) \ + { \ + m_cached_##attribute = value; \ } ENUMERATE_ARIA_ELEMENT_LIST_REFERENCING_ATTRIBUTES #undef __ENUMERATE_ARIA_ATTRIBUTE diff --git a/Libraries/LibWeb/ARIA/ARIAMixin.h b/Libraries/LibWeb/ARIA/ARIAMixin.h index de3943dbacd..13be0d2858f 100644 --- a/Libraries/LibWeb/ARIA/ARIAMixin.h +++ b/Libraries/LibWeb/ARIA/ARIAMixin.h @@ -67,11 +67,11 @@ public: ENUMERATE_ARIA_ELEMENT_REFERENCING_ATTRIBUTES #undef __ENUMERATE_ARIA_ATTRIBUTE -#define __ENUMERATE_ARIA_ATTRIBUTE(attribute, referencing_attribute) \ - Optional>> const& attribute() const; \ - void set_##attribute(Optional>>); \ - \ - GC::Ptr cached_##attribute() const; \ +#define __ENUMERATE_ARIA_ATTRIBUTE(attribute, referencing_attribute) \ + Optional>> const& attribute() const; \ + void set_##attribute(Optional>>); \ + \ + GC::Ptr cached_##attribute() const; \ void set_cached_##attribute(GC::Ptr); ENUMERATE_ARIA_ELEMENT_LIST_REFERENCING_ATTRIBUTES #undef __ENUMERATE_ARIA_ATTRIBUTE @@ -85,12 +85,12 @@ protected: private: #define __ENUMERATE_ARIA_ATTRIBUTE(attribute, referencing_attribute) \ - WeakPtr m_##attribute; + GC::Weak m_##attribute; ENUMERATE_ARIA_ELEMENT_REFERENCING_ATTRIBUTES #undef __ENUMERATE_ARIA_ATTRIBUTE #define __ENUMERATE_ARIA_ATTRIBUTE(attribute, referencing_attribute) \ - Optional>> m_##attribute; \ + Optional>> m_##attribute; \ GC::Ptr m_cached_##attribute; ENUMERATE_ARIA_ELEMENT_LIST_REFERENCING_ATTRIBUTES #undef __ENUMERATE_ARIA_ATTRIBUTE diff --git a/Libraries/LibWeb/Bindings/MainThreadVM.cpp b/Libraries/LibWeb/Bindings/MainThreadVM.cpp index 788d9b7aa2e..7409df4ab17 100644 --- a/Libraries/LibWeb/Bindings/MainThreadVM.cpp +++ b/Libraries/LibWeb/Bindings/MainThreadVM.cpp @@ -744,7 +744,7 @@ void queue_mutation_observer_microtask(DOM::Document const& document) // 3. For each node of mo’s node list, remove all transient registered observers whose observer is mo from node’s registered observer list. for (auto& node : mutation_observer->node_list()) { // FIXME: Is this correct? - if (node.is_null()) + if (!node) continue; if (node->registered_observer_list()) { diff --git a/Libraries/LibWeb/CSS/Fetch.cpp b/Libraries/LibWeb/CSS/Fetch.cpp index 655c3e9343b..175ae67ffb9 100644 --- a/Libraries/LibWeb/CSS/Fetch.cpp +++ b/Libraries/LibWeb/CSS/Fetch.cpp @@ -126,7 +126,7 @@ GC::Ptr fetch_an_external_image_for_a_stylesheet(St auto shared_resource_request = HTML::SharedResourceRequest::get_or_create(realm, document->page(), request->url()); shared_resource_request->add_callbacks( - [document, weak_document = document->make_weak_ptr()] { + [document, weak_document = GC::Weak { document }] { if (!weak_document) return; diff --git a/Libraries/LibWeb/CSS/MediaQuery.cpp b/Libraries/LibWeb/CSS/MediaQuery.cpp index 38777478bcc..588207e4094 100644 --- a/Libraries/LibWeb/CSS/MediaQuery.cpp +++ b/Libraries/LibWeb/CSS/MediaQuery.cpp @@ -99,7 +99,12 @@ String MediaFeature::to_string() const MatchResult MediaFeature::evaluate(DOM::Document const* document) const { VERIFY(document); - VERIFY(document->window()); + + // FIXME: In some cases (e.g. when parsing HTML using DOMParser::parse_from_string()) a document may not be associated with a window - + // for now we just return false but perhaps there are some media queries we should still attempt to resolve. + if (!document->window()) + return MatchResult::False; + auto maybe_queried_value = document->window()->query_media_feature(m_id); if (!maybe_queried_value.has_value()) return MatchResult::False; diff --git a/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h b/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h index 4b356b0e887..cc1939f40a2 100644 --- a/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h @@ -9,6 +9,7 @@ #pragma once +#include #include #include #include @@ -79,7 +80,7 @@ private: GC::Ptr m_style_sheet; URL m_url; - WeakPtr m_document; + GC::Weak m_document; size_t m_current_frame_index { 0 }; size_t m_loops_completed { 0 }; diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 3b081ec092c..376577dec54 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -494,8 +494,11 @@ Document::Document(JS::Realm& realm, URL::URL const& url, TemporaryDocumentForFr HTML::main_thread_event_loop().register_document({}, *this); } -Document::~Document() +Document::~Document() = default; + +void Document::finalize() { + Base::finalize(); HTML::main_thread_event_loop().unregister_document({}, *this); } @@ -1785,9 +1788,9 @@ void Document::invalidate_style_of_elements_affected_by_has() auto nodes = move(m_pending_nodes_for_style_invalidation_due_to_presence_of_has); for (auto const& node : nodes) { - if (node.is_null()) + if (!node) continue; - for (auto* ancestor = node.ptr(); ancestor; ancestor = ancestor->parent_or_shadow_host()) { + for (auto ancestor = node.ptr(); ancestor; ancestor = ancestor->parent_or_shadow_host()) { if (!ancestor->is_element()) continue; auto& element = static_cast(*ancestor); @@ -3417,7 +3420,7 @@ void Document::run_the_scroll_steps() void Document::add_media_query_list(GC::Ref media_query_list) { m_needs_media_query_evaluation = true; - m_media_query_lists.append(*media_query_list); + m_media_query_lists.append(media_query_list); } // https://drafts.csswg.org/cssom-view/#evaluate-media-queries-and-report-changes @@ -3429,7 +3432,7 @@ void Document::evaluate_media_queries_and_report_changes() // NOTE: Not in the spec, but we take this opportunity to prune null WeakPtrs. m_media_query_lists.remove_all_matching([](auto& it) { - return it.is_null(); + return !it; }); // 1. For each MediaQueryList object target that has doc as its document, @@ -3440,7 +3443,7 @@ void Document::evaluate_media_queries_and_report_changes() // with its type attribute initialized to change, its isTrusted attribute // initialized to true, its media attribute initialized to target’s media, // and its matches attribute initialized to target’s matches state. - if (media_query_list_ptr.is_null()) + if (!media_query_list_ptr) continue; GC::Ptr media_query_list = media_query_list_ptr.ptr(); bool did_match = media_query_list->matches(); @@ -6842,7 +6845,7 @@ NonnullRefPtr Document::custom_property_initial_value(Fly GC::Ptr ElementByIdMap::get(FlyString const& element_id) const { if (auto elements = m_map.get(element_id); elements.has_value() && !elements->is_empty()) { - if (auto element = elements->first(); element.has_value()) + if (auto element = elements->first()) return *element; } return {}; diff --git a/Libraries/LibWeb/DOM/Document.h b/Libraries/LibWeb/DOM/Document.h index 5e19597a9e2..8a1e5eee81a 100644 --- a/Libraries/LibWeb/DOM/Document.h +++ b/Libraries/LibWeb/DOM/Document.h @@ -681,7 +681,7 @@ public: WebIDL::ExceptionOr query_command_value(FlyString const& command); WebIDL::ExceptionOr> create_expression(String const& expression, GC::Ptr resolver = nullptr); - WebIDL::ExceptionOr> evaluate(String const& expression, const DOM::Node& context_node, GC::Ptr resolver = nullptr, WebIDL::UnsignedShort type = 0, GC::Ptr result = nullptr); + WebIDL::ExceptionOr> evaluate(String const& expression, DOM::Node const& context_node, GC::Ptr resolver = nullptr, WebIDL::UnsignedShort type = 0, GC::Ptr result = nullptr); GC::Ref create_ns_resolver(GC::Ref node_resolver); // legacy // https://w3c.github.io/selection-api/#dfn-has-scheduled-selectionchange-event @@ -924,7 +924,7 @@ public: void schedule_ancestors_style_invalidation_due_to_presence_of_has(Node& node) { - m_pending_nodes_for_style_invalidation_due_to_presence_of_has.set(node.make_weak_ptr()); + m_pending_nodes_for_style_invalidation_due_to_presence_of_has.set(node); } ElementByIdMap& element_by_id() const; @@ -953,6 +953,8 @@ private: // ^HTML::GlobalEventHandlers virtual GC::Ptr global_event_handlers_to_event_target(FlyString const&) final { return *this; } + virtual void finalize() override final; + void invalidate_style_of_elements_affected_by_has(); void tear_down_layout_tree(); @@ -1115,7 +1117,7 @@ private: // Used by evaluate_media_queries_and_report_changes(). bool m_needs_media_query_evaluation { false }; - Vector> m_media_query_lists; + Vector> m_media_query_lists; bool m_needs_full_style_update { false }; bool m_needs_full_layout_tree_update { false }; @@ -1278,8 +1280,8 @@ private: RefPtr m_cursor_blink_timer; bool m_cursor_blink_state { false }; - // NOTE: This is WeakPtr, not GCPtr, on purpose. We don't want the document to keep some old detached navigable alive. - WeakPtr m_cached_navigable; + // NOTE: This is GC::Weak, not GC::Ptr, on purpose. We don't want the document to keep some old detached navigable alive. + GC::Weak m_cached_navigable; bool m_enable_cookies_on_file_domains { false }; @@ -1331,7 +1333,7 @@ private: // https://drafts.csswg.org/css-view-transitions-1/#document-update-callback-queue Vector> m_update_callback_queue = {}; - HashTable> m_pending_nodes_for_style_invalidation_due_to_presence_of_has; + HashTable> m_pending_nodes_for_style_invalidation_due_to_presence_of_has; GC::Ref m_style_invalidator; diff --git a/Libraries/LibWeb/DOM/Element.cpp b/Libraries/LibWeb/DOM/Element.cpp index 27203e07c8d..e9503cacd57 100644 --- a/Libraries/LibWeb/DOM/Element.cpp +++ b/Libraries/LibWeb/DOM/Element.cpp @@ -541,7 +541,7 @@ GC::Ptr Element::get_the_attribute_associated_element(FlyString co } // https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#attr-associated-elements -Optional>> Element::get_the_attribute_associated_elements(FlyString const& content_attribute, Optional>> const& explicitly_set_attribute_elements) const +Optional>> Element::get_the_attribute_associated_elements(FlyString const& content_attribute, Optional>> const& explicitly_set_attribute_elements) const { // 1. Let elements be an empty list. GC::RootVector> elements(heap()); diff --git a/Libraries/LibWeb/DOM/Element.h b/Libraries/LibWeb/DOM/Element.h index a8bf89a8152..2fd9b30f3bf 100644 --- a/Libraries/LibWeb/DOM/Element.h +++ b/Libraries/LibWeb/DOM/Element.h @@ -173,7 +173,7 @@ public: GC::Ptr get_attribute_node_ns(Optional const& namespace_, FlyString const& name) const; GC::Ptr get_the_attribute_associated_element(FlyString const& content_attribute, GC::Ptr explicitly_set_attribute_element) const; - Optional>> get_the_attribute_associated_elements(FlyString const& content_attribute, Optional>> const& explicitly_set_attribute_elements) const; + Optional>> get_the_attribute_associated_elements(FlyString const& content_attribute, Optional>> const& explicitly_set_attribute_elements) const; DOMTokenList* class_list(); diff --git a/Libraries/LibWeb/DOM/ElementByIdMap.cpp b/Libraries/LibWeb/DOM/ElementByIdMap.cpp index 229984ba40e..c7d8e415ee5 100644 --- a/Libraries/LibWeb/DOM/ElementByIdMap.cpp +++ b/Libraries/LibWeb/DOM/ElementByIdMap.cpp @@ -10,17 +10,17 @@ namespace Web::DOM { void ElementByIdMap::add(FlyString const& element_id, Element& element) { - auto& elements_with_id = m_map.ensure(element_id, [] { return Vector> {}; }); + auto& elements_with_id = m_map.ensure(element_id, [] { return Vector> {}; }); // Remove all elements that were deallocated. - elements_with_id.remove_all_matching([](WeakPtr& element) { - return !element.has_value(); + elements_with_id.remove_all_matching([](GC::Weak& element) { + return !element; }); elements_with_id.remove_first_matching([&](auto const& another_element) { return &element == another_element.ptr(); }); - elements_with_id.insert_before_matching(element, [&](auto& another_element) { + elements_with_id.insert_before_matching(GC::Weak { element }, [&](auto& another_element) { return element.is_before(*another_element); }); } @@ -32,7 +32,7 @@ void ElementByIdMap::remove(FlyString const& element_id, Element& element) return; auto& elements_with_id = *maybe_elements_with_id; elements_with_id.remove_all_matching([&](auto& another_element) { - if (!another_element.has_value()) + if (!another_element) return true; return &element == another_element.ptr(); }); diff --git a/Libraries/LibWeb/DOM/ElementByIdMap.h b/Libraries/LibWeb/DOM/ElementByIdMap.h index 57b12678342..cfb528ec6a2 100644 --- a/Libraries/LibWeb/DOM/ElementByIdMap.h +++ b/Libraries/LibWeb/DOM/ElementByIdMap.h @@ -18,7 +18,7 @@ public: GC::Ptr get(FlyString const& element_id) const; private: - HashMap>> m_map; + HashMap>> m_map; }; } diff --git a/Libraries/LibWeb/DOM/MutationObserver.cpp b/Libraries/LibWeb/DOM/MutationObserver.cpp index 5f30c900953..ea6a847e396 100644 --- a/Libraries/LibWeb/DOM/MutationObserver.cpp +++ b/Libraries/LibWeb/DOM/MutationObserver.cpp @@ -97,7 +97,7 @@ WebIDL::ExceptionOr MutationObserver::observe(Node& target, MutationObserv // 1. For each node of this’s node list, remove all transient registered observers whose source is registered from node’s registered observer list. for (auto& node : m_node_list) { // FIXME: Is this correct? - if (node.is_null()) + if (!node) continue; if (node->registered_observer_list()) { @@ -120,7 +120,7 @@ WebIDL::ExceptionOr MutationObserver::observe(Node& target, MutationObserv target.add_registered_observer(new_registered_observer); // 2. Append target to this’s node list. - m_node_list.append(target.make_weak_ptr()); + m_node_list.append(target); } return {}; @@ -132,7 +132,7 @@ void MutationObserver::disconnect() // 1. For each node of this’s node list, remove any registered observer from node’s registered observer list for which this is the observer. for (auto& node : m_node_list) { // FIXME: Is this correct? - if (node.is_null()) + if (!node) continue; if (node->registered_observer_list()) { diff --git a/Libraries/LibWeb/DOM/MutationObserver.h b/Libraries/LibWeb/DOM/MutationObserver.h index 2b334b23e93..e9c3483f9c0 100644 --- a/Libraries/LibWeb/DOM/MutationObserver.h +++ b/Libraries/LibWeb/DOM/MutationObserver.h @@ -38,8 +38,8 @@ public: void disconnect(); Vector> take_records(); - Vector>& node_list() { return m_node_list; } - Vector> const& node_list() const { return m_node_list; } + Vector>& node_list() { return m_node_list; } + Vector> const& node_list() const { return m_node_list; } WebIDL::CallbackType& callback() { return *m_callback; } @@ -61,7 +61,7 @@ private: // https://dom.spec.whatwg.org/#mutationobserver-node-list // NOTE: These are weak, per https://dom.spec.whatwg.org/#garbage-collection // Registered observers in a node’s registered observer list have a weak reference to the node. - Vector> m_node_list; + Vector> m_node_list; // https://dom.spec.whatwg.org/#concept-mo-queue Vector> m_record_queue; diff --git a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp index 857a673d917..d275eb2c414 100644 --- a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp +++ b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp @@ -100,8 +100,13 @@ void CanvasRenderingContext2D::fill_rect(float x, float y, float width, float he fill_internal(rect_path(x, y, width, height), Gfx::WindingRule::EvenOdd); } +// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-clearrect void CanvasRenderingContext2D::clear_rect(float x, float y, float width, float height) { + // 1. If any of the arguments are infinite or NaN, then return. + if (!isfinite(x) || !isfinite(y) || !isfinite(width) || !isfinite(height)) + return; + if (auto* painter = this->painter()) { auto rect = Gfx::FloatRect(x, y, width, height); painter->clear_rect(rect, clear_color()); diff --git a/Libraries/LibWeb/HTML/EventLoop/EventLoop.h b/Libraries/LibWeb/HTML/EventLoop/EventLoop.h index 14df272e813..5d41e7ecda2 100644 --- a/Libraries/LibWeb/HTML/EventLoop/EventLoop.h +++ b/Libraries/LibWeb/HTML/EventLoop/EventLoop.h @@ -8,9 +8,9 @@ #include #include -#include #include #include +#include #include #include #include @@ -119,7 +119,7 @@ private: // https://html.spec.whatwg.org/multipage/webappapis.html#performing-a-microtask-checkpoint bool m_performing_a_microtask_checkpoint { false }; - Vector> m_documents; + Vector> m_documents; // Used to implement step 4 of "perform a microtask checkpoint". // NOTE: These are weak references! ESO registers and unregisters itself from the event loop manually. diff --git a/Libraries/LibWeb/HTML/FormAssociatedElement.h b/Libraries/LibWeb/HTML/FormAssociatedElement.h index 1d61b0c72fe..8fd3235baf3 100644 --- a/Libraries/LibWeb/HTML/FormAssociatedElement.h +++ b/Libraries/LibWeb/HTML/FormAssociatedElement.h @@ -8,7 +8,7 @@ #pragma once #include -#include +#include #include #include #include @@ -178,7 +178,7 @@ protected: private: void reset_form_owner(); - WeakPtr m_form; + GC::Weak m_form; // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#parser-inserted-flag bool m_parser_inserted { false }; diff --git a/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 74089fb424e..990aab8c878 100644 --- a/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -380,7 +380,7 @@ static void show_the_picker_if_applicable(HTMLInputElement& element) // 2. Wait for the user to have made their selection. auto accepted_file_types = element.parse_accept_attribute(); auto allow_multiple_files = element.has_attribute(HTML::AttributeNames::multiple) ? AllowMultipleFiles::Yes : AllowMultipleFiles::No; - auto weak_element = element.make_weak_ptr(); + auto weak_element = GC::Weak { element }; element.set_is_open(true); element.document().browsing_context()->top_level_browsing_context()->page().did_request_file_picker(weak_element, accepted_file_types, allow_multiple_files); @@ -404,7 +404,7 @@ static void show_the_picker_if_applicable(HTMLInputElement& element) // events, or a cancel event.) else { if (element.type_state() == HTMLInputElement::TypeAttributeState::Color) { - auto weak_element = element.make_weak_ptr(); + auto weak_element = GC::Weak { element }; element.set_is_open(true); element.document().browsing_context()->top_level_browsing_context()->page().did_request_color_picker(weak_element, Color::from_utf16_string(element.value()).value_or(Color(0, 0, 0))); } diff --git a/Libraries/LibWeb/HTML/HTMLLinkElement.cpp b/Libraries/LibWeb/HTML/HTMLLinkElement.cpp index 0905ef6cfdd..84648c497b3 100644 --- a/Libraries/LibWeb/HTML/HTMLLinkElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLLinkElement.cpp @@ -570,7 +570,7 @@ bool HTMLLinkElement::stylesheet_linked_resource_fetch_setup_steps(Fetch::Infras void HTMLLinkElement::set_parser_document(Badge, GC::Ref document) { - m_parser_document = document->make_weak_ptr(); + m_parser_document = document; } bool HTMLLinkElement::is_implicitly_potentially_render_blocking() const diff --git a/Libraries/LibWeb/HTML/HTMLLinkElement.h b/Libraries/LibWeb/HTML/HTMLLinkElement.h index 7d4813d950a..629f4c1ab77 100644 --- a/Libraries/LibWeb/HTML/HTMLLinkElement.h +++ b/Libraries/LibWeb/HTML/HTMLLinkElement.h @@ -167,7 +167,7 @@ private: Optional m_mime_type; - WeakPtr m_parser_document; + GC::Weak m_parser_document; }; } diff --git a/Libraries/LibWeb/HTML/HTMLSelectElement.cpp b/Libraries/LibWeb/HTML/HTMLSelectElement.cpp index fdb1a8e81b4..eb53e22d199 100644 --- a/Libraries/LibWeb/HTML/HTMLSelectElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLSelectElement.cpp @@ -480,7 +480,7 @@ void HTMLSelectElement::show_the_picker_if_applicable() } // Request select dropdown - auto weak_element = make_weak_ptr(); + auto weak_element = GC::Weak { *this }; auto rect = get_bounding_client_rect(); auto position = document().navigable()->to_top_level_position(Web::CSSPixelPoint { rect.x(), rect.bottom() }); document().page().did_request_select_dropdown(weak_element, position, rect.width(), m_select_items); diff --git a/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp b/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp index 85639cde151..dda2ba70a1b 100644 --- a/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp @@ -31,10 +31,10 @@ GC_DEFINE_ALLOCATOR(HTMLTextAreaElement); HTMLTextAreaElement::HTMLTextAreaElement(DOM::Document& document, DOM::QualifiedName qualified_name) : HTMLElement(document, move(qualified_name)) - , m_input_event_timer(Core::Timer::create_single_shot(0, [weak_this = make_weak_ptr()]() { + , m_input_event_timer(Core::Timer::create_single_shot(0, [weak_this = GC::Weak { *this }]() { if (!weak_this) return; - static_cast(weak_this.ptr())->queue_firing_input_event(); + weak_this->queue_firing_input_event(); })) { } diff --git a/Libraries/LibWeb/HTML/Navigable.h b/Libraries/LibWeb/HTML/Navigable.h index de3fa2027bf..9e4f700594d 100644 --- a/Libraries/LibWeb/HTML/Navigable.h +++ b/Libraries/LibWeb/HTML/Navigable.h @@ -51,8 +51,7 @@ struct PaintConfig { }; // https://html.spec.whatwg.org/multipage/document-sequences.html#navigable -class WEB_API Navigable : public JS::Cell - , public Weakable { +class WEB_API Navigable : public JS::Cell { GC_CELL(Navigable, JS::Cell); GC_DECLARE_ALLOCATOR(Navigable); diff --git a/Libraries/LibWeb/IntersectionObserver/IntersectionObserver.cpp b/Libraries/LibWeb/IntersectionObserver/IntersectionObserver.cpp index f7154f69fb3..241ad3d5fec 100644 --- a/Libraries/LibWeb/IntersectionObserver/IntersectionObserver.cpp +++ b/Libraries/LibWeb/IntersectionObserver/IntersectionObserver.cpp @@ -298,7 +298,7 @@ CSSPixelRect IntersectionObserver::root_intersection_rectangle() const } else { document = &intersection_root.get>().cell()->document(); } - if (m_document.has_value() && document->origin().is_same_origin(m_document->origin())) { + if (m_document && document->origin().is_same_origin(m_document->origin())) { if (auto layout_node = intersection_root.visit([&](auto& node) -> GC::Ptr { return node->layout_node(); })) { rect.inflate( m_root_margin[0].to_px(*layout_node, rect.height()), diff --git a/Libraries/LibWeb/IntersectionObserver/IntersectionObserver.h b/Libraries/LibWeb/IntersectionObserver/IntersectionObserver.h index 1ea66401e6b..9f1489bdea1 100644 --- a/Libraries/LibWeb/IntersectionObserver/IntersectionObserver.h +++ b/Libraries/LibWeb/IntersectionObserver/IntersectionObserver.h @@ -106,7 +106,7 @@ private: Vector> m_observation_targets; // AD-HOC: This is the document where we've registered the IntersectionObserver. - WeakPtr m_document; + GC::Weak m_document; }; } diff --git a/Libraries/LibWeb/Page/EventHandler.h b/Libraries/LibWeb/Page/EventHandler.h index 6a47bc175ce..c4d41007520 100644 --- a/Libraries/LibWeb/Page/EventHandler.h +++ b/Libraries/LibWeb/Page/EventHandler.h @@ -86,7 +86,7 @@ private: NonnullOwnPtr m_drag_and_drop_event_handler; - WeakPtr m_mousedown_target; + GC::Weak m_mousedown_target; Optional m_mousemove_previous_screen_position; diff --git a/Libraries/LibWeb/Page/Page.cpp b/Libraries/LibWeb/Page/Page.cpp index 535048a1631..8e56432144e 100644 --- a/Libraries/LibWeb/Page/Page.cpp +++ b/Libraries/LibWeb/Page/Page.cpp @@ -68,7 +68,7 @@ void Page::set_focused_navigable(Badge, HTML::Navigable& navigable void Page::navigable_document_destroyed(Badge, HTML::Navigable& navigable) { if (&navigable == m_focused_navigable.ptr()) - m_focused_navigable.clear(); + m_focused_navigable = nullptr; } void Page::load(URL::URL const& url) @@ -405,7 +405,7 @@ void Page::on_pending_dialog_closed() } } -void Page::did_request_color_picker(WeakPtr target, Color current_color) +void Page::did_request_color_picker(GC::Weak target, Color current_color) { if (m_pending_non_blocking_dialog == PendingNonBlockingDialog::None) { m_pending_non_blocking_dialog = PendingNonBlockingDialog::ColorPicker; @@ -425,12 +425,12 @@ void Page::color_picker_update(Optional picked_color, HTML::ColorPickerUp auto& input_element = as(*m_pending_non_blocking_dialog_target); input_element.did_pick_color(move(picked_color), state); if (state == HTML::ColorPickerUpdateState::Closed) - m_pending_non_blocking_dialog_target.clear(); + m_pending_non_blocking_dialog_target = nullptr; } } } -void Page::did_request_file_picker(WeakPtr target, HTML::FileFilter const& accepted_file_types, HTML::AllowMultipleFiles allow_multiple_files) +void Page::did_request_file_picker(GC::Weak target, HTML::FileFilter const& accepted_file_types, HTML::AllowMultipleFiles allow_multiple_files) { if (m_pending_non_blocking_dialog == PendingNonBlockingDialog::None) { m_pending_non_blocking_dialog = PendingNonBlockingDialog::FilePicker; @@ -449,12 +449,12 @@ void Page::file_picker_closed(Span selected_files) auto& input_element = as(*m_pending_non_blocking_dialog_target); input_element.did_select_files(selected_files); - m_pending_non_blocking_dialog_target.clear(); + m_pending_non_blocking_dialog_target = nullptr; } } } -void Page::did_request_select_dropdown(WeakPtr target, Web::CSSPixelPoint content_position, Web::CSSPixels minimum_width, Vector items) +void Page::did_request_select_dropdown(GC::Weak target, Web::CSSPixelPoint content_position, Web::CSSPixels minimum_width, Vector items) { if (m_pending_non_blocking_dialog == PendingNonBlockingDialog::None) { m_pending_non_blocking_dialog = PendingNonBlockingDialog::Select; @@ -471,7 +471,7 @@ void Page::select_dropdown_closed(Optional const& selected_item_id) if (m_pending_non_blocking_dialog_target) { auto& select_element = as(*m_pending_non_blocking_dialog_target); select_element.did_select_item(selected_item_id); - m_pending_non_blocking_dialog_target.clear(); + m_pending_non_blocking_dialog_target = nullptr; } } } diff --git a/Libraries/LibWeb/Page/Page.h b/Libraries/LibWeb/Page/Page.h index bd66fb732c7..ae7140885d6 100644 --- a/Libraries/LibWeb/Page/Page.h +++ b/Libraries/LibWeb/Page/Page.h @@ -9,8 +9,8 @@ #pragma once -#include #include +#include #include #include #include @@ -159,13 +159,13 @@ public: void dismiss_dialog(GC::Ref> on_dialog_closed); void accept_dialog(GC::Ref> on_dialog_closed); - void did_request_color_picker(WeakPtr target, Color current_color); + void did_request_color_picker(GC::Weak target, Color current_color); void color_picker_update(Optional picked_color, HTML::ColorPickerUpdateState state); - void did_request_file_picker(WeakPtr target, HTML::FileFilter const& accepted_file_types, HTML::AllowMultipleFiles); + void did_request_file_picker(GC::Weak target, HTML::FileFilter const& accepted_file_types, HTML::AllowMultipleFiles); void file_picker_closed(Span selected_files); - void did_request_select_dropdown(WeakPtr target, Web::CSSPixelPoint content_position, Web::CSSPixels minimum_width, Vector items); + void did_request_select_dropdown(GC::Weak target, Web::CSSPixelPoint content_position, Web::CSSPixels minimum_width, Vector items); void select_dropdown_closed(Optional const& selected_item_id); using ClipboardRequest = GC::Ref)>>; @@ -246,7 +246,7 @@ private: GC::Ref m_client; - WeakPtr m_focused_navigable; + GC::Weak m_focused_navigable; GC::Ptr m_top_level_traversable; @@ -275,7 +275,7 @@ private: GC::Ptr> m_on_pending_dialog_closed; PendingNonBlockingDialog m_pending_non_blocking_dialog { PendingNonBlockingDialog::None }; - WeakPtr m_pending_non_blocking_dialog_target; + GC::Weak m_pending_non_blocking_dialog_target; HashMap m_pending_clipboard_requests; u64 m_next_clipboard_request_id { 0 }; diff --git a/Libraries/LibWeb/Painting/PaintableBox.h b/Libraries/LibWeb/Painting/PaintableBox.h index 3f321249d02..bd5199f38a8 100644 --- a/Libraries/LibWeb/Painting/PaintableBox.h +++ b/Libraries/LibWeb/Painting/PaintableBox.h @@ -25,8 +25,7 @@ namespace Web::Painting { WEB_API void set_paint_viewport_scrollbars(bool enabled); -class WEB_API PaintableBox : public Paintable - , public Weakable { +class WEB_API PaintableBox : public Paintable { GC_CELL(PaintableBox, Paintable); public: diff --git a/Libraries/LibWeb/Painting/ScrollFrame.h b/Libraries/LibWeb/Painting/ScrollFrame.h index c93d42e94a6..a5604cd8da9 100644 --- a/Libraries/LibWeb/Painting/ScrollFrame.h +++ b/Libraries/LibWeb/Painting/ScrollFrame.h @@ -6,7 +6,7 @@ #pragma once -#include +#include #include #include @@ -40,7 +40,7 @@ public: } private: - WeakPtr m_paintable_box; + GC::Weak m_paintable_box; size_t m_id { 0 }; bool m_sticky { false }; RefPtr m_parent; diff --git a/Libraries/LibWeb/ResizeObserver/ResizeObserver.h b/Libraries/LibWeb/ResizeObserver/ResizeObserver.h index b5b6ab1592a..040d658120d 100644 --- a/Libraries/LibWeb/ResizeObserver/ResizeObserver.h +++ b/Libraries/LibWeb/ResizeObserver/ResizeObserver.h @@ -52,7 +52,7 @@ private: Vector> m_skipped_targets; // AD-HOC: This is the document where we've registered the observer. - WeakPtr m_document; + GC::Weak m_document; IntrusiveListNode m_list_node; diff --git a/Libraries/LibWeb/WebSockets/WebSocket.cpp b/Libraries/LibWeb/WebSockets/WebSocket.cpp index 1f188ab6215..f929e9c956a 100644 --- a/Libraries/LibWeb/WebSockets/WebSocket.cpp +++ b/Libraries/LibWeb/WebSockets/WebSocket.cpp @@ -218,25 +218,25 @@ ErrorOr WebSocket::establish_web_socket_connection(URL::URL const& url_rec m_websocket = request_client->websocket_connect(url_record, origin_string, protocol_byte_strings, {}, additional_headers); - m_websocket->on_open = [weak_this = make_weak_ptr()] { + m_websocket->on_open = [weak_this = GC::Weak { *this }] { if (!weak_this) return; auto& websocket = const_cast(*weak_this); websocket.on_open(); }; - m_websocket->on_message = [weak_this = make_weak_ptr()](auto message) { + m_websocket->on_message = [weak_this = GC::Weak { *this }](auto message) { if (!weak_this) return; auto& websocket = const_cast(*weak_this); websocket.on_message(move(message.data), message.is_text); }; - m_websocket->on_close = [weak_this = make_weak_ptr()](auto code, auto reason, bool was_clean) { + m_websocket->on_close = [weak_this = GC::Weak { *this }](auto code, auto reason, bool was_clean) { if (!weak_this) return; auto& websocket = const_cast(*weak_this); websocket.on_close(code, String::from_byte_string(reason).release_value_but_fixme_should_propagate_errors(), was_clean); }; - m_websocket->on_error = [weak_this = make_weak_ptr()](auto) { + m_websocket->on_error = [weak_this = GC::Weak { *this }](auto) { if (!weak_this) return; auto& websocket = const_cast(*weak_this); diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp index 4119454401f..2d03d71a60e 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp @@ -4471,7 +4471,7 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.setter_callback@) // 1. Append a weak reference to element to elements. // 5. Set this's explicitly set attr-elements to elements. attribute_generator.append(R"~~~( - Vector> elements; + Vector> elements; elements.ensure_capacity(cpp_value->size()); for (auto const& element : *cpp_value) { diff --git a/Tests/LibWeb/Crash/CSS/media-query-evaluation-without-window.html b/Tests/LibWeb/Crash/CSS/media-query-evaluation-without-window.html new file mode 100644 index 00000000000..dd4a203cbfd --- /dev/null +++ b/Tests/LibWeb/Crash/CSS/media-query-evaluation-without-window.html @@ -0,0 +1,6 @@ + + + + diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.nonfinite.txt b/Tests/LibWeb/Text/expected/wpt-import/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.nonfinite.txt new file mode 100644 index 00000000000..d4954b563c8 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.nonfinite.txt @@ -0,0 +1,6 @@ +Harness status: OK + +Found 1 tests + +1 Pass +Pass clearRect() with Infinity/NaN is ignored \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/wpt-import/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.nonfinite.html b/Tests/LibWeb/Text/input/wpt-import/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.nonfinite.html new file mode 100644 index 00000000000..57c62f33585 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/canvas/element/drawing-rectangles-to-the-canvas/2d.clearRect.nonfinite.html @@ -0,0 +1,52 @@ + + + +Canvas test: 2d.clearRect.nonfinite + + + + + + +

2d.clearRect.nonfinite

+

clearRect() with Infinity/NaN is ignored

+ + +

Actual output:

+

FAIL (fallback content)

+

Expected output:

+

    + +