Diff
Modified: trunk/LayoutTests/ChangeLog (160416 => 160417)
--- trunk/LayoutTests/ChangeLog 2013-12-11 10:00:59 UTC (rev 160416)
+++ trunk/LayoutTests/ChangeLog 2013-12-11 10:28:21 UTC (rev 160417)
@@ -1,3 +1,23 @@
+2013-12-11 Mario Sanchez Prada <mario.pr...@samsung.com>
+
+ Programmatically-inserted children lack accessibility events
+ https://bugs.webkit.org/show_bug.cgi?id=100275
+
+ Reviewed by Chris Fleizach.
+
+ Add new test to chack that children-changed signals are properly
+ emitted when adding/removing elements in the accessibility hierarchy.
+
+ * accessibility/children-changed-sends-notification-expected.txt: Added.
+ * accessibility/children-changed-sends-notification.html: Added.
+
+ Update test to filter out unrelated non-loading events.
+ * accessibility/loading-iframe-sends-notification.html: Updated.
+
+ Skip the test on the Mac as it does not expose these kind of
+ notifications when children are being added or removed.
+ * platform/mac/TestExpectations: Skip newly added test.
+
2013-12-10 Gurpreet Kaur <k.gurpr...@samsung.com>
top and bottom black background line not getting displayed
Added: trunk/LayoutTests/accessibility/children-changed-sends-notification-expected.txt (0 => 160417)
--- trunk/LayoutTests/accessibility/children-changed-sends-notification-expected.txt (rev 0)
+++ trunk/LayoutTests/accessibility/children-changed-sends-notification-expected.txt 2013-12-11 10:28:21 UTC (rev 160417)
@@ -0,0 +1,18 @@
+This test ensures that a notification is being emitted when children are added or removed for an accessibility object
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Plain text paragraph
+
+End of test
+PARAGRAPH notification: AXChildrenAdded
+GLOBAL notification: AXChildrenAdded on element with role AXRole: AXParagraph
+PARAGRAPH notification: AXChildrenRemoved
+GLOBAL notification: AXChildrenRemoved on element with role AXRole: AXParagraph
+PASS paragraphNotificationCount is globalNotificationCount
+PASS globalNotificationCount is 2
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/accessibility/children-changed-sends-notification.html (0 => 160417)
--- trunk/LayoutTests/accessibility/children-changed-sends-notification.html (rev 0)
+++ trunk/LayoutTests/accessibility/children-changed-sends-notification.html 2013-12-11 10:28:21 UTC (rev 160417)
@@ -0,0 +1,84 @@
+<html>
+<head>
+<script src=""
+</head>
+<body>
+
+<p id="description"></p>
+<p id="paragraph">Plain text paragraph <a id="testLink" href="" a link at the end</a></p>
+<div>End of test</div>
+
+<div id="console"></div>
+
+<script>
+window.jsTestIsAsync = true;
+
+description("This test ensures that a notification is being emitted when children are added or removed for an accessibility object");
+
+function touchAccessibilityTree(accessibilityObject) {
+ if (accessibilityObject.stringValue.indexOf('End of test') >= 0)
+ return false;
+
+ var count = accessibilityObject.childrenCount;
+ for (var i = 0; i < count; ++i) {
+ if (!touchAccessibilityTree(accessibilityObject.childAtIndex(i)))
+ return false;
+ }
+
+ return true;
+}
+
+function runTest() {
+ window.paragraphNotificationCount = 0;
+ window.globalNotificationCount = 0;
+
+ if (window.accessibilityController) {
+ var axWebArea = accessibilityController.rootElement.childAtIndex(0);
+
+ // Ensure the accessibility hierarchy is generated.
+ touchAccessibilityTree(axWebArea);
+
+ window.paragraph = axWebArea.childAtIndex(1);
+ paragraph.addNotificationListener(function(notification) {
+ paragraphNotificationCount++;
+ debug("PARAGRAPH notification: " + notification);
+ });
+
+ accessibilityController.addNotificationListener(function(element, notification) {
+ if (!element.isEqual(paragraph))
+ return;
+
+ globalNotificationCount++;
+ debug("GLOBAL notification: " + notification + " on element with role " + element.role);
+ });
+ }
+
+ var par = document.getElementById('paragraph');
+ window.newChild = document.createElement('button');
+ par.appendChild(newChild);
+
+ window.setTimeout(function() {
+ var link = document.getElementById('testLink');
+ link.style.display = 'none';
+
+ // Ensure the accessibility hierarchy is regenerated.
+ touchAccessibilityTree(axWebArea);
+
+ shouldBe("paragraphNotificationCount", "globalNotificationCount");
+ shouldBe("globalNotificationCount", "2");
+
+ if (window.accessibilityController) {
+ paragraph.removeNotificationListener();
+ accessibilityController.removeNotificationListener();
+ }
+
+ finishJSTest();
+ }, 0);
+}
+
+runTest();
+</script>
+
+<script src=""
+</body>
+</html>
Modified: trunk/LayoutTests/accessibility/loading-iframe-sends-notification.html (160416 => 160417)
--- trunk/LayoutTests/accessibility/loading-iframe-sends-notification.html 2013-12-11 10:00:59 UTC (rev 160416)
+++ trunk/LayoutTests/accessibility/loading-iframe-sends-notification.html 2013-12-11 10:28:21 UTC (rev 160417)
@@ -53,8 +53,9 @@
shouldBeFalse("findByAccessibleTitleSubstring(root, 'InnerButton') != null");
window.accessibilityController.addNotificationListener(function (target, notification) {
- // Ignore this notification if it's not on the iframe.
- if (target.description.indexOf("InnerFrame") == -1)
+ // Ignore this notification if it's not on the iframe or not about the iframe being loaded.
+ if (target.description.indexOf("InnerFrame") == -1
+ || (notification != "AXLoadComplete" && notification != "AXLayoutComplete"))
return;
debug("Got notification on iframe.");
Modified: trunk/LayoutTests/platform/mac/TestExpectations (160416 => 160417)
--- trunk/LayoutTests/platform/mac/TestExpectations 2013-12-11 10:00:59 UTC (rev 160416)
+++ trunk/LayoutTests/platform/mac/TestExpectations 2013-12-11 10:28:21 UTC (rev 160417)
@@ -40,6 +40,7 @@
# Accessibility tests for notifications that don't exist or aren't needed on Mac OS X.
accessibility/aria-checkbox-sends-notification.html
+accessibility/children-changed-sends-notification.html
accessibility/menu-list-sends-change-notification.html
accessibility/multiselect-list-reports-active-option.html
Modified: trunk/Source/WebCore/ChangeLog (160416 => 160417)
--- trunk/Source/WebCore/ChangeLog 2013-12-11 10:00:59 UTC (rev 160416)
+++ trunk/Source/WebCore/ChangeLog 2013-12-11 10:28:21 UTC (rev 160417)
@@ -1,3 +1,50 @@
+2013-12-11 Mario Sanchez Prada <mario.pr...@samsung.com>
+
+ Programmatically-inserted children lack accessibility events
+ https://bugs.webkit.org/show_bug.cgi?id=100275
+
+ Reviewed by Chris Fleizach.
+
+ Test: accessibility/children-changed-sends-notification.html
+
+ Emit children-changed::add and children-changed::remove whenever
+ an object has been added/removed to the accessibility hierarchy,
+ that is, when a new AtkObject is being attached/detached.
+
+ * accessibility/AXObjectCache.h: Added new enumeration to know
+ when we are detaching a wrapper because of the cache or the
+ element is being destroyed, so we can use that information.
+ (WebCore::AXObjectCache::detachWrapper): Added a new parameter and
+ updated all the prototypes in different ports.
+ * accessibility/AXObjectCache.cpp:
+ (WebCore::AXObjectCache::~AXObjectCache): Call detachWrapper()
+ specifying that we do it because the cache is being destroyed.
+ (WebCore::AXObjectCache::remove): Call detachWrapper() specifying
+ that we do it because an accessible element is being destroyed.
+
+ * accessibility/atk/AXObjectCacheAtk.cpp:
+ (WebCore::AXObjectCache::detachWrapper): Emit the children-changed
+ signal when needed. We rely on the cached reference to the parent
+ AtkObject (using the implementation of atk_object_get_parent from
+ the AtkObject class) to find the right object to emit the signal
+ from here, since the accessibility hierarchy from WebCore will no
+ longer be accessible at this point.
+ (WebCore::AXObjectCache::attachWrapper): Emit the children-change
+ signal from here unless we are in the middle of a layout update,
+ trying to provide as much information (e.g. the offset) as possible.
+ (WebCore::AXObjectCache::postPlatformNotification): Make sure we
+ update (touch) the subtree under an accessibility object whenever
+ we receive AXChildrenChanded from WebCore, to ensure that those
+ objects will also be visible rightaway to ATs, and that those get
+ properly notified of the event at that very same moment.
+
+ * accessibility/ios/AXObjectCacheIOS.mm:
+ (WebCore::AXObjectCache::detachWrapper): Updated function signature.
+ * accessibility/mac/AXObjectCacheMac.mm:
+ (WebCore::AXObjectCache::detachWrapper): Ditto.
+ * accessibility/win/AXObjectCacheWin.cpp:
+ (WebCore::AXObjectCache::detachWrapper): Ditto.
+
2013-12-11 Andreas Kling <akl...@apple.com>
REGRESSION(r160389): SVG test assertion extravaganza.
Modified: trunk/Source/WebCore/accessibility/AXObjectCache.cpp (160416 => 160417)
--- trunk/Source/WebCore/accessibility/AXObjectCache.cpp 2013-12-11 10:00:59 UTC (rev 160416)
+++ trunk/Source/WebCore/accessibility/AXObjectCache.cpp 2013-12-11 10:28:21 UTC (rev 160417)
@@ -121,7 +121,7 @@
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).value.get();
- detachWrapper(obj);
+ detachWrapper(obj, CacheDestroyed);
obj->detach();
removeAXID(obj);
}
@@ -493,7 +493,7 @@
if (!obj)
return;
- detachWrapper(obj);
+ detachWrapper(obj, ElementDestroyed);
obj->detach();
removeAXID(obj);
Modified: trunk/Source/WebCore/accessibility/AXObjectCache.h (160416 => 160417)
--- trunk/Source/WebCore/accessibility/AXObjectCache.h 2013-12-11 10:00:59 UTC (rev 160416)
+++ trunk/Source/WebCore/accessibility/AXObjectCache.h 2013-12-11 10:28:21 UTC (rev 160417)
@@ -71,6 +71,8 @@
enum PostType { PostSynchronously, PostAsynchronously };
+enum DetachmentType { CacheDestroyed, ElementDestroyed };
+
class AXObjectCache {
WTF_MAKE_NONCOPYABLE(AXObjectCache); WTF_MAKE_FAST_ALLOCATED;
public:
@@ -102,7 +104,7 @@
void remove(Widget*);
void remove(AXID);
- void detachWrapper(AccessibilityObject*);
+ void detachWrapper(AccessibilityObject*, DetachmentType);
void attachWrapper(AccessibilityObject*);
void childrenChanged(Node*);
void childrenChanged(RenderObject*);
@@ -284,7 +286,7 @@
inline void AXObjectCache::textChanged(Node*) { }
inline void AXObjectCache::textChanged(AccessibilityObject*) { }
inline void AXObjectCache::updateCacheAfterNodeIsAttached(Node*) { }
-inline void AXObjectCache::detachWrapper(AccessibilityObject*) { }
+inline void AXObjectCache::detachWrapper(AccessibilityObject*, DetachmentType) { }
inline void AXObjectCache::frameLoadingEventNotification(Frame*, AXLoadingEvent) { }
inline void AXObjectCache::frameLoadingEventPlatformNotification(AccessibilityObject*, AXLoadingEvent) { }
inline void AXObjectCache::handleActiveDescendantChanged(Node*) { }
Modified: trunk/Source/WebCore/accessibility/atk/AXObjectCacheAtk.cpp (160416 => 160417)
--- trunk/Source/WebCore/accessibility/atk/AXObjectCacheAtk.cpp 2013-12-11 10:00:59 UTC (rev 160416)
+++ trunk/Source/WebCore/accessibility/atk/AXObjectCacheAtk.cpp 2013-12-11 10:28:21 UTC (rev 160417)
@@ -31,13 +31,37 @@
#include "TextIterator.h"
#include "WebKitAccessibleWrapperAtk.h"
#include <wtf/gobject/GOwnPtr.h>
+#include <wtf/gobject/GRefPtr.h>
#include <wtf/text/CString.h>
namespace WebCore {
-void AXObjectCache::detachWrapper(AccessibilityObject* obj)
+void AXObjectCache::detachWrapper(AccessibilityObject* obj, DetachmentType detachmentType)
{
- webkitAccessibleDetach(WEBKIT_ACCESSIBLE(obj->wrapper()));
+ AtkObject* wrapper = obj->wrapper();
+ ASSERT(wrapper);
+
+ // If an object is being detached NOT because of the AXObjectCache being destroyed,
+ // then it's being removed from the accessibility tree and we should emit a signal.
+ if (detachmentType != CacheDestroyed) {
+ if (obj->document()) {
+ // Look for the right object to emit the signal from, but using the implementation
+ // of atk_object_get_parent from AtkObject class (which uses a cached pointer if set)
+ // since the accessibility hierarchy in WebCore will no longer be navigable.
+ gpointer webkitAccessibleClass = g_type_class_peek_parent(WEBKIT_ACCESSIBLE_GET_CLASS(wrapper));
+ gpointer atkObjectClass = g_type_class_peek_parent(webkitAccessibleClass);
+ AtkObject* atkParent = ATK_OBJECT_CLASS(atkObjectClass)->get_parent(ATK_OBJECT(wrapper));
+
+ // We don't want to emit any signal from an object outside WebKit's world.
+ if (WEBKIT_IS_ACCESSIBLE(atkParent)) {
+ // The accessibility hierarchy is already invalid, so the parent-children relationships
+ // in the AccessibilityObject tree are not there anymore, so we can't know the offset.
+ g_signal_emit_by_name(atkParent, "children-changed::remove", -1, wrapper);
+ }
+ }
+ }
+
+ webkitAccessibleDetach(WEBKIT_ACCESSIBLE(wrapper));
}
void AXObjectCache::attachWrapper(AccessibilityObject* obj)
@@ -45,6 +69,25 @@
AtkObject* atkObj = ATK_OBJECT(webkitAccessibleNew(obj));
obj->setWrapper(atkObj);
g_object_unref(atkObj);
+
+ // If an object is being attached and we are not in the middle of a layout update, then
+ // we should report ATs by emitting the children-changed::add signal from the parent.
+ Document* document = obj->document();
+ if (!document || document->childNeedsStyleRecalc())
+ return;
+
+ // Don't emit the signal for objects that we already know won't be exposed directly.
+ AccessibilityObject* coreParent = obj->parentObjectUnignored();
+ if (!coreParent || coreParent->accessibilityIsIgnoredByDefault())
+ return;
+
+ // Look for the right object to emit the signal from.
+ AtkObject* atkParent = coreParent ? coreParent->wrapper() : 0;
+ if (!atkParent)
+ return;
+
+ size_t index = coreParent->children().find(obj);
+ g_signal_emit_by_name(atkParent, "children-changed::add", index, atkObj);
}
static AccessibilityObject* getListObject(AccessibilityObject* object)
@@ -143,29 +186,50 @@
if (!axObject)
return;
- if (notification == AXCheckedStateChanged) {
+ switch (notification) {
+ case AXCheckedStateChanged:
if (!coreObject->isCheckboxOrRadio())
return;
atk_object_notify_state_change(axObject, ATK_STATE_CHECKED, coreObject->isChecked());
- } else if (notification == AXSelectedChildrenChanged || notification == AXMenuListValueChanged) {
+ break;
+
+ case AXChildrenChanged:
+ // We need to make sure that the children AtkObjects are created at this moment,
+ // so the children-changed::add signal gets properly emitted in attachWrapper().
+ if (int numOfChildren = atk_object_get_n_accessible_children(axObject)) {
+ for (int i = 0; i < numOfChildren; ++i)
+ GRefPtr<AtkObject> child(atk_object_ref_accessible_child(axObject, i));
+ }
+ break;
+
+ case AXSelectedChildrenChanged:
+ case AXMenuListValueChanged:
if (notification == AXMenuListValueChanged && coreObject->isMenuList()) {
g_signal_emit_by_name(axObject, "focus-event", true);
atk_object_notify_state_change(axObject, ATK_STATE_FOCUSED, true);
}
notifyChildrenSelectionChange(coreObject);
- } else if (notification == AXValueChanged) {
- if (!ATK_IS_VALUE(axObject))
- return;
+ break;
- AtkPropertyValues propertyValues;
- propertyValues.property_name = "accessible-value";
+ case AXValueChanged:
+ if (ATK_IS_VALUE(axObject)) {
+ AtkPropertyValues propertyValues;
+ propertyValues.property_name = "accessible-value";
- memset(&propertyValues.new_value, 0, sizeof(GValue));
- atk_value_get_current_value(ATK_VALUE(axObject), &propertyValues.new_value);
+ memset(&propertyValues.new_value, 0, sizeof(GValue));
+ atk_value_get_current_value(ATK_VALUE(axObject), &propertyValues.new_value);
- g_signal_emit_by_name(ATK_OBJECT(axObject), "property-change::accessible-value", &propertyValues, NULL);
- } else if (notification == AXInvalidStatusChanged)
+ g_signal_emit_by_name(ATK_OBJECT(axObject), "property-change::accessible-value", &propertyValues, NULL);
+ }
+ break;
+
+ case AXInvalidStatusChanged:
atk_object_notify_state_change(axObject, ATK_STATE_INVALID_ENTRY, coreObject->invalidStatus() != "false");
+ break;
+
+ default:
+ break;
+ }
}
void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject* object, AXTextChange textChange, unsigned offset, const String& text)
Modified: trunk/Source/WebCore/accessibility/ios/AXObjectCacheIOS.mm (160416 => 160417)
--- trunk/Source/WebCore/accessibility/ios/AXObjectCacheIOS.mm 2013-12-11 10:00:59 UTC (rev 160416)
+++ trunk/Source/WebCore/accessibility/ios/AXObjectCacheIOS.mm 2013-12-11 10:28:21 UTC (rev 160417)
@@ -37,7 +37,7 @@
namespace WebCore {
-void AXObjectCache::detachWrapper(AccessibilityObject* obj)
+void AXObjectCache::detachWrapper(AccessibilityObject* obj, DetachmentType)
{
[obj->wrapper() detach];
obj->setWrapper(0);
Modified: trunk/Source/WebCore/accessibility/mac/AXObjectCacheMac.mm (160416 => 160417)
--- trunk/Source/WebCore/accessibility/mac/AXObjectCacheMac.mm 2013-12-11 10:00:59 UTC (rev 160416)
+++ trunk/Source/WebCore/accessibility/mac/AXObjectCacheMac.mm 2013-12-11 10:28:21 UTC (rev 160417)
@@ -43,7 +43,7 @@
namespace WebCore {
-void AXObjectCache::detachWrapper(AccessibilityObject* obj)
+void AXObjectCache::detachWrapper(AccessibilityObject* obj, DetachmentType)
{
[obj->wrapper() detach];
obj->setWrapper(0);
Modified: trunk/Source/WebCore/accessibility/win/AXObjectCacheWin.cpp (160416 => 160417)
--- trunk/Source/WebCore/accessibility/win/AXObjectCacheWin.cpp 2013-12-11 10:00:59 UTC (rev 160416)
+++ trunk/Source/WebCore/accessibility/win/AXObjectCacheWin.cpp 2013-12-11 10:28:21 UTC (rev 160417)
@@ -42,7 +42,7 @@
namespace WebCore {
-void AXObjectCache::detachWrapper(AccessibilityObject* obj)
+void AXObjectCache::detachWrapper(AccessibilityObject* obj, DetachmentType)
{
// On Windows, AccessibilityObjects are created when get_accChildCount is
// called, but they are not wrapped until get_accChild is called, so this
Modified: trunk/Tools/ChangeLog (160416 => 160417)
--- trunk/Tools/ChangeLog 2013-12-11 10:00:59 UTC (rev 160416)
+++ trunk/Tools/ChangeLog 2013-12-11 10:28:21 UTC (rev 160417)
@@ -1,3 +1,17 @@
+2013-12-11 Mario Sanchez Prada <mario.pr...@samsung.com>
+
+ Programmatically-inserted children lack accessibility events
+ https://bugs.webkit.org/show_bug.cgi?id=100275
+
+ Reviewed by Chris Fleizach.
+
+ Update DRT and WebKitTestRunner to handle the children-changed
+ signal properly, considering the detail and optional parameters.
+
+ * DumpRenderTree/atk/AccessibilityCallbacksAtk.cpp: Updated.
+ (axObjectEventListener):
+ * WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.cpp: Updated.
+
2013-12-03 Mark Rowe <mr...@apple.com>
<https://webkit.org/b/125139> Modernize the WebKit API headers
Modified: trunk/Tools/DumpRenderTree/atk/AccessibilityCallbacksAtk.cpp (160416 => 160417)
--- trunk/Tools/DumpRenderTree/atk/AccessibilityCallbacksAtk.cpp 2013-12-11 10:00:59 UTC (rev 160416)
+++ trunk/Tools/DumpRenderTree/atk/AccessibilityCallbacksAtk.cpp 2013-12-11 10:28:21 UTC (rev 160417)
@@ -113,8 +113,10 @@
if (g_value_get_boolean(¶mValues[1]))
notificationName = "AXFocusedUIElementChanged";
} else if (!g_strcmp0(signalQuery.signal_name, "children-changed")) {
- signalName.set(g_strdup("children-changed"));
+ const gchar* childrenChangedDetail = g_quark_to_string(signalHint->detail);
+ signalName.set(g_strdup_printf("children-changed:%s", childrenChangedDetail));
signalValue.set(g_strdup_printf("%d", g_value_get_uint(¶mValues[1])));
+ notificationName = !g_strcmp0(childrenChangedDetail, "add") ? "AXChildrenAdded" : "AXChildrenRemoved";
} else if (!g_strcmp0(signalQuery.signal_name, "property-change")) {
signalName.set(g_strdup_printf("property-change:%s", g_quark_to_string(signalHint->detail)));
if (!g_strcmp0(g_quark_to_string(signalHint->detail), "accessible-value"))
Modified: trunk/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.cpp (160416 => 160417)
--- trunk/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.cpp 2013-12-11 10:00:59 UTC (rev 160416)
+++ trunk/Tools/WebKitTestRunner/InjectedBundle/atk/AccessibilityNotificationHandlerAtk.cpp 2013-12-11 10:28:21 UTC (rev 160417)
@@ -102,8 +102,10 @@
if (g_value_get_boolean(¶mValues[1]))
notificationName = "AXFocusedUIElementChanged";
} else if (!g_strcmp0(signalQuery.signal_name, "children-changed")) {
- signalName.set(g_strdup("children-changed"));
+ const gchar* childrenChangedDetail = g_quark_to_string(signalHint->detail);
+ signalName.set(g_strdup_printf("children-changed:%s", childrenChangedDetail));
signalValue.set(g_strdup_printf("%d", g_value_get_uint(¶mValues[1])));
+ notificationName = !g_strcmp0(childrenChangedDetail, "add") ? "AXChildrenAdded" : "AXChildrenRemoved";
} else if (!g_strcmp0(signalQuery.signal_name, "property-change")) {
signalName.set(g_strdup_printf("property-change:%s", g_quark_to_string(signalHint->detail)));
if (!g_strcmp0(g_quark_to_string(signalHint->detail), "accessible-value"))