diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2009-06-15 11:06:43 +0200 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-06-15 11:31:31 +0200 |
commit | c411f16870f112c3407c28c22b617f613a82cff4 (patch) | |
tree | 29a1bcd590c8b31af2aab445bfe8a978dc5bf582 /src/3rdparty/webkit/WebCore/accessibility | |
parent | 3d77b56b32a0c53ec0bbfaa07236fedb900ff336 (diff) |
Updated WebKit from /home/shausman/src/webkit/trunk to qtwebkit-4.6-snapshot-15062009 ( 65232bf00dc494ebfd978f998c88f58d18ecce1e )
Diffstat (limited to 'src/3rdparty/webkit/WebCore/accessibility')
31 files changed, 7553 insertions, 0 deletions
diff --git a/src/3rdparty/webkit/WebCore/accessibility/AXObjectCache.cpp b/src/3rdparty/webkit/WebCore/accessibility/AXObjectCache.cpp new file mode 100644 index 0000000000..ec250edc74 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AXObjectCache.cpp @@ -0,0 +1,341 @@ +/* + * 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 "AXObjectCache.h" + +#include "AccessibilityARIAGrid.h" +#include "AccessibilityARIAGridRow.h" +#include "AccessibilityARIAGridCell.h" +#include "AccessibilityList.h" +#include "AccessibilityListBox.h" +#include "AccessibilityListBoxOption.h" +#include "AccessibilityImageMapLink.h" +#include "AccessibilityRenderObject.h" +#include "AccessibilityTable.h" +#include "AccessibilityTableCell.h" +#include "AccessibilityTableColumn.h" +#include "AccessibilityTableHeaderContainer.h" +#include "AccessibilityTableRow.h" +#include "HTMLNames.h" +#include "RenderObject.h" +#include "RenderView.h" + +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +using namespace HTMLNames; + +bool AXObjectCache::gAccessibilityEnabled = false; +bool AXObjectCache::gAccessibilityEnhancedUserInterfaceEnabled = false; + +AXObjectCache::AXObjectCache() + : m_notificationPostTimer(this, &AXObjectCache::notificationPostTimerFired) +{ +} + +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::get(RenderObject* renderer) +{ + if (!renderer) + return 0; + + AccessibilityObject* obj = 0; + AXID axID = m_renderObjectMapping.get(renderer); + ASSERT(!HashTraits<AXID>::isDeletedValue(axID)); + + if (axID) + obj = m_objects.get(axID).get(); + + return obj; +} + +bool AXObjectCache::nodeIsAriaType(Node* node, String role) +{ + if (!node || !node->isElementNode()) + return false; + + return equalIgnoringCase(static_cast<Element*>(node)->getAttribute(roleAttr), role); +} + +AccessibilityObject* AXObjectCache::getOrCreate(RenderObject* renderer) +{ + if (!renderer) + return 0; + + AccessibilityObject* obj = get(renderer); + + if (!obj) { + Node* node = renderer->node(); + RefPtr<AccessibilityObject> newObj = 0; + if (renderer->isListBox()) + newObj = AccessibilityListBox::create(renderer); + else if (node && (node->hasTagName(ulTag) || node->hasTagName(olTag) || node->hasTagName(dlTag))) + newObj = AccessibilityList::create(renderer); + + // aria tables + else if (nodeIsAriaType(node, "grid")) + newObj = AccessibilityARIAGrid::create(renderer); + else if (nodeIsAriaType(node, "row")) + newObj = AccessibilityARIAGridRow::create(renderer); + else if (nodeIsAriaType(node, "gridcell") || nodeIsAriaType(node, "columnheader") || nodeIsAriaType(node, "rowheader")) + newObj = AccessibilityARIAGridCell::create(renderer); + + // standard tables + else if (renderer->isTable()) + newObj = AccessibilityTable::create(renderer); + else if (renderer->isTableRow()) + newObj = AccessibilityTableRow::create(renderer); + else if (renderer->isTableCell()) + newObj = AccessibilityTableCell::create(renderer); + + else + newObj = AccessibilityRenderObject::create(renderer); + + obj = newObj.get(); + + getAXID(obj); + + m_renderObjectMapping.set(renderer, obj->axObjectID()); + m_objects.set(obj->axObjectID(), obj); + attachWrapper(obj); + } + + return obj; +} + +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; + 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); +} + +AXID AXObjectCache::getAXID(AccessibilityObject* obj) +{ + // check for already-assigned ID + AXID objID = obj->axObjectID(); + if (objID) { + ASSERT(m_idsInUse.contains(objID)); + return objID; + } + + // generate a new ID + static AXID lastUsedID = 0; + objID = lastUsedID; + do + ++objID; + while (objID == 0 || HashTraits<AXID>::isDeletedValue(objID) || m_idsInUse.contains(objID)); + m_idsInUse.add(objID); + lastUsedID = objID; + obj->setAXObjectID(objID); + + return objID; +} + +void AXObjectCache::removeAXID(AccessibilityObject* obj) +{ + if (!obj) + return; + + AXID objID = obj->axObjectID(); + if (objID == 0) + return; + ASSERT(!HashTraits<AXID>::isDeletedValue(objID)); + ASSERT(m_idsInUse.contains(objID)); + obj->setAXObjectID(0); + m_idsInUse.remove(objID); +} + +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; +#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, const String& message, bool postToElement) +{ + // 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> obj = get(renderer); + while (!obj && renderer) { + renderer = renderer->parent(); + obj = get(renderer); + } + + if (!renderer) + return; + + if (obj && !postToElement) + obj = obj->observableObject(); + + Document* document = renderer->document(); + if (!obj && document) + obj = get(document->renderer()); + + if (!obj) + return; + + m_notificationsToPost.append(make_pair(obj.get(), message)); + if (!m_notificationPostTimer.isActive()) + m_notificationPostTimer.startOneShot(0); +} + +void AXObjectCache::selectedChildrenChanged(RenderObject* renderer) +{ + postNotification(renderer, "AXSelectedChildrenChanged", true); +} +#endif + +#if HAVE(ACCESSIBILITY) +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)->setAriaRole(); +} +#endif + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/accessibility/AXObjectCache.h b/src/3rdparty/webkit/WebCore/accessibility/AXObjectCache.h new file mode 100644 index 0000000000..904283cf85 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AXObjectCache.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2003, 2006, 2007, 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. + */ + +#ifndef AXObjectCache_h +#define AXObjectCache_h + +#include "AccessibilityObject.h" +#include "EventHandler.h" +#include "Timer.h" +#include <limits.h> +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/RefPtr.h> + +#ifdef __OBJC__ +@class WebCoreTextMarker; +#else +class WebCoreTextMarker; +#endif + +namespace WebCore { + + class RenderObject; + class String; + class VisiblePosition; + class AccessibilityObject; + class Node; + + typedef unsigned AXID; + + struct TextMarkerData { + AXID axID; + Node* node; + int offset; + EAffinity affinity; + }; + + class AXObjectCache { + public: + AXObjectCache(); + ~AXObjectCache(); + + // to be used with render objects + AccessibilityObject* getOrCreate(RenderObject*); + + // 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(AXID); + + void detachWrapper(AccessibilityObject*); + void attachWrapper(AccessibilityObject*); + void postNotification(RenderObject*, const String&, bool postToElement); + void postPlatformNotification(AccessibilityObject*, const String&); + void childrenChanged(RenderObject*); + void selectedChildrenChanged(RenderObject*); + void handleActiveDescendantChanged(RenderObject*); + void handleAriaRoleChanged(RenderObject*); + void handleFocusedUIElementChanged(); +#if PLATFORM(GTK) + void handleFocusedUIElementChangedWithRenderers(RenderObject*, RenderObject*); +#endif + 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); } + + private: + HashMap<AXID, RefPtr<AccessibilityObject> > m_objects; + HashMap<RenderObject*, AXID> m_renderObjectMapping; + static bool gAccessibilityEnabled; + static bool gAccessibilityEnhancedUserInterfaceEnabled; + + HashSet<AXID> m_idsInUse; + + Timer<AXObjectCache> m_notificationPostTimer; + Vector<pair<AccessibilityObject*, const String> > m_notificationsToPost; + void notificationPostTimerFired(Timer<AXObjectCache>*); + + AXID getAXID(AccessibilityObject*); + bool nodeIsAriaType(Node* node, String role); + }; + +#if !HAVE(ACCESSIBILITY) + inline void AXObjectCache::handleActiveDescendantChanged(RenderObject*) { } + inline void AXObjectCache::handleAriaRoleChanged(RenderObject*) { } + inline void AXObjectCache::handleFocusedUIElementChanged() { } + inline void AXObjectCache::detachWrapper(AccessibilityObject*) { } + inline void AXObjectCache::attachWrapper(AccessibilityObject*) { } + inline void AXObjectCache::selectedChildrenChanged(RenderObject*) { } + inline void AXObjectCache::postNotification(RenderObject*, const String&, bool postToElement) { } + inline void AXObjectCache::postPlatformNotification(AccessibilityObject*, const String&) { } +#if PLATFORM(GTK) + inline void AXObjectCache::handleFocusedUIElementChangedWithRenderers(RenderObject*, RenderObject*) { } +#endif +#endif + +} + +#endif diff --git a/src/3rdparty/webkit/WebCore/accessibility/AccessibilityARIAGrid.cpp b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityARIAGrid.cpp new file mode 100644 index 0000000000..69c4512695 --- /dev/null +++ b/src/3rdparty/webkit/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 "AccessibilityTableCell.h" +#include "AccessibilityTableColumn.h" +#include "AccessibilityTableHeaderContainer.h" +#include "AccessibilityTableRow.h" +#include "AXObjectCache.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 (!isDataTable()) { + 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); + m_children.append(column); + } + + AccessibilityObject* headerContainerObject = headerContainer(); + if (headerContainerObject) + m_children.append(headerContainerObject); +} + +AccessibilityTableCell* AccessibilityARIAGrid::cellForColumnAndRow(unsigned column, unsigned row) +{ + if (!m_renderer) + return 0; + + if (!hasChildren()) + addChildren(); + + 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/src/3rdparty/webkit/WebCore/accessibility/AccessibilityARIAGrid.h b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityARIAGrid.h new file mode 100644 index 0000000000..32c8ce9e9b --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityARIAGrid.h @@ -0,0 +1,60 @@ +/* + * 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" + +namespace WebCore { + +class String; +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: + void addChild(AccessibilityObject* object, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount); +}; + +} // namespace WebCore + +#endif // AccessibilityARIAGrid_h diff --git a/src/3rdparty/webkit/WebCore/accessibility/AccessibilityARIAGridCell.cpp b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityARIAGridCell.cpp new file mode 100644 index 0000000000..1771bb87dc --- /dev/null +++ b/src/3rdparty/webkit/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->isDataTable()) + 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/src/3rdparty/webkit/WebCore/accessibility/AccessibilityARIAGridCell.h b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityARIAGridCell.h new file mode 100644 index 0000000000..2923de896d --- /dev/null +++ b/src/3rdparty/webkit/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/src/3rdparty/webkit/WebCore/accessibility/AccessibilityARIAGridRow.cpp b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityARIAGridRow.cpp new file mode 100644 index 0000000000..6e1f1c8122 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityARIAGridRow.cpp @@ -0,0 +1,75 @@ +/* + * 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 "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)); +} + +AccessibilityObject* AccessibilityARIAGridRow::parentTable() const +{ + AccessibilityObject* parent = parentObjectUnignored(); + if (!parent->isDataTable()) + 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/src/3rdparty/webkit/WebCore/accessibility/AccessibilityARIAGridRow.h b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityARIAGridRow.h new file mode 100644 index 0000000000..c2ca8b8e32 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityARIAGridRow.h @@ -0,0 +1,50 @@ +/* + * 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(); + + virtual AccessibilityObject* headerObject(); + virtual AccessibilityObject* parentTable() const; +}; + +} // namespace WebCore + +#endif // AccessibilityARIAGridRow_h diff --git a/src/3rdparty/webkit/WebCore/accessibility/AccessibilityImageMapLink.cpp b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityImageMapLink.cpp new file mode 100644 index 0000000000..943122e4b9 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityImageMapLink.cpp @@ -0,0 +1,138 @@ +/* + * 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 "AccessibilityRenderObject.h" +#include "AXObjectCache.h" +#include "Document.h" +#include "HTMLNames.h" +#include "IntRect.h" +#include "RenderObject.h" + +using namespace std; + +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 || !m_mapElement->renderer()) + return 0; + + return m_mapElement->document()->axObjectCache()->getOrCreate(m_mapElement->renderer()); +} + +Element* AccessibilityImageMapLink::actionElement() const +{ + return anchorElement(); +} + +Element* AccessibilityImageMapLink::anchorElement() const +{ + return m_areaElement; +} + +KURL AccessibilityImageMapLink::url() const +{ + if (!m_areaElement) + return KURL(); + + return m_areaElement->href(); +} + +String AccessibilityImageMapLink::accessibilityDescription() const +{ + if (!m_areaElement) + return String(); + + const AtomicString& alt = m_areaElement->getAttribute(altAttr); + if (!alt.isEmpty()) + return alt; + + return String(); +} + +String AccessibilityImageMapLink::title() const +{ + if (!m_areaElement) + return String(); + + const AtomicString& title = m_areaElement->getAttribute(titleAttr); + if (!title.isEmpty()) + return title; + const AtomicString& summary = m_areaElement->getAttribute(summaryAttr); + if (!summary.isEmpty()) + return summary; + + return String(); +} + +IntRect AccessibilityImageMapLink::elementRect() const +{ + if (!m_mapElement || !m_areaElement) + 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); +} + +IntSize AccessibilityImageMapLink::size() const +{ + return elementRect().size(); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/accessibility/AccessibilityImageMapLink.h b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityImageMapLink.h new file mode 100644 index 0000000000..2c27e4609a --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityImageMapLink.h @@ -0,0 +1,73 @@ +/* + * 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; } + void setHTMLMapElement(HTMLMapElement* element) { m_mapElement = element; } + void setParent(AccessibilityObject* parent) { m_parent = parent; } + + virtual AccessibilityRole roleValue() const { return WebCoreLinkRole; } + 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 String title() const; + virtual String accessibilityDescription() const; + + virtual IntSize size() const; + virtual IntRect elementRect() const; + +private: + HTMLAreaElement* m_areaElement; + HTMLMapElement* m_mapElement; + AccessibilityObject* m_parent; +}; + +} // namespace WebCore + +#endif // AccessibilityImageMapLink_h diff --git a/src/3rdparty/webkit/WebCore/accessibility/AccessibilityList.cpp b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityList.cpp new file mode 100644 index 0000000000..3b7c7a46f3 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityList.cpp @@ -0,0 +1,94 @@ +/* + * 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 +{ + // 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(); + return node && node->hasTagName(ulTag); +} + +bool AccessibilityList::isOrderedList() const +{ + if (!m_renderer) + return false; + + 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/src/3rdparty/webkit/WebCore/accessibility/AccessibilityList.h b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityList.h new file mode 100644 index 0000000000..89befb24fd --- /dev/null +++ b/src/3rdparty/webkit/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/src/3rdparty/webkit/WebCore/accessibility/AccessibilityListBox.cpp b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityListBox.cpp new file mode 100644 index 0000000000..e38aff5d3a --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityListBox.cpp @@ -0,0 +1,181 @@ +/* + * 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 "HitTestResult.h" +#include "HTMLNames.h" +#include "HTMLSelectElement.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 accessbility features at all. + AccessibilityObject* listOption = listBoxOptionAccessibilityObject(static_cast<HTMLElement*>(listItems[i])); + if (listOption) + 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 (static_cast<RenderListBox*>(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; +} + +AccessibilityObject* AccessibilityListBox::doAccessibilityHitTest(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(); + + const Vector<Element*>& listItems = static_cast<HTMLSelectElement*>(node)->listItems(); + unsigned length = listItems.size(); + for (unsigned i = 0; i < length; i++) { + IntRect rect = static_cast<RenderListBox*>(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 accessbility features at all. + if (rect.contains(point)) + return listBoxOptionAccessibilityObject(static_cast<HTMLElement*>(listItems[i])); + } + + return axObjectCache()->getOrCreate(m_renderer); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/accessibility/AccessibilityListBox.h b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityListBox.h new file mode 100644 index 0000000000..3f3352d9ad --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityListBox.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 AccessibilityListBox_h +#define AccessibilityListBox_h + +#include "AccessibilityObject.h" +#include "AccessibilityRenderObject.h" + +namespace WebCore { + +class AccessibilityListBox : public AccessibilityRenderObject { + +private: + AccessibilityListBox(RenderObject*); +public: + static PassRefPtr<AccessibilityListBox> create(RenderObject*); + virtual ~AccessibilityListBox(); + + virtual AccessibilityObject* doAccessibilityHitTest(const IntPoint&) const; + 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 bool accessibilityIsIgnored() const { return false; } + + virtual void selectedChildren(AccessibilityChildrenVector&); + virtual void visibleChildren(AccessibilityChildrenVector&); + + virtual void addChildren(); + +private: + AccessibilityObject* listBoxOptionAccessibilityObject(HTMLElement*) const; +}; + +} // namespace WebCore + +#endif // AccessibilityListBox_h diff --git a/src/3rdparty/webkit/WebCore/accessibility/AccessibilityListBoxOption.cpp b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityListBoxOption.cpp new file mode 100644 index 0000000000..a5cd5da61b --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityListBoxOption.cpp @@ -0,0 +1,207 @@ +/* + * 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 "HTMLOptionElement.h" +#include "HTMLOptGroupElement.h" +#include "HTMLSelectElement.h" +#include "IntRect.h" +#include "RenderObject.h" +#include "RenderListBox.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 = static_cast<RenderListBox*>(listBoxRenderer)->itemBoundingBoxRect(parentRect.x(), parentRect.y(), index); + + return rect; +} + +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(); + + 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(); +} + +IntSize AccessibilityListBoxOption::size() const +{ + return elementRect().size(); +} + +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; + + selectElement->accessKeySetSelectedIndex(listBoxOptionIndex()); +} + +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/src/3rdparty/webkit/WebCore/accessibility/AccessibilityListBoxOption.h b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityListBoxOption.h new file mode 100644 index 0000000000..1b588cd457 --- /dev/null +++ b/src/3rdparty/webkit/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" + +namespace WebCore { + +class AccessibilityListBox; +class Element; +class HTMLElement; +class HTMLSelectElement; +class String; + +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 { return false; } + virtual bool isSelected() const; + virtual bool isEnabled() const; + virtual String stringValue() const; + virtual Element* actionElement() const; + + virtual void setSelected(bool); + virtual bool canSetSelectedAttribute() const; + + virtual IntRect elementRect() const; + virtual IntSize size() const; + virtual AccessibilityObject* parentObject() const; + bool isListBoxOption() const { return true; }; + +private: + HTMLElement* m_optionElement; + + HTMLSelectElement* listBoxOptionParentNode() const; + int listBoxOptionIndex() const; + IntRect listBoxOptionRect() const; + AccessibilityObject* listBoxOptionAccessibilityObject(HTMLElement* element) const; +}; + +} // namespace WebCore + +#endif // AccessibilityListBoxOption_h diff --git a/src/3rdparty/webkit/WebCore/accessibility/AccessibilityObject.cpp b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityObject.cpp new file mode 100644 index 0000000000..dccff8220b --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityObject.cpp @@ -0,0 +1,1036 @@ +/* + * 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 "AccessibilityObject.h" + +#include "AccessibilityRenderObject.h" +#include "AXObjectCache.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 "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) +#if PLATFORM(GTK) + , m_wrapper(0) +#endif +{ +} + +AccessibilityObject::~AccessibilityObject() +{ + ASSERT(isDetached()); +} + +void AccessibilityObject::detach() +{ +#if HAVE(ACCESSIBILITY) + setWrapper(0); +#endif +} + +AccessibilityObject* AccessibilityObject::firstChild() const +{ + return 0; +} + +AccessibilityObject* AccessibilityObject::lastChild() const +{ + return 0; +} + +AccessibilityObject* AccessibilityObject::previousSibling() const +{ + return 0; +} + +AccessibilityObject* AccessibilityObject::nextSibling() const +{ + return 0; +} + +AccessibilityObject* AccessibilityObject::parentObject() const +{ + return 0; +} + +AccessibilityObject* AccessibilityObject::parentObjectUnignored() const +{ + AccessibilityObject* parent; + for (parent = parentObject(); parent && parent->accessibilityIsIgnored(); parent = parent->parentObject()) + ; + return parent; +} + +AccessibilityObject* AccessibilityObject::parentObjectIfExists() const +{ + return 0; +} + +int AccessibilityObject::layoutCount() const +{ + return 0; +} + +String AccessibilityObject::text() const +{ + return String(); +} + +String AccessibilityObject::helpText() const +{ + return String(); +} + +String AccessibilityObject::textUnderElement() const +{ + return String(); +} + +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; +} + +int AccessibilityObject::intValue() const +{ + return 0; +} + +String AccessibilityObject::stringValue() const +{ + return String(); +} + +String AccessibilityObject::ariaAccessiblityName(const String&) const +{ + return String(); +} + +String AccessibilityObject::ariaLabeledByAttribute() const +{ + return String(); +} + +String AccessibilityObject::title() const +{ + return String(); +} + +String AccessibilityObject::ariaDescribedByAttribute() const +{ + return String(); +} + +String AccessibilityObject::accessibilityDescription() const +{ + return String(); +} + +IntRect AccessibilityObject::boundingBoxRect() const +{ + return IntRect(); +} + +IntRect AccessibilityObject::elementRect() const +{ + return IntRect(); +} + +IntSize AccessibilityObject::size() const +{ + return IntSize(); +} + +IntPoint AccessibilityObject::clickPoint() const +{ + IntRect rect = elementRect(); + return IntPoint(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2); +} + +void AccessibilityObject::linkedUIElements(AccessibilityChildrenVector&) const +{ + return; +} + +AccessibilityObject* AccessibilityObject::titleUIElement() const +{ + return 0; +} + +int AccessibilityObject::textLength() const +{ + return 0; +} + +PassRefPtr<Range> AccessibilityObject::ariaSelectedTextDOMRange() const +{ + return 0; +} + +String AccessibilityObject::selectedText() const +{ + return String(); +} + +const AtomicString& AccessibilityObject::accessKey() const +{ + return nullAtom; +} + +VisibleSelection AccessibilityObject::selection() const +{ + return VisibleSelection(); +} + +PlainTextRange AccessibilityObject::selectedTextRange() const +{ + return PlainTextRange(); +} + +unsigned AccessibilityObject::selectionStart() const +{ + return selectedTextRange().start; +} + +unsigned AccessibilityObject::selectionEnd() const +{ + return selectedTextRange().length; +} + +void AccessibilityObject::setSelectedText(const String&) +{ + // TODO: set selected text (ReplaceSelectionCommand). <rdar://problem/4712125> + notImplemented(); +} + +void AccessibilityObject::setSelectedTextRange(const PlainTextRange&) +{ +} + +void AccessibilityObject::makeRangeVisible(const PlainTextRange&) +{ + // TODO: make range visible (scrollRectToVisible). <rdar://problem/4712101> + notImplemented(); +} + +KURL AccessibilityObject::url() const +{ + return KURL(); +} + +void AccessibilityObject::setFocused(bool) +{ +} + +void AccessibilityObject::setValue(const String&) +{ +} + +void AccessibilityObject::setSelected(bool) +{ +} + +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; +} + +AXObjectCache* AccessibilityObject::axObjectCache() const +{ + return 0; +} + +Widget* AccessibilityObject::widget() const +{ + return 0; +} + +Widget* AccessibilityObject::widgetForAttachmentView() const +{ + return 0; +} + +Element* AccessibilityObject::anchorElement() const +{ + return 0; +} + +Element* AccessibilityObject::actionElement() const +{ + return 0; +} + +// This function is like a cross-platform version of - (WebCoreTextMarkerRange*)textMarkerRange. It returns +// a Range that we can convert to a WebCoreRange in the Obj-C file +VisiblePositionRange AccessibilityObject::visiblePositionRange() const +{ + return VisiblePositionRange(); +} + +VisiblePositionRange AccessibilityObject::visiblePositionRangeForLine(unsigned) const +{ + return VisiblePositionRange(); +} + +VisiblePosition AccessibilityObject::visiblePositionForIndex(int) const +{ + return VisiblePosition(); +} + +int AccessibilityObject::indexForVisiblePosition(const VisiblePosition&) const +{ + return 0; +} + +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 +{ + if (range.start + range.length > text().length()) + 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; +} + +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() != 0) { + 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); +} + +IntRect AccessibilityObject::boundsForVisiblePositionRange(const VisiblePositionRange&) const +{ + return IntRect(); +} + +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() != 0) { + 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; +} + +void AccessibilityObject::setSelectedVisiblePositionRange(const VisiblePositionRange&) const +{ +} + +VisiblePosition AccessibilityObject::visiblePositionForPoint(const IntPoint&) const +{ + return VisiblePosition(); +} + +VisiblePosition AccessibilityObject::nextVisiblePosition(const VisiblePosition& visiblePos) const +{ + return visiblePos.next(); +} + +VisiblePosition AccessibilityObject::previousVisiblePosition(const VisiblePosition& visiblePos) const +{ + return visiblePos.previous(); +} + +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); +} + +// NOTE: Consider providing this utility method as AX API +VisiblePosition AccessibilityObject::visiblePositionForIndex(unsigned, bool) const +{ + return VisiblePosition(); +} + +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); +} + +// NOTE: Consider providing this utility method as AX API +int AccessibilityObject::index(const VisiblePosition&) const +{ + return -1; +} + +// Given a line number, the range of characters of the text associated with this accessibility +// object that contains the line number. +PlainTextRange AccessibilityObject::doAXRangeForLine(unsigned) const +{ + return PlainTextRange(); +} + +// 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); +} + +// 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 AccessibilityObject::doAXRangeForIndex(unsigned) const +{ + return PlainTextRange(); +} + +// 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); +} + +// A substring of the text associated with this accessibility object that is +// specified by the given character range. +String AccessibilityObject::doAXStringForRange(const PlainTextRange&) const +{ + return String(); +} + +// 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 AccessibilityObject::doAXBoundsForRange(const PlainTextRange&) const +{ + return IntRect(); +} + +// 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(); +} + +AccessibilityObject* AccessibilityObject::doAccessibilityHitTest(const IntPoint&) const +{ + return 0; +} + +AccessibilityObject* AccessibilityObject::focusedUIElement() const +{ + return 0; +} + +AccessibilityObject* AccessibilityObject::observableObject() const +{ + return 0; +} + +AccessibilityRole AccessibilityObject::roleValue() const +{ + return UnknownRole; +} + +AccessibilityRole AccessibilityObject::ariaRoleAttribute() const +{ + return UnknownRole; +} + +bool AccessibilityObject::isPresentationalChildOfAriaRole() const +{ + return false; +} + +bool AccessibilityObject::ariaRoleHasPresentationalChildren() const +{ + return false; +} + +void AccessibilityObject::clearChildren() +{ + m_haveChildren = false; + m_children.clear(); +} + +void AccessibilityObject::childrenChanged() +{ + return; +} + +void AccessibilityObject::addChildren() +{ +} + +void AccessibilityObject::selectedChildren(AccessibilityChildrenVector&) +{ +} + +void AccessibilityObject::visibleChildren(AccessibilityChildrenVector&) +{ +} + +unsigned AccessibilityObject::axObjectID() const +{ + return m_id; +} + +void AccessibilityObject::setAXObjectID(unsigned axObjectID) +{ + m_id = axObjectID; +} + +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, 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 AccessibilityObject::updateBackingStore() +{ +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/accessibility/AccessibilityObject.h b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityObject.h new file mode 100644 index 0000000000..f71be99ba6 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityObject.h @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2008 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 "VisiblePosition.h" +#include <wtf/Platform.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +#if PLATFORM(MAC) +#include <wtf/RetainPtr.h> +#elif PLATFORM(WIN) +#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 VisibleSelection; +class String; +class Widget; + +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 +}; + +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 == 0 && length == 0; } +}; + +class AccessibilityObject : public RefCounted<AccessibilityObject> { +protected: + AccessibilityObject(); +public: + virtual ~AccessibilityObject(); + + typedef Vector<RefPtr<AccessibilityObject> > AccessibilityChildrenVector; + + virtual bool isAccessibilityRenderObject() 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 isTextControl() const { return false; }; + virtual bool isNativeTextControl() const { return false; }; + virtual bool isWebArea() const { return false; }; + virtual bool isCheckboxOrRadio() const { return false; }; + virtual bool isListBox() const { return roleValue() == ListBoxRole; }; + 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 isControl() const { return false; }; + virtual bool isList() 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 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 isMultiSelect() 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 canSetFocusAttribute() const { return false; }; + virtual bool canSetTextRangeAttributes() const { return false; }; + virtual bool canSetValueAttribute() const { return false; }; + virtual bool canSetSelectedAttribute() const { return false; } + virtual bool canSetSelectedChildrenAttribute() const { return false; } + + virtual bool hasIntValue() const { return false; }; + + bool accessibilityShouldUseUniqueId() const { return true; }; + virtual bool accessibilityIsIgnored() const { return true; }; + + virtual int intValue() const; + virtual float valueForRange() const { return 0.0f; } + virtual float maxValueForRange() const { return 0.0f; } + virtual float minValueForRange() const { return 0.0f; } + virtual int layoutCount() const; + static bool isARIAControl(AccessibilityRole); + static bool isARIAInput(AccessibilityRole); + unsigned axObjectID() const; + + virtual AccessibilityObject* doAccessibilityHitTest(const IntPoint&) const; + virtual AccessibilityObject* focusedUIElement() const; + virtual AccessibilityObject* firstChild() const; + virtual AccessibilityObject* lastChild() const; + virtual AccessibilityObject* previousSibling() const; + virtual AccessibilityObject* nextSibling() const; + virtual AccessibilityObject* parentObject() const; + virtual AccessibilityObject* parentObjectUnignored() const; + virtual AccessibilityObject* parentObjectIfExists() const; + virtual AccessibilityObject* observableObject() const; + virtual void linkedUIElements(AccessibilityChildrenVector&) const; + virtual AccessibilityObject* titleUIElement() const; + virtual bool exposesTitleUIElement() const { return true; } + virtual AccessibilityRole ariaRoleAttribute() const; + virtual bool isPresentationalChildOfAriaRole() const; + virtual bool ariaRoleHasPresentationalChildren() const; + + virtual AccessibilityRole roleValue() const; + virtual AXObjectCache* axObjectCache() const; + + virtual Element* anchorElement() const; + virtual Element* actionElement() const; + virtual IntRect boundingBoxRect() const; + virtual IntRect elementRect() const; + virtual IntSize size() const; + IntPoint clickPoint() const; + + virtual KURL url() const; + virtual PlainTextRange selectedTextRange() const; + virtual VisibleSelection selection() const; + unsigned selectionStart() const; + unsigned selectionEnd() const; + virtual String stringValue() const; + virtual String ariaAccessiblityName(const String&) 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 PassRefPtr<Range> ariaSelectedTextDOMRange() const; + virtual String selectedText() const; + virtual const AtomicString& accessKey() const; + const String& actionVerb() const; + virtual Widget* widget() const; + virtual Widget* widgetForAttachmentView() const; + virtual Document* document() const { return 0; } + virtual FrameView* topDocumentFrameView() const { return 0; } + virtual FrameView* documentFrameView() const; + + void setAXObjectID(unsigned); + virtual void setFocused(bool); + virtual void setSelectedText(const String&); + virtual void setSelectedTextRange(const PlainTextRange&); + virtual void setValue(const String&); + virtual void setSelected(bool); + + virtual void detach(); + virtual void makeRangeVisible(const PlainTextRange&); + virtual bool press() const; + bool performDefaultAction() const { return press(); } + + virtual void childrenChanged(); + 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 selectedChildren(AccessibilityChildrenVector&); + virtual void visibleChildren(AccessibilityChildrenVector&); + virtual bool shouldFocusActiveDescendant() const { return false; } + virtual AccessibilityObject* activeDescendant() const { return 0; } + virtual void handleActiveDescendantChanged() { } + + virtual VisiblePositionRange visiblePositionRange() const; + virtual VisiblePositionRange visiblePositionRangeForLine(unsigned) const; + + 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; + int lengthForVisiblePositionRange(const VisiblePositionRange&) const; + virtual void setSelectedVisiblePositionRange(const VisiblePositionRange&) const; + + virtual VisiblePosition visiblePositionForPoint(const IntPoint&) const; + VisiblePosition nextVisiblePosition(const VisiblePosition&) const; + VisiblePosition previousVisiblePosition(const VisiblePosition&) const; + 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 indexValue, bool lastIndexOK) const; + + virtual VisiblePosition visiblePositionForIndex(int) const; + virtual int indexForVisiblePosition(const VisiblePosition&) const; + + AccessibilityObject* accessibilityObjectForPosition(const VisiblePosition&) const; + int lineForPosition(const VisiblePosition&) const; + PlainTextRange plainTextRangeForVisiblePositionRange(const VisiblePositionRange&) const; + virtual int index(const VisiblePosition&) const; + + virtual PlainTextRange doAXRangeForLine(unsigned) const; + PlainTextRange doAXRangeForPosition(const IntPoint&) const; + virtual PlainTextRange doAXRangeForIndex(unsigned) const; + PlainTextRange doAXStyleRangeForIndex(unsigned) const; + + virtual String doAXStringForRange(const PlainTextRange&) const; + virtual IntRect doAXBoundsForRange(const PlainTextRange&) const; + + unsigned doAXLineForIndex(unsigned); + +#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 + + // a platform-specific method for determining if an attachment is ignored +#if HAVE(ACCESSIBILITY) + bool accessibilityIgnoreAttachment() const; +#else + bool accessibilityIgnoreAttachment() const { return true; } +#endif + + // allows for an AccessibilityObject to update its render tree or perform + // other operations update type operations + virtual void updateBackingStore(); + +protected: + unsigned m_id; + AccessibilityChildrenVector m_children; + mutable bool m_haveChildren; + + virtual void clearChildren(); + virtual bool isDetached() const { return true; } + +#if PLATFORM(MAC) + RetainPtr<AccessibilityObjectWrapper> m_wrapper; +#elif PLATFORM(WIN) + 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/src/3rdparty/webkit/WebCore/accessibility/AccessibilityRenderObject.cpp b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityRenderObject.cpp new file mode 100644 index 0000000000..8c522a1cb9 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityRenderObject.cpp @@ -0,0 +1,2539 @@ +/* +* 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 "AccessibilityListBox.h" +#include "AccessibilityImageMapLink.h" +#include "CharacterNames.h" +#include "EventNames.h" +#include "FloatRect.h" +#include "FocusController.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 "Page.h" +#include "RenderFieldset.h" +#include "RenderFileUploadControl.h" +#include "RenderHTMLCanvas.h" +#include "RenderImage.h" +#include "RenderInline.h" +#include "RenderListBox.h" +#include "RenderListMarker.h" +#include "RenderMenuList.h" +#include "RenderText.h" +#include "RenderTextControl.h" +#include "RenderTheme.h" +#include "RenderView.h" +#include "RenderWidget.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) + : m_renderer(renderer) + , m_ariaRole(UnknownRole) +{ + setAriaRole(); +#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; +} + +AccessibilityObject* AccessibilityRenderObject::firstChild() const +{ + if (!m_renderer) + return 0; + + RenderObject* firstChild = m_renderer->firstChild(); + if (!firstChild) + return 0; + + return m_renderer->document()->axObjectCache()->getOrCreate(firstChild); +} + +AccessibilityObject* AccessibilityRenderObject::lastChild() const +{ + if (!m_renderer) + return 0; + + RenderObject* lastChild = m_renderer->lastChild(); + if (!lastChild) + return 0; + + return m_renderer->document()->axObjectCache()->getOrCreate(lastChild); +} + +AccessibilityObject* AccessibilityRenderObject::previousSibling() const +{ + if (!m_renderer) + return 0; + + RenderObject* previousSibling = m_renderer->previousSibling(); + if (!previousSibling) + return 0; + + return m_renderer->document()->axObjectCache()->getOrCreate(previousSibling); +} + +AccessibilityObject* AccessibilityRenderObject::nextSibling() const +{ + if (!m_renderer) + return 0; + + RenderObject* nextSibling = m_renderer->nextSibling(); + if (!nextSibling) + return 0; + + return m_renderer->document()->axObjectCache()->getOrCreate(nextSibling); +} + +AccessibilityObject* AccessibilityRenderObject::parentObjectIfExists() const +{ + if (!m_renderer) + return 0; + + RenderObject *parent = m_renderer->parent(); + if (!parent) + return 0; + + return m_renderer->document()->axObjectCache()->get(parent); +} + +AccessibilityObject* AccessibilityRenderObject::parentObject() const +{ + if (!m_renderer) + return 0; + + RenderObject *parent = m_renderer->parent(); + if (!parent) + return 0; + + if (ariaRoleAttribute() == MenuBarRole) + return m_renderer->document()->axObjectCache()->getOrCreate(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; + } + + return m_renderer->document()->axObjectCache()->getOrCreate(parent); +} + +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::isTextControl() const +{ + AccessibilityRole role = roleValue(); + return role == TextAreaRole || role == TextFieldRole; +} + +bool AccessibilityRenderObject::isNativeImage() const +{ + return m_renderer->isImage(); +} + +bool AccessibilityRenderObject::isImage() const +{ + return roleValue() == ImageRole; +} + +bool AccessibilityRenderObject::isAttachment() const +{ + if (!m_renderer) + return false; + + // Widgets are the replaced elements that we represent to AX as attachments + bool isWidget = m_renderer && m_renderer->isWidget(); + ASSERT(!isWidget || (m_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::isCheckboxOrRadio() const +{ + AccessibilityRole role = roleValue(); + return role == RadioButtonRole || role == CheckBoxRole; +} + +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->inputType() == HTMLInputElement::FILE; + } + + return false; +} + +bool AccessibilityRenderObject::isInputImage() const +{ + if (m_renderer && m_renderer->node() && m_renderer->node()->hasTagName(inputTag)) { + HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->node()); + return input->inputType() == HTMLInputElement::IMAGE; + } + + 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).string(), "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::isChecked() 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->isChecked(); +} + +bool AccessibilityRenderObject::isHovered() const +{ + ASSERT(m_renderer); + return m_renderer->node() && m_renderer->node()->hovered(); +} + +bool AccessibilityRenderObject::isMultiSelect() const +{ + ASSERT(m_renderer); + if (!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(); + } + + return !m_renderer->node() || !m_renderer->node()->isContentEditable(); +} + +bool AccessibilityRenderObject::isOffScreen() const +{ + ASSERT(m_renderer); + IntRect contentRect = m_renderer->absoluteClippedOverflowRect(); + FrameView* view = m_renderer->document()->frame()->view(); + FloatRect viewRect = view->visibleContentRect(); + viewRect.intersect(contentRect); + return viewRect.isEmpty(); +} + +int AccessibilityRenderObject::headingLevel(Node* node) +{ + // headings can be in block flow and non-block flow + if (!node) + return 0; + + if (RenderObject* renderer = node->renderer()) { + AccessibilityObject* axObjectForNode = node->document()->axObjectCache()->getOrCreate(renderer); + if (axObjectForNode->ariaRoleAttribute() == HeadingRole) { + if (!node->isElementNode()) + return 0; + Element* element = static_cast<Element*>(node); + return element->getAttribute(aria_levelAttr).toInt(); + } + } + + + if (node->hasTagName(h1Tag)) + return 1; + + if (node->hasTagName(h2Tag)) + return 2; + + if (node->hasTagName(h3Tag)) + return 3; + + if (node->hasTagName(h4Tag)) + return 4; + + if (node->hasTagName(h5Tag)) + return 5; + + if (node->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 +{ + if (!m_renderer) + return false; + + return m_renderer->isFieldset(); +} + +bool AccessibilityRenderObject::isGroup() const +{ + return roleValue() == GroupRole; +} + +const AtomicString& AccessibilityRenderObject::getAttribute(const QualifiedName& attribute) const +{ + Node* node = m_renderer->node(); + if (!node) + return nullAtom; + + if (!node->isElementNode()) + return nullAtom; + + Element* element = static_cast<Element*>(node); + return element->getAttribute(attribute); +} + +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->isRenderBlock()) { + RenderInline* continuation = toRenderBlock(currRenderer)->inlineContinuation(); + 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->isMenuList()) + 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->parent(); + + 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; +} + +static Element* siblingWithAriaRole(String role, Node* node) +{ + Node* sibling = node->parent()->firstChild(); + while (sibling) { + if (sibling->isElementNode()) { + String siblingAriaRole = static_cast<Element*>(sibling)->getAttribute(roleAttr).string(); + 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 m_renderer->document()->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 = m_renderer->document()->axObjectCache()->getOrCreate(menuItem->renderer()); + if (menuItemAX->isMenuButton()) + return menuItemAX; + } + return 0; +} + +String AccessibilityRenderObject::helpText() const +{ + if (!m_renderer) + return String(); + + 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; + } + } + + return String(); +} + +String AccessibilityRenderObject::textUnderElement() const +{ + if (!m_renderer) + return String(); + + if (isFileUploadButton()) { + RenderFileUploadControl* uploadControl = static_cast<RenderFileUploadControl*>(m_renderer); + return uploadControl->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()); + } + } + + // 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(); +} + +bool AccessibilityRenderObject::hasIntValue() const +{ + if (isHeading()) + return true; + + if (m_renderer->node() && isCheckboxOrRadio()) + return true; + + return false; +} + +int AccessibilityRenderObject::intValue() const +{ + if (!m_renderer || isPasswordField()) + return 0; + + if (isHeading()) + return headingLevel(m_renderer->node()); + + Node* node = m_renderer->node(); + if (!node || !isCheckboxOrRadio()) + return 0; + + // If this is an ARIA checkbox or radio, check the aria-checked attribute rather than node()->checked() + AccessibilityRole ariaRole = ariaRoleAttribute(); + if (ariaRole == RadioButtonRole || ariaRole == CheckBoxRole) { + if (equalIgnoringCase(getAttribute(aria_checkedAttr).string(), "true")) + return true; + return false; + } + + return static_cast<HTMLInputElement*>(node)->checked(); +} + +float AccessibilityRenderObject::valueForRange() const +{ + if (!isProgressIndicator() && !isSlider()) + 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(); + + if (m_renderer->isText()) + return textUnderElement(); + + if (m_renderer->isMenuList()) + return static_cast<RenderMenuList*>(m_renderer)->text(); + + if (m_renderer->isListMarker()) + return static_cast<RenderListMarker*>(m_renderer)->text(); + + if (isWebArea()) { + if (m_renderer->document()->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()); + } + + if (isTextControl()) + return text(); + + if (isFileUploadButton()) { + RenderFileUploadControl* uploadControl = static_cast<RenderFileUploadControl*>(m_renderer); + return uploadControl->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::ariaAccessiblityName(const String& s) const +{ + Document* document = m_renderer->document(); + if (!document) + return String(); + + String idList = s; + idList.replace('\n', ' '); + Vector<String> idVector; + idList.split(' ', idVector); + + Vector<UChar> ariaLabel; + unsigned size = idVector.size(); + for (unsigned i = 0; i < size; ++i) { + String idName = idVector[i]; + Element* idElement = document->getElementById(idName); + if (idElement) { + 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()); + } + ariaLabel.append(' '); + } + } + return String::adopt(ariaLabel); +} + +String AccessibilityRenderObject::ariaLabeledByAttribute() const +{ + Node* node = m_renderer->node(); + if (!node) + return String(); + + if (!node->isElementNode()) + return String(); + + // The ARIA spec uses the British spelling: "labelled." It seems prudent to support the American + // spelling ("labeled") as well. + String idList = getAttribute(aria_labeledbyAttr).string(); + if (idList.isEmpty()) { + idList = getAttribute(aria_labelledbyAttr).string(); + if (idList.isEmpty()) + return String(); + } + + return ariaAccessiblityName(idList); +} + +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->correspondingControl() == element) + return label; + } + } + + return 0; +} + +HTMLLabelElement* AccessibilityRenderObject::labelElementContainer() const +{ + if (!m_renderer) + return false; + + // the control element should not be considered part of the label + if (isControl()) + return false; + + // 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(); + + const AtomicString& placeholder = getAttribute(placeholderAttr); + if (!placeholder.isEmpty()) + return placeholder; + } + + if (roleValue() == ButtonRole + || ariaRole == ListBoxOptionRole + || ariaRole == MenuItemRole + || ariaRole == MenuButtonRole + || isHeading()) + return textUnderElement(); + + if (isLink()) + return textUnderElement(); + + return String(); +} + +String AccessibilityRenderObject::ariaDescribedByAttribute() const +{ + String idList = getAttribute(aria_describedbyAttr).string(); + if (idList.isEmpty()) + return String(); + + return ariaAccessiblityName(idList); +} + +String AccessibilityRenderObject::accessibilityDescription() const +{ + if (!m_renderer) + return String(); + + String ariaDescription = ariaDescribedByAttribute(); + 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(); + 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)->name(); + } + if (owner->isHTMLElement()) + return static_cast<HTMLElement*>(owner)->getAttribute(nameAttr); + } + owner = document->body(); + if (owner && owner->isHTMLElement()) + return static_cast<HTMLElement*>(owner)->getAttribute(nameAttr); + } + + if (roleValue() == DefinitionListTermRole) + return AXDefinitionListTermText(); + if (roleValue() == DefinitionListDefinitionRole) + return AXDefinitionListDefinitionText(); + + 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(); + + Vector<FloatQuad> quads; + obj->absoluteQuads(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()) + theme()->adjustRepaintRect(obj, r); + result.unite(r); + } + } + 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(); +} + +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 ref = linkURL.ref(); + if (ref.isEmpty()) + return 0; + + // check if URL is the same as current URL + linkURL.removeRef(); + if (m_renderer->document()->url() != linkURL) + return 0; + + Node* linkedNode = m_renderer->document()->findAnchor(ref); + if (!linkedNode) + return 0; + + // the element we find may not be accessible, keep searching until we find a good one + AccessibilityObject* linkedAXElement = m_renderer->document()->axObjectCache()->getOrCreate(linkedNode->renderer()); + while (linkedAXElement && linkedAXElement->accessibilityIsIgnored()) { + linkedNode = linkedNode->traverseNextNode(); + + while (linkedNode && !linkedNode->renderer()) + linkedNode = linkedNode->traverseNextSibling(); + + if (!linkedNode) + return 0; + linkedAXElement = m_renderer->document()->axObjectCache()->getOrCreate(linkedNode->renderer()); + } + + return linkedAXElement; +} + +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 = m_renderer->document()->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 = m_renderer->document()->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 +{ + if (isAnchor()) { + AccessibilityObject* linkedAXElement = internalLinkElement(); + if (linkedAXElement) + linkedUIElements.append(linkedAXElement); + } + + if (roleValue() == RadioButtonRole) + addRadioButtonGroupMembers(linkedUIElements); +} + +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; + + 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(static_cast<RenderFieldset*>(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::accessibilityIsIgnored() const +{ + // ignore invisible element + if (!m_renderer || m_renderer->style()->visibility() != VISIBLE) + return true; + + if (isPresentationalChildOfAriaRole()) + return true; + + // ignore popup menu items because AppKit does + for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) { + if (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 + HTMLLabelElement* labelElement = labelElementContainer(); + if (labelElement) { + HTMLElement* correspondingControl = labelElement->correspondingControl(); + if (correspondingControl && correspondingControl->renderer()) { + AccessibilityObject* controlObject = axObjectCache()->getOrCreate(correspondingControl->renderer()); + if (!controlObject->exposesTitleUIElement()) + return true; + } + } + + AccessibilityRole ariaRole = ariaRoleAttribute(); + if (ariaRole == TextAreaRole || ariaRole == StaticTextRole) { + String ariaText = text(); + return ariaText.isNull() || ariaText.isEmpty(); + } + + // 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; + + // 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; + + // don't ignore labels, because they serve as TitleUIElements + Node* node = m_renderer->node(); + if (node && node->hasTagName(labelTag)) + 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 = static_cast<RenderHTMLCanvas*>(m_renderer); + if (canvas->height() <= 1 || canvas->width() <= 1) + return true; + return false; + } + + // 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 (isNativeImage()) { + if (image->cachedImage()) { + IntSize imageSize = image->cachedImage()->imageSize(image->view()->zoomFactor()); + return imageSize.height() <= 1 || imageSize.width() <= 1; + } + } + return false; + } + + if (ariaRole != UnknownRole) + return false; + + // make a platform-specific decision + if (isAttachment()) + return accessibilityIgnoreAttachment(); + + return !m_renderer->isListMarker() && !isWebArea(); +} + +bool AccessibilityRenderObject::isLoaded() const +{ + return !m_renderer->document()->tokenizer(); +} + +int AccessibilityRenderObject::layoutCount() const +{ + if (!m_renderer->isRenderView()) + return 0; + return toRenderView(m_renderer)->frameView()->layoutCount(); +} + +String AccessibilityRenderObject::text() const +{ + 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(); +} + +PassRefPtr<Range> AccessibilityRenderObject::ariaSelectedTextDOMRange() const +{ + Node* node = m_renderer->node(); + if (!node) + return 0; + + RefPtr<Range> currentSelectionRange = selection().toNormalizedRange(); + if (!currentSelectionRange) + return 0; + + ExceptionCode ec = 0; + if (!currentSelectionRange->intersectsNode(node, ec)) + return Range::create(currentSelectionRange->ownerDocument()); + + RefPtr<Range> ariaRange = rangeOfContents(node); + Position startPosition, endPosition; + + // Find intersection of currentSelectionRange and ariaRange + if (ariaRange->startOffset() > currentSelectionRange->startOffset()) + startPosition = ariaRange->startPosition(); + else + startPosition = currentSelectionRange->startPosition(); + + if (ariaRange->endOffset() < currentSelectionRange->endOffset()) + endPosition = ariaRange->endPosition(); + else + endPosition = currentSelectionRange->endPosition(); + + return Range::create(ariaRange->ownerDocument(), startPosition, endPosition); +} + +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(); + + RefPtr<Range> ariaRange = ariaSelectedTextDOMRange(); + if (!ariaRange) + return String(); + return ariaRange->text(); +} + +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->document()->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(); + + RefPtr<Range> ariaRange = ariaSelectedTextDOMRange(); + if (!ariaRange) + return PlainTextRange(); + return PlainTextRange(ariaRange->startOffset(), ariaRange->endOffset()); +} + +void AccessibilityRenderObject::setSelectedTextRange(const PlainTextRange& range) +{ + if (isNativeTextControl()) { + RenderTextControl* textControl = toRenderTextControl(m_renderer); + textControl->setSelectionRange(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 +{ + return m_renderer->style()->pseudoState() == PseudoVisited; +} + +bool AccessibilityRenderObject::isSelected() const +{ + if (!m_renderer) + return false; + + Node* node = m_renderer->node(); + if (!node) + return false; + + 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::setValue(const String& string) +{ + // FIXME: Do we want to do anything here for ARIA textboxes? + if (m_renderer->isTextField()) { + HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->node()); + input->setValue(string); + } else if (m_renderer->isTextArea()) { + HTMLTextAreaElement* textArea = static_cast<HTMLTextAreaElement*>(m_renderer->node()); + textArea->setValue(string); + } +} + +bool AccessibilityRenderObject::isEnabled() const +{ + ASSERT(m_renderer); + 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 +{ + return m_renderer->document(); +} + +FrameView* AccessibilityRenderObject::topDocumentFrameView() const +{ + return topRenderer()->view()->frameView(); +} + +Widget* AccessibilityRenderObject::widget() const +{ + if (!m_renderer->isWidget()) + return 0; + + return static_cast<RenderWidget*>(m_renderer)->widget(); +} + +AXObjectCache* AccessibilityRenderObject::axObjectCache() const +{ + return m_renderer->document()->axObjectCache(); +} + +AccessibilityObject* AccessibilityRenderObject::accessibilityParentForImageMap(HTMLMapElement* map) const +{ + // find an image that is using this map + if (!m_renderer || !map) + return 0; + + String mapName = map->getName().string().lower(); + RefPtr<HTMLCollection> coll = m_renderer->document()->images(); + for (Node* curr = coll->firstItem(); curr; curr = coll->nextItem()) { + RenderObject* obj = curr->renderer(); + if (!obj || !curr->hasTagName(imgTag)) + continue; + + // The HTMLImageElement's useMap() value includes the '#' symbol at the beginning, + // which has to be stripped off + String useMapName = static_cast<HTMLImageElement*>(curr)->useMap().substring(1).lower(); + if (useMapName == mapName) + return axObjectCache()->getOrCreate(obj); + } + + return 0; +} + +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->parent(); + 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 static_cast<RenderWidget*>(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 == 0 || !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 != 0) { + 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::EXTEND, SelectionController::RIGHT, 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 (!isTextControl() && !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->document()->frame()->selection()->moveTo(range.start, true); + } + else { + VisibleSelection newSelection = VisibleSelection(range.start, range.end); + m_renderer->document()->frame()->selection()->setSelection(newSelection); + } +} + +VisiblePosition AccessibilityRenderObject::visiblePositionForPoint(const IntPoint& point) const +{ + // 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 || !innerNode->renderer()) + return VisiblePosition(); + + pointResult = result.localPoint(); + + // done if hit something other than a widget + RenderObject* renderer = innerNode->renderer(); + if (!renderer->isWidget()) + break; + + // descend into widget (FRAME, IFRAME, OBJECT...) + Widget* widget = static_cast<RenderWidget*>(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 != 0; lineCount -= 1) { + savedVisiblePos = visiblePos; + visiblePos = nextLinePosition(visiblePos, 0); + if (visiblePos.isNull() || visiblePos == savedVisiblePos) + return PlainTextRange(); + } + + // make a caret selection for the marker position, then extend it to the line + // NOTE: ignores results of selection.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::EXTEND, SelectionController::LEFT, LineBoundary); + selection.modify(SelectionController::EXTEND, SelectionController::RIGHT, LineBoundary); + + // calculate the indices for the selection start and end + VisiblePosition startPosition = selection.selection().visibleStart(); + VisiblePosition endPosition = selection.selection().visibleEnd(); + int index1 = indexForVisiblePosition(startPosition); + 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 == 0) + return ""; + + 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 (isTextControl()) + 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->parent()); + 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::doAccessibilityHitTest(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); + + RenderObject* obj = node->renderer(); + if (!obj) + return 0; + + AccessibilityObject *result = obj->document()->axObjectCache()->getOrCreate(obj); + + if (obj->isListBox()) + return static_cast<AccessibilityListBox*>(result)->doAccessibilityHitTest(point); + + if (result->accessibilityIsIgnored()) + result = result->parentObjectUnignored(); + + return result; +} + +AccessibilityObject* AccessibilityRenderObject::focusedUIElement() const +{ + // get the focused node in the page + Page* page = m_renderer->document()->page(); + if (!page) + return 0; + + Document* focusedDocument = page->focusController()->focusedOrMainFrame()->document(); + Node* focusedNode = focusedDocument->focusedNode(); + if (!focusedNode) + focusedNode = focusedDocument; + + 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; +} + +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: + /* FIXME: replace these with actual roles when they are added to AccessibilityRole + composite + alert + alertdialog + grid + status + timer + tree + */ + return true; + default: + return false; + } +} + +AccessibilityObject* AccessibilityRenderObject::activeDescendant() const +{ + if (renderer()->node() && !renderer()->node()->isElementNode()) + return 0; + Element* element = static_cast<Element*>(renderer()->node()); + + String activeDescendantAttrStr = element->getAttribute(aria_activedescendantAttr).string(); + if (activeDescendantAttrStr.isNull() || activeDescendantAttrStr.isEmpty()) + return 0; + + Element* target = renderer()->document()->getElementById(activeDescendantAttrStr); + if (!target) + return 0; + + AccessibilityObject* obj = renderer()->document()->axObjectCache()->getOrCreate(target->renderer()); + if (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::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(activedescendant->renderer(), "AXFocusedUIElementChanged", true); +} + + +AccessibilityObject* AccessibilityRenderObject::observableObject() const +{ + for (RenderObject* renderer = m_renderer; renderer && renderer->node(); renderer = renderer->parent()) { + if (renderer->isTextControl()) + return renderer->document()->axObjectCache()->getOrCreate(renderer); + } + + return 0; +} + +typedef HashMap<String, AccessibilityRole, CaseFoldingHash> ARIARoleMap; + +static const ARIARoleMap& createARIARoleMap() +{ + struct RoleEntry { + String ariaRole; + AccessibilityRole webcoreRole; + }; + + const RoleEntry roles[] = { + { "button", ButtonRole }, + { "checkbox", CheckBoxRole }, + { "grid", TableRole }, + { "gridcell", CellRole }, + { "columnheader", ColumnHeaderRole }, + { "rowheader", RowHeaderRole }, + { "group", GroupRole }, + { "heading", HeadingRole }, + { "img", ImageRole }, + { "link", WebCoreLinkRole }, + { "listbox", ListBoxRole }, + // "option" isn't here because it may map to different roles depending on the parent element's role + { "menu", MenuRole }, + { "menubar", GroupRole }, + // "menuitem" isn't here because it may map to different roles depending on the parent element's role + { "menuitemcheckbox", MenuItemRole }, + { "menuitemradio", MenuItemRole }, + { "progressbar", ProgressIndicatorRole }, + { "radio", RadioButtonRole }, + { "row", RowRole }, + { "range", SliderRole }, + { "slider", SliderRole }, + { "spinbutton", ProgressIndicatorRole }, + { "textbox", TextAreaRole } + }; + ARIARoleMap& roleMap = *new ARIARoleMap; + + const unsigned numRoles = sizeof(roles) / sizeof(roles[0]); + for (unsigned i = 0; i < numRoles; ++i) + roleMap.set(roles[i].ariaRole, roles[i].webcoreRole); + return roleMap; +} + +static AccessibilityRole ariaRoleToWebCoreRole(String value) +{ + ASSERT(!value.isEmpty() && !value.isNull()); + static const ARIARoleMap& roleMap = createARIARoleMap(); + return roleMap.get(value); +} + +AccessibilityRole AccessibilityRenderObject::determineAriaRoleAttribute() const +{ + String ariaRole = getAttribute(roleAttr).string(); + if (ariaRole.isNull() || ariaRole.isEmpty()) + return UnknownRole; + + AccessibilityRole role = ariaRoleToWebCoreRole(ariaRole); + 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; +} + +void AccessibilityRenderObject::setAriaRole() +{ + m_ariaRole = determineAriaRoleAttribute(); +} + +AccessibilityRole AccessibilityRenderObject::ariaRoleAttribute() const +{ + return m_ariaRole; +} + +AccessibilityRole AccessibilityRenderObject::roleValue() const +{ + if (!m_renderer) + return UnknownRole; + + Node* node = m_renderer->node(); + AccessibilityRole ariaRole = ariaRoleAttribute(); + if (ariaRole != UnknownRole) + return ariaRole; + + if (node && node->isLink()) { + if (m_renderer->isImage()) + return ImageMapRole; + return WebCoreLinkRole; + } + if (m_renderer->isListMarker()) + return ListMarkerRole; + if (node && node->hasTagName(buttonTag)) + return ButtonRole; + if (m_renderer->isText()) + return StaticTextRole; + if (m_renderer->isImage()) { + if (node && node->hasTagName(inputTag)) + return ButtonRole; + return ImageRole; + } + if (node && node->hasTagName(canvasTag)) + return ImageRole; + + if (m_renderer->isRenderView()) + return WebAreaRole; + + if (m_renderer->isTextField()) + return TextFieldRole; + + if (m_renderer->isTextArea()) + return TextAreaRole; + + if (node && node->hasTagName(inputTag)) { + HTMLInputElement* input = static_cast<HTMLInputElement*>(node); + if (input->inputType() == HTMLInputElement::CHECKBOX) + return CheckBoxRole; + if (input->inputType() == HTMLInputElement::RADIO) + return RadioButtonRole; + if (input->isTextButton()) + return ButtonRole; + } + + if (node && node->hasTagName(buttonTag)) + return ButtonRole; + + if (isFileUploadButton()) + return ButtonRole; + + if (m_renderer->isMenuList()) + return PopUpButtonRole; + + if (headingLevel(m_renderer->node()) != 0) + return HeadingRole; + + if (node && node->hasTagName(ddTag)) + return DefinitionListDefinitionRole; + + if (node && node->hasTagName(dtTag)) + return DefinitionListTermRole; + + if (m_renderer->isBlockFlow() || (node && node->hasTagName(labelTag))) + return GroupRole; + + return UnknownRole; +} + +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, it 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: + return true; + default: + return false; + } +} + +bool AccessibilityRenderObject::canSetValueAttribute() const +{ + if (isWebArea()) + return !isReadOnly(); + + return isTextControl() || isProgressIndicator() || isSlider(); +} + +bool AccessibilityRenderObject::canSetTextRangeAttributes() const +{ + return isTextControl(); +} + +void AccessibilityRenderObject::childrenChanged() +{ + // this method is meant as a quick way of marking dirty + // a portion of the accessibility tree + + markChildrenDirty(); + + if (!m_renderer) + return; + + // Go up the render parent chain, marking children as dirty. + // We can't rely on the accessibilityParent() because it may not exist and we must not create an AX object here either + for (RenderObject* renderParent = m_renderer->parent(); renderParent; renderParent = renderParent->parent()) { + AccessibilityObject* parent = m_renderer->document()->axObjectCache()->get(renderParent); + if (parent && parent->isAccessibilityRenderObject()) + static_cast<AccessibilityRenderObject *>(parent)->markChildrenDirty(); + } +} + +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: + return false; + default: + return true; + } +} + +const AccessibilityObject::AccessibilityChildrenVector& AccessibilityRenderObject::children() +{ + if (m_childrenDirty) { + clearChildren(); + m_childrenDirty = false; + } + + if (!m_haveChildren) + addChildren(); + 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()) { + if (!obj->hasChildren()) + obj->addChildren(); + AccessibilityChildrenVector children = obj->children(); + unsigned length = children.size(); + for (unsigned i = 0; i < length; ++i) + m_children.append(children[i]); + } else + m_children.append(obj); + } + + // for a RenderImage, add the <area> elements as individual accessibility objects + if (m_renderer->isRenderImage()) { + HTMLMapElement* map = toRenderImage(m_renderer)->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->isLink()) { + AccessibilityImageMapLink* areaObject = static_cast<AccessibilityImageMapLink*>(m_renderer->document()->axObjectCache()->getOrCreate(ImageMapLinkRole)); + areaObject->setHTMLAreaElement(static_cast<HTMLAreaElement*>(current)); + areaObject->setHTMLMapElement(map); + areaObject->setParent(this); + + m_children.append(areaObject); + } + } + } + } +} + +void AccessibilityRenderObject::ariaListboxSelectedChildren(AccessibilityChildrenVector& result) +{ + AccessibilityObject* child = firstChild(); + bool isMultiselectable = false; + + Element* element = static_cast<Element*>(renderer()->node()); + if (!element || !element->isElementNode()) // do this check to ensure safety of static_cast above + return; + + String multiselectablePropertyStr = element->getAttribute("aria-multiselectable").string(); + isMultiselectable = equalIgnoringCase(multiselectablePropertyStr, "true"); + + while (child) { + // every child should have aria-role option, and if so, check for selected attribute/state + AccessibilityRole ariaRole = child->ariaRoleAttribute(); + RenderObject* childRenderer = 0; + if (child->isAccessibilityRenderObject()) + childRenderer = static_cast<AccessibilityRenderObject*>(child)->renderer(); + if (childRenderer && ariaRole == ListBoxOptionRole) { + Element* childElement = static_cast<Element*>(childRenderer->node()); + if (childElement && childElement->isElementNode()) { // do this check to ensure safety of static_cast above + String selectedAttrString = childElement->getAttribute("aria-selected").string(); + if (equalIgnoringCase(selectedAttrString, "true")) { + result.append(child); + if (isMultiselectable) + return; + } + } + } + child = child->nextSibling(); + } +} + +void AccessibilityRenderObject::selectedChildren(AccessibilityChildrenVector& result) +{ + ASSERT(result.isEmpty()); + + // only listboxes should be asked for their selected children. + if (ariaRoleAttribute() != ListBoxRole) { // native list boxes would be AccessibilityListBoxes, so only check for aria list boxes + ASSERT_NOT_REACHED(); + return; + } + return ariaListboxSelectedChildren(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); +} + +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::updateBackingStore() +{ + if (!m_renderer) + return; + m_renderer->view()->layoutIfNeeded(); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/accessibility/AccessibilityRenderObject.h b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityRenderObject.h new file mode 100644 index 0000000000..3fa88a2c7b --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityRenderObject.h @@ -0,0 +1,245 @@ +/* + * 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" + +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 RenderObject; +class RenderListBox; +class RenderTextControl; +class RenderView; +class VisibleSelection; +class String; +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 isTextControl() const; + virtual bool isNativeTextControl() const; + virtual bool isWebArea() const; + virtual bool isCheckboxOrRadio() 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 isMultiSelect() const; + virtual bool isOffScreen() const; + virtual bool isPressed() const; + virtual bool isReadOnly() const; + virtual bool isVisited() const; + + const AtomicString& getAttribute(const QualifiedName&) const; + virtual bool canSetFocusAttribute() const; + virtual bool canSetTextRangeAttributes() const; + virtual bool canSetValueAttribute() const; + + virtual bool hasIntValue() const; + + virtual bool accessibilityIsIgnored() const; + + static int headingLevel(Node*); + virtual int intValue() const; + virtual float valueForRange() const; + virtual float maxValueForRange() const; + virtual float minValueForRange() const; + virtual int layoutCount() const; + + virtual AccessibilityObject* doAccessibilityHitTest(const IntPoint&) const; + virtual AccessibilityObject* focusedUIElement() 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 AccessibilityRole ariaRoleAttribute() const; + virtual bool isPresentationalChildOfAriaRole() const; + virtual bool ariaRoleHasPresentationalChildren() const; + void setAriaRole(); + virtual AccessibilityRole roleValue() const; + virtual AXObjectCache* axObjectCache() 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; + + void setRenderer(RenderObject* renderer) { m_renderer = renderer; } + RenderObject* renderer() const { return m_renderer; } + 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 ariaAccessiblityName(const String&) 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 PassRefPtr<Range> ariaSelectedTextDOMRange() 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 const AccessibilityChildrenVector& children(); + + virtual void setFocused(bool); + virtual void setSelectedTextRange(const PlainTextRange&); + virtual void setValue(const String&); + + virtual void detach(); + virtual void childrenChanged(); + virtual void addChildren(); + virtual bool canHaveChildren() const; + virtual void selectedChildren(AccessibilityChildrenVector&); + virtual void visibleChildren(AccessibilityChildrenVector&); + virtual bool shouldFocusActiveDescendant() const; + virtual AccessibilityObject* activeDescendant() const; + virtual void handleActiveDescendantChanged(); + + virtual VisiblePositionRange visiblePositionRange() const; + virtual VisiblePositionRange visiblePositionRangeForLine(unsigned) const; + virtual IntRect boundsForVisiblePositionRange(const VisiblePositionRange&) const; + virtual void setSelectedVisiblePositionRange(const VisiblePositionRange&) const; + + 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(); + +protected: + RenderObject* m_renderer; + AccessibilityRole m_ariaRole; + mutable bool m_childrenDirty; + + void setRenderObject(RenderObject* renderer) { m_renderer = renderer; } + + virtual bool isDetached() const { return !m_renderer; } + +private: + void ariaListboxSelectedChildren(AccessibilityChildrenVector&); + void ariaListboxVisibleChildren(AccessibilityChildrenVector&); + + Element* menuElementForMenuButton() const; + Element* menuItemElementForMenu() const; + AccessibilityRole determineAriaRoleAttribute() const; + + IntRect checkboxOrRadioRect() const; + void addRadioButtonGroupMembers(AccessibilityChildrenVector& linkedUIElements) const; + AccessibilityObject* internalLinkElement() const; + AccessibilityObject* accessibilityImageMapHitTest(HTMLAreaElement*, const IntPoint&) const; + AccessibilityObject* accessibilityParentForImageMap(HTMLMapElement* map) const; + + void markChildrenDirty() const { m_childrenDirty = true; } +}; + +} // namespace WebCore + +#endif // AccessibilityRenderObject_h diff --git a/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTable.cpp b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTable.cpp new file mode 100644 index 0000000000..6d7dbe2ba9 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTable.cpp @@ -0,0 +1,489 @@ +/* + * 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 "AccessibilityTableCell.h" +#include "AccessibilityTableColumn.h" +#include "AccessibilityTableHeaderContainer.h" +#include "AccessibilityTableRow.h" +#include "AXObjectCache.h" +#include "HTMLNames.h" +#include "HTMLTableElement.h" +#include "HTMLTableCaptionElement.h" +#include "HTMLTableCellElement.h" +#include "RenderObject.h" +#include "RenderTable.h" +#include "RenderTableCell.h" +#include "RenderTableSection.h" + +using namespace std; + +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::isTableExposableThroughAccessibility() +{ + // 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 || !m_renderer->isTable()) + 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 + AccessibilityRole ariaRole = ariaRoleAttribute(); + if (ariaRole != UnknownRole) + return false; + + RenderTable* table = static_cast<RenderTable*>(m_renderer); + + // 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 + + 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->backgroundColor(); + + // 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; + + for (int row = 0; row < numRows; ++row) { + for (int col = 0; col < numCols; ++col) { + RenderTableCell* cell = firstBody->cellAt(row, col).cell; + 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); + + // 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->backgroundColor(); + 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; + } + } + + // 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; + + return false; +} + +void AccessibilityTable::clearChildren() +{ + m_children.clear(); + m_rows.clear(); + m_columns.clear(); + m_haveChildren = false; +} + +void AccessibilityTable::addChildren() +{ + if (!isDataTable()) { + AccessibilityRenderObject::addChildren(); + return; + } + + ASSERT(!m_haveChildren); + + m_haveChildren = true; + if (!m_renderer || !m_renderer->isTable()) + return; + + RenderTable* table = static_cast<RenderTable*>(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->cellAt(rowIndex, colIndex).cell; + 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); + m_children.append(row); + 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); + m_children.append(column); + } + + AccessibilityObject* headerContainerObject = headerContainer(); + if (headerContainerObject) + 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() +{ + if (!hasChildren()) + addChildren(); + + return m_columns; +} + +AccessibilityObject::AccessibilityChildrenVector& AccessibilityTable::rows() +{ + if (!hasChildren()) + addChildren(); + + return m_rows; +} + +void AccessibilityTable::rowHeaders(AccessibilityChildrenVector& headers) +{ + if (!m_renderer) + return; + + if (!hasChildren()) + addChildren(); + + 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; + + if (!hasChildren()) + addChildren(); + + 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; + + if (!hasChildren()) + addChildren(); + + int numRows = m_rows.size(); + for (int row = 0; row < numRows; ++row) { + AccessibilityChildrenVector rowChildren = m_rows[row]->children(); + cells.append(rowChildren); + } +} + +unsigned AccessibilityTable::columnCount() +{ + if (!hasChildren()) + addChildren(); + + return m_columns.size(); +} + +unsigned AccessibilityTable::rowCount() +{ + if (!hasChildren()) + addChildren(); + + return m_rows.size(); +} + +AccessibilityTableCell* AccessibilityTable::cellForColumnAndRow(unsigned column, unsigned row) +{ + if (!m_renderer || !m_renderer->isTable()) + return 0; + + if (!hasChildren()) + addChildren(); + + RenderTable* table = static_cast<RenderTable*>(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->cellAt(sectionSpecificRow, column).cell; + + // 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->cellAt(testRow, column).cell; + // 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->cellAt(sectionSpecificRow, testCol).cell; + // 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 (!isDataTable()) + return AccessibilityRenderObject::roleValue(); + + return TableRole; +} + +bool AccessibilityTable::accessibilityIsIgnored() const +{ + if (!isDataTable()) + return AccessibilityRenderObject::accessibilityIsIgnored(); + + return false; +} + +String AccessibilityTable::title() const +{ + if (!isDataTable()) + 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; +} + +bool AccessibilityTable::isDataTable() const +{ + if (!m_renderer) + return false; + + return m_isAccessibilityTable; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTable.h b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTable.h new file mode 100644 index 0000000000..b6aa3cac9e --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTable.h @@ -0,0 +1,93 @@ +/* + * 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" + +#if PLATFORM(MAC) && (defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD)) +#define ACCESSIBILITY_TABLES 0 +#else +#define ACCESSIBILITY_TABLES 1 +#endif + +namespace WebCore { + +class String; +class AccessibilityTableCell; +class AccessibilityTableHeaderContainer; + +class AccessibilityTable : public AccessibilityRenderObject { + +protected: + AccessibilityTable(RenderObject*); +public: + static PassRefPtr<AccessibilityTable> create(RenderObject*); + virtual ~AccessibilityTable(); + + 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(); + + 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 isTableExposableThroughAccessibility(); +}; + +} // namespace WebCore + +#endif // AccessibilityTable_h diff --git a/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableCell.cpp b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableCell.cpp new file mode 100644 index 0000000000..4d7cf5a769 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableCell.cpp @@ -0,0 +1,162 @@ +/* + * 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 +{ + if (!isTableCell()) + return AccessibilityRenderObject::accessibilityIsIgnored(); + + return false; +} + +AccessibilityObject* AccessibilityTableCell::parentTable() const +{ + if (!m_renderer || !m_renderer->isTableCell()) + return false; + + return axObjectCache()->getOrCreate(static_cast<RenderTableCell*>(m_renderer)->table()); +} + +bool AccessibilityTableCell::isTableCell() const +{ + AccessibilityObject* table = parentTable(); + if (!table || !table->isDataTable()) + 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 = static_cast<RenderTableCell*>(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 = static_cast<RenderTableCell*>(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; + + RenderTableCell* renderCell = static_cast<RenderTableCell*>(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->cellAt(row, 0).cell; + 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/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableCell.h b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableCell.h new file mode 100644 index 0000000000..dabbce2049 --- /dev/null +++ b/src/3rdparty/webkit/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/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableColumn.cpp b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableColumn.cpp new file mode 100644 index 0000000000..ee79444581 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableColumn.cpp @@ -0,0 +1,185 @@ +/* + * 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 "AccessibilityTableCell.h" +#include "AXObjectCache.h" +#include "HTMLNames.h" +#include "RenderTable.h" +#include "RenderTableSection.h" +#include "RenderTableCell.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 = static_cast<RenderTable*>(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; + + RenderTableCell* cell = 0; + // also account for cells that have a span + for (int testCol = m_columnIndex; testCol >= 0; --testCol) { + RenderTableCell* testCell = section->cellAt(0, testCol).cell; + 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); +} + +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/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableColumn.h b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableColumn.h new file mode 100644 index 0000000000..62703980ab --- /dev/null +++ b/src/3rdparty/webkit/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 AccessibilityRole roleValue() const { return ColumnRole; } + virtual bool accessibilityIsIgnored() const { return false; } + 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/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableHeaderContainer.cpp b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableHeaderContainer.cpp new file mode 100644 index 0000000000..af9de39805 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableHeaderContainer.cpp @@ -0,0 +1,87 @@ +/* + * 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 "AccessibilityTable.h" +#include "AXObjectCache.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(); +} + +void AccessibilityTableHeaderContainer::addChildren() +{ + ASSERT(!m_haveChildren); + + m_haveChildren = true; + if (!m_parentTable || !m_parentTable->isDataTable()) + 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/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableHeaderContainer.h b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableHeaderContainer.h new file mode 100644 index 0000000000..8a9448a5db --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableHeaderContainer.h @@ -0,0 +1,67 @@ +/* + * 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 bool accessibilityIsIgnored() const { return false; } + + virtual const AccessibilityChildrenVector& children(); + virtual void addChildren(); + + virtual IntSize size() const; + virtual IntRect elementRect() const; + +private: + AccessibilityTable* m_parentTable; + IntRect m_headerRect; + +}; + +} // namespace WebCore + +#endif // AccessibilityTableHeaderContainer_h diff --git a/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableRow.cpp b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableRow.cpp new file mode 100644 index 0000000000..9f5f972927 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableRow.cpp @@ -0,0 +1,118 @@ +/* + * 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 "AccessibilityTableCell.h" +#include "AXObjectCache.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->isDataTable()) + return false; + + return true; +} + +bool AccessibilityTableRow::accessibilityIsIgnored() const +{ + if (!isTableRow()) + return AccessibilityRenderObject::accessibilityIsIgnored(); + + return false; +} + +AccessibilityObject* AccessibilityTableRow::parentTable() const +{ + if (!m_renderer || !m_renderer->isTableRow()) + return 0; + + return axObjectCache()->getOrCreate(static_cast<RenderTableRow*>(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/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableRow.h b/src/3rdparty/webkit/WebCore/accessibility/AccessibilityTableRow.h new file mode 100644 index 0000000000..29ac935c95 --- /dev/null +++ b/src/3rdparty/webkit/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/src/3rdparty/webkit/WebCore/accessibility/qt/AccessibilityObjectQt.cpp b/src/3rdparty/webkit/WebCore/accessibility/qt/AccessibilityObjectQt.cpp new file mode 100644 index 0000000000..17100274f4 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/accessibility/qt/AccessibilityObjectQt.cpp @@ -0,0 +1,34 @@ +/* + * 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; +} + +} // namespace WebCore + +#endif // HAVE(ACCESSIBILITY) |