mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-10-19 07:33:20 +00:00
Compare commits
6 commits
1a3635cda5
...
7fb65283c2
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7fb65283c2 | ||
![]() |
b1801c0bc9 | ||
![]() |
7ee8645b9c | ||
![]() |
dfa796a4e4 | ||
![]() |
25a5ed94d6 | ||
![]() |
127208f3d6 |
52 changed files with 659 additions and 130 deletions
|
@ -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)
|
||||
|
|
|
@ -9,6 +9,7 @@ set(SOURCES
|
|||
RootVector.cpp
|
||||
Heap.cpp
|
||||
HeapBlock.cpp
|
||||
WeakBlock.cpp
|
||||
WeakContainer.cpp
|
||||
)
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ class Heap;
|
|||
class HeapBlock;
|
||||
class NanBoxedValue;
|
||||
class WeakContainer;
|
||||
class WeakImpl;
|
||||
|
||||
template<typename T>
|
||||
class Function;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
166
Libraries/LibGC/Weak.h
Normal 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());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
77
Libraries/LibGC/WeakBlock.cpp
Normal file
77
Libraries/LibGC/WeakBlock.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
45
Libraries/LibGC/WeakBlock.h
Normal file
45
Libraries/LibGC/WeakBlock.h
Normal 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);
|
||||
|
||||
}
|
113
Libraries/LibGC/WeakInlines.h
Normal file
113
Libraries/LibGC/WeakInlines.h
Normal 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/WeakPtr.h>
|
||||
#include <LibJS/Bytecode/BasicBlock.h>
|
||||
#include <LibJS/Export.h>
|
||||
#include <LibJS/Forward.h>
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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 {};
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -167,7 +167,7 @@ private:
|
|||
|
||||
Optional<String> m_mime_type;
|
||||
|
||||
WeakPtr<DOM::Document> m_parser_document;
|
||||
GC::Weak<DOM::Document> m_parser_document;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}))
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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()),
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<script>
|
||||
const dom = new DOMParser().parseFromString(`<style media="width < 50px">`, "text/html");
|
||||
</script>
|
||||
</html>
|
|
@ -0,0 +1,6 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 1 tests
|
||||
|
||||
1 Pass
|
||||
Pass clearRect() with Infinity/NaN is ignored
|
|
@ -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>
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue