2007-03-22 10:30:00 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Netscape Communications Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2003
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Original Author: Aaron Leventhal (aaronl@netscape.com)
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
2011-08-09 18:44:00 -07:00
|
|
|
#include "AccIterator.h"
|
2010-04-26 23:52:03 -07:00
|
|
|
#include "nsAccCache.h"
|
2009-07-29 02:01:48 -07:00
|
|
|
#include "nsAccessibilityService.h"
|
2012-02-01 22:14:51 -08:00
|
|
|
#include "nsAccessiblePivot.h"
|
2010-07-01 18:50:03 -07:00
|
|
|
#include "nsAccTreeWalker.h"
|
2010-04-26 23:52:03 -07:00
|
|
|
#include "nsAccUtils.h"
|
|
|
|
#include "nsRootAccessible.h"
|
|
|
|
#include "nsTextEquivUtils.h"
|
2012-01-11 19:07:35 -08:00
|
|
|
#include "Role.h"
|
|
|
|
#include "States.h"
|
2010-04-26 23:52:03 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "nsIMutableArray.h"
|
|
|
|
#include "nsICommandManager.h"
|
|
|
|
#include "nsIDocShell.h"
|
|
|
|
#include "nsIDocShellTreeItem.h"
|
|
|
|
#include "nsIDocument.h"
|
|
|
|
#include "nsIDOMAttr.h"
|
|
|
|
#include "nsIDOMCharacterData.h"
|
|
|
|
#include "nsIDOMDocument.h"
|
|
|
|
#include "nsIDOMDocumentType.h"
|
2010-06-11 01:23:18 -07:00
|
|
|
#include "nsIDOMXULDocument.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "nsIDOMMutationEvent.h"
|
|
|
|
#include "nsPIDOMWindow.h"
|
|
|
|
#include "nsIDOMXULPopupElement.h"
|
|
|
|
#include "nsIEditingSession.h"
|
2011-04-21 10:35:52 -07:00
|
|
|
#include "nsEventStateManager.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "nsIFrame.h"
|
|
|
|
#include "nsHTMLSelectAccessible.h"
|
|
|
|
#include "nsIInterfaceRequestorUtils.h"
|
|
|
|
#include "nsINameSpaceManager.h"
|
|
|
|
#include "nsIPresShell.h"
|
|
|
|
#include "nsIServiceManager.h"
|
|
|
|
#include "nsIViewManager.h"
|
2009-09-02 20:57:41 -07:00
|
|
|
#include "nsIScrollableFrame.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "nsUnicharUtils.h"
|
|
|
|
#include "nsIURI.h"
|
|
|
|
#include "nsIWebNavigation.h"
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
#include "nsFocusManager.h"
|
2010-08-24 00:05:56 -07:00
|
|
|
#include "mozilla/dom/Element.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef MOZ_XUL
|
|
|
|
#include "nsIXULDocument.h"
|
|
|
|
#endif
|
|
|
|
|
2011-10-10 22:50:08 -07:00
|
|
|
using namespace mozilla;
|
2011-07-27 05:43:01 -07:00
|
|
|
using namespace mozilla::a11y;
|
2010-08-24 00:05:56 -07:00
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Static member initialization
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-11-17 18:55:44 -08:00
|
|
|
static nsIAtom** kRelationAttrs[] =
|
|
|
|
{
|
2011-06-03 14:35:17 -07:00
|
|
|
&nsGkAtoms::aria_labelledby,
|
|
|
|
&nsGkAtoms::aria_describedby,
|
|
|
|
&nsGkAtoms::aria_owns,
|
|
|
|
&nsGkAtoms::aria_controls,
|
|
|
|
&nsGkAtoms::aria_flowto,
|
|
|
|
&nsGkAtoms::_for,
|
|
|
|
&nsGkAtoms::control
|
2010-11-17 18:55:44 -08:00
|
|
|
};
|
|
|
|
|
2011-10-12 12:21:53 -07:00
|
|
|
static const PRUint32 kRelationAttrsLen = NS_ARRAY_LENGTH(kRelationAttrs);
|
2009-12-10 11:12:19 -08:00
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Constructor/desctructor
|
|
|
|
|
2010-06-11 01:23:18 -07:00
|
|
|
nsDocAccessible::
|
2012-02-07 14:38:54 -08:00
|
|
|
nsDocAccessible(nsIDocument* aDocument, nsIContent* aRootContent,
|
2012-02-09 08:49:17 -08:00
|
|
|
nsIPresShell* aPresShell) :
|
2012-02-07 14:38:54 -08:00
|
|
|
nsHyperTextAccessibleWrap(aRootContent, this),
|
2011-08-08 00:55:36 -07:00
|
|
|
mDocument(aDocument), mScrollPositionChangedTicks(0),
|
2012-02-01 22:14:51 -08:00
|
|
|
mLoadState(eTreeConstructionPending), mLoadEventType(0),
|
2012-02-07 14:38:54 -08:00
|
|
|
mVirtualCursor(nsnull),
|
2012-02-09 08:49:17 -08:00
|
|
|
mPresShell(aPresShell)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2011-05-19 22:17:47 -07:00
|
|
|
mFlags |= eDocAccessible;
|
|
|
|
|
2010-11-17 18:55:44 -08:00
|
|
|
mDependentIDsHash.Init();
|
2009-08-10 23:59:05 -07:00
|
|
|
// XXX aaronl should we use an algorithm for the initial cache size?
|
2010-06-11 21:04:35 -07:00
|
|
|
mAccessibleCache.Init(kDefaultCacheSize);
|
2010-10-20 21:16:10 -07:00
|
|
|
mNodeToAccessibleMap.Init(kDefaultCacheSize);
|
2009-08-10 23:59:05 -07:00
|
|
|
|
2011-02-14 18:44:15 -08:00
|
|
|
// If this is a XUL Document, it should not implement nsHyperText
|
|
|
|
if (mDocument && mDocument->IsXUL())
|
|
|
|
mFlags &= ~eHyperTextAccessible;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// For GTK+ native window, we do nothing here.
|
2010-06-11 01:23:18 -07:00
|
|
|
if (!mDocument)
|
2007-03-22 10:30:00 -07:00
|
|
|
return;
|
|
|
|
|
2010-06-08 09:39:58 -07:00
|
|
|
// nsAccDocManager creates document accessible when scrollable frame is
|
|
|
|
// available already, it should be safe time to add scroll listener.
|
|
|
|
AddScrollListener();
|
2012-02-01 22:14:51 -08:00
|
|
|
|
|
|
|
// We provide a virtual cursor if this is a root doc or if it's a tab doc.
|
|
|
|
mIsCursorable = (!(mDocument->GetParentDocument()) ||
|
|
|
|
nsCoreUtils::IsTabDocument(mDocument));
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nsDocAccessible::~nsDocAccessible()
|
|
|
|
{
|
2012-02-07 14:38:54 -08:00
|
|
|
NS_ASSERTION(!mPresShell, "LastRelease was never called!?!");
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2012-01-31 13:14:30 -08:00
|
|
|
|
2008-08-06 05:19:56 -07:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2009-12-10 11:12:19 -08:00
|
|
|
// nsISupports
|
2008-08-06 05:19:56 -07:00
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocAccessible)
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
|
2011-06-09 02:44:31 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument)
|
2011-01-18 00:03:38 -08:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mNotificationController,
|
|
|
|
NotificationController)
|
2009-09-07 09:46:56 -07:00
|
|
|
|
2012-02-01 22:14:51 -08:00
|
|
|
if (tmp->mVirtualCursor) {
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mVirtualCursor,
|
|
|
|
nsAccessiblePivot)
|
|
|
|
}
|
|
|
|
|
2010-09-09 07:44:56 -07:00
|
|
|
PRUint32 i, length = tmp->mChildDocuments.Length();
|
|
|
|
for (i = 0; i < length; ++i) {
|
2011-06-09 02:44:31 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mChildDocuments[i],
|
|
|
|
nsIAccessible)
|
2010-09-09 07:44:56 -07:00
|
|
|
}
|
|
|
|
|
2010-06-11 21:04:35 -07:00
|
|
|
CycleCollectorTraverseCache(tmp->mAccessibleCache, &cb);
|
2008-08-06 05:19:56 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
|
2011-06-09 02:44:31 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
|
2011-01-18 00:03:38 -08:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mNotificationController)
|
2012-02-01 22:14:51 -08:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mVirtualCursor)
|
2010-09-09 07:44:56 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mChildDocuments)
|
2010-11-17 18:55:44 -08:00
|
|
|
tmp->mDependentIDsHash.Clear();
|
2010-10-20 21:16:10 -07:00
|
|
|
tmp->mNodeToAccessibleMap.Clear();
|
2010-06-11 21:04:35 -07:00
|
|
|
ClearCache(tmp->mAccessibleCache);
|
2008-08-06 05:19:56 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDocAccessible)
|
2009-06-24 19:08:53 -07:00
|
|
|
NS_INTERFACE_MAP_STATIC_AMBIGUOUS(nsDocAccessible)
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIAccessibleDocument)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
2012-02-01 22:14:51 -08:00
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivotObserver)
|
2009-06-24 19:08:53 -07:00
|
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessibleDocument)
|
2012-02-01 22:14:51 -08:00
|
|
|
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleCursorable,
|
|
|
|
mIsCursorable)
|
2010-06-11 01:23:18 -07: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.
|
|
|
|
|
2011-02-14 18:44:15 -08:00
|
|
|
status = IsHyperText() ?
|
|
|
|
nsHyperTextAccessible::QueryInterface(aIID,
|
|
|
|
(void**)&foundInterface) :
|
|
|
|
nsAccessible::QueryInterface(aIID, (void**)&foundInterface);
|
2010-06-11 01:23:18 -07:00
|
|
|
} else {
|
|
|
|
NS_ADDREF(foundInterface);
|
|
|
|
status = NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
*aInstancePtr = foundInterface;
|
|
|
|
return status;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
NS_IMPL_ADDREF_INHERITED(nsDocAccessible, nsHyperTextAccessible)
|
|
|
|
NS_IMPL_RELEASE_INHERITED(nsDocAccessible, nsHyperTextAccessible)
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsIAccessible
|
|
|
|
|
2008-10-10 05:26:55 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDocAccessible::GetName(nsAString& aName)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
aName.Truncate();
|
2008-03-14 13:49:38 -07:00
|
|
|
if (mParent) {
|
|
|
|
rv = mParent->GetName(aName); // Allow owning iframe to override the name
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
if (aName.IsEmpty()) {
|
2008-10-10 05:26:55 -07:00
|
|
|
// Allow name via aria-labelledby or title attribute
|
|
|
|
rv = nsAccessible::GetName(aName);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2008-03-14 13:49:38 -07:00
|
|
|
if (aName.IsEmpty()) {
|
2008-04-15 08:17:59 -07:00
|
|
|
rv = GetTitle(aName); // Try title element
|
|
|
|
}
|
|
|
|
if (aName.IsEmpty()) { // Last resort: use URL
|
|
|
|
rv = GetURL(aName);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
// nsAccessible public method
|
2012-01-11 19:07:35 -08:00
|
|
|
role
|
2010-09-04 19:14:01 -07:00
|
|
|
nsDocAccessible::NativeRole()
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
|
2010-06-11 01:23:18 -07:00
|
|
|
nsCoreUtils::GetDocShellTreeItemFor(mDocument);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (docShellTreeItem) {
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
|
|
|
|
docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
|
2007-06-14 10:12:50 -07:00
|
|
|
PRInt32 itemType;
|
|
|
|
docShellTreeItem->GetItemType(&itemType);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (sameTypeRoot == docShellTreeItem) {
|
|
|
|
// Root of content or chrome tree
|
2010-09-04 19:14:01 -07:00
|
|
|
if (itemType == nsIDocShellTreeItem::typeChrome)
|
2012-01-11 19:07:35 -08:00
|
|
|
return roles::CHROME_WINDOW;
|
2010-09-04 19:14:01 -07:00
|
|
|
|
|
|
|
if (itemType == nsIDocShellTreeItem::typeContent) {
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef MOZ_XUL
|
|
|
|
nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
|
2010-09-04 19:14:01 -07:00
|
|
|
if (xulDoc)
|
2012-01-11 19:07:35 -08:00
|
|
|
return roles::APPLICATION;
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
2012-01-11 19:07:35 -08:00
|
|
|
return roles::DOCUMENT;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
2007-06-14 10:12:50 -07:00
|
|
|
else if (itemType == nsIDocShellTreeItem::typeContent) {
|
2012-01-11 19:07:35 -08:00
|
|
|
return roles::DOCUMENT;
|
2007-06-14 10:12:50 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2012-01-11 19:07:35 -08:00
|
|
|
return roles::PANE; // Fall back;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
// nsAccessible public method
|
2009-06-18 00:37:38 -07:00
|
|
|
void
|
|
|
|
nsDocAccessible::SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2009-06-18 00:37:38 -07:00
|
|
|
NS_ASSERTION(mDocument, "No document during initialization!");
|
|
|
|
if (!mDocument)
|
|
|
|
return;
|
2008-03-14 13:49:38 -07:00
|
|
|
|
|
|
|
mRoleMapEntry = aRoleMapEntry;
|
|
|
|
|
|
|
|
nsIDocument *parentDoc = mDocument->GetParentDocument();
|
2009-06-18 00:37:38 -07:00
|
|
|
if (!parentDoc)
|
2009-06-29 06:26:45 -07:00
|
|
|
return; // No parent document for the root document
|
2009-06-18 00:37:38 -07:00
|
|
|
|
2009-06-29 06:26:45 -07:00
|
|
|
// Allow use of ARIA role from outer to override
|
2008-03-14 13:49:38 -07:00
|
|
|
nsIContent *ownerContent = parentDoc->FindContentForSubDocument(mDocument);
|
2010-06-11 01:23:18 -07:00
|
|
|
if (ownerContent) {
|
|
|
|
nsRoleMapEntry *roleMapEntry = nsAccUtils::GetRoleMapEntry(ownerContent);
|
2008-03-14 13:49:38 -07:00
|
|
|
if (roleMapEntry)
|
|
|
|
mRoleMapEntry = roleMapEntry; // Override
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2011-04-23 06:14:05 -07:00
|
|
|
void
|
|
|
|
nsDocAccessible::Description(nsString& aDescription)
|
2007-07-01 23:14:11 -07:00
|
|
|
{
|
2008-03-14 13:49:38 -07:00
|
|
|
if (mParent)
|
2011-04-23 06:14:05 -07:00
|
|
|
mParent->Description(aDescription);
|
2008-03-14 13:49:38 -07:00
|
|
|
|
2011-04-23 06:14:05 -07:00
|
|
|
if (aDescription.IsEmpty())
|
2009-02-18 23:06:14 -08:00
|
|
|
nsTextEquivUtils::
|
2011-06-03 14:35:17 -07:00
|
|
|
GetTextEquivFromIDRefs(this, nsGkAtoms::aria_describedby,
|
2011-04-23 06:14:05 -07:00
|
|
|
aDescription);
|
2007-07-01 23:14:11 -07:00
|
|
|
}
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
// nsAccessible public method
|
2011-04-09 16:38:06 -07:00
|
|
|
PRUint64
|
|
|
|
nsDocAccessible::NativeState()
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2011-04-09 16:38:06 -07:00
|
|
|
// The root content of the document might be removed so that mContent is
|
|
|
|
// out of date.
|
|
|
|
PRUint64 state = (mContent->GetCurrentDoc() == mDocument) ?
|
|
|
|
0 : states::STALE;
|
2007-04-02 08:56:24 -07:00
|
|
|
|
2011-09-27 18:46:11 -07:00
|
|
|
// Document is always focusable.
|
|
|
|
state |= states::FOCUSABLE;
|
|
|
|
if (FocusMgr()->IsFocused(this))
|
|
|
|
state |= states::FOCUSED;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2011-08-08 00:55:36 -07:00
|
|
|
// Expose stale state until the document is ready (DOM is loaded and tree is
|
|
|
|
// constructed).
|
|
|
|
if (!HasLoadState(eReady))
|
|
|
|
state |= states::STALE;
|
|
|
|
|
|
|
|
// Expose state busy until the document and all its subdocuments is completely
|
|
|
|
// loaded.
|
|
|
|
if (!HasLoadState(eCompletelyLoaded))
|
|
|
|
state |= states::BUSY;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsIFrame* frame = GetFrame();
|
2011-10-26 16:57:55 -07:00
|
|
|
if (!frame ||
|
|
|
|
!frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY)) {
|
2011-04-09 16:38:06 -07:00
|
|
|
state |= states::INVISIBLE | states::OFFSCREEN;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2012-03-07 19:28:38 -08:00
|
|
|
nsCOMPtr<nsIEditor> editor = GetEditor();
|
2011-04-09 16:38:06 -07:00
|
|
|
state |= editor ? states::EDITABLE : states::READONLY;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2011-04-09 16:38:06 -07:00
|
|
|
return state;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
// nsAccessible public method
|
2011-04-09 16:38:06 -07:00
|
|
|
void
|
|
|
|
nsDocAccessible::ApplyARIAState(PRUint64* aState)
|
2008-03-14 13:49:38 -07:00
|
|
|
{
|
|
|
|
// Combine with states from outer doc
|
2011-04-09 16:38:06 -07:00
|
|
|
//
|
|
|
|
nsAccessible::ApplyARIAState(aState);
|
2008-03-14 13:49:38 -07:00
|
|
|
|
2011-04-09 16:38:06 -07:00
|
|
|
// Allow iframe/frame etc. to have final state override via ARIA
|
|
|
|
if (mParent)
|
|
|
|
mParent->ApplyARIAState(aState);
|
2008-03-14 13:49:38 -07:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDocAccessible::GetAttributes(nsIPersistentProperties **aAttributes)
|
|
|
|
{
|
|
|
|
nsAccessible::GetAttributes(aAttributes);
|
|
|
|
if (mParent) {
|
|
|
|
mParent->GetAttributes(aAttributes); // Add parent attributes (override inner)
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2011-07-15 15:58:49 -07:00
|
|
|
nsAccessible*
|
|
|
|
nsDocAccessible::FocusedChild()
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
// Return an accessible for the current global focus, which does not have to
|
|
|
|
// be contained within the current document.
|
2011-09-27 18:46:11 -07:00
|
|
|
return FocusMgr()->FocusedAccessible();
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsDocAccessible::TakeFocus()
|
|
|
|
{
|
2010-06-11 01:23:18 -07:00
|
|
|
if (IsDefunct())
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
// Focus the document.
|
2012-02-11 18:34:00 -08:00
|
|
|
nsFocusManager* fm = nsFocusManager::GetFocusManager();
|
2010-06-11 01:23:18 -07:00
|
|
|
NS_ENSURE_STATE(fm);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMElement> newFocus;
|
|
|
|
return fm->MoveFocus(mDocument->GetWindow(), nsnull,
|
|
|
|
nsIFocusManager::MOVEFOCUS_ROOT, 0,
|
|
|
|
getter_AddRefs(newFocus));
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsIAccessibleDocument
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
NS_IMETHODIMP nsDocAccessible::GetURL(nsAString& aURL)
|
|
|
|
{
|
2010-06-11 01:23:18 -07:00
|
|
|
if (IsDefunct())
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsISupports> container = mDocument->GetContainer();
|
|
|
|
nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(container));
|
|
|
|
nsCAutoString theURL;
|
|
|
|
if (webNav) {
|
|
|
|
nsCOMPtr<nsIURI> pURI;
|
|
|
|
webNav->GetCurrentURI(getter_AddRefs(pURI));
|
|
|
|
if (pURI)
|
|
|
|
pURI->GetSpec(theURL);
|
|
|
|
}
|
|
|
|
CopyUTF8toUTF16(theURL, aURL);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2011-05-28 00:43:53 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDocAccessible::GetTitle(nsAString& aTitle)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2011-05-28 00:43:53 -07:00
|
|
|
nsCOMPtr<nsIDOMDocument> domDocument = do_QueryInterface(mDocument);
|
|
|
|
if (!domDocument) {
|
|
|
|
return NS_ERROR_FAILURE;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2011-05-28 00:43:53 -07:00
|
|
|
return domDocument->GetTitle(aTitle);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2011-05-28 00:43:53 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDocAccessible::GetMimeType(nsAString& aMimeType)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2011-05-28 00:43:53 -07:00
|
|
|
nsCOMPtr<nsIDOMDocument> domDocument = do_QueryInterface(mDocument);
|
|
|
|
if (!domDocument) {
|
|
|
|
return NS_ERROR_FAILURE;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2011-05-28 00:43:53 -07:00
|
|
|
return domDocument->GetContentType(aMimeType);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsDocAccessible::GetDocType(nsAString& aDocType)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(mDocument));
|
|
|
|
nsCOMPtr<nsIDOMDocumentType> docType;
|
|
|
|
|
|
|
|
#ifdef MOZ_XUL
|
|
|
|
nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocument));
|
|
|
|
if (xulDoc) {
|
|
|
|
aDocType.AssignLiteral("window"); // doctype not implemented for XUL at time of writing - causes assertion
|
|
|
|
return NS_OK;
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
if (domDoc && NS_SUCCEEDED(domDoc->GetDoctype(getter_AddRefs(docType))) && docType) {
|
|
|
|
return docType->GetPublicId(aDocType);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsDocAccessible::GetNameSpaceURIForID(PRInt16 aNameSpaceID, nsAString& aNameSpaceURI)
|
|
|
|
{
|
|
|
|
if (mDocument) {
|
|
|
|
nsCOMPtr<nsINameSpaceManager> nameSpaceManager =
|
|
|
|
do_GetService(NS_NAMESPACEMANAGER_CONTRACTID);
|
|
|
|
if (nameSpaceManager)
|
|
|
|
return nameSpaceManager->GetNameSpaceURI(aNameSpaceID, aNameSpaceURI);
|
|
|
|
}
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsDocAccessible::GetWindowHandle(void **aWindow)
|
|
|
|
{
|
2010-09-16 20:23:17 -07:00
|
|
|
NS_ENSURE_ARG_POINTER(aWindow);
|
|
|
|
*aWindow = GetNativeWindow();
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsDocAccessible::GetWindow(nsIDOMWindow **aDOMWin)
|
|
|
|
{
|
|
|
|
*aDOMWin = nsnull;
|
|
|
|
if (!mDocument) {
|
|
|
|
return NS_ERROR_FAILURE; // Accessible is Shutdown()
|
|
|
|
}
|
|
|
|
*aDOMWin = mDocument->GetWindow();
|
|
|
|
|
|
|
|
if (!*aDOMWin)
|
|
|
|
return NS_ERROR_FAILURE; // No DOM Window
|
|
|
|
|
|
|
|
NS_ADDREF(*aDOMWin);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2010-04-26 20:19:49 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDocAccessible::GetDOMDocument(nsIDOMDocument **aDOMDocument)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2010-04-26 20:19:49 -07:00
|
|
|
NS_ENSURE_ARG_POINTER(aDOMDocument);
|
|
|
|
*aDOMDocument = nsnull;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-04-26 20:19:49 -07:00
|
|
|
if (mDocument)
|
|
|
|
CallQueryInterface(mDocument, aDOMDocument);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-04-26 20:19:49 -07:00
|
|
|
return NS_OK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2010-09-09 07:44:56 -07: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;
|
|
|
|
}
|
|
|
|
|
2012-02-01 22:14:51 -08:00
|
|
|
// nsIAccessibleVirtualCursor method
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDocAccessible::GetVirtualCursor(nsIAccessiblePivot** aVirtualCursor)
|
|
|
|
{
|
|
|
|
NS_ENSURE_ARG_POINTER(aVirtualCursor);
|
|
|
|
*aVirtualCursor = nsnull;
|
|
|
|
|
|
|
|
if (IsDefunct())
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
NS_ENSURE_TRUE(mIsCursorable, NS_ERROR_NOT_IMPLEMENTED);
|
|
|
|
|
|
|
|
if (!mVirtualCursor) {
|
|
|
|
mVirtualCursor = new nsAccessiblePivot(this);
|
|
|
|
mVirtualCursor->AddObserver(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_ADDREF(*aVirtualCursor = mVirtualCursor);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2012-03-07 19:28:38 -08:00
|
|
|
// nsHyperTextAccessible method
|
|
|
|
already_AddRefed<nsIEditor>
|
|
|
|
nsDocAccessible::GetEditor() const
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-10-08 05:50:36 -07:00
|
|
|
// 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 01:23:18 -07:00
|
|
|
if (!mDocument->HasFlag(NODE_IS_EDITABLE) &&
|
|
|
|
!mContent->HasFlag(NODE_IS_EDITABLE))
|
2012-03-07 19:28:38 -08:00
|
|
|
return nsnull;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
nsCOMPtr<nsISupports> container = mDocument->GetContainer();
|
|
|
|
nsCOMPtr<nsIEditingSession> editingSession(do_GetInterface(container));
|
|
|
|
if (!editingSession)
|
2012-03-07 19:28:38 -08:00
|
|
|
return nsnull; // No editing session interface
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
nsCOMPtr<nsIEditor> editor;
|
2007-08-14 09:25:24 -07:00
|
|
|
editingSession->GetEditorForWindow(mDocument->GetWindow(), getter_AddRefs(editor));
|
2012-03-07 19:28:38 -08:00
|
|
|
if (!editor)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
bool isEditable = false;
|
2007-08-14 09:25:24 -07:00
|
|
|
editor->GetIsDocumentEditable(&isEditable);
|
2012-03-07 19:28:38 -08:00
|
|
|
if (isEditable)
|
|
|
|
return editor.forget();
|
|
|
|
|
|
|
|
return nsnull;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2010-09-09 07:44:56 -07:00
|
|
|
// nsDocAccessible public method
|
2011-01-27 20:37:08 -08:00
|
|
|
nsAccessible*
|
|
|
|
nsDocAccessible::GetAccessible(nsINode* aNode) const
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2010-10-20 21:16:10 -07:00
|
|
|
nsAccessible* accessible = mNodeToAccessibleMap.Get(aNode);
|
2010-02-11 05:56:01 -08:00
|
|
|
|
|
|
|
// No accessible in the cache, check if the given ID is unique ID of this
|
2010-06-11 21:04:35 -07:00
|
|
|
// document accessible.
|
|
|
|
if (!accessible) {
|
2010-10-20 21:16:10 -07:00
|
|
|
if (GetNode() != aNode)
|
2010-06-11 21:04:35 -07:00
|
|
|
return nsnull;
|
|
|
|
|
2011-01-27 20:37:08 -08:00
|
|
|
accessible = const_cast<nsDocAccessible*>(this);
|
2010-02-11 05:56:01 -08:00
|
|
|
}
|
|
|
|
|
2010-02-09 05:29:22 -08:00
|
|
|
#ifdef DEBUG
|
2007-03-22 10:30:00 -07: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.
|
2011-07-23 01:38:33 -07:00
|
|
|
nsAccessible* parent = accessible->Parent();
|
2010-06-11 21:04:35 -07:00
|
|
|
if (parent)
|
|
|
|
parent->TestChildCache(accessible);
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
2010-02-11 05:58:35 -08:00
|
|
|
|
2010-06-11 21:04:35 -07:00
|
|
|
return accessible;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsAccessNode
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2011-09-28 23:19:26 -07:00
|
|
|
bool
|
2008-10-31 20:58:07 -07:00
|
|
|
nsDocAccessible::Init()
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2010-06-17 19:44:09 -07:00
|
|
|
NS_LOG_ACCDOCCREATE_FOR("document initialize", mDocument, this)
|
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
// Initialize notification controller.
|
2012-02-07 14:38:54 -08:00
|
|
|
mNotificationController = new NotificationController(this, mPresShell);
|
2011-01-18 00:03:38 -08:00
|
|
|
if (!mNotificationController)
|
2011-10-17 07:59:28 -07:00
|
|
|
return false;
|
2010-02-20 16:56:35 -08:00
|
|
|
|
2011-05-26 19:18:00 -07:00
|
|
|
// Mark the document accessible as loaded if its DOM document was loaded at
|
|
|
|
// this point (this can happen because a11y is started late or DOM document
|
|
|
|
// having no container was loaded.
|
|
|
|
if (mDocument->GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE)
|
2011-08-08 00:55:36 -07:00
|
|
|
mLoadState |= eDOMLoaded;
|
2011-05-26 19:18:00 -07:00
|
|
|
|
2010-02-20 16:56:35 -08:00
|
|
|
AddEventListeners();
|
2011-10-17 07:59:28 -07:00
|
|
|
return true;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2010-06-11 21:04:35 -07:00
|
|
|
void
|
2008-10-31 20:58:07 -07:00
|
|
|
nsDocAccessible::Shutdown()
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2012-02-07 14:38:54 -08:00
|
|
|
if (!mPresShell) // already shutdown
|
2010-06-11 21:04:35 -07:00
|
|
|
return;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-06-08 09:39:58 -07:00
|
|
|
NS_LOG_ACCDOCDESTROY_FOR("document shutdown", mDocument, this)
|
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
if (mNotificationController) {
|
|
|
|
mNotificationController->Shutdown();
|
|
|
|
mNotificationController = nsnull;
|
2010-02-26 11:02:39 -08:00
|
|
|
}
|
2010-01-27 03:42:08 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
RemoveEventListeners();
|
|
|
|
|
2011-03-28 07:00:02 -07:00
|
|
|
// Mark the document as shutdown before AT is notified about the document
|
|
|
|
// removal from its container (valid for root documents on ATK).
|
|
|
|
nsCOMPtr<nsIDocument> kungFuDeathGripDoc = mDocument;
|
|
|
|
mDocument = nsnull;
|
|
|
|
|
2010-09-09 07:44:56 -07:00
|
|
|
if (mParent) {
|
2011-11-09 14:52:00 -08:00
|
|
|
nsDocAccessible* parentDocument = mParent->Document();
|
2010-09-09 07:44:56 -07:00
|
|
|
if (parentDocument)
|
|
|
|
parentDocument->RemoveChildDocument(this);
|
|
|
|
|
2010-06-08 09:39:58 -07:00
|
|
|
mParent->RemoveChild(this);
|
2010-09-09 07:44:56 -07:00
|
|
|
}
|
|
|
|
|
2010-11-09 11:34:25 -08: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 02:34:26 -07:00
|
|
|
mChildDocuments[idx]->Shutdown();
|
|
|
|
|
2010-09-09 07:44:56 -07:00
|
|
|
mChildDocuments.Clear();
|
2010-06-08 09:39:58 -07:00
|
|
|
|
2012-02-01 22:14:51 -08:00
|
|
|
if (mVirtualCursor) {
|
|
|
|
mVirtualCursor->RemoveObserver(this);
|
|
|
|
mVirtualCursor = nsnull;
|
|
|
|
}
|
|
|
|
|
2012-02-07 14:38:54 -08:00
|
|
|
mPresShell = nsnull; // Avoid reentrancy
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-11-17 18:55:44 -08:00
|
|
|
mDependentIDsHash.Clear();
|
2010-10-20 21:16:10 -07:00
|
|
|
mNodeToAccessibleMap.Clear();
|
2010-06-11 21:04:35 -07:00
|
|
|
ClearCache(mAccessibleCache);
|
2007-12-26 21:13:40 -08:00
|
|
|
|
|
|
|
nsHyperTextAccessibleWrap::Shutdown();
|
2010-10-28 02:34:26 -07:00
|
|
|
|
|
|
|
GetAccService()->NotifyOfDocumentShutdown(kungFuDeathGripDoc);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2008-10-31 20:58:07 -07:00
|
|
|
nsIFrame*
|
2011-02-22 05:54:56 -08:00
|
|
|
nsDocAccessible::GetFrame() const
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
nsIFrame* root = nsnull;
|
2012-02-07 14:38:54 -08:00
|
|
|
if (mPresShell)
|
|
|
|
root = mPresShell->GetRootFrame();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
2011-05-28 02:26:45 -07:00
|
|
|
bool
|
|
|
|
nsDocAccessible::IsDefunct() const
|
2009-06-24 19:08:53 -07:00
|
|
|
{
|
2010-06-11 01:23:18 -07:00
|
|
|
return nsHyperTextAccessibleWrap::IsDefunct() || !mDocument;
|
2009-06-24 19:08:53 -07:00
|
|
|
}
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
// nsDocAccessible protected member
|
2007-03-22 10:30:00 -07:00
|
|
|
void nsDocAccessible::GetBoundsRect(nsRect& aBounds, nsIFrame** aRelativeFrame)
|
|
|
|
{
|
|
|
|
*aRelativeFrame = GetFrame();
|
|
|
|
|
|
|
|
nsIDocument *document = mDocument;
|
|
|
|
nsIDocument *parentDoc = nsnull;
|
|
|
|
|
|
|
|
while (document) {
|
2010-06-25 06:59:57 -07:00
|
|
|
nsIPresShell *presShell = document->GetShell();
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!presShell) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-09-02 20:57:41 -07:00
|
|
|
nsRect scrollPort;
|
|
|
|
nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollableExternal();
|
|
|
|
if (sf) {
|
|
|
|
scrollPort = sf->GetScrollPortRect();
|
|
|
|
} else {
|
2010-01-20 17:07:35 -08:00
|
|
|
nsIFrame* rootFrame = presShell->GetRootFrame();
|
|
|
|
if (!rootFrame) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
scrollPort = rootFrame->GetRect();
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (parentDoc) { // After first time thru loop
|
2009-09-02 20:57:41 -07: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);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
else { // First time through loop
|
2009-09-02 20:57:41 -07:00
|
|
|
aBounds = scrollPort;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
document = parentDoc = document->GetParentDocument();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
// nsDocAccessible protected member
|
2007-03-22 10:30:00 -07:00
|
|
|
nsresult nsDocAccessible::AddEventListeners()
|
|
|
|
{
|
|
|
|
// 1) Set up scroll position listener
|
|
|
|
// 2) Check for editor and listen for changes to editor
|
|
|
|
|
2012-02-07 14:38:54 -08:00
|
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_FAILURE);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
nsCOMPtr<nsISupports> container = mDocument->GetContainer();
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(do_QueryInterface(container));
|
|
|
|
NS_ENSURE_TRUE(docShellTreeItem, NS_ERROR_FAILURE);
|
|
|
|
|
|
|
|
// Make sure we're a content docshell
|
|
|
|
// We don't want to listen to chrome progress
|
|
|
|
PRInt32 itemType;
|
|
|
|
docShellTreeItem->GetItemType(&itemType);
|
|
|
|
|
2011-09-28 23:19:26 -07:00
|
|
|
bool isContent = (itemType == nsIDocShellTreeItem::typeContent);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (isContent) {
|
2007-08-14 09:25:24 -07:00
|
|
|
// We're not an editor yet, but we might become one
|
|
|
|
nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(docShellTreeItem);
|
|
|
|
if (commandManager) {
|
|
|
|
commandManager->AddCommandObserver(this, "obs_documentCreated");
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-05-07 11:55:17 -07:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
|
|
|
|
docShellTreeItem->GetRootTreeItem(getter_AddRefs(rootTreeItem));
|
|
|
|
if (rootTreeItem) {
|
2011-03-01 21:16:28 -08:00
|
|
|
nsRootAccessible* rootAccessible = RootAccessible();
|
2007-06-14 10:12:50 -07:00
|
|
|
NS_ENSURE_TRUE(rootAccessible, NS_ERROR_FAILURE);
|
|
|
|
nsRefPtr<nsCaretAccessible> caretAccessible = rootAccessible->GetCaretAccessible();
|
|
|
|
if (caretAccessible) {
|
2012-02-07 14:38:54 -08:00
|
|
|
caretAccessible->AddDocSelectionListener(mPresShell);
|
2007-05-07 11:55:17 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// add document observer
|
|
|
|
mDocument->AddObserver(this);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
// nsDocAccessible protected member
|
2007-03-22 10:30:00 -07:00
|
|
|
nsresult nsDocAccessible::RemoveEventListeners()
|
|
|
|
{
|
|
|
|
// Remove listeners associated with content documents
|
|
|
|
// Remove scroll position listener
|
|
|
|
RemoveScrollListener();
|
|
|
|
|
2009-06-28 10:45:04 -07:00
|
|
|
NS_ASSERTION(mDocument, "No document during removal of listeners.");
|
|
|
|
|
|
|
|
if (mDocument) {
|
|
|
|
mDocument->RemoveObserver(this);
|
|
|
|
|
|
|
|
nsCOMPtr<nsISupports> container = mDocument->GetContainer();
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(do_QueryInterface(container));
|
|
|
|
NS_ASSERTION(docShellTreeItem, "doc should support nsIDocShellTreeItem.");
|
|
|
|
|
|
|
|
if (docShellTreeItem) {
|
|
|
|
PRInt32 itemType;
|
|
|
|
docShellTreeItem->GetItemType(&itemType);
|
|
|
|
if (itemType == nsIDocShellTreeItem::typeContent) {
|
|
|
|
nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(docShellTreeItem);
|
|
|
|
if (commandManager) {
|
|
|
|
commandManager->RemoveCommandObserver(this, "obs_documentCreated");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (mScrollWatchTimer) {
|
|
|
|
mScrollWatchTimer->Cancel();
|
|
|
|
mScrollWatchTimer = nsnull;
|
2008-01-18 12:36:44 -08:00
|
|
|
NS_RELEASE_THIS(); // Kung fu death grip
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2011-03-01 21:16:28 -08:00
|
|
|
nsRootAccessible* rootAccessible = RootAccessible();
|
2007-05-07 11:55:17 -07:00
|
|
|
if (rootAccessible) {
|
2007-06-14 10:12:50 -07:00
|
|
|
nsRefPtr<nsCaretAccessible> caretAccessible = rootAccessible->GetCaretAccessible();
|
2012-02-07 14:38:54 -08:00
|
|
|
if (caretAccessible)
|
|
|
|
caretAccessible->RemoveDocSelectionListener(mPresShell);
|
2007-05-07 11:55:17 -07:00
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nsDocAccessible::ScrollTimerCallback(nsITimer *aTimer, void *aClosure)
|
|
|
|
{
|
2007-07-08 00:08:04 -07:00
|
|
|
nsDocAccessible *docAcc = reinterpret_cast<nsDocAccessible*>(aClosure);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (docAcc && docAcc->mScrollPositionChangedTicks &&
|
|
|
|
++docAcc->mScrollPositionChangedTicks > 2) {
|
|
|
|
// Whenever scroll position changes, mScrollPositionChangeTicks gets reset to 1
|
|
|
|
// We only want to fire accessibilty scroll event when scrolling stops or pauses
|
|
|
|
// Therefore, we wait for no scroll events to occur between 2 ticks of this timer
|
|
|
|
// That indicates a pause in scrolling, so we fire the accessibilty scroll event
|
2010-01-18 08:16:07 -08:00
|
|
|
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SCROLLING_END, docAcc);
|
2007-07-05 09:02:55 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
docAcc->mScrollPositionChangedTicks = 0;
|
|
|
|
if (docAcc->mScrollWatchTimer) {
|
|
|
|
docAcc->mScrollWatchTimer->Cancel();
|
|
|
|
docAcc->mScrollWatchTimer = nsnull;
|
2008-01-18 12:36:44 -08:00
|
|
|
NS_RELEASE(docAcc); // Release kung fu death grip
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
// nsDocAccessible protected member
|
2007-03-22 10:30:00 -07:00
|
|
|
void nsDocAccessible::AddScrollListener()
|
|
|
|
{
|
2012-02-07 14:38:54 -08:00
|
|
|
if (!mPresShell)
|
2009-09-02 20:57:41 -07:00
|
|
|
return;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2012-02-07 14:38:54 -08:00
|
|
|
nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollableExternal();
|
2009-09-02 20:57:41 -07:00
|
|
|
if (sf) {
|
|
|
|
sf->AddScrollPositionListener(this);
|
2010-06-08 09:39:58 -07:00
|
|
|
NS_LOG_ACCDOCCREATE_TEXT("add scroll listener")
|
2009-09-02 20:57:41 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
// nsDocAccessible protected member
|
2007-03-22 10:30:00 -07:00
|
|
|
void nsDocAccessible::RemoveScrollListener()
|
|
|
|
{
|
2012-02-07 14:38:54 -08:00
|
|
|
if (!mPresShell)
|
2009-09-02 20:57:41 -07:00
|
|
|
return;
|
|
|
|
|
2012-02-07 14:38:54 -08:00
|
|
|
nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollableExternal();
|
2009-09-02 20:57:41 -07:00
|
|
|
if (sf) {
|
|
|
|
sf->RemoveScrollPositionListener(this);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsIScrollPositionListener
|
|
|
|
|
2010-01-28 16:03:42 -08:00
|
|
|
void nsDocAccessible::ScrollPositionDidChange(nscoord aX, nscoord aY)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
// Start new timer, if the timer cycles at least 1 full cycle without more scroll position changes,
|
|
|
|
// then the ::Notify() method will fire the accessibility event for scroll position changes
|
|
|
|
const PRUint32 kScrollPosCheckWait = 50;
|
|
|
|
if (mScrollWatchTimer) {
|
|
|
|
mScrollWatchTimer->SetDelay(kScrollPosCheckWait); // Create new timer, to avoid leaks
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mScrollWatchTimer = do_CreateInstance("@mozilla.org/timer;1");
|
|
|
|
if (mScrollWatchTimer) {
|
2008-01-18 12:36:44 -08:00
|
|
|
NS_ADDREF_THIS(); // Kung fu death grip
|
2007-03-22 10:30:00 -07:00
|
|
|
mScrollWatchTimer->InitWithFuncCallback(ScrollTimerCallback, this,
|
|
|
|
kScrollPosCheckWait,
|
|
|
|
nsITimer::TYPE_REPEATING_SLACK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mScrollPositionChangedTicks = 1;
|
|
|
|
}
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsIObserver
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_IMETHODIMP nsDocAccessible::Observe(nsISupports *aSubject, const char *aTopic,
|
|
|
|
const PRUnichar *aData)
|
|
|
|
{
|
2007-08-14 09:25:24 -07:00
|
|
|
if (!nsCRT::strcmp(aTopic,"obs_documentCreated")) {
|
|
|
|
// State editable will now be set, readonly is now clear
|
2010-06-02 05:30:08 -07:00
|
|
|
// Normally we only fire delayed events created from the node, not an
|
2010-08-24 19:08:28 -07:00
|
|
|
// accessible object. See the AccStateChangeEvent constructor for details
|
2010-06-02 05:30:08 -07:00
|
|
|
// about this exceptional case.
|
2010-08-24 19:08:28 -07:00
|
|
|
nsRefPtr<AccEvent> event =
|
2011-10-17 07:59:28 -07:00
|
|
|
new AccStateChangeEvent(this, states::EDITABLE, true);
|
2010-06-02 05:30:08 -07:00
|
|
|
FireDelayedAccessibleEvent(event);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2012-02-01 22:14:51 -08:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsIAccessiblePivotObserver
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsDocAccessible::OnPivotChanged(nsIAccessiblePivot* aPivot,
|
|
|
|
nsIAccessible* aOldAccessible,
|
|
|
|
PRInt32 aOldStart, PRInt32 aOldEnd)
|
|
|
|
{
|
|
|
|
nsRefPtr<AccEvent> event = new AccEvent(nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED, this);
|
|
|
|
nsEventShell::FireEvent(event);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2007-03-22 10:30:00 -07:00
|
|
|
// nsIDocumentObserver
|
|
|
|
|
|
|
|
NS_IMPL_NSIDOCUMENTOBSERVER_CORE_STUB(nsDocAccessible)
|
|
|
|
NS_IMPL_NSIDOCUMENTOBSERVER_LOAD_STUB(nsDocAccessible)
|
|
|
|
NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(nsDocAccessible)
|
|
|
|
|
2009-06-29 11:36:25 -07:00
|
|
|
void
|
|
|
|
nsDocAccessible::AttributeWillChange(nsIDocument *aDocument,
|
2010-08-24 00:06:20 -07:00
|
|
|
dom::Element* aElement,
|
|
|
|
PRInt32 aNameSpaceID,
|
2009-06-29 11:36:25 -07:00
|
|
|
nsIAtom* aAttribute, PRInt32 aModType)
|
|
|
|
{
|
2011-08-11 04:45:36 -07:00
|
|
|
nsAccessible* accessible = GetAccessible(aElement);
|
|
|
|
if (!accessible) {
|
|
|
|
if (aElement != mContent)
|
|
|
|
return;
|
|
|
|
|
|
|
|
accessible = this;
|
|
|
|
}
|
2010-11-17 18:55:44 -08:00
|
|
|
|
2010-12-16 11:29:51 -08:00
|
|
|
// Update dependent IDs cache. Take care of elements that are accessible
|
|
|
|
// because dependent IDs cache doesn't contain IDs from non accessible
|
|
|
|
// elements.
|
2011-08-11 04:45:36 -07:00
|
|
|
if (aModType != nsIDOMMutationEvent::ADDITION)
|
|
|
|
RemoveDependentIDsFor(accessible, aAttribute);
|
|
|
|
|
|
|
|
// Store the ARIA attribute old value so that it can be used after
|
|
|
|
// attribute change. Note, we assume there's no nested ARIA attribute
|
|
|
|
// changes. If this happens then we should end up with keeping a stack of
|
|
|
|
// old values.
|
|
|
|
|
|
|
|
// XXX TODO: bugs 472142, 472143.
|
|
|
|
// Here we will want to cache whatever attribute values we are 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.
|
2011-06-03 14:35:17 -07:00
|
|
|
if (aAttribute == nsGkAtoms::aria_checked ||
|
|
|
|
aAttribute == nsGkAtoms::aria_pressed) {
|
2011-08-11 04:45:36 -07:00
|
|
|
mARIAAttrOldValue = (aModType != nsIDOMMutationEvent::ADDITION) ?
|
|
|
|
nsAccUtils::GetARIAToken(aElement, aAttribute) : nsnull;
|
2010-11-17 18:55:44 -08:00
|
|
|
}
|
2009-06-29 11:36:25 -07:00
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
void
|
2010-08-24 00:05:56 -07:00
|
|
|
nsDocAccessible::AttributeChanged(nsIDocument *aDocument,
|
|
|
|
dom::Element* aElement,
|
2007-03-22 10:30:00 -07:00
|
|
|
PRInt32 aNameSpaceID, nsIAtom* aAttribute,
|
2009-12-10 14:36:04 -08:00
|
|
|
PRInt32 aModType)
|
2007-09-18 14:36:41 -07:00
|
|
|
{
|
2011-01-17 20:15:05 -08:00
|
|
|
NS_ASSERTION(!IsDefunct(),
|
|
|
|
"Attribute changed called on defunct document accessible!");
|
|
|
|
|
2010-12-18 10:33:00 -08: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 14:36:41 -07:00
|
|
|
|
2010-12-18 10:33:00 -08: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-17 20:15:05 -08: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.
|
2011-01-27 20:37:08 -08:00
|
|
|
nsAccessible* accessible = GetAccessible(aElement);
|
2011-02-21 17:56:57 -08:00
|
|
|
if (!accessible) {
|
|
|
|
if (mContent != aElement)
|
|
|
|
return;
|
|
|
|
|
|
|
|
accessible = this;
|
|
|
|
}
|
2010-12-16 11:29:51 -08:00
|
|
|
|
2010-12-18 10:33:00 -08: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 11:29:51 -08: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-17 18:55:44 -08:00
|
|
|
if (aModType == nsIDOMMutationEvent::MODIFICATION ||
|
|
|
|
aModType == nsIDOMMutationEvent::ADDITION) {
|
2010-12-16 11:29:51 -08:00
|
|
|
AddDependentIDsFor(accessible, aAttribute);
|
2010-11-17 18:55:44 -08:00
|
|
|
}
|
2007-09-18 14:36:41 -07:00
|
|
|
}
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
// nsDocAccessible protected member
|
2007-09-18 14:36:41 -07:00
|
|
|
void
|
|
|
|
nsDocAccessible::AttributeChangedImpl(nsIContent* aContent, PRInt32 aNameSpaceID, nsIAtom* aAttribute)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-04-16 21:45:42 -07:00
|
|
|
// Fire accessible event after short timer, because we need to wait for
|
|
|
|
// DOM attribute & resulting layout to actually change. Otherwise,
|
|
|
|
// assistive technology will retrieve the wrong state/value/selection info.
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// XXX todo
|
|
|
|
// We still need to handle special HTML cases here
|
|
|
|
// For example, if an <img>'s usemap attribute is modified
|
|
|
|
// Otherwise it may just be a state change, for example an object changing
|
|
|
|
// its visibility
|
2009-01-12 09:20:34 -08:00
|
|
|
//
|
|
|
|
// XXX todo: report aria state changes for "undefined" literal value changes
|
|
|
|
// filed as bug 472142
|
|
|
|
//
|
|
|
|
// XXX todo: invalidate accessible when aria state changes affect exposed role
|
|
|
|
// filed as bug 472143
|
2010-12-16 11:29:51 -08:00
|
|
|
|
2009-09-15 18:01:47 -07:00
|
|
|
// Universal boolean properties that don't require a role. Fire the state
|
|
|
|
// change when disabled or aria-disabled attribute is set.
|
2011-06-03 14:35:17 -07:00
|
|
|
if (aAttribute == nsGkAtoms::disabled ||
|
|
|
|
aAttribute == nsGkAtoms::aria_disabled) {
|
2009-09-15 18:01:47 -07: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-24 19:08:28 -07:00
|
|
|
nsRefPtr<AccEvent> enabledChangeEvent =
|
2011-04-09 16:38:06 -07:00
|
|
|
new AccStateChangeEvent(aContent, states::ENABLED);
|
2009-09-15 18:01:47 -07:00
|
|
|
|
2007-04-19 10:49:13 -07:00
|
|
|
FireDelayedAccessibleEvent(enabledChangeEvent);
|
2009-09-15 18:01:47 -07:00
|
|
|
|
2010-08-24 19:08:28 -07:00
|
|
|
nsRefPtr<AccEvent> sensitiveChangeEvent =
|
2011-04-09 16:38:06 -07:00
|
|
|
new AccStateChangeEvent(aContent, states::SENSITIVE);
|
2009-09-15 18:01:47 -07:00
|
|
|
|
2007-04-19 10:49:13 -07:00
|
|
|
FireDelayedAccessibleEvent(sensitiveChangeEvent);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-09-24 18:19:03 -07:00
|
|
|
// Check for namespaced ARIA attribute
|
2007-12-11 18:10:26 -08:00
|
|
|
if (aNameSpaceID == kNameSpaceID_None) {
|
2007-09-24 18:19:03 -07:00
|
|
|
// Check for hyphenated aria-foo property?
|
2010-03-08 07:45:00 -08:00
|
|
|
if (StringBeginsWith(nsDependentAtomString(aAttribute),
|
|
|
|
NS_LITERAL_STRING("aria-"))) {
|
2007-12-11 18:10:26 -08:00
|
|
|
ARIAAttributeChanged(aContent, aAttribute);
|
2007-09-24 18:19:03 -07:00
|
|
|
}
|
|
|
|
}
|
2007-04-19 10:49:13 -07:00
|
|
|
|
2011-06-03 14:35:17 -07:00
|
|
|
if (aAttribute == nsGkAtoms::alt ||
|
|
|
|
aAttribute == nsGkAtoms::title ||
|
|
|
|
aAttribute == nsGkAtoms::aria_label ||
|
|
|
|
aAttribute == nsGkAtoms::aria_labelledby) {
|
2009-09-02 19:01:18 -07:00
|
|
|
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE,
|
2010-06-11 01:23:18 -07:00
|
|
|
aContent);
|
2008-02-19 23:45:14 -08:00
|
|
|
return;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2011-06-03 14:35:17 -07:00
|
|
|
if (aAttribute == nsGkAtoms::aria_busy) {
|
2011-09-28 23:19:26 -07:00
|
|
|
bool isOn = aContent->AttrValueIs(aNameSpaceID, aAttribute,
|
2011-06-03 14:35:17 -07:00
|
|
|
nsGkAtoms::_true, eCaseMatters);
|
2011-05-24 07:02:30 -07:00
|
|
|
nsRefPtr<AccEvent> event = new AccStateChangeEvent(aContent, states::BUSY, isOn);
|
|
|
|
FireDelayedAccessibleEvent(event);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-10-31 17:52:27 -07:00
|
|
|
// ARIA or XUL selection
|
|
|
|
if ((aContent->IsXUL() && aAttribute == nsGkAtoms::selected) ||
|
2011-06-03 14:35:17 -07:00
|
|
|
aAttribute == nsGkAtoms::aria_selected) {
|
2011-10-31 17:52:27 -07:00
|
|
|
nsAccessible* item = GetAccessible(aContent);
|
|
|
|
nsAccessible* widget =
|
|
|
|
nsAccUtils::GetSelectableContainer(item, item->State());
|
|
|
|
if (widget) {
|
|
|
|
AccSelChangeEvent::SelChangeType selChangeType =
|
|
|
|
aContent->AttrValueIs(aNameSpaceID, aAttribute,
|
|
|
|
nsGkAtoms::_true, eCaseMatters) ?
|
|
|
|
AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
|
|
|
|
|
|
|
|
nsRefPtr<AccEvent> event =
|
|
|
|
new AccSelChangeEvent(widget, item, selChangeType);
|
|
|
|
FireDelayedAccessibleEvent(event);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2011-10-31 17:52:27 -07:00
|
|
|
return;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-08-14 09:25:24 -07:00
|
|
|
|
2011-06-03 14:35:17 -07:00
|
|
|
if (aAttribute == nsGkAtoms::contenteditable) {
|
2010-08-24 19:08:28 -07:00
|
|
|
nsRefPtr<AccEvent> editableChangeEvent =
|
2011-04-09 16:38:06 -07:00
|
|
|
new AccStateChangeEvent(aContent, states::EDITABLE);
|
2007-08-14 09:25:24 -07:00
|
|
|
FireDelayedAccessibleEvent(editableChangeEvent);
|
|
|
|
return;
|
|
|
|
}
|
2007-04-16 21:45:42 -07:00
|
|
|
}
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
// nsDocAccessible protected member
|
2007-04-16 21:45:42 -07:00
|
|
|
void
|
|
|
|
nsDocAccessible::ARIAAttributeChanged(nsIContent* aContent, nsIAtom* aAttribute)
|
|
|
|
{
|
2010-04-02 06:33:55 -07:00
|
|
|
// Note: For universal/global ARIA states and properties we don't care if
|
|
|
|
// there is an ARIA role present or not.
|
|
|
|
|
2011-06-03 14:35:17 -07:00
|
|
|
if (aAttribute == nsGkAtoms::aria_required) {
|
2010-08-24 19:08:28 -07:00
|
|
|
nsRefPtr<AccEvent> event =
|
2011-04-09 16:38:06 -07:00
|
|
|
new AccStateChangeEvent(aContent, states::REQUIRED);
|
2007-04-16 21:45:42 -07:00
|
|
|
FireDelayedAccessibleEvent(event);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-06-03 14:35:17 -07:00
|
|
|
if (aAttribute == nsGkAtoms::aria_invalid) {
|
2010-08-24 19:08:28 -07:00
|
|
|
nsRefPtr<AccEvent> event =
|
2011-04-09 16:38:06 -07:00
|
|
|
new AccStateChangeEvent(aContent, states::INVALID);
|
2007-04-16 21:45:42 -07:00
|
|
|
FireDelayedAccessibleEvent(event);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-09-27 18:46:11 -07:00
|
|
|
// The activedescendant universal property redirects accessible focus events
|
|
|
|
// to the element with the id that activedescendant points to. Make sure
|
|
|
|
// the tree up to date before processing.
|
2011-06-03 14:35:17 -07:00
|
|
|
if (aAttribute == nsGkAtoms::aria_activedescendant) {
|
2011-09-27 18:46:11 -07:00
|
|
|
mNotificationController->HandleNotification<nsDocAccessible, nsIContent>
|
|
|
|
(this, &nsDocAccessible::ARIAActiveDescendantChanged, aContent);
|
|
|
|
|
2007-04-16 21:45:42 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-04-02 06:33:55 -07: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.
|
2011-06-03 14:35:17 -07:00
|
|
|
if (aAttribute == nsGkAtoms::aria_grabbed ||
|
|
|
|
aAttribute == nsGkAtoms::aria_dropeffect ||
|
|
|
|
aAttribute == nsGkAtoms::aria_hidden ||
|
|
|
|
aAttribute == nsGkAtoms::aria_sort) {
|
2010-04-02 06:33:55 -07:00
|
|
|
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED,
|
2010-06-11 01:23:18 -07:00
|
|
|
aContent);
|
2010-04-02 06:33:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// We treat aria-expanded as a global ARIA state for historical reasons
|
2011-06-03 14:35:17 -07:00
|
|
|
if (aAttribute == nsGkAtoms::aria_expanded) {
|
2010-08-24 19:08:28 -07:00
|
|
|
nsRefPtr<AccEvent> event =
|
2011-04-09 16:38:06 -07:00
|
|
|
new AccStateChangeEvent(aContent, states::EXPANDED);
|
2010-04-02 06:33:55 -07:00
|
|
|
FireDelayedAccessibleEvent(event);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-06-03 14:35:17 -07:00
|
|
|
if (!aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) {
|
2007-04-16 21:45:42 -07:00
|
|
|
// We don't care about these other ARIA attribute changes unless there is
|
|
|
|
// an ARIA role set for the element
|
|
|
|
// XXX: we should check the role map to see if the changed property is
|
|
|
|
// relevant for that particular role.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-12-11 18:10:26 -08:00
|
|
|
// The following ARIA attributes only take affect when dynamic content role is present
|
2011-06-03 14:35:17 -07:00
|
|
|
if (aAttribute == nsGkAtoms::aria_checked ||
|
|
|
|
aAttribute == nsGkAtoms::aria_pressed) {
|
|
|
|
const PRUint32 kState = (aAttribute == nsGkAtoms::aria_checked) ?
|
2011-04-09 16:38:06 -07:00
|
|
|
states::CHECKED : states::PRESSED;
|
2011-08-11 04:45:36 -07:00
|
|
|
nsRefPtr<AccEvent> event = new AccStateChangeEvent(aContent, kState);
|
2007-08-14 09:15:12 -07:00
|
|
|
FireDelayedAccessibleEvent(event);
|
2011-08-11 04:45:36 -07:00
|
|
|
|
|
|
|
nsAccessible* accessible = event->GetAccessible();
|
|
|
|
if (accessible) {
|
2011-06-03 14:35:17 -07:00
|
|
|
bool wasMixed = (mARIAAttrOldValue == nsGkAtoms::mixed);
|
2011-08-11 04:45:36 -07:00
|
|
|
bool isMixed = aContent->AttrValueIs(kNameSpaceID_None, aAttribute,
|
2011-06-03 14:35:17 -07:00
|
|
|
nsGkAtoms::mixed, eCaseMatters);
|
2011-08-11 04:45:36 -07:00
|
|
|
if (isMixed != wasMixed) {
|
|
|
|
nsRefPtr<AccEvent> event =
|
|
|
|
new AccStateChangeEvent(aContent, states::MIXED, isMixed);
|
|
|
|
FireDelayedAccessibleEvent(event);
|
2007-09-18 14:36:41 -07:00
|
|
|
}
|
|
|
|
}
|
2007-08-14 09:15:12 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-06-03 14:35:17 -07:00
|
|
|
if (aAttribute == nsGkAtoms::aria_readonly) {
|
2010-08-24 19:08:28 -07:00
|
|
|
nsRefPtr<AccEvent> event =
|
2011-04-09 16:38:06 -07:00
|
|
|
new AccStateChangeEvent(aContent, states::READONLY);
|
2007-04-16 21:45:42 -07:00
|
|
|
FireDelayedAccessibleEvent(event);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-06-11 12:57:29 -07:00
|
|
|
// Fire value change event whenever aria-valuetext is changed, or
|
|
|
|
// when aria-valuenow is changed and aria-valuetext is empty
|
2011-06-03 14:35:17 -07:00
|
|
|
if (aAttribute == nsGkAtoms::aria_valuetext ||
|
|
|
|
(aAttribute == nsGkAtoms::aria_valuenow &&
|
|
|
|
(!aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuetext) ||
|
|
|
|
aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_valuetext,
|
|
|
|
nsGkAtoms::_empty, eCaseMatters)))) {
|
2009-09-02 19:01:18 -07:00
|
|
|
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
|
2010-06-11 01:23:18 -07:00
|
|
|
aContent);
|
2007-04-16 21:45:42 -07:00
|
|
|
return;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-27 18:46:11 -07:00
|
|
|
void
|
|
|
|
nsDocAccessible::ARIAActiveDescendantChanged(nsIContent* aElm)
|
|
|
|
{
|
|
|
|
if (FocusMgr()->HasDOMFocus(aElm)) {
|
|
|
|
nsAutoString id;
|
|
|
|
if (aElm->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant, id)) {
|
2011-10-18 03:53:36 -07:00
|
|
|
nsIDocument* DOMDoc = aElm->OwnerDoc();
|
2011-09-27 18:46:11 -07:00
|
|
|
dom::Element* activeDescendantElm = DOMDoc->GetElementById(id);
|
|
|
|
if (activeDescendantElm) {
|
|
|
|
nsAccessible* activeDescendant = GetAccessible(activeDescendantElm);
|
|
|
|
if (activeDescendant) {
|
|
|
|
FocusMgr()->ActiveItemChanged(activeDescendant, false);
|
|
|
|
A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("ARIA activedescedant changed",
|
|
|
|
activeDescendant)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
void nsDocAccessible::ContentAppended(nsIDocument *aDocument,
|
|
|
|
nsIContent* aContainer,
|
2010-05-10 18:12:34 -07:00
|
|
|
nsIContent* aFirstNewContent,
|
|
|
|
PRInt32 /* unused */)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2011-03-28 20:32:11 -07:00
|
|
|
void nsDocAccessible::ContentStateChanged(nsIDocument* aDocument,
|
|
|
|
nsIContent* aContent,
|
|
|
|
nsEventStates aStateMask)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2010-11-08 05:33:18 -08:00
|
|
|
if (aStateMask.HasState(NS_EVENT_STATE_CHECKED)) {
|
2011-10-31 17:52:27 -07:00
|
|
|
nsAccessible* item = GetAccessible(aContent);
|
|
|
|
if (item) {
|
|
|
|
nsAccessible* widget = item->ContainerWidget();
|
|
|
|
if (widget && widget->IsSelect()) {
|
|
|
|
AccSelChangeEvent::SelChangeType selChangeType =
|
|
|
|
aContent->AsElement()->State().HasState(NS_EVENT_STATE_CHECKED) ?
|
|
|
|
AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
|
|
|
|
nsRefPtr<AccEvent> event = new AccSelChangeEvent(widget, item,
|
|
|
|
selChangeType);
|
|
|
|
FireDelayedAccessibleEvent(event);
|
|
|
|
}
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2010-11-08 05:33:18 -08:00
|
|
|
if (aStateMask.HasState(NS_EVENT_STATE_INVALID)) {
|
|
|
|
nsRefPtr<AccEvent> event =
|
2011-10-17 07:59:28 -07:00
|
|
|
new AccStateChangeEvent(aContent, states::INVALID, true);
|
2010-11-08 05:33:18 -08:00
|
|
|
FireDelayedAccessibleEvent(event);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2010-03-17 10:10:57 -07:00
|
|
|
void nsDocAccessible::DocumentStatesChanged(nsIDocument* aDocument,
|
2010-10-20 04:26:32 -07:00
|
|
|
nsEventStates aStateMask)
|
2010-03-17 10:10:57 -07:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2007-09-05 01:22:17 -07:00
|
|
|
void nsDocAccessible::CharacterDataWillChange(nsIDocument *aDocument,
|
|
|
|
nsIContent* aContent,
|
|
|
|
CharacterDataChangeInfo* aInfo)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
void nsDocAccessible::CharacterDataChanged(nsIDocument *aDocument,
|
|
|
|
nsIContent* aContent,
|
|
|
|
CharacterDataChangeInfo* aInfo)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsDocAccessible::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer,
|
2010-05-10 18:12:34 -07:00
|
|
|
nsIContent* aChild, PRInt32 /* unused */)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsDocAccessible::ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer,
|
2010-07-21 15:05:17 -07:00
|
|
|
nsIContent* aChild, PRInt32 /* unused */,
|
|
|
|
nsIContent* aPreviousSibling)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsDocAccessible::ParentChainChanged(nsIContent *aContent)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsAccessible
|
|
|
|
|
2010-06-08 09:39:58 -07:00
|
|
|
#ifdef DEBUG_ACCDOCMGR
|
|
|
|
nsresult
|
2010-08-24 19:08:28 -07:00
|
|
|
nsDocAccessible::HandleAccEvent(AccEvent* aAccEvent)
|
2010-06-08 09:39:58 -07:00
|
|
|
{
|
|
|
|
NS_LOG_ACCDOCLOAD_HANDLEEVENT(aAccEvent)
|
2009-12-10 11:12:19 -08:00
|
|
|
|
2010-06-08 09:39:58 -07:00
|
|
|
return nsHyperTextAccessible::HandleAccEvent(aAccEvent);
|
2009-12-10 11:12:19 -08:00
|
|
|
|
|
|
|
}
|
2010-06-08 09:39:58 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Public members
|
2009-12-10 11:12:19 -08:00
|
|
|
|
2010-09-16 20:23:17 -07:00
|
|
|
void*
|
|
|
|
nsDocAccessible::GetNativeWindow() const
|
|
|
|
{
|
2012-02-07 14:38:54 -08:00
|
|
|
if (!mPresShell)
|
2012-01-10 10:52:14 -08:00
|
|
|
return nsnull;
|
|
|
|
|
2012-02-07 14:38:54 -08:00
|
|
|
nsIViewManager* vm = mPresShell->GetViewManager();
|
2012-01-10 10:52:14 -08:00
|
|
|
if (!vm)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIWidget> widget;
|
|
|
|
vm->GetRootWidget(getter_AddRefs(widget));
|
|
|
|
if (widget)
|
|
|
|
return widget->GetNativeData(NS_NATIVE_WINDOW);
|
|
|
|
|
2010-09-16 20:23:17 -07:00
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
2010-09-09 07:44:56 -07:00
|
|
|
nsAccessible*
|
2011-01-27 20:37:08 -08:00
|
|
|
nsDocAccessible::GetAccessibleByUniqueIDInSubtree(void* aUniqueID)
|
2010-09-09 07:44:56 -07:00
|
|
|
{
|
2011-01-27 20:37:08 -08:00
|
|
|
nsAccessible* child = GetAccessibleByUniqueID(aUniqueID);
|
2010-09-09 07:44:56 -07:00
|
|
|
if (child)
|
|
|
|
return child;
|
|
|
|
|
|
|
|
PRUint32 childDocCount = mChildDocuments.Length();
|
|
|
|
for (PRUint32 childDocIdx= 0; childDocIdx < childDocCount; childDocIdx++) {
|
|
|
|
nsDocAccessible* childDocument = mChildDocuments.ElementAt(childDocIdx);
|
2011-01-27 20:37:08 -08:00
|
|
|
child = childDocument->GetAccessibleByUniqueIDInSubtree(aUniqueID);
|
2010-09-09 07:44:56 -07:00
|
|
|
if (child)
|
|
|
|
return child;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nsnull;
|
|
|
|
}
|
2009-12-10 11:12:19 -08:00
|
|
|
|
2011-01-27 20:37:38 -08:00
|
|
|
nsAccessible*
|
|
|
|
nsDocAccessible::GetAccessibleOrContainer(nsINode* aNode)
|
|
|
|
{
|
|
|
|
if (!aNode || !aNode->IsInDoc())
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
nsINode* currNode = aNode;
|
|
|
|
nsAccessible* accessible = nsnull;
|
|
|
|
while (!(accessible = GetAccessible(currNode)) &&
|
|
|
|
(currNode = currNode->GetNodeParent()));
|
|
|
|
|
|
|
|
return accessible;
|
|
|
|
}
|
|
|
|
|
2010-11-12 11:00:55 -08: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 11:01:04 -08:00
|
|
|
|
2010-11-12 11:00:55 -08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize the accessible.
|
|
|
|
if (!aAccessible->Init()) {
|
|
|
|
NS_ERROR("Failed to initialize an accessible!");
|
|
|
|
|
|
|
|
UnbindFromDocument(aAccessible);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
aAccessible->SetRoleMapEntry(aRoleMapEntry);
|
2011-01-27 20:38:26 -08:00
|
|
|
if (aAccessible->IsElement())
|
|
|
|
AddDependentIDsFor(aAccessible);
|
|
|
|
|
2010-11-12 11:00:55 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsDocAccessible::UnbindFromDocument(nsAccessible* aAccessible)
|
|
|
|
{
|
2010-11-19 18:37:40 -08:00
|
|
|
NS_ASSERTION(mAccessibleCache.GetWeak(aAccessible->UniqueID()),
|
|
|
|
"Unbinding the unbound accessible!");
|
|
|
|
|
2011-09-27 18:46:11 -07:00
|
|
|
// Fire focus event on accessible having DOM focus if active item was removed
|
|
|
|
// from the tree.
|
|
|
|
if (FocusMgr()->IsActiveItem(aAccessible)) {
|
|
|
|
FocusMgr()->ActiveItemChanged(nsnull);
|
|
|
|
A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("tree shutdown", aAccessible)
|
|
|
|
}
|
|
|
|
|
2010-11-12 11:01:04 -08:00
|
|
|
// Remove an accessible from node-to-accessible map if it exists there.
|
2010-11-12 11:00:55 -08:00
|
|
|
if (aAccessible->IsPrimaryForNode() &&
|
|
|
|
mNodeToAccessibleMap.Get(aAccessible->GetNode()) == aAccessible)
|
|
|
|
mNodeToAccessibleMap.Remove(aAccessible->GetNode());
|
|
|
|
|
|
|
|
void* uniqueID = aAccessible->UniqueID();
|
2010-11-19 18:37:40 -08:00
|
|
|
|
|
|
|
NS_ASSERTION(!aAccessible->IsDefunct(), "Shutdown the shutdown accessible!");
|
2010-11-12 11:00:55 -08:00
|
|
|
aAccessible->Shutdown();
|
2010-11-19 18:37:40 -08:00
|
|
|
|
2010-11-12 11:00:55 -08:00
|
|
|
mAccessibleCache.Remove(uniqueID);
|
|
|
|
}
|
|
|
|
|
2010-10-20 21:16:10 -07:00
|
|
|
void
|
2011-01-18 00:03:38 -08:00
|
|
|
nsDocAccessible::ContentInserted(nsIContent* aContainerNode,
|
|
|
|
nsIContent* aStartChildNode,
|
|
|
|
nsIContent* aEndChildNode)
|
|
|
|
{
|
2011-08-08 00:55:36 -07:00
|
|
|
// Ignore content insertions until we constructed accessible tree. Otherwise
|
|
|
|
// schedule tree update on content insertion after layout.
|
|
|
|
if (mNotificationController && HasLoadState(eTreeConstructed)) {
|
2011-01-18 00:03:38 -08:00
|
|
|
// Update the whole tree of this document accessible when the container is
|
|
|
|
// null (document element is inserted or removed).
|
|
|
|
nsAccessible* container = aContainerNode ?
|
2011-01-27 20:37:38 -08:00
|
|
|
GetAccessibleOrContainer(aContainerNode) : this;
|
2010-10-20 21:16:10 -07:00
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
mNotificationController->ScheduleContentInsertion(container,
|
|
|
|
aStartChildNode,
|
|
|
|
aEndChildNode);
|
2010-10-20 21:16:10 -07:00
|
|
|
}
|
2011-01-18 00:03:38 -08:00
|
|
|
}
|
2010-10-20 21:16:10 -07:00
|
|
|
|
2011-01-18 00:03:38 -08: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 ?
|
2011-01-27 20:37:38 -08:00
|
|
|
GetAccessibleOrContainer(aContainerNode) : this;
|
2010-10-20 21:16:10 -07:00
|
|
|
|
2011-04-06 22:17:29 -07:00
|
|
|
UpdateTree(container, aChildNode, false);
|
2010-10-20 21:16:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2011-01-31 19:00:06 -08:00
|
|
|
nsDocAccessible::RecreateAccessible(nsIContent* aContent)
|
2010-10-20 21:16:10 -07:00
|
|
|
{
|
2011-01-31 19:00:06 -08:00
|
|
|
// XXX: we shouldn't recreate whole accessible subtree, instead we should
|
|
|
|
// subclass hide and show events to handle them separately and implement their
|
|
|
|
// coalescence with normal hide and show events. Note, in this case they
|
|
|
|
// should be coalesced with normal show/hide events.
|
2010-10-20 21:16:10 -07:00
|
|
|
|
2011-09-01 00:12:51 -07:00
|
|
|
// Check if the node is in accessible document.
|
|
|
|
nsAccessible* container = GetContainerAccessible(aContent);
|
|
|
|
if (container) {
|
2011-01-31 19:00:06 -08:00
|
|
|
// Remove and reinsert.
|
2011-04-06 22:17:29 -07:00
|
|
|
UpdateTree(container, aContent, false);
|
2011-01-31 19:00:06 -08:00
|
|
|
container->UpdateChildren();
|
2011-04-06 22:17:29 -07:00
|
|
|
UpdateTree(container, aContent, true);
|
2010-10-20 21:16:10 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-18 21:44:47 -08:00
|
|
|
void
|
2011-07-24 20:15:37 -07:00
|
|
|
nsDocAccessible::ProcessInvalidationList()
|
2010-11-18 21:44:47 -08:00
|
|
|
{
|
2011-07-24 20:15:37 -07:00
|
|
|
// Invalidate children of container accessible for each element in
|
|
|
|
// invalidation list. Allow invalidation list insertions while container
|
|
|
|
// children are recached.
|
|
|
|
for (PRUint32 idx = 0; idx < mInvalidationList.Length(); idx++) {
|
|
|
|
nsIContent* content = mInvalidationList[idx];
|
|
|
|
nsAccessible* accessible = GetAccessible(content);
|
|
|
|
if (!accessible) {
|
|
|
|
nsAccessible* container = GetContainerAccessible(content);
|
|
|
|
if (container) {
|
|
|
|
container->UpdateChildren();
|
|
|
|
accessible = GetAccessible(content);
|
2011-02-11 07:05:30 -08:00
|
|
|
}
|
2010-11-18 21:44:47 -08:00
|
|
|
}
|
|
|
|
|
2011-07-24 20:15:37 -07:00
|
|
|
// Make sure the subtree is created.
|
|
|
|
if (accessible)
|
|
|
|
CacheChildrenInSubtree(accessible);
|
2010-11-18 21:44:47 -08:00
|
|
|
}
|
2011-07-24 20:15:37 -07:00
|
|
|
|
|
|
|
mInvalidationList.Clear();
|
2010-11-18 21:44:47 -08:00
|
|
|
}
|
|
|
|
|
2010-11-20 17:00:29 -08: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.
|
2012-02-09 08:49:17 -08:00
|
|
|
nsAccTreeWalker walker(this, mDocument->GetRootElement(),
|
2012-02-07 23:31:25 -08:00
|
|
|
CanHaveAnonChildren());
|
2010-11-20 17:00:29 -08:00
|
|
|
|
2011-03-31 02:30:58 -07:00
|
|
|
nsAccessible* child = nsnull;
|
|
|
|
while ((child = walker.NextChild()) && AppendChild(child));
|
2010-11-20 17:00:29 -08:00
|
|
|
}
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Protected members
|
|
|
|
|
2011-03-02 06:41:42 -08:00
|
|
|
void
|
2011-08-08 00:55:36 -07:00
|
|
|
nsDocAccessible::NotifyOfLoading(bool aIsReloading)
|
|
|
|
{
|
|
|
|
// Mark the document accessible as loading, if it stays alive then we'll mark
|
|
|
|
// it as loaded when we receive proper notification.
|
|
|
|
mLoadState &= ~eDOMLoaded;
|
|
|
|
|
|
|
|
if (!IsLoadEventTarget())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (aIsReloading) {
|
|
|
|
// Fire reload and state busy events on existing document accessible while
|
|
|
|
// event from user input flag can be calculated properly and accessible
|
|
|
|
// is alive. When new document gets loaded then this one is destroyed.
|
|
|
|
nsRefPtr<AccEvent> reloadEvent =
|
|
|
|
new AccEvent(nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD, this);
|
|
|
|
nsEventShell::FireEvent(reloadEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fire state busy change event. Use delayed event since we don't care
|
|
|
|
// actually if event isn't delivered when the document goes away like a shot.
|
|
|
|
nsRefPtr<AccEvent> stateEvent =
|
2011-10-17 07:59:28 -07:00
|
|
|
new AccStateChangeEvent(mDocument, states::BUSY, true);
|
2011-08-08 00:55:36 -07:00
|
|
|
FireDelayedAccessibleEvent(stateEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsDocAccessible::DoInitialUpdate()
|
2011-03-02 06:41:42 -08:00
|
|
|
{
|
2011-08-08 00:55:36 -07:00
|
|
|
mLoadState |= eTreeConstructed;
|
|
|
|
|
2011-03-02 22:41:46 -08:00
|
|
|
// The content element may be changed before the initial update and then we
|
|
|
|
// miss the notification (since content tree change notifications are ignored
|
|
|
|
// prior to initial update). Make sure the content element is valid.
|
|
|
|
nsIContent* contentElm = nsCoreUtils::GetRoleContent(mDocument);
|
|
|
|
if (contentElm && mContent != contentElm)
|
|
|
|
mContent = contentElm;
|
|
|
|
|
|
|
|
// Build initial tree.
|
|
|
|
CacheChildrenInSubtree(this);
|
2011-07-19 01:30:32 -07:00
|
|
|
|
|
|
|
// Fire reorder event after the document tree is constructed. Note, since
|
|
|
|
// this reorder event is processed by parent document then events targeted to
|
|
|
|
// this document may be fired prior to this reorder event. If this is
|
|
|
|
// a problem then consider to keep event processing per tab document.
|
|
|
|
if (!IsRoot()) {
|
|
|
|
nsRefPtr<AccEvent> reorderEvent =
|
2011-07-23 01:38:33 -07:00
|
|
|
new AccEvent(nsIAccessibleEvent::EVENT_REORDER, Parent(), eAutoDetect,
|
|
|
|
AccEvent::eCoalesceFromSameSubtree);
|
2011-07-19 01:30:32 -07:00
|
|
|
ParentDocument()->FireDelayedAccessibleEvent(reorderEvent);
|
|
|
|
}
|
2011-03-02 06:41:42 -08:00
|
|
|
}
|
|
|
|
|
2011-08-08 00:55:36 -07:00
|
|
|
void
|
|
|
|
nsDocAccessible::ProcessLoad()
|
|
|
|
{
|
|
|
|
mLoadState |= eCompletelyLoaded;
|
|
|
|
|
|
|
|
// Do not fire document complete/stop events for root chrome document
|
|
|
|
// accessibles and for frame/iframe documents because
|
|
|
|
// a) screen readers start working on focus event in the case of root chrome
|
|
|
|
// documents
|
|
|
|
// b) document load event on sub documents causes screen readers to act is if
|
|
|
|
// entire page is reloaded.
|
|
|
|
if (!IsLoadEventTarget())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Fire complete/load stopped if the load event type is given.
|
|
|
|
if (mLoadEventType) {
|
|
|
|
nsRefPtr<AccEvent> loadEvent = new AccEvent(mLoadEventType, this);
|
|
|
|
nsEventShell::FireEvent(loadEvent);
|
|
|
|
|
|
|
|
mLoadEventType = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fire busy state change event.
|
|
|
|
nsRefPtr<AccEvent> stateEvent =
|
2011-10-17 07:59:28 -07:00
|
|
|
new AccStateChangeEvent(this, states::BUSY, false);
|
2011-08-08 00:55:36 -07:00
|
|
|
nsEventShell::FireEvent(stateEvent);
|
|
|
|
}
|
|
|
|
|
2010-11-17 18:55:44 -08:00
|
|
|
void
|
|
|
|
nsDocAccessible::AddDependentIDsFor(nsAccessible* aRelProvider,
|
|
|
|
nsIAtom* aRelAttr)
|
|
|
|
{
|
|
|
|
for (PRUint32 idx = 0; idx < kRelationAttrsLen; idx++) {
|
|
|
|
nsIAtom* relAttr = *kRelationAttrs[idx];
|
|
|
|
if (aRelAttr && aRelAttr != relAttr)
|
|
|
|
continue;
|
|
|
|
|
2011-06-03 14:35:17 -07:00
|
|
|
if (relAttr == nsGkAtoms::_for) {
|
2010-11-19 18:37:18 -08:00
|
|
|
if (!aRelProvider->GetContent()->IsHTML() ||
|
2011-12-17 22:00:47 -08:00
|
|
|
(aRelProvider->GetContent()->Tag() != nsGkAtoms::label &&
|
|
|
|
aRelProvider->GetContent()->Tag() != nsGkAtoms::output))
|
2010-11-19 18:37:18 -08:00
|
|
|
continue;
|
|
|
|
|
2011-06-03 14:35:17 -07:00
|
|
|
} else if (relAttr == nsGkAtoms::control) {
|
2010-11-19 18:37:18 -08:00
|
|
|
if (!aRelProvider->GetContent()->IsXUL() ||
|
2011-12-17 22:00:47 -08:00
|
|
|
(aRelProvider->GetContent()->Tag() != nsGkAtoms::label &&
|
|
|
|
aRelProvider->GetContent()->Tag() != nsGkAtoms::description))
|
2010-11-19 18:37:18 -08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2010-11-17 18:55:44 -08: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-18 21:44:47 -08:00
|
|
|
if (provider) {
|
2010-11-17 18:55:44 -08:00
|
|
|
providers->AppendElement(provider);
|
2010-11-18 21:44:47 -08: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);
|
2011-01-27 20:37:08 -08:00
|
|
|
if (dependentContent && !HasAccessible(dependentContent)) {
|
2010-11-18 21:44:47 -08:00
|
|
|
mInvalidationList.AppendElement(dependentContent);
|
|
|
|
}
|
|
|
|
}
|
2010-11-17 18:55:44 -08: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 10:33:00 -08:00
|
|
|
bool
|
|
|
|
nsDocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
|
|
|
|
nsIAtom* aAttribute)
|
|
|
|
{
|
2011-06-03 14:35:17 -07:00
|
|
|
if (aAttribute == nsGkAtoms::role) {
|
2010-12-18 10:33:00 -08:00
|
|
|
// 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-31 19:00:06 -08:00
|
|
|
HandleNotification<nsDocAccessible, nsIContent>
|
2011-01-19 00:03:12 -08:00
|
|
|
(this, &nsDocAccessible::RecreateAccessible, aElement);
|
|
|
|
|
2010-12-18 10:33:00 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-06-03 14:35:17 -07:00
|
|
|
if (aAttribute == nsGkAtoms::href ||
|
|
|
|
aAttribute == nsGkAtoms::onclick) {
|
2011-01-31 19:00:06 -08:00
|
|
|
// Not worth the expense to ensure which namespace these are in. It doesn't
|
|
|
|
// kill use to recreate the accessible even if the attribute was used in
|
|
|
|
// the wrong namespace or an element that doesn't support it.
|
|
|
|
|
|
|
|
// Recreate accessible asynchronously to allow the content to handle
|
|
|
|
// the attribute change.
|
|
|
|
mNotificationController->ScheduleNotification<nsDocAccessible, nsIContent>
|
2011-01-19 00:03:12 -08:00
|
|
|
(this, &nsDocAccessible::RecreateAccessible, aElement);
|
|
|
|
|
2010-12-18 10:33:00 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-06-03 14:35:17 -07:00
|
|
|
if (aAttribute == nsGkAtoms::aria_multiselectable &&
|
|
|
|
aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) {
|
2010-12-18 10:33:00 -08:00
|
|
|
// 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-31 19:00:06 -08:00
|
|
|
HandleNotification<nsDocAccessible, nsIContent>
|
2011-01-19 00:03:12 -08:00
|
|
|
(this, &nsDocAccessible::RecreateAccessible, aElement);
|
|
|
|
|
2010-12-18 10:33:00 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
// nsDocAccessible public member
|
2009-09-02 19:01:18 -07:00
|
|
|
nsresult
|
2010-06-11 01:23:18 -07:00
|
|
|
nsDocAccessible::FireDelayedAccessibleEvent(PRUint32 aEventType, nsINode *aNode,
|
2010-08-24 19:08:28 -07:00
|
|
|
AccEvent::EEventRule aAllowDupes,
|
2010-01-20 03:16:32 -08:00
|
|
|
EIsFromUserInput aIsFromUserInput)
|
2007-04-16 21:45:42 -07:00
|
|
|
{
|
2010-08-24 19:08:28 -07:00
|
|
|
nsRefPtr<AccEvent> event =
|
2010-10-20 21:16:10 -07:00
|
|
|
new AccEvent(aEventType, aNode, aIsFromUserInput, aAllowDupes);
|
2007-04-16 21:45:42 -07:00
|
|
|
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
|
2008-03-17 01:13:10 -07:00
|
|
|
return FireDelayedAccessibleEvent(event);
|
2007-04-16 21:45:42 -07:00
|
|
|
}
|
|
|
|
|
2009-12-10 11:12:19 -08:00
|
|
|
// nsDocAccessible public member
|
2007-04-16 21:45:42 -07:00
|
|
|
nsresult
|
2010-08-24 19:08:28 -07:00
|
|
|
nsDocAccessible::FireDelayedAccessibleEvent(AccEvent* aEvent)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2009-09-07 09:46:56 -07:00
|
|
|
NS_ENSURE_ARG(aEvent);
|
2010-06-08 09:39:58 -07:00
|
|
|
NS_LOG_ACCDOCLOAD_FIREEVENT(aEvent)
|
2009-09-07 09:46:56 -07:00
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
if (mNotificationController)
|
|
|
|
mNotificationController->QueueEvent(aEvent);
|
2009-09-07 09:46:56 -07:00
|
|
|
|
2010-01-27 03:42:08 -08:00
|
|
|
return NS_OK;
|
2009-09-07 09:46:56 -07:00
|
|
|
}
|
2007-08-20 20:16:27 -07:00
|
|
|
|
2010-01-27 03:42:08 -08:00
|
|
|
void
|
2010-08-24 19:08:28 -07:00
|
|
|
nsDocAccessible::ProcessPendingEvent(AccEvent* aEvent)
|
2010-10-20 21:16:10 -07:00
|
|
|
{
|
2010-01-27 03:42:08 -08:00
|
|
|
PRUint32 eventType = aEvent->GetEventType();
|
2010-10-20 21:16:10 -07:00
|
|
|
if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED) {
|
2011-10-11 08:33:06 -07:00
|
|
|
nsHyperTextAccessible* hyperText = aEvent->GetAccessible()->AsHyperText();
|
2010-10-20 21:16:10 -07:00
|
|
|
PRInt32 caretOffset;
|
2011-10-11 08:33:06 -07:00
|
|
|
if (hyperText &&
|
|
|
|
NS_SUCCEEDED(hyperText->GetCaretOffset(&caretOffset))) {
|
2007-08-17 11:21:49 -07:00
|
|
|
#ifdef DEBUG_A11Y
|
2010-10-20 21:16:10 -07:00
|
|
|
PRUnichar chAtOffset;
|
2011-10-11 08:33:06 -07:00
|
|
|
hyperText->GetCharacterAtOffset(caretOffset, &chAtOffset);
|
2010-10-20 21:16:10 -07:00
|
|
|
printf("\nCaret moved to %d with char %c", caretOffset, chAtOffset);
|
2007-07-13 10:19:40 -07:00
|
|
|
#endif
|
2010-10-20 21:16:10 -07:00
|
|
|
nsRefPtr<AccEvent> caretMoveEvent =
|
2011-10-11 08:33:06 -07:00
|
|
|
new AccCaretMoveEvent(hyperText, caretOffset);
|
2010-10-20 21:16:10 -07:00
|
|
|
nsEventShell::FireEvent(caretMoveEvent);
|
2008-02-02 09:02:09 -08:00
|
|
|
|
2010-10-20 21:16:10 -07:00
|
|
|
PRInt32 selectionCount;
|
2011-10-11 08:33:06 -07:00
|
|
|
hyperText->GetSelectionCount(&selectionCount);
|
2010-10-20 21:16:10 -07:00
|
|
|
if (selectionCount) { // There's a selection so fire selection change as well
|
|
|
|
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED,
|
2011-10-11 08:33:06 -07:00
|
|
|
hyperText);
|
2008-01-24 06:07:21 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-10-20 21:16:10 -07:00
|
|
|
else {
|
|
|
|
nsEventShell::FireEvent(aEvent);
|
2008-01-22 06:43:18 -08:00
|
|
|
|
2010-10-20 21:16:10 -07:00
|
|
|
// Post event processing
|
|
|
|
if (eventType == nsIAccessibleEvent::EVENT_HIDE)
|
2011-10-11 08:33:06 -07:00
|
|
|
ShutdownChildrenInSubtree(aEvent->GetAccessible());
|
2008-01-22 06:43:18 -08:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2011-01-18 00:03:38 -08:00
|
|
|
void
|
|
|
|
nsDocAccessible::ProcessContentInserted(nsAccessible* aContainer,
|
|
|
|
const nsTArray<nsCOMPtr<nsIContent> >* aInsertedContent)
|
|
|
|
{
|
|
|
|
// Process the notification if the container accessible is still in tree.
|
2011-01-27 20:37:08 -08:00
|
|
|
if (!HasAccessible(aContainer->GetNode()))
|
2011-01-18 00:03:38 -08:00
|
|
|
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.
|
2011-01-26 19:38:50 -08:00
|
|
|
aContainer->UpdateChildren();
|
2011-01-18 00:03:38 -08:00
|
|
|
|
|
|
|
// 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
|
2011-01-26 19:38:50 -08:00
|
|
|
// harm an AT.
|
2011-01-19 08:01:31 -08: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 =
|
2011-01-27 20:37:38 -08:00
|
|
|
GetContainerAccessible(aInsertedContent->ElementAt(idx));
|
2011-01-19 08:01:31 -08:00
|
|
|
if (directContainer)
|
2011-04-06 22:17:29 -07:00
|
|
|
UpdateTree(directContainer, aInsertedContent->ElementAt(idx), true);
|
2011-01-19 08:01:31 -08:00
|
|
|
}
|
2011-01-18 00:03:38 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsDocAccessible::UpdateTree(nsAccessible* aContainer, nsIContent* aChildNode,
|
2011-04-06 22:17:29 -07:00
|
|
|
bool aIsInsert)
|
2011-01-18 00:03:38 -08:00
|
|
|
{
|
2011-04-06 22:17:29 -07:00
|
|
|
PRUint32 updateFlags = eNoAccessible;
|
|
|
|
|
|
|
|
// If child node is not accessible then look for its accessible children.
|
|
|
|
nsAccessible* child = GetAccessible(aChildNode);
|
|
|
|
if (child) {
|
|
|
|
updateFlags |= UpdateTreeInternal(child, aIsInsert);
|
|
|
|
|
|
|
|
} else {
|
2012-02-09 08:49:17 -08:00
|
|
|
nsAccTreeWalker walker(this, aChildNode,
|
2012-02-07 23:31:25 -08:00
|
|
|
aContainer->CanHaveAnonChildren(), true);
|
2011-04-06 22:17:29 -07:00
|
|
|
|
|
|
|
while ((child = walker.NextChild()))
|
|
|
|
updateFlags |= UpdateTreeInternal(child, aIsInsert);
|
|
|
|
}
|
2011-01-18 00:03:38 -08:00
|
|
|
|
|
|
|
// 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) {
|
2012-01-11 19:07:35 -08:00
|
|
|
if (ancestor->ARIARole() == roles::ALERT) {
|
2011-01-18 00:03:38 -08:00
|
|
|
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_ALERT,
|
|
|
|
ancestor->GetNode());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't climb above this document.
|
|
|
|
if (ancestor == this)
|
|
|
|
break;
|
|
|
|
|
2011-07-23 01:38:33 -07:00
|
|
|
ancestor = ancestor->Parent();
|
2011-01-18 00:03:38 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-01 08:12:02 -08:00
|
|
|
MaybeNotifyOfValueChange(aContainer);
|
2011-01-18 00:03:38 -08:00
|
|
|
|
|
|
|
// 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-20 21:16:10 -07:00
|
|
|
PRUint32
|
2011-04-06 22:17:29 -07:00
|
|
|
nsDocAccessible::UpdateTreeInternal(nsAccessible* aChild, bool aIsInsert)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2011-04-06 22:17:29 -07:00
|
|
|
PRUint32 updateFlags = eAccessible;
|
2010-10-20 21:16:10 -07:00
|
|
|
|
2011-04-06 22:17:29 -07:00
|
|
|
nsINode* node = aChild->GetNode();
|
|
|
|
if (aIsInsert) {
|
|
|
|
// Create accessible tree for shown accessible.
|
|
|
|
CacheChildrenInSubtree(aChild);
|
2010-10-20 21:16:10 -07:00
|
|
|
|
2011-04-06 22:17:29 -07:00
|
|
|
} else {
|
|
|
|
// 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.
|
2012-01-11 19:07:35 -08:00
|
|
|
if (aChild->ARIARole() == roles::MENUPOPUP) {
|
2011-04-06 22:17:29 -07:00
|
|
|
nsRefPtr<AccEvent> event =
|
|
|
|
new AccEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END, aChild);
|
|
|
|
|
|
|
|
if (event)
|
|
|
|
FireDelayedAccessibleEvent(event);
|
2007-10-01 11:27:13 -07:00
|
|
|
}
|
2011-04-06 22:17:29 -07:00
|
|
|
}
|
2009-06-24 19:08:53 -07:00
|
|
|
|
2011-04-06 22:17:29 -07:00
|
|
|
// Fire show/hide event.
|
|
|
|
nsRefPtr<AccEvent> event;
|
|
|
|
if (aIsInsert)
|
|
|
|
event = new AccShowEvent(aChild, node);
|
|
|
|
else
|
|
|
|
event = new AccHideEvent(aChild, node);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2011-04-06 22:17:29 -07:00
|
|
|
if (event)
|
|
|
|
FireDelayedAccessibleEvent(event);
|
2011-01-20 00:02:00 -08:00
|
|
|
|
2011-04-06 22:17:29 -07:00
|
|
|
if (aIsInsert) {
|
2012-01-11 19:07:35 -08:00
|
|
|
roles::Role ariaRole = aChild->ARIARole();
|
|
|
|
if (ariaRole == roles::MENUPOPUP) {
|
2011-04-06 22:17:29 -07:00
|
|
|
// Fire EVENT_MENUPOPUP_START if ARIA menu appears.
|
|
|
|
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START,
|
|
|
|
node, AccEvent::eRemoveDupes);
|
|
|
|
|
2012-01-11 19:07:35 -08:00
|
|
|
} else if (ariaRole == roles::ALERT) {
|
2011-04-06 22:17:29 -07:00
|
|
|
// Fire EVENT_ALERT if ARIA alert appears.
|
|
|
|
updateFlags = eAlertAccessible;
|
|
|
|
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_ALERT, node,
|
|
|
|
AccEvent::eRemoveDupes);
|
2010-11-30 07:43:17 -08:00
|
|
|
}
|
|
|
|
|
2011-04-06 22:17:29 -07: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.
|
2011-09-27 18:46:11 -07:00
|
|
|
// XXX: do we really want to send focus to focused DOM node not taking into
|
|
|
|
// account active item?
|
|
|
|
if (FocusMgr()->IsFocused(aChild))
|
|
|
|
FocusMgr()->DispatchFocusEvent(this, aChild);
|
|
|
|
|
2011-04-06 22:17:29 -07:00
|
|
|
} else {
|
|
|
|
// Update the tree for content removal.
|
|
|
|
// The accessible parent may differ from container accessible if
|
|
|
|
// the parent doesn't have own DOM node like list accessible for HTML
|
|
|
|
// selects.
|
2011-07-23 01:38:33 -07:00
|
|
|
nsAccessible* parent = aChild->Parent();
|
2011-04-06 22:17:29 -07:00
|
|
|
NS_ASSERTION(parent, "No accessible parent?!");
|
|
|
|
if (parent)
|
|
|
|
parent->RemoveChild(aChild);
|
|
|
|
|
|
|
|
UncacheChildrenInSubtree(aChild);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2010-10-20 21:16:10 -07:00
|
|
|
return updateFlags;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2011-01-20 00:02:00 -08:00
|
|
|
void
|
|
|
|
nsDocAccessible::CacheChildrenInSubtree(nsAccessible* aRoot)
|
|
|
|
{
|
|
|
|
aRoot->EnsureChildren();
|
|
|
|
|
2011-06-06 19:24:01 -07:00
|
|
|
// Make sure we create accessible tree defined in DOM only, i.e. if accessible
|
|
|
|
// provides specific tree (like XUL trees) then tree creation is handled by
|
|
|
|
// this accessible.
|
|
|
|
PRUint32 count = aRoot->ContentChildCount();
|
|
|
|
for (PRUint32 idx = 0; idx < count; idx++) {
|
|
|
|
nsAccessible* child = aRoot->ContentChildAt(idx);
|
2011-03-28 06:59:17 -07:00
|
|
|
NS_ASSERTION(child, "Illicit tree change while tree is created!");
|
2011-01-20 00:02:00 -08:00
|
|
|
// Don't cross document boundaries.
|
2011-03-28 06:59:17 -07:00
|
|
|
if (child && child->IsContent())
|
2011-01-20 00:02:00 -08:00
|
|
|
CacheChildrenInSubtree(child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-20 21:16:10 -07:00
|
|
|
void
|
|
|
|
nsDocAccessible::UncacheChildrenInSubtree(nsAccessible* aRoot)
|
2007-10-06 09:24:57 -07:00
|
|
|
{
|
2011-02-11 07:05:38 -08:00
|
|
|
if (aRoot->IsElement())
|
|
|
|
RemoveDependentIDsFor(aRoot);
|
|
|
|
|
2011-06-06 19:24:01 -07:00
|
|
|
PRUint32 count = aRoot->ContentChildCount();
|
2010-10-20 21:16:10 -07:00
|
|
|
for (PRUint32 idx = 0; idx < count; idx++)
|
2011-06-06 19:24:01 -07:00
|
|
|
UncacheChildrenInSubtree(aRoot->ContentChildAt(idx));
|
2007-10-06 09:24:57 -07:00
|
|
|
|
2010-10-20 21:16:10 -07:00
|
|
|
if (aRoot->IsPrimaryForNode() &&
|
|
|
|
mNodeToAccessibleMap.Get(aRoot->GetNode()) == aRoot)
|
|
|
|
mNodeToAccessibleMap.Remove(aRoot->GetNode());
|
|
|
|
}
|
2007-10-06 09:24:57 -07:00
|
|
|
|
2010-10-20 21:16:10 -07: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.
|
2011-06-06 19:24:01 -07:00
|
|
|
PRUint32 count = aAccessible->ContentChildCount();
|
2010-10-20 21:16:10 -07:00
|
|
|
for (PRUint32 idx = 0, jdx = 0; idx < count; idx++) {
|
2011-06-06 19:24:01 -07:00
|
|
|
nsAccessible* child = aAccessible->ContentChildAt(jdx);
|
2010-10-20 21:16:10 -07:00
|
|
|
if (!child->IsBoundToParent()) {
|
|
|
|
NS_ERROR("Parent refers to a child, child doesn't refer to parent!");
|
|
|
|
jdx++;
|
2010-07-01 18:49:42 -07:00
|
|
|
}
|
2009-09-09 02:03:14 -07:00
|
|
|
|
2010-10-20 21:16:10 -07:00
|
|
|
ShutdownChildrenInSubtree(child);
|
2007-10-06 09:24:57 -07:00
|
|
|
}
|
|
|
|
|
2010-11-12 11:00:55 -08:00
|
|
|
UnbindFromDocument(aAccessible);
|
2007-10-06 09:24:57 -07:00
|
|
|
}
|
2011-08-08 00:55:36 -07:00
|
|
|
|
|
|
|
bool
|
|
|
|
nsDocAccessible::IsLoadEventTarget() const
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsISupports> container = mDocument->GetContainer();
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
|
|
|
|
do_QueryInterface(container);
|
|
|
|
NS_ASSERTION(docShellTreeItem, "No document shell for document!");
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
|
|
|
|
docShellTreeItem->GetParent(getter_AddRefs(parentTreeItem));
|
|
|
|
|
|
|
|
// It's not a root document.
|
|
|
|
if (parentTreeItem) {
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
|
|
|
|
docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
|
|
|
|
|
|
|
|
// It's not a sub document, i.e. a frame or iframe.
|
|
|
|
return (sameTypeRoot == docShellTreeItem);
|
|
|
|
}
|
|
|
|
|
|
|
|
// It's not chrome root document.
|
|
|
|
PRInt32 contentType;
|
|
|
|
docShellTreeItem->GetItemType(&contentType);
|
|
|
|
return (contentType == nsIDocShellTreeItem::typeContent);
|
|
|
|
}
|
|
|
|
|