Commit 84960235 authored by Masayuki Nakano's avatar Masayuki Nakano
Browse files

Bug 1408125 - part 2: EditorBase::CreateTxnForInsertNode() and...

Bug 1408125 - part 2: EditorBase::CreateTxnForInsertNode() and EditorBase::InsertNode() should take |const EditorRawDOMPoint&| as an argument specifying point to insert r=m_kato

EditorBase::CreateTxnForInsertNode() and EditorBase::InsertNode() should take
|const EditorRawDOMPoint&| as an argument specifying point to insert.

MozReview-Commit-ID: KhK19xS7wXb

--HG--
extra : rebase_source : f7b08b7d049eef8a9c03ee681e53e8d469915b15
parent 2e842218
Loading
Loading
Loading
Loading
+136 −81
Original line number Diff line number Diff line
@@ -1469,42 +1469,57 @@ EditorBase::CreateNode(nsAtom* aTag,
}

NS_IMETHODIMP
EditorBase::InsertNode(nsIDOMNode* aNode,
                       nsIDOMNode* aParent,
                       int32_t aPosition)
EditorBase::InsertNode(nsIDOMNode* aNodeToInsert,
                       nsIDOMNode* aContainer,
                       int32_t aOffset)
{
  nsCOMPtr<nsIContent> node = do_QueryInterface(aNode);
  nsCOMPtr<nsINode> parent = do_QueryInterface(aParent);
  NS_ENSURE_TRUE(node && parent, NS_ERROR_NULL_POINTER);

  return InsertNode(*node, *parent, aPosition);
  nsCOMPtr<nsIContent> contentToInsert = do_QueryInterface(aNodeToInsert);
  if (NS_WARN_IF(!contentToInsert)) {
    return NS_ERROR_NULL_POINTER;
  }
  nsCOMPtr<nsINode> container = do_QueryInterface(aContainer);
  if (NS_WARN_IF(!container)) {
    return NS_ERROR_NULL_POINTER;
  }
  int32_t offset =
    aOffset < 0 ? static_cast<int32_t>(container->Length()) :
                  std::min(aOffset, static_cast<int32_t>(container->Length()));
  return InsertNode(*contentToInsert, EditorRawDOMPoint(container, offset));
}

nsresult
EditorBase::InsertNode(nsIContent& aNode,
                       nsINode& aParent,
                       int32_t aPosition)
