RESOLVED FIXED 255433
Accessibility is not informed of changes to several attributes
https://bugs.webkit.org/show_bug.cgi?id=255433
Summary Accessibility is not informed of changes to several attributes
Tyler Wilcock
Reported 2023-04-13 21:09:26 PDT
Accessibility relies on attribute changes from the DOM to stay up to date, and we are missing quite a few. This is because accessibility is notified about attribute changes in Element::attributeChanged. However, this method is virtual, and many subclasses don't call up to Element::attributeChanged. I believe we've always had this problem for some attributes, but it was worsened with the restructuring in this change: https://bugs.webkit.org/show_bug.cgi?id=255258 (Drop Element::parseAttribute()) After this change, we stopped receiving updates for the disabled attribute for option elements, the placeholder attribute for HTMLTextFormControlElement subclasses, and likely more. This was caught because accessibility/dynamic-attribute-changes-should-update-isenabled.html started failing in isolated tree mode (--accessibility-isolated-tree test runner flag). This configuration is not in EWS (post-commit only for now), so it couldn't have been caught up-front.
Attachments
Patch (7.23 KB, patch)
2023-04-13 21:14 PDT, Tyler Wilcock
no flags
Patch (7.19 KB, patch)
2023-04-13 22:21 PDT, Tyler Wilcock
no flags
Patch (7.29 KB, patch)
2023-04-14 09:51 PDT, Tyler Wilcock
no flags
Radar WebKit Bug Importer
Comment 1 2023-04-13 21:09:36 PDT
Tyler Wilcock
Comment 2 2023-04-13 21:14:51 PDT
Tyler Wilcock
Comment 3 2023-04-13 21:16:31 PDT
cc Chris Dumez — would greatly appreciate your review.
Chris Dumez
Comment 4 2023-04-13 21:26:56 PDT
Comment on attachment 465908 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=465908&action=review > Source/WebCore/dom/Element.cpp:-2108 > - if (AXObjectCache* cache = document().existingAXObjectCache()) Can we just add a ScopeExit at the beginning of this function instead of introducing a new attributeChangedInternal() function? e.g. ``` auto notifyAccessibilityOfAttributeChangeOnExit = makeScopeExit([&] { if (AXObjectCache* cache = document().existingAXObjectCache()) cache->deferAttributeChangeIfNeeded(this, name, oldValue, newValue); }); ```
Chris Dumez
Comment 5 2023-04-13 21:29:57 PDT
Comment on attachment 465908 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=465908&action=review >> Source/WebCore/dom/Element.cpp:-2108 >> - if (AXObjectCache* cache = document().existingAXObjectCache()) > > Can we just add a ScopeExit at the beginning of this function instead of introducing a new attributeChangedInternal() function? > > e.g. > ``` > auto notifyAccessibilityOfAttributeChangeOnExit = makeScopeExit([&] { > if (AXObjectCache* cache = document().existingAXObjectCache()) > cache->deferAttributeChangeIfNeeded(this, name, oldValue, newValue); > }); > ``` Oh, I guess this still won't work if some subclasses don't end up calling the base class's attributeChanged() :/
Chris Dumez
Comment 6 2023-04-13 21:39:46 PDT
Comment on attachment 465908 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=465908&action=review > Source/WebCore/dom/Element.h:813 > + void attributeChangedInternal(const QualifiedName&, const AtomString& oldValue, const AtomString& newValue, AttributeModificationReason = ModifiedDirectly); I think I'm OK with this patch but: 1. I have slight concerns about perf so it would be nice to trigger A/B testing for Speedometer to be on the safe side 2. attributeChangedInternal() is too obscure. I'd prefer if we found a better name, maybe `notifyAttributeChanged()` 3. The patch doesn't seem to apply
Tyler Wilcock
Comment 7 2023-04-13 22:21:32 PDT
Tyler Wilcock
Comment 8 2023-04-13 22:26:32 PDT
(In reply to Chris Dumez from comment #6) > Comment on attachment 465908 [details] > Patch > > View in context: > https://bugs.webkit.org/attachment.cgi?id=465908&action=review > > > Source/WebCore/dom/Element.h:813 > > + void attributeChangedInternal(const QualifiedName&, const AtomString& oldValue, const AtomString& newValue, AttributeModificationReason = ModifiedDirectly); > > I think I'm OK with this patch but: > 1. I have slight concerns about perf so it would be nice to trigger A/B > testing for Speedometer to be on the safe side > 2. attributeChangedInternal() is too obscure. I'd prefer if we found a > better name, maybe `notifyAttributeChanged()` > 3. The patch doesn't seem to apply Rebased the patch and renamed `attributeChangedInternal` to `notifyAttributeChanged`. Was considering `attributeChangedWithNotification` to make it clear it's attributeChanged, _plus_ more behavior...but feels wordy? I just queued some Speedometer runs with this latest patch. Will report back here with results.
Chris Dumez
Comment 9 2023-04-13 22:33:46 PDT
Comment on attachment 465909 [details] Patch Looks fine assuming A/B testing comes back neutral.
Andres Gonzalez
Comment 10 2023-04-14 06:50:47 PDT
(In reply to Tyler Wilcock from comment #7) > Created attachment 465909 [details] > Patch I presume the speedometer results are going to be neutral since the AXObjectCache won't exist in the speedometer configuration. --- a/Source/WebCore/dom/Element.cpp +++ b/Source/WebCore/dom/Element.cpp +void Element::notifyAttributeChanged(const QualifiedName& name, const AtomString& oldValue, const AtomString& newValue, AttributeModificationReason reason) +{ + attributeChanged(name, oldValue, newValue, reason); + + if (auto* cache = document().existingAXObjectCache()) { + if (oldValue != newValue) Should we do these ifs in reverse order? The comparison of two AtomString should be fast, but so may be getting the AXObjectcache ? --- a/Source/WebCore/dom/Element.h +++ b/Source/WebCore/dom/Element.h // This function is called whenever an attribute is added, changed or removed. + // For use inside this class, prefer calling notifyAttributeChanged. virtual void attributeChanged(const QualifiedName&, const AtomString& oldValue, const AtomString& newValue, AttributeModificationReason = ModifiedDirectly); Can we make this comment more explicit in that notifyAttributeChanged must be called instead in order to update accessibility.
Chris Dumez
Comment 11 2023-04-14 08:25:34 PDT
(In reply to Andres Gonzalez from comment #10) > (In reply to Tyler Wilcock from comment #7) > > Created attachment 465909 [details] > > Patch > I presume the speedometer results are going to be neutral since the > AXObjectCache won't exist in the speedometer configuration. Results are indeed neutral. This is a very hot code path so we should not make assumptions and test. > --- a/Source/WebCore/dom/Element.cpp > +++ b/Source/WebCore/dom/Element.cpp > > +void Element::notifyAttributeChanged(const QualifiedName& name, const > AtomString& oldValue, const AtomString& newValue, > AttributeModificationReason reason) > +{ > + attributeChanged(name, oldValue, newValue, reason); > + > + if (auto* cache = document().existingAXObjectCache()) { > + if (oldValue != newValue) > > Should we do these ifs in reverse order? The comparison of two AtomString > should be fast, but so may be getting the AXObjectcache ? I don't mind either way. Getting the AXObjectCache is about as cheap as the AtomString comparison in the case where the AXObjectCache doesn't exist. Might be a little more expensive when the cache actually exists. > > --- a/Source/WebCore/dom/Element.h > +++ b/Source/WebCore/dom/Element.h > > // This function is called whenever an attribute is added, changed or > removed. > + // For use inside this class, prefer calling notifyAttributeChanged. > virtual void attributeChanged(const QualifiedName&, const AtomString& > oldValue, const AtomString& newValue, AttributeModificationReason = > ModifiedDirectly); > > Can we make this comment more explicit in that notifyAttributeChanged must > be called instead in order to update accessibility. Maybe something like this? // Do not call this function directly. notifyAttributeChanged() should be used instead in order to update accessibility.
Andres Gonzalez
Comment 12 2023-04-14 09:12:59 PDT
(In reply to Chris Dumez from comment #11) > (In reply to Andres Gonzalez from comment #10) > > (In reply to Tyler Wilcock from comment #7) > > > Created attachment 465909 [details] > > > Patch > > I presume the speedometer results are going to be neutral since the > > AXObjectCache won't exist in the speedometer configuration. > > Results are indeed neutral. This is a very hot code path so we should not > make assumptions and test. > > > --- a/Source/WebCore/dom/Element.cpp > > +++ b/Source/WebCore/dom/Element.cpp > > > > +void Element::notifyAttributeChanged(const QualifiedName& name, const > > AtomString& oldValue, const AtomString& newValue, > > AttributeModificationReason reason) > > +{ > > + attributeChanged(name, oldValue, newValue, reason); > > + > > + if (auto* cache = document().existingAXObjectCache()) { > > + if (oldValue != newValue) > > > > Should we do these ifs in reverse order? The comparison of two AtomString > > should be fast, but so may be getting the AXObjectcache ? > > I don't mind either way. Getting the AXObjectCache is about as cheap as the > AtomString comparison in the case where the AXObjectCache doesn't exist. > Might be a little more expensive when the cache actually exists. > > > > > --- a/Source/WebCore/dom/Element.h > > +++ b/Source/WebCore/dom/Element.h > > > > // This function is called whenever an attribute is added, changed or > > removed. > > + // For use inside this class, prefer calling notifyAttributeChanged. > > virtual void attributeChanged(const QualifiedName&, const AtomString& > > oldValue, const AtomString& newValue, AttributeModificationReason = > > ModifiedDirectly); > > > > Can we make this comment more explicit in that notifyAttributeChanged must > > be called instead in order to update accessibility. > > Maybe something like this? > // Do not call this function directly. notifyAttributeChanged() should be > used instead in order to update accessibility. Perfect! thanks!
Tyler Wilcock
Comment 13 2023-04-14 09:51:07 PDT
EWS
Comment 14 2023-04-14 14:01:26 PDT
Committed 262988@main (b6dc2fc14af4): <https://commits.webkit.org/262988@main> All reviewed patches have been landed. Closing bug and clearing flags on attachment 465916 [details].
Note You need to log in before you can comment on or make changes to this bug.