Bug 345195 - Replace the anonymous <input type='text'> in <input type='file'> by a xul:label. r=bz
authorMounir Lamouri <mounir.lamouri@gmail.com>
Sat, 09 Feb 2013 14:57:30 +0000
changeset 126372 511ea736284e
parent 126371 efa7756f90a4
child 126373 f372d9cf7d5c
push id25439
push usermlamouri@mozilla.com
push dateWed, 27 Mar 2013 11:38:49 +0000
treeherdermozilla-inbound@50d01a77718e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs345195
milestone22.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 345195 - Replace the anonymous <input type='text'> in <input type='file'> by a xul:label. r=bz
dom/locales/en-US/chrome/layout/HtmlForm.properties
layout/forms/nsFileControlFrame.cpp
layout/forms/nsFileControlFrame.h
layout/style/forms.css
--- a/dom/locales/en-US/chrome/layout/HtmlForm.properties
+++ b/dom/locales/en-US/chrome/layout/HtmlForm.properties
@@ -12,8 +12,14 @@ FileUpload=File Upload
 # trimming.
 IsIndexPromptWithSpace=This is a searchable index. Enter search keywords:\u0020
 ForgotPostWarning=Form contains enctype=%S, but does not contain method=post.  Submitting normally with method=GET and no enctype instead.
 ForgotFileEnctypeWarning=Form contains a file input, but is missing method=POST and enctype=multipart/form-data on the form.  The file will not be sent.
 # LOCALIZATION NOTE (DefaultFormSubject): %S will be replaced with brandShortName
 DefaultFormSubject=Form Post from %S
 CannotEncodeAllUnicode=A form was submitted in the %S encoding which cannot encode all Unicode characters, so user input may get corrupted. To avoid this problem, the page should be changed so that the form is submitted in the UTF-8 encoding either by changing the encoding of the page itself to UTF-8 or by specifying accept-charset=utf-8 on the form element.
 AllSupportedTypes=All Supported Types
+# LOCALIZATION NOTE (NoFileSelected): this string is shown on a
+# <input type='file'> when there is no file selected yet.
+NoFileSelected=No file selected.
+# LOCALIZATION NOTE (NoFilesSelected): this string is shown on a
+# <input type='file' multiple> when there is no file selected yet.
+NoFilesSelected=No files selected.
--- a/layout/forms/nsFileControlFrame.cpp
+++ b/layout/forms/nsFileControlFrame.cpp
@@ -43,16 +43,17 @@
 #include "nsDirectoryServiceDefs.h"
 #include "nsDOMFile.h"
 #include "nsEventStates.h"
 #include "nsTextControlFrame.h"
 
 #include "nsIDOMDOMStringList.h"
 #include "nsIDOMDragEvent.h"
 #include "nsContentList.h"
+#include "nsIDOMMutationEvent.h"
 
 using namespace mozilla;
 
 #define SYNC_TEXT 0x1
 #define SYNC_BUTTON 0x2
 
 nsIFrame*
 NS_NewFileControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
@@ -112,47 +113,40 @@ nsFileControlFrame::DestroyFrom(nsIFrame
 
 nsresult
 nsFileControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
 {
   // Get the NodeInfoManager and tag necessary to create input elements
   nsCOMPtr<nsIDocument> doc = mContent->GetDocument();
 
   nsCOMPtr<nsINodeInfo> nodeInfo;
-  nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::input, nullptr,
-                                                 kNameSpaceID_XHTML,
+  nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::label, nullptr,
+                                                 kNameSpaceID_XUL,
                                                  nsIDOMNode::ELEMENT_NODE);
 
   // Create the text content
-  NS_NewHTMLElement(getter_AddRefs(mTextContent), nodeInfo.forget(),
-                    dom::NOT_FROM_PARSER);
+  NS_TrustedNewXULElement(getter_AddRefs(mTextContent), nodeInfo.forget());
   if (!mTextContent)
     return NS_ERROR_OUT_OF_MEMORY;
 
   // Mark the element to be native anonymous before setting any attributes.
   mTextContent->SetNativeAnonymous();
 
-  mTextContent->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
-                        NS_LITERAL_STRING("text"), false);
+  mTextContent->SetAttr(kNameSpaceID_None, nsGkAtoms::crop,
+                        NS_LITERAL_STRING("center"), false);
 
   nsHTMLInputElement* inputElement =
     nsHTMLInputElement::FromContent(mContent);
   NS_ASSERTION(inputElement, "Why is our content not a <input>?");
 
   // Initialize value when we create the content in case the value was set
   // before we got here
   nsAutoString value;
   inputElement->GetDisplayFileName(value);
