зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1261425 - coalesce mutation events by a tree structure, r=yzen
This commit is contained in:
Родитель
6a9e630b92
Коммит
51947ead2c
|
@ -77,28 +77,6 @@ AccTextChangeEvent::
|
|||
(states::FOCUSED | states::EDITABLE);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccReorderEvent
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint32_t
|
||||
AccReorderEvent::IsShowHideEventTarget(const Accessible* aTarget) const
|
||||
{
|
||||
uint32_t count = mDependentEvents.Length();
|
||||
for (uint32_t index = count - 1; index < count; index--) {
|
||||
if (mDependentEvents[index]->mAccessible == aTarget) {
|
||||
uint32_t eventType = mDependentEvents[index]->mEventType;
|
||||
if (eventType == nsIAccessibleEvent::EVENT_SHOW ||
|
||||
eventType == nsIAccessibleEvent::EVENT_HIDE) {
|
||||
return mDependentEvents[index]->mEventType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccHideEvent
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -130,7 +130,7 @@ protected:
|
|||
RefPtr<Accessible> mAccessible;
|
||||
|
||||
friend class EventQueue;
|
||||
friend class AccReorderEvent;
|
||||
friend class EventTree;
|
||||
};
|
||||
|
||||
|
||||
|
@ -201,8 +201,7 @@ private:
|
|||
bool mIsInserted;
|
||||
nsString mModifiedText;
|
||||
|
||||
friend class EventQueue;
|
||||
friend class AccReorderEvent;
|
||||
friend class EventTree;
|
||||
};
|
||||
|
||||
|
||||
|
@ -239,7 +238,7 @@ protected:
|
|||
RefPtr<Accessible> mParent;
|
||||
RefPtr<AccTextChangeEvent> mTextChangeEvent;
|
||||
|
||||
friend class EventQueue;
|
||||
friend class EventTree;
|
||||
};
|
||||
|
||||
|
||||
|
@ -269,7 +268,7 @@ protected:
|
|||
RefPtr<Accessible> mNextSibling;
|
||||
RefPtr<Accessible> mPrevSibling;
|
||||
|
||||
friend class EventQueue;
|
||||
friend class EventTree;
|
||||
};
|
||||
|
||||
|
||||
|
@ -312,37 +311,6 @@ public:
|
|||
{
|
||||
return AccEvent::GetEventGroups() | (1U << eReorderEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get connected with mutation event.
|
||||
*/
|
||||
void AddSubMutationEvent(AccMutationEvent* aEvent)
|
||||
{ mDependentEvents.AppendElement(aEvent); }
|
||||
|
||||
/**
|
||||
* Do not emit the reorder event and its connected mutation events.
|
||||
*/
|
||||
void DoNotEmitAll()
|
||||
{
|
||||
mEventRule = AccEvent::eDoNotEmit;
|
||||
uint32_t eventsCount = mDependentEvents.Length();
|
||||
for (uint32_t idx = 0; idx < eventsCount; idx++)
|
||||
mDependentEvents[idx]->mEventRule = AccEvent::eDoNotEmit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given accessible is a target of connected mutation
|
||||
* event.
|
||||
*/
|
||||
uint32_t IsShowHideEventTarget(const Accessible* aTarget) const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Show and hide events causing this reorder event.
|
||||
*/
|
||||
nsTArray<AccMutationEvent*> mDependentEvents;
|
||||
|
||||
friend class EventQueue;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -39,20 +39,25 @@ EventQueue::PushEvent(AccEvent* aEvent)
|
|||
// Filter events.
|
||||
CoalesceEvents();
|
||||
|
||||
if (aEvent->mEventRule != AccEvent::eDoNotEmit &&
|
||||
(aEvent->mEventType == nsIAccessibleEvent::EVENT_NAME_CHANGE ||
|
||||
aEvent->mEventType == nsIAccessibleEvent::EVENT_TEXT_REMOVED ||
|
||||
aEvent->mEventType == nsIAccessibleEvent::EVENT_TEXT_INSERTED)) {
|
||||
PushNameChange(aEvent->mAccessible);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
EventQueue::PushNameChange(Accessible* aTarget)
|
||||
{
|
||||
// Fire name change event on parent given that this event hasn't been
|
||||
// coalesced, the parent's name was calculated from its subtree, and the
|
||||
// subtree was changed.
|
||||
Accessible* target = aEvent->mAccessible;
|
||||
if (aEvent->mEventRule != AccEvent::eDoNotEmit &&
|
||||
target->HasNameDependentParent() &&
|
||||
(aEvent->mEventType == nsIAccessibleEvent::EVENT_NAME_CHANGE ||
|
||||
aEvent->mEventType == nsIAccessibleEvent::EVENT_TEXT_REMOVED ||
|
||||
aEvent->mEventType == nsIAccessibleEvent::EVENT_TEXT_INSERTED ||
|
||||
aEvent->mEventType == nsIAccessibleEvent::EVENT_SHOW ||
|
||||
aEvent->mEventType == nsIAccessibleEvent::EVENT_HIDE)) {
|
||||
if (aTarget->HasNameDependentParent()) {
|
||||
// Only continue traversing up the tree if it's possible that the parent
|
||||
// accessible's name can depend on this accessible's name.
|
||||
Accessible* parent = target->Parent();
|
||||
Accessible* parent = aTarget->Parent();
|
||||
while (parent &&
|
||||
nsTextEquivUtils::HasNameRule(parent, eNameFromSubtreeIfReqRule)) {
|
||||
// Test possible name dependent parent.
|
||||
|
@ -63,21 +68,14 @@ EventQueue::PushEvent(AccEvent* aEvent)
|
|||
if (nameFlag == eNameFromSubtree) {
|
||||
RefPtr<AccEvent> nameChangeEvent =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, parent);
|
||||
PushEvent(nameChangeEvent);
|
||||
return PushEvent(nameChangeEvent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
parent = parent->Parent();
|
||||
}
|
||||
}
|
||||
|
||||
// Associate text change with hide event if it wasn't stolen from hiding
|
||||
// siblings during coalescence.
|
||||
AccMutationEvent* showOrHideEvent = downcast_accEvent(aEvent);
|
||||
if (showOrHideEvent && !showOrHideEvent->mTextChangeEvent)
|
||||
CreateTextChangeEventFor(showOrHideEvent);
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -92,46 +90,12 @@ EventQueue::CoalesceEvents()
|
|||
|
||||
switch(tailEvent->mEventRule) {
|
||||
case AccEvent::eCoalesceReorder:
|
||||
CoalesceReorderEvents(tailEvent);
|
||||
MOZ_ASSERT(tailEvent->mAccessible->IsApplication() ||
|
||||
tailEvent->mAccessible->IsOuterDoc() ||
|
||||
tailEvent->mAccessible->IsXULTree(),
|
||||
"Only app or outerdoc accessible reorder events are in the queue");
|
||||
break; // case eCoalesceReorder
|
||||
|
||||
case AccEvent::eCoalesceMutationTextChange:
|
||||
{
|
||||
for (uint32_t index = tail - 1; index < tail; index--) {
|
||||
AccEvent* thisEvent = mEvents[index];
|
||||
if (thisEvent->mEventRule != tailEvent->mEventRule)
|
||||
continue;
|
||||
|
||||
// We don't currently coalesce text change events from show/hide events.
|
||||
if (thisEvent->mEventType != tailEvent->mEventType)
|
||||
continue;
|
||||
|
||||
// Show events may be duped because of reinsertion (removal is ignored
|
||||
// because initial insertion is not processed). Ignore initial
|
||||
// insertion.
|
||||
if (thisEvent->mAccessible == tailEvent->mAccessible)
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
|
||||
AccMutationEvent* tailMutationEvent = downcast_accEvent(tailEvent);
|
||||
AccMutationEvent* thisMutationEvent = downcast_accEvent(thisEvent);
|
||||
if (tailMutationEvent->mParent != thisMutationEvent->mParent)
|
||||
continue;
|
||||
|
||||
// Coalesce text change events for hide and show events.
|
||||
if (thisMutationEvent->IsHide()) {
|
||||
AccHideEvent* tailHideEvent = downcast_accEvent(tailEvent);
|
||||
AccHideEvent* thisHideEvent = downcast_accEvent(thisEvent);
|
||||
CoalesceTextChangeEventsFor(tailHideEvent, thisHideEvent);
|
||||
break;
|
||||
}
|
||||
|
||||
AccShowEvent* tailShowEvent = downcast_accEvent(tailEvent);
|
||||
AccShowEvent* thisShowEvent = downcast_accEvent(thisEvent);
|
||||
CoalesceTextChangeEventsFor(tailShowEvent, thisShowEvent);
|
||||
break;
|
||||
}
|
||||
} break; // case eCoalesceMutationTextChange
|
||||
|
||||
case AccEvent::eCoalesceOfSameType:
|
||||
{
|
||||
// Coalesce old events by newer event.
|
||||
|
@ -226,93 +190,6 @@ EventQueue::CoalesceEvents()
|
|||
} // switch
|
||||
}
|
||||
|
||||
void
|
||||
EventQueue::CoalesceReorderEvents(AccEvent* aTailEvent)
|
||||
{
|
||||
uint32_t count = mEvents.Length();
|
||||
for (uint32_t index = count - 2; index < count; index--) {
|
||||
AccEvent* thisEvent = mEvents[index];
|
||||
|
||||
// Skip events of different types and targeted to application accessible.
|
||||
if (thisEvent->mEventType != aTailEvent->mEventType ||
|
||||
thisEvent->mAccessible->IsApplication())
|
||||
continue;
|
||||
|
||||
// If thisEvent target is not in document longer, i.e. if it was
|
||||
// removed from the tree then do not emit the event.
|
||||
if (!thisEvent->mAccessible->IsDoc() &&
|
||||
!thisEvent->mAccessible->IsInDocument()) {
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Coalesce earlier event of the same target.
|
||||
if (thisEvent->mAccessible == aTailEvent->mAccessible) {
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If tailEvent contains thisEvent
|
||||
// then
|
||||
// if show or hide of tailEvent contains a grand parent of thisEvent
|
||||
// then ignore thisEvent and its show and hide events
|
||||
// otherwise ignore thisEvent but not its show and hide events
|
||||
Accessible* thisParent = thisEvent->mAccessible;
|
||||
while (thisParent && thisParent != mDocument) {
|
||||
if (thisParent->Parent() == aTailEvent->mAccessible) {
|
||||
AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent);
|
||||
uint32_t eventType = tailReorder->IsShowHideEventTarget(thisParent);
|
||||
|
||||
// It can be either hide or show events which may occur because of
|
||||
// accessible reparenting.
|
||||
if (eventType == nsIAccessibleEvent::EVENT_SHOW ||
|
||||
eventType == nsIAccessibleEvent::EVENT_HIDE) {
|
||||
AccReorderEvent* thisReorder = downcast_accEvent(thisEvent);
|
||||
thisReorder->DoNotEmitAll();
|
||||
} else {
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
thisParent = thisParent->Parent();
|
||||
}
|
||||
|
||||
// If tailEvent is contained by thisEvent
|
||||
// then
|
||||
// if show of thisEvent contains the tailEvent
|
||||
// then ignore tailEvent
|
||||
// if hide of thisEvent contains the tailEvent
|
||||
// then assert
|
||||
// otherwise ignore tailEvent but not its show and hide events
|
||||
Accessible* tailParent = aTailEvent->mAccessible;
|
||||
while (tailParent && tailParent != mDocument) {
|
||||
if (tailParent->Parent() == thisEvent->mAccessible) {
|
||||
AccReorderEvent* thisReorder = downcast_accEvent(thisEvent);
|
||||
AccReorderEvent* tailReorder = downcast_accEvent(aTailEvent);
|
||||
uint32_t eventType = thisReorder->IsShowHideEventTarget(tailParent);
|
||||
if (eventType == nsIAccessibleEvent::EVENT_SHOW) {
|
||||
tailReorder->DoNotEmitAll();
|
||||
}
|
||||
else if (eventType == nsIAccessibleEvent::EVENT_HIDE) {
|
||||
NS_ERROR("Accessible tree was modified after it was removed! Huh?");
|
||||
}
|
||||
else {
|
||||
aTailEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
mEvents[index].swap(mEvents[count - 1]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
tailParent = tailParent->Parent();
|
||||
}
|
||||
|
||||
} // for (index)
|
||||
}
|
||||
|
||||
void
|
||||
EventQueue::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
|
||||
AccSelChangeEvent* aThisEvent,
|
||||
|
@ -393,90 +270,6 @@ EventQueue::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
|
|||
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
|
||||
}
|
||||
|
||||
void
|
||||
EventQueue::CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
|
||||
AccHideEvent* aThisEvent)
|
||||
{
|
||||
// XXX: we need a way to ignore SplitNode and JoinNode() when they do not
|
||||
// affect the text within the hypertext.
|
||||
|
||||
AccTextChangeEvent* textEvent = aThisEvent->mTextChangeEvent;
|
||||
if (!textEvent)
|
||||
return;
|
||||
|
||||
if (aThisEvent->mNextSibling == aTailEvent->mAccessible) {
|
||||
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText);
|
||||
|
||||
} else if (aThisEvent->mPrevSibling == aTailEvent->mAccessible) {
|
||||
uint32_t oldLen = textEvent->GetLength();
|
||||
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText);
|
||||
textEvent->mStart -= textEvent->GetLength() - oldLen;
|
||||
}
|
||||
|
||||
aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent);
|
||||
}
|
||||
|
||||
void
|
||||
EventQueue::CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
|
||||
AccShowEvent* aThisEvent)
|
||||
{
|
||||
AccTextChangeEvent* textEvent = aThisEvent->mTextChangeEvent;
|
||||
if (!textEvent)
|
||||
return;
|
||||
|
||||
if (aTailEvent->mAccessible->IndexInParent() ==
|
||||
aThisEvent->mAccessible->IndexInParent() + 1) {
|
||||
// If tail target was inserted after this target, i.e. tail target is next
|
||||
// sibling of this target.
|
||||
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText);
|
||||
|
||||
} else if (aTailEvent->mAccessible->IndexInParent() ==
|
||||
aThisEvent->mAccessible->IndexInParent() -1) {
|
||||
// If tail target was inserted before this target, i.e. tail target is
|
||||
// previous sibling of this target.
|
||||
nsAutoString startText;
|
||||
aTailEvent->mAccessible->AppendTextTo(startText);
|
||||
textEvent->mModifiedText = startText + textEvent->mModifiedText;
|
||||
textEvent->mStart -= startText.Length();
|
||||
}
|
||||
|
||||
aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent);
|
||||
}
|
||||
|
||||
void
|
||||
EventQueue::CreateTextChangeEventFor(AccMutationEvent* aEvent)
|
||||
{
|
||||
Accessible* container = aEvent->mAccessible->Parent();
|
||||
if (!container)
|
||||
return;
|
||||
|
||||
HyperTextAccessible* textAccessible = container->AsHyperText();
|
||||
if (!textAccessible)
|
||||
return;
|
||||
|
||||
// Don't fire event for the first html:br in an editor.
|
||||
if (aEvent->mAccessible->Role() == roles::WHITESPACE) {
|
||||
nsCOMPtr<nsIEditor> editor = textAccessible->GetEditor();
|
||||
if (editor) {
|
||||
bool isEmpty = false;
|
||||
editor->GetDocumentIsEmpty(&isEmpty);
|
||||
if (isEmpty)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t offset = textAccessible->GetChildOffset(aEvent->mAccessible);
|
||||
|
||||
nsAutoString text;
|
||||
aEvent->mAccessible->AppendTextTo(text);
|
||||
if (text.IsEmpty())
|
||||
return;
|
||||
|
||||
aEvent->mTextChangeEvent =
|
||||
new AccTextChangeEvent(textAccessible, offset, text, aEvent->IsShow(),
|
||||
aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// EventQueue: event queue
|
||||
|
||||
|
@ -539,18 +332,6 @@ EventQueue::ProcessEventQueue()
|
|||
}
|
||||
|
||||
nsEventShell::FireEvent(event);
|
||||
|
||||
// Fire text change events.
|
||||
AccMutationEvent* mutationEvent = downcast_accEvent(event);
|
||||
if (mutationEvent) {
|
||||
if (mutationEvent->mTextChangeEvent)
|
||||
nsEventShell::FireEvent(mutationEvent->mTextChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
AccHideEvent* hideEvent = downcast_accEvent(event);
|
||||
if (hideEvent && hideEvent->NeedsShutdown()) {
|
||||
mDocument->ShutdownChildrenInSubtree(event->mAccessible);
|
||||
}
|
||||
|
||||
if (!mDocument)
|
||||
|
|
|
@ -26,6 +26,11 @@ protected:
|
|||
*/
|
||||
bool PushEvent(AccEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Puts a name change event into the queue, if needed.
|
||||
*/
|
||||
bool PushNameChange(Accessible* aTarget);
|
||||
|
||||
/**
|
||||
* Process events from the queue and fires events.
|
||||
*/
|
||||
|
@ -53,23 +58,7 @@ private:
|
|||
AccSelChangeEvent* aThisEvent,
|
||||
uint32_t aThisIndex);
|
||||
|
||||
/**
|
||||
* Coalesce text change events caused by sibling hide events.
|
||||
*/
|
||||
void CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
|
||||
AccHideEvent* aThisEvent);
|
||||
void CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
|
||||
AccShowEvent* aThisEvent);
|
||||
|
||||
/**
|
||||
* Create text change event caused by hide or show event. When a node is
|
||||
* hidden/removed or shown/appended, the text in an ancestor hyper text will
|
||||
* lose or get new characters.
|
||||
*/
|
||||
void CreateTextChangeEventFor(AccMutationEvent* aEvent);
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The document accessible reference owning this queue.
|
||||
*/
|
||||
|
@ -79,7 +68,7 @@ protected:
|
|||
* Pending events array. Don't make this an AutoTArray; we use
|
||||
* SwapElements() on it.
|
||||
*/
|
||||
nsTArray<RefPtr<AccEvent> > mEvents;
|
||||
nsTArray<RefPtr<AccEvent>> mEvents;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
|
|
|
@ -0,0 +1,536 @@
|
|||
/* -*- 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 "EventTree.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "nsEventShell.h"
|
||||
#include "DocAccessible.h"
|
||||
#ifdef A11Y_LOG
|
||||
#include "Logging.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TreeMutation class
|
||||
|
||||
EventTree* const TreeMutation::kNoEventTree = reinterpret_cast<EventTree*>(-1);
|
||||
|
||||
TreeMutation::TreeMutation(Accessible* aParent, bool aNoEvents) :
|
||||
mParent(aParent), mStartIdx(UINT32_MAX),
|
||||
mStateFlagsCopy(mParent->mStateFlags),
|
||||
mEventTree(aNoEvents ? kNoEventTree : nullptr)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
mIsDone = false;
|
||||
#endif
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
if (mEventTree != kNoEventTree && logging::IsEnabled(logging::eEventTree)) {
|
||||
logging::MsgBegin("EVENTS_TREE", "reordering tree before");
|
||||
logging::AccessibleInfo("reordering for", mParent);
|
||||
Controller()->RootEventTree().Log();
|
||||
logging::MsgEnd();
|
||||
|
||||
logging::MsgBegin("EVENTS_TREE", "Container tree");
|
||||
if (logging::IsEnabled(logging::eVerbose)) {
|
||||
nsAutoString level;
|
||||
Accessible* root = mParent->Document();
|
||||
do {
|
||||
const char* prefix = "";
|
||||
if (mParent == root) {
|
||||
prefix = "_X_";
|
||||
}
|
||||
else {
|
||||
const EventTree& ret = Controller()->RootEventTree();
|
||||
if (ret.Find(root)) {
|
||||
prefix = "_с_";
|
||||
}
|
||||
}
|
||||
|
||||
printf("%s", NS_ConvertUTF16toUTF8(level).get());
|
||||
logging::AccessibleInfo(prefix, root);
|
||||
if (root->FirstChild() && !root->FirstChild()->IsDoc()) {
|
||||
level.Append(NS_LITERAL_STRING(" "));
|
||||
root = root->FirstChild();
|
||||
continue;
|
||||
}
|
||||
int32_t idxInParent = root->mParent ?
|
||||
root->mParent->mChildren.IndexOf(root) : -1;
|
||||
if (idxInParent != -1 &&
|
||||
idxInParent < static_cast<int32_t>(root->mParent->mChildren.Length() - 1)) {
|
||||
root = root->mParent->mChildren.ElementAt(idxInParent + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
while ((root = root->Parent()) && !root->IsDoc()) {
|
||||
level.Cut(0, 2);
|
||||
|
||||
int32_t idxInParent = root->mParent ?
|
||||
root->mParent->mChildren.IndexOf(root) : -1;
|
||||
if (idxInParent != -1 &&
|
||||
idxInParent < static_cast<int32_t>(root->mParent->mChildren.Length() - 1)) {
|
||||
root = root->mParent->mChildren.ElementAt(idxInParent + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (root && !root->IsDoc());
|
||||
}
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
|
||||
mParent->mStateFlags |= Accessible::eKidsMutating;
|
||||
}
|
||||
|
||||
TreeMutation::~TreeMutation()
|
||||
{
|
||||
MOZ_ASSERT(mIsDone, "Done() must be called explicitly");
|
||||
}
|
||||
|
||||
void
|
||||
TreeMutation::AfterInsertion(Accessible* aChild)
|
||||
{
|
||||
MOZ_ASSERT(aChild->Parent() == mParent);
|
||||
|
||||
if (static_cast<uint32_t>(aChild->mIndexInParent) < mStartIdx) {
|
||||
mStartIdx = aChild->mIndexInParent + 1;
|
||||
}
|
||||
|
||||
if (!mEventTree) {
|
||||
mEventTree = Controller()->QueueMutation(mParent);
|
||||
if (!mEventTree) {
|
||||
mEventTree = kNoEventTree;
|
||||
}
|
||||
}
|
||||
|
||||
if (mEventTree != kNoEventTree) {
|
||||
mEventTree->Shown(aChild);
|
||||
Controller()->QueueNameChange(aChild);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TreeMutation::BeforeRemoval(Accessible* aChild, bool aNoShutdown)
|
||||
{
|
||||
MOZ_ASSERT(aChild->Parent() == mParent);
|
||||
|
||||
if (static_cast<uint32_t>(aChild->mIndexInParent) < mStartIdx) {
|
||||
mStartIdx = aChild->mIndexInParent;
|
||||
}
|
||||
|
||||
if (!mEventTree) {
|
||||
mEventTree = Controller()->QueueMutation(mParent);
|
||||
if (!mEventTree) {
|
||||
mEventTree = kNoEventTree;
|
||||
}
|
||||
}
|
||||
|
||||
if (mEventTree != kNoEventTree) {
|
||||
mEventTree->Hidden(aChild, !aNoShutdown);
|
||||
Controller()->QueueNameChange(aChild);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TreeMutation::Done()
|
||||
{
|
||||
MOZ_ASSERT(mParent->mStateFlags & Accessible::eKidsMutating);
|
||||
mParent->mStateFlags &= ~Accessible::eKidsMutating;
|
||||
|
||||
uint32_t length = mParent->mChildren.Length();
|
||||
#ifdef DEBUG
|
||||
for (uint32_t idx = 0; idx < mStartIdx && idx < length; idx++) {
|
||||
MOZ_ASSERT(mParent->mChildren[idx]->mIndexInParent == static_cast<int32_t>(idx),
|
||||
"Wrong index detected");
|
||||
}
|
||||
#endif
|
||||
|
||||
for (uint32_t idx = mStartIdx; idx < length; idx++) {
|
||||
mParent->mChildren[idx]->mIndexInParent = idx;
|
||||
mParent->mChildren[idx]->mStateFlags |= Accessible::eGroupInfoDirty;
|
||||
}
|
||||
|
||||
if (mStartIdx < mParent->mChildren.Length() - 1) {
|
||||
mParent->mEmbeddedObjCollector = nullptr;
|
||||
}
|
||||
|
||||
mParent->mStateFlags |= mStateFlagsCopy & Accessible::eKidsMutating;
|
||||
|
||||
#ifdef DEBUG
|
||||
mIsDone = true;
|
||||
#endif
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
if (mEventTree != kNoEventTree && logging::IsEnabled(logging::eEventTree)) {
|
||||
logging::MsgBegin("EVENTS_TREE", "reordering tree after");
|
||||
logging::AccessibleInfo("reordering for", mParent);
|
||||
Controller()->RootEventTree().Log();
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// EventTree
|
||||
|
||||
void
|
||||
EventTree::Process()
|
||||
{
|
||||
EventTree* node = mFirst;
|
||||
while (node) {
|
||||
node->Process();
|
||||
node = node->mNext;
|
||||
}
|
||||
|
||||
// Fire mutation events.
|
||||
if (mContainer) {
|
||||
uint32_t eventsCount = mDependentEvents.Length();
|
||||
for (uint32_t jdx = 0; jdx < eventsCount; jdx++) {
|
||||
AccMutationEvent* mtEvent = mDependentEvents[jdx];
|
||||
MOZ_ASSERT(mtEvent->mEventRule != AccEvent::eDoNotEmit,
|
||||
"The event shouldn't be presented in the tree");
|
||||
|
||||
nsEventShell::FireEvent(mtEvent);
|
||||
if (mtEvent->mTextChangeEvent) {
|
||||
nsEventShell::FireEvent(mtEvent->mTextChangeEvent);
|
||||
}
|
||||
|
||||
if (mtEvent->IsHide()) {
|
||||
// Fire menupopup end event before a hide event if a menu goes away.
|
||||
|
||||
// XXX: We don't look into children of hidden subtree to find hiding
|
||||
// menupopup (as we did prior bug 570275) because we don't do that when
|
||||
// menu is showing (and that's impossible until bug 606924 is fixed).
|
||||
// Nevertheless we should do this at least because layout coalesces
|
||||
// the changes before our processing and we may miss some menupopup
|
||||
// events. Now we just want to be consistent in content insertion/removal
|
||||
// handling.
|
||||
if (mtEvent->mAccessible->ARIARole() == roles::MENUPOPUP) {
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
|
||||
mtEvent->mAccessible);
|
||||
}
|
||||
|
||||
AccHideEvent* hideEvent = downcast_accEvent(mtEvent);
|
||||
if (hideEvent->NeedsShutdown()) {
|
||||
mContainer->Document()->ShutdownChildrenInSubtree(hideEvent->mAccessible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fire reorder event at last.
|
||||
if (mFireReorder) {
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_REORDER, mContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EventTree*
|
||||
EventTree::FindOrInsert(Accessible* aContainer)
|
||||
{
|
||||
if (!mFirst) {
|
||||
return mFirst = new EventTree(aContainer);
|
||||
}
|
||||
|
||||
EventTree* prevNode = nullptr;
|
||||
EventTree* node = mFirst;
|
||||
do {
|
||||
MOZ_ASSERT(!node->mContainer->IsApplication(),
|
||||
"No event for application accessible is expected here");
|
||||
MOZ_ASSERT(!node->mContainer->IsDefunct(), "An event target has to be alive");
|
||||
|
||||
// Case of same target.
|
||||
if (node->mContainer == aContainer) {
|
||||
return node;
|
||||
}
|
||||
|
||||
// Check if the given container is contained by a current node
|
||||
Accessible* tailRoot = aContainer->Document();
|
||||
Accessible* tailParent = aContainer;
|
||||
|
||||
EventTree* matchNode = nullptr;
|
||||
Accessible* matchParent = nullptr;
|
||||
while (true) {
|
||||
// Reached a top, no match for a current event.
|
||||
if (tailParent == tailRoot) {
|
||||
// If we have a match in parents then continue to look in siblings.
|
||||
if (matchNode && node->mNext) {
|
||||
node = node->mNext;
|
||||
if (node->mContainer == aContainer) {
|
||||
return node; // case of same target
|
||||
}
|
||||
tailParent = aContainer;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// We got a match.
|
||||
if (tailParent->Parent() == node->mContainer) {
|
||||
matchNode = node;
|
||||
matchParent = tailParent;
|
||||
|
||||
// Search the subtree for a better match.
|
||||
if (node->mFirst) {
|
||||
tailRoot = node->mContainer;
|
||||
node = node->mFirst;
|
||||
if (node->mContainer == aContainer) {
|
||||
return node; // case of same target
|
||||
}
|
||||
tailParent = aContainer;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
tailParent = tailParent->Parent();
|
||||
MOZ_ASSERT(tailParent, "Wrong tree");
|
||||
if (!tailParent) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The given node is contained by a current node
|
||||
// if hide of a current node contains the given node
|
||||
// then assert
|
||||
// if show of a current node contains the given node
|
||||
// then ignore the given node
|
||||
// otherwise ignore the given node, but not its show and hide events
|
||||
if (matchNode) {
|
||||
uint32_t eventType = 0;
|
||||
uint32_t count = matchNode->mDependentEvents.Length();
|
||||
for (uint32_t idx = count - 1; idx < count; idx--) {
|
||||
if (matchNode->mDependentEvents[idx]->mAccessible == matchParent) {
|
||||
eventType = matchNode->mDependentEvents[idx]->mEventType;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(eventType != nsIAccessibleEvent::EVENT_HIDE,
|
||||
"Accessible tree was modified after it was removed");
|
||||
|
||||
// If contained by show event target then no events are required.
|
||||
if (eventType == nsIAccessibleEvent::EVENT_SHOW) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
node->mFirst = new EventTree(aContainer);
|
||||
node->mFirst->mFireReorder = false;
|
||||
return node->mFirst;
|
||||
}
|
||||
|
||||
// If the given node contains a current node
|
||||
// then
|
||||
// if show or hide of the given node contains a grand parent of the current node
|
||||
// then ignore the current node and its show and hide events
|
||||
// otherwise ignore the current node, but not its show and hide events
|
||||
Accessible* curParent = node->mContainer;
|
||||
while (curParent && !curParent->IsDoc()) {
|
||||
if (curParent->Parent() != aContainer) {
|
||||
curParent = curParent->Parent();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Insert the tail node into the hierarchy between the current node and
|
||||
// its parent.
|
||||
node->mFireReorder = false;
|
||||
nsAutoPtr<EventTree>& nodeOwnerRef = prevNode ? prevNode->mNext : mFirst;
|
||||
nsAutoPtr<EventTree> newNode(new EventTree(aContainer));
|
||||
newNode->mFirst = Move(nodeOwnerRef);
|
||||
nodeOwnerRef = Move(newNode);
|
||||
nodeOwnerRef->mNext = Move(node->mNext);
|
||||
|
||||
// Check if a next node is contained by the given node too, and move them
|
||||
// under the given node if so.
|
||||
prevNode = nodeOwnerRef;
|
||||
node = nodeOwnerRef->mNext;
|
||||
nsAutoPtr<EventTree>* nodeRef = &nodeOwnerRef->mNext;
|
||||
EventTree* insNode = nodeOwnerRef->mFirst;
|
||||
while (node) {
|
||||
Accessible* curParent = node->mContainer;
|
||||
while (curParent && !curParent->IsDoc()) {
|
||||
if (curParent->Parent() != aContainer) {
|
||||
curParent = curParent->Parent();
|
||||
continue;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!insNode->mNext);
|
||||
|
||||
node->mFireReorder = false;
|
||||
insNode->mNext = Move(*nodeRef);
|
||||
insNode = insNode->mNext;
|
||||
|
||||
prevNode->mNext = Move(node->mNext);
|
||||
node = prevNode;
|
||||
break;
|
||||
}
|
||||
|
||||
prevNode = node;
|
||||
nodeRef = &node->mNext;
|
||||
node = node->mNext;
|
||||
}
|
||||
|
||||
return nodeOwnerRef;
|
||||
}
|
||||
|
||||
prevNode = node;
|
||||
} while ((node = node->mNext));
|
||||
|
||||
MOZ_ASSERT(prevNode, "Nowhere to insert");
|
||||
return prevNode->mNext = new EventTree(aContainer);
|
||||
}
|
||||
|
||||
const EventTree*
|
||||
EventTree::Find(const Accessible* aContainer) const
|
||||
{
|
||||
const EventTree* et = this;
|
||||
while (et) {
|
||||
if (et->mContainer == aContainer) {
|
||||
return et;
|
||||
}
|
||||
|
||||
if (et->mFirst) {
|
||||
et = et->mFirst;
|
||||
const EventTree* cet = et->Find(aContainer);
|
||||
if (cet) {
|
||||
return cet;
|
||||
}
|
||||
}
|
||||
|
||||
et = et->mNext;
|
||||
const EventTree* cet = et->Find(aContainer);
|
||||
if (cet) {
|
||||
return cet;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
void
|
||||
EventTree::Log(uint32_t aLevel) const
|
||||
{
|
||||
if (aLevel == UINT32_MAX) {
|
||||
if (mFirst) {
|
||||
mFirst->Log(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < aLevel; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
logging::AccessibleInfo("container", mContainer);
|
||||
|
||||
for (uint32_t i = 0; i < mDependentEvents.Length(); i++) {
|
||||
AccMutationEvent* ev = mDependentEvents[i];
|
||||
if (ev->IsShow()) {
|
||||
for (uint32_t i = 0; i < aLevel; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
logging::AccessibleInfo("shown", ev->mAccessible);
|
||||
}
|
||||
else {
|
||||
for (uint32_t i = 0; i < aLevel; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
logging::AccessibleInfo("hidden", ev->mAccessible);
|
||||
}
|
||||
}
|
||||
|
||||
if (mFirst) {
|
||||
mFirst->Log(aLevel + 1);
|
||||
}
|
||||
|
||||
if (mNext) {
|
||||
mNext->Log(aLevel);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
EventTree::Mutated(AccMutationEvent* aEv)
|
||||
{
|
||||
// If shown or hidden node is a root of previously mutated subtree, then
|
||||
// discard those subtree mutations as we are no longer interested in them.
|
||||
EventTree* node = mFirst;
|
||||
while (node) {
|
||||
if (node->mContainer == aEv->mAccessible) {
|
||||
node->Clear();
|
||||
break;
|
||||
}
|
||||
node = node->mNext;
|
||||
}
|
||||
|
||||
AccMutationEvent* prevEvent = mDependentEvents.SafeLastElement(nullptr);
|
||||
mDependentEvents.AppendElement(aEv);
|
||||
|
||||
// Coalesce text change events from this hide/show event and the previous one.
|
||||
if (prevEvent && aEv->mEventType == prevEvent->mEventType) {
|
||||
if (aEv->IsHide()) {
|
||||
// XXX: we need a way to ignore SplitNode and JoinNode() when they do not
|
||||
// affect the text within the hypertext.
|
||||
AccTextChangeEvent* prevTextEvent = prevEvent->mTextChangeEvent;
|
||||
if (prevTextEvent) {
|
||||
AccHideEvent* hideEvent = downcast_accEvent(aEv);
|
||||
AccHideEvent* prevHideEvent = downcast_accEvent(prevEvent);
|
||||
|
||||
if (prevHideEvent->mNextSibling == hideEvent->mAccessible) {
|
||||
hideEvent->mAccessible->AppendTextTo(prevTextEvent->mModifiedText);
|
||||
}
|
||||
else if (prevHideEvent->mPrevSibling == hideEvent->mAccessible) {
|
||||
uint32_t oldLen = prevTextEvent->GetLength();
|
||||
hideEvent->mAccessible->AppendTextTo(prevTextEvent->mModifiedText);
|
||||
prevTextEvent->mStart -= prevTextEvent->GetLength() - oldLen;
|
||||
}
|
||||
|
||||
hideEvent->mTextChangeEvent.swap(prevEvent->mTextChangeEvent);
|
||||
}
|
||||
}
|
||||
else {
|
||||
AccTextChangeEvent* prevTextEvent = prevEvent->mTextChangeEvent;
|
||||
if (prevTextEvent) {
|
||||
if (aEv->mAccessible->IndexInParent() ==
|
||||
prevEvent->mAccessible->IndexInParent() + 1) {
|
||||
// If tail target was inserted after this target, i.e. tail target is next
|
||||
// sibling of this target.
|
||||
aEv->mAccessible->AppendTextTo(prevTextEvent->mModifiedText);
|
||||
}
|
||||
else if (aEv->mAccessible->IndexInParent() ==
|
||||
prevEvent->mAccessible->IndexInParent() - 1) {
|
||||
// If tail target was inserted before this target, i.e. tail target is
|
||||
// previous sibling of this target.
|
||||
nsAutoString startText;
|
||||
aEv->mAccessible->AppendTextTo(startText);
|
||||
prevTextEvent->mModifiedText = startText + prevTextEvent->mModifiedText;
|
||||
prevTextEvent->mStart -= startText.Length();
|
||||
}
|
||||
|
||||
aEv->mTextChangeEvent.swap(prevEvent->mTextChangeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a text change event caused by this hide/show event. When a node is
|
||||
// hidden/removed or shown/appended, the text in an ancestor hyper text will
|
||||
// lose or get new characters.
|
||||
if (aEv->mTextChangeEvent || !mContainer->IsHyperText()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString text;
|
||||
aEv->mAccessible->AppendTextTo(text);
|
||||
if (text.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t offset = mContainer->AsHyperText()->GetChildOffset(aEv->mAccessible);
|
||||
aEv->mTextChangeEvent =
|
||||
new AccTextChangeEvent(mContainer, offset, text, aEv->IsShow(),
|
||||
aEv->mIsFromUserInput ? eFromUserInput : eNoUserInput);
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_a11y_EventTree_h_
|
||||
#define mozilla_a11y_EventTree_h_
|
||||
|
||||
#include "AccEvent.h"
|
||||
#include "Accessible.h"
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
/**
|
||||
* This class makes sure required tasks are done before and after tree
|
||||
* mutations. Currently this only includes group info invalidation. You must
|
||||
* have an object of this class on the stack when calling methods that mutate
|
||||
* the accessible tree.
|
||||
*/
|
||||
class TreeMutation final
|
||||
{
|
||||
public:
|
||||
static const bool kNoEvents = true;
|
||||
static const bool kNoShutdown = true;
|
||||
|
||||
explicit TreeMutation(Accessible* aParent, bool aNoEvents = false);
|
||||
~TreeMutation();
|
||||
|
||||
void AfterInsertion(Accessible* aChild);
|
||||
void BeforeRemoval(Accessible* aChild, bool aNoShutdown = false);
|
||||
void Done();
|
||||
|
||||
private:
|
||||
NotificationController* Controller() const
|
||||
{ return mParent->Document()->Controller(); }
|
||||
|
||||
static EventTree* const kNoEventTree;
|
||||
|
||||
Accessible* mParent;
|
||||
uint32_t mStartIdx;
|
||||
uint32_t mStateFlagsCopy;
|
||||
EventTree* mEventTree;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool mIsDone;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A mutation events coalescence structure.
|
||||
*/
|
||||
class EventTree final {
|
||||
public:
|
||||
EventTree() :
|
||||
mFirst(nullptr), mNext(nullptr), mContainer(nullptr), mFireReorder(true) { }
|
||||
explicit EventTree(Accessible* aContainer) :
|
||||
mFirst(nullptr), mNext(nullptr), mContainer(aContainer), mFireReorder(true) { }
|
||||
~EventTree() { Clear(); }
|
||||
|
||||
void Shown(Accessible* aChild)
|
||||
{
|
||||
RefPtr<AccShowEvent> ev = new AccShowEvent(aChild);
|
||||
Mutated(ev);
|
||||
}
|
||||
|
||||
void Hidden(Accessible* aChild, bool aNeedsShutdown = true)
|
||||
{
|
||||
RefPtr<AccHideEvent> ev = new AccHideEvent(aChild, aNeedsShutdown);
|
||||
Mutated(ev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an event tree node for the given accessible.
|
||||
*/
|
||||
const EventTree* Find(const Accessible* aContainer) const;
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
void Log(uint32_t aLevel = UINT32_MAX) const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
/**
|
||||
* Processes the event queue and fires events.
|
||||
*/
|
||||
void Process();
|
||||
|
||||
/**
|
||||
* Return an event subtree for the given accessible.
|
||||
*/
|
||||
EventTree* FindOrInsert(Accessible* aContainer);
|
||||
|
||||
void Mutated(AccMutationEvent* aEv);
|
||||
void Clear() { mFirst = nullptr; mNext = nullptr; mContainer = nullptr; }
|
||||
|
||||
nsAutoPtr<EventTree> mFirst;
|
||||
nsAutoPtr<EventTree> mNext;
|
||||
|
||||
Accessible* mContainer;
|
||||
nsTArray<RefPtr<AccMutationEvent>> mDependentEvents;
|
||||
bool mFireReorder;
|
||||
|
||||
friend class NotificationController;
|
||||
};
|
||||
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_a11y_EventQueue_h_
|
|
@ -44,6 +44,7 @@ static ModuleRep sModuleMap[] = {
|
|||
{ "doclifecycle", logging::eDocLifeCycle },
|
||||
|
||||
{ "events", logging::eEvents },
|
||||
{ "eventTree", logging::eEventTree },
|
||||
{ "platforms", logging::ePlatforms },
|
||||
{ "text", logging::eText },
|
||||
{ "tree", logging::eTree },
|
||||
|
|
|
@ -34,18 +34,19 @@ enum EModules {
|
|||
eDocLifeCycle = eDocLoad | eDocCreate | eDocDestroy,
|
||||
|
||||
eEvents = 1 << 3,
|
||||
ePlatforms = 1 << 4,
|
||||
eText = 1 << 5,
|
||||
eTree = 1 << 6,
|
||||
eEventTree = 1 << 4,
|
||||
ePlatforms = 1 << 5,
|
||||
eText = 1 << 6,
|
||||
eTree = 1 << 7,
|
||||
|
||||
eDOMEvents = 1 << 7,
|
||||
eFocus = 1 << 8,
|
||||
eSelection = 1 << 9,
|
||||
eDOMEvents = 1 << 8,
|
||||
eFocus = 1 << 9,
|
||||
eSelection = 1 << 10,
|
||||
eNotifications = eDOMEvents | eSelection | eFocus,
|
||||
|
||||
// extras
|
||||
eStack = 1 << 10,
|
||||
eVerbose = 1 << 11
|
||||
eStack = 1 << 11,
|
||||
eVerbose = 1 << 12
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -97,6 +97,17 @@ NotificationController::Shutdown()
|
|||
mNotifications.Clear();
|
||||
mEvents.Clear();
|
||||
mRelocations.Clear();
|
||||
mEventTree.Clear();
|
||||
}
|
||||
|
||||
EventTree*
|
||||
NotificationController::QueueMutation(Accessible* aContainer)
|
||||
{
|
||||
EventTree* tree = mEventTree.FindOrInsert(aContainer);
|
||||
if (tree) {
|
||||
ScheduleProcessing();
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -388,6 +399,9 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
|||
// events causes script to run.
|
||||
mObservingState = eRefreshProcessing;
|
||||
|
||||
mEventTree.Process();
|
||||
mEventTree.Clear();
|
||||
|
||||
ProcessEventQueue();
|
||||
|
||||
if (IPCAccessibilityActive()) {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#define mozilla_a11y_NotificationController_h_
|
||||
|
||||
#include "EventQueue.h"
|
||||
#include "EventTree.h"
|
||||
|
||||
#include "mozilla/IndexSequence.h"
|
||||
#include "mozilla/Tuple.h"
|
||||
|
@ -104,13 +105,36 @@ public:
|
|||
void Shutdown();
|
||||
|
||||
/**
|
||||
* Put an accessible event into the queue to process it later.
|
||||
* Add an accessible event into the queue to process it later.
|
||||
*/
|
||||
void QueueEvent(AccEvent* aEvent)
|
||||
{
|
||||
if (PushEvent(aEvent))
|
||||
if (PushEvent(aEvent)) {
|
||||
ScheduleProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and adds a name change event into the queue for a container of
|
||||
* the given accessible, if the accessible is a part of name computation of
|
||||
* the container.
|
||||
*/
|
||||
void QueueNameChange(Accessible* aChangeTarget)
|
||||
{
|
||||
if (PushNameChange(aChangeTarget)) {
|
||||
ScheduleProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns existing event tree for the given the accessible or creates one if
|
||||
* it doesn't exists yet.
|
||||
*/
|
||||
EventTree* QueueMutation(Accessible* aContainer);
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
const EventTree& RootEventTree() const { return mEventTree; };
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Schedule binding the child document to the tree of this document.
|
||||
|
@ -291,6 +315,11 @@ private:
|
|||
* Holds all scheduled relocations.
|
||||
*/
|
||||
nsTArray<RefPtr<Accessible> > mRelocations;
|
||||
|
||||
/**
|
||||
* Holds all mutation events.
|
||||
*/
|
||||
EventTree mEventTree;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
|
|
|
@ -35,6 +35,7 @@ UNIFIED_SOURCES += [
|
|||
'Asserts.cpp',
|
||||
'DocManager.cpp',
|
||||
'EventQueue.cpp',
|
||||
'EventTree.cpp',
|
||||
'Filters.cpp',
|
||||
'FocusManager.cpp',
|
||||
'NotificationController.cpp',
|
||||
|
|
|
@ -580,8 +580,9 @@ nsAccessibilityService::ContentRemoved(nsIPresShell* aPresShell,
|
|||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eTree)) {
|
||||
logging::MsgBegin("TREE", "content removed");
|
||||
logging::Node("container", aChildNode->GetFlattenedTreeParent());
|
||||
logging::Node("content", aChildNode);
|
||||
logging::Node("container node", aChildNode->GetFlattenedTreeParent());
|
||||
logging::Node("content node", aChildNode);
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -13,9 +13,11 @@
|
|||
#include "nsAccUtils.h"
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "ApplicationAccessible.h"
|
||||
#include "NotificationController.h"
|
||||
#include "nsEventShell.h"
|
||||
#include "nsTextEquivUtils.h"
|
||||
#include "DocAccessibleChild.h"
|
||||
#include "EventTree.h"
|
||||
#include "Logging.h"
|
||||
#include "Relation.h"
|
||||
#include "Role.h"
|
||||
|
@ -2119,6 +2121,11 @@ Accessible::MoveChild(uint32_t aNewIndex, Accessible* aChild)
|
|||
"No move, same index");
|
||||
MOZ_ASSERT(aNewIndex <= mChildren.Length(), "Wrong new index was given");
|
||||
|
||||
EventTree* eventTree = mDoc->Controller()->QueueMutation(this);
|
||||
if (eventTree) {
|
||||
eventTree->Hidden(aChild, false);
|
||||
}
|
||||
|
||||
mEmbeddedObjCollector = nullptr;
|
||||
mChildren.RemoveElementAt(aChild->mIndexInParent);
|
||||
|
||||
|
@ -2127,7 +2134,6 @@ Accessible::MoveChild(uint32_t aNewIndex, Accessible* aChild)
|
|||
// If the child is moved after its current position.
|
||||
if (static_cast<uint32_t>(aChild->mIndexInParent) < aNewIndex) {
|
||||
startIdx = aChild->mIndexInParent;
|
||||
|
||||
if (aNewIndex == mChildren.Length() + 1) {
|
||||
// The child is moved to the end.
|
||||
mChildren.AppendElement(aChild);
|
||||
|
@ -2148,6 +2154,11 @@ Accessible::MoveChild(uint32_t aNewIndex, Accessible* aChild)
|
|||
mChildren[idx]->mStateFlags |= eGroupInfoDirty;
|
||||
mChildren[idx]->mInt.mIndexOfEmbeddedChild = -1;
|
||||
}
|
||||
|
||||
if (eventTree) {
|
||||
eventTree->Shown(aChild);
|
||||
mDoc->Controller()->QueueNameChange(aChild);
|
||||
}
|
||||
}
|
||||
|
||||
Accessible*
|
||||
|
@ -2791,34 +2802,3 @@ KeyBinding::ToAtkFormat(nsAString& aValue) const
|
|||
|
||||
aValue.Append(mKey);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AutoTreeMutation class
|
||||
|
||||
void
|
||||
AutoTreeMutation::Done()
|
||||
{
|
||||
MOZ_ASSERT(mParent->mStateFlags & Accessible::eKidsMutating,
|
||||
"The parent is not in mutating state.");
|
||||
mParent->mStateFlags &= ~Accessible::eKidsMutating;
|
||||
|
||||
uint32_t length = mParent->mChildren.Length();
|
||||
#ifdef DEBUG
|
||||
for (uint32_t idx = 0; idx < mStartIdx && idx < length; idx++) {
|
||||
MOZ_ASSERT(mParent->mChildren[idx]->mIndexInParent == static_cast<int32_t>(idx),
|
||||
"Wrong index detected");
|
||||
}
|
||||
#endif
|
||||
|
||||
for (uint32_t idx = mStartIdx; idx < length; idx++) {
|
||||
mParent->mChildren[idx]->mIndexInParent = idx;
|
||||
mParent->mChildren[idx]->mStateFlags |= Accessible::eGroupInfoDirty;
|
||||
}
|
||||
|
||||
if (mStartIdx < mParent->mChildren.Length() - 1) {
|
||||
mParent->mEmbeddedObjCollector = nullptr;
|
||||
}
|
||||
|
||||
mParent->mStateFlags |= mStateFlagsCopy & Accessible::eKidsMutating;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ class AccGroupInfo;
|
|||
class ApplicationAccessible;
|
||||
class DocAccessible;
|
||||
class EmbeddedObjCollector;
|
||||
class EventTree;
|
||||
class HTMLImageMapAccessible;
|
||||
class HTMLLIAccessible;
|
||||
class HyperTextAccessible;
|
||||
|
@ -1112,7 +1113,7 @@ protected:
|
|||
|
||||
friend class DocAccessible;
|
||||
friend class xpcAccessible;
|
||||
friend class AutoTreeMutation;
|
||||
friend class TreeMutation;
|
||||
|
||||
nsAutoPtr<mozilla::a11y::EmbeddedObjCollector> mEmbeddedObjCollector;
|
||||
union {
|
||||
|
@ -1204,43 +1205,6 @@ private:
|
|||
uint32_t mModifierMask;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This class makes sure required tasks are done before and after tree
|
||||
* mutations. Currently this only includes group info invalidation. You must
|
||||
* have an object of this class on the stack when calling methods that mutate
|
||||
* the accessible tree.
|
||||
*/
|
||||
class AutoTreeMutation
|
||||
{
|
||||
public:
|
||||
explicit AutoTreeMutation(Accessible* aParent) :
|
||||
mParent(aParent), mStartIdx(UINT32_MAX),
|
||||
mStateFlagsCopy(mParent->mStateFlags)
|
||||
{
|
||||
mParent->mStateFlags |= Accessible::eKidsMutating;
|
||||
}
|
||||
|
||||
void AfterInsertion(const Accessible* aChild) {
|
||||
if (static_cast<uint32_t>(aChild->IndexInParent()) < mStartIdx) {
|
||||
mStartIdx = aChild->IndexInParent() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void BeforeRemoval(const Accessible* aChild) {
|
||||
if (static_cast<uint32_t>(aChild->IndexInParent()) < mStartIdx) {
|
||||
mStartIdx = aChild->IndexInParent();
|
||||
}
|
||||
}
|
||||
|
||||
void Done();
|
||||
|
||||
private:
|
||||
Accessible* mParent;
|
||||
uint32_t mStartIdx;
|
||||
uint32_t mStateFlagsCopy;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -1751,17 +1751,19 @@ DocAccessible::ProcessContentInserted(Accessible* aContainer,
|
|||
UpdateRootElIfNeeded();
|
||||
}
|
||||
|
||||
uint32_t updateFlags = 0;
|
||||
AutoTreeMutation mt(aContainer);
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(aContainer);
|
||||
InsertIterator iter(aContainer, aNodes);
|
||||
if (!iter.Next()) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
logging::TreeInfo("children before insertion", logging::eVerbose,
|
||||
aContainer);
|
||||
#endif
|
||||
|
||||
InsertIterator iter(aContainer, aNodes);
|
||||
while (iter.Next()) {
|
||||
uint32_t updateFlags = 0;
|
||||
TreeMutation mt(aContainer);
|
||||
do {
|
||||
Accessible* parent = iter.Child()->Parent();
|
||||
if (parent) {
|
||||
if (parent != aContainer) {
|
||||
|
@ -1788,12 +1790,13 @@ DocAccessible::ProcessContentInserted(Accessible* aContainer,
|
|||
#endif
|
||||
|
||||
mt.AfterInsertion(iter.Child());
|
||||
updateFlags |= UpdateTreeInternal(iter.Child(), true, reorderEvent);
|
||||
updateFlags |= UpdateTreeInternal(iter.Child(), true);
|
||||
continue;
|
||||
}
|
||||
|
||||
MOZ_ASSERT_UNREACHABLE("accessible was rejected");
|
||||
}
|
||||
} while (iter.Next());
|
||||
|
||||
mt.Done();
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
|
@ -1801,7 +1804,7 @@ DocAccessible::ProcessContentInserted(Accessible* aContainer,
|
|||
aContainer);
|
||||
#endif
|
||||
|
||||
FireEventsOnInsertion(aContainer, reorderEvent, updateFlags);
|
||||
FireEventsOnInsertion(aContainer, updateFlags);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1819,22 +1822,19 @@ DocAccessible::ProcessContentInserted(Accessible* aContainer, nsIContent* aNode)
|
|||
}
|
||||
|
||||
if (child) {
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(aContainer);
|
||||
|
||||
AutoTreeMutation mt(aContainer);
|
||||
TreeMutation mt(aContainer);
|
||||
aContainer->InsertAfter(child, walker.Prev());
|
||||
mt.AfterInsertion(child);
|
||||
mt.Done();
|
||||
|
||||
uint32_t flags = UpdateTreeInternal(child, true, reorderEvent);
|
||||
FireEventsOnInsertion(aContainer, reorderEvent, flags);
|
||||
uint32_t flags = UpdateTreeInternal(child, true);
|
||||
FireEventsOnInsertion(aContainer, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DocAccessible::FireEventsOnInsertion(Accessible* aContainer,
|
||||
AccReorderEvent* aReorderEvent,
|
||||
uint32_t aUpdateFlags)
|
||||
{
|
||||
// Content insertion did not cause an accessible tree change.
|
||||
|
@ -1857,7 +1857,6 @@ DocAccessible::FireEventsOnInsertion(Accessible* aContainer,
|
|||
}
|
||||
|
||||
MaybeNotifyOfValueChange(aContainer);
|
||||
FireDelayedEvent(aReorderEvent);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1880,32 +1879,33 @@ DocAccessible::UpdateTreeOnRemoval(Accessible* aContainer, nsIContent* aChildNod
|
|||
#endif
|
||||
|
||||
uint32_t updateFlags = eNoAccessible;
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(aContainer);
|
||||
AutoTreeMutation mt(aContainer);
|
||||
TreeMutation mt(aContainer);
|
||||
|
||||
if (child) {
|
||||
mt.BeforeRemoval(child);
|
||||
updateFlags |= UpdateTreeInternal(child, false, reorderEvent);
|
||||
} else {
|
||||
updateFlags |= UpdateTreeInternal(child, false);
|
||||
}
|
||||
else {
|
||||
TreeWalker walker(aContainer, aChildNode, TreeWalker::eWalkCache);
|
||||
while (Accessible* child = walker.Next()) {
|
||||
Accessible* child = walker.Next();
|
||||
if (child) {
|
||||
do {
|
||||
mt.BeforeRemoval(child);
|
||||
updateFlags |= UpdateTreeInternal(child, false, reorderEvent);
|
||||
updateFlags |= UpdateTreeInternal(child, false);
|
||||
}
|
||||
while ((child = walker.Next()));
|
||||
}
|
||||
}
|
||||
mt.Done();
|
||||
|
||||
// Content insertion/removal is not cause of accessible tree change.
|
||||
if (updateFlags == eNoAccessible)
|
||||
return;
|
||||
|
||||
if (updateFlags != eNoAccessible) {
|
||||
MaybeNotifyOfValueChange(aContainer);
|
||||
FireDelayedEvent(reorderEvent);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t
|
||||
DocAccessible::UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
|
||||
AccReorderEvent* aReorderEvent)
|
||||
DocAccessible::UpdateTreeInternal(Accessible* aChild, bool aIsInsert)
|
||||
{
|
||||
uint32_t updateFlags = eAccessible;
|
||||
|
||||
|
@ -1918,31 +1918,8 @@ DocAccessible::UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
|
|||
if (aIsInsert) {
|
||||
// Create accessible tree for shown accessible.
|
||||
CacheChildrenInSubtree(aChild, &focusedAcc);
|
||||
|
||||
} else {
|
||||
// Fire menupopup end event before hide event if a menu goes away.
|
||||
|
||||
// XXX: We don't look into children of hidden subtree to find hiding
|
||||
// menupopup (as we did prior bug 570275) because we don't do that when
|
||||
// menu is showing (and that's impossible until bug 606924 is fixed).
|
||||
// Nevertheless we should do this at least because layout coalesces
|
||||
// the changes before our processing and we may miss some menupopup
|
||||
// events. Now we just want to be consistent in content insertion/removal
|
||||
// handling.
|
||||
if (aChild->ARIARole() == roles::MENUPOPUP)
|
||||
FireDelayedEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END, aChild);
|
||||
}
|
||||
|
||||
// Fire show/hide event.
|
||||
RefPtr<AccMutationEvent> event;
|
||||
if (aIsInsert)
|
||||
event = new AccShowEvent(aChild);
|
||||
else
|
||||
event = new AccHideEvent(aChild);
|
||||
|
||||
FireDelayedEvent(event);
|
||||
aReorderEvent->AddSubMutationEvent(event);
|
||||
|
||||
if (aIsInsert) {
|
||||
roles::Role ariaRole = aChild->ARIARole();
|
||||
if (ariaRole == roles::MENUPOPUP) {
|
||||
|
@ -2065,7 +2042,7 @@ DocAccessible::DoARIAOwnsRelocation(Accessible* aOwner)
|
|||
if (aOwner->IsAcceptableChild(childEl)) {
|
||||
child = GetAccService()->CreateAccessible(childEl, aOwner);
|
||||
if (child) {
|
||||
AutoTreeMutation imut(aOwner);
|
||||
TreeMutation imut(aOwner);
|
||||
aOwner->InsertChildAt(insertIdx, child);
|
||||
imut.AfterInsertion(child);
|
||||
imut.Done();
|
||||
|
@ -2076,9 +2053,8 @@ DocAccessible::DoARIAOwnsRelocation(Accessible* aOwner)
|
|||
insertIdx = child->IndexInParent() + 1;
|
||||
arrayIdx++;
|
||||
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(aOwner);
|
||||
uint32_t flags = UpdateTreeInternal(child, true, reorderEvent);
|
||||
FireEventsOnInsertion(aOwner, reorderEvent, flags);
|
||||
uint32_t flags = UpdateTreeInternal(child, true);
|
||||
FireEventsOnInsertion(aOwner, flags);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
|
@ -2196,19 +2172,8 @@ DocAccessible::MoveChild(Accessible* aChild, Accessible* aNewParent,
|
|||
if (curParent == aNewParent) {
|
||||
MOZ_ASSERT(aChild->IndexInParent() != aIdxInParent, "No move case");
|
||||
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(curParent);
|
||||
RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(aChild, false);
|
||||
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||
FireDelayedEvent(hideEvent);
|
||||
|
||||
curParent->MoveChild(aIdxInParent, aChild);
|
||||
|
||||
RefPtr<AccMutationEvent> showEvent = new AccShowEvent(aChild);
|
||||
reorderEvent->AddSubMutationEvent(showEvent);
|
||||
FireDelayedEvent(showEvent);
|
||||
|
||||
MaybeNotifyOfValueChange(curParent);
|
||||
FireDelayedEvent(reorderEvent);
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
logging::TreeInfo("move child: parent tree after",
|
||||
|
@ -2221,36 +2186,24 @@ DocAccessible::MoveChild(Accessible* aChild, Accessible* aNewParent,
|
|||
return false;
|
||||
}
|
||||
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(curParent);
|
||||
RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(aChild, false);
|
||||
reorderEvent->AddSubMutationEvent(hideEvent);
|
||||
FireDelayedEvent(hideEvent);
|
||||
|
||||
AutoTreeMutation rmut(curParent);
|
||||
rmut.BeforeRemoval(aChild);
|
||||
TreeMutation rmut(curParent);
|
||||
rmut.BeforeRemoval(aChild, TreeMutation::kNoShutdown);
|
||||
curParent->RemoveChild(aChild);
|
||||
rmut.Done();
|
||||
|
||||
MaybeNotifyOfValueChange(curParent);
|
||||
FireDelayedEvent(reorderEvent);
|
||||
|
||||
// No insertion point for the child.
|
||||
if (aIdxInParent == -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
AutoTreeMutation imut(aNewParent);
|
||||
TreeMutation imut(aNewParent);
|
||||
aNewParent->InsertChildAt(aIdxInParent, aChild);
|
||||
imut.AfterInsertion(aChild);
|
||||
imut.Done();
|
||||
|
||||
reorderEvent = new AccReorderEvent(aNewParent);
|
||||
RefPtr<AccMutationEvent> showEvent = new AccShowEvent(aChild);
|
||||
reorderEvent->AddSubMutationEvent(showEvent);
|
||||
FireDelayedEvent(showEvent);
|
||||
|
||||
MaybeNotifyOfValueChange(aNewParent);
|
||||
FireDelayedEvent(reorderEvent);
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
logging::TreeInfo("move child: old parent tree after",
|
||||
|
@ -2275,7 +2228,10 @@ DocAccessible::CacheChildrenInSubtree(Accessible* aRoot,
|
|||
|
||||
Accessible* root = aRoot->IsHTMLCombobox() ? aRoot->FirstChild() : aRoot;
|
||||
if (root->KidsFromDOM()) {
|
||||
AutoTreeMutation mt(root);
|
||||
#ifdef A11Y_LOG
|
||||
logging::TreeInfo("caching children", logging::eVerbose, aRoot);
|
||||
#endif
|
||||
TreeMutation mt(root, TreeMutation::kNoEvents);
|
||||
TreeWalker walker(root);
|
||||
while (Accessible* child = walker.Next()) {
|
||||
if (child->IsBoundToParent()) {
|
||||
|
@ -2291,10 +2247,6 @@ DocAccessible::CacheChildrenInSubtree(Accessible* aRoot,
|
|||
mt.Done();
|
||||
}
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
logging::TreeInfo("cached children", logging::eVerbose, aRoot);
|
||||
#endif
|
||||
|
||||
// Fire document load complete on ARIA documents.
|
||||
// XXX: we should delay an event if the ARIA document has aria-busy.
|
||||
if (aRoot->HasARIARole() && !aRoot->IsDoc()) {
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
#include "nsIAccessiblePivot.h"
|
||||
|
||||
#include "AccEvent.h"
|
||||
#include "HyperTextAccessibleWrap.h"
|
||||
#include "AccEvent.h"
|
||||
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
@ -187,9 +187,7 @@ public:
|
|||
*/
|
||||
void FireDelayedEvent(AccEvent* aEvent);
|
||||
void FireDelayedEvent(uint32_t aEventType, Accessible* aTarget);
|
||||
void FireEventsOnInsertion(Accessible* aContainer,
|
||||
AccReorderEvent* aReorderEvent,
|
||||
uint32_t aUpdateFlags);
|
||||
void FireEventsOnInsertion(Accessible* aContainer, uint32_t aUpdateFlags);
|
||||
|
||||
/**
|
||||
* Fire value change event on the given accessible if applicable.
|
||||
|
@ -372,6 +370,11 @@ public:
|
|||
*/
|
||||
bool RelocateARIAOwnedIfNeeded(nsIContent* aEl);
|
||||
|
||||
/**
|
||||
* Return a notification controller associated with the document.
|
||||
*/
|
||||
NotificationController* Controller() const { return mNotificationController; }
|
||||
|
||||
/**
|
||||
* If this document is in a content process return the object responsible for
|
||||
* communicating with the main process for it.
|
||||
|
@ -522,9 +525,7 @@ protected:
|
|||
eAccessible = 1,
|
||||
eAlertAccessible = 2
|
||||
};
|
||||
|
||||
uint32_t UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
|
||||
AccReorderEvent* aReorderEvent);
|
||||
uint32_t UpdateTreeInternal(Accessible* aChild, bool aIsInsert);
|
||||
|
||||
/**
|
||||
* Validates all aria-owns connections and updates the tree accordingly.
|
||||
|
@ -703,7 +704,7 @@ protected:
|
|||
* Used to process notification from core and accessible events.
|
||||
*/
|
||||
RefPtr<NotificationController> mNotificationController;
|
||||
friend class EventQueue;
|
||||
friend class EventTree;
|
||||
friend class NotificationController;
|
||||
|
||||
private:
|
||||
|
|
|
@ -87,9 +87,7 @@ HTMLImageMapAccessible::UpdateChildAreas(bool aDoFireEvents)
|
|||
if (!imageMapObj)
|
||||
return;
|
||||
|
||||
bool treeChanged = false;
|
||||
AutoTreeMutation mt(this);
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(this);
|
||||
TreeMutation mt(this, TreeMutation::kNoEvents & !aDoFireEvents);
|
||||
|
||||
// Remove areas that are not a valid part of the image map anymore.
|
||||
for (int32_t childIdx = mChildren.Length() - 1; childIdx >= 0; childIdx--) {
|
||||
|
@ -97,15 +95,8 @@ HTMLImageMapAccessible::UpdateChildAreas(bool aDoFireEvents)
|
|||
if (area->GetContent()->GetPrimaryFrame())
|
||||
continue;
|
||||
|
||||
if (aDoFireEvents) {
|
||||
RefPtr<AccHideEvent> event = new AccHideEvent(area, area->GetContent());
|
||||
mDoc->FireDelayedEvent(event);
|
||||
reorderEvent->AddSubMutationEvent(event);
|
||||
}
|
||||
|
||||
mt.BeforeRemoval(area);
|
||||
RemoveChild(area);
|
||||
treeChanged = true;
|
||||
}
|
||||
|
||||
// Insert new areas into the tree.
|
||||
|
@ -123,22 +114,10 @@ HTMLImageMapAccessible::UpdateChildAreas(bool aDoFireEvents)
|
|||
}
|
||||
|
||||
mt.AfterInsertion(area);
|
||||
|
||||
if (aDoFireEvents) {
|
||||
RefPtr<AccShowEvent> event = new AccShowEvent(area);
|
||||
mDoc->FireDelayedEvent(event);
|
||||
reorderEvent->AddSubMutationEvent(event);
|
||||
}
|
||||
|
||||
treeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
mt.Done();
|
||||
|
||||
// Fire reorder event if needed.
|
||||
if (treeChanged && aDoFireEvents)
|
||||
mDoc->FireDelayedEvent(reorderEvent);
|
||||
}
|
||||
|
||||
Accessible*
|
||||
|
|
|
@ -103,31 +103,19 @@ HTMLLIAccessible::UpdateBullet(bool aHasBullet)
|
|||
return;
|
||||
}
|
||||
|
||||
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(this);
|
||||
AutoTreeMutation mt(this);
|
||||
|
||||
DocAccessible* document = Document();
|
||||
TreeMutation mt(this);
|
||||
if (aHasBullet) {
|
||||
mBullet = new HTMLListBulletAccessible(mContent, mDoc);
|
||||
document->BindToDocument(mBullet, nullptr);
|
||||
mDoc->BindToDocument(mBullet, nullptr);
|
||||
InsertChildAt(0, mBullet);
|
||||
mt.AfterInsertion(mBullet);
|
||||
|
||||
RefPtr<AccShowEvent> event = new AccShowEvent(mBullet);
|
||||
mDoc->FireDelayedEvent(event);
|
||||
reorderEvent->AddSubMutationEvent(event);
|
||||
} else {
|
||||
RefPtr<AccHideEvent> event = new AccHideEvent(mBullet, mBullet->GetContent());
|
||||
mDoc->FireDelayedEvent(event);
|
||||
reorderEvent->AddSubMutationEvent(event);
|
||||
|
||||
}
|
||||
else {
|
||||
mt.BeforeRemoval(mBullet);
|
||||
RemoveChild(mBullet);
|
||||
mBullet = nullptr;
|
||||
}
|
||||
mt.Done();
|
||||
|
||||
mDoc->FireDelayedEvent(reorderEvent);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -54,9 +54,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
//gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
//gA11yEventDumpToConsole = true;
|
||||
//enableLogging("tree");
|
||||
//enableLogging("tree,eventTree,verbose");
|
||||
|
||||
function doTest()
|
||||
{
|
||||
|
@ -144,7 +143,5 @@
|
|||
<a id="link4" onmouseup="">
|
||||
<img src="../moz.png" id="img4">
|
||||
</a>
|
||||
|
||||
<div id="eventdump"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -56,10 +56,10 @@
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
// Do tests
|
||||
|
||||
//gA11yEventDumpToConsole = true; // debuging
|
||||
//enableLogging("tree,events,verbose");
|
||||
|
||||
var gQueue = null;
|
||||
|
||||
//gA11yEventDumpID = "eventdump";
|
||||
|
||||
function doTests()
|
||||
{
|
||||
gQueue = new eventQueue(nsIAccessibleEvent.EVENT_ALERT);
|
||||
|
@ -87,7 +87,6 @@
|
|||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<div id="eventdump"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -81,8 +81,8 @@
|
|||
this.menu = null;
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_MENUPOPUP_END, getMenu, this),
|
||||
new invokerChecker(EVENT_HIDE, getMenu, this),
|
||||
new invokerChecker(EVENT_MENUPOPUP_END, getMenu, this),
|
||||
new invokerChecker(EVENT_REORDER, getNode(aParentMenuID))
|
||||
];
|
||||
|
||||
|
@ -150,11 +150,10 @@
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
// Do tests
|
||||
|
||||
var gQueue = null;
|
||||
|
||||
//gA11yEventDumpToConsole = true; // debuging
|
||||
//enableLogging("tree,verbose");
|
||||
//enableLogging("tree,events,verbose");
|
||||
|
||||
var gQueue = null;
|
||||
function doTests()
|
||||
{
|
||||
gQueue = new eventQueue();
|
||||
|
@ -257,7 +256,8 @@
|
|||
<div id="mb3-mi-outside" role="menuitem" tabindex="0">Outside</div>
|
||||
|
||||
<div id="menubar4" role="menubar">
|
||||
<div role="menuitem" aria-haspopup="true" aria-owns="mb4-menu">Item</div>
|
||||
<div id="mb4_topitem" role="menuitem" aria-haspopup="true"
|
||||
aria-owns="mb4-menu">Item</div>
|
||||
</div>
|
||||
<div id="mb4-menu" role="menu" style="display:none;">
|
||||
<div role="menuitem" id="mb4-item1" tabindex="0">Item 1.1</div>
|
||||
|
|
|
@ -323,13 +323,39 @@
|
|||
this.initSequence();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove children and parent
|
||||
*/
|
||||
function removeGrandChildrenNHideParent(aChild1Id, aChild2Id, aParentId)
|
||||
{
|
||||
this.child1 = getNode(aChild1Id);
|
||||
this.child2 = getNode(aChild2Id);
|
||||
this.parent = getNode(aParentId);
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getAccessible(aParentId)),
|
||||
new invokerChecker(EVENT_REORDER, getNode(aParentId).parentNode)
|
||||
];
|
||||
|
||||
this.invoke = function removeGrandChildrenNHideParent_invoke()
|
||||
{
|
||||
this.child1.parentNode.removeChild(this.child1);
|
||||
this.child2.parentNode.removeChild(this.child2);
|
||||
this.parent.hidden = true;
|
||||
}
|
||||
|
||||
this.getID = function removeGrandChildrenNHideParent_getID() {
|
||||
return "remove grand children of different parents and then hide their grand parent";
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Do tests.
|
||||
|
||||
var gQueue = null;
|
||||
//gA11yEventDumpToConsole = true; // debug stuff
|
||||
//enableLogging("tree");
|
||||
//enableLogging("events,tree,eventTree,verbose");
|
||||
|
||||
var gQueue = null;
|
||||
function doTests()
|
||||
{
|
||||
gQueue = new eventQueue();
|
||||
|
@ -350,6 +376,8 @@
|
|||
gQueue.push(new showParentNAddChild("select11", false));
|
||||
gQueue.push(new showParentNAddChild("select12", true));
|
||||
|
||||
gQueue.push(new removeGrandChildrenNHideParent("t1_child1", "t1_child2", "t1_parent"));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
@ -412,5 +440,12 @@
|
|||
<select id="select11" style="display: none"></select>
|
||||
<select id="select12" style="display: none"></select>
|
||||
</div>
|
||||
|
||||
<div id="testContainer2">
|
||||
<div id="t1_parent">
|
||||
<div id="t1_mid1"><div id="t1_child1"></div></div>
|
||||
<div id="t1_mid2"><div id="t1_child2"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -361,8 +361,8 @@
|
|||
function showHiddenParentOfVisibleChild()
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode("c4_child")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("c4_middle")),
|
||||
new invokerChecker(EVENT_HIDE, getNode("c4_child")),
|
||||
new invokerChecker(EVENT_REORDER, getNode("c4"))
|
||||
];
|
||||
|
||||
|
@ -415,7 +415,7 @@
|
|||
}
|
||||
|
||||
//gA11yEventDumpToConsole = true; // debug stuff
|
||||
//enableLogging("tree,verbose");
|
||||
//enableLogging("events,verbose");
|
||||
|
||||
/**
|
||||
* Do tests.
|
||||
|
|
|
@ -38,6 +38,27 @@
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* No name change on an accessible, because the accessible is recreated.
|
||||
*/
|
||||
function setAttr_recreate(aID, aAttr, aValue)
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getAccessible(aID)),
|
||||
new invokerChecker(EVENT_SHOW, aID)
|
||||
];
|
||||
this.invoke = function setAttr_recreate_invoke()
|
||||
{
|
||||
todo(false, "No accessible recreation should happen, just name change event");
|
||||
getNode(aID).setAttribute(aAttr, aValue);
|
||||
}
|
||||
|
||||
this.getID = function setAttr_recreate_getID()
|
||||
{
|
||||
return "set attr '" + aAttr + "', value '" + aValue + "'";
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Do tests
|
||||
|
||||
|
@ -64,8 +85,7 @@
|
|||
gQueue.push(new setAttr("tst2", "title", "title",
|
||||
new unexpectedInvokerChecker(EVENT_NAME_CHANGE, "tst2")));
|
||||
|
||||
gQueue.push(new setAttr("tst3", "alt", "alt",
|
||||
new invokerChecker(EVENT_NAME_CHANGE, "tst3")));
|
||||
gQueue.push(new setAttr_recreate("tst3", "alt", "alt"));
|
||||
gQueue.push(new setAttr("tst3", "title", "title",
|
||||
new unexpectedInvokerChecker(EVENT_NAME_CHANGE, "tst3")));
|
||||
|
||||
|
|
|
@ -114,7 +114,5 @@
|
|||
<option id="lb2_item4" value="tomatoes">tomatoes
|
||||
<option id="lb2_item5" value="olives">olives
|
||||
</select>
|
||||
|
||||
<div id="eventdump"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -25,12 +25,20 @@
|
|||
<script type="application/javascript">
|
||||
function advanceTab(aTabsID, aDirection, aNextTabID)
|
||||
{
|
||||
this.eventSeq = [
|
||||
var eventSeq1 = [
|
||||
new invokerChecker(EVENT_SELECTION, aNextTabID)
|
||||
]
|
||||
defineScenario(this, eventSeq1);
|
||||
|
||||
var eventSeq2 = [
|
||||
new invokerChecker(EVENT_HIDE, getAccessible(aNextTabID)),
|
||||
new invokerChecker(EVENT_SHOW, aNextTabID)
|
||||
];
|
||||
defineScenario(this, eventSeq2);
|
||||
|
||||
this.invoke = function advanceTab_invoke()
|
||||
{
|
||||
todo(false, "No accessible recreation should happen, just selection event");
|
||||
getNode(aTabsID).advanceSelectedTab(aDirection, true);
|
||||
}
|
||||
|
||||
|
@ -148,6 +156,7 @@
|
|||
*/
|
||||
var gQueue = null;
|
||||
|
||||
//enableLogging("events");
|
||||
//gA11yEventDumpToConsole = true; // debuggin
|
||||
|
||||
function doTests()
|
||||
|
|
|
@ -254,9 +254,9 @@
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Do tests
|
||||
var gQueue = null;
|
||||
//gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
gA11yEventDumpToConsole = true; // debugging
|
||||
|
||||
var gQueue = null;
|
||||
function doTests()
|
||||
{
|
||||
gQueue = new eventQueue();
|
||||
|
@ -327,7 +327,6 @@
|
|||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<div id="eventdump"></div>
|
||||
|
||||
<p id="p"><span><span>333</span><span>22</span></span>1111</p>
|
||||
<div id="div">hello<div>hello</div>hello</div>
|
||||
|
|
|
@ -28,8 +28,8 @@
|
|||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_button")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_button")),
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_subdiv")),
|
||||
// no hide for t1_subdiv because it is contained by hidden t1_checkbox
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_checkbox")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_checkbox")),
|
||||
new invokerChecker(EVENT_REORDER, getNode("t1_container"))
|
||||
|
@ -73,10 +73,10 @@
|
|||
function removeARIAOwns()
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_subdiv")),
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_button")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_button")),
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_subdiv")),
|
||||
new invokerChecker(EVENT_REORDER, getNode("t1_container"))
|
||||
];
|
||||
|
||||
|
@ -107,9 +107,9 @@
|
|||
function setARIAOwns()
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")),
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_button")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_button")),
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_subdiv")),
|
||||
new invokerChecker(EVENT_REORDER, getNode("t1_container"))
|
||||
];
|
||||
|
@ -142,8 +142,8 @@
|
|||
function addIdToARIAOwns()
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_group")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_group")),
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_group")),
|
||||
new invokerChecker(EVENT_REORDER, document)
|
||||
];
|
||||
|
||||
|
@ -275,8 +275,8 @@
|
|||
function setId()
|
||||
{
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_grouptmp")),
|
||||
new invokerChecker(EVENT_SHOW, getNode("t1_grouptmp")),
|
||||
new invokerChecker(EVENT_HIDE, getNode("t1_grouptmp")),
|
||||
new invokerChecker(EVENT_REORDER, document)
|
||||
];
|
||||
|
||||
|
@ -515,7 +515,7 @@
|
|||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//gA11yEventDumpToConsole = true;
|
||||
//enableLogging("tree,verbose"); // debug stuff
|
||||
//enableLogging("tree,eventTree,verbose"); // debug stuff
|
||||
|
||||
var gQueue = null;
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
this.containerNode = getNode("outerDiv");
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_HIDE, getNode("child")),
|
||||
new invokerChecker(EVENT_HIDE, getNode("childDoc")),
|
||||
new invokerChecker(EVENT_SHOW, "newChildDoc"),
|
||||
new invokerChecker(EVENT_HIDE, getNode("child")),
|
||||
new invokerChecker(EVENT_REORDER, this.containerNode)
|
||||
];
|
||||
|
||||
|
@ -46,7 +46,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
gA11yEventDumpToConsole = true;
|
||||
//enableLogging("tree");
|
||||
//gA11yEventDumpToConsole = true;
|
||||
var gQueue = null;
|
||||
function doTest()
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче