Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
AX: CSS speak-as, AXAccessKey, aria-owns, and URL AX APIs don't work …
…for display:contents elements

https://bugs.webkit.org/show_bug.cgi?id=255707
rdar://problem/108301432

Reviewed by Chris Fleizach and Andres Gonzalez.

Move implementations from AccessibilityRenderObject to the appropriate base class.

This patch also removes unused MSAA functions.

This patch also fixes a bug where AXPropertyName::AccessKey was not updated for isolated objects when the accesskey attribute changed.

* LayoutTests/accessibility/display-contents-aria-owns-expected.txt: Added.
* LayoutTests/accessibility/display-contents-aria-owns.html: Added.

* LayoutTests/accessibility/mac/accesskey-expected.txt:
* LayoutTests/accessibility/mac/accesskey.html:
Add display:contents testcase.
Add dynamic attribute change testcase.

* LayoutTests/accessibility/mac/css-speech-speak-expected.txt:
* LayoutTests/accessibility/mac/css-speech-speak.html:
* LayoutTests/accessibility/url-test-expected.txt:
* LayoutTests/accessibility/url-test.html:
Add display:contents testcase.

* Source/WebCore/accessibility/AXLogger.cpp:
(WebCore::operator<<):
* Source/WebCore/accessibility/AXObjectCache.cpp:
(WebCore::AXObjectCache::handleAttributeChange):
(WebCore::AXObjectCache::updateIsolatedTree):
* Source/WebCore/accessibility/AXObjectCache.h:
* Source/WebCore/accessibility/AccessibilityImageMapLink.cpp:
(WebCore::AccessibilityImageMapLink::stringValueForMSAA const): Deleted.
(WebCore::AccessibilityImageMapLink::nameForMSAA const): Deleted.
* Source/WebCore/accessibility/AccessibilityImageMapLink.h:
* Source/WebCore/accessibility/AccessibilityMenuListOption.cpp:
(WebCore::AccessibilityMenuListOption::nameForMSAA const): Deleted.
* Source/WebCore/accessibility/AccessibilityMenuListOption.h:
* Source/WebCore/accessibility/AccessibilityNodeObject.cpp:
(WebCore::AccessibilityNodeObject::url const):
* Source/WebCore/accessibility/AccessibilityNodeObject.h:
* Source/WebCore/accessibility/AccessibilityObject.cpp:
(WebCore::AccessibilityObject::accessKey const):
(WebCore::AccessibilityObject::speakAsProperty const):
(WebCore::AccessibilityObject::supportsARIAOwns const):
(WebCore::AccessibilityObject::loadingProgress const):
* Source/WebCore/accessibility/AccessibilityObject.h:
(WebCore::AccessibilityObject::stringValueForMSAA const): Deleted.
(WebCore::AccessibilityObject::stringRoleForMSAA const): Deleted.
(WebCore::AccessibilityObject::nameForMSAA const): Deleted.
(WebCore::AccessibilityObject::descriptionForMSAA const): Deleted.
(WebCore::AccessibilityObject::roleValueForMSAA const): Deleted.
(WebCore::AccessibilityObject::speakAsProperty const): Deleted.
* Source/WebCore/accessibility/AccessibilityRenderObject.cpp:
(WebCore::AccessibilityRenderObject::url const):
(WebCore::AccessibilityRenderObject::speakAsProperty const): Deleted.
(WebCore::AccessibilityRenderObject::loadingProgress const): Deleted.
(WebCore::AccessibilityRenderObject::accessKey const): Deleted.
(WebCore::AccessibilityRenderObject::supportsARIAOwns const): Deleted.
(WebCore::isLinkable): Deleted.
(WebCore::AccessibilityRenderObject::stringValueForMSAA const): Deleted.
(WebCore::AccessibilityRenderObject::nameForMSAA const): Deleted.
(WebCore::shouldReturnTagNameAsRoleForMSAA): Deleted.
(WebCore::AccessibilityRenderObject::stringRoleForMSAA const): Deleted.
(WebCore::AccessibilityRenderObject::positionalDescriptionForMSAA const): Deleted.
(WebCore::AccessibilityRenderObject::descriptionForMSAA const): Deleted.
(WebCore::msaaRoleForRenderer): Deleted.
(WebCore::AccessibilityRenderObject::roleValueForMSAA const): Deleted.
* Source/WebCore/accessibility/AccessibilityRenderObject.h:
* Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.cpp:
(WebCore::AXIsolatedTree::updateNodeProperties):

Canonical link: https://commits.webkit.org/263205@main
  • Loading branch information
twilco committed Apr 21, 2023
1 parent a0dcbd3 commit 204cc1e
Show file tree
Hide file tree
Showing 24 changed files with 179 additions and 238 deletions.
14 changes: 14 additions & 0 deletions LayoutTests/accessibility/display-contents-aria-owns-expected.txt
@@ -0,0 +1,14 @@
This test ensures aria-owns works for display:contents elements.

#list role: AXRole: AXList
#list supports AXOwns: true
Child 1 #item1 AXRole: AXGroup
Child 2 #item2 AXRole: AXGroup
Child 3 #item3 AXRole: AXGroup

PASS successfullyParsed is true

TEST COMPLETE
1
2
3
36 changes: 36 additions & 0 deletions LayoutTests/accessibility/display-contents-aria-owns.html
@@ -0,0 +1,36 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<script src="../resources/accessibility-helper.js"></script>
<script src="../resources/js-test.js"></script>
</head>
<body>

<div id="list" role="list" aria-owns="item3" style="display:contents">
<div id="item1" role="listitem">1</div>
<div id="item2" role="listitem">2</div>
</div>

<div id="item3" role="listitem">3</div>

<script>
var output = "This test ensures aria-owns works for display:contents elements.\n\n";

if (window.accessibilityController) {
const axList = accessibilityController.accessibleElementById('list');
output += `#list role: ${axList.role}\n`;
if (accessibilityController.platformName === "mac")
output += `#list supports AXOwns: ${axList.isAttributeSupported('AXOwns')}\n`;

const count = axList.childrenCount;
for (let i = 0; i < count; i++) {
const child = axList.childAtIndex(i);
output += `Child ${i + 1} #${child.domIdentifier} ${child.role}\n`;
}
debug(output);
}
</script>
</body>
</html>


12 changes: 7 additions & 5 deletions LayoutTests/accessibility/mac/accesskey-expected.txt
@@ -1,13 +1,15 @@
webkit

This test makes sure that elements return their accesskeys.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS: link.allAttributes().indexOf('AXAccessKey: w') !== -1 === true
PASS: linkDisplayContents.allAttributes().indexOf('AXAccessKey: z') !== -1 === true
PASS: input.allAttributes().indexOf('AXAccessKey: q') !== -1 === true

Updating #link accesskey to 'u'.
PASS: accesskey AX attribute was updated correctly.

PASS link.allAttributes().indexOf(linkAccessKey) != -1 is true
PASS text.allAttributes().indexOf(inputAccessKey) != -1 is true
PASS successfullyParsed is true

TEST COMPLETE
webkit
webkit

45 changes: 29 additions & 16 deletions LayoutTests/accessibility/mac/accesskey.html
@@ -1,28 +1,41 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<script src="../../resources/js-test-pre.js"></script>
<script src="../../resources/accessibility-helper.js"></script>
<script src="../../resources/js-test.js"></script>
</head>
<body>

<a href="http://www.webkit.org" id='link1' accesskey="w">webkit</a><br>
<input type='text' id='text1' accesskey='q'>

<p id="description"></p>
<div id="console"></div>
<a href="http://www.webkit.org" id="link" accesskey="w">webkit</a><br>
<a href="http://www.webkit.org" id="link-display-contents" accesskey="z" style="display:contents">webkit</a><br>
<input type="text" id="input" accesskey="q">

<script>
description("This test makes sure that elements return their accesskeys.");
var output = "This test makes sure that elements return their accesskeys.\n\n";

