summaryrefslogtreecommitdiffstats
path: root/webkit/pending/AccessibilityObject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'webkit/pending/AccessibilityObject.cpp')
-rw-r--r--webkit/pending/AccessibilityObject.cpp1956
1 files changed, 0 insertions, 1956 deletions
diff --git a/webkit/pending/AccessibilityObject.cpp b/webkit/pending/AccessibilityObject.cpp
deleted file mode 100644
index f397671..0000000
--- a/webkit/pending/AccessibilityObject.cpp
+++ /dev/null
@@ -1,1956 +0,0 @@
-/*
- * 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 "AXObjectCache.h"
-#include "CharacterNames.h"
-#include "EventNames.h"
-#include "FloatRect.h"
-#include "FocusController.h"
-#include "Frame.h"
-#include "FrameLoader.h"
-#include "HTMLAreaElement.h"
-#include "HTMLFrameElementBase.h"
-#include "HTMLImageElement.h"
-#include "HTMLInputElement.h"
-#include "HTMLLabelElement.h"
-#include "HTMLMapElement.h"
-#include "HTMLSelectElement.h"
-#include "HTMLTextAreaElement.h"
-#include "HitTestRequest.h"
-#include "HitTestResult.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"
-
-using namespace std;
-
-namespace WebCore {
-
-using namespace EventNames;
-using namespace HTMLNames;
-
-AccessibilityObject::AccessibilityObject(RenderObject* renderer)
- : RefCounted<AccessibilityObject>(1)
- , m_renderer(renderer)
- , m_id(0)
- , m_haveChildren(false)
-{
- ASSERT_ARG(renderer, renderer);
-#ifndef NDEBUG
- m_renderer->setHasAXObject(true);
-#endif
-}
-
-AccessibilityObject::~AccessibilityObject()
-{
- ASSERT(isDetached());
-}
-
-PassRefPtr<AccessibilityObject> AccessibilityObject::create(RenderObject* renderer)
-{
- return adoptRef(new AccessibilityObject(renderer));
-}
-
-void AccessibilityObject::detach()
-{
- removeAXObjectID();
-#ifndef NDEBUG
- if (m_renderer)
- m_renderer->setHasAXObject(false);
-#endif
- m_renderer = 0;
-#if HAVE(ACCESSIBILITY)
- m_wrapper = 0;
-#endif
- clearChildren();
-}
-
-AccessibilityObject* AccessibilityObject::firstChild() const
-{
- if (!m_renderer || !m_renderer->firstChild())
- return 0;
-
- return m_renderer->document()->axObjectCache()->get(m_renderer->firstChild());
-}
-
-AccessibilityObject* AccessibilityObject::lastChild() const
-{
- if (!m_renderer || !m_renderer->lastChild())
- return 0;
-
- return m_renderer->document()->axObjectCache()->get(m_renderer->lastChild());
-}
-
-AccessibilityObject* AccessibilityObject::previousSibling() const
-{
- if (!m_renderer || !m_renderer->previousSibling())
- return 0;
-
- return m_renderer->document()->axObjectCache()->get(m_renderer->previousSibling());
-}
-
-AccessibilityObject* AccessibilityObject::nextSibling() const
-{
- if (!m_renderer || !m_renderer->nextSibling())
- return 0;
-
- return m_renderer->document()->axObjectCache()->get(m_renderer->nextSibling());
-}
-
-AccessibilityObject* AccessibilityObject::parentObject() const
-{
- if (m_areaElement)
- return m_renderer->document()->axObjectCache()->get(m_renderer);
-
- if (!m_renderer || !m_renderer->parent())
- return 0;
-
- return m_renderer->document()->axObjectCache()->get(m_renderer->parent());
-}
-
-AccessibilityObject* AccessibilityObject::parentObjectUnignored() const
-{
- AccessibilityObject* parent;
- for (parent = parentObject(); parent && parent->accessibilityIsIgnored(); parent = parent->parentObject())
- ;
- return parent;
-}
-
-bool AccessibilityObject::isWebArea() const
-{
- return m_renderer->isRenderView();
-}
-
-bool AccessibilityObject::isImageButton() const
-{
- return isImage() && m_renderer->element() && m_renderer->element()->hasTagName(inputTag);
-}
-
-bool AccessibilityObject::isRenderImage() const
-{
- return isImage() && !strcmp(m_renderer->renderName(), "RenderImage");
-}
-
-bool AccessibilityObject::isAnchor() const
-{
- return m_areaElement || (!isImage() && m_renderer->element() && m_renderer->element()->isLink());
-}
-
-bool AccessibilityObject::isTextControl() const
-{
- return m_renderer->isTextField() || m_renderer->isTextArea();
-}
-
-bool AccessibilityObject::isImage() const
-{
- return m_renderer->isImage();
-}
-
-bool AccessibilityObject::isAttachment() const
-{
- // widgets are the replaced elements that we represent to AX as attachments
- bool isWidget = m_renderer && m_renderer->isWidget();
-
- // assert that a widget is a replaced element that is not an image
- ASSERT(!isWidget || (m_renderer->isReplaced() && !isImage()));
- return isWidget;
-}
-
-bool isPasswordFieldElement(Node* node)
-{
- if (!node || !node->hasTagName(inputTag))
- return false;
-
- HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
- return input->inputType() == HTMLInputElement::PASSWORD;
-}
-
-bool AccessibilityObject::isPasswordField() const
-{
- return m_renderer && isPasswordFieldElement(m_renderer->element());
-}
-
-bool AccessibilityObject::isPressed() const
-{
- ASSERT(m_renderer);
- if (roleValue() != ButtonRole)
- return false;
- return m_renderer->node() && m_renderer->node()->active();
-}
-
-bool AccessibilityObject::isIndeterminate() const
-{
- ASSERT(m_renderer);
- return m_renderer->node() && m_renderer->node()->isIndeterminate();
-}
-
-bool AccessibilityObject::isChecked() const
-{
- ASSERT(m_renderer);
- return m_renderer->node() && m_renderer->node()->isChecked();
-}
-
-bool AccessibilityObject::isHovered() const
-{
- ASSERT(m_renderer);
- return m_renderer->node() && m_renderer->node()->hovered();
-}
-
-bool AccessibilityObject::isMultiSelect() const
-{
- ASSERT(m_renderer);
- if (!m_renderer->isListBox())
- return false;
- return m_renderer->element() && static_cast<HTMLSelectElement*>(m_renderer->element())->multiple();
-}
-
-bool AccessibilityObject::isReadOnly() const
-{
- ASSERT(m_renderer);
- return !m_renderer->node() || !m_renderer->node()->isContentEditable();
-}
-
-bool AccessibilityObject::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 AccessibilityObject::headingLevel(Node* node)
-{
- // headings can be in block flow and non-block flow
-
- if (!node)
- return 0;
-
- 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 AccessibilityObject::isHeading() const
-{
- return headingLevel(m_renderer->element()) != 0;
-}
-
-HTMLAnchorElement* AccessibilityObject::anchorElement() const
-{
- // return already-known anchor for image areas
- if (m_areaElement)
- return m_areaElement.get();
-
- // Search up the render tree for a RenderObject with a DOM node. Defer to an earlier continuation, though.
- RenderObject* currRenderer;
- for (currRenderer = m_renderer; currRenderer && !currRenderer->element(); currRenderer = currRenderer->parent()) {
- if (currRenderer->continuation())
- return currRenderer->document()->axObjectCache()->get(currRenderer->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))
- return static_cast<HTMLAnchorElement*>(node);
- }
-
- return 0;
-}
-
-static bool isCheckboxOrRadio(HTMLInputElement* input)
-{
- return (input->inputType() == HTMLInputElement::CHECKBOX ||
- input->inputType() == HTMLInputElement::RADIO);
-}
-
-static bool isCheckboxOrRadio(Node* node)
-{
- if (!node->hasTagName(inputTag))
- return false;
- return isCheckboxOrRadio(static_cast<HTMLInputElement*>(node));
-}
-
-Element* AccessibilityObject::actionElement() const
-{
- if (m_renderer->element() && m_renderer->element()->hasTagName(inputTag)) {
- HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->element());
- if (!input->disabled() && (isCheckboxOrRadio(input) ||
- input->isTextButton()))
- return input;
- }
-
- if (isImageButton())
- return static_cast<Element*>(m_renderer->element());
-
- if (m_renderer->isMenuList())
- return static_cast<RenderMenuList*>(m_renderer)->selectElement();
-
- Element* elt = anchorElement();
- if (!elt)
- elt = mouseButtonListener();
- return elt;
-}
-
-Element* AccessibilityObject::mouseButtonListener() const
-{
- Node* node = m_renderer->element();
- if (!node)
- return 0;
- if (!node->isEventTargetNode())
- return 0;
-
- // FIXME: Do the continuation search like anchorElement does
- for (EventTargetNode* elt = static_cast<EventTargetNode*>(node); elt; elt = static_cast<EventTargetNode*>(elt->parentNode())) {
- if (elt->getHTMLEventListener(clickEvent) || elt->getHTMLEventListener(mousedownEvent) || elt->getHTMLEventListener(mouseupEvent))
- return static_cast<Element*>(elt);
- }
-
- return 0;
-}
-
-String AccessibilityObject::helpText() const
-{
- if (!m_renderer)
- return String();
-
- if (m_areaElement) {
- const AtomicString& summary = m_areaElement->getAttribute(summaryAttr);
- if (!summary.isEmpty())
- return summary;
- const AtomicString& title = m_areaElement->getAttribute(titleAttr);
- if (!title.isEmpty())
- return title;
- }
-
- for (RenderObject* curr = m_renderer; curr; curr = curr->parent()) {
- if (curr->element() && curr->element()->isHTMLElement()) {
- const AtomicString& summary = static_cast<Element*>(curr->element())->getAttribute(summaryAttr);
- if (!summary.isEmpty())
- return summary;
- const AtomicString& title = static_cast<Element*>(curr->element())->getAttribute(titleAttr);
- if (!title.isEmpty())
- return title;
- }
- }
-
- return String();
-}
-
-String AccessibilityObject::textUnderElement() const
-{
- if (!m_renderer)
- return String();
-
- Node* node = m_renderer->element();
- 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 AccessibilityObject::hasIntValue() const
-{
- if (isHeading())
- return true;
-
- if (m_renderer->element() && isCheckboxOrRadio(m_renderer->element()))
- return true;
-
- return false;
-}
-
-int AccessibilityObject::intValue() const
-{
- if (!m_renderer || m_areaElement || isPasswordField())
- return 0;
-
- if (isHeading())
- return headingLevel(m_renderer->element());
-
- Node* node = m_renderer->element();
- if (node && isCheckboxOrRadio(node))
- return static_cast<HTMLInputElement*>(node)->checked();
-
- return 0;
-}
-
-String AccessibilityObject::stringValue() const
-{
- if (!m_renderer || m_areaElement || 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 static_cast<RenderTextControl*>(m_renderer)->text();
-
- // 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();
-}
-
-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;
-}
-
-String AccessibilityObject::title() const
-{
- if (!m_renderer || m_areaElement || !m_renderer->element())
- return String();
-
- if (m_renderer->element()->hasTagName(buttonTag))
- return textUnderElement();
-
- if (m_renderer->element()->hasTagName(inputTag)) {
- HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->element());
- if (input->isTextButton())
- return input->value();
-
- HTMLLabelElement* label = labelForElement(input);
- if (label)
- return label->innerText();
- }
-
- if (m_renderer->element()->isLink() || isHeading())
- return textUnderElement();
-
- return String();
-}
-
-String AccessibilityObject::accessibilityDescription() const
-{
- if (!m_renderer || m_areaElement)
- return String();
-
- if (isImage()) {
- if (m_renderer->element() && m_renderer->element()->isHTMLElement()) {
- const AtomicString& alt = static_cast<HTMLElement*>(m_renderer->element())->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))
- 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);
- }
-
- return String();
-}
-
-IntRect AccessibilityObject::boundingBoxRect() const
-{
- IntRect rect;
- RenderObject* obj = m_renderer;
-
- if (!obj)
- return IntRect();
-
- if (obj->isInlineContinuation())
- obj = obj->element()->renderer();
-
- // FIXME: This doesn't work correctly with transforms.
- Vector<IntRect> rects;
- int x, y;
- obj->absolutePosition(x, y);
- obj->absoluteRects(rects, x, y);
- const size_t n = rects.size();
- for (size_t i = 0; i < n; ++i) {
- IntRect r = rects[i];
- if (!r.isEmpty()) {
- if (obj->style()->hasAppearance())
- theme()->adjustRepaintRect(obj, r);
- rect.unite(r);
- }
- }
- return rect;
-}
-
-IntSize AccessibilityObject::size() const
-{
- IntRect rect = m_areaElement ? m_areaElement->getRect(m_renderer) : boundingBoxRect();
- return rect.size();
-}
-
-// the closest object for an internal anchor
-AccessibilityObject* AccessibilityObject::linkedUIElement() const
-{
- if (!isAnchor())
- return 0;
-
- HTMLAnchorElement* anchor = anchorElement();
- if (!anchor)
- return 0;
-
- KURL linkURL = KURL(anchor->href().deprecatedString());
- String ref = linkURL.ref();
- if (ref.isEmpty())
- return 0;
-
- // check if URL is the same as current URL
- linkURL.setRef("");
- if (m_renderer->document()->url() != linkURL)
- return 0;
-
- Node* linkedNode = m_renderer->document()->getElementById(ref);
- if (!linkedNode) {
- linkedNode = m_renderer->document()->anchors()->namedItem(ref, !m_renderer->document()->inCompatMode());
- 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()->get(linkedNode->renderer());
- while (linkedAXElement && linkedAXElement->accessibilityIsIgnored()) {
- linkedNode = linkedNode->traverseNextNode();
- if (!linkedNode)
- return 0;
- linkedAXElement = m_renderer->document()->axObjectCache()->get(linkedNode->renderer());
- }
-
- return linkedAXElement;
-}
-
-bool AccessibilityObject::accessibilityShouldUseUniqueId() const
-{
- return isWebArea() || isTextControl();
-}
-
-bool AccessibilityObject::accessibilityIsIgnored() const
-{
- // ignore invisible element
- if (!m_renderer || m_renderer->style()->visibility() != VISIBLE)
- return true;
-
- // ignore popup menu items because AppKit does
- for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) {
- if (parent->isMenuList())
- return true;
- }
-
- // NOTE: BRs always have text boxes now, so the text box check here can be removed
- if (m_renderer->isText())
- return m_renderer->isBR() || !static_cast<RenderText*>(m_renderer)->firstTextBox();
-
- if (isHeading())
- return false;
-
- if (m_areaElement || (m_renderer->element() && m_renderer->element()->isLink()))
- return false;
-
- // all controls are accessible
- if (m_renderer->element() && m_renderer->element()->isControl())
- return false;
-
- if (m_renderer->isBlockFlow() && m_renderer->childrenInline())
- return !static_cast<RenderBlock*>(m_renderer)->firstLineBox() && !mouseButtonListener();
-
- // ignore images seemingly used as spacers
- if (isRenderImage()) {
- // informal standard is to ignore images with zero-length alt strings
- Node* node = m_renderer->element();
- if (node && node->isElementNode()) {
- Element* elt = static_cast<Element*>(node);
- const AtomicString& alt = elt->getAttribute(altAttr);
- if (alt.isEmpty() && !alt.isNull())
- return true;
- }
-
- // check for one-dimensional image
- if (m_renderer->height() <= 1 || m_renderer->width() <= 1)
- return true;
-
- return false;
- }
-
- return !m_renderer->isListMarker() && !isWebArea();
-}
-
-bool AccessibilityObject::isLoaded() const
-{
- return !m_renderer->document()->tokenizer();
-}
-
-int AccessibilityObject::layoutCount() const
-{
- if (!m_renderer->isRenderView())
- return 0;
- return static_cast<RenderView*>(m_renderer)->frameView()->layoutCount();
-}
-
-int AccessibilityObject::textLength() const
-{
- ASSERT(isTextControl());
-
- if (isPasswordField())
- return -1; // need to return something distinct from 0
-
- return static_cast<RenderTextControl*>(m_renderer)->text().length();
-}
-
-String AccessibilityObject::selectedText() const
-{
- ASSERT(isTextControl());
-
- if (isPasswordField())
- return String(); // need to return something distinct from empty string
-
- RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer);
- return textControl->text().substring(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart());
-}
-
-const AtomicString& AccessibilityObject::accessKey() const
-{
- Node* node = m_renderer->element();
- if (!node || !node->isElementNode())
- return nullAtom;
- return static_cast<Element*>(node)->getAttribute(accesskeyAttr);
-}
-
-Selection AccessibilityObject::selection() const
-{
- return m_renderer->document()->frame()->selectionController()->selection();
-}
-
-AccessibilityObject::PlainTextRange AccessibilityObject::selectedTextRange() const
-{
- ASSERT(isTextControl());
-
- if (isPasswordField())
- return PlainTextRange();
-
- RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer);
- return PlainTextRange(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart());
-}
-
-void AccessibilityObject::setSelectedText(const String&)
-{
- // TODO: set selected text (ReplaceSelectionCommand). <rdar://problem/4712125>
- notImplemented();
-}
-
-void AccessibilityObject::setSelectedTextRange(const PlainTextRange& range)
-{
- RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer);
- textControl->setSelectionRange(range.start, range.start + range.length);
-}
-
-void AccessibilityObject::makeRangeVisible(const PlainTextRange&)
-{
- // TODO: make range visible (scrollRectToVisible). <rdar://problem/4712101>
- notImplemented();
-}
-
-KURL AccessibilityObject::url() const
-{
- if (isAnchor()) {
- if (HTMLAnchorElement* anchor = anchorElement())
- return KURL(anchor->href().deprecatedString());
- }
- if (isImage() && m_renderer->element() && m_renderer->element()->hasTagName(imgTag))
- return KURL(static_cast<HTMLImageElement*>(m_renderer->element())->src().deprecatedString());
-
- return KURL();
-}
-
-bool AccessibilityObject::isVisited() const
-{
- return m_renderer->style()->pseudoState() == PseudoVisited;
-}
-
-bool AccessibilityObject::isFocused() const
-{
- return (m_renderer->element() && m_renderer->document()->focusedNode() == m_renderer->element());
-}
-
-void AccessibilityObject::setFocused(bool on)
-{
- if (!canSetFocusAttribute())
- return;
-
- if (!on)
- m_renderer->document()->setFocusedNode(0);
- else {
- if (m_renderer->element()->isElementNode())
- static_cast<Element*>(m_renderer->element())->focus();
- else
- m_renderer->document()->setFocusedNode(m_renderer->element());
- }
-}
-
-void AccessibilityObject::setValue(const String& string)
-{
- if (m_renderer->isTextField()) {
- HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->element());
- input->setValue(string);
- } else if (m_renderer->isTextArea()) {
- HTMLTextAreaElement* textArea = static_cast<HTMLTextAreaElement*>(m_renderer->element());
- textArea->setValue(string);
- }
-}
-
-bool AccessibilityObject::isEnabled() const
-{
- return m_renderer->element() ? m_renderer->element()->isEnabled() : true;
-}
-
-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;
-}
-
-RenderObject* AccessibilityObject::topRenderer() const
-{
- return m_renderer->document()->topDocument()->renderer();
-}
-
-RenderTextControl* AccessibilityObject::textControl() const
-{
- if (!isTextControl())
- return 0;
-
- return static_cast<RenderTextControl*>(m_renderer);
-}
-
-Widget* AccessibilityObject::widget() const
-{
- if (!m_renderer->isWidget())
- return 0;
-
- return static_cast<RenderWidget*>(m_renderer)->widget();
-}
-
-AXObjectCache* AccessibilityObject::axObjectCache() const
-{
- return m_renderer->document()->axObjectCache();
-}
-
-void AccessibilityObject::getDocumentLinks(Vector<RefPtr<AccessibilityObject> >& result) const
-{
- 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()->get(obj);
- ASSERT(axobj);
- ASSERT(axobj->roleValue() == WebCoreLinkRole);
- if (!axobj->accessibilityIsIgnored())
- result.append(axobj);
- }
- curr = coll->nextItem();
- }
-}
-
-FrameView* AccessibilityObject::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* AccessibilityObject::widgetForAttachmentView() const
-{
- if (!isAttachment())
- return 0;
- return static_cast<RenderWidget*>(m_renderer)->widget();
-}
-
-FrameView* AccessibilityObject::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 AccessibilityObject::visiblePositionRange() const
-{
- if (!m_renderer)
- return VisiblePositionRange();
-
- // construct VisiblePositions for start and end
- Node* node = m_renderer->element();
- VisiblePosition startPos = VisiblePosition(node, 0, VP_DEFAULT_AFFINITY);
- VisiblePosition endPos = VisiblePosition(node, maxDeepOffset(node), VP_DEFAULT_AFFINITY);
-
- // the VisiblePositions are equal for nodes like buttons, so adjust for that
- if (startPos == endPos) {
- endPos = endPos.next();
- if (endPos.isNull())
- endPos = startPos;
- }
-
- return VisiblePositionRange(startPos, endPos);
-}
-
-VisiblePositionRange AccessibilityObject::doAXTextMarkerRangeForLine(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 selectionController;
- selectionController.setSelection(Selection(visiblePos));
- selectionController.modify(SelectionController::EXTEND, SelectionController::RIGHT, LineBoundary);
-
- return VisiblePositionRange(selectionController.selection().visibleStart(), selectionController.selection().visibleEnd());
-}
-
-VisiblePositionRange AccessibilityObject::doAXTextMarkerRangeForUnorderedTextMarkers(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 = Selection(visiblePos1, visiblePos2).isBaseFirst();
-
- if (alreadyInOrder) {
- startPos = visiblePos1;
- endPos = visiblePos2;
- } else {
- startPos = visiblePos2;
- endPos = visiblePos1;
- }
-
- return VisiblePositionRange(startPos, endPos);
-}
-
-VisiblePositionRange AccessibilityObject::doAXLeftWordTextMarkerRangeForTextMarker(const VisiblePosition& visiblePos) const
-{
- VisiblePosition startPosition = startOfWord(visiblePos, LeftWordIfOnBoundary);
- VisiblePosition endPosition = endOfWord(startPosition);
- return VisiblePositionRange(startPosition, endPosition);
-}
-
-VisiblePositionRange AccessibilityObject::doAXRightWordTextMarkerRangeForTextMarker(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->inlineBox(p.offset(), tempPosition.affinity()) || (renderer->isRenderBlock() && p.offset() == 0))
- break;
- startPosition = tempPosition;
- }
-
- return startPosition;
-}
-
-VisiblePositionRange AccessibilityObject::doAXLeftLineTextMarkerRangeForTextMarker(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 textmarker 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::doAXRightLineTextMarkerRangeForTextMarker(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 textmarker 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::doAXSentenceTextMarkerRangeForTextMarker(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::doAXParagraphTextMarkerRangeForTextMarker(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 VisiblePosition(endRenderer->node(), maxDeepOffset(endRenderer->node()), VP_DEFAULT_AFFINITY);
-}
-
-VisiblePositionRange AccessibilityObject::doAXStyleTextMarkerRangeForTextMarker(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::textMarkerRangeForRange(const PlainTextRange& range, RenderTextControl* textControl) const
-{
- if (range.start + range.length > textControl->text().length())
- return VisiblePositionRange();
-
- VisiblePosition startPosition = textControl->visiblePositionForIndex(range.start);
- startPosition.setAffinity(DOWNSTREAM);
- VisiblePosition endPosition = textControl->visiblePositionForIndex(range.start + range.length);
- return VisiblePositionRange(startPosition, endPosition);
-}
-
-static String stringForReplacedNode(Node* replacedNode)
-{
- // we should always be given a rendered node and a replaced node, but be safe
- // replaced nodes are either attachments (widgets) or images
- if (!replacedNode || !replacedNode->renderer() || !replacedNode->renderer()->isReplaced() || replacedNode->isTextNode()) {
- ASSERT_NOT_REACHED();
- return String();
- }
-
- // create an AX object, but skip it if it is not supposed to be seen
- AccessibilityObject* object = replacedNode->renderer()->document()->axObjectCache()->get(replacedNode->renderer());
- if (object->accessibilityIsIgnored())
- return String();
-
- return String(&objectReplacementCharacter, 1);
-}
-
-String AccessibilityObject::doAXStringForTextMarkerRange(const VisiblePositionRange& visiblePositionRange) const
-{
- if (visiblePositionRange.isNull())
- return String();
-
- String resultString;
- TextIterator it(makeRange(visiblePositionRange.start, visiblePositionRange.end).get());
- while (!it.atEnd()) {
- // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
- if (it.length() != 0) {
- resultString.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);
- String attachmentString = stringForReplacedNode(node->childNode(offset));
-
- // append the replacement string
- if (!attachmentString.isNull())
- resultString.append(attachmentString);
- }
- it.advance();
- }
-
- return resultString.isEmpty() ? String() : resultString;
-}
-
-IntRect AccessibilityObject::doAXBoundsForTextMarkerRange(const VisiblePositionRange& visiblePositionRange) const
-{
- if (visiblePositionRange.isNull())
- return IntRect();
-
- // Create a mutable VisiblePositionRange.
- VisiblePositionRange range(visiblePositionRange);
- IntRect rect1 = range.start.caretRect();
- IntRect rect2 = range.end.caretRect();
-
- // 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.caretRect();
- }
- if (range.end == endOfFirstLine) {
- range.end.setAffinity(UPSTREAM);
- rect2 = range.end.caretRect();
- }
- }
-
- 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;
- }
-
- return ourrect;
-
-}
-
-int AccessibilityObject::doAXLengthForTextMarkerRange(const VisiblePositionRange& visiblePositionRange) const
-{
- // FIXME: Multi-byte support
- String string = doAXStringForTextMarkerRange(visiblePositionRange);
- if (string.isEmpty())
- return -1;
-
- return string.length();
-}
-
-void AccessibilityObject::doSetAXSelectedTextMarkerRange(const VisiblePositionRange& textMarkerRange) const
-{
- if (textMarkerRange.start.isNull() || textMarkerRange.end.isNull())
- return;
-
- // make selection and tell the document to use it
- Selection newSelection = Selection(textMarkerRange.start, textMarkerRange.end);
- m_renderer->document()->frame()->selectionController()->setSelection(newSelection);
-}
-
-VisiblePosition AccessibilityObject::doAXTextMarkerForPosition(const IntPoint& point) const
-{
- // convert absolute point to view coordinates
- FrameView* frameView = m_renderer->document()->topDocument()->renderer()->view()->frameView();
- RenderObject* renderer = topRenderer();
- Node* innerNode = 0;
-
- // locate the node containing the point
- IntPoint pointResult;
- while (1) {
- IntPoint ourpoint;
- ourpoint = point;
-
- HitTestRequest request(true, true);
- HitTestResult result(ourpoint);
- renderer->layer()->hitTest(request, result);
- innerNode = result.innerNode();
- if (!innerNode || !innerNode->renderer())
- return VisiblePosition();
-
- pointResult = result.localPoint();
-
- // done if hit something other than a widget
- 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;
- Document* document = frame->document();
- if (!document)
- break;
- renderer = document->renderer();
- frameView = static_cast<FrameView*>(widget);
- }
-
- return innerNode->renderer()->positionForPoint(pointResult);
-}
-
-VisiblePosition AccessibilityObject::doAXNextTextMarkerForTextMarker(const VisiblePosition& visiblePos) const
-{
- return visiblePos.next();
-}
-
-VisiblePosition AccessibilityObject::doAXPreviousTextMarkerForTextMarker(const VisiblePosition& visiblePos) const
-{
- return visiblePos.previous();
-}
-
-VisiblePosition AccessibilityObject::doAXNextWordEndTextMarkerForTextMarker(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::doAXPreviousWordStartTextMarkerForTextMarker(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::doAXNextLineEndTextMarkerForTextMarker(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::doAXPreviousLineStartTextMarkerForTextMarker(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::doAXNextSentenceEndTextMarkerForTextMarker(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(visiblePos), endOfLine(visiblePos)).get());
- if (lineString.isEmpty())
- endPosition = nextVisiblePos;
- else
- endPosition = endOfSentence(nextVisiblePos);
-
- return endPosition;
-}
-
-VisiblePosition AccessibilityObject::doAXPreviousSentenceStartTextMarkerForTextMarker(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::doAXNextParagraphEndTextMarkerForTextMarker(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::doAXPreviousParagraphStartTextMarkerForTextMarker(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::textMarkerForIndex(unsigned indexValue, bool lastIndexOK) const
-{
- if (!isTextControl())
- return VisiblePosition();
-
- RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer);
-
- // lastIndexOK specifies whether the position after the last character is acceptable
- if (indexValue >= textControl->text().length()) {
- if (!lastIndexOK || indexValue > textControl->text().length())
- return VisiblePosition();
- }
- VisiblePosition position = textControl->visiblePositionForIndex(indexValue);
- position.setAffinity(DOWNSTREAM);
- return position;
-}
-
-AccessibilityObject* AccessibilityObject::doAXUIElementForTextMarker(const VisiblePosition& visiblePos) const
-{
- if (visiblePos.isNull())
- return 0;
-
- RenderObject* obj = visiblePos.deepEquivalent().node()->renderer();
- if (!obj)
- return 0;
-
- return obj->document()->axObjectCache()->get(obj);
-}
-
-unsigned AccessibilityObject::doAXLineForTextMarker(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 += 1;
- savedVisiblePos = currentVisiblePos;
- VisiblePosition prevVisiblePos = previousLinePosition(visiblePos, 0);
- currentVisiblePos = prevVisiblePos;
- }
-
- return lineCount - 1;
-}
-
-// NOTE: Consider providing this utility method as AX API
-AccessibilityObject::PlainTextRange AccessibilityObject::rangeForTextMarkerRange(const VisiblePositionRange& positionRange) const
-{
- int index1 = indexForTextMarker(positionRange.start);
- int index2 = indexForTextMarker(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::indexForTextMarker(const VisiblePosition& position) const
-{
- if (!isTextControl())
- return -1;
-
- Node* node = position.deepEquivalent().node();
- if (!node)
- return -1;
-
- RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer);
- for (RenderObject* renderer = node->renderer(); renderer && renderer->element(); renderer = renderer->parent()) {
- if (renderer == textControl)
- return textControl->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.
-AccessibilityObject::PlainTextRange AccessibilityObject::doAXRangeForLine(unsigned lineNumber) const
-{
- if (!isTextControl())
- return PlainTextRange();
-
- // iterate to the specified line
- RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer);
- VisiblePosition visiblePos = textControl->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 selectionController.modify because it returns false when
- // starting at an empty line. The resulting selection in that case
- // will be a caret at visiblePos.
- SelectionController selectionController;
- selectionController.setSelection(Selection(visiblePos));
- selectionController.modify(SelectionController::EXTEND, SelectionController::LEFT, LineBoundary);
- selectionController.modify(SelectionController::EXTEND, SelectionController::RIGHT, LineBoundary);
-
- // calculate the indices for the selection start and end
- VisiblePosition startPosition = selectionController.selection().visibleStart();
- VisiblePosition endPosition = selectionController.selection().visibleEnd();
- int index1 = textControl->indexForVisiblePosition(startPosition);
- int index2 = textControl->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 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?
-AccessibilityObject::PlainTextRange AccessibilityObject::doAXRangeForPosition(const IntPoint& point) const
-{
- int index = indexForTextMarker(doAXTextMarkerForPosition(point));
- if (index < 0)
- return PlainTextRange();
-
- return PlainTextRange(index, 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.
-AccessibilityObject::PlainTextRange AccessibilityObject::doAXRangeForIndex(unsigned index) const
-{
- if (!isTextControl())
- return PlainTextRange();
-
- RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer);
- String text = textControl->text();
- if (!text.length() || index > text.length() - 1)
- return PlainTextRange();
-
- return PlainTextRange(index, 1);
-}
-
-// Given a character index, the range of text associated with this accessibility object
-// over which the style in effect at that character index applies.
-AccessibilityObject::PlainTextRange AccessibilityObject::doAXStyleRangeForIndex(unsigned index) const
-{
- VisiblePositionRange textMarkerRange = doAXStyleTextMarkerRangeForTextMarker(textMarkerForIndex(index, false));
- return rangeForTextMarkerRange(textMarkerRange);
-}
-
-// A substring of the text associated with this accessibility object that is
-// specified by the given character range.
-String AccessibilityObject::doAXStringForRange(const PlainTextRange& range) const
-{
- if (isPasswordField())
- return String();
-
- if (range.length == 0)
- return "";
-
- if (!isTextControl())
- return String();
-
- RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer);
- String text = textControl->text();
- if (range.start + range.length > text.length())
- return String();
-
- return text.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 AccessibilityObject::doAXBoundsForRange(const PlainTextRange& range) const
-{
- if (isTextControl())
- return doAXBoundsForTextMarkerRange(textMarkerRangeForRange(range, static_cast<RenderTextControl*>(m_renderer)));
- 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 doAXLineForTextMarker(textMarkerForIndex(index, false));
-}
-
-AccessibilityObject* AccessibilityObject::doAccessibilityHitTest(const IntPoint& point) const
-{
- RenderLayer* layer = m_renderer->layer();
- if (!layer)
- return 0;
-
- HitTestRequest request(true, true);
- HitTestResult hitTestResult = HitTestResult(point);
- layer->hitTest(request, hitTestResult);
- if (!hitTestResult.innerNode())
- return 0;
- Node* node = hitTestResult.innerNode()->shadowAncestorNode();
- RenderObject* obj = node->renderer();
- if (!obj)
- return 0;
-
- AccessibilityObject* result = obj->document()->axObjectCache()->get(obj);
- if (result->accessibilityIsIgnored())
- result = result->parentObjectUnignored();
-
- return result;
-}
-
-AccessibilityObject* AccessibilityObject::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->renderer())
- focusedNode = focusedDocument;
-
- AccessibilityObject* obj = focusedNode->renderer()->document()->axObjectCache()->get(focusedNode->renderer());
-
- // the HTML element, for example, is focusable but has an AX object that is ignored
- if (obj->accessibilityIsIgnored())
- obj = obj->parentObjectUnignored();
-
- return obj;
-}
-
-AccessibilityObject* AccessibilityObject::observableObject() const
-{
- for (RenderObject* renderer = m_renderer; renderer && renderer->element(); renderer = renderer->parent()) {
- if (renderer->isTextField() || renderer->isTextArea())
- return renderer->document()->axObjectCache()->get(renderer);
- }
-
- return 0;
-}
-
-AccessibilityRole AccessibilityObject::roleValue() const
-{
- if (!m_renderer)
- return UnknownRole;
-
- if (m_areaElement)
- return WebCoreLinkRole;
- if (m_renderer->element() && m_renderer->element()->isLink()) {
- if (isImage())
- return ImageMapRole;
- return WebCoreLinkRole;
- }
- if (m_renderer->isListMarker())
- return ListMarkerRole;
- if (m_renderer->element() && m_renderer->element()->hasTagName(buttonTag))
- return ButtonRole;
- if (m_renderer->isText())
- return StaticTextRole;
- if (isImage()) {
- if (isImageButton())
- return ButtonRole;
- return ImageRole;
- }
- if (isWebArea())
- return WebAreaRole;
-
- if (m_renderer->isTextField())
- return TextFieldRole;
-
- if (m_renderer->isTextArea())
- return TextAreaRole;
-
- if (m_renderer->element() && m_renderer->element()->hasTagName(inputTag)) {
- HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->element());
- if (input->inputType() == HTMLInputElement::CHECKBOX)
- return CheckBoxRole;
- if (input->inputType() == HTMLInputElement::RADIO)
- return RadioButtonRole;
- if (input->isTextButton())
- return ButtonRole;
- }
-
- if (m_renderer->isMenuList())
- return PopUpButtonRole;
-
- if (isHeading())
- return HeadingRole;
-
- if (m_renderer->isBlockFlow())
- return GroupRole;
-
- return UnknownRole;
-}
-
-bool AccessibilityObject::canSetFocusAttribute() const
-{
- // 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 (!m_renderer->element() || !m_renderer->element()->isEnabled())
- return false;
-
- switch (roleValue()) {
- case WebCoreLinkRole:
- case TextFieldRole:
- case TextAreaRole:
- case ButtonRole:
- case PopUpButtonRole:
- case CheckBoxRole:
- case RadioButtonRole:
- return true;
- case UnknownRole:
- case SliderRole:
- case TabGroupRole:
- case StaticTextRole:
- case ScrollAreaRole:
- case MenuButtonRole:
- case TableRole:
- case ApplicationRole:
- case GroupRole:
- case RadioGroupRole:
- case ListRole:
- case ScrollBarRole:
- case ValueIndicatorRole:
- case ImageRole:
- case MenuBarRole:
- case MenuRole:
- case MenuItemRole:
- case ColumnRole:
- case RowRole:
- case ToolbarRole:
- case BusyIndicatorRole:
- case ProgressIndicatorRole:
- case WindowRole:
- case DrawerRole:
- case SystemWideRole:
- case OutlineRole:
- case IncrementorRole:
- case BrowserRole:
- case ComboBoxRole:
- case SplitGroupRole:
- case SplitterRole:
- case ColorWellRole:
- case GrowAreaRole:
- case SheetRole:
- case HelpTagRole:
- case MatteRole:
- case RulerRole:
- case RulerMarkerRole:
- case LinkRole:
- case DisclosureTriangleRole:
- case GridRole:
- case ImageMapRole:
- case ListMarkerRole:
- case WebAreaRole:
- case HeadingRole:
- return false;
- }
- ASSERT_NOT_REACHED();
- return false;
-}
-
-bool AccessibilityObject::canSetValueAttribute() const
-{
- return isTextControl();
-}
-
-bool AccessibilityObject::canSetTextRangeAttributes() const
-{
- return isTextControl();
-}
-
-void AccessibilityObject::childrenChanged()
-{
- clearChildren();
-
- if (accessibilityIsIgnored())
- parentObject()->childrenChanged();
-}
-
-void AccessibilityObject::clearChildren()
-{
- m_haveChildren = false;
- m_children.clear();
-}
-
-const Vector<RefPtr<AccessibilityObject> >& AccessibilityObject::children() const
-{
- if (!m_haveChildren)
- addChildren();
- return m_children;
-}
-
-void AccessibilityObject::addChildren() const
-{
- // 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;
-
- // add all unignored acc children
- for (RefPtr<AccessibilityObject> obj = firstChild(); obj; obj = obj->nextSibling()) {
- if (obj->accessibilityIsIgnored()) {
- obj->addChildren();
- Vector<RefPtr<AccessibilityObject> >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 (isRenderImage() && !m_areaElement) {
- HTMLMapElement* map = static_cast<RenderImage*>(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
- // NOTE: can't cache these because they all have the same renderer, which is the cache key, right?
- // plus there may be little reason to since they are being added to the handy array
- if (current->isLink()) {
- RefPtr<AccessibilityObject> obj = new AccessibilityObject(m_renderer);
- obj->m_areaElement = static_cast<HTMLAreaElement*>(current);
- m_children.append(obj);
- }
- }
- }
- }
-}
-
-unsigned AccessibilityObject::axObjectID() const
-{
- return m_id;
-}
-
-void AccessibilityObject::setAXObjectID(unsigned axObjectID)
-{
- m_id = axObjectID;
-}
-
-void AccessibilityObject::removeAXObjectID()
-{
- if (!m_id)
- return;
-#if PLATFORM(MAC)
- m_renderer->document()->axObjectCache()->removeAXID(this);
-#endif
-}
-
-const String& AccessibilityObject::actionVerb() const
-{
- // FIXME: Need to add verbs for select elements.
- static const String buttonAction = AXButtonActionVerb();
- static const String textFieldAction = AXTextFieldActionVerb();
- static const String radioButtonAction = AXRadioButtonActionVerb();
- static const String checkedCheckBoxAction = AXCheckedCheckBoxActionVerb();
- static const String uncheckedCheckBoxAction = AXUncheckedCheckBoxActionVerb();
- static const String linkAction = AXLinkActionVerb();
- static 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;
- }
-}
-
-} // namespace WebCore