mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +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
|
@ -51,8 +51,16 @@ impl<'dom> NodeAndStyleInfo<'dom> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Whether this is a container for the text within a single-line text input. This
|
||||
/// is used to solve the special case of line height for a text entry widget.
|
||||
/// <https://html.spec.whatwg.org/multipage/#the-input-element-as-a-text-entry-widget>
|
||||
// TODO(stevennovaryo): Remove the addition of HTMLInputElement here once all of the
|
||||
// input element is implemented with UA shadow DOM. This is temporary
|
||||
// workaround for past version of input element where we are
|
||||
// rendering it as a bare html element.
|
||||
pub(crate) fn is_single_line_text_input(&self) -> bool {
|
||||
self.node.type_id() == LayoutNodeType::Element(LayoutElementType::HTMLInputElement)
|
||||
self.node.type_id() == LayoutNodeType::Element(LayoutElementType::HTMLInputElement) ||
|
||||
self.node.is_text_container_of_single_line_input()
|
||||
}
|
||||
|
||||
pub(crate) fn pseudo(
|
||||
|
@ -75,7 +83,18 @@ impl<'dom> NodeAndStyleInfo<'dom> {
|
|||
}
|
||||
|
||||
pub(crate) fn get_selected_style(&self) -> ServoArc<ComputedValues> {
|
||||
self.node.to_threadsafe().selected_style()
|
||||
// This is a workaround for handling the `::selection` pseudos where it would not
|
||||
// propagate to the children and Shadow DOM elements. For this case, UA widget
|
||||
// inner elements should follow the originating element in terms of selection.
|
||||
if self.node.is_in_ua_widget() {
|
||||
self.node
|
||||
.containing_shadow_host()
|
||||
.expect("Ua widget inner editor is not contained")
|
||||
.to_threadsafe()
|
||||
.selected_style()
|
||||
} else {
|
||||
self.node.to_threadsafe().selected_style()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_selection_range(&self) -> Option<Range<ByteIndex>> {
|
||||
|
@ -194,16 +213,13 @@ fn traverse_children_of<'dom>(
|
|||
) {
|
||||
traverse_eager_pseudo_element(PseudoElement::Before, parent_element_info, context, handler);
|
||||
|
||||
// TODO(stevennovaryo): In the past we are rendering text input as a normal element,
|
||||
// and the processing of text is happening here. Remove this
|
||||
// special case after the implementation of UA Shadow DOM for
|
||||
// all affected input elements.
|
||||
if parent_element_info.node.is_text_input() {
|
||||
let node_text_content = parent_element_info.node.to_threadsafe().node_text_content();
|
||||
if node_text_content.is_empty() {
|
||||
// The addition of zero-width space here forces the text input to have an inline formatting
|
||||
// context that might otherwise be trimmed if there's no text. This is important to ensure
|
||||
// that the input element is at least as tall as the line gap of the caret:
|
||||
// <https://drafts.csswg.org/css-ui/#element-with-default-preferred-size>.
|
||||
//
|
||||
// This is also used to ensure that the caret will still be rendered when the input is empty.
|
||||
// TODO: Is there a less hacky way to do this?
|
||||
handler.handle_text(parent_element_info, "\u{200B}".into());
|
||||
} else {
|
||||
handler.handle_text(parent_element_info, node_text_content);
|
||||
|
|
|
@ -168,7 +168,8 @@ pub(crate) struct InlineFormattingContext {
|
|||
/// Whether or not this [`InlineFormattingContext`] contains floats.
|
||||
pub(super) contains_floats: bool,
|
||||
|
||||
/// Whether or not this is an [`InlineFormattingContext`] for a single line text input.
|
||||
/// Whether or not this is an [`InlineFormattingContext`] for a single line text input's inner
|
||||
/// text container.
|
||||
pub(super) is_single_line_text_input: bool,
|
||||
|
||||
/// Whether or not this is an [`InlineFormattingContext`] has right-to-left content, which
|
||||
|
@ -2237,8 +2238,9 @@ fn line_height(
|
|||
LineHeight::Length(length) => length.0.into(),
|
||||
};
|
||||
|
||||
// Single line text inputs line height is clamped to the size of `normal`. See
|
||||
// <https://github.com/whatwg/html/pull/5462>.
|
||||
// The line height of a single-line text input's inner text container is clamped to
|
||||
// the size of `normal`.
|
||||
// <https://html.spec.whatwg.org/multipage/#the-input-element-as-a-text-entry-widget>
|
||||
if is_single_line_text_input {
|
||||
line_height.max_assign(font_metrics.line_gap);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,36 @@ textarea {
|
|||
font-size: 0.8333em;
|
||||
}
|
||||
|
||||
input::-servo-text-control-inner-editor {
|
||||
overflow-wrap: normal;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
input::-servo-text-control-inner-container {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
input:not(:placeholder-shown)::placeholder {
|
||||
visibility: hidden !important
|
||||
}
|
||||
|
||||
input::-servo-text-control-inner-editor, input::placeholder {
|
||||
white-space: pre;
|
||||
margin-block: auto !important;
|
||||
inset-block: 0 !important;
|
||||
block-size: fit-content !important;
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
overflow: hidden !important;
|
||||
position: absolute !important;
|
||||
color: grey !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
input::color-swatch {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue