Bug 527003 - make a11y service store info about its consumers. Only shut down a11y when there are no more consumers remaining. r=surkov, tbsaunde
authorYura Zenevich <yzenevich@mozilla.com>
Mon, 29 Aug 2016 11:06:48 -0400
changeset 311870 8f96a0ce280f311f2a8970806b50663611326da2
parent 311869 633c05b48792f4d55a13e43ad12034a53006797d
child 311871 cc3d60b79315de026a521f483ecbc3506df650db
push id30625
push userkwierso@gmail.com
push dateWed, 31 Aug 2016 00:35:07 +0000
treeherdermozilla-central@5931a8286060 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssurkov, tbsaunde
bugs527003
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 527003 - make a11y service store info about its consumers. Only shut down a11y when there are no more consumers remaining. r=surkov, tbsaunde MozReview-Commit-ID: KF5d6xaO83E
accessible/base/nsAccessibilityService.cpp
accessible/base/nsAccessibilityService.h
accessible/xpcom/xpcAccessibilityService.cpp
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/PContent.ipdl
--- 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(IsShutdown(), "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,56 @@ 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 aNewConsumer)
 {
-  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 |= aNewConsumer;
   return nsAccessibilityService::gAccessibilityService;
 }
 
-bool
-CanShutdownAccService()
+void
+MaybeShutdownAccService(uint32_t aFormerConsumer)
 {
-  nsAccessibilityService* accService = nsAccessibilityService::gAccessibilityService;
-  if (!accService) {
-    return false;
+  nsAccessibilityService* accService =
+    nsAccessibilityService::gAccessibilityService;
+
+  if (!accService || accService->IsShutdown()) {
+    return;
   }
-  return !xpcAccessibilityService::IsInUse() &&
-         !accService->IsPlatformCaller() && !accService->IsShutdown() &&
-         !nsCoreUtils::AccEventObserversExist();
+
+  if (nsCoreUtils::AccEventObserversExist() ||
+      xpcAccessibilityService::IsInUse()) {
+    // Still used by XPCOM
+    nsAccessibilityService::gConsumers =
+      (nsAccessibilityService::gConsumers & ~aFormerConsumer) |
+      nsAccessibilityService::eXPCOM;
+    return;
+  }
+
+  if (nsAccessibilityService::gConsumers & ~aFormerConsumer) {
+    nsAccessibilityService::gConsumers &= ~aFormerConsumer;
+  } else {
+    accService->Shutdown(); // Will unset all nsAccessibilityService::gConsumers
+  }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Services
 ////////////////////////////////////////////////////////////////////////////////
 
 namespace mozilla {
 namespace a11y {
--- a/accessible/base/nsAccessibilityService.h
+++ b/accessible/base/nsAccessibilityService.h
@@ -190,22 +190,20 @@ 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 +219,35 @@ 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 only be shut down when there are no remaining consumers.
+   *
+   * eXPCOM       - accessibility service is used by XPCOM.
+   *
+   * eMainProcess - accessibility service was started by main process in the
+   *                content process.
+   *
+   * ePlatformAPI - accessibility service is used by the platform api in the
+   *                main process.
+   */
+  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 +289,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 set 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 +317,23 @@ inline nsAccessibilityService*
 GetAccService()
 {
   return nsAccessibilityService::gAccessibilityService;
 }
 
 /**
  * Return accessibility service instance; creating one if necessary.
  */
-nsAccessibilityService* GetOrCreateAccService(bool aIsPlatformCaller = true);
+nsAccessibilityService* GetOrCreateAccService(
+  uint32_t aNewConsumer = nsAccessibilityService::ePlatformAPI);
 
 /**
- * Return a flag indicating if accessibility service can be shutdown.
+ * Shutdown accessibility service if needed.
  */
-bool CanShutdownAccService();
+void MaybeShutdownAccService(uint32_t aFormerConsumer);
 
 /**
  * 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
@@ -2472,17 +2472,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;
@@ -2837,29 +2841,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);