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 66279859..7bee0135 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 892c1699..897c15ec 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 4f323b5a..8c9c319a 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