2022-12-12 11:46:54 +01:00
/*
2024-04-26 16:59:04 +02:00
* Copyright ( c ) 2022 - 2024 , Andreas Kling < kling @ serenityos . org >
2023-01-01 17:46:00 +01:00
* Copyright ( c ) 2023 , Aliaksandr Kalenik < kalenik . aliaksandr @ gmail . com >
2022-12-12 11:46:54 +01:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2024-08-08 14:17:54 +03:00
# include <LibWeb/CSS/SystemColor.h>
2023-01-01 17:46:00 +01:00
# include <LibWeb/Crypto/Crypto.h>
2022-12-12 11:46:54 +01:00
# include <LibWeb/DOM/Document.h>
2023-04-06 18:10:12 +03:00
# include <LibWeb/DOM/DocumentLoading.h>
2024-01-18 12:52:13 -07:00
# include <LibWeb/DOM/Event.h>
2024-04-26 16:59:04 +02:00
# include <LibWeb/DOM/Range.h>
2023-04-06 18:10:12 +03:00
# include <LibWeb/Fetch/Fetching/Fetching.h>
# include <LibWeb/Fetch/Infrastructure/FetchAlgorithms.h>
# include <LibWeb/Fetch/Infrastructure/FetchController.h>
# include <LibWeb/Fetch/Infrastructure/URL.h>
2022-12-12 11:46:54 +01:00
# include <LibWeb/HTML/BrowsingContext.h>
# include <LibWeb/HTML/DocumentState.h>
2023-09-21 13:47:19 -06:00
# include <LibWeb/HTML/HTMLIFrameElement.h>
2023-08-28 18:06:10 +02:00
# include <LibWeb/HTML/HistoryHandlingBehavior.h>
2022-12-12 11:46:54 +01:00
# include <LibWeb/HTML/Navigable.h>
2023-08-28 18:06:10 +02:00
# include <LibWeb/HTML/Navigation.h>
2023-04-06 18:10:12 +03:00
# include <LibWeb/HTML/NavigationParams.h>
2023-08-28 18:06:10 +02:00
# include <LibWeb/HTML/POSTResource.h>
2023-12-19 16:16:24 +00:00
# include <LibWeb/HTML/Parser/HTMLParser.h>
2023-08-28 18:06:10 +02:00
# include <LibWeb/HTML/SandboxingFlagSet.h>
2023-09-13 15:03:44 +02:00
# include <LibWeb/HTML/Scripting/ClassicScript.h>
2022-12-12 11:46:54 +01:00
# include <LibWeb/HTML/SessionHistoryEntry.h>
2023-08-28 18:06:10 +02:00
# include <LibWeb/HTML/StructuredSerialize.h>
2022-12-12 12:07:41 +01:00
# include <LibWeb/HTML/TraversableNavigable.h>
2023-10-08 11:59:40 +02:00
# include <LibWeb/HTML/Window.h>
2024-02-04 03:44:19 -07:00
# include <LibWeb/HTML/WindowProxy.h>
2023-04-11 10:20:11 +03:00
# include <LibWeb/Infra/Strings.h>
2023-08-22 16:00:42 +02:00
# include <LibWeb/Layout/Node.h>
2023-09-21 17:55:14 +02:00
# include <LibWeb/Loader/GeneratedPagesLoader.h>
2023-09-19 19:16:50 +02:00
# include <LibWeb/Page/Page.h>
2024-01-14 11:14:20 +01:00
# include <LibWeb/Painting/Paintable.h>
2024-01-15 11:28:21 +01:00
# include <LibWeb/Painting/ViewportPaintable.h>
2023-01-01 17:46:00 +01:00
# include <LibWeb/Platform/EventLoopPlugin.h>
2024-04-26 16:59:04 +02:00
# include <LibWeb/Selection/Selection.h>
2023-08-28 18:06:10 +02:00
# include <LibWeb/XHR/FormData.h>
2022-12-12 11:46:54 +01:00
namespace Web : : HTML {
2023-11-19 19:47:52 +01:00
JS_DEFINE_ALLOCATOR ( Navigable ) ;
2023-08-23 01:03:28 +02:00
class ResponseHolder : public JS : : Cell {
JS_CELL ( ResponseHolder , JS : : Cell ) ;
2023-11-19 19:47:52 +01:00
JS_DECLARE_ALLOCATOR ( ResponseHolder ) ;
2023-08-23 01:03:28 +02:00
public :
[[nodiscard]] static JS : : NonnullGCPtr < ResponseHolder > create ( JS : : VM & vm )
{
return vm . heap ( ) . allocate_without_realm < ResponseHolder > ( ) ;
}
[[nodiscard]] JS : : GCPtr < Fetch : : Infrastructure : : Response > response ( ) const { return m_response ; }
void set_response ( JS : : GCPtr < Fetch : : Infrastructure : : Response > response ) { m_response = response ; }
virtual void visit_edges ( Cell : : Visitor & visitor ) override
{
2024-04-03 15:48:46 +02:00
Base : : visit_edges ( visitor ) ;
2023-08-23 01:03:28 +02:00
visitor . visit ( m_response ) ;
}
private :
JS : : GCPtr < Fetch : : Infrastructure : : Response > m_response ;
} ;
2023-11-19 19:47:52 +01:00
JS_DEFINE_ALLOCATOR ( ResponseHolder ) ;
2023-09-05 23:36:20 +02:00
HashTable < Navigable * > & all_navigables ( )
2023-01-01 17:43:05 +01:00
{
static HashTable < Navigable * > set ;
return set ;
}
2023-04-06 23:16:38 +03:00
// https://html.spec.whatwg.org/multipage/document-sequences.html#child-navigable
Vector < JS : : Handle < Navigable > > Navigable : : child_navigables ( ) const
{
Vector < JS : : Handle < Navigable > > results ;
for ( auto & entry : all_navigables ( ) ) {
2024-03-27 15:59:12 +01:00
if ( entry - > current_session_history_entry ( ) - > step ( ) = = SessionHistoryEntry : : Pending : : Tag )
2023-09-01 22:13:30 +02:00
continue ;
2023-04-06 23:16:38 +03:00
if ( entry - > parent ( ) = = this )
results . append ( entry ) ;
}
return results ;
}
2023-08-27 17:06:39 +02:00
bool Navigable : : is_traversable ( ) const
{
return is < TraversableNavigable > ( * this ) ;
}
2024-04-19 15:38:17 +02:00
bool Navigable : : is_ancestor_of ( JS : : NonnullGCPtr < Navigable > other ) const
{
for ( auto ancestor = other - > parent ( ) ; ancestor ; ancestor = ancestor - > parent ( ) ) {
if ( ancestor = = this )
return true ;
}
return false ;
}
2024-04-26 14:55:54 +02:00
Navigable : : Navigable ( JS : : NonnullGCPtr < Page > page )
: m_page ( page )
2024-04-26 16:59:04 +02:00
, m_event_handler ( { } , * this )
2023-01-01 17:43:05 +01:00
{
all_navigables ( ) . set ( this ) ;
}
2022-12-12 11:46:54 +01:00
2023-01-01 17:43:05 +01:00
Navigable : : ~ Navigable ( )
{
all_navigables ( ) . remove ( this ) ;
}
2022-12-12 11:46:54 +01:00
void Navigable : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2024-04-26 14:55:54 +02:00
visitor . visit ( m_page ) ;
2022-12-12 11:46:54 +01:00
visitor . visit ( m_parent ) ;
visitor . visit ( m_current_session_history_entry ) ;
visitor . visit ( m_active_session_history_entry ) ;
visitor . visit ( m_container ) ;
2024-04-26 16:59:04 +02:00
m_event_handler . visit_edges ( visitor ) ;
2022-12-12 11:46:54 +01:00
}
2023-09-14 01:22:00 +02:00
void Navigable : : set_delaying_load_events ( bool value )
{
if ( value ) {
auto document = container_document ( ) ;
VERIFY ( document ) ;
m_delaying_the_load_event . emplace ( * document ) ;
} else {
m_delaying_the_load_event . clear ( ) ;
}
}
2023-01-01 17:43:05 +01:00
JS : : GCPtr < Navigable > Navigable : : navigable_with_active_document ( JS : : NonnullGCPtr < DOM : : Document > document )
{
for ( auto * navigable : all_navigables ( ) ) {
if ( navigable - > active_document ( ) = = document )
return navigable ;
}
return nullptr ;
}
2023-04-25 20:46:32 +03:00
// https://html.spec.whatwg.org/multipage/document-sequences.html#initialize-the-navigable
ErrorOr < void > Navigable : : initialize_navigable ( JS : : NonnullGCPtr < DocumentState > document_state , JS : : GCPtr < Navigable > parent )
{
static int next_id = 0 ;
m_id = TRY ( String : : number ( next_id + + ) ) ;
2024-03-28 13:20:48 +01:00
// 1. Assert: documentState's document is non-null.
VERIFY ( document_state - > document ( ) ) ;
2023-04-25 20:46:32 +03:00
2024-03-28 13:20:48 +01:00
// 2. Let entry be a new session history entry, with
JS : : NonnullGCPtr < SessionHistoryEntry > entry = * heap ( ) . allocate_without_realm < SessionHistoryEntry > ( ) ;
2023-04-25 20:46:32 +03:00
// URL: document's URL
2024-03-27 15:59:12 +01:00
entry - > set_url ( document_state - > document ( ) - > url ( ) ) ;
2023-04-25 20:46:32 +03:00
// document state: documentState
2024-03-27 15:59:12 +01:00
entry - > set_document_state ( document_state ) ;
2023-04-25 20:46:32 +03:00
2024-03-28 13:20:48 +01:00
// 3. Set navigable's current session history entry to entry.
2023-04-25 20:46:32 +03:00
m_current_session_history_entry = entry ;
2024-03-28 13:20:48 +01:00
// 4. Set navigable's active session history entry to entry.
2023-04-25 20:46:32 +03:00
m_active_session_history_entry = entry ;
2024-03-28 13:20:48 +01:00
// 5. Set navigable's parent to parent.
2023-04-25 20:46:32 +03:00
m_parent = parent ;
return { } ;
}
2023-04-06 23:30:02 +03:00
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#getting-the-target-history-entry
JS : : GCPtr < SessionHistoryEntry > Navigable : : get_the_target_history_entry ( int target_step ) const
{
// 1. Let entries be the result of getting session history entries for navigable.
auto & entries = get_session_history_entries ( ) ;
// 2. Return the item in entries that has the greatest step less than or equal to step.
JS : : GCPtr < SessionHistoryEntry > result = nullptr ;
for ( auto & entry : entries ) {
2024-03-27 15:59:12 +01:00
auto entry_step = entry - > step ( ) . get < int > ( ) ;
2023-04-06 23:30:02 +03:00
if ( entry_step < = target_step ) {
2024-03-27 15:59:12 +01:00
if ( ! result | | result - > step ( ) . get < int > ( ) < entry_step ) {
2023-04-06 23:30:02 +03:00
result = entry ;
}
}
}
return result ;
}
2023-04-06 23:33:21 +03:00
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#activate-history-entry
void Navigable : : activate_history_entry ( JS : : GCPtr < SessionHistoryEntry > entry )
{
// FIXME: 1. Save persisted state to the navigable's active session history entry.
// 2. Let newDocument be entry's document.
2024-03-27 16:14:52 +01:00
JS : : GCPtr < DOM : : Document > new_document = entry - > document ( ) . ptr ( ) ;
2023-04-06 23:33:21 +03:00
// 3. Assert: newDocument's is initial about:blank is false, i.e., we never traverse
// back to the initial about:blank Document because it always gets replaced when we
// navigate away from it.
VERIFY ( ! new_document - > is_initial_about_blank ( ) ) ;
// 4. Set navigable's active session history entry to entry.
m_active_session_history_entry = entry ;
// 5. Make active newDocument.
new_document - > make_active ( ) ;
}
2022-12-12 11:46:54 +01:00
// https://html.spec.whatwg.org/multipage/document-sequences.html#nav-document
JS : : GCPtr < DOM : : Document > Navigable : : active_document ( )
{
// A navigable's active document is its active session history entry's document.
2024-03-27 16:14:52 +01:00
return m_active_session_history_entry - > document ( ) ;
2022-12-12 11:46:54 +01:00
}
// https://html.spec.whatwg.org/multipage/document-sequences.html#nav-bc
JS : : GCPtr < BrowsingContext > Navigable : : active_browsing_context ( )
{
// A navigable's active browsing context is its active document's browsing context.
// If this navigable is a traversable navigable, then its active browsing context will be a top-level browsing context.
if ( auto document = active_document ( ) )
return document - > browsing_context ( ) ;
return nullptr ;
}
// https://html.spec.whatwg.org/multipage/document-sequences.html#nav-wp
JS : : GCPtr < HTML : : WindowProxy > Navigable : : active_window_proxy ( )
{
// A navigable's active WindowProxy is its active browsing context's associated WindowProxy.
if ( auto browsing_context = active_browsing_context ( ) )
return browsing_context - > window_proxy ( ) ;
return nullptr ;
}
// https://html.spec.whatwg.org/multipage/document-sequences.html#nav-window
JS : : GCPtr < HTML : : Window > Navigable : : active_window ( )
{
// A navigable's active window is its active WindowProxy's [[Window]].
if ( auto window_proxy = active_window_proxy ( ) )
return window_proxy - > window ( ) ;
return nullptr ;
}
// https://html.spec.whatwg.org/multipage/document-sequences.html#nav-target
String Navigable : : target_name ( ) const
{
2023-08-31 20:51:46 +02:00
// A navigable's target name is its active session history entry's document state's navigable target name.
2024-03-27 15:59:12 +01:00
return active_session_history_entry ( ) - > document_state ( ) - > navigable_target_name ( ) ;
2022-12-12 11:46:54 +01:00
}
// https://html.spec.whatwg.org/multipage/document-sequences.html#nav-container
JS : : GCPtr < NavigableContainer > Navigable : : container ( ) const
{
// The container of a navigable navigable is the navigable container whose nested navigable is navigable, or null if there is no such element.
2023-04-23 17:50:08 +03:00
return NavigableContainer : : navigable_container_with_content_navigable ( const_cast < Navigable & > ( * this ) ) ;
2022-12-12 11:46:54 +01:00
}
2022-12-16 12:05:19 +01:00
// https://html.spec.whatwg.org/multipage/document-sequences.html#nav-container-document
JS : : GCPtr < DOM : : Document > Navigable : : container_document ( ) const
{
auto container = this - > container ( ) ;
// 1. If navigable's container is null, then return null.
if ( ! container )
return nullptr ;
// 2. Return navigable's container's node document.
return container - > document ( ) ;
}
2022-12-12 12:07:41 +01:00
// https://html.spec.whatwg.org/multipage/document-sequences.html#nav-traversable
2023-04-06 12:06:45 +03:00
JS : : GCPtr < TraversableNavigable > Navigable : : traversable_navigable ( ) const
2022-12-12 12:07:41 +01:00
{
// 1. Let navigable be inputNavigable.
2023-04-06 12:06:45 +03:00
auto navigable = const_cast < Navigable * > ( this ) ;
2022-12-12 12:07:41 +01:00
// 2. While navigable is not a traversable navigable, set navigable to navigable's parent.
while ( navigable & & ! is < TraversableNavigable > ( * navigable ) )
navigable = navigable - > parent ( ) ;
// 3. Return navigable.
return static_cast < TraversableNavigable * > ( navigable ) ;
}
2022-12-12 12:17:46 +01:00
// https://html.spec.whatwg.org/multipage/document-sequences.html#nav-top
JS : : GCPtr < TraversableNavigable > Navigable : : top_level_traversable ( )
{
// 1. Let navigable be inputNavigable.
auto navigable = this ;
// 2. While navigable's parent is not null, set navigable to navigable's parent.
while ( navigable - > parent ( ) )
navigable = navigable - > parent ( ) ;
// 3. Return navigable.
return verify_cast < TraversableNavigable > ( navigable ) ;
}
2023-08-22 15:22:50 +02:00
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#set-the-ongoing-navigation
void Navigable : : set_ongoing_navigation ( Variant < Empty , Traversal , String > ongoing_navigation )
{
// 1. If navigable's ongoing navigation is equal to newValue, then return.
if ( m_ongoing_navigation = = ongoing_navigation )
return ;
2023-09-20 23:21:16 -06:00
// 2. Inform the navigation API about aborting navigation given navigable.
inform_the_navigation_api_about_aborting_navigation ( ) ;
2023-08-22 15:22:50 +02:00
// 3. Set navigable's ongoing navigation to newValue.
m_ongoing_navigation = ongoing_navigation ;
}
2023-11-23 08:09:12 -07:00
// https://html.spec.whatwg.org/multipage/document-sequences.html#the-rules-for-choosing-a-navigable
2024-05-28 09:12:18 -06:00
Navigable : : ChosenNavigable Navigable : : choose_a_navigable ( StringView name , TokenizedFeature : : NoOpener no_opener , ActivateTab activate_tab , Optional < TokenizedFeature : : Map const & > window_features )
2023-04-11 10:20:11 +03:00
{
2023-11-28 08:36:09 -07:00
// NOTE: Implementation for step 7 here.
JS : : GCPtr < Navigable > same_name_navigable = nullptr ;
if ( ! Infra : : is_ascii_case_insensitive_match ( name , " _blank " sv ) ) {
for ( auto & n : all_navigables ( ) ) {
2024-02-12 17:34:03 -07:00
if ( n - > target_name ( ) = = name & & ! n - > has_been_destroyed ( ) ) {
2023-11-28 08:36:09 -07:00
same_name_navigable = n ;
}
}
}
2023-04-11 10:20:11 +03:00
// 1. Let chosen be null.
JS : : GCPtr < Navigable > chosen = nullptr ;
// 2. Let windowType be "existing or none".
auto window_type = WindowType : : ExistingOrNone ;
// 3. Let sandboxingFlagSet be current's active document's active sandboxing flag set.
2023-11-23 08:09:12 -07:00
auto sandboxing_flag_set = active_document ( ) - > active_sandboxing_flag_set ( ) ;
2023-04-11 10:20:11 +03:00
// 4. If name is the empty string or an ASCII case-insensitive match for "_self", then set chosen to currentNavigable.
if ( name . is_empty ( ) | | Infra : : is_ascii_case_insensitive_match ( name , " _self " sv ) ) {
chosen = this ;
}
2023-11-23 08:09:12 -07:00
2023-04-11 10:20:11 +03:00
// 5. Otherwise, if name is an ASCII case-insensitive match for "_parent",
// set chosen to currentNavigable's parent, if any, and currentNavigable otherwise.
else if ( Infra : : is_ascii_case_insensitive_match ( name , " _parent " sv ) ) {
if ( auto parent = this - > parent ( ) )
chosen = parent ;
else
chosen = this ;
}
2023-11-23 08:09:12 -07:00
2023-04-11 10:20:11 +03:00
// 6. Otherwise, if name is an ASCII case-insensitive match for "_top",
// set chosen to currentNavigable's traversable navigable.
else if ( Infra : : is_ascii_case_insensitive_match ( name , " _top " sv ) ) {
chosen = traversable_navigable ( ) ;
}
2023-11-23 08:09:12 -07:00
2023-11-28 08:36:09 -07:00
// 7. Otherwise, if name is not an ASCII case-insensitive match for "_blank",
2023-04-11 10:20:11 +03:00
// there exists a navigable whose target name is the same as name, currentNavigable's
// active browsing context is familiar with that navigable's active browsing context,
// and the user agent determines that the two browsing contexts are related enough that
// it is ok if they reach each other, set chosen to that navigable. If there are multiple
// matching navigables, the user agent should pick one in some arbitrary consistent manner,
// such as the most recently opened, most recently focused, or more closely related, and set
// chosen to it.
2023-11-28 08:36:09 -07:00
else if ( same_name_navigable ! = nullptr & & ( active_browsing_context ( ) - > is_familiar_with ( * same_name_navigable - > active_browsing_context ( ) ) ) ) {
// FIXME: Handle multiple name-match case
// FIXME: When are these contexts 'not related enough' ?
chosen = same_name_navigable ;
}
2023-11-23 08:09:12 -07:00
// 8. Otherwise, a new top-level traversable is being requested, and what happens depends on the
2023-04-11 10:20:11 +03:00
// user agent's configuration and abilities — it is determined by the rules given for the first
// applicable option from the following list:
else {
2023-11-23 08:09:12 -07:00
// --> If current's active window does not have transient activation and the user agent has been configured to
// not show popups (i.e., the user agent has a "popup blocker" enabled)
2023-12-03 20:06:06 +13:00
if ( ! active_window ( ) - > has_transient_activation ( ) & & traversable_navigable ( ) - > page ( ) . should_block_pop_ups ( ) ) {
2023-11-23 08:09:12 -07:00
// FIXME: The user agent may inform the user that a popup has been blocked.
dbgln ( " Pop-up blocked! " ) ;
}
// --> If sandboxingFlagSet has the sandboxed auxiliary navigation browsing context flag set
else if ( has_flag ( sandboxing_flag_set , SandboxingFlagSet : : SandboxedAuxiliaryNavigation ) ) {
// FIXME: The user agent may report to a developer console that a popup has been blocked.
dbgln ( " Pop-up blocked! " ) ;
}
// --> If the user agent has been configured such that in this instance it will create a new top-level traversable
else if ( true ) { // FIXME: When is this the case?
// 1. Set windowType to "new and unrestricted".
window_type = WindowType : : NewAndUnrestricted ;
// 2. Let currentDocument be currentNavigable's active document.
auto current_document = active_document ( ) ;
// 3. If currentDocument's cross-origin opener policy's value is "same-origin" or "same-origin-plus-COEP",
// and currentDocument's origin is not same origin with currentDocument's relevant settings object's top-level origin, then:
if ( ( current_document - > cross_origin_opener_policy ( ) . value = = CrossOriginOpenerPolicyValue : : SameOrigin | | current_document - > cross_origin_opener_policy ( ) . value = = CrossOriginOpenerPolicyValue : : SameOriginPlusCOEP )
& & ! current_document - > origin ( ) . is_same_origin ( relevant_settings_object ( * current_document ) . top_level_origin ) ) {
// 1. Set noopener to true.
no_opener = TokenizedFeature : : NoOpener : : Yes ;
// 2. Set name to "_blank".
name = " _blank " sv ;
// 3. Set windowType to "new with no opener".
window_type = WindowType : : NewWithNoOpener ;
}
// NOTE: In the presence of a cross-origin opener policy,
// nested documents that are cross-origin with their top-level browsing context's active document always set noopener to true.
// 4. Let chosen be null.
chosen = nullptr ;
// 5. Let targetName be the empty string.
String target_name ;
// 6. If name is not an ASCII case-insensitive match for "_blank", then set targetName to name.
if ( ! Infra : : is_ascii_case_insensitive_match ( name , " _blank " sv ) )
target_name = MUST ( String : : from_utf8 ( name ) ) ;
2024-05-28 09:12:18 -06:00
auto create_new_traversable_closure = [ this , no_opener , target_name , activate_tab , window_features ] ( JS : : GCPtr < BrowsingContext > opener ) - > JS : : NonnullGCPtr < Navigable > {
auto hints = WebViewHints : : from_tokenised_features ( window_features . value_or ( { } ) , traversable_navigable ( ) - > page ( ) ) ;
2024-02-03 09:09:33 -07:00
auto [ page , window_handle ] = traversable_navigable ( ) - > page ( ) . client ( ) . page_did_request_new_web_view ( activate_tab , hints , no_opener ) ;
2024-01-30 20:55:24 -07:00
auto traversable = TraversableNavigable : : create_a_new_top_level_traversable ( * page , opener , target_name ) . release_value_but_fixme_should_propagate_errors ( ) ;
page - > set_top_level_traversable ( traversable ) ;
traversable - > set_window_handle ( window_handle ) ;
return traversable ;
} ;
auto create_new_traversable = JS : : create_heap_function ( heap ( ) , move ( create_new_traversable_closure ) ) ;
2023-11-23 08:09:12 -07:00
// 7. If noopener is true, then set chosen to the result of creating a new top-level traversable given null and targetName.
if ( no_opener = = TokenizedFeature : : NoOpener : : Yes ) {
2024-01-30 20:55:24 -07:00
chosen = create_new_traversable - > function ( ) ( nullptr ) ;
2023-11-23 08:09:12 -07:00
}
// 8. Otherwise:
else {
// 1. Set chosen to the result of creating a new top-level traversable given currentNavigable's active browsing context and targetName.
2024-01-30 20:55:24 -07:00
chosen = create_new_traversable - > function ( ) ( active_browsing_context ( ) ) ;
2023-11-23 08:09:12 -07:00
// FIXME: 2. If sandboxingFlagSet's sandboxed navigation browsing context flag is set,
// then set chosen's active browsing context's one permitted sandboxed navigator to currentNavigable's active browsing context.
}
// FIXME: 5. If sandboxingFlagSet's sandbox propagates to auxiliary browsing contexts flag is set,
// then all the flags that are set in sandboxingFlagSet must be set in chosen's active browsing context's popup sandboxing flag set.
// Our BrowsingContexts do not have SandboxingFlagSets yet, only documents do
}
// --> If the user agent has been configured such that in this instance t will reuse current
else if ( false ) { // FIXME: When is this the case?
// Set chosen to current.
chosen = * this ;
}
// --> If the user agent has been configured such that in this instance it will not find a browsing context
else if ( false ) { // FIXME: When is this the case?
// Do nothing.
}
2023-04-11 10:20:11 +03:00
}
return { chosen . ptr ( ) , window_type } ;
}
2023-04-06 18:08:44 +03:00
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#getting-session-history-entries
Vector < JS : : NonnullGCPtr < SessionHistoryEntry > > & Navigable : : get_session_history_entries ( ) const
{
// 1. Let traversable be navigable's traversable navigable.
auto traversable = traversable_navigable ( ) ;
// FIXME 2. Assert: this is running within traversable's session history traversal queue.
// 3. If navigable is traversable, return traversable's session history entries.
if ( this = = traversable )
return traversable - > session_history_entries ( ) ;
// 4. Let docStates be an empty ordered set of document states.
Vector < JS : : GCPtr < DocumentState > > doc_states ;
// 5. For each entry of traversable's session history entries, append entry's document state to docStates.
for ( auto & entry : traversable - > session_history_entries ( ) )
2024-03-27 15:59:12 +01:00
doc_states . append ( entry - > document_state ( ) ) ;
2023-04-06 18:08:44 +03:00
// 6. For each docState of docStates:
while ( ! doc_states . is_empty ( ) ) {
auto doc_state = doc_states . take_first ( ) ;
// 1. For each nestedHistory of docState's nested histories:
for ( auto & nested_history : doc_state - > nested_histories ( ) ) {
// 1. If nestedHistory's id equals navigable's id, return nestedHistory's entries.
if ( nested_history . id = = id ( ) )
return nested_history . entries ;
// 2. For each entry of nestedHistory's entries, append entry's document state to docStates.
for ( auto & entry : nested_history . entries )
2024-03-27 15:59:12 +01:00
doc_states . append ( entry - > document_state ( ) ) ;
2023-04-06 18:08:44 +03:00
}
}
VERIFY_NOT_REACHED ( ) ;
}
2023-09-21 13:47:19 -06:00
// https://html.spec.whatwg.org/multipage/browsers.html#determining-navigation-params-policy-container
2024-03-18 16:22:27 +13:00
static PolicyContainer determine_navigation_params_policy_container ( URL : : URL const & response_url ,
2023-09-21 13:47:19 -06:00
Optional < PolicyContainer > history_policy_container ,
Optional < PolicyContainer > initiator_policy_container ,
Optional < PolicyContainer > parent_policy_container ,
Optional < PolicyContainer > response_policy_container )
{
// NOTE: The clone a policy container AO is just a C++ copy
// 1. If historyPolicyContainer is not null, then:
if ( history_policy_container . has_value ( ) ) {
// FIXME: 1. Assert: responseURL requires storing the policy container in history.
// 2. Return a clone of historyPolicyContainer.
return * history_policy_container ;
}
// 2. If responseURL is about:srcdoc, then:
if ( response_url = = " about:srcdoc " sv ) {
// 1. Assert: parentPolicyContainer is not null.
VERIFY ( parent_policy_container . has_value ( ) ) ;
// 2. Return a clone of parentPolicyContainer.
return * parent_policy_container ;
}
// 3. If responseURL is local and initiatorPolicyContainer is not null, then return a clone of initiatorPolicyContainer.
if ( Fetch : : Infrastructure : : is_local_url ( response_url ) & & initiator_policy_container . has_value ( ) )
return * initiator_policy_container ;
// 4. If responsePolicyContainer is not null, then return responsePolicyContainer.
// FIXME: File a spec issue to say "a clone of" here for consistency
if ( response_policy_container . has_value ( ) )
return * response_policy_container ;
// 5. Return a new policy container.
return { } ;
}
// https://html.spec.whatwg.org/multipage/browsers.html#obtain-coop
static CrossOriginOpenerPolicy obtain_a_cross_origin_opener_policy ( JS : : NonnullGCPtr < Fetch : : Infrastructure : : Response > , Fetch : : Infrastructure : : Request : : ReservedClientType const & reserved_client )
{
// 1. Let policy be a new cross-origin opener policy.
CrossOriginOpenerPolicy policy = { } ;
// AD-HOC: We don't yet setup environments in all cases
2024-04-24 11:09:36 +02:00
if ( ! reserved_client )
2023-09-21 13:47:19 -06:00
return policy ;
2024-04-24 11:09:36 +02:00
auto & reserved_environment = * reserved_client ;
2023-09-21 13:47:19 -06:00
// 2. If reservedEnvironment is a non-secure context, then return policy.
if ( is_non_secure_context ( reserved_environment ) )
return policy ;
// FIXME: We don't yet have the technology to extract structured data from Fetch headers
// FIXME: 3. Let parsedItem be the result of getting a structured field value given `Cross-Origin-Opener-Policy` and "item" from response's header list.
// FIXME: 4. If parsedItem is not null, then:
// FIXME: nested steps...
// FIXME: 5. Set parsedItem to the result of getting a structured field value given `Cross-Origin-Opener-Policy-Report-Only` and "item" from response's header list.
// FIXME: 6. If parsedItem is not null, then:
// FIXME: nested steps...
// 7. Return policy.
return policy ;
}
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#attempt-to-create-a-non-fetch-scheme-document
static JS : : GCPtr < DOM : : Document > attempt_to_create_a_non_fetch_scheme_document ( NonFetchSchemeNavigationParams const & params )
{
// FIXME: Implement this algorithm to hand off to external software or display inline content
dbgln ( " (FIXME) Don't know how to navigate to {} " , params . url ) ;
return nullptr ;
}
2023-04-05 09:57:45 +03:00
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#create-navigation-params-from-a-srcdoc-resource
2024-04-21 19:57:33 +00:00
static WebIDL : : ExceptionOr < JS : : NonnullGCPtr < NavigationParams > > create_navigation_params_from_a_srcdoc_resource ( JS : : GCPtr < SessionHistoryEntry > entry , JS : : GCPtr < Navigable > navigable , TargetSnapshotParams const & target_snapshot_params , Optional < String > navigation_id )
2023-04-05 09:57:45 +03:00
{
auto & vm = navigable - > vm ( ) ;
auto & realm = navigable - > active_window ( ) - > realm ( ) ;
// 1. Let documentResource be entry's document state's resource.
2024-03-27 15:59:12 +01:00
auto document_resource = entry - > document_state ( ) - > resource ( ) ;
2023-04-05 09:57:45 +03:00
VERIFY ( document_resource . has < String > ( ) ) ;
// 2. Let response be a new response with
// URL: about:srcdoc
// header list: (`Content-Type`, `text/html`)
// body: the UTF-8 encoding of documentResource, as a body
auto response = Fetch : : Infrastructure : : Response : : create ( vm ) ;
2024-03-18 16:22:27 +13:00
response - > url_list ( ) . append ( URL : : URL ( " about:srcdoc " ) ) ;
2024-04-26 13:24:20 -04:00
auto header = Fetch : : Infrastructure : : Header : : from_string_pair ( " Content-Type " sv , " text/html " sv ) ;
response - > header_list ( ) - > append ( move ( header ) ) ;
2023-04-05 09:57:45 +03:00
response - > set_body ( TRY ( Fetch : : Infrastructure : : byte_sequence_as_body ( realm , document_resource . get < String > ( ) . bytes ( ) ) ) ) ;
2023-09-21 13:47:19 -06:00
// 3. Let responseOrigin be the result of determining the origin given response's URL, targetSnapshotParams's sandboxing flags, and entry's document state's origin.
2024-03-27 15:59:12 +01:00
auto response_origin = determine_the_origin ( * response - > url ( ) , target_snapshot_params . sandboxing_flags , entry - > document_state ( ) - > origin ( ) ) ;
2023-04-05 09:57:45 +03:00
// 4. Let coop be a new cross-origin opener policy.
2023-09-21 13:47:19 -06:00
CrossOriginOpenerPolicy coop = { } ;
2023-04-05 09:57:45 +03:00
// 5. Let coopEnforcementResult be a new cross-origin opener policy enforcement result with
// url: response's URL
2023-09-13 22:38:28 +02:00
// origin: responseOrigin
2023-04-05 09:57:45 +03:00
// cross-origin opener policy: coop
CrossOriginOpenerPolicyEnforcementResult coop_enforcement_result {
. url = * response - > url ( ) ,
2023-09-13 22:38:28 +02:00
. origin = response_origin ,
2023-04-05 09:57:45 +03:00
. cross_origin_opener_policy = coop
} ;
2023-09-21 13:47:19 -06:00
// 6. Let policyContainer be the result of determining navigation params policy container given response's URL,
// entry's document state's history policy container, null, navigable's container document's policy container, and null.
2024-03-27 15:59:12 +01:00
Optional < PolicyContainer > history_policy_container = entry - > document_state ( ) - > history_policy_container ( ) . visit (
2023-09-21 13:47:19 -06:00
[ ] ( PolicyContainer const & c ) - > Optional < PolicyContainer > { return c ; } ,
[ ] ( DocumentState : : Client ) - > Optional < PolicyContainer > { return { } ; } ) ;
2023-09-28 02:12:06 +02:00
PolicyContainer policy_container ;
if ( navigable - > container ( ) ) {
// NOTE: Specification assumes that only navigables corresponding to iframes can be navigated to about:srcdoc.
// We also use srcdoc to implement load_html() for top level navigables so we need to null check container
// because it might be null.
policy_container = determine_navigation_params_policy_container ( * response - > url ( ) , history_policy_container , { } , navigable - > container_document ( ) - > policy_container ( ) , { } ) ;
}
2023-04-05 09:57:45 +03:00
// 7. Return a new navigation params, with
// id: navigationId
2023-09-21 13:47:19 -06:00
// navigable: navigable
2023-04-05 09:57:45 +03:00
// request: null
// response: response
2023-09-21 13:47:19 -06:00
// fetch controller: null
// commit early hints: null
2023-04-05 09:57:45 +03:00
// COOP enforcement result: coopEnforcementResult
// reserved environment: null
2023-09-21 13:47:19 -06:00
// origin: responseOrigin
// policy container: policyContainer
// final sandboxing flag set: targetSnapshotParams's sandboxing flags
// cross-origin opener policy: coop
2023-04-05 09:57:45 +03:00
// FIXME: navigation timing type: navTimingType
2023-09-21 13:47:19 -06:00
// about base URL: entry's document state's about base URL
2024-04-21 19:57:33 +00:00
auto navigation_params = vm . heap ( ) . allocate_without_realm < NavigationParams > ( ) ;
navigation_params - > id = move ( navigation_id ) ;
navigation_params - > navigable = navigable ;
navigation_params - > response = response ;
navigation_params - > coop_enforcement_result = move ( coop_enforcement_result ) ;
navigation_params - > origin = move ( response_origin ) ;
navigation_params - > policy_container = policy_container ;
navigation_params - > final_sandboxing_flag_set = target_snapshot_params . sandboxing_flags ;
navigation_params - > cross_origin_opener_policy = move ( coop ) ;
navigation_params - > about_base_url = entry - > document_state ( ) - > about_base_url ( ) ;
return navigation_params ;
2023-04-05 09:57:45 +03:00
}
2023-04-06 18:11:10 +03:00
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#create-navigation-params-by-fetching
2024-06-18 15:26:12 +02:00
static WebIDL : : ExceptionOr < Navigable : : NavigationParamsVariant > create_navigation_params_by_fetching ( JS : : GCPtr < SessionHistoryEntry > entry , JS : : GCPtr < Navigable > navigable , SourceSnapshotParams const & source_snapshot_params , TargetSnapshotParams const & target_snapshot_params , CSPNavigationType csp_navigation_type , Optional < String > navigation_id )
2023-04-06 18:11:10 +03:00
{
auto & vm = navigable - > vm ( ) ;
auto & realm = navigable - > active_window ( ) - > realm ( ) ;
2023-09-21 13:47:19 -06:00
auto & active_document = * navigable - > active_document ( ) ;
( void ) csp_navigation_type ;
2023-04-06 18:11:10 +03:00
// FIXME: 1. Assert: this is running in parallel.
// 2. Let documentResource be entry's document state's resource.
2024-03-27 15:59:12 +01:00
auto document_resource = entry - > document_state ( ) - > resource ( ) ;
2023-04-06 18:11:10 +03:00
// 3. Let request be a new request, with
// url: entry's URL
// client: sourceSnapshotParams's fetch client
// destination: "document"
// credentials mode: "include"
// use-URL-credentials flag: set
// redirect mode: "manual"
// replaces client id: navigable's active document's relevant settings object's id
// mode: "navigate"
// referrer: entry's document state's request referrer
2023-09-21 13:47:19 -06:00
// referrer policy: entry's document state's request referrer policy
2023-04-06 18:11:10 +03:00
auto request = Fetch : : Infrastructure : : Request : : create ( vm ) ;
2024-03-27 15:59:12 +01:00
request - > set_url ( entry - > url ( ) ) ;
2023-04-06 18:11:10 +03:00
request - > set_client ( source_snapshot_params . fetch_client ) ;
request - > set_destination ( Fetch : : Infrastructure : : Request : : Destination : : Document ) ;
request - > set_credentials_mode ( Fetch : : Infrastructure : : Request : : CredentialsMode : : Include ) ;
request - > set_use_url_credentials ( true ) ;
request - > set_redirect_mode ( Fetch : : Infrastructure : : Request : : RedirectMode : : Manual ) ;
2023-12-24 15:44:50 +13:00
request - > set_replaces_client_id ( active_document . relevant_settings_object ( ) . id ) ;
2023-04-06 18:11:10 +03:00
request - > set_mode ( Fetch : : Infrastructure : : Request : : Mode : : Navigate ) ;
2024-03-27 15:59:12 +01:00
request - > set_referrer ( entry - > document_state ( ) - > request_referrer ( ) ) ;
2023-04-06 18:11:10 +03:00
// 4. If documentResource is a POST resource, then:
if ( document_resource . has < POSTResource > ( ) ) {
// 1. Set request's method to `POST`.
2023-10-14 18:55:18 +01:00
request - > set_method ( TRY_OR_THROW_OOM ( vm , ByteBuffer : : copy ( " POST " sv . bytes ( ) ) ) ) ;
2023-04-06 18:11:10 +03:00
// 2. Set request's body to documentResource's request body.
request - > set_body ( document_resource . get < POSTResource > ( ) . request_body . value ( ) ) ;
// 3. Set `Content-Type` to documentResource's request content-type in request's header list.
auto request_content_type = document_resource . get < POSTResource > ( ) . request_content_type ;
auto request_content_type_string = [ request_content_type ] ( ) {
switch ( request_content_type ) {
case POSTResource : : RequestContentType : : ApplicationXWWWFormUrlencoded :
return " application/x-www-form-urlencoded " sv ;
case POSTResource : : RequestContentType : : MultipartFormData :
return " multipart/form-data " sv ;
case POSTResource : : RequestContentType : : TextPlain :
return " text/plain " sv ;
default :
VERIFY_NOT_REACHED ( ) ;
}
} ( ) ;
2024-04-26 13:24:20 -04:00
auto header = Fetch : : Infrastructure : : Header : : from_string_pair ( " Content-Type " sv , request_content_type_string ) ;
request - > header_list ( ) - > append ( move ( header ) ) ;
2023-04-06 18:11:10 +03:00
}
// 5. If entry's document state's reload pending is true, then set request's reload-navigation flag.
2024-03-27 15:59:12 +01:00
if ( entry - > document_state ( ) - > reload_pending ( ) )
2023-04-06 18:11:10 +03:00
request - > set_reload_navigation ( true ) ;
// 6. Otherwise, if entry's document state's ever populated is true, then set request's history-navigation flag.
2024-03-27 15:59:12 +01:00
if ( entry - > document_state ( ) - > ever_populated ( ) )
2023-04-06 18:11:10 +03:00
request - > set_history_navigation ( true ) ;
2023-09-21 13:47:19 -06:00
// 7. If sourceSnapshotParams's has transient activation is true, then set request's user-activation to true.
if ( source_snapshot_params . has_transient_activation )
request - > set_user_activation ( true ) ;
// 8. If navigable's container is non-null:
if ( navigable - > container ( ) ! = nullptr ) {
// 1. If the navigable's container has a browsing context scope origin, then set request's origin to that browsing context scope origin.
// FIXME: From "browsing context scope origin": This definition is broken and needs investigation to see what it was intended to express: see issue #4703.
// The referenced issue suggests that it is a no-op to retrieve the browsing context scope origin.
// 2. Set request's destination to navigable's container's local name.
// FIXME: Are there other container types? If so, we need a helper here
Web : : Fetch : : Infrastructure : : Request : : Destination destination = is < HTMLIFrameElement > ( * navigable - > container ( ) ) ? Web : : Fetch : : Infrastructure : : Request : : Destination : : IFrame
: Web : : Fetch : : Infrastructure : : Request : : Destination : : Object ;
request - > set_destination ( destination ) ;
// 3. If sourceSnapshotParams's fetch client is navigable's container document's relevant settings object,
// then set request's initiator type to navigable's container's local name.
// NOTE: This ensure that only container-initiated navigations are reported to resource timing.
if ( source_snapshot_params . fetch_client = = & navigable - > container_document ( ) - > relevant_settings_object ( ) ) {
// FIXME: Are there other container types? If so, we need a helper here
Web : : Fetch : : Infrastructure : : Request : : InitiatorType initiator_type = is < HTMLIFrameElement > ( * navigable - > container ( ) ) ? Web : : Fetch : : Infrastructure : : Request : : InitiatorType : : IFrame
: Web : : Fetch : : Infrastructure : : Request : : InitiatorType : : Object ;
request - > set_initiator_type ( initiator_type ) ;
}
}
2023-04-06 18:11:10 +03:00
// 9. Let response be null.
2023-08-23 01:03:28 +02:00
// NOTE: We use a heap-allocated cell to hold the response pointer because the processResponse callback below
// might use it after this stack is freed.
auto response_holder = ResponseHolder : : create ( vm ) ;
2023-04-06 18:11:10 +03:00
// 10. Let responseOrigin be null.
Optional < HTML : : Origin > response_origin ;
// 11. Let fetchController be null.
JS : : GCPtr < Fetch : : Infrastructure : : FetchController > fetch_controller = nullptr ;
2023-09-21 13:47:19 -06:00
// 12. Let coopEnforcementResult be a new cross-origin opener policy enforcement result, with
// - url: navigable's active document's URL
// - origin: navigable's active document's origin
// - cross-origin opener policy: navigable's active document's cross-origin opener policy
// - current context is navigation source: true if navigable's active document's origin is same origin with
// entry's document state's initiator origin otherwise false
CrossOriginOpenerPolicyEnforcementResult coop_enforcement_result = {
. url = active_document . url ( ) ,
. origin = active_document . origin ( ) ,
. cross_origin_opener_policy = active_document . cross_origin_opener_policy ( ) ,
2024-03-27 15:59:12 +01:00
. current_context_is_navigation_source = entry - > document_state ( ) - > initiator_origin ( ) . has_value ( ) & & active_document . origin ( ) . is_same_origin ( * entry - > document_state ( ) - > initiator_origin ( ) )
2023-09-21 13:47:19 -06:00
} ;
2023-04-06 18:11:10 +03:00
// 13. Let finalSandboxFlags be an empty sandboxing flag set.
2023-08-28 11:57:21 +02:00
SandboxingFlagSet final_sandbox_flags = { } ;
2023-04-06 18:11:10 +03:00
2023-09-21 13:47:19 -06:00
// 14. Let responsePolicyContainer be null.
Optional < PolicyContainer > response_policy_container = { } ;
// 15. Let responseCOOP be a new cross-origin opener policy.
CrossOriginOpenerPolicy response_coop = { } ;
2023-04-06 18:11:10 +03:00
// 16. Let locationURL be null.
2024-03-18 16:22:27 +13:00
ErrorOr < Optional < URL : : URL > > location_url { OptionalNone { } } ;
2023-04-06 18:11:10 +03:00
// 17. Let currentURL be request's current URL.
2024-03-18 16:22:27 +13:00
URL : : URL current_url = request - > current_url ( ) ;
2023-04-06 18:11:10 +03:00
2023-09-21 13:47:19 -06:00
// 18. Let commitEarlyHints be null.
Function < void ( DOM : : Document & ) > commit_early_hints = nullptr ;
2023-04-06 18:11:10 +03:00
// 19. While true:
while ( true ) {
// FIXME: 1. If request's reserved client is not null and currentURL's origin is not the same as request's reserved client's creation URL's origin, then:
// FIXME: 2. If request's reserved client is null, then:
// FIXME: 3. If the result of should navigation request of type be blocked by Content Security Policy? given request and cspNavigationType is "Blocked", then set response to a network error and break. [CSP]
// 4. Set response to null.
2023-08-23 01:03:28 +02:00
response_holder - > set_response ( nullptr ) ;
2023-04-06 18:11:10 +03:00
// 5. If fetchController is null, then set fetchController to the result of fetching request,
// with processEarlyHintsResponse set to processEarlyHintsResponseas defined below, processResponse
// set to processResponse as defined below, and useParallelQueue set to true.
if ( ! fetch_controller ) {
// FIXME: Let processEarlyHintsResponse be the following algorithm given a response earlyResponse:
// Let processResponse be the following algorithm given a response fetchedResponse:
2023-08-23 01:03:28 +02:00
auto process_response = [ response_holder ] ( JS : : NonnullGCPtr < Fetch : : Infrastructure : : Response > fetch_response ) {
2023-04-06 18:11:10 +03:00
// 1. Set response to fetchedResponse.
2023-08-23 01:03:28 +02:00
response_holder - > set_response ( fetch_response ) ;
2023-04-06 18:11:10 +03:00
} ;
fetch_controller = TRY ( Fetch : : Fetching : : fetch (
realm ,
request ,
Fetch : : Infrastructure : : FetchAlgorithms : : create ( vm ,
{
. process_request_body_chunk_length = { } ,
. process_request_end_of_body = { } ,
. process_early_hints_response = { } ,
. process_response = move ( process_response ) ,
. process_response_end_of_body = { } ,
. process_response_consume_body = { } ,
} ) ,
Fetch : : Fetching : : UseParallelQueue : : Yes ) ) ;
}
// 6. Otherwise, process the next manual redirect for fetchController.
else {
fetch_controller - > process_next_manual_redirect ( ) ;
}
// 7. Wait until either response is non-null, or navigable's ongoing navigation changes to no longer equal navigationId.
Platform : : EventLoopPlugin : : the ( ) . spin_until ( [ & ] ( ) {
2023-08-23 01:03:28 +02:00
if ( response_holder - > response ( ) ! = nullptr )
2023-04-06 18:11:10 +03:00
return true ;
if ( navigation_id . has_value ( ) & & ( ! navigable - > ongoing_navigation ( ) . has < String > ( ) | | navigable - > ongoing_navigation ( ) . get < String > ( ) ! = * navigation_id ) )
return true ;
return false ;
} ) ;
// If the latter condition occurs, then abort fetchController, and return. Otherwise, proceed onward.
if ( navigation_id . has_value ( ) & & ( ! navigable - > ongoing_navigation ( ) . has < String > ( ) | | navigable - > ongoing_navigation ( ) . get < String > ( ) ! = * navigation_id ) ) {
fetch_controller - > abort ( realm , { } ) ;
2023-09-21 13:47:19 -06:00
return Empty { } ;
2023-04-06 18:11:10 +03:00
}
// 8. If request's body is null, then set entry's document state's resource to null.
if ( ! request - > body ( ) . has < Empty > ( ) ) {
2024-03-27 15:59:12 +01:00
entry - > document_state ( ) - > set_resource ( Empty { } ) ;
2023-04-06 18:11:10 +03:00
}
2023-09-21 13:47:19 -06:00
// FIXME 9. Set responsePolicyContainer to the result of creating a policy container from a fetch response given response and request's reserved client.
// FIXME 10. Set finalSandboxFlags to the union of targetSnapshotParams's sandboxing flags and responsePolicyContainer's CSP list's CSP-derived sandboxing flags.
2023-09-17 17:11:34 +02:00
// 11. Set responseOrigin to the result of determining the origin given response's URL, finalSandboxFlags, and entry's document state's initiator origin.
2024-03-27 15:59:12 +01:00
response_origin = determine_the_origin ( * response_holder - > response ( ) - > url ( ) , final_sandbox_flags , entry - > document_state ( ) - > initiator_origin ( ) ) ;
2023-04-06 18:11:10 +03:00
2023-09-21 13:47:19 -06:00
// 12. If navigable is a top-level traversable, then:
if ( navigable - > is_top_level_traversable ( ) ) {
// 1. Set responseCOOP to the result of obtaining a cross-origin opener policy given response and request's reserved client.
response_coop = obtain_a_cross_origin_opener_policy ( * response_holder - > response ( ) , request - > reserved_client ( ) ) ;
// FIXME: 2. Set coopEnforcementResult to the result of enforcing the response's cross-origin opener policy given navigable's active browsing context,
// response's URL, responseOrigin, responseCOOP, coopEnforcementResult and request's referrer.
// FIXME: 3. If finalSandboxFlags is not empty and responseCOOP's value is not "unsafe-none", then set response to an appropriate network error and break.
// NOTE: This results in a network error as one cannot simultaneously provide a clean slate to a response
// using cross-origin opener policy and sandbox the result of navigating to that response.
}
// 13. FIXME If response is not a network error, navigable is a child navigable, and the result of performing a cross-origin resource policy check
// with navigable's container document's origin, navigable's container document's relevant settings object, request's destination, response,
// and true is blocked, then set response to a network error and break.
// NOTE: Here we're running the cross-origin resource policy check against the parent navigable rather than navigable itself
// This is because we care about the same-originness of the embedded content against the parent context, not the navigation source.
2023-04-06 18:11:10 +03:00
// 14. Set locationURL to response's location URL given currentURL's fragment.
2023-12-22 12:28:29 +00:00
location_url = response_holder - > response ( ) - > location_url ( current_url . fragment ( ) ) ;
2023-04-06 18:11:10 +03:00
VERIFY ( ! location_url . is_error ( ) ) ;
// 15. If locationURL is failure or null, then break.
if ( location_url . is_error ( ) | | ! location_url . value ( ) . has_value ( ) ) {
break ;
}
// 16. Assert: locationURL is a URL.
VERIFY ( location_url . value ( ) - > is_valid ( ) ) ;
2023-09-21 13:47:19 -06:00
// 17. Set entry's classic history API state to StructuredSerializeForStorage(null).
2024-03-27 15:59:12 +01:00
entry - > set_classic_history_api_state ( MUST ( structured_serialize_for_storage ( vm , JS : : js_null ( ) ) ) ) ;
2023-04-06 18:11:10 +03:00
// 18. Let oldDocState be entry's document state.
2024-03-27 15:59:12 +01:00
auto old_doc_state = entry - > document_state ( ) ;
2023-04-06 18:11:10 +03:00
// 19. Set entry's document state to a new document state, with
// history policy container: a clone of the oldDocState's history policy container if it is non-null; null otherwise
// request referrer: oldDocState's request referrer
// request referrer policy: oldDocState's request referrer policy
// origin: oldDocState's origin
// resource: oldDocState's resource
// ever populated: oldDocState's ever populated
// navigable target name: oldDocState's navigable target name
2024-03-27 15:59:12 +01:00
auto new_document_state = navigable - > heap ( ) . allocate_without_realm < DocumentState > ( ) ;
new_document_state - > set_history_policy_container ( old_doc_state - > history_policy_container ( ) ) ;
new_document_state - > set_request_referrer ( old_doc_state - > request_referrer ( ) ) ;
new_document_state - > set_request_referrer_policy ( old_doc_state - > request_referrer_policy ( ) ) ;
new_document_state - > set_origin ( old_doc_state - > origin ( ) ) ;
new_document_state - > set_resource ( old_doc_state - > resource ( ) ) ;
new_document_state - > set_ever_populated ( old_doc_state - > ever_populated ( ) ) ;
new_document_state - > set_navigable_target_name ( old_doc_state - > navigable_target_name ( ) ) ;
entry - > set_document_state ( new_document_state ) ;
2023-04-06 18:11:10 +03:00
// 20. If locationURL's scheme is not an HTTP(S) scheme, then:
if ( ! Fetch : : Infrastructure : : is_http_or_https_scheme ( location_url . value ( ) - > scheme ( ) ) ) {
// 1. Set entry's document state's resource to null.
2024-03-27 15:59:12 +01:00
entry - > document_state ( ) - > set_resource ( Empty { } ) ;
2023-04-06 18:11:10 +03:00
// 2. Break.
break ;
}
// 21. Set currentURL to locationURL.
current_url = location_url . value ( ) . value ( ) ;
// 22. Set entry's URL to currentURL.
2024-03-27 15:59:12 +01:00
entry - > set_url ( current_url ) ;
2023-04-06 18:11:10 +03:00
}
2023-09-21 13:47:19 -06:00
// 20. If locationURL is a URL whose scheme is not a fetch scheme, then return a new non-fetch scheme navigation params, with
2023-04-06 18:11:10 +03:00
if ( ! location_url . is_error ( ) & & location_url . value ( ) . has_value ( ) & & ! Fetch : : Infrastructure : : is_fetch_scheme ( location_url . value ( ) . value ( ) . scheme ( ) ) ) {
2023-09-21 13:47:19 -06:00
// - id: navigationId
// - navigable: navigable
// - URL: locationURL
// - target snapshot sandboxing flags: targetSnapshotParams's sandboxing flags
// - source snapshot has transient activation: sourceSnapshotParams's has transient activation
// - initiator origin: responseOrigin
// FIXME: - navigation timing type: navTimingType
2024-04-21 19:57:33 +00:00
auto navigation_params = vm . heap ( ) . allocate_without_realm < NonFetchSchemeNavigationParams > ( ) ;
navigation_params - > id = navigation_id ;
navigation_params - > navigable = navigable ;
navigation_params - > url = location_url . release_value ( ) . value ( ) ;
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 ) ;
return navigation_params ;
2023-04-06 18:11:10 +03:00
}
// 21. If any of the following are true:
// - response is a network error;
// - locationURL is failure; or
// - locationURL is a URL whose scheme is a fetch scheme
// then return null.
2024-06-18 15:26:12 +02:00
if ( response_holder - > response ( ) - > is_network_error ( ) ) {
// AD-HOC: We pass the error message if we have one in NullWithError
if ( response_holder - > response ( ) - > network_error_message ( ) . has_value ( ) & & ! response_holder - > response ( ) - > network_error_message ( ) . value ( ) . is_null ( ) )
return response_holder - > response ( ) - > network_error_message ( ) . value ( ) ;
else
return Empty { } ;
} else if ( location_url . is_error ( ) | | ( location_url . value ( ) . has_value ( ) & & Fetch : : Infrastructure : : is_fetch_scheme ( location_url . value ( ) . value ( ) . scheme ( ) ) ) )
2023-09-21 13:47:19 -06:00
return Empty { } ;
2023-04-06 18:11:10 +03:00
// 22. Assert: locationURL is null and response is not a network error.
VERIFY ( ! location_url . value ( ) . has_value ( ) ) ;
2023-08-23 01:03:28 +02:00
VERIFY ( ! response_holder - > response ( ) - > is_network_error ( ) ) ;
2023-04-06 18:11:10 +03:00
2023-09-21 13:47:19 -06:00
// 23. Let resultPolicyContainer be the result of determining navigation params policy container given response's URL,
// entry's document state's history policy container, sourceSnapshotParams's source policy container, null, and responsePolicyContainer.
2024-03-27 15:59:12 +01:00
Optional < PolicyContainer > history_policy_container = entry - > document_state ( ) - > history_policy_container ( ) . visit (
2023-09-21 13:47:19 -06:00
[ ] ( PolicyContainer const & c ) - > Optional < PolicyContainer > { return c ; } ,
[ ] ( DocumentState : : Client ) - > Optional < PolicyContainer > { return { } ; } ) ;
auto result_policy_container = determine_navigation_params_policy_container ( * response_holder - > response ( ) - > url ( ) , history_policy_container , source_snapshot_params . source_policy_container , { } , response_policy_container ) ;
// 24. If navigable's container is an iframe, and response's timing allow passed flag is set, then set container's pending resource-timing start time to null.
if ( navigable - > container ( ) & & is < HTML : : HTMLIFrameElement > ( * navigable - > container ( ) ) & & response_holder - > response ( ) - > timing_allow_passed ( ) )
static_cast < HTML : : HTMLIFrameElement & > ( * navigable - > container ( ) ) . set_pending_resource_start_time ( { } ) ;
2023-04-06 18:11:10 +03:00
// 25. Return a new navigation params, with
// id: navigationId
2023-09-21 13:47:19 -06:00
// navigable: navigable
2023-04-06 18:11:10 +03:00
// request: request
// response: response
2023-09-21 13:47:19 -06:00
// fetch controller: fetchController
// commit early hints: commitEarlyHints
// cross-origin opener policy: responseCOOP
// reserved environment: request's reserved client
2023-04-06 18:11:10 +03:00
// origin: responseOrigin
2023-09-21 13:47:19 -06:00
// policy container: resultPolicyContainer
// final sandboxing flag set: finalSandboxFlags
// COOP enforcement result: coopEnforcementResult
2023-04-06 18:11:10 +03:00
// FIXME: navigation timing type: navTimingType
2023-09-21 13:47:19 -06:00
// about base URL: entry's document state's about base URL
2024-04-21 19:57:33 +00:00
auto navigation_params = vm . heap ( ) . allocate_without_realm < NavigationParams > ( ) ;
navigation_params - > id = navigation_id ;
navigation_params - > navigable = navigable ;
navigation_params - > request = request ;
navigation_params - > response = * response_holder - > response ( ) ;
navigation_params - > fetch_controller = fetch_controller ;
navigation_params - > commit_early_hints = move ( commit_early_hints ) ;
navigation_params - > coop_enforcement_result = coop_enforcement_result ;
navigation_params - > reserved_environment = request - > reserved_client ( ) ;
navigation_params - > origin = * response_origin ;
navigation_params - > policy_container = result_policy_container ;
navigation_params - > final_sandboxing_flag_set = final_sandbox_flags ;
navigation_params - > cross_origin_opener_policy = response_coop ;
navigation_params - > about_base_url = entry - > document_state ( ) - > about_base_url ( ) ;
2023-09-21 13:47:19 -06:00
return navigation_params ;
2023-04-06 18:11:10 +03:00
}
2023-04-06 18:10:12 +03:00
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#attempt-to-populate-the-history-entry's-document
2023-09-21 13:47:19 -06:00
WebIDL : : ExceptionOr < void > Navigable : : populate_session_history_entry_document (
JS : : GCPtr < SessionHistoryEntry > entry ,
SourceSnapshotParams const & source_snapshot_params ,
TargetSnapshotParams const & target_snapshot_params ,
Optional < String > navigation_id ,
2024-06-18 15:26:12 +02:00
Navigable : : NavigationParamsVariant navigation_params ,
2023-09-21 13:47:19 -06:00
CSPNavigationType csp_navigation_type ,
bool allow_POST ,
2024-04-24 00:56:51 +02:00
JS : : SafeFunction < void ( ) > completion_steps )
2023-04-06 18:10:12 +03:00
{
// FIXME: 1. Assert: this is running in parallel.
// 2. Assert: if navigationParams is non-null, then navigationParams's response is non-null.
2023-09-21 13:47:19 -06:00
// NavigationParams' response field is NonnullGCPtr
if ( ! navigation_params . has < Empty > ( ) )
2024-04-21 19:57:33 +00:00
VERIFY ( navigation_params . has < JS : : NonnullGCPtr < NavigationParams > > ( ) ) ;
2023-04-06 18:10:12 +03:00
// 3. Let currentBrowsingContext be navigable's active browsing context.
[[maybe_unused]] auto current_browsing_context = active_browsing_context ( ) ;
// 4. Let documentResource be entry's document state's resource.
2024-03-27 15:59:12 +01:00
auto document_resource = entry - > document_state ( ) - > resource ( ) ;
2023-04-06 18:10:12 +03:00
// 5. If navigationParams is null, then:
2023-09-21 13:47:19 -06:00
if ( navigation_params . has < Empty > ( ) ) {
2023-04-06 18:10:12 +03:00
// 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.
if ( document_resource . has < String > ( ) ) {
2023-09-21 13:47:19 -06:00
navigation_params = TRY ( create_navigation_params_from_a_srcdoc_resource ( entry , this , target_snapshot_params , navigation_id ) ) ;
2023-04-06 18:10:12 +03:00
}
// 2. Otherwise, if both of the following are true:
// - entry's URL's scheme is a fetch scheme; and
2023-08-14 15:07:57 +02:00
// - documentResource is null, or allowPOST is true and documentResource's request body is not failure (FIXME: check if request body is not failure)
2024-03-27 15:59:12 +01:00
else if ( Fetch : : Infrastructure : : is_fetch_scheme ( entry - > url ( ) . scheme ( ) ) & & ( document_resource . has < Empty > ( ) | | allow_POST ) ) {
2023-09-21 13:47:19 -06:00
navigation_params = TRY ( create_navigation_params_by_fetching ( entry , this , source_snapshot_params , target_snapshot_params , csp_navigation_type , navigation_id ) ) ;
2023-04-06 18:10:12 +03:00
}
2023-09-21 13:47:19 -06:00
// 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:
2024-03-27 15:59:12 +01:00
else if ( ! Fetch : : Infrastructure : : is_fetch_scheme ( entry - > url ( ) . scheme ( ) ) ) {
2023-09-21 13:47:19 -06:00
// - id: navigationId
// - navigable: navigable
// - URL: entry's URL
// - target snapshot sandboxing flags: targetSnapshotParams's sandboxing flags
// - source snapshot has transient activation: sourceSnapshotParams's has transient activation
// - initiator origin: entry's document state's initiator origin
// FIXME: - navigation timing type: navTimingType
2024-04-21 19:57:33 +00:00
auto non_fetching_scheme_navigation_params = vm ( ) . heap ( ) . allocate_without_realm < NonFetchSchemeNavigationParams > ( ) ;
non_fetching_scheme_navigation_params - > id = navigation_id ;
non_fetching_scheme_navigation_params - > navigable = this ;
non_fetching_scheme_navigation_params - > url = entry - > url ( ) ;
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 ( ) ;
navigation_params = non_fetching_scheme_navigation_params ;
2023-04-06 18:10:12 +03:00
}
}
2023-09-05 23:36:20 +02:00
// NOTE: Not in the spec but queuing task on the next step will fail because active_window() does not exist for destroyed navigable.
if ( has_been_destroyed ( ) )
return { } ;
2023-04-06 18:10:12 +03:00
// 6. Queue a global task on the navigation and traversal task source, given navigable's active window, to run these steps:
2024-04-16 22:04:01 +02:00
queue_global_task ( Task : : Source : : NavigationAndTraversal , * active_window ( ) , JS : : create_heap_function ( heap ( ) , [ this , entry , navigation_params = move ( navigation_params ) , navigation_id , completion_steps = move ( completion_steps ) ] ( ) mutable {
2023-09-05 23:36:20 +02:00
// NOTE: This check is not in the spec but we should not continue navigation if navigable has been destroyed.
if ( has_been_destroyed ( ) )
return ;
2023-04-06 18:10:12 +03:00
// 1. If navigable's ongoing navigation no longer equals navigationId, then run completionSteps and return.
if ( navigation_id . has_value ( ) & & ( ! ongoing_navigation ( ) . has < String > ( ) | | ongoing_navigation ( ) . get < String > ( ) ! = * navigation_id ) ) {
completion_steps ( ) ;
return ;
}
// 2. Let failure be false.
auto failure = false ;
2023-09-21 13:47:19 -06:00
// 3. If navigationParams is a non-fetch scheme navigation params, then set entry's document state's document to the result of
// running attempt to create a non-fetch scheme document navigationParams
2024-04-21 19:57:33 +00:00
if ( navigation_params . has < JS : : NonnullGCPtr < NonFetchSchemeNavigationParams > > ( ) ) {
2023-09-21 13:47:19 -06:00
// FIXME: https://github.com/whatwg/html/issues/9767
// We probably are expected to skip to steps 13 and 14 and return after doing this
2024-04-21 19:57:33 +00:00
entry - > document_state ( ) - > set_document ( attempt_to_create_a_non_fetch_scheme_document ( navigation_params . get < JS : : NonnullGCPtr < NonFetchSchemeNavigationParams > > ( ) ) ) ;
2024-03-27 16:14:52 +01:00
if ( entry - > document ( ) ) {
2024-03-27 15:59:12 +01:00
entry - > document_state ( ) - > set_ever_populated ( true ) ;
2023-09-21 13:47:19 -06:00
}
completion_steps ( ) ;
return ;
}
2023-04-06 18:10:12 +03:00
// 4. Otherwise, if navigationParams is null, then set failure to true.
2024-06-18 15:26:12 +02:00
if ( navigation_params . has < Empty > ( ) | | navigation_params . has < NullWithError > ( ) ) {
2023-04-06 18:10:12 +03:00
failure = true ;
}
// FIXME: 5. Otherwise, if 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", then set failure to true.
// FIXME: 6. Otherwise, if 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, then set failure to true.
2023-09-21 13:47:19 -06:00
// FIXME: 7. Otherwise, if 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 set failure to true.
2023-04-06 18:10:12 +03:00
// 8. If failure is true, then:
if ( failure ) {
// 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.
2024-06-18 15:26:12 +02:00
auto error_message = navigation_params . has < NullWithError > ( ) ? navigation_params . get < NullWithError > ( ) : " Unknown error " sv ;
auto error_html = load_error_page ( entry - > url ( ) , error_message ) . release_value_but_fixme_should_propagate_errors ( ) ;
2024-03-27 15:59:12 +01:00
entry - > document_state ( ) - > set_document ( create_document_for_inline_content ( this , navigation_id , [ error_html ] ( auto & document ) {
2024-04-03 21:56:58 -04:00
auto parser = HTML : : HTMLParser : : create ( document , error_html , " utf-8 " sv ) ;
2024-03-18 16:22:27 +13:00
document . set_url ( URL : : URL ( " about:error " ) ) ;
2023-12-19 16:16:24 +00:00
parser - > run ( ) ;
} ) ) ;
2023-04-06 18:10:12 +03:00
// 2. Set entry's document state's document's salvageable to false.
2024-03-27 16:14:52 +01:00
entry - > document ( ) - > set_salvageable ( false ) ;
2023-04-06 18:10:12 +03:00
// FIXME: 3. If navigationParams is not null, then:
2023-09-21 13:47:19 -06:00
if ( ! navigation_params . has < Empty > ( ) ) {
// 1. FIXME: Run the environment discarding steps for navigationParams's reserved environment.
// 2. Invoke WebDriver BiDi navigation failed with currentBrowsingContext and a new WebDriver BiDi navigation status
// whose id is navigationId, status is "canceled", and url is navigationParams's response's URL.
2023-04-06 18:10:12 +03:00
}
}
// FIXME: 9. Otherwise, if navigationParams's response's status is 204 or 205, then:
2024-04-21 19:57:33 +00:00
else if ( navigation_params . get < JS : : NonnullGCPtr < NavigationParams > > ( ) - > response - > status ( ) = = 204 | | navigation_params . get < JS : : NonnullGCPtr < NavigationParams > > ( ) - > response - > status ( ) = = 205 ) {
2023-04-06 18:10:12 +03:00
// 1. Run completionSteps.
completion_steps ( ) ;
// 2. Return.
return ;
}
// FIXME: 10. Otherwise, if navigationParams's response has a `Content-Disposition`
// header specifying the attachment disposition type, then:
// 11. Otherwise:
else {
// 1. Let document be the result of loading a document given navigationParams, sourceSnapshotParams,
// and entry's document state's initiator origin.
2024-04-21 19:57:33 +00:00
auto document = load_document ( navigation_params . get < JS : : NonnullGCPtr < NavigationParams > > ( ) ) ;
2023-04-06 18:10:12 +03:00
// 2. If document is null, then run completionSteps and return.
if ( ! document ) {
completion_steps ( ) ;
return ;
}
// 3. Set entry's document state's document to document.
2024-03-27 15:59:12 +01:00
entry - > document_state ( ) - > set_document ( document . ptr ( ) ) ;
2023-04-06 18:10:12 +03:00
// 4. Set entry's document state's origin to document's origin.
2024-03-27 15:59:12 +01:00
entry - > document_state ( ) - > set_origin ( document - > origin ( ) ) ;
2023-04-06 18:10:12 +03:00
}
// FIXME: 12. If entry's document state's request referrer is "client", then set it to request's referrer.
2023-09-21 13:47:19 -06:00
// https://github.com/whatwg/html/issues/9767
// What is "request" here?
2023-04-06 18:10:12 +03:00
// 13. If entry's document state's document is not null, then set entry's document state's ever populated to true.
2024-03-27 16:14:52 +01:00
if ( entry - > document ( ) ) {
2024-03-27 15:59:12 +01:00
entry - > document_state ( ) - > set_ever_populated ( true ) ;
2023-04-06 18:10:12 +03:00
}
// 14. Run completionSteps.
completion_steps ( ) ;
2024-04-16 22:04:01 +02:00
} ) ) ;
2023-04-06 18:10:12 +03:00
return { } ;
}
2023-01-01 17:46:00 +01:00
// To navigate a navigable navigable to a URL url using a Document sourceDocument,
// with an optional POST resource, string, or null documentResource (default null),
// an optional response-or-null response (default null), an optional boolean exceptionsEnabled (default false),
2023-08-28 18:06:10 +02:00
// an optional NavigationHistoryBehavior historyHandling (default "auto"),
// an optional serialized state-or-null navigationAPIState (default null),
// an optional entry list or null formDataEntryList (default null),
// an optional referrer policy referrerPolicy (default the empty string),
// and an optional user navigation involvement userInvolvement (default "none"):
2023-01-01 17:46:00 +01:00
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigate
2023-10-10 16:05:38 +02:00
WebIDL : : ExceptionOr < void > Navigable : : navigate ( NavigateParams params )
2023-01-01 17:46:00 +01:00
{
2023-10-10 16:05:38 +02:00
auto const & url = params . url ;
auto source_document = params . source_document ;
auto const & document_resource = params . document_resource ;
auto response = params . response ;
auto exceptions_enabled = params . exceptions_enabled ;
auto history_handling = params . history_handling ;
auto const & navigation_api_state = params . navigation_api_state ;
auto const & form_data_entry_list = params . form_data_entry_list ;
auto referrer_policy = params . referrer_policy ;
auto user_involvement = params . user_involvement ;
2023-08-28 18:06:10 +02:00
auto & active_document = * this - > active_document ( ) ;
auto & realm = active_document . realm ( ) ;
auto & vm = this - > vm ( ) ;
// 1. Let cspNavigationType be "form-submission" if formDataEntryList is non-null; otherwise "other".
auto csp_navigation_type = form_data_entry_list . has_value ( ) ? CSPNavigationType : : FormSubmission : CSPNavigationType : : Other ;
// 2. Let sourceSnapshotParams be the result of snapshotting source snapshot params given sourceDocument.
2023-08-25 20:34:50 -06:00
auto source_snapshot_params = source_document - > snapshot_source_snapshot_params ( ) ;
2023-01-01 17:46:00 +01:00
2023-08-28 18:06:10 +02:00
// 3. Let initiatorOriginSnapshot be sourceDocument's origin.
2023-01-01 17:46:00 +01:00
auto initiator_origin_snapshot = source_document - > origin ( ) ;
2023-08-28 18:06:10 +02:00
// 4. Let initiatorBaseURLSnapshot be sourceDocument's document base URL.
auto initiator_base_url_snapshot = source_document - > base_url ( ) ;
// 5. If sourceDocument's node navigable is not allowed by sandboxing to navigate navigable given and sourceSnapshotParams, then:
if ( ! source_document - > navigable ( ) - > allowed_by_sandboxing_to_navigate ( * this , source_snapshot_params ) ) {
2023-01-01 17:46:00 +01:00
// 1. If exceptionsEnabled is true, then throw a "SecurityError" DOMException.
if ( exceptions_enabled ) {
2023-09-06 16:03:01 +12:00
return WebIDL : : SecurityError : : create ( realm , " Source document's node navigable is not allowed to navigate " _fly_string ) ;
2023-01-01 17:46:00 +01:00
}
// 2 Return.
return { } ;
}
2023-08-28 18:06:10 +02:00
// 6. Let navigationId be the result of generating a random UUID.
String navigation_id = TRY_OR_THROW_OOM ( vm , Crypto : : generate_random_uuid ( ) ) ;
2023-01-01 17:46:00 +01:00
2023-08-28 18:06:10 +02:00
// FIXME: 7. If the surrounding agent is equal to navigable's active document's relevant agent, then continue these steps.
2023-01-01 17:46:00 +01:00
// Otherwise, queue a global task on the navigation and traversal task source given navigable's active window to continue these steps.
2023-08-28 18:06:10 +02:00
// 8. If navigable's active document's unload counter is greater than 0,
// then invoke WebDriver BiDi navigation failed with a WebDriver BiDi navigation status whose id is navigationId,
// status is "canceled", and url is url, and return.
if ( active_document . unload_counter ( ) > 0 ) {
// FIXME: invoke WebDriver BiDi navigation failed with a WebDriver BiDi navigation status whose id is navigationId,
// status is "canceled", and url is url
return { } ;
2023-01-01 17:46:00 +01:00
}
2023-08-28 18:06:10 +02:00
// 9. If historyHandling is "auto", then:
if ( history_handling = = Bindings : : NavigationHistoryBehavior : : Auto ) {
// FIXME: Fix spec typo targetNavigable --> navigable
// 1. If url equals navigable's active document's URL,
// and initiatorOriginSnapshot is same origin with targetNavigable's active document's origin,
// then set historyHandling to "replace".
2024-02-11 20:15:39 +13:00
if ( url . equals ( active_document . url ( ) , URL : : ExcludeFragment : : Yes ) & & initiator_origin_snapshot . is_same_origin ( active_document . origin ( ) ) )
2023-08-28 18:06:10 +02:00
history_handling = Bindings : : NavigationHistoryBehavior : : Replace ;
// 2. Otherwise, set historyHandling to "push".
else
history_handling = Bindings : : NavigationHistoryBehavior : : Push ;
}
// 10. If the navigation must be a replace given url and navigable's active document, then set historyHandling to "replace".
if ( navigation_must_be_a_replace ( url , active_document ) )
history_handling = Bindings : : NavigationHistoryBehavior : : Replace ;
// 11. If all of the following are true:
2023-01-01 17:46:00 +01:00
// - documentResource is null;
// - response is null;
// - url equals navigable's active session history entry's URL with exclude fragments set to true; and
// - url's fragment is non-null
if ( document_resource . has < Empty > ( )
& & ! response
2024-03-27 15:59:12 +01:00
& & url . equals ( active_session_history_entry ( ) - > url ( ) , URL : : ExcludeFragment : : Yes )
2023-08-12 16:52:42 +12:00
& & url . fragment ( ) . has_value ( ) ) {
2023-09-27 22:59:57 -06:00
// 1. Navigate to a fragment given navigable, url, historyHandling, userInvolvement, navigationAPIState, and navigationId.
TRY ( navigate_to_a_fragment ( url , to_history_handling_behavior ( history_handling ) , user_involvement , navigation_api_state , navigation_id ) ) ;
2023-01-01 17:46:00 +01:00
// 2. Return.
return { } ;
}
2023-08-28 18:06:10 +02:00
// 12. If navigable's parent is non-null, then set navigable's is delaying load events to true.
if ( parent ( ) ! = nullptr )
2023-01-01 17:46:00 +01:00
set_delaying_load_events ( true ) ;
2023-08-28 18:06:10 +02:00
// 13. Let targetBrowsingContext be navigable's active browsing context.
2023-01-01 17:46:00 +01:00
[[maybe_unused]] auto target_browsing_context = active_browsing_context ( ) ;
2023-08-28 18:06:10 +02:00
// 14. Let targetSnapshotParams be the result of snapshotting target snapshot params given navigable.
2023-09-21 13:47:19 -06:00
auto target_snapshot_params = snapshot_target_snapshot_params ( ) ;
2023-01-01 17:46:00 +01:00
2023-12-11 16:16:16 +00:00
// FIXME: 15. Invoke WebDriver BiDi navigation started with targetBrowsingContext, and a new WebDriver BiDi navigation status whose id is navigationId, url is url, and status is "pending".
2023-01-01 17:46:00 +01:00
2023-08-28 18:06:10 +02:00
// 16. If navigable's ongoing navigation is "traversal", then:
2023-01-01 17:46:00 +01:00
if ( ongoing_navigation ( ) . has < Traversal > ( ) ) {
// FIXME: 1. Invoke WebDriver BiDi navigation failed with targetBrowsingContext and a new WebDriver BiDi navigation status whose id is navigationId, status is "canceled", and url is url.
// 2. Return.
return { } ;
}
2023-08-28 18:06:10 +02:00
// 17. Set navigable's ongoing navigation to navigationId.
2023-08-22 15:22:50 +02:00
set_ongoing_navigation ( navigation_id ) ;
2023-01-01 17:46:00 +01:00
2023-08-28 18:06:10 +02:00
// 18. If url's scheme is "javascript", then:
2023-01-01 17:46:00 +01:00
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.
2024-04-16 22:04:01 +02:00
queue_global_task ( Task : : Source : : NavigationAndTraversal , * active_window ( ) , JS : : create_heap_function ( heap ( ) , [ this , url , history_handling , initiator_origin_snapshot , csp_navigation_type , navigation_id ] {
2023-09-13 15:03:44 +02:00
( void ) navigate_to_a_javascript_url ( url , to_history_handling_behavior ( history_handling ) , initiator_origin_snapshot , csp_navigation_type , navigation_id ) ;
2024-04-16 22:04:01 +02:00
} ) ) ;
2023-01-01 17:46:00 +01:00
// 2. Return.
return { } ;
}
2023-08-28 18:06:10 +02:00
// 19. If all of the following are true:
// - userInvolvement is not "browser UI";
// - navigable's active document's origin is same origin-domain with sourceDocument's origin;
// - navigable's active document's is initial about:blank is false; and
// - url's scheme is a fetch scheme
// then:
2023-09-20 22:21:20 -06:00
if ( user_involvement ! = UserNavigationInvolvement : : BrowserUI & & active_document . origin ( ) . is_same_origin_domain ( source_document - > origin ( ) ) & & ! active_document . is_initial_about_blank ( ) & & Fetch : : Infrastructure : : is_fetch_scheme ( url . scheme ( ) ) ) {
2023-08-28 18:06:10 +02:00
// 1. Let navigation be navigable's active window's navigation API.
auto navigation = active_window ( ) - > navigation ( ) ;
// 2. Let entryListForFiring be formDataEntryList if documentResource is a POST resource; otherwise, null.
auto entry_list_for_firing = [ & ] ( ) - > Optional < Vector < XHR : : FormDataEntry > & > {
if ( document_resource . has < POSTResource > ( ) )
return form_data_entry_list ;
return { } ;
} ( ) ;
// 3. Let navigationAPIStateForFiring be navigationAPIState if navigationAPIState is not null;
// otherwise, StructuredSerializeForStorage(undefined).
auto navigation_api_state_for_firing = navigation_api_state . value_or ( MUST ( structured_serialize_for_storage ( vm , JS : : js_undefined ( ) ) ) ) ;
// FIXME: 4. Let continue be the result of firing a push/replace/reload navigate event at navigation
// with navigationType set to historyHandling, isSameDocument set to false, userInvolvement set to userInvolvement,
// formDataEntryList set to entryListForFiring, destinationURL set to url, and navigationAPIState set to navigationAPIStateForFiring.
( void ) navigation ;
( void ) entry_list_for_firing ;
( void ) navigation_api_state_for_firing ;
// FIXME: 5. If continue is false, then return.
}
2023-08-31 18:33:38 +02:00
if ( is_top_level_traversable ( ) ) {
2023-12-03 20:29:37 +13:00
active_browsing_context ( ) - > page ( ) . client ( ) . page_did_start_loading ( url , false ) ;
2023-08-31 18:33:38 +02:00
}
2023-08-28 18:06:10 +02:00
// 20. In parallel, run these steps:
2023-09-21 13:47:19 -06:00
Platform : : EventLoopPlugin : : the ( ) . deferred_invoke ( [ 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 ] {
2023-09-05 23:36:20 +02:00
// NOTE: Not in the spec but subsequent steps will fail because destroyed navigable does not have active document.
2024-04-05 02:41:00 +02:00
if ( has_been_destroyed ( ) ) {
set_delaying_load_events ( false ) ;
2023-09-05 23:36:20 +02:00
return ;
2024-04-05 02:41:00 +02:00
}
2023-09-05 23:36:20 +02:00
2023-01-01 17:46:00 +01:00
// FIXME: 1. Let unloadPromptCanceled be the result of checking if unloading is user-canceled for navigable's active document's inclusive descendant navigables.
// FIXME: 2. If unloadPromptCanceled is true, or navigable's ongoing navigation is no longer navigationId, then:
2023-04-22 22:28:29 +03:00
if ( ! ongoing_navigation ( ) . has < String > ( ) | | ongoing_navigation ( ) . get < String > ( ) ! = navigation_id ) {
// FIXME: 1. Invoke WebDriver BiDi navigation failed with targetBrowsingContext and a new WebDriver BiDi navigation status whose id is navigationId, status is "canceled", and url is url.
// 2. Abort these steps.
2024-04-05 02:41:00 +02:00
set_delaying_load_events ( false ) ;
2023-04-22 22:28:29 +03:00
return ;
}
2023-01-01 17:46:00 +01:00
2024-03-31 23:42:51 +02:00
// 3. Queue a global task on the navigation and traversal task source given navigable's active window to abort a document and its descendants given navigable's active document.
2024-04-16 22:04:01 +02:00
queue_global_task ( Task : : Source : : NavigationAndTraversal , * active_window ( ) , JS : : create_heap_function ( heap ( ) , [ this ] {
2023-08-28 18:06:10 +02:00
VERIFY ( this - > active_document ( ) ) ;
2024-03-31 23:42:51 +02:00
this - > active_document ( ) - > abort_a_document_and_its_descendants ( ) ;
2024-04-16 22:04:01 +02:00
} ) ) ;
2023-01-01 17:46:00 +01:00
// 4. Let documentState be a new document state with
// request referrer policy: referrerPolicy
// initiator origin: initiatorOriginSnapshot
2023-04-05 09:48:23 +03:00
// resource: documentResource
2023-01-01 17:46:00 +01:00
// navigable target name: navigable's target name
JS : : NonnullGCPtr < DocumentState > document_state = * heap ( ) . allocate_without_realm < DocumentState > ( ) ;
document_state - > set_request_referrer_policy ( referrer_policy ) ;
document_state - > set_initiator_origin ( initiator_origin_snapshot ) ;
2023-08-28 18:06:10 +02:00
document_state - > set_resource ( document_resource ) ;
2023-01-01 17:46:00 +01:00
document_state - > set_navigable_target_name ( target_name ( ) ) ;
2023-08-28 18:06:10 +02:00
// 5. If url matches about:blank or is about:srcdoc, then set documentState's origin to documentState's initiator origin.
2024-03-08 22:17:36 +01:00
if ( url_matches_about_blank ( url ) | | url_matches_about_srcdoc ( url ) ) {
// document_resource cannot have an Empty if the url is about:srcdoc since we rely on document_resource
// having a String to call create_navigation_params_from_a_srcdoc_resource
if ( url_matches_about_srcdoc ( url ) & & document_resource . has < Empty > ( ) ) {
document_state - > set_resource ( { String { } } ) ;
}
2023-08-28 18:06:10 +02:00
// 1. Set documentState's origin to initiatorOriginSnapshot.
2023-01-01 17:46:00 +01:00
document_state - > set_origin ( document_state - > initiator_origin ( ) ) ;
2023-08-28 18:06:10 +02:00
// 2. Set documentState's about base URL to initiatorBaseURLSnapshot.
document_state - > set_about_base_url ( initiator_base_url_snapshot ) ;
2023-01-01 17:46:00 +01:00
}
2023-08-28 18:06:10 +02:00
// 6. Let historyEntry be a new session history entry, with its URL set to url and its document state set to documentState.
2023-01-01 17:46:00 +01:00
JS : : NonnullGCPtr < SessionHistoryEntry > history_entry = * heap ( ) . allocate_without_realm < SessionHistoryEntry > ( ) ;
2024-03-27 15:59:12 +01:00
history_entry - > set_url ( url ) ;
history_entry - > set_document_state ( document_state ) ;
2023-01-01 17:46:00 +01:00
2023-09-27 22:59:57 -06:00
// 7. Let navigationParams be null.
2024-06-18 15:26:12 +02:00
NavigationParamsVariant navigation_params = Empty { } ;
2023-01-01 17:46:00 +01:00
2023-09-27 22:59:57 -06:00
// FIXME: 8. If response is non-null:
2023-01-01 17:46:00 +01:00
if ( response ) {
}
2023-04-05 09:48:23 +03:00
2023-09-27 22:59:57 -06:00
// 9. Attempt to populate the history entry's document
2023-04-05 09:48:23 +03:00
// for historyEntry, given navigable, "navigate", sourceSnapshotParams,
// targetSnapshotParams, navigationId, navigationParams, cspNavigationType, with allowPOST
// set to true and completionSteps set to the following step:
2024-04-21 19:57:33 +00:00
populate_session_history_entry_document ( history_entry , source_snapshot_params , target_snapshot_params , navigation_id , navigation_params , csp_navigation_type , true , [ this , history_entry , history_handling , navigation_id ] {
2023-09-21 13:47:19 -06:00
// 1. Append session history traversal steps to navigable's traversable to finalize a cross-document navigation given navigable, historyHandling, and historyEntry.
2023-09-05 23:36:20 +02:00
traversable_navigable ( ) - > append_session_history_traversal_steps ( [ this , history_entry , history_handling , navigation_id ] {
if ( this - > has_been_destroyed ( ) ) {
// NOTE: This check is not in the spec but we should not continue navigation if navigable has been destroyed.
2024-04-05 02:41:00 +02:00
set_delaying_load_events ( false ) ;
2023-09-05 23:36:20 +02:00
return ;
}
2023-09-09 22:58:26 +02:00
if ( this - > ongoing_navigation ( ) ! = navigation_id ) {
// NOTE: This check is not in the spec but we should not continue navigation if ongoing navigation id has changed.
2024-04-05 02:41:00 +02:00
set_delaying_load_events ( false ) ;
2023-09-09 22:58:26 +02:00
return ;
}
2023-08-28 18:06:10 +02:00
finalize_a_cross_document_navigation ( * this , to_history_handling_behavior ( history_handling ) , history_entry ) ;
2023-07-25 01:22:57 +02:00
} ) ;
2023-04-05 09:48:23 +03:00
} ) . release_value_but_fixme_should_propagate_errors ( ) ;
2023-01-01 17:46:00 +01:00
} ) ;
return { } ;
}
2023-09-27 22:59:57 -06:00
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigate-fragid
2024-03-18 16:22:27 +13:00
WebIDL : : ExceptionOr < void > Navigable : : navigate_to_a_fragment ( URL : : URL const & url , HistoryHandlingBehavior history_handling , UserNavigationInvolvement user_involvement , Optional < SerializationRecord > navigation_api_state , String navigation_id )
2023-01-01 17:46:00 +01:00
{
2023-09-27 22:59:57 -06:00
( void ) navigation_id ;
// 1. Let navigation be navigable's active window's navigation API.
auto navigation = active_window ( ) - > navigation ( ) ;
// 2. Let destinationNavigationAPIState be navigable's active session history entry's navigation API state.
// 3. If navigationAPIState is not null, then set destinationNavigationAPIState to navigationAPIState.
2024-03-27 15:59:12 +01:00
auto destination_navigation_api_state = navigation_api_state . has_value ( ) ? * navigation_api_state : active_session_history_entry ( ) - > navigation_api_state ( ) ;
2023-09-27 22:59:57 -06:00
// 4. Let continue be the result of firing a push/replace/reload navigate event at navigation with navigationType set to historyHandling, isSameDocument set to true,
// userInvolvement set to userInvolvement, and destinationURL set to url, and navigationAPIState set to destinationNavigationAPIState.
auto navigation_type = history_handling = = HistoryHandlingBehavior : : Push ? Bindings : : NavigationType : : Push : Bindings : : NavigationType : : Replace ;
bool const continue_ = navigation - > fire_a_push_replace_reload_navigate_event ( navigation_type , url , true , user_involvement , { } , destination_navigation_api_state ) ;
// 5. If continue is false, then return.
if ( ! continue_ )
return { } ;
2023-08-20 14:31:49 +02:00
// 6. Let historyEntry be a new session history entry, with
// URL: url
// document state: navigable's active session history entry's document state
// navigation API state: destinationNavigationAPIState
// scroll restoration mode: navigable's active session history entry's scroll restoration mode
JS : : NonnullGCPtr < SessionHistoryEntry > history_entry = heap ( ) . allocate_without_realm < SessionHistoryEntry > ( ) ;
2024-03-27 15:59:12 +01:00
history_entry - > set_url ( url ) ;
history_entry - > set_document_state ( active_session_history_entry ( ) - > document_state ( ) ) ;
history_entry - > set_navigation_api_state ( destination_navigation_api_state ) ;
history_entry - > set_scroll_restoration_mode ( active_session_history_entry ( ) - > scroll_restoration_mode ( ) ) ;
2023-08-20 14:31:49 +02:00
// 7. Let entryToReplace be navigable's active session history entry if historyHandling is "replace", otherwise null.
auto entry_to_replace = history_handling = = HistoryHandlingBehavior : : Replace ? active_session_history_entry ( ) : nullptr ;
2023-09-23 22:59:27 +02:00
// 8. Let history be navigable's active document's history object.
auto history = active_document ( ) - > history ( ) ;
2023-08-20 14:31:49 +02:00
2023-09-23 22:59:27 +02:00
// 9. Let scriptHistoryIndex be history's index.
auto script_history_index = history - > m_index ;
2023-08-20 14:31:49 +02:00
2023-09-23 22:59:27 +02:00
// 10. Let scriptHistoryLength be history's length.
auto script_history_length = history - > m_length ;
2023-08-20 14:31:49 +02:00
// 11. If historyHandling is "push", then:
if ( history_handling = = HistoryHandlingBehavior : : Push ) {
2023-09-27 22:59:57 -06:00
// 1. Set history's state to null.
history - > set_state ( JS : : js_null ( ) ) ;
2023-09-23 22:59:27 +02:00
// 2. Increment scriptHistoryIndex.
script_history_index + + ;
// 3. Set scriptHistoryLength to scriptHistoryIndex + 1.
script_history_length = script_history_index + 1 ;
2023-08-20 14:31:49 +02:00
}
// 12. Set navigable's active session history entry to historyEntry.
m_active_session_history_entry = history_entry ;
2023-09-23 22:59:27 +02:00
// 13. Update document for history step application given navigable's active document, historyEntry, true, scriptHistoryIndex, and scriptHistoryLength.
2023-09-27 22:59:57 -06:00
// AD HOC: Skip updating the navigation api entries twice here
2024-07-21 23:09:20 +01:00
active_document ( ) - > update_for_history_step_application ( * history_entry , true , script_history_length , script_history_index , navigation_type , { } , { } , false ) ;
2023-08-20 14:31:49 +02:00
2023-09-27 22:59:57 -06:00
// 14. Update the navigation API entries for a same-document navigation given navigation, historyEntry, and historyHandling.
navigation - > update_the_navigation_api_entries_for_a_same_document_navigation ( history_entry , navigation_type ) ;
2023-08-20 14:31:49 +02:00
// 15. Scroll to the fragment given navigable's active document.
// FIXME: Specification doesn't say when document url needs to update during fragment navigation
active_document ( ) - > set_url ( url ) ;
active_document ( ) - > scroll_to_the_fragment ( ) ;
// 16. Let traversable be navigable's traversable navigable.
auto traversable = traversable_navigable ( ) ;
// 17. Append the following session history synchronous navigation steps involving navigable to traversable:
2024-03-28 12:19:57 +01:00
traversable - > append_session_history_synchronous_navigation_steps ( * this , [ this , traversable , history_entry , entry_to_replace , navigation_id , history_handling ] {
2023-08-20 14:31:49 +02:00
// 1. Finalize a same-document navigation given traversable, navigable, historyEntry, and entryToReplace.
2024-03-28 12:19:57 +01:00
finalize_a_same_document_navigation ( * traversable , * this , history_entry , entry_to_replace , history_handling ) ;
2023-08-20 14:31:49 +02:00
// 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".
2023-11-03 19:18:18 -06:00
( void ) navigation_id ;
2023-08-20 14:31:49 +02:00
} ) ;
return { } ;
2023-01-01 17:46:00 +01:00
}
2023-09-13 15:03:44 +02:00
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#evaluate-a-javascript:-url
2024-03-18 16:22:27 +13:00
WebIDL : : ExceptionOr < JS : : GCPtr < DOM : : Document > > Navigable : : evaluate_javascript_url ( URL : : URL const & url , Origin const & new_document_origin , String navigation_id )
2023-01-01 17:46:00 +01:00
{
2023-09-13 15:03:44 +02:00
auto & vm = this - > vm ( ) ;
auto & realm = active_window ( ) - > realm ( ) ;
// 1. Let urlString be the result of running the URL serializer on url.
auto url_string = url . serialize ( ) ;
// 2. Let encodedScriptSource be the result of removing the leading "javascript:" from urlString.
auto encoded_script_source = url_string . substring_view ( 11 , url_string . length ( ) - 11 ) ;
2023-09-21 13:47:19 -06:00
// 3. Let scriptSource be the UTF-8 decoding of the percent-decoding of encodedScriptSource.
2024-02-11 20:15:39 +13:00
auto script_source = URL : : percent_decode ( encoded_script_source ) ;
2023-09-13 15:03:44 +02:00
// 4. Let settings be targetNavigable's active document's relevant settings object.
auto & settings = active_document ( ) - > relevant_settings_object ( ) ;
// 5. Let baseURL be settings's API base URL.
auto base_url = settings . api_base_url ( ) ;
// 6. Let script be the result of creating a classic script given scriptSource, settings, baseURL, and the default classic script fetch options.
2023-09-21 13:47:19 -06:00
auto script = HTML : : ClassicScript : : create ( " (javascript url) " , script_source , settings , base_url ) ;
2023-09-13 15:03:44 +02:00
// 7. Let evaluationStatus be the result of running the classic script script.
auto evaluation_status = script - > run ( ) ;
// 8. Let result be null.
String result ;
// 9. If evaluationStatus is a normal completion, and evaluationStatus.[[Value]] is a String, then set result to evaluationStatus.[[Value]].
if ( evaluation_status . type ( ) = = JS : : Completion : : Type : : Normal & & evaluation_status . value ( ) - > is_string ( ) ) {
result = evaluation_status . value ( ) - > as_string ( ) . utf8_string ( ) ;
} else {
// 10. Otherwise, return null.
return nullptr ;
}
// 11. Let response be a new response with
// URL: targetNavigable's active document's URL
// header list: «(`Content-Type`, `text/html;charset=utf-8`)»
// body: the UTF-8 encoding of result, as a body
auto response = Fetch : : Infrastructure : : Response : : create ( vm ) ;
response - > url_list ( ) . append ( active_document ( ) - > url ( ) ) ;
2024-04-26 13:24:20 -04:00
auto header = Fetch : : Infrastructure : : Header : : from_string_pair ( " Content-Type " sv , " text/html " sv ) ;
response - > header_list ( ) - > append ( move ( header ) ) ;
2023-09-13 15:03:44 +02:00
response - > set_body ( TRY ( Fetch : : Infrastructure : : byte_sequence_as_body ( realm , result . bytes ( ) ) ) ) ;
// 12. Let policyContainer be targetNavigable's active document's policy container.
auto const & policy_container = active_document ( ) - > policy_container ( ) ;
// FIXME: 13. Let finalSandboxFlags be policyContainer's CSP list's CSP-derived sandboxing flags.
2023-09-21 13:47:19 -06:00
auto final_sandbox_flags = SandboxingFlagSet { } ;
2023-09-13 15:03:44 +02:00
2023-09-21 13:47:19 -06:00
// 14. Let coop be targetNavigable's active document's cross-origin opener policy.
2023-09-13 15:03:44 +02:00
auto const & coop = active_document ( ) - > cross_origin_opener_policy ( ) ;
// 15. Let coopEnforcementResult be a new cross-origin opener policy enforcement result with
// url: url
// origin: newDocumentOrigin
// cross-origin opener policy: coop
CrossOriginOpenerPolicyEnforcementResult coop_enforcement_result {
. url = url ,
. origin = new_document_origin ,
. cross_origin_opener_policy = coop ,
} ;
// 16. Let navigationParams be a new navigation params, with
// id: navigationId
// navigable: targetNavigable
// request: null
// response: response
// fetch controller: null
// commit early hints: null
// COOP enforcement result: coopEnforcementResult
// reserved environment: null
// origin: newDocumentOrigin
// policy container: policyContainer
// final sandboxing flag set: finalSandboxFlags
// cross-origin opener policy: coop
2023-09-21 13:47:19 -06:00
// FIXME: navigation timing type: "navigate"
2023-09-13 15:03:44 +02:00
// about base URL: targetNavigable's active document's about base URL
2024-04-21 19:57:33 +00:00
auto navigation_params = vm . heap ( ) . allocate_without_realm < NavigationParams > ( ) ;
navigation_params - > id = navigation_id ;
navigation_params - > navigable = this ;
navigation_params - > request = { } ;
navigation_params - > response = response ;
navigation_params - > fetch_controller = nullptr ;
navigation_params - > commit_early_hints = nullptr ;
navigation_params - > coop_enforcement_result = move ( coop_enforcement_result ) ;
navigation_params - > reserved_environment = { } ;
navigation_params - > origin = new_document_origin ;
navigation_params - > policy_container = policy_container ;
navigation_params - > final_sandboxing_flag_set = final_sandbox_flags ;
navigation_params - > cross_origin_opener_policy = coop ;
navigation_params - > about_base_url = active_document ( ) - > about_base_url ( ) ;
2023-09-13 15:03:44 +02:00
// 17. Return the result of loading an HTML document given navigationParams.
2024-03-28 13:10:34 +01:00
return load_document ( navigation_params ) ;
2023-09-13 15:03:44 +02:00
}
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigate-to-a-javascript:-url
2024-03-18 16:22:27 +13:00
WebIDL : : ExceptionOr < void > Navigable : : navigate_to_a_javascript_url ( URL : : URL const & url , HistoryHandlingBehavior history_handling , Origin const & initiator_origin , CSPNavigationType csp_navigation_type , String navigation_id )
2023-09-13 15:03:44 +02:00
{
// 1. Assert: historyHandling is "replace".
VERIFY ( history_handling = = HistoryHandlingBehavior : : Replace ) ;
// 2. Set the ongoing navigation for targetNavigable to null.
set_ongoing_navigation ( { } ) ;
// 3. If initiatorOrigin is not same origin-domain with targetNavigable's active document's origin, then return.
if ( ! initiator_origin . is_same_origin_domain ( active_document ( ) - > origin ( ) ) )
return { } ;
// FIXME: 4. Let request be a new request whose URL is url.
// 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.
2023-01-01 17:46:00 +01:00
( void ) csp_navigation_type ;
2023-09-13 15:03:44 +02:00
// 6. Let newDocument be the result of evaluating a javascript: URL given targetNavigable, url, and initiatorOrigin.
auto new_document = TRY ( evaluate_javascript_url ( url , initiator_origin , navigation_id ) ) ;
// 7. If newDocument is null, then return.
if ( ! new_document ) {
// NOTE: In this case, some JavaScript code was executed, but no new Document was created, so we will not perform a navigation.
return { } ;
}
// 8. Assert: initiatorOrigin is newDocument's origin.
VERIFY ( initiator_origin = = new_document - > origin ( ) ) ;
// 9. Let entryToReplace be targetNavigable's active session history entry.
auto entry_to_replace = active_session_history_entry ( ) ;
// 10. Let oldDocState be entryToReplace's document state.
2024-03-27 15:59:12 +01:00
auto old_doc_state = entry_to_replace - > document_state ( ) ;
2023-09-13 15:03:44 +02:00
// 11. Let documentState be a new document state with
// document: newDocument
// history policy container: a clone of the oldDocState's history policy container if it is non-null; null otherwise
// request referrer: oldDocState's request referrer
// request referrer policy: oldDocState's request referrer policy
// initiator origin: initiatorOrigin
// origin: initiatorOrigin
// about base URL: oldDocState's about base URL
// resource: null
// ever populated: true
// navigable target name: oldDocState's navigable target name
JS : : NonnullGCPtr < DocumentState > document_state = * heap ( ) . allocate_without_realm < DocumentState > ( ) ;
document_state - > set_document ( new_document ) ;
document_state - > set_history_policy_container ( old_doc_state - > history_policy_container ( ) ) ;
document_state - > set_request_referrer ( old_doc_state - > request_referrer ( ) ) ;
document_state - > set_request_referrer_policy ( old_doc_state - > request_referrer_policy ( ) ) ;
document_state - > set_initiator_origin ( initiator_origin ) ;
document_state - > set_origin ( initiator_origin ) ;
2023-09-22 17:31:25 -06:00
document_state - > set_about_base_url ( old_doc_state - > about_base_url ( ) ) ;
2023-09-13 15:03:44 +02:00
document_state - > set_ever_populated ( true ) ;
document_state - > set_navigable_target_name ( old_doc_state - > navigable_target_name ( ) ) ;
// 12. Let historyEntry be a new session history entry, with
// URL: entryToReplace's URL
// document state: documentState
JS : : NonnullGCPtr < SessionHistoryEntry > history_entry = * heap ( ) . allocate_without_realm < SessionHistoryEntry > ( ) ;
2024-03-27 15:59:12 +01:00
history_entry - > set_url ( entry_to_replace - > url ( ) ) ;
history_entry - > set_document_state ( document_state ) ;
2023-09-13 15:03:44 +02:00
// 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 ( [ this , history_entry , history_handling , navigation_id ] {
finalize_a_cross_document_navigation ( * this , history_handling , history_entry ) ;
} ) ;
return { } ;
2023-01-01 17:46:00 +01:00
}
2023-04-07 00:08:39 +03:00
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#reload
void Navigable : : reload ( )
{
// 1. Set navigable's active session history entry's document state's reload pending to true.
2024-03-27 15:59:12 +01:00
active_session_history_entry ( ) - > document_state ( ) - > set_reload_pending ( true ) ;
2023-04-07 00:08:39 +03:00
// 2. Let traversable be navigable's traversable navigable.
auto traversable = traversable_navigable ( ) ;
2023-07-25 01:22:57 +02:00
// 3. Append the following session history traversal steps to traversable:
traversable - > append_session_history_traversal_steps ( [ traversable ] {
2023-09-03 22:20:32 +02:00
// 1. Apply the reload history step to traversable.
traversable - > apply_the_reload_history_step ( ) ;
2023-07-25 01:22:57 +02:00
} ) ;
2023-04-07 00:08:39 +03:00
}
2023-08-24 16:18:40 -06:00
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#the-navigation-must-be-a-replace
2024-03-18 16:22:27 +13:00
bool navigation_must_be_a_replace ( URL : : URL const & url , DOM : : Document const & document )
2023-08-24 16:18:40 -06:00
{
return url . scheme ( ) = = " javascript " sv | | document . is_initial_about_blank ( ) ;
}
2023-08-28 18:06:10 +02:00
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#allowed-to-navigate
bool Navigable : : allowed_by_sandboxing_to_navigate ( Navigable const & target , SourceSnapshotParams const & source_snapshot_params )
{
auto & source = * this ;
auto is_ancestor_of = [ ] ( Navigable const & a , Navigable const & b ) {
for ( auto parent = b . parent ( ) ; parent ; parent = parent - > parent ( ) ) {
if ( parent . ptr ( ) = = & a )
return true ;
}
return false ;
} ;
// A navigable source is allowed by sandboxing to navigate a second navigable target,
// given a source snapshot params sourceSnapshotParams, if the following steps return true:
// 1. If source is target, then return true.
if ( & source = = & target )
return true ;
// 2. If source is an ancestor of target, then return true.
if ( is_ancestor_of ( source , target ) )
return true ;
// 3. If target is an ancestor of source, then:
if ( is_ancestor_of ( target , source ) ) {
// 1. If target is not a top-level traversable, then return true.
if ( ! target . is_top_level_traversable ( ) )
return true ;
// 2. If sourceSnapshotParams's has transient activation is true, and sourceSnapshotParams's sandboxing flags's
// sandboxed top-level navigation with user activation browsing context flag is set, then return false.
if ( source_snapshot_params . has_transient_activation & & has_flag ( source_snapshot_params . sandboxing_flags , SandboxingFlagSet : : SandboxedTopLevelNavigationWithUserActivation ) )
return false ;
// 3. If sourceSnapshotParams's has transient activation is false, and sourceSnapshotParams's sandboxing flags's
// sandboxed top-level navigation without user activation browsing context flag is set, then return false.
if ( ! source_snapshot_params . has_transient_activation & & has_flag ( source_snapshot_params . sandboxing_flags , SandboxingFlagSet : : SandboxedTopLevelNavigationWithoutUserActivation ) )
return false ;
// 4. Return true.
return true ;
}
// 4. If target is a top-level traversable:
if ( target . is_top_level_traversable ( ) ) {
// FIXME: 1. If source is the one permitted sandboxed navigator of target, then return true.
// 2. If sourceSnapshotParams's sandboxing flags's sandboxed navigation browsing context flag is set, then return false.
if ( has_flag ( source_snapshot_params . sandboxing_flags , SandboxingFlagSet : : SandboxedNavigation ) )
return false ;
// 3. Return true.
return true ;
}
// 5. If sourceSnapshotParams's sandboxing flags's sandboxed navigation browsing context flag is set, then return false.
// 6. Return true.
return ! has_flag ( source_snapshot_params . sandboxing_flags , SandboxingFlagSet : : SandboxedNavigation ) ;
}
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#snapshotting-target-snapshot-params
TargetSnapshotParams Navigable : : snapshot_target_snapshot_params ( )
{
// To snapshot target snapshot params given a navigable targetNavigable, return a new target snapshot params
// with sandboxing flags set to the result of determining the creation sandboxing flags given targetNavigable's
// active browsing context and targetNavigable's container.
return { determine_the_creation_sandboxing_flags ( * active_browsing_context ( ) , container ( ) ) } ;
}
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#finalize-a-cross-document-navigation
void finalize_a_cross_document_navigation ( JS : : NonnullGCPtr < Navigable > navigable , HistoryHandlingBehavior history_handling , JS : : NonnullGCPtr < SessionHistoryEntry > history_entry )
{
2023-09-05 23:36:20 +02:00
// NOTE: This is not in the spec but we should not navigate destroyed navigable.
if ( navigable - > has_been_destroyed ( ) )
return ;
2023-08-28 18:06:10 +02:00
// 1. FIXME: Assert: this is running on navigable's traversable navigable's session history traversal queue.
// 2. Set navigable's is delaying load events to false.
navigable - > set_delaying_load_events ( false ) ;
// 3. If historyEntry's document is null, then return.
2024-03-27 16:14:52 +01:00
if ( ! history_entry - > document ( ) )
2023-08-28 18:06:10 +02:00
return ;
// 4. If all of the following are true:
// - navigable's parent is null;
// - historyEntry's document's browsing context is not an auxiliary browsing context whose opener browsing context is non-null; and
// - historyEntry's document's origin is not navigable's active document's origin
// then set historyEntry's document state's navigable target name to the empty string.
2024-03-27 16:14:52 +01:00
if ( navigable - > parent ( ) = = nullptr & & history_entry - > document ( ) - > browsing_context ( ) - > opener_browsing_context ( ) ! = nullptr & & history_entry - > document ( ) - > origin ( ) ! = navigable - > active_document ( ) - > origin ( ) )
2024-03-27 15:59:12 +01:00
history_entry - > document_state ( ) - > set_navigable_target_name ( String { } ) ;
2023-08-28 18:06:10 +02:00
// 5. Let entryToReplace be navigable's active session history entry if historyHandling is "replace", otherwise null.
auto entry_to_replace = history_handling = = HistoryHandlingBehavior : : Replace ? navigable - > active_session_history_entry ( ) : nullptr ;
// 6. Let traversable be navigable's traversable navigable.
auto traversable = navigable - > traversable_navigable ( ) ;
// 7. Let targetStep be null.
int target_step ;
// 8. Let targetEntries be the result of getting session history entries for navigable.
auto & target_entries = navigable - > get_session_history_entries ( ) ;
// 9. If entryToReplace is null, then:
if ( entry_to_replace = = nullptr ) {
// 1. Clear the forward session history of traversable.
traversable - > clear_the_forward_session_history ( ) ;
// 2. Set targetStep to traversable's current session history step + 1.
target_step = traversable - > current_session_history_step ( ) + 1 ;
// 3. Set historyEntry's step to targetStep.
2024-03-27 15:59:12 +01:00
history_entry - > set_step ( target_step ) ;
2023-08-28 18:06:10 +02:00
// 4. Append historyEntry to targetEntries.
target_entries . append ( history_entry ) ;
} else {
// 1. Replace entryToReplace with historyEntry in targetEntries.
* ( target_entries . find ( * entry_to_replace ) ) = history_entry ;
// 2. Set historyEntry's step to entryToReplace's step.
2024-03-27 15:59:12 +01:00
history_entry - > set_step ( entry_to_replace - > step ( ) ) ;
2023-08-28 18:06:10 +02:00
// 3. If historyEntry's document state's origin is same origin with entryToReplace's document state's origin,
// then set historyEntry's navigation API key to entryToReplace's navigation API key.
2024-03-27 15:59:12 +01:00
if ( history_entry - > document_state ( ) - > origin ( ) . has_value ( ) & & entry_to_replace - > document_state ( ) - > origin ( ) . has_value ( ) & & history_entry - > document_state ( ) - > origin ( ) - > is_same_origin ( * entry_to_replace - > document_state ( ) - > origin ( ) ) ) {
history_entry - > set_navigation_api_key ( entry_to_replace - > navigation_api_key ( ) ) ;
2023-08-28 18:06:10 +02:00
}
// 4. Set targetStep to traversable's current session history step.
target_step = traversable - > current_session_history_step ( ) ;
}
2023-09-03 22:20:32 +02:00
// 10. Apply the push/replace history step targetStep to traversable.
2024-04-05 16:02:52 +02:00
traversable - > apply_the_push_or_replace_history_step ( target_step , history_handling , TraversableNavigable : : SynchronousNavigation : : No ) ;
2023-08-28 18:06:10 +02:00
}
2023-08-20 14:42:55 +02:00
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#url-and-history-update-steps
2024-03-18 16:22:27 +13:00
void perform_url_and_history_update_steps ( DOM : : Document & document , URL : : URL new_url , Optional < SerializationRecord > serialized_data , HistoryHandlingBehavior history_handling )
2023-08-20 14:42:55 +02:00
{
// 1. Let navigable be document's node navigable.
auto navigable = document . navigable ( ) ;
// 2. Let activeEntry be navigable's active session history entry.
auto active_entry = navigable - > active_session_history_entry ( ) ;
2023-09-27 22:59:57 -06:00
// FIXME: Spec should be updated to say "classic history api state" instead of serialized state
2023-08-20 14:42:55 +02:00
// 3. Let newEntry be a new session history entry, with
// URL: newURL
// serialized state: if serializedData is not null, serializedData; otherwise activeEntry's classic history API state
// document state: activeEntry's document state
// scroll restoration mode: activeEntry's scroll restoration mode
2023-09-27 22:59:57 -06:00
// FIXME: persisted user state: activeEntry's persisted user state
2023-08-20 14:42:55 +02:00
JS : : NonnullGCPtr < SessionHistoryEntry > new_entry = document . heap ( ) . allocate_without_realm < SessionHistoryEntry > ( ) ;
2024-03-27 15:59:12 +01:00
new_entry - > set_url ( new_url ) ;
new_entry - > set_classic_history_api_state ( serialized_data . value_or ( active_entry - > classic_history_api_state ( ) ) ) ;
new_entry - > set_document_state ( active_entry - > document_state ( ) ) ;
new_entry - > set_scroll_restoration_mode ( active_entry - > scroll_restoration_mode ( ) ) ;
2023-08-20 14:42:55 +02:00
// 4. If document's is initial about:blank is true, then set historyHandling to "replace".
if ( document . is_initial_about_blank ( ) ) {
history_handling = HistoryHandlingBehavior : : Replace ;
}
// 5. Let entryToReplace be activeEntry if historyHandling is "replace", otherwise null.
auto entry_to_replace = history_handling = = HistoryHandlingBehavior : : Replace ? active_entry : nullptr ;
// 6. If historyHandling is "push", then:
if ( history_handling = = HistoryHandlingBehavior : : Push ) {
2023-09-23 22:57:04 +02:00
// 1. Increment document's history object's index.
document . history ( ) - > m_index + + ;
// 2. Set document's history object's length to its index + 1.
document . history ( ) - > m_length = document . history ( ) - > m_index + 1 ;
2023-08-20 14:42:55 +02:00
}
2023-09-27 22:59:57 -06:00
// If serializedData is not null, then restore the history object state given document and newEntry.
if ( serialized_data . has_value ( ) )
document . restore_the_history_object_state ( new_entry ) ;
2023-08-20 14:42:55 +02:00
// 8. Set document's URL to newURL.
document . set_url ( new_url ) ;
2023-09-27 22:59:57 -06:00
// 9. Set document's latest entry to newEntry.
document . set_latest_entry ( new_entry ) ;
2023-08-20 14:42:55 +02:00
// 10. Set navigable's active session history entry to newEntry.
navigable - > set_active_session_history_entry ( new_entry ) ;
2023-09-27 22:59:57 -06:00
// 11. Update the navigation API entries for a same-document navigation given document's relevant global object's navigation API, newEntry, and historyHandling.
auto & relevant_global_object = verify_cast < Window > ( HTML : : relevant_global_object ( document ) ) ;
auto navigation_type = history_handling = = HistoryHandlingBehavior : : Push ? Bindings : : NavigationType : : Push : Bindings : : NavigationType : : Replace ;
relevant_global_object . navigation ( ) - > update_the_navigation_api_entries_for_a_same_document_navigation ( new_entry , navigation_type ) ;
2023-08-20 14:42:55 +02:00
// 12. Let traversable be navigable's traversable navigable.
auto traversable = navigable - > traversable_navigable ( ) ;
// 13. Append the following session history synchronous navigation steps involving navigable to traversable:
2024-03-28 12:19:57 +01:00
traversable - > append_session_history_synchronous_navigation_steps ( * navigable , [ traversable , navigable , new_entry , entry_to_replace , history_handling ] {
2023-08-20 14:42:55 +02:00
// 1. Finalize a same-document navigation given traversable, navigable, newEntry, and entryToReplace.
2024-03-28 12:19:57 +01:00
finalize_a_same_document_navigation ( * traversable , * navigable , new_entry , entry_to_replace , history_handling ) ;
2023-08-20 14:42:55 +02:00
} ) ;
}
2023-08-22 16:00:42 +02:00
void Navigable : : scroll_offset_did_change ( )
{
// https://w3c.github.io/csswg-drafts/cssom-view-1/#scrolling-events
// Whenever a viewport gets scrolled (whether in response to user interaction or by an API), the user agent must run these steps:
// 1. Let doc be the viewport’ s associated Document.
auto doc = active_document ( ) ;
VERIFY ( doc ) ;
// 2. If doc is already in doc’ s pending scroll event targets, abort these steps.
for ( auto & target : doc - > pending_scroll_event_targets ( ) ) {
if ( target . ptr ( ) = = doc )
return ;
}
// 3. Append doc to doc’ s pending scroll event targets.
doc - > pending_scroll_event_targets ( ) . append ( * doc ) ;
}
CSSPixelRect Navigable : : to_top_level_rect ( CSSPixelRect const & a_rect )
{
auto rect = a_rect ;
rect . set_location ( to_top_level_position ( a_rect . location ( ) ) ) ;
return rect ;
}
CSSPixelPoint Navigable : : to_top_level_position ( CSSPixelPoint a_position )
{
auto position = a_position ;
for ( auto ancestor = parent ( ) ; ancestor ; ancestor = ancestor - > parent ( ) ) {
if ( is < TraversableNavigable > ( * ancestor ) )
break ;
if ( ! ancestor - > container ( ) )
return { } ;
2024-01-14 11:14:20 +01:00
if ( ! ancestor - > container ( ) - > paintable ( ) )
2023-08-22 16:00:42 +02:00
return { } ;
2024-01-14 11:14:20 +01:00
position . translate_by ( ancestor - > container ( ) - > paintable ( ) - > box_type_agnostic_position ( ) ) ;
2023-08-22 16:00:42 +02:00
}
return position ;
}
2024-06-03 17:53:55 +03:00
void Navigable : : set_viewport_size ( CSSPixelSize size )
2023-08-22 16:00:42 +02:00
{
if ( m_size = = size )
return ;
2024-06-03 17:53:55 +03:00
m_size = size ;
2023-08-22 16:00:42 +02:00
if ( auto document = active_document ( ) ) {
2024-06-03 17:53:55 +03:00
// NOTE: Resizing the viewport changes the reference value for viewport-relative CSS lengths.
2023-08-22 16:00:42 +02:00
document - > invalidate_style ( ) ;
document - > set_needs_layout ( ) ;
}
2024-08-07 22:43:06 +03:00
set_needs_display ( ) ;
2023-08-22 16:00:42 +02:00
if ( auto document = active_document ( ) ) {
document - > inform_all_viewport_clients_about_the_current_viewport_rect ( ) ;
2024-06-03 17:53:55 +03:00
// Schedule the HTML event loop to ensure that a `resize` event gets fired.
HTML : : main_thread_event_loop ( ) . schedule ( ) ;
}
}
void Navigable : : perform_scroll_of_viewport ( CSSPixelPoint new_position )
{
if ( m_viewport_scroll_offset ! = new_position ) {
m_viewport_scroll_offset = new_position ;
scroll_offset_did_change ( ) ;
2024-08-07 22:43:06 +03:00
set_needs_display ( ) ;
2024-06-03 17:53:55 +03:00
2024-08-06 15:32:31 +03:00
if ( auto document = active_document ( ) ) {
document - > set_needs_to_refresh_scroll_state ( true ) ;
2024-06-03 17:53:55 +03:00
document - > inform_all_viewport_clients_about_the_current_viewport_rect ( ) ;
2024-08-06 15:32:31 +03:00
}
2023-08-22 16:00:42 +02:00
}
// Schedule the HTML event loop to ensure that a `resize` event gets fired.
HTML : : main_thread_event_loop ( ) . schedule ( ) ;
}
void Navigable : : set_needs_display ( )
{
set_needs_display ( viewport_rect ( ) ) ;
}
2024-02-24 07:38:17 +01:00
void Navigable : : set_needs_display ( CSSPixelRect const & )
2023-08-22 16:00:42 +02:00
{
2023-12-26 22:38:44 +01:00
// FIXME: Ignore updates outside the visible viewport rect.
// This requires accounting for fixed-position elements in the input rect, which we don't do yet.
2023-08-22 16:00:42 +02:00
2024-02-23 22:10:35 +01:00
m_needs_repaint = true ;
2023-08-22 16:00:42 +02:00
if ( is < TraversableNavigable > ( * this ) ) {
2024-02-24 07:38:17 +01:00
// Schedule the main thread event loop, which will, in turn, schedule a repaint.
Web : : HTML : : main_thread_event_loop ( ) . schedule ( ) ;
2023-08-22 16:00:42 +02:00
return ;
}
2024-01-14 13:46:52 +01:00
if ( container ( ) & & container ( ) - > paintable ( ) )
container ( ) - > paintable ( ) - > set_needs_display ( ) ;
2023-08-22 16:00:42 +02:00
}
2023-09-20 08:09:59 +02:00
// https://html.spec.whatwg.org/#rendering-opportunity
bool Navigable : : has_a_rendering_opportunity ( ) const
{
// A navigable has a rendering opportunity if the user agent is currently able to present
// the contents of the navigable to the user,
// accounting for hardware refresh rate constraints and user agent throttling for performance reasons,
// but considering content presentable even if it's outside the viewport.
// A navigable has no rendering opportunities if its active document is render-blocked
// or if it is suppressed for view transitions;
// otherwise, rendering opportunities are determined based on hardware constraints
// such as display refresh rates and other factors such as page performance
// or whether the document's visibility state is "visible".
// Rendering opportunities typically occur at regular intervals.
2024-03-18 20:11:06 +01:00
// FIXME: Return `false` here if we're an inactive browser tab.
auto browsing_context = const_cast < Navigable * > ( this ) - > active_browsing_context ( ) ;
if ( ! browsing_context )
return false ;
return browsing_context - > page ( ) . client ( ) . is_ready_to_paint ( ) ;
2023-09-20 08:09:59 +02:00
}
2023-09-20 23:21:16 -06:00
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#inform-the-navigation-api-about-aborting-navigation
void Navigable : : inform_the_navigation_api_about_aborting_navigation ( )
{
// FIXME: 1. If this algorithm is running on navigable's active window's relevant agent's event loop, then continue on to the following steps.
// Otherwise, queue a global task on the navigation and traversal task source given navigable's active window to run the following steps.
2024-04-16 22:04:01 +02:00
queue_global_task ( Task : : Source : : NavigationAndTraversal , * active_window ( ) , JS : : create_heap_function ( heap ( ) , [ this ] {
2023-09-20 23:21:16 -06:00
// 2. Let navigation be navigable's active window's navigation API.
auto navigation = active_window ( ) - > navigation ( ) ;
// 3. If navigation's ongoing navigate event is null, then return.
if ( navigation - > ongoing_navigate_event ( ) = = nullptr )
return ;
// 4. Abort the ongoing navigation given navigation.
navigation - > abort_the_ongoing_navigation ( ) ;
2024-04-16 22:04:01 +02:00
} ) ) ;
2023-09-20 23:21:16 -06:00
}
2024-08-08 14:17:54 +03:00
RefPtr < Painting : : DisplayList > Navigable : : record_display_list ( PaintConfig config )
2024-01-15 11:28:21 +01:00
{
auto document = active_document ( ) ;
if ( ! document )
2024-08-08 14:17:54 +03:00
return { } ;
auto display_list = Painting : : DisplayList : : create ( ) ;
Painting : : DisplayListRecorder display_list_recorder ( display_list ) ;
if ( config . canvas_fill_rect . has_value ( ) ) {
display_list_recorder . fill_rect ( config . canvas_fill_rect . value ( ) , CSS : : SystemColor : : canvas ( ) ) ;
}
2024-01-15 11:28:21 +01:00
auto const & page = traversable_navigable ( ) - > page ( ) ;
auto viewport_rect = page . css_to_device_rect ( this - > viewport_rect ( ) ) ;
Gfx : : IntRect bitmap_rect { { } , viewport_rect . size ( ) . to_type < int > ( ) } ;
auto background_color = document - > background_color ( ) ;
2024-06-23 18:40:10 +02:00
display_list_recorder . fill_rect ( bitmap_rect , background_color ) ;
2024-05-28 15:51:53 +02:00
if ( ! document - > paintable ( ) ) {
VERIFY_NOT_REACHED ( ) ;
}
2024-01-15 11:28:21 +01:00
2024-06-23 18:40:10 +02:00
Web : : PaintContext context ( display_list_recorder , page . palette ( ) , page . client ( ) . device_pixels_per_css_pixel ( ) ) ;
2024-01-15 11:28:21 +01:00
context . set_device_viewport_rect ( viewport_rect ) ;
context . set_should_show_line_box_borders ( config . should_show_line_box_borders ) ;
context . set_should_paint_overlay ( config . paint_overlay ) ;
context . set_has_focus ( config . has_focus ) ;
2024-02-08 17:23:14 +01:00
document - > update_paint_and_hit_testing_properties_if_needed ( ) ;
2024-02-02 12:04:16 +01:00
2024-02-08 17:30:07 +01:00
auto & viewport_paintable = * document - > paintable ( ) ;
2024-08-08 14:17:54 +03:00
viewport_paintable . refresh_scroll_state ( ) ;
2024-01-15 11:28:21 +01:00
2024-02-08 17:30:07 +01:00
viewport_paintable . paint_all_phases ( context ) ;
2024-01-15 11:28:21 +01:00
2024-08-08 14:17:54 +03:00
Vector < Gfx : : IntPoint > scroll_offsets_by_frame_id ;
scroll_offsets_by_frame_id . resize ( viewport_paintable . scroll_state . size ( ) ) ;
for ( auto [ _ , scrollable_frame ] : viewport_paintable . scroll_state ) {
auto scroll_offset = context . rounded_device_point ( scrollable_frame - > offset ) . to_type < int > ( ) ;
scroll_offsets_by_frame_id [ scrollable_frame - > id ] = scroll_offset ;
2024-01-15 11:28:21 +01:00
}
2024-08-08 14:17:54 +03:00
display_list_recorder . display_list ( ) . apply_scroll_offsets ( scroll_offsets_by_frame_id ) ;
2024-02-23 22:10:35 +01:00
m_needs_repaint = false ;
2024-08-08 14:17:54 +03:00
return display_list ;
2024-01-15 11:28:21 +01:00
}
2024-01-18 12:52:13 -07:00
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#event-uni
UserNavigationInvolvement user_navigation_involvement ( DOM : : Event const & event )
{
// For convenience at certain call sites, the user navigation involvement for an Event event is defined as follows:
// 1. Assert: this algorithm is being called as part of an activation behavior definition.
// 2. Assert: event's type is "click".
VERIFY ( event . type ( ) = = " click " _fly_string ) ;
// 3. If event's isTrusted is initialized to true, then return "activation".
// 4. Return "none".
return event . is_trusted ( ) ? UserNavigationInvolvement : : Activation : UserNavigationInvolvement : : None ;
}
2024-04-26 16:59:04 +02:00
bool Navigable : : is_focused ( ) const
{
return & m_page - > focused_navigable ( ) = = this ;
}
static String visible_text_in_range ( DOM : : Range const & range )
{
// NOTE: This is an adaption of Range stringification, but we skip over DOM nodes that don't have a corresponding layout node.
StringBuilder builder ;
if ( range . start_container ( ) = = range . end_container ( ) & & is < DOM : : Text > ( * range . start_container ( ) ) ) {
if ( ! range . start_container ( ) - > layout_node ( ) )
return String { } ;
return MUST ( static_cast < DOM : : Text const & > ( * range . start_container ( ) ) . data ( ) . substring_from_byte_offset ( range . start_offset ( ) , range . end_offset ( ) - range . start_offset ( ) ) ) ;
}
if ( is < DOM : : Text > ( * range . start_container ( ) ) & & range . start_container ( ) - > layout_node ( ) )
builder . append ( static_cast < DOM : : Text const & > ( * range . start_container ( ) ) . data ( ) . bytes_as_string_view ( ) . substring_view ( range . start_offset ( ) ) ) ;
for ( DOM : : Node const * node = range . start_container ( ) ; node ! = range . end_container ( ) - > next_sibling ( ) ; node = node - > next_in_pre_order ( ) ) {
if ( is < DOM : : Text > ( * node ) & & range . contains_node ( * node ) & & node - > layout_node ( ) )
builder . append ( static_cast < DOM : : Text const & > ( * node ) . data ( ) ) ;
}
if ( is < DOM : : Text > ( * range . end_container ( ) ) & & range . end_container ( ) - > layout_node ( ) )
builder . append ( static_cast < DOM : : Text const & > ( * range . end_container ( ) ) . data ( ) . bytes_as_string_view ( ) . substring_view ( 0 , range . end_offset ( ) ) ) ;
return MUST ( builder . to_string ( ) ) ;
}
String Navigable : : selected_text ( ) const
{
auto document = const_cast < Navigable * > ( this ) - > active_document ( ) ;
if ( ! document )
return String { } ;
auto selection = const_cast < DOM : : Document & > ( * document ) . get_selection ( ) ;
auto range = selection - > range ( ) ;
if ( ! range )
return String { } ;
return visible_text_in_range ( * range ) ;
}
void Navigable : : select_all ( )
{
auto document = active_document ( ) ;
if ( ! document )
return ;
auto * body = document - > body ( ) ;
if ( ! body )
return ;
auto selection = document - > get_selection ( ) ;
if ( ! selection )
return ;
( void ) selection - > select_all_children ( * document - > body ( ) ) ;
}
void Navigable : : paste ( String const & text )
{
auto document = active_document ( ) ;
if ( ! document )
return ;
m_event_handler . handle_paste ( text ) ;
}
2022-12-12 11:46:54 +01:00
}