Bug 670083 - expose placeholder as description if wasn't used as name r?surkov draft
authorTakeshi Kurosawa <taken.spc@gmail.com>
Wed, 24 Aug 2016 14:50:31 +0900
changeset 413282 58a4b914a40ab2cbd504bf8cf8353d58f21990ea
parent 413053 f5d043ce6d36a3c461cbd829d4a4a38394b7c436
child 531187 733b4e2f0edaf1b017e2bc013af7f6b4f27046f3
push id29388
push userbmo:taken.spc@gmail.com
push dateTue, 13 Sep 2016 22:13:49 +0000
reviewerssurkov
bugs670083
milestone51.0a1
Bug 670083 - expose placeholder as description if wasn't used as name r?surkov - Pass nameFlag to NativeDescription to prepare further implementation of HTML Accessibility API Mappings - Use placeholder as description if it wasn't used as name MozReview-Commit-ID: ApuMKpUi8iM
accessible/generic/Accessible.cpp
accessible/generic/Accessible.h
accessible/html/HTMLFormControlAccessible.cpp
accessible/html/HTMLFormControlAccessible.h
accessible/tests/mochitest/name/test_general.html
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -140,26 +140,28 @@ Accessible::Name(nsString& aName)
   }
 
   ENameValueFlag nameFlag = NativeName(aName);
   if (!aName.IsEmpty())
     return nameFlag;
 
   // In the end get the name from tooltip.
   if (mContent->IsHTMLElement()) {
+    // https://w3c.github.io/aria/html-aam/html-aam.html
     if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName)) {
       aName.CompressWhitespace();
       return eNameFromTooltip;
     }
   } else if (mContent->IsXULElement()) {
     if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aName)) {
       aName.CompressWhitespace();
       return eNameFromTooltip;
     }
   } else if (mContent->IsSVGElement()) {
+    // https://w3c.github.io/aria/svg-aam/svg-aam.html
     // If user agents need to choose among multiple ‘desc’ or ‘title’ elements
     // for processing, the user agent shall choose the first one.
     for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
          childElm = childElm->GetNextSibling()) {
       if (childElm->IsSVGElement(nsGkAtoms::desc)) {
         nsTextEquivUtils::AppendTextEquivFromContent(this, childElm, &aName);
         return eNameFromTooltip;
       }
@@ -183,43 +185,51 @@ Accessible::Description(nsString& aDescr
 
   if (!HasOwnContent() || mContent->IsNodeOfType(nsINode::eTEXT))
     return;
 
   nsTextEquivUtils::
     GetTextEquivFromIDRefs(this, nsGkAtoms::aria_describedby,
                            aDescription);
 
+  nsAutoString name;
+  bool didNameInitialized = false;
+
   if (aDescription.IsEmpty()) {
-    NativeDescription(aDescription);
-
-    if (aDescription.IsEmpty()) {
+    ENameValueFlag nameFlag = Name(name);
+    NativeDescription(aDescription, nameFlag);
+    didNameInitialized = true;
+
+    if (aDescription.IsEmpty() && nameFlag != eNameFromTooltip) {
       // Keep the Name() method logic.
       if (mContent->IsHTMLElement()) {
+        // https://w3c.github.io/aria/html-aam/html-aam.html
         mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aDescription);
       } else if (mContent->IsXULElement()) {
         mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aDescription);
       } else if (mContent->IsSVGElement()) {
+        // https://w3c.github.io/aria/svg-aam/svg-aam.html
         for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
              childElm = childElm->GetNextSibling()) {
           if (childElm->IsSVGElement(nsGkAtoms::desc)) {
             nsTextEquivUtils::AppendTextEquivFromContent(this, childElm,
                                                          &aDescription);
             break;
           }
         }
       }
     }
   }
 
   if (!aDescription.IsEmpty()) {
     aDescription.CompressWhitespace();
-    nsAutoString name;
-    Name(name);
     // Don't expose a description if it is the same as the name.
+    if (!didNameInitialized) {
+      Name(name);
+    }
     if (aDescription.Equals(name))
       aDescription.Truncate();
   }
 }
 
 KeyBinding
 Accessible::AccessKey() const
 {
@@ -1963,17 +1973,17 @@ Accessible::NativeName(nsString& aName)
     }
   }
 
   return eNameOK;
 }
 
 // Accessible protected
 void
-Accessible::NativeDescription(nsString& aDescription)
+Accessible::NativeDescription(nsString& aDescription, ENameValueFlag nameFlag)
 {
   bool isXUL = mContent->IsXULElement();
   if (isXUL) {
     // Try XUL <description control="[id]">description text</description>
     XULDescriptionIterator iter(Document(), mContent);
     Accessible* descr = nullptr;
     while ((descr = iter.Next())) {
       nsTextEquivUtils::AppendTextEquivFromContent(this, descr->GetContent(),
--- a/accessible/generic/Accessible.h
+++ b/accessible/generic/Accessible.h
@@ -80,17 +80,22 @@ enum ENameValueFlag {
  /**
   * Name was computed from the subtree.
   */
  eNameFromSubtree,
 
  /**
   * Tooltip was used as a name.
   */
- eNameFromTooltip
+ eNameFromTooltip,
+
+ /**
+  * HTML placeholder attribute was used as a name.
+  */
+ eNameFromHTMLPlaceholder
 };
 
 /**
  * Group position (level, position in set and set size).
  */
 struct GroupPos
 {
   GroupPos() : level(0), posInSet(0), setSize(0) { }
@@ -964,17 +969,18 @@ protected:
    * into account ARIA markup used to specify the name.
    */
   virtual mozilla::a11y::ENameValueFlag NativeName(nsString& aName);
 
   /**
    * Return the accessible description provided by native markup. It doesn't take
    * into account ARIA markup used to specify the description.
    */
-  virtual void NativeDescription(nsString& aDescription);
+  virtual void NativeDescription(nsString& aDescription,
+                                 mozilla::a11y::ENameValueFlag nameFlag);
 
   /**
    * Return object attributes provided by native markup. It doesn't take into
    * account ARIA.
    */
   virtual already_AddRefed<nsIPersistentProperties> NativeAttributes();
 
   //////////////////////////////////////////////////////////////////////////////
--- a/accessible/html/HTMLFormControlAccessible.cpp
+++ b/accessible/html/HTMLFormControlAccessible.cpp
@@ -327,22 +327,39 @@ HTMLTextFieldAccessible::NativeName(nsSt
   // If part of compound of XUL widget then grab a name from XUL widget element.
   nsIContent* widgetElm = XULWidgetElm();
   if (widgetElm)
     XULElmName(mDoc, widgetElm, aName);
 
   if (!aName.IsEmpty())
     return eNameOK;
 
+  // https://w3c.github.io/aria/html-aam/html-aam.html#input-type-text-input-type-password-input-type-search-input-type-tel-input-type-url-and-textarea-element
   // text inputs and textareas might have useful placeholder text
   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, aName);
-  return eNameOK;
+  return eNameFromHTMLPlaceholder;
 }
 
 void
+HTMLTextFieldAccessible::NativeDescription(nsString& aDescription,
+                                           ENameValueFlag nameFlag)
+{
+  Accessible::NativeDescription(aDescription, nameFlag);
+  if (!aDescription.IsEmpty())
+    return;
+
+  // https://w3c.github.io/aria/html-aam/html-aam.html#input-type-text-input-type-password-input-type-search-input-type-tel-input-type-url-and-textarea-element
+  // Use description from placeholder.
+  if (nameFlag != eNameFromHTMLPlaceholder) {
+    mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, aDescription);
+  }
+}
+
+
+void
 HTMLTextFieldAccessible::Value(nsString& aValue)
 {
   aValue.Truncate();
   if (NativeState() & states::PROTECTED)    // Don't return password text!
     return;
 
   nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea(do_QueryInterface(mContent));
   if (textArea) {
--- a/accessible/html/HTMLFormControlAccessible.h
+++ b/accessible/html/HTMLFormControlAccessible.h
@@ -134,16 +134,17 @@ public:
   virtual bool IsWidget() const override;
   virtual Accessible* ContainerWidget() const override;
 
 protected:
   virtual ~HTMLTextFieldAccessible() {}
 
   // Accessible
   virtual ENameValueFlag NativeName(nsString& aName) override;
+  virtual void NativeDescription(nsString& aName, ENameValueFlag nameFlag) override;
 
   /**
    * Return a XUL widget element this input is part of.
    */
   nsIContent* XULWidgetElm() const { return mContent->GetBindingParent(); }
 };
 
 
--- a/accessible/tests/mochitest/name/test_general.html
+++ b/accessible/tests/mochitest/name/test_general.html
@@ -201,19 +201,28 @@
 
       testName("textboxinend", "This day was sunny");
       testName("textbox2", "This day was");
 
       // placeholder
       testName("ph_password", "a placeholder");
       testName("ph_text", "a placeholder");
       testName("ph_textarea", "a placeholder");
+
+      // placeholder does not win labels
       testName("ph_text2", "a label");
+      testDescr("ph_text2", "meh");
       testName("ph_textarea2", "a label");
+      testDescr("ph_textarea2", "meh");
       testName("ph_text3", "a label");
+      testDescr("ph_text3", "meh");
+
+      // placeholder wins title attribute
+      testName("ph_text4", "meh");
+      testDescr("ph_text4", "title");
 
       // Test equation image
       testName("img_eq", "x^2 + y^2 + z^2")
       testName("input_img_eq", "x^2 + y^2 + z^2")
       testName("txt_eq", "x^2 + y^2 + z^2")
 
       ////////////////////////////////////////////////////////////////////////
       // tests for duplicate announcement of content
@@ -602,24 +611,28 @@
     </label>
   </form>
 
   <!-- placeholder  -->
   <input id="ph_password" type="password" value="" placeholder="a placeholder" />
   <input id="ph_text" type="text" placeholder="a placeholder" />
   <textarea id="ph_textarea" cols="5" placeholder="a placeholder"></textarea>  
 
-  <!-- placeholder does not win -->
+  <!-- placeholder does not win labels -->
   <input id="ph_text2" type="text" aria-label="a label" placeholder="meh" />
   <textarea id="ph_textarea2" cols="5" aria-labelledby="ph_text2" 
             placeholder="meh"></textarea>
 
   <label for="ph_text3">a label</label>
   <input id="ph_text3" placeholder="meh" />
 
+  <!-- placeholder wins title attribute -->
+  <input id="ph_text4" type="text" placeholder="meh" title="title" />
+
+  <!-- Test equation image -->
   <p>Image: 
     <img id="img_eq" role="math" src="foo" alt="x^2 + y^2 + z^2">
     <input type="image"  id="input_img_eq" src="foo" alt="x^2 + y^2 + z^2">
   </p>
 
   <p>Text: 
     <span id="txt_eq" role="math" title="x^2 + y^2 + z^2">x<sup>2</sup> + 
       y<sup>2</sup> + z<sup>2</sup></span>