diff --git a/accessible/src/base/nsAccessibilityService.cpp b/accessible/src/base/nsAccessibilityService.cpp index d3412bd505d3..559cce253255 100644 --- a/accessible/src/base/nsAccessibilityService.cpp +++ b/accessible/src/base/nsAccessibilityService.cpp @@ -160,7 +160,7 @@ NS_IMETHODIMP nsAccessibilityService::OnStateChange(nsIWebProgress *aWebProgress { NS_ASSERTION(aStateFlags & STATE_IS_DOCUMENT, "Other notifications excluded"); - if (0 == (aStateFlags & (STATE_START | STATE_STOP))) { + if (0 == (aStateFlags & (STATE_START | STATE_STOP)) || NS_FAILED(aStatus)) { return NS_OK; } @@ -197,8 +197,9 @@ NS_IMETHODIMP nsAccessibilityService::OnStateChange(nsIWebProgress *aWebProgress nsCOMPtr sameTypeRoot; docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot)); PRBool isFinished = !(aStateFlags & STATE_START); - if (sameTypeRoot != docShellTreeItem && !isFinished) { - return NS_OK; // A frame or iframe has begun to load new content + if (sameTypeRoot != docShellTreeItem) { + // Frame and iframe handling done via DOMContentLoaded in nsRootAccessible::HandleEvent() + return NS_OK; } docAccessible->FireDocLoadingEvent(isFinished); diff --git a/accessible/src/base/nsDocAccessible.cpp b/accessible/src/base/nsDocAccessible.cpp index 71ee2033745b..a1a6247bbf70 100644 --- a/accessible/src/base/nsDocAccessible.cpp +++ b/accessible/src/base/nsDocAccessible.cpp @@ -84,7 +84,7 @@ //----------------------------------------------------- nsDocAccessible::nsDocAccessible(nsIDOMNode *aDOMNode, nsIWeakReference* aShell): nsBlockAccessible(aDOMNode, aShell), mWnd(nsnull), - mEditor(nsnull), mScrollPositionChangedTicks(0) + mEditor(nsnull), mScrollPositionChangedTicks(0), mIsContentLoaded(PR_FALSE) { // 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 @@ -189,15 +189,8 @@ NS_IMETHODIMP nsDocAccessible::GetState(PRUint32 *aState) } nsAccessible::GetState(aState); *aState |= STATE_FOCUSABLE; - - nsCOMPtr docShellTreeItem = - GetDocShellTreeItemFor(mDOMNode); - nsCOMPtr docShell(do_QueryInterface(docShellTreeItem)); - NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); - - PRUint32 busyFlags; - docShell->GetBusyFlags(&busyFlags); - if (busyFlags != nsIDocShell::BUSY_FLAGS_NONE) { + + if (!mIsContentLoaded) { *aState |= STATE_BUSY; } @@ -538,12 +531,7 @@ void nsDocAccessible::GetBoundsRect(nsRect& aBounds, nsIFrame** aRelativeFrame) nsresult nsDocAccessible::AddEventListeners() { // 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 + // 2) Check for editor and listen for changes to editor nsCOMPtr presShell(GetPresShell()); NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); @@ -617,6 +605,11 @@ NS_IMETHODIMP nsDocAccessible::FireDocLoadingEvent(PRBool aIsFinished) return NS_OK; // Document has been shut down } + if (mIsContentLoaded == aIsFinished) { + return NS_OK; + } + mIsContentLoaded = aIsFinished; + if (aIsFinished) { // Need to wait until scrollable view is available AddScrollListener(); @@ -1060,14 +1053,7 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild, nsCOMPtr childNode = aChild ? do_QueryInterface(aChild) : mDOMNode; - // Don't fire any other events if doc is still loading - nsCOMPtr container = mDocument->GetContainer(); - nsCOMPtr docShell = do_QueryInterface(container); - NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); - - PRUint32 busyFlags; - docShell->GetBusyFlags(&busyFlags); - if (busyFlags && mAccessNodeCache.Count() <= 1) { + if (!mIsContentLoaded && mAccessNodeCache.Count() <= 1) { return NS_OK; // Still loading and nothing to invalidate yet } @@ -1101,7 +1087,10 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild, nsCOMPtr containerAccessible; if (childNode == mDOMNode) { // Don't get parent accessible if already at the root of a docshell chain like UI or content - nsCOMPtr docShellTreeItem(do_QueryInterface(docShell)); + // Don't fire any other events if doc is still loading + nsCOMPtr container = mDocument->GetContainer(); + nsCOMPtr docShellTreeItem(do_QueryInterface(container)); + NS_ENSURE_TRUE(docShellTreeItem, NS_ERROR_FAILURE); nsCOMPtr sameTypeRoot; docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot)); if (sameTypeRoot == docShellTreeItem) { @@ -1117,7 +1106,7 @@ NS_IMETHODIMP nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild, privateContainerAccessible->InvalidateChildren(); } - if (busyFlags) { + if (!mIsContentLoaded) { return NS_OK; } diff --git a/accessible/src/base/nsDocAccessible.h b/accessible/src/base/nsDocAccessible.h index 02bc3a6f4be5..eedcd9f6486e 100644 --- a/accessible/src/base/nsDocAccessible.h +++ b/accessible/src/base/nsDocAccessible.h @@ -116,6 +116,7 @@ class nsDocAccessible : public nsBlockAccessible, nsCOMPtr mFireEventTimer; nsCOMPtr mEditor; // Editor, if there is one PRUint16 mScrollPositionChangedTicks; // Used for tracking scroll events + PRPackedBool mIsContentLoaded; nsCOMArray mEventsToFire; }; diff --git a/accessible/src/base/nsRootAccessible.cpp b/accessible/src/base/nsRootAccessible.cpp index f2611a1d3a43..07e458f5b134 100644 --- a/accessible/src/base/nsRootAccessible.cpp +++ b/accessible/src/base/nsRootAccessible.cpp @@ -69,6 +69,7 @@ #include "nsRootAccessible.h" #include "nsIDocShell.h" #include "nsIDocShellTreeItem.h" +#include "nsIDocShellTreeNode.h" #include "nsIDocShellTreeOwner.h" #include "nsIBaseWindow.h" @@ -274,6 +275,9 @@ nsresult nsRootAccessible::AddEventListeners() rv = target->AddEventListener(NS_LITERAL_STRING("DOMMenuBarInactive"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE); NS_ENSURE_SUCCESS(rv, rv); + + rv = target->AddEventListener(NS_LITERAL_STRING("DOMContentLoaded"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE); + NS_ENSURE_SUCCESS(rv, rv); } GetChromeEventHandler(getter_AddRefs(target)); @@ -320,6 +324,7 @@ nsresult nsRootAccessible::RemoveEventListeners() target->RemoveEventListener(NS_LITERAL_STRING("DOMMenuItemActive"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE); target->RemoveEventListener(NS_LITERAL_STRING("DOMMenuBarActive"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE); target->RemoveEventListener(NS_LITERAL_STRING("DOMMenuBarInactive"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE); + target->RemoveEventListener(NS_LITERAL_STRING("DOMContentLoaded"), NS_STATIC_CAST(nsIDOMXULListener*, this), PR_TRUE); } GetChromeEventHandler(getter_AddRefs(target)); @@ -352,6 +357,65 @@ NS_IMETHODIMP nsRootAccessible::GetCaretAccessible(nsIAccessible **aCaretAccessi return NS_OK; } +void nsRootAccessible::TryFireEarlyLoadEvent(nsIAccessible *aAccessible, nsIDOMNode *aDocNode) +{ + // We can fire an early load event based on DOMContentLoaded unless we + // have subdocuments. For that we wait until WebProgressListener + // STATE_STOP handling in nsAccessibilityService. + + // Note, we don't fire any page load finished events for chrome or for + // frame/iframe page loads during the initial complete page load -- that page + // load event for the entire content pane needs to stand alone. + + // This also works for firing events for error pages + + nsCOMPtr treeItem = + GetDocShellTreeItemFor(aDocNode); + NS_ASSERTION(treeItem, "No docshelltreeitem for aDocNode"); + if (!treeItem) { + return; + } + PRInt32 itemType; + treeItem->GetItemType(&itemType); + if (itemType != nsIDocShellTreeItem::typeContent) { + return; + } + nsCOMPtr treeNode(do_QueryInterface(treeItem)); + if (treeNode) { + PRInt32 subDocuments; + treeNode->GetChildCount(&subDocuments); + if (subDocuments) { + return; + } + } + nsCOMPtr rootContentTreeItem; + treeItem->GetSameTypeRootTreeItem(getter_AddRefs(rootContentTreeItem)); + NS_ASSERTION(rootContentTreeItem, "No root content tree item"); + if (!rootContentTreeItem) { // Not at root of content + return; + } + if (rootContentTreeItem != treeItem) { + nsCOMPtr rootContentDocAccessible = + GetDocAccessibleFor(rootContentTreeItem); + nsCOMPtr rootContentAccessible = + do_QueryInterface(rootContentDocAccessible); + PRUint32 state; + rootContentAccessible->GetFinalState(&state); + if (state & STATE_BUSY) { + // Don't fire page load events on subdocuments for initial page load of entire page + return; + } + } + + // No frames or iframes, so we can fire the doc load finished event early + nsCOMPtr docAccessible = + do_QueryInterface(aAccessible); + NS_ASSERTION(docAccessible, "No doc aAccessible for DOMContentLoaded"); + if (docAccessible) { + docAccessible->FireDocLoadingEvent(PR_TRUE); + } +} + void nsRootAccessible::FireAccessibleFocusEvent(nsIAccessible *aAccessible, nsIDOMNode *aNode, PRBool aForceEvent) @@ -680,6 +744,9 @@ NS_IMETHODIMP nsRootAccessible::HandleEvent(nsIDOMEvent* aEvent) // Focus was inside of popup that's being hidden FireCurrentFocusEvent(); } + else if (eventType.LowerCaseEqualsLiteral("domcontentloaded")) { + TryFireEarlyLoadEvent(accessible, targetNode); + } else { // Menu popup events PRUint32 menuEvent = 0; diff --git a/accessible/src/base/nsRootAccessible.h b/accessible/src/base/nsRootAccessible.h index 88674f62a959..c617229e2ce5 100644 --- a/accessible/src/base/nsRootAccessible.h +++ b/accessible/src/base/nsRootAccessible.h @@ -109,6 +109,8 @@ class nsRootAccessible : public nsDocAccessibleWrap, nsresult AddEventListeners(); nsresult RemoveEventListeners(); static void GetTargetNode(nsIDOMEvent *aEvent, nsIDOMNode **aTargetNode); + void TryFireEarlyLoadEvent(cnsIAccessible *focusAccessible, + nsIDOMNode *focusNode); void FireAccessibleFocusEvent(nsIAccessible *focusAccessible, nsIDOMNode *focusNode, PRBool aForceEvent = PR_FALSE); diff --git a/accessible/src/msaa/nsDocAccessibleWrap.cpp b/accessible/src/msaa/nsDocAccessibleWrap.cpp index 2d1c40ea1410..a95790b6f767 100644 --- a/accessible/src/msaa/nsDocAccessibleWrap.cpp +++ b/accessible/src/msaa/nsDocAccessibleWrap.cpp @@ -330,9 +330,7 @@ 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 - PRUint32 state; - GetState(&state); - if (state & STATE_BUSY) { + if (!mIsContentLoaded) { return NS_OK; } nsCOMPtr container = mDocument->GetContainer(); @@ -418,14 +416,19 @@ void nsDocAccessibleWrap::DocLoadCallback(nsITimer *aTimer, void *aClosure) return; } - nsCOMPtr docShell = do_QueryInterface(container); - NS_ASSERTION(docShell, "No docShell for docShellTreeItem"); - PRBool hasFocus; - docShell->GetHasFocus(&hasFocus); - if (hasFocus) { - docAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, - docAcc, nsnull); - docAcc->FireAnchorJumpEvent(); + // Fire STATE_CHANGE event for doc load finish if focus is in same doc tree + if (gLastFocusedNode) { + nsCOMPtr focusedTreeItem = + GetDocShellTreeItemFor(gLastFocusedNode); + if (focusedTreeItem) { + nsCOMPtr sameTypeRootOfFocus; + focusedTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRootOfFocus)); + if (sameTypeRoot == sameTypeRootOfFocus) { + docAcc->FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, + docAcc, nsnull); + docAcc->FireAnchorJumpEvent(); + } + } } } } @@ -435,6 +438,12 @@ 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 @@ -450,7 +459,7 @@ NS_IMETHODIMP nsDocAccessibleWrap::FireDocLoadingEvent(PRBool aIsFinished) FireToolkitEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, this, nsnull); } - return nsDocAccessible::FireDocLoadingEvent(aIsFinished); + return NS_OK; } STDMETHODIMP nsDocAccessibleWrap::get_URL(/* [out] */ BSTR __RPC_FAR *aURL)