AX: Notification should be sent when accessibilityIsIgnored changes
authordmazzoni@google.com <dmazzoni@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 25 Oct 2012 19:31:25 +0000 (19:31 +0000)
committerdmazzoni@google.com <dmazzoni@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 25 Oct 2012 19:31:25 +0000 (19:31 +0000)
https://bugs.webkit.org/show_bug.cgi?id=99547

Reviewed by Chris Fleizach.

Source/WebCore:

Adds a new flag in AccessibilityObject that keeps track of the most recent
value of accessibilityIsIgnored(). After certain events such as an ARIA
attribute change or content change, checks the new value of
accessibilityIsIgnored() and posts a "children changed" notification on the
parent node if it changed, making sure the parent recomputes its vector of
(unignored) children.

Also moves handling of attribute changes to AXObjectCache, and sends
notifications for some attribute changes that were previously silent. On
Chromium, all changes to an accessibility object's attributes should
result in some notification.

Some tests would have broken because an AccessibilityScrollView was created
and holding a reference to a ScrollView for an iframe after it was deleted,
so this change switches AccessibilityScrollView to hold a weak reference
to ScrollView instead.

Tests: platform/chromium/accessibility/is-ignored-change-sends-notification.html
       platform/chromium/accessibility/other-aria-attribute-change-sends-notification.html
       platform/chromium/accessibility/text-change-notification.html

* accessibility/AXObjectCache.cpp:
(WebCore::AXObjectCache::focusedUIElementForPage):
(WebCore::AXObjectCache::getOrCreate):
(WebCore::AXObjectCache::textChanged):
(WebCore):
(WebCore::AXObjectCache::childrenChanged):
(WebCore::AXObjectCache::handleAriaRoleChanged):
(WebCore::AXObjectCache::handleAttributeChanged):
(WebCore::AXObjectCache::labelChanged):
(WebCore::AXObjectCache::recomputeIsIgnored):
* accessibility/AXObjectCache.h:
(AXObjectCache):
(WebCore::AXObjectCache::childrenChanged):
(WebCore::AXObjectCache::textChanged):
(WebCore::AXObjectCache::handleAttributeChanged):
(WebCore::AXObjectCache::recomputeIsIgnored):
* accessibility/AccessibilityNodeObject.cpp:
(WebCore::AccessibilityNodeObject::insertChild):
* accessibility/AccessibilityObject.cpp:
(WebCore::AccessibilityObject::AccessibilityObject):
(WebCore::AccessibilityObject::cachedIsIgnoredValue):
(WebCore):
(WebCore::AccessibilityObject::setCachedIsIgnoredValue):
(WebCore::AccessibilityObject::notifyIfIgnoredValueChanged):
* accessibility/AccessibilityObject.h:
(WebCore::AccessibilityObject::textChanged):
(AccessibilityObject):
* accessibility/AccessibilityRenderObject.cpp:
(WebCore::AccessibilityRenderObject::accessibilityIsIgnored):
(WebCore::AccessibilityRenderObject::correspondingControlForLabelElement):
(WebCore::AccessibilityRenderObject::textChanged):
(WebCore::AccessibilityRenderObject::addHiddenChildren):
(WebCore::AccessibilityRenderObject::addChildren):
* accessibility/AccessibilityRenderObject.h:
(AccessibilityRenderObject):
* accessibility/AccessibilityScrollView.cpp:
(WebCore::AccessibilityScrollView::~AccessibilityScrollView):
(WebCore):
(WebCore::AccessibilityScrollView::detach):
(WebCore::AccessibilityScrollView::isAttachment):
(WebCore::AccessibilityScrollView::widgetForAttachmentView):
(WebCore::AccessibilityScrollView::updateScrollbars):
(WebCore::AccessibilityScrollView::webAreaObject):
(WebCore::AccessibilityScrollView::elementRect):
(WebCore::AccessibilityScrollView::documentFrameView):
(WebCore::AccessibilityScrollView::parentObject):
(WebCore::AccessibilityScrollView::parentObjectIfExists):
(WebCore::AccessibilityScrollView::getScrollableAreaIfScrollable):
(WebCore::AccessibilityScrollView::scrollTo):
* accessibility/AccessibilityScrollView.h:
(WebCore::AccessibilityScrollView::scrollView):
(AccessibilityScrollView):
* accessibility/AccessibilityTable.cpp:
(WebCore::AccessibilityTable::isDataTable):
* accessibility/chromium/AXObjectCacheChromium.cpp:
(WebCore::AXObjectCache::postPlatformNotification):
* dom/Element.cpp:
(WebCore::Element::attributeChanged):
* rendering/RenderBlock.cpp:
(WebCore::RenderBlock::deleteLineBoxTree):
(WebCore::RenderBlock::createAndAppendRootInlineBox):
* rendering/RenderObject.cpp:
(WebCore::RenderObject::styleWillChange):
* rendering/RenderText.cpp:
(WebCore::RenderText::setText):

Source/WebKit/chromium:

Adds new accessibility notifications.

* public/WebAccessibilityNotification.h:
* src/AssertMatchingEnums.cpp:

Tools:

Add additional accessibility notifications.

* DumpRenderTree/chromium/WebViewHost.cpp:
(WebViewHost::postAccessibilityNotification):

LayoutTests:

Adds 3 new test to ensure:
1. A "children changed" notification is fired on the parent object when an
   object that was previously ignored becomes unignored.
2. A notification is sent when an element's text (incl. title or label) changes.
3. A notification is sent when another ARIA attribute changes.

Modifies add-to-menu-list-crashes because it was too brittle; it was
    referencing a stale object rather than retrieving its latest handle.
Modifies aria-checkbox-sends-notification to listen on the correct
    object on all platforms.
Simplifies notification-listeners so it doesn't generate additional
    notifications that are inconsistent between platforms now.

* accessibility/aria-checkbox-sends-notification.html:
* accessibility/notification-listeners.html:
* platform/chromium/accessibility/add-to-menu-list-crashes-expected.txt:
* platform/chromium/accessibility/add-to-menu-list-crashes.html:
* platform/chromium/accessibility/is-ignored-change-sends-notification-expected.txt: Added.
* platform/chromium/accessibility/is-ignored-change-sends-notification.html: Added.
* platform/chromium/accessibility/other-aria-attribute-change-sends-notification-expected.txt: Added.
* platform/chromium/accessibility/other-aria-attribute-change-sends-notification.html: Added.
* platform/chromium/accessibility/text-change-notification-expected.txt: Added.
* platform/chromium/accessibility/text-change-notification.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@132514 268f45cc-cd09-0410-ab3c-d52691b4dbfc

33 files changed:
LayoutTests/ChangeLog
LayoutTests/accessibility/aria-checkbox-sends-notification.html
LayoutTests/accessibility/notification-listeners.html
LayoutTests/fast/forms/date-multiple-fields/date-multiple-fields-ax-value-changed-notification-expected.txt
LayoutTests/fast/forms/time-multiple-fields/time-multiple-fields-ax-value-changed-notification-expected.txt
LayoutTests/platform/chromium/accessibility/add-to-menu-list-crashes-expected.txt
LayoutTests/platform/chromium/accessibility/add-to-menu-list-crashes.html
LayoutTests/platform/chromium/accessibility/is-ignored-change-sends-notification-expected.txt [new file with mode: 0644]
LayoutTests/platform/chromium/accessibility/is-ignored-change-sends-notification.html [new file with mode: 0644]
LayoutTests/platform/chromium/accessibility/other-aria-attribute-change-sends-notification-expected.txt [new file with mode: 0644]
LayoutTests/platform/chromium/accessibility/other-aria-attribute-change-sends-notification.html [new file with mode: 0644]
LayoutTests/platform/chromium/accessibility/text-change-notification-expected.txt [new file with mode: 0644]
LayoutTests/platform/chromium/accessibility/text-change-notification.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/accessibility/AXObjectCache.cpp
Source/WebCore/accessibility/AXObjectCache.h
Source/WebCore/accessibility/AccessibilityObject.cpp
Source/WebCore/accessibility/AccessibilityObject.h
Source/WebCore/accessibility/AccessibilityRenderObject.cpp
Source/WebCore/accessibility/AccessibilityRenderObject.h
Source/WebCore/accessibility/AccessibilityScrollView.cpp
Source/WebCore/accessibility/AccessibilityScrollView.h
Source/WebCore/accessibility/AccessibilityTable.cpp
Source/WebCore/accessibility/chromium/AXObjectCacheChromium.cpp
Source/WebCore/dom/Element.cpp
Source/WebCore/rendering/RenderBlock.cpp
Source/WebCore/rendering/RenderObject.cpp
Source/WebCore/rendering/RenderText.cpp
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/public/WebAccessibilityNotification.h
Source/WebKit/chromium/src/AssertMatchingEnums.cpp
Tools/ChangeLog
Tools/DumpRenderTree/chromium/WebViewHost.cpp

