diff --git a/Libraries/LibWeb/DOM/DocumentLoading.cpp b/Libraries/LibWeb/DOM/DocumentLoading.cpp index 93addf354815a..246e0284715df 100644 --- a/Libraries/LibWeb/DOM/DocumentLoading.cpp +++ b/Libraries/LibWeb/DOM/DocumentLoading.cpp @@ -460,15 +460,16 @@ GC::Ptr load_document(HTML::NavigationParams const& navigation_pa if (type.essence() == "application/pdf"_string || type.essence() == "text/pdf"_string) { // FIXME: If the user agent's PDF viewer supported is true, return the result of creating a document for inline - // content that doesn't have a DOM given navigationParams's navigable. + // content that doesn't have a DOM given navigationParams's navigable, navigationParams's id, + // navigationParams's navigation timing type, and navigationParams's user involvement. } // Otherwise, proceed onward. - // 3. If, given type, the new resource is to be handled by displaying some sort of inline content, e.g., a + // FIXME: 3. If, given type, the new resource is to be handled by displaying some sort of inline content, e.g., a // native rendering of the content or an error message because the specified type is not supported, then // return the result of creating a document for inline content that doesn't have a DOM given navigationParams's - // navigable, navigationParams's id, and navigationParams's navigation timing type. + // navigable, navigationParams's id, navigationParams's navigation timing type, and navigationParams's user involvement. // FIXME: 4. Otherwise, the document's type is such that the resource will not affect navigationParams's navigable, // e.g., because the resource is to be handed to an external application or because it is an unknown type diff --git a/Libraries/LibWeb/DOM/DocumentLoading.h b/Libraries/LibWeb/DOM/DocumentLoading.h index c09d5714c5e8e..987da8403be4e 100644 --- a/Libraries/LibWeb/DOM/DocumentLoading.h +++ b/Libraries/LibWeb/DOM/DocumentLoading.h @@ -18,7 +18,7 @@ bool can_load_document_with_type(MimeSniff::MimeType const&); // https://html.spec.whatwg.org/multipage/document-lifecycle.html#read-ua-inline template -GC::Ref create_document_for_inline_content(GC::Ptr navigable, Optional navigation_id, MutateDocument mutate_document) +GC::Ref create_document_for_inline_content(GC::Ptr navigable, Optional navigation_id, HTML::UserNavigationInvolvement user_involvement, MutateDocument mutate_document) { auto& vm = navigable->vm(); @@ -53,6 +53,7 @@ GC::Ref create_document_for_inline_content(GC::Ptrurl_list().append(URL::URL("about:error")); // AD-HOC: https://github.com/whatwg/html/issues/9122 auto navigation_params = vm.heap().allocate(); @@ -69,6 +70,7 @@ GC::Ref create_document_for_inline_content(GC::Ptrfinal_sandboxing_flag_set = HTML::SandboxingFlagSet {}; navigation_params->opener_policy = move(coop); navigation_params->about_base_url = {}; + navigation_params->user_involvement = user_involvement; // 5. Let document be the result of creating and initializing a Document object given "html", "text/html", and navigationParams. auto document = DOM::Document::create_and_initialize(DOM::Document::Type::HTML, "text/html"_string, navigation_params).release_value_but_fixme_should_propagate_errors(); diff --git a/Libraries/LibWeb/HTML/Navigable.cpp b/Libraries/LibWeb/HTML/Navigable.cpp index 404ff4ce491a9..ed9fa305721f3 100644 --- a/Libraries/LibWeb/HTML/Navigable.cpp +++ b/Libraries/LibWeb/HTML/Navigable.cpp @@ -618,8 +618,7 @@ static GC::Ptr attempt_to_create_a_non_fetch_scheme_document(NonF } // https://html.spec.whatwg.org/multipage/browsing-the-web.html#create-navigation-params-from-a-srcdoc-resource - -static GC::Ref create_navigation_params_from_a_srcdoc_resource(GC::Ptr entry, GC::Ptr navigable, TargetSnapshotParams const& target_snapshot_params, Optional navigation_id) +static GC::Ref create_navigation_params_from_a_srcdoc_resource(GC::Ptr entry, GC::Ptr navigable, TargetSnapshotParams const& target_snapshot_params, UserNavigationInvolvement user_involvement, Optional navigation_id) { auto& vm = navigable->vm(); VERIFY(navigable->active_window()); @@ -685,6 +684,7 @@ static GC::Ref create_navigation_params_from_a_srcdoc_resource // opener policy: coop // FIXME: navigation timing type: navTimingType // about base URL: entry's document state's about base URL + // user involvement: userInvolvement auto navigation_params = vm.heap().allocate(); navigation_params->id = move(navigation_id); navigation_params->navigable = navigable; @@ -695,12 +695,13 @@ static GC::Ref create_navigation_params_from_a_srcdoc_resource navigation_params->final_sandboxing_flag_set = target_snapshot_params.sandboxing_flags; navigation_params->opener_policy = move(coop); navigation_params->about_base_url = entry->document_state()->about_base_url(); + navigation_params->user_involvement = user_involvement; return navigation_params; } // https://html.spec.whatwg.org/multipage/browsing-the-web.html#create-navigation-params-by-fetching -static WebIDL::ExceptionOr create_navigation_params_by_fetching(GC::Ptr entry, GC::Ptr navigable, SourceSnapshotParams const& source_snapshot_params, TargetSnapshotParams const& target_snapshot_params, CSPNavigationType csp_navigation_type, Optional navigation_id) +static WebIDL::ExceptionOr create_navigation_params_by_fetching(GC::Ptr entry, GC::Ptr navigable, SourceSnapshotParams const& source_snapshot_params, TargetSnapshotParams const& target_snapshot_params, CSPNavigationType csp_navigation_type, UserNavigationInvolvement user_involvement, Optional navigation_id) { auto& vm = navigable->vm(); VERIFY(navigable->active_window()); @@ -997,6 +998,7 @@ static WebIDL::ExceptionOr create_navigation // - source snapshot has transient activation: sourceSnapshotParams's has transient activation // - initiator origin: responseOrigin // FIXME: - navigation timing type: navTimingType + // - user involvement: userInvolvement auto navigation_params = vm.heap().allocate(); navigation_params->id = navigation_id; navigation_params->navigable = navigable; @@ -1004,6 +1006,7 @@ static WebIDL::ExceptionOr create_navigation navigation_params->target_snapshot_sandboxing_flags = target_snapshot_params.sandboxing_flags; navigation_params->source_snapshot_has_transient_activation = source_snapshot_params.has_transient_activation; navigation_params->initiator_origin = move(*response_origin); + navigation_params->user_involvement = user_involvement; return navigation_params; } @@ -1051,6 +1054,7 @@ static WebIDL::ExceptionOr create_navigation // COOP enforcement result: coopEnforcementResult // FIXME: navigation timing type: navTimingType // about base URL: entry's document state's about base URL + // user involvement: userInvolvement auto navigation_params = vm.heap().allocate(); navigation_params->id = navigation_id; navigation_params->navigable = navigable; @@ -1065,6 +1069,7 @@ static WebIDL::ExceptionOr create_navigation navigation_params->final_sandboxing_flag_set = final_sandbox_flags; navigation_params->opener_policy = response_coop; navigation_params->about_base_url = entry->document_state()->about_base_url(); + navigation_params->user_involvement = user_involvement; return navigation_params; } @@ -1073,6 +1078,7 @@ WebIDL::ExceptionOr Navigable::populate_session_history_entry_document( GC::Ptr entry, SourceSnapshotParams const& source_snapshot_params, TargetSnapshotParams const& target_snapshot_params, + UserNavigationInvolvement user_involvement, Optional navigation_id, Navigable::NavigationParamsVariant navigation_params, CSPNavigationType csp_navigation_type, @@ -1099,16 +1105,16 @@ WebIDL::ExceptionOr Navigable::populate_session_history_entry_document( if (navigation_params.has()) { // 1. If documentResource is a string, then set navigationParams to the result // of creating navigation params from a srcdoc resource given entry, navigable, - // targetSnapshotParams, navigationId, and navTimingType. + // targetSnapshotParams, userInvolvement, navigationId, and navTimingType. if (document_resource.has()) { - navigation_params = create_navigation_params_from_a_srcdoc_resource(entry, this, target_snapshot_params, navigation_id); + navigation_params = create_navigation_params_from_a_srcdoc_resource(entry, this, target_snapshot_params, user_involvement, navigation_id); } // 2. Otherwise, if all of the following are true: // - entry's URL's scheme is a fetch scheme; and // - documentResource is null, or allowPOST is true and documentResource's request body is not failure (FIXME: check if request body is not failure) - // then set navigationParams to the result of creating navigation params by fetching given entry, navigable, sourceSnapshotParams, targetSnapshotParams, cspNavigationType, navigationId, and navTimingType. + // then set navigationParams to the result of creating navigation params by fetching given entry, navigable, sourceSnapshotParams, targetSnapshotParams, cspNavigationType, userInvolvement, navigationId, and navTimingType. else if (Fetch::Infrastructure::is_fetch_scheme(entry->url().scheme()) && (document_resource.has() || allow_POST)) { - navigation_params = TRY(create_navigation_params_by_fetching(entry, this, source_snapshot_params, target_snapshot_params, csp_navigation_type, navigation_id)); + navigation_params = TRY(create_navigation_params_by_fetching(entry, this, source_snapshot_params, target_snapshot_params, csp_navigation_type, user_involvement, navigation_id)); } // 3. Otherwise, if entry's URL's scheme is not a fetch scheme, then set navigationParams to a new non-fetch scheme navigation params, with: else if (!Fetch::Infrastructure::is_fetch_scheme(entry->url().scheme())) { @@ -1119,6 +1125,7 @@ WebIDL::ExceptionOr Navigable::populate_session_history_entry_document( // - source snapshot has transient activation: sourceSnapshotParams's has transient activation // - initiator origin: entry's document state's initiator origin // FIXME: - navigation timing type: navTimingType + // - user involvement: userInvolvement auto non_fetching_scheme_navigation_params = vm().heap().allocate(); non_fetching_scheme_navigation_params->id = navigation_id; non_fetching_scheme_navigation_params->navigable = this; @@ -1126,6 +1133,7 @@ WebIDL::ExceptionOr Navigable::populate_session_history_entry_document( non_fetching_scheme_navigation_params->target_snapshot_sandboxing_flags = target_snapshot_params.sandboxing_flags; non_fetching_scheme_navigation_params->source_snapshot_has_transient_activation = source_snapshot_params.has_transient_activation; non_fetching_scheme_navigation_params->initiator_origin = *entry->document_state()->initiator_origin(); + non_fetching_scheme_navigation_params->user_involvement = user_involvement; navigation_params = non_fetching_scheme_navigation_params; } } @@ -1135,7 +1143,7 @@ WebIDL::ExceptionOr Navigable::populate_session_history_entry_document( return {}; // 6. Queue a global task on the navigation and traversal task source, given navigable's active window, to run these steps: - queue_global_task(Task::Source::NavigationAndTraversal, *active_window(), GC::create_function(heap(), [this, entry, navigation_params = move(navigation_params), navigation_id, completion_steps]() mutable { + queue_global_task(Task::Source::NavigationAndTraversal, *active_window(), GC::create_function(heap(), [this, entry, navigation_params = move(navigation_params), navigation_id, user_involvement, completion_steps]() mutable { // NOTE: This check is not in the spec but we should not continue navigation if navigable has been destroyed. if (has_been_destroyed()) return; @@ -1167,6 +1175,7 @@ WebIDL::ExceptionOr Navigable::populate_session_history_entry_document( // - FIXME: the result of should navigation response to navigation request of type in target be blocked by Content Security Policy? given navigationParams's request, navigationParams's response, navigationParams's policy container's CSP list, cspNavigationType, and navigable is "Blocked"; // - FIXME: navigationParams's reserved environment is non-null and the result of checking a navigation response's adherence to its embedder policy given navigationParams's response, navigable, and navigationParams's policy container's embedder policy is false; or // - the result of checking a navigation response's adherence to `X-Frame-Options` given navigationParams's response, navigable, navigationParams's policy container's CSP list, and navigationParams's origin is false, + // then: if (navigation_params.visit( [](NullOrError) { return true; }, [this](GC::Ref navigation_params) { @@ -1174,11 +1183,11 @@ WebIDL::ExceptionOr Navigable::populate_session_history_entry_document( return !check_a_navigation_responses_adherence_to_x_frame_options(navigation_params->response, this, navigation_params->origin); }, [](GC::Ref) { return false; })) { - // 1. Set entry's document state's document to the result of creating a document for inline content that doesn't have a DOM, given navigable, null, and navTimingType. The inline content should indicate to the user the sort of error that occurred. + // 1. Set entry's document state's document to the result of creating a document for inline content that doesn't have a DOM, given navigable, null, navTimingType, and userInvolvement. The inline content should indicate to the user the sort of error that occurred. auto error_message = navigation_params.has() ? navigation_params.get().value_or("Unknown error"sv) : "The request was denied."sv; auto error_html = load_error_page(entry->url(), error_message).release_value_but_fixme_should_propagate_errors(); - entry->document_state()->set_document(create_document_for_inline_content(this, navigation_id, [this, error_html](auto& document) { + entry->document_state()->set_document(create_document_for_inline_content(this, navigation_id, user_involvement, [this, error_html](auto& document) { auto parser = HTML::HTMLParser::create(document, error_html, "utf-8"sv); document.set_url(URL::URL("about:error")); parser->run(); @@ -1375,10 +1384,10 @@ WebIDL::ExceptionOr Navigable::navigate(NavigateParams params) // 18. If url's scheme is "javascript", then: if (url.scheme() == "javascript"sv) { - // 1. Queue a global task on the navigation and traversal task source given navigable's active window to navigate to a javascript: URL given navigable, url, historyHandling, initiatorOriginSnapshot, and cspNavigationType. + // 1. Queue a global task on the navigation and traversal task source given navigable's active window to navigate to a javascript: URL given navigable, url, historyHandling, initiatorOriginSnapshot, userInvolvement, and cspNavigationType. VERIFY(active_window()); - queue_global_task(Task::Source::NavigationAndTraversal, *active_window(), GC::create_function(heap(), [this, url, history_handling, initiator_origin_snapshot, csp_navigation_type, navigation_id] { - navigate_to_a_javascript_url(url, to_history_handling_behavior(history_handling), initiator_origin_snapshot, csp_navigation_type, navigation_id); + queue_global_task(Task::Source::NavigationAndTraversal, *active_window(), GC::create_function(heap(), [this, url, history_handling, initiator_origin_snapshot, user_involvement, csp_navigation_type, navigation_id] { + navigate_to_a_javascript_url(url, to_history_handling_behavior(history_handling), initiator_origin_snapshot, user_involvement, csp_navigation_type, navigation_id); })); // 2. Return. @@ -1422,7 +1431,7 @@ WebIDL::ExceptionOr Navigable::navigate(NavigateParams params) } // 20. In parallel, run these steps: - Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(heap(), [this, source_snapshot_params, target_snapshot_params, csp_navigation_type, document_resource, url, navigation_id, referrer_policy, initiator_origin_snapshot, response, history_handling, initiator_base_url_snapshot] { + Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(heap(), [this, source_snapshot_params, target_snapshot_params, csp_navigation_type, document_resource, url, navigation_id, referrer_policy, initiator_origin_snapshot, response, history_handling, initiator_base_url_snapshot, user_involvement] { // AD-HOC: Not in the spec but subsequent steps will fail if the navigable doesn't have an active window. if (!active_window()) { set_delaying_load_events(false); @@ -1490,13 +1499,12 @@ WebIDL::ExceptionOr Navigable::navigate(NavigateParams params) if (response) { } - // 9. Attempt to populate the history entry's document - // for historyEntry, given navigable, "navigate", sourceSnapshotParams, - // targetSnapshotParams, navigationId, navigationParams, cspNavigationType, with allowPOST - // set to true and completionSteps set to the following step: - populate_session_history_entry_document(history_entry, source_snapshot_params, target_snapshot_params, navigation_id, navigation_params, csp_navigation_type, true, GC::create_function(heap(), [this, history_entry, history_handling, navigation_id] { - // 1. Append session history traversal steps to navigable's traversable to finalize a cross-document navigation given navigable, historyHandling, and historyEntry. - traversable_navigable()->append_session_history_traversal_steps(GC::create_function(heap(), [this, history_entry, history_handling, navigation_id] { + // 9. Attempt to populate the history entry's document for historyEntry, given navigable, "navigate", + // sourceSnapshotParams, targetSnapshotParams, userInvolvement, navigationId, navigationParams, + // cspNavigationType, with allowPOST set to true and completionSteps set to the following step: + populate_session_history_entry_document(history_entry, source_snapshot_params, target_snapshot_params, user_involvement, navigation_id, navigation_params, csp_navigation_type, true, GC::create_function(heap(), [this, history_entry, history_handling, navigation_id, user_involvement] { + // 1. Append session history traversal steps to navigable's traversable to finalize a cross-document navigation given navigable, historyHandling, userInvolvement, and historyEntry. + traversable_navigable()->append_session_history_traversal_steps(GC::create_function(heap(), [this, history_entry, history_handling, navigation_id, user_involvement] { if (this->has_been_destroyed()) { // NOTE: This check is not in the spec but we should not continue navigation if navigable has been destroyed. set_delaying_load_events(false); @@ -1507,7 +1515,7 @@ WebIDL::ExceptionOr Navigable::navigate(NavigateParams params) set_delaying_load_events(false); return; } - finalize_a_cross_document_navigation(*this, to_history_handling_behavior(history_handling), history_entry); + finalize_a_cross_document_navigation(*this, to_history_handling_behavior(history_handling), user_involvement, history_entry); })); })).release_value_but_fixme_should_propagate_errors(); })); @@ -1591,9 +1599,9 @@ WebIDL::ExceptionOr Navigable::navigate_to_a_fragment(URL::URL const& url, auto traversable = traversable_navigable(); // 17. Append the following session history synchronous navigation steps involving navigable to traversable: - traversable->append_session_history_synchronous_navigation_steps(*this, GC::create_function(heap(), [this, traversable, history_entry, entry_to_replace, navigation_id, history_handling] { - // 1. Finalize a same-document navigation given traversable, navigable, historyEntry, and entryToReplace. - finalize_a_same_document_navigation(*traversable, *this, history_entry, entry_to_replace, history_handling); + traversable->append_session_history_synchronous_navigation_steps(*this, GC::create_function(heap(), [this, traversable, history_entry, entry_to_replace, navigation_id, history_handling, user_involvement] { + // 1. Finalize a same-document navigation given traversable, navigable, historyEntry, entryToReplace, historyHandling, and userInvolvement. + finalize_a_same_document_navigation(*traversable, *this, history_entry, entry_to_replace, history_handling, user_involvement); // FIXME: 2. Invoke WebDriver BiDi fragment navigated with navigable's active browsing context and a new WebDriver BiDi // navigation status whose id is navigationId, url is url, and status is "complete". @@ -1605,7 +1613,7 @@ WebIDL::ExceptionOr Navigable::navigate_to_a_fragment(URL::URL const& url, // https://html.spec.whatwg.org/multipage/browsing-the-web.html#evaluate-a-javascript:-url // https://whatpr.org/html/9893/browsing-the-web.html#evaluate-a-javascript:-url -GC::Ptr Navigable::evaluate_javascript_url(URL::URL const& url, URL::Origin const& new_document_origin, String navigation_id) +GC::Ptr Navigable::evaluate_javascript_url(URL::URL const& url, URL::Origin const& new_document_origin, UserNavigationInvolvement user_involvement, String navigation_id) { auto& vm = this->vm(); VERIFY(active_window()); @@ -1687,8 +1695,9 @@ GC::Ptr Navigable::evaluate_javascript_url(URL::URL const& url, U // policy container: policyContainer // final sandboxing flag set: finalSandboxFlags // opener policy: coop - // FIXME: navigation timing type: "navigate" + // FIXME: navigation timing type: "navigate" // about base URL: targetNavigable's active document's about base URL + // user involvement: userInvolvement auto navigation_params = vm.heap().allocate(); navigation_params->id = navigation_id; navigation_params->navigable = this; @@ -1703,13 +1712,14 @@ GC::Ptr Navigable::evaluate_javascript_url(URL::URL const& url, U navigation_params->final_sandboxing_flag_set = final_sandbox_flags; navigation_params->opener_policy = coop; navigation_params->about_base_url = active_document()->about_base_url(); + navigation_params->user_involvement = user_involvement; // 17. Return the result of loading an HTML document given navigationParams. return load_document(navigation_params); } // https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigate-to-a-javascript:-url -void Navigable::navigate_to_a_javascript_url(URL::URL const& url, HistoryHandlingBehavior history_handling, URL::Origin const& initiator_origin, CSPNavigationType csp_navigation_type, String navigation_id) +void Navigable::navigate_to_a_javascript_url(URL::URL const& url, HistoryHandlingBehavior history_handling, URL::Origin const& initiator_origin, UserNavigationInvolvement user_involvement, CSPNavigationType csp_navigation_type, String navigation_id) { // 1. Assert: historyHandling is "replace". VERIFY(history_handling == HistoryHandlingBehavior::Replace); @@ -1726,8 +1736,8 @@ void Navigable::navigate_to_a_javascript_url(URL::URL const& url, HistoryHandlin // FIXME: 5. If the result of should navigation request of type be blocked by Content Security Policy? given request and cspNavigationType is "Blocked", then return. (void)csp_navigation_type; - // 6. Let newDocument be the result of evaluating a javascript: URL given targetNavigable, url, and initiatorOrigin. - auto new_document = evaluate_javascript_url(url, initiator_origin, navigation_id); + // 6. Let newDocument be the result of evaluating a javascript: URL given targetNavigable, url, initiatorOrigin, and userInvolvement. + auto new_document = evaluate_javascript_url(url, initiator_origin, user_involvement, navigation_id); // 7. If newDocument is null, then return. if (!new_document) { @@ -1773,14 +1783,14 @@ void Navigable::navigate_to_a_javascript_url(URL::URL const& url, HistoryHandlin history_entry->set_url(entry_to_replace->url()); history_entry->set_document_state(document_state); - // 13. Append session history traversal steps to targetNavigable's traversable to finalize a cross-document navigation with targetNavigable, historyHandling, and historyEntry. - traversable_navigable()->append_session_history_traversal_steps(GC::create_function(heap(), [this, history_entry, history_handling, navigation_id] { - finalize_a_cross_document_navigation(*this, history_handling, history_entry); + // 13. Append session history traversal steps to targetNavigable's traversable to finalize a cross-document navigation with targetNavigable, historyHandling, userInvolvement, and historyEntry. + traversable_navigable()->append_session_history_traversal_steps(GC::create_function(heap(), [this, history_entry, history_handling, user_involvement] { + finalize_a_cross_document_navigation(*this, history_handling, user_involvement, history_entry); })); } // https://html.spec.whatwg.org/multipage/browsing-the-web.html#reload -void Navigable::reload() +void Navigable::reload(UserNavigationInvolvement user_involvement) { // 1. Set navigable's active session history entry's document state's reload pending to true. active_session_history_entry()->document_state()->set_reload_pending(true); @@ -1789,9 +1799,9 @@ void Navigable::reload() auto traversable = traversable_navigable(); // 3. Append the following session history traversal steps to traversable: - traversable->append_session_history_traversal_steps(GC::create_function(heap(), [traversable] { - // 1. Apply the reload history step to traversable. - traversable->apply_the_reload_history_step(); + traversable->append_session_history_traversal_steps(GC::create_function(heap(), [traversable, user_involvement] { + // 1. Apply the reload history step to traversable given userInvolvement. + traversable->apply_the_reload_history_step(user_involvement); })); } @@ -1874,7 +1884,7 @@ TargetSnapshotParams Navigable::snapshot_target_snapshot_params() } // https://html.spec.whatwg.org/multipage/browsing-the-web.html#finalize-a-cross-document-navigation -void finalize_a_cross_document_navigation(GC::Ref navigable, HistoryHandlingBehavior history_handling, GC::Ref history_entry) +void finalize_a_cross_document_navigation(GC::Ref navigable, HistoryHandlingBehavior history_handling, UserNavigationInvolvement user_involvement, GC::Ref history_entry) { // NOTE: This is not in the spec but we should not navigate destroyed navigable. if (navigable->has_been_destroyed()) @@ -1942,8 +1952,8 @@ void finalize_a_cross_document_navigation(GC::Ref navigable, HistoryH target_step = traversable->current_session_history_step(); } - // 10. Apply the push/replace history step targetStep to traversable. - traversable->apply_the_push_or_replace_history_step(target_step, history_handling, TraversableNavigable::SynchronousNavigation::No); + // 10. Apply the push/replace history step targetStep to traversable given historyHandling and userInvolvement. + traversable->apply_the_push_or_replace_history_step(target_step, history_handling, user_involvement, TraversableNavigable::SynchronousNavigation::No); // AD-HOC: If we're inside a navigable container, let's trigger a relayout in the container document. // This allows size negotiation between the containing document and SVG documents to happen. @@ -2013,8 +2023,8 @@ void perform_url_and_history_update_steps(DOM::Document& document, URL::URL new_ // 13. Append the following session history synchronous navigation steps involving navigable to traversable: traversable->append_session_history_synchronous_navigation_steps(*navigable, GC::create_function(document.realm().heap(), [traversable, navigable, new_entry, entry_to_replace, history_handling] { - // 1. Finalize a same-document navigation given traversable, navigable, newEntry, and entryToReplace. - finalize_a_same_document_navigation(*traversable, *navigable, new_entry, entry_to_replace, history_handling); + // 1. Finalize a same-document navigation given traversable, navigable, newEntry, entryToReplace, historyHandling, and "none". + finalize_a_same_document_navigation(*traversable, *navigable, new_entry, entry_to_replace, history_handling, UserNavigationInvolvement::None); // 2. FIXME: Invoke WebDriver BiDi history updated with navigable. })); diff --git a/Libraries/LibWeb/HTML/Navigable.h b/Libraries/LibWeb/HTML/Navigable.h index a5607f72b25de..627ee960c765a 100644 --- a/Libraries/LibWeb/HTML/Navigable.h +++ b/Libraries/LibWeb/HTML/Navigable.h @@ -34,13 +34,6 @@ enum class CSPNavigationType { FormSubmission, }; -// https://html.spec.whatwg.org/multipage/browsing-the-web.html#user-navigation-involvement -enum class UserNavigationInvolvement { - BrowserUI, - Activation, - None, -}; - // https://html.spec.whatwg.org/multipage/browsing-the-web.html#target-snapshot-params struct TargetSnapshotParams { SandboxingFlagSet sandboxing_flags {}; @@ -133,6 +126,7 @@ class Navigable : public JS::Cell { GC::Ptr entry, SourceSnapshotParams const& source_snapshot_params, TargetSnapshotParams const& target_snapshot_params, + UserNavigationInvolvement user_involvement, Optional navigation_id = {}, NavigationParamsVariant navigation_params = Navigable::NullOrError {}, CSPNavigationType csp_navigation_type = CSPNavigationType::Other, @@ -156,12 +150,12 @@ class Navigable : public JS::Cell { WebIDL::ExceptionOr navigate_to_a_fragment(URL::URL const&, HistoryHandlingBehavior, UserNavigationInvolvement, Optional navigation_api_state, String navigation_id); - GC::Ptr evaluate_javascript_url(URL::URL const&, URL::Origin const& new_document_origin, String navigation_id); - void navigate_to_a_javascript_url(URL::URL const&, HistoryHandlingBehavior, URL::Origin const& initiator_origin, CSPNavigationType csp_navigation_type, String navigation_id); + GC::Ptr evaluate_javascript_url(URL::URL const&, URL::Origin const& new_document_origin, UserNavigationInvolvement, String navigation_id); + void navigate_to_a_javascript_url(URL::URL const&, HistoryHandlingBehavior, URL::Origin const& initiator_origin, UserNavigationInvolvement, CSPNavigationType csp_navigation_type, String navigation_id); bool allowed_by_sandboxing_to_navigate(Navigable const& target, SourceSnapshotParams const&); - void reload(); + void reload(UserNavigationInvolvement = UserNavigationInvolvement::None); // https://github.com/whatwg/html/issues/9690 [[nodiscard]] bool has_been_destroyed() const { return m_has_been_destroyed; } @@ -245,7 +239,7 @@ class Navigable : public JS::Cell { HashTable& all_navigables(); bool navigation_must_be_a_replace(URL::URL const& url, DOM::Document const& document); -void finalize_a_cross_document_navigation(GC::Ref, HistoryHandlingBehavior, GC::Ref); +void finalize_a_cross_document_navigation(GC::Ref, HistoryHandlingBehavior, UserNavigationInvolvement, GC::Ref); void perform_url_and_history_update_steps(DOM::Document& document, URL::URL new_url, Optional = {}, HistoryHandlingBehavior history_handling = HistoryHandlingBehavior::Replace); UserNavigationInvolvement user_navigation_involvement(DOM::Event const&); diff --git a/Libraries/LibWeb/HTML/NavigationParams.h b/Libraries/LibWeb/HTML/NavigationParams.h index 086261eb7c3c2..6fddc20cff595 100644 --- a/Libraries/LibWeb/HTML/NavigationParams.h +++ b/Libraries/LibWeb/HTML/NavigationParams.h @@ -20,6 +20,13 @@ namespace Web::HTML { +// https://html.spec.whatwg.org/multipage/browsing-the-web.html#user-navigation-involvement +enum class UserNavigationInvolvement { + BrowserUI, + Activation, + None, +}; + // https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigation-params struct NavigationParams : JS::Cell { GC_CELL(NavigationParams, JS::Cell); @@ -66,6 +73,9 @@ struct NavigationParams : JS::Cell { // a URL or null used to populate the new Document's about base URL Optional about_base_url; + // a user navigation involvement used when obtaining a browsing context for the new Document + UserNavigationInvolvement user_involvement; + void visit_edges(Visitor& visitor) override; }; @@ -94,6 +104,9 @@ struct NonFetchSchemeNavigationParams : JS::Cell { // FIXME: a NavigationTimingType used for creating the navigation timing entry for the new Document + // a user navigation involvement used when obtaining a browsing context for the new Document (if one is created) + UserNavigationInvolvement user_involvement; + void visit_edges(Visitor& visitor) override; }; diff --git a/Libraries/LibWeb/HTML/TraversableNavigable.cpp b/Libraries/LibWeb/HTML/TraversableNavigable.cpp index 12bb33a080a84..df6e0b0e6d3ca 100644 --- a/Libraries/LibWeb/HTML/TraversableNavigable.cpp +++ b/Libraries/LibWeb/HTML/TraversableNavigable.cpp @@ -429,7 +429,7 @@ TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_history_ bool check_for_cancelation, IGNORE_USE_IN_ESCAPING_LAMBDA Optional source_snapshot_params, GC::Ptr initiator_to_check, - IGNORE_USE_IN_ESCAPING_LAMBDA Optional user_involvement_for_navigate_events, + IGNORE_USE_IN_ESCAPING_LAMBDA UserNavigationInvolvement user_involvement, IGNORE_USE_IN_ESCAPING_LAMBDA Optional navigation_type, IGNORE_USE_IN_ESCAPING_LAMBDA SynchronousNavigation synchronous_navigation) { @@ -459,9 +459,9 @@ TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_history_ auto navigables_crossing_documents = get_all_navigables_that_might_experience_a_cross_document_traversal(target_step); // 5. If checkForCancelation is true, and the result of checking if unloading is canceled given navigablesCrossingDocuments, traversable, targetStep, - // and userInvolvementForNavigateEvents is not "continue", then return that result. + // and userInvolvement is not "continue", then return that result. if (check_for_cancelation) { - auto result = check_if_unloading_is_canceled(navigables_crossing_documents, *this, target_step, user_involvement_for_navigate_events); + auto result = check_if_unloading_is_canceled(navigables_crossing_documents, *this, target_step, user_involvement); if (result == CheckIfUnloadingIsCanceledResult::CanceledByBeforeUnload) return HistoryStepResult::CanceledByBeforeUnload; if (result == CheckIfUnloadingIsCanceledResult::CanceledByNavigate) @@ -570,14 +570,11 @@ TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_history_ && target_entry != navigable->current_session_history_entry() && old_origin == navigable->current_session_history_entry()->document_state()->origin()) { - // 1. Assert: userInvolvementForNavigateEvents is not null. - VERIFY(user_involvement_for_navigate_events.has_value()); - - // 2. Let navigation be navigable's active window's navigation API. + // 1. Let navigation be navigable's active window's navigation API. auto navigation = active_window()->navigation(); - // 3. Fire a traverse navigate event at navigation given targetEntry and userInvolvementForNavigateEvents. - navigation->fire_a_traverse_navigate_event(*target_entry, *user_involvement_for_navigate_events); + // 2. Fire a traverse navigate event at navigation given targetEntry and userInvolvement. + navigation->fire_a_traverse_navigate_event(*target_entry, user_involvement); } auto after_document_populated = [old_origin, changing_navigable_continuation, &changing_navigable_continuations, &vm, &navigable](bool populated_cloned_target_she, GC::Ref target_entry) mutable { @@ -641,10 +638,11 @@ TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_history_ auto populated_target_entry = target_entry->clone(); // 7. In parallel, attempt to populate the history entry's document for targetEntry, given navigable, potentiallyTargetSpecificSourceSnapshotParams, - // targetSnapshotParams, with allowPOST set to allowPOST and completionSteps set to queue a global task on the navigation and traversal task source given - // navigable's active window to run afterDocumentPopulated. - Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(this->heap(), [populated_target_entry, potentially_target_specific_source_snapshot_params, target_snapshot_params, this, allow_POST, navigable, after_document_populated = GC::create_function(this->heap(), move(after_document_populated))] { - navigable->populate_session_history_entry_document(populated_target_entry, *potentially_target_specific_source_snapshot_params, target_snapshot_params, {}, Navigable::NullOrError {}, CSPNavigationType::Other, allow_POST, GC::create_function(this->heap(), [this, after_document_populated, populated_target_entry]() mutable { + // targetSnapshotParams, userInvolvement, with allowPOST set to allowPOST and completionSteps set to + // queue a global task on the navigation and traversal task source given navigable's active window to + // run afterDocumentPopulated. + Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(this->heap(), [populated_target_entry, potentially_target_specific_source_snapshot_params, target_snapshot_params, this, allow_POST, navigable, after_document_populated = GC::create_function(this->heap(), move(after_document_populated)), user_involvement] { + navigable->populate_session_history_entry_document(populated_target_entry, *potentially_target_specific_source_snapshot_params, target_snapshot_params, user_involvement, {}, Navigable::NullOrError {}, CSPNavigationType::Other, allow_POST, GC::create_function(this->heap(), [this, after_document_populated, populated_target_entry]() mutable { VERIFY(active_window()); queue_global_task(Task::Source::NavigationAndTraversal, *active_window(), GC::create_function(this->heap(), [after_document_populated, populated_target_entry]() mutable { after_document_populated->function()(true, populated_target_entry); @@ -789,8 +787,8 @@ TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_history_ // 1. Assert: navigationType is not null. VERIFY(navigation_type.has_value()); - // 2. Deactivate displayedDocument, given userNavigationInvolvement, targetEntry, navigationType, and afterPotentialUnloads. - deactivate_a_document_for_cross_document_navigation(*displayed_document, user_involvement_for_navigate_events, *populated_target_entry, after_potential_unload); + // 2. Deactivate displayedDocument, given userInvolvement, targetEntry, navigationType, and afterPotentialUnloads. + deactivate_a_document_for_cross_document_navigation(*displayed_document, user_involvement, *populated_target_entry, after_potential_unload); } } @@ -896,26 +894,23 @@ TraversableNavigable::CheckIfUnloadingIsCanceledResult TraversableNavigable::che // 3. If targetEntry is not traversable's current session history entry, and targetEntry's document state's origin is the same as // traversable's current session history entry's document state's origin, then: if (target_entry != traversable->current_session_history_entry() && target_entry->document_state()->origin() != traversable->current_session_history_entry()->document_state()->origin()) { - // 1. Assert: userInvolvementForNavigateEvent is not null. - VERIFY(user_involvement_for_navigate_events.has_value()); - - // 2. Let eventsFired be false. + // 1. Let eventsFired be false. IGNORE_USE_IN_ESCAPING_LAMBDA auto events_fired = false; - // 3. Let needsBeforeunload be true if navigablesThatNeedBeforeUnload contains traversable; otherwise false. + // 2. Let needsBeforeunload be true if navigablesThatNeedBeforeUnload contains traversable; otherwise false. auto it = navigables_that_need_before_unload.find_if([&traversable](GC::Root navigable) { return navigable.ptr() == traversable.ptr(); }); auto needs_beforeunload = it != navigables_that_need_before_unload.end(); - // 4. If needsBeforeunload is true, then remove traversable's active document from documentsToFireBeforeunload. + // 3. If needsBeforeunload is true, then remove traversable's active document from documentsToFireBeforeunload. if (needs_beforeunload) { documents_to_fire_beforeunload.remove_first_matching([&](auto& document) { return document.ptr() == traversable->active_document().ptr(); }); } - // 5. Queue a global task on the navigation and traversal task source given traversable's active window to perform the following steps: + // 4. Queue a global task on the navigation and traversal task source given traversable's active window to perform the following steps: VERIFY(traversable->active_window()); queue_global_task(Task::Source::NavigationAndTraversal, *traversable->active_window(), GC::create_function(heap(), [needs_beforeunload, user_involvement_for_navigate_events, traversable, target_entry, &final_status, &unload_prompt_shown, &events_fired] { // 1. if needsBeforeunload is true, then: @@ -952,12 +947,12 @@ TraversableNavigable::CheckIfUnloadingIsCanceledResult TraversableNavigable::che events_fired = true; })); - // 6. Wait for eventsFired to be true. + // 5. Wait for eventsFired to be true. main_thread_event_loop().spin_until(GC::create_function(heap(), [&] { return events_fired; })); - // 7. If finalStatus is not "continue", then return finalStatus. + // 6. If finalStatus is not "continue", then return finalStatus. if (final_status != CheckIfUnloadingIsCanceledResult::Continue) return final_status; } @@ -1177,24 +1172,25 @@ TraversableNavigable::HistoryStepResult TraversableNavigable::update_for_navigab auto step = current_session_history_step(); // 2. Return the result of applying the history step to traversable given false, null, null, null, and null. - return apply_the_history_step(step, false, {}, {}, {}, {}, SynchronousNavigation::No); + return apply_the_history_step(step, false, {}, {}, UserNavigationInvolvement::None, {}, SynchronousNavigation::No); } // https://html.spec.whatwg.org/multipage/browsing-the-web.html#apply-the-reload-history-step -TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_reload_history_step() +TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_reload_history_step(UserNavigationInvolvement user_involvement) { // 1. Let step be traversable's current session history step. auto step = current_session_history_step(); // 2. Return the result of applying the history step step to traversable given true, null, null, null, and "reload". - return apply_the_history_step(step, true, {}, {}, {}, Bindings::NavigationType::Reload, SynchronousNavigation::No); + return apply_the_history_step(step, true, {}, {}, user_involvement, Bindings::NavigationType::Reload, SynchronousNavigation::No); } -TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_push_or_replace_history_step(int step, HistoryHandlingBehavior history_handling, SynchronousNavigation synchronous_navigation) +// https://html.spec.whatwg.org/multipage/browsing-the-web.html#apply-the-push/replace-history-step +TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_push_or_replace_history_step(int step, HistoryHandlingBehavior history_handling, UserNavigationInvolvement user_involvement, SynchronousNavigation synchronous_navigation) { - // 1. Return the result of applying the history step step to traversable given false, null, null, null, and historyHandling. + // 1. Return the result of applying the history step step to traversable given false, null, null, userInvolvement, and historyHandling. auto navigation_type = history_handling == HistoryHandlingBehavior::Replace ? Bindings::NavigationType::Replace : Bindings::NavigationType::Push; - return apply_the_history_step(step, false, {}, {}, {}, navigation_type, synchronous_navigation); + return apply_the_history_step(step, false, {}, {}, user_involvement, navigation_type, synchronous_navigation); } TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_traverse_history_step(int step, Optional source_snapshot_params, GC::Ptr initiator_to_check, UserNavigationInvolvement user_involvement) @@ -1276,7 +1272,7 @@ void TraversableNavigable::destroy_top_level_traversable() } // https://html.spec.whatwg.org/multipage/browsing-the-web.html#finalize-a-same-document-navigation -void finalize_a_same_document_navigation(GC::Ref traversable, GC::Ref target_navigable, GC::Ref target_entry, GC::Ptr entry_to_replace, HistoryHandlingBehavior history_handling) +void finalize_a_same_document_navigation(GC::Ref traversable, GC::Ref target_navigable, GC::Ref target_entry, GC::Ptr entry_to_replace, HistoryHandlingBehavior history_handling, UserNavigationInvolvement user_involvement) { // NOTE: This is not in the spec but we should not navigate destroyed navigable. if (target_navigable->has_been_destroyed()) @@ -1321,8 +1317,8 @@ void finalize_a_same_document_navigation(GC::Ref traversab target_step = traversable->current_session_history_step(); } - // 6. Apply the push/replace history step targetStep to traversable given historyHandling. - traversable->apply_the_push_or_replace_history_step(*target_step, history_handling, TraversableNavigable::SynchronousNavigation::Yes); + // 6. Apply the push/replace history step targetStep to traversable given historyHandling and userInvolvement. + traversable->apply_the_push_or_replace_history_step(*target_step, history_handling, user_involvement, TraversableNavigable::SynchronousNavigation::Yes); } // https://html.spec.whatwg.org/multipage/interaction.html#system-visibility-state diff --git a/Libraries/LibWeb/HTML/TraversableNavigable.h b/Libraries/LibWeb/HTML/TraversableNavigable.h index 2c0ea774e42ba..f4e132a6e00b3 100644 --- a/Libraries/LibWeb/HTML/TraversableNavigable.h +++ b/Libraries/LibWeb/HTML/TraversableNavigable.h @@ -61,12 +61,12 @@ class TraversableNavigable final : public Navigable { }; HistoryStepResult apply_the_traverse_history_step(int, Optional, GC::Ptr, UserNavigationInvolvement); - HistoryStepResult apply_the_reload_history_step(); + HistoryStepResult apply_the_reload_history_step(UserNavigationInvolvement); enum class SynchronousNavigation : bool { Yes, No, }; - HistoryStepResult apply_the_push_or_replace_history_step(int step, HistoryHandlingBehavior history_handling, SynchronousNavigation); + HistoryStepResult apply_the_push_or_replace_history_step(int step, HistoryHandlingBehavior history_handling, UserNavigationInvolvement, SynchronousNavigation); HistoryStepResult update_for_navigable_creation_or_destruction(); int get_the_used_step(int step) const; @@ -122,7 +122,7 @@ class TraversableNavigable final : public Navigable { bool check_for_cancelation, Optional, GC::Ptr initiator_to_check, - Optional user_involvement_for_navigate_events, + UserNavigationInvolvement user_involvement, Optional navigation_type, SynchronousNavigation); @@ -167,6 +167,6 @@ struct BrowsingContextAndDocument { }; WebIDL::ExceptionOr create_a_new_top_level_browsing_context_and_document(GC::Ref page); -void finalize_a_same_document_navigation(GC::Ref traversable, GC::Ref target_navigable, GC::Ref target_entry, GC::Ptr entry_to_replace, HistoryHandlingBehavior); +void finalize_a_same_document_navigation(GC::Ref traversable, GC::Ref target_navigable, GC::Ref target_entry, GC::Ptr entry_to_replace, HistoryHandlingBehavior, UserNavigationInvolvement); }