diff options
Diffstat (limited to 'accessible/windows/ia2/ia2AccessibleText.cpp')
-rw-r--r-- | accessible/windows/ia2/ia2AccessibleText.cpp | 481 |
1 files changed, 481 insertions, 0 deletions
diff --git a/accessible/windows/ia2/ia2AccessibleText.cpp b/accessible/windows/ia2/ia2AccessibleText.cpp new file mode 100644 index 0000000000..66b888bc63 --- /dev/null +++ b/accessible/windows/ia2/ia2AccessibleText.cpp @@ -0,0 +1,481 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=2: + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ia2Accessible.h" +#include "ia2AccessibleHypertext.h" +#include "ia2AccessibleText.h" + +#include "AccessibleText_i.c" + +#include "HyperTextAccessibleWrap.h" +#include "HyperTextAccessible-inl.h" +#include "mozilla/ClearOnShutdown.h" + +using namespace mozilla::a11y; + +HyperTextAccessibleBase* ia2AccessibleText::sLastTextChangeAcc = nullptr; +StaticAutoPtr<nsString> ia2AccessibleText::sLastTextChangeString; +uint32_t ia2AccessibleText::sLastTextChangeStart = 0; +uint32_t ia2AccessibleText::sLastTextChangeEnd = 0; +bool ia2AccessibleText::sLastTextChangeWasInsert = false; + +HyperTextAccessibleBase* ia2AccessibleText::TextAcc() { + auto hyp = static_cast<ia2AccessibleHypertext*>(this); + Accessible* acc = hyp->Acc(); + return acc ? acc->AsHyperTextBase() : nullptr; +} + +std::pair<HyperTextAccessibleWrap*, HRESULT> ia2AccessibleText::LocalTextAcc() { + auto hyp = static_cast<ia2AccessibleHypertext*>(this); + Accessible* acc = hyp->Acc(); + if (!acc) { + return {nullptr, CO_E_OBJNOTCONNECTED}; + } + auto localAcc = static_cast<AccessibleWrap*>(acc->AsLocal()); + if (!localAcc) { + return {nullptr, E_NOTIMPL}; + } + return {static_cast<HyperTextAccessibleWrap*>(localAcc), S_OK}; +} + +// IAccessibleText + +STDMETHODIMP +ia2AccessibleText::addSelection(long aStartOffset, long aEndOffset) { + auto [textAcc, hr] = LocalTextAcc(); + if (!textAcc) { + return hr; + } + + return textAcc->AddToSelection(aStartOffset, aEndOffset) ? S_OK + : E_INVALIDARG; +} + +STDMETHODIMP +ia2AccessibleText::get_attributes(long aOffset, long* aStartOffset, + long* aEndOffset, BSTR* aTextAttributes) { + if (!aStartOffset || !aEndOffset || !aTextAttributes) return E_INVALIDARG; + + *aStartOffset = 0; + *aEndOffset = 0; + *aTextAttributes = nullptr; + + int32_t startOffset = 0, endOffset = 0; + HyperTextAccessibleBase* textAcc = TextAcc(); + if (!textAcc) { + return CO_E_OBJNOTCONNECTED; + } + + RefPtr<AccAttributes> attributes = + textAcc->TextAttributes(true, aOffset, &startOffset, &endOffset); + + HRESULT hr = + ia2Accessible::ConvertToIA2Attributes(attributes, aTextAttributes); + if (FAILED(hr)) return hr; + + *aStartOffset = startOffset; + *aEndOffset = endOffset; + + return S_OK; +} + +STDMETHODIMP +ia2AccessibleText::get_caretOffset(long* aOffset) { + if (!aOffset) return E_INVALIDARG; + + *aOffset = -1; + + HyperTextAccessibleBase* textAcc = TextAcc(); + if (!textAcc) { + return CO_E_OBJNOTCONNECTED; + } + + *aOffset = textAcc->CaretOffset(); + + return *aOffset != -1 ? S_OK : S_FALSE; +} + +STDMETHODIMP +ia2AccessibleText::get_characterExtents(long aOffset, + enum IA2CoordinateType aCoordType, + long* aX, long* aY, long* aWidth, + long* aHeight) { + if (!aX || !aY || !aWidth || !aHeight) return E_INVALIDARG; + *aX = *aY = *aWidth = *aHeight = 0; + + uint32_t geckoCoordType = + (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) + ? nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE + : nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE; + LayoutDeviceIntRect rect; + auto textAcc = TextAcc(); + if (!textAcc) { + return CO_E_OBJNOTCONNECTED; + } + + rect = textAcc->CharBounds(aOffset, geckoCoordType); + + // Can't use GetRect() because of long vs. int32_t mismatch + *aX = rect.X(); + *aY = rect.Y(); + *aWidth = rect.Width(); + *aHeight = rect.Height(); + return S_OK; +} + +STDMETHODIMP +ia2AccessibleText::get_nSelections(long* aNSelections) { + if (!aNSelections) return E_INVALIDARG; + *aNSelections = 0; + + HyperTextAccessibleBase* textAcc = TextAcc(); + if (!textAcc) { + return CO_E_OBJNOTCONNECTED; + } + + *aNSelections = textAcc->SelectionCount(); + + return S_OK; +} + +STDMETHODIMP +ia2AccessibleText::get_offsetAtPoint(long aX, long aY, + enum IA2CoordinateType aCoordType, + long* aOffset) { + if (!aOffset) return E_INVALIDARG; + *aOffset = 0; + + uint32_t geckoCoordType = + (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) + ? nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE + : nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE; + + HyperTextAccessibleBase* textAcc = TextAcc(); + if (!textAcc) { + return CO_E_OBJNOTCONNECTED; + } + + *aOffset = textAcc->OffsetAtPoint(aX, aY, geckoCoordType); + + return *aOffset == -1 ? S_FALSE : S_OK; +} + +STDMETHODIMP +ia2AccessibleText::get_selection(long aSelectionIndex, long* aStartOffset, + long* aEndOffset) { + if (!aStartOffset || !aEndOffset) return E_INVALIDARG; + *aStartOffset = *aEndOffset = 0; + + int32_t startOffset = 0, endOffset = 0; + HyperTextAccessibleBase* textAcc = TextAcc(); + if (!textAcc) { + return CO_E_OBJNOTCONNECTED; + } + + if (!textAcc->SelectionBoundsAt(aSelectionIndex, &startOffset, &endOffset)) { + return E_INVALIDARG; + } + + *aStartOffset = startOffset; + *aEndOffset = endOffset; + return S_OK; +} + +STDMETHODIMP +ia2AccessibleText::get_text(long aStartOffset, long aEndOffset, BSTR* aText) { + if (!aText) return E_INVALIDARG; + + *aText = nullptr; + + nsAutoString text; + HyperTextAccessibleBase* textAcc = TextAcc(); + if (!textAcc) { + return CO_E_OBJNOTCONNECTED; + } + + if (!textAcc->IsValidRange(aStartOffset, aEndOffset)) { + return E_INVALIDARG; + } + + textAcc->TextSubstring(aStartOffset, aEndOffset, text); + + if (text.IsEmpty()) return S_FALSE; + + *aText = ::SysAllocStringLen(text.get(), text.Length()); + return *aText ? S_OK : E_OUTOFMEMORY; +} + +STDMETHODIMP +ia2AccessibleText::get_textBeforeOffset(long aOffset, + enum IA2TextBoundaryType aBoundaryType, + long* aStartOffset, long* aEndOffset, + BSTR* aText) { + if (!aStartOffset || !aEndOffset || !aText) return E_INVALIDARG; + + *aStartOffset = *aEndOffset = 0; + *aText = nullptr; + + HyperTextAccessibleBase* textAcc = TextAcc(); + if (!textAcc) { + return CO_E_OBJNOTCONNECTED; + } + + if (!textAcc->IsValidOffset(aOffset)) return E_INVALIDARG; + + nsAutoString text; + int32_t startOffset = 0, endOffset = 0; + + if (aBoundaryType == IA2_TEXT_BOUNDARY_ALL) { + startOffset = 0; + endOffset = textAcc->CharacterCount(); + textAcc->TextSubstring(startOffset, endOffset, text); + } else { + AccessibleTextBoundary boundaryType = GetGeckoTextBoundary(aBoundaryType); + if (boundaryType == -1) return S_FALSE; + + textAcc->TextBeforeOffset(aOffset, boundaryType, &startOffset, &endOffset, + text); + } + + *aStartOffset = startOffset; + *aEndOffset = endOffset; + + if (text.IsEmpty()) return S_FALSE; + + *aText = ::SysAllocStringLen(text.get(), text.Length()); + return *aText ? S_OK : E_OUTOFMEMORY; +} + +STDMETHODIMP +ia2AccessibleText::get_textAfterOffset(long aOffset, + enum IA2TextBoundaryType aBoundaryType, + long* aStartOffset, long* aEndOffset, + BSTR* aText) { + if (!aStartOffset || !aEndOffset || !aText) return E_INVALIDARG; + + *aStartOffset = 0; + *aEndOffset = 0; + *aText = nullptr; + + HyperTextAccessibleBase* textAcc = TextAcc(); + if (!textAcc) { + return CO_E_OBJNOTCONNECTED; + } + + if (!textAcc->IsValidOffset(aOffset)) return E_INVALIDARG; + + nsAutoString text; + int32_t startOffset = 0, endOffset = 0; + + if (aBoundaryType == IA2_TEXT_BOUNDARY_ALL) { + startOffset = 0; + endOffset = textAcc->CharacterCount(); + textAcc->TextSubstring(startOffset, endOffset, text); + } else { + AccessibleTextBoundary boundaryType = GetGeckoTextBoundary(aBoundaryType); + if (boundaryType == -1) return S_FALSE; + textAcc->TextAfterOffset(aOffset, boundaryType, &startOffset, &endOffset, + text); + } + + *aStartOffset = startOffset; + *aEndOffset = endOffset; + + if (text.IsEmpty()) return S_FALSE; + + *aText = ::SysAllocStringLen(text.get(), text.Length()); + return *aText ? S_OK : E_OUTOFMEMORY; +} + +STDMETHODIMP +ia2AccessibleText::get_textAtOffset(long aOffset, + enum IA2TextBoundaryType aBoundaryType, + long* aStartOffset, long* aEndOffset, + BSTR* aText) { + if (!aStartOffset || !aEndOffset || !aText) return E_INVALIDARG; + + *aStartOffset = *aEndOffset = 0; + *aText = nullptr; + + HyperTextAccessibleBase* textAcc = TextAcc(); + if (!textAcc) return CO_E_OBJNOTCONNECTED; + + if (!textAcc->IsValidOffset(aOffset)) return E_INVALIDARG; + + nsAutoString text; + int32_t startOffset = 0, endOffset = 0; + if (aBoundaryType == IA2_TEXT_BOUNDARY_ALL) { + startOffset = 0; + endOffset = textAcc->CharacterCount(); + textAcc->TextSubstring(startOffset, endOffset, text); + } else { + AccessibleTextBoundary boundaryType = GetGeckoTextBoundary(aBoundaryType); + if (boundaryType == -1) return S_FALSE; + textAcc->TextAtOffset(aOffset, boundaryType, &startOffset, &endOffset, + text); + } + + *aStartOffset = startOffset; + *aEndOffset = endOffset; + + if (text.IsEmpty()) return S_FALSE; + + *aText = ::SysAllocStringLen(text.get(), text.Length()); + return *aText ? S_OK : E_OUTOFMEMORY; +} + +STDMETHODIMP +ia2AccessibleText::removeSelection(long aSelectionIndex) { + auto [textAcc, hr] = LocalTextAcc(); + if (!textAcc) { + return hr; + } + + return textAcc->RemoveFromSelection(aSelectionIndex) ? S_OK : E_INVALIDARG; +} + +STDMETHODIMP +ia2AccessibleText::setCaretOffset(long aOffset) { + HyperTextAccessibleBase* textAcc = TextAcc(); + if (!textAcc) { + return CO_E_OBJNOTCONNECTED; + } + + if (!textAcc->IsValidOffset(aOffset)) return E_INVALIDARG; + + textAcc->SetCaretOffset(aOffset); + return S_OK; +} + +STDMETHODIMP +ia2AccessibleText::setSelection(long aSelectionIndex, long aStartOffset, + long aEndOffset) { + auto [textAcc, hr] = LocalTextAcc(); + if (!textAcc) { + return hr; + } + + return textAcc->SetSelectionBoundsAt(aSelectionIndex, aStartOffset, + aEndOffset) + ? S_OK + : E_INVALIDARG; +} + +STDMETHODIMP +ia2AccessibleText::get_nCharacters(long* aNCharacters) { + if (!aNCharacters) return E_INVALIDARG; + *aNCharacters = 0; + + HyperTextAccessibleBase* textAcc = TextAcc(); + if (!textAcc) return CO_E_OBJNOTCONNECTED; + + *aNCharacters = textAcc->CharacterCount(); + return S_OK; +} + +STDMETHODIMP +ia2AccessibleText::scrollSubstringTo(long aStartIndex, long aEndIndex, + enum IA2ScrollType aScrollType) { + auto [textAcc, hr] = LocalTextAcc(); + if (!textAcc) { + return hr; + } + + if (!textAcc->IsValidRange(aStartIndex, aEndIndex)) return E_INVALIDARG; + + textAcc->ScrollSubstringTo(aStartIndex, aEndIndex, aScrollType); + return S_OK; +} + +STDMETHODIMP +ia2AccessibleText::scrollSubstringToPoint(long aStartIndex, long aEndIndex, + enum IA2CoordinateType aCoordType, + long aX, long aY) { + uint32_t geckoCoordType = + (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) + ? nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE + : nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE; + + auto [textAcc, hr] = LocalTextAcc(); + if (!textAcc) { + return hr; + } + + if (!textAcc->IsValidRange(aStartIndex, aEndIndex)) return E_INVALIDARG; + + textAcc->ScrollSubstringToPoint(aStartIndex, aEndIndex, geckoCoordType, aX, + aY); + return S_OK; +} + +STDMETHODIMP +ia2AccessibleText::get_newText(IA2TextSegment* aNewText) { + return GetModifiedText(true, aNewText); +} + +STDMETHODIMP +ia2AccessibleText::get_oldText(IA2TextSegment* aOldText) { + return GetModifiedText(false, aOldText); +} + +// ia2AccessibleText + +HRESULT +ia2AccessibleText::GetModifiedText(bool aGetInsertedText, + IA2TextSegment* aText) { + if (!aText) return E_INVALIDARG; + + if (!sLastTextChangeAcc) return S_OK; + + if (aGetInsertedText != sLastTextChangeWasInsert) return S_OK; + + if (sLastTextChangeAcc != TextAcc()) return S_OK; + + aText->start = sLastTextChangeStart; + aText->end = sLastTextChangeEnd; + + if (sLastTextChangeString->IsEmpty()) return S_FALSE; + + aText->text = ::SysAllocStringLen(sLastTextChangeString->get(), + sLastTextChangeString->Length()); + return aText->text ? S_OK : E_OUTOFMEMORY; +} + +AccessibleTextBoundary ia2AccessibleText::GetGeckoTextBoundary( + enum IA2TextBoundaryType aBoundaryType) { + switch (aBoundaryType) { + case IA2_TEXT_BOUNDARY_CHAR: + return nsIAccessibleText::BOUNDARY_CHAR; + case IA2_TEXT_BOUNDARY_WORD: + return nsIAccessibleText::BOUNDARY_WORD_START; + case IA2_TEXT_BOUNDARY_LINE: + return nsIAccessibleText::BOUNDARY_LINE_START; + case IA2_TEXT_BOUNDARY_PARAGRAPH: + return nsIAccessibleText::BOUNDARY_PARAGRAPH; + // case IA2_TEXT_BOUNDARY_SENTENCE: + // XXX: not implemented + default: + return -1; + } +} + +void ia2AccessibleText::InitTextChangeData() { + ClearOnShutdown(&sLastTextChangeString); +} + +void ia2AccessibleText::UpdateTextChangeData(HyperTextAccessibleBase* aAcc, + bool aInsert, + const nsAString& aStr, + int32_t aStart, uint32_t aLen) { + if (!sLastTextChangeString) sLastTextChangeString = new nsString(); + + sLastTextChangeAcc = aAcc; + sLastTextChangeStart = aStart; + sLastTextChangeEnd = aStart + aLen; + sLastTextChangeWasInsert = aInsert; + *sLastTextChangeString = aStr; +} |