index 6fa6db6..23f1f1c 100644 (file)
@@ -1,3 +1,34 @@
+2012-10-25  Dominic Mazzoni  <dmazzoni@google.com>
+
+        AX: Notification should be sent when accessibilityIsIgnored changes
+        https://bugs.webkit.org/show_bug.cgi?id=99547
+
+        Reviewed by Chris Fleizach.
+
+        Adds 3 new test to ensure:
+        1. A "children changed" notification is fired on the parent object when an
+           object that was previously ignored becomes unignored.
+        2. A notification is sent when an element's text (incl. title or label) changes.
+        3. A notification is sent when another ARIA attribute changes.
+
+        Modifies add-to-menu-list-crashes because it was too brittle; it was
+            referencing a stale object rather than retrieving its latest handle.
+        Modifies aria-checkbox-sends-notification to listen on the correct
+            object on all platforms.
+        Simplifies notification-listeners so it doesn't generate additional
+            notifications that are inconsistent between platforms now.
+
+        * accessibility/aria-checkbox-sends-notification.html:
+        * accessibility/notification-listeners.html:
+        * platform/chromium/accessibility/add-to-menu-list-crashes-expected.txt:
+        * platform/chromium/accessibility/add-to-menu-list-crashes.html:
+        * platform/chromium/accessibility/is-ignored-change-sends-notification-expected.txt: Added.
+        * platform/chromium/accessibility/is-ignored-change-sends-notification.html: Added.
+        * platform/chromium/accessibility/other-aria-attribute-change-sends-notification-expected.txt: Added.
+        * platform/chromium/accessibility/other-aria-attribute-change-sends-notification.html: Added.
+        * platform/chromium/accessibility/text-change-notification-expected.txt: Added.
+        * platform/chromium/accessibility/text-change-notification.html: Added.
+
 2012-10-25  Tom Sepez  <tsepez@chromium.org>
 
         XSSAuditor must replace form action with about:blank when reflected action detected.
index 4f6c60e..f799112 100644 (file)
@@ -7,15 +7,13 @@
             testRunner.waitUntilDone();
 
         description("This tests that checking of an aria checkbox sends a notification.");
-        window.root = accessibilityController.rootElement;
-        window.body = root.childAtIndex(0);
 
