Add a shadow element to input[type=file]

The element has status text of input[type=file], such as "No file
chosen". This element is not rendered in legacy layout.

This CL is a preparation to enable LayoutNG for input[type=file], and
has no behavior changes at this moment.

Bug: 1040826
Change-Id: I58490a5f0524c1e61407b067c8f26311d4035a4a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2108246
Reviewed-by: Yoshifumi Inoue <yosin@chromium.org>
Commit-Queue: Kent Tamura <tkent@chromium.org>
Cr-Commit-Position: refs/heads/master@{#751256}
diff --git a/third_party/blink/renderer/core/html/forms/file_input_type.cc b/third_party/blink/renderer/core/html/forms/file_input_type.cc
index e36a7cd9..68800687 100644
--- a/third_party/blink/renderer/core/html/forms/file_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/file_input_type.cc
@@ -325,8 +325,10 @@
 
 void FileInputType::CreateShadowSubtree() {
   DCHECK(IsShadowHost(GetElement()));
-  auto* button = MakeGarbageCollected<HTMLInputElement>(
-      GetElement().GetDocument(), CreateElementFlags());
+  Document& document = GetElement().GetDocument();
+
+  auto* button =
+      MakeGarbageCollected<HTMLInputElement>(document, CreateElementFlags());
   button->setType(input_type_names::kButton);
   button->setAttribute(
       html_names::kValueAttr,
@@ -338,6 +340,15 @@
                        shadow_element_names::FileUploadButton());
   button->SetActive(GetElement().CanReceiveDroppedFiles());
   GetElement().UserAgentShadowRoot()->AppendChild(button);
+
+  // The following element is used only in LayoutNG.
+  // See LayoutFileUploadControl::IsChildAllowed().
+  auto* span = document.CreateRawElement(html_names::kSpanTag);
+  // This element is hidden from AX trees for a historical reason.
+  span->setAttribute(html_names::kAriaHiddenAttr, "true");
+  GetElement().UserAgentShadowRoot()->AppendChild(span);
+
+  UpdateView();
 }
 
 HTMLInputElement* FileInputType::UploadButton() const {
@@ -347,6 +358,10 @@
   return To<HTMLInputElement>(element);
 }
 
+Node* FileInputType::FileStatusElement() const {
+  return GetElement().UserAgentShadowRoot()->lastChild();
+}
+
 void FileInputType::DisabledAttributeChanged() {
   DCHECK(IsShadowHost(GetElement()));
   if (Element* button = UploadButton()) {
@@ -545,8 +560,12 @@
 }
 
 void FileInputType::UpdateView() {
-  if (auto* layout_object = GetElement().GetLayoutObject())
+  auto* layout_object = GetElement().GetLayoutObject();
+  if (layout_object && layout_object->IsFileUploadControl())
     layout_object->SetShouldDoFullPaintInvalidation();
+
+  if (auto* span = FileStatusElement())
+    span->setTextContent(FileStatusText());
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/forms/file_input_type.h b/third_party/blink/renderer/core/html/forms/file_input_type.h
index 3c27ba6..425bf6b 100644
--- a/third_party/blink/renderer/core/html/forms/file_input_type.h
+++ b/third_party/blink/renderer/core/html/forms/file_input_type.h
@@ -111,6 +111,7 @@
   void WillOpenPopup() override;
 
   void SetFilesFromDirectory(const String&);
+  Node* FileStatusElement() const;
 
   Member<FileList> file_list_;
   String dropped_file_system_id_;
diff --git a/third_party/blink/renderer/core/layout/layout_file_upload_control.cc b/third_party/blink/renderer/core/layout/layout_file_upload_control.cc
index 5a9c6ec..589ba9e 100644
--- a/third_party/blink/renderer/core/layout/layout_file_upload_control.cc
+++ b/third_party/blink/renderer/core/layout/layout_file_upload_control.cc
@@ -42,6 +42,16 @@
 
 LayoutFileUploadControl::~LayoutFileUploadControl() = default;
 
+bool LayoutFileUploadControl::IsChildAllowed(LayoutObject* child,
+                                             const ComputedStyle& style) const {
+  const Node* child_node = child->GetNode();
+  // Reject shadow nodes other than UploadButton.
+  if (child_node && child_node->OwnerShadowHost() == GetNode() &&
+      child_node != UploadButton())
+    return false;
+  return LayoutBlockFlow::IsChildAllowed(child, style);
+}
+
 int LayoutFileUploadControl::MaxFilenameWidth() const {
   int upload_button_width =
       (UploadButton() && UploadButton()->GetLayoutBox())
diff --git a/third_party/blink/renderer/core/layout/layout_file_upload_control.h b/third_party/blink/renderer/core/layout/layout_file_upload_control.h
index 3e9ac619..372714a 100644
--- a/third_party/blink/renderer/core/layout/layout_file_upload_control.h
+++ b/third_party/blink/renderer/core/layout/layout_file_upload_control.h
@@ -54,6 +54,8 @@
   const char* GetName() const override { return "LayoutFileUploadControl"; }
 
  private:
+  bool IsChildAllowed(LayoutObject* child,
+                      const ComputedStyle& style) const override;
   void PaintObject(const PaintInfo&,
                    const PhysicalOffset& paint_offset) const override;