Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  MoveSiblingsTransaction.cpp

  Sprache: C
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "MoveNodeTransaction.h"

#include "EditorBase.h"           // for EditorBase
#include "EditorDOMAPIWrapper.h"  // for AutoNodeAPIWrapper
#include "EditorDOMPoint.h"       // for EditorDOMPoint
#include "HTMLEditor.h"           // for HTMLEditor
#include "HTMLEditUtils.h"        // for HTMLEditUtils

#include "mozilla/Likely.h"
#include "mozilla/Logging.h"
#include "mozilla/ToString.h"

#include "nsDebug.h"     // for NS_WARNING, etc.
#include "nsError.h"     // for NS_ERROR_NULL_POINTER, etc.
#include "nsIContent.h"  // for nsIContent
#include "nsString.h"    // for nsString

namespace mozilla {

using namespace dom;

template already_AddRefed<MoveSiblingsTransaction>
MoveSiblingsTransaction::MaybeCreate(HTMLEditor& aHTMLEditor,
                                     nsIContent& aFirstContentToMove,
                                     nsIContent& aLastContentToMove,
                                     const EditorDOMPoint& aPointToInsert);
template already_AddRefed<MoveSiblingsTransaction>
MoveSiblingsTransaction::MaybeCreate(HTMLEditor& aHTMLEditor,
                                     nsIContent& aFirstContentToMove,
                                     nsIContent& aLastContentToMove,
                                     const EditorRawDOMPoint& aPointToInsert);

// static
template <typename PT, typename CT>
already_AddRefed<MoveSiblingsTransaction> MoveSiblingsTransaction::MaybeCreate(
    HTMLEditor& aHTMLEditor, nsIContent& aFirstContentToMove,
    nsIContent& aLastContentToMove,
    const EditorDOMPointBase<PT, CT>& aPointToInsert) {
  if (NS_WARN_IF(!aFirstContentToMove.GetParentNode()) ||
      NS_WARN_IF(&aFirstContentToMove == &aLastContentToMove) ||
      NS_WARN_IF(aFirstContentToMove.GetParentNode() !=
                 aLastContentToMove.GetParentNode()) ||
      NS_WARN_IF(!aPointToInsert.IsSet())) {
    return nullptr;
  }

  // The destination should be editable, but it may be in an orphan node
  // or sub-tree to reduce number of DOM mutation events.  In such case,
  // we're okay to move a node into the non-editable content because we
  // can assume that the caller will insert it into an editable element.
  if (NS_WARN_IF(aPointToInsert.IsInComposedDoc() &&
                 !HTMLEditUtils::IsSimplyEditableNode(
                     *aPointToInsert.GetContainer()))) {
    return nullptr;
  }
  const uint32_t numberOfSiblings = [&]() -> uint32_t {
    uint32_t num = 1;
    for (nsIContent* content = aFirstContentToMove.GetNextSibling(); content;
         content = content->GetNextSibling()) {
      // TODO: We should not allow to move a node to improper container element.
      //       However, this is currently used to move invalid parent while
      //       processing the nodes.  Therefore, treating the case as error
      //       breaks a lot.
      if (NS_WARN_IF(content->IsInComposedDoc() &&
                     !HTMLEditUtils::IsRemovableNode(*content))) {
        return 0;
      }
      num++;
      if (content == &aLastContentToMove) {
        return num;
      }
    }
    return 0;
  }();
  if (NS_WARN_IF(!numberOfSiblings)) {
    return nullptr;
  }
  RefPtr<MoveSiblingsTransaction> transaction = new MoveSiblingsTransaction(
      aHTMLEditor, aFirstContentToMove, aLastContentToMove, numberOfSiblings,
      aPointToInsert);
  return transaction.forget();
}

template <typename PT, typename CT>
MoveSiblingsTransaction::MoveSiblingsTransaction(
    HTMLEditor& aHTMLEditor, nsIContent& aFirstContentToMove,
    nsIContent& aLastContentToMove, uint32_t aNumberOfSiblings,
    const EditorDOMPointBase<PT, CT>& aPointToInsert)
    : MoveNodeTransactionBase(aHTMLEditor, aLastContentToMove,
                              aPointToInsert.template To<EditorRawDOMPoint>()) {
  mSiblingsToMove.SetCapacity(aNumberOfSiblings);
  for (nsIContent* content = &aFirstContentToMove; content;
       content = content->GetNextSibling()) {
    mSiblingsToMove.AppendElement(*content);
    if (content == &aLastContentToMove) {
      break;
    }
  }
  MOZ_ASSERT(mSiblingsToMove.Length() == aNumberOfSiblings);
}

std::ostream& operator<<(std::ostream& aStream,
                         const MoveSiblingsTransaction& aTransaction) {
  auto DumpNodeDetails = [&](const nsINode* aNode) {
    if (aNode) {
      if (aNode->IsText()) {
        nsAutoString data;
        aNode->AsText()->GetData(data);
        aStream << " (#text \"" << NS_ConvertUTF16toUTF8(data).get() << "\")";
      } else {
        aStream << " (" << *aNode << ")";
      }
    }
  };
  aStream << "{ mSiblingsToMove[0]=" << aTransaction.mSiblingsToMove[0].get();
  DumpNodeDetails(aTransaction.mSiblingsToMove[0]);
  aStream << ", mSiblingsToMove[" << aTransaction.mSiblingsToMove.Length() - 1
          << aTransaction.mSiblingsToMove.LastElement().get();
  DumpNodeDetails(aTransaction.mSiblingsToMove.LastElement());
  aStream << ", mContainer=" << aTransaction.mContainer.get();
  DumpNodeDetails(aTransaction.mContainer);
  aStream << ", mReference=" << aTransaction.mReference.get();
  DumpNodeDetails(aTransaction.mReference);
  aStream << ", mOldContainer=" << aTransaction.mOldContainer.get();
  DumpNodeDetails(aTransaction.mOldContainer);
  aStream << ", mOldNextSibling=" << aTransaction.mOldNextSibling.get();
  DumpNodeDetails(aTransaction.mOldNextSibling);
  aStream << ", mHTMLEditor=" << aTransaction.mHTMLEditor.get() << " }";
  return aStream;
}

NS_IMPL_CYCLE_COLLECTION_INHERITED(MoveSiblingsTransaction, EditTransactionBase,
                                   mSiblingsToMove)

NS_IMPL_ADDREF_INHERITED(MoveSiblingsTransaction, EditTransactionBase)
NS_IMPL_RELEASE_INHERITED(MoveSiblingsTransaction, EditTransactionBase)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MoveSiblingsTransaction)
NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)

NS_IMETHODIMP MoveSiblingsTransaction::DoTransaction() {
  MOZ_LOG(GetLogModule(), LogLevel::Info,
          ("%p MoveSiblingsTransaction::%s this=%s", this, __FUNCTION__,
           ToString(*this).c_str()));
  mDone = true;
  return DoTransactionInternal();
}

nsresult MoveSiblingsTransaction::DoTransactionInternal() {
  MOZ_DIAGNOSTIC_ASSERT(mHTMLEditor);
  MOZ_DIAGNOSTIC_ASSERT(!mSiblingsToMove.IsEmpty());
  MOZ_DIAGNOSTIC_ASSERT(mContainer);
  MOZ_DIAGNOSTIC_ASSERT(mOldContainer);

  {
    const OwningNonNull<HTMLEditor> htmlEditor = *mHTMLEditor;
    const OwningNonNull<nsINode> newContainer = *mContainer;
    const nsCOMPtr<nsIContent> newNextSibling = mReference;
    const CopyableAutoTArray<OwningNonNull<nsIContent>, 64> siblingsToMove(
        mSiblingsToMove);
    AutoMoveNodeSelNotify notifier(
        htmlEditor->RangeUpdaterRef(),
        mReference ? EditorRawDOMPoint(mReference)
                   : EditorRawDOMPoint::AtEndOf(*newContainer));
    // First, remove all nodes from the DOM if they are removable.  Then,
    // IMEContentObserver can use cache to avoid to compute the start offset of
    // each deleting text.
    RemoveAllSiblingsToMove(htmlEditor, siblingsToMove, notifier);
    // Next, insert all removed nodes into the DOM.  Then, IMEContentObserver
    // can use cache to avoid to compute the start offset of each inserting
    // text.
    InsertAllSiblingsToMove(htmlEditor, siblingsToMove, newContainer,
                            newNextSibling, notifier);
  }
  return NS_WARN_IF(mHTMLEditor->Destroyed()) ? NS_ERROR_EDITOR_DESTROYED
                                              : NS_OK;
}

