import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1412825 - fix lz4 deprecated attribute with clang and c++14; r=RyanVM (059d86484b)
- Bug 1245886 - Manually stop the profiler module at the end of all tests, r=me (1e00edd00c)
- Bug 1262359 (part 1) - Remove unused |hashRef| parameter from nsDataHandler::ParseURI(). r=jduell. (dcae9e057a)
- Bug 1262359 (part 2) - Make the filling in of two parameters optional in nsDataHandler::ParseURI(). r=jduell. (142ff6c86d)
- Bug 1262359 (part 3) - Add a missing fallible nsTSubstring_CharT::Assign() variant. r=erahm. (df93f41b86)
- Bug 1262359 (part 4) - Make data URL payload assignment fallible in nsDataHandler::ParseURI(). r=jduell. (05868a4269)
- Bug 1262359 (part 5) - Add a missing rv check for call to nsDataHandler::ParseURI(). r=jduell. (67d8a9c642)
- Bug 1263764 - Make the external string API's Truncate compatible with the internal API's Truncate. r=froydnj (b369693809)
- bug 1262563 - stop passing an event to FireShowHideEvent() r=davidb (b2893a18c2)
- bug 1262563 - fix how FireShowHideEvent gets the parent of a hide event target r=davidb (fbf7c39766)
- bug 1262563 - make FireShowHideEvent() return void r=davidb (c77c6c1d57)
- bug 1262563 - make FireShowHideEvent() a method of MaiAtkObject r=davidb (b0cc3aaf06)
- Bug 1260237 - remove InvalidateChildren, r=yzen (1eecf43b01)
- Bug 1251680 - get container accessible computation should take into account the HTML select, r=marcoz (553274c049)
- Bug 1252857 - test value change events for closed HTML select, r=marcoz (e3248842f5)
- Bug 1252857 - value change events for HTML:select have wrong target, r=marcoz (734ace8006)
- Bug 1105611 - Add tests of nsIAccessibleEditableText with contentediable editors which have ::before or ::after, patch=nakano, surkov, r=yzen (3b423d91cd)
- Bug 1249400 - add a test for missed hide events in case of accessible stealing, r=yzen (901c61e650)
- Bug 1255009 - insert children into the tree on content insertion instead the recaching, r=yzen (8074d82484)
- Bug 1255614 - make ProcessInvalidationList to insert accessibles instead the recaching, r=yzen (89a81d8b3f)
- Bug 1255617 - make PutChildrenBack to insert accessibles instead the recaching, r=yzen (39548b5922)
- Bug 1260187 - remove recaching version of DocAccessible::UpdateTreeOnInsertion, r=yzen (4bf8b09193)
- Bug 1260277 - remove empty CacheChildren's, r=marcoz (4eabc70d60)
- Bug 1256461 - merge MoveChild and SeizeChild methods, r=yzen (649b87dfad)
- Bug 1260494 - rebuild child indexes by AutoTreeMutation guard, r=yzen (e49a381192)
- Bug 1260862 - "remove Cache/EnsureChildren". r=mzehe (10751f0792)
- Bug 1260860 - stop illicit accessible stealing, r=yzen (55621a1af3)
- Bug 1260496 - get rid of CacheChildren for application accessible, r=marcoz (cde59765c3)
- Bug 1250878 - add acceptable child check for HTML select, r=marcoz (6e70925079)
- Bug 1252260 - get rid of HTML table CacheChildren, r=marcoz (7108ee2e06)
- Bug 1261165 - remove Accessible::ChildrenFlags, r=yzen (6e6c4db99d)
- Bug 1261167 - remove Accessible::TestChildCache, r=marcoz (69c9276da0)
- Bug 1261170 - add a single node ProcessContentInserted method version, r=yzen (5385e407b8)
- Bug 1261177 - split GetOrCreateAccessible method into two (Get and Create versions), r=yzen (ded9e7c0e5)
- Bug 1261408 - detect ARIA owned children early to avoid tree moving, r=yzen (ffd090ff2c)
- Bug 1261425 - coalesce mutation events by a tree structure, r=yzen (14ca8f3978)
- bug 1261144 - rename AccCollector.{h,cpp} to EmbeddedObjCollector.{h,cpp} r=lsocks (386be7f834)
- bug 1259023 - make nsIAccessible.parent work with proxies r=yzen (d611ef1fbf)
This commit is contained in:
2024-05-14 22:53:16 +08:00
parent 10160cd03d
commit 8655c2747d
90 changed files with 1778 additions and 1328 deletions
+40 -30
View File
@@ -793,24 +793,13 @@ getParentCB(AtkObject *aAtkObj)
if (aAtkObj->accessible_parent)
return aAtkObj->accessible_parent;
AtkObject* atkParent = nullptr;
if (AccessibleWrap* wrapper = GetAccessibleWrap(aAtkObj)) {
Accessible* parent = wrapper->Parent();
atkParent = parent ? AccessibleWrap::GetAtkObject(parent) : nullptr;
} else if (ProxyAccessible* proxy = GetProxy(aAtkObj)) {
ProxyAccessible* parent = proxy->Parent();
if (parent) {
atkParent = GetWrapperFor(parent);
} else {
// Otherwise this should be the proxy for the tab's top level document.
Accessible* outerDocParent = proxy->OuterDocOfRemoteBrowser();
NS_ASSERTION(outerDocParent, "this document should have an outerDoc as a parent");
if (outerDocParent) {
atkParent = AccessibleWrap::GetAtkObject(outerDocParent);
}
}
AccessibleOrProxy acc = GetInternalObj(aAtkObj);
if (acc.IsNull()) {
return nullptr;
}
AccessibleOrProxy parent = acc.Parent();
AtkObject* atkParent = !parent.IsNull() ? GetWrapperFor(parent) : nullptr;
if (atkParent)
atk_object_set_parent(aAtkObj, atkParent);
@@ -1101,6 +1090,16 @@ GetWrapperFor(ProxyAccessible* aProxy)
return reinterpret_cast<AtkObject*>(aProxy->GetWrapper() & ~IS_PROXY);
}
AtkObject*
GetWrapperFor(AccessibleOrProxy aObj)
{
if (aObj.IsProxy()) {
return GetWrapperFor(aObj.AsProxy());
}
return AccessibleWrap::GetAtkObject(aObj.AsAccessible());
}
static uint16_t
GetInterfacesForProxy(ProxyAccessible* aProxy, uint32_t aInterfaces)
{
@@ -1355,16 +1354,33 @@ AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
break;
case nsIAccessibleEvent::EVENT_SHOW:
return FireAtkShowHideEvent(aEvent, atkObj, true);
{
AccMutationEvent* event = downcast_accEvent(aEvent);
Accessible* parentAcc = event ? event->Parent() : accessible->Parent();
AtkObject* parent = AccessibleWrap::GetAtkObject(parentAcc);
NS_ENSURE_STATE(parent);
auto obj = reinterpret_cast<MaiAtkObject*>(atkObj);
obj->FireAtkShowHideEvent(parent, true, aEvent->IsFromUserInput());
return NS_OK;
}
case nsIAccessibleEvent::EVENT_HIDE:
{
// XXX - Handle native dialog accessibles.
if (!accessible->IsRoot() && accessible->HasARIARole() &&
accessible->ARIARole() == roles::DIALOG) {
guint id = g_signal_lookup("deactivate", MAI_TYPE_ATK_OBJECT);
g_signal_emit(atkObj, id, 0);
}
return FireAtkShowHideEvent(aEvent, atkObj, false);
AccMutationEvent* event = downcast_accEvent(aEvent);
Accessible* parentAcc = event ? event->Parent() : accessible->Parent();
AtkObject* parent = AccessibleWrap::GetAtkObject(parentAcc);
NS_ENSURE_STATE(parent);
auto obj = reinterpret_cast<MaiAtkObject*>(atkObj);
obj->FireAtkShowHideEvent(parent, false, aEvent->IsFromUserInput());
return NS_OK;
}
/*
* Because dealing with menu is very different between nsIAccessible
@@ -1577,19 +1593,13 @@ static const char *kMutationStrings[2][2] = {
{ HIDE_EVENT, ADD_EVENT },
};
nsresult
AccessibleWrap::FireAtkShowHideEvent(AccEvent* aEvent,
AtkObject* aObject, bool aIsAdded)
void
MaiAtkObject::FireAtkShowHideEvent(AtkObject* aParent, bool aIsAdded,
bool aFromUser)
{
int32_t indexInParent = getIndexInParentCB(aObject);
AtkObject *parentObject = getParentCB(aObject);
NS_ENSURE_STATE(parentObject);
bool isFromUserInput = aEvent->IsFromUserInput();
const char *signal_name = kMutationStrings[isFromUserInput][aIsAdded];
g_signal_emit_by_name(parentObject, signal_name, indexInParent, aObject, nullptr);
return NS_OK;
int32_t indexInParent = getIndexInParentCB(&this->parent);
const char *signal_name = kMutationStrings[aFromUser][aIsAdded];
g_signal_emit_by_name(aParent, signal_name, indexInParent, this, nullptr);
}
// static
-2
View File
@@ -79,8 +79,6 @@ protected:
nsresult FireAtkStateChangeEvent(AccEvent* aEvent, AtkObject *aObject);
nsresult FireAtkTextChangedEvent(AccEvent* aEvent, AtkObject *aObject);
nsresult FireAtkShowHideEvent(AccEvent* aEvent, AtkObject *aObject,
bool aIsAdded);
AtkObject *mAtkObject;
+7
View File
@@ -69,6 +69,7 @@ mozilla::a11y::AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj);
mozilla::a11y::ProxyAccessible* GetProxy(AtkObject* aAtkObj);
mozilla::a11y::AccessibleOrProxy GetInternalObj(AtkObject* aObj);
AtkObject* GetWrapperFor(mozilla::a11y::ProxyAccessible* aProxy);
AtkObject* GetWrapperFor(mozilla::a11y::AccessibleOrProxy aObj);
extern int atkMajorVersion, atkMinorVersion;
@@ -120,6 +121,12 @@ struct MaiAtkObject
void FireTextChangeEvent(const nsString& aStr, int32_t aStart, uint32_t aLen,
bool aIsInsert, bool aIsFromUser);
/**
* Notify ATK of a shown or hidden subtree rooted at aObject whose parent is
* aParent
*/
void FireAtkShowHideEvent(AtkObject* aParent, bool aIsAdded, bool aFromUser);
private:
/*
* do we have text-remove and text-insert signals if not we need to use
-22
View File
@@ -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
////////////////////////////////////////////////////////////////////////////////
+4 -36
View File
@@ -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;
};
@@ -307,37 +306,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;
};
+27
View File
@@ -0,0 +1,27 @@
/* -*- 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/. */
#include "AccessibleOrProxy.h"
AccessibleOrProxy
AccessibleOrProxy::Parent() const
{
if (IsAccessible()) {
return AsAccessible()->Parent();
}
ProxyAccessible* proxy = AsProxy();
if (!proxy) {
return nullptr;
}
if (ProxyAccessible* parent = proxy->Parent()) {
return parent;
}
// Otherwise this should be the proxy for the tab's top level document.
return proxy->OuterDocOfRemoteBrowser();
}
+2
View File
@@ -106,6 +106,8 @@ public:
return AsAccessible()->Role();
}
AccessibleOrProxy Parent() const;
// XXX these are implementation details that ideally would not be exposed.
uintptr_t Bits() const { return mBits; }
void SetBits(uintptr_t aBits) { mBits = aBits; }
-3
View File
@@ -61,9 +61,6 @@ DocManager::GetDocAccessible(nsIDocument* aDocument)
if (!aDocument)
return nullptr;
// Ensure CacheChildren is called before we query cache.
ApplicationAcc()->EnsureChildren();
DocAccessible* docAcc = GetExistingDocAccessible(aDocument);
if (docAcc)
return docAcc;
@@ -2,7 +2,7 @@
* 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 "AccCollector.h"
#include "EmbeddedObjCollector.h"
#include "Accessible.h"
@@ -2,8 +2,8 @@
* 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_AccCollector_h__
#define mozilla_a11y_AccCollector_h__
#ifndef mozilla_a11y_EmbeddedObjCollector_h__
#define mozilla_a11y_EmbeddedObjCollector_h__
#include "Filters.h"
+20 -241
View File
@@ -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,95 +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);
// Sometimes InvalidateChildren() and
// DocAccessible::CacheChildrenInSubtree() can conspire to reparent an
// accessible in this case no need for mutation events. Se bug 883708
// for details.
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,
@@ -395,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
@@ -541,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)
+6 -17
View File
@@ -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
+536
View File
@@ -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);
}
+113
View File
@@ -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_
+1
View File
@@ -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 },
+9 -8
View File
@@ -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
};
/**
+18 -6
View File
@@ -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
@@ -301,13 +312,11 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
#endif
// Make sure the text node is in accessible document still.
Accessible* container = mDocument->GetAccessibleOrContainer(containerNode);
NS_ASSERTION(container,
"Text node having rendered text hasn't accessible document!");
Accessible* container = mDocument->AccessibleOrTrueContainer(containerNode);
MOZ_ASSERT(container,
"Text node having rendered text hasn't accessible document!");
if (container) {
nsTArray<nsCOMPtr<nsIContent> > insertedContents;
insertedContents.AppendElement(textNode);
mDocument->ProcessContentInserted(container, &insertedContents);
mDocument->ProcessContentInserted(container, textNode);
}
}
}
@@ -390,6 +399,9 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
// events causes script to run.
mObservingState = eRefreshProcessing;
mEventTree.Process();
mEventTree.Clear();
ProcessEventQueue();
if (IPCAccessibilityActive()) {
+31 -2
View File
@@ -7,6 +7,7 @@
#define mozilla_a11y_NotificationController_h_
#include "EventQueue.h"
#include "EventTree.h"
#include "mozilla/IndexSequence.h"
#include "mozilla/Tuple.h"
@@ -104,14 +105,37 @@ 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
+17 -13
View File
@@ -275,23 +275,27 @@ TreeWalker::Prev()
Accessible*
TreeWalker::AccessibleFor(nsIContent* aNode, uint32_t aFlags, bool* aSkipSubtree)
{
Accessible* child = nullptr;
if (aFlags & eWalkCache) {
child = mDoc->GetAccessible(aNode);
}
else if (mContext->IsAcceptableChild(aNode)) {
child = GetAccService()->
GetOrCreateAccessible(aNode, mContext, aSkipSubtree);
}
// Ignore the accessible and its subtree if it was repositioned by means
// of aria-owns.
if (child && child->IsRelocated()) {
*aSkipSubtree = true;
return nullptr;
Accessible* child = mDoc->GetAccessible(aNode);
if (child) {
if (child->IsRelocated()) {
*aSkipSubtree = true;
return nullptr;
}
return child;
}
return child;
// Create an accessible if allowed.
if (!(aFlags & eWalkCache) && mContext->IsAcceptableChild(aNode)) {
if (mDoc->RelocateARIAOwnedIfNeeded(aNode)) {
*aSkipSubtree = true;
return nullptr;
}
return GetAccService()->CreateAccessible(aNode, mContext, aSkipSubtree);
}
return nullptr;
}
dom::AllChildrenIterator*
+3 -1
View File
@@ -26,7 +26,7 @@ if CONFIG['MOZ_DEBUG']:
]
UNIFIED_SOURCES += [
'AccCollector.cpp',
'AccessibleOrProxy.cpp',
'AccEvent.cpp',
'AccGroupInfo.cpp',
'AccIterator.cpp',
@@ -34,7 +34,9 @@ UNIFIED_SOURCES += [
'ARIAStateMap.cpp',
'Asserts.cpp',
'DocManager.cpp',
'EmbeddedObjCollector.cpp',
'EventQueue.cpp',
'EventTree.cpp',
'Filters.cpp',
'FocusManager.cpp',
'NotificationController.cpp',
+14 -18
View File
@@ -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
@@ -1010,26 +1011,20 @@ nsAccessibilityService::IsLogged(const nsAString& aModule, bool* aIsLogged)
// nsAccessibilityService public
Accessible*
nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
Accessible* aContext,
bool* aIsSubtreeHidden)
nsAccessibilityService::CreateAccessible(nsINode* aNode,
Accessible* aContext,
bool* aIsSubtreeHidden)
{
NS_PRECONDITION(aContext && aNode && !gIsShutdown,
"Maybe let'd do a crash? Oh, yes, baby!");
MOZ_ASSERT(aContext, "No context provided");
MOZ_ASSERT(aNode, "No node to create an accessible for");
MOZ_ASSERT(!gIsShutdown, "No creation after shutdown");
if (aIsSubtreeHidden)
*aIsSubtreeHidden = false;
DocAccessible* document = aContext->Document();
// Check to see if we already have an accessible for this node in the cache.
// XXX: we don't have context check here. It doesn't really necessary until
// we have in-law children adoption.
Accessible* cachedAccessible = document->GetAccessible(aNode);
if (cachedAccessible)
return cachedAccessible;
// No cache entry, so we must create the accessible.
MOZ_ASSERT(!document->GetAccessible(aNode),
"We already have an accessible for this node.");
if (aNode->IsNodeOfType(nsINode::eDOCUMENT)) {
// If it's document node then ask accessible document loader for
@@ -1327,12 +1322,15 @@ nsAccessibilityService::Init()
logging::CheckEnv();
#endif
gAccessibilityService = this;
if (XRE_IsParentProcess())
gApplicationAccessible = new ApplicationAccessibleWrap();
else
gApplicationAccessible = new ApplicationAccessible();
NS_ADDREF(gApplicationAccessible); // will release in Shutdown()
gApplicationAccessible->Init();
#ifdef MOZ_CRASHREPORTER
CrashReporter::
@@ -1828,9 +1826,7 @@ NS_GetAccessibilityService(nsIAccessibilityService** aResult)
statistics::A11yInitialized();
nsAccessibilityService::gAccessibilityService = service;
NS_ADDREF(*aResult = service);
return NS_OK;
}
+3 -4
View File
@@ -180,16 +180,15 @@ public:
static bool IsShutdown() { return gIsShutdown; }
/**
* Return an accessible for the given DOM node from the cache or create new
* one.
* Creates an accessible for the given DOM node.
*
* @param aNode [in] the given node
* @param aContext [in] context the accessible is created in
* @param aIsSubtreeHidden [out, optional] indicates whether the node's
* frame and its subtree is hidden
*/
Accessible* GetOrCreateAccessible(nsINode* aNode, Accessible* aContext,
bool* aIsSubtreeHidden = nullptr);
Accessible* CreateAccessible(nsINode* aNode, Accessible* aContext,
bool* aIsSubtreeHidden = nullptr);
mozilla::a11y::role MarkupRole(const nsIContent* aContent) const
{
+49 -138
View File
@@ -7,15 +7,18 @@
#include "nsIXBLAccessible.h"
#include "AccCollector.h"
#include "EmbeddedObjCollector.h"
#include "AccGroupInfo.h"
#include "AccIterator.h"
#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"
#include "RootAccessible.h"
@@ -106,7 +109,7 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(Accessible, LastRelease())
Accessible::Accessible(nsIContent* aContent, DocAccessible* aDoc) :
mContent(aContent), mDoc(aDoc),
mParent(nullptr), mIndexInParent(-1), mChildrenFlags(eChildrenUninitialized),
mParent(nullptr), mIndexInParent(-1),
mStateFlags(0), mContextFlags(0), mType(0), mGenericTypes(0),
mRoleMapEntry(nullptr)
{
@@ -1879,7 +1882,14 @@ Accessible::Shutdown()
// other accessibles, also make sure none of its children point to this parent
mStateFlags |= eIsDefunct;
InvalidateChildren();
int32_t childCount = mChildren.Length();
for (int32_t childIdx = 0; childIdx < childCount; childIdx++) {
mChildren.ElementAt(childIdx)->UnbindFromParent();
}
mChildren.Clear();
mEmbeddedObjCollector = nullptr;
if (mParent)
mParent->RemoveChild(this);
@@ -1954,26 +1964,21 @@ Accessible::NativeName(nsString& aName)
void
Accessible::BindToParent(Accessible* aParent, uint32_t aIndexInParent)
{
NS_PRECONDITION(aParent, "This method isn't used to set null parent!");
MOZ_ASSERT(aParent, "This method isn't used to set null parent");
MOZ_ASSERT(!mParent, "The child was expected to be moved");
#ifdef A11Y_LOG
if (mParent) {
if (mParent != aParent) {
NS_ERROR("Adopting child!");
mParent->InvalidateChildrenGroupInfo();
mParent->RemoveChild(this);
} else {
NS_ERROR("Binding to the same parent!");
return;
}
logging::TreeInfo("BindToParent: stealing accessible", 0,
"old parent", mParent.get(),
"new parent", aParent,
"child", this, nullptr);
}
#endif
mParent = aParent;
mIndexInParent = aIndexInParent;
#ifdef DEBUG
AssertInMutatingSubtree();
#endif
// Note: this is currently only used for richlistitems and their children.
if (mParent->HasNameDependentParent() || mParent->IsXULListItem())
mContextFlags |= eHasNameDependentParent;
@@ -1992,9 +1997,6 @@ Accessible::BindToParent(Accessible* aParent, uint32_t aIndexInParent)
void
Accessible::UnbindFromParent()
{
#ifdef DEBUG
AssertInMutatingSubtree();
#endif
mParent = nullptr;
mIndexInParent = -1;
mInt.mIndexOfEmbeddedChild = -1;
@@ -2056,19 +2058,6 @@ Accessible::Language(nsAString& aLanguage)
}
}
void
Accessible::InvalidateChildren()
{
int32_t childCount = mChildren.Length();
for (int32_t childIdx = 0; childIdx < childCount; childIdx++) {
mChildren.ElementAt(childIdx)->UnbindFromParent();
}
mEmbeddedObjCollector = nullptr;
mChildren.Clear();
SetChildrenFlag(eChildrenUninitialized);
}
bool
Accessible::InsertChildAt(uint32_t aIndex, Accessible* aChild)
{
@@ -2083,17 +2072,12 @@ Accessible::InsertChildAt(uint32_t aIndex, Accessible* aChild)
if (!mChildren.InsertElementAt(aIndex, aChild))
return false;
for (uint32_t idx = aIndex + 1; idx < mChildren.Length(); idx++) {
NS_ASSERTION(static_cast<uint32_t>(mChildren[idx]->mIndexInParent) == idx - 1,
"Accessible child index doesn't match");
mChildren[idx]->mIndexInParent = idx;
}
mEmbeddedObjCollector = nullptr;
MOZ_ASSERT(mStateFlags & eKidsMutating, "Illicit children change");
}
if (!nsAccUtils::IsEmbeddedObject(aChild))
SetChildrenFlag(eMixedChildren);
if (!nsAccUtils::IsEmbeddedObject(aChild)) {
mStateFlags |= eHasTextKids;
}
aChild->BindToParent(this, aIndex);
return true;
@@ -2108,23 +2092,21 @@ Accessible::RemoveChild(Accessible* aChild)
if (aChild->mParent != this || aChild->mIndexInParent == -1)
return false;
uint32_t index = static_cast<uint32_t>(aChild->mIndexInParent);
if (index >= mChildren.Length() || mChildren[index] != aChild) {
NS_ERROR("Child is bound to parent but parent hasn't this child at its index!");
aChild->UnbindFromParent();
return false;
}
MOZ_ASSERT((mStateFlags & eKidsMutating) || aChild->IsDefunct() || aChild->IsDoc(),
"Illicit children change");
for (uint32_t idx = index + 1; idx < mChildren.Length(); idx++) {
NS_ASSERTION(static_cast<uint32_t>(mChildren[idx]->mIndexInParent) == idx,
"Accessible child index doesn't match");
mChildren[idx]->mIndexInParent = idx - 1;
int32_t index = static_cast<uint32_t>(aChild->mIndexInParent);
// If we adopt a child during a tree construction, then indexes might be not
// rebuilt yet.
if (mChildren.SafeElementAt(index) != aChild) {
index = mChildren.IndexOf(aChild);
MOZ_ASSERT(index != -1,
"Child is bound to parent but parent hasn't this child at its index.");
}
aChild->UnbindFromParent();
mChildren.RemoveElementAt(index);
mEmbeddedObjCollector = nullptr;
return true;
}
@@ -2138,10 +2120,10 @@ Accessible::MoveChild(uint32_t aNewIndex, Accessible* aChild)
"No move, same index");
MOZ_ASSERT(aNewIndex <= mChildren.Length(), "Wrong new index was given");
#ifdef DEBUG
// AutoTreeMutation should update group info.
AssertInMutatingSubtree();
#endif
EventTree* eventTree = mDoc->Controller()->QueueMutation(this);
if (eventTree) {
eventTree->Hidden(aChild, false);
}
mEmbeddedObjCollector = nullptr;
mChildren.RemoveElementAt(aChild->mIndexInParent);
@@ -2151,7 +2133,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);
@@ -2169,8 +2150,14 @@ Accessible::MoveChild(uint32_t aNewIndex, Accessible* aChild)
for (uint32_t idx = startIdx; idx <= endIdx; idx++) {
mChildren[idx]->mIndexInParent = idx;
mChildren[idx]->mStateFlags |= eGroupInfoDirty;
mChildren[idx]->mInt.mIndexOfEmbeddedChild = -1;
}
if (eventTree) {
eventTree->Shown(aChild);
mDoc->Controller()->QueueNameChange(aChild);
}
}
Accessible*
@@ -2204,7 +2191,7 @@ Accessible::IndexInParent() const
uint32_t
Accessible::EmbeddedChildCount()
{
if (IsChildrenFlag(eMixedChildren)) {
if (mStateFlags & eHasTextKids) {
if (!mEmbeddedObjCollector)
mEmbeddedObjCollector = new EmbeddedObjCollector(this);
return mEmbeddedObjCollector->Count();
@@ -2216,7 +2203,7 @@ Accessible::EmbeddedChildCount()
Accessible*
Accessible::GetEmbeddedChildAt(uint32_t aIndex)
{
if (IsChildrenFlag(eMixedChildren)) {
if (mStateFlags & eHasTextKids) {
if (!mEmbeddedObjCollector)
mEmbeddedObjCollector = new EmbeddedObjCollector(this);
return mEmbeddedObjCollector ?
@@ -2229,7 +2216,7 @@ Accessible::GetEmbeddedChildAt(uint32_t aIndex)
int32_t
Accessible::GetIndexOfEmbeddedChild(Accessible* aChild)
{
if (IsChildrenFlag(eMixedChildren)) {
if (mStateFlags & eHasTextKids) {
if (!mEmbeddedObjCollector)
mEmbeddedObjCollector = new EmbeddedObjCollector(this);
return mEmbeddedObjCollector ?
@@ -2542,53 +2529,6 @@ Accessible::LastRelease()
delete this;
}
void
Accessible::CacheChildren()
{
NS_ENSURE_TRUE_VOID(Document());
TreeWalker walker(this);
Accessible* child = nullptr;
while ((child = walker.Next()) && AppendChild(child));
}
void
Accessible::TestChildCache(Accessible* aCachedChild) const
{
#ifdef DEBUG
int32_t childCount = mChildren.Length();
if (childCount == 0) {
NS_ASSERTION(IsChildrenFlag(eChildrenUninitialized),
"No children but initialized!");
return;
}
Accessible* child = nullptr;
for (int32_t childIdx = 0; childIdx < childCount; childIdx++) {
child = mChildren[childIdx];
if (child == aCachedChild)
break;
}
NS_ASSERTION(child == aCachedChild,
"[TestChildCache] cached accessible wasn't found. Wrong accessible tree!");
#endif
}
void
Accessible::EnsureChildren()
{
NS_ASSERTION(!IsDefunct(), "Caching children for defunct accessible!");
if (!IsChildrenFlag(eChildrenUninitialized))
return;
// State is embedded children until text leaf accessible is appended.
SetChildrenFlag(eEmbeddedChildren); // Prevent reentry
CacheChildren();
}
Accessible*
Accessible::GetSiblingAtOffset(int32_t aOffset, nsresult* aError) const
{
@@ -2666,7 +2606,7 @@ Accessible::GetGroupInfo()
if (mBits.groupInfo){
if (HasDirtyGroupInfo()) {
mBits.groupInfo->Update();
SetDirtyGroupInfo(false);
mStateFlags &= ~eGroupInfoDirty;
}
return mBits.groupInfo;
@@ -2676,16 +2616,6 @@ Accessible::GetGroupInfo()
return mBits.groupInfo;
}
void
Accessible::InvalidateChildrenGroupInfo()
{
uint32_t length = mChildren.Length();
for (uint32_t i = 0; i < length; i++) {
Accessible* child = mChildren[i];
child->SetDirtyGroupInfo(true);
}
}
void
Accessible::GetPositionAndSizeInternal(int32_t *aPosInSet, int32_t *aSetSize)
{
@@ -2767,8 +2697,6 @@ Accessible::GetLevelInternal()
void
Accessible::StaticAsserts() const
{
static_assert(eLastChildrenFlag <= (1 << kChildrenFlagsBits) - 1,
"Accessible::mChildrenFlags was oversized by eLastChildrenFlag!");
static_assert(eLastStateFlag <= (1 << kStateFlagsBits) - 1,
"Accessible::mStateFlags was oversized by eLastStateFlag!");
static_assert(eLastAccType <= (1 << kTypeBits) - 1,
@@ -2779,22 +2707,6 @@ Accessible::StaticAsserts() const
"Accessible::mGenericType was oversized by eLastAccGenericType!");
}
void
Accessible::AssertInMutatingSubtree() const
{
if (IsDoc() || IsApplication())
return;
const Accessible *acc = this;
while (!acc->IsDoc() && !(acc->mStateFlags & eSubtreeMutating)) {
acc = acc->Parent();
if (!acc)
return;
}
MOZ_ASSERT(acc->mStateFlags & eSubtreeMutating);
}
////////////////////////////////////////////////////////////////////////////////
// KeyBinding class
@@ -2889,4 +2801,3 @@ KeyBinding::ToAtkFormat(nsAString& aValue) const
aValue.Append(mKey);
}
+24 -107
View File
@@ -33,6 +33,7 @@ class AccGroupInfo;
class ApplicationAccessible;
class DocAccessible;
class EmbeddedObjCollector;
class EventTree;
class HTMLImageMapAccessible;
class HTMLLIAccessible;
class HyperTextAccessible;
@@ -375,25 +376,20 @@ public:
void SetRoleMapEntry(const nsRoleMapEntry* aRoleMapEntry)
{ mRoleMapEntry = aRoleMapEntry; }
/**
* Cache children if necessary.
*/
void EnsureChildren();
/**
* Set the child count to -1 (unknown) and null out cached child pointers.
* Should be called when accessible tree is changed because document has
* transformed. Note, if accessible cares about its parent relation chain
* itself should override this method to do nothing.
*/
virtual void InvalidateChildren();
/**
* Append/insert/remove a child. Return true if operation was successful.
*/
bool AppendChild(Accessible* aChild)
{ return InsertChildAt(mChildren.Length(), aChild); }
virtual bool InsertChildAt(uint32_t aIndex, Accessible* aChild);
bool InsertAfter(Accessible* aNewChild, Accessible* aRefChild)
{
MOZ_ASSERT(aNewChild, "No new child to insert");
return InsertChildAt(aRefChild ? aRefChild->IndexInParent() + 1 : 0,
aNewChild);
}
virtual bool RemoveChild(Accessible* aChild);
/**
@@ -474,12 +470,6 @@ public:
Accessible* ContentChildAt(uint32_t aIndex) const
{ return mChildren.ElementAt(aIndex); }
/**
* Return true if children were initialized.
*/
inline bool AreChildrenCached() const
{ return !IsChildrenFlag(eChildrenUninitialized); }
/**
* Return true if the accessible is attached to tree.
*/
@@ -513,12 +503,6 @@ public:
virtual void AppendTextTo(nsAString& aText, uint32_t aStartOffset = 0,
uint32_t aLength = UINT32_MAX);
/**
* Assert if child not in parent's cache if the cache was initialized at this
* point.
*/
void TestChildCache(Accessible* aCachedChild) const;
/**
* Return boundaries in screen coordinates.
*/
@@ -590,6 +574,7 @@ public:
HyperTextAccessible* AsHyperText();
bool IsHTMLBr() const { return mType == eHTMLBRType; }
bool IsHTMLCaption() const { return mType == eHTMLCaptionType; }
bool IsHTMLCombobox() const { return mType == eHTMLComboboxType; }
bool IsHTMLFileInput() const { return mType == eHTMLFileInputType; }
@@ -932,7 +917,13 @@ public:
* Return true if the accessible doesn't allow accessible children from XBL
* anonymous subtree.
*/
bool NoXBLKids() { return mStateFlags & eNoXBLKids; }
bool NoXBLKids() const { return mStateFlags & eNoXBLKids; }
/**
* Return true if the accessible allows accessible children from subtree of
* a DOM element of this accessible.
*/
bool KidsFromDOM() const { return !(mStateFlags & eNoKidsFromDOM); }
/**
* Return true if this accessible has a parent whose name depends on this
@@ -976,11 +967,6 @@ protected:
*/
void LastRelease();
/**
* Cache accessible children.
*/
virtual void CacheChildren();
/**
* Set accessible parent and index in parent.
*/
@@ -993,31 +979,8 @@ protected:
virtual Accessible* GetSiblingAtOffset(int32_t aOffset,
nsresult *aError = nullptr) const;
/**
* Flags used to describe the state and type of children.
*/
enum ChildrenFlags {
eChildrenUninitialized = 0, // children aren't initialized
eMixedChildren = 1 << 0, // text leaf children are presented
eEmbeddedChildren = 1 << 1, // all children are embedded objects
eLastChildrenFlag = eEmbeddedChildren
};
/**
* Return true if the children flag is set.
*/
bool IsChildrenFlag(ChildrenFlags aFlag) const
{ return static_cast<ChildrenFlags>(mChildrenFlags) == aFlag; }
/**
* Set children flag.
*/
void SetChildrenFlag(ChildrenFlags aFlag) { mChildrenFlags = aFlag; }
/**
* Flags used to describe the state of this accessible.
* @note keep these flags in sync with ChildrenFlags
*/
enum StateFlags {
eIsDefunct = 1 << 0, // accessible is defunct
@@ -1026,13 +989,15 @@ protected:
eNotNodeMapEntry = 1 << 3, // accessible shouldn't be in document node map
eHasNumericValue = 1 << 4, // accessible has a numeric value
eGroupInfoDirty = 1 << 5, // accessible needs to update group info
eSubtreeMutating = 1 << 6, // subtree is being mutated
eKidsMutating = 1 << 6, // subtree is being mutated
eIgnoreDOMUIEvent = 1 << 7, // don't process DOM UI events for a11y events
eSurvivingInUpdate = 1 << 8, // parent drops children to recollect them
eRelocated = 1 << 9, // accessible was moved in tree
eNoXBLKids = 1 << 10, // accessible don't allows XBL children
eNoKidsFromDOM = 1 << 11, // accessible doesn't allow children from DOM
eHasTextKids = 1 << 12, // accessible have a text leaf in children
eLastStateFlag = eNoXBLKids
eLastStateFlag = eNoKidsFromDOM
};
/**
@@ -1123,22 +1088,6 @@ protected:
*/
AccGroupInfo* GetGroupInfo();
/**
* Set dirty state of the accessible's group info.
*/
inline void SetDirtyGroupInfo(bool aIsDirty)
{
if (aIsDirty)
mStateFlags |= eGroupInfoDirty;
else
mStateFlags &= ~eGroupInfoDirty;
}
/**
* Flag all children group info as needing to be updated.
*/
void InvalidateChildrenGroupInfo();
// Data Members
nsCOMPtr<nsIContent> mContent;
DocAccessible* mDoc;
@@ -1147,27 +1096,24 @@ protected:
nsTArray<RefPtr<Accessible> > mChildren;
int32_t mIndexInParent;
static const uint8_t kChildrenFlagsBits = 2;
static const uint8_t kStateFlagsBits = 11;
static const uint8_t kStateFlagsBits = 13;
static const uint8_t kContextFlagsBits = 3;
static const uint8_t kTypeBits = 6;
static const uint8_t kGenericTypesBits = 15;
/**
* Keep in sync with ChildrenFlags, StateFlags, ContextFlags, and AccTypes.
* Keep in sync with StateFlags, ContextFlags, and AccTypes.
*/
uint32_t mChildrenFlags : kChildrenFlagsBits;
uint32_t mStateFlags : kStateFlagsBits;
uint32_t mContextFlags : kContextFlagsBits;
uint32_t mType : kTypeBits;
uint32_t mGenericTypes : kGenericTypesBits;
void StaticAsserts() const;
void AssertInMutatingSubtree() const;
friend class DocAccessible;
friend class xpcAccessible;
friend class AutoTreeMutation;
friend class TreeMutation;
nsAutoPtr<mozilla::a11y::EmbeddedObjCollector> mEmbeddedObjCollector;
union {
@@ -1259,35 +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* aRoot, bool aInvalidationRequired = true) :
mInvalidationRequired(aInvalidationRequired), mRoot(aRoot)
{
MOZ_ASSERT(!(mRoot->mStateFlags & Accessible::eSubtreeMutating));
mRoot->mStateFlags |= Accessible::eSubtreeMutating;
}
~AutoTreeMutation()
{
if (mInvalidationRequired)
mRoot->InvalidateChildrenGroupInfo();
MOZ_ASSERT(mRoot->mStateFlags & Accessible::eSubtreeMutating);
mRoot->mStateFlags &= ~Accessible::eSubtreeMutating;
}
bool mInvalidationRequired;
private:
Accessible* mRoot;
};
} // namespace a11y
} // namespace mozilla
+1 -15
View File
@@ -150,29 +150,15 @@ ApplicationAccessible::NativeState()
return 0;
}
void
ApplicationAccessible::InvalidateChildren()
{
// Do nothing because application children are kept updated by AppendChild()
// and RemoveChild() method calls.
}
KeyBinding
ApplicationAccessible::AccessKey() const
{
return KeyBinding();
}
////////////////////////////////////////////////////////////////////////////////
// Accessible protected methods
void
ApplicationAccessible::CacheChildren()
ApplicationAccessible::Init()
{
// CacheChildren is called only once for application accessible when its
// children are requested because empty InvalidateChldren() prevents its
// repeated calls.
// Basically children are kept updated by Append/RemoveChild method calls.
// However if there are open windows before accessibility was started
// then we need to make sure root accessibles for open windows are created so
+2 -3
View File
@@ -52,12 +52,12 @@ public:
EWhichChildAtPoint aWhichChild) override;
virtual Accessible* FocusedChild() override;
virtual void InvalidateChildren() override;
// ActionAccessible
virtual KeyBinding AccessKey() const override;
// ApplicationAccessible
void Init();
void AppName(nsAString& aName) const
{
nsAutoCString cname;
@@ -88,7 +88,6 @@ protected:
virtual ~ApplicationAccessible() {}
// Accessible
virtual void CacheChildren() override;
virtual Accessible* GetSiblingAtOffset(int32_t aOffset,
nsresult *aError = nullptr) const override;
+1 -9
View File
@@ -24,6 +24,7 @@ LeafAccessible::
LeafAccessible(nsIContent* aContent, DocAccessible* aDoc) :
AccessibleWrap(aContent, aDoc)
{
mStateFlags |= eNoKidsFromDOM;
}
NS_IMPL_ISUPPORTS_INHERITED0(LeafAccessible, Accessible)
@@ -53,15 +54,6 @@ LeafAccessible::RemoveChild(Accessible* aChild)
return false;
}
////////////////////////////////////////////////////////////////////////////////
// LeafAccessible: Accessible private
void
LeafAccessible::CacheChildren()
{
// No children for leaf accessible.
}
////////////////////////////////////////////////////////////////////////////////
// LinkableAccessible
-3
View File
@@ -40,9 +40,6 @@ public:
protected:
virtual ~LeafAccessible() {}
// Accessible
virtual void CacheChildren() override;
};
/**
+12
View File
@@ -22,6 +22,18 @@
namespace mozilla {
namespace a11y {
inline Accessible*
DocAccessible::AccessibleOrTrueContainer(nsINode* aNode) const
{
// HTML comboboxes have no-content list accessible as an intermediate
// containing all options.
Accessible* container = GetAccessibleOrContainer(aNode);
if (container && container->IsHTMLCombobox()) {
return container->FirstChild();
}
return container;
}
inline nsIAccessiblePivot*
DocAccessible::VirtualCursor()
{
+366 -304
View File
@@ -387,33 +387,6 @@ DocAccessible::DocType(nsAString& aType) const
docType->GetPublicId(aType);
}
Accessible*
DocAccessible::GetAccessible(nsINode* aNode) const
{
Accessible* accessible = mNodeToAccessibleMap.Get(aNode);
// No accessible in the cache, check if the given ID is unique ID of this
// document accessible.
if (!accessible) {
if (GetNode() != aNode)
return nullptr;
accessible = const_cast<DocAccessible*>(this);
}
#ifdef DEBUG
// All cached accessible nodes should be in the parent
// It will assert if not all the children were created
// when they were first cached, and no invalidation
// ever corrected parent accessible's child cache.
Accessible* parent = accessible->Parent();
if (parent)
parent->TestChildCache(accessible);
#endif
return accessible;
}
////////////////////////////////////////////////////////////////////////////////
// Accessible
@@ -495,13 +468,7 @@ DocAccessible::Shutdown()
mDependentIDsHash.Clear();
mNodeToAccessibleMap.Clear();
{
// We're about to get rid of all of our children so there won't be anything
// to invalidate.
AutoTreeMutation mut(this, false);
ClearCache(mAccessibleCache);
}
ClearCache(mAccessibleCache);
HyperTextAccessibleWrap::Shutdown();
@@ -1296,8 +1263,6 @@ DocAccessible::BindToDocument(Accessible* aAccessible,
if (el->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_owns)) {
mNotificationController->ScheduleRelocation(aAccessible);
}
RelocateARIAOwnedIfNeeded(el);
}
}
@@ -1348,7 +1313,7 @@ DocAccessible::ContentInserted(nsIContent* aContainerNode,
// Update the whole tree of this document accessible when the container is
// null (document element is inserted or removed).
Accessible* container = aContainerNode ?
GetAccessibleOrContainer(aContainerNode) : this;
AccessibleOrTrueContainer(aContainerNode) : this;
if (container) {
// Ignore notification if the container node is no longer in the DOM tree.
mNotificationController->ScheduleContentInsertion(container,
@@ -1389,8 +1354,9 @@ DocAccessible::ProcessInvalidationList()
nsIContent* content = mInvalidationList[idx];
if (!HasAccessible(content)) {
Accessible* container = GetContainerAccessible(content);
if (container)
UpdateTreeOnInsertion(container);
if (container) {
ProcessContentInserted(container, content);
}
}
}
@@ -1461,9 +1427,7 @@ DocAccessible::DoInitialUpdate()
// Set up a root element and ARIA role mapping.
UpdateRootElIfNeeded();
// Build initial tree. Since its the initial tree there's no group info to
// invalidate.
AutoTreeMutation mut(this, false);
// Build initial tree.
CacheChildrenInSubtree(this);
// Fire reorder event after the document tree is constructed. Note, since
@@ -1668,95 +1632,218 @@ DocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
return false;
}
void
DocAccessible::ProcessContentInserted(Accessible* aContainer,
const nsTArray<nsCOMPtr<nsIContent> >* aInsertedContent)
/**
* Content insertion helper.
*/
class InsertIterator final
{
// Process insertions if the container accessible is still in tree.
if (!HasAccessible(aContainer->GetNode()))
return;
public:
InsertIterator(Accessible* aContext,
const nsTArray<nsCOMPtr<nsIContent> >* aNodes) :
mChild(nullptr), mChildBefore(nullptr), mWalker(aContext),
mStopNode(nullptr), mNodes(aNodes), mNodesIdx(0)
{
MOZ_ASSERT(aContext, "No context");
MOZ_ASSERT(aNodes, "No nodes to search for accessible elements");
MOZ_COUNT_CTOR(InsertIterator);
}
~InsertIterator() { MOZ_COUNT_DTOR(InsertIterator); }
Accessible* Context() const { return mWalker.Context(); }
Accessible* Child() const { return mChild; }
Accessible* ChildBefore() const { return mChildBefore; }
DocAccessible* Document() const { return mWalker.Document(); }
/**
* Iterates to a next accessible within the inserted content.
*/
bool Next();
private:
Accessible* mChild;
Accessible* mChildBefore;
TreeWalker mWalker;
nsIContent* mStopNode;
const nsTArray<nsCOMPtr<nsIContent> >* mNodes;
uint32_t mNodesIdx;
};
bool
InsertIterator::Next()
{
if (mNodesIdx > 0) {
Accessible* nextChild = mWalker.Next(mStopNode);
if (nextChild) {
mChildBefore = mChild;
mChild = nextChild;
return true;
}
}
while (mNodesIdx < mNodes->Length()) {
// Ignore nodes that are not contained by the container anymore.
for (uint32_t idx = 0; idx < aInsertedContent->Length(); idx++) {
// The container might be changed, for example, because of the subsequent
// overlapping content insertion (i.e. other content was inserted between
// this inserted content and its container or the content was reinserted
// into different container of unrelated part of tree). To avoid a double
// processing of the content insertion ignore this insertion notification.
// Note, the inserted content might be not in tree at all at this point what
// means there's no container. Ignore the insertion too.
Accessible* container =
GetContainerAccessible(aInsertedContent->ElementAt(idx));
if (container != aContainer)
// Note, the inserted content might be not in tree at all at this point
// what means there's no container. Ignore the insertion too.
nsIContent* prevNode = mNodes->SafeElementAt(mNodesIdx - 1);
nsIContent* node = mNodes->ElementAt(mNodesIdx++);
Accessible* container = Document()->AccessibleOrTrueContainer(node);
if (container != Context()) {
continue;
if (container == this) {
// If new root content has been inserted then update it.
UpdateRootElIfNeeded();
// Continue to update the tree even if we don't have root content.
// For example, elements may be inserted under the document element while
// there is no HTML body element.
}
// HTML comboboxes have no-content list accessible as an intermidiate
// HTML comboboxes have no-content list accessible as an intermediate
// containing all options.
if (container->IsHTMLCombobox())
if (container->IsHTMLCombobox()) {
container = container->FirstChild();
}
// We have a DOM/layout change under the container accessible, and its tree
// might need an update. Since DOM/layout change of the element may affect
// on the accessibleness of adjacent elements (for example, insertion of
// extra HTML:body make the old body accessible) then we have to recache
// children of the container, and then fire show/hide events for a change.
UpdateTreeOnInsertion(container);
break;
if (!container->IsAcceptableChild(node)) {
continue;
}
#ifdef A11Y_LOG
logging::TreeInfo("traversing an inserted node", logging::eVerbose,
"container", container, "node", node);
#endif
// If inserted nodes are siblings then just move the walker next.
if (prevNode && prevNode->GetNextSibling() == node) {
mStopNode = node;
Accessible* nextChild = mWalker.Next(mStopNode);
if (nextChild) {
mChildBefore = mChild;
mChild = nextChild;
return true;
}
}
else if (mWalker.Seek(node)) {
mStopNode = node;
mChildBefore = mWalker.Prev();
mChild = mWalker.Next(mStopNode);
if (mChild) {
return true;
}
}
}
return false;
}
void
DocAccessible::ProcessContentInserted(Accessible* aContainer,
const nsTArray<nsCOMPtr<nsIContent> >* aNodes)
{
// Process insertions if the container accessible is still in tree.
if (!aContainer->IsInDocument()) {
return;
}
// If new root content has been inserted then update it.
if (aContainer == this) {
UpdateRootElIfNeeded();
}
InsertIterator iter(aContainer, aNodes);
if (!iter.Next()) {
return;
}
#ifdef A11Y_LOG
logging::TreeInfo("children before insertion", logging::eVerbose,
aContainer);
#endif
uint32_t updateFlags = 0;
TreeMutation mt(aContainer);
do {
Accessible* parent = iter.Child()->Parent();
if (parent) {
if (parent != aContainer) {
#ifdef A11Y_LOG
logging::TreeInfo("stealing accessible", 0,
"old parent", parent, "new parent",
aContainer, "child", iter.Child(), nullptr);
#endif
MOZ_ASSERT_UNREACHABLE("stealing accessible");
continue;
}
#ifdef A11Y_LOG
logging::TreeInfo("binding to same parent", logging::eVerbose,
"parent", aContainer, "child", iter.Child(), nullptr);
#endif
continue;
}
if (aContainer->InsertAfter(iter.Child(), iter.ChildBefore())) {
#ifdef A11Y_LOG
logging::TreeInfo("accessible was inserted", 0,
"container", aContainer, "child", iter.Child(), nullptr);
#endif
mt.AfterInsertion(iter.Child());
updateFlags |= UpdateTreeInternal(iter.Child(), true);
continue;
}
MOZ_ASSERT_UNREACHABLE("accessible was rejected");
} while (iter.Next());
mt.Done();
#ifdef A11Y_LOG
logging::TreeInfo("children after insertion", logging::eVerbose,
aContainer);
#endif
FireEventsOnInsertion(aContainer, updateFlags);
}
void
DocAccessible::ProcessContentInserted(Accessible* aContainer, nsIContent* aNode)
{
if (!aContainer->IsInDocument()) {
return;
}
TreeWalker walker(aContainer);
if (aContainer->IsAcceptableChild(aNode) && walker.Seek(aNode)) {
Accessible* child = GetAccessible(aNode);
if (!child) {
child = GetAccService()->CreateAccessible(aNode, aContainer);
}
if (child) {
TreeMutation mt(aContainer);
aContainer->InsertAfter(child, walker.Prev());
mt.AfterInsertion(child);
mt.Done();
uint32_t flags = UpdateTreeInternal(child, true);
FireEventsOnInsertion(aContainer, flags);
}
}
}
void
DocAccessible::UpdateTreeOnInsertion(Accessible* aContainer)
DocAccessible::FireEventsOnInsertion(Accessible* aContainer,
uint32_t aUpdateFlags)
{
for (uint32_t idx = 0; idx < aContainer->ContentChildCount(); idx++) {
Accessible* child = aContainer->ContentChildAt(idx);
child->SetSurvivingInUpdate(true);
}
AutoTreeMutation mut(aContainer);
aContainer->InvalidateChildren();
aContainer->EnsureChildren();
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(aContainer);
uint32_t updateFlags = eNoAccessible;
for (uint32_t idx = 0; idx < aContainer->ContentChildCount(); idx++) {
Accessible* child = aContainer->ContentChildAt(idx);
if (child->IsSurvivingInUpdate()) {
child->SetSurvivingInUpdate(false);
continue;
}
// A new child has been created, update its tree.
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eTree)) {
logging::MsgBegin("TREE", "process content insertion");
logging::Node("container", aContainer->GetNode());
logging::Node("child", child->GetContent());
logging::Address("child", child);
logging::MsgEnd();
}
#endif
updateFlags |= UpdateTreeInternal(child, true, reorderEvent);
}
// Content insertion/removal is not cause of accessible tree change.
if (updateFlags == eNoAccessible)
// Content insertion did not cause an accessible tree change.
if (aUpdateFlags == eNoAccessible) {
return;
}
// Check to see if change occurred inside an alert, and fire an EVENT_ALERT
// if it did.
if (!(updateFlags & eAlertAccessible) &&
if (!(aUpdateFlags & eAlertAccessible) &&
(aContainer->IsAlert() || aContainer->IsInsideAlert())) {
Accessible* ancestor = aContainer;
do {
@@ -1769,7 +1856,6 @@ DocAccessible::UpdateTreeOnInsertion(Accessible* aContainer)
}
MaybeNotifyOfValueChange(aContainer);
FireDelayedEvent(reorderEvent);
}
void
@@ -1792,29 +1878,33 @@ DocAccessible::UpdateTreeOnRemoval(Accessible* aContainer, nsIContent* aChildNod
#endif
uint32_t updateFlags = eNoAccessible;
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(aContainer);
AutoTreeMutation mut(aContainer);
TreeMutation mt(aContainer);
if (child) {
updateFlags |= UpdateTreeInternal(child, false, reorderEvent);
} else {
mt.BeforeRemoval(child);
updateFlags |= UpdateTreeInternal(child, false);
}
else {
TreeWalker walker(aContainer, aChildNode, TreeWalker::eWalkCache);
while (Accessible* child = walker.Next()) {
updateFlags |= UpdateTreeInternal(child, false, reorderEvent);
Accessible* child = walker.Next();
if (child) {
do {
mt.BeforeRemoval(child);
updateFlags |= UpdateTreeInternal(child, false);
}
while ((child = walker.Next()));
}
}
mt.Done();
// Content insertion/removal is not cause of accessible tree change.
if (updateFlags == eNoAccessible)
return;
MaybeNotifyOfValueChange(aContainer);
FireDelayedEvent(reorderEvent);
if (updateFlags != eNoAccessible) {
MaybeNotifyOfValueChange(aContainer);
}
}
uint32_t
DocAccessible::UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
AccReorderEvent* aReorderEvent)
DocAccessible::UpdateTreeInternal(Accessible* aChild, bool aIsInsert)
{
uint32_t updateFlags = eAccessible;
@@ -1827,31 +1917,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) {
@@ -1886,11 +1953,11 @@ DocAccessible::UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
return updateFlags;
}
void
bool
DocAccessible::RelocateARIAOwnedIfNeeded(nsIContent* aElement)
{
if (!aElement->HasID())
return;
return false;
AttrRelProviderArray* list =
mDependentIDsHash.Get(nsDependentAtomString(aElement->GetID()));
@@ -1900,10 +1967,13 @@ DocAccessible::RelocateARIAOwnedIfNeeded(nsIContent* aElement)
Accessible* owner = GetAccessible(list->ElementAt(idx)->mContent);
if (owner) {
mNotificationController->ScheduleRelocation(owner);
return true;
}
}
}
}
return false;
}
void
@@ -1957,11 +2027,43 @@ DocAccessible::DoARIAOwnsRelocation(Accessible* aOwner)
MOZ_ASSERT(aOwner, "aOwner must be a valid pointer");
MOZ_ASSERT(aOwner->Elm(), "aOwner->Elm() must be a valid pointer");
IDRefsIterator iter(this, aOwner->Elm(), nsGkAtoms::aria_owns);
Accessible* child = nullptr;
#ifdef A11Y_LOG
logging::TreeInfo("aria owns relocation", logging::eVerbose, aOwner);
#endif
IDRefsIterator iter(this, aOwner->Elm(), nsGkAtoms::aria_owns);
uint32_t arrayIdx = 0, insertIdx = aOwner->ChildCount() - children->Length();
while ((child = iter.Next())) {
while (nsIContent* childEl = iter.NextElem()) {
Accessible* child = GetAccessible(childEl);
// Make an attempt to create an accessible if it wasn't created yet.
if (!child) {
if (aOwner->IsAcceptableChild(childEl)) {
child = GetAccService()->CreateAccessible(childEl, aOwner);
if (child) {
TreeMutation imut(aOwner);
aOwner->InsertChildAt(insertIdx, child);
imut.AfterInsertion(child);
imut.Done();
child->SetRelocated(true);
children->InsertElementAt(arrayIdx, child);
insertIdx = child->IndexInParent() + 1;
arrayIdx++;
uint32_t flags = UpdateTreeInternal(child, true);
FireEventsOnInsertion(aOwner, flags);
}
}
continue;
}
#ifdef A11Y_LOG
logging::TreeInfo("aria owns traversal", logging::eVerbose,
"candidate", child, nullptr);
#endif
// Same child on same position, no change.
if (child->Parent() == aOwner &&
child->IndexInParent() == static_cast<int32_t>(insertIdx)) {
@@ -1989,18 +2091,11 @@ DocAccessible::DoARIAOwnsRelocation(Accessible* aOwner)
}
}
if (child->Parent() == aOwner) {
if (child->IsRelocated()) {
children->RemoveElement(child);
}
MoveChild(child, insertIdx);
if (MoveChild(child, aOwner, insertIdx)) {
child->SetRelocated(true);
children->InsertElementAt(arrayIdx, child);
arrayIdx++;
insertIdx = child->IndexInParent() + 1;
} else if (SeizeChild(aOwner, child, insertIdx)) {
children->InsertElementAt(arrayIdx, child);
insertIdx++; arrayIdx++;
}
}
@@ -2011,106 +2106,6 @@ DocAccessible::DoARIAOwnsRelocation(Accessible* aOwner)
}
}
bool
DocAccessible::SeizeChild(Accessible* aNewParent, Accessible* aChild,
int32_t aIdxInParent)
{
Accessible* oldParent = aChild->Parent();
if (!oldParent) {
NS_ERROR("No parent? The tree is broken!");
return false;
}
int32_t oldIdxInParent = aChild->IndexInParent();
#ifdef A11Y_LOG
logging::TreeInfo("aria owns seize child", 0,
"old parent", oldParent, "new parent", aNewParent,
"child", aChild, nullptr);
#endif
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(oldParent);
RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(aChild, false);
reorderEvent->AddSubMutationEvent(hideEvent);
{
AutoTreeMutation mut(oldParent);
oldParent->RemoveChild(aChild);
}
bool isReinserted = false;
{
AutoTreeMutation mut(aNewParent);
isReinserted = aNewParent->InsertChildAt(aIdxInParent, aChild);
}
#ifdef A11Y_LOG
logging::TreeInfo("aria owns seize child: new parent tree after",
logging::eVerbose, aNewParent);
#endif
if (!isReinserted) {
AutoTreeMutation mut(oldParent);
oldParent->InsertChildAt(oldIdxInParent, aChild);
return false;
}
// The child may be stolen from other ARIA owns element.
if (aChild->IsRelocated()) {
nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.Get(oldParent);
children->RemoveElement(aChild);
}
FireDelayedEvent(hideEvent);
MaybeNotifyOfValueChange(oldParent);
FireDelayedEvent(reorderEvent);
reorderEvent = new AccReorderEvent(aNewParent);
RefPtr<AccMutationEvent> showEvent = new AccShowEvent(aChild);
reorderEvent->AddSubMutationEvent(showEvent);
FireDelayedEvent(showEvent);
MaybeNotifyOfValueChange(aNewParent);
FireDelayedEvent(reorderEvent);
aChild->SetRelocated(true);
return true;
}
void
DocAccessible::MoveChild(Accessible* aChild, int32_t aIdxInParent)
{
NS_PRECONDITION(aChild->Parent(), "No parent?");
Accessible* parent = aChild->Parent();
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(parent);
RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(aChild, false);
reorderEvent->AddSubMutationEvent(hideEvent);
#ifdef A11Y_LOG
logging::TreeInfo("aria owns move child", 0,
"parent", parent, "child", aChild, nullptr);
#endif
AutoTreeMutation mut(parent);
parent->MoveChild(aIdxInParent, aChild);
aChild->SetRelocated(true);
#ifdef A11Y_LOG
logging::TreeInfo("aria owns move child: parent tree after",
logging::eVerbose, parent);
#endif
FireDelayedEvent(hideEvent);
RefPtr<AccMutationEvent> showEvent = new AccShowEvent(aChild);
reorderEvent->AddSubMutationEvent(showEvent);
FireDelayedEvent(showEvent);
MaybeNotifyOfValueChange(parent);
FireDelayedEvent(reorderEvent);
}
void
DocAccessible::PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
uint32_t aStartIdx)
@@ -2118,47 +2113,108 @@ DocAccessible::PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
nsTArray<RefPtr<Accessible> > containers;
for (auto idx = aStartIdx; idx < aChildren->Length(); idx++) {
Accessible* child = aChildren->ElementAt(idx);
// If the child is in the tree then remove it from the owner.
if (child->IsInDocument()) {
Accessible* owner = child->Parent();
if (!owner) {
NS_ERROR("Cannot put the child back. No parent, a broken tree.");
continue;
}
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(owner);
RefPtr<AccMutationEvent> hideEvent = new AccHideEvent(child, false);
reorderEvent->AddSubMutationEvent(hideEvent);
FireDelayedEvent(hideEvent);
{
AutoTreeMutation mut(owner);
owner->RemoveChild(child);
child->SetRelocated(false);
}
MaybeNotifyOfValueChange(owner);
FireDelayedEvent(reorderEvent);
if (!child->IsInDocument()) {
continue;
}
Accessible* container = GetContainerAccessible(child->GetContent());
if (container &&
containers.IndexOf(container) == nsTArray<Accessible*>::NoIndex) {
containers.AppendElement(container);
// Remove the child from the owner
Accessible* owner = child->Parent();
if (!owner) {
NS_ERROR("Cannot put the child back. No parent, a broken tree.");
continue;
}
#ifdef A11Y_LOG
logging::TreeInfo("aria owns put child back", 0,
"old parent", owner, "child", child, nullptr);
#endif
// Unset relocated flag to find an insertion point for the child.
child->SetRelocated(false);
int32_t idxInParent = -1;
Accessible* origContainer = GetContainerAccessible(child->GetContent());
if (origContainer) {
TreeWalker walker(origContainer);
if (walker.Seek(child->GetContent())) {
Accessible* prevChild = walker.Prev();
idxInParent = prevChild ? prevChild->IndexInParent() + 1 : 0;
}
}
MoveChild(child, origContainer, idxInParent);
}
// And put it back where it belongs to.
aChildren->RemoveElementsAt(aStartIdx, aChildren->Length() - aStartIdx);
for (uint32_t idx = 0; idx < containers.Length(); idx++) {
NS_ASSERTION(containers[idx]->IsInDocument(),
"A container has been destroyed.");
if (containers[idx]->IsInDocument()) {
UpdateTreeOnInsertion(containers[idx]);
}
}
}
bool
DocAccessible::MoveChild(Accessible* aChild, Accessible* aNewParent,
int32_t aIdxInParent)
{
MOZ_ASSERT(aChild, "No child");
MOZ_ASSERT(aChild->Parent(), "No parent");
Accessible* curParent = aChild->Parent();
#ifdef A11Y_LOG
logging::TreeInfo("move child", 0,
"old parent", curParent, "new parent", aNewParent,
"child", aChild, nullptr);
#endif
// If the child was taken from from an ARIA owns element.
if (aChild->IsRelocated()) {
nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.Get(curParent);
children->RemoveElement(aChild);
}
if (curParent == aNewParent) {
MOZ_ASSERT(aChild->IndexInParent() != aIdxInParent, "No move case");
curParent->MoveChild(aIdxInParent, aChild);
MaybeNotifyOfValueChange(curParent);
#ifdef A11Y_LOG
logging::TreeInfo("move child: parent tree after",
logging::eVerbose, curParent);
#endif
return true;
}
if (!aNewParent->IsAcceptableChild(aChild->GetContent())) {
return false;
}
TreeMutation rmut(curParent);
rmut.BeforeRemoval(aChild, TreeMutation::kNoShutdown);
curParent->RemoveChild(aChild);
rmut.Done();
MaybeNotifyOfValueChange(curParent);
// No insertion point for the child.
if (aIdxInParent == -1) {
return true;
}
TreeMutation imut(aNewParent);
aNewParent->InsertChildAt(aIdxInParent, aChild);
imut.AfterInsertion(aChild);
imut.Done();
MaybeNotifyOfValueChange(aNewParent);
#ifdef A11Y_LOG
logging::TreeInfo("move child: old parent tree after",
logging::eVerbose, curParent);
logging::TreeInfo("move child: new parent tree after",
logging::eVerbose, aNewParent);
#endif
return true;
}
void
DocAccessible::CacheChildrenInSubtree(Accessible* aRoot,
Accessible** aFocusedAcc)
@@ -2169,18 +2225,25 @@ DocAccessible::CacheChildrenInSubtree(Accessible* aRoot,
FocusMgr()->HasDOMFocus(aRoot->GetContent()))
*aFocusedAcc = aRoot;
aRoot->EnsureChildren();
Accessible* root = aRoot->IsHTMLCombobox() ? aRoot->FirstChild() : aRoot;
if (root->KidsFromDOM()) {
#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()) {
MoveChild(child, root, root->ChildCount());
continue;
}
root->AppendChild(child);
mt.AfterInsertion(child);
// Make sure we create accessible tree defined in DOM only, i.e. if accessible
// provides specific tree (like XUL trees) then tree creation is handled by
// this accessible.
uint32_t count = aRoot->ContentChildCount();
for (uint32_t idx = 0; idx < count; idx++) {
Accessible* child = aRoot->ContentChildAt(idx);
NS_ASSERTION(child, "Illicit tree change while tree is created!");
// Don't cross document boundaries.
if (child && child->IsContent())
CacheChildrenInSubtree(child, aFocusedAcc);
}
mt.Done();
}
// Fire document load complete on ARIA documents.
@@ -2259,4 +2322,3 @@ DocAccessible::IsLoadEventTarget() const
// It's content (not chrome) root document.
return (treeItem->ItemType() == nsIDocShellTreeItem::typeContent);
}
+31 -27
View File
@@ -8,8 +8,8 @@
#include "nsIAccessiblePivot.h"
#include "AccEvent.h"
#include "HyperTextAccessibleWrap.h"
#include "AccEvent.h"
#include "nsClassHashtable.h"
#include "nsDataHashtable.h"
@@ -187,6 +187,7 @@ public:
*/
void FireDelayedEvent(AccEvent* aEvent);
void FireDelayedEvent(uint32_t aEventType, Accessible* aTarget);
void FireEventsOnInsertion(Accessible* aContainer, uint32_t aUpdateFlags);
/**
* Fire value change event on the given accessible if applicable.
@@ -225,7 +226,11 @@ public:
*
* @return the accessible object
*/
Accessible* GetAccessible(nsINode* aNode) const;
Accessible* GetAccessible(nsINode* aNode) const
{
return aNode == mDocumentNode ?
const_cast<DocAccessible*>(this) : mNodeToAccessibleMap.Get(aNode);
}
/**
* Return an accessible for the given node even if the node is not in
@@ -275,6 +280,12 @@ public:
return aNode ? GetAccessibleOrContainer(aNode->GetParentNode()) : nullptr;
}
/**
* Return an accessible for the given node if any, or an immediate accessible
* container for it.
*/
Accessible* AccessibleOrTrueContainer(nsINode* aNode) const;
/**
* Return an accessible for the given node or its first accessible descendant.
*/
@@ -353,6 +364,17 @@ public:
*/
void RecreateAccessible(nsIContent* aContent);
/**
* Schedule ARIA owned element relocation if needed. Return true if relocation
* was scheduled.
*/
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.
@@ -477,6 +499,8 @@ protected:
*/
void ProcessContentInserted(Accessible* aContainer,
const nsTArray<nsCOMPtr<nsIContent> >* aInsertedContent);
void ProcessContentInserted(Accessible* aContainer,
nsIContent* aInsertedContent);
/**
* Used to notify the document to make it process the invalidation list.
@@ -487,11 +511,6 @@ protected:
*/
void ProcessInvalidationList();
/**
* Update the tree on content insertion.
*/
void UpdateTreeOnInsertion(Accessible* aContainer);
/**
* Update the accessible tree for content removal.
*/
@@ -506,14 +525,7 @@ protected:
eAccessible = 1,
eAlertAccessible = 2
};
uint32_t UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
AccReorderEvent* aReorderEvent);
/**
* Schedule ARIA owned element relocation if needed.
*/
void RelocateARIAOwnedIfNeeded(nsIContent* aEl);
uint32_t UpdateTreeInternal(Accessible* aChild, bool aIsInsert);
/**
* Validates all aria-owns connections and updates the tree accordingly.
@@ -525,23 +537,15 @@ protected:
*/
void DoARIAOwnsRelocation(Accessible* aOwner);
/**
* Moves the child from old parent under new one.
*/
bool SeizeChild(Accessible* aNewParent, Accessible* aChild,
int32_t aIdxInParent);
/**
* Move the child under same parent.
*/
void MoveChild(Accessible* aChild, int32_t aIdxInParent);
/**
* Moves children back under their original parents.
*/
void PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
uint32_t aStartIdx);
bool MoveChild(Accessible* aChild, Accessible* aNewParent,
int32_t aIdxInParent);
/**
* Create accessible tree.
*
@@ -700,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:
+2 -3
View File
@@ -1880,11 +1880,10 @@ HyperTextAccessible::NativeName(nsString& aName)
}
void
HyperTextAccessible::InvalidateChildren()
HyperTextAccessible::Shutdown()
{
mOffsets.Clear();
AccessibleWrap::InvalidateChildren();
AccessibleWrap::Shutdown();
}
bool
+1 -1
View File
@@ -60,7 +60,7 @@ public:
virtual mozilla::a11y::role NativeRole() override;
virtual uint64_t NativeState() override;
virtual void InvalidateChildren() override;
virtual void Shutdown() override;
virtual bool RemoveChild(Accessible* aAccessible) override;
virtual Relation RelationByType(RelationType aType) override;
-15
View File
@@ -110,21 +110,6 @@ OuterDocAccessible::Shutdown()
AccessibleWrap::Shutdown();
}
void
OuterDocAccessible::InvalidateChildren()
{
// Do not invalidate children because DocManager is responsible for
// document accessible lifetime when DOM document is created or destroyed. If
// DOM document isn't destroyed but its presshell is destroyed (for example,
// when DOM node of outerdoc accessible is hidden), then outerdoc accessible
// notifies DocManager about this. If presshell is created for existing
// DOM document (for example when DOM node of outerdoc accessible is shown)
// then allow DocManager to handle this case since the document
// accessible is created and appended as a child when it's requested.
SetChildrenFlag(eChildrenUninitialized);
}
bool
OuterDocAccessible::InsertChildAt(uint32_t aIdx, Accessible* aAccessible)
{
-1
View File
@@ -36,7 +36,6 @@ public:
virtual Accessible* ChildAtPoint(int32_t aX, int32_t aY,
EWhichChildAtPoint aWhichChild) override;
virtual void InvalidateChildren() override;
virtual bool InsertChildAt(uint32_t aIdx, Accessible* aChild) override;
virtual bool RemoveChild(Accessible* aAccessible) override;
+1 -6
View File
@@ -20,6 +20,7 @@ TextLeafAccessible::
LinkableAccessible(aContent, aDoc)
{
mType = eTextLeafType;
mStateFlags |= eNoKidsFromDOM;
}
TextLeafAccessible::~TextLeafAccessible()
@@ -50,9 +51,3 @@ TextLeafAccessible::Name(nsString& aName)
aName = mText;
return eNameOK;
}
void
TextLeafAccessible::CacheChildren()
{
// No children for text accessible.
}
-4
View File
@@ -30,10 +30,6 @@ public:
void SetText(const nsAString& aText) { mText = aText; }
const nsString& Text() const { return mText; }
protected:
// Accessible
virtual void CacheChildren() override;
protected:
nsString mText;
};
+4 -23
View File
@@ -87,9 +87,7 @@ HTMLImageMapAccessible::UpdateChildAreas(bool aDoFireEvents)
if (!imageMapObj)
return;
bool treeChanged = false;
AutoTreeMutation mut(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,14 +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.
@@ -121,22 +113,11 @@ HTMLImageMapAccessible::UpdateChildAreas(bool aDoFireEvents)
break;
}
if (aDoFireEvents) {
RefPtr<AccShowEvent> event = new AccShowEvent(area);
mDoc->FireDelayedEvent(event);
reorderEvent->AddSubMutationEvent(event);
}
treeChanged = true;
mt.AfterInsertion(area);
}
}
// Fire reorder event if needed.
if (treeChanged && aDoFireEvents)
mDoc->FireDelayedEvent(reorderEvent);
if (!treeChanged)
mut.mInvalidationRequired = false;
mt.Done();
}
Accessible*
+8 -29
View File
@@ -51,6 +51,7 @@ HTMLLIAccessible::
if (blockFrame && blockFrame->HasBullet()) {
mBullet = new HTMLListBulletAccessible(mContent, mDoc);
Document()->BindToDocument(mBullet, nullptr);
AppendChild(mBullet);
}
}
@@ -102,41 +103,19 @@ HTMLLIAccessible::UpdateBullet(bool aHasBullet)
return;
}
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(this);
AutoTreeMutation mut(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);
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);
mt.AfterInsertion(mBullet);
}
else {
mt.BeforeRemoval(mBullet);
RemoveChild(mBullet);
mBullet = nullptr;
}
mDoc->FireDelayedEvent(reorderEvent);
}
////////////////////////////////////////////////////////////////////////////////
// HTMLLIAccessible: Accessible protected
void
HTMLLIAccessible::CacheChildren()
{
if (mBullet)
AppendChild(mBullet);
// Cache children from subtree.
AccessibleWrap::CacheChildren();
mt.Done();
}
////////////////////////////////////////////////////////////////////////////////
-3
View File
@@ -60,9 +60,6 @@ public:
protected:
virtual ~HTMLLIAccessible() { }
// Accessible
virtual void CacheChildren() override;
private:
RefPtr<HTMLListBulletAccessible> mBullet;
};
+8 -41
View File
@@ -114,35 +114,12 @@ HTMLSelectListAccessible::SetCurrentItem(Accessible* aItem)
true);
}
////////////////////////////////////////////////////////////////////////////////
// HTMLSelectListAccessible: Accessible protected
void
HTMLSelectListAccessible::CacheChildren()
bool
HTMLSelectListAccessible::IsAcceptableChild(nsIContent* aEl) const
{
// Cache accessibles for <optgroup> and <option> DOM decendents as children,
// as well as the accessibles for them. Avoid whitespace text nodes. We want
// to count all the <optgroup>s and <option>s as children because we want
// a flat tree under the Select List.
for (nsIContent* childContent = mContent->GetFirstChild(); childContent;
childContent = childContent->GetNextSibling()) {
if (!childContent->IsHTMLElement()) {
continue;
}
if (childContent->IsAnyOfHTMLElements(nsGkAtoms::option,
nsGkAtoms::optgroup)) {
// Get an accessible for option or optgroup and cache it.
RefPtr<Accessible> accessible =
GetAccService()->GetOrCreateAccessible(childContent, this);
if (accessible)
AppendChild(accessible);
}
}
return aEl->IsAnyOfHTMLElements(nsGkAtoms::option, nsGkAtoms::optgroup);
}
////////////////////////////////////////////////////////////////////////////////
// HTMLSelectOptionAccessible
////////////////////////////////////////////////////////////////////////////////
@@ -360,6 +337,7 @@ HTMLComboboxAccessible::
{
mType = eHTMLComboboxType;
mGenericTypes |= eCombobox;
mStateFlags |= eNoKidsFromDOM;
nsIComboboxControlFrame* comboFrame = do_QueryFrame(GetFrame());
if (comboFrame) {
@@ -381,15 +359,6 @@ HTMLComboboxAccessible::NativeRole()
return roles::COMBOBOX;
}
void
HTMLComboboxAccessible::InvalidateChildren()
{
AccessibleWrap::InvalidateChildren();
if (mListAccessible)
mListAccessible->InvalidateChildren();
}
bool
HTMLComboboxAccessible::RemoveChild(Accessible* aChild)
{
@@ -401,16 +370,14 @@ HTMLComboboxAccessible::RemoveChild(Accessible* aChild)
return false;
}
void
HTMLComboboxAccessible::CacheChildren()
{
}
void
HTMLComboboxAccessible::Shutdown()
{
MOZ_ASSERT(mDoc->IsDefunct() || !mListAccessible);
mListAccessible = nullptr;
if (mListAccessible) {
mListAccessible->Shutdown();
mListAccessible = nullptr;
}
AccessibleWrap::Shutdown();
}
+1 -8
View File
@@ -51,10 +51,7 @@ public:
virtual Accessible* CurrentItem() override;
virtual void SetCurrentItem(Accessible* aItem) override;
protected:
// Accessible
virtual void CacheChildren() override;
virtual bool IsAcceptableChild(nsIContent* aEl) const override;
};
/*
@@ -171,7 +168,6 @@ public:
virtual void Value(nsString& aValue) override;
virtual a11y::role NativeRole() override;
virtual uint64_t NativeState() override;
virtual void InvalidateChildren() override;
virtual bool RemoveChild(Accessible* aChild) override;
// ActionAccessible
@@ -187,9 +183,6 @@ public:
virtual void SetCurrentItem(Accessible* aItem) override;
protected:
// Accessible
virtual void CacheChildren() override;
/**
* Return selected option.
*/
+4 -14
View File
@@ -393,24 +393,14 @@ NS_IMPL_ISUPPORTS_INHERITED0(HTMLTableAccessible, Accessible)
////////////////////////////////////////////////////////////////////////////////
// HTMLTableAccessible: Accessible
void
HTMLTableAccessible::CacheChildren()
bool
HTMLTableAccessible::InsertChildAt(uint32_t aIndex, Accessible* aChild)
{
// Move caption accessible so that it's the first child. Check for the first
// caption only, because nsAccessibilityService ensures we don't create
// accessibles for the other captions, since only the first is actually
// visible.
TreeWalker walker(this, mContent);
Accessible* child = nullptr;
while ((child = walker.Next())) {
if (child->Role() == roles::CAPTION) {
InsertChildAt(0, child);
while ((child = walker.Next()) && AppendChild(child));
break;
}
AppendChild(child);
}
return Accessible::InsertChildAt(aChild->IsHTMLCaption() ? 0 : aIndex, aChild);
}
role
@@ -922,7 +912,7 @@ HTMLTableAccessible::HasDescendant(const nsAString& aTagName, bool aAllowEmpty)
// performance problems only. Note, currently 'aAllowEmpty' flag is used for
// caption element only. On another hand we create accessible object for
// the first entry of caption element (see
// HTMLTableAccessible::CacheChildren).
// HTMLTableAccessible::InsertChildAt).
return !!elements->Item(1);
}
+3 -2
View File
@@ -157,12 +157,13 @@ public:
virtual already_AddRefed<nsIPersistentProperties> NativeAttributes() override;
virtual Relation RelationByType(RelationType aRelationType) override;
bool InsertChildAt(uint32_t aIndex, Accessible* aChild) override;
protected:
virtual ~HTMLTableAccessible() {}
// Accessible
virtual ENameValueFlag NativeName(nsString& aName) override;
virtual void CacheChildren() override;
// HTMLTableAccessible
@@ -209,7 +210,7 @@ class HTMLCaptionAccessible : public HyperTextAccessibleWrap
{
public:
HTMLCaptionAccessible(nsIContent* aContent, DocAccessible* aDoc) :
HyperTextAccessibleWrap(aContent, aDoc) { }
HyperTextAccessibleWrap(aContent, aDoc) { mType = eHTMLCaptionType; }
// Accessible
virtual a11y::role NativeRole() override;
-1
View File
@@ -46,7 +46,6 @@ public: // construction, destruction
virtual Class GetNativeType ();
virtual void Shutdown () override;
virtual void InvalidateChildren() override;
virtual bool InsertChildAt(uint32_t aIdx, Accessible* aChild) override;
virtual bool RemoveChild(Accessible* aAccessible) override;
-12
View File
@@ -135,18 +135,6 @@ AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
}
void
AccessibleWrap::InvalidateChildren()
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
[GetNativeObject() invalidateChildren];
Accessible::InvalidateChildren();
NS_OBJC_END_TRY_ABORT_BLOCK;
}
bool
AccessibleWrap::InsertChildAt(uint32_t aIdx, Accessible* aAccessible)
{
+2 -2
View File
@@ -678,11 +678,11 @@ ConvertToNSArray(nsTArray<ProxyAccessible*>& aArray)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
AccessibleWrap* accWrap = [self getGeckoAccessible];
if (mChildren || (accWrap && !accWrap->AreChildrenCached()))
if (mChildren)
return mChildren;
// get the array of children.
AccessibleWrap* accWrap = [self getGeckoAccessible];
if (accWrap) {
AutoTArray<Accessible*, 10> childrenArray;
accWrap->GetUnignoredChildren(&childrenArray);
@@ -21,6 +21,9 @@
<script type="application/javascript">
<![CDATA[
//gA11yEventDumpToConsole = true;
//enableLogging("tree,verbose"); // debug
if (navigator.platform.startsWith("Mac")) {
SimpleTest.expectAssertions(0, 1);
} else {
@@ -54,8 +54,8 @@
}
}
//gA11yEventDumpID = "eventdump"; // debug stuff
gA11yEventDumpToConsole = true;
//gA11yEventDumpToConsole = true;
//enableLogging("tree,eventTree,verbose");
function doTest()
{
@@ -143,7 +143,5 @@
<a id="link4" onmouseup="">
<img src="../moz.png" id="img4">
</a>
<div id="eventdump"></div>
</body>
</html>
@@ -23,6 +23,7 @@
<script type="application/javascript">
//gA11yEventDumpToConsole = true;
//enableLogging("tree,verbose");
function doPreTest()
{
var tabDocument = currentTabDocument();
@@ -56,7 +56,7 @@ function editableTextTest(aID)
/**
* setTextContents test.
*/
this.setTextContents = function setTextContents(aValue)
this.setTextContents = function setTextContents(aValue, aSkipStartOffset)
{
var testID = "setTextContents '" + aValue + "' for " + prettyName(aID);
@@ -67,9 +67,12 @@ function editableTextTest(aID)
acc.setTextContents(aValue);
}
var insertTripple = aValue ? [0, aValue.length, aValue] : null;
aSkipStartOffset = aSkipStartOffset || 0;
var insertTripple = aValue ?
[ aSkipStartOffset, aSkipStartOffset + aValue.length, aValue ] : null;
var oldValue = getValue(aID);
var removeTripple = oldValue ? [0, oldValue.length, oldValue] : null;
var removeTripple = oldValue ?
[ aSkipStartOffset, aSkipStartOffset + oldValue.length, oldValue ] : null;
this.generateTest(aID, removeTripple, insertTripple, setTextContentsInvoke,
getValueChecker(aID, aValue), testID);
@@ -293,6 +296,8 @@ function editableTextTest(aID)
invoke: aInvokeFunc,
finalCheck: function finalCheck()
{
//dumpTree(aID, `'${aID}' tree:`);
aChecker.check();
et.unwrapNextTest(); // replace dummy invoker on real invoker object.
},
@@ -19,23 +19,27 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=452161
src="editabletext.js"></script>
<script type="application/javascript">
//gA11yEventDumpToConsole = true;
//enableLogging("tree,verbose"); // debug
function addTestEditable(aID, aTestRun)
function addTestEditable(aID, aTestRun, aBeforeContent, aAfterContent)
{
var et = new editableTextTest(aID);
var startOffset = aBeforeContent ? aBeforeContent.length : 0;
// XXX afterContent currently is not used
//////////////////////////////////////////////////////////////////////////
// setTextContents
et.scheduleTest(et.setTextContents, "hello");
et.scheduleTest(et.setTextContents, "olleh");
et.scheduleTest(et.setTextContents, "");
et.scheduleTest(et.setTextContents, "hello", startOffset);
et.scheduleTest(et.setTextContents, "olleh", startOffset);
et.scheduleTest(et.setTextContents, "", startOffset);
//////////////////////////////////////////////////////////////////////////
// insertText
et.scheduleTest(et.insertText, "hello", 0, "hello");
et.scheduleTest(et.insertText, "ma ", 0, "ma hello");
et.scheduleTest(et.insertText, "ma", 2, "mama hello");
et.scheduleTest(et.insertText, " hello", 10, "mama hello hello");
et.scheduleTest(et.insertText, "hello", startOffset, "hello");
et.scheduleTest(et.insertText, "ma ", startOffset, "ma hello");
et.scheduleTest(et.insertText, "ma", startOffset + 2, "mama hello");
et.scheduleTest(et.insertText, " hello", startOffset + 10, "mama hello hello");
// XXX: bug 452584
@@ -71,7 +75,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=452161
addTestEditable("input", testRun);
addTestEditable("div", testRun);
addTestEditable(getNode("frame").contentDocument, testRun, '\n');
addTestEditable("divb", testRun, "pseudo element", "");
addTestEditable("diva", testRun, "", "pseudo element");
addTestEditable("divba", testRun, "before", "after");
addTestEditable(getNode("frame").contentDocument, testRun);
testRun.run(); // Will call SimpleTest.finish();
}
@@ -90,16 +97,35 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=452161
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
<style>
#divb::before,
#diva::after {
content: "pseudo element";
}
#divba::before {
content: "before";
}
#divba::after {
content: "after";
}
</style>
</head>
<body>
<a target="_blank"
title="nsIAccessibleEditableText chrome tests"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=452161">Mozilla Bug 452161</a>
href="https://bugzilla.mozilla.org/show_bug.cgi?id=452161">
Bug 452161
</a>
<a target="_blank"
title="Cache rendered text on a11y side"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=626660">
Mozilla Bug 626660
Bug 626660
</a>
<a target="_blank"
title="Pseudo element support test"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1105611">
Bug 1105611
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
@@ -108,7 +134,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=452161
<input id="input"/>
<div id="div" contentEditable="true"></div>
<div id="div" contenteditable="true"></div>
<div id="divb" contenteditable="true"></div>
<div id="diva" contenteditable="true"></div>
<div id="divba" contenteditable="true"></div>
<iframe id="frame"/>
</body>
+28 -4
View File
@@ -345,10 +345,17 @@ function eventQueue(aEventType)
var msg = "Test with ID = '" + this.getEventID(checker) +
"' succeed. ";
if (checker.unexpected)
ok(true, msg + "There's no unexpected " + typeStr + " event.");
else
if (checker.unexpected) {
if (checker.todo) {
todo(false, "Event " + typeStr + " event is still missing");
}
else {
ok(true, msg + "There's no unexpected " + typeStr + " event.");
}
}
else {
ok(true, msg + "Event " + typeStr + " was handled.");
}
}
}
}
@@ -371,8 +378,13 @@ function eventQueue(aEventType)
ok(false, msg + "Dupe " + typeStr + " event.");
if (checker.unexpected) {
if (checker.wasCaught)
if (checker.todo) {
todo(checker.wasCaught,
"Event " + typeStr + " event is still missing");
}
else if (checker.wasCaught) {
ok(false, msg + "There's unexpected " + typeStr + " event.");
}
} else if (!checker.wasCaught) {
ok(false, msg + typeStr + " event was missed.");
}
@@ -1667,6 +1679,18 @@ function invokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg, aIsAsync)
this.mTargetFuncArg = aTargetFuncArg;
}
/**
* Generic invoker checker for todo events.
*/
function todo_invokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg)
{
this.__proto__ = new invokerChecker(aEventType, aTargetOrFunc,
aTargetFuncArg, true);
this.unexpected = true;
this.todo = true;
}
/**
* Generic invoker checker for unexpected events.
*/
@@ -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,12 +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("events,tree,eventTree,verbose");
var gQueue = null;
function doTests()
{
gQueue = new eventQueue();
@@ -349,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();
}
@@ -411,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>
@@ -358,6 +358,25 @@
}
}
function showHiddenParentOfVisibleChild()
{
this.eventSeq = [
new invokerChecker(EVENT_SHOW, getNode("c4_middle")),
new invokerChecker(EVENT_HIDE, getNode("c4_child")),
new invokerChecker(EVENT_REORDER, getNode("c4"))
];
this.invoke = function showHiddenParentOfVisibleChild_invoke()
{
getNode("c4_middle").style.visibility = 'visible';
}
this.getID = function showHiddenParentOfVisibleChild_getID()
{
return "show hidden parent of visible child";
}
}
/**
* Target getters.
*/
@@ -395,11 +414,13 @@
return aNode.parentNode;
}
//gA11yEventDumpToConsole = true; // debug stuff
//enableLogging("events,verbose");
/**
* Do tests.
*/
var gQueue = null;
//gA11yEventDumpToConsole = true; // debug stuff
function doTests()
{
@@ -484,6 +505,7 @@
gQueue.push(new test2("testContainer", "testNestedContainer"));
gQueue.push(new test3("testContainer"));
gQueue.push(new insertReferredElm("testContainer3"));
gQueue.push(new showHiddenParentOfVisibleChild());
gQueue.invoke(); // Will call SimpleTest.finish();
}
@@ -544,5 +566,10 @@
</div>
<div id="testContainer2"></div>
<div id="testContainer3"></div>
<div id="c4">
<div style="visibility:hidden" id="c4_middle">
<div style="visibility:visible" id="c4_child"></div>
</div>
</body>
</html>
@@ -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>
@@ -20,8 +20,6 @@
src="../value.js"></script>
<script type="application/javascript">
/**
* Do tests.
*/
@@ -132,6 +130,30 @@
}
}
function changeSelectValue(aID, aKey, aValue)
{
this.eventSeq =
[ new invokerChecker(EVENT_TEXT_VALUE_CHANGE, getAccessible(aID)) ];
this.invoke = function changeSelectValue_invoke()
{
getNode(aID).focus();
synthesizeKey(aKey, {}, window);
}
this.finalCheck = function changeSelectValue_finalCheck()
{
is(getAccessible(aID).value, aValue, "Wrong value for " + prettyName(aID));
}
this.getID = function changeSelectValue_getID()
{
return `${prettyName(aID)} closed select value change on '${aKey}'' key press`;
}
}
//enableLogging("DOMEvents");
//gA11yEventDumpToConsole = true;
function doTests()
{
// Test initial values
@@ -155,6 +177,9 @@
gQueue.push(new changeProgressValue("progress", "50"));
gQueue.push(new changeRangeValue("range"));
gQueue.push(new changeSelectValue("select", "VK_DOWN", "2nd"));
gQueue.push(new changeSelectValue("select", "3", "3rd"));
gQueue.invoke(); // Will call SimpleTest.finish();
}
@@ -221,5 +246,10 @@
<!-- input@type="range" -->
<input type="range" id="range" min="0" max="10" value="6">
<select id="select">
<option>1st</option>
<option>2nd</option>
<option>3rd</option>
</select>
</body>
</html>
@@ -28,8 +28,8 @@
////////////////////////////////////////////////////////////////////////////
// Tests
//gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpToConsole = true; // debug
//enableLogging("tree,verbose");
var gQueue = null;
function doTests()
@@ -55,7 +55,7 @@
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=723833"
title="IAccessibleText::setCaretOffset on location or search bar causes focus to jump">
Mozilla Bug 723833
Bug 723833
</a>
<p id="display"></p>
<div id="content" style="display: none">
@@ -63,7 +63,5 @@
<pre id="test">
</pre>
</body>
<vbox id="eventdump"></vbox>
</vbox>
</window>
@@ -20,7 +20,7 @@
// Tests
////////////////////////////////////////////////////////////////////////////
//enableLogging("tree"); // debug stuff
//enableLogging("tree,verbose"); // debug stuff
var gQueue = null;
@@ -154,8 +154,8 @@
<div id="t4_1"><div aria-owns="t4_1" role="group"></div></div>
<!-- natural and aria-owns hierarchy -->
<div id="t5_1"><div aria-owns="t5_2" role="group"></div></div>
<div id="t5_2" role="note"><div aria-owns="t5_3" role="heading"></div></div>
<div id="t5_1"><div aria-owns="t5_2" role="group"></div></div>
<div id="t5_3" role="form"><div aria-owns="t5_1" role="tooltip"></div></div>
<!-- rearrange children -->
@@ -16,6 +16,8 @@
src="../states.js"></script>
<script type="application/javascript">
//gA11yEventDumpToConsole = true;
//enableLogging("tree,verbose");
function doTest()
{
var tree =
@@ -201,6 +201,9 @@
////////////////////////////////////////////////////////////////////////////
// Test
//gA11yEventDumpToConsole = true;
//enableLogging("tree,verbose,stack");
var gQueue = null;
function doTest()
{
@@ -18,6 +18,9 @@
<script type="application/javascript">
<![CDATA[
//gA11yEventDumpToConsole = true;
//enableLogging("tree,verbose"); // debug stuff
////////////////////////////////////////////////////////////////////////////
// Test
@@ -160,6 +163,7 @@
SimpleTest.ok(true, "Testing (New) Toolkit autocomplete widget.");
// Dumb access to trigger popup lazy creation.
dump("Trigget popup lazy creation");
waitForEvent(EVENT_REORDER, txc, test_AutocompleteControl);
txc.popup;
@@ -33,6 +33,7 @@ skip-if = buildapp == "mulet"
[test_recreation.html]
[test_select.html]
[test_shutdown.xul]
[test_table.html]
[test_textleaf.html]
[test_visibility.html]
[test_whitespace.html]
@@ -72,6 +72,7 @@
function doTest()
{
//enableLogging("tree");
gQueue = new eventQueue();
// make the accessible an inaccessible
@@ -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_HIDE, getNode("t1_button")),
new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")),
new invokerChecker(EVENT_SHOW, getNode("t1_button")),
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_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)
];
@@ -194,8 +194,8 @@
// newly inserted child.
var tree =
{ SECTION: [
{ CHECKBUTTON: [ ] },
{ RADIOBUTTON: [ ] },
{ CHECKBUTTON: [ ] }, // existing explicit, t1_checkbox
{ RADIOBUTTON: [ ] }, // new explicit, t1_child3
{ PUSHBUTTON: [ ] }, // ARIA owned, t1_button
{ SECTION: [ ] }, // ARIA owned, t1_subdiv
{ GROUPING: [ ] } // ARIA owned, t1_group
@@ -228,8 +228,8 @@
// subdiv should go away
var tree =
{ SECTION: [
{ CHECKBUTTON: [ ] },
{ RADIOBUTTON: [ ] },
{ CHECKBUTTON: [ ] }, // explicit, t1_checkbox
{ RADIOBUTTON: [ ] }, // explicit, t1_child3
{ PUSHBUTTON: [ ] }, // ARIA owned, t1_button
{ GROUPING: [ ] } // ARIA owned, t1_group
] };
@@ -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()
{
@@ -117,8 +117,8 @@
}
}
//gA11yEventDumpID = "eventdump";
//gA11yEventDumpToConsole = true;
//enableLogging("tree,verbose");
var gQueue = null;
var gContextTree = {};
@@ -312,8 +312,6 @@
</menupopup>
<button context="context" id="button">btn</button>
<vbox id="eventdump" role="log"/>
</vbox>
</hbox>
</window>
@@ -102,7 +102,7 @@
}
}
// gA11yEventDumpToConsole = true;
//gA11yEventDumpToConsole = true;
function doTest()
{
@@ -0,0 +1,81 @@
<!DOCTYPE html>
<html>
<head>
<title>Table update 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="../role.js"></script>
<script type="application/javascript"
src="../events.js"></script>
<script type="application/javascript">
function appendCaption(aTableID)
{
this.invoke = function appendCaption_invoke()
{
// append a caption, it should appear as a first element in the
// accessible tree.
var caption = document.createElement("caption");
caption.textContent = "table caption";
getNode(aTableID).appendChild(caption);
}
this.eventSeq = [
new invokerChecker(EVENT_REORDER, aTableID)
];
this.finalCheck = function appendCaption_finalCheck()
{
var tree =
{ TABLE: [
{ CAPTION: [
{ TEXT_LEAF: [] }
] },
{ ROW: [
{ CELL: [ {TEXT_LEAF: [] }]},
{ CELL: [ {TEXT_LEAF: [] }]}
] }
] };
testAccessibleTree(aTableID, tree);
}
this.getID = function appendCaption_getID()
{
return "append caption";
}
}
function doTest()
{
gQueue = new eventQueue();
gQueue.push(new appendCaption("table"));
gQueue.invoke(); // Will call SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<table id="table">
<tr>
<td>cell1</td>
<td>cell2</td>
</tr>
</table>
</body>
</html>
@@ -19,6 +19,8 @@ HTMLWin32ObjectOwnerAccessible::
DocAccessible* aDoc, void* aHwnd) :
AccessibleWrap(aContent, aDoc), mHwnd(aHwnd)
{
mStateFlags |= eNoKidsFromDOM;
// Our only child is a HTMLWin32ObjectAccessible object.
if (mHwnd) {
mNativeAccessible = new HTMLWin32ObjectAccessible(mHwnd, aDoc);
@@ -50,15 +52,6 @@ HTMLWin32ObjectOwnerAccessible::NativelyUnavailable() const
return !mHwnd;
}
////////////////////////////////////////////////////////////////////////////////
// HTMLWin32ObjectOwnerAccessible: Accessible protected implementation
void
HTMLWin32ObjectOwnerAccessible::CacheChildren()
{
}
////////////////////////////////////////////////////////////////////////////////
// HTMLWin32ObjectAccessible
////////////////////////////////////////////////////////////////////////////////
@@ -32,10 +32,6 @@ public:
virtual bool NativelyUnavailable() const;
protected:
// Accessible
virtual void CacheChildren();
void* mHwnd;
RefPtr<Accessible> mNativeAccessible;
};
+3 -2
View File
@@ -24,10 +24,11 @@ xpcAccessible::GetParent(nsIAccessible** aParent)
{
NS_ENSURE_ARG_POINTER(aParent);
*aParent = nullptr;
if (!Intl())
if (IntlGeneric().IsNull())
return NS_ERROR_FAILURE;
NS_IF_ADDREF(*aParent = ToXPC(Intl()->Parent()));
AccessibleOrProxy parent = IntlGeneric().Parent();
NS_IF_ADDREF(*aParent = ToXPC(parent));
return NS_OK;
}
+1 -12
View File
@@ -50,6 +50,7 @@ XULLabelAccessible::
nsAutoString text;
textBoxFrame->GetCroppedTitle(text);
mValueTextLeaf->SetText(text);
AppendChild(mValueTextLeaf);
}
}
@@ -120,18 +121,6 @@ XULLabelAccessible::UpdateLabelValue(const nsString& aValue)
TextUpdater::Run(mDoc, mValueTextLeaf, aValue);
}
void
XULLabelAccessible::CacheChildren()
{
if (mValueTextLeaf) {
AppendChild(mValueTextLeaf);
return;
}
// Cache children from subtree.
AccessibleWrap::CacheChildren();
}
////////////////////////////////////////////////////////////////////////////////
// XULLabelTextLeafAccessible
-1
View File
@@ -33,7 +33,6 @@ public:
protected:
// Accessible
virtual ENameValueFlag NativeName(nsString& aName) override;
virtual void CacheChildren() override;
private:
RefPtr<XULLabelTextLeafAccessible> mValueTextLeaf;
+1 -8
View File
@@ -1070,6 +1070,7 @@ XULTreeItemAccessible::
nsITreeView* aTreeView, int32_t aRow) :
XULTreeItemAccessibleBase(aContent, aDoc, aParent, aTree, aTreeView, aRow)
{
mStateFlags |= eNoKidsFromDOM;
mColumn = nsCoreUtils::GetFirstSensibleColumn(mTree);
GetCellName(mColumn, mCachedName);
}
@@ -1143,14 +1144,6 @@ XULTreeItemAccessible::RowInvalidated(int32_t aStartColIdx, int32_t aEndColIdx)
}
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeItemAccessible: Accessible protected implementation
void
XULTreeItemAccessible::CacheChildren()
{
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeColumAccessible
-3
View File
@@ -241,9 +241,6 @@ public:
protected:
virtual ~XULTreeItemAccessible();
// Accessible
virtual void CacheChildren() override;
// XULTreeItemAccessible
nsCOMPtr<nsITreeColumn> mColumn;
nsString mCachedName;
+1 -7
View File
@@ -249,6 +249,7 @@ XULTreeGridRowAccessible::
mAccessibleCache(kDefaultTreeCacheLength)
{
mGenericTypes |= eTableRow;
mStateFlags |= eNoKidsFromDOM;
}
XULTreeGridRowAccessible::~XULTreeGridRowAccessible()
@@ -409,13 +410,6 @@ XULTreeGridRowAccessible::RowInvalidated(int32_t aStartColIdx,
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeGridRowAccessible: Accessible protected implementation
void
XULTreeGridRowAccessible::CacheChildren()
{
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeGridCellAccessible
-3
View File
@@ -97,9 +97,6 @@ public:
protected:
virtual ~XULTreeGridRowAccessible();
// Accessible
virtual void CacheChildren() override;
// XULTreeItemAccessibleBase
mutable nsRefPtrHashtable<nsPtrHashKey<const void>, XULTreeGridCellAccessible>
mAccessibleCache;
+4 -2
View File
@@ -419,9 +419,11 @@ union LZ4_streamDecode_u {
# define LZ4_DEPRECATED(message) /* disable deprecation warnings */
#else
# define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
# if defined(__clang__) /* clang doesn't handle mixed C++11 and CNU attributes */
# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
# elif defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
# define LZ4_DEPRECATED(message) [[deprecated(message)]]
# elif (LZ4_GCC_VERSION >= 405) || defined(__clang__)
# elif (LZ4_GCC_VERSION >= 405)
# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
# elif (LZ4_GCC_VERSION >= 301)
# define LZ4_DEPRECATED(message) __attribute__((deprecated))
+5 -3
View File
@@ -29,10 +29,12 @@ nsDataChannel::OpenContentStream(bool async, nsIInputStream **result,
rv = URI()->GetAsciiSpec(spec);
if (NS_FAILED(rv)) return rv;
nsCString contentType, contentCharset, dataBuffer, hashRef;
nsCString contentType, contentCharset, dataBuffer;
bool lBase64;
rv = nsDataHandler::ParseURI(spec, contentType, contentCharset,
lBase64, dataBuffer, hashRef);
rv = nsDataHandler::ParseURI(spec, contentType, &contentCharset,
lBase64, &dataBuffer);
if (NS_FAILED(rv))
return rv;
NS_UnescapeURL(dataBuffer);
+27 -21
View File
@@ -77,9 +77,10 @@ nsDataHandler::NewURI(const nsACString &aSpec,
rv = uri->SetRef(spec);
} else {
// Otherwise, we'll assume |spec| is a fully-specified data URI
nsAutoCString contentType, contentCharset, dataBuffer, hashRef;
nsAutoCString contentType;
bool base64;
rv = ParseURI(spec, contentType, contentCharset, base64, dataBuffer, hashRef);
rv = ParseURI(spec, contentType, /* contentCharset = */ nullptr,
base64, /* dataBuffer = */ nullptr);
if (NS_FAILED(rv))
return rv;
@@ -153,10 +154,10 @@ nsDataHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) {
nsresult
nsDataHandler::ParseURI(nsCString& spec,
nsCString& contentType,
nsCString& contentCharset,
nsCString* contentCharset,
bool& isBase64,
nsCString& dataBuffer,
nsCString& hashRef) {
nsCString* dataBuffer)
{
isBase64 = false;
// move past "data:"
@@ -192,25 +193,32 @@ nsDataHandler::ParseURI(nsCString& spec,
if (comma == buffer) {
// nothing but data
contentType.AssignLiteral("text/plain");
contentCharset.AssignLiteral("US-ASCII");
if (contentCharset) {
contentCharset->AssignLiteral("US-ASCII");
}
} else {
// everything else is content type
char *semiColon = (char *) strchr(buffer, ';');
if (semiColon)
*semiColon = '\0';
if (semiColon == buffer || base64 == buffer) {
// there is no content type, but there are other parameters
contentType.AssignLiteral("text/plain");
} else {
contentType = buffer;
ToLowerCase(contentType);
contentType.StripWhitespace();
}
if (semiColon) {
char *charset = PL_strcasestr(semiColon + 1, "charset=");
if (charset)
contentCharset = charset + sizeof("charset=") - 1;
if (contentCharset) {
char *charset = PL_strcasestr(semiColon + 1, "charset=");
if (charset) {
contentCharset->Assign(charset + sizeof("charset=") - 1);
contentCharset->StripWhitespace();
}
}
*semiColon = ';';
}
@@ -220,17 +228,15 @@ nsDataHandler::ParseURI(nsCString& spec,
if (isBase64)
*base64 = ';';
contentType.StripWhitespace();
contentCharset.StripWhitespace();
// Split encoded data from terminal "#ref" (if present)
char *data = comma + 1;
if (!hash) {
dataBuffer.Assign(data);
hashRef.Truncate();
} else {
dataBuffer.Assign(data, hash - data);
hashRef.Assign(hash);
if (dataBuffer) {
// Split encoded data from terminal "#ref" (if present)
char *data = comma + 1;
bool ok = !hash
? dataBuffer->Assign(data, mozilla::fallible)
: dataBuffer->Assign(data, hash - data, mozilla::fallible);
if (!ok) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
return NS_OK;
+5 -5
View File
@@ -30,12 +30,12 @@ public:
// Parse a data: URI and return the individual parts
// (the given spec will temporarily be modified but will be returned
// to the original before returning)
// contentCharset and dataBuffer can be nullptr if they are not needed.
static nsresult ParseURI(nsCString& spec,
nsCString& contentType,
nsCString& contentCharset,
bool& isBase64,
nsCString& dataBuffer,
nsCString& hashRef);
nsCString& contentType,
nsCString* contentCharset,
bool& isBase64,
nsCString* dataBuffer);
};
#endif /* nsDataHandler_h___ */
+10 -2
View File
@@ -226,7 +226,11 @@ public:
Replace(aCutStart, aCutLength, nullptr, 0);
}
NS_HIDDEN_(void) Truncate() { SetLength(0); }
NS_HIDDEN_(void) Truncate(size_type aNewLength = 0)
{
NS_ASSERTION(aNewLength <= Length(), "Truncate cannot make string longer");
SetLength(aNewLength);
}
/**
* Remove all occurences of characters in aSet from the string.
@@ -643,7 +647,11 @@ public:
Replace(aCutStart, aCutLength, nullptr, 0);
}
NS_HIDDEN_(void) Truncate() { SetLength(0); }
NS_HIDDEN_(void) Truncate(size_type aNewLength = 0)
{
NS_ASSERTION(aNewLength <= Length(), "Truncate cannot make string longer");
SetLength(aNewLength);
}
/**
* Remove all occurences of characters in aSet from the string.
+7 -1
View File
@@ -308,11 +308,17 @@ nsTSubstring_CharT::Assign(char_type aChar, const fallible_t&)
void
nsTSubstring_CharT::Assign(const char_type* aData)
{
if (!Assign(aData, size_type(-1), mozilla::fallible)) {
if (!Assign(aData, mozilla::fallible)) {
AllocFailed(char_traits::length(aData));
}
}
bool
nsTSubstring_CharT::Assign(const char_type* aData, const fallible_t&)
{
return Assign(aData, size_type(-1), mozilla::fallible);
}
void
nsTSubstring_CharT::Assign(const char_type* aData, size_type aLength)
{
+3
View File
@@ -365,6 +365,9 @@ public:
const fallible_t&);
void NS_FASTCALL Assign(const char_type* aData);
MOZ_WARN_UNUSED_RESULT bool NS_FASTCALL Assign(const char_type* aData,
const fallible_t&);
void NS_FASTCALL Assign(const char_type* aData, size_type aLength);
MOZ_WARN_UNUSED_RESULT bool NS_FASTCALL Assign(const char_type* aData,
size_type aLength,