2020-01-18 09:38:21 +01:00
|
|
|
/*
|
2024-10-04 13:19:50 +02:00
|
|
|
* Copyright (c) 2018-2022, Andreas Kling <andreas@ladybird.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
|
|
|
*/
|
|
|
|
|
2019-09-29 11:59:38 +02:00
|
|
|
#pragma once
|
|
|
|
|
2024-12-06 23:10:03 +00:00
|
|
|
#include <AK/Optional.h>
|
2020-03-07 10:32:51 +01:00
|
|
|
#include <LibWeb/DOM/Element.h>
|
2021-02-03 22:47:50 +01:00
|
|
|
#include <LibWeb/HTML/EventNames.h>
|
|
|
|
#include <LibWeb/HTML/GlobalEventHandlers.h>
|
2024-10-29 11:07:02 +01:00
|
|
|
#include <LibWeb/HTML/HTMLOrSVGElement.h>
|
2024-12-06 23:10:03 +00:00
|
|
|
#include <LibWeb/HTML/ToggleTaskTracker.h>
|
2023-06-18 16:22:10 +01:00
|
|
|
#include <LibWeb/HTML/TokenizedFeatures.h>
|
2019-09-29 11:59:38 +02:00
|
|
|
|
2020-07-28 18:20:36 +02:00
|
|
|
namespace Web::HTML {
|
2020-03-07 10:27:02 +01:00
|
|
|
|
2022-11-04 22:56:42 -03:00
|
|
|
// https://html.spec.whatwg.org/multipage/dom.html#attr-dir
|
|
|
|
#define ENUMERATE_HTML_ELEMENT_DIR_ATTRIBUTES \
|
|
|
|
__ENUMERATE_HTML_ELEMENT_DIR_ATTRIBUTE(ltr) \
|
|
|
|
__ENUMERATE_HTML_ELEMENT_DIR_ATTRIBUTE(rtl) \
|
|
|
|
__ENUMERATE_HTML_ELEMENT_DIR_ATTRIBUTE(auto)
|
|
|
|
|
2025-02-04 13:01:46 +01:00
|
|
|
// https://html.spec.whatwg.org/multipage/interaction.html#attr-contenteditable
|
2024-12-02 11:51:55 +01:00
|
|
|
enum class ContentEditableState {
|
|
|
|
True,
|
|
|
|
False,
|
|
|
|
PlaintextOnly,
|
|
|
|
Inherit,
|
|
|
|
};
|
|
|
|
|
2024-12-05 23:24:24 +00:00
|
|
|
struct ShowPopoverOptions {
|
|
|
|
GC::Ptr<HTMLElement> source;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct TogglePopoverOptions : public ShowPopoverOptions {
|
|
|
|
Optional<bool> force {};
|
|
|
|
};
|
|
|
|
|
|
|
|
using TogglePopoverOptionsOrForceBoolean = Variant<TogglePopoverOptions, bool>;
|
|
|
|
|
|
|
|
enum class ThrowExceptions {
|
|
|
|
Yes,
|
|
|
|
No,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class FocusPreviousElement {
|
|
|
|
Yes,
|
|
|
|
No,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class FireEvents {
|
|
|
|
Yes,
|
|
|
|
No,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class ExpectedToBeShowing {
|
|
|
|
Yes,
|
|
|
|
No,
|
|
|
|
};
|
|
|
|
|
2025-01-14 11:06:06 +11:00
|
|
|
enum class IgnoreDomState {
|
|
|
|
Yes,
|
|
|
|
No,
|
|
|
|
};
|
|
|
|
|
2025-02-04 10:23:09 +11:00
|
|
|
enum class IsPopover {
|
|
|
|
Yes,
|
|
|
|
No,
|
|
|
|
};
|
|
|
|
|
2021-02-03 22:47:50 +01:00
|
|
|
class HTMLElement
|
|
|
|
: public DOM::Element
|
2024-10-29 11:07:02 +01:00
|
|
|
, public HTML::GlobalEventHandlers
|
|
|
|
, public HTML::HTMLOrSVGElement<HTMLElement> {
|
2022-08-28 13:42:07 +02:00
|
|
|
WEB_PLATFORM_OBJECT(HTMLElement, DOM::Element);
|
2024-11-15 04:01:23 +13:00
|
|
|
GC_DECLARE_ALLOCATOR(HTMLElement);
|
2020-06-21 14:35:00 +02:00
|
|
|
|
2022-08-28 13:42:07 +02:00
|
|
|
public:
|
2019-09-29 11:59:38 +02:00
|
|
|
virtual ~HTMLElement() override;
|
2019-09-29 12:24:36 +02:00
|
|
|
|
2023-10-10 15:00:58 +03:30
|
|
|
Optional<String> title() const { return attribute(HTML::AttributeNames::title); }
|
2019-09-29 12:24:36 +02:00
|
|
|
|
2023-09-03 16:42:25 +12:00
|
|
|
StringView dir() const;
|
|
|
|
void set_dir(String const&);
|
2022-11-04 22:56:42 -03:00
|
|
|
|
2024-02-23 21:29:43 +01:00
|
|
|
virtual bool is_focusable() const override;
|
2024-02-25 07:00:04 +01:00
|
|
|
bool is_content_editable() const;
|
2023-09-03 16:42:25 +12:00
|
|
|
StringView content_editable() const;
|
2024-12-02 11:51:55 +01:00
|
|
|
ContentEditableState content_editable_state() const { return m_content_editable_state; }
|
2023-09-03 16:42:25 +12:00
|
|
|
WebIDL::ExceptionOr<void> set_content_editable(StringView);
|
2020-08-03 02:57:28 +01:00
|
|
|
|
2023-09-03 16:42:25 +12:00
|
|
|
String inner_text();
|
2020-11-11 09:46:53 +00:00
|
|
|
void set_inner_text(StringView);
|
|
|
|
|
2024-04-14 18:26:44 +02:00
|
|
|
[[nodiscard]] String outer_text();
|
2024-11-11 05:33:44 +13:00
|
|
|
WebIDL::ExceptionOr<void> set_outer_text(String const&);
|
2024-04-14 18:26:44 +02:00
|
|
|
|
2021-09-30 01:35:19 +02:00
|
|
|
int offset_top() const;
|
|
|
|
int offset_left() const;
|
|
|
|
int offset_width() const;
|
|
|
|
int offset_height() const;
|
2024-11-15 04:01:23 +13:00
|
|
|
GC::Ptr<Element> offset_parent() const;
|
2021-04-15 20:48:55 +03:00
|
|
|
|
2020-11-21 21:53:18 +00:00
|
|
|
bool cannot_navigate() const;
|
|
|
|
|
2025-01-24 00:53:31 +00:00
|
|
|
Variant<bool, double, String> hidden() const;
|
|
|
|
void set_hidden(Variant<bool, double, String> const&);
|
|
|
|
|
2022-02-15 00:25:51 +01:00
|
|
|
void click();
|
|
|
|
|
2024-04-14 17:55:48 +02:00
|
|
|
[[nodiscard]] String access_key_label() const;
|
|
|
|
|
2023-04-09 11:26:59 +02:00
|
|
|
bool fire_a_synthetic_pointer_event(FlyString const& type, DOM::Element& target, bool not_trusted);
|
2022-02-25 20:45:19 +01:00
|
|
|
|
2022-03-01 21:03:30 +00:00
|
|
|
// https://html.spec.whatwg.org/multipage/forms.html#category-label
|
|
|
|
virtual bool is_labelable() const { return false; }
|
|
|
|
|
2024-11-15 04:01:23 +13:00
|
|
|
GC::Ptr<DOM::NodeList> labels();
|
2024-05-18 14:10:00 +01:00
|
|
|
|
2023-01-28 22:23:16 +00:00
|
|
|
virtual Optional<ARIA::Role> default_role() const override;
|
2022-11-28 17:58:13 -06:00
|
|
|
|
2025-01-29 16:48:10 +00:00
|
|
|
String get_an_elements_target(Optional<String> target = {}) const;
|
2025-01-29 16:56:55 +00:00
|
|
|
TokenizedFeature::NoOpener get_an_elements_noopener(URL::URL const& url, StringView target) const;
|
2023-06-18 16:22:10 +01:00
|
|
|
|
2024-11-15 04:01:23 +13:00
|
|
|
WebIDL::ExceptionOr<GC::Ref<ElementInternals>> attach_internals();
|
2024-06-24 21:54:42 +01:00
|
|
|
|
2024-07-01 19:55:33 +01:00
|
|
|
WebIDL::ExceptionOr<void> set_popover(Optional<String> value);
|
|
|
|
Optional<String> popover() const;
|
2025-02-04 10:23:09 +11:00
|
|
|
Optional<String> opened_in_popover_mode() const { return m_opened_in_popover_mode; }
|
2024-07-01 19:55:33 +01:00
|
|
|
|
2025-01-23 17:37:18 +01:00
|
|
|
virtual void removed_from(Node* old_parent, Node& old_root) override;
|
2024-12-06 13:05:29 +00:00
|
|
|
|
2024-12-05 23:24:24 +00:00
|
|
|
enum class PopoverVisibilityState {
|
|
|
|
Hidden,
|
|
|
|
Showing,
|
|
|
|
};
|
|
|
|
PopoverVisibilityState popover_visibility_state() const { return m_popover_visibility_state; }
|
|
|
|
|
|
|
|
WebIDL::ExceptionOr<void> show_popover_for_bindings(ShowPopoverOptions const& = {});
|
|
|
|
WebIDL::ExceptionOr<void> hide_popover_for_bindings();
|
|
|
|
WebIDL::ExceptionOr<bool> toggle_popover(TogglePopoverOptionsOrForceBoolean const&);
|
|
|
|
|
2025-01-14 11:06:06 +11:00
|
|
|
WebIDL::ExceptionOr<bool> check_popover_validity(ExpectedToBeShowing expected_to_be_showing, ThrowExceptions throw_exceptions, GC::Ptr<DOM::Document>, IgnoreDomState ignore_dom_state);
|
2024-12-05 23:24:24 +00:00
|
|
|
WebIDL::ExceptionOr<void> show_popover(ThrowExceptions throw_exceptions, GC::Ptr<HTMLElement> invoker);
|
2025-01-14 11:06:06 +11:00
|
|
|
WebIDL::ExceptionOr<void> hide_popover(FocusPreviousElement focus_previous_element, FireEvents fire_events, ThrowExceptions throw_exceptions, IgnoreDomState ignore_dom_state);
|
2024-12-05 23:24:24 +00:00
|
|
|
|
2025-02-04 10:23:09 +11:00
|
|
|
static void hide_all_popovers_until(Variant<GC::Ptr<HTMLElement>, GC::Ptr<DOM::Document>> endpoint, FocusPreviousElement focus_previous_element, FireEvents fire_events);
|
|
|
|
static GC::Ptr<HTMLElement> topmost_popover_ancestor(GC::Ptr<DOM::Node> new_popover_or_top_layer_element, Vector<GC::Ref<HTMLElement>> const& popover_list, GC::Ptr<HTMLElement> invoker, IsPopover is_popover);
|
|
|
|
|
2025-04-13 18:58:40 +10:00
|
|
|
static void light_dismiss_open_popovers(UIEvents::PointerEvent const&, GC::Ptr<DOM::Node>);
|
|
|
|
|
2025-02-05 23:24:47 +00:00
|
|
|
bool is_inert() const { return m_inert; }
|
|
|
|
|
2025-04-04 14:30:56 +02:00
|
|
|
virtual bool is_valid_invoker_command(String&) { return false; }
|
|
|
|
virtual void invoker_command_steps(DOM::Element&, String&) { }
|
|
|
|
|
2021-02-03 22:47:50 +01:00
|
|
|
protected:
|
2022-08-28 13:42:07 +02:00
|
|
|
HTMLElement(DOM::Document&, DOM::QualifiedName);
|
|
|
|
|
2023-08-07 08:41:28 +02:00
|
|
|
virtual void initialize(JS::Realm&) override;
|
2022-09-03 18:49:56 +02:00
|
|
|
|
2024-11-14 08:14:16 -05:00
|
|
|
virtual void attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_) override;
|
2025-01-11 17:37:08 +00:00
|
|
|
virtual WebIDL::ExceptionOr<void> cloned(DOM::Node&, bool) const override;
|
2024-10-29 13:27:01 +01:00
|
|
|
virtual void inserted() override;
|
2021-02-03 22:47:50 +01:00
|
|
|
|
2022-08-28 13:42:07 +02:00
|
|
|
virtual void visit_edges(Cell::Visitor&) override;
|
|
|
|
|
2025-02-04 19:09:14 +01:00
|
|
|
// https://html.spec.whatwg.org/multipage/dom.html#block-rendering
|
|
|
|
void block_rendering();
|
|
|
|
// https://html.spec.whatwg.org/multipage/dom.html#unblock-rendering
|
|
|
|
void unblock_rendering();
|
|
|
|
// https://html.spec.whatwg.org/multipage/urls-and-fetching.html#potentially-render-blocking
|
|
|
|
bool is_potentially_render_blocking();
|
|
|
|
// https://html.spec.whatwg.org/multipage/urls-and-fetching.html#implicitly-potentially-render-blocking
|
|
|
|
virtual bool is_implicitly_potentially_render_blocking() const { return false; }
|
|
|
|
|
2025-02-05 23:24:47 +00:00
|
|
|
void set_inert(bool inert) { m_inert = inert; }
|
|
|
|
void set_subtree_inertness(bool is_inert);
|
|
|
|
|
2019-09-29 12:24:36 +02:00
|
|
|
private:
|
2022-07-27 16:04:31 +02:00
|
|
|
virtual bool is_html_element() const final { return true; }
|
|
|
|
|
2024-12-20 11:32:17 +01:00
|
|
|
virtual void adjust_computed_style(CSS::ComputedProperties&) override;
|
2024-11-08 20:14:37 +08:00
|
|
|
|
2021-02-03 22:47:50 +01:00
|
|
|
// ^HTML::GlobalEventHandlers
|
2024-11-15 04:01:23 +13:00
|
|
|
virtual GC::Ptr<DOM::EventTarget> global_event_handlers_to_event_target(FlyString const&) override { return *this; }
|
2024-02-24 02:43:57 +01:00
|
|
|
virtual void did_receive_focus() override;
|
LibWeb: Separate text control input events handling from contenteditable
This input event handling change is intended to address the following
design issues:
- Having `DOM::Position` is unnecessary complexity when `Selection`
exists because caret position could be described by the selection
object with a collapsed state. Before this change, we had to
synchronize those whenever one of them was modified, and there were
already bugs caused by that, i.e., caret position was not changed when
selection offset was modified from the JS side.
- Selection API exposes selection offset within `<textarea>` and
`<input>`, which is not supposed to happen. These objects should
manage their selection state by themselves and have selection offset
even when they are not displayed.
- `EventHandler` looks only at `DOM::Text` owned by `DOM::Position`
while doing text manipulations. It works fine for `<input>` and
`<textarea>`, but `contenteditable` needs to consider all text
descendant text nodes; i.e., if the cursor is moved outside of
`DOM::Text`, we need to look for an adjacent text node to move the
cursor there.
With this change, `EventHandler` no longer does direct manipulations on
caret position or text content, but instead delegates them to the active
`InputEventsTarget`, which could be either
`FormAssociatedTextControlElement` (for `<input>` and `<textarea>`) or
`EditingHostManager` (for `contenteditable`). The `Selection` object is
used to manage both selection and caret position for `contenteditable`,
and text control elements manage their own selection state that is not
exposed by Selection API.
This change improves text editing on Discord, as now we don't have to
refocus the `contenteditable` element after character input. The problem
was that selection manipulations from the JS side were not propagated
to `DOM::Position`.
I expect this change to make future correctness improvements for
`contenteditable` (and `designMode`) easier, as now it's decoupled from
`<input>` and `<textarea>` and separated from `EventHandler`, which is
quite a busy file.
2024-10-23 21:26:58 +02:00
|
|
|
virtual void did_lose_focus() override;
|
2021-02-03 22:47:50 +01:00
|
|
|
|
2024-04-14 18:26:44 +02:00
|
|
|
[[nodiscard]] String get_the_text_steps();
|
2024-11-15 04:01:23 +13:00
|
|
|
GC::Ref<DOM::DocumentFragment> rendered_text_fragment(StringView input);
|
2024-04-14 18:26:44 +02:00
|
|
|
|
2024-11-15 04:01:23 +13:00
|
|
|
GC::Ptr<DOM::NodeList> m_labels;
|
2024-05-18 14:10:00 +01:00
|
|
|
|
2024-12-06 23:10:03 +00:00
|
|
|
void queue_a_popover_toggle_event_task(String old_state, String new_state);
|
|
|
|
|
2025-01-27 21:38:18 +11:00
|
|
|
static Optional<String> popover_value_to_state(Optional<String> value);
|
2025-02-04 10:23:09 +11:00
|
|
|
void hide_popover_stack_until(Vector<GC::Ref<HTMLElement>> const& popover_list, FocusPreviousElement focus_previous_element, FireEvents fire_events);
|
|
|
|
GC::Ptr<HTMLElement> nearest_inclusive_open_popover();
|
2025-04-13 18:58:40 +10:00
|
|
|
GC::Ptr<HTMLElement> nearest_inclusive_target_popover_for_invoker();
|
2025-02-04 10:23:09 +11:00
|
|
|
static void close_entire_popover_list(Vector<GC::Ref<HTMLElement>> const& popover_list, FocusPreviousElement focus_previous_element, FireEvents fire_events);
|
2025-04-13 18:58:40 +10:00
|
|
|
static GC::Ptr<HTMLElement> topmost_clicked_popover(GC::Ptr<DOM::Node> node);
|
|
|
|
size_t popover_stack_position();
|
2025-01-27 21:38:18 +11:00
|
|
|
|
2024-06-24 21:54:42 +01:00
|
|
|
// https://html.spec.whatwg.org/multipage/custom-elements.html#attached-internals
|
2024-11-15 04:01:23 +13:00
|
|
|
GC::Ptr<ElementInternals> m_attached_internals;
|
2024-06-24 21:54:42 +01:00
|
|
|
|
2025-02-04 13:01:46 +01:00
|
|
|
// https://html.spec.whatwg.org/multipage/interaction.html#attr-contenteditable
|
2023-05-15 10:08:13 +02:00
|
|
|
ContentEditableState m_content_editable_state { ContentEditableState::Inherit };
|
2021-09-26 15:24:41 +01:00
|
|
|
|
2022-02-15 00:25:51 +01:00
|
|
|
// https://html.spec.whatwg.org/multipage/interaction.html#click-in-progress-flag
|
|
|
|
bool m_click_in_progress { false };
|
2024-12-05 23:24:24 +00:00
|
|
|
|
2025-02-05 23:24:47 +00:00
|
|
|
bool m_inert { false };
|
|
|
|
|
2024-12-05 23:24:24 +00:00
|
|
|
// Popover API
|
|
|
|
|
|
|
|
// https://html.spec.whatwg.org/multipage/popover.html#popover-visibility-state
|
|
|
|
PopoverVisibilityState m_popover_visibility_state { PopoverVisibilityState::Hidden };
|
|
|
|
|
|
|
|
// https://html.spec.whatwg.org/multipage/popover.html#popover-invoker
|
|
|
|
GC::Ptr<HTMLElement> m_popover_invoker;
|
|
|
|
|
|
|
|
// https://html.spec.whatwg.org/multipage/popover.html#popover-showing-or-hiding
|
|
|
|
bool m_popover_showing_or_hiding { false };
|
2024-12-06 23:10:03 +00:00
|
|
|
|
|
|
|
// https://html.spec.whatwg.org/multipage/popover.html#the-popover-attribute:toggle-task-tracker
|
|
|
|
Optional<ToggleTaskTracker> m_popover_toggle_task_tracker;
|
2024-12-06 13:05:29 +00:00
|
|
|
|
|
|
|
// https://html.spec.whatwg.org/multipage/popover.html#popover-close-watcher
|
|
|
|
GC::Ptr<CloseWatcher> m_popover_close_watcher;
|
2025-02-04 10:23:09 +11:00
|
|
|
|
|
|
|
Optional<String> m_opened_in_popover_mode;
|
2019-09-29 11:59:38 +02:00
|
|
|
};
|
2019-10-06 20:37:39 +02:00
|
|
|
|
|
|
|
}
|
2022-07-27 16:04:31 +02:00
|
|
|
|
|
|
|
namespace Web::DOM {
|
|
|
|
template<>
|
|
|
|
inline bool Node::fast_is<HTML::HTMLElement>() const { return is_html_element(); }
|
|
|
|
}
|