Files
palemoon27/accessible/base/EventTree.cpp
T
roytam1 946864fcd3 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1263188 - stop bleeding, r=yzen (e121b0dff3)
- Bug 1263188 - more assertions, part3, r=yzen (6623350334)
- Bug 1263188 - fix event tree coalescence, part4, r=yzen (dd4b0293ab)
- Bug 1262417 - bind a value change event with reorder event firing, r=yzen (eee45f738e)
- Bug 1287874: Add missing math.h include. r=drno (cd8b7e74d3)
- Bug 967300 - enable cairo's atomic support on gcc-esque compilers; r=mshal (025d886e53)
- Bug 1158871 - use new-style __atomic_* primitives in cairo; r=jrmuizel,ted.mielczarek (9ac76e7769)
- fix build on non-windows systems (d807139c54)
- Bug 1205854 - Workaround for Windows printer drivers that can't handle swapped X and Y axes. r=jrmuizel (1f35fc8d76)
- Bug 1186339 - skip STUN/TURN servers with non-matching IP versions for TCP sockets. r=bwc (61f841554e)
- Bug 933986. Switch over from index to an id, and ensure uniqueness when feeding into the candidate priority calculation. r=drno (168f4d68a3)
- Bug 1190720 - only check IP version for STUN server IPs. r=bwc (9f95c5995f)
- Bug 1192403 - improve ICE TCP error message. r=mjf (28afcf181c)
- Bug 1037618 - Part 1: Add some logging to highlight TCP connection failures. r=drno (f5f861563a)
- Bug 1251214: Ignore R_WOULDBLOCK in nr_stun_client_send_request r=ekr (00db6e3022)
- Bug 980609: Do not assert that TURN TCP client contexts can't fail before attempting to allocate. r=ekr (3c20586954)
- Bug 1237299: addedd missing address family to DNS lookup for proxies r=bwc (eea322d3e1)
- Bug 1258753: Base candidate pair priority on controlling/controlled. r=drno (52dc0783ca)
- Bug 1246363: add logging to detect if relay only option is set. r=mjf (fab0c5aec8)
- Bug 1252777: skip over ICE TCP host candidate creation failures. r=bwc (2225289e5e)
- Bug 1242324: Give VP9 priority over vp8 when both are enabled r=drno (5e31cde0b1)
- Bug 1247656: Make sure that remote reoffer does not change the media type of an m-line. r=drno (d34bd649b3)
- Bug 906986 - Ice restart and tests. r=bwc, r=drno, r=smaug (080ee96a4c)
- Bug 1259842: allow RFC1918 pairing again r=mjf (ab11d2a41a)
- Bug 1233181: improve misleading log message about missing STUN & TURN servers. r?=mjf (f744195587)
- Bug 1244338 - Don't try to clean up |ctx| if null. r=drno (4c7da59d2e)
- Bug 1256720: Remove a bad assertion, and simplify some code. r=drno (e8f5d04e93)
- Bug 1257472: Forgive rport of 0. r=drno,ekr (8d02b24855)
- Bug 1214279: Fix the same infinite loop from bug 957236 in a different place. r=drno (9780450268)
- Bug 1229633: hash interface names on Windows. r=ekr (6ff60bf22a)
- Bug 895793: added interface type and link speed detection for Windows. r=bwc (f7783adae6)
- Bug 1180311 - Add null check to ifa_addr. r=ekr (90658f741b)
- Bug 1183985: Fixed WebRTC socket leak on Linux. r=bwc (b06efe6ee9)
- Bug 1231117 - Use xlocale on DragonFly as well. r=jesup (e7a09ee751)
- Bug 1254780 (attempt 2) - Shrink log_types from 1024 entries to 16. r=ekr. (efa73ca5af)
- Bug 1187075: Implement cairo atomics for Win32. r=jrmuizel (5b92bd3cff)
- Bug 1161170 - backport upstream fix for race in Cairo freed_pool. r=jrmuizel (3287e5b154)
- Bug 1255269. Get rid of nsITCPSocketCallback.fireDataEvent. r=jdm (9460469c15)
- Bug 1246925 - log filtering_type and mapping_type only if they are valid pointers. r=ekr (57af98cd1f)
- Bug 1248637: Prune duplicate CANCELLED candidate pairs. r=drno (c982b8492d)
- Bug 676001 - Fix for stroke hit testing on cairo. r=jrmuizel (1cedebd554)
- Bug 1252171: Update last_used_ on TCP port mappings when they are used, similar to UDP. r=drno (9229a3316a)
- Bug 906986 - Rework rollback/finalize to include a committed state. r=bwc, r=drno (14ec947fd3)
- Bug 1264344 - Don't restart ICE on first CreateOffer call if iceRestart option is true. r=bwc (d2850773f6)
- Bug 1208371 - Update sink identity after adding track. r=mt (032aa5fcc4)
- Bug 1247547: removed double accounting for WEBRTC_ICE_ADD_CANDIDATE telemetry probes. r=bwc (5abdc7d93a)
- Bug 1254691 - Remove SEC_NORMAL from webrtc/. r=bwc (20dfff4efd)
- Bug 1264351: removed hand break which disables ICE TCP on e10s r=jesup (6bc2a9936d)
- bit of  Bug 906986 - Rework rollback/finalize (ab1f5378f2)
- Bug 1161619: RunStatsQuery leak fix. r=jib (2e4aca6869)
- Bug 1256430: start AEC log independently of webrtc TRACE r=jesup,pkerr (29978d0a69)
- Bug 1260784 - fix Stop Debug Mode button r=jesup (a1ac6fb833)
- Bug 1220043 - Add workaround for internal complier error by VS2015. r=rjesup (c4368b1cad)
- Bug 1252073 - Uninitialised value uses in mozilla::EncodingConstraints::operator==. r=docfaraday@gmail.com. (1be0174cd9)
- Bug 1179859 - Fix _cairo_box_intersects_line_segment early rejection tests. r=jrmuizel (b2493a8c1e)
- Bug 1186040 - use XPCOM refcounting macros instead of mozilla::RefCounted in WebrtcGlobalParent.h; r=jesup (f9bb1ba504)
- Bug 1188407: switch packetloss to a rate from total-packets-lost-per-update r=jib (96297c4cd7)
- Bug 1202696 - check surface status in _cairo_surface_get_extents. r=jmuizelaar (3f5c49d9d5)
- Bug 1207750 - setting an environment variable will let us crash as Cairo errors happen. r=bschouten (caf8d9ee9a)
- Bug 1215774 - use abort() to abort on error in Cairo. r=jmuizelaar (36e9c0bb2b)
- minor (d2d4a96024)
- Bug 1246011: fixed PT comparising for PT's without rtpmap. r=jesup (38c1f91a1f)
- Bug 1249098: Support maxplaybackrate for opus. r=jesup (7a38717c47)
- Bug 818618: Honor (and emit) opus stereo fmtp param. r=jesup (436175287f)
- Bug 1258558 - Don't check extents for empty regions. r=jrmuizel (3751780ae0)
- Bug 1236266 - Don't generate invalid empty regions in pixman (r=jmuielaar) (2c22835afd)
- Bug 1255281. Add pixman fast path for bilinear x888_8888_SRC. r=lsalzman (0e585d5114)
- Bug 1241012: Remove moz-d2d1-1.h stub headers from the tree. r=jrmuizel  Please enter the commit message for your changes. Lines starting (b1dc61c08b)
- Bug 1240790: Add newlines to WEBRTC_TRACE_FILE. r=rjesup (d25a696ff8)
- bug 1241064 - updating stats filter SSRC when audio channel SSRC changes; r=jib (481c2ad1e0)
- Bug 1247574: Force webrtc audio input processing to resample to target rate to fix 16KHz mics. r=padenot (9dea99341e)
- Bug 1158741 - Implement a version of omxSP_FFTInv_CCSToR_F32_Sfs in openmax DL's FFT that is not scaled r=padenot (0acefaf93a)
- Bug 1253149 - Use bool instead of int for boolean return values. r=SimonSapin (0303c4c8ab)
2024-06-07 10:07:03 +08:00

