2003-04-01 22:18:29 +00:00
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version : MPL 1.1 / GPL 2.0 / LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 ( the " License " ) ; you may not use this file except in compliance with
* the License . You may obtain a copy of the License at
* http : //www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an " AS IS " basis ,
* WITHOUT WARRANTY OF ANY KIND , either express or implied . See the License
* for the specific language governing rights and limitations under the
* License .
*
* The Original Code is mozilla . org code .
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation .
* Portions created by the Initial Developer are Copyright ( C ) 2003
* the Initial Developer . All Rights Reserved .
*
* Contributor ( s ) :
* Original Author : Aaron Leventhal ( aaronl @ netscape . com )
*
* Alternatively , the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later ( the " GPL " ) , or
* the GNU Lesser General Public License Version 2.1 or later ( the " LGPL " ) ,
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above . If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL , and not to allow others to
* use your version of this file under the terms of the MPL , indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL . If you do not delete
* the provisions above , a recipient may use your version of this file under
* the terms of any one of the MPL , the GPL or the LGPL .
*
* * * * * * END LICENSE BLOCK * * * * * */
# include "nsDocAccessible.h"
2003-05-15 08:37:38 +00:00
# include "nsHashtable.h"
# include "nsIAccessibilityService.h"
# include "nsICommandManager.h"
# include "nsIDocShell.h"
# include "nsIDocShellTreeItem.h"
2003-04-01 22:18:29 +00:00
# include "nsIDocument.h"
2003-05-15 08:37:38 +00:00
# include "nsIDOMCharacterData.h"
2003-04-01 22:18:29 +00:00
# include "nsIDOMDocument.h"
2003-04-28 10:24:52 +00:00
# include "nsIDOMEventTarget.h"
# include "nsIDOMEvent.h"
2003-04-01 22:18:29 +00:00
# include "nsIDOMDocument.h"
# include "nsIDOMDocumentType.h"
2003-04-28 10:24:52 +00:00
# include "nsIDOMNSDocument.h"
2003-05-15 08:37:38 +00:00
# include "nsIDOMNSHTMLDocument.h"
2003-04-28 10:24:52 +00:00
# include "nsIDOMMutationEvent.h"
2003-05-15 08:37:38 +00:00
# include "nsIDOMWindow.h"
# include "nsIEditorDocShell.h"
# include "nsIEditingSession.h"
# include "nsIFrame.h"
2003-04-01 22:18:29 +00:00
# include "nsIInterfaceRequestorUtils.h"
2003-05-15 08:37:38 +00:00
# include "nsINameSpaceManager.h"
# include "nsIPlaintextEditor.h"
# include "nsIPresShell.h"
# include "nsIScriptGlobalObject.h"
2003-04-01 22:18:29 +00:00
# include "nsIServiceManager.h"
2003-04-15 08:45:55 +00:00
# include "nsIScrollableView.h"
2003-05-15 08:37:38 +00:00
# include "nsIURI.h"
# include "nsIWebNavigation.h"
# ifdef MOZ_XUL
# include "nsIXULDocument.h"
# endif
2003-04-15 08:45:55 +00:00
# ifdef MOZ_ACCESSIBILITY_ATK
# include "nsAccessibleEventData.h"
# endif
2003-04-01 22:18:29 +00:00
//=============================//
// nsDocAccessible //
//=============================//
//-----------------------------------------------------
// construction
//-----------------------------------------------------
nsDocAccessible : : nsDocAccessible ( nsIDOMNode * aDOMNode , nsIWeakReference * aShell ) :
2003-04-15 08:45:55 +00:00
nsAccessibleWrap ( aDOMNode , aShell ) ,
mAccessNodeCache ( nsnull ) , mWnd ( nsnull ) ,
mScrollWatchTimer ( nsnull ) , mDocLoadTimer ( nsnull ) ,
2003-05-15 08:37:38 +00:00
mWebProgress ( nsnull ) , mEditor ( nsnull ) ,
mBusy ( eBusyStateUninitialized ) ,
2003-04-15 08:45:55 +00:00
mScrollPositionChangedTicks ( 0 ) , mIsNewDocument ( PR_FALSE )
2003-04-01 22:18:29 +00:00
{
2003-04-15 08:45:55 +00:00
// Because of the way document loading happens, the new nsIWidget is created before
// the old one is removed. Since it creates the nsDocAccessible, for a brief moment
// there can be 2 nsDocAccessible's for the content area, although for 2 different
// pres shells.
nsCOMPtr < nsIPresShell > shell ( do_QueryReferent ( mWeakShell ) ) ;
2003-04-01 22:18:29 +00:00
if ( shell ) {
shell - > GetDocument ( getter_AddRefs ( mDocument ) ) ;
2003-04-15 08:45:55 +00:00
nsCOMPtr < nsIViewManager > vm ;
shell - > GetViewManager ( getter_AddRefs ( vm ) ) ;
nsCOMPtr < nsIWidget > widget ;
2003-05-17 12:00:07 +00:00
vm - > GetWidget ( getter_AddRefs ( widget ) ) ;
mWnd = widget - > GetNativeData ( NS_NATIVE_WINDOW ) ;
2003-04-01 22:18:29 +00:00
}
2003-04-15 08:45:55 +00:00
NS_ASSERTION ( gGlobalDocAccessibleCache , " No global doc accessible cache " ) ;
2003-05-15 08:37:38 +00:00
PutCacheEntry ( gGlobalDocAccessibleCache , mWeakShell , this ) ;
2003-05-17 12:00:07 +00:00
# ifdef DEBUG
printf ( " \n ATTENTION: New doc accessible for weak shell %x \n " , mWeakShell . get ( ) ) ;
# endif
2003-04-15 08:45:55 +00:00
// XXX aaronl should we use an algorithm for the initial cache size?
# ifdef OLD_HASH
mAccessNodeCache = new nsSupportsHashtable ( kDefaultCacheSize ) ;
# else
mAccessNodeCache = new nsInterfaceHashtable < nsVoidHashKey , nsIAccessNode > ;
mAccessNodeCache - > Init ( kDefaultCacheSize ) ;
# endif
2003-04-01 22:18:29 +00:00
}
//-----------------------------------------------------
// destruction
//-----------------------------------------------------
nsDocAccessible : : ~ nsDocAccessible ( )
{
}
2003-05-15 08:37:38 +00:00
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 )
2003-04-01 22:18:29 +00:00
2003-04-15 08:45:55 +00:00
NS_IMETHODIMP nsDocAccessible : : AddEventListeners ( )
2003-04-01 22:18:29 +00:00
{
2003-04-15 08:45:55 +00:00
AddContentDocListeners ( ) ;
2003-04-01 22:18:29 +00:00
return NS_OK ;
}
/* void removeAccessibleEventListener (); */
2003-04-15 08:45:55 +00:00
NS_IMETHODIMP nsDocAccessible : : RemoveEventListeners ( )
2003-04-01 22:18:29 +00:00
{
2003-04-15 08:45:55 +00:00
RemoveContentDocListeners ( ) ;
2003-04-01 22:18:29 +00:00
return NS_OK ;
}
2003-04-15 08:45:55 +00:00
NS_IMETHODIMP nsDocAccessible : : GetAccName ( nsAString & aAccName )
{
return GetTitle ( aAccName ) ;
}
2003-04-01 22:18:29 +00:00
NS_IMETHODIMP nsDocAccessible : : GetAccRole ( PRUint32 * _retval )
{
* _retval = ROLE_PANE ;
return NS_OK ;
}
2003-04-15 08:45:55 +00:00
NS_IMETHODIMP nsDocAccessible : : GetAccValue ( nsAString & aAccValue )
2003-04-01 22:18:29 +00:00
{
2003-04-15 08:45:55 +00:00
return GetURL ( aAccValue ) ;
2003-04-01 22:18:29 +00:00
}
2003-04-15 08:45:55 +00:00
NS_IMETHODIMP nsDocAccessible : : GetAccState ( PRUint32 * aAccState )
2003-04-01 22:18:29 +00:00
{
2003-04-15 08:45:55 +00:00
* aAccState = STATE_FOCUSABLE ;
if ( mBusy = = eBusyStateLoading )
* aAccState | = STATE_BUSY ;
2003-05-15 08:37:38 +00:00
# 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
2003-04-15 08:45:55 +00:00
return NS_OK ;
2003-04-01 22:18:29 +00:00
}
// ------- nsIAccessibleDocument Methods (5) ---------------
NS_IMETHODIMP nsDocAccessible : : GetURL ( nsAString & aURL )
{
2003-04-15 08:45:55 +00:00
if ( ! mDocument ) {
return NS_ERROR_FAILURE ; // Document has been shut down
}
2003-04-01 22:18:29 +00:00
nsCOMPtr < nsISupports > container ;
mDocument - > GetContainer ( getter_AddRefs ( container ) ) ;
nsCOMPtr < nsIWebNavigation > webNav ( do_GetInterface ( container ) ) ;
nsCAutoString theURL ;
if ( webNav ) {
nsCOMPtr < nsIURI > pURI ;
webNav - > GetCurrentURI ( getter_AddRefs ( pURI ) ) ;
if ( pURI )
pURI - > GetSpec ( theURL ) ;
}
//XXXaaronl Need to use CopyUTF8toUCS2(nsDependentCString(theURL), aURL);
// when it's written
aURL . Assign ( NS_ConvertUTF8toUCS2 ( theURL ) ) ;
return NS_OK ;
}
NS_IMETHODIMP nsDocAccessible : : GetTitle ( nsAString & aTitle )
{
2003-04-15 08:45:55 +00:00
return mDocument ? mDocument - > GetDocumentTitle ( aTitle ) : NS_ERROR_FAILURE ;
2003-04-01 22:18:29 +00:00
}
NS_IMETHODIMP nsDocAccessible : : GetMimeType ( nsAString & aMimeType )
{
nsCOMPtr < nsIDOMNSDocument > domnsDocument ( do_QueryInterface ( mDocument ) ) ;
if ( domnsDocument ) {
return domnsDocument - > GetContentType ( aMimeType ) ;
}
return NS_ERROR_FAILURE ;
}
NS_IMETHODIMP nsDocAccessible : : GetDocType ( nsAString & aDocType )
{
nsCOMPtr < nsIDOMDocument > domDoc ( do_QueryInterface ( mDocument ) ) ;
nsCOMPtr < nsIDOMDocumentType > docType ;
2003-04-11 00:56:27 +00:00
# ifdef MOZ_XUL
nsCOMPtr < nsIXULDocument > xulDoc ( do_QueryInterface ( mDocument ) ) ;
2003-04-01 22:18:29 +00:00
if ( xulDoc ) {
aDocType = NS_LITERAL_STRING ( " window " ) ; // doctype not implemented for XUL at time of writing - causes assertion
return NS_OK ;
2003-04-11 00:56:27 +00:00
} else
# endif
if ( domDoc & & NS_SUCCEEDED ( domDoc - > GetDoctype ( getter_AddRefs ( docType ) ) ) & & docType ) {
2003-04-01 22:18:29 +00:00
return docType - > GetName ( aDocType ) ;
}
return NS_ERROR_FAILURE ;
}
NS_IMETHODIMP nsDocAccessible : : GetNameSpaceURIForID ( PRInt16 aNameSpaceID , nsAString & aNameSpaceURI )
{
if ( mDocument ) {
nsCOMPtr < nsINameSpaceManager > nameSpaceManager =
do_GetService ( NS_NAMESPACEMANAGER_CONTRACTID ) ;
if ( nameSpaceManager )
return nameSpaceManager - > GetNameSpaceURI ( aNameSpaceID , aNameSpaceURI ) ;
}
return NS_ERROR_FAILURE ;
}
NS_IMETHODIMP nsDocAccessible : : GetCaretAccessible ( nsIAccessibleCaret * * aCaretAccessible )
{
// We only have a caret accessible on the root document
* aCaretAccessible = nsnull ;
return NS_OK ;
}
NS_IMETHODIMP nsDocAccessible : : GetWindow ( void * * aWindow )
{
* aWindow = mWnd ;
return NS_OK ;
}
2003-05-15 08:37:38 +00:00
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 ;
}
2003-04-15 08:45:55 +00:00
NS_IMETHODIMP nsDocAccessible : : GetCachedAccessNode ( void * aUniqueID , nsIAccessNode * * aAccessNode )
{
NS_ASSERTION ( mAccessNodeCache , " No accessibility cache for document " ) ;
GetCacheEntry ( mAccessNodeCache , aUniqueID , aAccessNode ) ; // Addrefs for us
return NS_OK ;
}
NS_IMETHODIMP nsDocAccessible : : CacheAccessNode ( void * aUniqueID , nsIAccessNode * aAccessNode )
2003-04-01 22:18:29 +00:00
{
2003-04-15 08:45:55 +00:00
NS_ASSERTION ( mAccessNodeCache , " No accessibility cache for document " ) ;
PutCacheEntry ( mAccessNodeCache , aUniqueID , aAccessNode ) ;
2003-04-01 22:18:29 +00:00
return NS_OK ;
}
2003-05-15 08:37:38 +00:00
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 ( ) ;
}
2003-04-28 10:24:52 +00:00
NS_IMETHODIMP nsDocAccessible : : Destroy ( )
{
2003-05-13 08:23:32 +00:00
NS_ASSERTION ( gGlobalDocAccessibleCache , " No global doc accessible cache " ) ;
if ( gGlobalDocAccessibleCache ) {
2003-04-28 10:24:52 +00:00
# ifdef OLD_HASH
2003-05-13 08:23:32 +00:00
nsVoidKey key ( NS_STATIC_CAST ( void * , mWeakShell ) ) ;
gGlobalDocAccessibleCache - > Remove ( & key ) ;
2003-04-28 10:24:52 +00:00
# else
2003-05-13 08:23:32 +00:00
gGlobalDocAccessibleCache - > Remove ( NS_STATIC_CAST ( void * , mWeakShell ) ) ;
2003-04-28 10:24:52 +00:00
# endif
2003-05-13 08:23:32 +00:00
}
2003-04-28 10:24:52 +00:00
return Shutdown ( ) ;
}
2003-04-01 22:18:29 +00:00
NS_IMETHODIMP nsDocAccessible : : Shutdown ( )
{
2003-04-15 08:45:55 +00:00
if ( ! mWeakShell ) {
return NS_OK ; // Already shutdown
}
2003-05-15 08:37:38 +00:00
RemoveEventListeners ( ) ;
2003-04-15 08:45:55 +00:00
mWeakShell = nsnull ; // Avoid reentrancy
2003-05-15 08:37:38 +00:00
mEditor = nsnull ;
2003-04-28 10:24:52 +00:00
if ( mScrollWatchTimer ) {
mScrollWatchTimer - > Cancel ( ) ;
mScrollWatchTimer = nsnull ;
}
if ( mDocLoadTimer ) {
mDocLoadTimer - > Cancel ( ) ;
mDocLoadTimer = nsnull ;
}
2003-04-15 08:45:55 +00:00
mWebProgress = nsnull ;
if ( mAccessNodeCache ) {
# ifdef OLD_HASH
nsSupportsHashtable * hashToClear = mAccessNodeCache ; // Avoid reentrency
# else
nsInterfaceHashtable < nsVoidHashKey , nsIAccessNode > * hashToClear = mAccessNodeCache ; // Avoid reentrency
# endif
mAccessNodeCache = nsnull ;
ClearCache ( hashToClear ) ;
delete hashToClear ;
}
2003-04-01 22:18:29 +00:00
mDocument = nsnull ;
2003-05-15 08:37:38 +00:00
2003-04-01 22:18:29 +00:00
return nsAccessibleWrap : : Shutdown ( ) ;
}
nsIFrame * nsDocAccessible : : GetFrame ( )
{
2003-04-15 08:45:55 +00:00
nsCOMPtr < nsIPresShell > shell ( do_QueryReferent ( mWeakShell ) ) ;
2003-04-01 22:18:29 +00:00
nsIFrame * root = nsnull ;
if ( shell )
shell - > GetRootFrame ( & root ) ;
return root ;
}
void nsDocAccessible : : GetBounds ( nsRect & aBounds , nsIFrame * * aRelativeFrame )
{
* aRelativeFrame = GetFrame ( ) ;
2003-05-17 12:00:07 +00:00
if ( * aRelativeFrame )
( * aRelativeFrame ) - > GetRect ( aBounds ) ;
2003-04-01 22:18:29 +00:00
}
2003-04-15 08:45:55 +00:00
void nsDocAccessible : : AddContentDocListeners ( )
{
// 1) Set up scroll position listener
// 2) Set up web progress listener - we need to know
// when page loading is finished
// That way we can send the STATE_CHANGE events for
// the MSAA root "pane" object (ROLE_PANE),
// and change the STATE_BUSY bit flag
// Do this only for top level content documents
nsCOMPtr < nsIPresShell > presShell ( GetPresShell ( ) ) ;
if ( ! presShell )
return ;
AddScrollListener ( presShell ) ;
nsCOMPtr < nsISupports > container ;
mDocument - > GetContainer ( getter_AddRefs ( container ) ) ;
nsCOMPtr < nsIDocShellTreeItem > docShellTreeItem ( do_QueryInterface ( container ) ) ;
if ( ! docShellTreeItem )
return ;
// Make sure we're a content docshell
// We don't want to listen to chrome progress
PRInt32 itemType ;
docShellTreeItem - > GetItemType ( & itemType ) ;
if ( itemType ! = nsIDocShellTreeItem : : typeContent )
return ;
2003-05-15 08:37:38 +00:00
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 ;
}
2003-04-15 08:45:55 +00:00
2003-05-15 08:37:38 +00:00
nsCOMPtr < nsIPresContext > context ;
presShell - > GetPresContext ( getter_AddRefs ( context ) ) ;
if ( ! context )
return ;
2003-04-15 08:45:55 +00:00
2003-05-15 08:37:38 +00:00
mWebProgress = do_GetInterface ( docShellTreeItem ) ;
if ( ! mWebProgress )
return ;
2003-04-15 08:45:55 +00:00
2003-05-15 08:37:38 +00:00
mWebProgress - > AddProgressListener ( this , nsIWebProgress : : NOTIFY_LOCATION |
nsIWebProgress : : NOTIFY_STATE_DOCUMENT ) ;
2003-04-15 08:45:55 +00:00
2003-05-15 08:37:38 +00:00
mWebProgress - > GetIsLoadingDocument ( & isLoading ) ;
}
2003-04-15 08:45:55 +00:00
mIsNewDocument = PR_TRUE ;
mBusy = eBusyStateLoading ;
if ( ! isLoading ) {
// If already loaded, fire "done loading" event after short timeout
// If we fired the event here, we'd get reentrancy problems
2003-05-15 08:37:38 +00:00
// Otherwise, if the doc is still loading,
// the event will be fired from OnStateChange when the load is done
2003-04-15 08:45:55 +00:00
mDocLoadTimer = do_CreateInstance ( " @mozilla.org/timer;1 " ) ;
if ( mDocLoadTimer ) {
mDocLoadTimer - > InitWithFuncCallback ( DocLoadCallback , this , 1 ,
nsITimer : : TYPE_ONE_SHOT ) ;
}
}
2003-04-28 10:24:52 +00:00
// 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 ) ) ;
NS_ASSERTION ( target , " No dom event target for document " ) ;
nsresult rv = target - > AddEventListener ( NS_LITERAL_STRING ( " DOMAttrModified " ) ,
this , PR_TRUE ) ;
NS_ASSERTION ( NS_SUCCEEDED ( rv ) , " failed to register listener " ) ;
rv = target - > AddEventListener ( NS_LITERAL_STRING ( " DOMSubtreeModified " ) ,
this , PR_TRUE ) ;
NS_ASSERTION ( NS_SUCCEEDED ( rv ) , " failed to register listener " ) ;
rv = target - > AddEventListener ( NS_LITERAL_STRING ( " DOMNodeInserted " ) ,
this , PR_TRUE ) ;
NS_ASSERTION ( NS_SUCCEEDED ( rv ) , " failed to register listener " ) ;
rv = target - > AddEventListener ( NS_LITERAL_STRING ( " DOMNodeRemoved " ) ,
this , PR_TRUE ) ;
NS_ASSERTION ( NS_SUCCEEDED ( rv ) , " failed to register listener " ) ;
rv = target - > AddEventListener ( NS_LITERAL_STRING ( " DOMNodeInsertedIntoDocument " ) ,
this , PR_TRUE ) ;
NS_ASSERTION ( NS_SUCCEEDED ( rv ) , " failed to register listener " ) ;
rv = target - > AddEventListener ( NS_LITERAL_STRING ( " DOMNodeRemovedFromDocument " ) ,
this , PR_TRUE ) ;
NS_ASSERTION ( NS_SUCCEEDED ( rv ) , " failed to register listener " ) ;
2003-04-15 08:45:55 +00:00
}
void nsDocAccessible : : RemoveContentDocListeners ( )
{
// Remove listeners associated with content documents
// Remove web progress listener
if ( mWebProgress ) {
mWebProgress - > RemoveProgressListener ( this ) ;
mWebProgress = nsnull ;
}
// Remove scroll position listener
nsCOMPtr < nsIPresShell > presShell ( do_QueryReferent ( mWeakShell ) ) ;
2003-05-15 08:37:38 +00:00
if ( presShell ) {
RemoveScrollListener ( presShell ) ;
}
2003-04-28 10:24:52 +00:00
nsCOMPtr < nsIDOMEventTarget > target ( do_QueryInterface ( mDocument ) ) ;
NS_ASSERTION ( target , " No dom event target for document " ) ;
target - > RemoveEventListener ( NS_LITERAL_STRING ( " DOMAttrModified " ) , this , PR_TRUE ) ;
target - > RemoveEventListener ( NS_LITERAL_STRING ( " DOMSubtreeModified " ) , this , PR_TRUE ) ;
target - > RemoveEventListener ( NS_LITERAL_STRING ( " DOMNodeInserted " ) , this , PR_TRUE ) ;
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 ) ;
2003-05-15 08:37:38 +00:00
nsCOMPtr < nsISupports > container ;
mDocument - > GetContainer ( getter_AddRefs ( container ) ) ;
nsCOMPtr < nsICommandManager > commandManager = do_GetInterface ( container ) ;
if ( commandManager ) {
commandManager - > RemoveCommandObserver ( this , " obs_documentCreated " ) ;
}
2003-04-15 08:45:55 +00:00
}
void nsDocAccessible : : FireDocLoadFinished ( )
{
2003-04-28 10:24:52 +00:00
if ( ! mDocument | | ! mWeakShell )
return ; // Document has been shut down
2003-04-25 02:18:54 +00:00
2003-04-15 08:45:55 +00:00
if ( mIsNewDocument ) {
mIsNewDocument = PR_FALSE ;
if ( mBusy ! = eBusyStateDone ) {
# ifndef MOZ_ACCESSIBILITY_ATK
FireToolkitEvent ( nsIAccessibleEventReceiver : : EVENT_STATE_CHANGE , this , nsnull ) ;
# endif
}
}
2003-05-15 08:37:38 +00:00
mBusy = eBusyStateDone ;
2003-04-15 08:45:55 +00:00
}
void nsDocAccessible : : DocLoadCallback ( nsITimer * aTimer , void * aClosure )
{
// Doc has finished loading, fire "load finished" event
// This path is only used if the doc was already finished loading
// when the DocAccessible was created.
// Otherwise, ::OnStateChange() fires the event when doc is loaded.
nsDocAccessible * docAcc = NS_REINTERPRET_CAST ( nsDocAccessible * , aClosure ) ;
if ( docAcc )
docAcc - > FireDocLoadFinished ( ) ;
}
void nsDocAccessible : : ScrollTimerCallback ( nsITimer * aTimer , void * aClosure )
{
nsDocAccessible * docAcc = NS_REINTERPRET_CAST ( nsDocAccessible * , aClosure ) ;
if ( docAcc & & docAcc - > mScrollPositionChangedTicks & &
+ + docAcc - > mScrollPositionChangedTicks > 2 ) {
// Whenever scroll position changes, mScrollPositionChangeTicks gets reset to 1
// We only want to fire accessibilty scroll event when scrolling stops or pauses
// Therefore, we wait for no scroll events to occur between 2 ticks of this timer
// That indicates a pause in scrolling, so we fire the accessibilty scroll event
docAcc - > FireToolkitEvent ( nsIAccessibleEventReceiver : : EVENT_SCROLLINGEND , docAcc , nsnull ) ;
docAcc - > mScrollPositionChangedTicks = 0 ;
2003-05-01 10:25:45 +00:00
if ( docAcc - > mScrollWatchTimer ) {
docAcc - > mScrollWatchTimer - > Cancel ( ) ;
docAcc - > mScrollWatchTimer = nsnull ;
}
2003-04-15 08:45:55 +00:00
}
}
void nsDocAccessible : : AddScrollListener ( nsIPresShell * aPresShell )
{
nsCOMPtr < nsIViewManager > vm ;
if ( aPresShell )
aPresShell - > GetViewManager ( getter_AddRefs ( vm ) ) ;
nsIScrollableView * scrollableView = nsnull ;
if ( vm )
vm - > GetRootScrollableView ( & scrollableView ) ;
if ( scrollableView )
2003-05-15 08:37:38 +00:00
scrollableView - > AddScrollPositionListener ( this ) ;
2003-04-15 08:45:55 +00:00
}
void nsDocAccessible : : RemoveScrollListener ( nsIPresShell * aPresShell )
{
nsCOMPtr < nsIViewManager > vm ;
if ( aPresShell )
aPresShell - > GetViewManager ( getter_AddRefs ( vm ) ) ;
nsIScrollableView * scrollableView = nsnull ;
if ( vm )
vm - > GetRootScrollableView ( & scrollableView ) ;
if ( scrollableView )
2003-05-15 08:37:38 +00:00
scrollableView - > RemoveScrollPositionListener ( this ) ;
2003-04-15 08:45:55 +00:00
}
NS_IMETHODIMP nsDocAccessible : : ScrollPositionWillChange ( nsIScrollableView * aView , nscoord aX , nscoord aY )
{
return NS_OK ;
}
NS_IMETHODIMP nsDocAccessible : : ScrollPositionDidChange ( nsIScrollableView * aScrollableView , nscoord aX , nscoord aY )
{
// Start new timer, if the timer cycles at least 1 full cycle without more scroll position changes,
// then the ::Notify() method will fire the accessibility event for scroll position changes
const PRUint32 kScrollPosCheckWait = 50 ;
if ( mScrollWatchTimer ) {
mScrollWatchTimer - > SetDelay ( kScrollPosCheckWait ) ; // Create new timer, to avoid leaks
}
else {
mScrollWatchTimer = do_CreateInstance ( " @mozilla.org/timer;1 " ) ;
if ( mScrollWatchTimer ) {
mScrollWatchTimer - > InitWithFuncCallback ( ScrollTimerCallback , this ,
kScrollPosCheckWait ,
nsITimer : : TYPE_REPEATING_SLACK ) ;
}
}
mScrollPositionChangedTicks = 1 ;
return NS_OK ;
}
NS_IMETHODIMP nsDocAccessible : : OnStateChange ( nsIWebProgress * aWebProgress ,
nsIRequest * aRequest , PRUint32 aStateFlags , nsresult aStatus )
{
2003-05-15 08:37:38 +00:00
if ( ( aStateFlags & STATE_IS_DOCUMENT ) & & ( aStateFlags & STATE_STOP ) ) {
2003-04-15 08:45:55 +00:00
FireDocLoadFinished ( ) ; // Doc is ready!
2003-05-15 08:37:38 +00:00
}
2003-04-15 08:45:55 +00:00
return NS_OK ;
}
/* void onProgressChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long aCurSelfProgress, in long aMaxSelfProgress, in long aCurTotalProgress, in long aMaxTotalProgress); */
NS_IMETHODIMP nsDocAccessible : : OnProgressChange ( nsIWebProgress * aWebProgress ,
nsIRequest * aRequest , PRInt32 aCurSelfProgress , PRInt32 aMaxSelfProgress ,
PRInt32 aCurTotalProgress , PRInt32 aMaxTotalProgress )
{
NS_NOTREACHED ( " notification excluded in AddProgressListener(...) " ) ;
return NS_OK ;
}
/* void onLocationChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsIURI location); */
NS_IMETHODIMP nsDocAccessible : : OnLocationChange ( nsIWebProgress * aWebProgress ,
nsIRequest * aRequest , nsIURI * location )
{
// Load has been verified, it will occur, about to commence
2003-04-28 10:24:52 +00:00
// We won't fire a "doc finished loading" event on this nsDocAccessible
// Instead we fire that on the new nsDocAccessible that is created for the new doc
2003-04-15 08:45:55 +00:00
mIsNewDocument = PR_FALSE ; // We're a doc that's going away
if ( mBusy ! = eBusyStateLoading ) {
mBusy = eBusyStateLoading ;
// Fire a "new doc has started to load" event
# ifndef MOZ_ACCESSIBILITY_ATK
FireToolkitEvent ( nsIAccessibleEventReceiver : : EVENT_STATE_CHANGE , this , nsnull ) ;
# else
AtkChildrenChange childrenData ;
childrenData . index = - 1 ;
childrenData . child = 0 ;
childrenData . add = PR_FALSE ;
FireToolkitEvent ( nsIAccessibleEventReceiver : : EVENT_REORDER , this , & childrenData ) ;
# endif
}
return NS_OK ;
}
/* void onStatusChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsresult aStatus, in wstring aMessage); */
NS_IMETHODIMP nsDocAccessible : : OnStatusChange ( nsIWebProgress * aWebProgress ,
nsIRequest * aRequest , nsresult aStatus , const PRUnichar * aMessage )
{
NS_NOTREACHED ( " notification excluded in AddProgressListener(...) " ) ;
return NS_OK ;
}
/* void onSecurityChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in unsigned long state); */
NS_IMETHODIMP nsDocAccessible : : OnSecurityChange ( nsIWebProgress * aWebProgress ,
nsIRequest * aRequest , PRUint32 state )
{
NS_NOTREACHED ( " notification excluded in AddProgressListener(...) " ) ;
return NS_OK ;
}
2003-05-15 08:37:38 +00:00
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 ) ;
}
2003-04-28 10:24:52 +00:00
// ---------- Mutation event listeners ------------
NS_IMETHODIMP nsDocAccessible : : NodeInserted ( nsIDOMEvent * aEvent )
{
HandleMutationEvent ( aEvent , nsIAccessibleEventReceiver : : EVENT_CREATE ) ;
return NS_OK ;
}
NS_IMETHODIMP nsDocAccessible : : NodeRemoved ( nsIDOMEvent * aEvent )
{
// The related node for the event will be the parent of the removed node or subtree
HandleMutationEvent ( aEvent , nsIAccessibleEventReceiver : : EVENT_DESTROY ) ;
return NS_OK ;
}
NS_IMETHODIMP nsDocAccessible : : SubtreeModified ( nsIDOMEvent * aEvent )
{
HandleMutationEvent ( aEvent , nsIAccessibleEventReceiver : : EVENT_REORDER ) ;
return NS_OK ;
}
NS_IMETHODIMP nsDocAccessible : : AttrModified ( nsIDOMEvent * aMutationEvent )
{
// XXX todo
// We will probably need to handle special cases here
// For example, if an <img>'s usemap attribute is modified
// Otherwise it may just be a state change, for example an object changing
// its visibility.
return NS_OK ;
}
NS_IMETHODIMP nsDocAccessible : : NodeRemovedFromDocument ( nsIDOMEvent * aMutationEvent )
{
// Not implemented yet, see bug 74220
return NS_OK ;
}
NS_IMETHODIMP nsDocAccessible : : NodeInsertedIntoDocument ( nsIDOMEvent * aMutationEvent )
{
// Not implemented yet, see bug 74219
// This is different from NodeInserted() in that it's fired when
// a node is inserted into a document, but isn't necessarily mean that
// it's becoming part of the DOM tree.
return NS_OK ;
}
NS_IMETHODIMP nsDocAccessible : : HandleEvent ( nsIDOMEvent * aEvent )
{
NS_NOTREACHED ( " Should be handled by specific methods like NodeInserted, etc. " ) ;
return NS_OK ;
}
NS_IMETHODIMP nsDocAccessible : : CharacterDataModified ( nsIDOMEvent * aMutationEvent )
{
return NS_OK ;
}
2003-05-15 08:37:38 +00:00
NS_IMETHODIMP nsDocAccessible : : InvalidateCacheSubtree ( nsIDOMNode * aStartNode )
2003-04-28 10:24:52 +00:00
{
// Invalidate cache subtree
// We have to check for accessibles for each dom node by traversing DOM tree
// instead of just the accessible tree, although that would be faster
// Otherwise we might miss the nsAccessNode's that are not nsAccessible's.
if ( ! aStartNode )
2003-05-15 08:37:38 +00:00
return NS_ERROR_FAILURE ;
2003-04-28 10:24:52 +00:00
nsCOMPtr < nsIDOMNode > iterNode ( aStartNode ) , nextNode ;
nsCOMPtr < nsIAccessNode > accessNode ;
do {
2003-05-15 08:37:38 +00:00
GetCachedAccessNode ( iterNode , getter_AddRefs ( accessNode ) ) ;
2003-04-28 10:24:52 +00:00
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()
2003-05-15 08:37:38 +00:00
// 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
}
2003-04-28 10:24:52 +00:00
}
iterNode - > GetFirstChild ( getter_AddRefs ( nextNode ) ) ;
if ( nextNode ) {
iterNode = nextNode ;
continue ;
}
if ( iterNode = = aStartNode )
break ;
iterNode - > GetNextSibling ( getter_AddRefs ( nextNode ) ) ;
if ( nextNode ) {
iterNode = nextNode ;
continue ;
}
do {
iterNode - > GetParentNode ( getter_AddRefs ( nextNode ) ) ;
if ( ! nextNode | | nextNode = = aStartNode ) {
2003-05-15 08:37:38 +00:00
return NS_OK ;
2003-04-28 10:24:52 +00:00
}
nextNode - > GetNextSibling ( getter_AddRefs ( iterNode ) ) ;
if ( iterNode )
break ;
iterNode = nextNode ;
} while ( PR_TRUE ) ;
}
while ( iterNode & & iterNode ! = aStartNode ) ;
2003-05-15 08:37:38 +00:00
return NS_OK ;
2003-04-28 10:24:52 +00:00
}
2003-05-15 08:37:38 +00:00
NS_IMETHODIMP
nsDocAccessible : : GetAccessibleInParentChain ( nsIDOMNode * aNode ,
nsIAccessible * * aAccessible )
2003-04-28 10:24:52 +00:00
{
2003-05-15 08:37:38 +00:00
nsCOMPtr < nsIAccessibilityService > accService =
do_GetService ( " @mozilla.org/accessibilityService;1 " ) ;
if ( ! accService )
return nsnull ;
2003-04-28 10:24:52 +00:00
2003-05-15 08:37:38 +00:00
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 ;
2003-04-28 10:24:52 +00:00
}
2003-05-15 08:37:38 +00:00
return NS_OK ;
}
void nsDocAccessible : : HandleMutationEvent ( nsIDOMEvent * aEvent , PRUint32 aAccessibleEventType )
{
nsCOMPtr < nsIDOMMutationEvent > mutationEvent ( do_QueryInterface ( aEvent ) ) ;
NS_ASSERTION ( mutationEvent , " Not a mutation event! " ) ;
nsCOMPtr < nsIDOMEventTarget > domEventTarget ;
mutationEvent - > GetTarget ( getter_AddRefs ( domEventTarget ) ) ;
2003-04-28 10:24:52 +00:00
nsCOMPtr < nsIDOMNode > targetNode ( do_QueryInterface ( domEventTarget ) ) ;
2003-05-15 08:37:38 +00:00
nsCOMPtr < nsIDOMNode > subTreeToInvalidate ;
mutationEvent - > GetRelatedNode ( getter_AddRefs ( subTreeToInvalidate ) ) ;
NS_ASSERTION ( subTreeToInvalidate , " No old sub tree being replaced in DOMSubtreeModified " ) ;
2003-04-28 10:24:52 +00:00
2003-05-15 08:37:38 +00:00
if ( ! targetNode ) {
targetNode = subTreeToInvalidate ;
2003-04-28 10:24:52 +00:00
}
2003-05-15 08:37:38 +00:00
else if ( aAccessibleEventType = = nsIAccessibleEventReceiver : : EVENT_REORDER ) {
subTreeToInvalidate = targetNode ; // targetNode is parent for DOMNodeRemoved event
2003-04-28 10:24:52 +00:00
}
2003-05-15 08:37:38 +00:00
nsCOMPtr < nsIAccessibleDocument > docAccessible ;
GetEventDocAccessible ( subTreeToInvalidate , getter_AddRefs ( docAccessible ) ) ;
docAccessible - > InvalidateCacheSubtree ( subTreeToInvalidate ) ;
2003-04-28 10:24:52 +00:00
// 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.
nsCOMPtr < nsIAccessible > accessible ;
2003-05-15 08:37:38 +00:00
docAccessible - > GetAccessibleInParentChain ( targetNode , getter_AddRefs ( accessible ) ) ;
2003-04-28 10:24:52 +00:00
accessible - > InvalidateChildren ( ) ;
# ifdef XP_WIN
2003-05-15 08:37:38 +00:00
// Windows MSAA clients crash if they listen to create or destroy events
2003-04-28 10:24:52 +00:00
aAccessibleEventType = nsIAccessibleEventReceiver : : EVENT_REORDER ;
# endif
accessible - > FireToolkitEvent ( aAccessibleEventType , accessible , nsnull ) ;
}