Bug 1056166 part 6 - Clean up IMETextTxn; r=ehsan

This commit is contained in:
Aryeh Gregor 2014-08-29 14:43:24 +03:00
parent 71324b4c4a
commit c1bae97988
6 changed files with 162 additions and 217 deletions

View File

@ -4,149 +4,111 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "IMETextTxn.h"
#include "mozilla/DebugOnly.h" // for DebugOnly
#include "mozilla/mozalloc.h" // for operator new
#include "mozilla/TextEvents.h" // for TextRangeStyle
#include "nsAString.h" // for nsAString_internal::Length, etc
#include "nsAutoPtr.h" // for nsRefPtr
#include "mozilla/dom/Selection.h" // local var
#include "mozilla/dom/Text.h" // mTextNode
#include "nsAString.h" // params
#include "nsDebug.h" // for NS_ASSERTION, etc
#include "nsEditor.h" // mEditor
#include "nsError.h" // for NS_SUCCEEDED, NS_FAILED, etc
#include "nsIDOMCharacterData.h" // for nsIDOMCharacterData
#include "nsIDOMRange.h" // for nsRange::SetEnd, etc
#include "nsIContent.h" // for nsIContent
#include "nsIEditor.h" // for nsIEditor
#include "nsIPresShell.h" // for SelectionType
#include "nsISelection.h" // for nsISelection
#include "nsISelectionController.h" // for nsISelectionController, etc
#include "nsISelectionPrivate.h" // for nsISelectionPrivate
#include "nsISupportsImpl.h" // for nsRange::AddRef, etc
#include "nsISupportsUtils.h" // for NS_ADDREF_THIS, NS_RELEASE
#include "nsITransaction.h" // for nsITransaction
#include "nsRange.h" // for nsRange
#include "nsString.h" // for nsString
#include "nsIPresShell.h" // nsISelectionController constants
#include "nsRange.h" // local var
using namespace mozilla;
using namespace mozilla::dom;
// #define DEBUG_IMETXN
IMETextTxn::IMETextTxn()
IMETextTxn::IMETextTxn(Text& aTextNode, uint32_t aOffset,
uint32_t aReplaceLength,
TextRangeArray* aTextRangeArray,
const nsAString& aStringToInsert,
nsEditor& aEditor)
: EditTxn()
, mTextNode(&aTextNode)
, mOffset(aOffset)
, mReplaceLength(aReplaceLength)
, mRanges(aTextRangeArray)
, mStringToInsert(aStringToInsert)
, mEditor(aEditor)
, mFixed(false)
{
}
IMETextTxn::~IMETextTxn()
{
}
NS_IMPL_CYCLE_COLLECTION_INHERITED(IMETextTxn, EditTxn,
mElement)
mTextNode)
// mRangeList can't lead to cycles
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMETextTxn)
if (aIID.Equals(IMETextTxn::GetCID())) {
*aInstancePtr = (void*)(IMETextTxn*)this;
NS_ADDREF_THIS();
return NS_OK;
} else
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsITransaction, IMETextTxn)
NS_INTERFACE_MAP_END_INHERITING(EditTxn)
NS_IMETHODIMP IMETextTxn::Init(nsIDOMCharacterData *aElement,
uint32_t aOffset,
uint32_t aReplaceLength,
TextRangeArray *aTextRangeArray,
const nsAString &aStringToInsert,
nsIEditor *aEditor)
NS_IMPL_ADDREF_INHERITED(IMETextTxn, EditTxn)
NS_IMPL_RELEASE_INHERITED(IMETextTxn, EditTxn)
NS_IMETHODIMP
IMETextTxn::DoTransaction()
{
NS_ENSURE_ARG_POINTER(aElement);
mElement = aElement;
mOffset = aOffset;
mReplaceLength = aReplaceLength;
mStringToInsert = aStringToInsert;
mEditor = aEditor;
mRanges = aTextRangeArray;
mFixed = false;
// Fail before making any changes if there's no selection controller
nsCOMPtr<nsISelectionController> selCon;
mEditor.GetSelectionController(getter_AddRefs(selCon));
NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
// Advance caret: This requires the presentation shell to get the selection.
nsresult res;
if (mReplaceLength == 0) {
res = mTextNode->InsertData(mOffset, mStringToInsert);
} else {
res = mTextNode->ReplaceData(mOffset, mReplaceLength, mStringToInsert);
}
NS_ENSURE_SUCCESS(res, res);
res = SetSelectionForRanges();
NS_ENSURE_SUCCESS(res, res);
return NS_OK;
}
NS_IMETHODIMP IMETextTxn::DoTransaction(void)
NS_IMETHODIMP
IMETextTxn::UndoTransaction()
{
// Get the selection first so we'll fail before making any changes if we
// can't get it
nsRefPtr<Selection> selection = mEditor.GetSelection();
NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED);
#ifdef DEBUG_IMETXN
printf("Do IME Text element = %p replace = %d len = %d\n", mElement.get(), mReplaceLength, mStringToInsert.Length());
#endif
nsresult res = mTextNode->DeleteData(mOffset, mStringToInsert.Length());
NS_ENSURE_SUCCESS(res, res);
nsCOMPtr<nsISelectionController> selCon;
mEditor->GetSelectionController(getter_AddRefs(selCon));
NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
// set the selection to the insertion point where the string was removed
res = selection->Collapse(mTextNode, mOffset);
NS_ASSERTION(NS_SUCCEEDED(res),
"Selection could not be collapsed after undo of IME insert.");
NS_ENSURE_SUCCESS(res, res);
// advance caret: This requires the presentation shell to get the selection.
nsresult result;
if (mReplaceLength == 0) {
result = mElement->InsertData(mOffset, mStringToInsert);
} else {
result = mElement->ReplaceData(mOffset, mReplaceLength, mStringToInsert);
}
if (NS_SUCCEEDED(result)) {
result = SetSelectionForRanges();
}
return result;
return NS_OK;
}
NS_IMETHODIMP IMETextTxn::UndoTransaction(void)
NS_IMETHODIMP
IMETextTxn::Merge(nsITransaction* aTransaction, bool* aDidMerge)
{
#ifdef DEBUG_IMETXN
printf("Undo IME Text element = %p\n", mElement.get());
#endif
NS_ENSURE_ARG_POINTER(aTransaction && aDidMerge);
nsCOMPtr<nsISelectionController> selCon;
mEditor->GetSelectionController(getter_AddRefs(selCon));
NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
nsresult result = mElement->DeleteData(mOffset, mStringToInsert.Length());
if (NS_SUCCEEDED(result))
{ // set the selection to the insertion point where the string was removed
nsCOMPtr<nsISelection> selection;
result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
if (NS_SUCCEEDED(result) && selection) {
result = selection->Collapse(mElement, mOffset);
NS_ASSERTION((NS_SUCCEEDED(result)), "selection could not be collapsed after undo of IME insert.");
}
}
return result;
}
NS_IMETHODIMP IMETextTxn::Merge(nsITransaction *aTransaction, bool *aDidMerge)
{
NS_ASSERTION(aDidMerge, "illegal vaule- null ptr- aDidMerge");
NS_ASSERTION(aTransaction, "illegal vaule- null ptr- aTransaction");
NS_ENSURE_TRUE(aDidMerge && aTransaction, NS_ERROR_NULL_POINTER);
#ifdef DEBUG_IMETXN
printf("Merge IME Text element = %p\n", mElement.get());
#endif
//
// check to make sure we aren't fixed, if we are then nothing get's absorbed
//
// Check to make sure we aren't fixed, if we are then nothing gets absorbed
if (mFixed) {
*aDidMerge = false;
return NS_OK;
}
//
// if aTransaction is another IMETextTxn then absorb it
//
IMETextTxn* otherTxn = nullptr;
nsresult result = aTransaction->QueryInterface(IMETextTxn::GetCID(),(void**)&otherTxn);
if (otherTxn && NS_SUCCEEDED(result))
{
//
// we absorb the next IME transaction by adopting its insert string as our own
//
// If aTransaction is another IMETextTxn then absorb it
nsRefPtr<IMETextTxn> otherTxn = do_QueryObject(aTransaction);
if (otherTxn) {
// We absorb the next IME transaction by adopting its insert string
mStringToInsert = otherTxn->mStringToInsert;
mRanges = otherTxn->mRanges;
*aDidMerge = true;
#ifdef DEBUG_IMETXN
printf("IMETextTxn assimilated IMETextTxn:%p\n", aTransaction);
#endif
NS_RELEASE(otherTxn);
return NS_OK;
}
@ -154,20 +116,21 @@ NS_IMETHODIMP IMETextTxn::Merge(nsITransaction *aTransaction, bool *aDidMerge)
return NS_OK;
}
NS_IMETHODIMP IMETextTxn::MarkFixed(void)
void
IMETextTxn::MarkFixed()
{
mFixed = true;
return NS_OK;
}
NS_IMETHODIMP IMETextTxn::GetTxnDescription(nsAString& aString)
NS_IMETHODIMP
IMETextTxn::GetTxnDescription(nsAString& aString)
{
aString.AssignLiteral("IMETextTxn: ");
aString += mStringToInsert;
return NS_OK;
}
/* ============ protected methods ================== */
/* ============ private methods ================== */
static SelectionType
ToSelectionType(uint32_t aTextRangeType)
{
@ -189,18 +152,10 @@ ToSelectionType(uint32_t aTextRangeType)
nsresult
IMETextTxn::SetSelectionForRanges()
{
nsCOMPtr<nsISelectionController> selCon;
mEditor->GetSelectionController(getter_AddRefs(selCon));
NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
nsRefPtr<Selection> selection = mEditor.GetSelection();
NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED);
nsCOMPtr<nsISelection> selection;
nsresult rv =
selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
getter_AddRefs(selection));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
rv = selPriv->StartBatchChanges();
nsresult rv = selection->StartBatchChanges();
NS_ENSURE_SUCCESS(rv, rv);
// First, remove all selections of IME composition.
@ -210,13 +165,18 @@ IMETextTxn::SetSelectionForRanges()
nsISelectionController::SELECTION_IME_CONVERTEDTEXT,
nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT
};
nsCOMPtr<nsISelectionController> selCon;
mEditor.GetSelectionController(getter_AddRefs(selCon));
NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
for (uint32_t i = 0; i < ArrayLength(kIMESelections); ++i) {
nsCOMPtr<nsISelection> selectionOfIME;
if (NS_FAILED(selCon->GetSelection(kIMESelections[i],
getter_AddRefs(selectionOfIME)))) {
continue;
}
DebugOnly<nsresult> rv = selectionOfIME->RemoveAllRanges();
rv = selectionOfIME->RemoveAllRanges();
NS_ASSERTION(NS_SUCCEEDED(rv),
"Failed to remove all ranges of IME selection");
}
@ -224,15 +184,14 @@ IMETextTxn::SetSelectionForRanges()
// Set caret position and selection of IME composition with TextRangeArray.
bool setCaret = false;
uint32_t countOfRanges = mRanges ? mRanges->Length() : 0;
#ifdef DEBUG
// When this sets selection (caret) offset to out of the content of
// the editor, let's crash the process only on debug build. That makes such
// bugs detectable with automated tests.
uint32_t maxOffset = UINT32_MAX;
mElement->GetLength(&maxOffset);
// Bounds-checking on debug builds
uint32_t maxOffset = mTextNode->Length();
#endif
// The mStringToInsert may be truncated if maxlength attribute value doesn't
// allow to input all text of this composition. So, we can get actual length
// allow input of all text of this composition. So, we can get actual length
// of the inserted string from it.
uint32_t insertedLength = mStringToInsert.Length();
for (uint32_t i = 0; i < countOfRanges; ++i) {
@ -247,13 +206,13 @@ IMETextTxn::SetSelectionForRanges()
mOffset + std::min(textRange.mStartOffset, insertedLength));
MOZ_ASSERT(caretOffset >= 0 &&
static_cast<uint32_t>(caretOffset) <= maxOffset);
rv = selection->Collapse(mElement, caretOffset);
rv = selection->Collapse(mTextNode, caretOffset);
setCaret = setCaret || NS_SUCCEEDED(rv);
NS_ASSERTION(setCaret, "Failed to collapse normal selection");
continue;
}
// If the clause length is 0, it's should be a bug.
// If the clause length is 0, it should be a bug.
if (!textRange.Length()) {
NS_WARNING("Any clauses must not be empty");
continue;
@ -268,8 +227,8 @@ IMETextTxn::SetSelectionForRanges()
mOffset + std::min(textRange.mEndOffset, insertedLength));
MOZ_ASSERT(endOffset >= startOffset &&
static_cast<uint32_t>(endOffset) <= maxOffset);
rv = nsRange::CreateRange(mElement, startOffset,
mElement, endOffset,
rv = nsRange::CreateRange(mTextNode, startOffset,
mTextNode, endOffset,
getter_AddRefs(clauseRange));
if (NS_FAILED(rv)) {
NS_WARNING("Failed to create a DOM range for a clause of composition");
@ -312,14 +271,13 @@ IMETextTxn::SetSelectionForRanges()
int32_t caretOffset = static_cast<int32_t>(mOffset + insertedLength);
MOZ_ASSERT(caretOffset >= 0 &&
static_cast<uint32_t>(caretOffset) <= maxOffset);
rv = selection->Collapse(mElement, caretOffset);
rv = selection->Collapse(mTextNode, caretOffset);
NS_ASSERTION(NS_SUCCEEDED(rv),
"Failed to set caret at the end of composition string");
}
rv = selPriv->EndBatchChanges();
rv = selection->EndBatchChanges();
NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to end batch changes");
return rv;
}

View File

@ -6,86 +6,82 @@
#ifndef IMETextTxn_h__
#define IMETextTxn_h__
#include "EditTxn.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsID.h"
#include "nsIDOMCharacterData.h"
#include "nsString.h"
#include "nscore.h"
#include "mozilla/TextRange.h"
#include "EditTxn.h" // base class
#include "nsAutoPtr.h" // mTextNode, mRanges
#include "nsCycleCollectionParticipant.h" // various macros
#include "nsString.h" // mStringToInsert
class nsITransaction;
class nsEditor;
// {D4D25721-2813-11d3-9EA3-0060089FE59B}
#define IME_TEXT_TXN_CID \
{0xd4d25721, 0x2813, 0x11d3, \
{0x9e, 0xa3, 0x0, 0x60, 0x8, 0x9f, 0xe5, 0x9b }}
#define NS_IMETEXTTXN_IID \
{ 0xb391355d, 0x346c, 0x43d1, \
{ 0x85, 0xed, 0x9e, 0x65, 0xbe, 0xe7, 0x7e, 0x48 } }
namespace mozilla {
class nsIEditor;
class TextRangeArray;
namespace dom {
class Text;
/**
* A transaction that inserts text into a content node.
* A transaction that inserts text into a content node.
*/
class IMETextTxn : public EditTxn
{
public:
static const nsIID& GetCID() { static const nsIID iid = IME_TEXT_TXN_CID; return iid; }
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IMETEXTTXN_IID)
/** initialize the transaction
* @param aElement the text content node
* @param aOffset the location in aElement to do the insertion
/** @param aTextNode the text content node
* @param aOffset the location in aTextNode to do the insertion
* @param aReplaceLength the length of text to replace (0 == no replacement)
* @param aTextRangeArray clauses and/or caret information. This may be null.
* @param aString the new text to insert
* @param aSelCon used to get and set the selection
* @param aString the new text to insert
* @param aEditor used to get and set the selection
*/
NS_IMETHOD Init(nsIDOMCharacterData *aElement,
uint32_t aOffset,
uint32_t aReplaceLength,
mozilla::TextRangeArray* aTextRangeArray,
const nsAString& aString,
nsIEditor* aEditor);
IMETextTxn();
IMETextTxn(Text& aTextNode, uint32_t aOffset, uint32_t aReplaceLength,
TextRangeArray* aTextRangeArray, const nsAString& aString,
nsEditor& aEditor);
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IMETextTxn, EditTxn)
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_EDITTXN
NS_IMETHOD Merge(nsITransaction *aTransaction, bool *aDidMerge);
NS_IMETHOD Merge(nsITransaction* aTransaction, bool* aDidMerge) MOZ_OVERRIDE;
NS_IMETHOD MarkFixed(void);
void MarkFixed();
// nsISupports declarations
// override QueryInterface to handle IMETextTxn request
NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
protected:
private:
~IMETextTxn();
nsresult SetSelectionForRanges();
/** the text element to operate upon */
nsCOMPtr<nsIDOMCharacterData> mElement;
/** the offsets into mElement where the insertion should be placed*/
/** The text element to operate upon */
nsRefPtr<Text> mTextNode;
/** The offsets into mTextNode where the insertion should be placed */
uint32_t mOffset;
uint32_t mReplaceLength;
/** the text to insert into mElement at mOffset */
nsString mStringToInsert;
/** the range list **/
/** The range list **/
nsRefPtr<mozilla::TextRangeArray> mRanges;
/** the editor, which is used to get the selection controller */
nsIEditor *mEditor;
/** The text to insert into mTextNode at mOffset */
nsString mStringToInsert;
bool mFixed;
/** The editor, which is used to get the selection controller */
nsEditor& mEditor;
bool mFixed;
};
NS_DEFINE_STATIC_IID_ACCESSOR(IMETextTxn, NS_IMETEXTTXN_IID)
}
}
#endif

