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/. */
6 #include "ImageAccessible.h"
8 #include "DocAccessible-inl.h"
9 #include "LocalAccessible-inl.h"
10 #include "nsAccUtils.h"
12 #include "AccAttributes.h"
13 #include "AccIterator.h"
14 #include "CacheConstants.h"
17 #include "imgIContainer.h"
18 #include "imgIRequest.h"
19 #include "nsGenericHTMLElement.h"
20 #include "mozilla/dom/BrowsingContext.h"
21 #include "mozilla/dom/Document.h"
22 #include "mozilla/dom/MutationEventBinding.h"
23 #include "nsContentUtils.h"
24 #include "nsIImageLoadingContent.h"
25 #include "nsPIDOMWindow.h"
28 namespace mozilla::a11y
{
30 NS_IMPL_ISUPPORTS_INHERITED(ImageAccessible
, LinkableAccessible
,
31 imgINotificationObserver
)
33 ////////////////////////////////////////////////////////////////////////////////
35 ////////////////////////////////////////////////////////////////////////////////
37 ImageAccessible::ImageAccessible(nsIContent
* aContent
, DocAccessible
* aDoc
)
38 : LinkableAccessible(aContent
, aDoc
),
39 mImageRequestStatus(imgIRequest::STATUS_NONE
) {
41 nsCOMPtr
<nsIImageLoadingContent
> content(do_QueryInterface(mContent
));
43 content
->AddNativeObserver(this);
44 nsCOMPtr
<imgIRequest
> imageRequest
;
45 content
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
46 getter_AddRefs(imageRequest
));
48 imageRequest
->GetImageStatus(&mImageRequestStatus
);
53 ImageAccessible::~ImageAccessible() {}
55 ////////////////////////////////////////////////////////////////////////////////
56 // LocalAccessible public
58 void ImageAccessible::Shutdown() {
59 nsCOMPtr
<nsIImageLoadingContent
> content(do_QueryInterface(mContent
));
61 content
->RemoveNativeObserver(this);
64 LinkableAccessible::Shutdown();
67 uint64_t ImageAccessible::NativeState() const {
68 // The state is a bitfield, get our inherited state, then logically OR it with
69 // states::ANIMATED if this is an animated image.
71 uint64_t state
= LinkableAccessible::NativeState();
73 if (mImageRequestStatus
& imgIRequest::STATUS_IS_ANIMATED
) {
74 state
|= states::ANIMATED
;
77 if (!(mImageRequestStatus
& imgIRequest::STATUS_SIZE_AVAILABLE
)) {
78 nsIFrame
* frame
= GetFrame();
79 MOZ_ASSERT(!frame
|| frame
->AccessibleType() == eImageType
||
80 frame
->AccessibleType() == a11y::eHTMLImageMapType
||
81 frame
->IsImageBoxFrame());
82 if (frame
&& !(frame
->GetStateBits() & IMAGE_SIZECONSTRAINED
)) {
83 // The size of this image hasn't been constrained and we haven't loaded
84 // enough of the image to know its size yet. This means it currently
85 // has 0 width and height.
86 state
|= states::INVISIBLE
;
93 ENameValueFlag
ImageAccessible::NativeName(nsString
& aName
) const {
95 mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::alt
, aName
);
96 if (!aName
.IsEmpty()) return eNameOK
;
98 ENameValueFlag nameFlag
= LocalAccessible::NativeName(aName
);
99 if (!aName
.IsEmpty()) return nameFlag
;
101 // No accessible name but empty 'alt' attribute is present. If further name
102 // computation algorithm doesn't provide non empty name then it means
103 // an empty 'alt' attribute was used to indicate a decorative image (see
104 // LocalAccessible::Name() method for details).
105 return hasAltAttrib
? eNoNameOnPurpose
: eNameOK
;
108 role
ImageAccessible::NativeRole() const { return roles::GRAPHIC
; }
110 void ImageAccessible::DOMAttributeChanged(int32_t aNameSpaceID
,
111 nsAtom
* aAttribute
, int32_t aModType
,
112 const nsAttrValue
* aOldValue
,
113 uint64_t aOldState
) {
114 LinkableAccessible::DOMAttributeChanged(aNameSpaceID
, aAttribute
, aModType
,
115 aOldValue
, aOldState
);
117 if (aAttribute
== nsGkAtoms::longdesc
&&
118 (aModType
== dom::MutationEvent_Binding::ADDITION
||
119 aModType
== dom::MutationEvent_Binding::REMOVAL
)) {
120 SendCache(CacheDomain::Actions
, CacheUpdateType::Update
);
124 ////////////////////////////////////////////////////////////////////////////////
127 uint8_t ImageAccessible::ActionCount() const {
128 uint8_t actionCount
= LinkableAccessible::ActionCount();
129 return HasLongDesc() ? actionCount
+ 1 : actionCount
;
132 void ImageAccessible::ActionNameAt(uint8_t aIndex
, nsAString
& aName
) {
134 if (IsLongDescIndex(aIndex
) && HasLongDesc()) {
135 aName
.AssignLiteral("showlongdesc");
137 LinkableAccessible::ActionNameAt(aIndex
, aName
);
141 bool ImageAccessible::DoAction(uint8_t aIndex
) const {
142 // Get the long description uri and open in a new window.
143 if (!IsLongDescIndex(aIndex
)) return LinkableAccessible::DoAction(aIndex
);
145 nsCOMPtr
<nsIURI
> uri
= GetLongDescURI();
146 if (!uri
) return false;
148 nsAutoCString utf8spec
;
149 uri
->GetSpec(utf8spec
);
150 NS_ConvertUTF8toUTF16
spec(utf8spec
);
152 dom::Document
* document
= mContent
->OwnerDoc();
153 nsCOMPtr
<nsPIDOMWindowOuter
> piWindow
= document
->GetWindow();
154 if (!piWindow
) return false;
156 RefPtr
<dom::BrowsingContext
> tmp
;
157 return NS_SUCCEEDED(piWindow
->Open(spec
, u
""_ns
, u
""_ns
,
158 /* aLoadInfo = */ nullptr,
159 /* aForceNoOpener = */ false,
160 getter_AddRefs(tmp
)));
163 ////////////////////////////////////////////////////////////////////////////////
166 LayoutDeviceIntPoint
ImageAccessible::Position(uint32_t aCoordType
) {
167 LayoutDeviceIntPoint point
= Bounds().TopLeft();
168 nsAccUtils::ConvertScreenCoordsTo(&point
.x
, &point
.y
, aCoordType
, this);
172 LayoutDeviceIntSize
ImageAccessible::Size() { return Bounds().Size(); }
175 already_AddRefed
<AccAttributes
> ImageAccessible::NativeAttributes() {
176 RefPtr
<AccAttributes
> attributes
= LinkableAccessible::NativeAttributes();
179 mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::src
, src
);
180 if (!src
.IsEmpty()) attributes
->SetAttribute(nsGkAtoms::src
, std::move(src
));
182 return attributes
.forget();
185 ////////////////////////////////////////////////////////////////////////////////
188 already_AddRefed
<nsIURI
> ImageAccessible::GetLongDescURI() const {
189 if (mContent
->AsElement()->HasAttr(kNameSpaceID_None
, nsGkAtoms::longdesc
)) {
190 // To check if longdesc contains an invalid url.
191 nsAutoString longdesc
;
192 mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::longdesc
,
194 if (longdesc
.FindChar(' ') != -1 || longdesc
.FindChar('\t') != -1 ||
195 longdesc
.FindChar('\r') != -1 || longdesc
.FindChar('\n') != -1) {
198 nsCOMPtr
<nsIURI
> uri
;
199 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri
), longdesc
,
200 mContent
->OwnerDoc(),
201 mContent
->GetBaseURI());
205 DocAccessible
* document
= Document();
207 IDRefsIterator
iter(document
, mContent
, nsGkAtoms::aria_describedby
);
208 while (nsIContent
* target
= iter
.NextElem()) {
209 if ((target
->IsHTMLElement(nsGkAtoms::a
) ||
210 target
->IsHTMLElement(nsGkAtoms::area
)) &&
211 target
->AsElement()->HasAttr(kNameSpaceID_None
, nsGkAtoms::href
)) {
212 nsGenericHTMLElement
* element
= nsGenericHTMLElement::FromNode(target
);
214 nsCOMPtr
<nsIURI
> uri
;
215 element
->GetURIAttr(nsGkAtoms::href
, nullptr, getter_AddRefs(uri
));
224 bool ImageAccessible::IsLongDescIndex(uint8_t aIndex
) const {
225 return aIndex
== LinkableAccessible::ActionCount();
228 ////////////////////////////////////////////////////////////////////////////////
229 // imgINotificationObserver
231 void ImageAccessible::Notify(imgIRequest
* aRequest
, int32_t aType
,
232 const nsIntRect
* aData
) {
233 if (aType
!= imgINotificationObserver::FRAME_COMPLETE
&&
234 aType
!= imgINotificationObserver::LOAD_COMPLETE
&&
235 aType
!= imgINotificationObserver::DECODE_COMPLETE
) {
236 // We should update our state if the whole image was decoded,
237 // or the first frame in the case of a gif.
241 if (IsDefunct() || !mParent
) {
245 uint32_t status
= imgIRequest::STATUS_NONE
;
246 aRequest
->GetImageStatus(&status
);
248 if ((status
^ mImageRequestStatus
) & imgIRequest::STATUS_SIZE_AVAILABLE
) {
249 nsIFrame
* frame
= GetFrame();
250 if (frame
&& !(frame
->GetStateBits() & IMAGE_SIZECONSTRAINED
)) {
251 RefPtr
<AccEvent
> event
= new AccStateChangeEvent(
252 this, states::INVISIBLE
,
253 !(status
& imgIRequest::STATUS_SIZE_AVAILABLE
));
254 mDoc
->FireDelayedEvent(event
);
258 if ((status
^ mImageRequestStatus
) & imgIRequest::STATUS_IS_ANIMATED
) {
259 RefPtr
<AccEvent
> event
= new AccStateChangeEvent(
260 this, states::ANIMATED
, (status
& imgIRequest::STATUS_IS_ANIMATED
));
261 mDoc
->FireDelayedEvent(event
);
264 mImageRequestStatus
= status
;
267 } // namespace mozilla::a11y