tor-browser

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

DeleteNodeTransaction.cpp (6789B)


      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 #include "DeleteNodeTransaction.h"
      7 
      8 #include "EditorBase.h"
      9 #include "EditorDOMAPIWrapper.h"
     10 #include "EditorDOMPoint.h"
     11 #include "HTMLEditUtils.h"
     12 #include "SelectionState.h"  // RangeUpdater
     13 #include "TextEditor.h"
     14 
     15 #include "mozilla/Logging.h"
     16 #include "mozilla/ToString.h"
     17 
     18 #include "nsDebug.h"
     19 #include "nsError.h"
     20 #include "nsAString.h"
     21 
     22 namespace mozilla {
     23 
     24 // static
     25 already_AddRefed<DeleteNodeTransaction> DeleteNodeTransaction::MaybeCreate(
     26    EditorBase& aEditorBase, nsIContent& aContentToDelete) {
     27  RefPtr<DeleteNodeTransaction> transaction =
     28      new DeleteNodeTransaction(aEditorBase, aContentToDelete);
     29  if (NS_WARN_IF(!transaction->CanDoIt())) {
     30    return nullptr;
     31  }
     32  return transaction.forget();
     33 }
     34 
     35 DeleteNodeTransaction::DeleteNodeTransaction(EditorBase& aEditorBase,
     36                                             nsIContent& aContentToDelete)
     37    : DeleteContentTransactionBase(aEditorBase),
     38      mContentToDelete(&aContentToDelete),
     39      mParentNode(aContentToDelete.GetParentNode()) {
     40  MOZ_DIAGNOSTIC_ASSERT_IF(
     41      aEditorBase.IsHTMLEditor(),
     42      HTMLEditUtils::IsRemovableNode(aContentToDelete) ||
     43          // It's okay to delete text node if it's added by `HTMLEditor` since
     44          // remaining it may be noisy for the users.
     45          (aContentToDelete.IsText() &&
     46           aContentToDelete.HasFlag(NS_MAYBE_MODIFIED_FREQUENTLY)));
     47  NS_ASSERTION(
     48      !aEditorBase.IsHTMLEditor() ||
     49          HTMLEditUtils::IsRemovableNode(aContentToDelete),
     50      "Deleting non-editable text node, please write a test for this!!");
     51 }
     52 
     53 std::ostream& operator<<(std::ostream& aStream,
     54                         const DeleteNodeTransaction& aTransaction) {
     55  aStream << "{ mContentToDelete=" << aTransaction.mContentToDelete.get();
     56  if (aTransaction.mContentToDelete) {
     57    aStream << " (" << *aTransaction.mContentToDelete << ")";
     58  }
     59  aStream << ", mParentNode=" << aTransaction.mParentNode.get();
     60  if (aTransaction.mParentNode) {
     61    aStream << " (" << *aTransaction.mParentNode << ")";
     62  }
     63  aStream << ", mRefContent=" << aTransaction.mRefContent.get();
     64  if (aTransaction.mRefContent) {
     65    aStream << " (" << *aTransaction.mRefContent << ")";
     66  }
     67  aStream << ", mEditorBase=" << aTransaction.mEditorBase.get() << " }";
     68  return aStream;
     69 }
     70 
     71 NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteNodeTransaction,
     72                                   DeleteContentTransactionBase,
     73                                   mContentToDelete, mParentNode, mRefContent)
     74 
     75 NS_IMPL_ADDREF_INHERITED(DeleteNodeTransaction, DeleteContentTransactionBase)
     76 NS_IMPL_RELEASE_INHERITED(DeleteNodeTransaction, DeleteContentTransactionBase)
     77 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteNodeTransaction)
     78 NS_INTERFACE_MAP_END_INHERITING(DeleteContentTransactionBase)
     79 
     80 bool DeleteNodeTransaction::CanDoIt() const {
     81  if (NS_WARN_IF(!mContentToDelete) || NS_WARN_IF(!mEditorBase) ||
     82      !mParentNode) {
     83    return false;
     84  }
     85  return mEditorBase->IsTextEditor() ||
     86         HTMLEditUtils::IsSimplyEditableNode(*mParentNode);
     87 }
     88 
     89 NS_IMETHODIMP DeleteNodeTransaction::DoTransaction() {
     90  MOZ_LOG(GetLogModule(), LogLevel::Info,
     91          ("%p DeleteNodeTransaction::%s this=%s", this, __FUNCTION__,
     92           ToString(*this).c_str()));
     93 
     94  if (NS_WARN_IF(!CanDoIt())) {
     95    return NS_OK;
     96  }
     97 
     98  MOZ_ASSERT_IF(mEditorBase->IsTextEditor(), !mContentToDelete->IsText());
     99 
    100  // Remember which child mContentToDelete was (by remembering which child was
    101  // next).  Note that mRefContent can be nullptr.
    102  mRefContent = mContentToDelete->GetNextSibling();
    103 
    104  // give range updater a chance.  SelAdjDeleteNode() needs to be called
    105  // *before* we do the action, unlike some of the other RangeItem update
    106  // methods.
    107  const OwningNonNull<EditorBase> editorBase = *mEditorBase;
    108  editorBase->RangeUpdaterRef().SelAdjDeleteNode(*mContentToDelete);
    109 
    110  const OwningNonNull<nsINode> parentNode = *mParentNode;
    111  const OwningNonNull<nsIContent> contentToDelete = *mContentToDelete;
    112  AutoNodeAPIWrapper nodeWrapper(editorBase, parentNode);
    113  nsresult rv = nodeWrapper.RemoveChild(contentToDelete);
    114  if (NS_FAILED(rv)) {
    115    NS_WARNING("AutoNodeAPIWrapper::RemoveChild() failed");
    116    return rv;
    117  }
    118  NS_WARNING_ASSERTION(
    119      nodeWrapper.IsExpectedResult(),
    120      "Removing a content node caused other mutations, but ignored");
    121  return NS_OK;
    122 }
    123 
    124 EditorDOMPoint DeleteNodeTransaction::SuggestPointToPutCaret() const {
    125  return EditorDOMPoint();
    126 }
    127 
    128 NS_IMETHODIMP DeleteNodeTransaction::UndoTransaction() {
    129  MOZ_LOG(GetLogModule(), LogLevel::Info,
    130          ("%p DeleteNodeTransaction::%s this=%s", this, __FUNCTION__,
    131           ToString(*this).c_str()));
    132 
    133  if (NS_WARN_IF(!CanDoIt())) {
    134    // This is a legal state, the transaction is a no-op.
    135    return NS_OK;
    136  }
    137  const OwningNonNull<EditorBase> editorBase = *mEditorBase;
    138  const OwningNonNull<nsINode> parentNode = *mParentNode;
    139  const OwningNonNull<nsIContent> contentToDelete = *mContentToDelete;
    140  const nsCOMPtr<nsIContent> refContent = mRefContent;
    141  // XXX Perhaps, we should check `refContent` is a child of `parentNode`,
    142  //     and if it's not, we should stop undoing or something.
    143  AutoNodeAPIWrapper nodeWrapper(editorBase, parentNode);
    144  nsresult rv = nodeWrapper.InsertBefore(contentToDelete, refContent);
    145  if (NS_FAILED(rv)) {
    146    NS_WARNING("AutoNodeAPIWrapper::InsertBefore() failed");
    147    return rv;
    148  }
    149  NS_WARNING_ASSERTION(nodeWrapper.IsExpectedResult(),
    150                       "Inserting a node caused other mutations, but ignored");
    151  return NS_OK;
    152 }
    153 
    154 NS_IMETHODIMP DeleteNodeTransaction::RedoTransaction() {
    155  MOZ_LOG(GetLogModule(), LogLevel::Info,
    156          ("%p DeleteNodeTransaction::%s this=%s", this, __FUNCTION__,
    157           ToString(*this).c_str()));
    158 
    159  if (NS_WARN_IF(!CanDoIt())) {
    160    // This is a legal state, the transaction is a no-op.
    161    return NS_OK;
    162  }
    163 
    164  const OwningNonNull<EditorBase> editorBase = *mEditorBase;
    165  editorBase->RangeUpdaterRef().SelAdjDeleteNode(*mContentToDelete);
    166 
    167  const OwningNonNull<nsINode> parentNode = *mParentNode;
    168  const OwningNonNull<nsIContent> contentToDelete = *mContentToDelete;
    169  AutoNodeAPIWrapper nodeWrapper(editorBase, parentNode);
    170  nsresult rv = nodeWrapper.RemoveChild(contentToDelete);
    171  if (NS_FAILED(rv)) {
    172    NS_WARNING("AutoNodeAPIWrapper::RemoveChild() failed");
    173    return rv;
    174  }
    175  NS_WARNING_ASSERTION(
    176      nodeWrapper.IsExpectedResult(),
    177      "Removing a content node caused other mutations, but ignored");
    178  return NS_OK;
    179 }
    180 
    181 }  // namespace mozilla