AccessibilityObject.cpp 37.5 KB
Newer Older
alice.liu@apple.com's avatar
alice.liu@apple.com committed
1
/*
2
 * Copyright (C) 2008, 2009, 2011 Apple Inc. All rights reserved.
alice.liu@apple.com's avatar
alice.liu@apple.com committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
 *     its contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "AccessibilityObject.h"

#include "AXObjectCache.h"
33
#include "AccessibilityRenderObject.h"
34
#include "FloatRect.h"
alice.liu@apple.com's avatar
alice.liu@apple.com committed
35 36 37
#include "FocusController.h"
#include "Frame.h"
#include "FrameLoader.h"
38
#include "FrameSelection.h"
39
#include "HTMLNames.h"
40
#include "LocalizedStrings.h"
alice.liu@apple.com's avatar
alice.liu@apple.com committed
41
#include "NodeList.h"
42
#include "NotImplemented.h"
alice.liu@apple.com's avatar
alice.liu@apple.com committed
43 44
#include "Page.h"
#include "RenderImage.h"
45
#include "RenderListItem.h"
alice.liu@apple.com's avatar
alice.liu@apple.com committed
46 47 48 49 50 51 52 53 54
#include "RenderListMarker.h"
#include "RenderMenuList.h"
#include "RenderTextControl.h"
#include "RenderTheme.h"
#include "RenderView.h"
#include "RenderWidget.h"
#include "TextIterator.h"
#include "htmlediting.h"
#include "visible_units.h"
55
#include <wtf/StdLibExtras.h>
56 57
#include <wtf/text/StringBuilder.h>
#include <wtf/text/StringConcatenate.h>
58
#include <wtf/unicode/CharacterNames.h>
alice.liu@apple.com's avatar
alice.liu@apple.com committed
59 60 61 62 63 64 65

using namespace std;

namespace WebCore {

using namespace HTMLNames;

66 67
AccessibilityObject::AccessibilityObject()
    : m_id(0)
68
    , m_haveChildren(false)
69
    , m_role(UnknownRole)
70 71 72
#if PLATFORM(GTK)
    , m_wrapper(0)
#endif
alice.liu@apple.com's avatar
alice.liu@apple.com committed
73 74 75 76 77
{
}

AccessibilityObject::~AccessibilityObject()
{
78
    ASSERT(isDetached());
alice.liu@apple.com's avatar
alice.liu@apple.com committed
79 80 81 82
}

void AccessibilityObject::detach()
{
83
#if HAVE(ACCESSIBILITY)
84
    setWrapper(0);
85
#endif    
alice.liu@apple.com's avatar
alice.liu@apple.com committed
86 87
}

88
AccessibilityObject* AccessibilityObject::parentObjectUnignored() const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
89 90
{
    AccessibilityObject* parent;
91 92 93
    for (parent = parentObject(); parent && parent->accessibilityIsIgnored(); parent = parent->parentObject()) {
    }
    
alice.liu@apple.com's avatar
alice.liu@apple.com committed
94 95 96
    return parent;
}

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
AccessibilityObject* AccessibilityObject::firstAccessibleObjectFromNode(const Node* node)
{
    ASSERT(AXObjectCache::accessibilityEnabled());

    if (!node)
        return 0;

    Document* document = node->document();
    if (!document)
        return 0;

    AXObjectCache* cache = document->axObjectCache();

    AccessibilityObject* accessibleObject = cache->getOrCreate(node->renderer());
    while (accessibleObject && accessibleObject->accessibilityIsIgnored()) {
        node = node->traverseNextNode();

        while (node && !node->renderer())
            node = node->traverseNextSibling();

        if (!node)
            return 0;

        accessibleObject = cache->getOrCreate(node->renderer());
    }

    return accessibleObject;
}

126
bool AccessibilityObject::isARIAInput(AccessibilityRole ariaRole)
alice.liu@apple.com's avatar
alice.liu@apple.com committed
127
{
128 129 130 131 132 133 134
    return ariaRole == RadioButtonRole || ariaRole == CheckBoxRole || ariaRole == TextFieldRole;
}    
    
bool AccessibilityObject::isARIAControl(AccessibilityRole ariaRole)
{
    return isARIAInput(ariaRole) || ariaRole == TextAreaRole || ariaRole == ButtonRole 
    || ariaRole == ComboBoxRole || ariaRole == SliderRole; 
alice.liu@apple.com's avatar
alice.liu@apple.com committed
135 136
}

137 138 139 140 141
IntPoint AccessibilityObject::clickPoint() const
{
    IntRect rect = elementRect();
    return IntPoint(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2);
}
142

143
bool AccessibilityObject::press() const
144
{
alice.liu@apple.com's avatar
alice.liu@apple.com committed
145 146
    Element* actionElem = actionElement();
    if (!actionElem)
147
        return false;
alice.liu@apple.com's avatar
alice.liu@apple.com committed
148 149 150
    if (Frame* f = actionElem->document()->frame())
        f->loader()->resetMultipleFormSubmissionProtection();
    actionElem->accessKeyAction(true);
151
    return true;
alice.liu@apple.com's avatar
alice.liu@apple.com committed
152
}
153 154 155
    
String AccessibilityObject::language() const
{
156 157 158 159
    const AtomicString& lang = getAttribute(langAttr);
    if (!lang.isEmpty())
        return lang;

160 161 162 163 164 165 166
    AccessibilityObject* parent = parentObject();
    
    // as a last resort, fall back to the content language specified in the meta tag
    if (!parent) {
        Document* doc = document();
        if (doc)
            return doc->contentLanguage();
167
        return nullAtom;
168 169 170 171 172
    }
    
    return parent->language();
}
    
173
VisiblePositionRange AccessibilityObject::visiblePositionRangeForUnorderedPositions(const VisiblePosition& visiblePos1, const VisiblePosition& visiblePos2) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
174 175 176
{
    if (visiblePos1.isNull() || visiblePos2.isNull())
        return VisiblePositionRange();
177

alice.liu@apple.com's avatar
alice.liu@apple.com committed
178 179 180
    VisiblePosition startPos;
    VisiblePosition endPos;
    bool alreadyInOrder;
181

alice.liu@apple.com's avatar
alice.liu@apple.com committed
182
    // upstream is ordered before downstream for the same position
183
    if (visiblePos1 == visiblePos2 && visiblePos2.affinity() == UPSTREAM)
alice.liu@apple.com's avatar
alice.liu@apple.com committed
184
        alreadyInOrder = false;
185

alice.liu@apple.com's avatar
alice.liu@apple.com committed
186
    // use selection order to see if the positions are in order
187
    else
188
        alreadyInOrder = VisibleSelection(visiblePos1, visiblePos2).isBaseFirst();
189

alice.liu@apple.com's avatar
alice.liu@apple.com committed
190 191 192 193 194 195 196
    if (alreadyInOrder) {
        startPos = visiblePos1;
        endPos = visiblePos2;
    } else {
        startPos = visiblePos2;
        endPos = visiblePos1;
    }
197

alice.liu@apple.com's avatar
alice.liu@apple.com committed
198 199 200
    return VisiblePositionRange(startPos, endPos);
}

201
VisiblePositionRange AccessibilityObject::positionOfLeftWord(const VisiblePosition& visiblePos) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
202 203 204 205 206 207
{
    VisiblePosition startPosition = startOfWord(visiblePos, LeftWordIfOnBoundary);
    VisiblePosition endPosition = endOfWord(startPosition);
    return VisiblePositionRange(startPosition, endPosition);
}

208
VisiblePositionRange AccessibilityObject::positionOfRightWord(const VisiblePosition& visiblePos) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
{
    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();
228
        if (!p.deprecatedNode())
alice.liu@apple.com's avatar
alice.liu@apple.com committed
229
            break;
230
        renderer = p.deprecatedNode()->renderer();
eric@webkit.org's avatar
eric@webkit.org committed
231
        if (!renderer || (renderer->isRenderBlock() && !p.deprecatedEditingOffset()))
mitz@apple.com's avatar
mitz@apple.com committed
232 233 234 235 236
            break;
        InlineBox* box;
        int ignoredCaretOffset;
        p.getInlineBoxAndOffset(tempPosition.affinity(), box, ignoredCaretOffset);
        if (box)
alice.liu@apple.com's avatar
alice.liu@apple.com committed
237 238 239
            break;
        startPosition = tempPosition;
    }
240

alice.liu@apple.com's avatar
alice.liu@apple.com committed
241 242 243
    return startPosition;
}

244
VisiblePositionRange AccessibilityObject::leftLineVisiblePositionRange(const VisiblePosition& visiblePos) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
245 246 247
{
    if (visiblePos.isNull())
        return VisiblePositionRange();
248

alice.liu@apple.com's avatar
alice.liu@apple.com committed
249 250 251 252 253
    // 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();
254

alice.liu@apple.com's avatar
alice.liu@apple.com committed
255 256
    VisiblePosition startPosition = startOfLine(prevVisiblePos);

257
    // keep searching for a valid line start position.  Unless the VisiblePosition is at the very beginning, there should
258 259
    // 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.
alice.liu@apple.com's avatar
alice.liu@apple.com committed
260 261 262 263 264 265
    // 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);
        }
266
    } else
alice.liu@apple.com's avatar
alice.liu@apple.com committed
267
        startPosition = updateAXLineStartForVisiblePosition(startPosition);
268

alice.liu@apple.com's avatar
alice.liu@apple.com committed
269 270 271 272
    VisiblePosition endPosition = endOfLine(prevVisiblePos);
    return VisiblePositionRange(startPosition, endPosition);
}

273
VisiblePositionRange AccessibilityObject::rightLineVisiblePositionRange(const VisiblePosition& visiblePos) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
274 275 276
{
    if (visiblePos.isNull())
        return VisiblePositionRange();
277

alice.liu@apple.com's avatar
alice.liu@apple.com committed
278 279 280 281
    // make sure we move off of a line end
    VisiblePosition nextVisiblePos = visiblePos.next();
    if (nextVisiblePos.isNull())
        return VisiblePositionRange();
282

alice.liu@apple.com's avatar
alice.liu@apple.com committed
283
    VisiblePosition startPosition = startOfLine(nextVisiblePos);
284

alice.liu@apple.com's avatar
alice.liu@apple.com committed
285
    // fetch for a valid line start position
286
    if (startPosition.isNull()) {
alice.liu@apple.com's avatar
alice.liu@apple.com committed
287 288
        startPosition = visiblePos;
        nextVisiblePos = nextVisiblePos.next();
289
    } else
alice.liu@apple.com's avatar
alice.liu@apple.com committed
290
        startPosition = updateAXLineStartForVisiblePosition(startPosition);
291

alice.liu@apple.com's avatar
alice.liu@apple.com committed
292 293 294
    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
295
    // Unless the VisiblePosition is at the very end, there should always be a valid line range.  However, endOfLine will
296
    // return null for position by a floating object, since floating object doesn't really belong to any line.
alice.liu@apple.com's avatar
alice.liu@apple.com committed
297 298 299 300 301
    // 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);
    }
302

alice.liu@apple.com's avatar
alice.liu@apple.com committed
303 304 305
    return VisiblePositionRange(startPosition, endPosition);
}

306
VisiblePositionRange AccessibilityObject::sentenceForPosition(const VisiblePosition& visiblePos) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
307 308 309 310 311 312 313 314
{
    // 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);
}

315
VisiblePositionRange AccessibilityObject::paragraphForPosition(const VisiblePosition& visiblePos) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
316 317 318 319 320 321 322 323
{
    VisiblePosition startPosition = startOfParagraph(visiblePos);
    VisiblePosition endPosition = endOfParagraph(startPosition);
    return VisiblePositionRange(startPosition, endPosition);
}

static VisiblePosition startOfStyleRange(const VisiblePosition visiblePos)
{
324
    RenderObject* renderer = visiblePos.deepEquivalent().deprecatedNode()->renderer();
alice.liu@apple.com's avatar
alice.liu@apple.com committed
325 326
    RenderObject* startRenderer = renderer;
    RenderStyle* style = renderer->style();
327

alice.liu@apple.com's avatar
alice.liu@apple.com committed
328 329 330 331 332
    // 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;
333

alice.liu@apple.com's avatar
alice.liu@apple.com committed
334 335 336
        // stop at style change
        if (r->style() != style)
            break;
337

alice.liu@apple.com's avatar
alice.liu@apple.com committed
338 339 340
        // remember match
        startRenderer = r;
    }
341

342
    return firstPositionInOrBeforeNode(startRenderer->node());
alice.liu@apple.com's avatar
alice.liu@apple.com committed
343 344
}

eric@webkit.org's avatar
eric@webkit.org committed
345
static VisiblePosition endOfStyleRange(const VisiblePosition& visiblePos)
alice.liu@apple.com's avatar
alice.liu@apple.com committed
346
{
347
    RenderObject* renderer = visiblePos.deepEquivalent().deprecatedNode()->renderer();
alice.liu@apple.com's avatar
alice.liu@apple.com committed
348 349 350 351 352 353 354 355
    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;
356

alice.liu@apple.com's avatar
alice.liu@apple.com committed
357 358 359
        // stop at style change
        if (r->style() != style)
            break;
360

alice.liu@apple.com's avatar
alice.liu@apple.com committed
361 362 363
        // remember match
        endRenderer = r;
    }
364

365
    return lastPositionInOrAfterNode(endRenderer->node());
alice.liu@apple.com's avatar
alice.liu@apple.com committed
366 367
}

368
VisiblePositionRange AccessibilityObject::styleRangeForPosition(const VisiblePosition& visiblePos) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
369 370 371 372 373 374 375 376
{
    if (visiblePos.isNull())
        return VisiblePositionRange();

    return VisiblePositionRange(startOfStyleRange(visiblePos), endOfStyleRange(visiblePos));
}

// NOTE: Consider providing this utility method as AX API
377
VisiblePositionRange AccessibilityObject::visiblePositionRangeForRange(const PlainTextRange& range) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
378
{
379
    unsigned textLength = getLengthForTextRange();
380
    if (range.start + range.length > textLength)
alice.liu@apple.com's avatar
alice.liu@apple.com committed
381
        return VisiblePositionRange();
382

383
    VisiblePosition startPosition = visiblePositionForIndex(range.start);
alice.liu@apple.com's avatar
alice.liu@apple.com committed
384
    startPosition.setAffinity(DOWNSTREAM);
385
    VisiblePosition endPosition = visiblePositionForIndex(range.start + range.length);
alice.liu@apple.com's avatar
alice.liu@apple.com committed
386 387 388
    return VisiblePositionRange(startPosition, endPosition);
}

389
static bool replacedNodeNeedsCharacter(Node* replacedNode)
alice.liu@apple.com's avatar
alice.liu@apple.com committed
390 391 392
{
    // we should always be given a rendered node and a replaced node, but be safe
    // replaced nodes are either attachments (widgets) or images
393
    if (!replacedNode || !replacedNode->renderer() || !replacedNode->renderer()->isReplaced() || replacedNode->isTextNode())
394
        return false;
alice.liu@apple.com's avatar
alice.liu@apple.com committed
395 396

    // create an AX object, but skip it if it is not supposed to be seen
397
    AccessibilityObject* object = replacedNode->renderer()->document()->axObjectCache()->getOrCreate(replacedNode->renderer());
alice.liu@apple.com's avatar
alice.liu@apple.com committed
398
    if (object->accessibilityIsIgnored())
399
        return false;
400

401
    return true;
alice.liu@apple.com's avatar
alice.liu@apple.com committed
402 403
}

404
// Finds a RenderListItem parent give a node.
405
static RenderListItem* renderListItemContainerForNode(Node* node)
406
{
407
    for (; node; node = node->parentNode()) {
408 409 410
        RenderBoxModelObject* renderer = node->renderBoxModelObject();
        if (renderer && renderer->isListItem())
            return toRenderListItem(renderer);
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
    }
    return 0;
}
    
// Returns the text associated with a list marker if this node is contained within a list item.
String AccessibilityObject::listMarkerTextForNodeAndPosition(Node* node, const VisiblePosition& visiblePositionStart) const
{
    // If the range does not contain the start of the line, the list marker text should not be included.
    if (!isStartOfLine(visiblePositionStart))
        return String();

    RenderListItem* listItem = renderListItemContainerForNode(node);
    if (!listItem)
        return String();
        
    // If this is in a list item, we need to manually add the text for the list marker 
    // because a RenderListMarker does not have a Node equivalent and thus does not appear
    // when iterating text.
    const String& markerText = listItem->markerText();
    if (markerText.isEmpty())
        return String();
                
    // Append text, plus the period that follows the text.
    // FIXME: Not all list marker styles are followed by a period, but this
    // sounds much better when there is a synthesized pause because of a period.
436
    return makeString(markerText, ". ");
437 438
}
    
439
String AccessibilityObject::stringForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
440 441 442
{
    if (visiblePositionRange.isNull())
        return String();
443

444
    StringBuilder builder;
445 446
    RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end);
    for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
alice.liu@apple.com's avatar
alice.liu@apple.com committed
447
        // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
448
        if (it.length()) {
449 450 451
            // Add a textual representation for list marker text
            String listMarkerText = listMarkerTextForNodeAndPosition(it.node(), visiblePositionRange.start);
            if (!listMarkerText.isEmpty())
452 453 454
                builder.append(listMarkerText);

            builder.append(it.characters(), it.length());
alice.liu@apple.com's avatar
alice.liu@apple.com committed
455 456 457 458 459 460
        } 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);
461

462
            if (replacedNodeNeedsCharacter(node->childNode(offset)))
463
                builder.append(objectReplacementCharacter);
alice.liu@apple.com's avatar
alice.liu@apple.com committed
464 465
        }
    }
466

467
    return builder.toString();
alice.liu@apple.com's avatar
alice.liu@apple.com committed
468 469
}

470
int AccessibilityObject::lengthForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
471 472
{
    // FIXME: Multi-byte support
473
    if (visiblePositionRange.isNull())
alice.liu@apple.com's avatar
alice.liu@apple.com committed
474
        return -1;
475 476 477 478 479
    
    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)
480
        if (it.length())
481
            length += it.length();
482
        else {
483 484 485 486 487
            // 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);
alice.liu@apple.com's avatar
alice.liu@apple.com committed
488

489 490 491 492 493 494
            if (replacedNodeNeedsCharacter(node->childNode(offset)))
                length++;
        }
    }
    
    return length;
alice.liu@apple.com's avatar
alice.liu@apple.com committed
495 496
}

497
VisiblePosition AccessibilityObject::nextWordEnd(const VisiblePosition& visiblePos) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
498 499 500 501 502 503 504 505 506 507 508 509
{
    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);
}

510
VisiblePosition AccessibilityObject::previousWordStart(const VisiblePosition& visiblePos) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
511 512 513 514 515 516 517 518
{
    if (visiblePos.isNull())
        return VisiblePosition();

    // make sure we move off of a word start
    VisiblePosition prevVisiblePos = visiblePos.previous();
    if (prevVisiblePos.isNull())
        return VisiblePosition();
519

alice.liu@apple.com's avatar
alice.liu@apple.com committed
520 521 522
    return startOfWord(prevVisiblePos, RightWordIfOnBoundary);
}

523
VisiblePosition AccessibilityObject::nextLineEndPosition(const VisiblePosition& visiblePos) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
524 525 526
{
    if (visiblePos.isNull())
        return VisiblePosition();
527

alice.liu@apple.com's avatar
alice.liu@apple.com committed
528 529 530 531
    // to make sure we move off of a line end
    VisiblePosition nextVisiblePos = visiblePos.next();
    if (nextVisiblePos.isNull())
        return VisiblePosition();
532

alice.liu@apple.com's avatar
alice.liu@apple.com committed
533 534 535 536 537 538 539 540
    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);
    }
541

alice.liu@apple.com's avatar
alice.liu@apple.com committed
542 543 544
    return endPosition;
}

545
VisiblePosition AccessibilityObject::previousLineStartPosition(const VisiblePosition& visiblePos) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
546 547 548
{
    if (visiblePos.isNull())
        return VisiblePosition();
549

alice.liu@apple.com's avatar
alice.liu@apple.com committed
550 551 552 553
    // make sure we move off of a line start
    VisiblePosition prevVisiblePos = visiblePos.previous();
    if (prevVisiblePos.isNull())
        return VisiblePosition();
554

alice.liu@apple.com's avatar
alice.liu@apple.com committed
555 556 557 558 559 560 561 562 563
    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);
        }
564
    } else
alice.liu@apple.com's avatar
alice.liu@apple.com committed
565
        startPosition = updateAXLineStartForVisiblePosition(startPosition);
566

alice.liu@apple.com's avatar
alice.liu@apple.com committed
567 568 569
    return startPosition;
}

570
VisiblePosition AccessibilityObject::nextSentenceEndPosition(const VisiblePosition& visiblePos) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
571 572 573 574 575
{
    // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
    // Related? <rdar://problem/3927736> Text selection broken in 8A336
    if (visiblePos.isNull())
        return VisiblePosition();
576

alice.liu@apple.com's avatar
alice.liu@apple.com committed
577 578 579 580 581 582
    // 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
583
    // see this empty line.  Instead, return the end position of the empty line.
alice.liu@apple.com's avatar
alice.liu@apple.com committed
584
    VisiblePosition endPosition;
585 586
    
    String lineString = plainText(makeRange(startOfLine(nextVisiblePos), endOfLine(nextVisiblePos)).get());
alice.liu@apple.com's avatar
alice.liu@apple.com committed
587 588 589 590
    if (lineString.isEmpty())
        endPosition = nextVisiblePos;
    else
        endPosition = endOfSentence(nextVisiblePos);
591

alice.liu@apple.com's avatar
alice.liu@apple.com committed
592 593 594
    return endPosition;
}

595
VisiblePosition AccessibilityObject::previousSentenceStartPosition(const VisiblePosition& visiblePos) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
596 597 598 599 600 601 602 603 604 605
{
    // 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();
606 607

    // treat empty line as a separate sentence.
alice.liu@apple.com's avatar
alice.liu@apple.com committed
608
    VisiblePosition startPosition;
609
    
610
    String lineString = plainText(makeRange(startOfLine(previousVisiblePos), endOfLine(previousVisiblePos)).get());
alice.liu@apple.com's avatar
alice.liu@apple.com committed
611 612 613 614
    if (lineString.isEmpty())
        startPosition = previousVisiblePos;
    else
        startPosition = startOfSentence(previousVisiblePos);
615

alice.liu@apple.com's avatar
alice.liu@apple.com committed
616 617 618
    return startPosition;
}

619
VisiblePosition AccessibilityObject::nextParagraphEndPosition(const VisiblePosition& visiblePos) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
620 621 622
{
    if (visiblePos.isNull())
        return VisiblePosition();
623

alice.liu@apple.com's avatar
alice.liu@apple.com committed
624 625 626 627 628 629 630 631
    // make sure we move off of a paragraph end
    VisiblePosition nextPos = visiblePos.next();
    if (nextPos.isNull())
        return VisiblePosition();

    return endOfParagraph(nextPos);
}

632
VisiblePosition AccessibilityObject::previousParagraphStartPosition(const VisiblePosition& visiblePos) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
633 634 635
{
    if (visiblePos.isNull())
        return VisiblePosition();
636

alice.liu@apple.com's avatar
alice.liu@apple.com committed
637 638 639 640 641 642 643 644
    // make sure we move off of a paragraph start
    VisiblePosition previousPos = visiblePos.previous();
    if (previousPos.isNull())
        return VisiblePosition();

    return startOfParagraph(previousPos);
}

645
AccessibilityObject* AccessibilityObject::accessibilityObjectForPosition(const VisiblePosition& visiblePos) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
646 647 648 649
{
    if (visiblePos.isNull())
        return 0;

650
    RenderObject* obj = visiblePos.deepEquivalent().deprecatedNode()->renderer();
alice.liu@apple.com's avatar
alice.liu@apple.com committed
651 652
    if (!obj)
        return 0;
653

654
    return obj->document()->axObjectCache()->getOrCreate(obj);
alice.liu@apple.com's avatar
alice.liu@apple.com committed
655 656
}

657
int AccessibilityObject::lineForPosition(const VisiblePosition& visiblePos) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
658 659 660
{
    if (visiblePos.isNull())
        return 0;
661

alice.liu@apple.com's avatar
alice.liu@apple.com committed
662 663 664 665 666 667 668 669
    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))) {
670
        ++lineCount;
alice.liu@apple.com's avatar
alice.liu@apple.com committed
671
        savedVisiblePos = currentVisiblePos;
672
        VisiblePosition prevVisiblePos = previousLinePosition(currentVisiblePos, 0);
alice.liu@apple.com's avatar
alice.liu@apple.com committed
673 674
        currentVisiblePos = prevVisiblePos;
    }
675

alice.liu@apple.com's avatar
alice.liu@apple.com committed
676 677 678 679
    return lineCount - 1;
}

// NOTE: Consider providing this utility method as AX API
680
PlainTextRange AccessibilityObject::plainTextRangeForVisiblePositionRange(const VisiblePositionRange& positionRange) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
681
{
682 683
    int index1 = index(positionRange.start);
    int index2 = index(positionRange.end);
alice.liu@apple.com's avatar
alice.liu@apple.com committed
684 685
    if (index1 < 0 || index2 < 0 || index1 > index2)
        return PlainTextRange();
686

alice.liu@apple.com's avatar
alice.liu@apple.com committed
687 688 689 690 691 692 693 694 695
    return PlainTextRange(index1, index2 - index1);
}

// The composed character range in the text associated with this accessibility object that
// is specified by the given screen coordinates. This parameterized attribute returns the
// complete range of characters (including surrogate pairs of multi-byte glyphs) at the given
// screen coordinates.
// NOTE: This varies from AppKit when the point is below the last line. AppKit returns an
// an error in that case. We return textControl->text().length(), 1. Does this matter?
696
PlainTextRange AccessibilityObject::doAXRangeForPosition(const IntPoint& point) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
697
{
698 699
    int i = index(visiblePositionForPoint(point));
    if (i < 0)
alice.liu@apple.com's avatar
alice.liu@apple.com committed
700
        return PlainTextRange();
701

702
    return PlainTextRange(i, 1);
alice.liu@apple.com's avatar
alice.liu@apple.com committed
703 704 705 706
}

// Given a character index, the range of text associated with this accessibility object
// over which the style in effect at that character index applies.
707
PlainTextRange AccessibilityObject::doAXStyleRangeForIndex(unsigned index) const
alice.liu@apple.com's avatar
alice.liu@apple.com committed
708
{
709 710
    VisiblePositionRange range = styleRangeForPosition(visiblePositionForIndex(index, false));
    return plainTextRangeForVisiblePositionRange(range);
alice.liu@apple.com's avatar
alice.liu@apple.com committed
711 712 713 714 715 716
}

// Given an indexed character, the line number of the text associated with this accessibility
// object that contains the character.
unsigned AccessibilityObject::doAXLineForIndex(unsigned index)
{
717
    return lineForPosition(visiblePositionForIndex(index, false));
alice.liu@apple.com's avatar
alice.liu@apple.com committed
718
}
719 720 721 722 723 724 725 726 727
    
Document* AccessibilityObject::document() const
{
    FrameView* frameView = documentFrameView();
    if (!frameView)
        return 0;
    
    return frameView->frame()->document();
}
alice.liu@apple.com's avatar
alice.liu@apple.com committed
728

729 730 731 732 733 734 735 736 737 738
FrameView* AccessibilityObject::documentFrameView() const 
{ 
    const AccessibilityObject* object = this;
    while (object && !object->isAccessibilityRenderObject()) 
        object = object->parentObject();
        
    if (!object)
        return 0;

    return object->documentFrameView();
739
}
740 741 742 743 744 745
    
void AccessibilityObject::updateChildrenIfNecessary()
{
    if (!hasChildren())
        addChildren();    
}
746

alice.liu@apple.com's avatar
alice.liu@apple.com committed
747 748 749
void AccessibilityObject::clearChildren()
{
    m_children.clear();
750
    m_haveChildren = false;
alice.liu@apple.com's avatar
alice.liu@apple.com committed
751 752
}

753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
AccessibilityObject* AccessibilityObject::anchorElementForNode(Node* node)
{
    RenderObject* obj = node->renderer();
    if (!obj)
        return 0;
    
    RefPtr<AccessibilityObject> axObj = obj->document()->axObjectCache()->getOrCreate(obj);
    Element* anchor = axObj->anchorElement();
    if (!anchor)
        return 0;
    
    RenderObject* anchorRenderer = anchor->renderer();
    if (!anchorRenderer)
        return 0;
    
    return anchorRenderer->document()->axObjectCache()->getOrCreate(anchorRenderer);
}
    
771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817
void AccessibilityObject::ariaTreeRows(AccessibilityChildrenVector& result)
{
    AccessibilityChildrenVector axChildren = children();
    unsigned count = axChildren.size();
    for (unsigned k = 0; k < count; ++k) {
        AccessibilityObject* obj = axChildren[k].get();
        
        // Add tree items as the rows.
        if (obj->roleValue() == TreeItemRole) 
            result.append(obj);

        // Now see if this item also has rows hiding inside of it.
        obj->ariaTreeRows(result);
    }
}
    
void AccessibilityObject::ariaTreeItemContent(AccessibilityChildrenVector& result)
{
    // The ARIA tree item content are the item that are not other tree items or their containing groups.
    AccessibilityChildrenVector axChildren = children();
    unsigned count = axChildren.size();
    for (unsigned k = 0; k < count; ++k) {
        AccessibilityObject* obj = axChildren[k].get();
        AccessibilityRole role = obj->roleValue();
        if (role == TreeItemRole || role == GroupRole)
            continue;
        
        result.append(obj);
    }
}

void AccessibilityObject::ariaTreeItemDisclosedRows(AccessibilityChildrenVector& result)
{
    AccessibilityChildrenVector axChildren = children();
    unsigned count = axChildren.size();
    for (unsigned k = 0; k < count; ++k) {
        AccessibilityObject* obj = axChildren[k].get();
        
        // Add tree items as the rows.
        if (obj->roleValue() == TreeItemRole)
            result.append(obj);
        // If it's not a tree item, then descend into the group to find more tree items.
        else 
            obj->ariaTreeRows(result);
    }    
}
    
818 819 820
const String& AccessibilityObject::actionVerb() const
{
    // FIXME: Need to add verbs for select elements.
821 822 823 824 825 826
    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()));
827 828
    DEFINE_STATIC_LOCAL(const String, menuListAction, (AXMenuListActionVerb()));
    DEFINE_STATIC_LOCAL(const String, menuListPopupAction, (AXMenuListPopupActionVerb()));
829
    DEFINE_STATIC_LOCAL(const String, noAction, ());
830 831

    switch (roleValue()) {
832 833 834 835 836 837 838 839 840 841 842 843
    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;
844 845 846 847
    case PopUpButtonRole:
        return menuListAction;
    case MenuListPopupRole:
        return menuListPopupAction;
848 849
    default:
        return noAction;
850 851
    }
}
852

853 854 855 856 857
bool AccessibilityObject::ariaIsMultiline() const
{
    return equalIgnoringCase(getAttribute(aria_multilineAttr), "true");
}

858 859 860 861 862 863 864 865 866 867 868 869 870
const AtomicString& AccessibilityObject::invalidStatus() const
{
    DEFINE_STATIC_LOCAL(const AtomicString, invalidStatusFalse, ("false"));
    
    // aria-invalid can return false (default), grammer, spelling, or true.
    const AtomicString& ariaInvalid = getAttribute(aria_invalidAttr);
    
    // If empty or not present, it should return false.
    if (ariaInvalid.isEmpty())
        return invalidStatusFalse;
    
    return ariaInvalid;
}
cfleizach@apple.com's avatar
cfleizach@apple.com committed
871
 
872
const AtomicString& AccessibilityObject::getAttribute(const QualifiedName& attribute) const
873
{
874 875
    Node* elementNode = node();
    if (!elementNode)
876 877
        return nullAtom;
    
878
    if (!elementNode->isElementNode())
879 880
        return nullAtom;
    
881
    Element* element = static_cast<Element*>(elementNode);
882
    return element->fastGetAttribute(attribute);
883 884
}
    
cfleizach@apple.com's avatar
cfleizach@apple.com committed
885 886 887 888 889 890 891 892 893 894 895
// Lacking concrete evidence of orientation, horizontal means width > height. vertical is height > width;
AccessibilityOrientation AccessibilityObject::orientation() const
{
    IntRect bounds = elementRect();
    if (bounds.size().width() > bounds.size().height())
        return AccessibilityOrientationHorizontal;
    if (bounds.size().height() > bounds.size().width())
        return AccessibilityOrientationVertical;

    // A tie goes to horizontal.
    return AccessibilityOrientationHorizontal;
896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930
}    

typedef HashMap<String, AccessibilityRole, CaseFoldingHash> ARIARoleMap;

struct RoleEntry {
    String ariaRole;
    AccessibilityRole webcoreRole;
};

static ARIARoleMap* createARIARoleMap()
{
    const RoleEntry roles[] = {
        { "alert", ApplicationAlertRole },
        { "alertdialog", ApplicationAlertDialogRole },
        { "application", LandmarkApplicationRole },
        { "article", DocumentArticleRole },
        { "banner", LandmarkBannerRole },
        { "button", ButtonRole },
        { "checkbox", CheckBoxRole },
        { "complementary", LandmarkComplementaryRole },
        { "contentinfo", LandmarkContentInfoRole },
        { "dialog", ApplicationDialogRole },
        { "directory", DirectoryRole },
        { "grid", TableRole },
        { "gridcell", CellRole },
        { "columnheader", ColumnHeaderRole },
        { "combobox", ComboBoxRole },
        { "definition", DefinitionListDefinitionRole },
        { "document", DocumentRole },
        { "rowheader", RowHeaderRole },
        { "group", GroupRole },
        { "heading", HeadingRole },
        { "img", ImageRole },
        { "link", WebCoreLinkRole },
        { "list", ListRole },        
931
        { "listitem", ListItemRole },        
932 933 934 935 936
        { "listbox", ListBoxRole },
        { "log", ApplicationLogRole },
        // "option" isn't here because it may map to different roles depending on the parent element's role
        { "main", LandmarkMainRole },
        { "marquee", ApplicationMarqueeRole },
937
        { "math", DocumentMathRole },
938
        { "menu", MenuRole },
939
        { "menubar", MenuBarRole },
940 941 942 943 944 945
        // "menuitem" isn't here because it may map to different roles depending on the parent element's role
        { "menuitemcheckbox", MenuItemRole },
        { "menuitemradio", MenuItemRole },
        { "note", DocumentNoteRole },
        { "navigation", LandmarkNavigationRole },
        { "option", ListBoxOptionRole },
946
        { "presentation", PresentationalRole },
947 948 949 950 951 952
        { "progressbar", ProgressIndicatorRole },
        { "radio", RadioButtonRole },
        { "radiogroup", RadioGroupRole },
        { "region", DocumentRegionRole },
        { "row", RowRole },
        { "range", SliderRole },
953
        { "scrollbar", ScrollBarRole },
954 955 956 957 958 959 960 961
        { "search", LandmarkSearchRole },
        { "separator", SplitterRole },
        { "slider", SliderRole },
        { "spinbutton", ProgressIndicatorRole },
        { "status", ApplicationStatusRole },
        { "tab", TabRole },
        { "tablist", TabListRole },
        { "tabpanel", TabPanelRole },
962
        { "text", StaticTextRole },
963 964 965 966 967
        { "textbox", TextAreaRole },
        { "timer", ApplicationTimerRole },
        { "toolbar", ToolbarRole },
        { "tooltip", UserInterfaceTooltipRole },
        { "tree", TreeRole },
968
        { "treegrid", TreeGridRole },
969 970 971
        { "treeitem", TreeItemRole }
    };
    ARIARoleMap* roleMap = new ARIARoleMap;
972 973

    for (size_t i = 0; i < WTF_ARRAY_LENGTH(roles); ++i)
974 975 976 977 978 979 980
        roleMap->set(roles[i].ariaRole, roles[i].webcoreRole);
    return roleMap;
}

AccessibilityRole AccessibilityObject::ariaRoleToWebCoreRole(const String& value)
{
    ASSERT(!value.isEmpty());
981
    
982
    static const ARIARoleMap* roleMap = createARIARoleMap();
983 984 985 986 987 988 989 990 991 992 993 994 995

    Vector<String> roleVector;
    value.split(' ', roleVector);
    AccessibilityRole role = UnknownRole;
    unsigned size = roleVector.size();
    for (unsigned i = 0; i < size; ++i) {
        String roleName = roleVector[i];
        role = roleMap->get(roleName);
        if (role)
            return role;
    }
    
    return role;
cfleizach@apple.com's avatar
cfleizach@apple.com committed
996
}
997

998 999 1000 1001 1002 1003 1004 1005 1006
const AtomicString& AccessibilityObject::placeholderValue() const
{
    const AtomicString& placeholder = getAttribute(placeholderAttr);
    if (!placeholder.isEmpty())
        return placeholder;
    
    return nullAtom;
}
    
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
bool AccessibilityObject::isInsideARIALiveRegion() const
{
    if (supportsARIALiveRegion())
        return true;
    
    for (AccessibilityObject* axParent = parentObject(); axParent; axParent = axParent->parentObject()) {
        if (axParent->supportsARIALiveRegion())
            return true;
    }
    
    return false;
}

1020 1021 1022 1023 1024
bool AccessibilityObject::supportsARIAAttributes() const
{
    return supportsARIALiveRegion() || supportsARIADragging() || supportsARIADropping() || supportsARIAFlowTo() || supportsARIAOwns();
}
    
1025 1026 1027 1028 1029
bool AccessibilityObject::supportsARIALiveRegion() const
{
    const AtomicString& liveRegion = ariaLiveRegionStatus();
    return equalIgnoringCase(liveRegion, "polite") || equalIgnoringCase(liveRegion, "assertive");
}
1030 1031 1032 1033 1034 1035

AccessibilityObject* AccessibilityObject::elementAccessibilityHitTest(const IntPoint& point) const
{ 
    // Send the hit test back into the sub-frame if necessary.
    if (isAttachment()) {
        Widget* widget = widgetForAttachmentView();
1036
        // Normalize the point for the widget's bounds.
1037
        if (widget && widget->isFrameView())
1038
            return axObjectCache()->getOrCreate(widget)->accessibilityHitTest(IntPoint(point - widget->frameRect().location()));
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
    }

    return const_cast<AccessibilityObject*>(this); 
}
    
AXObjectCache* AccessibilityObject::axObjectCache() const
{
    Document* doc = document();
    if (doc)
        return doc->axObjectCache();
    return 0;
}
    
AccessibilityObject* AccessibilityObject::focusedUIElement() const
{
    Document* doc = document();
    if (!doc)
        return 0;
    
    Page* page = doc->page();
    if (!page)
        return 0;
    
    return AXObjectCache::focusedUIElementForPage(page);
}