2020-01-18 09:38:21 +01:00
/*
2022-03-16 12:33:51 +01:00
* Copyright ( c ) 2018 - 2022 , Andreas Kling < kling @ serenityos . org >
2020-01-18 09:38:21 +01:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 09:38:21 +01:00
*/
2022-08-05 10:55:47 +02:00
# include <LibWeb/Bindings/MainThreadVM.h>
2020-03-07 10:32:51 +01:00
# include <LibWeb/DOM/Document.h>
2023-03-29 23:46:18 +01:00
# include <LibWeb/DOM/ElementFactory.h>
2022-09-19 17:46:34 +02:00
# include <LibWeb/DOM/Event.h>
2021-04-22 21:11:20 +02:00
# include <LibWeb/DOM/HTMLCollection.h>
2023-01-11 19:47:38 +01:00
# include <LibWeb/DOM/Range.h>
2022-09-19 17:46:34 +02:00
# include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
2021-11-18 15:01:28 +01:00
# include <LibWeb/HTML/BrowsingContext.h>
2022-09-19 12:28:46 +02:00
# include <LibWeb/HTML/BrowsingContextGroup.h>
2022-08-05 10:55:47 +02:00
# include <LibWeb/HTML/CrossOrigin/CrossOriginOpenerPolicy.h>
2023-01-01 18:25:01 +01:00
# include <LibWeb/HTML/DocumentState.h>
2021-10-03 16:42:03 +02:00
# include <LibWeb/HTML/EventLoop/EventLoop.h>
2020-07-26 15:08:16 +02:00
# include <LibWeb/HTML/HTMLAnchorElement.h>
2023-06-21 13:53:09 +02:00
# include <LibWeb/HTML/HTMLDocument.h>
2022-02-17 12:59:32 +01:00
# include <LibWeb/HTML/HTMLInputElement.h>
2022-12-12 12:20:02 +01:00
# include <LibWeb/HTML/NavigableContainer.h>
2023-03-14 13:46:55 +03:00
# include <LibWeb/HTML/RemoteBrowsingContext.h>
2022-08-05 10:55:47 +02:00
# include <LibWeb/HTML/SandboxingFlagSet.h>
# include <LibWeb/HTML/Scripting/WindowEnvironmentSettingsObject.h>
2022-12-17 13:35:20 +01:00
# include <LibWeb/HTML/TraversableNavigable.h>
2022-03-07 23:08:26 +01:00
# include <LibWeb/HTML/Window.h>
2022-10-15 23:10:56 +02:00
# include <LibWeb/HTML/WindowProxy.h>
2022-10-04 21:30:29 +01:00
# include <LibWeb/HighResolutionTime/TimeOrigin.h>
2023-02-17 15:21:32 +00:00
# include <LibWeb/Infra/Strings.h>
2020-11-22 15:53:01 +01:00
# include <LibWeb/Layout/BreakNode.h>
# include <LibWeb/Layout/TextNode.h>
2023-02-25 11:04:29 +01:00
# include <LibWeb/Layout/Viewport.h>
2023-03-29 23:46:18 +01:00
# include <LibWeb/Namespace.h>
2021-08-24 16:28:08 +02:00
# include <LibWeb/Page/Page.h>
2024-01-14 11:14:20 +01:00
# include <LibWeb/Painting/Paintable.h>
2022-10-13 18:25:00 +02:00
# include <LibWeb/URL/URL.h>
2019-10-04 15:50:04 +02:00
2021-11-18 15:01:28 +01:00
namespace Web : : HTML {
2020-03-07 10:27:02 +01:00
2023-11-19 19:47:52 +01:00
JS_DEFINE_ALLOCATOR ( BrowsingContext ) ;
2022-08-05 10:55:47 +02:00
// https://html.spec.whatwg.org/multipage/urls-and-fetching.html#matches-about:blank
2023-08-28 17:41:09 +02:00
bool url_matches_about_blank ( AK : : URL const & url )
2022-08-05 10:55:47 +02:00
{
// A URL matches about:blank if its scheme is "about", its path contains a single string "blank", its username and password are the empty string, and its host is null.
return url . scheme ( ) = = " about " sv
2023-04-14 20:12:03 +01:00
& & url . serialize_path ( ) = = " blank " sv
2023-08-12 16:52:38 +12:00
& & url . raw_username ( ) . is_empty ( )
& & url . raw_password ( ) . is_empty ( )
2023-07-27 21:40:41 +12:00
& & url . host ( ) . has < Empty > ( ) ;
2022-08-05 10:55:47 +02:00
}
2022-12-12 17:36:20 +01:00
// https://html.spec.whatwg.org/multipage/document-sequences.html#determining-the-origin
2023-09-17 17:11:34 +02:00
HTML : : Origin determine_the_origin ( AK : : URL const & url , SandboxingFlagSet sandbox_flags , Optional < HTML : : Origin > source_origin )
2022-12-12 17:36:20 +01:00
{
// 1. If sandboxFlags has its sandboxed origin browsing context flag set, then return a new opaque origin.
2023-08-28 11:57:21 +02:00
if ( has_flag ( sandbox_flags , SandboxingFlagSet : : SandboxedOrigin ) ) {
2022-12-12 17:36:20 +01:00
return HTML : : Origin { } ;
}
// FIXME: 2. If url is null, then return a new opaque origin.
// FIXME: There appears to be no way to get a null URL here, so it might be a spec bug.
// 3. If url is about:srcdoc, then:
if ( url = = " about:srcdoc " sv ) {
2023-09-17 17:11:34 +02:00
// 1. Assert: sourceOrigin is non-null.
VERIFY ( source_origin . has_value ( ) ) ;
2022-12-12 17:36:20 +01:00
2023-09-17 17:11:34 +02:00
// 2. Return sourceOrigin.
return source_origin . release_value ( ) ;
2022-12-12 17:36:20 +01:00
}
// 4. If url matches about:blank and sourceOrigin is non-null, then return sourceOrigin.
if ( url_matches_about_blank ( url ) & & source_origin . has_value ( ) )
return source_origin . release_value ( ) ;
// 5. Return url's origin.
return URL : : url_origin ( url ) ;
}
2023-04-23 19:31:46 +03:00
// https://html.spec.whatwg.org/multipage/document-sequences.html#creating-a-new-auxiliary-browsing-context
2023-06-26 06:58:10 +02:00
WebIDL : : ExceptionOr < BrowsingContext : : BrowsingContextAndDocument > BrowsingContext : : create_a_new_auxiliary_browsing_context_and_document ( JS : : NonnullGCPtr < Page > page , JS : : NonnullGCPtr < HTML : : BrowsingContext > opener )
2023-04-23 19:31:46 +03:00
{
// 1. Let openerTopLevelBrowsingContext be opener's top-level traversable's active browsing context.
auto opener_top_level_browsing_context = opener - > top_level_traversable ( ) - > active_browsing_context ( ) ;
// 2. Let group be openerTopLevelBrowsingContext's group.
auto group = opener_top_level_browsing_context - > group ( ) ;
// 3. Assert: group is non-null, as navigating invokes this directly.
VERIFY ( group ) ;
// 4. Set browsingContext and document be the result of creating a new browsing context and document with opener's active document, null, and group.
auto [ browsing_context , document ] = TRY ( create_a_new_browsing_context_and_document ( page , opener - > active_document ( ) , nullptr , * group ) ) ;
// FIXME: 5. Set browsingContext's is auxiliary to true.
// 6. Append browsingContext to group.
group - > append ( browsing_context ) ;
// 7. Set browsingContext's opener browsing context to opener.
browsing_context - > set_opener_browsing_context ( opener ) ;
// FIXME: 8. Set browsingContext's virtual browsing context group ID to openerTopLevelBrowsingContext's virtual browsing context group ID.
// FIXME: 9. Set browsingContext's opener origin at creation to opener's active document's origin.
// 10. Return browsingContext and document.
return BrowsingContext : : BrowsingContextAndDocument { browsing_context , document } ;
}
// https://html.spec.whatwg.org/multipage/document-sequences.html#creating-a-new-browsing-context
2023-06-26 06:58:10 +02:00
WebIDL : : ExceptionOr < BrowsingContext : : BrowsingContextAndDocument > BrowsingContext : : create_a_new_browsing_context_and_document ( JS : : NonnullGCPtr < Page > page , JS : : GCPtr < DOM : : Document > creator , JS : : GCPtr < DOM : : Element > embedder , JS : : NonnullGCPtr < BrowsingContextGroup > group )
2023-04-23 19:31:46 +03:00
{
auto & vm = group - > vm ( ) ;
// 1. Let browsingContext be a new browsing context.
JS : : NonnullGCPtr < BrowsingContext > browsing_context = * vm . heap ( ) . allocate_without_realm < BrowsingContext > ( page , nullptr ) ;
// 2. Let unsafeContextCreationTime be the unsafe shared current time.
[[maybe_unused]] auto unsafe_context_creation_time = HighResolutionTime : : unsafe_shared_current_time ( ) ;
// 3. Let creatorOrigin be null.
Optional < Origin > creator_origin = { } ;
2023-09-22 17:31:25 -06:00
// FIXME: This algorithm needs re-aligned with the spec
Optional < AK : : URL > creator_base_url = { } ;
2023-04-23 19:31:46 +03:00
// 4. If creator is non-null, then:
if ( creator ) {
// 1. Set creatorOrigin to creator's origin.
creator_origin = creator - > origin ( ) ;
2023-09-22 17:31:25 -06:00
// FIXME: This algorithm needs re-aligned with the spec
creator_base_url = creator - > base_url ( ) ;
2023-04-23 19:31:46 +03:00
// FIXME: 2. Set browsingContext's creator base URL to an algorithm which returns creator's base URL.
// FIXME: 3. Set browsingContext's virtual browsing context group ID to creator's browsing context's top-level browsing context's virtual browsing context group ID.
}
// FIXME: 5. Let sandboxFlags be the result of determining the creation sandboxing flags given browsingContext and embedder.
2023-08-28 11:57:21 +02:00
SandboxingFlagSet sandbox_flags = { } ;
2023-04-23 19:31:46 +03:00
2023-09-17 17:11:34 +02:00
// 6. Let origin be the result of determining the origin given about:blank, sandboxFlags, and creatorOrigin.
auto origin = determine_the_origin ( AK : : URL ( " about:blank " sv ) , sandbox_flags , creator_origin ) ;
2023-04-23 19:31:46 +03:00
// FIXME: 7. Let permissionsPolicy be the result of creating a permissions policy given browsingContext and origin. [PERMISSIONSPOLICY]
// FIXME: 8. Let agent be the result of obtaining a similar-origin window agent given origin, group, and false.
JS : : GCPtr < Window > window ;
// 9. Let realm execution context be the result of creating a new JavaScript realm given agent and the following customizations:
auto realm_execution_context = Bindings : : create_a_new_javascript_realm (
Bindings : : main_thread_vm ( ) ,
[ & ] ( JS : : Realm & realm ) - > JS : : Object * {
auto window_proxy = realm . heap ( ) . allocate < WindowProxy > ( realm , realm ) ;
2023-08-13 13:05:26 +02:00
browsing_context - > set_window_proxy ( window_proxy ) ;
2023-04-23 19:31:46 +03:00
// - For the global object, create a new Window object.
2023-08-13 13:05:26 +02:00
window = Window : : create ( realm ) ;
2023-04-23 19:31:46 +03:00
return window . ptr ( ) ;
} ,
[ & ] ( JS : : Realm & ) - > JS : : Object * {
// - For the global this binding, use browsingContext's WindowProxy object.
return browsing_context - > window_proxy ( ) ;
} ) ;
// 10. Let topLevelCreationURL be about:blank if embedder is null; otherwise embedder's relevant settings object's top-level creation URL.
auto top_level_creation_url = ! embedder ? AK : : URL ( " about:blank " ) : relevant_settings_object ( * embedder ) . top_level_creation_url ;
// 11. Let topLevelOrigin be origin if embedder is null; otherwise embedder's relevant settings object's top-level origin.
auto top_level_origin = ! embedder ? origin : relevant_settings_object ( * embedder ) . origin ( ) ;
// 12. Set up a window environment settings object with about:blank, realm execution context, null, topLevelCreationURL, and topLevelOrigin.
2023-08-13 13:05:26 +02:00
WindowEnvironmentSettingsObject : : setup (
2023-12-15 13:43:39 +01:00
page ,
2023-04-23 19:31:46 +03:00
AK : : URL ( " about:blank " ) ,
move ( realm_execution_context ) ,
{ } ,
top_level_creation_url ,
2023-08-13 13:05:26 +02:00
top_level_origin ) ;
2023-04-23 19:31:46 +03:00
// 13. Let loadTimingInfo be a new document load timing info with its navigation start time set to the result of calling
// coarsen time with unsafeContextCreationTime and the new environment settings object's cross-origin isolated capability.
auto load_timing_info = DOM : : DocumentLoadTimingInfo ( ) ;
load_timing_info . navigation_start_time = HighResolutionTime : : coarsen_time (
unsafe_context_creation_time ,
verify_cast < WindowEnvironmentSettingsObject > ( Bindings : : host_defined_environment_settings_object ( window - > realm ( ) ) ) . cross_origin_isolated_capability ( ) = = CanUseCrossOriginIsolatedAPIs : : Yes ) ;
// 14. Let document be a new Document, with:
2023-08-13 13:05:26 +02:00
auto document = HTML : : HTMLDocument : : create ( window - > realm ( ) ) ;
2023-04-23 19:31:46 +03:00
// Non-standard
window - > set_associated_document ( * document ) ;
// type: "html"
document - > set_document_type ( DOM : : Document : : Type : : HTML ) ;
// content type: "text/html"
2023-09-15 21:46:58 +12:00
document - > set_content_type ( " text/html " _string ) ;
2023-04-23 19:31:46 +03:00
// mode: "quirks"
document - > set_quirks_mode ( DOM : : QuirksMode : : Yes ) ;
// origin: origin
document - > set_origin ( origin ) ;
// browsing context: browsingContext
document - > set_browsing_context ( browsing_context ) ;
// FIXME: permissions policy: permissionsPolicy
// FIXME: active sandboxing flag set: sandboxFlags
// load timing info: loadTimingInfo
document - > set_load_timing_info ( load_timing_info ) ;
// is initial about:blank: true
document - > set_is_initial_about_blank ( true ) ;
2023-09-22 17:31:25 -06:00
// about base URL: creatorBaseURL
document - > set_about_base_url ( creator_base_url ) ;
2023-04-23 19:31:46 +03:00
// 15. If creator is non-null, then:
if ( creator ) {
// 1. Set document's referrer to the serialization of creator's URL.
2023-12-16 17:49:34 +03:30
document - > set_referrer ( MUST ( String : : from_byte_string ( creator - > url ( ) . serialize ( ) ) ) ) ;
2023-04-23 19:31:46 +03:00
// FIXME: 2. Set document's policy container to a clone of creator's policy container.
// 3. If creator's origin is same origin with creator's relevant settings object's top-level origin,
if ( creator - > origin ( ) . is_same_origin ( creator - > relevant_settings_object ( ) . top_level_origin ) ) {
// then set document's cross-origin opener policy to creator's browsing context's top-level browsing context's active document's cross-origin opener policy.
VERIFY ( creator - > browsing_context ( ) ) ;
2023-09-04 15:33:08 +02:00
VERIFY ( creator - > browsing_context ( ) - > top_level_browsing_context ( ) - > active_document ( ) ) ;
document - > set_cross_origin_opener_policy ( creator - > browsing_context ( ) - > top_level_browsing_context ( ) - > active_document ( ) - > cross_origin_opener_policy ( ) ) ;
2023-04-23 19:31:46 +03:00
}
}
// 16. Assert: document's URL and document's relevant settings object's creation URL are about:blank.
VERIFY ( document - > url ( ) = = " about:blank " sv ) ;
VERIFY ( document - > relevant_settings_object ( ) . creation_url = = " about:blank " sv ) ;
// 17. Mark document as ready for post-load tasks.
document - > set_ready_for_post_load_tasks ( true ) ;
// 18. Ensure that document has a single child html node, which itself has two empty child nodes: a head element, and a body element.
2023-11-04 18:42:04 +01:00
auto html_node = TRY ( DOM : : create_element ( document , HTML : : TagNames : : html , Namespace : : HTML ) ) ;
auto head_element = TRY ( DOM : : create_element ( document , HTML : : TagNames : : head , Namespace : : HTML ) ) ;
2023-04-23 19:31:46 +03:00
TRY ( html_node - > append_child ( head_element ) ) ;
2023-11-04 18:42:04 +01:00
auto body_element = TRY ( DOM : : create_element ( document , HTML : : TagNames : : body , Namespace : : HTML ) ) ;
2023-04-23 19:31:46 +03:00
TRY ( html_node - > append_child ( body_element ) ) ;
TRY ( document - > append_child ( html_node ) ) ;
// 19. Make active document.
document - > make_active ( ) ;
// 20. Completely finish loading document.
document - > completely_finish_loading ( ) ;
// 21. Return browsingContext and document.
return BrowsingContext : : BrowsingContextAndDocument { browsing_context , document } ;
}
2023-06-26 06:58:10 +02:00
BrowsingContext : : BrowsingContext ( JS : : NonnullGCPtr < Page > page , HTML : : NavigableContainer * container )
2021-09-08 02:07:39 +02:00
: m_page ( page )
2020-06-07 14:40:38 +02:00
, m_event_handler ( { } , * this )
2021-09-09 02:07:32 +02:00
, m_container ( container )
2020-06-05 23:36:02 +02:00
{
2023-08-19 16:19:52 +02:00
m_cursor_blink_timer = Core : : Timer : : create_repeating ( 500 , [ this ] {
2021-09-08 02:07:39 +02:00
if ( ! is_focused_context ( ) )
return ;
2024-01-16 12:01:29 +01:00
if ( m_cursor_position & & m_cursor_position - > node ( ) - > paintable ( ) ) {
2021-09-08 02:07:39 +02:00
m_cursor_blink_state = ! m_cursor_blink_state ;
2024-01-14 13:46:52 +01:00
m_cursor_position - > node ( ) - > paintable ( ) - > set_needs_display ( ) ;
2021-09-08 02:07:39 +02:00
}
2023-08-19 16:19:52 +02:00
} ) . release_value_but_fixme_should_propagate_errors ( ) ;
2020-06-05 23:36:02 +02:00
}
2022-03-14 13:21:51 -06:00
BrowsingContext : : ~ BrowsingContext ( ) = default ;
2020-08-02 11:52:35 +02:00
2022-10-17 11:06:50 +02:00
void BrowsingContext : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2023-06-26 06:58:10 +02:00
visitor . visit ( m_page ) ;
2022-10-17 11:06:50 +02:00
for ( auto & entry : m_session_history )
2022-12-12 10:58:30 +01:00
visitor . visit ( entry ) ;
2022-10-17 11:06:50 +02:00
visitor . visit ( m_container ) ;
2023-09-26 19:34:21 +02:00
visitor . visit ( m_cursor_position ) ;
2022-10-17 11:06:50 +02:00
visitor . visit ( m_window_proxy ) ;
visitor . visit ( m_group ) ;
visitor . visit ( m_parent ) ;
visitor . visit ( m_first_child ) ;
visitor . visit ( m_last_child ) ;
visitor . visit ( m_next_sibling ) ;
visitor . visit ( m_previous_sibling ) ;
2024-01-14 10:14:36 +01:00
m_event_handler . visit_edges ( visitor ) ;
2022-10-17 11:06:50 +02:00
}
2022-12-17 13:35:20 +01:00
// https://html.spec.whatwg.org/multipage/document-sequences.html#bc-traversable
JS : : NonnullGCPtr < HTML : : TraversableNavigable > BrowsingContext : : top_level_traversable ( ) const
{
// A browsing context's top-level traversable is its active document's node navigable's top-level traversable.
auto traversable = active_document ( ) - > navigable ( ) - > top_level_traversable ( ) ;
VERIFY ( traversable ) ;
VERIFY ( traversable - > is_top_level_traversable ( ) ) ;
return * traversable ;
}
2021-05-30 12:36:53 +02:00
void BrowsingContext : : did_edit ( Badge < EditEventHandler > )
2021-01-04 20:48:27 +01:00
{
2021-02-09 21:23:50 +01:00
reset_cursor_blink_cycle ( ) ;
2022-02-17 12:59:32 +01:00
2023-09-26 19:34:21 +02:00
if ( m_cursor_position & & is < DOM : : Text > ( * m_cursor_position - > node ( ) ) ) {
auto & text_node = static_cast < DOM : : Text & > ( * m_cursor_position - > node ( ) ) ;
2023-08-30 14:38:58 +01:00
if ( auto * text_node_owner = text_node . editable_text_node_owner ( ) )
text_node_owner - > did_edit_text_node ( { } ) ;
2022-02-17 12:59:32 +01:00
}
2021-02-09 21:23:50 +01:00
}
2021-05-30 12:36:53 +02:00
void BrowsingContext : : reset_cursor_blink_cycle ( )
2021-02-09 21:23:50 +01:00
{
2021-01-04 20:48:27 +01:00
m_cursor_blink_state = true ;
m_cursor_blink_timer - > restart ( ) ;
2024-01-14 13:46:52 +01:00
if ( m_cursor_position & & m_cursor_position - > node ( ) - > paintable ( ) )
m_cursor_position - > node ( ) - > paintable ( ) - > set_needs_display ( ) ;
2021-01-04 20:48:27 +01:00
}
2022-03-01 21:17:53 +00:00
// https://html.spec.whatwg.org/multipage/browsers.html#top-level-browsing-context
bool BrowsingContext : : is_top_level ( ) const
{
// A browsing context that has no parent browsing context is the top-level browsing context for itself and all of the browsing contexts for which it is an ancestor browsing context.
return ! parent ( ) ;
}
2021-05-30 12:36:53 +02:00
bool BrowsingContext : : is_focused_context ( ) const
2020-08-14 11:33:20 +02:00
{
2023-06-26 06:58:10 +02:00
return & m_page - > focused_context ( ) = = this ;
2020-08-14 11:33:20 +02:00
}
2023-09-04 15:33:08 +02:00
JS : : GCPtr < BrowsingContext > BrowsingContext : : top_level_browsing_context ( ) const
{
auto const * start = this ;
// 1. If start's active document is not fully active, then return null.
if ( ! start - > active_document ( ) - > is_fully_active ( ) ) {
return nullptr ;
}
// 2. Let navigable be start's active document's node navigable.
auto navigable = start - > active_document ( ) - > navigable ( ) ;
// 3. While navigable's parent is not null, set navigable to navigable's parent.
while ( navigable - > parent ( ) ) {
navigable = navigable - > parent ( ) ;
}
// 4. Return navigable's active browsing context.
return navigable - > active_browsing_context ( ) ;
}
2022-11-02 17:35:53 +00:00
CSSPixelRect BrowsingContext : : to_top_level_rect ( CSSPixelRect const & a_rect )
2020-06-08 20:31:49 +02:00
{
auto rect = a_rect ;
2021-05-30 12:36:53 +02:00
rect . set_location ( to_top_level_position ( a_rect . location ( ) ) ) ;
2020-06-08 20:31:49 +02:00
return rect ;
}
2022-11-02 17:35:53 +00:00
CSSPixelPoint BrowsingContext : : to_top_level_position ( CSSPixelPoint a_position )
2020-06-08 20:31:49 +02:00
{
auto position = a_position ;
2022-10-17 11:06:50 +02:00
for ( auto ancestor = parent ( ) ; ancestor ; ancestor = ancestor - > parent ( ) ) {
2021-05-30 12:36:53 +02:00
if ( ancestor - > is_top_level ( ) )
2020-06-08 20:31:49 +02:00
break ;
2021-09-09 02:07:32 +02:00
if ( ! ancestor - > container ( ) )
2020-06-08 20:31:49 +02:00
return { } ;
2024-01-14 11:14:20 +01:00
if ( ! ancestor - > container ( ) - > paintable ( ) )
2020-06-08 20:31:49 +02:00
return { } ;
2024-01-14 11:14:20 +01:00
position . translate_by ( ancestor - > container ( ) - > paintable ( ) - > box_type_agnostic_position ( ) ) ;
2020-06-08 20:31:49 +02:00
}
return position ;
2020-06-06 13:02:44 +02:00
}
2023-09-26 19:34:21 +02:00
void BrowsingContext : : set_cursor_position ( JS : : NonnullGCPtr < DOM : : Position > position )
2020-08-02 11:52:35 +02:00
{
2023-09-26 19:34:21 +02:00
if ( m_cursor_position & & m_cursor_position - > equals ( position ) )
2020-08-02 11:52:35 +02:00
return ;
2024-01-14 13:46:52 +01:00
if ( m_cursor_position & & m_cursor_position - > node ( ) - > paintable ( ) )
m_cursor_position - > node ( ) - > paintable ( ) - > set_needs_display ( ) ;
2020-08-02 11:52:35 +02:00
2023-09-26 19:34:21 +02:00
m_cursor_position = position ;
2020-08-02 11:52:35 +02:00
2024-01-14 13:46:52 +01:00
if ( m_cursor_position & & m_cursor_position - > node ( ) - > paintable ( ) )
m_cursor_position - > node ( ) - > paintable ( ) - > set_needs_display ( ) ;
2020-08-02 11:52:35 +02:00
2021-02-09 21:23:50 +01:00
reset_cursor_blink_cycle ( ) ;
2020-08-02 11:52:35 +02:00
}
2023-12-24 15:49:57 +13:00
static String visible_text_in_range ( DOM : : Range const & range )
2020-08-06 19:21:59 +02:00
{
2023-01-11 19:47:38 +01:00
// NOTE: This is an adaption of Range stringification, but we skip over DOM nodes that don't have a corresponding layout node.
2020-08-06 19:21:59 +02:00
StringBuilder builder ;
2023-01-11 19:47:38 +01:00
if ( range . start_container ( ) = = range . end_container ( ) & & is < DOM : : Text > ( * range . start_container ( ) ) ) {
if ( ! range . start_container ( ) - > layout_node ( ) )
2023-12-24 15:49:57 +13:00
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 ( ) ) ) ;
2020-08-06 19:21:59 +02:00
}
2023-01-11 19:47:38 +01:00
if ( is < DOM : : Text > ( * range . start_container ( ) ) & & range . start_container ( ) - > layout_node ( ) )
2023-09-07 21:36:05 +12:00
builder . append ( static_cast < DOM : : Text const & > ( * range . start_container ( ) ) . data ( ) . bytes_as_string_view ( ) . substring_view ( range . start_offset ( ) ) ) ;
2020-08-06 19:21:59 +02:00
2023-01-11 19:47:38 +01:00
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 ( ) ) ;
2020-08-06 19:21:59 +02:00
}
2023-01-11 19:47:38 +01:00
if ( is < DOM : : Text > ( * range . end_container ( ) ) & & range . end_container ( ) - > layout_node ( ) )
2023-09-07 21:36:05 +12:00
builder . append ( static_cast < DOM : : Text const & > ( * range . end_container ( ) ) . data ( ) . bytes_as_string_view ( ) . substring_view ( 0 , range . end_offset ( ) ) ) ;
2020-08-06 19:21:59 +02:00
2023-12-24 15:49:57 +13:00
return MUST ( builder . to_string ( ) ) ;
2020-08-06 19:21:59 +02:00
}
2023-12-24 15:49:57 +13:00
String BrowsingContext : : selected_text ( ) const
2023-01-11 19:47:38 +01:00
{
2023-12-24 15:49:57 +13:00
auto const * document = active_document ( ) ;
2023-01-11 19:47:38 +01:00
if ( ! document )
2023-12-24 15:49:57 +13:00
return String { } ;
2023-01-11 19:47:38 +01:00
auto selection = const_cast < DOM : : Document & > ( * document ) . get_selection ( ) ;
auto range = selection - > range ( ) ;
if ( ! range )
2023-12-24 15:49:57 +13:00
return String { } ;
2023-01-11 19:47:38 +01:00
return visible_text_in_range ( * range ) ;
}
2021-07-14 08:38:10 -04:00
void BrowsingContext : : select_all ( )
{
2023-01-11 19:48:17 +01:00
auto * document = active_document ( ) ;
if ( ! document )
2021-07-14 08:38:10 -04:00
return ;
2023-01-11 19:48:17 +01:00
auto * body = document - > body ( ) ;
if ( ! body )
2021-07-14 08:38:10 -04:00
return ;
2023-01-11 19:48:17 +01:00
auto selection = document - > get_selection ( ) ;
if ( ! selection )
return ;
( void ) selection - > select_all_children ( * document - > body ( ) ) ;
2021-07-14 08:38:10 -04:00
}
2021-05-30 12:36:53 +02:00
bool BrowsingContext : : increment_cursor_position_offset ( )
2021-05-18 22:01:12 +02:00
{
2023-09-26 19:34:21 +02:00
if ( ! m_cursor_position - > increment_offset ( ) )
2021-05-18 22:01:12 +02:00
return false ;
reset_cursor_blink_cycle ( ) ;
return true ;
}
2021-05-30 12:36:53 +02:00
bool BrowsingContext : : decrement_cursor_position_offset ( )
2021-05-18 22:01:12 +02:00
{
2023-09-26 19:34:21 +02:00
if ( ! m_cursor_position - > decrement_offset ( ) )
2021-05-18 22:01:12 +02:00
return false ;
reset_cursor_blink_cycle ( ) ;
return true ;
}
2022-02-06 18:45:29 +01:00
// https://html.spec.whatwg.org/multipage/interaction.html#currently-focused-area-of-a-top-level-browsing-context
2022-08-28 13:42:07 +02:00
JS : : GCPtr < DOM : : Node > BrowsingContext : : currently_focused_area ( )
2022-02-06 18:45:29 +01:00
{
// 1. If topLevelBC does not have system focus, then return null.
if ( ! is_focused_context ( ) )
return nullptr ;
// 2. Let candidate be topLevelBC's active document.
auto * candidate = active_document ( ) ;
// 3. While candidate's focused area is a browsing context container with a non-null nested browsing context:
// set candidate to the active document of that browsing context container's nested browsing context.
while ( candidate - > focused_element ( )
2022-12-12 12:20:02 +01:00
& & is < HTML : : NavigableContainer > ( candidate - > focused_element ( ) )
& & static_cast < HTML : : NavigableContainer & > ( * candidate - > focused_element ( ) ) . nested_browsing_context ( ) ) {
candidate = static_cast < HTML : : NavigableContainer & > ( * candidate - > focused_element ( ) ) . nested_browsing_context ( ) - > active_document ( ) ;
2022-02-06 18:45:29 +01:00
}
// 4. If candidate's focused area is non-null, set candidate to candidate's focused area.
if ( candidate - > focused_element ( ) ) {
// NOTE: We return right away here instead of assigning to candidate,
// since that would require compromising type safety.
return candidate - > focused_element ( ) ;
}
// 5. Return candidate.
return candidate ;
}
2022-11-15 02:00:27 +02:00
// https://html.spec.whatwg.org/#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name
2024-01-30 20:05:00 -07:00
BrowsingContext : : ChosenBrowsingContext BrowsingContext : : choose_a_browsing_context ( StringView name , TokenizedFeature : : NoOpener no_opener , ActivateTab )
2022-03-15 14:37:58 +00:00
{
2022-11-15 02:00:27 +02:00
// The rules for choosing a browsing context, given a browsing context name name, a browsing context current, and
// a boolean noopener are as follows:
2023-05-31 00:08:14 +02:00
JS : : GCPtr < AbstractBrowsingContext > matching_name_in_tree = nullptr ;
2023-09-04 15:33:08 +02:00
top_level_browsing_context ( ) - > for_each_in_subtree ( [ & ] ( auto & context ) {
2023-05-31 00:08:14 +02:00
if ( context . name ( ) = = name ) {
matching_name_in_tree = & context ;
return IterationDecision : : Break ;
}
return IterationDecision : : Continue ;
} ) ;
2022-03-15 14:37:58 +00:00
// 1. Let chosen be null.
2023-03-14 12:40:03 +03:00
JS : : GCPtr < AbstractBrowsingContext > chosen = nullptr ;
2022-03-15 14:37:58 +00:00
2022-11-15 02:00:27 +02:00
// 2. Let windowType be "existing or none".
auto window_type = WindowType : : ExistingOrNone ;
2022-03-15 14:37:58 +00:00
2022-11-15 02:00:27 +02:00
// 3. Let sandboxingFlagSet be current's active document's active sandboxing flag set.
auto sandboxing_flag_set = active_document ( ) - > active_sandboxing_flag_set ( ) ;
2022-03-15 14:37:58 +00:00
// 4. If name is the empty string or an ASCII case-insensitive match for "_self", then set chosen to current.
2023-02-17 15:21:32 +00:00
if ( name . is_empty ( ) | | Infra : : is_ascii_case_insensitive_match ( name , " _self " sv ) ) {
2022-03-15 14:37:58 +00:00
chosen = this ;
2022-11-15 02:00:27 +02:00
}
2022-03-15 14:37:58 +00:00
2022-11-15 02:00:27 +02:00
// 5. Otherwise, if name is an ASCII case-insensitive match for "_parent", set chosen to current's parent browsing
// context, if any, and current otherwise.
2023-02-17 15:21:32 +00:00
else if ( Infra : : is_ascii_case_insensitive_match ( name , " _parent " sv ) ) {
2022-10-17 11:06:50 +02:00
if ( auto parent = this - > parent ( ) )
2022-03-15 14:37:58 +00:00
chosen = parent ;
else
chosen = this ;
}
2022-11-15 02:00:27 +02:00
// 6. Otherwise, if name is an ASCII case-insensitive match for "_top", set chosen to current's top-level browsing
// context, if any, and current otherwise.
2023-02-17 15:21:32 +00:00
else if ( Infra : : is_ascii_case_insensitive_match ( name , " _top " sv ) ) {
2023-09-04 15:33:08 +02:00
chosen = top_level_browsing_context ( ) ;
2022-03-15 14:37:58 +00:00
}
2023-05-31 00:08:14 +02:00
// 7. Otherwise, if name is not an ASCII case-insensitive match for "_blank", there exists a browsing context
// whose name is the same as name, current is familiar with that browsing context, and the user agent
// determines that the two browsing contexts are related enough that it is ok if they reach each other,
// set chosen to that browsing context. If there are multiple matching browsing contexts, the user agent
// should set chosen to one in some arbitrary consistent manner, such as the most recently opened, most
// recently focused, or more closely related.
else if ( ! Infra : : is_ascii_case_insensitive_match ( name , " _blank " sv ) & & matching_name_in_tree ) {
chosen = matching_name_in_tree ;
2022-03-15 14:37:58 +00:00
} else {
2022-11-15 02:00:27 +02:00
// 8. Otherwise, a new browsing context is being requested, and what happens depends on the user agent's
// configuration and abilities — it is determined by the rules given for the first applicable option from
// the following list:
// --> If current's active window does not have transient activation and the user agent has been configured to
// not show popups (i.e., the user agent has a "popup blocker" enabled)
VERIFY ( m_page ) ;
if ( ! active_window ( ) - > has_transient_activation ( ) & & m_page - > should_block_pop_ups ( ) ) {
// FIXME: The user agent may inform the user that a popup has been blocked.
dbgln ( " Pop-up blocked! " ) ;
}
// --> If sandboxingFlagSet has the sandboxed auxiliary navigation browsing context flag set
2023-08-28 11:57:21 +02:00
else if ( has_flag ( sandboxing_flag_set , SandboxingFlagSet : : SandboxedAuxiliaryNavigation ) ) {
2022-11-15 02:00:27 +02:00
// FIXME: The user agent may report to a developer console that a popup has been blocked.
dbgln ( " Pop-up blocked! " ) ;
}
// --> If the user agent has been configured such that in this instance it will create a new browsing context
else if ( true ) { // FIXME: When is this the case?
// 1. Set windowType to "new and unrestricted".
window_type = WindowType : : NewAndUnrestricted ;
// 2. If current's top-level browsing context's active document's cross-origin opener policy's value is
// "same-origin" or "same-origin-plus-COEP", then:
2023-09-04 15:33:08 +02:00
if ( top_level_browsing_context ( ) - > active_document ( ) - > cross_origin_opener_policy ( ) . value = = CrossOriginOpenerPolicyValue : : SameOrigin | | top_level_browsing_context ( ) - > active_document ( ) - > cross_origin_opener_policy ( ) . value = = CrossOriginOpenerPolicyValue : : SameOriginPlusCOEP ) {
2022-11-15 02:00:27 +02:00
// 1. Let currentDocument be current's active document.
2023-09-04 15:33:08 +02:00
auto * current_document = top_level_browsing_context ( ) - > active_document ( ) ;
2022-11-15 02:00:27 +02:00
// 2. If currentDocument's origin is not same origin with currentDocument's relevant settings object's
// top-level origin, then set noopener to true, name to "_blank", and windowType to "new with no opener".
if ( ! current_document - > origin ( ) . is_same_origin ( current_document - > relevant_settings_object ( ) . top_level_origin ) ) {
2023-03-21 06:52:29 -04:00
no_opener = TokenizedFeature : : NoOpener : : Yes ;
2022-11-15 02:00:27 +02:00
name = " _blank " sv ;
window_type = WindowType : : NewWithNoOpener ;
}
}
// 3. If noopener is true, then set chosen to the result of creating a new top-level browsing context.
2023-03-21 06:52:29 -04:00
if ( no_opener = = TokenizedFeature : : NoOpener : : Yes ) {
2024-01-30 20:05:00 -07:00
// FIXME: This is completely wrong, but all of this should be removed in favor of choose_a_navigable
chosen = this ;
2022-11-15 02:00:27 +02:00
}
// 4. Otherwise:
else {
// 1. Set chosen to the result of creating a new auxiliary browsing context with current.
// FIXME: We have no concept of auxiliary browsing context
2023-09-03 21:46:36 +02:00
chosen = HTML : : create_a_new_top_level_browsing_context_and_document ( * m_page ) . release_value_but_fixme_should_propagate_errors ( ) . browsing_context ;
2022-11-15 02:00:27 +02:00
// 2. If sandboxingFlagSet's sandboxed navigation browsing context flag is set, then current must be
// set as chosen's one permitted sandboxed navigator.
// FIXME: We have no concept of one permitted sandboxed navigator
}
// 5. If sandboxingFlagSet's sandbox propagates to auxiliary browsing contexts flag is set, then all the
// flags that are set in sandboxingFlagSet must be set in chosen's popup sandboxing flag set.
// FIXME: Our BrowsingContexts do not have SandboxingFlagSets yet, only documents do
// 6. If name is not an ASCII case-insensitive match for "_blank", then set chosen's name to name.
2023-02-17 15:21:32 +00:00
if ( ! Infra : : is_ascii_case_insensitive_match ( name , " _blank " sv ) )
2023-06-11 19:46:53 +02:00
chosen - > set_name ( String : : from_utf8 ( name ) . release_value_but_fixme_should_propagate_errors ( ) ) ;
2022-11-15 02:00:27 +02:00
}
// --> 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.
}
2022-03-15 14:37:58 +00:00
}
// 9. Return chosen and windowType.
2023-03-14 12:40:03 +03:00
return { chosen . ptr ( ) , window_type } ;
2022-03-15 14:37:58 +00:00
}
2022-08-01 16:42:11 +02:00
// https://html.spec.whatwg.org/multipage/dom.html#still-on-its-initial-about:blank-document
bool BrowsingContext : : still_on_its_initial_about_blank_document ( ) const
{
// A browsing context browsingContext is still on its initial about:blank Document
// if browsingContext's session history's size is 1
// and browsingContext's session history[0]'s document's is initial about:blank is true.
return m_session_history . size ( ) = = 1
2023-01-01 18:25:01 +01:00
& & m_session_history [ 0 ] - > document_state - > document ( )
& & m_session_history [ 0 ] - > document_state - > document ( ) - > is_initial_about_blank ( ) ;
2022-08-01 16:42:11 +02:00
}
2022-08-28 13:42:07 +02:00
DOM : : Document const * BrowsingContext : : active_document ( ) const
{
2022-10-15 23:10:56 +02:00
auto * window = active_window ( ) ;
if ( ! window )
2022-09-20 21:44:42 +02:00
return nullptr ;
2022-10-15 23:10:56 +02:00
return & window - > associated_document ( ) ;
2022-08-28 13:42:07 +02:00
}
DOM : : Document * BrowsingContext : : active_document ( )
{
2022-10-15 23:10:56 +02:00
auto * window = active_window ( ) ;
if ( ! window )
2022-09-20 21:44:42 +02:00
return nullptr ;
2022-10-15 23:10:56 +02:00
return & window - > associated_document ( ) ;
2022-08-28 13:42:07 +02:00
}
2022-10-15 23:10:56 +02:00
// https://html.spec.whatwg.org/multipage/browsers.html#active-window
2022-09-19 17:46:34 +02:00
HTML : : Window * BrowsingContext : : active_window ( )
{
2022-10-15 23:10:56 +02:00
return m_window_proxy - > window ( ) ;
2022-09-19 17:46:34 +02:00
}
2022-10-15 23:10:56 +02:00
// https://html.spec.whatwg.org/multipage/browsers.html#active-window
2022-09-19 17:46:34 +02:00
HTML : : Window const * BrowsingContext : : active_window ( ) const
{
2022-10-15 23:10:56 +02:00
return m_window_proxy - > window ( ) ;
}
HTML : : WindowProxy * BrowsingContext : : window_proxy ( )
{
return m_window_proxy . ptr ( ) ;
}
HTML : : WindowProxy const * BrowsingContext : : window_proxy ( ) const
{
return m_window_proxy . ptr ( ) ;
2022-09-19 17:46:34 +02:00
}
2023-04-23 19:31:46 +03:00
void BrowsingContext : : set_window_proxy ( JS : : GCPtr < WindowProxy > window_proxy )
{
m_window_proxy = move ( window_proxy ) ;
}
2022-09-19 12:28:46 +02:00
BrowsingContextGroup * BrowsingContext : : group ( )
{
return m_group ;
}
void BrowsingContext : : set_group ( BrowsingContextGroup * group )
{
m_group = group ;
}
// https://html.spec.whatwg.org/multipage/browsers.html#bcg-remove
void BrowsingContext : : remove ( )
{
// 1. Assert: browsingContext's group is non-null, because a browsing context only gets discarded once.
VERIFY ( group ( ) ) ;
// 2. Let group be browsingContext's group.
2022-10-17 11:06:50 +02:00
JS : : NonnullGCPtr < BrowsingContextGroup > group = * this - > group ( ) ;
2022-09-19 12:28:46 +02:00
// 3. Set browsingContext's group to null.
set_group ( nullptr ) ;
// 4. Remove browsingContext from group's browsing context set.
2023-04-20 16:44:54 +01:00
group - > browsing_context_set ( ) . remove ( * this ) ;
2022-09-19 12:28:46 +02:00
// 5. If group's browsing context set is empty, then remove group from the user agent's browsing context group set.
// NOTE: This is done by ~BrowsingContextGroup() when the refcount reaches 0.
}
2022-09-19 17:46:34 +02:00
// https://html.spec.whatwg.org/multipage/origin.html#one-permitted-sandboxed-navigator
BrowsingContext const * BrowsingContext : : the_one_permitted_sandboxed_navigator ( ) const
{
// FIXME: Implement this.
return nullptr ;
}
2022-10-17 11:06:50 +02:00
JS : : GCPtr < BrowsingContext > BrowsingContext : : first_child ( ) const
{
return m_first_child ;
}
JS : : GCPtr < BrowsingContext > BrowsingContext : : next_sibling ( ) const
{
return m_next_sibling ;
}
bool BrowsingContext : : is_ancestor_of ( BrowsingContext const & other ) const
{
for ( auto ancestor = other . parent ( ) ; ancestor ; ancestor = ancestor - > parent ( ) ) {
if ( ancestor = = this )
return true ;
}
return false ;
}
2023-11-28 08:36:09 -07:00
// https://html.spec.whatwg.org/multipage/document-sequences.html#familiar-with
bool BrowsingContext : : is_familiar_with ( BrowsingContext const & other ) const
{
// A browsing context A is familiar with a second browsing context B if the following algorithm returns true:
auto const & A = * this ;
auto const & B = other ;
// 1. If A's active document's origin is same origin with B's active document's origin, then return true.
if ( A . active_document ( ) - > origin ( ) . is_same_origin ( B . active_document ( ) - > origin ( ) ) )
return true ;
// 2. If A's top-level browsing context is B, then return true.
if ( A . top_level_browsing_context ( ) = = & B )
return true ;
// 3. If B is an auxiliary browsing context and A is familiar with B's opener browsing context, then return true.
if ( B . opener_browsing_context ( ) ! = nullptr & & A . is_familiar_with ( * B . opener_browsing_context ( ) ) )
return true ;
// 4. If there exists an ancestor browsing context of B whose active document has the same origin as the active document of A, then return true.
// NOTE: This includes the case where A is an ancestor browsing context of B.
for ( auto ancestor = B . parent ( ) ; ancestor ; ancestor = ancestor - > parent ( ) ) {
if ( ancestor - > active_document ( ) - > origin ( ) . is_same_origin ( A . active_document ( ) - > origin ( ) ) )
return true ;
}
// 5. Return false.
return false ;
}
2023-08-28 17:44:50 +02:00
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#snapshotting-target-snapshot-params
SandboxingFlagSet determine_the_creation_sandboxing_flags ( BrowsingContext const & , JS : : GCPtr < DOM : : Element > )
{
// FIXME: Populate this once we have the proper flag sets on BrowsingContext
return { } ;
}
2020-03-07 10:27:02 +01:00
}