diff --git a/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp b/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp index 8dd14cffbd..3d075fe0ea 100644 --- a/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp +++ b/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp @@ -748,6 +748,36 @@ bool BrowsingContext::is_ancestor_of(BrowsingContext const& other) const return false; } +// https://html.spec.whatwg.org/multipage/document-sequences.html#familiar-with +bool BrowsingContext::is_familiar_with(BrowsingContext const& other) const +{ + // A browsing context A is familiar with a second browsing context B if the following algorithm returns true: + auto const& A = *this; + auto const& B = other; + + // 1. If A's active document's origin is same origin with B's active document's origin, then return true. + if (A.active_document()->origin().is_same_origin(B.active_document()->origin())) + return true; + + // 2. If A's top-level browsing context is B, then return true. + if (A.top_level_browsing_context() == &B) + return true; + + // 3. If B is an auxiliary browsing context and A is familiar with B's opener browsing context, then return true. + if (B.opener_browsing_context() != nullptr && A.is_familiar_with(*B.opener_browsing_context())) + return true; + + // 4. If there exists an ancestor browsing context of B whose active document has the same origin as the active document of A, then return true. + // NOTE: This includes the case where A is an ancestor browsing context of B. + for (auto ancestor = B.parent(); ancestor; ancestor = ancestor->parent()) { + if (ancestor->active_document()->origin().is_same_origin(A.active_document()->origin())) + return true; + } + + // 5. Return false. + return false; +} + // https://html.spec.whatwg.org/multipage/browsing-the-web.html#snapshotting-target-snapshot-params SandboxingFlagSet determine_the_creation_sandboxing_flags(BrowsingContext const&, JS::GCPtr) { diff --git a/Userland/Libraries/LibWeb/HTML/BrowsingContext.h b/Userland/Libraries/LibWeb/HTML/BrowsingContext.h index e960072815..b731625399 100644 --- a/Userland/Libraries/LibWeb/HTML/BrowsingContext.h +++ b/Userland/Libraries/LibWeb/HTML/BrowsingContext.h @@ -55,6 +55,7 @@ public: JS::GCPtr next_sibling() const; bool is_ancestor_of(BrowsingContext const&) const; + bool is_familiar_with(BrowsingContext const&) const; template IterationDecision for_each_in_inclusive_subtree(Callback callback) const diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.cpp b/Userland/Libraries/LibWeb/HTML/Navigable.cpp index c97a8599f4..f983d35aa8 100644 --- a/Userland/Libraries/LibWeb/HTML/Navigable.cpp +++ b/Userland/Libraries/LibWeb/HTML/Navigable.cpp @@ -304,6 +304,16 @@ void Navigable::set_ongoing_navigation(Variant ongoing // https://html.spec.whatwg.org/multipage/document-sequences.html#the-rules-for-choosing-a-navigable Navigable::ChosenNavigable Navigable::choose_a_navigable(StringView name, TokenizedFeature::NoOpener no_opener, ActivateTab) { + // NOTE: Implementation for step 7 here. + JS::GCPtr same_name_navigable = nullptr; + if (!Infra::is_ascii_case_insensitive_match(name, "_blank"sv)) { + for (auto& n : all_navigables()) { + if (n->target_name() == name) { + same_name_navigable = n; + } + } + } + // 1. Let chosen be null. JS::GCPtr chosen = nullptr; @@ -333,7 +343,7 @@ Navigable::ChosenNavigable Navigable::choose_a_navigable(StringView name, Tokeni chosen = traversable_navigable(); } - // FIXME: 7. Otherwise, if name is not an ASCII case-insensitive match for "_blank", + // 7. Otherwise, if name is not an ASCII case-insensitive match for "_blank", // there exists a navigable whose target name is the same as name, currentNavigable's // active browsing context is familiar with that navigable's active browsing context, // and the user agent determines that the two browsing contexts are related enough that @@ -341,6 +351,11 @@ Navigable::ChosenNavigable Navigable::choose_a_navigable(StringView name, Tokeni // matching navigables, the user agent should pick one in some arbitrary consistent manner, // such as the most recently opened, most recently focused, or more closely related, and set // chosen to it. + else if (same_name_navigable != nullptr && (active_browsing_context()->is_familiar_with(*same_name_navigable->active_browsing_context()))) { + // FIXME: Handle multiple name-match case + // FIXME: When are these contexts 'not related enough' ? + chosen = same_name_navigable; + } // 8. Otherwise, a new top-level traversable is being requested, and what happens depends on the // user agent's configuration and abilities — it is determined by the rules given for the first