Bug 1737919 part 4: Update cached spelling errors when a spelling error is added without the text changing. r=morgan,smaug

We already have an nsISelectionListener, but that only tells us that a change happened somewhere in the selection, not which range changed.
We don't want to push a cache update for all ranges when only one changed.
Therefore, this patch adds an accessibility notification in mozInlineSpellChecker::AddRange.
We don't need this for removed ranges because the text will change for any spelling error corrections and text updates trigger spelling error cache updates.

Differential Revision: https://phabricator.services.mozilla.com/D147244
This commit is contained in:
James Teh 2022-05-27 10:56:41 +00:00
parent 3daa72eb49
commit 7564ed957c
5 changed files with 80 additions and 2 deletions

View File

@ -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;

View File

@ -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:

View File

@ -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<int32_t> 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<AccAttributes> TextLeafPoint::GetTextAttributesLocalAcc(
bool aIncludeDefaults) const {
LocalAccessible* acc = mAcc->AsLocal();

View File

@ -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<int32_t> 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

View File

@ -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
}
}