Compare commits

..

6 commits

Author SHA1 Message Date
ljamar
7fb65283c2 LibWeb: Ignore non-finite args in CanvasRenderingContext2D::clear_rect() 2025-10-17 17:41:58 +01:00
Callum Law
b1801c0bc9 LibWeb: Avoid crash evaluating media query in document lacking window
In some cases a document may lack an associated window - to fix this for
now we just return false but perhaps there are some media queries we
should still attempt to resolve.
2025-10-17 18:24:59 +02:00
Andreas Kling
7ee8645b9c LibJS: Avoid redundant GC::Weak checks in hot inline cache code paths
By copying out pointees to a temporary, we can avoid some redundant
pointer chasing inside GC::Weak when hitting our inline caches.
2025-10-17 17:22:16 +02:00
Andreas Kling
dfa796a4e4 LibJS+LibWeb: Use GC::Weak instead of AK::WeakPtr for GC-allocated types
This makes some common types like JS::Object smaller (by 8 bytes) and
yields a minor speed improvement on many benchmarks.
2025-10-17 17:22:16 +02:00
Andreas Kling
25a5ed94d6 LibGC: Add GC::Weak<T> as an alternative to AK::WeakPtr<T>
This is a weak pointer that integrates with the garbage collector.
It has a number of differences compared to AK::WeakPtr, including:

- The "control block" is allocated from a well-packed WeakBlock owned by
  the GC heap, not just a generic malloc allocation.

- Pointers to dead cells are nulled out by the garbage collector
  immediately before running destructors.

- It works on any GC::Cell derived type, meaning you don't have to
  inherit from AK::Weakable for the ability to be weakly referenced.

- The Weak always points to a control block, even when "null" (it then
  points to a null WeakImpl), which means one less null check when
  chasing pointers.
2025-10-17 17:22:16 +02:00
Andreas Kling
127208f3d6 LibWeb: Move Document unregistration from EventLoop to finalizer
This ensure that it's not visible to event loops by the time we're
running destructors.
2025-10-17 17:22:16 +02:00
52 changed files with 659 additions and 130 deletions

View file

@ -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)

View file

@ -9,6 +9,7 @@ set(SOURCES
RootVector.cpp
Heap.cpp
HeapBlock.cpp
WeakBlock.cpp
WeakContainer.cpp
)

View file

@ -20,6 +20,7 @@ class Heap;
class HeapBlock;
class NanBoxedValue;
class WeakContainer;
class WeakImpl;
template<typename T>
class Function;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020-2022, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2020-2025, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
@ -20,6 +20,8 @@
#include <LibGC/HeapBlock.h>
#include <LibGC/NanBoxedValue.h>
#include <LibGC/Root.h>
#include <LibGC/Weak.h>
#include <LibGC/WeakInlines.h>
#include <setjmp.h>
#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<WeakBlock&> 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<Cell*>(ptr));
if (!weak_block->can_allocate()) {
m_full_weak_blocks.append(*weak_block);
}
return new_weak_impl;
}
}

View file