-        var accessibleCheckbox = body.childAtIndex(0);
+        var accessibleCheckbox = accessibilityController.accessibleElementById("checkbox1");
         var notificationCount = 0;
 
         function listener(notification) {
-        if (notification == "CheckedStateChanged")
-            notificationCount++;
+            if (notification == "CheckedStateChanged")
+                notificationCount++;
 
             document.getElementById("console").innerText += "Got notification: " + notification + "\n";
 
index 3dd8888..626c5eb 100644 (file)
@@ -7,7 +7,7 @@
 
 <p id="description"></p>
 
-<select id="select" value="Select"><option>A<option>B</select>
+<select id="select" value="Select"></select>
 
 <div id="slider" tabindex="0" role="slider" aria-valuenow="5">Slider</div>
 
index a06b3c4..d4fc8ae 100644 (file)
@@ -7,6 +7,8 @@ FocusedUIElementChanged AXHelp: Month=AXValueDescription: 10
 FocusedUIElementChanged AXHelp: Day=AXValueDescription: 09
 FocusedUIElementChanged AXHelp: Year=AXValueDescription: 2012
 ValueChanged AXHelp: Day=AXValueDescription: 04
+ValueChanged AXHelp: Day=AXValueDescription: 04
+ValueChanged AXHelp: Year=AXValueDescription: 2013
 ValueChanged AXHelp: Year=AXValueDescription: 2013
 
 PASS successfullyParsed is true
index 9e6e6d2..2e5ed9b 100644 (file)
@@ -7,6 +7,8 @@ FocusedUIElementChanged AXHelp: Hours=AXValueDescription: 12
 FocusedUIElementChanged AXHelp: Minutes=AXValueDescription: 34
 ValueChanged AXHelp: Minutes=AXValueDescription: 05
 ValueChanged AXHelp: Minutes=AXValueDescription: 05
+ValueChanged AXHelp: Minutes=AXValueDescription: 05
+ValueChanged AXHelp: Minutes=AXValueDescription: 05
 
 PASS successfullyParsed is true
 
index 89fa424..c962ea3 100644 (file)
@@ -1,8 +1,8 @@
 This test makes sure that adding a selected option to a menu list via an unusual route (document.write from an external script) doesn't trigger a crash when didUpdateActiveOption is called before the children are updated.
 
-PASS accessiblePopup.childrenCount is 1
-PASS accessiblePopup.childrenCount is 2
-PASS accessiblePopup.childrenCount is 1
+PASS accessibleMenulist.childAtIndex(0).childrenCount is 1
+PASS accessibleMenulist.childAtIndex(0).childrenCount is 2
+PASS accessibleMenulist.childAtIndex(0).childrenCount is 1
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 7510338..aecb4e5 100644 (file)
         menulist.focus();
         if (window.testRunner && window.accessibilityController) {
             window.accessibleMenulist = accessibilityController.focusedElement;
-            window.accessiblePopup = accessibleMenulist.childAtIndex(0);
-            shouldBe("accessiblePopup.childrenCount", "1");
+            shouldBe("accessibleMenulist.childAtIndex(0).childrenCount", "1");
         }
     </script>
     <script src="data:text/javascript,document.write('<option selected>2');"></script>
     <script>
         if (window.testRunner && window.accessibilityController)
-            shouldBe("accessiblePopup.childrenCount", "2");
+            shouldBe("accessibleMenulist.childAtIndex(0).childrenCount", "2");
     </script>
     <script>
         menulist.removeChild(menulist.selectedOptions[0]);
         if (window.testRunner && window.accessibilityController)
-            shouldBe("accessiblePopup.childrenCount", "1");
+            shouldBe("accessibleMenulist.childAtIndex(0).childrenCount", "1");
     </script>
 </select>
 
diff --git a/LayoutTests/platform/chromium/accessibility/is-ignored-change-sends-notification-expected.txt b/LayoutTests/platform/chromium/accessibility/is-ignored-change-sends-notification-expected.txt
new file mode 100644 (file)
index 0000000..0dbdc6d
--- /dev/null
@@ -0,0 +1,31 @@
+This test ensures that a change to accessibilityIsIgnored fires a children changed notification on the parent.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS accessibleElementById('hiddenDivContainer') != null is true
+PASS accessibleElementById('hiddenDiv') != null is false
+PASS accessibleElementById('invisibleDivContainer') != null is true
+PASS accessibleElementById('invisibleDiv') != null is false
+PASS accessibleElementById('emptyDivContainer') != null is true
+PASS accessibleElementById('emptyDiv') != null is false
+PASS accessibleElementById('divWithoutRoleContainer') != null is true
+PASS accessibleElementById('divWithoutRole') != null is false
+PASS accessibleElementById('divWithoutLabelContainer') != null is true
+PASS accessibleElementById('divWithoutLabel') != null is false
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+Got ChildrenChanged notification on hiddenDivContainer
+PASS accessibleElementById('hiddenDivContainer').childrenCount is 1
+Got ChildrenChanged notification on invisibleDivContainer
+PASS accessibleElementById('invisibleDivContainer').childrenCount is 1
+Got ChildrenChanged notification on emptyDivContainer
+PASS accessibleElementById('emptyDivContainer').childrenCount is 1
+Got ChildrenChanged notification on divWithoutRoleContainer
+PASS accessibleElementById('divWithoutRoleContainer').childrenCount is 1
+Got ChildrenChanged notification on divWithoutLabelContainer
+PASS accessibleElementById('divWithoutLabelContainer').childrenCount is 1
+All notifications received successfully.
+
diff --git a/LayoutTests/platform/chromium/accessibility/is-ignored-change-sends-notification.html b/LayoutTests/platform/chromium/accessibility/is-ignored-change-sends-notification.html
new file mode 100644 (file)
index 0000000..6065970
--- /dev/null
@@ -0,0 +1,131 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script src="../../../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body>
+
+<div id="container">
+
+  <div id="hiddenDivContainer" aria-label="hiddenDivContainer">
+    <div id="hiddenDiv" hidden>
+      <div>
+        <button>Button</button>
+      </div>
+    </div>
+  </div>
+
+  <div id="invisibleDivContainer" aria-label="invisibleDivContainer">
+    <div id="invisibleDiv" style="visibility: hidden">
+      <div>
+        <button>Button</button>
+      </div>
+    </div>
+  </div>
+
+  <div id="emptyDivContainer" aria-label="emptyDivContainer">
+    <div id="emptyDiv"></div>
+  </div>
+
+  <div id="divWithoutRoleContainer" aria-label="divWithoutRoleContainer">
+    <div id="divWithoutRole">
+      <div>
+        <button>Button</button>
+      </div>
+    </div>
+  </div>
+
+  <div id="divWithoutLabelContainer" aria-label="divWithoutLabelContainer">
+    <div id="divWithoutLabel">
+      <div>
+        <button>Button</button>
+      </div>
+    </div>
+  </div>
+
+</div>
+
+<div id="console"></div>
+<script>
+description("This test ensures that a change to accessibilityIsIgnored fires a children changed notification on the parent.");
+
+if (window.testRunner && window.accessibilityController) {
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+
+    function accessibleElementById(id) {
+        return accessibilityController.accessibleElementById(id);
+    }
+
+    window.successCount = 0;
+    function gotSuccessfulNotification() {
+        successCount++;
+        if (successCount != 5)
+            return;
+
+        debug('All notifications received successfully.');
+        accessibleElementById('hiddenDivContainer').removeNotificationListener();
+        accessibleElementById('invisibleDivContainer').removeNotificationListener();
+        accessibleElementById('emptyDivContainer').removeNotificationListener();
+        accessibleElementById('divWithoutRoleContainer').removeNotificationListener();
+        accessibleElementById('divWithoutLabelContainer').removeNotificationListener();
+        document.getElementById('container').hidden = true;
+        if (window.testRunner)
+            testRunner.notifyDone();
+    }
+
+    shouldBeTrue("accessibleElementById('hiddenDivContainer') != null");
+    shouldBeFalse("accessibleElementById('hiddenDiv') != null");
+    accessibleElementById('hiddenDivContainer').addNotificationListener(function(notification) {
+        debug('Got ' + notification + ' notification on hiddenDivContainer');
+        shouldBe("accessibleElementById('hiddenDivContainer').childrenCount", "1");
+        gotSuccessfulNotification();
+    });
+    document.getElementById('hiddenDiv').hidden = false;
+
+    shouldBeTrue("accessibleElementById('invisibleDivContainer') != null");
+    shouldBeFalse("accessibleElementById('invisibleDiv') != null");
+    accessibleElementById('invisibleDivContainer').addNotificationListener(function(notification) {
+        debug('Got ' + notification + ' notification on invisibleDivContainer');
+        shouldBe("accessibleElementById('invisibleDivContainer').childrenCount", "1");
+        gotSuccessfulNotification();
+    });
+    document.getElementById('invisibleDiv').style.visibility = 'visible';
+
+    shouldBeTrue("accessibleElementById('emptyDivContainer') != null");
+    shouldBeFalse("accessibleElementById('emptyDiv') != null");
+    accessibleElementById('emptyDivContainer').addNotificationListener(function(notification) {
+        debug('Got ' + notification + ' notification on emptyDivContainer');
+        shouldBe("accessibleElementById('emptyDivContainer').childrenCount", "1");
+        gotSuccessfulNotification();
+    });
+
+    document.getElementById('emptyDiv').innerText = 'Not empty anymore.';
+    document.getElementById('emptyDiv').offsetLeft;
+
+    shouldBeTrue("accessibleElementById('divWithoutRoleContainer') != null");
+    shouldBeFalse("accessibleElementById('divWithoutRole') != null");
+    accessibleElementById('divWithoutRoleContainer').addNotificationListener(function(notification) {
+        debug('Got ' + notification + ' notification on divWithoutRoleContainer');
+        shouldBe("accessibleElementById('divWithoutRoleContainer').childrenCount", "1");
+        gotSuccessfulNotification();
+    });
+    document.getElementById('divWithoutRole').setAttribute('role', 'heading');
+
+    shouldBeTrue("accessibleElementById('divWithoutLabelContainer') != null");
+    shouldBeFalse("accessibleElementById('divWithoutLabel') != null");
+    accessibleElementById('divWithoutLabelContainer').addNotificationListener(function(notification) {
+        debug('Got ' + notification + ' notification on divWithoutLabelContainer');
+        shouldBe("accessibleElementById('divWithoutLabelContainer').childrenCount", "1");
+        gotSuccessfulNotification();
+    });
+    document.getElementById('divWithoutLabel').setAttribute('aria-label', 'Label');
+
+    debug('');
+}
+
+</script>
+
+<script src="../../../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/platform/chromium/accessibility/other-aria-attribute-change-sends-notification-expected.txt b/LayoutTests/platform/chromium/accessibility/other-aria-attribute-change-sends-notification-expected.txt
new file mode 100644 (file)
index 0000000..be707e6
--- /dev/null
@@ -0,0 +1,15 @@
+This test ensures that a change to any ARIA attribute, not just a state, sends a notification.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+Got AriaAttributeChanged notification on aria-busy
+Got AriaAttributeChanged notification on aria-disabled
+Got AriaAttributeChanged notification on aria-readonly
+Got AriaAttributeChanged notification on aria-required
+All notifications received successfully.
+
diff --git a/LayoutTests/platform/chromium/accessibility/other-aria-attribute-change-sends-notification.html b/LayoutTests/platform/chromium/accessibility/other-aria-attribute-change-sends-notification.html
new file mode 100644 (file)
index 0000000..a1b8017
--- /dev/null
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script src="../../../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body>
+
+<div id="container">
+  <button id="aria-busy" aria-busy="false">Busy</button>
+  <button id="aria-disabled" aria-disabled="false">Disabled</button>
+  <button id="aria-readonly" aria-readonly="false">Readonly</button>
+  <button id="aria-required" aria-required="false">Required</button>
+</div>
+
+<div id="console"></div>
+<script>
+description("This test ensures that a change to any ARIA attribute, not just a state, sends a notification.");
+
+if (window.testRunner && window.accessibilityController) {
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+
+    function accessibleElementById(id) {
+        return accessibilityController.accessibleElementById(id);
+    }
+
+    window.successCount = 0;
+    function gotSuccessfulNotification() {
+        successCount++;
+        if (successCount != 4)
+            return;
+
+        debug('All notifications received successfully.');
+        accessibleElementById('aria-busy').removeNotificationListener();
+        accessibleElementById('aria-disabled').removeNotificationListener();
+        accessibleElementById('aria-readonly').removeNotificationListener();
+        accessibleElementById('aria-required').removeNotificationListener();
+
+        document.getElementById('container').hidden = true;
+        if (window.testRunner)
+            testRunner.notifyDone();
+    }
+
+    accessibleElementById('aria-busy').addNotificationListener(function(notification) {
+        debug('Got ' + notification + ' notification on aria-busy');
+        gotSuccessfulNotification();
+    });
+    document.getElementById('aria-busy').setAttribute('aria-busy', 'true');
+
+    accessibleElementById('aria-disabled').addNotificationListener(function(notification) {
+        debug('Got ' + notification + ' notification on aria-disabled');
+        gotSuccessfulNotification();
+    });
+    document.getElementById('aria-disabled').setAttribute('aria-disabled', 'true');
+
+    accessibleElementById('aria-readonly').addNotificationListener(function(notification) {
+        debug('Got ' + notification + ' notification on aria-readonly');
+        gotSuccessfulNotification();
+    });
+    document.getElementById('aria-readonly').setAttribute('aria-readonly', 'true');
+
+    accessibleElementById('aria-required').addNotificationListener(function(notification) {
+        debug('Got ' + notification + ' notification on aria-required');
+        gotSuccessfulNotification();
+    });
+    document.getElementById('aria-required').setAttribute('aria-required', 'true');
+
+    debug('');
+}
+
+</script>
+
+<script src="../../../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/platform/chromium/accessibility/text-change-notification-expected.txt b/LayoutTests/platform/chromium/accessibility/text-change-notification-expected.txt
new file mode 100644 (file)
index 0000000..bbba971
--- /dev/null
@@ -0,0 +1,15 @@
+This test ensures that a change to an element's accessible text, even if indirect, sends a notification.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+Got TextChanged notification on aria-label
+Got TextChanged notification on title
+Got TextChanged notification on labelledby
+Got TextChanged notification on labeled
+All notifications received successfully.
+
diff --git a/LayoutTests/platform/chromium/accessibility/text-change-notification.html b/LayoutTests/platform/chromium/accessibility/text-change-notification.html
new file mode 100644 (file)
index 0000000..6d5db0a
--- /dev/null
@@ -0,0 +1,78 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script src="../../../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body>
+
+<div id="container">
+  <button id="aria-label" aria-label="Aria Label">Content</button>
+  <button id="title" title="Title">Content</button>
+  <button id="labelledby" aria-labelledby="label1">Content</button>
+  <button id="labeled">Content</button>
+
+  <label id="label1">Label1</label>
+  <label id="label2">Label2</label>
+</div>
+
+<div id="console"></div>
+<script>
+description("This test ensures that a change to an element's accessible text, even if indirect, sends a notification.");
+
+if (window.testRunner && window.accessibilityController) {
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+
+    function accessibleElementById(id) {
+        return accessibilityController.accessibleElementById(id);
+    }
+
+    window.successCount = 0;
+    function gotSuccessfulNotification() {
+        successCount++;
+        if (successCount != 4)
+            return;
+
+        debug('All notifications received successfully.');
+        accessibleElementById('aria-label').removeNotificationListener();
+        accessibleElementById('title').removeNotificationListener();
+        accessibleElementById('labelledby').removeNotificationListener();
+        accessibleElementById('labeled').removeNotificationListener();
+
+        document.getElementById('container').hidden = true;
+        if (window.testRunner)
+            testRunner.notifyDone();
+    }
+
+    accessibleElementById('aria-label').addNotificationListener(function(notification) {
+        debug('Got ' + notification + ' notification on aria-label');
+        gotSuccessfulNotification();
+    });
+    document.getElementById('aria-label').setAttribute('aria-label', 'New aria-label');
+
+    accessibleElementById('title').addNotificationListener(function(notification) {
+        debug('Got ' + notification + ' notification on title');
+        gotSuccessfulNotification();
+    });
+    document.getElementById('title').title = 'New title';
+
+    accessibleElementById('labelledby').addNotificationListener(function(notification) {
+        debug('Got ' + notification + ' notification on labelledby');
+        gotSuccessfulNotification();
+    });
+    document.getElementById('labelledby').setAttribute('aria-labelledby', 'label2');
+
+    accessibleElementById('labeled').addNotificationListener(function(notification) {
+        debug('Got ' + notification + ' notification on labeled');
+        gotSuccessfulNotification();
+    });
+    document.getElementById('label1').setAttribute('for', 'labeled');
+
+    debug('');
+}
+
+</script>
+
+<script src="../../../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
index 2e72306..564c418 100644 (file)
@@ -1,3 +1,97 @@
+2012-10-25  Dominic Mazzoni  <dmazzoni@google.com>
+
+        AX: Notification should be sent when accessibilityIsIgnored changes
+        https://bugs.webkit.org/show_bug.cgi?id=99547
+
+        Reviewed by Chris Fleizach.
+
+        Adds a new flag in AccessibilityObject that keeps track of the most recent
+        value of accessibilityIsIgnored(). After certain events such as an ARIA
+        attribute change or content change, checks the new value of
+        accessibilityIsIgnored() and posts a "children changed" notification on the
+        parent node if it changed, making sure the parent recomputes its vector of
+        (unignored) children.
+
+        Also moves handling of attribute changes to AXObjectCache, and sends
+        notifications for some attribute changes that were previously silent. On
+        Chromium, all changes to an accessibility object's attributes should
+        result in some notification.
+
+        Some tests would have broken because an AccessibilityScrollView was created
+        and holding a reference to a ScrollView for an iframe after it was deleted,
+        so this change switches AccessibilityScrollView to hold a weak reference
+        to ScrollView instead.
+
+        Tests: platform/chromium/accessibility/is-ignored-change-sends-notification.html
+               platform/chromium/accessibility/other-aria-attribute-change-sends-notification.html
+               platform/chromium/accessibility/text-change-notification.html
+
+        * accessibility/AXObjectCache.cpp:
+        (WebCore::AXObjectCache::focusedUIElementForPage):
+        (WebCore::AXObjectCache::getOrCreate):
+        (WebCore::AXObjectCache::textChanged):
+        (WebCore):
+        (WebCore::AXObjectCache::childrenChanged):
+        (WebCore::AXObjectCache::handleAriaRoleChanged):
+        (WebCore::AXObjectCache::handleAttributeChanged):
+        (WebCore::AXObjectCache::labelChanged):
+        (WebCore::AXObjectCache::recomputeIsIgnored):
+        * accessibility/AXObjectCache.h:
+        (AXObjectCache):
+        (WebCore::AXObjectCache::childrenChanged):
+        (WebCore::AXObjectCache::textChanged):
+        (WebCore::AXObjectCache::handleAttributeChanged):
+        (WebCore::AXObjectCache::recomputeIsIgnored):
+        * accessibility/AccessibilityNodeObject.cpp:
+        (WebCore::AccessibilityNodeObject::insertChild):
+        * accessibility/AccessibilityObject.cpp:
+        (WebCore::AccessibilityObject::AccessibilityObject):
+        (WebCore::AccessibilityObject::cachedIsIgnoredValue):
+        (WebCore):
+        (WebCore::AccessibilityObject::setCachedIsIgnoredValue):
+        (WebCore::AccessibilityObject::notifyIfIgnoredValueChanged):
+        * accessibility/AccessibilityObject.h:
+        (WebCore::AccessibilityObject::textChanged):
+        (AccessibilityObject):
+        * accessibility/AccessibilityRenderObject.cpp:
+        (WebCore::AccessibilityRenderObject::accessibilityIsIgnored):
+        (WebCore::AccessibilityRenderObject::correspondingControlForLabelElement):
+        (WebCore::AccessibilityRenderObject::textChanged):
+        (WebCore::AccessibilityRenderObject::addHiddenChildren):
+        (WebCore::AccessibilityRenderObject::addChildren):
+        * accessibility/AccessibilityRenderObject.h:
+        (AccessibilityRenderObject):
+        * accessibility/AccessibilityScrollView.cpp:
+        (WebCore::AccessibilityScrollView::~AccessibilityScrollView):
+        (WebCore):
+        (WebCore::AccessibilityScrollView::detach):
+        (WebCore::AccessibilityScrollView::isAttachment):
+        (WebCore::AccessibilityScrollView::widgetForAttachmentView):
+        (WebCore::AccessibilityScrollView::updateScrollbars):
+        (WebCore::AccessibilityScrollView::webAreaObject):
+        (WebCore::AccessibilityScrollView::elementRect):
+        (WebCore::AccessibilityScrollView::documentFrameView):
+        (WebCore::AccessibilityScrollView::parentObject):
+        (WebCore::AccessibilityScrollView::parentObjectIfExists):
+        (WebCore::AccessibilityScrollView::getScrollableAreaIfScrollable):
+        (WebCore::AccessibilityScrollView::scrollTo):
+        * accessibility/AccessibilityScrollView.h:
+        (WebCore::AccessibilityScrollView::scrollView):
+        (AccessibilityScrollView):
+        * accessibility/AccessibilityTable.cpp:
+        (WebCore::AccessibilityTable::isDataTable):
+        * accessibility/chromium/AXObjectCacheChromium.cpp:
+        (WebCore::AXObjectCache::postPlatformNotification):
+        * dom/Element.cpp:
+        (WebCore::Element::attributeChanged):
+        * rendering/RenderBlock.cpp:
+        (WebCore::RenderBlock::deleteLineBoxTree):
+        (WebCore::RenderBlock::createAndAppendRootInlineBox):
+        * rendering/RenderObject.cpp:
+        (WebCore::RenderObject::styleWillChange):
+        * rendering/RenderText.cpp:
+        (WebCore::RenderText::setText):
+
 2012-10-25  Dominik Röttsches  <dominik.rottsches@intel.com>
 
         Conditionalize XHR timeout support
index 665abc0..ceac94b 100644 (file)
@@ -60,6 +60,7 @@
 #include "HTMLAreaElement.h"
 #include "HTMLImageElement.h"
 #include "HTMLInputElement.h"
+#include "HTMLLabelElement.h"
 #include "HTMLNames.h"
 #if ENABLE(VIDEO)
 #include "MediaControlElements.h"
@@ -333,6 +334,9 @@ AccessibilityObject* AXObjectCache::getOrCreate(Node* node)
     m_nodeObjectMapping.set(node, newObj->axObjectID());
     m_objects.set(newObj->axObjectID(), newObj);
     attachWrapper(newObj.get());
+
+    newObj->setCachedIsIgnoredValue(newObj->accessibilityIsIgnored());
+
     return newObj.get();
 }
 
