Bug 570275 - rework accessible tree update code, r=marcoz, davidb, bz, a=blocking

This commit is contained in:
Alexander Surkov 2010-10-21 13:16:10 +09:00
parent ef94abe288
commit b049d3e67a
58 changed files with 1531 additions and 1410 deletions

View File

@ -140,27 +140,19 @@ public:
virtual void RemoveNativeRootAccessible(nsAccessible* aRootAccessible) = 0;
/**
* Used to describe sort of changes leading to accessible tree invalidation.
* Notification used to update the accessible tree when new content is
* inserted.
*/
enum {
NODE_APPEND = 0x01,
NODE_REMOVE = 0x02,
NODE_SIGNIFICANT_CHANGE = 0x03,
FRAME_SHOW = 0x04,
FRAME_HIDE = 0x05,
FRAME_SIGNIFICANT_CHANGE = 0x06
};
virtual void ContentRangeInserted(nsIPresShell* aPresShell,
nsIContent* aContainer,
nsIContent* aStartChild,
nsIContent* aEndChild) = 0;
/**
* Invalidate the accessible tree when DOM tree or frame tree is changed.
*
* @param aPresShell [in] the presShell where changes occurred
* @param aContent [in] the affected DOM content
* @param aChangeType [in] the change type (see constants declared above)
* Notification used to update the accessible tree when content is removed.
*/
virtual nsresult InvalidateSubtreeFor(nsIPresShell *aPresShell,
nsIContent *aContent,
PRUint32 aChangeType) = 0;
virtual void ContentRemoved(nsIPresShell* aPresShell, nsIContent* aContainer,
nsIContent* aChild) = 0;
/**
* Notify accessibility that anchor jump has been accomplished to the given
@ -174,6 +166,12 @@ public:
*/
virtual void PresShellDestroyed(nsIPresShell *aPresShell) = 0;
/**
* Recreate an accessible for the given content node in the presshell.
*/
virtual void RecreateAccessible(nsIPresShell* aPresShell,
nsIContent* aContent) = 0;
/**
* Fire accessible event of the given type for the given target.
*

View File

@ -64,19 +64,15 @@
// AccEvent constructors
AccEvent::AccEvent(PRUint32 aEventType, nsAccessible* aAccessible,
PRBool aIsAsync, EIsFromUserInput aIsFromUserInput,
EEventRule aEventRule) :
mEventType(aEventType), mEventRule(aEventRule), mIsAsync(aIsAsync),
mAccessible(aAccessible)
EIsFromUserInput aIsFromUserInput, EEventRule aEventRule) :
mEventType(aEventType), mEventRule(aEventRule), mAccessible(aAccessible)
{
CaptureIsFromUserInput(aIsFromUserInput);
}
AccEvent::AccEvent(PRUint32 aEventType, nsINode* aNode,
PRBool aIsAsync, EIsFromUserInput aIsFromUserInput,
EEventRule aEventRule) :
mEventType(aEventType), mEventRule(aEventRule), mIsAsync(aIsAsync),
mNode(aNode)
EIsFromUserInput aIsFromUserInput, EEventRule aEventRule) :
mEventType(aEventType), mEventRule(aEventRule), mNode(aNode)
{
CaptureIsFromUserInput(aIsFromUserInput);
}
@ -217,36 +213,6 @@ AccEvent::CaptureIsFromUserInput(EIsFromUserInput aIsFromUserInput)
}
////////////////////////////////////////////////////////////////////////////////
// AccReorderEvent
////////////////////////////////////////////////////////////////////////////////
AccReorderEvent::
AccReorderEvent(nsAccessible* aAccTarget, PRBool aIsAsynch,
PRBool aIsUnconditional, nsINode* aReasonNode) :
AccEvent(::nsIAccessibleEvent::EVENT_REORDER, aAccTarget,
aIsAsynch, eAutoDetect, AccEvent::eCoalesceFromSameSubtree),
mUnconditionalEvent(aIsUnconditional), mReasonNode(aReasonNode)
{
}
PRBool
AccReorderEvent::IsUnconditionalEvent()
{
return mUnconditionalEvent;
}
PRBool
AccReorderEvent::HasAccessibleInReasonSubtree()
{
if (!mReasonNode)
return PR_FALSE;
nsAccessible *accessible = GetAccService()->GetAccessible(mReasonNode);
return accessible || nsAccUtils::HasAccessibleChildren(mReasonNode);
}
////////////////////////////////////////////////////////////////////////////////
// AccStateChangeEvent
////////////////////////////////////////////////////////////////////////////////
@ -257,9 +223,8 @@ AccReorderEvent::HasAccessibleInReasonSubtree()
AccStateChangeEvent::
AccStateChangeEvent(nsAccessible* aAccessible,
PRUint32 aState, PRBool aIsExtraState,
PRBool aIsEnabled, PRBool aIsAsynch,
EIsFromUserInput aIsFromUserInput):
AccEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, aAccessible, aIsAsynch,
PRBool aIsEnabled, EIsFromUserInput aIsFromUserInput):
AccEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, aAccessible,
aIsFromUserInput, eAllowDupes),
mState(aState), mIsExtraState(aIsExtraState), mIsEnabled(aIsEnabled)
{
@ -315,11 +280,11 @@ AccStateChangeEvent::CreateXPCOMObject()
AccTextChangeEvent::
AccTextChangeEvent(nsAccessible* aAccessible, PRInt32 aStart,
nsAString& aModifiedText, PRBool aIsInserted,
PRBool aIsAsynch, EIsFromUserInput aIsFromUserInput)
EIsFromUserInput aIsFromUserInput)
: AccEvent(aIsInserted ?
static_cast<PRUint32>(nsIAccessibleEvent::EVENT_TEXT_INSERTED) :
static_cast<PRUint32>(nsIAccessibleEvent::EVENT_TEXT_REMOVED),
aAccessible, aIsAsynch, aIsFromUserInput, eAllowDupes)
aAccessible, aIsFromUserInput, eAllowDupes)
, mStart(aStart)
, mIsInserted(aIsInserted)
, mModifiedText(aModifiedText)
@ -335,37 +300,62 @@ AccTextChangeEvent::CreateXPCOMObject()
}
////////////////////////////////////////////////////////////////////////////////
// AccMutationEvent
////////////////////////////////////////////////////////////////////////////////
AccMutationEvent::
AccMutationEvent(PRUint32 aEventType, nsAccessible* aTarget,
nsINode* aTargetNode, EIsFromUserInput aIsFromUserInput) :
AccEvent(aEventType, aTarget, aIsFromUserInput, eCoalesceFromSameSubtree)
{
mNode = aTargetNode;
}
////////////////////////////////////////////////////////////////////////////////
// AccHideEvent
////////////////////////////////////////////////////////////////////////////////
AccHideEvent::
AccHideEvent(nsAccessible* aTarget, nsINode* aTargetNode,
PRBool aIsAsynch, EIsFromUserInput aIsFromUserInput) :
AccEvent(nsIAccessibleEvent::EVENT_HIDE, aTarget, aIsAsynch,
aIsFromUserInput, eCoalesceFromSameSubtree)
EIsFromUserInput aIsFromUserInput) :
AccMutationEvent(::nsIAccessibleEvent::EVENT_HIDE, aTarget, aTargetNode,
aIsFromUserInput)
{
mNode = aTargetNode;
mParent = mAccessible->GetCachedParent();
mNextSibling = mAccessible->GetCachedNextSibling();
mPrevSibling = mAccessible->GetCachedPrevSibling();
}
////////////////////////////////////////////////////////////////////////////////
// AccShowEvent
////////////////////////////////////////////////////////////////////////////////
AccShowEvent::
AccShowEvent(nsAccessible* aTarget, nsINode* aTargetNode,
EIsFromUserInput aIsFromUserInput) :
AccMutationEvent(::nsIAccessibleEvent::EVENT_SHOW, aTarget, aTargetNode,
aIsFromUserInput)
{
}
////////////////////////////////////////////////////////////////////////////////
// AccCaretMoveEvent
////////////////////////////////////////////////////////////////////////////////
AccCaretMoveEvent::
AccCaretMoveEvent(nsAccessible* aAccessible, PRInt32 aCaretOffset) :
AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible, PR_TRUE), // Currently always asynch
AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible),
mCaretOffset(aCaretOffset)
{
}
AccCaretMoveEvent::
AccCaretMoveEvent(nsINode* aNode) :
AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aNode, PR_TRUE), // Currently always asynch
AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aNode),
mCaretOffset(-1)
{
}
@ -385,9 +375,8 @@ AccCaretMoveEvent::CreateXPCOMObject()
AccTableChangeEvent::
AccTableChangeEvent(nsAccessible* aAccessible, PRUint32 aEventType,
PRInt32 aRowOrColIndex, PRInt32 aNumRowsOrCols,
PRBool aIsAsynch) :
AccEvent(aEventType, aAccessible, aIsAsynch),
PRInt32 aRowOrColIndex, PRInt32 aNumRowsOrCols) :
AccEvent(aEventType, aAccessible),
mRowOrColIndex(aRowOrColIndex), mNumRowsOrCols(aNumRowsOrCols)
{
}

View File

@ -41,6 +41,8 @@
#ifndef _AccEvent_H_
#define _AccEvent_H_
#include "nsIAccessibleEvent.h"
#include "nsAccessible.h"
class nsAccEvent;
@ -90,11 +92,10 @@ public:
// Initialize with an nsIAccessible
AccEvent(PRUint32 aEventType, nsAccessible* aAccessible,
PRBool aIsAsynch = PR_FALSE,
EIsFromUserInput aIsFromUserInput = eAutoDetect,
EEventRule aEventRule = eRemoveDupes);
// Initialize with an nsIDOMNode
AccEvent(PRUint32 aEventType, nsINode* aNode, PRBool aIsAsynch = PR_FALSE,
AccEvent(PRUint32 aEventType, nsINode* aNode,
EIsFromUserInput aIsFromUserInput = eAutoDetect,
EEventRule aEventRule = eRemoveDupes);
virtual ~AccEvent() {}
@ -102,7 +103,6 @@ public:
// AccEvent
PRUint32 GetEventType() const { return mEventType; }
EEventRule GetEventRule() const { return mEventRule; }
PRBool IsAsync() const { return mIsAsync; }
PRBool IsFromUserInput() const { return mIsFromUserInput; }
nsAccessible *GetAccessible();
@ -119,10 +119,11 @@ public:
*/
enum EventGroup {
eGenericEvent,
eReorderEvent,
eStateChangeEvent,
eTextChangeEvent,
eMutationEvent,
eHideEvent,
eShowEvent,
eCaretMoveEvent,
eTableChangeEvent
};
@ -154,7 +155,6 @@ protected:
PRBool mIsFromUserInput;
PRUint32 mEventType;
EEventRule mEventRule;
PRPackedBool mIsAsync;
nsRefPtr<nsAccessible> mAccessible;
nsCOMPtr<nsINode> mNode;
@ -162,39 +162,6 @@ protected:
};
/**
* Accessible reorder event.
*/
class AccReorderEvent : public AccEvent
{
public:
AccReorderEvent(nsAccessible* aAccTarget, PRBool aIsAsynch,
PRBool aIsUnconditional, nsINode* aReasonNode);
// AccEvent
static const EventGroup kEventGroup = eReorderEvent;
virtual unsigned int GetEventGroups() const
{
return AccEvent::GetEventGroups() | (1U << eReorderEvent);
}
// AccReorderEvent
/**
* Return true if event is unconditional, i.e. must be fired.
*/
PRBool IsUnconditionalEvent();
/**
* Return true if changed DOM node has accessible in its tree.
*/
PRBool HasAccessibleInReasonSubtree();
private:
PRBool mUnconditionalEvent;
nsCOMPtr<nsINode> mReasonNode;
};
/**
* Accessible state change event.
*/
@ -203,7 +170,7 @@ class AccStateChangeEvent: public AccEvent
public:
AccStateChangeEvent(nsAccessible* aAccessible,
PRUint32 aState, PRBool aIsExtraState,
PRBool aIsEnabled, PRBool aIsAsynch = PR_FALSE,
PRBool aIsEnabled,
EIsFromUserInput aIsFromUserInput = eAutoDetect);
AccStateChangeEvent(nsINode* aNode, PRUint32 aState, PRBool aIsExtraState,
@ -239,8 +206,7 @@ class AccTextChangeEvent: public AccEvent
{
public:
AccTextChangeEvent(nsAccessible* aAccessible, PRInt32 aStart,
nsAString& aModifiedText,
PRBool aIsInserted, PRBool aIsAsynch = PR_FALSE,
nsAString& aModifiedText, PRBool aIsInserted,
EIsFromUserInput aIsFromUserInput = eAutoDetect);
// AccEvent
@ -269,31 +235,75 @@ private:
/**
* Accessible hide events.
* Base class for show and hide accessible events.
*/
class AccHideEvent : public AccEvent
class AccMutationEvent: public AccEvent
{
public:
AccMutationEvent(PRUint32 aEventType, nsAccessible* aTarget,
nsINode* aTargetNode, EIsFromUserInput aIsFromUserInput);
// Event
static const EventGroup kEventGroup = eMutationEvent;
virtual unsigned int GetEventGroups() const
{
return AccEvent::GetEventGroups() | (1U << eMutationEvent);
}
// MutationEvent
bool IsShow() const { return mEventType == nsIAccessibleEvent::EVENT_SHOW; }
bool IsHide() const { return mEventType == nsIAccessibleEvent::EVENT_HIDE; }
protected:
nsRefPtr<AccTextChangeEvent> mTextChangeEvent;
friend class nsAccEventQueue;
};
/**
* Accessible hide event.
*/
class AccHideEvent: public AccMutationEvent
{
public:
AccHideEvent(nsAccessible* aTarget, nsINode* aTargetNode,
PRBool aIsAsynch, EIsFromUserInput aIsFromUserInput);
EIsFromUserInput aIsFromUserInput);
// Event
static const EventGroup kEventGroup = eHideEvent;
virtual unsigned int GetEventGroups() const
{
return AccEvent::GetEventGroups() | (1U << eHideEvent);
return AccMutationEvent::GetEventGroups() | (1U << eHideEvent);
}
protected:
nsRefPtr<nsAccessible> mParent;
nsRefPtr<nsAccessible> mNextSibling;
nsRefPtr<nsAccessible> mPrevSibling;
nsRefPtr<AccTextChangeEvent> mTextChangeEvent;
friend class nsAccEventQueue;
};
/**
* Accessible show event.
*/
class AccShowEvent: public AccMutationEvent
{
public:
AccShowEvent(nsAccessible* aTarget, nsINode* aTargetNode,
EIsFromUserInput aIsFromUserInput);
// Event
static const EventGroup kEventGroup = eShowEvent;
virtual unsigned int GetEventGroups() const
{
return AccMutationEvent::GetEventGroups() | (1U << eShowEvent);
}
};
/**
* Accessible caret move event.
*/
@ -327,8 +337,7 @@ class AccTableChangeEvent : public AccEvent
{
public:
AccTableChangeEvent(nsAccessible* aAccessible, PRUint32 aEventType,
PRInt32 aRowOrColIndex, PRInt32 aNumRowsOrCols,
PRBool aIsAsynch);
PRInt32 aRowOrColIndex, PRInt32 aNumRowsOrCols);
// AccEvent
virtual already_AddRefed<nsAccEvent> CreateXPCOMObject();

View File

@ -72,8 +72,7 @@ nsAccDocManager::GetDocAccessible(nsIDocument *aDocument)
// Ensure CacheChildren is called before we query cache.
nsAccessNode::GetApplicationAccessible()->EnsureChildren();
nsDocAccessible *docAcc =
mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument));
nsDocAccessible* docAcc = mDocAccessibleCache.GetWeak(aDocument);
if (docAcc)
return docAcc;
@ -81,10 +80,10 @@ nsAccDocManager::GetDocAccessible(nsIDocument *aDocument)
}
nsAccessible*
nsAccDocManager::FindAccessibleInCache(void *aUniqueID) const
nsAccDocManager::FindAccessibleInCache(nsINode* aNode) const
{
nsSearchAccessibleInCacheArg arg;
arg.mUniqueID = aUniqueID;
arg.mNode = aNode;
mDocAccessibleCache.EnumerateRead(SearchAccessibleInDocCache,
static_cast<void*>(&arg));
@ -136,8 +135,7 @@ nsAccDocManager::Shutdown()
void
nsAccDocManager::ShutdownDocAccessible(nsIDocument *aDocument)
{
nsDocAccessible* docAccessible =
mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument));
nsDocAccessible* docAccessible = mDocAccessibleCache.GetWeak(aDocument);
if (!docAccessible)
return;
@ -146,7 +144,7 @@ nsAccDocManager::ShutdownDocAccessible(nsIDocument *aDocument)
// are removed automatically when chrome event target goes away.
docAccessible->Shutdown();
mDocAccessibleCache.Remove(static_cast<void*>(aDocument));
mDocAccessibleCache.Remove(aDocument);
}
////////////////////////////////////////////////////////////////////////////////
@ -215,8 +213,7 @@ nsAccDocManager::OnStateChange(nsIWebProgress *aWebProgress,
if (!IsEventTargetDocument(document))
return NS_OK;
nsDocAccessible *docAcc =
mDocAccessibleCache.GetWeak(static_cast<void*>(document));
nsDocAccessible* docAcc = mDocAccessibleCache.GetWeak(document);
if (!docAcc)
return NS_OK;
@ -346,9 +343,7 @@ nsAccDocManager::HandleDOMDocumentLoad(nsIDocument *aDocument,
{
// Document accessible can be created before we were notified the DOM document
// was loaded completely. However if it's not created yet then create it.
nsDocAccessible *docAcc =
mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument));
nsDocAccessible* docAcc = mDocAccessibleCache.GetWeak(aDocument);
if (!docAcc) {
docAcc = CreateDocOrRootAccessible(aDocument);
NS_ASSERTION(docAcc, "Can't create document accessible!");
@ -365,16 +360,8 @@ nsAccDocManager::HandleDOMDocumentLoad(nsIDocument *aDocument,
// documents
// b) document load event on sub documents causes screen readers to act is if
// entire page is reloaded.
if (!IsEventTargetDocument(aDocument)) {
// XXX: AT doesn't update their virtual buffer once frame is loaded and it
// has dynamic content added after frame load. There's something wrong how
// we handle this changes.
if (!nsCoreUtils::IsRootDocument(aDocument)) {
docAcc->InvalidateCacheSubtree(nsnull,
nsIAccessibilityService::NODE_SIGNIFICANT_CHANGE);
}
if (!IsEventTargetDocument(aDocument))
return;
}
// Fire complete/load stopped if the load event type is given.
if (aLoadEventType) {
@ -493,7 +480,7 @@ nsAccDocManager::CreateDocOrRootAccessible(nsIDocument *aDocument)
return nsnull;
// Cache and addref document accessible.
if (!mDocAccessibleCache.Put(static_cast<void*>(aDocument), docAcc)) {
if (!mDocAccessibleCache.Put(aDocument, docAcc)) {
delete docAcc;
return nsnull;
}
@ -503,7 +490,7 @@ nsAccDocManager::CreateDocOrRootAccessible(nsIDocument *aDocument)
// while initialized.
if (!outerDocAcc->AppendChild(docAcc) ||
!GetAccService()->InitAccessible(docAcc, nsAccUtils::GetRoleMapEntry(aDocument))) {
mDocAccessibleCache.Remove(static_cast<void*>(aDocument));
mDocAccessibleCache.Remove(aDocument);
return nsnull;
}
@ -546,7 +533,7 @@ nsAccDocManager::ShutdownDocAccessiblesInTree(nsIDocShellTreeItem *aTreeItem,
// nsAccDocManager static
PLDHashOperator
nsAccDocManager::ClearDocCacheEntry(const void* aKey,
nsAccDocManager::ClearDocCacheEntry(const nsIDocument* aKey,
nsRefPtr<nsDocAccessible>& aDocAccessible,
void* aUserArg)
{
@ -560,7 +547,7 @@ nsAccDocManager::ClearDocCacheEntry(const void* aKey,
}
PLDHashOperator
nsAccDocManager::SearchAccessibleInDocCache(const void* aKey,
nsAccDocManager::SearchAccessibleInDocCache(const nsIDocument* aKey,
nsDocAccessible* aDocAccessible,
void* aUserArg)
{
@ -570,7 +557,7 @@ nsAccDocManager::SearchAccessibleInDocCache(const void* aKey,
if (aDocAccessible) {
nsSearchAccessibleInCacheArg* arg =
static_cast<nsSearchAccessibleInCacheArg*>(aUserArg);
arg->mAccessible = aDocAccessible->GetCachedAccessible(arg->mUniqueID);
arg->mAccessible = aDocAccessible->GetCachedAccessible(arg->mNode);
if (arg->mAccessible)
return PL_DHASH_STOP;
}

View File

@ -73,7 +73,7 @@ public:
* Search through all document accessibles for an accessible with the given
* unique id.
*/
nsAccessible *FindAccessibleInCache(void *aUniqueID) const;
nsAccessible* FindAccessibleInCache(nsINode* aNode) const;
/**
* Shutdown document accessibles in the tree starting from the given one.
@ -87,7 +87,7 @@ public:
*/
inline nsDocAccessible* GetDocAccessibleFromCache(nsIDocument* aDocument) const
{
return mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument));
return mDocAccessibleCache.GetWeak(aDocument);
}
protected:
@ -162,14 +162,14 @@ private:
void ShutdownDocAccessiblesInTree(nsIDocShellTreeItem *aTreeItem,
nsIDocument *aDocument);
typedef nsRefPtrHashtable<nsVoidPtrHashKey, nsDocAccessible>
typedef nsRefPtrHashtable<nsPtrHashKey<const nsIDocument>, nsDocAccessible>
nsDocAccessibleHashtable;
/**
* Shutdown and remove the document accessible from cache.
*/
static PLDHashOperator
ClearDocCacheEntry(const void* aKey,
ClearDocCacheEntry(const nsIDocument* aKey,
nsRefPtr<nsDocAccessible>& aDocAccessible,
void* aUserArg);
@ -184,11 +184,11 @@ private:
struct nsSearchAccessibleInCacheArg
{
nsAccessible *mAccessible;
void *mUniqueID;
nsINode* mNode;
};
static PLDHashOperator
SearchAccessibleInDocCache(const void* aKey,
SearchAccessibleInDocCache(const nsIDocument* aKey,
nsDocAccessible* aDocAccessible,
void* aUserArg);

View File

@ -152,9 +152,12 @@ nsAccessNode::Shutdown()
}
// nsIAccessNode
NS_IMETHODIMP nsAccessNode::GetUniqueID(void **aUniqueID)
NS_IMETHODIMP
nsAccessNode::GetUniqueID(void **aUniqueID)
{
*aUniqueID = static_cast<void*>(GetNode());
NS_ENSURE_ARG_POINTER(aUniqueID);
*aUniqueID = UniqueID();
return NS_OK;
}
@ -310,6 +313,12 @@ nsAccessNode::GetFrame()
return mContent ? mContent->GetPrimaryFrame() : nsnull;
}
bool
nsAccessNode::IsPrimaryForNode() const
{
return true;
}
////////////////////////////////////////////////////////////////////////////////
// nsIAccessNode

View File

@ -181,6 +181,20 @@ public:
*/
nsIWeakReference* GetWeakShell() const { return mWeakShell; }
/**
* Return the unique identifier of the accessible.
*/
void* UniqueID() { return static_cast<void*>(this); }
/**
* Return true if the accessible is primary accessible for the given DOM node.
*
* Accessible hierarchy may be complex for single DOM node, in this case
* these accessibles share the same DOM node. The primary accessible "owns"
* that DOM node in terms it gets stored in the accessible to node map.
*/
virtual bool IsPrimaryForNode() const;
protected:
nsPresContext* GetPresContext();

View File

@ -74,6 +74,7 @@
#include "nsRootAccessibleWrap.h"
#include "nsTextFragment.h"
#include "mozilla/Services.h"
#include "nsIEventStateManager.h"
#ifdef MOZ_XUL
#include "nsXULAlertAccessible.h"
@ -470,6 +471,62 @@ nsAccessibilityService::CreateHTMLCaptionAccessible(nsIContent* aContent,
return accessible;
}
void
nsAccessibilityService::ContentRangeInserted(nsIPresShell* aPresShell,
nsIContent* aContainer,
nsIContent* aStartChild,
nsIContent* aEndChild)
{
#ifdef DEBUG_A11Y
nsAutoString tag;
aStartChild->Tag()->ToString(tag);
nsIAtom* id = aStartChild->GetID();
nsCAutoString strid;
if (id)
id->ToUTF8String(strid);
nsAutoString ctag;
aContainer->Tag()->ToString(ctag);
nsIAtom* cid = aContainer->GetID();
nsCAutoString strcid;
if (cid)
cid->ToUTF8String(strcid);
printf("\ncontent inserted: %s@id='%s', container: %s@id='%s', end node: %p\n\n",
NS_ConvertUTF16toUTF8(tag).get(), strid.get(),
NS_ConvertUTF16toUTF8(ctag).get(), strcid.get(), aEndChild);
#endif
// XXX: bug 606082. aContainer is null when root element is inserted into
// document, we need to handle this and update the tree, also we need to
// update a content node of the document accessible.
if (aContainer) {
nsDocAccessible* docAccessible = GetDocAccessible(aPresShell->GetDocument());
if (docAccessible)
docAccessible->UpdateTree(aContainer, aStartChild, aEndChild, PR_TRUE);
}
}
void
nsAccessibilityService::ContentRemoved(nsIPresShell* aPresShell,
nsIContent* aContainer,
nsIContent* aChild)
{
#ifdef DEBUG_A11Y
nsAutoString id;
aChild->Tag()->ToString(id);
printf("\ncontent removed: %s\n", NS_ConvertUTF16toUTF8(id).get());
#endif
// XXX: bug 606082. aContainer is null when root element is inserted into
// document, we need to handle this and update the tree, perhaps destroy
// the document accessible.
if (aContainer) {
nsDocAccessible* docAccessible = GetDocAccessible(aPresShell->GetDocument());
if (docAccessible)
docAccessible->UpdateTree(aContainer, aChild, aChild->GetNextSibling(),
PR_FALSE);
}
}
void
nsAccessibilityService::PresShellDestroyed(nsIPresShell *aPresShell)
{
@ -489,14 +546,22 @@ nsAccessibilityService::PresShellDestroyed(nsIPresShell *aPresShell)
ShutdownDocAccessible(doc);
}
void
nsAccessibilityService::RecreateAccessible(nsIPresShell* aPresShell,
nsIContent* aContent)
{
nsDocAccessible* document = GetDocAccessible(aPresShell->GetDocument());
if (document)
document->RecreateAccessible(aContent);
}
// nsAccessibilityService protected
nsAccessible *
nsAccessibilityService::GetCachedAccessible(nsINode *aNode,
nsIWeakReference *aWeakShell)
{
nsDocAccessible *docAccessible = GetDocAccessible(aNode->GetOwnerDoc());
return docAccessible ?
docAccessible->GetCachedAccessible(static_cast<void*>(aNode)) : nsnull;
return docAccessible ? docAccessible->GetCachedAccessible(aNode) : nsnull;
}
////////////////////////////////////////////////////////////////////////////////
@ -694,7 +759,7 @@ nsAccessibilityService::GetAccessibleFromCache(nsIDOMNode* aNode,
// "unofficially" shutdown document (i.e. not from nsAccDocManager) can still
// exist in the document cache.
nsCOMPtr<nsINode> node(do_QueryInterface(aNode));
nsAccessible* accessible = FindAccessibleInCache(static_cast<void*>(node));
nsAccessible* accessible = FindAccessibleInCache(node);
if (!accessible) {
nsCOMPtr<nsIDocument> document(do_QueryInterface(node));
if (document)
@ -732,7 +797,7 @@ nsAccessibilityService::GetAccessible(nsINode* aNode)
}
nsAccessible*
nsAccessibilityService::GetCachedContainerAccessible(nsINode* aNode)
nsAccessibilityService::GetCachedAccessibleOrContainer(nsINode* aNode)
{
if (!aNode)
return nsnull;
@ -749,8 +814,8 @@ nsAccessibilityService::GetCachedContainerAccessible(nsINode* aNode)
nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(presShell));
nsAccessible *accessible = nsnull;
while ((currNode = currNode->GetNodeParent()) &&
!(accessible = GetCachedAccessible(currNode, weakShell)));
while (!(accessible = GetCachedAccessible(currNode, weakShell)) &&
(currNode = currNode->GetNodeParent()));
return accessible;
}
@ -857,8 +922,7 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
nsWeakFrame weakFrame = content->GetPrimaryFrame();
// Check frame to see if it is hidden.
if (!weakFrame.GetFrame() ||
!weakFrame.GetFrame()->GetStyleVisibility()->IsVisible()) {
if (!weakFrame.GetFrame()) {
if (aIsHidden)
*aIsHidden = PR_TRUE;
@ -1195,23 +1259,24 @@ nsAccessibilityService::GetAccessibleByRule(nsINode* aNode,
if (!aNode || !aWeakShell)
return nsnull;
nsAccessible* cachedAcc = GetCachedAccessible(aNode, aWeakShell);
if (cachedAcc) {
if (aWhatToGet & eGetAccForNode)
if (aWhatToGet & eGetAccForNode) {
nsAccessible* cachedAcc = GetCachedAccessible(aNode, aWeakShell);
if (cachedAcc && cachedAcc->IsBoundToParent())
return cachedAcc;
// XXX: while nsAccessible::GetParent() tries to repair broken tree and
// may not return cached parent then we use GetAccessibleOrContainer().
return GetAccessibleByRule(aNode->GetNodeParent(), aWeakShell,
eGetAccForNodeOrContainer);
}
// Go up looking for the nearest accessible container stored in cache.
// Go up looking for the nearest accessible container having cached children.
nsTArray<nsINode*> nodes;
nsINode* node = aNode;
while ((node = node->GetNodeParent()) &&
!(cachedAcc = GetCachedAccessible(node, aWeakShell)))
nsAccessible* cachedAcc = nsnull;
while ((node = node->GetNodeParent())) {
cachedAcc = GetCachedAccessible(node, aWeakShell);
if (cachedAcc && cachedAcc->IsBoundToParent())
break;
nodes.AppendElement(node);
}
// Node is not in accessible document.
if (!cachedAcc)
@ -1713,29 +1778,6 @@ nsAccessibilityService::RemoveNativeRootAccessible(nsAccessible* aAccessible)
#endif
}
// Called from layout when the frame tree owned by a node changes significantly
nsresult
nsAccessibilityService::InvalidateSubtreeFor(nsIPresShell *aShell,
nsIContent *aChangeContent,
PRUint32 aChangeType)
{
NS_ASSERTION(aChangeType == nsIAccessibilityService::FRAME_SIGNIFICANT_CHANGE ||
aChangeType == nsIAccessibilityService::FRAME_SHOW ||
aChangeType == nsIAccessibilityService::FRAME_HIDE ||
aChangeType == nsIAccessibilityService::NODE_SIGNIFICANT_CHANGE ||
aChangeType == nsIAccessibilityService::NODE_APPEND ||
aChangeType == nsIAccessibilityService::NODE_REMOVE,
"Incorrect aEvent passed in");
NS_ENSURE_ARG_POINTER(aShell);
nsDocAccessible *docAccessible = GetDocAccessible(aShell->GetDocument());
if (docAccessible)
docAccessible->InvalidateCacheSubtree(aChangeContent, aChangeType);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// NS_GetAccessibilityService
////////////////////////////////////////////////////////////////////////////////

View File

@ -110,14 +110,21 @@ public:
virtual nsAccessible* AddNativeRootAccessible(void* aAtkAccessible);
virtual void RemoveNativeRootAccessible(nsAccessible* aRootAccessible);
virtual nsresult InvalidateSubtreeFor(nsIPresShell *aPresShell,
nsIContent *aContent,
PRUint32 aChangeType);
virtual void ContentRangeInserted(nsIPresShell* aPresShell,
nsIContent* aContainer,
nsIContent* aStartChild,
nsIContent* aEndChild);
virtual void ContentRemoved(nsIPresShell* aPresShell, nsIContent* aContainer,
nsIContent* aChild);
virtual void NotifyOfAnchorJumpTo(nsIContent *aTarget);
virtual void PresShellDestroyed(nsIPresShell* aPresShell);
virtual void RecreateAccessible(nsIPresShell* aPresShell,
nsIContent* aContent);
virtual void FireAccessibleEvent(PRUint32 aEvent, nsAccessible* aTarget);
// nsAccessibiltiyService
@ -178,12 +185,22 @@ public:
return GetAccessibleByRule(aNode, aWeakShell, eGetAccForContainer);
}
/**
* Return cached accessible for the given DOM node or cached container
* accessible if there's no cached accessible for the given node.
*/
nsAccessible* GetCachedAccessibleOrContainer(nsINode* aNode);
/**
* Return the first cached accessible parent of a DOM node.
*
* @param aDOMNode [in] the DOM node to get an accessible for
*/
nsAccessible* GetCachedContainerAccessible(nsINode *aNode);
inline nsAccessible* GetCachedContainerAccessible(nsINode *aNode)
{
return aNode ?
GetCachedAccessibleOrContainer(aNode->GetNodeParent()) : nsnull;
}
/**
* Initialize an accessible and cache it. The method should be called for

View File

@ -2629,16 +2629,11 @@ nsAccessible::Init()
if (!nsAccessNodeWrap::Init())
return PR_FALSE;
nsDocAccessible *docAcc =
nsDocAccessible* document =
GetAccService()->GetDocAccessible(mContent->GetOwnerDoc());
NS_ASSERTION(docAcc, "Cannot cache new nsAccessible!");
if (!docAcc)
return PR_FALSE;
NS_ASSERTION(document, "Cannot cache new nsAccessible!");
void *uniqueID = nsnull;
GetUniqueID(&uniqueID);
return docAcc->CacheAccessible(uniqueID, this);
return document ? document->CacheAccessible(this) : PR_FALSE;
}
void
@ -2647,10 +2642,8 @@ nsAccessible::Shutdown()
// Invalidate the child count and pointers to other accessibles, also make
// sure none of its children point to this parent
InvalidateChildren();
if (mParent) {
mParent->InvalidateChildren();
UnbindFromParent();
}
if (mParent)
mParent->RemoveChild(this);
nsAccessNodeWrap::Shutdown();
}
@ -2699,14 +2692,14 @@ nsAccessible::BindToParent(nsAccessible* aParent, PRUint32 aIndexInParent)
{
NS_PRECONDITION(aParent, "This method isn't used to set null parent!");
if (mParent && mParent != aParent) {
// Adopt a child -- we allow this now. the new parent
// may be a dom node which wasn't previously accessible but now is.
// The old parent's children now need to be invalidated, since
// it no longer owns the child, the new parent does
NS_ASSERTION(PR_FALSE, "Adopting child!");
if (mParent)
if (mParent) {
if (mParent != aParent) {
NS_ERROR("Adopting child!");
mParent->InvalidateChildren();
} else {
NS_ERROR("Binding to the same parent!");
return;
}
}
mParent = aParent;
@ -2907,13 +2900,10 @@ nsAccessible::IsInCache()
{
nsDocAccessible *docAccessible =
GetAccService()->GetDocAccessible(mContent->GetOwnerDoc());
if (!docAccessible)
return nsnull;
if (docAccessible)
return docAccessible->GetCachedAccessibleByUniqueID(UniqueID()) ? PR_TRUE : PR_FALSE;
void *uniqueID = nsnull;
GetUniqueID(&uniqueID);
return docAccessible->GetCachedAccessible(uniqueID) ? PR_TRUE : PR_FALSE;
return PR_FALSE;
}
#endif

View File

@ -51,6 +51,7 @@
#include "nsStringGlue.h"
#include "nsTArray.h"
#include "nsRefPtrHashtable.h"
#include "nsDataHashtable.h"
class AccGroupInfo;
class EmbeddedObjCollector;
@ -66,6 +67,8 @@ class nsIView;
typedef nsRefPtrHashtable<nsVoidPtrHashKey, nsAccessible>
nsAccessibleHashtable;
typedef nsDataHashtable<nsPtrHashKey<const nsINode>, nsAccessible*>
NodeToAccessibleMap;
// see nsAccessible::GetAttrValue
#define NS_OK_NO_ARIA_VALUE \
@ -206,6 +209,7 @@ public:
* nsnull if none.
*/
virtual void SetRoleMapEntry(nsRoleMapEntry *aRoleMapEntry);
const nsRoleMapEntry* GetRoleMapEntry() const { return mRoleMapEntry; }
/**
* Cache children if necessary. Return true if the accessible is defunct.
@ -290,7 +294,9 @@ public:
mParent->mChildren.SafeElementAt(mIndexInParent - 1, nsnull).get() : nsnull;
}
PRUint32 GetCachedChildCount() const { return mChildren.Length(); }
nsAccessible* GetCachedChildAt(PRUint32 aIndex) const { return mChildren.ElementAt(aIndex); }
PRBool AreChildrenCached() const { return mChildrenFlags != eChildrenUninitialized; }
bool IsBoundToParent() const { return mParent; }
#ifdef DEBUG
/**

View File

@ -363,6 +363,12 @@ nsApplicationAccessible::Shutdown()
mAppInfo = nsnull;
}
bool
nsApplicationAccessible::IsPrimaryForNode() const
{
return false;
}
////////////////////////////////////////////////////////////////////////////////
// nsAccessible public methods
@ -470,7 +476,7 @@ nsApplicationAccessible::GetSiblingAtOffset(PRInt32 aOffset, nsresult* aError)
}
////////////////////////////////////////////////////////////////////////////////
// nsIAccessNode
// nsIAccessNode and nsAccessNode
NS_IMETHODIMP
nsApplicationAccessible::GetDOMNode(nsIDOMNode **aDOMNode)
@ -524,14 +530,6 @@ nsApplicationAccessible::GetOwnerWindow(void **aOwnerWindow)
return NS_OK;
}
NS_IMETHODIMP
nsApplicationAccessible::GetUniqueID(void **aUniqueID)
{
NS_ENSURE_ARG_POINTER(aUniqueID);
*aUniqueID = static_cast<void *>(this);
return NS_OK;
}
NS_IMETHODIMP
nsApplicationAccessible::GetComputedStyleValue(const nsAString &aPseudoElt,
const nsAString &aPropertyName,
@ -556,3 +554,4 @@ nsApplicationAccessible::GetLanguage(nsAString &aLanguage)
aLanguage.Truncate();
return NS_OK;
}

View File

@ -71,7 +71,20 @@ public:
NS_DECL_ISUPPORTS_INHERITED
// nsIAccessNode
NS_DECL_NSIACCESSNODE
NS_SCRIPTABLE NS_IMETHOD GetDOMNode(nsIDOMNode** aDOMNode);
NS_SCRIPTABLE NS_IMETHOD GetDocument(nsIAccessibleDocument** aDocument);
NS_SCRIPTABLE NS_IMETHOD GetRootDocument(nsIAccessibleDocument** aRootDocument);
NS_SCRIPTABLE NS_IMETHOD GetInnerHTML(nsAString& aInnerHTML);
NS_SCRIPTABLE NS_IMETHOD ScrollTo(PRUint32 aScrollType);
NS_SCRIPTABLE NS_IMETHOD ScrollToPoint(PRUint32 aCoordinateType, PRInt32 aX, PRInt32 aY);
NS_IMETHOD GetOwnerWindow(void **aOwnerWindow);
NS_SCRIPTABLE NS_IMETHOD GetComputedStyleValue(const nsAString& aPseudoElt,
const nsAString& aPropertyName,
nsAString& aValue NS_OUTPARAM);
NS_SCRIPTABLE NS_IMETHOD GetComputedStyleCSSValue(const nsAString& aPseudoElt,
const nsAString& aPropertyName,
nsIDOMCSSPrimitiveValue** aValue NS_OUTPARAM);
NS_SCRIPTABLE NS_IMETHOD GetLanguage(nsAString& aLanguage);
// nsIAccessible
NS_IMETHOD GetParent(nsIAccessible **aParent);
@ -109,6 +122,7 @@ public:
virtual PRBool IsDefunct();
virtual PRBool Init();
virtual void Shutdown();
virtual bool IsPrimaryForNode() const;
// nsAccessible
virtual nsresult GetARIAState(PRUint32 *aState, PRUint32 *aExtraState);

View File

@ -285,8 +285,7 @@ nsCaretAccessible::SpellcheckSelectionChanged(nsIDOMDocument *aDoc,
NS_ENSURE_STATE(textAcc);
nsRefPtr<AccEvent> event =
new AccEvent(nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED,
textAcc, nsnull);
new AccEvent(nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED, textAcc);
nsEventShell::FireEvent(event);
return NS_OK;

File diff suppressed because it is too large Load Diff

View File

@ -170,12 +170,9 @@ public:
* @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)
* @param aIsAsynch [in] set to PR_TRUE if this is not being called from
* code synchronous with a DOM event
*/
nsresult FireDelayedAccessibleEvent(PRUint32 aEventType, nsINode *aNode,
AccEvent::EEventRule aAllowDupes = AccEvent::eRemoveDupes,
PRBool aIsAsynch = PR_FALSE,
EIsFromUserInput aIsFromUserInput = eAutoDetect);
/**
@ -186,51 +183,45 @@ public:
nsresult FireDelayedAccessibleEvent(AccEvent* aEvent);
/**
* Find the accessible object in the accessibility cache that corresponds to
* the given node or the first ancestor of it that has an accessible object
* associated with it. Clear that accessible object's parent's cache of
* accessible children and remove the accessible object and any descendants
* from the accessible cache. Fires proper events. New accessible objects will
* be created and cached again on demand.
*
* @param aContent [in] the child that is changing
* @param aEvent [in] the event from nsIAccessibleEvent that caused
* the change.
*/
void InvalidateCacheSubtree(nsIContent *aContent, PRUint32 aEvent);
/**
* Return the cached accessible by the given unique ID if it's in subtree of
* Return the cached accessible by the given DOM node if it's in subtree of
* this document accessible or the document accessible itself, otherwise null.
*
* @note the unique ID matches with the uniqueID attribute on nsIAccessNode
*
* @param aUniqueID [in] the unique ID used to cache the node.
*
* @return the accessible object
*/
nsAccessible* GetCachedAccessible(void *aUniqueID);
nsAccessible* GetCachedAccessible(nsINode* aNode);
/**
* Return the cached accessible by the given unique ID within this document.
*
* @note the unique ID matches with the uniqueID() of nsAccessNode
*
* @param aUniqueID [in] the unique ID used to cache the node.
*/
nsAccessible* GetCachedAccessibleByUniqueID(void* aUniqueID)
{
return UniqueID() == aUniqueID ?
this : mAccessibleCache.GetWeak(aUniqueID);
}
/**
* Return the cached accessible by the given unique ID looking through
* this and nested documents.
*/
nsAccessible* GetCachedAccessibleInSubtree(void* aUniqueID);
nsAccessible* GetCachedAccessibleByUniqueIDInSubtree(void* aUniqueID);
/**
* Cache the accessible.
*
* @param aUniquID [in] the unique identifier of accessible
* @param aAccessible [in] accessible to cache
*
* @return true if accessible being cached, otherwise false
*/
PRBool CacheAccessible(void *aUniqueID, nsAccessible *aAccessible);
PRBool CacheAccessible(nsAccessible *aAccessible);
/**
* Remove the given accessible from document cache.
* Shutdown the accessible and remove it from document cache.
*/
void RemoveAccessNodeFromCache(nsAccessible *aAccessible);
void ShutdownAccessible(nsAccessible *aAccessible);
/**
* Process the event when the queue of pending events is untwisted. Fire
@ -238,6 +229,17 @@ public:
*/
void ProcessPendingEvent(AccEvent* aEvent);
/**
* Update the accessible tree.
*/
void UpdateTree(nsIContent* aContainerNode, nsIContent* aStartChildNode,
nsIContent* aEndChildNode, PRBool aIsInsert);
/**
* Recreate an accessible, results in hide/show events pair.
*/
void RecreateAccessible(nsINode* aNode);
protected:
virtual void GetBoundsRect(nsRect& aRect, nsIFrame** aRelativeFrame);
@ -264,20 +266,6 @@ protected:
mChildDocuments.RemoveElement(aChildDocument);
}
/**
* Invalidate parent-child relations for any cached accessible in the DOM
* subtree. Accessible objects aren't destroyed.
*
* @param aStartNode [in] the root of the subrtee to invalidate accessible
* child/parent refs in
*/
void InvalidateChildrenInSubtree(nsINode *aStartNode);
/**
* Traverse through DOM tree and shutdown accessible objects.
*/
void RefreshNodes(nsINode *aStartNode);
static void ScrollTimerCallback(nsITimer *aTimer, void *aClosure);
/**
@ -310,27 +298,6 @@ protected:
CharacterDataChangeInfo* aInfo,
PRBool aIsInserted);
/**
* Create a text change event for a changed node.
*
* @param aContainerAccessible [in] the parent accessible for the node
* @param aChangeNode [in] the node that is being inserted or
* removed, or shown/hidden
* @param aAccessible [in] the accessible for that node, or nsnull
* if none exists
* @param aIsInserting [in] is aChangeNode being created or shown
* (vs. removed or hidden)
* @param aIsAsync [in] whether casual change is async
* @param aIsFromUserInput [in] the event is known to be from user input
*/
already_AddRefed<AccEvent>
CreateTextChangeEventForNode(nsAccessible *aContainerAccessible,
nsIContent *aChangeNode,
nsAccessible *aAccessible,
PRBool aIsInserting,
PRBool aIsAsynch,
EIsFromUserInput aIsFromUserInput = eAutoDetect);
/**
* Used to define should the event be fired on a delay.
*/
@ -339,34 +306,47 @@ protected:
eDelayedEvent
};
/**
* Fire show/hide events for either the current node if it has an accessible,
* or the first-line accessible descendants of the given node.
*
* @param aDOMNode [in] the given node
* @param aAvoidOnThisNode [in] call with PR_TRUE the first time to
* prevent event firing on root node for change
* @param aEventType [in] event type to fire an event
* @param aDelayedOrNormal [in] whether to fire the event on a delay
* @param aIsAsyncChange [in] whether casual change is async
* @param aIsFromUserInput [in] the event is known to be from user input
*/
nsresult FireShowHideEvents(nsINode *aDOMNode, PRBool aAvoidOnThisNode,
PRUint32 aEventType,
EEventFiringType aDelayedOrNormal,
PRBool aIsAsyncChange,
EIsFromUserInput aIsFromUserInput = eAutoDetect);
/**
* Fire a value change event for the the given accessible if it is a text
* field (has a ROLE_ENTRY).
*/
void FireValueChangeForTextFields(nsAccessible *aAccessible);
/**
* Helper for UpdateTree() method. Go down to DOM subtree and updates
* accessible tree. Return one of these flags.
*/
enum EUpdateTreeFlags {
eNoAccessible = 0,
eAccessible = 1,
eAlertAccessible = 2
};
PRUint32 UpdateTreeInternal(nsAccessible* aContainer,
nsIContent* aStartNode,
nsIContent* aEndNode,
PRBool aIsInsert,
PRBool aFireEvents,
EIsFromUserInput aFromUserInput);
/**
* Remove accessibles in subtree from node to accessible map.
*/
void UncacheChildrenInSubtree(nsAccessible* aRoot);
/**
* Shutdown any cached accessible in the subtree.
*
* @param aAccessible [in] the root of the subrtee to invalidate accessible
* child/parent refs in
*/
void ShutdownChildrenInSubtree(nsAccessible *aAccessible);
/**
* Cache of accessibles within this document accessible.
*/
nsAccessibleHashtable mAccessibleCache;
NodeToAccessibleMap mNodeToAccessibleMap;
nsCOMPtr<nsIDocument> mDocument;
nsCOMPtr<nsITimer> mScrollWatchTimer;

View File

@ -68,12 +68,12 @@ nsEventShell::FireEvent(AccEvent* aEvent)
void
nsEventShell::FireEvent(PRUint32 aEventType, nsAccessible *aAccessible,
PRBool aIsAsynch, EIsFromUserInput aIsFromUserInput)
EIsFromUserInput aIsFromUserInput)
{
NS_ENSURE_TRUE(aAccessible,);
nsRefPtr<AccEvent> event = new AccEvent(aEventType, aAccessible,
aIsAsynch, aIsFromUserInput);
aIsFromUserInput);
FireEvent(event);
}
@ -147,9 +147,9 @@ nsAccEventQueue::Push(AccEvent* aEvent)
// Associate text change with hide event if it wasn't stolen from hiding
// siblings during coalescence.
AccHideEvent* hideEvent = downcast_accEvent(aEvent);
if (hideEvent && !hideEvent->mTextChangeEvent)
CreateTextChangeEventFor(hideEvent);
AccMutationEvent* showOrHideEvent = downcast_accEvent(aEvent);
if (showOrHideEvent && !showOrHideEvent->mTextChangeEvent)
CreateTextChangeEventFor(showOrHideEvent);
// Process events.
PrepareFlush();
@ -209,10 +209,10 @@ nsAccEventQueue::WillRefresh(mozilla::TimeStamp aTime)
if (accEvent->mEventRule != AccEvent::eDoNotEmit) {
mDocument->ProcessPendingEvent(accEvent);
AccHideEvent* hideEvent = downcast_accEvent(accEvent);
if (hideEvent) {
if (hideEvent->mTextChangeEvent)
mDocument->ProcessPendingEvent(hideEvent->mTextChangeEvent);
AccMutationEvent* showOrhideEvent = downcast_accEvent(accEvent);
if (showOrhideEvent) {
if (showOrhideEvent->mTextChangeEvent)
mDocument->ProcessPendingEvent(showOrhideEvent->mTextChangeEvent);
}
}
@ -267,7 +267,7 @@ nsAccEventQueue::CoalesceEvents()
// accessibles can't be created at this point because of lazy frame
// construction (bug 570275).
// Coalesce hide events for sibling targets.
// Coalesce hide and show events for sibling targets.
if (tailEvent->mEventType == nsIAccessibleEvent::EVENT_HIDE) {
AccHideEvent* tailHideEvent = downcast_accEvent(tailEvent);
AccHideEvent* thisHideEvent = downcast_accEvent(thisEvent);
@ -278,6 +278,20 @@ nsAccEventQueue::CoalesceEvents()
if (tailEvent->mEventRule != AccEvent::eDoNotEmit)
CoalesceTextChangeEventsFor(tailHideEvent, thisHideEvent);
return;
}
} else if (tailEvent->mEventType == nsIAccessibleEvent::EVENT_SHOW) {
if (thisEvent->mAccessible->GetParent() ==
tailEvent->mAccessible->GetParent()) {
tailEvent->mEventRule = thisEvent->mEventRule;
// Coalesce text change events for show events.
if (tailEvent->mEventRule != AccEvent::eDoNotEmit) {
AccShowEvent* tailShowEvent = downcast_accEvent(tailEvent);
AccShowEvent* thisShowEvent = downcast_accEvent(thisEvent);
CoalesceTextChangeEventsFor(tailShowEvent, thisShowEvent);
}
return;
}
}
@ -286,70 +300,22 @@ nsAccEventQueue::CoalesceEvents()
if (!thisEvent->mNode->IsInDoc())
continue;
// Coalesce show and reorder events by sibling targets.
// Coalesce earlier event for the same target.
if (thisEvent->mNode == tailEvent->mNode) {
thisEvent->mEventRule = AccEvent::eDoNotEmit;
return;
}
// Coalesce events by sibling targets (this is a case for reorder
// events).
if (thisEvent->mNode->GetNodeParent() ==
tailEvent->mNode->GetNodeParent()) {
tailEvent->mEventRule = thisEvent->mEventRule;
return;
}
// Specifies if this event target can be descendant of tail node.
PRBool thisCanBeDescendantOfTail = PR_FALSE;
// Coalesce depending on whether this event was coalesced or not.
if (thisEvent->mEventRule == AccEvent::eDoNotEmit) {
// If this event was coalesced then do not emit tail event iff tail
// event has the same target or its target is contained by this event
// target. Note, we don't need to check whether tail event target
// contains this event target since this event was coalesced already.
// As well we don't need to apply the calculated rule for siblings of
// tail node because tail event rule was applied to possible tail
// node siblings while this event was coalesced.
if (thisEvent->mNode == tailEvent->mNode) {
thisEvent->mEventRule = AccEvent::eDoNotEmit;
return;
}
} else {
// If this event wasn't coalesced already then try to coalesce it or
// tail event. If this event is coalesced by tail event then continue
// search through events other events that can be coalesced by tail
// event.
// If tail and this events have the same target then coalesce tail
// event because more early event we should fire early and then stop
// processing.
if (thisEvent->mNode == tailEvent->mNode) {
// Coalesce reorder events by special way since reorder events can
// be conditional events (be or not be fired in the end).
if (thisEvent->mEventType == nsIAccessibleEvent::EVENT_REORDER) {
CoalesceReorderEventsFromSameSource(thisEvent, tailEvent);
if (tailEvent->mEventRule != AccEvent::eDoNotEmit)
continue;
}
else {
tailEvent->mEventRule = AccEvent::eDoNotEmit;
}
return;
}
// This and tail events can be anywhere in the tree, make assumptions
// for mutation events.
// More older show event target (thisNode) can't be contained by
// recent.
// show event target (tailNode), i.e be a descendant of tailNode.
// XXX: target of older show event caused by DOM node appending can be
// contained by target of recent show event caused by style change.
// XXX: target of older show event caused by style change can be
// contained by target of recent show event caused by style change.
thisCanBeDescendantOfTail =
tailEvent->mEventType != nsIAccessibleEvent::EVENT_SHOW ||
tailEvent->mIsAsync;
}
// This and tail events can be anywhere in the tree, make assumptions
// for mutation events.
// Coalesce tail event if tail node is descendant of this node. Stop
// processing if tail event is coalesced since all possible descendants
@ -359,54 +325,20 @@ nsAccEventQueue::CoalesceEvents()
// this check for hide events.
if (tailEvent->mEventType != nsIAccessibleEvent::EVENT_HIDE &&
nsCoreUtils::IsAncestorOf(thisEvent->mNode, tailEvent->mNode)) {
if (thisEvent->mEventType == nsIAccessibleEvent::EVENT_REORDER) {
CoalesceReorderEventsFromSameTree(thisEvent, tailEvent);
if (tailEvent->mEventRule != AccEvent::eDoNotEmit)
continue;
return;
}
tailEvent->mEventRule = AccEvent::eDoNotEmit;
return;
}
#ifdef DEBUG
if (tailEvent->mEventType == nsIAccessibleEvent::EVENT_HIDE &&
nsCoreUtils::IsAncestorOf(thisEvent->mNode, tailEvent->mNode)) {
NS_NOTREACHED("More older hide event target is an ancestor of recent hide event target!");
}
#endif
// If this node is a descendant of tail node then coalesce this event,
// check other events in the queue.
if (thisCanBeDescendantOfTail &&
nsCoreUtils::IsAncestorOf(tailEvent->mNode, thisEvent->mNode)) {
if (thisEvent->mEventType == nsIAccessibleEvent::EVENT_REORDER) {
CoalesceReorderEventsFromSameTree(tailEvent, thisEvent);
if (tailEvent->mEventRule != AccEvent::eDoNotEmit)
continue;
return;
}
// Do not emit thisEvent, also apply this result to sibling nodes of
// thisNode.
// check other events in the queue. Do not emit thisEvent, also apply
// this result to sibling nodes of thisNode.
if (nsCoreUtils::IsAncestorOf(tailEvent->mNode, thisEvent->mNode)) {
thisEvent->mEventRule = AccEvent::eDoNotEmit;
ApplyToSiblings(0, index, thisEvent->mEventType,
thisEvent->mNode, AccEvent::eDoNotEmit);
continue;
}
#ifdef DEBUG
if (!thisCanBeDescendantOfTail &&
nsCoreUtils::IsAncestorOf(tailEvent->mNode, thisEvent->mNode)) {
NS_NOTREACHED("Older event target is a descendant of recent event target!");
}
#endif
} // for (index)
} break; // case eCoalesceFromSameSubtree
@ -462,41 +394,6 @@ nsAccEventQueue::ApplyToSiblings(PRUint32 aStart, PRUint32 aEnd,
}
}
void
nsAccEventQueue::CoalesceReorderEventsFromSameSource(AccEvent* aAccEvent1,
AccEvent* aAccEvent2)
{
// Do not emit event2 if event1 is unconditional.
AccReorderEvent* reorderEvent1 = downcast_accEvent(aAccEvent1);
if (reorderEvent1->IsUnconditionalEvent()) {
aAccEvent2->mEventRule = AccEvent::eDoNotEmit;
return;
}
// Do not emit event1 if event2 is unconditional.
AccReorderEvent* reorderEvent2 = downcast_accEvent(aAccEvent2);
if (reorderEvent2->IsUnconditionalEvent()) {
aAccEvent1->mEventRule = AccEvent::eDoNotEmit;
return;
}
// Do not emit event2 if event1 is valid, otherwise do not emit event1.
if (reorderEvent1->HasAccessibleInReasonSubtree())
aAccEvent2->mEventRule = AccEvent::eDoNotEmit;
else
aAccEvent1->mEventRule = AccEvent::eDoNotEmit;
}
void
nsAccEventQueue::CoalesceReorderEventsFromSameTree(AccEvent* aAccEvent,
AccEvent* aDescendantAccEvent)
{
// Do not emit descendant event if this event is unconditional.
AccReorderEvent* reorderEvent = downcast_accEvent(aAccEvent);
if (reorderEvent->IsUnconditionalEvent())
aDescendantAccEvent->mEventRule = AccEvent::eDoNotEmit;
}
void
nsAccEventQueue::CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
AccHideEvent* aThisEvent)
@ -523,7 +420,35 @@ nsAccEventQueue::CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
}
void
nsAccEventQueue::CreateTextChangeEventFor(AccHideEvent* aEvent)
nsAccEventQueue::CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
AccShowEvent* aThisEvent)
{
AccTextChangeEvent* textEvent = aThisEvent->mTextChangeEvent;
if (!textEvent)
return;
if (aTailEvent->mAccessible->GetIndexInParent() ==
aThisEvent->mAccessible->GetIndexInParent() + 1) {
// If tail target was inserted after this target, i.e. tail target is next
// sibling of this target.
aTailEvent->mAccessible->AppendTextTo(textEvent->mModifiedText,
0, PR_UINT32_MAX);
} else if (aTailEvent->mAccessible->GetIndexInParent() ==
aThisEvent->mAccessible->GetIndexInParent() -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, 0, PR_UINT32_MAX);
textEvent->mModifiedText = startText + textEvent->mModifiedText;
textEvent->mStart -= startText.Length();
}
aTailEvent->mTextChangeEvent.swap(aThisEvent->mTextChangeEvent);
}
void
nsAccEventQueue::CreateTextChangeEventFor(AccMutationEvent* aEvent)
{
nsRefPtr<nsHyperTextAccessible> textAccessible = do_QueryObject(
GetAccService()->GetContainerAccessible(aEvent->mNode,
@ -551,7 +476,6 @@ nsAccEventQueue::CreateTextChangeEventFor(AccHideEvent* aEvent)
return;
aEvent->mTextChangeEvent =
new AccTextChangeEvent(textAccessible, offset, text, PR_FALSE,
aEvent->mIsAsync,
new AccTextChangeEvent(textAccessible, offset, text, aEvent->IsShow(),
aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput);
}

View File

@ -66,11 +66,8 @@ public:
*
* @param aEventType [in] the event type
* @param aAccessible [in] the event target
* @param aIsAsync [in, optional] specifies whether the origin change
* this event is fired owing to is async.
*/
static void FireEvent(PRUint32 aEventType, nsAccessible *aAccessible,
PRBool aIsAsynch = PR_FALSE,
EIsFromUserInput aIsFromUserInput = eAutoDetect);
/**
@ -144,12 +141,6 @@ private:
PRUint32 aEventType, nsINode* aNode,
AccEvent::EEventRule aEventRule);
/**
* Do not emit one of two given reorder events fired for the same DOM node.
*/
void CoalesceReorderEventsFromSameSource(AccEvent* aAccEvent1,
AccEvent* aAccEvent2);
/**
* Do not emit one of two given reorder events fired for DOM nodes in the case
* when one DOM node is in parent chain of second one.
@ -162,14 +153,15 @@ private:
*/
void CoalesceTextChangeEventsFor(AccHideEvent* aTailEvent,
AccHideEvent* aThisEvent);
void CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent,
AccShowEvent* aThisEvent);
/**
* Create text change event caused by hide event. When a node is hidden or
* removed, the text in an ancestor hyper text will lose characters. Create
* text change event unless the node is being removed or frame is being
* destroyed.
* 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(AccHideEvent* aEvent);
void CreateTextChangeEventFor(AccMutationEvent* aEvent);
/**
* Indicates whether we're waiting on a refresh notification from our

View File

@ -319,7 +319,6 @@ nsRootAccessible::FireAccessibleFocusEvent(nsAccessible *aAccessible,
nsINode *aNode,
nsIDOMEvent *aFocusEvent,
PRBool aForceEvent,
PRBool aIsAsynch,
EIsFromUserInput aIsFromUserInput)
{
// Implementors: only fire delayed/async events from this method.
@ -398,7 +397,7 @@ nsRootAccessible::FireAccessibleFocusEvent(nsAccessible *aAccessible,
if (mCurrentARIAMenubar) {
nsRefPtr<AccEvent> menuStartEvent =
new AccEvent(nsIAccessibleEvent::EVENT_MENU_START,
menuBarAccessible, PR_FALSE, aIsFromUserInput,
menuBarAccessible, aIsFromUserInput,
AccEvent::eAllowDupes);
if (menuStartEvent) {
FireDelayedAccessibleEvent(menuStartEvent);
@ -411,7 +410,7 @@ nsRootAccessible::FireAccessibleFocusEvent(nsAccessible *aAccessible,
else if (mCurrentARIAMenubar) {
nsRefPtr<AccEvent> menuEndEvent =
new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mCurrentARIAMenubar,
PR_FALSE, aIsFromUserInput, AccEvent::eAllowDupes);
aIsFromUserInput, AccEvent::eAllowDupes);
if (menuEndEvent) {
FireDelayedAccessibleEvent(menuEndEvent);
}
@ -434,13 +433,11 @@ nsRootAccessible::FireAccessibleFocusEvent(nsAccessible *aAccessible,
gLastFocusedNode = finalFocusNode;
NS_IF_ADDREF(gLastFocusedNode);
gLastFocusedFrameType = (focusFrame && focusFrame->GetStyleVisibility()->IsVisible()) ? focusFrame->GetType() : 0;
// Coalesce focus events from the same document, because DOM focus event might
// be fired for the document node and then for the focused DOM element.
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_FOCUS,
finalFocusNode, AccEvent::eCoalesceFromSameDocument,
aIsAsynch, aIsFromUserInput);
aIsFromUserInput);
return PR_TRUE;
}
@ -663,7 +660,6 @@ nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
}
else if (eventType.EqualsLiteral("blur")) {
NS_IF_RELEASE(gLastFocusedNode);
gLastFocusedFrameType = nsnull;
gLastFocusedAccessiblesState = 0;
}
else if (eventType.EqualsLiteral("AlertActive")) {
@ -732,16 +728,16 @@ nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
if (fireFocus) {
// Always asynch, always from user input.
FireAccessibleFocusEvent(accessible, targetNode, aEvent, PR_TRUE,
PR_TRUE, eFromUserInput);
eFromUserInput);
}
}
else if (eventType.EqualsLiteral("DOMMenuBarActive")) { // Always asynch, always from user input
else if (eventType.EqualsLiteral("DOMMenuBarActive")) { // Always from user input
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_START,
accessible, PR_TRUE, eFromUserInput);
accessible, eFromUserInput);
}
else if (eventType.EqualsLiteral("DOMMenuBarInactive")) { // Always asynch, always from user input
else if (eventType.EqualsLiteral("DOMMenuBarInactive")) { // Always from user input
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_END,
accessible, PR_TRUE, eFromUserInput);
accessible, eFromUserInput);
FireCurrentFocusEvent();
}
else if (eventType.EqualsLiteral("ValueChange")) {

View File

@ -106,7 +106,6 @@ public:
nsINode *aFocusNode,
nsIDOMEvent *aFocusEvent,
PRBool aForceEvent = PR_FALSE,
PRBool aIsAsynch = PR_FALSE,
EIsFromUserInput aIsFromUserInput = eAutoDetect);
/**

View File

@ -237,6 +237,11 @@ nsresult
nsTextEquivUtils::AppendFromAccessible(nsAccessible *aAccessible,
nsAString *aString)
{
// Ignore hidden accessible for name computation.
nsIFrame* frame = aAccessible->GetFrame();
if (!frame || !frame->GetStyleVisibility()->IsVisible())
return NS_OK;
//XXX: is it necessary to care the accessible is not a document?
if (aAccessible->IsContent()) {
nsresult rv = AppendTextEquivFromTextContent(aAccessible->GetContent(),

View File

@ -665,6 +665,9 @@ nsHTMLComboboxAccessible::
{
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLComboboxAccessible: nsAccessible
PRUint32
nsHTMLComboboxAccessible::NativeRole()
{
@ -859,6 +862,9 @@ nsHTMLComboboxListAccessible::
{
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLComboboxAccessible: nsAccessNode
nsIFrame*
nsHTMLComboboxListAccessible::GetFrame()
{
@ -874,6 +880,15 @@ nsHTMLComboboxListAccessible::GetFrame()
return nsnull;
}
bool
nsHTMLComboboxListAccessible::IsPrimaryForNode() const
{
return false;
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLComboboxAccessible: nsAccessible
/**
* As a nsHTMLComboboxListAccessible we can have the following states:
* STATE_FOCUSED
@ -899,14 +914,6 @@ nsHTMLComboboxListAccessible::GetStateInternal(PRUint32 *aState,
return NS_OK;
}
NS_IMETHODIMP nsHTMLComboboxListAccessible::GetUniqueID(void **aUniqueID)
{
// Since mContent is same for all tree item, use |this| pointer as the unique
// Id.
*aUniqueID = static_cast<void*>(this);
return NS_OK;
}
/**
* Gets the bounds for the areaFrame.
* Walks the Frame tree and checks for proper frames.

View File

@ -226,11 +226,9 @@ public:
nsIWeakReference* aShell);
virtual ~nsHTMLComboboxListAccessible() {}
// nsIAccessible
NS_IMETHOD GetUniqueID(void **aUniqueID);
// nsAccessNode
virtual nsIFrame* GetFrame();
virtual bool IsPrimaryForNode() const;
// nsAccessible
virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);

View File

@ -335,14 +335,8 @@ nsHTMLListBulletAccessible::
mBulletText += ' '; // Otherwise bullets are jammed up against list text
}
NS_IMETHODIMP
nsHTMLListBulletAccessible::GetUniqueID(void **aUniqueID)
{
// Since mContent is same as for list item, use |this| pointer as the unique
// id.
*aUniqueID = static_cast<void*>(this);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLListBulletAccessible: nsAccessNode
void
nsHTMLListBulletAccessible::Shutdown()
@ -351,6 +345,15 @@ nsHTMLListBulletAccessible::Shutdown()
nsLeafAccessible::Shutdown();
}
bool
nsHTMLListBulletAccessible::IsPrimaryForNode() const
{
return false;
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLListBulletAccessible: nsAccessible
NS_IMETHODIMP
nsHTMLListBulletAccessible::GetName(nsAString &aName)
{

View File

@ -132,14 +132,12 @@ class nsHTMLListBulletAccessible : public nsLeafAccessible
public:
nsHTMLListBulletAccessible(nsIContent* aContent, nsIWeakReference* aShell);
// nsIAccessNode
NS_IMETHOD GetUniqueID(void **aUniqueID);
// nsIAccessible
NS_IMETHOD GetName(nsAString& aName);
// nsAccessNode
virtual void Shutdown();
virtual bool IsPrimaryForNode() const;
// nsAccessible
virtual PRUint32 NativeRole();

View File

@ -257,9 +257,7 @@ __try{
// accessibility application can compare this to the childID we
// return for events such as focus events, to correlate back to
// data nodes in their internal object model.
void *uniqueID;
GetUniqueID(&uniqueID);
*aUniqueID = - NS_PTR_TO_INT32(uniqueID);
*aUniqueID = - NS_PTR_TO_INT32(UniqueID());
*aNumChildren = GetNode()->GetChildCount();

View File

@ -1354,12 +1354,7 @@ STDMETHODIMP
nsAccessibleWrap::get_uniqueID(long *uniqueID)
{
__try {
void *id = nsnull;
nsresult rv = GetUniqueID(&id);
if (NS_FAILED(rv))
return GetHRESULT(rv);
*uniqueID = - reinterpret_cast<long>(id);
*uniqueID = - reinterpret_cast<long>(UniqueID());
return S_OK;
} __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
@ -1575,60 +1570,53 @@ nsAccessibleWrap::FirePlatformEvent(AccEvent* aEvent)
if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED ||
eventType == nsIAccessibleEvent::EVENT_FOCUS) {
UpdateSystemCaret();
} else if (eventType == nsIAccessibleEvent::EVENT_REORDER) {
// If the accessible children are changed then drop the IEnumVariant current
// position of the accessible.
UnattachIEnumVariant();
}
PRInt32 childID = GetChildIDFor(accessible); // get the id for the accessible
if (!childID)
return NS_OK; // Can't fire an event without a child ID
// See if we're in a scrollable area with its own window
nsAccessible *newAccessible = nsnull;
if (eventType == nsIAccessibleEvent::EVENT_HIDE) {
// Don't use frame from current accessible when we're hiding that
// accessible.
newAccessible = accessible->GetCachedParent();
} else {
newAccessible = accessible;
}
HWND hWnd = GetHWNDFor(newAccessible);
HWND hWnd = GetHWNDFor(accessible);
NS_ENSURE_TRUE(hWnd, NS_ERROR_FAILURE);
// Gecko uses two windows for every scrollable area. One window contains
// scrollbars and the child window contains only the client area.
// Details of the 2 window system:
// * Scrollbar window: caret drawing window & return value for WindowFromAccessibleObject()
// * Client area window: text drawing window & MSAA event window
nsAutoString tag;
nsCAutoString id;
nsIContent* cnt = accessible->GetContent();
if (cnt) {
cnt->Tag()->ToString(tag);
nsIAtom* aid = cnt->GetID();
if (aid)
aid->ToUTF8String(id);
}
#ifdef DEBUG_A11Y
printf("\n\nMSAA event: event: %d, target: %s@id='%s', childid: %d, hwnd: %d\n\n",
eventType, NS_ConvertUTF16toUTF8(tag).get(), id.get(),
childID, hWnd);
#endif
// Fire MSAA event for client area window.
NotifyWinEvent(winEvent, hWnd, OBJID_CLIENT, childID);
// If the accessible children are changed then drop the IEnumVariant current
// position of the accessible.
if (eventType == nsIAccessibleEvent::EVENT_REORDER)
UnattachIEnumVariant();
return NS_OK;
}
//------- Helper methods ---------
PRInt32 nsAccessibleWrap::GetChildIDFor(nsIAccessible* aAccessible)
PRInt32 nsAccessibleWrap::GetChildIDFor(nsAccessible* aAccessible)
{
// A child ID of the window is required, when we use NotifyWinEvent,
// so that the 3rd party application can call back and get the IAccessible
// the event occurred on.
void *uniqueID = nsnull;
nsCOMPtr<nsIAccessNode> accessNode(do_QueryInterface(aAccessible));
if (!accessNode) {
return 0;
}
accessNode->GetUniqueID(&uniqueID);
// Yes, this means we're only compatibible with 32 bit
// MSAA is only available for 32 bit windows, so it's okay
return - NS_PTR_TO_INT32(uniqueID);
// XXX: bug 606080
return aAccessible ? - NS_PTR_TO_INT32(aAccessible->UniqueID()) : 0;
}
HWND

View File

@ -310,7 +310,7 @@ public: // construction, destruction
virtual nsresult HandleAccEvent(AccEvent* aEvent);
// Helper methods
static PRInt32 GetChildIDFor(nsIAccessible* aAccessible);
static PRInt32 GetChildIDFor(nsAccessible* aAccessible);
static HWND GetHWNDFor(nsAccessible *aAccessible);
static HRESULT ConvertToIA2Attributes(nsIPersistentProperties *aAttributes,
BSTR *aIA2Attributes);

View File

@ -109,7 +109,7 @@ nsDocAccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild)
if (aVarChild.vt == VT_I4 && aVarChild.lVal < 0) {
// Convert child ID to unique ID.
void* uniqueID = reinterpret_cast<void*>(-aVarChild.lVal);
return GetCachedAccessibleInSubtree(uniqueID);
return GetCachedAccessibleByUniqueIDInSubtree(uniqueID);
}
return nsAccessibleWrap::GetXPAccessibleFor(aVarChild);

View File

@ -509,15 +509,13 @@ nsXULTreeAccessible::InvalidateCache(PRInt32 aRow, PRInt32 aCount)
if (accessible) {
nsRefPtr<AccEvent> event =
new AccEvent(nsIAccessibleEvent::EVENT_HIDE, accessible, PR_FALSE);
new AccEvent(nsIAccessibleEvent::EVENT_HIDE, accessible);
nsEventShell::FireEvent(event);
accessible->Shutdown();
// Remove accessible from document cache and tree cache.
// Shutdown and remove accessible from document cache and tree cache.
nsDocAccessible *docAccessible = GetDocAccessible();
if (docAccessible)
docAccessible->RemoveAccessNodeFromCache(accessible);
docAccessible->ShutdownAccessible(accessible);
mAccessibleCache.Remove(key);
}
@ -539,12 +537,10 @@ nsXULTreeAccessible::InvalidateCache(PRInt32 aRow, PRInt32 aCount)
nsAccessible *accessible = mAccessibleCache.GetWeak(key);
if (accessible) {
accessible->Shutdown();
// Remove accessible from document cache and tree cache.
// Shutdown and remove accessible from document cache and tree cache.
nsDocAccessible *docAccessible = GetDocAccessible();
if (docAccessible)
docAccessible->RemoveAccessNodeFromCache(accessible);
docAccessible->ShutdownAccessible(accessible);
mAccessibleCache.Remove(key);
}
@ -610,7 +606,7 @@ nsXULTreeAccessible::TreeViewChanged()
// AT because it should be expensive to fire destroy events for each tree item
// in cache.
nsRefPtr<AccEvent> eventDestroy =
new AccEvent(nsIAccessibleEvent::EVENT_HIDE, this, PR_FALSE);
new AccEvent(nsIAccessibleEvent::EVENT_HIDE, this);
if (!eventDestroy)
return;
@ -621,7 +617,7 @@ nsXULTreeAccessible::TreeViewChanged()
mTree->GetView(getter_AddRefs(mTreeView));
nsRefPtr<AccEvent> eventCreate =
new AccEvent(nsIAccessibleEvent::EVENT_SHOW, this, PR_FALSE);
new AccEvent(nsIAccessibleEvent::EVENT_SHOW, this);
if (!eventCreate)
return;
@ -662,18 +658,6 @@ NS_IMPL_ISUPPORTS_INHERITED1(nsXULTreeItemAccessibleBase,
nsAccessible,
nsXULTreeItemAccessibleBase)
////////////////////////////////////////////////////////////////////////////////
// nsXULTreeItemAccessibleBase: nsIAccessNode implementation
NS_IMETHODIMP
nsXULTreeItemAccessibleBase::GetUniqueID(void **aUniqueID)
{
// Since mContent is same for all tree items and tree itself, use |this|
// pointer as the unique ID.
*aUniqueID = static_cast<void*>(this);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsXULTreeItemAccessibleBase: nsIAccessible implementation
@ -888,6 +872,12 @@ nsXULTreeItemAccessibleBase::Shutdown()
nsAccessibleWrap::Shutdown();
}
bool
nsXULTreeItemAccessibleBase::IsPrimaryForNode() const
{
return false;
}
////////////////////////////////////////////////////////////////////////////////
// nsXULTreeItemAccessibleBase: nsAccessible public methods

View File

@ -181,9 +181,6 @@ public:
// nsISupports
NS_DECL_ISUPPORTS_INHERITED
// nsIAccessNode
NS_IMETHOD GetUniqueID(void **aUniqueID);
// nsIAccessible
NS_IMETHOD GetFocusedChild(nsIAccessible **aFocusedChild);
@ -207,6 +204,7 @@ public:
// nsAccessNode
virtual PRBool IsDefunct();
virtual void Shutdown();
virtual bool IsPrimaryForNode() const;
// nsAccessible
virtual nsresult GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState);

View File

@ -804,18 +804,6 @@ NS_IMPL_ISUPPORTS_INHERITED2(nsXULTreeGridCellAccessible,
nsIAccessibleTableCell,
nsXULTreeGridCellAccessible)
////////////////////////////////////////////////////////////////////////////////
// nsXULTreeGridCellAccessible: nsIAccessNode implementation
NS_IMETHODIMP
nsXULTreeGridCellAccessible::GetUniqueID(void **aUniqueID)
{
NS_ENSURE_ARG_POINTER(aUniqueID);
*aUniqueID = static_cast<void*>(this);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsXULTreeGridCellAccessible: nsIAccessible implementation
@ -1124,6 +1112,12 @@ nsXULTreeGridCellAccessible::Init()
return PR_TRUE;
}
bool
nsXULTreeGridCellAccessible::IsPrimaryForNode() const
{
return false;
}
////////////////////////////////////////////////////////////////////////////////
// nsXULTreeGridCellAccessible: nsAccessible public implementation

View File

@ -141,9 +141,6 @@ public:
// nsISupports
NS_DECL_ISUPPORTS_INHERITED
// nsIAccessNode
NS_IMETHOD GetUniqueID(void **aUniqueID);
// nsIAccessible
NS_IMETHOD GetFocusedChild(nsIAccessible **aFocusedChild);
@ -161,6 +158,7 @@ public:
// nsAccessNode
virtual PRBool IsDefunct();
virtual PRBool Init();
virtual bool IsPrimaryForNode() const;
// nsAccessible
virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);

View File

@ -230,6 +230,19 @@ function isAccessible(aAccOrElmOrID, aInterfaces)
true : false;
}
/**
* Return an accessible that contains the DOM node for the given identifier.
*/
function getContainerAccessible(aAccOrElmOrID)
{
var node = getNode(aAccOrElmOrID);
if (!node)
return null;
while ((node = node.parentNode) && !isAccessible(node));
return node ? getAccessible(node) : null;
}
/**
* Return root accessible for the given identifier.
*/
@ -527,6 +540,15 @@ function prettyName(aIdentifier)
} catch (e) {
msg += "defunct";
}
if (acc) {
var exp = /native\s*@\s*(0x[a-f0-9]+)/g;
var match = exp.exec(acc.valueOf());
if (match)
msg += ", address: " + match[1];
else
msg += ", address: " + acc.valueOf();
}
msg += "]";
return msg;
@ -556,14 +578,16 @@ addLoadEvent(initialize);
function getNodePrettyName(aNode)
{
try {
if (aNode.nodeType == nsIDOMNode.ELEMENT_NODE && aNode.hasAttribute("id"))
return " '" + aNode.getAttribute("id") + "' ";
if (aNode.nodeType == nsIDOMNode.DOCUMENT_NODE)
return " 'document node' ";
return " '" + aNode.localName + " node' ";
var name = " '" + aNode.localName;
if (aNode.nodeType == nsIDOMNode.ELEMENT_NODE && aNode.hasAttribute("id"))
name += "@id='" + aNode.getAttribute("id") + "'";
name += " node' "
return name;
} catch (e) {
return "no node info";
return "' no node info '";
}
}

View File

@ -8,6 +8,8 @@ const EVENT_DOCUMENT_LOAD_STOPPED = nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_STOPP
const EVENT_HIDE = nsIAccessibleEvent.EVENT_HIDE;
const EVENT_FOCUS = nsIAccessibleEvent.EVENT_FOCUS;
const EVENT_NAME_CHANGE = nsIAccessibleEvent.EVENT_NAME_CHANGE;
const EVENT_MENUPOPUP_START = nsIAccessibleEvent.EVENT_MENUPOPUP_START;
const EVENT_MENUPOPUP_END = nsIAccessibleEvent.EVENT_MENUPOPUP_END;
const EVENT_REORDER = nsIAccessibleEvent.EVENT_REORDER;
const EVENT_SCROLLING_START = nsIAccessibleEvent.EVENT_SCROLLING_START;
const EVENT_SELECTION_ADD = nsIAccessibleEvent.EVENT_SELECTION_ADD;
@ -554,7 +556,9 @@ function eventQueue(aEventType)
styledNode.textContent = "matched";
// Dump matched events into console.
dump("\n*****\nEQ matched: " + eventTypeToString(currType) + "\n*****\n");
if (gA11yEventDumpToConsole)
dump("\n*****\nEQ matched: " + eventTypeToString(currType) + "\n*****\n");
} else {
styledNode.textContent = "expected";
}
@ -932,6 +936,9 @@ var gA11yEventObserver =
info += ". Listeners count: " + listenersArray.length;
eventFromDumpArea = false;
if (gA11yEventDumpToConsole)
dump("\n" + info + "\n");
dumpInfoToDOM(info);
}
}

