Bug 100498. Active accessibility: Clear, consistent events for page load busy states. r=jgaunt, sr=waterson.

This commit is contained in:
aaronl%netscape.com 2001-09-25 08:34:42 +00:00
Родитель 3558787cfd
Коммит 97eb525c51
10 изменённых файлов: 408 добавлений и 237 удалений

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

@ -28,5 +28,5 @@
interface nsIAccessibleEventReceiver : nsISupports
{
void addAccessibleEventListener(in nsIAccessibleEventListener aListener);
void removeAccessibleEventListener(in nsIAccessibleEventListener aListener);
void removeAccessibleEventListener();
};

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

@ -48,6 +48,7 @@
#include "nsIDOMEventTarget.h"
#include "nsIDOMElement.h"
#include "nsIDOMEventReceiver.h"
#include "nsIDOMEventListener.h"
#include "nsReadableUtils.h"
#include "nsILink.h"
#include "nsHTMLFormControlAccessible.h"
@ -68,12 +69,19 @@
#include "nsIServiceManager.h"
#include "nsHTMLSelectListAccessible.h"
#include "nsIDOMHTMLSelectElement.h"
#include "nsIWebProgress.h"
#include "nsCURILoader.h"
#include "nsIInterfaceRequestorUtils.h"
NS_INTERFACE_MAP_BEGIN(nsRootAccessible)
NS_INTERFACE_MAP_ENTRY(nsIAccessibleDocument)
NS_INTERFACE_MAP_ENTRY(nsIAccessibleEventReceiver)
NS_INTERFACE_MAP_ENTRY(nsIDOMFocusListener)
NS_INTERFACE_MAP_ENTRY(nsIDOMFormListener)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFormListener)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMFormListener)
NS_INTERFACE_MAP_END_INHERITING(nsAccessible)
@ -86,7 +94,8 @@ NS_IMPL_RELEASE_INHERITED(nsRootAccessible, nsAccessible);
// construction
//-----------------------------------------------------
nsRootAccessible::nsRootAccessible(nsIWeakReference* aShell):nsAccessible(nsnull,aShell),
nsDocAccessibleMixin(aShell), mAccService(do_GetService("@mozilla.org/accessibilityService;1"))
nsDocAccessibleMixin(aShell), mAccService(do_GetService("@mozilla.org/accessibilityService;1")),
mBusy(eBusyStateUninitialized)
{
mListener = nsnull;
nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mPresShell));
@ -104,7 +113,7 @@ nsRootAccessible::nsRootAccessible(nsIWeakReference* aShell):nsAccessible(nsnull
nsRootAccessible::~nsRootAccessible()
{
nsLayoutAtoms::ReleaseAtoms();
RemoveAccessibleEventListener(mListener);
RemoveAccessibleEventListener();
}
/* attribute wstring accName; */
@ -174,81 +183,128 @@ NS_IMETHODIMP nsRootAccessible::GetAccRole(PRUint32 *aAccRole)
*/
*aAccRole = ROLE_PANE;
return NS_OK;
return NS_OK;
}
NS_IMETHODIMP nsRootAccessible::GetAccState(PRUint32 *aAccState)
{
return nsDocAccessibleMixin::GetAccState(aAccState);
*aAccState = STATE_FOCUSABLE;
if (mBusy == eBusyStateLoading)
*aAccState |= STATE_BUSY;
return NS_OK;
}
NS_IMETHODIMP nsRootAccessible::GetAccValue(nsAWritableString& aAccValue)
{
return GetURL(aAccValue);
}
void nsRootAccessible::Notify(nsITimer *timer)
{
// Short timer is finished
if (mBusy != eBusyStateDone) {
mBusy = eBusyStateDone;
if (mListener)
mListener->HandleEvent(nsIAccessibleEventListener::EVENT_STATE_CHANGE, this);
}
}
NS_IMETHODIMP nsRootAccessible::StartDocReadyTimer()
{
nsresult rv;
if (!mTimer)
mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
if (NS_SUCCEEDED(rv)) {
const PRUint32 kUpdateTimerDelay = 1;
rv = mTimer->Init(NS_STATIC_CAST(nsITimerCallback*, this), kUpdateTimerDelay);
}
return rv;
}
/* void addAccessibleEventListener (in nsIAccessibleEventListener aListener); */
NS_IMETHODIMP nsRootAccessible::AddAccessibleEventListener(nsIAccessibleEventListener *aListener)
{
if (!mListener)
{
// add an event listener to the document
nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mPresShell));
if (!shell)
return NS_ERROR_FAILURE;
if (mListener)
return NS_OK;
nsCOMPtr<nsIDocument> document;
shell->GetDocument(getter_AddRefs(document));
mListener = aListener;
// use AddEventListener from the nsIDOMEventTarget interface
nsCOMPtr<nsIDOMEventTarget> target;
if (NS_SUCCEEDED(document->QueryInterface(NS_GET_IID(nsIDOMEventTarget), getter_AddRefs(target))) && target)
{
nsresult rv = NS_OK;
// we're a DOMEventListener now!!
nsCOMPtr<nsIDOMEventListener> listener;
rv = this->QueryInterface( NS_GET_IID(nsIDOMEventListener), getter_AddRefs(listener) );
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to QI");
// capture DOM focus events
rv = target->AddEventListener( NS_LITERAL_STRING("focus") , listener, PR_TRUE );
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
// capture Form change events
rv = target->AddEventListener( NS_LITERAL_STRING("change") , listener, PR_TRUE );
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
// add ourself as a CheckboxStateChange listener ( custom event fired in nsHTMLInputElement.cpp )
rv = target->AddEventListener( NS_LITERAL_STRING("CheckboxStateChange") , listener, PR_TRUE );
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
// add ourself as a RadiobuttonStateChange listener ( custom event fired in nsHTMLInputElement.cpp )
rv = target->AddEventListener( NS_LITERAL_STRING("RadiobuttonStateChange") , listener, PR_TRUE );
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
}
// add an event listener to the document
nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mPresShell));
if (!shell)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDocument> document;
shell->GetDocument(getter_AddRefs(document));
if (!document)
return NS_ERROR_FAILURE;
// use AddEventListener from the nsIDOMEventTarget interface
nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(document));
if (target) {
// capture DOM focus events
nsresult rv = target->AddEventListener(NS_LITERAL_STRING("focus"), NS_STATIC_CAST(nsIDOMFocusListener*, this), PR_TRUE);
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
rv = target->AddEventListener(NS_LITERAL_STRING("blur"), NS_STATIC_CAST(nsIDOMFocusListener*, this), PR_TRUE);
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
// capture Form change events
rv = target->AddEventListener(NS_LITERAL_STRING("change"), NS_STATIC_CAST(nsIDOMFormListener*, this), PR_TRUE);
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
// add ourself as a CheckboxStateChange listener (custom event fired in nsHTMLInputElement.cpp)
rv = target->AddEventListener(NS_LITERAL_STRING("CheckboxStateChange"), NS_STATIC_CAST(nsIDOMFormListener*, this), PR_TRUE);
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
// add ourself as a RadiobuttonStateChange listener (custom event fired in nsHTMLInputElement.cpp)
rv = target->AddEventListener(NS_LITERAL_STRING("RadiobuttonStateChange"), NS_STATIC_CAST(nsIDOMFormListener*, this), PR_TRUE);
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
// Extremely short timer, after which we announce that pane is finished loading
// By waiting until after this short time, we know that the 3rd party accessibility software
// has received it's accessible, and can handle events on it.
StartDocReadyTimer();
}
// create a weak reference to the listener
mListener = aListener;
nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mPresShell));
NS_ASSERTION(shell,"Shell is gone!!! What are we doing here?");
if (presShell) {
nsCOMPtr<nsIPresContext> context;
presShell->GetPresContext(getter_AddRefs(context));
if (context) {
nsCOMPtr<nsISupports> container;
context->GetContainer(getter_AddRefs(container));
if (container) {
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
if (docShell) {
nsCOMPtr<nsIWebProgress> webProgress(do_GetInterface(docShell));
if (webProgress) {
webProgress->AddProgressListener(this);
}
}
}
}
}
return NS_OK;
}
/* void removeAccessibleEventListener (in nsIAccessibleEventListener aListener); */
NS_IMETHODIMP nsRootAccessible::RemoveAccessibleEventListener(nsIAccessibleEventListener *aListener)
/* void removeAccessibleEventListener (); */
NS_IMETHODIMP nsRootAccessible::RemoveAccessibleEventListener()
{
if (mListener)
{
nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mPresShell));
nsCOMPtr<nsIDocument> document;
if (!shell)
return NS_OK;
shell->GetDocument(getter_AddRefs(document));
nsCOMPtr<nsIDOMEventReceiver> erP;
if (NS_SUCCEEDED(document->QueryInterface(NS_GET_IID(nsIDOMEventReceiver), getter_AddRefs(erP))) && erP)
{
nsresult rv = erP->RemoveEventListenerByIID(NS_STATIC_CAST(nsIDOMFocusListener *, this), NS_GET_IID(nsIDOMFocusListener));
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
}
if (mListener) {
nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mPresShell));
nsCOMPtr<nsIDocument> document;
if (shell) {
shell->GetDocument(getter_AddRefs(document));
nsCOMPtr<nsIDOMEventReceiver> eventReceiver(do_QueryInterface(document));
if (eventReceiver) {
nsresult rv = eventReceiver->RemoveEventListenerByIID(NS_STATIC_CAST(nsIDOMFocusListener *, this), NS_GET_IID(nsIDOMFocusListener));
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to unregister listener");
rv = eventReceiver->RemoveEventListenerByIID(NS_STATIC_CAST(nsIDOMFormListener *, this), NS_GET_IID(nsIDOMFormListener));
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to unregister listener");
}
}
}
mListener = nsnull;
@ -278,13 +334,13 @@ NS_IMETHODIMP nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
nsCOMPtr<nsIAccessible> accessible;
if (NS_SUCCEEDED(mAccService->GetAccessibleFor(targetNode, getter_AddRefs(accessible)))) {
if ( eventType.EqualsIgnoreCase("focus") ) {
if (eventType.EqualsIgnoreCase("focus")) {
if (mCurrentFocus != targetNode) {
mListener->HandleEvent(nsIAccessibleEventListener::EVENT_FOCUS, accessible);
mCurrentFocus = targetNode;
}
}
else if ( eventType.EqualsIgnoreCase("change") ) {
else if (eventType.EqualsIgnoreCase("change")) {
if (optionTargetNode) { // Set to current option only for HTML selects
mListener->HandleEvent(nsIAccessibleEventListener::EVENT_SELECTION, accessible);
if (mCurrentFocus != optionTargetNode &&
@ -296,12 +352,10 @@ NS_IMETHODIMP nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
else
mListener->HandleEvent(nsIAccessibleEventListener::EVENT_STATE_CHANGE, accessible);
}
else if ( eventType.EqualsIgnoreCase("CheckboxStateChange") ) {
else if (eventType.EqualsIgnoreCase("CheckboxStateChange"))
mListener->HandleEvent(nsIAccessibleEventListener::EVENT_STATE_CHANGE, accessible);
}
else if ( eventType.EqualsIgnoreCase("RadiobuttonStateChange") ) {
else if (eventType.EqualsIgnoreCase("RadiobuttonStateChange"))
mListener->HandleEvent(nsIAccessibleEventListener::EVENT_STATE_CHANGE, accessible);
}
}
}
return NS_OK;
@ -381,6 +435,49 @@ NS_IMETHODIMP nsRootAccessible::GetDocument(nsIDocument **doc)
return nsDocAccessibleMixin::GetDocument(doc);
}
NS_IMETHODIMP nsRootAccessible::OnStateChange(nsIWebProgress *aWebProgress,
nsIRequest *aRequest, PRInt32 aStateFlags, PRUint32 aStatus)
{
if (!mListener)
return NS_ERROR_FAILURE;
if (mBusy != eBusyStateLoading && (aStateFlags & STATE_START)) {
mBusy = eBusyStateLoading;
mListener->HandleEvent(nsIAccessibleEventListener::EVENT_STATE_CHANGE, this);
}
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 nsRootAccessible::OnProgressChange(nsIWebProgress *aWebProgress,
nsIRequest *aRequest, PRInt32 aCurSelfProgress, PRInt32 aMaxSelfProgress,
PRInt32 aCurTotalProgress, PRInt32 aMaxTotalProgress)
{
return NS_OK;
}
/* void onLocationChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsIURI location); */
NS_IMETHODIMP nsRootAccessible::OnLocationChange(nsIWebProgress *aWebProgress,
nsIRequest *aRequest, nsIURI *location)
{
// Load has been verified, it will occur, about to commence
return NS_OK;
}
/* void onStatusChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsresult aStatus, in wstring aMessage); */
NS_IMETHODIMP nsRootAccessible::OnStatusChange(nsIWebProgress *aWebProgress,
nsIRequest *aRequest, nsresult aStatus, const PRUnichar *aMessage)
{
return NS_OK;
}
/* void onSecurityChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long state); */
NS_IMETHODIMP nsRootAccessible::OnSecurityChange(nsIWebProgress *aWebProgress,
nsIRequest *aRequest, PRInt32 state)
{
return NS_OK;
}
nsDocAccessibleMixin::nsDocAccessibleMixin(nsIDocument *aDoc):mDocument(aDoc)
{
@ -452,7 +549,6 @@ NS_IMETHODIMP nsDocAccessibleMixin::GetNameSpaceURIForID(PRInt16 aNameSpaceID, n
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP nsDocAccessibleMixin::GetDocument(nsIDocument **doc)
{
*doc = mDocument;
@ -463,35 +559,4 @@ NS_IMETHODIMP nsDocAccessibleMixin::GetDocument(nsIDocument **doc)
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP nsDocAccessibleMixin::GetAccState(PRUint32 *aAccState)
{
// Screen readers need to know when the document is finished loading (STATE_BUSY flag)
// We do it this way, rather than via nsIWebProgressListener, because
// if accessibility was turned on after a document already finished loading,
// we would get no state changes from nsIWebProgressListener.
// The GetBusyFlags method, however, always has the current busy state information for us.
*aAccState = 0;
if (mDocument) {
nsCOMPtr<nsIPresShell> presShell;
mDocument->GetShellAt(0, getter_AddRefs(presShell));
if (presShell) {
nsCOMPtr<nsIPresContext> context;
presShell->GetPresContext(getter_AddRefs(context));
if (context) {
nsCOMPtr<nsISupports> container;
context->GetContainer(getter_AddRefs(container));
if (container) {
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
if (docShell) {
PRUint32 busyFlags;
docShell->GetBusyFlags(&busyFlags);
if (busyFlags != nsIDocShell::BUSY_FLAGS_NONE)
*aAccState = nsIAccessible::STATE_BUSY;
}
}
}
}
}
return NS_OK;
}

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

@ -47,6 +47,11 @@
#include "nsIDOMFocusListener.h"
#include "nsIDocument.h"
#include "nsIAccessibilityService.h"
#include "nsIWebProgressListener.h"
#include "nsIWeakReference.h"
#include "nsITimer.h"
#include "nsITimerCallback.h"
class nsDocAccessibleMixin
{
@ -57,9 +62,8 @@ class nsDocAccessibleMixin
NS_DECL_NSIACCESSIBLEDOCUMENT
NS_IMETHOD GetAccState(PRUint32 *aAccState);
protected:
PRBool mTopLevelDocument;
nsCOMPtr<nsIDocument> mDocument;
};
@ -68,12 +72,18 @@ class nsRootAccessible : public nsAccessible,
public nsIAccessibleDocument,
public nsIAccessibleEventReceiver,
public nsIDOMFocusListener,
public nsIDOMFormListener
public nsIDOMFormListener,
public nsIWebProgressListener,
public nsITimerCallback,
public nsSupportsWeakReference
{
NS_DECL_ISUPPORTS_INHERITED
public:
enum EBusyState {eBusyStateUninitialized, eBusyStateLoading, eBusyStateDone};
nsRootAccessible(nsIWeakReference* aShell);
virtual ~nsRootAccessible();
@ -87,10 +97,10 @@ class nsRootAccessible : public nsAccessible,
// ----- nsIAccessibleEventReceiver -------------------
NS_IMETHOD AddAccessibleEventListener(nsIAccessibleEventListener *aListener);
NS_IMETHOD RemoveAccessibleEventListener(nsIAccessibleEventListener *aListener);
NS_IMETHOD RemoveAccessibleEventListener();
// ----- nsIDOMEventListener --------------------------
NS_IMETHOD HandleEvent(nsIDOMEvent* anEvent);
NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
// ----- nsIDOMFocusListener --------------------------
NS_IMETHOD Focus(nsIDOMEvent* aEvent);
@ -103,19 +113,27 @@ class nsRootAccessible : public nsAccessible,
NS_IMETHOD Select(nsIDOMEvent* aEvent);
NS_IMETHOD Input(nsIDOMEvent* aEvent);
NS_IMETHOD_(void) Notify(nsITimer *timer);
NS_IMETHOD StartDocReadyTimer();
NS_DECL_NSIACCESSIBLEDOCUMENT
NS_DECL_NSIWEBPROGRESSLISTENER
protected:
NS_IMETHOD GetTargetNode(nsIDOMEvent *aEvent, nsCOMPtr<nsIDOMNode>& aTargetNode);
virtual void GetBounds(nsRect& aRect, nsIFrame** aRelativeFrame);
virtual nsIFrame* GetFrame();
// not a com pointer. We don't own the listener
// mListener is not a com pointer. We don't own the listener
// it is the callers responsibility to remove the listener
// otherwise we will get into circular referencing problems
nsIAccessibleEventListener* mListener;
// We don't need a weak reference, because we're owned by this listener
nsIAccessibleEventListener *mListener;
nsCOMPtr<nsITimer> mTimer;
nsCOMPtr<nsIDOMNode> mCurrentFocus;
nsCOMPtr<nsIAccessibilityService> mAccService;
EBusyState mBusy;
};

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

@ -53,10 +53,6 @@
#include "nsReadableUtils.h"
NS_INTERFACE_MAP_BEGIN(nsHTMLIFrameRootAccessible)
NS_INTERFACE_MAP_ENTRY(nsIDOMFocusListener)
NS_INTERFACE_MAP_ENTRY(nsIDOMFormListener)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFormListener)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMFormListener)
NS_INTERFACE_MAP_END_INHERITING(nsRootAccessible)
NS_IMPL_ADDREF_INHERITED(nsHTMLIFrameRootAccessible, nsRootAccessible);
@ -124,7 +120,7 @@ NS_IMETHODIMP nsHTMLIFrameAccessible::GetAccRole(PRUint32 *_retval)
NS_IMETHODIMP nsHTMLIFrameAccessible::GetAccState(PRUint32 *aAccState)
{
return nsDocAccessibleMixin::GetAccState(aAccState);
return nsAccessible::GetAccState(aAccState);
}
NS_IMETHODIMP nsHTMLIFrameAccessible::GetURL(nsAWritableString& aURL)

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

@ -53,10 +53,6 @@
#include "nsReadableUtils.h"
NS_INTERFACE_MAP_BEGIN(nsHTMLIFrameRootAccessible)
NS_INTERFACE_MAP_ENTRY(nsIDOMFocusListener)
NS_INTERFACE_MAP_ENTRY(nsIDOMFormListener)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFormListener)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMFormListener)
NS_INTERFACE_MAP_END_INHERITING(nsRootAccessible)
NS_IMPL_ADDREF_INHERITED(nsHTMLIFrameRootAccessible, nsRootAccessible);
@ -124,7 +120,7 @@ NS_IMETHODIMP nsHTMLIFrameAccessible::GetAccRole(PRUint32 *_retval)
NS_IMETHODIMP nsHTMLIFrameAccessible::GetAccState(PRUint32 *aAccState)
{
return nsDocAccessibleMixin::GetAccState(aAccState);
return nsAccessible::GetAccState(aAccState);
}
NS_IMETHODIMP nsHTMLIFrameAccessible::GetURL(nsAWritableString& aURL)

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

@ -48,6 +48,7 @@
#include "nsIDOMEventTarget.h"
#include "nsIDOMElement.h"
#include "nsIDOMEventReceiver.h"
#include "nsIDOMEventListener.h"
#include "nsReadableUtils.h"
#include "nsILink.h"
#include "nsHTMLFormControlAccessible.h"
@ -68,12 +69,19 @@
#include "nsIServiceManager.h"
#include "nsHTMLSelectListAccessible.h"
#include "nsIDOMHTMLSelectElement.h"
#include "nsIWebProgress.h"
#include "nsCURILoader.h"
#include "nsIInterfaceRequestorUtils.h"
NS_INTERFACE_MAP_BEGIN(nsRootAccessible)
NS_INTERFACE_MAP_ENTRY(nsIAccessibleDocument)
NS_INTERFACE_MAP_ENTRY(nsIAccessibleEventReceiver)
NS_INTERFACE_MAP_ENTRY(nsIDOMFocusListener)
NS_INTERFACE_MAP_ENTRY(nsIDOMFormListener)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFormListener)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMFormListener)
NS_INTERFACE_MAP_END_INHERITING(nsAccessible)
@ -86,7 +94,8 @@ NS_IMPL_RELEASE_INHERITED(nsRootAccessible, nsAccessible);
// construction
//-----------------------------------------------------
nsRootAccessible::nsRootAccessible(nsIWeakReference* aShell):nsAccessible(nsnull,aShell),
nsDocAccessibleMixin(aShell), mAccService(do_GetService("@mozilla.org/accessibilityService;1"))
nsDocAccessibleMixin(aShell), mAccService(do_GetService("@mozilla.org/accessibilityService;1")),
mBusy(eBusyStateUninitialized)
{
mListener = nsnull;
nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mPresShell));
@ -104,7 +113,7 @@ nsRootAccessible::nsRootAccessible(nsIWeakReference* aShell):nsAccessible(nsnull
nsRootAccessible::~nsRootAccessible()
{
nsLayoutAtoms::ReleaseAtoms();
RemoveAccessibleEventListener(mListener);
RemoveAccessibleEventListener();
}
/* attribute wstring accName; */
@ -174,81 +183,128 @@ NS_IMETHODIMP nsRootAccessible::GetAccRole(PRUint32 *aAccRole)
*/
*aAccRole = ROLE_PANE;
return NS_OK;
return NS_OK;
}
NS_IMETHODIMP nsRootAccessible::GetAccState(PRUint32 *aAccState)
{
return nsDocAccessibleMixin::GetAccState(aAccState);
*aAccState = STATE_FOCUSABLE;
if (mBusy == eBusyStateLoading)
*aAccState |= STATE_BUSY;
return NS_OK;
}
NS_IMETHODIMP nsRootAccessible::GetAccValue(nsAWritableString& aAccValue)
{
return GetURL(aAccValue);
}
void nsRootAccessible::Notify(nsITimer *timer)
{
// Short timer is finished
if (mBusy != eBusyStateDone) {
mBusy = eBusyStateDone;
if (mListener)
mListener->HandleEvent(nsIAccessibleEventListener::EVENT_STATE_CHANGE, this);
}
}
NS_IMETHODIMP nsRootAccessible::StartDocReadyTimer()
{
nsresult rv;
if (!mTimer)
mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
if (NS_SUCCEEDED(rv)) {
const PRUint32 kUpdateTimerDelay = 1;
rv = mTimer->Init(NS_STATIC_CAST(nsITimerCallback*, this), kUpdateTimerDelay);
}
return rv;
}
/* void addAccessibleEventListener (in nsIAccessibleEventListener aListener); */
NS_IMETHODIMP nsRootAccessible::AddAccessibleEventListener(nsIAccessibleEventListener *aListener)
{
if (!mListener)
{
// add an event listener to the document
nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mPresShell));
if (!shell)
return NS_ERROR_FAILURE;
if (mListener)
return NS_OK;
nsCOMPtr<nsIDocument> document;
shell->GetDocument(getter_AddRefs(document));
mListener = aListener;
// use AddEventListener from the nsIDOMEventTarget interface
nsCOMPtr<nsIDOMEventTarget> target;
if (NS_SUCCEEDED(document->QueryInterface(NS_GET_IID(nsIDOMEventTarget), getter_AddRefs(target))) && target)
{
nsresult rv = NS_OK;
// we're a DOMEventListener now!!
nsCOMPtr<nsIDOMEventListener> listener;
rv = this->QueryInterface( NS_GET_IID(nsIDOMEventListener), getter_AddRefs(listener) );
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to QI");
// capture DOM focus events
rv = target->AddEventListener( NS_LITERAL_STRING("focus") , listener, PR_TRUE );
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
// capture Form change events
rv = target->AddEventListener( NS_LITERAL_STRING("change") , listener, PR_TRUE );
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
// add ourself as a CheckboxStateChange listener ( custom event fired in nsHTMLInputElement.cpp )
rv = target->AddEventListener( NS_LITERAL_STRING("CheckboxStateChange") , listener, PR_TRUE );
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
// add ourself as a RadiobuttonStateChange listener ( custom event fired in nsHTMLInputElement.cpp )
rv = target->AddEventListener( NS_LITERAL_STRING("RadiobuttonStateChange") , listener, PR_TRUE );
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
}
// add an event listener to the document
nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mPresShell));
if (!shell)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDocument> document;
shell->GetDocument(getter_AddRefs(document));
if (!document)
return NS_ERROR_FAILURE;
// use AddEventListener from the nsIDOMEventTarget interface
nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(document));
if (target) {
// capture DOM focus events
nsresult rv = target->AddEventListener(NS_LITERAL_STRING("focus"), NS_STATIC_CAST(nsIDOMFocusListener*, this), PR_TRUE);
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
rv = target->AddEventListener(NS_LITERAL_STRING("blur"), NS_STATIC_CAST(nsIDOMFocusListener*, this), PR_TRUE);
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
// capture Form change events
rv = target->AddEventListener(NS_LITERAL_STRING("change"), NS_STATIC_CAST(nsIDOMFormListener*, this), PR_TRUE);
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
// add ourself as a CheckboxStateChange listener (custom event fired in nsHTMLInputElement.cpp)
rv = target->AddEventListener(NS_LITERAL_STRING("CheckboxStateChange"), NS_STATIC_CAST(nsIDOMFormListener*, this), PR_TRUE);
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
// add ourself as a RadiobuttonStateChange listener (custom event fired in nsHTMLInputElement.cpp)
rv = target->AddEventListener(NS_LITERAL_STRING("RadiobuttonStateChange"), NS_STATIC_CAST(nsIDOMFormListener*, this), PR_TRUE);
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
// Extremely short timer, after which we announce that pane is finished loading
// By waiting until after this short time, we know that the 3rd party accessibility software
// has received it's accessible, and can handle events on it.
StartDocReadyTimer();
}
// create a weak reference to the listener
mListener = aListener;
nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mPresShell));
NS_ASSERTION(shell,"Shell is gone!!! What are we doing here?");
if (presShell) {
nsCOMPtr<nsIPresContext> context;
presShell->GetPresContext(getter_AddRefs(context));
if (context) {
nsCOMPtr<nsISupports> container;
context->GetContainer(getter_AddRefs(container));
if (container) {
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
if (docShell) {
nsCOMPtr<nsIWebProgress> webProgress(do_GetInterface(docShell));
if (webProgress) {
webProgress->AddProgressListener(this);
}
}
}
}
}
return NS_OK;
}
/* void removeAccessibleEventListener (in nsIAccessibleEventListener aListener); */
NS_IMETHODIMP nsRootAccessible::RemoveAccessibleEventListener(nsIAccessibleEventListener *aListener)
/* void removeAccessibleEventListener (); */
NS_IMETHODIMP nsRootAccessible::RemoveAccessibleEventListener()
{
if (mListener)
{
nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mPresShell));
nsCOMPtr<nsIDocument> document;
if (!shell)
return NS_OK;
shell->GetDocument(getter_AddRefs(document));
nsCOMPtr<nsIDOMEventReceiver> erP;
if (NS_SUCCEEDED(document->QueryInterface(NS_GET_IID(nsIDOMEventReceiver), getter_AddRefs(erP))) && erP)
{
nsresult rv = erP->RemoveEventListenerByIID(NS_STATIC_CAST(nsIDOMFocusListener *, this), NS_GET_IID(nsIDOMFocusListener));
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register listener");
}
if (mListener) {
nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mPresShell));
nsCOMPtr<nsIDocument> document;
if (shell) {
shell->GetDocument(getter_AddRefs(document));
nsCOMPtr<nsIDOMEventReceiver> eventReceiver(do_QueryInterface(document));
if (eventReceiver) {
nsresult rv = eventReceiver->RemoveEventListenerByIID(NS_STATIC_CAST(nsIDOMFocusListener *, this), NS_GET_IID(nsIDOMFocusListener));
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to unregister listener");
rv = eventReceiver->RemoveEventListenerByIID(NS_STATIC_CAST(nsIDOMFormListener *, this), NS_GET_IID(nsIDOMFormListener));
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to unregister listener");
}
}
}
mListener = nsnull;
@ -278,13 +334,13 @@ NS_IMETHODIMP nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
nsCOMPtr<nsIAccessible> accessible;
if (NS_SUCCEEDED(mAccService->GetAccessibleFor(targetNode, getter_AddRefs(accessible)))) {
if ( eventType.EqualsIgnoreCase("focus") ) {
if (eventType.EqualsIgnoreCase("focus")) {
if (mCurrentFocus != targetNode) {
mListener->HandleEvent(nsIAccessibleEventListener::EVENT_FOCUS, accessible);
mCurrentFocus = targetNode;
}
}
else if ( eventType.EqualsIgnoreCase("change") ) {
else if (eventType.EqualsIgnoreCase("change")) {
if (optionTargetNode) { // Set to current option only for HTML selects
mListener->HandleEvent(nsIAccessibleEventListener::EVENT_SELECTION, accessible);
if (mCurrentFocus != optionTargetNode &&
@ -296,12 +352,10 @@ NS_IMETHODIMP nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent)
else
mListener->HandleEvent(nsIAccessibleEventListener::EVENT_STATE_CHANGE, accessible);
}
else if ( eventType.EqualsIgnoreCase("CheckboxStateChange") ) {
else if (eventType.EqualsIgnoreCase("CheckboxStateChange"))
mListener->HandleEvent(nsIAccessibleEventListener::EVENT_STATE_CHANGE, accessible);
}
else if ( eventType.EqualsIgnoreCase("RadiobuttonStateChange") ) {
else if (eventType.EqualsIgnoreCase("RadiobuttonStateChange"))
mListener->HandleEvent(nsIAccessibleEventListener::EVENT_STATE_CHANGE, accessible);
}
}
}
return NS_OK;
@ -381,6 +435,49 @@ NS_IMETHODIMP nsRootAccessible::GetDocument(nsIDocument **doc)
return nsDocAccessibleMixin::GetDocument(doc);
}
NS_IMETHODIMP nsRootAccessible::OnStateChange(nsIWebProgress *aWebProgress,
nsIRequest *aRequest, PRInt32 aStateFlags, PRUint32 aStatus)
{
if (!mListener)
return NS_ERROR_FAILURE;
if (mBusy != eBusyStateLoading && (aStateFlags & STATE_START)) {
mBusy = eBusyStateLoading;
mListener->HandleEvent(nsIAccessibleEventListener::EVENT_STATE_CHANGE, this);
}
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 nsRootAccessible::OnProgressChange(nsIWebProgress *aWebProgress,
nsIRequest *aRequest, PRInt32 aCurSelfProgress, PRInt32 aMaxSelfProgress,
PRInt32 aCurTotalProgress, PRInt32 aMaxTotalProgress)
{
return NS_OK;
}
/* void onLocationChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsIURI location); */
NS_IMETHODIMP nsRootAccessible::OnLocationChange(nsIWebProgress *aWebProgress,
nsIRequest *aRequest, nsIURI *location)
{
// Load has been verified, it will occur, about to commence
return NS_OK;
}
/* void onStatusChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsresult aStatus, in wstring aMessage); */
NS_IMETHODIMP nsRootAccessible::OnStatusChange(nsIWebProgress *aWebProgress,
nsIRequest *aRequest, nsresult aStatus, const PRUnichar *aMessage)
{
return NS_OK;
}
/* void onSecurityChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long state); */
NS_IMETHODIMP nsRootAccessible::OnSecurityChange(nsIWebProgress *aWebProgress,
nsIRequest *aRequest, PRInt32 state)
{
return NS_OK;
}
nsDocAccessibleMixin::nsDocAccessibleMixin(nsIDocument *aDoc):mDocument(aDoc)
{
@ -452,7 +549,6 @@ NS_IMETHODIMP nsDocAccessibleMixin::GetNameSpaceURIForID(PRInt16 aNameSpaceID, n
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP nsDocAccessibleMixin::GetDocument(nsIDocument **doc)
{
*doc = mDocument;
@ -463,35 +559,4 @@ NS_IMETHODIMP nsDocAccessibleMixin::GetDocument(nsIDocument **doc)
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP nsDocAccessibleMixin::GetAccState(PRUint32 *aAccState)
{
// Screen readers need to know when the document is finished loading (STATE_BUSY flag)
// We do it this way, rather than via nsIWebProgressListener, because
// if accessibility was turned on after a document already finished loading,
// we would get no state changes from nsIWebProgressListener.
// The GetBusyFlags method, however, always has the current busy state information for us.
*aAccState = 0;
if (mDocument) {
nsCOMPtr<nsIPresShell> presShell;
mDocument->GetShellAt(0, getter_AddRefs(presShell));
if (presShell) {
nsCOMPtr<nsIPresContext> context;
presShell->GetPresContext(getter_AddRefs(context));
if (context) {
nsCOMPtr<nsISupports> container;
context->GetContainer(getter_AddRefs(container));
if (container) {
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
if (docShell) {
PRUint32 busyFlags;
docShell->GetBusyFlags(&busyFlags);
if (busyFlags != nsIDocShell::BUSY_FLAGS_NONE)
*aAccState = nsIAccessible::STATE_BUSY;
}
}
}
}
}
return NS_OK;
}

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

@ -47,6 +47,11 @@
#include "nsIDOMFocusListener.h"
#include "nsIDocument.h"
#include "nsIAccessibilityService.h"
#include "nsIWebProgressListener.h"
#include "nsIWeakReference.h"
#include "nsITimer.h"
#include "nsITimerCallback.h"
class nsDocAccessibleMixin
{
@ -57,9 +62,8 @@ class nsDocAccessibleMixin
NS_DECL_NSIACCESSIBLEDOCUMENT
NS_IMETHOD GetAccState(PRUint32 *aAccState);
protected:
PRBool mTopLevelDocument;
nsCOMPtr<nsIDocument> mDocument;
};
@ -68,12 +72,18 @@ class nsRootAccessible : public nsAccessible,
public nsIAccessibleDocument,
public nsIAccessibleEventReceiver,
public nsIDOMFocusListener,
public nsIDOMFormListener
public nsIDOMFormListener,
public nsIWebProgressListener,
public nsITimerCallback,
public nsSupportsWeakReference
{
NS_DECL_ISUPPORTS_INHERITED
public:
enum EBusyState {eBusyStateUninitialized, eBusyStateLoading, eBusyStateDone};
nsRootAccessible(nsIWeakReference* aShell);
virtual ~nsRootAccessible();
@ -87,10 +97,10 @@ class nsRootAccessible : public nsAccessible,
// ----- nsIAccessibleEventReceiver -------------------
NS_IMETHOD AddAccessibleEventListener(nsIAccessibleEventListener *aListener);
NS_IMETHOD RemoveAccessibleEventListener(nsIAccessibleEventListener *aListener);
NS_IMETHOD RemoveAccessibleEventListener();
// ----- nsIDOMEventListener --------------------------
NS_IMETHOD HandleEvent(nsIDOMEvent* anEvent);
NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
// ----- nsIDOMFocusListener --------------------------
NS_IMETHOD Focus(nsIDOMEvent* aEvent);
@ -103,19 +113,27 @@ class nsRootAccessible : public nsAccessible,
NS_IMETHOD Select(nsIDOMEvent* aEvent);
NS_IMETHOD Input(nsIDOMEvent* aEvent);
NS_IMETHOD_(void) Notify(nsITimer *timer);
NS_IMETHOD StartDocReadyTimer();
NS_DECL_NSIACCESSIBLEDOCUMENT
NS_DECL_NSIWEBPROGRESSLISTENER
protected:
NS_IMETHOD GetTargetNode(nsIDOMEvent *aEvent, nsCOMPtr<nsIDOMNode>& aTargetNode);
virtual void GetBounds(nsRect& aRect, nsIFrame** aRelativeFrame);
virtual nsIFrame* GetFrame();
// not a com pointer. We don't own the listener
// mListener is not a com pointer. We don't own the listener
// it is the callers responsibility to remove the listener
// otherwise we will get into circular referencing problems
nsIAccessibleEventListener* mListener;
// We don't need a weak reference, because we're owned by this listener
nsIAccessibleEventListener *mListener;
nsCOMPtr<nsITimer> mTimer;
nsCOMPtr<nsIDOMNode> mCurrentFocus;
nsCOMPtr<nsIAccessibilityService> mAccService;
EBusyState mBusy;
};

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

@ -938,7 +938,7 @@ RootAccessible::~RootAccessible()
{
nsCOMPtr<nsIAccessibleEventReceiver> r(do_QueryInterface(mAccessible));
if (r)
r->RemoveAccessibleEventListener(this);
r->RemoveAccessibleEventListener();
// free up accessibles
for (int i=0; i < mListCount; i++)

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

@ -142,6 +142,9 @@ UINT nsWindow::uMSH_MOUSEWHEEL = 0;
UINT nsWindow::uWM_MSIME_RECONVERT = 0; // reconvert messge for MSIME
UINT nsWindow::uWM_MSIME_MOUSE = 0; // mouse messge for MSIME
UINT nsWindow::uWM_ATOK_RECONVERT = 0; // reconvert messge for ATOK
#ifdef ACCESSIBILITY
BOOL nsWindow::gIsAccessibilityOn = FALSE;
#endif
nsWindow* nsWindow::gCurrentWindow = nsnull;
////////////////////////////////////////////////////
@ -530,8 +533,10 @@ UINT nsWindow::gCurrentKeyboardCP = 0;
nsWindow::~nsWindow()
{
#ifdef ACCESSIBILITY
if (mRootAccessible)
if (mRootAccessible) {
mRootAccessible->Release();
mRootAccessible = nsnull;
}
#endif
mIsDestroying = PR_TRUE;
@ -3360,6 +3365,11 @@ PRBool nsWindow::ProcessMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT
gJustGotActivate = PR_FALSE;
result = DispatchFocus(NS_ACTIVATE, isMozWindowTakingFocus);
}
#ifdef ACCESSIBILITY
if (nsWindow::gIsAccessibilityOn && !mRootAccessible && mIsTopWidgetWindow)
CreateRootAccessible();
#endif
break;
case WM_KILLFOCUS:
@ -3586,32 +3596,16 @@ PRBool nsWindow::ProcessMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT
#ifdef ACCESSIBILITY
case WM_GETOBJECT:
{
if (lParam == OBJID_CLIENT) {
if (!mRootAccessible) {
nsCOMPtr<nsIAccessible> acc;
DispatchAccessibleEvent(NS_GETACCESSIBLE, getter_AddRefs(acc));
// create the COM accessible object
if (acc)
{
HWND wnd = GetWindowHandle();
mRootAccessible = new RootAccessible(acc, wnd); // ref is 0
mRootAccessible->AddRef();
}
}
if (mRootAccessible) {
// ask accessible to do this do it loads the library dynamically
LRESULT lAcc = Accessible::LresultFromObject(IID_IAccessible, wParam, mRootAccessible); // ref 1
if (lAcc == 0) {
*aRetValue = NULL;
return PR_FALSE;
}
*aRetValue = lAcc;
return PR_TRUE; // yes we handled it.
}
}
*aRetValue = NULL;
return PR_FALSE;
} break;
nsWindow::gIsAccessibilityOn = TRUE;
LRESULT lAcc = 0;
if (mIsTopWidgetWindow && !mRootAccessible)
CreateRootAccessible();
if (lParam == OBJID_CLIENT && mRootAccessible) // oleacc.dll will be loaded dynamically
lAcc = Accessible::LresultFromObject(IID_IAccessible, wParam, mRootAccessible); // ref 1
return (*aRetValue = lAcc) != 0;
}
#endif
default: {
// Handle both flavors of mouse wheel events.
@ -5828,3 +5822,20 @@ VOID CALLBACK nsWindow::HookTimerForPopups( HWND hwnd, UINT uMsg, UINT idEvent,
}
}
#ifdef ACCESSIBILITY
void nsWindow::CreateRootAccessible()
{
// Create this as early as possible in new window, if accessibility is turned on
// We need it to be created early so it can generate accessibility events right away
nsCOMPtr<nsIAccessible> acc;
DispatchAccessibleEvent(NS_GETACCESSIBLE, getter_AddRefs(acc));
// create the COM accessible object
if (acc) {
HWND wnd = GetWindowHandle();
mRootAccessible = new RootAccessible(acc, wnd); // ref is 0
mRootAccessible->AddRef();
}
}
#endif

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

@ -298,6 +298,7 @@ public:
virtual PRBool DispatchMouseEvent(PRUint32 aEventType, nsPoint* aPoint = nsnull);
#ifdef ACCESSIBILITY
virtual PRBool DispatchAccessibleEvent(PRUint32 aEventType, nsIAccessible** aAccessible, nsPoint* aPoint = nsnull);
void CreateRootAccessible();
#endif
virtual PRBool AutoErase();
nsPoint* GetLastPoint() { return &mLastPoint; }
@ -473,6 +474,7 @@ protected:
#ifdef ACCESSIBILITY
IAccessible* mRootAccessible;
static BOOL gIsAccessibilityOn;
#endif
};