@@ -351,6 +355,9 @@ AccessibilityObject* AXObjectCache::getOrCreate(RenderObject* renderer)
     m_renderObjectMapping.set(renderer, newObj->axObjectID());
     m_objects.set(newObj->axObjectID(), newObj);
     attachWrapper(newObj.get());
+
+    newObj->setCachedIsIgnoredValue(newObj->accessibilityIsIgnored());
+
     return newObj.get();
 }
     
@@ -527,16 +534,26 @@ void AXObjectCache::removeAXID(AccessibilityObject* object)
     m_idsInUse.remove(objID);
 }
 
-void AXObjectCache::contentChanged(Node* node)
+void AXObjectCache::textChanged(Node* node)
 {
-    if (AccessibilityObject* object = getOrCreate(node))
-        object->contentChanged(); 
+    textChanged(getOrCreate(node));
 }
 
-void AXObjectCache::contentChanged(RenderObject* renderer)
+void AXObjectCache::textChanged(RenderObject* renderer)
 {
-    if (AccessibilityObject* object = getOrCreate(renderer))
-        object->contentChanged(); 
+    textChanged(getOrCreate(renderer));
+}
+
+void AXObjectCache::textChanged(AccessibilityObject* obj)
+{
+    if (!obj)
+        return;
+
+    bool parentAlreadyExists = obj->parentObjectIfExists();
+    obj->textChanged();
+    postNotification(obj, obj->document(), AXObjectCache::AXTextChanged, true);
+    if (parentAlreadyExists)
+        obj->notifyIfIgnoredValueChanged();
 }
 
 void AXObjectCache::updateCacheAfterNodeIsAttached(Node* node)
