DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (cdf352f02ac4)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "JoinNodeTransaction.h"

#include "mozilla/EditorBase.h"  // for EditorBase
#include "mozilla/dom/Text.h"
#include "nsAString.h"
#include "nsDebug.h"          // for NS_ASSERTION, etc.
#include "nsError.h"          // for NS_ERROR_NULL_POINTER, etc.
#include "nsIContent.h"       // for nsIContent
#include "nsISupportsImpl.h"  // for QueryInterface, etc.

namespace mozilla {

using namespace dom;

// static
already_AddRefed<JoinNodeTransaction> JoinNodeTransaction::MaybeCreate(
    EditorBase& aEditorBase, nsIContent& aLeftContent,
    nsIContent& aRightContent) {
  RefPtr<JoinNodeTransaction> transaction =
      new JoinNodeTransaction(aEditorBase, aLeftContent, aRightContent);
  if (NS_WARN_IF(!transaction->CanDoIt())) {
    return nullptr;
  }
  return transaction.forget();
}

JoinNodeTransaction::JoinNodeTransaction(EditorBase& aEditorBase,
                                         nsIContent& aLeftContent,
                                         nsIContent& aRightContent)
    : mEditorBase(&aEditorBase),
      mLeftContent(&aLeftContent),
      mRightContent(&aRightContent),
      mOffset(0) {}

NS_IMPL_CYCLE_COLLECTION_INHERITED(JoinNodeTransaction, EditTransactionBase,
                                   mEditorBase, mLeftContent, mRightContent,
                                   mParentNode)

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JoinNodeTransaction)
NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)

bool JoinNodeTransaction::CanDoIt() const {
  if (NS_WARN_IF(!mLeftContent) || NS_WARN_IF(!mRightContent) ||
      NS_WARN_IF(!mEditorBase) || !mLeftContent->GetParentNode()) {
    return false;
  }
  return mEditorBase->IsModifiableNode(*mLeftContent->GetParentNode());
}

// After DoTransaction() and RedoTransaction(), the left node is removed from
// the content tree and right node remains.
NS_IMETHODIMP JoinNodeTransaction::DoTransaction() {
  if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mLeftContent) ||
      NS_WARN_IF(!mRightContent)) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  // Get the parent node
  nsCOMPtr<nsINode> leftContentParent = mLeftContent->GetParentNode();
  if (NS_WARN_IF(!leftContentParent)) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  // Verify that mLeftContent and mRightContent have the same parent
  if (leftContentParent != mRightContent->GetParentNode()) {
    NS_ASSERTION(false, "Nodes do not have same parent");
    return NS_ERROR_NOT_AVAILABLE;
  }

  // Set this instance's mParentNode.  Other methods will see a non-null
  // mParentNode and know all is well
  mParentNode = leftContentParent;
  mOffset = mLeftContent->Length();

  OwningNonNull<EditorBase> editorBase = *mEditorBase;
  OwningNonNull<nsINode> leftNode = *mLeftContent;
  OwningNonNull<nsINode> rightNode = *mRightContent;
  nsresult rv = editorBase->DoJoinNodes(rightNode, leftNode, leftContentParent);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::DoJoinNodes() failed");
  return rv;
}

// XXX: What if instead of split, we just deleted the unneeded children of
//     mRight and re-inserted mLeft?
NS_IMETHODIMP JoinNodeTransaction::UndoTransaction() {
  if (NS_WARN_IF(!mParentNode) || NS_WARN_IF(!mLeftContent) ||
      NS_WARN_IF(!mRightContent) || NS_WARN_IF(!mEditorBase)) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  OwningNonNull<nsIContent> leftContent = *mLeftContent;
  OwningNonNull<nsIContent> rightContent = *mRightContent;
  OwningNonNull<nsINode> parentNode = *mParentNode;

  // First, massage the existing node so it is in its post-split state
  ErrorResult error;
  if (Text* rightTextNode = rightContent->GetAsText()) {
    OwningNonNull<EditorBase> editorBase = *mEditorBase;
    editorBase->DoDeleteText(MOZ_KnownLive(*rightTextNode), 0, mOffset, error);
    if (error.Failed()) {
      NS_WARNING("EditorBase::DoDeleteText() failed");
      return error.StealNSResult();
    }
  } else {
    AutoTArray<OwningNonNull<nsIContent>, 24> movingChildren;
    if (nsIContent* child = mRightContent->GetFirstChild()) {
      movingChildren.AppendElement(*child);
      for (uint32_t i = 0; i < mOffset; i++) {
        child = child->GetNextSibling();
        if (!child) {
          break;
        }
        movingChildren.AppendElement(*child);
      }
    }
    for (OwningNonNull<nsIContent>& child : movingChildren) {
      leftContent->AppendChild(child, error);
      if (error.Failed()) {
        NS_WARNING("nsINode::AppendChild() failed");
        return error.StealNSResult();
      }
    }
  }

  NS_WARNING_ASSERTION(!error.Failed(), "The previous error was ignored");

  // Second, re-insert the left node into the tree
  parentNode->InsertBefore(leftContent, rightContent, error);
  NS_WARNING_ASSERTION(!error.Failed(), "nsINode::InsertBefore() failed");
  return error.StealNSResult();
}

}  // namespace mozilla