AX: Refactor accessibility name computation so it's more platform independent
authorcfleizach@apple.com <cfleizach@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 19 Oct 2012 16:24:43 +0000 (16:24 +0000)
committercfleizach@apple.com <cfleizach@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 19 Oct 2012 16:24:43 +0000 (16:24 +0000)
https://bugs.webkit.org/show_bug.cgi?id=99502

Reviewed by Beth Dakin.

Source/WebCore:

The current model of determining the accessible text for an object has a lot of Mac biases built in
due to legacy implementation.

This change categorizes and orders accessibility text based on WAI-ARIA text computation rules and then
allows the platform (only Mac right now) to decide how best to apply that text to its own AX API.
http://www.w3.org/TR/wai-aria/roles#textalternativecomputation

This change tried very hard not to change any test behavior, even though it exposed a number of weird
edge cases where we were treating attributes differently based on element type.

Future patches will resolve those discrepancies.

* accessibility/AccessibilityImageMapLink.cpp:
(WebCore::AccessibilityImageMapLink::accessibilityText):
* accessibility/AccessibilityImageMapLink.h:
(AccessibilityImageMapLink):
* accessibility/AccessibilityMediaControls.cpp:
(WebCore::AccessibilityMediaControl::accessibilityText):
* accessibility/AccessibilityMediaControls.h:
(AccessibilityMediaControl):
(WebCore::AccessibilityMediaTimeDisplay::isMediaControlLabel):
* accessibility/AccessibilityNodeObject.cpp:
(WebCore::AccessibilityNodeObject::titleElementText):
(WebCore::AccessibilityNodeObject::accessibilityText):
(WebCore::AccessibilityNodeObject::ariaLabeledByText):
(WebCore::AccessibilityNodeObject::alternativeText):
(WebCore::AccessibilityNodeObject::alternativeTextForWebArea):
(WebCore::AccessibilityNodeObject::visibleText):
(WebCore::AccessibilityNodeObject::helpText):
(WebCore::AccessibilityNodeObject::ariaDescribedByAttribute):
* accessibility/AccessibilityNodeObject.h:
(AccessibilityNodeObject):
* accessibility/AccessibilityObject.h:
(AccessibilityText):
(WebCore::AccessibilityText::AccessibilityText):
(WebCore::AccessibilityObject::isMediaControlLabel):
(AccessibilityObject):
(WebCore::AccessibilityObject::accessibilityText):
(WebCore::AccessibilityObject::setAccessibleName):
(WebCore::AccessibilityObject::accessibilityDescription):
(WebCore::AccessibilityObject::title):
(WebCore::AccessibilityObject::helpText):
(WebCore::AccessibilityObject::stringValue):
(WebCore::AccessibilityObject::textUnderElement):
(WebCore::AccessibilityObject::text):
(WebCore::AccessibilityObject::textLength):
(WebCore::AccessibilityObject::setRoleValue):
(WebCore::AccessibilityObject::roleValue):
(WebCore::AccessibilityObject::selection):
(WebCore::AccessibilityObject::hierarchicalLevel):
* accessibility/AccessibilityRenderObject.cpp:
* accessibility/AccessibilityRenderObject.h:
(AccessibilityRenderObject):
* accessibility/mac/WebAccessibilityObjectWrapper.mm:
(-[WebAccessibilityObjectWrapper titleTagShouldBeUsedInDescriptionField]):
(-[WebAccessibilityObjectWrapper accessibilityTitle]):
(-[WebAccessibilityObjectWrapper accessibilityDescription]):
(-[WebAccessibilityObjectWrapper accessibilityHelpText]):
(-[WebAccessibilityObjectWrapper accessibilityAttributeValue:]):
* platform/LocalizedStrings.cpp:
(WebCore::localizedMediaControlElementHelpText):

LayoutTests:

Update a test to reflect change in what shoud be recognized as a title.

* platform/mac/accessibility/aria-radiobutton-text.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@131905 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
LayoutTests/ChangeLog
LayoutTests/platform/chromium/TestExpectations
LayoutTests/platform/mac/accessibility/aria-radiobutton-text.html
Source/WebCore/ChangeLog
Source/WebCore/accessibility/AccessibilityImageMapLink.cpp
Source/WebCore/accessibility/AccessibilityImageMapLink.h
Source/WebCore/accessibility/AccessibilityMediaControls.cpp
Source/WebCore/accessibility/AccessibilityMediaControls.h
Source/WebCore/accessibility/AccessibilityNodeObject.cpp
Source/WebCore/accessibility/AccessibilityNodeObject.h
Source/WebCore/accessibility/AccessibilityObject.h
Source/WebCore/accessibility/AccessibilityRenderObject.cpp
Source/WebCore/accessibility/AccessibilityRenderObject.h
Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapper.mm
Source/WebCore/platform/LocalizedStrings.cpp

index d1628b5..73292ff 100644 (file)
@@ -1,3 +1,14 @@
+2012-10-19  Chris Fleizach  <cfleizach@apple.com>
+
+        AX: Refactor accessibility name computation so it's more platform independent
+        https://bugs.webkit.org/show_bug.cgi?id=99502
+
+        Reviewed by Beth Dakin.
+
+        Update a test to reflect change in what shoud be recognized as a title.
+
+        * platform/mac/accessibility/aria-radiobutton-text.html:
+
 2012-10-19  Zan Dobersek  <zandobersek@gmail.com>
 
         [WK2][GTK] Fullscreen tests timing out in bots
index b994227..4650d25 100644 (file)
@@ -1420,6 +1420,7 @@ crbug.com/10322 accessibility/visible-elements.html [ Skip ]
 
 webkit.org/b/97359 accessibility/svg-bounds.html [ Skip ]
 webkit.org/b/73912 accessibility/aria-checkbox-text.html [ Skip ]
+webkit.org/b/99665 accessibility/loading-iframe-sends-notification.html [ Skip ]
 
 webkit.org/b/73912 accessibility/aria-checkbox-sends-notification.html [ Failure Pass ]
 
index 919b4da..0fa4948 100644 (file)
@@ -30,7 +30,9 @@
           var succeeded = obj.childAtIndex(1).title == "AXTitle: Two";
           shouldBe("succeeded", "true");
 
-          var succeeded = obj.childAtIndex(2).title == "AXTitle: Three";
+          // Because the third button uses aria-labelledby, the accessible name
+          // will be inside the description field.
+          var succeeded = obj.childAtIndex(2).description == "AXDescription: Three";
           shouldBe("succeeded", "true");
     }
 
index b48ec11..e9afb99 100644 (file)
@@ -1,3 +1,72 @@
+2012-10-17  Chris Fleizach  <cfleizach@apple.com>
+
+        AX: Refactor accessibility name computation so it's more platform independent
+        https://bugs.webkit.org/show_bug.cgi?id=99502
+
+        Reviewed by Beth Dakin.
+
+        The current model of determining the accessible text for an object has a lot of Mac biases built in
+        due to legacy implementation. 
+
+        This change categorizes and orders accessibility text based on WAI-ARIA text computation rules and then
+        allows the platform (only Mac right now) to decide how best to apply that text to its own AX API.
+        http://www.w3.org/TR/wai-aria/roles#textalternativecomputation
+
+        This change tried very hard not to change any test behavior, even though it exposed a number of weird
+        edge cases where we were treating attributes differently based on element type. 
+
+        Future patches will resolve those discrepancies.
+
+        * accessibility/AccessibilityImageMapLink.cpp:
+        (WebCore::AccessibilityImageMapLink::accessibilityText):
+        * accessibility/AccessibilityImageMapLink.h:
+        (AccessibilityImageMapLink):
+        * accessibility/AccessibilityMediaControls.cpp:
+        (WebCore::AccessibilityMediaControl::accessibilityText):
+        * accessibility/AccessibilityMediaControls.h:
+        (AccessibilityMediaControl):
+        (WebCore::AccessibilityMediaTimeDisplay::isMediaControlLabel):
+        * accessibility/AccessibilityNodeObject.cpp:
+        (WebCore::AccessibilityNodeObject::titleElementText):
+        (WebCore::AccessibilityNodeObject::accessibilityText):
+        (WebCore::AccessibilityNodeObject::ariaLabeledByText):
+        (WebCore::AccessibilityNodeObject::alternativeText):
+        (WebCore::AccessibilityNodeObject::alternativeTextForWebArea):
+        (WebCore::AccessibilityNodeObject::visibleText):
+        (WebCore::AccessibilityNodeObject::helpText):
+        (WebCore::AccessibilityNodeObject::ariaDescribedByAttribute):
+        * accessibility/AccessibilityNodeObject.h:
+        (AccessibilityNodeObject):
+        * accessibility/AccessibilityObject.h:
+        (AccessibilityText):
+        (WebCore::AccessibilityText::AccessibilityText):
+        (WebCore::AccessibilityObject::isMediaControlLabel):
+        (AccessibilityObject):
+        (WebCore::AccessibilityObject::accessibilityText):
+        (WebCore::AccessibilityObject::setAccessibleName):
+        (WebCore::AccessibilityObject::accessibilityDescription):
+        (WebCore::AccessibilityObject::title):
+        (WebCore::AccessibilityObject::helpText):
+        (WebCore::AccessibilityObject::stringValue):
+        (WebCore::AccessibilityObject::textUnderElement):
+        (WebCore::AccessibilityObject::text):
+        (WebCore::AccessibilityObject::textLength):
+        (WebCore::AccessibilityObject::setRoleValue):
+        (WebCore::AccessibilityObject::roleValue):
+        (WebCore::AccessibilityObject::selection):
+        (WebCore::AccessibilityObject::hierarchicalLevel):
+        * accessibility/AccessibilityRenderObject.cpp:
+        * accessibility/AccessibilityRenderObject.h:
+        (AccessibilityRenderObject):
+        * accessibility/mac/WebAccessibilityObjectWrapper.mm:
+        (-[WebAccessibilityObjectWrapper titleTagShouldBeUsedInDescriptionField]):
+        (-[WebAccessibilityObjectWrapper accessibilityTitle]):
+        (-[WebAccessibilityObjectWrapper accessibilityDescription]):
+        (-[WebAccessibilityObjectWrapper accessibilityHelpText]):
+        (-[WebAccessibilityObjectWrapper accessibilityAttributeValue:]):
+        * platform/LocalizedStrings.cpp:
+        (WebCore::localizedMediaControlElementHelpText):
+
 2012-10-19  Kent Tamura  <tkent@chromium.org>
 
         Use Localizer::monthFormat to construct input[type=month] UI
index 13ff212..9089f66 100644 (file)
@@ -94,6 +94,21 @@ KURL AccessibilityImageMapLink::url() const
     
     return m_areaElement->href();
 }
+
+void AccessibilityImageMapLink::accessibilityText(Vector<AccessibilityText>& textOrder)
+{
+    String description = accessibilityDescription();
+    if (!description.isEmpty())
+        textOrder.append(AccessibilityText(description, AlternativeText));
+
+    const AtomicString& titleText = getAttribute(titleAttr);
+    if (!titleText.isEmpty())
+        textOrder.append(AccessibilityText(titleText, TitleTagText));
+
+    const AtomicString& summary = getAttribute(summaryAttr);
+    if (!summary.isEmpty())
+        textOrder.append(AccessibilityText(summary, SummaryText));
+}
     
 String AccessibilityImageMapLink::accessibilityDescription() const
 {
index bc6ce3c..0447b63 100644 (file)
@@ -73,6 +73,7 @@ private:
     RefPtr<HTMLAreaElement> m_areaElement;
     RefPtr<HTMLMapElement> m_mapElement;
     
+    virtual void accessibilityText(Vector<AccessibilityText>&);
     virtual bool isImageMapLink() const { return true; }
 };
     
index 3d380df..887c7e2 100644 (file)
@@ -140,6 +140,22 @@ String AccessibilityMediaControl::controlTypeName() const
     return String();
 }
 
+void AccessibilityMediaControl::accessibilityText(Vector<AccessibilityText>& textOrder)
+{
+    String description = accessibilityDescription();
+    if (!description.isEmpty())
+        textOrder.append(AccessibilityText(description, AlternativeText));
+
+    String title = this->title();
+    if (!title.isEmpty())
+        textOrder.append(AccessibilityText(title, AlternativeText));
+
+    String helptext = helpText();
+    if (!helptext.isEmpty())
+        textOrder.append(AccessibilityText(helptext, HelpText));
+}
+    
+
 String AccessibilityMediaControl::title() const
 {
     DEFINE_STATIC_LOCAL(const String, controlsPanel, (ASCIILiteral("ControlsPanel")));
index 05c03ee..30d9d5e 100644 (file)
@@ -54,6 +54,7 @@ protected:
     explicit AccessibilityMediaControl(RenderObject*);
     MediaControlElementType controlType() const;
     String controlTypeName() const;
+    virtual void accessibilityText(Vector<AccessibilityText>&);
 };
 
 
@@ -107,6 +108,7 @@ public:
 
 private:
     explicit AccessibilityMediaTimeDisplay(RenderObject*);
+    virtual bool isMediaControlLabel() const { return true; }
 };
 
 
index 2b5f85c..96c6da9 100644 (file)
@@ -1079,6 +1079,215 @@ AccessibilityObject* AccessibilityNodeObject::menuButtonForMenu() const
     return 0;
 }
 