@@ -548,14 +565,23 @@ void AXObjectCache::updateCacheAfterNodeIsAttached(Node* node)
 
 void AXObjectCache::childrenChanged(Node* node)
 {
-    if (AccessibilityObject* obj = get(node))
-        obj->childrenChanged();
+    childrenChanged(get(node));
 }
 
 void AXObjectCache::childrenChanged(RenderObject* renderer)
 {
-    if (AccessibilityObject* obj = get(renderer))
-        obj->childrenChanged();
+    childrenChanged(get(renderer));
+}
+
+void AXObjectCache::childrenChanged(AccessibilityObject* obj)
+{
+    if (!obj)
+        return;
+
+    obj->childrenChanged();
+
+    if (obj->parentObjectIfExists() && obj->cachedIsIgnoredValue() != obj->accessibilityIsIgnored())
+        childrenChanged(obj->parentObject());
 }
     
 void AXObjectCache::notificationPostTimerFired(Timer<AXObjectCache>*)
@@ -706,8 +732,55 @@ void AXObjectCache::handleActiveDescendantChanged(Node* node)
 
 void AXObjectCache::handleAriaRoleChanged(Node* node)
 {
-    if (AccessibilityObject* obj = getOrCreate(node))
+    if (AccessibilityObject* obj = getOrCreate(node)) {
         obj->updateAccessibilityRole();
+        obj->notifyIfIgnoredValueChanged();
+    }
+}
+
+void AXObjectCache::handleAttributeChanged(const QualifiedName& attrName, Element* element)
+{
+    if (attrName == roleAttr)
+        handleAriaRoleChanged(element);
+    else if (attrName == altAttr || attrName == titleAttr)
+        textChanged(element);
+    else if (attrName == forAttr && element->hasTagName(labelTag))
+        labelChanged(element);
+
+    if (!attrName.localName().string().startsWith("aria-"))
+        return;
+
+    if (attrName == aria_activedescendantAttr)
+        handleActiveDescendantChanged(element);
+    else if (attrName == aria_valuenowAttr || attrName == aria_valuetextAttr)
+        postNotification(element, AXObjectCache::AXValueChanged, true);
+    else if (attrName == aria_labelAttr || attrName == aria_labeledbyAttr || attrName == aria_labelledbyAttr)
+        textChanged(element);
+    else if (attrName == aria_checkedAttr)
+        checkedStateChanged(element);
+    else if (attrName == aria_selectedAttr)
+        selectedChildrenChanged(element);
+    else if (attrName == aria_expandedAttr)
+        handleAriaExpandedChange(element);
+    else if (attrName == aria_hiddenAttr)
+        childrenChanged(element->parentNode());
+    else if (attrName == aria_invalidAttr)
+        postNotification(element, AXObjectCache::AXInvalidStatusChanged, true);
+    else
+        postNotification(element, AXObjectCache::AXAriaAttributeChanged, true);
+}
+
+void AXObjectCache::labelChanged(Element* element)
+{
+    ASSERT(element->hasTagName(labelTag));
+    HTMLElement* correspondingControl = static_cast<HTMLLabelElement*>(element)->control();
+    textChanged(correspondingControl);
+}
+
+void AXObjectCache::recomputeIsIgnored(RenderObject* renderer)
+{
+    if (AccessibilityObject* obj = get(renderer))
+        obj->notifyIfIgnoredValueChanged();
 }
 
 VisiblePosition AXObjectCache::visiblePositionForTextMarkerData(TextMarkerData& textMarkerData)
