michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: michael@0: #include "CreateElementTxn.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "nsAlgorithm.h" michael@0: #include "nsDebug.h" michael@0: #include "nsEditor.h" michael@0: #include "nsError.h" michael@0: #include "nsIContent.h" michael@0: #include "nsIDOMCharacterData.h" michael@0: #include "nsIEditor.h" michael@0: #include "nsINode.h" michael@0: #include "nsISelection.h" michael@0: #include "nsISupportsUtils.h" michael@0: #include "nsMemory.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsStringFwd.h" michael@0: #include "nsString.h" michael@0: #include "nsAString.h" michael@0: #include michael@0: michael@0: using namespace mozilla; michael@0: michael@0: CreateElementTxn::CreateElementTxn() michael@0: : EditTxn() michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_INHERITED(CreateElementTxn, EditTxn, michael@0: mParent, michael@0: mNewNode, michael@0: mRefNode) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(CreateElementTxn, EditTxn) michael@0: NS_IMPL_RELEASE_INHERITED(CreateElementTxn, EditTxn) michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CreateElementTxn) michael@0: NS_INTERFACE_MAP_END_INHERITING(EditTxn) michael@0: NS_IMETHODIMP CreateElementTxn::Init(nsEditor *aEditor, michael@0: const nsAString &aTag, michael@0: nsIDOMNode *aParent, michael@0: uint32_t aOffsetInParent) michael@0: { michael@0: NS_ASSERTION(aEditor&&aParent, "null args"); michael@0: if (!aEditor || !aParent) { return NS_ERROR_NULL_POINTER; } michael@0: michael@0: mEditor = aEditor; michael@0: mTag = aTag; michael@0: mParent = do_QueryInterface(aParent); michael@0: mOffsetInParent = aOffsetInParent; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP CreateElementTxn::DoTransaction(void) michael@0: { michael@0: NS_ASSERTION(mEditor && mParent, "bad state"); michael@0: NS_ENSURE_TRUE(mEditor && mParent, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: nsCOMPtr newContent; michael@0: michael@0: //new call to use instead to get proper HTML element, bug# 39919 michael@0: nsresult result = mEditor->CreateHTMLContent(mTag, getter_AddRefs(newContent)); michael@0: NS_ENSURE_SUCCESS(result, result); michael@0: NS_ENSURE_STATE(newContent); michael@0: michael@0: mNewNode = newContent->AsDOMNode(); michael@0: // Try to insert formatting whitespace for the new node: michael@0: mEditor->MarkNodeDirty(mNewNode); michael@0: michael@0: // insert the new node michael@0: if (CreateElementTxn::eAppend == int32_t(mOffsetInParent)) { michael@0: nsCOMPtr resultNode; michael@0: return mParent->AppendChild(mNewNode, getter_AddRefs(resultNode)); michael@0: } michael@0: michael@0: nsCOMPtr parent = do_QueryInterface(mParent); michael@0: NS_ENSURE_STATE(parent); michael@0: michael@0: mOffsetInParent = XPCOM_MIN(mOffsetInParent, parent->GetChildCount()); michael@0: michael@0: // note, it's ok for mRefNode to be null. that means append michael@0: nsIContent* refNode = parent->GetChildAt(mOffsetInParent); michael@0: mRefNode = refNode ? refNode->AsDOMNode() : nullptr; michael@0: michael@0: nsCOMPtr resultNode; michael@0: result = mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode)); michael@0: NS_ENSURE_SUCCESS(result, result); michael@0: michael@0: // only set selection to insertion point if editor gives permission michael@0: bool bAdjustSelection; michael@0: mEditor->ShouldTxnSetSelection(&bAdjustSelection); michael@0: if (!bAdjustSelection) { michael@0: // do nothing - dom range gravity will adjust selection michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr selection; michael@0: result = mEditor->GetSelection(getter_AddRefs(selection)); michael@0: NS_ENSURE_SUCCESS(result, result); michael@0: NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); michael@0: michael@0: nsCOMPtr parentContent = do_QueryInterface(mParent); michael@0: NS_ENSURE_STATE(parentContent); michael@0: michael@0: result = selection->CollapseNative(parentContent, michael@0: parentContent->IndexOf(newContent) + 1); michael@0: NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after insert."); michael@0: return result; michael@0: } michael@0: michael@0: NS_IMETHODIMP CreateElementTxn::UndoTransaction(void) michael@0: { michael@0: NS_ASSERTION(mEditor && mParent, "bad state"); michael@0: NS_ENSURE_TRUE(mEditor && mParent, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: nsCOMPtr resultNode; michael@0: return mParent->RemoveChild(mNewNode, getter_AddRefs(resultNode)); michael@0: } michael@0: michael@0: NS_IMETHODIMP CreateElementTxn::RedoTransaction(void) michael@0: { michael@0: NS_ASSERTION(mEditor && mParent, "bad state"); michael@0: NS_ENSURE_TRUE(mEditor && mParent, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: // first, reset mNewNode so it has no attributes or content michael@0: nsCOMPtrnodeAsText = do_QueryInterface(mNewNode); michael@0: if (nodeAsText) michael@0: { michael@0: nodeAsText->SetData(EmptyString()); michael@0: } michael@0: michael@0: // now, reinsert mNewNode michael@0: nsCOMPtr resultNode; michael@0: return mParent->InsertBefore(mNewNode, mRefNode, getter_AddRefs(resultNode)); michael@0: } michael@0: michael@0: NS_IMETHODIMP CreateElementTxn::GetTxnDescription(nsAString& aString) michael@0: { michael@0: aString.AssignLiteral("CreateElementTxn: "); michael@0: aString += mTag; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP CreateElementTxn::GetNewNode(nsIDOMNode **aNewNode) michael@0: { michael@0: NS_ENSURE_TRUE(aNewNode, NS_ERROR_NULL_POINTER); michael@0: NS_ENSURE_TRUE(mNewNode, NS_ERROR_NOT_INITIALIZED); michael@0: *aNewNode = mNewNode; michael@0: NS_ADDREF(*aNewNode); michael@0: return NS_OK; michael@0: }