mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1233118 - implement IAccessible2_3::selectionRanges, r=yzen
This commit is contained in:
parent
60b138f772
commit
1eda297f31
29
accessible/base/TextRange-inl.h
Normal file
29
accessible/base/TextRange-inl.h
Normal file
@ -0,0 +1,29 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_a11y_TextRange_inl_h__
|
||||
#define mozilla_a11y_TextRange_inl_h__
|
||||
|
||||
#include "TextRange.h"
|
||||
#include "HyperTextAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
inline Accessible*
|
||||
TextRange::Container() const
|
||||
{
|
||||
uint32_t pos1 = 0, pos2 = 0;
|
||||
nsAutoTArray<Accessible*, 30> parents1, parents2;
|
||||
return CommonParent(mStartContainer, mEndContainer,
|
||||
&parents1, &pos1, &parents2, &pos2);
|
||||
}
|
||||
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -4,10 +4,9 @@
|
||||
* 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 "TextRange.h"
|
||||
#include "TextRange-inl.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "HyperTextAccessible.h"
|
||||
#include "nsAccUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -59,42 +58,6 @@ TextRange::TextRange(HyperTextAccessible* aRoot,
|
||||
{
|
||||
}
|
||||
|
||||
Accessible*
|
||||
TextRange::Container() const
|
||||
{
|
||||
if (mStartContainer == mEndContainer)
|
||||
return mStartContainer;
|
||||
|
||||
// Build the chain of parents
|
||||
Accessible* p1 = mStartContainer;
|
||||
Accessible* p2 = mEndContainer;
|
||||
nsAutoTArray<Accessible*, 30> parents1, parents2;
|
||||
do {
|
||||
parents1.AppendElement(p1);
|
||||
p1 = p1->Parent();
|
||||
} while (p1);
|
||||
do {
|
||||
parents2.AppendElement(p2);
|
||||
p2 = p2->Parent();
|
||||
} while (p2);
|
||||
|
||||
// Find where the parent chain differs
|
||||
uint32_t pos1 = parents1.Length();
|
||||
uint32_t pos2 = parents2.Length();
|
||||
Accessible* parent = nullptr;
|
||||
uint32_t len = 0;
|
||||
for (len = std::min(pos1, pos2); len > 0; --len) {
|
||||
Accessible* child1 = parents1.ElementAt(--pos1);
|
||||
Accessible* child2 = parents2.ElementAt(--pos2);
|
||||
if (child1 != child2)
|
||||
break;
|
||||
|
||||
parent = child1;
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
void
|
||||
TextRange::EmbeddedChildren(nsTArray<Accessible*>* aChildren) const
|
||||
{
|
||||
@ -111,28 +74,11 @@ TextRange::EmbeddedChildren(nsTArray<Accessible*>* aChildren) const
|
||||
|
||||
Accessible* p1 = mStartContainer->GetChildAtOffset(mStartOffset);
|
||||
Accessible* p2 = mEndContainer->GetChildAtOffset(mEndOffset);
|
||||
|
||||
uint32_t pos1 = 0, pos2 = 0;
|
||||
nsAutoTArray<Accessible*, 30> parents1, parents2;
|
||||
do {
|
||||
parents1.AppendElement(p1);
|
||||
p1 = p1->Parent();
|
||||
} while (p1);
|
||||
do {
|
||||
parents2.AppendElement(p2);
|
||||
p2 = p2->Parent();
|
||||
} while (p2);
|
||||
|
||||
// Find deepest common container.
|
||||
uint32_t pos1 = parents1.Length();
|
||||
uint32_t pos2 = parents2.Length();
|
||||
Accessible* container = nullptr;
|
||||
for (uint32_t len = std::min(pos1, pos2); len > 0; --len) {
|
||||
Accessible* child1 = parents1.ElementAt(--pos1);
|
||||
Accessible* child2 = parents2.ElementAt(--pos2);
|
||||
if (child1 != child2)
|
||||
break;
|
||||
|
||||
container = child1;
|
||||
}
|
||||
Accessible* container =
|
||||
CommonParent(p1, p2, &parents1, &pos1, &parents2, &pos2);
|
||||
|
||||
// Traverse the tree up to the container and collect embedded objects.
|
||||
for (uint32_t idx = 0; idx < pos1 - 1; idx++) {
|
||||
@ -196,6 +142,95 @@ TextRange::Normalize(ETextUnit aUnit)
|
||||
|
||||
}
|
||||
|
||||
bool
|
||||
TextRange::Crop(Accessible* aContainer)
|
||||
{
|
||||
uint32_t boundaryPos = 0, containerPos = 0;
|
||||
nsAutoTArray<Accessible*, 30> boundaryParents, containerParents;
|
||||
|
||||
// Crop the start boundary.
|
||||
Accessible* container = nullptr;
|
||||
Accessible* boundary = mStartContainer->GetChildAtOffset(mStartOffset);
|
||||
if (boundary != aContainer) {
|
||||
CommonParent(boundary, aContainer, &boundaryParents, &boundaryPos,
|
||||
&containerParents, &containerPos);
|
||||
|
||||
if (boundaryPos == 0) {
|
||||
if (containerPos != 0) {
|
||||
// The container is contained by the start boundary, reduce the range to
|
||||
// the point starting at the container.
|
||||
aContainer->ToTextPoint(mStartContainer.StartAssignment(), &mStartOffset);
|
||||
static_cast<Accessible*>(mStartContainer)->AddRef();
|
||||
}
|
||||
else {
|
||||
// The start boundary and the container are siblings.
|
||||
container = aContainer;
|
||||
}
|
||||
}
|
||||
else if (containerPos != 0) {
|
||||
// The container does not contain the start boundary.
|
||||
boundary = boundaryParents[boundaryPos];
|
||||
container = containerParents[containerPos];
|
||||
}
|
||||
|
||||
if (container) {
|
||||
// If the range start is after the container, then make the range invalid.
|
||||
if (boundary->IndexInParent() > container->IndexInParent()) {
|
||||
return !!(mRoot = nullptr);
|
||||
}
|
||||
|
||||
// If the range starts before the container, then reduce the range to
|
||||
// the point starting at the container.
|
||||
if (boundary->IndexInParent() < container->IndexInParent()) {
|
||||
container->ToTextPoint(mStartContainer.StartAssignment(), &mStartOffset);
|
||||
mStartContainer.get()->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
boundaryParents.SetLengthAndRetainStorage(0);
|
||||
containerParents.SetLengthAndRetainStorage(0);
|
||||
}
|
||||
|
||||
boundary = mEndContainer->GetChildAtOffset(mEndOffset);
|
||||
if (boundary == aContainer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Crop the end boundary.
|
||||
container = nullptr;
|
||||
CommonParent(boundary, aContainer, &boundaryParents, &boundaryPos,
|
||||
&containerParents, &containerPos);
|
||||
|
||||
if (boundaryPos == 0) {
|
||||
if (containerPos != 0) {
|
||||
aContainer->ToTextPoint(mEndContainer.StartAssignment(), &mEndOffset, false);
|
||||
static_cast<Accessible*>(mEndContainer)->AddRef();
|
||||
}
|
||||
else {
|
||||
container = aContainer;
|
||||
}
|
||||
}
|
||||
else if (containerPos != 0) {
|
||||
boundary = boundaryParents[boundaryPos];
|
||||
container = containerParents[containerPos];
|
||||
}
|
||||
|
||||
if (!container) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (boundary->IndexInParent() < container->IndexInParent()) {
|
||||
return !!(mRoot = nullptr);
|
||||
}
|
||||
|
||||
if (boundary->IndexInParent() > container->IndexInParent()) {
|
||||
container->ToTextPoint(mEndContainer.StartAssignment(), &mEndOffset, false);
|
||||
static_cast<Accessible*>(mEndContainer)->AddRef();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
TextRange::FindText(const nsAString& aText, EDirection aDirection,
|
||||
nsCaseTreatment aCaseSensitive, TextRange* aFoundRange) const
|
||||
@ -296,5 +331,46 @@ TextRange::MoveInternal(ETextUnit aUnit, int32_t aCount,
|
||||
|
||||
}
|
||||
|
||||
Accessible*
|
||||
TextRange::CommonParent(Accessible* aAcc1, Accessible* aAcc2,
|
||||
nsTArray<Accessible*>* aParents1, uint32_t* aPos1,
|
||||
nsTArray<Accessible*>* aParents2, uint32_t* aPos2) const
|
||||
{
|
||||
if (aAcc1 == aAcc2) {
|
||||
return aAcc1;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aParents1->Length() == 0 || aParents2->Length() == 0,
|
||||
"Wrong arguments");
|
||||
|
||||
// Build the chain of parents.
|
||||
Accessible* p1 = aAcc1;
|
||||
Accessible* p2 = aAcc2;
|
||||
do {
|
||||
aParents1->AppendElement(p1);
|
||||
p1 = p1->Parent();
|
||||
} while (p1);
|
||||
do {
|
||||
aParents2->AppendElement(p2);
|
||||
p2 = p2->Parent();
|
||||
} while (p2);
|
||||
|
||||
// Find where the parent chain differs
|
||||
*aPos1 = aParents1->Length();
|
||||
*aPos2 = aParents2->Length();
|
||||
Accessible* parent = nullptr;
|
||||
uint32_t len = 0;
|
||||
for (len = std::min(*aPos1, *aPos2); len > 0; --len) {
|
||||
Accessible* child1 = aParents1->ElementAt(--(*aPos1));
|
||||
Accessible* child2 = aParents2->ElementAt(--(*aPos2));
|
||||
if (child1 != child2)
|
||||
break;
|
||||
|
||||
parent = child1;
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
@ -131,6 +131,12 @@ public:
|
||||
*/
|
||||
void Normalize(ETextUnit aUnit);
|
||||
|
||||
/**
|
||||
* Crops the range if it overlaps the given accessible element boundaries,
|
||||
* returns true if the range was cropped successfully.
|
||||
*/
|
||||
bool Crop(Accessible* aContainer);
|
||||
|
||||
enum EDirection {
|
||||
eBackward,
|
||||
eForward
|
||||
@ -243,6 +249,14 @@ private:
|
||||
HyperTextAccessible* aStopContainer = nullptr,
|
||||
int32_t aStopOffset = 0);
|
||||
|
||||
/**
|
||||
* A helper method returning a common parent for two given accessible
|
||||
* elements.
|
||||
*/
|
||||
Accessible* CommonParent(Accessible* aAcc1, Accessible* aAcc2,
|
||||
nsTArray<Accessible*>* aParents1, uint32_t* aPos1,
|
||||
nsTArray<Accessible*>* aParents2, uint32_t* aPos2) const;
|
||||
|
||||
RefPtr<HyperTextAccessible> mRoot;
|
||||
RefPtr<HyperTextAccessible> mStartContainer;
|
||||
RefPtr<HyperTextAccessible> mEndContainer;
|
||||
|
@ -2250,6 +2250,30 @@ Accessible::AnchorURIAt(uint32_t aAnchorIndex)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
Accessible::ToTextPoint(HyperTextAccessible** aContainer, int32_t* aOffset,
|
||||
bool aIsBefore) const
|
||||
{
|
||||
if (IsHyperText()) {
|
||||
*aContainer = const_cast<Accessible*>(this)->AsHyperText();
|
||||
*aOffset = aIsBefore ? 0 : (*aContainer)->CharacterCount();
|
||||
return;
|
||||
}
|
||||
|
||||
const Accessible* child = nullptr;
|
||||
const Accessible* parent = this;
|
||||
do {
|
||||
child = parent;
|
||||
parent = parent->Parent();
|
||||
} while (parent && !parent->IsHyperText());
|
||||
|
||||
if (parent) {
|
||||
*aContainer = const_cast<Accessible*>(parent)->AsHyperText();
|
||||
*aOffset = (*aContainer)->GetChildOffset(
|
||||
child->IndexInParent() + static_cast<int32_t>(!aIsBefore));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SelectAccessible
|
||||
|
@ -758,6 +758,12 @@ public:
|
||||
*/
|
||||
virtual already_AddRefed<nsIURI> AnchorURIAt(uint32_t aAnchorIndex);
|
||||
|
||||
/**
|
||||
* Returns a text point for the accessible element.
|
||||
*/
|
||||
void ToTextPoint(HyperTextAccessible** aContainer, int32_t* aOffset,
|
||||
bool aIsBefore = true) const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// SelectAccessible
|
||||
|
||||
|
@ -1769,7 +1769,7 @@ HyperTextAccessible::EnclosingRange(a11y::TextRange& aRange) const
|
||||
void
|
||||
HyperTextAccessible::SelectionRanges(nsTArray<a11y::TextRange>* aRanges) const
|
||||
{
|
||||
NS_ASSERTION(aRanges->Length() != 0, "TextRange array supposed to be empty");
|
||||
MOZ_ASSERT(aRanges->Length() == 0, "TextRange array supposed to be empty");
|
||||
|
||||
dom::Selection* sel = DOMSelection();
|
||||
if (!sel)
|
||||
|
@ -258,7 +258,7 @@ public:
|
||||
* @param aInvalidateAfter [in, optional] indicates whether invalidate
|
||||
* cached offsets for next siblings of the child
|
||||
*/
|
||||
int32_t GetChildOffset(Accessible* aChild,
|
||||
int32_t GetChildOffset(const Accessible* aChild,
|
||||
bool aInvalidateAfter = false) const
|
||||
{
|
||||
int32_t index = GetIndexOf(aChild);
|
||||
|
@ -11,6 +11,7 @@ GARBAGE += $(MIDL_GENERATED_FILES)
|
||||
MIDL_INTERFACES = \
|
||||
Accessible2.idl \
|
||||
Accessible2_2.idl \
|
||||
Accessible2_3.idl \
|
||||
AccessibleAction.idl \
|
||||
AccessibleApplication.idl \
|
||||
AccessibleComponent.idl \
|
||||
|
@ -13,7 +13,7 @@ interface nsIVariant;
|
||||
/**
|
||||
* A range representing a piece of text in the document.
|
||||
*/
|
||||
[scriptable, uuid(525b3401-8a67-4822-b35d-661065767cd8)]
|
||||
[scriptable, uuid(c4515623-55f9-4543-a3d5-c1e9afa588f4)]
|
||||
interface nsIAccessibleTextRange : nsISupports
|
||||
{
|
||||
readonly attribute nsIAccessibleText startContainer;
|
||||
@ -81,6 +81,11 @@ interface nsIAccessibleTextRange : nsISupports
|
||||
*/
|
||||
void normalize(in unsigned long aUnit);
|
||||
|
||||
/**
|
||||
* Crops the range by the given accessible element.
|
||||
*/
|
||||
boolean crop(in nsIAccessible aContainer);
|
||||
|
||||
/**
|
||||
* Return range enclosing the found text.
|
||||
*/
|
||||
|
@ -473,6 +473,10 @@ function testTextRange(aRange, aRangeDescr, aStartContainer, aStartOffset,
|
||||
is(aRange.endOffset, aEndOffset,
|
||||
"Wrong end offset of " + aRangeDescr);
|
||||
|
||||
if (aText === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
is(aRange.text, aText, "Wrong text of " + aRangeDescr);
|
||||
|
||||
var children = aRange.embeddedChildren;
|
||||
|
@ -1,3 +1,4 @@
|
||||
[DEFAULT]
|
||||
|
||||
[test_general.html]
|
||||
[test_selection.html]
|
||||
|
120
accessible/tests/mochitest/textrange/test_selection.html
Normal file
120
accessible/tests/mochitest/textrange/test_selection.html
Normal file
@ -0,0 +1,120 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Text Range selection tests</title>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../text.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../layout.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
function doTest()
|
||||
{
|
||||
var sel = window.getSelection();
|
||||
var p = getNode("p1");
|
||||
var a = getNode("p2_a");
|
||||
|
||||
var range = document.createRange();
|
||||
sel.addRange(range);
|
||||
|
||||
// the accessible is contained by the range
|
||||
range.selectNode(p);
|
||||
|
||||
var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
|
||||
var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
|
||||
|
||||
testTextRange(a11yrange, "selection range #1", document, 3, document, 4);
|
||||
|
||||
ok(a11yrange.crop(getAccessible(a)), "Range failed to crop #1.");
|
||||
testTextRange(a11yrange, "cropped range #1", a, 0, a, 5);
|
||||
|
||||
// the range is contained by the accessible
|
||||
range.selectNode(a);
|
||||
var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
|
||||
var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
|
||||
|
||||
testTextRange(a11yrange, "selection range #2", p, 5, p, 6);
|
||||
|
||||
ok(a11yrange.crop(getAccessible(p)), "Range failed to crop #2.");
|
||||
testTextRange(a11yrange, "cropped range #2", p, 5, p, 6);
|
||||
|
||||
// the range starts before the accessible and ends inside it
|
||||
range.setStart(p, 0);
|
||||
range.setEndAfter(a.firstChild, 4);
|
||||
var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
|
||||
var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
|
||||
|
||||
testTextRange(a11yrange, "selection range #3", p, 0, a, 4);
|
||||
|
||||
ok(a11yrange.crop(getAccessible(a)), "Range failed to crop #3.");
|
||||
testTextRange(a11yrange, "cropped range #3", a, 0, a, 4);
|
||||
|
||||
// the range starts inside the accessible and ends after it
|
||||
range.setStart(a.firstChild, 1);
|
||||
range.setEndAfter(p);
|
||||
var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
|
||||
var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
|
||||
|
||||
testTextRange(a11yrange, "selection range #4", a, 1, document, 4);
|
||||
|
||||
ok(a11yrange.crop(getAccessible(a)), "Range failed to crop #4.");
|
||||
testTextRange(a11yrange, "cropped range #4", a, 1, a, 5);
|
||||
|
||||
// the range ends before the accessible
|
||||
range.setStart(p.firstChild, 0);
|
||||
range.setEnd(p.firstChild, 4);
|
||||
var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
|
||||
var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
|
||||
|
||||
testTextRange(a11yrange, "selection range #5", p, 0, p, 4);
|
||||
ok(!a11yrange.crop(getAccessible(a)), "Crop #5 succeeded while it shouldn't");
|
||||
|
||||
// the range starts after the accessible
|
||||
range.setStart(p.lastChild, 0);
|
||||
range.setEnd(p.lastChild, 4);
|
||||
var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
|
||||
var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
|
||||
|
||||
testTextRange(a11yrange, "selection range #6", p, 6, p, 10);
|
||||
|
||||
ok(!a11yrange.crop(getAccessible(a)), "Crop #6 succeeded while it shouldn't");
|
||||
|
||||
// crop a range by a table
|
||||
range.selectNode(getNode("c2"));
|
||||
var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
|
||||
var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
|
||||
|
||||
testTextRange(a11yrange, "selection range #7", document, 4, document, 5);
|
||||
|
||||
ok(a11yrange.crop(getAccessible("table")), "Range failed to crop #7.");
|
||||
testTextRange(a11yrange, "cropped range #7", "c2", 5, "c2", 6);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTest);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
title="Implement IAccessible2_3::selectionRanges"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1233118">Bug 1233118</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<p id="p1">text <a id="p2_a" href="www">link<img id="p2_img", src="../moz.png"></a> text</p>
|
||||
|
||||
<div id="c2">start<table id="table"><tr><td>cell</td></tr></table>end</div>
|
||||
</body>
|
||||
</html>
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "Accessible2_i.c"
|
||||
#include "Accessible2_2_i.c"
|
||||
#include "Accessible2_3_i.c"
|
||||
#include "AccessibleRole.h"
|
||||
#include "AccessibleStates.h"
|
||||
|
||||
@ -18,6 +19,7 @@
|
||||
#include "nsIAccessibleTypes.h"
|
||||
#include "mozilla/a11y/PDocAccessible.h"
|
||||
#include "Relation.h"
|
||||
#include "TextRange-inl.h"
|
||||
#include "nsAccessibilityService.h"
|
||||
|
||||
#include "nsIPersistentProperties2.h"
|
||||
@ -40,7 +42,9 @@ ia2Accessible::QueryInterface(REFIID iid, void** ppv)
|
||||
|
||||
*ppv = nullptr;
|
||||
|
||||
if (IID_IAccessible2_2 == iid)
|
||||
if (IID_IAccessible2_3 == iid)
|
||||
*ppv = static_cast<IAccessible2_3*>(this);
|
||||
else if (IID_IAccessible2_2 == iid)
|
||||
*ppv = static_cast<IAccessible2_2*>(this);
|
||||
else if (IID_IAccessible2 == iid && !Compatibility::IsIA2Off())
|
||||
*ppv = static_cast<IAccessible2*>(this);
|
||||
@ -749,6 +753,58 @@ ia2Accessible::get_relationTargetsOfType(BSTR aType,
|
||||
A11Y_TRYBLOCK_END
|
||||
}
|
||||
|
||||
STDMETHODIMP
|
||||
ia2Accessible::get_selectionRanges(IA2Range** aRanges,
|
||||
long *aNRanges)
|
||||
{
|
||||
A11Y_TRYBLOCK_BEGIN
|
||||
|
||||
if (!aRanges || !aNRanges || aNRanges <= 0)
|
||||
return E_INVALIDARG;
|
||||
|
||||
*aNRanges = 0;
|
||||
|
||||
AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
|
||||
if (acc->IsDefunct())
|
||||
return CO_E_OBJNOTCONNECTED;
|
||||
|
||||
nsAutoTArray<TextRange, 1> ranges;
|
||||
acc->Document()->SelectionRanges(&ranges);
|
||||
uint32_t len = ranges.Length();
|
||||
for (uint32_t idx = 0; idx < len; idx++) {
|
||||
if (!ranges[idx].Crop(acc)) {
|
||||
ranges.RemoveElementAt(idx);
|
||||
}
|
||||
}
|
||||
|
||||
*aNRanges = ranges.Length();
|
||||
*aRanges = static_cast<IA2Range*>(
|
||||
::CoTaskMemAlloc(sizeof(IA2Range) * *aNRanges));
|
||||
if (!*aRanges)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
for (uint32_t idx = 0; idx < static_cast<uint32_t>(*aNRanges); idx++) {
|
||||
AccessibleWrap* anchor =
|
||||
static_cast<AccessibleWrap*>(ranges[idx].StartContainer());
|
||||
(*aRanges)[idx].anchor = static_cast<IAccessible2*>(anchor);
|
||||
anchor->AddRef();
|
||||
|
||||
(*aRanges)[idx].anchorOffset = ranges[idx].StartOffset();
|
||||
|
||||
AccessibleWrap* active =
|
||||
static_cast<AccessibleWrap*>(ranges[idx].EndContainer());
|
||||
(*aRanges)[idx].active = static_cast<IAccessible2*>(active);
|
||||
active->AddRef();
|
||||
|
||||
(*aRanges)[idx].activeOffset = ranges[idx].EndOffset();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
|
||||
A11Y_TRYBLOCK_END
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Helpers
|
||||
|
||||
|
@ -9,13 +9,13 @@
|
||||
|
||||
#include "nsISupports.h"
|
||||
|
||||
#include "Accessible2_2.h"
|
||||
#include "Accessible2_3.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
class Attribute;
|
||||
|
||||
class ia2Accessible : public IAccessible2_2
|
||||
class ia2Accessible : public IAccessible2_3
|
||||
{
|
||||
public:
|
||||
|
||||
@ -104,6 +104,11 @@ public:
|
||||
/* [out, retval] */ long* nTargets
|
||||
);
|
||||
|
||||
// IAccessible2_3
|
||||
virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_selectionRanges(
|
||||
/* [out, size_is(,*nRanges)] */ IA2Range** ranges,
|
||||
/* [out, retval] */ long *nRanges);
|
||||
|
||||
// Helper method
|
||||
static HRESULT ConvertToIA2Attributes(nsIPersistentProperties* aAttributes,
|
||||
BSTR* aIA2Attributes);
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
#include "xpcAccessibleTextRange.h"
|
||||
|
||||
#include "TextRange.h"
|
||||
#include "TextRange-inl.h"
|
||||
#include "xpcAccessibleDocument.h"
|
||||
|
||||
#include "nsIMutableArray.h"
|
||||
@ -170,6 +170,16 @@ xpcAccessibleTextRange::Normalize(uint32_t aUnit)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
xpcAccessibleTextRange::Crop(nsIAccessible* aContainer, bool* aSuccess)
|
||||
{
|
||||
Accessible* container = aContainer->ToInternalAccessible();
|
||||
NS_ENSURE_TRUE(container, NS_ERROR_INVALID_ARG);
|
||||
|
||||
*aSuccess = mRange.Crop(container);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
xpcAccessibleTextRange::FindText(const nsAString& aText, bool aIsBackward,
|
||||
bool aIsIgnoreCase,
|
||||
|
@ -49,6 +49,7 @@ public:
|
||||
NS_IMETHOD MoveStart(uint32_t aUnit, int32_t aCount) final override;
|
||||
NS_IMETHOD MoveEnd(uint32_t aUnit, int32_t aCount) final override;
|
||||
NS_IMETHOD Normalize(uint32_t aUnit) final override;
|
||||
NS_IMETHOD Crop(nsIAccessible* aContainer, bool* aSuccess) final override;
|
||||
NS_IMETHOD FindText(const nsAString& aText, bool aIsBackward, bool aIsIgnoreCase,
|
||||
nsIAccessibleTextRange** aRange) final override;
|
||||
NS_IMETHOD FindAttr(uint32_t aAttr, nsIVariant* aVal, bool aIsBackward,
|
||||
|
47
other-licenses/ia2/Accessible2_3.idl
Normal file
47
other-licenses/ia2/Accessible2_3.idl
Normal file
@ -0,0 +1,47 @@
|
||||
import "objidl.idl";
|
||||
import "oaidl.idl";
|
||||
import "oleacc.idl";
|
||||
import "Accessible2_2.idl";
|
||||
|
||||
/**
|
||||
* This structure represents a directional range of the content. It is defined
|
||||
* by two points in the content, where each one is defined by an accessible
|
||||
* object and an offset relative to it. A typical case of a range point is
|
||||
* a text accessible and text offset within it.
|
||||
*
|
||||
* The "anchor" is one point of the range and typically remains constant.
|
||||
* The other point is the "active" point, which typically corresponds to
|
||||
* the user's focus or point of interest. The user moves the active point to
|
||||
* expand or collapse the range. In most cases, anchor is the start of the range
|
||||
* and active is the end. However, in case of selection, when selecting
|
||||
* backwards (e.g. pressing shift+left arrow in a text field), the start of
|
||||
* the range is the active point, as the user moves this to manipulate
|
||||
* the selection.
|
||||
*/
|
||||
typedef struct IA2Range {
|
||||
IUnknown* anchor;
|
||||
long anchorOffset;
|
||||
IUnknown* active;
|
||||
long activeOffset;
|
||||
} IA2Range;
|
||||
|
||||
/**
|
||||
* @brief This interface is an extension of IAccessible2_2 and IAccessible2
|
||||
* interfaces.
|
||||
*/
|
||||
[object, uuid(5BE18059-762E-4E73-9476-ABA294FED411)]
|
||||
interface IAccessible2_3 : IAccessible2_2
|
||||
{
|
||||
/**
|
||||
* @brief Returns an array of ranges for selections within the accessible.
|
||||
* @param [out] the array of selection ranges
|
||||
* @param [out] the array length
|
||||
* @retval S_OK
|
||||
* @retval S_FALSE returned if there is no selection within the accessible
|
||||
*/
|
||||
[propget] HRESULT selectionRanges
|
||||
(
|
||||
[out, size_is(,*nRanges)] IA2Range **ranges,
|
||||
[out, retval] long *nRanges
|
||||
);
|
||||
}
|
@ -71,6 +71,7 @@ library IAccessible2Lib
|
||||
importlib ("oleacc.dll");
|
||||
interface IAccessible2;
|
||||
interface IAccessible2_2;
|
||||
interface IAccessible2_3;
|
||||
interface IAccessibleAction;
|
||||
interface IAccessibleApplication;
|
||||
interface IAccessibleComponent;
|
||||
|
Loading…
Reference in New Issue
Block a user