зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1647483 - Add a TextRange getter to text selection change events. r=Jamie
This includes 3 changes: 1. Add a lazy ranges getter to AccTextSelChangeEvent. 2. Create an XPCOM interface for testing purposes. 3. Add IPDL bindings for passing ranges in e10s. Differential Revision: https://phabricator.services.mozilla.com/D80556
This commit is contained in:
Родитель
70c8aeee0b
Коммит
ccdf272301
|
@ -10,11 +10,15 @@
|
|||
#include "DocAccessible.h"
|
||||
#include "xpcAccEvents.h"
|
||||
#include "States.h"
|
||||
#include "TextRange.h"
|
||||
#include "xpcAccessibleDocument.h"
|
||||
#include "xpcAccessibleTextRange.h"
|
||||
|
||||
#include "mozilla/dom/Selection.h"
|
||||
#include "mozilla/dom/UserActivation.h"
|
||||
|
||||
#include "nsIMutableArray.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
|
@ -134,6 +138,11 @@ bool AccTextSelChangeEvent::IsCaretMoveOnly() const {
|
|||
nsISelectionListener::COLLAPSETOEND_REASON)) == 0);
|
||||
}
|
||||
|
||||
void AccTextSelChangeEvent::SelectionRanges(
|
||||
nsTArray<TextRange>* aRanges) const {
|
||||
TextRange::TextRangesFromSelection(mSel, aRanges);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccSelChangeEvent
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -235,6 +244,24 @@ already_AddRefed<nsIAccessibleEvent> a11y::MakeXPCEvent(AccEvent* aEvent) {
|
|||
return xpEvent.forget();
|
||||
}
|
||||
|
||||
if (eventGroup & (1 << AccEvent::eTextSelChangeEvent)) {
|
||||
AccTextSelChangeEvent* tsc = downcast_accEvent(aEvent);
|
||||
AutoTArray<TextRange, 1> ranges;
|
||||
tsc->SelectionRanges(&ranges);
|
||||
|
||||
nsCOMPtr<nsIMutableArray> xpcRanges =
|
||||
do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
uint32_t len = ranges.Length();
|
||||
for (uint32_t idx = 0; idx < len; idx++) {
|
||||
xpcRanges->AppendElement(
|
||||
new xpcAccessibleTextRange(std::move(ranges[idx])));
|
||||
}
|
||||
|
||||
xpEvent = new xpcAccTextSelectionChangeEvent(
|
||||
type, ToXPC(acc), ToXPCDocument(doc), node, fromUser, xpcRanges);
|
||||
return xpEvent.forget();
|
||||
}
|
||||
|
||||
if (eventGroup & (1 << AccEvent::eVirtualCursorChangeEvent)) {
|
||||
AccVCChangeEvent* vcc = downcast_accEvent(aEvent);
|
||||
xpEvent = new xpcAccVirtualCursorChangeEvent(
|
||||
|
|
|
@ -21,6 +21,7 @@ namespace a11y {
|
|||
|
||||
class DocAccessible;
|
||||
class EventQueue;
|
||||
class TextRange;
|
||||
|
||||
// Constants used to point whether the event is from user input.
|
||||
enum EIsFromUserInput {
|
||||
|
@ -380,6 +381,11 @@ class AccTextSelChangeEvent : public AccEvent {
|
|||
*/
|
||||
bool IsCaretMoveOnly() const;
|
||||
|
||||
/**
|
||||
* Return selection ranges in document/control.
|
||||
*/
|
||||
void SelectionRanges(nsTArray<a11y::TextRange>* aRanges) const;
|
||||
|
||||
private:
|
||||
RefPtr<dom::Selection> mSel;
|
||||
int32_t mReason;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "TextRange-inl.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "mozilla/dom/Selection.h"
|
||||
#include "nsAccUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -233,6 +234,37 @@ void TextRange::Select() const {}
|
|||
|
||||
void TextRange::ScrollIntoView(EHowToAlign aHow) const {}
|
||||
|
||||
void TextRange::TextRangesFromSelection(dom::Selection* aSelection,
|
||||
nsTArray<TextRange>* aRanges) {
|
||||
MOZ_ASSERT(aRanges->Length() == 0, "TextRange array supposed to be empty");
|
||||
|
||||
aRanges->SetCapacity(aSelection->RangeCount());
|
||||
|
||||
for (uint32_t idx = 0; idx < aSelection->RangeCount(); idx++) {
|
||||
const nsRange* DOMRange = aSelection->GetRangeAt(idx);
|
||||
HyperTextAccessible* startContainer =
|
||||
nsAccUtils::GetTextContainer(DOMRange->GetStartContainer());
|
||||
HyperTextAccessible* endContainer =
|
||||
nsAccUtils::GetTextContainer(DOMRange->GetEndContainer());
|
||||
HyperTextAccessible* commonAncestor = nsAccUtils::GetTextContainer(
|
||||
DOMRange->GetClosestCommonInclusiveAncestor());
|
||||
if (!startContainer || !endContainer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int32_t startOffset = startContainer->DOMPointToOffset(
|
||||
DOMRange->GetStartContainer(), DOMRange->StartOffset(), false);
|
||||
int32_t endOffset = endContainer->DOMPointToOffset(
|
||||
DOMRange->GetEndContainer(), DOMRange->EndOffset(), true);
|
||||
|
||||
TextRange tr(commonAncestor && commonAncestor->IsTextField()
|
||||
? commonAncestor
|
||||
: startContainer->Document(),
|
||||
startContainer, startOffset, endContainer, endOffset);
|
||||
*(aRanges->AppendElement()) = std::move(tr);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// pivate
|
||||
|
||||
|
|
|
@ -214,6 +214,9 @@ class TextRange final {
|
|||
mStartOffset = aOffset;
|
||||
}
|
||||
|
||||
static void TextRangesFromSelection(dom::Selection* aSelection,
|
||||
nsTArray<TextRange>* aRanges);
|
||||
|
||||
private:
|
||||
TextRange(const TextRange& aRange) = delete;
|
||||
TextRange& operator=(const TextRange& aRange) = delete;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "RootAccessible.h"
|
||||
#include "States.h"
|
||||
#include "StyleInfo.h"
|
||||
#include "TextRange.h"
|
||||
#include "TableAccessible.h"
|
||||
#include "TableCellAccessible.h"
|
||||
#include "TreeWalker.h"
|
||||
|
@ -894,6 +895,27 @@ nsresult Accessible::HandleAccEvent(AccEvent* aEvent) {
|
|||
announcementEvent->Priority());
|
||||
break;
|
||||
}
|
||||
case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED: {
|
||||
AccTextSelChangeEvent* textSelChangeEvent = downcast_accEvent(aEvent);
|
||||
AutoTArray<TextRange, 1> ranges;
|
||||
textSelChangeEvent->SelectionRanges(&ranges);
|
||||
nsTArray<TextRangeData> textRangeData(ranges.Length());
|
||||
for (size_t i = 0; i < ranges.Length(); i++) {
|
||||
const TextRange& range = ranges.ElementAt(i);
|
||||
Accessible* start = range.StartContainer();
|
||||
Accessible* end = range.EndContainer();
|
||||
textRangeData.AppendElement(TextRangeData(
|
||||
start->IsDoc() && start->AsDoc()->IPCDoc()
|
||||
? 0
|
||||
: reinterpret_cast<uint64_t>(start->UniqueID()),
|
||||
end->IsDoc() && end->AsDoc()->IPCDoc()
|
||||
? 0
|
||||
: reinterpret_cast<uint64_t>(end->UniqueID()),
|
||||
range.StartOffset(), range.EndOffset()));
|
||||
}
|
||||
ipcDoc->SendTextSelectionChangeEvent(id, textRangeData);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
ipcDoc->SendEvent(id, aEvent->GetEventType());
|
||||
|
|
|
@ -1703,32 +1703,12 @@ void HyperTextAccessible::EnclosingRange(a11y::TextRange& aRange) const {
|
|||
|
||||
void HyperTextAccessible::SelectionRanges(
|
||||
nsTArray<a11y::TextRange>* aRanges) const {
|
||||
MOZ_ASSERT(aRanges->Length() == 0, "TextRange array supposed to be empty");
|
||||
|
||||
dom::Selection* sel = DOMSelection();
|
||||
if (!sel) return;
|
||||
|
||||
aRanges->SetCapacity(sel->RangeCount());
|
||||
|
||||
for (uint32_t idx = 0; idx < sel->RangeCount(); idx++) {
|
||||
const nsRange* DOMRange = sel->GetRangeAt(idx);
|
||||
HyperTextAccessible* startContainer =
|
||||
nsAccUtils::GetTextContainer(DOMRange->GetStartContainer());
|
||||
HyperTextAccessible* endContainer =
|
||||
nsAccUtils::GetTextContainer(DOMRange->GetEndContainer());
|
||||
if (!startContainer || !endContainer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int32_t startOffset = startContainer->DOMPointToOffset(
|
||||
DOMRange->GetStartContainer(), DOMRange->StartOffset(), false);
|
||||
int32_t endOffset = endContainer->DOMPointToOffset(
|
||||
DOMRange->GetEndContainer(), DOMRange->EndOffset(), true);
|
||||
|
||||
TextRange tr(IsTextField() ? const_cast<HyperTextAccessible*>(this) : mDoc,
|
||||
startContainer, startOffset, endContainer, endOffset);
|
||||
*(aRanges->AppendElement()) = std::move(tr);
|
||||
if (!sel) {
|
||||
return;
|
||||
}
|
||||
|
||||
TextRange::TextRangesFromSelection(sel, aRanges);
|
||||
}
|
||||
|
||||
void HyperTextAccessible::VisibleRanges(
|
||||
|
|
|
@ -36,6 +36,7 @@ XPIDL_SOURCES += [
|
|||
'nsIAccessibleText.idl',
|
||||
'nsIAccessibleTextChangeEvent.idl',
|
||||
'nsIAccessibleTextRange.idl',
|
||||
'nsIAccessibleTextSelectionChangeEvent.idl',
|
||||
'nsIAccessibleTypes.idl',
|
||||
'nsIAccessibleValue.idl',
|
||||
'nsIAccessibleVirtualCursorChangeEvent.idl',
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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 "nsIAccessibleEvent.idl"
|
||||
|
||||
interface nsIArray;
|
||||
|
||||
/**
|
||||
* Fired when the caret changes position in text.
|
||||
*/
|
||||
[scriptable, builtinclass, uuid(011f98e2-2beb-4ec3-97a5-f154f624e112)]
|
||||
interface nsIAccessibleTextSelectionChangeEvent: nsIAccessibleEvent
|
||||
{
|
||||
/**
|
||||
* Return an array of disjoint ranges for selected text within the
|
||||
* source of this event.
|
||||
*/
|
||||
readonly attribute nsIArray selectionRanges;
|
||||
};
|
|
@ -11,7 +11,7 @@
|
|||
#include "xpcAccessibleDocument.h"
|
||||
#include "xpcAccEvents.h"
|
||||
#include "nsAccUtils.h"
|
||||
#include "nsCoreUtils.h"
|
||||
#include "TextRange.h"
|
||||
|
||||
#if defined(XP_WIN)
|
||||
# include "AccessibleWrap.h"
|
||||
|
@ -499,6 +499,15 @@ mozilla::ipc::IPCResult DocAccessibleParent::RecvAnnouncementEvent(
|
|||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult DocAccessibleParent::RecvTextSelectionChangeEvent(
|
||||
const uint64_t& aID, nsTArray<TextRangeData>&& aSelection) {
|
||||
// XXX: nsIAccessibleTextRange is not e10s friendly, so don't bother
|
||||
// supporting nsIAccessibleTextSelectionChangeEvent for now.
|
||||
// This is a placeholder for potential platform support.
|
||||
return RecvEvent(aID, nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
mozilla::ipc::IPCResult DocAccessibleParent::RecvRoleChangedEvent(
|
||||
|
|
|
@ -140,6 +140,9 @@ class DocAccessibleParent : public ProxyAccessible,
|
|||
virtual mozilla::ipc::IPCResult RecvAnnouncementEvent(
|
||||
const uint64_t& aID, const nsString& aAnnouncement,
|
||||
const uint16_t& aPriority) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvTextSelectionChangeEvent(
|
||||
const uint64_t& aID, nsTArray<TextRangeData>&& aSelection) override;
|
||||
#endif
|
||||
|
||||
mozilla::ipc::IPCResult RecvRoleChangedEvent(const a11y::role& aRole) final;
|
||||
|
|
|
@ -69,6 +69,14 @@ struct RelationTargets
|
|||
uint64_t[] Targets;
|
||||
};
|
||||
|
||||
struct TextRangeData
|
||||
{
|
||||
uint64_t StartID;
|
||||
uint64_t EndID;
|
||||
int32_t StartOffset;
|
||||
int32_t EndOffset;
|
||||
};
|
||||
|
||||
nested(upto inside_sync) sync protocol PDocAccessible
|
||||
{
|
||||
manager PBrowser;
|
||||
|
@ -104,6 +112,7 @@ parent:
|
|||
async AnnouncementEvent(uint64_t aID,
|
||||
nsString aAnnouncement,
|
||||
uint16_t aPriority);
|
||||
async TextSelectionChangeEvent(uint64_t aID, TextRangeData[] aSelection);
|
||||
|
||||
/*
|
||||
* Tell the parent document to bind the existing document as a new child
|
||||
|
|
|
@ -22,6 +22,8 @@ const nsIAccessibleStateChangeEvent = Ci.nsIAccessibleStateChangeEvent;
|
|||
const nsIAccessibleCaretMoveEvent = Ci.nsIAccessibleCaretMoveEvent;
|
||||
const nsIAccessibleScrollingEvent = Ci.nsIAccessibleScrollingEvent;
|
||||
const nsIAccessibleTextChangeEvent = Ci.nsIAccessibleTextChangeEvent;
|
||||
const nsIAccessibleTextSelectionChangeEvent =
|
||||
Ci.nsIAccessibleTextSelectionChangeEvent;
|
||||
const nsIAccessibleVirtualCursorChangeEvent =
|
||||
Ci.nsIAccessibleVirtualCursorChangeEvent;
|
||||
const nsIAccessibleObjectAttributeChangedEvent =
|
||||
|
|
|
@ -2045,7 +2045,15 @@ function asyncCaretMoveChecker(aCaretOffset, aTargetOrFunc, aTargetFuncArg) {
|
|||
/**
|
||||
* Text selection change checker.
|
||||
*/
|
||||
function textSelectionChecker(aID, aStartOffset, aEndOffset) {
|
||||
function textSelectionChecker(
|
||||
aID,
|
||||
aStartOffset,
|
||||
aEndOffset,
|
||||
aRangeStartContainer,
|
||||
aRangeStartOffset,
|
||||
aRangeEndContainer,
|
||||
aRangeEndOffset
|
||||
) {
|
||||
this.__proto__ = new invokerChecker(EVENT_TEXT_SELECTION_CHANGED, aID);
|
||||
|
||||
this.check = function textSelectionChecker_check(aEvent) {
|
||||
|
@ -2053,6 +2061,24 @@ function textSelectionChecker(aID, aStartOffset, aEndOffset) {
|
|||
ok(true, "Collapsed selection triggered text selection change event.");
|
||||
} else {
|
||||
testTextGetSelection(aID, aStartOffset, aEndOffset, 0);
|
||||
|
||||
// Test selection test range
|
||||
let selectionRanges = aEvent.QueryInterface(
|
||||
nsIAccessibleTextSelectionChangeEvent
|
||||
).selectionRanges;
|
||||
let range = selectionRanges.queryElementAt(0, nsIAccessibleTextRange);
|
||||
is(
|
||||
range.startContainer,
|
||||
getAccessible(aRangeStartContainer),
|
||||
"correct range start container"
|
||||
);
|
||||
is(range.startOffset, aRangeStartOffset, "correct range start offset");
|
||||
is(range.endOffset, aRangeEndOffset, "correct range end offset");
|
||||
is(
|
||||
range.endContainer,
|
||||
getAccessible(aRangeEndContainer),
|
||||
"correct range end container"
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -34,15 +34,15 @@
|
|||
gQueue = new eventQueue();
|
||||
|
||||
gQueue.push(new synthClick("c1_p1", getOnclickSeq("c1_p1")));
|
||||
gQueue.push(new synthDownKey("c1", new textSelectionChecker("c1", 0, 1), { shiftKey: true }));
|
||||
gQueue.push(new synthDownKey("c1", new textSelectionChecker("c1", 0, 2), { shiftKey: true }));
|
||||
gQueue.push(new synthDownKey("c1", new textSelectionChecker("c1", 0, 1, "c1_p1", 0, "c1_p2", 0), { shiftKey: true }));
|
||||
gQueue.push(new synthDownKey("c1", new textSelectionChecker("c1", 0, 2, "c1_p1", 0, "c1_p2", 9), { shiftKey: true }));
|
||||
|
||||
gQueue.push(new synthClick("ta1", getOnclickSeq("ta1")));
|
||||
gQueue.push(new synthRightKey("ta1",
|
||||
new textSelectionChecker("ta1", 0, 1),
|
||||
new textSelectionChecker("ta1", 0, 1, "ta1", 0, "ta1", 1),
|
||||
{ shiftKey: true }));
|
||||
gQueue.push(new synthLeftKey("ta1",
|
||||
[new textSelectionChecker("ta1", 0, 0),
|
||||
[new textSelectionChecker("ta1", 0, 0, "ta1", 0, "ta1", 0),
|
||||
new caretMoveChecker(0, "ta1")]));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
|
|
|
@ -10,6 +10,7 @@ simple_events = [
|
|||
'Event',
|
||||
'StateChangeEvent',
|
||||
'TextChangeEvent',
|
||||
'TextSelectionChangeEvent',
|
||||
'HideEvent',
|
||||
'CaretMoveEvent',
|
||||
'ObjectAttributeChangedEvent',
|
||||
|
|
|
@ -27,6 +27,9 @@ class TextRange;
|
|||
|
||||
class xpcAccessibleTextRange final : public nsIAccessibleTextRange {
|
||||
public:
|
||||
explicit xpcAccessibleTextRange(TextRange&& aRange)
|
||||
: mRange(std::forward<TextRange>(aRange)) {}
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(xpcAccessibleTextRange)
|
||||
|
||||
|
@ -61,8 +64,6 @@ class xpcAccessibleTextRange final : public nsIAccessibleTextRange {
|
|||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ACCESSIBLETEXTRANGE_IMPL_IID)
|
||||
|
||||
private:
|
||||
explicit xpcAccessibleTextRange(TextRange&& aRange)
|
||||
: mRange(std::forward<TextRange>(aRange)) {}
|
||||
xpcAccessibleTextRange() {}
|
||||
|
||||
~xpcAccessibleTextRange() {}
|
||||
|
|
Загрузка…
Ссылка в новой задаче