--- a/widget/ContentCache.cpp
+++ b/widget/ContentCache.cpp
@@ -517,18 +517,19 @@ ContentCacheInChild::SetSelection(nsIWid
ContentCacheInParent::ContentCacheInParent(TabParent& aTabParent)
: ContentCache()
, mTabParent(aTabParent)
, mCommitStringByRequest(nullptr)
, mPendingEventsNeedingAck(0)
, mCompositionStartInChild(UINT32_MAX)
, mPendingCommitLength(0)
, mPendingCompositionCount(0)
+ , mPendingCommitCount(0)
, mWidgetHasComposition(false)
- , mIsPendingLastCommitEvent(false)
+ , mIsChildIgnoringCompositionEvents(false)
{
}
void
ContentCacheInParent::AssignContent(const ContentCache& aOther,
nsIWidget* aWidget,
const IMENotification* aNotification)
{
@@ -1100,21 +1101,23 @@ ContentCacheInParent::GetCaretRect(uint3
bool
ContentCacheInParent::OnCompositionEvent(const WidgetCompositionEvent& aEvent)
{
MOZ_LOG(sContentCacheLog, LogLevel::Info,
("0x%p OnCompositionEvent(aEvent={ "
"mMessage=%s, mData=\"%s\" (Length()=%u), mRanges->Length()=%zu }), "
"mPendingEventsNeedingAck=%u, mWidgetHasComposition=%s, "
- "mPendingCompositionCount=%u, mCommitStringByRequest=0x%p",
+ "mPendingCompositionCount=%" PRIu8 ", mPendingCommitCount=%" PRIu8 ", "
+ "mIsChildIgnoringCompositionEvents=%s, mCommitStringByRequest=0x%p",
this, ToChar(aEvent.mMessage),
GetEscapedUTF8String(aEvent.mData).get(), aEvent.mData.Length(),
aEvent.mRanges ? aEvent.mRanges->Length() : 0, mPendingEventsNeedingAck,
GetBoolName(mWidgetHasComposition), mPendingCompositionCount,
+ mPendingCommitCount, GetBoolName(mIsChildIgnoringCompositionEvents),
mCommitStringByRequest));
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
mDispatchedEventMessages.AppendElement(aEvent.mMessage);
#endif // #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
// We must be able to simulate the selection because
// we might not receive selection updates in time
@@ -1138,17 +1141,17 @@ ContentCacheInParent::OnCompositionEvent
mWidgetHasComposition = !aEvent.CausesDOMCompositionEndEvent();
if (!mWidgetHasComposition) {
mCompositionStart = UINT32_MAX;
if (mPendingCompositionCount == 1) {
mPendingCommitLength = aEvent.mData.Length();
}
- mIsPendingLastCommitEvent = true;
+ mPendingCommitCount++;
} else if (aEvent.mMessage != eCompositionStart) {
mCompositionString = aEvent.mData;
}
// During REQUEST_TO_COMMIT_COMPOSITION or REQUEST_TO_CANCEL_COMPOSITION,
// widget usually sends a eCompositionChange and/or eCompositionCommit event
// to finalize or clear the composition, respectively. In this time,
// we need to intercept all composition events here and pass the commit
@@ -1156,46 +1159,51 @@ ContentCacheInParent::OnCompositionEvent
// RequestIMEToCommitComposition(). Then, eCommitComposition event will
// be dispatched with the committed string in the remote process internally.
if (mCommitStringByRequest) {
MOZ_ASSERT(aEvent.mMessage == eCompositionChange ||
aEvent.mMessage == eCompositionCommit);
*mCommitStringByRequest = aEvent.mData;
// We need to wait eCompositionCommitRequestHandled from the remote process
// in this case. Therefore, mPendingEventsNeedingAck needs to be
- // incremented here.
+ // incremented here. Additionally, we stop sending eCompositionCommit(AsIs)
+ // event. Therefore, we need to decrement mPendingCommitCount which has
+ // been incremented above.
if (!mWidgetHasComposition) {
mPendingEventsNeedingAck++;
+ MOZ_DIAGNOSTIC_ASSERT(mPendingCommitCount);
+ if (mPendingCommitCount) {
+ mPendingCommitCount--;
+ }
}
- // Cancel mIsPendingLastCommitEvent because we won't send the commit event
- // to the remote process.
- mIsPendingLastCommitEvent = false;
return false;
}
mPendingEventsNeedingAck++;
return true;
}
void
ContentCacheInParent::OnSelectionEvent(
const WidgetSelectionEvent& aSelectionEvent)
{
MOZ_LOG(sContentCacheLog, LogLevel::Info,
("0x%p OnSelectionEvent(aEvent={ "
"mMessage=%s, mOffset=%u, mLength=%u, mReversed=%s, "
"mExpandToClusterBoundary=%s, mUseNativeLineBreak=%s }), "
"mPendingEventsNeedingAck=%u, mWidgetHasComposition=%s, "
- "mPendingCompositionCount=%u",
+ "mPendingCompositionCount=%" PRIu8 ", mPendingCommitCount=%" PRIu8 ", "
+ "mIsChildIgnoringCompositionEvents=%s",
this, ToChar(aSelectionEvent.mMessage),
aSelectionEvent.mOffset, aSelectionEvent.mLength,
GetBoolName(aSelectionEvent.mReversed),
GetBoolName(aSelectionEvent.mExpandToClusterBoundary),
GetBoolName(aSelectionEvent.mUseNativeLineBreak), mPendingEventsNeedingAck,
- GetBoolName(mWidgetHasComposition), mPendingCompositionCount));
+ GetBoolName(mWidgetHasComposition), mPendingCompositionCount,
+ mPendingCommitCount, GetBoolName(mIsChildIgnoringCompositionEvents)));
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
mDispatchedEventMessages.AppendElement(aSelectionEvent.mMessage);
#endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
mPendingEventsNeedingAck++;
}
@@ -1203,60 +1211,102 @@ void
ContentCacheInParent::OnEventNeedingAckHandled(nsIWidget* aWidget,
EventMessage aMessage)
{
// This is called when the child process receives WidgetCompositionEvent or
// WidgetSelectionEvent.
MOZ_LOG(sContentCacheLog, LogLevel::Info,
("0x%p OnEventNeedingAckHandled(aWidget=0x%p, "
- "aMessage=%s), mPendingEventsNeedingAck=%u, mPendingCompositionCount=%" PRIu8,
- this, aWidget, ToChar(aMessage), mPendingEventsNeedingAck, mPendingCompositionCount));
+ "aMessage=%s), mPendingEventsNeedingAck=%u, "
+ "mPendingCompositionCount=%" PRIu8 ", mPendingCommitCount=%" PRIu8 ", "
+ "mIsChildIgnoringCompositionEvents=%s",
+ this, aWidget, ToChar(aMessage), mPendingEventsNeedingAck,
+ mPendingCompositionCount, mPendingCommitCount,
+ GetBoolName(mIsChildIgnoringCompositionEvents)));
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
mReceivedEventMessages.AppendElement(aMessage);
-#endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
+#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
- if (WidgetCompositionEvent::IsFollowedByCompositionEnd(aMessage) ||
- aMessage == eCompositionCommitRequestHandled) {
+ bool isCommittedInChild =
+ // Commit requester in the remote process has committed the composition.
+ aMessage == eCompositionCommitRequestHandled ||
+ // The commit event has been handled normally in the remote process.
+ (!mIsChildIgnoringCompositionEvents &&
+ WidgetCompositionEvent::IsFollowedByCompositionEnd(aMessage));
+ if (isCommittedInChild) {
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
if (mPendingCompositionCount == 1) {
RemoveUnnecessaryEventMessageLog();
}
#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
if (NS_WARN_IF(!mPendingCompositionCount)) {
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
nsPrintfCString info("\nThere is no pending composition but received %s "
"message from the remote child\n\n",
ToChar(aMessage));
AppendEventMessageLog(info);
CrashReporter::AppendAppNotesToCrashReport(info);
-#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
MOZ_DIAGNOSTIC_ASSERT(false,
"No pending composition but received unexpected commit event");
+#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
+
+ // Prevent odd behavior in release channel.
mPendingCompositionCount = 1;
}
mPendingCompositionCount--;
+
// Forget composition string only when the latest composition string is
// handled in the remote process because if there is 2 or more pending
// composition, this value shouldn't be referred.
if (!mPendingCompositionCount) {
mCompositionString.Truncate();
- mIsPendingLastCommitEvent = false;
}
+
// Forget pending commit string length if it's handled in the remote
// process. Note that this doesn't care too old composition's commit
// string because in such case, we cannot return proper information
// to IME synchornously.
mPendingCommitLength = 0;
}
+ if (WidgetCompositionEvent::IsFollowedByCompositionEnd(aMessage)) {
+ // After the remote process receives eCompositionCommit(AsIs) event,
+ // it'll restart to handle composition events.
+ mIsChildIgnoringCompositionEvents = false;
+
+ if (NS_WARN_IF(!mPendingCommitCount)) {
+#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ nsPrintfCString info("\nThere is no pending comment events but received "
+ "%s message from the remote child\n\n",
+ ToChar(aMessage));
+ AppendEventMessageLog(info);
+ CrashReporter::AppendAppNotesToCrashReport(info);
+ MOZ_DIAGNOSTIC_ASSERT(false,
+ "No pending commit events but received unexpected commit event");
+#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
+
+ // Prevent odd behavior in release channel.
+ mPendingCommitCount = 1;
+ }
+
+ mPendingCommitCount--;
+ } else if (aMessage == eCompositionCommitRequestHandled &&
+ mPendingCommitCount) {
+ // If the remote process commits composition synchronously after
+ // requesting commit composition and we've already sent commit composition,
+ // it starts to ignore following composition events until receiving
+ // eCompositionStart event.
+ mIsChildIgnoringCompositionEvents = true;
+ }
+
if (NS_WARN_IF(!mPendingEventsNeedingAck)) {
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
nsPrintfCString info("\nThere is no pending events but received %s "
"message from the remote child\n\n",
ToChar(aMessage));
AppendEventMessageLog(info);
CrashReporter::AppendAppNotesToCrashReport(info);
#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
@@ -1273,20 +1323,22 @@ ContentCacheInParent::OnEventNeedingAckH
bool
ContentCacheInParent::RequestIMEToCommitComposition(nsIWidget* aWidget,
bool aCancel,
nsAString& aCommittedString)
{
MOZ_LOG(sContentCacheLog, LogLevel::Info,
("0x%p RequestToCommitComposition(aWidget=%p, "
- "aCancel=%s), mPendingCompositionCount=%u, "
+ "aCancel=%s), mPendingCompositionCount=%" PRIu8 ", "
+ "mPendingCommitCount=%" PRIu8 ", mIsChildIgnoringCompositionEvents=%s, "
"IMEStateManager::DoesTabParentHaveIMEFocus(&mTabParent)=%s, "
"mWidgetHasComposition=%s, mCommitStringByRequest=%p",
this, aWidget, GetBoolName(aCancel), mPendingCompositionCount,
+ mPendingCommitCount, GetBoolName(mIsChildIgnoringCompositionEvents),
GetBoolName(IMEStateManager::DoesTabParentHaveIMEFocus(&mTabParent)),
GetBoolName(mWidgetHasComposition), mCommitStringByRequest));
MOZ_ASSERT(!mCommitStringByRequest);
// If there are 2 or more pending compositions, we already sent
// eCompositionCommit(AsIs) to the remote process. So, this request is
// too late for IME. The remote process should wait following
@@ -1301,17 +1353,22 @@ ContentCacheInParent::RequestIMEToCommit
return false;
}
// If there is no pending composition, we may have already sent
// eCompositionCommit(AsIs) event for the active composition. If so, the
// remote process will receive composition events which causes cleaning up
// TextComposition. So, this shouldn't do nothing and TextComposition
// should handle the request as it's handled asynchronously.
- if (mIsPendingLastCommitEvent) {
+ // XXX Perhaps, this is wrong because TextComposition in child process
+ // may commit the composition with current composition string in the
+ // remote process. I.e., it may be different from actual commit string
+ // which user typed. So, perhaps, we should return true and the commit
+ // string.
+ if (mPendingCommitCount) {
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
mRequestIMEToCommitCompositionResults.
AppendElement(RequestIMEToCommitCompositionResult::
eToCommittedCompositionReceived);
#endif // #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
return false;
}