Bug 204186. Make editor and Midas accessible via MSAA. r=kyle, sr=jst, a=asa

This commit is contained in:
aaronl%netscape.com 2003-05-15 08:37:38 +00:00
Родитель 8db1a1851b
Коммит 4b337314fb
13 изменённых файлов: 358 добавлений и 170 удалений

Просмотреть файл

@ -38,10 +38,13 @@ interface nsIAccessibleDocument : nsISupports
readonly attribute AString title;
readonly attribute AString mimeType;
readonly attribute AString docType;
readonly attribute boolean isEditable;
AString getNameSpaceURIForID(in short nameSpaceID);
readonly attribute nsIAccessibleCaret caretAccessible;
[noscript] readonly attribute voidPtr window;
[noscript] nsIAccessNode getCachedAccessNode(in voidPtr aUniqueID);
[noscript] void InvalidateCacheSubtree(in nsIDOMNode aStartNode);
[noscript] nsIAccessible getAccessibleInParentChain(in nsIDOMNode aDOMNode);
[noscript] void cacheAccessNode(in voidPtr aUniqueID, in nsIAccessNode aAccessNode);
[noscript] void destroy();
};

Просмотреть файл

@ -27,7 +27,9 @@ include $(DEPTH)/config/autoconf.mk
MODULE = accessibility
LIBRARY_NAME = accessibility_base_s
REQUIRES = content \
REQUIRES = commandhandler \
composer \
content \
docshell \
dom \
editor \

Просмотреть файл

@ -67,7 +67,7 @@ class nsIFrame;
class nsIDOMNodeList;
enum { eChildCountUninitialized = 0xffff };
enum { eSiblingsUninitialized = -1, eSiblingsWalkNormalDOM = -2};
enum { eSiblingsUninitialized = -1, eSiblingsWalkNormalDOM = -2 };
#define ACCESSIBLE_BUNDLE_URL "chrome://global-platform/locale/accessible.properties"
#define PLATFORM_KEYS_BUNDLE_URL "chrome://global-platform/locale/platformKeys.properties"

Просмотреть файл

@ -38,16 +38,13 @@
* ***** END LICENSE BLOCK ***** */
#include "nsBaseWidgetAccessible.h"
#include "nsIAccessibilityService.h"
#include "nsAccessibleWrap.h"
#include "nsGUIEvent.h"
#include "nsIFrame.h"
#include "nsILink.h"
#include "nsIPresContext.h"
#include "nsIPresShell.h"
#include "nsISelection.h"
#include "nsISelectionController.h"
#include "nsIServiceManager.h"
#include "nsIAccessibilityService.h"
// ------------
// nsBlockAccessible
@ -217,42 +214,15 @@ NS_IMETHODIMP nsLinkableAccessible::AccTakeFocus()
}
/* long GetAccState (); */
NS_IMETHODIMP nsLinkableAccessible::GetAccState(PRUint32 *_retval)
NS_IMETHODIMP nsLinkableAccessible::GetAccState(PRUint32 *aState)
{
nsAccessible::GetAccState(_retval);
*_retval |= STATE_READONLY | STATE_SELECTABLE;
nsAccessible::GetAccState(aState);
if (IsALink()) {
*_retval |= STATE_LINKED;
*aState |= STATE_LINKED;
if (mIsLinkVisited)
*_retval |= STATE_TRAVERSED;
*aState |= STATE_TRAVERSED;
}
// Get current selection and find out if current node is in it
nsCOMPtr<nsIPresShell> shell(GetPresShell());
if (!shell) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIPresContext> context;
shell->GetPresContext(getter_AddRefs(context));
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
nsIFrame *frame = nsnull;
if (content && NS_SUCCEEDED(shell->GetPrimaryFrameFor(content, &frame)) && frame) {
nsCOMPtr<nsISelectionController> selCon;
frame->GetSelectionController(context,getter_AddRefs(selCon));
if (selCon) {
nsCOMPtr<nsISelection> domSel;
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel));
if (domSel) {
PRBool isSelected = PR_FALSE, isCollapsed = PR_TRUE;
domSel->ContainsNode(mDOMNode, PR_TRUE, &isSelected);
domSel->GetIsCollapsed(&isCollapsed);
if (isSelected && !isCollapsed)
*_retval |=STATE_SELECTED;
}
}
}
if (IsALink()) {
// Make sure we also include all the states of the parent link, such as focusable, focused, etc.
PRUint32 role;
@ -263,11 +233,19 @@ NS_IMETHODIMP nsLinkableAccessible::GetAccState(PRUint32 *_retval)
if (parentAccessible) {
PRUint32 orState = 0;
parentAccessible->GetAccState(&orState);
*_retval |= orState;
*aState |= orState;
}
}
}
nsCOMPtr<nsIAccessibleDocument> docAccessible(GetDocAccessible());
if (docAccessible) {
PRBool isEditable;
docAccessible->GetIsEditable(&isEditable);
if (isEditable) {
*aState &= ~(STATE_FOCUSED | STATE_FOCUSABLE); // Links not focusable in editor
}
}
return NS_OK;
}