if (window.accessibilityController) {
window.jsTestIsAsync = true;

var link = accessibilityController.accessibleElementById("link");
output += expect("link.allAttributes().indexOf('AXAccessKey: w') !== -1", "true");

if (window.accessibilityController) {
var link = window.accessibilityController.accessibleElementById("link1");
var linkAccessKey = "AXAccessKey: w";
shouldBeTrue("link.allAttributes().indexOf(linkAccessKey) != -1")
var linkDisplayContents = accessibilityController.accessibleElementById("link-display-contents");
output += expect("linkDisplayContents.allAttributes().indexOf('AXAccessKey: z') !== -1", "true");

var text = window.accessibilityController.accessibleElementById("text1");
var inputAccessKey = "AXAccessKey: q";
shouldBeTrue("text.allAttributes().indexOf(inputAccessKey) != -1");
}
var input = accessibilityController.accessibleElementById("input");
output += expect("input.allAttributes().indexOf('AXAccessKey: q') !== -1", "true");

output += "\nUpdating #link accesskey to 'u'.\n";
document.getElementById("link").setAttribute("accesskey", "u");
setTimeout(async function() {
await waitFor(() => link.allAttributes().indexOf("AXAccessKey: u") !== -1);
output += "PASS: accesskey AX attribute was updated correctly.\n";

debug(output);
finishJSTest();
}, 0);
}
</script>
<script src="../../resources/js-test-post.js"></script>
</body>
</html>

3 changes: 3 additions & 0 deletions LayoutTests/accessibility/mac/css-speech-speak-expected.txt
@@ -1,6 +1,7 @@
Initial
Normal
Spellout
Spellout (display:contents)
Digits
Literal
No punctuation
Expand All @@ -18,6 +19,8 @@ Testing #speech-normal
PASS element.childAtIndex(0).speakAs is "normal"
Testing #speech-spellout
PASS element.childAtIndex(0).speakAs is "spell-out"
Testing #speech-spellout-display-contents
PASS element.childAtIndex(0).speakAs is "spell-out"
Testing #speech-digits
PASS element.childAtIndex(0).speakAs is "normal, digits"
Testing #speech-literalpunc
Expand Down
2 changes: 2 additions & 0 deletions LayoutTests/accessibility/mac/css-speech-speak.html
Expand Up @@ -18,6 +18,7 @@
<div tabindex="0" id="initial" >Initial</div>
<div tabindex="0" id="speech-normal" class="speech-normal">Normal</div>
<div tabindex="0" id="speech-spellout" class="speech-spellout">Spellout</div>
<div tabindex="0" id="speech-spellout-display-contents" class="speech-spellout" style="display:contents">Spellout (display:contents)</div>
<div tabindex="0" id="speech-digits" class="speech-digits">Digits</div>
<div tabindex="0" id="speech-literalpunc" class="speech-literalpunc">Literal</div>
<div tabindex="0" id="speech-nopunc" class="speech-nopunc">No punctuation</div>
Expand Down Expand Up @@ -50,6 +51,7 @@
expectValueForId("initial", "normal");
expectValueForId("speech-normal", "normal");
expectValueForId("speech-spellout", "spell-out");
expectValueForId("speech-spellout-display-contents", "spell-out");
expectValueForId("speech-digits", "normal, digits");
expectValueForId("speech-literalpunc", "normal, literal-punctuation");
expectValueForId("speech-nopunc", "normal, no-punctuation");
Expand Down
2 changes: 2 additions & 0 deletions LayoutTests/accessibility/url-test-expected.txt
Expand Up @@ -2,6 +2,7 @@ This test ensures that we can retrieve URLs for appropriate elements.

PASS: accessibilityController.accessibleElementById('input').url.indexOf('url-test1.html') !== -1 === true
PASS: accessibilityController.accessibleElementById('anchor').url.indexOf('url-test2.html') !== -1 === true
PASS: accessibilityController.accessibleElementById('anchor-display-contents').url.indexOf('url-test2-display-contents.html') !== -1 === true
PASS: accessibilityController.accessibleElementById('img').url.indexOf('url-test3.png') !== -1 === true

Changing href of #anchor to 'foobar.html'
Expand All @@ -12,5 +13,6 @@ PASS successfullyParsed is true
TEST COMPLETE

test
test


2 changes: 2 additions & 0 deletions LayoutTests/accessibility/url-test.html
Expand Up @@ -8,6 +8,7 @@

<input id="input" type="image" src="url-test1.html"><BR>
<a id="anchor" href="#url-test2.html">test</a><BR>
<a id="anchor-display-contents" href="#url-test2-display-contents.html" style="display:contents">test</a><BR>
<img id="img" src="url-test3.png" width=100 height=100 alt="test"><BR>

<script>
Expand All @@ -18,6 +19,7 @@

