diff options
Diffstat (limited to 'dom/base/Highlight.cpp')
-rw-r--r-- | dom/base/Highlight.cpp | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/dom/base/Highlight.cpp b/dom/base/Highlight.cpp new file mode 100644 index 0000000000..efff5b81b8 --- /dev/null +++ b/dom/base/Highlight.cpp @@ -0,0 +1,170 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "Highlight.h" +#include "HighlightRegistry.h" +#include "mozilla/StaticAnalysisFunctions.h" +#include "mozilla/dom/HighlightBinding.h" + +#include "mozilla/AlreadyAddRefed.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/RefPtr.h" + +#include "AbstractRange.h" +#include "Document.h" +#include "PresShell.h" +#include "Selection.h" + +#include "nsFrameSelection.h" +#include "nsPIDOMWindow.h" + +namespace mozilla::dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Highlight, mRanges, mWindow) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(Highlight) +NS_IMPL_CYCLE_COLLECTING_RELEASE(Highlight) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Highlight) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +Highlight::Highlight( + const Sequence<OwningNonNull<AbstractRange>>& aInitialRanges, + nsPIDOMWindowInner* aWindow, ErrorResult& aRv) + : mWindow(aWindow) { + for (RefPtr<AbstractRange> range : aInitialRanges) { + Add(*range, aRv); + if (aRv.Failed()) { + return; + } + } +} + +already_AddRefed<Highlight> Highlight::Constructor( + const GlobalObject& aGlobal, + const Sequence<OwningNonNull<AbstractRange>>& aInitialRanges, + ErrorResult& aRv) { + MOZ_ASSERT(NS_IsMainThread()); + nsCOMPtr<nsPIDOMWindowInner> window = + do_QueryInterface(aGlobal.GetAsSupports()); + if (!window) { + aRv.ThrowUnknownError( + "There is no window associated to " + "this highlight object!"); + return nullptr; + } + + RefPtr<Highlight> highlight = new Highlight(aInitialRanges, window, aRv); + return aRv.Failed() ? nullptr : highlight.forget(); +} + +void Highlight::AddToHighlightRegistry(HighlightRegistry& aHighlightRegistry, + const nsAtom& aHighlightName) { + mHighlightRegistries.LookupOrInsert(&aHighlightRegistry) + .Insert(&aHighlightName); +} + +void Highlight::RemoveFromHighlightRegistry( + HighlightRegistry& aHighlightRegistry, const nsAtom& aHighlightName) { + if (auto entry = mHighlightRegistries.Lookup(&aHighlightRegistry)) { + auto& highlightNames = entry.Data(); + highlightNames.Remove(&aHighlightName); + if (highlightNames.IsEmpty()) { + entry.Remove(); + } + } +} + +already_AddRefed<Selection> Highlight::CreateHighlightSelection( + const nsAtom* aHighlightName, nsFrameSelection* aFrameSelection) const { + MOZ_ASSERT(aFrameSelection); + MOZ_ASSERT(aFrameSelection->GetPresShell()); + RefPtr<Selection> selection = + MakeRefPtr<Selection>(SelectionType::eHighlight, aFrameSelection); + selection->SetHighlightName(aHighlightName); + AutoFrameSelectionBatcher selectionBatcher(__FUNCTION__); + selectionBatcher.AddFrameSelection(aFrameSelection); + for (const RefPtr<AbstractRange>& range : mRanges) { + if (range->GetComposedDocOfContainers() == + aFrameSelection->GetPresShell()->GetDocument()) { + // since this is run in a context guarded by a selection batcher, + // no strong reference is needed to keep `range` alive. + selection->AddHighlightRangeAndSelectFramesAndNotifyListeners( + MOZ_KnownLive(*range)); + } + } + return selection.forget(); +} + +void Highlight::Add(AbstractRange& aRange, ErrorResult& aRv) { + Highlight_Binding::SetlikeHelpers::Add(this, aRange, aRv); + if (aRv.Failed()) { + return; + } + if (!mRanges.Contains(&aRange)) { + mRanges.AppendElement(&aRange); + AutoFrameSelectionBatcher selectionBatcher(__FUNCTION__, + mHighlightRegistries.Count()); + for (const RefPtr<HighlightRegistry>& registry : + mHighlightRegistries.Keys()) { + auto frameSelection = registry->GetFrameSelection(); + selectionBatcher.AddFrameSelection(frameSelection); + // since this is run in a context guarded by a selection batcher, + // no strong reference is needed to keep `registry` alive. + MOZ_KnownLive(registry)->MaybeAddRangeToHighlightSelection(aRange, *this); + if (aRv.Failed()) { + return; + } + } + } +} + +void Highlight::Clear(ErrorResult& aRv) { + Highlight_Binding::SetlikeHelpers::Clear(this, aRv); + if (!aRv.Failed()) { + mRanges.Clear(); + AutoFrameSelectionBatcher selectionBatcher(__FUNCTION__, + mHighlightRegistries.Count()); + + for (const RefPtr<HighlightRegistry>& registry : + mHighlightRegistries.Keys()) { + auto frameSelection = registry->GetFrameSelection(); + selectionBatcher.AddFrameSelection(frameSelection); + // since this is run in a context guarded by a selection batcher, + // no strong reference is needed to keep `registry` alive. + MOZ_KnownLive(registry)->RemoveHighlightSelection(*this); + } + } +} + +bool Highlight::Delete(AbstractRange& aRange, ErrorResult& aRv) { + if (Highlight_Binding::SetlikeHelpers::Delete(this, aRange, aRv)) { + mRanges.RemoveElement(&aRange); + AutoFrameSelectionBatcher selectionBatcher(__FUNCTION__, + mHighlightRegistries.Count()); + + for (const RefPtr<HighlightRegistry>& registry : + mHighlightRegistries.Keys()) { + auto frameSelection = registry->GetFrameSelection(); + selectionBatcher.AddFrameSelection(frameSelection); + // since this is run in a context guarded by a selection batcher, + // no strong reference is needed to keep `registry` alive. + MOZ_KnownLive(registry)->MaybeRemoveRangeFromHighlightSelection(aRange, + *this); + } + return true; + } + return false; +} + +JSObject* Highlight::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return Highlight_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace mozilla::dom |