Просмотреть файл

@ -37,29 +37,37 @@
* ***** END LICENSE BLOCK ***** */
#include "nsDocAccessible.h"
#include "nsHashtable.h"
#include "nsIAccessibilityService.h"
#include "nsICommandManager.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeItem.h"
#include "nsIDocument.h"
#include "nsIPresShell.h"
#include "nsIFrame.h"
#include "nsIDOMCharacterData.h"
#include "nsIDOMDocument.h"
#include "nsIDOMEventTarget.h"
#include "nsIDOMEvent.h"
#include "nsIDOMDocument.h"
#include "nsIDOMDocumentType.h"
#include "nsIDOMNSDocument.h"
#include "nsIDOMNSHTMLDocument.h"
#include "nsIDOMMutationEvent.h"
#include "nsIDOMWindow.h"
#include "nsIEditorDocShell.h"
#include "nsIEditingSession.h"
#include "nsIFrame.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsINameSpaceManager.h"
#include "nsIPlaintextEditor.h"
#include "nsIPresShell.h"
#include "nsIScriptGlobalObject.h"
#include "nsIServiceManager.h"
#include "nsIScrollableView.h"
#include "nsIURI.h"
#include "nsIWebNavigation.h"
#ifdef MOZ_XUL
#include "nsIXULDocument.h"
#endif
#include "nsINameSpaceManager.h"
#include "nsIWebNavigation.h"
#include "nsIURI.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIServiceManager.h"
#include "nsHashtable.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeItem.h"
#include "nsIScrollableView.h"
#include "nsIAccessibilityService.h"
#ifdef MOZ_ACCESSIBILITY_ATK
#include "nsAccessibleEventData.h"
#endif
@ -75,7 +83,8 @@ nsDocAccessible::nsDocAccessible(nsIDOMNode *aDOMNode, nsIWeakReference* aShell)
nsAccessibleWrap(aDOMNode, aShell),
mAccessNodeCache(nsnull), mWnd(nsnull),
mScrollWatchTimer(nsnull), mDocLoadTimer(nsnull),
mWebProgress(nsnull), mBusy(eBusyStateUninitialized),
mWebProgress(nsnull), mEditor(nsnull),
mBusy(eBusyStateUninitialized),
mScrollPositionChangedTicks(0), mIsNewDocument(PR_FALSE)
{
// Because of the way document loading happens, the new nsIWidget is created before
@ -94,7 +103,7 @@ nsDocAccessible::nsDocAccessible(nsIDOMNode *aDOMNode, nsIWeakReference* aShell)
}
NS_ASSERTION(gGlobalDocAccessibleCache, "No global doc accessible cache");
PutCacheEntry(gGlobalDocAccessibleCache, NS_STATIC_CAST(void*, mWeakShell), this);
PutCacheEntry(gGlobalDocAccessibleCache, mWeakShell, this);
#ifdef DEBUG
printf("\nATTENTION: New doc accessible for weak shell %x\n", mWeakShell.get());
#endif
@ -115,10 +124,19 @@ nsDocAccessible::~nsDocAccessible()
{
}
NS_IMPL_ISUPPORTS_INHERITED6(nsDocAccessible, nsAccessible, nsIAccessibleDocument,
nsIAccessibleEventReceiver, nsIWebProgressListener,
nsIDOMMutationListener,
nsIScrollPositionListener, nsISupportsWeakReference)
NS_INTERFACE_MAP_BEGIN(nsDocAccessible)
NS_INTERFACE_MAP_ENTRY(nsIAccessibleDocument)
NS_INTERFACE_MAP_ENTRY(nsIAccessibleEventReceiver)
NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
NS_INTERFACE_MAP_ENTRY(nsIDOMMutationListener)
NS_INTERFACE_MAP_ENTRY(nsIScrollPositionListener)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessibleDocument)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_END_INHERITING(nsAccessible)
NS_IMPL_ADDREF_INHERITED(nsDocAccessible, nsAccessible)
NS_IMPL_RELEASE_INHERITED(nsDocAccessible, nsAccessible)
NS_IMETHODIMP nsDocAccessible::AddEventListeners()
{
@ -154,6 +172,17 @@ NS_IMETHODIMP nsDocAccessible::GetAccState(PRUint32 *aAccState)
*aAccState = STATE_FOCUSABLE;
if (mBusy == eBusyStateLoading)
*aAccState |= STATE_BUSY;
#ifdef DEBUG
PRBool isEditable;
GetIsEditable(&isEditable);
if (isEditable) {
// Just for debugging, to show we're in editor on pane object
// We don't use STATE_MARQUEED for anything else
*aAccState |= STATE_MARQUEED;
}
#endif
return NS_OK;
}
@ -237,6 +266,34 @@ NS_IMETHODIMP nsDocAccessible::GetWindow(void **aWindow)
return NS_OK;
}
void nsDocAccessible::CheckForEditor()
{
nsCOMPtr<nsIScriptGlobalObject> ourGlobal;
mDocument->GetScriptGlobalObject(getter_AddRefs(ourGlobal));
nsCOMPtr<nsIDOMWindow> domWindow(do_QueryInterface(ourGlobal));
if (!domWindow)
return; // No DOM Window
nsCOMPtr<nsISupports> container;
mDocument->GetContainer(getter_AddRefs(container));
nsCOMPtr<nsIEditingSession> editingSession(do_GetInterface(container));
if (!editingSession)
return; // No editing session interface
editingSession->GetEditorForWindow(domWindow, getter_AddRefs(mEditor));
}
NS_IMETHODIMP nsDocAccessible::GetIsEditable(PRBool *aIsEditable)
{
*aIsEditable = PR_FALSE;
if (mEditor) {
PRUint32 flags;
mEditor->GetFlags(&flags);
*aIsEditable = (flags & nsIPlaintextEditor::eEditorReadonlyMask) == 0;
}
return NS_OK;
}
NS_IMETHODIMP nsDocAccessible::GetCachedAccessNode(void *aUniqueID, nsIAccessNode **aAccessNode)
{
NS_ASSERTION(mAccessNodeCache, "No accessibility cache for document");
@ -251,6 +308,40 @@ NS_IMETHODIMP nsDocAccessible::CacheAccessNode(void *aUniqueID, nsIAccessNode *a
return NS_OK;
}
NS_IMETHODIMP nsDocAccessible::Init()
{
// Hook up our new accessible with our parent
if (!mParent) {
nsCOMPtr<nsIDocument> parentDoc;
mDocument->GetParentDocument(getter_AddRefs(parentDoc));
if (parentDoc) {
nsCOMPtr<nsIContent> ownerContent;
parentDoc->FindContentForSubDocument(mDocument,
getter_AddRefs(ownerContent));
nsCOMPtr<nsIDOMNode> ownerNode(do_QueryInterface(ownerContent));
if (ownerNode) {
nsCOMPtr<nsIAccessibilityService> accService =
do_GetService("@mozilla.org/accessibilityService;1");
if (accService) {
// XXX aaronl: ideally we would traverse the presshell chain
// Since there's no easy way to do that, we cheat and use
// the document hierarchy. GetAccessibleFor() is bad because
// it doesn't support our concept of multiple presshells per doc.
// It should be changed to use GetAccessibleInWeakShell()
nsCOMPtr<nsIAccessible> accParent;
accService->GetAccessibleFor(ownerNode, getter_AddRefs(accParent));
if (accParent) {
SetAccParent(accParent);
accParent->SetAccFirstChild(this);
}
}
}
}
}
return nsAccessibleWrap::Init();
}
NS_IMETHODIMP nsDocAccessible::Destroy()
{
NS_ASSERTION(gGlobalDocAccessibleCache, "No global doc accessible cache");
@ -271,9 +362,12 @@ NS_IMETHODIMP nsDocAccessible::Shutdown()
return NS_OK; // Already shutdown
}
RemoveEventListeners();
mWeakShell = nsnull; // Avoid reentrancy
RemoveEventListeners();
mEditor = nsnull;
if (mScrollWatchTimer) {
mScrollWatchTimer->Cancel();
mScrollWatchTimer = nsnull;
@ -295,8 +389,8 @@ NS_IMETHODIMP nsDocAccessible::Shutdown()
delete hashToClear;
}
NS_ASSERTION(gGlobalDocAccessibleCache, "Global doc cache does not exist");
mDocument = nsnull;
return nsAccessibleWrap::Shutdown();
}
@ -349,42 +443,54 @@ void nsDocAccessible::AddContentDocListeners()
if (itemType != nsIDocShellTreeItem::typeContent)
return;
// Make sure we're the top content doc shell
// We don't want to listen to iframe progress
nsCOMPtr<nsIDocShellTreeItem> topOfContentTree;
docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(topOfContentTree));
if (topOfContentTree != docShellTreeItem)
return;
PRBool isLoading = PR_TRUE;
CheckForEditor();
if (!mEditor) {
// We're not an editor yet, but we might become one
nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(container);
if (commandManager) {
commandManager->AddCommandObserver(this, "obs_documentCreated");
}
// Make sure we're the top content doc shell
// We don't want to listen to iframe progress
nsCOMPtr<nsIDocShellTreeItem> topOfContentTree;
docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(topOfContentTree));
if (topOfContentTree != docShellTreeItem) {
mBusy = eBusyStateDone;
return;
}
nsCOMPtr<nsIPresContext> context;
presShell->GetPresContext(getter_AddRefs(context));
if (!context)
return;
nsCOMPtr<nsIPresContext> context;
presShell->GetPresContext(getter_AddRefs(context));
if (!context)
return;
mWebProgress = do_GetInterface(docShellTreeItem);
if (!mWebProgress)
return;
mWebProgress = do_GetInterface(docShellTreeItem);
if (!mWebProgress)
return;
mWebProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_LOCATION |
nsIWebProgress::NOTIFY_STATE_DOCUMENT);
mWebProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_LOCATION |
nsIWebProgress::NOTIFY_STATE_DOCUMENT);
mWebProgress->GetIsLoadingDocument(&isLoading);
}
mIsNewDocument = PR_TRUE;
mBusy = eBusyStateLoading;
PRBool isLoading;
mWebProgress->GetIsLoadingDocument(&isLoading);
if (!isLoading) {
// If already loaded, fire "done loading" event after short timeout
// If we fired the event here, we'd get reentrancy problems
// Otherwise it will be fired from OnStateChange when the load is done
// Otherwise, if the doc is still loading,
// the event will be fired from OnStateChange when the load is done
mDocLoadTimer = do_CreateInstance("@mozilla.org/timer;1");
if (mDocLoadTimer) {
mDocLoadTimer->InitWithFuncCallback(DocLoadCallback, this, 1,
nsITimer::TYPE_ONE_SHOT);
}
}
// add ourself as a mutation event listener
// (this slows down mozilla about 3%, but only used when accessibility APIs active)
nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(mDocument));
@ -421,7 +527,9 @@ void nsDocAccessible::RemoveContentDocListeners()
// Remove scroll position listener
nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell));
RemoveScrollListener(presShell);
if (presShell) {
RemoveScrollListener(presShell);
}
nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(mDocument));
NS_ASSERTION(target, "No dom event target for document");
@ -431,6 +539,13 @@ void nsDocAccessible::RemoveContentDocListeners()
target->RemoveEventListener(NS_LITERAL_STRING("DOMNodeRemoved"), this, PR_TRUE);
target->RemoveEventListener(NS_LITERAL_STRING("DOMNodeInsertedIntoDocument"), this, PR_TRUE);
target->RemoveEventListener(NS_LITERAL_STRING("DOMNodeRemovedFromDocument"), this, PR_TRUE);
nsCOMPtr<nsISupports> container;
mDocument->GetContainer(getter_AddRefs(container));
nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(container);
if (commandManager) {
commandManager->RemoveCommandObserver(this, "obs_documentCreated");
}
}
void nsDocAccessible::FireDocLoadFinished()
@ -438,45 +553,17 @@ void nsDocAccessible::FireDocLoadFinished()
if (!mDocument || !mWeakShell)
return; // Document has been shut down
// Hook up our new accessible with our parent
if (!mParent) {
nsCOMPtr<nsIDocument> parentDoc;
mDocument->GetParentDocument(getter_AddRefs(parentDoc));
if (parentDoc) {
nsCOMPtr<nsIContent> ownerContent;
parentDoc->FindContentForSubDocument(mDocument,
getter_AddRefs(ownerContent));
nsCOMPtr<nsIDOMNode> ownerNode(do_QueryInterface(ownerContent));
if (ownerNode) {
nsCOMPtr<nsIAccessibilityService> accService =
do_GetService("@mozilla.org/accessibilityService;1");
if (accService) {
// XXX aaronl: ideally we would traverse the presshell chain
// Since there's no easy way to do that, we cheat and use
// the document hierarchy. GetAccessibleFor() is bad because
// it doesn't support our concept of multiple presshells per doc.
// It should be changed to use GetAccessibleInWeakShell()
nsCOMPtr<nsIAccessible> accParent;
accService->GetAccessibleFor(ownerNode, getter_AddRefs(accParent));
SetAccParent(accParent);
if (accParent) {
accParent->SetAccFirstChild(this);
}
}
}
}
}
if (mIsNewDocument) {
mIsNewDocument = PR_FALSE;
if (mBusy != eBusyStateDone) {
mBusy = eBusyStateDone;
#ifndef MOZ_ACCESSIBILITY_ATK
FireToolkitEvent(nsIAccessibleEventReceiver::EVENT_STATE_CHANGE, this, nsnull);
#endif
}
}
mBusy = eBusyStateDone;
}
void nsDocAccessible::DocLoadCallback(nsITimer *aTimer, void *aClosure)
@ -522,7 +609,7 @@ void nsDocAccessible::AddScrollListener(nsIPresShell *aPresShell)
vm->GetRootScrollableView(&scrollableView);
if (scrollableView)
scrollableView->AddScrollPositionListener(NS_STATIC_CAST(nsIScrollPositionListener *, this));
scrollableView->AddScrollPositionListener(this);
}
void nsDocAccessible::RemoveScrollListener(nsIPresShell *aPresShell)
@ -537,7 +624,7 @@ void nsDocAccessible::RemoveScrollListener(nsIPresShell *aPresShell)
vm->GetRootScrollableView(&scrollableView);
if (scrollableView)
scrollableView->RemoveScrollPositionListener(NS_STATIC_CAST(nsIScrollPositionListener *, this));
scrollableView->RemoveScrollPositionListener(this);
}
NS_IMETHODIMP nsDocAccessible::ScrollPositionWillChange(nsIScrollableView *aView, nscoord aX, nscoord aY)
@ -568,8 +655,9 @@ NS_IMETHODIMP nsDocAccessible::ScrollPositionDidChange(nsIScrollableView *aScrol
NS_IMETHODIMP nsDocAccessible::OnStateChange(nsIWebProgress *aWebProgress,
nsIRequest *aRequest, PRUint32 aStateFlags, nsresult aStatus)
{
if ((aStateFlags & STATE_IS_DOCUMENT) && (aStateFlags & STATE_STOP))
if ((aStateFlags & STATE_IS_DOCUMENT) && (aStateFlags & STATE_STOP)) {
FireDocLoadFinished(); // Doc is ready!
}
return NS_OK;
}
@ -626,6 +714,46 @@ NS_IMETHODIMP nsDocAccessible::OnSecurityChange(nsIWebProgress *aWebProgress,
return NS_OK;
}
NS_IMETHODIMP nsDocAccessible::Observe(nsISupports *aSubject, const char *aTopic,
const PRUnichar *aData)
{
if (!nsCRT::strcmp(aTopic,"obs_documentCreated")) {
CheckForEditor();
NS_ASSERTION(mEditor, "Should have editor if we see obs_documentCreated");
}
return NS_OK;
}
void nsDocAccessible::GetEventShell(nsIDOMNode *aNode, nsIPresShell **aEventShell)
{
// XXX aaronl - this is not ideal.
// We could avoid this whole section and the fallible
// doc->GetShellAt(0, ...) by putting the event handler
// on nsDocAccessible instead.
// The disadvantage would be that we would be seeing some events
// for inner documents that we don't care about.
*aEventShell = nsnull;
nsCOMPtr<nsIDOMDocument> domDocument;
aNode->GetOwnerDocument(getter_AddRefs(domDocument));
nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDocument));
if (doc)
doc->GetShellAt(0, aEventShell);
}
void nsDocAccessible::GetEventDocAccessible(nsIDOMNode *aNode,
nsIAccessibleDocument **aAccessibleDoc)
{
*aAccessibleDoc = nsnull;
nsCOMPtr<nsIPresShell> eventShell;
GetEventShell(aNode, getter_AddRefs(eventShell));
nsCOMPtr<nsIWeakReference> weakEventShell(do_GetWeakReference(eventShell));
if (!weakEventShell) {
return;
}
GetDocAccessibleFor(weakEventShell, aAccessibleDoc);
}
// ---------- Mutation event listeners ------------
NS_IMETHODIMP nsDocAccessible::NodeInserted(nsIDOMEvent* aEvent)
{
@ -687,7 +815,7 @@ NS_IMETHODIMP nsDocAccessible::CharacterDataModified(nsIDOMEvent* aMutationEvent
return NS_OK;
}
void nsDocAccessible::InvalidateCacheSubtree(nsIDOMNode *aStartNode)
NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIDOMNode *aStartNode)
{
// Invalidate cache subtree
// We have to check for accessibles for each dom node by traversing DOM tree
@ -695,18 +823,30 @@ void nsDocAccessible::InvalidateCacheSubtree(nsIDOMNode *aStartNode)
// Otherwise we might miss the nsAccessNode's that are not nsAccessible's.
if (!aStartNode)
return;
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMNode> iterNode(aStartNode), nextNode;
nsCOMPtr<nsIAccessNode> accessNode;
do {
GetCachedAccessNode(NS_STATIC_CAST(void*, iterNode),
getter_AddRefs(accessNode));
GetCachedAccessNode(iterNode, getter_AddRefs(accessNode));
if (accessNode) {
// XXX aaronl todo: accessibles that implement their own subtrees,
// like html combo boxes and xul trees, need to shutdown all of their own
// children when they override Shutdown()
accessNode->Shutdown();
// Don't shutdown our doc object!
if (accessNode != NS_STATIC_CAST(nsIAccessNode*, this)) {
void *uniqueID;
accessNode->GetUniqueID(&uniqueID);
accessNode->Shutdown();
// Remove from hash table as well
#ifdef OLD_HASH
nsVoidKey key(uniqueID);
mAccessNodeCache->Remove(&key);
#else
mAccessNodeCache->Remove(uniqueID);
#endif
}
}
iterNode->GetFirstChild(getter_AddRefs(nextNode));
@ -726,7 +866,7 @@ void nsDocAccessible::InvalidateCacheSubtree(nsIDOMNode *aStartNode)
do {
iterNode->GetParentNode(getter_AddRefs(nextNode));
if (!nextNode || nextNode == aStartNode) {
return;
return NS_OK;
}
nextNode->GetNextSibling(getter_AddRefs(iterNode));
if (iterNode)
@ -735,76 +875,71 @@ void nsDocAccessible::InvalidateCacheSubtree(nsIDOMNode *aStartNode)
} while (PR_TRUE);
}
while (iterNode && iterNode != aStartNode);
return NS_OK;
}
void nsDocAccessible::HandleMutationEvent(nsIDOMEvent *aEvent, PRUint32 aAccessibleEventType)
NS_IMETHODIMP
nsDocAccessible::GetAccessibleInParentChain(nsIDOMNode *aNode,
nsIAccessible **aAccessible)
{
if (mBusy != eBusyStateDone)
return; // We don't want mutation events until doc is fully loaded
nsCOMPtr<nsIAccessibilityService> accService =
do_GetService("@mozilla.org/accessibilityService;1");
if (!accService)
return nsnull;
nsCOMPtr<nsIDOMNode> currentNode(aNode), parentNode;
while (NS_FAILED(accService->GetAccessibleInWeakShell(currentNode, mWeakShell,
aAccessible))) {
currentNode->GetParentNode(getter_AddRefs(parentNode));
NS_ASSERTION(parentNode, "Impossible! Crawled up parent chain without "
"finding accessible. There should have at least "
"been a document accessible at the root.");
currentNode = parentNode;
}
return NS_OK;
}
void nsDocAccessible::HandleMutationEvent(nsIDOMEvent *aEvent, PRUint32 aAccessibleEventType)
{
nsCOMPtr<nsIDOMMutationEvent> mutationEvent(do_QueryInterface(aEvent));
NS_ASSERTION(mutationEvent, "Not a mutation event!");
nsCOMPtr<nsIDOMEvent> domEvent(do_QueryInterface(aEvent));
nsCOMPtr<nsIDOMEventTarget> domEventTarget;
if (domEvent) {
domEvent->GetTarget(getter_AddRefs(domEventTarget));
}
nsCOMPtr<nsIDOMNode> targetNode(do_QueryInterface(domEventTarget));
if (!targetNode)
return;
mutationEvent->GetTarget(getter_AddRefs(domEventTarget));
nsCOMPtr<nsIDOMNode> subTreeNode;
if (aAccessibleEventType == nsIAccessibleEventReceiver::EVENT_CREATE) {
// DOMNodeInserted
// We'll need to invalidate the child count and cached children of
// the direct parent, otherwise no new accessibles will appear at this
// place in the tree.
// However, don't invalidate the entire subtree - most of it is still good.
targetNode->GetParentNode(getter_AddRefs(subTreeNode));
NS_ASSERTION(subTreeNode, "No valid parent of insertion");
targetNode = subTreeNode;
nsCOMPtr<nsIDOMNode> targetNode(do_QueryInterface(domEventTarget));
nsCOMPtr<nsIDOMNode> subTreeToInvalidate;
mutationEvent->GetRelatedNode(getter_AddRefs(subTreeToInvalidate));
NS_ASSERTION(subTreeToInvalidate, "No old sub tree being replaced in DOMSubtreeModified");
if (!targetNode) {
targetNode = subTreeToInvalidate;
}
else {
if (aAccessibleEventType == nsIAccessibleEventReceiver::EVENT_REORDER) {
// DOMSubtreeModified
// The event target is the parent of the reordered tree
// We don't want to invalidate that entire subtree.
nsCOMPtr<nsIDOMMutationEvent> mutationEvent(do_QueryInterface(aEvent));
NS_ASSERTION(mutationEvent, "Not a mutation event!");
mutationEvent->GetRelatedNode(getter_AddRefs(subTreeNode));
NS_ASSERTION(subTreeNode, "No old sub tree being replaced in DOMSubtreeModified");
}
else {
// nsIAccessibleEventReceiver::EVENT_DESTROY / DOMNodeRemoved
subTreeNode = targetNode;
}
InvalidateCacheSubtree(subTreeNode);
else if (aAccessibleEventType == nsIAccessibleEventReceiver::EVENT_REORDER) {
subTreeToInvalidate = targetNode; // targetNode is parent for DOMNodeRemoved event
}
nsCOMPtr<nsIAccessibleDocument> docAccessible;
GetEventDocAccessible(subTreeToInvalidate, getter_AddRefs(docAccessible));
docAccessible->InvalidateCacheSubtree(subTreeToInvalidate);
// We need to get an accessible for the mutation event's target node
// If there is no accessible for that node, we need to keep moving up the parent
// chain so there is some accessible.
// We will use this accessible to fire the accessible mutation event.
// We're guaranteed success, because we will eventually end up at the doc accessible,
// and there is always one of those.
// XXX aaronl todo: are MSAA mutation events always fired on the container?
// if so, we always need to get the dom parent at least once
nsCOMPtr<nsIAccessibilityService> accService =
do_GetService("@mozilla.org/accessibilityService;1");
if (!accService)
return;
nsCOMPtr<nsIAccessible> accessible;
while (NS_FAILED(accService->GetAccessibleInWeakShell(targetNode, mWeakShell,
getter_AddRefs(accessible)))) {
targetNode->GetParentNode(getter_AddRefs(subTreeNode));
NS_ASSERTION(subTreeNode, "Crawled up parent chain without finding accessible");
targetNode = subTreeNode;
}
docAccessible->GetAccessibleInParentChain(targetNode, getter_AddRefs(accessible));
accessible->InvalidateChildren();
#ifdef XP_WIN
// Windows MSAA clients crash if the listen to create or destroy events
// Windows MSAA clients crash if they listen to create or destroy events
aAccessibleEventType = nsIAccessibleEventReceiver::EVENT_REORDER;
#endif
accessible->FireToolkitEvent(aAccessibleEventType, accessible, nsnull);

Просмотреть файл

@ -45,6 +45,8 @@
#include "nsIAccessibleEventReceiver.h"
#include "nsIDocument.h"
#include "nsIDOMMutationListener.h"
#include "nsIEditor.h"
#include "nsIObserver.h"
#include "nsIScrollPositionListener.h"
#include "nsITimer.h"
#include "nsIWeakReference.h"
@ -59,6 +61,7 @@ class nsDocAccessible : public nsAccessibleWrap,
public nsIAccessibleDocument,
public nsIAccessibleEventReceiver,
public nsIWebProgressListener,
public nsIObserver,
public nsIDOMMutationListener,
public nsIScrollPositionListener,
public nsSupportsWeakReference
@ -68,6 +71,7 @@ class nsDocAccessible : public nsAccessibleWrap,
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIACCESSIBLEDOCUMENT
NS_DECL_NSIACCESSIBLEEVENTRECEIVER
NS_DECL_NSIOBSERVER
public:
nsDocAccessible(nsIDOMNode *aNode, nsIWeakReference* aShell);
@ -96,6 +100,7 @@ class nsDocAccessible : public nsAccessibleWrap,
// nsIAccessNode
NS_IMETHOD Shutdown();
NS_IMETHOD Init();
protected:
virtual void GetBounds(nsRect& aRect, nsIFrame** aRelativeFrame);
@ -105,10 +110,13 @@ class nsDocAccessible : public nsAccessibleWrap,
void AddScrollListener(nsIPresShell *aPresShell);
void RemoveScrollListener(nsIPresShell *aPresShell);
void FireDocLoadFinished();
void InvalidateCacheSubtree(nsIDOMNode *aStartNode);
void HandleMutationEvent(nsIDOMEvent *aEvent, PRUint32 aEventType);
static void DocLoadCallback(nsITimer *aTimer, void *aClosure);
static void ScrollTimerCallback(nsITimer *aTimer, void *aClosure);
void GetEventShell(nsIDOMNode *aNode, nsIPresShell **aEventShell);
void GetEventDocAccessible(nsIDOMNode *aNode,
nsIAccessibleDocument **aAccessibleDoc);
void CheckForEditor();
#ifdef OLD_HASH
nsSupportsHashtable *mAccessNodeCache;
@ -120,6 +128,7 @@ class nsDocAccessible : public nsAccessibleWrap,
nsCOMPtr<nsITimer> mScrollWatchTimer;
nsCOMPtr<nsITimer> mDocLoadTimer;
nsCOMPtr<nsIWebProgress> mWebProgress;
nsCOMPtr<nsIEditor> mEditor; // Editor, if there is one
EBusyState mBusy;
PRUint16 mScrollPositionChangedTicks; // Used for tracking scroll events
PRPackedBool mIsNewDocument;

Просмотреть файл

@ -39,6 +39,11 @@
* ***** END LICENSE BLOCK ***** */
#include "nsHTMLTextAccessible.h"
#include "nsIFrame.h"
#include "nsIPresContext.h"
#include "nsIPresShell.h"
#include "nsISelection.h"
#include "nsISelectionController.h"
nsHTMLTextAccessible::nsHTMLTextAccessible(nsIDOMNode* aDomNode, nsIWeakReference* aShell):
nsTextAccessible(aDomNode, aShell)
@ -55,6 +60,48 @@ NS_IMETHODIMP nsHTMLTextAccessible::GetAccName(nsAString& aName)
return NS_OK;
}
NS_IMETHODIMP nsHTMLTextAccessible::GetAccState(PRUint32 *aState)
{
nsTextAccessible::GetAccState(aState);
// Get current selection and find out if current node is in it
nsCOMPtr<nsIPresShell> shell(GetPresShell());
if (!shell) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIPresContext> context;
shell->GetPresContext(getter_AddRefs(context));
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
nsIFrame *frame = nsnull;
if (content && NS_SUCCEEDED(shell->GetPrimaryFrameFor(content, &frame)) && frame) {
nsCOMPtr<nsISelectionController> selCon;
frame->GetSelectionController(context, getter_AddRefs(selCon));
if (selCon) {
nsCOMPtr<nsISelection> domSel;
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel));
if (domSel) {
PRBool isSelected = PR_FALSE, isCollapsed = PR_TRUE;
domSel->ContainsNode(mDOMNode, PR_TRUE, &isSelected);
domSel->GetIsCollapsed(&isCollapsed);
if (isSelected && !isCollapsed)
*aState |=STATE_SELECTED;
}
}
}
*aState |= STATE_SELECTABLE;
nsCOMPtr<nsIAccessibleDocument> docAccessible(GetDocAccessible());
if (docAccessible) {
PRBool isEditable;
docAccessible->GetIsEditable(&isEditable);
if (!isEditable) {
*aState |= STATE_READONLY;
}
}
return NS_OK;
}
nsHTMLHRAccessible::nsHTMLHRAccessible(nsIDOMNode* aDomNode, nsIWeakReference* aShell):
nsLeafAccessible(aDomNode, aShell)
{

Просмотреть файл

@ -50,6 +50,7 @@ class nsHTMLTextAccessible : public nsTextAccessible
public:
nsHTMLTextAccessible(nsIDOMNode* aDomNode, nsIWeakReference* aShell);
NS_IMETHOD GetAccName(nsAString& _retval);
NS_IMETHOD GetAccState(PRUint32 *aState);
};
class nsHTMLHRAccessible : public nsLeafAccessible