EditorBase::InsertNode(nsIContent& aContentToInsert,
                       const EditorRawDOMPoint& aPointToInsert)
{
  if (NS_WARN_IF(!aPointToInsert.IsSet())) {
    return NS_ERROR_INVALID_ARG;
  }
  MOZ_ASSERT(aPointToInsert.IsSetAndValid());

  AutoRules beginRulesSniffing(this, EditAction::insertNode, nsIEditor::eNext);

  {
    AutoActionListenerArray listeners(mActionListeners);
    for (auto& listener : listeners) {
      listener->WillInsertNode(aNode.AsDOMNode(), aParent.AsDOMNode(),
                               aPosition);
      listener->WillInsertNode(aContentToInsert.AsDOMNode(),
                               aPointToInsert.Container()->AsDOMNode(),
                               aPointToInsert.Offset());
    }
  }

  RefPtr<InsertNodeTransaction> transaction =
    CreateTxnForInsertNode(aNode, aParent, aPosition);
    CreateTxnForInsertNode(aContentToInsert, aPointToInsert);
  nsresult rv = DoTransaction(transaction);

  mRangeUpdater.SelAdjInsertNode(&aParent, aPosition);
  mRangeUpdater.SelAdjInsertNode(aPointToInsert.Container(),
                                 aPointToInsert.Offset());

  {
    AutoActionListenerArray listeners(mActionListeners);
    for (auto& listener : listeners) {
      listener->DidInsertNode(aNode.AsDOMNode(), aParent.AsDOMNode(), aPosition,
      listener->DidInsertNode(aContentToInsert.AsDOMNode(),
                              aPointToInsert.Container()->AsDOMNode(),
                              aPointToInsert.Offset(),
                              rv);
    }
  }
@@ -1694,51 +1709,67 @@ EditorBase::ReplaceContainer(Element* aOldContainer,
{
  MOZ_ASSERT(aOldContainer && aNodeType);

  nsCOMPtr<nsIContent> parent = aOldContainer->GetParent();
  NS_ENSURE_TRUE(parent, nullptr);

  int32_t offset = parent->IndexOf(aOldContainer);
  EditorDOMPoint atOldContainer(aOldContainer);
  if (NS_WARN_IF(!atOldContainer.IsSet())) {
    return nullptr;
  }

  // create new container
  nsCOMPtr<Element> ret = CreateHTMLContent(aNodeType);
  NS_ENSURE_TRUE(ret, nullptr);
  RefPtr<Element> newContainer = CreateHTMLContent(aNodeType);
  if (NS_WARN_IF(!newContainer)) {
    return nullptr;
  }

  // set attribute if needed
  // Set attribute if needed.
  if (aAttribute && aValue && aAttribute != nsGkAtoms::_empty) {
    nsresult rv = ret->SetAttr(kNameSpaceID_None, aAttribute, *aValue, true);
    NS_ENSURE_SUCCESS(rv, nullptr);
    nsresult rv =
      newContainer->SetAttr(kNameSpaceID_None, aAttribute, *aValue, true);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return nullptr;
    }
  }
  if (aCloneAttributes == eCloneAttributes) {
    CloneAttributes(ret, aOldContainer);
    CloneAttributes(newContainer, aOldContainer);
  }

  // notify our internal selection state listener
  // (Note: An AutoSelectionRestorer object must be created
  //  before calling this to initialize mRangeUpdater)
  // Notify our internal selection state listener.
  // Note: An AutoSelectionRestorer object must be created before calling this
  // to initialize mRangeUpdater.
  AutoReplaceContainerSelNotify selStateNotify(mRangeUpdater, aOldContainer,
                                               ret);
                                               newContainer);
  {
    AutoTransactionsConserveSelection conserveSelection(this);
    // Move all children from the old container to the new container.
    while (aOldContainer->HasChildren()) {
      nsCOMPtr<nsIContent> child = aOldContainer->GetFirstChild();

      nsresult rv = DeleteNode(child);
      NS_ENSURE_SUCCESS(rv, nullptr);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return nullptr;
      }

      rv = InsertNode(*child, *ret, -1);
      NS_ENSURE_SUCCESS(rv, nullptr);
      rv = InsertNode(*child, EditorRawDOMPoint(newContainer,
                                                newContainer->Length()));
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return nullptr;
      }
    }
  }

  // insert new container into tree
  nsresult rv = InsertNode(*ret, *parent, offset);
  NS_ENSURE_SUCCESS(rv, nullptr);
  // Insert new container into tree.
  NS_WARNING_ASSERTION(atOldContainer.IsSetAndValid(),
    "The old container might be moved by mutation observer");
  nsresult rv = InsertNode(*newContainer, atOldContainer.AsRaw());
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return nullptr;
  }

  // delete old container
  // Delete old container.
  rv = DeleteNode(aOldContainer);
  NS_ENSURE_SUCCESS(rv, nullptr);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return nullptr;
  }

  return ret.forget();
  return newContainer.forget();
}

