mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 05:37:11 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -77,28 +77,6 @@ AccTextChangeEvent::
|
||||
(states::FOCUSED | states::EDITABLE);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccReorderEvent
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint32_t
|
||||
AccReorderEvent::IsShowHideEventTarget(const Accessible* aTarget) const
|
||||
{
|
||||
uint32_t count = mDependentEvents.Length();
|
||||
for (uint32_t index = count - 1; index < count; index--) {
|
||||
if (mDependentEvents[index]->mAccessible == aTarget) {
|
||||
uint32_t eventType = mDependentEvents[index]->mEventType;
|
||||
if (eventType == nsIAccessibleEvent::EVENT_SHOW ||
|
||||
eventType == nsIAccessibleEvent::EVENT_HIDE) {
|
||||
return mDependentEvents[index]->mEventType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccHideEvent
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -130,7 +130,7 @@ protected:
|
||||
RefPtr<Accessible> mAccessible;
|
||||
|
||||
friend class EventQueue;
|
||||
friend class AccReorderEvent;
|
||||
friend class EventTree;
|
||||
};
|
||||
|
||||
|
||||
@@ -201,8 +201,7 @@ private:
|
||||
bool mIsInserted;
|
||||
nsString mModifiedText;
|
||||
|
||||
friend class EventQueue;
|
||||
friend class AccReorderEvent;
|
||||
friend class EventTree;
|
||||
};
|
||||
|
||||
|
||||
@@ -239,7 +238,7 @@ protected:
|
||||
RefPtr<Accessible> mParent;
|
||||
RefPtr<AccTextChangeEvent> mTextChangeEvent;
|
||||
|
||||
friend class EventQueue;
|
||||
friend class EventTree;
|
||||
};
|
||||
|
||||
|
||||
@@ -269,7 +268,7 @@ protected:
|
||||
RefPtr<Accessible> mNextSibling;
|
||||
RefPtr<Accessible> mPrevSibling;
|
||||
|
||||
friend class EventQueue;
|
||||
friend class EventTree;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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; }
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -26,6 +26,11 @@ protected:
|
||||
*/
|
||||
bool PushEvent(AccEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Puts a name change event into the queue, if needed.
|
||||
*/
|
||||
bool PushNameChange(Accessible* aTarget);
|
||||
|
||||
/**
|
||||
* Process events from the queue and fires events.
|
||||
*/
|
||||
@@ -53,23 +58,7 @@ private:
|
||||
AccSelChangeEvent* aThisEvent,
|
||||
uint32_t aThisIndex);
|
||||
|
||||
/**
|
||||
* Coalesce text change events caused by sibling hide events.
|
||||
*/
|
||||
void CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
|
||||
AccHideEvent* aThisEvent);
|
||||
void CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
|
||||
AccShowEvent* aThisEvent);
|
||||
|
||||
/**
|
||||
* Create text change event caused by hide or show event. When a node is
|
||||
* hidden/removed or shown/appended, the text in an ancestor hyper text will
|
||||
* lose or get new characters.
|
||||
*/
|
||||
void CreateTextChangeEventFor(AccMutationEvent* aEvent);
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* The document accessible reference owning this queue.
|
||||
*/
|
||||
@@ -79,7 +68,7 @@ protected:
|
||||
* Pending events array. Don't make this an AutoTArray; we use
|
||||
* SwapElements() on it.
|
||||
*/
|
||||
nsTArray<RefPtr<AccEvent> > mEvents;
|
||||
nsTArray<RefPtr<AccEvent>> mEvents;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
|
||||
@@ -0,0 +1,536 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "EventTree.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "nsEventShell.h"
|
||||
#include "DocAccessible.h"
|
||||
#ifdef A11Y_LOG
|
||||
#include "Logging.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TreeMutation class
|
||||
|
||||
EventTree* const TreeMutation::kNoEventTree = reinterpret_cast<EventTree*>(-1);
|
||||
|
||||
TreeMutation::TreeMutation(Accessible* aParent, bool aNoEvents) :
|
||||
mParent(aParent), mStartIdx(UINT32_MAX),
|
||||
mStateFlagsCopy(mParent->mStateFlags),
|
||||
mEventTree(aNoEvents ? kNoEventTree : nullptr)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
mIsDone = false;
|
||||
#endif
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
if (mEventTree != kNoEventTree && logging::IsEnabled(logging::eEventTree)) {
|
||||
logging::MsgBegin("EVENTS_TREE", "reordering tree before");
|
||||
logging::AccessibleInfo("reordering for", mParent);
|
||||
Controller()->RootEventTree().Log();
|
||||
logging::MsgEnd();
|
||||
|
||||
logging::MsgBegin("EVENTS_TREE", "Container tree");
|
||||
if (logging::IsEnabled(logging::eVerbose)) {
|
||||
nsAutoString level;
|
||||
Accessible* root = mParent->Document();
|
||||
do {
|
||||
const char* prefix = "";
|
||||
if (mParent == root) {
|
||||
prefix = "_X_";
|
||||
}
|
||||
else {
|
||||
const EventTree& ret = Controller()->RootEventTree();
|
||||
if (ret.Find(root)) {
|
||||
prefix = "_с_";
|
||||
}
|
||||
}
|
||||
|
||||
printf("%s", NS_ConvertUTF16toUTF8(level).get());
|
||||
logging::AccessibleInfo(prefix, root);
|
||||
if (root->FirstChild() && !root->FirstChild()->IsDoc()) {
|
||||
level.Append(NS_LITERAL_STRING(" "));
|
||||
root = root->FirstChild();
|
||||
continue;
|
||||
}
|
||||
int32_t idxInParent = root->mParent ?
|
||||
root->mParent->mChildren.IndexOf(root) : -1;
|
||||
if (idxInParent != -1 &&
|
||||
idxInParent < static_cast<int32_t>(root->mParent->mChildren.Length() - 1)) {
|
||||
root = root->mParent->mChildren.ElementAt(idxInParent + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
while ((root = root->Parent()) && !root->IsDoc()) {
|
||||
level.Cut(0, 2);
|
||||
|
||||
int32_t idxInParent = root->mParent ?
|
||||
root->mParent->mChildren.IndexOf(root) : -1;
|
||||
if (idxInParent != -1 &&
|
||||
idxInParent < static_cast<int32_t>(root->mParent->mChildren.Length() - 1)) {
|
||||
root = root->mParent->mChildren.ElementAt(idxInParent + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (root && !root->IsDoc());
|
||||
}
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
|
||||
mParent->mStateFlags |= Accessible::eKidsMutating;
|
||||
}
|
||||
|
||||
TreeMutation::~TreeMutation()
|
||||
{
|
||||
MOZ_ASSERT(mIsDone, "Done() must be called explicitly");
|
||||
}
|
||||
|
||||
void
|
||||
TreeMutation::AfterInsertion(Accessible* aChild)
|
||||
{
|
||||
MOZ_ASSERT(aChild->Parent() == mParent);
|
||||
|
||||
if (static_cast<uint32_t>(aChild->mIndexInParent) < mStartIdx) {
|
||||
mStartIdx = aChild->mIndexInParent + 1;
|
||||
}
|
||||
|
||||
if (!mEventTree) {
|
||||
mEventTree = Controller()->QueueMutation(mParent);
|
||||
if (!mEventTree) {
|
||||
mEventTree = kNoEventTree;
|
||||
}
|
||||
}
|
||||
|
||||
if (mEventTree != kNoEventTree) {
|
||||
mEventTree->Shown(aChild);
|
||||
Controller()->QueueNameChange(aChild);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TreeMutation::BeforeRemoval(Accessible* aChild, bool aNoShutdown)
|
||||
{
|
||||
MOZ_ASSERT(aChild->Parent() == mParent);
|
||||
|
||||
if (static_cast<uint32_t>(aChild->mIndexInParent) < mStartIdx) {
|
||||
mStartIdx = aChild->mIndexInParent;
|
||||
}
|
||||
|
||||
if (!mEventTree) {
|
||||
mEventTree = Controller()->QueueMutation(mParent);
|
||||
if (!mEventTree) {
|
||||
mEventTree = kNoEventTree;
|
||||
}
|
||||
}
|
||||
|
||||
if (mEventTree != kNoEventTree) {
|
||||
mEventTree->Hidden(aChild, !aNoShutdown);
|
||||
Controller()->QueueNameChange(aChild);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TreeMutation::Done()
|
||||
{
|
||||
MOZ_ASSERT(mParent->mStateFlags & Accessible::eKidsMutating);
|
||||
mParent->mStateFlags &= ~Accessible::eKidsMutating;
|
||||
|
||||
uint32_t length = mParent->mChildren.Length();
|
||||
#ifdef DEBUG
|
||||
for (uint32_t idx = 0; idx < mStartIdx && idx < length; idx++) {
|
||||
MOZ_ASSERT(mParent->mChildren[idx]->mIndexInParent == static_cast<int32_t>(idx),
|
||||
"Wrong index detected");
|
||||
}
|
||||
#endif
|
||||
|
||||
for (uint32_t idx = mStartIdx; idx < length; idx++) {
|
||||
mParent->mChildren[idx]->mIndexInParent = idx;
|
||||
mParent->mChildren[idx]->mStateFlags |= Accessible::eGroupInfoDirty;
|
||||
}
|
||||
|
||||
if (mStartIdx < mParent->mChildren.Length() - 1) {
|
||||
mParent->mEmbeddedObjCollector = nullptr;
|
||||
}
|
||||
|
||||
mParent->mStateFlags |= mStateFlagsCopy & Accessible::eKidsMutating;
|
||||
|
||||
#ifdef DEBUG
|
||||
mIsDone = true;
|
||||
#endif
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
if (mEventTree != kNoEventTree && logging::IsEnabled(logging::eEventTree)) {
|
||||
logging::MsgBegin("EVENTS_TREE", "reordering tree after");
|
||||
logging::AccessibleInfo("reordering for", mParent);
|
||||
Controller()->RootEventTree().Log();
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// EventTree
|
||||
|
||||
void
|
||||
EventTree::Process()
|
||||
{
|
||||
EventTree* node = mFirst;
|
||||
while (node) {
|
||||
node->Process();
|
||||
node = node->mNext;
|
||||
}
|
||||
|
||||
// Fire mutation events.
|
||||
if (mContainer) {
|
||||
uint32_t eventsCount = mDependentEvents.Length();
|
||||
for (uint32_t jdx = 0; jdx < eventsCount; jdx++) {
|
||||
AccMutationEvent* mtEvent = mDependentEvents[jdx];
|
||||
MOZ_ASSERT(mtEvent->mEventRule != AccEvent::eDoNotEmit,
|
||||
"The event shouldn't be presented in the tree");
|
||||
|
||||
nsEventShell::FireEvent(mtEvent);
|
||||
if (mtEvent->mTextChangeEvent) {
|
||||
nsEventShell::FireEvent(mtEvent->mTextChangeEvent);
|
||||
}
|
||||
|
||||
if (mtEvent->IsHide()) {
|
||||
// Fire menupopup end event before a hide event if a menu goes away.
|
||||
|
||||
// XXX: We don't look into children of hidden subtree to find hiding
|
||||
// menupopup (as we did prior bug 570275) because we don't do that when
|
||||
// menu is showing (and that's impossible until bug 606924 is fixed).
|
||||
// Nevertheless we should do this at least because layout coalesces
|
||||
// the changes before our processing and we may miss some menupopup
|
||||
// events. Now we just want to be consistent in content insertion/removal
|
||||
// handling.
|
||||
if (mtEvent->mAccessible->ARIARole() == roles::MENUPOPUP) {
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
|
||||
mtEvent->mAccessible);
|
||||
}
|
||||
|
||||
AccHideEvent* hideEvent = downcast_accEvent(mtEvent);
|
||||
if (hideEvent->NeedsShutdown()) {
|
||||
mContainer->Document()->ShutdownChildrenInSubtree(hideEvent->mAccessible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fire reorder event at last.
|
||||
if (mFireReorder) {
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_REORDER, mContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EventTree*
|
||||
EventTree::FindOrInsert(Accessible* aContainer)
|
||||
{
|
||||
if (!mFirst) {
|
||||
return mFirst = new EventTree(aContainer);
|
||||
}
|
||||
|
||||
EventTree* prevNode = nullptr;
|
||||
EventTree* node = mFirst;
|
||||
do {
|
||||
MOZ_ASSERT(!node->mContainer->IsApplication(),
|
||||
"No event for application accessible is expected here");
|
||||
MOZ_ASSERT(!node->mContainer->IsDefunct(), "An event target has to be alive");
|
||||
|
||||
// Case of same target.
|
||||
if (node->mContainer == aContainer) {
|
||||
return node;
|
||||
}
|
||||
|
||||
// Check if the given container is contained by a current node
|
||||
Accessible* tailRoot = aContainer->Document();
|
||||
Accessible* tailParent = aContainer;
|
||||
|
||||
EventTree* matchNode = nullptr;
|
||||
Accessible* matchParent = nullptr;
|
||||
while (true) {
|
||||
// Reached a top, no match for a current event.
|
||||
if (tailParent == tailRoot) {
|
||||
// If we have a match in parents then continue to look in siblings.
|
||||
if (matchNode && node->mNext) {
|
||||
node = node->mNext;
|
||||
if (node->mContainer == aContainer) {
|
||||
return node; // case of same target
|
||||
}
|
||||
tailParent = aContainer;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// We got a match.
|
||||
if (tailParent->Parent() == node->mContainer) {
|
||||
matchNode = node;
|
||||
matchParent = tailParent;
|
||||
|
||||
// Search the subtree for a better match.
|
||||
if (node->mFirst) {
|
||||
tailRoot = node->mContainer;
|
||||
node = node->mFirst;
|
||||
if (node->mContainer == aContainer) {
|
||||
return node; // case of same target
|
||||
}
|
||||
tailParent = aContainer;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
tailParent = tailParent->Parent();
|
||||
MOZ_ASSERT(tailParent, "Wrong tree");
|
||||
if (!tailParent) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The given node is contained by a current node
|
||||
// if hide of a current node contains the given node
|
||||
// then assert
|
||||
// if show of a current node contains the given node
|
||||
// then ignore the given node
|
||||
// otherwise ignore the given node, but not its show and hide events
|
||||
if (matchNode) {
|
||||
uint32_t eventType = 0;
|
||||
uint32_t count = matchNode->mDependentEvents.Length();
|
||||
for (uint32_t idx = count - 1; idx < count; idx--) {
|
||||
if (matchNode->mDependentEvents[idx]->mAccessible == matchParent) {
|
||||
eventType = matchNode->mDependentEvents[idx]->mEventType;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(eventType != nsIAccessibleEvent::EVENT_HIDE,
|
||||
"Accessible tree was modified after it was removed");
|
||||
|
||||
// If contained by show event target then no events are required.
|
||||
if (eventType == nsIAccessibleEvent::EVENT_SHOW) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
node->mFirst = new EventTree(aContainer);
|
||||
node->mFirst->mFireReorder = false;
|
||||
return node->mFirst;
|
||||
}
|
||||
|
||||
// If the given node contains a current node
|
||||
// then
|
||||
// if show or hide of the given node contains a grand parent of the current node
|
||||
// then ignore the current node and its show and hide events
|
||||
// otherwise ignore the current node, but not its show and hide events
|
||||
Accessible* curParent = node->mContainer;
|
||||
while (curParent && !curParent->IsDoc()) {
|
||||
if (curParent->Parent() != aContainer) {
|
||||
curParent = curParent->Parent();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Insert the tail node into the hierarchy between the current node and
|
||||
// its parent.
|
||||
node->mFireReorder = false;
|
||||
nsAutoPtr<EventTree>& nodeOwnerRef = prevNode ? prevNode->mNext : mFirst;
|
||||
nsAutoPtr<EventTree> newNode(new EventTree(aContainer));
|
||||
newNode->mFirst = Move(nodeOwnerRef);
|
||||
nodeOwnerRef = Move(newNode);
|
||||
nodeOwnerRef->mNext = Move(node->mNext);
|
||||
|
||||
// Check if a next node is contained by the given node too, and move them
|
||||
// under the given node if so.
|
||||
prevNode = nodeOwnerRef;
|
||||
node = nodeOwnerRef->mNext;
|
||||
nsAutoPtr<EventTree>* nodeRef = &nodeOwnerRef->mNext;
|
||||
EventTree* insNode = nodeOwnerRef->mFirst;
|
||||
while (node) {
|
||||
Accessible* curParent = node->mContainer;
|
||||
while (curParent && !curParent->IsDoc()) {
|
||||
if (curParent->Parent() != aContainer) {
|
||||
curParent = curParent->Parent();
|
||||
continue;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!insNode->mNext);
|
||||
|
||||
node->mFireReorder = false;
|
||||
insNode->mNext = Move(*nodeRef);
|
||||
insNode = insNode->mNext;
|
||||
|
||||
prevNode->mNext = Move(node->mNext);
|
||||
node = prevNode;
|
||||
break;
|
||||
}
|
||||
|
||||
prevNode = node;
|
||||
nodeRef = &node->mNext;
|
||||
node = node->mNext;
|
||||
}
|
||||
|
||||
return nodeOwnerRef;
|
||||
}
|
||||
|
||||
prevNode = node;
|
||||
} while ((node = node->mNext));
|
||||
|
||||
MOZ_ASSERT(prevNode, "Nowhere to insert");
|
||||
return prevNode->mNext = new EventTree(aContainer);
|
||||
}
|
||||
|
||||
const EventTree*
|
||||
EventTree::Find(const Accessible* aContainer) const
|
||||
{
|
||||
const EventTree* et = this;
|
||||
while (et) {
|
||||
if (et->mContainer == aContainer) {
|
||||
return et;
|
||||
}
|
||||
|
||||
if (et->mFirst) {
|
||||
et = et->mFirst;
|
||||
const EventTree* cet = et->Find(aContainer);
|
||||
if (cet) {
|
||||
return cet;
|
||||
}
|
||||
}
|
||||
|
||||
et = et->mNext;
|
||||
const EventTree* cet = et->Find(aContainer);
|
||||
if (cet) {
|
||||
return cet;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
void
|
||||
EventTree::Log(uint32_t aLevel) const
|
||||
{
|
||||
if (aLevel == UINT32_MAX) {
|
||||
if (mFirst) {
|
||||
mFirst->Log(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < aLevel; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
logging::AccessibleInfo("container", mContainer);
|
||||
|
||||
for (uint32_t i = 0; i < mDependentEvents.Length(); i++) {
|
||||
AccMutationEvent* ev = mDependentEvents[i];
|
||||
if (ev->IsShow()) {
|
||||
for (uint32_t i = 0; i < aLevel; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
logging::AccessibleInfo("shown", ev->mAccessible);
|
||||
}
|
||||
else {
|
||||
for (uint32_t i = 0; i < aLevel; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
logging::AccessibleInfo("hidden", ev->mAccessible);
|
||||
}
|
||||
}
|
||||
|
||||
if (mFirst) {
|
||||
mFirst->Log(aLevel + 1);
|
||||
}
|
||||
|
||||
if (mNext) {
|
||||
mNext->Log(aLevel);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
EventTree::Mutated(AccMutationEvent* aEv)
|
||||
{
|
||||
// If shown or hidden node is a root of previously mutated subtree, then
|
||||
// discard those subtree mutations as we are no longer interested in them.
|
||||
EventTree* node = mFirst;
|
||||
while (node) {
|
||||
if (node->mContainer == aEv->mAccessible) {
|
||||
node->Clear();
|
||||
break;
|
||||
}
|
||||
node = node->mNext;
|
||||
}
|
||||
|
||||
AccMutationEvent* prevEvent = mDependentEvents.SafeLastElement(nullptr);
|
||||
mDependentEvents.AppendElement(aEv);
|
||||
|
||||
// Coalesce text change events from this hide/show event and the previous one.
|
||||
if (prevEvent && aEv->mEventType == prevEvent->mEventType) {
|
||||
if (aEv->IsHide()) {
|
||||
// XXX: we need a way to ignore SplitNode and JoinNode() when they do not
|
||||
// affect the text within the hypertext.
|
||||
AccTextChangeEvent* prevTextEvent = prevEvent->mTextChangeEvent;
|
||||
if (prevTextEvent) {
|
||||
AccHideEvent* hideEvent = downcast_accEvent(aEv);
|
||||
AccHideEvent* prevHideEvent = downcast_accEvent(prevEvent);
|
||||
|
||||
if (prevHideEvent->mNextSibling == hideEvent->mAccessible) {
|
||||
hideEvent->mAccessible->AppendTextTo(prevTextEvent->mModifiedText);
|
||||
}
|
||||
else if (prevHideEvent->mPrevSibling == hideEvent->mAccessible) {
|
||||
uint32_t oldLen = prevTextEvent->GetLength();
|
||||
hideEvent->mAccessible->AppendTextTo(prevTextEvent->mModifiedText);
|
||||
prevTextEvent->mStart -= prevTextEvent->GetLength() - oldLen;
|
||||
}
|
||||
|
||||
hideEvent->mTextChangeEvent.swap(prevEvent->mTextChangeEvent);
|
||||
}
|
||||
}
|
||||
else {
|
||||
AccTextChangeEvent* prevTextEvent = prevEvent->mTextChangeEvent;
|
||||
if (prevTextEvent) {
|
||||
if (aEv->mAccessible->IndexInParent() ==
|
||||
prevEvent->mAccessible->IndexInParent() + 1) {
|
||||
// If tail target was inserted after this target, i.e. tail target is next
|
||||
// sibling of this target.
|
||||
aEv->mAccessible->AppendTextTo(prevTextEvent->mModifiedText);
|
||||
}
|
||||
else if (aEv->mAccessible->IndexInParent() ==
|
||||
prevEvent->mAccessible->IndexInParent() - 1) {
|
||||
// If tail target was inserted before this target, i.e. tail target is
|
||||
// previous sibling of this target.
|
||||
nsAutoString startText;
|
||||
aEv->mAccessible->AppendTextTo(startText);
|
||||
prevTextEvent->mModifiedText = startText + prevTextEvent->mModifiedText;
|
||||
prevTextEvent->mStart -= startText.Length();
|
||||
}
|
||||
|
||||
aEv->mTextChangeEvent.swap(prevEvent->mTextChangeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a text change event caused by this hide/show event. When a node is
|
||||
// hidden/removed or shown/appended, the text in an ancestor hyper text will
|
||||
// lose or get new characters.
|
||||
if (aEv->mTextChangeEvent || !mContainer->IsHyperText()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString text;
|
||||
aEv->mAccessible->AppendTextTo(text);
|
||||
if (text.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t offset = mContainer->AsHyperText()->GetChildOffset(aEv->mAccessible);
|
||||
aEv->mTextChangeEvent =
|
||||
new AccTextChangeEvent(mContainer, offset, text, aEv->IsShow(),
|
||||
aEv->mIsFromUserInput ? eFromUserInput : eNoUserInput);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_a11y_EventTree_h_
|
||||
#define mozilla_a11y_EventTree_h_
|
||||
|
||||
#include "AccEvent.h"
|
||||
#include "Accessible.h"
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
/**
|
||||
* This class makes sure required tasks are done before and after tree
|
||||
* mutations. Currently this only includes group info invalidation. You must
|
||||
* have an object of this class on the stack when calling methods that mutate
|
||||
* the accessible tree.
|
||||
*/
|
||||
class TreeMutation final
|
||||
{
|
||||
public:
|
||||
static const bool kNoEvents = true;
|
||||
static const bool kNoShutdown = true;
|
||||
|
||||
explicit TreeMutation(Accessible* aParent, bool aNoEvents = false);
|
||||
~TreeMutation();
|
||||
|
||||
void AfterInsertion(Accessible* aChild);
|
||||
void BeforeRemoval(Accessible* aChild, bool aNoShutdown = false);
|
||||
void Done();
|
||||
|
||||
private:
|
||||
NotificationController* Controller() const
|
||||
{ return mParent->Document()->Controller(); }
|
||||
|
||||
static EventTree* const kNoEventTree;
|
||||
|
||||
Accessible* mParent;
|
||||
uint32_t mStartIdx;
|
||||
uint32_t mStateFlagsCopy;
|
||||
EventTree* mEventTree;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool mIsDone;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A mutation events coalescence structure.
|
||||
*/
|
||||
class EventTree final {
|
||||
public:
|
||||
EventTree() :
|
||||
mFirst(nullptr), mNext(nullptr), mContainer(nullptr), mFireReorder(true) { }
|
||||
explicit EventTree(Accessible* aContainer) :
|
||||
mFirst(nullptr), mNext(nullptr), mContainer(aContainer), mFireReorder(true) { }
|
||||
~EventTree() { Clear(); }
|
||||
|
||||
void Shown(Accessible* aChild)
|
||||
{
|
||||
RefPtr<AccShowEvent> ev = new AccShowEvent(aChild);
|
||||
Mutated(ev);
|
||||
}
|
||||
|
||||
void Hidden(Accessible* aChild, bool aNeedsShutdown = true)
|
||||
{
|
||||
RefPtr<AccHideEvent> ev = new AccHideEvent(aChild, aNeedsShutdown);
|
||||
Mutated(ev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an event tree node for the given accessible.
|
||||
*/
|
||||
const EventTree* Find(const Accessible* aContainer) const;
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
void Log(uint32_t aLevel = UINT32_MAX) const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
/**
|
||||
* Processes the event queue and fires events.
|
||||
*/
|
||||
void Process();
|
||||
|
||||
/**
|
||||
* Return an event subtree for the given accessible.
|
||||
*/
|
||||
EventTree* FindOrInsert(Accessible* aContainer);
|
||||
|
||||
void Mutated(AccMutationEvent* aEv);
|
||||
void Clear() { mFirst = nullptr; mNext = nullptr; mContainer = nullptr; }
|
||||
|
||||
nsAutoPtr<EventTree> mFirst;
|
||||
nsAutoPtr<EventTree> mNext;
|
||||
|
||||
Accessible* mContainer;
|
||||
nsTArray<RefPtr<AccMutationEvent>> mDependentEvents;
|
||||
bool mFireReorder;
|
||||
|
||||
friend class NotificationController;
|
||||
};
|
||||
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_a11y_EventQueue_h_
|
||||
@@ -44,6 +44,7 @@ static ModuleRep sModuleMap[] = {
|
||||
{ "doclifecycle", logging::eDocLifeCycle },
|
||||
|
||||
{ "events", logging::eEvents },
|
||||
{ "eventTree", logging::eEventTree },
|
||||
{ "platforms", logging::ePlatforms },
|
||||
{ "text", logging::eText },
|
||||
{ "tree", logging::eTree },
|
||||
|
||||
@@ -34,18 +34,19 @@ enum EModules {
|
||||
eDocLifeCycle = eDocLoad | eDocCreate | eDocDestroy,
|
||||
|
||||
eEvents = 1 << 3,
|
||||
ePlatforms = 1 << 4,
|
||||
eText = 1 << 5,
|
||||
eTree = 1 << 6,
|
||||
eEventTree = 1 << 4,
|
||||
ePlatforms = 1 << 5,
|
||||
eText = 1 << 6,
|
||||
eTree = 1 << 7,
|
||||
|
||||
eDOMEvents = 1 << 7,
|
||||
eFocus = 1 << 8,
|
||||
eSelection = 1 << 9,
|
||||
eDOMEvents = 1 << 8,
|
||||
eFocus = 1 << 9,
|
||||
eSelection = 1 << 10,
|
||||
eNotifications = eDOMEvents | eSelection | eFocus,
|
||||
|
||||
// extras
|
||||
eStack = 1 << 10,
|
||||
eVerbose = 1 << 11
|
||||
eStack = 1 << 11,
|
||||
eVerbose = 1 << 12
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -97,6 +97,17 @@ NotificationController::Shutdown()
|
||||
mNotifications.Clear();
|
||||
mEvents.Clear();
|
||||
mRelocations.Clear();
|
||||
mEventTree.Clear();
|
||||
}
|
||||
|
||||
EventTree*
|
||||
NotificationController::QueueMutation(Accessible* aContainer)
|
||||
{
|
||||
EventTree* tree = mEventTree.FindOrInsert(aContainer);
|
||||
if (tree) {
|
||||
ScheduleProcessing();
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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*
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -40,9 +40,6 @@ public:
|
||||
|
||||
protected:
|
||||
virtual ~LeafAccessible() {}
|
||||
|
||||
// Accessible
|
||||
virtual void CacheChildren() override;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -1880,11 +1880,10 @@ HyperTextAccessible::NativeName(nsString& aName)
|
||||
}
|
||||
|
||||
void
|
||||
HyperTextAccessible::InvalidateChildren()
|
||||
HyperTextAccessible::Shutdown()
|
||||
{
|
||||
mOffsets.Clear();
|
||||
|
||||
AccessibleWrap::InvalidateChildren();
|
||||
AccessibleWrap::Shutdown();
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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*
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -60,9 +60,6 @@ public:
|
||||
protected:
|
||||
virtual ~HTMLLIAccessible() { }
|
||||
|
||||
// Accessible
|
||||
virtual void CacheChildren() override;
|
||||
|
||||
private:
|
||||
RefPtr<HTMLListBulletAccessible> mBullet;
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -33,7 +33,6 @@ public:
|
||||
protected:
|
||||
// Accessible
|
||||
virtual ENameValueFlag NativeName(nsString& aName) override;
|
||||
virtual void CacheChildren() override;
|
||||
|
||||
private:
|
||||
RefPtr<XULLabelTextLeafAccessible> mValueTextLeaf;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -241,9 +241,6 @@ public:
|
||||
protected:
|
||||
virtual ~XULTreeItemAccessible();
|
||||
|
||||
// Accessible
|
||||
virtual void CacheChildren() override;
|
||||
|
||||
// XULTreeItemAccessible
|
||||
nsCOMPtr<nsITreeColumn> mColumn;
|
||||
nsString mCachedName;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -97,9 +97,6 @@ public:
|
||||
protected:
|
||||
virtual ~XULTreeGridRowAccessible();
|
||||
|
||||
// Accessible
|
||||
virtual void CacheChildren() override;
|
||||
|
||||
// XULTreeItemAccessibleBase
|
||||
mutable nsRefPtrHashtable<nsPtrHashKey<const void>, XULTreeGridCellAccessible>
|
||||
mAccessibleCache;
|
||||
|
||||
+4
-2
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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___ */
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user