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 "PlaceholderTxn.h" michael@0: #include "nsEditor.h" michael@0: #include "IMETextTxn.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "mozilla/dom/Selection.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: PlaceholderTxn::PlaceholderTxn() : EditAggregateTxn(), michael@0: mAbsorb(true), michael@0: mForwarding(nullptr), michael@0: mIMETextTxn(nullptr), michael@0: mCommitted(false), michael@0: mStartSel(nullptr), michael@0: mEndSel(), michael@0: mEditor(nullptr) michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(PlaceholderTxn) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PlaceholderTxn, michael@0: EditAggregateTxn) michael@0: tmp->mStartSel->DoUnlink(); michael@0: tmp->mEndSel.DoUnlink(); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PlaceholderTxn, michael@0: EditAggregateTxn) michael@0: tmp->mStartSel->DoTraverse(cb); michael@0: tmp->mEndSel.DoTraverse(cb); michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PlaceholderTxn) michael@0: NS_INTERFACE_MAP_ENTRY(nsIAbsorbingTransaction) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) michael@0: NS_INTERFACE_MAP_END_INHERITING(EditAggregateTxn) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(PlaceholderTxn, EditAggregateTxn) michael@0: NS_IMPL_RELEASE_INHERITED(PlaceholderTxn, EditAggregateTxn) michael@0: michael@0: NS_IMETHODIMP michael@0: PlaceholderTxn::Init(nsIAtom* aName, nsSelectionState* aSelState, michael@0: nsEditor* aEditor) michael@0: { michael@0: NS_ENSURE_TRUE(aEditor && aSelState, NS_ERROR_NULL_POINTER); michael@0: michael@0: mName = aName; michael@0: mStartSel = aSelState; michael@0: mEditor = aEditor; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP PlaceholderTxn::DoTransaction(void) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP PlaceholderTxn::UndoTransaction(void) michael@0: { michael@0: // undo txns michael@0: nsresult res = EditAggregateTxn::UndoTransaction(); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: NS_ENSURE_TRUE(mStartSel, NS_ERROR_NULL_POINTER); michael@0: michael@0: // now restore selection michael@0: nsCOMPtr selection; michael@0: res = mEditor->GetSelection(getter_AddRefs(selection)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); michael@0: return mStartSel->RestoreSelection(selection); michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP PlaceholderTxn::RedoTransaction(void) michael@0: { michael@0: // redo txns michael@0: nsresult res = EditAggregateTxn::RedoTransaction(); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: michael@0: // now restore selection michael@0: nsCOMPtr selection; michael@0: res = mEditor->GetSelection(getter_AddRefs(selection)); michael@0: NS_ENSURE_SUCCESS(res, res); michael@0: NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); michael@0: return mEndSel.RestoreSelection(selection); michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP PlaceholderTxn::Merge(nsITransaction *aTransaction, bool *aDidMerge) michael@0: { michael@0: NS_ENSURE_TRUE(aDidMerge && aTransaction, NS_ERROR_NULL_POINTER); michael@0: michael@0: // set out param default value michael@0: *aDidMerge=false; michael@0: michael@0: if (mForwarding) michael@0: { michael@0: NS_NOTREACHED("tried to merge into a placeholder that was in forwarding mode!"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // check to see if aTransaction is one of the editor's michael@0: // private transactions. If not, we want to avoid merging michael@0: // the foreign transaction into our placeholder since we michael@0: // don't know what it does. michael@0: michael@0: nsCOMPtr pTxn = do_QueryInterface(aTransaction); michael@0: NS_ENSURE_TRUE(pTxn, NS_OK); // it's foreign so just bail! michael@0: michael@0: EditTxn *editTxn = (EditTxn*)aTransaction; //XXX: hack, not safe! need nsIEditTransaction! michael@0: // determine if this incoming txn is a placeholder txn michael@0: nsCOMPtr plcTxn = do_QueryObject(editTxn); michael@0: michael@0: // we are absorbing all txn's if mAbsorb is lit. michael@0: if (mAbsorb) michael@0: { michael@0: nsRefPtr otherTxn; michael@0: if (NS_SUCCEEDED(aTransaction->QueryInterface(IMETextTxn::GetCID(), getter_AddRefs(otherTxn))) && otherTxn) michael@0: { michael@0: // special handling for IMETextTxn's: they need to merge with any previous michael@0: // IMETextTxn in this placeholder, if possible. michael@0: if (!mIMETextTxn) michael@0: { michael@0: // this is the first IME txn in the placeholder michael@0: mIMETextTxn =otherTxn; michael@0: AppendChild(editTxn); michael@0: } michael@0: else michael@0: { michael@0: bool didMerge; michael@0: mIMETextTxn->Merge(otherTxn, &didMerge); michael@0: if (!didMerge) michael@0: { michael@0: // it wouldn't merge. Earlier IME txn is already committed and will michael@0: // not absorb further IME txns. So just stack this one after it michael@0: // and remember it as a candidate for further merges. michael@0: mIMETextTxn =otherTxn; michael@0: AppendChild(editTxn); michael@0: } michael@0: } michael@0: } michael@0: else if (!plcTxn) // see bug 171243: just drop incoming placeholders on the floor. michael@0: { // their children will be swallowed by this preexisting one. michael@0: AppendChild(editTxn); michael@0: } michael@0: *aDidMerge = true; michael@0: // RememberEndingSelection(); michael@0: // efficiency hack: no need to remember selection here, as we haven't yet michael@0: // finished the initial batch and we know we will be told when the batch ends. michael@0: // we can remeber the selection then. michael@0: } michael@0: else michael@0: { // merge typing or IME or deletion transactions if the selection matches michael@0: if (((mName.get() == nsGkAtoms::TypingTxnName) || michael@0: (mName.get() == nsGkAtoms::IMETxnName) || michael@0: (mName.get() == nsGkAtoms::DeleteTxnName)) michael@0: && !mCommitted ) michael@0: { michael@0: nsCOMPtr plcTxn = do_QueryObject(editTxn); michael@0: if (plcTxn) { michael@0: nsCOMPtr atom; michael@0: plcTxn->GetTxnName(getter_AddRefs(atom)); michael@0: if (atom && (atom == mName)) michael@0: { michael@0: // check if start selection of next placeholder matches michael@0: // end selection of this placeholder michael@0: bool isSame; michael@0: plcTxn->StartSelectionEquals(&mEndSel, &isSame); michael@0: if (isSame) michael@0: { michael@0: mAbsorb = true; // we need to start absorbing again michael@0: plcTxn->ForwardEndBatchTo(this); michael@0: // AppendChild(editTxn); michael@0: // see bug 171243: we don't need to merge placeholders michael@0: // into placeholders. We just reactivate merging in the pre-existing michael@0: // placeholder and drop the new one on the floor. The EndPlaceHolderBatch() michael@0: // call on the new placeholder will be forwarded to this older one. michael@0: RememberEndingSelection(); michael@0: *aDidMerge = true; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP PlaceholderTxn::GetTxnDescription(nsAString& aString) michael@0: { michael@0: aString.AssignLiteral("PlaceholderTxn: "); michael@0: michael@0: if (mName) michael@0: { michael@0: nsAutoString name; michael@0: mName->ToString(name); michael@0: aString += name; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP PlaceholderTxn::GetTxnName(nsIAtom **aName) michael@0: { michael@0: return GetName(aName); michael@0: } michael@0: michael@0: NS_IMETHODIMP PlaceholderTxn::StartSelectionEquals(nsSelectionState *aSelState, bool *aResult) michael@0: { michael@0: // determine if starting selection matches the given selection state. michael@0: // note that we only care about collapsed selections. michael@0: NS_ENSURE_TRUE(aResult && aSelState, NS_ERROR_NULL_POINTER); michael@0: if (!mStartSel->IsCollapsed() || !aSelState->IsCollapsed()) michael@0: { michael@0: *aResult = false; michael@0: return NS_OK; michael@0: } michael@0: *aResult = mStartSel->IsEqual(aSelState); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP PlaceholderTxn::EndPlaceHolderBatch() michael@0: { michael@0: mAbsorb = false; michael@0: michael@0: if (mForwarding) michael@0: { michael@0: nsCOMPtr plcTxn = do_QueryReferent(mForwarding); michael@0: if (plcTxn) plcTxn->EndPlaceHolderBatch(); michael@0: } michael@0: michael@0: // remember our selection state. michael@0: return RememberEndingSelection(); michael@0: } michael@0: michael@0: NS_IMETHODIMP PlaceholderTxn::ForwardEndBatchTo(nsIAbsorbingTransaction *aForwardingAddress) michael@0: { michael@0: mForwarding = do_GetWeakReference(aForwardingAddress); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP PlaceholderTxn::Commit() michael@0: { michael@0: mCommitted = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP PlaceholderTxn::RememberEndingSelection() michael@0: { michael@0: nsRefPtr selection = mEditor->GetSelection(); michael@0: NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); michael@0: mEndSel.SaveSelection(selection); michael@0: return NS_OK; michael@0: } michael@0: