mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-21 03:55:24 +00:00
Merge branch 'LadybirdBrowser:master' into master
This commit is contained in:
commit
4f403913cd
234 changed files with 7866 additions and 1153 deletions
|
@ -114,6 +114,10 @@
|
|||
# cmakedefine01 ICO_DEBUG
|
||||
#endif
|
||||
|
||||
#ifndef IDB_DEBUG
|
||||
# cmakedefine01 IDB_DEBUG
|
||||
#endif
|
||||
|
||||
#ifndef IDL_DEBUG
|
||||
# cmakedefine01 IDL_DEBUG
|
||||
#endif
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <LibCore/EventLoopImplementationWindows.h>
|
||||
#include <LibCore/Notifier.h>
|
||||
#include <LibCore/ThreadEventQueue.h>
|
||||
#include <LibCore/Timer.h>
|
||||
|
||||
#include <AK/Windows.h>
|
||||
|
||||
|
@ -55,10 +56,12 @@ struct EventLoopTimer {
|
|||
};
|
||||
|
||||
struct ThreadData {
|
||||
static ThreadData& the()
|
||||
static ThreadData* the()
|
||||
{
|
||||
thread_local OwnPtr<ThreadData> thread_data = make<ThreadData>();
|
||||
return *thread_data;
|
||||
if (thread_data)
|
||||
return &*thread_data;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ThreadData()
|
||||
|
@ -76,7 +79,7 @@ struct ThreadData {
|
|||
};
|
||||
|
||||
EventLoopImplementationWindows::EventLoopImplementationWindows()
|
||||
: m_wake_event(ThreadData::the().wake_event.handle)
|
||||
: m_wake_event(ThreadData::the()->wake_event.handle)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -92,9 +95,10 @@ int EventLoopImplementationWindows::exec()
|
|||
|
||||
size_t EventLoopImplementationWindows::pump(PumpMode)
|
||||
{
|
||||
auto& thread_data = ThreadData::the();
|
||||
auto& notifiers = thread_data.notifiers;
|
||||
auto& timers = thread_data.timers;
|
||||
auto& event_queue = ThreadEventQueue::current();
|
||||
auto* thread_data = ThreadData::the();
|
||||
auto& notifiers = thread_data->notifiers;
|
||||
auto& timers = thread_data->timers;
|
||||
|
||||
size_t event_count = 1 + notifiers.size() + timers.size();
|
||||
// If 64 events limit proves to be insufficient RegisterWaitForSingleObject or other methods
|
||||
|
@ -103,30 +107,41 @@ size_t EventLoopImplementationWindows::pump(PumpMode)
|
|||
VERIFY(event_count <= MAXIMUM_WAIT_OBJECTS);
|
||||
|
||||
Vector<HANDLE, MAXIMUM_WAIT_OBJECTS> event_handles;
|
||||
event_handles.append(thread_data.wake_event.handle);
|
||||
event_handles.append(thread_data->wake_event.handle);
|
||||
|
||||
for (auto& entry : notifiers)
|
||||
event_handles.append(entry.key.handle);
|
||||
for (auto& entry : timers)
|
||||
event_handles.append(entry.key.handle);
|
||||
|
||||
DWORD result = WaitForMultipleObjects(event_count, event_handles.data(), FALSE, INFINITE);
|
||||
size_t index = result - WAIT_OBJECT_0;
|
||||
VERIFY(index < event_count);
|
||||
|
||||
if (index != 0) {
|
||||
if (index <= notifiers.size()) {
|
||||
Notifier* notifier = *notifiers.get(event_handles[index]);
|
||||
ThreadEventQueue::current().post_event(*notifier, make<NotifierActivationEvent>(notifier->fd(), notifier->type()));
|
||||
} else {
|
||||
auto& timer = *timers.get(event_handles[index]);
|
||||
if (auto strong_owner = timer.owner.strong_ref())
|
||||
if (timer.fire_when_not_visible == TimerShouldFireWhenNotVisible::Yes || strong_owner->is_visible_for_timer_purposes())
|
||||
ThreadEventQueue::current().post_event(*strong_owner, make<TimerEvent>());
|
||||
bool has_pending_events = event_queue.has_pending_events();
|
||||
int timeout = has_pending_events ? 0 : INFINITE;
|
||||
DWORD result = WaitForMultipleObjects(event_count, event_handles.data(), FALSE, timeout);
|
||||
if (result == WAIT_TIMEOUT) {
|
||||
// FIXME: This verification sometimes fails with ERROR_INVALID_HANDLE, but when I check
|
||||
// the handles they all seem to be valid.
|
||||
// VERIFY(GetLastError() == ERROR_SUCCESS || GetLastError() == ERROR_IO_PENDING);
|
||||
} else {
|
||||
size_t const index = result - WAIT_OBJECT_0;
|
||||
VERIFY(index < event_count);
|
||||
// : 1 - skip wake event
|
||||
for (size_t i = index ? index : 1; i < event_count; i++) {
|
||||
// i == index already checked by WaitForMultipleObjects
|
||||
if (i == index || WaitForSingleObject(event_handles[i], 0) == WAIT_OBJECT_0) {
|
||||
if (i <= notifiers.size()) {
|
||||
Notifier* notifier = *notifiers.get(event_handles[i]);
|
||||
event_queue.post_event(*notifier, make<NotifierActivationEvent>(notifier->fd(), notifier->type()));
|
||||
} else {
|
||||
auto& timer = *timers.get(event_handles[i]);
|
||||
if (auto strong_owner = timer.owner.strong_ref())
|
||||
if (timer.fire_when_not_visible == TimerShouldFireWhenNotVisible::Yes || strong_owner->is_visible_for_timer_purposes())
|
||||
event_queue.post_event(*strong_owner, make<TimerEvent>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ThreadEventQueue::current().process();
|
||||
return event_queue.process();
|
||||
}
|
||||
|
||||
void EventLoopImplementationWindows::quit(int code)
|
||||
|
@ -167,7 +182,7 @@ void EventLoopManagerWindows::register_notifier(Notifier& notifier)
|
|||
int rc = WSAEventSelect(notifier.fd(), event, notifier_type_to_network_event(notifier.type()));
|
||||
VERIFY(!rc);
|
||||
|
||||
auto& notifiers = ThreadData::the().notifiers;
|
||||
auto& notifiers = ThreadData::the()->notifiers;
|
||||
VERIFY(!notifiers.get(event).has_value());
|
||||
notifiers.set(Handle(event), ¬ifier);
|
||||
}
|
||||
|
@ -175,13 +190,16 @@ void EventLoopManagerWindows::register_notifier(Notifier& notifier)
|
|||
void EventLoopManagerWindows::unregister_notifier(Notifier& notifier)
|
||||
{
|
||||
// remove_first_matching would be clearer, but currently there is no such method in HashMap
|
||||
ThreadData::the().notifiers.remove_all_matching([&](auto&, auto value) { return value == ¬ifier; });
|
||||
if (ThreadData::the())
|
||||
ThreadData::the()->notifiers.remove_all_matching([&](auto&, auto value) { return value == ¬ifier; });
|
||||
}
|
||||
|
||||
intptr_t EventLoopManagerWindows::register_timer(EventReceiver& object, int milliseconds, bool should_reload, TimerShouldFireWhenNotVisible fire_when_not_visible)
|
||||
{
|
||||
VERIFY(milliseconds >= 0);
|
||||
HANDLE timer = CreateWaitableTimer(NULL, FALSE, NULL);
|
||||
// FIXME: This is a temporary fix for issue #3641
|
||||
bool manual_reset = static_cast<Timer&>(object).is_single_shot();
|
||||
HANDLE timer = CreateWaitableTimer(NULL, manual_reset, NULL);
|
||||
VERIFY(timer);
|
||||
|
||||
LARGE_INTEGER first_time = {};
|
||||
|
@ -190,15 +208,16 @@ intptr_t EventLoopManagerWindows::register_timer(EventReceiver& object, int mill
|
|||
BOOL rc = SetWaitableTimer(timer, &first_time, should_reload ? milliseconds : 0, NULL, NULL, FALSE);
|
||||
VERIFY(rc);
|
||||
|
||||
auto& timers = ThreadData::the().timers;
|
||||
auto& timers = ThreadData::the()->timers;
|
||||
VERIFY(!timers.get(timer).has_value());
|
||||
timers.set(Handle(timer), { object, fire_when_not_visible });
|
||||
return (intptr_t)timer;
|
||||
return reinterpret_cast<intptr_t>(timer);
|
||||
}
|
||||
|
||||
void EventLoopManagerWindows::unregister_timer(intptr_t timer_id)
|
||||
{
|
||||
ThreadData::the().timers.remove((HANDLE)timer_id);
|
||||
if (ThreadData::the())
|
||||
ThreadData::the()->timers.remove(reinterpret_cast<HANDLE>(timer_id));
|
||||
}
|
||||
|
||||
int EventLoopManagerWindows::register_signal([[maybe_unused]] int signal_number, [[maybe_unused]] Function<void(int)> handler)
|
||||
|
|
|
@ -107,8 +107,8 @@ public:
|
|||
visit(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void visit(Vector<T> const& vector)
|
||||
template<typename T, size_t inline_capacity>
|
||||
void visit(Vector<T, inline_capacity> const& vector)
|
||||
{
|
||||
for (auto& value : vector)
|
||||
visit(value);
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <LibIPC/Connection.h>
|
||||
#include <LibIPC/Message.h>
|
||||
#include <LibIPC/Stub.h>
|
||||
#include <LibIPC/UnprocessedFileDescriptors.h>
|
||||
|
||||
namespace IPC {
|
||||
|
||||
|
@ -40,21 +39,16 @@ bool ConnectionBase::is_open() const
|
|||
|
||||
ErrorOr<void> ConnectionBase::post_message(Message const& message)
|
||||
{
|
||||
return post_message(message.endpoint_magic(), TRY(message.encode()));
|
||||
return post_message(TRY(message.encode()));
|
||||
}
|
||||
|
||||
ErrorOr<void> ConnectionBase::post_message(u32 endpoint_magic, MessageBuffer buffer)
|
||||
ErrorOr<void> ConnectionBase::post_message(MessageBuffer buffer)
|
||||
{
|
||||
// NOTE: If this connection is being shut down, but has not yet been destroyed,
|
||||
// the socket will be closed. Don't try to send more messages.
|
||||
if (!m_transport->is_open())
|
||||
return Error::from_string_literal("Trying to post_message during IPC shutdown");
|
||||
|
||||
if (buffer.data().size() > TransportSocket::SOCKET_BUFFER_SIZE) {
|
||||
auto wrapper = LargeMessageWrapper::create(endpoint_magic, buffer);
|
||||
buffer = MUST(wrapper->encode());
|
||||
}
|
||||
|
||||
MUST(buffer.transfer_message(*m_transport));
|
||||
|
||||
m_responsiveness_timer->start();
|
||||
|
@ -85,7 +79,7 @@ void ConnectionBase::handle_messages()
|
|||
}
|
||||
|
||||
if (auto response = handler_result.release_value()) {
|
||||
if (auto post_result = post_message(m_local_endpoint_magic, *response); post_result.is_error()) {
|
||||
if (auto post_result = post_message(*response); post_result.is_error()) {
|
||||
dbgln("IPC::ConnectionBase::handle_messages: {}", post_result.error());
|
||||
}
|
||||
}
|
||||
|
@ -100,24 +94,11 @@ void ConnectionBase::wait_for_transport_to_become_readable()
|
|||
|
||||
ErrorOr<void> ConnectionBase::drain_messages_from_peer()
|
||||
{
|
||||
auto schedule_shutdown = m_transport->read_as_many_messages_as_possible_without_blocking([&](auto&& unparsed_message) {
|
||||
auto const& bytes = unparsed_message.bytes;
|
||||
UnprocessedFileDescriptors unprocessed_fds;
|
||||
unprocessed_fds.return_fds_to_front_of_queue(move(unparsed_message.fds));
|
||||
if (auto message = try_parse_message(bytes, unprocessed_fds)) {
|
||||
if (message->message_id() == LargeMessageWrapper::MESSAGE_ID) {
|
||||
LargeMessageWrapper* wrapper = static_cast<LargeMessageWrapper*>(message.ptr());
|
||||
auto wrapped_message = wrapper->wrapped_message_data();
|
||||
unprocessed_fds.return_fds_to_front_of_queue(wrapper->take_fds());
|
||||
auto parsed_message = try_parse_message(wrapped_message, unprocessed_fds);
|
||||
VERIFY(parsed_message);
|
||||
m_unprocessed_messages.append(parsed_message.release_nonnull());
|
||||
return;
|
||||
}
|
||||
|
||||
auto schedule_shutdown = m_transport->read_as_many_messages_as_possible_without_blocking([&](auto&& raw_message) {
|
||||
if (auto message = try_parse_message(raw_message.bytes, raw_message.fds)) {
|
||||
m_unprocessed_messages.append(message.release_nonnull());
|
||||
} else {
|
||||
dbgln("Failed to parse IPC message {:hex-dump}", bytes);
|
||||
dbgln("Failed to parse IPC message {:hex-dump}", raw_message.bytes);
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -15,10 +15,6 @@
|
|||
#include <LibIPC/Forward.h>
|
||||
#include <LibIPC/Message.h>
|
||||
#include <LibIPC/Transport.h>
|
||||
#include <LibIPC/UnprocessedFileDescriptors.h>
|
||||
#include <LibThreading/ConditionVariable.h>
|
||||
#include <LibThreading/MutexProtected.h>
|
||||
#include <LibThreading/Thread.h>
|
||||
|
||||
namespace IPC {
|
||||
|
||||
|
@ -30,7 +26,7 @@ public:
|
|||
|
||||
[[nodiscard]] bool is_open() const;
|
||||
ErrorOr<void> post_message(Message const&);
|
||||
ErrorOr<void> post_message(u32 endpoint_magic, MessageBuffer);
|
||||
ErrorOr<void> post_message(MessageBuffer);
|
||||
|
||||
void shutdown();
|
||||
virtual void die() { }
|
||||
|
@ -43,7 +39,7 @@ protected:
|
|||
virtual void may_have_become_unresponsive() { }
|
||||
virtual void did_become_responsive() { }
|
||||
virtual void shutdown_with_error(Error const&);
|
||||
virtual OwnPtr<Message> try_parse_message(ReadonlyBytes, UnprocessedFileDescriptors&) = 0;
|
||||
virtual OwnPtr<Message> try_parse_message(ReadonlyBytes, Queue<File>&) = 0;
|
||||
|
||||
OwnPtr<IPC::Message> wait_for_specific_endpoint_message_impl(u32 endpoint_magic, int message_id);
|
||||
void wait_for_transport_to_become_readable();
|
||||
|
@ -102,7 +98,7 @@ protected:
|
|||
return {};
|
||||
}
|
||||
|
||||
virtual OwnPtr<Message> try_parse_message(ReadonlyBytes bytes, UnprocessedFileDescriptors& fds) override
|
||||
virtual OwnPtr<Message> try_parse_message(ReadonlyBytes bytes, Queue<File>& fds) override
|
||||
{
|
||||
auto local_message = LocalEndpoint::decode_message(bytes, fds);
|
||||
if (!local_message.is_error())
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include <LibIPC/File.h>
|
||||
#include <LibIPC/Forward.h>
|
||||
#include <LibIPC/Message.h>
|
||||
#include <LibIPC/UnprocessedFileDescriptors.h>
|
||||
#include <LibURL/Origin.h>
|
||||
#include <LibURL/URL.h>
|
||||
|
||||
|
@ -38,7 +37,7 @@ inline ErrorOr<T> decode(Decoder&)
|
|||
|
||||
class Decoder {
|
||||
public:
|
||||
Decoder(Stream& stream, UnprocessedFileDescriptors& files)
|
||||
Decoder(Stream& stream, Queue<File>& files)
|
||||
: m_stream(stream)
|
||||
, m_files(files)
|
||||
{
|
||||
|
@ -63,11 +62,11 @@ public:
|
|||
ErrorOr<size_t> decode_size();
|
||||
|
||||
Stream& stream() { return m_stream; }
|
||||
UnprocessedFileDescriptors& files() { return m_files; }
|
||||
Queue<File>& files() { return m_files; }
|
||||
|
||||
private:
|
||||
Stream& m_stream;
|
||||
UnprocessedFileDescriptors& m_files;
|
||||
Queue<File>& m_files;
|
||||
};
|
||||
|
||||
template<Arithmetic T>
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#include <AK/Checked.h>
|
||||
#include <LibIPC/Decoder.h>
|
||||
#include <LibIPC/Encoder.h>
|
||||
#include <LibIPC/Message.h>
|
||||
|
||||
namespace IPC {
|
||||
|
@ -47,53 +46,4 @@ ErrorOr<void> MessageBuffer::transfer_message(Transport& transport)
|
|||
return {};
|
||||
}
|
||||
|
||||
NonnullOwnPtr<LargeMessageWrapper> LargeMessageWrapper::create(u32 endpoint_magic, MessageBuffer& buffer_to_wrap)
|
||||
{
|
||||
auto size = buffer_to_wrap.data().size();
|
||||
auto wrapped_message_data = MUST(Core::AnonymousBuffer::create_with_size(size));
|
||||
memcpy(wrapped_message_data.data<void>(), buffer_to_wrap.data().data(), size);
|
||||
Vector<File> files;
|
||||
for (auto& owned_fd : buffer_to_wrap.take_fds()) {
|
||||
files.append(File::adopt_fd(owned_fd->take_fd()));
|
||||
}
|
||||
return make<LargeMessageWrapper>(endpoint_magic, move(wrapped_message_data), move(files));
|
||||
}
|
||||
|
||||
LargeMessageWrapper::LargeMessageWrapper(u32 endpoint_magic, Core::AnonymousBuffer wrapped_message_data, Vector<File>&& wrapped_fds)
|
||||
: m_endpoint_magic(endpoint_magic)
|
||||
, m_wrapped_message_data(move(wrapped_message_data))
|
||||
, m_wrapped_fds(move(wrapped_fds))
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<MessageBuffer> LargeMessageWrapper::encode() const
|
||||
{
|
||||
MessageBuffer buffer;
|
||||
Encoder stream { buffer };
|
||||
TRY(stream.encode(m_endpoint_magic));
|
||||
TRY(stream.encode(MESSAGE_ID));
|
||||
TRY(stream.encode(m_wrapped_message_data));
|
||||
TRY(stream.encode(m_wrapped_fds.size()));
|
||||
for (auto const& wrapped_fd : m_wrapped_fds) {
|
||||
TRY(stream.append_file_descriptor(wrapped_fd.take_fd()));
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<LargeMessageWrapper>> LargeMessageWrapper::decode(u32 endpoint_magic, Stream& stream, UnprocessedFileDescriptors& files)
|
||||
{
|
||||
Decoder decoder { stream, files };
|
||||
auto wrapped_message_data = TRY(decoder.decode<Core::AnonymousBuffer>());
|
||||
|
||||
Vector<File> wrapped_fds;
|
||||
auto num_fds = TRY(decoder.decode<u32>());
|
||||
for (u32 i = 0; i < num_fds; ++i) {
|
||||
auto fd = TRY(decoder.decode<IPC::File>());
|
||||
wrapped_fds.append(move(fd));
|
||||
}
|
||||
|
||||
return make<LargeMessageWrapper>(endpoint_magic, wrapped_message_data, move(wrapped_fds));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,14 +8,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Error.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibCore/AnonymousBuffer.h>
|
||||
#include <LibCore/Forward.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibIPC/Transport.h>
|
||||
#include <LibIPC/UnprocessedFileDescriptors.h>
|
||||
|
||||
namespace IPC {
|
||||
|
||||
|
@ -67,30 +61,4 @@ protected:
|
|||
Message() = default;
|
||||
};
|
||||
|
||||
class LargeMessageWrapper : public Message {
|
||||
public:
|
||||
~LargeMessageWrapper() override = default;
|
||||
|
||||
static constexpr int MESSAGE_ID = 0x0;
|
||||
|
||||
static NonnullOwnPtr<LargeMessageWrapper> create(u32 endpoint_magic, MessageBuffer& buffer_to_wrap);
|
||||
|
||||
u32 endpoint_magic() const override { return m_endpoint_magic; }
|
||||
int message_id() const override { return MESSAGE_ID; }
|
||||
char const* message_name() const override { return "LargeMessageWrapper"; }
|
||||
ErrorOr<MessageBuffer> encode() const override;
|
||||
|
||||
static ErrorOr<NonnullOwnPtr<LargeMessageWrapper>> decode(u32 endpoint_magic, Stream& stream, UnprocessedFileDescriptors& files);
|
||||
|
||||
ReadonlyBytes wrapped_message_data() const { return ReadonlyBytes { m_wrapped_message_data.data<u8>(), m_wrapped_message_data.size() }; }
|
||||
auto take_fds() { return move(m_wrapped_fds); }
|
||||
|
||||
LargeMessageWrapper(u32 endpoint_magic, Core::AnonymousBuffer wrapped_message_data, Vector<IPC::File>&& wrapped_fds);
|
||||
|
||||
private:
|
||||
u32 m_endpoint_magic { 0 };
|
||||
Core::AnonymousBuffer m_wrapped_message_data;
|
||||
Vector<File> m_wrapped_fds;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -13,26 +13,80 @@
|
|||
|
||||
namespace IPC {
|
||||
|
||||
void SendQueue::enqueue_message(Vector<u8>&& bytes, Vector<int>&& fds)
|
||||
{
|
||||
Threading::MutexLocker locker(m_mutex);
|
||||
m_bytes.append(bytes.data(), bytes.size());
|
||||
m_fds.append(fds.data(), fds.size());
|
||||
m_condition.signal();
|
||||
}
|
||||
|
||||
SendQueue::Running SendQueue::block_until_message_enqueued()
|
||||
{
|
||||
Threading::MutexLocker locker(m_mutex);
|
||||
while (m_bytes.is_empty() && m_fds.is_empty() && m_running)
|
||||
m_condition.wait();
|
||||
return m_running ? Running::Yes : Running::No;
|
||||
}
|
||||
|
||||
SendQueue::BytesAndFds SendQueue::dequeue(size_t max_bytes)
|
||||
{
|
||||
Threading::MutexLocker locker(m_mutex);
|
||||
auto bytes_to_send = min(max_bytes, m_bytes.size());
|
||||
Vector<u8> bytes;
|
||||
bytes.append(m_bytes.data(), bytes_to_send);
|
||||
m_bytes.remove(0, bytes_to_send);
|
||||
return { move(bytes), move(m_fds) };
|
||||
}
|
||||
|
||||
void SendQueue::return_unsent_data_to_front_of_queue(ReadonlyBytes const& bytes, Vector<int> const& fds)
|
||||
{
|
||||
Threading::MutexLocker locker(m_mutex);
|
||||
m_bytes.prepend(bytes.data(), bytes.size());
|
||||
m_fds.prepend(fds.data(), fds.size());
|
||||
}
|
||||
|
||||
void SendQueue::stop()
|
||||
{
|
||||
Threading::MutexLocker locker(m_mutex);
|
||||
m_running = false;
|
||||
m_condition.signal();
|
||||
}
|
||||
|
||||
TransportSocket::TransportSocket(NonnullOwnPtr<Core::LocalSocket> socket)
|
||||
: m_socket(move(socket))
|
||||
{
|
||||
m_send_queue = adopt_ref(*new SendQueue);
|
||||
m_send_thread = Threading::Thread::construct([this, send_queue = m_send_queue]() -> intptr_t {
|
||||
for (;;) {
|
||||
send_queue->mutex.lock();
|
||||
while (send_queue->messages.is_empty() && send_queue->running)
|
||||
send_queue->condition.wait();
|
||||
|
||||
if (!send_queue->running) {
|
||||
send_queue->mutex.unlock();
|
||||
if (send_queue->block_until_message_enqueued() == SendQueue::Running::No)
|
||||
break;
|
||||
|
||||
auto [bytes, fds] = send_queue->dequeue(4096);
|
||||
ReadonlyBytes remaining_to_send_bytes = bytes;
|
||||
|
||||
Threading::RWLockLocker<Threading::LockMode::Read> lock(m_socket_rw_lock);
|
||||
auto result = send_message(*m_socket, remaining_to_send_bytes, fds);
|
||||
if (result.is_error()) {
|
||||
dbgln("TransportSocket::send_thread: {}", result.error());
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
auto [bytes, fds] = send_queue->messages.take_first();
|
||||
send_queue->mutex.unlock();
|
||||
if (!remaining_to_send_bytes.is_empty() || !fds.is_empty()) {
|
||||
send_queue->return_unsent_data_to_front_of_queue(remaining_to_send_bytes, fds);
|
||||
}
|
||||
|
||||
if (auto result = send_message(*m_socket, bytes, fds); result.is_error()) {
|
||||
dbgln("TransportSocket::send_thread: {}", result.error());
|
||||
if (!m_socket->is_open())
|
||||
break;
|
||||
|
||||
{
|
||||
Vector<struct pollfd, 1> pollfds;
|
||||
pollfds.append({ .fd = m_socket->fd().value(), .events = POLLOUT, .revents = 0 });
|
||||
|
||||
ErrorOr<int> result { 0 };
|
||||
do {
|
||||
result = Core::System::poll(pollfds, -1);
|
||||
} while (result.is_error() && result.error().code() == EINTR);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -45,32 +99,32 @@ TransportSocket::TransportSocket(NonnullOwnPtr<Core::LocalSocket> socket)
|
|||
|
||||
TransportSocket::~TransportSocket()
|
||||
{
|
||||
{
|
||||
Threading::MutexLocker locker(m_send_queue->mutex);
|
||||
m_send_queue->running = false;
|
||||
m_send_queue->condition.signal();
|
||||
}
|
||||
m_send_queue->stop();
|
||||
(void)m_send_thread->join();
|
||||
}
|
||||
|
||||
void TransportSocket::set_up_read_hook(Function<void()> hook)
|
||||
{
|
||||
Threading::RWLockLocker<Threading::LockMode::Write> lock(m_socket_rw_lock);
|
||||
VERIFY(m_socket->is_open());
|
||||
m_socket->on_ready_to_read = move(hook);
|
||||
}
|
||||
|
||||
bool TransportSocket::is_open() const
|
||||
{
|
||||
Threading::RWLockLocker<Threading::LockMode::Read> lock(m_socket_rw_lock);
|
||||
return m_socket->is_open();
|
||||
}
|
||||
|
||||
void TransportSocket::close()
|
||||
{
|
||||
Threading::RWLockLocker<Threading::LockMode::Write> lock(m_socket_rw_lock);
|
||||
m_socket->close();
|
||||
}
|
||||
|
||||
void TransportSocket::wait_until_readable()
|
||||
{
|
||||
Threading::RWLockLocker<Threading::LockMode::Read> lock(m_socket_rw_lock);
|
||||
auto maybe_did_become_readable = m_socket->can_read_without_blocking(-1);
|
||||
if (maybe_did_become_readable.is_error()) {
|
||||
dbgln("TransportSocket::wait_until_readable: {}", maybe_did_become_readable.error());
|
||||
|
@ -114,67 +168,39 @@ void TransportSocket::post_message(Vector<u8> const& bytes_to_write, Vector<Nonn
|
|||
}
|
||||
}
|
||||
|
||||
queue_message_on_send_thread({ move(message_buffer), move(raw_fds) });
|
||||
m_send_queue->enqueue_message(move(message_buffer), move(raw_fds));
|
||||
}
|
||||
|
||||
void TransportSocket::queue_message_on_send_thread(MessageToSend&& message_to_send) const
|
||||
{
|
||||
Threading::MutexLocker lock(m_send_queue->mutex);
|
||||
m_send_queue->messages.append(move(message_to_send));
|
||||
m_send_queue->condition.signal();
|
||||
}
|
||||
|
||||
ErrorOr<void> TransportSocket::send_message(Core::LocalSocket& socket, ReadonlyBytes&& bytes_to_write, Vector<int, 1> const& unowned_fds)
|
||||
ErrorOr<void> TransportSocket::send_message(Core::LocalSocket& socket, ReadonlyBytes& bytes_to_write, Vector<int>& unowned_fds)
|
||||
{
|
||||
auto num_fds_to_transfer = unowned_fds.size();
|
||||
while (!bytes_to_write.is_empty()) {
|
||||
ErrorOr<ssize_t> maybe_nwritten = 0;
|
||||
if (num_fds_to_transfer > 0) {
|
||||
maybe_nwritten = socket.send_message(bytes_to_write, 0, unowned_fds);
|
||||
if (!maybe_nwritten.is_error())
|
||||
num_fds_to_transfer = 0;
|
||||
} else {
|
||||
maybe_nwritten = socket.write_some(bytes_to_write);
|
||||
}
|
||||
|
||||
if (maybe_nwritten.is_error()) {
|
||||
if (auto error = maybe_nwritten.release_error(); error.is_errno() && (error.code() == EAGAIN || error.code() == EWOULDBLOCK)) {
|
||||
|
||||
// FIXME: Refactor this to pass the unwritten bytes back to the caller to send 'later'
|
||||
// or next time the socket is writable
|
||||
Vector<struct pollfd, 1> pollfds;
|
||||
if (pollfds.is_empty())
|
||||
pollfds.append({ .fd = socket.fd().value(), .events = POLLOUT, .revents = 0 });
|
||||
|
||||
ErrorOr<int> result { 0 };
|
||||
do {
|
||||
constexpr u32 POLL_TIMEOUT_MS = 100;
|
||||
result = Core::System::poll(pollfds, POLL_TIMEOUT_MS);
|
||||
} while (result.is_error() && result.error().code() == EINTR);
|
||||
|
||||
if (!result.is_error() && result.value() != 0)
|
||||
continue;
|
||||
|
||||
switch (error.code()) {
|
||||
case EPIPE:
|
||||
return Error::from_string_literal("IPC::transfer_message: Disconnected from peer");
|
||||
case EAGAIN:
|
||||
return Error::from_string_literal("IPC::transfer_message: Timed out waiting for socket to become writable");
|
||||
default:
|
||||
return Error::from_syscall("IPC::transfer_message write"sv, -error.code());
|
||||
}
|
||||
if (auto error = maybe_nwritten.release_error(); error.is_errno() && (error.code() == EAGAIN || error.code() == EWOULDBLOCK || error.code() == EINTR)) {
|
||||
return {};
|
||||
} else {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
bytes_to_write = bytes_to_write.slice(maybe_nwritten.value());
|
||||
num_fds_to_transfer = 0;
|
||||
unowned_fds.clear();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
TransportSocket::ShouldShutdown TransportSocket::read_as_many_messages_as_possible_without_blocking(Function<void(Message)>&& callback)
|
||||
TransportSocket::ShouldShutdown TransportSocket::read_as_many_messages_as_possible_without_blocking(Function<void(Message&&)>&& callback)
|
||||
{
|
||||
Threading::RWLockLocker<Threading::LockMode::Read> lock(m_socket_rw_lock);
|
||||
|
||||
bool should_shutdown = false;
|
||||
while (is_open()) {
|
||||
u8 buffer[4096];
|
||||
|
@ -222,7 +248,7 @@ TransportSocket::ShouldShutdown TransportSocket::read_as_many_messages_as_possib
|
|||
Message message;
|
||||
received_fd_count += header.fd_count;
|
||||
for (size_t i = 0; i < header.fd_count; ++i)
|
||||
message.fds.append(m_unprocessed_fds.dequeue());
|
||||
message.fds.enqueue(m_unprocessed_fds.dequeue());
|
||||
message.bytes.append(m_unprocessed_bytes.data() + index + sizeof(MessageHeader), header.payload_size);
|
||||
callback(move(message));
|
||||
} else if (header.type == MessageHeader::Type::FileDescriptorAcknowledgement) {
|
||||
|
@ -252,7 +278,7 @@ TransportSocket::ShouldShutdown TransportSocket::read_as_many_messages_as_possib
|
|||
header.fd_count = received_fd_count;
|
||||
header.type = MessageHeader::Type::FileDescriptorAcknowledgement;
|
||||
memcpy(message_buffer.data(), &header, sizeof(MessageHeader));
|
||||
queue_message_on_send_thread({ move(message_buffer), {} });
|
||||
m_send_queue->enqueue_message(move(message_buffer), {});
|
||||
}
|
||||
|
||||
if (index < m_unprocessed_bytes.size()) {
|
||||
|
@ -267,11 +293,13 @@ TransportSocket::ShouldShutdown TransportSocket::read_as_many_messages_as_possib
|
|||
|
||||
ErrorOr<int> TransportSocket::release_underlying_transport_for_transfer()
|
||||
{
|
||||
Threading::RWLockLocker<Threading::LockMode::Write> lock(m_socket_rw_lock);
|
||||
return m_socket->release_fd();
|
||||
}
|
||||
|
||||
ErrorOr<IPC::File> TransportSocket::clone_for_transfer()
|
||||
{
|
||||
Threading::RWLockLocker<Threading::LockMode::Write> lock(m_socket_rw_lock);
|
||||
return IPC::File::clone_fd(m_socket->fd().value());
|
||||
}
|
||||
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
|
||||
#include <AK/Queue.h>
|
||||
#include <LibCore/Socket.h>
|
||||
#include <LibIPC/UnprocessedFileDescriptors.h>
|
||||
#include <LibThreading/ConditionVariable.h>
|
||||
#include <LibThreading/MutexProtected.h>
|
||||
#include <LibThreading/RWLock.h>
|
||||
#include <LibThreading/Thread.h>
|
||||
|
||||
namespace IPC {
|
||||
|
@ -42,6 +42,31 @@ private:
|
|||
int m_fd;
|
||||
};
|
||||
|
||||
class SendQueue : public AtomicRefCounted<SendQueue> {
|
||||
public:
|
||||
enum class Running {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
Running block_until_message_enqueued();
|
||||
void stop();
|
||||
|
||||
void enqueue_message(Vector<u8>&& bytes, Vector<int>&& fds);
|
||||
struct BytesAndFds {
|
||||
Vector<u8> bytes;
|
||||
Vector<int> fds;
|
||||
};
|
||||
BytesAndFds dequeue(size_t max_bytes);
|
||||
void return_unsent_data_to_front_of_queue(ReadonlyBytes const& bytes, Vector<int> const& fds);
|
||||
|
||||
private:
|
||||
Vector<u8> m_bytes;
|
||||
Vector<int> m_fds;
|
||||
Threading::Mutex m_mutex;
|
||||
Threading::ConditionVariable m_condition { m_mutex };
|
||||
bool m_running { true };
|
||||
};
|
||||
|
||||
class TransportSocket {
|
||||
AK_MAKE_NONCOPYABLE(TransportSocket);
|
||||
AK_MAKE_NONMOVABLE(TransportSocket);
|
||||
|
@ -66,9 +91,9 @@ public:
|
|||
};
|
||||
struct Message {
|
||||
Vector<u8> bytes;
|
||||
Vector<File> fds;
|
||||
Queue<File> fds;
|
||||
};
|
||||
ShouldShutdown read_as_many_messages_as_possible_without_blocking(Function<void(Message)>&& schedule_shutdown);
|
||||
ShouldShutdown read_as_many_messages_as_possible_without_blocking(Function<void(Message&&)>&&);
|
||||
|
||||
// Obnoxious name to make it clear that this is a dangerous operation.
|
||||
ErrorOr<int> release_underlying_transport_for_transfer();
|
||||
|
@ -76,30 +101,20 @@ public:
|
|||
ErrorOr<IPC::File> clone_for_transfer();
|
||||
|
||||
private:
|
||||
static ErrorOr<void> send_message(Core::LocalSocket&, ReadonlyBytes&&, Vector<int, 1> const& unowned_fds);
|
||||
static ErrorOr<void> send_message(Core::LocalSocket&, ReadonlyBytes& bytes, Vector<int>& unowned_fds);
|
||||
|
||||
NonnullOwnPtr<Core::LocalSocket> m_socket;
|
||||
mutable Threading::RWLock m_socket_rw_lock;
|
||||
ByteBuffer m_unprocessed_bytes;
|
||||
UnprocessedFileDescriptors m_unprocessed_fds;
|
||||
Queue<File> m_unprocessed_fds;
|
||||
|
||||
// After file descriptor is sent, it is moved to the wait queue until an acknowledgement is received from the peer.
|
||||
// This is necessary to handle a specific behavior of the macOS kernel, which may prematurely garbage-collect the file
|
||||
// descriptor contained in the message before the peer receives it. https://openradar.me/9477351
|
||||
Queue<NonnullRefPtr<AutoCloseFileDescriptor>> m_fds_retained_until_received_by_peer;
|
||||
|
||||
struct MessageToSend {
|
||||
Vector<u8> bytes;
|
||||
Vector<int, 1> fds;
|
||||
};
|
||||
struct SendQueue : public AtomicRefCounted<SendQueue> {
|
||||
AK::SinglyLinkedList<MessageToSend> messages;
|
||||
Threading::Mutex mutex;
|
||||
Threading::ConditionVariable condition { mutex };
|
||||
bool running { true };
|
||||
};
|
||||
RefPtr<Threading::Thread> m_send_thread;
|
||||
RefPtr<SendQueue> m_send_queue;
|
||||
void queue_message_on_send_thread(MessageToSend&&) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibIPC/File.h>
|
||||
|
||||
namespace IPC {
|
||||
|
||||
class UnprocessedFileDescriptors {
|
||||
public:
|
||||
void enqueue(File&& fd)
|
||||
{
|
||||
m_fds.append(move(fd));
|
||||
}
|
||||
|
||||
File dequeue()
|
||||
{
|
||||
return m_fds.take_first();
|
||||
}
|
||||
|
||||
void return_fds_to_front_of_queue(Vector<File>&& fds)
|
||||
{
|
||||
m_fds.prepend(move(fds));
|
||||
}
|
||||
|
||||
size_t size() const { return m_fds.size(); }
|
||||
|
||||
private:
|
||||
Vector<File> m_fds;
|
||||
};
|
||||
|
||||
}
|
|
@ -69,7 +69,7 @@ static void update_function_name(Value value, FlyString const& name)
|
|||
if (!value.is_function())
|
||||
return;
|
||||
auto& function = value.as_function();
|
||||
if (is<ECMAScriptFunctionObject>(function) && function.name().is_empty())
|
||||
if (is<ECMAScriptFunctionObject>(function) && static_cast<ECMAScriptFunctionObject const&>(function).name().is_empty())
|
||||
static_cast<ECMAScriptFunctionObject&>(function).set_name(name);
|
||||
}
|
||||
|
||||
|
|
|
@ -1212,14 +1212,14 @@ inline ThrowCompletionOr<void> put_by_property_key(VM& vm, Value base, Value thi
|
|||
switch (kind) {
|
||||
case Op::PropertyKind::Getter: {
|
||||
auto& function = value.as_function();
|
||||
if (function.name().is_empty() && is<ECMAScriptFunctionObject>(function))
|
||||
if (is<ECMAScriptFunctionObject>(function) && static_cast<ECMAScriptFunctionObject const&>(function).name().is_empty())
|
||||
static_cast<ECMAScriptFunctionObject*>(&function)->set_name(MUST(String::formatted("get {}", name)));
|
||||
object->define_direct_accessor(name, &function, nullptr, Attribute::Configurable | Attribute::Enumerable);
|
||||
break;
|
||||
}
|
||||
case Op::PropertyKind::Setter: {
|
||||
auto& function = value.as_function();
|
||||
if (function.name().is_empty() && is<ECMAScriptFunctionObject>(function))
|
||||
if (is<ECMAScriptFunctionObject>(function) && static_cast<ECMAScriptFunctionObject const&>(function).name().is_empty())
|
||||
static_cast<ECMAScriptFunctionObject*>(&function)->set_name(MUST(String::formatted("set {}", name)));
|
||||
object->define_direct_accessor(name, nullptr, &function, Attribute::Configurable | Attribute::Enumerable);
|
||||
break;
|
||||
|
|
|
@ -58,11 +58,16 @@ protected:
|
|||
explicit Array(Object& prototype);
|
||||
|
||||
private:
|
||||
virtual bool is_array_exotic_object() const final { return true; }
|
||||
|
||||
ThrowCompletionOr<bool> set_length(PropertyDescriptor const&);
|
||||
|
||||
bool m_length_writable { true };
|
||||
};
|
||||
|
||||
template<>
|
||||
inline bool Object::fast_is<Array>() const { return is_array_exotic_object(); }
|
||||
|
||||
enum class Holes {
|
||||
SkipHoles,
|
||||
ReadThroughHoles,
|
||||
|
|
|
@ -39,8 +39,6 @@ BoundFunction::BoundFunction(Realm& realm, FunctionObject& bound_target_function
|
|||
, m_bound_target_function(&bound_target_function)
|
||||
, m_bound_this(bound_this)
|
||||
, m_bound_arguments(move(bound_arguments))
|
||||
// FIXME: Non-standard and redundant, remove.
|
||||
, m_name(MUST(String::formatted("bound {}", bound_target_function.name())))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ public:
|
|||
virtual ThrowCompletionOr<Value> internal_call(Value this_argument, ReadonlySpan<Value> arguments_list) override;
|
||||
virtual ThrowCompletionOr<GC::Ref<Object>> internal_construct(ReadonlySpan<Value> arguments_list, FunctionObject& new_target) override;
|
||||
|
||||
virtual FlyString const& name() const override { return m_name; }
|
||||
virtual bool is_strict_mode() const override { return m_bound_target_function->is_strict_mode(); }
|
||||
virtual bool has_constructor() const override { return m_bound_target_function->has_constructor(); }
|
||||
|
||||
|
@ -39,8 +38,6 @@ private:
|
|||
GC::Ptr<FunctionObject> m_bound_target_function; // [[BoundTargetFunction]]
|
||||
Value m_bound_this; // [[BoundThis]]
|
||||
Vector<Value> m_bound_arguments; // [[BoundArguments]]
|
||||
|
||||
FlyString m_name;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -426,10 +426,9 @@ ECMAScriptFunctionObject::ECMAScriptFunctionObject(
|
|||
, m_shared_data(move(shared_data))
|
||||
, m_environment(parent_environment)
|
||||
, m_private_environment(private_environment)
|
||||
, m_realm(&prototype.shape().realm())
|
||||
{
|
||||
if (!is_arrow_function() && kind() == FunctionKind::Normal)
|
||||
unsafe_set_shape(m_realm->intrinsics().normal_function_shape());
|
||||
unsafe_set_shape(realm()->intrinsics().normal_function_shape());
|
||||
|
||||
// 15. Set F.[[ScriptOrModule]] to GetActiveScriptOrModule().
|
||||
m_script_or_module = vm().get_active_script_or_module();
|
||||
|
@ -643,7 +642,6 @@ void ECMAScriptFunctionObject::visit_edges(Visitor& visitor)
|
|||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_environment);
|
||||
visitor.visit(m_private_environment);
|
||||
visitor.visit(m_realm);
|
||||
visitor.visit(m_home_object);
|
||||
visitor.visit(m_name_string);
|
||||
|
||||
|
@ -694,27 +692,13 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::prepare_for_ordinary_call(Exec
|
|||
// 1. Let callerContext be the running execution context.
|
||||
// 2. Let calleeContext be a new ECMAScript code execution context.
|
||||
|
||||
// NOTE: In the specification, PrepareForOrdinaryCall "returns" a new callee execution context.
|
||||
// To avoid heap allocations, we put our ExecutionContext objects on the C++ stack instead.
|
||||
// Whoever calls us should put an ExecutionContext on their stack and pass that as the `callee_context`.
|
||||
|
||||
// 3. Set the Function of calleeContext to F.
|
||||
callee_context.function = this;
|
||||
callee_context.function_name = m_name_string;
|
||||
|
||||
// 4. Let calleeRealm be F.[[Realm]].
|
||||
auto callee_realm = m_realm;
|
||||
// NOTE: This non-standard fallback is needed until we can guarantee that literally
|
||||
// every function has a realm - especially in LibWeb that's sometimes not the case
|
||||
// when a function is created while no JS is running, as we currently need to rely on
|
||||
// that (:acid2:, I know - see set_event_handler_attribute() for an example).
|
||||
// If there's no 'current realm' either, we can't continue and crash.
|
||||
if (!callee_realm)
|
||||
callee_realm = vm.current_realm();
|
||||
VERIFY(callee_realm);
|
||||
|
||||
// 5. Set the Realm of calleeContext to calleeRealm.
|
||||
callee_context.realm = callee_realm;
|
||||
callee_context.realm = realm();
|
||||
|
||||
// 6. Set the ScriptOrModule of calleeContext to F.[[ScriptOrModule]].
|
||||
callee_context.script_or_module = m_script_or_module;
|
||||
|
@ -758,15 +742,7 @@ void ECMAScriptFunctionObject::ordinary_call_bind_this(ExecutionContext& callee_
|
|||
return;
|
||||
|
||||
// 3. Let calleeRealm be F.[[Realm]].
|
||||
auto callee_realm = m_realm;
|
||||
// NOTE: This non-standard fallback is needed until we can guarantee that literally
|
||||
// every function has a realm - especially in LibWeb that's sometimes not the case
|
||||
// when a function is created while no JS is running, as we currently need to rely on
|
||||
// that (:acid2:, I know - see set_event_handler_attribute() for an example).
|
||||
// If there's no 'current realm' either, we can't continue and crash.
|
||||
if (!callee_realm)
|
||||
callee_realm = vm.current_realm();
|
||||
VERIFY(callee_realm);
|
||||
auto callee_realm = realm();
|
||||
|
||||
// 4. Let localEnv be the LexicalEnvironment of calleeContext.
|
||||
auto local_env = callee_context.lexical_environment;
|
||||
|
|
|
@ -127,7 +127,7 @@ public:
|
|||
Statement const& ecmascript_code() const { return *shared_data().m_ecmascript_code; }
|
||||
[[nodiscard]] virtual FunctionParameters const& formal_parameters() const override { return *shared_data().m_formal_parameters; }
|
||||
|
||||
virtual FlyString const& name() const override { return shared_data().m_name; }
|
||||
FlyString const& name() const { return shared_data().m_name; }
|
||||
void set_name(FlyString const& name);
|
||||
|
||||
void set_is_class_constructor() { const_cast<SharedFunctionInstanceData&>(shared_data()).m_is_class_constructor = true; }
|
||||
|
@ -135,7 +135,7 @@ public:
|
|||
auto& bytecode_executable() const { return m_bytecode_executable; }
|
||||
|
||||
Environment* environment() { return m_environment; }
|
||||
virtual Realm* realm() const override { return m_realm; }
|
||||
virtual Realm* realm() const override { return &shape().realm(); }
|
||||
|
||||
[[nodiscard]] ConstructorKind constructor_kind() const { return shared_data().m_constructor_kind; }
|
||||
void set_constructor_kind(ConstructorKind constructor_kind) { const_cast<SharedFunctionInstanceData&>(shared_data()).m_constructor_kind = constructor_kind; }
|
||||
|
@ -210,7 +210,6 @@ private:
|
|||
// Internal Slots of ECMAScript Function Objects, https://tc39.es/ecma262/#table-internal-slots-of-ecmascript-function-objects
|
||||
GC::Ptr<Environment> m_environment; // [[Environment]]
|
||||
GC::Ptr<PrivateEnvironment> m_private_environment; // [[PrivateEnvironment]]
|
||||
GC::Ptr<Realm> m_realm; // [[Realm]]
|
||||
ScriptOrModule m_script_or_module; // [[ScriptOrModule]]
|
||||
GC::Ptr<Object> m_home_object; // [[HomeObject]]
|
||||
struct ClassData {
|
||||
|
|
|
@ -26,8 +26,6 @@ public:
|
|||
virtual ThrowCompletionOr<Value> internal_call(Value this_argument, ReadonlySpan<Value> arguments_list) = 0;
|
||||
virtual ThrowCompletionOr<GC::Ref<Object>> internal_construct([[maybe_unused]] ReadonlySpan<Value> arguments_list, [[maybe_unused]] FunctionObject& new_target) { VERIFY_NOT_REACHED(); }
|
||||
|
||||
virtual FlyString const& name() const = 0;
|
||||
|
||||
void set_function_name(Variant<PropertyKey, PrivateName> const& name_arg, Optional<StringView> const& prefix = {});
|
||||
void set_function_length(double length);
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ public:
|
|||
virtual ~FunctionPrototype() override = default;
|
||||
|
||||
virtual ThrowCompletionOr<Value> internal_call(Value this_argument, ReadonlySpan<Value> arguments_list) override;
|
||||
virtual FlyString const& name() const override { return m_name; }
|
||||
|
||||
private:
|
||||
explicit FunctionPrototype(Realm&);
|
||||
|
@ -29,9 +28,6 @@ private:
|
|||
JS_DECLARE_NATIVE_FUNCTION(call);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_string);
|
||||
JS_DECLARE_NATIVE_FUNCTION(symbol_has_instance);
|
||||
|
||||
// 20.2.3: The Function prototype object has a "name" property whose value is the empty String.
|
||||
FlyString m_name;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ public:
|
|||
virtual ThrowCompletionOr<Value> call();
|
||||
virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target);
|
||||
|
||||
virtual FlyString const& name() const override { return m_name; }
|
||||
FlyString const& name() const { return m_name; }
|
||||
virtual bool is_strict_mode() const override;
|
||||
virtual bool has_constructor() const override { return false; }
|
||||
virtual Realm* realm() const override { return m_realm; }
|
||||
|
|
|
@ -198,6 +198,7 @@ public:
|
|||
virtual bool is_regexp_object() const { return false; }
|
||||
virtual bool is_bigint_object() const { return false; }
|
||||
virtual bool is_string_object() const { return false; }
|
||||
virtual bool is_array_exotic_object() const { return false; }
|
||||
virtual bool is_global_object() const { return false; }
|
||||
virtual bool is_proxy_object() const { return false; }
|
||||
virtual bool is_native_function() const { return false; }
|
||||
|
|
|
@ -106,6 +106,17 @@ Utf16View PrimitiveString::utf16_string_view() const
|
|||
return m_utf16_string->view();
|
||||
}
|
||||
|
||||
bool PrimitiveString::operator==(PrimitiveString const& other) const
|
||||
{
|
||||
if (this == &other)
|
||||
return true;
|
||||
if (m_utf8_string.has_value() && other.m_utf8_string.has_value())
|
||||
return m_utf8_string->bytes_as_string_view() == other.m_utf8_string->bytes_as_string_view();
|
||||
if (m_utf16_string.has_value() && other.m_utf16_string.has_value())
|
||||
return m_utf16_string->string() == other.m_utf16_string->string();
|
||||
return utf8_string_view() == other.utf8_string_view();
|
||||
}
|
||||
|
||||
ThrowCompletionOr<Optional<Value>> PrimitiveString::get(VM& vm, PropertyKey const& property_key) const
|
||||
{
|
||||
if (property_key.is_symbol())
|
||||
|
|
|
@ -47,6 +47,8 @@ public:
|
|||
|
||||
ThrowCompletionOr<Optional<Value>> get(VM&, PropertyKey const&) const;
|
||||
|
||||
[[nodiscard]] bool operator==(PrimitiveString const&) const;
|
||||
|
||||
protected:
|
||||
enum class RopeTag { Rope };
|
||||
explicit PrimitiveString(RopeTag)
|
||||
|
|
|
@ -897,10 +897,4 @@ void ProxyObject::visit_edges(Cell::Visitor& visitor)
|
|||
visitor.visit(m_handler);
|
||||
}
|
||||
|
||||
FlyString const& ProxyObject::name() const
|
||||
{
|
||||
VERIFY(is_function());
|
||||
return static_cast<FunctionObject&>(*m_target).name();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ public:
|
|||
|
||||
virtual ~ProxyObject() override = default;
|
||||
|
||||
virtual FlyString const& name() const override;
|
||||
virtual bool has_constructor() const override;
|
||||
|
||||
Object const& target() const { return m_target; }
|
||||
|
|
|
@ -759,13 +759,6 @@ void VM::pop_execution_context()
|
|||
on_call_stack_emptied();
|
||||
}
|
||||
|
||||
#if ARCH(X86_64)
|
||||
struct [[gnu::packed]] NativeStackFrame {
|
||||
NativeStackFrame* prev;
|
||||
FlatPtr return_address;
|
||||
};
|
||||
#endif
|
||||
|
||||
static RefPtr<CachedSourceRange> get_source_range(ExecutionContext const* context)
|
||||
{
|
||||
// native function
|
||||
|
|
|
@ -926,6 +926,26 @@ ThrowCompletionOr<PropertyKey> Value::to_property_key(VM& vm) const
|
|||
return MUST(key.to_string(vm));
|
||||
}
|
||||
|
||||
// 7.1.6 ToInt32 ( argument ), https://tc39.es/ecma262/#sec-toint32
|
||||
ThrowCompletionOr<i32> Value::to_i32(VM& vm) const
|
||||
{
|
||||
if (is_int32())
|
||||
return as_i32();
|
||||
|
||||
#if __has_builtin(__builtin_arm_jcvt)
|
||||
if (is_double())
|
||||
return __builtin_arm_jcvt(m_value.as_double);
|
||||
#endif
|
||||
|
||||
return to_i32_slow_case(vm);
|
||||
}
|
||||
|
||||
// 7.1.7 ToUint32 ( argument ), https://tc39.es/ecma262/#sec-touint32
|
||||
ThrowCompletionOr<u32> Value::to_u32(VM& vm) const
|
||||
{
|
||||
return static_cast<u32>(TRY(to_i32(vm)));
|
||||
}
|
||||
|
||||
// 7.1.6 ToInt32 ( argument ), https://tc39.es/ecma262/#sec-toint32
|
||||
ThrowCompletionOr<i32> Value::to_i32_slow_case(VM& vm) const
|
||||
{
|
||||
|
@ -934,6 +954,9 @@ ThrowCompletionOr<i32> Value::to_i32_slow_case(VM& vm) const
|
|||
// 1. Let number be ? ToNumber(argument).
|
||||
double number = TRY(to_number(vm)).as_double();
|
||||
|
||||
#if __has_builtin(__builtin_arm_jcvt)
|
||||
return __builtin_arm_jcvt(number);
|
||||
#else
|
||||
// 2. If number is not finite or number is either +0𝔽 or -0𝔽, return +0𝔽.
|
||||
if (!isfinite(number) || number == 0)
|
||||
return 0;
|
||||
|
@ -951,42 +974,7 @@ ThrowCompletionOr<i32> Value::to_i32_slow_case(VM& vm) const
|
|||
if (int32bit >= 2147483648.0)
|
||||
int32bit -= 4294967296.0;
|
||||
return static_cast<i32>(int32bit);
|
||||
}
|
||||
|
||||
// 7.1.6 ToInt32 ( argument ), https://tc39.es/ecma262/#sec-toint32
|
||||
ThrowCompletionOr<i32> Value::to_i32(VM& vm) const
|
||||
{
|
||||
if (is_int32())
|
||||
return as_i32();
|
||||
return to_i32_slow_case(vm);
|
||||
}
|
||||
|
||||
// 7.1.7 ToUint32 ( argument ), https://tc39.es/ecma262/#sec-touint32
|
||||
ThrowCompletionOr<u32> Value::to_u32(VM& vm) const
|
||||
{
|
||||
// OPTIMIZATION: If this value is encoded as a positive i32, return it directly.
|
||||
if (is_int32() && as_i32() >= 0)
|
||||
return as_i32();
|
||||
|
||||
// 1. Let number be ? ToNumber(argument).
|
||||
double number = TRY(to_number(vm)).as_double();
|
||||
|
||||
// 2. If number is not finite or number is either +0𝔽 or -0𝔽, return +0𝔽.
|
||||
if (!isfinite(number) || number == 0)
|
||||
return 0;
|
||||
|
||||
// 3. Let int be the mathematical value whose sign is the sign of number and whose magnitude is floor(abs(ℝ(number))).
|
||||
auto int_val = floor(fabs(number));
|
||||
if (signbit(number))
|
||||
int_val = -int_val;
|
||||
|
||||
// 4. Let int32bit be int modulo 2^32.
|
||||
auto int32bit = modulo(int_val, NumericLimits<u32>::max() + 1.0);
|
||||
|
||||
// 5. Return 𝔽(int32bit).
|
||||
// Cast to i64 here to ensure that the double --> u32 cast doesn't invoke undefined behavior
|
||||
// Otherwise, negative numbers cause a UBSAN warning.
|
||||
return static_cast<u32>(static_cast<i64>(int32bit));
|
||||
#endif
|
||||
}
|
||||
|
||||
// 7.1.8 ToInt16 ( argument ), https://tc39.es/ecma262/#sec-toint16
|
||||
|
@ -2237,7 +2225,7 @@ bool same_value_non_number(Value lhs, Value rhs)
|
|||
// 5. If x is a String, then
|
||||
if (lhs.is_string()) {
|
||||
// a. If x and y are exactly the same sequence of code units (same length and same code units at corresponding indices), return true; otherwise, return false.
|
||||
return lhs.as_string().utf8_string_view() == rhs.as_string().utf8_string_view();
|
||||
return lhs.as_string() == rhs.as_string();
|
||||
}
|
||||
|
||||
// 3. If x is undefined, return true.
|
||||
|
|
|
@ -22,9 +22,6 @@ public:
|
|||
|
||||
virtual ThrowCompletionOr<Value> internal_call(Value this_argument, ReadonlySpan<Value> arguments_list) override;
|
||||
|
||||
// FIXME: Remove this (and stop inventing random internal slots that shouldn't exist, jeez)
|
||||
virtual FlyString const& name() const override { return m_wrapped_target_function->name(); }
|
||||
|
||||
virtual Realm* realm() const override { return m_realm; }
|
||||
|
||||
FunctionObject const& wrapped_target_function() const { return m_wrapped_target_function; }
|
||||
|
|
|
@ -25,9 +25,9 @@ public:
|
|||
|
||||
void restart() { m_started = UnixDateTime::now(); }
|
||||
|
||||
u64 elapsed_milliseconds()
|
||||
AK::Duration elapsed() const
|
||||
{
|
||||
return (UnixDateTime::now() - m_started).to_milliseconds();
|
||||
return UnixDateTime::now() - m_started;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -187,17 +187,18 @@ int TestSuite::run(Vector<NonnullRefPtr<TestCase>> const& tests)
|
|||
m_current_test_result = TestResult::NotRun;
|
||||
enable_reporting();
|
||||
|
||||
u64 total_time = 0;
|
||||
AK::Duration total_time;
|
||||
u64 sum_of_squared_times = 0;
|
||||
u64 min_time = NumericLimits<u64>::max();
|
||||
u64 max_time = 0;
|
||||
AK::Duration min_time = AK::Duration::max();
|
||||
AK::Duration max_time;
|
||||
|
||||
for (u64 i = 0; i < repetitions; ++i) {
|
||||
TestElapsedTimer timer;
|
||||
t->func()();
|
||||
auto const iteration_time = timer.elapsed_milliseconds();
|
||||
auto const iteration_time = timer.elapsed();
|
||||
auto const iteration_ms = iteration_time.to_milliseconds();
|
||||
total_time += iteration_time;
|
||||
sum_of_squared_times += iteration_time * iteration_time;
|
||||
sum_of_squared_times += iteration_ms * iteration_ms;
|
||||
min_time = min(min_time, iteration_time);
|
||||
max_time = max(max_time, iteration_time);
|
||||
|
||||
|
@ -206,20 +207,26 @@ int TestSuite::run(Vector<NonnullRefPtr<TestCase>> const& tests)
|
|||
m_current_test_result = TestResult::Passed;
|
||||
}
|
||||
|
||||
auto const total_time_ms = total_time.to_milliseconds();
|
||||
|
||||
if (repetitions != 1) {
|
||||
double average = total_time / double(repetitions);
|
||||
double average = total_time_ms / static_cast<double>(repetitions);
|
||||
double average_squared = average * average;
|
||||
double standard_deviation = sqrt((sum_of_squared_times + repetitions * average_squared - 2 * total_time * average) / (repetitions - 1));
|
||||
double standard_deviation = sqrt((sum_of_squared_times + repetitions * average_squared - 2 * total_time_ms * average) / (repetitions - 1));
|
||||
|
||||
dbgln("{} {} '{}' on average in {:.1f}±{:.1f}ms (min={}ms, max={}ms, total={}ms)",
|
||||
test_result_to_string(m_current_test_result), test_type, t->name(),
|
||||
average, standard_deviation, min_time, max_time, total_time);
|
||||
average,
|
||||
standard_deviation,
|
||||
min_time.to_milliseconds(),
|
||||
max_time.to_milliseconds(),
|
||||
total_time_ms);
|
||||
} else {
|
||||
dbgln("{} {} '{}' in {}ms", test_result_to_string(m_current_test_result), test_type, t->name(), total_time);
|
||||
dbgln("{} {} '{}' in {}ms", test_result_to_string(m_current_test_result), test_type, t->name(), total_time_ms);
|
||||
}
|
||||
|
||||
if (t->is_benchmark()) {
|
||||
m_benchtime += total_time;
|
||||
m_bench_time += total_time;
|
||||
benchmark_count++;
|
||||
|
||||
switch (m_current_test_result) {
|
||||
|
@ -233,7 +240,7 @@ int TestSuite::run(Vector<NonnullRefPtr<TestCase>> const& tests)
|
|||
break;
|
||||
}
|
||||
} else {
|
||||
m_testtime += total_time;
|
||||
m_test_time += total_time;
|
||||
test_count++;
|
||||
|
||||
switch (m_current_test_result) {
|
||||
|
@ -249,13 +256,16 @@ int TestSuite::run(Vector<NonnullRefPtr<TestCase>> const& tests)
|
|||
}
|
||||
}
|
||||
|
||||
auto const runtime = m_test_time + m_bench_time;
|
||||
auto const elapsed = global_timer.elapsed() - runtime;
|
||||
|
||||
dbgln("Finished {} tests and {} benchmarks in {}ms ({}ms tests, {}ms benchmarks, {}ms other).",
|
||||
test_count,
|
||||
benchmark_count,
|
||||
global_timer.elapsed_milliseconds(),
|
||||
m_testtime,
|
||||
m_benchtime,
|
||||
global_timer.elapsed_milliseconds() - (m_testtime + m_benchtime));
|
||||
elapsed.to_truncated_milliseconds(),
|
||||
m_test_time.to_truncated_milliseconds(),
|
||||
m_bench_time.to_truncated_milliseconds(),
|
||||
(elapsed - runtime).to_truncated_milliseconds());
|
||||
|
||||
if (test_count != 0) {
|
||||
if (test_passed_count == test_count) {
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/Time.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibTest/Macros.h>
|
||||
#include <LibTest/Randomized/RandomnessSource.h>
|
||||
|
@ -65,8 +66,8 @@ public:
|
|||
private:
|
||||
static TestSuite* s_global;
|
||||
Vector<NonnullRefPtr<TestCase>> m_cases;
|
||||
u64 m_testtime = 0;
|
||||
u64 m_benchtime = 0;
|
||||
AK::Duration m_test_time;
|
||||
AK::Duration m_bench_time;
|
||||
ByteString m_suite_name;
|
||||
u64 m_benchmark_repetitions = 1;
|
||||
u64 m_randomized_runs = 100;
|
||||
|
|
|
@ -246,6 +246,18 @@ String URL::serialize_path() const
|
|||
return output.to_string_without_validation();
|
||||
}
|
||||
|
||||
// This function is used whenever a path is needed to access the actual file on disk.
|
||||
// On Windows serialize_path can produce a path like /C:/path/to/tst.htm, so the leading slash needs to be removed to obtain a valid path.
|
||||
ByteString URL::file_path() const
|
||||
{
|
||||
ByteString path = percent_decode(serialize_path());
|
||||
#ifdef AK_OS_WINDOWS
|
||||
if (path.starts_with('/'))
|
||||
path = path.substring(1);
|
||||
#endif
|
||||
return path;
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#concept-url-serializer
|
||||
String URL::serialize(ExcludeFragment exclude_fragment) const
|
||||
{
|
||||
|
|
|
@ -122,6 +122,7 @@ public:
|
|||
}
|
||||
|
||||
String serialize_path() const;
|
||||
ByteString file_path() const;
|
||||
String serialize(ExcludeFragment = ExcludeFragment::No) const;
|
||||
ByteString serialize_for_display() const;
|
||||
ByteString to_byte_string() const { return serialize().to_byte_string(); }
|
||||
|
|
|
@ -87,15 +87,13 @@ public:
|
|||
virtual Optional<size_t> previous_boundary(size_t boundary, Inclusive inclusive) override
|
||||
{
|
||||
auto icu_boundary = align_boundary(boundary);
|
||||
if (!icu_boundary.has_value())
|
||||
return {};
|
||||
|
||||
if (inclusive == Inclusive::Yes) {
|
||||
if (static_cast<bool>(m_segmenter->isBoundary(*icu_boundary)))
|
||||
return static_cast<size_t>(*icu_boundary);
|
||||
if (static_cast<bool>(m_segmenter->isBoundary(icu_boundary)))
|
||||
return static_cast<size_t>(icu_boundary);
|
||||
}
|
||||
|
||||
if (auto index = m_segmenter->preceding(*icu_boundary); index != icu::BreakIterator::DONE)
|
||||
if (auto index = m_segmenter->preceding(icu_boundary); index != icu::BreakIterator::DONE)
|
||||
return static_cast<size_t>(index);
|
||||
|
||||
return {};
|
||||
|
@ -104,15 +102,13 @@ public:
|
|||
virtual Optional<size_t> next_boundary(size_t boundary, Inclusive inclusive) override
|
||||
{
|
||||
auto icu_boundary = align_boundary(boundary);
|
||||
if (!icu_boundary.has_value())
|
||||
return {};
|
||||
|
||||
if (inclusive == Inclusive::Yes) {
|
||||
if (static_cast<bool>(m_segmenter->isBoundary(*icu_boundary)))
|
||||
return static_cast<size_t>(*icu_boundary);
|
||||
if (static_cast<bool>(m_segmenter->isBoundary(icu_boundary)))
|
||||
return static_cast<size_t>(icu_boundary);
|
||||
}
|
||||
|
||||
if (auto index = m_segmenter->following(*icu_boundary); index != icu::BreakIterator::DONE)
|
||||
if (auto index = m_segmenter->following(icu_boundary); index != icu::BreakIterator::DONE)
|
||||
return static_cast<size_t>(index);
|
||||
|
||||
return {};
|
||||
|
@ -177,25 +173,25 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
Optional<i32> align_boundary(size_t boundary)
|
||||
i32 align_boundary(size_t boundary)
|
||||
{
|
||||
auto icu_boundary = static_cast<i32>(boundary);
|
||||
|
||||
return m_segmented_text.visit(
|
||||
[&](String const& text) -> Optional<i32> {
|
||||
[&](String const& text) {
|
||||
if (boundary >= text.byte_count())
|
||||
return {};
|
||||
return static_cast<i32>(text.byte_count());
|
||||
|
||||
U8_SET_CP_START(text.bytes().data(), 0, icu_boundary);
|
||||
return icu_boundary;
|
||||
},
|
||||
[&](icu::UnicodeString const& text) -> Optional<i32> {
|
||||
[&](icu::UnicodeString const& text) {
|
||||
if (icu_boundary >= text.length())
|
||||
return {};
|
||||
return text.length();
|
||||
|
||||
return text.getChar32Start(icu_boundary);
|
||||
},
|
||||
[](Empty) -> Optional<i32> { VERIFY_NOT_REACHED(); });
|
||||
[](Empty) -> i32 { VERIFY_NOT_REACHED(); });
|
||||
}
|
||||
|
||||
void for_each_boundary(SegmentationCallback callback)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/ValueInlines.h>
|
||||
#include <LibWeb/Bindings/ExceptionOrUtils.h>
|
||||
#include <LibWeb/Bindings/HTMLImageElementPrototype.h>
|
||||
#include <LibWeb/Bindings/ImageConstructor.h>
|
||||
|
|
|
@ -195,6 +195,7 @@ set(SOURCES
|
|||
CSS/Time.cpp
|
||||
CSS/Transformation.cpp
|
||||
CSS/TransitionEvent.cpp
|
||||
CSS/URL.cpp
|
||||
CSS/VisualViewport.cpp
|
||||
Cookie/Cookie.cpp
|
||||
Cookie/ParsedCookie.cpp
|
||||
|
@ -566,6 +567,7 @@ set(SOURCES
|
|||
IndexedDB/IDBVersionChangeEvent.cpp
|
||||
IndexedDB/Internal/Algorithms.cpp
|
||||
IndexedDB/Internal/Database.cpp
|
||||
IndexedDB/Internal/Index.cpp
|
||||
IndexedDB/Internal/Key.cpp
|
||||
IndexedDB/Internal/ObjectStore.cpp
|
||||
IndexedDB/Internal/RequestList.cpp
|
||||
|
@ -689,6 +691,7 @@ set(SOURCES
|
|||
Painting/SVGPaintable.cpp
|
||||
Painting/SVGSVGPaintable.cpp
|
||||
Painting/ScrollFrame.cpp
|
||||
Painting/ScrollState.cpp
|
||||
Painting/ShadowPainting.cpp
|
||||
Painting/StackingContext.cpp
|
||||
Painting/TableBordersPainting.cpp
|
||||
|
|
|
@ -24,7 +24,7 @@ Optional<int> CSSAnimation::class_specific_composite_order(GC::Ref<Animations::A
|
|||
{
|
||||
auto other = GC::Ref { as<CSSAnimation>(*other_animation) };
|
||||
|
||||
// The existance of an owning element determines the animation class, so both animations should have their owning
|
||||
// The existence of an owning element determines the animation class, so both animations should have their owning
|
||||
// element in the same state
|
||||
VERIFY(!owning_element() == !other->owning_element());
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <AK/Debug.h>
|
||||
#include <AK/ScopeGuard.h>
|
||||
#include <LibTextCodec/Decoder.h>
|
||||
#include <LibURL/URL.h>
|
||||
#include <LibWeb/Bindings/CSSImportRulePrototype.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/CSS/CSSImportRule.h>
|
||||
|
@ -17,23 +16,23 @@
|
|||
#include <LibWeb/CSS/Parser/Parser.h>
|
||||
#include <LibWeb/CSS/StyleComputer.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOMURL/DOMURL.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(CSSImportRule);
|
||||
|
||||
GC::Ref<CSSImportRule> CSSImportRule::create(URL::URL url, DOM::Document& document, RefPtr<Supports> supports, Vector<NonnullRefPtr<MediaQuery>> media_query_list)
|
||||
GC::Ref<CSSImportRule> CSSImportRule::create(JS::Realm& realm, URL url, GC::Ptr<DOM::Document> document, RefPtr<Supports> supports, Vector<NonnullRefPtr<MediaQuery>> media_query_list)
|
||||
{
|
||||
auto& realm = document.realm();
|
||||
return realm.create<CSSImportRule>(move(url), document, supports, move(media_query_list));
|
||||
return realm.create<CSSImportRule>(realm, move(url), document, move(supports), move(media_query_list));
|
||||
}
|
||||
|
||||
CSSImportRule::CSSImportRule(URL::URL url, DOM::Document& document, RefPtr<Supports> supports, Vector<NonnullRefPtr<MediaQuery>> media_query_list)
|
||||
: CSSRule(document.realm(), Type::Import)
|
||||
CSSImportRule::CSSImportRule(JS::Realm& realm, URL url, GC::Ptr<DOM::Document> document, RefPtr<Supports> supports, Vector<NonnullRefPtr<MediaQuery>> media_query_list)
|
||||
: CSSRule(realm, Type::Import)
|
||||
, m_url(move(url))
|
||||
, m_document(document)
|
||||
, m_supports(supports)
|
||||
, m_supports(move(supports))
|
||||
, m_media_query_list(move(media_query_list))
|
||||
{
|
||||
}
|
||||
|
@ -57,7 +56,10 @@ void CSSImportRule::set_parent_style_sheet(CSSStyleSheet* parent_style_sheet)
|
|||
// Crude detection of whether we're already fetching.
|
||||
if (m_style_sheet || m_document_load_event_delayer.has_value())
|
||||
return;
|
||||
fetch();
|
||||
|
||||
// Only try to fetch if we now have a parent
|
||||
if (parent_style_sheet)
|
||||
fetch();
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/cssom/#serialize-a-css-rule
|
||||
|
@ -70,7 +72,7 @@ String CSSImportRule::serialized() const
|
|||
builder.append("@import "sv);
|
||||
|
||||
// 2. The result of performing serialize a URL on the rule’s location.
|
||||
serialize_a_url(builder, m_url.to_string());
|
||||
builder.append(m_url.to_string());
|
||||
|
||||
// AD-HOC: Serialize the rule's supports condition if it exists.
|
||||
// This isn't currently specified, but major browsers include this in their serialization of import rules
|
||||
|
@ -104,15 +106,18 @@ void CSSImportRule::fetch()
|
|||
|
||||
// 3. Let parsedUrl be the result of the URL parser steps with rule’s URL and parentStylesheet’s location.
|
||||
// If the algorithm returns an error, return. [CSSOM]
|
||||
// FIXME: Stop producing a URL::URL when parsing the @import
|
||||
auto parsed_url = url().to_string();
|
||||
auto parsed_url = DOMURL::parse(href(), parent_style_sheet.location());
|
||||
if (!parsed_url.has_value()) {
|
||||
dbgln("Unable to parse @import url `{}` parent location `{}` as a URL.", href(), parent_style_sheet.location());
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: Figure out the "correct" way to delay the load event.
|
||||
m_document_load_event_delayer.emplace(*m_document);
|
||||
|
||||
// 4. Fetch a style resource from parsedUrl, with stylesheet parentStylesheet, destination "style", CORS mode "no-cors", and processResponse being the following steps given response response and byte stream, null or failure byteStream:
|
||||
fetch_a_style_resource(parsed_url, parent_style_sheet, Fetch::Infrastructure::Request::Destination::Style, CorsMode::NoCors,
|
||||
[strong_this = GC::Ref { *this }, parent_style_sheet = GC::Ref { parent_style_sheet }](auto response, auto maybe_byte_stream) {
|
||||
fetch_a_style_resource(parsed_url->to_string(), parent_style_sheet, Fetch::Infrastructure::Request::Destination::Style, CorsMode::NoCors,
|
||||
[strong_this = GC::Ref { *this }, parent_style_sheet = GC::Ref { parent_style_sheet }, parsed_url = parsed_url.value()](auto response, auto maybe_byte_stream) {
|
||||
// AD-HOC: Stop delaying the load event.
|
||||
ScopeGuard guard = [strong_this] {
|
||||
strong_this->m_document_load_event_delayer.clear();
|
||||
|
@ -139,19 +144,19 @@ void CSSImportRule::fetch()
|
|||
auto encoding = "utf-8"sv;
|
||||
auto maybe_decoder = TextCodec::decoder_for(encoding);
|
||||
if (!maybe_decoder.has_value()) {
|
||||
dbgln_if(CSS_LOADER_DEBUG, "CSSImportRule: Failed to decode CSS file: {} Unsupported encoding: {}", strong_this->url(), encoding);
|
||||
dbgln_if(CSS_LOADER_DEBUG, "CSSImportRule: Failed to decode CSS file: {} Unsupported encoding: {}", parsed_url, encoding);
|
||||
return;
|
||||
}
|
||||
auto& decoder = maybe_decoder.release_value();
|
||||
|
||||
auto decoded_or_error = TextCodec::convert_input_to_utf8_using_given_decoder_unless_there_is_a_byte_order_mark(decoder, *byte_stream);
|
||||
if (decoded_or_error.is_error()) {
|
||||
dbgln_if(CSS_LOADER_DEBUG, "CSSImportRule: Failed to decode CSS file: {} Encoding was: {}", strong_this->url(), encoding);
|
||||
dbgln_if(CSS_LOADER_DEBUG, "CSSImportRule: Failed to decode CSS file: {} Encoding was: {}", parsed_url, encoding);
|
||||
return;
|
||||
}
|
||||
auto decoded = decoded_or_error.release_value();
|
||||
|
||||
auto* imported_style_sheet = parse_css_stylesheet(Parser::ParsingParams(*strong_this->m_document, strong_this->url()), decoded, strong_this->url(), strong_this->m_media_query_list);
|
||||
auto* imported_style_sheet = parse_css_stylesheet(Parser::ParsingParams(*strong_this->m_document, parsed_url), decoded, parsed_url, strong_this->m_media_query_list);
|
||||
|
||||
// 5. Set importedStylesheet’s origin-clean flag to parentStylesheet’s origin-clean flag.
|
||||
imported_style_sheet->set_origin_clean(parent_style_sheet->is_origin_clean());
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
* Copyright (c) 2021-2024, Sam Atkins <sam@ladybird.org>
|
||||
* Copyright (c) 2021-2025, Sam Atkins <sam@ladybird.org>
|
||||
* Copyright (c) 2022, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
|
@ -8,9 +8,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <LibURL/URL.h>
|
||||
#include <LibWeb/CSS/CSSRule.h>
|
||||
#include <LibWeb/CSS/CSSStyleSheet.h>
|
||||
#include <LibWeb/CSS/URL.h>
|
||||
#include <LibWeb/DOM/DocumentLoadEventDelayer.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
@ -21,13 +21,12 @@ class CSSImportRule final
|
|||
GC_DECLARE_ALLOCATOR(CSSImportRule);
|
||||
|
||||
public:
|
||||
[[nodiscard]] static GC::Ref<CSSImportRule> create(URL::URL, DOM::Document&, RefPtr<Supports>, Vector<NonnullRefPtr<MediaQuery>>);
|
||||
[[nodiscard]] static GC::Ref<CSSImportRule> create(JS::Realm&, URL, GC::Ptr<DOM::Document>, RefPtr<Supports>, Vector<NonnullRefPtr<MediaQuery>>);
|
||||
|
||||
virtual ~CSSImportRule() = default;
|
||||
|
||||
URL::URL const& url() const { return m_url; }
|
||||
// FIXME: This should return only the specified part of the url. eg, "stuff/foo.css", not "https://example.com/stuff/foo.css".
|
||||
String href() const { return m_url.to_string(); }
|
||||
URL const& url() const { return m_url; }
|
||||
String href() const { return m_url.url(); }
|
||||
|
||||
CSSStyleSheet* loaded_style_sheet() { return m_style_sheet; }
|
||||
CSSStyleSheet const* loaded_style_sheet() const { return m_style_sheet; }
|
||||
|
@ -37,7 +36,7 @@ public:
|
|||
Optional<String> supports_text() const;
|
||||
|
||||
private:
|
||||
CSSImportRule(URL::URL, DOM::Document&, RefPtr<Supports>, Vector<NonnullRefPtr<MediaQuery>>);
|
||||
CSSImportRule(JS::Realm&, URL, GC::Ptr<DOM::Document>, RefPtr<Supports>, Vector<NonnullRefPtr<MediaQuery>>);
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
@ -49,7 +48,7 @@ private:
|
|||
void fetch();
|
||||
void set_style_sheet(GC::Ref<CSSStyleSheet>);
|
||||
|
||||
URL::URL m_url;
|
||||
URL m_url;
|
||||
GC::Ptr<DOM::Document> m_document;
|
||||
RefPtr<Supports> m_supports;
|
||||
Vector<NonnullRefPtr<MediaQuery>> m_media_query_list;
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
[Exposed=Window]
|
||||
interface CSSImportRule : CSSRule {
|
||||
readonly attribute USVString href;
|
||||
[SameObject, PutForwards=mediaText] readonly attribute MediaList media;
|
||||
// AD-HOC: media is null if styleSheet is null. Spec issue: https://github.com/w3c/csswg-drafts/issues/12063
|
||||
[SameObject, PutForwards=mediaText] readonly attribute MediaList? media;
|
||||
[SameObject, ImplementedAs=style_sheet_for_bindings] readonly attribute CSSStyleSheet? styleSheet;
|
||||
[FIXME] readonly attribute CSSOMString? layerName;
|
||||
readonly attribute CSSOMString? supportsText;
|
||||
|
|
|
@ -1158,6 +1158,13 @@ WebIDL::ExceptionOr<void> CSSStyleProperties::set_css_text(StringView css_text)
|
|||
// 4. Update style attribute for the CSS declaration block.
|
||||
update_style_attribute();
|
||||
|
||||
// Non-standard: Invalidate style for the owners of our containing sheet, if any.
|
||||
if (auto rule = parent_rule()) {
|
||||
if (auto sheet = rule->parent_style_sheet()) {
|
||||
sheet->invalidate_owners(DOM::StyleInvalidationReason::CSSStylePropertiesTextChange);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace Web::CSS {
|
|||
|
||||
GC_DEFINE_ALLOCATOR(CSSStyleSheet);
|
||||
|
||||
GC::Ref<CSSStyleSheet> CSSStyleSheet::create(JS::Realm& realm, CSSRuleList& rules, MediaList& media, Optional<URL::URL> location)
|
||||
GC::Ref<CSSStyleSheet> CSSStyleSheet::create(JS::Realm& realm, CSSRuleList& rules, MediaList& media, Optional<::URL::URL> location)
|
||||
{
|
||||
return realm.create<CSSStyleSheet>(realm, rules, media, move(location));
|
||||
}
|
||||
|
@ -37,16 +37,16 @@ WebIDL::ExceptionOr<GC::Ref<CSSStyleSheet>> CSSStyleSheet::construct_impl(JS::Re
|
|||
|
||||
// 2. Set sheet’s location to the base URL of the associated Document for the current principal global object.
|
||||
auto associated_document = as<HTML::Window>(HTML::current_principal_global_object()).document();
|
||||
sheet->set_location(associated_document->base_url().to_string());
|
||||
sheet->set_location(associated_document->base_url());
|
||||
|
||||
// 3. Set sheet’s stylesheet base URL to the baseURL attribute value from options.
|
||||
if (options.has_value() && options->base_url.has_value()) {
|
||||
Optional<URL::URL> sheet_location_url;
|
||||
Optional<::URL::URL> sheet_location_url;
|
||||
if (sheet->location().has_value())
|
||||
sheet_location_url = URL::Parser::basic_parse(sheet->location().release_value());
|
||||
sheet_location_url = sheet->location().release_value();
|
||||
|
||||
// AD-HOC: This isn't explicitly mentioned in the specification, but multiple modern browsers do this.
|
||||
Optional<URL::URL> url = sheet->location().has_value() ? sheet_location_url->complete_url(options->base_url.value()) : URL::Parser::basic_parse(options->base_url.value());
|
||||
Optional<::URL::URL> url = sheet->location().has_value() ? sheet_location_url->complete_url(options->base_url.value()) : ::URL::Parser::basic_parse(options->base_url.value());
|
||||
if (!url.has_value())
|
||||
return WebIDL::NotAllowedError::create(realm, "Constructed style sheets must have a valid base URL"_string);
|
||||
|
||||
|
@ -95,12 +95,12 @@ WebIDL::ExceptionOr<GC::Ref<CSSStyleSheet>> CSSStyleSheet::construct_impl(JS::Re
|
|||
return sheet;
|
||||
}
|
||||
|
||||
CSSStyleSheet::CSSStyleSheet(JS::Realm& realm, CSSRuleList& rules, MediaList& media, Optional<URL::URL> location)
|
||||
CSSStyleSheet::CSSStyleSheet(JS::Realm& realm, CSSRuleList& rules, MediaList& media, Optional<::URL::URL> location)
|
||||
: StyleSheet(realm, media)
|
||||
, m_rules(&rules)
|
||||
{
|
||||
if (location.has_value())
|
||||
set_location(location->to_string());
|
||||
set_location(move(location));
|
||||
|
||||
for (auto& rule : *m_rules)
|
||||
rule->set_parent_style_sheet(this);
|
||||
|
@ -140,8 +140,7 @@ WebIDL::ExceptionOr<unsigned> CSSStyleSheet::insert_rule(StringView rule, unsign
|
|||
return WebIDL::NotAllowedError::create(realm(), "Can't call insert_rule() on non-modifiable stylesheets."_string);
|
||||
|
||||
// 3. Let parsed rule be the return value of invoking parse a rule with rule.
|
||||
auto context = !m_owning_documents_or_shadow_roots.is_empty() ? Parser::ParsingParams { (*m_owning_documents_or_shadow_roots.begin())->document() } : Parser::ParsingParams { realm() };
|
||||
auto parsed_rule = parse_css_rule(context, rule);
|
||||
auto parsed_rule = parse_css_rule(make_parsing_params(), rule);
|
||||
|
||||
// 4. If parsed rule is a syntax error, return parsed rule.
|
||||
if (!parsed_rule)
|
||||
|
@ -208,8 +207,7 @@ GC::Ref<WebIDL::Promise> CSSStyleSheet::replace(String text)
|
|||
HTML::TemporaryExecutionContext execution_context { realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes };
|
||||
|
||||
// 1. Let rules be the result of running parse a stylesheet’s contents from text.
|
||||
auto context = !m_owning_documents_or_shadow_roots.is_empty() ? Parser::ParsingParams { (*m_owning_documents_or_shadow_roots.begin())->document() } : CSS::Parser::ParsingParams { realm };
|
||||
auto* parsed_stylesheet = parse_css_stylesheet(context, text);
|
||||
auto* parsed_stylesheet = parse_css_stylesheet(make_parsing_params(), text);
|
||||
auto& rules = parsed_stylesheet->rules();
|
||||
|
||||
// 2. If rules contains one or more @import rules, remove those rules from rules.
|
||||
|
@ -242,8 +240,7 @@ WebIDL::ExceptionOr<void> CSSStyleSheet::replace_sync(StringView text)
|
|||
return WebIDL::NotAllowedError::create(realm(), "Can't call replaceSync() on non-modifiable stylesheets"_string);
|
||||
|
||||
// 2. Let rules be the result of running parse a stylesheet’s contents from text.
|
||||
auto context = !m_owning_documents_or_shadow_roots.is_empty() ? Parser::ParsingParams { (*m_owning_documents_or_shadow_roots.begin())->document() } : CSS::Parser::ParsingParams { realm() };
|
||||
auto* parsed_stylesheet = parse_css_stylesheet(context, text);
|
||||
auto* parsed_stylesheet = parse_css_stylesheet(make_parsing_params(), text);
|
||||
auto& rules = parsed_stylesheet->rules();
|
||||
|
||||
// 3. If rules contains one or more @import rules, remove those rules from rules.
|
||||
|
@ -426,4 +423,11 @@ bool CSSStyleSheet::has_associated_font_loader(FontLoader& font_loader) const
|
|||
return false;
|
||||
}
|
||||
|
||||
Parser::ParsingParams CSSStyleSheet::make_parsing_params() const
|
||||
{
|
||||
if (!m_owning_documents_or_shadow_roots.is_empty())
|
||||
return Parser::ParsingParams { (*m_owning_documents_or_shadow_roots.begin())->document() };
|
||||
return Parser::ParsingParams { realm() };
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <LibWeb/CSS/CSSRule.h>
|
||||
#include <LibWeb/CSS/CSSRuleList.h>
|
||||
#include <LibWeb/CSS/CSSStyleRule.h>
|
||||
#include <LibWeb/CSS/Parser/Parser.h>
|
||||
#include <LibWeb/CSS/StyleSheet.h>
|
||||
#include <LibWeb/DOM/Node.h>
|
||||
#include <LibWeb/WebIDL/Types.h>
|
||||
|
@ -27,12 +28,13 @@ struct CSSStyleSheetInit {
|
|||
bool disabled { false };
|
||||
};
|
||||
|
||||
// https://drafts.csswg.org/cssom-1/#cssstylesheet
|
||||
class CSSStyleSheet final : public StyleSheet {
|
||||
WEB_PLATFORM_OBJECT(CSSStyleSheet, StyleSheet);
|
||||
GC_DECLARE_ALLOCATOR(CSSStyleSheet);
|
||||
|
||||
public:
|
||||
[[nodiscard]] static GC::Ref<CSSStyleSheet> create(JS::Realm&, CSSRuleList&, MediaList&, Optional<URL::URL> location);
|
||||
[[nodiscard]] static GC::Ref<CSSStyleSheet> create(JS::Realm&, CSSRuleList&, MediaList&, Optional<::URL::URL> location);
|
||||
static WebIDL::ExceptionOr<GC::Ref<CSSStyleSheet>> construct_impl(JS::Realm&, Optional<CSSStyleSheetInit> const& options = {});
|
||||
|
||||
virtual ~CSSStyleSheet() override = default;
|
||||
|
@ -74,8 +76,8 @@ public:
|
|||
|
||||
Vector<GC::Ref<CSSImportRule>> const& import_rules() const { return m_import_rules; }
|
||||
|
||||
Optional<URL::URL> base_url() const { return m_base_url; }
|
||||
void set_base_url(Optional<URL::URL> base_url) { m_base_url = move(base_url); }
|
||||
Optional<::URL::URL> base_url() const { return m_base_url; }
|
||||
void set_base_url(Optional<::URL::URL> base_url) { m_base_url = move(base_url); }
|
||||
|
||||
bool constructed() const { return m_constructed; }
|
||||
|
||||
|
@ -94,7 +96,7 @@ public:
|
|||
bool has_associated_font_loader(FontLoader& font_loader) const;
|
||||
|
||||
private:
|
||||
CSSStyleSheet(JS::Realm&, CSSRuleList&, MediaList&, Optional<URL::URL> location);
|
||||
CSSStyleSheet(JS::Realm&, CSSRuleList&, MediaList&, Optional<::URL::URL> location);
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
@ -104,6 +106,8 @@ private:
|
|||
void set_constructed(bool constructed) { m_constructed = constructed; }
|
||||
void set_disallow_modification(bool disallow_modification) { m_disallow_modification = disallow_modification; }
|
||||
|
||||
Parser::ParsingParams make_parsing_params() const;
|
||||
|
||||
Optional<String> m_source_text;
|
||||
|
||||
GC::Ptr<CSSRuleList> m_rules;
|
||||
|
@ -113,7 +117,7 @@ private:
|
|||
|
||||
GC::Ptr<CSSRule> m_owner_css_rule;
|
||||
|
||||
Optional<URL::URL> m_base_url;
|
||||
Optional<::URL::URL> m_base_url;
|
||||
GC::Ptr<DOM::Document const> m_constructor_document;
|
||||
HashTable<GC::Ptr<DOM::Node>> m_owning_documents_or_shadow_roots;
|
||||
bool m_constructed { false };
|
||||
|
|
|
@ -225,40 +225,40 @@ public:
|
|||
: m_value(color)
|
||||
{
|
||||
}
|
||||
SVGPaint(URL::URL const& url)
|
||||
SVGPaint(::URL::URL const& url)
|
||||
: m_value(url)
|
||||
{
|
||||
}
|
||||
|
||||
bool is_color() const { return m_value.has<Color>(); }
|
||||
bool is_url() const { return m_value.has<URL::URL>(); }
|
||||
bool is_url() const { return m_value.has<::URL::URL>(); }
|
||||
Color as_color() const { return m_value.get<Color>(); }
|
||||
URL::URL const& as_url() const { return m_value.get<URL::URL>(); }
|
||||
::URL::URL const& as_url() const { return m_value.get<::URL::URL>(); }
|
||||
|
||||
private:
|
||||
Variant<URL::URL, Color> m_value;
|
||||
Variant<::URL::URL, Color> m_value;
|
||||
};
|
||||
|
||||
// https://drafts.fxtf.org/css-masking-1/#typedef-mask-reference
|
||||
class MaskReference {
|
||||
public:
|
||||
// TODO: Support other mask types.
|
||||
MaskReference(URL::URL const& url)
|
||||
MaskReference(::URL::URL const& url)
|
||||
: m_url(url)
|
||||
{
|
||||
}
|
||||
|
||||
URL::URL const& url() const { return m_url; }
|
||||
::URL::URL const& url() const { return m_url; }
|
||||
|
||||
private:
|
||||
URL::URL m_url;
|
||||
::URL::URL m_url;
|
||||
};
|
||||
|
||||
// https://drafts.fxtf.org/css-masking/#the-clip-path
|
||||
// TODO: Support clip sources.
|
||||
class ClipPathReference {
|
||||
public:
|
||||
ClipPathReference(URL::URL const& url)
|
||||
ClipPathReference(::URL::URL const& url)
|
||||
: m_clip_source(url)
|
||||
{
|
||||
}
|
||||
|
@ -270,16 +270,16 @@ public:
|
|||
|
||||
bool is_basic_shape() const { return m_clip_source.has<BasicShape>(); }
|
||||
|
||||
bool is_url() const { return m_clip_source.has<URL::URL>(); }
|
||||
bool is_url() const { return m_clip_source.has<::URL::URL>(); }
|
||||
|
||||
URL::URL const& url() const { return m_clip_source.get<URL::URL>(); }
|
||||
::URL::URL const& url() const { return m_clip_source.get<::URL::URL>(); }
|
||||
|
||||
BasicShapeStyleValue const& basic_shape() const { return *m_clip_source.get<BasicShape>(); }
|
||||
|
||||
private:
|
||||
using BasicShape = NonnullRefPtr<BasicShapeStyleValue const>;
|
||||
|
||||
Variant<URL::URL, BasicShape> m_clip_source;
|
||||
Variant<::URL::URL, BasicShape> m_clip_source;
|
||||
};
|
||||
|
||||
struct BackgroundLayerData {
|
||||
|
|
|
@ -79,7 +79,7 @@ input[type=range] {
|
|||
width: 20ch;
|
||||
height: 16px;
|
||||
|
||||
&::track {
|
||||
&::slider-track {
|
||||
display: block;
|
||||
position: relative;
|
||||
height: 4px;
|
||||
|
@ -89,14 +89,14 @@ input[type=range] {
|
|||
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
&::fill {
|
||||
&::slider-fill {
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
background-color: AccentColor;
|
||||
}
|
||||
|
||||
&::thumb {
|
||||
&::slider-thumb {
|
||||
display: block;
|
||||
margin-top: -6px;
|
||||
width: 16px;
|
||||
|
@ -115,27 +115,27 @@ meter {
|
|||
width: 300px;
|
||||
height: 12px;
|
||||
|
||||
&::track {
|
||||
&::slider-track {
|
||||
display: block;
|
||||
height: 100%;
|
||||
background-color: hsl(0, 0%, 96%);
|
||||
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
&::fill {
|
||||
&::slider-fill {
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&:optimal-value::fill {
|
||||
&:optimal-value::slider-fill {
|
||||
background-color: hsl(141, 53%, 53%);
|
||||
}
|
||||
|
||||
&:suboptimal-value::fill {
|
||||
&:suboptimal-value::slider-fill {
|
||||
background-color: hsl(48, 100%, 67%);
|
||||
}
|
||||
|
||||
&:even-less-good-value::fill {
|
||||
&:even-less-good-value::slider-fill {
|
||||
background-color: hsl(348, 100%, 61%);
|
||||
}
|
||||
}
|
||||
|
@ -146,14 +146,14 @@ progress {
|
|||
width: 300px;
|
||||
height: 12px;
|
||||
|
||||
&::track {
|
||||
&::slider-track {
|
||||
display: block;
|
||||
height: 100%;
|
||||
background-color: AccentColorText;
|
||||
border: 1px solid rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
&::fill {
|
||||
&::slider-fill {
|
||||
display: block;
|
||||
height: 100%;
|
||||
background-color: AccentColor;
|
||||
|
|
|
@ -23,7 +23,7 @@ void fetch_a_style_resource(String const& url_value, CSSStyleSheet const& sheet,
|
|||
auto base = sheet.base_url().value_or(environment_settings.api_base_url());
|
||||
|
||||
// 3. Let parsedUrl be the result of the URL parser steps with urlValue’s url and base. If the algorithm returns an error, return.
|
||||
auto parsed_url = URL::Parser::basic_parse(url_value, base);
|
||||
auto parsed_url = ::URL::Parser::basic_parse(url_value, base);
|
||||
if (!parsed_url.has_value())
|
||||
return;
|
||||
|
||||
|
|
|
@ -586,8 +586,13 @@ NonnullRefPtr<CSSStyleValue const> interpolate_value(DOM::Element& element, Calc
|
|||
layout_node = *node;
|
||||
return CSSColorValue::create_from_color(interpolate_color(from.to_color(layout_node), to.to_color(layout_node), delta), ColorSyntax::Modern);
|
||||
}
|
||||
case CSSStyleValue::Type::Integer:
|
||||
return IntegerStyleValue::create(interpolate_raw(from.as_integer().integer(), to.as_integer().integer(), delta));
|
||||
case CSSStyleValue::Type::Integer: {
|
||||
// https://drafts.csswg.org/css-values/#combine-integers
|
||||
// Interpolation of <integer> is defined as Vresult = round((1 - p) × VA + p × VB);
|
||||
// that is, interpolation happens in the real number space as for <number>s, and the result is converted to an <integer> by rounding to the nearest integer.
|
||||
auto interpolated_value = interpolate_raw(from.as_integer().value(), to.as_integer().value(), delta);
|
||||
return IntegerStyleValue::create(round_to<i64>(interpolated_value));
|
||||
}
|
||||
case CSSStyleValue::Type::Length: {
|
||||
// FIXME: Absolutize values
|
||||
auto const& from_length = from.as_length().length();
|
||||
|
@ -611,6 +616,11 @@ NonnullRefPtr<CSSStyleValue const> interpolate_value(DOM::Element& element, Calc
|
|||
auto from_ratio = from.as_ratio().ratio();
|
||||
auto to_ratio = to.as_ratio().ratio();
|
||||
|
||||
// https://drafts.csswg.org/css-values/#combine-ratio
|
||||
// If either <ratio> is degenerate, the values cannot be interpolated.
|
||||
if (from_ratio.is_degenerate() || to_ratio.is_degenerate())
|
||||
return delta >= 0.5f ? to : from;
|
||||
|
||||
// The interpolation of a <ratio> is defined by converting each <ratio> to a number by dividing the first value
|
||||
// by the second (so a ratio of 3 / 2 would become 1.5), taking the logarithm of that result (so the 1.5 would
|
||||
// become approximately 0.176), then interpolating those values. The result during the interpolation is
|
||||
|
|
|
@ -38,7 +38,7 @@ Vector<ParsedFontFace::Source> ParsedFontFace::sources_from_style_value(CSSStyle
|
|||
[&](FontSourceStyleValue::Local const& local) {
|
||||
sources.empend(extract_font_name(local.name), OptionalNone {});
|
||||
},
|
||||
[&](URL::URL const& url) {
|
||||
[&](::URL::URL const& url) {
|
||||
// FIXME: tech()
|
||||
sources.empend(url, font_source.format());
|
||||
});
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Web::CSS {
|
|||
class ParsedFontFace {
|
||||
public:
|
||||
struct Source {
|
||||
Variant<FlyString, URL::URL> local_or_url;
|
||||
Variant<FlyString, ::URL::URL> local_or_url;
|
||||
// FIXME: Do we need to keep this around, or is it only needed to discard unwanted formats during parsing?
|
||||
Optional<FlyString> format;
|
||||
};
|
||||
|
|
|
@ -42,7 +42,7 @@ GC::Ref<JS::Realm> internal_css_realm()
|
|||
return *realm;
|
||||
}
|
||||
|
||||
CSS::CSSStyleSheet* parse_css_stylesheet(CSS::Parser::ParsingParams const& context, StringView css, Optional<URL::URL> location, Vector<NonnullRefPtr<CSS::MediaQuery>> media_query_list)
|
||||
CSS::CSSStyleSheet* parse_css_stylesheet(CSS::Parser::ParsingParams const& context, StringView css, Optional<::URL::URL> location, Vector<NonnullRefPtr<CSS::MediaQuery>> media_query_list)
|
||||
{
|
||||
if (css.is_empty()) {
|
||||
auto rule_list = CSS::CSSRuleList::create_empty(*context.realm);
|
||||
|
|
|
@ -45,14 +45,14 @@ ParsingParams::ParsingParams(JS::Realm& realm, ParsingMode mode)
|
|||
{
|
||||
}
|
||||
|
||||
ParsingParams::ParsingParams(JS::Realm& realm, URL::URL url, ParsingMode mode)
|
||||
ParsingParams::ParsingParams(JS::Realm& realm, ::URL::URL url, ParsingMode mode)
|
||||
: realm(realm)
|
||||
, url(move(url))
|
||||
, mode(mode)
|
||||
{
|
||||
}
|
||||
|
||||
ParsingParams::ParsingParams(DOM::Document const& document, URL::URL url, ParsingMode mode)
|
||||
ParsingParams::ParsingParams(DOM::Document const& document, ::URL::URL url, ParsingMode mode)
|
||||
: realm(const_cast<JS::Realm&>(document.realm()))
|
||||
, document(&document)
|
||||
, url(move(url))
|
||||
|
@ -86,7 +86,7 @@ Parser::Parser(ParsingParams const& context, Vector<Token> tokens)
|
|||
|
||||
// https://drafts.csswg.org/css-syntax/#parse-stylesheet
|
||||
template<typename T>
|
||||
Parser::ParsedStyleSheet Parser::parse_a_stylesheet(TokenStream<T>& input, Optional<URL::URL> location)
|
||||
Parser::ParsedStyleSheet Parser::parse_a_stylesheet(TokenStream<T>& input, Optional<::URL::URL> location)
|
||||
{
|
||||
// To parse a stylesheet from an input given an optional url location:
|
||||
|
||||
|
@ -119,10 +119,10 @@ Vector<Rule> Parser::parse_a_stylesheets_contents(TokenStream<T>& input)
|
|||
}
|
||||
|
||||
// https://drafts.csswg.org/css-syntax/#parse-a-css-stylesheet
|
||||
CSSStyleSheet* Parser::parse_as_css_stylesheet(Optional<URL::URL> location, Vector<NonnullRefPtr<MediaQuery>> media_query_list)
|
||||
CSSStyleSheet* Parser::parse_as_css_stylesheet(Optional<::URL::URL> location, Vector<NonnullRefPtr<MediaQuery>> media_query_list)
|
||||
{
|
||||
// To parse a CSS stylesheet, first parse a stylesheet.
|
||||
auto const& style_sheet = parse_a_stylesheet(m_token_stream, {});
|
||||
auto const& style_sheet = parse_a_stylesheet(m_token_stream, location);
|
||||
|
||||
// Interpret all of the resulting top-level qualified rules as style rules, defined below.
|
||||
GC::RootVector<CSSRule*> rules(realm().heap());
|
||||
|
@ -1772,8 +1772,8 @@ Parser::ContextType Parser::context_type_for_at_rule(FlyString const& name)
|
|||
return ContextType::Unknown;
|
||||
}
|
||||
|
||||
template Parser::ParsedStyleSheet Parser::parse_a_stylesheet(TokenStream<Token>&, Optional<URL::URL>);
|
||||
template Parser::ParsedStyleSheet Parser::parse_a_stylesheet(TokenStream<ComponentValue>&, Optional<URL::URL>);
|
||||
template Parser::ParsedStyleSheet Parser::parse_a_stylesheet(TokenStream<Token>&, Optional<::URL::URL>);
|
||||
template Parser::ParsedStyleSheet Parser::parse_a_stylesheet(TokenStream<ComponentValue>&, Optional<::URL::URL>);
|
||||
|
||||
template Vector<Rule> Parser::parse_a_stylesheets_contents(TokenStream<Token>& input);
|
||||
template Vector<Rule> Parser::parse_a_stylesheets_contents(TokenStream<ComponentValue>& input);
|
||||
|
@ -1853,10 +1853,10 @@ bool Parser::is_parsing_svg_presentation_attribute() const
|
|||
|
||||
// https://www.w3.org/TR/css-values-4/#relative-urls
|
||||
// FIXME: URLs shouldn't be completed during parsing, but when used.
|
||||
Optional<URL::URL> Parser::complete_url(StringView relative_url) const
|
||||
Optional<::URL::URL> Parser::complete_url(StringView relative_url) const
|
||||
{
|
||||
if (!m_url.is_valid())
|
||||
return URL::Parser::basic_parse(relative_url);
|
||||
return ::URL::Parser::basic_parse(relative_url);
|
||||
return m_url.complete_url(relative_url);
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <LibWeb/CSS/StyleValues/BasicShapeStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
|
||||
#include <LibWeb/CSS/Supports.h>
|
||||
#include <LibWeb/CSS/URL.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
|
||||
namespace Web::CSS::Parser {
|
||||
|
@ -69,13 +70,13 @@ enum class ParsingMode {
|
|||
struct ParsingParams {
|
||||
explicit ParsingParams(ParsingMode = ParsingMode::Normal);
|
||||
explicit ParsingParams(JS::Realm&, ParsingMode = ParsingMode::Normal);
|
||||
explicit ParsingParams(JS::Realm&, URL::URL, ParsingMode = ParsingMode::Normal);
|
||||
explicit ParsingParams(DOM::Document const&, URL::URL, ParsingMode = ParsingMode::Normal);
|
||||
explicit ParsingParams(JS::Realm&, ::URL::URL, ParsingMode = ParsingMode::Normal);
|
||||
explicit ParsingParams(DOM::Document const&, ::URL::URL, ParsingMode = ParsingMode::Normal);
|
||||
explicit ParsingParams(DOM::Document const&, ParsingMode = ParsingMode::Normal);
|
||||
|
||||
GC::Ptr<JS::Realm> realm;
|
||||
GC::Ptr<DOM::Document const> document;
|
||||
URL::URL url;
|
||||
::URL::URL url;
|
||||
ParsingMode mode { ParsingMode::Normal };
|
||||
};
|
||||
|
||||
|
@ -89,7 +90,7 @@ class Parser {
|
|||
public:
|
||||
static Parser create(ParsingParams const&, StringView input, StringView encoding = "utf-8"sv);
|
||||
|
||||
CSSStyleSheet* parse_as_css_stylesheet(Optional<URL::URL> location, Vector<NonnullRefPtr<MediaQuery>> media_query_list = {});
|
||||
CSSStyleSheet* parse_as_css_stylesheet(Optional<::URL::URL> location, Vector<NonnullRefPtr<MediaQuery>> media_query_list = {});
|
||||
|
||||
struct PropertiesAndCustomProperties {
|
||||
Vector<StyleProperty> properties;
|
||||
|
@ -142,11 +143,11 @@ private:
|
|||
|
||||
// "Parse a stylesheet" is intended to be the normal parser entry point, for parsing stylesheets.
|
||||
struct ParsedStyleSheet {
|
||||
Optional<URL::URL> location;
|
||||
Optional<::URL::URL> location;
|
||||
Vector<Rule> rules;
|
||||
};
|
||||
template<typename T>
|
||||
ParsedStyleSheet parse_a_stylesheet(TokenStream<T>&, Optional<URL::URL> location);
|
||||
ParsedStyleSheet parse_a_stylesheet(TokenStream<T>&, Optional<::URL::URL> location);
|
||||
|
||||
// "Parse a stylesheet’s contents" is intended for use by the CSSStyleSheet replace() method, and similar, which parse text into the contents of an existing stylesheet.
|
||||
template<typename T>
|
||||
|
@ -276,7 +277,7 @@ private:
|
|||
Optional<GridRepeat> parse_repeat(Vector<ComponentValue> const&);
|
||||
Optional<ExplicitGridTrack> parse_track_sizing_function(ComponentValue const&);
|
||||
|
||||
Optional<URL::URL> parse_url_function(TokenStream<ComponentValue>&);
|
||||
Optional<URL> parse_url_function(TokenStream<ComponentValue>&);
|
||||
RefPtr<CSSStyleValue> parse_url_value(TokenStream<ComponentValue>&);
|
||||
|
||||
Optional<ShapeRadius> parse_shape_radius(TokenStream<ComponentValue>&);
|
||||
|
@ -471,11 +472,11 @@ private:
|
|||
JS::Realm& realm() const;
|
||||
bool in_quirks_mode() const;
|
||||
bool is_parsing_svg_presentation_attribute() const;
|
||||
Optional<URL::URL> complete_url(StringView) const;
|
||||
Optional<::URL::URL> complete_url(StringView) const;
|
||||
|
||||
GC::Ptr<DOM::Document const> m_document;
|
||||
GC::Ptr<JS::Realm> m_realm;
|
||||
URL::URL m_url;
|
||||
::URL::URL m_url;
|
||||
ParsingMode m_parsing_mode { ParsingMode::Normal };
|
||||
|
||||
Vector<Token> m_tokens;
|
||||
|
@ -519,7 +520,7 @@ private:
|
|||
|
||||
namespace Web {
|
||||
|
||||
CSS::CSSStyleSheet* parse_css_stylesheet(CSS::Parser::ParsingParams const&, StringView, Optional<URL::URL> location = {}, Vector<NonnullRefPtr<CSS::MediaQuery>> = {});
|
||||
CSS::CSSStyleSheet* parse_css_stylesheet(CSS::Parser::ParsingParams const&, StringView, Optional<::URL::URL> location = {}, Vector<NonnullRefPtr<CSS::MediaQuery>> = {});
|
||||
CSS::Parser::Parser::PropertiesAndCustomProperties parse_css_style_attribute(CSS::Parser::ParsingParams const&, StringView);
|
||||
Vector<CSS::Descriptor> parse_css_list_of_descriptors(CSS::Parser::ParsingParams const&, CSS::AtRuleID, StringView);
|
||||
RefPtr<CSS::CSSStyleValue> parse_css_value(CSS::Parser::ParsingParams const&, StringView, CSS::PropertyID property_id = CSS::PropertyID::Invalid);
|
||||
|
|
|
@ -153,9 +153,9 @@ GC::Ptr<CSSImportRule> Parser::convert_to_import_rule(AtRule const& rule)
|
|||
TokenStream tokens { rule.prelude };
|
||||
tokens.discard_whitespace();
|
||||
|
||||
Optional<URL::URL> url = parse_url_function(tokens);
|
||||
Optional<URL> url = parse_url_function(tokens);
|
||||
if (!url.has_value() && tokens.next_token().is(Token::Type::String))
|
||||
url = complete_url(tokens.consume_a_token().token().string());
|
||||
url = URL { tokens.consume_a_token().token().string().to_string() };
|
||||
|
||||
if (!url.has_value()) {
|
||||
dbgln_if(CSS_PARSER_DEBUG, "Failed to parse @import rule: Unable to parse `{}` as URL.", tokens.next_token().to_debug_string());
|
||||
|
@ -191,7 +191,7 @@ GC::Ptr<CSSImportRule> Parser::convert_to_import_rule(AtRule const& rule)
|
|||
return {};
|
||||
}
|
||||
|
||||
return CSSImportRule::create(url.value(), const_cast<DOM::Document&>(*document()), supports, move(media_query_list));
|
||||
return CSSImportRule::create(realm(), url.release_value(), const_cast<DOM::Document*>(m_document.ptr()), supports, move(media_query_list));
|
||||
}
|
||||
|
||||
Optional<FlyString> Parser::parse_layer_name(TokenStream<ComponentValue>& tokens, AllowBlankLayerName allow_blank_layer_name)
|
||||
|
@ -435,7 +435,10 @@ GC::Ptr<CSSNamespaceRule> Parser::convert_to_namespace_rule(AtRule const& rule)
|
|||
|
||||
FlyString namespace_uri;
|
||||
if (auto url = parse_url_function(tokens); url.has_value()) {
|
||||
namespace_uri = url.value().to_string();
|
||||
// "A URI string parsed from the URI syntax must be treated as a literal string: as with the STRING syntax, no
|
||||
// URI-specific normalization is applied."
|
||||
// https://drafts.csswg.org/css-namespaces/#syntax
|
||||
namespace_uri = url->url();
|
||||
} else if (auto& url_token = tokens.consume_a_token(); url_token.is(Token::Type::String)) {
|
||||
namespace_uri = url_token.token().string();
|
||||
} else {
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include <AK/Debug.h>
|
||||
#include <AK/GenericLexer.h>
|
||||
#include <AK/TemporaryChange.h>
|
||||
#include <LibURL/URL.h>
|
||||
#include <LibWeb/CSS/FontFace.h>
|
||||
#include <LibWeb/CSS/Parser/Parser.h>
|
||||
#include <LibWeb/CSS/PropertyName.h>
|
||||
|
@ -2014,9 +2013,13 @@ RefPtr<AbstractImageStyleValue> Parser::parse_image_value(TokenStream<ComponentV
|
|||
if (url.has_value()) {
|
||||
// If the value is a 'url(..)' parse as image, but if it is just a reference 'url(#xx)', leave it alone,
|
||||
// so we can parse as URL further on. These URLs are used as references inside SVG documents for masks.
|
||||
if (!url.value().equals(m_url, URL::ExcludeFragment::Yes)) {
|
||||
tokens.discard_a_mark();
|
||||
return ImageStyleValue::create(url.value());
|
||||
if (!url->url().starts_with('#')) {
|
||||
// FIXME: Stop completing the URL here
|
||||
auto completed_url = complete_url(url->url());
|
||||
if (completed_url.has_value()) {
|
||||
tokens.discard_a_mark();
|
||||
return ImageStyleValue::create(completed_url.release_value());
|
||||
}
|
||||
}
|
||||
tokens.restore_a_mark();
|
||||
return nullptr;
|
||||
|
@ -2562,24 +2565,16 @@ RefPtr<CSSStyleValue> Parser::parse_easing_value(TokenStream<ComponentValue>& to
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Optional<URL::URL> Parser::parse_url_function(TokenStream<ComponentValue>& tokens)
|
||||
Optional<URL> Parser::parse_url_function(TokenStream<ComponentValue>& tokens)
|
||||
{
|
||||
auto transaction = tokens.begin_transaction();
|
||||
auto& component_value = tokens.consume_a_token();
|
||||
|
||||
auto convert_string_to_url = [&](StringView url_string) -> Optional<URL::URL> {
|
||||
auto url = complete_url(url_string);
|
||||
if (url.has_value()) {
|
||||
transaction.commit();
|
||||
return url;
|
||||
}
|
||||
return {};
|
||||
};
|
||||
auto const& component_value = tokens.consume_a_token();
|
||||
|
||||
if (component_value.is(Token::Type::Url)) {
|
||||
auto url_string = component_value.token().url();
|
||||
return convert_string_to_url(url_string);
|
||||
transaction.commit();
|
||||
return URL { component_value.token().url().to_string() };
|
||||
}
|
||||
|
||||
if (component_value.is_function("url"sv)) {
|
||||
auto const& function_values = component_value.function().value;
|
||||
// FIXME: Handle url-modifiers. https://www.w3.org/TR/css-values-4/#url-modifiers
|
||||
|
@ -2588,8 +2583,8 @@ Optional<URL::URL> Parser::parse_url_function(TokenStream<ComponentValue>& token
|
|||
if (value.is(Token::Type::Whitespace))
|
||||
continue;
|
||||
if (value.is(Token::Type::String)) {
|
||||
auto url_string = value.token().string();
|
||||
return convert_string_to_url(url_string);
|
||||
transaction.commit();
|
||||
return URL { value.token().string().to_string() };
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -2603,7 +2598,11 @@ RefPtr<CSSStyleValue> Parser::parse_url_value(TokenStream<ComponentValue>& token
|
|||
auto url = parse_url_function(tokens);
|
||||
if (!url.has_value())
|
||||
return nullptr;
|
||||
return URLStyleValue::create(*url);
|
||||
// FIXME: Stop completing the URL here
|
||||
auto completed_url = complete_url(url->url());
|
||||
if (!completed_url.has_value())
|
||||
return nullptr;
|
||||
return URLStyleValue::create(completed_url.release_value());
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/css-shapes-1/#typedef-shape-radius
|
||||
|
@ -3681,7 +3680,11 @@ RefPtr<FontSourceStyleValue> Parser::parse_font_source_value(TokenStream<Compone
|
|||
|
||||
// <url> [ format(<font-format>)]? [ tech( <font-tech>#)]?
|
||||
auto url = parse_url_function(tokens);
|
||||
if (!url.has_value() || !url->is_valid())
|
||||
if (!url.has_value())
|
||||
return nullptr;
|
||||
// FIXME: Stop completing the URL here
|
||||
auto completed_url = complete_url(url->url());
|
||||
if (!completed_url.has_value())
|
||||
return nullptr;
|
||||
|
||||
Optional<FlyString> format;
|
||||
|
@ -3719,7 +3722,7 @@ RefPtr<FontSourceStyleValue> Parser::parse_font_source_value(TokenStream<Compone
|
|||
// FIXME: [ tech( <font-tech>#)]?
|
||||
|
||||
transaction.commit();
|
||||
return FontSourceStyleValue::create(url.release_value(), move(format));
|
||||
return FontSourceStyleValue::create(completed_url.release_value(), move(format));
|
||||
}
|
||||
|
||||
NonnullRefPtr<CSSStyleValue> Parser::resolve_unresolved_style_value(ParsingParams const& context, DOM::Element& element, Optional<PseudoElement> pseudo_element, PropertyID property_id, UnresolvedStyleValue const& unresolved)
|
||||
|
|
|
@ -1,33 +1,33 @@
|
|||
{
|
||||
"-moz-meter-bar": {
|
||||
"alias-for": "fill"
|
||||
"alias-for": "slider-fill"
|
||||
},
|
||||
"-moz-progress-bar": {
|
||||
"alias-for": "fill"
|
||||
"alias-for": "slider-fill"
|
||||
},
|
||||
"-moz-range-progress": {
|
||||
"alias-for": "fill"
|
||||
"alias-for": "slider-fill"
|
||||
},
|
||||
"-moz-range-track": {
|
||||
"alias-for": "track"
|
||||
"alias-for": "slider-track"
|
||||
},
|
||||
"-moz-range-thumb": {
|
||||
"alias-for": "thumb"
|
||||
"alias-for": "slider-thumb"
|
||||
},
|
||||
"-webkit-meter-bar": {
|
||||
"alias-for": "track"
|
||||
"alias-for": "slider-track"
|
||||
},
|
||||
"-webkit-progress-bar": {
|
||||
"alias-for": "track"
|
||||
"alias-for": "slider-track"
|
||||
},
|
||||
"-webkit-progress-value": {
|
||||
"alias-for": "fill"
|
||||
"alias-for": "slider-fill"
|
||||
},
|
||||
"-webkit-slider-runnable-track": {
|
||||
"alias-for": "track"
|
||||
"alias-for": "slider-track"
|
||||
},
|
||||
"-webkit-slider-thumb": {
|
||||
"alias-for": "thumb"
|
||||
"alias-for": "slider-thumb"
|
||||
},
|
||||
"after": {
|
||||
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-after",
|
||||
|
@ -47,9 +47,6 @@
|
|||
"file-selector-button": {
|
||||
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-file-selector-button"
|
||||
},
|
||||
"fill": {
|
||||
"spec": "https://drafts.csswg.org/css-forms-1/#selectordef-fill"
|
||||
},
|
||||
"first-letter": {
|
||||
"spec": "https://drafts.csswg.org/css-pseudo-4/#selectordef-first-letter",
|
||||
"property-whitelist": [
|
||||
|
@ -107,11 +104,14 @@
|
|||
"#custom-properties"
|
||||
]
|
||||
},
|
||||
"thumb": {
|
||||
"spec": "https://drafts.csswg.org/css-forms-1/#selectordef-thumb"
|
||||
"slider-fill": {
|
||||
"spec": "https://drafts.csswg.org/css-forms-1/#selectordef-slider-fill"
|
||||
},
|
||||
"track": {
|
||||
"spec": "https://drafts.csswg.org/css-forms-1/#selectordef-track"
|
||||
"slider-thumb": {
|
||||
"spec": "https://drafts.csswg.org/css-forms-1/#selectordef-slider-thumb"
|
||||
},
|
||||
"slider-track": {
|
||||
"spec": "https://drafts.csswg.org/css-forms-1/#selectordef-slider-track"
|
||||
},
|
||||
"view-transition": {
|
||||
"spec": "https://drafts.csswg.org/css-view-transitions-1/#selectordef-view-transition"
|
||||
|
|
|
@ -185,7 +185,7 @@ StyleComputer::StyleComputer(DOM::Document& document)
|
|||
|
||||
StyleComputer::~StyleComputer() = default;
|
||||
|
||||
FontLoader::FontLoader(StyleComputer& style_computer, FlyString family_name, Vector<Gfx::UnicodeRange> unicode_ranges, Vector<URL::URL> urls, Function<void(FontLoader const&)> on_load, Function<void()> on_fail)
|
||||
FontLoader::FontLoader(StyleComputer& style_computer, FlyString family_name, Vector<Gfx::UnicodeRange> unicode_ranges, Vector<::URL::URL> urls, Function<void(FontLoader const&)> on_load, Function<void()> on_fail)
|
||||
: m_style_computer(style_computer)
|
||||
, m_family_name(move(family_name))
|
||||
, m_unicode_ranges(move(unicode_ranges))
|
||||
|
@ -3028,11 +3028,11 @@ Optional<FontLoader&> StyleComputer::load_font_face(ParsedFontFace const& font_f
|
|||
.slope = font_face.slope().value_or(0),
|
||||
};
|
||||
|
||||
Vector<URL::URL> urls;
|
||||
Vector<::URL::URL> urls;
|
||||
for (auto const& source : font_face.sources()) {
|
||||
// FIXME: These should be loaded relative to the stylesheet URL instead of the document URL.
|
||||
if (source.local_or_url.has<URL::URL>())
|
||||
urls.append(*m_document->encoding_parse_url(source.local_or_url.get<URL::URL>().to_string()));
|
||||
if (source.local_or_url.has<::URL::URL>())
|
||||
urls.append(*m_document->encoding_parse_url(source.local_or_url.get<::URL::URL>().to_string()));
|
||||
// FIXME: Handle local()
|
||||
}
|
||||
|
||||
|
|
|
@ -315,7 +315,7 @@ private:
|
|||
|
||||
class FontLoader : public ResourceClient {
|
||||
public:
|
||||
FontLoader(StyleComputer& style_computer, FlyString family_name, Vector<Gfx::UnicodeRange> unicode_ranges, Vector<URL::URL> urls, ESCAPING Function<void(FontLoader const&)> on_load = {}, ESCAPING Function<void()> on_fail = {});
|
||||
FontLoader(StyleComputer& style_computer, FlyString family_name, Vector<Gfx::UnicodeRange> unicode_ranges, Vector<::URL::URL> urls, ESCAPING Function<void(FontLoader const&)> on_load = {}, ESCAPING Function<void()> on_fail = {});
|
||||
|
||||
virtual ~FontLoader() override;
|
||||
|
||||
|
@ -340,7 +340,7 @@ private:
|
|||
FlyString m_family_name;
|
||||
Vector<Gfx::UnicodeRange> m_unicode_ranges;
|
||||
RefPtr<Gfx::Typeface> m_vector_font;
|
||||
Vector<URL::URL> m_urls;
|
||||
Vector<::URL::URL> m_urls;
|
||||
Function<void(FontLoader const&)> m_on_load;
|
||||
Function<void()> m_on_fail;
|
||||
};
|
||||
|
|
|
@ -27,6 +27,13 @@ void StyleSheet::visit_edges(Cell::Visitor& visitor)
|
|||
visitor.visit(m_media);
|
||||
}
|
||||
|
||||
Optional<String> StyleSheet::href() const
|
||||
{
|
||||
if (m_location.has_value())
|
||||
return m_location->to_string();
|
||||
return {};
|
||||
}
|
||||
|
||||
void StyleSheet::set_owner_node(DOM::Element* element)
|
||||
{
|
||||
m_owner_node = element;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
namespace Web::CSS {
|
||||
|
||||
// https://drafts.csswg.org/cssom-1/#the-stylesheet-interface
|
||||
class StyleSheet : public Bindings::PlatformObject {
|
||||
WEB_PLATFORM_OBJECT(StyleSheet, Bindings::PlatformObject);
|
||||
|
||||
|
@ -24,10 +25,10 @@ public:
|
|||
DOM::Element* owner_node() { return m_owner_node; }
|
||||
void set_owner_node(DOM::Element*);
|
||||
|
||||
Optional<String> href() const { return m_location; }
|
||||
Optional<String> href() const;
|
||||
|
||||
Optional<String> location() const { return m_location; }
|
||||
void set_location(Optional<String> location) { m_location = move(location); }
|
||||
Optional<::URL::URL> location() const { return m_location; }
|
||||
void set_location(Optional<::URL::URL> location) { m_location = move(location); }
|
||||
|
||||
String title() const { return m_title; }
|
||||
Optional<String> title_for_bindings() const;
|
||||
|
@ -35,7 +36,7 @@ public:
|
|||
|
||||
void set_type(String type) { m_type_string = move(type); }
|
||||
|
||||
MediaList* media() const
|
||||
GC::Ref<MediaList> media() const
|
||||
{
|
||||
return m_media;
|
||||
}
|
||||
|
@ -67,7 +68,7 @@ private:
|
|||
GC::Ptr<DOM::Element> m_owner_node;
|
||||
GC::Ptr<CSSStyleSheet> m_parent_style_sheet;
|
||||
|
||||
Optional<String> m_location;
|
||||
Optional<::URL::URL> m_location;
|
||||
String m_title;
|
||||
String m_type_string;
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ void StyleSheetList::add_a_css_style_sheet(CSS::CSSStyleSheet& sheet)
|
|||
}
|
||||
|
||||
// https://www.w3.org/TR/cssom/#create-a-css-style-sheet
|
||||
void StyleSheetList::create_a_css_style_sheet(String type, DOM::Element* owner_node, String media, String title, bool alternate, bool origin_clean, Optional<String> location, CSS::CSSStyleSheet* parent_style_sheet, CSS::CSSRule* owner_rule, CSS::CSSStyleSheet& sheet)
|
||||
void StyleSheetList::create_a_css_style_sheet(String type, DOM::Element* owner_node, String media, String title, bool alternate, bool origin_clean, Optional<::URL::URL> location, CSS::CSSStyleSheet* parent_style_sheet, CSS::CSSRule* owner_rule, CSS::CSSStyleSheet& sheet)
|
||||
{
|
||||
// 1. Create a new CSS style sheet object and set its properties as specified.
|
||||
// FIXME: We receive `sheet` from the caller already. This is weird.
|
||||
|
|
|
@ -21,7 +21,7 @@ public:
|
|||
|
||||
void add_a_css_style_sheet(CSS::CSSStyleSheet&);
|
||||
void remove_a_css_style_sheet(CSS::CSSStyleSheet&);
|
||||
void create_a_css_style_sheet(String type, DOM::Element* owner_node, String media, String title, bool alternate, bool origin_clean, Optional<String> location, CSS::CSSStyleSheet* parent_style_sheet, CSS::CSSRule* owner_rule, CSS::CSSStyleSheet&);
|
||||
void create_a_css_style_sheet(String type, DOM::Element* owner_node, String media, String title, bool alternate, bool origin_clean, Optional<::URL::URL> location, CSS::CSSStyleSheet* parent_style_sheet, CSS::CSSRule* owner_rule, CSS::CSSStyleSheet&);
|
||||
|
||||
Vector<GC::Ref<CSSStyleSheet>> const& sheets() const { return m_sheets; }
|
||||
Vector<GC::Ref<CSSStyleSheet>>& sheets() { return m_sheets; }
|
||||
|
|
|
@ -94,7 +94,8 @@ Optional<Gfx::ImageCursor> CursorStyleValue::make_image_cursor(Layout::NodeWithS
|
|||
case DisplayListPlayerType::SkiaCPU: {
|
||||
auto painting_surface = Gfx::PaintingSurface::wrap_bitmap(bitmap);
|
||||
Painting::DisplayListPlayerSkia display_list_player;
|
||||
display_list_player.execute(*display_list, painting_surface);
|
||||
Painting::ScrollStateSnapshot scroll_state_snapshot;
|
||||
display_list_player.execute(*display_list, scroll_state_snapshot, painting_surface);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ String FontSourceStyleValue::to_string(SerializationMode) const
|
|||
builder.append(')');
|
||||
return builder.to_string_without_validation();
|
||||
},
|
||||
[this](URL::URL const& url) {
|
||||
[this](::URL::URL const& url) {
|
||||
// <url> [ format(<font-format>)]? [ tech( <font-tech>#)]?
|
||||
// FIXME: tech()
|
||||
StringBuilder builder;
|
||||
|
@ -59,8 +59,8 @@ bool FontSourceStyleValue::properties_equal(FontSourceStyleValue const& other) c
|
|||
}
|
||||
return false;
|
||||
},
|
||||
[&other](URL::URL const& url) {
|
||||
if (auto* other_url = other.m_source.get_pointer<URL::URL>()) {
|
||||
[&other](::URL::URL const& url) {
|
||||
if (auto* other_url = other.m_source.get_pointer<::URL::URL>()) {
|
||||
return url == *other_url;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -16,7 +16,7 @@ public:
|
|||
struct Local {
|
||||
NonnullRefPtr<CSSStyleValue> name;
|
||||
};
|
||||
using Source = Variant<Local, URL::URL>;
|
||||
using Source = Variant<Local, ::URL::URL>;
|
||||
|
||||
static ValueComparingNonnullRefPtr<FontSourceStyleValue> create(Source source, Optional<FlyString> format)
|
||||
{
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
namespace Web::CSS {
|
||||
|
||||
ImageStyleValue::ImageStyleValue(URL::URL const& url)
|
||||
ImageStyleValue::ImageStyleValue(::URL::URL const& url)
|
||||
: AbstractImageStyleValue(Type::Image)
|
||||
, m_url(url)
|
||||
{
|
||||
|
|
|
@ -25,7 +25,7 @@ class ImageStyleValue final
|
|||
using Base = AbstractImageStyleValue;
|
||||
|
||||
public:
|
||||
static ValueComparingNonnullRefPtr<ImageStyleValue> create(URL::URL const& url)
|
||||
static ValueComparingNonnullRefPtr<ImageStyleValue> create(::URL::URL const& url)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) ImageStyleValue(url));
|
||||
}
|
||||
|
@ -53,14 +53,14 @@ public:
|
|||
GC::Ptr<HTML::DecodedImageData> image_data() const;
|
||||
|
||||
private:
|
||||
ImageStyleValue(URL::URL const&);
|
||||
ImageStyleValue(::URL::URL const&);
|
||||
|
||||
GC::Ptr<HTML::SharedResourceRequest> m_resource_request;
|
||||
|
||||
void animate();
|
||||
Gfx::ImmutableBitmap const* bitmap(size_t frame_index, Gfx::IntSize = {}) const;
|
||||
|
||||
URL::URL m_url;
|
||||
::URL::URL m_url;
|
||||
WeakPtr<DOM::Document> m_document;
|
||||
|
||||
size_t m_current_frame_index { 0 };
|
||||
|
|
|
@ -14,14 +14,14 @@ namespace Web::CSS {
|
|||
|
||||
class URLStyleValue final : public StyleValueWithDefaultOperators<URLStyleValue> {
|
||||
public:
|
||||
static ValueComparingNonnullRefPtr<URLStyleValue> create(URL::URL const& url)
|
||||
static ValueComparingNonnullRefPtr<URLStyleValue> create(::URL::URL const& url)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) URLStyleValue(url));
|
||||
}
|
||||
|
||||
virtual ~URLStyleValue() override = default;
|
||||
|
||||
URL::URL const& url() const { return m_url; }
|
||||
::URL::URL const& url() const { return m_url; }
|
||||
|
||||
bool properties_equal(URLStyleValue const& other) const { return m_url == other.m_url; }
|
||||
|
||||
|
@ -31,13 +31,13 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
URLStyleValue(URL::URL const& url)
|
||||
URLStyleValue(::URL::URL const& url)
|
||||
: StyleValueWithDefaultOperators(Type::URL)
|
||||
, m_url(url)
|
||||
{
|
||||
}
|
||||
|
||||
URL::URL m_url;
|
||||
::URL::URL m_url;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
31
Libraries/LibWeb/CSS/URL.cpp
Normal file
31
Libraries/LibWeb/CSS/URL.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/CSS/Serialize.h>
|
||||
#include <LibWeb/CSS/URL.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
||||
URL::URL(String url)
|
||||
: m_url(move(url))
|
||||
{
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/cssom-1/#serialize-a-url
|
||||
String URL::to_string() const
|
||||
{
|
||||
// To serialize a URL means to create a string represented by "url(", followed by the serialization of the URL as a string, followed by ")".
|
||||
StringBuilder builder;
|
||||
builder.append("url("sv);
|
||||
serialize_a_string(builder, m_url);
|
||||
builder.append(')');
|
||||
|
||||
return builder.to_string_without_validation();
|
||||
}
|
||||
|
||||
bool URL::operator==(URL const&) const = default;
|
||||
|
||||
}
|
27
Libraries/LibWeb/CSS/URL.h
Normal file
27
Libraries/LibWeb/CSS/URL.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
||||
// https://drafts.csswg.org/css-values-4/#urls
|
||||
class URL {
|
||||
public:
|
||||
URL(String url);
|
||||
|
||||
String const& url() const { return m_url; }
|
||||
|
||||
String to_string() const;
|
||||
bool operator==(URL const&) const;
|
||||
|
||||
private:
|
||||
String m_url;
|
||||
};
|
||||
|
||||
}
|
|
@ -30,6 +30,7 @@
|
|||
#include <LibJS/Runtime/ArrayBuffer.h>
|
||||
#include <LibJS/Runtime/DataView.h>
|
||||
#include <LibJS/Runtime/TypedArray.h>
|
||||
#include <LibJS/Runtime/ValueInlines.h>
|
||||
#include <LibWeb/Bindings/ExceptionOrUtils.h>
|
||||
#include <LibWeb/Crypto/CryptoAlgorithms.h>
|
||||
#include <LibWeb/Crypto/KeyAlgorithms.h>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <LibCrypto/Hash/HashManager.h>
|
||||
#include <LibJS/Runtime/ArrayBuffer.h>
|
||||
#include <LibJS/Runtime/JSONObject.h>
|
||||
#include <LibJS/Runtime/ValueInlines.h>
|
||||
#include <LibWeb/Bindings/ExceptionOrUtils.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/Bindings/SubtleCryptoPrototype.h>
|
||||
|
|
|
@ -35,14 +35,22 @@ void AbortSignal::initialize(JS::Realm& realm)
|
|||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#abortsignal-add
|
||||
void AbortSignal::add_abort_algorithm(Function<void()> abort_algorithm)
|
||||
Optional<AbortSignal::AbortAlgorithmID> AbortSignal::add_abort_algorithm(Function<void()> abort_algorithm)
|
||||
{
|
||||
// 1. If signal is aborted, then return.
|
||||
if (aborted())
|
||||
return;
|
||||
return {};
|
||||
|
||||
// 2. Append algorithm to signal’s abort algorithms.
|
||||
m_abort_algorithms.append(GC::create_function(vm().heap(), move(abort_algorithm)));
|
||||
m_abort_algorithms.set(++m_next_abort_algorithm_id, GC::create_function(vm().heap(), move(abort_algorithm)));
|
||||
return m_next_abort_algorithm_id;
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#abortsignal-remove
|
||||
void AbortSignal::remove_abort_algorithm(AbortAlgorithmID id)
|
||||
{
|
||||
// To remove an algorithm algorithm from an AbortSignal signal, remove algorithm from signal’s abort algorithms.
|
||||
m_abort_algorithms.remove(id);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#abortsignal-signal-abort
|
||||
|
@ -76,8 +84,8 @@ void AbortSignal::signal_abort(JS::Value reason)
|
|||
// https://dom.spec.whatwg.org/#run-the-abort-steps
|
||||
auto run_the_abort_steps = [](auto& signal) {
|
||||
// 1. For each algorithm in signal’s abort algorithms: run algorithm.
|
||||
for (auto& algorithm : signal.m_abort_algorithms)
|
||||
algorithm->function()();
|
||||
for (auto const& algorithm : signal.m_abort_algorithms)
|
||||
algorithm.value->function()();
|
||||
|
||||
// 2. Empty signal’s abort algorithms.
|
||||
signal.m_abort_algorithms.clear();
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/Weakable.h>
|
||||
#include <LibGC/Function.h>
|
||||
|
@ -26,7 +27,9 @@ public:
|
|||
|
||||
virtual ~AbortSignal() override = default;
|
||||
|
||||
void add_abort_algorithm(ESCAPING Function<void()>);
|
||||
using AbortAlgorithmID = u64;
|
||||
Optional<AbortAlgorithmID> add_abort_algorithm(Function<void()>);
|
||||
void remove_abort_algorithm(AbortAlgorithmID);
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-abortsignal-aborted
|
||||
// An AbortSignal object is aborted when its abort reason is not undefined.
|
||||
|
@ -68,8 +71,8 @@ private:
|
|||
JS::Value m_abort_reason { JS::js_undefined() };
|
||||
|
||||
// https://dom.spec.whatwg.org/#abortsignal-abort-algorithms
|
||||
// FIXME: This should be a set.
|
||||
Vector<GC::Ref<GC::Function<void()>>> m_abort_algorithms;
|
||||
OrderedHashMap<AbortAlgorithmID, GC::Ref<GC::Function<void()>>> m_abort_algorithms;
|
||||
AbortAlgorithmID m_next_abort_algorithm_id { 0 };
|
||||
|
||||
// https://dom.spec.whatwg.org/#abortsignal-source-signals
|
||||
// An AbortSignal object has associated source signals (a weak set of AbortSignal objects that the object is dependent on for its aborted state), which is initially empty.
|
||||
|
|
|
@ -584,7 +584,8 @@ void Document::visit_edges(Cell::Visitor& visitor)
|
|||
|
||||
visitor.visit(m_adopted_style_sheets);
|
||||
|
||||
visitor.visit(m_shadow_roots);
|
||||
for (auto& shadow_root : m_shadow_roots)
|
||||
visitor.visit(shadow_root);
|
||||
|
||||
visitor.visit(m_top_layer_elements);
|
||||
visitor.visit(m_top_layer_pending_removals);
|
||||
|
@ -5879,7 +5880,7 @@ void Document::for_each_active_css_style_sheet(Function<void(CSS::CSSStyleSheet&
|
|||
|
||||
static Optional<CSS::CSSStyleSheet&> find_style_sheet_with_url(String const& url, CSS::CSSStyleSheet& style_sheet)
|
||||
{
|
||||
if (style_sheet.location() == url)
|
||||
if (style_sheet.href() == url)
|
||||
return style_sheet;
|
||||
|
||||
for (auto& import_rule : style_sheet.import_rules()) {
|
||||
|
@ -5953,9 +5954,7 @@ void Document::register_shadow_root(Badge<DOM::ShadowRoot>, DOM::ShadowRoot& sha
|
|||
|
||||
void Document::unregister_shadow_root(Badge<DOM::ShadowRoot>, DOM::ShadowRoot& shadow_root)
|
||||
{
|
||||
m_shadow_roots.remove_all_matching([&](auto& item) {
|
||||
return item.ptr() == &shadow_root;
|
||||
});
|
||||
m_shadow_roots.remove(shadow_root);
|
||||
}
|
||||
|
||||
void Document::for_each_shadow_root(Function<void(DOM::ShadowRoot&)>&& callback)
|
||||
|
@ -5967,7 +5966,7 @@ void Document::for_each_shadow_root(Function<void(DOM::ShadowRoot&)>&& callback)
|
|||
void Document::for_each_shadow_root(Function<void(DOM::ShadowRoot&)>&& callback) const
|
||||
{
|
||||
for (auto& shadow_root : m_shadow_roots)
|
||||
callback(shadow_root);
|
||||
callback(const_cast<ShadowRoot&>(shadow_root));
|
||||
}
|
||||
|
||||
bool Document::is_decoded_svg() const
|
||||
|
@ -6297,8 +6296,9 @@ void Document::invalidate_display_list()
|
|||
|
||||
RefPtr<Painting::DisplayList> Document::record_display_list(PaintConfig config)
|
||||
{
|
||||
if (m_cached_display_list && m_cached_display_list_paint_config == config)
|
||||
if (m_cached_display_list && m_cached_display_list_paint_config == config) {
|
||||
return m_cached_display_list;
|
||||
}
|
||||
|
||||
auto display_list = Painting::DisplayList::create();
|
||||
Painting::DisplayListRecorder display_list_recorder(display_list);
|
||||
|
@ -6355,7 +6355,6 @@ RefPtr<Painting::DisplayList> Document::record_display_list(PaintConfig config)
|
|||
viewport_paintable.paint_all_phases(context);
|
||||
|
||||
display_list->set_device_pixels_per_css_pixel(page().client().device_pixels_per_css_pixel());
|
||||
display_list->set_scroll_state(viewport_paintable.scroll_state());
|
||||
|
||||
m_cached_display_list = display_list;
|
||||
m_cached_display_list_paint_config = config;
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <LibWeb/CSS/StyleSheetList.h>
|
||||
#include <LibWeb/Cookie/Cookie.h>
|
||||
#include <LibWeb/DOM/ParentNode.h>
|
||||
#include <LibWeb/DOM/ShadowRoot.h>
|
||||
#include <LibWeb/HTML/BrowsingContext.h>
|
||||
#include <LibWeb/HTML/CrossOrigin/OpenerPolicy.h>
|
||||
#include <LibWeb/HTML/DocumentReadyState.h>
|
||||
|
@ -1180,7 +1181,7 @@ private:
|
|||
|
||||
mutable GC::Ptr<WebIDL::ObservableArray> m_adopted_style_sheets;
|
||||
|
||||
Vector<GC::Ref<DOM::ShadowRoot>> m_shadow_roots;
|
||||
ShadowRoot::DocumentShadowRootList m_shadow_roots;
|
||||
|
||||
Optional<Core::DateTime> m_last_modified;
|
||||
|
||||
|
|
|
@ -448,7 +448,7 @@ GC::Ptr<DOM::Document> load_document(HTML::NavigationParams const& navigation_pa
|
|||
// sourceSnapshotParams, and initiatorOrigin.
|
||||
}
|
||||
|
||||
// -> A supported image, video, or audio type
|
||||
// -> a supported image, video, or audio type
|
||||
if (type.is_image()
|
||||
|| type.is_audio_or_video()) {
|
||||
// Return the result of loading a media document given navigationParams and type.
|
||||
|
|
|
@ -2548,7 +2548,7 @@ JS::ThrowCompletionOr<void> Element::upgrade_element(GC::Ref<HTML::CustomElement
|
|||
|
||||
// FIXME: 9. If element is a form-associated custom element, then:
|
||||
// 1. Reset the form owner of element. If element is associated with a form element, then enqueue a custom element callback reaction with element, callback name "formAssociatedCallback", and « the associated form ».
|
||||
// 2. If element is disabled, then enqueue a custom element callback reaction with element, callback name "formDisabledCallback" and « true ».
|
||||
// 2. If element is disabled, then enqueue a custom element callback reaction with element, callback name "formDisabledCallback", and « true ».
|
||||
|
||||
// 10. Set element's custom element state to "custom".
|
||||
set_custom_element_state(CustomElementState::Custom);
|
||||
|
|
|
@ -120,6 +120,10 @@ interface Element : Node {
|
|||
// FIXME: [CEReactions] undefined insertAdjacentHTML(DOMString position, (TrustedHTML or DOMString) string);
|
||||
[CEReactions] undefined insertAdjacentHTML(DOMString position, DOMString text);
|
||||
|
||||
// https://w3c.github.io/pointerevents/#extensions-to-the-element-interface
|
||||
[FIXME] undefined setPointerCapture(long pointerId);
|
||||
[FIXME] undefined releasePointerCapture(long pointerId);
|
||||
[FIXME] boolean hasPointerCapture(long pointerId);
|
||||
};
|
||||
|
||||
dictionary GetHTMLOptions {
|
||||
|
|
|
@ -231,7 +231,7 @@ void EventTarget::add_an_event_listener(DOMEventListener& listener)
|
|||
// 6. If listener’s signal is not null, then add the following abort steps to it:
|
||||
if (listener.signal) {
|
||||
// NOTE: `this` and `listener` are protected by AbortSignal using GC::HeapFunction.
|
||||
listener.signal->add_abort_algorithm([this, &listener] {
|
||||
(void)listener.signal->add_abort_algorithm([this, &listener] {
|
||||
// 1. Remove an event listener with eventTarget and listener.
|
||||
remove_an_event_listener(listener);
|
||||
});
|
||||
|
|
|
@ -53,6 +53,7 @@ enum class ShouldComputeRole {
|
|||
X(AdoptedStyleSheetsList) \
|
||||
X(CSSFontLoaded) \
|
||||
X(CSSImportRule) \
|
||||
X(CSSStylePropertiesTextChange) \
|
||||
X(CustomElementStateChange) \
|
||||
X(DidLoseFocus) \
|
||||
X(DidReceiveFocus) \
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/ValueInlines.h>
|
||||
#include <LibWeb/Bindings/NodeIteratorPrototype.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/DOM/Node.h>
|
||||
|
|
|
@ -97,6 +97,11 @@ private:
|
|||
|
||||
GC::Ptr<CSS::StyleSheetList> m_style_sheets;
|
||||
mutable GC::Ptr<WebIDL::ObservableArray> m_adopted_style_sheets;
|
||||
|
||||
IntrusiveListNode<ShadowRoot> m_list_node;
|
||||
|
||||
public:
|
||||
using DocumentShadowRootList = IntrusiveList<&ShadowRoot::m_list_node>;
|
||||
};
|
||||
|
||||
template<>
|
||||
|
|
|
@ -54,7 +54,8 @@ void StyleElementUtils::update_a_style_block(DOM::Element& style_element)
|
|||
|
||||
// FIXME: This is a bit awkward, as the spec doesn't actually tell us when to parse the CSS text,
|
||||
// so we just do it here and pass the parsed sheet to create_a_css_style_sheet().
|
||||
auto* sheet = parse_css_stylesheet(CSS::Parser::ParsingParams(style_element.document()), style_element.text_content().value_or(String {}));
|
||||
// AD-HOC: Are we supposed to use the document's URL for the stylesheet's location? Not doing it breaks things.
|
||||
auto* sheet = parse_css_stylesheet(CSS::Parser::ParsingParams(style_element.document()), style_element.text_content().value_or(String {}), style_element.document().url());
|
||||
if (!sheet)
|
||||
return;
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/ValueInlines.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/Bindings/TreeWalkerPrototype.h>
|
||||
#include <LibWeb/DOM/Node.h>
|
||||
|
|
|
@ -790,7 +790,7 @@ void dump_font_face_rule(StringBuilder& builder, CSS::CSSFontFaceRule const& rul
|
|||
void dump_import_rule(StringBuilder& builder, CSS::CSSImportRule const& rule, int indent_levels)
|
||||
{
|
||||
indent(builder, indent_levels);
|
||||
builder.appendff(" Document URL: {}\n", rule.url());
|
||||
builder.appendff(" Document URL: {}\n", rule.url().to_string());
|
||||
}
|
||||
|
||||
void dump_layer_block_rule(StringBuilder& builder, CSS::CSSLayerBlockRule const& layer_block, int indent_levels)
|
||||
|
|
|
@ -36,13 +36,13 @@ FlyString const& PerformanceEventTiming::entry_type() const
|
|||
|
||||
HighResolutionTime::DOMHighResTimeStamp PerformanceEventTiming::processing_end() const
|
||||
{
|
||||
dbgln("FIXME: Implement PeformanceEventTiming processing_end()");
|
||||
dbgln("FIXME: Implement PerformanceEventTiming processing_end()");
|
||||
return 0;
|
||||
}
|
||||
|
||||
HighResolutionTime::DOMHighResTimeStamp PerformanceEventTiming::processing_start() const
|
||||
{
|
||||
dbgln("FIXME: Implement PeformanceEventTiming processing_start()");
|
||||
dbgln("FIXME: Implement PerformanceEventTiming processing_start()");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -53,20 +53,20 @@ bool PerformanceEventTiming::cancelable() const
|
|||
|
||||
JS::ThrowCompletionOr<GC::Ptr<DOM::Node>> PerformanceEventTiming::target()
|
||||
{
|
||||
dbgln("FIXME: Implement PerformanceEventTiming::PeformanceEventTiming target()");
|
||||
dbgln("FIXME: Implement PerformanceEventTiming::PerformanceEventTiming target()");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
unsigned long long PerformanceEventTiming::interaction_id()
|
||||
{
|
||||
dbgln("FIXME: Implement PeformanceEventTiming interaction_id()");
|
||||
dbgln("FIXME: Implement PerformanceEventTiming interaction_id()");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/event-timing/#sec-should-add-performanceeventtiming
|
||||
PerformanceTimeline::ShouldAddEntry PerformanceEventTiming::should_add_performance_event_timing() const
|
||||
{
|
||||
dbgln("FIXME: Implement PeformanceEventTiming should_add_performance_event_timing()");
|
||||
dbgln("FIXME: Implement PerformanceEventTiming should_add_performance_event_timing()");
|
||||
// 1. If entry’s entryType attribute value equals to "first-input", return true.
|
||||
if (entry_type() == "first-input")
|
||||
return PerformanceTimeline::ShouldAddEntry::Yes;
|
||||
|
@ -89,7 +89,7 @@ PerformanceTimeline::ShouldAddEntry PerformanceEventTiming::should_add_performan
|
|||
// the commented out if statement won't compile
|
||||
PerformanceTimeline::AvailableFromTimeline PerformanceEventTiming::available_from_timeline()
|
||||
{
|
||||
dbgln("FIXME: Implement PeformanceEventTiming available_from_timeline()");
|
||||
dbgln("FIXME: Implement PerformanceEventTiming available_from_timeline()");
|
||||
// if (entry_type() == "first-input")
|
||||
return PerformanceTimeline::AvailableFromTimeline::Yes;
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ PerformanceTimeline::AvailableFromTimeline PerformanceEventTiming::available_fro
|
|||
// FIXME: Same issue as available_from_timeline() above
|
||||
Optional<u64> PerformanceEventTiming::max_buffer_size()
|
||||
{
|
||||
dbgln("FIXME: Implement PeformanceEventTiming max_buffer_size()");
|
||||
dbgln("FIXME: Implement PerformanceEventTiming max_buffer_size()");
|
||||
if (true) //(entry_type() == "first-input")
|
||||
return 1;
|
||||
// else return 150;
|
||||
|
|
|
@ -130,7 +130,7 @@ GC::Ref<WebIDL::Promise> fetch(JS::VM& vm, RequestInfo const& input, RequestInit
|
|||
}))));
|
||||
|
||||
// 11. Add the following abort steps to requestObject’s signal:
|
||||
request_object->signal()->add_abort_algorithm([locally_aborted, request, controller_holder, promise_capability, request_object, response_object, &relevant_realm] {
|
||||
(void)request_object->signal()->add_abort_algorithm([locally_aborted, request, controller_holder, promise_capability, request_object, response_object, &relevant_realm] {
|
||||
dbgln_if(WEB_FETCH_DEBUG, "Fetch: Request object signal's abort algorithm called");
|
||||
|
||||
// 1. Set locallyAborted to true.
|
||||
|
|
|
@ -269,6 +269,7 @@ class TransformationStyleValue;
|
|||
class TransitionStyleValue;
|
||||
class UnicodeRangeStyleValue;
|
||||
class UnresolvedStyleValue;
|
||||
class URL;
|
||||
class URLStyleValue;
|
||||
class VisualViewport;
|
||||
|
||||
|
@ -624,6 +625,7 @@ class IDBOpenDBRequest;
|
|||
class IDBRequest;
|
||||
class IDBTransaction;
|
||||
class IDBVersionChangeEvent;
|
||||
class Index;
|
||||
class ObjectStore;
|
||||
class RequestList;
|
||||
}
|
||||
|
|
|
@ -103,17 +103,17 @@ GC::Ref<DOMRect> DOMQuad::get_bounds() const
|
|||
}
|
||||
|
||||
// https://drafts.fxtf.org/geometry/#structured-serialization
|
||||
WebIDL::ExceptionOr<void> DOMQuad::serialization_steps(HTML::SerializationRecord& serialzied, bool for_storage, HTML::SerializationMemory& memory)
|
||||
WebIDL::ExceptionOr<void> DOMQuad::serialization_steps(HTML::SerializationRecord& serialized, bool for_storage, HTML::SerializationMemory& memory)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
// 1. Set serialized.[[P1]] to the sub-serialization of value’s point 1.
|
||||
serialzied.extend(TRY(HTML::structured_serialize_internal(vm, m_p1, for_storage, memory)));
|
||||
serialized.extend(TRY(HTML::structured_serialize_internal(vm, m_p1, for_storage, memory)));
|
||||
// 2. Set serialized.[[P2]] to the sub-serialization of value’s point 2.
|
||||
serialzied.extend(TRY(HTML::structured_serialize_internal(vm, m_p2, for_storage, memory)));
|
||||
serialized.extend(TRY(HTML::structured_serialize_internal(vm, m_p2, for_storage, memory)));
|
||||
// 3. Set serialized.[[P3]] to the sub-serialization of value’s point 3.
|
||||
serialzied.extend(TRY(HTML::structured_serialize_internal(vm, m_p3, for_storage, memory)));
|
||||
serialized.extend(TRY(HTML::structured_serialize_internal(vm, m_p3, for_storage, memory)));
|
||||
// 4. Set serialized.[[P4]] to the sub-serialization of value’s point 4.
|
||||
serialzied.extend(TRY(HTML::structured_serialize_internal(vm, m_p4, for_storage, memory)));
|
||||
serialized.extend(TRY(HTML::structured_serialize_internal(vm, m_p4, for_storage, memory)));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ void CanvasPath::bezier_curve_to(double cp1x, double cp1y, double cp2x, double c
|
|||
// 2. Ensure there is a subpath for (cp1x, cp1y)
|
||||
ensure_subpath(cp1x, cp1y);
|
||||
|
||||
// 3. Connect the last point in the subpath to the given point (x, y) using a cubic Bézier curve with control poits (cp1x, cp1y) and (cp2x, cp2y).
|
||||
// 3. Connect the last point in the subpath to the given point (x, y) using a cubic Bézier curve with control points (cp1x, cp1y) and (cp2x, cp2y).
|
||||
// 4. Add the point (x, y) to the subpath.
|
||||
m_path.cubic_bezier_curve_to(
|
||||
Gfx::FloatPoint { cp1x, cp1y }, Gfx::FloatPoint { cp2x, cp2y }, Gfx::FloatPoint { x, y });
|
||||
|
|
|
@ -263,10 +263,10 @@ Gfx::Path CanvasRenderingContext2D::text_path(StringView text, float x, float y,
|
|||
}
|
||||
|
||||
// Apply text baseline
|
||||
// FIXME: Implement CanvasTextBasline::Hanging, Bindings::CanvasTextAlign::Alphabetic and Bindings::CanvasTextAlign::Ideographic for real
|
||||
// FIXME: Implement CanvasTextBaseline::Hanging, Bindings::CanvasTextAlign::Alphabetic and Bindings::CanvasTextAlign::Ideographic for real
|
||||
// right now they are just handled as textBaseline = top or bottom.
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-textbaseline-hanging
|
||||
// Default baseline of draw_text is top so do nothing by CanvasTextBaseline::Top and CanvasTextBasline::Hanging
|
||||
// Default baseline of draw_text is top so do nothing by CanvasTextBaseline::Top and CanvasTextBaseline::Hanging
|
||||
if (drawing_state.text_baseline == Bindings::CanvasTextBaseline::Middle) {
|
||||
transform = Gfx::AffineTransform {}.set_translation({ 0, font->pixel_size() / 2 }).multiply(transform);
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ WebIDL::ExceptionOr<GC::Ref<CloseWatcher>> CloseWatcher::construct_impl(JS::Real
|
|||
}
|
||||
|
||||
// 3.2 Add the following steps to options["signal"]:
|
||||
signal->add_abort_algorithm([close_watcher] {
|
||||
(void)signal->add_abort_algorithm([close_watcher] {
|
||||
// 3.2.1 Destroy closeWatcher.
|
||||
close_watcher->destroy();
|
||||
});
|
||||
|
|
|
@ -233,7 +233,7 @@ static Optional<YearAndMonth> parse_a_month_component(GenericLexer& input)
|
|||
{
|
||||
// 1. Collect a sequence of code points that are ASCII digits from input given position. If the collected sequence is
|
||||
// not at least four characters long, then fail. Otherwise, interpret the resulting sequence as a base-ten integer.
|
||||
// Let that number be the year.
|
||||
// Let year be that number.
|
||||
auto year_string = input.consume_while(is_ascii_digit);
|
||||
if (year_string.length() < 4)
|
||||
return {};
|
||||
|
@ -252,8 +252,8 @@ static Optional<YearAndMonth> parse_a_month_component(GenericLexer& input)
|
|||
return {};
|
||||
|
||||
// 4. Collect a sequence of code points that are ASCII digits from input given position. If the collected sequence is not
|
||||
// exactly two characters long, then fail. Otherwise, interpret the resulting sequence as a base-ten integer. Let that
|
||||
// number be the month.
|
||||
// exactly two characters long, then fail. Otherwise, interpret the resulting sequence as a base-ten integer. Let month
|
||||
// be that number.
|
||||
auto month_string = input.consume_while(is_ascii_digit);
|
||||
if (month_string.length() != 2)
|
||||
return {};
|
||||
|
@ -301,7 +301,7 @@ Optional<WeekYearAndWeek> parse_a_week_string(StringView input_view)
|
|||
|
||||
// 3. Collect a sequence of code points that are ASCII digits from input given position. If the collected sequence is
|
||||
// not at least four characters long, then fail. Otherwise, interpret the resulting sequence as a base-ten integer.
|
||||
// Let that number be the year.
|
||||
// Let year be that number.
|
||||
auto year_string = input.consume_while(is_ascii_digit);
|
||||
if (year_string.length() < 4)
|
||||
return {};
|
||||
|
@ -325,8 +325,8 @@ Optional<WeekYearAndWeek> parse_a_week_string(StringView input_view)
|
|||
return {};
|
||||
|
||||
// 7. Collect a sequence of code points that are ASCII digits from input given position. If the collected sequence is not
|
||||
// exactly two characters long, then fail. Otherwise, interpret the resulting sequence as a base-ten integer. Let that
|
||||
// number be the week.
|
||||
// exactly two characters long, then fail. Otherwise, interpret the resulting sequence as a base-ten integer. Let week
|
||||
// be that number.
|
||||
auto week_string = input.consume_while(is_ascii_digit);
|
||||
if (week_string.length() != 2)
|
||||
return {};
|
||||
|
@ -365,8 +365,8 @@ static Optional<YearMonthDay> parse_a_date_component(GenericLexer& input)
|
|||
return {};
|
||||
|
||||
// 4. Collect a sequence of code points that are ASCII digits from input given position. If the collected sequence is not
|
||||
// exactly two characters long, then fail. Otherwise, interpret the resulting sequence as a base-ten integer. Let that
|
||||
// number be the day.
|
||||
// exactly two characters long, then fail. Otherwise, interpret the resulting sequence as a base-ten integer. Let day
|
||||
// be that number.
|
||||
auto day_string = input.consume_while(is_ascii_digit);
|
||||
if (day_string.length() != 2)
|
||||
return {};
|
||||
|
@ -406,7 +406,7 @@ static Optional<HourMinuteSecond> parse_a_time_component(GenericLexer& input)
|
|||
{
|
||||
// 1. Collect a sequence of code points that are ASCII digits from input given position. If the collected sequence
|
||||
// is not exactly two characters long, then fail. Otherwise, interpret the resulting sequence as a base-ten
|
||||
// integer. Let that number be the hour.
|
||||
// integer. Let hour be that number.
|
||||
auto hour_string = input.consume_while(is_ascii_digit);
|
||||
if (hour_string.length() != 2)
|
||||
return {};
|
||||
|
@ -426,7 +426,7 @@ static Optional<HourMinuteSecond> parse_a_time_component(GenericLexer& input)
|
|||
|
||||
// 4. Collect a sequence of code points that are ASCII digits from input given position. If the collected sequence
|
||||
// is not exactly two characters long, then fail. Otherwise, interpret the resulting sequence as a base-ten integer.
|
||||
// Let that number be the minute.
|
||||
// Let minute be that number.
|
||||
auto minute_string = input.consume_while(is_ascii_digit);
|
||||
if (minute_string.length() != 2)
|
||||
return {};
|
||||
|
|
|
@ -389,7 +389,7 @@ void EventSource::process_field(StringView field, StringView value)
|
|||
{
|
||||
// -> If the field name is "event"
|
||||
if (field == "event"sv) {
|
||||
// Set the event type buffer to field value.
|
||||
// Set the event type buffer to the field value.
|
||||
m_event_type = MUST(String::from_utf8(value));
|
||||
}
|
||||
// -> If the field name is "data"
|
||||
|
|
|
@ -1121,7 +1121,7 @@ WebIDL::ExceptionOr<bool> HTMLElement::check_popover_validity(ExpectedToBeShowin
|
|||
// - ignoreDomState is false and element is not connected;
|
||||
// - element's node document is not fully active;
|
||||
// - ignoreDomState is false and expectedDocument is not null and element's node document is not expectedDocument;
|
||||
// - element is a dialog element and its is modal flage is set to true; or
|
||||
// - element is a dialog element and its is modal flag is set to true; or
|
||||
// - FIXME: element's fullscreen flag is set,
|
||||
// then:
|
||||
// 3.1 If throwExceptions is true, then throw an "InvalidStateError" DOMException.
|
||||
|
|
|
@ -145,7 +145,7 @@ void HTMLInputElement::adjust_computed_style(CSS::ComputedProperties& style)
|
|||
style.set_property(CSS::PropertyID::Width, CSS::LengthStyleValue::create(CSS::Length(size(), CSS::Length::Type::Ch)));
|
||||
}
|
||||
|
||||
// NOTE: The following line-height check is done for web compatability and usability reasons.
|
||||
// NOTE: The following line-height check is done for web compatibility and usability reasons.
|
||||
// FIXME: The "normal" line-height value should be calculated but assume 1.0 for now.
|
||||
double normal_line_height = 1.0;
|
||||
double current_line_height = style.line_height().to_double();
|
||||
|
@ -1217,15 +1217,15 @@ void HTMLInputElement::create_range_input_shadow_tree()
|
|||
set_shadow_root(shadow_root);
|
||||
|
||||
m_slider_runnable_track = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
|
||||
m_slider_runnable_track->set_use_pseudo_element(CSS::PseudoElement::Track);
|
||||
m_slider_runnable_track->set_use_pseudo_element(CSS::PseudoElement::SliderTrack);
|
||||
MUST(shadow_root->append_child(*m_slider_runnable_track));
|
||||
|
||||
m_slider_progress_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
|
||||
m_slider_progress_element->set_use_pseudo_element(CSS::PseudoElement::Fill);
|
||||
m_slider_progress_element->set_use_pseudo_element(CSS::PseudoElement::SliderFill);
|
||||
MUST(m_slider_runnable_track->append_child(*m_slider_progress_element));
|
||||
|
||||
m_slider_thumb = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
|
||||
m_slider_thumb->set_use_pseudo_element(CSS::PseudoElement::Thumb);
|
||||
m_slider_thumb->set_use_pseudo_element(CSS::PseudoElement::SliderThumb);
|
||||
MUST(m_slider_runnable_track->append_child(*m_slider_thumb));
|
||||
|
||||
update_slider_shadow_tree_elements();
|
||||
|
@ -2404,7 +2404,7 @@ WebIDL::ExceptionOr<GC::Ptr<JS::Date>> HTMLInputElement::convert_string_to_date(
|
|||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/input.html#concept-input-value-date-string
|
||||
String HTMLInputElement::covert_date_to_string(GC::Ref<JS::Date> input) const
|
||||
String HTMLInputElement::convert_date_to_string(GC::Ref<JS::Date> input) const
|
||||
{
|
||||
// https://html.spec.whatwg.org/multipage/input.html#date-state-(type=date):concept-input-value-date-string
|
||||
if (type_state() == TypeAttributeState::Date) {
|
||||
|
@ -2420,7 +2420,7 @@ String HTMLInputElement::covert_date_to_string(GC::Ref<JS::Date> input) const
|
|||
return convert_number_to_time_string(input->date_value());
|
||||
}
|
||||
|
||||
dbgln("HTMLInputElement::covert_date_to_string() not implemented for input type {}", type());
|
||||
dbgln("HTMLInputElement::convert_date_to_string() not implemented for input type {}", type());
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -2589,7 +2589,7 @@ WebIDL::ExceptionOr<void> HTMLInputElement::set_value_as_date(Optional<GC::Root<
|
|||
}
|
||||
|
||||
// otherwise, run the algorithm to convert a Date object to a string, as defined for that state, on the new value, and set the value of the element to the resulting string.
|
||||
TRY(set_value(covert_date_to_string(date)));
|
||||
TRY(set_value(convert_date_to_string(date)));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -289,7 +289,7 @@ private:
|
|||
String convert_number_to_string(double input) const;
|
||||
|
||||
WebIDL::ExceptionOr<GC::Ptr<JS::Date>> convert_string_to_date(StringView input) const;
|
||||
String covert_date_to_string(GC::Ref<JS::Date> input) const;
|
||||
String convert_date_to_string(GC::Ref<JS::Date> input) const;
|
||||
|
||||
Optional<double> min() const;
|
||||
Optional<double> max() const;
|
||||
|
|
|
@ -280,7 +280,7 @@ HTMLLinkElement::LinkProcessingOptions HTMLLinkElement::create_link_options()
|
|||
options.policy_container = document.policy_container();
|
||||
// document document
|
||||
options.document = &document;
|
||||
// FIXME: cryptographic nonce metadata The current value of el's [[CryptographicNonce]] internal slot
|
||||
// FIXME: cryptographic nonce metadata the current value of el's [[CryptographicNonce]] internal slot
|
||||
// fetch priority the state of el's fetchpriority content attribute
|
||||
options.fetch_priority = Fetch::Infrastructure::request_priority_from_string(get_attribute_value(HTML::AttributeNames::fetchpriority)).value_or(Fetch::Infrastructure::Request::Priority::Auto);
|
||||
|
||||
|
@ -490,9 +490,9 @@ void HTMLLinkElement::process_stylesheet_resource(bool success, Fetch::Infrastru
|
|||
m_loaded_style_sheet = parse_css_stylesheet(CSS::Parser::ParsingParams(document(), *response.url()), decoded_string);
|
||||
|
||||
if (m_loaded_style_sheet) {
|
||||
Optional<String> location;
|
||||
Optional<::URL::URL> location;
|
||||
if (!response.url_list().is_empty())
|
||||
location = response.url_list().first().to_string();
|
||||
location = response.url_list().first();
|
||||
|
||||
document_or_shadow_root_style_sheets().create_a_css_style_sheet(
|
||||
"text/css"_string,
|
||||
|
|
|
@ -637,11 +637,11 @@ public:
|
|||
{
|
||||
// 2. ⌛ Process candidate: If candidate does not have a src attribute, or if its src attribute's value is the
|
||||
// empty string, then end the synchronous section, and jump down to the failed with elements step below.
|
||||
String candiate_src;
|
||||
String candidate_src;
|
||||
if (auto maybe_src = m_candidate->get_attribute(HTML::AttributeNames::src); maybe_src.has_value())
|
||||
candiate_src = *maybe_src;
|
||||
candidate_src = *maybe_src;
|
||||
|
||||
if (candiate_src.is_empty()) {
|
||||
if (candidate_src.is_empty()) {
|
||||
TRY(failed_with_elements());
|
||||
return {};
|
||||
}
|
||||
|
@ -649,7 +649,7 @@ public:
|
|||
// 3. ⌛ Let urlString and urlRecord be the resulting URL string and the resulting URL record, respectively, that
|
||||
// would have resulted from parsing the URL specified by candidate's src attribute's value relative to the
|
||||
// candidate's node document when the src attribute was last changed.
|
||||
auto url_record = m_candidate->document().parse_url(candiate_src);
|
||||
auto url_record = m_candidate->document().parse_url(candidate_src);
|
||||
|
||||
// 4. ⌛ If urlString was not obtained successfully, then end the synchronous section, and jump down to the failed
|
||||
// with elements step below.
|
||||
|
@ -1008,7 +1008,7 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::fetch_resource(URL::URL const& url_r
|
|||
// 6. Let byteRange, which is "entire resource" or a (number, number or "until end") tuple, be the byte range required to satisfy missing data in
|
||||
// media data. This value is implementation-defined and may rely on codec, network conditions or other heuristics. The user-agent may determine
|
||||
// to fetch the resource in full, in which case byteRange would be "entire resource", to fetch from a byte offset until the end, in which case
|
||||
// byteRange would be (number, "until end"), or to fetch a range between two byte offsets, im which case byteRange would be a (number, number)
|
||||
// byteRange would be (number, "until end"), or to fetch a range between two byte offsets, in which case byteRange would be a (number, number)
|
||||
// tuple representing the two offsets.
|
||||
ByteRange byte_range = EntireResource {};
|
||||
|
||||
|
|
|
@ -197,11 +197,11 @@ void HTMLMeterElement::create_shadow_tree_if_needed()
|
|||
set_shadow_root(shadow_root);
|
||||
|
||||
auto meter_bar_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
|
||||
meter_bar_element->set_use_pseudo_element(CSS::PseudoElement::Track);
|
||||
meter_bar_element->set_use_pseudo_element(CSS::PseudoElement::SliderTrack);
|
||||
MUST(shadow_root->append_child(*meter_bar_element));
|
||||
|
||||
m_meter_value_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
|
||||
m_meter_value_element->set_use_pseudo_element(CSS::PseudoElement::Fill);
|
||||
m_meter_value_element->set_use_pseudo_element(CSS::PseudoElement::SliderFill);
|
||||
MUST(meter_bar_element->append_child(*m_meter_value_element));
|
||||
update_meter_value_element();
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue