From 1fe801ca643d65c246b2a02a9874672955ecafad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Mon, 21 Sep 2026 09:24:20 +0200 Subject: [PATCH] Resolve fragment and query URLs against the current document path --- .../native/webscene_native_resource_url.h | 10 ++++++++++ .../tests/native_v8_runtime_resource_tests.inc | 12 ++++++++++++ .../tests/native_v8_runtime_tests.cpp | 1 + 3 files changed, 23 insertions(+) diff --git a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_resource_url.h b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_resource_url.h index 662798596..7bee01352 100644 --- a/experiments/WebScene.NativeEngine.Probe/native/webscene_native_resource_url.h +++ b/experiments/WebScene.NativeEngine.Probe/native/webscene_native_resource_url.h @@ -70,6 +70,16 @@ inline std::string resolve_url(std::string value, const std::string& base) const auto origin = authority_end == std::string::npos ? base : base.substr(0U, authority_end); if (value.starts_with('/')) return origin + value; + // A query or fragment changes this document's URL, not its path. + // Rebuilding from the containing directory drops the final segment + // (and breaks same-document iframe history traversal). + if (value.starts_with('#')) { + return base.substr(0U, base.find('#')) + value; + } + if (value.starts_with('?')) { + return base.substr(0U, base.find_first_of("?#")) + value; + } + const auto value_suffix_offset = value.find_first_of("?#"); const auto value_suffix = value_suffix_offset == std::string::npos ? std::string{} diff --git a/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_resource_tests.inc b/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_resource_tests.inc index 892c1699f..897c15ecb 100644 --- a/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_resource_tests.inc +++ b/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_resource_tests.inc @@ -122,6 +122,18 @@ struct contextual_resource_server final { void test_same_origin_iframe_navigation_document_replacement() { + using webscene_native::resources::resolve_url; + constexpr std::string_view base{ + "https://iframe-navigation.test/nested/fake.html?id=history-reload#old"}; + require(resolve_url("#one", std::string{base}) + == "https://iframe-navigation.test/nested/fake.html?id=history-reload#one", + "fragment-only URL discarded document path or query"); + require(resolve_url("?next=1#two", std::string{base}) + == "https://iframe-navigation.test/nested/fake.html?next=1#two", + "query-only URL discarded document path or retained stale query"); + require(resolve_url("../sibling.html", std::string{base}) + == "https://iframe-navigation.test/sibling.html", + "ordinary relative URL resolution regressed"); constexpr std::string_view address{ "https://iframe-navigation.test/index.html"}; constexpr std::string_view history_reload_page = R"HTML( diff --git a/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_tests.cpp b/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_tests.cpp index 4f323b5ad..8c9c319a1 100644 --- a/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_tests.cpp +++ b/experiments/WebScene.NativeEngine.Probe/tests/native_v8_runtime_tests.cpp @@ -2,6 +2,7 @@ #include "webscene_native_dom.h" #include "webscene_embed_fallback.h" #include "webscene_native_websocket.h" +#include "webscene_native_resource_url.h" #include #include