diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.cpp b/Userland/Libraries/LibWeb/HTML/Navigable.cpp
index b49c08caeb..12cb9ee0d3 100644
--- a/Userland/Libraries/LibWeb/HTML/Navigable.cpp
+++ b/Userland/Libraries/LibWeb/HTML/Navigable.cpp
@@ -18,6 +18,7 @@
#include
#include
#include
+#include
#include
namespace Web::HTML {
@@ -225,6 +226,55 @@ JS::GCPtr Navigable::top_level_traversable()
return verify_cast(navigable);
}
+Navigable::ChosenNavigable Navigable::choose_a_navigable(StringView name, TokenizedFeature::NoOpener, ActivateTab)
+{
+ // 1. Let chosen be null.
+ JS::GCPtr chosen = nullptr;
+
+ // 2. Let windowType be "existing or none".
+ auto window_type = WindowType::ExistingOrNone;
+
+ // 3. Let sandboxingFlagSet be current's active document's active sandboxing flag set.
+ [[maybe_unused]] auto sandboxing_flag_set = active_document()->active_sandboxing_flag_set();
+
+ // 4. If name is the empty string or an ASCII case-insensitive match for "_self", then set chosen to currentNavigable.
+ if (name.is_empty() || Infra::is_ascii_case_insensitive_match(name, "_self"sv)) {
+ chosen = this;
+ }
+ // 5. Otherwise, if name is an ASCII case-insensitive match for "_parent",
+ // set chosen to currentNavigable's parent, if any, and currentNavigable otherwise.
+ else if (Infra::is_ascii_case_insensitive_match(name, "_parent"sv)) {
+ if (auto parent = this->parent())
+ chosen = parent;
+ else
+ chosen = this;
+ }
+ // 6. Otherwise, if name is an ASCII case-insensitive match for "_top",
+ // set chosen to currentNavigable's traversable navigable.
+ else if (Infra::is_ascii_case_insensitive_match(name, "_top"sv)) {
+ 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 (!Infra::is_ascii_case_insensitive_match(name, "_blank"sv)) {
+ TODO();
+ }
+ // 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 {
+ TODO();
+ }
+
+ return { chosen.ptr(), window_type };
+}
+
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#getting-session-history-entries
Vector>& Navigable::get_session_history_entries() const
{
diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.h b/Userland/Libraries/LibWeb/HTML/Navigable.h
index 50ac7da32a..a1d111081a 100644
--- a/Userland/Libraries/LibWeb/HTML/Navigable.h
+++ b/Userland/Libraries/LibWeb/HTML/Navigable.h
@@ -9,9 +9,11 @@
#include
#include
+#include
#include
#include
#include
+#include
namespace Web::HTML {
@@ -63,6 +65,19 @@ public:
JS::GCPtr traversable_navigable() const;
JS::GCPtr top_level_traversable();
+ enum class WindowType {
+ ExistingOrNone,
+ NewAndUnrestricted,
+ NewWithNoOpener,
+ };
+
+ struct ChosenNavigable {
+ JS::GCPtr navigable;
+ WindowType window_type;
+ };
+
+ ChosenNavigable choose_a_navigable(StringView name, TokenizedFeature::NoOpener no_opener, ActivateTab = ActivateTab::Yes);
+
static JS::GCPtr navigable_with_active_document(JS::NonnullGCPtr);
enum class Traversal {