NS_IMETHODIMP MoveSiblingsTransaction::UndoTransaction() {
  MOZ_LOG(GetLogModule(), LogLevel::Info,
          ("%p MoveSiblingsTransaction::%s this=%s", this, __FUNCTION__,
           ToString(*this).c_str()));

  mDone = false;

  if (NS_WARN_IF(!mHTMLEditor) || NS_WARN_IF(mSiblingsToMove.IsEmpty()) ||
      NS_WARN_IF(!IsSiblingsToMoveValid()) || NS_WARN_IF(!mOldContainer)) {
    // Perhaps, nulled-out by the cycle collector.
    return NS_ERROR_FAILURE;
  }

  // If the original point has been changed, refer mOldNextSibling if it's
  // reasonable.  Otherwise, use end of the old container.
  if (mOldNextSibling && mOldContainer != mOldNextSibling->GetParentNode()) {
    // TODO: Check whether the new container is proper one for containing
    //       content in mSiblingsToMove.  However, there are few testcases so
    //       that we shouldn't change here without creating a lot of undo tests.
    if (mOldNextSibling->GetParentNode() &&
        (mOldNextSibling->IsInComposedDoc() ||
         !mOldContainer->IsInComposedDoc())) {
      mOldContainer = mOldNextSibling->GetParentNode();
    } else {
      mOldNextSibling = nullptr;  // end of mOldContainer
    }
  }

  if (MOZ_UNLIKELY(mOldContainer->IsInComposedDoc() &&
                   !HTMLEditUtils::IsSimplyEditableNode(*mOldContainer))) {
    NS_WARNING(
        "MoveSiblingsTransaction::UndoTransaction() couldn't move the "
        "content into the old container due to non-editable one");
    return NS_ERROR_FAILURE;
  }

  // And store the latest node which should be referred at redoing.
  mContainer = mSiblingsToMove.LastElement()->GetParentNode();
  mReference = mSiblingsToMove.LastElement()->GetNextSibling();

  {
    const OwningNonNull<HTMLEditor> htmlEditor = *mHTMLEditor;
    const OwningNonNull<nsINode> oldContainer = *mOldContainer;
    const nsCOMPtr<nsIContent> oldNextSibling = mOldNextSibling;
    const CopyableAutoTArray<OwningNonNull<nsIContent>, 64> siblingsToMove(
        mSiblingsToMove);
    AutoMoveNodeSelNotify notifier(
        htmlEditor->RangeUpdaterRef(),
        oldNextSibling ? EditorRawDOMPoint(oldNextSibling)
                       : EditorRawDOMPoint::AtEndOf(*oldContainer));
    // First, remove all nodes from the DOM if they are removable.  Then,
    // IMEContentObserver can use cache to avoid to compute the start offset of
    // each deleting text.
    RemoveAllSiblingsToMove(htmlEditor, siblingsToMove, notifier);
    // Next, insert all removed nodes into the DOM.  Then, IMEContentObserver
    // can use cache to avoid to compute the start offset of each inserting
    // text.
    InsertAllSiblingsToMove(htmlEditor, siblingsToMove, oldContainer,
                            oldNextSibling, notifier);
  }

  return NS_WARN_IF(mHTMLEditor->Destroyed()) ? NS_ERROR_EDITOR_DESTROYED
                                              : NS_OK;
}

NS_IMETHODIMP MoveSiblingsTransaction::RedoTransaction() {
  MOZ_LOG(GetLogModule(), LogLevel::Info,
          ("%p MoveSiblingsTransaction::%s this=%s", this, __FUNCTION__,
           ToString(*this).c_str()));

  mDone = true;

  if (NS_WARN_IF(!mHTMLEditor) || NS_WARN_IF(mSiblingsToMove.IsEmpty()) ||
      NS_WARN_IF(!IsSiblingsToMoveValid()) || NS_WARN_IF(!mContainer)) {
    // Perhaps, nulled-out by the cycle collector.
    return NS_ERROR_FAILURE;
  }

  // If the inserting point has been changed, refer mReference if it's
  // reasonable.  Otherwise, use end of the container.
  if (mReference && mContainer != mReference->GetParentNode()) {
    // TODO: Check whether the new container is proper one for containing
    //       mContentToMove.  However, there are few testcases so that we
    //       shouldn't change here without creating a lot of redo tests.
    if (mReference->GetParentNode() &&
        (mReference->IsInComposedDoc() || !mContainer->IsInComposedDoc())) {
      mContainer = mReference->GetParentNode();
    } else {
      mReference = nullptr;  // end of mContainer
    }
  }

  if (MOZ_UNLIKELY(mContainer->IsInComposedDoc() &&
                   !HTMLEditUtils::IsSimplyEditableNode(*mContainer))) {
    NS_WARNING(
        "MoveSiblingsTransaction::RedoTransaction() couldn't move the "
        "content into the new container due to non-editable one");
    return NS_ERROR_FAILURE;
  }

  // And store the latest node which should be back.
  mOldContainer = mSiblingsToMove.LastElement()->GetParentNode();
  mOldNextSibling = mSiblingsToMove.LastElement()->GetNextSibling();

  nsresult rv = DoTransactionInternal();
  if (NS_FAILED(rv)) {
    NS_WARNING("MoveSiblingsTransaction::DoTransactionInternal() failed");
    return rv;
  }
  return NS_OK;
}

nsIContent* MoveSiblingsTransaction::GetFirstMovedContent() const {
  nsINode* const expectedContainer = mDone ? mContainer : mOldContainer;
  for (const OwningNonNull<nsIContent>& content : mSiblingsToMove) {
    if (MOZ_LIKELY(content->GetParentNode() == expectedContainer)) {
      return content;
    }
  }
  return nullptr;
}

