1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "HighlightRegistry.h"
9 #include "mozilla/ErrorResult.h"
10 #include "mozilla/CompactPair.h"
13 #include "Highlight.h"
14 #include "mozilla/dom/HighlightBinding.h"
15 #include "PresShell.h"
18 #include "nsCycleCollectionParticipant.h"
19 #include "nsFrameSelection.h"
21 namespace mozilla::dom
{
23 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(HighlightRegistry
)
25 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HighlightRegistry
)
26 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument
)
27 for (auto const& iter
: tmp
->mHighlightsOrdered
) {
28 iter
.second()->RemoveFromHighlightRegistry(*tmp
, *iter
.first());
30 NS_IMPL_CYCLE_COLLECTION_UNLINK(mHighlightsOrdered
)
31 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
32 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
34 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(HighlightRegistry
)
35 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument
)
36 for (size_t i
= 0; i
< tmp
->mHighlightsOrdered
.Length(); ++i
) {
37 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHighlightsOrdered
[i
].second())
39 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
41 NS_IMPL_CYCLE_COLLECTING_ADDREF(HighlightRegistry
)
42 NS_IMPL_CYCLE_COLLECTING_RELEASE(HighlightRegistry
)
43 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HighlightRegistry
)
44 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
45 NS_INTERFACE_MAP_ENTRY(nsISupports
)
48 HighlightRegistry::HighlightRegistry(Document
* aDocument
)
49 : mDocument(aDocument
) {}
51 HighlightRegistry::~HighlightRegistry() {
52 for (auto const& iter
: mHighlightsOrdered
) {
53 iter
.second()->RemoveFromHighlightRegistry(*this, *iter
.first());
57 JSObject
* HighlightRegistry::WrapObject(JSContext
* aCx
,
58 JS::Handle
<JSObject
*> aGivenProto
) {
59 return HighlightRegistry_Binding::Wrap(aCx
, this, aGivenProto
);
62 void HighlightRegistry::MaybeAddRangeToHighlightSelection(
63 AbstractRange
& aRange
, Highlight
& aHighlight
) {
64 RefPtr
<nsFrameSelection
> frameSelection
= GetFrameSelection();
65 if (!frameSelection
) {
68 MOZ_ASSERT(frameSelection
->GetPresShell());
69 if (!frameSelection
->GetPresShell()->GetDocument() ||
70 frameSelection
->GetPresShell()->GetDocument() !=
71 aRange
.GetComposedDocOfContainers()) {
72 // ranges that belong to a different document must not be added.
75 for (auto const& iter
: mHighlightsOrdered
) {
76 if (iter
.second() != &aHighlight
) {
80 const RefPtr
<nsAtom
> highlightName
= iter
.first();
81 frameSelection
->AddHighlightSelectionRange(highlightName
, aHighlight
,
86 void HighlightRegistry::MaybeRemoveRangeFromHighlightSelection(
87 AbstractRange
& aRange
, Highlight
& aHighlight
) {
88 RefPtr
<nsFrameSelection
> frameSelection
= GetFrameSelection();
89 if (!frameSelection
) {
92 MOZ_ASSERT(frameSelection
->GetPresShell());
94 for (auto const& iter
: mHighlightsOrdered
) {
95 if (iter
.second() != &aHighlight
) {
99 const RefPtr
<nsAtom
> highlightName
= iter
.first();
100 frameSelection
->RemoveHighlightSelectionRange(highlightName
, aRange
);
104 void HighlightRegistry::RemoveHighlightSelection(Highlight
& aHighlight
) {
105 RefPtr
<nsFrameSelection
> frameSelection
= GetFrameSelection();
106 if (!frameSelection
) {
109 for (auto const& iter
: mHighlightsOrdered
) {
110 if (iter
.second() != &aHighlight
) {
114 const RefPtr
<nsAtom
> highlightName
= iter
.first();
115 frameSelection
->RemoveHighlightSelection(highlightName
);
119 void HighlightRegistry::AddHighlightSelectionsToFrameSelection() {
120 if (mHighlightsOrdered
.IsEmpty()) {
123 RefPtr
<nsFrameSelection
> frameSelection
= GetFrameSelection();
124 if (!frameSelection
) {
127 for (auto const& iter
: mHighlightsOrdered
) {
128 RefPtr
<nsAtom
> highlightName
= iter
.first();
129 RefPtr
<Highlight
> highlight
= iter
.second();
130 frameSelection
->AddHighlightSelection(highlightName
, *highlight
);
134 void HighlightRegistry::Set(const nsAString
& aKey
, Highlight
& aValue
,
136 // manually check if the highlight `aKey` is already registered to be able to
137 // provide a fast path later that avoids calling `std::find_if()`.
138 const bool highlightAlreadyPresent
=
139 HighlightRegistry_Binding::MaplikeHelpers::Has(this, aKey
, aRv
);
143 HighlightRegistry_Binding::MaplikeHelpers::Set(this, aKey
, aValue
, aRv
);
147 RefPtr
<nsFrameSelection
> frameSelection
= GetFrameSelection();
148 RefPtr
<nsAtom
> highlightNameAtom
= NS_AtomizeMainThread(aKey
);
149 if (highlightAlreadyPresent
) {
150 // If the highlight named `aKey` was present before, replace its value.
152 std::find_if(mHighlightsOrdered
.begin(), mHighlightsOrdered
.end(),
153 [&highlightNameAtom
](auto const& aElm
) {
154 return aElm
.first() == highlightNameAtom
;
156 MOZ_ASSERT(foundIter
!= mHighlightsOrdered
.end(),
157 "webIDL maplike and DOM mirror are not in sync");
158 foundIter
->second()->RemoveFromHighlightRegistry(*this, *highlightNameAtom
);
159 if (frameSelection
) {
160 frameSelection
->RemoveHighlightSelection(highlightNameAtom
);
162 foundIter
->second() = &aValue
;
164 mHighlightsOrdered
.AppendElement(
165 CompactPair
<RefPtr
<nsAtom
>, RefPtr
<Highlight
>>(highlightNameAtom
,
168 aValue
.AddToHighlightRegistry(*this, *highlightNameAtom
);
169 if (frameSelection
) {
170 frameSelection
->AddHighlightSelection(highlightNameAtom
, aValue
);
174 void HighlightRegistry::Clear(ErrorResult
& aRv
) {
175 HighlightRegistry_Binding::MaplikeHelpers::Clear(this, aRv
);
179 auto frameSelection
= GetFrameSelection();
180 AutoFrameSelectionBatcher
batcher(__FUNCTION__
);
181 batcher
.AddFrameSelection(frameSelection
);
182 for (auto const& iter
: mHighlightsOrdered
) {
183 const RefPtr
<nsAtom
>& highlightName
= iter
.first();
184 const RefPtr
<Highlight
>& highlight
= iter
.second();
185 highlight
->RemoveFromHighlightRegistry(*this, *highlightName
);
186 if (frameSelection
) {
187 // The selection batcher makes sure that no script is run in this call.
188 // However, `nsFrameSelection::RemoveHighlightSelection` is marked
189 // `MOZ_CAN_RUN_SCRIPT`, therefore `MOZ_KnownLive` is needed regardless.
190 frameSelection
->RemoveHighlightSelection(MOZ_KnownLive(highlightName
));
194 mHighlightsOrdered
.Clear();
197 bool HighlightRegistry::Delete(const nsAString
& aKey
, ErrorResult
& aRv
) {
198 if (!HighlightRegistry_Binding::MaplikeHelpers::Delete(this, aKey
, aRv
)) {
201 RefPtr
<nsAtom
> highlightNameAtom
= NS_AtomizeMainThread(aKey
);
203 std::find_if(mHighlightsOrdered
.cbegin(), mHighlightsOrdered
.cend(),
204 [&highlightNameAtom
](auto const& aElm
) {
205 return aElm
.first() == highlightNameAtom
;
207 MOZ_ASSERT(foundIter
!= mHighlightsOrdered
.cend(),
208 "HighlightRegistry: maplike and internal data are out of sync!");
210 RefPtr
<Highlight
> highlight
= foundIter
->second();
211 mHighlightsOrdered
.RemoveElementAt(foundIter
);
213 if (auto frameSelection
= GetFrameSelection()) {
214 frameSelection
->RemoveHighlightSelection(highlightNameAtom
);
216 highlight
->RemoveFromHighlightRegistry(*this, *highlightNameAtom
);
220 RefPtr
<nsFrameSelection
> HighlightRegistry::GetFrameSelection() {
221 return RefPtr
<nsFrameSelection
>(
222 mDocument
->GetPresShell() ? mDocument
->GetPresShell()->FrameSelection()
226 } // namespace mozilla::dom