testOutput += expect("accessibilityController.accessibleElementById('input').url.indexOf('url-test1.html') !== -1", "true");
testOutput += expect("accessibilityController.accessibleElementById('anchor').url.indexOf('url-test2.html') !== -1", "true");
testOutput += expect("accessibilityController.accessibleElementById('anchor-display-contents').url.indexOf('url-test2-display-contents.html') !== -1", "true");
testOutput += expect("accessibilityController.accessibleElementById('img').url.indexOf('url-test3.png') !== -1", "true");

testOutput += "\nChanging href of #anchor to 'foobar.html'\n";
Expand Down
@@ -0,0 +1,13 @@
This test ensures aria-owns works for display:contents elements.

#list role: AXRole: AXList
Child 1 #item1 AXRole: AXListItem
Child 2 #item2 AXRole: AXListItem
Child 3 #item3 AXRole: AXListItem

PASS successfullyParsed is true

TEST COMPLETE
1
2
3
1 change: 1 addition & 0 deletions LayoutTests/platform/mac-wk1/TestExpectations
Expand Up @@ -812,6 +812,7 @@ accessibility/progress-indeterminate-value.html [ Skip ]
accessibility/mac/date-input-increment-decrement.html [ Skip ]

accessibility/display-contents-dynamically-added-children.html [ Skip ]
accessibility/display-contents-aria-owns.html [ Skip ]

