Skip to content

Commit

Permalink
AX: Add notification for VoiceOver to announce percentage of completi…
Browse files Browse the repository at this point in the history
…on of isolated tree creation.

https://bugs.webkit.org/show_bug.cgi?id=270395
rdar://122335605

Reviewed by Tyler Wilcock.

VoiceOver responds to the AXLayoutComplete notification by announcing the page loading progress. This patch combines the percentage of page download with the isolated tree creation progress to report a single measure of page readiness to VoiceOver on the AXLayoutComplete notification.
In addition, we are setting the title of the WebArea element to the same percentage of readiness to allow the VoiceOver user to read it on demand.
Part of this fix involves making AXCoreObject::titleAttributeValue virtual so that the AXIsolatedObject can override this property when it is set by reportLoadingProgress.

* Source/WebCore/accessibility/AXCoreObject.h:
* Source/WebCore/accessibility/AXLogger.cpp:
(WebCore::AXLogger::shouldLog):
* Source/WebCore/accessibility/AXObjectCache.cpp:
(WebCore::AXObjectCache::AXObjectCache):
* Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.cpp:
(WebCore::AXIsolatedObject::titleAttributeValue const):
* Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.h:
* Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.cpp:
(WebCore::AXIsolatedTree::createEmpty):
(WebCore::AXIsolatedTree::create):
(WebCore::AXIsolatedTree::loadingProgress):
(WebCore::AXIsolatedTree::reportLoadingProgress):
(WebCore::AXIsolatedTree::resolveAppends):
(WebCore::AXIsolatedTree::reportCreationProgress): Deleted.
* Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.h:
(WebCore::AXIsolatedTree::loadingProgress): Deleted.
* Source/WebCore/accessibility/mac/AXObjectCacheMac.mm:
(WebCore::AXObjectCache::postPlatformNotification):

Canonical link: https://commits.webkit.org/275788@main
  • Loading branch information
AndresGonzalezApple committed Mar 7, 2024
1 parent 338528b commit 44f5b66
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 24 deletions.
2 changes: 1 addition & 1 deletion Source/WebCore/accessibility/AXCoreObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -1418,7 +1418,7 @@ class AXCoreObject : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<AXCo
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.
String titleAttributeValue() const;
virtual String titleAttributeValue() const;
bool shouldComputeTitleAttributeValue() const;

virtual bool hasApplePDFAnnotationAttribute() const = 0;
Expand Down
7 changes: 3 additions & 4 deletions Source/WebCore/accessibility/AXObjectCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,10 +282,9 @@ AXObjectCache::AXObjectCache(Document& document)

// If loading completed before the cache was created, loading progress will have been reset to zero.
// Consider loading progress to be 100% in this case.
double loadingProgress = document.page() ? document.page()->progress().estimatedProgress() : 1;
if (loadingProgress <= 0)
loadingProgress = 1;
m_loadingProgress = loadingProgress;
m_loadingProgress = document.page() ? document.page()->progress().estimatedProgress() : 1;
if (m_loadingProgress <= 0)
m_loadingProgress = 1;

if (m_pageID)
m_pageActivityState = m_document->page()->activityState();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1821,6 +1821,15 @@ void AXIsolatedObject::setAccessibleName(const AtomString&)
ASSERT_NOT_REACHED();
}

String AXIsolatedObject::titleAttributeValue() const
{
AXTRACE("AXIsolatedObject::titleAttributeValue"_s);

if (m_propertyMap.contains(AXPropertyName::TitleAttributeValue))
return propertyValue<String>(AXPropertyName::TitleAttributeValue);
return AXCoreObject::titleAttributeValue();
}

String AXIsolatedObject::stringValue() const
{
if (m_propertyMap.contains(AXPropertyName::StringValue))
Expand Down
5 changes: 3 additions & 2 deletions Source/WebCore/accessibility/isolatedtree/AXIsolatedObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,9 @@ class AXIsolatedObject final : public AXCoreObject {
#endif
IntSize size() const final { return snappedIntRect(LayoutRect(relativeFrame())).size(); }
FloatRect relativeFrameFromChildren() const;
WallTime dateTimeValue() const { return propertyValue<WallTime>(AXPropertyName::DateTimeValue); }
WallTime dateTimeValue() const final { return propertyValue<WallTime>(AXPropertyName::DateTimeValue); }
#if PLATFORM(MAC)
unsigned dateTimeComponents() const { return propertyValue<unsigned>(AXPropertyName::DateTimeComponents); }
unsigned dateTimeComponents() const final { return propertyValue<unsigned>(AXPropertyName::DateTimeComponents); }
#endif
bool supportsDatetimeAttribute() const final { return boolAttributeValue(AXPropertyName::SupportsDatetimeAttribute); }
String datetimeAttributeValue() const final { return stringAttributeValue(AXPropertyName::DatetimeAttributeValue); }
Expand Down Expand Up @@ -502,6 +502,7 @@ class AXIsolatedObject final : public AXCoreObject {
bool inheritsPresentationalRole() const final;
void setAccessibleName(const AtomString&) final;

String titleAttributeValue() const final;
String title() const final { return stringAttributeValue(AXPropertyName::Title); }
String description() const final { return stringAttributeValue(AXPropertyName::Description); }

Expand Down
41 changes: 26 additions & 15 deletions Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ Ref<AXIsolatedTree> AXIsolatedTree::createEmpty(AXObjectCache& axObjectCache)
}

tree->updateLoadingProgress(axObjectCache.loadingProgress());
tree->m_processingProgress = 0;

// Now that the tree is ready to take client requests, add it to the tree maps so that it can be found.
storeTree(axObjectCache, tree);
Expand Down Expand Up @@ -139,10 +140,8 @@ Ref<AXIsolatedTree> AXIsolatedTree::create(AXObjectCache& axObjectCache)
ASSERT(axObjectCache.pageID());

auto tree = adoptRef(*new AXIsolatedTree(axObjectCache));
if (RefPtr existingTree = isolatedTreeForID(tree->treeID())) {
if (RefPtr existingTree = isolatedTreeForID(tree->treeID()))
tree->m_replacingTree = existingTree;
tree->reportCreationProgress(axObjectCache, 0);
}

auto& document = axObjectCache.document();
if (!Accessibility::inRenderTreeOrStyleUpdate(document))
Expand Down Expand Up @@ -178,21 +177,33 @@ void AXIsolatedTree::storeTree(AXObjectCache& axObjectCache, const Ref<AXIsolate
treePageCache().set(*axObjectCache.pageID(), tree.copyRef());
}

void AXIsolatedTree::reportCreationProgress(AXObjectCache& cache, unsigned percentComplete)
double AXIsolatedTree::loadingProgress()
{
ASSERT(m_replacingTree->isEmptyContentTree());
return .50 * m_loadingProgress + .50 * m_processingProgress;
}

String percent = String::number(percentComplete) + "%"_s;
String title = AXProcessingPage() + " "_s + percent;
if (RefPtr axRoot = cache.get(cache.document().view())) {
m_replacingTree->overrideNodeProperties(axRoot->objectID(), {
{ AXPropertyName::TitleAttributeValue, title },
});
void AXIsolatedTree::reportLoadingProgress(double processingProgress)
{
AXTRACE("AXIsolatedTree::reportLoadingProgress"_s);
ASSERT(isMainThread());

if (!isEmptyContentTree()) {
ASSERT_NOT_REACHED();
return;
}
if (RefPtr axWebarea = cache.rootWebArea()) {
m_replacingTree->overrideNodeProperties(axWebarea->objectID(), {

m_processingProgress = processingProgress;
String percent = String::number(std::ceil(loadingProgress() * 100)) + "%"_s;
String title = AXProcessingPage() + " "_s + percent;
AXLOG(title);

WeakPtr cache = axObjectCache();
if (RefPtr axWebArea = cache ? cache->rootWebArea() : nullptr) {
overrideNodeProperties(axWebArea->objectID(), {
{ AXPropertyName::TitleAttributeValue, WTFMove(title) },
});
if (cache)
cache->postPlatformNotification(axWebArea.get(), AXObjectCache::AXNotification::AXLayoutComplete);
}
}

Expand Down Expand Up @@ -384,7 +395,7 @@ Vector<AXIsolatedTree::NodeChange> AXIsolatedTree::resolveAppends()
if (m_replacingTree) {
++counter;
if (MonotonicTime::now() - lastFeedbackTime > CreationFeedbackInterval) {
reportCreationProgress(*cache, std::ceil((counter / m_unresolvedPendingAppends.size()) * 100));
m_replacingTree->reportLoadingProgress(counter / m_unresolvedPendingAppends.size());
lastFeedbackTime = MonotonicTime::now();
}
}
Expand All @@ -398,7 +409,7 @@ Vector<AXIsolatedTree::NodeChange> AXIsolatedTree::resolveAppends()
m_unresolvedPendingAppends.clear();

if (m_replacingTree)
reportCreationProgress(*cache, 100);
m_replacingTree->reportLoadingProgress(1);
return resolvedAppends;
}

Expand Down
5 changes: 3 additions & 2 deletions Source/WebCore/accessibility/isolatedtree/AXIsolatedTree.h
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ class AXIsolatedTree : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<AX
void updateRootScreenRelativePosition();
void overrideNodeProperties(AXID, AXPropertyMap&&);

double loadingProgress() { return m_loadingProgress; }
double loadingProgress();
void updateLoadingProgress(double);

void addUnconnectedNode(Ref<AccessibilityObject>);
Expand Down Expand Up @@ -394,7 +394,7 @@ class AXIsolatedTree : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<AX
private:
AXIsolatedTree(AXObjectCache&);
static void storeTree(AXObjectCache&, const Ref<AXIsolatedTree>&);
void reportCreationProgress(AXObjectCache&, unsigned percentComplete);
void reportLoadingProgress(double);

// Queue this isolated tree up to destroy itself on the secondary thread.
// We can't destroy the tree on the main-thread (by removing all `Ref`s to it)
Expand Down Expand Up @@ -485,6 +485,7 @@ class AXIsolatedTree : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<AX
bool m_queuedForDestruction WTF_GUARDED_BY_LOCK(m_changeLogLock) { false };
AXID m_focusedNodeID;
std::atomic<double> m_loadingProgress { 0 };
std::atomic<double> m_processingProgress { 1 };

// Relationships between objects.
HashMap<AXID, AXRelations> m_relations WTF_GUARDED_BY_LOCK(m_changeLogLock);
Expand Down
3 changes: 3 additions & 0 deletions Source/WebCore/accessibility/mac/AXObjectCacheMac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,9 @@ static void exerciseIsIgnored(AccessibilityObject& object)
case AXLayoutComplete:
macNotification = @"AXLayoutComplete";
break;
case AXLabelChanged:
macNotification = NSAccessibilityTitleChangedNotification;
break;
case AXLoadComplete:
case AXFrameLoadComplete:
macNotification = @"AXLoadComplete";
Expand Down

0 comments on commit 44f5b66

Please sign in to comment.