ImageAccessible.cpp (8909B)
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 "ImageAccessible.h" 7 8 #include "DocAccessible-inl.h" 9 #include "LocalAccessible-inl.h" 10 #include "nsAccUtils.h" 11 #include "mozilla/a11y/Role.h" 12 #include "AccAttributes.h" 13 #include "AccIterator.h" 14 #include "CacheConstants.h" 15 #include "States.h" 16 17 #include "imgIRequest.h" 18 #include "nsGenericHTMLElement.h" 19 #include "mozilla/dom/BrowsingContext.h" 20 #include "mozilla/dom/Document.h" 21 #include "nsContentUtils.h" 22 #include "nsIImageLoadingContent.h" 23 #include "nsPIDOMWindow.h" 24 #include "nsIURI.h" 25 26 namespace mozilla::a11y { 27 28 NS_IMPL_ISUPPORTS_INHERITED(ImageAccessible, LinkableAccessible, 29 imgINotificationObserver) 30 31 //////////////////////////////////////////////////////////////////////////////// 32 // ImageAccessible 33 //////////////////////////////////////////////////////////////////////////////// 34 35 ImageAccessible::ImageAccessible(nsIContent* aContent, DocAccessible* aDoc) 36 : LinkableAccessible(aContent, aDoc), 37 mImageRequestStatus(imgIRequest::STATUS_NONE) { 38 mType = eImageType; 39 nsCOMPtr<nsIImageLoadingContent> content(do_QueryInterface(mContent)); 40 if (content) { 41 content->AddNativeObserver(this); 42 nsCOMPtr<imgIRequest> imageRequest; 43 content->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, 44 getter_AddRefs(imageRequest)); 45 if (imageRequest) { 46 imageRequest->GetImageStatus(&mImageRequestStatus); 47 } 48 } 49 } 50 51 ImageAccessible::~ImageAccessible() {} 52 53 //////////////////////////////////////////////////////////////////////////////// 54 // LocalAccessible public 55 56 void ImageAccessible::Shutdown() { 57 nsCOMPtr<nsIImageLoadingContent> content(do_QueryInterface(mContent)); 58 if (content) { 59 content->RemoveNativeObserver(this); 60 } 61 62 LinkableAccessible::Shutdown(); 63 } 64 65 uint64_t ImageAccessible::NativeState() const { 66 // The state is a bitfield, get our inherited state, then logically OR it with 67 // states::ANIMATED if this is an animated image. 68 69 uint64_t state = LinkableAccessible::NativeState(); 70 71 if (mImageRequestStatus & imgIRequest::STATUS_IS_ANIMATED) { 72 state |= states::ANIMATED; 73 } 74 75 if (!(mImageRequestStatus & imgIRequest::STATUS_SIZE_AVAILABLE)) { 76 nsIFrame* frame = GetFrame(); 77 MOZ_ASSERT(!frame || frame->AccessibleType() == eImageType || 78 frame->AccessibleType() == a11y::eHTMLImageMapType); 79 if (frame && !frame->HasAnyStateBits(IMAGE_SIZECONSTRAINED)) { 80 // The size of this image hasn't been constrained and we haven't loaded 81 // enough of the image to know its size yet. This means it currently 82 // has 0 width and height. 83 state |= states::INVISIBLE; 84 } 85 } 86 87 return state; 88 } 89 90 ENameValueFlag ImageAccessible::NativeName(nsString& aName) const { 91 mContent->AsElement()->GetAttr(nsGkAtoms::alt, aName); 92 if (!aName.IsEmpty()) return eNameOK; 93 94 ENameValueFlag nameFlag = LocalAccessible::NativeName(aName); 95 if (!aName.IsEmpty()) return nameFlag; 96 97 return eNameOK; 98 } 99 100 role ImageAccessible::NativeRole() const { return roles::GRAPHIC; } 101 102 void ImageAccessible::DOMAttributeChanged(int32_t aNameSpaceID, 103 nsAtom* aAttribute, 104 AttrModType aModType, 105 const nsAttrValue* aOldValue, 106 uint64_t aOldState) { 107 LinkableAccessible::DOMAttributeChanged(aNameSpaceID, aAttribute, aModType, 108 aOldValue, aOldState); 109 110 if (aAttribute == nsGkAtoms::longdesc && IsAdditionOrRemoval(aModType)) { 111 mDoc->QueueCacheUpdate(this, CacheDomain::Actions); 112 } 113 } 114 115 //////////////////////////////////////////////////////////////////////////////// 116 // LocalAccessible 117 118 uint8_t ImageAccessible::ActionCount() const { 119 uint8_t actionCount = LinkableAccessible::ActionCount(); 120 return HasLongDesc() ? actionCount + 1 : actionCount; 121 } 122 123 void ImageAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) { 124 aName.Truncate(); 125 if (IsLongDescIndex(aIndex) && HasLongDesc()) { 126 aName.AssignLiteral("showlongdesc"); 127 } else { 128 LinkableAccessible::ActionNameAt(aIndex, aName); 129 } 130 } 131 132 bool ImageAccessible::DoAction(uint8_t aIndex) const { 133 // Get the long description uri and open in a new window. 134 if (!IsLongDescIndex(aIndex)) return LinkableAccessible::DoAction(aIndex); 135 136 nsCOMPtr<nsIURI> uri = GetLongDescURI(); 137 if (!uri) return false; 138 139 nsAutoCString spec; 140 uri->GetSpec(spec); 141 142 dom::Document* document = mContent->OwnerDoc(); 143 nsCOMPtr<nsPIDOMWindowOuter> piWindow = document->GetWindow(); 144 if (!piWindow) return false; 145 146 RefPtr<dom::BrowsingContext> tmp; 147 return NS_SUCCEEDED(piWindow->Open(spec, u""_ns, u""_ns, 148 /* aLoadInfo = */ nullptr, 149 /* aForceNoOpener = */ false, 150 getter_AddRefs(tmp))); 151 } 152 153 //////////////////////////////////////////////////////////////////////////////// 154 // ImageAccessible 155 156 LayoutDeviceIntPoint ImageAccessible::Position(uint32_t aCoordType) { 157 LayoutDeviceIntPoint point = Bounds().TopLeft(); 158 nsAccUtils::ConvertScreenCoordsTo(&point.x.value, &point.y.value, aCoordType, 159 this); 160 return point; 161 } 162 163 LayoutDeviceIntSize ImageAccessible::Size() { return Bounds().Size(); } 164 165 // LocalAccessible 166 already_AddRefed<AccAttributes> ImageAccessible::NativeAttributes() { 167 RefPtr<AccAttributes> attributes = LinkableAccessible::NativeAttributes(); 168 169 nsString src; 170 mContent->AsElement()->GetAttr(nsGkAtoms::src, src); 171 if (!src.IsEmpty()) attributes->SetAttribute(nsGkAtoms::src, std::move(src)); 172 173 return attributes.forget(); 174 } 175 176 //////////////////////////////////////////////////////////////////////////////// 177 // Private methods 178 179 already_AddRefed<nsIURI> ImageAccessible::GetLongDescURI() const { 180 if (mContent->AsElement()->HasAttr(nsGkAtoms::longdesc)) { 181 // To check if longdesc contains an invalid url. 182 nsAutoString longdesc; 183 mContent->AsElement()->GetAttr(nsGkAtoms::longdesc, longdesc); 184 if (longdesc.FindChar(' ') != -1 || longdesc.FindChar('\t') != -1 || 185 longdesc.FindChar('\r') != -1 || longdesc.FindChar('\n') != -1) { 186 return nullptr; 187 } 188 nsCOMPtr<nsIURI> uri; 189 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), longdesc, 190 mContent->OwnerDoc(), 191 mContent->GetBaseURI()); 192 return uri.forget(); 193 } 194 195 DocAccessible* document = Document(); 196 if (document) { 197 AssociatedElementsIterator iter(document, mContent, 198 nsGkAtoms::aria_describedby); 199 while (nsIContent* target = iter.NextElem()) { 200 if ((target->IsHTMLElement(nsGkAtoms::a) || 201 target->IsHTMLElement(nsGkAtoms::area)) && 202 target->AsElement()->HasAttr(nsGkAtoms::href)) { 203 nsGenericHTMLElement* element = nsGenericHTMLElement::FromNode(target); 204 205 nsCOMPtr<nsIURI> uri; 206 element->GetURIAttr(nsGkAtoms::href, nullptr, getter_AddRefs(uri)); 207 return uri.forget(); 208 } 209 } 210 } 211 212 return nullptr; 213 } 214 215 bool ImageAccessible::IsLongDescIndex(uint8_t aIndex) const { 216 return aIndex == LinkableAccessible::ActionCount(); 217 } 218 219 //////////////////////////////////////////////////////////////////////////////// 220 // imgINotificationObserver 221 222 void ImageAccessible::Notify(imgIRequest* aRequest, int32_t aType, 223 const nsIntRect* aData) { 224 if (aType != imgINotificationObserver::FRAME_COMPLETE && 225 aType != imgINotificationObserver::LOAD_COMPLETE && 226 aType != imgINotificationObserver::DECODE_COMPLETE) { 227 // We should update our state if the whole image was decoded, 228 // or the first frame in the case of a gif. 229 return; 230 } 231 232 if (IsDefunct() || !mParent) { 233 return; 234 } 235 236 uint32_t status = imgIRequest::STATUS_NONE; 237 aRequest->GetImageStatus(&status); 238 239 if ((status ^ mImageRequestStatus) & imgIRequest::STATUS_SIZE_AVAILABLE) { 240 nsIFrame* frame = GetFrame(); 241 if (frame && !frame->HasAnyStateBits(IMAGE_SIZECONSTRAINED)) { 242 RefPtr<AccEvent> event = new AccStateChangeEvent( 243 this, states::INVISIBLE, 244 !(status & imgIRequest::STATUS_SIZE_AVAILABLE)); 245 mDoc->FireDelayedEvent(event); 246 } 247 } 248 249 if ((status ^ mImageRequestStatus) & imgIRequest::STATUS_IS_ANIMATED) { 250 RefPtr<AccEvent> event = new AccStateChangeEvent( 251 this, states::ANIMATED, (status & imgIRequest::STATUS_IS_ANIMATED)); 252 mDoc->FireDelayedEvent(event); 253 } 254 255 mImageRequestStatus = status; 256 } 257 258 } // namespace mozilla::a11y