/* 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/. */ #ifndef mozilla_dom_Highlight_h #define mozilla_dom_Highlight_h #include #include "mozilla/Attributes.h" #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/HighlightBinding.h" #include "nsAtomHashKeys.h" #include "nsCycleCollectionParticipant.h" #include "nsTArray.h" #include "nsTHashSet.h" #include "nsWrapperCache.h" class nsFrameSelection; class nsPIDOMWindowInner; namespace mozilla { class ErrorResult; } namespace mozilla::dom { class AbstractRange; class Document; class Element; class HighlightRegistry; class Selection; class ShadowRoot; /** * @brief Collection of all data of a highlight instance. * * This struct is intended to be allocated on the stack and passed on * to the `nsFrameSelection` and layout code. */ struct HighlightSelectionData { friend auto format_as(const HighlightSelectionData& aData) { nsAutoCString highlightName; if (aData.mHighlightName) { highlightName = nsAtomCString(aData.mHighlightName); } return fmt::format("{{ mHighlightName=\"{}\", mHighlight={} }}", highlightName.get(), static_cast(aData.mHighlight.get())); } friend std::ostream& operator<<(std::ostream& aStream, const HighlightSelectionData& aData) { return aStream << format_as(aData); } RefPtr mHighlightName; RefPtr mHighlight; }; /** * @brief Representation of a custom `Highlight`. * * A `Highlight` is defined in JS as a collection of `AbstractRange`s. * Furthermore, a custom highlight contains the highlight type and priority. * * A highlight is added to a document using the `HighlightRegistry` interface. * A highlight can be added to a document using different names as well as to * multiple `HighlightRegistries`. * To propagate runtime changes of the highlight to its registries, an * observer pattern is implemented. * * The spec defines this class as a `setlike`. To allow access and iteration * of the setlike contents from C++, the insertion and deletion operations are * overridden and the Ranges are also stored internally in an Array. * * @see https://drafts.csswg.org/css-highlight-api-1/#creation */ class Highlight final : public nsISupports, public nsWrapperCache { public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS_FINAL NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(Highlight) protected: MOZ_CAN_RUN_SCRIPT Highlight( const Sequence>& aInitialRanges, nsPIDOMWindowInner* aWindow, ErrorResult& aRv); ~Highlight() = default; public: /** * @brief Adds `this` to `aHighlightRegistry`. * * Highlights must know of all registry objects which contain them, so that * the registries can be notified when a property of the Highlight changes. * * Since a Highlight can be part of a registry using different names, * the name has to be provided as well. */ void AddToHighlightRegistry(HighlightRegistry& aHighlightRegistry, nsAtom& aHighlightName); /** * @brief Removes `this` from `aHighlightRegistry`. */ void RemoveFromHighlightRegistry(HighlightRegistry& aHighlightRegistry, nsAtom& aHighlightName); // WebIDL interface nsPIDOMWindowInner* GetParentObject() const { return mWindow; } JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; static MOZ_CAN_RUN_SCRIPT_BOUNDARY already_AddRefed Constructor( const GlobalObject& aGlobal, const Sequence>& aInitialRanges, ErrorResult& aRv); /** * @brief Priority of this highlight. * * Priority is used to stack overlapping highlights. */ int32_t Priority() const { return mPriority; } /** * @brief Set the priority of this Highlight. * * Priority is used to stack overlapping highlights. */ void SetPriority(int32_t aPriority); /** * @brief The HighlightType of this Highlight (Highlight, Spelling Error, * Grammar Error) */ HighlightType Type() const { return mHighlightType; } /** * @brief Sets the HighlightType (Highlight, Spelling Error, Grammar Error) */ void SetType(HighlightType aHighlightType); /** * @brief This mirrors the `size` property in JS world (_not_ exposed via * webIDL) */ uint32_t Size() const { return mRanges.Length(); } /** * @brief Access to the ranges for frame selection population. */ const nsTArray>& Ranges() const { return mRanges; } /** * @brief Adds a `Range` to this highlight. * * This adds `aRange` both to the setlike data storage and the internal one * needed for iteration, if it is not yet present. * * Also notifies all `HighlightRegistry` instances. */ MOZ_CAN_RUN_SCRIPT Highlight* Add(AbstractRange& aRange, ErrorResult& aRv); /** * @brief Removes all ranges from this highlight. * * This removes all highlights from the setlike data structure as well as from * the internal one. * * Also notifies all `HighlightRegistry` instances. */ MOZ_CAN_RUN_SCRIPT void Clear(ErrorResult& aRv); /** * @brief Removes `aRange` from this highlight. * * This removes `aRange` from the setlike data structure as well as from the * internal one. * * Also notifies all `HighlightRegistry` instances. * * @return As per spec, returns true if the range was deleted. */ MOZ_CAN_RUN_SCRIPT bool Delete(AbstractRange& aRange, ErrorResult& aRv); /** * @brief Returns ranges of this highlight present at the given point. * * This method uses `range.getClientRects()` to determine if a range is * present at the given point. * Ranges in a shadow tree are only considered if the shadow root is in * `aShadowRoots`. If `aPointShadowRoot` is non-null, only ranges in that * shadow tree are considered (the point itself is inside it). * * @param aX x coordinate * @param aY y coordinate * @param aShadowRoots List of shadow roots to consider * @param aElementAtPoint The element at the hit-tested point. */ nsTArray> RangesAtPoint( float aX, float aY, const Sequence>& aShadowRoots, mozilla::dom::Element* aElementAtPoint) const; private: void Repaint(); RefPtr mWindow; /** * All Range objects contained in this highlight. */ nsTArray> mRanges; /** * Type of this highlight. * @see HighlightType */ HighlightType mHighlightType{HighlightType::Highlight}; /** * Priority of this highlight. * * If highlights are overlapping, the priority can * be used to prioritize. If the priorities of all * Highlights involved are equal, the highlights are * stacked in order of ther insertion into the * `HighlightRegistry`. */ int32_t mPriority{0}; /** * All highlight registries that contain this Highlight. * * A highlight can be included in several registries * using several names. * * Note: Storing `HighlightRegistry` as raw pointer is safe here * because it unregisters itself from `this` when it is destroyed/CC'd */ nsTHashMap, nsTHashSet>> mHighlightRegistries; }; } // namespace mozilla::dom #endif // mozilla_dom_Highlight_h