index fdfd5af..4edf815 100644 (file)
@@ -89,12 +89,13 @@ public:
     void attachWrapper(AccessibilityObject*);
     void childrenChanged(Node*);
     void childrenChanged(RenderObject*);
+    void childrenChanged(AccessibilityObject*);
     void checkedStateChanged(Node*);
     void selectedChildrenChanged(Node*);
     void selectedChildrenChanged(RenderObject*);
     // Called by a node when text or a text equivalent (e.g. alt) attribute is changed.
-    void contentChanged(Node*);
-    void contentChanged(RenderObject*);
+    void textChanged(Node*);
+    void textChanged(RenderObject*);
     // Called when a node has just been attached, so we can make sure we have the right subclass of AccessibilityObject.
     void updateCacheAfterNodeIsAttached(Node*);
 
@@ -105,6 +106,9 @@ public:
     void handleAriaExpandedChange(Node*);
     void handleScrollbarUpdate(ScrollView*);
 
+    void handleAttributeChanged(const QualifiedName& attrName, Element*);
+    void recomputeIsIgnored(RenderObject* renderer);
+
 #if HAVE(ACCESSIBILITY)
     static void enableAccessibility() { gAccessibilityEnabled = true; }
     // Enhanced user interface accessibility can be toggled by the assistive technology.
@@ -152,6 +156,8 @@ public:
         AXRowCollapsed,
         AXRowExpanded,
         AXInvalidStatusChanged,
+        AXTextChanged,
+        AXAriaAttributeChanged
     };
 
     void postNotification(RenderObject*, AXNotification, bool postToElement, PostType = PostAsynchronously);
@@ -180,6 +186,8 @@ protected:
     void postPlatformNotification(AccessibilityObject*, AXNotification);
     void nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned offset, const String&);
     void frameLoadingEventPlatformNotification(AccessibilityObject*, AXLoadingEvent);
+    void textChanged(AccessibilityObject*);
+    void labelChanged(Element*);
 
     // This is a weak reference cache for knowing if Nodes used by TextMarkers are valid.
     void setNodeInUse(Node* n) { m_textMarkerNodes.add(n); }
@@ -232,8 +240,10 @@ inline void AXObjectCache::attachWrapper(AccessibilityObject*) { }
 inline void AXObjectCache::checkedStateChanged(Node*) { }
 inline void AXObjectCache::childrenChanged(RenderObject*) { }
 inline void AXObjectCache::childrenChanged(Node*) { }
-inline void AXObjectCache::contentChanged(RenderObject*) { }
-inline void AXObjectCache::contentChanged(Node*) { }
+inline void AXObjectCache::childrenChanged(AccessibilityObject*) { }
+inline void AXObjectCache::textChanged(RenderObject*) { }
+inline void AXObjectCache::textChanged(Node*) { }
+inline void AXObjectCache::textChanged(AccessibilityObject*) { }
 inline void AXObjectCache::updateCacheAfterNodeIsAttached(Node*) { }
 inline void AXObjectCache::detachWrapper(AccessibilityObject*) { }
 inline void AXObjectCache::frameLoadingEventNotification(Frame*, AXLoadingEvent) { }
@@ -243,6 +253,8 @@ inline void AXObjectCache::handleAriaExpandedChange(Node*) { }
 inline void AXObjectCache::handleAriaRoleChanged(Node*) { }
 inline void AXObjectCache::handleFocusedUIElementChanged(Node*, Node*) { }
 inline void AXObjectCache::handleScrollbarUpdate(ScrollView*) { }
+inline void AXObjectCache::handleAttributeChanged(const QualifiedName&, Element*) { }
+inline void AXObjectCache::recomputeIsIgnored(RenderObject*) { }
 inline void AXObjectCache::handleScrolledToAnchor(const Node*) { }
 inline void AXObjectCache::nodeTextChangeNotification(Node*, AXTextChange, unsigned, const String&) { }
 inline void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, const String&) { }
index 74c8d9f..e675be2 100644 (file)
@@ -72,6 +72,7 @@ AccessibilityObject::AccessibilityObject()
     : m_id(0)
     , m_haveChildren(false)
     , m_role(UnknownRole)
+    , m_cachedIsIgnoredValue(DefaultBehavior)
 #if PLATFORM(GTK)
     , m_wrapper(0)
 #elif PLATFORM(CHROMIUM)
@@ -1770,6 +1771,28 @@ void AccessibilityObject::scrollToGlobalPoint(const IntPoint& globalPoint) const
     }
 }
 
+bool AccessibilityObject::cachedIsIgnoredValue()
+{
+    if (m_cachedIsIgnoredValue == DefaultBehavior)
+        m_cachedIsIgnoredValue = accessibilityIsIgnored() ? IgnoreObject : IncludeObject;
+
+    return m_cachedIsIgnoredValue == IgnoreObject;
+}
+
+void AccessibilityObject::setCachedIsIgnoredValue(bool isIgnored)
+{
+    m_cachedIsIgnoredValue = isIgnored ? IgnoreObject : IncludeObject;
+}
+
+void AccessibilityObject::notifyIfIgnoredValueChanged()
+{
+    bool isIgnored = accessibilityIsIgnored();
+    if (cachedIsIgnoredValue() != isIgnored) {
+        axObjectCache()->childrenChanged(parentObject());
+        setCachedIsIgnoredValue(isIgnored);
+    }
+}
+
 bool AccessibilityObject::ariaPressedIsPresent() const
 {
     return !getAttribute(aria_pressedAttr).isEmpty();
index 873a5c6..60bc92d 100644 (file)
@@ -610,7 +610,7 @@ public:
     virtual void decrement() { }
 
     virtual void childrenChanged() { }
-    virtual void contentChanged() { }
+    virtual void textChanged() { }
     virtual void updateAccessibilityRole() { }
     const AccessibilityChildrenVector& children();
     virtual void addChildren() { }
@@ -729,6 +729,12 @@ public:
     // Scroll this object to a given point in global coordinates of the top-level window.
     virtual void scrollToGlobalPoint(const IntPoint&) const;
 
+    bool cachedIsIgnoredValue();
+    void setCachedIsIgnoredValue(bool);
+
+    // Fires a children changed notification on the parent if the isIgnored value changed.
+    void notifyIfIgnoredValueChanged();
+
 #if HAVE(ACCESSIBILITY)
 #if PLATFORM(GTK)
     AccessibilityObjectWrapper* wrapper() const;
@@ -767,6 +773,7 @@ protected:
     AccessibilityChildrenVector m_children;
     mutable bool m_haveChildren;
     AccessibilityRole m_role;
+    AccessibilityObjectInclusion m_cachedIsIgnoredValue;
     
     // If this object itself scrolls, return its ScrollableArea.
     virtual ScrollableArea* getScrollableAreaIfScrollable() const { return 0; }
index 687716c..02e7698 100644 (file)
@@ -1094,8 +1094,8 @@ bool AccessibilityRenderObject::accessibilityIsIgnored() const
     // NOTE: BRs always have text boxes now, so the text box check here can be removed
     if (m_renderer->isText()) {
         // static text beneath MenuItems and MenuButtons are just reported along with the menu item, so it's ignored on an individual level
-        if (parentObjectUnignored()->ariaRoleAttribute() == MenuItemRole
-            || parentObjectUnignored()->ariaRoleAttribute() == MenuButtonRole)
+        AccessibilityObject* parent = parentObjectUnignored();
+        if (parent && (parent->ariaRoleAttribute() == MenuItemRole || parent->ariaRoleAttribute() == MenuButtonRole))
             return true;
         RenderText* renderText = toRenderText(m_renderer);
         if (m_renderer->isBR() || !renderText->firstTextBox())
@@ -2212,6 +2212,10 @@ AccessibilityObject* AccessibilityRenderObject::correspondingControlForLabelElem
     HTMLElement* correspondingControl = labelElement->control();
     if (!correspondingControl)
         return 0;
+
+    // Make sure the corresponding control isn't a descendant of this label that's in the middle of being destroyed.
+    if (correspondingControl->renderer() && !correspondingControl->renderer()->parent())
+        return 0;
     
     return axObjectCache()->getOrCreate(correspondingControl);     
 }
@@ -2528,7 +2532,7 @@ bool AccessibilityRenderObject::canSetTextRangeAttributes() const
     return isTextControl();
 }
 
