Bug 527003 - adding support for dynamic nsAccessibilityService shutdown. draft
authorYura Zenevich <yzenevich@mozilla.com>
Tue, 21 Jun 2016 16:46:28 -0400
changeset 380369 abf2c7f58576849edc86460701525e2fea3cf36f
parent 380368 290beeedc8ba0e8d8e9854d241835d6edda64b11
child 523714 05c7316bc2ca0400584c897f9a8c484a15431ce7
push id21208
push useryura.zenevich@gmail.com
push dateTue, 21 Jun 2016 20:51:29 +0000
bugs527003, 100644
milestone50.0a1
Bug 527003 - adding support for dynamic nsAccessibilityService shutdown. MozReview-Commit-ID: 669NJIUeq0d --- accessible/base/DocManager.cpp | 25 +- accessible/base/nsAccessibilityService.cpp | 23 +- accessible/base/nsAccessibilityService.h | 19 +- accessible/moz.build | 5 +- accessible/tests/browser/browser.ini | 53 +--- accessible/tests/browser/browser_shutdown.js | 33 +++ accessible/tests/browser/{ => e10s}/browser.ini | 1 + .../{ => e10s}/browser_caching_attributes.js | 0 .../{ => e10s}/browser_caching_description.js | 0 .../browser/{ => e10s}/browser_caching_name.js | 0 .../{ => e10s}/browser_caching_relations.js | 0 .../browser/{ => e10s}/browser_caching_states.js | 0 .../browser/{ => e10s}/browser_caching_value.js | 0 .../browser/{ => e10s}/browser_events_caretmove.js | 0 .../browser/{ => e10s}/browser_events_hide.js | 0 .../browser/{ => e10s}/browser_events_show.js | 0 .../{ => e10s}/browser_events_statechange.js | 0 .../{ => e10s}/browser_events_textchange.js | 0 .../{ => e10s}/browser_treeupdate_ariadialog.js | 0 .../{ => e10s}/browser_treeupdate_ariaowns.js | 0 .../{ => e10s}/browser_treeupdate_canvas.js | 0 .../{ => e10s}/browser_treeupdate_cssoverflow.js | 0 .../browser/{ => e10s}/browser_treeupdate_doc.js | 0 .../{ => e10s}/browser_treeupdate_gencontent.js | 0 .../{ => e10s}/browser_treeupdate_hidden.js | 0 .../{ => e10s}/browser_treeupdate_imagemap.js | 0 .../browser/{ => e10s}/browser_treeupdate_list.js | 0 .../browser_treeupdate_list_editabledoc.js | 0 .../{ => e10s}/browser_treeupdate_listener.js | 0 .../{ => e10s}/browser_treeupdate_optgroup.js | 0 .../{ => e10s}/browser_treeupdate_removal.js | 0 .../browser/{ => e10s}/browser_treeupdate_table.js | 0 .../{ => e10s}/browser_treeupdate_textleaf.js | 0 .../{ => e10s}/browser_treeupdate_visibility.js | 0 .../{ => e10s}/browser_treeupdate_whitespace.js | 0 .../{ => e10s}/doc_treeupdate_ariadialog.html | 0 .../{ => e10s}/doc_treeupdate_ariaowns.html | 0 .../{ => e10s}/doc_treeupdate_imagemap.html | 0 .../{ => e10s}/doc_treeupdate_removal.xhtml | 0 .../{ => e10s}/doc_treeupdate_visibility.html | 0 .../{ => e10s}/doc_treeupdate_whitespace.html | 0 accessible/tests/browser/{ => e10s}/events.js | 0 accessible/tests/browser/e10s/head.js | 84 ++++++ accessible/tests/browser/head.js | 301 +-------------------- .../tests/browser/{head.js => shared-head.js} | 82 +----- accessible/xpcom/xpcAccessibilityService.cpp | 69 ++++- accessible/xpcom/xpcAccessibilityService.h | 16 +- 47 files changed, 254 insertions(+), 457 deletions(-) create mode 100644 accessible/tests/browser/browser_shutdown.js copy accessible/tests/browser/{ => e10s}/browser.ini (97%) rename accessible/tests/browser/{ => e10s}/browser_caching_attributes.js (100%) rename accessible/tests/browser/{ => e10s}/browser_caching_description.js (100%) rename accessible/tests/browser/{ => e10s}/browser_caching_name.js (100%) rename accessible/tests/browser/{ => e10s}/browser_caching_relations.js (100%) rename accessible/tests/browser/{ => e10s}/browser_caching_states.js (100%) rename accessible/tests/browser/{ => e10s}/browser_caching_value.js (100%) rename accessible/tests/browser/{ => e10s}/browser_events_caretmove.js (100%) rename accessible/tests/browser/{ => e10s}/browser_events_hide.js (100%) rename accessible/tests/browser/{ => e10s}/browser_events_show.js (100%) rename accessible/tests/browser/{ => e10s}/browser_events_statechange.js (100%) rename accessible/tests/browser/{ => e10s}/browser_events_textchange.js (100%) rename accessible/tests/browser/{ => e10s}/browser_treeupdate_ariadialog.js (100%) rename accessible/tests/browser/{ => e10s}/browser_treeupdate_ariaowns.js (100%) rename accessible/tests/browser/{ => e10s}/browser_treeupdate_canvas.js (100%) rename accessible/tests/browser/{ => e10s}/browser_treeupdate_cssoverflow.js (100%) rename accessible/tests/browser/{ => e10s}/browser_treeupdate_doc.js (100%) rename accessible/tests/browser/{ => e10s}/browser_treeupdate_gencontent.js (100%) rename accessible/tests/browser/{ => e10s}/browser_treeupdate_hidden.js (100%) rename accessible/tests/browser/{ => e10s}/browser_treeupdate_imagemap.js (100%) rename accessible/tests/browser/{ => e10s}/browser_treeupdate_list.js (100%) rename accessible/tests/browser/{ => e10s}/browser_treeupdate_list_editabledoc.js (100%) rename accessible/tests/browser/{ => e10s}/browser_treeupdate_listener.js (100%) rename accessible/tests/browser/{ => e10s}/browser_treeupdate_optgroup.js (100%) rename accessible/tests/browser/{ => e10s}/browser_treeupdate_removal.js (100%) rename accessible/tests/browser/{ => e10s}/browser_treeupdate_table.js (100%) rename accessible/tests/browser/{ => e10s}/browser_treeupdate_textleaf.js (100%) rename accessible/tests/browser/{ => e10s}/browser_treeupdate_visibility.js (100%) rename accessible/tests/browser/{ => e10s}/browser_treeupdate_whitespace.js (100%) rename accessible/tests/browser/{ => e10s}/doc_treeupdate_ariadialog.html (100%) rename accessible/tests/browser/{ => e10s}/doc_treeupdate_ariaowns.html (100%) rename accessible/tests/browser/{ => e10s}/doc_treeupdate_imagemap.html (100%) rename accessible/tests/browser/{ => e10s}/doc_treeupdate_removal.xhtml (100%) rename accessible/tests/browser/{ => e10s}/doc_treeupdate_visibility.html (100%) rename accessible/tests/browser/{ => e10s}/doc_treeupdate_whitespace.html (100%) rename accessible/tests/browser/{ => e10s}/events.js (100%) create mode 100644 accessible/tests/browser/e10s/head.js copy accessible/tests/browser/{head.js => shared-head.js} (71%)
accessible/base/DocManager.cpp
accessible/base/nsAccessibilityService.cpp
accessible/base/nsAccessibilityService.h
accessible/moz.build
accessible/tests/browser/browser.ini
accessible/tests/browser/browser_caching_attributes.js
accessible/tests/browser/browser_caching_description.js
accessible/tests/browser/browser_caching_name.js
accessible/tests/browser/browser_caching_relations.js
accessible/tests/browser/browser_caching_states.js
accessible/tests/browser/browser_caching_value.js
accessible/tests/browser/browser_events_caretmove.js
accessible/tests/browser/browser_events_hide.js
accessible/tests/browser/browser_events_show.js
accessible/tests/browser/browser_events_statechange.js
accessible/tests/browser/browser_events_textchange.js
accessible/tests/browser/browser_shutdown.js
accessible/tests/browser/browser_treeupdate_ariadialog.js
accessible/tests/browser/browser_treeupdate_ariaowns.js
accessible/tests/browser/browser_treeupdate_canvas.js
accessible/tests/browser/browser_treeupdate_cssoverflow.js
accessible/tests/browser/browser_treeupdate_doc.js
accessible/tests/browser/browser_treeupdate_gencontent.js
accessible/tests/browser/browser_treeupdate_hidden.js
accessible/tests/browser/browser_treeupdate_imagemap.js
accessible/tests/browser/browser_treeupdate_list.js
accessible/tests/browser/browser_treeupdate_list_editabledoc.js
accessible/tests/browser/browser_treeupdate_listener.js
accessible/tests/browser/browser_treeupdate_optgroup.js
accessible/tests/browser/browser_treeupdate_removal.js
accessible/tests/browser/browser_treeupdate_table.js
accessible/tests/browser/browser_treeupdate_textleaf.js
accessible/tests/browser/browser_treeupdate_visibility.js
accessible/tests/browser/browser_treeupdate_whitespace.js
accessible/tests/browser/doc_treeupdate_ariadialog.html
accessible/tests/browser/doc_treeupdate_ariaowns.html
accessible/tests/browser/doc_treeupdate_imagemap.html
accessible/tests/browser/doc_treeupdate_removal.xhtml
accessible/tests/browser/doc_treeupdate_visibility.html
accessible/tests/browser/doc_treeupdate_whitespace.html
accessible/tests/browser/e10s/browser.ini
accessible/tests/browser/e10s/browser_caching_attributes.js
accessible/tests/browser/e10s/browser_caching_description.js
accessible/tests/browser/e10s/browser_caching_name.js
accessible/tests/browser/e10s/browser_caching_relations.js
accessible/tests/browser/e10s/browser_caching_states.js
accessible/tests/browser/e10s/browser_caching_value.js
accessible/tests/browser/e10s/browser_events_caretmove.js
accessible/tests/browser/e10s/browser_events_hide.js
accessible/tests/browser/e10s/browser_events_show.js
accessible/tests/browser/e10s/browser_events_statechange.js
accessible/tests/browser/e10s/browser_events_textchange.js
accessible/tests/browser/e10s/browser_treeupdate_ariadialog.js
accessible/tests/browser/e10s/browser_treeupdate_ariaowns.js
accessible/tests/browser/e10s/browser_treeupdate_canvas.js
accessible/tests/browser/e10s/browser_treeupdate_cssoverflow.js
accessible/tests/browser/e10s/browser_treeupdate_doc.js
accessible/tests/browser/e10s/browser_treeupdate_gencontent.js
accessible/tests/browser/e10s/browser_treeupdate_hidden.js
accessible/tests/browser/e10s/browser_treeupdate_imagemap.js
accessible/tests/browser/e10s/browser_treeupdate_list.js
accessible/tests/browser/e10s/browser_treeupdate_list_editabledoc.js
accessible/tests/browser/e10s/browser_treeupdate_listener.js
accessible/tests/browser/e10s/browser_treeupdate_optgroup.js
accessible/tests/browser/e10s/browser_treeupdate_removal.js
accessible/tests/browser/e10s/browser_treeupdate_table.js
accessible/tests/browser/e10s/browser_treeupdate_textleaf.js
accessible/tests/browser/e10s/browser_treeupdate_visibility.js
accessible/tests/browser/e10s/browser_treeupdate_whitespace.js
accessible/tests/browser/e10s/doc_treeupdate_ariadialog.html
accessible/tests/browser/e10s/doc_treeupdate_ariaowns.html
accessible/tests/browser/e10s/doc_treeupdate_imagemap.html
accessible/tests/browser/e10s/doc_treeupdate_removal.xhtml
accessible/tests/browser/e10s/doc_treeupdate_visibility.html
accessible/tests/browser/e10s/doc_treeupdate_whitespace.html
accessible/tests/browser/e10s/events.js
accessible/tests/browser/e10s/head.js
accessible/tests/browser/events.js
accessible/tests/browser/head.js
accessible/tests/browser/shared-head.js
accessible/xpcom/xpcAccessibilityService.cpp
accessible/xpcom/xpcAccessibilityService.h
--- a/accessible/base/DocManager.cpp
+++ b/accessible/base/DocManager.cpp
@@ -84,24 +84,29 @@ DocManager::FindAccessibleInCache(nsINod
   }
   return nullptr;
 }
 
 void
 DocManager::NotifyOfDocumentShutdown(DocAccessible* aDocument,
                                      nsIDocument* aDOMDocument)
 {
+  RemoveListeners(aDOMDocument);
+
+  if (nsAccessibilityService::IsShutdown()) {
+    return;
+  }
+
   xpcAccessibleDocument* xpcDoc = mXPCDocumentCache.GetWeak(aDocument);
   if (xpcDoc) {
     xpcDoc->Shutdown();
     mXPCDocumentCache.Remove(aDocument);
   }
 
   mDocAccessibleCache.Remove(aDOMDocument);
-  RemoveListeners(aDOMDocument);
 }
 
 void
 DocManager::NotifyOfRemoteDocShutdown(DocAccessibleParent* aDoc)
 {
   xpcAccessibleDocument* doc = GetCachedXPCDocument(aDoc);
   if (doc) {
     doc->Shutdown();
@@ -525,29 +530,41 @@ DocManager::CreateDocOrRootAccessible(ns
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // DocManager static
 
 void
 DocManager::ClearDocCache()
 {
-  // This unusual do-one-element-per-iterator approach is required because each
-  // DocAccessible is removed elsewhere upon its Shutdown() method being
-  // called, which invalidates the existing iterator.
   while (mDocAccessibleCache.Count() > 0) {
     auto iter = mDocAccessibleCache.Iter();
     MOZ_ASSERT(!iter.Done());
     DocAccessible* docAcc = iter.UserData();
     NS_ASSERTION(docAcc,
                  "No doc accessible for the object in doc accessible cache!");
     if (docAcc) {
       docAcc->Shutdown();
     }
+
+    iter.Remove();
   }
+
+  while(mXPCDocumentCache.Count() > 0) {
+    auto iter = mXPCDocumentCache.Iter();
+    MOZ_ASSERT(!iter.Done());
+    xpcAccessibleDocument* xpcDoc = iter.UserData();
+    NS_ASSERTION(xpcDoc, "No xpc doc for the object in xpc doc cache!");
+
+    if (xpcDoc) {
+      xpcDoc->Shutdown();
+    }
+
+    iter.Remove();
+   }
 }
 
 void
 DocManager::RemoteDocAdded(DocAccessibleParent* aDoc)
 {
   if (!sRemoteDocuments) {
     sRemoteDocuments = new nsTArray<DocAccessibleParent*>;
     ClearOnShutdown(&sRemoteDocuments);
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -258,16 +258,17 @@ static const MarkupMapInfo sMarkupMapLis
 ////////////////////////////////////////////////////////////////////////////////
 // nsAccessibilityService
 ////////////////////////////////////////////////////////////////////////////////
 
 nsAccessibilityService *nsAccessibilityService::gAccessibilityService = nullptr;
 ApplicationAccessible* nsAccessibilityService::gApplicationAccessible = nullptr;
 xpcAccessibleApplication* nsAccessibilityService::gXPCApplicationAccessible = nullptr;
 bool nsAccessibilityService::gIsShutdown = true;
+bool nsAccessibilityService::gIsPlatform = false;
 
 nsAccessibilityService::nsAccessibilityService() :
   DocManager(), FocusManager(), mMarkupMaps(ArrayLength(sMarkupMapList))
 {
 }
 
 nsAccessibilityService::~nsAccessibilityService()
 {
@@ -1105,16 +1106,25 @@ nsAccessibilityService::Init()
     PlatformInit();
 
   return true;
 }
 
 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.
+
+  NS_ASSERTION(!gIsShutdown, "Accessibility was shutdown already");
+
+  gIsShutdown = true;
+
   // 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 };
     observerService->NotifyObservers(nullptr, "a11y-init-or-shutdown", kShutdownIndicator);
@@ -1130,34 +1140,29 @@ nsAccessibilityService::Shutdown()
 
   uint32_t timerCount = sPluginTimers->Length();
   for (uint32_t i = 0; i < timerCount; i++)
     sPluginTimers->ElementAt(i)->Cancel();
 
   sPluginTimers = nullptr;
 #endif
 
-  // 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.
-
-  NS_ASSERTION(!gIsShutdown, "Accessibility was shutdown already");
-
-  gIsShutdown = true;
-
   if (XRE_IsParentProcess())
     PlatformShutdown();
 
   gApplicationAccessible->Shutdown();
   NS_RELEASE(gApplicationAccessible);
   gApplicationAccessible = nullptr;
 
   NS_IF_RELEASE(gXPCApplicationAccessible);
   gXPCApplicationAccessible = nullptr;
+
+  NS_RELEASE(gAccessibilityService);
+  gAccessibilityService = nullptr;
+  gIsPlatform = false;
 }
 
 already_AddRefed<Accessible>
 nsAccessibilityService::CreateAccessibleByType(nsIContent* aContent,
                                                DocAccessible* aDoc)
 {
   nsAutoString role;
   nsCoreUtils::XBLBindingRole(aContent, role);
--- a/accessible/base/nsAccessibilityService.h
+++ b/accessible/base/nsAccessibilityService.h
@@ -181,16 +181,21 @@ public:
   // 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 IsPlatform() { return gIsPlatform; };
+
+  /**
    * 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
    */
   Accessible* CreateAccessible(nsINode* aNode, Accessible* aContext,
@@ -259,41 +264,51 @@ private:
   static mozilla::a11y::ApplicationAccessible* gApplicationAccessible;
   static mozilla::a11y::xpcAccessibleApplication* gXPCApplicationAccessible;
 
   /**
    * Indicates whether accessibility service was shutdown.
    */
   static bool gIsShutdown;
 
+  /**
+   * Indicates whether accessibility service was initialized by platform.
+   */
+  static bool gIsPlatform;
+
   nsDataHashtable<nsPtrHashKey<const nsIAtom>, const mozilla::a11y::MarkupMapInfo*> mMarkupMaps;
 
   friend nsAccessibilityService* GetAccService();
-  friend nsAccessibilityService* GetOrCreateAccService();
+  friend nsAccessibilityService* GetOrCreateAccService(bool);
   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;
 };
 
 /**
  * Return the accessibility service instance. (Handy global function)
  */
 inline nsAccessibilityService*
 GetAccService()
 {
   return nsAccessibilityService::gAccessibilityService;
 }
 
 /**
  * Return accessibility service instance; creating one if necessary.
  */
 inline nsAccessibilityService*
-GetOrCreateAccService()
+GetOrCreateAccService(bool aIsPlatform = true)
 {
+  if (aIsPlatform) {
+    nsAccessibilityService::gIsPlatform = aIsPlatform;
+  }
+
   if (nsAccessibilityService::gAccessibilityService) {
     return nsAccessibilityService::gAccessibilityService;
   }
 
   RefPtr<nsAccessibilityService> service = new nsAccessibilityService();
 
   if (!service->Init()) {
     service->Shutdown();
--- a/accessible/moz.build
+++ b/accessible/moz.build
@@ -17,9 +17,12 @@ else:
 
 DIRS += ['base', 'generic', 'html', 'interfaces', 'ipc', 'jsat', 'xpcom']
 
 if CONFIG['MOZ_XUL']:
     DIRS += ['xul']
 
 TEST_DIRS += ['tests/mochitest']
 
-BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
+BROWSER_CHROME_MANIFESTS += [
+  'tests/browser/browser.ini',
+  'tests/browser/e10s/browser.ini'
+]
--- a/accessible/tests/browser/browser.ini
+++ b/accessible/tests/browser/browser.ini
@@ -1,54 +1,7 @@
 [DEFAULT]
-skip-if = (e10s && os == 'win') # Bug 1269369: Document loaded event does not fire in Windows
-support-files =
-  events.js
-  head.js
-  doc_treeupdate_ariadialog.html
-  doc_treeupdate_ariaowns.html
-  doc_treeupdate_imagemap.html
-  doc_treeupdate_removal.xhtml
-  doc_treeupdate_visibility.html
-  doc_treeupdate_whitespace.html
-  !/accessible/tests/mochitest/*.js
-  !/accessible/tests/mochitest/letters.gif
-  !/accessible/tests/mochitest/moz.png
-
-# Caching tests
-[browser_caching_attributes.js]
-[browser_caching_description.js]
-[browser_caching_name.js]
-skip-if = e10s
-[browser_caching_relations.js]
-[browser_caching_states.js]
-[browser_caching_value.js]
-skip-if = e10s # Bug 1276721: QueryInterface is not working with proxies.
 
-# Events tests
-[browser_events_caretmove.js]
-[browser_events_hide.js]
-[browser_events_show.js]
-[browser_events_statechange.js]
-[browser_events_textchange.js]
+support-files =
+  head.js
+  shared-head.js
 
-# Tree update tests
-[browser_treeupdate_ariadialog.js]
-[browser_treeupdate_ariaowns.js]
-skip-if = e10s
-[browser_treeupdate_canvas.js]
-[browser_treeupdate_cssoverflow.js]
-[browser_treeupdate_doc.js]
-skip-if = e10s
-[browser_treeupdate_gencontent.js]
-[browser_treeupdate_hidden.js]
-[browser_treeupdate_imagemap.js]
-skip-if = e10s
-[browser_treeupdate_list.js]
-[browser_treeupdate_list_editabledoc.js]
-[browser_treeupdate_listener.js]
-[browser_treeupdate_optgroup.js]
-[browser_treeupdate_removal.js]
-[browser_treeupdate_table.js]
-[browser_treeupdate_textleaf.js]
-[browser_treeupdate_visibility.js]
-[browser_treeupdate_whitespace.js]
-skip-if = true # Failing due to incorrect index of test container children on document load.
+[browser_shutdown.js]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/browser_shutdown.js
@@ -0,0 +1,33 @@
+/* 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/. */
+
+'use strict';
+
+/* global add_task */
+
+function a11yInitOrShutdownPromise() {
+  return new Promise(resolve => {
+    let observe = (subject, topic, data) => {
+      Services.obs.removeObserver(observe, 'a11y-init-or-shutdown');
+      resolve(data);
+    };
+    Services.obs.addObserver(observe, 'a11y-init-or-shutdown', false);
+  });
+}
+
+add_task(function* () {
+  info('About to create a service');
+  let a11yInitOrShutdown = a11yInitOrShutdownPromise();
+  let accService = Cc['@mozilla.org/accessibilityService;1'].getService(
+    Ci.nsIAccessibilityService);
+  let flag = yield a11yInitOrShutdown;
+  ok(accService, 'Service initialized');
+  is(flag, '1', 'Service initialized with correct event');
+
+  a11yInitOrShutdown = a11yInitOrShutdownPromise();
+  accService = null;
+  ok(!accService, 'Service is removed');
+  flag = yield a11yInitOrShutdown;
+  is(flag, '0', 'Service shutdown with correct event');
+});
copy from accessible/tests/browser/browser.ini
copy to accessible/tests/browser/e10s/browser.ini
--- a/accessible/tests/browser/browser.ini
+++ b/accessible/tests/browser/e10s/browser.ini
@@ -4,16 +4,17 @@ support-files =
   events.js
   head.js
   doc_treeupdate_ariadialog.html
   doc_treeupdate_ariaowns.html
   doc_treeupdate_imagemap.html
   doc_treeupdate_removal.xhtml
   doc_treeupdate_visibility.html
   doc_treeupdate_whitespace.html
+  !/accessible/tests/browser/shared-head.js
   !/accessible/tests/mochitest/*.js
   !/accessible/tests/mochitest/letters.gif
   !/accessible/tests/mochitest/moz.png
 
 # Caching tests
 [browser_caching_attributes.js]
 [browser_caching_description.js]
 [browser_caching_name.js]
rename from accessible/tests/browser/browser_caching_attributes.js
rename to accessible/tests/browser/e10s/browser_caching_attributes.js
rename from accessible/tests/browser/browser_caching_description.js
rename to accessible/tests/browser/e10s/browser_caching_description.js
rename from accessible/tests/browser/browser_caching_name.js
rename to accessible/tests/browser/e10s/browser_caching_name.js
rename from accessible/tests/browser/browser_caching_relations.js
rename to accessible/tests/browser/e10s/browser_caching_relations.js
rename from accessible/tests/browser/browser_caching_states.js
rename to accessible/tests/browser/e10s/browser_caching_states.js
rename from accessible/tests/browser/browser_caching_value.js
rename to accessible/tests/browser/e10s/browser_caching_value.js
rename from accessible/tests/browser/browser_events_caretmove.js
rename to accessible/tests/browser/e10s/browser_events_caretmove.js
rename from accessible/tests/browser/browser_events_hide.js
rename to accessible/tests/browser/e10s/browser_events_hide.js
rename from accessible/tests/browser/browser_events_show.js
rename to accessible/tests/browser/e10s/browser_events_show.js
rename from accessible/tests/browser/browser_events_statechange.js
rename to accessible/tests/browser/e10s/browser_events_statechange.js
rename from accessible/tests/browser/browser_events_textchange.js
rename to accessible/tests/browser/e10s/browser_events_textchange.js
rename from accessible/tests/browser/browser_treeupdate_ariadialog.js
rename to accessible/tests/browser/e10s/browser_treeupdate_ariadialog.js
rename from accessible/tests/browser/browser_treeupdate_ariaowns.js
rename to accessible/tests/browser/e10s/browser_treeupdate_ariaowns.js
rename from accessible/tests/browser/browser_treeupdate_canvas.js
rename to accessible/tests/browser/e10s/browser_treeupdate_canvas.js
rename from accessible/tests/browser/browser_treeupdate_cssoverflow.js
rename to accessible/tests/browser/e10s/browser_treeupdate_cssoverflow.js
rename from accessible/tests/browser/browser_treeupdate_doc.js
rename to accessible/tests/browser/e10s/browser_treeupdate_doc.js
rename from accessible/tests/browser/browser_treeupdate_gencontent.js
rename to accessible/tests/browser/e10s/browser_treeupdate_gencontent.js
rename from accessible/tests/browser/browser_treeupdate_hidden.js
rename to accessible/tests/browser/e10s/browser_treeupdate_hidden.js
rename from accessible/tests/browser/browser_treeupdate_imagemap.js
rename to accessible/tests/browser/e10s/browser_treeupdate_imagemap.js
rename from accessible/tests/browser/browser_treeupdate_list.js
rename to accessible/tests/browser/e10s/browser_treeupdate_list.js
rename from accessible/tests/browser/browser_treeupdate_list_editabledoc.js
rename to accessible/tests/browser/e10s/browser_treeupdate_list_editabledoc.js
rename from accessible/tests/browser/browser_treeupdate_listener.js
rename to accessible/tests/browser/e10s/browser_treeupdate_listener.js
rename from accessible/tests/browser/browser_treeupdate_optgroup.js
rename to accessible/tests/browser/e10s/browser_treeupdate_optgroup.js
rename from accessible/tests/browser/browser_treeupdate_removal.js
rename to accessible/tests/browser/e10s/browser_treeupdate_removal.js
rename from accessible/tests/browser/browser_treeupdate_table.js
rename to accessible/tests/browser/e10s/browser_treeupdate_table.js
rename from accessible/tests/browser/browser_treeupdate_textleaf.js
rename to accessible/tests/browser/e10s/browser_treeupdate_textleaf.js
rename from accessible/tests/browser/browser_treeupdate_visibility.js
rename to accessible/tests/browser/e10s/browser_treeupdate_visibility.js
rename from accessible/tests/browser/browser_treeupdate_whitespace.js
rename to accessible/tests/browser/e10s/browser_treeupdate_whitespace.js
rename from accessible/tests/browser/doc_treeupdate_ariadialog.html
rename to accessible/tests/browser/e10s/doc_treeupdate_ariadialog.html
rename from accessible/tests/browser/doc_treeupdate_ariaowns.html
rename to accessible/tests/browser/e10s/doc_treeupdate_ariaowns.html
rename from accessible/tests/browser/doc_treeupdate_imagemap.html
rename to accessible/tests/browser/e10s/doc_treeupdate_imagemap.html
rename from accessible/tests/browser/doc_treeupdate_removal.xhtml
rename to accessible/tests/browser/e10s/doc_treeupdate_removal.xhtml
rename from accessible/tests/browser/doc_treeupdate_visibility.html
rename to accessible/tests/browser/e10s/doc_treeupdate_visibility.html
rename from accessible/tests/browser/doc_treeupdate_whitespace.html
rename to accessible/tests/browser/e10s/doc_treeupdate_whitespace.html
rename from accessible/tests/browser/events.js
rename to accessible/tests/browser/e10s/events.js
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/e10s/head.js
@@ -0,0 +1,84 @@
+/* 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/. */
+
+'use strict';
+
+/* global EVENT_DOCUMENT_LOAD_COMPLETE, CURRENT_CONTENT_DIR, loadFrameScripts */
+
+/* exported addAccessibleTask */
+
+// Load the shared-head file first.
+Services.scriptloader.loadSubScript(
+  'chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js',
+  this);
+
+/**
+ * A wrapper around browser test add_task that triggers an accessible test task
+ * as a new browser test task with given document, data URL or markup snippet.
+ * @param  {String}             doc    URL (relative to current directory) or
+ *                                     data URL or markup snippet that is used
+ *                                     to test content with
+ * @param  {Function|Function*} task   a generator or a function with tests to
+ *                                     run
+ */
+function addAccessibleTask(doc, task) {
+  add_task(function*() {
+    let url;
+    if (doc.includes('doc_')) {
+      url = `${CURRENT_CONTENT_DIR}e10s/${doc}`;
+    } else {
+      // Assume it's a markup snippet.
+      url = `data:text/html,
+        <html>
+          <head>
+            <meta charset="utf-8"/>
+            <title>Accessibility Test</title>
+          </head>
+          <body id="body">${doc}</body>
+        </html>`;
+    }
+
+    registerCleanupFunction(() => {
+      let observers = Services.obs.enumerateObservers('accessible-event');
+      while (observers.hasMoreElements()) {
+        Services.obs.removeObserver(
+          observers.getNext().QueryInterface(Ci.nsIObserver),
+          'accessible-event');
+      }
+    });
+
+    let onDocLoad = waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, 'body');
+
+    yield BrowserTestUtils.withNewTab({
+      gBrowser,
+      url: url
+    }, function*(browser) {
+      registerCleanupFunction(() => {
+        if (browser) {
+          let tab = gBrowser.getTabForBrowser(browser);
+          if (tab && !tab.closing && tab.linkedBrowser) {
+            gBrowser.removeTab(tab);
+          }
+        }
+      });
+
+      yield SimpleTest.promiseFocus(browser);
+
+      loadFrameScripts(browser,
+        'let { document, window, navigator } = content;',
+        { name: 'common.js', dir: MOCHITESTS_DIR });
+
+      Logger.log(
+        `e10s enabled: ${Services.appinfo.browserTabsRemoteAutostart}`);
+      Logger.log(`Actually remote browser: ${browser.isRemoteBrowser}`);
+
+      let event = yield onDocLoad;
+      yield task(browser, event.accessible);
+    });
+  });
+}
+
+// Loading and common.js from accessible/tests/mochitest/ for all tests, as
+// well as events.js.
+loadScripts({ name: 'common.js', dir: MOCHITESTS_DIR }, 'e10s/events.js');
--- a/accessible/tests/browser/head.js
+++ b/accessible/tests/browser/head.js
@@ -1,303 +1,10 @@
 /* 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/. */
 
 'use strict';
 
-/* global EVENT_DOCUMENT_LOAD_COMPLETE */
-
-/* exported Logger, MOCHITESTS_DIR, isDefunct, addAccessibleTask,
-            invokeSetAttribute, invokeFocus, invokeSetStyle,
-            findAccessibleChildByID, getAccessibleDOMNodeID */
-
-const { interfaces: Ci, utils: Cu } = Components;
-
-Cu.import('resource://gre/modules/Services.jsm');
-
-/**
- * Current browser test directory path used to load subscripts.
- */
-const CURRENT_DIR =
-  'chrome://mochitests/content/browser/accessible/tests/browser/';
-/**
- * A11y mochitest directory where we find common files used in both browser and
- * plain tests.
- */
-const MOCHITESTS_DIR =
-  'chrome://mochitests/content/a11y/accessible/tests/mochitest/';
-/**
- * A base URL for test files used in content.
- */
-const CURRENT_CONTENT_DIR =
-  'http://example.com/browser/accessible/tests/browser/';
-
-/**
- * Used to dump debug information.
- */
-let Logger = {
-  /**
-   * Set up this variable to dump log messages into console.
-   */
-  dumpToConsole: false,
-
-  /**
-   * Set up this variable to dump log messages into error console.
-   */
-  dumpToAppConsole: false,
-
-  /**
-   * Return true if dump is enabled.
-   */
-  get enabled() {
-    return this.dumpToConsole || this.dumpToAppConsole;
-  },
-
-  /**
-   * Dump information into console if applicable.
-   */
-  log(msg) {
-    if (this.enabled) {
-      this.logToConsole(msg);
-      this.logToAppConsole(msg);
-    }
-  },
-
-  /**
-   * Log message to console.
-   */
-  logToConsole(msg) {
-    if (this.dumpToConsole) {
-      dump(`\n${msg}\n`);
-    }
-  },
-
-  /**
-   * Log message to error console.
-   */
-  logToAppConsole(msg) {
-    if (this.dumpToAppConsole) {
-      Services.console.logStringMessage(`${msg}`);
-    }
-  }
-};
-
-/**
- * Check if an accessible object has a defunct test.
- * @param  {nsIAccessible}  accessible object to test defunct state for
- * @return {Boolean}        flag indicating defunct state
- */
-function isDefunct(accessible) {
-  let defunct = false;
-  try {
-    let extState = {};
-    accessible.getState({}, extState);
-    defunct = extState.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT;
-  } catch (x) {
-    defunct = true;
-  } finally {
-    if (defunct) {
-      Logger.log(`Defunct accessible: ${prettyName(accessible)}`);
-    }
-  }
-  return defunct;
-}
-
-/**
- * Asynchronously set or remove content element's attribute (in content process
- * if e10s is enabled).
- * @param  {Object}  browser  current "tabbrowser" element
- * @param  {String}  id       content element id
- * @param  {String}  attr     attribute name
- * @param  {String?} value    optional attribute value, if not present, remove
- *                            attribute
- * @return {Promise}          promise indicating that attribute is set/removed
- */
-function invokeSetAttribute(browser, id, attr, value) {
-  if (value) {
-    Logger.log(`Setting ${attr} attribute to ${value} for node with id: ${id}`);
-  } else {
-    Logger.log(`Removing ${attr} attribute from node with id: ${id}`);
-  }
-  return ContentTask.spawn(browser, { id, attr, value },
-    ({ id, attr, value }) => {
-      let elm = content.document.getElementById(id);
-      if (value) {
-        elm.setAttribute(attr, value);
-      } else {
-        elm.removeAttribute(attr);
-      }
-    });
-}
-
-/**
- * Asynchronously set or remove content element's style (in content process if
- * e10s is enabled).
- * @param  {Object}  browser  current "tabbrowser" element
- * @param  {String}  id       content element id
- * @param  {String}  aStyle   style property name
- * @param  {String?} aValue   optional style property value, if not present,
- *                            remove style
- * @return {Promise}          promise indicating that style is set/removed
- */
-function invokeSetStyle(browser, id, style, value) {
-  if (value) {
-    Logger.log(`Setting ${style} style to ${value} for node with id: ${id}`);
-  } else {
-    Logger.log(`Removing ${style} style from node with id: ${id}`);
-  }
-  return ContentTask.spawn(browser, { id, style, value },
-    ({ id, style, value }) => {
-      let elm = content.document.getElementById(id);
-      if (value) {
-        elm.style[style] = value;
-      } else {
-        delete elm.style[style];
-      }
-    });
-}
-
-/**
- * Asynchronously set focus on a content element (in content process if e10s is
- * enabled).
- * @param  {Object}  browser  current "tabbrowser" element
- * @param  {String}  id       content element id
- * @return {Promise} promise  indicating that focus is set
- */
-function invokeFocus(browser, id) {
-  Logger.log(`Setting focus on a node with id: ${id}`);
-  return ContentTask.spawn(browser, id, id => {
-    let elm = content.document.getElementById(id);
-    if (elm instanceof Ci.nsIDOMNSEditableElement && elm.editor ||
-        elm instanceof Ci.nsIDOMXULTextBoxElement) {
-      elm.selectionStart = elm.selectionEnd = elm.value.length;
-    }
-    elm.focus();
-  });
-}
-
-/**
- * Traverses the accessible tree starting from a given accessible as a root and
- * looks for an accessible that matches based on its DOMNode id.
- * @param  {nsIAccessible}  accessible root accessible
- * @param  {String}         id         id to look up accessible for
- * @return {nsIAccessible?}            found accessible if any
- */
-function findAccessibleChildByID(accessible, id) {
-  if (getAccessibleDOMNodeID(accessible) === id) {
-    return accessible;
-  }
-  for (let i = 0; i < accessible.children.length; ++i) {
-    let found = findAccessibleChildByID(accessible.getChildAt(i), id);
-    if (found) {
-      return found;
-    }
-  }
-}
-
-/**
- * Load a list of scripts into the test
- * @param {Array} scripts  a list of scripts to load
- */
-function loadScripts(...scripts) {
-  for (let script of scripts) {
-    let path = typeof script === 'string' ? `${CURRENT_DIR}${script}` :
-      `${script.dir}${script.name}`;
-    Services.scriptloader.loadSubScript(path, this);
-  }
-}
-
-/**
- * Load a list of frame scripts into test's content.
- * @param {Object} browser   browser element that content belongs to
- * @param {Array}  scripts   a list of scripts to load into content
- */
-function loadFrameScripts(browser, ...scripts) {
-  let mm = browser.messageManager;
-  for (let script of scripts) {
-    let frameScript;
-    if (typeof script === 'string') {
-      if (script.includes('.js')) {
-        // If script string includes a .js extention, assume it is a script
-        // path.
-        frameScript = `${CURRENT_DIR}${script}`;
-      } else {
-        // Otherwise it is a serealized script.
-        frameScript = `data:,${script}`;
-      }
-    } else {
-      // Script is a object that has { dir, name } format.
-      frameScript = `${script.dir}${script.name}`;
-    }
-    mm.loadFrameScript(frameScript, false, true);
-  }
-}
-
-/**
- * A wrapper around browser test add_task that triggers an accessible test task
- * as a new browser test task with given document, data URL or markup snippet.
- * @param  {String}             doc    URL (relative to current directory) or
- *                                     data URL or markup snippet that is used
- *                                     to test content with
- * @param  {Function|Function*} task   a generator or a function with tests to
- *                                     run
- */
-function addAccessibleTask(doc, task) {
-  add_task(function*() {
-    let url;
-    if (doc.includes('doc_')) {
-      url = `${CURRENT_CONTENT_DIR}${doc}`;
-    } else {
-      // Assume it's a markup snippet.
-      url = `data:text/html,
-        <html>
-          <head>
-            <meta charset="utf-8"/>
-            <title>Accessibility Test</title>
-          </head>
-          <body id="body">${doc}</body>
-        </html>`;
-    }
-
-    registerCleanupFunction(() => {
-      let observers = Services.obs.enumerateObservers('accessible-event');
-      while (observers.hasMoreElements()) {
-        Services.obs.removeObserver(
-          observers.getNext().QueryInterface(Ci.nsIObserver),
-          'accessible-event');
-      }
-    });
-
-    let onDocLoad = waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, 'body');
-
-    yield BrowserTestUtils.withNewTab({
-      gBrowser,
-      url: url
-    }, function*(browser) {
-      registerCleanupFunction(() => {
-        if (browser) {
-          let tab = gBrowser.getTabForBrowser(browser);
-          if (tab && !tab.closing && tab.linkedBrowser) {
-            gBrowser.removeTab(tab);
-          }
-        }
-      });
-
-      yield SimpleTest.promiseFocus(browser);
-
-      loadFrameScripts(browser,
-        'let { document, window, navigator } = content;',
-        { name: 'common.js', dir: MOCHITESTS_DIR });
-
-      Logger.log(
-        `e10s enabled: ${Services.appinfo.browserTabsRemoteAutostart}`);
-      Logger.log(`Actually remote browser: ${browser.isRemoteBrowser}`);
-
-      let event = yield onDocLoad;
-      yield task(browser, event.accessible);
-    });
-  });
-}
-
-// Loading and common.js from accessible/tests/mochitest/ for all tests, as well
-// as events.js.
-loadScripts({ name: 'common.js', dir: MOCHITESTS_DIR }, 'events.js');
+// Load the shared-head file first.
+Services.scriptloader.loadSubScript(
+  'chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js',
+  this);
copy from accessible/tests/browser/head.js
copy to accessible/tests/browser/shared-head.js
--- a/accessible/tests/browser/head.js
+++ b/accessible/tests/browser/shared-head.js
@@ -1,23 +1,19 @@
 /* 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/. */
 
 'use strict';
 
-/* global EVENT_DOCUMENT_LOAD_COMPLETE */
+/* exported Logger, MOCHITESTS_DIR, isDefunct, invokeSetAttribute, invokeFocus,
+            invokeSetStyle, findAccessibleChildByID, getAccessibleDOMNodeID,
+            CURRENT_CONTENT_DIR, loadScripts, loadFrameScripts, Cc, Cu */
 
-/* exported Logger, MOCHITESTS_DIR, isDefunct, addAccessibleTask,
-            invokeSetAttribute, invokeFocus, invokeSetStyle,
-            findAccessibleChildByID, getAccessibleDOMNodeID */
-
-const { interfaces: Ci, utils: Cu } = Components;
-
-Cu.import('resource://gre/modules/Services.jsm');
+const { interfaces: Ci, utils: Cu, classes: Cc } = Components;
 
 /**
  * Current browser test directory path used to load subscripts.
  */
 const CURRENT_DIR =
   'chrome://mochitests/content/browser/accessible/tests/browser/';
 /**
  * A11y mochitest directory where we find common files used in both browser and
@@ -226,78 +222,8 @@ function loadFrameScripts(browser, ...sc
       }
     } else {
       // Script is a object that has { dir, name } format.
       frameScript = `${script.dir}${script.name}`;
     }
     mm.loadFrameScript(frameScript, false, true);
   }
 }
-
-/**
- * A wrapper around browser test add_task that triggers an accessible test task
- * as a new browser test task with given document, data URL or markup snippet.
- * @param  {String}             doc    URL (relative to current directory) or
- *                                     data URL or markup snippet that is used
- *                                     to test content with
- * @param  {Function|Function*} task   a generator or a function with tests to
- *                                     run
- */
-function addAccessibleTask(doc, task) {
-  add_task(function*() {
-    let url;
-    if (doc.includes('doc_')) {
-      url = `${CURRENT_CONTENT_DIR}${doc}`;
-    } else {
-      // Assume it's a markup snippet.
-      url = `data:text/html,
-        <html>
-          <head>
-            <meta charset="utf-8"/>
-            <title>Accessibility Test</title>
-          </head>
-          <body id="body">${doc}</body>
-        </html>`;
-    }
-
-    registerCleanupFunction(() => {
-      let observers = Services.obs.enumerateObservers('accessible-event');
-      while (observers.hasMoreElements()) {
-        Services.obs.removeObserver(
-          observers.getNext().QueryInterface(Ci.nsIObserver),
-          'accessible-event');
-      }
-    });
-
-    let onDocLoad = waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, 'body');
-
-    yield BrowserTestUtils.withNewTab({
-      gBrowser,
-      url: url
-    }, function*(browser) {
-      registerCleanupFunction(() => {
-        if (browser) {
-          let tab = gBrowser.getTabForBrowser(browser);
-          if (tab && !tab.closing && tab.linkedBrowser) {
-            gBrowser.removeTab(tab);
-          }
-        }
-      });
-
-      yield SimpleTest.promiseFocus(browser);
-
-      loadFrameScripts(browser,
-        'let { document, window, navigator } = content;',
-        { name: 'common.js', dir: MOCHITESTS_DIR });
-
-      Logger.log(
-        `e10s enabled: ${Services.appinfo.browserTabsRemoteAutostart}`);
-      Logger.log(`Actually remote browser: ${browser.isRemoteBrowser}`);
-
-      let event = yield onDocLoad;
-      yield task(browser, event.accessible);
-    });
-  });
-}
-
-// Loading and common.js from accessible/tests/mochitest/ for all tests, as well
-// as events.js.
-loadScripts({ name: 'common.js', dir: MOCHITESTS_DIR }, 'events.js');
--- a/accessible/xpcom/xpcAccessibilityService.cpp
+++ b/accessible/xpcom/xpcAccessibilityService.cpp
@@ -3,31 +3,83 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "xpcAccessibilityService.h"
 
 #include "mozilla/dom/DOMStringList.h"
 #include "nsAccessiblePivot.h"
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
+#include "nsCoreUtils.h"
 
 #ifdef A11Y_LOG
 #include "Logging.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 using namespace mozilla::dom;
 
-xpcAccessibilityService *xpcAccessibilityService::accessibilityService = nullptr;
-
 ////////////////////////////////////////////////////////////////////////////////
 // nsISupports
 
-NS_IMPL_ISUPPORTS(xpcAccessibilityService, nsIAccessibilityService)
+void
+xpcAccessibilityService::ShutdownCallback(nsITimer* aTimer, void* aClosure)
+{
+  xpcAccessibilityService* xpcAccService =
+    reinterpret_cast<xpcAccessibilityService*>(aClosure);
+
+  if (xpcAccService->mRefCnt <= 1) {
+    nsAccessibilityService* accService = GetAccService();
+    if (accService && !accService->IsPlatform() && !accService->IsShutdown() &&
+        !nsCoreUtils::AccEventObserversExist()) {
+      accService->Shutdown();
+    }
+  }
+
+  if (xpcAccService->mShutdownTimer) {
+    xpcAccService->mShutdownTimer->Cancel();
+    xpcAccService->mShutdownTimer = nullptr;
+  }
+}
+
+NS_IMPL_ADDREF(xpcAccessibilityService)
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+xpcAccessibilityService::Release(void)
+{
+  MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
+
+  if (!mRefCnt.isThreadSafe) {
+    NS_ASSERT_OWNINGTHREAD(xpcAccessibilityService);
+  }
+
+  nsrefcnt count = --mRefCnt;
+  NS_LOG_RELEASE(this, count, "xpcAccessibilityService");
+
+  if (count == 0) {
+    if (!mRefCnt.isThreadSafe) {
+      NS_ASSERT_OWNINGTHREAD(xpcAccessibilityService);
+    }
+
+    mRefCnt = 1; /* stabilize */
+    delete (this);
+    return 0;
+  }
+
+  if (count == 1 && !mShutdownTimer) {
+    mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+    mShutdownTimer->InitWithFuncCallback(ShutdownCallback, this, 100,
+                                         nsITimer::TYPE_ONE_SHOT);
+  }
+
+  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;
@@ -332,25 +384,18 @@ xpcAccessibilityService::IsLogged(const 
 ////////////////////////////////////////////////////////////////////////////////
 
 nsresult
 NS_GetAccessibilityService(nsIAccessibilityService** aResult)
 {
   NS_ENSURE_TRUE(aResult, NS_ERROR_NULL_POINTER);
   *aResult = nullptr;
 
-  if (xpcAccessibilityService::accessibilityService) {
-    NS_ADDREF(*aResult = xpcAccessibilityService::accessibilityService);
-
-    return NS_OK;
+  if (!GetAccService()) {
+    nsAccessibilityService* accService = GetOrCreateAccService(false);
   }
 
-  if (!GetAccService()) {
-    nsAccessibilityService* accService = GetOrCreateAccService();
-  }
-
-  RefPtr<xpcAccessibilityService> service = new xpcAccessibilityService();
+  xpcAccessibilityService* service = new xpcAccessibilityService();
   NS_ENSURE_TRUE(service, NS_ERROR_OUT_OF_MEMORY);
-  xpcAccessibilityService::accessibilityService = service;
   NS_ADDREF(*aResult = service);
 
   return NS_OK;
 }
--- a/accessible/xpcom/xpcAccessibilityService.h
+++ b/accessible/xpcom/xpcAccessibilityService.h
@@ -10,27 +10,35 @@
 class xpcAccessibilityService : public nsIAccessibilityService
 {
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIACCESSIBILITYSERVICE
 
 protected:
-  virtual ~xpcAccessibilityService() {}
+  virtual ~xpcAccessibilityService() {
+    if (mShutdownTimer) {
+      mShutdownTimer->Cancel();
+      mShutdownTimer = nullptr;
+    }
+  }
 
 private:
   // xpcAccessibilityService creation is controlled by friend
   // NS_GetAccessibilityService, keep constructor private.
-  xpcAccessibilityService() {};
+  xpcAccessibilityService() { };
+
+  nsCOMPtr<nsITimer> mShutdownTimer;
 
   /**
-   * Reference for xpc accessibility service instance.
+   * Used to shutdown nsAccessibilityService if xpcom accessible service is not
+   * in use any more.
    */
-  static xpcAccessibilityService* accessibilityService;
+  static void ShutdownCallback(nsITimer* aTimer, void* aClosure);
 
   friend nsresult NS_GetAccessibilityService(nsIAccessibilityService** aResult);
 };
 
 // for component registration
 // {3b265b69-f813-48ff-880d-d88d101af404}
 #define NS_ACCESSIBILITY_SERVICE_CID \
 { 0x3b265b69, 0xf813, 0x48ff, { 0x88, 0x0d, 0xd8, 0x8d, 0x10, 0x1a, 0xf4, 0x04 } }