Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1672)

Unified Diff: Source/modules/accessibility/AXLayoutObject.cpp

Issue 1185343003: Implements the ability to get and set the caret position and the current selection from anywhere in… (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Moved layout tests to another CL. Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « Source/modules/accessibility/AXLayoutObject.h ('k') | Source/modules/accessibility/AXObject.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: Source/modules/accessibility/AXLayoutObject.cpp
diff --git a/Source/modules/accessibility/AXLayoutObject.cpp b/Source/modules/accessibility/AXLayoutObject.cpp
index 035cedc08aba8d7f9a781657f0474b85ed540ae9..48152e2d3f4f80428b67d9fe05bfb7e130b81737 100644
--- a/Source/modules/accessibility/AXLayoutObject.cpp
+++ b/Source/modules/accessibility/AXLayoutObject.cpp
@@ -33,6 +33,7 @@
#include "core/CSSPropertyNames.h"
#include "core/InputTypeNames.h"
#include "core/dom/ElementTraversal.h"
+#include "core/dom/Range.h"
#include "core/dom/shadow/ShadowRoot.h"
#include "core/editing/FrameSelection.h"
#include "core/editing/RenderedPosition.h"
@@ -57,6 +58,7 @@
#include "core/layout/LayoutListMarker.h"
#include "core/layout/LayoutMenuList.h"
#include "core/layout/LayoutPart.h"
+#include "core/layout/LayoutTextControl.h"
#include "core/layout/LayoutTextControlSingleLine.h"
#include "core/layout/LayoutTextFragment.h"
#include "core/layout/LayoutView.h"
@@ -1834,46 +1836,211 @@ Widget* AXLayoutObject::widgetForAttachmentView() const
}
//
-// Selected text.
+// Functions that retrieve the current selection.
//
-AXObject::PlainTextRange AXLayoutObject::selectedTextRange() const
+AXObject::AXRange AXLayoutObject::selection() const
{
- if (!isTextControl())
- return PlainTextRange();
+ AXRange textSelection = textControlSelection();
+ if (textSelection.isValid())
+ return textSelection;
+
+ if (!layoutObject() || !layoutObject()->frame())
+ return AXRange();
+
+ VisibleSelection selection = layoutObject()->frame()->selection().selection();
+ RefPtrWillBeRawPtr<Range> selectionRange = selection.firstRange();
+ if (!selectionRange)
+ return AXRange();
+
+ int anchorOffset = selectionRange->startOffset();
+ ASSERT(anchorOffset >= 0);
+ int focusOffset = selectionRange->endOffset();
+ ASSERT(focusOffset >= 0);
+
+ Node* anchorNode = selectionRange->startContainer();
+ ASSERT(anchorNode);
- if (m_layoutObject->isTextControl()) {
- HTMLTextFormControlElement* textControl = toLayoutTextControl(m_layoutObject)->textFormControlElement();
- return PlainTextRange(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart());
+ RefPtrWillBeRawPtr<AXObject> anchorObject = nullptr;
+ // Find the closest node that has a corresponding AXObject.
+ // This is because some nodes may be aria hidden or might not even have
+ // a layout object if they are part of the shadow DOM.
+ while (anchorNode && !(anchorObject = axObjectCache().getOrCreate(anchorNode))
+ && (!anchorObject->isAXLayoutObject() || !anchorObject->node() || anchorObject->accessibilityIsIgnored())) {
+ if (anchorNode->nextSibling())
+ anchorNode = anchorNode->nextSibling();
+ else
+ anchorNode = anchorNode->parentNode();
}
+ if (anchorNode != selectionRange->startContainer())
+ anchorOffset = 0;
- return visibleSelectionUnderObject();
+ Node* focusNode = selectionRange->endContainer();
+ ASSERT(focusNode);
+
+ RefPtrWillBeRawPtr<AXObject> focusObject = nullptr;
+ while (focusNode && !(focusObject = axObjectCache().getOrCreate(focusNode))
+ && (!focusObject->isAXLayoutObject() || !focusObject->node() || focusObject->accessibilityIsIgnored())) {
+ if (focusNode->previousSibling())
+ focusNode = focusNode->previousSibling();
+ else
+ focusNode = focusNode->parentNode();
+ }
+ if (focusNode != selectionRange->endContainer())
+ focusOffset = 0;
+
+ if (!anchorObject || !focusObject)
+ return AXRange();
+
+ return AXRange(
+ anchorObject, anchorOffset,
+ focusObject, focusOffset);
}
-VisibleSelection AXLayoutObject::selection() const
+// Gets only the start and end offsets of the selection computed using the
+// current object as the starting point. Returns a null selection if there is
+// no selection in the subtree rooted at this object.
+AXObject::AXRange AXLayoutObject::selectionUnderObject() const
{
- return m_layoutObject->frame()->selection().selection();
+ AXRange textSelection = textControlSelection();
+ if (textSelection.isValid())
+ return textSelection;
+
+ if (!layoutObject() || !layoutObject()->frame())
+ return AXRange();
+
+ VisibleSelection selection = layoutObject()->frame()->selection().selection();
+ RefPtrWillBeRawPtr<Range> selectionRange = selection.firstRange();
+ ContainerNode* parentNode = node()->parentNode();
+ int nodeIndex = node()->nodeIndex();
+ if (!selectionRange
+ // Selection is contained in node.
+ || !(parentNode
+ && selectionRange->comparePoint(parentNode, nodeIndex, IGNORE_EXCEPTION) < 0
+ && selectionRange->comparePoint(parentNode, nodeIndex + 1, IGNORE_EXCEPTION) > 0)) {
+ return AXRange();
+ }
+
+ int start = indexForVisiblePosition(selection.visibleStart());
+ ASSERT(start >= 0);
+ int end = indexForVisiblePosition(selection.visibleEnd());
+ ASSERT(end >= 0);
+
+ return AXRange(start, end);
+}
+
+AXObject::AXRange AXLayoutObject::textControlSelection() const
+{
+ if (!layoutObject())
+ return AXRange();
+
+ LayoutObject* layout = nullptr;
+ if (layoutObject()->isTextControl()) {
+ layout = layoutObject();
+ } else {
+ Element* focusedElement = document()->focusedElement();
+ if (focusedElement && focusedElement->layoutObject()
+ && focusedElement->layoutObject()->isTextControl())
+ layout = focusedElement->layoutObject();
+ }
+
+ if (!layout)
+ return AXRange();
+
+ AXObject* axObject = axObjectCache().getOrCreate(layout);
+ if (!axObject || !axObject->isAXLayoutObject())
+ return AXRange();
+
+ HTMLTextFormControlElement* textControl = toLayoutTextControl(
+ layout)->textFormControlElement();
+ ASSERT(textControl);
+ int start = textControl->selectionStart();
+ int end = textControl->selectionEnd();
+ return AXRange(axObject, start, axObject, end);
+}
+
+int AXLayoutObject::indexForVisiblePosition(const VisiblePosition& position) const
+{
+ if (layoutObject() && layoutObject()->isTextControl()) {
+ HTMLTextFormControlElement* textControl = toLayoutTextControl(
+ layoutObject())->textFormControlElement();
+ return textControl->indexForVisiblePosition(position);
+ }
+
+ if (!node())
+ return 0;
+
+ Position indexPosition = position.deepEquivalent();
+ if (indexPosition.isNull())
+ return 0;
+
+ RefPtrWillBeRawPtr<Range> range = Range::create(*document());
+ range->setStart(node(), 0, IGNORE_EXCEPTION);
+ range->setEnd(indexPosition, IGNORE_EXCEPTION);
+
+ return TextIterator::rangeLength(range->startPosition(), range->endPosition());
}
//
// Modify or take an action on an object.
//
-void AXLayoutObject::setSelectedTextRange(const PlainTextRange& range)
+void AXLayoutObject::setSelection(const AXRange& selection)
{
- if (m_layoutObject->isTextControl()) {
- HTMLTextFormControlElement* textControl = toLayoutTextControl(m_layoutObject)->textFormControlElement();
- textControl->setSelectionRange(range.start, range.start + range.length, SelectionHasNoDirection, NotDispatchSelectEvent);
+ if (!layoutObject() || !selection.isValid())
+ return;
+
+ if (selection.anchorObject && !isValidSelectionBound(selection.anchorObject.get()))
+ return;
+
+ if (selection.focusObject && !isValidSelectionBound(selection.focusObject.get()))
+ return;
+
+ AXObject* anchorObject = selection.anchorObject ?
+ selection.anchorObject.get() : this;
+ AXObject* focusObject = selection.focusObject ?
+ selection.focusObject.get() : this;
+
+ if (anchorObject == this && anchorObject == focusObject
+ && layoutObject()->isTextControl()) {
+ HTMLTextFormControlElement* textControl = toLayoutTextControl(
+ layoutObject())->textFormControlElement();
+ textControl->setSelectionRange(selection.anchorOffset, selection.focusOffset,
+ SelectionHasNoDirection, NotDispatchSelectEvent);
return;
}
- Document& document = m_layoutObject->document();
- LocalFrame* frame = document.frame();
+ Node* anchorNode = nullptr;
+ while (anchorObject && !anchorNode) {
+ anchorNode = anchorObject->node();
+ anchorObject = anchorObject->parentObject();
+ }
+
+ Node* focusNode = nullptr;
+ while (focusObject && !focusNode) {
+ focusNode = focusObject->node();
+ focusObject = focusObject->parentObject();
+ }
+
+ if (!anchorNode || !focusNode)
+ return;
+
+ LocalFrame* frame = layoutObject()->frame();
if (!frame)
return;
- Node* node = m_layoutObject->node();
- frame->selection().setSelection(VisibleSelection(Position(node, range.start),
- Position(node, range.start + range.length), DOWNSTREAM));
+
+ frame->selection().setSelection(VisibleSelection(
+ Position(anchorNode, selection.anchorOffset),
+ Position(focusNode, selection.focusOffset),
+ DOWNSTREAM));
+}
+
+bool AXLayoutObject::isValidSelectionBound(const AXObject* boundObject) const
+{
+ return boundObject && !boundObject->isDetached()
+ && boundObject->isAXLayoutObject()
+ && boundObject->layoutObject()->frame() == layoutObject()->frame()
+ && &boundObject->axObjectCache() == &axObjectCache();
}
void AXLayoutObject::setValue(const String& string)
@@ -2008,33 +2175,6 @@ VisiblePosition AXLayoutObject::visiblePositionForIndex(int index) const
return VisiblePosition(Position(it.currentContainer(), it.endOffset()), UPSTREAM);
}
-int AXLayoutObject::indexForVisiblePosition(const VisiblePosition& pos) const
-{
- if (m_layoutObject->isTextControl()) {
- HTMLTextFormControlElement* textControl = toLayoutTextControl(m_layoutObject)->textFormControlElement();
- return textControl->indexForVisiblePosition(pos);
- }
-
- if (!isTextControl())
- return 0;
-
- Node* node = m_layoutObject->node();
- if (!node)
- return 0;
-
- Position indexPosition = pos.deepEquivalent();
- if (indexPosition.isNull()
- || (highestEditableRoot(indexPosition) != node
- && highestEditableRoot(indexPosition, HasEditableAXRole) != node))
- return 0;
-
- RefPtrWillBeRawPtr<Range> range = Range::create(m_layoutObject->document());
- range->setStart(node, 0, IGNORE_EXCEPTION);
- range->setEnd(indexPosition, IGNORE_EXCEPTION);
-
- return TextIterator::rangeLength(range->startPosition(), range->endPosition());
-}
-
void AXLayoutObject::addInlineTextBoxChildren(bool force)
{
Settings* settings = document()->settings();
@@ -2116,23 +2256,6 @@ void AXLayoutObject::ariaListboxSelectedChildren(AccessibilityChildrenVector& re
}
}
-AXObject::PlainTextRange AXLayoutObject::visibleSelectionUnderObject() const
-{
- Node* node = m_layoutObject->node();
- if (!node)
- return PlainTextRange();
-
- VisibleSelection visibleSelection = selection();
- RefPtrWillBeRawPtr<Range> currentSelectionRange = visibleSelection.toNormalizedRange();
- if (!currentSelectionRange || !currentSelectionRange->intersectsNode(node, IGNORE_EXCEPTION))
- return PlainTextRange();
-
- int start = indexForVisiblePosition(visibleSelection.visibleStart());
- int end = indexForVisiblePosition(visibleSelection.visibleEnd());
-
- return PlainTextRange(start, end - start);
-}
-
bool AXLayoutObject::nodeIsTextControl(const Node* node) const
{
if (!node)
« no previous file with comments | « Source/modules/accessibility/AXLayoutObject.h ('k') | Source/modules/accessibility/AXObject.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698