| /* |
| * Copyright (C) 2013 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. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR |
| * 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 "AccessibleTextImpl.h" |
| |
| #include "WebKitDLL.h" |
| #include "WebView.h" |
| |
| #include <WebCore/Document.h> |
| #include <WebCore/Editing.h> |
| #include <WebCore/Editor.h> |
| #include <WebCore/Frame.h> |
| #include <WebCore/FrameSelection.h> |
| #include <WebCore/GeometryUtilities.h> |
| #include <WebCore/HTMLTextFormControlElement.h> |
| #include <WebCore/Node.h> |
| #include <WebCore/Position.h> |
| #include <WebCore/Range.h> |
| #include <WebCore/RenderTextControl.h> |
| #include <WebCore/VisibleSelection.h> |
| #include <WebCore/VisibleUnits.h> |
| |
| using namespace WebCore; |
| |
| AccessibleText::AccessibleText(WebCore::AccessibilityObject* obj, HWND window) |
| : AccessibleBase(obj, window) |
| { |
| ASSERT_ARG(obj, obj->isStaticText() || obj->isTextControl() || (obj->node() && obj->node()->isTextNode())); |
| ASSERT_ARG(obj, obj->isAccessibilityRenderObject()); |
| } |
| |
| // IAccessibleText |
| HRESULT AccessibleText::addSelection(long startOffset, long endOffset) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| startOffset = convertSpecialOffset(startOffset); |
| endOffset = convertSpecialOffset(endOffset); |
| |
| m_object->setSelectedTextRange(PlainTextRange(startOffset, endOffset-startOffset)); |
| |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::get_attributes(long offset, long* startOffset, long* endOffset, BSTR* textAttributes) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| offset = convertSpecialOffset(offset); |
| |
| return E_NOTIMPL; |
| } |
| |
| HRESULT AccessibleText::get_caretOffset(long* offset) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| VisiblePosition caretPosition = m_object->visiblePositionForPoint(m_object->document()->frame()->selection().absoluteCaretBounds().center()); |
| |
| int caretOffset = caretPosition.deepEquivalent().offsetInContainerNode(); |
| if (caretOffset < 0) |
| return E_FAIL; |
| *offset = caretOffset; |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::get_characterExtents(long offset, enum IA2CoordinateType coordType, long* x, long* y, long* width, long* height) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| offset = convertSpecialOffset(offset); |
| |
| Node* node = m_object->node(); |
| if (!node) |
| return E_POINTER; |
| |
| IntRect boundingRect = m_object->boundsForVisiblePositionRange({ |
| VisiblePosition(Position(node, offset, Position::PositionIsOffsetInAnchor)), |
| VisiblePosition(Position(node, offset + 1, Position::PositionIsOffsetInAnchor)) |
| }); |
| *width = boundingRect.width(); |
| *height = boundingRect.height(); |
| switch (coordType) { |
| case IA2_COORDTYPE_SCREEN_RELATIVE: |
| POINT points[1]; |
| points[0].x = boundingRect.x(); |
| points[0].y = boundingRect.y(); |
| MapWindowPoints(m_window, 0, points, 1); |
| *x = points[0].x; |
| *y = points[0].y; |
| break; |
| case IA2_COORDTYPE_PARENT_RELATIVE: |
| *x = boundingRect.x(); |
| *y = boundingRect.y(); |
| break; |
| default: |
| return E_INVALIDARG; |
| } |
| |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::get_nSelections(long* nSelections) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| if (m_object->document()->frame()->selection().isNone()) |
| *nSelections = 0; |
| else |
| *nSelections = 1; |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::get_offsetAtPoint(long x, long y, enum IA2CoordinateType coordType, long* offset) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| Node* node = m_object->node(); |
| if (!node) |
| return E_POINTER; |
| |
| VisiblePosition vpos; |
| switch (coordType) { |
| case IA2_COORDTYPE_SCREEN_RELATIVE: |
| POINT points[1]; |
| points[0].x = x; |
| points[0].y = y; |
| MapWindowPoints(0, m_window, points, 1); |
| vpos = m_object->visiblePositionForPoint(IntPoint(points[0].x, points[0].y)); |
| break; |
| case IA2_COORDTYPE_PARENT_RELATIVE: |
| vpos = m_object->visiblePositionForPoint(IntPoint(x, y)); |
| break; |
| default: |
| return E_INVALIDARG; |
| } |
| |
| int caretPosition = vpos.deepEquivalent().offsetInContainerNode(); |
| if (caretPosition < 0 || caretPosition > m_object->stringValue().length()) |
| return S_FALSE; |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::get_selection(long selectionIndex, long* startOffset, long* endOffset) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| long selections; |
| get_nSelections(&selections); |
| if (selectionIndex < 0 || selectionIndex >= selections) |
| return E_INVALIDARG; |
| |
| PlainTextRange selectionRange = m_object->selectedTextRange(); |
| |
| *startOffset = selectionRange.start; |
| *endOffset = selectionRange.length; |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::get_text(long startOffset, long endOffset, BSTR* text) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| startOffset = convertSpecialOffset(startOffset); |
| endOffset = convertSpecialOffset(endOffset); |
| WTF::String substringText = m_object->stringValue().substring(startOffset, endOffset - startOffset); |
| |
| *text = BString(substringText).release(); |
| if (substringText.length() && !*text) |
| return E_OUTOFMEMORY; |
| |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::get_textBeforeOffset(long offset, enum IA2TextBoundaryType boundaryType, long* startOffset, long* endOffset, BSTR* text) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| if (!startOffset || !endOffset || !text) |
| return E_POINTER; |
| |
| offset = convertSpecialOffset(offset); |
| |
| if (offset < 0 || offset > m_object->stringValue().length()) |
| return E_INVALIDARG; |
| |
| // Obtain the desired text range |
| VisiblePosition currentPosition = m_object->visiblePositionForIndex(offset); |
| VisiblePositionRange textRange; |
| int previousPos = std::max(0, static_cast<int>(offset-1)); |
| switch (boundaryType) { |
| case IA2_TEXT_BOUNDARY_CHAR: |
| textRange = m_object->visiblePositionRangeForRange(PlainTextRange(previousPos, 1)); |
| break; |
| case IA2_TEXT_BOUNDARY_WORD: |
| textRange = m_object->positionOfLeftWord(currentPosition); |
| textRange = m_object->positionOfRightWord(leftWordPosition(textRange.start, true)); |
| break; |
| case IA2_TEXT_BOUNDARY_SENTENCE: |
| textRange.start = m_object->previousSentenceStartPosition(currentPosition); |
| textRange = m_object->sentenceForPosition(textRange.start); |
| if (isInRange(currentPosition, textRange)) |
| textRange = m_object->sentenceForPosition(m_object->previousSentenceStartPosition(textRange.start.previous())); |
| break; |
| case IA2_TEXT_BOUNDARY_PARAGRAPH: |
| textRange.start = m_object->previousParagraphStartPosition(currentPosition); |
| textRange = m_object->paragraphForPosition(textRange.start); |
| if (isInRange(currentPosition, textRange)) |
| textRange = m_object->paragraphForPosition(m_object->previousParagraphStartPosition(textRange.start.previous())); |
| break; |
| case IA2_TEXT_BOUNDARY_LINE: |
| textRange = m_object->visiblePositionRangeForLine(m_object->lineForPosition(currentPosition)); |
| textRange = m_object->leftLineVisiblePositionRange(textRange.start.previous()); |
| break; |
| case IA2_TEXT_BOUNDARY_ALL: |
| textRange = m_object->visiblePositionRangeForRange(PlainTextRange(0, offset)); |
| break; |
| default: |
| return E_INVALIDARG; |
| break; |
| } |
| |
| // Obtain string and offsets associated with text range |
| *startOffset = textRange.start.deepEquivalent().offsetInContainerNode(); |
| *endOffset = textRange.end.deepEquivalent().offsetInContainerNode(); |
| |
| if (*startOffset == *endOffset) |
| return S_FALSE; |
| |
| WTF::String substringText = m_object->text().substring(*startOffset, *endOffset - *startOffset); |
| *text = BString(substringText).release(); |
| |
| if (substringText.length() && !*text) |
| return E_OUTOFMEMORY; |
| |
| if (!*text) |
| return S_FALSE; |
| |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::get_textAfterOffset(long offset, enum IA2TextBoundaryType boundaryType, long* startOffset, long* endOffset, BSTR* text) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| if (!startOffset || !endOffset || !text) |
| return E_POINTER; |
| |
| int textLength = m_object->stringValue().length(); |
| offset = convertSpecialOffset(offset); |
| |
| if (offset < 0 || offset > textLength) |
| return E_INVALIDARG; |
| |
| VisiblePosition currentPosition = m_object->visiblePositionForIndex(offset); |
| VisiblePositionRange textRange; |
| |
| // Obtain the desired text range |
| switch (boundaryType) { |
| case IA2_TEXT_BOUNDARY_CHAR: |
| textRange = m_object->visiblePositionRangeForRange(PlainTextRange(offset + 1, offset + 2)); |
| break; |
| case IA2_TEXT_BOUNDARY_WORD: |
| textRange = m_object->positionOfRightWord(rightWordPosition(currentPosition, true)); |
| break; |
| case IA2_TEXT_BOUNDARY_SENTENCE: |
| textRange.end = m_object->nextSentenceEndPosition(currentPosition); |
| textRange = m_object->sentenceForPosition(textRange.end); |
| if (isInRange(currentPosition, textRange)) |
| textRange = m_object->sentenceForPosition(m_object->nextSentenceEndPosition(textRange.end.next())); |
| break; |
| case IA2_TEXT_BOUNDARY_PARAGRAPH: |
| textRange.end = m_object->nextParagraphEndPosition(currentPosition); |
| textRange = m_object->paragraphForPosition(textRange.end); |
| if (isInRange(currentPosition, textRange)) |
| textRange = m_object->paragraphForPosition(m_object->nextParagraphEndPosition(textRange.end.next())); |
| break; |
| case IA2_TEXT_BOUNDARY_LINE: |
| textRange = m_object->visiblePositionRangeForLine(m_object->lineForPosition(currentPosition)); |
| textRange = m_object->rightLineVisiblePositionRange(textRange.end.next()); |
| break; |
| case IA2_TEXT_BOUNDARY_ALL: |
| textRange = m_object->visiblePositionRangeForRange(PlainTextRange(offset, textLength-offset)); |
| break; |
| default: |
| return E_INVALIDARG; |
| break; |
| } |
| |
| // Obtain string and offsets associated with text range |
| *startOffset = textRange.start.deepEquivalent().offsetInContainerNode(); |
| *endOffset = textRange.end.deepEquivalent().offsetInContainerNode(); |
| |
| if (*startOffset == *endOffset) |
| return S_FALSE; |
| |
| WTF::String substringText = m_object->text().substring(*startOffset, *endOffset - *startOffset); |
| *text = BString(substringText).release(); |
| if (substringText.length() && !*text) |
| return E_OUTOFMEMORY; |
| |
| if (!*text) |
| return S_FALSE; |
| |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::get_textAtOffset(long offset, enum IA2TextBoundaryType boundaryType, long* startOffset, long* endOffset, BSTR* text) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| if (!startOffset || !endOffset || !text) |
| return E_POINTER; |
| |
| int textLength = m_object->stringValue().length(); |
| |
| offset = convertSpecialOffset(offset); |
| |
| if (offset < 0 || offset > textLength) |
| return E_INVALIDARG; |
| |
| // Obtain the desired text range |
| VisiblePosition currentPosition = m_object->visiblePositionForIndex(offset); |
| VisiblePositionRange textRange; |
| switch (boundaryType) { |
| case IA2_TEXT_BOUNDARY_CHAR: |
| textRange = m_object->visiblePositionRangeForRange(PlainTextRange(offset, 1)); |
| break; |
| case IA2_TEXT_BOUNDARY_WORD: |
| textRange = m_object->positionOfRightWord(leftWordPosition(currentPosition.next(), true)); |
| break; |
| case IA2_TEXT_BOUNDARY_SENTENCE: |
| textRange = m_object->sentenceForPosition(currentPosition); |
| break; |
| case IA2_TEXT_BOUNDARY_PARAGRAPH: |
| textRange = m_object->paragraphForPosition(currentPosition); |
| break; |
| case IA2_TEXT_BOUNDARY_LINE: |
| textRange = m_object->leftLineVisiblePositionRange(currentPosition); |
| break; |
| case IA2_TEXT_BOUNDARY_ALL: |
| textRange = m_object->visiblePositionRangeForRange(PlainTextRange(0, m_object->text().length())); |
| break; |
| default: |
| return E_INVALIDARG; |
| break; |
| } |
| |
| // Obtain string and offsets associated with text range |
| *startOffset = textRange.start.deepEquivalent().offsetInContainerNode(); |
| *endOffset = textRange.end.deepEquivalent().offsetInContainerNode(); |
| |
| if (*startOffset == *endOffset) |
| return S_FALSE; |
| |
| WTF::String substringText = m_object->text().substring(*startOffset, *endOffset - *startOffset); |
| *text = BString(substringText).release(); |
| |
| if (substringText.length() && !*text) |
| return E_OUTOFMEMORY; |
| |
| if (!*text) |
| return S_FALSE; |
| |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::removeSelection(long selectionIndex) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| long selections; |
| get_nSelections(&selections); |
| if (selectionIndex < 0 || selectionIndex >= selections) |
| return E_INVALIDARG; |
| |
| m_object->document()->frame()->selection().clear(); |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::setCaretOffset(long offset) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| offset = convertSpecialOffset(offset); |
| |
| Node* node = m_object->node(); |
| if (!node) |
| return E_POINTER; |
| |
| m_object->document()->frame()->selection().setSelection(VisibleSelection(VisiblePosition(Position(node, offset, Position::PositionIsOffsetInAnchor)))); |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::setSelection(long selectionIndex, long startOffset, long endOffset) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| long selections; |
| get_nSelections(&selections); |
| if (selectionIndex < 0 || selectionIndex >= selections) |
| return E_INVALIDARG; |
| |
| m_object->setSelectedTextRange(PlainTextRange(startOffset, endOffset - startOffset)); |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::get_nCharacters(long* characters) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| int length = m_object->stringValue().length(); |
| if (length < 0) |
| return E_FAIL; |
| |
| *characters = length; |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::scrollSubstringTo(long startIndex, long endIndex, enum IA2ScrollType scrollType) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| startIndex = convertSpecialOffset(startIndex); |
| endIndex = convertSpecialOffset(endIndex); |
| |
| auto textRange = makeSimpleRange(m_object->visiblePositionRangeForRange(PlainTextRange(startIndex, endIndex-startIndex))); |
| if (!textRange) |
| return S_FALSE; |
| |
| IntRect boundingBox = unionRect(RenderObject::absoluteTextRects(*textRange)); |
| switch (scrollType) { |
| case IA2_SCROLL_TYPE_TOP_LEFT: |
| m_object->scrollToGlobalPoint(boundingBox.minXMinYCorner()); |
| break; |
| case IA2_SCROLL_TYPE_BOTTOM_RIGHT: |
| m_object->scrollToGlobalPoint(boundingBox.maxXMaxYCorner()); |
| break; |
| case IA2_SCROLL_TYPE_TOP_EDGE: |
| m_object->scrollToGlobalPoint(IntPoint((boundingBox.x() + boundingBox.maxX()) / 2, boundingBox.y())); |
| break; |
| case IA2_SCROLL_TYPE_BOTTOM_EDGE: |
| m_object->scrollToGlobalPoint(IntPoint((boundingBox.x() + boundingBox.maxX()) / 2, boundingBox.maxY())); |
| break; |
| case IA2_SCROLL_TYPE_LEFT_EDGE: |
| m_object->scrollToGlobalPoint(IntPoint(boundingBox.x(), (boundingBox.y() + boundingBox.maxY()) / 2)); |
| break; |
| case IA2_SCROLL_TYPE_RIGHT_EDGE: |
| m_object->scrollToGlobalPoint(IntPoint(boundingBox.maxX(), (boundingBox.y() + boundingBox.maxY()) / 2)); |
| break; |
| case IA2_SCROLL_TYPE_ANYWHERE: |
| m_object->scrollToGlobalPoint(boundingBox.center()); |
| break; |
| default: |
| return E_INVALIDARG; |
| } |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::scrollSubstringToPoint(long startIndex, long endIndex, enum IA2CoordinateType coordinateType, long x, long y) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| startIndex = convertSpecialOffset(startIndex); |
| endIndex = convertSpecialOffset(endIndex); |
| |
| switch (coordinateType) { |
| case IA2_COORDTYPE_SCREEN_RELATIVE: |
| POINT points[1]; |
| points[0].x = x; |
| points[0].y = y; |
| MapWindowPoints(0, m_window, points, 1); |
| m_object->scrollToGlobalPoint(IntPoint(points[0].x, points[0].y)); |
| break; |
| case IA2_COORDTYPE_PARENT_RELATIVE: |
| m_object->scrollToGlobalPoint(IntPoint(x, y)); |
| break; |
| default: |
| return E_INVALIDARG; |
| } |
| |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::get_newText(IA2TextSegment* newText) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| return E_NOTIMPL; |
| } |
| |
| HRESULT AccessibleText::get_oldText(IA2TextSegment* oldText) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| return E_NOTIMPL; |
| } |
| |
| |
| // IAccessibleText2 |
| HRESULT AccessibleText::get_attributeRange(long offset, BSTR filter, long* startOffset, long* endOffset, BSTR* attributeValues) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| return E_NOTIMPL; |
| } |
| |
| HRESULT AccessibleText::copyText(long startOffset, long endOffset) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| startOffset = convertSpecialOffset(startOffset); |
| endOffset = convertSpecialOffset(endOffset); |
| |
| Frame* frame = m_object->document()->frame(); |
| if (!frame) |
| return E_POINTER; |
| |
| addSelection(startOffset, endOffset); |
| |
| frame->editor().copy(); |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::deleteText(long startOffset, long endOffset) |
| { |
| if (!m_object->canSetValueAttribute()) |
| return S_FALSE; |
| |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| Frame* frame = m_object->document()->frame(); |
| if (!frame) |
| return E_POINTER; |
| |
| addSelection(startOffset, endOffset); |
| |
| frame->editor().deleteSelectionWithSmartDelete(false); |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::insertText(long offset, BSTR* text) |
| { |
| if (!m_object->canSetValueAttribute()) |
| return S_FALSE; |
| |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| offset = convertSpecialOffset(offset); |
| |
| Frame* frame = m_object->document()->frame(); |
| if (!frame) |
| return E_POINTER; |
| |
| addSelection(offset, offset); |
| |
| frame->editor().insertText(*text, 0); |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::cutText(long startOffset, long endOffset) |
| { |
| if (!m_object->canSetValueAttribute()) |
| return S_FALSE; |
| |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| startOffset = convertSpecialOffset(startOffset); |
| endOffset = convertSpecialOffset(endOffset); |
| |
| Frame* frame = m_object->document()->frame(); |
| if (!frame) |
| return E_POINTER; |
| |
| addSelection(startOffset, endOffset); |
| |
| frame->editor().cut(); |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::pasteText(long offset) |
| { |
| if (!m_object->canSetValueAttribute()) |
| return S_FALSE; |
| |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| offset = convertSpecialOffset(offset); |
| |
| Frame* frame = m_object->document()->frame(); |
| if (!frame) |
| return E_POINTER; |
| |
| addSelection(offset, offset); |
| |
| frame->editor().paste(); |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::replaceText(long startOffset, long endOffset, BSTR* text) |
| { |
| if (!m_object->canSetValueAttribute()) |
| return S_FALSE; |
| |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| startOffset = convertSpecialOffset(startOffset); |
| endOffset = convertSpecialOffset(endOffset); |
| |
| Frame* frame = m_object->document()->frame(); |
| if (!frame) |
| return E_POINTER; |
| |
| addSelection(startOffset, endOffset); |
| |
| frame->editor().replaceSelectionWithText(*text, Editor::SelectReplacement::Yes, Editor::SmartReplace::No); |
| return S_OK; |
| } |
| |
| HRESULT AccessibleText::setAttributes(long startOffset, long endOffset, BSTR* attributes) |
| { |
| if (initialCheck() == E_POINTER) |
| return E_POINTER; |
| |
| return E_NOTIMPL; |
| } |
| |
| // IAccessible2 |
| HRESULT AccessibleText::get_attributes(BSTR* attributes) |
| { |
| WTF::String text("text-model:a1"_s); |
| *attributes = BString(text).release(); |
| return S_OK; |
| } |
| |
| // IUnknown |
| HRESULT AccessibleText::QueryInterface(_In_ REFIID riid, _COM_Outptr_ void** ppvObject) |
| { |
| if (!ppvObject) |
| return E_POINTER; |
| if (IsEqualGUID(riid, __uuidof(IAccessibleText))) |
| *ppvObject = static_cast<IAccessibleText*>(this); |
| else if (IsEqualGUID(riid, __uuidof(IAccessibleEditableText))) |
| *ppvObject = static_cast<IAccessibleEditableText*>(this); |
| else if (IsEqualGUID(riid, __uuidof(IAccessible))) |
| *ppvObject = static_cast<IAccessible*>(this); |
| else if (IsEqualGUID(riid, __uuidof(IDispatch))) |
| *ppvObject = static_cast<IAccessible*>(this); |
| else if (IsEqualGUID(riid, __uuidof(IUnknown))) |
| *ppvObject = static_cast<IAccessible*>(this); |
| else if (IsEqualGUID(riid, __uuidof(IAccessible2_2))) |
| *ppvObject = static_cast<IAccessible2_2*>(this); |
| else if (IsEqualGUID(riid, __uuidof(IAccessible2))) |
| *ppvObject = static_cast<IAccessible2*>(this); |
| else if (IsEqualGUID(riid, __uuidof(IAccessibleComparable))) |
| *ppvObject = static_cast<IAccessibleComparable*>(this); |
| else if (IsEqualGUID(riid, __uuidof(IServiceProvider))) |
| *ppvObject = static_cast<IServiceProvider*>(this); |
| else if (IsEqualGUID(riid, __uuidof(AccessibleBase))) |
| *ppvObject = static_cast<AccessibleBase*>(this); |
| else { |
| *ppvObject = nullptr; |
| return E_NOINTERFACE; |
| } |
| AddRef(); |
| return S_OK; |
| } |
| |
| ULONG AccessibleText::Release() |
| { |
| ASSERT(m_refCount > 0); |
| if (--m_refCount) |
| return m_refCount; |
| delete this; |
| return 0; |
| } |
| |
| int AccessibleText::convertSpecialOffset(int offset) |
| { |
| ASSERT(m_object); |
| |
| if (offset == IA2_TEXT_OFFSET_LENGTH) |
| return m_object->stringValue().length(); |
| if (offset == IA2_TEXT_OFFSET_CARET) { |
| long caretOffset; |
| get_caretOffset(&caretOffset); |
| return caretOffset; |
| } |
| return offset; |
| } |
| |
| HRESULT AccessibleText::initialCheck() |
| { |
| if (!m_object) |
| return E_FAIL; |
| |
| Document* document = m_object->document(); |
| if (!document) |
| return E_FAIL; |
| |
| Frame* frame = document->frame(); |
| if (!frame) |
| return E_FAIL; |
| |
| return S_OK; |
| } |
| |
| bool AccessibleText::isInRange(VisiblePosition& current, VisiblePositionRange& wordRange) |
| { |
| auto range = makeSimpleRange(wordRange); |
| return range && contains<ComposedTree>(*range, makeBoundaryPoint(current)); |
| } |