OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) 2012, Google Inc. All rights reserved. | 2 * Copyright (C) 2012, Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * | 7 * |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 12 matching lines...) Expand all Loading... | |
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 */ | 27 */ |
28 | 28 |
29 #include "config.h" | 29 #include "config.h" |
30 #include "modules/accessibility/AXNodeObject.h" | 30 #include "modules/accessibility/AXNodeObject.h" |
31 | 31 |
32 #include "core/InputTypeNames.h" | 32 #include "core/InputTypeNames.h" |
33 #include "core/dom/Element.h" | |
33 #include "core/dom/NodeTraversal.h" | 34 #include "core/dom/NodeTraversal.h" |
34 #include "core/dom/Text.h" | 35 #include "core/dom/Text.h" |
35 #include "core/dom/shadow/ComposedTreeTraversal.h" | 36 #include "core/dom/shadow/ComposedTreeTraversal.h" |
36 #include "core/html/HTMLDListElement.h" | 37 #include "core/html/HTMLDListElement.h" |
37 #include "core/html/HTMLFieldSetElement.h" | 38 #include "core/html/HTMLFieldSetElement.h" |
38 #include "core/html/HTMLFrameElementBase.h" | 39 #include "core/html/HTMLFrameElementBase.h" |
39 #include "core/html/HTMLImageElement.h" | 40 #include "core/html/HTMLImageElement.h" |
40 #include "core/html/HTMLInputElement.h" | 41 #include "core/html/HTMLInputElement.h" |
41 #include "core/html/HTMLLabelElement.h" | 42 #include "core/html/HTMLLabelElement.h" |
42 #include "core/html/HTMLLegendElement.h" | 43 #include "core/html/HTMLLegendElement.h" |
(...skipping 1447 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1490 return String(); | 1491 return String(); |
1491 | 1492 |
1492 if (ariaRoleAttribute() == StaticTextRole) { | 1493 if (ariaRoleAttribute() == StaticTextRole) { |
1493 String staticText = text(); | 1494 String staticText = text(); |
1494 if (!staticText.length()) | 1495 if (!staticText.length()) |
1495 staticText = deprecatedTextUnderElement(TextUnderElementAll); | 1496 staticText = deprecatedTextUnderElement(TextUnderElementAll); |
1496 return staticText; | 1497 return staticText; |
1497 } | 1498 } |
1498 | 1499 |
1499 if (node->isTextNode()) | 1500 if (node->isTextNode()) |
1500 return deprecatedTextUnderElement(TextUnderElementAll); | 1501 return deprecatedTextUnderElement(TextUnderElementAll); |
aboxhall
2015/11/05 17:49:21
Should this be addressed in this change?
dmazzoni
2015/11/06 00:21:17
Not yet. Changing this could affect existing Chrom
| |
1501 | 1502 |
1502 if (isHTMLSelectElement(*node)) { | 1503 if (isHTMLSelectElement(*node)) { |
1503 HTMLSelectElement& selectElement = toHTMLSelectElement(*node); | 1504 HTMLSelectElement& selectElement = toHTMLSelectElement(*node); |
1504 int selectedIndex = selectElement.selectedIndex(); | 1505 int selectedIndex = selectElement.selectedIndex(); |
1505 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& listItems = sel ectElement.listItems(); | 1506 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& listItems = sel ectElement.listItems(); |
1506 if (selectedIndex >= 0 && static_cast<size_t>(selectedIndex) < listItems .size()) { | 1507 if (selectedIndex >= 0 && static_cast<size_t>(selectedIndex) < listItems .size()) { |
1507 const AtomicString& overriddenDescription = listItems[selectedIndex] ->fastGetAttribute(aria_labelAttr); | 1508 const AtomicString& overriddenDescription = listItems[selectedIndex] ->fastGetAttribute(aria_labelAttr); |
1508 if (!overriddenDescription.isNull()) | 1509 if (!overriddenDescription.isNull()) |
1509 return overriddenDescription; | 1510 return overriddenDescription; |
1510 } | 1511 } |
1511 if (!selectElement.multiple()) | 1512 if (!selectElement.multiple()) |
1512 return selectElement.value(); | 1513 return selectElement.value(); |
1513 return String(); | 1514 return String(); |
1514 } | 1515 } |
1515 | 1516 |
1516 if (isNativeTextControl()) | 1517 if (isNativeTextControl()) |
1517 return text(); | 1518 return text(); |
1518 | 1519 |
1520 // Handle other HTML input elements that aren't text controls, like date and time | |
1521 // controls, by returning the string value, with the exception of checkboxes | |
1522 // and radio buttons (which would return "on"). | |
1523 if (isHTMLInputElement(node)) { | |
1524 HTMLInputElement* input = toHTMLInputElement(node); | |
1525 if (input->type() != InputTypeNames::checkbox && input->type() != InputT ypeNames::radio) | |
1526 return input->value(); | |
1527 } | |
1528 | |
1519 // FIXME: We might need to implement a value here for more types | 1529 // FIXME: We might need to implement a value here for more types |
1520 // FIXME: It would be better not to advertise a value at all for the types f or which we don't implement one; | 1530 // FIXME: It would be better not to advertise a value at all for the types f or which we don't implement one; |
1521 // this would require subclassing or making accessibilityAttributeNames do s omething other than return a | 1531 // this would require subclassing or making accessibilityAttributeNames do s omething other than return a |
1522 // single static array. | 1532 // single static array. |
1523 return String(); | 1533 return String(); |
1524 } | 1534 } |
1525 | 1535 |
1526 String AXNodeObject::ariaDescribedByAttribute() const | 1536 String AXNodeObject::ariaDescribedByAttribute() const |
1527 { | 1537 { |
1528 WillBeHeapVector<RawPtrWillBeMember<Element>> elements; | 1538 WillBeHeapVector<RawPtrWillBeMember<Element>> elements; |
(...skipping 389 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1918 } | 1928 } |
1919 | 1929 |
1920 // Step 2D from: http://www.w3.org/TR/accname-aam-1.1 | 1930 // Step 2D from: http://www.w3.org/TR/accname-aam-1.1 |
1921 textAlternative = nativeTextAlternative(visited, nameFrom, relatedObjects, n ameSources, &foundTextAlternative); | 1931 textAlternative = nativeTextAlternative(visited, nameFrom, relatedObjects, n ameSources, &foundTextAlternative); |
1922 if (!textAlternative.isNull() && !nameSources) | 1932 if (!textAlternative.isNull() && !nameSources) |
1923 return textAlternative; | 1933 return textAlternative; |
1924 | 1934 |
1925 // Step 2E from: http://www.w3.org/TR/accname-aam-1.1 | 1935 // Step 2E from: http://www.w3.org/TR/accname-aam-1.1 |
1926 if (recursive && !inAriaLabelledByTraversal && isControl()) { | 1936 if (recursive && !inAriaLabelledByTraversal && isControl()) { |
1927 // No need to set any name source info in a recursive call. | 1937 // No need to set any name source info in a recursive call. |
1928 if (roleValue() == TextFieldRole || roleValue() == ComboBoxRole) | |
1929 return text(); | |
1930 if (isRange()) { | 1938 if (isRange()) { |
1931 const AtomicString& ariaValuetext = getAttribute(aria_valuetextAttr) ; | 1939 const AtomicString& ariaValuetext = getAttribute(aria_valuetextAttr) ; |
1932 if (!ariaValuetext.isNull()) | 1940 if (!ariaValuetext.isNull()) |
1933 return ariaValuetext.string(); | 1941 return ariaValuetext.string(); |
1934 return String::number(valueForRange()); | 1942 return String::number(valueForRange()); |
1935 } | 1943 } |
1944 | |
1945 return stringValue(); | |
aboxhall
2015/11/05 17:49:21
I'm not sure how I feel about this. It's calling i
dmazzoni
2015/11/06 00:21:17
The current code only handles the string value of
aboxhall
2015/11/06 01:11:23
Could we possibly pull out the control-related log
dmazzoni
2015/11/06 04:54:06
Good idea!
Done.
| |
1936 } | 1946 } |
1937 | 1947 |
1938 // Step 2F / 2G from: http://www.w3.org/TR/accname-aam-1.1 | 1948 // Step 2F / 2G from: http://www.w3.org/TR/accname-aam-1.1 |
1939 if (recursive || nameFromContents()) { | 1949 if (recursive || nameFromContents()) { |
1940 nameFrom = AXNameFromContents; | 1950 nameFrom = AXNameFromContents; |
1941 if (nameSources) { | 1951 if (nameSources) { |
1942 nameSources->append(NameSource(foundTextAlternative)); | 1952 nameSources->append(NameSource(foundTextAlternative)); |
1943 nameSources->last().type = nameFrom; | 1953 nameSources->last().type = nameFrom; |
1944 } | 1954 } |
1945 | 1955 |
1946 Node* node = this->node(); | 1956 Node* node = this->node(); |
1947 if (node && node->isTextNode()) | 1957 if (node && node->isTextNode()) |
1948 textAlternative = toText(node)->wholeText(); | 1958 textAlternative = toText(node)->wholeText(); |
1959 else if (isHTMLBRElement(node)) | |
1960 textAlternative = String("\n"); | |
1949 else | 1961 else |
1950 textAlternative = textFromDescendants(visited); | 1962 textAlternative = textFromDescendants(visited); |
1951 | 1963 |
1952 if (!textAlternative.isEmpty()) { | 1964 if (!textAlternative.isEmpty()) { |
1953 if (nameSources) { | 1965 if (nameSources) { |
1954 foundTextAlternative = true; | 1966 foundTextAlternative = true; |
1955 nameSources->last().text = textAlternative; | 1967 nameSources->last().text = textAlternative; |
1956 } else { | 1968 } else { |
1957 return textAlternative; | 1969 return textAlternative; |
1958 } | 1970 } |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1991 } | 2003 } |
1992 | 2004 |
1993 return String(); | 2005 return String(); |
1994 } | 2006 } |
1995 | 2007 |
1996 String AXNodeObject::textFromDescendants(AXObjectSet& visited) const | 2008 String AXNodeObject::textFromDescendants(AXObjectSet& visited) const |
1997 { | 2009 { |
1998 StringBuilder accumulatedText; | 2010 StringBuilder accumulatedText; |
1999 AXObject* previous = nullptr; | 2011 AXObject* previous = nullptr; |
2000 for (AXObject* child = firstChild(); child; child = child->nextSibling()) { | 2012 for (AXObject* child = firstChild(); child; child = child->nextSibling()) { |
2013 // Skip hidden children | |
2014 if (child->isInertOrAriaHidden()) | |
2015 continue; | |
2016 | |
2001 // If we're going between two layoutObjects that are in separate LayoutB oxes, add | 2017 // If we're going between two layoutObjects that are in separate LayoutB oxes, add |
2002 // whitespace if it wasn't there already. Intuitively if you have | 2018 // whitespace if it wasn't there already. Intuitively if you have |
2003 // <span>Hello</span><span>World</span>, those are part of the same Layo utBox | 2019 // <span>Hello</span><span>World</span>, those are part of the same Layo utBox |
2004 // so we should return "HelloWorld", but given <div>Hello</div><div>Worl d</div> the | 2020 // so we should return "HelloWorld", but given <div>Hello</div><div>Worl d</div> the |
2005 // strings are in separate boxes so we should return "Hello World". | 2021 // strings are in separate boxes so we should return "Hello World". |
2006 if (previous && accumulatedText.length() && !isHTMLSpace(accumulatedText [accumulatedText.length() - 1])) { | 2022 if (previous && accumulatedText.length() && !isHTMLSpace(accumulatedText [accumulatedText.length() - 1])) { |
2007 if (!isInSameNonInlineBlockFlow(child->layoutObject(), previous->lay outObject())) | 2023 if (!isInSameNonInlineBlockFlow(child->layoutObject(), previous->lay outObject())) |
2008 accumulatedText.append(' '); | 2024 accumulatedText.append(' '); |
2009 } | 2025 } |
2010 | 2026 |
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2218 } | 2234 } |
2219 | 2235 |
2220 bool AXNodeObject::canHaveChildren() const | 2236 bool AXNodeObject::canHaveChildren() const |
2221 { | 2237 { |
2222 // If this is an AXLayoutObject, then it's okay if this object | 2238 // If this is an AXLayoutObject, then it's okay if this object |
2223 // doesn't have a node - there are some layoutObjects that don't have associ ated | 2239 // doesn't have a node - there are some layoutObjects that don't have associ ated |
2224 // nodes, like scroll areas and css-generated text. | 2240 // nodes, like scroll areas and css-generated text. |
2225 if (!node() && !isAXLayoutObject()) | 2241 if (!node() && !isAXLayoutObject()) |
2226 return false; | 2242 return false; |
2227 | 2243 |
2244 if (node() && isHTMLMapElement(node())) | |
2245 return false; | |
2246 | |
2228 // Elements that should not have children | 2247 // Elements that should not have children |
2229 switch (roleValue()) { | 2248 switch (roleValue()) { |
2230 case ImageRole: | 2249 case ImageRole: |
2231 case ButtonRole: | 2250 case ButtonRole: |
2232 case PopUpButtonRole: | 2251 case PopUpButtonRole: |
2233 case CheckBoxRole: | 2252 case CheckBoxRole: |
2234 case RadioButtonRole: | 2253 case RadioButtonRole: |
2235 case SwitchRole: | 2254 case SwitchRole: |
2236 case TabRole: | 2255 case TabRole: |
2237 case ToggleButtonRole: | 2256 case ToggleButtonRole: |
(...skipping 517 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2755 source.text = textAlternative; | 2774 source.text = textAlternative; |
2756 *foundTextAlternative = true; | 2775 *foundTextAlternative = true; |
2757 } else { | 2776 } else { |
2758 return textAlternative; | 2777 return textAlternative; |
2759 } | 2778 } |
2760 } | 2779 } |
2761 } | 2780 } |
2762 return textAlternative; | 2781 return textAlternative; |
2763 } | 2782 } |
2764 | 2783 |
2765 // 5.8 img Element | 2784 // 5.8 img or area Element |
2766 if (isHTMLImageElement(node())) { | 2785 if (isHTMLImageElement(node()) || isHTMLAreaElement(node()) || (layoutObject () && layoutObject()->isSVGImage())) { |
2767 // alt | 2786 // alt |
2768 nameFrom = AXNameFromAttribute; | 2787 nameFrom = AXNameFromAttribute; |
2769 if (nameSources) { | 2788 if (nameSources) { |
2770 nameSources->append(NameSource(*foundTextAlternative, altAttr)); | 2789 nameSources->append(NameSource(*foundTextAlternative, altAttr)); |
2771 nameSources->last().type = nameFrom; | 2790 nameSources->last().type = nameFrom; |
2772 } | 2791 } |
2773 const AtomicString& alt = getAttribute(altAttr); | 2792 const AtomicString& alt = getAttribute(altAttr); |
2774 if (!alt.isNull()) { | 2793 if (!alt.isNull()) { |
2775 textAlternative = alt; | 2794 textAlternative = alt; |
2776 if (nameSources) { | 2795 if (nameSources) { |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2833 source.text = textAlternative; | 2852 source.text = textAlternative; |
2834 *foundTextAlternative = true; | 2853 *foundTextAlternative = true; |
2835 } else { | 2854 } else { |
2836 return textAlternative; | 2855 return textAlternative; |
2837 } | 2856 } |
2838 } | 2857 } |
2839 | 2858 |
2840 return textAlternative; | 2859 return textAlternative; |
2841 } | 2860 } |
2842 | 2861 |
2862 // Fieldset / legend. | |
aboxhall
2015/11/05 17:49:21
Wohoo! You can add http://crbug.com/550663 to the
dmazzoni
2015/11/06 00:21:17
Done.
| |
2863 if (isHTMLFieldSetElement(node())) { | |
2864 nameFrom = AXNameFromRelatedElement; | |
2865 if (nameSources) { | |
2866 nameSources->append(NameSource(*foundTextAlternative)); | |
2867 nameSources->last().type = nameFrom; | |
2868 nameSources->last().nativeSource = AXTextFromNativeHTMLLegend; | |
2869 } | |
2870 HTMLElement* legend = toHTMLFieldSetElement(node())->legend(); | |
2871 if (legend) { | |
2872 AXObject* legendAXObject = axObjectCache().getOrCreate(legend); | |
2873 // Avoid an infinite loop | |
2874 if (legendAXObject && !visited.contains(legendAXObject)) { | |
2875 textAlternative = recursiveTextAlternative(*legendAXObject, fals e, visited); | |
2876 | |
2877 if (relatedObjects) { | |
2878 localRelatedObjects.append(new NameSourceRelatedObject(legen dAXObject, textAlternative)); | |
2879 *relatedObjects = localRelatedObjects; | |
2880 localRelatedObjects.clear(); | |
2881 } | |
2882 | |
2883 if (nameSources) { | |
2884 NameSource& source = nameSources->last(); | |
2885 source.relatedObjects = *relatedObjects; | |
2886 source.text = textAlternative; | |
2887 source.nativeSource = AXTextFromNativeHTMLLegend; | |
aboxhall
2015/11/05 17:49:21
No need to re-set this.
dmazzoni
2015/11/06 00:21:17
Done.
| |
2888 *foundTextAlternative = true; | |
2889 } else { | |
2890 return textAlternative; | |
2891 } | |
2892 } | |
2893 } | |
2894 } | |
2895 | |
2843 return textAlternative; | 2896 return textAlternative; |
2844 } | 2897 } |
2845 | 2898 |
2846 String AXNodeObject::description(AXNameFrom nameFrom, AXDescriptionFrom& descrip tionFrom, AXObjectVector* descriptionObjects) const | 2899 String AXNodeObject::description(AXNameFrom nameFrom, AXDescriptionFrom& descrip tionFrom, AXObjectVector* descriptionObjects) const |
2847 { | 2900 { |
2848 AXRelatedObjectVector relatedObjects; | 2901 AXRelatedObjectVector relatedObjects; |
2849 String result = description(nameFrom, descriptionFrom, nullptr, &relatedObje cts); | 2902 String result = description(nameFrom, descriptionFrom, nullptr, &relatedObje cts); |
2850 if (descriptionObjects) { | 2903 if (descriptionObjects) { |
2851 descriptionObjects->clear(); | 2904 descriptionObjects->clear(); |
2852 for (size_t i = 0; i < relatedObjects.size(); i++) | 2905 for (size_t i = 0; i < relatedObjects.size(); i++) |
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3012 description = title; | 3065 description = title; |
3013 if (descriptionSources) { | 3066 if (descriptionSources) { |
3014 foundDescription = true; | 3067 foundDescription = true; |
3015 descriptionSources->last().text = description; | 3068 descriptionSources->last().text = description; |
3016 } else { | 3069 } else { |
3017 return description; | 3070 return description; |
3018 } | 3071 } |
3019 } | 3072 } |
3020 } | 3073 } |
3021 | 3074 |
3075 // aria-help. | |
3076 // FIXME: this is not part of the official standard, but it's needed because the built-in date/time controls use it. | |
3077 descriptionFrom = AXDescriptionFromAttribute; | |
3078 if (descriptionSources) { | |
3079 descriptionSources->append(DescriptionSource(foundDescription, aria_help Attr)); | |
3080 descriptionSources->last().type = descriptionFrom; | |
3081 } | |
3082 const AtomicString& help = getAttribute(aria_helpAttr); | |
3083 if (!help.isEmpty()) { | |
3084 description = help; | |
3085 if (descriptionSources) { | |
3086 foundDescription = true; | |
3087 descriptionSources->last().text = description; | |
3088 } else { | |
3089 return description; | |
3090 } | |
3091 } | |
3092 | |
3022 descriptionFrom = AXDescriptionFromUninitialized; | 3093 descriptionFrom = AXDescriptionFromUninitialized; |
3023 | 3094 |
3024 if (foundDescription) { | 3095 if (foundDescription) { |
3025 for (size_t i = 0; i < descriptionSources->size(); ++i) { | 3096 for (size_t i = 0; i < descriptionSources->size(); ++i) { |
3026 if (!(*descriptionSources)[i].text.isNull() && !(*descriptionSources )[i].superseded) { | 3097 if (!(*descriptionSources)[i].text.isNull() && !(*descriptionSources )[i].superseded) { |
3027 DescriptionSource& descriptionSource = (*descriptionSources)[i]; | 3098 DescriptionSource& descriptionSource = (*descriptionSources)[i]; |
3028 descriptionFrom = descriptionSource.type; | 3099 descriptionFrom = descriptionSource.type; |
3029 if (!descriptionSource.relatedObjects.isEmpty()) | 3100 if (!descriptionSource.relatedObjects.isEmpty()) |
3030 *relatedObjects = descriptionSource.relatedObjects; | 3101 *relatedObjects = descriptionSource.relatedObjects; |
3031 return descriptionSource.text; | 3102 return descriptionSource.text; |
3032 } | 3103 } |
3033 } | 3104 } |
3034 } | 3105 } |
3035 | 3106 |
3036 return String(); | 3107 return String(); |
3037 } | 3108 } |
3038 | 3109 |
3039 DEFINE_TRACE(AXNodeObject) | 3110 DEFINE_TRACE(AXNodeObject) |
3040 { | 3111 { |
3041 visitor->trace(m_node); | 3112 visitor->trace(m_node); |
3042 AXObject::trace(visitor); | 3113 AXObject::trace(visitor); |
3043 } | 3114 } |
3044 | 3115 |
3045 } // namespace blink | 3116 } // namespace blin |
OLD | NEW |