AX: Need AccessibilityObjects for nodes without renderers in canvas subtree
authordmazzoni@google.com <dmazzoni@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 20 Jul 2012 06:06:04 +0000 (06:06 +0000)
committerdmazzoni@google.com <dmazzoni@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 20 Jul 2012 06:06:04 +0000 (06:06 +0000)
https://bugs.webkit.org/show_bug.cgi?id=87899

Reviewed by Chris Fleizach.

Source/WebCore:

Refactors AccessibilityRenderObject so that it inherits from a new class,
AccessibilityNodeObject, that can be constructed from a Node without a
renderer. Modifies AXObjectCache so that it automatically creates an
AccessibilityNodeObject for elements in a canvas subtree but not otherwise.
A new layout test verifies that this correctly exposes an accessibility
tree with appropriate roles for elements in a canvas subtree.

This patch does not try to complete the implementation of
AccessibilityNodeObject. Most AX methods are still unimplemented and need
to be migrated from AccessibilityRenderObject to AccessibilityNodeObject
in a future patch.

This patch also doesn't change anything outside of WebCore/accessibility, so
the rest of WebCore only calls AXObjectCache when there are changes to a
RenderObject, not to a Node. Accessible notifications on nodes without
renderers need to be implemented in a future patch.

Test: accessibility/canvas-accessibilitynodeobject.html

* CMakeLists.txt:
* GNUmakefile.list.am:
* Target.pri:
* WebCore.gypi:
* accessibility/AXObjectCache.cpp:
(WebCore::AXObjectCache::get):
(WebCore):
(WebCore::createFromNode):
(WebCore::AXObjectCache::getOrCreate):
(WebCore::AXObjectCache::remove):
* accessibility/AXObjectCache.h:
(AXObjectCache):
(WebCore::AXObjectCache::get):
(WebCore::AXObjectCache::getOrCreate):
(WebCore::AXObjectCache::remove):
* accessibility/AccessibilityARIAGrid.cpp:
(WebCore::AccessibilityARIAGrid::AccessibilityARIAGrid):
(WebCore):
(WebCore::AccessibilityARIAGrid::~AccessibilityARIAGrid):
(WebCore::AccessibilityARIAGrid::init):
(WebCore::AccessibilityARIAGrid::create):
* accessibility/AccessibilityARIAGrid.h:
(AccessibilityARIAGrid):
* accessibility/AccessibilityARIAGridCell.cpp:
(WebCore::AccessibilityARIAGridCell::create):
* accessibility/AccessibilityARIAGridRow.cpp:
(WebCore::AccessibilityARIAGridRow::create):
* accessibility/AccessibilityAllInOne.cpp:
* accessibility/AccessibilityList.cpp:
(WebCore::AccessibilityList::create):
* accessibility/AccessibilityListBox.cpp:
(WebCore::AccessibilityListBox::create):
* accessibility/AccessibilityMediaControls.cpp:
(WebCore::AccessibilityMediaControl::create):
(WebCore::AccessibilityMediaControlsContainer::create):
(WebCore::AccessibilityMediaTimeline::create):
(WebCore::AccessibilityMediaTimeDisplay::create):
* accessibility/AccessibilityMenuList.cpp:
(WebCore::AccessibilityMenuList::create):
(WebCore):
* accessibility/AccessibilityMenuList.h:
(AccessibilityMenuList):
* accessibility/AccessibilityNodeObject.cpp: Added.
(WebCore):
(WebCore::AccessibilityNodeObject::AccessibilityNodeObject):
(WebCore::AccessibilityNodeObject::~AccessibilityNodeObject):
(WebCore::AccessibilityNodeObject::init):
(WebCore::AccessibilityNodeObject::create):
(WebCore::AccessibilityNodeObject::detach):
(WebCore::AccessibilityNodeObject::childrenChanged):
(WebCore::AccessibilityNodeObject::updateAccessibilityRole):
(WebCore::AccessibilityNodeObject::firstChild):
(WebCore::AccessibilityNodeObject::lastChild):
(WebCore::AccessibilityNodeObject::previousSibling):
(WebCore::AccessibilityNodeObject::nextSibling):
(WebCore::AccessibilityNodeObject::parentObjectIfExists):
(WebCore::AccessibilityNodeObject::parentObject):
(WebCore::AccessibilityNodeObject::elementRect):
(WebCore::AccessibilityNodeObject::setNode):
(WebCore::AccessibilityNodeObject::document):
(WebCore::AccessibilityNodeObject::determineAccessibilityRole):
(WebCore::AccessibilityNodeObject::addChildren):
(WebCore::AccessibilityNodeObject::accessibilityIsIgnored):
(WebCore::AccessibilityNodeObject::canSetFocusAttribute):
(WebCore::AccessibilityNodeObject::determineAriaRoleAttribute):
(WebCore::AccessibilityNodeObject::ariaRoleAttribute):
(WebCore::AccessibilityNodeObject::remapAriaRoleDueToParent):
* accessibility/AccessibilityNodeObject.h: Added.
(WebCore):
(AccessibilityNodeObject):
(WebCore::AccessibilityNodeObject::isAccessibilityNodeObject):
(WebCore::AccessibilityNodeObject::node):
(WebCore::AccessibilityNodeObject::isDetached):
(WebCore::toAccessibilityNodeObject):
* accessibility/AccessibilityObject.h:
(WebCore::AccessibilityObject::isAccessibilityNodeObject):
* accessibility/AccessibilityProgressIndicator.cpp:
(WebCore::AccessibilityProgressIndicator::create):
* accessibility/AccessibilityRenderObject.cpp:
(WebCore::AccessibilityRenderObject::AccessibilityRenderObject):
(WebCore::AccessibilityRenderObject::init):
(WebCore):
(WebCore::AccessibilityRenderObject::create):
(WebCore::AccessibilityRenderObject::detach):
(WebCore::AccessibilityRenderObject::setRenderer):
(WebCore::AccessibilityRenderObject::canHaveChildren):
(WebCore::AccessibilityRenderObject::addCanvasChildren):
(WebCore::AccessibilityRenderObject::addChildren):
* accessibility/AccessibilityRenderObject.h:
(AccessibilityRenderObject):
* accessibility/AccessibilitySlider.cpp:
(WebCore::AccessibilitySlider::create):
* accessibility/AccessibilityTable.cpp:
(WebCore::AccessibilityTable::AccessibilityTable):
(WebCore):
(WebCore::AccessibilityTable::~AccessibilityTable):
(WebCore::AccessibilityTable::init):
(WebCore::AccessibilityTable::create):
* accessibility/AccessibilityTable.h:
(AccessibilityTable):
* accessibility/AccessibilityTableCell.cpp:
(WebCore::AccessibilityTableCell::create):
* accessibility/AccessibilityTableRow.cpp:
(WebCore::AccessibilityTableRow::create):

LayoutTests:

This new test adds a bunch of elements to a normal container and the same elements to
a canvas subtree, then it iterates over all of the accessible objects within each
container and makes sure they have identical roles. Because we know the nodes in the
canvas subtree don't have renderers, this successfully tests that AccessibilityNodeObjects
are getting created for these nodes.

* accessibility/canvas-accessibilitynodeobject-expected.txt: Added.
* accessibility/canvas-accessibilitynodeobject.html: Added.

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

31 files changed:
LayoutTests/ChangeLog
LayoutTests/accessibility/canvas-accessibilitynodeobject-expected.txt [new file with mode: 0644]
LayoutTests/accessibility/canvas-accessibilitynodeobject.html [new file with mode: 0644]
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/GNUmakefile.list.am
Source/WebCore/Target.pri
Source/WebCore/WebCore.gypi
Source/WebCore/accessibility/AXObjectCache.cpp
Source/WebCore/accessibility/AXObjectCache.h
Source/WebCore/accessibility/AccessibilityARIAGrid.cpp
Source/WebCore/accessibility/AccessibilityARIAGrid.h
Source/WebCore/accessibility/AccessibilityARIAGridCell.cpp
Source/WebCore/accessibility/AccessibilityARIAGridRow.cpp
Source/WebCore/accessibility/AccessibilityAllInOne.cpp
Source/WebCore/accessibility/AccessibilityList.cpp
Source/WebCore/accessibility/AccessibilityListBox.cpp
Source/WebCore/accessibility/AccessibilityMediaControls.cpp
Source/WebCore/accessibility/AccessibilityMenuList.cpp
Source/WebCore/accessibility/AccessibilityMenuList.h
Source/WebCore/accessibility/AccessibilityNodeObject.cpp [new file with mode: 0644]
Source/WebCore/accessibility/AccessibilityNodeObject.h [new file with mode: 0644]
Source/WebCore/accessibility/AccessibilityObject.h
Source/WebCore/accessibility/AccessibilityProgressIndicator.cpp
Source/WebCore/accessibility/AccessibilityRenderObject.cpp
Source/WebCore/accessibility/AccessibilityRenderObject.h
Source/WebCore/accessibility/AccessibilitySlider.cpp
Source/WebCore/accessibility/AccessibilityTable.cpp
Source/WebCore/accessibility/AccessibilityTable.h
Source/WebCore/accessibility/AccessibilityTableCell.cpp
Source/WebCore/accessibility/AccessibilityTableRow.cpp

