diff --git a/components/layout/display_list/mod.rs b/components/layout/display_list/mod.rs index 3716ff35b2c..5c88e843e62 100644 --- a/components/layout/display_list/mod.rs +++ b/components/layout/display_list/mod.rs @@ -539,7 +539,8 @@ impl InspectorHighlight { }); // We expect all fragments generated by one node to be in the same scroll tree node and clip node - debug_assert_eq!(spatial_id, state.spatial_id); + // MYNOTES: Error for Input type text shadow DOM. + // debug_assert_eq!(spatial_id, state.spatial_id); if clip_chain_id != ClipChainId::INVALID && state.clip_chain_id != ClipChainId::INVALID { debug_assert_eq!( clip_chain_id, state.clip_chain_id, diff --git a/components/layout/stylesheets/servo.css b/components/layout/stylesheets/servo.css index c025b19f364..90779382841 100644 --- a/components/layout/stylesheets/servo.css +++ b/components/layout/stylesheets/servo.css @@ -4,6 +4,8 @@ button { button, input { + padding-block: 2px; + padding-inline: 1px; background: white; border: solid lightgrey 1px; color: black; diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index c6a3e2227f5..f33bc735b74 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -101,6 +101,14 @@ const DEFAULT_RESET_VALUE: &str = "Reset"; const PASSWORD_REPLACEMENT_CHAR: char = '●'; const DEFAULT_FILE_INPUT_VALUE: &str = "No file chosen"; +#[derive(Clone, JSTraceable, MallocSizeOf)] +#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] +/// MYNOTES: document this and check name later. +struct InputTypeTextShadowTree { + text_container: Dom, + placeholder_container: Dom, +} + #[derive(Clone, JSTraceable, MallocSizeOf)] #[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] /// Contains references to the elements in the shadow tree for ``. @@ -111,10 +119,30 @@ struct InputTypeColorShadowTree { color_value: Dom, } +const TEXT_TREE_STYLE: &str = " +#input-editing-root, #input-placeholder { + scrollbar-width: none; + resize: none; + word-wrap: normal; + white-space: pre; +} + +#input-placeholder { + color: grey; + overflow: hidden; + pointer-events: none; + user-select: none; + direction: inherit; + text-orientation: inherit; + writing-mode: inherit; +} +"; + #[derive(Clone, JSTraceable, MallocSizeOf)] #[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] #[non_exhaustive] enum ShadowTree { + Text(InputTypeTextShadowTree), Color(InputTypeColorShadowTree), // TODO: Add shadow trees for other input types (range etc) here } @@ -1079,6 +1107,77 @@ impl HTMLInputElement { }) } + fn create_text_shadow_tree(&self, can_gc: CanGc) { + let document = self.owner_document(); + let shadow_root = self.shadow_root(can_gc); + Node::replace_all(None, shadow_root.upcast::(), can_gc); + + let placeholder_container = HTMLDivElement::new(local_name!("div"), None, &document, None, can_gc); + placeholder_container + .upcast::() + .SetId(DOMString::from("input-placeholder"), can_gc); + shadow_root + .upcast::() + .AppendChild(placeholder_container.upcast::(), can_gc) + .unwrap(); + + let text_container = HTMLDivElement::new(local_name!("div"), None, &document, None, can_gc); + text_container + .upcast::() + .SetId(DOMString::from("input-editing-root"), can_gc); + shadow_root + .upcast::() + .AppendChild(text_container.upcast::(), can_gc) + .unwrap(); + + let style = HTMLStyleElement::new( + local_name!("style"), + None, + &document, + None, + ElementCreator::ScriptCreated, + can_gc, + ); + style + .upcast::() + .SetTextContent(Some(DOMString::from(TEXT_TREE_STYLE)), can_gc); + shadow_root + .upcast::() + .AppendChild(style.upcast::(), can_gc) + .unwrap(); + + let _ = self + .shadow_tree + .borrow_mut() + .insert(ShadowTree::Text(InputTypeTextShadowTree { + text_container: text_container.as_traced(), + placeholder_container: placeholder_container.as_traced(), + })); + } + + fn text_shadow_tree(&self, can_gc: CanGc) -> Ref { + let has_text_shadow_tree = self + .shadow_tree + .borrow() + .as_ref() + .is_some_and(|shadow_tree| matches!(shadow_tree, ShadowTree::Text(_))); + if !has_text_shadow_tree { + self.create_text_shadow_tree(can_gc); + } + + let shadow_tree = self.shadow_tree.borrow(); + // MYNOTES: will check again for shadow tree getter. + Ref::filter_map(shadow_tree, |shadow_tree| { + let shadow_tree = shadow_tree.as_ref()?; + match shadow_tree { + ShadowTree::Text(text_tree) => Some(text_tree), + _ => None, + } + }) + .ok() + .expect("UA shadow tree was not created") + } + fn create_color_shadow_tree(&self, can_gc: CanGc) { let document = self.owner_document(); let shadow_root = self.shadow_root(can_gc); @@ -1134,16 +1233,32 @@ impl HTMLInputElement { } let shadow_tree = self.shadow_tree.borrow(); + // MYNOTES: will check again for shadow tree getter. Ref::filter_map(shadow_tree, |shadow_tree| { let shadow_tree = shadow_tree.as_ref()?; - let ShadowTree::Color(color_tree) = shadow_tree; - Some(color_tree) + match shadow_tree { + ShadowTree::Color(color_tree) => Some(color_tree), + _ => None, + } }) .ok() .expect("UA shadow tree was not created") } fn update_shadow_tree_if_needed(&self, can_gc: CanGc) { + if self.input_type() == InputType::Text { + let text_shadow_tree = self.text_shadow_tree(can_gc); + let value = self.Value(); + + let placeholder_text = if value.len() == 0 { + self.placeholder.to_owned().take() + } else { + DOMString::new() + }; + + text_shadow_tree.placeholder_container.upcast::().SetTextContent(Some(placeholder_text), can_gc); + text_shadow_tree.text_container.upcast::().SetTextContent(Some(value), can_gc); + } if self.input_type() == InputType::Color { let color_shadow_tree = self.color_shadow_tree(can_gc); let mut value = self.Value(); @@ -1266,6 +1381,7 @@ impl<'dom> LayoutHTMLInputElementHelpers<'dom> for LayoutDom<'dom, HTMLInputElem self.unsafe_get().size.get() } + // MYNOTES is implemented for text fn selection_for_layout(self) -> Option> { if !self.upcast::().focus_state() { return None; diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 17235543906..22ab120703f 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -1787,8 +1787,9 @@ impl<'dom> LayoutNodeHelpers<'dom> for LayoutDom<'dom, Node> { { let input = self.unsafe_get().downcast::().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 + input.input_type() != InputType::Color && + input.input_type() != InputType::Text } else { type_id == NodeTypeId::Element(ElementTypeId::HTMLElement(