AX: Implement CSS -webkit-alt property (text alternative for generated content pseudo...
authorcfleizach@apple.com <cfleizach@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 21 Nov 2013 00:28:14 +0000 (00:28 +0000)
committercfleizach@apple.com <cfleizach@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 21 Nov 2013 00:28:14 +0000 (00:28 +0000)
https://bugs.webkit.org/show_bug.cgi?id=120188

Reviewed by Dean Jackson.

Source/WebCore:

Add a -webkit-alt CSS property that can be used to label Image content or Text content for accessibility clients.

To accomplish this, it sets the string in the RenderStyle. Then when the ContentData creates an anonymous renderer,
it sets that string on the TextFragment or RenderImage, which can be queried by accessibility code.

Test: platform/mac/accessibility/webkit-alt-for-css-content.html

* accessibility/AccessibilityNodeObject.cpp:
(WebCore::AccessibilityNodeObject::alternativeText):
* accessibility/AccessibilityRenderObject.cpp:
(WebCore::AccessibilityRenderObject::textUnderElement):
(WebCore::objectInclusionFromAltText):
(WebCore::AccessibilityRenderObject::computeAccessibilityIsIgnored):
* css/CSSComputedStyleDeclaration.cpp:
(WebCore::altTextToCSSValue):
(WebCore::ComputedStyleExtractor::propertyValue):
* css/CSSParser.cpp:
(WebCore::CSSParser::parseValue):
(WebCore::CSSParser::parseAlt):
* css/CSSParser.h:
* css/CSSPropertyNames.in:
* css/StyleResolver.cpp:
(WebCore::StyleResolver::applyProperty):
* rendering/RenderImage.h:
(WebCore::RenderImage::altText):
(WebCore::RenderImage::setAltText):
* rendering/RenderTextFragment.h:
* rendering/style/ContentData.cpp:
(WebCore::ImageContentData::createRenderer):
(WebCore::TextContentData::createRenderer):
* rendering/style/ContentData.h:
(WebCore::ContentData::setAltText):
(WebCore::ContentData::altText):
* rendering/style/RenderStyle.cpp:
(WebCore::RenderStyle::setContent):
(WebCore::RenderStyle::setContentAltText):
(WebCore::RenderStyle::contentAltText):
* rendering/style/RenderStyle.h:
* rendering/style/StyleRareNonInheritedData.h:

LayoutTests:

* platform/mac/accessibility/webkit-alt-for-css-content-expected.txt: Added.
* platform/mac/accessibility/webkit-alt-for-css-content.html: Added.

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

18 files changed:
LayoutTests/ChangeLog
LayoutTests/platform/mac/accessibility/webkit-alt-for-css-content-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac/accessibility/webkit-alt-for-css-content.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/accessibility/AccessibilityNodeObject.cpp
Source/WebCore/accessibility/AccessibilityRenderObject.cpp
Source/WebCore/css/CSSComputedStyleDeclaration.cpp
Source/WebCore/css/CSSParser.cpp
Source/WebCore/css/CSSParser.h
Source/WebCore/css/CSSPropertyNames.in
Source/WebCore/css/StyleResolver.cpp
Source/WebCore/rendering/RenderImage.h
Source/WebCore/rendering/RenderTextFragment.h
Source/WebCore/rendering/style/ContentData.cpp
Source/WebCore/rendering/style/ContentData.h
Source/WebCore/rendering/style/RenderStyle.cpp
Source/WebCore/rendering/style/RenderStyle.h
Source/WebCore/rendering/style/StyleRareNonInheritedData.h

index ed453e1..54bdfb8 100644 (file)
@@ -1,3 +1,13 @@
+2013-11-20  Chris Fleizach  <cfleizach@apple.com>
+
+        AX: Implement CSS -webkit-alt property (text alternative for generated content pseudo-elements ::before and ::after)
+        https://bugs.webkit.org/show_bug.cgi?id=120188
+
+        Reviewed by Dean Jackson.
+
+        * platform/mac/accessibility/webkit-alt-for-css-content-expected.txt: Added.
+        * platform/mac/accessibility/webkit-alt-for-css-content.html: Added.
+
 2013-11-20  Dean Jackson  <dino@apple.com>
 
         Unreviewed. Updated expected result following
diff --git a/LayoutTests/platform/mac/accessibility/webkit-alt-for-css-content-expected.txt b/LayoutTests/platform/mac/accessibility/webkit-alt-for-css-content-expected.txt
new file mode 100644 (file)
index 0000000..0eca0e8
--- /dev/null
@@ -0,0 +1,52 @@
+This tests that -webkit-alt applies to text and image content in CSS styles, and that it makes it to accessibility.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Test1 - webkit-alt on image content with attr() function
+AXRole: AXImage
+AXDescription: ALTERNATIVE CONTENT TEST1
+AXTitle: 
+AXValue: 
+
+Test2 - webkit-alt on image content that is a string
+AXRole: AXImage
+AXDescription: ALTERNATIVE CONTENT TEST2
+AXTitle: 
+AXValue: 
+
+Test3 - webkit-alt on image content that is an empty string - representing that it should be ignored.
+AXRole: 
+AXDescription: 
+AXTitle: 
+AXValue: 
+
+Test4 - webkit-alt on text content
+AXRole: AXStaticText
+AXDescription: 
+AXTitle: 
+AXValue: ALTERNATIVE CONTENT TEST4
+
+Test5 - webkit-alt on text content that is an empty string - representing that is should be ignored.
+AXRole: AXStaticText
+AXDescription: 
+AXTitle: 
+AXValue: test5
+
+Test6 - webkit-alt on text content that uses the attr() function.
+AXRole: AXStaticText
+AXDescription: 
+AXTitle: 
+AXValue: ALTERNATIVE CONTENT TEST6
+
+WebKitAlt accessed through Javascript: 'ALTERNATIVE CONTENT TEST2'
+Test7 - webkit-alt does not apply to DOM nodes.
+AXRole: AXImage
+AXDescription: This is the right text
+AXTitle: 
+AXValue: 
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/platform/mac/accessibility/webkit-alt-for-css-content.html b/LayoutTests/platform/mac/accessibility/webkit-alt-for-css-content.html
new file mode 100644 (file)
index 0000000..6b8f2c7
--- /dev/null
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<html>
+<title id="title">TITLE</title>
+<head>
+<script src="../../../resources/js-test-pre.js"></script>
+</head>
+<body>
+
+<style>
+
+/*  webkit-alt on image content with attr() function. */
+[test1]::after {
+    content: url(resources/cake.png);
+    -webkit-alt: attr(test1);
+}
+
+/* webkit-alt on image content that is a string. */
+.test2::after {
+    content: url(resources/cake.png);
+    -webkit-alt: "ALTERNATIVE CONTENT TEST2";
+}
+
+/* webkit-alt on image content that is an empty string - representing that it should be ignored. */
+.test3::after {
+    content: url(resources/cake.png);
+    -webkit-alt: "";
+}
+
+/* webkit-alt on text content. */
+[aria-expanded="test4"]::before {
+    content: "\25BB";
+    -webkit-alt: "ALTERNATIVE CONTENT TEST4";
+}
+
+/* webkit-alt on text content that is an empty string - representing that is should be ignored. */
+[aria-expanded="test5"]::before {
+    content: "\25BC";
+    -webkit-alt: "";
+}
+
+/* webkit-alt on text content that uses the attr() function. */
+[aria-expanded="test6"]::before {
+    content: "\25BC";
+    -webkit-alt: attr(test6);
+}
+</style>
+
+<div id="content">
+<div id="test1" test1="ALTERNATIVE CONTENT TEST1">test1</div>
+<div id="test2" class="test2">test2</div>
+<div id="test3" class="test3">test3</div>
+<div id="test4" aria-expanded="test4">test4</div>
+<div id="test5" test5="ALTERNATIVE CONTENT TEST5" aria-expanded="test5">test5</div>
+<div id="test6" test6="ALTERNATIVE CONTENT TEST6" aria-expanded="test6">test6</div>
+
+<img id="image1" src="#" alt="This is the right text" style="width:100px; height:100px; -webkit-alt: 'this is the wrong text';">
+</div>
+
+<p id="description"></p>
+<div id="console"></div>
+
+<script>
+    description("This tests that -webkit-alt applies to text and image content in CSS styles, and that it makes it to accessibility.");
+    
+    function outputElement(element) {
+        var role = "AXRole: ";
+        var description = "AXDescription: ";
+        var title = "AXTitle: ";
+        var value = "AXValue: ";
+        if (element) {
+            role = element.role;
+            description = element.description;
+            title = element.title;
+            value = element.stringValue;
+        }
+        debug(role);
+        debug(description);
+        debug(title);
+        debug(value + "\n");
+    }
+
+    if (window.accessibilityController) {
+        debug("Test1 - webkit-alt on image content with attr() function");
+        outputElement(accessibilityController.accessibleElementById("test1").childAtIndex(1));
+
+        debug("Test2 - webkit-alt on image content that is a string");
+        outputElement(accessibilityController.accessibleElementById("test2").childAtIndex(1));
+
+        debug("Test3 - webkit-alt on image content that is an empty string - representing that it should be ignored.");
+        outputElement(accessibilityController.accessibleElementById("test3").childAtIndex(1));
+
+        debug("Test4 - webkit-alt on text content");
+        outputElement(accessibilityController.accessibleElementById("test4").childAtIndex(0));
+
+        debug("Test5 - webkit-alt on text content that is an empty string - representing that is should be ignored.");
+        outputElement(accessibilityController.accessibleElementById("test5").childAtIndex(0));
+
+        debug("Test6 - webkit-alt on text content that uses the attr() function.");
+        outputElement(accessibilityController.accessibleElementById("test6").childAtIndex(0));
+
+        debug("WebKitAlt accessed through Javascript: " + getComputedStyle(document.getElementById("test2"), ':after').webkitAlt);
+
+        debug("Test7 - webkit-alt does not apply to DOM nodes.");
+        outputElement(accessibilityController.accessibleElementById("image1"));
+
+        document.getElementById("content").style.visibility = "hidden";
+    }
+
+</script>
+
+<script src="../../../resources/js-test-post.js"></script>
+</body>
+</html>
index 77a9358..4785410 100644 (file)
@@ -1,3 +1,50 @@
+2013-11-20  Chris Fleizach  <cfleizach@apple.com>
+
+        AX: Implement CSS -webkit-alt property (text alternative for generated content pseudo-elements ::before and ::after)
+        https://bugs.webkit.org/show_bug.cgi?id=120188
+
+        Reviewed by Dean Jackson.
+
+        Add a -webkit-alt CSS property that can be used to label Image content or Text content for accessibility clients.
+
+        To accomplish this, it sets the string in the RenderStyle. Then when the ContentData creates an anonymous renderer,
+        it sets that string on the TextFragment or RenderImage, which can be queried by accessibility code.
+
+        Test: platform/mac/accessibility/webkit-alt-for-css-content.html
+
+        * accessibility/AccessibilityNodeObject.cpp:
+        (WebCore::AccessibilityNodeObject::alternativeText):
+        * accessibility/AccessibilityRenderObject.cpp:
+        (WebCore::AccessibilityRenderObject::textUnderElement):
+        (WebCore::objectInclusionFromAltText):
+        (WebCore::AccessibilityRenderObject::computeAccessibilityIsIgnored):
+        * css/CSSComputedStyleDeclaration.cpp:
+        (WebCore::altTextToCSSValue):
+        (WebCore::ComputedStyleExtractor::propertyValue):
+        * css/CSSParser.cpp:
+        (WebCore::CSSParser::parseValue):
+        (WebCore::CSSParser::parseAlt):
+        * css/CSSParser.h:
+        * css/CSSPropertyNames.in:
+        * css/StyleResolver.cpp:
+        (WebCore::StyleResolver::applyProperty):
+        * rendering/RenderImage.h:
+        (WebCore::RenderImage::altText):
+        (WebCore::RenderImage::setAltText):
+        * rendering/RenderTextFragment.h:
+        * rendering/style/ContentData.cpp:
+        (WebCore::ImageContentData::createRenderer):
+        (WebCore::TextContentData::createRenderer):
+        * rendering/style/ContentData.h:
+        (WebCore::ContentData::setAltText):
+        (WebCore::ContentData::altText):
+        * rendering/style/RenderStyle.cpp:
+        (WebCore::RenderStyle::setContent):
+        (WebCore::RenderStyle::setContentAltText):
+        (WebCore::RenderStyle::contentAltText):
+        * rendering/style/RenderStyle.h:
+        * rendering/style/StyleRareNonInheritedData.h:
+
 2013-11-20  Roger Fong  <roger_fong@apple.com>
 
         Use compile flag SH_UNFOLD_SHORT_CIRCUIT when compiling shaders.
index 507e2d2..788c83d 100644 (file)
@@ -68,6 +68,7 @@
 #include "NodeTraversal.h"
 #include "Page.h"
 #include "ProgressTracker.h"
+#include "RenderImage.h"
 #include "RenderView.h"
 #include "SVGElement.h"
 #include "SVGNames.h"
@@ -1250,10 +1251,19 @@ void AccessibilityNodeObject::alternativeText(Vector<AccessibilityText>& textOrd
         textOrder.append(AccessibilityText(ariaLabel, AlternativeText));
     
     if (isImage() || isInputImage() || isNativeImage() || isCanvas()) {
+        if (renderer() && renderer()->isRenderImage()) {
+            String renderAltText = toRenderImage(renderer())->altText();
+
+            // RenderImage will return title as a fallback from altText, but we don't want title here because we consider that in helpText.
+            if (!renderAltText.isEmpty() && renderAltText != getAttribute(titleAttr)) {
+                textOrder.append(AccessibilityText(renderAltText, AlternativeText));
+                return;
+            }
+        }
         // 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())
+        if (!alt.isEmpty())
             textOrder.append(AccessibilityText(alt, AlternativeText));
     }
     
index 68e1283..5022da7 100644 (file)
@@ -690,8 +690,14 @@ String AccessibilityRenderObject::textUnderElement(AccessibilityTextUnderElement
         // CSS content is used to insert text or when a RenderCounter is used.)
         if (m_renderer->isText()) {
             RenderText* renderTextObject = toRenderText(m_renderer);
-            if (renderTextObject->isTextFragment())
+            if (renderTextObject->isTextFragment()) {
+                
+                // The alt attribute may be set on a text fragment through CSS, which should be honored.
+                const String& altText = toRenderTextFragment(renderTextObject)->altText();
+                if (!altText.isEmpty())
+                    return altText;
                 return String(static_cast<RenderTextFragment*>(m_renderer)->contentString());
+            }
 
             return String(renderTextObject->text());
         }
@@ -1114,6 +1120,19 @@ bool AccessibilityRenderObject::isAllowedChildOfTree() const
     return true;
 }
     
+static AccessibilityObjectInclusion objectInclusionFromAltText(const String& altText)
+{
+    // Don't ignore an image that has an alt tag.
+    if (!altText.containsOnlyWhitespace())
+        return IncludeObject;
+    
+    // The informal standard is to ignore images with zero-length alt strings.
+    if (!altText.isNull())
+        return IgnoreObject;
+    
+    return DefaultBehavior;
+}
+
 AccessibilityObjectInclusion AccessibilityRenderObject::defaultObjectInclusion() const
 {
     // The following cases can apply to any element that's a subclass of AccessibilityRenderObject.
@@ -1192,6 +1211,15 @@ bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
             if (parent->roleValue() == TextFieldRole)
                 return true;
         }
+        
+        // The alt attribute may be set on a text fragment through CSS, which should be honored.
+        if (renderText.isTextFragment()) {
+            AccessibilityObjectInclusion altTextInclusion = objectInclusionFromAltText(toRenderTextFragment(&renderText)->altText());
+            if (altTextInclusion == IgnoreObject)
+                return true;
+            if (altTextInclusion == IncludeObject)
+                return false;
+        }
 
         // text elements that are just empty whitespace should not be returned
         return renderText.text()->containsOnlyWhitespace();
@@ -1253,21 +1281,25 @@ bool AccessibilityRenderObject::computeAccessibilityIsIgnored() const
         if (canSetFocusAttribute())
             return false;
         
-        if (node && node->isElementNode()) {
-            Element* elt = toElement(node);
-            const AtomicString& alt = elt->getAttribute(altAttr);
-            // don't ignore an image that has an alt tag
-            if (!alt.string().containsOnlyWhitespace())
-                return false;
-            // informal standard is to ignore images with zero-length alt strings
-            if (!alt.isNull())
-                return true;
-            // If an image has a title attribute on it, accessibility should be lenient and allow it to appear in the hierarchy (according to WAI-ARIA).
-            if (!getAttribute(titleAttr).isEmpty())
-                return false;
-        }
+        // First check the RenderImage's altText (which can be set through a style sheet, or come from the Element).
+        // However, if this is not a native image, fallback to the attribute on the Element.
+        AccessibilityObjectInclusion altTextInclusion = DefaultBehavior;
+        bool isRenderImage = m_renderer && m_renderer->isRenderImage();
+        if (isRenderImage)
+            altTextInclusion = objectInclusionFromAltText(toRenderImage(m_renderer)->altText());
+        else
+            altTextInclusion = objectInclusionFromAltText(getAttribute(altAttr).string());
+
+        if (altTextInclusion == IgnoreObject)
+            return true;
+        if (altTextInclusion == IncludeObject)
+            return false;
         
-        if (isNativeImage()) {
+        // If an image has a title attribute on it, accessibility should be lenient and allow it to appear in the hierarchy (according to WAI-ARIA).
+        if (!getAttribute(titleAttr).isEmpty())
+            return false;
+    
+        if (isRenderImage) {
             // check for one-dimensional image
             RenderImage* image = toRenderImage(m_renderer);
             if (image->height() <= 1 || image->width() <= 1)
index 24766c0..012dafc 100644 (file)
@@ -222,6 +222,7 @@ static const CSSPropertyID computedProperties[] = {
     CSSPropertyZIndex,
     CSSPropertyZoom,
 
+    CSSPropertyWebkitAlt,
     CSSPropertyWebkitAnimationDelay,
     CSSPropertyWebkitAnimationDirection,
     CSSPropertyWebkitAnimationDuration,
@@ -1507,6 +1508,11 @@ static PassRefPtr<CSSValue> fillSizeToCSSValue(const FillSize& fillSize, const R
     return list.release();
 }
 
+static PassRefPtr<CSSValue> altTextToCSSValue(const RenderStyle* style)
+{
+    return cssValuePool().createValue(style->contentAltText(), CSSPrimitiveValue::CSS_STRING);
+}
+    
 static PassRefPtr<CSSValue> contentToCSSValue(const RenderStyle* style)
 {
     RefPtr<CSSValueList> list = CSSValueList::createSpaceSeparated();
@@ -2846,6 +2852,8 @@ PassRefPtr<CSSValue> ComputedStyleExtractor::propertyValue(CSSPropertyID propert
             return CSSPrimitiveValue::create(style->textOrientation());
         case CSSPropertyWebkitLineBoxContain:
             return createLineBoxContainValue(style->lineBoxContain());
+        case CSSPropertyWebkitAlt:
+            return altTextToCSSValue(style.get());
         case CSSPropertyContent:
             return contentToCSSValue(style.get());
         case CSSPropertyCounterIncrement:
index 3b79744..4ba86ee 100644 (file)
@@ -1951,6 +1951,9 @@ bool CSSParser::parseValue(CSSPropertyID propId, bool important)
         // close-quote | no-open-quote | no-close-quote ]+ | inherit
         return parseContent(propId, important);
 
+    case CSSPropertyWebkitAlt: // [ <string> | attr(X) ]
+        return parseAlt(propId, important);
+            
     case CSSPropertyClip:                 // <shape> | auto | inherit
         if (id == CSSValueAuto)
             validPrimitive = true;
@@ -3782,6 +3785,30 @@ bool CSSParser::parseQuotes(CSSPropertyID propId, bool important)
     return false;
 }
 
+bool CSSParser::parseAlt(CSSPropertyID propID, bool important)
+{
+    CSSParserValue* val = m_valueList->current();
+    RefPtr<CSSValue> parsedValue;
+
+    if (val->unit == CSSPrimitiveValue::CSS_STRING)
+        parsedValue = createPrimitiveStringValue(val);
+    else if (val->unit == CSSParserValue::Function) {
+        CSSParserValueList* args = val->function->args.get();
+        if (!args)
+            return false;
+        if (equalIgnoringCase(val->function->name, "attr("))
+            parsedValue = parseAttr(args);
+    }
+    
+    if (parsedValue) {
+        addProperty(propID, parsedValue.release(), important);
+        m_valueList->next();
+        return true;
+    }
+
+    return false;
+}
+    
 // [ <string> | <uri> | <counter> | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit
 // in CSS 2.1 this got somewhat reduced:
 // [ <string> | attr(X) | open-quote | close-quote | no-open-quote | no-close-quote ]+ | inherit
index 866bf74..bc2bf0f 100644 (file)
@@ -113,7 +113,8 @@ public:
     bool parse4Values(CSSPropertyID, const CSSPropertyID* properties, bool important);
     bool parseContent(CSSPropertyID, bool important);
     bool parseQuotes(CSSPropertyID, bool important);
-
+    bool parseAlt(CSSPropertyID, bool important);
+    
 #if ENABLE(CSS_VARIABLES)
     static bool parseValue(MutableStylePropertySet*, CSSPropertyID, const String&, bool important, Document&);
     bool cssVariablesEnabled() const;
index 940e922..71e0aff 100644 (file)
@@ -213,6 +213,7 @@ word-break [Inherited]
 word-spacing [Inherited]
 word-wrap [Inherited]
 z-index
+-webkit-alt
 -webkit-animation
 -webkit-animation-delay
 -webkit-animation-direction
index ba106ae..6ca4303 100644 (file)
@@ -2214,7 +2214,7 @@ void StyleResolver::applyProperty(CSSPropertyID id, CSSValue* value)
                     const AtomicString& value = state.element()->getAttribute(attr);
                     state.style()->setContent(value.isNull() ? emptyAtom : value.impl(), didSet);
                     didSet = true;
-                    // register the fact that the attribute value affects the style
+                    // Register the fact that the attribute value affects the style.
                     m_ruleSets.features().attrsInRules.add(attr.localName().impl());
                 } else if (contentValue->isCounter()) {
                     Counter* counterValue = contentValue->getCounterValue();
@@ -2253,6 +2253,28 @@ void StyleResolver::applyProperty(CSSPropertyID id, CSSValue* value)
                 state.style()->clearContent();
             return;
         }
+    case CSSPropertyWebkitAlt:
+        {
+            bool didSet = false;
+            if (primitiveValue->isString()) {
+                state.style()->setContentAltText(primitiveValue->getStringValue().impl());
+                didSet = true;
+            } else if (primitiveValue->isAttr()) {
+                // FIXME: Can a namespace be specified for an attr(foo)?
+                if (state.style()->styleType() == NOPSEUDO)
+                    state.style()->setUnique();
+                else
+                    state.parentStyle()->setUnique();
+                QualifiedName attr(nullAtom, primitiveValue->getStringValue().impl(), nullAtom);
+                const AtomicString& value = state.element()->getAttribute(attr);
+                state.style()->setContentAltText(value.isNull() ? emptyAtom : value.impl());
+                didSet = true;
+                // Register the fact that the attribute value affects the style.
+                m_ruleSets.features().attrsInRules.add(attr.localName().impl());
+            }
+            if (!didSet)
+                state.style()->setContentAltText(emptyAtom);
+        }
     case CSSPropertyQuotes:
         if (isInherit) {
             state.style()->setQuotes(state.parentStyle()->quotes());
index 781a088..2353678 100644 (file)
@@ -61,8 +61,9 @@ public:
 
     bool isGeneratedContent() const { return m_isGeneratedContent; }
 
-    String altText() const { return m_altText; }
-
+    const String& altText() const { return m_altText; }
+    void setAltText(const String& altText) { m_altText = altText; }
+    
 protected:
     virtual bool needsPreferredWidthsRecalculation() const OVERRIDE FINAL;
     virtual RenderBox* embeddedContentBox() const OVERRIDE FINAL;
index 941989b..abdd171 100644 (file)
@@ -56,6 +56,9 @@ public:
 
     virtual void transformText() OVERRIDE;
 
+    const String& altText() const { return m_altText; }
+    void setAltText(const String& altText) { m_altText = altText; }
+    
 private:
     virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle) OVERRIDE;
     virtual void willBeDestroyed() OVERRIDE;
@@ -65,6 +68,8 @@ private:
 
     unsigned m_start;
     unsigned m_end;
+    // Alternative description that can be used for accessibility instead of the native text.
+    String m_altText;
     String m_contentString;
     RenderBoxModelObject* m_firstLetter;
 };
index 8a6eb28..c39438a 100644 (file)
@@ -52,6 +52,7 @@ RenderObject* ImageContentData::createRenderer(Document& document, RenderStyle&
     // FIXME: We should find a way to avoid setting the style twice here.
     RenderImage* image = new RenderImage(document, pseudoStyle);
     image->setPseudoStyle(&pseudoStyle);
+    image->setAltText(altText());
     if (m_image)
         image->setImageResource(RenderImageResourceStyleImage::create(*m_image));
     else
@@ -61,7 +62,9 @@ RenderObject* ImageContentData::createRenderer(Document& document, RenderStyle&
 
 RenderObject* TextContentData::createRenderer(Document& document, RenderStyle&) const
 {
-    return new RenderTextFragment(document, m_text);
+    RenderTextFragment* fragment = new RenderTextFragment(document, m_text);
+    fragment->setAltText(altText());
+    return fragment;
 }
 
 RenderObject* CounterContentData::createRenderer(Document& document, RenderStyle&) const
index af28248..b0ff7d4 100644 (file)
@@ -52,12 +52,16 @@ public:
     ContentData* next() const { return m_next.get(); }
     void setNext(std::unique_ptr<ContentData> next) { m_next = std::move(next); }
 
+    void setAltText(const String& alt) { m_altText = alt; }
+    const String& altText() const { return m_altText; }
+    
     virtual bool equals(const ContentData&) const = 0;
 
 private:
     virtual std::unique_ptr<ContentData> cloneInternal() const = 0;
 
     std::unique_ptr<ContentData> m_next;
+    String m_altText;
 };
 
 class ImageContentData FINAL : public ContentData {
index faae15b..21382da 100644 (file)
@@ -916,6 +916,8 @@ void RenderStyle::setContent(PassRefPtr<StyleImage> image, bool add)
     }
 
     rareNonInheritedData.access()->m_content = std::make_unique<ImageContentData>(image);
+    if (!rareNonInheritedData.access()->m_altText.isNull())
+        rareNonInheritedData.access()->m_content->setAltText(rareNonInheritedData.access()->m_altText);
 }
 
 void RenderStyle::setContent(const String& string, bool add)
@@ -934,11 +936,15 @@ void RenderStyle::setContent(const String& string, bool add)
             } else
                 lastContent->setNext(std::make_unique<TextContentData>(string));
 
+            if (!rareNonInheritedData.access()->m_altText.isNull())
+                lastContent->setAltText(rareNonInheritedData.access()->m_altText);
             return;
         }
     }
 
     content = std::make_unique<TextContentData>(string);
+    if (!rareNonInheritedData.access()->m_altText.isNull())
+        content->setAltText(rareNonInheritedData.access()->m_altText);
 }
 
 void RenderStyle::setContent(std::unique_ptr<CounterContent> counter, bool add)
@@ -963,7 +969,20 @@ void RenderStyle::setContent(QuoteType quote, bool add)
 
     rareNonInheritedData.access()->m_content = std::make_unique<QuoteContentData>(quote);
 }
+
+void RenderStyle::setContentAltText(const String& string)
+{
+    rareNonInheritedData.access()->m_altText = string;
     
+    if (rareNonInheritedData.access()->m_content)
+        rareNonInheritedData.access()->m_content->setAltText(string);
+}
+
+const String& RenderStyle::contentAltText() const
+{
+    return rareNonInheritedData->m_altText;
+}
+
 inline bool requireTransformOrigin(const Vector<RefPtr<TransformOperation>>& transformOperations, RenderStyle::ApplyTransformOrigin applyOrigin)
 {
     // transform-origin brackets the transform with translate operations.
index d2f86f6..8f31554 100644 (file)
@@ -1569,6 +1569,8 @@ public:
     void setContent(PassRefPtr<StyleImage>, bool add = false);
     void setContent(std::unique_ptr<CounterContent>, bool add = false);
     void setContent(QuoteType, bool add = false);
+    void setContentAltText(const String&);
+    const String& contentAltText() const;
 
     const CounterDirectiveMap* counterDirectives() const;
     CounterDirectiveMap& accessCounterDirectives();
index 0585607..45a9665 100644 (file)
@@ -125,6 +125,7 @@ public:
 
     std::unique_ptr<ContentData> m_content;
     OwnPtr<CounterDirectiveMap> m_counterDirectives;
+    String m_altText;
 
     OwnPtr<ShadowData> m_boxShadow;  // For box-shadow decorations.