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"
11 #include "mozilla/a11y/Role.h"
12 #include "AccAttributes.h"
13 #include "AccIterator.h"
14 #include "CacheConstants.h"
17 #include "imgIRequest.h"
18 #include "nsGenericHTMLElement.h"
19 #include "mozilla/dom/BrowsingContext.h"
20 #include "mozilla/dom/Document.h"
21 #include "mozilla/dom/MutationEventBinding.h"
22 #include "nsContentUtils.h"
23 #include "nsIImageLoadingContent.h"
24 #include "nsPIDOMWindow.h"
27 namespace mozilla::a11y
{
29 NS_IMPL_ISUPPORTS_INHERITED(ImageAccessible
, LinkableAccessible
,
30 imgINotificationObserver
)
32 ////////////////////////////////////////////////////////////////////////////////
34 ////////////////////////////////////////////////////////////////////////////////
36 ImageAccessible::ImageAccessible(nsIContent
* aContent
, DocAccessible
* aDoc
)
37 : LinkableAccessible(aContent
, aDoc
),
38 mImageRequestStatus(imgIRequest::STATUS_NONE
) {
40 nsCOMPtr
<nsIImageLoadingContent
> content(do_QueryInterface(mContent
));
42 content
->AddNativeObserver(this);
43 nsCOMPtr
<imgIRequest
> imageRequest
;
44 content
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
45 getter_AddRefs(imageRequest
));
47 imageRequest
->GetImageStatus(&mImageRequestStatus
);
52 ImageAccessible::~ImageAccessible() {}
54 ////////////////////////////////////////////////////////////////////////////////
55 // LocalAccessible public
57 void ImageAccessible::Shutdown() {
58 nsCOMPtr
<nsIImageLoadingContent
> content(do_QueryInterface(mContent
));
60 content
->RemoveNativeObserver(this);
63 LinkableAccessible::Shutdown();
66 uint64_t ImageAccessible::NativeState() const {
67 // The state is a bitfield, get our inherited state, then logically OR it with
68 // states::ANIMATED if this is an animated image.
70 uint64_t state
= LinkableAccessible::NativeState();
72 if (mImageRequestStatus
& imgIRequest::STATUS_IS_ANIMATED
) {
73 state
|= states::ANIMATED
;
76 if (!(mImageRequestStatus
& imgIRequest::STATUS_SIZE_AVAILABLE
)) {
77 nsIFrame
* frame
= GetFrame();
78 MOZ_ASSERT(!frame
|| frame
->AccessibleType() == eImageType
||
79 frame
->AccessibleType() == a11y::eHTMLImageMapType
);
80 if (frame
&& !frame
->HasAnyStateBits(IMAGE_SIZECONSTRAINED
)) {
81 // The size of this image hasn't been constrained and we haven't loaded
82 // enough of the image to know its size yet. This means it currently
83 // has 0 width and height.
84 state
|= states::INVISIBLE
;
91 ENameValueFlag
ImageAccessible::NativeName(nsString
& aName
) const {
92 mContent
->AsElement()->GetAttr(nsGkAtoms::alt
, aName
);
93 if (!aName
.IsEmpty()) return eNameOK
;
95 ENameValueFlag nameFlag
= LocalAccessible::NativeName(aName
);
96 if (!aName
.IsEmpty()) return nameFlag
;
101 role
ImageAccessible::NativeRole() const { return roles::GRAPHIC
; }
103 void ImageAccessible::DOMAttributeChanged(int32_t aNameSpaceID
,
104 nsAtom
* aAttribute
, int32_t aModType
,
105 const nsAttrValue
* aOldValue
,
106 uint64_t aOldState
) {
107 LinkableAccessible::DOMAttributeChanged(aNameSpaceID
, aAttribute
, aModType
,
108 aOldValue
, aOldState
);
110 if (aAttribute
== nsGkAtoms::longdesc
&&
111 (aModType
== dom::MutationEvent_Binding::ADDITION
||
112 aModType
== dom::MutationEvent_Binding::REMOVAL
)) {
113 mDoc
->QueueCacheUpdate(this, CacheDomain::Actions
);
117 ////////////////////////////////////////////////////////////////////////////////
120 uint8_t ImageAccessible::ActionCount() const {
121 uint8_t actionCount
= LinkableAccessible::ActionCount();
122 return HasLongDesc() ? actionCount
+ 1 : actionCount
;
125 void ImageAccessible::ActionNameAt(uint8_t aIndex
, nsAString
& aName
) {
127 if (IsLongDescIndex(aIndex
) && HasLongDesc()) {
128 aName
.AssignLiteral("showlongdesc");
130 LinkableAccessible::ActionNameAt(aIndex
, aName
);
134 bool ImageAccessible::DoAction(uint8_t aIndex
) const {
135 // Get the long description uri and open in a new window.
136 if (!IsLongDescIndex(aIndex
)) return LinkableAccessible::DoAction(aIndex
);
138 nsCOMPtr
<nsIURI
> uri
= GetLongDescURI();
139 if (!uri
) return false;
141 nsAutoCString utf8spec
;
142 uri
->GetSpec(utf8spec
);
143 NS_ConvertUTF8toUTF16
spec(utf8spec
);
145 dom::Document
* document
= mContent
->OwnerDoc();
146 nsCOMPtr
<nsPIDOMWindowOuter
> piWindow
= document
->GetWindow();
147 if (!piWindow
) return false;
149 RefPtr
<dom::BrowsingContext
> tmp
;
150 return NS_SUCCEEDED(piWindow
->Open(spec
, u
""_ns
, u
""_ns
,
151 /* aLoadInfo = */ nullptr,
152 /* aForceNoOpener = */ false,
153 getter_AddRefs(tmp
)));
156 ////////////////////////////////////////////////////////////////////////////////
159 LayoutDeviceIntPoint
ImageAccessible::Position(uint32_t aCoordType
) {
160 LayoutDeviceIntPoint point
= Bounds().TopLeft();
161 nsAccUtils::ConvertScreenCoordsTo(&point
.x
.value
, &point
.y
.value
, aCoordType
,
166 LayoutDeviceIntSize
ImageAccessible::Size() { return Bounds().Size(); }
169 already_AddRefed
<AccAttributes
> ImageAccessible::NativeAttributes() {
170 RefPtr
<AccAttributes
> attributes
= LinkableAccessible::NativeAttributes();
173 mContent
->AsElement()->GetAttr(nsGkAtoms::src
, src
);
174 if (!src
.IsEmpty()) attributes
->SetAttribute(nsGkAtoms::src
, std::move(src
));
176 return attributes
.forget();
179 ////////////////////////////////////////////////////////////////////////////////
182 already_AddRefed
<nsIURI
> ImageAccessible::GetLongDescURI() const {
183 if (mContent
->AsElement()->HasAttr(nsGkAtoms::longdesc
)) {
184 // To check if longdesc contains an invalid url.
185 nsAutoString longdesc
;
186 mContent
->AsElement()->GetAttr(nsGkAtoms::longdesc
, longdesc
);
187 if (longdesc
.FindChar(' ') != -1 || longdesc
.FindChar('\t') != -1 ||
188 longdesc
.FindChar('\r') != -1 || longdesc
.FindChar('\n') != -1) {
191 nsCOMPtr
<nsIURI
> uri
;
192 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri
), longdesc
,
193 mContent
->OwnerDoc(),
194 mContent
->GetBaseURI());
198 DocAccessible
* document
= Document();
200 IDRefsIterator
iter(document
, mContent
, nsGkAtoms::aria_describedby
);
201 while (nsIContent
* target
= iter
.NextElem()) {
202 if ((target
->IsHTMLElement(nsGkAtoms::a
) ||
203 target
->IsHTMLElement(nsGkAtoms::area
)) &&
204 target
->AsElement()->HasAttr(nsGkAtoms::href
)) {
205 nsGenericHTMLElement
* element
= nsGenericHTMLElement::FromNode(target
);
207 nsCOMPtr
<nsIURI
> uri
;
208 element
->GetURIAttr(nsGkAtoms::href
, nullptr, getter_AddRefs(uri
));
217 bool ImageAccessible::IsLongDescIndex(uint8_t aIndex
) const {
218 return aIndex
== LinkableAccessible::ActionCount();
221 ////////////////////////////////////////////////////////////////////////////////
222 // imgINotificationObserver
224 void ImageAccessible::Notify(imgIRequest
* aRequest
, int32_t aType
,
225 const nsIntRect
* aData
) {
226 if (aType
!= imgINotificationObserver::FRAME_COMPLETE
&&
227 aType
!= imgINotificationObserver::LOAD_COMPLETE
&&
228 aType
!= imgINotificationObserver::DECODE_COMPLETE
) {
229 // We should update our state if the whole image was decoded,
230 // or the first frame in the case of a gif.
234 if (IsDefunct() || !mParent
) {
238 uint32_t status
= imgIRequest::STATUS_NONE
;
239 aRequest
->GetImageStatus(&status
);
241 if ((status
^ mImageRequestStatus
) & imgIRequest::STATUS_SIZE_AVAILABLE
) {
242 nsIFrame
* frame
= GetFrame();
243 if (frame
&& !frame
->HasAnyStateBits(IMAGE_SIZECONSTRAINED
)) {
244 RefPtr
<AccEvent
> event
= new AccStateChangeEvent(
245 this, states::INVISIBLE
,
246 !(status
& imgIRequest::STATUS_SIZE_AVAILABLE
));
247 mDoc
->FireDelayedEvent(event
);
251 if ((status
^ mImageRequestStatus
) & imgIRequest::STATUS_IS_ANIMATED
) {
252 RefPtr
<AccEvent
> event
= new AccStateChangeEvent(
253 this, states::ANIMATED
, (status
& imgIRequest::STATUS_IS_ANIMATED
));
254 mDoc
->FireDelayedEvent(event
);
257 mImageRequestStatus
= status
;
260 } // namespace mozilla::a11y