mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
Reland Input type=text
Shadow DOM With Performance Improvement (#37483)
Depends on #37427. In addition to the changes introduced by https://github.com/servo/servo/pull/37065, there are several performance improvements and nits as follows: - Use the internal pseudo element for style matching, this will reduce the performance regression by ~66%. - Manual construction of the `Text` node inside a text container. This is followed by the modification of the inner `Text` node instead of using `SetTextContent` which is more expensive. - Use `implemented_pseudo_element` instead of `text_control_inner_editor` `NodeFlag` to handle the special cases that these elements should follow, specifically the: - focus delegation workaround; - selections; and - line height resolving. - More documentation. Servo's side of: https://github.com/servo/stylo/pull/217 Testing: No new unexpected WPT failure, except for the one introduced by https://github.com/servo/servo/pull/37065/. Fixes: #36307 #37205 --------- Signed-off-by: stevennovaryo <steven.novaryo@gmail.com>
This commit is contained in:
parent
f523445fc3
commit
3cb16eb864
27 changed files with 710 additions and 85 deletions
|
@ -1625,6 +1625,7 @@ pub(crate) trait LayoutNodeHelpers<'dom> {
|
|||
fn assigned_slot_for_layout(self) -> Option<LayoutDom<'dom, HTMLSlotElement>>;
|
||||
|
||||
fn is_element_for_layout(&self) -> bool;
|
||||
fn is_text_node_for_layout(&self) -> bool;
|
||||
unsafe fn get_flag(self, flag: NodeFlags) -> bool;
|
||||
unsafe fn set_flag(self, flag: NodeFlags, value: bool);
|
||||
|
||||
|
@ -1659,7 +1660,19 @@ pub(crate) trait LayoutNodeHelpers<'dom> {
|
|||
unsafe fn clear_style_and_layout_data(self);
|
||||
|
||||
/// Whether this element is a `<input>` rendered as text or a `<textarea>`.
|
||||
/// This is used for the rendering of text control element in the past
|
||||
/// where the necessities is being handled within layout. With the current
|
||||
/// implementation of Shadow DOM, we are able to move and expand this kind
|
||||
/// of behavior in the previous pipelines (i.e. DOM, style traversal).
|
||||
fn is_text_input(&self) -> bool;
|
||||
|
||||
/// Whether this element serve as a container of editable text for a text input
|
||||
/// that is implemented as an UA widget.
|
||||
fn is_single_line_text_inner_editor(&self) -> bool;
|
||||
|
||||
/// Whether this element serve as a container of any text inside a text input
|
||||
/// that is implemented as an UA widget.
|
||||
fn is_text_container_of_single_line_input(&self) -> bool;
|
||||
fn text_content(self) -> Cow<'dom, str>;
|
||||
fn selection(self) -> Option<Range<usize>>;
|
||||
fn image_url(self) -> Option<ServoUrl>;
|
||||
|
@ -1694,6 +1707,11 @@ impl<'dom> LayoutNodeHelpers<'dom> for LayoutDom<'dom, Node> {
|
|||
(*self).is::<Element>()
|
||||
}
|
||||
|
||||
fn is_text_node_for_layout(&self) -> bool {
|
||||
self.type_id_for_layout() ==
|
||||
NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(unsafe_code)]
|
||||
fn parent_node_ref(self) -> Option<LayoutDom<'dom, Node>> {
|
||||
|
@ -1843,8 +1861,8 @@ impl<'dom> LayoutNodeHelpers<'dom> for LayoutDom<'dom, Node> {
|
|||
{
|
||||
let input = self.unsafe_get().downcast::<HTMLInputElement>().unwrap();
|
||||
|
||||
// FIXME: All the non-color input types currently render as text
|
||||
input.input_type() != InputType::Color
|
||||
// FIXME: All the non-color and non-text input types currently render as text
|
||||
!matches!(input.input_type(), InputType::Color | InputType::Text)
|
||||
} else {
|
||||
type_id ==
|
||||
NodeTypeId::Element(ElementTypeId::HTMLElement(
|
||||
|
@ -1853,6 +1871,30 @@ impl<'dom> LayoutNodeHelpers<'dom> for LayoutDom<'dom, Node> {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_single_line_text_inner_editor(&self) -> bool {
|
||||
matches!(
|
||||
self.unsafe_get().implemented_pseudo_element(),
|
||||
Some(PseudoElement::ServoTextControlInnerEditor)
|
||||
)
|
||||
}
|
||||
|
||||
fn is_text_container_of_single_line_input(&self) -> bool {
|
||||
let is_single_line_text_inner_placeholder = matches!(
|
||||
self.unsafe_get().implemented_pseudo_element(),
|
||||
Some(PseudoElement::Placeholder)
|
||||
);
|
||||
// Currently `::placeholder` is only implemented for single line text input element.
|
||||
debug_assert!(
|
||||
!is_single_line_text_inner_placeholder ||
|
||||
self.containing_shadow_root_for_layout()
|
||||
.map(|root| root.get_host_for_layout())
|
||||
.map(|host| host.downcast::<HTMLInputElement>())
|
||||
.is_some()
|
||||
);
|
||||
|
||||
self.is_single_line_text_inner_editor() || is_single_line_text_inner_placeholder
|
||||
}
|
||||
|
||||
fn text_content(self) -> Cow<'dom, str> {
|
||||
if let Some(text) = self.downcast::<Text>() {
|
||||
return text.upcast().data_for_layout().into();
|
||||
|
@ -1870,6 +1912,24 @@ impl<'dom> LayoutNodeHelpers<'dom> for LayoutDom<'dom, Node> {
|
|||
}
|
||||
|
||||
fn selection(self) -> Option<Range<usize>> {
|
||||
// If this is a inner editor of an UA widget element, we should find
|
||||
// the selection from its shadow host.
|
||||
// FIXME(stevennovaryo): This should account for the multiline text input,
|
||||
// but we are yet to support that input with UA widget.
|
||||
if self.is_in_ua_widget() &&
|
||||
self.is_text_node_for_layout() &&
|
||||
self.parent_node_ref()
|
||||
.is_some_and(|parent| parent.is_single_line_text_inner_editor())
|
||||
{
|
||||
let shadow_root = self.containing_shadow_root_for_layout();
|
||||
if let Some(containing_shadow_host) = shadow_root.map(|root| root.get_host_for_layout())
|
||||
{
|
||||
if let Some(input) = containing_shadow_host.downcast::<HTMLInputElement>() {
|
||||
return input.selection_for_layout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(area) = self.downcast::<HTMLTextAreaElement>() {
|
||||
return area.selection_for_layout();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue