Title: [96340] trunk
Revision
96340
Author
[email protected]
Date
2011-09-29 10:46:14 -0700 (Thu, 29 Sep 2011)

Log Message

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

Source/WebCore: 

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):

LayoutTests: 

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.

Modified Paths

Added Paths

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()
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to