Skip to content

Commit

Permalink
accAX: Cache accessibilityText to serve Title, Description, Help Text…
Browse files Browse the repository at this point in the history
… attributes off of the main thread

https://bugs.webkit.org/show_bug.cgi?id=264642
rdar://118255218

Reviewed by Tyler Wilcock.

When the title, description, or help text attributes are requested for an AX element for the first time,
they are currently served on the main thread. To reduce this main thread dependency, this patch lazily
caches accessibilityText instead of the three attributes individually.

Caching accessibilityText allows us to compute the values for title, description, and help text on the
AX thread. This also allows us to move the implementations for these methods to AXCoreObject, so that
they can be shared between the live and isolated trees. We also benefit from caching AccessibilityText
by eliminating the redundant work within title, description, and help text separately.

Our old implementation of titleAttributeValue used textUnderElement() for calculating the title
of summary elements. We do not cache this, so instead, the implementation of visibleText was
modified to expose summary's visible text.

* Source/WebCore/accessibility/AXCoreObject.cpp:
(WebCore::isVisibleText):
(WebCore::isDescriptiveText):
(WebCore::AXCoreObject::descriptionAttributeValue const):
(WebCore::AXCoreObject::titleAttributeValue const):
(WebCore::AXCoreObject::helpTextAttributeValue const):
* Source/WebCore/accessibility/AXCoreObject.h:
(WebCore::AccessibilityText::AccessibilityText):
* Source/WebCore/accessibility/AccessibilityNodeObject.cpp:
(WebCore::AccessibilityNodeObject::visibleText const):
* Source/WebCore/accessibility/AccessibilityObject.h:
(WebCore::AccessibilityText::AccessibilityText): Deleted.
* Source/WebCore/accessibility/cocoa/AccessibilityObjectCocoa.mm:
(WebCore::isVisibleText): Deleted.
(WebCore::isDescriptiveText): Deleted.
(WebCore::AccessibilityObject::descriptionAttributeValue const): Deleted.
(WebCore::AccessibilityObject::titleAttributeValue const): Deleted.
(WebCore::AccessibilityObject::helpTextAttributeValue const): Deleted.
* Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.cpp:
(WebCore::AXIsolatedObject::initializeProperties):
(WebCore::AXIsolatedObject::fileUploadButtonReturnsValueInTitle const):
(WebCore::AXIsolatedObject::getOrRetrievePropertyValue):
* Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.h:
* Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.h:
* Source/WebCore/accessibility/isolatedtree/mac/AXIsolatedObjectMac.mm:
(WebCore::AXIsolatedObject::descriptionAttributeValue const): Deleted.
(WebCore::AXIsolatedObject::helpTextAttributeValue const): Deleted.
(WebCore::AXIsolatedObject::titleAttributeValue const): Deleted.

Canonical link: https://commits.webkit.org/270860@main
  • Loading branch information
hoffmanjoshua authored and twilco committed Nov 17, 2023
1 parent 130fd13 commit ef43e42
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 209 deletions.
149 changes: 149 additions & 0 deletions Source/WebCore/accessibility/AXCoreObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -386,4 +386,153 @@ AXCoreObject::AccessibilityChildrenVector AXCoreObject::selectedCells()
return selectedCells;
}

#if PLATFORM(COCOA)
static bool isVisibleText(AccessibilityTextSource textSource)
{
switch (textSource) {
case AccessibilityTextSource::Visible:
case AccessibilityTextSource::Children:
case AccessibilityTextSource::LabelByElement:
return true;
case AccessibilityTextSource::Alternative:
case AccessibilityTextSource::Summary:
case AccessibilityTextSource::Help:
case AccessibilityTextSource::TitleTag:
case AccessibilityTextSource::Placeholder:
case AccessibilityTextSource::Title:
case AccessibilityTextSource::Subtitle:
case AccessibilityTextSource::Action:
return false;
}
}

static bool isDescriptiveText(AccessibilityTextSource textSource)
{
switch (textSource) {
case AccessibilityTextSource::Alternative:
case AccessibilityTextSource::Visible:
case AccessibilityTextSource::Children:
case AccessibilityTextSource::LabelByElement:
return true;
case AccessibilityTextSource::Summary:
case AccessibilityTextSource::Help:
case AccessibilityTextSource::TitleTag:
case AccessibilityTextSource::Placeholder:
case AccessibilityTextSource::Title:
case AccessibilityTextSource::Subtitle:
case AccessibilityTextSource::Action:
return false;
}
}

String AXCoreObject::descriptionAttributeValue() const
{
if (!shouldComputeDescriptionAttributeValue())
return { };

Vector<AccessibilityText> textOrder;
accessibilityText(textOrder);

// Determine if any visible text is available, which influences our usage of title tag.
bool visibleTextAvailable = false;
for (const auto& text : textOrder) {
if (isVisibleText(text.textSource) && !text.text.isEmpty()) {
visibleTextAvailable = true;
break;
}
}

StringBuilder returnText;
for (const auto& text : textOrder) {
if (text.textSource == AccessibilityTextSource::Alternative) {
returnText.append(text.text);
break;
}

switch (text.textSource) {
// These are sub-components of one element (Attachment) that are re-combined in OSX and iOS.
case AccessibilityTextSource::Title:
case AccessibilityTextSource::Subtitle:
case AccessibilityTextSource::Action: {
if (!text.text.length())
break;
if (returnText.length())
returnText.append(", "_s);
returnText.append(text.text);
break;
}
default:
break;
}

if (text.textSource == AccessibilityTextSource::TitleTag && !visibleTextAvailable) {
returnText.append(text.text);
break;
}
}

return returnText.toString();
}

String AXCoreObject::titleAttributeValue() const
{
// Meter elements should communicate their content via AXValueDescription.
if (!shouldComputeTitleAttributeValue() || isMeter())
return { };

// A file upload button presents a challenge because it has button text and a value, but the
// API doesn't support this paradigm.
// The compromise is to return the button type in the role description and the value of the file path in the title
if (isFileUploadButton() && fileUploadButtonReturnsValueInTitle())
return stringValue();

Vector<AccessibilityText> textOrder;
accessibilityText(textOrder);

for (const auto& text : textOrder) {
// If we have alternative text, then we should not expose a title.
if (text.textSource == AccessibilityTextSource::Alternative)
break;

// Once we encounter visible text, or the text from our children that should be used foremost.
if (text.textSource == AccessibilityTextSource::Visible || text.textSource == AccessibilityTextSource::Children)
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 == AccessibilityTextSource::LabelByElement)
return text.text;
}

return { };
}

String AXCoreObject::helpTextAttributeValue() const
{
Vector<AccessibilityText> textOrder;
accessibilityText(textOrder);

// Determine if any descriptive text is available, which influences our usage of title tag.
bool descriptiveTextAvailable = false;
for (const auto& text : textOrder) {
if (isDescriptiveText(text.textSource) && !text.text.isEmpty()) {
descriptiveTextAvailable = true;
break;
}
}

for (const auto& text : textOrder) {
if (text.textSource == AccessibilityTextSource::Help || text.textSource == AccessibilityTextSource::Summary)
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.
if (text.textSource == AccessibilityTextSource::TitleTag && descriptiveTextAvailable)
return text.text;
}

return { };
}
#endif // PLATFORM(COCOA)

} // namespace WebCore
19 changes: 16 additions & 3 deletions Source/WebCore/accessibility/AXCoreObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,16 @@ struct AccessibilitySearchTextCriteria {
{ }
};

struct AccessibilityText {
String text;
AccessibilityTextSource textSource;

AccessibilityText(const String& text, const AccessibilityTextSource& source)
: text(text)
, textSource(source)
{ }
};

