Bug 1175913 - (Part 1) Subscribe to EventListenerService and recreate accessibles on click listener changes r=tbsaunde

This commit is contained in:
Lorien Hu 2015-08-04 23:35:54 -04:00
parent 0790078b20
commit 77ddb155a5
6 changed files with 186 additions and 17 deletions

View File

@ -22,6 +22,7 @@
#include "RootAccessible.h"
#include "nsAccessiblePivot.h"
#include "nsAccUtils.h"
#include "nsArrayUtils.h"
#include "nsAttrName.h"
#include "nsEventShell.h"
#include "nsIURI.h"
@ -273,6 +274,64 @@ nsAccessibilityService::~nsAccessibilityService()
gAccessibilityService = nullptr;
}
////////////////////////////////////////////////////////////////////////////////
// nsIListenerChangeListener
NS_IMETHODIMP
nsAccessibilityService::ListenersChanged(nsIArray* aEventChanges)
{
uint32_t targetCount;
nsresult rv = aEventChanges->GetLength(&targetCount);
NS_ENSURE_SUCCESS(rv, rv);
for (uint32_t i = 0 ; i < targetCount ; i++) {
nsCOMPtr<nsIEventListenerChange> change = do_QueryElementAt(aEventChanges, i);
nsCOMPtr<nsIDOMEventTarget> target;
change->GetTarget(getter_AddRefs(target));
nsCOMPtr<nsIContent> node(do_QueryInterface(target));
if (!node || !node->IsHTMLElement()) {
continue;
}
nsCOMPtr<nsIArray> listenerNames;
change->GetChangedListenerNames(getter_AddRefs(listenerNames));
uint32_t changeCount;
rv = listenerNames->GetLength(&changeCount);
NS_ENSURE_SUCCESS(rv, rv);
for (uint32_t i = 0 ; i < changeCount ; i++) {
nsCOMPtr<nsIAtom> listenerName = do_QueryElementAt(listenerNames, i);
// We are only interested in event listener changes which may
// make an element accessible or inaccessible.
if (listenerName != nsGkAtoms::onclick &&
listenerName != nsGkAtoms::onmousedown &&
listenerName != nsGkAtoms::onmouseup) {
continue;
}
nsIDocument* ownerDoc = node->OwnerDoc();
DocAccessible* document = GetExistingDocAccessible(ownerDoc);
// Always recreate for onclick changes.
if (document) {
if (nsCoreUtils::HasClickListener(node)) {
if (!document->GetAccessible(node)) {
document->RecreateAccessible(node);
}
} else {
if (document->GetAccessible(node)) {
document->RecreateAccessible(node);
}
}
break;
}
}
}
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsISupports
@ -281,6 +340,7 @@ NS_IMPL_ISUPPORTS_INHERITED(nsAccessibilityService,
nsIAccessibilityService,
nsIAccessibleRetrieval,
nsIObserver,
nsIListenerChangeListener,
nsISelectionListener) // from SelectionManager
////////////////////////////////////////////////////////////////////////////////
@ -1258,6 +1318,14 @@ nsAccessibilityService::Init()
static const char16_t kInitIndicator[] = { '1', 0 };
observerService->NotifyObservers(nullptr, "a11y-init-or-shutdown", kInitIndicator);
// Subscribe to EventListenerService.
nsCOMPtr<nsIEventListenerService> eventListenerService =
do_GetService("@mozilla.org/eventlistenerservice;1");
if (!eventListenerService)
return false;
eventListenerService->AddListenerChangeListener(this);
for (uint32_t i = 0; i < ArrayLength(sMarkupMapList); i++)
mMarkupMaps.Put(*sMarkupMapList[i].tag, &sMarkupMapList[i]);

View File

@ -15,8 +15,10 @@
#include "mozilla/Preferences.h"
#include "nsIObserver.h"
#include "nsIEventListenerService.h"
class nsImageFrame;
class nsIArray;
class nsIPersistentProperties;
class nsPluginFrame;
class nsITreeView;
@ -67,12 +69,16 @@ class nsAccessibilityService final : public mozilla::a11y::DocManager,
public mozilla::a11y::FocusManager,
public mozilla::a11y::SelectionManager,
public nsIAccessibilityService,
public nsIListenerChangeListener,
public nsIObserver
{
public:
typedef mozilla::a11y::Accessible Accessible;
typedef mozilla::a11y::DocAccessible DocAccessible;
// nsIListenerChangeListener
NS_IMETHOD ListenersChanged(nsIArray* aEventChanges) override;
protected:
virtual ~nsAccessibilityService();

View File

@ -1611,8 +1611,7 @@ DocAccessible::UpdateAccessibleOnAttrChange(dom::Element* aElement,
return true;
}
if (aAttribute == nsGkAtoms::href ||
aAttribute == nsGkAtoms::onclick) {
if (aAttribute == nsGkAtoms::href) {
// Not worth the expense to ensure which namespace these are in. It doesn't
// kill use to recreate the accessible even if the attribute was used in
// the wrong namespace or an element that doesn't support it.

View File

@ -7,6 +7,7 @@
[test_bug895082.html]
[test_bug1040735.html]
[test_bug1100602.html]
[test_bug1175913.html]
[test_canvas.html]
[test_colorpicker.xul]
[test_contextmenu.xul]

View File

@ -0,0 +1,103 @@
<html>
<head>
<title>Test hide/show events on event listener changes</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../role.js"></script>
<script type="application/javascript"
src="../events.js"></script>
<script type="application/javascript">
function dummyListener() {}
function testAddListener()
{
this.eventSeq = [
new invokerChecker(EVENT_SHOW, getNode("parent")),
];
this.invoke = function testAddListener_invoke()
{
is(getAccessible("parent", null, null, DONOTFAIL_IF_NO_ACC), null, "Check that parent is not accessible.");
is(getAccessible("child", null, null, DONOTFAIL_IF_NO_ACC), null, "Check that child is not accessible.");
getNode("parent").addEventListener("click", dummyListener);
}
this.finalCheck = function testAddListener_finalCheck()
{
var tree = { TEXT: [] };
testAccessibleTree("parent", tree);
}
this.getID = function testAddListener_getID()
{
return "Test that show event is sent when click listener is added";
}
}
function testRemoveListener()
{
this.eventSeq = [
new invokerChecker(EVENT_HIDE, getNode("parent")),
];
this.invoke = function testRemoveListener_invoke()
{
getNode("parent").removeEventListener("click", dummyListener);
}
this.finalCheck = function testRemoveListener_finalCheck()
{
is(getAccessible("parent", null, null, DONOTFAIL_IF_NO_ACC), null, "Check that parent is not accessible.");
is(getAccessible("child", null, null, DONOTFAIL_IF_NO_ACC), null, "Check that child is not accessible.");
}
this.getID = function testRemoveListener_getID()
{
return "Test that hide event is sent when click listener is removed";
}
}
var gQueue = null;
function doTest()
{
gQueue = new eventQueue();
gQueue.push(new testAddListener());
gQueue.push(new testRemoveListener());
gQueue.invoke(); // SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1175913"
title="Crash in mozilla::a11y::DocAccessibleParent::RemoveAccessible(ProxyAccessible* aAccessible)">
Mozilla Bug 1175913
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<span id="parent">
<span id="child">
</span>
</span>
</body>
</html>

View File

@ -40,17 +40,10 @@
function setOnClickAttr(aID)
{
this.__proto__ = new textLeafUpdate(aID, true);
this.invoke = function setOnClickAttr_invoke()
{
this.node.setAttribute("onclick", "alert(3);");
}
this.getID = function setOnClickAttr_getID()
{
return "make " + prettyName(aID) + " linkable";
}
var node = getNode(aID);
node.setAttribute("onclick", "alert(3);");
var textLeaf = getAccessible(node).firstChild;
is(textLeaf.actionCount, 1, "Wrong action numbers!");
}
function removeOnClickAttr(aID)
@ -134,11 +127,10 @@
function doTest()
{
gQueue = new eventQueue();
// adds onclick on element, text leaf should inherit its action
gQueue.push(new setOnClickAttr("div"));
setOnClickAttr("div");
// Call rest of event tests.
gQueue = new eventQueue();
// remove onclick attribute, text leaf shouldn't have any action
gQueue.push(new removeOnClickAttr("div"));