Revert "Implement Input UA Shadow DOM (#37065)" (#37296)

This reverts commit 5580704438.

Let's re-land that fix when a working solution is found. Keeping that
regression makes it hard to evaluate other potential improvements.

Signed-off-by: webbeef <me@webbeef.org>
This commit is contained in:
webbeef 2025-06-06 08:23:08 -07:00 committed by GitHub
parent aff2a85372
commit a1f43ab06d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 36 additions and 635 deletions

View file

@ -59,14 +59,8 @@ impl<'dom> NodeAndStyleInfo<'dom> {
}
}
/// Whether this is a container for the editable text within a single-line text input.
/// This is used to solve the special case of line height for a text editor.
/// <https://html.spec.whatwg.org/multipage/#the-input-element-as-a-text-entry-widget>
// FIXME(stevennovaryo): Now, this would also refer to HTMLInputElement, to handle input
// elements without shadow DOM.
pub(crate) fn is_single_line_text_input(&self) -> bool {
self.node.type_id() == LayoutNodeType::Element(LayoutElementType::HTMLInputElement) ||
self.node.is_text_control_inner_editor()
self.node.type_id() == LayoutNodeType::Element(LayoutElementType::HTMLInputElement)
}
pub(crate) fn pseudo(

View file

@ -1556,14 +1556,11 @@ impl Document {
return;
}
// For a node within a text input UA shadow DOM, delegate the focus target into its shadow host.
// TODO: This focus delegation should be done with shadow DOM delegateFocus attribute.
let target_el = el.find_focusable_shadow_host_if_necessary();
self.begin_focus_transaction();
// Try to focus `el`. If it's not focusable, focus the document instead.
// Try to focus `el`. If it's not focusable, focus the document
// instead.
self.request_focus(None, FocusInitiator::Local, can_gc);
self.request_focus(target_el.as_deref(), FocusInitiator::Local, can_gc);
self.request_focus(Some(&*el), FocusInitiator::Local, can_gc);
}
let dom_event = DomRoot::upcast::<Event>(MouseEvent::for_platform_mouse_event(

View file

@ -1703,27 +1703,6 @@ impl Element {
)
}
/// Returns the focusable shadow host if this is a text control inner editor.
/// This is a workaround for the focus delegation of shadow DOM and should be
/// used only to delegate focusable inner editor of [HTMLInputElement] and
/// [HTMLTextAreaElement].
pub(crate) fn find_focusable_shadow_host_if_necessary(&self) -> Option<DomRoot<Element>> {
if self.is_focusable_area() {
Some(DomRoot::from_ref(self))
} else if self.upcast::<Node>().is_text_control_inner_editor() {
let containing_shadow_host = self.containing_shadow_root().map(|root| root.Host());
assert!(
containing_shadow_host
.as_ref()
.is_some_and(|e| e.is_focusable_area()),
"Containing shadow host is not focusable"
);
containing_shadow_host
} else {
None
}
}
pub(crate) fn is_actually_disabled(&self) -> bool {
let node = self.upcast::<Node>();
match node.type_id() {

View file

@ -101,29 +101,6 @@ 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)]
/// 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.
///
/// ```
/// <input type="text">
/// #shadow-root
/// <div id="inner-container">
/// <div id="input-editor"></div>
/// <div id="input-placeholder"></div>
/// </div>
/// </input>
/// ```
// TODO(stevennovaryo): We are trying to use CSS to mimic Chrome and Firefox's layout for the <input> element.
// But, this could be slower in performance and does have some discrepancies. For example,
// they would try to vertically align <input> text baseline with the baseline of other
// TextNode within an inline flow. Another example is the horizontal scroll.
struct InputTypeTextShadowTree {
text_container: Dom<HTMLDivElement>,
placeholder_container: Dom<HTMLDivElement>,
}
#[derive(Clone, JSTraceable, MallocSizeOf)]
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
/// Contains references to the elements in the shadow tree for `<input type=range>`.
@ -134,49 +111,10 @@ struct InputTypeColorShadowTree {
color_value: Dom<HTMLDivElement>,
}
// FIXME: These styles should be inside UA stylesheet, but it is not possible without internal pseudo element support.
const TEXT_TREE_STYLE: &str = "
#input-editor::selection {
background: rgba(176, 214, 255, 1.0);
color: black;
}
:host:not(:placeholder-shown) #input-placeholder {
visibility: hidden !important
}
#input-editor {
overflow-wrap: normal;
pointer-events: auto;
}
#input-container {
position: relative;
height: 100%;
pointer-events: none;
display: flex;
}
#input-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;
pointer-events: none !important;
}
";
#[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
}
@ -1133,7 +1071,7 @@ impl HTMLInputElement {
ShadowRootMode::Closed,
false,
false,
true,
false,
SlotAssignmentMode::Manual,
can_gc,
)
@ -1141,92 +1079,6 @@ 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 inner_container =
HTMLDivElement::new(local_name!("div"), None, &document, None, can_gc);
inner_container
.upcast::<Element>()
.SetId(DOMString::from("input-container"), can_gc);
shadow_root
.upcast::<Node>()
.AppendChild(inner_container.upcast::<Node>(), can_gc)
.unwrap();
let placeholder_container =
HTMLDivElement::new(local_name!("div"), None, &document, None, can_gc);
placeholder_container
.upcast::<Element>()
.SetId(DOMString::from("input-placeholder"), can_gc);
inner_container
.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-editor"), can_gc);
text_container
.upcast::<Node>()
.set_text_control_inner_editor();
inner_container
.upcast::<Node>()
.AppendChild(text_container.upcast::<Node>(), can_gc)
.unwrap();
let style = HTMLStyleElement::new(
local_name!("style"),
None,
&document,
None,
ElementCreator::ScriptCreated,
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
.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();
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);
@ -1284,53 +1136,27 @@ impl HTMLInputElement {
let shadow_tree = self.shadow_tree.borrow();
Ref::filter_map(shadow_tree, |shadow_tree| {
let shadow_tree = shadow_tree.as_ref()?;
match shadow_tree {
ShadowTree::Color(color_tree) => Some(color_tree),
_ => None,
}
let ShadowTree::Color(color_tree) = shadow_tree;
Some(color_tree)
})
.ok()
.expect("UA shadow tree was not created")
}
fn update_shadow_tree_if_needed(&self, can_gc: CanGc) {
match self.input_type() {
InputType::Text => {
let text_shadow_tree = self.text_shadow_tree(can_gc);
let value = self.Value();
// 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?
let value_text = match value.is_empty() {
false => value,
true => "\u{200B}".into(),
};
text_shadow_tree
.text_container
.upcast::<Node>()
.SetTextContent(Some(value_text), can_gc);
},
InputType::Color => {
let color_shadow_tree = self.color_shadow_tree(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);
},
_ => {},
if self.input_type() == InputType::Color {
let color_shadow_tree = self.color_shadow_tree(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);
}
}
}
@ -1639,29 +1465,22 @@ impl HTMLInputElementMethods<crate::DomTypeHolder> for HTMLInputElement {
fn SetValue(&self, mut value: DOMString, can_gc: CanGc) -> ErrorResult {
match self.value_mode() {
ValueMode::Value => {
{
// Step 3.
self.value_dirty.set(true);
// Step 3.
self.value_dirty.set(true);
// Step 4.
self.sanitize_value(&mut value);
// Step 4.
self.sanitize_value(&mut value);
let mut textinput = self.textinput.borrow_mut();
let mut textinput = self.textinput.borrow_mut();
// Step 5.
if *textinput.single_line_content() != value {
// Steps 1-2
textinput.set_content(value);
// Step 5.
if *textinput.single_line_content() != value {
// Steps 1-2
textinput.set_content(value);
// Step 5.
textinput.clear_selection_to_limit(Direction::Forward);
}
textinput.clear_selection_to_limit(Direction::Forward);
}
// Additionaly, update the placeholder shown state. This is
// normally being done in the attributed mutated. And, being
// done in another scope to prevent borrow checker issues.
self.update_placeholder_shown_state();
},
ValueMode::Default | ValueMode::DefaultOn => {
self.upcast::<Element>()
@ -2244,19 +2063,6 @@ impl HTMLInputElement {
el.set_placeholder_shown_state(has_placeholder && !has_value);
}
// Update the placeholder text in the text shadow tree.
// To increase the performance, we would only do this when it is necessary.
fn update_text_shadow_tree_placeholder(&self, can_gc: CanGc) {
if self.input_type() != InputType::Text {
return;
}
self.text_shadow_tree(can_gc)
.placeholder_container
.upcast::<Node>()
.SetTextContent(Some(self.placeholder.borrow().clone()), can_gc);
}
// https://html.spec.whatwg.org/multipage/#file-upload-state-(type=file)
// Select files by invoking UI or by passed in argument
fn select_files(&self, opt_test_paths: Option<Vec<DOMString>>, can_gc: CanGc) {
@ -2882,11 +2688,8 @@ impl VirtualMethods for HTMLInputElement {
},
}
self.update_text_shadow_tree_placeholder(can_gc);
self.update_placeholder_shown_state();
},
// FIXME(stevennovaryo): This is only reachable by Default and DefaultOn value mode. While others
// are being handled in [Self::SetValue]. Should we merge this two together?
local_name!("value") if !self.value_dirty.get() => {
let value = mutation.new_value(attr).map(|value| (**value).to_owned());
let mut value = value.map_or(DOMString::new(), DOMString::from);
@ -2935,7 +2738,6 @@ impl VirtualMethods for HTMLInputElement {
.extend(attr.value().chars().filter(|&c| c != '\n' && c != '\r'));
}
}
self.update_text_shadow_tree_placeholder(can_gc);
self.update_placeholder_shown_state();
},
local_name!("readonly") => {

View file

@ -230,10 +230,6 @@ bitflags! {
/// Whether this node has a weird parser insertion mode. i.e whether setting innerHTML
/// needs extra work or not
const HAS_WEIRD_PARSER_INSERTION_MODE = 1 << 11;
/// Whether this node serves as the text container for editable content of
/// <input> or <textarea> element.
const IS_TEXT_CONTROL_INNER_EDITOR = 1 << 12;
}
}
@ -701,16 +697,6 @@ impl Node {
self.flags.get().contains(NodeFlags::IS_CONNECTED)
}
pub(crate) fn set_text_control_inner_editor(&self) {
self.set_flag(NodeFlags::IS_TEXT_CONTROL_INNER_EDITOR, true)
}
pub(crate) fn is_text_control_inner_editor(&self) -> bool {
self.flags
.get()
.contains(NodeFlags::IS_TEXT_CONTROL_INNER_EDITOR)
}
/// Returns the type ID of this node.
pub(crate) fn type_id(&self) -> NodeTypeId {
match *self.eventtarget.type_id() {
@ -1608,7 +1594,6 @@ pub(crate) trait LayoutNodeHelpers<'dom> {
fn assigned_slot_for_layout(self) -> Option<LayoutDom<'dom, HTMLSlotElement>>;
fn is_element_for_layout(&self) -> bool;
fn is_text_node_for_layout(&self) -> bool;
unsafe fn get_flag(self, flag: NodeFlags) -> bool;
unsafe fn set_flag(self, flag: NodeFlags, value: bool);
@ -1644,9 +1629,6 @@ pub(crate) trait LayoutNodeHelpers<'dom> {
/// Whether this element is a `<input>` rendered as text or a `<textarea>`.
fn is_text_input(&self) -> bool;
/// Whether this element serve as a container of editable text for a text input.
fn is_text_control_inner_editor(&self) -> bool;
fn text_content(self) -> Cow<'dom, str>;
fn selection(self) -> Option<Range<usize>>;
fn image_url(self) -> Option<ServoUrl>;
@ -1679,11 +1661,6 @@ impl<'dom> LayoutNodeHelpers<'dom> for LayoutDom<'dom, Node> {
(*self).is::<Element>()
}
fn is_text_node_for_layout(&self) -> bool {
self.type_id_for_layout() ==
NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text))
}
#[inline]
fn composed_parent_node_ref(self) -> Option<LayoutDom<'dom, Node>> {
let parent = self.parent_node_ref();
@ -1825,8 +1802,8 @@ impl<'dom> LayoutNodeHelpers<'dom> for LayoutDom<'dom, Node> {
{
let input = self.unsafe_get().downcast::<HTMLInputElement>().unwrap();
// FIXME: All the non-color and non-text input types currently render as text
!matches!(input.input_type(), InputType::Color | InputType::Text)
// FIXME: All the non-color input types currently render as text
input.input_type() != InputType::Color
} else {
type_id ==
NodeTypeId::Element(ElementTypeId::HTMLElement(
@ -1835,10 +1812,6 @@ impl<'dom> LayoutNodeHelpers<'dom> for LayoutDom<'dom, Node> {
}
}
fn is_text_control_inner_editor(&self) -> bool {
self.unsafe_get().is_text_control_inner_editor()
}
fn text_content(self) -> Cow<'dom, str> {
if let Some(text) = self.downcast::<Text>() {
return text.upcast().data_for_layout().into();
@ -1856,25 +1829,6 @@ impl<'dom> LayoutNodeHelpers<'dom> for LayoutDom<'dom, Node> {
}
fn selection(self) -> Option<Range<usize>> {
// This node is a text node of a text control inner editor in a <input> or <textarea> element.
// So we should find those corresponding element, and get its selection.
if self.is_text_node_for_layout() &&
self.parent_node_ref()
.is_some_and(|parent| parent.is_text_control_inner_editor())
{
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(area) = containing_shadow_host.downcast::<HTMLTextAreaElement>() {
return area.selection_for_layout();
}
if let Some(input) = containing_shadow_host.downcast::<HTMLInputElement>() {
return input.selection_for_layout();
}
}
}
if let Some(area) = self.downcast::<HTMLTextAreaElement>() {
return area.selection_for_layout();
}

View file

@ -28,7 +28,7 @@ use style::selector_parser::PseudoElement;
use super::{
ServoLayoutDocument, ServoLayoutElement, ServoShadowRoot, ServoThreadSafeLayoutElement,
};
use crate::dom::bindings::inheritance::NodeTypeId;
use crate::dom::bindings::inheritance::{CharacterDataTypeId, NodeTypeId, TextTypeId};
use crate::dom::bindings::root::LayoutDom;
use crate::dom::element::{Element, LayoutElementHelpers};
use crate::dom::node::{LayoutNodeHelpers, Node, NodeFlags, NodeTypeIdWrapper};
@ -100,10 +100,6 @@ impl<'dom> ServoLayoutNode<'dom> {
pub fn is_text_input(&self) -> bool {
self.node.is_text_input()
}
pub fn is_text_control_inner_editor(&self) -> bool {
self.node.is_text_control_inner_editor()
}
}
impl style::dom::NodeInfo for ServoLayoutNode<'_> {
@ -112,7 +108,8 @@ impl style::dom::NodeInfo for ServoLayoutNode<'_> {
}
fn is_text_node(&self) -> bool {
self.node.is_text_node_for_layout()
self.script_type_id() ==
NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text))
}
}

