HighlightRegistry.cpp (8529B)
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/. */ 6 7 #include "HighlightRegistry.h" 8 9 #include "Document.h" 10 #include "Highlight.h" 11 #include "PresShell.h" 12 #include "mozilla/CompactPair.h" 13 #include "mozilla/ErrorResult.h" 14 #include "mozilla/dom/HighlightBinding.h" 15 #include "nsAtom.h" 16 #include "nsCycleCollectionParticipant.h" 17 #include "nsFrameSelection.h" 18 19 namespace mozilla::dom { 20 21 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(HighlightRegistry) 22 23 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HighlightRegistry) 24 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument) 25 for (auto const& iter : tmp->mHighlightsOrdered) { 26 iter.second()->RemoveFromHighlightRegistry(*tmp, *iter.first()); 27 } 28 NS_IMPL_CYCLE_COLLECTION_UNLINK(mHighlightsOrdered) 29 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 30 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 31 32 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(HighlightRegistry) 33 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument) 34 for (size_t i = 0; i < tmp->mHighlightsOrdered.Length(); ++i) { 35 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHighlightsOrdered[i].second()) 36 } 37 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 38 39 NS_IMPL_CYCLE_COLLECTING_ADDREF(HighlightRegistry) 40 NS_IMPL_CYCLE_COLLECTING_RELEASE(HighlightRegistry) 41 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HighlightRegistry) 42 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 43 NS_INTERFACE_MAP_ENTRY(nsISupports) 44 NS_INTERFACE_MAP_END 45 46 HighlightRegistry::HighlightRegistry(Document* aDocument) 47 : mDocument(aDocument) {} 48 49 HighlightRegistry::~HighlightRegistry() { 50 for (auto const& iter : mHighlightsOrdered) { 51 iter.second()->RemoveFromHighlightRegistry(*this, *iter.first()); 52 } 53 } 54 55 JSObject* HighlightRegistry::WrapObject(JSContext* aCx, 56 JS::Handle<JSObject*> aGivenProto) { 57 return HighlightRegistry_Binding::Wrap(aCx, this, aGivenProto); 58 } 59 60 void HighlightRegistry::MaybeAddRangeToHighlightSelection( 61 AbstractRange& aRange, Highlight& aHighlight) { 62 RefPtr<nsFrameSelection> frameSelection = GetFrameSelection(); 63 if (!frameSelection) { 64 return; 65 } 66 MOZ_ASSERT(frameSelection->GetPresShell()); 67 if (!frameSelection->GetPresShell()->GetDocument() || 68 frameSelection->GetPresShell()->GetDocument() != 69 aRange.GetComposedDocOfContainers()) { 70 // ranges that belong to a different document must not be added. 71 return; 72 } 73 for (auto const& iter : mHighlightsOrdered) { 74 if (iter.second() != &aHighlight) { 75 continue; 76 } 77 78 const RefPtr<nsAtom> highlightName = iter.first(); 79 frameSelection->AddHighlightSelectionRange(highlightName, aHighlight, 80 aRange); 81 } 82 } 83 84 void HighlightRegistry::MaybeRemoveRangeFromHighlightSelection( 85 AbstractRange& aRange, Highlight& aHighlight) { 86 RefPtr<nsFrameSelection> frameSelection = GetFrameSelection(); 87 if (!frameSelection) { 88 return; 89 } 90 MOZ_ASSERT(frameSelection->GetPresShell()); 91 92 for (auto const& iter : mHighlightsOrdered) { 93 if (iter.second() != &aHighlight) { 94 continue; 95 } 96 97 const RefPtr<nsAtom> highlightName = iter.first(); 98 frameSelection->RemoveHighlightSelectionRange(highlightName, aRange); 99 } 100 } 101 102 void HighlightRegistry::RemoveHighlightSelection(Highlight& aHighlight) { 103 RefPtr<nsFrameSelection> frameSelection = GetFrameSelection(); 104 if (!frameSelection) { 105 return; 106 } 107 for (auto const& iter : mHighlightsOrdered) { 108 if (iter.second() != &aHighlight) { 109 continue; 110 } 111 112 const RefPtr<nsAtom> highlightName = iter.first(); 113 frameSelection->RemoveHighlightSelection(highlightName); 114 } 115 } 116 117 void HighlightRegistry::RepaintHighlightSelection(Highlight& aHighlight) { 118 RefPtr<nsFrameSelection> frameSelection = GetFrameSelection(); 119 if (!frameSelection) { 120 return; 121 } 122 for (auto const& iter : mHighlightsOrdered) { 123 if (iter.second() != &aHighlight) { 124 continue; 125 } 126 127 const RefPtr<nsAtom> highlightName = iter.first(); 128 frameSelection->RepaintHighlightSelection(highlightName); 129 } 130 } 131 132 void HighlightRegistry::AddHighlightSelectionsToFrameSelection() { 133 if (mHighlightsOrdered.IsEmpty()) { 134 return; 135 } 136 RefPtr<nsFrameSelection> frameSelection = GetFrameSelection(); 137 if (!frameSelection) { 138 return; 139 } 140 for (auto const& iter : mHighlightsOrdered) { 141 RefPtr<nsAtom> highlightName = iter.first(); 142 RefPtr<Highlight> highlight = iter.second(); 143 frameSelection->AddHighlightSelection(highlightName, *highlight); 144 } 145 } 146 147 HighlightRegistry* HighlightRegistry::Set(const nsAString& aKey, 148 Highlight& aValue, ErrorResult& aRv) { 149 // manually check if the highlight `aKey` is already registered to be able to 150 // provide a fast path later that avoids calling `std::find_if()`. 151 const bool highlightAlreadyPresent = 152 HighlightRegistry_Binding::MaplikeHelpers::Has(this, aKey, aRv); 153 if (aRv.Failed()) { 154 return this; 155 } 156 HighlightRegistry_Binding::MaplikeHelpers::Set(this, aKey, aValue, aRv); 157 if (aRv.Failed()) { 158 return this; 159 } 160 RefPtr<nsFrameSelection> frameSelection = GetFrameSelection(); 161 RefPtr<nsAtom> highlightNameAtom = NS_AtomizeMainThread(aKey); 162 if (highlightAlreadyPresent) { 163 // If the highlight named `aKey` was present before, replace its value. 164 auto foundIter = 165 std::find_if(mHighlightsOrdered.begin(), mHighlightsOrdered.end(), 166 [&highlightNameAtom](auto const& aElm) { 167 return aElm.first() == highlightNameAtom; 168 }); 169 MOZ_ASSERT(foundIter != mHighlightsOrdered.end(), 170 "webIDL maplike and DOM mirror are not in sync"); 171 foundIter->second()->RemoveFromHighlightRegistry(*this, *highlightNameAtom); 172 if (frameSelection) { 173 frameSelection->RemoveHighlightSelection(highlightNameAtom); 174 } 175 foundIter->second() = &aValue; 176 } else { 177 mHighlightsOrdered.AppendElement( 178 CompactPair<RefPtr<nsAtom>, RefPtr<Highlight>>(highlightNameAtom, 179 &aValue)); 180 } 181 aValue.AddToHighlightRegistry(*this, *highlightNameAtom); 182 if (frameSelection) { 183 frameSelection->AddHighlightSelection(highlightNameAtom, aValue); 184 } 185 return this; 186 } 187 188 void HighlightRegistry::Clear(ErrorResult& aRv) { 189 HighlightRegistry_Binding::MaplikeHelpers::Clear(this, aRv); 190 if (aRv.Failed()) { 191 return; 192 } 193 auto frameSelection = GetFrameSelection(); 194 AutoFrameSelectionBatcher batcher(__FUNCTION__); 195 batcher.AddFrameSelection(frameSelection); 196 for (auto const& iter : mHighlightsOrdered) { 197 const RefPtr<nsAtom>& highlightName = iter.first(); 198 const RefPtr<Highlight>& highlight = iter.second(); 199 highlight->RemoveFromHighlightRegistry(*this, *highlightName); 200 if (frameSelection) { 201 // The selection batcher makes sure that no script is run in this call. 202 // However, `nsFrameSelection::RemoveHighlightSelection` is marked 203 // `MOZ_CAN_RUN_SCRIPT`, therefore `MOZ_KnownLive` is needed regardless. 204 frameSelection->RemoveHighlightSelection(MOZ_KnownLive(highlightName)); 205 } 206 } 207 208 mHighlightsOrdered.Clear(); 209 } 210 211 bool HighlightRegistry::Delete(const nsAString& aKey, ErrorResult& aRv) { 212 if (!HighlightRegistry_Binding::MaplikeHelpers::Delete(this, aKey, aRv)) { 213 return false; 214 } 215 RefPtr<nsAtom> highlightNameAtom = NS_AtomizeMainThread(aKey); 216 auto foundIter = 217 std::find_if(mHighlightsOrdered.cbegin(), mHighlightsOrdered.cend(), 218 [&highlightNameAtom](auto const& aElm) { 219 return aElm.first() == highlightNameAtom; 220 }); 221 MOZ_ASSERT(foundIter != mHighlightsOrdered.cend(), 222 "HighlightRegistry: maplike and internal data are out of sync!"); 223 224 RefPtr<Highlight> highlight = foundIter->second(); 225 mHighlightsOrdered.RemoveElementAt(foundIter); 226 227 if (auto frameSelection = GetFrameSelection()) { 228 frameSelection->RemoveHighlightSelection(highlightNameAtom); 229 } 230 highlight->RemoveFromHighlightRegistry(*this, *highlightNameAtom); 231 return true; 232 } 233 234 RefPtr<nsFrameSelection> HighlightRegistry::GetFrameSelection() { 235 return RefPtr<nsFrameSelection>( 236 mDocument->GetPresShell() ? mDocument->GetPresShell()->FrameSelection() 237 : nullptr); 238 } 239 240 } // namespace mozilla::dom