/**
@@ -1750,25 +1781,33 @@ EditorBase::RemoveContainer(nsIContent* aNode)
{
  MOZ_ASSERT(aNode);

  nsCOMPtr<nsINode> parent = aNode->GetParentNode();
  NS_ENSURE_STATE(parent);

  int32_t offset = parent->IndexOf(aNode);

  // Loop through the children of inNode and promote them into inNode's parent
  uint32_t nodeOrigLen = aNode->GetChildCount();
  EditorDOMPoint pointToInsertChildren(aNode);
  if (NS_WARN_IF(!pointToInsertChildren.IsSet())) {
    return NS_ERROR_FAILURE;
  }

  // notify our internal selection state listener
  AutoRemoveContainerSelNotify selNotify(mRangeUpdater, aNode, parent,
                                         offset, nodeOrigLen);
  // Notify our internal selection state listener.
  AutoRemoveContainerSelNotify selNotify(mRangeUpdater, aNode,
                                         pointToInsertChildren.Container(),
                                         pointToInsertChildren.Offset(),
                                         aNode->GetChildCount());

  // Move all children from aNode to its parent.
  while (aNode->HasChildren()) {
    nsCOMPtr<nsIContent> child = aNode->GetLastChild();
    nsresult rv = DeleteNode(child);
    NS_ENSURE_SUCCESS(rv, rv);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }

    rv = InsertNode(*child, *parent, offset);
    NS_ENSURE_SUCCESS(rv, rv);
    // Insert the last child before the previous last child.  So, we need to
    // use offset here because previous child might have been moved to
    // container.
    rv = InsertNode(*child, EditorRawDOMPoint(pointToInsertChildren.Container(),
                                              pointToInsertChildren.Offset()));
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }
  }

  return DeleteNode(aNode);
@@ -1788,39 +1827,56 @@ EditorBase::InsertContainerAbove(nsIContent* aNode,
{
  MOZ_ASSERT(aNode && aNodeType);

  nsCOMPtr<nsIContent> parent = aNode->GetParent();
  NS_ENSURE_TRUE(parent, nullptr);
  int32_t offset = parent->IndexOf(aNode);
  EditorDOMPoint pointToInsertNewContainer(aNode);
  if (NS_WARN_IF(!pointToInsertNewContainer.IsSet())) {
    return nullptr;
  }
  // aNode will be moved to the new container before inserting the new
  // container.  So, when we insert the container, the insertion point
  // is before the next sibling of aNode.
  DebugOnly<bool> advanced = pointToInsertNewContainer.AdvanceOffset();
  NS_WARNING_ASSERTION(advanced,
    "Failed to advance offset to after aNode");

  // Create new container
  nsCOMPtr<Element> newContent = CreateHTMLContent(aNodeType);
  NS_ENSURE_TRUE(newContent, nullptr);
  RefPtr<Element> newContainer = CreateHTMLContent(aNodeType);
  if (NS_WARN_IF(!newContainer)) {
    return nullptr;
  }

  // Set attribute if needed
  if (aAttribute && aValue && aAttribute != nsGkAtoms::_empty) {
    nsresult rv =
      newContent->SetAttr(kNameSpaceID_None, aAttribute, *aValue, true);
    NS_ENSURE_SUCCESS(rv, nullptr);
      newContainer->SetAttr(kNameSpaceID_None, aAttribute, *aValue, true);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return nullptr;
    }
  }

  // Notify our internal selection state listener
  AutoInsertContainerSelNotify selNotify(mRangeUpdater);

  // Put inNode in new parent, outNode
  // Put aNode in the new container, first.
  nsresult rv = DeleteNode(aNode);
  NS_ENSURE_SUCCESS(rv, nullptr);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return nullptr;
  }

  {
    AutoTransactionsConserveSelection conserveSelection(this);
    rv = InsertNode(*aNode, *newContent, 0);
    NS_ENSURE_SUCCESS(rv, nullptr);
    rv = InsertNode(*aNode, EditorRawDOMPoint(newContainer, 0));
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return nullptr;
    }
  }

  // Put new parent in doc
  rv = InsertNode(*newContent, *parent, offset);
  NS_ENSURE_SUCCESS(rv, nullptr);
  // Put the new container where aNode was.
  rv = InsertNode(*newContainer, pointToInsertNewContainer.AsRaw());
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return nullptr;
  }

  return newContent.forget();
  return newContainer.forget();
}

/**
@@ -1864,9 +1920,15 @@ EditorBase::MoveNode(nsIContent* aNode,
  nsCOMPtr<nsINode> kungFuDeathGrip = aNode;

  nsresult rv = DeleteNode(aNode);
  NS_ENSURE_SUCCESS(rv, rv);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  return InsertNode(*aNode, *aParent, aOffset);
  rv = InsertNode(*aNode, EditorRawDOMPoint(aParent, aOffset));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }
  return NS_OK;
}

void
@@ -2665,8 +2727,7 @@ EditorBase::InsertTextImpl(nsIDocument& aDocument,
      RefPtr<nsTextNode> newNode =
        EditorBase::CreateTextNode(aDocument, EmptyString());
      // then we insert it into the dom tree
      nsresult rv = InsertNode(*newNode, *pointToInsert.Container(),
                               pointToInsert.Offset());
      nsresult rv = InsertNode(*newNode, pointToInsert);
      NS_ENSURE_SUCCESS(rv, rv);
      pointToInsert.Set(newNode, 0);
      newOffset = lengthToInsert;
@@ -2707,8 +2768,7 @@ EditorBase::InsertTextImpl(nsIDocument& aDocument,
  RefPtr<nsTextNode> newNode =
    EditorBase::CreateTextNode(aDocument, aStringToInsert);
  // then we insert it into the dom tree
  nsresult rv = InsertNode(*newNode, *pointToInsert.Container(),
                           pointToInsert.Offset());
  nsresult rv = InsertNode(*newNode, pointToInsert);
  NS_ENSURE_SUCCESS(rv, rv);
  if (aPointAfterInsertedString) {
    aPointAfterInsertedString->Set(newNode, lengthToInsert.value());
@@ -4622,15 +4682,10 @@ EditorBase::CreateTxnForCreateElement(nsAtom& aTag,

already_AddRefed<InsertNodeTransaction>
EditorBase::CreateTxnForInsertNode(nsIContent& aNode,
                                   nsINode& aParent,
                                   int32_t aPosition)
                                   const EditorRawDOMPoint& aPointToInsert)
{
  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(*this, aNode,
                              EditorRawDOMPoint(&aParent, offset));
    new InsertNodeTransaction(*this, aNode, aPointToInsert);
  return transaction.forget();
}

+29 −5
Original line number Diff line number Diff line
@@ -346,7 +346,21 @@ public:
   * Helper routines for node/parent manipulations.
   */
  nsresult DeleteNode(nsINode* aNode);
  nsresult InsertNode(nsIContent& aNode, nsINode& aParent, int32_t aPosition);

  /**
   * InsertNode() inserts aContentToInsert before the child specified by
   * aPointToInsert.
   *
   * @param aContentToInsert    The node to be inserted.
   * @param aPointToInsert      The insertion point of aContentToInsert.
   *                            If this refers end of the container, the
   *                            transaction will append the node to the
   *                            container.  Otherwise, will insert the node
   *                            before child node referred by this.
   */
  nsresult InsertNode(nsIContent& aContentToInsert,
                      const EditorRawDOMPoint& aPointToInsert);

  enum ECloneAttributes { eDontCloneAttributes, eCloneAttributes };
  already_AddRefed<Element> ReplaceContainer(Element* aOldContainer,
                                             nsAtom* aNodeType,
@@ -512,7 +526,7 @@ protected:
   *                        will append the element to the container.
   *                        Otherwise, will insert the element before the
   *                        child node referred by this.
   * @return                A CreateElementTransaction which are initialized
   * @return                A CreateElementTransaction which was initialized
   *                        with the arguments.
   */
  already_AddRefed<CreateElementTransaction>
@@ -538,11 +552,21 @@ protected:
                                       const EditorRawDOMPoint& aPointToInsert);

  /**
   * Create a transaction for inserting aNode as a child of aParent.
   * Create a transaction for inserting aContentToInsert before the child
   * at aPointToInsert.
   *
   * @param aContentToInsert    The node to be inserted.
   * @param aPointToInsert      The insertion point of aContentToInsert.
   *                            If this refers end of the container, the
   *                            transaction will append the node to the
   *                            container.  Otherwise, will insert the node
   *                            before child node referred by this.
   * @return                    A InsertNodeTranaction which was initialized
   *                            with the arguments.
   */
  already_AddRefed<InsertNodeTransaction>
    CreateTxnForInsertNode(nsIContent& aNode, nsINode& aParent,
                           int32_t aOffset);
    CreateTxnForInsertNode(nsIContent& aContentToInsert,
                           const EditorRawDOMPoint& aPointToInsert);

  /**
   * Create a transaction for removing aNode from its parent.
+4 −1
Original line number Diff line number Diff line
@@ -4864,6 +4864,8 @@ HTMLEditRules::CreateStyleForInsertText(Selection& aSelection,
    Move(mHTMLEditor->mTypeInState->TakeClearProperty());
  while (item && node != rootElement) {
    NS_ENSURE_STATE(mHTMLEditor);
    // XXX If we redesign ClearStyle(), we can use EditorDOMPoint in this
    //     method.
    nsresult rv =
      mHTMLEditor->ClearStyle(address_of(node), &offset,
                              item->tag, &item->attr);
@@ -4900,7 +4902,8 @@ HTMLEditRules::CreateStyleForInsertText(Selection& aSelection,
    OwningNonNull<Text> newNode =
      EditorBase::CreateTextNode(aDoc, EmptyString());
    NS_ENSURE_STATE(mHTMLEditor);
    nsresult rv = mHTMLEditor->InsertNode(*newNode, *node, offset);
    nsresult rv =
      mHTMLEditor->InsertNode(*newNode, EditorRawDOMPoint(node, offset));
    NS_ENSURE_SUCCESS(rv, rv);
    node = newNode;
    offset = 0;
+6 −3
Original line number Diff line number Diff line
@@ -1167,8 +1167,11 @@ HTMLEditor::ReplaceHeadContentsWithHTML(const nsAString& aSourceToInsert)

  // Loop over the contents of the fragment and move into the document
  while (nsCOMPtr<nsIContent> child = docfrag->GetFirstChild()) {
    nsresult rv = InsertNode(*child, *headNode, offsetOfNewNode++);
    NS_ENSURE_SUCCESS(rv, rv);
    nsresult rv =
      InsertNode(*child, EditorRawDOMPoint(headNode, offsetOfNewNode++));
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }
  }

  return NS_OK;
@@ -1612,7 +1615,7 @@ HTMLEditor::InsertNodeAtPoint(nsIDOMNode* aNode,
    }
  }
  // Now we can insert the new node
  nsresult rv = InsertNode(*node, *parent, *ioOffset);
  nsresult rv = InsertNode(*node, EditorRawDOMPoint(parent, *ioOffset));
  if (isDocumentFragment) {
    *ioChildAtOffset = do_QueryInterface(parent->GetChildAt(*ioOffset));
  }
+34 −16
Original line number Diff line number Diff line
@@ -124,22 +124,40 @@ HTMLEditor::LoadHTML(const nsAString& aInputString)
    RefPtr<nsRange> range = selection->GetRangeAt(0);
    NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);

    // create fragment for pasted html
    nsCOMPtr<nsIDOMDocumentFragment> docfrag;
    rv = range->CreateContextualFragment(aInputString, getter_AddRefs(docfrag));
    NS_ENSURE_SUCCESS(rv, rv);
    // put the fragment into the document
    nsCOMPtr<nsINode> parent = range->GetStartContainer();
    NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
    uint32_t childOffset = range->StartOffset();

    nsCOMPtr<nsIDOMNode> nodeToInsert;
    docfrag->GetFirstChild(getter_AddRefs(nodeToInsert));
    while (nodeToInsert) {
      rv = InsertNode(nodeToInsert, GetAsDOMNode(parent),
                      static_cast<int32_t>(childOffset++));
      NS_ENSURE_SUCCESS(rv, rv);
      docfrag->GetFirstChild(getter_AddRefs(nodeToInsert));
    // Create fragment for pasted HTML.
    ErrorResult error;
    RefPtr<DocumentFragment> documentFragment =
      range->CreateContextualFragment(aInputString, error);
    if (NS_WARN_IF(error.Failed())) {
      return error.StealNSResult();
    }

    // Put the fragment into the document at start of selection.
    EditorDOMPoint pointToInsert(range->StartRef());
    // XXX We need to make pointToInsert store offset for keeping traditional
    //     behavior since using only child node to pointing insertion point
    //     changes the behavior when inserted child is moved by mutation
    //     observer.  We need to investigate what we should do here.
    Unused << pointToInsert.Offset();
    for (nsCOMPtr<nsIContent> contentToInsert =
           documentFragment->GetFirstChild();
         contentToInsert;
         contentToInsert = documentFragment->GetFirstChild()) {
      rv = InsertNode(*contentToInsert, pointToInsert.AsRaw());
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }
      // XXX If the inserted node has been moved by mutation observer,
      //     incrementing offset will cause odd result.  Next new node
      //     will be inserted after existing node and the offset will be
      //     overflown from the container node.
      pointToInsert.Set(pointToInsert.Container(), pointToInsert.Offset() + 1);
      if (NS_WARN_IF(!pointToInsert.Offset())) {
        // Append the remaining children to the container if offset is
        // overflown.
        pointToInsert.Set(pointToInsert.Container(),
                          pointToInsert.Container()->Length());
      }
    }
  }

Loading