2020-01-18 09:38:21 +01:00
/*
2022-02-13 01:02:00 +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
*/
2021-02-07 11:35:26 +01:00
# include <AK/AnyOf.h>
2022-02-25 16:48:05 +01:00
# include <AK/CharacterTypes.h>
2022-02-13 01:02:00 +01:00
# include <AK/Debug.h>
2020-03-25 18:53:20 +01:00
# include <AK/StringBuilder.h>
2021-07-30 19:31:46 +01:00
# include <LibWeb/CSS/Parser/Parser.h>
2020-03-25 18:53:20 +01:00
# include <LibWeb/CSS/PropertyID.h>
2021-09-23 12:35:56 +02:00
# include <LibWeb/CSS/ResolvedCSSStyleDeclaration.h>
2021-09-30 02:16:36 +02:00
# include <LibWeb/CSS/SelectorEngine.h>
2021-02-19 19:13:08 +01:00
# include <LibWeb/DOM/DOMException.h>
2021-10-18 13:21:23 -04:00
# include <LibWeb/DOM/DOMTokenList.h>
2020-03-07 10:32:51 +01:00
# include <LibWeb/DOM/Document.h>
# include <LibWeb/DOM/Element.h>
2021-02-19 19:13:08 +01:00
# include <LibWeb/DOM/ExceptionOr.h>
2021-04-22 21:11:20 +02:00
# include <LibWeb/DOM/HTMLCollection.h>
2021-02-10 18:21:35 +01:00
# include <LibWeb/DOM/ShadowRoot.h>
2020-03-25 18:53:20 +01:00
# include <LibWeb/DOM/Text.h>
2021-09-13 22:42:57 +01:00
# include <LibWeb/DOMParsing/InnerHTML.h>
2021-09-27 00:55:13 +02:00
# include <LibWeb/Geometry/DOMRect.h>
2022-02-12 16:48:00 +03:00
# include <LibWeb/Geometry/DOMRectList.h>
2021-11-18 15:01:28 +01:00
# include <LibWeb/HTML/BrowsingContext.h>
2021-09-09 00:59:09 +02:00
# include <LibWeb/HTML/EventLoop/EventLoop.h>
2021-09-25 23:15:48 +02:00
# include <LibWeb/HTML/Parser/HTMLParser.h>
2021-10-06 20:02:41 +02:00
# include <LibWeb/Layout/BlockContainer.h>
2020-11-22 15:53:01 +01:00
# include <LibWeb/Layout/InlineNode.h>
# include <LibWeb/Layout/ListItemBox.h>
# include <LibWeb/Layout/TableBox.h>
# include <LibWeb/Layout/TableCellBox.h>
# include <LibWeb/Layout/TableRowBox.h>
# include <LibWeb/Layout/TableRowGroupBox.h>
2020-11-25 20:29:03 +01:00
# include <LibWeb/Layout/TreeBuilder.h>
2021-02-07 23:44:01 +01:00
# include <LibWeb/Namespace.h>
2019-06-15 18:55:47 +02:00
2020-07-26 19:37:56 +02:00
namespace Web : : DOM {
2020-03-07 10:27:02 +01:00
2022-02-18 21:00:52 +01:00
Element : : Element ( Document & document , DOM : : QualifiedName qualified_name )
2019-09-29 11:43:07 +02:00
: ParentNode ( document , NodeType : : ELEMENT_NODE )
2021-02-07 11:20:15 +01:00
, m_qualified_name ( move ( qualified_name ) )
2021-10-16 16:16:57 -04:00
, m_attributes ( NamedNodeMap : : create ( * this ) )
2019-06-15 18:55:47 +02:00
{
2021-05-04 22:25:43 +01:00
make_html_uppercased_qualified_name ( ) ;
2019-06-15 18:55:47 +02:00
}
Element : : ~ Element ( )
{
}
2021-10-16 16:16:57 -04:00
// https://dom.spec.whatwg.org/#dom-element-getattribute
String Element : : get_attribute ( const FlyString & name ) const
2019-06-15 21:08:36 +02:00
{
2021-10-16 16:16:57 -04:00
// 1. Let attr be the result of getting an attribute given qualifiedName and this.
auto const * attribute = m_attributes - > get_attribute ( name ) ;
2019-06-15 21:08:36 +02:00
2021-10-16 16:16:57 -04:00
// 2. If attr is null, return null.
if ( ! attribute )
return { } ;
2019-06-15 21:08:36 +02:00
2021-10-16 16:16:57 -04:00
// 3. Return attr’ s value.
return attribute - > value ( ) ;
2019-06-15 21:08:36 +02:00
}
2021-10-16 16:16:57 -04:00
// https://dom.spec.whatwg.org/#dom-element-setattribute
2021-02-19 19:13:08 +01:00
ExceptionOr < void > Element : : set_attribute ( const FlyString & name , const String & value )
2019-06-15 21:08:36 +02:00
{
2021-10-16 16:16:57 -04:00
// 1. If qualifiedName does not match the Name production in XML, then throw an "InvalidCharacterError" DOMException.
2021-02-19 19:13:08 +01:00
// FIXME: Proper name validation
if ( name . is_empty ( ) )
return InvalidCharacterError : : create ( " Attribute name must not be empty " ) ;
2021-10-16 16:16:57 -04:00
// 2. If this is in the HTML namespace and its node document is an HTML document, then set qualifiedName to qualifiedName in ASCII lowercase.
2021-10-28 08:58:11 -04:00
// FIXME: Handle the second condition, assume it is an HTML document for now.
bool insert_as_lowercase = namespace_uri ( ) = = Namespace : : HTML ;
2021-10-16 16:16:57 -04:00
// 3. Let attribute be the first attribute in this’ s attribute list whose qualified name is qualifiedName, and null otherwise.
auto * attribute = m_attributes - > get_attribute ( name ) ;
// 4. If attribute is null, create an attribute whose local name is qualifiedName, value is value, and node document is this’ s node document, then append this attribute to this, and then return.
if ( ! attribute ) {
2021-10-28 08:58:11 -04:00
auto new_attribute = Attribute : : create ( document ( ) , insert_as_lowercase ? name . to_lowercase ( ) : name , value ) ;
2021-10-16 16:16:57 -04:00
m_attributes - > append_attribute ( new_attribute ) ;
2021-10-28 08:58:11 -04:00
attribute = new_attribute . ptr ( ) ;
2021-10-16 16:16:57 -04:00
}
// 5. Change attribute to value.
else {
2019-06-15 21:08:36 +02:00
attribute - > set_value ( value ) ;
2021-10-16 16:16:57 -04:00
}
2019-10-06 10:09:55 +02:00
2021-10-28 08:58:11 -04:00
parse_attribute ( attribute - > local_name ( ) , value ) ;
2022-02-05 15:19:16 +01:00
// FIXME: Invalidate less.
document ( ) . invalidate_style ( ) ;
2021-02-19 19:13:08 +01:00
return { } ;
2019-06-15 21:08:36 +02:00
}
2022-02-05 18:21:49 +01:00
// https://dom.spec.whatwg.org/#validate-and-extract
static ExceptionOr < QualifiedName > validate_and_extract ( FlyString namespace_ , FlyString qualified_name )
{
// 1. If namespace is the empty string, then set it to null.
if ( namespace_ . is_empty ( ) )
namespace_ = { } ;
// FIXME: 2. Validate qualifiedName.
// 3. Let prefix be null.
FlyString prefix = { } ;
// 4. Let localName be qualifiedName.
auto local_name = qualified_name ;
// 5. If qualifiedName contains a U+003A (:), then strictly split the string on it and set prefix to the part before and localName to the part after.
if ( qualified_name . view ( ) . contains ( ' : ' ) ) {
auto parts = qualified_name . view ( ) . split_view ( ' : ' ) ;
// FIXME: Handle parts > 2
prefix = parts [ 0 ] ;
local_name = parts [ 1 ] ;
}
// 6. If prefix is non-null and namespace is null, then throw a "NamespaceError" DOMException.
if ( ! prefix . is_null ( ) & & namespace_ . is_null ( ) )
return NamespaceError : : create ( " Prefix is non-null and namespace is null. " ) ;
// 7. If prefix is "xml" and namespace is not the XML namespace, then throw a "NamespaceError" DOMException.
if ( prefix = = " xml " sv & & namespace_ ! = Namespace : : XML )
return NamespaceError : : create ( " Prefix is 'xml' and namespace is not the XML namespace. " ) ;
// 8. If either qualifiedName or prefix is "xmlns" and namespace is not the XMLNS namespace, then throw a "NamespaceError" DOMException.
if ( ( qualified_name = = " xmlns " sv | | prefix = = " xmlns " sv ) & & namespace_ ! = Namespace : : XMLNS )
return NamespaceError : : create ( " Either qualifiedName or prefix is 'xmlns' and namespace is not the XMLNS namespace. " ) ;
// 9. If namespace is the XMLNS namespace and neither qualifiedName nor prefix is "xmlns", then throw a "NamespaceError" DOMException.
if ( namespace_ = = Namespace : : XMLNS & & ! ( qualified_name = = " xmlns " sv | | prefix = = " xmlns " sv ) )
return NamespaceError : : create ( " Namespace is the XMLNS namespace and neither qualifiedName nor prefix is 'xmlns'. " ) ;
// 10. Return namespace, prefix, and localName.
return QualifiedName { namespace_ , prefix , local_name } ;
}
// https://dom.spec.whatwg.org/#dom-element-setattributens
ExceptionOr < void > Element : : set_attribute_ns ( FlyString const & namespace_ , FlyString const & qualified_name , String const & value )
{
// 1. Let namespace, prefix, and localName be the result of passing namespace and qualifiedName to validate and extract.
auto result = validate_and_extract ( namespace_ , qualified_name ) ;
if ( result . is_exception ( ) )
return result . exception ( ) ;
// FIXME: 2. Set an attribute value for this using localName, value, and also prefix and namespace.
// FIXME: Don't just call through to setAttribute() here.
return set_attribute ( result . value ( ) . local_name ( ) , value ) ;
}
2021-10-16 16:16:57 -04:00
// https://dom.spec.whatwg.org/#dom-element-removeattribute
2020-08-02 16:15:15 +02:00
void Element : : remove_attribute ( const FlyString & name )
{
2021-10-16 16:16:57 -04:00
m_attributes - > remove_attribute ( name ) ;
2022-02-05 15:19:16 +01:00
2022-02-15 19:15:11 +01:00
did_remove_attribute ( name ) ;
2022-02-05 15:19:16 +01:00
// FIXME: Invalidate less.
document ( ) . invalidate_style ( ) ;
2021-10-16 16:16:57 -04:00
}
2020-12-14 21:31:10 +00:00
2021-10-16 16:16:57 -04:00
// https://dom.spec.whatwg.org/#dom-element-hasattribute
bool Element : : has_attribute ( const FlyString & name ) const
{
return m_attributes - > get_attribute ( name ) ! = nullptr ;
2020-08-02 16:15:15 +02:00
}
2021-12-29 13:02:21 +00:00
// https://dom.spec.whatwg.org/#dom-element-getattributenames
Vector < String > Element : : get_attribute_names ( ) const
{
// The getAttributeNames() method steps are to return the qualified names of the attributes in this’ s attribute list, in order; otherwise a new list.
Vector < String > names ;
for ( size_t i = 0 ; i < m_attributes - > length ( ) ; + + i ) {
auto const * attribute = m_attributes - > item ( i ) ;
names . append ( attribute - > name ( ) ) ;
}
return names ;
}
2021-02-08 01:06:20 +01:00
bool Element : : has_class ( const FlyString & class_name , CaseSensitivity case_sensitivity ) const
2019-06-27 20:40:21 +02:00
{
2021-07-25 14:21:27 -06:00
return any_of ( m_classes , [ & ] ( auto & it ) {
2021-02-08 01:06:20 +01:00
return case_sensitivity = = CaseSensitivity : : CaseSensitive
? it = = class_name
2021-10-28 09:03:06 -04:00
: it . equals_ignoring_case ( class_name ) ;
2021-02-08 01:06:20 +01:00
} ) ;
2019-06-27 20:40:21 +02:00
}
2019-10-05 22:27:52 +02:00
2022-02-05 13:17:01 +01:00
RefPtr < Layout : : Node > Element : : create_layout_node ( NonnullRefPtr < CSS : : StyleProperties > style )
2019-10-05 22:27:52 +02:00
{
2020-07-23 18:18:13 +02:00
if ( local_name ( ) = = " noscript " & & document ( ) . is_scripting_enabled ( ) )
2020-06-05 13:24:35 +02:00
return nullptr ;
2022-02-24 11:56:03 +00:00
auto display = style - > display ( ) ;
return create_layout_node_for_display_type ( document ( ) , display , move ( style ) , this ) ;
}
RefPtr < Layout : : Node > Element : : create_layout_node_for_display_type ( DOM : : Document & document , CSS : : Display const & display , NonnullRefPtr < CSS : : StyleProperties > style , Element * element )
{
2021-10-06 17:57:44 +02:00
if ( display . is_table_inside ( ) )
2022-02-24 11:56:03 +00:00
return adopt_ref ( * new Layout : : TableBox ( document , element , move ( style ) ) ) ;
2021-10-06 17:57:44 +02:00
if ( display . is_list_item ( ) )
2022-02-24 11:56:03 +00:00
return adopt_ref ( * new Layout : : ListItemBox ( document , element , move ( style ) ) ) ;
2021-10-06 17:57:44 +02:00
if ( display . is_table_row ( ) )
2022-02-24 11:56:03 +00:00
return adopt_ref ( * new Layout : : TableRowBox ( document , element , move ( style ) ) ) ;
2021-10-06 17:57:44 +02:00
if ( display . is_table_cell ( ) )
2022-02-24 11:56:03 +00:00
return adopt_ref ( * new Layout : : TableCellBox ( document , element , move ( style ) ) ) ;
2021-10-06 17:57:44 +02:00
if ( display . is_table_row_group ( ) | | display . is_table_header_group ( ) | | display . is_table_footer_group ( ) )
2022-02-24 11:56:03 +00:00
return adopt_ref ( * new Layout : : TableRowGroupBox ( document , element , move ( style ) ) ) ;
2021-10-06 17:57:44 +02:00
if ( display . is_table_column ( ) | | display . is_table_column_group ( ) | | display . is_table_caption ( ) ) {
2021-02-07 11:47:33 +01:00
// FIXME: This is just an incorrect placeholder until we improve table layout support.
2022-02-24 11:56:03 +00:00
return adopt_ref ( * new Layout : : BlockContainer ( document , element , move ( style ) ) ) ;
2021-02-07 11:40:42 +01:00
}
2021-10-06 17:57:44 +02:00
if ( display . is_inline_outside ( ) ) {
if ( display . is_flow_root_inside ( ) ) {
2022-02-24 11:56:03 +00:00
auto block = adopt_ref ( * new Layout : : BlockContainer ( document , element , move ( style ) ) ) ;
2021-10-06 17:57:44 +02:00
block - > set_inline ( true ) ;
return block ;
}
if ( display . is_flow_inside ( ) )
2022-02-24 11:56:03 +00:00
return adopt_ref ( * new Layout : : InlineNode ( document , element , move ( style ) ) ) ;
2021-10-06 17:57:44 +02:00
2022-02-13 01:02:00 +01:00
dbgln_if ( LIBWEB_CSS_DEBUG , " FIXME: Support display: {} " , display . to_string ( ) ) ;
2022-02-24 11:56:03 +00:00
return adopt_ref ( * new Layout : : InlineNode ( document , element , move ( style ) ) ) ;
2021-10-06 17:57:44 +02:00
}
if ( display . is_flow_inside ( ) | | display . is_flow_root_inside ( ) | | display . is_flex_inside ( ) )
2022-02-24 11:56:03 +00:00
return adopt_ref ( * new Layout : : BlockContainer ( document , element , move ( style ) ) ) ;
2021-10-06 17:57:44 +02:00
TODO ( ) ;
2019-10-05 22:27:52 +02:00
}
2019-10-06 10:09:55 +02:00
2020-05-26 23:07:19 +02:00
void Element : : parse_attribute ( const FlyString & name , const String & value )
2019-10-06 10:09:55 +02:00
{
2020-12-07 19:56:53 +01:00
if ( name = = HTML : : AttributeNames : : class_ ) {
2022-02-25 16:48:05 +01:00
auto new_classes = value . split_view ( is_ascii_space ) ;
2020-05-26 23:07:19 +02:00
m_classes . clear ( ) ;
m_classes . ensure_capacity ( new_classes . size ( ) ) ;
for ( auto & new_class : new_classes ) {
m_classes . unchecked_append ( new_class ) ;
}
2021-10-18 13:21:23 -04:00
if ( m_class_list )
m_class_list - > associated_attribute_changed ( value ) ;
2020-12-07 19:56:53 +01:00
} else if ( name = = HTML : : AttributeNames : : style ) {
2021-07-23 10:35:39 +02:00
auto parsed_style = parse_css_declaration ( CSS : : ParsingContext ( document ( ) ) , value ) ;
if ( ! parsed_style . is_null ( ) ) {
m_inline_style = CSS : : ElementInlineCSSStyleDeclaration : : create_and_take_properties_from ( * this , parsed_style . release_nonnull ( ) ) ;
set_needs_style_update ( true ) ;
}
2020-05-26 23:07:19 +02:00
}
2019-10-06 10:09:55 +02:00
}
2019-10-14 18:32:02 +02:00
enum class StyleDifference {
None ,
NeedsRepaint ,
NeedsRelayout ,
} ;
2021-09-16 19:20:20 +01:00
static StyleDifference compute_style_difference ( CSS : : StyleProperties const & old_style , CSS : : StyleProperties const & new_style , Layout : : NodeWithStyle const & node )
2019-10-14 18:32:02 +02:00
{
if ( old_style = = new_style )
return StyleDifference : : None ;
bool needs_repaint = false ;
bool needs_relayout = false ;
2020-06-24 16:22:16 +02:00
if ( new_style . display ( ) ! = old_style . display ( ) )
2020-06-15 17:52:08 +02:00
needs_relayout = true ;
2021-09-16 19:20:20 +01:00
if ( new_style . color_or_fallback ( CSS : : PropertyID : : Color , node , Color : : Black ) ! = old_style . color_or_fallback ( CSS : : PropertyID : : Color , node , Color : : Black ) )
2019-10-14 18:32:02 +02:00
needs_repaint = true ;
2021-09-16 19:20:20 +01:00
else if ( new_style . color_or_fallback ( CSS : : PropertyID : : BackgroundColor , node , Color : : Black ) ! = old_style . color_or_fallback ( CSS : : PropertyID : : BackgroundColor , node , Color : : Black ) )
2019-10-14 18:32:02 +02:00
needs_repaint = true ;
if ( needs_relayout )
return StyleDifference : : NeedsRelayout ;
if ( needs_repaint )
return StyleDifference : : NeedsRepaint ;
return StyleDifference : : None ;
}
void Element : : recompute_style ( )
{
2019-10-19 18:57:02 +02:00
set_needs_style_update ( false ) ;
2021-02-23 20:42:32 +01:00
VERIFY ( parent ( ) ) ;
2021-01-06 12:44:58 +01:00
auto old_specified_css_values = m_specified_css_values ;
2021-09-24 13:49:57 +02:00
auto new_specified_css_values = document ( ) . style_computer ( ) . compute_style ( * this ) ;
2021-01-06 12:44:58 +01:00
m_specified_css_values = new_specified_css_values ;
2019-10-19 18:57:02 +02:00
if ( ! layout_node ( ) ) {
2021-10-06 17:57:44 +02:00
if ( new_specified_css_values - > display ( ) . is_none ( ) )
2019-10-19 18:57:02 +02:00
return ;
// We need a new layout tree here!
2020-11-25 20:29:03 +01:00
Layout : : TreeBuilder tree_builder ;
2021-12-02 12:54:34 +00:00
( void ) tree_builder . build ( * this ) ;
2019-10-19 18:57:02 +02:00
return ;
}
2020-06-05 21:47:33 +02:00
2021-01-06 12:44:58 +01:00
auto diff = StyleDifference : : NeedsRelayout ;
2021-09-16 19:20:20 +01:00
if ( old_specified_css_values & & layout_node ( ) )
diff = compute_style_difference ( * old_specified_css_values , * new_specified_css_values , * layout_node ( ) ) ;
2019-10-14 18:32:02 +02:00
if ( diff = = StyleDifference : : None )
return ;
2021-01-06 12:44:58 +01:00
layout_node ( ) - > apply_style ( * new_specified_css_values ) ;
2019-10-14 18:32:02 +02:00
if ( diff = = StyleDifference : : NeedsRelayout ) {
2021-10-05 22:30:53 +02:00
document ( ) . set_needs_layout ( ) ;
2020-06-15 17:52:08 +02:00
return ;
2019-10-14 18:32:02 +02:00
}
if ( diff = = StyleDifference : : NeedsRepaint ) {
layout_node ( ) - > set_needs_display ( ) ;
}
}
2020-01-04 03:09:22 +01:00
2020-07-26 20:01:35 +02:00
NonnullRefPtr < CSS : : StyleProperties > Element : : computed_style ( )
2020-01-04 03:09:22 +01:00
{
2021-09-23 12:35:56 +02:00
auto element_computed_style = CSS : : ResolvedCSSStyleDeclaration : : create ( * this ) ;
2021-09-13 21:45:27 +02:00
auto properties = CSS : : StyleProperties : : create ( ) ;
for ( auto i = to_underlying ( CSS : : first_property_id ) ; i < = to_underlying ( CSS : : last_property_id ) ; + + i ) {
auto property_id = ( CSS : : PropertyID ) i ;
auto maybe_value = element_computed_style - > property ( property_id ) ;
if ( ! maybe_value . has_value ( ) )
continue ;
properties - > set_property ( property_id , maybe_value . release_value ( ) . value ) ;
2020-01-04 03:09:22 +01:00
}
2021-09-13 21:45:27 +02:00
2020-01-05 17:38:52 +01:00
return properties ;
2020-01-04 03:09:22 +01:00
}
2020-03-07 10:27:02 +01:00
2021-10-18 13:21:23 -04:00
RefPtr < DOMTokenList > const & Element : : class_list ( )
{
if ( ! m_class_list )
m_class_list = DOMTokenList : : create ( * this , HTML : : AttributeNames : : class_ ) ;
return m_class_list ;
}
2021-09-30 02:16:36 +02:00
// https://dom.spec.whatwg.org/#dom-element-matches
DOM : : ExceptionOr < bool > Element : : matches ( StringView selectors ) const
{
auto maybe_selectors = parse_selector ( CSS : : ParsingContext ( static_cast < ParentNode & > ( const_cast < Element & > ( * this ) ) ) , selectors ) ;
if ( ! maybe_selectors . has_value ( ) )
return DOM : : SyntaxError : : create ( " Failed to parse selector " ) ;
auto sel = maybe_selectors . value ( ) ;
for ( auto & s : sel ) {
if ( SelectorEngine : : matches ( s , * this ) )
return true ;
}
return false ;
}
2022-02-15 20:41:51 +01:00
// https://dom.spec.whatwg.org/#dom-element-closest
DOM : : ExceptionOr < DOM : : Element const * > Element : : closest ( StringView selectors ) const
{
auto maybe_selectors = parse_selector ( CSS : : ParsingContext ( static_cast < ParentNode & > ( const_cast < Element & > ( * this ) ) ) , selectors ) ;
if ( ! maybe_selectors . has_value ( ) )
return DOM : : SyntaxError : : create ( " Failed to parse selector " ) ;
auto matches_selectors = [ ] ( CSS : : SelectorList const & selector_list , Element const * element ) {
for ( auto & selector : selector_list ) {
if ( ! SelectorEngine : : matches ( selector , * element ) )
return false ;
}
return true ;
} ;
auto const selector_list = maybe_selectors . release_value ( ) ;
for ( auto * element = this ; element ; element = element - > parent_element ( ) ) {
if ( ! matches_selectors ( selector_list , element ) )
continue ;
return element ;
}
return nullptr ;
}
2021-09-13 22:42:57 +01:00
ExceptionOr < void > Element : : set_inner_html ( String const & markup )
2020-03-25 18:53:20 +01:00
{
2021-10-02 22:45:37 +02:00
auto result = DOMParsing : : inner_html_setter ( * this , markup ) ;
2021-09-13 22:42:57 +01:00
if ( result . is_exception ( ) )
return result . exception ( ) ;
2020-03-25 18:53:20 +01:00
set_needs_style_update ( true ) ;
2021-09-13 22:42:57 +01:00
return { } ;
2020-03-25 18:53:20 +01:00
}
2021-09-13 22:42:15 +01:00
// https://w3c.github.io/DOM-Parsing/#dom-innerhtml-innerhtml
2020-03-25 18:53:20 +01:00
String Element : : inner_html ( ) const
{
2021-09-13 22:42:15 +01:00
return serialize_fragment ( /* FIXME: Providing true for the require well-formed flag (which may throw) */ ) ;
2020-03-25 18:53:20 +01:00
}
2020-08-14 19:40:37 +02:00
bool Element : : is_focused ( ) const
{
return document ( ) . focused_element ( ) = = this ;
}
2021-06-18 16:42:34 -06:00
bool Element : : is_active ( ) const
{
return document ( ) . active_element ( ) = = this ;
}
2021-04-22 21:11:20 +02:00
NonnullRefPtr < HTMLCollection > Element : : get_elements_by_class_name ( FlyString const & class_name )
2021-02-07 23:44:01 +01:00
{
2021-04-22 21:11:20 +02:00
return HTMLCollection : : create ( * this , [ class_name , quirks_mode = document ( ) . in_quirks_mode ( ) ] ( Element const & element ) {
return element . has_class ( class_name , quirks_mode ? CaseSensitivity : : CaseInsensitive : CaseSensitivity : : CaseSensitive ) ;
2021-02-07 23:44:01 +01:00
} ) ;
}
2021-02-10 18:21:35 +01:00
void Element : : set_shadow_root ( RefPtr < ShadowRoot > shadow_root )
{
if ( m_shadow_root = = shadow_root )
return ;
m_shadow_root = move ( shadow_root ) ;
invalidate_style ( ) ;
}
2021-03-13 22:39:55 +01:00
NonnullRefPtr < CSS : : CSSStyleDeclaration > Element : : style_for_bindings ( )
{
if ( ! m_inline_style )
2021-03-16 18:55:53 +01:00
m_inline_style = CSS : : ElementInlineCSSStyleDeclaration : : create ( * this ) ;
2021-03-13 22:39:55 +01:00
return * m_inline_style ;
}
2021-05-04 22:25:43 +01:00
// https://dom.spec.whatwg.org/#element-html-uppercased-qualified-name
void Element : : make_html_uppercased_qualified_name ( )
{
// This is allowed by the spec: "User agents could optimize qualified name and HTML-uppercased qualified name by storing them in internal slots."
if ( namespace_ ( ) = = Namespace : : HTML /* FIXME: and its node document is an HTML document */ )
m_html_uppercased_qualified_name = qualified_name ( ) . to_uppercase ( ) ;
else
m_html_uppercased_qualified_name = qualified_name ( ) ;
}
2021-09-09 00:59:09 +02:00
// https://html.spec.whatwg.org/multipage/webappapis.html#queue-an-element-task
void Element : : queue_an_element_task ( HTML : : Task : : Source source , Function < void ( ) > steps )
{
auto task = HTML : : Task : : create ( source , & document ( ) , [ strong_this = NonnullRefPtr ( * this ) , steps = move ( steps ) ] {
steps ( ) ;
} ) ;
HTML : : main_thread_event_loop ( ) . task_queue ( ) . add ( move ( task ) ) ;
}
2021-09-13 22:42:15 +01:00
// https://html.spec.whatwg.org/multipage/syntax.html#void-elements
bool Element : : is_void_element ( ) const
{
return local_name ( ) . is_one_of ( HTML : : TagNames : : area , HTML : : TagNames : : base , HTML : : TagNames : : br , HTML : : TagNames : : col , HTML : : TagNames : : embed , HTML : : TagNames : : hr , HTML : : TagNames : : img , HTML : : TagNames : : input , HTML : : TagNames : : link , HTML : : TagNames : : meta , HTML : : TagNames : : param , HTML : : TagNames : : source , HTML : : TagNames : : track , HTML : : TagNames : : wbr ) ;
}
// https://html.spec.whatwg.org/multipage/parsing.html#serializes-as-void
bool Element : : serializes_as_void ( ) const
{
return is_void_element ( ) | | local_name ( ) . is_one_of ( HTML : : TagNames : : basefont , HTML : : TagNames : : bgsound , HTML : : TagNames : : frame , HTML : : TagNames : : keygen ) ;
}
2021-09-27 00:55:13 +02:00
// https://drafts.csswg.org/cssom-view/#dom-element-getboundingclientrect
NonnullRefPtr < Geometry : : DOMRect > Element : : get_bounding_client_rect ( ) const
{
// FIXME: Support inline layout nodes as well.
if ( ! layout_node ( ) | | ! layout_node ( ) - > is_box ( ) )
return Geometry : : DOMRect : : create ( 0 , 0 , 0 , 0 ) ;
VERIFY ( document ( ) . browsing_context ( ) ) ;
auto viewport_offset = document ( ) . browsing_context ( ) - > viewport_scroll_offset ( ) ;
auto & box = static_cast < Layout : : Box const & > ( * layout_node ( ) ) ;
return Geometry : : DOMRect : : create ( box . absolute_rect ( ) . translated ( - viewport_offset . x ( ) , - viewport_offset . y ( ) ) ) ;
}
2022-02-12 16:48:00 +03:00
// https://drafts.csswg.org/cssom-view/#dom-element-getclientrects
NonnullRefPtr < Geometry : : DOMRectList > Element : : get_client_rects ( ) const
{
NonnullRefPtrVector < Geometry : : DOMRect > rects ;
// 1. If the element on which it was invoked does not have an associated layout box return an empty DOMRectList object and stop this algorithm.
if ( ! layout_node ( ) | | ! layout_node ( ) - > is_box ( ) )
return Geometry : : DOMRectList : : create ( move ( rects ) ) ;
// FIXME: 2. If the element has an associated SVG layout box return a DOMRectList object containing a single DOMRect object that describes
// the bounding box of the element as defined by the SVG specification, applying the transforms that apply to the element and its ancestors.
// FIXME: 3. Return a DOMRectList object containing DOMRect objects in content order, one for each box fragment,
// describing its border area (including those with a height or width of zero) with the following constraints:
// - Apply the transforms that apply to the element and its ancestors.
// - If the element on which the method was invoked has a computed value for the display property of table
// or inline-table include both the table box and the caption box, if any, but not the anonymous container box.
// - Replace each anonymous block box with its child box(es) and repeat this until no anonymous block boxes are left in the final list.
auto bounding_rect = get_bounding_client_rect ( ) ;
rects . append ( bounding_rect ) ;
return Geometry : : DOMRectList : : create ( move ( rects ) ) ;
}
2021-09-30 02:17:23 +02:00
int Element : : client_top ( ) const
{
if ( ! layout_node ( ) | | ! layout_node ( ) - > is_box ( ) )
return 0 ;
auto & box = static_cast < Layout : : Box const & > ( * layout_node ( ) ) ;
return box . absolute_rect ( ) . top ( ) ;
}
int Element : : client_left ( ) const
{
if ( ! layout_node ( ) | | ! layout_node ( ) - > is_box ( ) )
return 0 ;
auto & box = static_cast < Layout : : Box const & > ( * layout_node ( ) ) ;
return box . absolute_rect ( ) . left ( ) ;
}
int Element : : client_width ( ) const
{
if ( ! layout_node ( ) | | ! layout_node ( ) - > is_box ( ) )
return 0 ;
auto & box = static_cast < Layout : : Box const & > ( * layout_node ( ) ) ;
return box . absolute_rect ( ) . width ( ) ;
}
int Element : : client_height ( ) const
{
if ( ! layout_node ( ) | | ! layout_node ( ) - > is_box ( ) )
return 0 ;
auto & box = static_cast < Layout : : Box const & > ( * layout_node ( ) ) ;
return box . absolute_rect ( ) . height ( ) ;
}
2021-10-12 17:53:52 +02:00
void Element : : children_changed ( )
{
Node : : children_changed ( ) ;
set_needs_style_update ( true ) ;
}
2020-03-07 10:27:02 +01:00
}