/* * Copyright (c) 2018-2020, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Web::HTML { HTMLElement::HTMLElement(DOM::Document& document, QualifiedName qualified_name) : Element(document, move(qualified_name)) { } HTMLElement::~HTMLElement() { } HTMLElement::ContentEditableState HTMLElement::content_editable_state() const { auto contenteditable = attribute(HTML::AttributeNames::contenteditable); // "true" and the empty string map to the "true" state. if ((!contenteditable.is_null() && contenteditable.is_empty()) || contenteditable.equals_ignoring_case("true")) return ContentEditableState::True; // "false" maps to the "false" state. if (contenteditable.equals_ignoring_case("false")) return ContentEditableState::False; // "inherit", an invalid value, and a missing value all map to the "inherit" state. return ContentEditableState::Inherit; } bool HTMLElement::is_editable() const { switch (content_editable_state()) { case ContentEditableState::True: return true; case ContentEditableState::False: return false; case ContentEditableState::Inherit: return parent() && parent()->is_editable(); default: VERIFY_NOT_REACHED(); } } String HTMLElement::content_editable() const { switch (content_editable_state()) { case ContentEditableState::True: return "true"; case ContentEditableState::False: return "false"; case ContentEditableState::Inherit: return "inherit"; default: VERIFY_NOT_REACHED(); } } // https://html.spec.whatwg.org/multipage/interaction.html#contenteditable DOM::ExceptionOr HTMLElement::set_content_editable(const String& content_editable) { if (content_editable.equals_ignoring_case("inherit")) { remove_attribute(HTML::AttributeNames::contenteditable); return {}; } if (content_editable.equals_ignoring_case("true")) { set_attribute(HTML::AttributeNames::contenteditable, "true"); return {}; } if (content_editable.equals_ignoring_case("false")) { set_attribute(HTML::AttributeNames::contenteditable, "false"); return {}; } return DOM::SyntaxError::create("Invalid contentEditable value, must be 'true', 'false', or 'inherit'"); } void HTMLElement::set_inner_text(StringView text) { remove_all_children(); append_child(document().create_text_node(text)); set_needs_style_update(true); document().invalidate_layout(); } String HTMLElement::inner_text() { StringBuilder builder; // innerText for element being rendered takes visibility into account, so force a layout and then walk the layout tree. document().update_layout(); if (!layout_node()) return text_content(); Function recurse = [&](auto& node) { for (auto* child = node.first_child(); child; child = child->next_sibling()) { if (is(child)) builder.append(verify_cast(*child).text_for_rendering()); if (is(child)) builder.append('\n'); recurse(*child); } }; recurse(*layout_node()); return builder.to_string(); } unsigned HTMLElement::offset_top() const { if (is(this) || !layout_node() || !parent_element() || !parent_element()->layout_node()) return 0; auto position = layout_node()->box_type_agnostic_position(); auto parent_position = parent_element()->layout_node()->box_type_agnostic_position(); return position.y() - parent_position.y(); } unsigned HTMLElement::offset_left() const { if (is(this) || !layout_node() || !parent_element() || !parent_element()->layout_node()) return 0; auto position = layout_node()->box_type_agnostic_position(); auto parent_position = parent_element()->layout_node()->box_type_agnostic_position(); return position.x() - parent_position.x(); } bool HTMLElement::cannot_navigate() const { // FIXME: Return true if element's node document is not fully active return !is(this) && !is_connected(); } void HTMLElement::parse_attribute(const FlyString& name, const String& value) { Element::parse_attribute(name, value); #undef __ENUMERATE #define __ENUMERATE(attribute_name, event_name) \ if (name == HTML::AttributeNames::attribute_name) { \ set_event_handler_attribute(event_name, EventHandler { value }); \ } ENUMERATE_GLOBAL_EVENT_HANDLERS(__ENUMERATE) #undef __ENUMERATE } }