Bug 527003 - make sure a11y shutdown and start work with e10s. When accessibility service is requested, created, or shutdown, indicate if it is done by parent process, platform API or XPCOM. r=surkov, tbsaunde
MozReview-Commit-ID: KF5d6xaO83E
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -258,27 +258,26 @@ static const MarkupMapInfo sMarkupMapLis
////////////////////////////////////////////////////////////////////////////////
// nsAccessibilityService
////////////////////////////////////////////////////////////////////////////////
nsAccessibilityService *nsAccessibilityService::gAccessibilityService = nullptr;
ApplicationAccessible* nsAccessibilityService::gApplicationAccessible = nullptr;
xpcAccessibleApplication* nsAccessibilityService::gXPCApplicationAccessible = nullptr;
-bool nsAccessibilityService::gIsShutdown = true;
-bool nsAccessibilityService::gIsPlatformCaller = false;
+uint32_t nsAccessibilityService::gConsumers = 0;
nsAccessibilityService::nsAccessibilityService() :
DocManager(), FocusManager(), mMarkupMaps(ArrayLength(sMarkupMapList))
{
}
nsAccessibilityService::~nsAccessibilityService()
{
- NS_ASSERTION(gIsShutdown, "Accessibility wasn't shutdown!");
+ NS_ASSERTION(!gConsumers, "Accessibility wasn't shutdown!");
gAccessibilityService = nullptr;
}
////////////////////////////////////////////////////////////////////////////////
// nsIListenerChangeListener
NS_IMETHODIMP
nsAccessibilityService::ListenersChanged(nsIArray* aEventChanges)
@@ -952,17 +951,17 @@ nsAccessibilityService::GetStringRelatio
Accessible*
nsAccessibilityService::CreateAccessible(nsINode* aNode,
Accessible* aContext,
bool* aIsSubtreeHidden)
{
MOZ_ASSERT(aContext, "No context provided");
MOZ_ASSERT(aNode, "No node to create an accessible for");
- MOZ_ASSERT(!gIsShutdown, "No creation after shutdown");
+ MOZ_ASSERT(gConsumers, "No creation after shutdown");
if (aIsSubtreeHidden)
*aIsSubtreeHidden = false;
DocAccessible* document = aContext->Document();
MOZ_ASSERT(!document->GetAccessible(aNode),
"We already have an accessible for this node.");
@@ -1279,18 +1278,16 @@ nsAccessibilityService::Init()
NS_LITERAL_CSTRING("Active"));
#endif
#ifdef XP_WIN
sPendingPlugins = new nsTArray<nsCOMPtr<nsIContent> >;
sPluginTimers = new nsTArray<nsCOMPtr<nsITimer> >;
#endif
- gIsShutdown = false;
-
// Now its safe to start platform accessibility.
if (XRE_IsParentProcess())
PlatformInit();
statistics::A11yInitialized();
return true;
}
@@ -1298,19 +1295,19 @@ nsAccessibilityService::Init()
void
nsAccessibilityService::Shutdown()
{
// Application is going to be closed, shutdown accessibility and mark
// accessibility service as shutdown to prevent calls of its methods.
// Don't null accessibility service static member at this point to be safe
// if someone will try to operate with it.
- MOZ_ASSERT(!gIsShutdown, "Accessibility was shutdown already");
+ MOZ_ASSERT(gConsumers, "Accessibility was shutdown already");
- gIsShutdown = true;
+ gConsumers = 0;
// Remove observers.
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
static const char16_t kShutdownIndicator[] = { '0', 0 };
@@ -1339,17 +1336,16 @@ nsAccessibilityService::Shutdown()
NS_RELEASE(gApplicationAccessible);
gApplicationAccessible = nullptr;
NS_IF_RELEASE(gXPCApplicationAccessible);
gXPCApplicationAccessible = nullptr;
NS_RELEASE(gAccessibilityService);
gAccessibilityService = nullptr;
- gIsPlatformCaller = false;
}
already_AddRefed<Accessible>
nsAccessibilityService::CreateAccessibleByType(nsIContent* aContent,
DocAccessible* aDoc)
{
nsAutoString role;
nsCoreUtils::XBLBindingRole(aContent, role);
@@ -1773,45 +1769,51 @@ nsAccessibilityService::CreateAccessible
// Table or tree table accessible.
RefPtr<Accessible> accessible =
new XULTreeGridAccessibleWrap(aContent, aDoc, treeFrame);
return accessible.forget();
}
#endif
nsAccessibilityService*
-GetOrCreateAccService(bool aIsPlatformCaller)
+GetOrCreateAccService(uint32_t aRequestedBy)
{
- if (aIsPlatformCaller) {
- nsAccessibilityService::gIsPlatformCaller = aIsPlatformCaller;
- }
-
if (!nsAccessibilityService::gAccessibilityService) {
RefPtr<nsAccessibilityService> service = new nsAccessibilityService();
if (!service->Init()) {
service->Shutdown();
return nullptr;
}
}
MOZ_ASSERT(nsAccessibilityService::gAccessibilityService,
"Accessible service is not initialized.");
+ nsAccessibilityService::gConsumers |= aRequestedBy;
return nsAccessibilityService::gAccessibilityService;
}
-bool
-CanShutdownAccService()
+void
+MaybeShutdownAccService(uint32_t aRequestedBy)
{
nsAccessibilityService* accService = nsAccessibilityService::gAccessibilityService;
- if (!accService) {
- return false;
+ if (!accService ||
+ accService->IsShutdown() ||
+ !(nsAccessibilityService::gConsumers & aRequestedBy)) {
+ return;
}
- return !xpcAccessibilityService::IsInUse() &&
- !accService->IsPlatformCaller() && !accService->IsShutdown() &&
- !nsCoreUtils::AccEventObserversExist();
+
+ if (!nsCoreUtils::AccEventObserversExist() &&
+ !xpcAccessibilityService::IsInUse()) {
+ accService->Shutdown(); // Will unset all nsAccessibilityService::gConsumers
+ return;
+ }
+
+ if (aRequestedBy & nsAccessibilityService::eMainProcess) {
+ nsAccessibilityService::gConsumers &= ~nsAccessibilityService::eMainProcess;
+ }
}
////////////////////////////////////////////////////////////////////////////////
// Services
////////////////////////////////////////////////////////////////////////////////
namespace mozilla {
namespace a11y {
--- a/accessible/base/nsAccessibilityService.h
+++ b/accessible/base/nsAccessibilityService.h
@@ -190,22 +190,19 @@ public:
void FireAccessibleEvent(uint32_t aEvent, Accessible* aTarget);
// nsAccessibiltiyService
/**
* Return true if accessibility service has been shutdown.
*/
- static bool IsShutdown() { return gIsShutdown; }
-
- /**
- * Return true if accessibility service has been initialized by platform.
- */
- static bool IsPlatformCaller() { return gIsPlatformCaller; };
+ static bool IsShutdown() {
+ return gConsumers == 0;
+ };
/**
* Creates an accessible for the given DOM node.
*
* @param aNode [in] the given node
* @param aContext [in] context the accessible is created in
* @param aIsSubtreeHidden [out, optional] indicates whether the node's
* frame and its subtree is hidden
@@ -221,16 +218,39 @@ public:
}
/**
* Set the object attribute defined by markup for the given element.
*/
void MarkupAttributes(const nsIContent* aContent,
nsIPersistentProperties* aAttributes) const;
+ /**
+ * A list of possible accessibility service consumers. Accessibility service
+ * can be shut down by a consumer only when it is exclusively used by that
+ * consumer.
+ *
+ * eXPCOM - accessibility service is used by XPCOM. It can only be shut
+ * down if it's only used by XPCOM and not by main process
+ * and/or platform API.
+ *
+ * eMainProcess - accessibility service was started by main process in the
+ * content process. It can be shut down by main process if it's
+ * not also used by XPCOM.
+ *
+ * ePlatformAPI - accessibility service is used by the platform api in the
+ * main process. It can never can never be shut down.
+ */
+ enum ServiceConsumer
+ {
+ eXPCOM = 1 << 0,
+ eMainProcess = 1 << 1,
+ ePlatformAPI = 1 << 2,
+ };
+
private:
// nsAccessibilityService creation is controlled by friend
// GetOrCreateAccService, keep constructors private.
nsAccessibilityService();
nsAccessibilityService(const nsAccessibilityService&);
nsAccessibilityService& operator =(const nsAccessibilityService&);
private:
@@ -272,30 +292,25 @@ private:
/**
* Reference for application accessible instance.
*/
static mozilla::a11y::ApplicationAccessible* gApplicationAccessible;
static mozilla::a11y::xpcAccessibleApplication* gXPCApplicationAccessible;
/**
- * Indicates whether accessibility service was shutdown.
+ * Contains a list of accessibility service consumers.
*/
- static bool gIsShutdown;
-
- /**
- * Indicates whether accessibility service was initialized by platform.
- */
- static bool gIsPlatformCaller;
+ static uint32_t gConsumers;
nsDataHashtable<nsPtrHashKey<const nsIAtom>, const mozilla::a11y::MarkupMapInfo*> mMarkupMaps;
friend nsAccessibilityService* GetAccService();
- friend nsAccessibilityService* GetOrCreateAccService(bool);
- friend bool CanShutdownAccService();
+ friend nsAccessibilityService* GetOrCreateAccService(uint32_t);
+ friend void MaybeShutdownAccService(uint32_t);
friend mozilla::a11y::FocusManager* mozilla::a11y::FocusMgr();
friend mozilla::a11y::SelectionManager* mozilla::a11y::SelectionMgr();
friend mozilla::a11y::ApplicationAccessible* mozilla::a11y::ApplicationAcc();
friend mozilla::a11y::xpcAccessibleApplication* mozilla::a11y::XPCApplicationAcc();
friend class xpcAccessibilityService;
};
/**
@@ -305,22 +320,23 @@ inline nsAccessibilityService*
GetAccService()
{
return nsAccessibilityService::gAccessibilityService;
}
/**
* Return accessibility service instance; creating one if necessary.
*/
-nsAccessibilityService* GetOrCreateAccService(bool aIsPlatformCaller = true);
+nsAccessibilityService* GetOrCreateAccService(
+ uint32_t aRequestedBy = nsAccessibilityService::ePlatformAPI);
/**
- * Return a flag indicating if accessibility service can be shutdown.
+ * Shutdown accessibility service if needed.
*/
-bool CanShutdownAccService();
+void MaybeShutdownAccService(uint32_t aRequestedBy);
/**
* Return true if we're in a content process and not B2G.
*/
inline bool
IPCAccessibilityActive()
{
#ifdef MOZ_B2G
--- a/accessible/xpcom/xpcAccessibilityService.cpp
+++ b/accessible/xpcom/xpcAccessibilityService.cpp
@@ -18,20 +18,17 @@ using namespace mozilla::dom;
xpcAccessibilityService *xpcAccessibilityService::gXPCAccessibilityService = nullptr;
////////////////////////////////////////////////////////////////////////////////
// nsISupports
void
xpcAccessibilityService::ShutdownCallback(nsITimer* aTimer, void* aClosure)
{
- if (CanShutdownAccService()) {
- GetAccService()->Shutdown();
- }
-
+ MaybeShutdownAccService(nsAccessibilityService::eXPCOM);
xpcAccessibilityService* xpcAccService =
reinterpret_cast<xpcAccessibilityService*>(aClosure);
if (xpcAccService->mShutdownTimer) {
xpcAccService->mShutdownTimer->Cancel();
xpcAccService->mShutdownTimer = nullptr;
}
}
@@ -42,17 +39,17 @@ xpcAccessibilityService::AddRef(void)
MOZ_ASSERT_TYPE_OK_FOR_REFCOUNTING(xpcAccessibilityService)
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
if (!mRefCnt.isThreadSafe)
NS_ASSERT_OWNINGTHREAD(xpcAccessibilityService);
nsrefcnt count = ++mRefCnt;
NS_LOG_ADDREF(this, count, "xpcAccessibilityService", sizeof(*this));
if (mRefCnt > 1) {
- GetOrCreateAccService(false);
+ GetOrCreateAccService(nsAccessibilityService::eXPCOM);
}
return count;
}
NS_IMETHODIMP_(MozExternalRefCountType)
xpcAccessibilityService::Release(void)
{
@@ -233,17 +230,17 @@ xpcAccessibilityService::IsLogged(const
////////////////////////////////////////////////////////////////////////////////
nsresult
NS_GetAccessibilityService(nsIAccessibilityService** aResult)
{
NS_ENSURE_TRUE(aResult, NS_ERROR_NULL_POINTER);
*aResult = nullptr;
- GetOrCreateAccService(false);
+ GetOrCreateAccService(nsAccessibilityService::eXPCOM);
xpcAccessibilityService* service = new xpcAccessibilityService();
NS_ENSURE_TRUE(service, NS_ERROR_OUT_OF_MEMORY);
xpcAccessibilityService::gXPCAccessibilityService = service;
NS_ADDREF(*aResult = service);
return NS_OK;
}
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2468,17 +2468,28 @@ ContentChild::RecvFlushMemory(const nsSt
}
bool
ContentChild::RecvActivateA11y()
{
#ifdef ACCESSIBILITY
// Start accessibility in content process if it's running in chrome
// process.
- GetOrCreateAccService();
+ GetOrCreateAccService(nsAccessibilityService::eMainProcess);
+#endif
+ return true;
+}
+
+bool
+ContentChild::RecvShutdownA11y()
+{
+#ifdef ACCESSIBILITY
+ // Try to shutdown accessibility in content process if it's shutting down in
+ // chrome process.
+ MaybeShutdownAccService(nsAccessibilityService::eMainProcess);
#endif
return true;
}
bool
ContentChild::RecvGarbageCollect()
{
// Rebroadcast the "child-gc-request" so that workers will GC.
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -435,16 +435,17 @@ public:
virtual bool RecvUpdateDictionaryList(InfallibleTArray<nsString>&& aDictionaries) override;
virtual bool RecvAddPermission(const IPC::Permission& permission) override;
virtual bool RecvFlushMemory(const nsString& reason) override;
virtual bool RecvActivateA11y() override;
+ virtual bool RecvShutdownA11y() override;
virtual bool RecvGarbageCollect() override;
virtual bool RecvCycleCollect() override;
virtual bool RecvAppInfo(const nsCString& version, const nsCString& buildID,
const nsCString& name, const nsCString& UAName,
const nsCString& ID, const nsCString& vendor) override;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -270,16 +270,20 @@ using namespace mozilla::system;
#ifdef XP_WIN
#include "mozilla/widget/AudioSession.h"
#endif
#ifdef MOZ_CRASHREPORTER
#include "nsThread.h"
#endif
+#ifdef ACCESSIBILITY
+#include "nsAccessibilityService.h"
+#endif
+
// For VP9Benchmark::sBenchmarkFpsPref
#include "Benchmark.h"
static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
#if defined(XP_WIN)
// e10s forced enable pref, defined in nsAppRunner.cpp
extern const char* kForceEnableE10sPref;
@@ -2838,29 +2842,34 @@ ContentParent::Observe(nsISupports* aSub
Unused << SendNotifyPhoneStateChange(state);
}
else if(!strcmp(aTopic, NS_VOLUME_REMOVED)) {
nsString volName(aData);
Unused << SendVolumeRemoved(volName);
}
#endif
#ifdef ACCESSIBILITY
- // Make sure accessibility is running in content process when accessibility
- // gets initiated in chrome process.
- else if (aData && (*aData == '1') &&
- !strcmp(aTopic, "a11y-init-or-shutdown")) {
+ else if (aData && !strcmp(aTopic, "a11y-init-or-shutdown")) {
+ if (*aData == '1') {
+ // Make sure accessibility is running in content process when
+ // accessibility gets initiated in chrome process.
#if !defined(XP_WIN)
- Unused << SendActivateA11y();
+ Unused << SendActivateA11y();
#else
- // On Windows we currently only enable a11y in the content process
- // for testing purposes.
- if (Preferences::GetBool(kForceEnableE10sPref, false)) {
- Unused << SendActivateA11y();
+ // On Windows we currently only enable a11y in the content process
+ // for testing purposes.
+ if (Preferences::GetBool(kForceEnableE10sPref, false)) {
+ Unused << SendActivateA11y();
+ }
+#endif
+ } else {
+ // If possible, shut down accessibility in content process when
+ // accessibility gets shutdown in chrome process.
+ Unused << SendShutdownA11y();
}
-#endif
}
#endif
else if (!strcmp(aTopic, "app-theme-changed")) {
Unused << SendOnAppThemeChanged();
}
#ifdef MOZ_ENABLE_PROFILER_SPS
else if (!strcmp(aTopic, "profiler-started")) {
nsCOMPtr<nsIProfilerStartParams> params(do_QueryInterface(aSubject));
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -545,16 +545,21 @@ child:
async GarbageCollect();
async CycleCollect();
/**
* Start accessibility engine in content process.
*/
async ActivateA11y();
+ /**
+ * Shutdown accessibility engine in content process (if not in use).
+ */
+ async ShutdownA11y();
+
async AppInfo(nsCString version, nsCString buildID, nsCString name, nsCString UAName,
nsCString ID, nsCString vendor);
async AppInit();
/**
* Send ServiceWorkerRegistrationData to child process.
*/
async InitServiceWorkers(ServiceWorkerConfiguration aConfig);