View File

@ -54,7 +54,8 @@ _TEST_FILES =\
test_aria_statechange.html \
test_attrs.html \
test_caretmove.html \
$(warning test_coalescence.html temporarily disabled) \
test_coalescence.html \
test_contextmenu.html \
test_docload.html \
test_docload.xul \
test_dragndrop.html \

View File

@ -364,6 +364,11 @@
href="https://bugzilla.mozilla.org/show_bug.cgi?id=513213"
title="coalesce events when new event is appended to the queue">
Mozilla Bug 513213
</a><br>
<a target="_blank"
title="Rework accessible tree update code"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275">
Mozilla Bug 570275
</a>
<p id="display"></p>

View File

@ -0,0 +1,141 @@
<html>
<head>
<title>Context menu tests</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../role.js"></script>
<script type="application/javascript"
src="../states.js"></script>
<script type="application/javascript"
src="../events.js"></script>
<script type="application/javascript">
////////////////////////////////////////////////////////////////////////////
// Invokers
function showContextMenu(aID)
{
this.DOMNode = getNode(aID);
this.eventSeq = [
new invokerChecker(EVENT_MENUPOPUP_START, getContextMenuNode()),
];
this.invoke = function showContextMenu_invoke()
{
synthesizeMouse(this.DOMNode, 4, 4, { type: "contextmenu", button: 2 });
}
this.getID = function showContextMenu_getID()
{
return "show context menu";
}
}
function selectMenuItem()
{
this.eventSeq = [
new invokerChecker(EVENT_FOCUS, getFocusedMenuItem)
];
this.invoke = function selectMenuItem_invoke()
{
synthesizeKey("VK_DOWN", { });
}
this.getID = function selectMenuItem_getID()
{
return "select first menuitem";
}
}
function closeContextMenu(aID)
{
this.eventSeq = [
new invokerChecker(EVENT_MENUPOPUP_END,
getAccessible(getContextMenuNode()))
];
this.invoke = function closeContextMenu_invoke()
{
synthesizeKey("VK_ESCAPE", { });
}
this.getID = function closeContextMenu_getID()
{
return "close context menu";
}
}
function getContextMenuNode()
{
return getRootAccessible().DOMDocument.
getElementById("contentAreaContextMenu");
}
function getFocusedMenuItem()
{
var menu = getAccessible(getAccessible(getContextMenuNode()));
for (var idx = 0; idx < menu.childCount; idx++) {
var item = menu.getChildAt(idx);
if (hasState(item, STATE_FOCUSED))
return getAccessible(item, [nsIAccessNode]);
}
return null;
}
////////////////////////////////////////////////////////////////////////////
// Do tests
var gQueue = null;
//gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpToConsole = true;
function doTests()
{
gQueue = new eventQueue();
gQueue.push(new showContextMenu("input"));
gQueue.push(new selectMenuItem());
gQueue.push(new closeContextMenu());
gQueue.invoke(); // Will call SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTests);
</script>
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=580535"
title="Broken accessibility in context menus">
Mozilla Bug 580535
</a><br>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<input id="input">
<div id="eventdump"></div>
</body>
</html>

View File

@ -58,11 +58,34 @@
}
}
function showNFocusAlertDialog()
{
this.ID = "alertdialog";
this.DOMNode = getNode(this.ID);
this.invoke = function showNFocusAlertDialog_invoke()
{
document.getElementById(this.ID).style.display = 'block';
document.getElementById(this.ID).focus();
todo(false, "Enable show event handling when bug 422744 is fixed.");
}
this.eventSeq = [
new invokerChecker(EVENT_FOCUS, this.DOMNode),
// new invokerChecker(EVENT_SHOW, this.DOMNode)
];
this.getID = function showNFocusAlertDialog_getID()
{
return "Show and focus alert dialog " + prettyName(this.ID);
}
}
/**
* Do tests.
*/
// gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpID = "eventdump"; // debug stuff
var gQueue = null;
@ -82,6 +105,8 @@
gQueue.push(new focusElmWhileSubdocIsFocused("button"));
gQueue.push(new showNFocusAlertDialog());
gQueue.invoke(); // Will call SimpleTest.finish();
}
@ -102,6 +127,11 @@
title=" Inconsistent focus events when returning to a document frame">
Mozilla Bug 352220
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=580464"
title="Accessible focus incorrect after JS focus() but correct after switching apps or using menu bar">
Mozilla Bug 580464
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
@ -112,6 +142,13 @@
<button id="button">button</button>
<iframe id="editabledoc" src="focus.html"></iframe>
<div id="alertdialog" style="display: none" tabindex="-1" role="alertdialog" aria-labelledby="title2" aria-describedby="desc2">
<div id="title2">Blah blah</div>
<div id="desc2">Woof woof woof.</div>
<button>Close</button>
</div>
<div id="eventdump"></div>
</body>
</html>