-
-  nsCOMPtr<nsIDOMHTMLInputElement> textControl = do_QueryInterface(mTextContent);
-  NS_ASSERTION(textControl, "Why is the <input> we created not a <input>?");
-  textControl->SetValue(value);
-
-  textControl->SetTabIndex(-1);
-  textControl->SetReadOnly(true);
+  UpdateDisplayedValue(value, false);
 
   if (!aElements.AppendElement(mTextContent))
     return NS_ERROR_OUT_OF_MEMORY;
 
   // Register the whole frame as an event listener of drag events
   mContent->AddSystemEventListener(NS_LITERAL_STRING("drop"),
                                    mMouseListener, false);
   mContent->AddSystemEventListener(NS_LITERAL_STRING("dragover"),
@@ -193,17 +187,16 @@ nsFileControlFrame::CreateAnonymousConte
   if (!aElements.AppendElement(mBrowse))
     return NS_ERROR_OUT_OF_MEMORY;
 
   // Register as an event listener of the button
   // to open file dialog on mouse click
   mBrowse->AddSystemEventListener(NS_LITERAL_STRING("click"),
                                   mMouseListener, false);
 
-  SyncAttr(kNameSpaceID_None, nsGkAtoms::size,     SYNC_TEXT);
   SyncDisabledState();
 
   return NS_OK;
 }
 
 void
 nsFileControlFrame::AppendAnonymousContentTo(nsBaseContentList& aElements,
                                              uint32_t aFilter)
@@ -331,70 +324,40 @@ nsFileControlFrame::GetMinWidth(nsRender
   nscoord result;
   DISPLAY_MIN_WIDTH(this, result);
 
   // Our min width is our pref width
   result = GetPrefWidth(aRenderingContext);
   return result;
 }
 
-nsTextControlFrame*
-nsFileControlFrame::GetTextControlFrame()
-{
-  nsITextControlFrame* tc = do_QueryFrame(mTextContent->GetPrimaryFrame());
-  return static_cast<nsTextControlFrame*>(tc);
-}
-
-void
-nsFileControlFrame::SyncAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
-                             int32_t aWhichControls)
-{
-  nsAutoString value;
-  if (mContent->GetAttr(aNameSpaceID, aAttribute, value)) {
-    if (aWhichControls & SYNC_TEXT && mTextContent) {
-      mTextContent->SetAttr(aNameSpaceID, aAttribute, value, true);
-    }
-    if (aWhichControls & SYNC_BUTTON && mBrowse) {
-      mBrowse->SetAttr(aNameSpaceID, aAttribute, value, true);
-    }
-  } else {
-    if (aWhichControls & SYNC_TEXT && mTextContent) {
-      mTextContent->UnsetAttr(aNameSpaceID, aAttribute, true);
-    }
-    if (aWhichControls & SYNC_BUTTON && mBrowse) {
-      mBrowse->UnsetAttr(aNameSpaceID, aAttribute, true);
-    }
-  }
-}
-
 void
 nsFileControlFrame::SyncDisabledState()
 {
   nsEventStates eventStates = mContent->AsElement()->State();
   if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
-    mTextContent->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(),
-                          true);
     mBrowse->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(),
                      true);
   } else {
-    mTextContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
     mBrowse->UnsetAttr(kNameSpaceID_None, nsGkAtoms::disabled, true);
   }
 }
 
 NS_IMETHODIMP