enum class AccessibilityTextOperationType {
Select,
Replace,
Expand Down Expand Up @@ -816,6 +826,7 @@ class AXCoreObject : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<AXCo
virtual bool isControl() const = 0;
// lists support (l, ul, ol, dl)
virtual bool isList() const = 0;
virtual bool isFileUploadButton() const = 0;

// Table support.
virtual bool isTable() const = 0;
Expand Down Expand Up @@ -892,6 +903,7 @@ class AXCoreObject : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<AXCo
bool isTreeItem() const { return roleValue() == AccessibilityRole::TreeItem; }
bool isScrollbar() const { return roleValue() == AccessibilityRole::ScrollBar; }
bool isButton() const;
virtual bool isMeter() const = 0;

virtual HashMap<String, AXEditingStyleValueVariant> resolvedEditingStyles() const = 0;

Expand Down Expand Up @@ -1360,12 +1372,13 @@ class AXCoreObject : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<AXCo
virtual bool preventKeyboardDOMEventDispatch() const = 0;
virtual void setPreventKeyboardDOMEventDispatch(bool) = 0;
virtual String speechHintAttributeValue() const = 0;
virtual String descriptionAttributeValue() const = 0;
virtual bool fileUploadButtonReturnsValueInTitle() const = 0;
String descriptionAttributeValue() const;
bool shouldComputeDescriptionAttributeValue() const;
virtual String helpTextAttributeValue() const = 0;
String helpTextAttributeValue() const;
// This should be the visible text that's actually on the screen if possible.
// If there's alternative text, that can override the title.
virtual String titleAttributeValue() const = 0;
String titleAttributeValue() const;
bool shouldComputeTitleAttributeValue() const;

virtual bool hasApplePDFAnnotationAttribute() const = 0;
Expand Down
5 changes: 5 additions & 0 deletions Source/WebCore/accessibility/AccessibilityNodeObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1945,6 +1945,11 @@ void AccessibilityNodeObject::visibleText(Vector<AccessibilityText>& textOrder)
if (node->hasTagName(selectTag))
break;
FALLTHROUGH;
case AccessibilityRole::Summary:
// The text node for a <summary> element should be included in its visible text, unless a title attribute is present.
if (!hasAttribute(titleAttr))
useTextUnderElement = true;
break;
case AccessibilityRole::Button:
case AccessibilityRole::ToggleButton:
case AccessibilityRole::Checkbox:
Expand Down
19 changes: 3 additions & 16 deletions Source/WebCore/accessibility/AccessibilityObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,6 @@ class IntPoint;
class IntSize;
class ScrollableArea;

struct AccessibilityText {
String text;
AccessibilityTextSource textSource;

AccessibilityText(const String& t, const AccessibilityTextSource& s)
: text(t)
, textSource(s)
{ }
};

bool nodeHasPresentationRole(Node*);

class AccessibilityObject : public AXCoreObject, public CanMakeWeakPtr<AccessibilityObject>, public CanMakeCheckedPtr {
Expand Down Expand Up @@ -128,7 +118,7 @@ class AccessibilityObject : public AXCoreObject, public CanMakeWeakPtr<Accessibi
bool isListBoxOption() const override { return false; }
bool isAttachment() const override { return false; }
bool isMediaTimeline() const { return false; }
bool isFileUploadButton() const;
bool isFileUploadButton() const final;
bool isInputImage() const override { return false; }
virtual bool isSliderThumb() const { return false; }
bool isControl() const override { return false; }
Expand Down Expand Up @@ -197,7 +187,7 @@ class AccessibilityObject : public AXCoreObject, public CanMakeWeakPtr<Accessibi
bool isARIATextControl() const;
bool isNonNativeTextControl() const override;
bool isRangeControl() const;
bool isMeter() const;
bool isMeter() const final;
bool isStyleFormatGroup() const;
bool isFigureElement() const;
bool isKeyboardFocusable() const override;
Expand Down Expand Up @@ -729,11 +719,8 @@ class AccessibilityObject : public AXCoreObject, public CanMakeWeakPtr<Accessibi
#if PLATFORM(COCOA)
bool preventKeyboardDOMEventDispatch() const override;
void setPreventKeyboardDOMEventDispatch(bool) override;
bool fileUploadButtonReturnsValueInTitle() const;
bool fileUploadButtonReturnsValueInTitle() const override;
String speechHintAttributeValue() const override;
String descriptionAttributeValue() const override;
String helpTextAttributeValue() const override;
String titleAttributeValue() const override;
bool hasApplePDFAnnotationAttribute() const override { return hasAttribute(HTMLNames::x_apple_pdf_annotationAttr); }
#endif

Expand Down
Loading

0 comments on commit ef43e42

Please sign in to comment.