Browse Source

LibWeb: Bring BrowsingContext::choose_a_browsing_context closer to spec

Idan Horowitz 2 năm trước cách đây
mục cha
commit
ff2f31b

+ 98 - 88
Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp

@@ -651,118 +651,128 @@ JS::GCPtr<DOM::Node> BrowsingContext::currently_focused_area()
     return candidate;
 }
 
-BrowsingContext* BrowsingContext::choose_a_browsing_context(StringView name, bool)
+// https://html.spec.whatwg.org/#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name
+BrowsingContext::ChosenBrowsingContext BrowsingContext::choose_a_browsing_context(StringView name, bool no_opener)
 {
-    // The rules for choosing a browsing context, given a browsing context name
-    // name, a browsing context current, and a boolean noopener are as follows:
+    // The rules for choosing a browsing context, given a browsing context name name, a browsing context current, and
+    // a boolean noopener are as follows:
 
     // 1. Let chosen be null.
     JS::GCPtr<BrowsingContext> chosen = nullptr;
 
-    // FIXME: 2. Let windowType be "existing or none".
+    // 2. Let windowType be "existing or none".
+    auto window_type = WindowType::ExistingOrNone;
 
-    // FIXME: 3. Let sandboxingFlagSet be current's active document's active
-    // sandboxing flag set.
+    // 3. Let sandboxingFlagSet be current's active document's active sandboxing flag set.
+    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 current.
-    if (name.is_empty() || name.equals_ignoring_case("_self"sv))
+    if (name.is_empty() || name.equals_ignoring_case("_self"sv)) {
         chosen = this;
+    }
 
-    // 5. Otherwise, if name is an ASCII case-insensitive match for "_parent",
-    // set chosen to current's parent browsing context, if any, and current
-    // otherwise.
-    if (name.equals_ignoring_case("_parent"sv)) {
+    // 5. Otherwise, if name is an ASCII case-insensitive match for "_parent", set chosen to current's parent browsing
+    //    context, if any, and current otherwise.
+    else if (name.equals_ignoring_case("_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 current's top-level browsing context, if any, and current
-    // otherwise.
-    if (name.equals_ignoring_case("_top"sv)) {
+    // 6. Otherwise, if name is an ASCII case-insensitive match for "_top", set chosen to current's top-level browsing
+    //    context, if any, and current otherwise.
+    else if (name.equals_ignoring_case("_top"sv)) {
         chosen = &top_level_browsing_context();
     }
 
-    // FIXME: 7. Otherwise, if name is not an ASCII case-insensitive match for
-    // "_blank", there exists a browsing context whose name is the same as name,
-    // current is familiar with that 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 browsing context. If
-    // there are multiple matching browsing contexts, the user agent should set
-    // chosen to one in some arbitrary consistent manner, such as the most
-    // recently opened, most recently focused, or more closely related.
-    if (!name.equals_ignoring_case("_blank"sv)) {
+    // FIXME: 7. Otherwise, if name is not an ASCII case-insensitive match for "_blank", there exists a browsing context
+    //           whose name is the same as name, current is familiar with that 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 browsing context. If there are multiple matching browsing contexts, the user agent
+    //           should set chosen to one in some arbitrary consistent manner, such as the most recently opened, most
+    //           recently focused, or more closely related.
+    else if (!name.equals_ignoring_case("_blank"sv)) {
+        dbgln("FIXME: Find matching browser context for name {}", name);
         chosen = this;
     } else {
-        // 8. Otherwise, a new browsing context 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:
-        dbgln("FIXME: Create a new browsing context!");
-
-        // --> If current'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)
-        //
-        //     The user agent may inform the user that a popup has been blocked.
-
-        // --> If sandboxingFlagSet has the sandboxed auxiliary navigation
-        //     browsing context flag set
-        //
-        //     The user agent may report to a developer console that a popup has
-        //     been blocked.
-
-        // --> If the user agent has been configured such that in this instance
-        //     it will create a new browsing context
-        //
-        //     1. Set windowType to "new and unrestricted".
-
-        //     2. If current's top-level browsing context's active document's
-        //     cross-origin opener policy's value is "same-origin" or
-        //     "same-origin-plus-COEP", then:
-
-        //         2.1. Let currentDocument be current's active document.
-
-        //         2.2. If currentDocument's origin is not same origin with
-        //         currentDocument's relevant settings object's top-level
-        //         origin, then set noopener to true, name to "_blank", and
-        //         windowType to "new with no opener".
-
-        //     3. If noopener is true, then set chosen to the result of creating
-        //     a new top-level browsing context.
-
-        //     4. Otherwise:
-
-        //         4.1. Set chosen to the result of creating a new auxiliary
-        //         browsing context with current.
-
-        //         4.2. If sandboxingFlagSet's sandboxed navigation browsing
-        //         context flag is set, then current must be set as chosen's one
-        //         permitted sandboxed navigator.
-
-        //     5. If sandboxingFlagSet's sandbox propagates to auxiliary
-        //     browsing contexts flag is set, then all the flags that are set in
-        //     sandboxingFlagSet must be set in chosen's popup sandboxing flag
-        //     set.
-
-        //     6. If name is not an ASCII case-insensitive match for "_blank",
-        //     then set chosen's name to name.
-
-        // --> If the user agent has been configured such that in this instance
-        //     it will reuse current
-        //
-        //     Set chosen to current.
-
-        // --> If the user agent has been configured such that in this instance
-        //     it will not find a browsing context
-        //
-        //     Do nothing.
+        // 8. Otherwise, a new browsing context 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:
+
+        // --> If current'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)
+        VERIFY(m_page);
+        if (!active_window()->has_transient_activation() && m_page->should_block_pop_ups()) {
+            // FIXME: The user agent may inform the user that a popup has been blocked.
+            dbgln("Pop-up blocked!");
+        }
+
+        // --> If sandboxingFlagSet has the sandboxed auxiliary navigation browsing context flag set
+        else if (sandboxing_flag_set.flags & SandboxingFlagSet::SandboxedAuxiliaryNavigation) {
+            // FIXME: The user agent may report to a developer console that a popup has been blocked.
+            dbgln("Pop-up blocked!");
+        }
+
+        // --> If the user agent has been configured such that in this instance it will create a new browsing context
+        else if (true) { // FIXME: When is this the case?
+            // 1. Set windowType to "new and unrestricted".
+            window_type = WindowType::NewAndUnrestricted;
+
+            // 2. If current's top-level browsing context's active document's cross-origin opener policy's value is
+            //    "same-origin" or "same-origin-plus-COEP", then:
+            if (top_level_browsing_context().active_document()->cross_origin_opener_policy().value == CrossOriginOpenerPolicyValue::SameOrigin || top_level_browsing_context().active_document()->cross_origin_opener_policy().value == CrossOriginOpenerPolicyValue::SameOriginPlusCOEP) {
+                // 1. Let currentDocument be current's active document.
+                auto* current_document = top_level_browsing_context().active_document();
+
+                // 2. If currentDocument's origin is not same origin with currentDocument's relevant settings object's
+                //    top-level origin, then set noopener to true, name to "_blank", and windowType to "new with no opener".
+                if (!current_document->origin().is_same_origin(current_document->relevant_settings_object().top_level_origin)) {
+                    no_opener = true;
+                    name = "_blank"sv;
+                    window_type = WindowType::NewWithNoOpener;
+                }
+            }
+
+            // 3. If noopener is true, then set chosen to the result of creating a new top-level browsing context.
+            if (no_opener) {
+                chosen = HTML::BrowsingContext::create_a_new_top_level_browsing_context(*m_page);
+            }
+
+            // 4. Otherwise:
+            else {
+                // 1. Set chosen to the result of creating a new auxiliary browsing context with current.
+                // FIXME: We have no concept of auxiliary browsing context
+                chosen = HTML::BrowsingContext::create_a_new_top_level_browsing_context(*m_page);
+
+                // 2. If sandboxingFlagSet's sandboxed navigation browsing context flag is set, then current must be
+                //    set as chosen's one permitted sandboxed navigator.
+                // FIXME: We have no concept of one permitted sandboxed navigator
+            }
+
+            // 5. If sandboxingFlagSet's sandbox propagates to auxiliary browsing contexts flag is set, then all the
+            //    flags that are set in sandboxingFlagSet must be set in chosen's popup sandboxing flag set.
+            // FIXME: Our BrowsingContexts do not have SandboxingFlagSets yet, only documents do
+
+            // 6. If name is not an ASCII case-insensitive match for "_blank", then set chosen's name to name.
+            if (!name.equals_ignoring_case("_blank"sv))
+                chosen->set_name(name);
+        }
+
+        // --> If the user agent has been configured such that in this instance t will reuse current
+        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
+        else if (false) { // FIXME: When is this the case?
+            // Do nothing.
+        }
     }
 
     // 9. Return chosen and windowType.
-    return chosen;
+    return { chosen, window_type };
 }
 
 // https://html.spec.whatwg.org/multipage/browsers.html#document-tree-child-browsing-context

+ 12 - 1
Userland/Libraries/LibWeb/HTML/BrowsingContext.h

@@ -163,7 +163,18 @@ public:
 
     BrowsingContext const& top_level_browsing_context() const { return const_cast<BrowsingContext*>(this)->top_level_browsing_context(); }
 
-    BrowsingContext* choose_a_browsing_context(StringView name, bool noopener);
+    enum class WindowType {
+        ExistingOrNone,
+        NewAndUnrestricted,
+        NewWithNoOpener,
+    };
+
+    struct ChosenBrowsingContext {
+        JS::GCPtr<BrowsingContext> browsing_context;
+        WindowType window_type;
+    };
+
+    ChosenBrowsingContext choose_a_browsing_context(StringView name, bool no_opener);
 
     size_t document_tree_child_browsing_context_count() const;
 

+ 2 - 2
Userland/Libraries/LibWeb/HTML/HTMLHyperlinkElementUtils.cpp

@@ -493,7 +493,7 @@ void HTMLHyperlinkElementUtils::follow_the_hyperlink(Optional<String> hyperlink_
     // 7. Let target be the first return value of applying the rules for
     // choosing a browsing context given targetAttributeValue, source, and
     // noopener.
-    auto* target = source->choose_a_browsing_context(target_attribute_value, noopener);
+    auto target = source->choose_a_browsing_context(target_attribute_value, noopener).browsing_context;
 
     // 8. If target is null, then return.
     if (!target)
@@ -534,7 +534,7 @@ void HTMLHyperlinkElementUtils::follow_the_hyperlink(Optional<String> hyperlink_
     // set to source.
     // FIXME: "navigate" means implementing the navigation algorithm here:
     //        https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigate
-    hyperlink_element_utils_queue_an_element_task(Task::Source::DOMManipulation, [url_string, target] {
+    hyperlink_element_utils_queue_an_element_task(Task::Source::DOMManipulation, [url_string, &target] {
         target->loader().load(url_string, FrameLoader::Type::Navigation);
     });
 }