Bug 678454 - coalesce events by accessible tree, r=tbsaunde

This commit is contained in:
Alexander Surkov 2012-11-20 13:53:38 +09:00
parent b361a10312
commit 2acd72040f
18 changed files with 201 additions and 374 deletions

View File

@ -35,47 +35,9 @@ AccEvent::AccEvent(uint32_t aEventType, Accessible* aAccessible,
CaptureIsFromUserInput(aIsFromUserInput);
}
AccEvent::AccEvent(uint32_t aEventType, nsINode* aNode,
EIsFromUserInput aIsFromUserInput, EEventRule aEventRule) :
mEventType(aEventType), mEventRule(aEventRule), mNode(aNode)
{
CaptureIsFromUserInput(aIsFromUserInput);
}
////////////////////////////////////////////////////////////////////////////////
// AccEvent public methods
Accessible*
AccEvent::GetAccessible()
{
if (!mAccessible)
mAccessible = GetAccessibleForNode();
return mAccessible;
}
nsINode*
AccEvent::GetNode()
{
if (!mNode && mAccessible)
mNode = mAccessible->GetNode();
return mNode;
}
DocAccessible*
AccEvent::GetDocAccessible()
{
if (mAccessible)
return mAccessible->Document();
nsINode* node = GetNode();
if (node)
return GetAccService()->GetDocAccessible(node->OwnerDoc());
return nullptr;
}
already_AddRefed<nsAccEvent>
AccEvent::CreateXPCOMObject()
{
@ -104,56 +66,23 @@ NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AccEvent, Release)
////////////////////////////////////////////////////////////////////////////////
// AccEvent protected methods
Accessible*
AccEvent::GetAccessibleForNode() const
{
if (mNode) {
DocAccessible* document =
GetAccService()->GetDocAccessible(mNode->OwnerDoc());
if (document)
return document->GetAccessible(mNode);
}
return nullptr;
}
void
AccEvent::CaptureIsFromUserInput(EIsFromUserInput aIsFromUserInput)
{
nsINode *targetNode = GetNode();
#ifdef DEBUG
if (!targetNode) {
// XXX: remove this hack during reorganization of 506907. Meanwhile we
// want to get rid an assertion for application accessible events which
// don't have DOM node (see bug 506206).
if (mAccessible != static_cast<nsIAccessible*>(ApplicationAcc()))
NS_ASSERTION(targetNode, "There should always be a DOM node for an event");
}
#endif
if (aIsFromUserInput != eAutoDetect) {
mIsFromUserInput = aIsFromUserInput == eFromUserInput ? true : false;
return;
}
if (!targetNode)
return;
nsIPresShell *presShell = nsCoreUtils::GetPresShellFor(targetNode);
if (!presShell) {
NS_NOTREACHED("Threre should always be an pres shell for an event");
DocAccessible* document = mAccessible->Document();
if (!document) {
NS_ASSERTION(mAccessible == ApplicationAcc(),
"Accessible other than application should always have a doc!");
return;
}
nsEventStateManager *esm = presShell->GetPresContext()->EventStateManager();
if (!esm) {
NS_NOTREACHED("There should always be an ESM for an event");
return;
}
mIsFromUserInput = esm->IsHandlingUserInputExternal();
mIsFromUserInput =
document->PresContext()->EventStateManager()->IsHandlingUserInputExternal();
}
@ -161,39 +90,6 @@ AccEvent::CaptureIsFromUserInput(EIsFromUserInput aIsFromUserInput)
// AccStateChangeEvent
////////////////////////////////////////////////////////////////////////////////
// Note: we pass in eAllowDupes to the base class because we don't currently
// support correct state change coalescence (XXX Bug 569356). Also we need to
// decide how to coalesce events created via accessible (instead of node).
AccStateChangeEvent::
AccStateChangeEvent(Accessible* aAccessible, uint64_t aState,
bool aIsEnabled, EIsFromUserInput aIsFromUserInput):
AccEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, aAccessible,
aIsFromUserInput, eAllowDupes),
mState(aState), mIsEnabled(aIsEnabled)
{
}
AccStateChangeEvent::
AccStateChangeEvent(nsINode* aNode, uint64_t aState, bool aIsEnabled):
AccEvent(::nsIAccessibleEvent::EVENT_STATE_CHANGE, aNode,
eAutoDetect, eAllowDupes),
mState(aState), mIsEnabled(aIsEnabled)
{
}
AccStateChangeEvent::
AccStateChangeEvent(nsINode* aNode, uint64_t aState) :
AccEvent(::nsIAccessibleEvent::EVENT_STATE_CHANGE, aNode,
eAutoDetect, eAllowDupes),
mState(aState)
{
// Use GetAccessibleForNode() because we do not want to store an accessible
// since it leads to problems with delayed events in the case when
// an accessible gets reorder event before delayed event is processed.
Accessible* accessible = GetAccessibleForNode();
mIsEnabled = accessible && ((accessible->State() & mState) != 0);
}
already_AddRefed<nsAccEvent>
AccStateChangeEvent::CreateXPCOMObject()
{
@ -296,20 +192,6 @@ AccShowEvent::
// AccCaretMoveEvent
////////////////////////////////////////////////////////////////////////////////
AccCaretMoveEvent::
AccCaretMoveEvent(Accessible* aAccessible, int32_t aCaretOffset) :
AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible),
mCaretOffset(aCaretOffset)
{
}
AccCaretMoveEvent::
AccCaretMoveEvent(nsINode* aNode) :
AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aNode),
mCaretOffset(-1)
{
}
already_AddRefed<nsAccEvent>
AccCaretMoveEvent::CreateXPCOMObject()
{

View File

@ -38,17 +38,18 @@ public:
// Rule for accessible events.
// The rule will be applied when flushing pending events.
enum EEventRule {
// eAllowDupes : More than one event of the same type is allowed.
// This event will always be emitted.
eAllowDupes,
// eAllowDupes : More than one event of the same type is allowed.
// This event will always be emitted. This flag is used for events that
// don't support coalescence.
eAllowDupes,
// eCoalesceReorder : For reorder events from the same subtree or the same
// node, only the umbrella event on the ancestor will be emitted.
eCoalesceReorder,
eCoalesceReorder,
// eCoalesceMutationTextChange : coalesce text change events caused by
// tree mutations of the same tree level.
eCoalesceMutationTextChange,
eCoalesceMutationTextChange,
// eCoalesceOfSameType : For events of the same type, only the newest event
// will be processed.
@ -59,20 +60,16 @@ public:
// eRemoveDupes : For repeat events, only the newest event in queue
// will be emitted.
eRemoveDupes,
eRemoveDupes,
// eDoNotEmit : This event is confirmed as a duplicate, do not emit it.
eDoNotEmit
eDoNotEmit
};
// Initialize with an nsIAccessible
AccEvent(uint32_t aEventType, Accessible* aAccessible,
EIsFromUserInput aIsFromUserInput = eAutoDetect,
EEventRule aEventRule = eRemoveDupes);
// Initialize with an nsINode
AccEvent(uint32_t aEventType, nsINode* aNode,
EIsFromUserInput aIsFromUserInput = eAutoDetect,
EEventRule aEventRule = eRemoveDupes);
virtual ~AccEvent() {}
// AccEvent
@ -80,9 +77,8 @@ public:
EEventRule GetEventRule() const { return mEventRule; }
bool IsFromUserInput() const { return mIsFromUserInput; }
Accessible* GetAccessible();
DocAccessible* GetDocAccessible();
nsINode* GetNode();
Accessible* GetAccessible() const { return mAccessible; }
DocAccessible* GetDocAccessible() const { return mAccessible->Document(); }
/**
* Create and return an XPCOM object for accessible event object.
@ -119,10 +115,6 @@ public:
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(AccEvent)
protected:
/**
* Get an accessible from event target node.
*/
Accessible* GetAccessibleForNode() const;
/**
* Determine whether the event is from user input by event state manager if
@ -134,7 +126,6 @@ protected:
uint32_t mEventType;
EEventRule mEventRule;
nsRefPtr<Accessible> mAccessible;
nsCOMPtr<nsINode> mNode;
friend class NotificationController;
friend class AccReorderEvent;
@ -149,11 +140,15 @@ class AccStateChangeEvent: public AccEvent
public:
AccStateChangeEvent(Accessible* aAccessible, uint64_t aState,
bool aIsEnabled,
EIsFromUserInput aIsFromUserInput = eAutoDetect);
EIsFromUserInput aIsFromUserInput = eAutoDetect) :
AccEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, aAccessible,
aIsFromUserInput, eAllowDupes),
mState(aState), mIsEnabled(aIsEnabled) { }
AccStateChangeEvent(nsINode* aNode, uint64_t aState, bool aIsEnabled);
AccStateChangeEvent(nsINode* aNode, uint64_t aState);
AccStateChangeEvent(Accessible* aAccessible, uint64_t aState) :
AccEvent(::nsIAccessibleEvent::EVENT_STATE_CHANGE, aAccessible,
eAutoDetect, eAllowDupes), mState(aState)
{ mIsEnabled = (mAccessible->State() & mState) != 0; }
// AccEvent
virtual already_AddRefed<nsAccEvent> CreateXPCOMObject();
@ -239,6 +234,7 @@ public:
bool IsHide() const { return mEventType == nsIAccessibleEvent::EVENT_HIDE; }
protected:
nsCOMPtr<nsINode> mNode;
nsRefPtr<Accessible> mParent;
nsRefPtr<AccTextChangeEvent> mTextChangeEvent;
@ -350,8 +346,10 @@ protected:
class AccCaretMoveEvent: public AccEvent
{
public:
AccCaretMoveEvent(Accessible* aAccessible, int32_t aCaretOffset);
AccCaretMoveEvent(nsINode* aNode);
AccCaretMoveEvent(Accessible* aAccessible) :
AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible),
mCaretOffset(-1) { }
virtual ~AccCaretMoveEvent() { }
// AccEvent
virtual already_AddRefed<nsAccEvent> CreateXPCOMObject();
@ -367,6 +365,8 @@ public:
private:
int32_t mCaretOffset;
friend class NotificationController;
};

View File

@ -219,7 +219,7 @@ FocusManager::DispatchFocusEvent(DocAccessible* aDocument,
nsRefPtr<AccEvent> event =
new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, aTarget,
eAutoDetect, AccEvent::eCoalesceOfSameType);
aDocument->FireDelayedAccessibleEvent(event);
aDocument->FireDelayedEvent(event);
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eFocus))

View File

@ -471,12 +471,9 @@ logging::DocLoadEventHandled(AccEvent* aEvent)
MsgBegin(sDocEventTitle, "handled '%s' event", strEventType.get());
nsINode* node = aEvent->GetNode();
if (node->IsNodeOfType(nsINode::eDOCUMENT)) {
nsIDocument* documentNode = static_cast<nsIDocument*>(node);
DocAccessible* document = aEvent->GetDocAccessible();
LogDocInfo(documentNode, document);
}
DocAccessible* document = aEvent->GetAccessible()->AsDoc();
if (document)
LogDocInfo(document->DocumentNode(), document);
MsgEnd();
}

View File

@ -385,7 +385,7 @@ NotificationController::CoalesceEvents()
AccEvent* accEvent = mEvents[index];
if (accEvent->mEventType == tailEvent->mEventType &&
accEvent->mEventRule == tailEvent->mEventRule &&
accEvent->mNode == tailEvent->mNode) {
accEvent->mAccessible == tailEvent->mAccessible) {
tailEvent->mEventRule = AccEvent::eDoNotEmit;
return;
}
@ -705,12 +705,11 @@ NotificationController::ProcessEventQueue()
// Dispatch caret moved and text selection change events.
if (event->mEventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED) {
AccCaretMoveEvent* caretMoveEvent = downcast_accEvent(event);
HyperTextAccessible* hyperText = target->AsHyperText();
int32_t caretOffset = -1;
if (hyperText &&
NS_SUCCEEDED(hyperText->GetCaretOffset(&caretOffset))) {
nsRefPtr<AccEvent> caretMoveEvent =
new AccCaretMoveEvent(hyperText, caretOffset);
NS_SUCCEEDED(hyperText->GetCaretOffset(&caretMoveEvent->mCaretOffset))) {
nsEventShell::FireEvent(caretMoveEvent);
// There's a selection so fire selection change as well.

View File

@ -78,7 +78,7 @@ private:
Class* mInstance;
Callback mCallback;
nsCOMPtr<Arg> mArg;
nsRefPtr<Arg> mArg;
};
/**

View File

@ -82,14 +82,14 @@ TextUpdater::DoUpdate(const nsAString& aNewText, const nsAString& aOldText,
// Fire text change event for removal.
nsRefPtr<AccEvent> textRemoveEvent =
new AccTextChangeEvent(mHyperText, mTextOffset, str1, false);
mDocument->FireDelayedAccessibleEvent(textRemoveEvent);
mDocument->FireDelayedEvent(textRemoveEvent);
}
if (strLen2 > 0) {
// Fire text change event for insertion.
nsRefPtr<AccEvent> textInsertEvent =
new AccTextChangeEvent(mHyperText, mTextOffset, str2, true);
mDocument->FireDelayedAccessibleEvent(textInsertEvent);
mDocument->FireDelayedEvent(textInsertEvent);
}
mDocument->MaybeNotifyOfValueChange(mHyperText);
@ -135,7 +135,7 @@ TextUpdater::DoUpdate(const nsAString& aNewText, const nsAString& aOldText,
// Fire events.
for (int32_t idx = events.Length() - 1; idx >= 0; idx--)
mDocument->FireDelayedAccessibleEvent(events[idx]);
mDocument->FireDelayedEvent(events[idx]);
mDocument->MaybeNotifyOfValueChange(mHyperText);

View File

@ -400,9 +400,8 @@ nsAccDocManager::CreateDocOrRootAccessible(nsIDocument* aDocument)
// the same document.
// Note: don't use AccReorderEvent to avoid coalsecense and special reorder
// events processing.
nsRefPtr<AccEvent> reorderEvent =
new AccEvent(nsIAccessibleEvent::EVENT_REORDER, ApplicationAcc());
docAcc->FireDelayedAccessibleEvent(reorderEvent);
docAcc->FireDelayedEvent(nsIAccessibleEvent::EVENT_REORDER,
ApplicationAcc());
} else {
parentDocAcc->BindChildDocument(docAcc);

View File

@ -243,10 +243,8 @@ nsCaretAccessible::NormalSelectionChanged(nsISelection* aSelection)
mLastCaretOffset = caretOffset;
mLastTextAccessible = textAcc;
nsRefPtr<AccEvent> event =
new AccCaretMoveEvent(mLastTextAccessible->GetNode());
if (event)
mLastTextAccessible->Document()->FireDelayedAccessibleEvent(event);
nsRefPtr<AccEvent> event = new AccCaretMoveEvent(mLastTextAccessible);
mLastTextAccessible->Document()->FireDelayedEvent(event);
}
void
@ -258,15 +256,13 @@ nsCaretAccessible::SpellcheckSelectionChanged(nsISelection* aSelection)
// misspelled word). If spellchecking is disabled (for example,
// @spellcheck="false" on html:body) then we won't fire any event.
HyperTextAccessible* textAcc =
HyperTextAccessible* hyperText =
nsAccUtils::GetTextAccessibleFromSelection(aSelection);
if (!textAcc)
return;
nsRefPtr<AccEvent> event =
new AccEvent(nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED, textAcc);
if (event)
textAcc->Document()->FireDelayedAccessibleEvent(event);
if (hyperText) {
hyperText->Document()->
FireDelayedEvent(nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED,
hyperText);
}
}
nsIntRect

View File

@ -22,7 +22,7 @@ nsEventShell::FireEvent(AccEvent* aEvent)
Accessible* accessible = aEvent->GetAccessible();
NS_ENSURE_TRUE_VOID(accessible);
nsINode* node = aEvent->GetNode();
nsINode* node = accessible->GetNode();
if (node) {
sEventTargetNode = node;
sEventFromUserInput = aEvent->IsFromUserInput();

View File

@ -12,9 +12,31 @@
#include "NotificationController.h"
#include "States.h"
#ifdef A11Y_LOG
#include "Logging.h"
#endif
namespace mozilla {
namespace a11y {
inline void
DocAccessible::FireDelayedEvent(AccEvent* aEvent)
{
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eDocLoad))
logging::DocLoadEventFired(aEvent);
#endif
mNotificationController->QueueEvent(aEvent);
}
inline void
DocAccessible::FireDelayedEvent(uint32_t aEventType, Accessible* aTarget)
{
nsRefPtr<AccEvent> event = new AccEvent(aEventType, aTarget);
FireDelayedEvent(event);
}
inline void
DocAccessible::BindChildDocument(DocAccessible* aDocument)
{
@ -54,20 +76,16 @@ DocAccessible::NotifyOfLoad(uint32_t aLoadEventType)
if (HasLoadState(eCompletelyLoaded) && IsLoadEventTarget()) {
nsRefPtr<AccEvent> stateEvent =
new AccStateChangeEvent(this, states::BUSY, false);
FireDelayedAccessibleEvent(stateEvent);
FireDelayedEvent(stateEvent);
}
}
inline void
DocAccessible::MaybeNotifyOfValueChange(Accessible* aAccessible)
{
mozilla::a11y::role role = aAccessible->Role();
if (role == roles::ENTRY || role == roles::COMBOBOX) {
nsRefPtr<AccEvent> valueChangeEvent =
new AccEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible,
eAutoDetect, AccEvent::eRemoveDupes);
FireDelayedAccessibleEvent(valueChangeEvent);
}
a11y::role role = aAccessible->Role();
if (role == roles::ENTRY || role == roles::COMBOBOX)
FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible);
}
} // namespace a11y

View File

@ -44,10 +44,6 @@
#include "mozilla/Assertions.h"
#include "mozilla/dom/Element.h"
#ifdef A11Y_LOG
#include "Logging.h"
#endif
#ifdef MOZ_XUL
#include "nsIXULDocument.h"
#endif
@ -892,7 +888,7 @@ DocAccessible::Observe(nsISupports* aSubject, const char* aTopic,
// about this exceptional case.
nsRefPtr<AccEvent> event =
new AccStateChangeEvent(this, states::EDITABLE, true);
FireDelayedAccessibleEvent(event);
FireDelayedEvent(event);
}
return NS_OK;
@ -987,7 +983,7 @@ DocAccessible::AttributeChanged(nsIDocument* aDocument,
// Fire accessible events iff there's an accessible, otherwise we consider
// the accessible state wasn't changed, i.e. its state is initial state.
AttributeChangedImpl(aElement, aNameSpaceID, aAttribute);
AttributeChangedImpl(accessible, aNameSpaceID, aAttribute);
// Update dependent IDs cache. Take care of accessible elements because no
// accessible element means either the element is not accessible at all or
@ -1002,7 +998,8 @@ DocAccessible::AttributeChanged(nsIDocument* aDocument,
// DocAccessible protected member
void
DocAccessible::AttributeChangedImpl(nsIContent* aContent, int32_t aNameSpaceID, nsIAtom* aAttribute)
DocAccessible::AttributeChangedImpl(Accessible* aAccessible,
int32_t aNameSpaceID, nsIAtom* aAttribute)
{
// Fire accessible event after short timer, because we need to wait for
// DOM attribute & resulting layout to actually change. Otherwise,
@ -1033,14 +1030,12 @@ DocAccessible::AttributeChangedImpl(nsIContent* aContent, int32_t aNameSpaceID,
// ARIA's aria-disabled does not affect the disabled state bit.
nsRefPtr<AccEvent> enabledChangeEvent =
new AccStateChangeEvent(aContent, states::ENABLED);
FireDelayedAccessibleEvent(enabledChangeEvent);
new AccStateChangeEvent(aAccessible, states::ENABLED);
FireDelayedEvent(enabledChangeEvent);
nsRefPtr<AccEvent> sensitiveChangeEvent =
new AccStateChangeEvent(aContent, states::SENSITIVE);
FireDelayedAccessibleEvent(sensitiveChangeEvent);
new AccStateChangeEvent(aAccessible, states::SENSITIVE);
FireDelayedEvent(sensitiveChangeEvent);
return;
}
@ -1049,7 +1044,7 @@ DocAccessible::AttributeChangedImpl(nsIContent* aContent, int32_t aNameSpaceID,
// Check for hyphenated aria-foo property?
if (StringBeginsWith(nsDependentAtomString(aAttribute),
NS_LITERAL_STRING("aria-"))) {
ARIAAttributeChanged(aContent, aAttribute);
ARIAAttributeChanged(aAccessible, aAttribute);
}
}
@ -1057,75 +1052,69 @@ DocAccessible::AttributeChangedImpl(nsIContent* aContent, int32_t aNameSpaceID,
aAttribute == nsGkAtoms::title ||
aAttribute == nsGkAtoms::aria_label ||
aAttribute == nsGkAtoms::aria_labelledby) {
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE,
aContent);
FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
return;
}
if (aAttribute == nsGkAtoms::aria_busy) {
bool isOn = aContent->AttrValueIs(aNameSpaceID, aAttribute,
nsGkAtoms::_true, eCaseMatters);
nsRefPtr<AccEvent> event = new AccStateChangeEvent(aContent, states::BUSY, isOn);
FireDelayedAccessibleEvent(event);
bool isOn = aAccessible->GetContent()->
AttrValueIs(aNameSpaceID, aAttribute, nsGkAtoms::_true, eCaseMatters);
nsRefPtr<AccEvent> event =
new AccStateChangeEvent(aAccessible, states::BUSY, isOn);
FireDelayedEvent(event);
return;
}
// ARIA or XUL selection
if ((aContent->IsXUL() && aAttribute == nsGkAtoms::selected) ||
if ((aAccessible->GetContent()->IsXUL() && aAttribute == nsGkAtoms::selected) ||
aAttribute == nsGkAtoms::aria_selected) {
Accessible* item = GetAccessible(aContent);
if (!item)
return;
Accessible* widget =
nsAccUtils::GetSelectableContainer(item, item->State());
nsAccUtils::GetSelectableContainer(aAccessible, aAccessible->State());
if (widget) {
nsIContent* elm = aAccessible->GetContent();
AccSelChangeEvent::SelChangeType selChangeType =
aContent->AttrValueIs(aNameSpaceID, aAttribute,
nsGkAtoms::_true, eCaseMatters) ?
elm->AttrValueIs(aNameSpaceID, aAttribute, nsGkAtoms::_true, eCaseMatters) ?
AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
nsRefPtr<AccEvent> event =
new AccSelChangeEvent(widget, item, selChangeType);
FireDelayedAccessibleEvent(event);
new AccSelChangeEvent(widget, aAccessible, selChangeType);
FireDelayedEvent(event);
}
return;
}
if (aAttribute == nsGkAtoms::contenteditable) {
nsRefPtr<AccEvent> editableChangeEvent =
new AccStateChangeEvent(aContent, states::EDITABLE);
FireDelayedAccessibleEvent(editableChangeEvent);
new AccStateChangeEvent(aAccessible, states::EDITABLE);
FireDelayedEvent(editableChangeEvent);
return;
}
if (aAttribute == nsGkAtoms::value) {
Accessible* accessible = GetAccessible(aContent);
if(accessible && accessible->IsProgress()) {
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
aContent);
}
if (aAccessible->IsProgress())
FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible);
}
}
// DocAccessible protected member
void
DocAccessible::ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute)
DocAccessible::ARIAAttributeChanged(Accessible* aAccessible, nsIAtom* aAttribute)
{
// Note: For universal/global ARIA states and properties we don't care if
// there is an ARIA role present or not.
if (aAttribute == nsGkAtoms::aria_required) {
nsRefPtr<AccEvent> event =
new AccStateChangeEvent(aContent, states::REQUIRED);
FireDelayedAccessibleEvent(event);
new AccStateChangeEvent(aAccessible, states::REQUIRED);
FireDelayedEvent(event);
return;
}
if (aAttribute == nsGkAtoms::aria_invalid) {
nsRefPtr<AccEvent> event =
new AccStateChangeEvent(aContent, states::INVALID);
FireDelayedAccessibleEvent(event);
new AccStateChangeEvent(aAccessible, states::INVALID);
FireDelayedEvent(event);
return;
}
@ -1133,8 +1122,8 @@ DocAccessible::ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute)
// to the element with the id that activedescendant points to. Make sure
// the tree up to date before processing.
if (aAttribute == nsGkAtoms::aria_activedescendant) {
mNotificationController->HandleNotification<DocAccessible, nsIContent>
(this, &DocAccessible::ARIAActiveDescendantChanged, aContent);
mNotificationController->HandleNotification<DocAccessible, Accessible>
(this, &DocAccessible::ARIAActiveDescendantChanged, aAccessible);
return;
}
@ -1142,8 +1131,8 @@ DocAccessible::ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute)
// We treat aria-expanded as a global ARIA state for historical reasons
if (aAttribute == nsGkAtoms::aria_expanded) {
nsRefPtr<AccEvent> event =
new AccStateChangeEvent(aContent, states::EXPANDED);
FireDelayedAccessibleEvent(event);
new AccStateChangeEvent(aAccessible, states::EXPANDED);
FireDelayedEvent(event);
return;
}
@ -1151,10 +1140,11 @@ DocAccessible::ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute)
// change event; at least until native API comes up with a more meaningful event.
uint8_t attrFlags = nsAccUtils::GetAttributeCharacteristics(aAttribute);
if (!(attrFlags & ATTR_BYPASSOBJ))
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED,
aContent);
FireDelayedEvent(nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED,
aAccessible);
if (!aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) {
nsIContent* elm = aAccessible->GetContent();
if (!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) {
// We don't care about these other ARIA attribute changes unless there is
// an ARIA role set for the element
// XXX: we should check the role map to see if the changed property is
@ -1165,29 +1155,26 @@ DocAccessible::ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute)
// The following ARIA attributes only take affect when dynamic content role is present
if (aAttribute == nsGkAtoms::aria_checked ||
aAttribute == nsGkAtoms::aria_pressed) {
const uint32_t kState = (aAttribute == nsGkAtoms::aria_checked) ?
const uint64_t kState = (aAttribute == nsGkAtoms::aria_checked) ?
states::CHECKED : states::PRESSED;
nsRefPtr<AccEvent> event = new AccStateChangeEvent(aContent, kState);
FireDelayedAccessibleEvent(event);
nsRefPtr<AccEvent> event = new AccStateChangeEvent(aAccessible, kState);
FireDelayedEvent(event);
Accessible* accessible = event->GetAccessible();
if (accessible) {
bool wasMixed = (mARIAAttrOldValue == nsGkAtoms::mixed);
bool isMixed = aContent->AttrValueIs(kNameSpaceID_None, aAttribute,
nsGkAtoms::mixed, eCaseMatters);
if (isMixed != wasMixed) {
nsRefPtr<AccEvent> event =
new AccStateChangeEvent(aContent, states::MIXED, isMixed);
FireDelayedAccessibleEvent(event);
}
bool wasMixed = (mARIAAttrOldValue == nsGkAtoms::mixed);
bool isMixed = elm->AttrValueIs(kNameSpaceID_None, aAttribute,
nsGkAtoms::mixed, eCaseMatters);
if (isMixed != wasMixed) {
nsRefPtr<AccEvent> event =
new AccStateChangeEvent(aAccessible, states::MIXED, isMixed);
FireDelayedEvent(event);
}
return;
}
if (aAttribute == nsGkAtoms::aria_readonly) {
nsRefPtr<AccEvent> event =
new AccStateChangeEvent(aContent, states::READONLY);
FireDelayedAccessibleEvent(event);
new AccStateChangeEvent(aAccessible, states::READONLY);
FireDelayedEvent(event);
return;
}
@ -1195,23 +1182,22 @@ DocAccessible::ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute)
// when aria-valuenow is changed and aria-valuetext is empty
if (aAttribute == nsGkAtoms::aria_valuetext ||
(aAttribute == nsGkAtoms::aria_valuenow &&
(!aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuetext) ||
aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_valuetext,
nsGkAtoms::_empty, eCaseMatters)))) {
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
aContent);
(!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuetext) ||
elm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_valuetext,
nsGkAtoms::_empty, eCaseMatters)))) {
FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible);
return;
}
}
void
DocAccessible::ARIAActiveDescendantChanged(nsIContent* aElm)
DocAccessible::ARIAActiveDescendantChanged(Accessible* aAccessible)
{
Accessible* widget = GetAccessible(aElm);
if (widget && widget->IsActiveWidget()) {
nsIContent* elm = aAccessible->GetContent();
if (elm && aAccessible->IsActiveWidget()) {
nsAutoString id;
if (aElm->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant, id)) {
dom::Element* activeDescendantElm = aElm->OwnerDoc()->GetElementById(id);
if (elm->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant, id)) {
dom::Element* activeDescendantElm = elm->OwnerDoc()->GetElementById(id);
if (activeDescendantElm) {
Accessible* activeDescendant = GetAccessible(activeDescendantElm);
if (activeDescendant) {
@ -1240,31 +1226,32 @@ DocAccessible::ContentStateChanged(nsIDocument* aDocument,
nsIContent* aContent,
nsEventStates aStateMask)
{
Accessible* accessible = GetAccessible(aContent);
if (!accessible)
return;
if (aStateMask.HasState(NS_EVENT_STATE_CHECKED)) {
Accessible* item = GetAccessible(aContent);
if (item) {
Accessible* widget = item->ContainerWidget();
if (widget && widget->IsSelect()) {
AccSelChangeEvent::SelChangeType selChangeType =
aContent->AsElement()->State().HasState(NS_EVENT_STATE_CHECKED) ?
AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
nsRefPtr<AccEvent> event = new AccSelChangeEvent(widget, item,
selChangeType);
FireDelayedAccessibleEvent(event);
}
Accessible* widget = accessible->ContainerWidget();
if (widget && widget->IsSelect()) {
AccSelChangeEvent::SelChangeType selChangeType =
aContent->AsElement()->State().HasState(NS_EVENT_STATE_CHECKED) ?
AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
nsRefPtr<AccEvent> event =
new AccSelChangeEvent(widget, accessible, selChangeType);
FireDelayedEvent(event);
}
}
if (aStateMask.HasState(NS_EVENT_STATE_INVALID)) {
nsRefPtr<AccEvent> event =
new AccStateChangeEvent(aContent, states::INVALID, true);
FireDelayedAccessibleEvent(event);
new AccStateChangeEvent(accessible, states::INVALID, true);
FireDelayedEvent(event);
}
if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
nsRefPtr<AccEvent> event =
new AccStateChangeEvent(aContent, states::TRAVERSED, true);
FireDelayedAccessibleEvent(event);
new AccStateChangeEvent(accessible, states::TRAVERSED, true);
FireDelayedEvent(event);
}
}
@ -1542,8 +1529,8 @@ DocAccessible::NotifyOfLoading(bool aIsReloading)
// Fire state busy change event. Use delayed event since we don't care
// actually if event isn't delivered when the document goes away like a shot.
nsRefPtr<AccEvent> stateEvent =
new AccStateChangeEvent(mDocument, states::BUSY, true);
FireDelayedAccessibleEvent(stateEvent);
new AccStateChangeEvent(this, states::BUSY, true);
FireDelayedEvent(stateEvent);
}
void
@ -1567,7 +1554,7 @@ DocAccessible::DoInitialUpdate()
// a problem then consider to keep event processing per tab document.
if (!IsRoot()) {
nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(Parent());
ParentDocument()->FireDelayedAccessibleEvent(reorderEvent);
ParentDocument()->FireDelayedEvent(reorderEvent);
}
}
@ -1747,36 +1734,6 @@ DocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
return false;
}
// DocAccessible public member
nsresult
DocAccessible::FireDelayedAccessibleEvent(uint32_t aEventType, nsINode* aNode,
AccEvent::EEventRule aAllowDupes,
EIsFromUserInput aIsFromUserInput)
{
nsRefPtr<AccEvent> event =
new AccEvent(aEventType, aNode, aIsFromUserInput, aAllowDupes);
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
return FireDelayedAccessibleEvent(event);
}
// DocAccessible public member
nsresult
DocAccessible::FireDelayedAccessibleEvent(AccEvent* aEvent)
{
NS_ENSURE_ARG(aEvent);
#ifdef A11Y_LOG
if (logging::IsEnabled(logging::eDocLoad))
logging::DocLoadEventFired(aEvent);
#endif
if (mNotificationController)
mNotificationController->QueueEvent(aEvent);
return NS_OK;
}
void
DocAccessible::ProcessContentInserted(Accessible* aContainer,
const nsTArray<nsCOMPtr<nsIContent> >* aInsertedContent)
@ -1874,9 +1831,7 @@ DocAccessible::UpdateTree(Accessible* aContainer, nsIContent* aChildNode,
Accessible* ancestor = aContainer;
while (ancestor) {
if (ancestor->ARIARole() == roles::ALERT) {
nsRefPtr<AccEvent> alertEvent =
new AccEvent(nsIAccessibleEvent::EVENT_ALERT, ancestor);
FireDelayedAccessibleEvent(alertEvent);
FireDelayedEvent(nsIAccessibleEvent::EVENT_ALERT, ancestor);
break;
}
@ -1892,7 +1847,7 @@ DocAccessible::UpdateTree(Accessible* aContainer, nsIContent* aChildNode,
// Fire reorder event so the MSAA clients know the children have changed. Also
// the event is used internally by MSAA layer.
FireDelayedAccessibleEvent(reorderEvent);
FireDelayedEvent(reorderEvent);
}
uint32_t
@ -1916,11 +1871,8 @@ DocAccessible::UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
// 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) {
nsRefPtr<AccEvent> event =
new AccEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END, aChild);
FireDelayedAccessibleEvent(event);
}
if (aChild->ARIARole() == roles::MENUPOPUP)
FireDelayedEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END, aChild);
}
// Fire show/hide event.
@ -1930,23 +1882,19 @@ DocAccessible::UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
else
event = new AccHideEvent(aChild, node);
FireDelayedAccessibleEvent(event);
FireDelayedEvent(event);
aReorderEvent->AddSubMutationEvent(event);
if (aIsInsert) {
roles::Role ariaRole = aChild->ARIARole();
if (ariaRole == roles::MENUPOPUP) {
// Fire EVENT_MENUPOPUP_START if ARIA menu appears.
nsRefPtr<AccEvent> event =
new AccEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START, aChild);
FireDelayedAccessibleEvent(event);
FireDelayedEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START, aChild);
} else if (ariaRole == roles::ALERT) {
// Fire EVENT_ALERT if ARIA alert appears.
updateFlags = eAlertAccessible;
nsRefPtr<AccEvent> event =
new AccEvent(nsIAccessibleEvent::EVENT_ALERT, aChild);
FireDelayedAccessibleEvent(event);
FireDelayedEvent(nsIAccessibleEvent::EVENT_ALERT, aChild);
}
// If focused node has been shown then it means its frame was recreated
@ -1996,8 +1944,7 @@ DocAccessible::CacheChildrenInSubtree(Accessible* aRoot)
if (aRoot->HasARIARole() && !aRoot->IsDoc()) {
a11y::role role = aRoot->ARIARole();
if (role == roles::DIALOG || role == roles::DOCUMENT)
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE,
aRoot->GetContent());
FireDelayedEvent(nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE, aRoot);
}
}

View File

@ -173,22 +173,10 @@ public:
{ return mChildDocuments.SafeElementAt(aIndex, nullptr); }
/**
* Non-virtual method to fire a delayed event after a 0 length timeout.
*
* @param aEventType [in] the nsIAccessibleEvent event type
* @param aDOMNode [in] DOM node the accesible event should be fired for
* @param aAllowDupes [in] rule to process an event (see EEventRule constants)
* Fire accessible event asynchronously.
*/
nsresult FireDelayedAccessibleEvent(uint32_t aEventType, nsINode *aNode,
AccEvent::EEventRule aAllowDupes = AccEvent::eRemoveDupes,
EIsFromUserInput aIsFromUserInput = eAutoDetect);
/**
* Fire accessible event after timeout.
*
* @param aEvent [in] the event to fire
*/
nsresult FireDelayedAccessibleEvent(AccEvent* aEvent);
void FireDelayedEvent(AccEvent* aEvent);
void FireDelayedEvent(uint32_t aEventType, Accessible* aTarget);
/**
* Fire value change event on the given accessible if applicable.
@ -398,27 +386,28 @@ protected:
bool UpdateAccessibleOnAttrChange(mozilla::dom::Element* aElement,
nsIAtom* aAttribute);
/**
* Fires accessible events when attribute is changed.
*
* @param aContent - node that attribute is changed for
* @param aNameSpaceID - namespace of changed attribute
* @param aAttribute - changed attribute
*/
void AttributeChangedImpl(nsIContent* aContent, int32_t aNameSpaceID, nsIAtom* aAttribute);
/**
* Fire accessible events when attribute is changed.
*
* @param aAccessible [in] accessible the DOM attribute is changed for
* @param aNameSpaceID [in] namespace of changed attribute
* @param aAttribute [in] changed attribute
*/
void AttributeChangedImpl(Accessible* aAccessible,
int32_t aNameSpaceID, nsIAtom* aAttribute);
/**
* Fires accessible events when ARIA attribute is changed.
*
* @param aContent - node that attribute is changed for
* @param aAttribute - changed attribute
*/
void ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute);
/**
* Fire accessible events when ARIA attribute is changed.
*
* @param aAccessible [in] accesislbe the DOM attribute is changed for
* @param aAttribute [in] changed attribute
*/
void ARIAAttributeChanged(Accessible* aAccessible, nsIAtom* aAttribute);
/**
* Process ARIA active-descendant attribute change.
*/
void ARIAActiveDescendantChanged(nsIContent* aElm);
void ARIAActiveDescendantChanged(Accessible* aAccessible);
/**
* Update the accessible tree for inserted content.

View File

@ -311,8 +311,6 @@ RootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
if (!accessible)
return;
nsINode* targetNode = accessible->GetNode();
#ifdef MOZ_XUL
XULTreeAccessible* treeAcc = accessible->AsXULTree();
if (treeAcc) {
@ -383,6 +381,7 @@ RootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
return;
}
nsINode* targetNode = accessible->GetNode();
if (treeItemAcc && eventType.EqualsLiteral("select")) {
// XXX: We shouldn't be based on DOM select event which doesn't provide us
// any context info. We should integrate into nsTreeSelection instead.
@ -478,10 +477,10 @@ RootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
//We don't process 'ValueChange' events for progress meters since we listen
//@value attribute change for them.
if (!accessible->IsProgress())
targetDocument->
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
targetNode);
if (!accessible->IsProgress()) {
targetDocument->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
accessible);
}
}
#ifdef DEBUG_DRAGDROPSTART
else if (eventType.EqualsLiteral("mouseover")) {
@ -677,7 +676,7 @@ RootAccessible::HandlePopupHidingEvent(nsINode* aPopupNode)
if (notifyOf & kNotifyOfState) {
nsRefPtr<AccEvent> event =
new AccStateChangeEvent(widget, states::EXPANDED, false);
document->FireDelayedAccessibleEvent(event);
document->FireDelayedEvent(event);
}
}

View File

@ -7,7 +7,7 @@
#include "nsAccUtils.h"
#include "nsARIAMap.h"
#include "DocAccessible.h"
#include "DocAccessible-inl.h"
#include "Role.h"
#include "nsIDOMHTMLCollection.h"
@ -95,7 +95,7 @@ HTMLImageMapAccessible::UpdateChildAreas(bool aDoFireEvents)
if (aDoFireEvents) {
nsRefPtr<AccHideEvent> event = new AccHideEvent(area, area->GetContent());
mDoc->FireDelayedAccessibleEvent(event);
mDoc->FireDelayedEvent(event);
reorderEvent->AddSubMutationEvent(event);
doReorderEvent = true;
}
@ -121,7 +121,7 @@ HTMLImageMapAccessible::UpdateChildAreas(bool aDoFireEvents)
if (aDoFireEvents) {
nsRefPtr<AccShowEvent> event = new AccShowEvent(area, areaContent);
mDoc->FireDelayedAccessibleEvent(event);
mDoc->FireDelayedEvent(event);
reorderEvent->AddSubMutationEvent(event);
doReorderEvent = true;
}
@ -130,7 +130,7 @@ HTMLImageMapAccessible::UpdateChildAreas(bool aDoFireEvents)
// Fire reorder event if needed.
if (doReorderEvent)
mDoc->FireDelayedAccessibleEvent(reorderEvent);
mDoc->FireDelayedEvent(reorderEvent);
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -45,7 +45,7 @@ nsAccEvent::GetDOMNode(nsIDOMNode** aDOMNode)
NS_ENSURE_ARG_POINTER(aDOMNode);
*aDOMNode = nullptr;
nsINode* node = mEvent->GetNode();
nsINode* node = mEvent->GetAccessible()->GetNode();
if (node)
CallQueryInterface(node, aDOMNode);

View File

@ -6,6 +6,7 @@
#include "XULTreeAccessible.h"
#include "DocAccessible-inl.h"
#include "nsAccCache.h"
#include "nsAccUtils.h"
#include "nsCoreUtils.h"
@ -663,7 +664,7 @@ XULTreeAccessible::TreeViewChanged(nsITreeView* aView)
// show/hide events on tree items because it can be expensive to fire them for
// each tree item.
nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(this);
Document()->FireDelayedAccessibleEvent(reorderEvent);
Document()->FireDelayedEvent(reorderEvent);
// Clear cache.
ClearCache(mAccessibleCache);

View File

@ -58,7 +58,7 @@
if (!MAC) { // Mac failure is bug 541093
var checker =
new eventFromInputChecker(EVENT_TEXT_CARET_MOVED, id, "false", noTargetId);
new eventFromInputChecker(EVENT_TEXT_CARET_MOVED, id, "true", noTargetId);
gQueue.push(new synthHomeKey(id, checker));
}