View file

@ -1,3 +0,0 @@
[form-action-src-default-ignored.sub.html]
[Expecting logs: ["PASS","TEST COMPLETE"\]]
expected: FAIL

View file

@ -1,2 +0,0 @@
[spelling-markers-009.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[spelling-markers-010.html]
expected: FAIL

View file

@ -91,86 +91,6 @@
}
},
"reftest": {
"appearance": {
"input-text-definite-width.html": [
"fda46f8af9c14cef3911ec809054624204848b9d",
[
"appearance/input-text-definite-width.html",
[
[
"/_mozilla/appearance/input-text-definite-width-ref.html",
"=="
]
],
{}
]
],
"input-text-empty.html": [
"bd5f5f5a21ec0ce028922a6764de41dc904a1eb1",
[
"appearance/input-text-empty.html",
[
[
"/_mozilla/appearance/input-text-empty-ref.html",
"=="
]
],
{}
]
],
"input-text-nonempty-placeholder.html": [
"e075663cb6ae708b313b3cd5cd69f78c51b4bc1f",
[
"appearance/input-text-nonempty-placeholder.html",
[
[
"/_mozilla/appearance/input-text-nonempty-placeholder-ref.html",
"=="
]
],
{}
]
],
"input-text-overflow.html": [
"52db07c0f0274d2b7b086d7017982145c25918da",
[
"appearance/input-text-overflow.html",
[
[
"/_mozilla/appearance/input-text-overflow-ref.html",
"=="
]
],
{}
]
],
"input-text-placeholder-overflow.html": [
"c4d77ae2a22a5b7972f2798b8ca78742b81bacc4",
[
"appearance/input-text-placeholder-overflow.html",
[
[
"/_mozilla/appearance/input-text-placeholder-overflow-ref.html",
"=="
]
],
{}
]
],
"input-text-placeholder.html": [
"d75acade78038b14529135b1d63c0ac5a168a87b",
[
"appearance/input-text-placeholder.html",
[
[
"/_mozilla/appearance/input-text-placeholder-ref.html",
"=="
]
],
{}
]
]
},
"css": {
"abs-overflow-stackingcontext.html": [
"264df01aa64e0abe9ea3a75e57452c27d53a904f",
@ -8155,38 +8075,6 @@
"b485d435a63ada28eabe976b49a8a580725e7508",
[]
],
"appearance": {
"input-text-definite-width-ref.html": [
"86f7937755750261ed3b06dfe11e78a251b9d175",
[]
],
"input-text-empty-ref.html": [
"437c9988a13e094d870f67c8de0dd0becdeece76",
[]
],
"input-text-nonempty-placeholder-ref.html": [
"5415dfb2a4a88dc3bfed6ad04e23f288534351e4",
[]
],
"input-text-overflow-ref.html": [
"4cece657a2a09cfe3f1d91d49f0c9d76f5714516",
[]
],
"input-text-placeholder-overflow-ref.html": [
"0cccfff638c0d8687a3582310c73233b7d883b1a",
[]
],
"input-text-placeholder-ref.html": [
"fa5b60bdabdf2b9b818ebe66bfc7f2711173b88b",
[]
],
"supports": {
"input-text-ref.css": [
"8cf00d493138285e50aa510273abae98c099ae8b",
[]
]
}
},
"bluetooth": {
"bluetooth-helpers.js": [
"16a280cca298bcaa5796b36b48d331bfd15baae8",

View file

@ -1,2 +0,0 @@
[input_placeholder.html]
expected: FAIL

View file

@ -1,15 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Appearance of an Input type=text With a Definite Width</title>
<link rel="stylesheet" href="./supports/input-text-ref.css">
</head>
<body>
Display of an input type=text should match the display generated by the CSS reference.
<div>
<div id="input" class="definite-width">
Foo
</div>
</div>
</body>
</html>

View file

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Appearance of an Input type=text With a Definite Width</title>
<link rel="match" href="input-text-definite-width-ref.html">
<link rel="help" href="https://github.com/servo/servo/pull/37065">
</head>
<body>
Display of an input type=text should match the display generated by the CSS reference.
<div>
<input type="text" value="Foo" style="font-size: 1em !important; width: 100px;"></input>
</div>
</body>
</html>

View file

@ -1,15 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Appearance of an Empty Input type=text With a Definite Width</title>
<link rel="stylesheet" href="./supports/input-text-ref.css">
</head>
<body>
Display of an input type=text should match the display generated by the CSS reference.
<div>
<div id="input" class="definite-width">
<br>
</div>
</div>
</body>
</html>

View file

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Appearance of an Empty Input type=text With a Definite Width</title>
<link rel="match" href="input-text-empty-ref.html">
<link rel="help" href="https://github.com/servo/servo/pull/37065">
</head>
<body>
Display of an input type=text should match the display generated by the CSS reference.
<div>
<input type="text" style="font-size: 1em !important; width: 100px;"></input>
</div>
</body>
</html>

View file

@ -1,15 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Appearance of a Non-empty Input type=text With a Definite Width and a Placeholder</title>
<link rel="stylesheet" href="./supports/input-text-ref.css">
</head>
<body>
Display of an input type=text should match the display generated by the CSS reference.
<div>
<div id="input" class="definite-width">
Foo
</div>
</div>
</body>
</html>

View file

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Appearance of a Non-empty Input type=text With a Definite Width and a Placeholder</title>
<link rel="match" href="input-text-nonempty-placeholder-ref.html">
<link rel="help" href="https://github.com/servo/servo/pull/37065">
</head>
<body>
Display of an input type=text should match the display generated by the CSS reference.
<div>
<input type="text" value="Foo" placeholder="Bar" style="font-size: 1em !important; width: 100px;"></input>
</div>
</body>
</html>

View file

@ -1,15 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Appearance of an Overflowing Input type=text With a Definite Width</title>
<link rel="stylesheet" href="./supports/input-text-ref.css">
</head>
<body>
Display of an input type=text should match the display generated by the CSS reference.
<div>
<div id="input" class="definite-width">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</div>
</div>
</body>
</html>

View file

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Appearance of an Overflowing Input type=text With a Definite Width</title>
<link rel="match" href="input-text-overflow-ref.html">
<link rel="help" href="https://github.com/servo/servo/pull/37065">
</head>
<body>
Display of an input type=text should match the display generated by the CSS reference.
<div>
<input type="text" value="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." style="font-size: 1em !important; width: 100px;"></input>
</div>
</body>
</html>

View file

@ -1,15 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Appearance of an Input type=text With a Definite Width and an Overflowing Placeholder</title>
<link rel="stylesheet" href="./supports/input-text-ref.css">
</head>
<body>
Display of an input type=text should match the display generated by the CSS reference.
<div>
<div id="input" class="definite-width placeholder-color">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</div>
</div>
</body>
</html>

View file

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Appearance of an Input type=text With a Definite Width and an Overflowing Placeholder</title>
<link rel="match" href="input-text-placeholder-overflow-ref.html">
<link rel="help" href="https://github.com/servo/servo/pull/37065">
</head>
<body>
Display of an input type=text should match the display generated by the CSS reference.
<div>
<input type="text" placeholder="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." style="font-size: 1em !important; width: 100px;"></input>
</div>
</body>
</html>

View file

@ -1,15 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Appearance of an Input type=text With a Definite Width and a Placeholder</title>
<link rel="stylesheet" href="./supports/input-text-ref.css">
</head>
<body>
Display of an input type=text should match the display generated by the CSS reference.
<div>
<div id="input" class="definite-width placeholder-color">
Bar
</div>
</div>
</body>
</html>

View file

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Appearance of an Input type=text With a Definite Width and a Placeholder</title>
<link rel="match" href="input-text-placeholder-ref.html">
<link rel="help" href="https://github.com/servo/servo/pull/37065">
</head>
<body>
Display of an input type=text should match the display generated by the CSS reference.
<div>
<input type="text" placeholder="Bar" style="font-size: 1em !important; width: 100px;"></input>
</div>
</body>
</html>

View file

@ -1,27 +0,0 @@
/* Minimal stylesheet to mimic the appearence of an input type=text specific to Servo.
* This stylesheet is expected to be modified following the development of the
* Shadow DOM input type=text in Servo.
*/
#input {
display: inline-block;
background: white;
border: solid lightgrey 1px;
font-family: sans-serif;
font-size: 1em; /* We are using 1em here to reduce the effect of inconsistencies in layout */
overflow: hidden;
white-space: nowrap;
}
/* We are using definite width for most of the test to reduce the effect if calculating inline
* size of the input element. Which, will depends on the average character width of a font.
*
* <https://html.spec.whatwg.org/#converting-a-character-width-to-pixels>
*/
.definite-width {
width: 100px;
}
.placeholder-color {
color: grey;
}