Просмотреть файл

@ -46,6 +46,7 @@ LIBRARY_NAME = accessibility_toolkit_s
REQUIRES = content \
docshell \
dom \
editor \
gfx \
htmlparser \
intl \

Просмотреть файл

@ -46,6 +46,7 @@ LIBRARY_NAME = accessibility_toolkit_s
REQUIRES = content \
docshell \
dom \
editor \
gfx \
htmlparser \
intl \

Просмотреть файл

@ -70,7 +70,7 @@ EXTERN_C GUID CDECL CLSID_Accessible =
// construction
//-----------------------------------------------------
nsAccessibleWrap::nsAccessibleWrap(nsIDOMNode* aNode, nsIWeakReference *aShell):
nsAccessible(aNode, aShell)
nsAccessible(aNode, aShell), mEnumVARIANTPosition(0)
{
}

Просмотреть файл

@ -46,6 +46,7 @@ LIBRARY_NAME = accessibility_toolkit_s
REQUIRES = content \
docshell \
dom \
editor \
gfx \
htmlparser \
intl \

Просмотреть файл

@ -6,7 +6,7 @@
xmlns:xbl="http://www.mozilla.org/xbl">
<binding id="editor">
<implementation>
<implementation type="application/x-javascript" implements="nsIAccessibleProvider">
<constructor>
<![CDATA[
// Make window editable immediately only
@ -18,6 +18,16 @@
]]>
</constructor>
<destructor/>
<property name="accessible">
<getter>
<![CDATA[
var accService = Components.classes["@mozilla.org/accessibilityService;1"].getService(Components.interfaces.nsIAccessibilityService);
return accService.createOuterDocAccessible(this);
]]>
</getter>
</property>
<method name="makeEditable">
<parameter name="editortype"/>
<parameter name="waitForUrlLoad"/>