HTMLImageMapAccessible.cpp (6900B)
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 "EventTree.h" 10 #include "mozilla/a11y/Role.h" 11 12 #include "nsCoreUtils.h" 13 #include "nsIFrame.h" 14 #include "nsImageFrame.h" 15 #include "nsImageMap.h" 16 #include "nsLayoutUtils.h" 17 #include "mozilla/dom/HTMLAreaElement.h" 18 19 using namespace mozilla::a11y; 20 21 //////////////////////////////////////////////////////////////////////////////// 22 // HTMLImageMapAccessible 23 //////////////////////////////////////////////////////////////////////////////// 24 25 HTMLImageMapAccessible::HTMLImageMapAccessible(nsIContent* aContent, 26 DocAccessible* aDoc) 27 : ImageAccessible(aContent, aDoc) { 28 mType = eImageMapType; 29 30 UpdateChildAreas(false); 31 } 32 33 //////////////////////////////////////////////////////////////////////////////// 34 // HTMLImageMapAccessible: LocalAccessible public 35 36 role HTMLImageMapAccessible::NativeRole() const { return roles::IMAGE_MAP; } 37 38 //////////////////////////////////////////////////////////////////////////////// 39 // HTMLImageMapAccessible: public 40 41 void HTMLImageMapAccessible::UpdateChildAreas(bool aDoFireEvents) { 42 if (!mContent || !mContent->GetPrimaryFrame()) { 43 return; 44 } 45 nsImageFrame* imageFrame = do_QueryFrame(mContent->GetPrimaryFrame()); 46 47 // If image map is not initialized yet then we trigger one time more later. 48 nsImageMap* imageMapObj = imageFrame->GetExistingImageMap(); 49 if (!imageMapObj) return; 50 51 TreeMutation mt(this, TreeMutation::kNoEvents & !aDoFireEvents); 52 53 // Remove areas that are not a valid part of the image map anymore. 54 for (int32_t childIdx = mChildren.Length() - 1; childIdx >= 0; childIdx--) { 55 LocalAccessible* area = mChildren.ElementAt(childIdx); 56 if (area->GetContent()->GetPrimaryFrame()) continue; 57 58 mt.BeforeRemoval(area); 59 RemoveChild(area); 60 } 61 62 // Insert new areas into the tree. 63 uint32_t areaElmCount = imageMapObj->AreaCount(); 64 for (uint32_t idx = 0; idx < areaElmCount; idx++) { 65 nsIContent* areaContent = imageMapObj->GetAreaAt(idx); 66 LocalAccessible* area = mChildren.SafeElementAt(idx); 67 if (!area || area->GetContent() != areaContent) { 68 RefPtr<LocalAccessible> area = new HTMLAreaAccessible(areaContent, mDoc); 69 mDoc->BindToDocument(area, aria::GetRoleMap(areaContent->AsElement())); 70 71 if (!InsertChildAt(idx, area)) { 72 mDoc->UnbindFromDocument(area); 73 break; 74 } 75 76 mt.AfterInsertion(area); 77 } 78 } 79 80 mt.Done(); 81 } 82 83 LocalAccessible* HTMLImageMapAccessible::GetChildAccessibleFor( 84 const nsINode* aNode) const { 85 uint32_t length = mChildren.Length(); 86 for (uint32_t i = 0; i < length; i++) { 87 LocalAccessible* area = mChildren[i]; 88 if (area->GetContent() == aNode) return area; 89 } 90 91 return nullptr; 92 } 93 94 //////////////////////////////////////////////////////////////////////////////// 95 // HTMLAreaAccessible 96 //////////////////////////////////////////////////////////////////////////////// 97 98 HTMLAreaAccessible::HTMLAreaAccessible(nsIContent* aContent, 99 DocAccessible* aDoc) 100 : HTMLLinkAccessible(aContent, aDoc) { 101 // Make HTML area DOM element not accessible. HTML image map accessible 102 // manages its tree itself. 103 mStateFlags |= eNotNodeMapEntry; 104 } 105 106 //////////////////////////////////////////////////////////////////////////////// 107 // HTMLAreaAccessible: LocalAccessible 108 109 role HTMLAreaAccessible::NativeRole() const { 110 // A link element without an href attribute and without a click listener 111 // should be reported as a generic. 112 if (mContent->IsElement()) { 113 dom::Element* element = mContent->AsElement(); 114 if (!element->HasAttr(nsGkAtoms::href) && 115 !nsCoreUtils::HasClickListener(element)) { 116 return roles::TEXT; 117 } 118 } 119 return HTMLLinkAccessible::NativeRole(); 120 } 121 122 ENameValueFlag HTMLAreaAccessible::NativeName(nsString& aName) const { 123 ENameValueFlag nameFlag = LocalAccessible::NativeName(aName); 124 if (!aName.IsEmpty()) return nameFlag; 125 126 if (!mContent->AsElement()->GetAttr(nsGkAtoms::alt, aName)) { 127 Value(aName); 128 } 129 130 return eNameOK; 131 } 132 133 EDescriptionValueFlag HTMLAreaAccessible::Description( 134 nsString& aDescription) const { 135 aDescription.Truncate(); 136 137 // Still to do - follow IE's standard here 138 RefPtr<dom::HTMLAreaElement> area = 139 dom::HTMLAreaElement::FromNodeOrNull(mContent); 140 if (area) area->GetShape(aDescription); 141 142 return eDescriptionOK; 143 } 144 145 //////////////////////////////////////////////////////////////////////////////// 146 // HTMLAreaAccessible: LocalAccessible public 147 148 LocalAccessible* HTMLAreaAccessible::LocalChildAtPoint( 149 int32_t aX, int32_t aY, EWhichChildAtPoint aWhichChild) { 150 // Don't walk into area accessibles. 151 return this; 152 } 153 154 //////////////////////////////////////////////////////////////////////////////// 155 // HTMLImageMapAccessible: HyperLinkAccessible 156 157 uint32_t HTMLAreaAccessible::StartOffset() { 158 // Image map accessible is not hypertext accessible therefore 159 // StartOffset/EndOffset implementations of LocalAccessible doesn't work here. 160 // We return index in parent because image map contains area links only which 161 // are embedded objects. 162 // XXX: image map should be a hypertext accessible. 163 return IndexInParent(); 164 } 165 166 uint32_t HTMLAreaAccessible::EndOffset() { return IndexInParent() + 1; } 167 168 nsRect HTMLAreaAccessible::RelativeBounds(nsIFrame** aBoundingFrame) const { 169 nsIFrame* frame = GetFrame(); 170 if (!frame) return nsRect(); 171 172 nsImageFrame* imageFrame = do_QueryFrame(frame); 173 nsImageMap* map = imageFrame->GetImageMap(); 174 175 nsRect bounds; 176 nsresult rv = map->GetBoundsForAreaContent(mContent, bounds); 177 178 if (NS_FAILED(rv)) return nsRect(); 179 180 // XXX Areas are screwy; they return their rects as a pair of points, one pair 181 // stored into the width and height. 182 *aBoundingFrame = frame; 183 bounds.SizeTo(bounds.Width() - bounds.X(), bounds.Height() - bounds.Y()); 184 return bounds; 185 } 186 187 nsRect HTMLAreaAccessible::ParentRelativeBounds() { 188 nsIFrame* boundingFrame = nullptr; 189 nsRect relativeBoundsRect = RelativeBounds(&boundingFrame); 190 if (MOZ_UNLIKELY(!boundingFrame)) { 191 // Area is not attached to an image map? 192 return nsRect(); 193 } 194 195 // The relative bounds returned above are relative to this area's 196 // image map, which is technically already "parent relative". 197 // Because area elements are `display:none` to layout, they can't 198 // have transforms or other styling applied directly, and so we 199 // don't apply any additional transforms here. Any transform 200 // at the image map layer will be taken care of when computing bounds 201 // in the parent process. 202 return relativeBoundsRect; 203 }