View File

@ -126,9 +126,8 @@ NS_IMETHODIMP PlaceholderTxn::Merge(nsITransaction *aTransaction, bool *aDidMerg
// we are absorbing all txn's if mAbsorb is lit.
if (mAbsorb)
{
nsRefPtr<IMETextTxn> otherTxn;
if (NS_SUCCEEDED(aTransaction->QueryInterface(IMETextTxn::GetCID(), getter_AddRefs(otherTxn))) && otherTxn)
{
nsRefPtr<IMETextTxn> otherTxn = do_QueryObject(aTransaction);
if (otherTxn) {
// special handling for IMETextTxn's: they need to merge with any previous
// IMETextTxn in this placeholder, if possible.
if (!mIMETextTxn)

View File

@ -16,7 +16,12 @@
#include "nsAutoPtr.h"
class nsHTMLEditor;
namespace mozilla {
namespace dom {
class IMETextTxn;
}
}
/**
* An aggregate transaction that knows how to absorb all subsequent
@ -65,7 +70,7 @@ protected:
/** the presentation shell, which we'll need to get the selection */
bool mAbsorb; // do we auto absorb any and all transaction?
nsWeakPtr mForwarding;
IMETextTxn *mIMETextTxn; // first IME txn in this placeholder - used for IME merging
mozilla::dom::IMETextTxn *mIMETextTxn; // first IME txn in this placeholder - used for IME merging
// non-owning for now - can't nsCOMPtr it due to broken transaction interfaces
bool mCommitted; // do we stop auto absorbing any matching placeholder txns?
// these next two members store the state of the selection in a safe way.

View File

@ -2378,7 +2378,7 @@ nsEditor::InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert,
// IME insertion.
if (mComposition && !aSuppressIME) {
if (!mIMETextNode) {
mIMETextNode = do_QueryInterface(&aTextNode);
mIMETextNode = &aTextNode;
mIMETextOffset = aOffset;
}
// Modify mPhonetic with raw text input clauses.
@ -2397,11 +2397,7 @@ nsEditor::InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert,
textRange.mStartOffset, textRange.Length());
}
nsRefPtr<IMETextTxn> imeTxn;
nsresult res = CreateTxnForIMEText(aStringToInsert,
getter_AddRefs(imeTxn));
NS_ENSURE_SUCCESS(res, res);
txn = imeTxn;
txn = CreateTxnForIMEText(aStringToInsert);
isIMETransaction = true;
} else {
txn = CreateTxnForInsertText(aStringToInsert, aTextNode, aOffset);
@ -2440,8 +2436,7 @@ nsEditor::InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert,
// Delete empty IME text node if there is one
if (isIMETransaction && mIMETextNode) {
uint32_t len;
mIMETextNode->GetLength(&len);
uint32_t len = mIMETextNode->Length();
if (!len) {
DeleteNode(mIMETextNode);
mIMETextNode = nullptr;
@ -4290,25 +4285,16 @@ nsEditor::CreateTxnForDeleteNode(nsINode* aNode, DeleteNodeTxn** aTxn)
return NS_OK;
}
NS_IMETHODIMP
nsEditor::CreateTxnForIMEText(const nsAString& aStringToInsert,
IMETextTxn ** aTxn)
already_AddRefed<IMETextTxn>
nsEditor::CreateTxnForIMEText(const nsAString& aStringToInsert)
{
NS_ASSERTION(aTxn, "illegal value- null ptr- aTxn");
nsRefPtr<IMETextTxn> txn = new IMETextTxn();
// During handling IME composition, mComposition must have been initialized.
// TODO: We can simplify IMETextTxn::Init() with TextComposition class.
nsresult rv = txn->Init(mIMETextNode, mIMETextOffset,
mComposition->String().Length(),
mComposition->GetRanges(), aStringToInsert, this);
if (NS_SUCCEEDED(rv))
{
txn.forget(aTxn);
}
return rv;
nsRefPtr<IMETextTxn> txn = new IMETextTxn(*mIMETextNode, mIMETextOffset,
mComposition->String().Length(),
mComposition->GetRanges(),
aStringToInsert, *this);
return txn.forget();
}

View File

@ -30,7 +30,6 @@
class AddStyleSheetTxn;
class DeleteNodeTxn;
class EditAggregateTxn;
class IMETextTxn;
class JoinElementTxn;
class RemoveStyleSheetTxn;
class SplitElementTxn;
@ -73,6 +72,7 @@ class DataTransfer;
class DeleteTextTxn;
class Element;
class EventTarget;
class IMETextTxn;
class InsertTextTxn;
class InsertNodeTxn;
class Selection;
@ -310,8 +310,9 @@ protected:
CreateTxnForInsertText(const nsAString& aStringToInsert,
mozilla::dom::Text& aTextNode, int32_t aOffset);
NS_IMETHOD CreateTxnForIMEText(const nsAString & aStringToInsert,
IMETextTxn ** aTxn);
// Never returns null.
already_AddRefed<mozilla::dom::IMETextTxn>
CreateTxnForIMEText(const nsAString & aStringToInsert);
/** create a transaction for adding a style sheet
*/
@ -835,7 +836,7 @@ protected:
nsRefPtr<nsTransactionManager> mTxnMgr;
nsCOMPtr<mozilla::dom::Element> mRootElement; // cached root node
nsCOMPtr<nsIDOMCharacterData> mIMETextNode; // current IME text node
nsRefPtr<mozilla::dom::Text> mIMETextNode; // current IME text node
nsCOMPtr<mozilla::dom::EventTarget> mEventTarget; // The form field as an event receiver
nsCOMPtr<nsIDOMEventListener> mEventListener;
nsWeakPtr mSelConWeak; // weak reference to the nsISelectionController