Bug 1393816 - part2: Selection::SetBaseAndExtent() should use mCachedRange if it's available r?smaug
Similar to Selection::Collapse(), if mCachedRange is available,
Selection::SetBaseAndExtent() should use it rather than creating new nsRange
instance.
Then, it can reduce the allocation cost and may reduce some other cost, e.g.,
adding it to mutation observer.
MozReview-Commit-ID: InQQusw2KMc
--- a/dom/base/Selection.cpp
+++ b/dom/base/Selection.cpp
@@ -4074,49 +4074,54 @@ Selection::SetBaseAndExtent(nsINode& aAn
}
if (!HasSameRoot(aAnchorNode) ||
!HasSameRoot(aFocusNode)) {
// Return with no error
return;
}
- // Since a range will be created, we don't need the cached range because
- // Collapse() won't use it.
- // TODO: We should use the cache.
- mCachedRange = nullptr;
-
SelectionBatcher batch(this);
int32_t relativePosition =
nsContentUtils::ComparePoints(&aAnchorNode, aAnchorOffset,
&aFocusNode, aFocusOffset);
nsINode* start = &aAnchorNode;
nsINode* end = &aFocusNode;
uint32_t startOffset = aAnchorOffset;
uint32_t endOffset = aFocusOffset;
if (relativePosition > 0) {
start = &aFocusNode;
end = &aAnchorNode;
startOffset = aFocusOffset;
endOffset = aAnchorOffset;
}
- RefPtr<nsRange> newRange;
- nsresult rv = nsRange::CreateRange(start, startOffset, end, endOffset,
- getter_AddRefs(newRange));
- // CreateRange returns IndexSizeError if any offset is out of bounds.
+ // If there is cached range, we should reuse it for saving the allocation
+ // const (and some other cost in nsRange::DoSetRange().
+ RefPtr<nsRange> newRange = Move(mCachedRange);
+
+ nsresult rv = NS_OK;
+ if (newRange) {
+ rv = newRange->SetStartAndEnd(start, startOffset, end, endOffset);
+ } else {
+ rv = nsRange::CreateRange(start, startOffset, end, endOffset,
+ getter_AddRefs(newRange));
+ }
+
+ // nsRange::SetStartAndEnd() and nsRange::CreateRange() returns
+ // IndexSizeError if any offset is out of bounds.
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
}
- rv = RemoveAllRanges();
- if (NS_FAILED(rv)) {
- aRv.Throw(rv);
+ // Use non-virtual method instead of nsISelection::RemoveAllRanges().
+ RemoveAllRanges(aRv);
+ if (aRv.Failed()) {
return;
}
rv = AddRange(newRange);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
}
--- a/dom/base/Selection.h
+++ b/dom/base/Selection.h
@@ -477,20 +477,20 @@ private:
// two binary searches plus up to an additional 6 DOM comparisons. If this
// proves to be a performance concern, then an interval tree may be a
// possible solution, allowing the calculation of the overlap interval in
// O(log n) time, though this would require rebalancing and other overhead.
AutoTArray<RangeData, 1> mRanges;
RefPtr<nsRange> mAnchorFocusRange;
// mCachedRange is set by RemoveAllRangesTemporarily() and used by
- // Collapse(). If there is a range which will be released by Clear(),
- // RemoveAllRangesTemporarily() stores it with this. If Collapse() is
- // called without existing ranges, it'll reuse this range for saving the
- // creating cost.
+ // Collapse() and SetBaseAndExtent(). If there is a range which will be
+ // released by Clear(), RemoveAllRangesTemporarily() stores it with this.
+ // If Collapse() is called without existing ranges, it'll reuse this range
+ // for saving the creation cost.
RefPtr<nsRange> mCachedRange;
RefPtr<nsFrameSelection> mFrameSelection;
RefPtr<nsAutoScrollTimer> mAutoScrollTimer;
FallibleTArray<nsCOMPtr<nsISelectionListener>> mSelectionListeners;
nsRevocableEventPtr<ScrollSelectionIntoViewEvent> mScrollEvent;
CachedOffsetForFrame* mCachedOffsetForFrame;
nsDirection mDirection;
SelectionType mSelectionType;