Bug 1892589 - Custom Highlight API: Implemented speedup for Highlight::Add(). r=edgar,dom-core

To preserve uniqueness in the mirrored data structure for the JS-setlike object,
`nsTArray::Contains()` was used. This showed up in profiles due to its `O(n)` time complexity.
Performance could be improved by replacing it with `SetlikeHelpers::Has()`, which uses the more efficient version of the underlying data structure of the setlike.

The same idea has been applied to `HighlightRegistry`, which uses a maplike. However, this is mostly for consistency, since `Highlight::Add()` will likely be called more often and hence the bigger performance bottleneck.

Differential Revision: https://phabricator.services.mozilla.com/D208071
This commit is contained in:
Jan-Niklas Jaeschke 2024-04-23 09:42:15 +00:00
parent 5033a8c28c
commit 38224af61d
2 changed files with 42 additions and 20 deletions

View File

@ -101,24 +101,36 @@ already_AddRefed<Selection> Highlight::CreateHighlightSelection(
}
void Highlight::Add(AbstractRange& aRange, ErrorResult& aRv) {
// Manually check if the range `aKey` is already present in this highlight,
// because `SetlikeHelpers::Add()` doesn't indicate this.
// To keep the setlike and the mirrored array in sync, the range must not
// be added to `mRanges` if it was already present.
// `SetlikeHelpers::Has()` is much faster in checking this than
// `nsTArray<>::Contains()`.
if (Highlight_Binding::SetlikeHelpers::Has(this, aRange, aRv) ||
aRv.Failed()) {
return;
}
Highlight_Binding::SetlikeHelpers::Add(this, aRange, aRv);
if (aRv.Failed()) {
return;
}
if (!mRanges.Contains(&aRange)) {
mRanges.AppendElement(&aRange);
AutoFrameSelectionBatcher selectionBatcher(__FUNCTION__,
mHighlightRegistries.Count());
for (const RefPtr<HighlightRegistry>& registry :
mHighlightRegistries.Keys()) {
auto frameSelection = registry->GetFrameSelection();
selectionBatcher.AddFrameSelection(frameSelection);
// since this is run in a context guarded by a selection batcher,
// no strong reference is needed to keep `registry` alive.
MOZ_KnownLive(registry)->MaybeAddRangeToHighlightSelection(aRange, *this);
if (aRv.Failed()) {
return;
}
MOZ_ASSERT(!mRanges.Contains(&aRange),
"setlike and DOM mirror are not in sync");
mRanges.AppendElement(&aRange);
AutoFrameSelectionBatcher selectionBatcher(__FUNCTION__,
mHighlightRegistries.Count());
for (const RefPtr<HighlightRegistry>& registry :
mHighlightRegistries.Keys()) {
auto frameSelection = registry->GetFrameSelection();
selectionBatcher.AddFrameSelection(frameSelection);
// since this is run in a context guarded by a selection batcher,
// no strong reference is needed to keep `registry` alive.
MOZ_KnownLive(registry)->MaybeAddRangeToHighlightSelection(aRange, *this);
if (aRv.Failed()) {
return;
}
}
}

View File

@ -133,18 +133,28 @@ void HighlightRegistry::AddHighlightSelectionsToFrameSelection() {
void HighlightRegistry::Set(const nsAString& aKey, Highlight& aValue,
ErrorResult& aRv) {
// manually check if the highlight `aKey` is already registered to be able to
// provide a fast path later that avoids calling `std::find_if()`.
const bool highlightAlreadyPresent =
HighlightRegistry_Binding::MaplikeHelpers::Has(this, aKey, aRv);
if (aRv.Failed()) {
return;
}
HighlightRegistry_Binding::MaplikeHelpers::Set(this, aKey, aValue, aRv);
if (aRv.Failed()) {
return;
}
RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
RefPtr<nsAtom> highlightNameAtom = NS_AtomizeMainThread(aKey);
auto foundIter =
std::find_if(mHighlightsOrdered.begin(), mHighlightsOrdered.end(),
[&highlightNameAtom](auto const& aElm) {
return aElm.first() == highlightNameAtom;
});
if (foundIter != mHighlightsOrdered.end()) {
if (highlightAlreadyPresent) {
// If the highlight named `aKey` was present before, replace its value.
auto foundIter =
std::find_if(mHighlightsOrdered.begin(), mHighlightsOrdered.end(),
[&highlightNameAtom](auto const& aElm) {
return aElm.first() == highlightNameAtom;
});
MOZ_ASSERT(foundIter != mHighlightsOrdered.end(),
"webIDL maplike and DOM mirror are not in sync");
foundIter->second()->RemoveFromHighlightRegistry(*this, *highlightNameAtom);
if (frameSelection) {
frameSelection->RemoveHighlightSelection(highlightNameAtom);