diff --git a/Cargo.toml b/Cargo.toml index 1373440d67e..c18bdb3dadd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -118,7 +118,7 @@ rustls-pemfile = "2.0" rustls-pki-types = "1.12" script_layout_interface = { path = "components/shared/script_layout" } script_traits = { path = "components/shared/script" } -selectors = { git = "https://github.com/servo/stylo", branch = "2025-05-01" } +selectors = { git = "https://github.com/stevennovaryo/stylo", branch = "text-editing-root" } serde = "1.0.219" serde_bytes = "0.11" serde_json = "1.0" @@ -126,7 +126,7 @@ servo-media = { git = "https://github.com/servo/media" } servo-media-dummy = { git = "https://github.com/servo/media" } servo-media-gstreamer = { git = "https://github.com/servo/media" } servo-tracing = { path = "components/servo_tracing" } -servo_arc = { git = "https://github.com/servo/stylo", branch = "2025-05-01" } +servo_arc = { git = "https://github.com/stevennovaryo/stylo", branch = "text-editing-root" } smallbitvec = "2.6.0" smallvec = "1.15" snapshot = { path = "./components/shared/snapshot" } @@ -135,12 +135,12 @@ string_cache = "0.8" string_cache_codegen = "0.5" strum = "0.26" strum_macros = "0.26" -stylo = { git = "https://github.com/servo/stylo", branch = "2025-05-01" } -stylo_atoms = { git = "https://github.com/servo/stylo", branch = "2025-05-01" } -stylo_config = { git = "https://github.com/servo/stylo", branch = "2025-05-01" } -stylo_dom = { git = "https://github.com/servo/stylo", branch = "2025-05-01" } -stylo_malloc_size_of = { git = "https://github.com/servo/stylo", branch = "2025-05-01" } -stylo_traits = { git = "https://github.com/servo/stylo", branch = "2025-05-01" } +stylo = { git = "https://github.com/stevennovaryo/stylo", branch = "text-editing-root" } +stylo_atoms = { git = "https://github.com/stevennovaryo/stylo", branch = "text-editing-root" } +stylo_config = { git = "https://github.com/stevennovaryo/stylo", branch = "text-editing-root" } +stylo_dom = { git = "https://github.com/stevennovaryo/stylo", branch = "text-editing-root" } +stylo_malloc_size_of = { git = "https://github.com/stevennovaryo/stylo", branch = "text-editing-root" } +stylo_traits = { git = "https://github.com/stevennovaryo/stylo", branch = "text-editing-root" } surfman = { git = "https://github.com/servo/surfman", rev = "f7688b4585f9e0b5d4bf8ee8e4a91e82349610b1", features = ["chains"] } syn = { version = "2", default-features = false, features = ["clone-impls", "derive", "parsing"] } synstructure = "0.13" @@ -223,7 +223,7 @@ codegen-units = 1 # # Or for Stylo: # -# [patch."https://github.com/servo/stylo"] +# [patch."https://github.com/stylo/stylo"] # selectors = { path = "../stylo/selectors" } # servo_arc = { path = "../stylo/servo_arc" } # stylo = { path = "../stylo/style" } diff --git a/components/layout/dom_traversal.rs b/components/layout/dom_traversal.rs index 8bf6d919fa3..214d3882542 100644 --- a/components/layout/dom_traversal.rs +++ b/components/layout/dom_traversal.rs @@ -219,6 +219,16 @@ fn traverse_children_of<'dom>( } else { handler.handle_text(&info, node_text_content); } + } else if parent_element.is_text_editing_root() { + let info = NodeAndStyleInfo::new( + parent_element, + parent_element.style(context.shared_context()), + ); + for child in iter_child_nodes(parent_element) { + if child.is_text_node() { + handler.handle_text(&info, child.to_threadsafe().node_text_content()); + } + } } else { for child in iter_child_nodes(parent_element) { if child.is_text_node() { diff --git a/components/layout/flow/inline/construct.rs b/components/layout/flow/inline/construct.rs index 07a2e914835..1869b58cb54 100644 --- a/components/layout/flow/inline/construct.rs +++ b/components/layout/flow/inline/construct.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::any::Any; use std::borrow::Cow; use std::char::{ToLowercase, ToUppercase}; @@ -302,7 +303,6 @@ impl InlineFormattingContextBuilder { if new_text.is_empty() { return; } - let selection_range = info.get_selection_range(); if let Some(last_character) = new_text.chars().next_back() { self.on_word_boundary = last_character.is_whitespace(); diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index dbf0f14ab68..0c84cc3e821 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -865,6 +865,13 @@ impl LayoutDom<'_, Element> { pub(super) fn focus_state(self) -> bool { self.unsafe_get().state.get().contains(ElementState::FOCUS) } + + pub(super) fn text_editing_root(self) -> bool { + self.unsafe_get() + .state + .get() + .contains(ElementState::TEXT_EDITING_ROOT) + } } impl<'dom> LayoutElementHelpers<'dom> for LayoutDom<'dom, Element> { @@ -4879,6 +4886,10 @@ impl Element { self.set_state(ElementState::FULLSCREEN, value) } + pub(crate) fn set_text_editing_root_state(&self, value: bool) { + self.set_state(ElementState::TEXT_EDITING_ROOT, value) + } + /// pub(crate) fn is_connected(&self) -> bool { self.upcast::().is_connected() diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index f33bc735b74..dedc37eaaeb 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -119,22 +119,23 @@ struct InputTypeColorShadowTree { color_value: Dom, } +// FIXME: These styles should be inside UA stylesheet, +// but we should support pseudo element first. const TEXT_TREE_STYLE: &str = " +#input-editing-root::selection, #input-placeholder::selection { + background: rgba(176, 214, 255, 1.0); + color: black; +} + #input-editing-root, #input-placeholder { - scrollbar-width: none; - resize: none; - word-wrap: normal; + overflow-wrap: normal; white-space: pre; + pointer-events: none; } #input-placeholder { color: grey; overflow: hidden; - pointer-events: none; - user-select: none; - direction: inherit; - text-orientation: inherit; - writing-mode: inherit; } "; @@ -1112,10 +1113,17 @@ impl HTMLInputElement { 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); + let placeholder_container = + HTMLDivElement::new(local_name!("div"), None, &document, None, can_gc); placeholder_container .upcast::() .SetId(DOMString::from("input-placeholder"), can_gc); + // MYNOTES: + // This is not a text editing root, but we should do this to show it's + // editing caret when placeholder is still visible + placeholder_container + .upcast::() + .set_text_editing_root_state(true); shadow_root .upcast::() .AppendChild(placeholder_container.upcast::(), can_gc) @@ -1125,6 +1133,11 @@ impl HTMLInputElement { text_container .upcast::() .SetId(DOMString::from("input-editing-root"), can_gc); + // We should probably use pseudo element to check this. + // Chrome is using (private?) element attrs, + text_container + .upcast::() + .set_text_editing_root_state(true); shadow_root .upcast::() .AppendChild(text_container.upcast::(), can_gc) @@ -1250,14 +1263,20 @@ impl HTMLInputElement { 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() + let placeholder_text = match (value.is_empty(), self.placeholder.borrow().is_empty()) { + (true, false) => self.placeholder.to_owned().take(), + (true, true) => "\u{200B}".into(), + _ => 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); + 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); diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 22ab120703f..e7f286cf673 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -1614,6 +1614,12 @@ pub(crate) trait LayoutNodeHelpers<'dom> { /// Whether this element is a `` rendered as text or a `