2003-04-01 22:18:29 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Netscape Communications Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2003
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
2004-04-17 21:52:36 +00:00
|
|
|
* Original Author: Aaron Leventhal (aaronl@netscape.com)
|
2003-04-01 22:18:29 +00:00
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
2004-04-17 21:52:36 +00:00
|
|
|
* 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"),
|
2003-04-01 22:18:29 +00:00
|
|
|
* 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 ***** */
|
|
|
|
|
2010-04-27 06:52:03 +00:00
|
|
|
#include "nsAccCache.h"
|
2005-06-10 13:57:27 +00:00
|
|
|
#include "nsAccessibilityAtoms.h"
|
2009-07-29 09:01:48 +00:00
|
|
|
#include "nsAccessibilityService.h"
|
2010-07-02 01:50:03 +00:00
|
|
|
#include "nsAccTreeWalker.h"
|
2010-04-27 06:52:03 +00:00
|
|
|
#include "nsAccUtils.h"
|
|
|
|
#include "nsRootAccessible.h"
|
|
|
|
#include "nsTextEquivUtils.h"
|
|
|
|
|
2006-04-12 15:43:32 +00:00
|
|
|
#include "nsIMutableArray.h"
|
2003-05-15 08:37:38 +00:00
|
|
|
#include "nsICommandManager.h"
|
|
|
|
#include "nsIDocShell.h"
|
|
|
|
#include "nsIDocShellTreeItem.h"
|
2003-04-01 22:18:29 +00:00
|
|
|
#include "nsIDocument.h"
|
2005-01-28 02:35:26 +00:00
|
|
|
#include "nsIDOMAttr.h"
|
2003-05-15 08:37:38 +00:00
|
|
|
#include "nsIDOMCharacterData.h"
|
2003-04-01 22:18:29 +00:00
|
|
|
#include "nsIDOMDocument.h"
|
|
|
|
#include "nsIDOMDocumentType.h"
|
2003-04-28 10:24:52 +00:00
|
|
|
#include "nsIDOMNSDocument.h"
|
2003-05-15 08:37:38 +00:00
|
|
|
#include "nsIDOMNSHTMLDocument.h"
|
2010-06-11 08:23:18 +00:00
|
|
|
#include "nsIDOMXULDocument.h"
|
2005-08-05 18:16:32 +00:00
|
|
|
#include "nsIDOMMutationEvent.h"
|
2005-11-28 23:56:44 +00:00
|
|
|
#include "nsPIDOMWindow.h"
|
2005-09-30 19:23:42 +00:00
|
|
|
#include "nsIDOMXULPopupElement.h"
|
2003-05-15 08:37:38 +00:00
|
|
|
#include "nsIEditingSession.h"
|
2005-07-25 21:40:31 +00:00
|
|
|
#include "nsIEventStateManager.h"
|
2003-05-15 08:37:38 +00:00
|
|
|
#include "nsIFrame.h"
|
2005-07-25 21:40:31 +00:00
|
|
|
#include "nsHTMLSelectAccessible.h"
|
2003-04-01 22:18:29 +00:00
|
|
|
#include "nsIInterfaceRequestorUtils.h"
|
2003-05-15 08:37:38 +00:00
|
|
|
#include "nsINameSpaceManager.h"
|
|
|
|
#include "nsIPresShell.h"
|
2003-04-01 22:18:29 +00:00
|
|
|
#include "nsIServiceManager.h"
|
2007-02-12 19:46:41 +00:00
|
|
|
#include "nsIViewManager.h"
|
2009-09-03 03:57:41 +00:00
|
|
|
#include "nsIScrollableFrame.h"
|
2005-06-24 19:16:45 +00:00
|
|
|
#include "nsUnicharUtils.h"
|
2003-05-15 08:37:38 +00:00
|
|
|
#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 18:00:39 +00:00
|
|
|
#include "nsFocusManager.h"
|
2010-08-24 07:05:56 +00:00
|
|
|
#include "mozilla/dom/Element.h"
|
2003-05-15 08:37:38 +00:00
|
|
|
#ifdef MOZ_XUL
|
|
|
|
#include "nsIXULDocument.h"
|
|
|
|
#endif
|
2003-04-01 22:18:29 +00:00
|
|
|
|
2010-08-24 07:05:56 +00:00
|
|
|
namespace dom = mozilla::dom;
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Static member initialization
|
2003-04-01 22:18:29 +00:00
|
|
|
|
2007-09-18 21:36:41 +00:00
|
|
|
PRUint32 nsDocAccessible::gLastFocusedAccessiblesState = 0;
|
|
|
|
|
2010-11-18 02:55:44 +00:00
|
|
|
static nsIAtom** kRelationAttrs[] =
|
|
|
|
{
|
|
|
|
&nsAccessibilityAtoms::aria_labelledby,
|
|
|
|
&nsAccessibilityAtoms::aria_describedby,
|
|
|
|
&nsAccessibilityAtoms::aria_owns,
|
|
|
|
&nsAccessibilityAtoms::aria_controls,
|
2010-11-20 02:37:18 +00:00
|
|
|
&nsAccessibilityAtoms::aria_flowto,
|
|
|
|
&nsAccessibilityAtoms::_for,
|
|
|
|
&nsAccessibilityAtoms::control
|
2010-11-18 02:55:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static const PRUint32 kRelationAttrsLen = NS_ARRAY_LENGTH(kRelationAttrs);
|
2009-12-10 19:12:19 +00:00
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Constructor/desctructor
|
|
|
|
|
2010-06-11 08:23:18 +00:00
|
|
|
nsDocAccessible::
|
|
|
|
nsDocAccessible(nsIDocument *aDocument, nsIContent *aRootContent,
|
|
|
|
nsIWeakReference *aShell) :
|
2010-09-17 03:23:17 +00:00
|
|
|
nsHyperTextAccessibleWrap(aRootContent, aShell),
|
2010-11-19 05:44:47 +00:00
|
|
|
mDocument(aDocument), mScrollPositionChangedTicks(0), mIsLoaded(PR_FALSE),
|
|
|
|
mCacheRoot(nsnull), mIsPostCacheProcessing(PR_FALSE)
|
2003-04-01 22:18:29 +00:00
|
|
|
{
|
2010-11-18 02:55:44 +00:00
|
|
|
mDependentIDsHash.Init();
|
2009-08-11 06:59:05 +00:00
|
|
|
// XXX aaronl should we use an algorithm for the initial cache size?
|
2010-06-12 04:04:35 +00:00
|
|
|
mAccessibleCache.Init(kDefaultCacheSize);
|
2010-10-21 04:16:10 +00:00
|
|
|
mNodeToAccessibleMap.Init(kDefaultCacheSize);
|
2009-08-11 06:59:05 +00:00
|
|
|
|
2006-08-30 05:19:06 +00:00
|
|
|
// For GTK+ native window, we do nothing here.
|
2010-06-11 08:23:18 +00:00
|
|
|
if (!mDocument)
|
2006-08-30 05:19:06 +00:00
|
|
|
return;
|
|
|
|
|
2010-06-08 16:39:58 +00:00
|
|
|
// nsAccDocManager creates document accessible when scrollable frame is
|
|
|
|
// available already, it should be safe time to add scroll listener.
|
|
|
|
AddScrollListener();
|
2003-04-01 22:18:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsDocAccessible::~nsDocAccessible()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
|
2008-08-06 12:19:56 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2009-12-10 19:12:19 +00:00
|
|
|
// nsISupports
|
2008-08-06 12:19:56 +00:00
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocAccessible)
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
|
2011-01-18 08:03:38 +00:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mNotificationController,
|
|
|
|
NotificationController)
|
2009-09-07 16:46:56 +00:00
|
|
|
|
2010-09-09 14:44:56 +00:00
|
|
|
PRUint32 i, length = tmp->mChildDocuments.Length();
|
|
|
|
for (i = 0; i < length; ++i) {
|
|
|
|
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildDocuments[i]");
|
|
|
|
cb.NoteXPCOMChild(static_cast<nsIAccessible*>(tmp->mChildDocuments[i].get()));
|
|
|
|
}
|
|
|
|
|
2010-06-12 04:04:35 +00:00
|
|
|
CycleCollectorTraverseCache(tmp->mAccessibleCache, &cb);
|
2008-08-06 12:19:56 +00:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
|
2011-01-18 08:03:38 +00:00
|
|
|
tmp->mNotificationController->Shutdown();
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mNotificationController)
|
2010-09-09 14:44:56 +00:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mChildDocuments)
|
2010-11-18 02:55:44 +00:00
|
|
|
tmp->mDependentIDsHash.Clear();
|
2010-10-21 04:16:10 +00:00
|
|
|
tmp->mNodeToAccessibleMap.Clear();
|
2010-06-12 04:04:35 +00:00
|
|
|
ClearCache(tmp->mAccessibleCache);
|
2008-08-06 12:19:56 +00:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDocAccessible)
|
2009-06-25 02:08:53 +00:00
|
|
|
NS_INTERFACE_MAP_STATIC_AMBIGUOUS(nsDocAccessible)
|
2003-05-15 08:37:38 +00:00
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIAccessibleDocument)
|
2005-06-10 13:57:27 +00:00
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
|
2006-07-02 07:23:10 +00:00
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
|
2003-05-15 08:37:38 +00:00
|
|
|
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
2009-06-25 02:08:53 +00:00
|
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessibleDocument)
|
2010-06-11 08:23:18 +00:00
|
|
|
foundInterface = 0;
|
|
|
|
|
|
|
|
nsresult status;
|
|
|
|
if (!foundInterface) {
|
|
|
|
// HTML document accessible must inherit from nsHyperTextAccessible to get
|
|
|
|
// support text interfaces. XUL document accessible doesn't need this.
|
|
|
|
// However at some point we may push <body> to implement the interfaces and
|
|
|
|
// return nsDocAccessible to inherit from nsAccessibleWrap.
|
|
|
|
|
2010-11-18 19:45:45 +00:00
|
|
|
if (mDocument && mDocument->IsXUL())
|
2010-06-11 08:23:18 +00:00
|
|
|
status = nsAccessible::QueryInterface(aIID, (void**)&foundInterface);
|
|
|
|
else
|
|
|
|
status = nsHyperTextAccessible::QueryInterface(aIID,
|
|
|
|
(void**)&foundInterface);
|
|
|
|
} else {
|
|
|
|
NS_ADDREF(foundInterface);
|
|
|
|
status = NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
*aInstancePtr = foundInterface;
|
|
|
|
return status;
|
|
|
|
}
|
2003-05-15 08:37:38 +00:00
|
|
|
|
2006-06-21 13:29:10 +00:00
|
|
|
NS_IMPL_ADDREF_INHERITED(nsDocAccessible, nsHyperTextAccessible)
|
|
|
|
NS_IMPL_RELEASE_INHERITED(nsDocAccessible, nsHyperTextAccessible)
|
2003-04-01 22:18:29 +00:00
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsIAccessible
|
|
|
|
|
2008-10-10 12:26:55 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDocAccessible::GetName(nsAString& aName)
|
2006-04-10 14:25:14 +00:00
|
|
|
{
|
2005-06-01 14:03:38 +00:00
|
|
|
nsresult rv = NS_OK;
|
|
|
|
aName.Truncate();
|
2008-03-14 20:49:38 +00:00
|
|
|
if (mParent) {
|
|
|
|
rv = mParent->GetName(aName); // Allow owning iframe to override the name
|
2005-06-01 14:03:38 +00:00
|
|
|
}
|
|
|
|
if (aName.IsEmpty()) {
|
2008-10-10 12:26:55 +00:00
|
|
|
// Allow name via aria-labelledby or title attribute
|
|
|
|
rv = nsAccessible::GetName(aName);
|
2005-06-01 14:03:38 +00:00
|
|
|
}
|
2008-03-14 20:49:38 +00:00
|
|
|
if (aName.IsEmpty()) {
|
2008-04-15 15:17:59 +00:00
|
|
|
rv = GetTitle(aName); // Try title element
|
|
|
|
}
|
|
|
|
if (aName.IsEmpty()) { // Last resort: use URL
|
|
|
|
rv = GetURL(aName);
|
2005-08-17 19:08:26 +00:00
|
|
|
}
|
2005-06-01 14:03:38 +00:00
|
|
|
|
|
|
|
return rv;
|
2003-04-15 08:45:55 +00:00
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
// nsAccessible public method
|
2010-09-05 02:14:01 +00:00
|
|
|
PRUint32
|
|
|
|
nsDocAccessible::NativeRole()
|
2003-04-01 22:18:29 +00:00
|
|
|
{
|
2005-06-01 13:54:08 +00:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
|
2010-06-11 08:23:18 +00:00
|
|
|
nsCoreUtils::GetDocShellTreeItemFor(mDocument);
|
2005-06-01 13:54:08 +00:00
|
|
|
if (docShellTreeItem) {
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
|
|
|
|
docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
|
2007-06-14 17:12:50 +00:00
|
|
|
PRInt32 itemType;
|
|
|
|
docShellTreeItem->GetItemType(&itemType);
|
2005-06-01 13:54:08 +00:00
|
|
|
if (sameTypeRoot == docShellTreeItem) {
|
|
|
|
// Root of content or chrome tree
|
2010-09-05 02:14:01 +00:00
|
|
|
if (itemType == nsIDocShellTreeItem::typeChrome)
|
|
|
|
return nsIAccessibleRole::ROLE_CHROME_WINDOW;
|
|
|
|
|
|
|
|
if (itemType == nsIDocShellTreeItem::typeContent) {
|
2005-07-21 14:28:17 +00:00
|
|
|
#ifdef MOZ_XUL
|
|
|
|
nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
|
2010-09-05 02:14:01 +00:00
|
|
|
if (xulDoc)
|
|
|
|
return nsIAccessibleRole::ROLE_APPLICATION;
|
2005-07-21 14:28:17 +00:00
|
|
|
#endif
|
2010-09-05 02:14:01 +00:00
|
|
|
return nsIAccessibleRole::ROLE_DOCUMENT;
|
2005-06-01 13:54:08 +00:00
|
|
|
}
|
|
|
|
}
|
2007-06-14 17:12:50 +00:00
|
|
|
else if (itemType == nsIDocShellTreeItem::typeContent) {
|
2010-09-05 02:14:01 +00:00
|
|
|
return nsIAccessibleRole::ROLE_DOCUMENT;
|
2007-06-14 17:12:50 +00:00
|
|
|
}
|
2005-06-01 13:54:08 +00:00
|
|
|
}
|
2006-04-10 14:25:14 +00:00
|
|
|
|
2010-09-05 02:14:01 +00:00
|
|
|
return nsIAccessibleRole::ROLE_PANE; // Fall back;
|
2003-04-01 22:18:29 +00:00
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
// nsAccessible public method
|
2009-06-18 07:37:38 +00:00
|
|
|
void
|
|
|
|
nsDocAccessible::SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry)
|
2003-04-01 22:18:29 +00:00
|
|
|
{
|
2009-06-18 07:37:38 +00:00
|
|
|
NS_ASSERTION(mDocument, "No document during initialization!");
|
|
|
|
if (!mDocument)
|
|
|
|
return;
|
2008-03-14 20:49:38 +00:00
|
|
|
|
|
|
|
mRoleMapEntry = aRoleMapEntry;
|
|
|
|
|
|
|
|
nsIDocument *parentDoc = mDocument->GetParentDocument();
|
2009-06-18 07:37:38 +00:00
|
|
|
if (!parentDoc)
|
2009-06-29 13:26:45 +00:00
|
|
|
return; // No parent document for the root document
|
2009-06-18 07:37:38 +00:00
|
|
|
|
2009-06-29 13:26:45 +00:00
|
|
|
// Allow use of ARIA role from outer to override
|
2008-03-14 20:49:38 +00:00
|
|
|
nsIContent *ownerContent = parentDoc->FindContentForSubDocument(mDocument);
|
2010-06-11 08:23:18 +00:00
|
|
|
if (ownerContent) {
|
|
|
|
nsRoleMapEntry *roleMapEntry = nsAccUtils::GetRoleMapEntry(ownerContent);
|
2008-03-14 20:49:38 +00:00
|
|
|
if (roleMapEntry)
|
|
|
|
mRoleMapEntry = roleMapEntry; // Override
|
|
|
|
}
|
2003-04-01 22:18:29 +00:00
|
|
|
}
|
|
|
|
|
2007-07-02 06:14:11 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDocAccessible::GetDescription(nsAString& aDescription)
|
|
|
|
{
|
2008-03-14 20:49:38 +00:00
|
|
|
if (mParent)
|
|
|
|
mParent->GetDescription(aDescription);
|
|
|
|
|
|
|
|
if (aDescription.IsEmpty()) {
|
|
|
|
nsAutoString description;
|
2009-02-19 07:06:14 +00:00
|
|
|
nsTextEquivUtils::
|
|
|
|
GetTextEquivFromIDRefs(this, nsAccessibilityAtoms::aria_describedby,
|
|
|
|
description);
|
2008-03-14 20:49:38 +00:00
|
|
|
aDescription = description;
|
|
|
|
}
|
|
|
|
|
2007-07-02 06:14:11 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
// nsAccessible public method
|
2008-11-04 03:37:46 +00:00
|
|
|
nsresult
|
|
|
|
nsDocAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState)
|
2003-04-01 22:18:29 +00:00
|
|
|
{
|
2010-06-11 08:23:18 +00:00
|
|
|
*aState = 0;
|
|
|
|
|
|
|
|
if (IsDefunct()) {
|
|
|
|
if (aExtraState)
|
|
|
|
*aExtraState = nsIAccessibleStates::EXT_STATE_DEFUNCT;
|
|
|
|
|
|
|
|
return NS_OK_DEFUNCT_OBJECT;
|
|
|
|
}
|
|
|
|
|
2010-11-06 04:11:08 +00:00
|
|
|
if (aExtraState) {
|
|
|
|
// The root content of the document might be removed so that mContent is
|
|
|
|
// out of date.
|
|
|
|
*aExtraState = (mContent->GetCurrentDoc() == mDocument) ?
|
|
|
|
0 : nsIAccessibleStates::EXT_STATE_STALE;
|
|
|
|
}
|
2007-04-02 15:56:24 +00:00
|
|
|
|
2007-07-24 07:50:51 +00:00
|
|
|
#ifdef MOZ_XUL
|
2007-04-09 13:40:25 +00:00
|
|
|
nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
|
2007-07-24 07:50:51 +00:00
|
|
|
if (!xulDoc)
|
|
|
|
#endif
|
|
|
|
{
|
2007-04-09 13:40:25 +00: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;
|
2010-06-11 08:23:18 +00:00
|
|
|
if (gLastFocusedNode == mDocument)
|
2008-05-04 17:42:19 +00:00
|
|
|
*aState |= nsIAccessibleStates::STATE_FOCUSED;
|
2007-04-09 13:40:25 +00:00
|
|
|
}
|
2006-04-10 14:25:14 +00:00
|
|
|
|
2010-11-22 08:22:04 +00:00
|
|
|
if (!mIsLoaded) {
|
2007-03-15 14:18:33 +00:00
|
|
|
*aState |= nsIAccessibleStates::STATE_BUSY;
|
2007-05-04 15:15:00 +00:00
|
|
|
if (aExtraState) {
|
|
|
|
*aExtraState |= nsIAccessibleStates::EXT_STATE_STALE;
|
|
|
|
}
|
2005-04-15 21:22:25 +00:00
|
|
|
}
|
2007-03-19 06:45:35 +00:00
|
|
|
|
|
|
|
nsIFrame* frame = GetFrame();
|
|
|
|
while (frame != nsnull && !frame->HasView()) {
|
|
|
|
frame = frame->GetParent();
|
2004-05-26 12:31:43 +00:00
|
|
|
}
|
2007-03-19 06:45:35 +00:00
|
|
|
|
2008-02-08 13:59:46 +00:00
|
|
|
if (frame == nsnull ||
|
|
|
|
!CheckVisibilityInParentChain(mDocument, frame->GetViewExternal())) {
|
|
|
|
*aState |= nsIAccessibleStates::STATE_INVISIBLE |
|
|
|
|
nsIAccessibleStates::STATE_OFFSCREEN;
|
2004-05-26 12:31:43 +00:00
|
|
|
}
|
2003-05-15 08:37:38 +00:00
|
|
|
|
2007-08-14 16:25:24 +00:00
|
|
|
nsCOMPtr<nsIEditor> editor;
|
|
|
|
GetAssociatedEditor(getter_AddRefs(editor));
|
2006-06-26 14:54:25 +00:00
|
|
|
if (!editor) {
|
2007-03-15 14:18:33 +00:00
|
|
|
*aState |= nsIAccessibleStates::STATE_READONLY;
|
2003-05-15 08:37:38 +00:00
|
|
|
}
|
2007-04-13 11:38:43 +00:00
|
|
|
else if (aExtraState) {
|
|
|
|
*aExtraState |= nsIAccessibleStates::EXT_STATE_EDITABLE;
|
|
|
|
}
|
2006-06-21 13:29:10 +00:00
|
|
|
|
2003-04-15 08:45:55 +00:00
|
|
|
return NS_OK;
|
2003-04-01 22:18:29 +00:00
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
// nsAccessible public method
|
2009-06-18 07:37:38 +00:00
|
|
|
nsresult
|
2009-06-25 02:12:38 +00:00
|
|
|
nsDocAccessible::GetARIAState(PRUint32 *aState, PRUint32 *aExtraState)
|
2008-03-14 20:49:38 +00:00
|
|
|
{
|
|
|
|
// Combine with states from outer doc
|
|
|
|
NS_ENSURE_ARG_POINTER(aState);
|
2009-06-25 02:12:38 +00:00
|
|
|
nsresult rv = nsAccessible::GetARIAState(aState, aExtraState);
|
2008-03-14 20:49:38 +00:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
2010-01-11 14:14:06 +00:00
|
|
|
if (mParent) // Allow iframe/frame etc. to have final state override via ARIA
|
|
|
|
return mParent->GetARIAState(aState, aExtraState);
|
2008-03-14 20:49:38 +00: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;
|
|
|
|
}
|
|
|
|
|
2006-04-10 14:25:14 +00:00
|
|
|
NS_IMETHODIMP nsDocAccessible::GetFocusedChild(nsIAccessible **aFocusedChild)
|
2005-02-18 14:36:28 +00: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 18:00:39 +00:00
|
|
|
// XXXndeakin P3 accessibility shouldn't be caching the focus
|
2005-02-18 14:36:28 +00:00
|
|
|
if (!gLastFocusedNode) {
|
|
|
|
*aFocusedChild = nsnull;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2005-09-09 02:30:16 +00:00
|
|
|
// Return an accessible for the current global focus, which does not have to
|
|
|
|
// be contained within the current document.
|
2010-06-11 08:23:18 +00:00
|
|
|
NS_IF_ADDREF(*aFocusedChild = GetAccService()->GetAccessible(gLastFocusedNode));
|
|
|
|
return NS_OK;
|
2005-02-18 14:36:28 +00:00
|
|
|
}
|
|
|
|
|
2007-01-10 06:32:15 +00:00
|
|
|
NS_IMETHODIMP nsDocAccessible::TakeFocus()
|
|
|
|
{
|
2010-06-11 08:23:18 +00:00
|
|
|
if (IsDefunct())
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2007-04-27 15:15:19 +00:00
|
|
|
PRUint32 state;
|
2008-11-04 03:37:46 +00:00
|
|
|
GetStateInternal(&state, nsnull);
|
2007-04-27 15:15:19 +00:00
|
|
|
if (0 == (state & nsIAccessibleStates::STATE_FOCUSABLE)) {
|
|
|
|
return NS_ERROR_FAILURE; // Not focusable
|
2007-01-10 06:32:15 +00:00
|
|
|
}
|
2007-04-27 15:15:19 +00:00
|
|
|
|
2010-06-11 08:23:18 +00:00
|
|
|
// Focus the document.
|
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 18:00:39 +00:00
|
|
|
nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
|
2010-06-11 08:23:18 +00:00
|
|
|
NS_ENSURE_STATE(fm);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMElement> newFocus;
|
|
|
|
return fm->MoveFocus(mDocument->GetWindow(), nsnull,
|
|
|
|
nsIFocusManager::MOVEFOCUS_ROOT, 0,
|
|
|
|
getter_AddRefs(newFocus));
|
2007-01-10 06:32:15 +00:00
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsIAccessibleDocument
|
2003-04-01 22:18:29 +00:00
|
|
|
|
|
|
|
NS_IMETHODIMP nsDocAccessible::GetURL(nsAString& aURL)
|
2006-04-10 14:25:14 +00:00
|
|
|
{
|
2010-06-11 08:23:18 +00:00
|
|
|
if (IsDefunct())
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2003-10-22 06:09:48 +00:00
|
|
|
nsCOMPtr<nsISupports> container = mDocument->GetContainer();
|
2003-04-01 22:18:29 +00:00
|
|
|
nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(container));
|
|
|
|
nsCAutoString theURL;
|
|
|
|
if (webNav) {
|
|
|
|
nsCOMPtr<nsIURI> pURI;
|
|
|
|
webNav->GetCurrentURI(getter_AddRefs(pURI));
|
2006-04-10 14:25:14 +00:00
|
|
|
if (pURI)
|
2003-04-01 22:18:29 +00:00
|
|
|
pURI->GetSpec(theURL);
|
|
|
|
}
|
2003-12-23 16:48:40 +00:00
|
|
|
CopyUTF8toUTF16(theURL, aURL);
|
2003-04-01 22:18:29 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsDocAccessible::GetTitle(nsAString& aTitle)
|
|
|
|
{
|
2008-08-18 02:10:28 +00:00
|
|
|
nsCOMPtr<nsIDOMNSDocument> domnsDocument(do_QueryInterface(mDocument));
|
|
|
|
if (domnsDocument) {
|
|
|
|
return domnsDocument->GetTitle(aTitle);
|
2003-10-22 06:09:48 +00:00
|
|
|
}
|
|
|
|
return NS_ERROR_FAILURE;
|
2003-04-01 22:18:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsDocAccessible::GetMimeType(nsAString& aMimeType)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNSDocument> domnsDocument(do_QueryInterface(mDocument));
|
|
|
|
if (domnsDocument) {
|
|
|
|
return domnsDocument->GetContentType(aMimeType);
|
|
|
|
}
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsDocAccessible::GetDocType(nsAString& aDocType)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(mDocument));
|
|
|
|
nsCOMPtr<nsIDOMDocumentType> docType;
|
|
|
|
|
2003-04-11 00:56:27 +00:00
|
|
|
#ifdef MOZ_XUL
|
|
|
|
nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
|
2003-04-01 22:18:29 +00:00
|
|
|
if (xulDoc) {
|
2004-06-17 00:13:25 +00:00
|
|
|
aDocType.AssignLiteral("window"); // doctype not implemented for XUL at time of writing - causes assertion
|
2003-04-01 22:18:29 +00:00
|
|
|
return NS_OK;
|
2003-04-11 00:56:27 +00:00
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
if (domDoc && NS_SUCCEEDED(domDoc->GetDoctype(getter_AddRefs(docType))) && docType) {
|
2005-06-24 19:16:45 +00:00
|
|
|
return docType->GetPublicId(aDocType);
|
2003-04-01 22:18:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsDocAccessible::GetNameSpaceURIForID(PRInt16 aNameSpaceID, nsAString& aNameSpaceURI)
|
|
|
|
{
|
|
|
|
if (mDocument) {
|
|
|
|
nsCOMPtr<nsINameSpaceManager> nameSpaceManager =
|
|
|
|
do_GetService(NS_NAMESPACEMANAGER_CONTRACTID);
|
2006-04-10 14:25:14 +00:00
|
|
|
if (nameSpaceManager)
|
2003-04-01 22:18:29 +00:00
|
|
|
return nameSpaceManager->GetNameSpaceURI(aNameSpaceID, aNameSpaceURI);
|
|
|
|
}
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
2003-06-16 10:35:11 +00:00
|
|
|
NS_IMETHODIMP nsDocAccessible::GetWindowHandle(void **aWindow)
|
2003-04-01 22:18:29 +00:00
|
|
|
{
|
2010-09-17 03:23:17 +00:00
|
|
|
NS_ENSURE_ARG_POINTER(aWindow);
|
|
|
|
*aWindow = GetNativeWindow();
|
2003-04-01 22:18:29 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2003-06-16 10:35:11 +00:00
|
|
|
NS_IMETHODIMP nsDocAccessible::GetWindow(nsIDOMWindow **aDOMWin)
|
|
|
|
{
|
|
|
|
*aDOMWin = nsnull;
|
|
|
|
if (!mDocument) {
|
|
|
|
return NS_ERROR_FAILURE; // Accessible is Shutdown()
|
|
|
|
}
|
2005-11-28 23:56:44 +00:00
|
|
|
*aDOMWin = mDocument->GetWindow();
|
2003-06-16 10:35:11 +00:00
|
|
|
|
2005-11-28 23:56:44 +00:00
|
|
|
if (!*aDOMWin)
|
2003-06-16 10:35:11 +00:00
|
|
|
return NS_ERROR_FAILURE; // No DOM Window
|
|
|
|
|
2005-11-28 23:56:44 +00:00
|
|
|
NS_ADDREF(*aDOMWin);
|
2003-06-16 10:35:11 +00:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2010-04-27 03:19:49 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDocAccessible::GetDOMDocument(nsIDOMDocument **aDOMDocument)
|
2003-06-16 10:35:11 +00:00
|
|
|
{
|
2010-04-27 03:19:49 +00:00
|
|
|
NS_ENSURE_ARG_POINTER(aDOMDocument);
|
|
|
|
*aDOMDocument = nsnull;
|
2003-06-16 10:35:11 +00:00
|
|
|
|
2010-04-27 03:19:49 +00:00
|
|
|
if (mDocument)
|
|
|
|
CallQueryInterface(mDocument, aDOMDocument);
|
2003-06-16 10:35:11 +00:00
|
|
|
|
2010-04-27 03:19:49 +00:00
|
|
|
return NS_OK;
|
2003-06-16 10:35:11 +00:00
|
|
|
}
|
|
|
|
|
2010-09-09 14:44:56 +00:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDocAccessible::GetParentDocument(nsIAccessibleDocument** aDocument)
|
|
|
|
{
|
|
|
|
NS_ENSURE_ARG_POINTER(aDocument);
|
|
|
|
*aDocument = nsnull;
|
|
|
|
|
|
|
|
if (!IsDefunct())
|
|
|
|
NS_IF_ADDREF(*aDocument = ParentDocument());
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDocAccessible::GetChildDocumentCount(PRUint32* aCount)
|
|
|
|
{
|
|
|
|
NS_ENSURE_ARG_POINTER(aCount);
|
|
|
|
*aCount = 0;
|
|
|
|
|
|
|
|
if (!IsDefunct())
|
|
|
|
*aCount = ChildDocumentCount();
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDocAccessible::GetChildDocumentAt(PRUint32 aIndex,
|
|
|
|
nsIAccessibleDocument** aDocument)
|
|
|
|
{
|
|
|
|
NS_ENSURE_ARG_POINTER(aDocument);
|
|
|
|
*aDocument = nsnull;
|
|
|
|
|
|
|
|
if (IsDefunct())
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
NS_IF_ADDREF(*aDocument = GetChildDocumentAt(aIndex));
|
|
|
|
return *aDocument ? NS_OK : NS_ERROR_INVALID_ARG;
|
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
// nsIAccessibleHyperText method
|
2007-08-14 16:25:24 +00:00
|
|
|
NS_IMETHODIMP nsDocAccessible::GetAssociatedEditor(nsIEditor **aEditor)
|
2006-06-21 13:29:10 +00:00
|
|
|
{
|
2007-08-14 16:25:24 +00:00
|
|
|
NS_ENSURE_ARG_POINTER(aEditor);
|
|
|
|
*aEditor = nsnull;
|
|
|
|
|
2010-06-11 08:23:18 +00:00
|
|
|
if (IsDefunct())
|
2008-10-08 12:50:36 +00:00
|
|
|
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.
|
2010-06-11 08:23:18 +00:00
|
|
|
if (!mDocument->HasFlag(NODE_IS_EDITABLE) &&
|
|
|
|
!mContent->HasFlag(NODE_IS_EDITABLE))
|
|
|
|
return NS_OK;
|
2003-05-15 08:37:38 +00:00
|
|
|
|
2003-10-22 06:09:48 +00:00
|
|
|
nsCOMPtr<nsISupports> container = mDocument->GetContainer();
|
2003-05-15 08:37:38 +00:00
|
|
|
nsCOMPtr<nsIEditingSession> editingSession(do_GetInterface(container));
|
|
|
|
if (!editingSession)
|
2007-08-14 16:25:24 +00:00
|
|
|
return NS_OK; // No editing session interface
|
2003-05-15 08:37:38 +00:00
|
|
|
|
2006-07-19 01:56:54 +00:00
|
|
|
nsCOMPtr<nsIEditor> editor;
|
2007-08-14 16:25:24 +00: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;
|
2003-05-15 08:37:38 +00:00
|
|
|
}
|
|
|
|
|
2010-09-09 14:44:56 +00:00
|
|
|
// nsDocAccessible public method
|
2010-06-12 04:04:35 +00:00
|
|
|
nsAccessible *
|
2010-10-21 04:16:10 +00:00
|
|
|
nsDocAccessible::GetCachedAccessible(nsINode *aNode)
|
2003-04-15 08:45:55 +00:00
|
|
|
{
|
2010-10-21 04:16:10 +00:00
|
|
|
nsAccessible* accessible = mNodeToAccessibleMap.Get(aNode);
|
2010-02-11 13:56:01 +00:00
|
|
|
|
|
|
|
// No accessible in the cache, check if the given ID is unique ID of this
|
2010-06-12 04:04:35 +00:00
|
|
|
// document accessible.
|
|
|
|
if (!accessible) {
|
2010-10-21 04:16:10 +00:00
|
|
|
if (GetNode() != aNode)
|
2010-06-12 04:04:35 +00:00
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
accessible = this;
|
2010-02-11 13:56:01 +00:00
|
|
|
}
|
|
|
|
|
2010-02-09 13:29:22 +00:00
|
|
|
#ifdef DEBUG
|
2006-08-11 17:21:56 +00:00
|
|
|
// 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.
|
2010-06-12 04:04:35 +00:00
|
|
|
nsAccessible* parent(accessible->GetCachedParent());
|
|
|
|
if (parent)
|
|
|
|
parent->TestChildCache(accessible);
|
2006-08-11 17:21:56 +00:00
|
|
|
#endif
|
2010-02-11 13:58:35 +00:00
|
|
|
|
2010-06-12 04:04:35 +00:00
|
|
|
return accessible;
|
2003-04-15 08:45:55 +00:00
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsAccessNode
|
2005-08-10 01:39:43 +00:00
|
|
|
|
2010-06-12 04:04:35 +00:00
|
|
|
PRBool
|
2008-11-01 03:58:07 +00:00
|
|
|
nsDocAccessible::Init()
|
2005-08-10 01:39:43 +00:00
|
|
|
{
|
2010-06-18 02:44:09 +00:00
|
|
|
NS_LOG_ACCDOCCREATE_FOR("document initialize", mDocument, this)
|
|
|
|
|
2011-01-18 08:03:38 +00:00
|
|
|
// Initialize notification controller.
|
|
|
|
nsCOMPtr<nsIPresShell> shell(GetPresShell());
|
|
|
|
mNotificationController = new NotificationController(this, shell);
|
|
|
|
if (!mNotificationController)
|
2010-06-12 04:04:35 +00:00
|
|
|
return PR_FALSE;
|
2010-02-21 00:56:35 +00:00
|
|
|
|
|
|
|
AddEventListeners();
|
|
|
|
|
2010-09-09 14:44:56 +00:00
|
|
|
nsDocAccessible* parentDocument = mParent->GetDocAccessible();
|
|
|
|
if (parentDocument)
|
|
|
|
parentDocument->AppendChildDocument(this);
|
|
|
|
|
2009-07-18 03:09:16 +00:00
|
|
|
// Fire reorder event to notify new accessible document has been created and
|
|
|
|
// attached to the tree.
|
2010-08-25 02:08:28 +00:00
|
|
|
nsRefPtr<AccEvent> reorderEvent =
|
2010-10-21 04:16:10 +00:00
|
|
|
new AccEvent(nsIAccessibleEvent::EVENT_REORDER, mParent, eAutoDetect,
|
|
|
|
AccEvent::eCoalesceFromSameSubtree);
|
|
|
|
if (reorderEvent) {
|
|
|
|
FireDelayedAccessibleEvent(reorderEvent);
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
2009-07-18 03:09:16 +00:00
|
|
|
|
2010-10-21 04:16:10 +00:00
|
|
|
return PR_FALSE;
|
2006-04-10 14:25:14 +00:00
|
|
|
}
|
2003-05-15 08:37:38 +00:00
|
|
|
|
2010-06-12 04:04:35 +00:00
|
|
|
void
|
2008-11-01 03:58:07 +00:00
|
|
|
nsDocAccessible::Shutdown()
|
2003-04-01 22:18:29 +00:00
|
|
|
{
|
2010-06-12 04:04:35 +00:00
|
|
|
if (!mWeakShell) // already shutdown
|
|
|
|
return;
|
2003-04-15 08:45:55 +00:00
|
|
|
|
2010-06-08 16:39:58 +00:00
|
|
|
NS_LOG_ACCDOCDESTROY_FOR("document shutdown", mDocument, this)
|
|
|
|
|
2011-01-18 08:03:38 +00:00
|
|
|
if (mNotificationController) {
|
|
|
|
mNotificationController->Shutdown();
|
|
|
|
mNotificationController = nsnull;
|
2010-02-26 19:02:39 +00:00
|
|
|
}
|
2010-01-27 11:42:08 +00:00
|
|
|
|
2003-05-15 08:37:38 +00:00
|
|
|
RemoveEventListeners();
|
|
|
|
|
2010-09-09 14:44:56 +00:00
|
|
|
if (mParent) {
|
|
|
|
nsDocAccessible* parentDocument = mParent->GetDocAccessible();
|
|
|
|
if (parentDocument)
|
|
|
|
parentDocument->RemoveChildDocument(this);
|
|
|
|
|
2010-06-08 16:39:58 +00:00
|
|
|
mParent->RemoveChild(this);
|
2010-09-09 14:44:56 +00:00
|
|
|
}
|
|
|
|
|
2010-11-09 19:34:25 +00:00
|
|
|
// Walk the array backwards because child documents remove themselves from the
|
|
|
|
// array as they are shutdown.
|
|
|
|
PRInt32 childDocCount = mChildDocuments.Length();
|
|
|
|
for (PRInt32 idx = childDocCount - 1; idx >= 0; idx--)
|
2010-10-28 09:34:26 +00:00
|
|
|
mChildDocuments[idx]->Shutdown();
|
|
|
|
|
2010-09-09 14:44:56 +00:00
|
|
|
mChildDocuments.Clear();
|
2010-06-08 16:39:58 +00:00
|
|
|
|
2003-04-15 08:45:55 +00:00
|
|
|
mWeakShell = nsnull; // Avoid reentrancy
|
|
|
|
|
2010-11-18 02:55:44 +00:00
|
|
|
mDependentIDsHash.Clear();
|
2010-10-21 04:16:10 +00:00
|
|
|
mNodeToAccessibleMap.Clear();
|
2010-06-12 04:04:35 +00:00
|
|
|
ClearCache(mAccessibleCache);
|
2007-12-27 05:13:40 +00:00
|
|
|
|
2008-02-26 08:51:10 +00:00
|
|
|
nsCOMPtr<nsIDocument> kungFuDeathGripDoc = mDocument;
|
2007-12-27 05:13:40 +00:00
|
|
|
mDocument = nsnull;
|
|
|
|
|
|
|
|
nsHyperTextAccessibleWrap::Shutdown();
|
2010-10-28 09:34:26 +00:00
|
|
|
|
|
|
|
GetAccService()->NotifyOfDocumentShutdown(kungFuDeathGripDoc);
|
2003-04-01 22:18:29 +00:00
|
|
|
}
|
|
|
|
|
2008-11-01 03:58:07 +00:00
|
|
|
nsIFrame*
|
|
|
|
nsDocAccessible::GetFrame()
|
2003-04-01 22:18:29 +00:00
|
|
|
{
|
2003-04-15 08:45:55 +00:00
|
|
|
nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mWeakShell));
|
2003-04-01 22:18:29 +00:00
|
|
|
|
|
|
|
nsIFrame* root = nsnull;
|
2006-04-10 14:25:14 +00:00
|
|
|
if (shell)
|
2004-09-02 03:08:51 +00:00
|
|
|
root = shell->GetRootFrame();
|
2003-04-01 22:18:29 +00:00
|
|
|
|
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
2009-06-25 02:08:53 +00:00
|
|
|
PRBool
|
|
|
|
nsDocAccessible::IsDefunct()
|
|
|
|
{
|
2010-06-11 08:23:18 +00:00
|
|
|
return nsHyperTextAccessibleWrap::IsDefunct() || !mDocument;
|
2009-06-25 02:08:53 +00:00
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
// nsDocAccessible protected member
|
2003-07-31 08:09:39 +00:00
|
|
|
void nsDocAccessible::GetBoundsRect(nsRect& aBounds, nsIFrame** aRelativeFrame)
|
2003-04-01 22:18:29 +00:00
|
|
|
{
|
|
|
|
*aRelativeFrame = GetFrame();
|
2003-05-19 09:07:41 +00:00
|
|
|
|
2003-10-22 06:09:48 +00:00
|
|
|
nsIDocument *document = mDocument;
|
|
|
|
nsIDocument *parentDoc = nsnull;
|
2003-05-19 09:07:41 +00:00
|
|
|
|
|
|
|
while (document) {
|
2010-06-25 13:59:57 +00:00
|
|
|
nsIPresShell *presShell = document->GetShell();
|
2003-05-19 09:07:41 +00:00
|
|
|
if (!presShell) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-09-03 03:57:41 +00:00
|
|
|
nsRect scrollPort;
|
|
|
|
nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollableExternal();
|
|
|
|
if (sf) {
|
|
|
|
scrollPort = sf->GetScrollPortRect();
|
|
|
|
} else {
|
2010-01-21 01:07:35 +00:00
|
|
|
nsIFrame* rootFrame = presShell->GetRootFrame();
|
|
|
|
if (!rootFrame) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
scrollPort = rootFrame->GetRect();
|
2003-05-19 09:07:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (parentDoc) { // After first time thru loop
|
2009-09-03 03:57:41 +00:00
|
|
|
// XXXroc bogus code! scrollPort is relative to the viewport of
|
|
|
|
// this document, but we're intersecting rectangles derived from
|
|
|
|
// multiple documents and assuming they're all in the same coordinate
|
|
|
|
// system. See bug 514117.
|
|
|
|
aBounds.IntersectRect(scrollPort, aBounds);
|
2003-05-19 09:07:41 +00:00
|
|
|
}
|
|
|
|
else { // First time through loop
|
2009-09-03 03:57:41 +00:00
|
|
|
aBounds = scrollPort;
|
2003-05-19 09:07:41 +00:00
|
|
|
}
|
|
|
|
|
2003-10-22 06:09:48 +00:00
|
|
|
document = parentDoc = document->GetParentDocument();
|
2003-05-19 09:07:41 +00:00
|
|
|
}
|
2003-04-01 22:18:29 +00:00
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
// nsDocAccessible protected member
|
2003-07-22 14:55:22 +00:00
|
|
|
nsresult nsDocAccessible::AddEventListeners()
|
2003-04-15 08:45:55 +00:00
|
|
|
{
|
|
|
|
// 1) Set up scroll position listener
|
2005-08-30 22:37:16 +00:00
|
|
|
// 2) Check for editor and listen for changes to editor
|
2003-04-15 08:45:55 +00:00
|
|
|
|
|
|
|
nsCOMPtr<nsIPresShell> presShell(GetPresShell());
|
2003-07-22 14:55:22 +00:00
|
|
|
NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
|
2003-04-15 08:45:55 +00:00
|
|
|
|
2003-10-22 06:09:48 +00:00
|
|
|
nsCOMPtr<nsISupports> container = mDocument->GetContainer();
|
2003-04-15 08:45:55 +00:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(do_QueryInterface(container));
|
2003-07-22 14:55:22 +00:00
|
|
|
NS_ENSURE_TRUE(docShellTreeItem, NS_ERROR_FAILURE);
|
2003-04-15 08:45:55 +00:00
|
|
|
|
|
|
|
// Make sure we're a content docshell
|
|
|
|
// We don't want to listen to chrome progress
|
|
|
|
PRInt32 itemType;
|
|
|
|
docShellTreeItem->GetItemType(&itemType);
|
|
|
|
|
2003-06-26 08:45:39 +00:00
|
|
|
PRBool isContent = (itemType == nsIDocShellTreeItem::typeContent);
|
2003-04-15 08:45:55 +00:00
|
|
|
|
2003-06-26 08:45:39 +00:00
|
|
|
if (isContent) {
|
2007-08-14 16:25:24 +00: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");
|
2003-05-15 08:37:38 +00:00
|
|
|
}
|
2005-03-14 16:31:43 +00:00
|
|
|
}
|
2003-04-15 08:45:55 +00:00
|
|
|
|
2007-05-07 18:55:17 +00:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
|
|
|
|
docShellTreeItem->GetRootTreeItem(getter_AddRefs(rootTreeItem));
|
|
|
|
if (rootTreeItem) {
|
2010-06-08 16:39:58 +00:00
|
|
|
nsRefPtr<nsRootAccessible> rootAccessible = GetRootAccessible();
|
2007-06-14 17:12:50 +00:00
|
|
|
NS_ENSURE_TRUE(rootAccessible, NS_ERROR_FAILURE);
|
|
|
|
nsRefPtr<nsCaretAccessible> caretAccessible = rootAccessible->GetCaretAccessible();
|
|
|
|
if (caretAccessible) {
|
2008-01-29 04:38:18 +00:00
|
|
|
caretAccessible->AddDocSelectionListener(presShell);
|
2007-05-07 18:55:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-06-10 13:57:27 +00:00
|
|
|
// add document observer
|
|
|
|
mDocument->AddObserver(this);
|
|
|
|
return NS_OK;
|
2003-04-15 08:45:55 +00:00
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
// nsDocAccessible protected member
|
2003-07-22 14:55:22 +00:00
|
|
|
nsresult nsDocAccessible::RemoveEventListeners()
|
2003-04-15 08:45:55 +00:00
|
|
|
{
|
|
|
|
// Remove listeners associated with content documents
|
|
|
|
// Remove scroll position listener
|
2004-11-08 02:29:47 +00:00
|
|
|
RemoveScrollListener();
|
2003-04-28 10:24:52 +00:00
|
|
|
|
2009-06-28 17:45:04 +00: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");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2003-05-15 08:37:38 +00:00
|
|
|
|
2003-06-16 10:22:40 +00:00
|
|
|
if (mScrollWatchTimer) {
|
|
|
|
mScrollWatchTimer->Cancel();
|
|
|
|
mScrollWatchTimer = nsnull;
|
2008-01-18 20:36:44 +00:00
|
|
|
NS_RELEASE_THIS(); // Kung fu death grip
|
2003-06-16 10:22:40 +00:00
|
|
|
}
|
|
|
|
|
2007-05-07 18:55:17 +00:00
|
|
|
nsRefPtr<nsRootAccessible> rootAccessible(GetRootAccessible());
|
|
|
|
if (rootAccessible) {
|
2007-06-14 17:12:50 +00:00
|
|
|
nsRefPtr<nsCaretAccessible> caretAccessible = rootAccessible->GetCaretAccessible();
|
|
|
|
if (caretAccessible) {
|
2008-01-30 05:42:44 +00:00
|
|
|
// Don't use GetPresShell() which can call Shutdown() if it sees dead pres shell
|
|
|
|
nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell));
|
2008-01-29 04:38:18 +00:00
|
|
|
caretAccessible->RemoveDocSelectionListener(presShell);
|
2007-05-07 18:55:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-07-22 14:55:22 +00:00
|
|
|
return NS_OK;
|
2003-04-15 08:45:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void nsDocAccessible::ScrollTimerCallback(nsITimer *aTimer, void *aClosure)
|
|
|
|
{
|
2007-07-08 07:08:04 +00:00
|
|
|
nsDocAccessible *docAcc = reinterpret_cast<nsDocAccessible*>(aClosure);
|
2003-04-15 08:45:55 +00:00
|
|
|
|
2006-04-10 14:25:14 +00:00
|
|
|
if (docAcc && docAcc->mScrollPositionChangedTicks &&
|
2003-04-15 08:45:55 +00:00
|
|
|
++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
|
2010-01-18 16:16:07 +00:00
|
|
|
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SCROLLING_END, docAcc);
|
2007-07-05 16:02:55 +00:00
|
|
|
|
2003-04-15 08:45:55 +00:00
|
|
|
docAcc->mScrollPositionChangedTicks = 0;
|
2003-05-01 10:25:45 +00:00
|
|
|
if (docAcc->mScrollWatchTimer) {
|
|
|
|
docAcc->mScrollWatchTimer->Cancel();
|
|
|
|
docAcc->mScrollWatchTimer = nsnull;
|
2008-01-18 20:36:44 +00:00
|
|
|
NS_RELEASE(docAcc); // Release kung fu death grip
|
2003-05-01 10:25:45 +00:00
|
|
|
}
|
2003-04-15 08:45:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
// nsDocAccessible protected member
|
2004-11-08 02:29:47 +00:00
|
|
|
void nsDocAccessible::AddScrollListener()
|
2003-04-15 08:45:55 +00:00
|
|
|
{
|
2004-11-08 02:29:47 +00:00
|
|
|
nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell));
|
2009-09-03 03:57:41 +00:00
|
|
|
if (!presShell)
|
|
|
|
return;
|
2004-11-08 02:29:47 +00:00
|
|
|
|
2009-09-03 03:57:41 +00:00
|
|
|
nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollableExternal();
|
|
|
|
if (sf) {
|
|
|
|
sf->AddScrollPositionListener(this);
|
2010-06-08 16:39:58 +00:00
|
|
|
NS_LOG_ACCDOCCREATE_TEXT("add scroll listener")
|
2009-09-03 03:57:41 +00:00
|
|
|
}
|
2003-04-15 08:45:55 +00:00
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
// nsDocAccessible protected member
|
2004-11-08 02:29:47 +00:00
|
|
|
void nsDocAccessible::RemoveScrollListener()
|
2003-04-15 08:45:55 +00:00
|
|
|
{
|
2004-11-08 02:29:47 +00:00
|
|
|
nsCOMPtr<nsIPresShell> presShell(do_QueryReferent(mWeakShell));
|
2009-09-03 03:57:41 +00:00
|
|
|
if (!presShell)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollableExternal();
|
|
|
|
if (sf) {
|
|
|
|
sf->RemoveScrollPositionListener(this);
|
|
|
|
}
|
2003-04-15 08:45:55 +00:00
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsIScrollPositionListener
|
|
|
|
|
2010-01-29 00:03:42 +00:00
|
|
|
void nsDocAccessible::ScrollPositionDidChange(nscoord aX, nscoord aY)
|
2003-04-15 08:45:55 +00:00
|
|
|
{
|
2006-04-10 14:25:14 +00:00
|
|
|
// Start new timer, if the timer cycles at least 1 full cycle without more scroll position changes,
|
2003-04-15 08:45:55 +00:00
|
|
|
// 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 20:36:44 +00:00
|
|
|
NS_ADDREF_THIS(); // Kung fu death grip
|
2003-04-15 08:45:55 +00:00
|
|
|
mScrollWatchTimer->InitWithFuncCallback(ScrollTimerCallback, this,
|
2006-04-10 14:25:14 +00:00
|
|
|
kScrollPosCheckWait,
|
2003-04-15 08:45:55 +00:00
|
|
|
nsITimer::TYPE_REPEATING_SLACK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mScrollPositionChangedTicks = 1;
|
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsIObserver
|
|
|
|
|
2003-05-15 08:37:38 +00:00
|
|
|
NS_IMETHODIMP nsDocAccessible::Observe(nsISupports *aSubject, const char *aTopic,
|
|
|
|
const PRUnichar *aData)
|
|
|
|
{
|
2007-08-14 16:25:24 +00:00
|
|
|
if (!nsCRT::strcmp(aTopic,"obs_documentCreated")) {
|
|
|
|
// State editable will now be set, readonly is now clear
|
2010-06-02 12:30:08 +00:00
|
|
|
// Normally we only fire delayed events created from the node, not an
|
2010-08-25 02:08:28 +00:00
|
|
|
// accessible object. See the AccStateChangeEvent constructor for details
|
2010-06-02 12:30:08 +00:00
|
|
|
// about this exceptional case.
|
2010-08-25 02:08:28 +00:00
|
|
|
nsRefPtr<AccEvent> event =
|
|
|
|
new AccStateChangeEvent(this, nsIAccessibleStates::EXT_STATE_EDITABLE,
|
|
|
|
PR_TRUE, PR_TRUE);
|
2010-06-02 12:30:08 +00:00
|
|
|
FireDelayedAccessibleEvent(event);
|
2003-05-15 08:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2005-06-10 13:57:27 +00:00
|
|
|
// nsIDocumentObserver
|
2003-04-28 10:24:52 +00:00
|
|
|
|
2005-06-10 13:57:27 +00:00
|
|
|
NS_IMPL_NSIDOCUMENTOBSERVER_CORE_STUB(nsDocAccessible)
|
|
|
|
NS_IMPL_NSIDOCUMENTOBSERVER_LOAD_STUB(nsDocAccessible)
|
|
|
|
NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(nsDocAccessible)
|
2003-04-28 10:24:52 +00:00
|
|
|
|
2009-06-29 18:36:25 +00:00
|
|
|
void
|
|
|
|
nsDocAccessible::AttributeWillChange(nsIDocument *aDocument,
|
2010-08-24 07:06:20 +00:00
|
|
|
dom::Element* aElement,
|
|
|
|
PRInt32 aNameSpaceID,
|
2009-06-29 18:36:25 +00:00
|
|
|
nsIAtom* aAttribute, PRInt32 aModType)
|
|
|
|
{
|
2010-11-20 02:37:18 +00:00
|
|
|
// XXX TODO: bugs 467143, 472142, 472143.
|
2009-06-29 18:36:25 +00:00
|
|
|
// 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.
|
2010-11-18 02:55:44 +00:00
|
|
|
|
2010-12-16 19:29:51 +00:00
|
|
|
// Update dependent IDs cache. Take care of elements that are accessible
|
|
|
|
// because dependent IDs cache doesn't contain IDs from non accessible
|
|
|
|
// elements.
|
2010-11-18 02:55:44 +00:00
|
|
|
if (aModType == nsIDOMMutationEvent::MODIFICATION ||
|
|
|
|
aModType == nsIDOMMutationEvent::REMOVAL) {
|
2010-12-16 19:29:51 +00:00
|
|
|
nsAccessible* accessible = GetCachedAccessible(aElement);
|
2010-11-18 02:55:44 +00:00
|
|
|
if (accessible)
|
|
|
|
RemoveDependentIDsFor(accessible, aAttribute);
|
|
|
|
}
|
2009-06-29 18:36:25 +00:00
|
|
|
}
|
|
|
|
|
2005-06-10 13:57:27 +00:00
|
|
|
void
|
2010-08-24 07:05:56 +00:00
|
|
|
nsDocAccessible::AttributeChanged(nsIDocument *aDocument,
|
|
|
|
dom::Element* aElement,
|
2005-06-10 13:57:27 +00:00
|
|
|
PRInt32 aNameSpaceID, nsIAtom* aAttribute,
|
2009-12-10 22:36:04 +00:00
|
|
|
PRInt32 aModType)
|
2007-09-18 21:36:41 +00:00
|
|
|
{
|
2011-01-18 04:15:05 +00:00
|
|
|
NS_ASSERTION(!IsDefunct(),
|
|
|
|
"Attribute changed called on defunct document accessible!");
|
|
|
|
|
2010-12-18 18:33:00 +00:00
|
|
|
// Proceed even if the element is not accessible because element may become
|
|
|
|
// accessible if it gets certain attribute.
|
|
|
|
if (UpdateAccessibleOnAttrChange(aElement, aAttribute))
|
|
|
|
return;
|
2007-09-18 21:36:41 +00:00
|
|
|
|
2010-12-18 18:33:00 +00:00
|
|
|
// Ignore attribute change if the element doesn't have an accessible (at all
|
|
|
|
// or still) iff the element is not a root content of this document accessible
|
|
|
|
// (which is treated as attribute change on this document accessible).
|
2011-01-18 04:15:05 +00:00
|
|
|
// Note: we don't bail if all the content hasn't finished loading because
|
|
|
|
// these attributes are changing for a loaded part of the content.
|
2010-12-16 19:29:51 +00:00
|
|
|
nsAccessible* accessible = GetCachedAccessible(aElement);
|
2010-12-18 18:33:00 +00:00
|
|
|
if (!accessible && (mContent != aElement))
|
2010-12-16 19:29:51 +00:00
|
|
|
return;
|
|
|
|
|
2010-12-18 18:33:00 +00:00
|
|
|
// Fire accessible events iff there's an accessible, otherwise we consider
|
|
|
|
// the accessible state wasn't changed, i.e. its state is initial state.
|
|
|
|
AttributeChangedImpl(aElement, aNameSpaceID, aAttribute);
|
|
|
|
|
2010-12-16 19:29:51 +00:00
|
|
|
// Update dependent IDs cache. Take care of accessible elements because no
|
|
|
|
// accessible element means either the element is not accessible at all or
|
|
|
|
// its accessible will be created later. It doesn't make sense to keep
|
|
|
|
// dependent IDs for non accessible elements. For the second case we'll update
|
|
|
|
// dependent IDs cache when its accessible is created.
|
2010-11-18 02:55:44 +00:00
|
|
|
if (aModType == nsIDOMMutationEvent::MODIFICATION ||
|
|
|
|
aModType == nsIDOMMutationEvent::ADDITION) {
|
2010-12-16 19:29:51 +00:00
|
|
|
AddDependentIDsFor(accessible, aAttribute);
|
2010-11-18 02:55:44 +00:00
|
|
|
}
|
|
|
|
|
2010-12-16 19:29:51 +00:00
|
|
|
// If it was the focused node, cache the new state.
|
|
|
|
if (aElement == gLastFocusedNode)
|
|
|
|
gLastFocusedAccessiblesState = nsAccUtils::State(accessible);
|
2007-09-18 21:36:41 +00:00
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
// nsDocAccessible protected member
|
2007-09-18 21:36:41 +00:00
|
|
|
void
|
|
|
|
nsDocAccessible::AttributeChangedImpl(nsIContent* aContent, PRInt32 aNameSpaceID, nsIAtom* aAttribute)
|
2003-04-28 10:24:52 +00:00
|
|
|
{
|
2007-04-17 04:45:42 +00: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.
|
|
|
|
|
2003-04-28 10:24:52 +00:00
|
|
|
// XXX todo
|
2005-01-28 02:35:26 +00:00
|
|
|
// We still need to handle special HTML cases here
|
2003-04-28 10:24:52 +00:00
|
|
|
// For example, if an <img>'s usemap attribute is modified
|
|
|
|
// Otherwise it may just be a state change, for example an object changing
|
2005-01-28 02:35:26 +00:00
|
|
|
// its visibility
|
2009-01-12 17:20:34 +00: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
|
2010-12-16 19:29:51 +00:00
|
|
|
|
2009-09-16 01:01:47 +00:00
|
|
|
// Universal boolean properties that don't require a role. Fire the state
|
|
|
|
// change when disabled or aria-disabled attribute is set.
|
2007-09-25 01:19:03 +00:00
|
|
|
if (aAttribute == nsAccessibilityAtoms::disabled ||
|
2007-12-12 02:10:26 +00:00
|
|
|
aAttribute == nsAccessibilityAtoms::aria_disabled) {
|
2009-09-16 01:01:47 +00:00
|
|
|
|
|
|
|
// Note. Checking the XUL or HTML namespace would not seem to gain us
|
|
|
|
// anything, because disabled attribute really is going to mean the same
|
|
|
|
// thing in any namespace.
|
|
|
|
|
|
|
|
// Note. We use the attribute instead of the disabled state bit because
|
|
|
|
// ARIA's aria-disabled does not affect the disabled state bit.
|
|
|
|
|
2010-08-25 02:08:28 +00:00
|
|
|
nsRefPtr<AccEvent> enabledChangeEvent =
|
|
|
|
new AccStateChangeEvent(aContent,
|
|
|
|
nsIAccessibleStates::EXT_STATE_ENABLED,
|
|
|
|
PR_TRUE);
|
2009-09-16 01:01:47 +00:00
|
|
|
|
2007-04-19 17:49:13 +00:00
|
|
|
FireDelayedAccessibleEvent(enabledChangeEvent);
|
2009-09-16 01:01:47 +00:00
|
|
|
|
2010-08-25 02:08:28 +00:00
|
|
|
nsRefPtr<AccEvent> sensitiveChangeEvent =
|
|
|
|
new AccStateChangeEvent(aContent,
|
|
|
|
nsIAccessibleStates::EXT_STATE_SENSITIVE,
|
|
|
|
PR_TRUE);
|
2009-09-16 01:01:47 +00:00
|
|
|
|
2007-04-19 17:49:13 +00:00
|
|
|
FireDelayedAccessibleEvent(sensitiveChangeEvent);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-09-25 01:19:03 +00:00
|
|
|
// Check for namespaced ARIA attribute
|
2007-12-12 02:10:26 +00:00
|
|
|
if (aNameSpaceID == kNameSpaceID_None) {
|
2007-09-25 01:19:03 +00:00
|
|
|
// Check for hyphenated aria-foo property?
|
2010-03-08 15:45:00 +00:00
|
|
|
if (StringBeginsWith(nsDependentAtomString(aAttribute),
|
|
|
|
NS_LITERAL_STRING("aria-"))) {
|
2007-12-12 02:10:26 +00:00
|
|
|
ARIAAttributeChanged(aContent, aAttribute);
|
2007-09-25 01:19:03 +00:00
|
|
|
}
|
|
|
|
}
|
2007-04-19 17:49:13 +00:00
|
|
|
|
2008-02-20 07:45:14 +00:00
|
|
|
if (aAttribute == nsAccessibilityAtoms::alt ||
|
2009-05-14 05:27:40 +00:00
|
|
|
aAttribute == nsAccessibilityAtoms::title ||
|
|
|
|
aAttribute == nsAccessibilityAtoms::aria_label ||
|
|
|
|
aAttribute == nsAccessibilityAtoms::aria_labelledby) {
|
2009-09-03 02:01:18 +00:00
|
|
|
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE,
|
2010-06-11 08:23:18 +00:00
|
|
|
aContent);
|
2008-02-20 07:45:14 +00:00
|
|
|
return;
|
|
|
|
}
|
2005-08-10 01:51:39 +00:00
|
|
|
|
2007-12-12 02:10:26 +00:00
|
|
|
if (aAttribute == nsAccessibilityAtoms::selected ||
|
|
|
|
aAttribute == nsAccessibilityAtoms::aria_selected) {
|
|
|
|
// ARIA or XUL selection
|
2010-06-11 08:23:18 +00:00
|
|
|
|
|
|
|
nsAccessible *multiSelect =
|
|
|
|
nsAccUtils::GetMultiSelectableContainer(aContent);
|
2005-07-14 14:20:21 +00: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
|
2009-09-03 02:01:18 +00:00
|
|
|
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_SELECTION_WITHIN,
|
2010-06-11 08:23:18 +00:00
|
|
|
multiSelect->GetNode(),
|
2010-08-25 02:08:28 +00:00
|
|
|
AccEvent::eAllowDupes);
|
2007-04-17 04:45:42 +00:00
|
|
|
|
2006-04-24 05:40:11 +00:00
|
|
|
static nsIContent::AttrValuesArray strings[] =
|
|
|
|
{&nsAccessibilityAtoms::_empty, &nsAccessibilityAtoms::_false, nsnull};
|
2007-12-12 02:10:26 +00:00
|
|
|
if (aContent->FindAttrValueIn(kNameSpaceID_None, aAttribute,
|
|
|
|
strings, eCaseMatters) >= 0) {
|
2009-09-03 02:01:18 +00:00
|
|
|
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_SELECTION_REMOVE,
|
2010-06-11 08:23:18 +00:00
|
|
|
aContent);
|
2007-04-17 04:45:42 +00:00
|
|
|
return;
|
2005-07-14 14:20:21 +00:00
|
|
|
}
|
2007-04-17 04:45:42 +00:00
|
|
|
|
2009-09-03 02:01:18 +00:00
|
|
|
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_SELECTION_ADD,
|
2010-06-11 08:23:18 +00:00
|
|
|
aContent);
|
2005-07-14 14:20:21 +00:00
|
|
|
}
|
|
|
|
}
|
2007-08-14 16:25:24 +00:00
|
|
|
|
|
|
|
if (aAttribute == nsAccessibilityAtoms::contenteditable) {
|
2010-08-25 02:08:28 +00:00
|
|
|
nsRefPtr<AccEvent> editableChangeEvent =
|
|
|
|
new AccStateChangeEvent(aContent,
|
|
|
|
nsIAccessibleStates::EXT_STATE_EDITABLE,
|
|
|
|
PR_TRUE);
|
2007-08-14 16:25:24 +00:00
|
|
|
FireDelayedAccessibleEvent(editableChangeEvent);
|
|
|
|
return;
|
|
|
|
}
|
2007-04-17 04:45:42 +00:00
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
// nsDocAccessible protected member
|
2007-04-17 04:45:42 +00:00
|
|
|
void
|
|
|
|
nsDocAccessible::ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute)
|
|
|
|
{
|
2010-04-02 13:33:55 +00:00
|
|
|
// Note: For universal/global ARIA states and properties we don't care if
|
|
|
|
// there is an ARIA role present or not.
|
|
|
|
|
2007-12-12 02:10:26 +00:00
|
|
|
if (aAttribute == nsAccessibilityAtoms::aria_required) {
|
2010-08-25 02:08:28 +00:00
|
|
|
nsRefPtr<AccEvent> event =
|
|
|
|
new AccStateChangeEvent(aContent, nsIAccessibleStates::STATE_REQUIRED,
|
|
|
|
PR_FALSE);
|
2007-04-17 04:45:42 +00:00
|
|
|
FireDelayedAccessibleEvent(event);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-12-12 02:10:26 +00:00
|
|
|
if (aAttribute == nsAccessibilityAtoms::aria_invalid) {
|
2010-08-25 02:08:28 +00:00
|
|
|
nsRefPtr<AccEvent> event =
|
|
|
|
new AccStateChangeEvent(aContent, nsIAccessibleStates::STATE_INVALID,
|
|
|
|
PR_FALSE);
|
2007-04-17 04:45:42 +00:00
|
|
|
FireDelayedAccessibleEvent(event);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-12-12 02:10:26 +00:00
|
|
|
if (aAttribute == nsAccessibilityAtoms::aria_activedescendant) {
|
2007-04-17 04:45:42 +00:00
|
|
|
// The activedescendant universal property redirects accessible focus events
|
|
|
|
// to the element with the id that activedescendant points to
|
2010-06-11 08:23:18 +00:00
|
|
|
nsCOMPtr<nsINode> focusedNode = GetCurrentFocus();
|
|
|
|
if (nsCoreUtils::GetRoleContent(focusedNode) == aContent) {
|
2010-12-12 01:47:30 +00:00
|
|
|
nsAccessible* focusedAcc = GetAccService()->GetAccessible(focusedNode);
|
2007-04-17 04:45:42 +00:00
|
|
|
nsRefPtr<nsRootAccessible> rootAcc = GetRootAccessible();
|
2010-12-12 01:47:30 +00:00
|
|
|
if (rootAcc && focusedAcc) {
|
|
|
|
rootAcc->FireAccessibleFocusEvent(focusedAcc, nsnull, PR_TRUE);
|
2010-06-15 14:53:36 +00:00
|
|
|
}
|
2005-07-14 14:20:21 +00:00
|
|
|
}
|
2007-04-17 04:45:42 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-04-02 13:33:55 +00:00
|
|
|
// For aria drag and drop changes we fire a generic attribute change event;
|
|
|
|
// at least until native API comes up with a more meaningful event.
|
|
|
|
if (aAttribute == nsAccessibilityAtoms::aria_grabbed ||
|
|
|
|
aAttribute == nsAccessibilityAtoms::aria_dropeffect) {
|
|
|
|
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED,
|
2010-06-11 08:23:18 +00:00
|
|
|
aContent);
|
2010-04-02 13:33:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// We treat aria-expanded as a global ARIA state for historical reasons
|
|
|
|
if (aAttribute == nsAccessibilityAtoms::aria_expanded) {
|
2010-08-25 02:08:28 +00:00
|
|
|
nsRefPtr<AccEvent> event =
|
|
|
|
new AccStateChangeEvent(aContent, nsIAccessibleStates::STATE_EXPANDED,
|
|
|
|
PR_FALSE);
|
2010-04-02 13:33:55 +00:00
|
|
|
FireDelayedAccessibleEvent(event);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-12-12 02:10:26 +00:00
|
|
|
if (!aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::role)) {
|
2007-04-17 04:45:42 +00: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-12 02:10:26 +00: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 21:36:41 +00:00
|
|
|
nsIAccessibleStates::STATE_CHECKED :
|
|
|
|
nsIAccessibleStates::STATE_PRESSED;
|
2010-08-25 02:08:28 +00:00
|
|
|
nsRefPtr<AccEvent> event =
|
|
|
|
new AccStateChangeEvent(aContent, kState, PR_FALSE);
|
2007-08-14 16:15:12 +00:00
|
|
|
FireDelayedAccessibleEvent(event);
|
2010-06-11 08:23:18 +00:00
|
|
|
if (aContent == gLastFocusedNode) {
|
2007-09-18 21:36:41 +00:00
|
|
|
// 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-12 02:10:26 +00:00
|
|
|
// This is because we don't know if the previous value of aria-checked or aria-pressed was "mixed"
|
2007-09-18 21:36:41 +00:00
|
|
|
// without caching that info.
|
2010-06-12 04:04:24 +00:00
|
|
|
nsAccessible *accessible = event->GetAccessible();
|
2007-09-18 21:36:41 +00:00
|
|
|
if (accessible) {
|
|
|
|
PRBool wasMixed = (gLastFocusedAccessiblesState & nsIAccessibleStates::STATE_MIXED) != 0;
|
2008-10-17 10:10:43 +00:00
|
|
|
PRBool isMixed =
|
|
|
|
(nsAccUtils::State(accessible) & nsIAccessibleStates::STATE_MIXED) != 0;
|
2007-09-18 21:36:41 +00:00
|
|
|
if (wasMixed != isMixed) {
|
2010-08-25 02:08:28 +00:00
|
|
|
nsRefPtr<AccEvent> event =
|
|
|
|
new AccStateChangeEvent(aContent, nsIAccessibleStates::STATE_MIXED,
|
|
|
|
PR_FALSE, isMixed);
|
2007-09-18 21:36:41 +00:00
|
|
|
FireDelayedAccessibleEvent(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-08-14 16:15:12 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-12-12 02:10:26 +00:00
|
|
|
if (aAttribute == nsAccessibilityAtoms::aria_readonly) {
|
2010-08-25 02:08:28 +00:00
|
|
|
nsRefPtr<AccEvent> event =
|
|
|
|
new AccStateChangeEvent(aContent, nsIAccessibleStates::STATE_READONLY,
|
|
|
|
PR_FALSE);
|
2007-04-17 04:45:42 +00:00
|
|
|
FireDelayedAccessibleEvent(event);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-06-11 19:57:29 +00: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)))) {
|
2009-09-03 02:01:18 +00:00
|
|
|
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
|
2010-06-11 08:23:18 +00:00
|
|
|
aContent);
|
2007-04-17 04:45:42 +00:00
|
|
|
return;
|
2005-01-28 02:35:26 +00:00
|
|
|
}
|
2005-06-10 13:57:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void nsDocAccessible::ContentAppended(nsIDocument *aDocument,
|
|
|
|
nsIContent* aContainer,
|
2010-05-11 01:12:34 +00:00
|
|
|
nsIContent* aFirstNewContent,
|
|
|
|
PRInt32 /* unused */)
|
2005-06-10 13:57:27 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2005-07-25 21:40:31 +00:00
|
|
|
void nsDocAccessible::ContentStatesChanged(nsIDocument* aDocument,
|
|
|
|
nsIContent* aContent1,
|
|
|
|
nsIContent* aContent2,
|
2010-10-20 11:26:32 +00:00
|
|
|
nsEventStates aStateMask)
|
2005-07-25 21:40:31 +00:00
|
|
|
{
|
2010-11-08 13:33:18 +00:00
|
|
|
if (aStateMask.HasState(NS_EVENT_STATE_CHECKED)) {
|
|
|
|
nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent1);
|
|
|
|
nsHTMLSelectOptionAccessible::SelectionChangedIfOption(aContent2);
|
2005-07-25 21:40:31 +00:00
|
|
|
}
|
|
|
|
|
2010-11-08 13:33:18 +00:00
|
|
|
if (aStateMask.HasState(NS_EVENT_STATE_INVALID)) {
|
|
|
|
nsRefPtr<AccEvent> event =
|
|
|
|
new AccStateChangeEvent(aContent1, nsIAccessibleStates::STATE_INVALID,
|
|
|
|
PR_FALSE, PR_TRUE);
|
|
|
|
FireDelayedAccessibleEvent(event);
|
|
|
|
}
|
2005-07-25 21:40:31 +00:00
|
|
|
}
|
|
|
|
|
2010-03-17 17:10:57 +00:00
|
|
|
void nsDocAccessible::DocumentStatesChanged(nsIDocument* aDocument,
|
2010-10-20 11:26:32 +00:00
|
|
|
nsEventStates aStateMask)
|
2010-03-17 17:10:57 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2007-09-05 08:22:17 +00:00
|
|
|
void nsDocAccessible::CharacterDataWillChange(nsIDocument *aDocument,
|
|
|
|
nsIContent* aContent,
|
|
|
|
CharacterDataChangeInfo* aInfo)
|
|
|
|
{
|
|
|
|
FireTextChangeEventForText(aContent, aInfo, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
2005-06-10 13:57:27 +00:00
|
|
|
void nsDocAccessible::CharacterDataChanged(nsIDocument *aDocument,
|
|
|
|
nsIContent* aContent,
|
2006-11-03 21:51:01 +00:00
|
|
|
CharacterDataChangeInfo* aInfo)
|
2005-06-10 13:57:27 +00:00
|
|
|
{
|
2007-09-05 08:22:17 +00:00
|
|
|
FireTextChangeEventForText(aContent, aInfo, PR_TRUE);
|
2005-06-10 13:57:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsDocAccessible::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer,
|
2010-05-11 01:12:34 +00:00
|
|
|
nsIContent* aChild, PRInt32 /* unused */)
|
2005-06-10 13:57:27 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsDocAccessible::ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer,
|
2010-07-21 22:05:17 +00:00
|
|
|
nsIContent* aChild, PRInt32 /* unused */,
|
|
|
|
nsIContent* aPreviousSibling)
|
2005-06-10 13:57:27 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2007-03-10 13:49:43 +00:00
|
|
|
void
|
|
|
|
nsDocAccessible::ParentChainChanged(nsIContent *aContent)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsAccessible
|
|
|
|
|
2010-06-08 16:39:58 +00:00
|
|
|
#ifdef DEBUG_ACCDOCMGR
|
|
|
|
nsresult
|
2010-08-25 02:08:28 +00:00
|
|
|
nsDocAccessible::HandleAccEvent(AccEvent* aAccEvent)
|
2010-06-08 16:39:58 +00:00
|
|
|
{
|
|
|
|
NS_LOG_ACCDOCLOAD_HANDLEEVENT(aAccEvent)
|
2009-12-10 19:12:19 +00:00
|
|
|
|
2010-06-08 16:39:58 +00:00
|
|
|
return nsHyperTextAccessible::HandleAccEvent(aAccEvent);
|
2009-12-10 19:12:19 +00:00
|
|
|
|
|
|
|
}
|
2010-06-08 16:39:58 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Public members
|
2009-12-10 19:12:19 +00:00
|
|
|
|
2010-09-17 03:23:17 +00:00
|
|
|
void*
|
|
|
|
nsDocAccessible::GetNativeWindow() const
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIPresShell> shell(do_QueryReferent(mWeakShell));
|
|
|
|
nsIViewManager* vm = shell->GetViewManager();
|
|
|
|
if (vm) {
|
|
|
|
nsCOMPtr<nsIWidget> widget;
|
|
|
|
vm->GetRootWidget(getter_AddRefs(widget));
|
|
|
|
if (widget)
|
|
|
|
return widget->GetNativeData(NS_NATIVE_WINDOW);
|
|
|
|
}
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
2010-09-09 14:44:56 +00:00
|
|
|
nsAccessible*
|
2010-10-21 04:16:10 +00:00
|
|
|
nsDocAccessible::GetCachedAccessibleByUniqueIDInSubtree(void* aUniqueID)
|
2010-09-09 14:44:56 +00:00
|
|
|
{
|
2010-10-21 04:16:10 +00:00
|
|
|
nsAccessible* child = GetCachedAccessibleByUniqueID(aUniqueID);
|
2010-09-09 14:44:56 +00:00
|
|
|
if (child)
|
|
|
|
return child;
|
|
|
|
|
|
|
|
PRUint32 childDocCount = mChildDocuments.Length();
|
|
|
|
for (PRUint32 childDocIdx= 0; childDocIdx < childDocCount; childDocIdx++) {
|
|
|
|
nsDocAccessible* childDocument = mChildDocuments.ElementAt(childDocIdx);
|
2010-10-21 04:16:10 +00:00
|
|
|
child = childDocument->GetCachedAccessibleByUniqueIDInSubtree(aUniqueID);
|
2010-09-09 14:44:56 +00:00
|
|
|
if (child)
|
|
|
|
return child;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nsnull;
|
|
|
|
}
|
2009-12-10 19:12:19 +00:00
|
|
|
|
2010-11-12 19:00:55 +00:00
|
|
|
bool
|
|
|
|
nsDocAccessible::BindToDocument(nsAccessible* aAccessible,
|
|
|
|
nsRoleMapEntry* aRoleMapEntry)
|
|
|
|
{
|
|
|
|
if (!aAccessible)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Put into DOM node cache.
|
|
|
|
if (aAccessible->IsPrimaryForNode() &&
|
|
|
|
!mNodeToAccessibleMap.Put(aAccessible->GetNode(), aAccessible))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Put into unique ID cache.
|
|
|
|
if (!mAccessibleCache.Put(aAccessible->UniqueID(), aAccessible)) {
|
|
|
|
if (aAccessible->IsPrimaryForNode())
|
|
|
|
mNodeToAccessibleMap.Remove(aAccessible->GetNode());
|
2010-11-12 19:01:04 +00:00
|
|
|
|
2010-11-12 19:00:55 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize the accessible.
|
|
|
|
if (!aAccessible->Init()) {
|
|
|
|
NS_ERROR("Failed to initialize an accessible!");
|
|
|
|
|
|
|
|
UnbindFromDocument(aAccessible);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
aAccessible->SetRoleMapEntry(aRoleMapEntry);
|
2010-11-18 02:55:44 +00:00
|
|
|
AddDependentIDsFor(aAccessible);
|
2010-11-12 19:00:55 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsDocAccessible::UnbindFromDocument(nsAccessible* aAccessible)
|
|
|
|
{
|
2010-11-20 02:37:40 +00:00
|
|
|
NS_ASSERTION(mAccessibleCache.GetWeak(aAccessible->UniqueID()),
|
|
|
|
"Unbinding the unbound accessible!");
|
|
|
|
|
2010-11-12 19:01:04 +00:00
|
|
|
// Remove an accessible from node-to-accessible map if it exists there.
|
2010-11-12 19:00:55 +00:00
|
|
|
if (aAccessible->IsPrimaryForNode() &&
|
|
|
|
mNodeToAccessibleMap.Get(aAccessible->GetNode()) == aAccessible)
|
|
|
|
mNodeToAccessibleMap.Remove(aAccessible->GetNode());
|
|
|
|
|
2010-11-20 02:37:40 +00:00
|
|
|
if (!aAccessible->IsDefunct())
|
|
|
|
RemoveDependentIDsFor(aAccessible);
|
2010-11-12 19:00:55 +00:00
|
|
|
|
|
|
|
void* uniqueID = aAccessible->UniqueID();
|
2010-11-20 02:37:40 +00:00
|
|
|
|
|
|
|
NS_ASSERTION(!aAccessible->IsDefunct(), "Shutdown the shutdown accessible!");
|
2010-11-12 19:00:55 +00:00
|
|
|
aAccessible->Shutdown();
|
2010-11-20 02:37:40 +00:00
|
|
|
|
2010-11-12 19:00:55 +00:00
|
|
|
mAccessibleCache.Remove(uniqueID);
|
|
|
|
}
|
|
|
|
|
2010-10-21 04:16:10 +00:00
|
|
|
void
|
2011-01-18 08:03:38 +00:00
|
|
|
nsDocAccessible::ContentInserted(nsIContent* aContainerNode,
|
|
|
|
nsIContent* aStartChildNode,
|
|
|
|
nsIContent* aEndChildNode)
|
|
|
|
{
|
|
|
|
/// Pend tree update on content insertion until layout.
|
|
|
|
if (mNotificationController) {
|
|
|
|
// Update the whole tree of this document accessible when the container is
|
|
|
|
// null (document element is inserted or removed).
|
|
|
|
nsAccessible* container = aContainerNode ?
|
2010-11-06 04:11:08 +00:00
|
|
|
GetAccService()->GetCachedAccessibleOrContainer(aContainerNode) :
|
|
|
|
this;
|
2010-10-21 04:16:10 +00:00
|
|
|
|
2011-01-18 08:03:38 +00:00
|
|
|
mNotificationController->ScheduleContentInsertion(container,
|
|
|
|
aStartChildNode,
|
|
|
|
aEndChildNode);
|
2010-10-21 04:16:10 +00:00
|
|
|
}
|
2011-01-18 08:03:38 +00:00
|
|
|
}
|
2010-10-21 04:16:10 +00:00
|
|
|
|
2011-01-18 08:03:38 +00:00
|
|
|
void
|
|
|
|
nsDocAccessible::ContentRemoved(nsIContent* aContainerNode,
|
|
|
|
nsIContent* aChildNode)
|
|
|
|
{
|
|
|
|
// Update the whole tree of this document accessible when the container is
|
|
|
|
// null (document element is removed).
|
|
|
|
nsAccessible* container = aContainerNode ?
|
|
|
|
GetAccService()->GetCachedAccessibleOrContainer(aContainerNode) :
|
|
|
|
this;
|
2010-10-21 04:16:10 +00:00
|
|
|
|
2011-01-18 08:03:38 +00:00
|
|
|
UpdateTree(container, aChildNode, PR_FALSE);
|
2010-10-21 04:16:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsDocAccessible::RecreateAccessible(nsINode* aNode)
|
|
|
|
{
|
|
|
|
// XXX: we shouldn't recreate whole accessible subtree that happens when
|
|
|
|
// hide event is handled, instead we should subclass hide and show events
|
|
|
|
// to handle them separately and implement their coalescence with normal hide
|
|
|
|
// and show events.
|
|
|
|
|
|
|
|
nsAccessible* parent = nsnull;
|
|
|
|
|
|
|
|
// Fire hide event for old accessible.
|
|
|
|
nsAccessible* oldAccessible =
|
|
|
|
GetAccService()->GetAccessibleInWeakShell(aNode, mWeakShell);
|
|
|
|
if (oldAccessible) {
|
|
|
|
parent = oldAccessible->GetParent();
|
|
|
|
|
2011-01-18 08:03:38 +00:00
|
|
|
nsRefPtr<AccEvent> hideEvent = new AccHideEvent(oldAccessible, aNode);
|
2010-10-21 04:16:10 +00:00
|
|
|
if (hideEvent)
|
|
|
|
FireDelayedAccessibleEvent(hideEvent);
|
|
|
|
|
|
|
|
// Unbind old accessible from tree.
|
|
|
|
parent->RemoveChild(oldAccessible);
|
|
|
|
|
|
|
|
if (oldAccessible->IsPrimaryForNode() &&
|
|
|
|
mNodeToAccessibleMap.Get(oldAccessible->GetNode()) == oldAccessible)
|
|
|
|
mNodeToAccessibleMap.Remove(oldAccessible->GetNode());
|
|
|
|
|
|
|
|
} else {
|
2011-01-20 06:04:11 +00:00
|
|
|
// Not accessible node may not have container accessible if we recreate
|
|
|
|
// an accessible asynchronously.
|
|
|
|
// XXX: asynchronous RecreateAccessible notifications should be coalesced
|
|
|
|
// with accessible tree mutation notifications. We could trigger
|
|
|
|
// ContentRemoved/ContentInserted pair for that but it moves us away from
|
|
|
|
// the idea to not recreate the whole subtree.
|
2010-10-21 04:16:10 +00:00
|
|
|
parent = GetAccService()->GetContainerAccessible(aNode, mWeakShell);
|
2011-01-20 06:04:11 +00:00
|
|
|
if (!parent)
|
|
|
|
return;
|
2010-10-21 04:16:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get new accessible and fire show event.
|
|
|
|
parent->InvalidateChildren();
|
|
|
|
|
|
|
|
nsAccessible* newAccessible =
|
|
|
|
GetAccService()->GetAccessibleInWeakShell(aNode, mWeakShell);
|
|
|
|
if (newAccessible) {
|
2011-01-18 08:03:38 +00:00
|
|
|
nsRefPtr<AccEvent> showEvent = new AccShowEvent(newAccessible, aNode);
|
2010-10-21 04:16:10 +00:00
|
|
|
if (showEvent)
|
|
|
|
FireDelayedAccessibleEvent(showEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fire reorder event.
|
|
|
|
if (oldAccessible || newAccessible) {
|
|
|
|
nsRefPtr<AccEvent> reorderEvent =
|
|
|
|
new AccEvent(nsIAccessibleEvent::EVENT_REORDER, parent->GetNode(),
|
|
|
|
eAutoDetect, AccEvent::eCoalesceFromSameSubtree);
|
|
|
|
|
|
|
|
if (reorderEvent)
|
|
|
|
FireDelayedAccessibleEvent(reorderEvent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-19 05:44:47 +00:00
|
|
|
void
|
|
|
|
nsDocAccessible::NotifyOfCachingStart(nsAccessible* aAccessible)
|
|
|
|
{
|
|
|
|
if (!mCacheRoot)
|
|
|
|
mCacheRoot = aAccessible;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsDocAccessible::NotifyOfCachingEnd(nsAccessible* aAccessible)
|
|
|
|
{
|
|
|
|
if (mCacheRoot == aAccessible && !mIsPostCacheProcessing) {
|
|
|
|
// Allow invalidation list insertions while container children are recached.
|
|
|
|
mIsPostCacheProcessing = PR_TRUE;
|
|
|
|
|
|
|
|
// Invalidate children of container accessible for each element in
|
|
|
|
// invalidation list.
|
|
|
|
for (PRUint32 idx = 0; idx < mInvalidationList.Length(); idx++) {
|
|
|
|
nsIContent* content = mInvalidationList[idx];
|
|
|
|
nsAccessible* container =
|
|
|
|
GetAccService()->GetCachedContainerAccessible(content);
|
|
|
|
container->InvalidateChildren();
|
|
|
|
|
|
|
|
// Make sure we keep children updated. While we're inside of caching loop
|
|
|
|
// then we must exist it with cached children.
|
|
|
|
container->EnsureChildren();
|
|
|
|
}
|
|
|
|
mInvalidationList.Clear();
|
|
|
|
|
|
|
|
mCacheRoot = nsnull;
|
|
|
|
mIsPostCacheProcessing = PR_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-21 01:00:29 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsAccessible protected
|
|
|
|
|
|
|
|
void
|
|
|
|
nsDocAccessible::CacheChildren()
|
|
|
|
{
|
|
|
|
// Search for accessible children starting from the document element since
|
|
|
|
// some web pages tend to insert elements under it rather than document body.
|
|
|
|
nsAccTreeWalker walker(mWeakShell, mDocument->GetRootElement(),
|
|
|
|
GetAllowsAnonChildAccessibles());
|
|
|
|
|
|
|
|
nsRefPtr<nsAccessible> child;
|
|
|
|
while ((child = walker.GetNextChild()) && AppendChild(child));
|
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Protected members
|
|
|
|
|
2010-11-18 02:55:44 +00:00
|
|
|
void
|
|
|
|
nsDocAccessible::AddDependentIDsFor(nsAccessible* aRelProvider,
|
|
|
|
nsIAtom* aRelAttr)
|
|
|
|
{
|
|
|
|
for (PRUint32 idx = 0; idx < kRelationAttrsLen; idx++) {
|
|
|
|
nsIAtom* relAttr = *kRelationAttrs[idx];
|
|
|
|
if (aRelAttr && aRelAttr != relAttr)
|
|
|
|
continue;
|
|
|
|
|
2010-11-20 02:37:18 +00:00
|
|
|
if (relAttr == nsAccessibilityAtoms::_for) {
|
|
|
|
if (!aRelProvider->GetContent()->IsHTML() ||
|
|
|
|
aRelProvider->GetContent()->Tag() != nsAccessibilityAtoms::label &&
|
|
|
|
aRelProvider->GetContent()->Tag() != nsAccessibilityAtoms::output)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
} else if (relAttr == nsAccessibilityAtoms::control) {
|
|
|
|
if (!aRelProvider->GetContent()->IsXUL() ||
|
|
|
|
aRelProvider->GetContent()->Tag() != nsAccessibilityAtoms::label &&
|
|
|
|
aRelProvider->GetContent()->Tag() != nsAccessibilityAtoms::description)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2010-11-18 02:55:44 +00:00
|
|
|
IDRefsIterator iter(aRelProvider->GetContent(), relAttr);
|
|
|
|
while (true) {
|
|
|
|
const nsDependentSubstring id = iter.NextID();
|
|
|
|
if (id.IsEmpty())
|
|
|
|
break;
|
|
|
|
|
|
|
|
AttrRelProviderArray* providers = mDependentIDsHash.Get(id);
|
|
|
|
if (!providers) {
|
|
|
|
providers = new AttrRelProviderArray();
|
|
|
|
if (providers) {
|
|
|
|
if (!mDependentIDsHash.Put(id, providers)) {
|
|
|
|
delete providers;
|
|
|
|
providers = nsnull;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (providers) {
|
|
|
|
AttrRelProvider* provider =
|
|
|
|
new AttrRelProvider(relAttr, aRelProvider->GetContent());
|
2010-11-19 05:44:47 +00:00
|
|
|
if (provider) {
|
2010-11-18 02:55:44 +00:00
|
|
|
providers->AppendElement(provider);
|
2010-11-19 05:44:47 +00:00
|
|
|
|
|
|
|
// We've got here during the children caching. If the referenced
|
|
|
|
// content is not accessible then store it to pend its container
|
|
|
|
// children invalidation (this happens immediately after the caching
|
|
|
|
// is finished).
|
|
|
|
nsIContent* dependentContent = iter.GetElem(id);
|
|
|
|
if (dependentContent && !GetCachedAccessible(dependentContent)) {
|
|
|
|
mInvalidationList.AppendElement(dependentContent);
|
|
|
|
}
|
|
|
|
}
|
2010-11-18 02:55:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the relation attribute is given then we don't have anything else to
|
|
|
|
// check.
|
|
|
|
if (aRelAttr)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsDocAccessible::RemoveDependentIDsFor(nsAccessible* aRelProvider,
|
|
|
|
nsIAtom* aRelAttr)
|
|
|
|
{
|
|
|
|
for (PRUint32 idx = 0; idx < kRelationAttrsLen; idx++) {
|
|
|
|
nsIAtom* relAttr = *kRelationAttrs[idx];
|
|
|
|
if (aRelAttr && aRelAttr != *kRelationAttrs[idx])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
IDRefsIterator iter(aRelProvider->GetContent(), relAttr);
|
|
|
|
while (true) {
|
|
|
|
const nsDependentSubstring id = iter.NextID();
|
|
|
|
if (id.IsEmpty())
|
|
|
|
break;
|
|
|
|
|
|
|
|
AttrRelProviderArray* providers = mDependentIDsHash.Get(id);
|
|
|
|
if (providers) {
|
|
|
|
for (PRUint32 jdx = 0; jdx < providers->Length(); ) {
|
|
|
|
AttrRelProvider* provider = (*providers)[jdx];
|
|
|
|
if (provider->mRelAttr == relAttr &&
|
|
|
|
provider->mContent == aRelProvider->GetContent())
|
|
|
|
providers->RemoveElement(provider);
|
|
|
|
else
|
|
|
|
jdx++;
|
|
|
|
}
|
|
|
|
if (providers->Length() == 0)
|
|
|
|
mDependentIDsHash.Remove(id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the relation attribute is given then we don't have anything else to
|
|
|
|
// check.
|
|
|
|
if (aRelAttr)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-18 18:33:00 +00:00
|
|
|
bool
|
|
|
|
nsDocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
|
|
|
|
nsIAtom* aAttribute)
|
|
|
|
{
|
|
|
|
if (aAttribute == nsAccessibilityAtoms::role) {
|
|
|
|
// It is common for js libraries to set the role on the body element after
|
|
|
|
// the document has loaded. In this case we just update the role map entry.
|
|
|
|
if (mContent == aElement) {
|
|
|
|
SetRoleMapEntry(nsAccUtils::GetRoleMapEntry(aElement));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Recreate the accessible when role is changed because we might require a
|
|
|
|
// different accessible class for the new role or the accessible may expose
|
|
|
|
// a different sets of interfaces (COM restriction).
|
2011-01-19 08:03:12 +00:00
|
|
|
HandleNotification<nsDocAccessible, nsINode>
|
|
|
|
(this, &nsDocAccessible::RecreateAccessible, aElement);
|
|
|
|
|
2010-12-18 18:33:00 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aAttribute == nsAccessibilityAtoms::href ||
|
|
|
|
aAttribute == nsAccessibilityAtoms::onclick) {
|
|
|
|
// 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
|
2011-01-19 08:03:12 +00:00
|
|
|
HandleNotification<nsDocAccessible, nsINode>
|
|
|
|
(this, &nsDocAccessible::RecreateAccessible, aElement);
|
|
|
|
|
2010-12-18 18:33:00 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aAttribute == nsAccessibilityAtoms::aria_multiselectable &&
|
|
|
|
aElement->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::role)) {
|
|
|
|
// This affects whether the accessible supports SelectAccessible.
|
|
|
|
// COM says we cannot change what interfaces are supported on-the-fly,
|
|
|
|
// so invalidate this object. A new one will be created on demand.
|
2011-01-19 08:03:12 +00:00
|
|
|
HandleNotification<nsDocAccessible, nsINode>
|
|
|
|
(this, &nsDocAccessible::RecreateAccessible, aElement);
|
|
|
|
|
2010-12-18 18:33:00 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-03-20 01:58:11 +00:00
|
|
|
void
|
2010-06-12 04:04:24 +00:00
|
|
|
nsDocAccessible::FireValueChangeForTextFields(nsAccessible *aAccessible)
|
2008-03-20 01:58:11 +00:00
|
|
|
{
|
2010-09-07 02:41:53 +00:00
|
|
|
if (aAccessible->Role() != nsIAccessibleRole::ROLE_ENTRY)
|
2008-03-20 01:58:11 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Dependent value change event for text changes in textfields
|
2010-08-25 02:08:28 +00:00
|
|
|
nsRefPtr<AccEvent> valueChangeEvent =
|
|
|
|
new AccEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible,
|
2010-10-21 04:16:10 +00:00
|
|
|
eAutoDetect, AccEvent::eRemoveDupes);
|
2010-01-27 11:42:44 +00:00
|
|
|
FireDelayedAccessibleEvent(valueChangeEvent);
|
2008-03-20 01:58:11 +00:00
|
|
|
}
|
|
|
|
|
2007-07-25 11:54:15 +00:00
|
|
|
void
|
2007-09-05 08:22:17 +00:00
|
|
|
nsDocAccessible::FireTextChangeEventForText(nsIContent *aContent,
|
|
|
|
CharacterDataChangeInfo* aInfo,
|
|
|
|
PRBool aIsInserted)
|
2007-07-25 11:54:15 +00:00
|
|
|
{
|
2010-06-08 16:39:58 +00:00
|
|
|
if (!IsContentLoaded())
|
2007-08-06 03:19:37 +00:00
|
|
|
return;
|
|
|
|
|
2010-06-10 03:29:56 +00:00
|
|
|
PRInt32 contentOffset = aInfo->mChangeStart;
|
|
|
|
PRUint32 contentLength = aIsInserted ?
|
|
|
|
aInfo->mReplaceLength: // text has been added
|
|
|
|
aInfo->mChangeEnd - contentOffset; // text has been removed
|
|
|
|
|
|
|
|
if (contentLength == 0)
|
2007-07-25 11:54:15 +00:00
|
|
|
return;
|
|
|
|
|
2010-06-11 08:23:18 +00:00
|
|
|
nsAccessible *accessible = GetAccService()->GetAccessible(aContent);
|
2010-05-25 08:40:39 +00:00
|
|
|
if (!accessible)
|
2007-07-25 11:54:15 +00:00
|
|
|
return;
|
|
|
|
|
2010-06-10 03:29:56 +00:00
|
|
|
nsRefPtr<nsHyperTextAccessible> textAccessible =
|
|
|
|
do_QueryObject(accessible->GetParent());
|
2010-05-17 16:16:52 +00:00
|
|
|
if (!textAccessible)
|
2007-07-25 11:54:15 +00:00
|
|
|
return;
|
|
|
|
|
2010-07-02 01:50:03 +00:00
|
|
|
// Get offset within hypertext accessible and invalidate cached offsets after
|
|
|
|
// this child accessible.
|
|
|
|
PRInt32 offset = textAccessible->GetChildOffset(accessible, PR_TRUE);
|
2007-07-25 11:54:15 +00:00
|
|
|
|
2010-07-02 01:50:03 +00:00
|
|
|
// Get added or removed text.
|
2010-06-10 03:29:56 +00:00
|
|
|
nsIFrame* frame = aContent->GetPrimaryFrame();
|
|
|
|
if (!frame)
|
|
|
|
return;
|
2007-09-05 08:22:17 +00:00
|
|
|
|
2010-06-10 03:29:56 +00:00
|
|
|
PRUint32 textOffset = 0;
|
|
|
|
nsresult rv = textAccessible->ContentToRenderedOffset(frame, contentOffset,
|
|
|
|
&textOffset);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return;
|
2007-09-05 08:22:17 +00:00
|
|
|
|
2010-06-10 03:29:56 +00:00
|
|
|
nsAutoString text;
|
|
|
|
rv = accessible->AppendTextTo(text, textOffset, contentLength);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return;
|
2007-09-05 08:22:17 +00:00
|
|
|
|
2010-07-02 01:49:42 +00:00
|
|
|
if (text.IsEmpty())
|
2010-06-10 03:29:56 +00:00
|
|
|
return;
|
2007-07-25 11:54:15 +00:00
|
|
|
|
2010-06-10 03:29:56 +00:00
|
|
|
// Normally we only fire delayed events created from the node, not an
|
2010-08-25 02:08:28 +00:00
|
|
|
// accessible object. See the AccTextChangeEvent constructor for details
|
2010-06-10 03:29:56 +00:00
|
|
|
// about this exceptional case.
|
2010-08-25 02:08:28 +00:00
|
|
|
nsRefPtr<AccEvent> event =
|
|
|
|
new AccTextChangeEvent(textAccessible, offset + textOffset, text,
|
2010-10-21 04:16:10 +00:00
|
|
|
aIsInserted);
|
2010-06-10 03:29:56 +00:00
|
|
|
FireDelayedAccessibleEvent(event);
|
2008-03-20 01:58:11 +00:00
|
|
|
|
2010-06-10 03:29:56 +00:00
|
|
|
FireValueChangeForTextFields(textAccessible);
|
2007-07-25 11:54:15 +00:00
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
// nsDocAccessible public member
|
2009-09-03 02:01:18 +00:00
|
|
|
nsresult
|
2010-06-11 08:23:18 +00:00
|
|
|
nsDocAccessible::FireDelayedAccessibleEvent(PRUint32 aEventType, nsINode *aNode,
|
2010-08-25 02:08:28 +00:00
|
|
|
AccEvent::EEventRule aAllowDupes,
|
2010-01-20 11:16:32 +00:00
|
|
|
EIsFromUserInput aIsFromUserInput)
|
2007-04-17 04:45:42 +00:00
|
|
|
{
|
2010-08-25 02:08:28 +00:00
|
|
|
nsRefPtr<AccEvent> event =
|
2010-10-21 04:16:10 +00:00
|
|
|
new AccEvent(aEventType, aNode, aIsFromUserInput, aAllowDupes);
|
2007-04-17 04:45:42 +00:00
|
|
|
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
|
2008-03-17 08:13:10 +00:00
|
|
|
return FireDelayedAccessibleEvent(event);
|
2007-04-17 04:45:42 +00:00
|
|
|
}
|
|
|
|
|
2009-12-10 19:12:19 +00:00
|
|
|
// nsDocAccessible public member
|
2007-04-17 04:45:42 +00:00
|
|
|
nsresult
|
2010-08-25 02:08:28 +00:00
|
|
|
nsDocAccessible::FireDelayedAccessibleEvent(AccEvent* aEvent)
|
2005-06-10 13:57:27 +00:00
|
|
|
{
|
2009-09-07 16:46:56 +00:00
|
|
|
NS_ENSURE_ARG(aEvent);
|
2010-06-08 16:39:58 +00:00
|
|
|
NS_LOG_ACCDOCLOAD_FIREEVENT(aEvent)
|
2009-09-07 16:46:56 +00:00
|
|
|
|
2011-01-18 08:03:38 +00:00
|
|
|
if (mNotificationController)
|
|
|
|
mNotificationController->QueueEvent(aEvent);
|
2009-09-07 16:46:56 +00:00
|
|
|
|
2010-01-27 11:42:08 +00:00
|
|
|
return NS_OK;
|
2009-09-07 16:46:56 +00:00
|
|
|
}
|
2007-08-21 03:16:27 +00:00
|
|
|
|
2010-01-27 11:42:08 +00:00
|
|
|
void
|
2010-08-25 02:08:28 +00:00
|
|
|
nsDocAccessible::ProcessPendingEvent(AccEvent* aEvent)
|
2010-10-21 04:16:10 +00:00
|
|
|
{
|
|
|
|
nsAccessible* accessible = aEvent->GetAccessible();
|
|
|
|
if (!accessible)
|
|
|
|
return;
|
2010-01-27 11:42:08 +00:00
|
|
|
|
|
|
|
PRUint32 eventType = aEvent->GetEventType();
|
2007-09-28 20:55:46 +00:00
|
|
|
|
2010-10-21 04:16:10 +00:00
|
|
|
if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED) {
|
|
|
|
nsCOMPtr<nsIAccessibleText> accessibleText = do_QueryObject(accessible);
|
|
|
|
PRInt32 caretOffset;
|
|
|
|
if (accessibleText &&
|
|
|
|
NS_SUCCEEDED(accessibleText->GetCaretOffset(&caretOffset))) {
|
2007-08-17 18:21:49 +00:00
|
|
|
#ifdef DEBUG_A11Y
|
2010-10-21 04:16:10 +00:00
|
|
|
PRUnichar chAtOffset;
|
|
|
|
accessibleText->GetCharacterAtOffset(caretOffset, &chAtOffset);
|
|
|
|
printf("\nCaret moved to %d with char %c", caretOffset, chAtOffset);
|
2007-12-11 03:30:02 +00:00
|
|
|
#endif
|
|
|
|
#ifdef DEBUG_CARET
|
2010-10-21 04:16:10 +00:00
|
|
|
// Test caret line # -- fire an EVENT_ALERT on the focused node so we can watch the
|
|
|
|
// line-number object attribute on it
|
|
|
|
nsAccessible* focusedAcc =
|
|
|
|
GetAccService()->GetAccessible(gLastFocusedNode);
|
|
|
|
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_ALERT, focusedAcc);
|
2007-07-13 17:19:40 +00:00
|
|
|
#endif
|
2010-10-21 04:16:10 +00:00
|
|
|
nsRefPtr<AccEvent> caretMoveEvent =
|
2010-08-25 02:08:28 +00:00
|
|
|
new AccCaretMoveEvent(accessible, caretOffset);
|
2010-10-21 04:16:10 +00:00
|
|
|
if (!caretMoveEvent)
|
|
|
|
return;
|
2008-02-02 17:02:09 +00:00
|
|
|
|
2010-10-21 04:16:10 +00:00
|
|
|
nsEventShell::FireEvent(caretMoveEvent);
|
2008-02-02 17:02:09 +00:00
|
|
|
|
2010-10-21 04:16:10 +00:00
|
|
|
PRInt32 selectionCount;
|
|
|
|
accessibleText->GetSelectionCount(&selectionCount);
|
|
|
|
if (selectionCount) { // There's a selection so fire selection change as well
|
|
|
|
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED,
|
2010-01-18 16:16:07 +00:00
|
|
|
accessible);
|
2008-01-24 14:07:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-10-21 04:16:10 +00:00
|
|
|
else {
|
|
|
|
nsEventShell::FireEvent(aEvent);
|
2008-01-22 14:43:18 +00:00
|
|
|
|
2010-10-21 04:16:10 +00:00
|
|
|
// Post event processing
|
|
|
|
if (eventType == nsIAccessibleEvent::EVENT_HIDE)
|
|
|
|
ShutdownChildrenInSubtree(accessible);
|
2008-01-22 14:43:18 +00:00
|
|
|
}
|
2005-05-03 03:46:51 +00:00
|
|
|
}
|
|
|
|
|
2011-01-18 08:03:38 +00:00
|
|
|
void
|
|
|
|
nsDocAccessible::ProcessContentInserted(nsAccessible* aContainer,
|
|
|
|
const nsTArray<nsCOMPtr<nsIContent> >* aInsertedContent)
|
|
|
|
{
|
|
|
|
// Process the notification if the container accessible is still in tree.
|
|
|
|
if (!GetCachedAccessible(aContainer->GetNode()))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (aContainer == this) {
|
|
|
|
// If new root content has been inserted then update it.
|
|
|
|
nsIContent* rootContent = nsCoreUtils::GetRoleContent(mDocument);
|
|
|
|
if (rootContent && rootContent != mContent)
|
|
|
|
mContent = rootContent;
|
|
|
|
|
|
|
|
// Continue to update the tree even if we don't have root content.
|
|
|
|
// For example, elements may be inserted under the document element while
|
|
|
|
// there is no HTML body element.
|
|
|
|
}
|
|
|
|
|
|
|
|
// XXX: Invalidate parent-child relations for container accessible and its
|
|
|
|
// children because there's no good way to find insertion point of new child
|
|
|
|
// accessibles into accessible tree. We need to invalidate children even
|
|
|
|
// there's no inserted accessibles in the end because accessible children
|
|
|
|
// are created while parent recaches child accessibles.
|
|
|
|
aContainer->InvalidateChildren();
|
|
|
|
|
|
|
|
// The container might be changed, for example, because of the subsequent
|
|
|
|
// overlapping content insertion (i.e. other content was inserted between this
|
|
|
|
// inserted content and its container or the content was reinserted into
|
|
|
|
// different container of unrelated part of tree). These cases result in
|
|
|
|
// double processing, however generated events are coalesced and we don't
|
|
|
|
// harm an AT. On the another hand container can be different because direct
|
|
|
|
// container wasn't cached yet when we handled content insertion notification
|
|
|
|
// and therefore we can't ignore the case when container has been changed.
|
2011-01-19 16:01:31 +00:00
|
|
|
// Theoretically the element might be not in tree at all at this point what
|
|
|
|
// means there's no container.
|
|
|
|
for (PRUint32 idx = 0; idx < aInsertedContent->Length(); idx++) {
|
|
|
|
nsAccessible* directContainer =
|
|
|
|
GetAccService()->GetContainerAccessible(aInsertedContent->ElementAt(idx),
|
|
|
|
mWeakShell);
|
|
|
|
if (directContainer)
|
|
|
|
UpdateTree(directContainer, aInsertedContent->ElementAt(idx), PR_TRUE);
|
|
|
|
}
|
2011-01-18 08:03:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsDocAccessible::UpdateTree(nsAccessible* aContainer, nsIContent* aChildNode,
|
|
|
|
PRBool aIsInsert)
|
|
|
|
{
|
|
|
|
PRUint32 updateFlags =
|
|
|
|
UpdateTreeInternal(aContainer, aChildNode, aChildNode->GetNextSibling(),
|
|
|
|
aIsInsert);
|
|
|
|
|
|
|
|
// Content insertion/removal is not cause of accessible tree change.
|
|
|
|
if (updateFlags == eNoAccessible)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Check to see if change occurred inside an alert, and fire an EVENT_ALERT
|
|
|
|
// if it did.
|
|
|
|
if (aIsInsert && !(updateFlags & eAlertAccessible)) {
|
|
|
|
// XXX: tree traversal is perf issue, accessible should know if they are
|
|
|
|
// children of alert accessible to avoid this.
|
|
|
|
nsAccessible* ancestor = aContainer;
|
|
|
|
while (ancestor) {
|
|
|
|
if (ancestor->ARIARole() == nsIAccessibleRole::ROLE_ALERT) {
|
|
|
|
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_ALERT,
|
|
|
|
ancestor->GetNode());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't climb above this document.
|
|
|
|
if (ancestor == this)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ancestor = ancestor->GetParent();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fire value change event.
|
|
|
|
if (aContainer->Role() == nsIAccessibleRole::ROLE_ENTRY) {
|
|
|
|
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
|
|
|
|
aContainer->GetNode());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fire reorder event so the MSAA clients know the children have changed. Also
|
|
|
|
// the event is used internally by MSAA layer.
|
|
|
|
nsRefPtr<AccEvent> reorderEvent =
|
|
|
|
new AccEvent(nsIAccessibleEvent::EVENT_REORDER, aContainer->GetNode(),
|
|
|
|
eAutoDetect, AccEvent::eCoalesceFromSameSubtree);
|
|
|
|
if (reorderEvent)
|
|
|
|
FireDelayedAccessibleEvent(reorderEvent);
|
|
|
|
}
|
|
|
|
|
2010-10-21 04:16:10 +00:00
|
|
|
PRUint32
|
|
|
|
nsDocAccessible::UpdateTreeInternal(nsAccessible* aContainer,
|
|
|
|
nsIContent* aStartNode,
|
|
|
|
nsIContent* aEndNode,
|
2011-01-18 08:03:38 +00:00
|
|
|
PRBool aIsInsert)
|
2005-05-03 03:46:51 +00:00
|
|
|
{
|
2010-10-21 04:16:10 +00:00
|
|
|
PRUint32 updateFlags = eNoAccessible;
|
|
|
|
for (nsIContent* node = aStartNode; node != aEndNode;
|
|
|
|
node = node->GetNextSibling()) {
|
|
|
|
|
|
|
|
// Tree update triggers for content insertion even if no content was
|
|
|
|
// inserted actually, check if the given content has a frame to discard
|
|
|
|
// this case early.
|
|
|
|
if (aIsInsert && !node->GetPrimaryFrame())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
nsAccessible* accessible = aIsInsert ?
|
|
|
|
GetAccService()->GetAccessibleInWeakShell(node, mWeakShell) :
|
|
|
|
GetCachedAccessible(node);
|
|
|
|
|
|
|
|
if (!accessible) {
|
|
|
|
updateFlags |= UpdateTreeInternal(aContainer, node->GetFirstChild(),
|
2011-01-18 08:03:38 +00:00
|
|
|
nsnull, aIsInsert);
|
2010-10-21 04:16:10 +00:00
|
|
|
continue;
|
2007-10-01 18:27:13 +00:00
|
|
|
}
|
2009-06-25 02:08:53 +00:00
|
|
|
|
2010-10-21 04:16:10 +00:00
|
|
|
updateFlags |= eAccessible;
|
2005-06-10 13:57:27 +00:00
|
|
|
|
2010-11-30 15:43:17 +00:00
|
|
|
if (!aIsInsert) {
|
|
|
|
// Fire menupopup end event before hide event if a menu goes away.
|
|
|
|
|
|
|
|
// XXX: We don't look into children of hidden subtree to find hiding
|
|
|
|
// menupopup (as we did prior bug 570275) because we don't do that when
|
|
|
|
// menu is showing (and that's impossible until bug 606924 is fixed).
|
|
|
|
// Nevertheless we should do this at least because layout coalesces
|
|
|
|
// the changes before our processing and we may miss some menupopup
|
|
|
|
// events. Now we just want to be consistent in content insertion/removal
|
|
|
|
// handling.
|
2010-12-01 08:53:17 +00:00
|
|
|
if (accessible->ARIARole() == nsIAccessibleRole::ROLE_MENUPOPUP) {
|
2010-11-30 15:43:17 +00:00
|
|
|
nsRefPtr<AccEvent> event =
|
|
|
|
new AccEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END, accessible);
|
|
|
|
|
|
|
|
if (event)
|
|
|
|
FireDelayedAccessibleEvent(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-21 04:16:10 +00:00
|
|
|
// Fire show/hide event.
|
2011-01-18 08:03:38 +00:00
|
|
|
nsRefPtr<AccEvent> event;
|
|
|
|
if (aIsInsert)
|
|
|
|
event = new AccShowEvent(accessible, node);
|
|
|
|
else
|
|
|
|
event = new AccHideEvent(accessible, node);
|
|
|
|
|
|
|
|
if (event)
|
|
|
|
FireDelayedAccessibleEvent(event);
|
2006-08-11 17:21:56 +00:00
|
|
|
|
2010-10-21 04:16:10 +00:00
|
|
|
if (aIsInsert) {
|
2010-12-01 08:53:17 +00:00
|
|
|
PRUint32 ariaRole = accessible->ARIARole();
|
|
|
|
if (ariaRole == nsIAccessibleRole::ROLE_MENUPOPUP) {
|
|
|
|
// Fire EVENT_MENUPOPUP_START if ARIA menu appears.
|
|
|
|
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START,
|
2011-01-18 08:03:38 +00:00
|
|
|
node, AccEvent::eRemoveDupes);
|
2010-12-01 08:53:17 +00:00
|
|
|
|
|
|
|
} else if (ariaRole == nsIAccessibleRole::ROLE_ALERT) {
|
|
|
|
// Fire EVENT_ALERT if ARIA alert appears.
|
|
|
|
updateFlags = eAlertAccessible;
|
|
|
|
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_ALERT, node,
|
2011-01-18 08:03:38 +00:00
|
|
|
AccEvent::eRemoveDupes);
|
2007-10-06 16:24:57 +00:00
|
|
|
}
|
2005-05-03 03:46:51 +00:00
|
|
|
|
2010-10-21 04:16:10 +00:00
|
|
|
// If focused node has been shown then it means its frame was recreated
|
|
|
|
// while it's focused. Fire focus event on new focused accessible. If
|
|
|
|
// the queue contains focus event for this node then it's suppressed by
|
|
|
|
// this one.
|
|
|
|
if (node == gLastFocusedNode) {
|
|
|
|
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_FOCUS,
|
2011-01-18 08:03:38 +00:00
|
|
|
node, AccEvent::eCoalesceFromSameDocument);
|
2006-04-21 17:39:32 +00:00
|
|
|
}
|
2010-10-21 04:16:10 +00:00
|
|
|
} else {
|
|
|
|
// Update the tree for content removal.
|
2010-12-15 21:23:10 +00:00
|
|
|
// The accessible parent may differ from container accessible if
|
|
|
|
// the parent doesn't have own DOM node like list accessible for HTML
|
|
|
|
// selects.
|
|
|
|
nsAccessible* parent = accessible->GetParent();
|
|
|
|
NS_ASSERTION(parent, "No accessible parent?!");
|
|
|
|
parent->RemoveChild(accessible);
|
|
|
|
|
2010-10-21 04:16:10 +00:00
|
|
|
UncacheChildrenInSubtree(accessible);
|
2005-06-10 13:57:27 +00:00
|
|
|
}
|
|
|
|
}
|
2003-05-15 08:37:38 +00:00
|
|
|
|
2010-10-21 04:16:10 +00:00
|
|
|
return updateFlags;
|
2003-04-28 10:24:52 +00:00
|
|
|
}
|
|
|
|
|
2010-10-21 04:16:10 +00:00
|
|
|
void
|
|
|
|
nsDocAccessible::UncacheChildrenInSubtree(nsAccessible* aRoot)
|
2007-10-06 16:24:57 +00:00
|
|
|
{
|
2010-10-21 04:16:10 +00:00
|
|
|
PRUint32 count = aRoot->GetCachedChildCount();
|
|
|
|
for (PRUint32 idx = 0; idx < count; idx++)
|
|
|
|
UncacheChildrenInSubtree(aRoot->GetCachedChildAt(idx));
|
2007-10-06 16:24:57 +00:00
|
|
|
|
2010-10-21 04:16:10 +00:00
|
|
|
if (aRoot->IsPrimaryForNode() &&
|
|
|
|
mNodeToAccessibleMap.Get(aRoot->GetNode()) == aRoot)
|
|
|
|
mNodeToAccessibleMap.Remove(aRoot->GetNode());
|
|
|
|
}
|
2007-10-06 16:24:57 +00:00
|
|
|
|
2010-10-21 04:16:10 +00:00
|
|
|
void
|
|
|
|
nsDocAccessible::ShutdownChildrenInSubtree(nsAccessible* aAccessible)
|
|
|
|
{
|
|
|
|
// Traverse through children and shutdown them before this accessible. When
|
|
|
|
// child gets shutdown then it removes itself from children array of its
|
|
|
|
//parent. Use jdx index to process the cases if child is not attached to the
|
|
|
|
// parent and as result doesn't remove itself from its children.
|
|
|
|
PRUint32 count = aAccessible->GetCachedChildCount();
|
|
|
|
for (PRUint32 idx = 0, jdx = 0; idx < count; idx++) {
|
|
|
|
nsAccessible* child = aAccessible->GetCachedChildAt(jdx);
|
|
|
|
if (!child->IsBoundToParent()) {
|
|
|
|
NS_ERROR("Parent refers to a child, child doesn't refer to parent!");
|
|
|
|
jdx++;
|
2010-07-02 01:49:42 +00:00
|
|
|
}
|
2009-09-09 09:03:14 +00:00
|
|
|
|
2010-10-21 04:16:10 +00:00
|
|
|
ShutdownChildrenInSubtree(child);
|
2007-10-06 16:24:57 +00:00
|
|
|
}
|
|
|
|
|
2010-11-12 19:00:55 +00:00
|
|
|
UnbindFromDocument(aAccessible);
|
2007-10-06 16:24:57 +00:00
|
|
|
}
|