get_selections is largely based on the old IAccessible2_3::get_selectionRanges method. However, this makes use of HyperTextAccessibleBase::CroppedSelectionRanges, which didn't exist before. It has also been updated to return IAccessibleText pointers as required by the new interface. setSelections is entirely new, but relies on existing mechanisms to convert offsets and to add and remove selections. Differential Revision: https://phabricator.services.mozilla.com/D185135
127 lines
4.2 KiB
C++
127 lines
4.2 KiB
C++
/* -*- 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 "ia2AccessibleTextSelectionContainer.h"
|
|
|
|
#include "AccessibleTextSelectionContainer_i.c"
|
|
#include "ia2AccessibleHypertext.h"
|
|
#include "mozilla/a11y/HyperTextAccessibleBase.h"
|
|
#include "TextRange.h"
|
|
#include "TextLeafRange.h"
|
|
|
|
using namespace mozilla::a11y;
|
|
|
|
// IAccessibleTextSelectionContainer
|
|
|
|
STDMETHODIMP
|
|
ia2AccessibleTextSelectionContainer::get_selections(
|
|
IA2TextSelection** selections, long* nSelections) {
|
|
if (!selections || !nSelections) {
|
|
return E_INVALIDARG;
|
|
}
|
|
*nSelections = 0;
|
|
HyperTextAccessibleBase* text = TextAcc();
|
|
if (!text) {
|
|
return CO_E_OBJNOTCONNECTED;
|
|
}
|
|
AutoTArray<TextRange, 1> ranges;
|
|
text->CroppedSelectionRanges(ranges);
|
|
*nSelections = ranges.Length();
|
|
*selections = static_cast<IA2TextSelection*>(
|
|
::CoTaskMemAlloc(sizeof(IA2TextSelection) * *nSelections));
|
|
if (!*selections) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
for (uint32_t idx = 0; idx < static_cast<uint32_t>(*nSelections); idx++) {
|
|
RefPtr<IAccessibleText> startObj =
|
|
GetIATextFrom(ranges[idx].StartContainer());
|
|
startObj.forget(&(*selections)[idx].startObj);
|
|
(*selections)[idx].startOffset = ranges[idx].StartOffset();
|
|
RefPtr<IAccessibleText> endObj = GetIATextFrom(ranges[idx].EndContainer());
|
|
endObj.forget(&(*selections)[idx].endObj);
|
|
(*selections)[idx].endOffset = ranges[idx].EndOffset();
|
|
// XXX Expose this properly somehow.
|
|
(*selections)[idx].startIsActive = true;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
ia2AccessibleTextSelectionContainer::setSelections(
|
|
long nSelections, IA2TextSelection* selections) {
|
|
if (nSelections < 0 || (nSelections > 0 && !selections)) {
|
|
return E_INVALIDARG;
|
|
}
|
|
HyperTextAccessibleBase* text = TextAcc();
|
|
if (!text) {
|
|
return CO_E_OBJNOTCONNECTED;
|
|
}
|
|
// Build and validate new selection ranges.
|
|
AutoTArray<TextLeafRange, 1> newRanges;
|
|
newRanges.SetCapacity(nSelections);
|
|
for (long r = 0; r < nSelections; ++r) {
|
|
TextLeafRange range(GetTextLeafPointFrom(selections[r].startObj,
|
|
selections[r].startOffset, false),
|
|
GetTextLeafPointFrom(selections[r].endObj,
|
|
selections[r].endOffset, true));
|
|
if (!range) {
|
|
return E_INVALIDARG;
|
|
}
|
|
newRanges.AppendElement(range);
|
|
}
|
|
// Get the number of existing selections. We use SelectionRanges rather than
|
|
// SelectionCount because SelectionCount is restricted to this Accessible,
|
|
// whereas we want all selections within the control/document.
|
|
AutoTArray<TextRange, 1> oldRanges;
|
|
text->SelectionRanges(&oldRanges);
|
|
// Set the new selections.
|
|
for (long r = 0; r < nSelections; ++r) {
|
|
newRanges[r].SetSelection(r);
|
|
}
|
|
// Remove any remaining old selections if there were more than nSelections.
|
|
for (long r = nSelections; r < static_cast<long>(oldRanges.Length()); ++r) {
|
|
text->RemoveFromSelection(r);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
// ia2AccessibleTextSelectionContainer
|
|
|
|
HyperTextAccessibleBase* ia2AccessibleTextSelectionContainer::TextAcc() {
|
|
auto hyp = static_cast<ia2AccessibleHypertext*>(this);
|
|
Accessible* acc = hyp->Acc();
|
|
return acc ? acc->AsHyperTextBase() : nullptr;
|
|
}
|
|
|
|
/* static */
|
|
RefPtr<IAccessibleText> ia2AccessibleTextSelectionContainer::GetIATextFrom(
|
|
Accessible* aAcc) {
|
|
MsaaAccessible* msaa = MsaaAccessible::GetFrom(aAcc);
|
|
MOZ_ASSERT(msaa);
|
|
RefPtr<IAccessibleText> text;
|
|
msaa->QueryInterface(IID_IAccessibleText, getter_AddRefs(text));
|
|
MOZ_ASSERT(text);
|
|
return text;
|
|
}
|
|
|
|
/* static */
|
|
TextLeafPoint ia2AccessibleTextSelectionContainer::GetTextLeafPointFrom(
|
|
IAccessibleText* aText, long aOffset, bool aDescendToEnd) {
|
|
if (!aText) {
|
|
return TextLeafPoint();
|
|
}
|
|
Accessible* acc = MsaaAccessible::GetAccessibleFrom(aText);
|
|
if (!acc) {
|
|
return TextLeafPoint();
|
|
}
|
|
HyperTextAccessibleBase* hyp = acc->AsHyperTextBase();
|
|
if (!hyp) {
|
|
return TextLeafPoint();
|
|
}
|
|
return hyp->ToTextLeafPoint(aOffset, aDescendToEnd);
|
|
}
|