Bug 1808565, part 1: Adapt Selection to support StaticRanges. r=webidl,saschanaz,masayuki,smaug

This change is necessary to support the [CSS Highlight API](https://drafts.csswg.org/css-highlight-api-1/),
which uses `Selection` internally.

To replace `nsRange` with `AbstractRange`, some sections needed to be
adapted since `nsRange`-specific features were used.
Therefore, some methods (such as `GetRangeAt()`) may only be called if
the `Selection` is *not* of type `SelectionType::eHighlight`,
as it (per spec) returns an `nsRange`.
These methods will now `MOZ_ASSERT` if called for a highlight selection.
Additional methods are implemented which return `AbstractRange`
instead and are safe to be called for every selection type.

This commit also improves support of highlight features:
- Invalidation of highlight ranges: adding/removing Ranges in-place instead of
  removing and re-adding the Selection object associated with the highlight.
- Ranges are only associated with the Selection that shares the same Document
- Fixed minor IDL issue

Differential Revision: https://phabricator.services.mozilla.com/D170582
This commit is contained in:
Jan-Niklas Jaeschke 2023-03-03 14:59:47 +00:00
parent 51b56e8b3b
commit ebf3cd6240
27 changed files with 396 additions and 244 deletions

View File

@ -479,8 +479,8 @@ static nsTArray<nsRange*> FindDOMSpellingErrors(LocalAccessible* aAcc,
? dom::CharacterData::FromNode(node)->TextLength()
: RenderedToContentOffset(aAcc, aRenderedEnd);
nsTArray<nsRange*> domRanges;
domSel->GetRangesForIntervalArray(node, contentStart, node, contentEnd,
aAllowAdjacent, &domRanges);
domSel->GetDynamicRangesForIntervalArray(node, contentStart, node, contentEnd,
aAllowAdjacent, &domRanges);
return domRanges;
}

View File

@ -1838,8 +1838,8 @@ void HyperTextAccessible::GetSelectionDOMRanges(SelectionType aSelectionType,
if (!startNode) return;
uint32_t childCount = startNode->GetChildCount();
nsresult rv = domSel->GetRangesForIntervalArray(startNode, 0, startNode,
childCount, true, aRanges);
nsresult rv = domSel->GetDynamicRangesForIntervalArray(
startNode, 0, startNode, childCount, true, aRanges);
NS_ENSURE_SUCCESS_VOID(rv);
// Remove collapsed ranges

View File

@ -71,6 +71,10 @@ class AbstractRange : public nsISupports, public nsWrapperCache {
nsINode* GetStartContainer() const { return mStart.Container(); }
nsINode* GetEndContainer() const { return mEnd.Container(); }
Document* GetComposedDocOfContainers() const {
return mStart.Container() ? mStart.Container()->GetComposedDoc() : nullptr;
}
// FYI: Returns 0 if it's not positioned.
uint32_t StartOffset() const {
return static_cast<uint32_t>(

View File

@ -18,6 +18,8 @@
namespace mozilla {
using namespace dom;
static bool ComparePostMode(const RawRangeBoundary& aStart,
const RawRangeBoundary& aEnd, nsINode& aNode) {
nsINode* parent = aNode.GetParentNode();
@ -162,7 +164,7 @@ nsresult ContentIteratorBase::Init(nsINode* aRoot) {
return NS_OK;
}
nsresult ContentIteratorBase::Init(nsRange* aRange) {
nsresult ContentIteratorBase::Init(AbstractRange* aRange) {
if (NS_WARN_IF(!aRange)) {
return NS_ERROR_INVALID_ARG;
}
@ -735,7 +737,7 @@ nsresult ContentSubtreeIterator::Init(nsINode* aRoot) {
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult ContentSubtreeIterator::Init(nsRange* aRange) {
nsresult ContentSubtreeIterator::Init(AbstractRange* aRange) {
MOZ_ASSERT(aRange);
if (NS_WARN_IF(!aRange->IsPositioned())) {

View File

@ -38,7 +38,7 @@ class ContentIteratorBase {
*/
virtual nsresult Init(nsINode* aRoot);
virtual nsresult Init(nsRange* aRange);
virtual nsresult Init(dom::AbstractRange* aRange);
virtual nsresult Init(nsINode* aStartContainer, uint32_t aStartOffset,
nsINode* aEndContainer, uint32_t aEndOffset);
virtual nsresult Init(const RawRangeBoundary& aStart,
@ -175,7 +175,7 @@ class ContentSubtreeIterator final : public ContentIteratorBase {
*/
virtual nsresult Init(nsINode* aRoot) override;
virtual nsresult Init(nsRange* aRange) override;
virtual nsresult Init(dom::AbstractRange* aRange) override;
virtual nsresult Init(nsINode* aStartContainer, uint32_t aStartOffset,
nsINode* aEndContainer, uint32_t aEndOffset) override;
virtual nsresult Init(const RawRangeBoundary& aStartBoundary,
@ -233,7 +233,7 @@ class ContentSubtreeIterator final : public ContentIteratorBase {
// the range's start and end nodes will never be considered "in" it.
nsIContent* GetTopAncestorInRange(nsINode* aNode) const;
RefPtr<nsRange> mRange;
RefPtr<dom::AbstractRange> mRange;
// See <https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor>.
AutoTArray<nsIContent*, 8> mInclusiveAncestorsOfEndContainer;

View File

@ -17,6 +17,7 @@
#include "PresShell.h"
#include "Selection.h"
#include "nsFrameSelection.h"
#include "nsPIDOMWindow.h"
namespace mozilla::dom {
@ -81,49 +82,36 @@ void Highlight::RemoveFromHighlightRegistry(
already_AddRefed<Selection> Highlight::CreateHighlightSelection(
const nsAtom* aHighlightName, nsFrameSelection* aFrameSelection,
ErrorResult& aRv) const {
MOZ_ASSERT(aFrameSelection);
MOZ_ASSERT(aFrameSelection->GetPresShell());
RefPtr<Selection> selection =
MakeRefPtr<Selection>(SelectionType::eHighlight, aFrameSelection);
selection->SetHighlightName(aHighlightName);
for (auto const& range : mRanges) {
// this is safe because `Highlight::Add()` ensures all ranges are
// dynamic.
RefPtr<nsRange> dynamicRange = range->AsDynamicRange();
selection->AddRangeAndSelectFramesAndNotifyListeners(*dynamicRange, aRv);
if (aRv.Failed()) {
return nullptr;
SelectionBatcher selectionBatcher(selection, __FUNCTION__);
// NOLINTNEXTLINE(performance-for-range-copy)
for (const RefPtr<AbstractRange> range : mRanges) {
if (range->GetComposedDocOfContainers() ==
aFrameSelection->GetPresShell()->GetDocument()) {
selection->AddHighlightRangeAndSelectFramesAndNotifyListeners(*range,
aRv);
}
}
return selection.forget();
}
void Highlight::NotifyChangesToRegistries(ErrorResult& aRv) {
for (RefPtr<HighlightRegistry> highlightRegistry :
mHighlightRegistries.Keys()) {
MOZ_ASSERT(highlightRegistry);
highlightRegistry->HighlightPropertiesChanged(*this, aRv);
if (aRv.Failed()) {
return;
}
}
}
void Highlight::Add(AbstractRange& aRange, ErrorResult& aRv) {
if (aRange.IsStaticRange()) {
// TODO (jjaschke) Selection needs to be able to deal with StaticRanges
// (Bug 1808565)
aRv.ThrowUnknownError("Support for StaticRanges is not implemented yet!");
return;
}
Highlight_Binding::SetlikeHelpers::Add(this, aRange, aRv);
if (aRv.Failed()) {
return;
}
if (!mRanges.Contains(&aRange)) {
mRanges.AppendElement(&aRange);
NotifyChangesToRegistries(aRv);
if (aRv.Failed()) {
return;
for (const RefPtr<HighlightRegistry> registry :
mHighlightRegistries.Keys()) {
registry->MaybeAddRangeToHighlightSelection(aRange, *this, aRv);
if (aRv.Failed()) {
return;
}
}
}
}
@ -132,15 +120,23 @@ void Highlight::Clear(ErrorResult& aRv) {
Highlight_Binding::SetlikeHelpers::Clear(this, aRv);
if (!aRv.Failed()) {
mRanges.Clear();
NotifyChangesToRegistries(aRv);
for (const RefPtr<HighlightRegistry> registry :
mHighlightRegistries.Keys()) {
registry->RemoveHighlightSelection(*this);
}
}
}
void Highlight::Delete(AbstractRange& aRange, ErrorResult& aRv) {
bool Highlight::Delete(AbstractRange& aRange, ErrorResult& aRv) {
if (Highlight_Binding::SetlikeHelpers::Delete(this, aRange, aRv)) {
mRanges.RemoveElement(&aRange);
NotifyChangesToRegistries(aRv);
for (const RefPtr<HighlightRegistry> registry :
mHighlightRegistries.Keys()) {
registry->MaybeRemoveRangeFromHighlightSelection(aRange, *this);
}
return true;
}
return false;
}
JSObject* Highlight::WrapObject(JSContext* aCx,

View File

@ -149,12 +149,12 @@ class Highlight final : public nsISupports, public nsWrapperCache {
* internal one.
*
* Also notifies all `HighlightRegistry` instances.
*
* @return As per spec, returns true if the range was deleted.
*/
MOZ_CAN_RUN_SCRIPT void Delete(AbstractRange& aRange, ErrorResult& aRv);
MOZ_CAN_RUN_SCRIPT bool Delete(AbstractRange& aRange, ErrorResult& aRv);
private:
MOZ_CAN_RUN_SCRIPT void NotifyChangesToRegistries(ErrorResult& aRv);
RefPtr<nsPIDOMWindowInner> mWindow;
/**

View File

@ -59,8 +59,50 @@ JSObject* HighlightRegistry::WrapObject(JSContext* aCx,
return HighlightRegistry_Binding::Wrap(aCx, this, aGivenProto);
}
void HighlightRegistry::HighlightPropertiesChanged(Highlight& aHighlight,
ErrorResult& aRv) {
void HighlightRegistry::MaybeAddRangeToHighlightSelection(AbstractRange& aRange,
Highlight& aHighlight,
ErrorResult& aRv) {
RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
if (!frameSelection) {
return;
}
MOZ_ASSERT(frameSelection->GetPresShell());
if (!frameSelection->GetPresShell()->GetDocument() ||
frameSelection->GetPresShell()->GetDocument() !=
aRange.GetComposedDocOfContainers()) {
// ranges that belong to a different document must not be added.
return;
}
for (auto const& iter : mHighlightsOrdered) {
if (iter.second() != &aHighlight) {
continue;
}
const RefPtr<const nsAtom> highlightName = iter.first();
frameSelection->AddHighlightSelectionRange(highlightName, aHighlight,
aRange, aRv);
}
}
void HighlightRegistry::MaybeRemoveRangeFromHighlightSelection(
AbstractRange& aRange, Highlight& aHighlight) {
RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
if (!frameSelection) {
return;
}
MOZ_ASSERT(frameSelection->GetPresShell());
for (auto const& iter : mHighlightsOrdered) {
if (iter.second() != &aHighlight) {
continue;
}
const RefPtr<const nsAtom> highlightName = iter.first();
frameSelection->RemoveHighlightSelectionRange(highlightName, aRange);
}
}
void HighlightRegistry::RemoveHighlightSelection(Highlight& aHighlight) {
RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
if (!frameSelection) {
return;
@ -70,12 +112,8 @@ void HighlightRegistry::HighlightPropertiesChanged(Highlight& aHighlight,
continue;
}
RefPtr<const nsAtom> highlightName = iter.first();
const RefPtr<const nsAtom> highlightName = iter.first();
frameSelection->RemoveHighlightSelection(highlightName);
frameSelection->AddHighlightSelection(highlightName, aHighlight, aRv);
if (aRv.Failed()) {
return;
}
}
}
@ -135,8 +173,8 @@ void HighlightRegistry::Clear(ErrorResult& aRv) {
}
auto frameSelection = GetFrameSelection();
for (auto const& iter : mHighlightsOrdered) {
const auto& highlightName = iter.first();
const auto& highlight = iter.second();
const RefPtr<const nsAtom> highlightName = iter.first();
const RefPtr<Highlight>& highlight = iter.second();
highlight->RemoveFromHighlightRegistry(*this, *highlightName);
if (frameSelection) {
frameSelection->RemoveHighlightSelection(highlightName);
@ -145,9 +183,9 @@ void HighlightRegistry::Clear(ErrorResult& aRv) {
mHighlightsOrdered.Clear();
}
void HighlightRegistry::Delete(const nsAString& aKey, ErrorResult& aRv) {
bool HighlightRegistry::Delete(const nsAString& aKey, ErrorResult& aRv) {
if (!HighlightRegistry_Binding::MaplikeHelpers::Delete(this, aKey, aRv)) {
return;
return false;
}
RefPtr<nsAtom> highlightNameAtom = NS_AtomizeMainThread(aKey);
auto foundIter =
@ -155,9 +193,9 @@ void HighlightRegistry::Delete(const nsAString& aKey, ErrorResult& aRv) {
[&highlightNameAtom](auto const& aElm) {
return aElm.first() == highlightNameAtom;
});
if (foundIter == mHighlightsOrdered.cend()) {
return;
}
MOZ_ASSERT(foundIter != mHighlightsOrdered.cend(),
"HighlightRegistry: maplike and internal data are out of sync!");
RefPtr<Highlight> highlight = foundIter->second();
mHighlightsOrdered.RemoveElementAt(foundIter);
@ -165,6 +203,7 @@ void HighlightRegistry::Delete(const nsAString& aKey, ErrorResult& aRv) {
frameSelection->RemoveHighlightSelection(highlightNameAtom);
}
highlight->RemoveFromHighlightRegistry(*this, *highlightNameAtom);
return true;
}
RefPtr<nsFrameSelection> HighlightRegistry::GetFrameSelection() {

View File

@ -23,6 +23,7 @@ class ErrorResult;
}
namespace mozilla::dom {
class AbstractRange;
class Document;
class Highlight;
@ -62,10 +63,33 @@ class HighlightRegistry final : public nsISupports, public nsWrapperCache {
ErrorResult& aRv);
/**
* @brief Propagates changes to a highlight to the `FrameSelection`.
* @brief Adds the Range to the Highlight Selection if it belongs to the same
* Document.
*
* If no Highlight Selection for this highlight exists, it will be created.
* This may occur when a Highlight is added to the Registry after the
* nsFrameSelection is created.
*/
MOZ_CAN_RUN_SCRIPT void HighlightPropertiesChanged(Highlight& aHighlight,
ErrorResult& aRv);
MOZ_CAN_RUN_SCRIPT void MaybeAddRangeToHighlightSelection(
AbstractRange& aRange, Highlight& aHighlight, ErrorResult& aRv);
/**
* @brief Removes the Range from the Highlight Selection if it belongs to the
* same Document.
*
* @note If the last range of a highlight selection is removed, the selection
* itself is *not* removed.
*/
MOZ_CAN_RUN_SCRIPT void MaybeRemoveRangeFromHighlightSelection(
AbstractRange& aRange, Highlight& aHighlight);
/**
* @brief Removes the highlight selections associated with the highlight.
*
* This method is called when the Highlight is cleared
* (i.e., all Ranges are removed).
*/
MOZ_CAN_RUN_SCRIPT void RemoveHighlightSelection(Highlight& aHighlight);
// WebIDL interface
@ -92,15 +116,17 @@ class HighlightRegistry final : public nsISupports, public nsWrapperCache {
*
* If a `FrameSelection` is present, all highlight selections are removed.
*/
void Clear(ErrorResult& aRv);
MOZ_CAN_RUN_SCRIPT void Clear(ErrorResult& aRv);
/**
* @brief Removes the highlight named `aKey` from the registry.
*
* This call removes the combination of `this` and `aKey` from the highlight.
* If a `FrameSelection` is present, the highlight selection is removed.
*
* @return true if `aKey` existed and was deleted.
*/
void Delete(const nsAString& aKey, ErrorResult& aRv);
MOZ_CAN_RUN_SCRIPT bool Delete(const nsAString& aKey, ErrorResult& aRv);
private:
/**

View File

@ -23,6 +23,7 @@
#include "mozilla/dom/Element.h"
#include "mozilla/dom/SelectionBinding.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/dom/StaticRange.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/HTMLEditor.h"
#include "mozilla/IntegerRange.h"
@ -685,12 +686,15 @@ void Selection::SetAnchorFocusRange(size_t aIndex) {
if (aIndex >= mStyledRanges.Length()) {
return;
}
mAnchorFocusRange = mStyledRanges.mRanges[aIndex].mRange;
// Highlight selections may contain static ranges.
MOZ_ASSERT(mSelectionType != SelectionType::eHighlight);
AbstractRange* anchorFocusRange = mStyledRanges.mRanges[aIndex].mRange;
mAnchorFocusRange = anchorFocusRange->AsDynamicRange();
}
static int32_t CompareToRangeStart(const nsINode& aCompareNode,
uint32_t aCompareOffset,
const nsRange& aRange) {
const AbstractRange& aRange) {
MOZ_ASSERT(aRange.GetStartContainer());
nsINode* start = aRange.GetStartContainer();
// If the nodes that we're comparing are not in the same document, assume that
@ -709,7 +713,7 @@ static int32_t CompareToRangeStart(const nsINode& aCompareNode,
static int32_t CompareToRangeEnd(const nsINode& aCompareNode,
uint32_t aCompareOffset,
const nsRange& aRange) {
const AbstractRange& aRange) {
MOZ_ASSERT(aRange.IsPositioned());
nsINode* end = aRange.GetEndContainer();
// If the nodes that we're comparing are not in the same document or in the
@ -730,14 +734,14 @@ static int32_t CompareToRangeEnd(const nsINode& aCompareNode,
size_t Selection::StyledRanges::FindInsertionPoint(
const nsTArray<StyledRange>* aElementArray, const nsINode& aPointNode,
uint32_t aPointOffset,
int32_t (*aComparator)(const nsINode&, uint32_t, const nsRange&)) {
int32_t (*aComparator)(const nsINode&, uint32_t, const AbstractRange&)) {
int32_t beginSearch = 0;
int32_t endSearch = aElementArray->Length(); // one beyond what to check
if (endSearch) {
int32_t center = endSearch - 1; // Check last index, then binary search
do {
const nsRange* range = (*aElementArray)[center].mRange;
const AbstractRange* range = (*aElementArray)[center].mRange;
int32_t cmp{aComparator(aPointNode, aPointOffset, *range)};
@ -766,7 +770,7 @@ size_t Selection::StyledRanges::FindInsertionPoint(
// static
nsresult Selection::StyledRanges::SubtractRange(
StyledRange& aRange, nsRange& aSubtract, nsTArray<StyledRange>* aOutput) {
nsRange* range = aRange.mRange;
AbstractRange* range = aRange.mRange;
if (NS_WARN_IF(!range->IsPositioned())) {
return NS_ERROR_UNEXPECTED;
}
@ -1080,7 +1084,9 @@ nsresult Selection::StyledRanges::MaybeAddRangeAndTruncateOverlaps(
// Remove all the overlapping ranges
for (size_t i = startIndex; i < endIndex; ++i) {
mRanges[i].mRange->UnregisterSelection(mSelection);
if (mRanges[i].mRange->IsDynamicRange()) {
mRanges[i].mRange->AsDynamicRange()->UnregisterSelection(mSelection);
}
}
mRanges.RemoveElementsAt(startIndex, endIndex - startIndex);
@ -1102,9 +1108,12 @@ nsresult Selection::StyledRanges::MaybeAddRangeAndTruncateOverlaps(
mRanges.InsertElementsAt(startIndex, temp);
for (uint32_t i = 0; i < temp.Length(); ++i) {
MOZ_KnownLive(temp[i].mRange)->RegisterSelection(MOZ_KnownLive(mSelection));
// `MOZ_KnownLive` is required because of
// https://bugzilla.mozilla.org/show_bug.cgi?id=1622253.
if (temp[i].mRange->IsDynamicRange()) {
MOZ_KnownLive(temp[i].mRange->AsDynamicRange())
->RegisterSelection(MOZ_KnownLive(mSelection));
// `MOZ_KnownLive` is required because of
// https://bugzilla.mozilla.org/show_bug.cgi?id=1622253.
}
}
aOutIndex->emplace(startIndex + insertionPoint);
@ -1112,7 +1121,7 @@ nsresult Selection::StyledRanges::MaybeAddRangeAndTruncateOverlaps(
}
nsresult Selection::StyledRanges::RemoveRangeAndUnregisterSelection(
nsRange& aRange) {
AbstractRange& aRange) {
// Find the range's index & remove it. We could use FindInsertionPoint to
// get O(log n) time, but that requires many expensive DOM comparisons.
// For even several thousand items, this is probably faster because the
@ -1128,7 +1137,9 @@ nsresult Selection::StyledRanges::RemoveRangeAndUnregisterSelection(
if (idx < 0) return NS_ERROR_DOM_NOT_FOUND_ERR;
mRanges.RemoveElementAt(idx);
aRange.UnregisterSelection(mSelection);
if (aRange.IsDynamicRange()) {
aRange.AsDynamicRange()->UnregisterSelection(mSelection);
}
return NS_OK;
}
nsresult Selection::RemoveCollapsedRanges() {
@ -1170,7 +1181,7 @@ void Selection::Clear(nsPresContext* aPresContext) {
bool Selection::StyledRanges::HasEqualRangeBoundariesAt(
const nsRange& aRange, size_t aRangeIndex) const {
if (aRangeIndex < mRanges.Length()) {
const nsRange* range = mRanges[aRangeIndex].mRange;
const AbstractRange* range = mRanges[aRangeIndex].mRange;
return range->HasEqualBoundaries(aRange);
}
return false;
@ -1181,9 +1192,10 @@ void Selection::GetRangesForInterval(nsINode& aBeginNode, uint32_t aBeginOffset,
bool aAllowAdjacent,
nsTArray<RefPtr<nsRange>>& aReturn,
mozilla::ErrorResult& aRv) {
nsTArray<nsRange*> results;
nsresult rv = GetRangesForIntervalArray(&aBeginNode, aBeginOffset, &aEndNode,
aEndOffset, aAllowAdjacent, &results);
AutoTArray<nsRange*, 2> results;
nsresult rv =
GetDynamicRangesForIntervalArray(&aBeginNode, aBeginOffset, &aEndNode,
aEndOffset, aAllowAdjacent, &results);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
@ -1195,9 +1207,10 @@ void Selection::GetRangesForInterval(nsINode& aBeginNode, uint32_t aBeginOffset,
}
}
nsresult Selection::GetRangesForIntervalArray(
nsresult Selection::GetAbstractRangesForIntervalArray(
nsINode* aBeginNode, uint32_t aBeginOffset, nsINode* aEndNode,
uint32_t aEndOffset, bool aAllowAdjacent, nsTArray<nsRange*>* aRanges) {
uint32_t aEndOffset, bool aAllowAdjacent,
nsTArray<AbstractRange*>* aRanges) {
if (NS_WARN_IF(!aBeginNode)) {
return NS_ERROR_UNEXPECTED;
}
@ -1226,6 +1239,23 @@ nsresult Selection::GetRangesForIntervalArray(
return NS_OK;
}
nsresult Selection::GetDynamicRangesForIntervalArray(
nsINode* aBeginNode, uint32_t aBeginOffset, nsINode* aEndNode,
uint32_t aEndOffset, bool aAllowAdjacent, nsTArray<nsRange*>* aRanges) {
MOZ_ASSERT(mSelectionType != SelectionType::eHighlight);
AutoTArray<AbstractRange*, 2> abstractRanges;
nsresult rv = GetAbstractRangesForIntervalArray(
aBeginNode, aBeginOffset, aEndNode, aEndOffset, aAllowAdjacent,
&abstractRanges);
NS_ENSURE_SUCCESS(rv, rv);
aRanges->Clear();
aRanges->SetCapacity(abstractRanges.Length());
for (auto* abstractRange : abstractRanges) {
aRanges->AppendElement(abstractRange->AsDynamicRange());
}
return NS_OK;
}
nsresult Selection::StyledRanges::GetIndicesForInterval(
const nsINode* aBeginNode, uint32_t aBeginOffset, const nsINode* aEndNode,
uint32_t aEndOffset, bool aAllowAdjacent, Maybe<size_t>& aStartIndex,
@ -1254,7 +1284,7 @@ nsresult Selection::StyledRanges::GetIndicesForInterval(
&CompareToRangeStart)};
if (endsBeforeIndex == 0) {
const nsRange* endRange = mRanges[endsBeforeIndex].mRange;
const AbstractRange* endRange = mRanges[endsBeforeIndex].mRange;
// If the interval is strictly before the range at index 0, we can optimize
// by returning now - all ranges start after the given interval
@ -1289,9 +1319,9 @@ nsresult Selection::StyledRanges::GetIndicesForInterval(
// In the final case, there can be two such ranges, a collapsed range, and
// an adjacent range (they will appear in mRanges in that
// order). For this final case, we need to increment endsBeforeIndex, until
// one of the first two possibilites hold
// one of the first two possibilities hold
while (endsBeforeIndex < mRanges.Length()) {
const nsRange* endRange = mRanges[endsBeforeIndex].mRange;
const AbstractRange* endRange = mRanges[endsBeforeIndex].mRange;
if (!endRange->StartRef().Equals(aEndNode, aEndOffset)) {
break;
}
@ -1309,7 +1339,7 @@ nsresult Selection::StyledRanges::GetIndicesForInterval(
// order). For this final case, we only need to take action if both those
// ranges exist, and we are pointing to the collapsed range - we need to
// point to the adjacent range
const nsRange* beginRange = mRanges[beginsAfterIndex].mRange;
const AbstractRange* beginRange = mRanges[beginsAfterIndex].mRange;
if (beginsAfterIndex > 0 && beginRange->Collapsed() &&
beginRange->EndRef().Equals(aBeginNode, aBeginOffset)) {
beginRange = mRanges[beginsAfterIndex - 1].mRange;
@ -1322,7 +1352,7 @@ nsresult Selection::StyledRanges::GetIndicesForInterval(
// need to take action is when the range at beginsAfterIndex ends on
// the given interval's start point, but that range isn't collapsed (a
// collapsed range should be included in the returned results).
const nsRange* beginRange = mRanges[beginsAfterIndex].mRange;
const AbstractRange* beginRange = mRanges[beginsAfterIndex].mRange;
if (beginRange->EndRef().Equals(aBeginNode, aBeginOffset) &&
!beginRange->Collapsed()) {
beginsAfterIndex++;
@ -1333,7 +1363,7 @@ nsresult Selection::StyledRanges::GetIndicesForInterval(
// represents the point at the end of the interval - this range should be
// included
if (endsBeforeIndex < mRanges.Length()) {
const nsRange* endRange = mRanges[endsBeforeIndex].mRange;
const AbstractRange* endRange = mRanges[endsBeforeIndex].mRange;
if (endRange->StartRef().Equals(aEndNode, aEndOffset) &&
endRange->Collapsed()) {
endsBeforeIndex++;
@ -1465,8 +1495,12 @@ nsresult Selection::SelectFramesOfInclusiveDescendantsOfContent(
}
void Selection::SelectFramesInAllRanges(nsPresContext* aPresContext) {
// this method is currently only called in a user-initiated context.
// therefore it is safe to assume that we are not in a Highlight selection
// and we only have to deal with nsRanges (no StaticRanges).
MOZ_ASSERT(mSelectionType != SelectionType::eHighlight);
for (size_t i = 0; i < mStyledRanges.Length(); ++i) {
nsRange* range = mStyledRanges.mRanges[i].mRange;
nsRange* range = mStyledRanges.mRanges[i].mRange->AsDynamicRange();
MOZ_ASSERT(range->IsInAnySelection());
SelectFrames(aPresContext, range, range->IsInAnySelection());
}
@ -1476,14 +1510,19 @@ void Selection::SelectFramesInAllRanges(nsPresContext* aPresContext) {
* The idea of this helper method is to select or deselect "top to bottom",
* traversing through the frames
*/
nsresult Selection::SelectFrames(nsPresContext* aPresContext, nsRange* aRange,
bool aSelect) const {
nsresult Selection::SelectFrames(nsPresContext* aPresContext,
AbstractRange* aRange, bool aSelect) const {
if (!mFrameSelection || !aPresContext || !aPresContext->GetPresShell()) {
// nothing to do
return NS_OK;
}
MOZ_ASSERT(aRange && aRange->IsPositioned());
if (aRange->IsStaticRange() && !aRange->AsStaticRange()->IsValid()) {
// TODO jjaschke: Actions necessary to unselect invalid static ranges?
return NS_OK;
}
if (mFrameSelection->IsInTableSelectionMode()) {
nsINode* node = aRange->GetClosestCommonInclusiveAncestor();
nsIFrame* frame = node->IsContent()
@ -1629,10 +1668,10 @@ UniquePtr<SelectionDetails> Selection::LookUpSelection(
return aDetailsHead;
}
nsTArray<nsRange*> overlappingRanges;
nsresult rv = GetRangesForIntervalArray(aContent, aContentOffset, aContent,
aContentOffset + aContentLength,
false, &overlappingRanges);
nsTArray<AbstractRange*> overlappingRanges;
nsresult rv = GetAbstractRangesForIntervalArray(
aContent, aContentOffset, aContent, aContentOffset + aContentLength,
false, &overlappingRanges);
if (NS_FAILED(rv)) {
return aDetailsHead;
}
@ -1644,7 +1683,10 @@ UniquePtr<SelectionDetails> Selection::LookUpSelection(
UniquePtr<SelectionDetails> detailsHead = std::move(aDetailsHead);
for (size_t i = 0; i < overlappingRanges.Length(); i++) {
nsRange* range = overlappingRanges[i];
AbstractRange* range = overlappingRanges[i];
if (range->IsStaticRange() && !range->AsStaticRange()->IsValid()) {
continue;
}
nsINode* startNode = range->GetStartContainer();
nsINode* endNode = range->GetEndContainer();
uint32_t startOffset = range->StartOffset();
@ -1786,13 +1828,15 @@ void Selection::SetAncestorLimiter(nsIContent* aLimiter) {
void Selection::StyledRanges::UnregisterSelection() {
uint32_t count = mRanges.Length();
for (uint32_t i = 0; i < count; ++i) {
mRanges[i].mRange->UnregisterSelection(mSelection);
if (mRanges[i].mRange->IsDynamicRange()) {
mRanges[i].mRange->AsDynamicRange()->UnregisterSelection(mSelection);
}
}
}
void Selection::StyledRanges::Clear() { mRanges.Clear(); }
StyledRange* Selection::StyledRanges::FindRangeData(nsRange* aRange) {
StyledRange* Selection::StyledRanges::FindRangeData(AbstractRange* aRange) {
NS_ENSURE_TRUE(aRange, nullptr);
for (uint32_t i = 0; i < mRanges.Length(); i++) {
if (mRanges[i].mRange == aRange) {
@ -2009,6 +2053,26 @@ void Selection::AddRangeAndSelectFramesAndNotifyListeners(nsRange& aRange,
NotifySelectionListeners();
}
void Selection::AddHighlightRangeAndSelectFramesAndNotifyListeners(
AbstractRange& aRange, mozilla::ErrorResult& aRv) {
MOZ_ASSERT(mSelectionType == SelectionType::eHighlight);
mStyledRanges.mRanges.AppendElement(StyledRange{&aRange});
if (aRange.IsDynamicRange()) {
RefPtr<nsRange> range = aRange.AsDynamicRange();
range->RegisterSelection(*this);
}
if (!mFrameSelection) {
return; // nothing to do
}
RefPtr<nsPresContext> presContext = GetPresContext();
SelectFrames(presContext, &aRange, true);
// Be aware, this instance may be destroyed after this call.
NotifySelectionListeners();
}
// Selection::RemoveRangeAndUnselectFramesAndNotifyListeners
//
// Removes the given range from the selection. The tricky part is updating
@ -2022,7 +2086,7 @@ void Selection::AddRangeAndSelectFramesAndNotifyListeners(nsRange& aRange,
// selected frames after we've cleared the bit from ours.
void Selection::RemoveRangeAndUnselectFramesAndNotifyListeners(
nsRange& aRange, ErrorResult& aRv) {
AbstractRange& aRange, ErrorResult& aRv) {
nsresult rv = mStyledRanges.RemoveRangeAndUnregisterSelection(aRange);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
@ -2056,9 +2120,9 @@ void Selection::RemoveRangeAndUnselectFramesAndNotifyListeners(
SelectFrames(presContext, &aRange, false);
// add back the selected bit for each range touching our nodes
nsTArray<nsRange*> affectedRanges;
rv = GetRangesForIntervalArray(beginNode, beginOffset, endNode, endOffset,
true, &affectedRanges);
nsTArray<AbstractRange*> affectedRanges;
rv = GetAbstractRangesForIntervalArray(beginNode, beginOffset, endNode,
endOffset, true, &affectedRanges);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
@ -2232,7 +2296,7 @@ void Selection::CollapseToStart(ErrorResult& aRv) {
}
// Get the first range
const nsRange* firstRange = mStyledRanges.mRanges[0].mRange;
const AbstractRange* firstRange = mStyledRanges.mRanges[0].mRange;
if (!firstRange) {
aRv.Throw(NS_ERROR_FAILURE);
return;
@ -2269,7 +2333,7 @@ void Selection::CollapseToEnd(ErrorResult& aRv) {
}
// Get the last range
const nsRange* lastRange = mStyledRanges.mRanges[cnt - 1].mRange;
const AbstractRange* lastRange = mStyledRanges.mRanges[cnt - 1].mRange;
if (!lastRange) {
aRv.Throw(NS_ERROR_FAILURE);
return;
@ -2308,11 +2372,25 @@ nsRange* Selection::GetRangeAt(uint32_t aIndex, ErrorResult& aRv) {
return range;
}
nsRange* Selection::GetRangeAt(uint32_t aIndex) const {
AbstractRange* Selection::GetAbstractRangeAt(uint32_t aIndex) const {
StyledRange empty(nullptr);
return mStyledRanges.mRanges.SafeElementAt(aIndex, empty).mRange;
}
nsRange* Selection::GetRangeAt(uint32_t aIndex) const {
// This method per IDL spec returns a dynamic range.
// Therefore, it must be ensured that it is only called
// for a selection which contains dynamic ranges exclusively.
// Highlight Selections are allowed to contain StaticRanges,
// therefore this method must not be called.
MOZ_ASSERT(mSelectionType != SelectionType::eHighlight);
AbstractRange* abstractRange = GetAbstractRangeAt(aIndex);
if (!abstractRange) {
return nullptr;
}
return abstractRange->AsDynamicRange();
}
nsresult Selection::SetAnchorFocusToRange(nsRange* aRange) {
NS_ENSURE_STATE(mAnchorFocusRange);
@ -2737,9 +2815,9 @@ bool Selection::ContainsNode(nsINode& aNode, bool aAllowPartial,
nodeLength = aNode.GetChildCount();
}
nsTArray<nsRange*> overlappingRanges;
rv = GetRangesForIntervalArray(&aNode, 0, &aNode, nodeLength, false,
&overlappingRanges);
nsTArray<AbstractRange*> overlappingRanges;
rv = GetAbstractRangesForIntervalArray(&aNode, 0, &aNode, nodeLength, false,
&overlappingRanges);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return false;
@ -3074,7 +3152,7 @@ void Selection::RemoveSelectionListener(
Element* Selection::StyledRanges::GetCommonEditingHost() const {
Element* editingHost = nullptr;
for (const StyledRange& rangeData : mRanges) {
const nsRange* range = rangeData.mRange;
const AbstractRange* range = rangeData.mRange;
MOZ_ASSERT(range);
nsINode* commonAncestorNode = range->GetClosestCommonInclusiveAncestor();
if (!commonAncestorNode || !commonAncestorNode->IsContent()) {

View File

@ -230,6 +230,17 @@ class Selection final : public nsSupportsWeakReference,
*/
nsRange* GetRangeAt(uint32_t aIndex) const;
/**
* @brief Get the |AbstractRange| at |aIndex|.
*
* This method is safe to be called for every selection type.
* However, |StaticRange|s only occur for |SelectionType::eHighlight|.
* If the SelectionType may be eHighlight, this method must be called instead
* of |GetRangeAt()|.
*
* Returns null if |aIndex| is out of bounds.
*/
AbstractRange* GetAbstractRangeAt(uint32_t aIndex) const;
// Get the anchor-to-focus range if we don't care which end is
// anchor and which end is focus.
const nsRange* GetAnchorFocusRange() const { return mAnchorFocusRange; }
@ -365,7 +376,7 @@ class Selection final : public nsSupportsWeakReference,
* Callers need to keep `aRange` alive.
*/
MOZ_CAN_RUN_SCRIPT void RemoveRangeAndUnselectFramesAndNotifyListeners(
nsRange& aRange, mozilla::ErrorResult& aRv);
AbstractRange& aRange, mozilla::ErrorResult& aRv);
MOZ_CAN_RUN_SCRIPT void RemoveAllRanges(mozilla::ErrorResult& aRv);
@ -547,6 +558,9 @@ class Selection final : public nsSupportsWeakReference,
MOZ_CAN_RUN_SCRIPT void AddRangeAndSelectFramesAndNotifyListeners(
nsRange& aRange, mozilla::ErrorResult& aRv);
MOZ_CAN_RUN_SCRIPT void AddHighlightRangeAndSelectFramesAndNotifyListeners(
AbstractRange& aRange, mozilla::ErrorResult& aRv);
/**
* Adds all children of the specified node to the selection.
* @param aNode the parent of the children to be added to the selection.
@ -662,7 +676,7 @@ class Selection final : public nsSupportsWeakReference,
*/
void SetCanCacheFrameOffset(bool aCanCacheFrameOffset);
// Selection::GetRangesForIntervalArray
// Selection::GetAbstractRangesForIntervalArray
//
// Fills a nsTArray with the ranges overlapping the range specified by
// the given endpoints. Ranges in the selection exactly adjacent to the
@ -681,10 +695,23 @@ class Selection final : public nsSupportsWeakReference,
//
// Now that overlapping ranges are disallowed, there can be a maximum of
// 2 adjacent ranges
nsresult GetRangesForIntervalArray(nsINode* aBeginNode, uint32_t aBeginOffset,
nsINode* aEndNode, uint32_t aEndOffset,
bool aAllowAdjacent,
nsTArray<nsRange*>* aRanges);
nsresult GetAbstractRangesForIntervalArray(nsINode* aBeginNode,
uint32_t aBeginOffset,
nsINode* aEndNode,
uint32_t aEndOffset,
bool aAllowAdjacent,
nsTArray<AbstractRange*>* aRanges);
/**
* Converts the results of |GetAbstractRangesForIntervalArray()| to |nsRange|.
*
* |StaticRange|s can only occur in Selections of type |eHighlight|.
* Therefore, this method must not be called for this selection type
* as not every |AbstractRange| can be cast to |nsRange|.
*/
nsresult GetDynamicRangesForIntervalArray(
nsINode* aBeginNode, uint32_t aBeginOffset, nsINode* aEndNode,
uint32_t aEndOffset, bool aAllowAdjacent, nsTArray<nsRange*>* aRanges);
/**
* Modifies the cursor Bidi level after a change in keyboard direction
@ -792,7 +819,7 @@ class Selection final : public nsSupportsWeakReference,
PostContentIterator& aPostOrderIter, nsIContent* aContent,
bool aSelected) const;
nsresult SelectFrames(nsPresContext* aPresContext, nsRange* aRange,
nsresult SelectFrames(nsPresContext* aPresContext, AbstractRange* aRange,
bool aSelect) const;
/**
@ -817,7 +844,7 @@ class Selection final : public nsSupportsWeakReference,
explicit StyledRanges(Selection& aSelection) : mSelection(aSelection) {}
void Clear();
StyledRange* FindRangeData(nsRange* aRange);
StyledRange* FindRangeData(AbstractRange* aRange);
using Elements = AutoTArray<StyledRange, 1>;
@ -825,7 +852,7 @@ class Selection final : public nsSupportsWeakReference,
nsresult RemoveCollapsedRanges();
nsresult RemoveRangeAndUnregisterSelection(nsRange& aRange);
nsresult RemoveRangeAndUnregisterSelection(AbstractRange& aRange);
/**
* Binary searches the given sorted array of ranges for the insertion point
@ -841,7 +868,7 @@ class Selection final : public nsSupportsWeakReference,
static size_t FindInsertionPoint(
const nsTArray<StyledRange>* aElementArray, const nsINode& aPointNode,
uint32_t aPointOffset,
int32_t (*aComparator)(const nsINode&, uint32_t, const nsRange&));
int32_t (*aComparator)(const nsINode&, uint32_t, const AbstractRange&));
/**
* Works on the same principle as GetRangesForIntervalArray, however

View File

@ -60,6 +60,15 @@ class StaticRange final : public AbstractRange {
const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
const RangeBoundaryBase<EPT, ERT>& aEndBoundary, ErrorResult& aRv);
/**
* Returns true if the range is valid.
*
* @see https://dom.spec.whatwg.org/#staticrange-valid
*/
bool IsValid() const {
return mStart.IsSetAndValid() && mEnd.IsSetAndValid();
}
protected:
explicit StaticRange(nsINode* aNode)
: AbstractRange(aNode, /* aIsDynamicRange = */ false) {}

View File

@ -7,4 +7,5 @@
#include "mozilla/dom/StyledRange.h"
#include "nsRange.h"
StyledRange::StyledRange(nsRange* aRange) : mRange(aRange) {}
StyledRange::StyledRange(mozilla::dom::AbstractRange* aRange)
: mRange(aRange) {}

View File

@ -10,12 +10,14 @@
#include "mozilla/RefPtr.h"
#include "mozilla/TextRange.h"
class nsRange;
namespace mozilla::dom {
class AbstractRange;
}
struct StyledRange {
explicit StyledRange(nsRange* aRange);
explicit StyledRange(mozilla::dom::AbstractRange* aRange);
RefPtr<nsRange> mRange;
RefPtr<mozilla::dom::AbstractRange> mRange;
mozilla::TextRangeStyle mTextRangeStyle;
};

View File

@ -309,7 +309,7 @@ class IsItemInRangeComparator {
MOZ_ASSERT(aStartOffset <= aEndOffset);
}
int operator()(const nsRange* const aRange) const {
int operator()(const AbstractRange* const aRange) const {
int32_t cmp = nsContentUtils::ComparePoints_Deprecated(
&mNode, mEndOffset, aRange->GetStartContainer(), aRange->StartOffset(),
nullptr, mCache);
@ -373,25 +373,25 @@ bool nsINode::IsSelected(const uint32_t aStartOffset,
while (high != low) {
size_t middle = low + (high - low) / 2;
const nsRange* const range = selection->GetRangeAt(middle);
const AbstractRange* const range = selection->GetAbstractRangeAt(middle);
int result = comparator(range);
if (result == 0) {
if (!range->Collapsed()) {
return true;
}
const nsRange* middlePlus1;
const nsRange* middleMinus1;
const AbstractRange* middlePlus1;
const AbstractRange* middleMinus1;
// if node end > start of middle+1, result = 1
if (middle + 1 < high &&
(middlePlus1 = selection->GetRangeAt(middle + 1)) &&
(middlePlus1 = selection->GetAbstractRangeAt(middle + 1)) &&
nsContentUtils::ComparePoints_Deprecated(
this, aEndOffset, middlePlus1->GetStartContainer(),
middlePlus1->StartOffset(), nullptr, &cache) > 0) {
result = 1;
// if node start < end of middle - 1, result = -1
} else if (middle >= 1 &&
(middleMinus1 = selection->GetRangeAt(middle - 1)) &&
(middleMinus1 = selection->GetAbstractRangeAt(middle - 1)) &&
nsContentUtils::ComparePoints_Deprecated(
this, aStartOffset, middleMinus1->GetEndContainer(),
middleMinus1->EndOffset(), nullptr, &cache) < 0) {

View File

@ -893,15 +893,15 @@ void nsRange::NotifySelectionListenersAfterRangeSet() {
// way selections can be added or removed safely during iteration.
// To save allocation cost, the copy is only created if there is more than
// one Selection present (which will barely ever be the case).
if (mSelections.getFirst() != mSelections.getLast()) {
if (IsPartOfOneSelectionOnly()) {
RefPtr<Selection> selection = mSelections.getFirst()->Get();
selection->NotifySelectionListeners(calledByJSRestorer.SavedValue());
} else {
SelectionListLocalCopy copiedSelections{mSelections};
for (const auto* selectionWrapper : copiedSelections.Get()) {
RefPtr<Selection> selection = selectionWrapper->Get();
selection->NotifySelectionListeners(calledByJSRestorer.SavedValue());
}
} else {
RefPtr<Selection> selection = mSelections.getFirst()->Get();
selection->NotifySelectionListeners(calledByJSRestorer.SavedValue());
}
}
}

View File

@ -351,6 +351,14 @@ class nsRange final : public mozilla::dom::AbstractRange,
bool IsPointComparableToRange(const nsINode& aContainer, uint32_t aOffset,
ErrorResult& aErrorResult) const;
/**
* @brief Returns true if the range is part of exactly one |Selection|.
*/
bool IsPartOfOneSelectionOnly() const {
return !mSelections.isEmpty() &&
mSelections.getFirst() == mSelections.getLast();
};
public:
/**
* This helper function gets rects and correlated text for the given range.

View File

@ -4,7 +4,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* The origin of this IDL file is
* https://www.w3.org/TR/css-highlight-api-1/
* https://drafts.csswg.org/css-highlight-api-1/
*
* Copyright © 2021 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
* liability, trademark and document use rules apply.
@ -12,7 +12,7 @@
/**
* Enum defining the available highlight types.
* See https://www.w3.org/TR/css-highlight-api-1/#enumdef-highlighttype
* See https://drafts.csswg.org/css-highlight-api-1/#enumdef-highlighttype
*/
enum HighlightType {
"highlight",
@ -24,7 +24,7 @@ enum HighlightType {
* Definition of a highlight object, consisting of a set of ranges,
* a priority and a highlight type.
*
* See https://www.w3.org/TR/css-highlight-api-1/#highlight
* See https://drafts.csswg.org/css-highlight-api-1/#highlight
*/
[Pref="dom.customHighlightAPI.enabled", Exposed=Window]
interface Highlight {
@ -45,13 +45,13 @@ partial interface Highlight {
[Throws]
undefined clear();
[Throws]
undefined delete(AbstractRange range);
boolean delete(AbstractRange range);
};
/**
* Registry object that contains all Highlights associated with a Document.
*
* See https://www.w3.org/TR/css-highlight-api-1/#highlightregistry
* See https://drafts.csswg.org/css-highlight-api-1/#highlightregistry
*/
[Pref="dom.customHighlightAPI.enabled", Exposed=Window]
interface HighlightRegistry {
@ -67,5 +67,5 @@ partial interface HighlightRegistry {
[Throws]
undefined clear();
[Throws]
undefined delete(DOMString key);
boolean delete(DOMString key);
};

View File

@ -1722,8 +1722,8 @@ nsresult mozInlineSpellChecker::IsPointInSelection(Selection& aSelection,
*aRange = nullptr;
nsTArray<nsRange*> ranges;
nsresult rv = aSelection.GetRangesForIntervalArray(aNode, aOffset, aNode,
aOffset, true, &ranges);
nsresult rv = aSelection.GetDynamicRangesForIntervalArray(
aNode, aOffset, aNode, aOffset, true, &ranges);
NS_ENSURE_SUCCESS(rv, rv);
if (ranges.Length() == 0) return NS_OK; // no matches

View File

@ -235,7 +235,8 @@ struct MOZ_RAII AutoPrepareFocusRange {
// Scripted command or the user is starting a new explicit multi-range
// selection.
for (StyledRange& entry : ranges) {
entry.mRange->SetIsGenerated(false);
MOZ_ASSERT(entry.mRange->IsDynamicRange());
entry.mRange->AsDynamicRange()->SetIsGenerated(false);
}
return;
}
@ -280,16 +281,19 @@ struct MOZ_RAII AutoPrepareFocusRange {
nsRange* result{nullptr};
if (aSelection.GetDirection() == eDirNext) {
for (size_t i = 0; i < len; ++i) {
if (ranges[i].mRange->IsGenerated()) {
result = ranges[i].mRange;
// This function is only called for selections with type == eNormal.
// (see MOZ_ASSERT in constructor).
// Therefore, all ranges must be dynamic.
if (ranges[i].mRange->AsDynamicRange()->IsGenerated()) {
result = ranges[i].mRange->AsDynamicRange();
break;
}
}
} else {
size_t i = len;
while (i--) {
if (ranges[i].mRange->IsGenerated()) {
result = ranges[i].mRange;
if (ranges[i].mRange->AsDynamicRange()->IsGenerated()) {
result = ranges[i].mRange->AsDynamicRange();
break;
}
}
@ -303,7 +307,13 @@ struct MOZ_RAII AutoPrepareFocusRange {
nsTArray<StyledRange>& ranges = aSelection.mStyledRanges.mRanges;
size_t i = ranges.Length();
while (i--) {
nsRange* range = ranges[i].mRange;
// This function is only called for selections with type == eNormal.
// (see MOZ_ASSERT in constructor).
// Therefore, all ranges must be dynamic.
if (!ranges[i].mRange->IsDynamicRange()) {
continue;
}
nsRange* range = ranges[i].mRange->AsDynamicRange();
if (range->IsGenerated()) {
range->UnregisterSelection(aSelection);
aSelection.SelectFrames(presContext, range, false);
@ -1589,7 +1599,38 @@ void nsFrameSelection::AddHighlightSelection(
}
void nsFrameSelection::RemoveHighlightSelection(const nsAtom* aHighlightName) {
mHighlightSelections.Remove(aHighlightName);
if (auto maybeSelection = mHighlightSelections.MaybeGet(aHighlightName)) {
RefPtr<Selection> selection = *maybeSelection;
selection->RemoveAllRanges(IgnoreErrors());
mHighlightSelections.Remove(aHighlightName);
}
}
void nsFrameSelection::AddHighlightSelectionRange(
const nsAtom* aHighlightName, const mozilla::dom::Highlight& aHighlight,
mozilla::dom::AbstractRange& aRange, ErrorResult& aRv) {
if (auto lookupResult = mHighlightSelections.Lookup(aHighlightName)) {
RefPtr<Selection> selection = lookupResult.Data();
selection->AddHighlightRangeAndSelectFramesAndNotifyListeners(aRange, aRv);
} else {
// if the selection does not exist yet, add all of its ranges and exit.
RefPtr<Selection> selection =
aHighlight.CreateHighlightSelection(aHighlightName, this, aRv);
if (aRv.Failed()) {
return;
}
lookupResult.Data() = selection;
}
}
void nsFrameSelection::RemoveHighlightSelectionRange(
const nsAtom* aHighlightName, mozilla::dom::AbstractRange& aRange) {
if (const auto lookupResult = mHighlightSelections.Lookup(aHighlightName)) {
// NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
RefPtr<Selection> selection = lookupResult.Data();
selection->RemoveRangeAndUnselectFramesAndNotifyListeners(aRange,
IgnoreErrors());
}
}
nsresult nsFrameSelection::ScrollSelectionIntoView(SelectionType aSelectionType,

View File

@ -431,8 +431,24 @@ class nsFrameSelection final {
/**
* @brief Removes the Highlight selection identified by `aHighlightName`.
*/
void RemoveHighlightSelection(const nsAtom* aHighlightName);
MOZ_CAN_RUN_SCRIPT void RemoveHighlightSelection(
const nsAtom* aHighlightName);
/**
* @brief Adds a new range to the highlight selection.
*
* If there is no highlight selection for the given highlight yet, it is
* created using |AddHighlightSelection|.
*/
MOZ_CAN_RUN_SCRIPT void AddHighlightSelectionRange(
const nsAtom* aHighlightName, const mozilla::dom::Highlight& aHighlight,
mozilla::dom::AbstractRange& aRange, mozilla::ErrorResult& aRv);
/**
* @brief Removes a range from a highlight selection.
*/
MOZ_CAN_RUN_SCRIPT void RemoveHighlightSelectionRange(
const nsAtom* aHighlightName, mozilla::dom::AbstractRange& aRange);
/**
* ScrollSelectionIntoView scrolls a region of the selection,
* so that it is visible in the scrolled view.

View File

@ -13,6 +13,3 @@
[Highlight iteration is not modified when the range that was pointed to by the iterator was deleted using .clear() after starting the iteration]
expected: FAIL
[Highlight iteration is not modified when a range that was already visited is deleted and there are still ranges to visit]
expected: FAIL

View File

@ -1,30 +0,0 @@
[Highlight-iteration.html]
[Highlight can be iterated over all of its ranges initializing the iterator with customHighlight[Symbol.iterator\]() and adding two ranges by passing them to the constructor]
expected: FAIL
[Highlight can be iterated over all of its ranges initializing the iterator with customHighlight.values() and adding two ranges by passing them to the constructor]
expected: FAIL
[Highlight can be iterated over all of its ranges initializing the iterator with customHighlight.keys() and adding two ranges by passing them to the constructor]
expected: FAIL
[Highlight can be iterated over all of its ranges initializing the iterator with customHighlight[Symbol.iterator\]() and adding two ranges by passing them to the add function]
expected: FAIL
[Highlight can be iterated over all of its ranges initializing the iterator with customHighlight.values() and adding two ranges by passing them to the add function]
expected: FAIL
[Highlight can be iterated over all of its ranges initializing the iterator with customHighlight.keys() and adding two ranges by passing them to the add function]
expected: FAIL
[Highlight can be iterated over all of its ranges initializing the iterator with .entries() and adding two ranges by passing them to the constructor]
expected: FAIL
[Highlight can be iterated over all of its ranges initializing the iterator with .entries() and adding two ranges by passing them to the add function]
expected: FAIL
[Highlight can be iterated through using forEach when it has two ranges that were added by passing them to the constructor]
expected: FAIL
[Highlight can be iterated through using forEach when it has two ranges that were added by passing them to the add function]
expected: FAIL

View File

@ -1,3 +0,0 @@
[Highlight-setlike-tampered-Set-prototype.html]
[Highlight is a setlike interface that works as expected even if Set.prototype is tampered.]
expected: FAIL

View File

@ -1,54 +0,0 @@
[Highlight-setlike.html]
[Highlight delete method works as expected (using the following combination of ranges [[object Range\], [object Range\], [object Range\]\])]
expected: FAIL
[Highlight delete method works as expected (using the following combination of ranges [[object StaticRange\], [object StaticRange\], [object StaticRange\]\])]
expected: FAIL
[Highlight delete method works as expected (using the following combination of ranges [[object Range\], [object StaticRange\], [object Range\]\])]
expected: FAIL
[Highlight delete method works as expected (using the following combination of ranges [[object StaticRange\], [object Range\], [object StaticRange\]\])]
expected: FAIL
[Highlight add and has methods work as expected (using the following combination of ranges [[object StaticRange\], [object StaticRange\], [object StaticRange\]\])]
expected: FAIL
[Highlight constructor behaves like a set when using equal ranges (using the following combination of ranges [[object StaticRange\], [object StaticRange\], [object StaticRange\]\])]
expected: FAIL
[Highlight constructor works as expected when called with one range (using the following combination of ranges [[object StaticRange\], [object StaticRange\], [object StaticRange\]\])]
expected: FAIL
[Highlight constructor works as expected when called with two ranges (using the following combination of ranges [[object StaticRange\], [object StaticRange\], [object StaticRange\]\])]
expected: FAIL
[Highlight clear method works as expected (using the following combination of ranges [[object StaticRange\], [object StaticRange\], [object StaticRange\]\])]
expected: FAIL
[Highlight add and has methods work as expected (using the following combination of ranges [[object Range\], [object StaticRange\], [object Range\]\])]
expected: FAIL
[Highlight constructor behaves like a set when using equal ranges (using the following combination of ranges [[object Range\], [object StaticRange\], [object Range\]\])]
expected: FAIL
[Highlight constructor works as expected when called with two ranges (using the following combination of ranges [[object Range\], [object StaticRange\], [object Range\]\])]
expected: FAIL
[Highlight clear method works as expected (using the following combination of ranges [[object Range\], [object StaticRange\], [object Range\]\])]
expected: FAIL
[Highlight add and has methods work as expected (using the following combination of ranges [[object StaticRange\], [object Range\], [object StaticRange\]\])]
expected: FAIL
[Highlight constructor behaves like a set when using equal ranges (using the following combination of ranges [[object StaticRange\], [object Range\], [object StaticRange\]\])]
expected: FAIL
[Highlight constructor works as expected when called with one range (using the following combination of ranges [[object StaticRange\], [object Range\], [object StaticRange\]\])]
expected: FAIL
[Highlight constructor works as expected when called with two ranges (using the following combination of ranges [[object StaticRange\], [object Range\], [object StaticRange\]\])]
expected: FAIL
[Highlight clear method works as expected (using the following combination of ranges [[object StaticRange\], [object Range\], [object StaticRange\]\])]
expected: FAIL

View File

@ -1,5 +0,0 @@
[HighlightRegistry-maplike.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[HighlightRegistry has a maplike interface.]
expected: FAIL

View File

@ -1,2 +0,0 @@
[custom-highlight-painting-invalidation-002.html]
expected: FAIL