-nsFileControlFrame::AttributeChanged(int32_t         aNameSpaceID,
-                                     nsIAtom*        aAttribute,
-                                     int32_t         aModType)
+nsFileControlFrame::AttributeChanged(int32_t  aNameSpaceID,
+                                     nsIAtom* aAttribute,
+                                     int32_t  aModType)
 {
-  if (aNameSpaceID == kNameSpaceID_None) {
-    if (aAttribute == nsGkAtoms::size) {
-      SyncAttr(aNameSpaceID, aAttribute, SYNC_TEXT);
-    } else if (aAttribute == nsGkAtoms::tabindex) {
-      SyncAttr(aNameSpaceID, aAttribute, SYNC_BUTTON);
+  if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::tabindex) {
+    if (aModType == nsIDOMMutationEvent::REMOVAL) {
+      mBrowse->UnsetAttr(aNameSpaceID, aAttribute, true);
+    } else {
+      nsAutoString value;
+      mContent->GetAttr(aNameSpaceID, aAttribute, value);
+      mBrowse->SetAttr(aNameSpaceID, aAttribute, value, true);
     }
   }
 
   return nsBlockFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
 }
 
 void
 nsFileControlFrame::ContentStatesChanged(nsEventStates aStates)
@@ -413,29 +376,43 @@ nsFileControlFrame::IsLeaf() const
 #ifdef DEBUG
 NS_IMETHODIMP
 nsFileControlFrame::GetFrameName(nsAString& aResult) const
 {
   return MakeFrameName(NS_LITERAL_STRING("FileControl"), aResult);
 }
 #endif
 
+void
+nsFileControlFrame::UpdateDisplayedValue(const nsAString& aValue, bool aNotify)
+{
+  nsXPIDLString value;
+  if (aValue.IsEmpty()) {
+    if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) {
+      nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
+                                         "NoFilesSelected", value);
+    } else {
+      nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
+                                         "NoFileSelected", value);
+    }
+  } else {
+    value = aValue;
+  }
+  mTextContent->SetAttr(kNameSpaceID_None, nsGkAtoms::value, value, aNotify);
+}
+
 nsresult
 nsFileControlFrame::SetFormProperty(nsIAtom* aName,
                                     const nsAString& aValue)
 {
   if (nsGkAtoms::value == aName) {
-    nsCOMPtr<nsIDOMHTMLInputElement> textControl =
-      do_QueryInterface(mTextContent);
-    NS_ASSERTION(textControl,
-                 "The text control should exist and be an input element");
-    textControl->SetValue(aValue);
+    UpdateDisplayedValue(aValue, true);
   }
   return NS_OK;
-}      
+}
 
 nsresult
 nsFileControlFrame::GetFormProperty(nsIAtom* aName, nsAString& aValue) const
 {
   aValue.Truncate();  // initialize out param
 
   if (nsGkAtoms::value == aName) {
     nsHTMLInputElement* inputElement =
--- a/layout/forms/nsFileControlFrame.h
+++ b/layout/forms/nsFileControlFrame.h
@@ -138,30 +138,21 @@ protected:
 
   /**
    * Our mouse listener.  This makes sure we don't get used after destruction.
    */
   nsRefPtr<BrowseMouseListener> mMouseListener;
 
 protected:
   /**
-   * @return the text control frame, or null if not found
-   */
-  nsTextControlFrame* GetTextControlFrame();
-
-  /**
-   * Copy an attribute from file content to text and button content.
-   * @param aNameSpaceID namespace of attr
-   * @param aAttribute attribute atom
-   * @param aWhichControls which controls to apply to (SYNC_TEXT or SYNC_FILE)
-   */
-  void SyncAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
-                int32_t aWhichControls);
-
-  /**
    * Sync the disabled state of the content with anonymous children.
    */
   void SyncDisabledState();
+
+  /**
+   * Updates the displayed value by using aValue.
+   */
+  void UpdateDisplayedValue(const nsAString& aValue, bool aNotify);
 };
 
 #endif
 
 
--- a/layout/style/forms.css
+++ b/layout/style/forms.css
@@ -416,17 +416,19 @@ input[type="file"] {
   white-space: nowrap;
   cursor: default;
   -moz-binding: none;
 
   padding: 0 !important;
   border-style: none !important;
 }
 
-input[type="file"] > input[type="text"] {
+input[type="file"] > xul|label {
+  width: 12em;
+
   border-color: inherit;
   background-color: inherit;
   color: inherit;
   font-size: inherit;
   letter-spacing: inherit;
 }
 
 /* button part of file selector */
@@ -620,17 +622,17 @@ optgroup:before {
   text-overflow: inherit;
 }
 
  /*
   * Force the text control child of file input controls to have left-to-right
   * directionality. Otherwise filenames containing right-to-left characters
   * will be reordered with chaotic results.
   */
-input[type="file"] > input[type="text"] {
+input[type="file"] > xul|label {
   direction: ltr !important;
   text-align: inherit;
 }
 
 /**
  * Set default style for invalid elements.
  */
 :not(output):-moz-ui-invalid {