index 2a66f28..b3f8a3d 100644 (file)
@@ -1,3 +1,19 @@
+2012-07-19  Dominic Mazzoni  <dmazzoni@google.com>
+
+        AX: Need AccessibilityObjects for nodes without renderers in canvas subtree
+        https://bugs.webkit.org/show_bug.cgi?id=87899
+
+        Reviewed by Chris Fleizach.
+
+        This new test adds a bunch of elements to a normal container and the same elements to
+        a canvas subtree, then it iterates over all of the accessible objects within each
+        container and makes sure they have identical roles. Because we know the nodes in the
+        canvas subtree don't have renderers, this successfully tests that AccessibilityNodeObjects
+        are getting created for these nodes.
+
+        * accessibility/canvas-accessibilitynodeobject-expected.txt: Added.
+        * accessibility/canvas-accessibilitynodeobject.html: Added.
+
 2012-07-19  Kent Tamura  <tkent@chromium.org>
 
         Form state restore: Need to identify a form by its content
diff --git a/LayoutTests/accessibility/canvas-accessibilitynodeobject-expected.txt b/LayoutTests/accessibility/canvas-accessibilitynodeobject-expected.txt
new file mode 100644 (file)
index 0000000..07532f7
--- /dev/null
@@ -0,0 +1,20 @@
+Link  Button           ARIA button ARIA link
+This test makes sure that AccessibilityNodeObjects are created for elements in a canvas subtree.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS axRenderObjects.length is axNodeObjects.length
+PASS i == 0; axRenderObject.role == axNodeObject.role is true
+PASS i == 1; axRenderObject.role == axNodeObject.role is true
+PASS i == 2; axRenderObject.role == axNodeObject.role is true
+PASS i == 3; axRenderObject.role == axNodeObject.role is true
+PASS i == 4; axRenderObject.role == axNodeObject.role is true
+PASS i == 5; axRenderObject.role == axNodeObject.role is true
+PASS i == 6; axRenderObject.role == axNodeObject.role is true
+PASS i == 7; axRenderObject.role == axNodeObject.role is true
+PASS i == 8; axRenderObject.role == axNodeObject.role is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/accessibility/canvas-accessibilitynodeobject.html b/LayoutTests/accessibility/canvas-accessibilitynodeobject.html
new file mode 100644 (file)
index 0000000..86bbbb6
--- /dev/null
@@ -0,0 +1,73 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<script src="../fast/js/resources/js-test-pre.js"></script>
+
+<div id="myContainer" tabindex="-1">
+  <a href="#">Link</a>
+  <button>Button</button>
+  <input type="text">
+  <input type="checkbox">
+  <input type="radio">
+  <input type="submit">
+  <select><option>1<option>2</select>
+  <span tabindex="0" role="button">ARIA button</span>
+  <span tabindex="0" role="link">ARIA link</span>
+</div>
+
+<canvas id="myCanvas" width="300" height="300" tabindex="-1">
+  <a href="#">Link</a>
+  <button>Button</button>
+  <input type="text">
+  <input type="checkbox">
+  <input type="radio">
+  <input type="submit">
+  <select><option>1<option>2</select>
+  <span tabindex="0" role="button">ARIA button</span>
+  <span tabindex="0" role="link">ARIA link</span>
+</canvas>
+
+<div id="console"></div>
+<script>
+description("This test makes sure that AccessibilityNodeObjects are created for elements in a canvas subtree.");
+
+if (window.layoutTestController && window.accessibilityController) {
+    window.layoutTestController.dumpAsText();
+
+    function appendFocusableDescendants(axObject, axFocusableList) {
+        for (var i = 0; i < axObject.childrenCount; i++) {
+            var axChild = axObject.childAtIndex(i);
+            if (axChild.isFocusable)
+                axFocusableList.push(axChild);
+            appendFocusableDescendants(axChild, axFocusableList);
+        }
+    }
+
+    var container = document.getElementById("myContainer");
+    container.focus();
+    var axContainer = accessibilityController.focusedElement;
+
+    var canvas = document.getElementById("myCanvas");
+    canvas.focus();
+    var axCanvas = accessibilityController.focusedElement;
+
+    var axRenderObjects = [];
+    var axNodeObjects = [];
+
+    appendFocusableDescendants(axContainer, axRenderObjects);
+    appendFocusableDescendants(axCanvas, axNodeObjects);
+
+    shouldBe("axRenderObjects.length", "axNodeObjects.length");
+
+    for (var i = 0; i < axRenderObjects.length; i++) {
+        var axRenderObject = axRenderObjects[i];
+        var axNodeObject = axNodeObjects[i];
+        shouldBe("i == " + i + "; axRenderObject.role == axNodeObject.role", "true");
+    }
+}
+
+</script>
+
+<script src="../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
index 89d4502..372e9a8 100644 (file)
@@ -935,6 +935,7 @@ SET(WebCore_SOURCES
     accessibility/AccessibilityMenuListOption.cpp
     accessibility/AccessibilityMenuListPopup.cpp
     accessibility/AccessibilityMockObject.cpp
+    accessibility/AccessibilityNodeObject.cpp
     accessibility/AccessibilityObject.cpp
     accessibility/AccessibilityProgressIndicator.cpp
     accessibility/AccessibilityRenderObject.cpp
index 272540e..f0d16d7 100644 (file)
@@ -1,3 +1,134 @@
+2012-07-19  Dominic Mazzoni  <dmazzoni@google.com>
+
+        AX: Need AccessibilityObjects for nodes without renderers in canvas subtree
+        https://bugs.webkit.org/show_bug.cgi?id=87899
+
+        Reviewed by Chris Fleizach.
+
+        Refactors AccessibilityRenderObject so that it inherits from a new class,
+        AccessibilityNodeObject, that can be constructed from a Node without a
+        renderer. Modifies AXObjectCache so that it automatically creates an
+        AccessibilityNodeObject for elements in a canvas subtree but not otherwise.
+        A new layout test verifies that this correctly exposes an accessibility
+        tree with appropriate roles for elements in a canvas subtree.
+
+        This patch does not try to complete the implementation of
+        AccessibilityNodeObject. Most AX methods are still unimplemented and need
+        to be migrated from AccessibilityRenderObject to AccessibilityNodeObject
+        in a future patch.
+        
+        This patch also doesn't change anything outside of WebCore/accessibility, so
+        the rest of WebCore only calls AXObjectCache when there are changes to a
+        RenderObject, not to a Node. Accessible notifications on nodes without
+        renderers need to be implemented in a future patch.
+
+        Test: accessibility/canvas-accessibilitynodeobject.html
+
+        * CMakeLists.txt:
+        * GNUmakefile.list.am:
+        * Target.pri:
+        * WebCore.gypi:
+        * accessibility/AXObjectCache.cpp:
+        (WebCore::AXObjectCache::get):
+        (WebCore):
+        (WebCore::createFromNode):
+        (WebCore::AXObjectCache::getOrCreate):
+        (WebCore::AXObjectCache::remove):
+        * accessibility/AXObjectCache.h:
+        (AXObjectCache):
+        (WebCore::AXObjectCache::get):
+        (WebCore::AXObjectCache::getOrCreate):
+        (WebCore::AXObjectCache::remove):
+        * accessibility/AccessibilityARIAGrid.cpp:
+        (WebCore::AccessibilityARIAGrid::AccessibilityARIAGrid):
+        (WebCore):
+        (WebCore::AccessibilityARIAGrid::~AccessibilityARIAGrid):
+        (WebCore::AccessibilityARIAGrid::init):
+        (WebCore::AccessibilityARIAGrid::create):
+        * accessibility/AccessibilityARIAGrid.h:
+        (AccessibilityARIAGrid):
+        * accessibility/AccessibilityARIAGridCell.cpp:
+        (WebCore::AccessibilityARIAGridCell::create):
+        * accessibility/AccessibilityARIAGridRow.cpp:
+        (WebCore::AccessibilityARIAGridRow::create):
+        * accessibility/AccessibilityAllInOne.cpp:
+        * accessibility/AccessibilityList.cpp:
+        (WebCore::AccessibilityList::create):
+        * accessibility/AccessibilityListBox.cpp:
+        (WebCore::AccessibilityListBox::create):
+        * accessibility/AccessibilityMediaControls.cpp:
+        (WebCore::AccessibilityMediaControl::create):
+        (WebCore::AccessibilityMediaControlsContainer::create):
+        (WebCore::AccessibilityMediaTimeline::create):
+        (WebCore::AccessibilityMediaTimeDisplay::create):
+        * accessibility/AccessibilityMenuList.cpp:
+        (WebCore::AccessibilityMenuList::create):
+        (WebCore):
+        * accessibility/AccessibilityMenuList.h:
+        (AccessibilityMenuList):
+        * accessibility/AccessibilityNodeObject.cpp: Added.
+        (WebCore):
+        (WebCore::AccessibilityNodeObject::AccessibilityNodeObject):
+        (WebCore::AccessibilityNodeObject::~AccessibilityNodeObject):
+        (WebCore::AccessibilityNodeObject::init):
+        (WebCore::AccessibilityNodeObject::create):
+        (WebCore::AccessibilityNodeObject::detach):
+        (WebCore::AccessibilityNodeObject::childrenChanged):
+        (WebCore::AccessibilityNodeObject::updateAccessibilityRole):
+        (WebCore::AccessibilityNodeObject::firstChild):
+        (WebCore::AccessibilityNodeObject::lastChild):
+        (WebCore::AccessibilityNodeObject::previousSibling):
+        (WebCore::AccessibilityNodeObject::nextSibling):
+        (WebCore::AccessibilityNodeObject::parentObjectIfExists):
+        (WebCore::AccessibilityNodeObject::parentObject):
+        (WebCore::AccessibilityNodeObject::elementRect):
+        (WebCore::AccessibilityNodeObject::setNode):
+        (WebCore::AccessibilityNodeObject::document):
+        (WebCore::AccessibilityNodeObject::determineAccessibilityRole):
+        (WebCore::AccessibilityNodeObject::addChildren):
+        (WebCore::AccessibilityNodeObject::accessibilityIsIgnored):
+        (WebCore::AccessibilityNodeObject::canSetFocusAttribute):
+        (WebCore::AccessibilityNodeObject::determineAriaRoleAttribute):
+        (WebCore::AccessibilityNodeObject::ariaRoleAttribute):
+        (WebCore::AccessibilityNodeObject::remapAriaRoleDueToParent):
+        * accessibility/AccessibilityNodeObject.h: Added.
+        (WebCore):
+        (AccessibilityNodeObject):
+        (WebCore::AccessibilityNodeObject::isAccessibilityNodeObject):
+        (WebCore::AccessibilityNodeObject::node):
+        (WebCore::AccessibilityNodeObject::isDetached):
+        (WebCore::toAccessibilityNodeObject):
+        * accessibility/AccessibilityObject.h:
+        (WebCore::AccessibilityObject::isAccessibilityNodeObject):
+        * accessibility/AccessibilityProgressIndicator.cpp:
+        (WebCore::AccessibilityProgressIndicator::create):
+        * accessibility/AccessibilityRenderObject.cpp:
+        (WebCore::AccessibilityRenderObject::AccessibilityRenderObject):
+        (WebCore::AccessibilityRenderObject::init):
+        (WebCore):
+        (WebCore::AccessibilityRenderObject::create):
+        (WebCore::AccessibilityRenderObject::detach):
+        (WebCore::AccessibilityRenderObject::setRenderer):
+        (WebCore::AccessibilityRenderObject::canHaveChildren):
+        (WebCore::AccessibilityRenderObject::addCanvasChildren):
+        (WebCore::AccessibilityRenderObject::addChildren):
+        * accessibility/AccessibilityRenderObject.h:
+        (AccessibilityRenderObject):
+        * accessibility/AccessibilitySlider.cpp:
+        (WebCore::AccessibilitySlider::create):
+        * accessibility/AccessibilityTable.cpp:
+        (WebCore::AccessibilityTable::AccessibilityTable):
+        (WebCore):
+        (WebCore::AccessibilityTable::~AccessibilityTable):
+        (WebCore::AccessibilityTable::init):
+        (WebCore::AccessibilityTable::create):
+        * accessibility/AccessibilityTable.h:
+        (AccessibilityTable):
+        * accessibility/AccessibilityTableCell.cpp:
+        (WebCore::AccessibilityTableCell::create):
+        * accessibility/AccessibilityTableRow.cpp:
+        (WebCore::AccessibilityTableRow::create):
+
 2012-07-19  Dan Bernstein  <mitz@apple.com>
 
         The ATSUI-based complex text code is unused
index 3eb9439..7491043 100644 (file)
@@ -1317,6 +1317,8 @@ webcore_sources += \
        Source/WebCore/accessibility/AccessibilityMenuListPopup.h \
        Source/WebCore/accessibility/AccessibilityMockObject.cpp \
        Source/WebCore/accessibility/AccessibilityMockObject.h \
+       Source/WebCore/accessibility/AccessibilityNodeObject.cpp \
+       Source/WebCore/accessibility/AccessibilityNodeObject.h \
        Source/WebCore/accessibility/AccessibilityObject.cpp \
        Source/WebCore/accessibility/AccessibilityObject.h \
        Source/WebCore/accessibility/AccessibilityProgressIndicator.cpp \
index 23bc2d0..7782825 100644 (file)
@@ -52,6 +52,7 @@ SOURCES += \
     accessibility/AccessibilityList.cpp \
     accessibility/AccessibilityListBox.cpp \
     accessibility/AccessibilityListBoxOption.cpp \
+    accessibility/AccessibilityNodeObject.cpp \
     accessibility/AccessibilityProgressIndicator.cpp \
     accessibility/AccessibilityRenderObject.cpp \
     accessibility/AccessibilityScrollbar.cpp \
index 3a36325..5fcd6d4 100644 (file)
             'accessibility/AccessibilityMenuListPopup.h',
             'accessibility/AccessibilityMockObject.cpp',
             'accessibility/AccessibilityMockObject.h',
+            'accessibility/AccessibilityNodeObject.cpp',
+            'accessibility/AccessibilityNodeObject.h',
             'accessibility/AccessibilityObject.cpp',
             'accessibility/AccessibilityProgressIndicator.cpp',
             'accessibility/AccessibilityProgressIndicator.h',
index 71a2214..0f4e6a6 100644 (file)
@@ -188,6 +188,23 @@ AccessibilityObject* AXObjectCache::get(RenderObject* renderer)
     return m_objects.get(axID).get();    
 }
 
