Bug 732389 - image map accessible tree is not updated when image map is changed, r=bz, tbsaunde

This commit is contained in:
Alexander Surkov 2012-03-15 16:16:02 -04:00
parent d9ecb49f84
commit 12cc97eaa6
16 changed files with 647 additions and 85 deletions

View File

@ -298,23 +298,24 @@ already_AddRefed<nsAccessible>
nsAccessibilityService::CreateHTMLImageAccessible(nsIContent* aContent,
nsIPresShell* aPresShell)
{
nsAutoString mapElmName;
aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usemap, mapElmName);
nsCOMPtr<nsIDOMHTMLMapElement> mapElm;
if (nsIDocument* document = aContent->GetCurrentDoc()) {
mapElm = do_QueryInterface(document->FindImageMap(mapElmName));
}
nsAccessible* accessible = mapElm ?
new nsHTMLImageMapAccessible(aContent,
nsAccUtils::GetDocAccessibleFor(aPresShell),
mapElm) :
new nsHTMLImageAccessibleWrap(aContent,
nsAccessible* accessible =
new nsHTMLImageAccessibleWrap(aContent,
nsAccUtils::GetDocAccessibleFor(aPresShell));
NS_ADDREF(accessible);
return accessible;
}
already_AddRefed<nsAccessible>
nsAccessibilityService::CreateHTMLImageMapAccessible(nsIContent* aContent,
nsIPresShell* aPresShell)
{
nsAccessible* accessible =
new nsHTMLImageMapAccessible(aContent,
nsAccUtils::GetDocAccessibleFor(aPresShell));
NS_ADDREF(accessible);
return accessible;
}
already_AddRefed<nsAccessible>
nsAccessibilityService::CreateHTMLGroupboxAccessible(nsIContent* aContent,
nsIPresShell* aPresShell)
@ -611,6 +612,28 @@ nsAccessibilityService::UpdateListBullet(nsIPresShell* aPresShell,
}
}
void
nsAccessibilityService::UpdateImageMap(nsImageFrame* aImageFrame)
{
nsIPresShell* presShell = aImageFrame->PresContext()->PresShell();
nsDocAccessible* document = GetDocAccessible(presShell->GetDocument());
if (document) {
nsAccessible* accessible =
document->GetAccessible(aImageFrame->GetContent());
if (accessible) {
nsHTMLImageMapAccessible* imageMap = accessible->AsImageMap();
if (imageMap) {
imageMap->UpdateChildAreas();
return;
}
// If image map was initialized after we created an accessible (that'll
// be an image accessible) then recreate it.
RecreateAccessible(presShell, aImageFrame->GetContent());
}
}
}
void
nsAccessibilityService::PresShellDestroyed(nsIPresShell *aPresShell)
{

View File

@ -48,6 +48,8 @@
#include "nsIObserver.h"
class nsImageFrame;
namespace mozilla {
namespace a11y {
@ -109,6 +111,8 @@ public:
CreateHTMLHRAccessible(nsIContent* aContent, nsIPresShell* aPresShell);
virtual already_AddRefed<nsAccessible>
CreateHTMLImageAccessible(nsIContent* aContent, nsIPresShell* aPresShell);
already_AddRefed<nsAccessible>
CreateHTMLImageMapAccessible(nsIContent* aContent, nsIPresShell* aPresShell);
virtual already_AddRefed<nsAccessible>
CreateHTMLLabelAccessible(nsIContent* aContent, nsIPresShell* aPresShell);
virtual already_AddRefed<nsAccessible>
@ -155,6 +159,11 @@ public:
nsIContent* aHTMLListItemContent,
bool aHasBullet);
/**
* Update the image map.
*/
void UpdateImageMap(nsImageFrame* aImageFrame);
virtual void NotifyOfAnchorJumpTo(nsIContent *aTarget);
virtual void PresShellDestroyed(nsIPresShell* aPresShell);

View File

@ -62,6 +62,7 @@ class KeyBinding;
class nsAccessible;
class nsHyperTextAccessible;
class nsHTMLImageAccessible;
class nsHTMLImageMapAccessible;
class nsHTMLLIAccessible;
struct nsRoleMapEntry;
class Relation;
@ -454,10 +455,13 @@ public:
inline bool IsHTMLListItem() const { return mFlags & eHTMLListItemAccessible; }
nsHTMLLIAccessible* AsHTMLListItem();
inline bool IsImageAccessible() const { return mFlags & eImageAccessible; }
nsHTMLImageAccessible* AsImage();
bool IsImageMapAccessible() const { return mFlags & eImageMapAccessible; }
nsHTMLImageMapAccessible* AsImageMap();
inline bool IsListControl() const { return mFlags & eListControlAccessible; }
inline bool IsMenuButton() const { return mFlags & eMenuButtonAccessible; }
@ -689,11 +693,12 @@ protected:
eHTMLFileInputAccessible = 1 << 8,
eHTMLListItemAccessible = 1 << 9,
eImageAccessible = 1 << 10,
eListControlAccessible = 1 << 11,
eMenuButtonAccessible = 1 << 12,
eMenuPopupAccessible = 1 << 13,
eRootAccessible = 1 << 14,
eTextLeafAccessible = 1 << 15
eImageMapAccessible = 1 << 11,
eListControlAccessible = 1 << 12,
eMenuButtonAccessible = 1 << 13,
eMenuPopupAccessible = 1 << 14,
eRootAccessible = 1 << 15,
eTextLeafAccessible = 1 << 16
};
//////////////////////////////////////////////////////////////////////////////

View File

@ -52,15 +52,16 @@
#include "nsImageMap.h"
using namespace mozilla::a11y;
////////////////////////////////////////////////////////////////////////////////
// nsHTMLImageMapAccessible
////////////////////////////////////////////////////////////////////////////////
nsHTMLImageMapAccessible::
nsHTMLImageMapAccessible(nsIContent* aContent, nsDocAccessible* aDoc,
nsIDOMHTMLMapElement* aMapElm) :
nsHTMLImageAccessibleWrap(aContent, aDoc), mMapElement(aMapElm)
nsHTMLImageMapAccessible(nsIContent* aContent, nsDocAccessible* aDoc) :
nsHTMLImageAccessibleWrap(aContent, aDoc)
{
mFlags |= eImageMapAccessible;
}
////////////////////////////////////////////////////////////////////////////////
@ -104,39 +105,75 @@ nsHTMLImageMapAccessible::AnchorURIAt(PRUint32 aAnchorIndex)
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLImageMapAccessible: nsAccessible protected
// nsHTMLImageMapAccessible: public
void
nsHTMLImageMapAccessible::CacheChildren()
void
nsHTMLImageMapAccessible::UpdateChildAreas(bool aDoFireEvents)
{
if (!mMapElement)
nsImageFrame* imageFrame = do_QueryFrame(mContent->GetPrimaryFrame());
// If image map is not initialized yet then we trigger one time more later.
nsImageMap* imageMapObj = imageFrame->GetExistingImageMap();
if (!imageMapObj)
return;
nsCOMPtr<nsIDOMHTMLCollection> mapAreas;
mMapElement->GetAreas(getter_AddRefs(mapAreas));
if (!mapAreas)
return;
bool doReorderEvent = false;
nsDocAccessible* document = Document();
// Remove areas that are not a valid part of the image map anymore.
for (PRInt32 childIdx = mChildren.Length() - 1; childIdx >= 0; childIdx--) {
nsAccessible* area = mChildren.ElementAt(childIdx);
if (area->GetContent()->GetPrimaryFrame())
continue;
PRUint32 areaCount = 0;
mapAreas->GetLength(&areaCount);
if (aDoFireEvents) {
nsRefPtr<AccEvent> event = new AccHideEvent(area, area->GetContent());
mDoc->FireDelayedAccessibleEvent(event);
doReorderEvent = true;
}
for (PRUint32 areaIdx = 0; areaIdx < areaCount; areaIdx++) {
nsCOMPtr<nsIDOMNode> areaNode;
mapAreas->Item(areaIdx, getter_AddRefs(areaNode));
if (!areaNode)
return;
RemoveChild(area);
}
nsCOMPtr<nsIContent> areaContent(do_QueryInterface(areaNode));
nsRefPtr<nsAccessible> area =
new nsHTMLAreaAccessible(areaContent, mDoc);
// Insert new areas into the tree.
PRUint32 areaElmCount = imageMapObj->AreaCount();
for (PRUint32 idx = 0; idx < areaElmCount; idx++) {
nsIContent* areaContent = imageMapObj->GetAreaAt(idx);
if (!document->BindToDocument(area, nsAccUtils::GetRoleMapEntry(areaContent)) ||
!AppendChild(area)) {
return;
nsAccessible* area = mChildren.SafeElementAt(idx);
if (!area || area->GetContent() != areaContent) {
nsRefPtr<nsAccessible> area = new nsHTMLAreaAccessible(areaContent, mDoc);
if (!mDoc->BindToDocument(area, nsAccUtils::GetRoleMapEntry(areaContent)))
break;
if (!InsertChildAt(idx, area)) {
mDoc->UnbindFromDocument(area);
break;
}
if (aDoFireEvents) {
nsRefPtr<AccEvent> event = new AccShowEvent(area, areaContent);
mDoc->FireDelayedAccessibleEvent(event);
doReorderEvent = true;
}
}
}
// Fire reorder event if needed.
if (doReorderEvent) {
nsRefPtr<AccEvent> reorderEvent =
new AccEvent(nsIAccessibleEvent::EVENT_REORDER, mContent,
eAutoDetect, AccEvent::eCoalesceFromSameSubtree);
mDoc->FireDelayedAccessibleEvent(reorderEvent);
}
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLImageMapAccessible: nsAccessible protected
void
nsHTMLImageMapAccessible::CacheChildren()
{
UpdateChildAreas(false);
}
@ -226,6 +263,17 @@ nsHTMLAreaAccessible::GetBounds(PRInt32 *aX, PRInt32 *aY,
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLAreaAccessible: nsAccessNode public
bool
nsHTMLAreaAccessible::IsPrimaryForNode() const
{
// Make HTML area DOM element not accessible. HTML image map accessible
// manages its tree itself.
return false;
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLAreaAccessible: nsAccessible public

View File

@ -51,8 +51,8 @@
class nsHTMLImageMapAccessible : public nsHTMLImageAccessibleWrap
{
public:
nsHTMLImageMapAccessible(nsIContent* aContent, nsDocAccessible* aDoc,
nsIDOMHTMLMapElement* aMapElm);
nsHTMLImageMapAccessible(nsIContent* aContent, nsDocAccessible* aDoc);
virtual ~nsHTMLImageMapAccessible() { }
// nsISupports and cycle collector
NS_DECL_ISUPPORTS_INHERITED
@ -65,16 +65,27 @@ public:
virtual nsAccessible* AnchorAt(PRUint32 aAnchorIndex);
virtual already_AddRefed<nsIURI> AnchorURIAt(PRUint32 aAnchorIndex);
/**
* Update area children of the image map.
*/
void UpdateChildAreas(bool aDoFireEvents = true);
protected:
// nsAccessible
virtual void CacheChildren();
private:
// Reference on linked map element if any.
nsCOMPtr<nsIDOMHTMLMapElement> mMapElement;
};
////////////////////////////////////////////////////////////////////////////////
// nsAccessible downcasting method
inline nsHTMLImageMapAccessible*
nsAccessible::AsImageMap()
{
return IsImageMapAccessible() ?
static_cast<nsHTMLImageMapAccessible*>(this) : nsnull;
}
/**
* Accessible for image map areas - must be child of image.
@ -89,6 +100,9 @@ public:
NS_IMETHOD GetBounds(PRInt32 *x, PRInt32 *y, PRInt32 *width, PRInt32 *height);
// nsAccessNode
virtual bool IsPrimaryForNode() const;
// nsAccessible
virtual void Description(nsString& aDescription);
virtual nsresult GetNameInternal(nsAString& aName);

View File

@ -486,6 +486,21 @@ function testDefunctAccessible(aAcc, aNodeOrId)
ok(success, "parent" + msg);
}
/**
* Ensure that image map accessible tree is created.
*/
function ensureImageMapTree(aID)
{
// XXX: We send a useless mouse move to the image to force it to setup its
// image map, because flushing layout won't do it. Hopefully bug 135040
// will make this not suck.
synthesizeMouse(getNode(aID), 10, 10, { type: "mousemove" });
// XXX This may affect a11y more than other code because imagemaps may not
// get drawn or have an mouse event over them. Bug 570322 tracks a11y
// dealing with this.
todo(false, "Need to remove this image map workaround.");
}
/**
* Convert role to human readable string.

View File

@ -9,6 +9,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=418368
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript"
src="../common.js"></script>
@ -88,6 +90,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=418368
//////////////////////////////////////////////////////////////////////////
// image map and its link children
ensureImageMapTree("imgmap");
var imageMapHyperlinkAcc = getAccessible("imgmap",
[nsIAccessibleHyperLink]);
testThis("imgmap", imageMapHyperlinkAcc, ROLE_IMAGE_MAP, 2, "b", true,
@ -268,7 +272,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=418368
><img width="447" id="imgmap"
height="15"
usemap="#atoz_map"
src="letters.gif"><br>Empty link:<br
src="../letters.gif"><br>Empty link:<br
><a id="emptyLink" href=""><img src=""></a
><br>Link with embedded span<br
><a id="LinkWithSpan" href="http://www.heise.de/"><span lang="de">Heise Online</span></a

View File

@ -9,6 +9,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=428248
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript"
src="../common.js"></script>
@ -53,6 +55,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=428248
testThis("InvalidAriaHyperlink", 63, 2, "Invalid link");
// image map, but not its link children. They are not part of hypertext.
ensureImageMapTree("imgmap");
testThis("imgmap", 76, 3, "b");
// empty hyperlink
@ -131,7 +134,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=428248
><img width="447" id="imgmap"
height="15"
usemap="#atoz_map"
src="letters.gif"></img><br
src="../letters.gif"></img><br
>Empty link:<br
><a id="emptyLink" href=""><img src=""></img></a><br
>Link with embedded span<br

View File

@ -19,21 +19,22 @@
<script type="application/javascript">
function doTest()
{
//XXX We send a useless mouse move to the image to force it to setup its
// image map, because flushing layout won't do it. Hopefully bug 135040
// will make this not suck.
synthesizeMouse($("imagemap"), 10, 10, { type: "mousemove" });
//XXX This may affect a11y more than other code because imagemaps may not
// get drawn or have an mouse event over them. Bug 570322 tracks a11y
// dealing with this.
todo(false, "Need to remove this image map workaround.");
ensureImageMapTree("imagemap");
testStates("t1", 0, EXT_STATE_EDITABLE, STATE_LINKED);
testStates("t2", 0, EXT_STATE_EDITABLE, STATE_LINKED);
testStates("rb1", (STATE_CHECKABLE | STATE_CHECKED), 0, STATE_LINKED);
testStates("rb2", STATE_CHECKABLE, 0, STATE_CHECKED, STATE_LINKED);
testStates("cb1", (STATE_CHECKABLE | STATE_CHECKED), 0, STATE_LINKED);
testStates("cbox", (STATE_HASPOPUP | STATE_COLLAPSED),
var imageMap = getAccessible("imagemap");
var t1 = imageMap.getChildAt(0);
testStates(t1, 0, EXT_STATE_EDITABLE, STATE_LINKED);
var t2 = imageMap.getChildAt(1);
testStates(t2, 0, EXT_STATE_EDITABLE, STATE_LINKED);
var rb1 = imageMap.getChildAt(2);
testStates(rb1, (STATE_CHECKABLE | STATE_CHECKED), 0, STATE_LINKED);
var rb2 = imageMap.getChildAt(3);
testStates(rb2, STATE_CHECKABLE, 0, STATE_CHECKED, STATE_LINKED);
var cb1 = imageMap.getChildAt(4);
testStates(cb1, (STATE_CHECKABLE | STATE_CHECKED), 0, STATE_LINKED);
var cbox = imageMap.getChildAt(5);
testStates(cbox, (STATE_HASPOPUP | STATE_COLLAPSED),
EXT_STATE_EXPANDABLE, STATE_LINKED);
SimpleTest.finish();

View File

@ -19,14 +19,7 @@
<script type="application/javascript">
function doTest()
{
//XXX We send a useless mouse move to the image to force it to setup its
// image map, because flushing layout won't do it. Hopefully bug 135040
// will make this not suck.
synthesizeMouse($("imagemap"), 10, 10, { type: "mousemove" });
//XXX This may affect a11y more than other code because imagemaps may not
// get drawn or have an mouse event over them. Bug 570322 tracks a11y
// dealing with this.
todo(false, "Need to remove this image map workaround.");
ensureImageMapTree("imagemap");
var accTree = {
role: ROLE_IMAGE_MAP,

View File

@ -54,6 +54,7 @@ _TEST_FILES =\
test_doc.html \
test_gencontent.html \
test_hidden.html \
test_imagemap.html \
test_list_editabledoc.html \
test_list.html \
test_menu.xul \

View File

@ -0,0 +1,394 @@
<!DOCTYPE html>
<html>
<head>
<title>HTML img map accessible tree update tests</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="chrome://mochikit/content/tests/SimpleTest/EventUtils.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 insertArea(aImageMapID, aMapID)
{
this.imageMap = getAccessible(aImageMapID);
this.mapNode = getNode(aMapID);
function getInsertedArea(aThisObj)
{
return aThisObj.imageMap.firstChild;
}
this.eventSeq = [
new invokerChecker(EVENT_SHOW, getInsertedArea, this),
new invokerChecker(EVENT_REORDER, this.imageMap)
];
this.invoke = function insertArea_invoke()
{
var areaElm = document.createElement("area");
areaElm.setAttribute("href",
"http://www.bbc.co.uk/radio4/atoz/index.shtml#a");
areaElm.setAttribute("coords", "0,0,13,14");
areaElm.setAttribute("alt", "a");
areaElm.setAttribute("shape", "rect");
this.mapNode.insertBefore(areaElm, this.mapNode.firstChild);
}
this.finalCheck = function insertArea_finalCheck()
{
var accTree =
{ IMAGE_MAP: [
{
role: ROLE_LINK,
name: "a",
children: [ ]
},
{
role: ROLE_LINK,
name: "b",
children: [ ]
},
] };
testAccessibleTree(this.imageMap, accTree);
}
this.getID = function insertArea_getID()
{
return "insert area element";
}
}
function appendArea(aImageMapID, aMapID)
{
this.imageMap = getAccessible(aImageMapID);
this.mapNode = getNode(aMapID);
function getAppendedArea(aThisObj)
{
return aThisObj.imageMap.lastChild;
}
this.eventSeq = [
new invokerChecker(EVENT_SHOW, getAppendedArea, this),
new invokerChecker(EVENT_REORDER, this.imageMap)
];
this.invoke = function appendArea_invoke()
{
var areaElm = document.createElement("area");
areaElm.setAttribute("href",
"http://www.bbc.co.uk/radio4/atoz/index.shtml#c");
areaElm.setAttribute("coords", "34,0,47,14");
areaElm.setAttribute("alt", "c");
areaElm.setAttribute("shape", "rect");
this.mapNode.appendChild(areaElm);
}
this.finalCheck = function appendArea_finalCheck()
{
var accTree =
{ IMAGE_MAP: [
{
role: ROLE_LINK,
name: "a",
children: [ ]
},
{
role: ROLE_LINK,
name: "b",
children: [ ]
},
{
role: ROLE_LINK,
name: "c",
children: [ ]
}
] };
testAccessibleTree(this.imageMap, accTree);
}
this.getID = function appendArea_getID()
{
return "append area element";
}
}
function removeArea(aImageMapID, aMapID)
{
this.imageMap = getAccessible(aImageMapID);
this.area = null;
this.mapNode = getNode(aMapID);
function getRemovedArea(aThisObj)
{
return aThisObj.area;
}
this.eventSeq = [
new invokerChecker(EVENT_HIDE, getRemovedArea, this),
new invokerChecker(EVENT_REORDER, this.imageMap)
];
this.invoke = function removeArea_invoke()
{
this.area = this.imageMap.firstChild;
this.mapNode.removeChild(this.mapNode.firstElementChild);
}
this.finalCheck = function removeArea_finalCheck()
{
var accTree =
{ IMAGE_MAP: [
{
role: ROLE_LINK,
name: "b",
children: [ ]
},
{
role: ROLE_LINK,
name: "c",
children: [ ]
}
] };
testAccessibleTree(this.imageMap, accTree);
}
this.getID = function removeArea_getID()
{
return "remove area element";
}
}
function removeNameOnMap(aImageMapContainerID, aImageMapID, aMapID)
{
this.container = getAccessible(aImageMapContainerID);
this.containerNode = this.container.DOMNode;
this.imageMap = getAccessible(aImageMapID);
this.imgNode = this.imageMap.DOMNode;
this.mapNode = getNode(aMapID);
this.eventSeq = [
new invokerChecker(EVENT_HIDE, this.imageMap),
new invokerChecker(EVENT_SHOW, this.imgNode),
new invokerChecker(EVENT_REORDER, this.container)
];
this.invoke = function removeNameOnMap_invoke()
{
this.mapNode.removeAttribute("name");
}
this.finalCheck = function removeNameOnMap_finalCheck()
{
var accTree =
{ SECTION: [
{ GRAPHIC: [ ] }
] };
testAccessibleTree(this.container, accTree);
}
this.getID = function removeNameOnMap_getID()
{
return "remove @name on map element";
}
}
function restoreNameOnMap(aImageMapContainerID, aImageMapID, aMapID)
{
this.container = getAccessible(aImageMapContainerID);
this.containerNode = this.container.DOMNode;
this.imageMap = null;
this.imgNode = getNode(aImageMapID);
this.mapNode = getNode(aMapID);
function getImageMap(aThisObj)
{
return aThisObj.imageMap;
}
this.eventSeq = [
new invokerChecker(EVENT_HIDE, getImageMap, this),
new invokerChecker(EVENT_SHOW, this.imgNode),
new invokerChecker(EVENT_REORDER, this.container)
];
this.invoke = function restoreNameOnMap_invoke()
{
this.imageMap = getAccessible(aImageMapID);
this.mapNode.setAttribute("name", "atoz_map");
}
this.finalCheck = function removeNameOnMap_finalCheck()
{
var accTree =
{ SECTION: [
{ IMAGE_MAP: [
{ LINK: [ ] },
{ LINK: [ ] }
] }
] };
testAccessibleTree(this.container, accTree);
}
this.getID = function removeNameOnMap_getID()
{
return "restore @name on map element";
}
}
function removeMap(aImageMapContainerID, aImageMapID, aMapID)
{
this.container = getAccessible(aImageMapContainerID);
this.containerNode = this.container.DOMNode;
this.imageMap = null;
this.imgNode = getNode(aImageMapID);
this.mapNode = getNode(aMapID);
function getImageMap(aThisObj)
{
return aThisObj.imageMap;
}
this.eventSeq = [
new invokerChecker(EVENT_HIDE, getImageMap, this),
new invokerChecker(EVENT_SHOW, this.imgNode),
new invokerChecker(EVENT_REORDER, this.container)
];
this.invoke = function removeMap_invoke()
{
this.imageMap = getAccessible(aImageMapID);
this.mapNode.parentNode.removeChild(this.mapNode);
}
this.finalCheck = function removeMap_finalCheck()
{
var accTree =
{ SECTION: [
{ GRAPHIC: [ ] }
] };
testAccessibleTree(this.container, accTree);
}
this.getID = function removeMap_getID()
{
return "remove map element";
}
}
function insertMap(aImageMapContainerID, aImageID)
{
this.container = getAccessible(aImageMapContainerID);
this.containerNode = this.container.DOMNode;
this.image = null;
this.imgMapNode = getNode(aImageID);
function getImage(aThisObj)
{
return aThisObj.image;
}
this.eventSeq = [
new invokerChecker(EVENT_HIDE, getImage, this),
new invokerChecker(EVENT_SHOW, this.imgMapNode),
new invokerChecker(EVENT_REORDER, this.container)
];
this.invoke = function insertMap_invoke()
{
this.image = getAccessible(aImageID);
var map = document.createElement("map");
map.setAttribute("name", "atoz_map");
map.setAttribute("id", "map");
var area = document.createElement("area")
area.setAttribute("href",
"http://www.bbc.co.uk/radio4/atoz/index.shtml#b");
area.setAttribute("coords", "17,0,30,14");
area.setAttribute("alt", "b");
area.setAttribute("shape", "rect");
map.appendChild(area);
this.containerNode.appendChild(map);
ensureImageMapTree(aImageID);
}
this.finalCheck = function insertMap_finalCheck()
{
var accTree =
{ SECTION: [
{ IMAGE_MAP: [
{ LINK: [ ] }
] }
] };
testAccessibleTree(this.container, accTree);
}
this.getID = function insertMap_getID()
{
return "insert map element";
}
}
//gA11yEventDumpToConsole = true;
var gQueue = null;
function doTest()
{
gQueue = new eventQueue();
gQueue.push(new insertArea("imgmap", "map"));
gQueue.push(new appendArea("imgmap", "map"));
gQueue.push(new removeArea("imgmap", "map"));
gQueue.push(new removeNameOnMap("container", "imgmap", "map"));
gQueue.push(new restoreNameOnMap("container", "imgmap", "map"));
gQueue.push(new removeMap("container", "imgmap", "map"));
gQueue.push(new insertMap("container", "imgmap"));
gQueue.invoke(); // Will call SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank"
title="Image map accessible tree is not updated when image map is changed"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=732389">
Mozilla Bug 732389
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<map name="atoz_map" id="map">
<area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#b"
coords="17,0,30,14" alt="b" shape="rect">
</map>
<div id="container">
<img id="imgmap" width="447" height="15"
usemap="#atoz_map"
src="../letters.gif">
</div>
</body>
</html>

View File

@ -194,8 +194,14 @@ nsImageFrame::CreateAccessible()
{
nsAccessibilityService* accService = nsIPresShell::AccService();
if (accService) {
return accService->CreateHTMLImageAccessible(mContent,
PresContext()->PresShell());
// Don't use GetImageMap() to avoid reentrancy into accessibility.
if (HasImageMap()) {
return accService->CreateHTMLImageMapAccessible(mContent,
PresContext()->PresShell());
} else {
return accService->CreateHTMLImageAccessible(mContent,
PresContext()->PresShell());
}
}
return nsnull;
@ -208,6 +214,13 @@ nsImageFrame::DisconnectMap()
if (mImageMap) {
mImageMap->Destroy();
NS_RELEASE(mImageMap);
#ifdef ACCESSIBILITY
nsAccessibilityService* accService = GetAccService();
if (accService) {
accService->RecreateAccessible(PresContext()->PresShell(), mContent);
}
#endif
}
}
@ -1422,15 +1435,7 @@ nsImageMap*
nsImageFrame::GetImageMap()
{
if (!mImageMap) {
nsIDocument* doc = mContent->GetDocument();
if (!doc) {
return nsnull;
}
nsAutoString usemap;
mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usemap, usemap);
nsCOMPtr<nsIContent> map = doc->FindImageMap(usemap);
nsIContent* map = GetMapElement();
if (map) {
mImageMap = new nsImageMap();
NS_ADDREF(mImageMap);

View File

@ -183,7 +183,25 @@ public:
nsRect GetInnerArea() const;
/**
* Return a map element associated with this image.
*/
mozilla::dom::Element* GetMapElement() const
{
nsAutoString usemap;
if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usemap, usemap)) {
return mContent->OwnerDoc()->FindImageMap(usemap);
}
return nsnull;
}
/**
* Return true if the image has associated image map.
*/
bool HasImageMap() const { return mImageMap || GetMapElement(); }
nsImageMap* GetImageMap();
nsImageMap* GetExistingImageMap() const { return mImageMap; }
virtual void AddInlineMinWidth(nsRenderingContext *aRenderingContext,
InlineMinWidthData *aData);

View File

@ -61,6 +61,10 @@
#include "nsIStringBundle.h"
#include "nsContentUtils.h"
#ifdef ACCESSIBILITY
#include "nsAccessibilityService.h"
#endif
namespace dom = mozilla::dom;
static NS_DEFINE_CID(kCStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID);
@ -811,7 +815,16 @@ nsImageMap::UpdateAreas()
bool foundAnchor = false;
mContainsBlockContents = false;
return SearchForAreas(mMap, foundArea, foundAnchor);
nsresult rv = SearchForAreas(mMap, foundArea, foundAnchor);
#ifdef ACCESSIBILITY
if (NS_SUCCEEDED(rv)) {
nsAccessibilityService* accService = GetAccService();
if (accService) {
accService->UpdateImageMap(mImageFrame);
}
}
#endif
return rv;
}
nsresult
@ -886,6 +899,12 @@ nsImageMap::GetArea(nscoord aX, nscoord aY) const
return nsnull;
}
nsIContent*
nsImageMap::GetAreaAt(PRUint32 aIndex) const
{
return mAreas.ElementAt(aIndex)->mArea;
}
void
nsImageMap::Draw(nsIFrame* aFrame, nsRenderingContext& aRC)
{

View File

@ -66,6 +66,16 @@ public:
*/
nsIContent* GetArea(nscoord aX, nscoord aY) const;
/**
* Return area elements count associated with the image map.
*/
PRUint32 AreaCount() const { return mAreas.Length(); }
/**
* Return area element at the given index.
*/
nsIContent* GetAreaAt(PRUint32 aIndex) const;
void Draw(nsIFrame* aFrame, nsRenderingContext& aRC);
/**