Bug 1408125 - part 1: Make InsertNodeTransaction use EditorRawDOMPoint and RangeBoundary instead of pair of container node and offset in it r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 22 Nov 2017 17:29:22 +0900
changeset 710424 c6a7a807fa6c2b370dd6e51cd2cb2d2694eb4cdf
parent 710423 9b3f61e51d504d0fcb76ed2af397eaf71f75996f
child 710425 6b0737fa9567365a2c680a52a72348b0dc38913c
push id92823
push usermasayuki@d-toybox.com
push dateSun, 10 Dec 2017 02:40:55 +0000
reviewersm_kato
bugs1408125
milestone59.0a1
Bug 1408125 - part 1: Make InsertNodeTransaction use EditorRawDOMPoint and RangeBoundary instead of pair of container node and offset in it r?m_kato Like CreateNodeTransaction, InsertNodeTransaction should use EditorRawDOMPoint for temporary variable and arguments of its methods and should use RangeBoudary to store it as its member. MozReview-Commit-ID: FCGY8x97egb
editor/libeditor/EditorBase.cpp
editor/libeditor/InsertNodeTransaction.cpp
editor/libeditor/InsertNodeTransaction.h
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -4620,18 +4620,22 @@ EditorBase::CreateTxnForCreateElement(ns
 }
 
 
 already_AddRefed<InsertNodeTransaction>
 EditorBase::CreateTxnForInsertNode(nsIContent& aNode,
                                    nsINode& aParent,
                                    int32_t aPosition)
 {
+  int32_t offset =
+    aPosition < 0 ? static_cast<int32_t>(aParent.Length()) :
+                    std::min(aPosition, static_cast<int32_t>(aParent.Length()));
   RefPtr<InsertNodeTransaction> transaction =
-    new InsertNodeTransaction(aNode, aParent, aPosition, *this);
+    new InsertNodeTransaction(*this, aNode,
+                              EditorRawDOMPoint(&aParent, offset));
   return transaction.forget();
 }
 
 already_AddRefed<DeleteNodeTransaction>
 EditorBase::CreateTxnForDeleteNode(nsINode* aNode)
 {
   if (NS_WARN_IF(!aNode)) {
     return nullptr;
--- a/editor/libeditor/InsertNodeTransaction.cpp
+++ b/editor/libeditor/InsertNodeTransaction.cpp
@@ -1,100 +1,137 @@
 /* -*- 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 "InsertNodeTransaction.h"
 
 #include "mozilla/EditorBase.h"         // for EditorBase
+#include "mozilla/EditorDOMPoint.h"     // for EditorDOMPoint
 
 #include "mozilla/dom/Selection.h"      // for Selection
 
 #include "nsAString.h"
 #include "nsDebug.h"                    // for NS_ENSURE_TRUE, etc.
 #include "nsError.h"                    // for NS_ERROR_NULL_POINTER, etc.
 #include "nsIContent.h"                 // for nsIContent
 #include "nsMemory.h"                   // for nsMemory
 #include "nsReadableUtils.h"            // for ToNewCString
 #include "nsString.h"                   // for nsString
 
 namespace mozilla {
 
 using namespace dom;
 
-InsertNodeTransaction::InsertNodeTransaction(nsIContent& aNode,
-                                             nsINode& aParent,
-                                             int32_t aOffset,
-                                             EditorBase& aEditorBase)
-  : mNode(&aNode)
-  , mParent(&aParent)
-  , mOffset(aOffset)
+InsertNodeTransaction::InsertNodeTransaction(
+                         EditorBase& aEditorBase,
+                         nsIContent& aContentToInsert,
+                         const EditorRawDOMPoint& aPointToInsert)
+  : mContentToInsert(&aContentToInsert)
+  , mPointToInsert(aPointToInsert)
   , mEditorBase(&aEditorBase)
 {
+  MOZ_ASSERT(mPointToInsert.IsSetAndValid());
+  // Ensure mPointToInsert stores child at offset.
+  Unused << mPointToInsert.GetChildAtOffset();
 }
 
 InsertNodeTransaction::~InsertNodeTransaction()
 {
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertNodeTransaction, EditTransactionBase,
                                    mEditorBase,
-                                   mNode,
-                                   mParent)
+                                   mContentToInsert,
+                                   mPointToInsert)
 
 NS_IMPL_ADDREF_INHERITED(InsertNodeTransaction, EditTransactionBase)
 NS_IMPL_RELEASE_INHERITED(InsertNodeTransaction, EditTransactionBase)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(InsertNodeTransaction)
 NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
 
 NS_IMETHODIMP
 InsertNodeTransaction::DoTransaction()
 {
-  if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mNode) || NS_WARN_IF(!mParent)) {
+  if (NS_WARN_IF(!mEditorBase) ||
+      NS_WARN_IF(!mContentToInsert) ||
+      NS_WARN_IF(!mPointToInsert.IsSet())) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  uint32_t count = mParent->GetChildCount();
-  if (mOffset > static_cast<int32_t>(count) || mOffset == -1) {
-    // -1 is sentinel value meaning "append at end"
-    mOffset = count;
+  if (!mPointToInsert.IsSetAndValid()) {
+    // It seems that DOM tree has been changed after first DoTransaction()
+    // and current RedoTranaction() call.
+    if (mPointToInsert.GetChildAtOffset()) {
+      EditorDOMPoint newPointToInsert(mPointToInsert.GetChildAtOffset());
+      if (!newPointToInsert.IsSet()) {
+        // The insertion point has been removed from the DOM tree.
+        // In this case, we should append the node to the container instead.
+        newPointToInsert.Set(mPointToInsert.Container(),
+                             mPointToInsert.Container()->Length());
+        if (NS_WARN_IF(!newPointToInsert.IsSet())) {
+          return NS_ERROR_FAILURE;
+        }
+      }
+      mPointToInsert = newPointToInsert;
+    } else {
+      mPointToInsert.Set(mPointToInsert.Container(),
+                         mPointToInsert.Container()->Length());
+      if (NS_WARN_IF(!mPointToInsert.IsSet())) {
+        return NS_ERROR_FAILURE;
+      }
+    }
   }
 
-  // Note, it's ok for ref to be null. That means append.
-  nsCOMPtr<nsIContent> ref = mParent->GetChildAt(mOffset);
-
-  mEditorBase->MarkNodeDirty(GetAsDOMNode(mNode));
+  mEditorBase->MarkNodeDirty(GetAsDOMNode(mContentToInsert));
 
-  ErrorResult rv;
-  mParent->InsertBefore(*mNode, ref, rv);
-  rv.WouldReportJSException();
-  NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
+  ErrorResult error;
+  mPointToInsert.Container()->InsertBefore(*mContentToInsert,
+                                           mPointToInsert.GetChildAtOffset(),
+                                           error);
+  error.WouldReportJSException();
+  if (NS_WARN_IF(error.Failed())) {
+    return error.StealNSResult();
+  }
 
   // Only set selection to insertion point if editor gives permission
   if (mEditorBase->GetShouldTxnSetSelection()) {
     RefPtr<Selection> selection = mEditorBase->GetSelection();
-    NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
+    if (NS_WARN_IF(!selection)) {
+      return NS_ERROR_FAILURE;
+    }
     // Place the selection just after the inserted element
-    selection->Collapse(mParent, mOffset + 1);
-  } else {
-    // Do nothing - DOM Range gravity will adjust selection
+    EditorRawDOMPoint afterInsertedNode(mContentToInsert);
+    DebugOnly<bool> advanced = afterInsertedNode.AdvanceOffset();
+    NS_WARNING_ASSERTION(advanced,
+      "Failed to advance offset after the inserted node");
+    selection->Collapse(afterInsertedNode, error);
+    if (NS_WARN_IF(error.Failed())) {
+      error.SuppressException();
+    }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 InsertNodeTransaction::UndoTransaction()
 {
-  if (NS_WARN_IF(!mNode) || NS_WARN_IF(!mParent)) {
+  if (NS_WARN_IF(!mContentToInsert) ||
+      NS_WARN_IF(!mPointToInsert.IsSet())) {
     return NS_ERROR_NOT_INITIALIZED;
   }
-  ErrorResult rv;
-  mParent->RemoveChild(*mNode, rv);
-  return rv.StealNSResult();
+  // XXX If the inserted node has been moved to different container node or
+  //     just removed from the DOM tree, this always fails.
+  ErrorResult error;
+  mPointToInsert.Container()->RemoveChild(*mContentToInsert, error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.StealNSResult();
+  }
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 InsertNodeTransaction::GetTxnDescription(nsAString& aString)
 {
   aString.AssignLiteral("InsertNodeTransaction");
   return NS_OK;
 }
--- a/editor/libeditor/InsertNodeTransaction.h
+++ b/editor/libeditor/InsertNodeTransaction.h
@@ -2,16 +2,17 @@
 /* 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/. */
 
 #ifndef InsertNodeTransaction_h
 #define InsertNodeTransaction_h
 
 #include "mozilla/EditTransactionBase.h" // for EditTransactionBase, etc.
+#include "mozilla/EditorDOMPoint.h"     // for EditorDOMPoint
 #include "nsCOMPtr.h"                   // for nsCOMPtr
 #include "nsCycleCollectionParticipant.h"
 #include "nsIContent.h"                 // for nsIContent
 #include "nsISupportsImpl.h"            // for NS_DECL_ISUPPORTS_INHERITED
 
 namespace mozilla {
 
 class EditorBase;
@@ -19,40 +20,40 @@ class EditorBase;
 /**
  * A transaction that inserts a single element
  */
 class InsertNodeTransaction final : public EditTransactionBase
 {
 public:
   /**
    * Initialize the transaction.
-   * @param aNode       The node to insert.
-   * @param aParent     The node to insert into.
-   * @param aOffset     The offset in aParent to insert aNode.
+   *
+   * @param aEditorBase         The editor.
+   * @param aContentToInsert    The node to insert.
+   * @param aPointToInsert      The node to insert into.  I.e., aContentToInsert
+   *                            will be inserted before the child at offset.
    */
-  InsertNodeTransaction(nsIContent& aNode, nsINode& aParent, int32_t aOffset,
-                        EditorBase& aEditorBase);
+  InsertNodeTransaction(EditorBase& aEditorBase,
+                        nsIContent& aContentToInsert,
+                        const EditorRawDOMPoint& aPointToInsert);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(InsertNodeTransaction,
                                            EditTransactionBase)
 
   NS_DECL_EDITTRANSACTIONBASE
 
 protected:
   virtual ~InsertNodeTransaction();
 
   // The element to insert.
-  nsCOMPtr<nsIContent> mNode;
+  nsCOMPtr<nsIContent> mContentToInsert;
 
-  // The node into which the new node will be inserted.
-  nsCOMPtr<nsINode> mParent;
-
-  // The index in mParent for the new node.
-  int32_t mOffset;
+  // The DOM point we will insert mContentToInsert.
+  EditorDOMPoint mPointToInsert;
 
   // The editor for this transaction.
   RefPtr<EditorBase> mEditorBase;
 };
 
 } // namespace mozilla
 
 #endif // #ifndef InsertNodeTransaction_h