View File

@ -230,16 +230,17 @@
this.invoke = function cloneAndReplaceInDOM_invoke()
{
var newElm = this.DOMNode.cloneNode(true);
newElm.removeAttribute('id');
this.eventSeq[1][1] = newElm;
this.DOMNode.parentNode.replaceChild(newElm, this.DOMNode);
this.DOMNode.parentNode.replaceChild(this.newElm, this.DOMNode);
}
this.getID = function cloneAndReplaceInDOM_getID()
{
return aNodeOrID + " clone and replace in DOM.";
}
this.newElm = this.DOMNode.cloneNode(true);
this.newElm.removeAttribute('id');
this.setTarget(kShowEvent, this.newElm);
}
/**
@ -279,7 +280,7 @@
* Do tests.
*/
var gQueue = null;
// gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpID = "eventdump"; // debug stuff
function doTests()
{
@ -292,13 +293,6 @@
gQueue.push(new changeStyle(id, "display", "none", kHideEvents));
gQueue.push(new changeStyle(id, "display", "inline", kShowEvents));
// Show/hide events by changing of visibility style of accessible DOM node
// from 'visible' to 'hidden', 'hidden' to 'visible'.
var id = "link2";
getAccessible(id);
gQueue.push(new changeStyle(id, "visibility", "hidden", kHideEvents));
gQueue.push(new changeStyle(id, "visibility", "visible", kShowEvents));
// Show/hide events by changing of display style of accessible DOM node
// from 'inline' to 'block', 'block' to 'inline'.
var id = "link3";
@ -306,12 +300,6 @@
gQueue.push(new changeStyle(id, "display", "block", kHideAndShowEvents));
gQueue.push(new changeStyle(id, "display", "inline", kHideAndShowEvents));
// Show/hide events by changing of visibility style of accessible DOM node
// from 'collapse' to 'visible', 'visible' to 'collapse'.
var id = "link4";
gQueue.push(new changeStyle(id, "visibility", "visible", kShowEvents));
gQueue.push(new changeStyle(id, "visibility", "collapse", kHideEvents));
// Show/hide events by adding new accessible DOM node and removing old one.
var id = "link5";
gQueue.push(new cloneAndAppendToDOM(id));
@ -347,16 +335,13 @@
// Show/hide events by creating new accessible DOM node and replacing
// old one.
// XXX: bug 472810
// gQueue.push(new cloneAndReplaceInDOM("link6"));
getAccessible("link6"); // ensure accessible is created
gQueue.push(new cloneAndReplaceInDOM("link6"));
// Show/hide events by changing class name on the parent node.
gQueue.push(new changeClass("container2", "link7", "", kShowEvents));
gQueue.push(new changeClass("container2", "link7", "displayNone",
kHideEvents));
gQueue.push(new changeClass("container3", "link8", "", kShowEvents));
gQueue.push(new changeClass("container3", "link8", "visibilityHidden",
kHideEvents));
gQueue.invoke(); // Will call SimpleTest.finish();
}
@ -372,11 +357,16 @@
href="https://bugzilla.mozilla.org/show_bug.cgi?id=469985"
title=" turn the test from bug 354745 into mochitest">
Mozilla Bug 469985
</a>
</a><br>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=472662"
title="no reorder event when html:link display property is changed from 'none' to 'inline'">
Mozilla Bug 472662
</a><br>
<a target="_blank"
title="Rework accessible tree update code"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275">
Mozilla Bug 570275
</a>
<p id="display"></p>

