Highlight.cpp (7159B)
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 "Highlight.h" 8 9 #include "AbstractRange.h" 10 #include "Document.h" 11 #include "HighlightRegistry.h" 12 #include "PresShell.h" 13 #include "Selection.h" 14 #include "mozilla/AlreadyAddRefed.h" 15 #include "mozilla/ErrorResult.h" 16 #include "mozilla/RefPtr.h" 17 #include "mozilla/StaticAnalysisFunctions.h" 18 #include "mozilla/dom/HighlightBinding.h" 19 #include "nsFrameSelection.h" 20 #include "nsPIDOMWindow.h" 21 22 namespace mozilla::dom { 23 24 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Highlight, mRanges, mWindow) 25 26 NS_IMPL_CYCLE_COLLECTING_ADDREF(Highlight) 27 NS_IMPL_CYCLE_COLLECTING_RELEASE(Highlight) 28 29 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Highlight) 30 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 31 NS_INTERFACE_MAP_ENTRY(nsISupports) 32 NS_INTERFACE_MAP_END 33 34 Highlight::Highlight( 35 const Sequence<OwningNonNull<AbstractRange>>& aInitialRanges, 36 nsPIDOMWindowInner* aWindow, ErrorResult& aRv) 37 : mWindow(aWindow) { 38 for (RefPtr<AbstractRange> range : aInitialRanges) { 39 Add(*range, aRv); 40 if (aRv.Failed()) { 41 return; 42 } 43 } 44 } 45 46 already_AddRefed<Highlight> Highlight::Constructor( 47 const GlobalObject& aGlobal, 48 const Sequence<OwningNonNull<AbstractRange>>& aInitialRanges, 49 ErrorResult& aRv) { 50 MOZ_ASSERT(NS_IsMainThread()); 51 nsCOMPtr<nsPIDOMWindowInner> window = 52 do_QueryInterface(aGlobal.GetAsSupports()); 53 if (!window) { 54 aRv.ThrowUnknownError( 55 "There is no window associated to " 56 "this highlight object!"); 57 return nullptr; 58 } 59 60 RefPtr<Highlight> highlight = new Highlight(aInitialRanges, window, aRv); 61 return aRv.Failed() ? nullptr : highlight.forget(); 62 } 63 64 void Highlight::AddToHighlightRegistry(HighlightRegistry& aHighlightRegistry, 65 nsAtom& aHighlightName) { 66 mHighlightRegistries.LookupOrInsert(&aHighlightRegistry) 67 .Insert(&aHighlightName); 68 } 69 70 void Highlight::RemoveFromHighlightRegistry( 71 HighlightRegistry& aHighlightRegistry, nsAtom& aHighlightName) { 72 if (auto entry = mHighlightRegistries.Lookup(&aHighlightRegistry)) { 73 auto& highlightNames = entry.Data(); 74 highlightNames.Remove(&aHighlightName); 75 if (highlightNames.IsEmpty()) { 76 entry.Remove(); 77 } 78 } 79 } 80 81 already_AddRefed<Selection> Highlight::CreateHighlightSelection( 82 nsAtom* aHighlightName, nsFrameSelection* aFrameSelection) { 83 MOZ_ASSERT(aFrameSelection); 84 MOZ_ASSERT(aFrameSelection->GetPresShell()); 85 RefPtr<Selection> selection = 86 MakeRefPtr<Selection>(SelectionType::eHighlight, aFrameSelection); 87 selection->SetHighlightSelectionData({aHighlightName, this}); 88 AutoFrameSelectionBatcher selectionBatcher(__FUNCTION__); 89 for (const RefPtr<AbstractRange>& range : mRanges) { 90 if (range->GetComposedDocOfContainers() == 91 aFrameSelection->GetPresShell()->GetDocument()) { 92 // since this is run in a context guarded by a selection batcher, 93 // no strong reference is needed to keep `range` alive. 94 selection->AddHighlightRangeAndSelectFramesAndNotifyListeners( 95 MOZ_KnownLive(*range)); 96 } 97 } 98 return selection.forget(); 99 } 100 101 void Highlight::Repaint() { 102 for (const RefPtr<HighlightRegistry>& registry : 103 mHighlightRegistries.Keys()) { 104 // since this is run in a context guarded by a selection batcher, 105 // no strong reference is needed to keep `registry` alive. 106 registry->RepaintHighlightSelection(*this); 107 } 108 } 109 110 void Highlight::SetPriority(int32_t aPriority) { 111 if (mPriority == aPriority) { 112 return; 113 } 114 mPriority = aPriority; 115 Repaint(); 116 } 117 118 void Highlight::SetType(HighlightType aHighlightType) { 119 if (mHighlightType == aHighlightType) { 120 return; 121 } 122 mHighlightType = aHighlightType; 123 Repaint(); 124 } 125 126 Highlight* Highlight::Add(AbstractRange& aRange, ErrorResult& aRv) { 127 // Manually check if the range `aKey` is already present in this highlight, 128 // because `SetlikeHelpers::Add()` doesn't indicate this. 129 // To keep the setlike and the mirrored array in sync, the range must not 130 // be added to `mRanges` if it was already present. 131 // `SetlikeHelpers::Has()` is much faster in checking this than 132 // `nsTArray<>::Contains()`. 133 if (Highlight_Binding::SetlikeHelpers::Has(this, aRange, aRv) || 134 aRv.Failed()) { 135 return this; 136 } 137 Highlight_Binding::SetlikeHelpers::Add(this, aRange, aRv); 138 if (aRv.Failed()) { 139 return this; 140 } 141 142 MOZ_ASSERT(!mRanges.Contains(&aRange), 143 "setlike and DOM mirror are not in sync"); 144 145 mRanges.AppendElement(&aRange); 146 AutoFrameSelectionBatcher selectionBatcher(__FUNCTION__, 147 mHighlightRegistries.Count()); 148 for (const RefPtr<HighlightRegistry>& registry : 149 mHighlightRegistries.Keys()) { 150 auto frameSelection = registry->GetFrameSelection(); 151 selectionBatcher.AddFrameSelection(frameSelection); 152 // since this is run in a context guarded by a selection batcher, 153 // no strong reference is needed to keep `registry` alive. 154 MOZ_KnownLive(registry)->MaybeAddRangeToHighlightSelection(aRange, *this); 155 if (aRv.Failed()) { 156 return this; 157 } 158 } 159 return this; 160 } 161 162 void Highlight::Clear(ErrorResult& aRv) { 163 Highlight_Binding::SetlikeHelpers::Clear(this, aRv); 164 if (!aRv.Failed()) { 165 mRanges.Clear(); 166 AutoFrameSelectionBatcher selectionBatcher(__FUNCTION__, 167 mHighlightRegistries.Count()); 168 169 for (const RefPtr<HighlightRegistry>& registry : 170 mHighlightRegistries.Keys()) { 171 auto frameSelection = registry->GetFrameSelection(); 172 selectionBatcher.AddFrameSelection(frameSelection); 173 // since this is run in a context guarded by a selection batcher, 174 // no strong reference is needed to keep `registry` alive. 175 MOZ_KnownLive(registry)->RemoveHighlightSelection(*this); 176 } 177 } 178 } 179 180 bool Highlight::Delete(AbstractRange& aRange, ErrorResult& aRv) { 181 if (Highlight_Binding::SetlikeHelpers::Delete(this, aRange, aRv)) { 182 mRanges.RemoveElement(&aRange); 183 AutoFrameSelectionBatcher selectionBatcher(__FUNCTION__, 184 mHighlightRegistries.Count()); 185 186 for (const RefPtr<HighlightRegistry>& registry : 187 mHighlightRegistries.Keys()) { 188 auto frameSelection = registry->GetFrameSelection(); 189 selectionBatcher.AddFrameSelection(frameSelection); 190 // since this is run in a context guarded by a selection batcher, 191 // no strong reference is needed to keep `registry` alive. 192 MOZ_KnownLive(registry)->MaybeRemoveRangeFromHighlightSelection(aRange, 193 *this); 194 } 195 return true; 196 } 197 return false; 198 } 199 200 JSObject* Highlight::WrapObject(JSContext* aCx, 201 JS::Handle<JSObject*> aGivenProto) { 202 return Highlight_Binding::Wrap(aCx, this, aGivenProto); 203 } 204 205 } // namespace mozilla::dom