mirror of
https://github.com/servo/servo
synced 2026-05-01 03:47:53 +02:00
layout: Add placeholders for text carets after finishing a line (#44370)
Instead of readily adding a strut for text carets when processing a line break, wait until a line is finished. This allows placing a strut on the final line. In addition to fixing #41338 this will also make it possible to handle line breaks as a separate kind of text run item so that we can preserve shaping results between layouts. In addition, some changes are made in script to ensure that these placeholders are never placed for non-textual input elements. Testing: This change adds a Servo-specific WPT test. Caret rendering isn't specified. Fixes: #41338 Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
@@ -131,6 +131,10 @@ pub(crate) struct HTMLInputElement {
|
||||
htmlelement: HTMLElement,
|
||||
input_type: DomRefCell<InputType>,
|
||||
|
||||
/// Whether or not the [`InputType`] for this [`HTMLInputElement`] renders as
|
||||
/// textual input. This is cached so that it can be read during layout.
|
||||
is_textual_or_password: Cell<bool>,
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#concept-input-checked-dirty-flag>
|
||||
checked_changed: Cell<bool>,
|
||||
placeholder: DomRefCell<DOMString>,
|
||||
@@ -188,6 +192,7 @@ impl HTMLInputElement {
|
||||
document,
|
||||
),
|
||||
input_type: DomRefCell::new(InputType::new_text()),
|
||||
is_textual_or_password: Cell::new(true),
|
||||
placeholder: DomRefCell::new(DOMString::new()),
|
||||
checked_changed: Cell::new(false),
|
||||
maxlength: Cell::new(DEFAULT_MAX_LENGTH),
|
||||
@@ -826,25 +831,8 @@ impl HTMLInputElement {
|
||||
}
|
||||
|
||||
/// Whether this input type renders as a basic text input widget.
|
||||
///
|
||||
/// TODO(#38251): This should eventually only include `text`, `password`, `url`, `tel`,
|
||||
/// and `email`, but the others do not yet have a custom shadow DOM implementation.
|
||||
pub(crate) fn renders_as_text_input_widget(&self) -> bool {
|
||||
matches!(
|
||||
*self.input_type(),
|
||||
InputType::Date(_) |
|
||||
InputType::DatetimeLocal(_) |
|
||||
InputType::Email(_) |
|
||||
InputType::Month(_) |
|
||||
InputType::Number(_) |
|
||||
InputType::Password(_) |
|
||||
InputType::Search(_) |
|
||||
InputType::Tel(_) |
|
||||
InputType::Text(_) |
|
||||
InputType::Time(_) |
|
||||
InputType::Url(_) |
|
||||
InputType::Week(_)
|
||||
)
|
||||
pub(crate) fn is_textual_or_password(&self) -> bool {
|
||||
self.is_textual_or_password.get()
|
||||
}
|
||||
|
||||
fn may_have_embedder_control(&self) -> bool {
|
||||
@@ -922,6 +910,9 @@ impl<'dom> LayoutDom<'dom, HTMLInputElement> {
|
||||
}
|
||||
|
||||
pub(crate) fn selection_for_layout(self) -> Option<SharedSelection> {
|
||||
if !self.unsafe_get().is_textual_or_password.get() {
|
||||
return None;
|
||||
}
|
||||
Some(self.unsafe_get().shared_selection.clone())
|
||||
}
|
||||
}
|
||||
@@ -947,7 +938,7 @@ impl TextControlElement for HTMLInputElement {
|
||||
// Types omitted which could theoretically be included if they were
|
||||
// rendered as a text control: file
|
||||
fn has_selectable_text(&self) -> bool {
|
||||
self.renders_as_text_input_widget() && !self.textinput.borrow().get_content().is_empty()
|
||||
self.is_textual_or_password() && !self.textinput.borrow().get_content().is_empty()
|
||||
}
|
||||
|
||||
fn has_uncollapsed_selection(&self) -> bool {
|
||||
@@ -967,7 +958,7 @@ impl TextControlElement for HTMLInputElement {
|
||||
let offsets = self.textinput.borrow().sorted_selection_offsets_range();
|
||||
let (start, end) = (offsets.start.0, offsets.end.0);
|
||||
let range = TextByteRange::new(ByteIndex(start), ByteIndex(end));
|
||||
let enabled = self.renders_as_text_input_widget() && self.upcast::<Element>().focus_state();
|
||||
let enabled = self.is_textual_or_password() && self.upcast::<Element>().focus_state();
|
||||
|
||||
let mut shared_selection = self.shared_selection.borrow_mut();
|
||||
if range == shared_selection.range && enabled == shared_selection.enabled {
|
||||
@@ -2008,6 +1999,8 @@ impl VirtualMethods for HTMLInputElement {
|
||||
|
||||
*self.input_type.borrow_mut() =
|
||||
InputType::new_from_atom(attr.value().as_atom());
|
||||
self.is_textual_or_password
|
||||
.set(self.input_type().is_textual_or_password());
|
||||
|
||||
let element = self.upcast::<Element>();
|
||||
if self.input_type().is_textual() {
|
||||
@@ -2074,10 +2067,12 @@ impl VirtualMethods for HTMLInputElement {
|
||||
.as_specific()
|
||||
.signal_type_change(self, CanGc::from_cx(cx));
|
||||
*self.input_type.borrow_mut() = InputType::new_text();
|
||||
let el = self.upcast::<Element>();
|
||||
self.is_textual_or_password
|
||||
.set(self.input_type().is_textual_or_password());
|
||||
|
||||
let read_write = !(self.ReadOnly() || el.disabled_state());
|
||||
el.set_read_write_state(read_write);
|
||||
let element = self.upcast::<Element>();
|
||||
let read_write = !(self.ReadOnly() || element.disabled_state());
|
||||
element.set_read_write_state(read_write);
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user