View File

@ -84,22 +84,36 @@
/**
* Insert inaccessible child node containing accessibles.
*/
function insertChildSpan(aID)
function insertChildSpan(aID, aInsertAllTogether)
{
this.__proto__ = new textInsertInvoker(aID, 0, 5, "33322");
this.invoke = function insertChildSpan_invoke()
{
// <span><span>333</span><span>22</span></span>
var topSpan = document.createElement("span");
var fSpan = document.createElement("span");
fSpan.textContent = "333";
topSpan.appendChild(fSpan);
var sSpan = document.createElement("span");
sSpan.textContent = "22";
topSpan.appendChild(sSpan);
if (aInsertAllTogether) {
var topSpan = document.createElement("span");
var fSpan = document.createElement("span");
fSpan.textContent = "333";
topSpan.appendChild(fSpan);
var sSpan = document.createElement("span");
sSpan.textContent = "22";
topSpan.appendChild(sSpan);
this.DOMNode.insertBefore(topSpan, this.DOMNode.childNodes[0]);
this.DOMNode.insertBefore(topSpan, this.DOMNode.childNodes[0]);
} else {
var topSpan = document.createElement("span");
this.DOMNode.insertBefore(topSpan, this.DOMNode.childNodes[0]);
var fSpan = document.createElement("span");
fSpan.textContent = "333";
topSpan.appendChild(fSpan);
var sSpan = document.createElement("span");
sSpan.textContent = "22";
topSpan.appendChild(sSpan);
}
}
this.getID = function insertChildSpan_getID()
@ -271,7 +285,8 @@
// Text remove event on inaccessible child HTML span removal containing
// accessible text nodes.
gQueue.push(new removeChildSpan("p"));
gQueue.push(new insertChildSpan("p"));
gQueue.push(new insertChildSpan("p"), true);
gQueue.push(new insertChildSpan("p"), false);
// Remove embedded character.
gQueue.push(new removeChildDiv("div"));
@ -323,6 +338,11 @@
title="Cache text offsets within hypertext accessible">
Mozilla Bug 575052
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275"
title="Rework accessible tree update code">
Mozilla Bug 570275
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>

View File

@ -15,6 +15,7 @@ const ROLE_COMBOBOX = nsIAccessibleRole.ROLE_COMBOBOX;
const ROLE_COMBOBOX_LIST = nsIAccessibleRole.ROLE_COMBOBOX_LIST;
const ROLE_COMBOBOX_OPTION = nsIAccessibleRole.ROLE_COMBOBOX_OPTION;
const ROLE_COLUMNHEADER = nsIAccessibleRole.ROLE_COLUMNHEADER;
const ROLE_DIALOG = nsIAccessibleRole.ROLE_DIALOG;
const ROLE_DOCUMENT = nsIAccessibleRole.ROLE_DOCUMENT;
const ROLE_EMBEDDED_OBJECT = nsIAccessibleRole.ROLE_EMBEDDED_OBJECT;
const ROLE_ENTRY = nsIAccessibleRole.ROLE_ENTRY;
@ -100,3 +101,23 @@ function getRole(aAccOrElmOrID)
return role;
}
/**
* Analogy of SimpleTest.is function used to check the role.
*/
function isRole(aIdentifier, aRole, aMsg)
{
var role = getRole(aIdentifier);
if (role == - 1)
return;
if (role == aRole) {
ok(true, aMsg);
return;
}
var got = roleToString(role);
var expected = roleToString(aRole);
ok(false, aMsg + "got '" + got + "', expected '" + expected + "'");
}

View File

@ -189,6 +189,16 @@ function getStates(aAccOrElmOrID)
return [state.value, extraState.value];
}
/**
* Return true if the accessible has given states.
*/
function hasState(aAccOrElmOrID, aState, aExtraState)
{
var [state, exstate] = getStates(aAccOrElmOrID);
return (aState ? state & aState : true) &&
(aExtraState ? exstate & aExtraState : true);
}
////////////////////////////////////////////////////////////////////////////////
// Private implementation details

View File

@ -68,7 +68,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=452161
var frame = document.getElementById("frame");
frame.contentDocument.designMode = "on";
testEditable(frame.contentDocument);
SimpleTest.finish();
}

View File

@ -30,26 +30,22 @@
function focusAnchor(aID)
{
this.DOMNode = getNode(aID);
this.link = getAccessible(this.DOMNode);
this.linkChild = this.link.firstChild;
this.linkChildNode = getAccessible(this.linkChild, [nsIAccessNode]).DOMNode;
this.linkNode = getNode(aID);
this.link = getAccessible(this.linkNode);
this.eventSeq = [
// new invokerChecker(EVENT_HIDE, this.linkChild),
// new invokerChecker(EVENT_SHOW, getAccessible, this.linkChildNode),
new invokerChecker(EVENT_FOCUS, this.link)
];
this.unexpectedEventSeq = [
new invokerChecker(EVENT_HIDE, this.link),
new invokerChecker(EVENT_SHOW, getAccessible, this.DOMNode)
new invokerChecker(EVENT_FOCUS, getAccessible, this.linkNode)
];
this.invoke = function focusAnchor_invoke()
{
todo(false, "enable event hide/show events");
getNode(aID).focus();
this.linkNode.focus();
}
this.check = function focusAnchor_check(aEvent)
{
isnot(this.link, aEvent.accessible,
"Focus should be fired against new link accessible!");
}
this.getID = function focusAnchor_getID()
@ -58,34 +54,27 @@
}
}
function tabAnchor(aID, aPrevID)
function tabAnchor(aID)
{
this.DOMNode = getNode(aID);
this.link = getAccessible(this.DOMNode);
this.linkChild = this.link.firstChild;
this.linkChildNode = getAccessible(this.linkChild, [nsIAccessNode]).DOMNode;
this.prevLink = getAccessible(aPrevID);
this.prevLinkNode = getAccessible(this.prevLink, [nsIAccessNode]).DOMNode;
this.linkNode = getNode(aID);
this.link = getAccessible(this.linkNode);
this.eventSeq = [
new invokerChecker(EVENT_HIDE, this.prevLink),
new invokerChecker(EVENT_SHOW, getAccessible, this.prevLinkNode),
new invokerChecker(EVENT_HIDE, this.linkChild),
new invokerChecker(EVENT_SHOW, getAccessible, this.linkChildNode),
new invokerChecker(EVENT_FOCUS, this.link)
new invokerChecker(EVENT_FOCUS, getAccessible, this.linkNode)
];
this.unexpectedEventSeq = [
new invokerChecker(EVENT_HIDE, this.link),
new invokerChecker(EVENT_SHOW, getAccessible, this.DOMNode)
];
this.invoke = function focusAnchor_invoke()
this.invoke = function tabAnchor_invoke()
{
synthesizeKey("VK_TAB", { shiftKey: false });
}
this.getID = function focusAnchor_getID()
this.check = function tabAnchor_check(aEvent)
{
isnot(this.link, aEvent.accessible,
"Focus should be fired against new link accessible!");
}
this.getID = function tabAnchor_getID()
{
return "focus a:focus{overflow:scroll} #2";
}
@ -95,20 +84,20 @@
// Do tests
var gQueue = null;
// gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpToConsole = true;
function doTests()
{
gQueue = new eventQueue();
// CSS 'overflow: scroll' property setting and unsetting causes accessible
// recreation (and fire show/hide events) if the accessible is not
// focused. If it's focused its children are recreated. For example,
// focusing the HTML:a with ':focus {overflow: scroll; }' CSS style
// shouldn't cause of HTML:a accessible recreation. The same time blur
// makes its accessible to be recreated.
gQueue.push(new focusAnchor("a"))
gQueue.push(new tabAnchor("a2", "a"));
// recreation (and fire show/hide events). For example, the focus and
// blur of HTML:a with ':focus {overflow: scroll; }' CSS style causes its
// accessible recreation. The focus event should be fired on new
// accessible.
gQueue.push(new focusAnchor("a"));
gQueue.push(new tabAnchor("a2"));
gQueue.invoke(); // Will call SimpleTest.finish();
}
@ -125,6 +114,11 @@
title="mochitest for bug 413777: focus the a:focus {overflow: scroll;} shouldn't recreate HTML a accessible">
Mozilla Bug 591163
</a><br>
<a target="_blank"
title="Rework accessible tree update code"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275">
Mozilla Bug 570275
</a><br>
<p id="display"></p>
<div id="content" style="display: none"></div>

View File

@ -70,6 +70,9 @@
var tabsAccTree = {
role: ROLE_PAGETABLIST,
children: [
{
role: ROLE_PUSHBUTTON // tab scroll up button
},
{
role: ROLE_PAGETAB,
children: [
@ -88,6 +91,9 @@
},
{
role: ROLE_PUSHBUTTON
},
{
role: ROLE_PUSHBUTTON // tab scroll down button
}
]
};

View File

@ -48,6 +48,8 @@ include $(topsrcdir)/config/rules.mk
_TEST_FILES =\
test_list_editabledoc.html \
test_list.html \
test_recreation.html \
test_tableinsubtree.html \
$(NULL)
libs:: $(_TEST_FILES)

View File

@ -78,10 +78,6 @@
function doTest()
{
todo(false, "Enable when bug 570275 is fixed");
SimpleTest.finish();
return;
gQueue = new eventQueue();
gQueue.push(new addLi("list"));

View File

@ -0,0 +1,166 @@
<!DOCTYPE html>
<html>
<head>
<title>Test accessible recreation</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/MochiKit/packed.js"></script>
<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">
////////////////////////////////////////////////////////////////////////////
// Invokers
function recreateAccessible(aID, aWontBeAccessible)
{
this.node = getNode(aID);
this.accessible =
isAccessible(this.node) ? getAccessible(this.node) : null;
this.eventSeq = [ ];
if (this.accessible)
this.eventSeq.push(new invokerChecker(EVENT_HIDE,
this.accessible));
if (!aWontBeAccessible)
this.eventSeq.push(new invokerChecker(EVENT_SHOW, getAccessible,
this.node));
this.eventSeq.push(new invokerChecker(EVENT_REORDER,
getContainerAccessible(this.node)));
if (this.accessible) {
this.unexpectedEventSeq = [
new invokerChecker(EVENT_SHOW, this.accessible)
];
}
}
function changeAttr(aID, aAttr, aValue)
{
this.__proto__ = new recreateAccessible(aID);
this.invoke = function changeAttr_invoke()
{
this.node.setAttribute(aAttr, aValue);
}
this.getID = function changeAttr_getID()
{
return "change " + aAttr + "attribute for " + aID;
}
}
function removeAttr(aID, aAttr)
{
this.__proto__ = new recreateAccessible(aID, true);
this.invoke = function remvoeAttr_invoke()
{
this.node.removeAttribute(aAttr);
}
this.getID = function remvoeAttr_getID()
{
return "remove " + aAttr + "attribute for " + aID;
}
}
function changeRole(aID, aHasAccessible)
{
this.__proto__ = new changeAttr(aID, "role", "button");
}
function removeRole(aID)
{
this.__proto__ = new removeAttr(aID, "role");
}
function changeOnclick(aID)
{
this.__proto__ = new changeAttr(aID, "onclick", "alert(3);");
}
function changeHref(aID)
{
this.__proto__ = new changeAttr(aID, "href", "www");
}
function changeMultiselectable(aID)
{
this.__proto__ = new changeAttr(aID, "aria-multiselectable", "true");
}
////////////////////////////////////////////////////////////////////////////
// Test
//gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpToConsole = true;
var gQueue = null;
function doTest()
{
gQueue = new eventQueue();
// make the accessible an inaccessible
gQueue.push(new changeRole("span"));
// make the inaccessible an accessible
gQueue.push(new removeRole("span"));
// recreate an accessible by role change
gQueue.push(new changeRole("div1"));
// recreate an accessible by onclick change
gQueue.push(new changeOnclick("div2"));
// recreate an accessible by href change
gQueue.push(new changeHref("anchor"));
// recreate an accessible by aria-multiselectable change
gQueue.push(new changeMultiselectable("div3"));
gQueue.invoke(); // SimpleTest.finish() will be called in the end
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank"
title="Rework accessible tree update code"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275">
Mozilla Bug 570275
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<span id="span">span</span>
<div id="div1">div</div>
<div id="div2">div</div>
<a id="anchor">anchor</a>
<div id="div3" role="listbox">list</div>
<div id="eventdump"></div>
</body>
</html>

View File

@ -0,0 +1,120 @@
<!DOCTYPE html>
<html>
<head>
<title>Table creation in ARIA dialog test</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/MochiKit/packed.js"></script>
<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">
////////////////////////////////////////////////////////////////////////////
// Invokers
function showARIADialog(aID)
{
this.node = getNode(aID);
this.eventSeq = [
new invokerChecker(EVENT_SHOW, this.node)
];
this.invoke = function showARIADialog_invoke()
{
this.node.style.display = "block";
getNode("input").value = "hello";
getNode("cell").textContent = "cell1";
getNode("input").focus();
}
this.finalCheck = function showARIADialog_finalCheck()
{
var tree = {
role: ROLE_DIALOG,
children: [
{
role: ROLE_TABLE,
children: [
{
role: ROLE_ROW,
children: [
{
role: ROLE_CELL,
children: [ { role: ROLE_TEXT_LEAF } ]
},
{
role: ROLE_CELL,
children: [ { role: ROLE_ENTRY } ]
}
]
}
]
}
]
};
testAccessibleTree(aID, tree);
}
this.getID = function showARIADialog_getID()
{
return "show ARIA dialog";
}
}
////////////////////////////////////////////////////////////////////////////
// Test
//gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpToConsole = true;
var gQueue = null;
function doTest()
{
gQueue = new eventQueue();
// make the accessible an inaccessible
gQueue.push(new showARIADialog("dialog"));
gQueue.invoke(); // SimpleTest.finish() will be called in the end
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank"
title="Rework accessible tree update code"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275">
Mozilla Bug 570275
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<div id="dialog" role="dialog" style="display: none;">
<table>
<tr><td id="cell"></td><td><input id="input"></td>
</table>
</div>
<div id="eventdump"></div>
</body>
</html>

View File

@ -140,11 +140,6 @@
#include "nsIXULDocument.h"
#endif /* MOZ_XUL */
#ifdef ACCESSIBILITY
#include "nsIAccessibilityService.h"
#include "nsIAccessibleEvent.h"
#endif /* ACCESSIBILITY */
#include "nsCycleCollectionParticipant.h"
#include "nsCCUncollectableMarker.h"
@ -3651,21 +3646,6 @@ nsINode::doRemoveChildAt(PRUint32 aIndex, PRBool aNotify,
PRBool aMutationEvent)
{
nsIDocument* doc = GetCurrentDoc();
#ifdef ACCESSIBILITY
// A11y needs to be notified of content removals first, so accessibility
// events can be fired before any changes occur
if (aNotify && doc) {
nsIPresShell *presShell = doc->GetShell();
if (presShell && presShell->IsAccessibilityActive()) {
nsCOMPtr<nsIAccessibilityService> accService =
do_GetService("@mozilla.org/accessibilityService;1");
if (accService) {
accService->InvalidateSubtreeFor(presShell, aKid,
nsIAccessibilityService::NODE_REMOVE);
}
}
}
#endif
nsMutationGuard::DidMutate();

View File

@ -130,7 +130,6 @@
#endif
#ifdef ACCESSIBILITY
#include "nsIAccessibilityService.h"
#include "nsIAccessibleEvent.h"
#endif
#include "nsInlineFrame.h"
@ -6713,6 +6712,17 @@ nsCSSFrameConstructor::ContentAppended(nsIContent* aContainer,
}
#endif
#ifdef ACCESSIBILITY
if (mPresShell->IsAccessibilityActive()) {
nsCOMPtr<nsIAccessibilityService> accService =
do_GetService("@mozilla.org/accessibilityService;1");
if (accService) {
accService->ContentRangeInserted(mPresShell, aContainer,
aFirstNewContent, nsnull);
}
}
#endif
return NS_OK;
}
@ -7283,6 +7293,17 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
}
#endif
#ifdef ACCESSIBILITY
if (mPresShell->IsAccessibilityActive()) {
nsCOMPtr<nsIAccessibilityService> accService =
do_GetService("@mozilla.org/accessibilityService;1");
if (accService) {
accService->ContentRangeInserted(mPresShell, aContainer,
aStartChild, aEndChild);
}
}
#endif
return NS_OK;
}
@ -7414,7 +7435,17 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer,
LAYOUT_PHASE_TEMP_REENTER();
return rv;
}
#ifdef ACCESSIBILITY
if (mPresShell->IsAccessibilityActive()) {
nsCOMPtr<nsIAccessibilityService> accService =
do_GetService("@mozilla.org/accessibilityService;1");
if (accService) {
accService->ContentRemoved(mPresShell, aContainer, aChild);
}
}
#endif
// Examine the containing-block for the removed content and see if
// :first-letter style applies.
nsIFrame* inflowChild = childFrame;
@ -9080,28 +9111,6 @@ nsCSSFrameConstructor::RecreateFramesForContent(nsIContent* aContent,
}
}
#ifdef ACCESSIBILITY
if (mPresShell->IsAccessibilityActive()) {
PRUint32 changeType;
if (frame) {
nsIFrame *newFrame = aContent->GetPrimaryFrame();
changeType = newFrame ? nsIAccessibilityService::FRAME_SIGNIFICANT_CHANGE :
nsIAccessibilityService::FRAME_HIDE;
}
else {
changeType = nsIAccessibilityService::FRAME_SHOW;
}
// A significant enough change occurred that this part
// of the accessible tree is no longer valid.
nsCOMPtr<nsIAccessibilityService> accService =
do_GetService("@mozilla.org/accessibilityService;1");
if (accService) {
accService->InvalidateSubtreeFor(mPresShell, aContent, changeType);
}
}
#endif
return rv;
}

View File

@ -95,10 +95,6 @@
#include "RestyleTracker.h"
#include "nsFrameManager.h"
#ifdef ACCESSIBILITY
#include "nsIAccessibilityService.h"
#include "nsIAccessibleEvent.h"
#endif
#ifdef DEBUG
//#define NOISY_DEBUG
@ -1004,7 +1000,6 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext,
nsStyleChangeList *aChangeList,
nsChangeHint aMinChange,
nsRestyleHint aRestyleHint,
PRBool aFireAccessibilityEvents,
RestyleTracker& aRestyleTracker)
{
if (!NS_IsHintSubset(nsChangeHint_NeedDirtyReflow, aMinChange)) {
@ -1047,9 +1042,6 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext,
// XXXbz oldContext should just be an nsRefPtr
nsStyleContext* oldContext = aFrame->GetStyleContext();
nsStyleSet* styleSet = aPresContext->StyleSet();
#ifdef ACCESSIBILITY
PRBool isVisible = aFrame->GetStyleVisibility()->IsVisible();
#endif
// XXXbz the nsIFrame constructor takes an nsStyleContext, so how
// could oldContext be null?
@ -1105,12 +1097,10 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext,
// style context provider will be automatically propagated to
// the frame(s) with child style contexts.
// Accessibility: we don't need to fire a11y events for child provider
// frame because it is visible or hidden withitn this frame.
assumeDifferenceHint = ReResolveStyleContext(aPresContext, providerFrame,
aParentContent, aChangeList,
aMinChange, aRestyleHint,
PR_FALSE, aRestyleTracker);
aRestyleTracker);
// The provider's new context becomes the parent context of
// aFrame's context.
@ -1408,32 +1398,6 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext,
}
}
PRBool fireAccessibilityEvents = aFireAccessibilityEvents;
#ifdef ACCESSIBILITY
if (fireAccessibilityEvents && mPresShell->IsAccessibilityActive() &&
aFrame->GetStyleVisibility()->IsVisible() != isVisible &&
!aFrame->GetPrevContinuation()) {
// A significant enough change occurred that this part
// of the accessible tree is no longer valid. Fire event for primary
// frames only and if it wasn't fired for parent frame already.
// XXX: bug 355521. Visibility does not affect descendents with
// visibility set. Work on a separate, accurate mechanism for dealing with
// visibility changes.
nsCOMPtr<nsIAccessibilityService> accService =
do_GetService("@mozilla.org/accessibilityService;1");
if (accService) {
PRUint32 changeType = isVisible ?
nsIAccessibilityService::FRAME_HIDE :
nsIAccessibilityService::FRAME_SHOW;
accService->InvalidateSubtreeFor(mPresShell, aFrame->GetContent(),
changeType);
fireAccessibilityEvents = PR_FALSE;
}
}
#endif
if (!(aMinChange & nsChangeHint_ReconstructFrame)) {
// There is no need to waste time crawling into a frame's children on a frame change.
@ -1478,7 +1442,6 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext,
NS_SubtractHint(aMinChange,
nsChangeHint_ReflowFrame),
childRestyleHint,
fireAccessibilityEvents,
aRestyleTracker);
} while (outOfFlowFrame = outOfFlowFrame->GetNextContinuation());
@ -1487,7 +1450,6 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext,
ReResolveStyleContext(aPresContext, child, content,
aChangeList, aMinChange,
childRestyleHint,
fireAccessibilityEvents,
aRestyleTracker);
}
else { // regular child frame
@ -1495,7 +1457,6 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext,
ReResolveStyleContext(aPresContext, child, content,
aChangeList, aMinChange,
childRestyleHint,
fireAccessibilityEvents,
aRestyleTracker);
} else {
NOISY_TRACE_FRAME("child frame already resolved as descendant, skipping",aFrame);
@ -1548,7 +1509,6 @@ nsFrameManager::ComputeStyleChangeFor(nsIFrame *aFrame,
aChangeList, topLevelChange,
aRestyleDescendants ?
eRestyle_Subtree : eRestyle_Self,
PR_TRUE,
aRestyleTracker);
NS_UpdateHint(topLevelChange, frameChange);

View File

@ -214,7 +214,6 @@ private:
nsStyleChangeList *aChangeList,
nsChangeHint aMinChange,
nsRestyleHint aRestyleHint,
PRBool aFireAccessibilityEvents,
RestyleTracker& aRestyleTracker);
};

View File

@ -1189,18 +1189,6 @@ protected:
PRPackedBool mIsActive;
PRPackedBool mFrozen;
#ifdef ACCESSIBILITY
/**
* Call this when there have been significant changes in the rendering for
* a content subtree, so the matching accessibility subtree can be invalidated
*/
void InvalidateAccessibleSubtree(nsIContent *aContent);
#endif
// Set to true when the accessibility service is being used to mirror
// the dom/layout trees
PRPackedBool mIsAccessibilityActive;
PRPackedBool mObservesMutationsForPrint;
PRPackedBool mReflowScheduled; // If true, we have a reflow

View File

@ -3657,20 +3657,6 @@ PresShell::CancelAllPendingReflows()
ASSERT_REFLOW_SCHEDULED_STATE();
}
#ifdef ACCESSIBILITY
void nsIPresShell::InvalidateAccessibleSubtree(nsIContent *aContent)
{
if (gIsAccessibilityActive) {
nsCOMPtr<nsIAccessibilityService> accService =
do_GetService("@mozilla.org/accessibilityService;1");
if (accService) {
accService->InvalidateSubtreeFor(this, aContent,
nsIAccessibilityService::FRAME_SIGNIFICANT_CHANGE);
}
}
}
#endif
nsresult
PresShell::RecreateFramesFor(nsIContent* aContent)
{
@ -3704,9 +3690,6 @@ PresShell::RecreateFramesFor(nsIContent* aContent)
--mChangeNestCount;
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
#ifdef ACCESSIBILITY
InvalidateAccessibleSubtree(aContent);
#endif
return rv;
}
@ -5179,10 +5162,6 @@ nsIPresShell::ReconstructStyleDataInternal()
}
mFrameConstructor->PostRestyleEvent(root, eRestyle_Subtree, NS_STYLE_HINT_NONE);
#ifdef ACCESSIBILITY
InvalidateAccessibleSubtree(nsnull);
#endif
}
void
@ -8063,9 +8042,6 @@ PresShell::Observe(nsISupports* aSubject,
}
}
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
#ifdef ACCESSIBILITY
InvalidateAccessibleSubtree(nsnull);
#endif
}
return NS_OK;
}

