2007-03-22 10:30:00 -07: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 of 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 "nsRootAccessible.h"
|
|
|
|
#include "nsAccessibilityAtoms.h"
|
|
|
|
#include "nsAccessibleEventData.h"
|
2009-07-29 02:01:48 -07:00
|
|
|
#include "nsAccessibilityService.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "nsIMutableArray.h"
|
|
|
|
#include "nsICommandManager.h"
|
|
|
|
#include "nsIDocShell.h"
|
|
|
|
#include "nsIDocShellTreeItem.h"
|
|
|
|
#include "nsIDocument.h"
|
|
|
|
#include "nsIDOMAttr.h"
|
|
|
|
#include "nsIDOMCharacterData.h"
|
|
|
|
#include "nsIDOMDocument.h"
|
|
|
|
#include "nsIDOMDocumentType.h"
|
|
|
|
#include "nsIDOMNSDocument.h"
|
|
|
|
#include "nsIDOMNSHTMLDocument.h"
|
|
|
|
#include "nsIDOMMutationEvent.h"
|
|
|
|
#include "nsPIDOMWindow.h"
|
|
|
|
#include "nsIDOMXULPopupElement.h"
|
|
|
|
#include "nsIEditingSession.h"
|
|
|
|
#include "nsIEventStateManager.h"
|
|
|
|
#include "nsIFrame.h"
|
|
|
|
#include "nsHTMLSelectAccessible.h"
|
|
|
|
#include "nsIInterfaceRequestorUtils.h"
|
|
|
|
#include "nsINameSpaceManager.h"
|
|
|
|
#include "nsIPresShell.h"
|
|
|
|
#include "nsIServiceManager.h"
|
|
|
|
#include "nsIScrollableView.h"
|
|
|
|
#include "nsIViewManager.h"
|
|
|
|
#include "nsIView.h"
|
|
|
|
#include "nsUnicharUtils.h"
|
|
|
|
#include "nsIURI.h"
|
|
|
|
#include "nsIWebNavigation.h"
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
#include "nsFocusManager.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef MOZ_XUL
|
|
|
|
#include "nsIXULDocument.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//=============================//
|
|
|
|
// nsDocAccessible //
|
|
|
|
//=============================//
|
|
|
|
|
2007-09-18 14:36:41 -07:00
|
|
|
PRUint32 nsDocAccessible::gLastFocusedAccessiblesState = 0;
|
2008-02-08 18:28:01 -08:00
|
|
|
nsIAtom *nsDocAccessible::gLastFocusedFrameType = nsnull;
|
2007-09-18 14:36:41 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
//-----------------------------------------------------
|
|
|
|
// construction
|
|
|
|
//-----------------------------------------------------
|
|
|
|
nsDocAccessible::nsDocAccessible(nsIDOMNode *aDOMNode, nsIWeakReference* aShell):
|
2007-05-01 10:08:26 -07:00
|
|
|
nsHyperTextAccessibleWrap(aDOMNode, aShell), mWnd(nsnull),
|
2008-06-05 02:49:51 -07:00
|
|
|
mScrollPositionChangedTicks(0), mIsContentLoaded(PR_FALSE),
|
|
|
|
mIsLoadCompleteFired(PR_FALSE), mInFlushPendingEvents(PR_FALSE)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2009-08-10 23:59:05 -07:00
|
|
|
// XXX aaronl should we use an algorithm for the initial cache size?
|
|
|
|
mAccessNodeCache.Init(kDefaultCacheSize);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// For GTK+ native window, we do nothing here.
|
|
|
|
if (!mDOMNode)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// 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));
|
|
|
|
if (shell) {
|
2007-09-24 18:19:03 -07:00
|
|
|
// Find mDocument
|
2007-03-22 10:30:00 -07:00
|
|
|
mDocument = shell->GetDocument();
|
2007-09-24 18:19:03 -07:00
|
|
|
|
|
|
|
// Find mWnd
|
2007-03-22 10:30:00 -07:00
|
|
|
nsIViewManager* vm = shell->GetViewManager();
|
|
|
|
if (vm) {
|
|
|
|
nsCOMPtr<nsIWidget> widget;
|
2009-07-21 17:45:04 -07:00
|
|
|
vm->GetRootWidget(getter_AddRefs(widget));
|
2007-03-22 10:30:00 -07:00
|
|
|
if (widget) {
|
|
|
|
mWnd = widget->GetNativeData(NS_NATIVE_WINDOW);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-05 00:39:09 -07:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
|
2008-10-15 18:52:58 -07:00
|
|
|
nsCoreUtils::GetDocShellTreeItemFor(mDOMNode);
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(docShellTreeItem);
|
|
|
|
if (docShell) {
|
|
|
|
PRUint32 busyFlags;
|
|
|
|
docShell->GetBusyFlags(&busyFlags);
|
|
|
|
if (busyFlags == nsIDocShell::BUSY_FLAGS_NONE) {
|
|
|
|
mIsContentLoaded = PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------
|
|
|
|
// destruction
|
|
|
|
//-----------------------------------------------------
|
|
|
|
nsDocAccessible::~nsDocAccessible()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2008-08-06 05:19:56 -07:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsDocAccessible. nsISupports
|
|
|
|
|
2008-10-10 08:04:34 -07:00
|
|
|
static PLDHashOperator
|
2008-08-06 05:19:56 -07:00
|
|
|
ElementTraverser(const void *aKey, nsIAccessNode *aAccessNode,
|
|
|
|
void *aUserArg)
|
|
|
|
{
|
|
|
|
nsCycleCollectionTraversalCallback *cb =
|
|
|
|
static_cast<nsCycleCollectionTraversalCallback*>(aUserArg);
|
|
|
|
|
2008-09-04 21:44:45 -07:00
|
|
|
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mAccessNodeCache entry");
|
2008-08-06 05:19:56 -07:00
|
|
|
cb->NoteXPCOMChild(aAccessNode);
|
|
|
|
return PL_DHASH_NEXT;
|
|
|
|
}
|
|
|
|
|
2009-06-24 19:08:53 -07:00
|
|
|
// What we want is: NS_INTERFACE_MAP_ENTRY(self) for static IID accessors,
|
|
|
|
// but some of our classes have an ambiguous base class of nsISupports which
|
|
|
|
// prevents this from working (the default macro converts it to nsISupports,
|
|
|
|
// then addrefs it, then returns it). Therefore, we expand the macro here and
|
|
|
|
// change it so that it works. Yuck.
|
|
|
|
#define NS_INTERFACE_MAP_STATIC_AMBIGUOUS(_class) \
|
|
|
|
if (aIID.Equals(NS_GET_IID(_class))) { \
|
|
|
|
NS_ADDREF(this); \
|
|
|
|
*aInstancePtr = this; \
|
|
|
|
return NS_OK; \
|
|
|
|
} else
|
|
|
|
|
2008-08-06 05:19:56 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocAccessible)
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mEventsToFire)
|
|
|
|
tmp->mAccessNodeCache.EnumerateRead(ElementTraverser, &cb);
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mEventsToFire)
|
|
|
|
tmp->ClearCache(tmp->mAccessNodeCache);
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDocAccessible)
|
2009-06-24 19:08:53 -07:00
|
|
|
NS_INTERFACE_MAP_STATIC_AMBIGUOUS(nsDocAccessible)
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIAccessibleDocument)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIScrollPositionListener)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
2009-06-24 19:08:53 -07:00
|
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessibleDocument)
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_INTERFACE_MAP_END_INHERITING(nsHyperTextAccessible)
|
|
|
|
|
|
|
|
NS_IMPL_ADDREF_INHERITED(nsDocAccessible, nsHyperTextAccessible)
|
|
|
|
NS_IMPL_RELEASE_INHERITED(nsDocAccessible, nsHyperTextAccessible)
|
|
|
|
|
2008-10-10 05:26:55 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDocAccessible::GetName(nsAString& aName)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
aName.Truncate();
|
2008-03-14 13:49:38 -07:00
|
|
|
if (mParent) {
|
|
|
|
rv = mParent->GetName(aName); // Allow owning iframe to override the name
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
if (aName.IsEmpty()) {
|
2008-10-10 05:26:55 -07:00
|
|
|
// Allow name via aria-labelledby or title attribute
|
|
|
|
rv = nsAccessible::GetName(aName);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2008-03-14 13:49:38 -07:00
|
|
|
if (aName.IsEmpty()) {
|
2008-04-15 08:17:59 -07:00
|
|
|
rv = GetTitle(aName); // Try title element
|
|
|
|
}
|
|
|
|
if (aName.IsEmpty()) { // Last resort: use URL
|
|
|
|
rv = GetURL(aName);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2009-03-07 07:38:58 -08:00
|
|
|
nsresult
|
|
|
|
nsDocAccessible::GetRoleInternal(PRUint32 *aRole)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
*aRole = nsIAccessibleRole::ROLE_PANE; // Fall back
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
|
2008-10-15 18:52:58 -07:00
|
|
|
nsCoreUtils::GetDocShellTreeItemFor(mDOMNode);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (docShellTreeItem) {
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
|
|
|
|
docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
|
2007-06-14 10:12:50 -07:00
|
|
|
PRInt32 itemType;
|
|
|
|
docShellTreeItem->GetItemType(&itemType);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (sameTypeRoot == docShellTreeItem) {
|
|
|
|
// Root of content or chrome tree
|
|
|
|
if (itemType == nsIDocShellTreeItem::typeChrome) {
|
|
|
|
*aRole = nsIAccessibleRole::ROLE_CHROME_WINDOW;
|
|
|
|
}
|
|
|
|
else if (itemType == nsIDocShellTreeItem::typeContent) {
|
|
|
|
#ifdef MOZ_XUL
|
|
|
|
nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
|
2007-04-24 21:11:34 -07:00
|
|
|
if (xulDoc) {
|
|
|
|
*aRole = nsIAccessibleRole::ROLE_APPLICATION;
|
|
|
|
} else {
|
|
|
|
*aRole = nsIAccessibleRole::ROLE_DOCUMENT;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
#else
|
|
|
|
*aRole = nsIAccessibleRole::ROLE_DOCUMENT;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
2007-06-14 10:12:50 -07:00
|
|
|
else if (itemType == nsIDocShellTreeItem::typeContent) {
|
|
|
|
*aRole = nsIAccessibleRole::ROLE_DOCUMENT;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2009-06-18 00:37:38 -07:00
|
|
|
void
|
|
|
|
nsDocAccessible::SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2009-06-18 00:37:38 -07:00
|
|
|
NS_ASSERTION(mDocument, "No document during initialization!");
|
|
|
|
if (!mDocument)
|
|
|
|
return;
|
2008-03-14 13:49:38 -07:00
|
|
|
|
|
|
|
mRoleMapEntry = aRoleMapEntry;
|
|
|
|
|
|
|
|
nsIDocument *parentDoc = mDocument->GetParentDocument();
|
2009-06-18 00:37:38 -07:00
|
|
|
if (!parentDoc)
|
2009-06-29 06:26:45 -07:00
|
|
|
return; // No parent document for the root document
|
2009-06-18 00:37:38 -07:00
|
|
|
|
2009-06-29 06:26:45 -07:00
|
|
|
// Allow use of ARIA role from outer to override
|
2008-03-14 13:49:38 -07:00
|
|
|
nsIContent *ownerContent = parentDoc->FindContentForSubDocument(mDocument);
|
|
|
|
nsCOMPtr<nsIDOMNode> ownerNode(do_QueryInterface(ownerContent));
|
|
|
|
if (ownerNode) {
|
2008-10-16 02:12:05 -07:00
|
|
|
nsRoleMapEntry *roleMapEntry = nsAccUtils::GetRoleMapEntry(ownerNode);
|
2008-03-14 13:49:38 -07:00
|
|
|
if (roleMapEntry)
|
|
|
|
mRoleMapEntry = roleMapEntry; // Override
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-07-01 23:14:11 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDocAccessible::GetDescription(nsAString& aDescription)
|
|
|
|
{
|
2008-03-14 13:49:38 -07:00
|
|
|
if (mParent)
|
|
|
|
mParent->GetDescription(aDescription);
|
|
|
|
|
|
|
|
if (aDescription.IsEmpty()) {
|
|
|
|
nsAutoString description;
|
2009-02-18 23:06:14 -08:00
|
|
|
nsTextEquivUtils::
|
|
|
|
GetTextEquivFromIDRefs(this, nsAccessibilityAtoms::aria_describedby,
|
|
|
|
description);
|
2008-03-14 13:49:38 -07:00
|
|
|
aDescription = description;
|
|
|
|
}
|
|
|
|
|
2007-07-01 23:14:11 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2008-11-03 19:37:46 -08:00
|
|
|
nsresult
|
|
|
|
nsDocAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-11-26 20:04:05 -08:00
|
|
|
nsresult rv = nsAccessible::GetStateInternal(aState, aExtraState);
|
|
|
|
NS_ENSURE_A11Y_SUCCESS(rv, rv);
|
2007-04-02 08:56:24 -07:00
|
|
|
|
2007-07-24 00:50:51 -07:00
|
|
|
#ifdef MOZ_XUL
|
2007-04-09 06:40:25 -07:00
|
|
|
nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
|
2007-07-24 00:50:51 -07:00
|
|
|
if (!xulDoc)
|
|
|
|
#endif
|
|
|
|
{
|
2007-04-09 06:40:25 -07:00
|
|
|
// XXX Need to invent better check to see if doc is focusable,
|
|
|
|
// which it should be if it is scrollable. A XUL document could be focusable.
|
|
|
|
// See bug 376803.
|
|
|
|
*aState |= nsIAccessibleStates::STATE_FOCUSABLE;
|
2008-05-04 10:42:19 -07:00
|
|
|
if (gLastFocusedNode == mDOMNode) {
|
|
|
|
*aState |= nsIAccessibleStates::STATE_FOCUSED;
|
|
|
|
}
|
2007-04-09 06:40:25 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (!mIsContentLoaded) {
|
|
|
|
*aState |= nsIAccessibleStates::STATE_BUSY;
|
2007-05-04 08:15:00 -07:00
|
|
|
if (aExtraState) {
|
|
|
|
*aExtraState |= nsIAccessibleStates::EXT_STATE_STALE;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nsIFrame* frame = GetFrame();
|
|
|
|
while (frame != nsnull && !frame->HasView()) {
|
|
|
|
frame = frame->GetParent();
|
|
|
|
}
|
|
|
|
|
2008-02-08 05:59:46 -08:00
|
|
|
if (frame == nsnull ||
|
|
|
|
!CheckVisibilityInParentChain(mDocument, frame->GetViewExternal())) {
|
|
|
|
*aState |= nsIAccessibleStates::STATE_INVISIBLE |
|
|
|
|
nsIAccessibleStates::STATE_OFFSCREEN;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-08-14 09:25:24 -07:00
|
|
|
nsCOMPtr<nsIEditor> editor;
|
|
|
|
GetAssociatedEditor(getter_AddRefs(editor));
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!editor) {
|
|
|
|
*aState |= nsIAccessibleStates::STATE_READONLY;
|
|
|
|
}
|
2007-04-13 04:38:43 -07:00
|
|
|
else if (aExtraState) {
|
|
|
|
*aExtraState |= nsIAccessibleStates::EXT_STATE_EDITABLE;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2009-06-18 00:37:38 -07:00
|
|
|
nsresult
|
2009-06-24 19:12:38 -07:00
|
|
|
nsDocAccessible::GetARIAState(PRUint32 *aState, PRUint32 *aExtraState)
|
2008-03-14 13:49:38 -07:00
|
|
|
{
|
|
|
|
// Combine with states from outer doc
|
|
|
|
NS_ENSURE_ARG_POINTER(aState);
|
2009-06-24 19:12:38 -07:00
|
|
|
nsresult rv = nsAccessible::GetARIAState(aState, aExtraState);
|
2008-03-14 13:49:38 -07:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
2009-06-18 00:37:38 -07:00
|
|
|
nsRefPtr<nsAccessible> parent = nsAccUtils::QueryAccessible(mParent);
|
|
|
|
if (parent) // Allow iframe/frame etc. to have final state override via ARIA
|
2009-06-24 19:12:38 -07:00
|
|
|
return parent->GetARIAState(aState, aExtraState);
|
2008-03-14 13:49:38 -07:00
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDocAccessible::GetAttributes(nsIPersistentProperties **aAttributes)
|
|
|
|
{
|
|
|
|
nsAccessible::GetAttributes(aAttributes);
|
|
|
|
if (mParent) {
|
|
|
|
mParent->GetAttributes(aAttributes); // Add parent attributes (override inner)
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_IMETHODIMP nsDocAccessible::GetFocusedChild(nsIAccessible **aFocusedChild)
|
|
|
|
{
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
// XXXndeakin P3 accessibility shouldn't be caching the focus
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!gLastFocusedNode) {
|
|
|
|
*aFocusedChild = nsnull;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return an accessible for the current global focus, which does not have to
|
|
|
|
// be contained within the current document.
|
|
|
|
nsCOMPtr<nsIAccessibilityService> accService =
|
|
|
|
do_GetService("@mozilla.org/accessibilityService;1");
|
|
|
|
return accService->GetAccessibleFor(gLastFocusedNode, aFocusedChild);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsDocAccessible::TakeFocus()
|
|
|
|
{
|
2007-04-27 08:15:19 -07:00
|
|
|
NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
|
|
|
|
PRUint32 state;
|
2008-11-03 19:37:46 -08:00
|
|
|
GetStateInternal(&state, nsnull);
|
2007-04-27 08:15:19 -07:00
|
|
|
if (0 == (state & nsIAccessibleStates::STATE_FOCUSABLE)) {
|
|
|
|
return NS_ERROR_FAILURE; // Not focusable
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-04-27 08:15:19 -07:00
|
|
|
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
|
|
|
|
if (fm) {
|
|
|
|
nsCOMPtr<nsIDOMDocument> domDocument;
|
|
|
|
mDOMNode->GetOwnerDocument(getter_AddRefs(domDocument));
|
|
|
|
nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
|
|
|
|
if (document) {
|
|
|
|
// focus the document
|
|
|
|
nsCOMPtr<nsIDOMElement> newFocus;
|
2009-06-12 15:51:50 -07:00
|
|
|
return fm->MoveFocus(document->GetWindow(), nsnull,
|
|
|
|
nsIFocusManager::MOVEFOCUS_ROOT, 0,
|
|
|
|
getter_AddRefs(newFocus));
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
}
|
2007-09-05 06:40:57 -07:00
|
|
|
}
|
2009-06-12 15:51:50 -07:00
|
|
|
return NS_ERROR_FAILURE;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// ------- nsIAccessibleDocument Methods (5) ---------------
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsDocAccessible::GetURL(nsAString& aURL)
|
|
|
|
{
|
|
|
|
if (!mDocument) {
|
|
|
|
return NS_ERROR_FAILURE; // Document has been shut down
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
CopyUTF8toUTF16(theURL, aURL);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsDocAccessible::GetTitle(nsAString& aTitle)
|
|
|
|
{
|
2008-08-17 19:10:28 -07:00
|
|
|
nsCOMPtr<nsIDOMNSDocument> domnsDocument(do_QueryInterface(mDocument));
|
|
|
|
if (domnsDocument) {
|
|
|
|
return domnsDocument->GetTitle(aTitle);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
#ifdef MOZ_XUL
|
|
|
|
nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
|
|
|
|
if (xulDoc) {
|
|
|
|
aDocType.AssignLiteral("window"); // doctype not implemented for XUL at time of writing - causes assertion
|
|
|
|
return NS_OK;
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
if (domDoc && NS_SUCCEEDED(domDoc->GetDoctype(getter_AddRefs(docType))) && docType) {
|
|
|
|
return docType->GetPublicId(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::GetWindowHandle(void **aWindow)
|
|
|
|
{
|
|
|
|
*aWindow = mWnd;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsDocAccessible::GetWindow(nsIDOMWindow **aDOMWin)
|
|
|
|
{
|
|
|
|
*aDOMWin = nsnull;
|
|
|
|
if (!mDocument) {
|
|
|
|
return NS_ERROR_FAILURE; // Accessible is Shutdown()
|
|
|
|
}
|
|
|
|
*aDOMWin = mDocument->GetWindow();
|
|
|
|
|
|
|
|
if (!*aDOMWin)
|
|
|
|
return NS_ERROR_FAILURE; // No DOM Window
|
|
|
|
|
|
|
|
NS_ADDREF(*aDOMWin);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsDocAccessible::GetDocument(nsIDOMDocument **aDOMDoc)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(mDocument));
|
|
|
|
*aDOMDoc = domDoc;
|
|
|
|
|
|
|
|
if (domDoc) {
|
|
|
|
NS_ADDREF(*aDOMDoc);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
2007-08-14 09:25:24 -07:00
|
|
|
NS_IMETHODIMP nsDocAccessible::GetAssociatedEditor(nsIEditor **aEditor)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-08-14 09:25:24 -07:00
|
|
|
NS_ENSURE_ARG_POINTER(aEditor);
|
|
|
|
*aEditor = nsnull;
|
|
|
|
|
2008-10-08 05:50:36 -07:00
|
|
|
if (!mDocument)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
// Check if document is editable (designMode="on" case). Otherwise check if
|
|
|
|
// the html:body (for HTML document case) or document element is editable.
|
2007-08-14 09:25:24 -07:00
|
|
|
if (!mDocument->HasFlag(NODE_IS_EDITABLE)) {
|
2008-10-08 05:50:36 -07:00
|
|
|
nsCOMPtr<nsIDOMNode> DOMDocument(do_QueryInterface(mDocument));
|
|
|
|
nsCOMPtr<nsIDOMElement> DOMElement =
|
2008-10-15 18:52:58 -07:00
|
|
|
nsCoreUtils::GetDOMElementFor(DOMDocument);
|
2008-10-08 05:50:36 -07:00
|
|
|
nsCOMPtr<nsIContent> content(do_QueryInterface(DOMElement));
|
|
|
|
|
2008-10-29 10:03:20 -07:00
|
|
|
if (!content || !content->HasFlag(NODE_IS_EDITABLE))
|
2008-10-08 05:50:36 -07:00
|
|
|
return NS_OK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsISupports> container = mDocument->GetContainer();
|
|
|
|
nsCOMPtr<nsIEditingSession> editingSession(do_GetInterface(container));
|
|
|
|
if (!editingSession)
|
2007-08-14 09:25:24 -07:00
|
|
|
return NS_OK; // No editing session interface
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
nsCOMPtr<nsIEditor> editor;
|
2007-08-14 09:25:24 -07:00
|
|
|
editingSession->GetEditorForWindow(mDocument->GetWindow(), getter_AddRefs(editor));
|
|
|
|
if (!editor) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
PRBool isEditable;
|
|
|
|
editor->GetIsDocumentEditable(&isEditable);
|
|
|
|
if (isEditable) {
|
|
|
|
NS_ADDREF(*aEditor = editor);
|
|
|
|
}
|
|
|
|
return NS_OK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsDocAccessible::GetCachedAccessNode(void *aUniqueID, nsIAccessNode **aAccessNode)
|
|
|
|
{
|
|
|
|
GetCacheEntry(mAccessNodeCache, aUniqueID, aAccessNode); // Addrefs for us
|
|
|
|
#ifdef DEBUG_A11Y
|
|
|
|
// All cached accessible nodes should be in the parent
|
|
|
|
// It will assert if not all the children were created
|
|
|
|
// when they were first cached, and no invalidation
|
|
|
|
// ever corrected parent accessible's child cache.
|
|
|
|
nsCOMPtr<nsIAccessible> accessible = do_QueryInterface(*aAccessNode);
|
2009-06-18 00:37:38 -07:00
|
|
|
nsRefPtr<nsAccessible> acc = nsAccUtils::QueryAccessible(accessible);
|
|
|
|
if (acc) {
|
|
|
|
nsCOMPtr<nsIAccessible> parent = acc->GetCachedParent();
|
|
|
|
nsRefPtr<nsAccessible> parentAcc(nsAccUtils::QueryAccessible(parent));
|
|
|
|
if (parentAcc)
|
|
|
|
parentAcc->TestChildCache(accessible);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2009-06-24 19:08:53 -07:00
|
|
|
void
|
2008-06-11 02:17:37 -07:00
|
|
|
nsDocAccessible::CacheAccessNode(void *aUniqueID, nsIAccessNode *aAccessNode)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-06-11 02:17:37 -07:00
|
|
|
// If there is an access node for the given unique ID then let's shutdown it.
|
|
|
|
// The unique ID may be presented in the cache if originally we created
|
|
|
|
// access node object and then we want to create accessible object when
|
|
|
|
// DOM node is changed.
|
|
|
|
nsCOMPtr<nsIAccessNode> accessNode;
|
|
|
|
GetCacheEntry(mAccessNodeCache, aUniqueID, getter_AddRefs(accessNode));
|
|
|
|
if (accessNode) {
|
2008-10-31 20:58:07 -07:00
|
|
|
nsRefPtr<nsAccessNode> accNode = nsAccUtils::QueryAccessNode(accessNode);
|
|
|
|
accNode->Shutdown();
|
2008-06-11 02:17:37 -07:00
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
PutCacheEntry(mAccessNodeCache, aUniqueID, aAccessNode);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsDocAccessible::GetParent(nsIAccessible **aParent)
|
|
|
|
{
|
|
|
|
// Hook up our new accessible with our parent
|
2007-04-10 10:46:20 -07:00
|
|
|
*aParent = nsnull;
|
|
|
|
NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!mParent) {
|
|
|
|
nsIDocument *parentDoc = mDocument->GetParentDocument();
|
2007-04-10 10:46:20 -07:00
|
|
|
NS_ENSURE_TRUE(parentDoc, NS_ERROR_FAILURE);
|
|
|
|
nsIContent *ownerContent = parentDoc->FindContentForSubDocument(mDocument);
|
|
|
|
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()
|
|
|
|
accService->GetAccessibleFor(ownerNode, getter_AddRefs(mParent));
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return mParent ? nsAccessible::GetParent(aParent) : NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
2008-10-31 20:58:07 -07:00
|
|
|
nsresult
|
|
|
|
nsDocAccessible::Init()
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-11-30 11:47:16 -08:00
|
|
|
PutCacheEntry(gGlobalDocAccessibleCache, mDocument, this);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
AddEventListeners();
|
|
|
|
|
2008-03-14 13:49:38 -07:00
|
|
|
nsCOMPtr<nsIAccessible> parentAccessible; // Ensure outer doc mParent accessible
|
|
|
|
GetParent(getter_AddRefs(parentAccessible));
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-07-17 20:09:16 -07:00
|
|
|
nsresult rv = nsHyperTextAccessibleWrap::Init();
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
// Fire reorder event to notify new accessible document has been created and
|
|
|
|
// attached to the tree.
|
|
|
|
nsCOMPtr<nsIAccessibleEvent> reorderEvent =
|
|
|
|
new nsAccReorderEvent(mParent, PR_FALSE, PR_TRUE, mDOMNode);
|
|
|
|
NS_ENSURE_TRUE(reorderEvent, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
|
|
|
|
FireDelayedAccessibleEvent(reorderEvent);
|
|
|
|
return NS_OK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2008-10-31 20:58:07 -07:00
|
|
|
nsresult
|
|
|
|
nsDocAccessible::Shutdown()
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
if (!mWeakShell) {
|
|
|
|
return NS_OK; // Already shutdown
|
|
|
|
}
|
|
|
|
|
2007-09-05 00:39:09 -07:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> treeItem =
|
2008-10-15 18:52:58 -07:00
|
|
|
nsCoreUtils::GetDocShellTreeItemFor(mDOMNode);
|
2007-03-22 10:30:00 -07:00
|
|
|
ShutdownChildDocuments(treeItem);
|
|
|
|
|
|
|
|
RemoveEventListeners();
|
|
|
|
|
|
|
|
mWeakShell = nsnull; // Avoid reentrancy
|
|
|
|
|
2007-12-26 21:13:40 -08:00
|
|
|
ClearCache(mAccessNodeCache);
|
|
|
|
|
2008-02-26 00:51:10 -08:00
|
|
|
nsCOMPtr<nsIDocument> kungFuDeathGripDoc = mDocument;
|
2007-12-26 21:13:40 -08:00
|
|
|
mDocument = nsnull;
|
|
|
|
|
|
|
|
nsHyperTextAccessibleWrap::Shutdown();
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mFireEventTimer) {
|
2007-12-26 21:13:40 -08:00
|
|
|
// Doc being shut down before events fired,
|
2007-03-22 10:30:00 -07:00
|
|
|
mFireEventTimer->Cancel();
|
|
|
|
mFireEventTimer = nsnull;
|
2007-12-26 21:13:40 -08:00
|
|
|
if (mEventsToFire.Count() > 0 ) {
|
|
|
|
mEventsToFire.Clear();
|
|
|
|
// Make sure we release the kung fu death grip which is always
|
|
|
|
// there when there are still events left to be fired
|
2008-06-05 02:49:51 -07:00
|
|
|
// If FlushPendingEvents() is in call stack,
|
|
|
|
// kung fu death grip will be released there.
|
|
|
|
if (!mInFlushPendingEvents)
|
|
|
|
NS_RELEASE_THIS();
|
2007-12-26 21:13:40 -08:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2008-02-26 00:51:10 -08:00
|
|
|
// Remove from the cache after other parts of Shutdown(), so that Shutdown() procedures
|
|
|
|
// can find the doc or root accessible in the cache if they need it.
|
2008-02-29 07:38:35 -08:00
|
|
|
// We don't do this during ShutdownAccessibility() because that is already clearing the cache
|
2009-07-29 02:01:48 -07:00
|
|
|
if (!nsAccessibilityService::gIsShutdown)
|
2008-02-29 07:38:35 -08:00
|
|
|
gGlobalDocAccessibleCache.Remove(static_cast<void*>(kungFuDeathGripDoc));
|
2008-02-26 00:51:10 -08:00
|
|
|
|
2007-12-26 21:13:40 -08:00
|
|
|
return NS_OK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void nsDocAccessible::ShutdownChildDocuments(nsIDocShellTreeItem *aStart)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDocShellTreeNode> treeNode(do_QueryInterface(aStart));
|
|
|
|
if (treeNode) {
|
|
|
|
PRInt32 subDocuments;
|
|
|
|
treeNode->GetChildCount(&subDocuments);
|
|
|
|
for (PRInt32 count = 0; count < subDocuments; count ++) {
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> treeItemChild;
|
|
|
|
treeNode->GetChildAt(count, getter_AddRefs(treeItemChild));
|
|
|
|
NS_ASSERTION(treeItemChild, "No tree item when there should be");
|
|
|
|
if (!treeItemChild) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
nsCOMPtr<nsIAccessibleDocument> docAccessible =
|
|
|
|
GetDocAccessibleFor(treeItemChild);
|
2008-10-31 20:58:07 -07:00
|
|
|
if (docAccessible) {
|
|
|
|
nsRefPtr<nsAccessNode> docAccNode =
|
|
|
|
nsAccUtils::QueryAccessNode(docAccessible);
|
|
|
|
docAccNode->Shutdown();
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-31 20:58:07 -07:00
|
|
|
nsIFrame*
|
|
|
|
nsDocAccessible::GetFrame()
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mWeakShell));
|
|
|
|
|
|
|
|
nsIFrame* root = nsnull;
|
|
|
|
if (shell)
|
|
|
|
root = shell->GetRootFrame();
|
|
|
|
|
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
2009-06-24 19:08:53 -07:00
|
|
|
PRBool
|
|
|
|
nsDocAccessible::IsDefunct()
|
|
|
|
{
|
|
|
|
if (nsHyperTextAccessibleWrap::IsDefunct())
|
|
|
|
return PR_TRUE;
|
|
|
|
|
|
|
|
return !mDocument;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
void nsDocAccessible::GetBoundsRect(nsRect& aBounds, nsIFrame** aRelativeFrame)
|
|
|
|
{
|
|
|
|
*aRelativeFrame = GetFrame();
|
|
|
|
|
|
|
|
nsIDocument *document = mDocument;
|
|
|
|
nsIDocument *parentDoc = nsnull;
|
|
|
|
|
|
|
|
while (document) {
|
2007-05-01 15:24:20 -07:00
|
|
|
nsIPresShell *presShell = document->GetPrimaryShell();
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!presShell) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
nsIViewManager* vm = presShell->GetViewManager();
|
|
|
|
if (!vm) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIScrollableView* scrollableView = nsnull;
|
|
|
|
vm->GetRootScrollableView(&scrollableView);
|
|
|
|
|
|
|
|
nsRect viewBounds(0, 0, 0, 0);
|
|
|
|
if (scrollableView) {
|
|
|
|
viewBounds = scrollableView->View()->GetBounds();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
nsIView *view;
|
|
|
|
vm->GetRootView(view);
|
|
|
|
if (view) {
|
|
|
|
viewBounds = view->GetBounds();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parentDoc) { // After first time thru loop
|
|
|
|
aBounds.IntersectRect(viewBounds, aBounds);
|
|
|
|
}
|
|
|
|
else { // First time through loop
|
|
|
|
aBounds = viewBounds;
|
|
|
|
}
|
|
|
|
|
|
|
|
document = parentDoc = document->GetParentDocument();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nsresult nsDocAccessible::AddEventListeners()
|
|
|
|
{
|
|
|
|
// 1) Set up scroll position listener
|
|
|
|
// 2) Check for editor and listen for changes to editor
|
|
|
|
|
|
|
|
nsCOMPtr<nsIPresShell> presShell(GetPresShell());
|
|
|
|
NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
|
|
|
|
|
|
|
|
nsCOMPtr<nsISupports> container = mDocument->GetContainer();
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(do_QueryInterface(container));
|
|
|
|
NS_ENSURE_TRUE(docShellTreeItem, NS_ERROR_FAILURE);
|
|
|
|
|
|
|
|
// Make sure we're a content docshell
|
|
|
|
// We don't want to listen to chrome progress
|
|
|
|
PRInt32 itemType;
|
|
|
|
docShellTreeItem->GetItemType(&itemType);
|
|
|
|
|
|
|
|
PRBool isContent = (itemType == nsIDocShellTreeItem::typeContent);
|
|
|
|
|
|
|
|
if (isContent) {
|
2007-08-14 09:25:24 -07:00
|
|
|
// We're not an editor yet, but we might become one
|
|
|
|
nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(docShellTreeItem);
|
|
|
|
if (commandManager) {
|
|
|
|
commandManager->AddCommandObserver(this, "obs_documentCreated");
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-05-07 11:55:17 -07:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
|
|
|
|
docShellTreeItem->GetRootTreeItem(getter_AddRefs(rootTreeItem));
|
|
|
|
if (rootTreeItem) {
|
2007-10-10 09:51:45 -07:00
|
|
|
nsCOMPtr<nsIAccessibleDocument> rootAccDoc =
|
|
|
|
GetDocAccessibleFor(rootTreeItem, PR_TRUE); // Ensure root accessible is created;
|
|
|
|
nsRefPtr<nsRootAccessible> rootAccessible = GetRootAccessible(); // Then get it as ref ptr
|
2007-06-14 10:12:50 -07:00
|
|
|
NS_ENSURE_TRUE(rootAccessible, NS_ERROR_FAILURE);
|
|
|
|
nsRefPtr<nsCaretAccessible> caretAccessible = rootAccessible->GetCaretAccessible();
|
|
|
|
if (caretAccessible) {
|
2008-01-28 20:38:18 -08:00
|
|
|
caretAccessible->AddDocSelectionListener(presShell);
|
2007-05-07 11:55:17 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// add document observer
|
|
|
|
mDocument->AddObserver(this);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult nsDocAccessible::RemoveEventListeners()
|
|
|
|
{
|
|
|
|
// Remove listeners associated with content documents
|
|
|
|
// Remove scroll position listener
|
|
|
|
RemoveScrollListener();
|
|
|
|
|
2009-06-28 10:45:04 -07:00
|
|
|
NS_ASSERTION(mDocument, "No document during removal of listeners.");
|
|
|
|
|
|
|
|
if (mDocument) {
|
|
|
|
mDocument->RemoveObserver(this);
|
|
|
|
|
|
|
|
nsCOMPtr<nsISupports> container = mDocument->GetContainer();
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(do_QueryInterface(container));
|
|
|
|
NS_ASSERTION(docShellTreeItem, "doc should support nsIDocShellTreeItem.");
|
|
|
|
|
|
|
|
if (docShellTreeItem) {
|
|
|
|
PRInt32 itemType;
|
|
|
|
docShellTreeItem->GetItemType(&itemType);
|
|
|
|
if (itemType == nsIDocShellTreeItem::typeContent) {
|
|
|
|
nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(docShellTreeItem);
|
|
|
|
if (commandManager) {
|
|
|
|
commandManager->RemoveCommandObserver(this, "obs_documentCreated");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (mScrollWatchTimer) {
|
|
|
|
mScrollWatchTimer->Cancel();
|
|
|
|
mScrollWatchTimer = nsnull;
|
2008-01-18 12:36:44 -08:00
|
|
|
NS_RELEASE_THIS(); // Kung fu death grip
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-05-07 11:55:17 -07:00
|
|
|
nsRefPtr<nsRootAccessible> rootAccessible(GetRootAccessible());
|
|
|
|
if (rootAccessible) {
|
2007-06-14 10:12:50 -07:00
|
|
|
nsRefPtr<nsCaretAccessible> caretAccessible = rootAccessible->GetCaretAccessible();
|
|
|
|
if (caretAccessible) {
|
2008-01-29 21:42:44 -08:00
|
|
|
// Don't use GetPresShell() which can call Shutdown() if it sees dead pres shell
|
|
|
|
nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell));
|
2008-01-28 20:38:18 -08:00
|
|
|
caretAccessible->RemoveDocSelectionListener(presShell);
|
2007-05-07 11:55:17 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2009-06-24 19:08:53 -07:00
|
|
|
void
|
|
|
|
nsDocAccessible::FireAnchorJumpEvent()
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2009-06-24 19:08:53 -07:00
|
|
|
if (!mIsContentLoaded || !mDocument)
|
|
|
|
return;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-24 19:08:53 -07:00
|
|
|
void
|
|
|
|
nsDocAccessible::FireDocLoadEvents(PRUint32 aEventType)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2009-06-24 19:08:53 -07:00
|
|
|
if (IsDefunct())
|
|
|
|
return;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
PRBool isFinished =
|
|
|
|
(aEventType == nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE ||
|
|
|
|
aEventType == nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED);
|
|
|
|
|
|
|
|
mIsContentLoaded = isFinished;
|
2008-02-26 06:19:52 -08:00
|
|
|
if (isFinished) {
|
|
|
|
if (mIsLoadCompleteFired)
|
2009-06-24 19:08:53 -07:00
|
|
|
return;
|
|
|
|
|
2008-02-26 06:19:52 -08:00
|
|
|
mIsLoadCompleteFired = PR_TRUE;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-02-26 00:51:10 -08:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> treeItem =
|
2008-10-15 18:52:58 -07:00
|
|
|
nsCoreUtils::GetDocShellTreeItemFor(mDOMNode);
|
2009-06-24 19:08:53 -07:00
|
|
|
if (!treeItem)
|
|
|
|
return;
|
|
|
|
|
2008-02-26 00:51:10 -08:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
|
|
|
|
treeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (isFinished) {
|
|
|
|
// Need to wait until scrollable view is available
|
|
|
|
AddScrollListener();
|
|
|
|
nsCOMPtr<nsIAccessible> parent(nsAccessible::GetParent());
|
2009-06-18 00:37:38 -07:00
|
|
|
nsRefPtr<nsAccessible> acc(nsAccUtils::QueryAccessible(parent));
|
|
|
|
if (acc) {
|
2007-03-22 10:30:00 -07:00
|
|
|
// Make the parent forget about the old document as a child
|
2009-06-18 00:37:38 -07:00
|
|
|
acc->InvalidateChildren();
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2009-06-18 00:37:38 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (sameTypeRoot != treeItem) {
|
2008-02-26 00:51:10 -08:00
|
|
|
// Fire show/hide events to indicate frame/iframe content is new, rather than
|
|
|
|
// doc load event which causes screen readers to act is if entire page is reloaded
|
|
|
|
InvalidateCacheSubtree(nsnull, nsIAccessibleEvent::EVENT_DOM_SIGNIFICANT_CHANGE);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2008-02-26 06:19:52 -08:00
|
|
|
// Fire STATE_CHANGE event for doc load finish if focus is in same doc tree
|
|
|
|
if (gLastFocusedNode) {
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> focusedTreeItem =
|
2008-10-15 18:52:58 -07:00
|
|
|
nsCoreUtils::GetDocShellTreeItemFor(gLastFocusedNode);
|
2008-02-26 06:19:52 -08:00
|
|
|
if (focusedTreeItem) {
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> sameTypeRootOfFocus;
|
|
|
|
focusedTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRootOfFocus));
|
|
|
|
if (sameTypeRoot == sameTypeRootOfFocus) {
|
|
|
|
nsCOMPtr<nsIAccessibleStateChangeEvent> accEvent =
|
|
|
|
new nsAccStateChangeEvent(this, nsIAccessibleStates::STATE_BUSY, PR_FALSE, PR_FALSE);
|
|
|
|
FireAccessibleEvent(accEvent);
|
|
|
|
FireAnchorJumpEvent();
|
|
|
|
}
|
2008-02-26 00:51:10 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-05-03 22:08:31 -07:00
|
|
|
|
2008-02-26 00:51:10 -08:00
|
|
|
if (sameTypeRoot == treeItem) {
|
|
|
|
// Not a frame or iframe
|
2008-05-03 22:08:31 -07:00
|
|
|
if (!isFinished) {
|
|
|
|
// Fire state change event to set STATE_BUSY when document is loading. For
|
|
|
|
// example, Window-Eyes expects to get it.
|
|
|
|
nsCOMPtr<nsIAccessibleStateChangeEvent> accEvent =
|
|
|
|
new nsAccStateChangeEvent(this, nsIAccessibleStates::STATE_BUSY,
|
|
|
|
PR_FALSE, PR_TRUE);
|
|
|
|
FireAccessibleEvent(accEvent);
|
|
|
|
}
|
|
|
|
|
2008-10-16 02:12:05 -07:00
|
|
|
nsAccUtils::FireAccEvent(aEventType, this);
|
2008-02-26 00:51:10 -08:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void nsDocAccessible::ScrollTimerCallback(nsITimer *aTimer, void *aClosure)
|
|
|
|
{
|
2007-07-08 00:08:04 -07:00
|
|
|
nsDocAccessible *docAcc = reinterpret_cast<nsDocAccessible*>(aClosure);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
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
|
2008-10-16 02:12:05 -07:00
|
|
|
nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_SCROLLING_END, docAcc);
|
2007-07-05 09:02:55 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
docAcc->mScrollPositionChangedTicks = 0;
|
|
|
|
if (docAcc->mScrollWatchTimer) {
|
|
|
|
docAcc->mScrollWatchTimer->Cancel();
|
|
|
|
docAcc->mScrollWatchTimer = nsnull;
|
2008-01-18 12:36:44 -08:00
|
|
|
NS_RELEASE(docAcc); // Release kung fu death grip
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void nsDocAccessible::AddScrollListener()
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell));
|
|
|
|
|
|
|
|
nsIViewManager* vm = nsnull;
|
|
|
|
if (presShell)
|
|
|
|
vm = presShell->GetViewManager();
|
|
|
|
|
|
|
|
nsIScrollableView* scrollableView = nsnull;
|
|
|
|
if (vm)
|
|
|
|
vm->GetRootScrollableView(&scrollableView);
|
|
|
|
|
|
|
|
if (scrollableView)
|
|
|
|
scrollableView->AddScrollPositionListener(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void nsDocAccessible::RemoveScrollListener()
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell));
|
|
|
|
|
|
|
|
nsIViewManager* vm = nsnull;
|
|
|
|
if (presShell)
|
|
|
|
vm = presShell->GetViewManager();
|
|
|
|
|
|
|
|
nsIScrollableView* scrollableView = nsnull;
|
|
|
|
if (vm)
|
|
|
|
vm->GetRootScrollableView(&scrollableView);
|
|
|
|
|
|
|
|
if (scrollableView)
|
|
|
|
scrollableView->RemoveScrollPositionListener(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2008-01-18 12:36:44 -08:00
|
|
|
NS_ADDREF_THIS(); // Kung fu death grip
|
2007-03-22 10:30:00 -07:00
|
|
|
mScrollWatchTimer->InitWithFuncCallback(ScrollTimerCallback, this,
|
|
|
|
kScrollPosCheckWait,
|
|
|
|
nsITimer::TYPE_REPEATING_SLACK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mScrollPositionChangedTicks = 1;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsDocAccessible::Observe(nsISupports *aSubject, const char *aTopic,
|
|
|
|
const PRUnichar *aData)
|
|
|
|
{
|
2007-08-14 09:25:24 -07:00
|
|
|
if (!nsCRT::strcmp(aTopic,"obs_documentCreated")) {
|
|
|
|
// State editable will now be set, readonly is now clear
|
|
|
|
nsCOMPtr<nsIAccessibleStateChangeEvent> event =
|
|
|
|
new nsAccStateChangeEvent(this, nsIAccessibleStates::EXT_STATE_EDITABLE,
|
|
|
|
PR_TRUE, PR_TRUE);
|
|
|
|
FireAccessibleEvent(event);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2007-09-24 18:19:03 -07:00
|
|
|
///////////////////////////////////////////////////////////////////////
|
2007-03-22 10:30:00 -07:00
|
|
|
// nsIDocumentObserver
|
|
|
|
|
|
|
|
NS_IMPL_NSIDOCUMENTOBSERVER_CORE_STUB(nsDocAccessible)
|
|
|
|
NS_IMPL_NSIDOCUMENTOBSERVER_LOAD_STUB(nsDocAccessible)
|
|
|
|
NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(nsDocAccessible)
|
|
|
|
|
2009-06-29 11:36:25 -07:00
|
|
|
void
|
|
|
|
nsDocAccessible::AttributeWillChange(nsIDocument *aDocument,
|
|
|
|
nsIContent* aContent, PRInt32 aNameSpaceID,
|
|
|
|
nsIAtom* aAttribute, PRInt32 aModType)
|
|
|
|
{
|
|
|
|
// XXX TODO: bugs 381599 467143 472142 472143
|
|
|
|
// Here we will want to cache whatever state we are potentially interested in,
|
|
|
|
// such as the existence of aria-pressed for button (so we know if we need to
|
|
|
|
// newly expose it as a toggle button) etc.
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
void
|
|
|
|
nsDocAccessible::AttributeChanged(nsIDocument *aDocument, nsIContent* aContent,
|
|
|
|
PRInt32 aNameSpaceID, nsIAtom* aAttribute,
|
2007-07-12 13:05:45 -07:00
|
|
|
PRInt32 aModType, PRUint32 aStateMask)
|
2007-09-18 14:36:41 -07:00
|
|
|
{
|
|
|
|
AttributeChangedImpl(aContent, aNameSpaceID, aAttribute);
|
|
|
|
|
|
|
|
// If it was the focused node, cache the new state
|
|
|
|
nsCOMPtr<nsIDOMNode> targetNode = do_QueryInterface(aContent);
|
|
|
|
if (targetNode == gLastFocusedNode) {
|
|
|
|
nsCOMPtr<nsIAccessible> focusedAccessible;
|
|
|
|
GetAccService()->GetAccessibleFor(targetNode, getter_AddRefs(focusedAccessible));
|
|
|
|
if (focusedAccessible) {
|
2008-10-17 03:10:43 -07:00
|
|
|
gLastFocusedAccessiblesState = nsAccUtils::State(focusedAccessible);
|
2007-09-18 14:36:41 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
nsDocAccessible::AttributeChangedImpl(nsIContent* aContent, PRInt32 aNameSpaceID, nsIAtom* aAttribute)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-04-16 21:45:42 -07:00
|
|
|
// Fire accessible event after short timer, because we need to wait for
|
|
|
|
// DOM attribute & resulting layout to actually change. Otherwise,
|
|
|
|
// assistive technology will retrieve the wrong state/value/selection info.
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// XXX todo
|
|
|
|
// We still need to handle special HTML 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
|
2009-01-12 09:20:34 -08:00
|
|
|
//
|
|
|
|
// XXX todo: report aria state changes for "undefined" literal value changes
|
|
|
|
// filed as bug 472142
|
|
|
|
//
|
|
|
|
// XXX todo: invalidate accessible when aria state changes affect exposed role
|
|
|
|
// filed as bug 472143
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsISupports> container = mDocument->GetContainer();
|
|
|
|
nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container);
|
|
|
|
if (!docShell) {
|
|
|
|
return;
|
|
|
|
}
|
2007-09-24 18:19:03 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
PRUint32 busyFlags;
|
|
|
|
docShell->GetBusyFlags(&busyFlags);
|
|
|
|
if (busyFlags) {
|
|
|
|
return; // Still loading, ignore setting of initial attributes
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIPresShell> shell = GetPresShell();
|
|
|
|
if (!shell) {
|
|
|
|
return; // Document has been shut down
|
|
|
|
}
|
|
|
|
|
2007-04-19 09:15:04 -07:00
|
|
|
nsCOMPtr<nsIDOMNode> targetNode(do_QueryInterface(aContent));
|
|
|
|
NS_ASSERTION(targetNode, "No node for attr modified");
|
2008-10-17 03:10:43 -07:00
|
|
|
if (!targetNode || !nsAccUtils::IsNodeRelevant(targetNode))
|
2007-04-19 06:16:23 -07:00
|
|
|
return;
|
|
|
|
|
2007-08-10 18:44:44 -07:00
|
|
|
// Since we're in synchronous code, we can store whether the current attribute
|
|
|
|
// change is from user input or not. If the attribute change causes an asynchronous
|
|
|
|
// layout change, that event can use the last known user input state
|
|
|
|
nsAccEvent::PrepareForEvent(targetNode);
|
|
|
|
|
2007-04-19 10:49:13 -07:00
|
|
|
// Universal boolean properties that don't require a role.
|
2007-09-24 18:19:03 -07:00
|
|
|
if (aAttribute == nsAccessibilityAtoms::disabled ||
|
2007-12-11 18:10:26 -08:00
|
|
|
aAttribute == nsAccessibilityAtoms::aria_disabled) {
|
2007-04-19 10:49:13 -07:00
|
|
|
// Fire the state change whether disabled attribute is
|
|
|
|
// set for XUL, HTML or ARIA namespace.
|
|
|
|
// Checking the namespace would not seem to gain us anything, because
|
|
|
|
// disabled really is going to mean the same thing in any namespace.
|
2007-07-13 10:19:40 -07:00
|
|
|
// We use the attribute instead of the disabled state bit because
|
2007-12-11 18:10:26 -08:00
|
|
|
// ARIA's aria-disabled does not affect the disabled state bit
|
2007-04-19 10:49:13 -07:00
|
|
|
nsCOMPtr<nsIAccessibleStateChangeEvent> enabledChangeEvent =
|
|
|
|
new nsAccStateChangeEvent(targetNode,
|
|
|
|
nsIAccessibleStates::EXT_STATE_ENABLED,
|
|
|
|
PR_TRUE);
|
|
|
|
FireDelayedAccessibleEvent(enabledChangeEvent);
|
|
|
|
nsCOMPtr<nsIAccessibleStateChangeEvent> sensitiveChangeEvent =
|
|
|
|
new nsAccStateChangeEvent(targetNode,
|
|
|
|
nsIAccessibleStates::EXT_STATE_SENSITIVE,
|
|
|
|
PR_TRUE);
|
|
|
|
FireDelayedAccessibleEvent(sensitiveChangeEvent);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-09-24 18:19:03 -07:00
|
|
|
// Check for namespaced ARIA attribute
|
2007-12-11 18:10:26 -08:00
|
|
|
if (aNameSpaceID == kNameSpaceID_None) {
|
2007-09-24 18:19:03 -07:00
|
|
|
// Check for hyphenated aria-foo property?
|
|
|
|
const char* attributeName;
|
|
|
|
aAttribute->GetUTF8String(&attributeName);
|
|
|
|
if (!PL_strncmp("aria-", attributeName, 5)) {
|
2007-12-11 18:10:26 -08:00
|
|
|
ARIAAttributeChanged(aContent, aAttribute);
|
2007-09-24 18:19:03 -07:00
|
|
|
}
|
|
|
|
}
|
2007-04-19 10:49:13 -07:00
|
|
|
|
2007-09-28 11:16:05 -07:00
|
|
|
if (aAttribute == nsAccessibilityAtoms::role ||
|
|
|
|
aAttribute == nsAccessibilityAtoms::href ||
|
2009-04-09 08:36:25 -07:00
|
|
|
aAttribute == nsAccessibilityAtoms::onclick) {
|
2007-09-28 11:16:05 -07:00
|
|
|
// Not worth the expense to ensure which namespace these are in
|
|
|
|
// It doesn't kill use to recreate the accessible even if the attribute was used
|
|
|
|
// in the wrong namespace or an element that doesn't support it
|
2007-08-10 18:44:44 -07:00
|
|
|
InvalidateCacheSubtree(aContent, nsIAccessibleEvent::EVENT_DOM_SIGNIFICANT_CHANGE);
|
2007-03-22 10:30:00 -07:00
|
|
|
return;
|
|
|
|
}
|
2008-02-19 23:45:14 -08:00
|
|
|
|
|
|
|
if (aAttribute == nsAccessibilityAtoms::alt ||
|
2009-05-13 22:27:40 -07:00
|
|
|
aAttribute == nsAccessibilityAtoms::title ||
|
|
|
|
aAttribute == nsAccessibilityAtoms::aria_label ||
|
|
|
|
aAttribute == nsAccessibilityAtoms::aria_labelledby) {
|
2008-02-19 23:45:14 -08:00
|
|
|
FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE,
|
2008-03-17 01:13:10 -07:00
|
|
|
targetNode);
|
2008-02-19 23:45:14 -08:00
|
|
|
return;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-12-11 18:10:26 -08:00
|
|
|
if (aAttribute == nsAccessibilityAtoms::selected ||
|
|
|
|
aAttribute == nsAccessibilityAtoms::aria_selected) {
|
|
|
|
// ARIA or XUL selection
|
2008-10-17 03:10:43 -07:00
|
|
|
nsCOMPtr<nsIAccessible> multiSelect =
|
|
|
|
nsAccUtils::GetMultiSelectFor(targetNode);
|
2007-03-22 10:30:00 -07:00
|
|
|
// Multi selects use selection_add and selection_remove
|
|
|
|
// Single select widgets just mirror event_selection for
|
|
|
|
// whatever gets event_focus, which is done in
|
|
|
|
// nsRootAccessible::FireAccessibleFocusEvent()
|
|
|
|
// So right here we make sure only to deal with multi selects
|
|
|
|
if (multiSelect) {
|
|
|
|
// Need to find the right event to use here, SELECTION_WITHIN would
|
|
|
|
// seem right but we had started using it for something else
|
|
|
|
nsCOMPtr<nsIAccessNode> multiSelectAccessNode =
|
|
|
|
do_QueryInterface(multiSelect);
|
|
|
|
nsCOMPtr<nsIDOMNode> multiSelectDOMNode;
|
|
|
|
multiSelectAccessNode->GetDOMNode(getter_AddRefs(multiSelectDOMNode));
|
|
|
|
NS_ASSERTION(multiSelectDOMNode, "A new accessible without a DOM node!");
|
|
|
|
FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
|
2008-03-17 01:13:10 -07:00
|
|
|
multiSelectDOMNode,
|
|
|
|
nsAccEvent::eAllowDupes);
|
2007-04-16 21:45:42 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
static nsIContent::AttrValuesArray strings[] =
|
|
|
|
{&nsAccessibilityAtoms::_empty, &nsAccessibilityAtoms::_false, nsnull};
|
2007-12-11 18:10:26 -08:00
|
|
|
if (aContent->FindAttrValueIn(kNameSpaceID_None, aAttribute,
|
|
|
|
strings, eCaseMatters) >= 0) {
|
2007-04-16 21:45:42 -07:00
|
|
|
FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_SELECTION_REMOVE,
|
2008-01-17 18:56:38 -08:00
|
|
|
targetNode);
|
2007-04-16 21:45:42 -07:00
|
|
|
return;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-04-16 21:45:42 -07:00
|
|
|
|
|
|
|
FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_SELECTION_ADD,
|
2008-01-17 18:56:38 -08:00
|
|
|
targetNode);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
2007-08-14 09:25:24 -07:00
|
|
|
|
|
|
|
if (aAttribute == nsAccessibilityAtoms::contenteditable) {
|
|
|
|
nsCOMPtr<nsIAccessibleStateChangeEvent> editableChangeEvent =
|
|
|
|
new nsAccStateChangeEvent(targetNode,
|
|
|
|
nsIAccessibleStates::EXT_STATE_EDITABLE,
|
|
|
|
PR_TRUE);
|
|
|
|
FireDelayedAccessibleEvent(editableChangeEvent);
|
|
|
|
return;
|
|
|
|
}
|
2007-04-16 21:45:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsDocAccessible::ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNode> targetNode(do_QueryInterface(aContent));
|
|
|
|
if (!targetNode)
|
|
|
|
return;
|
|
|
|
|
2007-12-11 18:10:26 -08:00
|
|
|
if (aAttribute == nsAccessibilityAtoms::aria_required) {
|
2007-04-16 21:45:42 -07:00
|
|
|
nsCOMPtr<nsIAccessibleStateChangeEvent> event =
|
|
|
|
new nsAccStateChangeEvent(targetNode,
|
|
|
|
nsIAccessibleStates::STATE_REQUIRED,
|
|
|
|
PR_FALSE);
|
|
|
|
FireDelayedAccessibleEvent(event);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-12-11 18:10:26 -08:00
|
|
|
if (aAttribute == nsAccessibilityAtoms::aria_invalid) {
|
2007-04-16 21:45:42 -07:00
|
|
|
nsCOMPtr<nsIAccessibleStateChangeEvent> event =
|
|
|
|
new nsAccStateChangeEvent(targetNode,
|
|
|
|
nsIAccessibleStates::STATE_INVALID,
|
|
|
|
PR_FALSE);
|
|
|
|
FireDelayedAccessibleEvent(event);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-12-11 18:10:26 -08:00
|
|
|
if (aAttribute == nsAccessibilityAtoms::aria_activedescendant) {
|
2007-04-16 21:45:42 -07:00
|
|
|
// The activedescendant universal property redirects accessible focus events
|
|
|
|
// to the element with the id that activedescendant points to
|
|
|
|
nsCOMPtr<nsIDOMNode> currentFocus = GetCurrentFocus();
|
2008-10-17 03:10:43 -07:00
|
|
|
if (SameCOMIdentity(nsCoreUtils::GetRoleContent(currentFocus), targetNode)) {
|
2007-04-16 21:45:42 -07:00
|
|
|
nsRefPtr<nsRootAccessible> rootAcc = GetRootAccessible();
|
|
|
|
if (rootAcc)
|
|
|
|
rootAcc->FireAccessibleFocusEvent(nsnull, currentFocus, nsnull, PR_TRUE);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-04-16 21:45:42 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-12-11 18:10:26 -08:00
|
|
|
if (!aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::role)) {
|
2007-04-16 21:45:42 -07:00
|
|
|
// We don't care about these other ARIA attribute changes unless there is
|
|
|
|
// an ARIA role set for the element
|
|
|
|
// XXX: we should check the role map to see if the changed property is
|
|
|
|
// relevant for that particular role.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-12-11 18:10:26 -08:00
|
|
|
// The following ARIA attributes only take affect when dynamic content role is present
|
|
|
|
if (aAttribute == nsAccessibilityAtoms::aria_checked ||
|
|
|
|
aAttribute == nsAccessibilityAtoms::aria_pressed) {
|
|
|
|
const PRUint32 kState = (aAttribute == nsAccessibilityAtoms::aria_checked) ?
|
2007-09-18 14:36:41 -07:00
|
|
|
nsIAccessibleStates::STATE_CHECKED :
|
|
|
|
nsIAccessibleStates::STATE_PRESSED;
|
2007-08-14 09:15:12 -07:00
|
|
|
nsCOMPtr<nsIAccessibleStateChangeEvent> event =
|
2007-09-18 14:36:41 -07:00
|
|
|
new nsAccStateChangeEvent(targetNode, kState, PR_FALSE);
|
2007-08-14 09:15:12 -07:00
|
|
|
FireDelayedAccessibleEvent(event);
|
2007-09-18 14:36:41 -07:00
|
|
|
if (targetNode == gLastFocusedNode) {
|
|
|
|
// State changes for MIXED state currently only supported for focused item, because
|
|
|
|
// otherwise we would need access to the old attribute value in this listener.
|
2007-12-11 18:10:26 -08:00
|
|
|
// This is because we don't know if the previous value of aria-checked or aria-pressed was "mixed"
|
2007-09-18 14:36:41 -07:00
|
|
|
// without caching that info.
|
|
|
|
nsCOMPtr<nsIAccessible> accessible;
|
|
|
|
event->GetAccessible(getter_AddRefs(accessible));
|
|
|
|
if (accessible) {
|
|
|
|
PRBool wasMixed = (gLastFocusedAccessiblesState & nsIAccessibleStates::STATE_MIXED) != 0;
|
2008-10-17 03:10:43 -07:00
|
|
|
PRBool isMixed =
|
|
|
|
(nsAccUtils::State(accessible) & nsIAccessibleStates::STATE_MIXED) != 0;
|
2007-09-18 14:36:41 -07:00
|
|
|
if (wasMixed != isMixed) {
|
|
|
|
nsCOMPtr<nsIAccessibleStateChangeEvent> event =
|
|
|
|
new nsAccStateChangeEvent(targetNode,
|
|
|
|
nsIAccessibleStates::STATE_MIXED,
|
|
|
|
PR_FALSE, isMixed);
|
|
|
|
FireDelayedAccessibleEvent(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-08-14 09:15:12 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-12-11 18:10:26 -08:00
|
|
|
if (aAttribute == nsAccessibilityAtoms::aria_expanded) {
|
2007-04-16 21:45:42 -07:00
|
|
|
nsCOMPtr<nsIAccessibleStateChangeEvent> event =
|
|
|
|
new nsAccStateChangeEvent(targetNode,
|
|
|
|
nsIAccessibleStates::STATE_EXPANDED,
|
|
|
|
PR_FALSE);
|
|
|
|
FireDelayedAccessibleEvent(event);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-12-11 18:10:26 -08:00
|
|
|
if (aAttribute == nsAccessibilityAtoms::aria_readonly) {
|
2007-04-16 21:45:42 -07:00
|
|
|
nsCOMPtr<nsIAccessibleStateChangeEvent> event =
|
|
|
|
new nsAccStateChangeEvent(targetNode,
|
|
|
|
nsIAccessibleStates::STATE_READONLY,
|
|
|
|
PR_FALSE);
|
|
|
|
FireDelayedAccessibleEvent(event);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-06-11 12:57:29 -07:00
|
|
|
// Fire value change event whenever aria-valuetext is changed, or
|
|
|
|
// when aria-valuenow is changed and aria-valuetext is empty
|
|
|
|
if (aAttribute == nsAccessibilityAtoms::aria_valuetext ||
|
|
|
|
(aAttribute == nsAccessibilityAtoms::aria_valuenow &&
|
|
|
|
(!aContent->HasAttr(kNameSpaceID_None,
|
|
|
|
nsAccessibilityAtoms::aria_valuetext) ||
|
|
|
|
aContent->AttrValueIs(kNameSpaceID_None,
|
|
|
|
nsAccessibilityAtoms::aria_valuetext, nsAccessibilityAtoms::_empty,
|
|
|
|
eCaseMatters)))) {
|
|
|
|
FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, targetNode);
|
2007-04-16 21:45:42 -07:00
|
|
|
return;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-12-11 18:10:26 -08:00
|
|
|
if (aAttribute == nsAccessibilityAtoms::aria_multiselectable &&
|
|
|
|
aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::role)) {
|
2007-04-16 21:45:42 -07:00
|
|
|
// This affects whether the accessible supports nsIAccessibleSelectable.
|
|
|
|
// COM says we cannot change what interfaces are supported on-the-fly,
|
|
|
|
// so invalidate this object. A new one will be created on demand.
|
2007-12-11 18:10:26 -08:00
|
|
|
InvalidateCacheSubtree(aContent, nsIAccessibleEvent::EVENT_DOM_SIGNIFICANT_CHANGE);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void nsDocAccessible::ContentAppended(nsIDocument *aDocument,
|
|
|
|
nsIContent* aContainer,
|
|
|
|
PRInt32 aNewIndexInContainer)
|
|
|
|
{
|
2007-08-30 11:48:24 -07:00
|
|
|
if ((!mIsContentLoaded || !mDocument) && mAccessNodeCache.Count() <= 1) {
|
|
|
|
// See comments in nsDocAccessible::InvalidateCacheSubtree
|
|
|
|
InvalidateChildren();
|
2007-08-05 20:19:37 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
PRUint32 childCount = aContainer->GetChildCount();
|
|
|
|
for (PRUint32 index = aNewIndexInContainer; index < childCount; index ++) {
|
2007-07-25 04:54:15 -07:00
|
|
|
nsCOMPtr<nsIContent> child(aContainer->GetChildAt(index));
|
|
|
|
// InvalidateCacheSubtree will not fire the EVENT_SHOW for the new node
|
|
|
|
// unless an accessible can be created for the passed in node, which it
|
|
|
|
// can't do unless the node is visible. The right thing happens there so
|
|
|
|
// no need for an extra visibility check here.
|
2007-08-10 18:44:44 -07:00
|
|
|
InvalidateCacheSubtree(child, nsIAccessibleEvent::EVENT_DOM_CREATE);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void nsDocAccessible::ContentStatesChanged(nsIDocument* aDocument,
|
|
|
|
nsIContent* aContent1,
|
|
|
|
nsIContent* aContent2,
|
|
|
|
PRInt32 aStateMask)
|
|
|
|
{
|
|
|
|
if (0 == (aStateMask & NS_EVENT_STATE_CHECKED)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent1);
|
|
|
|
nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent2);
|
|
|
|
}
|
|
|
|
|
2007-09-05 01:22:17 -07:00
|
|
|
void nsDocAccessible::CharacterDataWillChange(nsIDocument *aDocument,
|
|
|
|
nsIContent* aContent,
|
|
|
|
CharacterDataChangeInfo* aInfo)
|
|
|
|
{
|
|
|
|
FireTextChangeEventForText(aContent, aInfo, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
void nsDocAccessible::CharacterDataChanged(nsIDocument *aDocument,
|
|
|
|
nsIContent* aContent,
|
|
|
|
CharacterDataChangeInfo* aInfo)
|
|
|
|
{
|
2007-09-05 01:22:17 -07:00
|
|
|
FireTextChangeEventForText(aContent, aInfo, PR_TRUE);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsDocAccessible::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer,
|
|
|
|
nsIContent* aChild, PRInt32 aIndexInContainer)
|
|
|
|
{
|
|
|
|
// InvalidateCacheSubtree will not fire the EVENT_SHOW for the new node
|
|
|
|
// unless an accessible can be created for the passed in node, which it
|
|
|
|
// can't do unless the node is visible. The right thing happens there so
|
|
|
|
// no need for an extra visibility check here.
|
2007-08-10 18:44:44 -07:00
|
|
|
InvalidateCacheSubtree(aChild, nsIAccessibleEvent::EVENT_DOM_CREATE);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsDocAccessible::ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer,
|
|
|
|
nsIContent* aChild, PRInt32 aIndexInContainer)
|
|
|
|
{
|
2007-07-25 04:54:15 -07:00
|
|
|
// Invalidate the subtree of the removed element.
|
2007-08-28 14:57:53 -07:00
|
|
|
// InvalidateCacheSubtree(aChild, nsIAccessibleEvent::EVENT_DOM_DESTROY);
|
|
|
|
// This is no longer needed, we get our notifications directly from content
|
|
|
|
// *before* the frame for the content is destroyed, or any other side effects occur.
|
|
|
|
// That allows us to correctly calculate the TEXT_REMOVED event if there is one.
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsDocAccessible::ParentChainChanged(nsIContent *aContent)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2008-03-19 18:58:11 -07:00
|
|
|
void
|
|
|
|
nsDocAccessible::FireValueChangeForTextFields(nsIAccessible *aPossibleTextFieldAccessible)
|
|
|
|
{
|
2008-10-17 03:10:43 -07:00
|
|
|
if (nsAccUtils::Role(aPossibleTextFieldAccessible) != nsIAccessibleRole::ROLE_ENTRY)
|
2008-03-19 18:58:11 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Dependent value change event for text changes in textfields
|
|
|
|
nsCOMPtr<nsIAccessibleEvent> valueChangeEvent =
|
|
|
|
new nsAccEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aPossibleTextFieldAccessible,
|
|
|
|
PR_FALSE, nsAccEvent::eRemoveDupes);
|
|
|
|
FireDelayedAccessibleEvent(valueChangeEvent );
|
|
|
|
}
|
|
|
|
|
2007-07-25 04:54:15 -07:00
|
|
|
void
|
2007-09-05 01:22:17 -07:00
|
|
|
nsDocAccessible::FireTextChangeEventForText(nsIContent *aContent,
|
|
|
|
CharacterDataChangeInfo* aInfo,
|
|
|
|
PRBool aIsInserted)
|
2007-07-25 04:54:15 -07:00
|
|
|
{
|
2007-08-05 20:19:37 -07:00
|
|
|
if (!mIsContentLoaded || !mDocument) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-07-25 04:54:15 -07:00
|
|
|
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aContent));
|
|
|
|
if (!node)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIAccessible> accessible;
|
2007-10-01 11:27:13 -07:00
|
|
|
nsresult rv = GetAccessibleInParentChain(node, PR_TRUE, getter_AddRefs(accessible));
|
2007-08-17 11:25:13 -07:00
|
|
|
if (NS_FAILED(rv) || !accessible)
|
2007-07-25 04:54:15 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
nsRefPtr<nsHyperTextAccessible> textAccessible;
|
|
|
|
rv = accessible->QueryInterface(NS_GET_IID(nsHyperTextAccessible),
|
|
|
|
getter_AddRefs(textAccessible));
|
|
|
|
if (NS_FAILED(rv) || !textAccessible)
|
|
|
|
return;
|
|
|
|
|
|
|
|
PRInt32 start = aInfo->mChangeStart;
|
|
|
|
|
|
|
|
PRInt32 offset = 0;
|
2007-08-03 18:12:24 -07:00
|
|
|
rv = textAccessible->DOMPointToHypertextOffset(node, start, &offset);
|
2007-07-25 04:54:15 -07:00
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return;
|
|
|
|
|
2007-09-05 01:22:17 -07:00
|
|
|
PRInt32 length = aIsInserted ?
|
2007-09-05 01:42:59 -07:00
|
|
|
aInfo->mReplaceLength: // text has been added
|
|
|
|
aInfo->mChangeEnd - start; // text has been removed
|
2007-09-05 01:22:17 -07:00
|
|
|
|
2007-07-25 04:54:15 -07:00
|
|
|
if (length > 0) {
|
2007-09-05 01:22:17 -07:00
|
|
|
nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mWeakShell));
|
|
|
|
if (!shell)
|
|
|
|
return;
|
|
|
|
|
|
|
|
PRUint32 renderedStartOffset, renderedEndOffset;
|
|
|
|
nsIFrame* frame = shell->GetPrimaryFrameFor(aContent);
|
2007-11-27 10:12:15 -08:00
|
|
|
if (!frame)
|
|
|
|
return;
|
2007-09-05 01:22:17 -07:00
|
|
|
|
|
|
|
rv = textAccessible->ContentToRenderedOffset(frame, start,
|
|
|
|
&renderedStartOffset);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return;
|
|
|
|
|
|
|
|
rv = textAccessible->ContentToRenderedOffset(frame, start + length,
|
|
|
|
&renderedEndOffset);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return;
|
2007-07-25 04:54:15 -07:00
|
|
|
|
|
|
|
nsCOMPtr<nsIAccessibleTextChangeEvent> event =
|
2007-09-05 01:22:17 -07:00
|
|
|
new nsAccTextChangeEvent(accessible, offset,
|
|
|
|
renderedEndOffset - renderedStartOffset,
|
|
|
|
aIsInserted, PR_FALSE);
|
2007-07-25 04:54:15 -07:00
|
|
|
textAccessible->FireAccessibleEvent(event);
|
2008-03-19 18:58:11 -07:00
|
|
|
|
|
|
|
FireValueChangeForTextFields(accessible);
|
2007-07-25 04:54:15 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-08-28 14:57:53 -07:00
|
|
|
already_AddRefed<nsIAccessibleTextChangeEvent>
|
|
|
|
nsDocAccessible::CreateTextChangeEventForNode(nsIAccessible *aContainerAccessible,
|
|
|
|
nsIDOMNode *aChangeNode,
|
|
|
|
nsIAccessible *aAccessibleForChangeNode,
|
|
|
|
PRBool aIsInserting,
|
|
|
|
PRBool aIsAsynch)
|
2007-07-25 04:54:15 -07:00
|
|
|
{
|
|
|
|
nsRefPtr<nsHyperTextAccessible> textAccessible;
|
2007-08-28 14:57:53 -07:00
|
|
|
aContainerAccessible->QueryInterface(NS_GET_IID(nsHyperTextAccessible),
|
|
|
|
getter_AddRefs(textAccessible));
|
|
|
|
if (!textAccessible) {
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 offset;
|
|
|
|
PRInt32 length = 0;
|
|
|
|
nsCOMPtr<nsIAccessible> changeAccessible;
|
|
|
|
nsresult rv = textAccessible->DOMPointToHypertextOffset(aChangeNode, -1, &offset,
|
|
|
|
getter_AddRefs(changeAccessible));
|
|
|
|
NS_ENSURE_SUCCESS(rv, nsnull);
|
|
|
|
|
|
|
|
if (!aAccessibleForChangeNode) {
|
|
|
|
// A span-level object or something else without an accessible is being removed, where
|
|
|
|
// it has no accessible but it has descendant content which is aggregated as text
|
|
|
|
// into the parent hypertext.
|
|
|
|
// In this case, accessibleToBeRemoved may just be the first
|
|
|
|
// accessible that is removed, which affects the text in the hypertext container
|
|
|
|
if (!changeAccessible) {
|
|
|
|
return nsnull; // No descendant content that represents any text in the hypertext parent
|
|
|
|
}
|
|
|
|
nsCOMPtr<nsIAccessible> child = changeAccessible;
|
|
|
|
while (PR_TRUE) {
|
|
|
|
nsCOMPtr<nsIAccessNode> childAccessNode =
|
|
|
|
do_QueryInterface(changeAccessible);
|
|
|
|
nsCOMPtr<nsIDOMNode> childNode;
|
|
|
|
childAccessNode->GetDOMNode(getter_AddRefs(childNode));
|
2008-10-15 18:52:58 -07:00
|
|
|
if (!nsCoreUtils::IsAncestorOf(aChangeNode, childNode)) {
|
2007-08-28 14:57:53 -07:00
|
|
|
break; // We only want accessibles with DOM nodes as children of this node
|
|
|
|
}
|
2008-10-17 03:10:43 -07:00
|
|
|
length += nsAccUtils::TextLength(child);
|
2007-08-28 14:57:53 -07:00
|
|
|
child->GetNextSibling(getter_AddRefs(changeAccessible));
|
|
|
|
if (!changeAccessible) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
child.swap(changeAccessible);
|
2007-07-25 04:54:15 -07:00
|
|
|
}
|
|
|
|
}
|
2007-08-28 14:57:53 -07:00
|
|
|
else {
|
2007-09-24 20:40:11 -07:00
|
|
|
NS_ASSERTION(!changeAccessible || changeAccessible == aAccessibleForChangeNode,
|
2007-08-28 14:57:53 -07:00
|
|
|
"Hypertext is reporting a different accessible for this node");
|
2008-10-17 03:10:43 -07:00
|
|
|
|
|
|
|
length = nsAccUtils::TextLength(aAccessibleForChangeNode);
|
|
|
|
if (nsAccUtils::Role(aAccessibleForChangeNode) == nsIAccessibleRole::ROLE_WHITESPACE) { // newline
|
2007-08-28 14:57:53 -07:00
|
|
|
// Don't fire event for the first html:br in an editor.
|
|
|
|
nsCOMPtr<nsIEditor> editor;
|
|
|
|
textAccessible->GetAssociatedEditor(getter_AddRefs(editor));
|
|
|
|
if (editor) {
|
|
|
|
PRBool isEmpty = PR_FALSE;
|
|
|
|
editor->GetDocumentIsEmpty(&isEmpty);
|
|
|
|
if (isEmpty) {
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
}
|
2007-07-25 04:54:15 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-08-28 14:57:53 -07:00
|
|
|
if (length <= 0) {
|
|
|
|
return nsnull;
|
|
|
|
}
|
2007-07-25 04:54:15 -07:00
|
|
|
|
2007-08-28 14:57:53 -07:00
|
|
|
nsIAccessibleTextChangeEvent *event =
|
|
|
|
new nsAccTextChangeEvent(aContainerAccessible, offset, length, aIsInserting, aIsAsynch);
|
|
|
|
NS_IF_ADDREF(event);
|
2007-07-25 04:54:15 -07:00
|
|
|
|
2007-08-28 14:57:53 -07:00
|
|
|
return event;
|
2007-07-25 04:54:15 -07:00
|
|
|
}
|
2007-08-28 14:57:53 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsresult nsDocAccessible::FireDelayedToolkitEvent(PRUint32 aEvent,
|
|
|
|
nsIDOMNode *aDOMNode,
|
2008-03-17 01:13:10 -07:00
|
|
|
nsAccEvent::EEventRule aAllowDupes,
|
2007-08-10 18:44:44 -07:00
|
|
|
PRBool aIsAsynch)
|
2007-04-16 21:45:42 -07:00
|
|
|
{
|
2007-08-10 18:44:44 -07:00
|
|
|
nsCOMPtr<nsIAccessibleEvent> event =
|
2008-03-17 01:13:10 -07:00
|
|
|
new nsAccEvent(aEvent, aDOMNode, aIsAsynch, aAllowDupes);
|
2007-04-16 21:45:42 -07:00
|
|
|
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
|
2008-03-17 01:13:10 -07:00
|
|
|
return FireDelayedAccessibleEvent(event);
|
2007-04-16 21:45:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
2008-03-17 01:13:10 -07:00
|
|
|
nsDocAccessible::FireDelayedAccessibleEvent(nsIAccessibleEvent *aEvent)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-08-20 20:16:27 -07:00
|
|
|
NS_ENSURE_TRUE(aEvent, NS_ERROR_FAILURE);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!mFireEventTimer) {
|
|
|
|
// Do not yet have a timer going for firing another event.
|
|
|
|
mFireEventTimer = do_CreateInstance("@mozilla.org/timer;1");
|
|
|
|
NS_ENSURE_TRUE(mFireEventTimer, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
}
|
2007-04-16 21:45:42 -07:00
|
|
|
|
|
|
|
mEventsToFire.AppendObject(aEvent);
|
2008-03-17 01:13:10 -07:00
|
|
|
if (mEventsToFire.Count() == 1) {
|
2007-03-22 10:30:00 -07:00
|
|
|
// This is be the first delayed event in queue, start timer
|
|
|
|
// so that event gets fired via FlushEventsCallback
|
2007-12-26 21:13:40 -08:00
|
|
|
NS_ADDREF_THIS(); // Kung fu death grip to prevent crash in callback
|
2007-03-22 10:30:00 -07:00
|
|
|
mFireEventTimer->InitWithFuncCallback(FlushEventsCallback,
|
2009-06-24 19:08:53 -07:00
|
|
|
this, 0, nsITimer::TYPE_ONE_SHOT);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-04-16 21:45:42 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2009-06-24 19:08:53 -07:00
|
|
|
void
|
|
|
|
nsDocAccessible::FlushPendingEvents()
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-06-05 02:49:51 -07:00
|
|
|
mInFlushPendingEvents = PR_TRUE;
|
2007-03-22 10:30:00 -07:00
|
|
|
PRUint32 length = mEventsToFire.Count();
|
|
|
|
NS_ASSERTION(length, "How did we get here without events to fire?");
|
2008-02-28 09:36:37 -08:00
|
|
|
nsCOMPtr<nsIPresShell> presShell = GetPresShell();
|
|
|
|
if (!presShell)
|
|
|
|
length = 0; // The doc is now shut down, don't fire events in it anymore
|
2009-06-22 11:28:20 -07:00
|
|
|
else {
|
|
|
|
// Flush layout so that all the frame construction, reflow, and styles are
|
|
|
|
// up-to-date. This will ensure we can get frames for the related nodes, as
|
|
|
|
// well as get the most current information for calculating things like
|
|
|
|
// visibility. We don't flush the display because we don't care about
|
|
|
|
// painting. If no flush is necessary the method will simple return.
|
|
|
|
presShell->FlushPendingNotifications(Flush_Layout);
|
|
|
|
|
|
|
|
// filter events
|
2008-03-17 01:13:10 -07:00
|
|
|
nsAccEvent::ApplyEventRules(mEventsToFire);
|
2009-06-22 11:28:20 -07:00
|
|
|
}
|
2008-03-17 01:13:10 -07:00
|
|
|
|
|
|
|
for (PRUint32 index = 0; index < length; index ++) {
|
2007-04-16 21:45:42 -07:00
|
|
|
nsCOMPtr<nsIAccessibleEvent> accessibleEvent(
|
|
|
|
do_QueryInterface(mEventsToFire[index]));
|
2008-03-17 01:13:10 -07:00
|
|
|
|
|
|
|
if (nsAccEvent::EventRule(accessibleEvent) == nsAccEvent::eDoNotEmit)
|
|
|
|
continue;
|
2007-04-16 21:45:42 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsIAccessible> accessible;
|
|
|
|
accessibleEvent->GetAccessible(getter_AddRefs(accessible));
|
2008-02-08 18:28:01 -08:00
|
|
|
nsCOMPtr<nsIDOMNode> domNode;
|
|
|
|
accessibleEvent->GetDOMNode(getter_AddRefs(domNode));
|
2008-03-17 01:13:10 -07:00
|
|
|
PRUint32 eventType = nsAccEvent::EventType(accessibleEvent);
|
|
|
|
PRBool isFromUserInput = nsAccEvent::IsFromUserInput(accessibleEvent);
|
2008-02-08 18:28:01 -08:00
|
|
|
|
|
|
|
if (domNode == gLastFocusedNode &&
|
2008-05-27 11:01:02 -07:00
|
|
|
(eventType == nsIAccessibleEvent::EVENT_ASYNCH_HIDE ||
|
|
|
|
eventType == nsIAccessibleEvent::EVENT_ASYNCH_SHOW)) {
|
2008-02-08 18:28:01 -08:00
|
|
|
// If frame type didn't change for this event, then we don't actually need to invalidate
|
|
|
|
// However, we only keep track of the old frame type for the focus, where it's very
|
|
|
|
// important not to destroy and recreate the accessible for minor style changes,
|
|
|
|
// such as a:focus { overflow: scroll; }
|
|
|
|
nsCOMPtr<nsIContent> focusContent(do_QueryInterface(domNode));
|
|
|
|
if (focusContent) {
|
|
|
|
nsIFrame *focusFrame = presShell->GetRealPrimaryFrameFor(focusContent);
|
|
|
|
nsIAtom *newFrameType =
|
|
|
|
(focusFrame && focusFrame->GetStyleVisibility()->IsVisible()) ?
|
|
|
|
focusFrame->GetType() : nsnull;
|
|
|
|
|
|
|
|
if (newFrameType == gLastFocusedFrameType) {
|
|
|
|
// Don't need to invalidate this current accessible, but can
|
|
|
|
// just invalidate the children instead
|
|
|
|
FireShowHideEvents(domNode, PR_TRUE, eventType, PR_FALSE, isFromUserInput);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
gLastFocusedFrameType = newFrameType;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-08-28 14:57:53 -07:00
|
|
|
if (eventType == nsIAccessibleEvent::EVENT_DOM_CREATE ||
|
|
|
|
eventType == nsIAccessibleEvent::EVENT_ASYNCH_SHOW) {
|
2008-08-14 01:30:26 -07:00
|
|
|
|
2007-09-28 13:55:46 -07:00
|
|
|
nsCOMPtr<nsIAccessible> containerAccessible;
|
2008-08-14 01:30:26 -07:00
|
|
|
if (accessible)
|
|
|
|
accessible->GetParent(getter_AddRefs(containerAccessible));
|
|
|
|
|
|
|
|
if (!containerAccessible) {
|
|
|
|
GetAccessibleInParentChain(domNode, PR_TRUE,
|
|
|
|
getter_AddRefs(containerAccessible));
|
|
|
|
if (!containerAccessible)
|
|
|
|
containerAccessible = this;
|
|
|
|
}
|
|
|
|
|
2008-02-02 09:02:09 -08:00
|
|
|
if (eventType == nsIAccessibleEvent::EVENT_ASYNCH_SHOW) {
|
2008-08-14 01:30:26 -07:00
|
|
|
// For asynch show, delayed invalidatation of parent's children
|
2009-06-18 00:37:38 -07:00
|
|
|
nsRefPtr<nsAccessible> containerAcc =
|
|
|
|
nsAccUtils::QueryAccessible(containerAccessible);
|
|
|
|
if (containerAcc)
|
|
|
|
containerAcc->InvalidateChildren();
|
2008-08-14 01:30:26 -07:00
|
|
|
|
2008-02-02 09:02:09 -08:00
|
|
|
// Some show events in the subtree may have been removed to
|
|
|
|
// avoid firing redundant events. But, we still need to make sure any
|
|
|
|
// accessibles parenting those shown nodes lose their child references.
|
|
|
|
InvalidateChildrenInSubtree(domNode);
|
2007-09-28 13:55:46 -07:00
|
|
|
}
|
|
|
|
|
2007-08-28 14:57:53 -07:00
|
|
|
// Also fire text changes if the node being created could affect the text in an nsIAccessibleText parent.
|
|
|
|
// When a node is being made visible or is inserted, the text in an ancestor hyper text will gain characters
|
|
|
|
// At this point we now have the frame and accessible for this node if there is one. That is why we
|
|
|
|
// wait to fire this here, instead of in InvalidateCacheSubtree(), where we wouldn't be able to calculate
|
|
|
|
// the offset, length and text for the text change.
|
|
|
|
if (domNode && domNode != mDOMNode) {
|
|
|
|
nsCOMPtr<nsIAccessibleTextChangeEvent> textChangeEvent =
|
|
|
|
CreateTextChangeEventForNode(containerAccessible, domNode, accessible, PR_TRUE, PR_TRUE);
|
|
|
|
if (textChangeEvent) {
|
2008-02-19 03:22:01 -08:00
|
|
|
nsAccEvent::PrepareForEvent(textChangeEvent, isFromUserInput);
|
2007-08-28 14:57:53 -07:00
|
|
|
// XXX Queue them up and merge the text change events
|
|
|
|
// XXX We need a way to ignore SplitNode and JoinNode() when they
|
|
|
|
// do not affect the text within the hypertext
|
|
|
|
FireAccessibleEvent(textChangeEvent);
|
|
|
|
}
|
|
|
|
}
|
2007-10-06 09:24:57 -07:00
|
|
|
|
|
|
|
// Fire show/create events for this node or first accessible descendants of it
|
2008-02-08 18:28:01 -08:00
|
|
|
FireShowHideEvents(domNode, PR_FALSE, eventType, PR_FALSE, isFromUserInput);
|
2007-10-06 09:24:57 -07:00
|
|
|
continue;
|
2007-08-28 14:57:53 -07:00
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (accessible) {
|
|
|
|
if (eventType == nsIAccessibleEvent::EVENT_INTERNAL_LOAD) {
|
2009-06-24 19:08:53 -07:00
|
|
|
nsRefPtr<nsDocAccessible> docAcc =
|
|
|
|
nsAccUtils::QueryAccessibleDocument(accessible);
|
|
|
|
NS_ASSERTION(docAcc, "No doc accessible for doc load event");
|
|
|
|
|
|
|
|
if (docAcc)
|
|
|
|
docAcc->FireDocLoadEvents(nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-04-04 01:37:23 -07:00
|
|
|
else if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED) {
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsIAccessibleText> accessibleText = do_QueryInterface(accessible);
|
|
|
|
PRInt32 caretOffset;
|
|
|
|
if (accessibleText && NS_SUCCEEDED(accessibleText->GetCaretOffset(&caretOffset))) {
|
2007-08-17 11:21:49 -07:00
|
|
|
#ifdef DEBUG_A11Y
|
2007-07-13 10:19:40 -07:00
|
|
|
PRUnichar chAtOffset;
|
|
|
|
accessibleText->GetCharacterAtOffset(caretOffset, &chAtOffset);
|
|
|
|
printf("\nCaret moved to %d with char %c", caretOffset, chAtOffset);
|
2007-12-10 19:30:02 -08:00
|
|
|
#endif
|
|
|
|
#ifdef DEBUG_CARET
|
|
|
|
// Test caret line # -- fire an EVENT_ALERT on the focused node so we can watch the
|
|
|
|
// line-number object attribute on it
|
|
|
|
nsCOMPtr<nsIAccessible> accForFocus;
|
|
|
|
GetAccService()->GetAccessibleFor(gLastFocusedNode, getter_AddRefs(accForFocus));
|
2008-10-16 02:12:05 -07:00
|
|
|
nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_ALERT, accForFocus);
|
2007-07-13 10:19:40 -07:00
|
|
|
#endif
|
2007-06-22 02:27:22 -07:00
|
|
|
nsCOMPtr<nsIAccessibleCaretMoveEvent> caretMoveEvent =
|
|
|
|
new nsAccCaretMoveEvent(accessible, caretOffset);
|
2008-06-05 02:49:51 -07:00
|
|
|
if (!caretMoveEvent)
|
|
|
|
break; // Out of memory, break out to release kung fu death grip
|
2007-06-22 02:27:22 -07:00
|
|
|
|
|
|
|
FireAccessibleEvent(caretMoveEvent);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
PRInt32 selectionCount;
|
|
|
|
accessibleText->GetSelectionCount(&selectionCount);
|
|
|
|
if (selectionCount) { // There's a selection so fire selection change as well
|
2008-10-16 02:12:05 -07:00
|
|
|
nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED,
|
|
|
|
accessible, PR_TRUE);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-05-07 11:55:17 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2009-02-04 22:23:18 -08:00
|
|
|
else if (eventType == nsIAccessibleEvent::EVENT_REORDER) {
|
|
|
|
// Fire reorder event if it's unconditional (see InvalidateCacheSubtree
|
|
|
|
// method) or if changed node (that is the reason of this reorder event)
|
|
|
|
// is accessible or has accessible children.
|
2009-02-05 03:46:34 -08:00
|
|
|
nsCOMPtr<nsAccReorderEvent> reorderEvent = do_QueryInterface(accessibleEvent);
|
2009-02-04 22:23:18 -08:00
|
|
|
if (reorderEvent->IsUnconditionalEvent() ||
|
|
|
|
reorderEvent->HasAccessibleInReasonSubtree()) {
|
|
|
|
nsAccEvent::PrepareForEvent(accessibleEvent);
|
|
|
|
FireAccessibleEvent(accessibleEvent);
|
|
|
|
}
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
else {
|
2007-08-10 18:44:44 -07:00
|
|
|
// The input state was previously stored with the nsIAccessibleEvent,
|
|
|
|
// so use that state now when firing the event
|
|
|
|
nsAccEvent::PrepareForEvent(accessibleEvent);
|
2007-04-16 21:45:42 -07:00
|
|
|
FireAccessibleEvent(accessibleEvent);
|
2007-08-14 11:47:49 -07:00
|
|
|
// Post event processing
|
|
|
|
if (eventType == nsIAccessibleEvent::EVENT_ASYNCH_HIDE ||
|
|
|
|
eventType == nsIAccessibleEvent::EVENT_DOM_DESTROY) {
|
|
|
|
// Shutdown nsIAccessNode's or nsIAccessibles for any DOM nodes in this subtree
|
|
|
|
nsCOMPtr<nsIDOMNode> hidingNode;
|
|
|
|
accessibleEvent->GetDOMNode(getter_AddRefs(hidingNode));
|
|
|
|
if (hidingNode) {
|
|
|
|
RefreshNodes(hidingNode); // Will this bite us with asynch events
|
|
|
|
}
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mEventsToFire.Clear(); // Clear out array
|
2009-08-12 07:38:42 -07:00
|
|
|
mInFlushPendingEvents = PR_FALSE;
|
2007-12-26 21:13:40 -08:00
|
|
|
NS_RELEASE_THIS(); // Release kung fu death grip
|
2008-02-19 03:22:01 -08:00
|
|
|
|
|
|
|
// After a flood of events, reset so that user input flag is off
|
|
|
|
nsAccEvent::ResetLastInputState();
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void nsDocAccessible::FlushEventsCallback(nsITimer *aTimer, void *aClosure)
|
|
|
|
{
|
2009-06-24 19:08:53 -07:00
|
|
|
nsDocAccessible *accessibleDoc = static_cast<nsDocAccessible*>(aClosure);
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_ASSERTION(accessibleDoc, "How did we get here without an accessible document?");
|
2007-12-26 21:13:40 -08:00
|
|
|
if (accessibleDoc) {
|
|
|
|
// A lot of crashes were happening here, so now we're reffing the doc
|
|
|
|
// now until the events are flushed
|
|
|
|
accessibleDoc->FlushPendingEvents();
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2008-02-02 09:02:09 -08:00
|
|
|
void nsDocAccessible::InvalidateChildrenInSubtree(nsIDOMNode *aStartNode)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIAccessNode> accessNode;
|
|
|
|
GetCachedAccessNode(aStartNode, getter_AddRefs(accessNode));
|
2009-06-18 00:37:38 -07:00
|
|
|
nsRefPtr<nsAccessible> acc(nsAccUtils::QueryAccessible(accessNode));
|
|
|
|
if (acc)
|
|
|
|
acc->InvalidateChildren();
|
2008-02-02 09:02:09 -08:00
|
|
|
|
|
|
|
// Invalidate accessible children in the DOM subtree
|
|
|
|
nsCOMPtr<nsINode> node = do_QueryInterface(aStartNode);
|
|
|
|
PRInt32 index, numChildren = node->GetChildCount();
|
|
|
|
for (index = 0; index < numChildren; index ++) {
|
|
|
|
nsCOMPtr<nsIDOMNode> childNode = do_QueryInterface(node->GetChildAt(index));
|
|
|
|
if (childNode)
|
|
|
|
InvalidateChildrenInSubtree(childNode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-08-10 18:44:44 -07:00
|
|
|
void nsDocAccessible::RefreshNodes(nsIDOMNode *aStartNode)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-01-23 10:48:45 -08:00
|
|
|
if (mAccessNodeCache.Count() <= 1) {
|
|
|
|
return; // All we have is a doc accessible. There is nothing to invalidate, quit early
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsIAccessNode> accessNode;
|
2007-11-20 12:39:36 -08:00
|
|
|
GetCachedAccessNode(aStartNode, getter_AddRefs(accessNode));
|
|
|
|
|
|
|
|
// Shut down accessible subtree, which may have been created for
|
|
|
|
// anonymous content subtree
|
|
|
|
nsCOMPtr<nsIAccessible> accessible(do_QueryInterface(accessNode));
|
|
|
|
if (accessible) {
|
2008-01-24 06:07:21 -08:00
|
|
|
// Fire menupopup end if a menu goes away
|
2008-10-17 03:10:43 -07:00
|
|
|
PRUint32 role = nsAccUtils::Role(accessible);
|
2008-01-24 06:07:21 -08:00
|
|
|
if (role == nsIAccessibleRole::ROLE_MENUPOPUP) {
|
|
|
|
nsCOMPtr<nsIDOMNode> domNode;
|
|
|
|
accessNode->GetDOMNode(getter_AddRefs(domNode));
|
|
|
|
nsCOMPtr<nsIDOMXULPopupElement> popup(do_QueryInterface(domNode));
|
|
|
|
if (!popup) {
|
|
|
|
// Popup elements already fire these via DOMMenuInactive
|
|
|
|
// handling in nsRootAccessible::HandleEvent
|
2008-10-16 02:12:05 -07:00
|
|
|
nsAccUtils::FireAccEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
|
|
|
|
accessible);
|
2008-01-24 06:07:21 -08:00
|
|
|
}
|
|
|
|
}
|
2009-06-18 00:37:38 -07:00
|
|
|
nsRefPtr<nsAccessible> acc = nsAccUtils::QueryAccessible(accessible);
|
2007-11-20 12:39:36 -08:00
|
|
|
|
2009-06-18 00:37:38 -07:00
|
|
|
// We only need to shutdown the accessibles here if one of them has been
|
|
|
|
// created.
|
|
|
|
nsCOMPtr<nsIAccessible> childAccessible = acc->GetCachedFirstChild();
|
2007-11-20 12:39:36 -08:00
|
|
|
if (childAccessible) {
|
|
|
|
nsCOMPtr<nsIArray> children;
|
|
|
|
// use GetChildren() to fetch children at one time, instead of using
|
|
|
|
// GetNextSibling(), because after we shutdown the first child,
|
|
|
|
// mNextSibling will be set null.
|
|
|
|
accessible->GetChildren(getter_AddRefs(children));
|
2008-01-28 20:03:26 -08:00
|
|
|
PRUint32 childCount =0;
|
|
|
|
if (children)
|
|
|
|
children->GetLength(&childCount);
|
2008-01-24 06:07:21 -08:00
|
|
|
nsCOMPtr<nsIDOMNode> possibleAnonNode;
|
2007-11-20 12:39:36 -08:00
|
|
|
for (PRUint32 index = 0; index < childCount; index++) {
|
|
|
|
nsCOMPtr<nsIAccessNode> childAccessNode;
|
|
|
|
children->QueryElementAt(index, NS_GET_IID(nsIAccessNode),
|
|
|
|
getter_AddRefs(childAccessNode));
|
2008-01-24 06:07:21 -08:00
|
|
|
childAccessNode->GetDOMNode(getter_AddRefs(possibleAnonNode));
|
|
|
|
nsCOMPtr<nsIContent> iterContent = do_QueryInterface(possibleAnonNode);
|
2008-07-22 21:50:20 -07:00
|
|
|
if (iterContent && iterContent->IsInAnonymousSubtree()) {
|
|
|
|
// IsInAnonymousSubtree() check is a perf win -- make sure we don't
|
2007-11-20 12:39:36 -08:00
|
|
|
// shut down the same subtree twice since we'll reach non-anon content via
|
|
|
|
// DOM traversal later in this method
|
2008-01-24 06:07:21 -08:00
|
|
|
RefreshNodes(possibleAnonNode);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-01-24 06:07:21 -08:00
|
|
|
}
|
2008-01-22 06:43:18 -08:00
|
|
|
|
2008-01-24 06:07:21 -08:00
|
|
|
// Shutdown ordinary content subtree as well -- there may be
|
|
|
|
// access node children which are not full accessible objects
|
|
|
|
nsCOMPtr<nsIDOMNode> nextNode, iterNode;
|
|
|
|
aStartNode->GetFirstChild(getter_AddRefs(nextNode));
|
|
|
|
while (nextNode) {
|
|
|
|
nextNode.swap(iterNode);
|
|
|
|
RefreshNodes(iterNode);
|
|
|
|
iterNode->GetNextSibling(getter_AddRefs(nextNode));
|
|
|
|
}
|
2008-01-22 06:43:18 -08:00
|
|
|
|
2008-01-24 06:07:21 -08:00
|
|
|
if (!accessNode)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (accessNode == this) {
|
|
|
|
// Don't shutdown our doc object -- this may just be from the finished loading.
|
|
|
|
// We will completely shut it down when the pagehide event is received
|
|
|
|
// However, we must invalidate the doc accessible's children in order to be sure
|
|
|
|
// all pointers to them are correct
|
|
|
|
InvalidateChildren();
|
|
|
|
return;
|
2008-01-22 06:43:18 -08:00
|
|
|
}
|
2008-01-24 06:07:21 -08:00
|
|
|
|
|
|
|
// Shut down the actual accessible or access node
|
|
|
|
void *uniqueID;
|
|
|
|
accessNode->GetUniqueID(&uniqueID);
|
2008-10-31 20:58:07 -07:00
|
|
|
nsRefPtr<nsAccessNode> accNode = nsAccUtils::QueryAccessNode(accessNode);
|
|
|
|
accNode->Shutdown();
|
2008-01-24 06:07:21 -08:00
|
|
|
|
|
|
|
// Remove from hash table as well
|
|
|
|
mAccessNodeCache.Remove(uniqueID);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2009-06-24 19:08:53 -07:00
|
|
|
void
|
|
|
|
nsDocAccessible::InvalidateCacheSubtree(nsIContent *aChild,
|
|
|
|
PRUint32 aChangeEventType)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-08-10 18:44:44 -07:00
|
|
|
PRBool isHiding =
|
|
|
|
aChangeEventType == nsIAccessibleEvent::EVENT_ASYNCH_HIDE ||
|
|
|
|
aChangeEventType == nsIAccessibleEvent::EVENT_DOM_DESTROY;
|
|
|
|
|
|
|
|
PRBool isShowing =
|
|
|
|
aChangeEventType == nsIAccessibleEvent::EVENT_ASYNCH_SHOW ||
|
|
|
|
aChangeEventType == nsIAccessibleEvent::EVENT_DOM_CREATE;
|
|
|
|
|
|
|
|
PRBool isChanging =
|
|
|
|
aChangeEventType == nsIAccessibleEvent::EVENT_DOM_SIGNIFICANT_CHANGE ||
|
|
|
|
aChangeEventType == nsIAccessibleEvent::EVENT_ASYNCH_SIGNIFICANT_CHANGE;
|
|
|
|
|
|
|
|
NS_ASSERTION(isChanging || isHiding || isShowing,
|
2007-03-22 10:30:00 -07:00
|
|
|
"Incorrect aChangeEventType passed in");
|
|
|
|
|
2007-08-10 18:44:44 -07:00
|
|
|
PRBool isAsynch =
|
|
|
|
aChangeEventType == nsIAccessibleEvent::EVENT_ASYNCH_HIDE ||
|
|
|
|
aChangeEventType == nsIAccessibleEvent::EVENT_ASYNCH_SHOW ||
|
|
|
|
aChangeEventType == nsIAccessibleEvent::EVENT_ASYNCH_SIGNIFICANT_CHANGE;
|
|
|
|
|
2007-03-22 10:30:00 -07: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.
|
|
|
|
|
2009-06-24 19:08:53 -07:00
|
|
|
NS_ENSURE_TRUE(mDOMNode,);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsIDOMNode> childNode = aChild ? do_QueryInterface(aChild) : mDOMNode;
|
2007-10-06 09:24:57 -07:00
|
|
|
|
2007-12-04 18:51:22 -08:00
|
|
|
nsCOMPtr<nsIPresShell> presShell = GetPresShell();
|
2009-06-24 19:08:53 -07:00
|
|
|
NS_ENSURE_TRUE(presShell,);
|
2007-12-04 18:51:22 -08:00
|
|
|
|
2007-10-01 11:27:13 -07:00
|
|
|
if (!mIsContentLoaded) {
|
|
|
|
// Still loading document
|
|
|
|
if (mAccessNodeCache.Count() <= 1) {
|
|
|
|
// Still loading and no accessibles has yet been created other than this
|
|
|
|
// doc accessible. In this case we optimize
|
|
|
|
// by not firing SHOW/HIDE/REORDER events for every document mutation
|
|
|
|
// caused by page load, since AT is not going to want to grab the
|
|
|
|
// document and listen to these changes until after the page is first loaded
|
|
|
|
// Leave early, and ensure mAccChildCount stays uninitialized instead of 0,
|
|
|
|
// which it is if anyone asks for its children right now.
|
2009-06-18 00:37:38 -07:00
|
|
|
InvalidateChildren();
|
2009-06-24 19:08:53 -07:00
|
|
|
return;
|
2007-10-01 11:27:13 -07:00
|
|
|
}
|
2009-06-24 19:08:53 -07:00
|
|
|
|
2008-01-23 13:27:20 -08:00
|
|
|
nsIEventStateManager *esm = presShell->GetPresContext()->EventStateManager();
|
2009-06-24 19:08:53 -07:00
|
|
|
NS_ENSURE_TRUE(esm,);
|
|
|
|
|
2008-01-23 13:27:20 -08:00
|
|
|
if (!esm->IsHandlingUserInputExternal()) {
|
|
|
|
// Changes during page load, but not caused by user input
|
|
|
|
// Just invalidate accessible hierarchy and return,
|
|
|
|
// otherwise the page load time slows down way too much
|
|
|
|
nsCOMPtr<nsIAccessible> containerAccessible;
|
|
|
|
GetAccessibleInParentChain(childNode, PR_FALSE, getter_AddRefs(containerAccessible));
|
|
|
|
if (!containerAccessible) {
|
|
|
|
containerAccessible = this;
|
|
|
|
}
|
2009-06-18 00:37:38 -07:00
|
|
|
|
|
|
|
nsRefPtr<nsAccessible> containerAcc =
|
|
|
|
nsAccUtils::QueryAccessible(containerAccessible);
|
|
|
|
containerAcc->InvalidateChildren();
|
2009-06-24 19:08:53 -07:00
|
|
|
return;
|
2008-01-23 13:27:20 -08:00
|
|
|
}
|
|
|
|
// else: user input, so we must fall through and for full handling,
|
|
|
|
// e.g. fire the mutation events. Note: user input could cause DOM_CREATE
|
|
|
|
// during page load if user typed into an input field or contentEditable area
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-08-10 18:44:44 -07:00
|
|
|
// Update last change state information
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsIAccessNode> childAccessNode;
|
|
|
|
GetCachedAccessNode(childNode, getter_AddRefs(childAccessNode));
|
|
|
|
nsCOMPtr<nsIAccessible> childAccessible = do_QueryInterface(childAccessNode);
|
2007-06-29 19:49:32 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef DEBUG_A11Y
|
|
|
|
nsAutoString localName;
|
|
|
|
childNode->GetLocalName(localName);
|
|
|
|
const char *hasAccessible = childAccessible ? " (acc)" : "";
|
2007-08-10 18:44:44 -07:00
|
|
|
if (aChangeEventType == nsIAccessibleEvent::EVENT_ASYNCH_HIDE) {
|
2007-03-22 10:30:00 -07:00
|
|
|
printf("[Hide %s %s]\n", NS_ConvertUTF16toUTF8(localName).get(), hasAccessible);
|
|
|
|
}
|
2007-08-10 18:44:44 -07:00
|
|
|
else if (aChangeEventType == nsIAccessibleEvent::EVENT_ASYNCH_SHOW) {
|
2007-03-22 10:30:00 -07:00
|
|
|
printf("[Show %s %s]\n", NS_ConvertUTF16toUTF8(localName).get(), hasAccessible);
|
|
|
|
}
|
2007-08-10 18:44:44 -07:00
|
|
|
else if (aChangeEventType == nsIAccessibleEvent::EVENT_ASYNCH_SIGNIFICANT_CHANGE) {
|
|
|
|
printf("[Layout change %s %s]\n", NS_ConvertUTF16toUTF8(localName).get(), hasAccessible);
|
|
|
|
}
|
|
|
|
else if (aChangeEventType == nsIAccessibleEvent::EVENT_DOM_CREATE) {
|
|
|
|
printf("[Create %s %s]\n", NS_ConvertUTF16toUTF8(localName).get(), hasAccessible);
|
|
|
|
}
|
|
|
|
else if (aChangeEventType == nsIAccessibleEvent::EVENT_DOM_DESTROY) {
|
|
|
|
printf("[Destroy %s %s]\n", NS_ConvertUTF16toUTF8(localName).get(), hasAccessible);
|
|
|
|
}
|
|
|
|
else if (aChangeEventType == nsIAccessibleEvent::EVENT_DOM_SIGNIFICANT_CHANGE) {
|
|
|
|
printf("[Type change %s %s]\n", NS_ConvertUTF16toUTF8(localName).get(), hasAccessible);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-08-28 14:57:53 -07:00
|
|
|
nsCOMPtr<nsIAccessible> containerAccessible;
|
2007-10-01 11:27:13 -07:00
|
|
|
GetAccessibleInParentChain(childNode, PR_TRUE, getter_AddRefs(containerAccessible));
|
2007-08-28 14:57:53 -07:00
|
|
|
if (!containerAccessible) {
|
|
|
|
containerAccessible = this;
|
|
|
|
}
|
|
|
|
|
2007-08-10 18:44:44 -07:00
|
|
|
if (!isShowing) {
|
2007-10-06 09:24:57 -07:00
|
|
|
// Fire EVENT_ASYNCH_HIDE or EVENT_DOM_DESTROY
|
|
|
|
if (isHiding) {
|
2007-12-04 18:51:22 -08:00
|
|
|
nsCOMPtr<nsIContent> content(do_QueryInterface(childNode));
|
2007-10-06 09:24:57 -07:00
|
|
|
if (content) {
|
|
|
|
nsIFrame *frame = presShell->GetPrimaryFrameFor(content);
|
|
|
|
if (frame) {
|
|
|
|
nsIFrame *frameParent = frame->GetParent();
|
|
|
|
if (!frameParent || !frameParent->GetStyleVisibility()->IsVisible()) {
|
|
|
|
// Ancestor already hidden or being hidden at the same time:
|
|
|
|
// don't process redundant hide event
|
|
|
|
// This often happens when visibility is cleared for node,
|
|
|
|
// which hides an entire subtree -- we get notified for each
|
|
|
|
// node in the subtree and need to collate the hide events ourselves.
|
2009-06-24 19:08:53 -07:00
|
|
|
return;
|
2007-10-06 09:24:57 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-08-10 18:44:44 -07:00
|
|
|
}
|
2007-10-06 09:24:57 -07:00
|
|
|
|
|
|
|
PRUint32 removalEventType = isAsynch ? nsIAccessibleEvent::EVENT_ASYNCH_HIDE :
|
|
|
|
nsIAccessibleEvent::EVENT_DOM_DESTROY;
|
|
|
|
|
|
|
|
// Fire an event if the accessible existed for node being hidden, otherwise
|
|
|
|
// for the first line accessible descendants. Fire before the accessible(s) away.
|
2008-02-08 18:28:01 -08:00
|
|
|
nsresult rv = FireShowHideEvents(childNode, PR_FALSE, removalEventType, PR_TRUE, PR_FALSE);
|
2009-06-24 19:08:53 -07:00
|
|
|
NS_ENSURE_SUCCESS(rv,);
|
|
|
|
|
2007-08-28 14:57:53 -07:00
|
|
|
if (childNode != mDOMNode) { // Fire text change unless the node being removed is for this doc
|
|
|
|
// When a node is hidden or removed, the text in an ancestor hyper text will lose characters
|
|
|
|
// At this point we still have the frame and accessible for this node if there was one
|
|
|
|
// XXX Collate events when a range is deleted
|
|
|
|
// XXX We need a way to ignore SplitNode and JoinNode() when they
|
|
|
|
// do not affect the text within the hypertext
|
|
|
|
nsCOMPtr<nsIAccessibleTextChangeEvent> textChangeEvent =
|
|
|
|
CreateTextChangeEventForNode(containerAccessible, childNode, childAccessible,
|
|
|
|
PR_FALSE, isAsynch);
|
|
|
|
if (textChangeEvent) {
|
|
|
|
FireAccessibleEvent(textChangeEvent);
|
|
|
|
}
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// We need to get an accessible for the mutation event's container 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.
|
|
|
|
|
2007-08-10 18:44:44 -07:00
|
|
|
if (aChild && !isHiding) {
|
2008-01-11 12:50:27 -08:00
|
|
|
if (!isAsynch) {
|
|
|
|
// DOM already updated with new objects -- invalidate parent's children now
|
|
|
|
// For asynch we must wait until layout updates before we invalidate the children
|
2009-06-18 00:37:38 -07:00
|
|
|
nsRefPtr<nsAccessible> containerAcc =
|
|
|
|
nsAccUtils::QueryAccessible(containerAccessible);
|
|
|
|
if (containerAcc)
|
|
|
|
containerAcc->InvalidateChildren();
|
|
|
|
|
2008-01-11 12:50:27 -08:00
|
|
|
}
|
2007-04-04 01:37:23 -07:00
|
|
|
// Fire EVENT_SHOW, EVENT_MENUPOPUP_START for newly visible content.
|
2007-03-22 10:30:00 -07:00
|
|
|
// Fire after a short timer, because we want to make sure the view has been
|
|
|
|
// updated to make this accessible content visible. If we don't wait,
|
|
|
|
// the assistive technology may receive the event and then retrieve
|
|
|
|
// nsIAccessibleStates::STATE_INVISIBLE for the event's accessible object.
|
2007-08-10 18:44:44 -07:00
|
|
|
PRUint32 additionEvent = isAsynch ? nsIAccessibleEvent::EVENT_ASYNCH_SHOW :
|
|
|
|
nsIAccessibleEvent::EVENT_DOM_CREATE;
|
2008-01-17 18:56:38 -08:00
|
|
|
FireDelayedToolkitEvent(additionEvent, childNode,
|
2008-03-17 01:13:10 -07:00
|
|
|
nsAccEvent::eCoalesceFromSameSubtree,
|
|
|
|
isAsynch);
|
2007-08-10 18:44:44 -07:00
|
|
|
|
2008-02-12 18:37:22 -08:00
|
|
|
// Check to see change occured in an ARIA menu, and fire
|
|
|
|
// an EVENT_MENUPOPUP_START if it did.
|
2008-10-16 02:12:05 -07:00
|
|
|
nsRoleMapEntry *roleMapEntry = nsAccUtils::GetRoleMapEntry(childNode);
|
2007-12-04 21:06:59 -08:00
|
|
|
if (roleMapEntry && roleMapEntry->role == nsIAccessibleRole::ROLE_MENUPOPUP) {
|
2007-04-04 01:37:23 -07:00
|
|
|
FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START,
|
2008-03-17 01:13:10 -07:00
|
|
|
childNode, nsAccEvent::eRemoveDupes,
|
|
|
|
isAsynch);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-08-10 18:44:44 -07:00
|
|
|
// Check to see if change occured inside an alert, and fire an EVENT_ALERT if it did
|
2007-03-22 10:30:00 -07:00
|
|
|
nsIContent *ancestor = aChild;
|
2007-12-04 21:06:59 -08:00
|
|
|
while (PR_TRUE) {
|
|
|
|
if (roleMapEntry && roleMapEntry->role == nsIAccessibleRole::ROLE_ALERT) {
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsIDOMNode> alertNode(do_QueryInterface(ancestor));
|
2008-01-17 18:56:38 -08:00
|
|
|
FireDelayedToolkitEvent(nsIAccessibleEvent::EVENT_ALERT, alertNode,
|
2008-03-17 01:13:10 -07:00
|
|
|
nsAccEvent::eRemoveDupes, isAsynch);
|
2007-03-22 10:30:00 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
ancestor = ancestor->GetParent();
|
2007-12-04 21:06:59 -08:00
|
|
|
nsCOMPtr<nsIDOMNode> ancestorNode = do_QueryInterface(ancestor);
|
|
|
|
if (!ancestorNode) {
|
|
|
|
break;
|
|
|
|
}
|
2008-10-16 02:12:05 -07:00
|
|
|
roleMapEntry = nsAccUtils::GetRoleMapEntry(ancestorNode);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-19 18:58:11 -07:00
|
|
|
FireValueChangeForTextFields(containerAccessible);
|
|
|
|
|
2009-02-04 22:23:18 -08:00
|
|
|
// Fire an event so the MSAA clients know the children have changed. Also
|
|
|
|
// the event is used internally by MSAA part.
|
|
|
|
|
2009-02-04 23:11:12 -08:00
|
|
|
// We need to fire a delayed reorder event for the accessible parent of the
|
|
|
|
// changed node. We fire an unconditional reorder event if the changed node or
|
|
|
|
// one of its children is already accessible. In the case of show events, the
|
|
|
|
// accessible object might not be created yet for an otherwise accessible
|
|
|
|
// changed node (because its frame might not be constructed yet). In this case
|
|
|
|
// we fire a conditional reorder event, so that we will later check whether
|
|
|
|
// the changed node is accessible or has accessible children.
|
|
|
|
// Filtering/coalescing of these events happens during the queue flush.
|
2009-02-04 22:23:18 -08:00
|
|
|
|
|
|
|
PRBool isUnconditionalEvent = childAccessible ||
|
|
|
|
aChild && nsAccUtils::HasAccessibleChildren(childNode);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIAccessibleEvent> reorderEvent =
|
|
|
|
new nsAccReorderEvent(containerAccessible, isAsynch,
|
|
|
|
isUnconditionalEvent,
|
2009-02-05 00:57:08 -08:00
|
|
|
aChild ? childNode.get() : nsnull);
|
2009-06-24 19:08:53 -07:00
|
|
|
NS_ENSURE_TRUE(reorderEvent,);
|
2009-02-04 22:23:18 -08:00
|
|
|
|
|
|
|
FireDelayedAccessibleEvent(reorderEvent);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2007-06-23 01:25:29 -07:00
|
|
|
nsDocAccessible::GetAccessibleInParentChain(nsIDOMNode *aNode,
|
2007-10-01 11:27:13 -07:00
|
|
|
PRBool aCanCreate,
|
2007-03-22 10:30:00 -07:00
|
|
|
nsIAccessible **aAccessible)
|
|
|
|
{
|
|
|
|
// Find accessible in parent chain of DOM nodes, or return null
|
|
|
|
*aAccessible = nsnull;
|
|
|
|
nsCOMPtr<nsIDOMNode> currentNode(aNode), parentNode;
|
|
|
|
nsCOMPtr<nsIAccessNode> accessNode;
|
|
|
|
|
|
|
|
nsIAccessibilityService *accService = GetAccService();
|
|
|
|
NS_ENSURE_TRUE(accService, NS_ERROR_FAILURE);
|
|
|
|
|
|
|
|
do {
|
|
|
|
currentNode->GetParentNode(getter_AddRefs(parentNode));
|
|
|
|
currentNode = parentNode;
|
|
|
|
if (!currentNode) {
|
|
|
|
NS_ADDREF_THIS();
|
|
|
|
*aAccessible = this;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMNode> relevantNode;
|
|
|
|
if (NS_SUCCEEDED(accService->GetRelevantContentNodeFor(currentNode, getter_AddRefs(relevantNode))) && relevantNode) {
|
|
|
|
currentNode = relevantNode;
|
|
|
|
}
|
2007-11-20 11:54:18 -08:00
|
|
|
if (aCanCreate) {
|
2007-11-15 07:33:52 -08:00
|
|
|
accService->GetAccessibleInWeakShell(currentNode, mWeakShell, aAccessible);
|
2007-11-20 11:54:18 -08:00
|
|
|
}
|
|
|
|
else { // Only return cached accessibles, don't create anything
|
|
|
|
nsCOMPtr<nsIAccessNode> accessNode;
|
|
|
|
GetCachedAccessNode(currentNode, getter_AddRefs(accessNode)); // AddRefs
|
|
|
|
if (accessNode) {
|
|
|
|
CallQueryInterface(accessNode, aAccessible); // AddRefs
|
2007-10-01 11:27:13 -07:00
|
|
|
}
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
} while (!*aAccessible);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2007-10-06 09:24:57 -07:00
|
|
|
nsresult
|
2008-02-08 18:28:01 -08:00
|
|
|
nsDocAccessible::FireShowHideEvents(nsIDOMNode *aDOMNode, PRBool aAvoidOnThisNode, PRUint32 aEventType,
|
2007-10-06 09:24:57 -07:00
|
|
|
PRBool aDelay, PRBool aForceIsFromUserInput)
|
|
|
|
{
|
|
|
|
NS_ENSURE_ARG(aDOMNode);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIAccessible> accessible;
|
2008-02-08 18:28:01 -08:00
|
|
|
if (!aAvoidOnThisNode) {
|
|
|
|
if (aEventType == nsIAccessibleEvent::EVENT_ASYNCH_HIDE ||
|
|
|
|
aEventType == nsIAccessibleEvent::EVENT_DOM_DESTROY) {
|
|
|
|
// Don't allow creation for accessibles when nodes going away
|
|
|
|
nsCOMPtr<nsIAccessNode> accessNode;
|
|
|
|
GetCachedAccessNode(aDOMNode, getter_AddRefs(accessNode));
|
|
|
|
accessible = do_QueryInterface(accessNode);
|
|
|
|
} else {
|
|
|
|
// Allow creation of new accessibles for show events
|
|
|
|
GetAccService()->GetAttachedAccessibleFor(aDOMNode,
|
|
|
|
getter_AddRefs(accessible));
|
|
|
|
}
|
2007-10-06 09:24:57 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (accessible) {
|
|
|
|
// Found an accessible, so fire the show/hide on it and don't
|
|
|
|
// look further into this subtree
|
|
|
|
PRBool isAsynch = aEventType == nsIAccessibleEvent::EVENT_ASYNCH_HIDE ||
|
|
|
|
aEventType == nsIAccessibleEvent::EVENT_ASYNCH_SHOW;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIAccessibleEvent> event =
|
2008-03-17 01:13:10 -07:00
|
|
|
new nsAccEvent(aEventType, accessible, isAsynch,
|
|
|
|
nsAccEvent::eCoalesceFromSameSubtree);
|
2007-10-06 09:24:57 -07:00
|
|
|
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
if (aForceIsFromUserInput) {
|
2008-02-19 03:22:01 -08:00
|
|
|
nsAccEvent::PrepareForEvent(event, aForceIsFromUserInput);
|
2007-10-06 09:24:57 -07:00
|
|
|
}
|
|
|
|
if (aDelay) {
|
2008-03-17 01:13:10 -07:00
|
|
|
return FireDelayedAccessibleEvent(event);
|
2007-10-06 09:24:57 -07:00
|
|
|
}
|
|
|
|
return FireAccessibleEvent(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Could not find accessible to show hide yet, so fire on any
|
|
|
|
// accessible descendants in this subtree
|
2008-04-22 23:06:39 -07:00
|
|
|
nsCOMPtr<nsINode> node(do_QueryInterface(aDOMNode));
|
|
|
|
PRUint32 count = node->GetChildCount();
|
2007-10-06 09:24:57 -07:00
|
|
|
for (PRUint32 index = 0; index < count; index++) {
|
2008-04-22 23:06:39 -07:00
|
|
|
nsCOMPtr<nsIDOMNode> childNode = do_QueryInterface(node->GetChildAt(index));
|
2008-02-08 18:28:01 -08:00
|
|
|
nsresult rv = FireShowHideEvents(childNode, PR_FALSE, aEventType,
|
2007-10-06 09:24:57 -07:00
|
|
|
aDelay, aForceIsFromUserInput);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|