mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 06:43:32 +00:00
Bug 1808565, part 1: Adapt Selection
to support StaticRange
s. 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:
parent
51b56e8b3b
commit
ebf3cd6240
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>(
|
||||
|
@ -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())) {
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -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() {
|
||||
|
@ -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:
|
||||
/**
|
||||
|
@ -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()) {
|
||||
|
@ -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
|
||||
|
@ -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) {}
|
||||
|
@ -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) {}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -1,5 +0,0 @@
|
||||
[HighlightRegistry-maplike.html]
|
||||
expected:
|
||||
if (os == "android") and fission: [OK, TIMEOUT]
|
||||
[HighlightRegistry has a maplike interface.]
|
||||
expected: FAIL
|
@ -1,2 +0,0 @@
|
||||
[custom-highlight-painting-invalidation-002.html]
|
||||
expected: FAIL
|
Loading…
Reference in New Issue
Block a user