diff --git a/Libraries/LibWeb/HTML/Navigable.cpp b/Libraries/LibWeb/HTML/Navigable.cpp index 404ff4ce491a9..0ff50f4a96d5b 100644 --- a/Libraries/LibWeb/HTML/Navigable.cpp +++ b/Libraries/LibWeb/HTML/Navigable.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -349,14 +350,15 @@ void Navigable::set_ongoing_navigation(Variant ongoing Navigable::ChosenNavigable Navigable::choose_a_navigable(StringView name, TokenizedFeature::NoOpener no_opener, ActivateTab activate_tab, Optional window_features) { // NOTE: Implementation for step 7 here. - GC::Ptr same_name_navigable = nullptr; - if (!Infra::is_ascii_case_insensitive_match(name, "_blank"sv)) { - for (auto& n : all_navigables()) { - if (n->target_name() == name && !n->has_been_destroyed()) { - same_name_navigable = n; - } + GC::Ptr navigable_by_target_name = nullptr; + auto find_and_assign_navigable_by_target_name = [&](StringView name) { + auto maybe_navigable = find_a_navigable_by_target_name(name); + if (maybe_navigable) { + navigable_by_target_name = maybe_navigable; + return true; } - } + return false; + }; // 1. Let chosen be null. GC::Ptr chosen = nullptr; @@ -387,25 +389,17 @@ Navigable::ChosenNavigable Navigable::choose_a_navigable(StringView name, Tokeni chosen = traversable_navigable(); } - // 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 - // it is ok if they reach each other, set chosen to that navigable. If there are multiple - // 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; + // 7. Otherwise, if name is not an ASCII case-insensitive match for "_blank", and there exists a navigable that is + // the result of finding a navigable by target name given name and currentNavigable, set chosen to that navigable. + else if (!Infra::is_ascii_case_insensitive_match(name, "_blank"sv) && find_and_assign_navigable_by_target_name(name)) { + chosen = navigable_by_target_name; } // 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 // applicable option from the following list: else { - // --> If current's active window does not have transient activation and the user agent has been configured to + // --> If currentNavigable's active window does not have transient activation and the user agent has been configured to // not show popups (i.e., the user agent has a "popup blocker" enabled) if (active_window() && !active_window()->has_transient_activation() && traversable_navigable()->page().should_block_pop_ups()) { // FIXME: The user agent may inform the user that a popup has been blocked. @@ -473,7 +467,8 @@ Navigable::ChosenNavigable Navigable::choose_a_navigable(StringView name, Tokeni // 9. Otherwise: else { - // 1. Set chosen to the result of creating a new top-level traversable given currentNavigable's active browsing context and targetName. + // 1. Set chosen to the result of creating a new top-level traversable given currentNavigable's active browsing context, targetName, and currentNavigable. + // FIXME: "and currentNavigable", which is the openerNavigableForWebDriver parameter. chosen = create_new_traversable->function()(active_browsing_context()); // 2. If sandboxingFlagSet's sandboxed navigation browsing context flag is set, @@ -488,21 +483,89 @@ Navigable::ChosenNavigable Navigable::choose_a_navigable(StringView name, Tokeni chosen->active_browsing_context()->set_popup_sandboxing_flag_set(chosen->active_browsing_context()->popup_sandboxing_flag_set() | sandboxing_flag_set); } - // --> If the user agent has been configured such that in this instance t will reuse current + // --> If the user agent has been configured such that in this instance it will choose currentNavigable else if (false) { // FIXME: When is this the case? // Set chosen to current. chosen = *this; } - // --> If the user agent has been configured such that in this instance it will not find a browsing context + // --> If the user agent has been configured such that in this instance it will not find a navigable else if (false) { // FIXME: When is this the case? // Do nothing. } } + // 9. Return chosen and windowType return { chosen.ptr(), window_type }; } +// https://html.spec.whatwg.org/multipage/document-sequences.html#find-a-navigable-by-target-name +GC::Ptr Navigable::find_a_navigable_by_target_name(StringView name) +{ + // 1. Let currentDocument be currentNavigable's active document. + auto& current_document = *active_document(); + + // 2. Let sourceSnapshotParams be the result of snapshotting source snapshot params given currentDocument. + auto source_snapshot_params = current_document.snapshot_source_snapshot_params(); + + // 3. Let subtreesToSearch be an implementation-defined choice of one of the following: + // - « currentNavigable's traversable navigable, currentNavigable » + // - the inclusive ancestor navigables of currentDocument + // FIXME: Decide which to use, or wait until the spec picks one. + auto subtrees_to_search = current_document.inclusive_ancestor_navigables(); + + // 4. For each subtreeToSearch of subtreesToSearch, in reverse order: + for (auto const& subtree_to_search : subtrees_to_search) { + // 1. Let documentToSearch be subtreeToSearch's active document. + auto& document_to_search = *subtree_to_search->active_document(); + + // 2. For each navigable of the inclusive descendant navigables of documentToSearch: + for (auto const& navigable : document_to_search.inclusive_descendant_navigables()) { + // 1. If currentNavigable is not allowed by sandboxing to navigate navigable given sourceSnapshotParams, then optionally continue. + if (!allowed_by_sandboxing_to_navigate(*navigable, source_snapshot_params)) + continue; + + // 2. If navigable's target name is name, then return navigable. + if (navigable->target_name() == name) + return *navigable; + } + } + + // 5. Let currentTopLevelBrowsingContext be currentNavigable's active browsing context's top-level browsing context. + auto& current_top_level_browsing_context = *active_browsing_context()->top_level_browsing_context(); + + // 6. Let group be currentTopLevelBrowsingContext's group. + auto* group = current_top_level_browsing_context.group(); + + // 7. For each topLevelBrowsingContext of group's browsing context set, in an implementation-defined order (the user agent should pick a consistent ordering, such as the most recently opened, most recently focused, or more closely related): + for (auto const& top_level_browsing_context : group->browsing_context_set()) { + // 1. If currentTopLevelBrowsingContext is topLevelBrowsingContext, then continue. + if (¤t_top_level_browsing_context == top_level_browsing_context) + continue; + + // 2. Let documentToSearch be topLevelBrowsingContext's active document. + auto* document_to_search = top_level_browsing_context->active_document(); + + // 3. For each navigable of the inclusive descendant navigables of documentToSearch: + for (auto const& navigable : document_to_search->inclusive_descendant_navigables()) { + // 1. If currentNavigable's active browsing context is not familiar with navigable's active browsing context, then continue. + if (!active_browsing_context()->is_familiar_with(*navigable->active_browsing_context())) + continue; + + // 2. If currentNavigable is not allowed by sandboxing to navigate navigable given sourceSnapshotParams, then optionally continue. + if (!allowed_by_sandboxing_to_navigate(*navigable, source_snapshot_params)) + continue; + + // 3. If navigable's target name is name, then return navigable. + if (navigable->target_name() == name) + return *navigable; + } + } + + // 8. Return null. + return nullptr; +} + // https://html.spec.whatwg.org/multipage/browsing-the-web.html#getting-session-history-entries Vector>& Navigable::get_session_history_entries() const { diff --git a/Libraries/LibWeb/HTML/Navigable.h b/Libraries/LibWeb/HTML/Navigable.h index a5607f72b25de..90159be6c8bc8 100644 --- a/Libraries/LibWeb/HTML/Navigable.h +++ b/Libraries/LibWeb/HTML/Navigable.h @@ -120,6 +120,8 @@ class Navigable : public JS::Cell { ChosenNavigable choose_a_navigable(StringView name, TokenizedFeature::NoOpener no_opener, ActivateTab = ActivateTab::Yes, Optional window_features = {}); + GC::Ptr find_a_navigable_by_target_name(StringView name); + static GC::Ptr navigable_with_active_document(GC::Ref); enum class Traversal {