Diff
Modified: trunk/LayoutTests/ChangeLog (96339 => 96340)
--- trunk/LayoutTests/ChangeLog 2011-09-29 17:35:41 UTC (rev 96339)
+++ trunk/LayoutTests/ChangeLog 2011-09-29 17:46:14 UTC (rev 96340)
@@ -1,3 +1,13 @@
+2011-09-29 Chris Fleizach <[email protected]>
+
+ ARIA live regions don't trigger notifications for elements that aren't in the AX tree
+ https://bugs.webkit.org/show_bug.cgi?id=62289
+
+ Reviewed by Darin Adler.
+
+ * platform/mac/accessibility/aria-liveregion-without-element-access-expected.txt: Added.
+ * platform/mac/accessibility/aria-liveregion-without-element-access.html: Added.
+
2011-09-29 David Reveman <[email protected]>
Rebaseline for r95870
Added: trunk/LayoutTests/platform/mac/accessibility/aria-liveregion-without-element-access-expected.txt (0 => 96340)
--- trunk/LayoutTests/platform/mac/accessibility/aria-liveregion-without-element-access-expected.txt (rev 0)
+++ trunk/LayoutTests/platform/mac/accessibility/aria-liveregion-without-element-access-expected.txt 2011-09-29 17:46:14 UTC (rev 96340)
@@ -0,0 +1,14 @@
+ALERT: Successfully received AXLiveRegionChanged
+text
+
+added
+This tests that adding an element to a live region will trigger a notification even though the AX element was not created previously.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS addedNotification is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/platform/mac/accessibility/aria-liveregion-without-element-access.html (0 => 96340)
--- trunk/LayoutTests/platform/mac/accessibility/aria-liveregion-without-element-access.html (rev 0)
+++ trunk/LayoutTests/platform/mac/accessibility/aria-liveregion-without-element-access.html 2011-09-29 17:46:14 UTC (rev 96340)
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href=""
+<script>
+var successfullyParsed = false;
+</script>
+<script src=""
+</head>
+<body id="body">
+
+<div role="group" tabindex=0 id="liveregion" aria-live="polite" aria-relevant="additions">
+<h3 id="innerlive">text</h3>
+<div role="group">
+ <div id="addregion"></div>
+</div>
+</div>
+
+<p id="description"></p>
+<div id="console"></div>
+
+<script>
+
+ description("This tests that adding an element to a live region will trigger a notification even though the AX element was not created previously.");
+
+ var liveRegion = 0;
+ function ariaCallback(notification) {
+ if (notification == "AXLiveRegionChanged") {
+ alert("Successfully received " + notification);
+ liveRegion.removeNotificationListener();
+ window.layoutTestController.notifyDone();
+ }
+ }
+
+ if (window.accessibilityController) {
+ window.layoutTestController.waitUntilDone();
+
+ document.getElementById("liveregion").focus();
+ liveRegion = window.accessibilityController.focusedElement;
+
+ // Trigger the live region callback for a new element. Add region has not been created
+ // as an element, but this notification should still be sent for liveregion.
+ document.getElementById("addregion").appendChild(document.createTextNode("added"));
+
+ var addedNotification = liveRegion.addNotificationListener(ariaCallback);
+ shouldBe("addedNotification", "true");
+ }
+
+ successfullyParsed = true;
+</script>
+
+<script src=""
+</body>
+</html>
Modified: trunk/Source/WebCore/ChangeLog (96339 => 96340)
--- trunk/Source/WebCore/ChangeLog 2011-09-29 17:35:41 UTC (rev 96339)
+++ trunk/Source/WebCore/ChangeLog 2011-09-29 17:46:14 UTC (rev 96340)
@@ -1,3 +1,43 @@
+2011-09-29 Chris Fleizach <[email protected]>
+
+ ARIA live regions don't trigger notifications for elements that aren't in the AX tree
+ https://bugs.webkit.org/show_bug.cgi?id=62289
+
+ If an ARIA Live region udpates an element that is not in the AX object cache, then the Live region
+ notification is not sent. To fix this, the childrenChanged() method needs to actually create
+ the appropriate objects, but since that method gets called during a render tree update, we've learned
+ that it's generally not safe to create objects.
+
+ Instead a one shot timer can be fired that will update and create the necessary objects so that the
+ correct notification can be sent.
+
+ Reviewed by Darin Adler.
+
+ Test: platform/mac/accessibility/aria-liveregion-without-element-access.html
+
+ * accessibility/AXObjectCache.cpp:
+ (WebCore::AXObjectCache::AXObjectCache):
+ (WebCore::AXObjectCache::~AXObjectCache):
+ (WebCore::AXObjectCache::remove):
+ (WebCore::AXObjectCache::childrenUpdateTimerFired):
+ (WebCore::AXObjectCache::childrenChanged):
+ * accessibility/AXObjectCache.h:
+ * accessibility/AccessibilityMenuList.cpp:
+ (WebCore::AccessibilityMenuList::childrenChanged):
+ * accessibility/AccessibilityMenuList.h:
+ * accessibility/AccessibilityMenuListPopup.cpp:
+ (WebCore::AccessibilityMenuListPopup::childrenChanged):
+ * accessibility/AccessibilityMenuListPopup.h:
+ * accessibility/AccessibilityObject.h:
+ (WebCore::AccessibilityObject::childrenChanged):
+ * accessibility/AccessibilityRenderObject.cpp:
+ (WebCore::startOfContinuations):
+ (WebCore::AccessibilityRenderObject::updateAccessibilityRole):
+ (WebCore::AccessibilityRenderObject::childrenChanged):
+ * accessibility/AccessibilityRenderObject.h:
+ * rendering/RenderObject.cpp:
+ (WebCore::RenderObject::willBeDestroyed):
+
2011-09-29 Martin Robinson <[email protected]>
[GTK] Dragging a selection does not produce a drag image
Modified: trunk/Source/WebCore/accessibility/AXObjectCache.cpp (96339 => 96340)
--- trunk/Source/WebCore/accessibility/AXObjectCache.cpp 2011-09-29 17:35:41 UTC (rev 96339)
+++ trunk/Source/WebCore/accessibility/AXObjectCache.cpp 2011-09-29 17:46:14 UTC (rev 96340)
@@ -82,12 +82,16 @@
AXObjectCache::AXObjectCache(const Document* doc)
: m_notificationPostTimer(this, &AXObjectCache::notificationPostTimerFired)
+ , m_childrenUpdateTimer(this, &AXObjectCache::childrenUpdateTimerFired)
{
m_document = const_cast<Document*>(doc);
}
AXObjectCache::~AXObjectCache()
{
+ m_childrenUpdateTimer.stop();
+ m_notificationPostTimer.stop();
+
HashMap<AXID, RefPtr<AccessibilityObject> >::iterator end = m_objects.end();
for (HashMap<AXID, RefPtr<AccessibilityObject> >::iterator it = m_objects.begin(); it != end; ++it) {
AccessibilityObject* obj = (*it).second.get();
@@ -367,6 +371,7 @@
AXID axID = m_renderObjectMapping.get(renderer);
remove(axID);
m_renderObjectMapping.remove(renderer);
+ m_childrenToUpdate.remove(renderer);
}
void AXObjectCache::remove(Widget* view)
@@ -436,6 +441,24 @@
object->contentChanged();
}
#endif
+
+void AXObjectCache::childrenUpdateTimerFired(Timer<AXObjectCache>*)
+{
+ if (m_childrenToUpdate.isEmpty())
+ return;
+
+ // Make a local copy in case childrenChanged() alters m_childrenToUpdate
+ // (which might happen if the client asks to update the render tree).
+ HashSet<RenderObject*> updateChildren;
+ m_childrenToUpdate.swap(updateChildren);
+ m_childrenToUpdate.clear();
+
+ HashSet<RenderObject*>::iterator end = updateChildren.end();
+ for (HashSet<RenderObject*>::iterator it = updateChildren.begin(); it != end; ++it) {
+ if (AccessibilityObject* object = getOrCreate(*it))
+ object->childrenChanged(AccessibilityObject::CreateParentObjects);
+ }
+}
void AXObjectCache::childrenChanged(RenderObject* renderer)
{
@@ -443,12 +466,18 @@
return;
AXID axID = m_renderObjectMapping.get(renderer);
- if (!axID)
- return;
-
- AccessibilityObject* obj = m_objects.get(axID).get();
- if (obj)
- obj->childrenChanged();
+ if (!axID) {
+ // If there's no AX object, creating one right now can be dangerous (because we're in the middle of adding/destroying a tree).
+ // Instead the update should be postponed and updated later.
+ m_childrenToUpdate.add(renderer);
+
+ if (!m_childrenUpdateTimer.isActive())
+ m_childrenUpdateTimer.startOneShot(0);
+ } else {
+ if (AccessibilityObject* object = m_objects.get(axID).get())
+ object->childrenChanged(AccessibilityObject::DoNotCreateParentObjects);
+ }
+
}
void AXObjectCache::notificationPostTimerFired(Timer<AXObjectCache>*)
Modified: trunk/Source/WebCore/accessibility/AXObjectCache.h (96339 => 96340)
--- trunk/Source/WebCore/accessibility/AXObjectCache.h 2011-09-29 17:35:41 UTC (rev 96339)
+++ trunk/Source/WebCore/accessibility/AXObjectCache.h 2011-09-29 17:46:14 UTC (rev 96340)
@@ -167,7 +167,11 @@
Timer<AXObjectCache> m_notificationPostTimer;
Vector<pair<RefPtr<AccessibilityObject>, AXNotification> > m_notificationsToPost;
void notificationPostTimerFired(Timer<AXObjectCache>*);
-
+
+ Timer<AXObjectCache> m_childrenUpdateTimer;
+ HashSet<RenderObject*> m_childrenToUpdate;
+ void childrenUpdateTimerFired(Timer<AXObjectCache>*);
+
static AccessibilityObject* focusedImageMapUIElement(HTMLAreaElement*);
AXID getAXID(AccessibilityObject*);
Modified: trunk/Source/WebCore/accessibility/AccessibilityMenuList.cpp (96339 => 96340)
--- trunk/Source/WebCore/accessibility/AccessibilityMenuList.cpp 2011-09-29 17:35:41 UTC (rev 96339)
+++ trunk/Source/WebCore/accessibility/AccessibilityMenuList.cpp 2011-09-29 17:46:14 UTC (rev 96340)
@@ -68,13 +68,13 @@
list->addChildren();
}
-void AccessibilityMenuList::childrenChanged()
+void AccessibilityMenuList::childrenChanged(ChildrenChangeOptions options)
{
if (m_children.isEmpty())
return;
ASSERT(m_children.size() == 1);
- m_children[0]->childrenChanged();
+ m_children[0]->childrenChanged(options);
}
bool AccessibilityMenuList::isCollapsed() const
Modified: trunk/Source/WebCore/accessibility/AccessibilityMenuList.h (96339 => 96340)
--- trunk/Source/WebCore/accessibility/AccessibilityMenuList.h 2011-09-29 17:35:41 UTC (rev 96339)
+++ trunk/Source/WebCore/accessibility/AccessibilityMenuList.h 2011-09-29 17:46:14 UTC (rev 96340)
@@ -53,7 +53,7 @@
virtual bool canSetFocusAttribute() const { return true; }
virtual void addChildren();
- virtual void childrenChanged();
+ virtual void childrenChanged(ChildrenChangeOptions);
};
} // namespace WebCore
Modified: trunk/Source/WebCore/accessibility/AccessibilityMenuListPopup.cpp (96339 => 96340)
--- trunk/Source/WebCore/accessibility/AccessibilityMenuListPopup.cpp 2011-09-29 17:35:41 UTC (rev 96339)
+++ trunk/Source/WebCore/accessibility/AccessibilityMenuListPopup.cpp 2011-09-29 17:46:14 UTC (rev 96340)
@@ -103,7 +103,7 @@
}
}
-void AccessibilityMenuListPopup::childrenChanged()
+void AccessibilityMenuListPopup::childrenChanged(ChildrenChangeOptions)
{
for (size_t i = m_children.size(); i > 0 ; --i) {
AccessibilityObject* child = m_children[i - 1].get();
Modified: trunk/Source/WebCore/accessibility/AccessibilityMenuListPopup.h (96339 => 96340)
--- trunk/Source/WebCore/accessibility/AccessibilityMenuListPopup.h 2011-09-29 17:35:41 UTC (rev 96339)
+++ trunk/Source/WebCore/accessibility/AccessibilityMenuListPopup.h 2011-09-29 17:46:14 UTC (rev 96340)
@@ -59,7 +59,7 @@
virtual AccessibilityObject* parentObject() const;
virtual bool press() const;
virtual void addChildren();
- virtual void childrenChanged();
+ virtual void childrenChanged(ChildrenChangeOptions);
AccessibilityMenuListOption* menuListOptionAccessibilityObject(HTMLElement*) const;
Modified: trunk/Source/WebCore/accessibility/AccessibilityObject.h (96339 => 96340)
--- trunk/Source/WebCore/accessibility/AccessibilityObject.h 2011-09-29 17:35:41 UTC (rev 96339)
+++ trunk/Source/WebCore/accessibility/AccessibilityObject.h 2011-09-29 17:46:14 UTC (rev 96340)
@@ -550,7 +550,8 @@
virtual void increment() { }
virtual void decrement() { }
- virtual void childrenChanged() { }
+ enum ChildrenChangeOptions { DoNotCreateParentObjects, CreateParentObjects };
+ virtual void childrenChanged(ChildrenChangeOptions) { }
virtual void contentChanged() { }
virtual const AccessibilityChildrenVector& children() { return m_children; }
virtual void addChildren() { }
Modified: trunk/Source/WebCore/accessibility/AccessibilityRenderObject.cpp (96339 => 96340)
--- trunk/Source/WebCore/accessibility/AccessibilityRenderObject.cpp 2011-09-29 17:35:41 UTC (rev 96339)
+++ trunk/Source/WebCore/accessibility/AccessibilityRenderObject.cpp 2011-09-29 17:46:14 UTC (rev 96340)
@@ -223,7 +223,7 @@
static inline RenderInline* startOfContinuations(RenderObject* r)
{
- if (r->isInlineElementContinuation())
+ if (r->isInlineElementContinuation() && r->node()->renderer() && r->isRenderInline())
return toRenderInline(r->node()->renderer());
// Blocks with a previous continuation always have a next continuation
@@ -3087,7 +3087,7 @@
// The AX hierarchy only needs to be updated if the ignored status of an element has changed.
if (ignoredStatus != accessibilityIsIgnored())
- childrenChanged();
+ childrenChanged(DoNotCreateParentObjects);
}
AccessibilityRole AccessibilityRenderObject::determineAccessibilityRole()
@@ -3370,7 +3370,7 @@
}
}
-void AccessibilityRenderObject::childrenChanged()
+void AccessibilityRenderObject::childrenChanged(ChildrenChangeOptions options)
{
// This method is meant as a quick way of marking a portion of the accessibility tree dirty.
if (!m_renderer)
@@ -3382,7 +3382,7 @@
// called during render layouts, minimal work should be done.
// If AX elements are created now, they could interrogate the render tree while it's in a funky state.
// At the same time, process ARIA live region changes.
- for (AccessibilityObject* parent = this; parent; parent = parent->parentObjectIfExists()) {
+ for (AccessibilityObject* parent = this; parent; parent = (options == CreateParentObjects) ? parent->parentObject() : parent->parentObjectIfExists()) {
if (!parent->isAccessibilityRenderObject())
continue;
Modified: trunk/Source/WebCore/accessibility/AccessibilityRenderObject.h (96339 => 96340)
--- trunk/Source/WebCore/accessibility/AccessibilityRenderObject.h 2011-09-29 17:35:41 UTC (rev 96339)
+++ trunk/Source/WebCore/accessibility/AccessibilityRenderObject.h 2011-09-29 17:46:14 UTC (rev 96340)
@@ -215,7 +215,7 @@
virtual void decrement();
virtual void detach();
- virtual void childrenChanged();
+ virtual void childrenChanged(ChildrenChangeOptions);
virtual void contentChanged();
virtual void addChildren();
virtual bool canHaveChildren() const;
Modified: trunk/Source/WebCore/rendering/RenderObject.cpp (96339 => 96340)
--- trunk/Source/WebCore/rendering/RenderObject.cpp 2011-09-29 17:35:41 UTC (rev 96339)
+++ trunk/Source/WebCore/rendering/RenderObject.cpp 2011-09-29 17:46:14 UTC (rev 96340)
@@ -2176,10 +2176,6 @@
if (frame() && frame()->eventHandler()->autoscrollRenderer() == this)
frame()->eventHandler()->stopAutoscrollTimer(true);
- if (AXObjectCache::accessibilityEnabled()) {
- document()->axObjectCache()->childrenChanged(this->parent());
- document()->axObjectCache()->remove(this);
- }
animation()->cancelAnimations(this);
remove();
@@ -2198,6 +2194,13 @@
setHasLayer(false);
toRenderBoxModelObject(this)->destroyLayer();
}
+
+ // Update accessibility at the end, so that all children nodes have been disassociated first.
+ // This ordering allows us to call childrenChanged() on the parent without worrying that the parent has been destroyed.
+ if (AXObjectCache::accessibilityEnabled()) {
+ document()->axObjectCache()->childrenChanged(this->parent());
+ document()->axObjectCache()->remove(this);
+ }
}
void RenderObject::destroy()