tor-browser

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

JoinNodesTransaction.cpp (7346B)


      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 "JoinNodesTransaction.h"
      7 
      8 #include "EditorDOMPoint.h"   // for EditorDOMPoint, etc.
      9 #include "HTMLEditHelpers.h"  // for SplitNodeResult
     10 #include "HTMLEditor.h"       // for HTMLEditor
     11 #include "HTMLEditorInlines.h"
     12 #include "HTMLEditUtils.h"
     13 
     14 #include "mozilla/Logging.h"
     15 #include "mozilla/ToString.h"
     16 #include "mozilla/dom/Text.h"
     17 
     18 #include "nsAString.h"
     19 #include "nsDebug.h"          // for NS_ASSERTION, etc.
     20 #include "nsError.h"          // for NS_ERROR_NULL_POINTER, etc.
     21 #include "nsIContent.h"       // for nsIContent
     22 #include "nsISupportsImpl.h"  // for QueryInterface, etc.
     23 
     24 namespace mozilla {
     25 
     26 using namespace dom;
     27 
     28 // static
     29 already_AddRefed<JoinNodesTransaction> JoinNodesTransaction::MaybeCreate(
     30    HTMLEditor& aHTMLEditor, nsIContent& aLeftContent,
     31    nsIContent& aRightContent) {
     32  RefPtr<JoinNodesTransaction> transaction =
     33      new JoinNodesTransaction(aHTMLEditor, aLeftContent, aRightContent);
     34  if (NS_WARN_IF(!transaction->CanDoIt())) {
     35    return nullptr;
     36  }
     37  return transaction.forget();
     38 }
     39 
     40 JoinNodesTransaction::JoinNodesTransaction(HTMLEditor& aHTMLEditor,
     41                                           nsIContent& aLeftContent,
     42                                           nsIContent& aRightContent)
     43    : mHTMLEditor(&aHTMLEditor),
     44      mRemovedContent(&aRightContent),
     45      mKeepingContent(&aLeftContent) {
     46  // printf("JoinNodesTransaction size: %zu\n", sizeof(JoinNodesTransaction));
     47  static_assert(sizeof(JoinNodesTransaction) <= 64,
     48                "Transaction classes may be created a lot and may be alive "
     49                "long so that keep the foot print smaller as far as possible");
     50 }
     51 
     52 std::ostream& operator<<(std::ostream& aStream,
     53                         const JoinNodesTransaction& aTransaction) {
     54  aStream << "{ mParentNode=" << aTransaction.mParentNode.get();
     55  if (aTransaction.mParentNode) {
     56    aStream << " (" << *aTransaction.mParentNode << ")";
     57  }
     58  aStream << ", mRemovedContent=" << aTransaction.mRemovedContent.get();
     59  if (aTransaction.mRemovedContent) {
     60    aStream << " (" << *aTransaction.mRemovedContent << ")";
     61  }
     62  aStream << ", mKeepingContent=" << aTransaction.mKeepingContent.get();
     63  if (aTransaction.mKeepingContent) {
     64    aStream << " (" << *aTransaction.mKeepingContent << ")";
     65  }
     66  aStream << ", mJoinedOffset=" << aTransaction.mJoinedOffset
     67          << ", mHTMLEditor=" << aTransaction.mHTMLEditor.get() << " }";
     68  return aStream;
     69 }
     70 
     71 NS_IMPL_CYCLE_COLLECTION_INHERITED(JoinNodesTransaction, EditTransactionBase,
     72                                   mHTMLEditor, mParentNode, mRemovedContent,
     73                                   mKeepingContent)
     74 
     75 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JoinNodesTransaction)
     76 NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
     77 
     78 bool JoinNodesTransaction::CanDoIt() const {
     79  if (NS_WARN_IF(!mKeepingContent) || NS_WARN_IF(!mRemovedContent) ||
     80      NS_WARN_IF(!mHTMLEditor) ||
     81      NS_WARN_IF(mRemovedContent->IsBeingRemoved()) ||
     82      !mKeepingContent->IsInComposedDoc()) {
     83    return false;
     84  }
     85  return HTMLEditUtils::IsRemovableFromParentNode(*mRemovedContent);
     86 }
     87 
     88 // After DoTransaction() and RedoTransaction(), the left node is removed from
     89 // the content tree and right node remains.
     90 NS_IMETHODIMP JoinNodesTransaction::DoTransaction() {
     91  MOZ_LOG(GetLogModule(), LogLevel::Info,
     92          ("%p JoinNodesTransaction::%s this=%s", this, __FUNCTION__,
     93           ToString(*this).c_str()));
     94 
     95  return DoTransactionInternal(RedoingTransaction::No);
     96 }
     97 
     98 nsresult JoinNodesTransaction::DoTransactionInternal(
     99    RedoingTransaction aRedoingTransaction) {
    100  if (MOZ_UNLIKELY(NS_WARN_IF(!mHTMLEditor) || NS_WARN_IF(!mKeepingContent) ||
    101                   NS_WARN_IF(!mRemovedContent) ||
    102                   NS_WARN_IF(mRemovedContent->IsBeingRemoved()))) {
    103    return NS_ERROR_NOT_AVAILABLE;
    104  }
    105 
    106  nsINode* removingContentParentNode = mRemovedContent->GetParentNode();
    107  if (MOZ_UNLIKELY(NS_WARN_IF(!removingContentParentNode))) {
    108    return NS_ERROR_NOT_AVAILABLE;
    109  }
    110 
    111  // Verify that the joining content nodes have the same parent
    112  if (MOZ_UNLIKELY(removingContentParentNode !=
    113                   mKeepingContent->GetParentNode())) {
    114    NS_ASSERTION(false, "Nodes do not have same parent");
    115    return NS_ERROR_NOT_AVAILABLE;
    116  }
    117 
    118  // Set this instance's mParentNode.  Other methods will see a non-null
    119  // mParentNode and know all is well
    120  mParentNode = removingContentParentNode;
    121  // For now, setting mJoinedOffset to removed content length so that
    122  // CreateJoinedPoint returns a point in mKeepingContent whose offset is
    123  // the result if all content in mRemovedContent are moved to start or end of
    124  // mKeepingContent without any intervention.  The offset will be adjusted
    125  // below.
    126  mJoinedOffset = mKeepingContent->Length();
    127 
    128  const OwningNonNull<HTMLEditor> htmlEditor = *mHTMLEditor;
    129  const OwningNonNull<nsIContent> removingContent = *mRemovedContent;
    130  const OwningNonNull<nsIContent> keepingContent = *mKeepingContent;
    131  nsresult rv;
    132  // Let's try to get actual joined point with the tacker.
    133  auto joinNodesPoint = EditorDOMPoint::AtEndOf(keepingContent);
    134  {
    135    AutoTrackDOMPoint trackJoinNodePoint(htmlEditor->RangeUpdaterRef(),
    136                                         &joinNodesPoint);
    137    rv = htmlEditor->DoJoinNodes(keepingContent, removingContent);
    138    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "HTMLEditor::DoJoinNodes() failed");
    139  }
    140  // Adjust join node offset to the actual offset where the original first
    141  // content of the right node is.
    142  mJoinedOffset = joinNodesPoint.Offset();
    143 
    144  if (aRedoingTransaction == RedoingTransaction::No) {
    145    htmlEditor->DidJoinNodesTransaction(*this, rv);
    146  }
    147 
    148  return rv;
    149 }
    150 
    151 // XXX: What if instead of split, we just deleted the unneeded children of
    152 //     mRight and re-inserted mLeft?
    153 NS_IMETHODIMP JoinNodesTransaction::UndoTransaction() {
    154  MOZ_LOG(GetLogModule(), LogLevel::Info,
    155          ("%p JoinNodesTransaction::%s this=%s", this, __FUNCTION__,
    156           ToString(*this).c_str()));
    157 
    158  if (NS_WARN_IF(!mParentNode) || NS_WARN_IF(!mKeepingContent) ||
    159      NS_WARN_IF(!mRemovedContent) || NS_WARN_IF(!mHTMLEditor)) {
    160    return NS_ERROR_NOT_AVAILABLE;
    161  }
    162 
    163  const OwningNonNull<HTMLEditor> htmlEditor = *mHTMLEditor;
    164  const OwningNonNull<nsIContent> removedContent = *mRemovedContent;
    165 
    166  Result<SplitNodeResult, nsresult> splitNodeResult = htmlEditor->DoSplitNode(
    167      CreateJoinedPoint<EditorDOMPoint>(), removedContent);
    168  if (MOZ_UNLIKELY(splitNodeResult.isErr())) {
    169    NS_WARNING("HTMLEditor::DoSplitNode() failed");
    170    return splitNodeResult.unwrapErr();
    171  }
    172  // When adding caret suggestion to SplitNodeResult, here didn't change
    173  // selection so that just ignore it.
    174  splitNodeResult.inspect().IgnoreCaretPointSuggestion();
    175  return NS_OK;
    176 }
    177 
    178 NS_IMETHODIMP JoinNodesTransaction::RedoTransaction() {
    179  MOZ_LOG(GetLogModule(), LogLevel::Info,
    180          ("%p JoinNodesTransaction::%s this=%s", this, __FUNCTION__,
    181           ToString(*this).c_str()));
    182  return DoTransactionInternal(RedoingTransaction::Yes);
    183 }
    184 
    185 }  // namespace mozilla