1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/editor/libeditor/base/PlaceholderTxn.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,262 @@ 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 "PlaceholderTxn.h" 1.10 +#include "nsEditor.h" 1.11 +#include "IMETextTxn.h" 1.12 +#include "nsGkAtoms.h" 1.13 +#include "mozilla/dom/Selection.h" 1.14 + 1.15 +using namespace mozilla; 1.16 +using namespace mozilla::dom; 1.17 + 1.18 +PlaceholderTxn::PlaceholderTxn() : EditAggregateTxn(), 1.19 + mAbsorb(true), 1.20 + mForwarding(nullptr), 1.21 + mIMETextTxn(nullptr), 1.22 + mCommitted(false), 1.23 + mStartSel(nullptr), 1.24 + mEndSel(), 1.25 + mEditor(nullptr) 1.26 +{ 1.27 +} 1.28 + 1.29 +NS_IMPL_CYCLE_COLLECTION_CLASS(PlaceholderTxn) 1.30 + 1.31 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PlaceholderTxn, 1.32 + EditAggregateTxn) 1.33 + tmp->mStartSel->DoUnlink(); 1.34 + tmp->mEndSel.DoUnlink(); 1.35 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.36 + 1.37 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PlaceholderTxn, 1.38 + EditAggregateTxn) 1.39 + tmp->mStartSel->DoTraverse(cb); 1.40 + tmp->mEndSel.DoTraverse(cb); 1.41 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.42 + 1.43 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PlaceholderTxn) 1.44 + NS_INTERFACE_MAP_ENTRY(nsIAbsorbingTransaction) 1.45 + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 1.46 +NS_INTERFACE_MAP_END_INHERITING(EditAggregateTxn) 1.47 + 1.48 +NS_IMPL_ADDREF_INHERITED(PlaceholderTxn, EditAggregateTxn) 1.49 +NS_IMPL_RELEASE_INHERITED(PlaceholderTxn, EditAggregateTxn) 1.50 + 1.51 +NS_IMETHODIMP 1.52 +PlaceholderTxn::Init(nsIAtom* aName, nsSelectionState* aSelState, 1.53 + nsEditor* aEditor) 1.54 +{ 1.55 + NS_ENSURE_TRUE(aEditor && aSelState, NS_ERROR_NULL_POINTER); 1.56 + 1.57 + mName = aName; 1.58 + mStartSel = aSelState; 1.59 + mEditor = aEditor; 1.60 + return NS_OK; 1.61 +} 1.62 + 1.63 +NS_IMETHODIMP PlaceholderTxn::DoTransaction(void) 1.64 +{ 1.65 + return NS_OK; 1.66 +} 1.67 + 1.68 +NS_IMETHODIMP PlaceholderTxn::UndoTransaction(void) 1.69 +{ 1.70 + // undo txns 1.71 + nsresult res = EditAggregateTxn::UndoTransaction(); 1.72 + NS_ENSURE_SUCCESS(res, res); 1.73 + 1.74 + NS_ENSURE_TRUE(mStartSel, NS_ERROR_NULL_POINTER); 1.75 + 1.76 + // now restore selection 1.77 + nsCOMPtr<nsISelection> selection; 1.78 + res = mEditor->GetSelection(getter_AddRefs(selection)); 1.79 + NS_ENSURE_SUCCESS(res, res); 1.80 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.81 + return mStartSel->RestoreSelection(selection); 1.82 +} 1.83 + 1.84 + 1.85 +NS_IMETHODIMP PlaceholderTxn::RedoTransaction(void) 1.86 +{ 1.87 + // redo txns 1.88 + nsresult res = EditAggregateTxn::RedoTransaction(); 1.89 + NS_ENSURE_SUCCESS(res, res); 1.90 + 1.91 + // now restore selection 1.92 + nsCOMPtr<nsISelection> selection; 1.93 + res = mEditor->GetSelection(getter_AddRefs(selection)); 1.94 + NS_ENSURE_SUCCESS(res, res); 1.95 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.96 + return mEndSel.RestoreSelection(selection); 1.97 +} 1.98 + 1.99 + 1.100 +NS_IMETHODIMP PlaceholderTxn::Merge(nsITransaction *aTransaction, bool *aDidMerge) 1.101 +{ 1.102 + NS_ENSURE_TRUE(aDidMerge && aTransaction, NS_ERROR_NULL_POINTER); 1.103 + 1.104 + // set out param default value 1.105 + *aDidMerge=false; 1.106 + 1.107 + if (mForwarding) 1.108 + { 1.109 + NS_NOTREACHED("tried to merge into a placeholder that was in forwarding mode!"); 1.110 + return NS_ERROR_FAILURE; 1.111 + } 1.112 + 1.113 + // check to see if aTransaction is one of the editor's 1.114 + // private transactions. If not, we want to avoid merging 1.115 + // the foreign transaction into our placeholder since we 1.116 + // don't know what it does. 1.117 + 1.118 + nsCOMPtr<nsPIEditorTransaction> pTxn = do_QueryInterface(aTransaction); 1.119 + NS_ENSURE_TRUE(pTxn, NS_OK); // it's foreign so just bail! 1.120 + 1.121 + EditTxn *editTxn = (EditTxn*)aTransaction; //XXX: hack, not safe! need nsIEditTransaction! 1.122 + // determine if this incoming txn is a placeholder txn 1.123 + nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryObject(editTxn); 1.124 + 1.125 + // we are absorbing all txn's if mAbsorb is lit. 1.126 + if (mAbsorb) 1.127 + { 1.128 + nsRefPtr<IMETextTxn> otherTxn; 1.129 + if (NS_SUCCEEDED(aTransaction->QueryInterface(IMETextTxn::GetCID(), getter_AddRefs(otherTxn))) && otherTxn) 1.130 + { 1.131 + // special handling for IMETextTxn's: they need to merge with any previous 1.132 + // IMETextTxn in this placeholder, if possible. 1.133 + if (!mIMETextTxn) 1.134 + { 1.135 + // this is the first IME txn in the placeholder 1.136 + mIMETextTxn =otherTxn; 1.137 + AppendChild(editTxn); 1.138 + } 1.139 + else 1.140 + { 1.141 + bool didMerge; 1.142 + mIMETextTxn->Merge(otherTxn, &didMerge); 1.143 + if (!didMerge) 1.144 + { 1.145 + // it wouldn't merge. Earlier IME txn is already committed and will 1.146 + // not absorb further IME txns. So just stack this one after it 1.147 + // and remember it as a candidate for further merges. 1.148 + mIMETextTxn =otherTxn; 1.149 + AppendChild(editTxn); 1.150 + } 1.151 + } 1.152 + } 1.153 + else if (!plcTxn) // see bug 171243: just drop incoming placeholders on the floor. 1.154 + { // their children will be swallowed by this preexisting one. 1.155 + AppendChild(editTxn); 1.156 + } 1.157 + *aDidMerge = true; 1.158 +// RememberEndingSelection(); 1.159 +// efficiency hack: no need to remember selection here, as we haven't yet 1.160 +// finished the initial batch and we know we will be told when the batch ends. 1.161 +// we can remeber the selection then. 1.162 + } 1.163 + else 1.164 + { // merge typing or IME or deletion transactions if the selection matches 1.165 + if (((mName.get() == nsGkAtoms::TypingTxnName) || 1.166 + (mName.get() == nsGkAtoms::IMETxnName) || 1.167 + (mName.get() == nsGkAtoms::DeleteTxnName)) 1.168 + && !mCommitted ) 1.169 + { 1.170 + nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryObject(editTxn); 1.171 + if (plcTxn) { 1.172 + nsCOMPtr<nsIAtom> atom; 1.173 + plcTxn->GetTxnName(getter_AddRefs(atom)); 1.174 + if (atom && (atom == mName)) 1.175 + { 1.176 + // check if start selection of next placeholder matches 1.177 + // end selection of this placeholder 1.178 + bool isSame; 1.179 + plcTxn->StartSelectionEquals(&mEndSel, &isSame); 1.180 + if (isSame) 1.181 + { 1.182 + mAbsorb = true; // we need to start absorbing again 1.183 + plcTxn->ForwardEndBatchTo(this); 1.184 + // AppendChild(editTxn); 1.185 + // see bug 171243: we don't need to merge placeholders 1.186 + // into placeholders. We just reactivate merging in the pre-existing 1.187 + // placeholder and drop the new one on the floor. The EndPlaceHolderBatch() 1.188 + // call on the new placeholder will be forwarded to this older one. 1.189 + RememberEndingSelection(); 1.190 + *aDidMerge = true; 1.191 + } 1.192 + } 1.193 + } 1.194 + } 1.195 + } 1.196 + return NS_OK; 1.197 +} 1.198 + 1.199 +NS_IMETHODIMP PlaceholderTxn::GetTxnDescription(nsAString& aString) 1.200 +{ 1.201 + aString.AssignLiteral("PlaceholderTxn: "); 1.202 + 1.203 + if (mName) 1.204 + { 1.205 + nsAutoString name; 1.206 + mName->ToString(name); 1.207 + aString += name; 1.208 + } 1.209 + 1.210 + return NS_OK; 1.211 +} 1.212 + 1.213 +NS_IMETHODIMP PlaceholderTxn::GetTxnName(nsIAtom **aName) 1.214 +{ 1.215 + return GetName(aName); 1.216 +} 1.217 + 1.218 +NS_IMETHODIMP PlaceholderTxn::StartSelectionEquals(nsSelectionState *aSelState, bool *aResult) 1.219 +{ 1.220 + // determine if starting selection matches the given selection state. 1.221 + // note that we only care about collapsed selections. 1.222 + NS_ENSURE_TRUE(aResult && aSelState, NS_ERROR_NULL_POINTER); 1.223 + if (!mStartSel->IsCollapsed() || !aSelState->IsCollapsed()) 1.224 + { 1.225 + *aResult = false; 1.226 + return NS_OK; 1.227 + } 1.228 + *aResult = mStartSel->IsEqual(aSelState); 1.229 + return NS_OK; 1.230 +} 1.231 + 1.232 +NS_IMETHODIMP PlaceholderTxn::EndPlaceHolderBatch() 1.233 +{ 1.234 + mAbsorb = false; 1.235 + 1.236 + if (mForwarding) 1.237 + { 1.238 + nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryReferent(mForwarding); 1.239 + if (plcTxn) plcTxn->EndPlaceHolderBatch(); 1.240 + } 1.241 + 1.242 + // remember our selection state. 1.243 + return RememberEndingSelection(); 1.244 +} 1.245 + 1.246 +NS_IMETHODIMP PlaceholderTxn::ForwardEndBatchTo(nsIAbsorbingTransaction *aForwardingAddress) 1.247 +{ 1.248 + mForwarding = do_GetWeakReference(aForwardingAddress); 1.249 + return NS_OK; 1.250 +} 1.251 + 1.252 +NS_IMETHODIMP PlaceholderTxn::Commit() 1.253 +{ 1.254 + mCommitted = true; 1.255 + return NS_OK; 1.256 +} 1.257 + 1.258 +NS_IMETHODIMP PlaceholderTxn::RememberEndingSelection() 1.259 +{ 1.260 + nsRefPtr<Selection> selection = mEditor->GetSelection(); 1.261 + NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); 1.262 + mEndSel.SaveSelection(selection); 1.263 + return NS_OK; 1.264 +} 1.265 +