/* 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/. */ #include "xpcAccessibilityService.h" #include "nsAccessiblePivot.h" #include "nsAccessibilityService.h" #include "Platform.h" #include "xpcAccessibleApplication.h" #include "xpcAccessibleDocument.h" #ifdef A11Y_LOG # include "Logging.h" #endif using namespace mozilla; using namespace mozilla::a11y; using namespace mozilla::dom; xpcAccessibilityService* xpcAccessibilityService::gXPCAccessibilityService = nullptr; //////////////////////////////////////////////////////////////////////////////// // nsISupports void xpcAccessibilityService::ShutdownCallback(nsITimer* aTimer, void* aClosure) { MaybeShutdownAccService(nsAccessibilityService::eXPCOM); xpcAccessibilityService* xpcAccService = reinterpret_cast(aClosure); if (xpcAccService->mShutdownTimer) { xpcAccService->mShutdownTimer->Cancel(); xpcAccService->mShutdownTimer = nullptr; } } NS_IMETHODIMP_(MozExternalRefCountType) xpcAccessibilityService::AddRef(void) { MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(xpcAccessibilityService) MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); if (!nsAutoRefCnt::isThreadSafe) NS_ASSERT_OWNINGTHREAD(xpcAccessibilityService); nsrefcnt count = ++mRefCnt; NS_LOG_ADDREF(this, count, "xpcAccessibilityService", sizeof(*this)); // We want refcount to be > 1 because one reference is added in the XPCOM // accessibility service getter. if (mRefCnt > 1) { if (mShutdownTimer) { mShutdownTimer->Cancel(); mShutdownTimer = nullptr; } GetOrCreateAccService(nsAccessibilityService::eXPCOM); } return count; } NS_IMETHODIMP_(MozExternalRefCountType) xpcAccessibilityService::Release(void) { MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); if (!nsAutoRefCnt::isThreadSafe) { NS_ASSERT_OWNINGTHREAD(xpcAccessibilityService); } nsrefcnt count = --mRefCnt; NS_LOG_RELEASE(this, count, "xpcAccessibilityService"); if (count == 0) { if (!nsAutoRefCnt::isThreadSafe) { NS_ASSERT_OWNINGTHREAD(xpcAccessibilityService); } mRefCnt = 1; /* stabilize */ delete (this); return 0; } // When ref count goes down to 1 (held internally as a static reference), // it means that there are no more external references to the // xpcAccessibilityService and we can attempt to shut down acceessiblity // service. if (count == 1 && !mShutdownTimer) { NS_NewTimerWithFuncCallback( getter_AddRefs(mShutdownTimer), ShutdownCallback, this, 100, nsITimer::TYPE_ONE_SHOT, "xpcAccessibilityService::Release"); } return count; } NS_IMPL_QUERY_INTERFACE(xpcAccessibilityService, nsIAccessibilityService) NS_IMETHODIMP xpcAccessibilityService::GetApplicationAccessible( nsIAccessible** aAccessibleApplication) { NS_ENSURE_ARG_POINTER(aAccessibleApplication); NS_IF_ADDREF(*aAccessibleApplication = XPCApplicationAcc()); return NS_OK; } NS_IMETHODIMP xpcAccessibilityService::GetAccessibleFor(nsINode* aNode, nsIAccessible** aAccessible) { NS_ENSURE_ARG_POINTER(aAccessible); *aAccessible = nullptr; if (!aNode) { return NS_OK; } nsAccessibilityService* accService = GetAccService(); if (!accService) { return NS_ERROR_SERVICE_NOT_AVAILABLE; } DocAccessible* document = accService->GetDocAccessible(aNode->OwnerDoc()); if (document) { NS_IF_ADDREF(*aAccessible = ToXPC(document->GetAccessible(aNode))); } return NS_OK; } NS_IMETHODIMP xpcAccessibilityService::GetStringRole(uint32_t aRole, nsAString& aString) { nsAccessibilityService* accService = GetAccService(); if (!accService) { return NS_ERROR_SERVICE_NOT_AVAILABLE; } accService->GetStringRole(aRole, aString); return NS_OK; } NS_IMETHODIMP xpcAccessibilityService::GetStringStates(uint32_t aState, uint32_t aExtraState, nsISupports** aStringStates) { nsAccessibilityService* accService = GetAccService(); if (!accService) { return NS_ERROR_SERVICE_NOT_AVAILABLE; } accService->GetStringStates(aState, aExtraState, aStringStates); return NS_OK; } NS_IMETHODIMP xpcAccessibilityService::GetStringEventType(uint32_t aEventType, nsAString& aString) { nsAccessibilityService* accService = GetAccService(); if (!accService) { return NS_ERROR_SERVICE_NOT_AVAILABLE; } accService->GetStringEventType(aEventType, aString); return NS_OK; } NS_IMETHODIMP xpcAccessibilityService::GetStringRelationType(uint32_t aRelationType, nsAString& aString) { nsAccessibilityService* accService = GetAccService(); if (!accService) { return NS_ERROR_SERVICE_NOT_AVAILABLE; } accService->GetStringRelationType(aRelationType, aString); return NS_OK; } NS_IMETHODIMP xpcAccessibilityService::GetAccessibleFromCache(nsINode* aNode, nsIAccessible** aAccessible) { NS_ENSURE_ARG_POINTER(aAccessible); *aAccessible = nullptr; if (!aNode) { return NS_OK; } nsAccessibilityService* accService = GetAccService(); if (!accService) { return NS_ERROR_SERVICE_NOT_AVAILABLE; } // Search for an accessible in each of our per document accessible object // caches. If we don't find it, and the given node is itself a document, check // our cache of document accessibles (document cache). Note usually shutdown // document accessibles are not stored in the document cache, however an // "unofficially" shutdown document (i.e. not from DocManager) can still // exist in the document cache. Accessible* accessible = accService->FindAccessibleInCache(aNode); if (!accessible && aNode->IsDocument()) { accessible = mozilla::a11y::GetExistingDocAccessible(aNode->AsDocument()); } NS_IF_ADDREF(*aAccessible = ToXPC(accessible)); return NS_OK; } NS_IMETHODIMP xpcAccessibilityService::CreateAccessiblePivot(nsIAccessible* aRoot, nsIAccessiblePivot** aPivot) { NS_ENSURE_ARG_POINTER(aPivot); NS_ENSURE_ARG(aRoot); *aPivot = nullptr; Accessible* accessibleRoot = aRoot->ToInternalAccessible(); NS_ENSURE_TRUE(accessibleRoot, NS_ERROR_INVALID_ARG); nsAccessiblePivot* pivot = new nsAccessiblePivot(accessibleRoot); NS_ADDREF(*aPivot = pivot); return NS_OK; } NS_IMETHODIMP xpcAccessibilityService::SetLogging(const nsACString& aModules) { #ifdef A11Y_LOG logging::Enable(PromiseFlatCString(aModules)); #endif return NS_OK; } NS_IMETHODIMP xpcAccessibilityService::IsLogged(const nsAString& aModule, bool* aIsLogged) { NS_ENSURE_ARG_POINTER(aIsLogged); *aIsLogged = false; #ifdef A11Y_LOG *aIsLogged = logging::IsEnabled(aModule); #endif return NS_OK; } NS_IMETHODIMP xpcAccessibilityService::GetConsumers(nsAString& aString) { nsAccessibilityService* accService = GetAccService(); if (!accService) { return NS_ERROR_SERVICE_NOT_AVAILABLE; } accService->GetConsumers(aString); return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // NS_GetAccessibilityService //////////////////////////////////////////////////////////////////////////////// nsresult NS_GetAccessibilityService(nsIAccessibilityService** aResult) { NS_ENSURE_TRUE(aResult, NS_ERROR_NULL_POINTER); *aResult = nullptr; if (!GetOrCreateAccService(nsAccessibilityService::eXPCOM)) { return NS_ERROR_SERVICE_NOT_AVAILABLE; } xpcAccessibilityService* service = new xpcAccessibilityService(); NS_ENSURE_TRUE(service, NS_ERROR_OUT_OF_MEMORY); xpcAccessibilityService::gXPCAccessibilityService = service; NS_ADDREF(*aResult = service); return NS_OK; }