LibWeb/HTML: Pass user_involvement through navigables code

This corresponds to part of https://github.com/whatwg/html/pull/10818
This commit is contained in:
Sam Atkins 2025-01-07 14:58:48 +00:00 committed by Andreas Kling
parent 8b5e9c2a1d
commit c6a18f795d
Notes: github-actions[bot] 2025-01-11 10:11:59 +00:00
7 changed files with 110 additions and 94 deletions

View file

@ -460,15 +460,16 @@ GC::Ptr<DOM::Document> 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

View file

@ -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<typename MutateDocument>
GC::Ref<DOM::Document> create_document_for_inline_content(GC::Ptr<HTML::Navigable> navigable, Optional<String> navigation_id, MutateDocument mutate_document)
GC::Ref<DOM::Document> create_document_for_inline_content(GC::Ptr<HTML::Navigable> navigable, Optional<String> navigation_id, HTML::UserNavigationInvolvement user_involvement, MutateDocument mutate_document)
{
auto& vm = navigable->vm();
@ -53,6 +53,7 @@ GC::Ref<DOM::Document> create_document_for_inline_content(GC::Ptr<HTML::Navigabl
// opener policy: coop
// FIXME: navigation timing type: navTimingType
// about base URL: null
// user involvement: userInvolvement
auto response = Fetch::Infrastructure::Response::create(vm);
response->url_list().append(URL::URL("about:error")); // AD-HOC: https://github.com/whatwg/html/issues/9122
auto navigation_params = vm.heap().allocate<HTML::NavigationParams>();
@ -69,6 +70,7 @@ GC::Ref<DOM::Document> create_document_for_inline_content(GC::Ptr<HTML::Navigabl
navigation_params->final_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();

View file

@ -618,8 +618,7 @@ static GC::Ptr<DOM::Document> 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<NavigationParams> create_navigation_params_from_a_srcdoc_resource(GC::Ptr<SessionHistoryEntry> entry, GC::Ptr<Navigable> navigable, TargetSnapshotParams const& target_snapshot_params, Optional<String> navigation_id)
static GC::Ref<NavigationParams> create_navigation_params_from_a_srcdoc_resource(GC::Ptr<SessionHistoryEntry> entry, GC::Ptr<Navigable> navigable, TargetSnapshotParams const& target_snapshot_params, UserNavigationInvolvement user_involvement, Optional<String> navigation_id)
{
auto& vm = navigable->vm();
VERIFY(navigable->active_window());
@ -685,6 +684,7 @@ static GC::Ref<NavigationParams> 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<NavigationParams>();
navigation_params->id = move(navigation_id);
navigation_params->navigable = navigable;
@ -695,12 +695,13 @@ static GC::Ref<NavigationParams> 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<Navigable::NavigationParamsVariant> create_navigation_params_by_fetching(GC::Ptr<SessionHistoryEntry> entry, GC::Ptr<Navigable> navigable, SourceSnapshotParams const& source_snapshot_params, TargetSnapshotParams const& target_snapshot_params, CSPNavigationType csp_navigation_type, Optional<String> navigation_id)
static WebIDL::ExceptionOr<Navigable::NavigationParamsVariant> create_navigation_params_by_fetching(GC::Ptr<SessionHistoryEntry> entry, GC::Ptr<Navigable> navigable, SourceSnapshotParams const& source_snapshot_params, TargetSnapshotParams const& target_snapshot_params, CSPNavigationType csp_navigation_type, UserNavigationInvolvement user_involvement, Optional<String> navigation_id)
{
auto& vm = navigable->vm();
VERIFY(navigable->active_window());
@ -997,6 +998,7 @@ static WebIDL::ExceptionOr<Navigable::NavigationParamsVariant> 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<NonFetchSchemeNavigationParams>();
navigation_params->id = navigation_id;
navigation_params->navigable = navigable;
@ -1004,6 +1006,7 @@ static WebIDL::ExceptionOr<Navigable::NavigationParamsVariant> 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<Navigable::NavigationParamsVariant> 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<NavigationParams>();
navigation_params->id = navigation_id;
navigation_params->navigable = navigable;
@ -1065,6 +1069,7 @@ static WebIDL::ExceptionOr<Navigable::NavigationParamsVariant> 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<void> Navigable::populate_session_history_entry_document(
GC::Ptr<SessionHistoryEntry> entry,
SourceSnapshotParams const& source_snapshot_params,
TargetSnapshotParams const& target_snapshot_params,
UserNavigationInvolvement user_involvement,
Optional<String> navigation_id,
Navigable::NavigationParamsVariant navigation_params,
CSPNavigationType csp_navigation_type,
@ -1099,16 +1105,16 @@ WebIDL::ExceptionOr<void> Navigable::populate_session_history_entry_document(
if (navigation_params.has<NullOrError>()) {
// 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<String>()) {
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<Empty>() || 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<void> 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<NonFetchSchemeNavigationParams>();
non_fetching_scheme_navigation_params->id = navigation_id;
non_fetching_scheme_navigation_params->navigable = this;
@ -1126,6 +1133,7 @@ WebIDL::ExceptionOr<void> 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<void> 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<void> 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<NavigationParams> navigation_params) {
@ -1174,11 +1183,11 @@ WebIDL::ExceptionOr<void> 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<NonFetchSchemeNavigationParams>) { 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<NullOrError>() ? navigation_params.get<NullOrError>().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<void> 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<void> 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<void> 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<void> 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<void> 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<void> 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<DOM::Document> Navigable::evaluate_javascript_url(URL::URL const& url, URL::Origin const& new_document_origin, String navigation_id)
GC::Ptr<DOM::Document> 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<DOM::Document> 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<NavigationParams>();
navigation_params->id = navigation_id;
navigation_params->navigable = this;
@ -1703,13 +1712,14 @@ GC::Ptr<DOM::Document> 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> navigable, HistoryHandlingBehavior history_handling, GC::Ref<SessionHistoryEntry> history_entry)
void finalize_a_cross_document_navigation(GC::Ref<Navigable> navigable, HistoryHandlingBehavior history_handling, UserNavigationInvolvement user_involvement, GC::Ref<SessionHistoryEntry> 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> 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.
}));

View file

@ -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 @@ public:
GC::Ptr<SessionHistoryEntry> entry,
SourceSnapshotParams const& source_snapshot_params,
TargetSnapshotParams const& target_snapshot_params,
UserNavigationInvolvement user_involvement,
Optional<String> navigation_id = {},
NavigationParamsVariant navigation_params = Navigable::NullOrError {},
CSPNavigationType csp_navigation_type = CSPNavigationType::Other,
@ -156,12 +150,12 @@ public:
WebIDL::ExceptionOr<void> navigate_to_a_fragment(URL::URL const&, HistoryHandlingBehavior, UserNavigationInvolvement, Optional<SerializationRecord> navigation_api_state, String navigation_id);
GC::Ptr<DOM::Document> 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<DOM::Document> 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 @@ private:
HashTable<Navigable*>& all_navigables();
bool navigation_must_be_a_replace(URL::URL const& url, DOM::Document const& document);
void finalize_a_cross_document_navigation(GC::Ref<Navigable>, HistoryHandlingBehavior, GC::Ref<SessionHistoryEntry>);
void finalize_a_cross_document_navigation(GC::Ref<Navigable>, HistoryHandlingBehavior, UserNavigationInvolvement, GC::Ref<SessionHistoryEntry>);
void perform_url_and_history_update_steps(DOM::Document& document, URL::URL new_url, Optional<SerializationRecord> = {}, HistoryHandlingBehavior history_handling = HistoryHandlingBehavior::Replace);
UserNavigationInvolvement user_navigation_involvement(DOM::Event const&);

View file

@ -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<URL::URL> 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;
};

View file

@ -429,7 +429,7 @@ TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_history_
bool check_for_cancelation,
IGNORE_USE_IN_ESCAPING_LAMBDA Optional<SourceSnapshotParams> source_snapshot_params,
GC::Ptr<Navigable> initiator_to_check,
IGNORE_USE_IN_ESCAPING_LAMBDA Optional<UserNavigationInvolvement> user_involvement_for_navigate_events,
IGNORE_USE_IN_ESCAPING_LAMBDA UserNavigationInvolvement user_involvement,
IGNORE_USE_IN_ESCAPING_LAMBDA Optional<Bindings::NavigationType> 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<SessionHistoryEntry> 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> 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<SourceSnapshotParams> source_snapshot_params, GC::Ptr<Navigable> 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<TraversableNavigable> traversable, GC::Ref<Navigable> target_navigable, GC::Ref<SessionHistoryEntry> target_entry, GC::Ptr<SessionHistoryEntry> entry_to_replace, HistoryHandlingBehavior history_handling)
void finalize_a_same_document_navigation(GC::Ref<TraversableNavigable> traversable, GC::Ref<Navigable> target_navigable, GC::Ref<SessionHistoryEntry> target_entry, GC::Ptr<SessionHistoryEntry> 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<TraversableNavigable> 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

View file

@ -61,12 +61,12 @@ public:
};
HistoryStepResult apply_the_traverse_history_step(int, Optional<SourceSnapshotParams>, GC::Ptr<Navigable>, 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 @@ private:
bool check_for_cancelation,
Optional<SourceSnapshotParams>,
GC::Ptr<Navigable> initiator_to_check,
Optional<UserNavigationInvolvement> user_involvement_for_navigate_events,
UserNavigationInvolvement user_involvement,
Optional<Bindings::NavigationType> navigation_type,
SynchronousNavigation);
@ -167,6 +167,6 @@ struct BrowsingContextAndDocument {
};
WebIDL::ExceptionOr<BrowsingContextAndDocument> create_a_new_top_level_browsing_context_and_document(GC::Ref<Page> page);
void finalize_a_same_document_navigation(GC::Ref<TraversableNavigable> traversable, GC::Ref<Navigable> target_navigable, GC::Ref<SessionHistoryEntry> target_entry, GC::Ptr<SessionHistoryEntry> entry_to_replace, HistoryHandlingBehavior);
void finalize_a_same_document_navigation(GC::Ref<TraversableNavigable> traversable, GC::Ref<Navigable> target_navigable, GC::Ref<SessionHistoryEntry> target_entry, GC::Ptr<SessionHistoryEntry> entry_to_replace, HistoryHandlingBehavior, UserNavigationInvolvement);
}