mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
Create shadow dom for text
Signed-off-by: stevennovaryo <steven.novaryo@gmail.com>
This commit is contained in:
parent
23ce7b31ac
commit
4e44ecafd2
4 changed files with 125 additions and 5 deletions
|
@ -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
|
// 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 {
|
if clip_chain_id != ClipChainId::INVALID && state.clip_chain_id != ClipChainId::INVALID {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
clip_chain_id, state.clip_chain_id,
|
clip_chain_id, state.clip_chain_id,
|
||||||
|
|
|
@ -4,6 +4,8 @@ button {
|
||||||
|
|
||||||
button,
|
button,
|
||||||
input {
|
input {
|
||||||
|
padding-block: 2px;
|
||||||
|
padding-inline: 1px;
|
||||||
background: white;
|
background: white;
|
||||||
border: solid lightgrey 1px;
|
border: solid lightgrey 1px;
|
||||||
color: black;
|
color: black;
|
||||||
|
|
|
@ -101,6 +101,14 @@ const DEFAULT_RESET_VALUE: &str = "Reset";
|
||||||
const PASSWORD_REPLACEMENT_CHAR: char = '●';
|
const PASSWORD_REPLACEMENT_CHAR: char = '●';
|
||||||
const DEFAULT_FILE_INPUT_VALUE: &str = "No file chosen";
|
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<HTMLDivElement>,
|
||||||
|
placeholder_container: Dom<HTMLDivElement>,
|
||||||
|
}
|
||||||
|
|
||||||
#[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 references to the elements in the shadow tree for `<input type=range>`.
|
/// Contains references to the elements in the shadow tree for `<input type=range>`.
|
||||||
|
@ -111,10 +119,30 @@ struct InputTypeColorShadowTree {
|
||||||
color_value: Dom<HTMLDivElement>,
|
color_value: Dom<HTMLDivElement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)]
|
#[derive(Clone, JSTraceable, MallocSizeOf)]
|
||||||
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
|
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
enum ShadowTree {
|
enum ShadowTree {
|
||||||
|
Text(InputTypeTextShadowTree),
|
||||||
Color(InputTypeColorShadowTree),
|
Color(InputTypeColorShadowTree),
|
||||||
// TODO: Add shadow trees for other input types (range etc) here
|
// 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::<Node>(), can_gc);
|
||||||
|
|
||||||
|
let placeholder_container = HTMLDivElement::new(local_name!("div"), None, &document, None, can_gc);
|
||||||
|
placeholder_container
|
||||||
|
.upcast::<Element>()
|
||||||
|
.SetId(DOMString::from("input-placeholder"), can_gc);
|
||||||
|
shadow_root
|
||||||
|
.upcast::<Node>()
|
||||||
|
.AppendChild(placeholder_container.upcast::<Node>(), can_gc)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let text_container = HTMLDivElement::new(local_name!("div"), None, &document, None, can_gc);
|
||||||
|
text_container
|
||||||
|
.upcast::<Element>()
|
||||||
|
.SetId(DOMString::from("input-editing-root"), can_gc);
|
||||||
|
shadow_root
|
||||||
|
.upcast::<Node>()
|
||||||
|
.AppendChild(text_container.upcast::<Node>(), can_gc)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let style = HTMLStyleElement::new(
|
||||||
|
local_name!("style"),
|
||||||
|
None,
|
||||||
|
&document,
|
||||||
|
None,
|
||||||
|
ElementCreator::ScriptCreated,
|
||||||
|
can_gc,
|
||||||
|
);
|
||||||
|
style
|
||||||
|
.upcast::<Node>()
|
||||||
|
.SetTextContent(Some(DOMString::from(TEXT_TREE_STYLE)), can_gc);
|
||||||
|
shadow_root
|
||||||
|
.upcast::<Node>()
|
||||||
|
.AppendChild(style.upcast::<Node>(), 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<InputTypeTextShadowTree> {
|
||||||
|
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) {
|
fn create_color_shadow_tree(&self, can_gc: CanGc) {
|
||||||
let document = self.owner_document();
|
let document = self.owner_document();
|
||||||
let shadow_root = self.shadow_root(can_gc);
|
let shadow_root = self.shadow_root(can_gc);
|
||||||
|
@ -1134,16 +1233,32 @@ impl HTMLInputElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
let shadow_tree = self.shadow_tree.borrow();
|
let shadow_tree = self.shadow_tree.borrow();
|
||||||
|
// MYNOTES: will check again for shadow tree getter.
|
||||||
Ref::filter_map(shadow_tree, |shadow_tree| {
|
Ref::filter_map(shadow_tree, |shadow_tree| {
|
||||||
let shadow_tree = shadow_tree.as_ref()?;
|
let shadow_tree = shadow_tree.as_ref()?;
|
||||||
let ShadowTree::Color(color_tree) = shadow_tree;
|
match shadow_tree {
|
||||||
Some(color_tree)
|
ShadowTree::Color(color_tree) => Some(color_tree),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.ok()
|
.ok()
|
||||||
.expect("UA shadow tree was not created")
|
.expect("UA shadow tree was not created")
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
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::<Node>().SetTextContent(Some(placeholder_text), can_gc);
|
||||||
|
text_shadow_tree.text_container.upcast::<Node>().SetTextContent(Some(value), can_gc);
|
||||||
|
}
|
||||||
if self.input_type() == InputType::Color {
|
if self.input_type() == InputType::Color {
|
||||||
let color_shadow_tree = self.color_shadow_tree(can_gc);
|
let color_shadow_tree = self.color_shadow_tree(can_gc);
|
||||||
let mut value = self.Value();
|
let mut value = self.Value();
|
||||||
|
@ -1266,6 +1381,7 @@ impl<'dom> LayoutHTMLInputElementHelpers<'dom> for LayoutDom<'dom, HTMLInputElem
|
||||||
self.unsafe_get().size.get()
|
self.unsafe_get().size.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MYNOTES is implemented for text
|
||||||
fn selection_for_layout(self) -> Option<Range<usize>> {
|
fn selection_for_layout(self) -> Option<Range<usize>> {
|
||||||
if !self.upcast::<Element>().focus_state() {
|
if !self.upcast::<Element>().focus_state() {
|
||||||
return None;
|
return None;
|
||||||
|
|
|
@ -1787,8 +1787,9 @@ impl<'dom> LayoutNodeHelpers<'dom> for LayoutDom<'dom, Node> {
|
||||||
{
|
{
|
||||||
let input = self.unsafe_get().downcast::<HTMLInputElement>().unwrap();
|
let input = self.unsafe_get().downcast::<HTMLInputElement>().unwrap();
|
||||||
|
|
||||||
// FIXME: All the non-color input types currently render as text
|
// FIXME: All the non-color and non-text input types currently render as text
|
||||||
input.input_type() != InputType::Color
|
input.input_type() != InputType::Color &&
|
||||||
|
input.input_type() != InputType::Text
|
||||||
} else {
|
} else {
|
||||||
type_id ==
|
type_id ==
|
||||||
NodeTypeId::Element(ElementTypeId::HTMLElement(
|
NodeTypeId::Element(ElementTypeId::HTMLElement(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue