Bug 545465 - don't keep rejected accessible in the cache, r=ginn.chen, davidb, a=blockingBetaN

This commit is contained in:
Alexander Surkov 2010-11-12 14:00:55 -05:00
parent 0ba5de8279
commit 082408ed24
14 changed files with 153 additions and 196 deletions

View File

@ -456,28 +456,29 @@ nsAccDocManager::CreateDocOrRootAccessible(nsIDocument *aDocument)
// We only create root accessibles for the true root, otherwise create a
// doc accessible.
nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(presShell));
nsDocAccessible *docAcc = isRootDoc ?
nsRefPtr<nsDocAccessible> docAcc = isRootDoc ?
new nsRootAccessibleWrap(aDocument, rootElm, weakShell) :
new nsDocAccessibleWrap(aDocument, rootElm, weakShell);
if (!docAcc)
// Cache the document accessible into document cache.
if (!docAcc || !mDocAccessibleCache.Put(aDocument, docAcc))
return nsnull;
// Cache and addref document accessible.
if (!mDocAccessibleCache.Put(aDocument, docAcc)) {
delete docAcc;
return nsnull;
}
// XXX: ideally we should initialize an accessible and then put it into tree,
// we can't since document accessible fires reorder event on its container
// while initialized.
if (!outerDocAcc->AppendChild(docAcc) ||
!GetAccService()->InitAccessible(docAcc, nsAccUtils::GetRoleMapEntry(aDocument))) {
// Bind the document accessible into tree.
if (!outerDocAcc->AppendChild(docAcc)) {
mDocAccessibleCache.Remove(aDocument);
return nsnull;
}
// Initialize the document accessible. Note, Init() should be called after
// the document accessible is bound to the tree.
if (!docAcc->Init()) {
docAcc->Shutdown();
mDocAccessibleCache.Remove(aDocument);
return nsnull;
}
docAcc->SetRoleMapEntry(nsAccUtils::GetRoleMapEntry(aDocument));
NS_LOG_ACCDOCCREATE("document creation finished", aDocument)
AddListeners(aDocument, isRootDoc);

View File

@ -837,28 +837,6 @@ nsAccessibilityService::GetCachedAccessibleOrContainer(nsINode* aNode)
return accessible;
}
PRBool
nsAccessibilityService::InitAccessible(nsAccessible *aAccessible,
nsRoleMapEntry *aRoleMapEntry)
{
if (!aAccessible)
return PR_FALSE;
// Add to cache an accessible, etc.
if (!aAccessible->Init()) {
NS_ERROR("Failed to initialize an accessible!");
aAccessible->Shutdown();
return PR_FALSE;
}
NS_ASSERTION(aAccessible->IsInCache(),
"Initialized accessible not in the cache!");
aAccessible->SetRoleMapEntry(aRoleMapEntry);
return PR_TRUE;
}
static PRBool HasRelatedContent(nsIContent *aContent)
{
nsAutoString id;
@ -958,6 +936,13 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
return areaAcc;
}
nsDocAccessible* docAcc =
GetAccService()->GetDocAccessible(aNode->GetOwnerDoc());
if (!docAcc) {
NS_NOTREACHED("No document for accessible being created!");
return nsnull;
}
// Attempt to create an accessible based on what we know.
nsRefPtr<nsAccessible> newAcc;
if (content->IsNodeOfType(nsINode::eTEXT)) {
@ -976,7 +961,7 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
}
if (weakFrame.IsAlive()) {
newAcc = weakFrame.GetFrame()->CreateAccessible();
if (InitAccessible(newAcc, nsnull))
if (docAcc->BindToDocument(newAcc, nsnull))
return newAcc.forget();
return nsnull;
}
@ -1004,7 +989,7 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
}
newAcc = new nsHyperTextAccessibleWrap(content, aWeakShell);
if (InitAccessible(newAcc, nsAccUtils::GetRoleMapEntry(aNode)))
if (docAcc->BindToDocument(newAcc, nsAccUtils::GetRoleMapEntry(aNode)))
return newAcc.forget();
return nsnull;
}
@ -1191,7 +1176,7 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
}
}
if (InitAccessible(newAcc, roleMapEntry))
if (docAcc->BindToDocument(newAcc, roleMapEntry))
return newAcc.forget();
return nsnull;
}
@ -1348,22 +1333,29 @@ nsAccessibilityService::GetAreaAccessible(nsIFrame* aImageFrame,
// Try to get image map accessible from the global cache or create it
// if failed.
nsRefPtr<nsAccessible> imageAcc =
GetCachedAccessible(aImageFrame->GetContent(), aWeakShell);
if (!imageAcc) {
imageAcc = CreateHTMLImageAccessible(aImageFrame->GetContent(),
aImageFrame->PresContext()->PresShell());
nsRefPtr<nsAccessible> image = GetCachedAccessible(aImageFrame->GetContent(),
aWeakShell);
if (!image) {
image = CreateHTMLImageAccessible(aImageFrame->GetContent(),
aImageFrame->PresContext()->PresShell());
if (!InitAccessible(imageAcc, nsnull))
nsDocAccessible* document =
GetAccService()->GetDocAccessible(aAreaNode->GetOwnerDoc());
if (!document) {
NS_NOTREACHED("No document for accessible being created!");
return nsnull;
}
if (!document->BindToDocument(image, nsnull))
return nsnull;
}
if (aImageAccessible)
*aImageAccessible = imageAcc;
*aImageAccessible = image;
// Make sure <area> accessible children of the image map are cached so
// that they should be available in global cache.
imageAcc->EnsureChildren();
image->EnsureChildren();
return GetCachedAccessible(aAreaNode, aWeakShell);
}
@ -1771,7 +1763,7 @@ nsAccessibilityService::AddNativeRootAccessible(void* aAtkAccessible)
if (!applicationAcc)
return nsnull;
nsNativeRootAccessibleWrap* nativeRootAcc =
nsRefPtr<nsNativeRootAccessibleWrap> nativeRootAcc =
new nsNativeRootAccessibleWrap((AtkObject*)aAtkAccessible);
if (!nativeRootAcc)
return nsnull;

View File

@ -202,19 +202,6 @@ public:
GetCachedAccessibleOrContainer(aNode->GetNodeParent()) : nsnull;
}
/**
* Initialize an accessible and cache it. The method should be called for
* every created accessible.
*
* @param aAccessible [in] accessible to initialize.
* @param aRoleMapEntry [in] the role map entry role the ARIA role or nsnull
* if none
*
* @return true if the accessible was initialized, otherwise false
*/
PRBool InitAccessible(nsAccessible *aAccessible,
nsRoleMapEntry *aRoleMapEntry);
protected:
/**
* Return an accessible for the DOM node in the given presentation shell if

View File

@ -2621,19 +2621,6 @@ nsAccessible::AppendTextTo(nsAString& aText, PRUint32 aStartOffset, PRUint32 aLe
////////////////////////////////////////////////////////////////////////////////
// nsAccessNode public methods
PRBool
nsAccessible::Init()
{
if (!nsAccessNodeWrap::Init())
return PR_FALSE;
nsDocAccessible* document =
GetAccService()->GetDocAccessible(mContent->GetOwnerDoc());
NS_ASSERTION(document, "Cannot cache new nsAccessible!");
return document ? document->CacheAccessible(this) : PR_FALSE;
}
void
nsAccessible::Shutdown()
{
@ -2912,20 +2899,6 @@ nsAccessible::GetIndexOfEmbeddedChild(nsAccessible* aChild)
return GetIndexOf(aChild);
}
#ifdef DEBUG
PRBool
nsAccessible::IsInCache()
{
nsDocAccessible *docAccessible =
GetAccService()->GetDocAccessible(mContent->GetOwnerDoc());
if (docAccessible)
return docAccessible->GetCachedAccessibleByUniqueID(UniqueID()) ? PR_TRUE : PR_FALSE;
return PR_FALSE;
}
#endif
////////////////////////////////////////////////////////////////////////////////
// HyperLinkAccessible methods

View File

@ -113,7 +113,6 @@ public:
//////////////////////////////////////////////////////////////////////////////
// nsAccessNode
virtual PRBool Init();
virtual void Shutdown();
//////////////////////////////////////////////////////////////////////////////
@ -298,13 +297,6 @@ public:
PRBool AreChildrenCached() const { return mChildrenFlags != eChildrenUninitialized; }
bool IsBoundToParent() const { return mParent; }
#ifdef DEBUG
/**
* Return true if the access node is cached.
*/
PRBool IsInCache();
#endif
//////////////////////////////////////////////////////////////////////////////
// Miscellaneous methods

View File

@ -599,31 +599,6 @@ nsDocAccessible::GetCachedAccessible(nsINode *aNode)
return accessible;
}
// nsDocAccessible public method
PRBool
nsDocAccessible::CacheAccessible(nsAccessible* aAccessible)
{
if (aAccessible->IsPrimaryForNode() &&
!mNodeToAccessibleMap.Put(aAccessible->GetNode(), aAccessible))
return PR_FALSE;
return mAccessibleCache.Put(aAccessible->UniqueID(), aAccessible);
}
// nsDocAccessible public method
void
nsDocAccessible::ShutdownAccessible(nsAccessible *aAccessible)
{
// Remove an accessible from node to accessible map if it is presented there.
if (aAccessible->IsPrimaryForNode() &&
mNodeToAccessibleMap.Get(aAccessible->GetNode()) == aAccessible)
mNodeToAccessibleMap.Remove(aAccessible->GetNode());
void* uniqueID = aAccessible->UniqueID();
aAccessible->Shutdown();
mAccessibleCache.Remove(uniqueID);
}
////////////////////////////////////////////////////////////////////////////////
// nsAccessNode
@ -1345,6 +1320,55 @@ nsDocAccessible::GetCachedAccessibleByUniqueIDInSubtree(void* aUniqueID)
return nsnull;
}
bool
nsDocAccessible::BindToDocument(nsAccessible* aAccessible,
nsRoleMapEntry* aRoleMapEntry)
{
if (!aAccessible)
return false;
// Put into DOM node cache.
if (aAccessible->IsPrimaryForNode() &&
!mNodeToAccessibleMap.Put(aAccessible->GetNode(), aAccessible))
return false;
// Put into unique ID cache.
if (!mAccessibleCache.Put(aAccessible->UniqueID(), aAccessible)) {
if (aAccessible->IsPrimaryForNode())
mNodeToAccessibleMap.Remove(aAccessible->GetNode());
return false;
}
// Initialize the accessible.
if (!aAccessible->Init()) {
NS_ERROR("Failed to initialize an accessible!");
UnbindFromDocument(aAccessible);
return false;
}
aAccessible->SetRoleMapEntry(aRoleMapEntry);
return true;
}
void
nsDocAccessible::UnbindFromDocument(nsAccessible* aAccessible)
{
// Remove an accessible from node to accessible map if it is presented there.
if (aAccessible->IsPrimaryForNode() &&
mNodeToAccessibleMap.Get(aAccessible->GetNode()) == aAccessible)
mNodeToAccessibleMap.Remove(aAccessible->GetNode());
#ifdef DEBUG
NS_ASSERTION(mAccessibleCache.GetWeak(aAccessible->UniqueID()),
"Illegitimate illegitimated accessible!");
#endif
void* uniqueID = aAccessible->UniqueID();
aAccessible->Shutdown();
mAccessibleCache.Remove(uniqueID);
}
void
nsDocAccessible::UpdateTree(nsIContent* aContainerNode,
nsIContent* aStartNode,
@ -1761,10 +1785,6 @@ nsDocAccessible::UncacheChildrenInSubtree(nsAccessible* aRoot)
void
nsDocAccessible::ShutdownChildrenInSubtree(nsAccessible* aAccessible)
{
#ifdef DEBUG
nsAccessible* incache = mAccessibleCache.GetWeak(aAccessible->UniqueID());
#endif
// Traverse through children and shutdown them before this accessible. When
// child gets shutdown then it removes itself from children array of its
//parent. Use jdx index to process the cases if child is not attached to the
@ -1780,6 +1800,6 @@ nsDocAccessible::ShutdownChildrenInSubtree(nsAccessible* aAccessible)
ShutdownChildrenInSubtree(child);
}
ShutdownAccessible(aAccessible);
UnbindFromDocument(aAccessible);
}

View File

@ -211,18 +211,18 @@ public:
nsAccessible* GetCachedAccessibleByUniqueIDInSubtree(void* aUniqueID);
/**
* Cache the accessible.
* Initialize the newly created accessible and put it into document caches.
*
* @param aAccessible [in] accessible to cache
*
* @return true if accessible being cached, otherwise false
* @param aAccessible [in] created accessible
* @param aRoleMapEntry [in] the role map entry role the ARIA role or nsnull
* if none
*/
PRBool CacheAccessible(nsAccessible *aAccessible);
bool BindToDocument(nsAccessible* aAccessible, nsRoleMapEntry* aRoleMapEntry);
/**
* Shutdown the accessible and remove it from document cache.
* Remove the existing accessible from document caches and shutdown it.
*/
void ShutdownAccessible(nsAccessible *aAccessible);
void UnbindFromDocument(nsAccessible* aAccessible);
/**
* Process the event when the queue of pending events is untwisted. Fire

View File

@ -40,6 +40,7 @@
#include "nsHTMLImageMapAccessible.h"
#include "nsAccUtils.h"
#include "nsDocAccessible.h"
#include "nsIDOMHTMLCollection.h"
#include "nsIServiceManager.h"
@ -114,6 +115,8 @@ nsHTMLImageMapAccessible::CacheChildren()
if (!mapAreas)
return;
nsDocAccessible* document = GetDocAccessible();
PRUint32 areaCount = 0;
mapAreas->GetLength(&areaCount);
@ -124,21 +127,13 @@ nsHTMLImageMapAccessible::CacheChildren()
return;
nsCOMPtr<nsIContent> areaContent(do_QueryInterface(areaNode));
nsRefPtr<nsAccessible> areaAcc =
nsRefPtr<nsAccessible> area =
new nsHTMLAreaAccessible(areaContent, mWeakShell);
if (!areaAcc)
return;
if (!areaAcc->Init()) {
areaAcc->Shutdown();
if (!document->BindToDocument(area, nsAccUtils::GetRoleMapEntry(areaContent)) ||
!AppendChild(area)) {
return;
}
// We must respect ARIA on area elements (for the canvas map technique)
areaAcc->SetRoleMapEntry(nsAccUtils::GetRoleMapEntry(areaContent));
if (!AppendChild(areaAcc))
return;
}
}

View File

@ -692,21 +692,17 @@ nsHTMLComboboxAccessible::CacheChildren()
if (!mListAccessible) {
mListAccessible =
new nsHTMLComboboxListAccessible(mParent, mContent, mWeakShell);
if (!mListAccessible)
return;
// Initialize and put into cache.
if (!mListAccessible->Init()) {
mListAccessible->Shutdown();
if (!GetDocAccessible()->BindToDocument(mListAccessible, nsnull))
return;
}
}
AppendChild(mListAccessible);
// Cache combobox option accessibles so that we build complete accessible tree
// for combobox.
mListAccessible->EnsureChildren();
if (AppendChild(mListAccessible)) {
// Cache combobox option accessibles so that we build complete accessible
// tree for combobox.
mListAccessible->EnsureChildren();
}
}
void

View File

@ -259,8 +259,7 @@ nsHTMLLIAccessible::
nsBlockFrame* blockFrame = do_QueryFrame(GetFrame());
if (blockFrame && !blockFrame->BulletIsEmptyExternal()) {
mBulletAccessible = new nsHTMLListBulletAccessible(mContent, mWeakShell);
if (mBulletAccessible)
mBulletAccessible->Init();
GetDocAccessible()->BindToDocument(mBulletAccessible, nsnull);
}
}

View File

@ -41,6 +41,7 @@
#include "nsAccUtils.h"
#include "nsAccTreeWalker.h"
#include "nsCoreUtils.h"
#include "nsDocAccessible.h"
#include "nsIDOMElement.h"
@ -171,5 +172,8 @@ nsXULColorPickerAccessible::CacheChildren()
AppendChild(child);
return;
}
// Unbind rejected accessibles from the document.
GetDocAccessible()->UnbindFromDocument(child);
}
}

View File

@ -43,6 +43,7 @@
#include "nsAccUtils.h"
#include "nsAccTreeWalker.h"
#include "nsCoreUtils.h"
#include "nsDocAccessible.h"
#include "nsRelUtils.h"
// NOTE: alphabetically ordered
@ -222,6 +223,10 @@ nsXULButtonAccessible::CacheChildren()
// for it. Ignore dropmarker button what is placed as a last child.
buttonAccessible.swap(child);
break;
} else {
// Unbind rejected accessible from document.
GetDocAccessible()->UnbindFromDocument(child);
}
}

View File

@ -472,23 +472,21 @@ nsXULTreeAccessible::GetTreeItemAccessible(PRInt32 aRow)
return nsnull;
void *key = reinterpret_cast<void*>(aRow);
nsRefPtr<nsAccessible> accessible = mAccessibleCache.GetWeak(key);
nsAccessible* cachedTreeItem = mAccessibleCache.GetWeak(key);
if (cachedTreeItem)
return cachedTreeItem;
if (!accessible) {
accessible = CreateTreeItemAccessible(aRow);
if (!accessible)
return nsnull;
nsRefPtr<nsAccessible> treeItem = CreateTreeItemAccessible(aRow);
if (treeItem) {
if (mAccessibleCache.Put(key, treeItem)) {
if (GetDocAccessible()->BindToDocument(treeItem, nsnull))
return treeItem;
if (!accessible->Init()) {
accessible->Shutdown();
return nsnull;
mAccessibleCache.Remove(key);
}
if (!mAccessibleCache.Put(key, accessible))
return nsnull;
}
return accessible;
return nsnull;
}
void
@ -501,22 +499,21 @@ nsXULTreeAccessible::InvalidateCache(PRInt32 aRow, PRInt32 aCount)
if (aCount > 0)
return;
nsDocAccessible* document = GetDocAccessible();
// Fire destroy event for removed tree items and delete them from caches.
for (PRInt32 rowIdx = aRow; rowIdx < aRow - aCount; rowIdx++) {
void* key = reinterpret_cast<void*>(rowIdx);
nsAccessible *accessible = mAccessibleCache.GetWeak(key);
nsAccessible* treeItem = mAccessibleCache.GetWeak(key);
if (accessible) {
if (treeItem) {
nsRefPtr<AccEvent> event =
new AccEvent(nsIAccessibleEvent::EVENT_HIDE, accessible);
new AccEvent(nsIAccessibleEvent::EVENT_HIDE, treeItem);
nsEventShell::FireEvent(event);
// Shutdown and remove accessible from document cache and tree cache.
nsDocAccessible *docAccessible = GetDocAccessible();
if (docAccessible)
docAccessible->ShutdownAccessible(accessible);
// Unbind from document, shutdown and remove from tree cache.
document->UnbindFromDocument(treeItem);
mAccessibleCache.Remove(key);
}
}
@ -534,14 +531,11 @@ nsXULTreeAccessible::InvalidateCache(PRInt32 aRow, PRInt32 aCount)
for (PRInt32 rowIdx = newRowCount; rowIdx < oldRowCount; ++rowIdx) {
void *key = reinterpret_cast<void*>(rowIdx);
nsAccessible *accessible = mAccessibleCache.GetWeak(key);
if (accessible) {
// Shutdown and remove accessible from document cache and tree cache.
nsDocAccessible *docAccessible = GetDocAccessible();
if (docAccessible)
docAccessible->ShutdownAccessible(accessible);
nsAccessible* treeItem = mAccessibleCache.GetWeak(key);
if (treeItem) {
// Unbind from document, shutdown and remove from tree cache.
document->UnbindFromDocument(treeItem);
mAccessibleCache.Remove(key);
}
}

View File

@ -41,6 +41,7 @@
#include "nsAccCache.h"
#include "nsAccessibilityService.h"
#include "nsAccUtils.h"
#include "nsDocAccessible.h"
#include "nsEventShell.h"
#include "nsITreeSelection.h"
@ -729,25 +730,23 @@ nsXULTreeGridRowAccessible::GetCellAccessible(nsITreeColumn* aColumn)
NS_PRECONDITION(aColumn, "No tree column!");
void* key = static_cast<void*>(aColumn);
nsRefPtr<nsAccessible> accessible = mAccessibleCache.GetWeak(key);
nsAccessible* cachedCell = mAccessibleCache.GetWeak(key);
if (cachedCell)
return cachedCell;
if (!accessible) {
accessible =
new nsXULTreeGridCellAccessibleWrap(mContent, mWeakShell, this, mTree,
mTreeView, mRow, aColumn);
if (!accessible)
return nsnull;
nsRefPtr<nsAccessible> cell =
new nsXULTreeGridCellAccessibleWrap(mContent, mWeakShell, this, mTree,
mTreeView, mRow, aColumn);
if (cell) {
if (mAccessibleCache.Put(key, cell)) {
if (GetDocAccessible()->BindToDocument(cell, nsnull))
return cell;
if (!accessible->Init()) {
accessible->Shutdown();
return nsnull;
mAccessibleCache.Remove(key);
}
if (!mAccessibleCache.Put(key, accessible))
return nsnull;
}
return accessible;
return nsnull;
}
void