nsIContent* MoveSiblingsTransaction::GetLastMovedContent() const {
  nsINode* const expectedContainer = mDone ? mContainer : mOldContainer;
  for (const OwningNonNull<nsIContent>& content : Reversed(mSiblingsToMove)) {
    if (MOZ_LIKELY(content->GetParentNode() == expectedContainer)) {
      return content;
    }
  }
  return nullptr;
}

void MoveSiblingsTransaction::RemoveAllSiblingsToMove(
    HTMLEditor& aHTMLEditor,
    const nsTArray<OwningNonNull<nsIContent>>& aClonedSiblingsToMove,
    AutoMoveNodeSelNotify& aNotifier) const {
  // Be aware, if we're undoing or redoing, some aClonedSiblingsToMove may not
  // be the adjacent sibling of prev/next element in the array.  Therefore, we
  // may need to compute the index within the expensive path.

  // First, we need to make AutoMoveNodeSelNotify instances store all indices of
  // the moving content nodes.
  {
    for (const OwningNonNull<nsIContent>& contentToMove :
         aClonedSiblingsToMove) {
      if (contentToMove->IsInComposedDoc() &&
          !HTMLEditUtils::IsRemovableNode(contentToMove)) {
        continue;
      }
      aNotifier.AppendContentWhichWillBeMoved(contentToMove);
    }
  }
  // Then, remove all nodes unless not removable.
  for (const size_t i : IntegerRange(aNotifier.MovingContentCount())) {
    nsIContent* const contentToMove = aNotifier.GetContentAt(i);
    MOZ_ASSERT(contentToMove);
    AutoNodeAPIWrapper nodeWrapper(aHTMLEditor,
                                   // MOZ_KnownLive because it's guaranteed by
                                   // both notifier and aClonedSiblingsToMove.
                                   MOZ_KnownLive(*contentToMove));
    if (NS_FAILED(nodeWrapper.Remove())) {
      NS_WARNING("AutoNodeAPIWrapper::Remove() failed, but ignored");
    } else {
      NS_WARNING_ASSERTION(
          nodeWrapper.IsExpectedResult(),
          "Temporarily removing node caused other mutations, but ignored");
    }
  }
}

nsresult MoveSiblingsTransaction::InsertAllSiblingsToMove(
    HTMLEditor& aHTMLEditor,
    const nsTArray<OwningNonNull<nsIContent>>& aClonedSiblingsToMove,
    nsINode& aParentNode, nsIContent* aReferenceNode,
    AutoMoveNodeSelNotify& aNotifier) const {
  MOZ_ASSERT(mHTMLEditor);
  nsresult rv = NS_SUCCESS_DOM_NO_OPERATION;
  for (const size_t i : IntegerRange(aNotifier.MovingContentCount())) {
    nsIContent* const contentToMove = aNotifier.GetContentAt(i);
    MOZ_ASSERT(contentToMove);
    if (Element* const elementToMove = Element::FromNodeOrNull(contentToMove)) {
      if (!elementToMove->HasAttr(nsGkAtoms::mozdirty)) {
        nsresult rvMarkElementDirty = aHTMLEditor.MarkElementDirty(
            MOZ_KnownLive(*contentToMove->AsElement()));
        NS_WARNING_ASSERTION(
            NS_SUCCEEDED(rvMarkElementDirty),
            "EditorBase::MarkElementDirty() failed, but ignored");
        (void)rvMarkElementDirty;
      }
    }

    AutoNodeAPIWrapper nodeWrapper(aHTMLEditor, aParentNode);
    // MOZ_KnownLive because of guaranteed by both aNotifier and
    // aClonedSiblingsToMove.
    nsresult rvInner =
        nodeWrapper.InsertBefore(MOZ_KnownLive(*contentToMove), aReferenceNode);
    if (NS_FAILED(rvInner)) {
      NS_WARNING("AutoNodeAPIWrapper::InsertBefore() failed");
      rv = rvInner;
    } else {
      NS_WARNING_ASSERTION(nodeWrapper.IsExpectedResult(),
                           "Moving a node caused other mutations, but ignored");
    }
  }

  Document* const document = aHTMLEditor.GetDocument();
  for (const size_t i : IntegerRange(aNotifier.MovingContentCount())) {
    nsIContent* const content = aNotifier.GetContentAt(i);
    MOZ_ASSERT(content);
    if (MOZ_LIKELY(content->GetParentNode() &&
                   content->OwnerDoc() == document)) {
      aNotifier.DidMoveContent(*content);
    }
  }
  return NS_WARN_IF(aHTMLEditor.Destroyed()) ? NS_ERROR_EDITOR_DESTROYED : rv;
}

}  // namespace mozilla

Messung V0.5 in Prozent
C=86 H=86 G=85

¤ Dauer der Verarbeitung: 0.6 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=752002