--- a/widget/windows/TSFTextStore.cpp
+++ b/widget/windows/TSFTextStore.cpp
@@ -7,16 +7,17 @@
#include <algorithm>
#include "mozilla/Logging.h"
#include "nscore.h"
#include "nsWindow.h"
#include "nsPrintfCString.h"
#include "WinUtils.h"
+#include "mozilla/AutoRestore.h"
#include "mozilla/Preferences.h"
#include "mozilla/TextEventDispatcher.h"
#include "mozilla/TextEvents.h"
#include "mozilla/WindowsVersion.h"
#include "nsIXULRuntime.h"
#define INPUTSCOPE_INIT_GUID
#define TEXTATTRS_INIT_GUID
@@ -1191,16 +1192,17 @@ TSFTextStore::TSFTextStore()
, mWaitingQueryLayout(false)
, mPendingDestroy(false)
, mDeferClearingLockedContent(false)
, mNativeCaretIsCreated(false)
, mDeferNotifyingTSF(false)
, mDeferCommittingComposition(false)
, mDeferCancellingComposition(false)
, mDestroyed(false)
+ , mBeingDestroyed(false)
{
for (int32_t i = 0; i < NUM_OF_SUPPORTED_ATTRS; i++) {
mRequestedAttrs[i] = false;
}
// We hope that 5 or more actions don't occur at once.
mPendingActions.SetCapacity(5);
@@ -1279,64 +1281,68 @@ TSFTextStore::Init(nsWindowBase* aWidget
MOZ_LOG(sTextStoreLog, LogLevel::Info,
("TSF: 0x%p TSFTextStore::Init() succeeded: "
"mDocumentMgr=0x%p, mContext=0x%p, mEditCookie=0x%08X",
this, mDocumentMgr.get(), mContext.get(), mEditCookie));
return true;
}
-bool
+void
TSFTextStore::Destroy()
{
+ if (mBeingDestroyed) {
+ return;
+ }
+
MOZ_LOG(sTextStoreLog, LogLevel::Info,
("TSF: 0x%p TSFTextStore::Destroy(), mLock=%s, "
"mComposition.IsComposing()=%s, mHandlingKeyMessage=%u",
this, GetLockFlagNameStr(mLock).get(),
GetBoolName(mComposition.IsComposing()),
mHandlingKeyMessage));
mDestroyed = true;
+ // Destroy native caret first because it's not directly related to TSF and
+ // there may be another textstore which gets focus. So, we should avoid
+ // to destroy caret after the new one recreates caret.
+ MaybeDestroyNativeCaret();
+
if (mLock) {
mPendingDestroy = true;
- return true;
- }
+ return;
+ }
+
+ AutoRestore<bool> savedBeingDestroyed(mBeingDestroyed);
+ mBeingDestroyed = true;
// If there is composition, TSF keeps the composition even after the text
// store destroyed. So, we should clear the composition here.
if (mComposition.IsComposing()) {
- NS_WARNING("Composition is still alive at destroying the text store");
CommitCompositionInternal(false);
}
- MaybeDestroyNativeCaret();
-
if (mSink) {
MOZ_LOG(sTextStoreLog, LogLevel::Debug,
("TSF: 0x%p TSFTextStore::Destroy(), calling "
"ITextStoreACPSink::OnLayoutChange(TS_LC_DESTROY)...",
this));
mSink->OnLayoutChange(TS_LC_DESTROY, TEXTSTORE_DEFAULT_VIEW);
}
- mLockedContent.Clear();
- mSelection.MarkDirty();
-
// If this is called during handling a keydown or keyup message, we should
// put off to release TSF objects until it completely finishes since
// MS-IME for Japanese refers some objects without grabbing them.
if (!mHandlingKeyMessage) {
ReleaseTSFObjects();
}
MOZ_LOG(sTextStoreLog, LogLevel::Info,
("TSF: 0x%p TSFTextStore::Destroy() succeeded", this));
-
- return true;
}
void
TSFTextStore::ReleaseTSFObjects()
{
MOZ_ASSERT(!mHandlingKeyMessage);
MOZ_LOG(sTextStoreLog, LogLevel::Info,
@@ -1483,20 +1489,21 @@ TSFTextStore::RequestLock(DWORD dwLockFl
phrSession, GetLockFlagNameStr(mLock).get(), GetBoolName(mDestroyed)));
if (!mSink) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
("TSF: 0x%p TSFTextStore::RequestLock() FAILED due to "
"any sink not stored", this));
return E_FAIL;
}
- if (mDestroyed) {
+ if (mDestroyed &&
+ (!mLockedContent.IsInitialized() || mSelection.IsDirty())) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
("TSF: 0x%p TSFTextStore::RequestLock() FAILED due to "
- "being destroyed", this));
+ "being destroyed and no information of the contents", this));
return E_FAIL;
}
if (!phrSession) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
("TSF: 0x%p TSFTextStore::RequestLock() FAILED due to "
"null phrSession", this));
return E_INVALIDARG;
}
@@ -1574,17 +1581,17 @@ TSFTextStore::DidLockGranted()
// OnUpdateComposition() without new range. Therefore, let's record the
// composition update information here.
CompleteLastActionIfStillIncomplete();
FlushPendingActions();
}
// If the widget has gone, we don't need to notify anything.
- if (!mWidget || mWidget->Destroyed()) {
+ if (mDestroyed || !mWidget || mWidget->Destroyed()) {
mPendingOnSelectionChange = false;
mHasReturnedNoLayoutError = false;
}
}
void
TSFTextStore::DispatchEvent(WidgetGUIEvent& aEvent)
{
@@ -1598,18 +1605,20 @@ TSFTextStore::DispatchEvent(WidgetGUIEve
}
mWidget->DispatchWindowEvent(&aEvent);
}
void
TSFTextStore::FlushPendingActions()
{
if (!mWidget || mWidget->Destroyed()) {
+ // Note that don't clear the locked contents because TIP may try to commit
+ // composition with a document lock. In such case, TSFTextStore needs to
+ // behave as expected by TIP.
mPendingActions.Clear();
- mLockedContent.Clear();
mPendingOnSelectionChange = false;
mHasReturnedNoLayoutError = false;
return;
}
RefPtr<nsWindowBase> kungFuDeathGrip(mWidget);
nsresult rv = mDispatcher->BeginNativeInputTransaction();
if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -1620,18 +1629,27 @@ TSFTextStore::FlushPendingActions()
}
for (uint32_t i = 0; i < mPendingActions.Length(); i++) {
PendingAction& action = mPendingActions[i];
switch (action.mType) {
case PendingAction::COMPOSITION_START: {
MOZ_LOG(sTextStoreLog, LogLevel::Debug,
("TSF: 0x%p TSFTextStore::FlushPendingActions() "
"flushing COMPOSITION_START={ mSelectionStart=%d, "
- "mSelectionLength=%d }",
- this, action.mSelectionStart, action.mSelectionLength));
+ "mSelectionLength=%d }, mDestroyed=%s",
+ this, action.mSelectionStart, action.mSelectionLength,
+ GetBoolName(mDestroyed)));
+
+ if (mDestroyed) {
+ MOZ_LOG(sTextStoreLog, LogLevel::Warning,
+ ("TSF: 0x%p TSFTextStore::FlushPendingActions() "
+ "IGNORED pending compositionstart due to already destroyed",
+ this));
+ break;
+ }
if (action.mAdjustSelection) {
// Select composition range so the new composition replaces the range
WidgetSelectionEvent selectionSet(true, eSetSelection, mWidget);
mWidget->InitEvent(selectionSet);
selectionSet.mOffset = static_cast<uint32_t>(action.mSelectionStart);
selectionSet.mLength = static_cast<uint32_t>(action.mSelectionLength);
selectionSet.mReversed = false;
@@ -1729,19 +1747,29 @@ TSFTextStore::FlushPendingActions()
mDeferClearingLockedContent = false;
}
break;
}
case PendingAction::SET_SELECTION: {
MOZ_LOG(sTextStoreLog, LogLevel::Debug,
("TSF: 0x%p TSFTextStore::FlushPendingActions() "
"flushing SET_SELECTION={ mSelectionStart=%d, "
- "mSelectionLength=%d, mSelectionReversed=%s }",
+ "mSelectionLength=%d, mSelectionReversed=%s }, "
+ "mDestroyed=%s",
this, action.mSelectionStart, action.mSelectionLength,
- GetBoolName(action.mSelectionReversed)));
+ GetBoolName(action.mSelectionReversed),
+ GetBoolName(mDestroyed)));
+
+ if (mDestroyed) {
+ MOZ_LOG(sTextStoreLog, LogLevel::Warning,
+ ("TSF: 0x%p TSFTextStore::FlushPendingActions() "
+ "IGNORED pending selectionset due to already destroyed",
+ this));
+ break;
+ }
WidgetSelectionEvent selectionSet(true, eSetSelection, mWidget);
selectionSet.mOffset =
static_cast<uint32_t>(action.mSelectionStart);
selectionSet.mLength =
static_cast<uint32_t>(action.mSelectionLength);
selectionSet.mReversed = action.mSelectionReversed;
break;
@@ -1795,16 +1823,25 @@ TSFTextStore::MaybeFlushPendingNotificat
return;
}
if (mPendingDestroy) {
Destroy();
return;
}
+ if (mDestroyed) {
+ // If it's already been destroyed completely, this shouldn't notify TSF of
+ // anything anymore.
+ MOZ_LOG(sTextStoreLog, LogLevel::Debug,
+ ("TSF: 0x%p TSFTextStore::MaybeFlushPendingNotifications(), "
+ "does nothing because this has already destroyed completely...", this));
+ return;
+ }
+
if (!mDeferClearingLockedContent && mLockedContent.IsInitialized()) {
mLockedContent.Clear();
MOZ_LOG(sTextStoreLog, LogLevel::Debug,
("TSF: 0x%p TSFTextStore::MaybeFlushPendingNotifications(), "
"mLockedContent is cleared", this));
}
if (mHasReturnedNoLayoutError) {
@@ -2001,16 +2038,17 @@ TSFTextStore::LockedContent()
bool
TSFTextStore::GetCurrentText(nsAString& aTextContent)
{
if (mLockedContent.IsInitialized()) {
aTextContent = mLockedContent.Text();
return true;
}
+ MOZ_ASSERT(!mDestroyed);
MOZ_ASSERT(mWidget && !mWidget->Destroyed());
MOZ_LOG(sTextStoreLog, LogLevel::Debug,
("TSF: 0x%p TSFTextStore::GetCurrentText(): "
"retrieving text from the content...", this));
WidgetQueryContentEvent queryText(true, eQueryTextContent, mWidget);
queryText.InitForQueryTextContent(0, UINT32_MAX);
@@ -2027,16 +2065,17 @@ TSFTextStore::GetCurrentText(nsAString&
aTextContent = queryText.mReply.mString;
return true;
}
TSFTextStore::Selection&
TSFTextStore::CurrentSelection()
{
if (mSelection.IsDirty()) {
+ MOZ_ASSERT(!mDestroyed);
// If the window has never been available, we should crash since working
// with broken values may make TIP confused.
if (!mWidget || mWidget->Destroyed()) {
MOZ_CRASH();
}
WidgetQueryContentEvent querySelection(true, eQuerySelectedText, mWidget);
mWidget->InitEvent(querySelection);
@@ -3281,17 +3320,17 @@ TSFTextStore::GetACPFromPoint(TsViewCook
MOZ_LOG(sTextStoreLog, LogLevel::Error,
("TSF: 0x%p TSFTextStore::GetACPFromPoint() FAILED due to "
"null pacp", this));
return E_INVALIDARG;
}
mWaitingQueryLayout = false;
- if (mLockedContent.IsLayoutChanged()) {
+ if (mDestroyed || mLockedContent.IsLayoutChanged()) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
("TSF: 0x%p TSFTextStore::GetACPFromPoint() returned "
"TS_E_NOLAYOUT", this));
mHasReturnedNoLayoutError = true;
return TS_E_NOLAYOUT;
}
LayoutDeviceIntPoint ourPt(pt->x, pt->y);
@@ -3528,16 +3567,25 @@ TSFTextStore::GetTextExt(TsViewCookie vc
if (mLockedContent.IsLayoutChangedAfter(acpEnd)) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
("TSF: 0x%p TSFTextStore::GetTextExt() returned TS_E_NOLAYOUT "
"(acpEnd=%d)", this, acpEnd));
mHasReturnedNoLayoutError = true;
return TS_E_NOLAYOUT;
}
+ if (mDestroyed) {
+ MOZ_LOG(sTextStoreLog, LogLevel::Error,
+ ("TSF: 0x%p TSFTextStore::GetTextExt() returned TS_E_NOLAYOUT "
+ "(acpEnd=%d) because this has already been destroyed",
+ this, acpEnd));
+ mHasReturnedNoLayoutError = true;
+ return TS_E_NOLAYOUT;
+ }
+
// use eQueryTextRect to get rect in system, screen coordinates
WidgetQueryContentEvent event(true, eQueryTextRect, mWidget);
mWidget->InitEvent(event);
event.InitForQueryTextRect(acpStart, acpEnd - acpStart);
DispatchEvent(event);
if (!event.mSucceeded) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
("TSF: 0x%p TSFTextStore::GetTextExt() FAILED due to "
@@ -3619,16 +3667,24 @@ TSFTextStore::GetScreenExt(TsViewCookie
if (!prc) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
("TSF: 0x%p TSFTextStore::GetScreenExt() FAILED due to "
"null argument", this));
return E_INVALIDARG;
}
+ if (mDestroyed) {
+ MOZ_LOG(sTextStoreLog, LogLevel::Error,
+ ("TSF: 0x%p TSFTextStore::GetScreenExt() returns empty rect "
+ "due to already destroyed", this));
+ prc->left = prc->top = prc->right = prc->left = 0;
+ return S_OK;
+ }
+
if (!GetScreenExtInternal(*prc)) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
("TSF: 0x%p TSFTextStore::GetScreenExt() FAILED due to "
"GetScreenExtInternal() failure", this));
return E_FAIL;
}
MOZ_LOG(sTextStoreLog, LogLevel::Info,
@@ -3639,16 +3695,18 @@ TSFTextStore::GetScreenExt(TsViewCookie
}
bool
TSFTextStore::GetScreenExtInternal(RECT& aScreenExt)
{
MOZ_LOG(sTextStoreLog, LogLevel::Debug,
("TSF: 0x%p TSFTextStore::GetScreenExtInternal()", this));
+ MOZ_ASSERT(!mDestroyed);
+
// use NS_QUERY_EDITOR_RECT to get rect in system, screen coordinates
WidgetQueryContentEvent event(true, eQueryEditorRect, mWidget);
mWidget->InitEvent(event);
DispatchEvent(event);
if (!event.mSucceeded) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
("TSF: 0x%p TSFTextStore::GetScreenExtInternal() FAILED due to "
"eQueryEditorRect failure", this));
@@ -3713,17 +3771,17 @@ TSFTextStore::GetWnd(TsViewCookie vcView
if (!phwnd) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
("TSF: 0x%p TSFTextStore::GetScreenExt() FAILED due to "
"null argument", this));
return E_INVALIDARG;
}
- *phwnd = mWidget->GetWindowHandle();
+ *phwnd = mWidget ? mWidget->GetWindowHandle() : nullptr;
MOZ_LOG(sTextStoreLog, LogLevel::Info,
("TSF: 0x%p TSFTextStore::GetWnd() succeeded: *phwnd=0x%p",
this, static_cast<void*>(*phwnd)));
return S_OK;
}
STDMETHODIMP
@@ -4498,28 +4556,36 @@ TSFTextStore::OnTextChangeInternal(const
MOZ_LOG(sTextStoreLog, LogLevel::Debug,
("TSF: 0x%p TSFTextStore::OnTextChangeInternal(aIMENotification={ "
"mMessage=0x%08X, mTextChangeData={ mStartOffset=%lu, "
"mRemovedEndOffset=%lu, mAddedEndOffset=%lu, "
"mCausedOnlyByComposition=%s, "
"mIncludingChangesDuringComposition=%s, "
"mIncludingChangesWithoutComposition=%s }), "
- "mSink=0x%p, mSinkMask=%s, mComposition.IsComposing()=%s",
+ "mDestroyed=%s, mSink=0x%p, mSinkMask=%s, "
+ "mComposition.IsComposing()=%s",
this, aIMENotification.mMessage,
textChangeData.mStartOffset,
textChangeData.mRemovedEndOffset,
textChangeData.mAddedEndOffset,
GetBoolName(textChangeData.mCausedOnlyByComposition),
GetBoolName(textChangeData.mIncludingChangesDuringComposition),
GetBoolName(textChangeData.mIncludingChangesWithoutComposition),
+ GetBoolName(mDestroyed),
mSink.get(),
GetSinkMaskNameStr(mSinkMask).get(),
GetBoolName(mComposition.IsComposing())));
+ if (mDestroyed) {
+ // If this instance is already destroyed, we shouldn't notify TSF of any
+ // changes.
+ return NS_OK;
+ }
+
if (textChangeData.mCausedOnlyByComposition) {
// Ignore text change notifications caused only by composition since it's
// already been handled internally.
return NS_OK;
}
if (mComposition.IsComposing() &&
!textChangeData.mIncludingChangesDuringComposition) {
@@ -4564,16 +4630,18 @@ TSFTextStore::OnTextChangeInternal(const
MaybeFlushPendingNotifications();
return NS_OK;
}
void
TSFTextStore::NotifyTSFOfTextChange(const TS_TEXTCHANGE& aTextChange)
{
+ MOZ_ASSERT(!mDestroyed);
+
// XXX We need to cache the text change ranges and notify TSF of that
// the document is unlocked.
if (NS_WARN_IF(IsReadLocked())) {
return;
}
// Some TIPs are confused by text change notification during composition.
// Especially, some of them stop working for composition in our process.
@@ -4600,29 +4668,36 @@ TSFTextStore::OnSelectionChangeInternal(
{
const IMENotification::SelectionChangeDataBase& selectionChangeData =
aIMENotification.mSelectionChangeData;
MOZ_LOG(sTextStoreLog, LogLevel::Debug,
("TSF: 0x%p TSFTextStore::OnSelectionChangeInternal("
"aIMENotification={ mSelectionChangeData={ mOffset=%lu, "
"Length()=%lu, mReversed=%s, mWritingMode=%s, "
"mCausedByComposition=%s, mCausedBySelectionEvent=%s, "
- "mOccurredDuringComposition=%s } }), "
+ "mOccurredDuringComposition=%s } }), mDestroyed=%s, "
"mSink=0x%p, mSinkMask=%s, mIsRecordingActionsWithoutLock=%s, "
"mComposition.IsComposing()=%s",
this, selectionChangeData.mOffset, selectionChangeData.Length(),
GetBoolName(selectionChangeData.mReversed),
GetWritingModeName(selectionChangeData.GetWritingMode()).get(),
GetBoolName(selectionChangeData.mCausedByComposition),
GetBoolName(selectionChangeData.mCausedBySelectionEvent),
GetBoolName(selectionChangeData.mOccurredDuringComposition),
+ GetBoolName(mDestroyed),
mSink.get(), GetSinkMaskNameStr(mSinkMask).get(),
GetBoolName(mIsRecordingActionsWithoutLock),
GetBoolName(mComposition.IsComposing())));
+ if (mDestroyed) {
+ // If this instance is already destroyed, we shouldn't notify TSF of any
+ // changes.
+ return NS_OK;
+ }
+
if (selectionChangeData.mCausedByComposition) {
// Ignore selection change notifications caused by composition since it's
// already been handled internally.
return NS_OK;
}
mDeferNotifyingTSF = false;
@@ -4679,16 +4754,18 @@ TSFTextStore::OnSelectionChangeInternal(
MaybeFlushPendingNotifications();
return NS_OK;
}
void
TSFTextStore::NotifyTSFOfSelectionChange()
{
+ MOZ_ASSERT(!mDestroyed);
+
if (NS_WARN_IF(IsReadLocked())) {
return;
}
mPendingOnSelectionChange = false;
if (!mSink || !(mSinkMask & TS_AS_SEL_CHANGE)) {
return;
@@ -4710,16 +4787,22 @@ TSFTextStore::NotifyTSFOfSelectionChange
("TSF: 0x%p TSFTextStore::NotifyTSFOfSelectionChange(), calling "
"ITextStoreACPSink::OnSelectionChange()...", this));
mSink->OnSelectionChange();
}
nsresult
TSFTextStore::OnLayoutChangeInternal()
{
+ if (mDestroyed) {
+ // If this instance is already destroyed, we shouldn't notify TSF of any
+ // changes.
+ return NS_OK;
+ }
+
NS_ENSURE_TRUE(mContext, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(mSink, NS_ERROR_FAILURE);
mDeferNotifyingTSF = false;
nsresult rv = NS_OK;
// We need to notify TSF of layout change even if the document is locked.
@@ -4738,16 +4821,18 @@ TSFTextStore::OnLayoutChangeInternal()
MaybeFlushPendingNotifications();
return rv;
}
bool
TSFTextStore::NotifyTSFOfLayoutChange()
{
+ MOZ_ASSERT(!mDestroyed);
+
// If we're waiting a query of layout information from TIP, it means that
// we've returned TS_E_NOLAYOUT error.
bool returnedNoLayoutError =
mHasReturnedNoLayoutError || mWaitingQueryLayout;
// If we returned TS_E_NOLAYOUT, TIP should query the computed layout again.
mWaitingQueryLayout = returnedNoLayoutError;
@@ -4843,16 +4928,22 @@ TSFTextStore::NotifyTSFOfLayoutChange()
reinterpret_cast<WPARAM>(this), 0);
return true;
}
void
TSFTextStore::NotifyTSFOfLayoutChangeAgain()
{
+ // Don't notify TSF of layout change after destroyed.
+ if (mDestroyed) {
+ mWaitingQueryLayout = false;
+ return;
+ }
+
// Before preforming this method, TIP has accessed our layout information by
// itself. In such case, we don't need to call OnLayoutChange() anymore.
if (!mWaitingQueryLayout) {
return;
}
MOZ_LOG(sTextStoreLog, LogLevel::Info,
("TSF: 0x%p TSFTextStore::NotifyTSFOfLayoutChangeAgain(), "
@@ -4877,31 +4968,42 @@ TSFTextStore::NotifyTSFOfLayoutChangeAga
}
}
nsresult
TSFTextStore::OnUpdateCompositionInternal()
{
MOZ_LOG(sTextStoreLog, LogLevel::Debug,
("TSF: 0x%p TSFTextStore::OnUpdateCompositionInternal(), "
- "mDeferNotifyingTSF=%s",
- this, GetBoolName(mDeferNotifyingTSF)));
+ "mDestroyed=%s, mDeferNotifyingTSF=%s",
+ this, GetBoolName(mDestroyed), GetBoolName(mDeferNotifyingTSF)));
+
+ // There are nothing to do after destroyed.
+ if (mDestroyed) {
+ return NS_OK;
+ }
// Now, all sent composition events are handled by the content even in
// e10s mode.
mDeferClearingLockedContent = false;
mDeferNotifyingTSF = false;
MaybeFlushPendingNotifications();
return NS_OK;
}
nsresult
TSFTextStore::OnMouseButtonEventInternal(
const IMENotification& aIMENotification)
{
+ if (mDestroyed) {
+ // If this instance is already destroyed, we shouldn't notify TSF of any
+ // events.
+ return NS_OK;
+ }
+
if (mMouseTrackers.IsEmpty()) {
return NS_OK;
}
MOZ_LOG(sTextStoreLog, LogLevel::Debug,
("TSF: 0x%p TSFTextStore::OnMouseButtonEventInternal("
"aIMENotification={ mEventMessage=%s, mOffset=%u, mCursorPos={ "
"mX=%d, mY=%d }, mCharRect={ mX=%d, mY=%d, mWidth=%d, mHeight=%d }, "
@@ -4967,16 +5069,21 @@ TSFTextStore::OnMouseButtonEventInternal
return NS_OK;
}
void
TSFTextStore::CreateNativeCaret()
{
MaybeDestroyNativeCaret();
+ // Don't create native caret after destroyed.
+ if (mDestroyed) {
+ return;
+ }
+
MOZ_LOG(sTextStoreLog, LogLevel::Debug,
("TSF: 0x%p TSFTextStore::CreateNativeCaret(), "
"mComposition.IsComposing()=%s",
this, GetBoolName(mComposition.IsComposing())));
Selection& currentSel = CurrentSelection();
if (currentSel.IsDirty()) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
@@ -5076,17 +5183,18 @@ TSFTextStore::CommitCompositionInternal(
"putting off to request to %s composition after unlocking the document",
this, aDiscard ? "cancel" : "commit"));
return;
}
if (mComposition.IsComposing() && aDiscard) {
LONG endOffset = mComposition.EndOffset();
mComposition.mString.Truncate(0);
- if (mSink) {
+ // Note that don't notify TSF of text change after this is destroyed.
+ if (mSink && !mDestroyed) {
TS_TEXTCHANGE textChange;
textChange.acpStart = mComposition.mStart;
textChange.acpOldEnd = endOffset;
textChange.acpNewEnd = mComposition.mStart;
MOZ_LOG(sTextStoreLog, LogLevel::Info,
("TSF: 0x%p TSFTextStore::CommitCompositionInternal(), calling"
"mSink->OnTextChange(0, { acpStart=%ld, acpOldEnd=%ld, "
"acpNewEnd=%ld })...", this, textChange.acpStart,