+void AccessibilityNodeObject::titleElementText(Vector<AccessibilityText>& textOrder)
+{
+    Node* node = this->node();
+    if (!node)
+        return;
+    
+    bool isInputTag = node->hasTagName(inputTag);
+    if (isInputTag || AccessibilityObject::isARIAInput(ariaRoleAttribute()) || isControl()) {
+        HTMLLabelElement* label = labelForElement(toElement(node));
+        if (label) {
+            AccessibilityObject* labelObject = axObjectCache()->getOrCreate(label);
+            textOrder.append(AccessibilityText(label->innerText(), LabelByElementText, labelObject));
+            return;
+        }
+    }
+    
+    AccessibilityObject* titleUIElement = this->titleUIElement();
+    if (titleUIElement)
+        textOrder.append(AccessibilityText(String(), LabelByElementText, titleUIElement));
+}
+
+void AccessibilityNodeObject::alternativeText(Vector<AccessibilityText>& textOrder) const
+{
+    if (isWebArea()) {
+        String webAreaText = alternativeTextForWebArea();
+        if (!webAreaText.isEmpty())
+            textOrder.append(AccessibilityText(webAreaText, AlternativeText));
+        return;
+    }
+    
+    ariaLabeledByText(textOrder);
+    
+    const AtomicString& ariaLabel = getAttribute(aria_labelAttr);
+    if (!ariaLabel.isEmpty())
+        textOrder.append(AccessibilityText(ariaLabel, AlternativeText));
+    
+    if (isImage() || isInputImage() || isNativeImage() || isCanvas()) {
+        // Images should use alt as long as the attribute is present, even if empty.
+        // Otherwise, it should fallback to other methods, like the title attribute.
+        const AtomicString& alt = getAttribute(altAttr);
+        if (!alt.isNull())
+            textOrder.append(AccessibilityText(alt, AlternativeText));
+    }
+    
+#if ENABLE(MATHML)
+    Node* node = this->node();
+    if (node && node->isElementNode() && toElement(node)->isMathMLElement())
+        textOrder.append(AccessibilityText(getAttribute(MathMLNames::alttextAttr), AlternativeText));
+#endif
+}
+
+void AccessibilityNodeObject::visibleText(Vector<AccessibilityText>& textOrder) const
+{
+    Node* node = this->node();
+    if (!node)
+        return;
+    
+    bool isInputTag = node->hasTagName(inputTag);
+    if (isInputTag) {
+        HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
+        if (input->isTextButton()) {
+            textOrder.append(AccessibilityText(input->valueWithDefault(), VisibleText));
+            return;
+        }
+    }
+    
+    // If this node isn't rendered, there's no inner text we can extract from a select element.
+    if (!isAccessibilityRenderObject() && node->hasTagName(selectTag))
+        return;
+    
+    bool useTextUnderElement = false;
+    
+    switch (roleValue()) {
+    case PopUpButtonRole:
+    case ButtonRole:
+    case ToggleButtonRole:
+    case CheckBoxRole:
+    case ListBoxOptionRole:
+    case MenuButtonRole:
+    case MenuItemRole:
+    case RadioButtonRole:
+    case TabRole:
+        useTextUnderElement = true;
+        break;
+    default:
+        break;
+    }
+    
+    // If it's focusable but it's not content editable or a known control type, then it will appear to
+    // the user as a single atomic object, so we should use its text as the default title.
+    if (isHeading() || isLink() || isGenericFocusableElement())
+        useTextUnderElement = true;
+    
+    if (useTextUnderElement) {
+        String text = textUnderElement();
+        if (!text.isEmpty())
+            textOrder.append(AccessibilityText(text, ChildrenText));
+    }
+}
+
+void AccessibilityNodeObject::helpText(Vector<AccessibilityText>& textOrder) const
+{
+    const AtomicString& ariaHelp = getAttribute(aria_helpAttr);
+    if (!ariaHelp.isEmpty())
+        textOrder.append(AccessibilityText(ariaHelp, HelpText));
+    
+    String describedBy = ariaDescribedByAttribute();
+    if (!describedBy.isEmpty())
+        textOrder.append(AccessibilityText(describedBy, SummaryText));
+    
+    // Add help type text that is derived from ancestors.
+    for (Node* curr = node(); curr; curr = curr->parentNode()) {
+        const AtomicString& summary = getAttribute(summaryAttr);
+        if (!summary.isEmpty())
+            textOrder.append(AccessibilityText(summary, SummaryText));
+        
+        // The title attribute should be used as help text unless it is already being used as descriptive text.
+        const AtomicString& title = getAttribute(titleAttr);
+        if (!title.isEmpty())
+            textOrder.append(AccessibilityText(title, TitleTagText));
+        
+        // Only take help text from an ancestor element if its a group or an unknown role. If help was
+        // added to those kinds of elements, it is likely it was meant for a child element.
+        AccessibilityObject* axObj = axObjectCache()->getOrCreate(curr);
+        if (!axObj)
+            return;
+        
+        AccessibilityRole role = axObj->roleValue();
+        if (role != GroupRole && role != UnknownRole)
+            break;
+    }
+}
+
+void AccessibilityNodeObject::accessibilityText(Vector<AccessibilityText>& textOrder)
+{
+    titleElementText(textOrder);
+    alternativeText(textOrder);
+    visibleText(textOrder);
+    helpText(textOrder);
+    
+    String placeholder = placeholderValue();
+    if (!placeholder.isEmpty())
+        textOrder.append(AccessibilityText(placeholder, PlaceholderText));
+}
+    
+void AccessibilityNodeObject::ariaLabeledByText(Vector<AccessibilityText>& textOrder) const
+{
+    String ariaLabeledBy = ariaLabeledByAttribute();
+    if (!ariaLabeledBy.isEmpty()) {
+        Vector<Element*> elements;
+        ariaLabeledByElements(elements);
+        
+        Vector<RefPtr<AccessibilityObject> > axElements;
+        unsigned length = elements.size();
+        for (unsigned k = 0; k < length; k++) {
+            RefPtr<AccessibilityObject> axElement = axObjectCache()->getOrCreate(elements[k]);
+            axElements.append(axElement);
+        }
+        
+        textOrder.append(AccessibilityText(ariaLabeledBy, AlternativeText, axElements));
+    }
+}
+    
+String AccessibilityNodeObject::alternativeTextForWebArea() const
+{
+    // The WebArea description should follow this order:
+    //     aria-label on the <html>
+    //     title on the <html>
+    //     <title> inside the <head> (of it was set through JS)
+    //     name on the <html>
+    // For iframes:
+    //     aria-label on the <iframe>
+    //     title on the <iframe>
+    //     name on the <iframe>
+    
+    Document* document = this->document();
+    if (!document)
+        return String();
+    
+    // Check if the HTML element has an aria-label for the webpage.
+    if (Element* documentElement = document->documentElement()) {
+        const AtomicString& ariaLabel = documentElement->getAttribute(aria_labelAttr);
+        if (!ariaLabel.isEmpty())
+            return ariaLabel;
+    }
+    
+    Node* owner = document->ownerElement();
+    if (owner) {
+        if (owner->hasTagName(frameTag) || owner->hasTagName(iframeTag)) {
+            const AtomicString& title = static_cast<HTMLFrameElementBase*>(owner)->getAttribute(titleAttr);
+            if (!title.isEmpty())
+                return title;
+            return static_cast<HTMLFrameElementBase*>(owner)->getNameAttribute();
+        }
+        if (owner->isHTMLElement())
+            return toHTMLElement(owner)->getNameAttribute();
+    }
+    
+    String documentTitle = document->title();
+    if (!documentTitle.isEmpty())
+        return documentTitle;
+    
+    owner = document->body();
+    if (owner && owner->isHTMLElement())
+        return toHTMLElement(owner)->getNameAttribute();
+    
+    return String();
+}
+    
 String AccessibilityNodeObject::accessibilityDescription() const
 {
     // Static text should not have a description, it should only have a stringValue.
@@ -1346,6 +1555,14 @@ String AccessibilityNodeObject::accessibilityDescriptionForElements(Vector<Eleme
     return builder.toString();
 }
 
+String AccessibilityNodeObject::ariaDescribedByAttribute() const
+{
+    Vector<Element*> elements;
+    elementsFromAttribute(elements, aria_describedbyAttr);
+    
+    return accessibilityDescriptionForElements(elements);
+}
+
 void AccessibilityNodeObject::elementsFromAttribute(Vector<Element*>& elements, const QualifiedName& attribute) const
 {
     Node* node = this->node();
index d65b1e0..a2c9078 100644 (file)
@@ -174,13 +174,22 @@ protected:
     void ariaLabeledByElements(Vector<Element*>& elements) const;
     String accessibilityDescriptionForElements(Vector<Element*> &elements) const;
     void elementsFromAttribute(Vector<Element*>& elements, const QualifiedName&) const;
-
+    String ariaDescribedByAttribute() const;
+    
     Element* menuElementForMenuButton() const;
     Element* menuItemElementForMenu() const;
     AccessibilityObject* menuButtonForMenu() const;
 
 private:
     Node* m_node;
+
+    virtual void accessibilityText(Vector<AccessibilityText>&);
+    void titleElementText(Vector<AccessibilityText>&);
+    void alternativeText(Vector<AccessibilityText>&) const;
+    void visibleText(Vector<AccessibilityText>&) const;
+    void helpText(Vector<AccessibilityText>&) const;
+    String alternativeTextForWebArea() const;
+    void ariaLabeledByText(Vector<AccessibilityText>&) const;
 };
 
 inline AccessibilityNodeObject* toAccessibilityNodeObject(AccessibilityObject* object)
index be62116..ca19c87 100644 (file)
@@ -201,6 +201,41 @@ enum AccessibilityRole {
     WindowRole,
 };
 
+enum AccessibilityTextSource {
+    AlternativeText,
+    ChildrenText,
+    SummaryText,
+    HelpText,
+    VisibleText,
+    TitleTagText,
+    PlaceholderText,
+    LabelByElementText,
+};
+    
+struct AccessibilityText {
+    String text;
+    AccessibilityTextSource textSource;
+    Vector<RefPtr<AccessibilityObject> > textElements;
+    
+    AccessibilityText(const String& t, const AccessibilityTextSource& s)
+    : text(t)
+    , textSource(s)
+    { }
+
+    AccessibilityText(const String& t, const AccessibilityTextSource& s, const Vector<RefPtr<AccessibilityObject> > elements)
+    : text(t)
+    , textSource(s)
+    , textElements(elements)
+    { }
+
+    AccessibilityText(const String& t, const AccessibilityTextSource& s, const RefPtr<AccessibilityObject> element)
+    : text(t)
+    , textSource(s)
+    {
+        textElements.append(element);
+    }
+};
+    
 enum AccessibilityOrientation {
     AccessibilityOrientationVertical,
     AccessibilityOrientationHorizontal,
@@ -371,6 +406,7 @@ public:
     virtual bool isSpinButton() const { return roleValue() == SpinButtonRole; }
     virtual bool isSpinButtonPart() const { return false; }
     virtual bool isMockObject() const { return false; }
+    virtual bool isMediaControlLabel() const { return false; }
     bool isTextControl() const { return roleValue() == TextAreaRole || roleValue() == TextFieldRole; }
     bool isARIATextControl() const;
     bool isTabList() const { return roleValue() == TabListRole; }
@@ -430,9 +466,6 @@ public:
     virtual bool canSetSelectedChildrenAttribute() const { return false; }
     virtual bool canSetExpandedAttribute() const { return false; }
     
-    // A programmatic way to set a name on an AccessibleObject.
-    virtual void setAccessibleName(const AtomicString&) { }
-    
     virtual Node* node() const { return 0; }
     virtual RenderObject* renderer() const { return 0; }
     virtual bool accessibilityIsIgnored() const  { return true; }
@@ -501,11 +534,28 @@ public:
     virtual bool isPresentationalChildOfAriaRole() const { return false; }
     virtual bool ariaRoleHasPresentationalChildren() const { return false; }
 
-    void setRoleValue(AccessibilityRole role) { m_role = role; }
-    virtual AccessibilityRole roleValue() const { return m_role; }
+    // Accessibility Text
+    virtual void accessibilityText(Vector<AccessibilityText>&) { };
+
+    // A programmatic way to set a name on an AccessibleObject.
+    virtual void setAccessibleName(const AtomicString&) { }
+
+    // Accessibility Text - (To be deprecated).
+    virtual String accessibilityDescription() const { return String(); }
+    virtual String title() const { return String(); }
+    virtual String helpText() const { return String(); }
+
+    // Methods for determining accessibility text.
+    virtual String stringValue() const { return String(); }
+    virtual String textUnderElement() const { return String(); }
+    virtual String text() const { return String(); }
+    virtual int textLength() const { return 0; }
     virtual String ariaLabeledByAttribute() const { return String(); }
     virtual String ariaDescribedByAttribute() const { return String(); }
-    virtual String accessibilityDescription() const { return String(); }
+    const AtomicString& placeholderValue() const;
+
+    void setRoleValue(AccessibilityRole role) { m_role = role; }
+    virtual AccessibilityRole roleValue() const { return m_role; }
 
     virtual AXObjectCache* axObjectCache() const;
     AXID axObjectID() const { return m_id; }
@@ -529,12 +579,6 @@ public:
     
     virtual KURL url() const { return KURL(); }
     virtual VisibleSelection selection() const { return VisibleSelection(); }
-    virtual String stringValue() const { return String(); }
-    virtual String title() const { return String(); }
-    virtual String helpText() const { return String(); }
-    virtual String textUnderElement() const { return String(); }
-    virtual String text() const { return String(); }
-    virtual int textLength() const { return 0; }
     virtual String selectedText() const { return String(); }
     virtual const AtomicString& accessKey() const { return nullAtom; }
     const String& actionVerb() const;
@@ -546,7 +590,6 @@ public:
     virtual FrameView* documentFrameView() const;
     String language() const;
     virtual unsigned hierarchicalLevel() const { return 0; }
-    const AtomicString& placeholderValue() const;
     
     virtual void setFocused(bool) { }
     virtual void setSelectedText(const String&) { }
index 535de57..806c93c 100644 (file)
@@ -725,72 +725,6 @@ HTMLLabelElement* AccessibilityRenderObject::labelElementContainer() const
     return 0;
 }
 
-String AccessibilityRenderObject::ariaDescribedByAttribute() const
-{
-    Vector<Element*> elements;
-    elementsFromAttribute(elements, aria_describedbyAttr);
-    
-    return accessibilityDescriptionForElements(elements);
-}
-    
-String AccessibilityRenderObject::webAreaAccessibilityDescription() const
-{
-    // The WebArea description should follow this order:
-    //     aria-label on the <html>
-    //     title on the <html>
-    //     <title> inside the <head> (of it was set through JS)
-    //     name on the <html>
-    // For iframes:
-    //     aria-label on the <iframe>
-    //     title on the <iframe>
-    //     name on the <iframe>
-    
-    if (!m_renderer)
-        return String();
-    
-    Document* document = m_renderer->document();
-    
-    // Check if the HTML element has an aria-label for the webpage.
-    if (Element* documentElement = document->documentElement()) {
-        const AtomicString& ariaLabel = documentElement->getAttribute(aria_labelAttr);
-        if (!ariaLabel.isEmpty())
-            return ariaLabel;
-    }
-    
-    Node* owner = document->ownerElement();
-    if (owner) {
-        if (owner->hasTagName(frameTag) || owner->hasTagName(iframeTag)) {
-            const AtomicString& title = static_cast<HTMLFrameElementBase*>(owner)->getAttribute(titleAttr);
-            if (!title.isEmpty())
-                return title;
-            return static_cast<HTMLFrameElementBase*>(owner)->getNameAttribute();
-        }
-        if (owner->isHTMLElement())
-            return toHTMLElement(owner)->getNameAttribute();
-    }
-
-    String documentTitle = document->title();
-    if (!documentTitle.isEmpty())
-        return documentTitle;
-    
-    owner = document->body();
-    if (owner && owner->isHTMLElement())
-        return toHTMLElement(owner)->getNameAttribute();
-    
-    return String();
-}
-    
-String AccessibilityRenderObject::accessibilityDescription() const
-{
-    if (!m_renderer)
-        return String();
-
-    if (isWebArea())
-        return webAreaAccessibilityDescription();
-
-    return AccessibilityNodeObject::accessibilityDescription();
-}
-
 LayoutRect AccessibilityRenderObject::boundingBoxRect() const
 {
     RenderObject* obj = m_renderer;
index 539b5e2..57493f3 100644 (file)
@@ -143,8 +143,6 @@ public:
     virtual PlainTextRange selectedTextRange() const;
     virtual VisibleSelection selection() const;
     virtual String stringValue() const;
-    virtual String ariaDescribedByAttribute() const;
-    virtual String accessibilityDescription() const;
     virtual String helpText() const;
     virtual String textUnderElement() const;
     virtual String text() const;
@@ -261,8 +259,6 @@ private:
     bool elementAttributeValue(const QualifiedName&) const;
     void setElementAttributeValue(const QualifiedName&, bool);
     
-    String webAreaAccessibilityDescription() const;
-
     virtual ESpeak speakProperty() const;
     
     virtual const AtomicString& ariaLiveRegionStatus() const;
index 6ec215c..552f9ff 100644 (file)
@@ -1826,6 +1826,103 @@ static NSString* roleValueToNSString(AccessibilityRole value)
     return [self remoteAccessibilityParentObject];
 }
 
+// FIXME: Different kinds of elements are putting the title tag to use in different
+// AX fields. This should be rectified, but in the initial patch I want to achieve
+// parity with existing behavior.
+- (BOOL)titleTagShouldBeUsedInDescriptionField
+{
+    return (m_object->isLink() && !m_object->isImageMapLink()) || m_object->isImage();
+}
+
+// This should be the "visible" text that's actually on the screen if possible.
+// If there's alternative text, that can override the title.
+- (NSString *)accessibilityTitle
+{
+    // Static text objects should not have a title. Its content is communicated in its AXValue.
+    if (m_object->roleValue() == StaticTextRole)
+        return [NSString string];
+
+    Vector<AccessibilityText> textOrder;
+    m_object->accessibilityText(textOrder);
+    
+    unsigned length = textOrder.size();
+    for (unsigned k = 0; k < length; k++) {
+        const AccessibilityText& text = textOrder[k];
+        
+        // Once we encounter visible text, or the text from our children that should be used foremost.
+        if (text.textSource == VisibleText || text.textSource == ChildrenText)
+            return text.text;
+        
+        // If there's an element that labels this object and it's not exposed, then we should use
+        // that text as our title.
+        if (text.textSource == LabelByElementText && !m_object->exposesTitleUIElement())
+            return text.text;
+        
+        // FIXME: The title tag is used in certain cases for the title. This usage should
+        // probably be in the description field since it's not "visible".
+        if (text.textSource == TitleTagText && ![self titleTagShouldBeUsedInDescriptionField])
+            return text.text;
+    }
+    
+    return [NSString string];
+}
+
+- (NSString *)accessibilityDescription
+{
+    // Static text objects should not have a description. Its content is communicated in its AXValue.
+    // One exception is the media control labels that have a value and a description. Those are set programatically.
+    if (m_object->roleValue() == StaticTextRole && !m_object->isMediaControlLabel())
+        return [NSString string];
+    
+    Vector<AccessibilityText> textOrder;
+    m_object->accessibilityText(textOrder);
+    
+    unsigned length = textOrder.size();
+    for (unsigned k = 0; k < length; k++) {
+        const AccessibilityText& text = textOrder[k];
+        
+        if (text.textSource == AlternativeText)
+            return text.text;
+        
+        if (text.textSource == TitleTagText && [self titleTagShouldBeUsedInDescriptionField])
+            return text.text;
+    }
+    
+    return [NSString string];
+}
+
+- (NSString *)accessibilityHelpText
+{
+    Vector<AccessibilityText> textOrder;
+    m_object->accessibilityText(textOrder);
+    
+    unsigned length = textOrder.size();
+    bool descriptiveTextAvailable = false;
+    for (unsigned k = 0; k < length; k++) {
+        const AccessibilityText& text = textOrder[k];
+        
+        if (text.textSource == HelpText || text.textSource == SummaryText)
+            return text.text;
+        
+        // If an element does NOT have other descriptive text the title tag should be used as its descriptive text.
+        // But, if those ARE available, then the title tag should be used for help text instead.
+        switch (text.textSource) {
+        case AlternativeText:
+        case VisibleText:
+        case ChildrenText:
+        case LabelByElementText:
+            descriptiveTextAvailable = true;
+        default:
+            break;
+        }
+        
+        if (text.textSource == TitleTagText && descriptiveTextAvailable)
+            return text.text;
+    }
+    
+    return [NSString string];
+}
+
 // FIXME: split up this function in a better way.  
 // suggestions: Use a hash table that maps attribute names to function calls,
 // or maybe pointers to member functions
@@ -1992,7 +2089,8 @@ static NSString* roleValueToNSString(AccessibilityRole value)
             if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityTitleAttribute]) 
                 return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityTitleAttribute];
         }
-        return m_object->title();
+        
+        return [self accessibilityTitle];
     }
     
     if ([attributeName isEqualToString: NSAccessibilityDescriptionAttribute]) {
@@ -2000,7 +2098,7 @@ static NSString* roleValueToNSString(AccessibilityRole value)
             if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityDescriptionAttribute])
                 return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityDescriptionAttribute];
         }
-        return m_object->accessibilityDescription();
+        return [self accessibilityDescription];
     }
 
     if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) {
@@ -2054,7 +2152,7 @@ static NSString* roleValueToNSString(AccessibilityRole value)
         return [NSNumber numberWithFloat:m_object->maxValueForRange()];
 
     if ([attributeName isEqualToString: NSAccessibilityHelpAttribute])
-        return m_object->helpText();
+        return [self accessibilityHelpText];
 
     if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute])
         return [NSNumber numberWithBool: m_object->isFocused()];
index 5a7ddf8..abf1bca 100644 (file)
@@ -882,6 +882,10 @@ String localizedMediaControlElementHelpText(const String& name)
     if (name == "HideClosedCaptionsButton")
         return WEB_UI_STRING("stop displaying closed captions", "accessibility help text for hide closed captions button");
 
+    // The description of this button is descriptive enough that it doesn't require help text.
+    if (name == "EnterFullscreenButton")
+        return String();
+    
     ASSERT_NOT_REACHED();
     return String();
 }