--- 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 } }