tor-browser

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

EditorUtils.h (20163B)


      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 mozilla_EditorUtils_h
      7 #define mozilla_EditorUtils_h
      8 
      9 #include "mozilla/EditorBase.h"      // for EditorBase
     10 #include "mozilla/EditorDOMPoint.h"  // for EditorDOMPoint, EditorDOMRange, etc
     11 #include "mozilla/EditorForwards.h"
     12 #include "mozilla/IntegerRange.h"       // for IntegerRange
     13 #include "mozilla/Maybe.h"              // for Maybe
     14 #include "mozilla/Result.h"             // for Result<>
     15 #include "mozilla/dom/DataTransfer.h"   // for dom::DataTransfer
     16 #include "mozilla/dom/Element.h"        // for dom::Element
     17 #include "mozilla/dom/HTMLBRElement.h"  // for dom::HTMLBRElement
     18 #include "mozilla/dom/Selection.h"      // for dom::Selection
     19 #include "mozilla/dom/Text.h"           // for dom::Text
     20 
     21 #include "nsAtom.h"          // for nsStaticAtom
     22 #include "nsCOMPtr.h"        // for nsCOMPtr
     23 #include "nsContentUtils.h"  // for nsContentUtils
     24 #include "nsDebug.h"         // for NS_WARNING, etc
     25 #include "nsError.h"         // for NS_SUCCESS_* and NS_ERROR_*
     26 #include "nsRange.h"         // for nsRange
     27 #include "nsString.h"        // for nsAString, nsString, etc
     28 
     29 class nsITransferable;
     30 
     31 namespace mozilla {
     32 
     33 enum class StyleWhiteSpace : uint8_t;
     34 
     35 enum class SuggestCaret {
     36  // If specified, the method returns NS_OK when there is no recommended caret
     37  // position.
     38  OnlyIfHasSuggestion,
     39  // If specified and if EditorBase::AllowsTransactionsToChangeSelection
     40  // returns false, the method does nothing and returns NS_OK.
     41  OnlyIfTransactionsAllowedToDoIt,
     42  // If specified, the method returns
     43  // NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR even if
     44  // EditorBase::CollapseSelectionTo returns an error except when
     45  // NS_ERROR_EDITOR_DESTROYED.
     46  AndIgnoreTrivialError,
     47 };
     48 
     49 /******************************************************************************
     50 * CaretPoint is a wrapper of EditorDOMPoint and provides a helper method to
     51 * collapse Selection there, or move it to a local variable.  This is typically
     52 * used as the ok type of Result or a base class of DoSomethingResult classes.
     53 ******************************************************************************/
     54 class MOZ_STACK_CLASS CaretPoint {
     55 public:
     56  explicit CaretPoint(const EditorDOMPoint& aPointToPutCaret)
     57      : mCaretPoint(aPointToPutCaret) {}
     58  explicit CaretPoint(EditorDOMPoint&& aPointToPutCaret)
     59      : mCaretPoint(std::move(aPointToPutCaret)) {}
     60 
     61  CaretPoint(const CaretPoint&) = delete;
     62  CaretPoint& operator=(const CaretPoint&) = delete;
     63  CaretPoint(CaretPoint&&) = default;
     64  CaretPoint& operator=(CaretPoint&&) = default;
     65 
     66  /**
     67   * Suggest caret position to aEditorBase.
     68   */
     69  [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SuggestCaretPointTo(
     70      EditorBase& aEditorBase, const SuggestCaretOptions& aOptions) const;
     71 
     72  /**
     73   * IgnoreCaretPointSuggestion() should be called if the method does not want
     74   * to use caret position recommended by this instance.
     75   */
     76  void IgnoreCaretPointSuggestion() const { mHandledCaretPoint = true; }
     77 
     78  /**
     79   * When propagating the result, it may not want to the caller modify
     80   * selection.  In such case, this can clear the caret point.  Use
     81   * IgnoreCaretPointSuggestion() in the caller side instead.
     82   */
     83  void ForgetCaretPointSuggestion() { mCaretPoint.Clear(); }
     84 
     85  bool HasCaretPointSuggestion() const { return mCaretPoint.IsSet(); }
     86  constexpr const EditorDOMPoint& CaretPointRef() const { return mCaretPoint; }
     87  constexpr EditorDOMPoint&& UnwrapCaretPoint() {
     88    mHandledCaretPoint = true;
     89    return std::move(mCaretPoint);
     90  }
     91  bool CopyCaretPointTo(EditorDOMPoint& aPointToPutCaret,
     92                        const SuggestCaretOptions& aOptions) const {
     93    MOZ_ASSERT(!aOptions.contains(SuggestCaret::AndIgnoreTrivialError));
     94    MOZ_ASSERT(
     95        !aOptions.contains(SuggestCaret::OnlyIfTransactionsAllowedToDoIt));
     96    mHandledCaretPoint = true;
     97    if (aOptions.contains(SuggestCaret::OnlyIfHasSuggestion) &&
     98        !mCaretPoint.IsSet()) {
     99      return false;
    100    }
    101    aPointToPutCaret = mCaretPoint;
    102    return true;
    103  }
    104  bool CopyCaretPointTo(CaretPoint& aCaretPoint,
    105                        const SuggestCaretOptions& aOptions) const {
    106    return CopyCaretPointTo(aCaretPoint.mCaretPoint, aOptions);
    107  }
    108  bool MoveCaretPointTo(EditorDOMPoint& aPointToPutCaret,
    109                        const SuggestCaretOptions& aOptions) {
    110    MOZ_ASSERT(!aOptions.contains(SuggestCaret::AndIgnoreTrivialError));
    111    MOZ_ASSERT(
    112        !aOptions.contains(SuggestCaret::OnlyIfTransactionsAllowedToDoIt));
    113    if (aOptions.contains(SuggestCaret::OnlyIfHasSuggestion) &&
    114        !mCaretPoint.IsSet()) {
    115      return false;
    116    }
    117    aPointToPutCaret = UnwrapCaretPoint();
    118    return true;
    119  }
    120  bool MoveCaretPointTo(CaretPoint& aCaretPoint,
    121                        const SuggestCaretOptions& aOptions) {
    122    return MoveCaretPointTo(aCaretPoint.mCaretPoint, aOptions);
    123  }
    124  bool CopyCaretPointTo(EditorDOMPoint& aPointToPutCaret,
    125                        const EditorBase& aEditorBase,
    126                        const SuggestCaretOptions& aOptions) const;
    127  bool MoveCaretPointTo(EditorDOMPoint& aPointToPutCaret,
    128                        const EditorBase& aEditorBase,
    129                        const SuggestCaretOptions& aOptions);
    130 
    131 protected:
    132  constexpr bool CaretPointHandled() const { return mHandledCaretPoint; }
    133 
    134  void SetCaretPoint(const EditorDOMPoint& aCaretPoint) {
    135    mHandledCaretPoint = false;
    136    mCaretPoint = aCaretPoint;
    137  }
    138  void SetCaretPoint(EditorDOMPoint&& aCaretPoint) {
    139    mHandledCaretPoint = false;
    140    mCaretPoint = std::move(aCaretPoint);
    141  }
    142 
    143  void UnmarkAsHandledCaretPoint() { mHandledCaretPoint = true; }
    144 
    145  CaretPoint() = default;
    146 
    147 private:
    148  EditorDOMPoint mCaretPoint;
    149  bool mutable mHandledCaretPoint = false;
    150 
    151  friend class AutoTrackDOMPoint;
    152 };
    153 
    154 /***************************************************************************
    155 * EditActionResult is useful to return the handling state of edit sub actions
    156 * without out params.
    157 */
    158 class MOZ_STACK_CLASS EditActionResult {
    159 public:
    160  bool Canceled() const { return mCanceled; }
    161  bool Handled() const { return mHandled; }
    162  bool Ignored() const { return !mCanceled && !mHandled; }
    163 
    164  void MarkAsCanceled() { mCanceled = true; }
    165  void MarkAsHandled() { mHandled = true; }
    166 
    167  EditActionResult& operator|=(const EditActionResult& aOther) {
    168    mCanceled |= aOther.mCanceled;
    169    mHandled |= aOther.mHandled;
    170    return *this;
    171  }
    172 
    173  static EditActionResult IgnoredResult() {
    174    return EditActionResult(false, false);
    175  }
    176  static EditActionResult HandledResult() {
    177    return EditActionResult(false, true);
    178  }
    179  static EditActionResult CanceledResult() {
    180    return EditActionResult(true, true);
    181  }
    182 
    183  EditActionResult(const EditActionResult&) = delete;
    184  EditActionResult& operator=(const EditActionResult&) = delete;
    185  EditActionResult(EditActionResult&&) = default;
    186  EditActionResult& operator=(EditActionResult&&) = default;
    187 
    188 protected:
    189  EditActionResult(bool aCanceled, bool aHandled)
    190      : mCanceled(aCanceled), mHandled(aHandled) {}
    191 
    192  EditActionResult() : mCanceled(false), mHandled(false) {}
    193 
    194  void UnmarkAsCanceled() { mCanceled = false; }
    195 
    196 private:
    197  bool mCanceled = false;
    198  bool mHandled = false;
    199 };
    200 
    201 /***************************************************************************
    202 * CreateNodeResultBase is a simple class for CreateSomething() methods
    203 * which want to return new node.
    204 */
    205 template <typename NodeType>
    206 class MOZ_STACK_CLASS CreateNodeResultBase final : public CaretPoint {
    207  using SelfType = CreateNodeResultBase<NodeType>;
    208 
    209 public:
    210  bool Handled() const { return mNode; }
    211  NodeType* GetNewNode() const { return mNode; }
    212  RefPtr<NodeType> UnwrapNewNode() { return std::move(mNode); }
    213 
    214  CreateNodeResultBase() = delete;
    215  explicit CreateNodeResultBase(NodeType& aNode) : mNode(&aNode) {}
    216  explicit CreateNodeResultBase(NodeType& aNode,
    217                                const EditorDOMPoint& aCandidateCaretPoint)
    218      : CaretPoint(aCandidateCaretPoint), mNode(&aNode) {}
    219  explicit CreateNodeResultBase(NodeType& aNode,
    220                                EditorDOMPoint&& aCandidateCaretPoint)
    221      : CaretPoint(std::move(aCandidateCaretPoint)), mNode(&aNode) {}
    222 
    223  template <typename NT>
    224  explicit CreateNodeResultBase(RefPtr<NT>&& aNode)
    225      : mNode(std::forward<RefPtr<NT>>(aNode)) {}
    226  template <typename NT>
    227  explicit CreateNodeResultBase(RefPtr<NT>&& aNode,
    228                                const EditorDOMPoint& aCandidateCaretPoint)
    229      : CaretPoint(aCandidateCaretPoint),
    230        mNode(std::forward<RefPtr<NT>>(aNode)) {
    231    MOZ_ASSERT(mNode);
    232  }
    233  template <typename NT>
    234  explicit CreateNodeResultBase(RefPtr<NT>&& aNode,
    235                                EditorDOMPoint&& aCandidateCaretPoint)
    236      : CaretPoint(std::move(aCandidateCaretPoint)),
    237        mNode(std::forward<RefPtr<NT>>(aNode)) {
    238    MOZ_ASSERT(mNode);
    239  }
    240 
    241  [[nodiscard]] static SelfType NotHandled() {
    242    return SelfType(EditorDOMPoint());
    243  }
    244  [[nodiscard]] static SelfType NotHandled(
    245      const EditorDOMPoint& aPointToPutCaret) {
    246    SelfType result(aPointToPutCaret);
    247    return result;
    248  }
    249  [[nodiscard]] static SelfType NotHandled(EditorDOMPoint&& aPointToPutCaret) {
    250    SelfType result(std::move(aPointToPutCaret));
    251    return result;
    252  }
    253 
    254 #ifdef DEBUG
    255  ~CreateNodeResultBase() {
    256    MOZ_ASSERT(!HasCaretPointSuggestion() || CaretPointHandled());
    257  }
    258 #endif
    259 
    260  CreateNodeResultBase(const SelfType& aOther) = delete;
    261  SelfType& operator=(const SelfType& aOther) = delete;
    262  CreateNodeResultBase(SelfType&& aOther) = default;
    263  SelfType& operator=(SelfType&& aOther) = default;
    264 
    265 private:
    266  explicit CreateNodeResultBase(const EditorDOMPoint& aCandidateCaretPoint)
    267      : CaretPoint(aCandidateCaretPoint) {}
    268  explicit CreateNodeResultBase(EditorDOMPoint&& aCandidateCaretPoint)
    269      : CaretPoint(std::move(aCandidateCaretPoint)) {}
    270 
    271  RefPtr<NodeType> mNode;
    272 };
    273 
    274 /**
    275 * This is a result of inserting text.  If the text inserted as a part of
    276 * composition, this does not return CaretPoint.  Otherwise, must return
    277 * CaretPoint which is typically same as end of inserted text.
    278 */
    279 class MOZ_STACK_CLASS InsertTextResult final : public CaretPoint {
    280 public:
    281  InsertTextResult() : CaretPoint(EditorDOMPoint()) {}
    282  template <typename EditorDOMPointType>
    283  explicit InsertTextResult(const EditorDOMPointType& aEndOfInsertedText)
    284      : CaretPoint(EditorDOMPoint()),
    285        mEndOfInsertedText(aEndOfInsertedText.template To<EditorDOMPoint>()) {}
    286  explicit InsertTextResult(EditorDOMPoint&& aEndOfInsertedText)
    287      : CaretPoint(EditorDOMPoint()),
    288        mEndOfInsertedText(std::move(aEndOfInsertedText)) {}
    289  template <typename PT, typename CT>
    290  InsertTextResult(EditorDOMPoint&& aEndOfInsertedText,
    291                   const EditorDOMPointBase<PT, CT>& aCaretPoint)
    292      : CaretPoint(aCaretPoint.template To<EditorDOMPoint>()),
    293        mEndOfInsertedText(std::move(aEndOfInsertedText)) {}
    294  InsertTextResult(EditorDOMPoint&& aEndOfInsertedText,
    295                   CaretPoint&& aCaretPoint)
    296      : CaretPoint(std::move(aCaretPoint)),
    297        mEndOfInsertedText(std::move(aEndOfInsertedText)) {
    298    UnmarkAsHandledCaretPoint();
    299  }
    300  InsertTextResult(InsertTextResult&& aOther, EditorDOMPoint&& aCaretPoint)
    301      : CaretPoint(std::move(aCaretPoint)),
    302        mEndOfInsertedText(std::move(aOther.mEndOfInsertedText)) {}
    303 
    304  [[nodiscard]] bool Handled() const { return mEndOfInsertedText.IsSet(); }
    305  const EditorDOMPoint& EndOfInsertedTextRef() const {
    306    return mEndOfInsertedText;
    307  }
    308 
    309 private:
    310  EditorDOMPoint mEndOfInsertedText;
    311 };
    312 
    313 /***************************************************************************
    314 * stack based helper class for calling EditorBase::EndTransaction() after
    315 * EditorBase::BeginTransaction().  This shouldn't be used in editor classes
    316 * or helper classes while an edit action is being handled.  Use
    317 * AutoTransactionBatch in such cases since it uses non-virtual internal
    318 * methods.
    319 ***************************************************************************/
    320 class MOZ_RAII AutoTransactionBatchExternal final {
    321 public:
    322  MOZ_CAN_RUN_SCRIPT explicit AutoTransactionBatchExternal(
    323      EditorBase& aEditorBase)
    324      : mEditorBase(aEditorBase) {
    325    MOZ_KnownLive(mEditorBase).BeginTransaction();
    326  }
    327 
    328  MOZ_CAN_RUN_SCRIPT ~AutoTransactionBatchExternal() {
    329    MOZ_KnownLive(mEditorBase).EndTransaction();
    330  }
    331 
    332 private:
    333  EditorBase& mEditorBase;
    334 };
    335 
    336 /******************************************************************************
    337 * AutoSelectionRangeArray stores all ranges in `aSelection`.
    338 * Note that modifying the ranges means modifing the selection ranges.
    339 *****************************************************************************/
    340 class MOZ_STACK_CLASS AutoSelectionRangeArray final {
    341 public:
    342  explicit AutoSelectionRangeArray(dom::Selection& aSelection) {
    343    for (const uint32_t i : IntegerRange(aSelection.RangeCount())) {
    344      MOZ_ASSERT(aSelection.GetRangeAt(i));
    345      mRanges.AppendElement(*aSelection.GetRangeAt(i));
    346    }
    347  }
    348 
    349  AutoTArray<mozilla::OwningNonNull<nsRange>, 8> mRanges;
    350 };
    351 
    352 /******************************************************************************
    353 * AutoTrackDataTransferForPaste keeps track of whether the paste event handler
    354 * in JS has modified the clipboard.
    355 *****************************************************************************/
    356 class MOZ_STACK_CLASS AutoTrackDataTransferForPaste {
    357 public:
    358  MOZ_CAN_RUN_SCRIPT AutoTrackDataTransferForPaste(
    359      const EditorBase& aEditorBase,
    360      RefPtr<dom::DataTransfer>& aDataTransferForPaste)
    361      : mEditorBase(aEditorBase),
    362        mDataTransferForPaste(aDataTransferForPaste.get_address()) {
    363    mEditorBase.GetDocument()->ClearClipboardCopyTriggered();
    364  }
    365 
    366  ~AutoTrackDataTransferForPaste() { FlushAndStopTracking(); }
    367 
    368 private:
    369  void FlushAndStopTracking() {
    370    if (!mDataTransferForPaste ||
    371        !mEditorBase.GetDocument()->IsClipboardCopyTriggered()) {
    372      return;
    373    }
    374    // The paste event copied new data to the clipboard, so we need to use
    375    // that data to paste into the DOM element below.
    376    if (*mDataTransferForPaste) {
    377      (*mDataTransferForPaste)->ClearForPaste();
    378    }
    379    // Just null this out so this data won't be used and we will get it directly
    380    // from the clipboard in the future.
    381    *mDataTransferForPaste = nullptr;
    382    mDataTransferForPaste = nullptr;
    383  }
    384 
    385  MOZ_KNOWN_LIVE const EditorBase& mEditorBase;
    386  RefPtr<dom::DataTransfer>* mDataTransferForPaste;
    387 };
    388 
    389 class EditorUtils final {
    390 public:
    391  using EditorType = EditorBase::EditorType;
    392  using Selection = dom::Selection;
    393 
    394  /**
    395   * IsDescendantOf() checks if aNode is a child or a descendant of aParent.
    396   * aOutPoint is set to the child of aParent.
    397   *
    398   * @return            true if aNode is a child or a descendant of aParent.
    399   */
    400  static bool IsDescendantOf(const nsINode& aNode, const nsINode& aParent,
    401                             EditorRawDOMPoint* aOutPoint = nullptr);
    402  static bool IsDescendantOf(const nsINode& aNode, const nsINode& aParent,
    403                             EditorDOMPoint* aOutPoint);
    404 
    405  /**
    406   * Returns true if aContent is a <br> element and it's marked as padding for
    407   * empty editor.
    408   */
    409  static bool IsPaddingBRElementForEmptyEditor(const nsIContent& aContent) {
    410    const dom::HTMLBRElement* brElement =
    411        dom::HTMLBRElement::FromNode(&aContent);
    412    return brElement && brElement->IsPaddingForEmptyEditor();
    413  }
    414 
    415  /**
    416   * Returns true if aContent is a <br> element and it's marked as padding for
    417   * empty last line.
    418   */
    419  static bool IsPaddingBRElementForEmptyLastLine(const nsIContent& aContent) {
    420    const dom::HTMLBRElement* brElement =
    421        dom::HTMLBRElement::FromNode(&aContent);
    422    return brElement && brElement->IsPaddingForEmptyLastLine();
    423  }
    424 
    425  /**
    426   * IsEditableContent() returns true if aContent's data or children is ediable
    427   * for the given editor type.  Be aware, returning true does NOT mean the
    428   * node can be removed from its parent node, and returning false does NOT
    429   * mean the node cannot be removed from the parent node.
    430   * XXX May be the anonymous nodes in TextEditor not editable?  If it's not
    431   *     so, we can get rid of aEditorType.
    432   */
    433  static bool IsEditableContent(const nsIContent& aContent,
    434                                EditorType aEditorType) {
    435    if (aEditorType == EditorType::HTML &&
    436        (!aContent.IsEditable() || !aContent.IsInComposedDoc())) {
    437      // FIXME(emilio): Why only for HTML editors? All content from the root
    438      // content in text editors is also editable, so afaict we can remove the
    439      // special-case.
    440      return false;
    441    }
    442    return IsElementOrText(aContent);
    443  }
    444 
    445  /**
    446   * Returns true if aContent is a usual element node (not padding <br> element
    447   * for empty editor) or a text node.  In other words, returns true if
    448   * aContent is a usual element node or visible data node.
    449   */
    450  static bool IsElementOrText(const nsIContent& aContent) {
    451    if (aContent.IsText()) {
    452      return true;
    453    }
    454    return aContent.IsElement() && !IsPaddingBRElementForEmptyEditor(aContent);
    455  }
    456 
    457  /**
    458   * Get the two longhands that make up computed white-space style of aContent.
    459   */
    460  static Maybe<std::pair<StyleWhiteSpaceCollapse, StyleTextWrapMode>>
    461  GetComputedWhiteSpaceStyles(const nsIContent& aContent);
    462 
    463  /**
    464   * IsWhiteSpacePreformatted() checks the style info for the node for the
    465   * preformatted text style.  This does NOT flush layout.
    466   */
    467  static bool IsWhiteSpacePreformatted(const nsIContent& aContent);
    468 
    469  /**
    470   * IsNewLinePreformatted() checks whether the linefeed characters are
    471   * preformatted or white-spaces.  This does NOT flush layout.
    472   * Be aware that even if this returns false, the linefeed characters may be
    473   * rendered as non-collapsible white-spaces.  Therefore, if you want to check
    474   * whether linefeeds are collapsible or not, you should refer the result of
    475   * IsWhiteSpacePreformatted().
    476   */
    477  static bool IsNewLinePreformatted(const nsIContent& aContent);
    478 
    479  /**
    480   * IsOnlyNewLinePreformatted() checks whether the linefeed characters are
    481   * preformated but white-spaces are collapsed, or otherwise.  I.e., this
    482   * returns true only when `white-space-collapse:pre-line`.
    483   */
    484  static bool IsOnlyNewLinePreformatted(const nsIContent& aContent);
    485 
    486  static nsStaticAtom* GetTagNameAtom(const nsAString& aTagName) {
    487    if (aTagName.IsEmpty()) {
    488      return nullptr;
    489    }
    490    nsAutoString lowerTagName;
    491    nsContentUtils::ASCIIToLower(aTagName, lowerTagName);
    492    return NS_GetStaticAtom(lowerTagName);
    493  }
    494 
    495  static nsStaticAtom* GetAttributeAtom(const nsAString& aAttribute) {
    496    if (aAttribute.IsEmpty()) {
    497      return nullptr;  // Don't use nsGkAtoms::_empty for attribute.
    498    }
    499    return NS_GetStaticAtom(aAttribute);
    500  }
    501 
    502  /**
    503   * Helper method for deletion.  When this returns true, Selection will be
    504   * computed with nsFrameSelection that also requires flushed layout
    505   * information.
    506   */
    507  template <typename SelectionOrAutoClonedRangeArray>
    508  static bool IsFrameSelectionRequiredToExtendSelection(
    509      nsIEditor::EDirection aDirectionAndAmount,
    510      SelectionOrAutoClonedRangeArray& aSelectionOrAutoClonedRangeArray) {
    511    switch (aDirectionAndAmount) {
    512      case nsIEditor::eNextWord:
    513      case nsIEditor::ePreviousWord:
    514      case nsIEditor::eToBeginningOfLine:
    515      case nsIEditor::eToEndOfLine:
    516        return true;
    517      case nsIEditor::ePrevious:
    518      case nsIEditor::eNext:
    519        return aSelectionOrAutoClonedRangeArray.IsCollapsed();
    520      default:
    521        return false;
    522    }
    523  }
    524 
    525  /**
    526   * Create an nsITransferable instance which has kTextMime and
    527   * kMozTextInternal flavors.
    528   */
    529  static Result<nsCOMPtr<nsITransferable>, nsresult>
    530  CreateTransferableForPlainText(const dom::Document& aDocument);
    531 };
    532 
    533 }  // namespace mozilla
    534 
    535 #endif  // #ifndef mozilla_EditorUtils_h