tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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