1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/editor/libeditor/base/DeleteRangeTxn.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,231 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "DeleteNodeTxn.h" 1.10 +#include "DeleteRangeTxn.h" 1.11 +#include "DeleteTextTxn.h" 1.12 +#include "mozilla/Assertions.h" 1.13 +#include "mozilla/dom/Selection.h" 1.14 +#include "mozilla/mozalloc.h" 1.15 +#include "nsCOMPtr.h" 1.16 +#include "nsDebug.h" 1.17 +#include "nsEditor.h" 1.18 +#include "nsError.h" 1.19 +#include "nsIContent.h" 1.20 +#include "nsIContentIterator.h" 1.21 +#include "nsIDOMCharacterData.h" 1.22 +#include "nsINode.h" 1.23 +#include "nsAString.h" 1.24 + 1.25 +class nsIDOMRange; 1.26 + 1.27 +using namespace mozilla; 1.28 +using namespace mozilla::dom; 1.29 + 1.30 +// note that aEditor is not refcounted 1.31 +DeleteRangeTxn::DeleteRangeTxn() 1.32 + : EditAggregateTxn(), 1.33 + mRange(), 1.34 + mEditor(nullptr), 1.35 + mRangeUpdater(nullptr) 1.36 +{ 1.37 +} 1.38 + 1.39 +NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteRangeTxn, EditAggregateTxn, 1.40 + mRange) 1.41 + 1.42 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteRangeTxn) 1.43 +NS_INTERFACE_MAP_END_INHERITING(EditAggregateTxn) 1.44 + 1.45 +nsresult 1.46 +DeleteRangeTxn::Init(nsEditor* aEditor, 1.47 + nsRange* aRange, 1.48 + nsRangeUpdater* aRangeUpdater) 1.49 +{ 1.50 + MOZ_ASSERT(aEditor && aRange); 1.51 + 1.52 + mEditor = aEditor; 1.53 + mRange = aRange->CloneRange(); 1.54 + mRangeUpdater = aRangeUpdater; 1.55 + 1.56 + NS_ENSURE_TRUE(mEditor->IsModifiableNode(mRange->GetStartParent()), 1.57 + NS_ERROR_FAILURE); 1.58 + NS_ENSURE_TRUE(mEditor->IsModifiableNode(mRange->GetEndParent()), 1.59 + NS_ERROR_FAILURE); 1.60 + NS_ENSURE_TRUE(mEditor->IsModifiableNode(mRange->GetCommonAncestor()), 1.61 + NS_ERROR_FAILURE); 1.62 + 1.63 + return NS_OK; 1.64 +} 1.65 + 1.66 +NS_IMETHODIMP 1.67 +DeleteRangeTxn::DoTransaction() 1.68 +{ 1.69 + MOZ_ASSERT(mRange && mEditor); 1.70 + nsresult res; 1.71 + 1.72 + // build the child transactions 1.73 + nsCOMPtr<nsINode> startParent = mRange->GetStartParent(); 1.74 + int32_t startOffset = mRange->StartOffset(); 1.75 + nsCOMPtr<nsINode> endParent = mRange->GetEndParent(); 1.76 + int32_t endOffset = mRange->EndOffset(); 1.77 + MOZ_ASSERT(startParent && endParent); 1.78 + 1.79 + if (startParent == endParent) { 1.80 + // the selection begins and ends in the same node 1.81 + res = CreateTxnsToDeleteBetween(startParent, startOffset, endOffset); 1.82 + NS_ENSURE_SUCCESS(res, res); 1.83 + } else { 1.84 + // the selection ends in a different node from where it started. delete 1.85 + // the relevant content in the start node 1.86 + res = CreateTxnsToDeleteContent(startParent, startOffset, nsIEditor::eNext); 1.87 + NS_ENSURE_SUCCESS(res, res); 1.88 + // delete the intervening nodes 1.89 + res = CreateTxnsToDeleteNodesBetween(); 1.90 + NS_ENSURE_SUCCESS(res, res); 1.91 + // delete the relevant content in the end node 1.92 + res = CreateTxnsToDeleteContent(endParent, endOffset, nsIEditor::ePrevious); 1.93 + NS_ENSURE_SUCCESS(res, res); 1.94 + } 1.95 + 1.96 + // if we've successfully built this aggregate transaction, then do it. 1.97 + res = EditAggregateTxn::DoTransaction(); 1.98 + NS_ENSURE_SUCCESS(res, res); 1.99 + 1.100 + // only set selection to deletion point if editor gives permission 1.101 + bool bAdjustSelection; 1.102 + mEditor->ShouldTxnSetSelection(&bAdjustSelection); 1.103 + if (bAdjustSelection) { 1.104 + nsRefPtr<Selection> selection = mEditor->GetSelection(); 1.105 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.106 + res = selection->Collapse(startParent, startOffset); 1.107 + NS_ENSURE_SUCCESS(res, res); 1.108 + } 1.109 + // else do nothing - dom range gravity will adjust selection 1.110 + 1.111 + return NS_OK; 1.112 +} 1.113 + 1.114 +NS_IMETHODIMP 1.115 +DeleteRangeTxn::UndoTransaction() 1.116 +{ 1.117 + MOZ_ASSERT(mRange && mEditor); 1.118 + 1.119 + return EditAggregateTxn::UndoTransaction(); 1.120 +} 1.121 + 1.122 +NS_IMETHODIMP 1.123 +DeleteRangeTxn::RedoTransaction() 1.124 +{ 1.125 + MOZ_ASSERT(mRange && mEditor); 1.126 + 1.127 + return EditAggregateTxn::RedoTransaction(); 1.128 +} 1.129 + 1.130 +NS_IMETHODIMP 1.131 +DeleteRangeTxn::GetTxnDescription(nsAString& aString) 1.132 +{ 1.133 + aString.AssignLiteral("DeleteRangeTxn"); 1.134 + return NS_OK; 1.135 +} 1.136 + 1.137 +nsresult 1.138 +DeleteRangeTxn::CreateTxnsToDeleteBetween(nsINode* aNode, 1.139 + int32_t aStartOffset, 1.140 + int32_t aEndOffset) 1.141 +{ 1.142 + // see what kind of node we have 1.143 + if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) { 1.144 + // if the node is a chardata node, then delete chardata content 1.145 + nsRefPtr<DeleteTextTxn> txn = new DeleteTextTxn(); 1.146 + 1.147 + int32_t numToDel; 1.148 + if (aStartOffset == aEndOffset) { 1.149 + numToDel = 1; 1.150 + } else { 1.151 + numToDel = aEndOffset - aStartOffset; 1.152 + } 1.153 + 1.154 + nsCOMPtr<nsIDOMCharacterData> charDataNode = do_QueryInterface(aNode); 1.155 + nsresult res = txn->Init(mEditor, charDataNode, aStartOffset, numToDel, 1.156 + mRangeUpdater); 1.157 + NS_ENSURE_SUCCESS(res, res); 1.158 + 1.159 + AppendChild(txn); 1.160 + return NS_OK; 1.161 + } 1.162 + 1.163 + nsCOMPtr<nsIContent> child = aNode->GetChildAt(aStartOffset); 1.164 + NS_ENSURE_STATE(child); 1.165 + 1.166 + nsresult res = NS_OK; 1.167 + for (int32_t i = aStartOffset; i < aEndOffset; ++i) { 1.168 + nsRefPtr<DeleteNodeTxn> txn = new DeleteNodeTxn(); 1.169 + res = txn->Init(mEditor, child, mRangeUpdater); 1.170 + if (NS_SUCCEEDED(res)) { 1.171 + AppendChild(txn); 1.172 + } 1.173 + 1.174 + child = child->GetNextSibling(); 1.175 + } 1.176 + 1.177 + NS_ENSURE_SUCCESS(res, res); 1.178 + return NS_OK; 1.179 +} 1.180 + 1.181 +nsresult 1.182 +DeleteRangeTxn::CreateTxnsToDeleteContent(nsINode* aNode, 1.183 + int32_t aOffset, 1.184 + nsIEditor::EDirection aAction) 1.185 +{ 1.186 + // see what kind of node we have 1.187 + if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) { 1.188 + // if the node is a chardata node, then delete chardata content 1.189 + uint32_t start, numToDelete; 1.190 + if (nsIEditor::eNext == aAction) { 1.191 + start = aOffset; 1.192 + numToDelete = aNode->Length() - aOffset; 1.193 + } else { 1.194 + start = 0; 1.195 + numToDelete = aOffset; 1.196 + } 1.197 + 1.198 + if (numToDelete) { 1.199 + nsRefPtr<DeleteTextTxn> txn = new DeleteTextTxn(); 1.200 + 1.201 + nsCOMPtr<nsIDOMCharacterData> charDataNode = do_QueryInterface(aNode); 1.202 + nsresult res = txn->Init(mEditor, charDataNode, start, numToDelete, 1.203 + mRangeUpdater); 1.204 + NS_ENSURE_SUCCESS(res, res); 1.205 + 1.206 + AppendChild(txn); 1.207 + } 1.208 + } 1.209 + 1.210 + return NS_OK; 1.211 +} 1.212 + 1.213 +nsresult 1.214 +DeleteRangeTxn::CreateTxnsToDeleteNodesBetween() 1.215 +{ 1.216 + nsCOMPtr<nsIContentIterator> iter = NS_NewContentSubtreeIterator(); 1.217 + 1.218 + nsresult res = iter->Init(mRange); 1.219 + NS_ENSURE_SUCCESS(res, res); 1.220 + 1.221 + while (!iter->IsDone()) { 1.222 + nsCOMPtr<nsINode> node = iter->GetCurrentNode(); 1.223 + NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER); 1.224 + 1.225 + nsRefPtr<DeleteNodeTxn> txn = new DeleteNodeTxn(); 1.226 + 1.227 + res = txn->Init(mEditor, node, mRangeUpdater); 1.228 + NS_ENSURE_SUCCESS(res, res); 1.229 + AppendChild(txn); 1.230 + 1.231 + iter->Next(); 1.232 + } 1.233 + return NS_OK; 1.234 +}