516 lines
15 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* -*- 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) {
// Skip a node and its subtree if its container is not in the document.
if (node->mContainer->IsInDocument()) {
node->Process();
}
node = node->mNext;
}
MOZ_ASSERT(mContainer || mDependentEvents.IsEmpty(),
"No container, no events");
MOZ_ASSERT(!mContainer || !mContainer->IsDefunct(),
"Processing events for defunct container");
// Fire mutation events.
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()) {
mtEvent->GetDocAccessible()->ShutdownChildrenInSubtree(mtEvent->mAccessible);
}
}
}
// Fire reorder event at last.
if (mFireReorder) {
MOZ_ASSERT(mContainer);
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_REORDER, mContainer);
mContainer->Document()->MaybeNotifyOfValueChange(mContainer);
}
mDependentEvents.Clear();
}
EventTree*
EventTree::FindOrInsert(Accessible* aContainer)
{
if (!mFirst) {
return mFirst = new EventTree(aContainer, true);
}
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* top = mContainer ? mContainer : aContainer->Document();
Accessible* parent = aContainer;
while (parent) {
// Reached a top, no match for a current event.
if (parent == top) {
break;
}
// We got a match.
if (parent->Parent() == node->mContainer) {
return node->FindOrInsert(aContainer);
}
parent = parent->Parent();
MOZ_ASSERT(parent, "Wrong tree");
}
// If the given container 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, mDependentEvents.IsEmpty()));
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");
MOZ_ASSERT(!prevNode->mNext, "Taken by another node");
// If 'this' node contains the given container accessible, then
// do not emit a reorder event for the container
// if a dependent show event target contains the given container then do not
// emit show / hide events (see Process() method)
return prevNode->mNext = new EventTree(aContainer, mDependentEvents.IsEmpty());
}
void
EventTree::Clear()
{
mFirst = nullptr;
mNext = nullptr;
mContainer = nullptr;
uint32_t eventsCount = mDependentEvents.Length();
for (uint32_t jdx = 0; jdx < eventsCount; jdx++) {
AccHideEvent* ev = downcast_accEvent(mDependentEvents[jdx]);
if (ev && ev->NeedsShutdown()) {
ev->GetDocAccessible()->ShutdownChildrenInSubtree(ev->mAccessible);
}
}
mDependentEvents.Clear();
}
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.
nsAutoPtr<EventTree>* node = &mFirst;
while (*node) {
if ((*node)->mContainer == aEv->mAccessible) {
*node = Move((*node)->mNext);
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);
}