-void AccessibilityRenderObject::contentChanged()
+void AccessibilityRenderObject::textChanged()
 {
     // If this element supports ARIA live regions, or is part of a region with an ARIA editable role,
     // then notify the AT of changes.
index b1752d3..ab09240 100644 (file)
@@ -165,7 +165,7 @@ public:
     virtual AccessibilityOrientation orientation() const;
     
     virtual void detach();
-    virtual void contentChanged();
+    virtual void textChanged();
     virtual void addChildren();
     virtual bool canHaveChildren() const;
     virtual void selectedChildren(AccessibilityChildrenVector&);
index 6d13aad..12e5b4b 100644 (file)
@@ -43,6 +43,17 @@ AccessibilityScrollView::AccessibilityScrollView(ScrollView* view)
 {
 }
 
+AccessibilityScrollView::~AccessibilityScrollView()
+{
+    ASSERT(isDetached());
+}
+
+void AccessibilityScrollView::detach()
+{
+    AccessibilityObject::detach();
+    m_scrollView = 0;
+}
+
 PassRefPtr<AccessibilityScrollView> AccessibilityScrollView::create(ScrollView* view)
 {
     return adoptRef(new AccessibilityScrollView(view));
@@ -67,12 +78,12 @@ AccessibilityObject* AccessibilityScrollView::scrollBar(AccessibilityOrientation
 // In WebKit2, the ScrollView object will return the AX information (because there are no platform widgets).
 bool AccessibilityScrollView::isAttachment() const
 {
-    return m_scrollView->platformWidget();
+    return m_scrollView && m_scrollView->platformWidget();
 }
 
 Widget* AccessibilityScrollView::widgetForAttachmentView() const
 {
-    return m_scrollView.get();
+    return m_scrollView;
 }
     
 void AccessibilityScrollView::updateChildrenIfNecessary()
@@ -88,6 +99,9 @@ void AccessibilityScrollView::updateChildrenIfNecessary()
 
 void AccessibilityScrollView::updateScrollbars()
 {
+    if (!m_scrollView)
+        return;
+
     if (m_scrollView->horizontalScrollbar() && !m_horizontalScrollbar)
         m_horizontalScrollbar = addChildScrollbar(m_scrollView->horizontalScrollbar());
     else if (!m_scrollView->horizontalScrollbar() && m_horizontalScrollbar) {
@@ -153,10 +167,10 @@ void AccessibilityScrollView::addChildren()
 
 AccessibilityObject* AccessibilityScrollView::webAreaObject() const
 {
-    if (!m_scrollView->isFrameView())
+    if (!m_scrollView || !m_scrollView->isFrameView())
         return 0;
     
-    Document* doc = static_cast<FrameView*>(m_scrollView.get())->frame()->document();
+    Document* doc = static_cast<FrameView*>(m_scrollView)->frame()->document();
     if (!doc || !doc->renderer())
         return 0;
 
@@ -179,23 +193,26 @@ AccessibilityObject* AccessibilityScrollView::accessibilityHitTest(const IntPoin
 
 LayoutRect AccessibilityScrollView::elementRect() const
 {
+    if (!m_scrollView)
+        return LayoutRect();
+
     return m_scrollView->frameRect();
 }
 
 FrameView* AccessibilityScrollView::documentFrameView() const
 {
-    if (!m_scrollView->isFrameView())
+    if (!m_scrollView || !m_scrollView->isFrameView())
         return 0;
     
-    return static_cast<FrameView*>(m_scrollView.get());
+    return static_cast<FrameView*>(m_scrollView);
 }    
 
 AccessibilityObject* AccessibilityScrollView::parentObject() const
 {
-    if (!m_scrollView->isFrameView())
+    if (!m_scrollView || !m_scrollView->isFrameView())
         return 0;
     
-    HTMLFrameOwnerElement* owner = static_cast<FrameView*>(m_scrollView.get())->frame()->ownerElement();
+    HTMLFrameOwnerElement* owner = static_cast<FrameView*>(m_scrollView)->frame()->ownerElement();
     if (owner && owner->renderer())
         return axObjectCache()->getOrCreate(owner);
 
@@ -204,10 +221,10 @@ AccessibilityObject* AccessibilityScrollView::parentObject() const
     
 AccessibilityObject* AccessibilityScrollView::parentObjectIfExists() const
 {
-    if (!m_scrollView->isFrameView())
+    if (!m_scrollView || !m_scrollView->isFrameView())
         return 0;
     
-    HTMLFrameOwnerElement* owner = static_cast<FrameView*>(m_scrollView.get())->frame()->ownerElement();
+    HTMLFrameOwnerElement* owner = static_cast<FrameView*>(m_scrollView)->frame()->ownerElement();
     if (owner && owner->renderer())
         return axObjectCache()->get(owner);
     
@@ -216,12 +233,13 @@ AccessibilityObject* AccessibilityScrollView::parentObjectIfExists() const
 
 ScrollableArea* AccessibilityScrollView::getScrollableAreaIfScrollable() const
 {
-    return m_scrollView.get();
+    return m_scrollView;
 }
 
 void AccessibilityScrollView::scrollTo(const IntPoint& point) const
 {
-    m_scrollView->setScrollPosition(point);
+    if (m_scrollView)
+        m_scrollView->setScrollPosition(point);
 }
 
 } // namespace WebCore    
index 9953164..4ff8a22 100644 (file)
@@ -38,7 +38,10 @@ class AccessibilityScrollView : public AccessibilityObject {
 public:
     static PassRefPtr<AccessibilityScrollView> create(ScrollView*);    
     virtual AccessibilityRole roleValue() const { return ScrollAreaRole; }
-    ScrollView* scrollView() const { return m_scrollView.get(); }
+    ScrollView* scrollView() const { return m_scrollView; }
+
+    virtual ~AccessibilityScrollView();
+    virtual void detach();
 
 protected:
     virtual ScrollableArea* getScrollableAreaIfScrollable() const;
@@ -72,7 +75,7 @@ private:
     AccessibilityScrollbar* addChildScrollbar(Scrollbar*);
     void removeChildScrollbar(AccessibilityObject*);
     
-    RefPtr<ScrollView> m_scrollView;
+    ScrollView* m_scrollView;
     RefPtr<AccessibilityObject> m_horizontalScrollbar;
     RefPtr<AccessibilityObject> m_verticalScrollbar;
     bool m_childrenDirty;
index b1d155d..a6dab25 100644 (file)
@@ -130,6 +130,7 @@ bool AccessibilityTable::isDataTable() const
     
     // go through the cell's and check for tell-tale signs of "data" table status
     // cells have borders, or use attributes like headers, abbr, scope or axis
+    table->recalcSectionsIfNeeded();
     RenderTableSection* firstBody = table->firstBody();
     if (!firstBody)
         return false;
index a95e044..f68a591 100644 (file)
@@ -82,10 +82,12 @@ void AXObjectCache::postPlatformNotification(AccessibilityObject* obj, AXNotific
         // descendant and send the AXFocusedUIElementChanged notification.
         handleFocusedUIElementChanged(0, obj->document()->focusedNode());
         break;
+    case AXAriaAttributeChanged:
     case AXAutocorrectionOccured:
     case AXCheckedStateChanged:
     case AXChildrenChanged:
     case AXFocusedUIElementChanged:
+    case AXInvalidStatusChanged:
     case AXLayoutComplete:
     case AXLiveRegionChanged:
     case AXLoadComplete:
@@ -97,8 +99,8 @@ void AXObjectCache::postPlatformNotification(AccessibilityObject* obj, AXNotific
     case AXScrolledToAnchor:
     case AXSelectedChildrenChanged:
     case AXSelectedTextChanged:
+    case AXTextChanged:
     case AXValueChanged:
-    case AXInvalidStatusChanged:
         break;
     }
 
index e8456f7..ecace1e 100644 (file)
@@ -722,31 +722,8 @@ void Element::attributeChanged(const QualifiedName& name, const AtomicString& ne
 
     invalidateNodeListCachesInAncestors(&name, this);
 
-    if (!AXObjectCache::accessibilityEnabled())
-        return;
-
-    if (name == aria_activedescendantAttr) {
-        // any change to aria-activedescendant attribute triggers accessibility focus change, but document focus remains intact
-        document()->axObjectCache()->handleActiveDescendantChanged(this);
-    } else if (name == roleAttr) {
-        // the role attribute can change at any time, and the AccessibilityObject must pick up these changes
-        document()->axObjectCache()->handleAriaRoleChanged(this);
-    } else if (name == aria_valuenowAttr) {
-        // If the valuenow attribute changes, AX clients need to be notified.
-        document()->axObjectCache()->postNotification(this, AXObjectCache::AXValueChanged, true);
-    } else if (name == aria_labelAttr || name == aria_labeledbyAttr || name == altAttr || name == titleAttr) {
-        // If the content of an element changes due to an attribute change, notify accessibility.
-        document()->axObjectCache()->contentChanged(this);
-    } else if (name == aria_checkedAttr)
-        document()->axObjectCache()->checkedStateChanged(this);
-    else if (name == aria_selectedAttr)
-        document()->axObjectCache()->selectedChildrenChanged(this);
-    else if (name == aria_expandedAttr)
-        document()->axObjectCache()->handleAriaExpandedChange(this);
-    else if (name == aria_hiddenAttr)
-        document()->axObjectCache()->childrenChanged(this);
-    else if (name == aria_invalidAttr)
-        document()->axObjectCache()->postNotification(this, AXObjectCache::AXInvalidStatusChanged, true);
+    if (AXObjectCache::accessibilityEnabled())
+        document()->axObjectCache()->handleAttributeChanged(name, this);
 }
 
 void Element::parseAttribute(const Attribute& attribute)
index dad4ec0..12186d1 100644 (file)
@@ -24,6 +24,7 @@
 #include "config.h"
 #include "RenderBlock.h"
 
+#include "AXObjectCache.h"
 #include "ColumnInfo.h"
 #include "Document.h"
 #include "Element.h"
@@ -1011,6 +1012,8 @@ void RenderBlock::deleteLineBoxTree()
         }
     }
     m_lineBoxes.deleteLineBoxTree(renderArena());
+    if (UNLIKELY(AXObjectCache::accessibilityEnabled()))
+        document()->axObjectCache()->recomputeIsIgnored(this);
 }
 
 RootInlineBox* RenderBlock::createRootInlineBox() 
@@ -1022,6 +1025,10 @@ RootInlineBox* RenderBlock::createAndAppendRootInlineBox()
 {
     RootInlineBox* rootBox = createRootInlineBox();
     m_lineBoxes.appendLineBox(rootBox);
+
+    if (UNLIKELY(AXObjectCache::accessibilityEnabled()) && m_lineBoxes.firstLineBox() == rootBox)
+        document()->axObjectCache()->recomputeIsIgnored(this);
+
     return rootBox;
 }
 
index e7eeee1..bf68c76 100644 (file)
@@ -1829,7 +1829,7 @@ void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newS
                 document()->setAnnotatedRegionsDirty(true);
 #endif
             if (visibilityChanged && AXObjectCache::accessibilityEnabled())
-                document()->axObjectCache()->childrenChanged(this);
+                document()->axObjectCache()->childrenChanged(parent());
 
             // Keep layer hierarchy visibility bits up to date if visibility changes.
             if (m_style->visibility() != newStyle->visibility()) {
index 9a077bf..3f8d715 100644 (file)
@@ -1466,7 +1466,7 @@ void RenderText::setText(PassRefPtr<StringImpl> text, bool force)
     
     AXObjectCache* axObjectCache = document()->axObjectCache();
     if (axObjectCache->accessibilityEnabled())
-        axObjectCache->contentChanged(this);
+        axObjectCache->textChanged(this);
 }
 
 String RenderText::textWithoutTranscoding() const
index f8ae104..6d20598 100644 (file)
@@ -1,3 +1,15 @@
+2012-10-25  Dominic Mazzoni  <dmazzoni@google.com>
+
+        AX: Notification should be sent when accessibilityIsIgnored changes
+        https://bugs.webkit.org/show_bug.cgi?id=99547
+
+        Reviewed by Chris Fleizach.
+
+        Adds new accessibility notifications.
+
+        * public/WebAccessibilityNotification.h:
+        * src/AssertMatchingEnums.cpp:
+
 2012-10-25  Dominik Röttsches  <dominik.rottsches@intel.com>
 
         Conditionalize XHR timeout support
index de68d37..629d988 100644 (file)
@@ -54,11 +54,8 @@ enum WebAccessibilityNotification {
     WebAccessibilityNotificationRowCollapsed,
     WebAccessibilityNotificationRowExpanded,
     WebAccessibilityNotificationInvalidStatusChanged,
-
-    // FIXME: now that we're using AssertMatchingEnums, we don't need a
-    // catch-all "invalid" notification enum. Remove this once it's been
-    // removed from Chromium.
-    WebAccessibilityNotificationInvalid,
+    WebAccessibilityNotificationTextChanged,
+    WebAccessibilityNotificationAriaAttributeChanged
 };
 
 } // namespace WebKit
index 95e8b60..0257279 100644 (file)
@@ -153,6 +153,8 @@ COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityNotificationRowCountChanged, AXObje
 COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityNotificationRowCollapsed, AXObjectCache::AXRowCollapsed);
 COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityNotificationRowExpanded, AXObjectCache::AXRowExpanded);
 COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityNotificationInvalidStatusChanged, AXObjectCache::AXInvalidStatusChanged);
+COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityNotificationTextChanged, AXObjectCache::AXTextChanged);
+COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityNotificationAriaAttributeChanged, AXObjectCache::AXAriaAttributeChanged);
 
 COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleUnknown, UnknownRole);
 COMPILE_ASSERT_MATCHING_ENUM(WebAccessibilityRoleButton, ButtonRole);
index 7f7708f..58d7d1c 100644 (file)
@@ -1,3 +1,15 @@
+2012-10-25  Dominic Mazzoni  <dmazzoni@google.com>
+
+        AX: Notification should be sent when accessibilityIsIgnored changes
+        https://bugs.webkit.org/show_bug.cgi?id=99547
+
+        Reviewed by Chris Fleizach.
+
+        Add additional accessibility notifications.
+
+        * DumpRenderTree/chromium/WebViewHost.cpp:
+        (WebViewHost::postAccessibilityNotification):
+
 2012-10-25  Dominik Röttsches  <dominik.rottsches@intel.com>
 
         Conditionalize XHR timeout support
index d37622d..29accee 100644 (file)
@@ -696,6 +696,12 @@ void WebViewHost::postAccessibilityNotification(const WebAccessibilityObject& ob
     case WebAccessibilityNotificationInvalidStatusChanged:
         notificationName = "InvalidStatusChanged";
         break;
+    case WebAccessibilityNotificationTextChanged:
+        notificationName = "TextChanged";
+        break;
+    case WebAccessibilityNotificationAriaAttributeChanged:
+        notificationName = "AriaAttributeChanged";
+        break;
     default:
         notificationName = "UnknownNotification";
         break;