Bug 343136 Support ATK document events

patch by Nian Liu at sun.com r=aaronlev, ginn.chen
This commit is contained in:
ginn.chen%sun.com 2006-09-29 05:45:07 +00:00
Родитель cd5da0b273
Коммит d85c8bd9c4
11 изменённых файлов: 211 добавлений и 159 удалений

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

@ -59,7 +59,7 @@ interface nsIDOMNode;
*
* @status UNDER_REVIEW
*/
[scriptable, uuid(87F29033-C4A6-40a3-AC7A-3BA391F9992D)]
[scriptable, uuid(b298afc0-4158-11db-b0de-0800200c9a66)]
interface nsIAccessibleEvent : nsISupports
{
/**
@ -127,6 +127,11 @@ interface nsIAccessibleEvent : nsISupports
const unsigned long EVENT_SCROLLINGEND = 0x0013;
const unsigned long EVENT_MINIMIZESTART = 0x0016;
const unsigned long EVENT_MINIMIZEEND = 0x0017;
const unsigned long EVENT_DOCUMENT_LOAD_START = 0x0018;
const unsigned long EVENT_DOCUMENT_LOAD_COMPLETE = 0x0019;
const unsigned long EVENT_DOCUMENT_RELOAD = 0x0020;
const unsigned long EVENT_DOCUMENT_LOAD_STOPPED = 0x0021;
const unsigned long EVENT_DOCUMENT_ATTRIBUTES_CHANGED = 0x0022;
// the additional events for ATK
const unsigned long EVENT_ATK_PROPERTY_CHANGE = 0x0100;

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

@ -62,6 +62,6 @@ interface nsPIAccessibleDocument : nsISupports
void cacheAccessNode(in voidPtr aUniqueID, in nsIAccessNode aAccessNode);
void destroy();
void flushPendingEvents();
void fireDocLoadingEvent(in boolean isFinished);
void fireDocLoadEvents(in PRUint32 aEventType);
void fireAnchorJumpEvent();
};

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

@ -118,34 +118,34 @@ NS_IMETHODIMP nsDocAccessibleWrap::FireToolkitEvent(PRUint32 aEvent,
} break;
case nsIAccessibleEvent::EVENT_STATE_CHANGE:
AtkStateChange *pAtkStateChange;
StateChange *pStateChange;
AtkStateType atkState;
MAI_LOG_DEBUG(("\n\nReceived: EVENT_STATE_CHANGE\n"));
if (!aEventData)
break;
pAtkStateChange = NS_REINTERPRET_CAST(AtkStateChange *, aEventData);
pStateChange = NS_REINTERPRET_CAST(StateChange *, aEventData);
switch (pAtkStateChange->state) {
switch (pStateChange->state) {
case nsIAccessible::STATE_INVISIBLE:
atkState = ATK_STATE_VISIBLE;
pAtkStateChange->enable = !pAtkStateChange->enable;
pStateChange->enable = !pStateChange->enable;
break;
case nsIAccessible::STATE_UNAVAILABLE:
atkState = ATK_STATE_ENABLED;
pAtkStateChange->enable = !pAtkStateChange->enable;
pStateChange->enable = !pStateChange->enable;
break;
case nsIAccessible::STATE_READONLY:
atkState = ATK_STATE_EDITABLE;
pAtkStateChange->enable = !pAtkStateChange->enable;
pStateChange->enable = !pStateChange->enable;
break;
default:
atkState = TranslateAState(pAtkStateChange->state, pAtkStateChange->extState);
atkState = TranslateAState(pStateChange->state, pStateChange->extState);
}
atk_object_notify_state_change(accWrap->GetAtkObject(),
atkState, pAtkStateChange->enable);
atkState, pStateChange->enable);
rv = NS_OK;
break;
@ -457,6 +457,38 @@ NS_IMETHODIMP nsDocAccessibleWrap::FireToolkitEvent(PRUint32 aEvent,
rv = NS_OK;
} break;
case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE:
{
MAI_LOG_DEBUG(("\n\nReceived: EVENT_DOCUMENT_LOAD_COMPLETE\n"));
g_signal_emit_by_name (accWrap->GetAtkObject(),
"load_complete");
rv = NS_OK;
} break;
case nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD:
{
MAI_LOG_DEBUG(("\n\nReceived: EVENT_DOCUMENT_RELOAD\n"));
g_signal_emit_by_name (accWrap->GetAtkObject(),
"reload");
rv = NS_OK;
} break;
case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED:
{
MAI_LOG_DEBUG(("\n\nReceived: EVENT_DOCUMENT_LOAD_STOPPED\n"));
g_signal_emit_by_name (accWrap->GetAtkObject(),
"load_stopped");
rv = NS_OK;
} break;
case nsIAccessibleEvent::EVENT_DOCUMENT_ATTRIBUTES_CHANGED:
{
MAI_LOG_DEBUG(("\n\nReceived: EVENT_DOCUMENT_ATTRIBUTES_CHANGED\n"));
g_signal_emit_by_name (accWrap->GetAtkObject(),
"attriubtes_changed");
rv = NS_OK;
} break;
default:
// Don't transfer others
MAI_LOG_DEBUG(("\n\nReceived an unknown event=0x%u\n", aEvent));
@ -538,19 +570,3 @@ TranslateAState(PRUint32 aState, PRUint32 aExtState)
return ATK_STATE_INVALID;
}
NS_IMETHODIMP nsDocAccessibleWrap::FireDocLoadingEvent(PRBool aIsFinished)
{
if (!mDocument || !mWeakShell)
return NS_OK; // Document has been shut down
if (!aIsFinished) {
// Load has been verified, it will occur, about to commence
AtkChildrenChange childrenData;
childrenData.index = -1;
childrenData.child = 0;
childrenData.add = PR_FALSE;
FireToolkitEvent(nsIAccessibleEvent::EVENT_REORDER, this, &childrenData);
}
return nsDocAccessible::FireDocLoadingEvent(aIsFinished);
}

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

@ -57,7 +57,6 @@ public:
virtual ~nsDocAccessibleWrap();
NS_IMETHOD FireToolkitEvent(PRUint32 aEvent, nsIAccessible* aAccessible,
void* aData);
NS_IMETHOD FireDocLoadingEvent(PRBool isFinished);
protected:
PRBool mActivated;

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

@ -141,7 +141,7 @@ nsresult nsRootAccessibleWrap::HandleEventWithTarget(nsIDOMEvent *aEvent,
}
#endif
AtkStateChange stateData;
StateChange stateData;
if (eventType.LowerCaseEqualsLiteral("focus")) {
#ifdef MOZ_XUL
if (treeItemAccessible) { // use focused treeitem

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

@ -79,6 +79,8 @@
#include "nsServiceManagerUtils.h"
#include "nsUnicharUtils.h"
#include "nsIWebProgress.h"
#include "nsNetError.h"
#include "nsDocShellLoadTypes.h"
#ifdef MOZ_XUL
#include "nsXULAlertAccessible.h"
@ -163,7 +165,7 @@ NS_IMETHODIMP nsAccessibilityService::OnStateChange(nsIWebProgress *aWebProgress
{
NS_ASSERTION(aStateFlags & STATE_IS_DOCUMENT, "Other notifications excluded");
if (0 == (aStateFlags & (STATE_START | STATE_STOP)) || NS_FAILED(aStatus)) {
if (0 == (aStateFlags & (STATE_START | STATE_STOP))) {
return NS_OK;
}
@ -196,9 +198,31 @@ NS_IMETHODIMP nsAccessibilityService::OnStateChange(nsIWebProgress *aWebProgress
nsCOMPtr<nsPIAccessibleDocument> docAccessible =
do_QueryInterface(accessible);
NS_ENSURE_TRUE(docAccessible, NS_ERROR_FAILURE);
PRBool isFinished = !(aStateFlags & STATE_START);
docAccessible->FireDocLoadingEvent(isFinished);
PRUint32 eventType = 0;
if ((aStateFlags & STATE_STOP) && NS_SUCCEEDED(aStatus)) {
eventType = nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE;
} else if ((aStateFlags & STATE_STOP) && (aStatus & NS_BINDING_ABORTED)) {
eventType = nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED;
} else if (aStateFlags & STATE_START) {
eventType = nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_START;
nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(domWindow));
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(webNav));
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
PRUint32 loadType;
docShell->GetLoadType(&loadType);
if (loadType == LOAD_RELOAD_NORMAL ||
loadType == LOAD_RELOAD_BYPASS_CACHE ||
loadType == LOAD_RELOAD_BYPASS_PROXY ||
loadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE) {
eventType = nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD;
}
}
if (eventType == 0)
return NS_OK; //no actural event need to be fired
docAccessible->FireDocLoadEvents(eventType);
return NS_OK;
}

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

@ -80,11 +80,11 @@ class nsAccessibleEventData: public nsIAccessibleEvent
// e.g., nsAccessibleTextChangeEvent: public nsIAccessibleTextChangeEvent
//
struct AtkStateChange {
struct StateChange {
PRUint32 state;
PRUint32 extState;
PRBool enable;
AtkStateChange() {
StateChange() {
state = 0;
extState = 0;
enable = PR_FALSE;

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

@ -382,7 +382,7 @@ void nsDocAccessible::CheckForEditor()
if (editor) {
// State readonly is now clear
#ifdef MOZ_ACCESSIBILITY_ATK
AtkStateChange stateData;
StateChange stateData;
stateData.enable = PR_TRUE;
stateData.state = STATE_READONLY; // Will be translated to ATK_STATE_EDITABLE
FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, this, &stateData);
@ -622,21 +622,61 @@ nsresult nsDocAccessible::RemoveEventListeners()
NS_IMETHODIMP nsDocAccessible::FireAnchorJumpEvent()
{
if (!mIsContentLoaded || !mDocument) {
return NS_OK;
}
nsCOMPtr<nsISupports> container = mDocument->GetContainer();
nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(container));
nsCAutoString theURL;
if (webNav) {
nsCOMPtr<nsIURI> pURI;
webNav->GetCurrentURI(getter_AddRefs(pURI));
if (pURI) {
pURI->GetSpec(theURL);
}
}
static nsCAutoString lastAnchor;
const char kHash = '#';
nsCAutoString currentAnchor;
PRInt32 hasPosition = theURL.FindChar(kHash);
if (hasPosition > 0 && hasPosition < (PRInt32)theURL.Length() - 1) {
mIsAnchor = PR_TRUE;
currentAnchor.Assign(Substring(theURL,
hasPosition+1,
(PRInt32)theURL.Length()-hasPosition-1));
}
if (currentAnchor.Equals(lastAnchor)) {
mIsAnchorJumped = PR_FALSE;
} else {
mIsAnchorJumped = PR_TRUE;
lastAnchor.Assign(currentAnchor);
}
if (mIsAnchorJumped) {
FireToolkitEvent(nsIAccessibleEvent::EVENT_DOCUMENT_ATTRIBUTES_CHANGED,
this, nsnull);
}
return NS_OK;
}
NS_IMETHODIMP nsDocAccessible::FireDocLoadingEvent(PRBool aIsFinished)
NS_IMETHODIMP nsDocAccessible::FireDocLoadEvents(PRUint32 aEventType)
{
if (!mDocument || !mWeakShell) {
return NS_OK; // Document has been shut down
}
if (mIsContentLoaded == aIsFinished) {
PRBool isFinished =
(aEventType == nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE ||
aEventType == nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED);
if (mIsContentLoaded == isFinished) {
return NS_OK;
}
mIsContentLoaded = aIsFinished;
mIsContentLoaded = isFinished;
if (aIsFinished) {
if (isFinished) {
// Need to wait until scrollable view is available
AddScrollListener();
nsCOMPtr<nsIAccessible> parent;
@ -646,8 +686,36 @@ NS_IMETHODIMP nsDocAccessible::FireDocLoadingEvent(PRBool aIsFinished)
// Make the parent forget about the old document as a child
privateAccessible->InvalidateChildren();
}
// Use short timer before firing state change event for finished doc,
// because the window is made visible asynchronously
if (!mDocLoadTimer) {
mDocLoadTimer = do_CreateInstance("@mozilla.org/timer;1");
}
if (mDocLoadTimer) {
mDocLoadTimer->InitWithFuncCallback(DocLoadCallback, this, 0,
nsITimer::TYPE_ONE_SHOT);
}
//fire EVENT_STATE_CHANGE to clear STATE_BUSY
StateChange stateData;
stateData.state = STATE_BUSY;
stateData.enable = PR_FALSE;
FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, this, &stateData);
} else {
nsCOMPtr<nsIDocShellTreeItem> treeItem = GetDocShellTreeItemFor(mDOMNode);
if (!treeItem) {
return NS_OK;
}
nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
treeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
if (sameTypeRoot != treeItem) {
return NS_OK;
}
FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, this, nsnull);
}
FireToolkitEvent(aEventType, this, nsnull);
return NS_OK;
}
@ -992,7 +1060,7 @@ NS_IMETHODIMP nsDocAccessible::FlushPendingEvents()
do_QueryInterface(accessible);
NS_ASSERTION(docAccessible, "No doc accessible for doc load event");
if (docAccessible) {
docAccessible->FireDocLoadingEvent(PR_TRUE);
docAccessible->FireDocLoadEvents(nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE);
}
}
else {
@ -1267,3 +1335,52 @@ NS_IMETHODIMP nsDocAccessible::FireToolkitEvent(PRUint32 aEvent, nsIAccessible*
return obsService->NotifyObservers(accEvent, NS_ACCESSIBLE_EVENT_TOPIC, nsnull);
}
void nsDocAccessible::DocLoadCallback(nsITimer *aTimer, void *aClosure)
{
// Doc has finished loading, fire "load finished" event
// By using short timer we can wait make the window visible,
// which it does asynchronously. This avoids confusing the screen reader with a
// hidden window. Waiting also allows us to see of the document has focus,
// which is important because we only fire doc loaded events for focused documents.
nsDocAccessible *docAcc =
NS_REINTERPRET_CAST(nsDocAccessible*, aClosure);
if (!docAcc) {
return;
}
// Fire doc finished event
nsCOMPtr<nsIDOMNode> docDomNode;
docAcc->GetDOMNode(getter_AddRefs(docDomNode));
nsCOMPtr<nsIDocument> doc(do_QueryInterface(docDomNode));
if (doc) {
nsCOMPtr<nsISupports> container = doc->GetContainer();
nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = do_QueryInterface(container);
if (!docShellTreeItem) {
return;
}
nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
if (sameTypeRoot != docShellTreeItem) {
// A frame or iframe has finished loading new content
docAcc->InvalidateCacheSubtree(nsnull, nsIAccessibleEvent::EVENT_REORDER);
return;
}
// Fire STATE_CHANGE event for doc load finish if focus is in same doc tree
if (gLastFocusedNode) {
nsCOMPtr<nsIDocShellTreeItem> focusedTreeItem =
GetDocShellTreeItemFor(gLastFocusedNode);
if (focusedTreeItem) {
nsCOMPtr<nsIDocShellTreeItem> sameTypeRootOfFocus;
focusedTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRootOfFocus));
if (sameTypeRoot == sameTypeRootOfFocus) {
docAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE,
docAcc, nsnull);
docAcc->FireAnchorJumpEvent();
}
}
}
}
}

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

@ -122,6 +122,14 @@ class nsDocAccessible : public nsHyperTextAccessible,
PRPackedBool mIsContentLoaded;
nsCOMArray<nsIAccessibleEvent> mEventsToFire;
nsCOMPtr<nsIEditor> mEditor;
protected:
PRBool mIsAnchor;
PRBool mIsAnchorJumped;
private:
static void DocLoadCallback(nsITimer *aTimer, void *aClosure);
nsCOMPtr<nsITimer> mDocLoadTimer;
};
#endif

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

@ -336,36 +336,12 @@ NS_IMETHODIMP nsDocAccessibleWrap::FireAnchorJumpEvent()
// the can only relate events back to their internal model if it's a leaf.
// There is usually an accessible for the focus node, but if it's an empty text node
// we have to move forward in the document to get one
if (!mIsContentLoaded || !mDocument) {
nsDocAccessible::FireAnchorJumpEvent();
if (!mIsAnchorJumped)
return NS_OK;
}
nsCOMPtr<nsISupports> container = mDocument->GetContainer();
nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(container));
nsCAutoString theURL;
if (webNav) {
nsCOMPtr<nsIURI> pURI;
webNav->GetCurrentURI(getter_AddRefs(pURI));
if (pURI) {
pURI->GetSpec(theURL);
}
}
const char kHash = '#';
PRBool hasAnchor = PR_FALSE;
PRInt32 hasPosition = theURL.FindChar(kHash);
if (hasPosition > 0 && hasPosition < (PRInt32)theURL.Length() - 1) {
hasAnchor = PR_TRUE;
}
// mWasAnchor is set when the previous URL included a named anchor.
// This way we still know to fire the EVENT_SCROLLINGSTART event when we
// move from a named anchor back to the top.
if (!mWasAnchor && !hasAnchor) {
return NS_OK;
}
mWasAnchor = hasAnchor;
nsCOMPtr<nsIDOMNode> focusNode;
if (hasAnchor) {
if (mIsAnchor) {
nsCOMPtr<nsISelectionController> selCon(do_QueryReferent(mWeakShell));
if (!selCon) {
return NS_OK;
@ -390,95 +366,6 @@ NS_IMETHODIMP nsDocAccessibleWrap::FireAnchorJumpEvent()
return NS_OK;
}
void nsDocAccessibleWrap::DocLoadCallback(nsITimer *aTimer, void *aClosure)
{
// Doc has finished loading, fire "load finished" event
// By using short timer we can wait for MS Windows to make the window visible,
// which it does asynchronously. This avoids confusing the screen reader with a
// hidden window. Waiting also allows us to see of the document has focus,
// which is important because we only fire doc loaded events for focused documents.
nsDocAccessibleWrap *docAcc =
NS_REINTERPRET_CAST(nsDocAccessibleWrap*, aClosure);
if (!docAcc) {
return;
}
// Fire doc finished event
nsCOMPtr<nsIDOMNode> docDomNode;
docAcc->GetDOMNode(getter_AddRefs(docDomNode));
nsCOMPtr<nsIDocument> doc(do_QueryInterface(docDomNode));
if (doc) {
nsCOMPtr<nsISupports> container = doc->GetContainer();
nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = do_QueryInterface(container);
if (!docShellTreeItem) {
return;
}
nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
if (sameTypeRoot != docShellTreeItem) {
// A frame or iframe has finished loading new content
docAcc->InvalidateCacheSubtree(nsnull, nsIAccessibleEvent::EVENT_REORDER);
return;
}
// Fire STATE_CHANGE event for doc load finish if focus is in same doc tree
if (gLastFocusedNode) {
nsCOMPtr<nsIDocShellTreeItem> focusedTreeItem =
GetDocShellTreeItemFor(gLastFocusedNode);
if (focusedTreeItem) {
nsCOMPtr<nsIDocShellTreeItem> sameTypeRootOfFocus;
focusedTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRootOfFocus));
if (sameTypeRoot == sameTypeRootOfFocus) {
docAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE,
docAcc, nsnull);
docAcc->FireAnchorJumpEvent();
}
}
}
}
}
NS_IMETHODIMP nsDocAccessibleWrap::FireDocLoadingEvent(PRBool aIsFinished)
{
if (!mDocument || !mWeakShell)
return NS_OK; // Document has been shut down
if (mIsContentLoaded == aIsFinished) {
return NS_OK; // Already fired the event
}
nsDocAccessible::FireDocLoadingEvent(aIsFinished);
if (aIsFinished) {
// Use short timer before firing state change event for finished doc,
// because the window is made visible asynchronously by Microsoft Windows
if (!mDocLoadTimer) {
mDocLoadTimer = do_CreateInstance("@mozilla.org/timer;1");
}
if (mDocLoadTimer) {
mDocLoadTimer->InitWithFuncCallback(DocLoadCallback, this, 0,
nsITimer::TYPE_ONE_SHOT);
}
}
else {
nsCOMPtr<nsIDocShellTreeItem> treeItem = GetDocShellTreeItemFor(mDOMNode);
if (!treeItem) {
return NS_OK;
}
nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
treeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
if (sameTypeRoot != treeItem) {
return NS_OK; // We only fire MSAA doc loading events for root content frame
}
FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, this, nsnull);
}
return NS_OK;
}
STDMETHODIMP nsDocAccessibleWrap::get_URL(/* [out] */ BSTR __RPC_FAR *aURL)
{
*aURL = NULL;

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

@ -91,14 +91,10 @@ public:
NS_IMETHOD Shutdown();
NS_IMETHOD FireToolkitEvent(PRUint32 aEvent, nsIAccessible* aAccessible, void* aData);
NS_IMETHOD FireDocLoadingEvent(PRBool isFinished);
NS_IMETHOD FireAnchorJumpEvent();
private:
static void DocLoadCallback(nsITimer *aTimer, void *aClosure);
already_AddRefed<nsIAccessible> GetFirstLeafAccessible(nsIDOMNode *aStartNode);
nsCOMPtr<nsITimer> mDocLoadTimer;
PRPackedBool mWasAnchor;
};
#endif