1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "HTMLImageMapAccessible.h"
7 
8 #include "ARIAMap.h"
9 #include "nsAccUtils.h"
10 #include "DocAccessible-inl.h"
11 #include "Role.h"
12 
13 #include "nsIFrame.h"
14 #include "nsImageFrame.h"
15 #include "nsImageMap.h"
16 #include "nsIURI.h"
17 #include "nsLayoutUtils.h"
18 #include "mozilla/dom/HTMLAreaElement.h"
19 
20 using namespace mozilla::a11y;
21 
22 ////////////////////////////////////////////////////////////////////////////////
23 // HTMLImageMapAccessible
24 ////////////////////////////////////////////////////////////////////////////////
25 
HTMLImageMapAccessible(nsIContent * aContent,DocAccessible * aDoc)26 HTMLImageMapAccessible::HTMLImageMapAccessible(nsIContent* aContent,
27                                                DocAccessible* aDoc)
28     : ImageAccessibleWrap(aContent, aDoc) {
29   mType = eImageMapType;
30 
31   UpdateChildAreas(false);
32 }
33 
34 ////////////////////////////////////////////////////////////////////////////////
35 // HTMLImageMapAccessible: LocalAccessible public
36 
NativeRole() const37 role HTMLImageMapAccessible::NativeRole() const { return roles::IMAGE_MAP; }
38 
39 ////////////////////////////////////////////////////////////////////////////////
40 // HTMLImageMapAccessible: HyperLinkAccessible
41 
AnchorCount()42 uint32_t HTMLImageMapAccessible::AnchorCount() { return ChildCount(); }
43 
AnchorAt(uint32_t aAnchorIndex)44 LocalAccessible* HTMLImageMapAccessible::AnchorAt(uint32_t aAnchorIndex) {
45   return LocalChildAt(aAnchorIndex);
46 }
47 
AnchorURIAt(uint32_t aAnchorIndex) const48 already_AddRefed<nsIURI> HTMLImageMapAccessible::AnchorURIAt(
49     uint32_t aAnchorIndex) const {
50   LocalAccessible* area = LocalChildAt(aAnchorIndex);
51   if (!area) return nullptr;
52 
53   nsIContent* linkContent = area->GetContent();
54   return linkContent ? linkContent->GetHrefURI() : nullptr;
55 }
56 
57 ////////////////////////////////////////////////////////////////////////////////
58 // HTMLImageMapAccessible: public
59 
UpdateChildAreas(bool aDoFireEvents)60 void HTMLImageMapAccessible::UpdateChildAreas(bool aDoFireEvents) {
61   nsImageFrame* imageFrame = do_QueryFrame(mContent->GetPrimaryFrame());
62 
63   // If image map is not initialized yet then we trigger one time more later.
64   nsImageMap* imageMapObj = imageFrame->GetExistingImageMap();
65   if (!imageMapObj) return;
66 
67   TreeMutation mt(this, TreeMutation::kNoEvents & !aDoFireEvents);
68 
69   // Remove areas that are not a valid part of the image map anymore.
70   for (int32_t childIdx = mChildren.Length() - 1; childIdx >= 0; childIdx--) {
71     LocalAccessible* area = mChildren.ElementAt(childIdx);
72     if (area->GetContent()->GetPrimaryFrame()) continue;
73 
74     mt.BeforeRemoval(area);
75     RemoveChild(area);
76   }
77 
78   // Insert new areas into the tree.
79   uint32_t areaElmCount = imageMapObj->AreaCount();
80   for (uint32_t idx = 0; idx < areaElmCount; idx++) {
81     nsIContent* areaContent = imageMapObj->GetAreaAt(idx);
82     LocalAccessible* area = mChildren.SafeElementAt(idx);
83     if (!area || area->GetContent() != areaContent) {
84       RefPtr<LocalAccessible> area = new HTMLAreaAccessible(areaContent, mDoc);
85       mDoc->BindToDocument(area, aria::GetRoleMap(areaContent->AsElement()));
86 
87       if (!InsertChildAt(idx, area)) {
88         mDoc->UnbindFromDocument(area);
89         break;
90       }
91 
92       mt.AfterInsertion(area);
93     }
94   }
95 
96   mt.Done();
97 }
98 
GetChildAccessibleFor(const nsINode * aNode) const99 LocalAccessible* HTMLImageMapAccessible::GetChildAccessibleFor(
100     const nsINode* aNode) const {
101   uint32_t length = mChildren.Length();
102   for (uint32_t i = 0; i < length; i++) {
103     LocalAccessible* area = mChildren[i];
104     if (area->GetContent() == aNode) return area;
105   }
106 
107   return nullptr;
108 }
109 
110 ////////////////////////////////////////////////////////////////////////////////
111 // HTMLAreaAccessible
112 ////////////////////////////////////////////////////////////////////////////////
113 
HTMLAreaAccessible(nsIContent * aContent,DocAccessible * aDoc)114 HTMLAreaAccessible::HTMLAreaAccessible(nsIContent* aContent,
115                                        DocAccessible* aDoc)
116     : HTMLLinkAccessible(aContent, aDoc) {
117   // Make HTML area DOM element not accessible. HTML image map accessible
118   // manages its tree itself.
119   mStateFlags |= eNotNodeMapEntry;
120 }
121 
122 ////////////////////////////////////////////////////////////////////////////////
123 // HTMLAreaAccessible: LocalAccessible
124 
NativeName(nsString & aName) const125 ENameValueFlag HTMLAreaAccessible::NativeName(nsString& aName) const {
126   ENameValueFlag nameFlag = LocalAccessible::NativeName(aName);
127   if (!aName.IsEmpty()) return nameFlag;
128 
129   if (!mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::alt,
130                                       aName)) {
131     Value(aName);
132   }
133 
134   return eNameOK;
135 }
136 
Description(nsString & aDescription) const137 void HTMLAreaAccessible::Description(nsString& aDescription) const {
138   aDescription.Truncate();
139 
140   // Still to do - follow IE's standard here
141   RefPtr<dom::HTMLAreaElement> area =
142       dom::HTMLAreaElement::FromNodeOrNull(mContent);
143   if (area) area->GetShape(aDescription);
144 }
145 
146 ////////////////////////////////////////////////////////////////////////////////
147 // HTMLAreaAccessible: LocalAccessible public
148 
LocalChildAtPoint(int32_t aX,int32_t aY,EWhichChildAtPoint aWhichChild)149 LocalAccessible* HTMLAreaAccessible::LocalChildAtPoint(
150     int32_t aX, int32_t aY, EWhichChildAtPoint aWhichChild) {
151   // Don't walk into area accessibles.
152   return this;
153 }
154 
155 ////////////////////////////////////////////////////////////////////////////////
156 // HTMLImageMapAccessible: HyperLinkAccessible
157 
StartOffset()158 uint32_t HTMLAreaAccessible::StartOffset() {
159   // Image map accessible is not hypertext accessible therefore
160   // StartOffset/EndOffset implementations of LocalAccessible doesn't work here.
161   // We return index in parent because image map contains area links only which
162   // are embedded objects.
163   // XXX: image map should be a hypertext accessible.
164   return IndexInParent();
165 }
166 
EndOffset()167 uint32_t HTMLAreaAccessible::EndOffset() { return IndexInParent() + 1; }
168 
RelativeBounds(nsIFrame ** aBoundingFrame) const169 nsRect HTMLAreaAccessible::RelativeBounds(nsIFrame** aBoundingFrame) const {
170   nsIFrame* frame = GetFrame();
171   if (!frame) return nsRect();
172 
173   nsImageFrame* imageFrame = do_QueryFrame(frame);
174   nsImageMap* map = imageFrame->GetImageMap();
175 
176   nsRect bounds;
177   nsresult rv = map->GetBoundsForAreaContent(mContent, bounds);
178 
179   if (NS_FAILED(rv)) return nsRect();
180 
181   // XXX Areas are screwy; they return their rects as a pair of points, one pair
182   // stored into the width and height.
183   *aBoundingFrame = frame;
184   bounds.SizeTo(bounds.Width() - bounds.X(), bounds.Height() - bounds.Y());
185   return bounds;
186 }
187 
ParentRelativeBounds()188 nsRect HTMLAreaAccessible::ParentRelativeBounds() {
189   nsIFrame* boundingFrame = nullptr;
190   nsRect relativeBoundsRect = RelativeBounds(&boundingFrame);
191 
192   nsIFrame* parentBoundingFrame = nullptr;
193   if (mParent) {
194     parentBoundingFrame = mParent->GetFrame();
195   }
196 
197   if (!parentBoundingFrame) {
198     // if we can't get the bounding frame, use the pres shell root for the
199     // bounding frame RelativeBounds returned
200     parentBoundingFrame =
201         nsLayoutUtils::GetContainingBlockForClientRect(boundingFrame);
202   }
203 
204   nsLayoutUtils::TransformRect(boundingFrame, parentBoundingFrame,
205                                relativeBoundsRect);
206 
207   return relativeBoundsRect;
208 }
209