2015-10-20 03:52:43 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
2012-05-21 15:12:37 +04:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2003-04-02 02:18:29 +04:00
|
|
|
|
2012-04-13 18:17:03 +04:00
|
|
|
#include "Accessible-inl.h"
|
2011-08-10 05:44:00 +04:00
|
|
|
#include "AccIterator.h"
|
2012-05-30 06:21:24 +04:00
|
|
|
#include "DocAccessible-inl.h"
|
2014-03-08 01:35:19 +04:00
|
|
|
#include "DocAccessibleChild.h"
|
2014-03-06 04:36:56 +04:00
|
|
|
#include "HTMLImageMapAccessible.h"
|
2010-04-27 10:52:03 +04:00
|
|
|
#include "nsAccCache.h"
|
2012-02-02 10:14:51 +04:00
|
|
|
#include "nsAccessiblePivot.h"
|
2010-04-27 10:52:03 +04:00
|
|
|
#include "nsAccUtils.h"
|
2012-07-28 08:21:40 +04:00
|
|
|
#include "nsEventShell.h"
|
2010-04-27 10:52:03 +04:00
|
|
|
#include "nsTextEquivUtils.h"
|
2012-01-12 07:07:35 +04:00
|
|
|
#include "Role.h"
|
2012-05-04 10:09:22 +04:00
|
|
|
#include "RootAccessible.h"
|
2012-11-19 13:20:09 +04:00
|
|
|
#include "TreeWalker.h"
|
2014-10-22 04:49:28 +04:00
|
|
|
#include "xpcAccessibleDocument.h"
|
2010-04-27 10:52:03 +04:00
|
|
|
|
2006-04-12 19:43:32 +04:00
|
|
|
#include "nsIMutableArray.h"
|
2003-05-15 12:37:38 +04:00
|
|
|
#include "nsICommandManager.h"
|
|
|
|
#include "nsIDocShell.h"
|
2003-04-02 02:18:29 +04:00
|
|
|
#include "nsIDocument.h"
|
2005-01-28 05:35:26 +03:00
|
|
|
#include "nsIDOMAttr.h"
|
2003-05-15 12:37:38 +04:00
|
|
|
#include "nsIDOMCharacterData.h"
|
2003-04-02 02:18:29 +04:00
|
|
|
#include "nsIDOMDocument.h"
|
2010-06-11 12:23:18 +04:00
|
|
|
#include "nsIDOMXULDocument.h"
|
2005-08-05 22:16:32 +04:00
|
|
|
#include "nsIDOMMutationEvent.h"
|
2005-11-29 02:56:44 +03:00
|
|
|
#include "nsPIDOMWindow.h"
|
2005-09-30 23:23:42 +04:00
|
|
|
#include "nsIDOMXULPopupElement.h"
|
2003-05-15 12:37:38 +04:00
|
|
|
#include "nsIEditingSession.h"
|
|
|
|
#include "nsIFrame.h"
|
2003-04-02 02:18:29 +04:00
|
|
|
#include "nsIInterfaceRequestorUtils.h"
|
2014-03-06 04:36:56 +04:00
|
|
|
#include "nsImageFrame.h"
|
2013-09-11 02:18:59 +04:00
|
|
|
#include "nsIPersistentProperties2.h"
|
2003-05-15 12:37:38 +04:00
|
|
|
#include "nsIPresShell.h"
|
2003-04-02 02:18:29 +04:00
|
|
|
#include "nsIServiceManager.h"
|
2013-01-05 07:12:24 +04:00
|
|
|
#include "nsViewManager.h"
|
2009-09-03 07:57:41 +04:00
|
|
|
#include "nsIScrollableFrame.h"
|
2005-06-24 23:16:45 +04:00
|
|
|
#include "nsUnicharUtils.h"
|
2003-05-15 12:37:38 +04:00
|
|
|
#include "nsIURI.h"
|
|
|
|
#include "nsIWebNavigation.h"
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 22:00:39 +04:00
|
|
|
#include "nsFocusManager.h"
|
2014-01-30 22:26:54 +04:00
|
|
|
#include "mozilla/ArrayUtils.h"
|
2012-10-16 02:06:03 +04:00
|
|
|
#include "mozilla/Assertions.h"
|
2014-04-03 08:18:36 +04:00
|
|
|
#include "mozilla/EventStates.h"
|
2014-01-21 01:52:04 +04:00
|
|
|
#include "mozilla/dom/DocumentType.h"
|
2010-08-24 11:05:56 +04:00
|
|
|
#include "mozilla/dom/Element.h"
|
2012-05-23 13:21:40 +04:00
|
|
|
|
2003-05-15 12:37:38 +04:00
|
|
|
#ifdef MOZ_XUL
|
|
|
|
#include "nsIXULDocument.h"
|
|
|
|
#endif
|
2003-04-02 02:18:29 +04:00
|
|
|
|
2011-10-11 09:50:08 +04:00
|
|
|
using namespace mozilla;
|
2011-07-27 16:43:01 +04:00
|
|
|
using namespace mozilla::a11y;
|
2010-08-24 11:05:56 +04:00
|
|
|
|
2009-12-10 22:12:19 +03:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Static member initialization
|
2003-04-02 02:18:29 +04:00
|
|
|
|
2010-11-18 05:55:44 +03:00
|
|
|
static nsIAtom** kRelationAttrs[] =
|
|
|
|
{
|
2011-06-04 01:35:17 +04:00
|
|
|
&nsGkAtoms::aria_labelledby,
|
|
|
|
&nsGkAtoms::aria_describedby,
|
|
|
|
&nsGkAtoms::aria_owns,
|
|
|
|
&nsGkAtoms::aria_controls,
|
|
|
|
&nsGkAtoms::aria_flowto,
|
|
|
|
&nsGkAtoms::_for,
|
|
|
|
&nsGkAtoms::control
|
2010-11-18 05:55:44 +03:00
|
|
|
};
|
|
|
|
|
2014-01-30 22:26:54 +04:00
|
|
|
static const uint32_t kRelationAttrsLen = ArrayLength(kRelationAttrs);
|
2009-12-10 22:12:19 +03:00
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Constructor/desctructor
|
|
|
|
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::
|
|
|
|
DocAccessible(nsIDocument* aDocument, nsIContent* aRootContent,
|
2012-02-09 20:49:17 +04:00
|
|
|
nsIPresShell* aPresShell) :
|
2014-10-22 04:49:28 +04:00
|
|
|
HyperTextAccessibleWrap(aRootContent, this),
|
2013-09-02 12:41:57 +04:00
|
|
|
// XXX aaronl should we use an algorithm for the initial cache size?
|
2014-08-06 17:31:21 +04:00
|
|
|
mAccessibleCache(kDefaultCacheLength),
|
|
|
|
mNodeToAccessibleMap(kDefaultCacheLength),
|
2013-09-23 09:37:19 +04:00
|
|
|
mDocumentNode(aDocument),
|
|
|
|
mScrollPositionChangedTicks(0),
|
2012-12-27 08:25:27 +04:00
|
|
|
mLoadState(eTreeConstructionPending), mDocFlags(0), mLoadEventType(0),
|
2012-07-30 18:20:58 +04:00
|
|
|
mVirtualCursor(nullptr),
|
2014-03-08 01:35:19 +04:00
|
|
|
mPresShell(aPresShell), mIPCDoc(nullptr)
|
2003-04-02 02:18:29 +04:00
|
|
|
{
|
2012-12-18 09:22:26 +04:00
|
|
|
mGenericTypes |= eDocument;
|
2012-12-11 07:31:42 +04:00
|
|
|
mStateFlags |= eNotNodeMapEntry;
|
|
|
|
|
2012-10-16 02:06:03 +04:00
|
|
|
MOZ_ASSERT(mPresShell, "should have been given a pres shell");
|
|
|
|
mPresShell->SetDocAccessible(this);
|
2011-05-20 09:17:47 +04:00
|
|
|
|
2011-02-15 05:44:15 +03:00
|
|
|
// If this is a XUL Document, it should not implement nsHyperText
|
2015-03-03 14:08:59 +03:00
|
|
|
if (mDocumentNode && mDocumentNode->IsXULDocument())
|
2012-12-18 09:22:26 +04:00
|
|
|
mGenericTypes &= ~eHyperText;
|
2003-04-02 02:18:29 +04:00
|
|
|
}
|
|
|
|
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::~DocAccessible()
|
2003-04-02 02:18:29 +04:00
|
|
|
{
|
2012-02-08 02:38:54 +04:00
|
|
|
NS_ASSERTION(!mPresShell, "LastRelease was never called!?!");
|
2003-04-02 02:18:29 +04:00
|
|
|
}
|
|
|
|
|
2012-02-01 01:14:30 +04:00
|
|
|
|
2008-08-06 16:19:56 +04:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2009-12-10 22:12:19 +03:00
|
|
|
// nsISupports
|
2008-08-06 16:19:56 +04:00
|
|
|
|
2013-08-02 05:29:05 +04:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(DocAccessible)
|
|
|
|
|
2012-05-29 05:18:45 +04:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DocAccessible, Accessible)
|
2012-11-15 11:32:40 +04:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationController)
|
2012-11-29 04:05:04 +04:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVirtualCursor)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildDocuments)
|
2015-10-20 03:52:43 +03:00
|
|
|
for (auto iter = tmp->mDependentIDsHash.Iter(); !iter.Done(); iter.Next()) {
|
|
|
|
AttrRelProviderArray* providers = iter.UserData();
|
|
|
|
|
|
|
|
for (int32_t jdx = providers->Length() - 1; jdx >= 0; jdx--) {
|
|
|
|
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
|
|
|
|
cb, "content of dependent ids hash entry of document accessible");
|
|
|
|
|
|
|
|
AttrRelProvider* provider = (*providers)[jdx];
|
|
|
|
cb.NoteXPCOMChild(provider->mContent);
|
|
|
|
|
|
|
|
NS_ASSERTION(provider->mContent->IsInDoc(),
|
|
|
|
"Referred content is not in document!");
|
|
|
|
}
|
|
|
|
}
|
2012-11-29 04:05:04 +04:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAccessibleCache)
|
2013-03-05 13:08:37 +04:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchorJumpElm)
|
2015-10-30 01:08:48 +03:00
|
|
|
for (auto it = tmp->mARIAOwnsHash.ConstIter(); !it.Done(); it.Next()) {
|
|
|
|
nsTArray<RefPtr<Accessible> >* ar = it.UserData();
|
|
|
|
for (uint32_t i = 0; i < ar->Length(); i++) {
|
|
|
|
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
|
|
|
"mARIAOwnsHash entry item");
|
|
|
|
cb.NoteXPCOMChild(ar->ElementAt(i));
|
|
|
|
}
|
2015-09-18 15:52:46 +03:00
|
|
|
}
|
2008-08-06 16:19:56 +04:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
|
2012-05-29 05:18:45 +04:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DocAccessible, Accessible)
|
2012-11-15 11:32:40 +04:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationController)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mVirtualCursor)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildDocuments)
|
2010-11-18 05:55:44 +03:00
|
|
|
tmp->mDependentIDsHash.Clear();
|
2010-10-21 08:16:10 +04:00
|
|
|
tmp->mNodeToAccessibleMap.Clear();
|
2012-11-29 04:05:04 +04:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAccessibleCache)
|
2013-03-05 13:08:37 +04:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchorJumpElm)
|
2015-10-30 01:08:48 +03:00
|
|
|
tmp->mARIAOwnsHash.Clear();
|
2008-08-06 16:19:56 +04:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
|
2012-05-27 13:01:40 +04:00
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DocAccessible)
|
2005-06-10 17:57:27 +04:00
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
|
2006-07-02 11:23:10 +04:00
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
|
2003-05-15 12:37:38 +04:00
|
|
|
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
2012-02-02 10:14:51 +04:00
|
|
|
NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivotObserver)
|
2014-10-22 04:49:28 +04:00
|
|
|
NS_INTERFACE_MAP_END_INHERITING(HyperTextAccessible)
|
2003-05-15 12:37:38 +04:00
|
|
|
|
2012-05-31 12:04:41 +04:00
|
|
|
NS_IMPL_ADDREF_INHERITED(DocAccessible, HyperTextAccessible)
|
|
|
|
NS_IMPL_RELEASE_INHERITED(DocAccessible, HyperTextAccessible)
|
2003-04-02 02:18:29 +04:00
|
|
|
|
2009-12-10 22:12:19 +03:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsIAccessible
|
|
|
|
|
2012-05-01 07:08:31 +04:00
|
|
|
ENameValueFlag
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::Name(nsString& aName)
|
2006-04-10 18:25:14 +04:00
|
|
|
{
|
2005-06-01 18:03:38 +04:00
|
|
|
aName.Truncate();
|
2012-05-01 07:08:31 +04:00
|
|
|
|
2008-03-14 23:49:38 +03:00
|
|
|
if (mParent) {
|
2012-05-01 07:08:31 +04:00
|
|
|
mParent->Name(aName); // Allow owning iframe to override the name
|
2005-06-01 18:03:38 +04:00
|
|
|
}
|
|
|
|
if (aName.IsEmpty()) {
|
2008-10-10 16:26:55 +04:00
|
|
|
// Allow name via aria-labelledby or title attribute
|
2012-05-29 05:18:45 +04:00
|
|
|
Accessible::Name(aName);
|
2005-06-01 18:03:38 +04:00
|
|
|
}
|
2008-03-14 23:49:38 +03:00
|
|
|
if (aName.IsEmpty()) {
|
2014-09-23 16:23:02 +04:00
|
|
|
Title(aName); // Try title element
|
2008-04-15 19:17:59 +04:00
|
|
|
}
|
|
|
|
if (aName.IsEmpty()) { // Last resort: use URL
|
2014-09-23 16:23:02 +04:00
|
|
|
URL(aName);
|
2005-08-17 23:08:26 +04:00
|
|
|
}
|
2014-09-23 16:23:02 +04:00
|
|
|
|
2012-05-01 07:08:31 +04:00
|
|
|
return eNameOK;
|
2003-04-15 12:45:55 +04:00
|
|
|
}
|
|
|
|
|
2012-05-29 05:18:45 +04:00
|
|
|
// Accessible public method
|
2012-01-12 07:07:35 +04:00
|
|
|
role
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::NativeRole()
|
2003-04-02 02:18:29 +04:00
|
|
|
{
|
2013-02-13 02:02:51 +04:00
|
|
|
nsCOMPtr<nsIDocShell> docShell = nsCoreUtils::GetDocShellFor(mDocumentNode);
|
|
|
|
if (docShell) {
|
2005-06-01 17:54:08 +04:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
|
2013-02-13 02:02:51 +04:00
|
|
|
docShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
|
2014-01-20 11:58:26 +04:00
|
|
|
int32_t itemType = docShell->ItemType();
|
2013-02-13 02:02:51 +04:00
|
|
|
if (sameTypeRoot == docShell) {
|
2005-06-01 17:54:08 +04:00
|
|
|
// Root of content or chrome tree
|
2010-09-05 06:14:01 +04:00
|
|
|
if (itemType == nsIDocShellTreeItem::typeChrome)
|
2012-01-12 07:07:35 +04:00
|
|
|
return roles::CHROME_WINDOW;
|
2010-09-05 06:14:01 +04:00
|
|
|
|
|
|
|
if (itemType == nsIDocShellTreeItem::typeContent) {
|
2005-07-21 18:28:17 +04:00
|
|
|
#ifdef MOZ_XUL
|
2012-11-23 02:15:25 +04:00
|
|
|
nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocumentNode));
|
2010-09-05 06:14:01 +04:00
|
|
|
if (xulDoc)
|
2012-01-12 07:07:35 +04:00
|
|
|
return roles::APPLICATION;
|
2005-07-21 18:28:17 +04:00
|
|
|
#endif
|
2012-01-12 07:07:35 +04:00
|
|
|
return roles::DOCUMENT;
|
2005-06-01 17:54:08 +04:00
|
|
|
}
|
|
|
|
}
|
2007-06-14 21:12:50 +04:00
|
|
|
else if (itemType == nsIDocShellTreeItem::typeContent) {
|
2012-01-12 07:07:35 +04:00
|
|
|
return roles::DOCUMENT;
|
2007-06-14 21:12:50 +04:00
|
|
|
}
|
2005-06-01 17:54:08 +04:00
|
|
|
}
|
2006-04-10 18:25:14 +04:00
|
|
|
|
2012-01-12 07:07:35 +04:00
|
|
|
return roles::PANE; // Fall back;
|
2003-04-02 02:18:29 +04:00
|
|
|
}
|
|
|
|
|
2011-04-23 17:14:05 +04:00
|
|
|
void
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::Description(nsString& aDescription)
|
2007-07-02 10:14:11 +04:00
|
|
|
{
|
2008-03-14 23:49:38 +03:00
|
|
|
if (mParent)
|
2011-04-23 17:14:05 +04:00
|
|
|
mParent->Description(aDescription);
|
2008-03-14 23:49:38 +03:00
|
|
|
|
2012-10-13 10:34:21 +04:00
|
|
|
if (HasOwnContent() && aDescription.IsEmpty()) {
|
2009-02-19 10:06:14 +03:00
|
|
|
nsTextEquivUtils::
|
2011-06-04 01:35:17 +04:00
|
|
|
GetTextEquivFromIDRefs(this, nsGkAtoms::aria_describedby,
|
2011-04-23 17:14:05 +04:00
|
|
|
aDescription);
|
2012-10-13 10:34:21 +04:00
|
|
|
}
|
2007-07-02 10:14:11 +04:00
|
|
|
}
|
|
|
|
|
2012-05-29 05:18:45 +04:00
|
|
|
// Accessible public method
|
2012-08-22 19:56:38 +04:00
|
|
|
uint64_t
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::NativeState()
|
2003-04-02 02:18:29 +04:00
|
|
|
{
|
2011-09-28 05:46:11 +04:00
|
|
|
// Document is always focusable.
|
2012-10-31 06:25:17 +04:00
|
|
|
uint64_t state = states::FOCUSABLE; // keep in sync with NativeInteractiveState() impl
|
2011-09-28 05:46:11 +04:00
|
|
|
if (FocusMgr()->IsFocused(this))
|
|
|
|
state |= states::FOCUSED;
|
2006-04-10 18:25:14 +04:00
|
|
|
|
2011-08-08 11:55:36 +04: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-19 09:45:35 +03:00
|
|
|
nsIFrame* frame = GetFrame();
|
2011-10-27 03:57:55 +04:00
|
|
|
if (!frame ||
|
|
|
|
!frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY)) {
|
2011-04-10 03:38:06 +04:00
|
|
|
state |= states::INVISIBLE | states::OFFSCREEN;
|
2004-05-26 16:31:43 +04:00
|
|
|
}
|
2003-05-15 12:37:38 +04:00
|
|
|
|
2012-03-08 07:28:38 +04:00
|
|
|
nsCOMPtr<nsIEditor> editor = GetEditor();
|
2011-04-10 03:38:06 +04:00
|
|
|
state |= editor ? states::EDITABLE : states::READONLY;
|
2006-06-21 17:29:10 +04:00
|
|
|
|
2011-04-10 03:38:06 +04:00
|
|
|
return state;
|
2003-04-02 02:18:29 +04:00
|
|
|
}
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
uint64_t
|
2012-06-04 09:41:06 +04:00
|
|
|
DocAccessible::NativeInteractiveState() const
|
|
|
|
{
|
|
|
|
// Document is always focusable.
|
|
|
|
return states::FOCUSABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
DocAccessible::NativelyUnavailable() const
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-05-29 05:18:45 +04:00
|
|
|
// Accessible public method
|
2011-04-10 03:38:06 +04:00
|
|
|
void
|
2012-08-22 19:56:38 +04:00
|
|
|
DocAccessible::ApplyARIAState(uint64_t* aState) const
|
2008-03-14 23:49:38 +03:00
|
|
|
{
|
2012-10-31 06:25:17 +04:00
|
|
|
// Grab states from content element.
|
|
|
|
if (mContent)
|
|
|
|
Accessible::ApplyARIAState(aState);
|
2008-03-14 23:49:38 +03:00
|
|
|
|
2012-10-31 06:25:17 +04:00
|
|
|
// Allow iframe/frame etc. to have final state override via ARIA.
|
2011-04-10 03:38:06 +04:00
|
|
|
if (mParent)
|
|
|
|
mParent->ApplyARIAState(aState);
|
2008-03-14 23:49:38 +03:00
|
|
|
}
|
|
|
|
|
2012-10-19 11:15:23 +04:00
|
|
|
already_AddRefed<nsIPersistentProperties>
|
|
|
|
DocAccessible::Attributes()
|
2008-03-14 23:49:38 +03:00
|
|
|
{
|
2012-10-19 11:15:23 +04:00
|
|
|
nsCOMPtr<nsIPersistentProperties> attributes =
|
|
|
|
HyperTextAccessibleWrap::Attributes();
|
|
|
|
|
2012-10-22 19:54:41 +04:00
|
|
|
// No attributes if document is not attached to the tree or if it's a root
|
|
|
|
// document.
|
|
|
|
if (!mParent || IsRoot())
|
2012-10-19 11:15:23 +04:00
|
|
|
return attributes.forget();
|
|
|
|
|
|
|
|
// Override ARIA object attributes from outerdoc.
|
|
|
|
aria::AttrIterator attribIter(mParent->GetContent());
|
|
|
|
nsAutoString name, value, unused;
|
|
|
|
while(attribIter.Next(name, value))
|
|
|
|
attributes->SetStringProperty(NS_ConvertUTF16toUTF8(name), value, unused);
|
|
|
|
|
|
|
|
return attributes.forget();
|
2008-03-14 23:49:38 +03:00
|
|
|
}
|
|
|
|
|
2012-05-29 05:18:45 +04:00
|
|
|
Accessible*
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::FocusedChild()
|
2005-02-18 17:36:28 +03:00
|
|
|
{
|
2005-09-09 06:30:16 +04:00
|
|
|
// Return an accessible for the current global focus, which does not have to
|
|
|
|
// be contained within the current document.
|
2011-09-28 05:46:11 +04:00
|
|
|
return FocusMgr()->FocusedAccessible();
|
2005-02-18 17:36:28 +03:00
|
|
|
}
|
|
|
|
|
2014-09-16 21:30:23 +04:00
|
|
|
void
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::TakeFocus()
|
2007-01-10 09:32:15 +03:00
|
|
|
{
|
2010-06-11 12:23:18 +04:00
|
|
|
// Focus the document.
|
2012-02-12 06:34:00 +04:00
|
|
|
nsFocusManager* fm = nsFocusManager::GetFocusManager();
|
2010-06-11 12:23:18 +04:00
|
|
|
nsCOMPtr<nsIDOMElement> newFocus;
|
2014-09-16 21:30:23 +04:00
|
|
|
fm->MoveFocus(mDocumentNode->GetWindow(), nullptr,
|
|
|
|
nsFocusManager::MOVEFOCUS_ROOT, 0, getter_AddRefs(newFocus));
|
2007-01-10 09:32:15 +03:00
|
|
|
}
|
|
|
|
|
2012-05-31 12:04:41 +04:00
|
|
|
// HyperTextAccessible method
|
2012-03-08 07:28:38 +04:00
|
|
|
already_AddRefed<nsIEditor>
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::GetEditor() const
|
2006-06-21 17:29:10 +04:00
|
|
|
{
|
2008-10-08 16:50:36 +04:00
|
|
|
// Check if document is editable (designMode="on" case). Otherwise check if
|
|
|
|
// the html:body (for HTML document case) or document element is editable.
|
2012-11-23 02:15:25 +04:00
|
|
|
if (!mDocumentNode->HasFlag(NODE_IS_EDITABLE) &&
|
2012-10-31 06:25:17 +04:00
|
|
|
(!mContent || !mContent->HasFlag(NODE_IS_EDITABLE)))
|
2012-07-30 18:20:58 +04:00
|
|
|
return nullptr;
|
2003-05-15 12:37:38 +04:00
|
|
|
|
2012-11-23 02:15:25 +04:00
|
|
|
nsCOMPtr<nsISupports> container = mDocumentNode->GetContainer();
|
2003-05-15 12:37:38 +04:00
|
|
|
nsCOMPtr<nsIEditingSession> editingSession(do_GetInterface(container));
|
|
|
|
if (!editingSession)
|
2012-07-30 18:20:58 +04:00
|
|
|
return nullptr; // No editing session interface
|
2003-05-15 12:37:38 +04:00
|
|
|
|
2006-07-19 05:56:54 +04:00
|
|
|
nsCOMPtr<nsIEditor> editor;
|
2012-11-23 02:15:25 +04:00
|
|
|
editingSession->GetEditorForWindow(mDocumentNode->GetWindow(), getter_AddRefs(editor));
|
2012-03-08 07:28:38 +04:00
|
|
|
if (!editor)
|
2012-07-30 18:20:58 +04:00
|
|
|
return nullptr;
|
2012-03-08 07:28:38 +04:00
|
|
|
|
|
|
|
bool isEditable = false;
|
2007-08-14 20:25:24 +04:00
|
|
|
editor->GetIsDocumentEditable(&isEditable);
|
2012-03-08 07:28:38 +04:00
|
|
|
if (isEditable)
|
|
|
|
return editor.forget();
|
|
|
|
|
2012-07-30 18:20:58 +04:00
|
|
|
return nullptr;
|
2003-05-15 12:37:38 +04:00
|
|
|
}
|
|
|
|
|
2012-05-27 13:01:40 +04:00
|
|
|
// DocAccessible public method
|
2014-09-23 16:23:02 +04:00
|
|
|
|
|
|
|
void
|
|
|
|
DocAccessible::URL(nsAString& aURL) const
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsISupports> container = mDocumentNode->GetContainer();
|
|
|
|
nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(container));
|
|
|
|
nsAutoCString theURL;
|
|
|
|
if (webNav) {
|
|
|
|
nsCOMPtr<nsIURI> pURI;
|
|
|
|
webNav->GetCurrentURI(getter_AddRefs(pURI));
|
|
|
|
if (pURI)
|
|
|
|
pURI->GetSpec(theURL);
|
|
|
|
}
|
|
|
|
CopyUTF8toUTF16(theURL, aURL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
DocAccessible::DocType(nsAString& aType) const
|
|
|
|
{
|
|
|
|
#ifdef MOZ_XUL
|
|
|
|
nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocumentNode));
|
|
|
|
if (xulDoc) {
|
|
|
|
aType.AssignLiteral("window"); // doctype not implemented for XUL at time of writing - causes assertion
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
dom::DocumentType* docType = mDocumentNode->GetDoctype();
|
|
|
|
if (docType)
|
|
|
|
docType->GetPublicId(aType);
|
|
|
|
}
|
|
|
|
|
2012-05-29 05:18:45 +04:00
|
|
|
Accessible*
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::GetAccessible(nsINode* aNode) const
|
2003-04-15 12:45:55 +04:00
|
|
|
{
|
2012-05-29 05:18:45 +04:00
|
|
|
Accessible* accessible = mNodeToAccessibleMap.Get(aNode);
|
2010-02-11 16:56:01 +03:00
|
|
|
|
|
|
|
// No accessible in the cache, check if the given ID is unique ID of this
|
2010-06-12 08:04:35 +04:00
|
|
|
// document accessible.
|
|
|
|
if (!accessible) {
|
2010-10-21 08:16:10 +04:00
|
|
|
if (GetNode() != aNode)
|
2012-07-30 18:20:58 +04:00
|
|
|
return nullptr;
|
2010-06-12 08:04:35 +04:00
|
|
|
|
2012-05-27 13:01:40 +04:00
|
|
|
accessible = const_cast<DocAccessible*>(this);
|
2010-02-11 16:56:01 +03:00
|
|
|
}
|
|
|
|
|
2010-02-09 16:29:22 +03:00
|
|
|
#ifdef DEBUG
|
2006-08-11 21:21:56 +04: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.
|
2012-05-29 05:18:45 +04:00
|
|
|
Accessible* parent = accessible->Parent();
|
2010-06-12 08:04:35 +04:00
|
|
|
if (parent)
|
|
|
|
parent->TestChildCache(accessible);
|
2006-08-11 21:21:56 +04:00
|
|
|
#endif
|
2010-02-11 16:58:35 +03:00
|
|
|
|
2010-06-12 08:04:35 +04:00
|
|
|
return accessible;
|
2003-04-15 12:45:55 +04:00
|
|
|
}
|
|
|
|
|
2009-12-10 22:12:19 +03:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2013-10-29 07:30:55 +04:00
|
|
|
// Accessible
|
2005-08-10 05:39:43 +04:00
|
|
|
|
2012-11-20 07:54:41 +04:00
|
|
|
void
|
|
|
|
DocAccessible::Init()
|
|
|
|
{
|
|
|
|
#ifdef A11Y_LOG
|
|
|
|
if (logging::IsEnabled(logging::eDocCreate))
|
2012-11-23 02:15:25 +04:00
|
|
|
logging::DocCreate("document initialize", mDocumentNode, this);
|
2012-11-20 07:54:41 +04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// Initialize notification controller.
|
|
|
|
mNotificationController = new NotificationController(this, mPresShell);
|
|
|
|
|
|
|
|
// 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.
|
2012-11-23 02:15:25 +04:00
|
|
|
if (mDocumentNode->GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE)
|
2012-11-20 07:54:41 +04:00
|
|
|
mLoadState |= eDOMLoaded;
|
|
|
|
|
|
|
|
AddEventListeners();
|
|
|
|
}
|
|
|
|
|
2010-06-12 08:04:35 +04:00
|
|
|
void
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::Shutdown()
|
2003-04-02 02:18:29 +04:00
|
|
|
{
|
2012-02-08 02:38:54 +04:00
|
|
|
if (!mPresShell) // already shutdown
|
2010-06-12 08:04:35 +04:00
|
|
|
return;
|
2003-04-15 12:45:55 +04:00
|
|
|
|
2012-10-04 13:57:09 +04:00
|
|
|
#ifdef A11Y_LOG
|
2012-05-23 13:21:40 +04:00
|
|
|
if (logging::IsEnabled(logging::eDocDestroy))
|
2012-11-23 02:15:25 +04:00
|
|
|
logging::DocDestroy("document shutdown", mDocumentNode, this);
|
2012-05-23 13:21:40 +04:00
|
|
|
#endif
|
2010-06-08 20:39:58 +04:00
|
|
|
|
2011-01-18 11:03:38 +03:00
|
|
|
if (mNotificationController) {
|
|
|
|
mNotificationController->Shutdown();
|
2012-07-30 18:20:58 +04:00
|
|
|
mNotificationController = nullptr;
|
2010-02-26 22:02:39 +03:00
|
|
|
}
|
2010-01-27 14:42:08 +03:00
|
|
|
|
2003-05-15 12:37:38 +04:00
|
|
|
RemoveEventListeners();
|
|
|
|
|
2011-03-28 18:00:02 +04:00
|
|
|
// Mark the document as shutdown before AT is notified about the document
|
2012-06-02 09:14:58 +04:00
|
|
|
// removal from its container (valid for root documents on ATK and due to
|
|
|
|
// some reason for MSAA, refer to bug 757392 for details).
|
2012-12-11 07:31:42 +04:00
|
|
|
mStateFlags |= eIsDefunct;
|
2012-11-23 02:15:25 +04:00
|
|
|
nsCOMPtr<nsIDocument> kungFuDeathGripDoc = mDocumentNode;
|
|
|
|
mDocumentNode = nullptr;
|
2011-03-28 18:00:02 +04:00
|
|
|
|
2010-09-09 18:44:56 +04:00
|
|
|
if (mParent) {
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible* parentDocument = mParent->Document();
|
2010-09-09 18:44:56 +04:00
|
|
|
if (parentDocument)
|
|
|
|
parentDocument->RemoveChildDocument(this);
|
|
|
|
|
2010-06-08 20:39:58 +04:00
|
|
|
mParent->RemoveChild(this);
|
2010-09-09 18:44:56 +04:00
|
|
|
}
|
|
|
|
|
2010-11-09 22:34:25 +03:00
|
|
|
// Walk the array backwards because child documents remove themselves from the
|
|
|
|
// array as they are shutdown.
|
2012-08-22 19:56:38 +04:00
|
|
|
int32_t childDocCount = mChildDocuments.Length();
|
|
|
|
for (int32_t idx = childDocCount - 1; idx >= 0; idx--)
|
2010-10-28 13:34:26 +04:00
|
|
|
mChildDocuments[idx]->Shutdown();
|
|
|
|
|
2010-09-09 18:44:56 +04:00
|
|
|
mChildDocuments.Clear();
|
2010-06-08 20:39:58 +04:00
|
|
|
|
2014-03-08 01:35:19 +04:00
|
|
|
// XXX thinking about ordering?
|
2015-06-08 18:38:40 +03:00
|
|
|
if (mIPCDoc) {
|
|
|
|
MOZ_ASSERT(IPCAccessibilityActive());
|
2015-06-02 17:30:51 +03:00
|
|
|
mIPCDoc->Shutdown();
|
2014-03-08 01:35:19 +04:00
|
|
|
MOZ_ASSERT(!mIPCDoc);
|
|
|
|
}
|
|
|
|
|
2012-02-02 10:14:51 +04:00
|
|
|
if (mVirtualCursor) {
|
|
|
|
mVirtualCursor->RemoveObserver(this);
|
2012-07-30 18:20:58 +04:00
|
|
|
mVirtualCursor = nullptr;
|
2012-02-02 10:14:51 +04:00
|
|
|
}
|
|
|
|
|
2012-11-03 03:57:58 +04:00
|
|
|
mPresShell->SetDocAccessible(nullptr);
|
2012-07-30 18:20:58 +04:00
|
|
|
mPresShell = nullptr; // Avoid reentrancy
|
2003-04-15 12:45:55 +04:00
|
|
|
|
2010-11-18 05:55:44 +03:00
|
|
|
mDependentIDsHash.Clear();
|
2010-10-21 08:16:10 +04:00
|
|
|
mNodeToAccessibleMap.Clear();
|
2014-09-02 22:54:04 +04:00
|
|
|
|
|
|
|
{
|
|
|
|
// We're about to get rid of all of our children so there won't be anything
|
|
|
|
// to invalidate.
|
|
|
|
AutoTreeMutation mut(this, false);
|
|
|
|
ClearCache(mAccessibleCache);
|
|
|
|
}
|
2007-12-27 08:13:40 +03:00
|
|
|
|
2012-05-31 12:04:41 +04:00
|
|
|
HyperTextAccessibleWrap::Shutdown();
|
2010-10-28 13:34:26 +04:00
|
|
|
|
2014-10-22 04:49:28 +04:00
|
|
|
GetAccService()->NotifyOfDocumentShutdown(this, kungFuDeathGripDoc);
|
2003-04-02 02:18:29 +04:00
|
|
|
}
|
|
|
|
|
2008-11-01 06:58:07 +03:00
|
|
|
nsIFrame*
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::GetFrame() const
|
2003-04-02 02:18:29 +04:00
|
|
|
{
|
2012-07-30 18:20:58 +04:00
|
|
|
nsIFrame* root = nullptr;
|
2012-02-08 02:38:54 +04:00
|
|
|
if (mPresShell)
|
|
|
|
root = mPresShell->GetRootFrame();
|
2003-04-02 02:18:29 +04:00
|
|
|
|
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
2012-05-27 13:01:40 +04:00
|
|
|
// DocAccessible protected member
|
2014-09-16 21:30:23 +04:00
|
|
|
nsRect
|
|
|
|
DocAccessible::RelativeBounds(nsIFrame** aRelativeFrame) const
|
2003-04-02 02:18:29 +04:00
|
|
|
{
|
|
|
|
*aRelativeFrame = GetFrame();
|
2003-05-19 13:07:41 +04:00
|
|
|
|
2012-11-23 02:15:25 +04:00
|
|
|
nsIDocument *document = mDocumentNode;
|
2012-07-30 18:20:58 +04:00
|
|
|
nsIDocument *parentDoc = nullptr;
|
2003-05-19 13:07:41 +04:00
|
|
|
|
2014-09-16 21:30:23 +04:00
|
|
|
nsRect bounds;
|
2003-05-19 13:07:41 +04:00
|
|
|
while (document) {
|
2010-06-25 17:59:57 +04:00
|
|
|
nsIPresShell *presShell = document->GetShell();
|
2014-09-16 21:30:23 +04:00
|
|
|
if (!presShell)
|
|
|
|
return nsRect();
|
2003-05-19 13:07:41 +04:00
|
|
|
|
2009-09-03 07:57:41 +04:00
|
|
|
nsRect scrollPort;
|
|
|
|
nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollableExternal();
|
|
|
|
if (sf) {
|
|
|
|
scrollPort = sf->GetScrollPortRect();
|
|
|
|
} else {
|
2010-01-21 04:07:35 +03:00
|
|
|
nsIFrame* rootFrame = presShell->GetRootFrame();
|
2014-09-16 21:30:23 +04:00
|
|
|
if (!rootFrame)
|
|
|
|
return nsRect();
|
|
|
|
|
2010-01-21 04:07:35 +03:00
|
|
|
scrollPort = rootFrame->GetRect();
|
2003-05-19 13:07:41 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (parentDoc) { // After first time thru loop
|
2009-09-03 07:57:41 +04: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.
|
2014-09-16 21:30:23 +04:00
|
|
|
bounds.IntersectRect(scrollPort, bounds);
|
2003-05-19 13:07:41 +04:00
|
|
|
}
|
|
|
|
else { // First time through loop
|
2014-09-16 21:30:23 +04:00
|
|
|
bounds = scrollPort;
|
2003-05-19 13:07:41 +04:00
|
|
|
}
|
|
|
|
|
2003-10-22 10:09:48 +04:00
|
|
|
document = parentDoc = document->GetParentDocument();
|
2003-05-19 13:07:41 +04:00
|
|
|
}
|
2014-09-16 21:30:23 +04:00
|
|
|
|
|
|
|
return bounds;
|
2003-04-02 02:18:29 +04:00
|
|
|
}
|
|
|
|
|
2012-11-20 07:54:41 +04:00
|
|
|
// DocAccessible protected member
|
|
|
|
nsresult
|
|
|
|
DocAccessible::AddEventListeners()
|
|
|
|
{
|
2013-11-15 20:32:12 +04:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(mDocumentNode->GetDocShell());
|
2012-11-20 07:54:41 +04:00
|
|
|
|
2012-11-20 19:33:30 +04:00
|
|
|
// We want to add a command observer only if the document is content and has
|
|
|
|
// an editor.
|
2014-01-20 11:58:26 +04:00
|
|
|
if (docShellTreeItem->ItemType() == nsIDocShellTreeItem::typeContent) {
|
2012-11-20 07:54:41 +04:00
|
|
|
nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(docShellTreeItem);
|
2012-11-20 19:33:30 +04:00
|
|
|
if (commandManager)
|
2012-11-20 07:54:41 +04:00
|
|
|
commandManager->AddCommandObserver(this, "obs_documentCreated");
|
|
|
|
}
|
|
|
|
|
2013-03-07 16:16:10 +04:00
|
|
|
SelectionMgr()->AddDocSelectionListener(mPresShell);
|
2012-11-20 07:54:41 +04:00
|
|
|
|
2012-11-20 19:33:30 +04:00
|
|
|
// Add document observer.
|
2012-11-23 02:15:25 +04:00
|
|
|
mDocumentNode->AddObserver(this);
|
2012-11-20 07:54:41 +04:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2012-05-27 13:01:40 +04:00
|
|
|
// DocAccessible protected member
|
|
|
|
nsresult
|
|
|
|
DocAccessible::RemoveEventListeners()
|
2003-04-15 12:45:55 +04:00
|
|
|
{
|
|
|
|
// Remove listeners associated with content documents
|
|
|
|
// Remove scroll position listener
|
2004-11-08 05:29:47 +03:00
|
|
|
RemoveScrollListener();
|
2003-04-28 14:24:52 +04:00
|
|
|
|
2012-11-23 02:15:25 +04:00
|
|
|
NS_ASSERTION(mDocumentNode, "No document during removal of listeners.");
|
2009-06-28 21:45:04 +04:00
|
|
|
|
2012-11-23 02:15:25 +04:00
|
|
|
if (mDocumentNode) {
|
|
|
|
mDocumentNode->RemoveObserver(this);
|
2009-06-28 21:45:04 +04:00
|
|
|
|
2013-11-15 20:32:12 +04:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem(mDocumentNode->GetDocShell());
|
2009-06-28 21:45:04 +04:00
|
|
|
NS_ASSERTION(docShellTreeItem, "doc should support nsIDocShellTreeItem.");
|
|
|
|
|
|
|
|
if (docShellTreeItem) {
|
2014-01-20 11:58:26 +04:00
|
|
|
if (docShellTreeItem->ItemType() == nsIDocShellTreeItem::typeContent) {
|
2009-06-28 21:45:04 +04:00
|
|
|
nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(docShellTreeItem);
|
|
|
|
if (commandManager) {
|
|
|
|
commandManager->RemoveCommandObserver(this, "obs_documentCreated");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2003-05-15 12:37:38 +04:00
|
|
|
|
2003-06-16 14:22:40 +04:00
|
|
|
if (mScrollWatchTimer) {
|
|
|
|
mScrollWatchTimer->Cancel();
|
2012-07-30 18:20:58 +04:00
|
|
|
mScrollWatchTimer = nullptr;
|
2008-01-18 23:36:44 +03:00
|
|
|
NS_RELEASE_THIS(); // Kung fu death grip
|
2003-06-16 14:22:40 +04:00
|
|
|
}
|
|
|
|
|
2013-03-07 16:16:10 +04:00
|
|
|
SelectionMgr()->RemoveDocSelectionListener(mPresShell);
|
2003-07-22 18:55:22 +04:00
|
|
|
return NS_OK;
|
2003-04-15 12:45:55 +04:00
|
|
|
}
|
|
|
|
|
2012-05-27 13:01:40 +04:00
|
|
|
void
|
|
|
|
DocAccessible::ScrollTimerCallback(nsITimer* aTimer, void* aClosure)
|
2003-04-15 12:45:55 +04:00
|
|
|
{
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible* docAcc = reinterpret_cast<DocAccessible*>(aClosure);
|
2003-04-15 12:45:55 +04:00
|
|
|
|
2006-04-10 18:25:14 +04:00
|
|
|
if (docAcc && docAcc->mScrollPositionChangedTicks &&
|
2003-04-15 12:45:55 +04:00
|
|
|
++docAcc->mScrollPositionChangedTicks > 2) {
|
|
|
|
// Whenever scroll position changes, mScrollPositionChangeTicks gets reset to 1
|
|
|
|
// We only want to fire accessibilty scroll event when scrolling stops or pauses
|
|
|
|
// Therefore, we wait for no scroll events to occur between 2 ticks of this timer
|
|
|
|
// That indicates a pause in scrolling, so we fire the accessibilty scroll event
|
2010-01-18 19:16:07 +03:00
|
|
|
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SCROLLING_END, docAcc);
|
2007-07-05 20:02:55 +04:00
|
|
|
|
2003-04-15 12:45:55 +04:00
|
|
|
docAcc->mScrollPositionChangedTicks = 0;
|
2003-05-01 14:25:45 +04:00
|
|
|
if (docAcc->mScrollWatchTimer) {
|
|
|
|
docAcc->mScrollWatchTimer->Cancel();
|
2012-07-30 18:20:58 +04:00
|
|
|
docAcc->mScrollWatchTimer = nullptr;
|
2008-01-18 23:36:44 +03:00
|
|
|
NS_RELEASE(docAcc); // Release kung fu death grip
|
2003-05-01 14:25:45 +04:00
|
|
|
}
|
2003-04-15 12:45:55 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-10 22:12:19 +03:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsIScrollPositionListener
|
|
|
|
|
2012-05-27 13:01:40 +04:00
|
|
|
void
|
|
|
|
DocAccessible::ScrollPositionDidChange(nscoord aX, nscoord aY)
|
2003-04-15 12:45:55 +04:00
|
|
|
{
|
2006-04-10 18:25:14 +04:00
|
|
|
// Start new timer, if the timer cycles at least 1 full cycle without more scroll position changes,
|
2003-04-15 12:45:55 +04:00
|
|
|
// then the ::Notify() method will fire the accessibility event for scroll position changes
|
2012-08-22 19:56:38 +04:00
|
|
|
const uint32_t kScrollPosCheckWait = 50;
|
2003-04-15 12:45:55 +04:00
|
|
|
if (mScrollWatchTimer) {
|
|
|
|
mScrollWatchTimer->SetDelay(kScrollPosCheckWait); // Create new timer, to avoid leaks
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mScrollWatchTimer = do_CreateInstance("@mozilla.org/timer;1");
|
|
|
|
if (mScrollWatchTimer) {
|
2008-01-18 23:36:44 +03:00
|
|
|
NS_ADDREF_THIS(); // Kung fu death grip
|
2003-04-15 12:45:55 +04:00
|
|
|
mScrollWatchTimer->InitWithFuncCallback(ScrollTimerCallback, this,
|
2006-04-10 18:25:14 +04:00
|
|
|
kScrollPosCheckWait,
|
2003-04-15 12:45:55 +04:00
|
|
|
nsITimer::TYPE_REPEATING_SLACK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mScrollPositionChangedTicks = 1;
|
|
|
|
}
|
|
|
|
|
2009-12-10 22:12:19 +03:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsIObserver
|
|
|
|
|
2012-05-27 13:01:40 +04:00
|
|
|
NS_IMETHODIMP
|
|
|
|
DocAccessible::Observe(nsISupports* aSubject, const char* aTopic,
|
2014-01-04 19:02:17 +04:00
|
|
|
const char16_t* aData)
|
2003-05-15 12:37:38 +04:00
|
|
|
{
|
2007-08-14 20:25:24 +04:00
|
|
|
if (!nsCRT::strcmp(aTopic,"obs_documentCreated")) {
|
|
|
|
// State editable will now be set, readonly is now clear
|
2010-06-02 16:30:08 +04:00
|
|
|
// Normally we only fire delayed events created from the node, not an
|
2010-08-25 06:08:28 +04:00
|
|
|
// accessible object. See the AccStateChangeEvent constructor for details
|
2010-06-02 16:30:08 +04:00
|
|
|
// about this exceptional case.
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> event =
|
2011-10-17 18:59:28 +04:00
|
|
|
new AccStateChangeEvent(this, states::EDITABLE, true);
|
2012-11-20 08:53:38 +04:00
|
|
|
FireDelayedEvent(event);
|
2003-05-15 12:37:38 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2012-02-02 10:14:51 +04:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsIAccessiblePivotObserver
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::OnPivotChanged(nsIAccessiblePivot* aPivot,
|
|
|
|
nsIAccessible* aOldAccessible,
|
2012-08-22 19:56:38 +04:00
|
|
|
int32_t aOldStart, int32_t aOldEnd,
|
2014-08-15 05:44:59 +04:00
|
|
|
PivotMoveReason aReason,
|
|
|
|
bool aIsFromUserInput)
|
2012-02-02 10:14:51 +04:00
|
|
|
{
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> event =
|
2014-10-22 04:49:28 +04:00
|
|
|
new AccVCChangeEvent(
|
|
|
|
this, (aOldAccessible ? aOldAccessible->ToInternalAccessible() : nullptr),
|
|
|
|
aOldStart, aOldEnd, aReason,
|
|
|
|
aIsFromUserInput ? eFromUserInput : eNoUserInput);
|
2012-02-02 10:14:51 +04:00
|
|
|
nsEventShell::FireEvent(event);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2009-12-10 22:12:19 +03:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2005-06-10 17:57:27 +04:00
|
|
|
// nsIDocumentObserver
|
2003-04-28 14:24:52 +04:00
|
|
|
|
2012-05-27 13:01:40 +04:00
|
|
|
NS_IMPL_NSIDOCUMENTOBSERVER_CORE_STUB(DocAccessible)
|
|
|
|
NS_IMPL_NSIDOCUMENTOBSERVER_LOAD_STUB(DocAccessible)
|
|
|
|
NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(DocAccessible)
|
2003-04-28 14:24:52 +04:00
|
|
|
|
2009-06-29 22:36:25 +04:00
|
|
|
void
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::AttributeWillChange(nsIDocument* aDocument,
|
|
|
|
dom::Element* aElement,
|
2012-08-22 19:56:38 +04:00
|
|
|
int32_t aNameSpaceID,
|
2015-07-25 09:05:19 +03:00
|
|
|
nsIAtom* aAttribute, int32_t aModType,
|
|
|
|
const nsAttrValue* aNewValue)
|
2009-06-29 22:36:25 +04:00
|
|
|
{
|
2012-05-29 05:18:45 +04:00
|
|
|
Accessible* accessible = GetAccessible(aElement);
|
2011-08-11 15:45:36 +04:00
|
|
|
if (!accessible) {
|
|
|
|
if (aElement != mContent)
|
|
|
|
return;
|
|
|
|
|
|
|
|
accessible = this;
|
|
|
|
}
|
2010-11-18 05:55:44 +03:00
|
|
|
|
2010-12-16 22:29:51 +03: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 15:45:36 +04:00
|
|
|
if (aModType != nsIDOMMutationEvent::ADDITION)
|
2015-09-15 19:01:51 +03:00
|
|
|
RemoveDependentIDsFor(accessible, aAttribute);
|
2011-08-11 15:45:36 +04:00
|
|
|
|
2015-10-30 01:08:48 +03:00
|
|
|
if (aAttribute == nsGkAtoms::id) {
|
|
|
|
RelocateARIAOwnedIfNeeded(aElement);
|
|
|
|
}
|
|
|
|
|
2011-08-11 15:45:36 +04:00
|
|
|
// 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-04 01:35:17 +04:00
|
|
|
if (aAttribute == nsGkAtoms::aria_checked ||
|
|
|
|
aAttribute == nsGkAtoms::aria_pressed) {
|
2011-08-11 15:45:36 +04:00
|
|
|
mARIAAttrOldValue = (aModType != nsIDOMMutationEvent::ADDITION) ?
|
2012-07-30 18:20:58 +04:00
|
|
|
nsAccUtils::GetARIAToken(aElement, aAttribute) : nullptr;
|
2013-11-20 22:24:30 +04:00
|
|
|
return;
|
2010-11-18 05:55:44 +03:00
|
|
|
}
|
2013-11-20 22:24:30 +04:00
|
|
|
|
|
|
|
if (aAttribute == nsGkAtoms::aria_disabled ||
|
|
|
|
aAttribute == nsGkAtoms::disabled)
|
|
|
|
mStateBitWasOn = accessible->Unavailable();
|
2009-06-29 22:36:25 +04:00
|
|
|
}
|
|
|
|
|
2015-09-24 18:23:32 +03:00
|
|
|
void
|
|
|
|
DocAccessible::NativeAnonymousChildListChange(nsIDocument* aDocument,
|
|
|
|
nsIContent* aContent,
|
|
|
|
bool aIsRemove)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2005-06-10 17:57:27 +04:00
|
|
|
void
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::AttributeChanged(nsIDocument* aDocument,
|
|
|
|
dom::Element* aElement,
|
2012-08-22 19:56:38 +04:00
|
|
|
int32_t aNameSpaceID, nsIAtom* aAttribute,
|
2015-07-25 09:01:19 +03:00
|
|
|
int32_t aModType,
|
|
|
|
const nsAttrValue* aOldValue)
|
2007-09-19 01:36:41 +04:00
|
|
|
{
|
2011-01-18 07:15:05 +03:00
|
|
|
NS_ASSERTION(!IsDefunct(),
|
|
|
|
"Attribute changed called on defunct document accessible!");
|
|
|
|
|
2010-12-18 21:33:00 +03: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-19 01:36:41 +04:00
|
|
|
|
2010-12-18 21:33:00 +03:00
|
|
|
// Ignore attribute change if the element doesn't have an accessible (at all
|
|
|
|
// or still) iff the element is not a root content of this document accessible
|
|
|
|
// (which is treated as attribute change on this document accessible).
|
2011-01-18 07:15:05 +03: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.
|
2012-05-29 05:18:45 +04:00
|
|
|
Accessible* accessible = GetAccessible(aElement);
|
2011-02-22 04:56:57 +03:00
|
|
|
if (!accessible) {
|
|
|
|
if (mContent != aElement)
|
|
|
|
return;
|
|
|
|
|
|
|
|
accessible = this;
|
|
|
|
}
|
2010-12-16 22:29:51 +03:00
|
|
|
|
2010-12-18 21:33:00 +03: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.
|
2012-11-20 08:53:38 +04:00
|
|
|
AttributeChangedImpl(accessible, aNameSpaceID, aAttribute);
|
2010-12-18 21:33:00 +03:00
|
|
|
|
2010-12-16 22:29:51 +03:00
|
|
|
// Update dependent IDs cache. Take care of accessible elements because no
|
|
|
|
// accessible element means either the element is not accessible at all or
|
|
|
|
// its accessible will be created later. It doesn't make sense to keep
|
|
|
|
// dependent IDs for non accessible elements. For the second case we'll update
|
|
|
|
// dependent IDs cache when its accessible is created.
|
2010-11-18 05:55:44 +03:00
|
|
|
if (aModType == nsIDOMMutationEvent::MODIFICATION ||
|
|
|
|
aModType == nsIDOMMutationEvent::ADDITION) {
|
2015-09-15 19:01:51 +03:00
|
|
|
AddDependentIDsFor(accessible, aAttribute);
|
2010-11-18 05:55:44 +03:00
|
|
|
}
|
2007-09-19 01:36:41 +04:00
|
|
|
}
|
|
|
|
|
2012-05-27 13:01:40 +04:00
|
|
|
// DocAccessible protected member
|
2007-09-19 01:36:41 +04:00
|
|
|
void
|
2012-11-20 08:53:38 +04:00
|
|
|
DocAccessible::AttributeChangedImpl(Accessible* aAccessible,
|
|
|
|
int32_t aNameSpaceID, nsIAtom* aAttribute)
|
2003-04-28 14:24:52 +04:00
|
|
|
{
|
2007-04-17 08:45:42 +04:00
|
|
|
// Fire accessible event after short timer, because we need to wait for
|
|
|
|
// DOM attribute & resulting layout to actually change. Otherwise,
|
|
|
|
// assistive technology will retrieve the wrong state/value/selection info.
|
|
|
|
|
2003-04-28 14:24:52 +04:00
|
|
|
// XXX todo
|
2005-01-28 05:35:26 +03:00
|
|
|
// We still need to handle special HTML cases here
|
2003-04-28 14:24:52 +04:00
|
|
|
// For example, if an <img>'s usemap attribute is modified
|
|
|
|
// Otherwise it may just be a state change, for example an object changing
|
2005-01-28 05:35:26 +03:00
|
|
|
// its visibility
|
2009-01-12 20:20:34 +03: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 22:29:51 +03:00
|
|
|
|
2009-09-16 05:01:47 +04:00
|
|
|
// Universal boolean properties that don't require a role. Fire the state
|
|
|
|
// change when disabled or aria-disabled attribute is set.
|
2013-11-20 22:24:30 +04: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.
|
2011-06-04 01:35:17 +04:00
|
|
|
if (aAttribute == nsGkAtoms::disabled ||
|
|
|
|
aAttribute == nsGkAtoms::aria_disabled) {
|
2013-11-20 22:24:30 +04:00
|
|
|
// Do nothing if state wasn't changed (like @aria-disabled was removed but
|
|
|
|
// @disabled is still presented).
|
|
|
|
if (aAccessible->Unavailable() == mStateBitWasOn)
|
|
|
|
return;
|
2009-09-16 05:01:47 +04:00
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> enabledChangeEvent =
|
2013-11-20 22:24:30 +04:00
|
|
|
new AccStateChangeEvent(aAccessible, states::ENABLED, mStateBitWasOn);
|
2012-11-20 08:53:38 +04:00
|
|
|
FireDelayedEvent(enabledChangeEvent);
|
2009-09-16 05:01:47 +04:00
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> sensitiveChangeEvent =
|
2013-11-20 22:24:30 +04:00
|
|
|
new AccStateChangeEvent(aAccessible, states::SENSITIVE, mStateBitWasOn);
|
2012-11-20 08:53:38 +04:00
|
|
|
FireDelayedEvent(sensitiveChangeEvent);
|
2007-04-19 21:49:13 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-09-25 05:19:03 +04:00
|
|
|
// Check for namespaced ARIA attribute
|
2007-12-12 05:10:26 +03:00
|
|
|
if (aNameSpaceID == kNameSpaceID_None) {
|
2007-09-25 05:19:03 +04:00
|
|
|
// Check for hyphenated aria-foo property?
|
2010-03-08 18:45:00 +03:00
|
|
|
if (StringBeginsWith(nsDependentAtomString(aAttribute),
|
|
|
|
NS_LITERAL_STRING("aria-"))) {
|
2012-11-20 08:53:38 +04:00
|
|
|
ARIAAttributeChanged(aAccessible, aAttribute);
|
2007-09-25 05:19:03 +04:00
|
|
|
}
|
|
|
|
}
|
2007-04-19 21:49:13 +04:00
|
|
|
|
2014-04-12 08:03:22 +04:00
|
|
|
// Fire name change and description change events. XXX: it's not complete and
|
|
|
|
// dupes the code logic of accessible name and description calculation, we do
|
|
|
|
// that for performance reasons.
|
|
|
|
if (aAttribute == nsGkAtoms::aria_label) {
|
2012-11-20 08:53:38 +04:00
|
|
|
FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
|
2008-02-20 10:45:14 +03:00
|
|
|
return;
|
|
|
|
}
|
2005-08-10 05:51:39 +04:00
|
|
|
|
2014-04-12 08:03:22 +04:00
|
|
|
if (aAttribute == nsGkAtoms::aria_describedby) {
|
|
|
|
FireDelayedEvent(nsIAccessibleEvent::EVENT_DESCRIPTION_CHANGE, aAccessible);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIContent* elm = aAccessible->GetContent();
|
|
|
|
if (aAttribute == nsGkAtoms::aria_labelledby &&
|
|
|
|
!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_label)) {
|
|
|
|
FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aAttribute == nsGkAtoms::alt &&
|
|
|
|
!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_label) &&
|
|
|
|
!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_labelledby)) {
|
|
|
|
FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aAttribute == nsGkAtoms::title) {
|
|
|
|
if (!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_label) &&
|
|
|
|
!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_labelledby) &&
|
|
|
|
!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::alt)) {
|
|
|
|
FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_describedby))
|
|
|
|
FireDelayedEvent(nsIAccessibleEvent::EVENT_DESCRIPTION_CHANGE, aAccessible);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-06-04 01:35:17 +04:00
|
|
|
if (aAttribute == nsGkAtoms::aria_busy) {
|
2014-04-12 08:03:22 +04:00
|
|
|
bool isOn = elm->AttrValueIs(aNameSpaceID, aAttribute, nsGkAtoms::_true,
|
|
|
|
eCaseMatters);
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> event =
|
2012-11-20 08:53:38 +04:00
|
|
|
new AccStateChangeEvent(aAccessible, states::BUSY, isOn);
|
|
|
|
FireDelayedEvent(event);
|
2011-05-24 18:02:30 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-10-30 01:08:48 +03:00
|
|
|
if (aAttribute == nsGkAtoms::id) {
|
|
|
|
RelocateARIAOwnedIfNeeded(elm);
|
|
|
|
}
|
|
|
|
|
2011-11-01 04:52:27 +04:00
|
|
|
// ARIA or XUL selection
|
2015-03-03 14:08:59 +03:00
|
|
|
if ((aAccessible->GetContent()->IsXULElement() &&
|
|
|
|
aAttribute == nsGkAtoms::selected) ||
|
2011-06-04 01:35:17 +04:00
|
|
|
aAttribute == nsGkAtoms::aria_selected) {
|
2012-05-29 05:18:45 +04:00
|
|
|
Accessible* widget =
|
2012-11-20 08:53:38 +04:00
|
|
|
nsAccUtils::GetSelectableContainer(aAccessible, aAccessible->State());
|
2011-11-01 04:52:27 +04:00
|
|
|
if (widget) {
|
|
|
|
AccSelChangeEvent::SelChangeType selChangeType =
|
2012-11-20 08:53:38 +04:00
|
|
|
elm->AttrValueIs(aNameSpaceID, aAttribute, nsGkAtoms::_true, eCaseMatters) ?
|
2011-11-01 04:52:27 +04:00
|
|
|
AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> event =
|
2012-11-20 08:53:38 +04:00
|
|
|
new AccSelChangeEvent(widget, aAccessible, selChangeType);
|
|
|
|
FireDelayedEvent(event);
|
2005-07-14 18:20:21 +04:00
|
|
|
}
|
2012-11-20 08:53:38 +04:00
|
|
|
|
2011-11-01 04:52:27 +04:00
|
|
|
return;
|
2005-07-14 18:20:21 +04:00
|
|
|
}
|
2007-08-14 20:25:24 +04:00
|
|
|
|
2011-06-04 01:35:17 +04:00
|
|
|
if (aAttribute == nsGkAtoms::contenteditable) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> editableChangeEvent =
|
2012-11-20 08:53:38 +04:00
|
|
|
new AccStateChangeEvent(aAccessible, states::EDITABLE);
|
|
|
|
FireDelayedEvent(editableChangeEvent);
|
2007-08-14 20:25:24 +04:00
|
|
|
return;
|
|
|
|
}
|
2012-10-16 07:05:20 +04:00
|
|
|
|
|
|
|
if (aAttribute == nsGkAtoms::value) {
|
2012-11-20 08:53:38 +04:00
|
|
|
if (aAccessible->IsProgress())
|
|
|
|
FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible);
|
2012-10-16 07:05:20 +04:00
|
|
|
}
|
2007-04-17 08:45:42 +04:00
|
|
|
}
|
|
|
|
|
2012-05-27 13:01:40 +04:00
|
|
|
// DocAccessible protected member
|
2007-04-17 08:45:42 +04:00
|
|
|
void
|
2012-11-20 08:53:38 +04:00
|
|
|
DocAccessible::ARIAAttributeChanged(Accessible* aAccessible, nsIAtom* aAttribute)
|
2007-04-17 08:45:42 +04:00
|
|
|
{
|
2010-04-02 17:33:55 +04:00
|
|
|
// Note: For universal/global ARIA states and properties we don't care if
|
|
|
|
// there is an ARIA role present or not.
|
|
|
|
|
2011-06-04 01:35:17 +04:00
|
|
|
if (aAttribute == nsGkAtoms::aria_required) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> event =
|
2012-11-20 08:53:38 +04:00
|
|
|
new AccStateChangeEvent(aAccessible, states::REQUIRED);
|
|
|
|
FireDelayedEvent(event);
|
2007-04-17 08:45:42 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-06-04 01:35:17 +04:00
|
|
|
if (aAttribute == nsGkAtoms::aria_invalid) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> event =
|
2012-11-20 08:53:38 +04:00
|
|
|
new AccStateChangeEvent(aAccessible, states::INVALID);
|
|
|
|
FireDelayedEvent(event);
|
2007-04-17 08:45:42 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-09-28 05:46:11 +04: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-04 01:35:17 +04:00
|
|
|
if (aAttribute == nsGkAtoms::aria_activedescendant) {
|
2012-11-20 08:53:38 +04:00
|
|
|
mNotificationController->HandleNotification<DocAccessible, Accessible>
|
|
|
|
(this, &DocAccessible::ARIAActiveDescendantChanged, aAccessible);
|
2011-09-28 05:46:11 +04:00
|
|
|
|
2007-04-17 08:45:42 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-04-02 17:33:55 +04:00
|
|
|
// We treat aria-expanded as a global ARIA state for historical reasons
|
2011-06-04 01:35:17 +04:00
|
|
|
if (aAttribute == nsGkAtoms::aria_expanded) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> event =
|
2012-11-20 08:53:38 +04:00
|
|
|
new AccStateChangeEvent(aAccessible, states::EXPANDED);
|
|
|
|
FireDelayedEvent(event);
|
2010-04-02 17:33:55 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-04-24 21:39:03 +04:00
|
|
|
// For aria attributes like drag and drop changes we fire a generic attribute
|
|
|
|
// change event; at least until native API comes up with a more meaningful event.
|
2013-04-25 07:48:26 +04:00
|
|
|
uint8_t attrFlags = aria::AttrCharacteristicsFor(aAttribute);
|
2014-08-28 16:42:06 +04:00
|
|
|
if (!(attrFlags & ATTR_BYPASSOBJ)) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> event =
|
2014-08-28 16:42:06 +04:00
|
|
|
new AccObjectAttrChangedEvent(aAccessible, aAttribute);
|
|
|
|
FireDelayedEvent(event);
|
|
|
|
}
|
2012-04-24 21:39:03 +04:00
|
|
|
|
2012-11-20 08:53:38 +04:00
|
|
|
nsIContent* elm = aAccessible->GetContent();
|
2007-04-17 08:45:42 +04:00
|
|
|
|
2015-02-05 02:33:33 +03:00
|
|
|
// Update aria-hidden flag for the whole subtree iff aria-hidden is changed
|
|
|
|
// on the root, i.e. ignore any affiliated aria-hidden changes in the subtree
|
|
|
|
// of top aria-hidden.
|
|
|
|
if (aAttribute == nsGkAtoms::aria_hidden) {
|
|
|
|
bool isDefined = aria::HasDefinedARIAHidden(elm);
|
|
|
|
if (isDefined != aAccessible->IsARIAHidden() &&
|
|
|
|
!aAccessible->Parent()->IsARIAHidden()) {
|
|
|
|
aAccessible->SetARIAHidden(isDefined);
|
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> event =
|
2015-02-05 02:33:33 +03:00
|
|
|
new AccObjectAttrChangedEvent(aAccessible, aAttribute);
|
|
|
|
FireDelayedEvent(event);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-06-04 01:35:17 +04:00
|
|
|
if (aAttribute == nsGkAtoms::aria_checked ||
|
2014-04-04 12:01:19 +04:00
|
|
|
(aAccessible->IsButton() &&
|
|
|
|
aAttribute == nsGkAtoms::aria_pressed)) {
|
2012-11-20 08:53:38 +04:00
|
|
|
const uint64_t kState = (aAttribute == nsGkAtoms::aria_checked) ?
|
2011-04-10 03:38:06 +04:00
|
|
|
states::CHECKED : states::PRESSED;
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> event = new AccStateChangeEvent(aAccessible, kState);
|
2012-11-20 08:53:38 +04:00
|
|
|
FireDelayedEvent(event);
|
|
|
|
|
|
|
|
bool wasMixed = (mARIAAttrOldValue == nsGkAtoms::mixed);
|
|
|
|
bool isMixed = elm->AttrValueIs(kNameSpaceID_None, aAttribute,
|
|
|
|
nsGkAtoms::mixed, eCaseMatters);
|
|
|
|
if (isMixed != wasMixed) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> event =
|
2012-11-20 08:53:38 +04:00
|
|
|
new AccStateChangeEvent(aAccessible, states::MIXED, isMixed);
|
|
|
|
FireDelayedEvent(event);
|
2007-09-19 01:36:41 +04:00
|
|
|
}
|
2007-08-14 20:15:12 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-06-04 01:35:17 +04:00
|
|
|
if (aAttribute == nsGkAtoms::aria_readonly) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> event =
|
2012-11-20 08:53:38 +04:00
|
|
|
new AccStateChangeEvent(aAccessible, states::READONLY);
|
|
|
|
FireDelayedEvent(event);
|
2007-04-17 08:45:42 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-11-02 23:34:51 +03:00
|
|
|
// Fire text value change event whenever aria-valuetext is changed.
|
|
|
|
if (aAttribute == nsGkAtoms::aria_valuetext) {
|
|
|
|
FireDelayedEvent(nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE, aAccessible);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fire numeric value change event when aria-valuenow is changed and
|
|
|
|
// aria-valuetext is empty
|
|
|
|
if (aAttribute == nsGkAtoms::aria_valuenow &&
|
|
|
|
(!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuetext) ||
|
|
|
|
elm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_valuetext,
|
|
|
|
nsGkAtoms::_empty, eCaseMatters))) {
|
2012-11-20 08:53:38 +04:00
|
|
|
FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, aAccessible);
|
2007-04-17 08:45:42 +04:00
|
|
|
return;
|
2005-01-28 05:35:26 +03:00
|
|
|
}
|
2015-10-30 01:08:48 +03:00
|
|
|
|
|
|
|
if (aAttribute == nsGkAtoms::aria_owns) {
|
|
|
|
mNotificationController->ScheduleRelocation(aAccessible);
|
|
|
|
}
|
2005-06-10 17:57:27 +04:00
|
|
|
}
|
|
|
|
|
2011-09-28 05:46:11 +04:00
|
|
|
void
|
2012-11-20 08:53:38 +04:00
|
|
|
DocAccessible::ARIAActiveDescendantChanged(Accessible* aAccessible)
|
2011-09-28 05:46:11 +04:00
|
|
|
{
|
2012-11-20 08:53:38 +04:00
|
|
|
nsIContent* elm = aAccessible->GetContent();
|
|
|
|
if (elm && aAccessible->IsActiveWidget()) {
|
2011-09-28 05:46:11 +04:00
|
|
|
nsAutoString id;
|
2012-11-20 08:53:38 +04:00
|
|
|
if (elm->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant, id)) {
|
|
|
|
dom::Element* activeDescendantElm = elm->OwnerDoc()->GetElementById(id);
|
2011-09-28 05:46:11 +04:00
|
|
|
if (activeDescendantElm) {
|
2012-05-29 05:18:45 +04:00
|
|
|
Accessible* activeDescendant = GetAccessible(activeDescendantElm);
|
2011-09-28 05:46:11 +04:00
|
|
|
if (activeDescendant) {
|
|
|
|
FocusMgr()->ActiveItemChanged(activeDescendant, false);
|
2012-10-04 13:57:09 +04:00
|
|
|
#ifdef A11Y_LOG
|
2012-09-27 03:53:23 +04:00
|
|
|
if (logging::IsEnabled(logging::eFocus))
|
|
|
|
logging::ActiveItemChangeCausedBy("ARIA activedescedant changed",
|
|
|
|
activeDescendant);
|
|
|
|
#endif
|
2011-09-28 05:46:11 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-27 13:01:40 +04:00
|
|
|
void
|
|
|
|
DocAccessible::ContentAppended(nsIDocument* aDocument,
|
|
|
|
nsIContent* aContainer,
|
|
|
|
nsIContent* aFirstNewContent,
|
2012-08-22 19:56:38 +04:00
|
|
|
int32_t /* unused */)
|
2005-06-10 17:57:27 +04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2012-05-27 13:01:40 +04:00
|
|
|
void
|
|
|
|
DocAccessible::ContentStateChanged(nsIDocument* aDocument,
|
|
|
|
nsIContent* aContent,
|
2014-04-03 08:18:36 +04:00
|
|
|
EventStates aStateMask)
|
2005-07-26 01:40:31 +04:00
|
|
|
{
|
2012-11-20 08:53:38 +04:00
|
|
|
Accessible* accessible = GetAccessible(aContent);
|
|
|
|
if (!accessible)
|
|
|
|
return;
|
|
|
|
|
2010-11-08 16:33:18 +03:00
|
|
|
if (aStateMask.HasState(NS_EVENT_STATE_CHECKED)) {
|
2012-11-20 08:53:38 +04:00
|
|
|
Accessible* widget = accessible->ContainerWidget();
|
|
|
|
if (widget && widget->IsSelect()) {
|
|
|
|
AccSelChangeEvent::SelChangeType selChangeType =
|
|
|
|
aContent->AsElement()->State().HasState(NS_EVENT_STATE_CHECKED) ?
|
|
|
|
AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> event =
|
2012-11-20 08:53:38 +04:00
|
|
|
new AccSelChangeEvent(widget, accessible, selChangeType);
|
|
|
|
FireDelayedEvent(event);
|
2013-08-02 20:42:36 +04:00
|
|
|
return;
|
2011-11-01 04:52:27 +04:00
|
|
|
}
|
2013-08-02 20:42:36 +04:00
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> event =
|
2013-08-02 20:42:36 +04:00
|
|
|
new AccStateChangeEvent(accessible, states::CHECKED,
|
|
|
|
aContent->AsElement()->State().HasState(NS_EVENT_STATE_CHECKED));
|
|
|
|
FireDelayedEvent(event);
|
2005-07-26 01:40:31 +04:00
|
|
|
}
|
|
|
|
|
2010-11-08 16:33:18 +03:00
|
|
|
if (aStateMask.HasState(NS_EVENT_STATE_INVALID)) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> event =
|
2012-11-20 08:53:38 +04:00
|
|
|
new AccStateChangeEvent(accessible, states::INVALID, true);
|
|
|
|
FireDelayedEvent(event);
|
2012-11-09 21:37:27 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> event =
|
2012-11-20 08:53:38 +04:00
|
|
|
new AccStateChangeEvent(accessible, states::TRAVERSED, true);
|
|
|
|
FireDelayedEvent(event);
|
2012-11-09 21:37:27 +04:00
|
|
|
}
|
2005-07-26 01:40:31 +04:00
|
|
|
}
|
|
|
|
|
2012-05-27 13:01:40 +04:00
|
|
|
void
|
|
|
|
DocAccessible::DocumentStatesChanged(nsIDocument* aDocument,
|
2014-04-03 08:18:36 +04:00
|
|
|
EventStates aStateMask)
|
2010-03-17 20:10:57 +03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2012-05-27 13:01:40 +04:00
|
|
|
void
|
|
|
|
DocAccessible::CharacterDataWillChange(nsIDocument* aDocument,
|
|
|
|
nsIContent* aContent,
|
|
|
|
CharacterDataChangeInfo* aInfo)
|
2007-09-05 12:22:17 +04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2012-05-27 13:01:40 +04:00
|
|
|
void
|
|
|
|
DocAccessible::CharacterDataChanged(nsIDocument* aDocument,
|
|
|
|
nsIContent* aContent,
|
|
|
|
CharacterDataChangeInfo* aInfo)
|
2005-06-10 17:57:27 +04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::ContentInserted(nsIDocument* aDocument, nsIContent* aContainer,
|
2012-08-22 19:56:38 +04:00
|
|
|
nsIContent* aChild, int32_t /* unused */)
|
2005-06-10 17:57:27 +04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::ContentRemoved(nsIDocument* aDocument, nsIContent* aContainer,
|
2012-08-22 19:56:38 +04:00
|
|
|
nsIContent* aChild, int32_t /* unused */,
|
2012-05-27 13:01:40 +04:00
|
|
|
nsIContent* aPreviousSibling)
|
2005-06-10 17:57:27 +04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2007-03-10 16:49:43 +03:00
|
|
|
void
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::ParentChainChanged(nsIContent* aContent)
|
2007-03-10 16:49:43 +03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2009-12-10 22:12:19 +03:00
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2012-05-29 05:18:45 +04:00
|
|
|
// Accessible
|
2009-12-10 22:12:19 +03:00
|
|
|
|
2012-10-04 13:57:09 +04:00
|
|
|
#ifdef A11Y_LOG
|
2010-06-08 20:39:58 +04:00
|
|
|
nsresult
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::HandleAccEvent(AccEvent* aEvent)
|
2010-06-08 20:39:58 +04:00
|
|
|
{
|
2012-05-23 13:21:40 +04:00
|
|
|
if (logging::IsEnabled(logging::eDocLoad))
|
|
|
|
logging::DocLoadEventHandled(aEvent);
|
2009-12-10 22:12:19 +03:00
|
|
|
|
2012-05-31 12:04:41 +04:00
|
|
|
return HyperTextAccessible::HandleAccEvent(aEvent);
|
2009-12-10 22:12:19 +03:00
|
|
|
}
|
2010-06-08 20:39:58 +04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Public members
|
2009-12-10 22:12:19 +03:00
|
|
|
|
2010-09-17 07:23:17 +04:00
|
|
|
void*
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::GetNativeWindow() const
|
2010-09-17 07:23:17 +04:00
|
|
|
{
|
2012-02-08 02:38:54 +04:00
|
|
|
if (!mPresShell)
|
2012-07-30 18:20:58 +04:00
|
|
|
return nullptr;
|
2012-01-10 22:52:14 +04:00
|
|
|
|
2013-01-05 07:12:24 +04:00
|
|
|
nsViewManager* vm = mPresShell->GetViewManager();
|
2012-01-10 22:52:14 +04:00
|
|
|
if (!vm)
|
2012-07-30 18:20:58 +04:00
|
|
|
return nullptr;
|
2012-01-10 22:52:14 +04:00
|
|
|
|
|
|
|
nsCOMPtr<nsIWidget> widget;
|
|
|
|
vm->GetRootWidget(getter_AddRefs(widget));
|
|
|
|
if (widget)
|
|
|
|
return widget->GetNativeData(NS_NATIVE_WINDOW);
|
|
|
|
|
2012-07-30 18:20:58 +04:00
|
|
|
return nullptr;
|
2010-09-17 07:23:17 +04:00
|
|
|
}
|
|
|
|
|
2012-05-29 05:18:45 +04:00
|
|
|
Accessible*
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::GetAccessibleByUniqueIDInSubtree(void* aUniqueID)
|
2010-09-09 18:44:56 +04:00
|
|
|
{
|
2012-05-29 05:18:45 +04:00
|
|
|
Accessible* child = GetAccessibleByUniqueID(aUniqueID);
|
2010-09-09 18:44:56 +04:00
|
|
|
if (child)
|
|
|
|
return child;
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
uint32_t childDocCount = mChildDocuments.Length();
|
|
|
|
for (uint32_t childDocIdx= 0; childDocIdx < childDocCount; childDocIdx++) {
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible* childDocument = mChildDocuments.ElementAt(childDocIdx);
|
2011-01-28 07:37:08 +03:00
|
|
|
child = childDocument->GetAccessibleByUniqueIDInSubtree(aUniqueID);
|
2010-09-09 18:44:56 +04:00
|
|
|
if (child)
|
|
|
|
return child;
|
|
|
|
}
|
|
|
|
|
2012-07-30 18:20:58 +04:00
|
|
|
return nullptr;
|
2010-09-09 18:44:56 +04:00
|
|
|
}
|
2009-12-10 22:12:19 +03:00
|
|
|
|
2012-05-29 05:18:45 +04:00
|
|
|
Accessible*
|
2013-07-22 19:58:19 +04:00
|
|
|
DocAccessible::GetAccessibleOrContainer(nsINode* aNode) const
|
2011-01-28 07:37:38 +03:00
|
|
|
{
|
2014-06-20 22:54:59 +04:00
|
|
|
if (!aNode || !aNode->GetCrossShadowCurrentDoc())
|
2012-07-30 18:20:58 +04:00
|
|
|
return nullptr;
|
2011-01-28 07:37:38 +03:00
|
|
|
|
|
|
|
nsINode* currNode = aNode;
|
2012-07-30 18:20:58 +04:00
|
|
|
Accessible* accessible = nullptr;
|
2014-08-23 00:10:57 +04:00
|
|
|
while (!(accessible = GetAccessible(currNode))) {
|
|
|
|
nsINode* parent = nullptr;
|
|
|
|
|
|
|
|
// If this is a content node, try to get a flattened parent content node.
|
|
|
|
// This will smartly skip from the shadow root to the host element,
|
|
|
|
// over parentless document fragment
|
|
|
|
if (currNode->IsContent())
|
|
|
|
parent = currNode->AsContent()->GetFlattenedTreeParent();
|
|
|
|
|
|
|
|
// Fallback to just get parent node, in case there is no parent content
|
|
|
|
// node. Or current node is not a content node.
|
|
|
|
if (!parent)
|
|
|
|
parent = currNode->GetParentNode();
|
|
|
|
|
|
|
|
if (!(currNode = parent)) break;
|
|
|
|
}
|
2011-01-28 07:37:38 +03:00
|
|
|
|
|
|
|
return accessible;
|
|
|
|
}
|
|
|
|
|
2013-07-22 19:58:19 +04:00
|
|
|
Accessible*
|
|
|
|
DocAccessible::GetAccessibleOrDescendant(nsINode* aNode) const
|
|
|
|
{
|
|
|
|
Accessible* acc = GetAccessible(aNode);
|
|
|
|
if (acc)
|
|
|
|
return acc;
|
|
|
|
|
|
|
|
acc = GetContainerAccessible(aNode);
|
|
|
|
if (acc) {
|
|
|
|
uint32_t childCnt = acc->ChildCount();
|
|
|
|
for (uint32_t idx = 0; idx < childCnt; idx++) {
|
|
|
|
Accessible* child = acc->GetChildAt(idx);
|
|
|
|
for (nsIContent* elm = child->GetContent();
|
|
|
|
elm && elm != acc->GetContent();
|
|
|
|
elm = elm->GetFlattenedTreeParent()) {
|
|
|
|
if (elm == aNode)
|
|
|
|
return child;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2013-09-06 23:27:07 +04:00
|
|
|
void
|
2012-05-29 05:18:45 +04:00
|
|
|
DocAccessible::BindToDocument(Accessible* aAccessible,
|
2012-05-27 13:01:40 +04:00
|
|
|
nsRoleMapEntry* aRoleMapEntry)
|
2010-11-12 22:00:55 +03:00
|
|
|
{
|
|
|
|
// Put into DOM node cache.
|
2012-10-13 10:34:21 +04:00
|
|
|
if (aAccessible->IsNodeMapEntry())
|
2012-05-18 21:30:49 +04:00
|
|
|
mNodeToAccessibleMap.Put(aAccessible->GetNode(), aAccessible);
|
2010-11-12 22:00:55 +03:00
|
|
|
|
|
|
|
// Put into unique ID cache.
|
2012-05-18 21:30:49 +04:00
|
|
|
mAccessibleCache.Put(aAccessible->UniqueID(), aAccessible);
|
2010-11-12 22:00:55 +03:00
|
|
|
|
|
|
|
aAccessible->SetRoleMapEntry(aRoleMapEntry);
|
2012-12-28 00:54:28 +04:00
|
|
|
|
2015-09-15 19:01:51 +03:00
|
|
|
AddDependentIDsFor(aAccessible);
|
2015-10-30 01:08:48 +03:00
|
|
|
|
|
|
|
if (aAccessible->HasOwnContent()) {
|
|
|
|
nsIContent* el = aAccessible->GetContent();
|
|
|
|
if (el->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_owns)) {
|
|
|
|
mNotificationController->ScheduleRelocation(aAccessible);
|
|
|
|
}
|
|
|
|
|
|
|
|
RelocateARIAOwnedIfNeeded(el);
|
|
|
|
}
|
2010-11-12 22:00:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2012-05-29 05:18:45 +04:00
|
|
|
DocAccessible::UnbindFromDocument(Accessible* aAccessible)
|
2010-11-12 22:00:55 +03:00
|
|
|
{
|
2010-11-20 05:37:40 +03:00
|
|
|
NS_ASSERTION(mAccessibleCache.GetWeak(aAccessible->UniqueID()),
|
|
|
|
"Unbinding the unbound accessible!");
|
|
|
|
|
2011-09-28 05:46:11 +04:00
|
|
|
// Fire focus event on accessible having DOM focus if active item was removed
|
|
|
|
// from the tree.
|
|
|
|
if (FocusMgr()->IsActiveItem(aAccessible)) {
|
2012-07-30 18:20:58 +04:00
|
|
|
FocusMgr()->ActiveItemChanged(nullptr);
|
2012-10-04 13:57:09 +04:00
|
|
|
#ifdef A11Y_LOG
|
2012-09-27 03:53:23 +04:00
|
|
|
if (logging::IsEnabled(logging::eFocus))
|
|
|
|
logging::ActiveItemChangeCausedBy("tree shutdown", aAccessible);
|
|
|
|
#endif
|
2011-09-28 05:46:11 +04:00
|
|
|
}
|
|
|
|
|
2010-11-12 22:01:04 +03:00
|
|
|
// Remove an accessible from node-to-accessible map if it exists there.
|
2012-10-13 10:34:21 +04:00
|
|
|
if (aAccessible->IsNodeMapEntry() &&
|
2010-11-12 22:00:55 +03:00
|
|
|
mNodeToAccessibleMap.Get(aAccessible->GetNode()) == aAccessible)
|
|
|
|
mNodeToAccessibleMap.Remove(aAccessible->GetNode());
|
|
|
|
|
2014-10-22 04:49:28 +04:00
|
|
|
// Update XPCOM part.
|
|
|
|
xpcAccessibleDocument* xpcDoc = GetAccService()->GetCachedXPCDocument(this);
|
|
|
|
if (xpcDoc)
|
|
|
|
xpcDoc->NotifyOfShutdown(aAccessible);
|
|
|
|
|
2010-11-12 22:00:55 +03:00
|
|
|
void* uniqueID = aAccessible->UniqueID();
|
2010-11-20 05:37:40 +03:00
|
|
|
|
|
|
|
NS_ASSERTION(!aAccessible->IsDefunct(), "Shutdown the shutdown accessible!");
|
2010-11-12 22:00:55 +03:00
|
|
|
aAccessible->Shutdown();
|
2010-11-20 05:37:40 +03:00
|
|
|
|
2010-11-12 22:00:55 +03:00
|
|
|
mAccessibleCache.Remove(uniqueID);
|
|
|
|
}
|
|
|
|
|
2010-10-21 08:16:10 +04:00
|
|
|
void
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::ContentInserted(nsIContent* aContainerNode,
|
|
|
|
nsIContent* aStartChildNode,
|
|
|
|
nsIContent* aEndChildNode)
|
2011-01-18 11:03:38 +03:00
|
|
|
{
|
2011-08-08 11:55:36 +04: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 11:03:38 +03:00
|
|
|
// Update the whole tree of this document accessible when the container is
|
|
|
|
// null (document element is inserted or removed).
|
2012-05-29 05:18:45 +04:00
|
|
|
Accessible* container = aContainerNode ?
|
2011-01-28 07:37:38 +03:00
|
|
|
GetAccessibleOrContainer(aContainerNode) : this;
|
2015-03-27 21:16:53 +03:00
|
|
|
if (container) {
|
|
|
|
// Ignore notification if the container node is no longer in the DOM tree.
|
|
|
|
mNotificationController->ScheduleContentInsertion(container,
|
|
|
|
aStartChildNode,
|
|
|
|
aEndChildNode);
|
|
|
|
}
|
2010-10-21 08:16:10 +04:00
|
|
|
}
|
2011-01-18 11:03:38 +03:00
|
|
|
}
|
2010-10-21 08:16:10 +04:00
|
|
|
|
|
|
|
void
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::RecreateAccessible(nsIContent* aContent)
|
2010-10-21 08:16:10 +04:00
|
|
|
{
|
2012-10-10 04:14:44 +04:00
|
|
|
#ifdef A11Y_LOG
|
|
|
|
if (logging::IsEnabled(logging::eTree)) {
|
|
|
|
logging::MsgBegin("TREE", "accessible recreated");
|
|
|
|
logging::Node("content", aContent);
|
|
|
|
logging::MsgEnd();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-02-01 06:00:06 +03: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-21 08:16:10 +04:00
|
|
|
|
2013-05-02 02:50:08 +04:00
|
|
|
nsIContent* parent = aContent->GetFlattenedTreeParent();
|
|
|
|
ContentRemoved(parent, aContent);
|
|
|
|
ContentInserted(parent, aContent, aContent->GetNextSibling());
|
2010-10-21 08:16:10 +04:00
|
|
|
}
|
|
|
|
|
2010-11-19 08:44:47 +03:00
|
|
|
void
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::ProcessInvalidationList()
|
2010-11-19 08:44:47 +03:00
|
|
|
{
|
2011-07-25 07:15:37 +04:00
|
|
|
// Invalidate children of container accessible for each element in
|
|
|
|
// invalidation list. Allow invalidation list insertions while container
|
|
|
|
// children are recached.
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t idx = 0; idx < mInvalidationList.Length(); idx++) {
|
2011-07-25 07:15:37 +04:00
|
|
|
nsIContent* content = mInvalidationList[idx];
|
2014-12-30 23:43:49 +03:00
|
|
|
if (!HasAccessible(content)) {
|
2012-05-29 05:18:45 +04:00
|
|
|
Accessible* container = GetContainerAccessible(content);
|
2014-12-30 23:43:49 +03:00
|
|
|
if (container)
|
|
|
|
UpdateTreeOnInsertion(container);
|
2014-09-02 22:54:04 +04:00
|
|
|
}
|
2010-11-19 08:44:47 +03:00
|
|
|
}
|
2011-07-25 07:15:37 +04:00
|
|
|
|
|
|
|
mInvalidationList.Clear();
|
2010-11-19 08:44:47 +03:00
|
|
|
}
|
|
|
|
|
2014-03-06 04:36:56 +04:00
|
|
|
Accessible*
|
|
|
|
DocAccessible::GetAccessibleEvenIfNotInMap(nsINode* aNode) const
|
|
|
|
{
|
2015-03-03 14:08:59 +03:00
|
|
|
if (!aNode->IsContent() || !aNode->AsContent()->IsHTMLElement(nsGkAtoms::area))
|
2014-03-06 04:36:56 +04:00
|
|
|
return GetAccessible(aNode);
|
|
|
|
|
|
|
|
// XXX Bug 135040, incorrect when multiple images use the same map.
|
|
|
|
nsIFrame* frame = aNode->AsContent()->GetPrimaryFrame();
|
|
|
|
nsImageFrame* imageFrame = do_QueryFrame(frame);
|
|
|
|
if (imageFrame) {
|
|
|
|
Accessible* parent = GetAccessible(imageFrame->GetContent());
|
|
|
|
if (parent) {
|
|
|
|
Accessible* area =
|
|
|
|
parent->AsImageMap()->GetChildAccessibleFor(aNode);
|
|
|
|
if (area)
|
|
|
|
return area;
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return GetAccessible(aNode);
|
|
|
|
}
|
|
|
|
|
2010-11-21 04:00:29 +03:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2012-05-29 05:18:45 +04:00
|
|
|
// Accessible protected
|
2010-11-21 04:00:29 +03:00
|
|
|
|
|
|
|
void
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::CacheChildren()
|
2010-11-21 04:00:29 +03:00
|
|
|
{
|
|
|
|
// Search for accessible children starting from the document element since
|
|
|
|
// some web pages tend to insert elements under it rather than document body.
|
2013-03-04 17:41:51 +04:00
|
|
|
dom::Element* rootElm = mDocumentNode->GetRootElement();
|
|
|
|
if (!rootElm)
|
|
|
|
return;
|
|
|
|
|
2013-12-09 19:43:51 +04:00
|
|
|
// Ignore last HTML:br, copied from HyperTextAccessible.
|
2013-03-04 17:41:51 +04:00
|
|
|
TreeWalker walker(this, rootElm);
|
2013-12-09 19:43:51 +04:00
|
|
|
Accessible* lastChild = nullptr;
|
|
|
|
while (Accessible* child = walker.NextChild()) {
|
|
|
|
if (lastChild)
|
|
|
|
AppendChild(lastChild);
|
2010-11-21 04:00:29 +03:00
|
|
|
|
2013-12-09 19:43:51 +04:00
|
|
|
lastChild = child;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lastChild) {
|
|
|
|
if (lastChild->IsHTMLBr())
|
|
|
|
Document()->UnbindFromDocument(lastChild);
|
|
|
|
else
|
|
|
|
AppendChild(lastChild);
|
|
|
|
}
|
2010-11-21 04:00:29 +03:00
|
|
|
}
|
|
|
|
|
2009-12-10 22:12:19 +03:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Protected members
|
|
|
|
|
2011-03-02 17:41:42 +03:00
|
|
|
void
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::NotifyOfLoading(bool aIsReloading)
|
2011-08-08 11:55:36 +04:00
|
|
|
{
|
|
|
|
// 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.
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> reloadEvent =
|
2011-08-08 11:55:36 +04:00
|
|
|
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.
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> stateEvent =
|
2012-11-20 08:53:38 +04:00
|
|
|
new AccStateChangeEvent(this, states::BUSY, true);
|
|
|
|
FireDelayedEvent(stateEvent);
|
2011-08-08 11:55:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::DoInitialUpdate()
|
2011-03-02 17:41:42 +03:00
|
|
|
{
|
2012-12-29 20:33:59 +04:00
|
|
|
if (nsCoreUtils::IsTabDocument(mDocumentNode))
|
|
|
|
mDocFlags |= eTabDocument;
|
|
|
|
|
2011-08-08 11:55:36 +04:00
|
|
|
mLoadState |= eTreeConstructed;
|
|
|
|
|
2011-03-03 09:41:46 +03: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.
|
2012-11-23 02:15:25 +04:00
|
|
|
nsIContent* contentElm = nsCoreUtils::GetRoleContent(mDocumentNode);
|
2012-12-24 16:40:15 +04:00
|
|
|
if (mContent != contentElm) {
|
2011-03-03 09:41:46 +03:00
|
|
|
mContent = contentElm;
|
2012-12-24 16:40:15 +04:00
|
|
|
SetRoleMapEntry(aria::GetRoleMap(mContent));
|
|
|
|
}
|
2011-03-03 09:41:46 +03:00
|
|
|
|
2014-09-02 22:54:04 +04:00
|
|
|
// Build initial tree. Since its the initial tree there's no group info to
|
|
|
|
// invalidate.
|
|
|
|
AutoTreeMutation mut(this, false);
|
2011-03-03 09:41:46 +03:00
|
|
|
CacheChildrenInSubtree(this);
|
2011-07-19 12:30:32 +04: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()) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(Parent());
|
2012-11-20 08:53:38 +04:00
|
|
|
ParentDocument()->FireDelayedEvent(reorderEvent);
|
2011-07-19 12:30:32 +04:00
|
|
|
}
|
2014-03-08 01:35:19 +04:00
|
|
|
|
|
|
|
uint32_t childCount = ChildCount();
|
|
|
|
for (uint32_t i = 0; i < childCount; i++) {
|
|
|
|
Accessible* child = GetChildAt(i);
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccShowEvent> event = new AccShowEvent(child, child->GetContent());
|
2014-03-08 01:35:19 +04:00
|
|
|
FireDelayedEvent(event);
|
|
|
|
}
|
2011-03-02 17:41:42 +03:00
|
|
|
}
|
|
|
|
|
2011-08-08 11:55:36 +04:00
|
|
|
void
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::ProcessLoad()
|
2011-08-08 11:55:36 +04:00
|
|
|
{
|
|
|
|
mLoadState |= eCompletelyLoaded;
|
|
|
|
|
2012-10-10 05:01:46 +04:00
|
|
|
#ifdef A11Y_LOG
|
|
|
|
if (logging::IsEnabled(logging::eDocLoad))
|
|
|
|
logging::DocCompleteLoad(this, IsLoadEventTarget());
|
|
|
|
#endif
|
|
|
|
|
2011-08-08 11:55:36 +04:00
|
|
|
// 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) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> loadEvent = new AccEvent(mLoadEventType, this);
|
2013-03-18 09:58:34 +04:00
|
|
|
FireDelayedEvent(loadEvent);
|
2011-08-08 11:55:36 +04:00
|
|
|
|
|
|
|
mLoadEventType = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fire busy state change event.
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccEvent> stateEvent =
|
2011-10-17 18:59:28 +04:00
|
|
|
new AccStateChangeEvent(this, states::BUSY, false);
|
2013-03-18 09:58:34 +04:00
|
|
|
FireDelayedEvent(stateEvent);
|
2011-08-08 11:55:36 +04:00
|
|
|
}
|
|
|
|
|
2010-11-18 05:55:44 +03:00
|
|
|
void
|
2015-09-15 19:01:51 +03:00
|
|
|
DocAccessible::AddDependentIDsFor(Accessible* aRelProvider, nsIAtom* aRelAttr)
|
2010-11-18 05:55:44 +03:00
|
|
|
{
|
2015-09-15 19:01:51 +03:00
|
|
|
dom::Element* relProviderEl = aRelProvider->Elm();
|
|
|
|
if (!relProviderEl)
|
|
|
|
return;
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t idx = 0; idx < kRelationAttrsLen; idx++) {
|
2010-11-18 05:55:44 +03:00
|
|
|
nsIAtom* relAttr = *kRelationAttrs[idx];
|
|
|
|
if (aRelAttr && aRelAttr != relAttr)
|
|
|
|
continue;
|
|
|
|
|
2011-06-04 01:35:17 +04:00
|
|
|
if (relAttr == nsGkAtoms::_for) {
|
2015-09-15 19:01:51 +03:00
|
|
|
if (!relProviderEl->IsAnyOfHTMLElements(nsGkAtoms::label,
|
|
|
|
nsGkAtoms::output))
|
2010-11-20 05:37:18 +03:00
|
|
|
continue;
|
|
|
|
|
2011-06-04 01:35:17 +04:00
|
|
|
} else if (relAttr == nsGkAtoms::control) {
|
2015-09-15 19:01:51 +03:00
|
|
|
if (!relProviderEl->IsAnyOfXULElements(nsGkAtoms::label,
|
|
|
|
nsGkAtoms::description))
|
2010-11-20 05:37:18 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-09-15 19:01:51 +03:00
|
|
|
IDRefsIterator iter(this, relProviderEl, relAttr);
|
2010-11-18 05:55:44 +03:00
|
|
|
while (true) {
|
|
|
|
const nsDependentSubstring id = iter.NextID();
|
|
|
|
if (id.IsEmpty())
|
|
|
|
break;
|
|
|
|
|
|
|
|
AttrRelProviderArray* providers = mDependentIDsHash.Get(id);
|
|
|
|
if (!providers) {
|
|
|
|
providers = new AttrRelProviderArray();
|
|
|
|
if (providers) {
|
2012-05-18 21:30:49 +04:00
|
|
|
mDependentIDsHash.Put(id, providers);
|
2010-11-18 05:55:44 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (providers) {
|
|
|
|
AttrRelProvider* provider =
|
2015-09-15 19:01:51 +03:00
|
|
|
new AttrRelProvider(relAttr, relProviderEl);
|
2010-11-19 08:44:47 +03:00
|
|
|
if (provider) {
|
2010-11-18 05:55:44 +03:00
|
|
|
providers->AppendElement(provider);
|
2010-11-19 08:44:47 +03: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);
|
2015-09-15 19:01:51 +03:00
|
|
|
if (dependentContent) {
|
|
|
|
if (!HasAccessible(dependentContent)) {
|
|
|
|
mInvalidationList.AppendElement(dependentContent);
|
|
|
|
}
|
2010-11-19 08:44:47 +03:00
|
|
|
}
|
|
|
|
}
|
2010-11-18 05:55:44 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the relation attribute is given then we don't have anything else to
|
|
|
|
// check.
|
|
|
|
if (aRelAttr)
|
|
|
|
break;
|
|
|
|
}
|
2015-09-15 19:01:51 +03:00
|
|
|
|
|
|
|
// Make sure to schedule the tree update if needed.
|
|
|
|
mNotificationController->ScheduleProcessing();
|
2010-11-18 05:55:44 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2015-09-15 19:01:51 +03:00
|
|
|
DocAccessible::RemoveDependentIDsFor(Accessible* aRelProvider,
|
2012-05-27 13:01:40 +04:00
|
|
|
nsIAtom* aRelAttr)
|
2010-11-18 05:55:44 +03:00
|
|
|
{
|
2015-09-15 19:01:51 +03:00
|
|
|
dom::Element* relProviderElm = aRelProvider->Elm();
|
|
|
|
if (!relProviderElm)
|
|
|
|
return;
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t idx = 0; idx < kRelationAttrsLen; idx++) {
|
2010-11-18 05:55:44 +03:00
|
|
|
nsIAtom* relAttr = *kRelationAttrs[idx];
|
|
|
|
if (aRelAttr && aRelAttr != *kRelationAttrs[idx])
|
|
|
|
continue;
|
|
|
|
|
2015-09-15 19:01:51 +03:00
|
|
|
IDRefsIterator iter(this, relProviderElm, relAttr);
|
2010-11-18 05:55:44 +03:00
|
|
|
while (true) {
|
|
|
|
const nsDependentSubstring id = iter.NextID();
|
|
|
|
if (id.IsEmpty())
|
|
|
|
break;
|
|
|
|
|
|
|
|
AttrRelProviderArray* providers = mDependentIDsHash.Get(id);
|
|
|
|
if (providers) {
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t jdx = 0; jdx < providers->Length(); ) {
|
2010-11-18 05:55:44 +03:00
|
|
|
AttrRelProvider* provider = (*providers)[jdx];
|
|
|
|
if (provider->mRelAttr == relAttr &&
|
2015-09-15 19:01:51 +03:00
|
|
|
provider->mContent == relProviderElm)
|
2010-11-18 05:55:44 +03:00
|
|
|
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 21:33:00 +03:00
|
|
|
bool
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
|
|
|
|
nsIAtom* aAttribute)
|
2010-12-18 21:33:00 +03:00
|
|
|
{
|
2011-06-04 01:35:17 +04:00
|
|
|
if (aAttribute == nsGkAtoms::role) {
|
2010-12-18 21:33:00 +03: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) {
|
2012-04-16 13:24:23 +04:00
|
|
|
SetRoleMapEntry(aria::GetRoleMap(aElement));
|
2010-12-18 21:33:00 +03:00
|
|
|
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).
|
2012-03-23 05:49:55 +04:00
|
|
|
RecreateAccessible(aElement);
|
2011-01-19 11:03:12 +03:00
|
|
|
|
2010-12-18 21:33:00 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-08-05 06:35:54 +03:00
|
|
|
if (aAttribute == nsGkAtoms::href) {
|
2011-02-01 06:00:06 +03: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.
|
|
|
|
|
2012-03-23 05:49:55 +04:00
|
|
|
// Make sure the accessible is recreated asynchronously to allow the content
|
|
|
|
// to handle the attribute change.
|
|
|
|
RecreateAccessible(aElement);
|
2010-12-18 21:33:00 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-06-04 01:35:17 +04:00
|
|
|
if (aAttribute == nsGkAtoms::aria_multiselectable &&
|
|
|
|
aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) {
|
2010-12-18 21:33:00 +03: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.
|
2012-03-23 05:49:55 +04:00
|
|
|
RecreateAccessible(aElement);
|
2011-01-19 11:03:12 +03:00
|
|
|
|
2010-12-18 21:33:00 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-01-18 11:03:38 +03:00
|
|
|
void
|
2012-05-29 05:18:45 +04:00
|
|
|
DocAccessible::ProcessContentInserted(Accessible* aContainer,
|
2012-05-27 13:01:40 +04:00
|
|
|
const nsTArray<nsCOMPtr<nsIContent> >* aInsertedContent)
|
2011-01-18 11:03:38 +03:00
|
|
|
{
|
2012-11-13 10:29:22 +04:00
|
|
|
// Process insertions if the container accessible is still in tree.
|
2011-01-28 07:37:08 +03:00
|
|
|
if (!HasAccessible(aContainer->GetNode()))
|
2011-01-18 11:03:38 +03:00
|
|
|
return;
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
for (uint32_t idx = 0; idx < aInsertedContent->Length(); idx++) {
|
2012-11-13 10:29:22 +04: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). To avoid a double
|
|
|
|
// processing of the content insertion ignore this insertion notification.
|
|
|
|
// Note, the inserted content might be not in tree at all at this point what
|
|
|
|
// means there's no container. Ignore the insertion too.
|
|
|
|
|
2014-12-30 23:43:49 +03:00
|
|
|
Accessible* container =
|
2011-01-28 07:37:38 +03:00
|
|
|
GetContainerAccessible(aInsertedContent->ElementAt(idx));
|
2014-12-30 23:43:49 +03:00
|
|
|
if (container != aContainer)
|
2012-11-13 10:29:22 +04:00
|
|
|
continue;
|
|
|
|
|
2014-12-30 23:43:49 +03:00
|
|
|
if (container == this) {
|
|
|
|
// If new root content has been inserted then update it.
|
|
|
|
nsIContent* rootContent = nsCoreUtils::GetRoleContent(mDocumentNode);
|
|
|
|
if (rootContent != mContent) {
|
|
|
|
mContent = rootContent;
|
|
|
|
SetRoleMapEntry(aria::GetRoleMap(mContent));
|
2012-11-13 10:29:22 +04:00
|
|
|
}
|
|
|
|
|
2014-12-30 23:43:49 +03:00
|
|
|
// 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.
|
2012-11-13 10:29:22 +04:00
|
|
|
}
|
|
|
|
|
2014-12-30 23:43:49 +03:00
|
|
|
// HTML comboboxes have no-content list accessible as an intermidiate
|
|
|
|
// containing all options.
|
|
|
|
if (container->IsHTMLCombobox())
|
|
|
|
container = container->FirstChild();
|
|
|
|
|
|
|
|
// We have a DOM/layout change under the container accessible, and its tree
|
|
|
|
// might need an update. Since DOM/layout change of the element may affect
|
|
|
|
// on the accessibleness of adjacent elements (for example, insertion of
|
|
|
|
// extra HTML:body make the old body accessible) then we have to recache
|
|
|
|
// children of the container, and then fire show/hide events for a change.
|
|
|
|
UpdateTreeOnInsertion(container);
|
|
|
|
break;
|
2011-01-19 19:01:31 +03:00
|
|
|
}
|
2011-01-18 11:03:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2014-12-30 23:43:49 +03:00
|
|
|
DocAccessible::UpdateTreeOnInsertion(Accessible* aContainer)
|
2011-01-18 11:03:38 +03:00
|
|
|
{
|
2014-12-30 23:43:49 +03:00
|
|
|
for (uint32_t idx = 0; idx < aContainer->ContentChildCount(); idx++) {
|
|
|
|
Accessible* child = aContainer->ContentChildAt(idx);
|
|
|
|
child->SetSurvivingInUpdate(true);
|
|
|
|
}
|
2012-06-01 08:27:25 +04:00
|
|
|
|
2014-09-02 22:54:04 +04:00
|
|
|
AutoTreeMutation mut(aContainer);
|
2014-12-30 23:43:49 +03:00
|
|
|
aContainer->InvalidateChildren();
|
|
|
|
aContainer->EnsureChildren();
|
2012-11-13 10:29:22 +04:00
|
|
|
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(aContainer);
|
2013-09-20 20:02:25 +04:00
|
|
|
|
2014-12-30 23:43:49 +03:00
|
|
|
uint32_t updateFlags = eNoAccessible;
|
|
|
|
for (uint32_t idx = 0; idx < aContainer->ContentChildCount(); idx++) {
|
|
|
|
Accessible* child = aContainer->ContentChildAt(idx);
|
|
|
|
if (child->IsSurvivingInUpdate()) {
|
|
|
|
child->SetSurvivingInUpdate(false);
|
|
|
|
continue;
|
|
|
|
}
|
2013-03-26 03:46:22 +04:00
|
|
|
|
2014-12-30 23:43:49 +03:00
|
|
|
// A new child has been created, update its tree.
|
|
|
|
#ifdef A11Y_LOG
|
|
|
|
if (logging::IsEnabled(logging::eTree)) {
|
|
|
|
logging::MsgBegin("TREE", "process content insertion");
|
|
|
|
logging::Node("container", aContainer->GetNode());
|
|
|
|
logging::Node("child", child->GetContent());
|
|
|
|
logging::Address("child", child);
|
|
|
|
logging::MsgEnd();
|
2013-03-26 03:46:22 +04:00
|
|
|
}
|
2014-12-30 23:43:49 +03:00
|
|
|
#endif
|
|
|
|
|
|
|
|
updateFlags |= UpdateTreeInternal(child, true, reorderEvent);
|
2011-04-07 09:17:29 +04:00
|
|
|
}
|
2011-01-18 11:03:38 +03: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.
|
2014-12-30 23:43:49 +03:00
|
|
|
if (!(updateFlags & eAlertAccessible)) {
|
2011-01-18 11:03:38 +03:00
|
|
|
// XXX: tree traversal is perf issue, accessible should know if they are
|
|
|
|
// children of alert accessible to avoid this.
|
2012-05-29 05:18:45 +04:00
|
|
|
Accessible* ancestor = aContainer;
|
2011-01-18 11:03:38 +03:00
|
|
|
while (ancestor) {
|
2012-01-12 07:07:35 +04:00
|
|
|
if (ancestor->ARIARole() == roles::ALERT) {
|
2012-11-20 08:53:38 +04:00
|
|
|
FireDelayedEvent(nsIAccessibleEvent::EVENT_ALERT, ancestor);
|
2011-01-18 11:03:38 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't climb above this document.
|
|
|
|
if (ancestor == this)
|
|
|
|
break;
|
|
|
|
|
2011-07-23 12:38:33 +04:00
|
|
|
ancestor = ancestor->Parent();
|
2011-01-18 11:03:38 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-01 20:12:02 +04:00
|
|
|
MaybeNotifyOfValueChange(aContainer);
|
2014-12-30 23:43:49 +03:00
|
|
|
FireDelayedEvent(reorderEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
DocAccessible::UpdateTreeOnRemoval(Accessible* aContainer, nsIContent* aChildNode)
|
|
|
|
{
|
|
|
|
// If child node is not accessible then look for its accessible children.
|
|
|
|
Accessible* child = GetAccessible(aChildNode);
|
|
|
|
#ifdef A11Y_LOG
|
|
|
|
if (logging::IsEnabled(logging::eTree)) {
|
|
|
|
logging::MsgBegin("TREE", "process content removal");
|
|
|
|
logging::Node("container", aContainer->GetNode());
|
|
|
|
logging::Node("child", aChildNode);
|
|
|
|
if (child)
|
|
|
|
logging::Address("child", child);
|
|
|
|
else
|
|
|
|
logging::MsgEntry("child accessible: null");
|
|
|
|
|
|
|
|
logging::MsgEnd();
|
|
|
|
}
|
|
|
|
#endif
|
2011-01-18 11:03:38 +03:00
|
|
|
|
2014-12-30 23:43:49 +03:00
|
|
|
uint32_t updateFlags = eNoAccessible;
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(aContainer);
|
2014-12-30 23:43:49 +03:00
|
|
|
AutoTreeMutation mut(aContainer);
|
|
|
|
|
|
|
|
if (child) {
|
|
|
|
updateFlags |= UpdateTreeInternal(child, false, reorderEvent);
|
|
|
|
} else {
|
|
|
|
// aChildNode may not coorespond to a particular accessible, to handle
|
|
|
|
// this we go through all the children of aContainer. Then if a child
|
|
|
|
// has aChildNode as an ancestor, or does not have the node for
|
|
|
|
// aContainer as an ancestor remove that child of aContainer. Note that
|
|
|
|
// when we are called aChildNode may already have been removed from the DOM
|
|
|
|
// so we can't expect it to have a parent or what was it's parent to have
|
|
|
|
// it as a child.
|
|
|
|
nsINode* containerNode = aContainer->GetNode();
|
|
|
|
for (uint32_t idx = 0; idx < aContainer->ContentChildCount();) {
|
|
|
|
Accessible* child = aContainer->ContentChildAt(idx);
|
|
|
|
|
|
|
|
// If accessible doesn't have its own content then we assume parent
|
|
|
|
// will handle its update. If child is DocAccessible then we don't
|
|
|
|
// handle updating it here either.
|
|
|
|
if (!child->HasOwnContent() || child->IsDoc()) {
|
|
|
|
idx++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsINode* childNode = child->GetContent();
|
|
|
|
while (childNode != aChildNode && childNode != containerNode &&
|
|
|
|
(childNode = childNode->GetParentNode()));
|
|
|
|
|
|
|
|
if (childNode != containerNode) {
|
|
|
|
updateFlags |= UpdateTreeInternal(child, false, reorderEvent);
|
|
|
|
} else {
|
|
|
|
idx++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Content insertion/removal is not cause of accessible tree change.
|
|
|
|
if (updateFlags == eNoAccessible)
|
|
|
|
return;
|
|
|
|
|
|
|
|
MaybeNotifyOfValueChange(aContainer);
|
2012-11-20 08:53:38 +04:00
|
|
|
FireDelayedEvent(reorderEvent);
|
2011-01-18 11:03:38 +03:00
|
|
|
}
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
uint32_t
|
2012-11-13 10:29:22 +04:00
|
|
|
DocAccessible::UpdateTreeInternal(Accessible* aChild, bool aIsInsert,
|
|
|
|
AccReorderEvent* aReorderEvent)
|
2005-05-03 07:46:51 +04:00
|
|
|
{
|
2012-08-22 19:56:38 +04:00
|
|
|
uint32_t updateFlags = eAccessible;
|
2010-10-21 08:16:10 +04:00
|
|
|
|
2013-07-31 18:47:39 +04:00
|
|
|
// If a focused node has been shown then it could mean its frame was recreated
|
|
|
|
// while the node stays focused and we need to fire focus event on
|
|
|
|
// the accessible we just created. If the queue contains a focus event for
|
|
|
|
// this node already then it will be suppressed by this one.
|
|
|
|
Accessible* focusedAcc = nullptr;
|
|
|
|
|
2011-04-07 09:17:29 +04:00
|
|
|
nsINode* node = aChild->GetNode();
|
|
|
|
if (aIsInsert) {
|
|
|
|
// Create accessible tree for shown accessible.
|
2013-07-31 18:47:39 +04:00
|
|
|
CacheChildrenInSubtree(aChild, &focusedAcc);
|
2010-10-21 08:16:10 +04:00
|
|
|
|
2011-04-07 09:17:29 +04: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-11-20 08:53:38 +04:00
|
|
|
if (aChild->ARIARole() == roles::MENUPOPUP)
|
|
|
|
FireDelayedEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END, aChild);
|
2011-04-07 09:17:29 +04:00
|
|
|
}
|
2009-06-25 06:08:53 +04:00
|
|
|
|
2011-04-07 09:17:29 +04:00
|
|
|
// Fire show/hide event.
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<AccMutationEvent> event;
|
2011-04-07 09:17:29 +04:00
|
|
|
if (aIsInsert)
|
|
|
|
event = new AccShowEvent(aChild, node);
|
|
|
|
else
|
|
|
|
event = new AccHideEvent(aChild, node);
|
2005-06-10 17:57:27 +04:00
|
|
|
|
2012-11-20 08:53:38 +04:00
|
|
|
FireDelayedEvent(event);
|
2012-11-13 10:29:22 +04:00
|
|
|
aReorderEvent->AddSubMutationEvent(event);
|
2011-01-20 11:02:00 +03:00
|
|
|
|
2011-04-07 09:17:29 +04:00
|
|
|
if (aIsInsert) {
|
2012-01-12 07:07:35 +04:00
|
|
|
roles::Role ariaRole = aChild->ARIARole();
|
|
|
|
if (ariaRole == roles::MENUPOPUP) {
|
2011-04-07 09:17:29 +04:00
|
|
|
// Fire EVENT_MENUPOPUP_START if ARIA menu appears.
|
2012-11-20 08:53:38 +04:00
|
|
|
FireDelayedEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START, aChild);
|
2011-04-07 09:17:29 +04:00
|
|
|
|
2012-01-12 07:07:35 +04:00
|
|
|
} else if (ariaRole == roles::ALERT) {
|
2011-04-07 09:17:29 +04:00
|
|
|
// Fire EVENT_ALERT if ARIA alert appears.
|
|
|
|
updateFlags = eAlertAccessible;
|
2012-11-20 08:53:38 +04:00
|
|
|
FireDelayedEvent(nsIAccessibleEvent::EVENT_ALERT, aChild);
|
2010-11-30 18:43:17 +03:00
|
|
|
}
|
2011-04-07 09:17:29 +04: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.
|
2012-05-29 05:18:45 +04:00
|
|
|
Accessible* parent = aChild->Parent();
|
2011-04-07 09:17:29 +04:00
|
|
|
NS_ASSERTION(parent, "No accessible parent?!");
|
|
|
|
if (parent)
|
|
|
|
parent->RemoveChild(aChild);
|
|
|
|
|
|
|
|
UncacheChildrenInSubtree(aChild);
|
2005-06-10 17:57:27 +04:00
|
|
|
}
|
2003-05-15 12:37:38 +04:00
|
|
|
|
2013-07-31 18:47:39 +04:00
|
|
|
// XXX: do we really want to send focus to focused DOM node not taking into
|
|
|
|
// account active item?
|
2014-01-30 02:07:35 +04:00
|
|
|
if (focusedAcc) {
|
2013-07-31 18:47:39 +04:00
|
|
|
FocusMgr()->DispatchFocusEvent(this, focusedAcc);
|
2014-01-30 02:07:35 +04:00
|
|
|
SelectionMgr()->SetControlSelectionListener(focusedAcc->GetNode()->AsElement());
|
|
|
|
}
|
2013-07-31 18:47:39 +04:00
|
|
|
|
2010-10-21 08:16:10 +04:00
|
|
|
return updateFlags;
|
2003-04-28 14:24:52 +04:00
|
|
|
}
|
|
|
|
|
2015-10-30 01:08:48 +03:00
|
|
|
void
|
|
|
|
DocAccessible::RelocateARIAOwnedIfNeeded(nsIContent* aElement)
|
|
|
|
{
|
|
|
|
if (!aElement->HasID())
|
|
|
|
return;
|
|
|
|
|
|
|
|
AttrRelProviderArray* list =
|
|
|
|
mDependentIDsHash.Get(nsDependentAtomString(aElement->GetID()));
|
|
|
|
if (list) {
|
|
|
|
for (uint32_t idx = 0; idx < list->Length(); idx++) {
|
|
|
|
if (list->ElementAt(idx)->mRelAttr == nsGkAtoms::aria_owns) {
|
|
|
|
Accessible* owner = GetAccessible(list->ElementAt(idx)->mContent);
|
|
|
|
if (owner) {
|
|
|
|
mNotificationController->ScheduleRelocation(owner);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-15 19:01:51 +03:00
|
|
|
void
|
|
|
|
DocAccessible::ValidateARIAOwned()
|
|
|
|
{
|
|
|
|
for (auto it = mARIAOwnsHash.Iter(); !it.Done(); it.Next()) {
|
2015-10-30 01:08:48 +03:00
|
|
|
Accessible* owner = it.Key();
|
|
|
|
nsTArray<RefPtr<Accessible> >* children = it.UserData();
|
|
|
|
|
|
|
|
// Owner is about to die, put children back if applicable.
|
2015-12-14 20:02:23 +03:00
|
|
|
if (!mAccessibleCache.GetWeak(reinterpret_cast<void*>(owner)) ||
|
|
|
|
!owner->IsInDocument()) {
|
2015-10-30 01:08:48 +03:00
|
|
|
PutChildrenBack(children, 0);
|
|
|
|
it.Remove();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint32_t idx = 0; idx < children->Length(); idx++) {
|
|
|
|
Accessible* child = children->ElementAt(idx);
|
|
|
|
if (!child->IsInDocument()) {
|
|
|
|
children->RemoveElementAt(idx);
|
|
|
|
idx--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_ASSERTION(child->Parent(), "No parent for ARIA owned?");
|
|
|
|
|
|
|
|
// If DOM node doesn't have a frame anymore then shutdown its accessible.
|
|
|
|
if (child->Parent() && !child->GetFrame()) {
|
|
|
|
UpdateTreeOnRemoval(child->Parent(), child->GetContent());
|
|
|
|
children->RemoveElementAt(idx);
|
|
|
|
idx--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_ASSERTION(child->Parent() == owner,
|
|
|
|
"Illigally stolen ARIA owned child!");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (children->Length() == 0) {
|
|
|
|
it.Remove();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
DocAccessible::DoARIAOwnsRelocation(Accessible* aOwner)
|
|
|
|
{
|
|
|
|
nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.LookupOrAdd(aOwner);
|
|
|
|
|
2015-12-04 01:07:43 +03:00
|
|
|
MOZ_ASSERT(aOwner, "aOwner must be a valid pointer");
|
|
|
|
MOZ_ASSERT(aOwner->Elm(), "aOwner->Elm() must be a valid pointer");
|
|
|
|
|
2015-10-30 01:08:48 +03:00
|
|
|
IDRefsIterator iter(this, aOwner->Elm(), nsGkAtoms::aria_owns);
|
|
|
|
Accessible* child = nullptr;
|
|
|
|
|
|
|
|
uint32_t arrayIdx = 0, insertIdx = aOwner->ChildCount() - children->Length();
|
|
|
|
while ((child = iter.Next())) {
|
|
|
|
// Same child on same position, no change.
|
|
|
|
if (child->Parent() == aOwner &&
|
|
|
|
child->IndexInParent() == static_cast<int32_t>(insertIdx)) {
|
|
|
|
NS_ASSERTION(child == children->ElementAt(arrayIdx), "Not in sync!");
|
|
|
|
insertIdx++; arrayIdx++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_ASSERTION(children->SafeElementAt(arrayIdx) != child, "Already in place!");
|
|
|
|
|
|
|
|
nsTArray<RefPtr<Accessible> >::index_type idx = children->IndexOf(child);
|
|
|
|
if (idx < arrayIdx) {
|
|
|
|
continue; // ignore second entry of same ID
|
|
|
|
}
|
|
|
|
|
|
|
|
// A new child is found, check for loops.
|
|
|
|
if (child->Parent() != aOwner) {
|
|
|
|
Accessible* parent = aOwner;
|
|
|
|
while (parent && parent != child && !parent->IsDoc()) {
|
|
|
|
parent = parent->Parent();
|
|
|
|
}
|
|
|
|
// A referred child cannot be a parent of the owner.
|
|
|
|
if (parent == child) {
|
|
|
|
continue;
|
2015-09-15 19:01:51 +03:00
|
|
|
}
|
|
|
|
}
|
2015-10-30 01:08:48 +03:00
|
|
|
|
|
|
|
if (child->Parent() == aOwner) {
|
|
|
|
MoveChild(child, insertIdx - 1);
|
|
|
|
children->InsertElementAt(arrayIdx, child);
|
|
|
|
arrayIdx++;
|
|
|
|
|
|
|
|
} else if (SeizeChild(aOwner, child, insertIdx)) {
|
|
|
|
children->InsertElementAt(arrayIdx, child);
|
|
|
|
insertIdx++; arrayIdx++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Put back children that are not seized anymore.
|
|
|
|
PutChildrenBack(children, arrayIdx);
|
|
|
|
if (children->Length() == 0) {
|
|
|
|
mARIAOwnsHash.Remove(aOwner);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
DocAccessible::SeizeChild(Accessible* aNewParent, Accessible* aChild,
|
|
|
|
int32_t aIdxInParent)
|
|
|
|
{
|
|
|
|
Accessible* oldParent = aChild->Parent();
|
2015-11-09 22:47:38 +03:00
|
|
|
if (!oldParent) {
|
|
|
|
NS_ERROR("No parent? The tree is broken!");
|
|
|
|
return false;
|
|
|
|
}
|
2015-10-30 01:08:48 +03:00
|
|
|
|
|
|
|
int32_t oldIdxInParent = aChild->IndexInParent();
|
|
|
|
|
|
|
|
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(oldParent);
|
|
|
|
RefPtr<AccMutationEvent> hideEvent =
|
|
|
|
new AccHideEvent(aChild, aChild->GetContent(), false);
|
|
|
|
reorderEvent->AddSubMutationEvent(hideEvent);
|
|
|
|
|
|
|
|
{
|
|
|
|
AutoTreeMutation mut(oldParent);
|
|
|
|
oldParent->RemoveChild(aChild);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isReinserted = false;
|
|
|
|
{
|
|
|
|
AutoTreeMutation mut(aNewParent);
|
|
|
|
isReinserted = aNewParent->InsertChildAt(aIdxInParent, aChild);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isReinserted) {
|
|
|
|
AutoTreeMutation mut(oldParent);
|
|
|
|
oldParent->InsertChildAt(oldIdxInParent, aChild);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The child may be stolen from other ARIA owns element.
|
|
|
|
if (aChild->IsRelocated()) {
|
|
|
|
nsTArray<RefPtr<Accessible> >* children = mARIAOwnsHash.Get(oldParent);
|
|
|
|
children->RemoveElement(aChild);
|
|
|
|
}
|
|
|
|
|
|
|
|
FireDelayedEvent(hideEvent);
|
|
|
|
MaybeNotifyOfValueChange(oldParent);
|
|
|
|
FireDelayedEvent(reorderEvent);
|
|
|
|
|
|
|
|
reorderEvent = new AccReorderEvent(aNewParent);
|
|
|
|
RefPtr<AccMutationEvent> showEvent =
|
|
|
|
new AccShowEvent(aChild, aChild->GetContent());
|
|
|
|
reorderEvent->AddSubMutationEvent(showEvent);
|
|
|
|
|
|
|
|
FireDelayedEvent(showEvent);
|
|
|
|
MaybeNotifyOfValueChange(aNewParent);
|
|
|
|
FireDelayedEvent(reorderEvent);
|
|
|
|
|
|
|
|
aChild->SetRelocated(true);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
DocAccessible::MoveChild(Accessible* aChild, int32_t aIdxInParent)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(aChild->Parent(), "No parent?");
|
|
|
|
|
|
|
|
Accessible* parent = aChild->Parent();
|
|
|
|
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(parent);
|
|
|
|
RefPtr<AccMutationEvent> hideEvent =
|
|
|
|
new AccHideEvent(aChild, aChild->GetContent(), false);
|
|
|
|
reorderEvent->AddSubMutationEvent(hideEvent);
|
|
|
|
|
|
|
|
AutoTreeMutation mut(parent);
|
|
|
|
parent->RemoveChild(aChild);
|
|
|
|
|
|
|
|
parent->InsertChildAt(aIdxInParent, aChild);
|
|
|
|
aChild->SetRelocated(true);
|
|
|
|
|
|
|
|
FireDelayedEvent(hideEvent);
|
|
|
|
|
|
|
|
RefPtr<AccMutationEvent> showEvent =
|
|
|
|
new AccShowEvent(aChild, aChild->GetContent());
|
|
|
|
reorderEvent->AddSubMutationEvent(showEvent);
|
|
|
|
FireDelayedEvent(showEvent);
|
|
|
|
|
|
|
|
MaybeNotifyOfValueChange(parent);
|
|
|
|
FireDelayedEvent(reorderEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
DocAccessible::PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
|
|
|
|
uint32_t aStartIdx)
|
|
|
|
{
|
2015-11-27 04:46:12 +03:00
|
|
|
nsTArray<RefPtr<Accessible> > containers;
|
2015-10-30 01:08:48 +03:00
|
|
|
for (auto idx = aStartIdx; idx < aChildren->Length(); idx++) {
|
|
|
|
Accessible* child = aChildren->ElementAt(idx);
|
|
|
|
|
|
|
|
// If the child is in the tree then remove it from the owner.
|
|
|
|
if (child->IsInDocument()) {
|
|
|
|
Accessible* owner = child->Parent();
|
2015-11-09 22:47:38 +03:00
|
|
|
if (!owner) {
|
|
|
|
NS_ERROR("Cannot put the child back. No parent, a broken tree.");
|
|
|
|
continue;
|
|
|
|
}
|
2015-10-30 01:08:48 +03:00
|
|
|
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(owner);
|
|
|
|
RefPtr<AccMutationEvent> hideEvent =
|
|
|
|
new AccHideEvent(child, child->GetContent(), false);
|
|
|
|
reorderEvent->AddSubMutationEvent(hideEvent);
|
|
|
|
|
|
|
|
{
|
|
|
|
AutoTreeMutation mut(owner);
|
|
|
|
owner->RemoveChild(child);
|
|
|
|
child->SetRelocated(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
FireDelayedEvent(hideEvent);
|
|
|
|
MaybeNotifyOfValueChange(owner);
|
|
|
|
FireDelayedEvent(reorderEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
Accessible* container = GetContainerAccessible(child->GetContent());
|
|
|
|
if (container &&
|
|
|
|
containers.IndexOf(container) == nsTArray<Accessible*>::NoIndex) {
|
|
|
|
containers.AppendElement(container);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// And put it back where it belongs to.
|
|
|
|
aChildren->RemoveElementsAt(aStartIdx, aChildren->Length() - aStartIdx);
|
|
|
|
for (uint32_t idx = 0; idx < containers.Length(); idx++) {
|
2015-11-27 04:46:12 +03:00
|
|
|
NS_ASSERTION(containers[idx]->IsInDocument(),
|
|
|
|
"A container has been destroyed.");
|
|
|
|
if (containers[idx]->IsInDocument()) {
|
|
|
|
UpdateTreeOnInsertion(containers[idx]);
|
|
|
|
}
|
2015-09-15 19:01:51 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-20 11:02:00 +03:00
|
|
|
void
|
2013-07-31 18:47:39 +04:00
|
|
|
DocAccessible::CacheChildrenInSubtree(Accessible* aRoot,
|
|
|
|
Accessible** aFocusedAcc)
|
2011-01-20 11:02:00 +03:00
|
|
|
{
|
2013-07-31 18:47:39 +04:00
|
|
|
// If the accessible is focused then report a focus event after all related
|
|
|
|
// mutation events.
|
|
|
|
if (aFocusedAcc && !*aFocusedAcc &&
|
|
|
|
FocusMgr()->HasDOMFocus(aRoot->GetContent()))
|
|
|
|
*aFocusedAcc = aRoot;
|
|
|
|
|
2011-01-20 11:02:00 +03:00
|
|
|
aRoot->EnsureChildren();
|
|
|
|
|
2011-06-07 06:24:01 +04: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.
|
2012-08-22 19:56:38 +04:00
|
|
|
uint32_t count = aRoot->ContentChildCount();
|
|
|
|
for (uint32_t idx = 0; idx < count; idx++) {
|
2012-05-29 05:18:45 +04:00
|
|
|
Accessible* child = aRoot->ContentChildAt(idx);
|
2011-03-28 17:59:17 +04:00
|
|
|
NS_ASSERTION(child, "Illicit tree change while tree is created!");
|
2011-01-20 11:02:00 +03:00
|
|
|
// Don't cross document boundaries.
|
2011-03-28 17:59:17 +04:00
|
|
|
if (child && child->IsContent())
|
2013-07-31 18:47:39 +04:00
|
|
|
CacheChildrenInSubtree(child, aFocusedAcc);
|
2011-01-20 11:02:00 +03:00
|
|
|
}
|
2012-06-08 17:36:41 +04:00
|
|
|
|
|
|
|
// Fire document load complete on ARIA documents.
|
|
|
|
// XXX: we should delay an event if the ARIA document has aria-busy.
|
|
|
|
if (aRoot->HasARIARole() && !aRoot->IsDoc()) {
|
|
|
|
a11y::role role = aRoot->ARIARole();
|
|
|
|
if (role == roles::DIALOG || role == roles::DOCUMENT)
|
2012-11-20 08:53:38 +04:00
|
|
|
FireDelayedEvent(nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE, aRoot);
|
2012-06-08 17:36:41 +04:00
|
|
|
}
|
2011-01-20 11:02:00 +03:00
|
|
|
}
|
|
|
|
|
2010-10-21 08:16:10 +04:00
|
|
|
void
|
2012-05-29 05:18:45 +04:00
|
|
|
DocAccessible::UncacheChildrenInSubtree(Accessible* aRoot)
|
2007-10-06 20:24:57 +04:00
|
|
|
{
|
2012-12-11 07:31:42 +04:00
|
|
|
aRoot->mStateFlags |= eIsNotInDocument;
|
2015-09-15 19:01:51 +03:00
|
|
|
RemoveDependentIDsFor(aRoot);
|
2011-02-11 18:05:38 +03:00
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
uint32_t count = aRoot->ContentChildCount();
|
|
|
|
for (uint32_t idx = 0; idx < count; idx++)
|
2011-06-07 06:24:01 +04:00
|
|
|
UncacheChildrenInSubtree(aRoot->ContentChildAt(idx));
|
2007-10-06 20:24:57 +04:00
|
|
|
|
2012-10-13 10:34:21 +04:00
|
|
|
if (aRoot->IsNodeMapEntry() &&
|
2010-10-21 08:16:10 +04:00
|
|
|
mNodeToAccessibleMap.Get(aRoot->GetNode()) == aRoot)
|
|
|
|
mNodeToAccessibleMap.Remove(aRoot->GetNode());
|
|
|
|
}
|
2007-10-06 20:24:57 +04:00
|
|
|
|
2010-10-21 08:16:10 +04:00
|
|
|
void
|
2012-05-29 05:18:45 +04:00
|
|
|
DocAccessible::ShutdownChildrenInSubtree(Accessible* aAccessible)
|
2010-10-21 08:16:10 +04:00
|
|
|
{
|
|
|
|
// 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.
|
2012-08-22 19:56:38 +04:00
|
|
|
uint32_t count = aAccessible->ContentChildCount();
|
|
|
|
for (uint32_t idx = 0, jdx = 0; idx < count; idx++) {
|
2012-05-29 05:18:45 +04:00
|
|
|
Accessible* child = aAccessible->ContentChildAt(jdx);
|
2010-10-21 08:16:10 +04:00
|
|
|
if (!child->IsBoundToParent()) {
|
|
|
|
NS_ERROR("Parent refers to a child, child doesn't refer to parent!");
|
|
|
|
jdx++;
|
2010-07-02 05:49:42 +04:00
|
|
|
}
|
2009-09-09 13:03:14 +04:00
|
|
|
|
2013-05-04 15:06:22 +04:00
|
|
|
// Don't cross document boundaries. The outerdoc shutdown takes care about
|
|
|
|
// its subdocument.
|
|
|
|
if (!child->IsDoc())
|
|
|
|
ShutdownChildrenInSubtree(child);
|
2007-10-06 20:24:57 +04:00
|
|
|
}
|
|
|
|
|
2010-11-12 22:00:55 +03:00
|
|
|
UnbindFromDocument(aAccessible);
|
2007-10-06 20:24:57 +04:00
|
|
|
}
|
2011-08-08 11:55:36 +04:00
|
|
|
|
|
|
|
bool
|
2012-05-27 13:01:40 +04:00
|
|
|
DocAccessible::IsLoadEventTarget() const
|
2011-08-08 11:55:36 +04:00
|
|
|
{
|
2013-11-15 20:32:12 +04:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> treeItem = mDocumentNode->GetDocShell();
|
2012-10-10 05:01:46 +04:00
|
|
|
NS_ASSERTION(treeItem, "No document shell for document!");
|
2011-08-08 11:55:36 +04:00
|
|
|
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
|
2012-10-10 05:01:46 +04:00
|
|
|
treeItem->GetParent(getter_AddRefs(parentTreeItem));
|
2011-08-08 11:55:36 +04:00
|
|
|
|
2012-10-10 05:01:46 +04:00
|
|
|
// Not a root document.
|
2012-06-02 09:14:42 +04:00
|
|
|
if (parentTreeItem) {
|
2012-10-10 05:01:46 +04:00
|
|
|
// Return true if it's either:
|
|
|
|
// a) tab document;
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
|
|
|
|
treeItem->GetRootTreeItem(getter_AddRefs(rootTreeItem));
|
|
|
|
if (parentTreeItem == rootTreeItem)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// b) frame/iframe document and its parent document is not in loading state
|
|
|
|
// Note: we can get notifications while document is loading (and thus
|
|
|
|
// while there's no parent document yet).
|
2012-06-02 09:14:42 +04:00
|
|
|
DocAccessible* parentDoc = ParentDocument();
|
|
|
|
return parentDoc && parentDoc->HasLoadState(eCompletelyLoaded);
|
|
|
|
}
|
2011-08-08 11:55:36 +04:00
|
|
|
|
2012-05-30 06:21:24 +04:00
|
|
|
// It's content (not chrome) root document.
|
2014-01-20 11:58:26 +04:00
|
|
|
return (treeItem->ItemType() == nsIDocShellTreeItem::typeContent);
|
2011-08-08 11:55:36 +04:00
|
|
|
}
|
|
|
|
|