diff --git a/accessible/base/SelectionManager.cpp b/accessible/base/SelectionManager.cpp index 1fc8290bfe20..5e8e0ef05c4a 100644 --- a/accessible/base/SelectionManager.cpp +++ b/accessible/base/SelectionManager.cpp @@ -13,11 +13,13 @@ #include "nsCoreUtils.h" #include "nsEventShell.h" #include "nsFrameSelection.h" +#include "TextLeafRange.h" #include "mozilla/PresShell.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/Selection.h" #include "mozilla/dom/Element.h" +#include "mozilla/StaticPrefs_accessibility.h" using namespace mozilla; using namespace mozilla::a11y; @@ -226,4 +228,14 @@ void SelectionManager::ProcessSelectionChanged(SelData* aSelData) { } } +void SelectionManager::SpellCheckRangeAdded(const nsRange& aRange) { + // Events are fired in SelectionManager::NotifySelectionChanged. This is only + // used to push cache updates. + if (StaticPrefs::accessibility_cache_enabled_AtStartup()) { + dom::Document* doc = aRange.GetStartContainer()->OwnerDoc(); + MOZ_ASSERT(doc); + TextLeafPoint::UpdateCachedSpellingError(doc, aRange); + } +} + SelectionManager::~SelectionManager() = default; diff --git a/accessible/base/SelectionManager.h b/accessible/base/SelectionManager.h index 34a841d525fb..1ca9132c750c 100644 --- a/accessible/base/SelectionManager.h +++ b/accessible/base/SelectionManager.h @@ -9,6 +9,8 @@ #include "nsISelectionListener.h" #include "mozilla/WeakPtr.h" +class nsRange; + namespace mozilla { class PresShell; @@ -106,6 +108,18 @@ class SelectionManager : public nsISelectionListener { mAccWithCaret = nullptr; } + /** + * Called by mozInlineSpellChecker when a spell check range is added. + * nsISelectionListener isn't sufficient for spelling errors, since it only + * tells us that there was a change, not which range changed. We don't want + * to unnecessarily push a cache update for all Accessibles in the entire + * selection. + * We don't need an equivalent notification for removals because a spelling + * error correction is triggered by a text change and text changes trigger a + * cache update already. + */ + void SpellCheckRangeAdded(const nsRange& aRange); + ~SelectionManager(); protected: diff --git a/accessible/base/TextLeafRange.cpp b/accessible/base/TextLeafRange.cpp index 87f24a96931c..c4a155722305 100644 --- a/accessible/base/TextLeafRange.cpp +++ b/accessible/base/TextLeafRange.cpp @@ -8,15 +8,18 @@ #include "HyperTextAccessible-inl.h" #include "mozilla/a11y/Accessible.h" +#include "mozilla/a11y/CacheConstants.h" #include "mozilla/a11y/DocAccessible.h" #include "mozilla/a11y/DocAccessibleParent.h" #include "mozilla/a11y/LocalAccessible.h" #include "mozilla/BinarySearch.h" #include "mozilla/Casting.h" #include "mozilla/dom/CharacterData.h" +#include "mozilla/dom/Document.h" #include "mozilla/intl/Segmenter.h" #include "mozilla/intl/WordBreaker.h" #include "mozilla/StaticPrefs_layout.h" +#include "nsAccessibilityService.h" #include "nsAccUtils.h" #include "nsBlockFrame.h" #include "nsContentUtils.h" @@ -24,6 +27,7 @@ #include "nsIAccessiblePivot.h" #include "nsILineIterator.h" #include "nsINode.h" +#include "nsRange.h" #include "nsStyleStructInlines.h" #include "nsTArray.h" #include "nsTextFrame.h" @@ -1236,6 +1240,32 @@ nsTArray TextLeafPoint::GetSpellingErrorOffsets( return offsets; } +/* static */ +void TextLeafPoint::UpdateCachedSpellingError(dom::Document* aDocument, + const nsRange& aRange) { + DocAccessible* docAcc = GetExistingDocAccessible(aDocument); + if (!docAcc) { + return; + } + LocalAccessible* startAcc = docAcc->GetAccessible(aRange.GetStartContainer()); + LocalAccessible* endAcc = docAcc->GetAccessible(aRange.GetEndContainer()); + if (!startAcc || !endAcc) { + return; + } + for (Accessible* acc = startAcc; acc; acc = NextLeaf(acc)) { + if (acc->IsTextLeaf()) { + docAcc->QueueCacheUpdate(acc->AsLocal(), CacheDomain::Spelling); + } + if (acc == endAcc) { + // Subtle: We check this here rather than in the loop condition because + // we want to include endAcc but stop once we reach it. Putting it in the + // loop condition would mean we stop at endAcc, but we would also exclude + // it; i.e. we wouldn't push the cache for it. + break; + } + } +} + already_AddRefed TextLeafPoint::GetTextAttributesLocalAcc( bool aIncludeDefaults) const { LocalAccessible* acc = mAcc->AsLocal(); diff --git a/accessible/base/TextLeafRange.h b/accessible/base/TextLeafRange.h index 0175d72f3fb8..efaf89e53b46 100644 --- a/accessible/base/TextLeafRange.h +++ b/accessible/base/TextLeafRange.h @@ -13,7 +13,14 @@ #include "nsDirection.h" #include "nsIAccessibleText.h" -namespace mozilla::a11y { +class nsRange; + +namespace mozilla { +namespace dom { +class Document; +} + +namespace a11y { class Accessible; class LocalAccessible; @@ -136,6 +143,12 @@ class TextLeafPoint final { */ static nsTArray GetSpellingErrorOffsets(LocalAccessible* aAcc); + /** + * Queue a cache update for a spelling error in a given DOM range. + */ + static void UpdateCachedSpellingError(dom::Document* aDocument, + const nsRange& aRange); + /** * Find the start of a run of text attributes in a specific direction. * A text attributes run is a span of text where the attributes are the same. @@ -212,6 +225,7 @@ class TextLeafRange final { TextLeafPoint mEnd; }; -} // namespace mozilla::a11y +} // namespace a11y +} // namespace mozilla #endif diff --git a/extensions/spellcheck/src/mozInlineSpellChecker.cpp b/extensions/spellcheck/src/mozInlineSpellChecker.cpp index 1abfc8654414..77ef9bb4f4e6 100644 --- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp +++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp @@ -53,6 +53,9 @@ #include "mozilla/dom/MouseEvent.h" #include "mozilla/dom/Selection.h" #include "mozInlineSpellWordUtil.h" +#ifdef ACCESSIBILITY +# include "nsAccessibilityService.h" +#endif #include "nsCOMPtr.h" #include "nsCRT.h" #include "nsGenericHTMLElement.h" @@ -1879,6 +1882,11 @@ nsresult mozInlineSpellChecker::AddRange(Selection* aSpellCheckSelection, rv = err.StealNSResult(); } else { mNumWordsInSpellSelection++; +#ifdef ACCESSIBILITY + if (nsAccessibilityService* accService = GetAccService()) { + accService->SpellCheckRangeAdded(*aRange); + } +#endif } }