+AccessibilityObject* AXObjectCache::get(Node* node)
+{
+    if (!node)
+        return 0;
+
+    // Always prefer building the AccessibilityObject from the renderer if there is one.
+    if (node->renderer())
+        return get(node->renderer());
+
+    AXID axID = m_nodeObjectMapping.get(node);
+    ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
+    if (!axID)
+        return 0;
+
+    return m_objects.get(axID).get();
+}
+
 // FIXME: This probably belongs on Node.
 // FIXME: This should take a const char*, but one caller passes nullAtom.
 bool nodeHasRole(Node* node, const String& role)
@@ -252,6 +269,11 @@ static PassRefPtr<AccessibilityObject> createFromRenderer(RenderObject* renderer
     return AccessibilityRenderObject::create(renderer);
 }
 
+static PassRefPtr<AccessibilityObject> createFromNode(Node* node)
+{
+    return AccessibilityNodeObject::create(node);
+}
+
 AccessibilityObject* AXObjectCache::getOrCreate(Widget* widget)
 {
     if (!widget)
@@ -273,7 +295,32 @@ AccessibilityObject* AXObjectCache::getOrCreate(Widget* widget)
     attachWrapper(newObj.get());
     return newObj.get();
 }
-    
+
+AccessibilityObject* AXObjectCache::getOrCreate(Node* node)
+{
+    if (!node)
+        return 0;
+
+    if (AccessibilityObject* obj = get(node))
+        return obj;
+
+    if (node->renderer())
+        return getOrCreate(node->renderer());
+
+    // It's only allowed to create an AccessibilityObject from a Node if it's in a canvas subtree.
+    if (!node->parentElement() || !node->parentElement()->isInCanvasSubtree())
+        return 0;
+
+    RefPtr<AccessibilityObject> newObj = createFromNode(node);
+
+    getAXID(newObj.get());
+
+    m_nodeObjectMapping.set(node, newObj->axObjectID());
+    m_objects.set(newObj->axObjectID(), newObj);
+    attachWrapper(newObj.get());
+    return newObj.get();
+}
+
 AccessibilityObject* AXObjectCache::getOrCreate(RenderObject* renderer)
 {
     if (!renderer)
@@ -388,6 +435,24 @@ void AXObjectCache::remove(RenderObject* renderer)
     m_renderObjectMapping.remove(renderer);
 }
 
