2010-06-11 19:23:13 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
2010-06-08 16:39:58 +00:00
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Mozilla Foundation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2010
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
#include "nsAccDocManager.h"
|
|
|
|
|
|
|
|
#include "nsAccessibilityService.h"
|
|
|
|
#include "nsAccUtils.h"
|
|
|
|
#include "nsOuterDocAccessible.h"
|
|
|
|
#include "nsRootAccessibleWrap.h"
|
|
|
|
|
|
|
|
#include "nsCURILoader.h"
|
|
|
|
#include "nsDocShellLoadTypes.h"
|
|
|
|
#include "nsIChannel.h"
|
|
|
|
#include "nsIContentViewer.h"
|
|
|
|
#include "nsIDOMDocument.h"
|
|
|
|
#include "nsIEventListenerManager.h"
|
|
|
|
#include "nsIDOMEventTarget.h"
|
|
|
|
#include "nsIDOMWindow.h"
|
|
|
|
#include "nsIInterfaceRequestorUtils.h"
|
|
|
|
#include "nsIWebNavigation.h"
|
|
|
|
#include "nsServiceManagerUtils.h"
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsAccDocManager
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsAccDocManager public
|
|
|
|
|
|
|
|
nsDocAccessible*
|
|
|
|
nsAccDocManager::GetDocAccessible(nsIDocument *aDocument)
|
|
|
|
{
|
|
|
|
if (!aDocument)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
nsDocAccessible *docAcc =
|
|
|
|
mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument));
|
|
|
|
if (docAcc)
|
|
|
|
return docAcc;
|
|
|
|
|
|
|
|
return CreateDocOrRootAccessible(aDocument);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsAccessible*
|
|
|
|
nsAccDocManager::FindAccessibleInCache(void *aUniqueID) const
|
|
|
|
{
|
|
|
|
nsSearchAccessibleInCacheArg arg;
|
|
|
|
arg.mUniqueID = aUniqueID;
|
|
|
|
|
|
|
|
mDocAccessibleCache.EnumerateRead(SearchAccessibleInDocCache,
|
|
|
|
static_cast<void*>(&arg));
|
|
|
|
|
|
|
|
return arg.mAccessible;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsAccDocManager::ShutdownDocAccessiblesInTree(nsIDocument *aDocument)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsISupports> container = aDocument->GetContainer();
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> treeItem = do_QueryInterface(container);
|
|
|
|
ShutdownDocAccessiblesInTree(treeItem, aDocument);
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsAccDocManager protected
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsAccDocManager::Init()
|
|
|
|
{
|
|
|
|
mDocAccessibleCache.Init(4);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIWebProgress> progress =
|
|
|
|
do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
|
|
|
|
|
|
|
|
if (!progress)
|
|
|
|
return PR_FALSE;
|
|
|
|
|
|
|
|
progress->AddProgressListener(static_cast<nsIWebProgressListener*>(this),
|
|
|
|
nsIWebProgress::NOTIFY_STATE_DOCUMENT);
|
|
|
|
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsAccDocManager::Shutdown()
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIWebProgress> progress =
|
|
|
|
do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
|
|
|
|
|
|
|
|
if (progress)
|
|
|
|
progress->RemoveProgressListener(static_cast<nsIWebProgressListener*>(this));
|
|
|
|
|
|
|
|
ClearDocCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsISupports
|
|
|
|
|
|
|
|
NS_IMPL_THREADSAFE_ISUPPORTS3(nsAccDocManager,
|
|
|
|
nsIWebProgressListener,
|
|
|
|
nsIDOMEventListener,
|
|
|
|
nsISupportsWeakReference)
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsIWebProgressListener
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsAccDocManager::OnStateChange(nsIWebProgress *aWebProgress,
|
|
|
|
nsIRequest *aRequest, PRUint32 aStateFlags,
|
|
|
|
nsresult aStatus)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(aStateFlags & STATE_IS_DOCUMENT, "Other notifications excluded");
|
|
|
|
|
|
|
|
if (nsAccessibilityService::IsShutdown() || !aWebProgress ||
|
|
|
|
(aStateFlags & (STATE_START | STATE_STOP)) == 0)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMWindow> DOMWindow;
|
|
|
|
aWebProgress->GetDOMWindow(getter_AddRefs(DOMWindow));
|
|
|
|
NS_ENSURE_STATE(DOMWindow);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMDocument> DOMDocument;
|
|
|
|
DOMWindow->GetDocument(getter_AddRefs(DOMDocument));
|
|
|
|
NS_ENSURE_STATE(DOMDocument);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocument> document(do_QueryInterface(DOMDocument));
|
|
|
|
|
|
|
|
// Document was loaded.
|
|
|
|
if (aStateFlags & STATE_STOP) {
|
|
|
|
NS_LOG_ACCDOCLOAD("document loaded", aWebProgress, aRequest, aStateFlags)
|
|
|
|
|
|
|
|
// Figure out an event type to notify the document has been loaded.
|
|
|
|
PRUint32 eventType = nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED;
|
|
|
|
|
|
|
|
// Some XUL documents get start state and then stop state with failure
|
|
|
|
// status when everything is ok. Fire document load complete event in this
|
|
|
|
// case.
|
|
|
|
if (NS_SUCCEEDED(aStatus) || !nsCoreUtils::IsContentDocument(document))
|
|
|
|
eventType = nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE;
|
|
|
|
|
|
|
|
// If end consumer has been retargeted for loaded content then do not fire
|
|
|
|
// any event because it means no new document has been loaded, for example,
|
|
|
|
// it happens when user clicks on file link.
|
|
|
|
if (aRequest) {
|
|
|
|
PRUint32 loadFlags = 0;
|
|
|
|
aRequest->GetLoadFlags(&loadFlags);
|
|
|
|
if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI)
|
|
|
|
eventType = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
HandleDOMDocumentLoad(document, eventType);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Document loading was started.
|
|
|
|
NS_LOG_ACCDOCLOAD("start document loading", aWebProgress, aRequest,
|
|
|
|
aStateFlags)
|
|
|
|
|
|
|
|
if (!IsEventTargetDocument(document))
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsDocAccessible *docAcc =
|
|
|
|
mDocAccessibleCache.GetWeak(static_cast<void*>(document));
|
|
|
|
if (!docAcc)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(DOMWindow));
|
|
|
|
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(webNav));
|
|
|
|
NS_ENSURE_STATE(docShell);
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
PRUint32 loadType;
|
|
|
|
docShell->GetLoadType(&loadType);
|
|
|
|
if (loadType == LOAD_RELOAD_NORMAL ||
|
|
|
|
loadType == LOAD_RELOAD_BYPASS_CACHE ||
|
|
|
|
loadType == LOAD_RELOAD_BYPASS_PROXY ||
|
|
|
|
loadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE) {
|
|
|
|
|
|
|
|
// Fire reload event.
|
|
|
|
nsRefPtr<nsAccEvent> reloadEvent =
|
|
|
|
new nsAccEvent(nsIAccessibleEvent::EVENT_DOCUMENT_RELOAD, docAcc);
|
|
|
|
nsEventShell::FireEvent(reloadEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fire state busy change event. Use delayed event since we don't care
|
|
|
|
// actually if event isn't delivered when the document goes away like a shot.
|
|
|
|
nsRefPtr<nsAccEvent> stateEvent =
|
2010-06-11 08:23:18 +00:00
|
|
|
new nsAccStateChangeEvent(document, nsIAccessibleStates::STATE_BUSY,
|
2010-06-08 16:39:58 +00:00
|
|
|
PR_FALSE, PR_TRUE);
|
|
|
|
docAcc->FireDelayedAccessibleEvent(stateEvent);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsAccDocManager::OnProgressChange(nsIWebProgress *aWebProgress,
|
|
|
|
nsIRequest *aRequest,
|
|
|
|
PRInt32 aCurSelfProgress,
|
|
|
|
PRInt32 aMaxSelfProgress,
|
|
|
|
PRInt32 aCurTotalProgress,
|
|
|
|
PRInt32 aMaxTotalProgress)
|
|
|
|
{
|
|
|
|
NS_NOTREACHED("notification excluded in AddProgressListener(...)");
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsAccDocManager::OnLocationChange(nsIWebProgress *aWebProgress,
|
|
|
|
nsIRequest *aRequest, nsIURI *aLocation)
|
|
|
|
{
|
|
|
|
NS_NOTREACHED("notification excluded in AddProgressListener(...)");
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsAccDocManager::OnStatusChange(nsIWebProgress *aWebProgress,
|
|
|
|
nsIRequest *aRequest, nsresult aStatus,
|
|
|
|
const PRUnichar *aMessage)
|
|
|
|
{
|
|
|
|
NS_NOTREACHED("notification excluded in AddProgressListener(...)");
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsAccDocManager::OnSecurityChange(nsIWebProgress *aWebProgress,
|
|
|
|
nsIRequest *aRequest,
|
|
|
|
PRUint32 aState)
|
|
|
|
{
|
|
|
|
NS_NOTREACHED("notification excluded in AddProgressListener(...)");
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsIDOMEventListener
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsAccDocManager::HandleEvent(nsIDOMEvent *aEvent)
|
|
|
|
{
|
|
|
|
nsAutoString type;
|
|
|
|
aEvent->GetType(type);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMEventTarget> target;
|
|
|
|
aEvent->GetTarget(getter_AddRefs(target));
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocument> document(do_QueryInterface(target));
|
|
|
|
NS_ASSERTION(document, "pagehide or DOMContentLoaded for non document!");
|
|
|
|
if (!document)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
if (type.EqualsLiteral("pagehide")) {
|
|
|
|
// 'pagehide' event is registered on every DOM document we create an
|
|
|
|
// accessible for, process the event for the target. This document
|
|
|
|
// accessible and all its sub document accessible are shutdown as result of
|
|
|
|
// processing.
|
|
|
|
|
|
|
|
NS_LOG_ACCDOCDESTROY("received 'pagehide' event", document)
|
|
|
|
|
|
|
|
// Ignore 'pagehide' on temporary documents since we ignore them entirely in
|
|
|
|
// accessibility.
|
|
|
|
if (document->IsInitialDocument())
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
// Shutdown this one and sub document accessibles.
|
|
|
|
ShutdownDocAccessiblesInTree(document);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// XXX: handle error pages loading separately since they get neither
|
|
|
|
// webprogress notifications nor 'pageshow' event.
|
|
|
|
if (type.EqualsLiteral("DOMContentLoaded") &&
|
|
|
|
nsCoreUtils::IsErrorPage(document)) {
|
|
|
|
NS_LOG_ACCDOCLOAD2("handled 'DOMContentLoaded' event", document)
|
|
|
|
HandleDOMDocumentLoad(document,
|
|
|
|
nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE,
|
|
|
|
PR_TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsAccDocManager private
|
|
|
|
|
|
|
|
void
|
|
|
|
nsAccDocManager::HandleDOMDocumentLoad(nsIDocument *aDocument,
|
|
|
|
PRUint32 aLoadEventType,
|
|
|
|
PRBool aMarkAsLoaded)
|
|
|
|
{
|
|
|
|
// Document accessible can be created before we were notified the DOM document
|
|
|
|
// was loaded completely. However if it's not created yet then create it.
|
|
|
|
nsDocAccessible *docAcc =
|
|
|
|
mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument));
|
|
|
|
|
|
|
|
if (!docAcc) {
|
|
|
|
docAcc = CreateDocOrRootAccessible(aDocument);
|
|
|
|
NS_ASSERTION(docAcc, "Can't create document accessible!");
|
|
|
|
if (!docAcc)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aMarkAsLoaded)
|
|
|
|
docAcc->MarkAsLoaded();
|
|
|
|
|
|
|
|
// 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 (!IsEventTargetDocument(aDocument)) {
|
|
|
|
// XXX: AT doesn't update their virtual buffer once frame is loaded and it
|
|
|
|
// has dynamic content added after frame load. There's something wrong how
|
|
|
|
// we handle this changes.
|
|
|
|
if (!nsCoreUtils::IsRootDocument(aDocument)) {
|
|
|
|
docAcc->InvalidateCacheSubtree(nsnull,
|
|
|
|
nsIAccessibilityService::NODE_SIGNIFICANT_CHANGE);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fire complete/load stopped if the load event type is given.
|
|
|
|
if (aLoadEventType) {
|
2010-06-11 08:23:18 +00:00
|
|
|
nsRefPtr<nsAccEvent> loadEvent = new nsAccEvent(aLoadEventType, aDocument);
|
2010-06-08 16:39:58 +00:00
|
|
|
docAcc->FireDelayedAccessibleEvent(loadEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fire busy state change event.
|
|
|
|
nsRefPtr<nsAccEvent> stateEvent =
|
2010-06-11 08:23:18 +00:00
|
|
|
new nsAccStateChangeEvent(aDocument, nsIAccessibleStates::STATE_BUSY,
|
2010-06-08 16:39:58 +00:00
|
|
|
PR_FALSE, PR_FALSE);
|
|
|
|
docAcc->FireDelayedAccessibleEvent(stateEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsAccDocManager::IsEventTargetDocument(nsIDocument *aDocument) const
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsISupports> container = aDocument->GetContainer();
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
|
|
|
|
do_QueryInterface(container);
|
|
|
|
NS_ASSERTION(docShellTreeItem, "No document shell for document!");
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
|
|
|
|
docShellTreeItem->GetParent(getter_AddRefs(parentTreeItem));
|
|
|
|
|
|
|
|
// It's not a root document.
|
|
|
|
if (parentTreeItem) {
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
|
|
|
|
docShellTreeItem->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
|
|
|
|
|
|
|
|
// It's not a sub document, i.e. a frame or iframe.
|
|
|
|
return (sameTypeRoot == docShellTreeItem);
|
|
|
|
}
|
|
|
|
|
|
|
|
// It's not chrome root document.
|
|
|
|
PRInt32 contentType;
|
|
|
|
docShellTreeItem->GetItemType(&contentType);
|
|
|
|
return (contentType == nsIDocShellTreeItem::typeContent);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsAccDocManager::AddListeners(nsIDocument *aDocument,
|
|
|
|
PRBool aAddDOMContentLoadedListener)
|
|
|
|
{
|
|
|
|
nsPIDOMWindow *window = aDocument->GetWindow();
|
|
|
|
nsPIDOMEventTarget *target = window->GetChromeEventHandler();
|
|
|
|
nsIEventListenerManager* elm = target->GetListenerManager(PR_TRUE);
|
|
|
|
elm->AddEventListenerByType(this, NS_LITERAL_STRING("pagehide"),
|
|
|
|
NS_EVENT_FLAG_CAPTURE, nsnull);
|
|
|
|
|
|
|
|
NS_LOG_ACCDOCCREATE_TEXT(" added 'pagehide' listener")
|
|
|
|
|
|
|
|
if (aAddDOMContentLoadedListener) {
|
|
|
|
elm->AddEventListenerByType(this, NS_LITERAL_STRING("DOMContentLoaded"),
|
|
|
|
NS_EVENT_FLAG_CAPTURE, nsnull);
|
|
|
|
NS_LOG_ACCDOCCREATE_TEXT(" added 'DOMContentLoaded' listener")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsAccDocManager::RemoveListeners(nsIDocument *aDocument)
|
|
|
|
{
|
|
|
|
// Document has no window when application shuts down. The document can still
|
|
|
|
// exist because we didn't receive a "pagehide" event.
|
|
|
|
nsPIDOMWindow *window = aDocument->GetWindow();
|
|
|
|
if (!window)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsPIDOMEventTarget *target = window->GetChromeEventHandler();
|
|
|
|
nsIEventListenerManager* elm = target->GetListenerManager(PR_TRUE);
|
|
|
|
elm->RemoveEventListenerByType(this, NS_LITERAL_STRING("pagehide"),
|
|
|
|
NS_EVENT_FLAG_CAPTURE, nsnull);
|
|
|
|
|
|
|
|
NS_LOG_ACCDOCDESTROY("removed 'pagehide' listener", aDocument)
|
|
|
|
|
|
|
|
if (nsCoreUtils::IsRootDocument(aDocument)) {
|
|
|
|
elm->RemoveEventListenerByType(this, NS_LITERAL_STRING("DOMContentLoaded"),
|
|
|
|
NS_EVENT_FLAG_CAPTURE, nsnull);
|
|
|
|
|
|
|
|
NS_LOG_ACCDOCDESTROY("removed 'DOMContentLoaded' listener", aDocument)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsDocAccessible*
|
|
|
|
nsAccDocManager::CreateDocOrRootAccessible(nsIDocument *aDocument)
|
|
|
|
{
|
|
|
|
// Ignore temporary and hiding documents.
|
|
|
|
if (aDocument->IsInitialDocument() || !aDocument->IsVisible())
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
// Ignore documents without presshell.
|
|
|
|
nsIPresShell *presShell = aDocument->GetPrimaryShell();
|
|
|
|
if (!presShell)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
// Do not create document accessible until role content is loaded, otherwise
|
|
|
|
// we get accessible document with wrong role.
|
2010-06-11 08:23:18 +00:00
|
|
|
nsIContent *rootElm = nsCoreUtils::GetRoleContent(aDocument);
|
|
|
|
if (!rootElm)
|
|
|
|
return nsnull;
|
2010-06-08 16:39:58 +00:00
|
|
|
|
|
|
|
PRBool isRootDoc = nsCoreUtils::IsRootDocument(aDocument);
|
|
|
|
|
|
|
|
// Ensure the document container node is accessible, otherwise do not create
|
|
|
|
// document accessible.
|
|
|
|
nsAccessible *outerDocAcc = nsnull;
|
|
|
|
if (!isRootDoc) {
|
|
|
|
nsIDocument* parentDoc = aDocument->GetParentDocument();
|
|
|
|
if (!parentDoc)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
nsIContent* ownerContent = parentDoc->FindContentForSubDocument(aDocument);
|
2010-06-11 08:23:18 +00:00
|
|
|
if (!ownerContent)
|
2010-06-08 16:39:58 +00:00
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
// XXXaaronl: ideally we would traverse the presshell chain. Since there's
|
|
|
|
// no easy way to do that, we cheat and use the document hierarchy.
|
|
|
|
// GetAccessible() is bad because it doesn't support our concept of multiple
|
|
|
|
// presshells per doc. It should be changed to use
|
|
|
|
// GetAccessibleInWeakShell().
|
2010-06-11 08:23:18 +00:00
|
|
|
outerDocAcc = GetAccService()->GetAccessible(ownerContent);
|
2010-06-08 16:39:58 +00:00
|
|
|
if (!outerDocAcc)
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(presShell));
|
|
|
|
|
|
|
|
// We only create root accessibles for the true root, otherwise create a
|
|
|
|
// doc accessible.
|
|
|
|
nsDocAccessible *docAcc = isRootDoc ?
|
2010-06-11 08:23:18 +00:00
|
|
|
new nsRootAccessibleWrap(aDocument, rootElm, weakShell) :
|
|
|
|
new nsDocAccessibleWrap(aDocument, rootElm, weakShell);
|
2010-06-08 16:39:58 +00:00
|
|
|
|
|
|
|
if (!docAcc)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
// Cache and addref document accessible.
|
|
|
|
if (!mDocAccessibleCache.Put(static_cast<void*>(aDocument), docAcc)) {
|
|
|
|
delete docAcc;
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
// XXX: ideally we should initialize an accessible and then put it into tree,
|
|
|
|
// also this code should be shared between doc and root accessibles.
|
|
|
|
if (outerDocAcc) {
|
|
|
|
// Root document accessible doesn't have associated outerdoc accessible, it
|
|
|
|
// adds itself to application accessible instead.
|
|
|
|
NS_LOG_ACCDOCCREATE("append document to outerdoc", aDocument)
|
|
|
|
outerDocAcc->AppendChild(docAcc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!GetAccService()->InitAccessible(docAcc,
|
2010-06-11 08:23:18 +00:00
|
|
|
nsAccUtils::GetRoleMapEntry(aDocument))) {
|
2010-06-08 16:39:58 +00:00
|
|
|
mDocAccessibleCache.Remove(static_cast<void*>(aDocument));
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_LOG_ACCDOCCREATE("document created", aDocument)
|
|
|
|
|
|
|
|
AddListeners(aDocument, isRootDoc);
|
|
|
|
return docAcc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsAccDocManager::ShutdownDocAccessiblesInTree(nsIDocShellTreeItem *aTreeItem,
|
|
|
|
nsIDocument *aDocument)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDocShellTreeNode> treeNode(do_QueryInterface(aTreeItem));
|
|
|
|
|
2010-06-11 19:23:13 +00:00
|
|
|
if (treeNode) {
|
|
|
|
PRInt32 subDocumentsCount = 0;
|
|
|
|
treeNode->GetChildCount(&subDocumentsCount);
|
|
|
|
for (PRInt32 idx = 0; idx < subDocumentsCount; idx++) {
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> treeItemChild;
|
|
|
|
treeNode->GetChildAt(idx, getter_AddRefs(treeItemChild));
|
|
|
|
NS_ASSERTION(treeItemChild, "No tree item when there should be");
|
|
|
|
if (!treeItemChild)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(treeItemChild));
|
|
|
|
nsCOMPtr<nsIContentViewer> contentViewer;
|
|
|
|
docShell->GetContentViewer(getter_AddRefs(contentViewer));
|
|
|
|
if (!contentViewer)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ShutdownDocAccessiblesInTree(treeItemChild, contentViewer->GetDocument());
|
|
|
|
}
|
2010-06-08 16:39:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ShutdownDocAccessible(aDocument);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsAccDocManager::ShutdownDocAccessible(nsIDocument *aDocument)
|
|
|
|
{
|
|
|
|
RemoveListeners(aDocument);
|
|
|
|
|
|
|
|
nsDocAccessible *docAccessible =
|
|
|
|
mDocAccessibleCache.GetWeak(static_cast<void*>(aDocument));
|
|
|
|
if (docAccessible)
|
|
|
|
docAccessible->Shutdown();
|
|
|
|
|
|
|
|
mDocAccessibleCache.Remove(static_cast<void*>(aDocument));
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// nsAccDocManager static
|
|
|
|
|
|
|
|
PLDHashOperator
|
|
|
|
nsAccDocManager::ClearDocCacheEntry(const void* aKey,
|
|
|
|
nsRefPtr<nsDocAccessible>& aDocAccessible,
|
|
|
|
void* aUserArg)
|
|
|
|
{
|
|
|
|
nsAccDocManager *accDocMgr = static_cast<nsAccDocManager*>(aUserArg);
|
|
|
|
|
|
|
|
NS_ASSERTION(aDocAccessible,
|
|
|
|
"Calling ClearDocCacheEntry with a NULL pointer!");
|
|
|
|
|
|
|
|
if (aDocAccessible) {
|
|
|
|
nsCOMPtr<nsIDocument> document = aDocAccessible->GetDOMDocument();
|
|
|
|
NS_ASSERTION(document, "Document accessible was shutdown already!");
|
|
|
|
if (document)
|
|
|
|
accDocMgr->RemoveListeners(document);
|
|
|
|
|
|
|
|
aDocAccessible->Shutdown();
|
|
|
|
}
|
|
|
|
|
|
|
|
return PL_DHASH_REMOVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
PLDHashOperator
|
|
|
|
nsAccDocManager::SearchAccessibleInDocCache(const void* aKey,
|
|
|
|
nsDocAccessible* aDocAccessible,
|
|
|
|
void* aUserArg)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(aDocAccessible,
|
|
|
|
"No doc accessible for the object in doc accessible cache!");
|
|
|
|
|
|
|
|
if (aDocAccessible) {
|
|
|
|
nsSearchAccessibleInCacheArg* arg =
|
|
|
|
static_cast<nsSearchAccessibleInCacheArg*>(aUserArg);
|
|
|
|
nsAccessNode* accessNode =
|
|
|
|
aDocAccessible->GetCachedAccessNode(arg->mUniqueID);
|
|
|
|
if (accessNode) {
|
|
|
|
arg->mAccessible = do_QueryObject(accessNode);
|
|
|
|
return PL_DHASH_STOP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return PL_DHASH_NEXT;
|
|
|
|
}
|