View File

@ -6519,8 +6519,12 @@ nsIFrame::IsFocusable(PRInt32 *aTabIndex, PRBool aWithMouse)
// will be enough to make them keyboard scrollable.
nsIScrollableFrame *scrollFrame = do_QueryFrame(this);
if (scrollFrame) {
nsMargin margin = scrollFrame->GetActualScrollbarSizes();
if (margin.top || margin.right || margin.bottom || margin.left) {
nsIScrollableFrame::ScrollbarStyles styles =
scrollFrame->GetScrollbarStyles();
if (styles.mVertical == NS_STYLE_OVERFLOW_SCROLL ||
styles.mVertical == NS_STYLE_OVERFLOW_AUTO ||
styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL ||
styles.mHorizontal == NS_STYLE_OVERFLOW_AUTO) {
// Scroll bars will be used for overflow
isFocusable = PR_TRUE;
tabIndex = 0;

View File

@ -2282,6 +2282,16 @@ nsObjectFrame::Instantiate(nsIChannel* aChannel, nsIStreamListener** aStreamList
"Instantiation should still be prevented!");
mPreventInstantiation = PR_FALSE;
#ifdef ACCESSIBILITY
if (PresContext()->PresShell()->IsAccessibilityActive()) {
nsCOMPtr<nsIAccessibilityService> accService =
do_GetService("@mozilla.org/accessibilityService;1");
if (accService) {
accService->RecreateAccessible(PresContext()->PresShell(), mContent);
}
}
#endif
return rv;
}
@ -2340,6 +2350,16 @@ nsObjectFrame::Instantiate(const char* aMimeType, nsIURI* aURI)
NS_ASSERTION(mPreventInstantiation,
"Instantiation should still be prevented!");
#ifdef ACCESSIBILITY
if (PresContext()->PresShell()->IsAccessibilityActive()) {
nsCOMPtr<nsIAccessibilityService> accService =
do_GetService("@mozilla.org/accessibilityService;1");
if (accService) {
accService->RecreateAccessible(PresContext()->PresShell(), mContent);
}
}
#endif
mPreventInstantiation = PR_FALSE;
return rv;