1/* 2 * Copyright (C) 2008 Nuanti Ltd. 3 * Copyright (C) 2009 Jan Alonzo 4 * Copyright (C) 2009, 2010, 2012 Igalia S.L. 5 * 6 * Portions from Mozilla a11y, copyright as follows: 7 * 8 * The Original Code is mozilla.org code. 9 * 10 * The Initial Developer of the Original Code is 11 * Sun Microsystems, Inc. 12 * Portions created by the Initial Developer are Copyright (C) 2002 13 * the Initial Developer. All Rights Reserved. 14 * 15 * This library is free software; you can redistribute it and/or 16 * modify it under the terms of the GNU Library General Public 17 * License as published by the Free Software Foundation; either 18 * version 2 of the License, or (at your option) any later version. 19 * 20 * This library is distributed in the hope that it will be useful, 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 23 * Library General Public License for more details. 24 * 25 * You should have received a copy of the GNU Library General Public License 26 * along with this library; see the file COPYING.LIB. If not, write to 27 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 28 * Boston, MA 02110-1301, USA. 29 */ 30 31#include "config.h" 32#include "WebKitAccessibleUtil.h" 33 34#if HAVE(ACCESSIBILITY) 35 36#include "AccessibilityObject.h" 37#include "FrameView.h" 38#include "IntRect.h" 39#include "Node.h" 40#include "Range.h" 41#include "VisibleSelection.h" 42 43#include <wtf/text/AtomicString.h> 44#include <wtf/text/CString.h> 45 46using namespace WebCore; 47 48AtkAttributeSet* addToAtkAttributeSet(AtkAttributeSet* attributeSet, const char* name, const char* value) 49{ 50 AtkAttribute* attribute = static_cast<AtkAttribute*>(g_malloc(sizeof(AtkAttribute))); 51 attribute->name = g_strdup(name); 52 attribute->value = g_strdup(value); 53 attributeSet = g_slist_prepend(attributeSet, attribute); 54 return attributeSet; 55} 56 57void contentsRelativeToAtkCoordinateType(AccessibilityObject* coreObject, AtkCoordType coordType, IntRect rect, gint* x, gint* y, gint* width, gint* height) 58{ 59 FrameView* frameView = coreObject->documentFrameView(); 60 61 if (frameView) { 62 switch (coordType) { 63 case ATK_XY_WINDOW: 64 rect = frameView->contentsToWindow(rect); 65 break; 66 case ATK_XY_SCREEN: 67 rect = frameView->contentsToScreen(rect); 68 break; 69 } 70 } 71 72 if (x) 73 *x = rect.x(); 74 if (y) 75 *y = rect.y(); 76 if (width) 77 *width = rect.width(); 78 if (height) 79 *height = rect.height(); 80} 81 82// FIXME: Different kinds of elements are putting the title tag to use 83// in different AX fields. This might not be 100% correct but we will 84// keep it now in order to achieve consistency with previous behavior. 85static bool titleTagShouldBeUsedInDescriptionField(AccessibilityObject* coreObject) 86{ 87 return (coreObject->isLink() && !coreObject->isImageMapLink()) || coreObject->isImage(); 88} 89 90// This should be the "visible" text that's actually on the screen if possible. 91// If there's alternative text, that can override the title. 92String accessibilityTitle(AccessibilityObject* coreObject) 93{ 94 Vector<AccessibilityText> textOrder; 95 coreObject->accessibilityText(textOrder); 96 97 unsigned length = textOrder.size(); 98 for (unsigned k = 0; k < length; k++) { 99 const AccessibilityText& text = textOrder[k]; 100 101 // Once we encounter visible text, or the text from our children that should be used foremost. 102 if (text.textSource == VisibleText || text.textSource == ChildrenText) 103 return text.text; 104 105 // If there's an element that labels this object and it's not exposed, then we should use 106 // that text as our title. 107 if (text.textSource == LabelByElementText && !coreObject->exposesTitleUIElement()) 108 return text.text; 109 110 // Elements of role ToolbarRole will return its title as AlternativeText. 111 if (coreObject->roleValue() == ToolbarRole && text.textSource == AlternativeText) 112 return text.text; 113 114 // FIXME: The title tag is used in certain cases for the title. This usage should 115 // probably be in the description field since it's not "visible". 116 if (text.textSource == TitleTagText && !titleTagShouldBeUsedInDescriptionField(coreObject)) 117 return text.text; 118 } 119 return String(); 120} 121 122String accessibilityDescription(AccessibilityObject* coreObject) 123{ 124 Vector<AccessibilityText> textOrder; 125 coreObject->accessibilityText(textOrder); 126 127 unsigned length = textOrder.size(); 128 for (unsigned k = 0; k < length; k++) { 129 const AccessibilityText& text = textOrder[k]; 130 131 if (text.textSource == AlternativeText) 132 return text.text; 133 134 if (text.textSource == TitleTagText && titleTagShouldBeUsedInDescriptionField(coreObject)) 135 return text.text; 136 } 137 138 return String(); 139} 140 141bool selectionBelongsToObject(AccessibilityObject* coreObject, VisibleSelection& selection) 142{ 143 if (!coreObject || !coreObject->isAccessibilityRenderObject()) 144 return false; 145 146 if (selection.isNone()) 147 return false; 148 149 RefPtr<Range> range = selection.toNormalizedRange(); 150 if (!range) 151 return false; 152 153 // We want to check that both the selection intersects the node 154 // AND that the selection is not just "touching" one of the 155 // boundaries for the selected node. We want to check whether the 156 // node is actually inside the region, at least partially. 157 Node* node = coreObject->node(); 158 Node* lastDescendant = node->lastDescendant(); 159 return (range->intersectsNode(node, IGNORE_EXCEPTION) 160 && (range->endContainer() != node || range->endOffset()) 161 && (range->startContainer() != lastDescendant || range->startOffset() != lastOffsetInNode(lastDescendant))); 162} 163 164#endif 165