aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDK Liao <dklassic@gmail.com>2025-03-10 12:55:38 +0900
committerGitHub <noreply@github.com>2025-03-10 03:55:38 +0000
commitce4ba309924ffa35e0dd4309527586b8f0c22b75 (patch)
tree226f769e796fb9447e6a63c037523ad3dc2b1cfe
parent34047f8da8e191324121023c7cb040a120e2a523 (diff)
downloadservo-ce4ba309924ffa35e0dd4309527586b8f0c22b75.tar.gz
servo-ce4ba309924ffa35e0dd4309527586b8f0c22b75.zip
feat: display file chosen for input file (#35789)
Signed-off-by: DK Liao <dklassic@gmail.com>
-rw-r--r--components/script/dom/filelist.rs21
-rw-r--r--components/script/dom/htmlinputelement.rs29
-rw-r--r--resources/servo.css8
3 files changed, 53 insertions, 5 deletions
diff --git a/components/script/dom/filelist.rs b/components/script/dom/filelist.rs
index d526b54b99b..8942ec92239 100644
--- a/components/script/dom/filelist.rs
+++ b/components/script/dom/filelist.rs
@@ -6,6 +6,7 @@ use std::slice::Iter;
use dom_struct::dom_struct;
+use super::bindings::root::{LayoutDom, ToLayout};
use crate::dom::bindings::codegen::Bindings::FileListBinding::FileListMethods;
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
use crate::dom::bindings::root::{Dom, DomRoot};
@@ -69,3 +70,23 @@ impl FileListMethods<crate::DomTypeHolder> for FileList {
self.Item(index)
}
}
+
+pub(crate) trait LayoutFileListHelpers<'dom> {
+ fn file_for_layout(&self, index: u32) -> Option<&File>;
+ fn len(&self) -> usize;
+}
+
+#[allow(unsafe_code)]
+impl<'dom> LayoutFileListHelpers<'dom> for LayoutDom<'dom, FileList> {
+ fn len(&self) -> usize {
+ self.unsafe_get().list.len()
+ }
+ fn file_for_layout(&self, index: u32) -> Option<&File> {
+ let list = &self.unsafe_get().list;
+ if (index as usize) < list.len() {
+ Some(unsafe { list[index as usize].to_layout().unsafe_get() })
+ } else {
+ None
+ }
+ }
+}
diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs
index 66fef852ffd..1cbf386e03e 100644
--- a/components/script/dom/htmlinputelement.rs
+++ b/components/script/dom/htmlinputelement.rs
@@ -57,7 +57,7 @@ use crate::dom::element::{AttributeMutation, Element, LayoutElementHelpers};
use crate::dom::event::{Event, EventBubbles, EventCancelable};
use crate::dom::eventtarget::EventTarget;
use crate::dom::file::File;
-use crate::dom::filelist::FileList;
+use crate::dom::filelist::{FileList, LayoutFileListHelpers};
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmldatalistelement::HTMLDataListElement;
use crate::dom::htmlelement::HTMLElement;
@@ -90,6 +90,7 @@ use crate::textinput::{
const DEFAULT_SUBMIT_VALUE: &str = "Submit";
const DEFAULT_RESET_VALUE: &str = "Reset";
const PASSWORD_REPLACEMENT_CHAR: char = '●';
+const DEFAULT_FILE_INPUT_VALUE: &str = "No file chosen";
/// <https://html.spec.whatwg.org/multipage/#attr-input-type>
#[derive(Clone, Copy, Default, JSTraceable, PartialEq)]
@@ -1037,6 +1038,9 @@ impl<'dom> LayoutDom<'dom, HTMLInputElement> {
.get_content()
}
}
+ fn get_filelist(self) -> Option<LayoutDom<'dom, FileList>> {
+ unsafe { self.unsafe_get().filelist.get_inner_as_layout() }
+ }
fn placeholder(self) -> &'dom str {
unsafe { self.unsafe_get().placeholder.borrow_for_layout() }
@@ -1070,8 +1074,27 @@ impl<'dom> LayoutHTMLInputElementHelpers<'dom> for LayoutDom<'dom, HTMLInputElem
}
match self.input_type() {
- InputType::Checkbox | InputType::Radio => "".into(),
- InputType::File | InputType::Image => "".into(),
+ InputType::Checkbox | InputType::Radio | InputType::Image => "".into(),
+ InputType::File => {
+ let filelist = self.get_filelist();
+ match filelist {
+ Some(filelist) => {
+ let length = filelist.len();
+ if length == 0 {
+ return DEFAULT_FILE_INPUT_VALUE.into();
+ }
+ if length == 1 {
+ match filelist.file_for_layout(0) {
+ Some(file) => return file.name().to_string().into(),
+ None => return DEFAULT_FILE_INPUT_VALUE.into(),
+ }
+ }
+
+ format!("{} files", length).into()
+ },
+ None => DEFAULT_FILE_INPUT_VALUE.into(),
+ }
+ },
InputType::Button => get_raw_attr_value(self, ""),
InputType::Submit => get_raw_attr_value(self, DEFAULT_SUBMIT_VALUE),
InputType::Reset => get_raw_attr_value(self, DEFAULT_RESET_VALUE),
diff --git a/resources/servo.css b/resources/servo.css
index ee2aca1f66f..d9f0366c217 100644
--- a/resources/servo.css
+++ b/resources/servo.css
@@ -74,13 +74,17 @@ input[type="radio"]:checked::before { content: "●"; line-height: 1em; }
input[type="file"]::before {
content: "Choose File";
+ background: lightgrey;
+ border-top: solid 1px #EEEEEE;
+ border-left: solid 1px #CCCCCC;
+ border-right: solid 1px #999999;
+ border-bottom: solid 1px #999999;
}
input[type="file"] {
- background: lightgrey;
text-align: center;
- vertical-align: middle;
color: black;
+ border-style: none;
}
select {