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