@ -25,6 +25,7 @@
#include <LibGC/Root.h>
#include <LibGC/RootHashMap.h>
#include <LibGC/RootVector.h>
#include <LibGC/WeakBlock.h>
#include <LibGC/WeakContainer.h>
namespace GC {
@ -81,6 +82,8 @@ public:
void enqueue_post_gc_task(AK::Function<void()>);
WeakImpl* create_weak_impl(void*);
private:
friend class MarkingVisitor;
friend class GraphConstructorVisitor;
@ -113,6 +116,7 @@ private:
void mark_live_cells(HashMap<Cell*, HeapRoot> 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<void(HashMap<Cell*, GC::HeapRoot>&)> m_gather_embedder_roots;
Vector<AK::Function<void()>> 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>, RootImpl& impl)

166
Libraries/LibGC/Weak.h Normal file
View file

@ -0,0 +1,166 @@
/*
* Copyright (c) 2025, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Badge.h>
#include <AK/RefPtr.h>
#include <LibGC/Export.h>
#include <LibGC/Ptr.h>
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<WeakBlock>, 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<typename T>
class Weak {
public:
constexpr Weak() = default;
Weak(nullptr_t) { }
Weak(T const* ptr);
Weak(T const& ptr);
template<typename U>
Weak(Weak<U> const& other)
requires(IsConvertible<U*, T*>);
Weak(Ref<T> const& other);
template<typename U>
Weak(Ref<U> const& other)
requires(IsConvertible<U*, T*>);
template<typename U>
Weak& operator=(Weak<U> const& other)
requires(IsConvertible<U*, T*>)
{
m_impl = other.impl();
return *this;
}
Weak& operator=(Ref<T> const& other);
template<typename U>
Weak& operator=(Ref<U> const& other)
requires(IsConvertible<U*, T*>);
Weak& operator=(T const& other);
template<typename U>
Weak& operator=(U const& other)
requires(IsConvertible<U*, T*>);
Weak& operator=(T const* other);
template<typename U>
Weak& operator=(U const* other)
requires(IsConvertible<U*, T*>);
T* operator->() const
{
ASSERT(ptr());
return ptr();
}
[[nodiscard]] T& operator*() const
{
ASSERT(ptr());
return *ptr();
}
Ptr<T> ptr() const { return static_cast<T*>(impl().ptr()); }
explicit operator bool() const { return !!ptr(); }
bool operator!() const { return !ptr(); }
operator T*() const { return ptr(); }
Ref<T> as_nonnull() const
{
ASSERT(ptr());
return *ptr();
}
WeakImpl& impl() const { return *m_impl; }
private:
NonnullRefPtr<WeakImpl> m_impl { WeakImpl::the_null_weak_impl };
};
template<typename T, typename U>
inline bool operator==(Weak<T> const& a, Ptr<U> const& b)
{
return a.ptr() == b.ptr();
}
template<typename T, typename U>
inline bool operator==(Weak<T> const& a, Ref<U> const& b)
{
return a.ptr() == b.ptr();
}
}
namespace AK {
template<typename T>
struct Traits<GC::Weak<T>> : public DefaultTraits<GC::Weak<T>> {
static unsigned hash(GC::Weak<T> const& value)
{
return Traits<T*>::hash(value.ptr());
}
};
template<typename T>
struct Formatter<GC::Weak<T>> : Formatter<T const*> {
ErrorOr<void> format(FormatBuilder& builder, GC::Weak<T> const& value)
{
return Formatter<T const*>::format(builder, value.ptr());
}
};
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2025, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGC/Cell.h>
#include <LibGC/WeakBlock.h>
#include <sys/mman.h>
#if defined(AK_OS_WINDOWS)
# include <AK/Windows.h>
# include <memoryapi.h>
#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<WeakImpl*>(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<Cell*>(impl.ptr());
if (!cell || !cell->is_marked())
impl.set_ptr({}, nullptr);
if (impl.ref_count() == 0)
deallocate(&impl);
}
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2025, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/IntrusiveList.h>
#include <LibGC/Forward.h>
#include <LibGC/Weak.h>
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<WeakBlock> 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);
}

View file

@ -0,0 +1,113 @@
/*
* Copyright (c) 2025, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGC/Weak.h>
namespace GC {
template<typename T>
Weak<T>::Weak(T const* ptr)
: m_impl(ptr ? *ptr->heap().create_weak_impl(const_cast<void*>(static_cast<void const*>(ptr))) : WeakImpl::the_null_weak_impl)
{
}
template<typename T>
Weak<T>::Weak(T const& ptr)
: m_impl(*ptr.heap().create_weak_impl(const_cast<void*>(static_cast<void const*>(&ptr))))
{
}
template<typename T>
template<typename U>
Weak<T>::Weak(Weak<U> const& other)
requires(IsConvertible<U*, T*>)
: m_impl(other.impl())
{
}
template<typename T>
Weak<T>::Weak(Ref<T> const& other)
: m_impl(*other.ptr()->heap().create_weak_impl(other.ptr()))
{
}
template<typename T>
template<typename U>
Weak<T>::Weak(Ref<U> const& other)
requires(IsConvertible<U*, T*>)
: m_impl(*other.ptr()->heap().create_weak_impl(other.ptr()))
{
}
template<typename T>
template<typename U>
Weak<T>& Weak<T>::operator=(U const& other)
requires(IsConvertible<U*, T*>)
{
if (ptr() != other) {
m_impl = *other.heap().create_weak_impl(const_cast<void*>(static_cast<void const*>(&other)));
}
return *this;
}
template<typename T>
Weak<T>& Weak<T>::operator=(Ref<T> const& other)
{
if (ptr() != other.ptr()) {
m_impl = *other.ptr()->heap().create_weak_impl(other.ptr());
}
return *this;
}
template<typename T>
template<typename U>
Weak<T>& Weak<T>::operator=(Ref<U> const& other)
requires(IsConvertible<U*, T*>)
{
if (ptr() != other.ptr()) {
m_impl = *other.ptr()->heap().create_weak_impl(other.ptr());
}
return *this;
}
template<typename T>
Weak<T>& Weak<T>::operator=(T const& other)
{
if (ptr() != &other) {
m_impl = *other.heap().create_weak_impl(const_cast<void*>(static_cast<void const*>(&other)));
}
return *this;
}
template<typename T>
Weak<T>& Weak<T>::operator=(T const* other)
{
if (ptr() != other) {
if (other)
m_impl = *other->heap().create_weak_impl(const_cast<void*>(static_cast<void const*>(other)));
else
m_impl = WeakImpl::the_null_weak_impl;
}
return *this;
}
template<typename T>
template<typename U>
Weak<T>& Weak<T>::operator=(U const* other)
requires(IsConvertible<U*, T*>)
{
if (ptr() != other) {
if (other)
m_impl = *other->heap().create_weak_impl(const_cast<void*>(static_cast<void const*>(other)));
else
m_impl = WeakImpl::the_null_weak_impl;
}
return *this;
}
}

View file

@ -10,8 +10,9 @@
#include <AK/NonnullOwnPtr.h>
#include <AK/OwnPtr.h>
#include <AK/Utf16FlyString.h>
#include <AK/WeakPtr.h>
#include <LibGC/CellAllocator.h>
#include <LibGC/Weak.h>
#include <LibGC/WeakInlines.h>
#include <LibJS/Bytecode/IdentifierTable.h>
#include <LibJS/Bytecode/Label.h>
#include <LibJS/Bytecode/StringTable.h>
@ -37,11 +38,11 @@ struct PropertyLookupCache {
GetPropertyInPrototypeChain,
};
Type type { Type::Empty };
WeakPtr<Shape> from_shape;
WeakPtr<Shape> shape;
GC::Weak<Shape> from_shape;
GC::Weak<Shape> shape;
Optional<u32> property_offset;
WeakPtr<Object> prototype;
WeakPtr<PrototypeChainValidity> prototype_chain_validity;
GC::Weak<Object> prototype;
GC::Weak<PrototypeChainValidity> prototype_chain_validity;
};
AK::Array<Entry, max_number_of_shapes_to_remember> entries;
};

View file

@ -1133,20 +1133,22 @@ ThrowCompletionOr<void> 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<void> 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 {};
}

View file

@ -92,19 +92,21 @@ ALWAYS_INLINE ThrowCompletionOr<Value> 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;

View file

@ -9,7 +9,6 @@
#pragma once
#include <AK/WeakPtr.h>
#include <LibJS/Bytecode/BasicBlock.h>
#include <LibJS/Export.h>
#include <LibJS/Forward.h>

View file

@ -64,8 +64,7 @@ struct CacheableSetPropertyMetadata {
GC::Ptr<Object const> prototype;
};
class JS_API Object : public Cell
, public Weakable<Object> {
class JS_API Object : public Cell {
GC_CELL(Object, Cell);
GC_DECLARE_ALLOCATOR(Object);

View file

@ -110,8 +110,8 @@ GC::Ref<Shape> 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<HashMap<TransitionKey, WeakPtr<Shape>>>();
m_forward_transitions->set(key, new_shape.ptr());
m_forward_transitions = make<HashMap<TransitionKey, GC::Weak<Shape>>>();
m_forward_transitions->set(key, new_shape);
}
return new_shape;
}
@ -125,7 +125,7 @@ GC::Ref<Shape> 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<HashMap<TransitionKey, WeakPtr<Shape>>>();
m_forward_transitions = make<HashMap<TransitionKey, GC::Weak<Shape>>>();
m_forward_transitions->set(key, new_shape.ptr());
}
return new_shape;
@ -141,7 +141,7 @@ GC::Ref<Shape> 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<HashMap<GC::Ptr<Object>, WeakPtr<Shape>>>();
m_prototype_transitions = make<HashMap<GC::Ptr<Object>, GC::Weak<Shape>>>();
m_prototype_transitions->set(new_prototype, new_shape.ptr());
}
return new_shape;
@ -279,7 +279,7 @@ GC::Ref<Shape> Shape::create_delete_transition(PropertyKey const& property_key)
auto new_shape = heap().allocate<Shape>(*this, property_key, TransitionType::Delete);
invalidate_prototype_if_needed_for_new_prototype(new_shape);
if (!m_delete_transitions)
m_delete_transitions = make<HashMap<PropertyKey, WeakPtr<Shape>>>();
m_delete_transitions = make<HashMap<PropertyKey, GC::Weak<Shape>>>();
m_delete_transitions->set(property_key, new_shape.ptr());
return new_shape;
}

View file

@ -10,8 +10,8 @@
#include <AK/HashMap.h>
#include <AK/OwnPtr.h>
#include <AK/StringView.h>
#include <AK/WeakPtr.h>
#include <AK/Weakable.h>
#include <LibGC/Weak.h>
#include <LibJS/Export.h>
#include <LibJS/Forward.h>
#include <LibJS/Heap/Cell.h>
@ -36,8 +36,7 @@ struct TransitionKey {
}
};
class PrototypeChainValidity final : public Cell
, public Weakable<PrototypeChainValidity> {
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<Shape> {
class JS_API Shape final : public Cell {
GC_CELL(Shape, Cell);
GC_DECLARE_ALLOCATOR(Shape);
@ -129,9 +127,9 @@ private:
mutable OwnPtr<OrderedHashMap<PropertyKey, PropertyMetadata>> m_property_table;
OwnPtr<HashMap<TransitionKey, WeakPtr<Shape>>> m_forward_transitions;
OwnPtr<HashMap<GC::Ptr<Object>, WeakPtr<Shape>>> m_prototype_transitions;
OwnPtr<HashMap<PropertyKey, WeakPtr<Shape>>> m_delete_transitions;
OwnPtr<HashMap<TransitionKey, GC::Weak<Shape>>> m_forward_transitions;
OwnPtr<HashMap<GC::Ptr<Object>, GC::Weak<Shape>>> m_prototype_transitions;
OwnPtr<HashMap<PropertyKey, GC::Weak<Shape>>> m_delete_transitions;
GC::Ptr<Shape> m_previous;
Optional<PropertyKey> m_property_key;
GC::Ptr<Object> m_prototype;

View file

@ -247,25 +247,25 @@ Vector<String> ARIAMixin::parse_id_reference_list(Optional<String> const& id_lis
ENUMERATE_ARIA_ELEMENT_REFERENCING_ATTRIBUTES
#undef __ENUMERATE_ARIA_ATTRIBUTE
#define __ENUMERATE_ARIA_ATTRIBUTE(attribute, referencing_attribute) \
Optional<Vector<WeakPtr<DOM::Element>>> const& ARIAMixin::attribute() const \
{ \
return m_##attribute; \
} \
\
void ARIAMixin::set_##attribute(Optional<Vector<WeakPtr<DOM::Element>>> value) \
{ \
m_##attribute = move(value); \
} \
\
GC::Ptr<JS::Array> ARIAMixin::cached_##attribute() const \
{ \
return m_cached_##attribute; \
} \
\
void ARIAMixin::set_cached_##attribute(GC::Ptr<JS::Array> value) \
{ \
m_cached_##attribute = value; \
#define __ENUMERATE_ARIA_ATTRIBUTE(attribute, referencing_attribute) \
Optional<Vector<GC::Weak<DOM::Element>>> const& ARIAMixin::attribute() const \
{ \
return m_##attribute; \
} \
\
void ARIAMixin::set_##attribute(Optional<Vector<GC::Weak<DOM::Element>>> value) \
{ \
m_##attribute = move(value); \
} \
\
GC::Ptr<JS::Array> ARIAMixin::cached_##attribute() const \
{ \
return m_cached_##attribute; \
} \
\
void ARIAMixin::set_cached_##attribute(GC::Ptr<JS::Array> value) \
{ \
m_cached_##attribute = value; \
}
ENUMERATE_ARIA_ELEMENT_LIST_REFERENCING_ATTRIBUTES
#undef __ENUMERATE_ARIA_ATTRIBUTE

View file

@ -67,11 +67,11 @@ public:
ENUMERATE_ARIA_ELEMENT_REFERENCING_ATTRIBUTES
#undef __ENUMERATE_ARIA_ATTRIBUTE
#define __ENUMERATE_ARIA_ATTRIBUTE(attribute, referencing_attribute) \
Optional<Vector<WeakPtr<DOM::Element>>> const& attribute() const; \
void set_##attribute(Optional<Vector<WeakPtr<DOM::Element>>>); \
\
GC::Ptr<JS::Array> cached_##attribute() const; \
#define __ENUMERATE_ARIA_ATTRIBUTE(attribute, referencing_attribute) \
Optional<Vector<GC::Weak<DOM::Element>>> const& attribute() const; \
void set_##attribute(Optional<Vector<GC::Weak<DOM::Element>>>); \
\
GC::Ptr<JS::Array> cached_##attribute() const; \
void set_cached_##attribute(GC::Ptr<JS::Array>);
ENUMERATE_ARIA_ELEMENT_LIST_REFERENCING_ATTRIBUTES
#undef __ENUMERATE_ARIA_ATTRIBUTE
@ -85,12 +85,12 @@ protected:
private:
#define __ENUMERATE_ARIA_ATTRIBUTE(attribute, referencing_attribute) \
WeakPtr<DOM::Element> m_##attribute;
GC::Weak<DOM::Element> m_##attribute;
ENUMERATE_ARIA_ELEMENT_REFERENCING_ATTRIBUTES
#undef __ENUMERATE_ARIA_ATTRIBUTE
#define __ENUMERATE_ARIA_ATTRIBUTE(attribute, referencing_attribute) \
Optional<Vector<WeakPtr<DOM::Element>>> m_##attribute; \
Optional<Vector<GC::Weak<DOM::Element>>> m_##attribute; \
GC::Ptr<JS::Array> m_cached_##attribute;
ENUMERATE_ARIA_ELEMENT_LIST_REFERENCING_ATTRIBUTES
#undef __ENUMERATE_ARIA_ATTRIBUTE

View file

@ -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()) {

View file

@ -126,7 +126,7 @@ GC::Ptr<HTML::SharedResourceRequest> 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<DOM::Document>()] {
[document, weak_document = GC::Weak { document }] {
if (!weak_document)
return;

View file

@ -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;

View file

@ -9,6 +9,7 @@
#pragma once
#include <LibGC/Weak.h>
#include <LibJS/Heap/Cell.h>
#include <LibWeb/CSS/Enums.h>
#include <LibWeb/CSS/StyleValues/AbstractImageStyleValue.h>
@ -79,7 +80,7 @@ private:
GC::Ptr<CSSStyleSheet> m_style_sheet;
URL m_url;
WeakPtr<DOM::Document> m_document;
GC::Weak<DOM::Document> m_document;
size_t m_current_frame_index { 0 };
size_t m_loops_completed { 0 };

View file

@ -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<Element&>(*ancestor);
@ -3417,7 +3420,7 @@ void Document::run_the_scroll_steps()
void Document::add_media_query_list(GC::Ref<CSS::MediaQueryList> 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<CSS::MediaQueryList> media_query_list = media_query_list_ptr.ptr();
bool did_match = media_query_list->matches();
@ -6842,7 +6845,7 @@ NonnullRefPtr<CSS::StyleValue const> Document::custom_property_initial_value(Fly
GC::Ptr<Element> 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 {};

View file

@ -681,7 +681,7 @@ public:
WebIDL::ExceptionOr<String> query_command_value(FlyString const& command);
WebIDL::ExceptionOr<GC::Ref<XPath::XPathExpression>> create_expression(String const& expression, GC::Ptr<XPath::XPathNSResolver> resolver = nullptr);
WebIDL::ExceptionOr<GC::Ref<XPath::XPathResult>> evaluate(String const& expression, const DOM::Node& context_node, GC::Ptr<XPath::XPathNSResolver> resolver = nullptr, WebIDL::UnsignedShort type = 0, GC::Ptr<XPath::XPathResult> result = nullptr);
WebIDL::ExceptionOr<GC::Ref<XPath::XPathResult>> evaluate(String const& expression, DOM::Node const& context_node, GC::Ptr<XPath::XPathNSResolver> resolver = nullptr, WebIDL::UnsignedShort type = 0, GC::Ptr<XPath::XPathResult> result = nullptr);
GC::Ref<DOM::Node> create_ns_resolver(GC::Ref<DOM::Node> 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<Node>());
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<EventTarget> 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<WeakPtr<CSS::MediaQueryList>> m_media_query_lists;
Vector<GC::Weak<CSS::MediaQueryList>> 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<Core::Timer> 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<HTML::Navigable> 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<HTML::Navigable> 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<GC::Ptr<ViewTransition::ViewTransition>> m_update_callback_queue = {};
HashTable<WeakPtr<Node>> m_pending_nodes_for_style_invalidation_due_to_presence_of_has;
HashTable<GC::Weak<Node>> m_pending_nodes_for_style_invalidation_due_to_presence_of_has;
GC::Ref<StyleInvalidator> m_style_invalidator;

View file

@ -541,7 +541,7 @@ GC::Ptr<DOM::Element> Element::get_the_attribute_associated_element(FlyString co
}
// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#attr-associated-elements
Optional<GC::RootVector<GC::Ref<DOM::Element>>> Element::get_the_attribute_associated_elements(FlyString const& content_attribute, Optional<Vector<WeakPtr<DOM::Element>>> const& explicitly_set_attribute_elements) const
Optional<GC::RootVector<GC::Ref<DOM::Element>>> Element::get_the_attribute_associated_elements(FlyString const& content_attribute, Optional<Vector<GC::Weak<DOM::Element>>> const& explicitly_set_attribute_elements) const
{
// 1. Let elements be an empty list.
GC::RootVector<GC::Ref<DOM::Element>> elements(heap());

View file

@ -173,7 +173,7 @@ public:
GC::Ptr<Attr> get_attribute_node_ns(Optional<FlyString> const& namespace_, FlyString const& name) const;
GC::Ptr<DOM::Element> get_the_attribute_associated_element(FlyString const& content_attribute, GC::Ptr<DOM::Element> explicitly_set_attribute_element) const;
Optional<GC::RootVector<GC::Ref<DOM::Element>>> get_the_attribute_associated_elements(FlyString const& content_attribute, Optional<Vector<WeakPtr<DOM::Element>>> const& explicitly_set_attribute_elements) const;
Optional<GC::RootVector<GC::Ref<DOM::Element>>> get_the_attribute_associated_elements(FlyString const& content_attribute, Optional<Vector<GC::Weak<DOM::Element>>> const& explicitly_set_attribute_elements) const;
DOMTokenList* class_list();

View file

@ -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<WeakPtr<Element>> {}; });
auto& elements_with_id = m_map.ensure(element_id, [] { return Vector<GC::Weak<Element>> {}; });
// Remove all elements that were deallocated.
elements_with_id.remove_all_matching([](WeakPtr<Element>& element) {
return !element.has_value();
elements_with_id.remove_all_matching([](GC::Weak<Element>& 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> { 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();
});

View file

@ -18,7 +18,7 @@ public:
GC::Ptr<Element> get(FlyString const& element_id) const;
private:
HashMap<FlyString, Vector<WeakPtr<Element>>> m_map;
HashMap<FlyString, Vector<GC::Weak<Element>>> m_map;
};
}

View file

@ -97,7 +97,7 @@ WebIDL::ExceptionOr<void> 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<void> 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<Node>());
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()) {

View file

@ -38,8 +38,8 @@ public:
void disconnect();
Vector<GC::Root<MutationRecord>> take_records();
Vector<WeakPtr<Node>>& node_list() { return m_node_list; }
Vector<WeakPtr<Node>> const& node_list() const { return m_node_list; }
Vector<GC::Weak<Node>>& node_list() { return m_node_list; }
Vector<GC::Weak<Node>> 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<WeakPtr<Node>> m_node_list;
Vector<GC::Weak<Node>> m_node_list;
// https://dom.spec.whatwg.org/#concept-mo-queue
Vector<GC::Ref<MutationRecord>> m_record_queue;

View file

@ -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());

View file

@ -8,9 +8,9 @@
#include <AK/Function.h>
#include <AK/Noncopyable.h>
#include <AK/WeakPtr.h>
#include <LibCore/Forward.h>
#include <LibGC/Ptr.h>
#include <LibGC/Weak.h>
#include <LibJS/Forward.h>
#include <LibWeb/Export.h>
#include <LibWeb/HTML/EventLoop/TaskQueue.h>
@ -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<WeakPtr<DOM::Document>> m_documents;
Vector<GC::Weak<DOM::Document>> 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.

View file

@ -8,7 +8,7 @@
#pragma once
#include <AK/String.h>
#include <AK/WeakPtr.h>
#include <LibGC/Weak.h>
#include <LibWeb/Bindings/HTMLFormElementPrototype.h>
#include <LibWeb/DOM/InputEventsTarget.h>
#include <LibWeb/Export.h>
@ -178,7 +178,7 @@ protected:
private:
void reset_form_owner();
WeakPtr<HTMLFormElement> m_form;
GC::Weak<HTMLFormElement> m_form;
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#parser-inserted-flag
bool m_parser_inserted { false };

View file

@ -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<HTMLInputElement>();
auto weak_element = GC::Weak<HTMLInputElement> { 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<HTMLInputElement>();
auto weak_element = GC::Weak<HTMLInputElement> { 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)));
}

View file

@ -570,7 +570,7 @@ bool HTMLLinkElement::stylesheet_linked_resource_fetch_setup_steps(Fetch::Infras
void HTMLLinkElement::set_parser_document(Badge<HTMLParser>, GC::Ref<DOM::Document> document)
{
m_parser_document = document->make_weak_ptr<DOM::Document>();
m_parser_document = document;
}
bool HTMLLinkElement::is_implicitly_potentially_render_blocking() const

View file

@ -167,7 +167,7 @@ private:
Optional<String> m_mime_type;
WeakPtr<DOM::Document> m_parser_document;
GC::Weak<DOM::Document> m_parser_document;
};
}

View file

@ -480,7 +480,7 @@ void HTMLSelectElement::show_the_picker_if_applicable()
}
// Request select dropdown
auto weak_element = make_weak_ptr<HTMLSelectElement>();
auto weak_element = GC::Weak<HTMLSelectElement> { *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);

View file

@ -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<HTMLTextAreaElement> { *this }]() {
if (!weak_this)
return;
static_cast<HTMLTextAreaElement*>(weak_this.ptr())->queue_firing_input_event();
weak_this->queue_firing_input_event();
}))
{
}

View file

@ -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<Navigable> {
class WEB_API Navigable : public JS::Cell {
GC_CELL(Navigable, JS::Cell);
GC_DECLARE_ALLOCATOR(Navigable);

View file

@ -298,7 +298,7 @@ CSSPixelRect IntersectionObserver::root_intersection_rectangle() const
} else {
document = &intersection_root.get<GC::Root<DOM::Element>>().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<Layout::Node> { return node->layout_node(); })) {
rect.inflate(
m_root_margin[0].to_px(*layout_node, rect.height()),

View file

@ -106,7 +106,7 @@ private:
Vector<GC::Ref<DOM::Element>> m_observation_targets;
// AD-HOC: This is the document where we've registered the IntersectionObserver.
WeakPtr<DOM::Document> m_document;
GC::Weak<DOM::Document> m_document;
};
}

View file

@ -86,7 +86,7 @@ private:
NonnullOwnPtr<DragAndDropEventHandler> m_drag_and_drop_event_handler;
WeakPtr<DOM::EventTarget> m_mousedown_target;
GC::Weak<DOM::EventTarget> m_mousedown_target;
Optional<CSSPixelPoint> m_mousemove_previous_screen_position;

View file

@ -68,7 +68,7 @@ void Page::set_focused_navigable(Badge<EventHandler>, HTML::Navigable& navigable
void Page::navigable_document_destroyed(Badge<DOM::Document>, 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<HTML::HTMLInputElement> target, Color current_color)
void Page::did_request_color_picker(GC::Weak<HTML::HTMLInputElement> 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<Color> picked_color, HTML::ColorPickerUp
auto& input_element = as<HTML::HTMLInputElement>(*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<HTML::HTMLInputElement> target, HTML::FileFilter const& accepted_file_types, HTML::AllowMultipleFiles allow_multiple_files)
void Page::did_request_file_picker(GC::Weak<HTML::HTMLInputElement> 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<HTML::SelectedFile> selected_files)
auto& input_element = as<HTML::HTMLInputElement>(*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<HTML::HTMLSelectElement> target, Web::CSSPixelPoint content_position, Web::CSSPixels minimum_width, Vector<Web::HTML::SelectItem> items)
void Page::did_request_select_dropdown(GC::Weak<HTML::HTMLSelectElement> target, Web::CSSPixelPoint content_position, Web::CSSPixels minimum_width, Vector<Web::HTML::SelectItem> 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<u32> const& selected_item_id)
if (m_pending_non_blocking_dialog_target) {
auto& select_element = as<HTML::HTMLSelectElement>(*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;
}
}
}

View file

@ -9,8 +9,8 @@
#pragma once
#include <AK/WeakPtr.h>
#include <LibGC/Root.h>
#include <LibGC/Weak.h>
#include <LibGfx/Cursor.h>
#include <LibGfx/Forward.h>
#include <LibGfx/Palette.h>
@ -159,13 +159,13 @@ public:
void dismiss_dialog(GC::Ref<GC::Function<void()>> on_dialog_closed);
void accept_dialog(GC::Ref<GC::Function<void()>> on_dialog_closed);
void did_request_color_picker(WeakPtr<HTML::HTMLInputElement> target, Color current_color);
void did_request_color_picker(GC::Weak<HTML::HTMLInputElement> target, Color current_color);
void color_picker_update(Optional<Color> picked_color, HTML::ColorPickerUpdateState state);
void did_request_file_picker(WeakPtr<HTML::HTMLInputElement> target, HTML::FileFilter const& accepted_file_types, HTML::AllowMultipleFiles);
void did_request_file_picker(GC::Weak<HTML::HTMLInputElement> target, HTML::FileFilter const& accepted_file_types, HTML::AllowMultipleFiles);
void file_picker_closed(Span<HTML::SelectedFile> selected_files);
void did_request_select_dropdown(WeakPtr<HTML::HTMLSelectElement> target, Web::CSSPixelPoint content_position, Web::CSSPixels minimum_width, Vector<Web::HTML::SelectItem> items);
void did_request_select_dropdown(GC::Weak<HTML::HTMLSelectElement> target, Web::CSSPixelPoint content_position, Web::CSSPixels minimum_width, Vector<Web::HTML::SelectItem> items);
void select_dropdown_closed(Optional<u32> const& selected_item_id);
using ClipboardRequest = GC::Ref<GC::Function<void(Vector<Clipboard::SystemClipboardItem>)>>;
@ -246,7 +246,7 @@ private:
GC::Ref<PageClient> m_client;
WeakPtr<HTML::Navigable> m_focused_navigable;
GC::Weak<HTML::Navigable> m_focused_navigable;
GC::Ptr<HTML::TraversableNavigable> m_top_level_traversable;
@ -275,7 +275,7 @@ private:
GC::Ptr<GC::Function<void()>> m_on_pending_dialog_closed;
PendingNonBlockingDialog m_pending_non_blocking_dialog { PendingNonBlockingDialog::None };
WeakPtr<HTML::HTMLElement> m_pending_non_blocking_dialog_target;
GC::Weak<HTML::HTMLElement> m_pending_non_blocking_dialog_target;
HashMap<u64, ClipboardRequest> m_pending_clipboard_requests;
u64 m_next_clipboard_request_id { 0 };

View file

@ -25,8 +25,7 @@ namespace Web::Painting {
WEB_API void set_paint_viewport_scrollbars(bool enabled);
class WEB_API PaintableBox : public Paintable
, public Weakable<PaintableBox> {
class WEB_API PaintableBox : public Paintable {
GC_CELL(PaintableBox, Paintable);
public:

View file

@ -6,7 +6,7 @@
#pragma once
#include <AK/WeakPtr.h>
#include <LibGC/Weak.h>
#include <LibWeb/Forward.h>
#include <LibWeb/PixelUnits.h>
@ -40,7 +40,7 @@ public:
}
private:
WeakPtr<PaintableBox> m_paintable_box;
GC::Weak<PaintableBox> m_paintable_box;
size_t m_id { 0 };
bool m_sticky { false };
RefPtr<ScrollFrame const> m_parent;

View file

@ -52,7 +52,7 @@ private:
Vector<GC::Ref<ResizeObservation>> m_skipped_targets;
// AD-HOC: This is the document where we've registered the observer.
WeakPtr<DOM::Document> m_document;
GC::Weak<DOM::Document> m_document;
IntrusiveListNode<ResizeObserver> m_list_node;

View file

@ -218,25 +218,25 @@ ErrorOr<void> 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<WebSocket>()] {
m_websocket->on_open = [weak_this = GC::Weak { *this }] {
if (!weak_this)
return;
auto& websocket = const_cast<WebSocket&>(*weak_this);
websocket.on_open();
};
m_websocket->on_message = [weak_this = make_weak_ptr<WebSocket>()](auto message) {
m_websocket->on_message = [weak_this = GC::Weak { *this }](auto message) {
if (!weak_this)
return;
auto& websocket = const_cast<WebSocket&>(*weak_this);
websocket.on_message(move(message.data), message.is_text);
};
m_websocket->on_close = [weak_this = make_weak_ptr<WebSocket>()](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<WebSocket&>(*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<WebSocket>()](auto) {
m_websocket->on_error = [weak_this = GC::Weak { *this }](auto) {
if (!weak_this)
return;
auto& websocket = const_cast<WebSocket&>(*weak_this);

View file

@ -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<WeakPtr<DOM::Element>> elements;
Vector<GC::Weak<DOM::Element>> elements;
elements.ensure_capacity(cpp_value->size());
for (auto const& element : *cpp_value) {

View file

@ -0,0 +1,6 @@
<!DOCTYPE html>
<html>
<script>
const dom = new DOMParser().parseFromString(`<style media="width < 50px">`, "text/html");
</script>
</html>

View file

@ -0,0 +1,6 @@
Harness status: OK
Found 1 tests
1 Pass
Pass clearRect() with Infinity/NaN is ignored

View file

@ -0,0 +1,52 @@
<!DOCTYPE html>
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
<meta charset="UTF-8">
<title>Canvas test: 2d.clearRect.nonfinite</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
<body class="show_output">
<h1>2d.clearRect.nonfinite</h1>
<p class="desc">clearRect() with Infinity/NaN is ignored</p>
<p class="output">Actual output:</p>
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
<p class="output expectedtext">Expected output:<p><img src="../../../../images/green-100x50.png" class="output expected" id="expected" alt="">
<ul id="d"></ul>
<script>
var t = async_test("clearRect() with Infinity/NaN is ignored");
_addTest(function(canvas, ctx) {
ctx.fillStyle = '#0f0';
ctx.fillRect(0, 0, 100, 50);
ctx.clearRect(Infinity, 0, 100, 50);
ctx.clearRect(-Infinity, 0, 100, 50);
ctx.clearRect(NaN, 0, 100, 50);
ctx.clearRect(0, Infinity, 100, 50);
ctx.clearRect(0, -Infinity, 100, 50);
ctx.clearRect(0, NaN, 100, 50);
ctx.clearRect(0, 0, Infinity, 50);
ctx.clearRect(0, 0, -Infinity, 50);
ctx.clearRect(0, 0, NaN, 50);
ctx.clearRect(0, 0, 100, Infinity);
ctx.clearRect(0, 0, 100, -Infinity);
ctx.clearRect(0, 0, 100, NaN);
ctx.clearRect(Infinity, Infinity, 100, 50);
ctx.clearRect(Infinity, Infinity, Infinity, 50);
ctx.clearRect(Infinity, Infinity, Infinity, Infinity);
ctx.clearRect(Infinity, Infinity, 100, Infinity);
ctx.clearRect(Infinity, 0, Infinity, 50);
ctx.clearRect(Infinity, 0, Infinity, Infinity);
ctx.clearRect(Infinity, 0, 100, Infinity);
ctx.clearRect(0, Infinity, Infinity, 50);
ctx.clearRect(0, Infinity, Infinity, Infinity);
ctx.clearRect(0, Infinity, 100, Infinity);
ctx.clearRect(0, 0, Infinity, Infinity);
_assertPixel(canvas, 50,25, 0,255,0,255);
});
</script>