diff --git a/Libraries/LibWeb/Internals/Internals.cpp b/Libraries/LibWeb/Internals/Internals.cpp index 48fd657e2f0..e6594e40552 100644 --- a/Libraries/LibWeb/Internals/Internals.cpp +++ b/Libraries/LibWeb/Internals/Internals.cpp @@ -1,9 +1,11 @@ /* * Copyright (c) 2023, Andreas Kling + * Copyright (c) 2025, Jelle Raaijmakers * * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include @@ -12,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -19,7 +22,6 @@ #include #include #include -#include namespace Web::Internals { @@ -50,6 +52,39 @@ void Internals::set_test_timeout(double milliseconds) page().client().page_did_set_test_timeout(milliseconds); } +// https://web-platform-tests.org/writing-tests/reftests.html#components-of-a-reftest +WebIDL::ExceptionOr Internals::load_reference_test_metadata() +{ + auto& vm = this->vm(); + auto& page = this->page(); + + auto* document = page.top_level_browsing_context().active_document(); + if (!document) + return vm.throw_completion("No active document available"sv); + + JsonObject metadata; + + // Collect all and references. + auto collect_references = [&vm, &document](StringView type) -> WebIDL::ExceptionOr { + JsonArray references; + auto reference_nodes = TRY(document->query_selector_all(MUST(String::formatted("link[rel={}]", type)))); + for (size_t i = 0; i < reference_nodes->length(); ++i) { + auto const* reference_node = reference_nodes->item(i); + auto href = as(reference_node)->get_attribute_value(HTML::AttributeNames::href); + auto url = document->encoding_parse_url(href); + if (!url.has_value()) + return vm.throw_completion(MUST(String::formatted("Failed to construct URL for '{}'", href))); + references.must_append(url->to_string()); + } + return references; + }; + metadata.set("match_references"sv, TRY(collect_references("match"sv))); + metadata.set("mismatch_references"sv, TRY(collect_references("mismatch"sv))); + + page.client().page_did_receive_reference_test_metadata(metadata); + return {}; +} + void Internals::gc() { vm().heap().collect_garbage(); diff --git a/Libraries/LibWeb/Internals/Internals.h b/Libraries/LibWeb/Internals/Internals.h index 4842ef5b520..48d9f178f76 100644 --- a/Libraries/LibWeb/Internals/Internals.h +++ b/Libraries/LibWeb/Internals/Internals.h @@ -22,6 +22,7 @@ public: void signal_test_is_done(String const& text); void set_test_timeout(double milliseconds); + WebIDL::ExceptionOr load_reference_test_metadata(); WebIDL::ExceptionOr set_time_zone(StringView time_zone); diff --git a/Libraries/LibWeb/Internals/Internals.idl b/Libraries/LibWeb/Internals/Internals.idl index 193c192466c..5348f4aebcf 100644 --- a/Libraries/LibWeb/Internals/Internals.idl +++ b/Libraries/LibWeb/Internals/Internals.idl @@ -7,6 +7,7 @@ interface Internals { undefined signalTestIsDone(DOMString text); undefined setTestTimeout(double milliseconds); + undefined loadReferenceTestMetadata(); DOMString setTimeZone(DOMString timeZone); diff --git a/Libraries/LibWeb/Page/Page.h b/Libraries/LibWeb/Page/Page.h index 0829315c4ca..330403fa8ab 100644 --- a/Libraries/LibWeb/Page/Page.h +++ b/Libraries/LibWeb/Page/Page.h @@ -388,6 +388,7 @@ public: virtual void page_did_finish_test([[maybe_unused]] String const& text) { } virtual void page_did_set_test_timeout([[maybe_unused]] double milliseconds) { } + virtual void page_did_receive_reference_test_metadata(JsonValue) { } virtual void page_did_set_browser_zoom([[maybe_unused]] double factor) { } diff --git a/Libraries/LibWebView/ViewImplementation.h b/Libraries/LibWebView/ViewImplementation.h index ace300d2d43..a887fc11f18 100644 --- a/Libraries/LibWebView/ViewImplementation.h +++ b/Libraries/LibWebView/ViewImplementation.h @@ -224,6 +224,7 @@ public: Function on_finish_handling_drag_event; Function on_test_finish; Function on_set_test_timeout; + Function on_reference_test_metadata; Function on_set_browser_zoom; Function const& total_match_count)> on_find_in_page; Function on_theme_color_change; diff --git a/Libraries/LibWebView/WebContentClient.cpp b/Libraries/LibWebView/WebContentClient.cpp index 6616d99a860..cc23c937830 100644 --- a/Libraries/LibWebView/WebContentClient.cpp +++ b/Libraries/LibWebView/WebContentClient.cpp @@ -132,6 +132,14 @@ void WebContentClient::did_set_test_timeout(u64 page_id, double milliseconds) } } +void WebContentClient::did_receive_reference_test_metadata(u64 page_id, JsonValue metadata) +{ + if (auto view = view_for_page_id(page_id); view.has_value()) { + if (view->on_reference_test_metadata) + view->on_reference_test_metadata(metadata); + } +} + void WebContentClient::did_set_browser_zoom(u64 page_id, double factor) { if (auto view = view_for_page_id(page_id); view.has_value()) { diff --git a/Libraries/LibWebView/WebContentClient.h b/Libraries/LibWebView/WebContentClient.h index 501f6b253d8..a4c981f14c5 100644 --- a/Libraries/LibWebView/WebContentClient.h +++ b/Libraries/LibWebView/WebContentClient.h @@ -129,6 +129,7 @@ private: virtual void did_finish_handling_input_event(u64 page_id, Web::EventResult event_result) override; virtual void did_finish_test(u64 page_id, String text) override; virtual void did_set_test_timeout(u64 page_id, double milliseconds) override; + virtual void did_receive_reference_test_metadata(u64 page_id, JsonValue) override; virtual void did_set_browser_zoom(u64 page_id, double factor) override; virtual void did_find_in_page(u64 page_id, size_t current_match_index, Optional total_match_count) override; virtual void did_change_theme_color(u64 page_id, Gfx::Color color) override; diff --git a/Services/WebContent/ConnectionFromClient.cpp b/Services/WebContent/ConnectionFromClient.cpp index 66a2135e656..fa1e3224794 100644 --- a/Services/WebContent/ConnectionFromClient.cpp +++ b/Services/WebContent/ConnectionFromClient.cpp @@ -398,43 +398,6 @@ void ConnectionFromClient::debug_request(u64 page_id, ByteString request, ByteSt return; } - if (request == "load-reference-page") { - if (auto* document = page->page().top_level_browsing_context().active_document()) { - auto has_mismatch_selector = false; - - auto maybe_link = [&]() -> Web::WebIDL::ExceptionOr> { - auto maybe_link = document->query_selector("link[rel=match]"sv); - if (maybe_link.is_error() || maybe_link.value()) - return maybe_link; - - auto maybe_mismatch_link = document->query_selector("link[rel=mismatch]"sv); - if (maybe_mismatch_link.is_error() || maybe_mismatch_link.value()) { - has_mismatch_selector = maybe_mismatch_link.value(); - return maybe_mismatch_link; - } - - return nullptr; - }(); - - if (maybe_link.is_error() || !maybe_link.value()) { - // To make sure that we fail the ref-test if the link is missing, load the error page-> - load_html(page_id, "

Failed to find <link rel="match" /> or <link rel="mismatch" /> in ref test page!

Make sure you added it."); - } else { - auto link = maybe_link.release_value(); - auto url = document->encoding_parse_url(link->get_attribute_value(Web::HTML::AttributeNames::href)); - if (url->query().has_value() && !url->query()->is_empty()) { - load_html(page_id, "

Invalid ref test link - query string must be empty

"); - return; - } - if (has_mismatch_selector) - url->set_query("mismatch"_string); - - load_url(page_id, *url); - } - } - return; - } - if (request == "navigator-compatibility-mode") { Web::NavigatorCompatibilityMode compatibility_mode; if (argument == "chrome") { diff --git a/Services/WebContent/PageClient.cpp b/Services/WebContent/PageClient.cpp index 6b20ddf036c..1d7c6deb7ee 100644 --- a/Services/WebContent/PageClient.cpp +++ b/Services/WebContent/PageClient.cpp @@ -333,6 +333,11 @@ void PageClient::page_did_set_test_timeout(double milliseconds) client().async_did_set_test_timeout(m_id, milliseconds); } +void PageClient::page_did_receive_reference_test_metadata(JsonValue metadata) +{ + client().async_did_receive_reference_test_metadata(m_id, metadata); +} + void PageClient::page_did_set_browser_zoom(double factor) { auto traversable = page().top_level_traversable(); diff --git a/Services/WebContent/PageClient.h b/Services/WebContent/PageClient.h index 53ed0dc2eb5..6f40269cbcb 100644 --- a/Services/WebContent/PageClient.h +++ b/Services/WebContent/PageClient.h @@ -164,6 +164,7 @@ private: virtual void page_did_request_select_dropdown(Web::CSSPixelPoint content_position, Web::CSSPixels minimum_width, Vector items) override; virtual void page_did_finish_test(String const& text) override; virtual void page_did_set_test_timeout(double milliseconds) override; + virtual void page_did_receive_reference_test_metadata(JsonValue) override; virtual void page_did_set_browser_zoom(double factor) override; virtual void page_did_change_theme_color(Gfx::Color color) override; virtual void page_did_insert_clipboard_entry(Web::Clipboard::SystemClipboardRepresentation const&, StringView presentation_style) override; diff --git a/Services/WebContent/WebContentClient.ipc b/Services/WebContent/WebContentClient.ipc index d5392186236..a0135f7fa4e 100644 --- a/Services/WebContent/WebContentClient.ipc +++ b/Services/WebContent/WebContentClient.ipc @@ -114,6 +114,7 @@ endpoint WebContentClient did_finish_test(u64 page_id, String text) =| did_set_test_timeout(u64 page_id, double milliseconds) =| + did_receive_reference_test_metadata(u64 page_id, JsonValue result) =| did_set_browser_zoom(u64 page_id, double factor) =| diff --git a/Tests/LibWeb/test-web/main.cpp b/Tests/LibWeb/test-web/main.cpp index bb501bc7969..35664bbe92e 100644 --- a/Tests/LibWeb/test-web/main.cpp +++ b/Tests/LibWeb/test-web/main.cpp @@ -3,6 +3,7 @@ * Copyright (c) 2023-2025, Tim Flynn * Copyright (c) 2023, Andreas Kling * Copyright (c) 2023-2024, Sam Atkins + * Copyright (c) 2025, Jelle Raaijmakers * * SPDX-License-Identifier: BSD-2-Clause */ @@ -28,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -425,25 +427,48 @@ static void run_ref_test(TestWebView& view, Test& test, URL::URL const& url, int view.on_test_finish = [&view, &test, on_test_complete = move(on_test_complete)](auto const&) { if (test.actual_screenshot) { - if (view.url().query().has_value() && view.url().query()->equals_ignoring_ascii_case("mismatch"sv)) { - test.ref_test_expectation_type = RefTestExpectationType::Mismatch; - } else { - test.ref_test_expectation_type = RefTestExpectationType::Match; - } + // The reference has finished loading; take another screenshot and move on to handling the result. view.take_screenshot()->when_resolved([&view, &test, on_test_complete = move(on_test_complete)](RefPtr screenshot) { test.expectation_screenshot = move(screenshot); view.reset_zoom(); on_test_complete(); }); } else { + // When the test initially finishes, we take a screenshot and request the reference test metadata. view.take_screenshot()->when_resolved([&view, &test](RefPtr screenshot) { test.actual_screenshot = move(screenshot); view.reset_zoom(); - view.debug_request("load-reference-page"); + view.run_javascript("internals.loadReferenceTestMetadata();"_string); }); } }; + view.on_reference_test_metadata = [&view, &test](JsonValue const& metadata) { + auto metadata_object = metadata.as_object(); + + auto match_references = metadata_object.get_array("match_references"sv); + auto mismatch_references = metadata_object.get_array("mismatch_references"sv); + VERIFY(!match_references->is_empty() || !mismatch_references->is_empty()); + + // Read (mis)match reference tests to load. + // FIXME: Currently we only support single match or mismatch reference. + String reference_to_load; + if (!match_references->is_empty()) { + if (match_references->size() > 1) + dbgln("FIXME: Only a single ref test match reference is supported"); + + test.ref_test_expectation_type = RefTestExpectationType::Match; + reference_to_load = match_references->at(0).as_string(); + } else { + if (mismatch_references->size() > 1) + dbgln("FIXME: Only a single ref test mismatch reference is supported"); + + test.ref_test_expectation_type = RefTestExpectationType::Mismatch; + reference_to_load = mismatch_references->at(0).as_string(); + } + view.load(URL::Parser::basic_parse(reference_to_load).release_value()); + }; + view.on_set_test_timeout = [timer, timeout_in_milliseconds](double milliseconds) { if (milliseconds <= timeout_in_milliseconds) return;