tor-browser

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

InsertNodeTransaction.cpp (7507B)


      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 "InsertNodeTransaction.h"
      7 
      8 #include "EditorBase.h"           // for EditorBase
      9 #include "EditorDOMAPIWrapper.h"  // For AutoNodeAPIWrapper
     10 #include "EditorDOMPoint.h"       // for EditorDOMPoint
     11 #include "HTMLEditor.h"           // for HTMLEditor
     12 #include "TextEditor.h"           // for TextEditor
     13 
     14 #include "mozilla/Logging.h"
     15 #include "mozilla/ToString.h"
     16 
     17 #include "nsAString.h"
     18 #include "nsDebug.h"          // for NS_WARNING, etc.
     19 #include "nsError.h"          // for NS_ERROR_NULL_POINTER, etc.
     20 #include "nsIContent.h"       // for nsIContent
     21 #include "nsReadableUtils.h"  // for ToNewCString
     22 #include "nsString.h"         // for nsString
     23 
     24 namespace mozilla {
     25 
     26 using namespace dom;
     27 
     28 template already_AddRefed<InsertNodeTransaction> InsertNodeTransaction::Create(
     29    EditorBase& aEditorBase, nsIContent& aContentToInsert,
     30    const EditorDOMPoint& aPointToInsert);
     31 template already_AddRefed<InsertNodeTransaction> InsertNodeTransaction::Create(
     32    EditorBase& aEditorBase, nsIContent& aContentToInsert,
     33    const EditorRawDOMPoint& aPointToInsert);
     34 
     35 // static
     36 template <typename PT, typename CT>
     37 already_AddRefed<InsertNodeTransaction> InsertNodeTransaction::Create(
     38    EditorBase& aEditorBase, nsIContent& aContentToInsert,
     39    const EditorDOMPointBase<PT, CT>& aPointToInsert) {
     40  RefPtr<InsertNodeTransaction> transaction =
     41      new InsertNodeTransaction(aEditorBase, aContentToInsert, aPointToInsert);
     42  return transaction.forget();
     43 }
     44 
     45 template <typename PT, typename CT>
     46 InsertNodeTransaction::InsertNodeTransaction(
     47    EditorBase& aEditorBase, nsIContent& aContentToInsert,
     48    const EditorDOMPointBase<PT, CT>& aPointToInsert)
     49    : mContentToInsert(&aContentToInsert),
     50      mPointToInsert(aPointToInsert.template To<EditorDOMPoint>()),
     51      mEditorBase(&aEditorBase) {
     52  MOZ_ASSERT(mPointToInsert.IsSetAndValid());
     53  // Ensure mPointToInsert stores child at offset.
     54  (void)mPointToInsert.GetChild();
     55 }
     56 
     57 std::ostream& operator<<(std::ostream& aStream,
     58                         const InsertNodeTransaction& aTransaction) {
     59  aStream << "{ mContentToInsert=" << aTransaction.mContentToInsert.get();
     60  if (aTransaction.mContentToInsert) {
     61    if (aTransaction.mContentToInsert->IsText()) {
     62      nsAutoString data;
     63      aTransaction.mContentToInsert->AsText()->GetData(data);
     64      aStream << " (#text \"" << NS_ConvertUTF16toUTF8(data).get() << "\")";
     65    } else {
     66      aStream << " (" << *aTransaction.mContentToInsert << ")";
     67    }
     68  }
     69  aStream << ", mPointToInsert=" << aTransaction.mPointToInsert
     70          << ", mEditorBase=" << aTransaction.mEditorBase.get() << " }";
     71  return aStream;
     72 }
     73 
     74 NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertNodeTransaction, EditTransactionBase,
     75                                   mEditorBase, mContentToInsert,
     76                                   mPointToInsert)
     77 
     78 NS_IMPL_ADDREF_INHERITED(InsertNodeTransaction, EditTransactionBase)
     79 NS_IMPL_RELEASE_INHERITED(InsertNodeTransaction, EditTransactionBase)
     80 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(InsertNodeTransaction)
     81 NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
     82 
     83 NS_IMETHODIMP InsertNodeTransaction::DoTransaction() {
     84  MOZ_LOG(GetLogModule(), LogLevel::Info,
     85          ("%p InsertNodeTransaction::%s this=%s", this, __FUNCTION__,
     86           ToString(*this).c_str()));
     87 
     88  if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mContentToInsert) ||
     89      NS_WARN_IF(!mPointToInsert.IsSet())) {
     90    return NS_ERROR_NOT_AVAILABLE;
     91  }
     92 
     93  MOZ_ASSERT_IF(mEditorBase->IsTextEditor(), !mContentToInsert->IsText());
     94 
     95  if (!mPointToInsert.IsSetAndValid()) {
     96    // It seems that DOM tree has been changed after first DoTransaction()
     97    // and current RedoTranaction() call.
     98    if (mPointToInsert.GetChild()) {
     99      EditorDOMPoint newPointToInsert(mPointToInsert.GetChild());
    100      if (!newPointToInsert.IsSet()) {
    101        // The insertion point has been removed from the DOM tree.
    102        // In this case, we should append the node to the container instead.
    103        newPointToInsert.SetToEndOf(mPointToInsert.GetContainer());
    104        if (NS_WARN_IF(!newPointToInsert.IsSet())) {
    105          return NS_ERROR_FAILURE;
    106        }
    107      }
    108      mPointToInsert = newPointToInsert;
    109    } else {
    110      mPointToInsert.SetToEndOf(mPointToInsert.GetContainer());
    111      if (NS_WARN_IF(!mPointToInsert.IsSet())) {
    112        return NS_ERROR_FAILURE;
    113      }
    114    }
    115  }
    116 
    117  const OwningNonNull<EditorBase> editorBase = *mEditorBase;
    118  const OwningNonNull<nsIContent> contentToInsert = *mContentToInsert;
    119  const OwningNonNull<nsINode> container = *mPointToInsert.GetContainer();
    120  const nsCOMPtr<nsIContent> refChild = mPointToInsert.GetChild();
    121  if (contentToInsert->IsElement()) {
    122    nsresult rv = editorBase->MarkElementDirty(
    123        MOZ_KnownLive(*contentToInsert->AsElement()));
    124    if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
    125      return EditorBase::ToGenericNSResult(rv);
    126    }
    127    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    128                         "EditorBase::MarkElementDirty() failed, but ignored");
    129  }
    130 
    131  AutoNodeAPIWrapper nodeWrapper(editorBase, container);
    132  nsresult rv = nodeWrapper.InsertBefore(contentToInsert, refChild);
    133  if (NS_FAILED(rv)) {
    134    NS_WARNING("AutoNodeAPIWrapper::InsertBefore() failed");
    135    return rv;
    136  }
    137  NS_WARNING_ASSERTION(nodeWrapper.IsExpectedResult(),
    138                       "Inserting a node caused other mutations, but ignored");
    139  return NS_OK;
    140 }
    141 
    142 NS_IMETHODIMP InsertNodeTransaction::UndoTransaction() {
    143  MOZ_LOG(GetLogModule(), LogLevel::Info,
    144          ("%p InsertNodeTransaction::%s this=%s", this, __FUNCTION__,
    145           ToString(*this).c_str()));
    146 
    147  if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mContentToInsert) ||
    148      NS_WARN_IF(!mPointToInsert.IsSet())) {
    149    return NS_ERROR_NOT_INITIALIZED;
    150  }
    151  // XXX If the inserted node has been moved to different container node or
    152  //     just removed from the DOM tree, this always fails.
    153  const OwningNonNull<EditorBase> editorBase = *mEditorBase;
    154  const OwningNonNull<nsINode> container = *mPointToInsert.GetContainer();
    155  const OwningNonNull<nsIContent> contentToInsert = *mContentToInsert;
    156  AutoNodeAPIWrapper nodeWrapper(editorBase, container);
    157  nsresult rv = nodeWrapper.RemoveChild(contentToInsert);
    158  if (NS_FAILED(rv)) {
    159    NS_WARNING("AutoNodeAPIWrapper::RemoveChild() failed");
    160    return rv;
    161  }
    162  NS_WARNING_ASSERTION(nodeWrapper.IsExpectedResult(),
    163                       "Removing a node caused other mutations, but ignored");
    164  return NS_OK;
    165 }
    166 
    167 NS_IMETHODIMP InsertNodeTransaction::RedoTransaction() {
    168  MOZ_LOG(GetLogModule(), LogLevel::Info,
    169          ("%p InsertNodeTransaction::%s this=%s", this, __FUNCTION__,
    170           ToString(*this).c_str()));
    171  nsresult rv = DoTransaction();
    172  if (MOZ_UNLIKELY(NS_FAILED(rv))) {
    173    NS_WARNING("InsertNodeTransaction::RedoTransaction() failed");
    174    return rv;
    175  }
    176 
    177  if (!mEditorBase->AllowsTransactionsToChangeSelection()) {
    178    return NS_OK;
    179  }
    180 
    181  OwningNonNull<EditorBase> editorBase(*mEditorBase);
    182  rv = editorBase->CollapseSelectionTo(
    183      SuggestPointToPutCaret<EditorRawDOMPoint>());
    184  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    185                       "EditorBase::CollapseSelectionTo() failed, but ignored");
    186  return NS_OK;
    187 }
    188 
    189 }  // namespace mozilla