FragmentDirective.h (5527B)
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 #ifndef DOM_FRAGMENTDIRECTIVE_H_ 8 #define DOM_FRAGMENTDIRECTIVE_H_ 9 10 #include "js/TypeDecls.h" 11 #include "mozilla/UniquePtr.h" 12 #include "mozilla/dom/BindingDeclarations.h" 13 #include "mozilla/dom/fragmentdirectives_ffi_generated.h" 14 #include "nsCycleCollectionParticipant.h" 15 #include "nsStringFwd.h" 16 #include "nsWrapperCache.h" 17 18 class nsINode; 19 class nsIURI; 20 class nsRange; 21 namespace mozilla::dom { 22 class Document; 23 class Promise; 24 class Text; 25 class TextDirectiveFinder; 26 27 /** 28 * @brief The `FragmentDirective` class is the C++ representation of the 29 * `Document.fragmentDirective` webidl property. 30 * 31 * This class also serves as the main interface to interact with the fragment 32 * directive from the C++ side. It allows to find text fragment ranges from a 33 * given list of `TextDirective`s using 34 * `FragmentDirective::FindTextFragmentsInDocument()`. 35 * To avoid Text Directives being applied multiple times, this class implements 36 * the `uninvoked directive` mechanism, which in the spec is defined to be part 37 * of the `Document` [0], by encapsuling the code in a lazily constructed 38 * helper, which is destroyed when all text directives have been found. 39 * 40 * [0] 41 * https://wicg.github.io/scroll-to-text-fragment/#document-uninvoked-directives 42 */ 43 class FragmentDirective final : public nsISupports, public nsWrapperCache { 44 public: 45 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 46 NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(FragmentDirective) 47 48 public: 49 explicit FragmentDirective(Document* aDocument); 50 51 protected: 52 ~FragmentDirective(); 53 54 public: 55 Document* GetParentObject() const { return mDocument; }; 56 57 JSObject* WrapObject(JSContext* aCx, 58 JS::Handle<JSObject*> aGivenProto) override; 59 60 /** 61 * @brief Sets Text Directives as "uninvoked directive". 62 */ 63 void SetTextDirectives(nsTArray<TextDirective>&& aTextDirectives); 64 65 /** Returns true if there are Text Directives that have not been applied to 66 * the `Document`. 67 */ 68 bool HasUninvokedDirectives() const; 69 70 /** Clears all uninvoked directives. */ 71 void ClearUninvokedDirectives(); 72 73 /** Inserts all text directive ranges into a `eTargetText` `Selection`. */ 74 MOZ_CAN_RUN_SCRIPT 75 void HighlightTextDirectives( 76 const nsTArray<RefPtr<nsRange>>& aTextDirectiveRanges); 77 78 /** Searches for the current uninvoked text directives and creates a range for 79 * each one that is found. 80 * 81 * When this method returns, the uninvoked directives for this document are 82 * cleared. 83 * 84 * This method tries to follow the specification as close as possible in how 85 * to find a matching range for a text directive. However, instead of using 86 * collator-based search, the Gecko find-in-page algorithm is used (`nsFind`). 87 */ 88 nsTArray<RefPtr<nsRange>> FindTextFragmentsInDocument(); 89 90 /** Utility function which parses the fragment directive and removes it from 91 * the hash of the given URI. This operation happens in-place. 92 * 93 * If aTextDirectives is nullptr, the parsed fragment directive is discarded. 94 */ 95 static void ParseAndRemoveFragmentDirectiveFromFragment( 96 nsCOMPtr<nsIURI>& aURI, 97 nsTArray<TextDirective>* aTextDirectives = nullptr); 98 99 /** Parses the fragment directive and removes it from the hash, given as 100 * string. This operation happens in-place. 101 * 102 * This function is called internally by 103 * `ParseAndRemoveFragmentDirectiveFromFragment()`. 104 * 105 * This function returns true if it modified `aFragment`. 106 * 107 * Note: the parameter `aURI` is only used for logging purposes. 108 */ 109 static bool ParseAndRemoveFragmentDirectiveFromFragmentString( 110 nsCString& aFragment, nsTArray<TextDirective>* aTextDirectives = nullptr, 111 nsIURI* aURI = nullptr); 112 113 /** Utility function than returns a string for `aURI` ignoring all fragment 114 * directives. 115 */ 116 static nsresult GetSpecIgnoringFragmentDirective( 117 nsCOMPtr<nsIURI>& aURI, nsACString& aSpecIgnoringFragmentDirective); 118 119 /** Performs various checks to determine if a text directive is allowed to be 120 * scrolled to. 121 * 122 * This follows the algorithm "check if a text directive can be scrolled" in 123 * section 3.5.4 of the text fragment spec 124 * (https://wicg.github.io/scroll-to-text-fragment/#restricting-the-text-fragment). 125 */ 126 bool IsTextDirectiveAllowedToBeScrolledTo(); 127 128 /** Return an array of all current text directive ranges. 129 * 130 * This is exposed as a Chrome-Only API. 131 */ 132 void GetTextDirectiveRanges(nsTArray<RefPtr<nsRange>>& aRanges) const; 133 134 /** Removes all text directive ranges. 135 * 136 * Under the hood this method only calls `Selection::RemoveAllRanges()`. 137 * This is exposed as a Chrome-Only API. 138 */ 139 MOZ_CAN_RUN_SCRIPT void RemoveAllTextDirectives(ErrorResult& aRv); 140 141 /** Creates a text directive string for the current selection. 142 * 143 * @return Returns the created text directive as resolved promise, or a 144 * rejected promise in case of an error. 145 */ 146 already_AddRefed<Promise> CreateTextDirectiveForRanges( 147 const Sequence<OwningNonNull<nsRange>>& aRanges); 148 149 private: 150 RefPtr<Document> mDocument; 151 UniquePtr<TextDirectiveFinder> mFinder; 152 }; 153 154 } // namespace mozilla::dom 155 156 #endif // DOM_FRAGMENTDIRECTIVE_H_