LibWeb: Implement user-select

This implements all values of user-select.
This commit is contained in:
Psychpsyo 2025-01-08 01:51:29 +01:00 committed by Sam Atkins
commit 9370990ff2
Notes: github-actions[bot] 2025-01-08 14:38:35 +00:00
11 changed files with 274 additions and 29 deletions

View file

@ -22,6 +22,7 @@
#include <LibWeb/CSS/StyleValues/URLStyleValue.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/Dump.h>
#include <LibWeb/HTML/FormAssociatedElement.h>
#include <LibWeb/HTML/HTMLHtmlElement.h>
#include <LibWeb/Layout/BlockContainer.h>
#include <LibWeb/Layout/FormattingContext.h>
@ -1004,6 +1005,9 @@ void NodeWithStyle::apply_style(const CSS::ComputedProperties& computed_style)
if (auto writing_mode = computed_style.writing_mode(); writing_mode.has_value())
computed_values.set_writing_mode(writing_mode.value());
if (auto user_select = computed_style.user_select(); user_select.has_value())
computed_values.set_user_select(user_select.value());
propagate_style_to_anonymous_wrappers();
}
@ -1214,4 +1218,53 @@ DOM::Document const& Node::document() const
return m_dom_node->document();
}
// https://drafts.csswg.org/css-ui/#propdef-user-select
CSS::UserSelect Node::user_select_used_value() const
{
// The used value is the same as the computed value, except:
auto computed_value = computed_values().user_select();
// 1. on editable elements where the used value is always 'contain' regardless of the computed value
// 2. when the computed value is 'auto', in which case the used value is one of the other values as defined below
// For the purpose of this specification, an editable element is either an editing host or a mutable form control with
// textual content, such as textarea.
auto* form_control = dynamic_cast<HTML::FormAssociatedTextControlElement const*>(dom_node());
// FIXME: Check if this needs to exclude input elements with types such as color or range, and if so, which ones exactly.
if ((dom_node() && dom_node()->is_editing_host()) || (form_control && form_control->is_mutable())) {
return CSS::UserSelect::Contain;
} else if (computed_value == CSS::UserSelect::Auto) {
// The used value of 'auto' is determined as follows:
// - On the '::before' and '::after' pseudo-elements, the used value is 'none'
if (is_generated_for_before_pseudo_element() || is_generated_for_after_pseudo_element()) {
return CSS::UserSelect::None;
}
// - If the element is an editable element, the used value is 'contain'
// NOTE: We already handled this above.
auto parent_element = parent();
if (parent_element) {
auto parent_used_value = parent_element->user_select_used_value();
// - Otherwise, if the used value of user-select on the parent of this element is 'all', the used value is 'all'
if (parent_used_value == CSS::UserSelect::All) {
return CSS::UserSelect::All;
}
// - Otherwise, if the used value of user-select on the parent of this element is 'none', the used value is
// 'none'
if (parent_used_value == CSS::UserSelect::None) {
return CSS::UserSelect::None;
}
}
// - Otherwise, the used value is 'text'
return CSS::UserSelect::Text;
}
return computed_value;
}
}