+void AXObjectCache::remove(Node* node)
+{
+    if (!node)
+        return;
+
+    removeNodeForUse(node);
+
+    // This is all safe even if we didn't have a mapping.
+    AXID axID = m_nodeObjectMapping.get(node);
+    remove(axID);
+    m_nodeObjectMapping.remove(node);
+
+    if (node->renderer()) {
+        remove(node->renderer());
+        return;
+    }
+}
+
 void AXObjectCache::remove(Widget* view)
 {
     if (!view)
index bc458e1..b768b7d 100644 (file)
@@ -70,6 +70,7 @@ public:
     // For AX objects with elements that back them.
     AccessibilityObject* getOrCreate(RenderObject*);
     AccessibilityObject* getOrCreate(Widget*);
+    AccessibilityObject* getOrCreate(Node*);
 
     // used for objects without backing elements
     AccessibilityObject* getOrCreate(AccessibilityRole);
@@ -77,8 +78,10 @@ public:
     // will only return the AccessibilityObject if it already exists
     AccessibilityObject* get(RenderObject*);
     AccessibilityObject* get(Widget*);
+    AccessibilityObject* get(Node*);
     
     void remove(RenderObject*);
+    void remove(Node*);
     void remove(Widget*);
     void remove(AXID);
 
@@ -182,6 +185,7 @@ private:
     HashMap<AXID, RefPtr<AccessibilityObject> > m_objects;
     HashMap<RenderObject*, AXID> m_renderObjectMapping;
     HashMap<Widget*, AXID> m_widgetObjectMapping;
+    HashMap<Node*, AXID> m_nodeObjectMapping;
     HashSet<Node*> m_textMarkerNodes;
     static bool gAccessibilityEnabled;
     static bool gAccessibilityEnhancedUserInterfaceEnabled;
@@ -204,9 +208,11 @@ inline AXObjectCache::AXObjectCache(const Document* doc) : m_document(const_cast
 inline AXObjectCache::~AXObjectCache() { }
 inline AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page*) { return 0; }
 inline AccessibilityObject* AXObjectCache::get(RenderObject*) { return 0; }
+inline AccessibilityObject* AXObjectCache::get(Node*) { return 0; }
 inline AccessibilityObject* AXObjectCache::get(Widget*) { return 0; }
 inline AccessibilityObject* AXObjectCache::getOrCreate(AccessibilityRole) { return 0; }
 inline AccessibilityObject* AXObjectCache::getOrCreate(RenderObject*) { return 0; }
+inline AccessibilityObject* AXObjectCache::getOrCreate(Node*) { return 0; }
 inline AccessibilityObject* AXObjectCache::getOrCreate(Widget*) { return 0; }
 inline AccessibilityObject* AXObjectCache::rootObject() { return 0; }
 inline AccessibilityObject* AXObjectCache::rootObjectForFrame(Frame*) { return 0; }
@@ -233,6 +239,7 @@ inline void AXObjectCache::postNotification(RenderObject*, AXNotification, bool
 inline void AXObjectCache::postPlatformNotification(AccessibilityObject*, AXNotification) { }
 inline void AXObjectCache::remove(AXID) { }
 inline void AXObjectCache::remove(RenderObject*) { }
+inline void AXObjectCache::remove(Node*) { }
 inline void AXObjectCache::remove(Widget*) { }
 inline void AXObjectCache::selectedChildrenChanged(RenderObject*) { }
 #endif
index cc12c3e..5e2106b 100644 (file)
@@ -43,6 +43,15 @@ namespace WebCore {
 AccessibilityARIAGrid::AccessibilityARIAGrid(RenderObject* renderer)
     : AccessibilityTable(renderer)
 {
+}
+
+AccessibilityARIAGrid::~AccessibilityARIAGrid()
+{
+}
+
+void AccessibilityARIAGrid::init()
+{
+    AccessibilityTable::init();
 #if ACCESSIBILITY_TABLES
     m_isAccessibilityTable = true;
 #else
@@ -50,13 +59,11 @@ AccessibilityARIAGrid::AccessibilityARIAGrid(RenderObject* renderer)
 #endif
 }
 
-AccessibilityARIAGrid::~AccessibilityARIAGrid()
-{
-}
-
 PassRefPtr<AccessibilityARIAGrid> AccessibilityARIAGrid::create(RenderObject* renderer)
 {
-    return adoptRef(new AccessibilityARIAGrid(renderer));
+    AccessibilityARIAGrid* obj = new AccessibilityARIAGrid(renderer);
+    obj->init();
+    return adoptRef(obj);
 }
 
 bool AccessibilityARIAGrid::addChild(AccessibilityObject* child, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount)
index 89b9172..063e18b 100644 (file)
@@ -44,6 +44,8 @@ private:
 public:
     static PassRefPtr<AccessibilityARIAGrid> create(RenderObject*);
     virtual ~AccessibilityARIAGrid();
+
+    virtual void init();
     
     virtual bool isAriaTable() const { return true; }    
     
index 55d09ad..9d16584 100644 (file)
@@ -48,7 +48,9 @@ AccessibilityARIAGridCell::~AccessibilityARIAGridCell()
 
 PassRefPtr<AccessibilityARIAGridCell> AccessibilityARIAGridCell::create(RenderObject* renderer)
 {
-    return adoptRef(new AccessibilityARIAGridCell(renderer));
+    AccessibilityARIAGridCell* obj = new AccessibilityARIAGridCell(renderer);
+    obj->init();
+    return adoptRef(obj);
 }
 
 AccessibilityObject* AccessibilityARIAGridCell::parentTable() const
index 2b96036..d32ba8a 100644 (file)
@@ -48,7 +48,9 @@ AccessibilityARIAGridRow::~AccessibilityARIAGridRow()
 
 PassRefPtr<AccessibilityARIAGridRow> AccessibilityARIAGridRow::create(RenderObject* renderer)
 {
-    return adoptRef(new AccessibilityARIAGridRow(renderer));
+    AccessibilityARIAGridRow* obj = new AccessibilityARIAGridRow(renderer);
+    obj->init();
+    return adoptRef(obj);
 }
 
 bool AccessibilityARIAGridRow::isARIATreeGridRow() const
index b329ad0..e353556 100644 (file)
@@ -34,6 +34,7 @@
 #include "AccessibilityListBox.cpp"
 #include "AccessibilityListBoxOption.cpp"
 #include "AccessibilityMediaControls.cpp"
+#include "AccessibilityNodeObject.cpp"
 #include "AccessibilityObject.cpp"
 #include "AccessibilityRenderObject.cpp"
 #include "AccessibilityScrollView.cpp"
index 073b0fc..6807314 100644 (file)
@@ -50,7 +50,9 @@ AccessibilityList::~AccessibilityList()
 
 PassRefPtr<AccessibilityList> AccessibilityList::create(RenderObject* renderer)
 {
-    return adoptRef(new AccessibilityList(renderer));
+    AccessibilityList* obj = new AccessibilityList(renderer);
+    obj->init();
+    return adoptRef(obj);
 }
 
 bool AccessibilityList::accessibilityIsIgnored() const
index 22e5b81..60f8546 100644 (file)
@@ -54,7 +54,9 @@ AccessibilityListBox::~AccessibilityListBox()
     
 PassRefPtr<AccessibilityListBox> AccessibilityListBox::create(RenderObject* renderer)
 {
-    return adoptRef(new AccessibilityListBox(renderer));
+    AccessibilityListBox* obj = new AccessibilityListBox(renderer);
+    obj->init();
+    return adoptRef(obj);
 }
     
 bool AccessibilityListBox::canSetSelectedChildrenAttribute() const
index 5636691..9778301 100644 (file)
@@ -67,8 +67,11 @@ PassRefPtr<AccessibilityObject> AccessibilityMediaControl::create(RenderObject*
     case MediaControlsPanel:
         return AccessibilityMediaControlsContainer::create(renderer);
 
-    default:
-        return adoptRef(new AccessibilityMediaControl(renderer));
+    default: {
+        AccessibilityMediaControl* obj = new AccessibilityMediaControl(renderer);
+        obj->init();
+        return adoptRef(obj);
+        }
     }
 }
 
@@ -207,7 +210,9 @@ AccessibilityMediaControlsContainer::AccessibilityMediaControlsContainer(RenderO
 
 PassRefPtr<AccessibilityObject> AccessibilityMediaControlsContainer::create(RenderObject* renderer)
 {
-    return adoptRef(new AccessibilityMediaControlsContainer(renderer));
+    AccessibilityMediaControlsContainer* obj = new AccessibilityMediaControlsContainer(renderer);
+    obj->init();
+    return adoptRef(obj);
 }
 
 String AccessibilityMediaControlsContainer::accessibilityDescription() const
@@ -251,7 +256,9 @@ AccessibilityMediaTimeline::AccessibilityMediaTimeline(RenderObject* renderer)
 
 PassRefPtr<AccessibilityObject> AccessibilityMediaTimeline::create(RenderObject* renderer)
 {
-    return adoptRef(new AccessibilityMediaTimeline(renderer));
+    AccessibilityMediaTimeline* obj = new AccessibilityMediaTimeline(renderer);
+    obj->init();
+    return adoptRef(obj);
 }
 
 String AccessibilityMediaTimeline::valueDescription() const
@@ -281,7 +288,9 @@ AccessibilityMediaTimeDisplay::AccessibilityMediaTimeDisplay(RenderObject* rende
 
 PassRefPtr<AccessibilityObject> AccessibilityMediaTimeDisplay::create(RenderObject* renderer)
 {
-    return adoptRef(new AccessibilityMediaTimeDisplay(renderer));
+    AccessibilityMediaTimeDisplay* obj = new AccessibilityMediaTimeDisplay(renderer);
+    obj->init();
+    return adoptRef(obj);
 }
 
 bool AccessibilityMediaTimeDisplay::accessibilityIsIgnored() const
index baec7d0..eebd784 100644 (file)
@@ -37,6 +37,13 @@ AccessibilityMenuList::AccessibilityMenuList(RenderMenuList* renderer)
 {
 }
 
+PassRefPtr<AccessibilityMenuList> AccessibilityMenuList::create(RenderMenuList* renderer)
+{
+    AccessibilityMenuList* obj = new AccessibilityMenuList(renderer);
+    obj->init();
+    return adoptRef(obj);
+}
+
 bool AccessibilityMenuList::press() const
 {
     RenderMenuList* menuList = static_cast<RenderMenuList*>(m_renderer);
index 089cdc3..281d736 100644 (file)
@@ -37,7 +37,7 @@ class RenderMenuList;
 
 class AccessibilityMenuList : public AccessibilityRenderObject {
 public:
-    static PassRefPtr<AccessibilityMenuList> create(RenderMenuList* renderer) { return adoptRef(new AccessibilityMenuList(renderer)); }
+    static PassRefPtr<AccessibilityMenuList> create(RenderMenuList* renderer);
 
     virtual bool isCollapsed() const;
     virtual bool press() const;
diff --git a/Source/WebCore/accessibility/AccessibilityNodeObject.cpp b/Source/WebCore/accessibility/AccessibilityNodeObject.cpp
new file mode 100644 (file)
index 0000000..c1eacad
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+* Copyright (C) 2012, Google Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1.  Redistributions of source code must retain the above copyright
+*     notice, this list of conditions and the following disclaimer.
+* 2.  Redistributions in binary form must reproduce the above copyright
+*     notice, this list of conditions and the following disclaimer in the
+*     documentation and/or other materials provided with the distribution.
+* 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+*     its contributors may be used to endorse or promote products derived
+*     from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "config.h"
+#include "AccessibilityNodeObject.h"
+
+#include "AXObjectCache.h"
+#include "AccessibilityImageMapLink.h"
+#include "AccessibilityListBox.h"
+#include "AccessibilitySpinButton.h"
+#include "AccessibilityTable.h"
+#include "EventNames.h"
+#include "FloatRect.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameSelection.h"
+#include "FrameView.h"
+#include "HTMLAreaElement.h"
+#include "HTMLFieldSetElement.h"
+#include "HTMLFormElement.h"
+#include "HTMLFrameElementBase.h"
+#include "HTMLImageElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLLabelElement.h"
+#include "HTMLLegendElement.h"
+#include "HTMLMapElement.h"
+#include "HTMLNames.h"
+#include "HTMLOptGroupElement.h"
+#include "HTMLOptionElement.h"
+#include "HTMLOptionsCollection.h"
+#include "HTMLPlugInImageElement.h"
+#include "HTMLSelectElement.h"
+#include "HTMLTextAreaElement.h"
+#include "HTMLTextFormControlElement.h"
+#include "HitTestRequest.h"
+#include "HitTestResult.h"
+#include "LocalizedStrings.h"
+#include "MathMLNames.h"
+#include "NodeList.h"
+#include "Page.h"
+#include "ProgressTracker.h"
+#include "Text.h"
+#include "TextControlInnerElements.h"
+#include "TextIterator.h"
+#include "Widget.h"
+#include "htmlediting.h"
+#include "visible_units.h"
+#include <wtf/StdLibExtras.h>
+#include <wtf/text/StringBuilder.h>
+#include <wtf/unicode/CharacterNames.h>
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+AccessibilityNodeObject::AccessibilityNodeObject(Node* node)
+    : AccessibilityObject()
+    , m_ariaRole(UnknownRole)
+    , m_childrenDirty(false)
+    , m_roleForMSAA(UnknownRole)
+    , m_node(node)
+{
+}
+
+AccessibilityNodeObject::~AccessibilityNodeObject()
+{
+    ASSERT(isDetached());
+}
+
+void AccessibilityNodeObject::init()
+{
+    m_role = determineAccessibilityRole();
+}
+
+PassRefPtr<AccessibilityNodeObject> AccessibilityNodeObject::create(Node* node)
+{
+    AccessibilityNodeObject* obj = new AccessibilityNodeObject(node);
+    obj->init();
+    return adoptRef(obj);
+}
+
+void AccessibilityNodeObject::detach()
+{
+    clearChildren();
+    AccessibilityObject::detach();
+    m_node = 0;
+}
+
+void AccessibilityNodeObject::childrenChanged()
+{
+    // This method is meant as a quick way of marking a portion of the accessibility tree dirty.
+    if (!node() && !renderer())
+        return;
+
+    axObjectCache()->postNotification(this, document(), AXObjectCache::AXChildrenChanged, true);
+
+    // Go up the accessibility parent chain, but only if the element already exists. This method is
+    // 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()) {
+        parent->setNeedsToUpdateChildren();
+
+        // These notifications always need to be sent because screenreaders are reliant on them to perform. 
+        // In other words, they need to be sent even when the screen reader has not accessed this live region since the last update.
+
+        // If this element supports ARIA live regions, then notify the AT of changes.
+        if (parent->supportsARIALiveRegion())
+            axObjectCache()->postNotification(parent, parent->document(), AXObjectCache::AXLiveRegionChanged, true);
+        
+        // If this element is an ARIA text control, notify the AT of changes.
+        if (parent->isARIATextControl() && !parent->isNativeTextControl() && !parent->node()->rendererIsEditable())
+            axObjectCache()->postNotification(parent, parent->document(), AXObjectCache::AXValueChanged, true);
+    }
+}
+
+void AccessibilityNodeObject::updateAccessibilityRole()
+{
+    bool ignoredStatus = accessibilityIsIgnored();
+    m_role = determineAccessibilityRole();
+    
+    // The AX hierarchy only needs to be updated if the ignored status of an element has changed.
+    if (ignoredStatus != accessibilityIsIgnored())
+        childrenChanged();
+}
+    
+AccessibilityObject* AccessibilityNodeObject::firstChild() const
+{
+    if (!node())
+        return 0;
+    
+    Node* firstChild = node()->firstChild();
+
+    if (!firstChild)
+        return 0;
+    
+    return axObjectCache()->getOrCreate(firstChild);
+}
+
+AccessibilityObject* AccessibilityNodeObject::lastChild() const
+{
+    if (!node())
+        return 0;
+    
+    Node* lastChild = node()->lastChild();
+    if (!lastChild)
+        return 0;
+    
+    return axObjectCache()->getOrCreate(lastChild);
+}
+
+AccessibilityObject* AccessibilityNodeObject::previousSibling() const
+{
+    if (!node())
+        return 0;
+
+    Node* previousSibling = node()->previousSibling();
+    if (!previousSibling)
+        return 0;
+
+    return axObjectCache()->getOrCreate(previousSibling);
+}
+
+AccessibilityObject* AccessibilityNodeObject::nextSibling() const
+{
+    if (!node())
+        return 0;
+
+    Node* nextSibling = node()->nextSibling();
+    if (!nextSibling)
+        return 0;
+
+    return axObjectCache()->getOrCreate(nextSibling);
+}
+    
+AccessibilityObject* AccessibilityNodeObject::parentObjectIfExists() const
+{
+    return parentObject();
+}
+    
+AccessibilityObject* AccessibilityNodeObject::parentObject() const
+{
+    if (!node())
+        return 0;
+
+    Node* parentObj = node()->parentNode();
+    if (parentObj)
+        return axObjectCache()->getOrCreate(parentObj);
+    
+    return 0;
+}
+
+LayoutRect AccessibilityNodeObject::elementRect() const
+{
+    return boundingBoxRect();
+}
+
+void AccessibilityNodeObject::setNode(Node* node)
+{
+    m_node = node;
+}
+
+Document* AccessibilityNodeObject::document() const
+{
+    if (!node())
+        return 0;
+    return node()->document();
+}
+
+AccessibilityRole AccessibilityNodeObject::determineAccessibilityRole()
+{
+    if (!node())
+        return UnknownRole;
+
+    m_ariaRole = determineAriaRoleAttribute();
+    
+    AccessibilityRole ariaRole = ariaRoleAttribute();
+    if (ariaRole != UnknownRole)
+        return ariaRole;
+
+    if (node()->isLink())
+        return WebCoreLinkRole;
+    if (node()->isTextNode())
+        return StaticTextRole;
+    if (node()->hasTagName(buttonTag))
+        return ariaHasPopup() ? PopUpButtonRole : ButtonRole;
+    if (node()->hasTagName(inputTag)) {
+        HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
+        if (input->isCheckbox())
+            return CheckBoxRole;
+        if (input->isRadioButton())
+            return RadioButtonRole;
+        if (input->isTextButton())
+            return ariaHasPopup() ? PopUpButtonRole : ButtonRole;
+        return TextFieldRole;
+    }
+    if (node()->hasTagName(selectTag)) {
+        HTMLSelectElement* selectElement = toHTMLSelectElement(node());
+        return selectElement->multiple() ? ListRole : PopUpButtonRole;
+    }
+    if (node()->isFocusable())
+        return GroupRole;
+    
+    return UnknownRole;
+}
+
+void AccessibilityNodeObject::addChildren()
+{
+    // If the need to add more children in addition to existing children arises, 
+    // childrenChanged should have been called, leaving the object with no children.
+    ASSERT(!m_haveChildren); 
+    
+    if (!m_node)
+        return;
+
+    // The only time we add children from the DOM tree to a node with a renderer is when it's a canvas.
+    if (renderer() && !m_node->hasTagName(canvasTag))
+        return;
+    
+    m_haveChildren = true;
+
+    for (Node* child = m_node->firstChild(); child; child = child->nextSibling()) {
+        RefPtr<AccessibilityObject> obj = axObjectCache()->getOrCreate(child);
+        obj->clearChildren();
+        if (obj->accessibilityIsIgnored()) {
+            AccessibilityChildrenVector children = obj->children();
+            size_t length = children.size();
+            for (size_t i = 0; i < length; ++i)
+                m_children.append(children[i]);
+        } else {
+            ASSERT(obj->parentObject() == this);
+            m_children.append(obj);
+        }
+    }
+}
+
+bool AccessibilityNodeObject::accessibilityIsIgnored() const
+{
+    return m_role == UnknownRole;
+}
+
+bool AccessibilityNodeObject::canSetFocusAttribute() const
+{
+    Node* node = this->node();
+
+    if (isWebArea())
+        return true;
+    
+    // NOTE: It would be more accurate to ask the document whether setFocusedNode() would
+    // do anything. For example, setFocusedNode() will do nothing if the current focused
+    // node will not relinquish the focus.
+    if (!node)
+        return false;
+
+    if (node->isElementNode() && !static_cast<Element*>(node)->isEnabledFormControl())
+        return false;
+
+    return node->supportsFocus();
+}
+
+AccessibilityRole AccessibilityNodeObject::determineAriaRoleAttribute() const
+{
+    const AtomicString& ariaRole = getAttribute(roleAttr);
+    if (ariaRole.isNull() || ariaRole.isEmpty())
+        return UnknownRole;
+    
+    AccessibilityRole role = ariaRoleToWebCoreRole(ariaRole);
+
+    // ARIA states if an item can get focus, it should not be presentational.
+    if (role == PresentationalRole && canSetFocusAttribute())
+        return UnknownRole;
+    
+    if (role == ButtonRole && ariaHasPopup())
+        role = PopUpButtonRole;
+
+    if (role == TextAreaRole && !ariaIsMultiline())
+        role = TextFieldRole;
+
+    role = remapAriaRoleDueToParent(role);
+    
+    if (role)
+        return role;
+
+    return UnknownRole;
+}
+
+AccessibilityRole AccessibilityNodeObject::ariaRoleAttribute() const
+{
+    return m_ariaRole;
+}
+
+AccessibilityRole AccessibilityNodeObject::remapAriaRoleDueToParent(AccessibilityRole role) const
+{
+    // Some objects change their role based on their parent.
+    // However, asking for the unignoredParent calls accessibilityIsIgnored(), which can trigger a loop. 
+    // While inside the call stack of creating an element, we need to avoid accessibilityIsIgnored().
+    // https://bugs.webkit.org/show_bug.cgi?id=65174
+
+    if (role != ListBoxOptionRole && role != MenuItemRole)
+        return role;
+    
+    for (AccessibilityObject* parent = parentObject(); parent && !parent->accessibilityIsIgnored(); parent = parent->parentObject()) {
+        AccessibilityRole parentAriaRole = parent->ariaRoleAttribute();
+
+        // Selects and listboxes both have options as child roles, but they map to different roles within WebCore.
+        if (role == ListBoxOptionRole && parentAriaRole == MenuRole)
+            return MenuItemRole;
+        // An aria "menuitem" may map to MenuButton or MenuItem depending on its parent.
+        if (role == MenuItemRole && parentAriaRole == GroupRole)
+            return MenuButtonRole;
+        
+        // If the parent had a different role, then we don't need to continue searching up the chain.
+        if (parentAriaRole)
+            break;
+    }
+    
+    return role;
+}   
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityNodeObject.h b/Source/WebCore/accessibility/AccessibilityNodeObject.h
new file mode 100644 (file)
index 0000000..9082f8b
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2012, Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ *     its contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef AccessibilityNodeObject_h
+#define AccessibilityNodeObject_h
+
+#include "AccessibilityObject.h"
+#include "LayoutTypes.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+    
+class AXObjectCache;
+class Element;
+class Frame;
+class FrameView;
+class HitTestResult;
+class HTMLAnchorElement;
+class HTMLAreaElement;
+class HTMLElement;
+class HTMLLabelElement;
+class HTMLMapElement;
+class HTMLSelectElement;
+class IntPoint;
+class IntSize;
+class Node;
+class RenderListBox;
+class RenderTextControl;
+class RenderView;
+class VisibleSelection;
+class Widget;
+    
+class AccessibilityNodeObject : public AccessibilityObject {
+protected:
+    AccessibilityNodeObject(Node*);
+public:
+    static PassRefPtr<AccessibilityNodeObject> create(Node*);
+    virtual ~AccessibilityNodeObject();
+
+    virtual void init();
+    
+    virtual bool isAccessibilityNodeObject() const { return true; }
+
+    virtual bool canSetFocusAttribute() const;
+    
+    virtual AccessibilityObject* firstChild() const;
+    virtual AccessibilityObject* lastChild() const;
+    virtual AccessibilityObject* previousSibling() const;
+    virtual AccessibilityObject* nextSibling() const;
+    virtual AccessibilityObject* parentObject() const;
+    virtual AccessibilityObject* parentObjectIfExists() const;
+
+    void setNode(Node*);
+    virtual Node* node() const { return m_node; }
+    virtual Document* document() const;
+
+    virtual void detach();
+    virtual void childrenChanged();
+    void updateAccessibilityRole();
+
+    virtual LayoutRect elementRect() const;
+
+protected:
+    AccessibilityRole m_ariaRole;
+    bool m_childrenDirty;
+    mutable AccessibilityRole m_roleForMSAA;
+
+    virtual bool isDetached() const { return !m_node; }
+
+    virtual AccessibilityRole determineAccessibilityRole();
+    virtual void addChildren();
+    virtual bool accessibilityIsIgnored() const;
+    AccessibilityRole ariaRoleAttribute() const;
+    AccessibilityRole determineAriaRoleAttribute() const;
+    AccessibilityRole remapAriaRoleDueToParent(AccessibilityRole) const;
+
+private:
+    Node* m_node;
+};
+
+inline AccessibilityNodeObject* toAccessibilityNodeObject(AccessibilityObject* object)
+{
+    ASSERT(!object || object->isAccessibilityNodeObject());
+    return static_cast<AccessibilityNodeObject*>(object);
+}
+
+inline const AccessibilityNodeObject* toAccessibilityNodeObject(const AccessibilityObject* object)
+{
+    ASSERT(!object || object->isAccessibilityNodeObject());
+    return static_cast<const AccessibilityNodeObject*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toAccessibilityNodeObject(const AccessibilityNodeObject*);
+
+} // namespace WebCore
+
+#endif // AccessibilityNodeObject_h
index 649ddd7..88b1f39 100644 (file)
@@ -317,6 +317,7 @@ public:
 
     typedef Vector<RefPtr<AccessibilityObject> > AccessibilityChildrenVector;
     
+    virtual bool isAccessibilityNodeObject() const { return false; }    
     virtual bool isAccessibilityRenderObject() const { return false; }
     virtual bool isAccessibilityScrollbar() const { return false; }
     virtual bool isAccessibilityScrollView() const { return false; }
index c7202f0..f57ed0a 100644 (file)
@@ -41,7 +41,9 @@ AccessibilityProgressIndicator::AccessibilityProgressIndicator(RenderProgress* r
 
 PassRefPtr<AccessibilityProgressIndicator> AccessibilityProgressIndicator::create(RenderProgress* renderer)
 {
-    return adoptRef(new AccessibilityProgressIndicator(renderer));
+    AccessibilityProgressIndicator* obj = new AccessibilityProgressIndicator(renderer);
+    obj->init();
+    return adoptRef(obj);
 }
 
 bool AccessibilityProgressIndicator::accessibilityIsIgnored() const
index 31e64e7..e08fa22 100644 (file)
@@ -93,14 +93,9 @@ namespace WebCore {
 using namespace HTMLNames;
 
 AccessibilityRenderObject::AccessibilityRenderObject(RenderObject* renderer)
-    : AccessibilityObject()
+    : AccessibilityNodeObject(renderer->node())
     , m_renderer(renderer)
-    , m_ariaRole(UnknownRole)
-    , m_childrenDirty(false)
-    , m_roleForMSAA(UnknownRole)
 {
-    m_role = determineAccessibilityRole();
-
 #ifndef NDEBUG
     m_renderer->setHasAXObject(true);
 #endif
@@ -111,21 +106,27 @@ AccessibilityRenderObject::~AccessibilityRenderObject()
     ASSERT(isDetached());
 }
 
+void AccessibilityRenderObject::init()
+{
+    AccessibilityNodeObject::init();
+}
+
 PassRefPtr<AccessibilityRenderObject> AccessibilityRenderObject::create(RenderObject* renderer)
 {
-    return adoptRef(new AccessibilityRenderObject(renderer));
+    AccessibilityRenderObject* obj = new AccessibilityRenderObject(renderer);
+    obj->init();
+    return adoptRef(obj);
 }
 
 void AccessibilityRenderObject::detach()
 {
-    clearChildren();
-    AccessibilityObject::detach();
+    AccessibilityNodeObject::detach();
     
 #ifndef NDEBUG
     if (m_renderer)
         m_renderer->setHasAXObject(false);
 #endif
-    m_renderer = 0;    
+    m_renderer = 0;
 }
 
 RenderBoxModelObject* AccessibilityRenderObject::renderBoxModelObject() const
@@ -135,6 +136,12 @@ RenderBoxModelObject* AccessibilityRenderObject::renderBoxModelObject() const
     return toRenderBoxModelObject(m_renderer);
 }
 
+void AccessibilityRenderObject::setRenderer(RenderObject* renderer)
+{
+    m_renderer = renderer;
+    setNode(renderer->node());
+}
+
 static inline bool isInlineWithContinuation(RenderObject* object)
 {
     if (!object->isBoxModelObject())
@@ -3084,75 +3091,6 @@ AccessibilityObject* AccessibilityRenderObject::observableObject() const
     return 0;
 }
 
-AccessibilityRole AccessibilityRenderObject::remapAriaRoleDueToParent(AccessibilityRole role) const
-{
-    // Some objects change their role based on their parent.
-    // However, asking for the unignoredParent calls accessibilityIsIgnored(), which can trigger a loop. 
-    // While inside the call stack of creating an element, we need to avoid accessibilityIsIgnored().
-    // https://bugs.webkit.org/show_bug.cgi?id=65174
-
-    if (role != ListBoxOptionRole && role != MenuItemRole)
-        return role;
-    
-    for (AccessibilityObject* parent = parentObject(); parent && !parent->accessibilityIsIgnored(); parent = parent->parentObject()) {
-        AccessibilityRole parentAriaRole = parent->ariaRoleAttribute();
-
-        // Selects and listboxes both have options as child roles, but they map to different roles within WebCore.
-        if (role == ListBoxOptionRole && parentAriaRole == MenuRole)
-            return MenuItemRole;
-        // An aria "menuitem" may map to MenuButton or MenuItem depending on its parent.
-        if (role == MenuItemRole && parentAriaRole == GroupRole)
-            return MenuButtonRole;
-        
-        // If the parent had a different role, then we don't need to continue searching up the chain.
-        if (parentAriaRole)
-            break;
-    }
-    
-    return role;
-}
-    
-AccessibilityRole AccessibilityRenderObject::determineAriaRoleAttribute() const
-{
-    const AtomicString& ariaRole = getAttribute(roleAttr);
-    if (ariaRole.isNull() || ariaRole.isEmpty())
-        return UnknownRole;
-    
-    AccessibilityRole role = ariaRoleToWebCoreRole(ariaRole);
-
-    // ARIA states if an item can get focus, it should not be presentational.
-    if (role == PresentationalRole && canSetFocusAttribute())
-        return UnknownRole;
-    
-    if (role == ButtonRole && ariaHasPopup())
-        role = PopUpButtonRole;
-
-    if (role == TextAreaRole && !ariaIsMultiline())
-        role = TextFieldRole;
-
-    role = remapAriaRoleDueToParent(role);
-    
-    if (role)
-        return role;
-
-    return UnknownRole;
-}
-
-AccessibilityRole AccessibilityRenderObject::ariaRoleAttribute() const
-{
-    return m_ariaRole;
-}
-    
-void AccessibilityRenderObject::updateAccessibilityRole()
-{
-    bool ignoredStatus = accessibilityIsIgnored();
-    m_role = determineAccessibilityRole();
-    
-    // The AX hierarchy only needs to be updated if the ignored status of an element has changed.
-    if (ignoredStatus != accessibilityIsIgnored())
-        childrenChanged();
-}
-    
 bool AccessibilityRenderObject::isDescendantOfElementType(const QualifiedName& tagName) const
 {
     for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) {
@@ -3389,25 +3327,6 @@ bool AccessibilityRenderObject::ariaRoleHasPresentationalChildren() const
     }
 }
 
-bool AccessibilityRenderObject::canSetFocusAttribute() const
-{
-    Node* node = this->node();
-
-    if (isWebArea())
-        return true;
-    
-    // NOTE: It would be more accurate to ask the document whether setFocusedNode() would
-    // do anything.  For example, setFocusedNode() will do nothing if the current focused
-    // node will not relinquish the focus.
-    if (!node)
-        return false;
-
-    if (node->isElementNode() && !static_cast<Element*>(node)->isEnabledFormControl())
-        return false;
-
-    return node->supportsFocus();
-}
-    
 bool AccessibilityRenderObject::canSetExpandedAttribute() const
 {
     // An object can be expanded if it aria-expanded is true or false.
@@ -3454,39 +3373,15 @@ void AccessibilityRenderObject::contentChanged()
     }
 }
     
-void AccessibilityRenderObject::childrenChanged()
-{
-    // This method is meant as a quick way of marking a portion of the accessibility tree dirty.
-    if (!m_renderer)
-        return;
-
-    axObjectCache()->postNotification(this, document(), AXObjectCache::AXChildrenChanged, true);
-
-    // Go up the accessibility parent chain, but only if the element already exists. This method is
-    // 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()) {
-        parent->setNeedsToUpdateChildren();
-
-        // These notifications always need to be sent because screenreaders are reliant on them to perform. 
-        // In other words, they need to be sent even when the screen reader has not accessed this live region since the last update.
-
-        // If this element supports ARIA live regions, then notify the AT of changes.
-        if (parent->supportsARIALiveRegion())
-            axObjectCache()->postNotification(parent, parent->document(), AXObjectCache::AXLiveRegionChanged, true);
-        
-        // If this element is an ARIA text control, notify the AT of changes.
-        if (parent->isARIATextControl() && !parent->isNativeTextControl() && !parent->node()->rendererIsEditable())
-            axObjectCache()->postNotification(parent, parent->document(), AXObjectCache::AXValueChanged, true);
-    }
-}
-    
 bool AccessibilityRenderObject::canHaveChildren() const
 {
     if (!m_renderer)
         return false;
     
+    // Canvas is a special case; its role is ImageRole but it is allowed to have children.
+    if (node() && node()->hasTagName(canvasTag))
+        return true;
+
     // Elements that should not have children
     switch (roleValue()) {
     case ImageRole:
@@ -3559,6 +3454,15 @@ void AccessibilityRenderObject::addTextFieldChildren()
     m_children.append(axSpinButton);
 }
 
+void AccessibilityRenderObject::addCanvasChildren()
+{
+    // If it's a canvas, it won't have rendered children, but it might have accessible fallback content.
+    if (node() && node()->hasTagName(canvasTag)) {
+        AccessibilityNodeObject::addChildren();
+        return;
+    }
+}
+
 void AccessibilityRenderObject::addAttachmentChildren()
 {
     if (!isAttachment())
@@ -3595,11 +3499,7 @@ void AccessibilityRenderObject::addChildren()
     // If the need to add more children in addition to existing children arises, 
     // childrenChanged should have been called, leaving the object with no children.
     ASSERT(!m_haveChildren); 
-    
-    // nothing to add if there is no RenderObject
-    if (!m_renderer)
-        return;
-    
+
     m_haveChildren = true;
     
     if (!canHaveChildren())
@@ -3607,7 +3507,6 @@ void AccessibilityRenderObject::addChildren()
     
     // add all unignored acc children
     for (RefPtr<AccessibilityObject> obj = firstChild(); obj; obj = obj->nextSibling()) {
-        
         // If the parent is asking for this child's children, then either it's the first time (and clearing is a no-op), 
         // or its visibility has changed. In the latter case, this child may have a stale child cached. 
         // This can prevent aria-hidden changes from working correctly. Hence, whenever a parent is getting children, ensure data is not stale.
@@ -3627,6 +3526,7 @@ void AccessibilityRenderObject::addChildren()
     addAttachmentChildren();
     addImageMapChildren();
     addTextFieldChildren();
+    addCanvasChildren();
 
 #if PLATFORM(MAC)
     updateAttachmentViewParents();
index a43b71d..264e70b 100644 (file)
@@ -29,7 +29,7 @@
 #ifndef AccessibilityRenderObject_h
 #define AccessibilityRenderObject_h
 
-#include "AccessibilityObject.h"
+#include "AccessibilityNodeObject.h"
 #include "LayoutTypes.h"
 #include <wtf/Forward.h>
 
@@ -55,7 +55,7 @@ class RenderView;
 class VisibleSelection;
 class Widget;
     
-class AccessibilityRenderObject : public AccessibilityObject {
+class AccessibilityRenderObject : public AccessibilityNodeObject {
 protected:
     AccessibilityRenderObject(RenderObject*);
 public:
@@ -63,6 +63,8 @@ public:
     virtual ~AccessibilityRenderObject();
     
     virtual bool isAccessibilityRenderObject() const { return true; }
+
+    virtual void init();
     
     virtual bool isAnchor() const;
     virtual bool isAttachment() const;
@@ -111,7 +113,6 @@ public:
     virtual bool hasSameStyle(RenderObject*) const;
     virtual bool hasUnderline() const;
 
-    virtual bool canSetFocusAttribute() const;
     virtual bool canSetTextRangeAttributes() const;
     virtual bool canSetValueAttribute() const;
     virtual bool canSetExpandedAttribute() const;
@@ -149,10 +150,8 @@ public:
 
     virtual void ariaOwnsElements(AccessibilityChildrenVector&) const;
     virtual bool supportsARIAOwns() const;
-    virtual AccessibilityRole ariaRoleAttribute() const;
     virtual bool isPresentationalChildOfAriaRole() const;
     virtual bool ariaRoleHasPresentationalChildren() const;
-    void updateAccessibilityRole();
     
     // Should be called on the root accessibility object to kick off a hit test.
     virtual AccessibilityObject* accessibilityHitTest(const IntPoint&) const;
@@ -168,14 +167,15 @@ public:
     virtual LayoutRect elementRect() const;
     virtual IntPoint clickPoint();
     
-    void setRenderer(RenderObject* renderer) { m_renderer = renderer; }
+    void setRenderer(RenderObject*);
     virtual RenderObject* renderer() const { return m_renderer; }
     RenderBoxModelObject* renderBoxModelObject() const;
     virtual Node* node() const;
 
+    virtual Document* document() const;
+
     RenderView* topRenderer() const;
     RenderTextControl* textControl() const;
-    Document* document() const;
     FrameView* topDocumentFrameView() const;  
     Document* topDocument() const;
     HTMLLabelElement* labelElementContainer() const;
@@ -214,7 +214,6 @@ public:
     virtual void decrement();
     
     virtual void detach();
-    virtual void childrenChanged();
     virtual void contentChanged();
     virtual void addChildren();
     virtual bool canHaveChildren() const;
@@ -260,8 +259,6 @@ public:
 
 protected:
     RenderObject* m_renderer;
-    AccessibilityRole m_ariaRole;
-    bool m_childrenDirty;
     
     void setRenderObject(RenderObject* renderer) { m_renderer = renderer; }
     void ariaLabeledByElements(Vector<Element*>& elements) const;
@@ -271,6 +268,8 @@ protected:
     
     virtual bool isDetached() const { return !m_renderer; }
 
+    virtual AccessibilityRole determineAccessibilityRole();
+
 private:
     void ariaListboxSelectedChildren(AccessibilityChildrenVector&);
     void ariaListboxVisibleChildren(AccessibilityChildrenVector&);
@@ -286,9 +285,6 @@ private:
 
     Element* menuElementForMenuButton() const;
     Element* menuItemElementForMenu() const;
-    AccessibilityRole determineAccessibilityRole();
-    AccessibilityRole determineAriaRoleAttribute() const;
-    AccessibilityRole remapAriaRoleDueToParent(AccessibilityRole) const;
 
     bool isTabItemSelected() const;
     void alterSliderValue(bool increase);
@@ -305,6 +301,7 @@ private:
 
     void addTextFieldChildren();
     void addImageMapChildren();
+    void addCanvasChildren();
     void addAttachmentChildren();
 #if PLATFORM(MAC)
     void updateAttachmentViewParents();
@@ -328,8 +325,6 @@ private:
     virtual bool ariaLiveRegionBusy() const;    
     
     bool inheritsPresentationalRole() const;
-    
-    mutable AccessibilityRole m_roleForMSAA;
 };
 
 inline AccessibilityRenderObject* toAccessibilityRenderObject(AccessibilityObject* object)
index 56cef7f..7a0abd0 100644 (file)
@@ -47,7 +47,9 @@ AccessibilitySlider::AccessibilitySlider(RenderObject* renderer)
 
 PassRefPtr<AccessibilitySlider> AccessibilitySlider::create(RenderObject* renderer)
 {
-    return adoptRef(new AccessibilitySlider(renderer));
+    AccessibilitySlider* obj = new AccessibilitySlider(renderer);
+    obj->init();
+    return adoptRef(obj);
 }
 
 AccessibilityOrientation AccessibilitySlider::orientation() const
index 4330055..b1d155d 100644 (file)
@@ -51,6 +51,15 @@ AccessibilityTable::AccessibilityTable(RenderObject* renderer)
     : AccessibilityRenderObject(renderer),
     m_headerContainer(0)
 {
+}
+
+AccessibilityTable::~AccessibilityTable()
+{
+}
+
+void AccessibilityTable::init()
+{
+    AccessibilityRenderObject::init();
 #if ACCESSIBILITY_TABLES
     m_isAccessibilityTable = isTableExposableThroughAccessibility();
 #else
@@ -58,13 +67,11 @@ AccessibilityTable::AccessibilityTable(RenderObject* renderer)
 #endif
 }
 
-AccessibilityTable::~AccessibilityTable()
-{
-}
-
 PassRefPtr<AccessibilityTable> AccessibilityTable::create(RenderObject* renderer)
 {
-    return adoptRef(new AccessibilityTable(renderer));
+    AccessibilityTable* obj = new AccessibilityTable(renderer);
+    obj->init();
+    return adoptRef(obj);
 }
 
 bool AccessibilityTable::hasARIARole() const
index 7fd7cc6..2ee1bc0 100644 (file)
@@ -50,6 +50,8 @@ public:
     static PassRefPtr<AccessibilityTable> create(RenderObject*);
     virtual ~AccessibilityTable();
 
+    virtual void init();
+
     virtual bool isAccessibilityTable() const;
     virtual bool isDataTable() const;
 
index 7ec87b2..f2b1c95 100644 (file)
@@ -51,7 +51,9 @@ AccessibilityTableCell::~AccessibilityTableCell()
 
 PassRefPtr<AccessibilityTableCell> AccessibilityTableCell::create(RenderObject* renderer)
 {
-    return adoptRef(new AccessibilityTableCell(renderer));
+    AccessibilityTableCell* obj = new AccessibilityTableCell(renderer);
+    obj->init();
+    return adoptRef(obj);
 }
 
 bool AccessibilityTableCell::accessibilityIsIgnored() const
index cfbdfb5..b4596d8 100644 (file)
@@ -54,7 +54,9 @@ AccessibilityTableRow::~AccessibilityTableRow()
 
 PassRefPtr<AccessibilityTableRow> AccessibilityTableRow::create(RenderObject* renderer)
 {
-    return adoptRef(new AccessibilityTableRow(renderer));
+    AccessibilityTableRow* obj = new AccessibilityTableRow(renderer);
+    obj->init();
+    return adoptRef(obj);
 }
 
 AccessibilityRole AccessibilityTableRow::roleValue() const