Bug 1733238 - P2: Make ImageAccessible an imgINotificationObserver. r=Jamie

Use imgINotificationObserver to get notified of size availability.
Layout uses an observer to wait for this too. Our observer is notified
after layout so we should have bounds by then.

We need to store the request status because we get a lot of "replayed"
status changes that would cause chatty and wrong state change events
when the accessible is first created and bound to parent.

We can use that status for both INVISIBLE and ANIMATED states in
NativeState.

Differential Revision: https://phabricator.services.mozilla.com/D127719
This commit is contained in:
Eitan Isaacson 2021-10-12 15:46:38 +00:00
Родитель 25d16b6a5d
Коммит bc023a74f1
5 изменённых файлов: 75 добавлений и 62 удалений

Просмотреть файл

@ -365,25 +365,6 @@ void nsAccessibilityService::FireAccessibleEvent(uint32_t aEvent,
nsEventShell::FireEvent(aEvent, aTarget);
}
void nsAccessibilityService::NotifyOfImageSizeAvailable(
mozilla::PresShell* aPresShell, nsIContent* aContent) {
// If the size of an image is initially unknown, it will have the invisible
// state (and a 0 width and height), causing it to be ignored by some screen
// readers. Fire a state change event to update any client caches.
DocAccessible* document = GetDocAccessible(aPresShell);
if (document) {
LocalAccessible* accessible = document->GetAccessible(aContent);
// The accessible may not be an ImageAccessible if this was previously a
// broken image with an alt attribute. In that case, do nothing; the
// accessible will be recreated if this becomes a valid image.
if (accessible && accessible->IsImage()) {
RefPtr<AccEvent> event =
new AccStateChangeEvent(accessible, states::INVISIBLE, false);
document->FireDelayedEvent(event);
}
}
}
void nsAccessibilityService::NotifyOfPossibleBoundsChange(
mozilla::PresShell* aPresShell, nsIContent* aContent) {
if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {

Просмотреть файл

@ -221,15 +221,6 @@ class nsAccessibilityService final : public mozilla::a11y::DocManager,
void FireAccessibleEvent(uint32_t aEvent, LocalAccessible* aTarget);
/**
* Notify accessibility that the size has become available for an image.
* This occurs when the size of an image is initially not known, but we've
* now loaded enough data to know the size.
* Called by layout.
*/
void NotifyOfImageSizeAvailable(mozilla::PresShell* aPresShell,
nsIContent* aContent);
void NotifyOfPossibleBoundsChange(mozilla::PresShell* aPresShell,
nsIContent* aContent);

Просмотреть файл

@ -5,6 +5,8 @@
#include "ImageAccessible.h"
#include "DocAccessible-inl.h"
#include "LocalAccessible-inl.h"
#include "nsAccUtils.h"
#include "Role.h"
#include "AccAttributes.h"
@ -23,13 +25,27 @@
using namespace mozilla::a11y;
NS_IMPL_ISUPPORTS_INHERITED(ImageAccessible, LinkableAccessible,
imgINotificationObserver)
////////////////////////////////////////////////////////////////////////////////
// ImageAccessible
////////////////////////////////////////////////////////////////////////////////
ImageAccessible::ImageAccessible(nsIContent* aContent, DocAccessible* aDoc)
: LinkableAccessible(aContent, aDoc) {
: LinkableAccessible(aContent, aDoc),
mImageRequestStatus(imgIRequest::STATUS_NONE) {
mType = eImageType;
nsCOMPtr<nsIImageLoadingContent> content(do_QueryInterface(mContent));
if (content) {
content->AddNativeObserver(this);
nsCOMPtr<imgIRequest> imageRequest;
content->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
getter_AddRefs(imageRequest));
if (imageRequest) {
imageRequest->GetImageStatus(&mImageRequestStatus);
}
}
}
ImageAccessible::~ImageAccessible() {}
@ -37,43 +53,34 @@ ImageAccessible::~ImageAccessible() {}
////////////////////////////////////////////////////////////////////////////////
// LocalAccessible public
void ImageAccessible::Shutdown() {
nsCOMPtr<nsIImageLoadingContent> content(do_QueryInterface(mContent));
if (content) {
content->RemoveNativeObserver(this);
}
LinkableAccessible::Shutdown();
}
uint64_t ImageAccessible::NativeState() const {
// The state is a bitfield, get our inherited state, then logically OR it with
// states::ANIMATED if this is an animated image.
uint64_t state = LinkableAccessible::NativeState();
nsCOMPtr<nsIImageLoadingContent> content(do_QueryInterface(mContent));
nsCOMPtr<imgIRequest> imageRequest;
if (content) {
content->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
getter_AddRefs(imageRequest));
if (mImageRequestStatus & imgIRequest::STATUS_IS_ANIMATED) {
state |= states::ANIMATED;
}
if (imageRequest) {
nsCOMPtr<imgIContainer> imgContainer;
imageRequest->GetImage(getter_AddRefs(imgContainer));
if (imgContainer) {
bool animated = false;
imgContainer->GetAnimated(&animated);
if (animated) {
state |= states::ANIMATED;
}
}
if (!(mImageRequestStatus & imgIRequest::STATUS_SIZE_AVAILABLE)) {
nsIFrame* frame = GetFrame();
MOZ_ASSERT(!frame || frame->AccessibleType() == eImageType ||
frame->AccessibleType() == a11y::eHTMLImageMapType);
if (frame && !(frame->GetStateBits() & IMAGE_SIZECONSTRAINED)) {
uint32_t status = imgIRequest::STATUS_NONE;
imageRequest->GetImageStatus(&status);
if (!(status & imgIRequest::STATUS_SIZE_AVAILABLE)) {
// The size of this image hasn't been constrained and we haven't loaded
// enough of the image to know its size yet. This means it currently
// has 0 width and height.
state |= states::INVISIBLE;
}
// The size of this image hasn't been constrained and we haven't loaded
// enough of the image to know its size yet. This means it currently
// has 0 width and height.
state |= states::INVISIBLE;
}
}
@ -200,3 +207,36 @@ already_AddRefed<nsIURI> ImageAccessible::GetLongDescURI() const {
bool ImageAccessible::IsLongDescIndex(uint8_t aIndex) const {
return aIndex == LinkableAccessible::ActionCount();
}
////////////////////////////////////////////////////////////////////////////////
// imgINotificationObserver
void ImageAccessible::Notify(imgIRequest* aRequest, int32_t aType,
const nsIntRect* aData) {
if (aType != imgINotificationObserver::FRAME_COMPLETE &&
aType != imgINotificationObserver::LOAD_COMPLETE &&
aType != imgINotificationObserver::DECODE_COMPLETE) {
// We should update our state if the whole image was decoded,
// or the first frame in the case of a gif.
return;
}
if (IsDefunct() || !mParent) {
return;
}
uint32_t status = imgIRequest::STATUS_NONE;
aRequest->GetImageStatus(&status);
if ((status ^ mImageRequestStatus) & imgIRequest::STATUS_SIZE_AVAILABLE) {
nsIFrame* frame = GetFrame();
if (frame && !(frame->GetStateBits() & IMAGE_SIZECONSTRAINED)) {
RefPtr<AccEvent> event = new AccStateChangeEvent(
this, states::INVISIBLE,
!(status & imgIRequest::STATUS_SIZE_AVAILABLE));
mDoc->FireDelayedEvent(event);
}
}
mImageRequestStatus = status;
}

Просмотреть файл

@ -7,6 +7,7 @@
#define mozilla_a11y_ImageAccessible_h__
#include "BaseAccessibles.h"
#include "imgINotificationObserver.h"
namespace mozilla {
namespace a11y {
@ -16,11 +17,16 @@ namespace a11y {
* - gets name, role
* - support basic state
*/
class ImageAccessible : public LinkableAccessible {
class ImageAccessible : public LinkableAccessible,
public imgINotificationObserver {
public:
ImageAccessible(nsIContent* aContent, DocAccessible* aDoc);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_IMGINOTIFICATIONOBSERVER
// LocalAccessible
virtual void Shutdown() override;
virtual a11y::role NativeRole() const override;
virtual uint64_t NativeState() const override;
virtual already_AddRefed<AccAttributes> NativeAttributes() override;
@ -66,6 +72,8 @@ class ImageAccessible : public LinkableAccessible {
* @returns true if index is valid for longdesc action.
*/
inline bool IsLongDescIndex(uint8_t aIndex) const;
uint32_t mImageRequestStatus;
};
////////////////////////////////////////////////////////////////////////////////

Просмотреть файл

@ -949,13 +949,6 @@ void nsImageFrame::UpdateImage(imgIRequest* aRequest, imgIContainer* aImage) {
// Now we need to reflow if we have an unconstrained size and have
// already gotten the initial reflow.
if (!(mState & IMAGE_SIZECONSTRAINED)) {
#ifdef ACCESSIBILITY
if (mKind != Kind::ListStyleImage) {
if (nsAccessibilityService* accService = GetAccService()) {
accService->NotifyOfImageSizeAvailable(PresShell(), mContent);
}
}
#endif
PresShell()->FrameNeedsReflow(this, IntrinsicDirty::StyleChange,
NS_FRAME_IS_DIRTY);
} else if (PresShell()->IsActive()) {