EditorLineBreak.h (11689B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #ifndef EditorLineBreak_h 7 #define EditorLineBreak_h 8 9 #include "EditorDOMPoint.h" 10 #include "EditorForwards.h" 11 #include "EditorUtils.h" 12 13 #include "mozilla/Maybe.h" 14 #include "mozilla/dom/Element.h" 15 #include "mozilla/dom/HTMLBRElement.h" 16 #include "mozilla/dom/Text.h" 17 18 #include "nsCOMPtr.h" 19 #include "nsDebug.h" 20 #include "nsGkAtoms.h" 21 #include "nsIContent.h" 22 23 namespace mozilla { 24 25 class AutoTrackLineBreak; 26 27 /****************************************************************************** 28 * EditorLineBreakBase stores <br> or a preformatted line break position. 29 * This cannot represent no line break. Therefore, if a method may not return 30 * a line break, they need to use Maybe. 31 ******************************************************************************/ 32 template <typename ContentType> 33 class EditorLineBreakBase { 34 using SelfType = EditorLineBreakBase<ContentType>; 35 36 public: 37 using HTMLBRElement = dom::HTMLBRElement; 38 using Text = dom::Text; 39 40 explicit EditorLineBreakBase(const HTMLBRElement& aBRElement) 41 : mContent(const_cast<HTMLBRElement*>(&aBRElement)) {} 42 explicit EditorLineBreakBase(RefPtr<HTMLBRElement>&& aBRElement); 43 explicit EditorLineBreakBase(RefPtr<dom::Element>&& aBRElement); 44 explicit EditorLineBreakBase(nsCOMPtr<nsIContent>&& aBRElement); 45 EditorLineBreakBase(const Text& aText, uint32_t aOffset) 46 : mContent(const_cast<Text*>(&aText)), mOffsetInText(Some(aOffset)) {} 47 EditorLineBreakBase(RefPtr<Text>&& aText, uint32_t aOffset); 48 EditorLineBreakBase(nsCOMPtr<nsIContent>&& aText, uint32_t aOffset); 49 50 [[nodiscard]] static SelfType AtLastChar(const Text& aText) { 51 MOZ_RELEASE_ASSERT(aText.TextDataLength()); 52 return SelfType(aText, aText.TextDataLength() - 1u); 53 } 54 [[nodiscard]] static SelfType AtLastChar(RefPtr<Text>&& aText) { 55 MOZ_RELEASE_ASSERT(aText); 56 MOZ_RELEASE_ASSERT(aText->TextDataLength()); 57 const uint32_t lastCharIndex = aText->TextDataLength() - 1u; 58 return SelfType(std::forward<RefPtr<Text>>(aText), lastCharIndex); 59 } 60 [[nodiscard]] static SelfType AtLastChar(nsCOMPtr<nsIContent>&& aText) { 61 MOZ_RELEASE_ASSERT(aText); 62 MOZ_RELEASE_ASSERT(aText->IsText()); 63 MOZ_RELEASE_ASSERT(aText->AsText()->TextDataLength()); 64 const uint32_t lastCharIndex = aText->AsText()->TextDataLength() - 1u; 65 return SelfType(std::forward<nsCOMPtr<nsIContent>>(aText), lastCharIndex); 66 } 67 68 [[nodiscard]] bool IsInComposedDoc() const { 69 return mContent->IsInComposedDoc(); 70 } 71 72 template <typename EditorDOMPointType> 73 [[nodiscard]] EditorDOMPointType To() const { 74 static_assert(std::is_same<EditorDOMPointType, EditorRawDOMPoint>::value || 75 std::is_same<EditorDOMPointType, EditorDOMPoint>::value); 76 return mOffsetInText.isSome() 77 ? EditorDOMPointType(mContent, mOffsetInText.value()) 78 : EditorDOMPointType(mContent); 79 } 80 template <typename EditorDOMPointType> 81 [[nodiscard]] EditorDOMPointType After() const { 82 if (IsHTMLBRElement()) { 83 return EditorDOMPointType::After(BRElementRef()); 84 } 85 if (mOffsetInText.value() + 1 < TextRef().TextDataLength()) { 86 return EditorDOMPointType(&TextRef(), mOffsetInText.value() + 1); 87 } 88 // If the line break is end of a Text node and it's followed by another 89 // Text, we should return start of the following Text. 90 if (Text* const followingText = 91 Text::FromNodeOrNull(TextRef().GetNextSibling())) { 92 return EditorDOMPointType(followingText, 0); 93 } 94 return EditorDOMPointType::After(TextRef()); 95 } 96 97 template <typename EditorDOMPointType> 98 [[nodiscard]] EditorDOMPointType Before() const { 99 if (IsHTMLBRElement()) { 100 return EditorDOMPointType(&BRElementRef(), 101 dom::Selection::InterlinePosition::EndOfLine); 102 } 103 return To<EditorDOMPointType>(); 104 } 105 106 [[nodiscard]] bool IsHTMLBRElement() const { 107 MOZ_ASSERT_IF(!mOffsetInText, mContent->IsHTMLElement(nsGkAtoms::br)); 108 return mOffsetInText.isNothing(); 109 } 110 [[nodiscard]] bool IsPreformattedLineBreak() const { 111 MOZ_ASSERT_IF(mOffsetInText, mContent->IsText()); 112 return mOffsetInText.isSome(); 113 } 114 [[nodiscard]] bool TextIsOnlyPreformattedLineBreak() const { 115 return IsPreformattedLineBreak() && !Offset() && 116 TextRef().TextDataLength() == 1u; 117 } 118 119 [[nodiscard]] nsIContent& ContentRef() const { return *mContent; } 120 121 [[nodiscard]] HTMLBRElement& BRElementRef() const { 122 MOZ_DIAGNOSTIC_ASSERT(IsHTMLBRElement()); 123 MOZ_DIAGNOSTIC_ASSERT(GetBRElement()); 124 return *GetBRElement(); 125 } 126 [[nodiscard]] HTMLBRElement* GetBRElement() const { 127 return HTMLBRElement::FromNode(mContent); 128 } 129 [[nodiscard]] Text& TextRef() const { 130 MOZ_DIAGNOSTIC_ASSERT(IsPreformattedLineBreak()); 131 MOZ_DIAGNOSTIC_ASSERT(GetText()); 132 return *GetText(); 133 } 134 [[nodiscard]] Text* GetText() const { return Text::FromNode(mContent); } 135 [[nodiscard]] uint32_t Offset() const { 136 MOZ_ASSERT(IsPreformattedLineBreak()); 137 return mOffsetInText.value(); 138 } 139 [[nodiscard]] bool CharAtOffsetIsLineBreak() const { 140 MOZ_DIAGNOSTIC_ASSERT(IsPreformattedLineBreak()); 141 return *mOffsetInText < TextRef().TextDataLength() && 142 TextRef().DataBuffer().CharAt(*mOffsetInText) == '\n'; 143 } 144 145 [[nodiscard]] bool IsDeletableFromComposedDoc() const { 146 if (IsPreformattedLineBreak()) { 147 return TextRef().IsEditable(); 148 } 149 const nsIContent* const parent = BRElementRef().GetParent(); 150 return parent && parent->IsEditable(); 151 } 152 153 private: 154 ContentType mContent; 155 Maybe<uint32_t> mOffsetInText; 156 157 friend class AutoTrackLineBreak; 158 }; 159 160 using EditorLineBreak = EditorLineBreakBase<nsCOMPtr<nsIContent>>; 161 using EditorRawLineBreak = EditorLineBreakBase<nsIContent*>; 162 163 template <> 164 inline EditorLineBreakBase<nsCOMPtr<nsIContent>>::EditorLineBreakBase( 165 RefPtr<HTMLBRElement>&& aBRElement) 166 : mContent(aBRElement.forget()) { 167 MOZ_RELEASE_ASSERT(mContent); 168 } 169 170 template <> 171 inline EditorLineBreakBase<nsCOMPtr<nsIContent>>::EditorLineBreakBase( 172 RefPtr<dom::Element>&& aBRElement) 173 : mContent(aBRElement.forget()) { 174 MOZ_RELEASE_ASSERT(mContent); 175 MOZ_RELEASE_ASSERT(mContent->IsHTMLElement(nsGkAtoms::br)); 176 } 177 178 template <> 179 inline EditorLineBreakBase<nsCOMPtr<nsIContent>>::EditorLineBreakBase( 180 nsCOMPtr<nsIContent>&& aBRElement) 181 : mContent(aBRElement.forget()) { 182 MOZ_RELEASE_ASSERT(mContent); 183 MOZ_RELEASE_ASSERT(mContent->IsHTMLElement(nsGkAtoms::br)); 184 } 185 186 template <> 187 inline EditorLineBreakBase<nsCOMPtr<nsIContent>>::EditorLineBreakBase( 188 RefPtr<Text>&& aText, uint32_t aOffset) 189 : mContent(std::move(aText)), mOffsetInText(Some(aOffset)) { 190 MOZ_RELEASE_ASSERT(mContent); 191 MOZ_ASSERT(EditorUtils::IsNewLinePreformatted(*mContent)); 192 MOZ_RELEASE_ASSERT(GetText()->TextDataLength() > aOffset); 193 MOZ_RELEASE_ASSERT(CharAtOffsetIsLineBreak()); 194 } 195 196 template <> 197 inline EditorLineBreakBase<nsCOMPtr<nsIContent>>::EditorLineBreakBase( 198 nsCOMPtr<nsIContent>&& aText, uint32_t aOffset) 199 : mContent(aText.forget()), mOffsetInText(Some(aOffset)) { 200 MOZ_RELEASE_ASSERT(mContent); 201 MOZ_RELEASE_ASSERT(mContent->IsText()); 202 MOZ_ASSERT(EditorUtils::IsNewLinePreformatted(*mContent)); 203 MOZ_RELEASE_ASSERT(TextRef().TextDataLength() > aOffset); 204 MOZ_ASSERT(CharAtOffsetIsLineBreak()); 205 } 206 207 template <> 208 inline EditorLineBreakBase<nsIContent*>::EditorLineBreakBase( 209 const HTMLBRElement& aBRElement) 210 : mContent(const_cast<HTMLBRElement*>(&aBRElement)) {} 211 212 template <> 213 inline EditorLineBreakBase<nsIContent*>::EditorLineBreakBase( 214 RefPtr<HTMLBRElement>&& aBRElement) 215 : mContent(aBRElement) { 216 MOZ_RELEASE_ASSERT(mContent); 217 aBRElement = nullptr; 218 } 219 220 template <> 221 inline EditorLineBreakBase<nsIContent*>::EditorLineBreakBase( 222 RefPtr<dom::Element>&& aBRElement) 223 : mContent(aBRElement) { 224 MOZ_RELEASE_ASSERT(mContent); 225 MOZ_RELEASE_ASSERT(mContent->IsHTMLElement(nsGkAtoms::br)); 226 aBRElement = nullptr; 227 } 228 229 template <> 230 inline EditorLineBreakBase<nsIContent*>::EditorLineBreakBase( 231 nsCOMPtr<nsIContent>&& aBRElement) 232 : mContent(aBRElement) { 233 MOZ_RELEASE_ASSERT(mContent); 234 MOZ_RELEASE_ASSERT(mContent->IsHTMLElement(nsGkAtoms::br)); 235 aBRElement = nullptr; 236 } 237 238 template <> 239 inline EditorLineBreakBase<nsIContent*>::EditorLineBreakBase( 240 RefPtr<Text>&& aText, uint32_t aOffset) 241 : mContent(aText), mOffsetInText(Some(aOffset)) { 242 MOZ_RELEASE_ASSERT(mContent); 243 MOZ_ASSERT(EditorUtils::IsNewLinePreformatted(*mContent)); 244 MOZ_RELEASE_ASSERT(GetText()->TextDataLength() > aOffset); 245 MOZ_RELEASE_ASSERT(CharAtOffsetIsLineBreak()); 246 aText = nullptr; 247 } 248 249 template <> 250 inline EditorLineBreakBase<nsIContent*>::EditorLineBreakBase( 251 nsCOMPtr<nsIContent>&& aText, uint32_t aOffset) 252 : mContent(aText), mOffsetInText(Some(aOffset)) { 253 MOZ_RELEASE_ASSERT(mContent); 254 MOZ_RELEASE_ASSERT(mContent->IsText()); 255 MOZ_ASSERT(EditorUtils::IsNewLinePreformatted(*mContent)); 256 MOZ_RELEASE_ASSERT(GetText()->TextDataLength() > aOffset); 257 MOZ_ASSERT(CharAtOffsetIsLineBreak()); 258 aText = nullptr; 259 } 260 261 class CreateLineBreakResult final : public CaretPoint { 262 public: 263 CreateLineBreakResult(const EditorLineBreak& aLineBreak, 264 const EditorDOMPoint& aCaretPoint) 265 : CaretPoint(aCaretPoint), mLineBreak(Some(aLineBreak)) {} 266 CreateLineBreakResult(EditorLineBreak&& aLineBreak, 267 const EditorDOMPoint& aCaretPoint) 268 : CaretPoint(aCaretPoint), mLineBreak(Some(std::move(aLineBreak))) {} 269 CreateLineBreakResult(const EditorLineBreak& aLineBreak, 270 EditorDOMPoint&& aCaretPoint) 271 : CaretPoint(aCaretPoint), mLineBreak(Some(aLineBreak)) {} 272 CreateLineBreakResult(EditorLineBreak&& aLineBreak, 273 EditorDOMPoint&& aCaretPoint) 274 : CaretPoint(std::move(aCaretPoint)), 275 mLineBreak(Some(std::move(aLineBreak))) {} 276 explicit CreateLineBreakResult(CreateElementResult&& aCreateElementResult) 277 : CaretPoint(aCreateElementResult.UnwrapCaretPoint()), 278 mLineBreak(Some(aCreateElementResult.UnwrapNewNode())) {} 279 280 [[nodiscard]] static CreateLineBreakResult NotHandled() { 281 return CreateLineBreakResult(); 282 } 283 284 [[nodiscard]] constexpr bool Handled() const { return mLineBreak.isSome(); } 285 [[nodiscard]] constexpr const EditorLineBreak& LineBreakRef() const { 286 MOZ_ASSERT(Handled()); 287 return mLineBreak.ref(); 288 } 289 [[nodiscard]] constexpr const EditorLineBreak* operator->() const { 290 return &LineBreakRef(); 291 } 292 293 // Shortcut for unclear methods of EditorLineBreak if `->` operator is used. 294 295 template <typename EditorDOMPointType> 296 [[nodiscard]] EditorDOMPointType AtLineBreak() const { 297 return LineBreakRef().To<EditorDOMPointType>(); 298 } 299 template <typename EditorDOMPointType> 300 [[nodiscard]] EditorDOMPointType BeforeLineBreak() const { 301 return LineBreakRef().Before<EditorDOMPointType>(); 302 } 303 template <typename EditorDOMPointType> 304 [[nodiscard]] EditorDOMPointType AfterLineBreak() const { 305 return LineBreakRef().After<EditorDOMPointType>(); 306 } 307 [[nodiscard]] nsIContent& LineBreakContentRef() const { 308 return LineBreakRef().ContentRef(); 309 } 310 [[nodiscard]] bool LineBreakIsInComposedDoc() const { 311 return LineBreakRef().IsInComposedDoc(); 312 } 313 314 private: 315 CreateLineBreakResult() : CaretPoint(EditorDOMPoint()) {} 316 317 Maybe<EditorLineBreak> mLineBreak; 318 }; 319 320 } // namespace mozilla 321 322 #endif // #ifndef EditorLineBreak_h