# DOM paste access requests are not implemented in WebKit1.
editing/async-clipboard/clipboard-change-data-while-reading.html [ Skip ]
Expand Down
3 changes: 3 additions & 0 deletions Source/WebCore/accessibility/AXLogger.cpp
Expand Up @@ -412,6 +412,9 @@ TextStream& operator<<(TextStream& stream, AccessibilityObjectInclusion inclusio
TextStream& operator<<(TextStream& stream, AXObjectCache::AXNotification notification)
{
switch (notification) {
case AXObjectCache::AXNotification::AXAccessKeyChanged:
stream << "AXAccessKeyChanged";
break;
case AXObjectCache::AXNotification::AXActiveDescendantChanged:
stream << "AXActiveDescendantChanged";
break;
Expand Down
8 changes: 6 additions & 2 deletions Source/WebCore/accessibility/AXObjectCache.cpp
Expand Up @@ -2110,8 +2110,9 @@ void AXObjectCache::handleAttributeChange(Element* element, const QualifiedName&
#if !LOG_DISABLED
updateIsolatedTree(get(element), AXIdAttributeChanged);
#endif
}
#endif
} else if (attrName == accesskeyAttr)
updateIsolatedTree(get(element), AXAccessKeyChanged);
#endif // ENABLE(ACCESSIBILITY_ISOLATED_TREE)
else if (attrName == openAttr && is<HTMLDialogElement>(*element)) {
deferModalChange(element);
recomputeIsIgnored(element->parentNode());
Expand Down Expand Up @@ -3705,6 +3706,9 @@ void AXObjectCache::updateIsolatedTree(const Vector<std::pair<RefPtr<Accessibili
continue;

switch (notification.second) {
case AXAccessKeyChanged:
tree->updateNodeProperty(*notification.first, AXPropertyName::AccessKey);
break;
case AXAutofillTypeChanged:
tree->updateNodeProperty(*notification.first, AXPropertyName::ValueAutofillButtonType);
break;
Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/accessibility/AXObjectCache.h
Expand Up @@ -291,6 +291,7 @@ class AXObjectCache : public CanMakeWeakPtr<AXObjectCache>
CharacterOffset characterOffsetForIndex(int, const AXCoreObject*);

enum AXNotification {
AXAccessKeyChanged,
AXActiveDescendantChanged,
AXAutocorrectionOccured,
AXAutofillTypeChanged,
Expand Down
10 changes: 0 additions & 10 deletions Source/WebCore/accessibility/AccessibilityImageMapLink.cpp
Expand Up @@ -172,14 +172,4 @@ LayoutRect AccessibilityImageMapLink::elementRect() const
return m_areaElement->computeRect(renderer);
}

String AccessibilityImageMapLink::stringValueForMSAA() const
{
return url().string();
}

String AccessibilityImageMapLink::nameForMSAA() const
{
return description();
}

} // namespace WebCore
3 changes: 0 additions & 3 deletions Source/WebCore/accessibility/AccessibilityImageMapLink.h
Expand Up @@ -58,9 +58,6 @@ class AccessibilityImageMapLink final : public AccessibilityMockObject {
String description() const override;
AccessibilityObject* parentObject() const override;

String stringValueForMSAA() const override;
String nameForMSAA() const override;

LayoutRect elementRect() const override;

private:
Expand Down
5 changes: 0 additions & 5 deletions Source/WebCore/accessibility/AccessibilityMenuListOption.cpp
Expand Up @@ -92,11 +92,6 @@ void AccessibilityMenuListOption::setSelected(bool selected)
m_element->setSelected(selected);
}

String AccessibilityMenuListOption::nameForMSAA() const
{
return stringValue();
}

bool AccessibilityMenuListOption::canSetSelectedAttribute() const
{
return isEnabled();
Expand Down
1 change: 0 additions & 1 deletion Source/WebCore/accessibility/AccessibilityMenuListOption.h
Expand Up @@ -51,7 +51,6 @@ class AccessibilityMenuListOption final : public AccessibilityObject {
bool isVisible() const final;
bool isOffScreen() const final;
bool isSelected() const final;
String nameForMSAA() const final;
void setSelected(bool) final;
bool canSetSelectedAttribute() const final;
LayoutRect elementRect() const final;
Expand Down
31 changes: 31 additions & 0 deletions Source/WebCore/accessibility/AccessibilityNodeObject.cpp
Expand Up @@ -811,6 +811,17 @@ bool AccessibilityNodeObject::isRequired() const
return false;
}

String AccessibilityNodeObject::accessKey() const
{
auto* element = this->element();
return element ? element->attributeWithoutSynchronization(accesskeyAttr) : String();
}

bool AccessibilityNodeObject::supportsARIAOwns() const
{
return !getAttribute(aria_ownsAttr).isEmpty();
}

bool AccessibilityNodeObject::supportsRequiredAttribute() const
{
switch (roleValue()) {
Expand Down Expand Up @@ -1917,6 +1928,26 @@ String AccessibilityNodeObject::helpText() const
return { };
}

URL AccessibilityNodeObject::url() const
{
auto* node = this->node();
if (isLink() && is<HTMLAnchorElement>(node))
return downcast<HTMLAnchorElement>(node)->href();

if (isImage() && is<HTMLImageElement>(node))
return downcast<HTMLImageElement>(node)->src();

if (isInputImage() && is<HTMLInputElement>(node))
return downcast<HTMLInputElement>(node)->src();

#if ENABLE(VIDEO)
if (isVideo() && is<HTMLVideoElement>(node))
return downcast<HTMLVideoElement>(node)->currentSrc();
#endif

return URL();
}

unsigned AccessibilityNodeObject::hierarchicalLevel() const
{
Node* node = this->node();
Expand Down
4 changes: 3 additions & 1 deletion Source/WebCore/accessibility/AccessibilityNodeObject.h
Expand Up @@ -70,6 +70,7 @@ class AccessibilityNodeObject : public AccessibilityObject {
bool isIndeterminate() const override;
bool isPressed() const override;
bool isRequired() const override;
bool supportsARIAOwns() const final;
bool supportsRequiredAttribute() const override;

bool canSetSelectedAttribute() const override;
Expand All @@ -95,6 +96,7 @@ class AccessibilityNodeObject : public AccessibilityObject {

AccessibilityButtonState checkboxOrRadioValue() const override;

URL url() const override;
unsigned hierarchicalLevel() const override;
String textUnderElement(AccessibilityTextUnderElementMode = AccessibilityTextUnderElementMode()) const override;
String accessibilityDescriptionForChildren() const;
Expand Down Expand Up @@ -137,7 +139,6 @@ class AccessibilityNodeObject : public AccessibilityObject {
void detachRemoteParts(AccessibilityDetachmentType) override;

AccessibilityRole m_ariaRole { AccessibilityRole::Unknown };
mutable AccessibilityRole m_roleForMSAA { AccessibilityRole::Unknown };
#ifndef NDEBUG
bool m_initialized { false };
#endif
Expand Down Expand Up @@ -172,6 +173,7 @@ class AccessibilityNodeObject : public AccessibilityObject {
const String liveRegionRelevant() const override;
bool liveRegionAtomic() const override;

String accessKey() const final;
bool isLabelable() const;
AccessibilityObject* correspondingControlForLabelElement() const override;
AccessibilityObject* correspondingLabelForControlElement() const override;
Expand Down

0 comments on commit 204cc1e

Please sign in to comment.