summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/accessibility
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-06 11:45:16 +0100
committerSteve Block <steveblock@google.com>2011-05-12 13:44:10 +0100
commitcad810f21b803229eb11403f9209855525a25d57 (patch)
tree29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/accessibility
parent121b0cf4517156d0ac5111caf9830c51b69bae8f (diff)
downloadexternal_webkit-cad810f21b803229eb11403f9209855525a25d57.zip
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/accessibility')
-rw-r--r--Source/WebCore/accessibility/AXObjectCache.cpp632
-rw-r--r--Source/WebCore/accessibility/AXObjectCache.h198
-rw-r--r--Source/WebCore/accessibility/AccessibilityARIAGrid.cpp154
-rw-r--r--Source/WebCore/accessibility/AccessibilityARIAGrid.h63
-rw-r--r--Source/WebCore/accessibility/AccessibilityARIAGridCell.cpp96
-rw-r--r--Source/WebCore/accessibility/AccessibilityARIAGridCell.h55
-rw-r--r--Source/WebCore/accessibility/AccessibilityARIAGridRow.cpp141
-rw-r--r--Source/WebCore/accessibility/AccessibilityARIAGridRow.h56
-rw-r--r--Source/WebCore/accessibility/AccessibilityAllInOne.cpp46
-rw-r--r--Source/WebCore/accessibility/AccessibilityImageMapLink.cpp149
-rw-r--r--Source/WebCore/accessibility/AccessibilityImageMapLink.h83
-rw-r--r--Source/WebCore/accessibility/AccessibilityList.cpp111
-rw-r--r--Source/WebCore/accessibility/AccessibilityList.h62
-rw-r--r--Source/WebCore/accessibility/AccessibilityListBox.cpp197
-rw-r--r--Source/WebCore/accessibility/AccessibilityListBox.h64
-rw-r--r--Source/WebCore/accessibility/AccessibilityListBoxOption.cpp219
-rw-r--r--Source/WebCore/accessibility/AccessibilityListBoxOption.h79
-rw-r--r--Source/WebCore/accessibility/AccessibilityMediaControls.cpp331
-rw-r--r--Source/WebCore/accessibility/AccessibilityMediaControls.h117
-rw-r--r--Source/WebCore/accessibility/AccessibilityMenuList.cpp90
-rw-r--r--Source/WebCore/accessibility/AccessibilityMenuList.h61
-rw-r--r--Source/WebCore/accessibility/AccessibilityMenuListOption.cpp118
-rw-r--r--Source/WebCore/accessibility/AccessibilityMenuListOption.h70
-rw-r--r--Source/WebCore/accessibility/AccessibilityMenuListPopup.cpp126
-rw-r--r--Source/WebCore/accessibility/AccessibilityMenuListPopup.h68
-rw-r--r--Source/WebCore/accessibility/AccessibilityObject.cpp1071
-rw-r--r--Source/WebCore/accessibility/AccessibilityObject.h614
-rw-r--r--Source/WebCore/accessibility/AccessibilityProgressIndicator.cpp78
-rw-r--r--Source/WebCore/accessibility/AccessibilityProgressIndicator.h57
-rw-r--r--Source/WebCore/accessibility/AccessibilityRenderObject.cpp3688
-rw-r--r--Source/WebCore/accessibility/AccessibilityRenderObject.h332
-rw-r--r--Source/WebCore/accessibility/AccessibilityScrollView.cpp165
-rw-r--r--Source/WebCore/accessibility/AccessibilityScrollView.h79
-rw-r--r--Source/WebCore/accessibility/AccessibilityScrollbar.cpp105
-rw-r--r--Source/WebCore/accessibility/AccessibilityScrollbar.h72
-rw-r--r--Source/WebCore/accessibility/AccessibilitySlider.cpp184
-rw-r--r--Source/WebCore/accessibility/AccessibilitySlider.h93
-rw-r--r--Source/WebCore/accessibility/AccessibilityTable.cpp567
-rw-r--r--Source/WebCore/accessibility/AccessibilityTable.h97
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableCell.cpp179
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableCell.h65
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableColumn.cpp200
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableColumn.h75
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableHeaderContainer.cpp98
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableHeaderContainer.h66
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableRow.cpp125
-rw-r--r--Source/WebCore/accessibility/AccessibilityTableRow.h65
-rw-r--r--Source/WebCore/accessibility/chromium/AXObjectCacheChromium.cpp127
-rw-r--r--Source/WebCore/accessibility/chromium/AccessibilityObjectChromium.cpp45
-rw-r--r--Source/WebCore/accessibility/chromium/AccessibilityObjectWrapper.h54
-rw-r--r--Source/WebCore/accessibility/efl/AccessibilityObjectEfl.cpp36
-rw-r--r--Source/WebCore/accessibility/gtk/AXObjectCacheAtk.cpp166
-rw-r--r--Source/WebCore/accessibility/gtk/AccessibilityObjectAtk.cpp136
-rw-r--r--Source/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.cpp2489
-rw-r--r--Source/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.h67
-rw-r--r--Source/WebCore/accessibility/gtk/WebKitAccessibleHyperlink.cpp392
-rw-r--r--Source/WebCore/accessibility/gtk/WebKitAccessibleHyperlink.h63
-rw-r--r--Source/WebCore/accessibility/mac/AXObjectCacheMac.mm139
-rw-r--r--Source/WebCore/accessibility/mac/AccessibilityObjectMac.mm66
-rw-r--r--Source/WebCore/accessibility/mac/AccessibilityObjectWrapper.h60
-rw-r--r--Source/WebCore/accessibility/mac/AccessibilityObjectWrapper.mm2850
-rw-r--r--Source/WebCore/accessibility/qt/AccessibilityObjectQt.cpp42
-rw-r--r--Source/WebCore/accessibility/win/AXObjectCacheWin.cpp147
-rw-r--r--Source/WebCore/accessibility/win/AccessibilityObjectWin.cpp48
-rw-r--r--Source/WebCore/accessibility/win/AccessibilityObjectWrapperWin.h54
-rw-r--r--Source/WebCore/accessibility/wx/AccessibilityObjectWx.cpp42
66 files changed, 18484 insertions, 0 deletions
diff --git a/Source/WebCore/accessibility/AXObjectCache.cpp b/Source/WebCore/accessibility/AXObjectCache.cpp
new file mode 100644
index 0000000..d0d19f7
--- /dev/null
+++ b/Source/WebCore/accessibility/AXObjectCache.cpp
@@ -0,0 +1,632 @@
+/*
+ * Copyright (C) 2008, 2009, 2010 Apple 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 "AXObjectCache.h"
+
+#include "AccessibilityARIAGrid.h"
+#include "AccessibilityARIAGridCell.h"
+#include "AccessibilityARIAGridRow.h"
+#include "AccessibilityImageMapLink.h"
+#include "AccessibilityList.h"
+#include "AccessibilityListBox.h"
+#include "AccessibilityListBoxOption.h"
+#include "AccessibilityMediaControls.h"
+#include "AccessibilityMenuList.h"
+#include "AccessibilityMenuListOption.h"
+#include "AccessibilityMenuListPopup.h"
+#include "AccessibilityProgressIndicator.h"
+#include "AccessibilityRenderObject.h"
+#include "AccessibilityScrollView.h"
+#include "AccessibilityScrollbar.h"
+#include "AccessibilitySlider.h"
+#include "AccessibilityTable.h"
+#include "AccessibilityTableCell.h"
+#include "AccessibilityTableColumn.h"
+#include "AccessibilityTableHeaderContainer.h"
+#include "AccessibilityTableRow.h"
+#include "Document.h"
+#include "FocusController.h"
+#include "Frame.h"
+#include "HTMLAreaElement.h"
+#include "HTMLImageElement.h"
+#include "HTMLNames.h"
+#if ENABLE(VIDEO)
+#include "MediaControlElements.h"
+#endif
+#include "InputElement.h"
+#include "Page.h"
+#include "RenderListBox.h"
+#include "RenderMenuList.h"
+#include "RenderProgress.h"
+#include "RenderSlider.h"
+#include "RenderTable.h"
+#include "RenderTableCell.h"
+#include "RenderTableRow.h"
+#include "RenderView.h"
+#include "ScrollView.h"
+
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+bool AXObjectCache::gAccessibilityEnabled = false;
+bool AXObjectCache::gAccessibilityEnhancedUserInterfaceEnabled = false;
+
+AXObjectCache::AXObjectCache(const Document* doc)
+ : m_notificationPostTimer(this, &AXObjectCache::notificationPostTimerFired)
+{
+ m_document = const_cast<Document*>(doc);
+}
+
+AXObjectCache::~AXObjectCache()
+{
+ HashMap<AXID, RefPtr<AccessibilityObject> >::iterator end = m_objects.end();
+ for (HashMap<AXID, RefPtr<AccessibilityObject> >::iterator it = m_objects.begin(); it != end; ++it) {
+ AccessibilityObject* obj = (*it).second.get();
+ detachWrapper(obj);
+ obj->detach();
+ removeAXID(obj);
+ }
+}
+
+AccessibilityObject* AXObjectCache::focusedImageMapUIElement(HTMLAreaElement* areaElement)
+{
+ // Find the corresponding accessibility object for the HTMLAreaElement. This should be
+ // in the list of children for its corresponding image.
+ if (!areaElement)
+ return 0;
+
+ HTMLImageElement* imageElement = areaElement->imageElement();
+ if (!imageElement)
+ return 0;
+
+ AccessibilityObject* axRenderImage = areaElement->document()->axObjectCache()->getOrCreate(imageElement->renderer());
+ if (!axRenderImage)
+ return 0;
+
+ AccessibilityObject::AccessibilityChildrenVector imageChildren = axRenderImage->children();
+ unsigned count = imageChildren.size();
+ for (unsigned k = 0; k < count; ++k) {
+ AccessibilityObject* child = imageChildren[k].get();
+ if (!child->isImageMapLink())
+ continue;
+
+ if (static_cast<AccessibilityImageMapLink*>(child)->areaElement() == areaElement)
+ return child;
+ }
+
+ return 0;
+}
+
+AccessibilityObject* AXObjectCache::focusedUIElementForPage(const Page* page)
+{
+ // get the focused node in the page
+ Document* focusedDocument = page->focusController()->focusedOrMainFrame()->document();
+ Node* focusedNode = focusedDocument->focusedNode();
+ if (!focusedNode)
+ focusedNode = focusedDocument;
+
+ if (focusedNode->hasTagName(areaTag))
+ return focusedImageMapUIElement(static_cast<HTMLAreaElement*>(focusedNode));
+
+ RenderObject* focusedNodeRenderer = focusedNode->renderer();
+ if (!focusedNodeRenderer)
+ return 0;
+
+ AccessibilityObject* obj = focusedNodeRenderer->document()->axObjectCache()->getOrCreate(focusedNodeRenderer);
+
+ if (obj->shouldFocusActiveDescendant()) {
+ if (AccessibilityObject* descendant = obj->activeDescendant())
+ obj = descendant;
+ }
+
+ // the HTML element, for example, is focusable but has an AX object that is ignored
+ if (obj->accessibilityIsIgnored())
+ obj = obj->parentObjectUnignored();
+
+ return obj;
+}
+
+AccessibilityObject* AXObjectCache::get(Widget* widget)
+{
+ if (!widget)
+ return 0;
+
+ AXID axID = m_widgetObjectMapping.get(widget);
+ ASSERT(!HashTraits<AXID>::isDeletedValue(axID));
+ if (!axID)
+ return 0;
+
+ return m_objects.get(axID).get();
+}
+
+AccessibilityObject* AXObjectCache::get(RenderObject* renderer)
+{
+ if (!renderer)
+ return 0;
+
+ AXID axID = m_renderObjectMapping.get(renderer);
+ 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)
+{
+ if (!node || !node->isElementNode())
+ return false;
+
+ return equalIgnoringCase(static_cast<Element*>(node)->getAttribute(roleAttr), role);
+}
+
+static PassRefPtr<AccessibilityObject> createFromRenderer(RenderObject* renderer)
+{
+ // FIXME: How could renderer->node() ever not be an Element?
+ Node* node = renderer->node();
+
+ // If the node is aria role="list" or the aria role is empty and its a
+ // ul/ol/dl type (it shouldn't be a list if aria says otherwise).
+ if (node && ((nodeHasRole(node, "list") || nodeHasRole(node, "directory"))
+ || (nodeHasRole(node, nullAtom) && (node->hasTagName(ulTag) || node->hasTagName(olTag) || node->hasTagName(dlTag)))))
+ return AccessibilityList::create(renderer);
+
+ // aria tables
+ if (nodeHasRole(node, "grid") || nodeHasRole(node, "treegrid"))
+ return AccessibilityARIAGrid::create(renderer);
+ if (nodeHasRole(node, "row"))
+ return AccessibilityARIAGridRow::create(renderer);
+ if (nodeHasRole(node, "gridcell") || nodeHasRole(node, "columnheader") || nodeHasRole(node, "rowheader"))
+ return AccessibilityARIAGridCell::create(renderer);
+
+#if ENABLE(VIDEO)
+ // media controls
+ if (node && node->isMediaControlElement())
+ return AccessibilityMediaControl::create(renderer);
+#endif
+
+ if (renderer->isBoxModelObject()) {
+ RenderBoxModelObject* cssBox = toRenderBoxModelObject(renderer);
+ if (cssBox->isListBox())
+ return AccessibilityListBox::create(toRenderListBox(cssBox));
+ if (cssBox->isMenuList())
+ return AccessibilityMenuList::create(toRenderMenuList(cssBox));
+
+ // standard tables
+ if (cssBox->isTable())
+ return AccessibilityTable::create(toRenderTable(cssBox));
+ if (cssBox->isTableRow())
+ return AccessibilityTableRow::create(toRenderTableRow(cssBox));
+ if (cssBox->isTableCell())
+ return AccessibilityTableCell::create(toRenderTableCell(cssBox));
+
+#if ENABLE(PROGRESS_TAG)
+ // progress bar
+ if (cssBox->isProgress())
+ return AccessibilityProgressIndicator::create(toRenderProgress(cssBox));
+#endif
+
+ // input type=range
+ if (cssBox->isSlider())
+ return AccessibilitySlider::create(toRenderSlider(cssBox));
+ }
+
+ return AccessibilityRenderObject::create(renderer);
+}
+
+AccessibilityObject* AXObjectCache::getOrCreate(Widget* widget)
+{
+ if (!widget)
+ return 0;
+
+ if (AccessibilityObject* obj = get(widget))
+ return obj;
+
+ RefPtr<AccessibilityObject> newObj = 0;
+ if (widget->isFrameView())
+ newObj = AccessibilityScrollView::create(static_cast<ScrollView*>(widget));
+ else if (widget->isScrollbar())
+ newObj = AccessibilityScrollbar::create(static_cast<Scrollbar*>(widget));
+
+ getAXID(newObj.get());
+
+ m_widgetObjectMapping.set(widget, newObj->axObjectID());
+ m_objects.set(newObj->axObjectID(), newObj);
+ attachWrapper(newObj.get());
+ return newObj.get();
+}
+
+AccessibilityObject* AXObjectCache::getOrCreate(RenderObject* renderer)
+{
+ if (!renderer)
+ return 0;
+
+ if (AccessibilityObject* obj = get(renderer))
+ return obj;
+
+ RefPtr<AccessibilityObject> newObj = createFromRenderer(renderer);
+
+ getAXID(newObj.get());
+
+ m_renderObjectMapping.set(renderer, newObj->axObjectID());
+ m_objects.set(newObj->axObjectID(), newObj);
+ attachWrapper(newObj.get());
+ return newObj.get();
+}
+
+AccessibilityObject* AXObjectCache::rootObject()
+{
+ return getOrCreate(m_document->view());
+}
+
+AccessibilityObject* AXObjectCache::getOrCreate(AccessibilityRole role)
+{
+ RefPtr<AccessibilityObject> obj = 0;
+
+ // will be filled in...
+ switch (role) {
+ case ListBoxOptionRole:
+ obj = AccessibilityListBoxOption::create();
+ break;
+ case ImageMapLinkRole:
+ obj = AccessibilityImageMapLink::create();
+ break;
+ case ColumnRole:
+ obj = AccessibilityTableColumn::create();
+ break;
+ case TableHeaderContainerRole:
+ obj = AccessibilityTableHeaderContainer::create();
+ break;
+ case SliderThumbRole:
+ obj = AccessibilitySliderThumb::create();
+ break;
+ case MenuListPopupRole:
+ obj = AccessibilityMenuListPopup::create();
+ break;
+ case MenuListOptionRole:
+ obj = AccessibilityMenuListOption::create();
+ break;
+ default:
+ obj = 0;
+ }
+
+ if (obj)
+ getAXID(obj.get());
+ else
+ return 0;
+
+ m_objects.set(obj->axObjectID(), obj);
+ attachWrapper(obj.get());
+ return obj.get();
+}
+
+void AXObjectCache::remove(AXID axID)
+{
+ if (!axID)
+ return;
+
+ // first fetch object to operate some cleanup functions on it
+ AccessibilityObject* obj = m_objects.get(axID).get();
+ if (!obj)
+ return;
+
+ detachWrapper(obj);
+ obj->detach();
+ removeAXID(obj);
+
+ // finally remove the object
+ if (!m_objects.take(axID))
+ return;
+
+ ASSERT(m_objects.size() >= m_idsInUse.size());
+}
+
+void AXObjectCache::remove(RenderObject* renderer)
+{
+ if (!renderer)
+ return;
+
+ AXID axID = m_renderObjectMapping.get(renderer);
+ remove(axID);
+ m_renderObjectMapping.remove(renderer);
+}
+
+void AXObjectCache::remove(Widget* view)
+{
+ if (!view)
+ return;
+
+ AXID axID = m_widgetObjectMapping.get(view);
+ remove(axID);
+ m_widgetObjectMapping.remove(view);
+}
+
+
+#if !PLATFORM(WIN) || OS(WINCE)
+AXID AXObjectCache::platformGenerateAXID() const
+{
+ static AXID lastUsedID = 0;
+
+ // Generate a new ID.
+ AXID objID = lastUsedID;
+ do {
+ ++objID;
+ } while (!objID || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.contains(objID));
+
+ lastUsedID = objID;
+
+ return objID;
+}
+#endif
+
+AXID AXObjectCache::getAXID(AccessibilityObject* obj)
+{
+ // check for already-assigned ID
+ AXID objID = obj->axObjectID();
+ if (objID) {
+ ASSERT(m_idsInUse.contains(objID));
+ return objID;
+ }
+
+ objID = platformGenerateAXID();
+
+ m_idsInUse.add(objID);
+ obj->setAXObjectID(objID);
+
+ return objID;
+}
+
+void AXObjectCache::removeAXID(AccessibilityObject* object)
+{
+ if (!object)
+ return;
+
+ AXID objID = object->axObjectID();
+ if (!objID)
+ return;
+ ASSERT(!HashTraits<AXID>::isDeletedValue(objID));
+ ASSERT(m_idsInUse.contains(objID));
+ object->setAXObjectID(0);
+ m_idsInUse.remove(objID);
+}
+
+#if HAVE(ACCESSIBILITY)
+void AXObjectCache::contentChanged(RenderObject* renderer)
+{
+ AccessibilityObject* object = getOrCreate(renderer);
+ if (object)
+ object->contentChanged();
+}
+#endif
+
+void AXObjectCache::childrenChanged(RenderObject* renderer)
+{
+ if (!renderer)
+ return;
+
+ AXID axID = m_renderObjectMapping.get(renderer);
+ if (!axID)
+ return;
+
+ AccessibilityObject* obj = m_objects.get(axID).get();
+ if (obj)
+ obj->childrenChanged();
+}
+
+void AXObjectCache::notificationPostTimerFired(Timer<AXObjectCache>*)
+{
+ m_notificationPostTimer.stop();
+
+ unsigned i = 0, count = m_notificationsToPost.size();
+ for (i = 0; i < count; ++i) {
+ AccessibilityObject* obj = m_notificationsToPost[i].first.get();
+#ifndef NDEBUG
+ // Make sure none of the render views are in the process of being layed out.
+ // Notifications should only be sent after the renderer has finished
+ if (obj->isAccessibilityRenderObject()) {
+ AccessibilityRenderObject* renderObj = static_cast<AccessibilityRenderObject*>(obj);
+ RenderObject* renderer = renderObj->renderer();
+ if (renderer && renderer->view())
+ ASSERT(!renderer->view()->layoutState());
+ }
+#endif
+
+ postPlatformNotification(obj, m_notificationsToPost[i].second);
+ }
+
+ m_notificationsToPost.clear();
+}
+
+#if HAVE(ACCESSIBILITY)
+void AXObjectCache::postNotification(RenderObject* renderer, AXNotification notification, bool postToElement, PostType postType)
+{
+ // Notifications for text input objects are sent to that object.
+ // All others are sent to the top WebArea.
+ if (!renderer)
+ return;
+
+ // Get an accessibility object that already exists. One should not be created here
+ // because a render update may be in progress and creating an AX object can re-trigger a layout
+ RefPtr<AccessibilityObject> object = get(renderer);
+ while (!object && renderer) {
+ renderer = renderer->parent();
+ object = get(renderer);
+ }
+
+ if (!renderer)
+ return;
+
+ postNotification(object.get(), renderer->document(), notification, postToElement, postType);
+}
+
+void AXObjectCache::postNotification(AccessibilityObject* object, Document* document, AXNotification notification, bool postToElement, PostType postType)
+{
+ if (object && !postToElement)
+ object = object->observableObject();
+
+ if (!object && document)
+ object = get(document->renderer());
+
+ if (!object)
+ return;
+
+ if (postType == PostAsynchronously) {
+ m_notificationsToPost.append(make_pair(object, notification));
+ if (!m_notificationPostTimer.isActive())
+ m_notificationPostTimer.startOneShot(0);
+ } else
+ postPlatformNotification(object, notification);
+}
+
+void AXObjectCache::selectedChildrenChanged(RenderObject* renderer)
+{
+ // postToElement is false so that you can pass in any child of an element and it will go up the parent tree
+ // to find the container which should send out the notification.
+ postNotification(renderer, AXSelectedChildrenChanged, false);
+}
+
+void AXObjectCache::nodeTextChangeNotification(RenderObject* renderer, AXTextChange textChange, unsigned offset, unsigned count)
+{
+ if (!renderer)
+ return;
+
+ // Delegate on the right platform
+ AccessibilityObject* obj = getOrCreate(renderer);
+ nodeTextChangePlatformNotification(obj, textChange, offset, count);
+}
+#endif
+
+#if HAVE(ACCESSIBILITY)
+
+void AXObjectCache::handleScrollbarUpdate(ScrollView* view)
+{
+ if (!view)
+ return;
+
+ // We don't want to create a scroll view from this method, only update an existing one.
+ AccessibilityObject* scrollViewObject = get(view);
+ if (scrollViewObject)
+ scrollViewObject->updateChildrenIfNecessary();
+}
+
+void AXObjectCache::handleAriaExpandedChange(RenderObject *renderer)
+{
+ if (!renderer)
+ return;
+ AccessibilityObject* obj = getOrCreate(renderer);
+ if (obj)
+ obj->handleAriaExpandedChanged();
+}
+
+void AXObjectCache::handleActiveDescendantChanged(RenderObject* renderer)
+{
+ if (!renderer)
+ return;
+ AccessibilityObject* obj = getOrCreate(renderer);
+ if (obj)
+ obj->handleActiveDescendantChanged();
+}
+
+void AXObjectCache::handleAriaRoleChanged(RenderObject* renderer)
+{
+ if (!renderer)
+ return;
+ AccessibilityObject* obj = getOrCreate(renderer);
+ if (obj && obj->isAccessibilityRenderObject())
+ static_cast<AccessibilityRenderObject*>(obj)->updateAccessibilityRole();
+}
+#endif
+
+VisiblePosition AXObjectCache::visiblePositionForTextMarkerData(TextMarkerData& textMarkerData)
+{
+ if (!isNodeInUse(textMarkerData.node))
+ return VisiblePosition();
+
+ VisiblePosition visiblePos = VisiblePosition(textMarkerData.node, textMarkerData.offset, textMarkerData.affinity);
+ Position deepPos = visiblePos.deepEquivalent();
+ if (deepPos.isNull())
+ return VisiblePosition();
+
+ RenderObject* renderer = deepPos.node()->renderer();
+ if (!renderer)
+ return VisiblePosition();
+
+ AXObjectCache* cache = renderer->document()->axObjectCache();
+ if (!cache->isIDinUse(textMarkerData.axID))
+ return VisiblePosition();
+
+ if (deepPos.node() != textMarkerData.node || deepPos.deprecatedEditingOffset() != textMarkerData.offset)
+ return VisiblePosition();
+
+ return visiblePos;
+}
+
+void AXObjectCache::textMarkerDataForVisiblePosition(TextMarkerData& textMarkerData, const VisiblePosition& visiblePos)
+{
+ // This memory must be bzero'd so instances of TextMarkerData can be tested for byte-equivalence.
+ // This also allows callers to check for failure by looking at textMarkerData upon return.
+ memset(&textMarkerData, 0, sizeof(TextMarkerData));
+
+ if (visiblePos.isNull())
+ return;
+
+ Position deepPos = visiblePos.deepEquivalent();
+ Node* domNode = deepPos.node();
+ ASSERT(domNode);
+ if (!domNode)
+ return;
+
+ if (domNode->isHTMLElement()) {
+ InputElement* inputElement = toInputElement(static_cast<Element*>(domNode));
+ if (inputElement && inputElement->isPasswordField())
+ return;
+ }
+
+ // locate the renderer, which must exist for a visible dom node
+ RenderObject* renderer = domNode->renderer();
+ ASSERT(renderer);
+
+ // find or create an accessibility object for this renderer
+ AXObjectCache* cache = renderer->document()->axObjectCache();
+ RefPtr<AccessibilityObject> obj = cache->getOrCreate(renderer);
+
+ textMarkerData.axID = obj.get()->axObjectID();
+ textMarkerData.node = domNode;
+ textMarkerData.offset = deepPos.deprecatedEditingOffset();
+ textMarkerData.affinity = visiblePos.affinity();
+
+ cache->setNodeInUse(domNode);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AXObjectCache.h b/Source/WebCore/accessibility/AXObjectCache.h
new file mode 100644
index 0000000..670d6e0
--- /dev/null
+++ b/Source/WebCore/accessibility/AXObjectCache.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010, 2011 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR
+ * 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 AXObjectCache_h
+#define AXObjectCache_h
+
+#include "AccessibilityObject.h"
+#include "Timer.h"
+#include <limits.h>
+#include <wtf/Forward.h>
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+#include <wtf/RefPtr.h>
+
+#ifdef __OBJC__
+@class WebCoreTextMarker;
+#else
+class WebCoreTextMarker;
+#endif
+
+namespace WebCore {
+
+class Document;
+class HTMLAreaElement;
+class Node;
+class Page;
+class RenderObject;
+class ScrollView;
+class VisiblePosition;
+class Widget;
+
+struct TextMarkerData {
+ AXID axID;
+ Node* node;
+ int offset;
+ EAffinity affinity;
+};
+
+enum PostType { PostSynchronously, PostAsynchronously };
+
+class AXObjectCache : public Noncopyable {
+public:
+ AXObjectCache(const Document*);
+ ~AXObjectCache();
+
+ static AccessibilityObject* focusedUIElementForPage(const Page*);
+
+ AccessibilityObject* rootObject();
+
+ // For AX objects with elements that back them.
+ AccessibilityObject* getOrCreate(RenderObject*);
+ AccessibilityObject* getOrCreate(Widget*);
+
+ // used for objects without backing elements
+ AccessibilityObject* getOrCreate(AccessibilityRole);
+
+ // will only return the AccessibilityObject if it already exists
+ AccessibilityObject* get(RenderObject*);
+
+ void remove(RenderObject*);
+ void remove(Widget*);
+ void remove(AXID);
+
+ void detachWrapper(AccessibilityObject*);
+ void attachWrapper(AccessibilityObject*);
+ void childrenChanged(RenderObject*);
+ void selectedChildrenChanged(RenderObject*);
+ // Called by a node when text or a text equivalent (e.g. alt) attribute is changed.
+ void contentChanged(RenderObject*);
+
+ void handleActiveDescendantChanged(RenderObject*);
+ void handleAriaRoleChanged(RenderObject*);
+ void handleFocusedUIElementChanged(RenderObject* oldFocusedRenderer, RenderObject* newFocusedRenderer);
+ void handleScrolledToAnchor(const Node* anchorNode);
+ void handleAriaExpandedChange(RenderObject*);
+ void handleScrollbarUpdate(ScrollView*);
+
+ static void enableAccessibility() { gAccessibilityEnabled = true; }
+ static void enableEnhancedUserInterfaceAccessibility() { gAccessibilityEnhancedUserInterfaceEnabled = true; }
+
+ static bool accessibilityEnabled() { return gAccessibilityEnabled; }
+ static bool accessibilityEnhancedUserInterfaceEnabled() { return gAccessibilityEnhancedUserInterfaceEnabled; }
+
+ void removeAXID(AccessibilityObject*);
+ bool isIDinUse(AXID id) const { return m_idsInUse.contains(id); }
+ AXID platformGenerateAXID() const;
+ AccessibilityObject* objectFromAXID(AXID id) const { return m_objects.get(id).get(); }
+
+ // This is a weak reference cache for knowing if Nodes used by TextMarkers are valid.
+ void setNodeInUse(Node* n) { m_textMarkerNodes.add(n); }
+ void removeNodeForUse(Node* n) { m_textMarkerNodes.remove(n); }
+ bool isNodeInUse(Node* n) { return m_textMarkerNodes.contains(n); }
+
+ // Text marker utilities.
+ void textMarkerDataForVisiblePosition(TextMarkerData&, const VisiblePosition&);
+ VisiblePosition visiblePositionForTextMarkerData(TextMarkerData&);
+
+ enum AXNotification {
+ AXActiveDescendantChanged,
+ AXCheckedStateChanged,
+ AXChildrenChanged,
+ AXFocusedUIElementChanged,
+ AXLayoutComplete,
+ AXLoadComplete,
+ AXSelectedChildrenChanged,
+ AXSelectedTextChanged,
+ AXValueChanged,
+ AXScrolledToAnchor,
+ AXLiveRegionChanged,
+ AXMenuListValueChanged,
+ AXRowCountChanged,
+ AXRowCollapsed,
+ AXRowExpanded,
+ AXInvalidStatusChanged,
+ };
+
+ void postNotification(RenderObject*, AXNotification, bool postToElement, PostType = PostAsynchronously);
+ void postNotification(AccessibilityObject*, Document*, AXNotification, bool postToElement, PostType = PostAsynchronously);
+
+ enum AXTextChange {
+ AXTextInserted,
+ AXTextDeleted,
+ };
+
+ void nodeTextChangeNotification(RenderObject*, AXTextChange, unsigned offset, unsigned count);
+
+ bool nodeHasRole(Node*, const AtomicString& role);
+
+protected:
+ void postPlatformNotification(AccessibilityObject*, AXNotification);
+ void nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned offset, unsigned count);
+
+private:
+ Document* m_document;
+ HashMap<AXID, RefPtr<AccessibilityObject> > m_objects;
+ HashMap<RenderObject*, AXID> m_renderObjectMapping;
+ HashMap<Widget*, AXID> m_widgetObjectMapping;
+ HashSet<Node*> m_textMarkerNodes;
+ static bool gAccessibilityEnabled;
+ static bool gAccessibilityEnhancedUserInterfaceEnabled;
+
+ HashSet<AXID> m_idsInUse;
+
+ Timer<AXObjectCache> m_notificationPostTimer;
+ Vector<pair<RefPtr<AccessibilityObject>, AXNotification> > m_notificationsToPost;
+ void notificationPostTimerFired(Timer<AXObjectCache>*);
+
+ static AccessibilityObject* focusedImageMapUIElement(HTMLAreaElement*);
+
+ AXID getAXID(AccessibilityObject*);
+ AccessibilityObject* get(Widget*);
+};
+
+bool nodeHasRole(Node*, const String& role);
+
+#if !HAVE(ACCESSIBILITY)
+inline void AXObjectCache::handleActiveDescendantChanged(RenderObject*) { }
+inline void AXObjectCache::handleAriaRoleChanged(RenderObject*) { }
+inline void AXObjectCache::detachWrapper(AccessibilityObject*) { }
+inline void AXObjectCache::attachWrapper(AccessibilityObject*) { }
+inline void AXObjectCache::selectedChildrenChanged(RenderObject*) { }
+inline void AXObjectCache::postNotification(RenderObject*, AXNotification, bool postToElement, PostType) { }
+inline void AXObjectCache::postNotification(AccessibilityObject*, Document*, AXNotification, bool postToElement, PostType) { }
+inline void AXObjectCache::postPlatformNotification(AccessibilityObject*, AXNotification) { }
+inline void AXObjectCache::nodeTextChangeNotification(RenderObject*, AXTextChange, unsigned, unsigned) { }
+inline void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, unsigned) { }
+inline void AXObjectCache::handleFocusedUIElementChanged(RenderObject*, RenderObject*) { }
+inline void AXObjectCache::handleScrolledToAnchor(const Node*) { }
+inline void AXObjectCache::contentChanged(RenderObject*) { }
+inline void AXObjectCache::handleAriaExpandedChange(RenderObject*) { }
+inline void AXObjectCache::handleScrollbarUpdate(ScrollView*) { }
+#endif
+
+}
+
+#endif
diff --git a/Source/WebCore/accessibility/AccessibilityARIAGrid.cpp b/Source/WebCore/accessibility/AccessibilityARIAGrid.cpp
new file mode 100644
index 0000000..8651a80
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityARIAGrid.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2009 Apple 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 "AccessibilityARIAGrid.h"
+
+#include "AXObjectCache.h"
+#include "AccessibilityTableCell.h"
+#include "AccessibilityTableColumn.h"
+#include "AccessibilityTableHeaderContainer.h"
+#include "AccessibilityTableRow.h"
+#include "RenderObject.h"
+
+using namespace std;
+
+namespace WebCore {
+
+AccessibilityARIAGrid::AccessibilityARIAGrid(RenderObject* renderer)
+ : AccessibilityTable(renderer)
+{
+#if ACCESSIBILITY_TABLES
+ m_isAccessibilityTable = true;
+#else
+ m_isAccessibilityTable = false;
+#endif
+}
+
+AccessibilityARIAGrid::~AccessibilityARIAGrid()
+{
+}
+
+PassRefPtr<AccessibilityARIAGrid> AccessibilityARIAGrid::create(RenderObject* renderer)
+{
+ return adoptRef(new AccessibilityARIAGrid(renderer));
+}
+
+void AccessibilityARIAGrid::addChild(AccessibilityObject* child, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount)
+{
+ if (!child || !child->isTableRow() || child->ariaRoleAttribute() != RowRole)
+ return;
+
+ AccessibilityTableRow* row = static_cast<AccessibilityTableRow*>(child);
+ if (appendedRows.contains(row))
+ return;
+
+ // store the maximum number of columns
+ unsigned rowCellCount = row->children().size();
+ if (rowCellCount > columnCount)
+ columnCount = rowCellCount;
+
+ row->setRowIndex((int)m_rows.size());
+ m_rows.append(row);
+ m_children.append(row);
+ appendedRows.add(row);
+}
+
+void AccessibilityARIAGrid::addChildren()
+{
+ ASSERT(!m_haveChildren);
+
+ if (!isAccessibilityTable()) {
+ AccessibilityRenderObject::addChildren();
+ return;
+ }
+
+ m_haveChildren = true;
+ if (!m_renderer)
+ return;
+
+ AXObjectCache* axCache = m_renderer->document()->axObjectCache();
+
+ // add only rows that are labeled as aria rows
+ HashSet<AccessibilityObject*> appendedRows;
+ unsigned columnCount = 0;
+ for (RefPtr<AccessibilityObject> child = firstChild(); child; child = child->nextSibling()) {
+
+ // in case the render tree doesn't match the expected ARIA hierarchy, look at the children
+ if (child->accessibilityIsIgnored()) {
+ if (!child->hasChildren())
+ child->addChildren();
+
+ AccessibilityChildrenVector children = child->children();
+ unsigned length = children.size();
+ for (unsigned i = 0; i < length; ++i)
+ addChild(children[i].get(), appendedRows, columnCount);
+ } else
+ addChild(child.get(), appendedRows, columnCount);
+ }
+
+ // make the columns based on the number of columns in the first body
+ for (unsigned i = 0; i < columnCount; ++i) {
+ AccessibilityTableColumn* column = static_cast<AccessibilityTableColumn*>(axCache->getOrCreate(ColumnRole));
+ column->setColumnIndex((int)i);
+ column->setParentTable(this);
+ m_columns.append(column);
+ if (!column->accessibilityIsIgnored())
+ m_children.append(column);
+ }
+
+ AccessibilityObject* headerContainerObject = headerContainer();
+ if (headerContainerObject && !headerContainerObject->accessibilityIsIgnored())
+ m_children.append(headerContainerObject);
+}
+
+AccessibilityTableCell* AccessibilityARIAGrid::cellForColumnAndRow(unsigned column, unsigned row)
+{
+ if (!m_renderer)
+ return 0;
+
+ updateChildrenIfNecessary();
+
+ if (column >= columnCount() || row >= rowCount())
+ return 0;
+
+ AccessibilityObject* tableRow = m_rows[row].get();
+ if (!tableRow)
+ return 0;
+
+ AccessibilityChildrenVector children = tableRow->children();
+ // in case this row had fewer columns than other rows
+ AccessibilityObject* tableCell = 0;
+ if (column >= children.size())
+ return 0;
+
+ tableCell = children[column].get();
+ return static_cast<AccessibilityTableCell*>(tableCell);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityARIAGrid.h b/Source/WebCore/accessibility/AccessibilityARIAGrid.h
new file mode 100644
index 0000000..02ff4d0
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityARIAGrid.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009 Apple 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 AccessibilityARIAGrid_h
+#define AccessibilityARIAGrid_h
+
+#include "AccessibilityTable.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class AccessibilityTableCell;
+class AccessibilityTableHeaderContainer;
+
+class AccessibilityARIAGrid : public AccessibilityTable {
+
+private:
+ AccessibilityARIAGrid(RenderObject*);
+public:
+ static PassRefPtr<AccessibilityARIAGrid> create(RenderObject*);
+ virtual ~AccessibilityARIAGrid();
+
+ virtual bool isAriaTable() const { return true; }
+
+ virtual void addChildren();
+
+ virtual AccessibilityTableCell* cellForColumnAndRow(unsigned column, unsigned row);
+
+private:
+ // ARIA treegrids and grids support selected rows.
+ virtual bool supportsSelectedRows() { return true; }
+
+ void addChild(AccessibilityObject*, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount);
+};
+
+} // namespace WebCore
+
+#endif // AccessibilityARIAGrid_h
diff --git a/Source/WebCore/accessibility/AccessibilityARIAGridCell.cpp b/Source/WebCore/accessibility/AccessibilityARIAGridCell.cpp
new file mode 100644
index 0000000..c8157f5
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityARIAGridCell.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2009 Apple 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 "AccessibilityARIAGridCell.h"
+
+#include "AccessibilityObject.h"
+#include "AccessibilityTableRow.h"
+
+using namespace std;
+
+namespace WebCore {
+
+AccessibilityARIAGridCell::AccessibilityARIAGridCell(RenderObject* renderer)
+ : AccessibilityTableCell(renderer)
+{
+}
+
+AccessibilityARIAGridCell::~AccessibilityARIAGridCell()
+{
+}
+
+PassRefPtr<AccessibilityARIAGridCell> AccessibilityARIAGridCell::create(RenderObject* renderer)
+{
+ return adoptRef(new AccessibilityARIAGridCell(renderer));
+}
+
+AccessibilityObject* AccessibilityARIAGridCell::parentTable() const
+{
+ AccessibilityObject* parent = parentObjectUnignored();
+ if (!parent || !parent->isTableRow())
+ return 0;
+
+ parent = parent->parentObjectUnignored();
+ if (!parent || !parent->isAccessibilityTable())
+ return 0;
+
+ return parent;
+}
+
+void AccessibilityARIAGridCell::rowIndexRange(pair<int, int>& rowRange)
+{
+ AccessibilityObject* parent = parentObjectUnignored();
+ if (!parent || !parent->isTableRow())
+ return;
+
+ // as far as I can tell, grid cells cannot span rows
+ rowRange.first = static_cast<AccessibilityTableRow*>(parent)->rowIndex();
+ rowRange.second = 1;
+}
+
+void AccessibilityARIAGridCell::columnIndexRange(pair<int, int>& columnRange)
+{
+ AccessibilityObject* parent = parentObjectUnignored();
+ if (!parent || !parent->isTableRow())
+ return;
+
+ AccessibilityChildrenVector siblings = parent->children();
+ unsigned childrenSize = siblings.size();
+ for (unsigned k = 0; k < childrenSize; ++k) {
+ if (siblings[k].get() == this) {
+ columnRange.first = k;
+ break;
+ }
+ }
+
+ // as far as I can tell, grid cells cannot span columns
+ columnRange.second = 1;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityARIAGridCell.h b/Source/WebCore/accessibility/AccessibilityARIAGridCell.h
new file mode 100644
index 0000000..2923de8
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityARIAGridCell.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2009 Apple 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 AccessibilityARIAGridCell_h
+#define AccessibilityARIAGridCell_h
+
+#include "AccessibilityTableCell.h"
+
+namespace WebCore {
+
+class AccessibilityARIAGridCell : public AccessibilityTableCell {
+
+private:
+ AccessibilityARIAGridCell(RenderObject*);
+public:
+ static PassRefPtr<AccessibilityARIAGridCell> create(RenderObject*);
+ virtual ~AccessibilityARIAGridCell();
+
+ // fills in the start location and row span of cell
+ virtual void rowIndexRange(pair<int, int>& rowRange);
+ // fills in the start location and column span of cell
+ virtual void columnIndexRange(pair<int, int>& columnRange);
+
+protected:
+ virtual AccessibilityObject* parentTable() const;
+};
+
+} // namespace WebCore
+
+#endif // AccessibilityARIAGridCell_h
diff --git a/Source/WebCore/accessibility/AccessibilityARIAGridRow.cpp b/Source/WebCore/accessibility/AccessibilityARIAGridRow.cpp
new file mode 100644
index 0000000..2b96036
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityARIAGridRow.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2009 Apple 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 "AccessibilityARIAGridRow.h"
+
+#include "AccessibilityObject.h"
+#include "AccessibilityTable.h"
+#include "RenderObject.h"
+
+using namespace std;
+
+namespace WebCore {
+
+AccessibilityARIAGridRow::AccessibilityARIAGridRow(RenderObject* renderer)
+ : AccessibilityTableRow(renderer)
+{
+}
+
+AccessibilityARIAGridRow::~AccessibilityARIAGridRow()
+{
+}
+
+PassRefPtr<AccessibilityARIAGridRow> AccessibilityARIAGridRow::create(RenderObject* renderer)
+{
+ return adoptRef(new AccessibilityARIAGridRow(renderer));
+}
+
+bool AccessibilityARIAGridRow::isARIATreeGridRow() const
+{
+ AccessibilityObject* parent = parentTable();
+ if (!parent)
+ return false;
+
+ return parent->ariaRoleAttribute() == TreeGridRole;
+}
+
+void AccessibilityARIAGridRow::disclosedRows(AccessibilityChildrenVector& disclosedRows)
+{
+ // The contiguous disclosed rows will be the rows in the table that
+ // have an aria-level of plus 1 from this row.
+ AccessibilityObject* parent = parentObjectUnignored();
+ if (!parent || !parent->isAccessibilityTable())
+ return;
+
+ // Search for rows that match the correct level.
+ // Only take the subsequent rows from this one that are +1 from this row's level.
+ int index = rowIndex();
+ if (index < 0)
+ return;
+
+ unsigned level = hierarchicalLevel();
+ AccessibilityChildrenVector& allRows = static_cast<AccessibilityTable*>(parent)->rows();
+ int rowCount = allRows.size();
+ for (int k = index + 1; k < rowCount; ++k) {
+ AccessibilityObject* row = allRows[k].get();
+ // Stop at the first row that doesn't match the correct level.
+ if (row->hierarchicalLevel() != level + 1)
+ break;
+
+ disclosedRows.append(row);
+ }
+}
+
+AccessibilityObject* AccessibilityARIAGridRow::disclosedByRow() const
+{
+ // The row that discloses this one is the row in the table
+ // that is aria-level subtract 1 from this row.
+ AccessibilityObject* parent = parentObjectUnignored();
+ if (!parent || !parent->isAccessibilityTable())
+ return 0;
+
+ // If the level is 1 or less, than nothing discloses this row.
+ unsigned level = hierarchicalLevel();
+ if (level <= 1)
+ return 0;
+
+ // Search for the previous row that matches the correct level.
+ int index = rowIndex();
+ AccessibilityChildrenVector& allRows = static_cast<AccessibilityTable*>(parent)->rows();
+ int rowCount = allRows.size();
+ if (index >= rowCount)
+ return 0;
+
+ for (int k = index - 1; k >= 0; --k) {
+ AccessibilityObject* row = allRows[k].get();
+ if (row->hierarchicalLevel() == level - 1)
+ return row;
+ }
+
+ return 0;
+}
+
+AccessibilityObject* AccessibilityARIAGridRow::parentTable() const
+{
+ AccessibilityObject* parent = parentObjectUnignored();
+ if (!parent->isAccessibilityTable())
+ return 0;
+
+ return parent;
+}
+
+AccessibilityObject* AccessibilityARIAGridRow::headerObject()
+{
+ AccessibilityChildrenVector rowChildren = children();
+ unsigned childrenCount = rowChildren.size();
+ for (unsigned i = 0; i < childrenCount; ++i) {
+ AccessibilityObject* cell = rowChildren[i].get();
+ if (cell->ariaRoleAttribute() == RowHeaderRole)
+ return cell;
+ }
+
+ return 0;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityARIAGridRow.h b/Source/WebCore/accessibility/AccessibilityARIAGridRow.h
new file mode 100644
index 0000000..f89ea92
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityARIAGridRow.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 Apple 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 AccessibilityARIAGridRow_h
+#define AccessibilityARIAGridRow_h
+
+#include "AccessibilityTableRow.h"
+
+namespace WebCore {
+
+class AccessibilityARIAGridRow : public AccessibilityTableRow {
+
+private:
+ AccessibilityARIAGridRow(RenderObject*);
+public:
+ static PassRefPtr<AccessibilityARIAGridRow> create(RenderObject*);
+ virtual ~AccessibilityARIAGridRow();
+
+ void disclosedRows(AccessibilityChildrenVector&);
+ AccessibilityObject* disclosedByRow() const;
+
+ virtual AccessibilityObject* headerObject();
+ virtual AccessibilityObject* parentTable() const;
+
+private:
+ virtual bool isARIATreeGridRow() const;
+};
+
+} // namespace WebCore
+
+#endif // AccessibilityARIAGridRow_h
diff --git a/Source/WebCore/accessibility/AccessibilityAllInOne.cpp b/Source/WebCore/accessibility/AccessibilityAllInOne.cpp
new file mode 100644
index 0000000..1c2836e
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityAllInOne.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2009 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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.
+ */
+
+// This all-in-one cpp file cuts down on template bloat to allow us to build our Windows release build.
+
+#include <AXObjectCache.cpp>
+#include <AccessibilityARIAGrid.cpp>
+#include <AccessibilityARIAGridCell.cpp>
+#include <AccessibilityARIAGridRow.cpp>
+#include <AccessibilityImageMapLink.cpp>
+#include <AccessibilityList.cpp>
+#include <AccessibilityListBox.cpp>
+#include <AccessibilityListBoxOption.cpp>
+#include <AccessibilityMediaControls.cpp>
+#include <AccessibilityObject.cpp>
+#include <AccessibilityRenderObject.cpp>
+#include <AccessibilityScrollView.cpp>
+#include <AccessibilityScrollbar.cpp>
+#include <AccessibilitySlider.cpp>
+#include <AccessibilityTable.cpp>
+#include <AccessibilityTableCell.cpp>
+#include <AccessibilityTableColumn.cpp>
+#include <AccessibilityTableHeaderContainer.cpp>
+#include <AccessibilityTableRow.cpp>
diff --git a/Source/WebCore/accessibility/AccessibilityImageMapLink.cpp b/Source/WebCore/accessibility/AccessibilityImageMapLink.cpp
new file mode 100644
index 0000000..5676206
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityImageMapLink.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2008 Apple 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 "AccessibilityImageMapLink.h"
+
+#include "AXObjectCache.h"
+#include "AccessibilityRenderObject.h"
+#include "Document.h"
+#include "HTMLNames.h"
+#include "RenderBoxModelObject.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+AccessibilityImageMapLink::AccessibilityImageMapLink()
+ : m_areaElement(0)
+ , m_mapElement(0)
+{
+}
+
+AccessibilityImageMapLink::~AccessibilityImageMapLink()
+{
+}
+
+PassRefPtr<AccessibilityImageMapLink> AccessibilityImageMapLink::create()
+{
+ return adoptRef(new AccessibilityImageMapLink());
+}
+
+AccessibilityObject* AccessibilityImageMapLink::parentObject() const
+{
+ if (m_parent)
+ return m_parent;
+
+ if (!m_mapElement.get() || !m_mapElement->renderer())
+ return 0;
+
+ return m_mapElement->document()->axObjectCache()->getOrCreate(m_mapElement->renderer());
+}
+
+AccessibilityRole AccessibilityImageMapLink::roleValue() const
+{
+ if (!m_areaElement)
+ return WebCoreLinkRole;
+
+ const AtomicString& ariaRole = getAttribute(roleAttr);
+ if (!ariaRole.isEmpty())
+ return AccessibilityObject::ariaRoleToWebCoreRole(ariaRole);
+
+ return WebCoreLinkRole;
+}
+
+Element* AccessibilityImageMapLink::actionElement() const
+{
+ return anchorElement();
+}
+
+Element* AccessibilityImageMapLink::anchorElement() const
+{
+ return m_areaElement.get();
+}
+
+KURL AccessibilityImageMapLink::url() const
+{
+ if (!m_areaElement.get())
+ return KURL();
+
+ return m_areaElement->href();
+}
+
+String AccessibilityImageMapLink::accessibilityDescription() const
+{
+ const AtomicString& ariaLabel = getAttribute(aria_labelAttr);
+ if (!ariaLabel.isEmpty())
+ return ariaLabel;
+ const AtomicString& alt = getAttribute(altAttr);
+ if (!alt.isEmpty())
+ return alt;
+
+ return String();
+}
+
+String AccessibilityImageMapLink::title() const
+{
+ const AtomicString& title = getAttribute(titleAttr);
+ if (!title.isEmpty())
+ return title;
+ const AtomicString& summary = getAttribute(summaryAttr);
+ if (!summary.isEmpty())
+ return summary;
+
+ return String();
+}
+
+IntRect AccessibilityImageMapLink::elementRect() const
+{
+ if (!m_mapElement.get() || !m_areaElement.get())
+ return IntRect();
+
+ RenderObject* renderer;
+ if (m_parent && m_parent->isAccessibilityRenderObject())
+ renderer = static_cast<AccessibilityRenderObject*>(m_parent)->renderer();
+ else
+ renderer = m_mapElement->renderer();
+
+ if (!renderer)
+ return IntRect();
+
+ return m_areaElement->getRect(renderer);
+}
+
+String AccessibilityImageMapLink::stringValueForMSAA() const
+{
+ return url();
+}
+
+String AccessibilityImageMapLink::nameForMSAA() const
+{
+ return accessibilityDescription();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityImageMapLink.h b/Source/WebCore/accessibility/AccessibilityImageMapLink.h
new file mode 100644
index 0000000..4b02b2b
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityImageMapLink.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2008 Apple 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 AccessibilityImageMapLink_h
+#define AccessibilityImageMapLink_h
+
+#include "AccessibilityObject.h"
+#include "HTMLAreaElement.h"
+#include "HTMLMapElement.h"
+
+namespace WebCore {
+
+class AccessibilityImageMapLink : public AccessibilityObject {
+
+private:
+ AccessibilityImageMapLink();
+public:
+ static PassRefPtr<AccessibilityImageMapLink> create();
+ virtual ~AccessibilityImageMapLink();
+
+ void setHTMLAreaElement(HTMLAreaElement* element) { m_areaElement = element; }
+ HTMLAreaElement* areaElement() const { return m_areaElement.get(); }
+
+ void setHTMLMapElement(HTMLMapElement* element) { m_mapElement = element; }
+ HTMLMapElement* mapElement() const { return m_mapElement.get(); }
+
+ virtual Node* node() const { return m_areaElement.get(); }
+ void setParent(AccessibilityObject* parent) { m_parent = parent; }
+
+ virtual AccessibilityRole roleValue() const;
+ virtual bool accessibilityIsIgnored() const { return false; }
+ virtual bool isEnabled() const { return true; }
+
+ virtual AccessibilityObject* parentObject() const;
+ virtual Element* anchorElement() const;
+ virtual Element* actionElement() const;
+ virtual KURL url() const;
+ virtual bool isLink() const { return true; }
+ virtual bool isLinked() const { return true; }
+ virtual String title() const;
+ virtual String accessibilityDescription() const;
+
+ virtual String stringValueForMSAA() const;
+ virtual String nameForMSAA() const;
+
+ virtual IntRect elementRect() const;
+
+private:
+ RefPtr<HTMLAreaElement> m_areaElement;
+ RefPtr<HTMLMapElement> m_mapElement;
+ AccessibilityObject* m_parent;
+
+ virtual bool isImageMapLink() const { return true; }
+};
+
+} // namespace WebCore
+
+#endif // AccessibilityImageMapLink_h
diff --git a/Source/WebCore/accessibility/AccessibilityList.cpp b/Source/WebCore/accessibility/AccessibilityList.cpp
new file mode 100644
index 0000000..073b0fc
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityList.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2008 Apple 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 "AccessibilityList.h"
+
+#include "AXObjectCache.h"
+#include "HTMLNames.h"
+#include "RenderObject.h"
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+AccessibilityList::AccessibilityList(RenderObject* renderer)
+ : AccessibilityRenderObject(renderer)
+{
+}
+
+AccessibilityList::~AccessibilityList()
+{
+}
+
+PassRefPtr<AccessibilityList> AccessibilityList::create(RenderObject* renderer)
+{
+ return adoptRef(new AccessibilityList(renderer));
+}
+
+bool AccessibilityList::accessibilityIsIgnored() const
+{
+ AccessibilityObjectInclusion decision = accessibilityIsIgnoredBase();
+ if (decision == IncludeObject)
+ return false;
+ if (decision == IgnoreObject)
+ return true;
+
+ // lists don't appear on tiger/leopard on the mac
+#if ACCESSIBILITY_LISTS
+ return false;
+#else
+ return true;
+#endif
+}
+
+bool AccessibilityList::isUnorderedList() const
+{
+ if (!m_renderer)
+ return false;
+
+ Node* node = m_renderer->node();
+
+ // The ARIA spec says the "list" role is supposed to mimic a UL or OL tag.
+ // Since it can't be both, it's probably OK to say that it's an un-ordered list.
+ // On the Mac, there's no distinction to the client.
+ if (ariaRoleAttribute() == ListRole)
+ return true;
+
+ return node && node->hasTagName(ulTag);
+}
+
+bool AccessibilityList::isOrderedList() const
+{
+ if (!m_renderer)
+ return false;
+
+ // ARIA says a directory is like a static table of contents, which sounds like an ordered list.
+ if (ariaRoleAttribute() == DirectoryRole)
+ return true;
+
+ Node* node = m_renderer->node();
+ return node && node->hasTagName(olTag);
+}
+
+bool AccessibilityList::isDefinitionList() const
+{
+ if (!m_renderer)
+ return false;
+
+ Node* node = m_renderer->node();
+ return node && node->hasTagName(dlTag);
+}
+
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityList.h b/Source/WebCore/accessibility/AccessibilityList.h
new file mode 100644
index 0000000..b7265b2
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityList.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2008 Apple 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 AccessibilityList_h
+#define AccessibilityList_h
+
+#if PLATFORM(MAC) && (defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD))
+#define ACCESSIBILITY_LISTS 0
+#else
+#define ACCESSIBILITY_LISTS 1
+#endif
+
+#include "AccessibilityRenderObject.h"
+
+namespace WebCore {
+
+class AccessibilityList : public AccessibilityRenderObject {
+
+private:
+ AccessibilityList(RenderObject*);
+public:
+ static PassRefPtr<AccessibilityList> create(RenderObject*);
+ virtual ~AccessibilityList();
+
+ virtual bool isList() const { return true; }
+ bool isUnorderedList() const;
+ bool isOrderedList() const;
+ bool isDefinitionList() const;
+
+ virtual AccessibilityRole roleValue() const { return ListRole; }
+ virtual bool accessibilityIsIgnored() const;
+
+};
+
+} // namespace WebCore
+
+#endif // AccessibilityList_h
diff --git a/Source/WebCore/accessibility/AccessibilityListBox.cpp b/Source/WebCore/accessibility/AccessibilityListBox.cpp
new file mode 100644
index 0000000..191a041
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityListBox.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2008 Apple 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 "AccessibilityListBox.h"
+
+#include "AXObjectCache.h"
+#include "AccessibilityListBoxOption.h"
+#include "HTMLNames.h"
+#include "HTMLSelectElement.h"
+#include "HitTestResult.h"
+#include "RenderListBox.h"
+#include "RenderObject.h"
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+AccessibilityListBox::AccessibilityListBox(RenderObject* renderer)
+ : AccessibilityRenderObject(renderer)
+{
+}
+
+AccessibilityListBox::~AccessibilityListBox()
+{
+}
+
+PassRefPtr<AccessibilityListBox> AccessibilityListBox::create(RenderObject* renderer)
+{
+ return adoptRef(new AccessibilityListBox(renderer));
+}
+
+bool AccessibilityListBox::canSetSelectedChildrenAttribute() const
+{
+ Node* selectNode = m_renderer->node();
+ if (!selectNode)
+ return false;
+
+ return !static_cast<HTMLSelectElement*>(selectNode)->disabled();
+}
+
+void AccessibilityListBox::addChildren()
+{
+ Node* selectNode = m_renderer->node();
+ if (!selectNode)
+ return;
+
+ m_haveChildren = true;
+
+ const Vector<Element*>& listItems = static_cast<HTMLSelectElement*>(selectNode)->listItems();
+ unsigned length = listItems.size();
+ for (unsigned i = 0; i < length; i++) {
+ // The cast to HTMLElement below is safe because the only other possible listItem type
+ // would be a WMLElement, but WML builds don't use accessibility features at all.
+ AccessibilityObject* listOption = listBoxOptionAccessibilityObject(static_cast<HTMLElement*>(listItems[i]));
+ if (listOption && !listOption->accessibilityIsIgnored())
+ m_children.append(listOption);
+ }
+}
+
+void AccessibilityListBox::setSelectedChildren(AccessibilityChildrenVector& children)
+{
+ if (!canSetSelectedChildrenAttribute())
+ return;
+
+ Node* selectNode = m_renderer->node();
+ if (!selectNode)
+ return;
+
+ // disable any selected options
+ unsigned length = m_children.size();
+ for (unsigned i = 0; i < length; i++) {
+ AccessibilityListBoxOption* listBoxOption = static_cast<AccessibilityListBoxOption*>(m_children[i].get());
+ if (listBoxOption->isSelected())
+ listBoxOption->setSelected(false);
+ }
+
+ length = children.size();
+ for (unsigned i = 0; i < length; i++) {
+ AccessibilityObject* obj = children[i].get();
+ if (obj->roleValue() != ListBoxOptionRole)
+ continue;
+
+ static_cast<AccessibilityListBoxOption*>(obj)->setSelected(true);
+ }
+}
+
+void AccessibilityListBox::selectedChildren(AccessibilityChildrenVector& result)
+{
+ ASSERT(result.isEmpty());
+
+ if (!hasChildren())
+ addChildren();
+
+ unsigned length = m_children.size();
+ for (unsigned i = 0; i < length; i++) {
+ if (static_cast<AccessibilityListBoxOption*>(m_children[i].get())->isSelected())
+ result.append(m_children[i]);
+ }
+}
+
+void AccessibilityListBox::visibleChildren(AccessibilityChildrenVector& result)
+{
+ ASSERT(result.isEmpty());
+
+ if (!hasChildren())
+ addChildren();
+
+ unsigned length = m_children.size();
+ for (unsigned i = 0; i < length; i++) {
+ if (toRenderListBox(m_renderer)->listIndexIsVisible(i))
+ result.append(m_children[i]);
+ }
+}
+
+AccessibilityObject* AccessibilityListBox::listBoxOptionAccessibilityObject(HTMLElement* element) const
+{
+ // skip hr elements
+ if (!element || element->hasTagName(hrTag))
+ return 0;
+
+ AccessibilityObject* listBoxObject = m_renderer->document()->axObjectCache()->getOrCreate(ListBoxOptionRole);
+ static_cast<AccessibilityListBoxOption*>(listBoxObject)->setHTMLElement(element);
+
+ return listBoxObject;
+}
+
+bool AccessibilityListBox::accessibilityIsIgnored() const
+{
+ AccessibilityObjectInclusion decision = accessibilityIsIgnoredBase();
+ if (decision == IncludeObject)
+ return false;
+ if (decision == IgnoreObject)
+ return true;
+
+ return false;
+}
+
+AccessibilityObject* AccessibilityListBox::elementAccessibilityHitTest(const IntPoint& point) const
+{
+ // the internal HTMLSelectElement methods for returning a listbox option at a point
+ // ignore optgroup elements.
+ if (!m_renderer)
+ return 0;
+
+ Node* node = m_renderer->node();
+ if (!node)
+ return 0;
+
+ IntRect parentRect = boundingBoxRect();
+
+ AccessibilityObject* listBoxOption = 0;
+ unsigned length = m_children.size();
+ for (unsigned i = 0; i < length; i++) {
+ IntRect rect = toRenderListBox(m_renderer)->itemBoundingBoxRect(parentRect.x(), parentRect.y(), i);
+ // The cast to HTMLElement below is safe because the only other possible listItem type
+ // would be a WMLElement, but WML builds don't use accessibility features at all.
+ if (rect.contains(point)) {
+ listBoxOption = m_children[i].get();
+ break;
+ }
+ }
+
+ if (listBoxOption && !listBoxOption->accessibilityIsIgnored())
+ return listBoxOption;
+
+ return axObjectCache()->getOrCreate(m_renderer);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityListBox.h b/Source/WebCore/accessibility/AccessibilityListBox.h
new file mode 100644
index 0000000..cfccb52
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityListBox.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 Apple 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 AccessibilityListBox_h
+#define AccessibilityListBox_h
+
+#include "AccessibilityRenderObject.h"
+
+namespace WebCore {
+
+class AccessibilityListBox : public AccessibilityRenderObject {
+
+private:
+ AccessibilityListBox(RenderObject*);
+public:
+ static PassRefPtr<AccessibilityListBox> create(RenderObject*);
+ virtual ~AccessibilityListBox();
+
+ virtual bool isListBox() const { return true; }
+
+ virtual bool canSetFocusAttribute() const { return true; }
+ virtual bool canSetSelectedChildrenAttribute() const;
+ void setSelectedChildren(AccessibilityChildrenVector&);
+ virtual AccessibilityRole roleValue() const { return ListBoxRole; }
+
+ virtual void selectedChildren(AccessibilityChildrenVector&);
+ virtual void visibleChildren(AccessibilityChildrenVector&);
+
+ virtual void addChildren();
+
+private:
+ AccessibilityObject* listBoxOptionAccessibilityObject(HTMLElement*) const;
+ virtual bool accessibilityIsIgnored() const;
+ virtual AccessibilityObject* elementAccessibilityHitTest(const IntPoint&) const;
+};
+
+} // namespace WebCore
+
+#endif // AccessibilityListBox_h
diff --git a/Source/WebCore/accessibility/AccessibilityListBoxOption.cpp b/Source/WebCore/accessibility/AccessibilityListBoxOption.cpp
new file mode 100644
index 0000000..01420c5
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityListBoxOption.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2008 Apple 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 "AccessibilityListBoxOption.h"
+
+#include "AXObjectCache.h"
+#include "AccessibilityListBox.h"
+#include "Element.h"
+#include "HTMLElement.h"
+#include "HTMLNames.h"
+#include "HTMLOptGroupElement.h"
+#include "HTMLOptionElement.h"
+#include "HTMLSelectElement.h"
+#include "IntRect.h"
+#include "RenderListBox.h"
+#include "RenderObject.h"
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+AccessibilityListBoxOption::AccessibilityListBoxOption()
+ : m_optionElement(0)
+{
+}
+
+AccessibilityListBoxOption::~AccessibilityListBoxOption()
+{
+}
+
+PassRefPtr<AccessibilityListBoxOption> AccessibilityListBoxOption::create()
+{
+ return adoptRef(new AccessibilityListBoxOption());
+}
+
+bool AccessibilityListBoxOption::isEnabled() const
+{
+ if (!m_optionElement)
+ return false;
+
+ if (m_optionElement->hasTagName(optgroupTag))
+ return false;
+
+ return true;
+}
+
+bool AccessibilityListBoxOption::isSelected() const
+{
+ if (!m_optionElement)
+ return false;
+
+ if (!m_optionElement->hasTagName(optionTag))
+ return false;
+
+ return static_cast<HTMLOptionElement*>(m_optionElement)->selected();
+}
+
+IntRect AccessibilityListBoxOption::elementRect() const
+{
+ IntRect rect;
+ if (!m_optionElement)
+ return rect;
+
+ HTMLSelectElement* listBoxParentNode = listBoxOptionParentNode();
+ if (!listBoxParentNode)
+ return rect;
+
+ RenderObject* listBoxRenderer = listBoxParentNode->renderer();
+ if (!listBoxRenderer)
+ return rect;
+
+ IntRect parentRect = listBoxRenderer->document()->axObjectCache()->getOrCreate(listBoxRenderer)->boundingBoxRect();
+ int index = listBoxOptionIndex();
+ if (index != -1)
+ rect = toRenderListBox(listBoxRenderer)->itemBoundingBoxRect(parentRect.x(), parentRect.y(), index);
+
+ return rect;
+}
+
+bool AccessibilityListBoxOption::accessibilityIsIgnored() const
+{
+ if (!m_optionElement)
+ return true;
+
+ if (equalIgnoringCase(getAttribute(aria_hiddenAttr), "true"))
+ return true;
+
+ return parentObject()->accessibilityIsIgnored();
+}
+
+bool AccessibilityListBoxOption::canSetSelectedAttribute() const
+{
+ if (!m_optionElement)
+ return false;
+
+ if (!m_optionElement->hasTagName(optionTag))
+ return false;
+
+ if (m_optionElement->disabled())
+ return false;
+
+ HTMLSelectElement* selectElement = listBoxOptionParentNode();
+ if (selectElement && selectElement->disabled())
+ return false;
+
+ return true;
+}
+
+String AccessibilityListBoxOption::stringValue() const
+{
+ if (!m_optionElement)
+ return String();
+
+ const AtomicString& ariaLabel = getAttribute(aria_labelAttr);
+ if (!ariaLabel.isNull())
+ return ariaLabel;
+
+ if (m_optionElement->hasTagName(optionTag))
+ return static_cast<HTMLOptionElement*>(m_optionElement)->text();
+
+ if (m_optionElement->hasTagName(optgroupTag))
+ return static_cast<HTMLOptGroupElement*>(m_optionElement)->groupLabelText();
+
+ return String();
+}
+
+Element* AccessibilityListBoxOption::actionElement() const
+{
+ return m_optionElement;
+}
+
+AccessibilityObject* AccessibilityListBoxOption::parentObject() const
+{
+ HTMLSelectElement* parentNode = listBoxOptionParentNode();
+ if (!parentNode)
+ return 0;
+
+ return m_optionElement->document()->axObjectCache()->getOrCreate(parentNode->renderer());
+}
+
+void AccessibilityListBoxOption::setSelected(bool selected)
+{
+ HTMLSelectElement* selectElement = listBoxOptionParentNode();
+ if (!selectElement)
+ return;
+
+ if (!canSetSelectedAttribute())
+ return;
+
+ bool isOptionSelected = isSelected();
+ if ((isOptionSelected && selected) || (!isOptionSelected && !selected))
+ return;
+
+ // Convert from the entire list index to the option index.
+ int optionIndex = static_cast<SelectElement*>(selectElement)->listToOptionIndex(listBoxOptionIndex());
+ selectElement->accessKeySetSelectedIndex(optionIndex);
+}
+
+HTMLSelectElement* AccessibilityListBoxOption::listBoxOptionParentNode() const
+{
+ if (!m_optionElement)
+ return 0;
+
+ if (m_optionElement->hasTagName(optionTag))
+ return static_cast<HTMLOptionElement*>(m_optionElement)->ownerSelectElement();
+
+ if (m_optionElement->hasTagName(optgroupTag))
+ return static_cast<HTMLOptGroupElement*>(m_optionElement)->ownerSelectElement();
+
+ return 0;
+}
+
+int AccessibilityListBoxOption::listBoxOptionIndex() const
+{
+ if (!m_optionElement)
+ return -1;
+
+ HTMLSelectElement* selectElement = listBoxOptionParentNode();
+ if (!selectElement)
+ return -1;
+
+ const Vector<Element*>& listItems = selectElement->listItems();
+ unsigned length = listItems.size();
+ for (unsigned i = 0; i < length; i++)
+ if (listItems[i] == m_optionElement)
+ return i;
+
+ return -1;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityListBoxOption.h b/Source/WebCore/accessibility/AccessibilityListBoxOption.h
new file mode 100644
index 0000000..597c731
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityListBoxOption.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2008 Apple 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 AccessibilityListBoxOption_h
+#define AccessibilityListBoxOption_h
+
+#include "AccessibilityObject.h"
+#include "HTMLElement.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class AccessibilityListBox;
+class Element;
+class HTMLElement;
+class HTMLSelectElement;
+
+class AccessibilityListBoxOption : public AccessibilityObject {
+
+private:
+ AccessibilityListBoxOption();
+public:
+ static PassRefPtr<AccessibilityListBoxOption> create();
+ virtual ~AccessibilityListBoxOption();
+
+ void setHTMLElement(HTMLElement* element) { m_optionElement = element; }
+
+ virtual AccessibilityRole roleValue() const { return ListBoxOptionRole; }
+ virtual bool accessibilityIsIgnored() const;
+ virtual bool isSelected() const;
+ virtual bool isEnabled() const;
+ virtual String stringValue() const;
+ virtual Element* actionElement() const;
+ virtual Node* node() const { return m_optionElement; }
+ virtual void setSelected(bool);
+ virtual bool canSetSelectedAttribute() const;
+
+ virtual IntRect elementRect() const;
+ virtual AccessibilityObject* parentObject() const;
+ bool isListBoxOption() const { return true; }
+
+private:
+ HTMLElement* m_optionElement;
+
+ virtual bool canHaveChildren() const { return false; }
+ HTMLSelectElement* listBoxOptionParentNode() const;
+ int listBoxOptionIndex() const;
+ IntRect listBoxOptionRect() const;
+ AccessibilityObject* listBoxOptionAccessibilityObject(HTMLElement*) const;
+};
+
+} // namespace WebCore
+
+#endif // AccessibilityListBoxOption_h
diff --git a/Source/WebCore/accessibility/AccessibilityMediaControls.cpp b/Source/WebCore/accessibility/AccessibilityMediaControls.cpp
new file mode 100644
index 0000000..2f98acd
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityMediaControls.cpp
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2009 Apple 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"
+
+#if ENABLE(VIDEO)
+
+#include "AccessibilityMediaControls.h"
+
+#include "AXObjectCache.h"
+#include "HTMLInputElement.h"
+#include "HTMLNames.h"
+#include "LocalizedStrings.h"
+#include "MediaControlElements.h"
+#include "RenderObject.h"
+#include "RenderSlider.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+
+AccessibilityMediaControl::AccessibilityMediaControl(RenderObject* renderer)
+ : AccessibilityRenderObject(renderer)
+{
+}
+
+PassRefPtr<AccessibilityObject> AccessibilityMediaControl::create(RenderObject* renderer)
+{
+ ASSERT(renderer->node() && renderer->node()->isMediaControlElement());
+
+ Node* node = renderer->node();
+ MediaControlElementType controlType;
+
+ if (node->hasTagName(inputTag))
+ controlType = static_cast<MediaControlInputElement*>(node)->displayType();
+ else
+ controlType = static_cast<MediaControlElement*>(node)->displayType();
+
+ PassRefPtr<AccessibilityObject> obj;
+ switch (controlType) {
+ case MediaSlider:
+ obj = AccessibilityMediaTimeline::create(renderer);
+ break;
+
+ case MediaCurrentTimeDisplay:
+ case MediaTimeRemainingDisplay:
+ obj = AccessibilityMediaTimeDisplay::create(renderer);
+ break;
+
+ case MediaControlsPanel:
+ obj = AccessibilityMediaControlsContainer::create(renderer);
+ break;
+
+ default:
+ obj = adoptRef(new AccessibilityMediaControl(renderer));
+ break;
+ }
+
+ return obj;
+}
+
+MediaControlElementType AccessibilityMediaControl::controlType() const
+{
+ if (!renderer() || !renderer()->node())
+ return MediaTimelineContainer; // Timeline container is not accessible.
+
+ Node* node = renderer()->node();
+
+ if (node->hasTagName(inputTag))
+ return static_cast<MediaControlInputElement*>(node)->displayType();
+
+ return static_cast<MediaControlElement*>(node)->displayType();
+}
+
+String AccessibilityMediaControl::controlTypeName() const
+{
+ DEFINE_STATIC_LOCAL(const String, mediaFullscreenButtonName, ("FullscreenButton"));
+ DEFINE_STATIC_LOCAL(const String, mediaMuteButtonName, ("MuteButton"));
+ DEFINE_STATIC_LOCAL(const String, mediaPlayButtonName, ("PlayButton"));
+ DEFINE_STATIC_LOCAL(const String, mediaSeekBackButtonName, ("SeekBackButton"));
+ DEFINE_STATIC_LOCAL(const String, mediaSeekForwardButtonName, ("SeekForwardButton"));
+ DEFINE_STATIC_LOCAL(const String, mediaRewindButtonName, ("RewindButton"));
+ DEFINE_STATIC_LOCAL(const String, mediaReturnToRealtimeButtonName, ("ReturnToRealtimeButton"));
+ DEFINE_STATIC_LOCAL(const String, mediaUnMuteButtonName, ("UnMuteButton"));
+ DEFINE_STATIC_LOCAL(const String, mediaPauseButtonName, ("PauseButton"));
+ DEFINE_STATIC_LOCAL(const String, mediaStatusDisplayName, ("StatusDisplay"));
+ DEFINE_STATIC_LOCAL(const String, mediaCurrentTimeDisplay, ("CurrentTimeDisplay"));
+ DEFINE_STATIC_LOCAL(const String, mediaTimeRemainingDisplay, ("TimeRemainingDisplay"));
+ DEFINE_STATIC_LOCAL(const String, mediaShowClosedCaptionsButtonName, ("ShowClosedCaptionsButton"));
+ DEFINE_STATIC_LOCAL(const String, mediaHideClosedCaptionsButtonName, ("HideClosedCaptionsButton"));
+
+ switch (controlType()) {
+ case MediaFullscreenButton:
+ return mediaFullscreenButtonName;
+ case MediaMuteButton:
+ return mediaMuteButtonName;
+ case MediaPlayButton:
+ return mediaPlayButtonName;
+ case MediaSeekBackButton:
+ return mediaSeekBackButtonName;
+ case MediaSeekForwardButton:
+ return mediaSeekForwardButtonName;
+ case MediaRewindButton:
+ return mediaRewindButtonName;
+ case MediaReturnToRealtimeButton:
+ return mediaReturnToRealtimeButtonName;
+ case MediaUnMuteButton:
+ return mediaUnMuteButtonName;
+ case MediaPauseButton:
+ return mediaPauseButtonName;
+ case MediaStatusDisplay:
+ return mediaStatusDisplayName;
+ case MediaCurrentTimeDisplay:
+ return mediaCurrentTimeDisplay;
+ case MediaTimeRemainingDisplay:
+ return mediaTimeRemainingDisplay;
+ case MediaShowClosedCaptionsButton:
+ return mediaShowClosedCaptionsButtonName;
+ case MediaHideClosedCaptionsButton:
+ return mediaHideClosedCaptionsButtonName;
+
+ default:
+ break;
+ }
+
+ return String();
+}
+
+String AccessibilityMediaControl::title() const
+{
+ DEFINE_STATIC_LOCAL(const String, controlsPanel, ("ControlsPanel"));
+
+ if (controlType() == MediaControlsPanel)
+ return localizedMediaControlElementString(controlsPanel);
+
+ return AccessibilityRenderObject::title();
+}
+
+String AccessibilityMediaControl::accessibilityDescription() const
+{
+ return localizedMediaControlElementString(controlTypeName());
+}
+
+String AccessibilityMediaControl::helpText() const
+{
+ return localizedMediaControlElementHelpText(controlTypeName());
+}
+
+bool AccessibilityMediaControl::accessibilityIsIgnored() const
+{
+ if (!m_renderer || !m_renderer->style() || m_renderer->style()->visibility() != VISIBLE || controlType() == MediaTimelineContainer)
+ return true;
+
+ return false;
+}
+
+AccessibilityRole AccessibilityMediaControl::roleValue() const
+{
+ switch (controlType()) {
+ case MediaFullscreenButton:
+ case MediaMuteButton:
+ case MediaPlayButton:
+ case MediaSeekBackButton:
+ case MediaSeekForwardButton:
+ case MediaRewindButton:
+ case MediaReturnToRealtimeButton:
+ case MediaUnMuteButton:
+ case MediaPauseButton:
+ case MediaShowClosedCaptionsButton:
+ case MediaHideClosedCaptionsButton:
+ return ButtonRole;
+
+ case MediaStatusDisplay:
+ return StaticTextRole;
+
+ case MediaTimelineContainer:
+ return GroupRole;
+
+ default:
+ break;
+ }
+
+ return UnknownRole;
+}
+
+
+
+//
+// AccessibilityMediaControlsContainer
+
+AccessibilityMediaControlsContainer::AccessibilityMediaControlsContainer(RenderObject* renderer)
+ : AccessibilityMediaControl(renderer)
+{
+}
+
+PassRefPtr<AccessibilityObject> AccessibilityMediaControlsContainer::create(RenderObject* renderer)
+{
+ return adoptRef(new AccessibilityMediaControlsContainer(renderer));
+}
+
+String AccessibilityMediaControlsContainer::accessibilityDescription() const
+{
+ return localizedMediaControlElementString(elementTypeName());
+}
+
+String AccessibilityMediaControlsContainer::helpText() const
+{
+ return localizedMediaControlElementHelpText(elementTypeName());
+}
+
+bool AccessibilityMediaControlsContainer::controllingVideoElement() const
+{
+ if (!m_renderer->node())
+ return true;
+
+ MediaControlTimeDisplayElement* element = static_cast<MediaControlTimeDisplayElement*>(m_renderer->node());
+
+ return element->mediaElement()->isVideo();
+}
+
+const String AccessibilityMediaControlsContainer::elementTypeName() const
+{
+ DEFINE_STATIC_LOCAL(const String, videoElement, ("VideoElement"));
+ DEFINE_STATIC_LOCAL(const String, audioElement, ("AudioElement"));
+
+ if (controllingVideoElement())
+ return videoElement;
+ return audioElement;
+}
+
+
+//
+// AccessibilityMediaTimeline
+
+AccessibilityMediaTimeline::AccessibilityMediaTimeline(RenderObject* renderer)
+ : AccessibilitySlider(renderer)
+{
+}
+
+PassRefPtr<AccessibilityObject> AccessibilityMediaTimeline::create(RenderObject* renderer)
+{
+ return adoptRef(new AccessibilityMediaTimeline(renderer));
+}
+
+String AccessibilityMediaTimeline::valueDescription() const
+{
+ ASSERT(m_renderer->node()->hasTagName(inputTag));
+
+ float time = static_cast<HTMLInputElement*>(m_renderer->node())->value().toFloat();
+ return localizedMediaTimeDescription(time);
+}
+
+String AccessibilityMediaTimeline::helpText() const
+{
+ DEFINE_STATIC_LOCAL(const String, slider, ("Slider"));
+ return localizedMediaControlElementHelpText(slider);
+}
+
+
+//
+// AccessibilityMediaTimeDisplay
+
+AccessibilityMediaTimeDisplay::AccessibilityMediaTimeDisplay(RenderObject* renderer)
+ : AccessibilityMediaControl(renderer)
+{
+}
+
+PassRefPtr<AccessibilityObject> AccessibilityMediaTimeDisplay::create(RenderObject* renderer)
+{
+ return adoptRef(new AccessibilityMediaTimeDisplay(renderer));
+}
+
+bool AccessibilityMediaTimeDisplay::accessibilityIsIgnored() const
+{
+ if (!m_renderer || !m_renderer->style() || m_renderer->style()->visibility() != VISIBLE)
+ return true;
+
+ return !m_renderer->style()->width().value();
+}
+
+String AccessibilityMediaTimeDisplay::accessibilityDescription() const
+{
+ DEFINE_STATIC_LOCAL(const String, currentTimeDisplay, ("CurrentTimeDisplay"));
+ DEFINE_STATIC_LOCAL(const String, timeRemainingDisplay, ("TimeRemainingDisplay"));
+
+ if (controlType() == MediaCurrentTimeDisplay)
+ return localizedMediaControlElementString(currentTimeDisplay);
+
+ return localizedMediaControlElementString(timeRemainingDisplay);
+}
+
+String AccessibilityMediaTimeDisplay::stringValue() const
+{
+ if (!m_renderer || !m_renderer->node())
+ return String();
+
+ MediaControlTimeDisplayElement* element = static_cast<MediaControlTimeDisplayElement*>(m_renderer->node());
+ float time = element->currentValue();
+ return localizedMediaTimeDescription(fabsf(time));
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(VIDEO)
diff --git a/Source/WebCore/accessibility/AccessibilityMediaControls.h b/Source/WebCore/accessibility/AccessibilityMediaControls.h
new file mode 100644
index 0000000..6f51b2c
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityMediaControls.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2009 Apple 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 AccessibilityMediaControls_h
+#define AccessibilityMediaControls_h
+
+#if ENABLE(VIDEO)
+
+#include "AccessibilitySlider.h"
+#include "MediaControlElements.h"
+
+namespace WebCore {
+
+class AccessibilityMediaControl : public AccessibilityRenderObject {
+
+public:
+ static PassRefPtr<AccessibilityObject> create(RenderObject*);
+ virtual ~AccessibilityMediaControl() { }
+
+ virtual AccessibilityRole roleValue() const;
+ virtual bool accessibilityIsIgnored() const;
+
+ virtual String title() const;
+ virtual String accessibilityDescription() const;
+ virtual String helpText() const;
+
+protected:
+ AccessibilityMediaControl(RenderObject*);
+ MediaControlElementType controlType() const;
+ String controlTypeName() const;
+};
+
+
+class AccessibilityMediaTimeline : public AccessibilitySlider {
+
+public:
+ static PassRefPtr<AccessibilityObject> create(RenderObject*);
+ virtual ~AccessibilityMediaTimeline() { }
+
+ virtual bool isMediaTimeline() const { return true; }
+
+ virtual String helpText() const;
+ virtual String valueDescription() const;
+ const AtomicString& getAttribute(const QualifiedName& attribute) const;
+
+private:
+ AccessibilityMediaTimeline(RenderObject*);
+};
+
+
+class AccessibilityMediaControlsContainer : public AccessibilityMediaControl {
+
+public:
+ static PassRefPtr<AccessibilityObject> create(RenderObject*);
+ virtual ~AccessibilityMediaControlsContainer() { }
+
+ virtual AccessibilityRole roleValue() const { return ToolbarRole; }
+ virtual bool accessibilityIsIgnored() const { return false; }
+
+ virtual String helpText() const;
+ virtual String accessibilityDescription() const;
+
+private:
+ AccessibilityMediaControlsContainer(RenderObject*);
+ bool controllingVideoElement() const;
+ const String elementTypeName() const;
+};
+
+
+class AccessibilityMediaTimeDisplay : public AccessibilityMediaControl {
+
+public:
+ static PassRefPtr<AccessibilityObject> create(RenderObject*);
+ virtual ~AccessibilityMediaTimeDisplay() { }
+
+ virtual AccessibilityRole roleValue() const { return StaticTextRole; }
+ virtual bool accessibilityIsIgnored() const;
+
+ virtual String stringValue() const;
+ virtual String accessibilityDescription() const;
+
+private:
+ AccessibilityMediaTimeDisplay(RenderObject*);
+};
+
+
+} // namespace WebCore
+
+#endif // ENABLE(VIDEO)
+
+#endif // AccessibilityMediaControls_h
diff --git a/Source/WebCore/accessibility/AccessibilityMenuList.cpp b/Source/WebCore/accessibility/AccessibilityMenuList.cpp
new file mode 100644
index 0000000..bde4cd4
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityMenuList.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2010 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 "AccessibilityMenuList.h"
+
+#include "AXObjectCache.h"
+#include "AccessibilityMenuListPopup.h"
+#include "RenderMenuList.h"
+
+namespace WebCore {
+
+AccessibilityMenuList::AccessibilityMenuList(RenderMenuList* renderer)
+ : AccessibilityRenderObject(renderer)
+{
+}
+
+RenderMenuList* AccessibilityMenuList::renderer() const
+{
+ return toRenderMenuList(AccessibilityRenderObject::renderer());
+}
+
+bool AccessibilityMenuList::press() const
+{
+ RenderMenuList* menuList = static_cast<RenderMenuList*>(m_renderer);
+ if (menuList->popupIsVisible())
+ menuList->hidePopup();
+ else
+ menuList->showPopup();
+ return true;
+}
+
+void AccessibilityMenuList::addChildren()
+{
+ m_haveChildren = true;
+
+ AXObjectCache* cache = m_renderer->document()->axObjectCache();
+
+ AccessibilityObject* list = cache->getOrCreate(MenuListPopupRole);
+ if (!list)
+ return;
+
+ if (list->accessibilityPlatformIncludesObject() == IgnoreObject) {
+ cache->remove(list->axObjectID());
+ return;
+ }
+
+ static_cast<AccessibilityMenuListPopup*>(list)->setMenuList(this);
+ m_children.append(list);
+
+ list->addChildren();
+}
+
+void AccessibilityMenuList::childrenChanged()
+{
+ if (m_children.isEmpty())
+ return;
+
+ ASSERT(m_children.size() == 1);
+ m_children[0]->childrenChanged();
+}
+
+bool AccessibilityMenuList::isCollapsed() const
+{
+ return !static_cast<RenderMenuList*>(m_renderer)->popupIsVisible();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityMenuList.h b/Source/WebCore/accessibility/AccessibilityMenuList.h
new file mode 100644
index 0000000..4082f0a
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityMenuList.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 AccessibilityMenuList_h
+#define AccessibilityMenuList_h
+
+#include "AccessibilityRenderObject.h"
+
+namespace WebCore {
+
+class AccessibilityMenuList;
+class AccessibilityMenuListPopup;
+class HTMLOptionElement;
+class RenderMenuList;
+
+class AccessibilityMenuList : public AccessibilityRenderObject {
+public:
+ static PassRefPtr<AccessibilityMenuList> create(RenderMenuList* renderer) { return adoptRef(new AccessibilityMenuList(renderer)); }
+
+ virtual bool isCollapsed() const;
+ virtual bool press() const;
+
+ RenderMenuList* renderer() const;
+
+private:
+ AccessibilityMenuList(RenderMenuList*);
+
+ virtual bool isMenuList() const { return true; }
+ virtual AccessibilityRole roleValue() const { return PopUpButtonRole; }
+ virtual bool accessibilityIsIgnored() const { return false; }
+ virtual bool canSetFocusAttribute() const { return true; }
+
+ virtual void addChildren();
+ virtual void childrenChanged();
+};
+
+} // namespace WebCore
+
+#endif // AccessibilityMenuList_h
diff --git a/Source/WebCore/accessibility/AccessibilityMenuListOption.cpp b/Source/WebCore/accessibility/AccessibilityMenuListOption.cpp
new file mode 100644
index 0000000..5bca580
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityMenuListOption.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2010 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 "AccessibilityMenuListOption.h"
+
+#include "AXObjectCache.h"
+#include "AccessibilityMenuListPopup.h"
+#include "HTMLNames.h"
+#include "HTMLOptionElement.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+AccessibilityMenuListOption::AccessibilityMenuListOption()
+ : m_popup(0)
+{
+}
+
+void AccessibilityMenuListOption::setElement(HTMLElement* element)
+{
+ ASSERT_ARG(element, element->hasTagName(optionTag));
+ m_element = element;
+}
+
+Element* AccessibilityMenuListOption::actionElement() const
+{
+ return m_element.get();
+}
+
+AccessibilityObject* AccessibilityMenuListOption::parentObject() const
+{
+ return m_popup;
+}
+
+bool AccessibilityMenuListOption::isEnabled() const
+{
+ // disabled() returns true if the parent <select> element is disabled,
+ // which we don't want.
+ return !static_cast<HTMLOptionElement*>(m_element.get())->ownElementDisabled();
+}
+
+bool AccessibilityMenuListOption::isVisible() const
+{
+ // In a single-option select with the popup collapsed, only the selected
+ // item is considered visible.
+ return !m_popup->isOffScreen() || isSelected();
+}
+
+bool AccessibilityMenuListOption::isOffScreen() const
+{
+ // Invisible list options are considered to be offscreen.
+ return !isVisible();
+}
+
+bool AccessibilityMenuListOption::isSelected() const
+{
+ return static_cast<HTMLOptionElement*>(m_element.get())->selected();
+}
+
+void AccessibilityMenuListOption::setSelected(bool b)
+{
+ if (!canSetSelectedAttribute())
+ return;
+
+ static_cast<HTMLOptionElement*>(m_element.get())->setSelected(b);
+}
+
+String AccessibilityMenuListOption::nameForMSAA() const
+{
+ return stringValue();
+}
+
+bool AccessibilityMenuListOption::canSetSelectedAttribute() const
+{
+ return isEnabled();
+}
+
+IntRect AccessibilityMenuListOption::elementRect() const
+{
+ AccessibilityObject* parent = parentObject();
+ ASSERT(parent->isMenuListPopup());
+
+ AccessibilityObject* grandparent = parent->parentObject();
+ ASSERT(grandparent->isMenuList());
+
+ return grandparent->elementRect();
+}
+
+String AccessibilityMenuListOption::stringValue() const
+{
+ return static_cast<HTMLOptionElement*>(m_element.get())->text();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityMenuListOption.h b/Source/WebCore/accessibility/AccessibilityMenuListOption.h
new file mode 100644
index 0000000..9393d56
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityMenuListOption.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2010 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 AccessibilityMenuListOption_h
+#define AccessibilityMenuListOption_h
+
+#include "AccessibilityObject.h"
+
+namespace WebCore {
+
+class AccessibilityMenuListPopup;
+class HTMLElement;
+
+class AccessibilityMenuListOption : public AccessibilityObject {
+public:
+ static PassRefPtr<AccessibilityMenuListOption> create() { return adoptRef(new AccessibilityMenuListOption); }
+
+ void setElement(HTMLElement*);
+ void setParent(AccessibilityMenuListPopup* popup) { m_popup = popup; }
+
+private:
+ AccessibilityMenuListOption();
+
+ virtual bool isMenuListOption() const { return true; }
+
+ virtual AccessibilityRole roleValue() const { return MenuListOptionRole; }
+ virtual bool canHaveChildren() const { return false; }
+ virtual IntSize size() const { return elementRect().size(); }
+
+ virtual Element* actionElement() const;
+ virtual AccessibilityObject* parentObject() const;
+ virtual bool isEnabled() const;
+ virtual bool isVisible() const;
+ virtual bool isOffScreen() const;
+ virtual bool isSelected() const;
+ virtual String nameForMSAA() const;
+ virtual void setSelected(bool);
+ virtual bool canSetSelectedAttribute() const;
+ virtual IntRect elementRect() const;
+ virtual String stringValue() const;
+
+ RefPtr<HTMLElement> m_element;
+ AccessibilityMenuListPopup* m_popup;
+};
+
+} // namespace WebCore
+
+#endif // AccessibilityMenuListOption_h
diff --git a/Source/WebCore/accessibility/AccessibilityMenuListPopup.cpp b/Source/WebCore/accessibility/AccessibilityMenuListPopup.cpp
new file mode 100644
index 0000000..515e02f
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityMenuListPopup.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2010 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 "AccessibilityMenuListPopup.h"
+
+#include "AXObjectCache.h"
+#include "AccessibilityMenuList.h"
+#include "AccessibilityMenuListOption.h"
+#include "HTMLNames.h"
+#include "HTMLSelectElement.h"
+#include "RenderMenuList.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+AccessibilityMenuListPopup::AccessibilityMenuListPopup()
+ : m_menuList(0)
+{
+}
+
+bool AccessibilityMenuListPopup::isVisible() const
+{
+ return false;
+}
+
+bool AccessibilityMenuListPopup::isOffScreen() const
+{
+ return m_menuList->isCollapsed();
+}
+
+AccessibilityObject* AccessibilityMenuListPopup::parentObject() const
+{
+ return m_menuList;
+}
+
+bool AccessibilityMenuListPopup::isEnabled() const
+{
+ return m_menuList->isEnabled();
+}
+
+AccessibilityMenuListOption* AccessibilityMenuListPopup::menuListOptionAccessibilityObject(HTMLElement* element) const
+{
+ if (!element || !element->hasTagName(optionTag))
+ return 0;
+
+ AccessibilityObject* object = m_menuList->renderer()->document()->axObjectCache()->getOrCreate(MenuListOptionRole);
+ ASSERT(object->isMenuListOption());
+
+ AccessibilityMenuListOption* option = static_cast<AccessibilityMenuListOption*>(object);
+ option->setElement(element);
+
+ return option;
+}
+
+bool AccessibilityMenuListPopup::press() const
+{
+ m_menuList->press();
+ return true;
+}
+
+void AccessibilityMenuListPopup::addChildren()
+{
+ Node* selectNode = m_menuList->renderer()->node();
+ if (!selectNode)
+ return;
+
+ m_haveChildren = true;
+
+ ASSERT(selectNode->hasTagName(selectTag));
+
+ const Vector<Element*>& listItems = static_cast<HTMLSelectElement*>(selectNode)->listItems();
+ unsigned length = listItems.size();
+ for (unsigned i = 0; i < length; i++) {
+ // The cast to HTMLElement below is safe because the only other possible listItem type
+ // would be a WMLElement, but WML builds don't use accessbility features at all.
+ AccessibilityMenuListOption* option = menuListOptionAccessibilityObject(static_cast<HTMLElement*>(listItems[i]));
+ if (option) {
+ option->setParent(this);
+ m_children.append(option);
+ }
+ }
+}
+
+void AccessibilityMenuListPopup::childrenChanged()
+{
+ for (size_t i = m_children.size(); i > 0 ; --i) {
+ AccessibilityObject* child = m_children[i - 1].get();
+ if (child->actionElement() && !child->actionElement()->attached()) {
+ m_menuList->renderer()->document()->axObjectCache()->remove(child->axObjectID());
+ m_children.remove(i - 1);
+ }
+ }
+}
+
+void AccessibilityMenuListPopup::setMenuList(AccessibilityMenuList* menuList)
+{
+ ASSERT_ARG(menuList, menuList);
+ ASSERT(!m_menuList);
+ m_menuList = menuList;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityMenuListPopup.h b/Source/WebCore/accessibility/AccessibilityMenuListPopup.h
new file mode 100644
index 0000000..88fbf7c
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityMenuListPopup.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 AccessibilityMenuListPopup_h
+#define AccessibilityMenuListPopup_h
+
+#include "AccessibilityObject.h"
+
+namespace WebCore {
+
+class AccessibilityMenuList;
+class AccessibilityMenuListOption;
+class HTMLElement;
+
+class AccessibilityMenuListPopup : public AccessibilityObject {
+public:
+ static PassRefPtr<AccessibilityMenuListPopup> create() { return adoptRef(new AccessibilityMenuListPopup); }
+
+ void setMenuList(AccessibilityMenuList*);
+
+ virtual bool isEnabled() const;
+ virtual bool isOffScreen() const;
+
+private:
+ AccessibilityMenuListPopup();
+
+ virtual bool isMenuListPopup() const { return true; }
+
+ virtual IntRect elementRect() const { return IntRect(); }
+ virtual IntSize size() const { return IntSize(); }
+ virtual AccessibilityRole roleValue() const { return MenuListPopupRole; }
+
+ virtual bool isVisible() const;
+ virtual AccessibilityObject* parentObject() const;
+ virtual bool press() const;
+ virtual void addChildren();
+ virtual void childrenChanged();
+
+ AccessibilityMenuListOption* menuListOptionAccessibilityObject(HTMLElement*) const;
+
+ AccessibilityMenuList* m_menuList;
+};
+
+} // namespace WebCore
+
+#endif // AccessibilityMenuListPopup_h
diff --git a/Source/WebCore/accessibility/AccessibilityObject.cpp b/Source/WebCore/accessibility/AccessibilityObject.cpp
new file mode 100644
index 0000000..511bd3f
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityObject.cpp
@@ -0,0 +1,1071 @@
+/*
+ * Copyright (C) 2008, 2009 Apple 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 "AccessibilityObject.h"
+
+#include "AXObjectCache.h"
+#include "AccessibilityRenderObject.h"
+#include "CharacterNames.h"
+#include "FloatRect.h"
+#include "FocusController.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "LocalizedStrings.h"
+#include "NodeList.h"
+#include "NotImplemented.h"
+#include "Page.h"
+#include "RenderImage.h"
+#include "RenderListItem.h"
+#include "RenderListMarker.h"
+#include "RenderMenuList.h"
+#include "RenderTextControl.h"
+#include "RenderTheme.h"
+#include "RenderView.h"
+#include "RenderWidget.h"
+#include "SelectionController.h"
+#include "TextIterator.h"
+#include "htmlediting.h"
+#include "visible_units.h"
+#include <wtf/StdLibExtras.h>
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+AccessibilityObject::AccessibilityObject()
+ : m_id(0)
+ , m_haveChildren(false)
+ , m_role(UnknownRole)
+#if PLATFORM(GTK)
+ , m_wrapper(0)
+#endif
+{
+}
+
+AccessibilityObject::~AccessibilityObject()
+{
+ ASSERT(isDetached());
+}
+
+void AccessibilityObject::detach()
+{
+#if HAVE(ACCESSIBILITY)
+ setWrapper(0);
+#endif
+}
+
+AccessibilityObject* AccessibilityObject::parentObjectUnignored() const
+{
+ AccessibilityObject* parent;
+ for (parent = parentObject(); parent && parent->accessibilityIsIgnored(); parent = parent->parentObject()) {
+ }
+
+ return parent;
+}
+
+AccessibilityObject* AccessibilityObject::firstAccessibleObjectFromNode(const Node* node)
+{
+ ASSERT(AXObjectCache::accessibilityEnabled());
+
+ if (!node)
+ return 0;
+
+ Document* document = node->document();
+ if (!document)
+ return 0;
+
+ AXObjectCache* cache = document->axObjectCache();
+
+ AccessibilityObject* accessibleObject = cache->getOrCreate(node->renderer());
+ while (accessibleObject && accessibleObject->accessibilityIsIgnored()) {
+ node = node->traverseNextNode();
+
+ while (node && !node->renderer())
+ node = node->traverseNextSibling();
+
+ if (!node)
+ return 0;
+
+ accessibleObject = cache->getOrCreate(node->renderer());
+ }
+
+ return accessibleObject;
+}
+
+bool AccessibilityObject::isARIAInput(AccessibilityRole ariaRole)
+{
+ return ariaRole == RadioButtonRole || ariaRole == CheckBoxRole || ariaRole == TextFieldRole;
+}
+
+bool AccessibilityObject::isARIAControl(AccessibilityRole ariaRole)
+{
+ return isARIAInput(ariaRole) || ariaRole == TextAreaRole || ariaRole == ButtonRole
+ || ariaRole == ComboBoxRole || ariaRole == SliderRole;
+}
+
+IntPoint AccessibilityObject::clickPoint() const
+{
+ IntRect rect = elementRect();
+ return IntPoint(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2);
+}
+
+bool AccessibilityObject::press() const
+{
+ Element* actionElem = actionElement();
+ if (!actionElem)
+ return false;
+ if (Frame* f = actionElem->document()->frame())
+ f->loader()->resetMultipleFormSubmissionProtection();
+ actionElem->accessKeyAction(true);
+ return true;
+}
+
+String AccessibilityObject::language() const
+{
+ const AtomicString& lang = getAttribute(langAttr);
+ if (!lang.isEmpty())
+ return lang;
+
+ AccessibilityObject* parent = parentObject();
+
+ // as a last resort, fall back to the content language specified in the meta tag
+ if (!parent) {
+ Document* doc = document();
+ if (doc)
+ return doc->contentLanguage();
+ return nullAtom;
+ }
+
+ return parent->language();
+}
+
+VisiblePositionRange AccessibilityObject::visiblePositionRangeForUnorderedPositions(const VisiblePosition& visiblePos1, const VisiblePosition& visiblePos2) const
+{
+ if (visiblePos1.isNull() || visiblePos2.isNull())
+ return VisiblePositionRange();
+
+ VisiblePosition startPos;
+ VisiblePosition endPos;
+ bool alreadyInOrder;
+
+ // upstream is ordered before downstream for the same position
+ if (visiblePos1 == visiblePos2 && visiblePos2.affinity() == UPSTREAM)
+ alreadyInOrder = false;
+
+ // use selection order to see if the positions are in order
+ else
+ alreadyInOrder = VisibleSelection(visiblePos1, visiblePos2).isBaseFirst();
+
+ if (alreadyInOrder) {
+ startPos = visiblePos1;
+ endPos = visiblePos2;
+ } else {
+ startPos = visiblePos2;
+ endPos = visiblePos1;
+ }
+
+ return VisiblePositionRange(startPos, endPos);
+}
+
+VisiblePositionRange AccessibilityObject::positionOfLeftWord(const VisiblePosition& visiblePos) const
+{
+ VisiblePosition startPosition = startOfWord(visiblePos, LeftWordIfOnBoundary);
+ VisiblePosition endPosition = endOfWord(startPosition);
+ return VisiblePositionRange(startPosition, endPosition);
+}
+
+VisiblePositionRange AccessibilityObject::positionOfRightWord(const VisiblePosition& visiblePos) const
+{
+ VisiblePosition startPosition = startOfWord(visiblePos, RightWordIfOnBoundary);
+ VisiblePosition endPosition = endOfWord(startPosition);
+ return VisiblePositionRange(startPosition, endPosition);
+}
+
+static VisiblePosition updateAXLineStartForVisiblePosition(const VisiblePosition& visiblePosition)
+{
+ // A line in the accessibility sense should include floating objects, such as aligned image, as part of a line.
+ // So let's update the position to include that.
+ VisiblePosition tempPosition;
+ VisiblePosition startPosition = visiblePosition;
+ Position p;
+ RenderObject* renderer;
+ while (true) {
+ tempPosition = startPosition.previous();
+ if (tempPosition.isNull())
+ break;
+ p = tempPosition.deepEquivalent();
+ if (!p.node())
+ break;
+ renderer = p.node()->renderer();
+ if (!renderer || (renderer->isRenderBlock() && !p.deprecatedEditingOffset()))
+ break;
+ InlineBox* box;
+ int ignoredCaretOffset;
+ p.getInlineBoxAndOffset(tempPosition.affinity(), box, ignoredCaretOffset);
+ if (box)
+ break;
+ startPosition = tempPosition;
+ }
+
+ return startPosition;
+}
+
+VisiblePositionRange AccessibilityObject::leftLineVisiblePositionRange(const VisiblePosition& visiblePos) const
+{
+ if (visiblePos.isNull())
+ return VisiblePositionRange();
+
+ // make a caret selection for the position before marker position (to make sure
+ // we move off of a line start)
+ VisiblePosition prevVisiblePos = visiblePos.previous();
+ if (prevVisiblePos.isNull())
+ return VisiblePositionRange();
+
+ VisiblePosition startPosition = startOfLine(prevVisiblePos);
+
+ // keep searching for a valid line start position. Unless the VisiblePosition is at the very beginning, there should
+ // always be a valid line range. However, startOfLine will return null for position next to a floating object,
+ // since floating object doesn't really belong to any line.
+ // This check will reposition the marker before the floating object, to ensure we get a line start.
+ if (startPosition.isNull()) {
+ while (startPosition.isNull() && prevVisiblePos.isNotNull()) {
+ prevVisiblePos = prevVisiblePos.previous();
+ startPosition = startOfLine(prevVisiblePos);
+ }
+ } else
+ startPosition = updateAXLineStartForVisiblePosition(startPosition);
+
+ VisiblePosition endPosition = endOfLine(prevVisiblePos);
+ return VisiblePositionRange(startPosition, endPosition);
+}
+
+VisiblePositionRange AccessibilityObject::rightLineVisiblePositionRange(const VisiblePosition& visiblePos) const
+{
+ if (visiblePos.isNull())
+ return VisiblePositionRange();
+
+ // make sure we move off of a line end
+ VisiblePosition nextVisiblePos = visiblePos.next();
+ if (nextVisiblePos.isNull())
+ return VisiblePositionRange();
+
+ VisiblePosition startPosition = startOfLine(nextVisiblePos);
+
+ // fetch for a valid line start position
+ if (startPosition.isNull()) {
+ startPosition = visiblePos;
+ nextVisiblePos = nextVisiblePos.next();
+ } else
+ startPosition = updateAXLineStartForVisiblePosition(startPosition);
+
+ VisiblePosition endPosition = endOfLine(nextVisiblePos);
+
+ // as long as the position hasn't reached the end of the doc, keep searching for a valid line end position
+ // Unless the VisiblePosition is at the very end, there should always be a valid line range. However, endOfLine will
+ // return null for position by a floating object, since floating object doesn't really belong to any line.
+ // This check will reposition the marker after the floating object, to ensure we get a line end.
+ while (endPosition.isNull() && nextVisiblePos.isNotNull()) {
+ nextVisiblePos = nextVisiblePos.next();
+ endPosition = endOfLine(nextVisiblePos);
+ }
+
+ return VisiblePositionRange(startPosition, endPosition);
+}
+
+VisiblePositionRange AccessibilityObject::sentenceForPosition(const VisiblePosition& visiblePos) const
+{
+ // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
+ // Related? <rdar://problem/3927736> Text selection broken in 8A336
+ VisiblePosition startPosition = startOfSentence(visiblePos);
+ VisiblePosition endPosition = endOfSentence(startPosition);
+ return VisiblePositionRange(startPosition, endPosition);
+}
+
+VisiblePositionRange AccessibilityObject::paragraphForPosition(const VisiblePosition& visiblePos) const
+{
+ VisiblePosition startPosition = startOfParagraph(visiblePos);
+ VisiblePosition endPosition = endOfParagraph(startPosition);
+ return VisiblePositionRange(startPosition, endPosition);
+}
+
+static VisiblePosition startOfStyleRange(const VisiblePosition visiblePos)
+{
+ RenderObject* renderer = visiblePos.deepEquivalent().node()->renderer();
+ RenderObject* startRenderer = renderer;
+ RenderStyle* style = renderer->style();
+
+ // traverse backward by renderer to look for style change
+ for (RenderObject* r = renderer->previousInPreOrder(); r; r = r->previousInPreOrder()) {
+ // skip non-leaf nodes
+ if (r->firstChild())
+ continue;
+
+ // stop at style change
+ if (r->style() != style)
+ break;
+
+ // remember match
+ startRenderer = r;
+ }
+
+ return VisiblePosition(startRenderer->node(), 0, VP_DEFAULT_AFFINITY);
+}
+
+static VisiblePosition endOfStyleRange(const VisiblePosition& visiblePos)
+{
+ RenderObject* renderer = visiblePos.deepEquivalent().node()->renderer();
+ RenderObject* endRenderer = renderer;
+ RenderStyle* style = renderer->style();
+
+ // traverse forward by renderer to look for style change
+ for (RenderObject* r = renderer->nextInPreOrder(); r; r = r->nextInPreOrder()) {
+ // skip non-leaf nodes
+ if (r->firstChild())
+ continue;
+
+ // stop at style change
+ if (r->style() != style)
+ break;
+
+ // remember match
+ endRenderer = r;
+ }
+
+ return lastDeepEditingPositionForNode(endRenderer->node());
+}
+
+VisiblePositionRange AccessibilityObject::styleRangeForPosition(const VisiblePosition& visiblePos) const
+{
+ if (visiblePos.isNull())
+ return VisiblePositionRange();
+
+ return VisiblePositionRange(startOfStyleRange(visiblePos), endOfStyleRange(visiblePos));
+}
+
+// NOTE: Consider providing this utility method as AX API
+VisiblePositionRange AccessibilityObject::visiblePositionRangeForRange(const PlainTextRange& range) const
+{
+ unsigned textLength = getLengthForTextRange();
+ if (range.start + range.length > textLength)
+ return VisiblePositionRange();
+
+ VisiblePosition startPosition = visiblePositionForIndex(range.start);
+ startPosition.setAffinity(DOWNSTREAM);
+ VisiblePosition endPosition = visiblePositionForIndex(range.start + range.length);
+ return VisiblePositionRange(startPosition, endPosition);
+}
+
+static bool replacedNodeNeedsCharacter(Node* replacedNode)
+{
+ // we should always be given a rendered node and a replaced node, but be safe
+ // replaced nodes are either attachments (widgets) or images
+ if (!replacedNode || !replacedNode->renderer() || !replacedNode->renderer()->isReplaced() || replacedNode->isTextNode())
+ return false;
+
+ // create an AX object, but skip it if it is not supposed to be seen
+ AccessibilityObject* object = replacedNode->renderer()->document()->axObjectCache()->getOrCreate(replacedNode->renderer());
+ if (object->accessibilityIsIgnored())
+ return false;
+
+ return true;
+}
+
+// Finds a RenderListItem parent give a node.
+static RenderListItem* renderListItemContainerForNode(Node* node)
+{
+ for (; node; node = node->parentNode()) {
+ RenderBoxModelObject* renderer = node->renderBoxModelObject();
+ if (renderer && renderer->isListItem())
+ return toRenderListItem(renderer);
+ }
+ return 0;
+}
+
+// Returns the text associated with a list marker if this node is contained within a list item.
+String AccessibilityObject::listMarkerTextForNodeAndPosition(Node* node, const VisiblePosition& visiblePositionStart) const
+{
+ // If the range does not contain the start of the line, the list marker text should not be included.
+ if (!isStartOfLine(visiblePositionStart))
+ return String();
+
+ RenderListItem* listItem = renderListItemContainerForNode(node);
+ if (!listItem)
+ return String();
+
+ // If this is in a list item, we need to manually add the text for the list marker
+ // because a RenderListMarker does not have a Node equivalent and thus does not appear
+ // when iterating text.
+ const String& markerText = listItem->markerText();
+ if (markerText.isEmpty())
+ return String();
+
+ // Append text, plus the period that follows the text.
+ // FIXME: Not all list marker styles are followed by a period, but this
+ // sounds much better when there is a synthesized pause because of a period.
+ Vector<UChar> resultVector;
+ resultVector.append(markerText.characters(), markerText.length());
+ resultVector.append('.');
+ resultVector.append(' ');
+
+ return String::adopt(resultVector);
+}
+
+String AccessibilityObject::stringForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
+{
+ if (visiblePositionRange.isNull())
+ return String();
+
+ Vector<UChar> resultVector;
+ RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end);
+ for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
+ // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
+ if (it.length()) {
+ // Add a textual representation for list marker text
+ String listMarkerText = listMarkerTextForNodeAndPosition(it.node(), visiblePositionRange.start);
+ if (!listMarkerText.isEmpty())
+ resultVector.append(listMarkerText.characters(), listMarkerText.length());
+
+ resultVector.append(it.characters(), it.length());
+ } else {
+ // locate the node and starting offset for this replaced range
+ int exception = 0;
+ Node* node = it.range()->startContainer(exception);
+ ASSERT(node == it.range()->endContainer(exception));
+ int offset = it.range()->startOffset(exception);
+
+ if (replacedNodeNeedsCharacter(node->childNode(offset)))
+ resultVector.append(objectReplacementCharacter);
+ }
+ }
+
+ return String::adopt(resultVector);
+}
+
+int AccessibilityObject::lengthForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
+{
+ // FIXME: Multi-byte support
+ if (visiblePositionRange.isNull())
+ return -1;
+
+ int length = 0;
+ RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end);
+ for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
+ // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
+ if (it.length())
+ length += it.length();
+ else {
+ // locate the node and starting offset for this replaced range
+ int exception = 0;
+ Node* node = it.range()->startContainer(exception);
+ ASSERT(node == it.range()->endContainer(exception));
+ int offset = it.range()->startOffset(exception);
+
+ if (replacedNodeNeedsCharacter(node->childNode(offset)))
+ length++;
+ }
+ }
+
+ return length;
+}
+
+VisiblePosition AccessibilityObject::nextWordEnd(const VisiblePosition& visiblePos) const
+{
+ if (visiblePos.isNull())
+ return VisiblePosition();
+
+ // make sure we move off of a word end
+ VisiblePosition nextVisiblePos = visiblePos.next();
+ if (nextVisiblePos.isNull())
+ return VisiblePosition();
+
+ return endOfWord(nextVisiblePos, LeftWordIfOnBoundary);
+}
+
+VisiblePosition AccessibilityObject::previousWordStart(const VisiblePosition& visiblePos) const
+{
+ if (visiblePos.isNull())
+ return VisiblePosition();
+
+ // make sure we move off of a word start
+ VisiblePosition prevVisiblePos = visiblePos.previous();
+ if (prevVisiblePos.isNull())
+ return VisiblePosition();
+
+ return startOfWord(prevVisiblePos, RightWordIfOnBoundary);
+}
+
+VisiblePosition AccessibilityObject::nextLineEndPosition(const VisiblePosition& visiblePos) const
+{
+ if (visiblePos.isNull())
+ return VisiblePosition();
+
+ // to make sure we move off of a line end
+ VisiblePosition nextVisiblePos = visiblePos.next();
+ if (nextVisiblePos.isNull())
+ return VisiblePosition();
+
+ VisiblePosition endPosition = endOfLine(nextVisiblePos);
+
+ // as long as the position hasn't reached the end of the doc, keep searching for a valid line end position
+ // There are cases like when the position is next to a floating object that'll return null for end of line. This code will avoid returning null.
+ while (endPosition.isNull() && nextVisiblePos.isNotNull()) {
+ nextVisiblePos = nextVisiblePos.next();
+ endPosition = endOfLine(nextVisiblePos);
+ }
+
+ return endPosition;
+}
+
+VisiblePosition AccessibilityObject::previousLineStartPosition(const VisiblePosition& visiblePos) const
+{
+ if (visiblePos.isNull())
+ return VisiblePosition();
+
+ // make sure we move off of a line start
+ VisiblePosition prevVisiblePos = visiblePos.previous();
+ if (prevVisiblePos.isNull())
+ return VisiblePosition();
+
+ VisiblePosition startPosition = startOfLine(prevVisiblePos);
+
+ // as long as the position hasn't reached the beginning of the doc, keep searching for a valid line start position
+ // There are cases like when the position is next to a floating object that'll return null for start of line. This code will avoid returning null.
+ if (startPosition.isNull()) {
+ while (startPosition.isNull() && prevVisiblePos.isNotNull()) {
+ prevVisiblePos = prevVisiblePos.previous();
+ startPosition = startOfLine(prevVisiblePos);
+ }
+ } else
+ startPosition = updateAXLineStartForVisiblePosition(startPosition);
+
+ return startPosition;
+}
+
+VisiblePosition AccessibilityObject::nextSentenceEndPosition(const VisiblePosition& visiblePos) const
+{
+ // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
+ // Related? <rdar://problem/3927736> Text selection broken in 8A336
+ if (visiblePos.isNull())
+ return VisiblePosition();
+
+ // make sure we move off of a sentence end
+ VisiblePosition nextVisiblePos = visiblePos.next();
+ if (nextVisiblePos.isNull())
+ return VisiblePosition();
+
+ // an empty line is considered a sentence. If it's skipped, then the sentence parser will not
+ // see this empty line. Instead, return the end position of the empty line.
+ VisiblePosition endPosition;
+
+ String lineString = plainText(makeRange(startOfLine(nextVisiblePos), endOfLine(nextVisiblePos)).get());
+ if (lineString.isEmpty())
+ endPosition = nextVisiblePos;
+ else
+ endPosition = endOfSentence(nextVisiblePos);
+
+ return endPosition;
+}
+
+VisiblePosition AccessibilityObject::previousSentenceStartPosition(const VisiblePosition& visiblePos) const
+{
+ // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
+ // Related? <rdar://problem/3927736> Text selection broken in 8A336
+ if (visiblePos.isNull())
+ return VisiblePosition();
+
+ // make sure we move off of a sentence start
+ VisiblePosition previousVisiblePos = visiblePos.previous();
+ if (previousVisiblePos.isNull())
+ return VisiblePosition();
+
+ // treat empty line as a separate sentence.
+ VisiblePosition startPosition;
+
+ String lineString = plainText(makeRange(startOfLine(previousVisiblePos), endOfLine(previousVisiblePos)).get());
+ if (lineString.isEmpty())
+ startPosition = previousVisiblePos;
+ else
+ startPosition = startOfSentence(previousVisiblePos);
+
+ return startPosition;
+}
+
+VisiblePosition AccessibilityObject::nextParagraphEndPosition(const VisiblePosition& visiblePos) const
+{
+ if (visiblePos.isNull())
+ return VisiblePosition();
+
+ // make sure we move off of a paragraph end
+ VisiblePosition nextPos = visiblePos.next();
+ if (nextPos.isNull())
+ return VisiblePosition();
+
+ return endOfParagraph(nextPos);
+}
+
+VisiblePosition AccessibilityObject::previousParagraphStartPosition(const VisiblePosition& visiblePos) const
+{
+ if (visiblePos.isNull())
+ return VisiblePosition();
+
+ // make sure we move off of a paragraph start
+ VisiblePosition previousPos = visiblePos.previous();
+ if (previousPos.isNull())
+ return VisiblePosition();
+
+ return startOfParagraph(previousPos);
+}
+
+AccessibilityObject* AccessibilityObject::accessibilityObjectForPosition(const VisiblePosition& visiblePos) const
+{
+ if (visiblePos.isNull())
+ return 0;
+
+ RenderObject* obj = visiblePos.deepEquivalent().node()->renderer();
+ if (!obj)
+ return 0;
+
+ return obj->document()->axObjectCache()->getOrCreate(obj);
+}
+
+int AccessibilityObject::lineForPosition(const VisiblePosition& visiblePos) const
+{
+ if (visiblePos.isNull())
+ return 0;
+
+ unsigned lineCount = 0;
+ VisiblePosition currentVisiblePos = visiblePos;
+ VisiblePosition savedVisiblePos;
+
+ // move up until we get to the top
+ // FIXME: This only takes us to the top of the rootEditableElement, not the top of the
+ // top document.
+ while (currentVisiblePos.isNotNull() && !(inSameLine(currentVisiblePos, savedVisiblePos))) {
+ ++lineCount;
+ savedVisiblePos = currentVisiblePos;
+ VisiblePosition prevVisiblePos = previousLinePosition(currentVisiblePos, 0);
+ currentVisiblePos = prevVisiblePos;
+ }
+
+ return lineCount - 1;
+}
+
+// NOTE: Consider providing this utility method as AX API
+PlainTextRange AccessibilityObject::plainTextRangeForVisiblePositionRange(const VisiblePositionRange& positionRange) const
+{
+ int index1 = index(positionRange.start);
+ int index2 = index(positionRange.end);
+ if (index1 < 0 || index2 < 0 || index1 > index2)
+ return PlainTextRange();
+
+ return PlainTextRange(index1, index2 - index1);
+}
+
+// The composed character range in the text associated with this accessibility object that
+// is specified by the given screen coordinates. This parameterized attribute returns the
+// complete range of characters (including surrogate pairs of multi-byte glyphs) at the given
+// screen coordinates.
+// NOTE: This varies from AppKit when the point is below the last line. AppKit returns an
+// an error in that case. We return textControl->text().length(), 1. Does this matter?
+PlainTextRange AccessibilityObject::doAXRangeForPosition(const IntPoint& point) const
+{
+ int i = index(visiblePositionForPoint(point));
+ if (i < 0)
+ return PlainTextRange();
+
+ return PlainTextRange(i, 1);
+}
+
+// Given a character index, the range of text associated with this accessibility object
+// over which the style in effect at that character index applies.
+PlainTextRange AccessibilityObject::doAXStyleRangeForIndex(unsigned index) const
+{
+ VisiblePositionRange range = styleRangeForPosition(visiblePositionForIndex(index, false));
+ return plainTextRangeForVisiblePositionRange(range);
+}
+
+// Given an indexed character, the line number of the text associated with this accessibility
+// object that contains the character.
+unsigned AccessibilityObject::doAXLineForIndex(unsigned index)
+{
+ return lineForPosition(visiblePositionForIndex(index, false));
+}
+
+FrameView* AccessibilityObject::documentFrameView() const
+{
+ const AccessibilityObject* object = this;
+ while (object && !object->isAccessibilityRenderObject())
+ object = object->parentObject();
+
+ if (!object)
+ return 0;
+
+ return object->documentFrameView();
+}
+
+void AccessibilityObject::updateChildrenIfNecessary()
+{
+ if (!hasChildren())
+ addChildren();
+}
+
+void AccessibilityObject::clearChildren()
+{
+ m_children.clear();
+ m_haveChildren = false;
+}
+
+AccessibilityObject* AccessibilityObject::anchorElementForNode(Node* node)
+{
+ RenderObject* obj = node->renderer();
+ if (!obj)
+ return 0;
+
+ RefPtr<AccessibilityObject> axObj = obj->document()->axObjectCache()->getOrCreate(obj);
+ Element* anchor = axObj->anchorElement();
+ if (!anchor)
+ return 0;
+
+ RenderObject* anchorRenderer = anchor->renderer();
+ if (!anchorRenderer)
+ return 0;
+
+ return anchorRenderer->document()->axObjectCache()->getOrCreate(anchorRenderer);
+}
+
+void AccessibilityObject::ariaTreeRows(AccessibilityChildrenVector& result)
+{
+ AccessibilityChildrenVector axChildren = children();
+ unsigned count = axChildren.size();
+ for (unsigned k = 0; k < count; ++k) {
+ AccessibilityObject* obj = axChildren[k].get();
+
+ // Add tree items as the rows.
+ if (obj->roleValue() == TreeItemRole)
+ result.append(obj);
+
+ // Now see if this item also has rows hiding inside of it.
+ obj->ariaTreeRows(result);
+ }
+}
+
+void AccessibilityObject::ariaTreeItemContent(AccessibilityChildrenVector& result)
+{
+ // The ARIA tree item content are the item that are not other tree items or their containing groups.
+ AccessibilityChildrenVector axChildren = children();
+ unsigned count = axChildren.size();
+ for (unsigned k = 0; k < count; ++k) {
+ AccessibilityObject* obj = axChildren[k].get();
+ AccessibilityRole role = obj->roleValue();
+ if (role == TreeItemRole || role == GroupRole)
+ continue;
+
+ result.append(obj);
+ }
+}
+
+void AccessibilityObject::ariaTreeItemDisclosedRows(AccessibilityChildrenVector& result)
+{
+ AccessibilityChildrenVector axChildren = children();
+ unsigned count = axChildren.size();
+ for (unsigned k = 0; k < count; ++k) {
+ AccessibilityObject* obj = axChildren[k].get();
+
+ // Add tree items as the rows.
+ if (obj->roleValue() == TreeItemRole)
+ result.append(obj);
+ // If it's not a tree item, then descend into the group to find more tree items.
+ else
+ obj->ariaTreeRows(result);
+ }
+}
+
+const String& AccessibilityObject::actionVerb() const
+{
+ // FIXME: Need to add verbs for select elements.
+ DEFINE_STATIC_LOCAL(const String, buttonAction, (AXButtonActionVerb()));
+ DEFINE_STATIC_LOCAL(const String, textFieldAction, (AXTextFieldActionVerb()));
+ DEFINE_STATIC_LOCAL(const String, radioButtonAction, (AXRadioButtonActionVerb()));
+ DEFINE_STATIC_LOCAL(const String, checkedCheckBoxAction, (AXCheckedCheckBoxActionVerb()));
+ DEFINE_STATIC_LOCAL(const String, uncheckedCheckBoxAction, (AXUncheckedCheckBoxActionVerb()));
+ DEFINE_STATIC_LOCAL(const String, linkAction, (AXLinkActionVerb()));
+ DEFINE_STATIC_LOCAL(const String, menuListAction, (AXMenuListActionVerb()));
+ DEFINE_STATIC_LOCAL(const String, menuListPopupAction, (AXMenuListPopupActionVerb()));
+ DEFINE_STATIC_LOCAL(const String, noAction, ());
+
+ switch (roleValue()) {
+ case ButtonRole:
+ return buttonAction;
+ case TextFieldRole:
+ case TextAreaRole:
+ return textFieldAction;
+ case RadioButtonRole:
+ return radioButtonAction;
+ case CheckBoxRole:
+ return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction;
+ case LinkRole:
+ case WebCoreLinkRole:
+ return linkAction;
+ case PopUpButtonRole:
+ return menuListAction;
+ case MenuListPopupRole:
+ return menuListPopupAction;
+ default:
+ return noAction;
+ }
+}
+
+bool AccessibilityObject::ariaIsMultiline() const
+{
+ return equalIgnoringCase(getAttribute(aria_multilineAttr), "true");
+}
+
+const AtomicString& AccessibilityObject::invalidStatus() const
+{
+ DEFINE_STATIC_LOCAL(const AtomicString, invalidStatusFalse, ("false"));
+
+ // aria-invalid can return false (default), grammer, spelling, or true.
+ const AtomicString& ariaInvalid = getAttribute(aria_invalidAttr);
+
+ // If empty or not present, it should return false.
+ if (ariaInvalid.isEmpty())
+ return invalidStatusFalse;
+
+ return ariaInvalid;
+}
+
+const AtomicString& AccessibilityObject::getAttribute(const QualifiedName& attribute) const
+{
+ Node* elementNode = node();
+ if (!elementNode)
+ return nullAtom;
+
+ if (!elementNode->isElementNode())
+ return nullAtom;
+
+ Element* element = static_cast<Element*>(elementNode);
+ return element->fastGetAttribute(attribute);
+}
+
+// Lacking concrete evidence of orientation, horizontal means width > height. vertical is height > width;
+AccessibilityOrientation AccessibilityObject::orientation() const
+{
+ IntRect bounds = elementRect();
+ if (bounds.size().width() > bounds.size().height())
+ return AccessibilityOrientationHorizontal;
+ if (bounds.size().height() > bounds.size().width())
+ return AccessibilityOrientationVertical;
+
+ // A tie goes to horizontal.
+ return AccessibilityOrientationHorizontal;
+}
+
+typedef HashMap<String, AccessibilityRole, CaseFoldingHash> ARIARoleMap;
+
+struct RoleEntry {
+ String ariaRole;
+ AccessibilityRole webcoreRole;
+};
+
+static ARIARoleMap* createARIARoleMap()
+{
+ const RoleEntry roles[] = {
+ { "alert", ApplicationAlertRole },
+ { "alertdialog", ApplicationAlertDialogRole },
+ { "application", LandmarkApplicationRole },
+ { "article", DocumentArticleRole },
+ { "banner", LandmarkBannerRole },
+ { "button", ButtonRole },
+ { "checkbox", CheckBoxRole },
+ { "complementary", LandmarkComplementaryRole },
+ { "contentinfo", LandmarkContentInfoRole },
+ { "dialog", ApplicationDialogRole },
+ { "directory", DirectoryRole },
+ { "grid", TableRole },
+ { "gridcell", CellRole },
+ { "columnheader", ColumnHeaderRole },
+ { "combobox", ComboBoxRole },
+ { "definition", DefinitionListDefinitionRole },
+ { "document", DocumentRole },
+ { "rowheader", RowHeaderRole },
+ { "group", GroupRole },
+ { "heading", HeadingRole },
+ { "img", ImageRole },
+ { "link", WebCoreLinkRole },
+ { "list", ListRole },
+ { "listitem", ListItemRole },
+ { "listbox", ListBoxRole },
+ { "log", ApplicationLogRole },
+ // "option" isn't here because it may map to different roles depending on the parent element's role
+ { "main", LandmarkMainRole },
+ { "marquee", ApplicationMarqueeRole },
+ { "math", DocumentMathRole },
+ { "menu", MenuRole },
+ { "menubar", MenuBarRole },
+ // "menuitem" isn't here because it may map to different roles depending on the parent element's role
+ { "menuitemcheckbox", MenuItemRole },
+ { "menuitemradio", MenuItemRole },
+ { "note", DocumentNoteRole },
+ { "navigation", LandmarkNavigationRole },
+ { "option", ListBoxOptionRole },
+ { "presentation", PresentationalRole },
+ { "progressbar", ProgressIndicatorRole },
+ { "radio", RadioButtonRole },
+ { "radiogroup", RadioGroupRole },
+ { "region", DocumentRegionRole },
+ { "row", RowRole },
+ { "range", SliderRole },
+ { "scrollbar", ScrollBarRole },
+ { "search", LandmarkSearchRole },
+ { "separator", SplitterRole },
+ { "slider", SliderRole },
+ { "spinbutton", ProgressIndicatorRole },
+ { "status", ApplicationStatusRole },
+ { "tab", TabRole },
+ { "tablist", TabListRole },
+ { "tabpanel", TabPanelRole },
+ { "text", StaticTextRole },
+ { "textbox", TextAreaRole },
+ { "timer", ApplicationTimerRole },
+ { "toolbar", ToolbarRole },
+ { "tooltip", UserInterfaceTooltipRole },
+ { "tree", TreeRole },
+ { "treegrid", TreeGridRole },
+ { "treeitem", TreeItemRole }
+ };
+ ARIARoleMap* roleMap = new ARIARoleMap;
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(roles); ++i)
+ roleMap->set(roles[i].ariaRole, roles[i].webcoreRole);
+ return roleMap;
+}
+
+AccessibilityRole AccessibilityObject::ariaRoleToWebCoreRole(const String& value)
+{
+ ASSERT(!value.isEmpty());
+ static const ARIARoleMap* roleMap = createARIARoleMap();
+ return roleMap->get(value);
+}
+
+const AtomicString& AccessibilityObject::placeholderValue() const
+{
+ const AtomicString& placeholder = getAttribute(placeholderAttr);
+ if (!placeholder.isEmpty())
+ return placeholder;
+
+ return nullAtom;
+}
+
+bool AccessibilityObject::isInsideARIALiveRegion() const
+{
+ if (supportsARIALiveRegion())
+ return true;
+
+ for (AccessibilityObject* axParent = parentObject(); axParent; axParent = axParent->parentObject()) {
+ if (axParent->supportsARIALiveRegion())
+ return true;
+ }
+
+ return false;
+}
+
+bool AccessibilityObject::supportsARIAAttributes() const
+{
+ return supportsARIALiveRegion() || supportsARIADragging() || supportsARIADropping() || supportsARIAFlowTo() || supportsARIAOwns();
+}
+
+bool AccessibilityObject::supportsARIALiveRegion() const
+{
+ const AtomicString& liveRegion = ariaLiveRegionStatus();
+ return equalIgnoringCase(liveRegion, "polite") || equalIgnoringCase(liveRegion, "assertive");
+}
+
+AccessibilityObject* AccessibilityObject::elementAccessibilityHitTest(const IntPoint& point) const
+{
+ // Send the hit test back into the sub-frame if necessary.
+ if (isAttachment()) {
+ Widget* widget = widgetForAttachmentView();
+ if (widget && widget->isFrameView())
+ return axObjectCache()->getOrCreate(static_cast<ScrollView*>(widget))->accessibilityHitTest(point);
+ }
+
+ return const_cast<AccessibilityObject*>(this);
+}
+
+AXObjectCache* AccessibilityObject::axObjectCache() const
+{
+ Document* doc = document();
+ if (doc)
+ return doc->axObjectCache();
+ return 0;
+}
+
+AccessibilityObject* AccessibilityObject::focusedUIElement() const
+{
+ Document* doc = document();
+ if (!doc)
+ return 0;
+
+ Page* page = doc->page();
+ if (!page)
+ return 0;
+
+ return AXObjectCache::focusedUIElementForPage(page);
+}
+
+bool AccessibilityObject::supportsARIAExpanded() const
+{
+ return !getAttribute(aria_expandedAttr).isEmpty();
+}
+
+bool AccessibilityObject::isExpanded() const
+{
+ if (equalIgnoringCase(getAttribute(aria_expandedAttr), "true"))
+ return true;
+
+ return false;
+}
+
+AccessibilityButtonState AccessibilityObject::checkboxOrRadioValue() const
+{
+ // If this is a real checkbox or radio button, AccessibilityRenderObject will handle.
+ // If it's an ARIA checkbox or radio, the aria-checked attribute should be used.
+
+ const AtomicString& result = getAttribute(aria_checkedAttr);
+ if (equalIgnoringCase(result, "true"))
+ return ButtonStateOn;
+ if (equalIgnoringCase(result, "mixed"))
+ return ButtonStateMixed;
+
+ return ButtonStateOff;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityObject.h b/Source/WebCore/accessibility/AccessibilityObject.h
new file mode 100644
index 0000000..45b54fc
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityObject.h
@@ -0,0 +1,614 @@
+/*
+ * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2008 Nuanti Ltd.
+ *
+ * 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 AccessibilityObject_h
+#define AccessibilityObject_h
+
+#include "IntRect.h"
+#include "Range.h"
+#include "VisiblePosition.h"
+#include "VisibleSelection.h"
+#include <wtf/Forward.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+#if PLATFORM(MAC)
+#include <wtf/RetainPtr.h>
+#elif PLATFORM(WIN) && !OS(WINCE)
+#include "AccessibilityObjectWrapperWin.h"
+#include "COMPtr.h"
+#elif PLATFORM(CHROMIUM)
+#include "AccessibilityObjectWrapper.h"
+#endif
+
+typedef struct _NSRange NSRange;
+
+#ifdef __OBJC__
+@class AccessibilityObjectWrapper;
+@class NSArray;
+@class NSAttributedString;
+@class NSData;
+@class NSMutableAttributedString;
+@class NSString;
+@class NSValue;
+@class NSView;
+#else
+class NSArray;
+class NSAttributedString;
+class NSData;
+class NSMutableAttributedString;
+class NSString;
+class NSValue;
+class NSView;
+#if PLATFORM(GTK)
+typedef struct _AtkObject AtkObject;
+typedef struct _AtkObject AccessibilityObjectWrapper;
+#else
+class AccessibilityObjectWrapper;
+#endif
+#endif
+
+namespace WebCore {
+
+class AXObjectCache;
+class Element;
+class Frame;
+class FrameView;
+class HTMLAnchorElement;
+class HTMLAreaElement;
+class IntPoint;
+class IntSize;
+class Node;
+class RenderObject;
+class RenderListItem;
+class VisibleSelection;
+class Widget;
+
+typedef unsigned AXID;
+
+enum AccessibilityRole {
+ UnknownRole = 1,
+ ButtonRole,
+ RadioButtonRole,
+ CheckBoxRole,
+ SliderRole,
+ TabGroupRole,
+ TextFieldRole,
+ StaticTextRole,
+ TextAreaRole,
+ ScrollAreaRole,
+ PopUpButtonRole,
+ MenuButtonRole,
+ TableRole,
+ ApplicationRole,
+ GroupRole,
+ RadioGroupRole,
+ ListRole,
+ ScrollBarRole,
+ ValueIndicatorRole,
+ ImageRole,
+ MenuBarRole,
+ MenuRole,
+ MenuItemRole,
+ ColumnRole,
+ RowRole,
+ ToolbarRole,
+ BusyIndicatorRole,
+ ProgressIndicatorRole,
+ WindowRole,
+ DrawerRole,
+ SystemWideRole,
+ OutlineRole,
+ IncrementorRole,
+ BrowserRole,
+ ComboBoxRole,
+ SplitGroupRole,
+ SplitterRole,
+ ColorWellRole,
+ GrowAreaRole,
+ SheetRole,
+ HelpTagRole,
+ MatteRole,
+ RulerRole,
+ RulerMarkerRole,
+ LinkRole,
+ DisclosureTriangleRole,
+ GridRole,
+ CellRole,
+ ColumnHeaderRole,
+ RowHeaderRole,
+ // AppKit includes SortButtonRole but it is misnamed and really a subrole of ButtonRole so we do not include it here.
+
+ // WebCore-specific roles
+ WebCoreLinkRole,
+ ImageMapLinkRole,
+ ImageMapRole,
+ ListMarkerRole,
+ WebAreaRole,
+ HeadingRole,
+ ListBoxRole,
+ ListBoxOptionRole,
+ TableHeaderContainerRole,
+ DefinitionListTermRole,
+ DefinitionListDefinitionRole,
+ AnnotationRole,
+ SliderThumbRole,
+ IgnoredRole,
+ PresentationalRole,
+ TabRole,
+ TabListRole,
+ TabPanelRole,
+ TreeRole,
+ TreeGridRole,
+ TreeItemRole,
+ DirectoryRole,
+ EditableTextRole,
+ ListItemRole,
+ MenuListPopupRole,
+ MenuListOptionRole,
+
+ // ARIA Grouping roles
+ LandmarkApplicationRole,
+ LandmarkBannerRole,
+ LandmarkComplementaryRole,
+ LandmarkContentInfoRole,
+ LandmarkMainRole,
+ LandmarkNavigationRole,
+ LandmarkSearchRole,
+
+ ApplicationAlertRole,
+ ApplicationAlertDialogRole,
+ ApplicationDialogRole,
+ ApplicationLogRole,
+ ApplicationMarqueeRole,
+ ApplicationStatusRole,
+ ApplicationTimerRole,
+
+ DocumentRole,
+ DocumentArticleRole,
+ DocumentMathRole,
+ DocumentNoteRole,
+ DocumentRegionRole,
+
+ UserInterfaceTooltipRole
+};
+
+enum AccessibilityOrientation {
+ AccessibilityOrientationVertical,
+ AccessibilityOrientationHorizontal,
+};
+
+enum AccessibilityObjectInclusion {
+ IncludeObject,
+ IgnoreObject,
+ DefaultBehavior,
+};
+
+enum AccessibilityButtonState {
+ ButtonStateOff = 0,
+ ButtonStateOn,
+ ButtonStateMixed,
+};
+
+struct VisiblePositionRange {
+
+ VisiblePosition start;
+ VisiblePosition end;
+
+ VisiblePositionRange() {}
+
+ VisiblePositionRange(const VisiblePosition& s, const VisiblePosition& e)
+ : start(s)
+ , end(e)
+ { }
+
+ bool isNull() const { return start.isNull() || end.isNull(); }
+};
+
+struct PlainTextRange {
+
+ unsigned start;
+ unsigned length;
+
+ PlainTextRange()
+ : start(0)
+ , length(0)
+ { }
+
+ PlainTextRange(unsigned s, unsigned l)
+ : start(s)
+ , length(l)
+ { }
+
+ bool isNull() const { return !start && !length; }
+};
+
+class AccessibilityObject : public RefCounted<AccessibilityObject> {
+protected:
+ AccessibilityObject();
+public:
+ virtual ~AccessibilityObject();
+ virtual void detach();
+
+ typedef Vector<RefPtr<AccessibilityObject> > AccessibilityChildrenVector;
+
+ virtual bool isAccessibilityRenderObject() const { return false; }
+ virtual bool isAccessibilityScrollbar() const { return false; }
+ virtual bool isAccessibilityScrollView() const { return false; }
+
+ virtual bool isAnchor() const { return false; }
+ virtual bool isAttachment() const { return false; }
+ virtual bool isHeading() const { return false; }
+ virtual bool isLink() const { return false; }
+ virtual bool isImage() const { return false; }
+ virtual bool isNativeImage() const { return false; }
+ virtual bool isImageButton() const { return false; }
+ virtual bool isPasswordField() const { return false; }
+ virtual bool isNativeTextControl() const { return false; }
+ virtual bool isWebArea() const { return false; }
+ virtual bool isCheckbox() const { return roleValue() == CheckBoxRole; }
+ virtual bool isRadioButton() const { return roleValue() == RadioButtonRole; }
+ virtual bool isListBox() const { return roleValue() == ListBoxRole; }
+ virtual bool isMediaTimeline() const { return false; }
+ virtual bool isMenuRelated() const { return false; }
+ virtual bool isMenu() const { return false; }
+ virtual bool isMenuBar() const { return false; }
+ virtual bool isMenuButton() const { return false; }
+ virtual bool isMenuItem() const { return false; }
+ virtual bool isFileUploadButton() const { return false; }
+ virtual bool isInputImage() const { return false; }
+ virtual bool isProgressIndicator() const { return false; }
+ virtual bool isSlider() const { return false; }
+ virtual bool isInputSlider() const { return false; }
+ virtual bool isControl() const { return false; }
+ virtual bool isList() const { return false; }
+ virtual bool isAccessibilityTable() const { return false; }
+ virtual bool isDataTable() const { return false; }
+ virtual bool isTableRow() const { return false; }
+ virtual bool isTableColumn() const { return false; }
+ virtual bool isTableCell() const { return false; }
+ virtual bool isFieldset() const { return false; }
+ virtual bool isGroup() const { return false; }
+ virtual bool isARIATreeGridRow() const { return false; }
+ virtual bool isImageMapLink() const { return false; }
+ virtual bool isMenuList() const { return false; }
+ virtual bool isMenuListPopup() const { return false; }
+ virtual bool isMenuListOption() const { return false; }
+ bool isTextControl() const { return roleValue() == TextAreaRole || roleValue() == TextFieldRole; }
+ bool isTabList() const { return roleValue() == TabListRole; }
+ bool isTabItem() const { return roleValue() == TabRole; }
+ bool isRadioGroup() const { return roleValue() == RadioGroupRole; }
+ bool isComboBox() const { return roleValue() == ComboBoxRole; }
+ bool isTree() const { return roleValue() == TreeRole; }
+ bool isTreeItem() const { return roleValue() == TreeItemRole; }
+ bool isScrollbar() const { return roleValue() == ScrollBarRole; }
+ bool isButton() const { return roleValue() == ButtonRole; }
+ bool isListItem() const { return roleValue() == ListItemRole; }
+ bool isCheckboxOrRadio() const { return isCheckbox() || isRadioButton(); }
+ bool isScrollView() const { return roleValue() == ScrollAreaRole; }
+
+ virtual bool isChecked() const { return false; }
+ virtual bool isEnabled() const { return false; }
+ virtual bool isSelected() const { return false; }
+ virtual bool isFocused() const { return false; }
+ virtual bool isHovered() const { return false; }
+ virtual bool isIndeterminate() const { return false; }
+ virtual bool isLoaded() const { return false; }
+ virtual bool isMultiSelectable() const { return false; }
+ virtual bool isOffScreen() const { return false; }
+ virtual bool isPressed() const { return false; }
+ virtual bool isReadOnly() const { return false; }
+ virtual bool isVisited() const { return false; }
+ virtual bool isRequired() const { return false; }
+ virtual bool isLinked() const { return false; }
+ virtual bool isExpanded() const;
+ virtual bool isVisible() const { return true; }
+ virtual bool isCollapsed() const { return false; }
+ virtual void setIsExpanded(bool) { }
+
+ virtual bool canSetFocusAttribute() const { return false; }
+ virtual bool canSetTextRangeAttributes() const { return false; }
+ virtual bool canSetValueAttribute() const { return false; }
+ virtual bool canSetNumericValue() const { return false; }
+ virtual bool canSetSelectedAttribute() const { return false; }
+ virtual bool canSetSelectedChildrenAttribute() const { return false; }
+ virtual bool canSetExpandedAttribute() const { return false; }
+
+ // A programmatic way to set a name on an AccessibleObject.
+ virtual void setAccessibleName(String&) { }
+
+ virtual Node* node() const { return 0; }
+ bool accessibilityShouldUseUniqueId() const { return true; }
+ virtual bool accessibilityIsIgnored() const { return true; }
+
+ virtual int headingLevel() const { return 0; }
+ virtual AccessibilityButtonState checkboxOrRadioValue() const;
+ virtual String valueDescription() const { return String(); }
+ virtual float valueForRange() const { return 0.0f; }
+ virtual float maxValueForRange() const { return 0.0f; }
+ virtual float minValueForRange() const { return 0.0f; }
+ virtual AccessibilityObject* selectedRadioButton() { return 0; }
+ virtual AccessibilityObject* selectedTabItem() { return 0; }
+ virtual int layoutCount() const { return 0; }
+ virtual double estimatedLoadingProgress() const { return 0; }
+ static bool isARIAControl(AccessibilityRole);
+ static bool isARIAInput(AccessibilityRole);
+ virtual bool supportsARIAOwns() const { return false; }
+ virtual void ariaOwnsElements(AccessibilityChildrenVector&) const { }
+ virtual bool supportsARIAFlowTo() const { return false; }
+ virtual void ariaFlowToElements(AccessibilityChildrenVector&) const { }
+ virtual bool ariaHasPopup() const { return false; }
+ bool ariaIsMultiline() const;
+ virtual const AtomicString& invalidStatus() const;
+ bool supportsARIAExpanded() const;
+
+ // ARIA drag and drop
+ virtual bool supportsARIADropping() const { return false; }
+ virtual bool supportsARIADragging() const { return false; }
+ virtual bool isARIAGrabbed() { return false; }
+ virtual void setARIAGrabbed(bool) { }
+ virtual void determineARIADropEffects(Vector<String>&) { }
+
+ // Called on the root AX object to return the deepest available element.
+ virtual AccessibilityObject* accessibilityHitTest(const IntPoint&) const { return 0; }
+ // Called on the AX object after the render tree determines which is the right AccessibilityRenderObject.
+ virtual AccessibilityObject* elementAccessibilityHitTest(const IntPoint&) const;
+
+ virtual AccessibilityObject* focusedUIElement() const;
+
+ virtual AccessibilityObject* firstChild() const { return 0; }
+ virtual AccessibilityObject* lastChild() const { return 0; }
+ virtual AccessibilityObject* previousSibling() const { return 0; }
+ virtual AccessibilityObject* nextSibling() const { return 0; }
+ virtual AccessibilityObject* parentObject() const = 0;
+ virtual AccessibilityObject* parentObjectUnignored() const;
+ virtual AccessibilityObject* parentObjectIfExists() const { return 0; }
+ static AccessibilityObject* firstAccessibleObjectFromNode(const Node*);
+
+ virtual AccessibilityObject* observableObject() const { return 0; }
+ virtual void linkedUIElements(AccessibilityChildrenVector&) const { }
+ virtual AccessibilityObject* titleUIElement() const { return 0; }
+ virtual bool exposesTitleUIElement() const { return true; }
+ virtual AccessibilityObject* correspondingControlForLabelElement() const { return 0; }
+ virtual AccessibilityObject* scrollBar(AccessibilityOrientation) const { return 0; }
+
+ virtual AccessibilityRole ariaRoleAttribute() const { return UnknownRole; }
+ virtual bool isPresentationalChildOfAriaRole() const { return false; }
+ virtual bool ariaRoleHasPresentationalChildren() const { return false; }
+
+ void setRoleValue(AccessibilityRole role) { m_role = role; }
+ virtual AccessibilityRole roleValue() const { return m_role; }
+ virtual String ariaLabeledByAttribute() const { return String(); }
+ virtual String ariaDescribedByAttribute() const { return String(); }
+ virtual String accessibilityDescription() const { return String(); }
+
+ virtual AXObjectCache* axObjectCache() const;
+ AXID axObjectID() const { return m_id; }
+ void setAXObjectID(AXID axObjectID) { m_id = axObjectID; }
+
+ static AccessibilityObject* anchorElementForNode(Node*);
+ virtual Element* anchorElement() const { return 0; }
+ virtual Element* actionElement() const { return 0; }
+ virtual IntRect boundingBoxRect() const { return IntRect(); }
+ virtual IntRect elementRect() const = 0;
+ virtual IntSize size() const { return elementRect().size(); }
+ virtual IntPoint clickPoint() const;
+
+ virtual PlainTextRange selectedTextRange() const { return PlainTextRange(); }
+ unsigned selectionStart() const { return selectedTextRange().start; }
+ unsigned selectionEnd() const { return selectedTextRange().length; }
+
+ virtual KURL url() const { return KURL(); }
+ virtual VisibleSelection selection() const { return VisibleSelection(); }
+ virtual String stringValue() const { return String(); }
+ virtual String title() const { return String(); }
+ virtual String helpText() const { return String(); }
+ virtual String textUnderElement() const { return String(); }
+ virtual String text() const { return String(); }
+ virtual int textLength() const { return 0; }
+ virtual String selectedText() const { return String(); }
+ virtual const AtomicString& accessKey() const { return nullAtom; }
+ const String& actionVerb() const;
+ virtual Widget* widget() const { return 0; }
+ virtual Widget* widgetForAttachmentView() const { return 0; }
+ virtual Document* document() const { return 0; }
+ virtual FrameView* topDocumentFrameView() const { return 0; }
+ virtual FrameView* documentFrameView() const;
+ String language() const;
+ virtual unsigned hierarchicalLevel() const { return 0; }
+ const AtomicString& placeholderValue() const;
+
+ virtual void setFocused(bool) { }
+ virtual void setSelectedText(const String&) { }
+ virtual void setSelectedTextRange(const PlainTextRange&) { }
+ virtual void setValue(const String&) { }
+ virtual void setValue(float) { }
+ virtual void setSelected(bool) { }
+ virtual void setSelectedRows(AccessibilityChildrenVector&) { }
+
+ virtual void makeRangeVisible(const PlainTextRange&) { }
+ virtual bool press() const;
+ bool performDefaultAction() const { return press(); }
+
+ virtual AccessibilityOrientation orientation() const;
+ virtual void increment() { }
+ virtual void decrement() { }
+
+ virtual void childrenChanged() { }
+ virtual void contentChanged() { }
+ virtual const AccessibilityChildrenVector& children() { return m_children; }
+ virtual void addChildren() { }
+ virtual bool canHaveChildren() const { return true; }
+ virtual bool hasChildren() const { return m_haveChildren; }
+ virtual void updateChildrenIfNecessary();
+ virtual void selectedChildren(AccessibilityChildrenVector&) { }
+ virtual void visibleChildren(AccessibilityChildrenVector&) { }
+ virtual void tabChildren(AccessibilityChildrenVector&) { }
+ virtual bool shouldFocusActiveDescendant() const { return false; }
+ virtual AccessibilityObject* activeDescendant() const { return 0; }
+ virtual void handleActiveDescendantChanged() { }
+ virtual void handleAriaExpandedChanged() { }
+
+ static AccessibilityRole ariaRoleToWebCoreRole(const String&);
+ const AtomicString& getAttribute(const QualifiedName&) const;
+
+ virtual VisiblePositionRange visiblePositionRange() const { return VisiblePositionRange(); }
+ virtual VisiblePositionRange visiblePositionRangeForLine(unsigned) const { return VisiblePositionRange(); }
+
+ VisiblePositionRange visiblePositionRangeForUnorderedPositions(const VisiblePosition&, const VisiblePosition&) const;
+ VisiblePositionRange positionOfLeftWord(const VisiblePosition&) const;
+ VisiblePositionRange positionOfRightWord(const VisiblePosition&) const;
+ VisiblePositionRange leftLineVisiblePositionRange(const VisiblePosition&) const;
+ VisiblePositionRange rightLineVisiblePositionRange(const VisiblePosition&) const;
+ VisiblePositionRange sentenceForPosition(const VisiblePosition&) const;
+ VisiblePositionRange paragraphForPosition(const VisiblePosition&) const;
+ VisiblePositionRange styleRangeForPosition(const VisiblePosition&) const;
+ VisiblePositionRange visiblePositionRangeForRange(const PlainTextRange&) const;
+
+ String stringForVisiblePositionRange(const VisiblePositionRange&) const;
+ virtual IntRect boundsForVisiblePositionRange(const VisiblePositionRange&) const { return IntRect(); }
+ int lengthForVisiblePositionRange(const VisiblePositionRange&) const;
+ virtual void setSelectedVisiblePositionRange(const VisiblePositionRange&) const { }
+
+ virtual VisiblePosition visiblePositionForPoint(const IntPoint&) const { return VisiblePosition(); }
+ VisiblePosition nextVisiblePosition(const VisiblePosition& visiblePos) const { return visiblePos.next(); }
+ VisiblePosition previousVisiblePosition(const VisiblePosition& visiblePos) const { return visiblePos.previous(); }
+ VisiblePosition nextWordEnd(const VisiblePosition&) const;
+ VisiblePosition previousWordStart(const VisiblePosition&) const;
+ VisiblePosition nextLineEndPosition(const VisiblePosition&) const;
+ VisiblePosition previousLineStartPosition(const VisiblePosition&) const;
+ VisiblePosition nextSentenceEndPosition(const VisiblePosition&) const;
+ VisiblePosition previousSentenceStartPosition(const VisiblePosition&) const;
+ VisiblePosition nextParagraphEndPosition(const VisiblePosition&) const;
+ VisiblePosition previousParagraphStartPosition(const VisiblePosition&) const;
+ virtual VisiblePosition visiblePositionForIndex(unsigned, bool /*lastIndexOK */) const { return VisiblePosition(); }
+
+ virtual VisiblePosition visiblePositionForIndex(int) const { return VisiblePosition(); }
+ virtual int indexForVisiblePosition(const VisiblePosition&) const { return 0; }
+
+ AccessibilityObject* accessibilityObjectForPosition(const VisiblePosition&) const;
+ int lineForPosition(const VisiblePosition&) const;
+ PlainTextRange plainTextRangeForVisiblePositionRange(const VisiblePositionRange&) const;
+ virtual int index(const VisiblePosition&) const { return -1; }
+
+ virtual PlainTextRange doAXRangeForLine(unsigned) const { return PlainTextRange(); }
+ PlainTextRange doAXRangeForPosition(const IntPoint&) const;
+ virtual PlainTextRange doAXRangeForIndex(unsigned) const { return PlainTextRange(); }
+ PlainTextRange doAXStyleRangeForIndex(unsigned) const;
+
+ virtual String doAXStringForRange(const PlainTextRange&) const { return String(); }
+ virtual IntRect doAXBoundsForRange(const PlainTextRange&) const { return IntRect(); }
+ String listMarkerTextForNodeAndPosition(Node*, const VisiblePosition&) const;
+
+ unsigned doAXLineForIndex(unsigned);
+
+ virtual String stringValueForMSAA() const { return String(); }
+ virtual String stringRoleForMSAA() const { return String(); }
+ virtual String nameForMSAA() const { return String(); }
+ virtual String descriptionForMSAA() const { return String(); }
+ virtual AccessibilityRole roleValueForMSAA() const { return roleValue(); }
+
+ // Used by an ARIA tree to get all its rows.
+ void ariaTreeRows(AccessibilityChildrenVector&);
+ // Used by an ARIA tree item to get all of its direct rows that it can disclose.
+ void ariaTreeItemDisclosedRows(AccessibilityChildrenVector&);
+ // Used by an ARIA tree item to get only its content, and not its child tree items and groups.
+ void ariaTreeItemContent(AccessibilityChildrenVector&);
+
+ // ARIA live-region features.
+ bool supportsARIALiveRegion() const;
+ bool isInsideARIALiveRegion() const;
+ virtual const AtomicString& ariaLiveRegionStatus() const { return nullAtom; }
+ virtual const AtomicString& ariaLiveRegionRelevant() const { return nullAtom; }
+ virtual bool ariaLiveRegionAtomic() const { return false; }
+ virtual bool ariaLiveRegionBusy() const { return false; }
+
+ bool supportsARIAAttributes() const;
+
+ // CSS3 Speech properties.
+ virtual ESpeak speakProperty() const { return SpeakNormal; }
+
+#if HAVE(ACCESSIBILITY)
+#if PLATFORM(GTK)
+ AccessibilityObjectWrapper* wrapper() const;
+ void setWrapper(AccessibilityObjectWrapper*);
+#else
+ AccessibilityObjectWrapper* wrapper() const { return m_wrapper.get(); }
+ void setWrapper(AccessibilityObjectWrapper* wrapper)
+ {
+ m_wrapper = wrapper;
+ }
+#endif
+#endif
+
+#if HAVE(ACCESSIBILITY)
+ // a platform-specific method for determining if an attachment is ignored
+ bool accessibilityIgnoreAttachment() const;
+ // gives platforms the opportunity to indicate if and how an object should be included
+ AccessibilityObjectInclusion accessibilityPlatformIncludesObject() const;
+#else
+ bool accessibilityIgnoreAttachment() const { return true; }
+ AccessibilityObjectInclusion accessibilityPlatformIncludesObject() const { return DefaultBehavior; }
+#endif
+
+ // allows for an AccessibilityObject to update its render tree or perform
+ // other operations update type operations
+ virtual void updateBackingStore() { }
+
+protected:
+ AXID m_id;
+ AccessibilityChildrenVector m_children;
+ mutable bool m_haveChildren;
+ AccessibilityRole m_role;
+
+ virtual void clearChildren();
+ virtual bool isDetached() const { return true; }
+
+#if PLATFORM(GTK)
+ bool allowsTextRanges() const;
+ unsigned getLengthForTextRange() const;
+#else
+ bool allowsTextRanges() const { return isTextControl(); }
+ unsigned getLengthForTextRange() const { return text().length(); }
+#endif
+
+#if PLATFORM(MAC)
+ RetainPtr<AccessibilityObjectWrapper> m_wrapper;
+#elif PLATFORM(WIN) && !OS(WINCE)
+ COMPtr<AccessibilityObjectWrapper> m_wrapper;
+#elif PLATFORM(GTK)
+ AtkObject* m_wrapper;
+#elif PLATFORM(CHROMIUM)
+ RefPtr<AccessibilityObjectWrapper> m_wrapper;
+#endif
+};
+
+} // namespace WebCore
+
+#endif // AccessibilityObject_h
diff --git a/Source/WebCore/accessibility/AccessibilityProgressIndicator.cpp b/Source/WebCore/accessibility/AccessibilityProgressIndicator.cpp
new file mode 100644
index 0000000..c7202f0
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityProgressIndicator.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#if ENABLE(PROGRESS_TAG)
+
+#include "AccessibilityProgressIndicator.h"
+
+#include "FloatConversion.h"
+#include "HTMLNames.h"
+#include "HTMLProgressElement.h"
+#include "RenderObject.h"
+#include "RenderProgress.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+AccessibilityProgressIndicator::AccessibilityProgressIndicator(RenderProgress* renderer)
+ : AccessibilityRenderObject(renderer)
+{
+}
+
+PassRefPtr<AccessibilityProgressIndicator> AccessibilityProgressIndicator::create(RenderProgress* renderer)
+{
+ return adoptRef(new AccessibilityProgressIndicator(renderer));
+}
+
+bool AccessibilityProgressIndicator::accessibilityIsIgnored() const
+{
+ return accessibilityIsIgnoredBase() == IgnoreObject;
+}
+
+float AccessibilityProgressIndicator::valueForRange() const
+{
+ if (element()->position() >= 0)
+ return narrowPrecisionToFloat(element()->value());
+ // Indeterminate progress bar should return 0.
+ return 0.0f;
+}
+
+float AccessibilityProgressIndicator::maxValueForRange() const
+{
+ return narrowPrecisionToFloat(element()->max());
+}
+
+float AccessibilityProgressIndicator::minValueForRange() const
+{
+ return 0.0f;
+}
+
+HTMLProgressElement* AccessibilityProgressIndicator::element() const
+{
+ return toRenderProgress(m_renderer)->progressElement();
+}
+
+
+} // namespace WebCore
+
+#endif // ENABLE(PROGRESS_TAG)
diff --git a/Source/WebCore/accessibility/AccessibilityProgressIndicator.h b/Source/WebCore/accessibility/AccessibilityProgressIndicator.h
new file mode 100644
index 0000000..b52a619
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityProgressIndicator.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef AccessibilityProgressIndicator_h
+#define AccessibilityProgressIndicator_h
+
+#if ENABLE(PROGRESS_TAG)
+
+#include "AccessibilityRenderObject.h"
+
+namespace WebCore {
+
+class HTMLProgressElement;
+class RenderProgress;
+
+class AccessibilityProgressIndicator : public AccessibilityRenderObject {
+public:
+ static PassRefPtr<AccessibilityProgressIndicator> create(RenderProgress*);
+
+private:
+ virtual AccessibilityRole roleValue() const { return ProgressIndicatorRole; }
+
+ virtual bool isProgressIndicator() const { return true; }
+
+ virtual float valueForRange() const;
+ virtual float maxValueForRange() const;
+ virtual float minValueForRange() const;
+
+ AccessibilityProgressIndicator(RenderProgress*);
+
+ HTMLProgressElement* element() const;
+ virtual bool accessibilityIsIgnored() const;
+};
+
+
+} // namespace WebCore
+
+#endif // ENABLE(PROGRESS_TAG)
+
+#endif // AccessibilityProgressIndicator_h
diff --git a/Source/WebCore/accessibility/AccessibilityRenderObject.cpp b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp
new file mode 100644
index 0000000..7dc6840
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityRenderObject.cpp
@@ -0,0 +1,3688 @@
+/*
+* Copyright (C) 2008 Apple 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 "AccessibilityRenderObject.h"
+
+#include "AXObjectCache.h"
+#include "AccessibilityImageMapLink.h"
+#include "AccessibilityListBox.h"
+#include "CharacterNames.h"
+#include "EventNames.h"
+#include "FloatRect.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "HTMLAreaElement.h"
+#include "HTMLFormElement.h"
+#include "HTMLFrameElementBase.h"
+#include "HTMLImageElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLLabelElement.h"
+#include "HTMLMapElement.h"
+#include "HTMLOptGroupElement.h"
+#include "HTMLOptionElement.h"
+#include "HTMLOptionsCollection.h"
+#include "HTMLSelectElement.h"
+#include "HTMLTextAreaElement.h"
+#include "HitTestRequest.h"
+#include "HitTestResult.h"
+#include "LocalizedStrings.h"
+#include "NodeList.h"
+#include "ProgressTracker.h"
+#include "RenderButton.h"
+#include "RenderFieldset.h"
+#include "RenderFileUploadControl.h"
+#include "RenderHTMLCanvas.h"
+#include "RenderImage.h"
+#include "RenderInline.h"
+#include "RenderLayer.h"
+#include "RenderListBox.h"
+#include "RenderListMarker.h"
+#include "RenderMenuList.h"
+#include "RenderText.h"
+#include "RenderTextControl.h"
+#include "RenderTextFragment.h"
+#include "RenderTheme.h"
+#include "RenderView.h"
+#include "RenderWidget.h"
+#include "SelectElement.h"
+#include "SelectionController.h"
+#include "Text.h"
+#include "TextIterator.h"
+#include "htmlediting.h"
+#include "visible_units.h"
+#include <wtf/StdLibExtras.h>
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+AccessibilityRenderObject::AccessibilityRenderObject(RenderObject* renderer)
+ : AccessibilityObject()
+ , m_renderer(renderer)
+ , m_ariaRole(UnknownRole)
+ , m_childrenDirty(false)
+ , m_roleForMSAA(UnknownRole)
+{
+ m_role = determineAccessibilityRole();
+
+#ifndef NDEBUG
+ m_renderer->setHasAXObject(true);
+#endif
+}
+
+AccessibilityRenderObject::~AccessibilityRenderObject()
+{
+ ASSERT(isDetached());
+}
+
+PassRefPtr<AccessibilityRenderObject> AccessibilityRenderObject::create(RenderObject* renderer)
+{
+ return adoptRef(new AccessibilityRenderObject(renderer));
+}
+
+void AccessibilityRenderObject::detach()
+{
+ clearChildren();
+ AccessibilityObject::detach();
+
+#ifndef NDEBUG
+ if (m_renderer)
+ m_renderer->setHasAXObject(false);
+#endif
+ m_renderer = 0;
+}
+
+RenderBoxModelObject* AccessibilityRenderObject::renderBoxModelObject() const
+{
+ if (!m_renderer || !m_renderer->isBoxModelObject())
+ return 0;
+ return toRenderBoxModelObject(m_renderer);
+}
+
+static inline bool isInlineWithContinuation(RenderObject* object)
+{
+ if (!object->isBoxModelObject())
+ return false;
+
+ RenderBoxModelObject* renderer = toRenderBoxModelObject(object);
+ if (!renderer->isRenderInline())
+ return false;
+
+ return toRenderInline(renderer)->continuation();
+}
+
+static inline RenderObject* firstChildInContinuation(RenderObject* renderer)
+{
+ RenderObject* r = toRenderInline(renderer)->continuation();
+
+ while (r) {
+ if (r->isRenderBlock())
+ return r;
+ if (RenderObject* child = r->firstChild())
+ return child;
+ r = toRenderInline(r)->continuation();
+ }
+
+ return 0;
+}
+
+static inline RenderObject* firstChildConsideringContinuation(RenderObject* renderer)
+{
+ RenderObject* firstChild = renderer->firstChild();
+
+ if (!firstChild && isInlineWithContinuation(renderer))
+ firstChild = firstChildInContinuation(renderer);
+
+ return firstChild;
+}
+
+
+static inline RenderObject* lastChildConsideringContinuation(RenderObject* renderer)
+{
+ RenderObject* lastChild = renderer->lastChild();
+ RenderObject* prev;
+ RenderObject* cur = renderer;
+
+ if (!cur->isRenderInline() && !cur->isRenderBlock())
+ return renderer;
+
+ while (cur) {
+ prev = cur;
+
+ if (RenderObject* lc = cur->lastChild())
+ lastChild = lc;
+
+ if (cur->isRenderInline()) {
+ cur = toRenderInline(cur)->inlineElementContinuation();
+ ASSERT(cur || !toRenderInline(prev)->continuation());
+ } else
+ cur = toRenderBlock(cur)->inlineElementContinuation();
+ }
+
+ return lastChild;
+}
+
+AccessibilityObject* AccessibilityRenderObject::firstChild() const
+{
+ if (!m_renderer)
+ return 0;
+
+ RenderObject* firstChild = firstChildConsideringContinuation(m_renderer);
+
+ if (!firstChild)
+ return 0;
+
+ return axObjectCache()->getOrCreate(firstChild);
+}
+
+AccessibilityObject* AccessibilityRenderObject::lastChild() const
+{
+ if (!m_renderer)
+ return 0;
+
+ RenderObject* lastChild = lastChildConsideringContinuation(m_renderer);
+
+ if (!lastChild)
+ return 0;
+
+ return axObjectCache()->getOrCreate(lastChild);
+}
+
+static inline RenderInline* startOfContinuations(RenderObject* r)
+{
+ if (r->isInlineElementContinuation())
+ return toRenderInline(r->node()->renderer());
+
+ // Blocks with a previous continuation always have a next continuation
+ if (r->isRenderBlock() && toRenderBlock(r)->inlineElementContinuation())
+ return toRenderInline(toRenderBlock(r)->inlineElementContinuation()->node()->renderer());
+
+ return 0;
+}
+
+static inline RenderObject* endOfContinuations(RenderObject* renderer)
+{
+ RenderObject* prev = renderer;
+ RenderObject* cur = renderer;
+
+ if (!cur->isRenderInline() && !cur->isRenderBlock())
+ return renderer;
+
+ while (cur) {
+ prev = cur;
+ if (cur->isRenderInline()) {
+ cur = toRenderInline(cur)->inlineElementContinuation();
+ ASSERT(cur || !toRenderInline(prev)->continuation());
+ } else
+ cur = toRenderBlock(cur)->inlineElementContinuation();
+ }
+
+ return prev;
+}
+
+
+static inline RenderObject* childBeforeConsideringContinuations(RenderInline* r, RenderObject* child)
+{
+ RenderBoxModelObject* curContainer = r;
+ RenderObject* cur = 0;
+ RenderObject* prev = 0;
+
+ while (curContainer) {
+ if (curContainer->isRenderInline()) {
+ cur = curContainer->firstChild();
+ while (cur) {
+ if (cur == child)
+ return prev;
+ prev = cur;
+ cur = cur->nextSibling();
+ }
+
+ curContainer = toRenderInline(curContainer)->continuation();
+ } else if (curContainer->isRenderBlock()) {
+ if (curContainer == child)
+ return prev;
+
+ prev = curContainer;
+ curContainer = toRenderBlock(curContainer)->inlineElementContinuation();
+ }
+ }
+
+ ASSERT_NOT_REACHED();
+
+ return 0;
+}
+
+static inline bool firstChildIsInlineContinuation(RenderObject* renderer)
+{
+ return renderer->firstChild() && renderer->firstChild()->isInlineElementContinuation();
+}
+
+AccessibilityObject* AccessibilityRenderObject::previousSibling() const
+{
+ if (!m_renderer)
+ return 0;
+
+ RenderObject* previousSibling = 0;
+
+ // Case 1: The node is a block and is an inline's continuation. In that case, the inline's
+ // last child is our previous sibling (or further back in the continuation chain)
+ RenderInline* startOfConts;
+ if (m_renderer->isRenderBlock() && (startOfConts = startOfContinuations(m_renderer)))
+ previousSibling = childBeforeConsideringContinuations(startOfConts, m_renderer);
+
+ // Case 2: Anonymous block parent of the end of a continuation - skip all the way to before
+ // the parent of the start, since everything in between will be linked up via the continuation.
+ else if (m_renderer->isAnonymousBlock() && firstChildIsInlineContinuation(m_renderer))
+ previousSibling = startOfContinuations(m_renderer->firstChild())->parent()->previousSibling();
+
+ // Case 3: The node has an actual previous sibling
+ else if (RenderObject* ps = m_renderer->previousSibling())
+ previousSibling = ps;
+
+ // Case 4: This node has no previous siblings, but its parent is an inline,
+ // and is another node's inline continutation. Follow the continuation chain.
+ else if (m_renderer->parent()->isRenderInline() && (startOfConts = startOfContinuations(m_renderer->parent())))
+ previousSibling = childBeforeConsideringContinuations(startOfConts, m_renderer->parent()->firstChild());
+
+ if (!previousSibling)
+ return 0;
+
+ return axObjectCache()->getOrCreate(previousSibling);
+}
+
+static inline bool lastChildHasContinuation(RenderObject* renderer)
+{
+ return renderer->lastChild() && isInlineWithContinuation(renderer->lastChild());
+}
+
+AccessibilityObject* AccessibilityRenderObject::nextSibling() const
+{
+ if (!m_renderer)
+ return 0;
+
+ RenderObject* nextSibling = 0;
+
+ // Case 1: node is a block and has an inline continuation. Next sibling is the inline continuation's
+ // first child.
+ RenderInline* inlineContinuation;
+ if (m_renderer->isRenderBlock() && (inlineContinuation = toRenderBlock(m_renderer)->inlineElementContinuation()))
+ nextSibling = firstChildConsideringContinuation(inlineContinuation);
+
+ // Case 2: Anonymous block parent of the start of a continuation - skip all the way to
+ // after the parent of the end, since everything in between will be linked up via the continuation.
+ else if (m_renderer->isAnonymousBlock() && lastChildHasContinuation(m_renderer))
+ nextSibling = endOfContinuations(m_renderer->lastChild())->parent()->nextSibling();
+
+ // Case 3: node has an actual next sibling
+ else if (RenderObject* ns = m_renderer->nextSibling())
+ nextSibling = ns;
+
+ // Case 4: node is an inline with a continuation. Next sibling is the next sibling of the end
+ // of the continuation chain.
+ else if (isInlineWithContinuation(m_renderer))
+ nextSibling = endOfContinuations(m_renderer)->nextSibling();
+
+ // Case 5: node has no next sibling, and its parent is an inline with a continuation.
+ else if (isInlineWithContinuation(m_renderer->parent())) {
+ RenderObject* continuation = toRenderInline(m_renderer->parent())->continuation();
+
+ // Case 5a: continuation is a block - in this case the block itself is the next sibling.
+ if (continuation->isRenderBlock())
+ nextSibling = continuation;
+ // Case 5b: continuation is an inline - in this case the inline's first child is the next sibling
+ else
+ nextSibling = firstChildConsideringContinuation(continuation);
+ }
+
+ if (!nextSibling)
+ return 0;
+
+ return axObjectCache()->getOrCreate(nextSibling);
+}
+
+static RenderBoxModelObject* nextContinuation(RenderObject* renderer)
+{
+ ASSERT(renderer);
+ if (renderer->isRenderInline() && !renderer->isReplaced())
+ return toRenderInline(renderer)->continuation();
+ if (renderer->isRenderBlock())
+ return toRenderBlock(renderer)->inlineElementContinuation();
+ return 0;
+}
+
+RenderObject* AccessibilityRenderObject::renderParentObject() const
+{
+ if (!m_renderer)
+ return 0;
+
+ RenderObject* parent = m_renderer->parent();
+
+ // Case 1: node is a block and is an inline's continuation. Parent
+ // is the start of the continuation chain.
+ RenderObject* startOfConts = 0;
+ RenderObject* firstChild = 0;
+ if (m_renderer->isRenderBlock() && (startOfConts = startOfContinuations(m_renderer)))
+ parent = startOfConts;
+
+ // Case 2: node's parent is an inline which is some node's continuation; parent is
+ // the earliest node in the continuation chain.
+ else if (parent && parent->isRenderInline() && (startOfConts = startOfContinuations(parent)))
+ parent = startOfConts;
+
+ // Case 3: The first sibling is the beginning of a continuation chain. Find the origin of that continuation.
+ else if (parent && (firstChild = parent->firstChild()) && firstChild->node()) {
+ // Get the node's renderer and follow that continuation chain until the first child is found
+ RenderObject* nodeRenderFirstChild = firstChild->node()->renderer();
+ if (nodeRenderFirstChild != firstChild) {
+ for (RenderObject* contsTest = nodeRenderFirstChild; contsTest; contsTest = nextContinuation(contsTest)) {
+ if (contsTest == firstChild) {
+ parent = nodeRenderFirstChild->parent();
+ break;
+ }
+ }
+ }
+ }
+
+ return parent;
+}
+
+AccessibilityObject* AccessibilityRenderObject::parentObjectIfExists() const
+{
+ return axObjectCache()->get(renderParentObject());
+}
+
+AccessibilityObject* AccessibilityRenderObject::parentObject() const
+{
+ if (!m_renderer)
+ return 0;
+
+ if (ariaRoleAttribute() == MenuBarRole)
+ return axObjectCache()->getOrCreate(m_renderer->parent());
+
+ // menuButton and its corresponding menu are DOM siblings, but Accessibility needs them to be parent/child
+ if (ariaRoleAttribute() == MenuRole) {
+ AccessibilityObject* parent = menuButtonForMenu();
+ if (parent)
+ return parent;
+ }
+
+ RenderObject* parentObj = renderParentObject();
+ if (parentObj)
+ return axObjectCache()->getOrCreate(parentObj);
+
+ // WebArea's parent should be the scroll view containing it.
+ if (isWebArea())
+ return axObjectCache()->getOrCreate(m_renderer->frame()->view());
+
+ return 0;
+}
+
+bool AccessibilityRenderObject::isWebArea() const
+{
+ return roleValue() == WebAreaRole;
+}
+
+bool AccessibilityRenderObject::isImageButton() const
+{
+ return isNativeImage() && roleValue() == ButtonRole;
+}
+
+bool AccessibilityRenderObject::isAnchor() const
+{
+ return !isNativeImage() && isLink();
+}
+
+bool AccessibilityRenderObject::isNativeTextControl() const
+{
+ return m_renderer->isTextControl();
+}
+
+bool AccessibilityRenderObject::isNativeImage() const
+{
+ return m_renderer->isBoxModelObject() && toRenderBoxModelObject(m_renderer)->isImage();
+}
+
+bool AccessibilityRenderObject::isImage() const
+{
+ return roleValue() == ImageRole;
+}
+
+bool AccessibilityRenderObject::isAttachment() const
+{
+ RenderBoxModelObject* renderer = renderBoxModelObject();
+ if (!renderer)
+ return false;
+ // Widgets are the replaced elements that we represent to AX as attachments
+ bool isWidget = renderer->isWidget();
+ ASSERT(!isWidget || (renderer->isReplaced() && !isImage()));
+ return isWidget && ariaRoleAttribute() == UnknownRole;
+}
+
+bool AccessibilityRenderObject::isPasswordField() const
+{
+ ASSERT(m_renderer);
+ if (!m_renderer->node() || !m_renderer->node()->isHTMLElement())
+ return false;
+ if (ariaRoleAttribute() != UnknownRole)
+ return false;
+
+ InputElement* inputElement = toInputElement(static_cast<Element*>(m_renderer->node()));
+ if (!inputElement)
+ return false;
+
+ return inputElement->isPasswordField();
+}
+
+bool AccessibilityRenderObject::isFileUploadButton() const
+{
+ if (m_renderer && m_renderer->node() && m_renderer->node()->hasTagName(inputTag)) {
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->node());
+ return input->isFileUpload();
+ }
+
+ return false;
+}
+
+bool AccessibilityRenderObject::isInputImage() const
+{
+ Node* elementNode = node();
+ if (roleValue() == ButtonRole && elementNode && elementNode->hasTagName(inputTag)) {
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(elementNode);
+ return input->isImageButton();
+ }
+
+ return false;
+}
+
+bool AccessibilityRenderObject::isProgressIndicator() const
+{
+ return roleValue() == ProgressIndicatorRole;
+}
+
+bool AccessibilityRenderObject::isSlider() const
+{
+ return roleValue() == SliderRole;
+}
+
+bool AccessibilityRenderObject::isMenuRelated() const
+{
+ AccessibilityRole role = roleValue();
+ return role == MenuRole
+ || role == MenuBarRole
+ || role == MenuButtonRole
+ || role == MenuItemRole;
+}
+
+bool AccessibilityRenderObject::isMenu() const
+{
+ return roleValue() == MenuRole;
+}
+
+bool AccessibilityRenderObject::isMenuBar() const
+{
+ return roleValue() == MenuBarRole;
+}
+
+bool AccessibilityRenderObject::isMenuButton() const
+{
+ return roleValue() == MenuButtonRole;
+}
+
+bool AccessibilityRenderObject::isMenuItem() const
+{
+ return roleValue() == MenuItemRole;
+}
+
+bool AccessibilityRenderObject::isPressed() const
+{
+ ASSERT(m_renderer);
+ if (roleValue() != ButtonRole)
+ return false;
+
+ Node* node = m_renderer->node();
+ if (!node)
+ return false;
+
+ // If this is an ARIA button, check the aria-pressed attribute rather than node()->active()
+ if (ariaRoleAttribute() == ButtonRole) {
+ if (equalIgnoringCase(getAttribute(aria_pressedAttr), "true"))
+ return true;
+ return false;
+ }
+
+ return node->active();
+}
+
+bool AccessibilityRenderObject::isIndeterminate() const
+{
+ ASSERT(m_renderer);
+ if (!m_renderer->node() || !m_renderer->node()->isElementNode())
+ return false;
+
+ InputElement* inputElement = toInputElement(static_cast<Element*>(m_renderer->node()));
+ if (!inputElement)
+ return false;
+
+ return inputElement->isIndeterminate();
+}
+
+bool AccessibilityRenderObject::isNativeCheckboxOrRadio() const
+{
+ Node* elementNode = node();
+ if (elementNode && elementNode->isElementNode()) {
+ InputElement* input = toInputElement(static_cast<Element*>(elementNode));
+ if (input)
+ return input->isCheckbox() || input->isRadioButton();
+ }
+
+ return false;
+}
+
+bool AccessibilityRenderObject::isChecked() const
+{
+ ASSERT(m_renderer);
+ if (!m_renderer->node() || !m_renderer->node()->isElementNode())
+ return false;
+
+ // First test for native checkedness semantics
+ InputElement* inputElement = toInputElement(static_cast<Element*>(m_renderer->node()));
+ if (inputElement)
+ return inputElement->isChecked();
+
+ // Else, if this is an ARIA checkbox or radio, respect the aria-checked attribute
+ AccessibilityRole ariaRole = ariaRoleAttribute();
+ if (ariaRole == RadioButtonRole || ariaRole == CheckBoxRole) {
+ if (equalIgnoringCase(getAttribute(aria_checkedAttr), "true"))
+ return true;
+ return false;
+ }
+
+ // Otherwise it's not checked
+ return false;
+}
+
+bool AccessibilityRenderObject::isHovered() const
+{
+ ASSERT(m_renderer);
+ return m_renderer->node() && m_renderer->node()->hovered();
+}
+
+bool AccessibilityRenderObject::isMultiSelectable() const
+{
+ ASSERT(m_renderer);
+
+ const AtomicString& ariaMultiSelectable = getAttribute(aria_multiselectableAttr);
+ if (equalIgnoringCase(ariaMultiSelectable, "true"))
+ return true;
+ if (equalIgnoringCase(ariaMultiSelectable, "false"))
+ return false;
+
+ if (!m_renderer->isBoxModelObject() || !toRenderBoxModelObject(m_renderer)->isListBox())
+ return false;
+ return m_renderer->node() && static_cast<HTMLSelectElement*>(m_renderer->node())->multiple();
+}
+
+bool AccessibilityRenderObject::isReadOnly() const
+{
+ ASSERT(m_renderer);
+
+ if (isWebArea()) {
+ Document* document = m_renderer->document();
+ if (!document)
+ return true;
+
+ HTMLElement* body = document->body();
+ if (body && body->isContentEditable())
+ return false;
+
+ Frame* frame = document->frame();
+ if (!frame)
+ return true;
+
+ return !frame->isContentEditable();
+ }
+
+ if (m_renderer->isBoxModelObject()) {
+ RenderBoxModelObject* box = toRenderBoxModelObject(m_renderer);
+ if (box->isTextField())
+ return static_cast<HTMLInputElement*>(box->node())->readOnly();
+ if (box->isTextArea())
+ return static_cast<HTMLTextAreaElement*>(box->node())->readOnly();
+ }
+
+ return !m_renderer->node() || !m_renderer->node()->isContentEditable();
+}
+
+bool AccessibilityRenderObject::isOffScreen() const
+{
+ ASSERT(m_renderer);
+ IntRect contentRect = m_renderer->absoluteClippedOverflowRect();
+ FrameView* view = m_renderer->frame()->view();
+ FloatRect viewRect = view->visibleContentRect();
+ viewRect.intersect(contentRect);
+ return viewRect.isEmpty();
+}
+
+int AccessibilityRenderObject::headingLevel() const
+{
+ // headings can be in block flow and non-block flow
+ Node* element = node();
+ if (!element)
+ return 0;
+
+ if (ariaRoleAttribute() == HeadingRole)
+ return getAttribute(aria_levelAttr).toInt();
+
+ if (element->hasTagName(h1Tag))
+ return 1;
+
+ if (element->hasTagName(h2Tag))
+ return 2;
+
+ if (element->hasTagName(h3Tag))
+ return 3;
+
+ if (element->hasTagName(h4Tag))
+ return 4;
+
+ if (element->hasTagName(h5Tag))
+ return 5;
+
+ if (element->hasTagName(h6Tag))
+ return 6;
+
+ return 0;
+}
+
+bool AccessibilityRenderObject::isHeading() const
+{
+ return roleValue() == HeadingRole;
+}
+
+bool AccessibilityRenderObject::isLink() const
+{
+ return roleValue() == WebCoreLinkRole;
+}
+
+bool AccessibilityRenderObject::isControl() const
+{
+ if (!m_renderer)
+ return false;
+
+ Node* node = m_renderer->node();
+ return node && ((node->isElementNode() && static_cast<Element*>(node)->isFormControlElement())
+ || AccessibilityObject::isARIAControl(ariaRoleAttribute()));
+}
+
+bool AccessibilityRenderObject::isFieldset() const
+{
+ RenderBoxModelObject* renderer = renderBoxModelObject();
+ if (!renderer)
+ return false;
+ return renderer->isFieldset();
+}
+
+bool AccessibilityRenderObject::isGroup() const
+{
+ return roleValue() == GroupRole;
+}
+
+AccessibilityObject* AccessibilityRenderObject::selectedRadioButton()
+{
+ if (!isRadioGroup())
+ return 0;
+
+ // Find the child radio button that is selected (ie. the intValue == 1).
+ int count = m_children.size();
+ for (int i = 0; i < count; ++i) {
+ AccessibilityObject* object = m_children[i].get();
+ if (object->roleValue() == RadioButtonRole && object->checkboxOrRadioValue() == ButtonStateOn)
+ return object;
+ }
+ return 0;
+}
+
+AccessibilityObject* AccessibilityRenderObject::selectedTabItem()
+{
+ if (!isTabList())
+ return 0;
+
+ // Find the child tab item that is selected (ie. the intValue == 1).
+ AccessibilityObject::AccessibilityChildrenVector tabs;
+ tabChildren(tabs);
+
+ int count = tabs.size();
+ for (int i = 0; i < count; ++i) {
+ AccessibilityObject* object = m_children[i].get();
+ if (object->isTabItem() && object->isChecked())
+ return object;
+ }
+ return 0;
+}
+
+Element* AccessibilityRenderObject::anchorElement() const
+{
+ if (!m_renderer)
+ return 0;
+
+ AXObjectCache* cache = axObjectCache();
+ RenderObject* currRenderer;
+
+ // Search up the render tree for a RenderObject with a DOM node. Defer to an earlier continuation, though.
+ for (currRenderer = m_renderer; currRenderer && !currRenderer->node(); currRenderer = currRenderer->parent()) {
+ if (currRenderer->isAnonymousBlock()) {
+ RenderObject* continuation = toRenderBlock(currRenderer)->continuation();
+ if (continuation)
+ return cache->getOrCreate(continuation)->anchorElement();
+ }
+ }
+
+ // bail if none found
+ if (!currRenderer)
+ return 0;
+
+ // search up the DOM tree for an anchor element
+ // NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElement
+ Node* node = currRenderer->node();
+ for ( ; node; node = node->parentNode()) {
+ if (node->hasTagName(aTag) || (node->renderer() && cache->getOrCreate(node->renderer())->isAnchor()))
+ return static_cast<Element*>(node);
+ }
+
+ return 0;
+}
+
+Element* AccessibilityRenderObject::actionElement() const
+{
+ if (!m_renderer)
+ return 0;
+
+ Node* node = m_renderer->node();
+ if (node) {
+ if (node->hasTagName(inputTag)) {
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
+ if (!input->disabled() && (isCheckboxOrRadio() || input->isTextButton()))
+ return input;
+ } else if (node->hasTagName(buttonTag))
+ return static_cast<Element*>(node);
+ }
+
+ if (isFileUploadButton())
+ return static_cast<Element*>(m_renderer->node());
+
+ if (AccessibilityObject::isARIAInput(ariaRoleAttribute()))
+ return static_cast<Element*>(m_renderer->node());
+
+ if (isImageButton())
+ return static_cast<Element*>(m_renderer->node());
+
+ if (m_renderer->isBoxModelObject() && toRenderBoxModelObject(m_renderer)->isMenuList())
+ return static_cast<Element*>(m_renderer->node());
+
+ AccessibilityRole role = roleValue();
+ if (role == ButtonRole || role == PopUpButtonRole)
+ return static_cast<Element*>(m_renderer->node());
+
+ Element* elt = anchorElement();
+ if (!elt)
+ elt = mouseButtonListener();
+ return elt;
+}
+
+Element* AccessibilityRenderObject::mouseButtonListener() const
+{
+ Node* node = m_renderer->node();
+ if (!node)
+ return 0;
+
+ // check if our parent is a mouse button listener
+ while (node && !node->isElementNode())
+ node = node->parentNode();
+
+ if (!node)
+ return 0;
+
+ // FIXME: Do the continuation search like anchorElement does
+ for (Element* element = static_cast<Element*>(node); element; element = element->parentElement()) {
+ if (element->getAttributeEventListener(eventNames().clickEvent) || element->getAttributeEventListener(eventNames().mousedownEvent) || element->getAttributeEventListener(eventNames().mouseupEvent))
+ return element;
+ }
+
+ return 0;
+}
+
+void AccessibilityRenderObject::increment()
+{
+ if (roleValue() != SliderRole)
+ return;
+
+ changeValueByPercent(5);
+}
+
+void AccessibilityRenderObject::decrement()
+{
+ if (roleValue() != SliderRole)
+ return;
+
+ changeValueByPercent(-5);
+}
+
+static Element* siblingWithAriaRole(String role, Node* node)
+{
+ Node* sibling = node->parentNode()->firstChild();
+ while (sibling) {
+ if (sibling->isElementNode()) {
+ const AtomicString& siblingAriaRole = static_cast<Element*>(sibling)->getAttribute(roleAttr);
+ if (equalIgnoringCase(siblingAriaRole, role))
+ return static_cast<Element*>(sibling);
+ }
+ sibling = sibling->nextSibling();
+ }
+
+ return 0;
+}
+
+Element* AccessibilityRenderObject::menuElementForMenuButton() const
+{
+ if (ariaRoleAttribute() != MenuButtonRole)
+ return 0;
+
+ return siblingWithAriaRole("menu", renderer()->node());
+}
+
+AccessibilityObject* AccessibilityRenderObject::menuForMenuButton() const
+{
+ Element* menu = menuElementForMenuButton();
+ if (menu && menu->renderer())
+ return axObjectCache()->getOrCreate(menu->renderer());
+ return 0;
+}
+
+Element* AccessibilityRenderObject::menuItemElementForMenu() const
+{
+ if (ariaRoleAttribute() != MenuRole)
+ return 0;
+
+ return siblingWithAriaRole("menuitem", renderer()->node());
+}
+
+AccessibilityObject* AccessibilityRenderObject::menuButtonForMenu() const
+{
+ Element* menuItem = menuItemElementForMenu();
+
+ if (menuItem && menuItem->renderer()) {
+ // ARIA just has generic menu items. AppKit needs to know if this is a top level items like MenuBarButton or MenuBarItem
+ AccessibilityObject* menuItemAX = axObjectCache()->getOrCreate(menuItem->renderer());
+ if (menuItemAX->isMenuButton())
+ return menuItemAX;
+ }
+ return 0;
+}
+
+String AccessibilityRenderObject::helpText() const
+{
+ if (!m_renderer)
+ return String();
+
+ const AtomicString& ariaHelp = getAttribute(aria_helpAttr);
+ if (!ariaHelp.isEmpty())
+ return ariaHelp;
+
+ for (RenderObject* curr = m_renderer; curr; curr = curr->parent()) {
+ if (curr->node() && curr->node()->isHTMLElement()) {
+ const AtomicString& summary = static_cast<Element*>(curr->node())->getAttribute(summaryAttr);
+ if (!summary.isEmpty())
+ return summary;
+ const AtomicString& title = static_cast<Element*>(curr->node())->getAttribute(titleAttr);
+ if (!title.isEmpty())
+ return title;
+ }
+
+ // Only take help text from an ancestor element if its a group or an unknown role. If help was
+ // added to those kinds of elements, it is likely it was meant for a child element.
+ AccessibilityObject* axObj = axObjectCache()->getOrCreate(curr);
+ if (axObj) {
+ AccessibilityRole role = axObj->roleValue();
+ if (role != GroupRole && role != UnknownRole)
+ break;
+ }
+ }
+
+ return String();
+}
+
+unsigned AccessibilityRenderObject::hierarchicalLevel() const
+{
+ if (!m_renderer)
+ return 0;
+
+ Node* node = m_renderer->node();
+ if (!node || !node->isElementNode())
+ return 0;
+ Element* element = static_cast<Element*>(node);
+ String ariaLevel = element->getAttribute(aria_levelAttr);
+ if (!ariaLevel.isEmpty())
+ return ariaLevel.toInt();
+
+ // Only tree item will calculate its level through the DOM currently.
+ if (roleValue() != TreeItemRole)
+ return 0;
+
+ // Hierarchy leveling starts at 0.
+ // We measure tree hierarchy by the number of groups that the item is within.
+ unsigned level = 0;
+ AccessibilityObject* parent = parentObject();
+ while (parent) {
+ AccessibilityRole parentRole = parent->roleValue();
+ if (parentRole == GroupRole)
+ level++;
+ else if (parentRole == TreeRole)
+ break;
+
+ parent = parent->parentObject();
+ }
+
+ return level;
+}
+
+String AccessibilityRenderObject::textUnderElement() const
+{
+ if (!m_renderer)
+ return String();
+
+ if (isFileUploadButton())
+ return toRenderFileUploadControl(m_renderer)->buttonValue();
+
+ Node* node = m_renderer->node();
+ if (node) {
+ if (Frame* frame = node->document()->frame()) {
+ // catch stale WebCoreAXObject (see <rdar://problem/3960196>)
+ if (frame->document() != node->document())
+ return String();
+ return plainText(rangeOfContents(node).get(), TextIteratorIgnoresStyleVisibility);
+ }
+ }
+
+ // Sometimes text fragments don't have Node's associated with them (like when
+ // CSS content is used to insert text).
+ if (m_renderer->isText()) {
+ RenderText* renderTextObject = toRenderText(m_renderer);
+ if (renderTextObject->isTextFragment())
+ return String(static_cast<RenderTextFragment*>(m_renderer)->contentString());
+ }
+
+ // return the null string for anonymous text because it is non-trivial to get
+ // the actual text and, so far, that is not needed
+ return String();
+}
+
+Node* AccessibilityRenderObject::node() const
+{
+ return m_renderer ? m_renderer->node() : 0;
+}
+
+AccessibilityButtonState AccessibilityRenderObject::checkboxOrRadioValue() const
+{
+ if (isNativeCheckboxOrRadio())
+ return isChecked() ? ButtonStateOn : ButtonStateOff;
+
+ return AccessibilityObject::checkboxOrRadioValue();
+}
+
+String AccessibilityRenderObject::valueDescription() const
+{
+ // Only sliders and progress bars support value descriptions currently.
+ if (!isProgressIndicator() && !isSlider())
+ return String();
+
+ return getAttribute(aria_valuetextAttr).string();
+}
+
+float AccessibilityRenderObject::valueForRange() const
+{
+ if (!isProgressIndicator() && !isSlider() && !isScrollbar())
+ return 0.0f;
+
+ return getAttribute(aria_valuenowAttr).toFloat();
+}
+
+float AccessibilityRenderObject::maxValueForRange() const
+{
+ if (!isProgressIndicator() && !isSlider())
+ return 0.0f;
+
+ return getAttribute(aria_valuemaxAttr).toFloat();
+}
+
+float AccessibilityRenderObject::minValueForRange() const
+{
+ if (!isProgressIndicator() && !isSlider())
+ return 0.0f;
+
+ return getAttribute(aria_valueminAttr).toFloat();
+}
+
+String AccessibilityRenderObject::stringValue() const
+{
+ if (!m_renderer || isPasswordField())
+ return String();
+
+ RenderBoxModelObject* cssBox = renderBoxModelObject();
+
+ if (ariaRoleAttribute() == StaticTextRole) {
+ String staticText = text();
+ if (!staticText.length())
+ staticText = textUnderElement();
+ return staticText;
+ }
+
+ if (m_renderer->isText())
+ return textUnderElement();
+
+ if (cssBox && cssBox->isMenuList()) {
+ // RenderMenuList will go straight to the text() of its selected item.
+ // This has to be overriden in the case where the selected item has an aria label
+ SelectElement* selectNode = toSelectElement(static_cast<Element*>(m_renderer->node()));
+ int selectedIndex = selectNode->selectedIndex();
+ const Vector<Element*> listItems = selectNode->listItems();
+
+ Element* selectedOption = 0;
+ if (selectedIndex >= 0 && selectedIndex < (int)listItems.size())
+ selectedOption = listItems[selectedIndex];
+ if (selectedOption) {
+ String overridenDescription = selectedOption->getAttribute(aria_labelAttr);
+ if (!overridenDescription.isNull())
+ return overridenDescription;
+ }
+
+ return toRenderMenuList(m_renderer)->text();
+ }
+
+ if (m_renderer->isListMarker())
+ return toRenderListMarker(m_renderer)->text();
+
+ if (cssBox && cssBox->isRenderButton())
+ return toRenderButton(m_renderer)->text();
+
+ if (isWebArea()) {
+ if (m_renderer->frame())
+ return String();
+
+ // FIXME: should use startOfDocument and endOfDocument (or rangeForDocument?) here
+ VisiblePosition startVisiblePosition = m_renderer->positionForCoordinates(0, 0);
+ VisiblePosition endVisiblePosition = m_renderer->positionForCoordinates(INT_MAX, INT_MAX);
+ if (startVisiblePosition.isNull() || endVisiblePosition.isNull())
+ return String();
+
+ return plainText(makeRange(startVisiblePosition, endVisiblePosition).get(), TextIteratorIgnoresStyleVisibility);
+ }
+
+ if (isTextControl())
+ return text();
+
+ if (isFileUploadButton())
+ return toRenderFileUploadControl(m_renderer)->fileTextValue();
+
+ // FIXME: We might need to implement a value here for more types
+ // FIXME: It would be better not to advertise a value at all for the types for which we don't implement one;
+ // this would require subclassing or making accessibilityAttributeNames do something other than return a
+ // single static array.
+ return String();
+}
+
+// This function implements the ARIA accessible name as described by the Mozilla
+// ARIA Implementer's Guide.
+static String accessibleNameForNode(Node* node)
+{
+ if (node->isTextNode())
+ return static_cast<Text*>(node)->data();
+
+ if (node->hasTagName(inputTag))
+ return static_cast<HTMLInputElement*>(node)->value();
+
+ if (node->isHTMLElement()) {
+ const AtomicString& alt = static_cast<HTMLElement*>(node)->getAttribute(altAttr);
+ if (!alt.isEmpty())
+ return alt;
+ }
+
+ return String();
+}
+
+String AccessibilityRenderObject::accessibilityDescriptionForElements(Vector<Element*> &elements) const
+{
+ Vector<UChar> ariaLabel;
+ unsigned size = elements.size();
+ for (unsigned i = 0; i < size; ++i) {
+ Element* idElement = elements[i];
+
+ String nameFragment = accessibleNameForNode(idElement);
+ ariaLabel.append(nameFragment.characters(), nameFragment.length());
+ for (Node* n = idElement->firstChild(); n; n = n->traverseNextNode(idElement)) {
+ nameFragment = accessibleNameForNode(n);
+ ariaLabel.append(nameFragment.characters(), nameFragment.length());
+ }
+
+ if (i != size - 1)
+ ariaLabel.append(' ');
+ }
+ return String::adopt(ariaLabel);
+}
+
+
+void AccessibilityRenderObject::elementsFromAttribute(Vector<Element*>& elements, const QualifiedName& attribute) const
+{
+ Node* node = m_renderer->node();
+ if (!node || !node->isElementNode())
+ return;
+
+ Document* document = m_renderer->document();
+ if (!document)
+ return;
+
+ String idList = getAttribute(attribute).string();
+ if (idList.isEmpty())
+ return;
+
+ idList.replace('\n', ' ');
+ Vector<String> idVector;
+ idList.split(' ', idVector);
+
+ unsigned size = idVector.size();
+ for (unsigned i = 0; i < size; ++i) {
+ String idName = idVector[i];
+ Element* idElement = document->getElementById(idName);
+ if (idElement)
+ elements.append(idElement);
+ }
+}
+
+void AccessibilityRenderObject::ariaLabeledByElements(Vector<Element*>& elements) const
+{
+ elementsFromAttribute(elements, aria_labeledbyAttr);
+ if (!elements.size())
+ elementsFromAttribute(elements, aria_labelledbyAttr);
+}
+
+String AccessibilityRenderObject::ariaLabeledByAttribute() const
+{
+ Vector<Element*> elements;
+ ariaLabeledByElements(elements);
+
+ return accessibilityDescriptionForElements(elements);
+}
+
+static HTMLLabelElement* labelForElement(Element* element)
+{
+ RefPtr<NodeList> list = element->document()->getElementsByTagName("label");
+ unsigned len = list->length();
+ for (unsigned i = 0; i < len; i++) {
+ if (list->item(i)->hasTagName(labelTag)) {
+ HTMLLabelElement* label = static_cast<HTMLLabelElement*>(list->item(i));
+ if (label->control() == element)
+ return label;
+ }
+ }
+
+ return 0;
+}
+
+HTMLLabelElement* AccessibilityRenderObject::labelElementContainer() const
+{
+ if (!m_renderer)
+ return 0;
+
+ // the control element should not be considered part of the label
+ if (isControl())
+ return 0;
+
+ // find if this has a parent that is a label
+ for (Node* parentNode = m_renderer->node(); parentNode; parentNode = parentNode->parentNode()) {
+ if (parentNode->hasTagName(labelTag))
+ return static_cast<HTMLLabelElement*>(parentNode);
+ }
+
+ return 0;
+}
+
+String AccessibilityRenderObject::title() const
+{
+ AccessibilityRole ariaRole = ariaRoleAttribute();
+
+ if (!m_renderer)
+ return String();
+
+ Node* node = m_renderer->node();
+ if (!node)
+ return String();
+
+ String ariaLabel = ariaLabeledByAttribute();
+ if (!ariaLabel.isEmpty())
+ return ariaLabel;
+
+ const AtomicString& title = getAttribute(titleAttr);
+ if (!title.isEmpty())
+ return title;
+
+ bool isInputTag = node->hasTagName(inputTag);
+ if (isInputTag) {
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
+ if (input->isTextButton())
+ return input->value();
+ }
+
+ if (isInputTag || AccessibilityObject::isARIAInput(ariaRole) || isControl()) {
+ HTMLLabelElement* label = labelForElement(static_cast<Element*>(node));
+ if (label && !titleUIElement())
+ return label->innerText();
+ }
+
+ if (roleValue() == ButtonRole
+ || ariaRole == ListBoxOptionRole
+ || ariaRole == MenuItemRole
+ || ariaRole == MenuButtonRole
+ || ariaRole == RadioButtonRole
+ || ariaRole == CheckBoxRole
+ || ariaRole == TabRole
+ || ariaRole == PopUpButtonRole
+ || isHeading()
+ || isLink())
+ return textUnderElement();
+
+ return String();
+}
+
+String AccessibilityRenderObject::ariaDescribedByAttribute() const
+{
+ Vector<Element*> elements;
+ elementsFromAttribute(elements, aria_describedbyAttr);
+
+ return accessibilityDescriptionForElements(elements);
+}
+
+String AccessibilityRenderObject::ariaAccessibilityDescription() const
+{
+ const AtomicString& ariaLabel = getAttribute(aria_labelAttr);
+ if (!ariaLabel.isEmpty())
+ return ariaLabel;
+
+ String ariaDescription = ariaDescribedByAttribute();
+ if (!ariaDescription.isEmpty())
+ return ariaDescription;
+
+ return String();
+}
+
+String AccessibilityRenderObject::accessibilityDescription() const
+{
+ if (!m_renderer)
+ return String();
+
+ // Static text should not have a description, it should only have a stringValue.
+ if (roleValue() == StaticTextRole)
+ return String();
+
+ String ariaDescription = ariaAccessibilityDescription();
+ if (!ariaDescription.isEmpty())
+ return ariaDescription;
+
+ if (isImage() || isInputImage() || isNativeImage()) {
+ Node* node = m_renderer->node();
+ if (node && node->isHTMLElement()) {
+ const AtomicString& alt = static_cast<HTMLElement*>(node)->getAttribute(altAttr);
+ if (alt.isEmpty())
+ return String();
+ return alt;
+ }
+ }
+
+ if (isWebArea()) {
+ Document* document = m_renderer->document();
+
+ // Check if the HTML element has an aria-label for the webpage.
+ Element* documentElement = document->documentElement();
+ if (documentElement) {
+ const AtomicString& ariaLabel = documentElement->getAttribute(aria_labelAttr);
+ if (!ariaLabel.isEmpty())
+ return ariaLabel;
+ }
+
+ Node* owner = document->ownerElement();
+ if (owner) {
+ if (owner->hasTagName(frameTag) || owner->hasTagName(iframeTag)) {
+ const AtomicString& title = static_cast<HTMLFrameElementBase*>(owner)->getAttribute(titleAttr);
+ if (!title.isEmpty())
+ return title;
+ return static_cast<HTMLFrameElementBase*>(owner)->getAttribute(nameAttr);
+ }
+ if (owner->isHTMLElement())
+ return static_cast<HTMLElement*>(owner)->getAttribute(nameAttr);
+ }
+ owner = document->body();
+ if (owner && owner->isHTMLElement())
+ return static_cast<HTMLElement*>(owner)->getAttribute(nameAttr);
+ }
+
+ return String();
+}
+
+IntRect AccessibilityRenderObject::boundingBoxRect() const
+{
+ RenderObject* obj = m_renderer;
+
+ if (!obj)
+ return IntRect();
+
+ if (obj->node()) // If we are a continuation, we want to make sure to use the primary renderer.
+ obj = obj->node()->renderer();
+
+ // absoluteFocusRingQuads will query the hierarchy below this element, which for large webpages can be very slow.
+ // For a web area, which will have the most elements of any element, absoluteQuads should be used.
+ Vector<FloatQuad> quads;
+ if (obj->isText())
+ toRenderText(obj)->absoluteQuads(quads, RenderText::ClipToEllipsis);
+ else if (isWebArea())
+ obj->absoluteQuads(quads);
+ else
+ obj->absoluteFocusRingQuads(quads);
+ const size_t n = quads.size();
+ if (!n)
+ return IntRect();
+
+ IntRect result;
+ for (size_t i = 0; i < n; ++i) {
+ IntRect r = quads[i].enclosingBoundingBox();
+ if (!r.isEmpty()) {
+ if (obj->style()->hasAppearance())
+ obj->theme()->adjustRepaintRect(obj, r);
+ result.unite(r);
+ }
+ }
+
+ // The size of the web area should be the content size, not the clipped size.
+ if (isWebArea() && obj->frame()->view())
+ result.setSize(obj->frame()->view()->contentsSize());
+
+ return result;
+}
+
+IntRect AccessibilityRenderObject::checkboxOrRadioRect() const
+{
+ if (!m_renderer)
+ return IntRect();
+
+ HTMLLabelElement* label = labelForElement(static_cast<Element*>(m_renderer->node()));
+ if (!label || !label->renderer())
+ return boundingBoxRect();
+
+ IntRect labelRect = axObjectCache()->getOrCreate(label->renderer())->elementRect();
+ labelRect.unite(boundingBoxRect());
+ return labelRect;
+}
+
+IntRect AccessibilityRenderObject::elementRect() const
+{
+ // a checkbox or radio button should encompass its label
+ if (isCheckboxOrRadio())
+ return checkboxOrRadioRect();
+
+ return boundingBoxRect();
+}
+
+IntSize AccessibilityRenderObject::size() const
+{
+ IntRect rect = elementRect();
+ return rect.size();
+}
+
+IntPoint AccessibilityRenderObject::clickPoint() const
+{
+ // use the default position unless this is an editable web area, in which case we use the selection bounds.
+ if (!isWebArea() || isReadOnly())
+ return AccessibilityObject::clickPoint();
+
+ VisibleSelection visSelection = selection();
+ VisiblePositionRange range = VisiblePositionRange(visSelection.visibleStart(), visSelection.visibleEnd());
+ IntRect bounds = boundsForVisiblePositionRange(range);
+#if PLATFORM(MAC)
+ bounds.setLocation(m_renderer->document()->view()->screenToContents(bounds.location()));
+#endif
+ return IntPoint(bounds.x() + (bounds.width() / 2), bounds.y() - (bounds.height() / 2));
+}
+
+AccessibilityObject* AccessibilityRenderObject::internalLinkElement() const
+{
+ Element* element = anchorElement();
+ if (!element)
+ return 0;
+
+ // Right now, we do not support ARIA links as internal link elements
+ if (!element->hasTagName(aTag))
+ return 0;
+ HTMLAnchorElement* anchor = static_cast<HTMLAnchorElement*>(element);
+
+ KURL linkURL = anchor->href();
+ String fragmentIdentifier = linkURL.fragmentIdentifier();
+ if (fragmentIdentifier.isEmpty())
+ return 0;
+
+ // check if URL is the same as current URL
+ KURL documentURL = m_renderer->document()->url();
+ if (!equalIgnoringFragmentIdentifier(documentURL, linkURL))
+ return 0;
+
+ Node* linkedNode = m_renderer->document()->findAnchor(fragmentIdentifier);
+ if (!linkedNode)
+ return 0;
+
+ // The element we find may not be accessible, so find the first accessible object.
+ return firstAccessibleObjectFromNode(linkedNode);
+}
+
+ESpeak AccessibilityRenderObject::speakProperty() const
+{
+ if (!m_renderer)
+ return AccessibilityObject::speakProperty();
+
+ return m_renderer->style()->speak();
+}
+
+void AccessibilityRenderObject::addRadioButtonGroupMembers(AccessibilityChildrenVector& linkedUIElements) const
+{
+ if (!m_renderer || roleValue() != RadioButtonRole)
+ return;
+
+ Node* node = m_renderer->node();
+ if (!node || !node->hasTagName(inputTag))
+ return;
+
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
+ // if there's a form, then this is easy
+ if (input->form()) {
+ Vector<RefPtr<Node> > formElements;
+ input->form()->getNamedElements(input->name(), formElements);
+
+ unsigned len = formElements.size();
+ for (unsigned i = 0; i < len; ++i) {
+ Node* associateElement = formElements[i].get();
+ if (AccessibilityObject* object = axObjectCache()->getOrCreate(associateElement->renderer()))
+ linkedUIElements.append(object);
+ }
+ } else {
+ RefPtr<NodeList> list = node->document()->getElementsByTagName("input");
+ unsigned len = list->length();
+ for (unsigned i = 0; i < len; ++i) {
+ if (list->item(i)->hasTagName(inputTag)) {
+ HTMLInputElement* associateElement = static_cast<HTMLInputElement*>(list->item(i));
+ if (associateElement->isRadioButton() && associateElement->name() == input->name()) {
+ if (AccessibilityObject* object = axObjectCache()->getOrCreate(associateElement->renderer()))
+ linkedUIElements.append(object);
+ }
+ }
+ }
+ }
+}
+
+// linked ui elements could be all the related radio buttons in a group
+// or an internal anchor connection
+void AccessibilityRenderObject::linkedUIElements(AccessibilityChildrenVector& linkedUIElements) const
+{
+ ariaFlowToElements(linkedUIElements);
+
+ if (isAnchor()) {
+ AccessibilityObject* linkedAXElement = internalLinkElement();
+ if (linkedAXElement)
+ linkedUIElements.append(linkedAXElement);
+ }
+
+ if (roleValue() == RadioButtonRole)
+ addRadioButtonGroupMembers(linkedUIElements);
+}
+
+bool AccessibilityRenderObject::hasTextAlternative() const
+{
+ // ARIA: section 2A, bullet #3 says if aria-labeledby or aria-label appears, it should
+ // override the "label" element association.
+ if (!ariaLabeledByAttribute().isEmpty() || !getAttribute(aria_labelAttr).isEmpty())
+ return true;
+
+ return false;
+}
+
+bool AccessibilityRenderObject::ariaHasPopup() const
+{
+ return elementAttributeValue(aria_haspopupAttr);
+}
+
+bool AccessibilityRenderObject::supportsARIAFlowTo() const
+{
+ return !getAttribute(aria_flowtoAttr).isEmpty();
+}
+
+void AccessibilityRenderObject::ariaFlowToElements(AccessibilityChildrenVector& flowTo) const
+{
+ Vector<Element*> elements;
+ elementsFromAttribute(elements, aria_flowtoAttr);
+
+ AXObjectCache* cache = axObjectCache();
+ unsigned count = elements.size();
+ for (unsigned k = 0; k < count; ++k) {
+ Element* element = elements[k];
+ AccessibilityObject* flowToElement = cache->getOrCreate(element->renderer());
+ if (flowToElement)
+ flowTo.append(flowToElement);
+ }
+
+}
+
+bool AccessibilityRenderObject::supportsARIADropping() const
+{
+ const AtomicString& dropEffect = getAttribute(aria_dropeffectAttr);
+ return !dropEffect.isEmpty();
+}
+
+bool AccessibilityRenderObject::supportsARIADragging() const
+{
+ const AtomicString& grabbed = getAttribute(aria_grabbedAttr);
+ return equalIgnoringCase(grabbed, "true") || equalIgnoringCase(grabbed, "false");
+}
+
+bool AccessibilityRenderObject::isARIAGrabbed()
+{
+ return elementAttributeValue(aria_grabbedAttr);
+}
+
+void AccessibilityRenderObject::setARIAGrabbed(bool grabbed)
+{
+ setElementAttributeValue(aria_grabbedAttr, grabbed);
+}
+
+void AccessibilityRenderObject::determineARIADropEffects(Vector<String>& effects)
+{
+ const AtomicString& dropEffects = getAttribute(aria_dropeffectAttr);
+ if (dropEffects.isEmpty()) {
+ effects.clear();
+ return;
+ }
+
+ String dropEffectsString = dropEffects.string();
+ dropEffectsString.replace('\n', ' ');
+ dropEffectsString.split(' ', effects);
+}
+
+bool AccessibilityRenderObject::exposesTitleUIElement() const
+{
+ if (!isControl())
+ return false;
+
+ // checkbox or radio buttons don't expose the title ui element unless it has a title already
+ if (isCheckboxOrRadio() && getAttribute(titleAttr).isEmpty())
+ return false;
+
+ if (hasTextAlternative())
+ return false;
+
+ return true;
+}
+
+AccessibilityObject* AccessibilityRenderObject::titleUIElement() const
+{
+ if (!m_renderer)
+ return 0;
+
+ // if isFieldset is true, the renderer is guaranteed to be a RenderFieldset
+ if (isFieldset())
+ return axObjectCache()->getOrCreate(toRenderFieldset(m_renderer)->findLegend());
+
+ if (!exposesTitleUIElement())
+ return 0;
+
+ Node* element = m_renderer->node();
+ HTMLLabelElement* label = labelForElement(static_cast<Element*>(element));
+ if (label && label->renderer())
+ return axObjectCache()->getOrCreate(label->renderer());
+
+ return 0;
+}
+
+bool AccessibilityRenderObject::ariaIsHidden() const
+{
+ if (equalIgnoringCase(getAttribute(aria_hiddenAttr), "true"))
+ return true;
+
+ // aria-hidden hides this object and any children
+ AccessibilityObject* object = parentObject();
+ while (object) {
+ if (object->isAccessibilityRenderObject() && equalIgnoringCase(static_cast<AccessibilityRenderObject*>(object)->getAttribute(aria_hiddenAttr), "true"))
+ return true;
+ object = object->parentObject();
+ }
+
+ return false;
+}
+
+bool AccessibilityRenderObject::isDescendantOfBarrenParent() const
+{
+ for (AccessibilityObject* object = parentObject(); object; object = object->parentObject()) {
+ if (!object->canHaveChildren())
+ return true;
+ }
+
+ return false;
+}
+
+bool AccessibilityRenderObject::isAllowedChildOfTree() const
+{
+ // Determine if this is in a tree. If so, we apply special behavior to make it work like an AXOutline.
+ AccessibilityObject* axObj = parentObject();
+ bool isInTree = false;
+ while (axObj) {
+ if (axObj->isTree()) {
+ isInTree = true;
+ break;
+ }
+ axObj = axObj->parentObject();
+ }
+
+ // If the object is in a tree, only tree items should be exposed (and the children of tree items).
+ if (isInTree) {
+ AccessibilityRole role = roleValue();
+ if (role != TreeItemRole && role != StaticTextRole)
+ return false;
+ }
+ return true;
+}
+
+AccessibilityObjectInclusion AccessibilityRenderObject::accessibilityIsIgnoredBase() const
+{
+ // The following cases can apply to any element that's a subclass of AccessibilityRenderObject.
+
+ // Ignore invisible elements.
+ if (!m_renderer || m_renderer->style()->visibility() != VISIBLE)
+ return IgnoreObject;
+
+ // Anything marked as aria-hidden or a child of something aria-hidden must be hidden.
+ if (ariaIsHidden())
+ return IgnoreObject;
+
+ // Anything that is a presentational role must be hidden.
+ if (isPresentationalChildOfAriaRole())
+ return IgnoreObject;
+
+ // Allow the platform to make a decision.
+ AccessibilityObjectInclusion decision = accessibilityPlatformIncludesObject();
+ if (decision == IncludeObject)
+ return IncludeObject;
+ if (decision == IgnoreObject)
+ return IgnoreObject;
+
+ return DefaultBehavior;
+}
+
+bool AccessibilityRenderObject::accessibilityIsIgnored() const
+{
+ // Check first if any of the common reasons cause this element to be ignored.
+ // Then process other use cases that need to be applied to all the various roles
+ // that AccessibilityRenderObjects take on.
+ AccessibilityObjectInclusion decision = accessibilityIsIgnoredBase();
+ if (decision == IncludeObject)
+ return false;
+ if (decision == IgnoreObject)
+ return true;
+
+ // If this element is within a parent that cannot have children, it should not be exposed.
+ if (isDescendantOfBarrenParent())
+ return true;
+
+ if (roleValue() == IgnoredRole)
+ return true;
+
+ if (roleValue() == PresentationalRole || inheritsPresentationalRole())
+ return true;
+
+ // An ARIA tree can only have tree items and static text as children.
+ if (!isAllowedChildOfTree())
+ return true;
+
+ // ignore popup menu items because AppKit does
+ for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) {
+ if (parent->isBoxModelObject() && toRenderBoxModelObject(parent)->isMenuList())
+ return true;
+ }
+
+ // find out if this element is inside of a label element.
+ // if so, it may be ignored because it's the label for a checkbox or radio button
+ AccessibilityObject* controlObject = correspondingControlForLabelElement();
+ if (controlObject && !controlObject->exposesTitleUIElement() && controlObject->isCheckboxOrRadio())
+ return true;
+
+ // 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)
+ return true;
+ RenderText* renderText = toRenderText(m_renderer);
+ if (m_renderer->isBR() || !renderText->firstTextBox())
+ return true;
+
+ // static text beneath TextControls is reported along with the text control text so it's ignored.
+ for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
+ if (parent->roleValue() == TextFieldRole)
+ return true;
+ }
+
+ // text elements that are just empty whitespace should not be returned
+ return renderText->text()->containsOnlyWhitespace();
+ }
+
+ if (isHeading())
+ return false;
+
+ if (isLink())
+ return false;
+
+ // all controls are accessible
+ if (isControl())
+ return false;
+
+ if (ariaRoleAttribute() != UnknownRole)
+ return false;
+
+ if (!helpText().isEmpty())
+ return false;
+
+ // don't ignore labels, because they serve as TitleUIElements
+ Node* node = m_renderer->node();
+ if (node && node->hasTagName(labelTag))
+ return false;
+
+ // Anything that is content editable should not be ignored.
+ // However, one cannot just call node->isContentEditable() since that will ask if its parents
+ // are also editable. Only the top level content editable region should be exposed.
+ if (node && node->isElementNode()) {
+ Element* element = static_cast<Element*>(node);
+ const AtomicString& contentEditable = element->getAttribute(contenteditableAttr);
+ if (equalIgnoringCase(contentEditable, "true"))
+ return false;
+ }
+
+ // if this element has aria attributes on it, it should not be ignored.
+ if (supportsARIAAttributes())
+ return false;
+
+ if (m_renderer->isBlockFlow() && m_renderer->childrenInline())
+ return !toRenderBlock(m_renderer)->firstLineBox() && !mouseButtonListener();
+
+ // ignore images seemingly used as spacers
+ if (isImage()) {
+ if (node && node->isElementNode()) {
+ Element* elt = static_cast<Element*>(node);
+ const AtomicString& alt = elt->getAttribute(altAttr);
+ // don't ignore an image that has an alt tag
+ if (!alt.isEmpty())
+ return false;
+ // informal standard is to ignore images with zero-length alt strings
+ if (!alt.isNull())
+ return true;
+ }
+
+ if (node && node->hasTagName(canvasTag)) {
+ RenderHTMLCanvas* canvas = toRenderHTMLCanvas(m_renderer);
+ if (canvas->height() <= 1 || canvas->width() <= 1)
+ return true;
+ return false;
+ }
+
+ if (isNativeImage()) {
+ // check for one-dimensional image
+ RenderImage* image = toRenderImage(m_renderer);
+ if (image->height() <= 1 || image->width() <= 1)
+ return true;
+
+ // check whether rendered image was stretched from one-dimensional file image
+ if (image->cachedImage()) {
+ IntSize imageSize = image->cachedImage()->imageSize(image->view()->zoomFactor());
+ return imageSize.height() <= 1 || imageSize.width() <= 1;
+ }
+ }
+ return false;
+ }
+
+ // make a platform-specific decision
+ if (isAttachment())
+ return accessibilityIgnoreAttachment();
+
+ return !m_renderer->isListMarker() && !isWebArea();
+}
+
+bool AccessibilityRenderObject::isLoaded() const
+{
+ return !m_renderer->document()->parser();
+}
+
+double AccessibilityRenderObject::estimatedLoadingProgress() const
+{
+ if (!m_renderer)
+ return 0;
+
+ if (isLoaded())
+ return 1.0;
+
+ Page* page = m_renderer->document()->page();
+ if (!page)
+ return 0;
+
+ return page->progress()->estimatedProgress();
+}
+
+int AccessibilityRenderObject::layoutCount() const
+{
+ if (!m_renderer->isRenderView())
+ return 0;
+ return toRenderView(m_renderer)->frameView()->layoutCount();
+}
+
+String AccessibilityRenderObject::text() const
+{
+ // If this is a user defined static text, use the accessible name computation.
+ if (ariaRoleAttribute() == StaticTextRole)
+ return ariaAccessibilityDescription();
+
+ if (!isTextControl() || isPasswordField())
+ return String();
+
+ if (isNativeTextControl())
+ return toRenderTextControl(m_renderer)->text();
+
+ Node* node = m_renderer->node();
+ if (!node)
+ return String();
+ if (!node->isElementNode())
+ return String();
+
+ return static_cast<Element*>(node)->innerText();
+}
+
+int AccessibilityRenderObject::textLength() const
+{
+ ASSERT(isTextControl());
+
+ if (isPasswordField())
+ return -1; // need to return something distinct from 0
+
+ return text().length();
+}
+
+PlainTextRange AccessibilityRenderObject::ariaSelectedTextRange() const
+{
+ Node* node = m_renderer->node();
+ if (!node)
+ return PlainTextRange();
+
+ ExceptionCode ec = 0;
+ VisibleSelection visibleSelection = selection();
+ RefPtr<Range> currentSelectionRange = visibleSelection.toNormalizedRange();
+ if (!currentSelectionRange || !currentSelectionRange->intersectsNode(node, ec))
+ return PlainTextRange();
+
+ int start = indexForVisiblePosition(visibleSelection.start());
+ int end = indexForVisiblePosition(visibleSelection.end());
+
+ return PlainTextRange(start, end - start);
+}
+
+String AccessibilityRenderObject::selectedText() const
+{
+ ASSERT(isTextControl());
+
+ if (isPasswordField())
+ return String(); // need to return something distinct from empty string
+
+ if (isNativeTextControl()) {
+ RenderTextControl* textControl = toRenderTextControl(m_renderer);
+ return textControl->text().substring(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart());
+ }
+
+ if (ariaRoleAttribute() == UnknownRole)
+ return String();
+
+ return doAXStringForRange(ariaSelectedTextRange());
+}
+
+const AtomicString& AccessibilityRenderObject::accessKey() const
+{
+ Node* node = m_renderer->node();
+ if (!node)
+ return nullAtom;
+ if (!node->isElementNode())
+ return nullAtom;
+ return static_cast<Element*>(node)->getAttribute(accesskeyAttr);
+}
+
+VisibleSelection AccessibilityRenderObject::selection() const
+{
+ return m_renderer->frame()->selection()->selection();
+}
+
+PlainTextRange AccessibilityRenderObject::selectedTextRange() const
+{
+ ASSERT(isTextControl());
+
+ if (isPasswordField())
+ return PlainTextRange();
+
+ AccessibilityRole ariaRole = ariaRoleAttribute();
+ if (isNativeTextControl() && ariaRole == UnknownRole) {
+ RenderTextControl* textControl = toRenderTextControl(m_renderer);
+ return PlainTextRange(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart());
+ }
+
+ if (ariaRole == UnknownRole)
+ return PlainTextRange();
+
+ return ariaSelectedTextRange();
+}
+
+void AccessibilityRenderObject::setSelectedTextRange(const PlainTextRange& range)
+{
+ if (isNativeTextControl()) {
+ setSelectionRange(m_renderer->node(), range.start, range.start + range.length);
+ return;
+ }
+
+ Document* document = m_renderer->document();
+ if (!document)
+ return;
+ Frame* frame = document->frame();
+ if (!frame)
+ return;
+ Node* node = m_renderer->node();
+ frame->selection()->setSelection(VisibleSelection(Position(node, range.start),
+ Position(node, range.start + range.length), DOWNSTREAM));
+}
+
+KURL AccessibilityRenderObject::url() const
+{
+ if (isAnchor() && m_renderer->node()->hasTagName(aTag)) {
+ if (HTMLAnchorElement* anchor = static_cast<HTMLAnchorElement*>(anchorElement()))
+ return anchor->href();
+ }
+
+ if (isWebArea())
+ return m_renderer->document()->url();
+
+ if (isImage() && m_renderer->node() && m_renderer->node()->hasTagName(imgTag))
+ return static_cast<HTMLImageElement*>(m_renderer->node())->src();
+
+ if (isInputImage())
+ return static_cast<HTMLInputElement*>(m_renderer->node())->src();
+
+ return KURL();
+}
+
+bool AccessibilityRenderObject::isVisited() const
+{
+ // FIXME: Is it a privacy violation to expose visited information to accessibility APIs?
+ return m_renderer->style()->isLink() && m_renderer->style()->insideLink() == InsideVisitedLink;
+}
+
+void AccessibilityRenderObject::setElementAttributeValue(const QualifiedName& attributeName, bool value)
+{
+ if (!m_renderer)
+ return;
+
+ Node* node = m_renderer->node();
+ if (!node || !node->isElementNode())
+ return;
+
+ Element* element = static_cast<Element*>(node);
+ element->setAttribute(attributeName, (value) ? "true" : "false");
+}
+
+bool AccessibilityRenderObject::elementAttributeValue(const QualifiedName& attributeName) const
+{
+ if (!m_renderer)
+ return false;
+
+ return equalIgnoringCase(getAttribute(attributeName), "true");
+}
+
+void AccessibilityRenderObject::setIsExpanded(bool isExpanded)
+{
+ // Combo boxes, tree items and rows can be expanded (in different ways on different platforms).
+ // That action translates into setting the aria-expanded attribute to true.
+ AccessibilityRole role = roleValue();
+ switch (role) {
+ case ComboBoxRole:
+ case TreeItemRole:
+ case RowRole:
+ setElementAttributeValue(aria_expandedAttr, isExpanded);
+ break;
+ default:
+ break;
+ }
+}
+
+bool AccessibilityRenderObject::isRequired() const
+{
+ if (equalIgnoringCase(getAttribute(aria_requiredAttr), "true"))
+ return true;
+
+ Node* n = node();
+ if (n && (n->isElementNode() && static_cast<Element*>(n)->isFormControlElement()))
+ return static_cast<HTMLFormControlElement*>(n)->required();
+
+ return false;
+}
+
+bool AccessibilityRenderObject::isSelected() const
+{
+ if (!m_renderer)
+ return false;
+
+ Node* node = m_renderer->node();
+ if (!node)
+ return false;
+
+ const AtomicString& ariaSelected = getAttribute(aria_selectedAttr);
+ if (equalIgnoringCase(ariaSelected, "true"))
+ return true;
+
+ if (isTabItem() && isTabItemSelected())
+ return true;
+
+ return false;
+}
+
+bool AccessibilityRenderObject::isTabItemSelected() const
+{
+ if (!isTabItem() || !m_renderer)
+ return false;
+
+ Node* node = m_renderer->node();
+ if (!node || !node->isElementNode())
+ return false;
+
+ // The ARIA spec says a tab item can also be selected if it is aria-labeled by a tabpanel
+ // that has keyboard focus inside of it, or if a tabpanel in its aria-controls list has KB
+ // focus inside of it.
+ AccessibilityObject* focusedElement = focusedUIElement();
+ if (!focusedElement)
+ return false;
+
+ Vector<Element*> elements;
+ elementsFromAttribute(elements, aria_controlsAttr);
+
+ unsigned count = elements.size();
+ for (unsigned k = 0; k < count; ++k) {
+ Element* element = elements[k];
+ AccessibilityObject* tabPanel = axObjectCache()->getOrCreate(element->renderer());
+
+ // A tab item should only control tab panels.
+ if (!tabPanel || tabPanel->roleValue() != TabPanelRole)
+ continue;
+
+ AccessibilityObject* checkFocusElement = focusedElement;
+ // Check if the focused element is a descendant of the element controlled by the tab item.
+ while (checkFocusElement) {
+ if (tabPanel == checkFocusElement)
+ return true;
+ checkFocusElement = checkFocusElement->parentObject();
+ }
+ }
+
+ return false;
+}
+
+bool AccessibilityRenderObject::isFocused() const
+{
+ if (!m_renderer)
+ return false;
+
+ Document* document = m_renderer->document();
+ if (!document)
+ return false;
+
+ Node* focusedNode = document->focusedNode();
+ if (!focusedNode)
+ return false;
+
+ // A web area is represented by the Document node in the DOM tree, which isn't focusable.
+ // Check instead if the frame's selection controller is focused
+ if (focusedNode == m_renderer->node()
+ || (roleValue() == WebAreaRole && document->frame()->selection()->isFocusedAndActive()))
+ return true;
+
+ return false;
+}
+
+void AccessibilityRenderObject::setFocused(bool on)
+{
+ if (!canSetFocusAttribute())
+ return;
+
+ if (!on)
+ m_renderer->document()->setFocusedNode(0);
+ else {
+ if (m_renderer->node()->isElementNode())
+ static_cast<Element*>(m_renderer->node())->focus();
+ else
+ m_renderer->document()->setFocusedNode(m_renderer->node());
+ }
+}
+
+void AccessibilityRenderObject::changeValueByPercent(float percentChange)
+{
+ float range = maxValueForRange() - minValueForRange();
+ float value = valueForRange();
+
+ value += range * (percentChange / 100);
+ setValue(String::number(value));
+
+ axObjectCache()->postNotification(m_renderer, AXObjectCache::AXValueChanged, true);
+}
+
+void AccessibilityRenderObject::setSelected(bool enabled)
+{
+ setElementAttributeValue(aria_selectedAttr, enabled);
+}
+
+void AccessibilityRenderObject::setSelectedRows(AccessibilityChildrenVector& selectedRows)
+{
+ // Setting selected only makes sense in trees and tables (and tree-tables).
+ AccessibilityRole role = roleValue();
+ if (role != TreeRole && role != TreeGridRole && role != TableRole)
+ return;
+
+ bool isMulti = isMultiSelectable();
+ unsigned count = selectedRows.size();
+ if (count > 1 && !isMulti)
+ count = 1;
+
+ for (unsigned k = 0; k < count; ++k)
+ selectedRows[k]->setSelected(true);
+}
+
+void AccessibilityRenderObject::setValue(const String& string)
+{
+ if (!m_renderer || !m_renderer->node() || !m_renderer->node()->isElementNode())
+ return;
+ Element* element = static_cast<Element*>(m_renderer->node());
+
+ if (roleValue() == SliderRole)
+ element->setAttribute(aria_valuenowAttr, string);
+
+ if (!m_renderer->isBoxModelObject())
+ return;
+ RenderBoxModelObject* renderer = toRenderBoxModelObject(m_renderer);
+
+ // FIXME: Do we want to do anything here for ARIA textboxes?
+ if (renderer->isTextField()) {
+ // FIXME: This is not safe! Other elements could have a TextField renderer.
+ static_cast<HTMLInputElement*>(element)->setValue(string);
+ } else if (renderer->isTextArea()) {
+ // FIXME: This is not safe! Other elements could have a TextArea renderer.
+ static_cast<HTMLTextAreaElement*>(element)->setValue(string);
+ }
+}
+
+void AccessibilityRenderObject::ariaOwnsElements(AccessibilityChildrenVector& axObjects) const
+{
+ Vector<Element*> elements;
+ elementsFromAttribute(elements, aria_ownsAttr);
+
+ unsigned count = elements.size();
+ for (unsigned k = 0; k < count; ++k) {
+ RenderObject* render = elements[k]->renderer();
+ AccessibilityObject* obj = axObjectCache()->getOrCreate(render);
+ if (obj)
+ axObjects.append(obj);
+ }
+}
+
+bool AccessibilityRenderObject::supportsARIAOwns() const
+{
+ if (!m_renderer)
+ return false;
+ const AtomicString& ariaOwns = getAttribute(aria_ownsAttr);
+
+ return !ariaOwns.isEmpty();
+}
+
+bool AccessibilityRenderObject::isEnabled() const
+{
+ ASSERT(m_renderer);
+
+ if (equalIgnoringCase(getAttribute(aria_disabledAttr), "true"))
+ return false;
+
+ Node* node = m_renderer->node();
+ if (!node || !node->isElementNode())
+ return true;
+
+ return static_cast<Element*>(node)->isEnabledFormControl();
+}
+
+RenderView* AccessibilityRenderObject::topRenderer() const
+{
+ return m_renderer->document()->topDocument()->renderView();
+}
+
+Document* AccessibilityRenderObject::document() const
+{
+ if (!m_renderer)
+ return 0;
+ return m_renderer->document();
+}
+
+FrameView* AccessibilityRenderObject::topDocumentFrameView() const
+{
+ return topRenderer()->view()->frameView();
+}
+
+Widget* AccessibilityRenderObject::widget() const
+{
+ if (!m_renderer->isBoxModelObject() || !toRenderBoxModelObject(m_renderer)->isWidget())
+ return 0;
+ return toRenderWidget(m_renderer)->widget();
+}
+
+AccessibilityObject* AccessibilityRenderObject::accessibilityParentForImageMap(HTMLMapElement* map) const
+{
+ // find an image that is using this map
+ if (!map)
+ return 0;
+
+ HTMLImageElement* imageElement = map->imageElement();
+ if (!imageElement)
+ return 0;
+
+ return axObjectCache()->getOrCreate(imageElement->renderer());
+}
+
+void AccessibilityRenderObject::getDocumentLinks(AccessibilityChildrenVector& result)
+{
+ Document* document = m_renderer->document();
+ RefPtr<HTMLCollection> coll = document->links();
+ Node* curr = coll->firstItem();
+ while (curr) {
+ RenderObject* obj = curr->renderer();
+ if (obj) {
+ RefPtr<AccessibilityObject> axobj = document->axObjectCache()->getOrCreate(obj);
+ ASSERT(axobj);
+ if (!axobj->accessibilityIsIgnored() && axobj->isLink())
+ result.append(axobj);
+ } else {
+ Node* parent = curr->parentNode();
+ if (parent && curr->hasTagName(areaTag) && parent->hasTagName(mapTag)) {
+ AccessibilityImageMapLink* areaObject = static_cast<AccessibilityImageMapLink*>(axObjectCache()->getOrCreate(ImageMapLinkRole));
+ areaObject->setHTMLAreaElement(static_cast<HTMLAreaElement*>(curr));
+ areaObject->setHTMLMapElement(static_cast<HTMLMapElement*>(parent));
+ areaObject->setParent(accessibilityParentForImageMap(static_cast<HTMLMapElement*>(parent)));
+
+ result.append(areaObject);
+ }
+ }
+ curr = coll->nextItem();
+ }
+}
+
+FrameView* AccessibilityRenderObject::documentFrameView() const
+{
+ if (!m_renderer || !m_renderer->document())
+ return 0;
+
+ // this is the RenderObject's Document's Frame's FrameView
+ return m_renderer->document()->view();
+}
+
+Widget* AccessibilityRenderObject::widgetForAttachmentView() const
+{
+ if (!isAttachment())
+ return 0;
+ return toRenderWidget(m_renderer)->widget();
+}
+
+FrameView* AccessibilityRenderObject::frameViewIfRenderView() const
+{
+ if (!m_renderer->isRenderView())
+ return 0;
+ // this is the RenderObject's Document's renderer's FrameView
+ return m_renderer->view()->frameView();
+}
+
+// This function is like a cross-platform version of - (WebCoreTextMarkerRange*)textMarkerRange. It returns
+// a Range that we can convert to a WebCoreTextMarkerRange in the Obj-C file
+VisiblePositionRange AccessibilityRenderObject::visiblePositionRange() const
+{
+ if (!m_renderer)
+ return VisiblePositionRange();
+
+ // construct VisiblePositions for start and end
+ Node* node = m_renderer->node();
+ if (!node)
+ return VisiblePositionRange();
+
+ VisiblePosition startPos = firstDeepEditingPositionForNode(node);
+ VisiblePosition endPos = lastDeepEditingPositionForNode(node);
+
+ // the VisiblePositions are equal for nodes like buttons, so adjust for that
+ // FIXME: Really? [button, 0] and [button, 1] are distinct (before and after the button)
+ // I expect this code is only hit for things like empty divs? In which case I don't think
+ // the behavior is correct here -- eseidel
+ if (startPos == endPos) {
+ endPos = endPos.next();
+ if (endPos.isNull())
+ endPos = startPos;
+ }
+
+ return VisiblePositionRange(startPos, endPos);
+}
+
+VisiblePositionRange AccessibilityRenderObject::visiblePositionRangeForLine(unsigned lineCount) const
+{
+ if (!lineCount || !m_renderer)
+ return VisiblePositionRange();
+
+ // iterate over the lines
+ // FIXME: this is wrong when lineNumber is lineCount+1, because nextLinePosition takes you to the
+ // last offset of the last line
+ VisiblePosition visiblePos = m_renderer->document()->renderer()->positionForCoordinates(0, 0);
+ VisiblePosition savedVisiblePos;
+ while (--lineCount) {
+ savedVisiblePos = visiblePos;
+ visiblePos = nextLinePosition(visiblePos, 0);
+ if (visiblePos.isNull() || visiblePos == savedVisiblePos)
+ return VisiblePositionRange();
+ }
+
+ // make a caret selection for the marker position, then extend it to the line
+ // NOTE: ignores results of sel.modify because it returns false when
+ // starting at an empty line. The resulting selection in that case
+ // will be a caret at visiblePos.
+ SelectionController selection;
+ selection.setSelection(VisibleSelection(visiblePos));
+ selection.modify(SelectionController::AlterationExtend, DirectionRight, LineBoundary);
+
+ return VisiblePositionRange(selection.selection().visibleStart(), selection.selection().visibleEnd());
+}
+
+VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(int index) const
+{
+ if (!m_renderer)
+ return VisiblePosition();
+
+ if (isNativeTextControl())
+ return toRenderTextControl(m_renderer)->visiblePositionForIndex(index);
+
+ if (!allowsTextRanges() && !m_renderer->isText())
+ return VisiblePosition();
+
+ Node* node = m_renderer->node();
+ if (!node)
+ return VisiblePosition();
+
+ if (index <= 0)
+ return VisiblePosition(node, 0, DOWNSTREAM);
+
+ ExceptionCode ec = 0;
+ RefPtr<Range> range = Range::create(m_renderer->document());
+ range->selectNodeContents(node, ec);
+ CharacterIterator it(range.get());
+ it.advance(index - 1);
+ return VisiblePosition(it.range()->endContainer(ec), it.range()->endOffset(ec), UPSTREAM);
+}
+
+int AccessibilityRenderObject::indexForVisiblePosition(const VisiblePosition& pos) const
+{
+ if (isNativeTextControl())
+ return toRenderTextControl(m_renderer)->indexForVisiblePosition(pos);
+
+ if (!isTextControl())
+ return 0;
+
+ Node* node = m_renderer->node();
+ if (!node)
+ return 0;
+
+ Position indexPosition = pos.deepEquivalent();
+ if (!indexPosition.node() || indexPosition.node()->rootEditableElement() != node)
+ return 0;
+
+ ExceptionCode ec = 0;
+ RefPtr<Range> range = Range::create(m_renderer->document());
+ range->setStart(node, 0, ec);
+ range->setEnd(indexPosition.node(), indexPosition.deprecatedEditingOffset(), ec);
+ return TextIterator::rangeLength(range.get());
+}
+
+IntRect AccessibilityRenderObject::boundsForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
+{
+ if (visiblePositionRange.isNull())
+ return IntRect();
+
+ // Create a mutable VisiblePositionRange.
+ VisiblePositionRange range(visiblePositionRange);
+ IntRect rect1 = range.start.absoluteCaretBounds();
+ IntRect rect2 = range.end.absoluteCaretBounds();
+
+ // readjust for position at the edge of a line. This is to exclude line rect that doesn't need to be accounted in the range bounds
+ if (rect2.y() != rect1.y()) {
+ VisiblePosition endOfFirstLine = endOfLine(range.start);
+ if (range.start == endOfFirstLine) {
+ range.start.setAffinity(DOWNSTREAM);
+ rect1 = range.start.absoluteCaretBounds();
+ }
+ if (range.end == endOfFirstLine) {
+ range.end.setAffinity(UPSTREAM);
+ rect2 = range.end.absoluteCaretBounds();
+ }
+ }
+
+ IntRect ourrect = rect1;
+ ourrect.unite(rect2);
+
+ // if the rectangle spans lines and contains multiple text chars, use the range's bounding box intead
+ if (rect1.bottom() != rect2.bottom()) {
+ RefPtr<Range> dataRange = makeRange(range.start, range.end);
+ IntRect boundingBox = dataRange->boundingBox();
+ String rangeString = plainText(dataRange.get());
+ if (rangeString.length() > 1 && !boundingBox.isEmpty())
+ ourrect = boundingBox;
+ }
+
+#if PLATFORM(MAC)
+ return m_renderer->document()->view()->contentsToScreen(ourrect);
+#else
+ return ourrect;
+#endif
+}
+
+void AccessibilityRenderObject::setSelectedVisiblePositionRange(const VisiblePositionRange& range) const
+{
+ if (range.start.isNull() || range.end.isNull())
+ return;
+
+ // make selection and tell the document to use it. if it's zero length, then move to that position
+ if (range.start == range.end)
+ m_renderer->frame()->selection()->moveTo(range.start, true);
+ else {
+ VisibleSelection newSelection = VisibleSelection(range.start, range.end);
+ m_renderer->frame()->selection()->setSelection(newSelection);
+ }
+}
+
+VisiblePosition AccessibilityRenderObject::visiblePositionForPoint(const IntPoint& point) const
+{
+ if (!m_renderer)
+ return VisiblePosition();
+
+ // convert absolute point to view coordinates
+ FrameView* frameView = m_renderer->document()->topDocument()->renderer()->view()->frameView();
+ RenderView* renderView = topRenderer();
+ Node* innerNode = 0;
+
+ // locate the node containing the point
+ IntPoint pointResult;
+ while (1) {
+ IntPoint ourpoint;
+#if PLATFORM(MAC)
+ ourpoint = frameView->screenToContents(point);
+#else
+ ourpoint = point;
+#endif
+ HitTestRequest request(HitTestRequest::ReadOnly |
+ HitTestRequest::Active);
+ HitTestResult result(ourpoint);
+ renderView->layer()->hitTest(request, result);
+ innerNode = result.innerNode();
+ if (!innerNode)
+ return VisiblePosition();
+
+ RenderObject* renderer = innerNode->renderer();
+ if (!renderer)
+ return VisiblePosition();
+
+ pointResult = result.localPoint();
+
+ // done if hit something other than a widget
+ if (!renderer->isWidget())
+ break;
+
+ // descend into widget (FRAME, IFRAME, OBJECT...)
+ Widget* widget = toRenderWidget(renderer)->widget();
+ if (!widget || !widget->isFrameView())
+ break;
+ Frame* frame = static_cast<FrameView*>(widget)->frame();
+ if (!frame)
+ break;
+ renderView = frame->document()->renderView();
+ frameView = static_cast<FrameView*>(widget);
+ }
+
+ return innerNode->renderer()->positionForPoint(pointResult);
+}
+
+// NOTE: Consider providing this utility method as AX API
+VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(unsigned indexValue, bool lastIndexOK) const
+{
+ if (!isTextControl())
+ return VisiblePosition();
+
+ // lastIndexOK specifies whether the position after the last character is acceptable
+ if (indexValue >= text().length()) {
+ if (!lastIndexOK || indexValue > text().length())
+ return VisiblePosition();
+ }
+ VisiblePosition position = visiblePositionForIndex(indexValue);
+ position.setAffinity(DOWNSTREAM);
+ return position;
+}
+
+// NOTE: Consider providing this utility method as AX API
+int AccessibilityRenderObject::index(const VisiblePosition& position) const
+{
+ if (!isTextControl())
+ return -1;
+
+ Node* node = position.deepEquivalent().node();
+ if (!node)
+ return -1;
+
+ for (RenderObject* renderer = node->renderer(); renderer && renderer->node(); renderer = renderer->parent()) {
+ if (renderer == m_renderer)
+ return indexForVisiblePosition(position);
+ }
+
+ return -1;
+}
+
+// Given a line number, the range of characters of the text associated with this accessibility
+// object that contains the line number.
+PlainTextRange AccessibilityRenderObject::doAXRangeForLine(unsigned lineNumber) const
+{
+ if (!isTextControl())
+ return PlainTextRange();
+
+ // iterate to the specified line
+ VisiblePosition visiblePos = visiblePositionForIndex(0);
+ VisiblePosition savedVisiblePos;
+ for (unsigned lineCount = lineNumber; lineCount; lineCount -= 1) {
+ savedVisiblePos = visiblePos;
+ visiblePos = nextLinePosition(visiblePos, 0);
+ if (visiblePos.isNull() || visiblePos == savedVisiblePos)
+ return PlainTextRange();
+ }
+
+ // Get the end of the line based on the starting position.
+ VisiblePosition endPosition = endOfLine(visiblePos);
+
+ int index1 = indexForVisiblePosition(visiblePos);
+ int index2 = indexForVisiblePosition(endPosition);
+
+ // add one to the end index for a line break not caused by soft line wrap (to match AppKit)
+ if (endPosition.affinity() == DOWNSTREAM && endPosition.next().isNotNull())
+ index2 += 1;
+
+ // return nil rather than an zero-length range (to match AppKit)
+ if (index1 == index2)
+ return PlainTextRange();
+
+ return PlainTextRange(index1, index2 - index1);
+}
+
+// The composed character range in the text associated with this accessibility object that
+// is specified by the given index value. This parameterized attribute returns the complete
+// range of characters (including surrogate pairs of multi-byte glyphs) at the given index.
+PlainTextRange AccessibilityRenderObject::doAXRangeForIndex(unsigned index) const
+{
+ if (!isTextControl())
+ return PlainTextRange();
+
+ String elementText = text();
+ if (!elementText.length() || index > elementText.length() - 1)
+ return PlainTextRange();
+
+ return PlainTextRange(index, 1);
+}
+
+// A substring of the text associated with this accessibility object that is
+// specified by the given character range.
+String AccessibilityRenderObject::doAXStringForRange(const PlainTextRange& range) const
+{
+ if (isPasswordField())
+ return String();
+
+ if (!range.length)
+ return String();
+
+ if (!isTextControl())
+ return String();
+
+ String elementText = text();
+ if (range.start + range.length > elementText.length())
+ return String();
+
+ return elementText.substring(range.start, range.length);
+}
+
+// The bounding rectangle of the text associated with this accessibility object that is
+// specified by the given range. This is the bounding rectangle a sighted user would see
+// on the display screen, in pixels.
+IntRect AccessibilityRenderObject::doAXBoundsForRange(const PlainTextRange& range) const
+{
+ if (allowsTextRanges())
+ return boundsForVisiblePositionRange(visiblePositionRangeForRange(range));
+ return IntRect();
+}
+
+AccessibilityObject* AccessibilityRenderObject::accessibilityImageMapHitTest(HTMLAreaElement* area, const IntPoint& point) const
+{
+ if (!area)
+ return 0;
+
+ HTMLMapElement* map = static_cast<HTMLMapElement*>(area->parentNode());
+ AccessibilityObject* parent = accessibilityParentForImageMap(map);
+ if (!parent)
+ return 0;
+
+ AccessibilityObject::AccessibilityChildrenVector children = parent->children();
+
+ unsigned count = children.size();
+ for (unsigned k = 0; k < count; ++k) {
+ if (children[k]->elementRect().contains(point))
+ return children[k].get();
+ }
+
+ return 0;
+}
+
+AccessibilityObject* AccessibilityRenderObject::accessibilityHitTest(const IntPoint& point) const
+{
+ if (!m_renderer || !m_renderer->hasLayer())
+ return 0;
+
+ RenderLayer* layer = toRenderBox(m_renderer)->layer();
+
+ HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active);
+ HitTestResult hitTestResult = HitTestResult(point);
+ layer->hitTest(request, hitTestResult);
+ if (!hitTestResult.innerNode())
+ return 0;
+ Node* node = hitTestResult.innerNode()->shadowAncestorNode();
+
+ if (node->hasTagName(areaTag))
+ return accessibilityImageMapHitTest(static_cast<HTMLAreaElement*>(node), point);
+
+ if (node->hasTagName(optionTag))
+ node = static_cast<HTMLOptionElement*>(node)->ownerSelectElement();
+
+ RenderObject* obj = node->renderer();
+ if (!obj)
+ return 0;
+
+ AccessibilityObject* result = obj->document()->axObjectCache()->getOrCreate(obj);
+ result->updateChildrenIfNecessary();
+
+ // Allow the element to perform any hit-testing it might need to do to reach non-render children.
+ result = result->elementAccessibilityHitTest(point);
+
+ if (result->accessibilityIsIgnored()) {
+ // If this element is the label of a control, a hit test should return the control.
+ AccessibilityObject* controlObject = result->correspondingControlForLabelElement();
+ if (controlObject && !controlObject->exposesTitleUIElement())
+ return controlObject;
+
+ result = result->parentObjectUnignored();
+ }
+
+ return result;
+}
+
+bool AccessibilityRenderObject::shouldFocusActiveDescendant() const
+{
+ switch (ariaRoleAttribute()) {
+ case GroupRole:
+ case ComboBoxRole:
+ case ListBoxRole:
+ case MenuRole:
+ case MenuBarRole:
+ case RadioGroupRole:
+ case RowRole:
+ case PopUpButtonRole:
+ case ProgressIndicatorRole:
+ case ToolbarRole:
+ case OutlineRole:
+ case TreeRole:
+ case GridRole:
+ /* FIXME: replace these with actual roles when they are added to AccessibilityRole
+ composite
+ alert
+ alertdialog
+ status
+ timer
+ */
+ return true;
+ default:
+ return false;
+ }
+}
+
+AccessibilityObject* AccessibilityRenderObject::activeDescendant() const
+{
+ if (!m_renderer)
+ return 0;
+
+ if (m_renderer->node() && !m_renderer->node()->isElementNode())
+ return 0;
+ Element* element = static_cast<Element*>(m_renderer->node());
+
+ const AtomicString& activeDescendantAttrStr = element->getAttribute(aria_activedescendantAttr);
+ if (activeDescendantAttrStr.isNull() || activeDescendantAttrStr.isEmpty())
+ return 0;
+
+ Element* target = document()->getElementById(activeDescendantAttrStr);
+ if (!target)
+ return 0;
+
+ AccessibilityObject* obj = axObjectCache()->getOrCreate(target->renderer());
+ if (obj && obj->isAccessibilityRenderObject())
+ // an activedescendant is only useful if it has a renderer, because that's what's needed to post the notification
+ return obj;
+ return 0;
+}
+
+void AccessibilityRenderObject::handleAriaExpandedChanged()
+{
+ // Find if a parent of this object should handle aria-expanded changes.
+ AccessibilityObject* containerParent = this->parentObject();
+ while (containerParent) {
+ bool foundParent = false;
+
+ switch (containerParent->roleValue()) {
+ case TreeRole:
+ case TreeGridRole:
+ case GridRole:
+ case TableRole:
+ case BrowserRole:
+ foundParent = true;
+ break;
+ default:
+ break;
+ }
+
+ if (foundParent)
+ break;
+
+ containerParent = containerParent->parentObject();
+ }
+
+ // Post that the row count changed.
+ if (containerParent)
+ axObjectCache()->postNotification(containerParent, document(), AXObjectCache::AXRowCountChanged, true);
+
+ // Post that the specific row either collapsed or expanded.
+ if (roleValue() == RowRole || roleValue() == TreeItemRole)
+ axObjectCache()->postNotification(this, document(), isExpanded() ? AXObjectCache::AXRowExpanded : AXObjectCache::AXRowCollapsed, true);
+}
+
+void AccessibilityRenderObject::handleActiveDescendantChanged()
+{
+ Element* element = static_cast<Element*>(renderer()->node());
+ if (!element)
+ return;
+ Document* doc = renderer()->document();
+ if (!doc->frame()->selection()->isFocusedAndActive() || doc->focusedNode() != element)
+ return;
+ AccessibilityRenderObject* activedescendant = static_cast<AccessibilityRenderObject*>(activeDescendant());
+
+ if (activedescendant && shouldFocusActiveDescendant())
+ doc->axObjectCache()->postNotification(m_renderer, AXObjectCache::AXActiveDescendantChanged, true);
+}
+
+AccessibilityObject* AccessibilityRenderObject::correspondingControlForLabelElement() const
+{
+ HTMLLabelElement* labelElement = labelElementContainer();
+ if (!labelElement)
+ return 0;
+
+ HTMLElement* correspondingControl = labelElement->control();
+ if (!correspondingControl)
+ return 0;
+
+ return axObjectCache()->getOrCreate(correspondingControl->renderer());
+}
+
+AccessibilityObject* AccessibilityRenderObject::correspondingLabelForControlElement() const
+{
+ if (!m_renderer)
+ return 0;
+
+ Node* node = m_renderer->node();
+ if (node && node->isHTMLElement()) {
+ HTMLLabelElement* label = labelForElement(static_cast<Element*>(node));
+ if (label)
+ return axObjectCache()->getOrCreate(label->renderer());
+ }
+
+ return 0;
+}
+
+bool AccessibilityRenderObject::renderObjectIsObservable(RenderObject* renderer) const
+{
+ // AX clients will listen for AXValueChange on a text control.
+ if (renderer->isTextControl())
+ return true;
+
+ // AX clients will listen for AXSelectedChildrenChanged on listboxes.
+ Node* node = renderer->node();
+ if (nodeHasRole(node, "listbox") || (renderer->isBoxModelObject() && toRenderBoxModelObject(renderer)->isListBox()))
+ return true;
+
+ // Textboxes should send out notifications.
+ if (nodeHasRole(node, "textbox"))
+ return true;
+
+ return false;
+}
+
+AccessibilityObject* AccessibilityRenderObject::observableObject() const
+{
+ // Find the object going up the parent chain that is used in accessibility to monitor certain notifications.
+ for (RenderObject* renderer = m_renderer; renderer && renderer->node(); renderer = renderer->parent()) {
+ if (renderObjectIsObservable(renderer))
+ return axObjectCache()->getOrCreate(renderer);
+ }
+
+ return 0;
+}
+
+AccessibilityRole AccessibilityRenderObject::determineAriaRoleAttribute() const
+{
+ const AtomicString& ariaRole = getAttribute(roleAttr);
+ if (ariaRole.isNull() || ariaRole.isEmpty())
+ return UnknownRole;
+
+ AccessibilityRole role = ariaRoleToWebCoreRole(ariaRole);
+
+ if (role == ButtonRole && ariaHasPopup())
+ role = PopUpButtonRole;
+
+ if (role == TextAreaRole && ariaIsMultiline())
+ role = TextFieldRole;
+
+ if (role)
+ return role;
+ // selects and listboxes both have options as child roles, but they map to different roles within WebCore
+ if (equalIgnoringCase(ariaRole, "option")) {
+ if (parentObjectUnignored()->ariaRoleAttribute() == MenuRole)
+ return MenuItemRole;
+ if (parentObjectUnignored()->ariaRoleAttribute() == ListBoxRole)
+ return ListBoxOptionRole;
+ }
+ // an aria "menuitem" may map to MenuButton or MenuItem depending on its parent
+ if (equalIgnoringCase(ariaRole, "menuitem")) {
+ if (parentObjectUnignored()->ariaRoleAttribute() == GroupRole)
+ return MenuButtonRole;
+ if (parentObjectUnignored()->ariaRoleAttribute() == MenuRole)
+ return MenuItemRole;
+ }
+
+ 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();
+}
+
+AccessibilityRole AccessibilityRenderObject::determineAccessibilityRole()
+{
+ if (!m_renderer)
+ return UnknownRole;
+
+ m_ariaRole = determineAriaRoleAttribute();
+
+ Node* node = m_renderer->node();
+ AccessibilityRole ariaRole = ariaRoleAttribute();
+ if (ariaRole != UnknownRole)
+ return ariaRole;
+
+ RenderBoxModelObject* cssBox = renderBoxModelObject();
+
+ if (node && node->isLink()) {
+ if (cssBox && cssBox->isImage())
+ return ImageMapRole;
+ return WebCoreLinkRole;
+ }
+ if (cssBox && cssBox->isListItem())
+ return ListItemRole;
+ if (m_renderer->isListMarker())
+ return ListMarkerRole;
+ if (node && node->hasTagName(buttonTag))
+ return ButtonRole;
+ if (m_renderer->isText())
+ return StaticTextRole;
+ if (cssBox && cssBox->isImage()) {
+ if (node && node->hasTagName(inputTag))
+ return ButtonRole;
+ return ImageRole;
+ }
+ if (node && node->hasTagName(canvasTag))
+ return ImageRole;
+
+ if (cssBox && cssBox->isRenderView())
+ return WebAreaRole;
+
+ if (cssBox && cssBox->isTextField())
+ return TextFieldRole;
+
+ if (cssBox && cssBox->isTextArea())
+ return TextAreaRole;
+
+ if (node && node->hasTagName(inputTag)) {
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
+ if (input->isCheckbox())
+ return CheckBoxRole;
+ if (input->isRadioButton())
+ return RadioButtonRole;
+ if (input->isTextButton())
+ return ButtonRole;
+ }
+
+ if (node && node->hasTagName(buttonTag))
+ return ButtonRole;
+
+ if (isFileUploadButton())
+ return ButtonRole;
+
+ if (cssBox && cssBox->isMenuList())
+ return PopUpButtonRole;
+
+ if (headingLevel())
+ return HeadingRole;
+
+ if (node && node->hasTagName(ddTag))
+ return DefinitionListDefinitionRole;
+
+ if (node && node->hasTagName(dtTag))
+ return DefinitionListTermRole;
+
+ if (node && (node->hasTagName(rpTag) || node->hasTagName(rtTag)))
+ return AnnotationRole;
+
+#if PLATFORM(GTK)
+ // Gtk ATs expect all tables, data and layout, to be exposed as tables.
+ if (node && (node->hasTagName(tdTag) || node->hasTagName(thTag)))
+ return CellRole;
+
+ if (node && node->hasTagName(trTag))
+ return RowRole;
+
+ if (node && node->hasTagName(tableTag))
+ return TableRole;
+#endif
+
+ // Table sections should be ignored.
+ if (m_renderer->isTableSection())
+ return IgnoredRole;
+
+#if PLATFORM(GTK)
+ if (m_renderer->isHR())
+ return SplitterRole;
+#endif
+
+ if (m_renderer->isBlockFlow() || (node && node->hasTagName(labelTag)))
+ return GroupRole;
+
+ // If the element does not have role, but it has ARIA attributes, accessibility should fallback to exposing it as a group.
+ if (supportsARIAAttributes())
+ return GroupRole;
+
+ return UnknownRole;
+}
+
+AccessibilityOrientation AccessibilityRenderObject::orientation() const
+{
+ const AtomicString& ariaOrientation = getAttribute(aria_orientationAttr);
+ if (equalIgnoringCase(ariaOrientation, "horizontal"))
+ return AccessibilityOrientationHorizontal;
+ if (equalIgnoringCase(ariaOrientation, "vertical"))
+ return AccessibilityOrientationVertical;
+
+ return AccessibilityObject::orientation();
+}
+
+bool AccessibilityRenderObject::inheritsPresentationalRole() const
+{
+ // ARIA spec says that when a parent object is presentational, and it has required child elements,
+ // those child elements are also presentational. For example, <li> becomes presentational from <ul>.
+ // http://www.w3.org/WAI/PF/aria/complete#presentation
+ DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, listItemParents, ());
+
+ HashSet<QualifiedName>* possibleParentTagNames = 0;
+ switch (roleValue()) {
+ case ListItemRole:
+ case ListMarkerRole:
+ if (listItemParents.isEmpty()) {
+ listItemParents.add(ulTag);
+ listItemParents.add(olTag);
+ listItemParents.add(dlTag);
+ }
+ possibleParentTagNames = &listItemParents;
+ break;
+ default:
+ break;
+ }
+
+ // Not all elements need to check for this, only ones that are required children.
+ if (!possibleParentTagNames)
+ return false;
+
+ for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
+ if (!parent->isAccessibilityRenderObject())
+ continue;
+
+ Node* elementNode = static_cast<AccessibilityRenderObject*>(parent)->node();
+ if (!elementNode || !elementNode->isElementNode())
+ continue;
+
+ // If native tag of the parent element matches an acceptable name, then return
+ // based on its presentational status.
+ if (possibleParentTagNames->contains(static_cast<Element*>(elementNode)->tagQName()))
+ return parent->roleValue() == PresentationalRole;
+ }
+
+ return false;
+}
+
+bool AccessibilityRenderObject::isPresentationalChildOfAriaRole() const
+{
+ // Walk the parent chain looking for a parent that has presentational children
+ AccessibilityObject* parent;
+ for (parent = parentObject(); parent && !parent->ariaRoleHasPresentationalChildren(); parent = parent->parentObject())
+ { }
+
+ return parent;
+}
+
+bool AccessibilityRenderObject::ariaRoleHasPresentationalChildren() const
+{
+ switch (m_ariaRole) {
+ case ButtonRole:
+ case SliderRole:
+ case ImageRole:
+ case ProgressIndicatorRole:
+ // case SeparatorRole:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool AccessibilityRenderObject::canSetFocusAttribute() const
+{
+ ASSERT(m_renderer);
+ Node* node = m_renderer->node();
+
+ // 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 || !node->isElementNode())
+ return false;
+
+ if (!static_cast<Element*>(node)->isEnabledFormControl())
+ return false;
+
+ switch (roleValue()) {
+ case WebCoreLinkRole:
+ case ImageMapLinkRole:
+ case TextFieldRole:
+ case TextAreaRole:
+ case ButtonRole:
+ case PopUpButtonRole:
+ case CheckBoxRole:
+ case RadioButtonRole:
+ case SliderRole:
+ return true;
+ default:
+ return node->supportsFocus();
+ }
+}
+
+bool AccessibilityRenderObject::canSetExpandedAttribute() const
+{
+ // An object can be expanded if it aria-expanded is true or false.
+ const AtomicString& ariaExpanded = getAttribute(aria_expandedAttr);
+ return equalIgnoringCase(ariaExpanded, "true") || equalIgnoringCase(ariaExpanded, "false");
+}
+
+bool AccessibilityRenderObject::canSetValueAttribute() const
+{
+ if (equalIgnoringCase(getAttribute(aria_readonlyAttr), "true"))
+ return false;
+
+ // Any node could be contenteditable, so isReadOnly should be relied upon
+ // for this information for all elements.
+ return isProgressIndicator() || isSlider() || !isReadOnly();
+}
+
+bool AccessibilityRenderObject::canSetTextRangeAttributes() const
+{
+ return isTextControl();
+}
+
+void AccessibilityRenderObject::contentChanged()
+{
+ // If this element supports ARIA live regions, then notify the AT of changes.
+ AXObjectCache* cache = axObjectCache();
+ for (RenderObject* renderParent = m_renderer; renderParent; renderParent = renderParent->parent()) {
+ AccessibilityObject* parent = cache->get(renderParent);
+ if (!parent)
+ continue;
+
+ // If we find a parent that has ARIA live region on, send the notification and stop processing.
+ // The spec does not talk about nested live regions.
+ if (parent->supportsARIALiveRegion()) {
+ axObjectCache()->postNotification(renderParent, AXObjectCache::AXLiveRegionChanged, true);
+ break;
+ }
+ }
+}
+
+void AccessibilityRenderObject::childrenChanged()
+{
+ // This method is meant as a quick way of marking a portion of the accessibility tree dirty.
+ if (!m_renderer)
+ return;
+
+ bool sentChildrenChanged = false;
+
+ // 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()) {
+ if (!parent->isAccessibilityRenderObject())
+ continue;
+
+ AccessibilityRenderObject* axParent = static_cast<AccessibilityRenderObject*>(parent);
+
+ // Send the children changed notification on the first accessibility render object ancestor.
+ if (!sentChildrenChanged) {
+ axObjectCache()->postNotification(axParent->renderer(), AXObjectCache::AXChildrenChanged, true);
+ sentChildrenChanged = true;
+ }
+
+ // Only do work if the children haven't been marked dirty. This has the effect of blocking
+ // future live region change notifications until the AX tree has been accessed again. This
+ // is a good performance win for all parties.
+ if (!axParent->needsToUpdateChildren()) {
+ axParent->setNeedsToUpdateChildren();
+
+ // If this element supports ARIA live regions, then notify the AT of changes.
+ if (axParent->supportsARIALiveRegion())
+ axObjectCache()->postNotification(axParent->renderer(), AXObjectCache::AXLiveRegionChanged, true);
+ }
+ }
+}
+
+bool AccessibilityRenderObject::canHaveChildren() const
+{
+ if (!m_renderer)
+ return false;
+
+ // Elements that should not have children
+ switch (roleValue()) {
+ case ImageRole:
+ case ButtonRole:
+ case PopUpButtonRole:
+ case CheckBoxRole:
+ case RadioButtonRole:
+ case TabRole:
+ case StaticTextRole:
+ case ListBoxOptionRole:
+ case ScrollBarRole:
+ return false;
+ default:
+ return true;
+ }
+}
+
+void AccessibilityRenderObject::clearChildren()
+{
+ AccessibilityObject::clearChildren();
+ m_childrenDirty = false;
+}
+
+void AccessibilityRenderObject::updateChildrenIfNecessary()
+{
+ if (needsToUpdateChildren())
+ clearChildren();
+
+ AccessibilityObject::updateChildrenIfNecessary();
+}
+
+const AccessibilityObject::AccessibilityChildrenVector& AccessibilityRenderObject::children()
+{
+ updateChildrenIfNecessary();
+
+ return m_children;
+}
+
+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())
+ return;
+
+ // add all unignored acc children
+ for (RefPtr<AccessibilityObject> obj = firstChild(); obj; obj = obj->nextSibling()) {
+ if (obj->accessibilityIsIgnored()) {
+ obj->updateChildrenIfNecessary();
+ AccessibilityChildrenVector children = obj->children();
+ unsigned length = children.size();
+ for (unsigned i = 0; i < length; ++i)
+ m_children.append(children[i]);
+ } else {
+ ASSERT(obj->parentObject() == this);
+ m_children.append(obj);
+ }
+ }
+
+ // FrameView's need to be inserted into the AX hierarchy when encountered.
+ if (isAttachment()) {
+ Widget* widget = widgetForAttachmentView();
+ if (widget && widget->isFrameView())
+ m_children.append(axObjectCache()->getOrCreate(widget));
+ }
+
+ // for a RenderImage, add the <area> elements as individual accessibility objects
+ RenderBoxModelObject* cssBox = renderBoxModelObject();
+ if (cssBox && cssBox->isRenderImage()) {
+ HTMLMapElement* map = toRenderImage(cssBox)->imageMap();
+ if (map) {
+ for (Node* current = map->firstChild(); current; current = current->traverseNextNode(map)) {
+
+ // add an <area> element for this child if it has a link
+ if (current->hasTagName(areaTag) && current->isLink()) {
+ AccessibilityImageMapLink* areaObject = static_cast<AccessibilityImageMapLink*>(axObjectCache()->getOrCreate(ImageMapLinkRole));
+ areaObject->setHTMLAreaElement(static_cast<HTMLAreaElement*>(current));
+ areaObject->setHTMLMapElement(map);
+ areaObject->setParent(this);
+
+ m_children.append(areaObject);
+ }
+ }
+ }
+ }
+}
+
+const AtomicString& AccessibilityRenderObject::ariaLiveRegionStatus() const
+{
+ DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusAssertive, ("assertive"));
+ DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusPolite, ("polite"));
+ DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusOff, ("off"));
+
+ const AtomicString& liveRegionStatus = getAttribute(aria_liveAttr);
+ // These roles have implicit live region status.
+ if (liveRegionStatus.isEmpty()) {
+ switch (roleValue()) {
+ case ApplicationAlertDialogRole:
+ case ApplicationAlertRole:
+ return liveRegionStatusAssertive;
+ case ApplicationLogRole:
+ case ApplicationStatusRole:
+ return liveRegionStatusPolite;
+ case ApplicationTimerRole:
+ case ApplicationMarqueeRole:
+ return liveRegionStatusOff;
+ default:
+ break;
+ }
+ }
+
+ return liveRegionStatus;
+}
+
+const AtomicString& AccessibilityRenderObject::ariaLiveRegionRelevant() const
+{
+ DEFINE_STATIC_LOCAL(const AtomicString, defaultLiveRegionRelevant, ("additions text"));
+ const AtomicString& relevant = getAttribute(aria_relevantAttr);
+
+ // Default aria-relevant = "additions text".
+ if (relevant.isEmpty())
+ return defaultLiveRegionRelevant;
+
+ return relevant;
+}
+
+bool AccessibilityRenderObject::ariaLiveRegionAtomic() const
+{
+ return elementAttributeValue(aria_atomicAttr);
+}
+
+bool AccessibilityRenderObject::ariaLiveRegionBusy() const
+{
+ return elementAttributeValue(aria_busyAttr);
+}
+
+void AccessibilityRenderObject::ariaSelectedRows(AccessibilityChildrenVector& result)
+{
+ // Get all the rows.
+ AccessibilityChildrenVector allRows;
+ ariaTreeRows(allRows);
+
+ // Determine which rows are selected.
+ bool isMulti = isMultiSelectable();
+
+ // Prefer active descendant over aria-selected.
+ AccessibilityObject* activeDesc = activeDescendant();
+ if (activeDesc && (activeDesc->isTreeItem() || activeDesc->isTableRow())) {
+ result.append(activeDesc);
+ if (!isMulti)
+ return;
+ }
+
+ unsigned count = allRows.size();
+ for (unsigned k = 0; k < count; ++k) {
+ if (allRows[k]->isSelected()) {
+ result.append(allRows[k]);
+ if (!isMulti)
+ break;
+ }
+ }
+}
+
+void AccessibilityRenderObject::ariaListboxSelectedChildren(AccessibilityChildrenVector& result)
+{
+ bool isMulti = isMultiSelectable();
+
+ AccessibilityChildrenVector childObjects = children();
+ unsigned childrenSize = childObjects.size();
+ for (unsigned k = 0; k < childrenSize; ++k) {
+ // Every child should have aria-role option, and if so, check for selected attribute/state.
+ AccessibilityObject* child = childObjects[k].get();
+ if (child->isSelected() && child->ariaRoleAttribute() == ListBoxOptionRole) {
+ result.append(child);
+ if (!isMulti)
+ return;
+ }
+ }
+}
+
+void AccessibilityRenderObject::selectedChildren(AccessibilityChildrenVector& result)
+{
+ ASSERT(result.isEmpty());
+
+ // only listboxes should be asked for their selected children.
+ AccessibilityRole role = roleValue();
+ if (role == ListBoxRole) // native list boxes would be AccessibilityListBoxes, so only check for aria list boxes
+ ariaListboxSelectedChildren(result);
+ else if (role == TreeRole || role == TreeGridRole || role == TableRole)
+ ariaSelectedRows(result);
+}
+
+void AccessibilityRenderObject::ariaListboxVisibleChildren(AccessibilityChildrenVector& result)
+{
+ if (!hasChildren())
+ addChildren();
+
+ unsigned length = m_children.size();
+ for (unsigned i = 0; i < length; i++) {
+ if (!m_children[i]->isOffScreen())
+ result.append(m_children[i]);
+ }
+}
+
+void AccessibilityRenderObject::visibleChildren(AccessibilityChildrenVector& result)
+{
+ ASSERT(result.isEmpty());
+
+ // only listboxes are asked for their visible children.
+ if (ariaRoleAttribute() != ListBoxRole) { // native list boxes would be AccessibilityListBoxes, so only check for aria list boxes
+ ASSERT_NOT_REACHED();
+ return;
+ }
+ return ariaListboxVisibleChildren(result);
+}
+
+void AccessibilityRenderObject::tabChildren(AccessibilityChildrenVector& result)
+{
+ ASSERT(roleValue() == TabListRole);
+
+ unsigned length = m_children.size();
+ for (unsigned i = 0; i < length; ++i) {
+ if (m_children[i]->isTabItem())
+ result.append(m_children[i]);
+ }
+}
+
+const String& AccessibilityRenderObject::actionVerb() const
+{
+ // FIXME: Need to add verbs for select elements.
+ DEFINE_STATIC_LOCAL(const String, buttonAction, (AXButtonActionVerb()));
+ DEFINE_STATIC_LOCAL(const String, textFieldAction, (AXTextFieldActionVerb()));
+ DEFINE_STATIC_LOCAL(const String, radioButtonAction, (AXRadioButtonActionVerb()));
+ DEFINE_STATIC_LOCAL(const String, checkedCheckBoxAction, (AXCheckedCheckBoxActionVerb()));
+ DEFINE_STATIC_LOCAL(const String, uncheckedCheckBoxAction, (AXUncheckedCheckBoxActionVerb()));
+ DEFINE_STATIC_LOCAL(const String, linkAction, (AXLinkActionVerb()));
+ DEFINE_STATIC_LOCAL(const String, noAction, ());
+
+ switch (roleValue()) {
+ case ButtonRole:
+ return buttonAction;
+ case TextFieldRole:
+ case TextAreaRole:
+ return textFieldAction;
+ case RadioButtonRole:
+ return radioButtonAction;
+ case CheckBoxRole:
+ return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction;
+ case LinkRole:
+ case WebCoreLinkRole:
+ return linkAction;
+ default:
+ return noAction;
+ }
+}
+
+void AccessibilityRenderObject::setAccessibleName(String& name)
+{
+ // Setting the accessible name can store the value in the DOM
+ if (!m_renderer)
+ return;
+
+ Node* domNode = 0;
+ // For web areas, set the aria-label on the HTML element.
+ if (isWebArea())
+ domNode = m_renderer->document()->documentElement();
+ else
+ domNode = m_renderer->node();
+
+ if (domNode && domNode->isElementNode())
+ static_cast<Element*>(domNode)->setAttribute(aria_labelAttr, name);
+}
+
+void AccessibilityRenderObject::updateBackingStore()
+{
+ if (!m_renderer)
+ return;
+
+ // Updating layout may delete m_renderer and this object.
+ m_renderer->document()->updateLayoutIgnorePendingStylesheets();
+}
+
+static bool isLinkable(const AccessibilityRenderObject& object)
+{
+ if (!object.renderer())
+ return false;
+
+ // See https://wiki.mozilla.org/Accessibility/AT-Windows-API for the elements
+ // Mozilla considers linkable.
+ return object.isLink() || object.isImage() || object.renderer()->isText();
+}
+
+String AccessibilityRenderObject::stringValueForMSAA() const
+{
+ if (isLinkable(*this)) {
+ Element* anchor = anchorElement();
+ if (anchor && anchor->hasTagName(aTag))
+ return static_cast<HTMLAnchorElement*>(anchor)->href();
+ }
+
+ return stringValue();
+}
+
+bool AccessibilityRenderObject::isLinked() const
+{
+ if (!isLinkable(*this))
+ return false;
+
+ Element* anchor = anchorElement();
+ if (!anchor || !anchor->hasTagName(aTag))
+ return false;
+
+ return !static_cast<HTMLAnchorElement*>(anchor)->href().isEmpty();
+}
+
+String AccessibilityRenderObject::nameForMSAA() const
+{
+ if (m_renderer && m_renderer->isText())
+ return textUnderElement();
+
+ return title();
+}
+
+static bool shouldReturnTagNameAsRoleForMSAA(const Element& element)
+{
+ // See "document structure",
+ // https://wiki.mozilla.org/Accessibility/AT-Windows-API
+ // FIXME: Add the other tag names that should be returned as the role.
+ return element.hasTagName(h1Tag) || element.hasTagName(h2Tag)
+ || element.hasTagName(h3Tag) || element.hasTagName(h4Tag)
+ || element.hasTagName(h5Tag) || element.hasTagName(h6Tag);
+}
+
+String AccessibilityRenderObject::stringRoleForMSAA() const
+{
+ if (!m_renderer)
+ return String();
+
+ Node* node = m_renderer->node();
+ if (!node || !node->isElementNode())
+ return String();
+
+ Element* element = static_cast<Element*>(node);
+ if (!shouldReturnTagNameAsRoleForMSAA(*element))
+ return String();
+
+ return element->tagName();
+}
+
+String AccessibilityRenderObject::positionalDescriptionForMSAA() const
+{
+ // See "positional descriptions",
+ // https://wiki.mozilla.org/Accessibility/AT-Windows-API
+ if (isHeading())
+ return "L" + String::number(headingLevel());
+
+ // FIXME: Add positional descriptions for other elements.
+ return String();
+}
+
+String AccessibilityRenderObject::descriptionForMSAA() const
+{
+ String description = positionalDescriptionForMSAA();
+ if (!description.isEmpty())
+ return description;
+
+ description = accessibilityDescription();
+ if (!description.isEmpty()) {
+ // From the Mozilla MSAA implementation:
+ // "Signal to screen readers that this description is speakable and is not
+ // a formatted positional information description. Don't localize the
+ // 'Description: ' part of this string, it will be parsed out by assistive
+ // technologies."
+ return "Description: " + description;
+ }
+
+ return String();
+}
+
+static AccessibilityRole msaaRoleForRenderer(const RenderObject* renderer)
+{
+ if (!renderer)
+ return UnknownRole;
+
+ if (renderer->isText())
+ return EditableTextRole;
+
+ if (renderer->isBoxModelObject() && toRenderBoxModelObject(renderer)->isListItem())
+ return ListItemRole;
+
+ return UnknownRole;
+}
+
+AccessibilityRole AccessibilityRenderObject::roleValueForMSAA() const
+{
+ if (m_roleForMSAA != UnknownRole)
+ return m_roleForMSAA;
+
+ m_roleForMSAA = msaaRoleForRenderer(m_renderer);
+
+ if (m_roleForMSAA == UnknownRole)
+ m_roleForMSAA = roleValue();
+
+ return m_roleForMSAA;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityRenderObject.h b/Source/WebCore/accessibility/AccessibilityRenderObject.h
new file mode 100644
index 0000000..3a29fe9
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityRenderObject.h
@@ -0,0 +1,332 @@
+
+/*
+ * Copyright (C) 2008 Apple 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 AccessibilityRenderObject_h
+#define AccessibilityRenderObject_h
+
+#include "AccessibilityObject.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 AccessibilityRenderObject : public AccessibilityObject {
+protected:
+ AccessibilityRenderObject(RenderObject*);
+public:
+ static PassRefPtr<AccessibilityRenderObject> create(RenderObject*);
+ virtual ~AccessibilityRenderObject();
+
+ bool isAccessibilityRenderObject() const { return true; }
+
+ virtual bool isAnchor() const;
+ virtual bool isAttachment() const;
+ virtual bool isHeading() const;
+ virtual bool isLink() const;
+ virtual bool isImageButton() const;
+ virtual bool isImage() const;
+ virtual bool isNativeImage() const;
+ virtual bool isPasswordField() const;
+ virtual bool isNativeTextControl() const;
+ virtual bool isWebArea() const;
+ virtual bool isFileUploadButton() const;
+ virtual bool isInputImage() const;
+ virtual bool isProgressIndicator() const;
+ virtual bool isSlider() const;
+ virtual bool isMenuRelated() const;
+ virtual bool isMenu() const;
+ virtual bool isMenuBar() const;
+ virtual bool isMenuButton() const;
+ virtual bool isMenuItem() const;
+ virtual bool isControl() const;
+ virtual bool isFieldset() const;
+ virtual bool isGroup() const;
+
+ virtual bool isEnabled() const;
+ virtual bool isSelected() const;
+ virtual bool isFocused() const;
+ virtual bool isChecked() const;
+ virtual bool isHovered() const;
+ virtual bool isIndeterminate() const;
+ virtual bool isLoaded() const;
+ virtual bool isMultiSelectable() const;
+ virtual bool isOffScreen() const;
+ virtual bool isPressed() const;
+ virtual bool isReadOnly() const;
+ virtual bool isVisited() const;
+ virtual bool isRequired() const;
+ virtual bool isLinked() const;
+ virtual void setIsExpanded(bool);
+
+ virtual bool canSetFocusAttribute() const;
+ virtual bool canSetTextRangeAttributes() const;
+ virtual bool canSetValueAttribute() const;
+ virtual bool canSetExpandedAttribute() const;
+
+ virtual void setAccessibleName(String&);
+
+ // Provides common logic used by all elements when determining isIgnored.
+ AccessibilityObjectInclusion accessibilityIsIgnoredBase() const;
+ virtual bool accessibilityIsIgnored() const;
+
+ virtual int headingLevel() const;
+ virtual AccessibilityButtonState checkboxOrRadioValue() const;
+ virtual String valueDescription() const;
+ virtual float valueForRange() const;
+ virtual float maxValueForRange() const;
+ virtual float minValueForRange() const;
+ virtual AccessibilityObject* selectedRadioButton();
+ virtual AccessibilityObject* selectedTabItem();
+ virtual int layoutCount() const;
+ virtual double estimatedLoadingProgress() 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;
+ virtual AccessibilityObject* observableObject() const;
+ virtual void linkedUIElements(AccessibilityChildrenVector&) const;
+ virtual bool exposesTitleUIElement() const;
+ virtual AccessibilityObject* titleUIElement() const;
+ virtual AccessibilityObject* correspondingControlForLabelElement() const;
+ virtual AccessibilityObject* correspondingLabelForControlElement() const;
+
+ 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;
+
+ virtual Element* actionElement() const;
+ Element* mouseButtonListener() const;
+ FrameView* frameViewIfRenderView() const;
+ virtual Element* anchorElement() const;
+ AccessibilityObject* menuForMenuButton() const;
+ AccessibilityObject* menuButtonForMenu() const;
+
+ virtual IntRect boundingBoxRect() const;
+ virtual IntRect elementRect() const;
+ virtual IntSize size() const;
+ virtual IntPoint clickPoint() const;
+
+ void setRenderer(RenderObject* renderer) { m_renderer = renderer; }
+ RenderObject* renderer() const { return m_renderer; }
+ RenderBoxModelObject* renderBoxModelObject() const;
+ virtual Node* node() const;
+
+ RenderView* topRenderer() const;
+ RenderTextControl* textControl() const;
+ Document* document() const;
+ FrameView* topDocumentFrameView() const;
+ HTMLLabelElement* labelElementContainer() const;
+
+ virtual KURL url() const;
+ virtual PlainTextRange selectedTextRange() const;
+ virtual VisibleSelection selection() const;
+ virtual String stringValue() const;
+ virtual String ariaLabeledByAttribute() const;
+ virtual String title() const;
+ virtual String ariaDescribedByAttribute() const;
+ virtual String accessibilityDescription() const;
+ virtual String helpText() const;
+ virtual String textUnderElement() const;
+ virtual String text() const;
+ virtual int textLength() const;
+ virtual String selectedText() const;
+ virtual const AtomicString& accessKey() const;
+ virtual const String& actionVerb() const;
+ virtual Widget* widget() const;
+ virtual Widget* widgetForAttachmentView() const;
+ virtual void getDocumentLinks(AccessibilityChildrenVector&);
+ virtual FrameView* documentFrameView() const;
+ virtual unsigned hierarchicalLevel() const;
+
+ virtual const AccessibilityChildrenVector& children();
+ virtual void clearChildren();
+ virtual void updateChildrenIfNecessary();
+
+ virtual void setFocused(bool);
+ virtual void setSelectedTextRange(const PlainTextRange&);
+ virtual void setValue(const String&);
+ virtual void setSelected(bool);
+ virtual void setSelectedRows(AccessibilityChildrenVector&);
+ virtual void changeValueByPercent(float percentChange);
+ virtual AccessibilityOrientation orientation() const;
+ virtual void increment();
+ virtual void decrement();
+
+ virtual void detach();
+ virtual void childrenChanged();
+ virtual void contentChanged();
+ virtual void addChildren();
+ virtual bool canHaveChildren() const;
+ virtual void selectedChildren(AccessibilityChildrenVector&);
+ virtual void visibleChildren(AccessibilityChildrenVector&);
+ virtual void tabChildren(AccessibilityChildrenVector&);
+ virtual bool shouldFocusActiveDescendant() const;
+ virtual AccessibilityObject* activeDescendant() const;
+ virtual void handleActiveDescendantChanged();
+ virtual void handleAriaExpandedChanged();
+
+ virtual VisiblePositionRange visiblePositionRange() const;
+ virtual VisiblePositionRange visiblePositionRangeForLine(unsigned) const;
+ virtual IntRect boundsForVisiblePositionRange(const VisiblePositionRange&) const;
+ virtual void setSelectedVisiblePositionRange(const VisiblePositionRange&) const;
+ virtual bool supportsARIAFlowTo() const;
+ virtual void ariaFlowToElements(AccessibilityChildrenVector&) const;
+ virtual bool ariaHasPopup() const;
+
+ virtual bool supportsARIADropping() const;
+ virtual bool supportsARIADragging() const;
+ virtual bool isARIAGrabbed();
+ virtual void setARIAGrabbed(bool);
+ virtual void determineARIADropEffects(Vector<String>&);
+
+ virtual VisiblePosition visiblePositionForPoint(const IntPoint&) const;
+ virtual VisiblePosition visiblePositionForIndex(unsigned indexValue, bool lastIndexOK) const;
+ virtual int index(const VisiblePosition&) const;
+
+ virtual VisiblePosition visiblePositionForIndex(int) const;
+ virtual int indexForVisiblePosition(const VisiblePosition&) const;
+
+ virtual PlainTextRange doAXRangeForLine(unsigned) const;
+ virtual PlainTextRange doAXRangeForIndex(unsigned) const;
+
+ virtual String doAXStringForRange(const PlainTextRange&) const;
+ virtual IntRect doAXBoundsForRange(const PlainTextRange&) const;
+
+ virtual void updateBackingStore();
+
+ virtual String stringValueForMSAA() const;
+ virtual String stringRoleForMSAA() const;
+ virtual String nameForMSAA() const;
+ virtual String descriptionForMSAA() const;
+ virtual AccessibilityRole roleValueForMSAA() const;
+
+protected:
+ RenderObject* m_renderer;
+ AccessibilityRole m_ariaRole;
+ mutable bool m_childrenDirty;
+
+ void setRenderObject(RenderObject* renderer) { m_renderer = renderer; }
+ void ariaLabeledByElements(Vector<Element*>& elements) const;
+ bool needsToUpdateChildren() const { return m_childrenDirty; }
+
+ virtual bool isDetached() const { return !m_renderer; }
+
+private:
+ void ariaListboxSelectedChildren(AccessibilityChildrenVector&);
+ void ariaListboxVisibleChildren(AccessibilityChildrenVector&);
+ bool ariaIsHidden() const;
+ bool isDescendantOfBarrenParent() const;
+ bool isAllowedChildOfTree() const;
+ bool hasTextAlternative() const;
+ String positionalDescriptionForMSAA() const;
+ PlainTextRange ariaSelectedTextRange() const;
+
+ Element* menuElementForMenuButton() const;
+ Element* menuItemElementForMenu() const;
+ AccessibilityRole determineAccessibilityRole();
+ AccessibilityRole determineAriaRoleAttribute() const;
+
+ bool isTabItemSelected() const;
+ bool isNativeCheckboxOrRadio() const;
+ IntRect checkboxOrRadioRect() const;
+ void addRadioButtonGroupMembers(AccessibilityChildrenVector& linkedUIElements) const;
+ AccessibilityObject* internalLinkElement() const;
+ AccessibilityObject* accessibilityImageMapHitTest(HTMLAreaElement*, const IntPoint&) const;
+ AccessibilityObject* accessibilityParentForImageMap(HTMLMapElement*) const;
+ bool renderObjectIsObservable(RenderObject*) const;
+ RenderObject* renderParentObject() const;
+
+ void ariaSelectedRows(AccessibilityChildrenVector&);
+
+ bool elementAttributeValue(const QualifiedName&) const;
+ void setElementAttributeValue(const QualifiedName&, bool);
+
+ String accessibilityDescriptionForElements(Vector<Element*> &elements) const;
+ void elementsFromAttribute(Vector<Element*>& elements, const QualifiedName&) const;
+ String ariaAccessibilityDescription() const;
+
+ virtual ESpeak speakProperty() const;
+
+ virtual const AtomicString& ariaLiveRegionStatus() const;
+ virtual const AtomicString& ariaLiveRegionRelevant() const;
+ virtual bool ariaLiveRegionAtomic() const;
+ virtual bool ariaLiveRegionBusy() const;
+
+ bool inheritsPresentationalRole() const;
+ void setNeedsToUpdateChildren() const { m_childrenDirty = true; }
+
+ mutable AccessibilityRole m_roleForMSAA;
+};
+
+inline AccessibilityRenderObject* toAccessibilityRenderObject(AccessibilityObject* object)
+{
+ ASSERT(!object || object->isAccessibilityRenderObject());
+ return static_cast<AccessibilityRenderObject*>(object);
+}
+
+inline const AccessibilityRenderObject* toAccessibilityRenderObject(const AccessibilityObject* object)
+{
+ ASSERT(!object || object->isAccessibilityRenderObject());
+ return static_cast<const AccessibilityRenderObject*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toAccessibilityRenderObject(const AccessibilityRenderObject*);
+
+} // namespace WebCore
+
+#endif // AccessibilityRenderObject_h
diff --git a/Source/WebCore/accessibility/AccessibilityScrollView.cpp b/Source/WebCore/accessibility/AccessibilityScrollView.cpp
new file mode 100644
index 0000000..c9dcbb6
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityScrollView.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2011 Apple 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.
+ *
+ * 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 "AccessibilityScrollView.h"
+
+#include "AXObjectCache.h"
+#include "AccessibilityScrollbar.h"
+#include "FrameView.h"
+#include "HTMLFrameOwnerElement.h"
+#include "RenderPart.h"
+#include "ScrollView.h"
+
+namespace WebCore {
+
+AccessibilityScrollView::AccessibilityScrollView(ScrollView* view)
+ : m_scrollView(view)
+{
+}
+
+PassRefPtr<AccessibilityScrollView> AccessibilityScrollView::create(ScrollView* view)
+{
+ return adoptRef(new AccessibilityScrollView(view));
+}
+
+AccessibilityObject* AccessibilityScrollView::scrollBar(AccessibilityOrientation orientation) const
+{
+ switch (orientation) {
+ case AccessibilityOrientationVertical:
+ return m_verticalScrollbar ? m_verticalScrollbar.get() : 0;
+ case AccessibilityOrientationHorizontal:
+ return m_horizontalScrollbar ? m_horizontalScrollbar.get() : 0;
+ }
+
+ return 0;
+}
+
+const AccessibilityObject::AccessibilityChildrenVector& AccessibilityScrollView::children()
+{
+ if (!m_haveChildren)
+ addChildren();
+ return m_children;
+}
+
+void AccessibilityScrollView::updateChildrenIfNecessary()
+{
+ if (m_scrollView->horizontalScrollbar() && !m_horizontalScrollbar)
+ m_horizontalScrollbar = addChildScrollbar(m_scrollView->horizontalScrollbar());
+ else if (!m_scrollView->horizontalScrollbar() && m_horizontalScrollbar) {
+ removeChildScrollbar(m_horizontalScrollbar.get());
+ m_horizontalScrollbar = 0;
+ }
+
+ if (m_scrollView->verticalScrollbar() && !m_verticalScrollbar)
+ m_verticalScrollbar = addChildScrollbar(m_scrollView->verticalScrollbar());
+ else if (!m_scrollView->verticalScrollbar() && m_verticalScrollbar) {
+ removeChildScrollbar(m_verticalScrollbar.get());
+ m_verticalScrollbar = 0;
+ }
+}
+
+void AccessibilityScrollView::removeChildScrollbar(AccessibilityObject* scrollbar)
+{
+ size_t pos = m_children.find(scrollbar);
+ if (pos != WTF::notFound)
+ m_children.remove(pos);
+}
+
+AccessibilityScrollbar* AccessibilityScrollView::addChildScrollbar(Scrollbar* scrollbar)
+{
+ if (!scrollbar)
+ return 0;
+
+ AccessibilityScrollbar* scrollBarObject = static_cast<AccessibilityScrollbar*>(axObjectCache()->getOrCreate(scrollbar));
+ scrollBarObject->setParent(this);
+ m_children.append(scrollBarObject);
+ return scrollBarObject;
+}
+
+void AccessibilityScrollView::addChildren()
+{
+ ASSERT(!m_haveChildren);
+ m_haveChildren = true;
+
+ AccessibilityObject* webArea = webAreaObject();
+ if (webArea)
+ m_children.append(webArea);
+
+ updateChildrenIfNecessary();
+}
+
+AccessibilityObject* AccessibilityScrollView::webAreaObject() const
+{
+ if (!m_scrollView->isFrameView())
+ return 0;
+
+ Document* doc = static_cast<FrameView*>(m_scrollView.get())->frame()->document();
+ if (!doc || !doc->renderer())
+ return 0;
+
+ return axObjectCache()->getOrCreate(doc->renderer());
+}
+
+AccessibilityObject* AccessibilityScrollView::accessibilityHitTest(const IntPoint& point) const
+{
+ AccessibilityObject* webArea = webAreaObject();
+ if (!webArea)
+ return 0;
+
+ if (m_horizontalScrollbar && m_horizontalScrollbar->elementRect().contains(point))
+ return m_horizontalScrollbar.get();
+ if (m_verticalScrollbar && m_verticalScrollbar->elementRect().contains(point))
+ return m_verticalScrollbar.get();
+
+ return webArea->accessibilityHitTest(point);
+}
+
+Document* AccessibilityScrollView::document() const
+{
+ if (!m_scrollView->isFrameView())
+ return 0;
+
+ return static_cast<FrameView*>(m_scrollView.get())->frame()->document();
+}
+
+IntRect AccessibilityScrollView::elementRect() const
+{
+ return m_scrollView->frameRect();
+}
+
+AccessibilityObject* AccessibilityScrollView::parentObject() const
+{
+ if (!m_scrollView->isFrameView())
+ return 0;
+
+ HTMLFrameOwnerElement* owner = static_cast<FrameView*>(m_scrollView.get())->frame()->ownerElement();
+ if (owner && owner->renderPart())
+ return axObjectCache()->getOrCreate(owner->renderPart()->parent());
+
+ return 0;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityScrollView.h b/Source/WebCore/accessibility/AccessibilityScrollView.h
new file mode 100644
index 0000000..e32cd1c
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityScrollView.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2011 Apple 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.
+ *
+ * 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 AccessibilityScrollView_h
+#define AccessibilityScrollView_h
+
+#include "AccessibilityObject.h"
+
+namespace WebCore {
+
+class AccessibilityScrollbar;
+class Scrollbar;
+class ScrollView;
+
+class AccessibilityScrollView : public AccessibilityObject {
+public:
+ static PassRefPtr<AccessibilityScrollView> create(ScrollView*);
+ virtual AccessibilityRole roleValue() const { return ScrollAreaRole; }
+ ScrollView* scrollView() const { return m_scrollView.get(); }
+
+private:
+ AccessibilityScrollView(ScrollView*);
+
+ virtual bool accessibilityIsIgnored() const { return false; }
+ virtual bool isAccessibilityScrollView() const { return true; }
+ virtual AccessibilityObject* scrollBar(AccessibilityOrientation) const;
+ virtual void addChildren();
+ virtual Document* document() const;
+ virtual AccessibilityObject* accessibilityHitTest(const IntPoint&) const;
+ virtual const AccessibilityChildrenVector& children();
+ virtual void updateChildrenIfNecessary();
+
+ virtual IntRect elementRect() const;
+ virtual AccessibilityObject* parentObject() const;
+
+ AccessibilityObject* webAreaObject() const;
+ AccessibilityScrollbar* addChildScrollbar(Scrollbar*);
+ void removeChildScrollbar(AccessibilityObject*);
+
+ RefPtr<ScrollView> m_scrollView;
+ RefPtr<AccessibilityObject> m_horizontalScrollbar;
+ RefPtr<AccessibilityObject> m_verticalScrollbar;
+};
+
+inline AccessibilityScrollView* toAccessibilityScrollView(AccessibilityObject* object)
+{
+ ASSERT(!object || object->isAccessibilityScrollView());
+ if (!object->isAccessibilityScrollView())
+ return 0;
+
+ return static_cast<AccessibilityScrollView*>(object);
+}
+
+} // namespace WebCore
+
+#endif // AccessibilityScrollView_h
+
diff --git a/Source/WebCore/accessibility/AccessibilityScrollbar.cpp b/Source/WebCore/accessibility/AccessibilityScrollbar.cpp
new file mode 100644
index 0000000..865797a
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityScrollbar.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 Apple 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 "AccessibilityScrollbar.h"
+
+#include "AXObjectCache.h"
+#include "FrameView.h"
+#include "ScrollView.h"
+#include "Scrollbar.h"
+
+namespace WebCore {
+
+AccessibilityScrollbar::AccessibilityScrollbar(Scrollbar* scrollbar)
+ : m_scrollbar(scrollbar)
+ , m_parent(0)
+{
+ ASSERT(scrollbar);
+}
+
+PassRefPtr<AccessibilityScrollbar> AccessibilityScrollbar::create(Scrollbar* scrollbar)
+{
+ return adoptRef(new AccessibilityScrollbar(scrollbar));
+}
+
+IntRect AccessibilityScrollbar::elementRect() const
+{
+ if (!m_scrollbar)
+ return IntRect();
+
+ return m_scrollbar->frameRect();
+}
+
+Document* AccessibilityScrollbar::document() const
+{
+ AccessibilityObject* parent = parentObject();
+ if (!parent)
+ return 0;
+ return parent->document();
+}
+
+AccessibilityOrientation AccessibilityScrollbar::orientation() const
+{
+ if (!m_scrollbar)
+ return AccessibilityOrientationHorizontal;
+
+ if (m_scrollbar->orientation() == HorizontalScrollbar)
+ return AccessibilityOrientationHorizontal;
+ if (m_scrollbar->orientation() == VerticalScrollbar)
+ return AccessibilityOrientationVertical;
+
+ return AccessibilityOrientationHorizontal;
+}
+
+bool AccessibilityScrollbar::isEnabled() const
+{
+ if (!m_scrollbar)
+ return false;
+ return m_scrollbar->enabled();
+}
+
+float AccessibilityScrollbar::valueForRange() const
+{
+ if (!m_scrollbar)
+ return 0;
+
+ return m_scrollbar->currentPos() / m_scrollbar->maximum();
+}
+
+void AccessibilityScrollbar::setValue(float value)
+{
+ if (!m_scrollbar)
+ return;
+
+ float newValue = value * m_scrollbar->maximum();
+
+ m_scrollbar->setValue(newValue, Scrollbar::NotFromScrollAnimator);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityScrollbar.h b/Source/WebCore/accessibility/AccessibilityScrollbar.h
new file mode 100644
index 0000000..3268347
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityScrollbar.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 Apple 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 AccessibilityScrollbar_h
+#define AccessibilityScrollbar_h
+
+#include "AccessibilityObject.h"
+
+namespace WebCore {
+
+class Scrollbar;
+
+class AccessibilityScrollbar : public AccessibilityObject {
+public:
+ static PassRefPtr<AccessibilityScrollbar> create(Scrollbar*);
+
+ Scrollbar* scrollbar() const { return m_scrollbar.get(); }
+ void setParent(AccessibilityObject* parent) { m_parent = parent; }
+
+private:
+ AccessibilityScrollbar(Scrollbar*);
+
+ virtual bool accessibilityIsIgnored() const { return false; }
+ virtual bool canSetValueAttribute() const { return true; }
+ virtual bool canSetNumericValue() const { return true; }
+
+ virtual bool isAccessibilityScrollbar() const { return true; }
+ virtual AccessibilityObject* parentObject() const { return m_parent; }
+ virtual IntRect elementRect() const;
+
+ virtual AccessibilityRole roleValue() const { return ScrollBarRole; }
+ virtual AccessibilityOrientation orientation() const;
+ virtual Document* document() const;
+ virtual bool isEnabled() const;
+
+ // Assumes float [0..1]
+ virtual void setValue(float);
+ virtual float valueForRange() const;
+
+ RefPtr<Scrollbar> m_scrollbar;
+ AccessibilityOrientation m_orientation;
+ AccessibilityObject* m_parent;
+};
+
+} // namespace WebCore
+
+#endif // AccessibilityScrollbar_h
diff --git a/Source/WebCore/accessibility/AccessibilitySlider.cpp b/Source/WebCore/accessibility/AccessibilitySlider.cpp
new file mode 100644
index 0000000..d4433a2
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilitySlider.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2009 Apple 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 "AccessibilitySlider.h"
+
+#include "AXObjectCache.h"
+#include "HTMLInputElement.h"
+#include "HTMLNames.h"
+#include "RenderObject.h"
+#include "RenderSlider.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+AccessibilitySlider::AccessibilitySlider(RenderObject* renderer)
+ : AccessibilityRenderObject(renderer)
+{
+}
+
+PassRefPtr<AccessibilitySlider> AccessibilitySlider::create(RenderObject* renderer)
+{
+ return adoptRef(new AccessibilitySlider(renderer));
+}
+
+const AccessibilityObject::AccessibilityChildrenVector& AccessibilitySlider::children()
+{
+ if (!m_haveChildren)
+ addChildren();
+ return m_children;
+}
+
+AccessibilityOrientation AccessibilitySlider::orientation() const
+{
+ // Default to horizontal in the unknown case.
+ if (!m_renderer)
+ return AccessibilityOrientationHorizontal;
+
+ RenderStyle* style = m_renderer->style();
+ if (!style)
+ return AccessibilityOrientationHorizontal;
+
+ ControlPart styleAppearance = style->appearance();
+ switch (styleAppearance) {
+ case SliderThumbHorizontalPart:
+ case SliderHorizontalPart:
+ case MediaSliderPart:
+ return AccessibilityOrientationHorizontal;
+
+ case SliderThumbVerticalPart:
+ case SliderVerticalPart:
+ case MediaVolumeSliderPart:
+ return AccessibilityOrientationVertical;
+
+ default:
+ return AccessibilityOrientationHorizontal;
+ }
+}
+
+void AccessibilitySlider::addChildren()
+{
+ ASSERT(!m_haveChildren);
+
+ m_haveChildren = true;
+
+ AccessibilitySliderThumb* thumb = static_cast<AccessibilitySliderThumb*>(m_renderer->document()->axObjectCache()->getOrCreate(SliderThumbRole));
+ thumb->setParentObject(this);
+ m_children.append(thumb);
+}
+
+const AtomicString& AccessibilitySlider::getAttribute(const QualifiedName& attribute) const
+{
+ return element()->getAttribute(attribute);
+}
+
+AccessibilityObject* AccessibilitySlider::elementAccessibilityHitTest(const IntPoint& point) const
+{
+ if (m_children.size()) {
+ ASSERT(m_children.size() == 1);
+ if (m_children[0]->elementRect().contains(point))
+ return m_children[0].get();
+ }
+
+ return axObjectCache()->getOrCreate(m_renderer);
+}
+
+bool AccessibilitySlider::accessibilityIsIgnored() const
+{
+ AccessibilityObjectInclusion decision = accessibilityIsIgnoredBase();
+ if (decision == IncludeObject)
+ return false;
+ if (decision == IgnoreObject)
+ return true;
+
+ return false;
+}
+
+float AccessibilitySlider::valueForRange() const
+{
+ return element()->value().toFloat();
+}
+
+float AccessibilitySlider::maxValueForRange() const
+{
+ return static_cast<float>(element()->maximum());
+}
+
+float AccessibilitySlider::minValueForRange() const
+{
+ return static_cast<float>(element()->minimum());
+}
+
+void AccessibilitySlider::setValue(const String& value)
+{
+ HTMLInputElement* input = element();
+
+ if (input->value() == value)
+ return;
+
+ input->setValue(value);
+
+ // Fire change event manually, as RenderSlider::setValueForPosition does.
+ input->dispatchFormControlChangeEvent();
+}
+
+HTMLInputElement* AccessibilitySlider::element() const
+{
+ return static_cast<HTMLInputElement*>(m_renderer->node());
+}
+
+
+AccessibilitySliderThumb::AccessibilitySliderThumb()
+ : m_parentSlider(0)
+{
+}
+
+PassRefPtr<AccessibilitySliderThumb> AccessibilitySliderThumb::create()
+{
+ return adoptRef(new AccessibilitySliderThumb());
+}
+
+IntRect AccessibilitySliderThumb::elementRect() const
+{
+ if (!m_parentSlider->renderer())
+ return IntRect();
+
+ IntRect intRect = toRenderSlider(m_parentSlider->renderer())->thumbRect();
+ FloatQuad floatQuad = m_parentSlider->renderer()->localToAbsoluteQuad(FloatRect(intRect));
+
+ return floatQuad.enclosingBoundingBox();
+}
+
+IntSize AccessibilitySliderThumb::size() const
+{
+ return elementRect().size();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilitySlider.h b/Source/WebCore/accessibility/AccessibilitySlider.h
new file mode 100644
index 0000000..406c3ab
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilitySlider.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2009 Apple 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 AccessibilitySlider_h
+#define AccessibilitySlider_h
+
+#include "AccessibilityRenderObject.h"
+
+namespace WebCore {
+
+class HTMLInputElement;
+
+class AccessibilitySlider : public AccessibilityRenderObject {
+
+public:
+ static PassRefPtr<AccessibilitySlider> create(RenderObject*);
+ virtual ~AccessibilitySlider() { }
+
+protected:
+ AccessibilitySlider(RenderObject*);
+
+private:
+ HTMLInputElement* element() const;
+ virtual bool accessibilityIsIgnored() const;
+ virtual AccessibilityObject* elementAccessibilityHitTest(const IntPoint&) const;
+
+ virtual AccessibilityRole roleValue() const { return SliderRole; }
+ virtual bool isSlider() const { return true; }
+ virtual bool isInputSlider() const { return true; }
+
+ virtual const AccessibilityChildrenVector& children();
+ virtual void addChildren();
+
+ virtual bool canSetValueAttribute() const { return true; }
+ const AtomicString& getAttribute(const QualifiedName& attribute) const;
+
+ virtual void setValue(const String&);
+ virtual float valueForRange() const;
+ virtual float maxValueForRange() const;
+ virtual float minValueForRange() const;
+ virtual AccessibilityOrientation orientation() const;
+};
+
+class AccessibilitySliderThumb : public AccessibilityObject {
+
+public:
+ static PassRefPtr<AccessibilitySliderThumb> create();
+ virtual ~AccessibilitySliderThumb() { }
+
+ virtual AccessibilityRole roleValue() const { return SliderThumbRole; }
+
+ void setParentObject(AccessibilitySlider* slider) { m_parentSlider = slider; }
+ virtual AccessibilityObject* parentObject() const { return m_parentSlider; }
+
+ virtual IntSize size() const;
+ virtual IntRect elementRect() const;
+
+private:
+ AccessibilitySliderThumb();
+ virtual bool accessibilityIsIgnored() const { return false; }
+
+ AccessibilitySlider* m_parentSlider;
+};
+
+
+} // namespace WebCore
+
+#endif // AccessibilitySlider_h
diff --git a/Source/WebCore/accessibility/AccessibilityTable.cpp b/Source/WebCore/accessibility/AccessibilityTable.cpp
new file mode 100644
index 0000000..aa51f9e
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityTable.cpp
@@ -0,0 +1,567 @@
+/*
+ * Copyright (C) 2008 Apple 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 "AccessibilityTable.h"
+
+#include "AXObjectCache.h"
+#include "AccessibilityTableCell.h"
+#include "AccessibilityTableColumn.h"
+#include "AccessibilityTableHeaderContainer.h"
+#include "AccessibilityTableRow.h"
+#include "HTMLNames.h"
+#include "HTMLTableCaptionElement.h"
+#include "HTMLTableCellElement.h"
+#include "HTMLTableElement.h"
+#include "RenderObject.h"
+#include "RenderTable.h"
+#include "RenderTableCell.h"
+#include "RenderTableSection.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+AccessibilityTable::AccessibilityTable(RenderObject* renderer)
+ : AccessibilityRenderObject(renderer),
+ m_headerContainer(0)
+{
+#if ACCESSIBILITY_TABLES
+ m_isAccessibilityTable = isTableExposableThroughAccessibility();
+#else
+ m_isAccessibilityTable = false;
+#endif
+}
+
+AccessibilityTable::~AccessibilityTable()
+{
+}
+
+PassRefPtr<AccessibilityTable> AccessibilityTable::create(RenderObject* renderer)
+{
+ return adoptRef(new AccessibilityTable(renderer));
+}
+
+bool AccessibilityTable::hasARIARole() const
+{
+ if (!m_renderer)
+ return false;
+
+ AccessibilityRole ariaRole = ariaRoleAttribute();
+ if (ariaRole != UnknownRole)
+ return true;
+
+ return false;
+}
+
+bool AccessibilityTable::isAccessibilityTable() const
+{
+ if (!m_renderer)
+ return false;
+
+ return m_isAccessibilityTable;
+}
+
+bool AccessibilityTable::isDataTable() const
+{
+ if (!m_renderer)
+ return false;
+
+ // Do not consider it a data table is it has an ARIA role.
+ if (hasARIARole())
+ return false;
+
+ // This employs a heuristic to determine if this table should appear.
+ // Only "data" tables should be exposed as tables.
+ // Unfortunately, there is no good way to determine the difference
+ // between a "layout" table and a "data" table.
+
+ RenderTable* table = toRenderTable(m_renderer);
+ Node* tableNode = table->node();
+ if (!tableNode || !tableNode->hasTagName(tableTag))
+ return false;
+
+ // if there is a caption element, summary, THEAD, or TFOOT section, it's most certainly a data table
+ HTMLTableElement* tableElement = static_cast<HTMLTableElement*>(tableNode);
+ if (!tableElement->summary().isEmpty() || tableElement->tHead() || tableElement->tFoot() || tableElement->caption())
+ return true;
+
+ // if someone used "rules" attribute than the table should appear
+ if (!tableElement->rules().isEmpty())
+ return true;
+
+ // 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
+ RenderTableSection* firstBody = table->firstBody();
+ if (!firstBody)
+ return false;
+
+ int numCols = firstBody->numColumns();
+ int numRows = firstBody->numRows();
+
+ // if there's only one cell, it's not a good AXTable candidate
+ if (numRows == 1 && numCols == 1)
+ return false;
+
+ // store the background color of the table to check against cell's background colors
+ RenderStyle* tableStyle = table->style();
+ if (!tableStyle)
+ return false;
+ Color tableBGColor = tableStyle->visitedDependentColor(CSSPropertyBackgroundColor);
+
+ // check enough of the cells to find if the table matches our criteria
+ // Criteria:
+ // 1) must have at least one valid cell (and)
+ // 2) at least half of cells have borders (or)
+ // 3) at least half of cells have different bg colors than the table, and there is cell spacing
+ unsigned validCellCount = 0;
+ unsigned borderedCellCount = 0;
+ unsigned backgroundDifferenceCellCount = 0;
+
+ Color alternatingRowColors[5];
+ int alternatingRowColorCount = 0;
+
+ int headersInFirstColumnCount = 0;
+ for (int row = 0; row < numRows; ++row) {
+
+ int headersInFirstRowCount = 0;
+ for (int col = 0; col < numCols; ++col) {
+ RenderTableCell* cell = firstBody->primaryCellAt(row, col);
+ if (!cell)
+ continue;
+ Node* cellNode = cell->node();
+ if (!cellNode)
+ continue;
+
+ if (cell->width() < 1 || cell->height() < 1)
+ continue;
+
+ validCellCount++;
+
+ HTMLTableCellElement* cellElement = static_cast<HTMLTableCellElement*>(cellNode);
+
+ bool isTHCell = cellElement->hasTagName(thTag);
+ // If the first row is comprised of all <th> tags, assume it is a data table.
+ if (!row && isTHCell)
+ headersInFirstRowCount++;
+
+ // If the first column is comprised of all <tg> tags, assume it is a data table.
+ if (!col && isTHCell)
+ headersInFirstColumnCount++;
+
+ // in this case, the developer explicitly assigned a "data" table attribute
+ if (!cellElement->headers().isEmpty() || !cellElement->abbr().isEmpty()
+ || !cellElement->axis().isEmpty() || !cellElement->scope().isEmpty())
+ return true;
+
+ RenderStyle* renderStyle = cell->style();
+ if (!renderStyle)
+ continue;
+
+ // a cell needs to have matching bordered sides, before it can be considered a bordered cell.
+ if ((cell->borderTop() > 0 && cell->borderBottom() > 0)
+ || (cell->borderLeft() > 0 && cell->borderRight() > 0))
+ borderedCellCount++;
+
+ // if the cell has a different color from the table and there is cell spacing,
+ // then it is probably a data table cell (spacing and colors take the place of borders)
+ Color cellColor = renderStyle->visitedDependentColor(CSSPropertyBackgroundColor);
+ if (table->hBorderSpacing() > 0 && table->vBorderSpacing() > 0
+ && tableBGColor != cellColor && cellColor.alpha() != 1)
+ backgroundDifferenceCellCount++;
+
+ // if we've found 10 "good" cells, we don't need to keep searching
+ if (borderedCellCount >= 10 || backgroundDifferenceCellCount >= 10)
+ return true;
+
+ // For the first 5 rows, cache the background color so we can check if this table has zebra-striped rows.
+ if (row < 5 && row == alternatingRowColorCount) {
+ RenderObject* renderRow = cell->parent();
+ if (!renderRow || !renderRow->isBoxModelObject() || !toRenderBoxModelObject(renderRow)->isTableRow())
+ continue;
+ RenderStyle* rowRenderStyle = renderRow->style();
+ if (!rowRenderStyle)
+ continue;
+ Color rowColor = rowRenderStyle->visitedDependentColor(CSSPropertyBackgroundColor);
+ alternatingRowColors[alternatingRowColorCount] = rowColor;
+ alternatingRowColorCount++;
+ }
+ }
+
+ if (!row && headersInFirstRowCount == numCols && numCols > 1)
+ return true;
+ }
+
+ if (headersInFirstColumnCount == numRows && numRows > 1)
+ return true;
+
+ // if there is less than two valid cells, it's not a data table
+ if (validCellCount <= 1)
+ return false;
+
+ // half of the cells had borders, it's a data table
+ unsigned neededCellCount = validCellCount / 2;
+ if (borderedCellCount >= neededCellCount)
+ return true;
+
+ // half had different background colors, it's a data table
+ if (backgroundDifferenceCellCount >= neededCellCount)
+ return true;
+
+ // Check if there is an alternating row background color indicating a zebra striped style pattern.
+ if (alternatingRowColorCount > 2) {
+ Color firstColor = alternatingRowColors[0];
+ for (int k = 1; k < alternatingRowColorCount; k++) {
+ // If an odd row was the same color as the first row, its not alternating.
+ if (k % 2 == 1 && alternatingRowColors[k] == firstColor)
+ return false;
+ // If an even row is not the same as the first row, its not alternating.
+ if (!(k % 2) && alternatingRowColors[k] != firstColor)
+ return false;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool AccessibilityTable::isTableExposableThroughAccessibility() const
+{
+ // The following is a heuristic used to determine if a
+ // <table> should be exposed as an AXTable. The goal
+ // is to only show "data" tables.
+
+ if (!m_renderer)
+ return false;
+
+ // If the developer assigned an aria role to this, then we
+ // shouldn't expose it as a table, unless, of course, the aria
+ // role is a table.
+ if (hasARIARole())
+ return false;
+
+ // Gtk+ ATs expect all tables to be exposed as tables.
+#if PLATFORM(GTK)
+ Node* tableNode = toRenderTable(m_renderer)->node();
+ return tableNode && tableNode->hasTagName(tableTag);
+#endif
+
+ return isDataTable();
+}
+
+void AccessibilityTable::clearChildren()
+{
+ AccessibilityRenderObject::clearChildren();
+ m_rows.clear();
+ m_columns.clear();
+}
+
+void AccessibilityTable::addChildren()
+{
+ if (!isAccessibilityTable()) {
+ AccessibilityRenderObject::addChildren();
+ return;
+ }
+
+ ASSERT(!m_haveChildren);
+
+ m_haveChildren = true;
+ if (!m_renderer || !m_renderer->isTable())
+ return;
+
+ RenderTable* table = toRenderTable(m_renderer);
+ AXObjectCache* axCache = m_renderer->document()->axObjectCache();
+
+ // go through all the available sections to pull out the rows
+ // and add them as children
+ RenderTableSection* tableSection = table->header();
+ if (!tableSection)
+ tableSection = table->firstBody();
+
+ if (!tableSection)
+ return;
+
+ RenderTableSection* initialTableSection = tableSection;
+
+ while (tableSection) {
+
+ HashSet<AccessibilityObject*> appendedRows;
+
+ unsigned numRows = tableSection->numRows();
+ unsigned numCols = tableSection->numColumns();
+ for (unsigned rowIndex = 0; rowIndex < numRows; ++rowIndex) {
+ for (unsigned colIndex = 0; colIndex < numCols; ++colIndex) {
+
+ RenderTableCell* cell = tableSection->primaryCellAt(rowIndex, colIndex);
+ if (!cell)
+ continue;
+
+ AccessibilityObject* rowObject = axCache->getOrCreate(cell->parent());
+ if (!rowObject->isTableRow())
+ continue;
+
+ AccessibilityTableRow* row = static_cast<AccessibilityTableRow*>(rowObject);
+ // we need to check every cell for a new row, because cell spans
+ // can cause us to mess rows if we just check the first column
+ if (appendedRows.contains(row))
+ continue;
+
+ row->setRowIndex((int)m_rows.size());
+ m_rows.append(row);
+ if (!row->accessibilityIsIgnored())
+ m_children.append(row);
+#if PLATFORM(GTK)
+ else
+ m_children.append(row->children());
+#endif
+ appendedRows.add(row);
+ }
+ }
+
+ tableSection = table->sectionBelow(tableSection, true);
+ }
+
+ // make the columns based on the number of columns in the first body
+ unsigned length = initialTableSection->numColumns();
+ for (unsigned i = 0; i < length; ++i) {
+ AccessibilityTableColumn* column = static_cast<AccessibilityTableColumn*>(axCache->getOrCreate(ColumnRole));
+ column->setColumnIndex((int)i);
+ column->setParentTable(this);
+ m_columns.append(column);
+ if (!column->accessibilityIsIgnored())
+ m_children.append(column);
+ }
+
+ AccessibilityObject* headerContainerObject = headerContainer();
+ if (headerContainerObject && !headerContainerObject->accessibilityIsIgnored())
+ m_children.append(headerContainerObject);
+}
+
+AccessibilityObject* AccessibilityTable::headerContainer()
+{
+ if (m_headerContainer)
+ return m_headerContainer;
+
+ m_headerContainer = static_cast<AccessibilityTableHeaderContainer*>(axObjectCache()->getOrCreate(TableHeaderContainerRole));
+ m_headerContainer->setParentTable(this);
+
+ return m_headerContainer;
+}
+
+AccessibilityObject::AccessibilityChildrenVector& AccessibilityTable::columns()
+{
+ updateChildrenIfNecessary();
+
+ return m_columns;
+}
+
+AccessibilityObject::AccessibilityChildrenVector& AccessibilityTable::rows()
+{
+ updateChildrenIfNecessary();
+
+ return m_rows;
+}
+
+void AccessibilityTable::rowHeaders(AccessibilityChildrenVector& headers)
+{
+ if (!m_renderer)
+ return;
+
+ updateChildrenIfNecessary();
+
+ unsigned rowCount = m_rows.size();
+ for (unsigned k = 0; k < rowCount; ++k) {
+ AccessibilityObject* header = static_cast<AccessibilityTableRow*>(m_rows[k].get())->headerObject();
+ if (!header)
+ continue;
+ headers.append(header);
+ }
+}
+
+void AccessibilityTable::columnHeaders(AccessibilityChildrenVector& headers)
+{
+ if (!m_renderer)
+ return;
+
+ updateChildrenIfNecessary();
+
+ unsigned colCount = m_columns.size();
+ for (unsigned k = 0; k < colCount; ++k) {
+ AccessibilityObject* header = static_cast<AccessibilityTableColumn*>(m_columns[k].get())->headerObject();
+ if (!header)
+ continue;
+ headers.append(header);
+ }
+}
+
+void AccessibilityTable::cells(AccessibilityObject::AccessibilityChildrenVector& cells)
+{
+ if (!m_renderer)
+ return;
+
+ updateChildrenIfNecessary();
+
+ int numRows = m_rows.size();
+ for (int row = 0; row < numRows; ++row) {
+ AccessibilityChildrenVector rowChildren = m_rows[row]->children();
+ cells.append(rowChildren);
+ }
+}
+
+unsigned AccessibilityTable::columnCount()
+{
+ updateChildrenIfNecessary();
+
+ return m_columns.size();
+}
+
+unsigned AccessibilityTable::rowCount()
+{
+ updateChildrenIfNecessary();
+
+ return m_rows.size();
+}
+
+AccessibilityTableCell* AccessibilityTable::cellForColumnAndRow(unsigned column, unsigned row)
+{
+ if (!m_renderer || !m_renderer->isTable())
+ return 0;
+
+ updateChildrenIfNecessary();
+
+ RenderTable* table = toRenderTable(m_renderer);
+ RenderTableSection* tableSection = table->header();
+ if (!tableSection)
+ tableSection = table->firstBody();
+
+ RenderTableCell* cell = 0;
+ unsigned rowCount = 0;
+ unsigned rowOffset = 0;
+ while (tableSection) {
+
+ unsigned numRows = tableSection->numRows();
+ unsigned numCols = tableSection->numColumns();
+
+ rowCount += numRows;
+
+ unsigned sectionSpecificRow = row - rowOffset;
+ if (row < rowCount && column < numCols && sectionSpecificRow < numRows) {
+ cell = tableSection->primaryCellAt(sectionSpecificRow, column);
+
+ // we didn't find the cell, which means there's spanning happening
+ // search backwards to find the spanning cell
+ if (!cell) {
+
+ // first try rows
+ for (int testRow = sectionSpecificRow-1; testRow >= 0; --testRow) {
+ cell = tableSection->primaryCellAt(testRow, column);
+ // cell overlapped. use this one
+ if (cell && ((cell->row() + (cell->rowSpan()-1)) >= (int)sectionSpecificRow))
+ break;
+ cell = 0;
+ }
+
+ if (!cell) {
+ // try cols
+ for (int testCol = column-1; testCol >= 0; --testCol) {
+ cell = tableSection->primaryCellAt(sectionSpecificRow, testCol);
+ // cell overlapped. use this one
+ if (cell && ((cell->col() + (cell->colSpan()-1)) >= (int)column))
+ break;
+ cell = 0;
+ }
+ }
+ }
+ }
+
+ if (cell)
+ break;
+
+ rowOffset += numRows;
+ // we didn't find anything between the rows we should have
+ if (row < rowCount)
+ break;
+ tableSection = table->sectionBelow(tableSection, true);
+ }
+
+ if (!cell)
+ return 0;
+
+ AccessibilityObject* cellObject = axObjectCache()->getOrCreate(cell);
+ ASSERT(cellObject->isTableCell());
+
+ return static_cast<AccessibilityTableCell*>(cellObject);
+}
+
+AccessibilityRole AccessibilityTable::roleValue() const
+{
+ if (!isAccessibilityTable())
+ return AccessibilityRenderObject::roleValue();
+
+ return TableRole;
+}
+
+bool AccessibilityTable::accessibilityIsIgnored() const
+{
+ AccessibilityObjectInclusion decision = accessibilityIsIgnoredBase();
+ if (decision == IncludeObject)
+ return false;
+ if (decision == IgnoreObject)
+ return true;
+
+ if (!isAccessibilityTable())
+ return AccessibilityRenderObject::accessibilityIsIgnored();
+
+ return false;
+}
+
+String AccessibilityTable::title() const
+{
+ if (!isAccessibilityTable())
+ return AccessibilityRenderObject::title();
+
+ String title;
+ if (!m_renderer)
+ return title;
+
+ // see if there is a caption
+ Node* tableElement = m_renderer->node();
+ if (tableElement && tableElement->hasTagName(tableTag)) {
+ HTMLTableCaptionElement* caption = static_cast<HTMLTableElement*>(tableElement)->caption();
+ if (caption)
+ title = caption->innerText();
+ }
+
+ // try the standard
+ if (title.isEmpty())
+ title = AccessibilityRenderObject::title();
+
+ return title;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityTable.h b/Source/WebCore/accessibility/AccessibilityTable.h
new file mode 100644
index 0000000..1666367
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityTable.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008 Apple 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 AccessibilityTable_h
+#define AccessibilityTable_h
+
+#include "AccessibilityRenderObject.h"
+#include <wtf/Forward.h>
+
+#if PLATFORM(MAC) && (defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD))
+#define ACCESSIBILITY_TABLES 0
+#else
+#define ACCESSIBILITY_TABLES 1
+#endif
+
+namespace WebCore {
+
+class AccessibilityTableCell;
+class AccessibilityTableHeaderContainer;
+
+class AccessibilityTable : public AccessibilityRenderObject {
+
+protected:
+ AccessibilityTable(RenderObject*);
+public:
+ static PassRefPtr<AccessibilityTable> create(RenderObject*);
+ virtual ~AccessibilityTable();
+
+ virtual bool isAccessibilityTable() const;
+ virtual bool isDataTable() const;
+
+ virtual AccessibilityRole roleValue() const;
+ virtual bool isAriaTable() const { return false; }
+
+ virtual bool accessibilityIsIgnored() const;
+
+ virtual void addChildren();
+ virtual void clearChildren();
+
+ AccessibilityChildrenVector& columns();
+ AccessibilityChildrenVector& rows();
+
+ virtual bool supportsSelectedRows() { return false; }
+ unsigned columnCount();
+ unsigned rowCount();
+
+ virtual String title() const;
+
+ // all the cells in the table
+ void cells(AccessibilityChildrenVector&);
+ virtual AccessibilityTableCell* cellForColumnAndRow(unsigned column, unsigned row);
+
+ void columnHeaders(AccessibilityChildrenVector&);
+ void rowHeaders(AccessibilityChildrenVector&);
+
+ // an object that contains, as children, all the objects that act as headers
+ AccessibilityObject* headerContainer();
+
+protected:
+ AccessibilityChildrenVector m_rows;
+ AccessibilityChildrenVector m_columns;
+
+ AccessibilityTableHeaderContainer* m_headerContainer;
+ mutable bool m_isAccessibilityTable;
+
+ bool hasARIARole() const;
+ bool isTableExposableThroughAccessibility() const;
+};
+
+} // namespace WebCore
+
+#endif // AccessibilityTable_h
diff --git a/Source/WebCore/accessibility/AccessibilityTableCell.cpp b/Source/WebCore/accessibility/AccessibilityTableCell.cpp
new file mode 100644
index 0000000..7ad6063
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityTableCell.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2008 Apple 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 "AccessibilityTableCell.h"
+
+#include "AXObjectCache.h"
+#include "HTMLNames.h"
+#include "RenderObject.h"
+#include "RenderTableCell.h"
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+AccessibilityTableCell::AccessibilityTableCell(RenderObject* renderer)
+ : AccessibilityRenderObject(renderer)
+{
+}
+
+AccessibilityTableCell::~AccessibilityTableCell()
+{
+}
+
+PassRefPtr<AccessibilityTableCell> AccessibilityTableCell::create(RenderObject* renderer)
+{
+ return adoptRef(new AccessibilityTableCell(renderer));
+}
+
+bool AccessibilityTableCell::accessibilityIsIgnored() const
+{
+ AccessibilityObjectInclusion decision = accessibilityIsIgnoredBase();
+ if (decision == IncludeObject)
+ return false;
+ if (decision == IgnoreObject)
+ return true;
+
+ if (!isTableCell())
+ return AccessibilityRenderObject::accessibilityIsIgnored();
+
+ return false;
+}
+
+AccessibilityObject* AccessibilityTableCell::parentTable() const
+{
+ if (!m_renderer || !m_renderer->isTableCell())
+ return 0;
+
+ // Do not use getOrCreate. parentTable() can be called while the render tree is being modified
+ // by javascript, and creating a table element may try to access the render tree while in a bad state.
+ // By using only get() implies that the AXTable must be created before AXTableCells. This should
+ // always be the case when AT clients access a table.
+ // https://bugs.webkit.org/show_bug.cgi?id=42652
+ return axObjectCache()->get(toRenderTableCell(m_renderer)->table());
+}
+
+bool AccessibilityTableCell::isTableCell() const
+{
+ AccessibilityObject* table = parentTable();
+ if (!table || !table->isAccessibilityTable())
+ return false;
+
+ return true;
+}
+
+AccessibilityRole AccessibilityTableCell::roleValue() const
+{
+ if (!isTableCell())
+ return AccessibilityRenderObject::roleValue();
+
+ return CellRole;
+}
+
+void AccessibilityTableCell::rowIndexRange(pair<int, int>& rowRange)
+{
+ if (!m_renderer || !m_renderer->isTableCell())
+ return;
+
+ RenderTableCell* renderCell = toRenderTableCell(m_renderer);
+ rowRange.first = renderCell->row();
+ rowRange.second = renderCell->rowSpan();
+
+ // since our table might have multiple sections, we have to offset our row appropriately
+ RenderTableSection* section = renderCell->section();
+ RenderTable* table = renderCell->table();
+ if (!table || !section)
+ return;
+
+ RenderTableSection* tableSection = table->header();
+ if (!tableSection)
+ tableSection = table->firstBody();
+
+ unsigned rowOffset = 0;
+ while (tableSection) {
+ if (tableSection == section)
+ break;
+ rowOffset += tableSection->numRows();
+ tableSection = table->sectionBelow(tableSection, true);
+ }
+
+ rowRange.first += rowOffset;
+}
+
+void AccessibilityTableCell::columnIndexRange(pair<int, int>& columnRange)
+{
+ if (!m_renderer || !m_renderer->isTableCell())
+ return;
+
+ RenderTableCell* renderCell = toRenderTableCell(m_renderer);
+ columnRange.first = renderCell->col();
+ columnRange.second = renderCell->colSpan();
+}
+
+AccessibilityObject* AccessibilityTableCell::titleUIElement() const
+{
+ // Try to find if the first cell in this row is a <th>. If it is,
+ // then it can act as the title ui element. (This is only in the
+ // case when the table is not appearing as an AXTable.)
+ if (isTableCell() || !m_renderer || !m_renderer->isTableCell())
+ return 0;
+
+ // Table cells that are th cannot have title ui elements, since by definition
+ // they are title ui elements
+ Node* node = m_renderer->node();
+ if (node && node->hasTagName(thTag))
+ return 0;
+
+ RenderTableCell* renderCell = toRenderTableCell(m_renderer);
+
+ // If this cell is in the first column, there is no need to continue.
+ int col = renderCell->col();
+ if (!col)
+ return 0;
+
+ int row = renderCell->row();
+
+ RenderTableSection* section = renderCell->section();
+ if (!section)
+ return 0;
+
+ RenderTableCell* headerCell = section->primaryCellAt(row, 0);
+ if (!headerCell || headerCell == renderCell)
+ return 0;
+
+ Node* cellElement = headerCell->node();
+ if (!cellElement || !cellElement->hasTagName(thTag))
+ return 0;
+
+ return axObjectCache()->getOrCreate(headerCell);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityTableCell.h b/Source/WebCore/accessibility/AccessibilityTableCell.h
new file mode 100644
index 0000000..dabbce2
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityTableCell.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008 Apple 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 AccessibilityTableCell_h
+#define AccessibilityTableCell_h
+
+#include "AccessibilityRenderObject.h"
+
+namespace WebCore {
+
+class AccessibilityTableCell : public AccessibilityRenderObject {
+
+protected:
+ AccessibilityTableCell(RenderObject*);
+public:
+ static PassRefPtr<AccessibilityTableCell> create(RenderObject*);
+ virtual ~AccessibilityTableCell();
+
+ virtual bool isTableCell() const;
+ virtual AccessibilityRole roleValue() const;
+
+ virtual bool accessibilityIsIgnored() const;
+
+ // fills in the start location and row span of cell
+ virtual void rowIndexRange(pair<int, int>& rowRange);
+ // fills in the start location and column span of cell
+ virtual void columnIndexRange(pair<int, int>& columnRange);
+
+ // if a table cell is not exposed as a table cell, a TH element can
+ // serve as its title ui element
+ AccessibilityObject* titleUIElement() const;
+
+protected:
+ virtual AccessibilityObject* parentTable() const;
+ int m_rowIndex;
+};
+
+} // namespace WebCore
+
+#endif // AccessibilityTableCell_h
diff --git a/Source/WebCore/accessibility/AccessibilityTableColumn.cpp b/Source/WebCore/accessibility/AccessibilityTableColumn.cpp
new file mode 100644
index 0000000..f776e40
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityTableColumn.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2008 Apple 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 "AccessibilityTableColumn.h"
+
+#include "AXObjectCache.h"
+#include "AccessibilityTableCell.h"
+#include "HTMLNames.h"
+#include "RenderTable.h"
+#include "RenderTableCell.h"
+#include "RenderTableSection.h"
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+AccessibilityTableColumn::AccessibilityTableColumn()
+ : m_parentTable(0)
+{
+}
+
+AccessibilityTableColumn::~AccessibilityTableColumn()
+{
+}
+
+PassRefPtr<AccessibilityTableColumn> AccessibilityTableColumn::create()
+{
+ return adoptRef(new AccessibilityTableColumn());
+}
+
+void AccessibilityTableColumn::setParentTable(AccessibilityTable* table)
+{
+ m_parentTable = table;
+
+ clearChildren();
+}
+
+IntRect AccessibilityTableColumn::elementRect() const
+{
+ // this will be filled in when addChildren is called
+ return m_columnRect;
+}
+
+IntSize AccessibilityTableColumn::size() const
+{
+ return elementRect().size();
+}
+
+const AccessibilityObject::AccessibilityChildrenVector& AccessibilityTableColumn::children()
+{
+ if (!m_haveChildren)
+ addChildren();
+ return m_children;
+}
+
+AccessibilityObject* AccessibilityTableColumn::headerObject()
+{
+ if (!m_parentTable)
+ return 0;
+
+ RenderObject* renderer = m_parentTable->renderer();
+ if (!renderer)
+ return 0;
+
+ if (m_parentTable->isAriaTable()) {
+ AccessibilityChildrenVector rowChildren = children();
+ unsigned childrenCount = rowChildren.size();
+ for (unsigned i = 0; i < childrenCount; ++i) {
+ AccessibilityObject* cell = rowChildren[i].get();
+ if (cell->ariaRoleAttribute() == ColumnHeaderRole)
+ return cell;
+ }
+
+ return 0;
+ }
+
+ if (!renderer->isTable())
+ return 0;
+
+ RenderTable* table = toRenderTable(renderer);
+
+ AccessibilityObject* headerObject = 0;
+
+ // try the <thead> section first. this doesn't require th tags
+ headerObject = headerObjectForSection(table->header(), false);
+
+ if (headerObject)
+ return headerObject;
+
+ // now try for <th> tags in the first body
+ headerObject = headerObjectForSection(table->firstBody(), true);
+
+ return headerObject;
+}
+
+AccessibilityObject* AccessibilityTableColumn::headerObjectForSection(RenderTableSection* section, bool thTagRequired)
+{
+ if (!section)
+ return 0;
+
+ int numCols = section->numColumns();
+ if (m_columnIndex >= numCols)
+ return 0;
+
+ if (!section->numRows())
+ return 0;
+
+ RenderTableCell* cell = 0;
+ // also account for cells that have a span
+ for (int testCol = m_columnIndex; testCol >= 0; --testCol) {
+ RenderTableCell* testCell = section->primaryCellAt(0, testCol);
+ if (!testCell)
+ continue;
+
+ // we've reached a cell that doesn't even overlap our column
+ // it can't be our header
+ if ((testCell->col() + (testCell->colSpan()-1)) < m_columnIndex)
+ break;
+
+ Node* node = testCell->node();
+ if (!node)
+ continue;
+
+ if (thTagRequired && !node->hasTagName(thTag))
+ continue;
+
+ cell = testCell;
+ }
+
+ if (!cell)
+ return 0;
+
+ return m_parentTable->axObjectCache()->getOrCreate(cell);
+}
+
+bool AccessibilityTableColumn::accessibilityIsIgnored() const
+{
+ if (!m_parentTable)
+ return true;
+
+#if PLATFORM(GTK)
+ return true;
+#endif
+
+ return m_parentTable->accessibilityIsIgnored();
+}
+
+void AccessibilityTableColumn::addChildren()
+{
+ ASSERT(!m_haveChildren);
+
+ m_haveChildren = true;
+ if (!m_parentTable)
+ return;
+
+ int numRows = m_parentTable->rowCount();
+
+ for (int i = 0; i < numRows; i++) {
+ AccessibilityTableCell* cell = m_parentTable->cellForColumnAndRow(m_columnIndex, i);
+ if (!cell)
+ continue;
+
+ // make sure the last one isn't the same as this one (rowspan cells)
+ if (m_children.size() > 0 && m_children.last() == cell)
+ continue;
+
+ m_children.append(cell);
+ m_columnRect.unite(cell->elementRect());
+ }
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityTableColumn.h b/Source/WebCore/accessibility/AccessibilityTableColumn.h
new file mode 100644
index 0000000..15d300c
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityTableColumn.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008 Apple 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 AccessibilityTableColumn_h
+#define AccessibilityTableColumn_h
+
+#include "AccessibilityObject.h"
+#include "AccessibilityTable.h"
+#include "IntRect.h"
+
+namespace WebCore {
+
+class RenderTableSection;
+
+class AccessibilityTableColumn : public AccessibilityObject {
+
+private:
+ AccessibilityTableColumn();
+public:
+ static PassRefPtr<AccessibilityTableColumn> create();
+ virtual ~AccessibilityTableColumn();
+
+ void setParentTable(AccessibilityTable*);
+ virtual AccessibilityObject* parentObject() const { return m_parentTable; }
+ AccessibilityObject* headerObject();
+
+ virtual bool accessibilityIsIgnored() const;
+ virtual AccessibilityRole roleValue() const { return ColumnRole; }
+ virtual bool isTableColumn() const { return true; }
+
+ void setColumnIndex(int columnIndex) { m_columnIndex = columnIndex; }
+ int columnIndex() const { return m_columnIndex; }
+
+ virtual const AccessibilityChildrenVector& children();
+ virtual void addChildren();
+
+ virtual IntSize size() const;
+ virtual IntRect elementRect() const;
+
+private:
+ AccessibilityTable* m_parentTable;
+ int m_columnIndex;
+ IntRect m_columnRect;
+
+ AccessibilityObject* headerObjectForSection(RenderTableSection*, bool thTagRequired);
+};
+
+} // namespace WebCore
+
+#endif // AccessibilityTableColumn_h
diff --git a/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.cpp b/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.cpp
new file mode 100644
index 0000000..dad6963
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2008 Apple 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 "AccessibilityTableHeaderContainer.h"
+
+#include "AXObjectCache.h"
+#include "AccessibilityTable.h"
+
+using namespace std;
+
+namespace WebCore {
+
+AccessibilityTableHeaderContainer::AccessibilityTableHeaderContainer()
+ : m_parentTable(0)
+{
+}
+
+AccessibilityTableHeaderContainer::~AccessibilityTableHeaderContainer()
+{
+}
+
+PassRefPtr<AccessibilityTableHeaderContainer> AccessibilityTableHeaderContainer::create()
+{
+ return adoptRef(new AccessibilityTableHeaderContainer());
+}
+
+const AccessibilityObject::AccessibilityChildrenVector& AccessibilityTableHeaderContainer::children()
+{
+ if (!m_haveChildren)
+ addChildren();
+ return m_children;
+}
+
+IntRect AccessibilityTableHeaderContainer::elementRect() const
+{
+ // this will be filled in when addChildren is called
+ return m_headerRect;
+}
+
+IntSize AccessibilityTableHeaderContainer::size() const
+{
+ return elementRect().size();
+}
+
+bool AccessibilityTableHeaderContainer::accessibilityIsIgnored() const
+{
+ if (!m_parentTable)
+ return true;
+
+#if PLATFORM(GTK)
+ return true;
+#endif
+
+ return m_parentTable->accessibilityIsIgnored();
+}
+
+void AccessibilityTableHeaderContainer::addChildren()
+{
+ ASSERT(!m_haveChildren);
+
+ m_haveChildren = true;
+ if (!m_parentTable || !m_parentTable->isAccessibilityTable())
+ return;
+
+ static_cast<AccessibilityTable*>(m_parentTable)->columnHeaders(m_children);
+
+ unsigned length = m_children.size();
+ for (unsigned k = 0; k < length; ++k)
+ m_headerRect.unite(m_children[k]->elementRect());
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.h b/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.h
new file mode 100644
index 0000000..79521c0
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityTableHeaderContainer.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2008 Apple 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 AccessibilityTableHeaderContainer_h
+#define AccessibilityTableHeaderContainer_h
+
+#include "AccessibilityObject.h"
+#include "AccessibilityTable.h"
+#include "IntRect.h"
+
+namespace WebCore {
+
+class AccessibilityTableHeaderContainer : public AccessibilityObject {
+
+private:
+ AccessibilityTableHeaderContainer();
+public:
+ static PassRefPtr<AccessibilityTableHeaderContainer> create();
+ virtual ~AccessibilityTableHeaderContainer();
+
+ virtual AccessibilityRole roleValue() const { return TableHeaderContainerRole; }
+
+ void setParentTable(AccessibilityTable* table) { m_parentTable = table; }
+ virtual AccessibilityObject* parentObject() const { return m_parentTable; }
+
+ virtual const AccessibilityChildrenVector& children();
+ virtual void addChildren();
+
+ virtual IntSize size() const;
+ virtual IntRect elementRect() const;
+
+private:
+ AccessibilityTable* m_parentTable;
+ IntRect m_headerRect;
+
+ virtual bool accessibilityIsIgnored() const;
+};
+
+} // namespace WebCore
+
+#endif // AccessibilityTableHeaderContainer_h
diff --git a/Source/WebCore/accessibility/AccessibilityTableRow.cpp b/Source/WebCore/accessibility/AccessibilityTableRow.cpp
new file mode 100644
index 0000000..09db132
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityTableRow.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2008 Apple 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 "AccessibilityTableRow.h"
+
+#include "AXObjectCache.h"
+#include "AccessibilityTableCell.h"
+#include "HTMLNames.h"
+#include "HTMLTableRowElement.h"
+#include "RenderObject.h"
+#include "RenderTableCell.h"
+#include "RenderTableRow.h"
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+AccessibilityTableRow::AccessibilityTableRow(RenderObject* renderer)
+ : AccessibilityRenderObject(renderer)
+{
+}
+
+AccessibilityTableRow::~AccessibilityTableRow()
+{
+}
+
+PassRefPtr<AccessibilityTableRow> AccessibilityTableRow::create(RenderObject* renderer)
+{
+ return adoptRef(new AccessibilityTableRow(renderer));
+}
+
+AccessibilityRole AccessibilityTableRow::roleValue() const
+{
+ if (!isTableRow())
+ return AccessibilityRenderObject::roleValue();
+
+ return RowRole;
+}
+
+bool AccessibilityTableRow::isTableRow() const
+{
+ AccessibilityObject* table = parentTable();
+ if (!table || !table->isAccessibilityTable())
+ return false;
+
+ return true;
+}
+
+bool AccessibilityTableRow::accessibilityIsIgnored() const
+{
+ AccessibilityObjectInclusion decision = accessibilityIsIgnoredBase();
+ if (decision == IncludeObject)
+ return false;
+ if (decision == IgnoreObject)
+ return true;
+
+ if (!isTableRow())
+ return AccessibilityRenderObject::accessibilityIsIgnored();
+
+ return false;
+}
+
+AccessibilityObject* AccessibilityTableRow::parentTable() const
+{
+ if (!m_renderer || !m_renderer->isTableRow())
+ return 0;
+
+ // Do not use getOrCreate. parentTable() can be called while the render tree is being modified.
+ return axObjectCache()->get(toRenderTableRow(m_renderer)->table());
+}
+
+AccessibilityObject* AccessibilityTableRow::headerObject()
+{
+ if (!m_renderer || !m_renderer->isTableRow())
+ return 0;
+
+ AccessibilityChildrenVector rowChildren = children();
+ if (!rowChildren.size())
+ return 0;
+
+ // check the first element in the row to see if it is a TH element
+ AccessibilityObject* cell = rowChildren[0].get();
+ if (!cell->isTableCell())
+ return 0;
+
+ RenderObject* cellRenderer = static_cast<AccessibilityTableCell*>(cell)->renderer();
+ if (!cellRenderer)
+ return 0;
+
+ Node* cellNode = cellRenderer->node();
+ if (!cellNode || !cellNode->hasTagName(thTag))
+ return 0;
+
+ return cell;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/AccessibilityTableRow.h b/Source/WebCore/accessibility/AccessibilityTableRow.h
new file mode 100644
index 0000000..29ac935
--- /dev/null
+++ b/Source/WebCore/accessibility/AccessibilityTableRow.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008 Apple 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 AccessibilityTableRow_h
+#define AccessibilityTableRow_h
+
+#include "AccessibilityRenderObject.h"
+
+namespace WebCore {
+
+class AccessibilityTableRow : public AccessibilityRenderObject {
+
+protected:
+ AccessibilityTableRow(RenderObject*);
+public:
+ static PassRefPtr<AccessibilityTableRow> create(RenderObject*);
+ virtual ~AccessibilityTableRow();
+
+ virtual bool isTableRow() const;
+ virtual AccessibilityRole roleValue() const;
+ virtual bool accessibilityIsIgnored() const;
+
+ // retrieves the "row" header (a th tag in the rightmost column)
+ virtual AccessibilityObject* headerObject();
+ virtual AccessibilityObject* parentTable() const;
+
+ void setRowIndex(int rowIndex) { m_rowIndex = rowIndex; }
+ int rowIndex() const { return m_rowIndex; }
+
+ // allows the table to add other children that may not originate
+ // in the row, but their col/row spans overlap into it
+ void appendChild(AccessibilityObject*);
+
+private:
+ int m_rowIndex;
+};
+
+} // namespace WebCore
+
+#endif // AccessibilityTableRow_h
diff --git a/Source/WebCore/accessibility/chromium/AXObjectCacheChromium.cpp b/Source/WebCore/accessibility/chromium/AXObjectCacheChromium.cpp
new file mode 100644
index 0000000..0dddc17
--- /dev/null
+++ b/Source/WebCore/accessibility/chromium/AXObjectCacheChromium.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 "AXObjectCache.h"
+#include "AccessibilityObject.h"
+#include "AccessibilityScrollbar.h"
+#include "Chrome.h"
+#include "ChromeClient.h"
+#include "FrameView.h"
+#include "Scrollbar.h"
+
+namespace WebCore {
+
+void AXObjectCache::detachWrapper(AccessibilityObject* obj)
+{
+ // In Chromium, AccessibilityObjects are wrapped lazily.
+ if (AccessibilityObjectWrapper* wrapper = obj->wrapper())
+ wrapper->detach();
+}
+
+void AXObjectCache::attachWrapper(AccessibilityObject*)
+{
+ // In Chromium, AccessibilityObjects are wrapped lazily.
+}
+
+void AXObjectCache::postPlatformNotification(AccessibilityObject* obj, AXNotification notification)
+{
+ if (obj && obj->isAccessibilityScrollbar() && notification == AXValueChanged) {
+ // Send document value changed on scrollbar value changed notification.
+ Scrollbar* scrollBar = static_cast<AccessibilityScrollbar*>(obj)->scrollbar();
+ if (!scrollBar || !scrollBar->parent() || !scrollBar->parent()->isFrameView())
+ return;
+ Document* document = static_cast<FrameView*>(scrollBar->parent())->frame()->document();
+ if (document != document->topDocument())
+ return;
+ obj = get(document->renderer());
+ }
+
+ if (!obj || !obj->document() || !obj->documentFrameView() || !obj->documentFrameView()->frame() || !obj->documentFrameView()->frame()->page())
+ return;
+
+ ChromeClient* client = obj->documentFrameView()->frame()->page()->chrome()->client();
+ if (!client)
+ return;
+
+ switch (notification) {
+ case AXActiveDescendantChanged:
+ if (!obj->document()->focusedNode() || (obj->node() != obj->document()->focusedNode()))
+ break;
+
+ // Calling handleFocusedUIElementChanged will focus the new active
+ // descendant and send the AXFocusedUIElementChanged notification.
+ handleFocusedUIElementChanged(0, obj->document()->focusedNode()->renderer());
+ break;
+ case AXCheckedStateChanged:
+ case AXChildrenChanged:
+ case AXFocusedUIElementChanged:
+ case AXLayoutComplete:
+ case AXLiveRegionChanged:
+ case AXLoadComplete:
+ case AXMenuListValueChanged:
+ case AXRowCollapsed:
+ case AXRowCountChanged:
+ case AXRowExpanded:
+ case AXScrolledToAnchor:
+ case AXSelectedChildrenChanged:
+ case AXSelectedTextChanged:
+ case AXValueChanged:
+ case AXInvalidStatusChanged:
+ break;
+ }
+
+ client->postAccessibilityNotification(obj, notification);
+}
+
+void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, unsigned)
+{
+}
+
+void AXObjectCache::handleFocusedUIElementChanged(RenderObject*, RenderObject* newFocusedRenderer)
+{
+ if (!newFocusedRenderer)
+ return;
+
+ Page* page = newFocusedRenderer->document()->page();
+ if (!page)
+ return;
+
+ AccessibilityObject* focusedObject = focusedUIElementForPage(page);
+ if (!focusedObject)
+ return;
+
+ postPlatformNotification(focusedObject, AXFocusedUIElementChanged);
+}
+
+void AXObjectCache::handleScrolledToAnchor(const Node* anchorNode)
+{
+ // The anchor node may not be accessible. Post the notification for the
+ // first accessible object.
+ postPlatformNotification(AccessibilityObject::firstAccessibleObjectFromNode(anchorNode), AXScrolledToAnchor);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/chromium/AccessibilityObjectChromium.cpp b/Source/WebCore/accessibility/chromium/AccessibilityObjectChromium.cpp
new file mode 100644
index 0000000..5b4cfd5
--- /dev/null
+++ b/Source/WebCore/accessibility/chromium/AccessibilityObjectChromium.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 "AccessibilityObject.h"
+
+namespace WebCore {
+
+bool AccessibilityObject::accessibilityIgnoreAttachment() const
+{
+ return false;
+}
+
+AccessibilityObjectInclusion AccessibilityObject::accessibilityPlatformIncludesObject() const
+{
+ if (isMenuListPopup() || isMenuListOption())
+ return IgnoreObject;
+
+ return DefaultBehavior;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/chromium/AccessibilityObjectWrapper.h b/Source/WebCore/accessibility/chromium/AccessibilityObjectWrapper.h
new file mode 100644
index 0000000..85a65ed
--- /dev/null
+++ b/Source/WebCore/accessibility/chromium/AccessibilityObjectWrapper.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 AccessibilityObjectWrapper_h
+#define AccessibilityObjectWrapper_h
+
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+ class AccessibilityObject;
+ class AccessibilityObjectWrapper : public RefCounted<AccessibilityObjectWrapper> {
+ public:
+ virtual ~AccessibilityObjectWrapper() {}
+ virtual void detach() = 0;
+ bool attached() const { return m_object; }
+ AccessibilityObject* accessibilityObject() const { return m_object; }
+
+ protected:
+ AccessibilityObjectWrapper(AccessibilityObject* obj)
+ : m_object(obj)
+ {
+ }
+ AccessibilityObjectWrapper() : m_object(0) { }
+
+ AccessibilityObject* m_object;
+ };
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/accessibility/efl/AccessibilityObjectEfl.cpp b/Source/WebCore/accessibility/efl/AccessibilityObjectEfl.cpp
new file mode 100644
index 0000000..d57c3fa
--- /dev/null
+++ b/Source/WebCore/accessibility/efl/AccessibilityObjectEfl.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2008 Apple Ltd.
+ * Copyright (C) 2009-2010 ProFUSION embedded systems
+ * Copyright (C) 2009-2010 Samsung Electronics
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "AccessibilityObject.h"
+
+#if HAVE(ACCESSIBILITY)
+
+namespace WebCore {
+
+bool AccessibilityObject::accessibilityIgnoreAttachment() const
+{
+ return false;
+}
+
+} // namespace WebCore
+
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/gtk/AXObjectCacheAtk.cpp b/Source/WebCore/accessibility/gtk/AXObjectCacheAtk.cpp
new file mode 100644
index 0000000..c341a2d
--- /dev/null
+++ b/Source/WebCore/accessibility/gtk/AXObjectCacheAtk.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2008 Nuanti Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "AXObjectCache.h"
+
+#include "AccessibilityObject.h"
+#include "AccessibilityObjectWrapperAtk.h"
+#include "AccessibilityRenderObject.h"
+#include "GOwnPtr.h"
+#include "Range.h"
+#include "SelectElement.h"
+#include "TextIterator.h"
+
+namespace WebCore {
+
+void AXObjectCache::detachWrapper(AccessibilityObject* obj)
+{
+ webkit_accessible_detach(WEBKIT_ACCESSIBLE(obj->wrapper()));
+}
+
+void AXObjectCache::attachWrapper(AccessibilityObject* obj)
+{
+ AtkObject* atkObj = ATK_OBJECT(webkit_accessible_new(obj));
+ obj->setWrapper(atkObj);
+ g_object_unref(atkObj);
+}
+
+static void notifyChildrenSelectionChange(AccessibilityObject* object)
+{
+ // This static variable is needed to keep track of the old focused
+ // object as per previous calls to this function, in order to
+ // properly decide whether to emit some signals or not.
+ static RefPtr<AccessibilityObject> oldFocusedObject = 0;
+
+ // Only list boxes supported so far.
+ if (!object || !object->isListBox())
+ return;
+
+ // Emit signal from the listbox's point of view first.
+ g_signal_emit_by_name(object->wrapper(), "selection-changed");
+
+ // Find the item where the selection change was triggered from.
+ AccessibilityObject::AccessibilityChildrenVector items = object->children();
+ SelectElement* select = toSelectElement(static_cast<Element*>(object->node()));
+ if (!select)
+ return;
+ int changedItemIndex = select->activeSelectionStartListIndex();
+ if (changedItemIndex < 0 || changedItemIndex >= static_cast<int>(items.size()))
+ return;
+ AccessibilityObject* item = items.at(changedItemIndex).get();
+
+ // Ensure the oldFocusedObject belongs to the same document that
+ // the current item so further comparisons make sense. Otherwise,
+ // just reset oldFocusedObject so it won't be taken into account.
+ if (item && oldFocusedObject && item->document() != oldFocusedObject->document())
+ oldFocusedObject = 0;
+
+ AtkObject* axItem = item ? item->wrapper() : 0;
+ AtkObject* axOldFocusedObject = oldFocusedObject ? oldFocusedObject->wrapper() : 0;
+
+ // Old focused object just lost focus, so emit the events.
+ if (axOldFocusedObject && axItem != axOldFocusedObject) {
+ g_signal_emit_by_name(axOldFocusedObject, "focus-event", false);
+ g_signal_emit_by_name(axOldFocusedObject, "state-change", "focused", false);
+ }
+
+ // Emit needed events for the currently (un)selected item.
+ if (axItem) {
+ bool isSelected = item->isSelected();
+ g_signal_emit_by_name(axItem, "state-change", "selected", isSelected);
+ g_signal_emit_by_name(axItem, "focus-event", isSelected);
+ g_signal_emit_by_name(axItem, "state-change", "focused", isSelected);
+ }
+
+ // Update pointer to the previously focused object.
+ oldFocusedObject = item;
+}
+
+void AXObjectCache::postPlatformNotification(AccessibilityObject* coreObject, AXNotification notification)
+{
+ AtkObject* axObject = coreObject->wrapper();
+ if (!axObject)
+ return;
+
+ if (notification == AXCheckedStateChanged) {
+ if (!coreObject->isCheckboxOrRadio())
+ return;
+ g_signal_emit_by_name(axObject, "state-change", "checked", coreObject->isChecked());
+ } else if (notification == AXMenuListValueChanged) {
+ if (!coreObject->isMenuList())
+ return;
+ g_signal_emit_by_name(axObject, "focus-event", true);
+ g_signal_emit_by_name(axObject, "state-change", "focused", true);
+ } else if (notification == AXSelectedChildrenChanged)
+ notifyChildrenSelectionChange(coreObject);
+}
+
+static void emitTextChanged(AccessibilityRenderObject* object, AXObjectCache::AXTextChange textChange, unsigned offset, unsigned count)
+{
+ // Get the axObject for the parent object
+ AtkObject* wrapper = object->parentObjectUnignored()->wrapper();
+ if (!wrapper || !ATK_IS_TEXT(wrapper))
+ return;
+
+ // Select the right signal to be emitted
+ CString detail;
+ switch (textChange) {
+ case AXObjectCache::AXTextInserted:
+ detail = "text-changed::insert";
+ break;
+ case AXObjectCache::AXTextDeleted:
+ detail = "text-changed::delete";
+ break;
+ }
+
+ if (!detail.isNull())
+ g_signal_emit_by_name(wrapper, detail.data(), offset, count);
+}
+
+void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject* object, AXTextChange textChange, unsigned offset, unsigned count)
+{
+ // Sanity check
+ if (count < 1 || !object || !object->isAccessibilityRenderObject())
+ return;
+
+ Node* node = object->node();
+ RefPtr<Range> range = Range::create(node->document(), Position(node->parentNode(), 0), Position(node, 0));
+ emitTextChanged(toAccessibilityRenderObject(object), textChange, offset + TextIterator::rangeLength(range.get()), count);
+}
+
+void AXObjectCache::handleFocusedUIElementChanged(RenderObject* oldFocusedRender, RenderObject* newFocusedRender)
+{
+ RefPtr<AccessibilityObject> oldObject = getOrCreate(oldFocusedRender);
+ if (oldObject) {
+ g_signal_emit_by_name(oldObject->wrapper(), "focus-event", false);
+ g_signal_emit_by_name(oldObject->wrapper(), "state-change", "focused", false);
+ }
+ RefPtr<AccessibilityObject> newObject = getOrCreate(newFocusedRender);
+ if (newObject) {
+ g_signal_emit_by_name(newObject->wrapper(), "focus-event", true);
+ g_signal_emit_by_name(newObject->wrapper(), "state-change", "focused", true);
+ }
+}
+
+void AXObjectCache::handleScrolledToAnchor(const Node*)
+{
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/gtk/AccessibilityObjectAtk.cpp b/Source/WebCore/accessibility/gtk/AccessibilityObjectAtk.cpp
new file mode 100644
index 0000000..9772b43
--- /dev/null
+++ b/Source/WebCore/accessibility/gtk/AccessibilityObjectAtk.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2008 Apple Ltd.
+ * Copyright (C) 2008 Alp Toker <alp@atoker.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "AccessibilityObject.h"
+#include "RenderObject.h"
+#include "RenderText.h"
+
+#include <glib-object.h>
+
+#if HAVE(ACCESSIBILITY)
+
+namespace WebCore {
+
+bool AccessibilityObject::accessibilityIgnoreAttachment() const
+{
+ return false;
+}
+
+AccessibilityObjectInclusion AccessibilityObject::accessibilityPlatformIncludesObject() const
+{
+ AccessibilityObject* parent = parentObject();
+ if (!parent)
+ return DefaultBehavior;
+
+ if (roleValue() == SplitterRole)
+ return IncludeObject;
+
+ if (isGroup()) {
+ // When a list item is made up entirely of children (e.g. paragraphs)
+ // the list item gets ignored. We need it.
+ if (parent->isList())
+ return IncludeObject;
+
+ // We expect the parent of a table cell to be a table.
+ AccessibilityObject* child = firstChild();
+ if (child && child->roleValue() == CellRole)
+ return IgnoreObject;
+ }
+
+ // Entries and password fields have extraneous children which we want to ignore.
+ if (parent->isPasswordField() || parent->isTextControl())
+ return IgnoreObject;
+
+ AccessibilityRole role = roleValue();
+
+ // Include all tables, even layout tables. The AT can decide what to do with each.
+ if (role == CellRole || role == TableRole)
+ return IncludeObject;
+
+ // We at some point might have a need to expose a table row; but it's not standard Gtk+.
+ if (role == RowRole)
+ return IgnoreObject;
+
+ // The object containing the text should implement AtkText itself.
+ if (role == StaticTextRole)
+ return IgnoreObject;
+
+ // Include all list items, regardless they have or not inline children
+ if (role == ListItemRole)
+ return IncludeObject;
+
+ // Bullets/numbers for list items shouldn't be exposed as AtkObjects.
+ if (role == ListMarkerRole)
+ return IgnoreObject;
+
+ return DefaultBehavior;
+}
+
+AccessibilityObjectWrapper* AccessibilityObject::wrapper() const
+{
+ return m_wrapper;
+}
+
+void AccessibilityObject::setWrapper(AccessibilityObjectWrapper* wrapper)
+{
+ if (wrapper == m_wrapper)
+ return;
+
+ if (m_wrapper)
+ g_object_unref(m_wrapper);
+
+ m_wrapper = wrapper;
+
+ if (m_wrapper)
+ g_object_ref(m_wrapper);
+}
+
+bool AccessibilityObject::allowsTextRanges() const
+{
+ return isTextControl() || isWebArea() || isGroup() || isLink() || isHeading();
+}
+
+unsigned AccessibilityObject::getLengthForTextRange() const
+{
+ unsigned textLength = text().length();
+
+ if (textLength)
+ return textLength;
+
+ // Gtk ATs need this for all text objects; not just text controls.
+ Node* node = this->node();
+ RenderObject* renderer = node ? node->renderer() : 0;
+ if (renderer && renderer->isText()) {
+ RenderText* renderText = toRenderText(renderer);
+ textLength = renderText ? renderText->textLength() : 0;
+ }
+
+ // Get the text length from the elements under the
+ // accessibility object if the value is still zero.
+ if (!textLength && allowsTextRanges())
+ textLength = textUnderElement().length();
+
+ return textLength;
+}
+
+} // namespace WebCore
+
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.cpp b/Source/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.cpp
new file mode 100644
index 0000000..7489034
--- /dev/null
+++ b/Source/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.cpp
@@ -0,0 +1,2489 @@
+/*
+ * Copyright (C) 2008 Nuanti Ltd.
+ * Copyright (C) 2009 Igalia S.L.
+ * Copyright (C) 2009 Jan Alonzo
+ *
+ * Portions from Mozilla a11y, copyright as follows:
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Sun Microsystems, Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "AccessibilityObjectWrapperAtk.h"
+
+#if HAVE(ACCESSIBILITY)
+
+#include "AXObjectCache.h"
+#include "AccessibilityList.h"
+#include "AccessibilityListBox.h"
+#include "AccessibilityListBoxOption.h"
+#include "AccessibilityRenderObject.h"
+#include "AccessibilityTable.h"
+#include "AccessibilityTableCell.h"
+#include "AccessibilityTableColumn.h"
+#include "AccessibilityTableRow.h"
+#include "Document.h"
+#include "DocumentType.h"
+#include "Editor.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "GOwnPtr.h"
+#include "HostWindow.h"
+#include "HTMLNames.h"
+#include "HTMLTableCaptionElement.h"
+#include "HTMLTableElement.h"
+#include "InlineTextBox.h"
+#include "IntRect.h"
+#include "NotImplemented.h"
+#include "RenderListItem.h"
+#include "RenderListMarker.h"
+#include "RenderText.h"
+#include "SelectElement.h"
+#include "Settings.h"
+#include "TextEncoding.h"
+#include "TextIterator.h"
+#include "WebKitAccessibleHyperlink.h"
+
+#include <atk/atk.h>
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <libgail-util/gail-util.h>
+#include <pango/pango.h>
+#include <wtf/text/AtomicString.h>
+#include <wtf/text/CString.h>
+
+using namespace WebCore;
+
+static AccessibilityObject* fallbackObject()
+{
+ // FIXME: An AXObjectCache with a Document is meaningless.
+ static AXObjectCache* fallbackCache = new AXObjectCache(0);
+ static AccessibilityObject* object = 0;
+ if (!object) {
+ // FIXME: using fallbackCache->getOrCreate(ListBoxOptionRole) is a hack
+ object = fallbackCache->getOrCreate(ListBoxOptionRole);
+ object->ref();
+ }
+
+ return object;
+}
+
+// Used to provide const char* returns.
+static const char* returnString(const String& str)
+{
+ static CString returnedString;
+ returnedString = str.utf8();
+ return returnedString.data();
+}
+
+static AccessibilityObject* core(WebKitAccessible* accessible)
+{
+ if (!accessible)
+ return 0;
+
+ return accessible->m_object;
+}
+
+static AccessibilityObject* core(AtkObject* object)
+{
+ if (!WEBKIT_IS_ACCESSIBLE(object))
+ return 0;
+
+ return core(WEBKIT_ACCESSIBLE(object));
+}
+
+static AccessibilityObject* core(AtkAction* action)
+{
+ return core(ATK_OBJECT(action));
+}
+
+static AccessibilityObject* core(AtkSelection* selection)
+{
+ return core(ATK_OBJECT(selection));
+}
+
+static AccessibilityObject* core(AtkText* text)
+{
+ return core(ATK_OBJECT(text));
+}
+
+static AccessibilityObject* core(AtkEditableText* text)
+{
+ return core(ATK_OBJECT(text));
+}
+
+static AccessibilityObject* core(AtkComponent* component)
+{
+ return core(ATK_OBJECT(component));
+}
+
+static AccessibilityObject* core(AtkImage* image)
+{
+ return core(ATK_OBJECT(image));
+}
+
+static AccessibilityObject* core(AtkTable* table)
+{
+ return core(ATK_OBJECT(table));
+}
+
+static AccessibilityObject* core(AtkHypertext* hypertext)
+{
+ return core(ATK_OBJECT(hypertext));
+}
+
+static AccessibilityObject* core(AtkDocument* document)
+{
+ return core(ATK_OBJECT(document));
+}
+
+static gchar* webkit_accessible_text_get_text(AtkText* text, gint startOffset, gint endOffset);
+
+static const gchar* webkit_accessible_get_name(AtkObject* object)
+{
+ AccessibilityObject* coreObject = core(object);
+ if (!coreObject->isAccessibilityRenderObject())
+ return returnString(coreObject->stringValue());
+
+ AccessibilityRenderObject* renderObject = static_cast<AccessibilityRenderObject*>(coreObject);
+ if (coreObject->isControl()) {
+ AccessibilityObject* label = renderObject->correspondingLabelForControlElement();
+ if (label) {
+ AtkObject* atkObject = label->wrapper();
+ if (ATK_IS_TEXT(atkObject))
+ return webkit_accessible_text_get_text(ATK_TEXT(atkObject), 0, -1);
+ }
+
+ // Try text under the node
+ String textUnder = renderObject->textUnderElement();
+ if (textUnder.length())
+ return returnString(textUnder);
+ }
+
+ if (renderObject->isImage() || renderObject->isInputImage()) {
+ Node* node = renderObject->renderer()->node();
+ if (node && node->isHTMLElement()) {
+ // Get the attribute rather than altText String so as not to fall back on title.
+ String alt = static_cast<HTMLElement*>(node)->getAttribute(HTMLNames::altAttr);
+ if (!alt.isEmpty())
+ return returnString(alt);
+ }
+ }
+
+ return returnString(coreObject->stringValue());
+}
+
+static const gchar* webkit_accessible_get_description(AtkObject* object)
+{
+ AccessibilityObject* coreObject = core(object);
+ Node* node = 0;
+ if (coreObject->isAccessibilityRenderObject())
+ node = static_cast<AccessibilityRenderObject*>(coreObject)->renderer()->node();
+ if (!node || !node->isHTMLElement() || coreObject->ariaRoleAttribute() != UnknownRole)
+ return returnString(coreObject->accessibilityDescription());
+
+ // atk_table_get_summary returns an AtkObject. We have no summary object, so expose summary here.
+ if (coreObject->roleValue() == TableRole) {
+ String summary = static_cast<HTMLTableElement*>(node)->summary();
+ if (!summary.isEmpty())
+ return returnString(summary);
+ }
+
+ // The title attribute should be reliably available as the object's descripton.
+ // We do not want to fall back on other attributes in its absence. See bug 25524.
+ String title = static_cast<HTMLElement*>(node)->title();
+ if (!title.isEmpty())
+ return returnString(title);
+
+ return returnString(coreObject->accessibilityDescription());
+}
+
+static void setAtkRelationSetFromCoreObject(AccessibilityObject* coreObject, AtkRelationSet* relationSet)
+{
+ AccessibilityRenderObject* accObject = static_cast<AccessibilityRenderObject*>(coreObject);
+ if (accObject->isControl()) {
+ AccessibilityObject* label = accObject->correspondingLabelForControlElement();
+ if (label)
+ atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABELLED_BY, label->wrapper());
+ } else {
+ AccessibilityObject* control = accObject->correspondingControlForLabelElement();
+ if (control)
+ atk_relation_set_add_relation_by_type(relationSet, ATK_RELATION_LABEL_FOR, control->wrapper());
+ }
+}
+
+static gpointer webkit_accessible_parent_class = 0;
+
+static bool isRootObject(AccessibilityObject* coreObject)
+{
+ // The root accessible object in WebCore is always an object with
+ // the ScrolledArea role with one child with the WebArea role.
+ if (!coreObject || !coreObject->isScrollView())
+ return false;
+
+ AccessibilityObject* firstChild = coreObject->firstChild();
+ if (!firstChild || !firstChild->isWebArea())
+ return false;
+
+ return true;
+}
+
+static AtkObject* atkParentOfRootObject(AtkObject* object)
+{
+ AccessibilityObject* coreObject = core(object);
+ AccessibilityObject* coreParent = coreObject->parentObjectUnignored();
+
+ // The top level object claims to not have a parent. This makes it
+ // impossible for assistive technologies to ascend the accessible
+ // hierarchy all the way to the application. (Bug 30489)
+ if (!coreParent && isRootObject(coreObject)) {
+ HostWindow* hostWindow = coreObject->document()->view()->hostWindow();
+ if (hostWindow) {
+ PlatformPageClient scrollView = hostWindow->platformPageClient();
+ if (scrollView) {
+ GtkWidget* scrollViewParent = gtk_widget_get_parent(scrollView);
+ if (scrollViewParent)
+ return gtk_widget_get_accessible(scrollViewParent);
+ }
+ }
+ }
+
+ if (!coreParent)
+ return 0;
+
+ return coreParent->wrapper();
+}
+
+static AtkObject* webkit_accessible_get_parent(AtkObject* object)
+{
+ AccessibilityObject* coreObject = core(object);
+ AccessibilityObject* coreParent = coreObject->parentObjectUnignored();
+ if (!coreParent && isRootObject(coreObject))
+ return atkParentOfRootObject(object);
+
+ if (!coreParent)
+ return 0;
+
+ return coreParent->wrapper();
+}
+
+static gint webkit_accessible_get_n_children(AtkObject* object)
+{
+ return core(object)->children().size();
+}
+
+static AtkObject* webkit_accessible_ref_child(AtkObject* object, gint index)
+{
+ AccessibilityObject* coreObject = core(object);
+ AccessibilityObject::AccessibilityChildrenVector children = coreObject->children();
+ if (index < 0 || static_cast<unsigned>(index) >= children.size())
+ return 0;
+
+ AccessibilityObject* coreChild = children.at(index).get();
+
+ if (!coreChild)
+ return 0;
+
+ AtkObject* child = coreChild->wrapper();
+ atk_object_set_parent(child, object);
+ g_object_ref(child);
+
+ return child;
+}
+
+static gint webkit_accessible_get_index_in_parent(AtkObject* object)
+{
+ AccessibilityObject* coreObject = core(object);
+ AccessibilityObject* parent = coreObject->parentObjectUnignored();
+
+ if (!parent && isRootObject(coreObject)) {
+ AtkObject* atkParent = atkParentOfRootObject(object);
+ if (!atkParent)
+ return -1;
+
+ unsigned count = atk_object_get_n_accessible_children(atkParent);
+ for (unsigned i = 0; i < count; ++i) {
+ AtkObject* child = atk_object_ref_accessible_child(atkParent, i);
+ bool childIsObject = child == object;
+ g_object_unref(child);
+ if (childIsObject)
+ return i;
+ }
+ }
+
+ AccessibilityObject::AccessibilityChildrenVector children = parent->children();
+ unsigned count = children.size();
+ for (unsigned i = 0; i < count; ++i) {
+ if (children[i] == coreObject)
+ return i;
+ }
+
+ return -1;
+}
+
+static AtkAttributeSet* addAttributeToSet(AtkAttributeSet* attributeSet, const char* name, const char* value)
+{
+ AtkAttribute* attribute = static_cast<AtkAttribute*>(g_malloc(sizeof(AtkAttribute)));
+ attribute->name = g_strdup(name);
+ attribute->value = g_strdup(value);
+ attributeSet = g_slist_prepend(attributeSet, attribute);
+
+ return attributeSet;
+}
+
+static AtkAttributeSet* webkit_accessible_get_attributes(AtkObject* object)
+{
+ AtkAttributeSet* attributeSet = 0;
+ attributeSet = addAttributeToSet(attributeSet, "toolkit", "WebKitGtk");
+
+ AccessibilityObject* coreObject = core(object);
+ if (!coreObject)
+ return attributeSet;
+
+ int headingLevel = coreObject->headingLevel();
+ if (headingLevel) {
+ String value = String::number(headingLevel);
+ attributeSet = addAttributeToSet(attributeSet, "level", value.utf8().data());
+ }
+
+ // Set the 'layout-guess' attribute to help Assistive
+ // Technologies know when an exposed table is not data table.
+ if (coreObject->isAccessibilityTable() && !coreObject->isDataTable())
+ attributeSet = addAttributeToSet(attributeSet, "layout-guess", "true");
+
+ return attributeSet;
+}
+
+static AtkRole atkRole(AccessibilityRole role)
+{
+ switch (role) {
+ case UnknownRole:
+ return ATK_ROLE_UNKNOWN;
+ case ButtonRole:
+ return ATK_ROLE_PUSH_BUTTON;
+ case RadioButtonRole:
+ return ATK_ROLE_RADIO_BUTTON;
+ case CheckBoxRole:
+ return ATK_ROLE_CHECK_BOX;
+ case SliderRole:
+ return ATK_ROLE_SLIDER;
+ case TabGroupRole:
+ return ATK_ROLE_PAGE_TAB_LIST;
+ case TextFieldRole:
+ case TextAreaRole:
+ return ATK_ROLE_ENTRY;
+ case StaticTextRole:
+ return ATK_ROLE_TEXT;
+ case OutlineRole:
+ return ATK_ROLE_TREE;
+ case MenuBarRole:
+ return ATK_ROLE_MENU_BAR;
+ case MenuListPopupRole:
+ case MenuRole:
+ return ATK_ROLE_MENU;
+ case MenuListOptionRole:
+ case MenuItemRole:
+ return ATK_ROLE_MENU_ITEM;
+ case ColumnRole:
+ //return ATK_ROLE_TABLE_COLUMN_HEADER; // Is this right?
+ return ATK_ROLE_UNKNOWN; // Matches Mozilla
+ case RowRole:
+ //return ATK_ROLE_TABLE_ROW_HEADER; // Is this right?
+ return ATK_ROLE_LIST_ITEM; // Matches Mozilla
+ case ToolbarRole:
+ return ATK_ROLE_TOOL_BAR;
+ case BusyIndicatorRole:
+ return ATK_ROLE_PROGRESS_BAR; // Is this right?
+ case ProgressIndicatorRole:
+ //return ATK_ROLE_SPIN_BUTTON; // Some confusion about this role in AccessibilityRenderObject.cpp
+ return ATK_ROLE_PROGRESS_BAR;
+ case WindowRole:
+ return ATK_ROLE_WINDOW;
+ case PopUpButtonRole:
+ case ComboBoxRole:
+ return ATK_ROLE_COMBO_BOX;
+ case SplitGroupRole:
+ return ATK_ROLE_SPLIT_PANE;
+ case SplitterRole:
+ return ATK_ROLE_SEPARATOR;
+ case ColorWellRole:
+ return ATK_ROLE_COLOR_CHOOSER;
+ case ListRole:
+ return ATK_ROLE_LIST;
+ case ScrollBarRole:
+ return ATK_ROLE_SCROLL_BAR;
+ case ScrollAreaRole:
+ return ATK_ROLE_SCROLL_PANE;
+ case GridRole: // Is this right?
+ case TableRole:
+ return ATK_ROLE_TABLE;
+ case ApplicationRole:
+ return ATK_ROLE_APPLICATION;
+ case GroupRole:
+ case RadioGroupRole:
+ return ATK_ROLE_PANEL;
+ case CellRole:
+ return ATK_ROLE_TABLE_CELL;
+ case LinkRole:
+ case WebCoreLinkRole:
+ case ImageMapLinkRole:
+ return ATK_ROLE_LINK;
+ case ImageMapRole:
+ case ImageRole:
+ return ATK_ROLE_IMAGE;
+ case ListMarkerRole:
+ return ATK_ROLE_TEXT;
+ case WebAreaRole:
+ //return ATK_ROLE_HTML_CONTAINER; // Is this right?
+ return ATK_ROLE_DOCUMENT_FRAME;
+ case HeadingRole:
+ return ATK_ROLE_HEADING;
+ case ListBoxRole:
+ return ATK_ROLE_LIST;
+ case ListItemRole:
+ case ListBoxOptionRole:
+ return ATK_ROLE_LIST_ITEM;
+ default:
+ return ATK_ROLE_UNKNOWN;
+ }
+}
+
+static AtkRole webkit_accessible_get_role(AtkObject* object)
+{
+ AccessibilityObject* axObject = core(object);
+
+ if (!axObject)
+ return ATK_ROLE_UNKNOWN;
+
+ // WebCore does not know about paragraph role, label role, or section role
+ if (axObject->isAccessibilityRenderObject()) {
+ Node* node = static_cast<AccessibilityRenderObject*>(axObject)->renderer()->node();
+ if (node) {
+ if (node->hasTagName(HTMLNames::pTag))
+ return ATK_ROLE_PARAGRAPH;
+ if (node->hasTagName(HTMLNames::labelTag))
+ return ATK_ROLE_LABEL;
+ if (node->hasTagName(HTMLNames::divTag))
+ return ATK_ROLE_SECTION;
+ if (node->hasTagName(HTMLNames::formTag))
+ return ATK_ROLE_FORM;
+ }
+ }
+
+ // Note: Why doesn't WebCore have a password field for this
+ if (axObject->isPasswordField())
+ return ATK_ROLE_PASSWORD_TEXT;
+
+ return atkRole(axObject->roleValue());
+}
+
+static bool selectionBelongsToObject(AccessibilityObject* coreObject, VisibleSelection& selection)
+{
+ if (!coreObject || !coreObject->isAccessibilityRenderObject())
+ return false;
+
+ if (selection.isNone())
+ return false;
+
+ RefPtr<Range> range = selection.toNormalizedRange();
+ if (!range)
+ return false;
+
+ // We want to check that both the selection intersects the node
+ // AND that the selection is not just "touching" one of the
+ // boundaries for the selected node. We want to check whether the
+ // node is actually inside the region, at least partially.
+ Node* node = coreObject->node();
+ Node* lastDescendant = node->lastDescendant();
+ ExceptionCode ec = 0;
+ return (range->intersectsNode(node, ec)
+ && (range->endContainer() != node || range->endOffset())
+ && (range->startContainer() != lastDescendant || range->startOffset() != lastOffsetInNode(lastDescendant)));
+}
+
+static bool isTextWithCaret(AccessibilityObject* coreObject)
+{
+ if (!coreObject || !coreObject->isAccessibilityRenderObject())
+ return false;
+
+ Document* document = coreObject->document();
+ if (!document)
+ return false;
+
+ Frame* frame = document->frame();
+ if (!frame)
+ return false;
+
+ Settings* settings = frame->settings();
+ if (!settings || !settings->caretBrowsingEnabled())
+ return false;
+
+ // Check text objects and paragraphs only.
+ AtkObject* axObject = coreObject->wrapper();
+ AtkRole role = axObject ? atk_object_get_role(axObject) : ATK_ROLE_INVALID;
+ if (role != ATK_ROLE_TEXT && role != ATK_ROLE_PARAGRAPH)
+ return false;
+
+ // Finally, check whether the caret is set in the current object.
+ VisibleSelection selection = coreObject->selection();
+ if (!selection.isCaret())
+ return false;
+
+ return selectionBelongsToObject(coreObject, selection);
+}
+
+static void setAtkStateSetFromCoreObject(AccessibilityObject* coreObject, AtkStateSet* stateSet)
+{
+ AccessibilityObject* parent = coreObject->parentObject();
+ bool isListBoxOption = parent && parent->isListBox();
+
+ // Please keep the state list in alphabetical order
+ if (coreObject->isChecked())
+ atk_state_set_add_state(stateSet, ATK_STATE_CHECKED);
+
+ // FIXME: isReadOnly does not seem to do the right thing for
+ // controls, so check explicitly for them. In addition, because
+ // isReadOnly is false for listBoxOptions, we need to add one
+ // more check so that we do not present them as being "editable".
+ if ((!coreObject->isReadOnly() ||
+ (coreObject->isControl() && coreObject->canSetValueAttribute())) &&
+ !isListBoxOption)
+ atk_state_set_add_state(stateSet, ATK_STATE_EDITABLE);
+
+ // FIXME: Put both ENABLED and SENSITIVE together here for now
+ if (coreObject->isEnabled()) {
+ atk_state_set_add_state(stateSet, ATK_STATE_ENABLED);
+ atk_state_set_add_state(stateSet, ATK_STATE_SENSITIVE);
+ }
+
+ if (coreObject->canSetExpandedAttribute())
+ atk_state_set_add_state(stateSet, ATK_STATE_EXPANDABLE);
+
+ if (coreObject->isExpanded())
+ atk_state_set_add_state(stateSet, ATK_STATE_EXPANDED);
+
+ if (coreObject->canSetFocusAttribute())
+ atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
+
+ if (coreObject->isFocused() || isTextWithCaret(coreObject))
+ atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
+
+ // TODO: ATK_STATE_HORIZONTAL
+
+ if (coreObject->isIndeterminate())
+ atk_state_set_add_state(stateSet, ATK_STATE_INDETERMINATE);
+
+ if (coreObject->isMultiSelectable())
+ atk_state_set_add_state(stateSet, ATK_STATE_MULTISELECTABLE);
+
+ // TODO: ATK_STATE_OPAQUE
+
+ if (coreObject->isPressed())
+ atk_state_set_add_state(stateSet, ATK_STATE_PRESSED);
+
+ // TODO: ATK_STATE_SELECTABLE_TEXT
+
+ if (coreObject->canSetSelectedAttribute()) {
+ atk_state_set_add_state(stateSet, ATK_STATE_SELECTABLE);
+ // Items in focusable lists in Gtk have both STATE_SELECT{ABLE,ED}
+ // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on the
+ // former.
+ if (isListBoxOption)
+ atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
+ }
+
+ if (coreObject->isSelected()) {
+ atk_state_set_add_state(stateSet, ATK_STATE_SELECTED);
+ // Items in focusable lists in Gtk have both STATE_SELECT{ABLE,ED}
+ // and STATE_FOCUS{ABLE,ED}. We'll fake the latter based on the
+ // former.
+ if (isListBoxOption)
+ atk_state_set_add_state(stateSet, ATK_STATE_FOCUSED);
+ }
+
+ // FIXME: Group both SHOWING and VISIBLE here for now
+ // Not sure how to handle this in WebKit, see bug
+ // http://bugzilla.gnome.org/show_bug.cgi?id=509650 for other
+ // issues with SHOWING vs VISIBLE within GTK+
+ if (!coreObject->isOffScreen()) {
+ atk_state_set_add_state(stateSet, ATK_STATE_SHOWING);
+ atk_state_set_add_state(stateSet, ATK_STATE_VISIBLE);
+ }
+
+ // Mutually exclusive, so we group these two
+ if (coreObject->roleValue() == TextFieldRole)
+ atk_state_set_add_state(stateSet, ATK_STATE_SINGLE_LINE);
+ else if (coreObject->roleValue() == TextAreaRole)
+ atk_state_set_add_state(stateSet, ATK_STATE_MULTI_LINE);
+
+ // TODO: ATK_STATE_SENSITIVE
+
+ // TODO: ATK_STATE_VERTICAL
+
+ if (coreObject->isVisited())
+ atk_state_set_add_state(stateSet, ATK_STATE_VISITED);
+}
+
+static AtkStateSet* webkit_accessible_ref_state_set(AtkObject* object)
+{
+ AtkStateSet* stateSet = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->ref_state_set(object);
+ AccessibilityObject* coreObject = core(object);
+
+ if (coreObject == fallbackObject()) {
+ atk_state_set_add_state(stateSet, ATK_STATE_DEFUNCT);
+ return stateSet;
+ }
+
+ // Text objects must be focusable.
+ AtkRole role = atk_object_get_role(object);
+ if (role == ATK_ROLE_TEXT || role == ATK_ROLE_PARAGRAPH)
+ atk_state_set_add_state(stateSet, ATK_STATE_FOCUSABLE);
+
+ setAtkStateSetFromCoreObject(coreObject, stateSet);
+ return stateSet;
+}
+
+static AtkRelationSet* webkit_accessible_ref_relation_set(AtkObject* object)
+{
+ AtkRelationSet* relationSet = ATK_OBJECT_CLASS(webkit_accessible_parent_class)->ref_relation_set(object);
+ AccessibilityObject* coreObject = core(object);
+
+ setAtkRelationSetFromCoreObject(coreObject, relationSet);
+
+ return relationSet;
+}
+
+static void webkit_accessible_init(AtkObject* object, gpointer data)
+{
+ if (ATK_OBJECT_CLASS(webkit_accessible_parent_class)->initialize)
+ ATK_OBJECT_CLASS(webkit_accessible_parent_class)->initialize(object, data);
+
+ WEBKIT_ACCESSIBLE(object)->m_object = reinterpret_cast<AccessibilityObject*>(data);
+}
+
+static void webkit_accessible_finalize(GObject* object)
+{
+ // This is a good time to clear the return buffer.
+ returnString(String());
+
+ G_OBJECT_CLASS(webkit_accessible_parent_class)->finalize(object);
+}
+
+static void webkit_accessible_class_init(AtkObjectClass* klass)
+{
+ GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
+
+ webkit_accessible_parent_class = g_type_class_peek_parent(klass);
+
+ gobjectClass->finalize = webkit_accessible_finalize;
+
+ klass->initialize = webkit_accessible_init;
+ klass->get_name = webkit_accessible_get_name;
+ klass->get_description = webkit_accessible_get_description;
+ klass->get_parent = webkit_accessible_get_parent;
+ klass->get_n_children = webkit_accessible_get_n_children;
+ klass->ref_child = webkit_accessible_ref_child;
+ klass->get_role = webkit_accessible_get_role;
+ klass->ref_state_set = webkit_accessible_ref_state_set;
+ klass->get_index_in_parent = webkit_accessible_get_index_in_parent;
+ klass->get_attributes = webkit_accessible_get_attributes;
+ klass->ref_relation_set = webkit_accessible_ref_relation_set;
+}
+
+GType
+webkit_accessible_get_type(void)
+{
+ static volatile gsize type_volatile = 0;
+
+ if (g_once_init_enter(&type_volatile)) {
+ static const GTypeInfo tinfo = {
+ sizeof(WebKitAccessibleClass),
+ (GBaseInitFunc) 0,
+ (GBaseFinalizeFunc) 0,
+ (GClassInitFunc) webkit_accessible_class_init,
+ (GClassFinalizeFunc) 0,
+ 0, /* class data */
+ sizeof(WebKitAccessible), /* instance size */
+ 0, /* nb preallocs */
+ (GInstanceInitFunc) 0,
+ 0 /* value table */
+ };
+
+ GType type = g_type_register_static(ATK_TYPE_OBJECT,
+ "WebKitAccessible", &tinfo, GTypeFlags(0));
+ g_once_init_leave(&type_volatile, type);
+ }
+
+ return type_volatile;
+}
+
+static gboolean webkit_accessible_action_do_action(AtkAction* action, gint i)
+{
+ g_return_val_if_fail(i == 0, FALSE);
+ return core(action)->performDefaultAction();
+}
+
+static gint webkit_accessible_action_get_n_actions(AtkAction* action)
+{
+ return 1;
+}
+
+static const gchar* webkit_accessible_action_get_description(AtkAction* action, gint i)
+{
+ g_return_val_if_fail(i == 0, 0);
+ // TODO: Need a way to provide/localize action descriptions.
+ notImplemented();
+ return "";
+}
+
+static const gchar* webkit_accessible_action_get_keybinding(AtkAction* action, gint i)
+{
+ g_return_val_if_fail(i == 0, 0);
+ // FIXME: Construct a proper keybinding string.
+ return returnString(core(action)->accessKey().string());
+}
+
+static const gchar* webkit_accessible_action_get_name(AtkAction* action, gint i)
+{
+ g_return_val_if_fail(i == 0, 0);
+ return returnString(core(action)->actionVerb());
+}
+
+static void atk_action_interface_init(AtkActionIface* iface)
+{
+ iface->do_action = webkit_accessible_action_do_action;
+ iface->get_n_actions = webkit_accessible_action_get_n_actions;
+ iface->get_description = webkit_accessible_action_get_description;
+ iface->get_keybinding = webkit_accessible_action_get_keybinding;
+ iface->get_name = webkit_accessible_action_get_name;
+}
+
+// Selection (for controls)
+
+static AccessibilityObject* listObjectForSelection(AtkSelection* selection)
+{
+ AccessibilityObject* coreSelection = core(selection);
+
+ // Only list boxes and menu lists supported so far.
+ if (!coreSelection->isListBox() && !coreSelection->isMenuList())
+ return 0;
+
+ // For list boxes the list object is just itself.
+ if (coreSelection->isListBox())
+ return coreSelection;
+
+ // For menu lists we need to return the first accessible child,
+ // with role MenuListPopupRole, since that's the one holding the list
+ // of items with role MenuListOptionRole.
+ AccessibilityObject::AccessibilityChildrenVector children = coreSelection->children();
+ if (!children.size())
+ return 0;
+
+ AccessibilityObject* listObject = children.at(0).get();
+ if (!listObject->isMenuListPopup())
+ return 0;
+
+ return listObject;
+}
+
+static AccessibilityObject* optionFromList(AtkSelection* selection, gint i)
+{
+ AccessibilityObject* coreSelection = core(selection);
+ if (!coreSelection || i < 0)
+ return 0;
+
+ // Need to select the proper list object depending on the type.
+ AccessibilityObject* listObject = listObjectForSelection(selection);
+ if (!listObject)
+ return 0;
+
+ AccessibilityRenderObject::AccessibilityChildrenVector options = listObject->children();
+ if (i < static_cast<gint>(options.size()))
+ return options.at(i).get();
+
+ return 0;
+}
+
+static AccessibilityObject* optionFromSelection(AtkSelection* selection, gint i)
+{
+ // i is the ith selection as opposed to the ith child.
+
+ AccessibilityObject* coreSelection = core(selection);
+ if (!coreSelection || !coreSelection->isAccessibilityRenderObject() || i < 0)
+ return 0;
+
+ AccessibilityRenderObject::AccessibilityChildrenVector selectedItems;
+ if (coreSelection->isListBox())
+ coreSelection->selectedChildren(selectedItems);
+ else if (coreSelection->isMenuList()) {
+ RenderObject* renderer = toAccessibilityRenderObject(coreSelection)->renderer();
+ if (!renderer)
+ return 0;
+
+ SelectElement* selectNode = toSelectElement(static_cast<Element*>(renderer->node()));
+ int selectedIndex = selectNode->selectedIndex();
+ const Vector<Element*> listItems = selectNode->listItems();
+
+ if (selectedIndex < 0 || selectedIndex >= static_cast<int>(listItems.size()))
+ return 0;
+
+ return optionFromList(selection, selectedIndex);
+ }
+
+ if (i < static_cast<gint>(selectedItems.size()))
+ return selectedItems.at(i).get();
+
+ return 0;
+}
+
+static gboolean webkit_accessible_selection_add_selection(AtkSelection* selection, gint i)
+{
+ AccessibilityObject* coreSelection = core(selection);
+ if (!coreSelection)
+ return false;
+
+ AccessibilityObject* option = optionFromList(selection, i);
+ if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) {
+ option->setSelected(true);
+ return option->isSelected();
+ }
+
+ return false;
+}
+
+static gboolean webkit_accessible_selection_clear_selection(AtkSelection* selection)
+{
+ AccessibilityObject* coreSelection = core(selection);
+ if (!coreSelection)
+ return false;
+
+ AccessibilityRenderObject::AccessibilityChildrenVector selectedItems;
+ if (coreSelection->isListBox() || coreSelection->isMenuList()) {
+ // Set the list of selected items to an empty list; then verify that it worked.
+ AccessibilityListBox* listBox = static_cast<AccessibilityListBox*>(coreSelection);
+ listBox->setSelectedChildren(selectedItems);
+ listBox->selectedChildren(selectedItems);
+ return selectedItems.size() == 0;
+ }
+ return false;
+}
+
+static AtkObject* webkit_accessible_selection_ref_selection(AtkSelection* selection, gint i)
+{
+ AccessibilityObject* option = optionFromSelection(selection, i);
+ if (option) {
+ AtkObject* child = option->wrapper();
+ g_object_ref(child);
+ return child;
+ }
+
+ return 0;
+}
+
+static gint webkit_accessible_selection_get_selection_count(AtkSelection* selection)
+{
+ AccessibilityObject* coreSelection = core(selection);
+ if (!coreSelection || !coreSelection->isAccessibilityRenderObject())
+ return 0;
+
+ if (coreSelection->isListBox()) {
+ AccessibilityRenderObject::AccessibilityChildrenVector selectedItems;
+ coreSelection->selectedChildren(selectedItems);
+ return static_cast<gint>(selectedItems.size());
+ }
+
+ if (coreSelection->isMenuList()) {
+ RenderObject* renderer = toAccessibilityRenderObject(coreSelection)->renderer();
+ if (!renderer)
+ return 0;
+
+ SelectElement* selectNode = toSelectElement(static_cast<Element*>(renderer->node()));
+ int selectedIndex = selectNode->selectedIndex();
+ const Vector<Element*> listItems = selectNode->listItems();
+
+ return selectedIndex >= 0 && selectedIndex < static_cast<int>(listItems.size());
+ }
+
+ return 0;
+}
+
+static gboolean webkit_accessible_selection_is_child_selected(AtkSelection* selection, gint i)
+{
+ AccessibilityObject* coreSelection = core(selection);
+ if (!coreSelection)
+ return 0;
+
+ AccessibilityObject* option = optionFromList(selection, i);
+ if (option && (coreSelection->isListBox() || coreSelection->isMenuList()))
+ return option->isSelected();
+
+ return false;
+}
+
+static gboolean webkit_accessible_selection_remove_selection(AtkSelection* selection, gint i)
+{
+ AccessibilityObject* coreSelection = core(selection);
+ if (!coreSelection)
+ return 0;
+
+ // TODO: This is only getting called if i == 0. What is preventing the rest?
+ AccessibilityObject* option = optionFromSelection(selection, i);
+ if (option && (coreSelection->isListBox() || coreSelection->isMenuList())) {
+ option->setSelected(false);
+ return !option->isSelected();
+ }
+
+ return false;
+}
+
+static gboolean webkit_accessible_selection_select_all_selection(AtkSelection* selection)
+{
+ AccessibilityObject* coreSelection = core(selection);
+ if (!coreSelection || !coreSelection->isMultiSelectable())
+ return false;
+
+ AccessibilityRenderObject::AccessibilityChildrenVector children = coreSelection->children();
+ if (coreSelection->isListBox()) {
+ AccessibilityListBox* listBox = static_cast<AccessibilityListBox*>(coreSelection);
+ listBox->setSelectedChildren(children);
+ AccessibilityRenderObject::AccessibilityChildrenVector selectedItems;
+ listBox->selectedChildren(selectedItems);
+ return selectedItems.size() == children.size();
+ }
+
+ return false;
+}
+
+static void atk_selection_interface_init(AtkSelectionIface* iface)
+{
+ iface->add_selection = webkit_accessible_selection_add_selection;
+ iface->clear_selection = webkit_accessible_selection_clear_selection;
+ iface->ref_selection = webkit_accessible_selection_ref_selection;
+ iface->get_selection_count = webkit_accessible_selection_get_selection_count;
+ iface->is_child_selected = webkit_accessible_selection_is_child_selected;
+ iface->remove_selection = webkit_accessible_selection_remove_selection;
+ iface->select_all_selection = webkit_accessible_selection_select_all_selection;
+}
+
+// Text
+
+static gchar* utf8Substr(const gchar* string, gint start, gint end)
+{
+ ASSERT(string);
+ glong strLen = g_utf8_strlen(string, -1);
+ if (start > strLen || end > strLen)
+ return 0;
+ gchar* startPtr = g_utf8_offset_to_pointer(string, start);
+ gsize lenInBytes = g_utf8_offset_to_pointer(string, end) - startPtr + 1;
+ gchar* output = static_cast<gchar*>(g_malloc0(lenInBytes + 1));
+ return g_utf8_strncpy(output, startPtr, end - start + 1);
+}
+
+// This function is not completely general, is it's tied to the
+// internals of WebCore's text presentation.
+static gchar* convertUniCharToUTF8(const UChar* characters, gint length, int from, int to)
+{
+ CString stringUTF8 = UTF8Encoding().encode(characters, length, QuestionMarksForUnencodables);
+ gchar* utf8String = utf8Substr(stringUTF8.data(), from, to);
+ if (!g_utf8_validate(utf8String, -1, 0)) {
+ g_free(utf8String);
+ return 0;
+ }
+ gsize len = strlen(utf8String);
+ GString* ret = g_string_new_len(0, len);
+ gchar* ptr = utf8String;
+
+ // WebCore introduces line breaks in the text that do not reflect
+ // the layout you see on the screen, replace them with spaces
+ while (len > 0) {
+ gint index, start;
+ pango_find_paragraph_boundary(ptr, len, &index, &start);
+ g_string_append_len(ret, ptr, index);
+ if (index == start)
+ break;
+ g_string_append_c(ret, ' ');
+ ptr += start;
+ len -= start;
+ }
+
+ g_free(utf8String);
+ return g_string_free(ret, FALSE);
+}
+
+gchar* textForRenderer(RenderObject* renderer)
+{
+ GString* resultText = g_string_new(0);
+
+ if (!renderer)
+ return g_string_free(resultText, FALSE);
+
+ // For RenderBlocks, piece together the text from the RenderText objects they contain.
+ for (RenderObject* object = renderer->firstChild(); object; object = object->nextSibling()) {
+ if (object->isBR()) {
+ g_string_append(resultText, "\n");
+ continue;
+ }
+
+ RenderText* renderText;
+ if (object->isText())
+ renderText = toRenderText(object);
+ else {
+ // We need to check children, if any, to consider when
+ // current object is not a text object but some of its
+ // children are, in order not to miss those portions of
+ // text by not properly handling those situations
+ if (object->firstChild())
+ g_string_append(resultText, textForRenderer(object));
+
+ continue;
+ }
+
+ InlineTextBox* box = renderText->firstTextBox();
+ while (box) {
+ gchar* text = convertUniCharToUTF8(renderText->characters(), renderText->textLength(), box->start(), box->end());
+ g_string_append(resultText, text);
+ // Newline chars in the source result in separate text boxes, so check
+ // before adding a newline in the layout. See bug 25415 comment #78.
+ // If the next sibling is a BR, we'll add the newline when we examine that child.
+ if (!box->nextOnLineExists() && (!object->nextSibling() || !object->nextSibling()->isBR()))
+ g_string_append(resultText, "\n");
+ box = box->nextTextBox();
+ }
+ }
+
+ // Insert the text of the marker for list item in the right place, if present
+ if (renderer->isListItem()) {
+ String markerText = toRenderListItem(renderer)->markerTextWithSuffix();
+ if (renderer->style()->direction() == LTR)
+ g_string_prepend(resultText, markerText.utf8().data());
+ else
+ g_string_append(resultText, markerText.utf8().data());
+ }
+
+ return g_string_free(resultText, FALSE);
+}
+
+gchar* textForObject(AccessibilityRenderObject* accObject)
+{
+ GString* str = g_string_new(0);
+
+ // For text controls, we can get the text line by line.
+ if (accObject->isTextControl()) {
+ unsigned textLength = accObject->textLength();
+ int lineNumber = 0;
+ PlainTextRange range = accObject->doAXRangeForLine(lineNumber);
+ while (range.length) {
+ // When a line of text wraps in a text area, the final space is removed.
+ if (range.start + range.length < textLength)
+ range.length -= 1;
+ String lineText = accObject->doAXStringForRange(range);
+ g_string_append(str, lineText.utf8().data());
+ g_string_append(str, "\n");
+ range = accObject->doAXRangeForLine(++lineNumber);
+ }
+ } else if (accObject->isAccessibilityRenderObject()) {
+ GOwnPtr<gchar> rendererText(textForRenderer(accObject->renderer()));
+ g_string_append(str, rendererText.get());
+ }
+
+ return g_string_free(str, FALSE);
+}
+
+static gchar* webkit_accessible_text_get_text(AtkText* text, gint startOffset, gint endOffset)
+{
+ AccessibilityObject* coreObject = core(text);
+ String ret;
+ unsigned start = startOffset;
+ if (endOffset == -1) {
+ endOffset = coreObject->stringValue().length();
+ if (!endOffset)
+ endOffset = coreObject->textUnderElement().length();
+ }
+ int length = endOffset - startOffset;
+
+ if (coreObject->isTextControl())
+ ret = coreObject->doAXStringForRange(PlainTextRange(start, length));
+ else {
+ ret = coreObject->stringValue().substring(start, length);
+ if (!ret)
+ ret = coreObject->textUnderElement().substring(start, length);
+ }
+
+ if (!ret.length()) {
+ // This can happen at least with anonymous RenderBlocks (e.g. body text amongst paragraphs)
+ ret = String(textForObject(static_cast<AccessibilityRenderObject*>(coreObject)));
+ if (!endOffset)
+ endOffset = ret.length();
+ ret = ret.substring(start, endOffset - startOffset);
+ }
+
+ // Prefix a item number/bullet if needed
+ if (coreObject->roleValue() == ListItemRole) {
+ RenderObject* objRenderer = static_cast<AccessibilityRenderObject*>(coreObject)->renderer();
+ if (objRenderer && objRenderer->isListItem()) {
+ String markerText = toRenderListItem(objRenderer)->markerTextWithSuffix();
+ ret = objRenderer->style()->direction() == LTR ? markerText + ret : ret + markerText;
+ }
+ }
+
+ return g_strdup(ret.utf8().data());
+}
+
+static GailTextUtil* getGailTextUtilForAtk(AtkText* textObject)
+{
+ gpointer data = g_object_get_data(G_OBJECT(textObject), "webkit-accessible-gail-text-util");
+ if (data)
+ return static_cast<GailTextUtil*>(data);
+
+ GailTextUtil* gailTextUtil = gail_text_util_new();
+ gail_text_util_text_setup(gailTextUtil, webkit_accessible_text_get_text(textObject, 0, -1));
+ g_object_set_data_full(G_OBJECT(textObject), "webkit-accessible-gail-text-util", gailTextUtil, g_object_unref);
+ return gailTextUtil;
+}
+
+static PangoLayout* getPangoLayoutForAtk(AtkText* textObject)
+{
+ AccessibilityObject* coreObject = core(textObject);
+
+ HostWindow* hostWindow = coreObject->document()->view()->hostWindow();
+ if (!hostWindow)
+ return 0;
+ PlatformPageClient webView = hostWindow->platformPageClient();
+ if (!webView)
+ return 0;
+
+ AccessibilityRenderObject* accObject = static_cast<AccessibilityRenderObject*>(coreObject);
+ if (!accObject)
+ return 0;
+
+ // Create a string with the layout as it appears on the screen
+ PangoLayout* layout = gtk_widget_create_pango_layout(static_cast<GtkWidget*>(webView), textForObject(accObject));
+ g_object_set_data_full(G_OBJECT(textObject), "webkit-accessible-pango-layout", layout, g_object_unref);
+ return layout;
+}
+
+static gchar* webkit_accessible_text_get_text_after_offset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
+{
+ return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutForAtk(text), GAIL_AFTER_OFFSET, boundaryType, offset, startOffset, endOffset);
+}
+
+static gchar* webkit_accessible_text_get_text_at_offset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
+{
+ return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutForAtk(text), GAIL_AT_OFFSET, boundaryType, offset, startOffset, endOffset);
+}
+
+static gchar* webkit_accessible_text_get_text_before_offset(AtkText* text, gint offset, AtkTextBoundary boundaryType, gint* startOffset, gint* endOffset)
+{
+ return gail_text_util_get_text(getGailTextUtilForAtk(text), getPangoLayoutForAtk(text), GAIL_BEFORE_OFFSET, boundaryType, offset, startOffset, endOffset);
+}
+
+static gunichar webkit_accessible_text_get_character_at_offset(AtkText* text, gint offset)
+{
+ notImplemented();
+ return 0;
+}
+
+static gint webkit_accessible_text_get_caret_offset(AtkText* text)
+{
+ // coreObject is the unignored object whose offset the caller is requesting.
+ // focusedObject is the object with the caret. It is likely ignored -- unless it's a link.
+ AccessibilityObject* coreObject = core(text);
+ Node* focusedNode = coreObject->selection().end().node();
+
+ if (!focusedNode)
+ return 0;
+
+ RenderObject* focusedRenderer = focusedNode->renderer();
+ AccessibilityObject* focusedObject = coreObject->document()->axObjectCache()->getOrCreate(focusedRenderer);
+
+ int offset;
+ // Don't ignore links if the offset is being requested for a link.
+ if (!objectAndOffsetUnignored(focusedObject, offset, !coreObject->isLink()))
+ return 0;
+
+ // TODO: Verify this for RTL text.
+ return offset;
+}
+
+static int baselinePositionForAccessibilityRenderObject(RenderObject* renderObject)
+{
+ // FIXME: This implementation of baselinePosition originates from RenderObject.cpp and was
+ // removed in r70072. The implementation looks incorrect though, because this is not the
+ // baseline of the underlying RenderObject, but of the AccessibilityRenderObject.
+ const Font& f = renderObject->firstLineStyle()->font();
+ return f.ascent() + (renderObject->firstLineStyle()->computedLineHeight() - f.height()) / 2;
+}
+
+static AtkAttributeSet* getAttributeSetForAccessibilityObject(const AccessibilityObject* object)
+{
+ if (!object->isAccessibilityRenderObject())
+ return 0;
+
+ RenderObject* renderer = static_cast<const AccessibilityRenderObject*>(object)->renderer();
+ RenderStyle* style = renderer->style();
+
+ AtkAttributeSet* result = 0;
+ GOwnPtr<gchar> buffer(g_strdup_printf("%i", style->fontSize()));
+ result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_SIZE), buffer.get());
+
+ Color bgColor = style->visitedDependentColor(CSSPropertyBackgroundColor);
+ if (bgColor.isValid()) {
+ buffer.set(g_strdup_printf("%i,%i,%i",
+ bgColor.red(), bgColor.green(), bgColor.blue()));
+ result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_BG_COLOR), buffer.get());
+ }
+
+ Color fgColor = style->visitedDependentColor(CSSPropertyColor);
+ if (fgColor.isValid()) {
+ buffer.set(g_strdup_printf("%i,%i,%i",
+ fgColor.red(), fgColor.green(), fgColor.blue()));
+ result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_FG_COLOR), buffer.get());
+ }
+
+ int baselinePosition;
+ bool includeRise = true;
+ switch (style->verticalAlign()) {
+ case SUB:
+ baselinePosition = -1 * baselinePositionForAccessibilityRenderObject(renderer);
+ break;
+ case SUPER:
+ baselinePosition = baselinePositionForAccessibilityRenderObject(renderer);
+ break;
+ case BASELINE:
+ baselinePosition = 0;
+ break;
+ default:
+ includeRise = false;
+ break;
+ }
+
+ if (includeRise) {
+ buffer.set(g_strdup_printf("%i", baselinePosition));
+ result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_RISE), buffer.get());
+ }
+
+ int indentation = style->textIndent().calcValue(object->size().width());
+ if (indentation != undefinedLength) {
+ buffer.set(g_strdup_printf("%i", indentation));
+ result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_INDENT), buffer.get());
+ }
+
+ String fontFamilyName = style->font().family().family().string();
+ if (fontFamilyName.left(8) == "-webkit-")
+ fontFamilyName = fontFamilyName.substring(8);
+
+ result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_FAMILY_NAME), fontFamilyName.utf8().data());
+
+ int fontWeight = -1;
+ switch (style->font().weight()) {
+ case FontWeight100:
+ fontWeight = 100;
+ break;
+ case FontWeight200:
+ fontWeight = 200;
+ break;
+ case FontWeight300:
+ fontWeight = 300;
+ break;
+ case FontWeight400:
+ fontWeight = 400;
+ break;
+ case FontWeight500:
+ fontWeight = 500;
+ break;
+ case FontWeight600:
+ fontWeight = 600;
+ break;
+ case FontWeight700:
+ fontWeight = 700;
+ break;
+ case FontWeight800:
+ fontWeight = 800;
+ break;
+ case FontWeight900:
+ fontWeight = 900;
+ }
+ if (fontWeight > 0) {
+ buffer.set(g_strdup_printf("%i", fontWeight));
+ result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_WEIGHT), buffer.get());
+ }
+
+ switch (style->textAlign()) {
+ case TAAUTO:
+ break;
+ case LEFT:
+ case WEBKIT_LEFT:
+ result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "left");
+ break;
+ case RIGHT:
+ case WEBKIT_RIGHT:
+ result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "right");
+ break;
+ case CENTER:
+ case WEBKIT_CENTER:
+ result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "center");
+ break;
+ case JUSTIFY:
+ result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_JUSTIFICATION), "fill");
+ }
+
+ result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_UNDERLINE), (style->textDecoration() & UNDERLINE) ? "single" : "none");
+
+ result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_STYLE), style->font().italic() ? "italic" : "normal");
+
+ result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_STRIKETHROUGH), (style->textDecoration() & LINE_THROUGH) ? "true" : "false");
+
+ result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_INVISIBLE), (style->visibility() == HIDDEN) ? "true" : "false");
+
+ result = addAttributeToSet(result, atk_text_attribute_get_name(ATK_TEXT_ATTR_EDITABLE), object->isReadOnly() ? "false" : "true");
+
+ return result;
+}
+
+static gint compareAttribute(const AtkAttribute* a, const AtkAttribute* b)
+{
+ return g_strcmp0(a->name, b->name) || g_strcmp0(a->value, b->value);
+}
+
+// Returns an AtkAttributeSet with the elements of a1 which are either
+// not present or different in a2. Neither a1 nor a2 should be used
+// after calling this function.
+static AtkAttributeSet* attributeSetDifference(AtkAttributeSet* a1, AtkAttributeSet* a2)
+{
+ if (!a2)
+ return a1;
+
+ AtkAttributeSet* i = a1;
+ AtkAttributeSet* found;
+ AtkAttributeSet* toDelete = 0;
+
+ while (i) {
+ found = g_slist_find_custom(a2, i->data, (GCompareFunc)compareAttribute);
+ if (found) {
+ AtkAttributeSet* t = i->next;
+ toDelete = g_slist_prepend(toDelete, i->data);
+ a1 = g_slist_delete_link(a1, i);
+ i = t;
+ } else
+ i = i->next;
+ }
+
+ atk_attribute_set_free(a2);
+ atk_attribute_set_free(toDelete);
+ return a1;
+}
+
+static guint accessibilityObjectLength(const AccessibilityObject* object)
+{
+ // Non render objects are not taken into account
+ if (!object->isAccessibilityRenderObject())
+ return 0;
+
+ // For those objects implementing the AtkText interface we use the
+ // well known API to always get the text in a consistent way
+ AtkObject* atkObj = ATK_OBJECT(object->wrapper());
+ if (ATK_IS_TEXT(atkObj)) {
+ GOwnPtr<gchar> text(webkit_accessible_text_get_text(ATK_TEXT(atkObj), 0, -1));
+ return g_utf8_strlen(text.get(), -1);
+ }
+
+ // Even if we don't expose list markers to Assistive
+ // Technologies, we need to have a way to measure their length
+ // for those cases when it's needed to take it into account
+ // separately (as in getAccessibilityObjectForOffset)
+ RenderObject* renderer = static_cast<const AccessibilityRenderObject*>(object)->renderer();
+ if (renderer && renderer->isListMarker()) {
+ RenderListMarker* marker = toRenderListMarker(renderer);
+ return marker->text().length() + marker->suffix().length();
+ }
+
+ return 0;
+}
+
+static const AccessibilityObject* getAccessibilityObjectForOffset(const AccessibilityObject* object, guint offset, gint* startOffset, gint* endOffset)
+{
+ const AccessibilityObject* result;
+ guint length = accessibilityObjectLength(object);
+ if (length > offset) {
+ *startOffset = 0;
+ *endOffset = length;
+ result = object;
+ } else {
+ *startOffset = -1;
+ *endOffset = -1;
+ result = 0;
+ }
+
+ if (!object->firstChild())
+ return result;
+
+ AccessibilityObject* child = object->firstChild();
+ guint currentOffset = 0;
+ guint childPosition = 0;
+ while (child && currentOffset <= offset) {
+ guint childLength = accessibilityObjectLength(child);
+ currentOffset = childLength + childPosition;
+ if (currentOffset > offset) {
+ gint childStartOffset;
+ gint childEndOffset;
+ const AccessibilityObject* grandChild = getAccessibilityObjectForOffset(child, offset-childPosition, &childStartOffset, &childEndOffset);
+ if (childStartOffset >= 0) {
+ *startOffset = childStartOffset + childPosition;
+ *endOffset = childEndOffset + childPosition;
+ result = grandChild;
+ }
+ } else {
+ childPosition += childLength;
+ child = child->nextSibling();
+ }
+ }
+ return result;
+}
+
+static AtkAttributeSet* getRunAttributesFromAccesibilityObject(const AccessibilityObject* element, gint offset, gint* startOffset, gint* endOffset)
+{
+ const AccessibilityObject *child = getAccessibilityObjectForOffset(element, offset, startOffset, endOffset);
+ if (!child) {
+ *startOffset = -1;
+ *endOffset = -1;
+ return 0;
+ }
+
+ AtkAttributeSet* defaultAttributes = getAttributeSetForAccessibilityObject(element);
+ AtkAttributeSet* childAttributes = getAttributeSetForAccessibilityObject(child);
+
+ return attributeSetDifference(childAttributes, defaultAttributes);
+}
+
+static AtkAttributeSet* webkit_accessible_text_get_run_attributes(AtkText* text, gint offset, gint* startOffset, gint* endOffset)
+{
+ AccessibilityObject* coreObject = core(text);
+ AtkAttributeSet* result;
+
+ if (!coreObject) {
+ *startOffset = 0;
+ *endOffset = atk_text_get_character_count(text);
+ return 0;
+ }
+
+ if (offset == -1)
+ offset = atk_text_get_caret_offset(text);
+
+ result = getRunAttributesFromAccesibilityObject(coreObject, offset, startOffset, endOffset);
+
+ if (*startOffset < 0) {
+ *startOffset = offset;
+ *endOffset = offset;
+ }
+
+ return result;
+}
+
+static AtkAttributeSet* webkit_accessible_text_get_default_attributes(AtkText* text)
+{
+ AccessibilityObject* coreObject = core(text);
+ if (!coreObject || !coreObject->isAccessibilityRenderObject())
+ return 0;
+
+ return getAttributeSetForAccessibilityObject(coreObject);
+}
+
+static IntRect textExtents(AtkText* text, gint startOffset, gint length, AtkCoordType coords)
+{
+ gchar* textContent = webkit_accessible_text_get_text(text, startOffset, -1);
+ gint textLength = g_utf8_strlen(textContent, -1);
+
+ // The first case (endOffset of -1) should work, but seems broken for all Gtk+ apps.
+ gint rangeLength = length;
+ if (rangeLength < 0 || rangeLength > textLength)
+ rangeLength = textLength;
+ AccessibilityObject* coreObject = core(text);
+
+ IntRect extents = coreObject->doAXBoundsForRange(PlainTextRange(startOffset, rangeLength));
+ switch(coords) {
+ case ATK_XY_SCREEN:
+ extents = coreObject->document()->view()->contentsToScreen(extents);
+ break;
+ case ATK_XY_WINDOW:
+ // No-op
+ break;
+ }
+
+ return extents;
+}
+
+static void webkit_accessible_text_get_character_extents(AtkText* text, gint offset, gint* x, gint* y, gint* width, gint* height, AtkCoordType coords)
+{
+ IntRect extents = textExtents(text, offset, 1, coords);
+ *x = extents.x();
+ *y = extents.y();
+ *width = extents.width();
+ *height = extents.height();
+}
+
+static void webkit_accessible_text_get_range_extents(AtkText* text, gint startOffset, gint endOffset, AtkCoordType coords, AtkTextRectangle* rect)
+{
+ IntRect extents = textExtents(text, startOffset, endOffset - startOffset + 1, coords);
+ rect->x = extents.x();
+ rect->y = extents.y();
+ rect->width = extents.width();
+ rect->height = extents.height();
+}
+
+static gint webkit_accessible_text_get_character_count(AtkText* text)
+{
+ return accessibilityObjectLength(core(text));
+}
+
+static gint webkit_accessible_text_get_offset_at_point(AtkText* text, gint x, gint y, AtkCoordType coords)
+{
+ // FIXME: Use the AtkCoordType
+ // TODO: Is it correct to ignore range.length?
+ IntPoint pos(x, y);
+ PlainTextRange range = core(text)->doAXRangeForPosition(pos);
+ return range.start;
+}
+
+static void getSelectionOffsetsForObject(AccessibilityObject* coreObject, VisibleSelection& selection, gint& startOffset, gint& endOffset)
+{
+ if (!coreObject->isAccessibilityRenderObject())
+ return;
+
+ // Early return if the selection doesn't affect the selected node.
+ if (!selectionBelongsToObject(coreObject, selection))
+ return;
+
+ // We need to find the exact start and end positions in the
+ // selected node that intersects the selection, to later on get
+ // the right values for the effective start and end offsets.
+ ExceptionCode ec = 0;
+ Position nodeRangeStart;
+ Position nodeRangeEnd;
+ Node* node = coreObject->node();
+ RefPtr<Range> selRange = selection.toNormalizedRange();
+
+ // If the selection affects the selected node and its first
+ // possible position is also in the selection, we must set
+ // nodeRangeStart to that position, otherwise to the selection's
+ // start position (it would belong to the node anyway).
+ Node* firstLeafNode = node->firstDescendant();
+ if (selRange->isPointInRange(firstLeafNode, 0, ec))
+ nodeRangeStart = firstPositionInNode(firstLeafNode);
+ else
+ nodeRangeStart = selRange->startPosition();
+
+ // If the selection affects the selected node and its last
+ // possible position is also in the selection, we must set
+ // nodeRangeEnd to that position, otherwise to the selection's
+ // end position (it would belong to the node anyway).
+ Node* lastLeafNode = node->lastDescendant();
+ if (selRange->isPointInRange(lastLeafNode, lastOffsetInNode(lastLeafNode), ec))
+ nodeRangeEnd = lastPositionInNode(lastLeafNode);
+ else
+ nodeRangeEnd = selRange->endPosition();
+
+ // Calculate position of the selected range inside the object.
+ Position parentFirstPosition = firstPositionInNode(node);
+ RefPtr<Range> rangeInParent = Range::create(node->document(), parentFirstPosition, nodeRangeStart);
+
+ // Set values for start and end offsets.
+ startOffset = TextIterator::rangeLength(rangeInParent.get());
+ RefPtr<Range> nodeRange = Range::create(node->document(), nodeRangeStart, nodeRangeEnd);
+ endOffset = startOffset + TextIterator::rangeLength(nodeRange.get());
+}
+
+static gint webkit_accessible_text_get_n_selections(AtkText* text)
+{
+ AccessibilityObject* coreObject = core(text);
+ VisibleSelection selection = coreObject->selection();
+
+ // Only range selections are needed for the purpose of this method
+ if (!selection.isRange())
+ return 0;
+
+ // We don't support multiple selections for now, so there's only
+ // two possibilities
+ // Also, we don't want to do anything if the selection does not
+ // belong to the currently selected object. We have to check since
+ // there's no way to get the selection for a given object, only
+ // the global one (the API is a bit confusing)
+ return selectionBelongsToObject(coreObject, selection) ? 1 : 0;
+}
+
+static gchar* webkit_accessible_text_get_selection(AtkText* text, gint selectionNum, gint* startOffset, gint* endOffset)
+{
+ // Default values, unless the contrary is proved
+ *startOffset = *endOffset = 0;
+
+ // WebCore does not support multiple selection, so anything but 0 does not make sense for now.
+ if (selectionNum)
+ return 0;
+
+ // Get the offsets of the selection for the selected object
+ AccessibilityObject* coreObject = core(text);
+ VisibleSelection selection = coreObject->selection();
+ getSelectionOffsetsForObject(coreObject, selection, *startOffset, *endOffset);
+
+ // Return 0 instead of "", as that's the expected result for
+ // this AtkText method when there's no selection
+ if (*startOffset == *endOffset)
+ return 0;
+
+ return webkit_accessible_text_get_text(text, *startOffset, *endOffset);
+}
+
+static gboolean webkit_accessible_text_add_selection(AtkText* text, gint start_offset, gint end_offset)
+{
+ notImplemented();
+ return FALSE;
+}
+
+static gboolean webkit_accessible_text_set_selection(AtkText* text, gint selectionNum, gint startOffset, gint endOffset)
+{
+ // WebCore does not support multiple selection, so anything but 0 does not make sense for now.
+ if (selectionNum)
+ return FALSE;
+
+ // Consider -1 and out-of-bound values and correct them to length
+ gint textCount = webkit_accessible_text_get_character_count(text);
+ if (startOffset < 0 || startOffset > textCount)
+ startOffset = textCount;
+ if (endOffset < 0 || endOffset > textCount)
+ endOffset = textCount;
+
+ AccessibilityObject* coreObject = core(text);
+ PlainTextRange textRange(startOffset, endOffset - startOffset);
+ VisiblePositionRange range = coreObject->visiblePositionRangeForRange(textRange);
+ coreObject->setSelectedVisiblePositionRange(range);
+
+ return TRUE;
+}
+
+static gboolean webkit_accessible_text_remove_selection(AtkText* text, gint selectionNum)
+{
+ // WebCore does not support multiple selection, so anything but 0 does not make sense for now.
+ if (selectionNum)
+ return FALSE;
+
+ // Do nothing if current selection doesn't belong to the object
+ if (!webkit_accessible_text_get_n_selections(text))
+ return FALSE;
+
+ // Set a new 0-sized selection to the caret position, in order
+ // to simulate selection removal (GAIL style)
+ gint caretOffset = webkit_accessible_text_get_caret_offset(text);
+ return webkit_accessible_text_set_selection(text, selectionNum, caretOffset, caretOffset);
+}
+
+static gboolean webkit_accessible_text_set_caret_offset(AtkText* text, gint offset)
+{
+ AccessibilityObject* coreObject = core(text);
+
+ PlainTextRange textRange(offset, 0);
+ VisiblePositionRange range = coreObject->visiblePositionRangeForRange(textRange);
+ coreObject->setSelectedVisiblePositionRange(range);
+
+ return TRUE;
+}
+
+static void atk_text_interface_init(AtkTextIface* iface)
+{
+ iface->get_text = webkit_accessible_text_get_text;
+ iface->get_text_after_offset = webkit_accessible_text_get_text_after_offset;
+ iface->get_text_at_offset = webkit_accessible_text_get_text_at_offset;
+ iface->get_character_at_offset = webkit_accessible_text_get_character_at_offset;
+ iface->get_text_before_offset = webkit_accessible_text_get_text_before_offset;
+ iface->get_caret_offset = webkit_accessible_text_get_caret_offset;
+ iface->get_run_attributes = webkit_accessible_text_get_run_attributes;
+ iface->get_default_attributes = webkit_accessible_text_get_default_attributes;
+ iface->get_character_extents = webkit_accessible_text_get_character_extents;
+ iface->get_range_extents = webkit_accessible_text_get_range_extents;
+ iface->get_character_count = webkit_accessible_text_get_character_count;
+ iface->get_offset_at_point = webkit_accessible_text_get_offset_at_point;
+ iface->get_n_selections = webkit_accessible_text_get_n_selections;
+ iface->get_selection = webkit_accessible_text_get_selection;
+
+ // set methods
+ iface->add_selection = webkit_accessible_text_add_selection;
+ iface->remove_selection = webkit_accessible_text_remove_selection;
+ iface->set_selection = webkit_accessible_text_set_selection;
+ iface->set_caret_offset = webkit_accessible_text_set_caret_offset;
+}
+
+// EditableText
+
+static gboolean webkit_accessible_editable_text_set_run_attributes(AtkEditableText* text, AtkAttributeSet* attrib_set, gint start_offset, gint end_offset)
+{
+ notImplemented();
+ return FALSE;
+}
+
+static void webkit_accessible_editable_text_set_text_contents(AtkEditableText* text, const gchar* string)
+{
+ // FIXME: string nullcheck?
+ core(text)->setValue(String::fromUTF8(string));
+}
+
+static void webkit_accessible_editable_text_insert_text(AtkEditableText* text, const gchar* string, gint length, gint* position)
+{
+ // FIXME: string nullcheck?
+
+ AccessibilityObject* coreObject = core(text);
+ // FIXME: Not implemented in WebCore
+ //coreObject->setSelectedTextRange(PlainTextRange(*position, 0));
+ //coreObject->setSelectedText(String::fromUTF8(string));
+
+ if (!coreObject->document() || !coreObject->document()->frame())
+ return;
+ coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(*position, 0)));
+ coreObject->setFocused(true);
+ // FIXME: We should set position to the actual inserted text length, which may be less than that requested.
+ if (coreObject->document()->frame()->editor()->insertTextWithoutSendingTextEvent(String::fromUTF8(string), false, 0))
+ *position += length;
+}
+
+static void webkit_accessible_editable_text_copy_text(AtkEditableText* text, gint start_pos, gint end_pos)
+{
+ notImplemented();
+}
+
+static void webkit_accessible_editable_text_cut_text(AtkEditableText* text, gint start_pos, gint end_pos)
+{
+ notImplemented();
+}
+
+static void webkit_accessible_editable_text_delete_text(AtkEditableText* text, gint start_pos, gint end_pos)
+{
+ AccessibilityObject* coreObject = core(text);
+ // FIXME: Not implemented in WebCore
+ //coreObject->setSelectedTextRange(PlainTextRange(start_pos, end_pos - start_pos));
+ //coreObject->setSelectedText(String());
+
+ if (!coreObject->document() || !coreObject->document()->frame())
+ return;
+ coreObject->setSelectedVisiblePositionRange(coreObject->visiblePositionRangeForRange(PlainTextRange(start_pos, end_pos - start_pos)));
+ coreObject->setFocused(true);
+ coreObject->document()->frame()->editor()->performDelete();
+}
+
+static void webkit_accessible_editable_text_paste_text(AtkEditableText* text, gint position)
+{
+ notImplemented();
+}
+
+static void atk_editable_text_interface_init(AtkEditableTextIface* iface)
+{
+ iface->set_run_attributes = webkit_accessible_editable_text_set_run_attributes;
+ iface->set_text_contents = webkit_accessible_editable_text_set_text_contents;
+ iface->insert_text = webkit_accessible_editable_text_insert_text;
+ iface->copy_text = webkit_accessible_editable_text_copy_text;
+ iface->cut_text = webkit_accessible_editable_text_cut_text;
+ iface->delete_text = webkit_accessible_editable_text_delete_text;
+ iface->paste_text = webkit_accessible_editable_text_paste_text;
+}
+
+static void contentsToAtk(AccessibilityObject* coreObject, AtkCoordType coordType, IntRect rect, gint* x, gint* y, gint* width = 0, gint* height = 0)
+{
+ FrameView* frameView = coreObject->documentFrameView();
+
+ if (frameView) {
+ switch (coordType) {
+ case ATK_XY_WINDOW:
+ rect = frameView->contentsToWindow(rect);
+ break;
+ case ATK_XY_SCREEN:
+ rect = frameView->contentsToScreen(rect);
+ break;
+ }
+ }
+
+ if (x)
+ *x = rect.x();
+ if (y)
+ *y = rect.y();
+ if (width)
+ *width = rect.width();
+ if (height)
+ *height = rect.height();
+}
+
+static IntPoint atkToContents(AccessibilityObject* coreObject, AtkCoordType coordType, gint x, gint y)
+{
+ IntPoint pos(x, y);
+
+ FrameView* frameView = coreObject->documentFrameView();
+ if (frameView) {
+ switch (coordType) {
+ case ATK_XY_SCREEN:
+ return frameView->screenToContents(pos);
+ case ATK_XY_WINDOW:
+ return frameView->windowToContents(pos);
+ }
+ }
+
+ return pos;
+}
+
+static AtkObject* webkit_accessible_component_ref_accessible_at_point(AtkComponent* component, gint x, gint y, AtkCoordType coordType)
+{
+ IntPoint pos = atkToContents(core(component), coordType, x, y);
+
+ AccessibilityObject* target = core(component)->accessibilityHitTest(pos);
+ if (!target)
+ return 0;
+ g_object_ref(target->wrapper());
+ return target->wrapper();
+}
+
+static void webkit_accessible_component_get_extents(AtkComponent* component, gint* x, gint* y, gint* width, gint* height, AtkCoordType coordType)
+{
+ IntRect rect = core(component)->elementRect();
+ contentsToAtk(core(component), coordType, rect, x, y, width, height);
+}
+
+static gboolean webkit_accessible_component_grab_focus(AtkComponent* component)
+{
+ core(component)->setFocused(true);
+ return core(component)->isFocused();
+}
+
+static void atk_component_interface_init(AtkComponentIface* iface)
+{
+ iface->ref_accessible_at_point = webkit_accessible_component_ref_accessible_at_point;
+ iface->get_extents = webkit_accessible_component_get_extents;
+ iface->grab_focus = webkit_accessible_component_grab_focus;
+}
+
+// Image
+
+static void webkit_accessible_image_get_image_position(AtkImage* image, gint* x, gint* y, AtkCoordType coordType)
+{
+ IntRect rect = core(image)->elementRect();
+ contentsToAtk(core(image), coordType, rect, x, y);
+}
+
+static const gchar* webkit_accessible_image_get_image_description(AtkImage* image)
+{
+ return returnString(core(image)->accessibilityDescription());
+}
+
+static void webkit_accessible_image_get_image_size(AtkImage* image, gint* width, gint* height)
+{
+ IntSize size = core(image)->size();
+
+ if (width)
+ *width = size.width();
+ if (height)
+ *height = size.height();
+}
+
+static void atk_image_interface_init(AtkImageIface* iface)
+{
+ iface->get_image_position = webkit_accessible_image_get_image_position;
+ iface->get_image_description = webkit_accessible_image_get_image_description;
+ iface->get_image_size = webkit_accessible_image_get_image_size;
+}
+
+// Table
+
+static AccessibilityTableCell* cell(AtkTable* table, guint row, guint column)
+{
+ AccessibilityObject* accTable = core(table);
+ if (accTable->isAccessibilityRenderObject())
+ return static_cast<AccessibilityTable*>(accTable)->cellForColumnAndRow(column, row);
+ return 0;
+}
+
+static gint cellIndex(AccessibilityTableCell* axCell, AccessibilityTable* axTable)
+{
+ // Calculate the cell's index as if we had a traditional Gtk+ table in
+ // which cells are all direct children of the table, arranged row-first.
+ AccessibilityObject::AccessibilityChildrenVector allCells;
+ axTable->cells(allCells);
+ AccessibilityObject::AccessibilityChildrenVector::iterator position;
+ position = std::find(allCells.begin(), allCells.end(), axCell);
+ if (position == allCells.end())
+ return -1;
+ return position - allCells.begin();
+}
+
+static AccessibilityTableCell* cellAtIndex(AtkTable* table, gint index)
+{
+ AccessibilityObject* accTable = core(table);
+ if (accTable->isAccessibilityRenderObject()) {
+ AccessibilityObject::AccessibilityChildrenVector allCells;
+ static_cast<AccessibilityTable*>(accTable)->cells(allCells);
+ if (0 <= index && static_cast<unsigned>(index) < allCells.size()) {
+ AccessibilityObject* accCell = allCells.at(index).get();
+ return static_cast<AccessibilityTableCell*>(accCell);
+ }
+ }
+ return 0;
+}
+
+static AtkObject* webkit_accessible_table_ref_at(AtkTable* table, gint row, gint column)
+{
+ AccessibilityTableCell* axCell = cell(table, row, column);
+ if (!axCell)
+ return 0;
+ return axCell->wrapper();
+}
+
+static gint webkit_accessible_table_get_index_at(AtkTable* table, gint row, gint column)
+{
+ AccessibilityTableCell* axCell = cell(table, row, column);
+ AccessibilityTable* axTable = static_cast<AccessibilityTable*>(core(table));
+ return cellIndex(axCell, axTable);
+}
+
+static gint webkit_accessible_table_get_column_at_index(AtkTable* table, gint index)
+{
+ AccessibilityTableCell* axCell = cellAtIndex(table, index);
+ if (axCell){
+ pair<int, int> columnRange;
+ axCell->columnIndexRange(columnRange);
+ return columnRange.first;
+ }
+ return -1;
+}
+
+static gint webkit_accessible_table_get_row_at_index(AtkTable* table, gint index)
+{
+ AccessibilityTableCell* axCell = cellAtIndex(table, index);
+ if (axCell){
+ pair<int, int> rowRange;
+ axCell->rowIndexRange(rowRange);
+ return rowRange.first;
+ }
+ return -1;
+}
+
+static gint webkit_accessible_table_get_n_columns(AtkTable* table)
+{
+ AccessibilityObject* accTable = core(table);
+ if (accTable->isAccessibilityRenderObject())
+ return static_cast<AccessibilityTable*>(accTable)->columnCount();
+ return 0;
+}
+
+static gint webkit_accessible_table_get_n_rows(AtkTable* table)
+{
+ AccessibilityObject* accTable = core(table);
+ if (accTable->isAccessibilityRenderObject())
+ return static_cast<AccessibilityTable*>(accTable)->rowCount();
+ return 0;
+}
+
+static gint webkit_accessible_table_get_column_extent_at(AtkTable* table, gint row, gint column)
+{
+ AccessibilityTableCell* axCell = cell(table, row, column);
+ if (axCell) {
+ pair<int, int> columnRange;
+ axCell->columnIndexRange(columnRange);
+ return columnRange.second;
+ }
+ return 0;
+}
+
+static gint webkit_accessible_table_get_row_extent_at(AtkTable* table, gint row, gint column)
+{
+ AccessibilityTableCell* axCell = cell(table, row, column);
+ if (axCell) {
+ pair<int, int> rowRange;
+ axCell->rowIndexRange(rowRange);
+ return rowRange.second;
+ }
+ return 0;
+}
+
+static AtkObject* webkit_accessible_table_get_column_header(AtkTable* table, gint column)
+{
+ AccessibilityObject* accTable = core(table);
+ if (accTable->isAccessibilityRenderObject()) {
+ AccessibilityObject::AccessibilityChildrenVector allColumnHeaders;
+ static_cast<AccessibilityTable*>(accTable)->columnHeaders(allColumnHeaders);
+ unsigned columnCount = allColumnHeaders.size();
+ for (unsigned k = 0; k < columnCount; ++k) {
+ pair<int, int> columnRange;
+ AccessibilityTableCell* cell = static_cast<AccessibilityTableCell*>(allColumnHeaders.at(k).get());
+ cell->columnIndexRange(columnRange);
+ if (columnRange.first <= column && column < columnRange.first + columnRange.second)
+ return allColumnHeaders[k]->wrapper();
+ }
+ }
+ return 0;
+}
+
+static AtkObject* webkit_accessible_table_get_row_header(AtkTable* table, gint row)
+{
+ AccessibilityObject* accTable = core(table);
+ if (accTable->isAccessibilityRenderObject()) {
+ AccessibilityObject::AccessibilityChildrenVector allRowHeaders;
+ static_cast<AccessibilityTable*>(accTable)->rowHeaders(allRowHeaders);
+ unsigned rowCount = allRowHeaders.size();
+ for (unsigned k = 0; k < rowCount; ++k) {
+ pair<int, int> rowRange;
+ AccessibilityTableCell* cell = static_cast<AccessibilityTableCell*>(allRowHeaders.at(k).get());
+ cell->rowIndexRange(rowRange);
+ if (rowRange.first <= row && row < rowRange.first + rowRange.second)
+ return allRowHeaders[k]->wrapper();
+ }
+ }
+ return 0;
+}
+
+static AtkObject* webkit_accessible_table_get_caption(AtkTable* table)
+{
+ AccessibilityObject* accTable = core(table);
+ if (accTable->isAccessibilityRenderObject()) {
+ Node* node = static_cast<AccessibilityRenderObject*>(accTable)->renderer()->node();
+ if (node && node->hasTagName(HTMLNames::tableTag)) {
+ HTMLTableCaptionElement* caption = static_cast<HTMLTableElement*>(node)->caption();
+ if (caption)
+ return AccessibilityObject::firstAccessibleObjectFromNode(caption->renderer()->node())->wrapper();
+ }
+ }
+ return 0;
+}
+
+static const gchar* webkit_accessible_table_get_column_description(AtkTable* table, gint column)
+{
+ AtkObject* columnHeader = atk_table_get_column_header(table, column);
+ if (columnHeader && ATK_IS_TEXT(columnHeader))
+ return webkit_accessible_text_get_text(ATK_TEXT(columnHeader), 0, -1);
+
+ return 0;
+}
+
+static const gchar* webkit_accessible_table_get_row_description(AtkTable* table, gint row)
+{
+ AtkObject* rowHeader = atk_table_get_row_header(table, row);
+ if (rowHeader && ATK_IS_TEXT(rowHeader))
+ return webkit_accessible_text_get_text(ATK_TEXT(rowHeader), 0, -1);
+
+ return 0;
+}
+
+static void atk_table_interface_init(AtkTableIface* iface)
+{
+ iface->ref_at = webkit_accessible_table_ref_at;
+ iface->get_index_at = webkit_accessible_table_get_index_at;
+ iface->get_column_at_index = webkit_accessible_table_get_column_at_index;
+ iface->get_row_at_index = webkit_accessible_table_get_row_at_index;
+ iface->get_n_columns = webkit_accessible_table_get_n_columns;
+ iface->get_n_rows = webkit_accessible_table_get_n_rows;
+ iface->get_column_extent_at = webkit_accessible_table_get_column_extent_at;
+ iface->get_row_extent_at = webkit_accessible_table_get_row_extent_at;
+ iface->get_column_header = webkit_accessible_table_get_column_header;
+ iface->get_row_header = webkit_accessible_table_get_row_header;
+ iface->get_caption = webkit_accessible_table_get_caption;
+ iface->get_column_description = webkit_accessible_table_get_column_description;
+ iface->get_row_description = webkit_accessible_table_get_row_description;
+}
+
+static AtkHyperlink* webkitAccessibleHypertextGetLink(AtkHypertext* hypertext, gint index)
+{
+ AccessibilityObject::AccessibilityChildrenVector children = core(hypertext)->children();
+ if (index < 0 || static_cast<unsigned>(index) >= children.size())
+ return 0;
+
+ gint currentLink = -1;
+ for (unsigned i = 0; i < children.size(); i++) {
+ AccessibilityObject* coreChild = children.at(i).get();
+ if (!coreChild->accessibilityIsIgnored() && coreChild->isLink()) {
+ currentLink++;
+ if (index != currentLink)
+ continue;
+
+ AtkObject* axObject = coreChild->wrapper();
+ if (!axObject || !ATK_IS_HYPERLINK_IMPL(axObject))
+ return 0;
+
+ return atk_hyperlink_impl_get_hyperlink(ATK_HYPERLINK_IMPL(axObject));
+ }
+ }
+
+ return 0;
+}
+
+static gint webkitAccessibleHypertextGetNLinks(AtkHypertext* hypertext)
+{
+ AccessibilityObject::AccessibilityChildrenVector children = core(hypertext)->children();
+ if (!children.size())
+ return 0;
+
+ gint linksFound = 0;
+ for (size_t i = 0; i < children.size(); i++) {
+ AccessibilityObject* coreChild = children.at(i).get();
+ if (!coreChild->accessibilityIsIgnored() && coreChild->isLink())
+ linksFound++;
+ }
+
+ return linksFound;
+}
+
+static gint webkitAccessibleHypertextGetLinkIndex(AtkHypertext* hypertext, gint charIndex)
+{
+ size_t linksCount = webkitAccessibleHypertextGetNLinks(hypertext);
+ if (!linksCount)
+ return -1;
+
+ for (size_t i = 0; i < linksCount; i++) {
+ AtkHyperlink* hyperlink = ATK_HYPERLINK(webkitAccessibleHypertextGetLink(hypertext, i));
+ gint startIndex = atk_hyperlink_get_start_index(hyperlink);
+ gint endIndex = atk_hyperlink_get_end_index(hyperlink);
+
+ // Check if the char index in the link's offset range
+ if (startIndex <= charIndex && charIndex < endIndex)
+ return i;
+ }
+
+ // Not found if reached
+ return -1;
+}
+
+static void atkHypertextInterfaceInit(AtkHypertextIface* iface)
+{
+ iface->get_link = webkitAccessibleHypertextGetLink;
+ iface->get_n_links = webkitAccessibleHypertextGetNLinks;
+ iface->get_link_index = webkitAccessibleHypertextGetLinkIndex;
+}
+
+static AtkHyperlink* webkitAccessibleHyperlinkImplGetHyperlink(AtkHyperlinkImpl* hyperlink)
+{
+ AtkHyperlink* hyperlinkObject = ATK_HYPERLINK(g_object_get_data(G_OBJECT(hyperlink), "hyperlink-object"));
+ if (!hyperlinkObject) {
+ hyperlinkObject = ATK_HYPERLINK(webkitAccessibleHyperlinkNew(hyperlink));
+ g_object_set_data(G_OBJECT(hyperlink), "hyperlink-object", hyperlinkObject);
+ }
+ return hyperlinkObject;
+}
+
+static void atkHyperlinkImplInterfaceInit(AtkHyperlinkImplIface* iface)
+{
+ iface->get_hyperlink = webkitAccessibleHyperlinkImplGetHyperlink;
+}
+
+static const gchar* documentAttributeValue(AtkDocument* document, const gchar* attribute)
+{
+ Document* coreDocument = core(document)->document();
+ if (!coreDocument)
+ return 0;
+
+ String value = String();
+ if (!g_ascii_strcasecmp(attribute, "DocType") && coreDocument->doctype())
+ value = coreDocument->doctype()->name();
+ else if (!g_ascii_strcasecmp(attribute, "Encoding"))
+ value = coreDocument->charset();
+ else if (!g_ascii_strcasecmp(attribute, "URI"))
+ value = coreDocument->documentURI();
+ if (!value.isEmpty())
+ return returnString(value);
+
+ return 0;
+}
+
+static const gchar* webkit_accessible_document_get_attribute_value(AtkDocument* document, const gchar* attribute)
+{
+ return documentAttributeValue(document, attribute);
+}
+
+static AtkAttributeSet* webkit_accessible_document_get_attributes(AtkDocument* document)
+{
+ AtkAttributeSet* attributeSet = 0;
+ const gchar* attributes [] = {"DocType", "Encoding", "URI"};
+
+ for (unsigned i = 0; i < G_N_ELEMENTS(attributes); i++) {
+ const gchar* value = documentAttributeValue(document, attributes[i]);
+ if (value)
+ attributeSet = addAttributeToSet(attributeSet, attributes[i], value);
+ }
+
+ return attributeSet;
+}
+
+static const gchar* webkit_accessible_document_get_locale(AtkDocument* document)
+{
+
+ // TODO: Should we fall back on lang xml:lang when the following comes up empty?
+ String language = core(document)->language();
+ if (!language.isEmpty())
+ return returnString(language);
+
+ return 0;
+}
+
+static void atk_document_interface_init(AtkDocumentIface* iface)
+{
+ iface->get_document_attribute_value = webkit_accessible_document_get_attribute_value;
+ iface->get_document_attributes = webkit_accessible_document_get_attributes;
+ iface->get_document_locale = webkit_accessible_document_get_locale;
+}
+
+static const GInterfaceInfo AtkInterfacesInitFunctions[] = {
+ {(GInterfaceInitFunc)atk_action_interface_init,
+ (GInterfaceFinalizeFunc) 0, 0},
+ {(GInterfaceInitFunc)atk_selection_interface_init,
+ (GInterfaceFinalizeFunc) 0, 0},
+ {(GInterfaceInitFunc)atk_editable_text_interface_init,
+ (GInterfaceFinalizeFunc) 0, 0},
+ {(GInterfaceInitFunc)atk_text_interface_init,
+ (GInterfaceFinalizeFunc) 0, 0},
+ {(GInterfaceInitFunc)atk_component_interface_init,
+ (GInterfaceFinalizeFunc) 0, 0},
+ {(GInterfaceInitFunc)atk_image_interface_init,
+ (GInterfaceFinalizeFunc) 0, 0},
+ {(GInterfaceInitFunc)atk_table_interface_init,
+ (GInterfaceFinalizeFunc) 0, 0},
+ {(GInterfaceInitFunc)atkHypertextInterfaceInit,
+ (GInterfaceFinalizeFunc) 0, 0},
+ {(GInterfaceInitFunc)atkHyperlinkImplInterfaceInit,
+ (GInterfaceFinalizeFunc) 0, 0},
+ {(GInterfaceInitFunc)atk_document_interface_init,
+ (GInterfaceFinalizeFunc) 0, 0}
+};
+
+enum WAIType {
+ WAI_ACTION,
+ WAI_SELECTION,
+ WAI_EDITABLE_TEXT,
+ WAI_TEXT,
+ WAI_COMPONENT,
+ WAI_IMAGE,
+ WAI_TABLE,
+ WAI_HYPERTEXT,
+ WAI_HYPERLINK,
+ WAI_DOCUMENT
+};
+
+static GType GetAtkInterfaceTypeFromWAIType(WAIType type)
+{
+ switch (type) {
+ case WAI_ACTION:
+ return ATK_TYPE_ACTION;
+ case WAI_SELECTION:
+ return ATK_TYPE_SELECTION;
+ case WAI_EDITABLE_TEXT:
+ return ATK_TYPE_EDITABLE_TEXT;
+ case WAI_TEXT:
+ return ATK_TYPE_TEXT;
+ case WAI_COMPONENT:
+ return ATK_TYPE_COMPONENT;
+ case WAI_IMAGE:
+ return ATK_TYPE_IMAGE;
+ case WAI_TABLE:
+ return ATK_TYPE_TABLE;
+ case WAI_HYPERTEXT:
+ return ATK_TYPE_HYPERTEXT;
+ case WAI_HYPERLINK:
+ return ATK_TYPE_HYPERLINK_IMPL;
+ case WAI_DOCUMENT:
+ return ATK_TYPE_DOCUMENT;
+ }
+
+ return G_TYPE_INVALID;
+}
+
+static guint16 getInterfaceMaskFromObject(AccessibilityObject* coreObject)
+{
+ guint16 interfaceMask = 0;
+
+ // Component interface is always supported
+ interfaceMask |= 1 << WAI_COMPONENT;
+
+ AccessibilityRole role = coreObject->roleValue();
+
+ // Action
+ // As the implementation of the AtkAction interface is a very
+ // basic one (just relays in executing the default action for each
+ // object, and only supports having one action per object), it is
+ // better just to implement this interface for every instance of
+ // the WebKitAccessible class and let WebCore decide what to do.
+ interfaceMask |= 1 << WAI_ACTION;
+
+ // Hyperlink
+ if (coreObject->isLink())
+ interfaceMask |= 1 << WAI_HYPERLINK;
+
+ // Selection
+ if (coreObject->isListBox() || coreObject->isMenuList())
+ interfaceMask |= 1 << WAI_SELECTION;
+
+ // Text & Editable Text
+ if (role == StaticTextRole || coreObject->isMenuListOption())
+ interfaceMask |= 1 << WAI_TEXT;
+ else if (coreObject->isAccessibilityRenderObject()) {
+ if (coreObject->isTextControl()) {
+ interfaceMask |= 1 << WAI_TEXT;
+ if (!coreObject->isReadOnly())
+ interfaceMask |= 1 << WAI_EDITABLE_TEXT;
+ } else {
+ AccessibilityRenderObject* axRenderObject = static_cast<AccessibilityRenderObject*>(coreObject);
+ RenderObject* renderer = axRenderObject->renderer();
+ if (role != TableRole) {
+ interfaceMask |= 1 << WAI_HYPERTEXT;
+ if (renderer && renderer->childrenInline())
+ interfaceMask |= 1 << WAI_TEXT;
+ }
+
+ // Add the TEXT interface for list items whose
+ // first accessible child has a text renderer
+ if (role == ListItemRole) {
+ AccessibilityObject::AccessibilityChildrenVector children = axRenderObject->children();
+ if (children.size()) {
+ AccessibilityObject* axRenderChild = children.at(0).get();
+ interfaceMask |= getInterfaceMaskFromObject(axRenderChild);
+ }
+ }
+ }
+ }
+
+ // Image
+ if (coreObject->isImage())
+ interfaceMask |= 1 << WAI_IMAGE;
+
+ // Table
+ if (role == TableRole)
+ interfaceMask |= 1 << WAI_TABLE;
+
+ // Document
+ if (role == WebAreaRole)
+ interfaceMask |= 1 << WAI_DOCUMENT;
+
+ return interfaceMask;
+}
+
+static const char* getUniqueAccessibilityTypeName(guint16 interfaceMask)
+{
+#define WAI_TYPE_NAME_LEN (30) /* Enough for prefix + 5 hex characters (max) */
+ static char name[WAI_TYPE_NAME_LEN + 1];
+
+ g_sprintf(name, "WAIType%x", interfaceMask);
+ name[WAI_TYPE_NAME_LEN] = '\0';
+
+ return name;
+}
+
+static GType getAccessibilityTypeFromObject(AccessibilityObject* coreObject)
+{
+ static const GTypeInfo typeInfo = {
+ sizeof(WebKitAccessibleClass),
+ (GBaseInitFunc) 0,
+ (GBaseFinalizeFunc) 0,
+ (GClassInitFunc) 0,
+ (GClassFinalizeFunc) 0,
+ 0, /* class data */
+ sizeof(WebKitAccessible), /* instance size */
+ 0, /* nb preallocs */
+ (GInstanceInitFunc) 0,
+ 0 /* value table */
+ };
+
+ guint16 interfaceMask = getInterfaceMaskFromObject(coreObject);
+ const char* atkTypeName = getUniqueAccessibilityTypeName(interfaceMask);
+ GType type = g_type_from_name(atkTypeName);
+ if (type)
+ return type;
+
+ type = g_type_register_static(WEBKIT_TYPE_ACCESSIBLE,
+ atkTypeName,
+ &typeInfo, GTypeFlags(0));
+ for (guint i = 0; i < G_N_ELEMENTS(AtkInterfacesInitFunctions); i++) {
+ if (interfaceMask & (1 << i))
+ g_type_add_interface_static(type,
+ GetAtkInterfaceTypeFromWAIType(static_cast<WAIType>(i)),
+ &AtkInterfacesInitFunctions[i]);
+ }
+
+ return type;
+}
+
+WebKitAccessible* webkit_accessible_new(AccessibilityObject* coreObject)
+{
+ GType type = getAccessibilityTypeFromObject(coreObject);
+ AtkObject* object = static_cast<AtkObject*>(g_object_new(type, 0));
+
+ atk_object_initialize(object, coreObject);
+
+ return WEBKIT_ACCESSIBLE(object);
+}
+
+AccessibilityObject* webkit_accessible_get_accessibility_object(WebKitAccessible* accessible)
+{
+ return accessible->m_object;
+}
+
+void webkit_accessible_detach(WebKitAccessible* accessible)
+{
+ ASSERT(accessible->m_object);
+
+ // We replace the WebCore AccessibilityObject with a fallback object that
+ // provides default implementations to avoid repetitive null-checking after
+ // detachment.
+ accessible->m_object = fallbackObject();
+}
+
+AtkObject* webkit_accessible_get_focused_element(WebKitAccessible* accessible)
+{
+ if (!accessible->m_object)
+ return 0;
+
+ RefPtr<AccessibilityObject> focusedObj = accessible->m_object->focusedUIElement();
+ if (!focusedObj)
+ return 0;
+
+ return focusedObj->wrapper();
+}
+
+AccessibilityObject* objectAndOffsetUnignored(AccessibilityObject* coreObject, int& offset, bool ignoreLinks)
+{
+ Node* endNode = static_cast<AccessibilityRenderObject*>(coreObject)->renderer()->node();
+ int endOffset = coreObject->selection().end().computeOffsetInContainerNode();
+ // Indication that something bogus has transpired.
+ offset = -1;
+
+ AccessibilityObject* realObject = coreObject;
+ if (realObject->accessibilityIsIgnored())
+ realObject = realObject->parentObjectUnignored();
+ if (!realObject)
+ return 0;
+
+ if (ignoreLinks && realObject->isLink())
+ realObject = realObject->parentObjectUnignored();
+ if (!realObject)
+ return 0;
+
+ Node* node = static_cast<AccessibilityRenderObject*>(realObject)->renderer()->node();
+ if (node) {
+ RefPtr<Range> range = rangeOfContents(node);
+ if (range->ownerDocument() == node->document()) {
+ ExceptionCode ec = 0;
+ range->setEndBefore(endNode, ec);
+ if (range->boundaryPointsValid())
+ offset = range->text().length() + endOffset;
+ }
+ }
+ return realObject;
+}
+
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.h b/Source/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.h
new file mode 100644
index 0000000..e8cec08
--- /dev/null
+++ b/Source/WebCore/accessibility/gtk/AccessibilityObjectWrapperAtk.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2008 Nuanti Ltd.
+ * Copyright (C) 2009 Jan Alonzo
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef AccessibilityObjectWrapperAtk_h
+#define AccessibilityObjectWrapperAtk_h
+
+#include <atk/atk.h>
+
+namespace WebCore {
+ class AccessibilityObject;
+}
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_ACCESSIBLE (webkit_accessible_get_type ())
+#define WEBKIT_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_ACCESSIBLE, WebKitAccessible))
+#define WEBKIT_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_ACCESSIBLE, WebKitAccessibleClass))
+#define WEBKIT_IS_ACCESSIBLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_ACCESSIBLE))
+#define WEBKIT_IS_ACCESSIBLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WEBKIT_TYPE_ACCESSIBLE))
+#define WEBKIT_ACCESSIBLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_ACCESSIBLE, WebKitAccessibleClass))
+
+typedef struct _WebKitAccessible WebKitAccessible;
+typedef struct _WebKitAccessibleClass WebKitAccessibleClass;
+
+struct _WebKitAccessible
+{
+ AtkObject parent;
+ WebCore::AccessibilityObject* m_object;
+};
+
+struct _WebKitAccessibleClass
+{
+ AtkObjectClass parent_class;
+};
+
+GType webkit_accessible_get_type (void) G_GNUC_CONST;
+
+WebKitAccessible* webkit_accessible_new (WebCore::AccessibilityObject* core_object);
+
+WebCore::AccessibilityObject* webkit_accessible_get_accessibility_object (WebKitAccessible* accessible);
+
+void webkit_accessible_detach (WebKitAccessible* accessible);
+
+AtkObject* webkit_accessible_get_focused_element(WebKitAccessible* accessible);
+
+WebCore::AccessibilityObject* objectAndOffsetUnignored(WebCore::AccessibilityObject* coreObject, int& offset, bool ignoreLinks);
+
+G_END_DECLS
+
+#endif // AccessibilityObjectWrapperAtk_h
diff --git a/Source/WebCore/accessibility/gtk/WebKitAccessibleHyperlink.cpp b/Source/WebCore/accessibility/gtk/WebKitAccessibleHyperlink.cpp
new file mode 100644
index 0000000..5927430
--- /dev/null
+++ b/Source/WebCore/accessibility/gtk/WebKitAccessibleHyperlink.cpp
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2010 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "WebKitAccessibleHyperlink.h"
+
+#if HAVE(ACCESSIBILITY)
+
+#include "AXObjectCache.h"
+#include "AccessibilityObject.h"
+#include "AccessibilityObjectWrapperAtk.h"
+#include "AccessibilityRenderObject.h"
+#include "NotImplemented.h"
+#include "Position.h"
+#include "Range.h"
+#include "RenderListMarker.h"
+#include "RenderObject.h"
+#include "TextIterator.h"
+
+#include <atk/atk.h>
+#include <glib.h>
+
+using namespace WebCore;
+
+struct _WebKitAccessibleHyperlinkPrivate {
+ WebKitAccessible* hyperlinkImpl;
+};
+
+#define WEBKIT_ACCESSIBLE_HYPERLINK_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_ACCESSIBLE_HYPERLINK, WebKitAccessibleHyperlinkPrivate))
+
+enum {
+ PROP_0,
+
+ PROP_HYPERLINK_IMPL
+};
+
+static gpointer webkitAccessibleHyperlinkParentClass = 0;
+
+// Used to provide const char* returns.
+static const char* returnString(const String& str)
+{
+ static CString returnedString;
+ returnedString = str.utf8();
+ return returnedString.data();
+}
+
+static AccessibilityObject* core(WebKitAccessible* accessible)
+{
+ if (!accessible || !WEBKIT_IS_ACCESSIBLE(accessible))
+ return 0;
+
+ return webkit_accessible_get_accessibility_object(accessible);
+}
+
+static AccessibilityObject* core(WebKitAccessibleHyperlink* link)
+{
+ if (!link)
+ return 0;
+
+ return core(link->priv->hyperlinkImpl);
+}
+
+static AccessibilityObject* core(AtkHyperlink* link)
+{
+ if (!WEBKIT_IS_ACCESSIBLE_HYPERLINK(link))
+ return 0;
+
+ return core(WEBKIT_ACCESSIBLE_HYPERLINK(link));
+}
+
+static AccessibilityObject* core(AtkAction* action)
+{
+ return core(WEBKIT_ACCESSIBLE_HYPERLINK(action));
+}
+
+
+static gboolean webkitAccessibleHyperlinkActionDoAction(AtkAction* action, gint index)
+{
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(action), FALSE);
+ g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl, FALSE);
+ g_return_val_if_fail(!index, FALSE);
+
+ if (!ATK_IS_ACTION(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl))
+ return FALSE;
+
+ AccessibilityObject* coreObject = core(action);
+ if (!coreObject)
+ return FALSE;
+
+ return coreObject->performDefaultAction();
+}
+
+static gint webkitAccessibleHyperlinkActionGetNActions(AtkAction* action)
+{
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(action), 0);
+ g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl, 0);
+
+ if (!ATK_IS_ACTION(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl))
+ return 0;
+
+ return 1;
+}
+
+static const gchar* webkitAccessibleHyperlinkActionGetDescription(AtkAction* action, gint index)
+{
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(action), 0);
+ g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl, 0);
+ g_return_val_if_fail(!index, 0);
+
+ // TODO: Need a way to provide/localize action descriptions.
+ notImplemented();
+ return "";
+}
+
+static const gchar* webkitAccessibleHyperlinkActionGetKeybinding(AtkAction* action, gint index)
+{
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(action), 0);
+ g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl, 0);
+ g_return_val_if_fail(!index, 0);
+
+ if (!ATK_IS_ACTION(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl))
+ return 0;
+
+ AccessibilityObject* coreObject = core(action);
+ if (!coreObject)
+ return 0;
+
+ return returnString(coreObject->accessKey().string());
+}
+
+static const gchar* webkitAccessibleHyperlinkActionGetName(AtkAction* action, gint index)
+{
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(action), 0);
+ g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl, 0);
+ g_return_val_if_fail(!index, 0);
+
+ if (!ATK_IS_ACTION(WEBKIT_ACCESSIBLE_HYPERLINK(action)->priv->hyperlinkImpl))
+ return 0;
+
+ AccessibilityObject* coreObject = core(action);
+ if (!coreObject)
+ return 0;
+
+ return returnString(coreObject->actionVerb());
+}
+
+static void atkActionInterfaceInit(AtkActionIface* iface)
+{
+ iface->do_action = webkitAccessibleHyperlinkActionDoAction;
+ iface->get_n_actions = webkitAccessibleHyperlinkActionGetNActions;
+ iface->get_description = webkitAccessibleHyperlinkActionGetDescription;
+ iface->get_keybinding = webkitAccessibleHyperlinkActionGetKeybinding;
+ iface->get_name = webkitAccessibleHyperlinkActionGetName;
+}
+
+static gchar* webkitAccessibleHyperlinkGetURI(AtkHyperlink* link, gint index)
+{
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), 0);
+ // FIXME: Do NOT support more than one instance of an AtkObject
+ // implementing AtkHyperlinkImpl in every instance of AtkHyperLink
+ g_return_val_if_fail(!index, 0);
+
+ AccessibilityObject* coreObject = core(link);
+ if (!coreObject || coreObject->url().isNull())
+ return 0;
+
+ return g_strdup(returnString(coreObject->url().string()));
+}
+
+static AtkObject* webkitAccessibleHyperlinkGetObject(AtkHyperlink* link, gint index)
+{
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), 0);
+ g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, 0);
+
+ // FIXME: Do NOT support more than one instance of an AtkObject
+ // implementing AtkHyperlinkImpl in every instance of AtkHyperLink
+ g_return_val_if_fail(!index, 0);
+
+ return ATK_OBJECT(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl);
+}
+
+static gint getRangeLengthForObject(AccessibilityObject* obj, Range* range)
+{
+ // This is going to be the actual length in most of the cases
+ int baseLength = TextIterator::rangeLength(range);
+
+ // Check whether the current hyperlink belongs to a list item.
+ // If so, we need to consider the length of the item's marker
+ AccessibilityObject* parent = obj->parentObjectUnignored();
+ if (!parent || !parent->isAccessibilityRenderObject() || !parent->isListItem())
+ return baseLength;
+
+ // Even if we don't expose list markers to Assistive
+ // Technologies, we need to have a way to measure their length
+ // for those cases when it's needed to take it into account
+ // separately (as in getAccessibilityObjectForOffset)
+ AccessibilityObject* markerObj = parent->firstChild();
+ if (!markerObj)
+ return baseLength;
+
+ RenderObject* renderer = static_cast<const AccessibilityRenderObject*>(markerObj)->renderer();
+ if (!renderer || !renderer->isListMarker())
+ return baseLength;
+
+ RenderListMarker* marker = toRenderListMarker(renderer);
+ return baseLength + marker->text().length() + marker->suffix().length();
+}
+
+static gint webkitAccessibleHyperlinkGetStartIndex(AtkHyperlink* link)
+{
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), 0);
+
+ AccessibilityObject* coreObject = core(link);
+ if (!coreObject)
+ return 0;
+
+ Node* node = coreObject->node();
+ if (!node)
+ return 0;
+
+ RefPtr<Range> range = Range::create(node->document(), firstPositionInNode(node->parentNode()), firstPositionInNode(node));
+ return getRangeLengthForObject(coreObject, range.get());
+}
+
+static gint webkitAccessibleHyperlinkGetEndIndex(AtkHyperlink* link)
+{
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), 0);
+
+ AccessibilityObject* coreObject = core(link);
+ if (!coreObject)
+ return 0;
+
+ Node* node = coreObject->node();
+ if (!node)
+ return 0;
+
+ RefPtr<Range> range = Range::create(node->document(), firstPositionInNode(node->parentNode()), lastPositionInNode(node));
+ return getRangeLengthForObject(coreObject, range.get());
+}
+
+static gboolean webkitAccessibleHyperlinkIsValid(AtkHyperlink* link)
+{
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), 0);
+ g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, FALSE);
+
+ // Link is valid for the whole object's lifetime
+ return TRUE;
+}
+
+static gint webkitAccessibleHyperlinkGetNAnchors(AtkHyperlink* link)
+{
+ // FIXME Do NOT support more than one instance of an AtkObject
+ // implementing AtkHyperlinkImpl in every instance of AtkHyperLink
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), 0);
+ g_return_val_if_fail(WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl, 0);
+ return 1;
+}
+
+static gboolean webkitAccessibleHyperlinkIsSelectedLink(AtkHyperlink* link)
+{
+ // Not implemented: this function is deprecated in ATK now
+ notImplemented();
+ return FALSE;
+}
+
+static void webkitAccessibleHyperlinkGetProperty(GObject* object, guint propId, GValue* value, GParamSpec* pspec)
+{
+ switch (propId) {
+ case PROP_HYPERLINK_IMPL:
+ g_value_set_object(value, WEBKIT_ACCESSIBLE_HYPERLINK(object)->priv->hyperlinkImpl);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
+ }
+}
+
+static void webkitAccessibleHyperlinkSetProperty(GObject* object, guint propId, const GValue* value, GParamSpec* pspec)
+{
+ WebKitAccessibleHyperlinkPrivate* priv = WEBKIT_ACCESSIBLE_HYPERLINK(object)->priv;
+
+ switch (propId) {
+ case PROP_HYPERLINK_IMPL:
+ // No need to check and unref previous values of
+ // priv->hyperlinkImpl as this is a CONSTRUCT ONLY property
+ priv->hyperlinkImpl = WEBKIT_ACCESSIBLE(g_value_get_object(value));
+ g_object_weak_ref(G_OBJECT(priv->hyperlinkImpl), (GWeakNotify)g_object_unref, object);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propId, pspec);
+ }
+}
+
+static void webkitAccessibleHyperlinkFinalize(GObject* object)
+{
+ G_OBJECT_CLASS(webkitAccessibleHyperlinkParentClass)->finalize(object);
+}
+
+static void webkitAccessibleHyperlinkClassInit(AtkHyperlinkClass* klass)
+{
+ GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
+
+ webkitAccessibleHyperlinkParentClass = g_type_class_peek_parent(klass);
+
+ gobjectClass->finalize = webkitAccessibleHyperlinkFinalize;
+ gobjectClass->set_property = webkitAccessibleHyperlinkSetProperty;
+ gobjectClass->get_property = webkitAccessibleHyperlinkGetProperty;
+
+ klass->get_uri = webkitAccessibleHyperlinkGetURI;
+ klass->get_object = webkitAccessibleHyperlinkGetObject;
+ klass->get_start_index = webkitAccessibleHyperlinkGetStartIndex;
+ klass->get_end_index = webkitAccessibleHyperlinkGetEndIndex;
+ klass->is_valid = webkitAccessibleHyperlinkIsValid;
+ klass->get_n_anchors = webkitAccessibleHyperlinkGetNAnchors;
+ klass->is_selected_link = webkitAccessibleHyperlinkIsSelectedLink;
+
+ g_object_class_install_property(gobjectClass, PROP_HYPERLINK_IMPL,
+ g_param_spec_object("hyperlink-impl",
+ "Hyperlink implementation",
+ "The associated WebKitAccessible instance.",
+ WEBKIT_TYPE_ACCESSIBLE,
+ (GParamFlags)(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)));
+
+ g_type_class_add_private(gobjectClass, sizeof(WebKitAccessibleHyperlinkPrivate));
+}
+
+static void webkitAccessibleHyperlinkInit(AtkHyperlink* link)
+{
+ WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv = WEBKIT_ACCESSIBLE_HYPERLINK_GET_PRIVATE(link);
+ WEBKIT_ACCESSIBLE_HYPERLINK(link)->priv->hyperlinkImpl = 0;
+}
+
+GType webkitAccessibleHyperlinkGetType()
+{
+ static volatile gsize typeVolatile = 0;
+
+ if (g_once_init_enter(&typeVolatile)) {
+ static const GTypeInfo tinfo = {
+ sizeof(WebKitAccessibleHyperlinkClass),
+ (GBaseInitFunc) 0,
+ (GBaseFinalizeFunc) 0,
+ (GClassInitFunc) webkitAccessibleHyperlinkClassInit,
+ (GClassFinalizeFunc) 0,
+ 0, /* class data */
+ sizeof(WebKitAccessibleHyperlink), /* instance size */
+ 0, /* nb preallocs */
+ (GInstanceInitFunc) webkitAccessibleHyperlinkInit,
+ 0 /* value table */
+ };
+
+ static const GInterfaceInfo actionInfo = {
+ (GInterfaceInitFunc)(GInterfaceInitFunc)atkActionInterfaceInit,
+ (GInterfaceFinalizeFunc) 0, 0
+ };
+
+ GType type = g_type_register_static(ATK_TYPE_HYPERLINK, "WebKitAccessibleHyperlink", &tinfo, GTypeFlags(0));
+ g_type_add_interface_static(type, ATK_TYPE_ACTION, &actionInfo);
+
+ g_once_init_leave(&typeVolatile, type);
+ }
+
+ return typeVolatile;
+}
+
+WebKitAccessibleHyperlink* webkitAccessibleHyperlinkNew(AtkHyperlinkImpl* hyperlinkImpl)
+{
+ g_return_val_if_fail(ATK_IS_HYPERLINK_IMPL(hyperlinkImpl), 0);
+ return WEBKIT_ACCESSIBLE_HYPERLINK(g_object_new(WEBKIT_TYPE_ACCESSIBLE_HYPERLINK, "hyperlink-impl", hyperlinkImpl, 0));
+}
+
+WebCore::AccessibilityObject* webkitAccessibleHyperlinkGetAccessibilityObject(WebKitAccessibleHyperlink* link)
+{
+ g_return_val_if_fail(WEBKIT_IS_ACCESSIBLE_HYPERLINK(link), 0);
+ return core(link);
+}
+
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/gtk/WebKitAccessibleHyperlink.h b/Source/WebCore/accessibility/gtk/WebKitAccessibleHyperlink.h
new file mode 100644
index 0000000..9df819d
--- /dev/null
+++ b/Source/WebCore/accessibility/gtk/WebKitAccessibleHyperlink.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WebKitAccessibleHyperlink_h
+#define WebKitAccessibleHyperlink_h
+
+#include "AccessibilityObjectWrapperAtk.h"
+
+#include <atk/atk.h>
+
+namespace WebCore {
+class AccessibilityObject;
+}
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_ACCESSIBLE_HYPERLINK (webkitAccessibleHyperlinkGetType ())
+#define WEBKIT_ACCESSIBLE_HYPERLINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_ACCESSIBLE_HYPERLINK, WebKitAccessibleHyperlink))
+#define WEBKIT_ACCESSIBLE_HYPERLINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_ACCESSIBLE_HYPERLINK, WebKitAccessibleHyperlinkClass))
+#define WEBKIT_IS_ACCESSIBLE_HYPERLINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_ACCESSIBLE_HYPERLINK))
+#define WEBKIT_IS_ACCESSIBLE_HYPERLINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WEBKIT_TYPE_ACCESSIBLE_HYPERLINK))
+#define WEBKIT_ACCESSIBLE_HYPERLINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_ACCESSIBLE_HYPERLINK, WebKitAccessibleHyperlinkClass))
+
+typedef struct _WebKitAccessibleHyperlink WebKitAccessibleHyperlink;
+typedef struct _WebKitAccessibleHyperlinkClass WebKitAccessibleHyperlinkClass;
+typedef struct _WebKitAccessibleHyperlinkPrivate WebKitAccessibleHyperlinkPrivate;
+
+struct _WebKitAccessibleHyperlink {
+ AtkHyperlink parent;
+
+ // private
+ WebKitAccessibleHyperlinkPrivate *priv;
+};
+
+struct _WebKitAccessibleHyperlinkClass {
+ AtkObjectClass parentClass;
+};
+
+GType webkitAccessibleHyperlinkGetType(void) G_GNUC_CONST;
+
+WebKitAccessibleHyperlink* webkitAccessibleHyperlinkNew(AtkHyperlinkImpl* hyperlinkImpl);
+
+WebCore::AccessibilityObject* webkitAccessibleHyperlinkGetAccessibilityObject(WebKitAccessibleHyperlink* link);
+
+G_END_DECLS
+
+#endif // WebKitAccessibleHyperlink_h
diff --git a/Source/WebCore/accessibility/mac/AXObjectCacheMac.mm b/Source/WebCore/accessibility/mac/AXObjectCacheMac.mm
new file mode 100644
index 0000000..13c8ec2
--- /dev/null
+++ b/Source/WebCore/accessibility/mac/AXObjectCacheMac.mm
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR
+ * 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.
+ */
+
+#import "config.h"
+#import "AXObjectCache.h"
+
+#if HAVE(ACCESSIBILITY)
+
+#import "AccessibilityObject.h"
+#import "AccessibilityObjectWrapper.h"
+#import "RenderObject.h"
+#import "WebCoreViewFactory.h"
+
+#import <wtf/PassRefPtr.h>
+
+#ifndef NSAccessibilityLiveRegionChangedNotification
+#define NSAccessibilityLiveRegionChangedNotification @"AXLiveRegionChanged"
+#endif
+
+// The simple Cocoa calls in this file don't throw exceptions.
+
+namespace WebCore {
+
+void AXObjectCache::detachWrapper(AccessibilityObject* obj)
+{
+ [obj->wrapper() detach];
+ [obj->wrapper() release];
+ obj->setWrapper(0);
+}
+
+void AXObjectCache::attachWrapper(AccessibilityObject* obj)
+{
+ obj->setWrapper([[AccessibilityObjectWrapper alloc] initWithAccessibilityObject:obj]);
+}
+
+void AXObjectCache::postPlatformNotification(AccessibilityObject* obj, AXNotification notification)
+{
+ if (!obj)
+ return;
+
+ // Some notifications are unique to Safari and do not have NSAccessibility equivalents.
+ String macNotification;
+ switch (notification) {
+ case AXActiveDescendantChanged:
+ // An active descendant change for trees means a selected rows change.
+ if (obj->isTree())
+ macNotification = NSAccessibilitySelectedRowsChangedNotification;
+ else
+ macNotification = NSAccessibilityFocusedUIElementChangedNotification;
+ break;
+ case AXFocusedUIElementChanged:
+ macNotification = NSAccessibilityFocusedUIElementChangedNotification;
+ break;
+ case AXLayoutComplete:
+ macNotification = "AXLayoutComplete";
+ break;
+ case AXLoadComplete:
+ macNotification = "AXLoadComplete";
+ break;
+ case AXInvalidStatusChanged:
+ macNotification = "AXInvalidStatusChanged";
+ break;
+ case AXSelectedChildrenChanged:
+ macNotification = NSAccessibilitySelectedChildrenChangedNotification;
+ break;
+ case AXSelectedTextChanged:
+ macNotification = NSAccessibilitySelectedTextChangedNotification;
+ break;
+ case AXValueChanged:
+ macNotification = NSAccessibilityValueChangedNotification;
+ break;
+ case AXLiveRegionChanged:
+ macNotification = NSAccessibilityLiveRegionChangedNotification;
+ break;
+ case AXRowCountChanged:
+ macNotification = NSAccessibilityRowCountChangedNotification;
+ break;
+#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
+ case AXRowExpanded:
+ macNotification = NSAccessibilityRowExpandedNotification;
+ break;
+ case AXRowCollapsed:
+ macNotification = NSAccessibilityRowCollapsedNotification;
+ break;
+#endif
+ // Does not exist on Mac.
+ case AXCheckedStateChanged:
+ default:
+ return;
+ }
+
+ // NSAccessibilityPostNotification will call this method, (but not when running DRT), so ASSERT here to make sure it does not crash.
+ // https://bugs.webkit.org/show_bug.cgi?id=46662
+ ASSERT([obj->wrapper() accessibilityIsIgnored] || true);
+
+ NSAccessibilityPostNotification(obj->wrapper(), macNotification);
+
+ // Used by DRT to know when notifications are posted.
+ [obj->wrapper() accessibilityPostedNotification:macNotification];
+}
+
+void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, unsigned)
+{
+}
+
+void AXObjectCache::handleFocusedUIElementChanged(RenderObject*, RenderObject*)
+{
+ [[WebCoreViewFactory sharedFactory] accessibilityHandleFocusChanged];
+}
+
+void AXObjectCache::handleScrolledToAnchor(const Node*)
+{
+}
+
+}
+
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/mac/AccessibilityObjectMac.mm b/Source/WebCore/accessibility/mac/AccessibilityObjectMac.mm
new file mode 100644
index 0000000..5c3ee39
--- /dev/null
+++ b/Source/WebCore/accessibility/mac/AccessibilityObjectMac.mm
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2008 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR
+ * 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.
+ */
+
+#import "config.h"
+#import "AccessibilityObject.h"
+
+#if HAVE(ACCESSIBILITY)
+
+#import "AccessibilityObjectWrapper.h"
+#import "Widget.h"
+
+namespace WebCore {
+
+bool AccessibilityObject::accessibilityIgnoreAttachment() const
+{
+ // FrameView attachments are now handled by AccessibilityScrollView,
+ // so if this is the attachment, it should be ignored.
+ Widget* widget = 0;
+ if (isAttachment() && (widget = widgetForAttachmentView()) && widget->isFrameView())
+ return true;
+
+ if ([wrapper() attachmentView])
+ return [[wrapper() attachmentView] accessibilityIsIgnored];
+
+ // Attachments are ignored by default (unless we determine that we should expose them).
+ return true;
+}
+
+AccessibilityObjectInclusion AccessibilityObject::accessibilityPlatformIncludesObject() const
+{
+ if (isMenuListPopup() || isMenuListOption())
+ return IgnoreObject;
+
+ // Never expose an unknown object on the Mac. Clients of the AX API will not know what to do with it.
+ // Special case is when the unknown object is actually an attachment.
+ if (roleValue() == UnknownRole && !isAttachment())
+ return IgnoreObject;
+
+ return DefaultBehavior;
+}
+
+} // WebCore
+
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/mac/AccessibilityObjectWrapper.h b/Source/WebCore/accessibility/mac/AccessibilityObjectWrapper.h
new file mode 100644
index 0000000..b54d3fc
--- /dev/null
+++ b/Source/WebCore/accessibility/mac/AccessibilityObjectWrapper.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008 Apple 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 AccessibilityObjectWrapper_h
+#define AccessibilityObjectWrapper_h
+
+#ifdef __OBJC__
+@class WebCoreTextMarker;
+@class WebCoreTextMarkerRange;
+#else
+class WebCoreTextMarker;
+class WebCoreTextMarkerRange;
+#endif
+
+namespace WebCore {
+class AccessibilityObject;
+class VisiblePosition;
+}
+
+@interface AccessibilityObjectWrapper : NSObject {
+ WebCore::AccessibilityObject* m_object;
+}
+
+- (id)initWithAccessibilityObject:(WebCore::AccessibilityObject*)axObject;
+- (void)detach;
+- (WebCore::AccessibilityObject*)accessibilityObject;
+
+// Used to inform an element when a notification is posted for it. Used by DRT.
+- (void)accessibilityPostedNotification:(NSString *)notificationName;
+
+- (NSView*)attachmentView;
+
+@end
+
+#endif // AccessibilityObjectWrapper_h
diff --git a/Source/WebCore/accessibility/mac/AccessibilityObjectWrapper.mm b/Source/WebCore/accessibility/mac/AccessibilityObjectWrapper.mm
new file mode 100644
index 0000000..f3b388b
--- /dev/null
+++ b/Source/WebCore/accessibility/mac/AccessibilityObjectWrapper.mm
@@ -0,0 +1,2850 @@
+/*
+ * Copyright (C) 2008, 2009, 2010 Apple 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.
+ */
+
+#import "config.h"
+#import "AccessibilityObjectWrapper.h"
+
+#if HAVE(ACCESSIBILITY)
+
+#import "AXObjectCache.h"
+#import "AccessibilityARIAGridRow.h"
+#import "AccessibilityListBox.h"
+#import "AccessibilityList.h"
+#import "AccessibilityRenderObject.h"
+#import "AccessibilityScrollView.h"
+#import "AccessibilityTable.h"
+#import "AccessibilityTableCell.h"
+#import "AccessibilityTableRow.h"
+#import "AccessibilityTableColumn.h"
+#import "ColorMac.h"
+#import "EditorClient.h"
+#import "Frame.h"
+#import "FrameLoaderClient.h"
+#import "HTMLAnchorElement.h"
+#import "HTMLAreaElement.h"
+#import "HTMLFrameOwnerElement.h"
+#import "HTMLImageElement.h"
+#import "HTMLInputElement.h"
+#import "HTMLTextAreaElement.h"
+#import "LocalizedStrings.h"
+#import "RenderTextControl.h"
+#import "RenderView.h"
+#import "RenderWidget.h"
+#import "ScrollView.h"
+#import "SelectionController.h"
+#import "SimpleFontData.h"
+#import "TextIterator.h"
+#import "WebCoreFrameView.h"
+#import "WebCoreObjCExtras.h"
+#import "WebCoreViewFactory.h"
+#import "htmlediting.h"
+#import "visible_units.h"
+
+using namespace WebCore;
+using namespace HTMLNames;
+using namespace std;
+
+// Cell Tables
+#ifndef NSAccessibilitySelectedCellsAttribute
+#define NSAccessibilitySelectedCellsAttribute @"AXSelectedCells"
+#endif
+
+#ifndef NSAccessibilityVisibleCellsAttribute
+#define NSAccessibilityVisibleCellsAttribute @"AXVisibleCells"
+#endif
+
+#ifndef NSAccessibilityRowHeaderUIElementsAttribute
+#define NSAccessibilityRowHeaderUIElementsAttribute @"AXRowHeaderUIElements"
+#endif
+
+#ifndef NSAccessibilityRowIndexRangeAttribute
+#define NSAccessibilityRowIndexRangeAttribute @"AXRowIndexRange"
+#endif
+
+#ifndef NSAccessibilityColumnIndexRangeAttribute
+#define NSAccessibilityColumnIndexRangeAttribute @"AXColumnIndexRange"
+#endif
+
+#ifndef NSAccessibilityCellForColumnAndRowParameterizedAttribute
+#define NSAccessibilityCellForColumnAndRowParameterizedAttribute @"AXCellForColumnAndRow"
+#endif
+
+#ifndef NSAccessibilityCellRole
+#define NSAccessibilityCellRole @"AXCell"
+#endif
+
+// Lists
+#ifndef NSAccessibilityContentListSubrole
+#define NSAccessibilityContentListSubrole @"AXContentList"
+#endif
+
+#ifndef NSAccessibilityDefinitionListSubrole
+#define NSAccessibilityDefinitionListSubrole @"AXDefinitionList"
+#endif
+
+// Miscellaneous
+#ifndef NSAccessibilityBlockQuoteLevelAttribute
+#define NSAccessibilityBlockQuoteLevelAttribute @"AXBlockQuoteLevel"
+#endif
+
+#ifndef NSAccessibilityAccessKeyAttribute
+#define NSAccessibilityAccessKeyAttribute @"AXAccessKey"
+#endif
+
+#ifndef NSAccessibilityLanguageAttribute
+#define NSAccessibilityLanguageAttribute @"AXLanguage"
+#endif
+
+#ifndef NSAccessibilityRequiredAttribute
+#define NSAccessibilityRequiredAttribute @"AXRequired"
+#endif
+
+#ifndef NSAccessibilityInvalidAttribute
+#define NSAccessibilityInvalidAttribute @"AXInvalid"
+#endif
+
+#ifndef NSAccessibilityOwnsAttribute
+#define NSAccessibilityOwnsAttribute @"AXOwns"
+#endif
+
+#ifndef NSAccessibilityGrabbedAttribute
+#define NSAccessibilityGrabbedAttribute @"AXGrabbed"
+#endif
+
+#ifndef NSAccessibilityDropEffectsAttribute
+#define NSAccessibilityDropEffectsAttribute @"AXDropEffects"
+#endif
+
+#ifndef NSAccessibilityARIALiveAttribute
+#define NSAccessibilityARIALiveAttribute @"AXARIALive"
+#endif
+
+#ifndef NSAccessibilityARIAAtomicAttribute
+#define NSAccessibilityARIAAtomicAttribute @"AXARIAAtomic"
+#endif
+
+#ifndef NSAccessibilityARIARelevantAttribute
+#define NSAccessibilityARIARelevantAttribute @"AXARIARelevant"
+#endif
+
+#ifndef NSAccessibilityARIABusyAttribute
+#define NSAccessibilityARIABusyAttribute @"AXARIABusy"
+#endif
+
+#ifndef NSAccessibilityLoadingProgressAttribute
+#define NSAccessibilityLoadingProgressAttribute @"AXLoadingProgress"
+#endif
+
+#ifndef NSAccessibilityHasPopupAttribute
+#define NSAccessibilityHasPopupAttribute @"AXHasPopup"
+#endif
+
+#ifndef NSAccessibilityPlaceholderValueAttribute
+#define NSAccessibilityPlaceholderValueAttribute @"AXPlaceholderValue"
+#endif
+
+#ifdef BUILDING_ON_TIGER
+typedef unsigned NSUInteger;
+#define NSAccessibilityValueDescriptionAttribute @"AXValueDescription"
+#define NSAccessibilityTimelineSubrole @"AXTimeline"
+#endif
+
+@interface NSObject (WebKitAccessibilityArrayCategory)
+
+- (NSUInteger)accessibilityIndexOfChild:(id)child;
+- (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute;
+- (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount;
+
+@end
+
+@implementation AccessibilityObjectWrapper
+
+- (id)initWithAccessibilityObject:(AccessibilityObject*)axObject
+{
+ [super init];
+
+ m_object = axObject;
+ return self;
+}
+
+- (void)unregisterUniqueIdForUIElement
+{
+ [[WebCoreViewFactory sharedFactory] unregisterUniqueIdForUIElement:self];
+}
+
+- (void)detach
+{
+ // Send unregisterUniqueIdForUIElement unconditionally because if it is
+ // ever accidentally not done (via other bugs in our AX implementation) you
+ // end up with a crash like <rdar://problem/4273149>. It is safe and not
+ // expensive to send even if the object is not registered.
+ [self unregisterUniqueIdForUIElement];
+ m_object = 0;
+}
+
+- (BOOL)updateObjectBackingStore
+{
+ // Calling updateBackingStore() can invalidate this element so self must be retained.
+ // If it does become invalidated, m_object will be nil.
+ [[self retain] autorelease];
+
+ if (!m_object)
+ return NO;
+
+ m_object->updateBackingStore();
+ if (!m_object)
+ return NO;
+
+ return YES;
+}
+
+- (AccessibilityObject*)accessibilityObject
+{
+ return m_object;
+}
+
+- (NSView*)attachmentView
+{
+ ASSERT(m_object->isAttachment());
+ Widget* widget = m_object->widgetForAttachmentView();
+ if (!widget)
+ return nil;
+ return NSAccessibilityUnignoredDescendant(widget->platformWidget());
+}
+
+static WebCoreTextMarker* textMarkerForVisiblePosition(AXObjectCache* cache, const VisiblePosition& visiblePos)
+{
+ ASSERT(cache);
+
+ TextMarkerData textMarkerData;
+ cache->textMarkerDataForVisiblePosition(textMarkerData, visiblePos);
+ if (!textMarkerData.axID)
+ return nil;
+
+ return [[WebCoreViewFactory sharedFactory] textMarkerWithBytes:&textMarkerData length:sizeof(textMarkerData)];
+}
+
+- (WebCoreTextMarker *)textMarkerForVisiblePosition:(const VisiblePosition &)visiblePos
+{
+ return textMarkerForVisiblePosition(m_object->axObjectCache(), visiblePos);
+}
+
+static VisiblePosition visiblePositionForTextMarker(AXObjectCache* cache, WebCoreTextMarker* textMarker)
+{
+ ASSERT(cache);
+
+ if (!textMarker)
+ return VisiblePosition();
+ TextMarkerData textMarkerData;
+ if (![[WebCoreViewFactory sharedFactory] getBytes:&textMarkerData fromTextMarker:textMarker length:sizeof(textMarkerData)])
+ return VisiblePosition();
+
+ return cache->visiblePositionForTextMarkerData(textMarkerData);
+}
+
+- (VisiblePosition)visiblePositionForTextMarker:(WebCoreTextMarker *)textMarker
+{
+ return visiblePositionForTextMarker(m_object->axObjectCache(), textMarker);
+}
+
+static VisiblePosition visiblePositionForStartOfTextMarkerRange(AXObjectCache *cache, WebCoreTextMarkerRange* textMarkerRange)
+{
+ return visiblePositionForTextMarker(cache, [[WebCoreViewFactory sharedFactory] startOfTextMarkerRange:textMarkerRange]);
+}
+
+static VisiblePosition visiblePositionForEndOfTextMarkerRange(AXObjectCache *cache, WebCoreTextMarkerRange* textMarkerRange)
+{
+ return visiblePositionForTextMarker(cache, [[WebCoreViewFactory sharedFactory] endOfTextMarkerRange:textMarkerRange]);
+}
+
+static WebCoreTextMarkerRange* textMarkerRangeFromMarkers(WebCoreTextMarker* textMarker1, WebCoreTextMarker* textMarker2)
+{
+ if (!textMarker1 || !textMarker2)
+ return nil;
+
+ return [[WebCoreViewFactory sharedFactory] textMarkerRangeWithStart:textMarker1 end:textMarker2];
+}
+
+static void AXAttributeStringSetFont(NSMutableAttributedString* attrString, NSString* attribute, NSFont* font, NSRange range)
+{
+ NSDictionary* dict;
+
+ if (font) {
+ dict = [NSDictionary dictionaryWithObjectsAndKeys:
+ [font fontName] , NSAccessibilityFontNameKey,
+ [font familyName] , NSAccessibilityFontFamilyKey,
+ [font displayName] , NSAccessibilityVisibleNameKey,
+ [NSNumber numberWithFloat:[font pointSize]] , NSAccessibilityFontSizeKey,
+ nil];
+
+ [attrString addAttribute:attribute value:dict range:range];
+ } else
+ [attrString removeAttribute:attribute range:range];
+
+}
+
+static CGColorRef CreateCGColorIfDifferent(NSColor* nsColor, CGColorRef existingColor)
+{
+ // get color information assuming NSDeviceRGBColorSpace
+ NSColor* rgbColor = [nsColor colorUsingColorSpaceName:NSDeviceRGBColorSpace];
+ if (rgbColor == nil)
+ rgbColor = [NSColor blackColor];
+ CGFloat components[4];
+ [rgbColor getRed:&components[0] green:&components[1] blue:&components[2] alpha:&components[3]];
+
+ // create a new CGColorRef to return
+ CGColorSpaceRef cgColorSpace = CGColorSpaceCreateDeviceRGB();
+ CGColorRef cgColor = CGColorCreate(cgColorSpace, components);
+ CGColorSpaceRelease(cgColorSpace);
+
+ // check for match with existing color
+ if (existingColor && CGColorEqualToColor(cgColor, existingColor)) {
+ CGColorRelease(cgColor);
+ cgColor = 0;
+ }
+
+ return cgColor;
+}
+
+static void AXAttributeStringSetColor(NSMutableAttributedString* attrString, NSString* attribute, NSColor* color, NSRange range)
+{
+ if (color) {
+ CGColorRef existingColor = (CGColorRef) [attrString attribute:attribute atIndex:range.location effectiveRange:nil];
+ CGColorRef cgColor = CreateCGColorIfDifferent(color, existingColor);
+ if (cgColor) {
+ [attrString addAttribute:attribute value:(id)cgColor range:range];
+ CGColorRelease(cgColor);
+ }
+ } else
+ [attrString removeAttribute:attribute range:range];
+}
+
+static void AXAttributeStringSetNumber(NSMutableAttributedString* attrString, NSString* attribute, NSNumber* number, NSRange range)
+{
+ if (number)
+ [attrString addAttribute:attribute value:number range:range];
+ else
+ [attrString removeAttribute:attribute range:range];
+}
+
+static void AXAttributeStringSetStyle(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
+{
+ RenderStyle* style = renderer->style();
+
+ // set basic font info
+ AXAttributeStringSetFont(attrString, NSAccessibilityFontTextAttribute, style->font().primaryFont()->getNSFont(), range);
+
+ // set basic colors
+ AXAttributeStringSetColor(attrString, NSAccessibilityForegroundColorTextAttribute, nsColor(style->visitedDependentColor(CSSPropertyColor)), range);
+ AXAttributeStringSetColor(attrString, NSAccessibilityBackgroundColorTextAttribute, nsColor(style->visitedDependentColor(CSSPropertyBackgroundColor)), range);
+
+ // set super/sub scripting
+ EVerticalAlign alignment = style->verticalAlign();
+ if (alignment == SUB)
+ AXAttributeStringSetNumber(attrString, NSAccessibilitySuperscriptTextAttribute, [NSNumber numberWithInt:(-1)], range);
+ else if (alignment == SUPER)
+ AXAttributeStringSetNumber(attrString, NSAccessibilitySuperscriptTextAttribute, [NSNumber numberWithInt:1], range);
+ else
+ [attrString removeAttribute:NSAccessibilitySuperscriptTextAttribute range:range];
+
+ // set shadow
+ if (style->textShadow())
+ AXAttributeStringSetNumber(attrString, NSAccessibilityShadowTextAttribute, [NSNumber numberWithBool:YES], range);
+ else
+ [attrString removeAttribute:NSAccessibilityShadowTextAttribute range:range];
+
+ // set underline and strikethrough
+ int decor = style->textDecorationsInEffect();
+ if ((decor & UNDERLINE) == 0) {
+ [attrString removeAttribute:NSAccessibilityUnderlineTextAttribute range:range];
+ [attrString removeAttribute:NSAccessibilityUnderlineColorTextAttribute range:range];
+ }
+
+ if ((decor & LINE_THROUGH) == 0) {
+ [attrString removeAttribute:NSAccessibilityStrikethroughTextAttribute range:range];
+ [attrString removeAttribute:NSAccessibilityStrikethroughColorTextAttribute range:range];
+ }
+
+ if ((decor & (UNDERLINE | LINE_THROUGH)) != 0) {
+ // find colors using quirk mode approach (strict mode would use current
+ // color for all but the root line box, which would use getTextDecorationColors)
+ Color underline, overline, linethrough;
+ renderer->getTextDecorationColors(decor, underline, overline, linethrough);
+
+ if ((decor & UNDERLINE) != 0) {
+ AXAttributeStringSetNumber(attrString, NSAccessibilityUnderlineTextAttribute, [NSNumber numberWithBool:YES], range);
+ AXAttributeStringSetColor(attrString, NSAccessibilityUnderlineColorTextAttribute, nsColor(underline), range);
+ }
+
+ if ((decor & LINE_THROUGH) != 0) {
+ AXAttributeStringSetNumber(attrString, NSAccessibilityStrikethroughTextAttribute, [NSNumber numberWithBool:YES], range);
+ AXAttributeStringSetColor(attrString, NSAccessibilityStrikethroughColorTextAttribute, nsColor(linethrough), range);
+ }
+ }
+}
+
+static int blockquoteLevel(RenderObject* renderer)
+{
+ if (!renderer)
+ return 0;
+
+ int result = 0;
+ for (Node* node = renderer->node(); node; node = node->parentNode()) {
+ if (node->hasTagName(blockquoteTag))
+ result += 1;
+ }
+
+ return result;
+}
+
+static void AXAttributeStringSetBlockquoteLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
+{
+ int quoteLevel = blockquoteLevel(renderer);
+
+ if (quoteLevel)
+ [attrString addAttribute:NSAccessibilityBlockQuoteLevelAttribute value:[NSNumber numberWithInt:quoteLevel] range:range];
+ else
+ [attrString removeAttribute:NSAccessibilityBlockQuoteLevelAttribute range:range];
+}
+
+static void AXAttributeStringSetSpelling(NSMutableAttributedString* attrString, Node* node, const UChar* chars, int charLength, NSRange range)
+{
+ // Check the spelling directly since document->markersForNode() does not store the misspelled marking when the cursor is in a word.
+ EditorClient* client = node->document()->page()->editorClient();
+ int currentPosition = 0;
+ while (charLength > 0) {
+ const UChar* charData = chars + currentPosition;
+
+ int misspellingLocation = -1;
+ int misspellingLength = 0;
+ client->checkSpellingOfString(charData, charLength, &misspellingLocation, &misspellingLength);
+ if (misspellingLocation == -1 || !misspellingLength)
+ break;
+
+ NSRange spellRange = NSMakeRange(range.location + currentPosition + misspellingLocation, misspellingLength);
+ AXAttributeStringSetNumber(attrString, NSAccessibilityMisspelledTextAttribute, [NSNumber numberWithBool:YES], spellRange);
+ charLength -= (misspellingLocation + misspellingLength);
+ currentPosition += (misspellingLocation + misspellingLength);
+ }
+}
+
+static void AXAttributeStringSetHeadingLevel(NSMutableAttributedString* attrString, RenderObject* renderer, NSRange range)
+{
+ if (!renderer)
+ return;
+
+ AccessibilityObject* parentObject = renderer->document()->axObjectCache()->getOrCreate(renderer->parent());
+ int parentHeadingLevel = parentObject->headingLevel();
+
+ if (parentHeadingLevel)
+ [attrString addAttribute:@"AXHeadingLevel" value:[NSNumber numberWithInt:parentHeadingLevel] range:range];
+ else
+ [attrString removeAttribute:@"AXHeadingLevel" range:range];
+}
+
+static void AXAttributeStringSetElement(NSMutableAttributedString* attrString, NSString* attribute, AccessibilityObject* object, NSRange range)
+{
+ if (object && object->isAccessibilityRenderObject()) {
+ // make a serializable AX object
+
+ RenderObject* renderer = static_cast<AccessibilityRenderObject*>(object)->renderer();
+ if (!renderer)
+ return;
+
+ Document* doc = renderer->document();
+ if (!doc)
+ return;
+
+ AXObjectCache* cache = doc->axObjectCache();
+ if (!cache)
+ return;
+
+ AXUIElementRef axElement = [[WebCoreViewFactory sharedFactory] AXUIElementForElement:object->wrapper()];
+ if (axElement) {
+ [attrString addAttribute:attribute value:(id)axElement range:range];
+ CFRelease(axElement);
+ }
+ } else
+ [attrString removeAttribute:attribute range:range];
+}
+
+static void AXAttributedStringAppendText(NSMutableAttributedString* attrString, Node* node, const UChar* chars, int length)
+{
+ // skip invisible text
+ if (!node->renderer())
+ return;
+
+ // easier to calculate the range before appending the string
+ NSRange attrStringRange = NSMakeRange([attrString length], length);
+
+ // append the string from this node
+ [[attrString mutableString] appendString:[NSString stringWithCharacters:chars length:length]];
+
+ // add new attributes and remove irrelevant inherited ones
+ // NOTE: color attributes are handled specially because -[NSMutableAttributedString addAttribute: value: range:] does not merge
+ // identical colors. Workaround is to not replace an existing color attribute if it matches what we are adding. This also means
+ // we cannot just pre-remove all inherited attributes on the appended string, so we have to remove the irrelevant ones individually.
+
+ // remove inherited attachment from prior AXAttributedStringAppendReplaced
+ [attrString removeAttribute:NSAccessibilityAttachmentTextAttribute range:attrStringRange];
+ [attrString removeAttribute:NSAccessibilityMisspelledTextAttribute range:attrStringRange];
+
+ // set new attributes
+ AXAttributeStringSetStyle(attrString, node->renderer(), attrStringRange);
+ AXAttributeStringSetHeadingLevel(attrString, node->renderer(), attrStringRange);
+ AXAttributeStringSetBlockquoteLevel(attrString, node->renderer(), attrStringRange);
+ AXAttributeStringSetElement(attrString, NSAccessibilityLinkTextAttribute, AccessibilityObject::anchorElementForNode(node), attrStringRange);
+
+ // do spelling last because it tends to break up the range
+ AXAttributeStringSetSpelling(attrString, node, chars, length, attrStringRange);
+}
+
+static NSString* nsStringForReplacedNode(Node* replacedNode)
+{
+ // we should always be given a rendered node and a replaced node, but be safe
+ // replaced nodes are either attachments (widgets) or images
+ if (!replacedNode || !replacedNode->renderer() || !replacedNode->renderer()->isReplaced() || replacedNode->isTextNode()) {
+ ASSERT_NOT_REACHED();
+ return nil;
+ }
+
+ // create an AX object, but skip it if it is not supposed to be seen
+ RefPtr<AccessibilityObject> obj = replacedNode->renderer()->document()->axObjectCache()->getOrCreate(replacedNode->renderer());
+ if (obj->accessibilityIsIgnored())
+ return nil;
+
+ // use the attachmentCharacter to represent the replaced node
+ const UniChar attachmentChar = NSAttachmentCharacter;
+ return [NSString stringWithCharacters:&attachmentChar length:1];
+}
+
+- (NSAttributedString*)doAXAttributedStringForTextMarkerRange:(WebCoreTextMarkerRange*)textMarkerRange
+{
+ if (!m_object)
+ return nil;
+
+ // extract the start and end VisiblePosition
+ VisiblePosition startVisiblePosition = visiblePositionForStartOfTextMarkerRange(m_object->axObjectCache(), textMarkerRange);
+ if (startVisiblePosition.isNull())
+ return nil;
+
+ VisiblePosition endVisiblePosition = visiblePositionForEndOfTextMarkerRange(m_object->axObjectCache(), textMarkerRange);
+ if (endVisiblePosition.isNull())
+ return nil;
+
+ VisiblePositionRange visiblePositionRange(startVisiblePosition, endVisiblePosition);
+ // iterate over the range to build the AX attributed string
+ NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] init];
+ TextIterator it(makeRange(startVisiblePosition, endVisiblePosition).get());
+ while (!it.atEnd()) {
+ // locate the node and starting offset for this range
+ int exception = 0;
+ Node* node = it.range()->startContainer(exception);
+ ASSERT(node == it.range()->endContainer(exception));
+ int offset = it.range()->startOffset(exception);
+
+ // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
+ if (it.length() != 0) {
+ // Add the text of the list marker item if necessary.
+ String listMarkerText = m_object->listMarkerTextForNodeAndPosition(node, VisiblePosition(it.range()->startPosition()));
+ if (!listMarkerText.isEmpty())
+ AXAttributedStringAppendText(attrString, node, listMarkerText.characters(), listMarkerText.length());
+
+ AXAttributedStringAppendText(attrString, node, it.characters(), it.length());
+ } else {
+ Node* replacedNode = node->childNode(offset);
+ NSString *attachmentString = nsStringForReplacedNode(replacedNode);
+ if (attachmentString) {
+ NSRange attrStringRange = NSMakeRange([attrString length], [attachmentString length]);
+
+ // append the placeholder string
+ [[attrString mutableString] appendString:attachmentString];
+
+ // remove all inherited attributes
+ [attrString setAttributes:nil range:attrStringRange];
+
+ // add the attachment attribute
+ AccessibilityObject* obj = replacedNode->renderer()->document()->axObjectCache()->getOrCreate(replacedNode->renderer());
+ AXAttributeStringSetElement(attrString, NSAccessibilityAttachmentTextAttribute, obj, attrStringRange);
+ }
+ }
+ it.advance();
+ }
+
+ return [attrString autorelease];
+}
+
+static WebCoreTextMarkerRange* textMarkerRangeFromVisiblePositions(AXObjectCache *cache, VisiblePosition startPosition, VisiblePosition endPosition)
+{
+ WebCoreTextMarker* startTextMarker = textMarkerForVisiblePosition(cache, startPosition);
+ WebCoreTextMarker* endTextMarker = textMarkerForVisiblePosition(cache, endPosition);
+ return textMarkerRangeFromMarkers(startTextMarker, endTextMarker);
+}
+
+- (WebCoreTextMarkerRange *)textMarkerRangeFromVisiblePositions:(VisiblePosition)startPosition endPosition:(VisiblePosition)endPosition
+{
+ return textMarkerRangeFromVisiblePositions(m_object->axObjectCache(), startPosition, endPosition);
+}
+
+- (NSArray*)accessibilityActionNames
+{
+ if (![self updateObjectBackingStore])
+ return nil;
+
+ static NSArray* actionElementActions = [[NSArray alloc] initWithObjects: NSAccessibilityPressAction, NSAccessibilityShowMenuAction, nil];
+ static NSArray* defaultElementActions = [[NSArray alloc] initWithObjects: NSAccessibilityShowMenuAction, nil];
+ static NSArray* menuElementActions = [[NSArray alloc] initWithObjects: NSAccessibilityCancelAction, NSAccessibilityPressAction, nil];
+ static NSArray* sliderActions = [[NSArray alloc] initWithObjects: NSAccessibilityIncrementAction, NSAccessibilityDecrementAction, nil];
+
+ NSArray *actions;
+ if (m_object->actionElement())
+ actions = actionElementActions;
+ else if (m_object->isMenuRelated())
+ actions = menuElementActions;
+ else if (m_object->isSlider())
+ actions = sliderActions;
+ else if (m_object->isAttachment())
+ actions = [[self attachmentView] accessibilityActionNames];
+ else
+ actions = defaultElementActions;
+
+ return actions;
+}
+
+- (NSArray*)additionalAccessibilityAttributeNames
+{
+ if (!m_object)
+ return nil;
+
+ NSMutableArray *additional = [NSMutableArray array];
+ if (m_object->supportsARIAOwns())
+ [additional addObject:NSAccessibilityOwnsAttribute];
+
+ if (m_object->supportsARIAExpanded())
+ [additional addObject:NSAccessibilityExpandedAttribute];
+
+ if (m_object->isScrollbar())
+ [additional addObject:NSAccessibilityOrientationAttribute];
+
+ if (m_object->supportsARIADragging())
+ [additional addObject:NSAccessibilityGrabbedAttribute];
+
+ if (m_object->supportsARIADropping())
+ [additional addObject:NSAccessibilityDropEffectsAttribute];
+
+ if (m_object->isAccessibilityTable() && static_cast<AccessibilityTable*>(m_object)->supportsSelectedRows())
+ [additional addObject:NSAccessibilitySelectedRowsAttribute];
+
+ if (m_object->supportsARIALiveRegion()) {
+ [additional addObject:NSAccessibilityARIALiveAttribute];
+ [additional addObject:NSAccessibilityARIARelevantAttribute];
+ }
+
+ // If an object is a child of a live region, then add these
+ if (m_object->isInsideARIALiveRegion()) {
+ [additional addObject:NSAccessibilityARIAAtomicAttribute];
+ [additional addObject:NSAccessibilityARIABusyAttribute];
+ }
+
+ if (m_object->ariaHasPopup())
+ [additional addObject:NSAccessibilityHasPopupAttribute];
+
+ return additional;
+}
+
+- (NSArray*)accessibilityAttributeNames
+{
+ if (![self updateObjectBackingStore])
+ return nil;
+
+ if (m_object->isAttachment())
+ return [[self attachmentView] accessibilityAttributeNames];
+
+ static NSArray* attributes = nil;
+ static NSArray* anchorAttrs = nil;
+ static NSArray* webAreaAttrs = nil;
+ static NSArray* textAttrs = nil;
+ static NSArray* listAttrs = nil;
+ static NSArray* listBoxAttrs = nil;
+ static NSArray* rangeAttrs = nil;
+ static NSArray* commonMenuAttrs = nil;
+ static NSArray* menuAttrs = nil;
+ static NSArray* menuBarAttrs = nil;
+ static NSArray* menuItemAttrs = nil;
+ static NSArray* menuButtonAttrs = nil;
+ static NSArray* controlAttrs = nil;
+ static NSArray* tableAttrs = nil;
+ static NSArray* tableRowAttrs = nil;
+ static NSArray* tableColAttrs = nil;
+ static NSArray* tableCellAttrs = nil;
+ static NSArray* groupAttrs = nil;
+ static NSArray* inputImageAttrs = nil;
+ static NSArray* passwordFieldAttrs = nil;
+ static NSArray* tabListAttrs = nil;
+ static NSArray* comboBoxAttrs = nil;
+ static NSArray* outlineAttrs = nil;
+ static NSArray* outlineRowAttrs = nil;
+ static NSArray* buttonAttrs = nil;
+ static NSArray* scrollViewAttrs = nil;
+ NSMutableArray* tempArray;
+ if (attributes == nil) {
+ attributes = [[NSArray alloc] initWithObjects: NSAccessibilityRoleAttribute,
+ NSAccessibilitySubroleAttribute,
+ NSAccessibilityRoleDescriptionAttribute,
+ NSAccessibilityChildrenAttribute,
+ NSAccessibilityHelpAttribute,
+ NSAccessibilityParentAttribute,
+ NSAccessibilityPositionAttribute,
+ NSAccessibilitySizeAttribute,
+ NSAccessibilityTitleAttribute,
+ NSAccessibilityDescriptionAttribute,
+ NSAccessibilityValueAttribute,
+ NSAccessibilityFocusedAttribute,
+ NSAccessibilityEnabledAttribute,
+ NSAccessibilityWindowAttribute,
+ @"AXSelectedTextMarkerRange",
+ @"AXStartTextMarker",
+ @"AXEndTextMarker",
+ @"AXVisited",
+ NSAccessibilityLinkedUIElementsAttribute,
+ NSAccessibilitySelectedAttribute,
+ NSAccessibilityBlockQuoteLevelAttribute,
+ NSAccessibilityTopLevelUIElementAttribute,
+ nil];
+ }
+ if (commonMenuAttrs == nil) {
+ commonMenuAttrs = [[NSArray alloc] initWithObjects: NSAccessibilityRoleAttribute,
+ NSAccessibilityRoleDescriptionAttribute,
+ NSAccessibilityChildrenAttribute,
+ NSAccessibilityParentAttribute,
+ NSAccessibilityEnabledAttribute,
+ NSAccessibilityPositionAttribute,
+ NSAccessibilitySizeAttribute,
+ nil];
+ }
+ if (anchorAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:attributes];
+ [tempArray addObject:NSAccessibilityURLAttribute];
+ [tempArray addObject:NSAccessibilityAccessKeyAttribute];
+ anchorAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (webAreaAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:attributes];
+ [tempArray addObject:@"AXLinkUIElements"];
+ [tempArray addObject:@"AXLoaded"];
+ [tempArray addObject:@"AXLayoutCount"];
+ [tempArray addObject:NSAccessibilityLoadingProgressAttribute];
+ [tempArray addObject:NSAccessibilityURLAttribute];
+ webAreaAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (textAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:attributes];
+ [tempArray addObject:NSAccessibilityNumberOfCharactersAttribute];
+ [tempArray addObject:NSAccessibilitySelectedTextAttribute];
+ [tempArray addObject:NSAccessibilitySelectedTextRangeAttribute];
+ [tempArray addObject:NSAccessibilityVisibleCharacterRangeAttribute];
+ [tempArray addObject:NSAccessibilityInsertionPointLineNumberAttribute];
+ [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
+ [tempArray addObject:NSAccessibilityAccessKeyAttribute];
+ [tempArray addObject:NSAccessibilityRequiredAttribute];
+ [tempArray addObject:NSAccessibilityInvalidAttribute];
+ [tempArray addObject:NSAccessibilityPlaceholderValueAttribute];
+ textAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (listAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:attributes];
+ [tempArray addObject:NSAccessibilitySelectedChildrenAttribute];
+ [tempArray addObject:NSAccessibilityVisibleChildrenAttribute];
+ [tempArray addObject:NSAccessibilityOrientationAttribute];
+ [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
+ listAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (listBoxAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:listAttrs];
+ [tempArray addObject:NSAccessibilityAccessKeyAttribute];
+ [tempArray addObject:NSAccessibilityRequiredAttribute];
+ [tempArray addObject:NSAccessibilityInvalidAttribute];
+ listBoxAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (rangeAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:attributes];
+ [tempArray addObject:NSAccessibilityMinValueAttribute];
+ [tempArray addObject:NSAccessibilityMaxValueAttribute];
+ [tempArray addObject:NSAccessibilityOrientationAttribute];
+ [tempArray addObject:NSAccessibilityValueDescriptionAttribute];
+ rangeAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (menuBarAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:commonMenuAttrs];
+ [tempArray addObject:NSAccessibilitySelectedChildrenAttribute];
+ [tempArray addObject:NSAccessibilityVisibleChildrenAttribute];
+ [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
+ menuBarAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (menuAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:commonMenuAttrs];
+ [tempArray addObject:NSAccessibilitySelectedChildrenAttribute];
+ [tempArray addObject:NSAccessibilityVisibleChildrenAttribute];
+ [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
+ menuAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (menuItemAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:commonMenuAttrs];
+ [tempArray addObject:NSAccessibilityTitleAttribute];
+ [tempArray addObject:NSAccessibilityHelpAttribute];
+ [tempArray addObject:NSAccessibilitySelectedAttribute];
+ [tempArray addObject:(NSString*)kAXMenuItemCmdCharAttribute];
+ [tempArray addObject:(NSString*)kAXMenuItemCmdVirtualKeyAttribute];
+ [tempArray addObject:(NSString*)kAXMenuItemCmdGlyphAttribute];
+ [tempArray addObject:(NSString*)kAXMenuItemCmdModifiersAttribute];
+ [tempArray addObject:(NSString*)kAXMenuItemMarkCharAttribute];
+ [tempArray addObject:(NSString*)kAXMenuItemPrimaryUIElementAttribute];
+ [tempArray addObject:NSAccessibilityServesAsTitleForUIElementsAttribute];
+ menuItemAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (menuButtonAttrs == nil) {
+ menuButtonAttrs = [[NSArray alloc] initWithObjects:NSAccessibilityRoleAttribute,
+ NSAccessibilityRoleDescriptionAttribute,
+ NSAccessibilityParentAttribute,
+ NSAccessibilityPositionAttribute,
+ NSAccessibilitySizeAttribute,
+ NSAccessibilityWindowAttribute,
+ NSAccessibilityEnabledAttribute,
+ NSAccessibilityFocusedAttribute,
+ NSAccessibilityTitleAttribute,
+ NSAccessibilityChildrenAttribute, nil];
+ }
+ if (controlAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:attributes];
+ [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
+ [tempArray addObject:NSAccessibilityAccessKeyAttribute];
+ [tempArray addObject:NSAccessibilityRequiredAttribute];
+ [tempArray addObject:NSAccessibilityInvalidAttribute];
+ controlAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (buttonAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:attributes];
+ // Buttons should not expose AXValue.
+ [tempArray removeObject:NSAccessibilityValueAttribute];
+ [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
+ [tempArray addObject:NSAccessibilityAccessKeyAttribute];
+ buttonAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (comboBoxAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:controlAttrs];
+ [tempArray addObject:NSAccessibilityExpandedAttribute];
+ comboBoxAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (tableAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:attributes];
+ [tempArray addObject:NSAccessibilityRowsAttribute];
+ [tempArray addObject:NSAccessibilityVisibleRowsAttribute];
+ [tempArray addObject:NSAccessibilityColumnsAttribute];
+ [tempArray addObject:NSAccessibilityVisibleColumnsAttribute];
+ [tempArray addObject:NSAccessibilityVisibleCellsAttribute];
+ [tempArray addObject:(NSString *)kAXColumnHeaderUIElementsAttribute];
+ [tempArray addObject:NSAccessibilityRowHeaderUIElementsAttribute];
+ [tempArray addObject:NSAccessibilityHeaderAttribute];
+ tableAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (tableRowAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:attributes];
+ [tempArray addObject:NSAccessibilityIndexAttribute];
+ tableRowAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (tableColAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:attributes];
+ [tempArray addObject:NSAccessibilityIndexAttribute];
+ [tempArray addObject:NSAccessibilityHeaderAttribute];
+ [tempArray addObject:NSAccessibilityRowsAttribute];
+ [tempArray addObject:NSAccessibilityVisibleRowsAttribute];
+ tableColAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (tableCellAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:attributes];
+ [tempArray addObject:NSAccessibilityRowIndexRangeAttribute];
+ [tempArray addObject:NSAccessibilityColumnIndexRangeAttribute];
+ tableCellAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (groupAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:attributes];
+ [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
+ groupAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (inputImageAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:buttonAttrs];
+ [tempArray addObject:NSAccessibilityURLAttribute];
+ inputImageAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (passwordFieldAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:attributes];
+ [tempArray addObject:NSAccessibilityTitleUIElementAttribute];
+ [tempArray addObject:NSAccessibilityRequiredAttribute];
+ [tempArray addObject:NSAccessibilityInvalidAttribute];
+ passwordFieldAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (tabListAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:attributes];
+ [tempArray addObject:NSAccessibilityTabsAttribute];
+ [tempArray addObject:NSAccessibilityContentsAttribute];
+ tabListAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (outlineAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:attributes];
+ [tempArray addObject:NSAccessibilitySelectedRowsAttribute];
+ [tempArray addObject:NSAccessibilityRowsAttribute];
+ [tempArray addObject:NSAccessibilityColumnsAttribute];
+ outlineAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (outlineRowAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:tableRowAttrs];
+ [tempArray addObject:NSAccessibilityDisclosingAttribute];
+ [tempArray addObject:NSAccessibilityDisclosedByRowAttribute];
+ [tempArray addObject:NSAccessibilityDisclosureLevelAttribute];
+ [tempArray addObject:NSAccessibilityDisclosedRowsAttribute];
+ outlineRowAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (scrollViewAttrs == nil) {
+ tempArray = [[NSMutableArray alloc] initWithArray:attributes];
+ [tempArray addObject:NSAccessibilityContentsAttribute];
+ [tempArray addObject:NSAccessibilityHorizontalScrollBarAttribute];
+ [tempArray addObject:NSAccessibilityVerticalScrollBarAttribute];
+ scrollViewAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+
+ NSArray *objectAttributes = attributes;
+
+ if (m_object->isPasswordField())
+ objectAttributes = passwordFieldAttrs;
+
+ else if (m_object->isWebArea())
+ objectAttributes = webAreaAttrs;
+
+ else if (m_object->isTextControl())
+ objectAttributes = textAttrs;
+
+ else if (m_object->isAnchor() || m_object->isImage() || m_object->isLink())
+ objectAttributes = anchorAttrs;
+
+ else if (m_object->isAccessibilityTable())
+ objectAttributes = tableAttrs;
+ else if (m_object->isTableColumn())
+ objectAttributes = tableColAttrs;
+ else if (m_object->isTableCell())
+ objectAttributes = tableCellAttrs;
+ else if (m_object->isTableRow()) {
+ // An ARIA table row can be collapsed and expanded, so it needs the extra attributes.
+ if (m_object->isARIATreeGridRow())
+ objectAttributes = outlineRowAttrs;
+ else
+ objectAttributes = tableRowAttrs;
+ }
+
+ else if (m_object->isTree())
+ objectAttributes = outlineAttrs;
+ else if (m_object->isTreeItem())
+ objectAttributes = outlineRowAttrs;
+
+ else if (m_object->isListBox())
+ objectAttributes = listBoxAttrs;
+ else if (m_object->isList())
+ objectAttributes = listAttrs;
+
+ else if (m_object->isComboBox())
+ objectAttributes = comboBoxAttrs;
+
+ else if (m_object->isProgressIndicator() || m_object->isSlider())
+ objectAttributes = rangeAttrs;
+
+ // These are processed in order because an input image is a button, and a button is a control.
+ else if (m_object->isInputImage())
+ objectAttributes = inputImageAttrs;
+ else if (m_object->isButton())
+ objectAttributes = buttonAttrs;
+ else if (m_object->isControl())
+ objectAttributes = controlAttrs;
+
+ else if (m_object->isGroup() || m_object->isListItem())
+ objectAttributes = groupAttrs;
+ else if (m_object->isTabList())
+ objectAttributes = tabListAttrs;
+ else if (m_object->isScrollView())
+ objectAttributes = scrollViewAttrs;
+
+ else if (m_object->isMenu())
+ objectAttributes = menuAttrs;
+ else if (m_object->isMenuBar())
+ objectAttributes = menuBarAttrs;
+ else if (m_object->isMenuButton())
+ objectAttributes = menuButtonAttrs;
+ else if (m_object->isMenuItem())
+ objectAttributes = menuItemAttrs;
+
+ NSArray *additionalAttributes = [self additionalAccessibilityAttributeNames];
+ if ([additionalAttributes count])
+ objectAttributes = [objectAttributes arrayByAddingObjectsFromArray:additionalAttributes];
+
+ return objectAttributes;
+}
+
+- (VisiblePositionRange)visiblePositionRangeForTextMarkerRange:(WebCoreTextMarkerRange*) textMarkerRange
+{
+ if (!textMarkerRange)
+ return VisiblePositionRange();
+ AXObjectCache* cache = m_object->axObjectCache();
+ return VisiblePositionRange(visiblePositionForStartOfTextMarkerRange(cache, textMarkerRange), visiblePositionForEndOfTextMarkerRange(cache, textMarkerRange));
+}
+
+- (NSArray*)renderWidgetChildren
+{
+ Widget* widget = m_object->widget();
+ if (!widget)
+ return nil;
+ return [(widget->platformWidget()) accessibilityAttributeValue: NSAccessibilityChildrenAttribute];
+}
+
+- (id)remoteAccessibilityParentObject
+{
+ if (!m_object || !m_object->document())
+ return nil;
+
+ return m_object->document()->frame()->loader()->client()->accessibilityRemoteObject();
+}
+
+static void convertToVector(NSArray* array, AccessibilityObject::AccessibilityChildrenVector& vector)
+{
+ unsigned length = [array count];
+ vector.reserveInitialCapacity(length);
+ for (unsigned i = 0; i < length; ++i) {
+ AccessibilityObject* obj = [[array objectAtIndex:i] accessibilityObject];
+ if (obj)
+ vector.append(obj);
+ }
+}
+
+static NSMutableArray* convertToNSArray(const AccessibilityObject::AccessibilityChildrenVector& vector)
+{
+ unsigned length = vector.size();
+ NSMutableArray* array = [NSMutableArray arrayWithCapacity: length];
+ for (unsigned i = 0; i < length; ++i) {
+ AccessibilityObjectWrapper* wrapper = vector[i]->wrapper();
+ ASSERT(wrapper);
+ if (wrapper) {
+ // we want to return the attachment view instead of the object representing the attachment.
+ // otherwise, we get palindrome errors in the AX hierarchy
+ if (vector[i]->isAttachment() && [wrapper attachmentView])
+ [array addObject:[wrapper attachmentView]];
+ else
+ [array addObject:wrapper];
+ }
+ }
+ return array;
+}
+
+- (WebCoreTextMarkerRange*)textMarkerRangeForSelection
+{
+ VisibleSelection selection = m_object->selection();
+ if (selection.isNone())
+ return nil;
+ return [self textMarkerRangeFromVisiblePositions:selection.visibleStart() endPosition:selection.visibleEnd()];
+}
+
+- (NSValue*)position
+{
+ IntRect rect = m_object->elementRect();
+ NSPoint point;
+
+ FrameView* frameView = m_object->documentFrameView();
+ id remoteParent = [self remoteAccessibilityParentObject];
+ if (remoteParent) {
+ point = NSMakePoint(rect.x(), rect.y());
+
+ NSPoint remotePosition = [[remoteParent accessibilityAttributeValue:NSAccessibilityPositionAttribute] pointValue];
+ NSSize remoteSize = [[remoteParent accessibilityAttributeValue:NSAccessibilitySizeAttribute] sizeValue];
+
+ // Get the y position of the WKView (we have to screen-flip and go from bottom left to top left).
+ CGFloat screenHeight = [[[NSScreen screens] objectAtIndex:0] frame].size.height;
+ remotePosition.y = (screenHeight - remotePosition.y) - remoteSize.height;
+
+ NSPoint scrollPosition = NSMakePoint(0, 0);
+ if (frameView && !m_object->isScrollbar() && !m_object->isScrollView()) {
+ IntPoint frameScrollPos = frameView->scrollPosition();
+ scrollPosition = NSMakePoint(frameScrollPos.x(), frameScrollPos.y());
+ }
+
+ point.x += remotePosition.x - scrollPosition.x;
+ // Set the new position, which means getting bottom y, and then flipping to screen coordinates.
+ point.y = screenHeight - (point.y + remotePosition.y + rect.height() - scrollPosition.y);
+ } else {
+ // The Cocoa accessibility API wants the lower-left corner.
+ point = NSMakePoint(rect.x(), rect.bottom());
+
+ if (frameView) {
+ NSView* view = frameView->documentView();
+ point = [[view window] convertBaseToScreen:[view convertPoint: point toView:nil]];
+ }
+ }
+
+ return [NSValue valueWithPoint:point];
+}
+
+typedef HashMap<int, NSString*> AccessibilityRoleMap;
+
+static const AccessibilityRoleMap& createAccessibilityRoleMap()
+{
+ struct RoleEntry {
+ AccessibilityRole value;
+ NSString* string;
+ };
+
+ static const RoleEntry roles[] = {
+ { UnknownRole, NSAccessibilityUnknownRole },
+ { ButtonRole, NSAccessibilityButtonRole },
+ { RadioButtonRole, NSAccessibilityRadioButtonRole },
+ { CheckBoxRole, NSAccessibilityCheckBoxRole },
+ { SliderRole, NSAccessibilitySliderRole },
+ { TabGroupRole, NSAccessibilityTabGroupRole },
+ { TextFieldRole, NSAccessibilityTextFieldRole },
+ { StaticTextRole, NSAccessibilityStaticTextRole },
+ { TextAreaRole, NSAccessibilityTextAreaRole },
+ { ScrollAreaRole, NSAccessibilityScrollAreaRole },
+ { PopUpButtonRole, NSAccessibilityPopUpButtonRole },
+ { MenuButtonRole, NSAccessibilityMenuButtonRole },
+ { TableRole, NSAccessibilityTableRole },
+ { ApplicationRole, NSAccessibilityApplicationRole },
+ { GroupRole, NSAccessibilityGroupRole },
+ { RadioGroupRole, NSAccessibilityRadioGroupRole },
+ { ListRole, NSAccessibilityListRole },
+ { DirectoryRole, NSAccessibilityListRole },
+ { ScrollBarRole, NSAccessibilityScrollBarRole },
+ { ValueIndicatorRole, NSAccessibilityValueIndicatorRole },
+ { ImageRole, NSAccessibilityImageRole },
+ { MenuBarRole, NSAccessibilityMenuBarRole },
+ { MenuRole, NSAccessibilityMenuRole },
+ { MenuItemRole, NSAccessibilityMenuItemRole },
+ { ColumnRole, NSAccessibilityColumnRole },
+ { RowRole, NSAccessibilityRowRole },
+ { ToolbarRole, NSAccessibilityToolbarRole },
+ { BusyIndicatorRole, NSAccessibilityBusyIndicatorRole },
+ { ProgressIndicatorRole, NSAccessibilityProgressIndicatorRole },
+ { WindowRole, NSAccessibilityWindowRole },
+ { DrawerRole, NSAccessibilityDrawerRole },
+ { SystemWideRole, NSAccessibilitySystemWideRole },
+ { OutlineRole, NSAccessibilityOutlineRole },
+ { IncrementorRole, NSAccessibilityIncrementorRole },
+ { BrowserRole, NSAccessibilityBrowserRole },
+ { ComboBoxRole, NSAccessibilityComboBoxRole },
+ { SplitGroupRole, NSAccessibilitySplitGroupRole },
+ { SplitterRole, NSAccessibilitySplitterRole },
+ { ColorWellRole, NSAccessibilityColorWellRole },
+ { GrowAreaRole, NSAccessibilityGrowAreaRole },
+ { SheetRole, NSAccessibilitySheetRole },
+ { HelpTagRole, NSAccessibilityHelpTagRole },
+ { MatteRole, NSAccessibilityMatteRole },
+ { RulerRole, NSAccessibilityRulerRole },
+ { RulerMarkerRole, NSAccessibilityRulerMarkerRole },
+ { LinkRole, NSAccessibilityLinkRole },
+#ifndef BUILDING_ON_TIGER
+ { DisclosureTriangleRole, NSAccessibilityDisclosureTriangleRole },
+ { GridRole, NSAccessibilityGridRole },
+#endif
+ { WebCoreLinkRole, NSAccessibilityLinkRole },
+ { ImageMapLinkRole, NSAccessibilityLinkRole },
+ { ImageMapRole, @"AXImageMap" },
+ { ListMarkerRole, @"AXListMarker" },
+ { WebAreaRole, @"AXWebArea" },
+ { HeadingRole, @"AXHeading" },
+ { ListBoxRole, NSAccessibilityListRole },
+ { ListBoxOptionRole, NSAccessibilityStaticTextRole },
+#if ACCESSIBILITY_TABLES
+ { CellRole, NSAccessibilityCellRole },
+#else
+ { CellRole, NSAccessibilityGroupRole },
+#endif
+ { TableHeaderContainerRole, NSAccessibilityGroupRole },
+ { DefinitionListDefinitionRole, NSAccessibilityGroupRole },
+ { DefinitionListTermRole, NSAccessibilityGroupRole },
+ { SliderThumbRole, NSAccessibilityValueIndicatorRole },
+ { LandmarkApplicationRole, NSAccessibilityGroupRole },
+ { LandmarkBannerRole, NSAccessibilityGroupRole },
+ { LandmarkComplementaryRole, NSAccessibilityGroupRole },
+ { LandmarkContentInfoRole, NSAccessibilityGroupRole },
+ { LandmarkMainRole, NSAccessibilityGroupRole },
+ { LandmarkNavigationRole, NSAccessibilityGroupRole },
+ { LandmarkSearchRole, NSAccessibilityGroupRole },
+ { ApplicationAlertRole, NSAccessibilityGroupRole },
+ { ApplicationAlertDialogRole, NSAccessibilityGroupRole },
+ { ApplicationDialogRole, NSAccessibilityGroupRole },
+ { ApplicationLogRole, NSAccessibilityGroupRole },
+ { ApplicationMarqueeRole, NSAccessibilityGroupRole },
+ { ApplicationStatusRole, NSAccessibilityGroupRole },
+ { ApplicationTimerRole, NSAccessibilityGroupRole },
+ { DocumentRole, NSAccessibilityGroupRole },
+ { DocumentArticleRole, NSAccessibilityGroupRole },
+ { DocumentMathRole, NSAccessibilityGroupRole },
+ { DocumentNoteRole, NSAccessibilityGroupRole },
+ { DocumentRegionRole, NSAccessibilityGroupRole },
+ { UserInterfaceTooltipRole, NSAccessibilityGroupRole },
+ { TabRole, NSAccessibilityRadioButtonRole },
+ { TabListRole, NSAccessibilityTabGroupRole },
+ { TabPanelRole, NSAccessibilityGroupRole },
+ { TreeRole, NSAccessibilityOutlineRole },
+ { TreeItemRole, NSAccessibilityRowRole },
+ { ListItemRole, NSAccessibilityGroupRole }
+ };
+ AccessibilityRoleMap& roleMap = *new AccessibilityRoleMap;
+
+ const unsigned numRoles = sizeof(roles) / sizeof(roles[0]);
+ for (unsigned i = 0; i < numRoles; ++i)
+ roleMap.set(roles[i].value, roles[i].string);
+ return roleMap;
+}
+
+static NSString* roleValueToNSString(AccessibilityRole value)
+{
+ ASSERT(value);
+ static const AccessibilityRoleMap& roleMap = createAccessibilityRoleMap();
+ return roleMap.get(value);
+}
+
+- (NSString*)role
+{
+ if (m_object->isAttachment())
+ return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityRoleAttribute];
+ NSString* string = roleValueToNSString(m_object->roleValue());
+ if (string != nil)
+ return string;
+ return NSAccessibilityUnknownRole;
+}
+
+- (NSString*)subrole
+{
+ if (m_object->isPasswordField())
+ return NSAccessibilitySecureTextFieldSubrole;
+
+ if (m_object->isAttachment()) {
+ NSView* attachView = [self attachmentView];
+ if ([[attachView accessibilityAttributeNames] containsObject:NSAccessibilitySubroleAttribute]) {
+ return [attachView accessibilityAttributeValue:NSAccessibilitySubroleAttribute];
+ }
+ }
+
+ if (m_object->isTreeItem())
+ return NSAccessibilityOutlineRowSubrole;
+
+ if (m_object->isList()) {
+ AccessibilityList* listObject = static_cast<AccessibilityList*>(m_object);
+ if (listObject->isUnorderedList() || listObject->isOrderedList())
+ return NSAccessibilityContentListSubrole;
+ if (listObject->isDefinitionList())
+ return NSAccessibilityDefinitionListSubrole;
+ }
+
+ // ARIA content subroles.
+ switch (m_object->roleValue()) {
+ case LandmarkApplicationRole:
+ return @"AXLandmarkApplication";
+ case LandmarkBannerRole:
+ return @"AXLandmarkBanner";
+ case LandmarkComplementaryRole:
+ return @"AXLandmarkComplementary";
+ case LandmarkContentInfoRole:
+ return @"AXLandmarkContentInfo";
+ case LandmarkMainRole:
+ return @"AXLandmarkMain";
+ case LandmarkNavigationRole:
+ return @"AXLandmarkNavigation";
+ case LandmarkSearchRole:
+ return @"AXLandmarkSearch";
+ case ApplicationAlertRole:
+ return @"AXApplicationAlert";
+ case ApplicationAlertDialogRole:
+ return @"AXApplicationAlertDialog";
+ case ApplicationDialogRole:
+ return @"AXApplicationDialog";
+ case ApplicationLogRole:
+ return @"AXApplicationLog";
+ case ApplicationMarqueeRole:
+ return @"AXApplicationMarquee";
+ case ApplicationStatusRole:
+ return @"AXApplicationStatus";
+ case ApplicationTimerRole:
+ return @"AXApplicationTimer";
+ case DocumentRole:
+ return @"AXDocument";
+ case DocumentArticleRole:
+ return @"AXDocumentArticle";
+ case DocumentMathRole:
+ return @"AXDocumentMath";
+ case DocumentNoteRole:
+ return @"AXDocumentNote";
+ case DocumentRegionRole:
+ return @"AXDocumentRegion";
+ case UserInterfaceTooltipRole:
+ return @"AXUserInterfaceTooltip";
+ case TabPanelRole:
+ return @"AXTabPanel";
+ case DefinitionListTermRole:
+ return @"AXTerm";
+ case DefinitionListDefinitionRole:
+ return @"AXDefinition";
+ // Default doesn't return anything, so roles defined below can be chosen.
+ default:
+ break;
+ }
+
+ if (m_object->isMediaTimeline())
+ return NSAccessibilityTimelineSubrole;
+
+ return nil;
+}
+
+- (NSString*)roleDescription
+{
+ if (!m_object)
+ return nil;
+
+ // attachments have the AXImage role, but a different subrole
+ if (m_object->isAttachment())
+ return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityRoleDescriptionAttribute];
+
+ NSString* axRole = [self role];
+
+ if ([axRole isEqualToString:NSAccessibilityGroupRole]) {
+ switch (m_object->roleValue()) {
+ default:
+ return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, [self subrole]);
+ case LandmarkApplicationRole:
+ return AXARIAContentGroupText(@"ARIALandmarkApplication");
+ case LandmarkBannerRole:
+ return AXARIAContentGroupText(@"ARIALandmarkBanner");
+ case LandmarkComplementaryRole:
+ return AXARIAContentGroupText(@"ARIALandmarkComplementary");
+ case LandmarkContentInfoRole:
+ return AXARIAContentGroupText(@"ARIALandmarkContentInfo");
+ case LandmarkMainRole:
+ return AXARIAContentGroupText(@"ARIALandmarkMain");
+ case LandmarkNavigationRole:
+ return AXARIAContentGroupText(@"ARIALandmarkNavigation");
+ case LandmarkSearchRole:
+ return AXARIAContentGroupText(@"ARIALandmarkSearch");
+ case ApplicationAlertRole:
+ return AXARIAContentGroupText(@"ARIAApplicationAlert");
+ case ApplicationAlertDialogRole:
+ return AXARIAContentGroupText(@"ARIAApplicationAlertDialog");
+ case ApplicationDialogRole:
+ return AXARIAContentGroupText(@"ARIAApplicationDialog");
+ case ApplicationLogRole:
+ return AXARIAContentGroupText(@"ARIAApplicationLog");
+ case ApplicationMarqueeRole:
+ return AXARIAContentGroupText(@"ARIAApplicationMarquee");
+ case ApplicationStatusRole:
+ return AXARIAContentGroupText(@"ARIAApplicationStatus");
+ case ApplicationTimerRole:
+ return AXARIAContentGroupText(@"ARIAApplicationTimer");
+ case DocumentRole:
+ return AXARIAContentGroupText(@"ARIADocument");
+ case DocumentArticleRole:
+ return AXARIAContentGroupText(@"ARIADocumentArticle");
+ case DocumentMathRole:
+ return AXARIAContentGroupText(@"ARIADocumentMath");
+ case DocumentNoteRole:
+ return AXARIAContentGroupText(@"ARIADocumentNote");
+ case DocumentRegionRole:
+ return AXARIAContentGroupText(@"ARIADocumentRegion");
+ case UserInterfaceTooltipRole:
+ return AXARIAContentGroupText(@"ARIAUserInterfaceTooltip");
+ case TabPanelRole:
+ return AXARIAContentGroupText(@"ARIATabPanel");
+ case DefinitionListTermRole:
+ return AXDefinitionListTermText();
+ case DefinitionListDefinitionRole:
+ return AXDefinitionListDefinitionText();
+ }
+ }
+
+ if ([axRole isEqualToString:@"AXWebArea"])
+ return AXWebAreaText();
+
+ if ([axRole isEqualToString:@"AXLink"])
+ return AXLinkText();
+
+ if ([axRole isEqualToString:@"AXListMarker"])
+ return AXListMarkerText();
+
+ if ([axRole isEqualToString:@"AXImageMap"])
+ return AXImageMapText();
+
+ if ([axRole isEqualToString:@"AXHeading"])
+ return AXHeadingText();
+
+ // AppKit also returns AXTab for the role description for a tab item.
+ if (m_object->isTabItem())
+ return NSAccessibilityRoleDescription(@"AXTab", nil);
+
+ // We should try the system default role description for all other roles.
+ // If we get the same string back, then as a last resort, return unknown.
+ NSString* defaultRoleDescription = NSAccessibilityRoleDescription(axRole, [self subrole]);
+ if (![defaultRoleDescription isEqualToString:axRole])
+ return defaultRoleDescription;
+
+ return NSAccessibilityRoleDescription(NSAccessibilityUnknownRole, nil);
+}
+
+- (id)scrollViewParent
+{
+ if (!m_object || !m_object->isAccessibilityScrollView())
+ return nil;
+
+ // If this scroll view provides it's parent object (because it's a sub-frame), then
+ // we should not find the remoteAccessibilityParent.
+ if (m_object->parentObject())
+ return nil;
+
+ AccessibilityScrollView* scrollView = toAccessibilityScrollView(m_object);
+ ScrollView* scroll = scrollView->scrollView();
+ if (!scroll)
+ return nil;
+
+ if (scroll->platformWidget())
+ return scroll->platformWidget();
+
+ return [self remoteAccessibilityParentObject];
+}
+
+// FIXME: split up this function in a better way.
+// suggestions: Use a hash table that maps attribute names to function calls,
+// or maybe pointers to member functions
+- (id)accessibilityAttributeValue:(NSString*)attributeName
+{
+ if (![self updateObjectBackingStore])
+ return nil;
+
+ if ([attributeName isEqualToString: NSAccessibilityRoleAttribute])
+ return [self role];
+
+ if ([attributeName isEqualToString: NSAccessibilitySubroleAttribute])
+ return [self subrole];
+
+ if ([attributeName isEqualToString: NSAccessibilityRoleDescriptionAttribute])
+ return [self roleDescription];
+
+ if ([attributeName isEqualToString: NSAccessibilityParentAttribute]) {
+
+ // This will return the parent of the AXWebArea, if this is a web area.
+ id scrollViewParent = [self scrollViewParent];
+ if (scrollViewParent)
+ return scrollViewParent;
+
+ // Tree item (changed to AXRows) can only report the tree (AXOutline) as its parent.
+ if (m_object->isTreeItem()) {
+ AccessibilityObject* parent = m_object->parentObjectUnignored();
+ while (parent) {
+ if (parent->isTree())
+ return parent->wrapper();
+ parent = parent->parentObjectUnignored();
+ }
+ }
+
+ AccessibilityObject* parent = m_object->parentObjectUnignored();
+ if (parent)
+ return parent->wrapper();
+ return nil;
+ }
+
+ if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) {
+ if (m_object->children().isEmpty()) {
+ NSArray* children = [self renderWidgetChildren];
+ if (children != nil)
+ return children;
+ }
+
+ // The tree's (AXOutline) children are supposed to be its rows and columns.
+ // The ARIA spec doesn't have columns, so we just need rows.
+ if (m_object->isTree())
+ return [self accessibilityAttributeValue:NSAccessibilityRowsAttribute];
+
+ // A tree item should only expose its content as its children (not its rows)
+ if (m_object->isTreeItem()) {
+ AccessibilityObject::AccessibilityChildrenVector contentCopy;
+ m_object->ariaTreeItemContent(contentCopy);
+ return convertToNSArray(contentCopy);
+ }
+
+ return convertToNSArray(m_object->children());
+ }
+
+ if ([attributeName isEqualToString: NSAccessibilitySelectedChildrenAttribute]) {
+ if (m_object->isListBox()) {
+ AccessibilityObject::AccessibilityChildrenVector selectedChildrenCopy;
+ m_object->selectedChildren(selectedChildrenCopy);
+ return convertToNSArray(selectedChildrenCopy);
+ }
+ return nil;
+ }
+
+ if ([attributeName isEqualToString: NSAccessibilityVisibleChildrenAttribute]) {
+ if (m_object->isListBox()) {
+ AccessibilityObject::AccessibilityChildrenVector visibleChildrenCopy;
+ m_object->visibleChildren(visibleChildrenCopy);
+ return convertToNSArray(visibleChildrenCopy);
+ }
+ else if (m_object->isList())
+ return [self accessibilityAttributeValue:NSAccessibilityChildrenAttribute];
+
+ return nil;
+ }
+
+
+ if (m_object->isWebArea()) {
+ if ([attributeName isEqualToString:@"AXLinkUIElements"]) {
+ AccessibilityObject::AccessibilityChildrenVector links;
+ static_cast<AccessibilityRenderObject*>(m_object)->getDocumentLinks(links);
+ return convertToNSArray(links);
+ }
+ if ([attributeName isEqualToString:@"AXLoaded"])
+ return [NSNumber numberWithBool:m_object->isLoaded()];
+ if ([attributeName isEqualToString:@"AXLayoutCount"])
+ return [NSNumber numberWithInt:m_object->layoutCount()];
+ if ([attributeName isEqualToString:NSAccessibilityLoadingProgressAttribute])
+ return [NSNumber numberWithDouble:m_object->estimatedLoadingProgress()];
+ }
+
+ if (m_object->isTextControl()) {
+ if ([attributeName isEqualToString: NSAccessibilityNumberOfCharactersAttribute]) {
+ int length = m_object->textLength();
+ if (length < 0)
+ return nil;
+ return [NSNumber numberWithUnsignedInt:length];
+ }
+ if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute]) {
+ String selectedText = m_object->selectedText();
+ if (selectedText.isNull())
+ return nil;
+ return (NSString*)selectedText;
+ }
+ if ([attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute]) {
+ PlainTextRange textRange = m_object->selectedTextRange();
+ if (textRange.isNull())
+ return [NSValue valueWithRange:NSMakeRange(0, 0)];
+ return [NSValue valueWithRange:NSMakeRange(textRange.start, textRange.length)];
+ }
+ // TODO: Get actual visible range. <rdar://problem/4712101>
+ if ([attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute])
+ return m_object->isPasswordField() ? nil : [NSValue valueWithRange: NSMakeRange(0, m_object->textLength())];
+ if ([attributeName isEqualToString: NSAccessibilityInsertionPointLineNumberAttribute]) {
+ // if selectionEnd > 0, then there is selected text and this question should not be answered
+ if (m_object->isPasswordField() || m_object->selectionEnd() > 0)
+ return nil;
+ int lineNumber = m_object->lineForPosition(m_object->visiblePositionForIndex(m_object->selectionStart(), true));
+ if (lineNumber < 0)
+ return nil;
+ return [NSNumber numberWithInt:lineNumber];
+ }
+ }
+
+ if ([attributeName isEqualToString: NSAccessibilityURLAttribute]) {
+ KURL url = m_object->url();
+ if (url.isNull())
+ return nil;
+ return (NSURL*)url;
+ }
+
+ if ([attributeName isEqualToString: @"AXVisited"])
+ return [NSNumber numberWithBool: m_object->isVisited()];
+
+ if ([attributeName isEqualToString: NSAccessibilityTitleAttribute]) {
+ if (m_object->isAttachment()) {
+ if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityTitleAttribute])
+ return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityTitleAttribute];
+ }
+ return m_object->title();
+ }
+
+ if ([attributeName isEqualToString: NSAccessibilityDescriptionAttribute]) {
+ if (m_object->isAttachment()) {
+ if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityDescriptionAttribute])
+ return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityDescriptionAttribute];
+ }
+ return m_object->accessibilityDescription();
+ }
+
+ if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) {
+ if (m_object->isAttachment()) {
+ if ([[[self attachmentView] accessibilityAttributeNames] containsObject:NSAccessibilityValueAttribute])
+ return [[self attachmentView] accessibilityAttributeValue:NSAccessibilityValueAttribute];
+ }
+ if (m_object->isProgressIndicator() || m_object->isSlider() || m_object->isScrollbar())
+ return [NSNumber numberWithFloat:m_object->valueForRange()];
+ if (m_object->roleValue() == SliderThumbRole)
+ return [NSNumber numberWithFloat:m_object->parentObject()->valueForRange()];
+ if (m_object->isHeading())
+ return [NSNumber numberWithInt:m_object->headingLevel()];
+
+ if (m_object->isCheckboxOrRadio()) {
+ switch (m_object->checkboxOrRadioValue()) {
+ case ButtonStateOff:
+ return [NSNumber numberWithInt:0];
+ case ButtonStateOn:
+ return [NSNumber numberWithInt:1];
+ case ButtonStateMixed:
+ return [NSNumber numberWithInt:2];
+ }
+ }
+
+ // radio groups return the selected radio button as the AXValue
+ if (m_object->isRadioGroup()) {
+ AccessibilityObject* radioButton = m_object->selectedRadioButton();
+ if (!radioButton)
+ return nil;
+ return radioButton->wrapper();
+ }
+
+ if (m_object->isTabList()) {
+ AccessibilityObject* tabItem = m_object->selectedTabItem();
+ if (!tabItem)
+ return nil;
+ return tabItem->wrapper();
+ }
+
+ if (m_object->isTabItem())
+ return [NSNumber numberWithInt:m_object->isSelected()];
+
+ return m_object->stringValue();
+ }
+
+ if ([attributeName isEqualToString: NSAccessibilityMinValueAttribute])
+ return [NSNumber numberWithFloat:m_object->minValueForRange()];
+
+ if ([attributeName isEqualToString: NSAccessibilityMaxValueAttribute])
+ return [NSNumber numberWithFloat:m_object->maxValueForRange()];
+
+ if ([attributeName isEqualToString: NSAccessibilityHelpAttribute])
+ return m_object->helpText();
+
+ if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute])
+ return [NSNumber numberWithBool: m_object->isFocused()];
+
+ if ([attributeName isEqualToString: NSAccessibilityEnabledAttribute])
+ return [NSNumber numberWithBool: m_object->isEnabled()];
+
+ if ([attributeName isEqualToString: NSAccessibilitySizeAttribute]) {
+ IntSize s = m_object->size();
+ return [NSValue valueWithSize: NSMakeSize(s.width(), s.height())];
+ }
+
+ if ([attributeName isEqualToString: NSAccessibilityPositionAttribute])
+ return [self position];
+
+ if ([attributeName isEqualToString: NSAccessibilityWindowAttribute] ||
+ [attributeName isEqualToString: NSAccessibilityTopLevelUIElementAttribute]) {
+
+ id remoteParent = [self remoteAccessibilityParentObject];
+ if (remoteParent)
+ return [remoteParent accessibilityAttributeValue:attributeName];
+
+ FrameView* fv = m_object->documentFrameView();
+ if (fv)
+ return [fv->platformWidget() window];
+ return nil;
+ }
+
+ if ([attributeName isEqualToString:NSAccessibilityAccessKeyAttribute]) {
+ AtomicString accessKey = m_object->accessKey();
+ if (accessKey.isNull())
+ return nil;
+ return accessKey;
+ }
+
+ if ([attributeName isEqualToString:NSAccessibilityTabsAttribute]) {
+ if (m_object->isTabList()) {
+ AccessibilityObject::AccessibilityChildrenVector tabsChildren;
+ m_object->tabChildren(tabsChildren);
+ return convertToNSArray(tabsChildren);
+ }
+ }
+
+ if ([attributeName isEqualToString:NSAccessibilityContentsAttribute]) {
+ // The contents of a tab list are all the children except the tabs.
+ if (m_object->isTabList()) {
+ AccessibilityObject::AccessibilityChildrenVector children = m_object->children();
+ AccessibilityObject::AccessibilityChildrenVector tabsChildren;
+ m_object->tabChildren(tabsChildren);
+
+ AccessibilityObject::AccessibilityChildrenVector contents;
+ unsigned childrenSize = children.size();
+ for (unsigned k = 0; k < childrenSize; ++k) {
+ if (tabsChildren.find(children[k]) == WTF::notFound)
+ contents.append(children[k]);
+ }
+ return convertToNSArray(contents);
+ } else if (m_object->isScrollView()) {
+ AccessibilityObject::AccessibilityChildrenVector children = m_object->children();
+
+ // A scrollView's contents are everything except the scroll bars.
+ AccessibilityObject::AccessibilityChildrenVector contents;
+ unsigned childrenSize = children.size();
+ for (unsigned k = 0; k < childrenSize; ++k) {
+ if (!children[k]->isScrollbar())
+ contents.append(children[k]);
+ }
+ return convertToNSArray(contents);
+ }
+ }
+
+ if (m_object->isAccessibilityTable()) {
+ // TODO: distinguish between visible and non-visible rows
+ if ([attributeName isEqualToString:NSAccessibilityRowsAttribute] ||
+ [attributeName isEqualToString:NSAccessibilityVisibleRowsAttribute]) {
+ return convertToNSArray(static_cast<AccessibilityTable*>(m_object)->rows());
+ }
+ // TODO: distinguish between visible and non-visible columns
+ if ([attributeName isEqualToString:NSAccessibilityColumnsAttribute] ||
+ [attributeName isEqualToString:NSAccessibilityVisibleColumnsAttribute]) {
+ return convertToNSArray(static_cast<AccessibilityTable*>(m_object)->columns());
+ }
+
+ if ([attributeName isEqualToString:NSAccessibilitySelectedRowsAttribute]) {
+ AccessibilityObject::AccessibilityChildrenVector selectedChildrenCopy;
+ m_object->selectedChildren(selectedChildrenCopy);
+ return convertToNSArray(selectedChildrenCopy);
+ }
+
+ // HTML tables don't support these
+ if ([attributeName isEqualToString:NSAccessibilitySelectedColumnsAttribute] ||
+ [attributeName isEqualToString:NSAccessibilitySelectedCellsAttribute])
+ return nil;
+
+ if ([attributeName isEqualToString:(NSString *)kAXColumnHeaderUIElementsAttribute]) {
+ AccessibilityObject::AccessibilityChildrenVector columnHeaders;
+ static_cast<AccessibilityTable*>(m_object)->columnHeaders(columnHeaders);
+ return convertToNSArray(columnHeaders);
+ }
+
+ if ([attributeName isEqualToString:NSAccessibilityHeaderAttribute]) {
+ AccessibilityObject* headerContainer = static_cast<AccessibilityTable*>(m_object)->headerContainer();
+ if (headerContainer)
+ return headerContainer->wrapper();
+ return nil;
+ }
+
+ if ([attributeName isEqualToString:NSAccessibilityRowHeaderUIElementsAttribute]) {
+ AccessibilityObject::AccessibilityChildrenVector rowHeaders;
+ static_cast<AccessibilityTable*>(m_object)->rowHeaders(rowHeaders);
+ return convertToNSArray(rowHeaders);
+ }
+
+ if ([attributeName isEqualToString:NSAccessibilityVisibleCellsAttribute]) {
+ AccessibilityObject::AccessibilityChildrenVector cells;
+ static_cast<AccessibilityTable*>(m_object)->cells(cells);
+ return convertToNSArray(cells);
+ }
+ }
+
+ if (m_object->isTableColumn()) {
+ if ([attributeName isEqualToString:NSAccessibilityIndexAttribute])
+ return [NSNumber numberWithInt:static_cast<AccessibilityTableColumn*>(m_object)->columnIndex()];
+
+ // rows attribute for a column is the list of all the elements in that column at each row
+ if ([attributeName isEqualToString:NSAccessibilityRowsAttribute] ||
+ [attributeName isEqualToString:NSAccessibilityVisibleRowsAttribute]) {
+ return convertToNSArray(static_cast<AccessibilityTableColumn*>(m_object)->children());
+ }
+ if ([attributeName isEqualToString:NSAccessibilityHeaderAttribute]) {
+ AccessibilityObject* header = static_cast<AccessibilityTableColumn*>(m_object)->headerObject();
+ if (!header)
+ return nil;
+ return header->wrapper();
+ }
+ }
+
+ if (m_object->isTableCell()) {
+ if ([attributeName isEqualToString:NSAccessibilityRowIndexRangeAttribute]) {
+ pair<int, int> rowRange;
+ static_cast<AccessibilityTableCell*>(m_object)->rowIndexRange(rowRange);
+ return [NSValue valueWithRange:NSMakeRange(rowRange.first, rowRange.second)];
+ }
+ if ([attributeName isEqualToString:NSAccessibilityColumnIndexRangeAttribute]) {
+ pair<int, int> columnRange;
+ static_cast<AccessibilityTableCell*>(m_object)->columnIndexRange(columnRange);
+ return [NSValue valueWithRange:NSMakeRange(columnRange.first, columnRange.second)];
+ }
+ }
+
+ if (m_object->isTree()) {
+ if ([attributeName isEqualToString:NSAccessibilitySelectedRowsAttribute]) {
+ AccessibilityObject::AccessibilityChildrenVector selectedChildrenCopy;
+ m_object->selectedChildren(selectedChildrenCopy);
+ return convertToNSArray(selectedChildrenCopy);
+ }
+ if ([attributeName isEqualToString:NSAccessibilityRowsAttribute]) {
+ AccessibilityObject::AccessibilityChildrenVector rowsCopy;
+ m_object->ariaTreeRows(rowsCopy);
+ return convertToNSArray(rowsCopy);
+ }
+
+ // TreeRoles do not support columns, but Mac AX expects to be able to ask about columns at the least.
+ if ([attributeName isEqualToString:NSAccessibilityColumnsAttribute])
+ return [NSArray array];
+ }
+
+ if ([attributeName isEqualToString:NSAccessibilityIndexAttribute]) {
+ if (m_object->isTreeItem()) {
+ AccessibilityObject* parent = m_object->parentObject();
+ for (; parent && !parent->isTree(); parent = parent->parentObject())
+ { }
+
+ if (!parent)
+ return nil;
+
+ // Find the index of this item by iterating the parents.
+ AccessibilityObject::AccessibilityChildrenVector rowsCopy;
+ parent->ariaTreeRows(rowsCopy);
+ size_t count = rowsCopy.size();
+ for (size_t k = 0; k < count; ++k)
+ if (rowsCopy[k]->wrapper() == self)
+ return [NSNumber numberWithUnsignedInt:k];
+
+ return nil;
+ }
+ if (m_object->isTableRow()) {
+ if ([attributeName isEqualToString:NSAccessibilityIndexAttribute])
+ return [NSNumber numberWithInt:static_cast<AccessibilityTableRow*>(m_object)->rowIndex()];
+ }
+ }
+
+ // The rows that are considered inside this row.
+ if ([attributeName isEqualToString:NSAccessibilityDisclosedRowsAttribute]) {
+ if (m_object->isTreeItem()) {
+ AccessibilityObject::AccessibilityChildrenVector rowsCopy;
+ m_object->ariaTreeItemDisclosedRows(rowsCopy);
+ return convertToNSArray(rowsCopy);
+ } else if (m_object->isARIATreeGridRow()) {
+ AccessibilityObject::AccessibilityChildrenVector rowsCopy;
+ static_cast<AccessibilityARIAGridRow*>(m_object)->disclosedRows(rowsCopy);
+ return convertToNSArray(rowsCopy);
+ }
+ }
+
+ // The row that contains this row. It should be the same as the first parent that is a treeitem.
+ if ([attributeName isEqualToString:NSAccessibilityDisclosedByRowAttribute]) {
+ if (m_object->isTreeItem()) {
+ AccessibilityObject* parent = m_object->parentObject();
+ while (parent) {
+ if (parent->isTreeItem())
+ return parent->wrapper();
+ // If the parent is the tree itself, then this value == nil.
+ if (parent->isTree())
+ return nil;
+ parent = parent->parentObject();
+ }
+ return nil;
+ } else if (m_object->isARIATreeGridRow()) {
+ AccessibilityObject* row = static_cast<AccessibilityARIAGridRow*>(m_object)->disclosedByRow();
+ if (!row)
+ return nil;
+ return row->wrapper();
+ }
+ }
+
+ if ([attributeName isEqualToString:NSAccessibilityDisclosureLevelAttribute])
+ return [NSNumber numberWithInt:m_object->hierarchicalLevel()];
+ if ([attributeName isEqualToString:NSAccessibilityDisclosingAttribute])
+ return [NSNumber numberWithBool:m_object->isExpanded()];
+
+ if ((m_object->isListBox() || m_object->isList()) && [attributeName isEqualToString:NSAccessibilityOrientationAttribute])
+ return NSAccessibilityVerticalOrientationValue;
+
+ if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"])
+ return [self textMarkerRangeForSelection];
+
+ if (m_object->isAccessibilityRenderObject()) {
+ RenderObject* renderer = static_cast<AccessibilityRenderObject*>(m_object)->renderer();
+ if (!renderer)
+ return nil;
+
+ if ([attributeName isEqualToString: @"AXStartTextMarker"])
+ return [self textMarkerForVisiblePosition:startOfDocument(renderer->document())];
+ if ([attributeName isEqualToString: @"AXEndTextMarker"])
+ return [self textMarkerForVisiblePosition:endOfDocument(renderer->document())];
+
+ if ([attributeName isEqualToString:NSAccessibilityBlockQuoteLevelAttribute])
+ return [NSNumber numberWithInt:blockquoteLevel(renderer)];
+ } else {
+ if ([attributeName isEqualToString:NSAccessibilityBlockQuoteLevelAttribute]) {
+ AccessibilityObject* parent = m_object->parentObjectUnignored();
+ if (!parent)
+ return [NSNumber numberWithInt:0];
+ return [parent->wrapper() accessibilityAttributeValue:NSAccessibilityBlockQuoteLevelAttribute];
+ }
+ }
+
+ if ([attributeName isEqualToString: NSAccessibilityLinkedUIElementsAttribute]) {
+ AccessibilityObject::AccessibilityChildrenVector linkedUIElements;
+ m_object->linkedUIElements(linkedUIElements);
+ if (linkedUIElements.size() == 0)
+ return nil;
+ return convertToNSArray(linkedUIElements);
+ }
+
+ if ([attributeName isEqualToString: NSAccessibilitySelectedAttribute])
+ return [NSNumber numberWithBool:m_object->isSelected()];
+
+ if ([attributeName isEqualToString: NSAccessibilityServesAsTitleForUIElementsAttribute] && m_object->isMenuButton()) {
+ AccessibilityObject* uiElement = static_cast<AccessibilityRenderObject*>(m_object)->menuForMenuButton();
+ if (uiElement)
+ return [NSArray arrayWithObject:uiElement->wrapper()];
+ }
+
+ if ([attributeName isEqualToString:NSAccessibilityTitleUIElementAttribute]) {
+ AccessibilityObject* obj = m_object->titleUIElement();
+ if (obj)
+ return obj->wrapper();
+ return nil;
+ }
+
+ if ([attributeName isEqualToString:NSAccessibilityValueDescriptionAttribute])
+ return m_object->valueDescription();
+
+ if ([attributeName isEqualToString:NSAccessibilityOrientationAttribute]) {
+ AccessibilityOrientation elementOrientation = m_object->orientation();
+ if (elementOrientation == AccessibilityOrientationVertical)
+ return NSAccessibilityVerticalOrientationValue;
+ if (elementOrientation == AccessibilityOrientationHorizontal)
+ return NSAccessibilityHorizontalOrientationValue;
+ return nil;
+ }
+
+ if ([attributeName isEqualToString:NSAccessibilityHorizontalScrollBarAttribute]) {
+ AccessibilityObject* scrollBar = m_object->scrollBar(AccessibilityOrientationHorizontal);
+ if (scrollBar)
+ return scrollBar->wrapper();
+ return nil;
+ }
+ if ([attributeName isEqualToString:NSAccessibilityVerticalScrollBarAttribute]) {
+ AccessibilityObject* scrollBar = m_object->scrollBar(AccessibilityOrientationVertical);
+ if (scrollBar)
+ return scrollBar->wrapper();
+ return nil;
+ }
+
+ if ([attributeName isEqualToString:NSAccessibilityLanguageAttribute])
+ return m_object->language();
+
+ if ([attributeName isEqualToString:NSAccessibilityExpandedAttribute])
+ return [NSNumber numberWithBool:m_object->isExpanded()];
+
+ if ([attributeName isEqualToString:NSAccessibilityRequiredAttribute])
+ return [NSNumber numberWithBool:m_object->isRequired()];
+
+ if ([attributeName isEqualToString:NSAccessibilityInvalidAttribute])
+ return m_object->invalidStatus();
+
+ if ([attributeName isEqualToString:NSAccessibilityOwnsAttribute]) {
+ AccessibilityObject::AccessibilityChildrenVector ariaOwns;
+ m_object->ariaOwnsElements(ariaOwns);
+ return convertToNSArray(ariaOwns);
+ }
+
+ if ([attributeName isEqualToString:NSAccessibilityGrabbedAttribute])
+ return [NSNumber numberWithBool:m_object->isARIAGrabbed()];
+
+ if ([attributeName isEqualToString:NSAccessibilityDropEffectsAttribute]) {
+ Vector<String> dropEffects;
+ m_object->determineARIADropEffects(dropEffects);
+ size_t length = dropEffects.size();
+
+ NSMutableArray* dropEffectsArray = [NSMutableArray arrayWithCapacity:length];
+ for (size_t i = 0; i < length; ++i)
+ [dropEffectsArray addObject:dropEffects[i]];
+ return dropEffectsArray;
+ }
+
+ if ([attributeName isEqualToString:NSAccessibilityPlaceholderValueAttribute])
+ return m_object->placeholderValue();
+
+ if ([attributeName isEqualToString:NSAccessibilityHasPopupAttribute])
+ return [NSNumber numberWithBool:m_object->ariaHasPopup()];
+
+ // ARIA Live region attributes.
+ if ([attributeName isEqualToString:NSAccessibilityARIALiveAttribute])
+ return m_object->ariaLiveRegionStatus();
+ if ([attributeName isEqualToString:NSAccessibilityARIARelevantAttribute])
+ return m_object->ariaLiveRegionRelevant();
+ if ([attributeName isEqualToString:NSAccessibilityARIAAtomicAttribute])
+ return [NSNumber numberWithBool:m_object->ariaLiveRegionAtomic()];
+ if ([attributeName isEqualToString:NSAccessibilityARIABusyAttribute])
+ return [NSNumber numberWithBool:m_object->ariaLiveRegionBusy()];
+
+ // this is used only by DumpRenderTree for testing
+ if ([attributeName isEqualToString:@"AXClickPoint"])
+ return [NSValue valueWithPoint:m_object->clickPoint()];
+
+ // This is used by DRT to verify CSS3 speech works.
+ if ([attributeName isEqualToString:@"AXDRTSpeechAttribute"]) {
+ ESpeak speakProperty = m_object->speakProperty();
+ switch (speakProperty) {
+ case SpeakNone:
+ return @"none";
+ case SpeakSpellOut:
+ return @"spell-out";
+ case SpeakDigits:
+ return @"digits";
+ case SpeakLiteralPunctuation:
+ return @"literal-punctuation";
+ case SpeakNoPunctuation:
+ return @"no-punctuation";
+ default:
+ case SpeakNormal:
+ return @"normal";
+ }
+ }
+
+ return nil;
+}
+
+- (id)accessibilityFocusedUIElement
+{
+ if (![self updateObjectBackingStore])
+ return nil;
+
+ RefPtr<AccessibilityObject> focusedObj = m_object->focusedUIElement();
+
+ if (!focusedObj)
+ return nil;
+
+ return focusedObj->wrapper();
+}
+
+- (id)accessibilityHitTest:(NSPoint)point
+{
+ if (![self updateObjectBackingStore])
+ return nil;
+
+ m_object->updateChildrenIfNecessary();
+ RefPtr<AccessibilityObject> axObject = m_object->accessibilityHitTest(IntPoint(point));
+ if (axObject)
+ return NSAccessibilityUnignoredAncestor(axObject->wrapper());
+ return NSAccessibilityUnignoredAncestor(self);
+}
+
+- (BOOL)accessibilityIsAttributeSettable:(NSString*)attributeName
+{
+ if (![self updateObjectBackingStore])
+ return nil;
+
+ if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"])
+ return YES;
+
+ if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute])
+ return m_object->canSetFocusAttribute();
+
+ if ([attributeName isEqualToString: NSAccessibilityValueAttribute])
+ return m_object->canSetValueAttribute();
+
+ if ([attributeName isEqualToString: NSAccessibilitySelectedAttribute])
+ return m_object->canSetSelectedAttribute();
+
+ if ([attributeName isEqualToString: NSAccessibilitySelectedChildrenAttribute])
+ return m_object->canSetSelectedChildrenAttribute();
+
+ if ([attributeName isEqualToString:NSAccessibilityDisclosingAttribute])
+ return m_object->canSetExpandedAttribute();
+
+ if ([attributeName isEqualToString:NSAccessibilitySelectedRowsAttribute])
+ return YES;
+
+ if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute] ||
+ [attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute] ||
+ [attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute])
+ return m_object->canSetTextRangeAttributes();
+
+ if ([attributeName isEqualToString:NSAccessibilityGrabbedAttribute])
+ return YES;
+
+ return NO;
+}
+
+// accessibilityShouldUseUniqueId is an AppKit method we override so that
+// objects will be given a unique ID, and therefore allow AppKit to know when they
+// become obsolete (e.g. when the user navigates to a new web page, making this one
+// unrendered but not deallocated because it is in the back/forward cache).
+// It is important to call NSAccessibilityUnregisterUniqueIdForUIElement in the
+// appropriate place (e.g. dealloc) to remove these non-retained references from
+// AppKit's id mapping tables. We do this in detach by calling unregisterUniqueIdForUIElement.
+//
+// Registering an object is also required for observing notifications. Only registered objects can be observed.
+- (BOOL)accessibilityIsIgnored
+{
+ if (![self updateObjectBackingStore])
+ return YES;
+
+ if (m_object->isAttachment())
+ return [[self attachmentView] accessibilityIsIgnored];
+ return m_object->accessibilityIsIgnored();
+}
+
+- (NSArray* )accessibilityParameterizedAttributeNames
+{
+ if (![self updateObjectBackingStore])
+ return nil;
+
+ if (m_object->isAttachment())
+ return nil;
+
+ static NSArray* paramAttrs = nil;
+ static NSArray* textParamAttrs = nil;
+ static NSArray* tableParamAttrs = nil;
+ if (paramAttrs == nil) {
+ paramAttrs = [[NSArray alloc] initWithObjects:
+ @"AXUIElementForTextMarker",
+ @"AXTextMarkerRangeForUIElement",
+ @"AXLineForTextMarker",
+ @"AXTextMarkerRangeForLine",
+ @"AXStringForTextMarkerRange",
+ @"AXTextMarkerForPosition",
+ @"AXBoundsForTextMarkerRange",
+ @"AXAttributedStringForTextMarkerRange",
+ @"AXTextMarkerRangeForUnorderedTextMarkers",
+ @"AXNextTextMarkerForTextMarker",
+ @"AXPreviousTextMarkerForTextMarker",
+ @"AXLeftWordTextMarkerRangeForTextMarker",
+ @"AXRightWordTextMarkerRangeForTextMarker",
+ @"AXLeftLineTextMarkerRangeForTextMarker",
+ @"AXRightLineTextMarkerRangeForTextMarker",
+ @"AXSentenceTextMarkerRangeForTextMarker",
+ @"AXParagraphTextMarkerRangeForTextMarker",
+ @"AXNextWordEndTextMarkerForTextMarker",
+ @"AXPreviousWordStartTextMarkerForTextMarker",
+ @"AXNextLineEndTextMarkerForTextMarker",
+ @"AXPreviousLineStartTextMarkerForTextMarker",
+ @"AXNextSentenceEndTextMarkerForTextMarker",
+ @"AXPreviousSentenceStartTextMarkerForTextMarker",
+ @"AXNextParagraphEndTextMarkerForTextMarker",
+ @"AXPreviousParagraphStartTextMarkerForTextMarker",
+ @"AXStyleTextMarkerRangeForTextMarker",
+ @"AXLengthForTextMarkerRange",
+ NSAccessibilityBoundsForRangeParameterizedAttribute,
+ NSAccessibilityStringForRangeParameterizedAttribute,
+ nil];
+ }
+
+ if (textParamAttrs == nil) {
+ NSMutableArray* tempArray = [[NSMutableArray alloc] initWithArray:paramAttrs];
+ [tempArray addObject:(NSString*)kAXLineForIndexParameterizedAttribute];
+ [tempArray addObject:(NSString*)kAXRangeForLineParameterizedAttribute];
+ [tempArray addObject:(NSString*)kAXStringForRangeParameterizedAttribute];
+ [tempArray addObject:(NSString*)kAXRangeForPositionParameterizedAttribute];
+ [tempArray addObject:(NSString*)kAXRangeForIndexParameterizedAttribute];
+ [tempArray addObject:(NSString*)kAXBoundsForRangeParameterizedAttribute];
+ [tempArray addObject:(NSString*)kAXRTFForRangeParameterizedAttribute];
+ [tempArray addObject:(NSString*)kAXAttributedStringForRangeParameterizedAttribute];
+ [tempArray addObject:(NSString*)kAXStyleRangeForIndexParameterizedAttribute];
+ textParamAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+ if (tableParamAttrs == nil) {
+ NSMutableArray* tempArray = [[NSMutableArray alloc] initWithArray:paramAttrs];
+ [tempArray addObject:NSAccessibilityCellForColumnAndRowParameterizedAttribute];
+ tableParamAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+
+ if (m_object->isPasswordField())
+ return [NSArray array];
+
+ if (!m_object->isAccessibilityRenderObject())
+ return paramAttrs;
+
+ if (m_object->isTextControl())
+ return textParamAttrs;
+
+ if (m_object->isAccessibilityTable())
+ return tableParamAttrs;
+
+ if (m_object->isMenuRelated())
+ return nil;
+
+ return paramAttrs;
+}
+
+- (void)accessibilityPerformPressAction
+{
+ if (![self updateObjectBackingStore])
+ return;
+
+ if (m_object->isAttachment())
+ [[self attachmentView] accessibilityPerformAction:NSAccessibilityPressAction];
+ else
+ m_object->press();
+}
+
+- (void)accessibilityPerformIncrementAction
+{
+ if (![self updateObjectBackingStore])
+ return;
+
+ if (m_object->isAttachment())
+ [[self attachmentView] accessibilityPerformAction:NSAccessibilityIncrementAction];
+ else
+ m_object->increment();
+}
+
+- (void)accessibilityPerformDecrementAction
+{
+ if (![self updateObjectBackingStore])
+ return;
+
+ if (m_object->isAttachment())
+ [[self attachmentView] accessibilityPerformAction:NSAccessibilityDecrementAction];
+ else
+ m_object->decrement();
+}
+
+- (void)accessibilityPerformShowMenuAction
+{
+ if (m_object->roleValue() == ComboBoxRole)
+ m_object->setIsExpanded(true);
+ else {
+ // This needs to be performed in an iteration of the run loop that did not start from an AX call.
+ // If it's the same run loop iteration, the menu open notification won't be sent
+ [self performSelector:@selector(accessibilityShowContextMenu) withObject:nil afterDelay:0.0];
+ }
+}
+
+- (void)accessibilityShowContextMenu
+{
+ FrameView* frameView = m_object->documentFrameView();
+ if (!frameView)
+ return;
+
+ // simulate a click in the middle of the object
+ IntPoint clickPoint = m_object->clickPoint();
+ NSPoint nsClickPoint = NSMakePoint(clickPoint.x(), clickPoint.y());
+
+ NSView* view = nil;
+ if (m_object->isAttachment())
+ view = [self attachmentView];
+ else
+ view = frameView->documentView();
+
+ if (!view)
+ return;
+
+ NSPoint nsScreenPoint = [view convertPoint:nsClickPoint toView:nil];
+
+ // Show the contextual menu for this event.
+ NSEvent* event = [NSEvent mouseEventWithType:NSRightMouseDown location:nsScreenPoint modifierFlags:0 timestamp:0 windowNumber:[[view window] windowNumber] context:0 eventNumber:0 clickCount:1 pressure:1];
+ NSMenu* menu = [view menuForEvent:event];
+
+ if (menu)
+ [NSMenu popUpContextMenu:menu withEvent:event forView:view];
+}
+
+- (void)accessibilityPerformAction:(NSString*)action
+{
+ if (![self updateObjectBackingStore])
+ return;
+
+ if ([action isEqualToString:NSAccessibilityPressAction])
+ [self accessibilityPerformPressAction];
+
+ else if ([action isEqualToString:NSAccessibilityShowMenuAction])
+ [self accessibilityPerformShowMenuAction];
+
+ else if ([action isEqualToString:NSAccessibilityIncrementAction])
+ [self accessibilityPerformIncrementAction];
+
+ else if ([action isEqualToString:NSAccessibilityDecrementAction])
+ [self accessibilityPerformDecrementAction];
+}
+
+- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attributeName
+{
+ if (![self updateObjectBackingStore])
+ return;
+
+ WebCoreTextMarkerRange* textMarkerRange = nil;
+ NSNumber* number = nil;
+ NSString* string = nil;
+ NSRange range = {0, 0};
+ NSArray* array = nil;
+
+ // decode the parameter
+ if ([[WebCoreViewFactory sharedFactory] objectIsTextMarkerRange:value])
+ textMarkerRange = (WebCoreTextMarkerRange*) value;
+
+ else if ([value isKindOfClass:[NSNumber self]])
+ number = value;
+
+ else if ([value isKindOfClass:[NSString self]])
+ string = value;
+
+ else if ([value isKindOfClass:[NSValue self]])
+ range = [value rangeValue];
+
+ else if ([value isKindOfClass:[NSArray self]])
+ array = value;
+
+ // handle the command
+ if ([attributeName isEqualToString: @"AXSelectedTextMarkerRange"]) {
+ ASSERT(textMarkerRange);
+ m_object->setSelectedVisiblePositionRange([self visiblePositionRangeForTextMarkerRange:textMarkerRange]);
+ } else if ([attributeName isEqualToString: NSAccessibilityFocusedAttribute]) {
+ ASSERT(number);
+ m_object->setFocused([number intValue] != 0);
+ } else if ([attributeName isEqualToString: NSAccessibilityValueAttribute]) {
+ if (number && m_object->canSetNumericValue())
+ m_object->setValue([number floatValue]);
+ else if (string)
+ m_object->setValue(string);
+ } else if ([attributeName isEqualToString: NSAccessibilitySelectedAttribute]) {
+ if (!number)
+ return;
+ m_object->setSelected([number boolValue]);
+ } else if ([attributeName isEqualToString: NSAccessibilitySelectedChildrenAttribute]) {
+ if (!array || m_object->roleValue() != ListBoxRole)
+ return;
+ AccessibilityObject::AccessibilityChildrenVector selectedChildren;
+ convertToVector(array, selectedChildren);
+ static_cast<AccessibilityListBox*>(m_object)->setSelectedChildren(selectedChildren);
+ } else if (m_object->isTextControl()) {
+ if ([attributeName isEqualToString: NSAccessibilitySelectedTextAttribute]) {
+ m_object->setSelectedText(string);
+ } else if ([attributeName isEqualToString: NSAccessibilitySelectedTextRangeAttribute]) {
+ m_object->setSelectedTextRange(PlainTextRange(range.location, range.length));
+ } else if ([attributeName isEqualToString: NSAccessibilityVisibleCharacterRangeAttribute]) {
+ m_object->makeRangeVisible(PlainTextRange(range.location, range.length));
+ }
+ } else if ([attributeName isEqualToString:NSAccessibilityDisclosingAttribute])
+ m_object->setIsExpanded([number boolValue]);
+ else if ([attributeName isEqualToString:NSAccessibilitySelectedRowsAttribute]) {
+ AccessibilityObject::AccessibilityChildrenVector selectedRows;
+ convertToVector(array, selectedRows);
+ if (m_object->isTree() || m_object->isAccessibilityTable())
+ m_object->setSelectedRows(selectedRows);
+ } else if ([attributeName isEqualToString:NSAccessibilityGrabbedAttribute])
+ m_object->setARIAGrabbed([number boolValue]);
+}
+
+static RenderObject* rendererForView(NSView* view)
+{
+ if (![view conformsToProtocol:@protocol(WebCoreFrameView)])
+ return 0;
+
+ NSView<WebCoreFrameView>* frameView = (NSView<WebCoreFrameView>*)view;
+ Frame* frame = [frameView _web_frame];
+ if (!frame)
+ return 0;
+
+ Node* node = frame->document()->ownerElement();
+ if (!node)
+ return 0;
+
+ return node->renderer();
+}
+
+- (id)_accessibilityParentForSubview:(NSView*)subview
+{
+ RenderObject* renderer = rendererForView(subview);
+ if (!renderer)
+ return nil;
+
+ AccessibilityObject* obj = renderer->document()->axObjectCache()->getOrCreate(renderer);
+ if (obj)
+ return obj->parentObjectUnignored()->wrapper();
+ return nil;
+}
+
+- (NSString*)accessibilityActionDescription:(NSString*)action
+{
+ // we have no custom actions
+ return NSAccessibilityActionDescription(action);
+}
+
+// The CFAttributedStringType representation of the text associated with this accessibility
+// object that is specified by the given range.
+- (NSAttributedString*)doAXAttributedStringForRange:(NSRange)range
+{
+ PlainTextRange textRange = PlainTextRange(range.location, range.length);
+ VisiblePositionRange visiblePosRange = m_object->visiblePositionRangeForRange(textRange);
+ return [self doAXAttributedStringForTextMarkerRange:[self textMarkerRangeFromVisiblePositions:visiblePosRange.start endPosition:visiblePosRange.end]];
+}
+
+// The RTF representation of the text associated with this accessibility object that is
+// specified by the given range.
+- (NSData*)doAXRTFForRange:(NSRange)range
+{
+ NSAttributedString* attrString = [self doAXAttributedStringForRange:range];
+ return [attrString RTFFromRange: NSMakeRange(0, [attrString length]) documentAttributes: nil];
+}
+
+- (id)accessibilityAttributeValue:(NSString*)attribute forParameter:(id)parameter
+{
+ WebCoreTextMarker* textMarker = nil;
+ WebCoreTextMarkerRange* textMarkerRange = nil;
+ NSNumber* number = nil;
+ NSArray* array = nil;
+ RefPtr<AccessibilityObject> uiElement = 0;
+ NSPoint point = NSZeroPoint;
+ bool pointSet = false;
+ NSRange range = {0, 0};
+ bool rangeSet = false;
+
+ // basic parameter validation
+ if (!m_object || !attribute || !parameter)
+ return nil;
+
+ if (![self updateObjectBackingStore])
+ return nil;
+
+ // common parameter type check/casting. Nil checks in handlers catch wrong type case.
+ // NOTE: This assumes nil is not a valid parameter, because it is indistinguishable from
+ // a parameter of the wrong type.
+ if ([[WebCoreViewFactory sharedFactory] objectIsTextMarker:parameter])
+ textMarker = (WebCoreTextMarker*) parameter;
+
+ else if ([[WebCoreViewFactory sharedFactory] objectIsTextMarkerRange:parameter])
+ textMarkerRange = (WebCoreTextMarkerRange*) parameter;
+
+ else if ([parameter isKindOfClass:[AccessibilityObjectWrapper self]])
+ uiElement = [(AccessibilityObjectWrapper*)parameter accessibilityObject];
+
+ else if ([parameter isKindOfClass:[NSNumber self]])
+ number = parameter;
+
+ else if ([parameter isKindOfClass:[NSArray self]])
+ array = parameter;
+
+ else if ([parameter isKindOfClass:[NSValue self]] && strcmp([(NSValue*)parameter objCType], @encode(NSPoint)) == 0) {
+ pointSet = true;
+ point = [(NSValue*)parameter pointValue];
+
+ } else if ([parameter isKindOfClass:[NSValue self]] && strcmp([(NSValue*)parameter objCType], @encode(NSRange)) == 0) {
+ rangeSet = true;
+ range = [(NSValue*)parameter rangeValue];
+ } else {
+ // Attribute type is not supported. Allow super to handle.
+ return [super accessibilityAttributeValue:attribute forParameter:parameter];
+ }
+
+ // dispatch
+ if ([attribute isEqualToString:@"AXUIElementForTextMarker"]) {
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
+ AccessibilityObject* axObject = m_object->accessibilityObjectForPosition(visiblePos);
+ if (!axObject)
+ return nil;
+ return axObject->wrapper();
+ }
+
+ if ([attribute isEqualToString:@"AXTextMarkerRangeForUIElement"]) {
+ VisiblePositionRange vpRange = uiElement.get()->visiblePositionRange();
+ return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
+ }
+
+ if ([attribute isEqualToString:@"AXLineForTextMarker"]) {
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
+ return [NSNumber numberWithUnsignedInt:m_object->lineForPosition(visiblePos)];
+ }
+
+ if ([attribute isEqualToString:@"AXTextMarkerRangeForLine"]) {
+ VisiblePositionRange vpRange = m_object->visiblePositionRangeForLine([number intValue]);
+ return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
+ }
+
+ if ([attribute isEqualToString:@"AXStringForTextMarkerRange"]) {
+ VisiblePositionRange visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];
+ return m_object->stringForVisiblePositionRange(visiblePosRange);
+ }
+
+ if ([attribute isEqualToString:@"AXTextMarkerForPosition"]) {
+ IntPoint webCorePoint = IntPoint(point);
+ return pointSet ? [self textMarkerForVisiblePosition:m_object->visiblePositionForPoint(webCorePoint)] : nil;
+ }
+
+ if ([attribute isEqualToString:@"AXBoundsForTextMarkerRange"]) {
+ VisiblePositionRange visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];
+ NSRect rect = m_object->boundsForVisiblePositionRange(visiblePosRange);
+ return [NSValue valueWithRect:rect];
+ }
+
+ if ([attribute isEqualToString:NSAccessibilityBoundsForRangeParameterizedAttribute]) {
+ VisiblePosition start = m_object->visiblePositionForIndex(range.location);
+ VisiblePosition end = m_object->visiblePositionForIndex(range.location+range.length);
+ if (start.isNull() || end.isNull())
+ return nil;
+ NSRect rect = m_object->boundsForVisiblePositionRange(VisiblePositionRange(start, end));
+ return [NSValue valueWithRect:rect];
+ }
+
+ if ([attribute isEqualToString:NSAccessibilityStringForRangeParameterizedAttribute]) {
+ VisiblePosition start = m_object->visiblePositionForIndex(range.location);
+ VisiblePosition end = m_object->visiblePositionForIndex(range.location+range.length);
+ if (start.isNull() || end.isNull())
+ return nil;
+ return m_object->stringForVisiblePositionRange(VisiblePositionRange(start, end));
+ }
+
+ if ([attribute isEqualToString:@"AXAttributedStringForTextMarkerRange"])
+ return [self doAXAttributedStringForTextMarkerRange:textMarkerRange];
+
+ if ([attribute isEqualToString:@"AXTextMarkerRangeForUnorderedTextMarkers"]) {
+ if ([array count] < 2)
+ return nil;
+
+ WebCoreTextMarker* textMarker1 = (WebCoreTextMarker*) [array objectAtIndex:0];
+ WebCoreTextMarker* textMarker2 = (WebCoreTextMarker*) [array objectAtIndex:1];
+ if (![[WebCoreViewFactory sharedFactory] objectIsTextMarker:textMarker1]
+ || ![[WebCoreViewFactory sharedFactory] objectIsTextMarker:textMarker2])
+ return nil;
+
+ VisiblePosition visiblePos1 = [self visiblePositionForTextMarker:(textMarker1)];
+ VisiblePosition visiblePos2 = [self visiblePositionForTextMarker:(textMarker2)];
+ VisiblePositionRange vpRange = m_object->visiblePositionRangeForUnorderedPositions(visiblePos1, visiblePos2);
+ return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
+ }
+
+ if ([attribute isEqualToString:@"AXNextTextMarkerForTextMarker"]) {
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
+ return [self textMarkerForVisiblePosition:m_object->nextVisiblePosition(visiblePos)];
+ }
+
+ if ([attribute isEqualToString:@"AXPreviousTextMarkerForTextMarker"]) {
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
+ return [self textMarkerForVisiblePosition:m_object->previousVisiblePosition(visiblePos)];
+ }
+
+ if ([attribute isEqualToString:@"AXLeftWordTextMarkerRangeForTextMarker"]) {
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
+ VisiblePositionRange vpRange = m_object->positionOfLeftWord(visiblePos);
+ return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
+ }
+
+ if ([attribute isEqualToString:@"AXRightWordTextMarkerRangeForTextMarker"]) {
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
+ VisiblePositionRange vpRange = m_object->positionOfRightWord(visiblePos);
+ return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
+ }
+
+ if ([attribute isEqualToString:@"AXLeftLineTextMarkerRangeForTextMarker"]) {
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
+ VisiblePositionRange vpRange = m_object->leftLineVisiblePositionRange(visiblePos);
+ return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
+ }
+
+ if ([attribute isEqualToString:@"AXRightLineTextMarkerRangeForTextMarker"]) {
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
+ VisiblePositionRange vpRange = m_object->rightLineVisiblePositionRange(visiblePos);
+ return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
+ }
+
+ if ([attribute isEqualToString:@"AXSentenceTextMarkerRangeForTextMarker"]) {
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
+ VisiblePositionRange vpRange = m_object->sentenceForPosition(visiblePos);
+ return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
+ }
+
+ if ([attribute isEqualToString:@"AXParagraphTextMarkerRangeForTextMarker"]) {
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
+ VisiblePositionRange vpRange = m_object->paragraphForPosition(visiblePos);
+ return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
+ }
+
+ if ([attribute isEqualToString:@"AXNextWordEndTextMarkerForTextMarker"]) {
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
+ return [self textMarkerForVisiblePosition:m_object->nextWordEnd(visiblePos)];
+ }
+
+ if ([attribute isEqualToString:@"AXPreviousWordStartTextMarkerForTextMarker"]) {
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
+ return [self textMarkerForVisiblePosition:m_object->previousWordStart(visiblePos)];
+ }
+
+ if ([attribute isEqualToString:@"AXNextLineEndTextMarkerForTextMarker"]) {
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
+ return [self textMarkerForVisiblePosition:m_object->nextLineEndPosition(visiblePos)];
+ }
+
+ if ([attribute isEqualToString:@"AXPreviousLineStartTextMarkerForTextMarker"]) {
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
+ return [self textMarkerForVisiblePosition:m_object->previousLineStartPosition(visiblePos)];
+ }
+
+ if ([attribute isEqualToString:@"AXNextSentenceEndTextMarkerForTextMarker"]) {
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
+ return [self textMarkerForVisiblePosition:m_object->nextSentenceEndPosition(visiblePos)];
+ }
+
+ if ([attribute isEqualToString:@"AXPreviousSentenceStartTextMarkerForTextMarker"]) {
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
+ return [self textMarkerForVisiblePosition:m_object->previousSentenceStartPosition(visiblePos)];
+ }
+
+ if ([attribute isEqualToString:@"AXNextParagraphEndTextMarkerForTextMarker"]) {
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
+ return [self textMarkerForVisiblePosition:m_object->nextParagraphEndPosition(visiblePos)];
+ }
+
+ if ([attribute isEqualToString:@"AXPreviousParagraphStartTextMarkerForTextMarker"]) {
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
+ return [self textMarkerForVisiblePosition:m_object->previousParagraphStartPosition(visiblePos)];
+ }
+
+ if ([attribute isEqualToString:@"AXStyleTextMarkerRangeForTextMarker"]) {
+ VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
+ VisiblePositionRange vpRange = m_object->styleRangeForPosition(visiblePos);
+ return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
+ }
+
+ if ([attribute isEqualToString:@"AXLengthForTextMarkerRange"]) {
+ VisiblePositionRange visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];
+ int length = m_object->lengthForVisiblePositionRange(visiblePosRange);
+ if (length < 0)
+ return nil;
+ return [NSNumber numberWithInt:length];
+ }
+
+ // Used only by DumpRenderTree (so far).
+ if ([attribute isEqualToString:@"AXStartTextMarkerForTextMarkerRange"]) {
+ VisiblePositionRange visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];
+ return [self textMarkerForVisiblePosition:visiblePosRange.start];
+ }
+
+ if ([attribute isEqualToString:@"AXEndTextMarkerForTextMarkerRange"]) {
+ VisiblePositionRange visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];
+ return [self textMarkerForVisiblePosition:visiblePosRange.end];
+ }
+
+ if (m_object->isAccessibilityTable()) {
+ if ([attribute isEqualToString:NSAccessibilityCellForColumnAndRowParameterizedAttribute]) {
+ if (array == nil || [array count] != 2)
+ return nil;
+ AccessibilityTableCell* cell = static_cast<AccessibilityTable*>(m_object)->cellForColumnAndRow([[array objectAtIndex:0] unsignedIntValue], [[array objectAtIndex:1] unsignedIntValue]);
+ if (!cell)
+ return nil;
+
+ return cell->wrapper();
+ }
+ }
+
+ if (m_object->isTextControl()) {
+ if ([attribute isEqualToString: (NSString *)kAXLineForIndexParameterizedAttribute]) {
+ int lineNumber = m_object->doAXLineForIndex([number intValue]);
+ if (lineNumber < 0)
+ return nil;
+ return [NSNumber numberWithUnsignedInt:lineNumber];
+ }
+
+ if ([attribute isEqualToString: (NSString *)kAXRangeForLineParameterizedAttribute]) {
+ PlainTextRange textRange = m_object->doAXRangeForLine([number intValue]);
+ return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)];
+ }
+
+ if ([attribute isEqualToString: (NSString*)kAXStringForRangeParameterizedAttribute]) {
+ PlainTextRange plainTextRange = PlainTextRange(range.location, range.length);
+ return rangeSet ? (id)(m_object->doAXStringForRange(plainTextRange)) : nil;
+ }
+
+ if ([attribute isEqualToString: (NSString*)kAXRangeForPositionParameterizedAttribute]) {
+ if (!pointSet)
+ return nil;
+ IntPoint webCorePoint = IntPoint(point);
+ PlainTextRange textRange = m_object->doAXRangeForPosition(webCorePoint);
+ return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)];
+ }
+
+ if ([attribute isEqualToString: (NSString*)kAXRangeForIndexParameterizedAttribute]) {
+ PlainTextRange textRange = m_object->doAXRangeForIndex([number intValue]);
+ return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)];
+ }
+
+ if ([attribute isEqualToString: (NSString*)kAXBoundsForRangeParameterizedAttribute]) {
+ if (!rangeSet)
+ return nil;
+ PlainTextRange plainTextRange = PlainTextRange(range.location, range.length);
+ NSRect rect = m_object->doAXBoundsForRange(plainTextRange);
+ return [NSValue valueWithRect:rect];
+ }
+
+ if ([attribute isEqualToString: (NSString*)kAXRTFForRangeParameterizedAttribute])
+ return rangeSet ? [self doAXRTFForRange:range] : nil;
+
+ if ([attribute isEqualToString: (NSString*)kAXAttributedStringForRangeParameterizedAttribute])
+ return rangeSet ? [self doAXAttributedStringForRange:range] : nil;
+
+ if ([attribute isEqualToString: (NSString*)kAXStyleRangeForIndexParameterizedAttribute]) {
+ PlainTextRange textRange = m_object->doAXStyleRangeForIndex([number intValue]);
+ return [NSValue valueWithRange: NSMakeRange(textRange.start, textRange.length)];
+ }
+ }
+
+ // There are some parameters that super handles that are not explicitly returned by the list of the element's attributes.
+ // In that case it must be passed to super.
+ return [super accessibilityAttributeValue:attribute forParameter:parameter];
+}
+
+- (BOOL)accessibilityShouldUseUniqueId
+{
+ return m_object->accessibilityShouldUseUniqueId();
+}
+
+// API that AppKit uses for faster access
+- (NSUInteger)accessibilityIndexOfChild:(id)child
+{
+ if (![self updateObjectBackingStore])
+ return NSNotFound;
+
+ // Tree objects return their rows as their children. We can use the original method
+ // here, because we won't gain any speed up.
+ if (m_object->isTree())
+ return [super accessibilityIndexOfChild:child];
+
+ const AccessibilityObject::AccessibilityChildrenVector& children = m_object->children();
+
+ if (children.isEmpty())
+ return [[self renderWidgetChildren] indexOfObject:child];
+
+ unsigned count = children.size();
+ for (unsigned k = 0; k < count; ++k) {
+ AccessibilityObjectWrapper* wrapper = children[k]->wrapper();
+ if (wrapper == child || (children[k]->isAttachment() && [wrapper attachmentView] == child))
+ return k;
+ }
+
+ return NSNotFound;
+}
+
+- (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute
+{
+ if (![self updateObjectBackingStore])
+ return 0;
+
+ if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
+ // Tree items object returns a different set of children than those that are in children()
+ // because an AXOutline (the mac role is becomes) has some odd stipulations.
+ if (m_object->isTree() || m_object->isTreeItem())
+ return [[self accessibilityAttributeValue:NSAccessibilityChildrenAttribute] count];
+
+ const AccessibilityObject::AccessibilityChildrenVector& children = m_object->children();
+ if (children.isEmpty())
+ return [[self renderWidgetChildren] count];
+
+ return children.size();
+ }
+
+ return [super accessibilityArrayAttributeCount:attribute];
+}
+
+- (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount
+{
+ if (![self updateObjectBackingStore])
+ return nil;
+
+ if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
+ if (m_object->children().isEmpty()) {
+ NSArray *children = [self renderWidgetChildren];
+ if (!children)
+ return nil;
+
+ NSUInteger childCount = [children count];
+ if (index >= childCount)
+ return nil;
+
+ NSUInteger arrayLength = min(childCount - index, maxCount);
+ return [children subarrayWithRange:NSMakeRange(index, arrayLength)];
+ } else if (m_object->isTree()) {
+ // Tree objects return their rows as their children. We can use the original method in this case.
+ return [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount];
+ }
+
+ const AccessibilityObject::AccessibilityChildrenVector& children = m_object->children();
+ unsigned childCount = children.size();
+ if (index >= childCount)
+ return nil;
+
+ unsigned available = min(childCount - index, maxCount);
+
+ NSMutableArray *subarray = [NSMutableArray arrayWithCapacity:available];
+ for (unsigned added = 0; added < available; ++index, ++added) {
+ AccessibilityObjectWrapper* wrapper = children[index]->wrapper();
+ if (wrapper) {
+ // The attachment view should be returned, otherwise AX palindrome errors occur.
+ if (children[index]->isAttachment() && [wrapper attachmentView])
+ [subarray addObject:[wrapper attachmentView]];
+ else
+ [subarray addObject:wrapper];
+ }
+ }
+
+ return subarray;
+ }
+
+ return [super accessibilityArrayAttributeValues:attribute index:index maxCount:maxCount];
+}
+
+// This is set by DRT when it wants to listen for notifications.
+static BOOL accessibilityShouldRepostNotifications;
+- (void)accessibilitySetShouldRepostNotifications:(BOOL)repost
+{
+ accessibilityShouldRepostNotifications = repost;
+}
+
+- (void)accessibilityPostedNotification:(NSString *)notificationName
+{
+ if (accessibilityShouldRepostNotifications) {
+ NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:notificationName, @"notificationName", nil];
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"AXDRTNotification" object:nil userInfo:userInfo];
+ }
+}
+
+@end
+
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/qt/AccessibilityObjectQt.cpp b/Source/WebCore/accessibility/qt/AccessibilityObjectQt.cpp
new file mode 100644
index 0000000..7232642
--- /dev/null
+++ b/Source/WebCore/accessibility/qt/AccessibilityObjectQt.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008 Apple Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "AccessibilityObject.h"
+
+#if HAVE(ACCESSIBILITY)
+
+namespace WebCore {
+
+bool AccessibilityObject::accessibilityIgnoreAttachment() const
+{
+ return false;
+}
+
+AccessibilityObjectInclusion AccessibilityObject::accessibilityPlatformIncludesObject() const
+{
+ if (isMenuListPopup() || isMenuListOption())
+ return IgnoreObject;
+
+ return DefaultBehavior;
+}
+
+} // namespace WebCore
+
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/win/AXObjectCacheWin.cpp b/Source/WebCore/accessibility/win/AXObjectCacheWin.cpp
new file mode 100644
index 0000000..b20b51a
--- /dev/null
+++ b/Source/WebCore/accessibility/win/AXObjectCacheWin.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2008, 2009, 2010 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 "AXObjectCache.h"
+
+#include "AccessibilityObject.h"
+#include "Chrome.h"
+#include "Document.h"
+#include "Page.h"
+#include "RenderObject.h"
+
+using namespace std;
+
+namespace WebCore {
+
+void AXObjectCache::detachWrapper(AccessibilityObject* obj)
+{
+ // On Windows, AccessibilityObjects are created when get_accChildCount is
+ // called, but they are not wrapped until get_accChild is called, so this
+ // object may not have a wrapper.
+ if (AccessibilityObjectWrapper* wrapper = obj->wrapper())
+ wrapper->detach();
+}
+
+void AXObjectCache::attachWrapper(AccessibilityObject*)
+{
+ // On Windows, AccessibilityObjects are wrapped when the accessibility
+ // software requests them via get_accChild.
+}
+
+void AXObjectCache::handleScrolledToAnchor(const Node* anchorNode)
+{
+ // The anchor node may not be accessible. Post the notification for the
+ // first accessible object.
+ postPlatformNotification(AccessibilityObject::firstAccessibleObjectFromNode(anchorNode), AXScrolledToAnchor);
+}
+
+void AXObjectCache::postPlatformNotification(AccessibilityObject* obj, AXNotification notification)
+{
+ if (!obj)
+ return;
+
+ Document* document = obj->document();
+ if (!document)
+ return;
+
+ Page* page = document->page();
+ if (!page || !page->chrome()->platformPageClient())
+ return;
+
+ DWORD msaaEvent;
+ switch (notification) {
+ case AXFocusedUIElementChanged:
+ case AXActiveDescendantChanged:
+ msaaEvent = EVENT_OBJECT_FOCUS;
+ break;
+
+ case AXScrolledToAnchor:
+ msaaEvent = EVENT_SYSTEM_SCROLLINGSTART;
+ break;
+
+ case AXValueChanged:
+ case AXMenuListValueChanged:
+ msaaEvent = EVENT_OBJECT_VALUECHANGE;
+ break;
+
+ default:
+ return;
+ }
+
+ // Windows will end up calling get_accChild() on the root accessible
+ // object for the WebView, passing the child ID that we specify below. We
+ // negate the AXID so we know that the caller is passing the ID of an
+ // element, not the index of a child element.
+
+ ASSERT(obj->axObjectID() >= 1);
+ ASSERT(obj->axObjectID() <= numeric_limits<LONG>::max());
+
+ NotifyWinEvent(msaaEvent, page->chrome()->platformPageClient(), OBJID_CLIENT, -static_cast<LONG>(obj->axObjectID()));
+}
+
+void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject*, AXTextChange, unsigned, unsigned)
+{
+}
+
+AXID AXObjectCache::platformGenerateAXID() const
+{
+ static AXID lastUsedID = 0;
+
+ // Generate a new ID. Windows accessibility relies on a positive AXID,
+ // ranging from 1 to LONG_MAX.
+ AXID objID = lastUsedID;
+ do {
+ ++objID;
+ objID %= std::numeric_limits<LONG>::max();
+ } while (objID == 0 || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.contains(objID));
+
+ ASSERT(objID >= 1 && objID <= std::numeric_limits<LONG>::max());
+
+ lastUsedID = objID;
+
+ return objID;
+}
+
+void AXObjectCache::handleFocusedUIElementChanged(RenderObject*, RenderObject* newFocusedRenderer)
+{
+ if (!newFocusedRenderer)
+ return;
+
+ Page* page = newFocusedRenderer->document()->page();
+ if (!page || !page->chrome()->platformPageClient())
+ return;
+
+ AccessibilityObject* focusedObject = focusedUIElementForPage(page);
+ if (!focusedObject)
+ return;
+
+ ASSERT(!focusedObject->accessibilityIsIgnored());
+
+ postPlatformNotification(focusedObject, AXFocusedUIElementChanged);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/accessibility/win/AccessibilityObjectWin.cpp b/Source/WebCore/accessibility/win/AccessibilityObjectWin.cpp
new file mode 100644
index 0000000..44122ef
--- /dev/null
+++ b/Source/WebCore/accessibility/win/AccessibilityObjectWin.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 "AccessibilityObject.h"
+
+#if HAVE(ACCESSIBILITY)
+
+namespace WebCore {
+
+bool AccessibilityObject::accessibilityIgnoreAttachment() const
+{
+ return false;
+}
+
+AccessibilityObjectInclusion AccessibilityObject::accessibilityPlatformIncludesObject() const
+{
+ if (isMenuListPopup() || isMenuListOption())
+ return IncludeObject;
+
+ return DefaultBehavior;
+}
+
+} // namespace WebCore
+
+#endif // HAVE(ACCESSIBILITY)
diff --git a/Source/WebCore/accessibility/win/AccessibilityObjectWrapperWin.h b/Source/WebCore/accessibility/win/AccessibilityObjectWrapperWin.h
new file mode 100644
index 0000000..779443c
--- /dev/null
+++ b/Source/WebCore/accessibility/win/AccessibilityObjectWrapperWin.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008 Apple 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR
+ * 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 AccessibilityObjectWrapperWin_h
+#define AccessibilityObjectWrapperWin_h
+
+namespace WebCore {
+
+ class AccessibilityObject;
+
+ class AccessibilityObjectWrapper : public IUnknown {
+ public:
+ // IUnknown
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) = 0;
+ virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0;
+ virtual ULONG STDMETHODCALLTYPE Release(void) = 0;
+
+ virtual void detach() = 0;
+ bool attached() const { return m_object; }
+ AccessibilityObject* accessibilityObject() const { return m_object; }
+
+ protected:
+ AccessibilityObjectWrapper(AccessibilityObject* obj) : m_object(obj) { }
+ AccessibilityObjectWrapper() : m_object(0) { }
+
+ AccessibilityObject* m_object;
+ };
+
+} // namespace WebCore
+
+#endif // AccessibilityObjectWrapperWin_h
diff --git a/Source/WebCore/accessibility/wx/AccessibilityObjectWx.cpp b/Source/WebCore/accessibility/wx/AccessibilityObjectWx.cpp
new file mode 100644
index 0000000..7232642
--- /dev/null
+++ b/Source/WebCore/accessibility/wx/AccessibilityObjectWx.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008 Apple Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "AccessibilityObject.h"
+
+#if HAVE(ACCESSIBILITY)
+
+namespace WebCore {
+
+bool AccessibilityObject::accessibilityIgnoreAttachment() const
+{
+ return false;
+}
+
+AccessibilityObjectInclusion AccessibilityObject::accessibilityPlatformIncludesObject() const
+{
+ if (isMenuListPopup() || isMenuListOption())
+ return IgnoreObject;
+
+ return DefaultBehavior;
+}
+
+} // namespace WebCore
+
+#endif // HAVE(ACCESSIBILITY)