mirror of
https://github.com/servo/servo.git
synced 2025-08-07 06:25:32 +01:00
Apply suggestion
Signed-off-by: stevennovaryo <steven.novaryo@gmail.com>
This commit is contained in:
parent
04a0ff0717
commit
eb393b944f
5 changed files with 94 additions and 73 deletions
|
@ -62,7 +62,7 @@ impl<'dom> NodeAndStyleInfo<'dom> {
|
||||||
// Whether this is a container for the editable text within a single-line text input.
|
// Whether this is a container for the editable text within a single-line text input.
|
||||||
pub(crate) fn is_single_line_text_input(&self) -> bool {
|
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_editing_root()
|
self.node.is_text_control_inner_editor()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn pseudo(
|
pub(crate) fn pseudo(
|
||||||
|
@ -221,7 +221,9 @@ fn traverse_children_of<'dom>(
|
||||||
} else {
|
} else {
|
||||||
handler.handle_text(&info, node_text_content);
|
handler.handle_text(&info, node_text_content);
|
||||||
}
|
}
|
||||||
} else if parent_element.is_text_editing_root() {
|
} else if parent_element.is_text_control_inner_editor() {
|
||||||
|
// We are using parent's style for caret and selection color.
|
||||||
|
// FIXME: Once textarea and input element's shadow tree is supported, this would replace the if condition above.
|
||||||
let info = NodeAndStyleInfo::new(
|
let info = NodeAndStyleInfo::new(
|
||||||
parent_element,
|
parent_element,
|
||||||
parent_element.style(context.shared_context()),
|
parent_element.style(context.shared_context()),
|
||||||
|
|
|
@ -1635,7 +1635,7 @@ impl Element {
|
||||||
pub(crate) fn find_focusable_shadow_host_if_necessary(&self) -> Option<DomRoot<Element>> {
|
pub(crate) fn find_focusable_shadow_host_if_necessary(&self) -> Option<DomRoot<Element>> {
|
||||||
if self.is_focusable_area() {
|
if self.is_focusable_area() {
|
||||||
Some(DomRoot::from_ref(self))
|
Some(DomRoot::from_ref(self))
|
||||||
} else if self.upcast::<Node>().is_text_editing_root() {
|
} else if self.upcast::<Node>().is_text_control_inner_editor() {
|
||||||
let containing_shadow_host = self.containing_shadow_root().map(|root| root.Host());
|
let containing_shadow_host = self.containing_shadow_root().map(|root| root.Host());
|
||||||
if containing_shadow_host
|
if containing_shadow_host
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
|
@ -103,20 +103,21 @@ const DEFAULT_FILE_INPUT_VALUE: &str = "No file chosen";
|
||||||
|
|
||||||
#[derive(Clone, JSTraceable, MallocSizeOf)]
|
#[derive(Clone, JSTraceable, MallocSizeOf)]
|
||||||
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
|
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
|
||||||
/// Contains reference to text editing root and placeholder container element in the UA
|
/// Contains reference to text control inner editor and placeholder container element in the UA
|
||||||
/// shadow tree for `<input type=text>`. The following is the structure of the shadow tree.
|
/// shadow tree for `<input type=text>`. The following is the structure of the shadow tree.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// <input type="text">
|
/// <input type="text">
|
||||||
/// <div id="inner-container">
|
/// #shadow-root
|
||||||
/// <div id="input-editing-root"></div>
|
/// <div id="inner-container">
|
||||||
/// <div id="input-placeholder"></div>
|
/// <div id="input-editor"></div>
|
||||||
/// </div>
|
/// <div id="input-placeholder"></div>
|
||||||
|
/// </div>
|
||||||
/// </input>
|
/// </input>
|
||||||
/// ```
|
/// ```
|
||||||
// TODO(stevennovaryo): We have an additional `<div>` element that contains both placeholder and editing root
|
// TODO(stevennovaryo): We are trying to use CSS to mimic Chrome and Firefox's layout for the <input> element.
|
||||||
// because we are using `position: absolute` to put the editing root and placeholder
|
// But this does have some discrepencies. For example, they would try to vertically align
|
||||||
// on top of each other. But we should probably provide a specifing layout algorithm instead.
|
// <input> text baseline with the baseline of other TextNode within an inline flow.
|
||||||
struct InputTypeTextShadowTree {
|
struct InputTypeTextShadowTree {
|
||||||
text_container: Dom<HTMLDivElement>,
|
text_container: Dom<HTMLDivElement>,
|
||||||
placeholder_container: Dom<HTMLDivElement>,
|
placeholder_container: Dom<HTMLDivElement>,
|
||||||
|
@ -134,25 +135,38 @@ struct InputTypeColorShadowTree {
|
||||||
|
|
||||||
// FIXME: These styles should be inside UA stylesheet, but it is not possible without internal pseudo element support.
|
// FIXME: These styles should be inside UA stylesheet, but it is not possible without internal pseudo element support.
|
||||||
const TEXT_TREE_STYLE: &str = "
|
const TEXT_TREE_STYLE: &str = "
|
||||||
#input-editing-root::selection, #input-placeholder::selection {
|
#input-editor::selection, #input-placeholder::selection {
|
||||||
background: rgba(176, 214, 255, 1.0);
|
background: rgba(176, 214, 255, 1.0);
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
#input-container {
|
:host:not(:placeholder-shown) #input-placeholder {
|
||||||
position: relative;
|
visibility: hidden !important
|
||||||
}
|
}
|
||||||
|
|
||||||
#input-editing-root, #input-placeholder {
|
#input-container {
|
||||||
|
position: relative !important;
|
||||||
|
height: 100% !important;
|
||||||
|
pointer-events: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#input-editor {
|
||||||
|
pointer-events: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#input-editor, #input-placeholder {
|
||||||
|
position: absolute !important;
|
||||||
overflow-wrap: normal;
|
overflow-wrap: normal;
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
|
margin-block: auto;
|
||||||
|
inset-block: 0;
|
||||||
|
block-size: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
#input-placeholder {
|
#input-placeholder {
|
||||||
position: absolute;
|
|
||||||
color: grey;
|
color: grey;
|
||||||
overflow: hidden;
|
overflow: hidden !important;
|
||||||
pointer-events: none;
|
pointer-events: none !important;
|
||||||
}
|
}
|
||||||
";
|
";
|
||||||
|
|
||||||
|
@ -1153,8 +1167,10 @@ impl HTMLInputElement {
|
||||||
let text_container = HTMLDivElement::new(local_name!("div"), None, &document, None, can_gc);
|
let text_container = HTMLDivElement::new(local_name!("div"), None, &document, None, can_gc);
|
||||||
text_container
|
text_container
|
||||||
.upcast::<Element>()
|
.upcast::<Element>()
|
||||||
.SetId(DOMString::from("input-editing-root"), can_gc);
|
.SetId(DOMString::from("input-editor"), can_gc);
|
||||||
text_container.upcast::<Node>().set_text_editing_root();
|
text_container
|
||||||
|
.upcast::<Node>()
|
||||||
|
.set_text_control_inner_editor();
|
||||||
inner_container
|
inner_container
|
||||||
.upcast::<Node>()
|
.upcast::<Node>()
|
||||||
.AppendChild(text_container.upcast::<Node>(), can_gc)
|
.AppendChild(text_container.upcast::<Node>(), can_gc)
|
||||||
|
@ -1168,6 +1184,8 @@ impl HTMLInputElement {
|
||||||
ElementCreator::ScriptCreated,
|
ElementCreator::ScriptCreated,
|
||||||
can_gc,
|
can_gc,
|
||||||
);
|
);
|
||||||
|
// TODO(stevennovaryo): Either use UA stylesheet with internal pseudo element or preemptively parse the stylesheet
|
||||||
|
// to reduce the costly operation and avoid CSP related error.
|
||||||
style
|
style
|
||||||
.upcast::<Node>()
|
.upcast::<Node>()
|
||||||
.SetTextContent(Some(DOMString::from(TEXT_TREE_STYLE)), can_gc);
|
.SetTextContent(Some(DOMString::from(TEXT_TREE_STYLE)), can_gc);
|
||||||
|
@ -1274,49 +1292,48 @@ impl HTMLInputElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_shadow_tree_if_needed(&self, can_gc: CanGc) {
|
fn update_shadow_tree_if_needed(&self, can_gc: CanGc) {
|
||||||
if self.input_type() == InputType::Text {
|
match self.input_type() {
|
||||||
let text_shadow_tree = self.text_shadow_tree(can_gc);
|
InputType::Text => {
|
||||||
let value = self.Value();
|
let text_shadow_tree = self.text_shadow_tree(can_gc);
|
||||||
|
let value = self.Value();
|
||||||
|
|
||||||
let placeholder_text = match value.is_empty() {
|
// The addition of zero-width space here forces the text input to have an inline formatting
|
||||||
true => self.placeholder.to_owned().take(),
|
// context that might otherwise be trimmed if there's no text. This is important to ensure
|
||||||
false => DOMString::new(),
|
// 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?
|
||||||
|
let value_text = match value.is_empty() {
|
||||||
|
false => value,
|
||||||
|
true => "\u{200B}".into(),
|
||||||
|
};
|
||||||
|
|
||||||
// The addition of zero-width space here forces the text input to have an inline formatting
|
// TODO(stevennovaryo): Introduce caching to prevent costly update.
|
||||||
// context that might otherwise be trimmed if there's no text. This is important to ensure
|
text_shadow_tree
|
||||||
// that the input element is at least as tall as the line gap of the caret:
|
.placeholder_container
|
||||||
// <https://drafts.csswg.org/css-ui/#element-with-default-preferred-size>.
|
.upcast::<Node>()
|
||||||
//
|
.SetTextContent(Some(self.placeholder.to_owned().take()), can_gc);
|
||||||
// This is also used to ensure that the caret will still be rendered when the input is empty.
|
text_shadow_tree
|
||||||
// TODO: Is there a less hacky way to do this?
|
.text_container
|
||||||
let value_text = match value.is_empty() {
|
.upcast::<Node>()
|
||||||
false => value,
|
.SetTextContent(Some(value_text), can_gc);
|
||||||
true => "\u{200B}".into(),
|
},
|
||||||
};
|
InputType::Color => {
|
||||||
|
let color_shadow_tree = self.color_shadow_tree(can_gc);
|
||||||
text_shadow_tree
|
let mut value = self.Value();
|
||||||
.placeholder_container
|
if value.str().is_valid_simple_color_string() {
|
||||||
.upcast::<Node>()
|
value.make_ascii_lowercase();
|
||||||
.SetTextContent(Some(placeholder_text), can_gc);
|
} else {
|
||||||
text_shadow_tree
|
value = DOMString::from("#000000");
|
||||||
.text_container
|
}
|
||||||
.upcast::<Node>()
|
let style = format!("background-color: {value}");
|
||||||
.SetTextContent(Some(value_text), can_gc);
|
color_shadow_tree
|
||||||
}
|
.color_value
|
||||||
if self.input_type() == InputType::Color {
|
.upcast::<Element>()
|
||||||
let color_shadow_tree = self.color_shadow_tree(can_gc);
|
.set_string_attribute(&local_name!("style"), style.into(), can_gc);
|
||||||
let mut value = self.Value();
|
},
|
||||||
if value.str().is_valid_simple_color_string() {
|
_ => {},
|
||||||
value.make_ascii_lowercase();
|
|
||||||
} else {
|
|
||||||
value = DOMString::from("#000000");
|
|
||||||
}
|
|
||||||
let style = format!("background-color: {value}");
|
|
||||||
color_shadow_tree
|
|
||||||
.color_value
|
|
||||||
.upcast::<Element>()
|
|
||||||
.set_string_attribute(&local_name!("style"), style.into(), can_gc);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,7 +233,7 @@ bitflags! {
|
||||||
|
|
||||||
/// Whether this node has a serve as the text container for editable content of
|
/// Whether this node has a serve as the text container for editable content of
|
||||||
/// <input> or <textarea> element.
|
/// <input> or <textarea> element.
|
||||||
const IS_TEXT_EDITING_ROOT = 1 << 13;
|
const IS_TEXT_CONTROL_INNER_EDITOR = 1 << 13;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -701,12 +701,14 @@ impl Node {
|
||||||
self.flags.get().contains(NodeFlags::IS_CONNECTED)
|
self.flags.get().contains(NodeFlags::IS_CONNECTED)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_text_editing_root(&self) {
|
pub(crate) fn set_text_control_inner_editor(&self) {
|
||||||
self.set_flag(NodeFlags::IS_TEXT_EDITING_ROOT, true)
|
self.set_flag(NodeFlags::IS_TEXT_CONTROL_INNER_EDITOR, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_text_editing_root(&self) -> bool {
|
pub(crate) fn is_text_control_inner_editor(&self) -> bool {
|
||||||
self.flags.get().contains(NodeFlags::IS_TEXT_EDITING_ROOT)
|
self.flags
|
||||||
|
.get()
|
||||||
|
.contains(NodeFlags::IS_TEXT_CONTROL_INNER_EDITOR)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the type ID of this node.
|
/// Returns the type ID of this node.
|
||||||
|
@ -1628,7 +1630,7 @@ pub(crate) trait LayoutNodeHelpers<'dom> {
|
||||||
fn is_text_input(&self) -> bool;
|
fn is_text_input(&self) -> bool;
|
||||||
|
|
||||||
/// Whether this element serve as a container of editable text for a text input.
|
/// Whether this element serve as a container of editable text for a text input.
|
||||||
fn is_text_editing_root(&self) -> bool;
|
fn is_text_control_inner_editor(&self) -> bool;
|
||||||
fn text_content(self) -> Cow<'dom, str>;
|
fn text_content(self) -> Cow<'dom, str>;
|
||||||
fn selection(self) -> Option<Range<usize>>;
|
fn selection(self) -> Option<Range<usize>>;
|
||||||
fn image_url(self) -> Option<ServoUrl>;
|
fn image_url(self) -> Option<ServoUrl>;
|
||||||
|
@ -1812,8 +1814,8 @@ impl<'dom> LayoutNodeHelpers<'dom> for LayoutDom<'dom, Node> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_text_editing_root(&self) -> bool {
|
fn is_text_control_inner_editor(&self) -> bool {
|
||||||
self.unsafe_get().is_text_editing_root()
|
self.unsafe_get().is_text_control_inner_editor()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn text_content(self) -> Cow<'dom, str> {
|
fn text_content(self) -> Cow<'dom, str> {
|
||||||
|
@ -1833,9 +1835,9 @@ impl<'dom> LayoutNodeHelpers<'dom> for LayoutDom<'dom, Node> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn selection(self) -> Option<Range<usize>> {
|
fn selection(self) -> Option<Range<usize>> {
|
||||||
// This container is a text editing root of a <input> or <textarea> element.
|
// This container is a text control inner editor of a <input> or <textarea> element.
|
||||||
// So we should find those corresponding element, and get its selection.
|
// So we should find those corresponding element, and get its selection.
|
||||||
if self.is_text_editing_root() {
|
if self.is_text_control_inner_editor() {
|
||||||
let shadow_root = self.containing_shadow_root_for_layout();
|
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(containing_shadow_host) = shadow_root.map(|root| root.get_host_for_layout())
|
||||||
{
|
{
|
||||||
|
|
|
@ -101,8 +101,8 @@ impl<'dom> ServoLayoutNode<'dom> {
|
||||||
self.node.is_text_input()
|
self.node.is_text_input()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_text_editing_root(&self) -> bool {
|
pub fn is_text_control_inner_editor(&self) -> bool {
|
||||||
self.node.is_text_editing_root()
|
self.node.is_text_control_inner_editor()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue