Bug 1199224 - TSFTextStore should clear mLockedContent unless it needs to wait the content to be modified asynchronously. r=emk, a=sledru
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 28 Aug 2015 23:17:37 +0900
changeset 289052 ef5f85d45d354130f52fa3c41d8a7e046f459b07
parent 289051 b1098c02a9cadcfc55ad0db900c26db3e7320214
child 289053 32f4ac6166137cf462dfec4cb9b4a35e9537919f
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemk, sledru
bugs1199224
milestone42.0a2
Bug 1199224 - TSFTextStore should clear mLockedContent unless it needs to wait the content to be modified asynchronously. r=emk, a=sledru
widget/windows/TSFTextStore.cpp
widget/windows/TSFTextStore.h
--- a/widget/windows/TSFTextStore.cpp
+++ b/widget/windows/TSFTextStore.cpp
@@ -1296,17 +1296,17 @@ TSFTextStore::TSFTextStore()
   , mLock(0)
   , mLockQueued(0)
   , mLockedContent(mComposition, mSelection)
   , mRequestedAttrValues(false)
   , mIsRecordingActionsWithoutLock(false)
   , mPendingOnSelectionChange(false)
   , mPendingOnLayoutChange(false)
   , mPendingDestroy(false)
-  , mPendingClearLockedContent(false)
+  , mDeferClearingLockedContent(false)
   , mNativeCaretIsCreated(false)
   , mDeferNotifyingTSF(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.
@@ -1669,21 +1669,16 @@ TSFTextStore::FlushPendingActions()
   if (!mWidget || mWidget->Destroyed()) {
     mPendingActions.Clear();
     mLockedContent.Clear();
     mPendingOnSelectionChange = false;
     mPendingOnLayoutChange = false;
     return;
   }
 
-  // If dispatching event causes NOTIFY_IME_OF_COMPOSITION_UPDATE, we should
-  // wait to abandon mLockedContent until it's notified because the dispatched
-  // event may be handled asynchronously in e10s mode.
-  mPendingClearLockedContent = !mPendingActions.Length();
-
   nsRefPtr<nsWindowBase> kungFuDeathGrip(mWidget);
   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, "
@@ -1707,17 +1702,19 @@ TSFTextStore::FlushPendingActions()
         }
         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
                ("TSF: 0x%p   TSFTextStore::FlushPendingActions() "
                 "dispatching compositionstart event...", this));
         WidgetCompositionEvent compositionStart(true, NS_COMPOSITION_START,
                                                 mWidget);
         mWidget->InitEvent(compositionStart);
         // NS_COMPOSITION_START always causes NOTIFY_IME_OF_COMPOSITION_UPDATE.
-        mPendingClearLockedContent = true;
+        // Therefore, we should wait to clear the locked content until it's
+        // notified.
+        mDeferClearingLockedContent = true;
         DispatchEvent(compositionStart);
         if (!mWidget || mWidget->Destroyed()) {
           break;
         }
         break;
       }
       case PendingAction::COMPOSITION_UPDATE: {
         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
@@ -1772,19 +1769,21 @@ TSFTextStore::FlushPendingActions()
           TextRange wholeRange;
           wholeRange.mStartOffset = 0;
           wholeRange.mEndOffset = compositionChange.mData.Length();
           wholeRange.mRangeType = NS_TEXTRANGE_RAWINPUT;
           action.mRanges->AppendElement(wholeRange);
         }
         compositionChange.mRanges = action.mRanges;
         // When the NS_COMPOSITION_CHANGE causes a DOM text event,
-        // NOTIFY_IME_OF_COMPOSITION_UPDATE will be notified.
+        // the IME will be notified of NOTIFY_IME_OF_COMPOSITION_UPDATE.  In
+        // such case, we should not clear the locked content until we notify
+        // the IME of the composition update.
         if (compositionChange.CausesDOMTextEvent()) {
-          mPendingClearLockedContent = true;
+          mDeferClearingLockedContent = true;
         }
         DispatchEvent(compositionChange);
         // Be aware, the mWidget might already have been destroyed.
         break;
       }
       case PendingAction::COMPOSITION_END: {
         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
                ("TSF: 0x%p   TSFTextStore::FlushPendingActions() "
@@ -1797,19 +1796,21 @@ TSFTextStore::FlushPendingActions()
         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
                ("TSF: 0x%p   TSFTextStore::FlushPendingActions(), "
                 "dispatching compositioncommit event...", this));
         WidgetCompositionEvent compositionCommit(true, NS_COMPOSITION_COMMIT,
                                                  mWidget);
         mWidget->InitEvent(compositionCommit);
         compositionCommit.mData = action.mData;
         // When the NS_COMPOSITION_COMMIT causes a DOM text event,
-        // NOTIFY_IME_OF_COMPOSITION_UPDATE will be notified.
+        // the IME will be notified of NOTIFY_IME_OF_COMPOSITION_UPDATE.  In
+        // such case, we should not clear the locked content until we notify
+        // the IME of the composition update.
         if (compositionCommit.CausesDOMTextEvent()) {
-          mPendingClearLockedContent = true;
+          mDeferClearingLockedContent = true;
         }
         DispatchEvent(compositionCommit);
         if (!mWidget || mWidget->Destroyed()) {
           break;
         }
         break;
       }
       case PendingAction::SELECTION_SET: {
@@ -1863,19 +1864,21 @@ TSFTextStore::MaybeFlushPendingNotificat
     return;
   }
 
   if (mPendingDestroy) {
     Destroy();
     return;
   }
 
-  if (mPendingClearLockedContent) {
-    mPendingClearLockedContent = false;
+  if (!mDeferClearingLockedContent && mLockedContent.IsInitialized()) {
     mLockedContent.Clear();
+    MOZ_LOG(sTextStoreLog, LogLevel::Debug,
+           ("TSF: 0x%p   TSFTextStore::MaybeFlushPendingNotifications(), "
+            "mLockedContent is cleared", this));
   }
 
   if (mPendingOnLayoutChange) {
     MOZ_LOG(sTextStoreLog, LogLevel::Info,
            ("TSF: 0x%p   TSFTextStore::MaybeFlushPendingNotifications(), "
             "calling TSFTextStore::NotifyTSFOfLayoutChange()...", this));
     NotifyTSFOfLayoutChange(true);
   }
@@ -1992,62 +1995,90 @@ TSFTextStore::GetSelection(ULONG ulIndex
 }
 
 TSFTextStore::Content&
 TSFTextStore::LockedContent()
 {
   // This should be called when the document is locked or the content hasn't
   // been abandoned yet.
   if (NS_WARN_IF(!IsReadLocked() && !mLockedContent.IsInitialized())) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+           ("TSF: 0x%p   TSFTextStore::LockedContent(), FAILED, due to "
+            "called wrong timing, IsReadLocked()=%s, "
+            "mLockedContent.IsInitialized()=%s",
+            this, GetBoolName(IsReadLocked()),
+            GetBoolName(mLockedContent.IsInitialized())));
     mLockedContent.Clear();
     return mLockedContent;
   }
 
   Selection& currentSel = CurrentSelection();
   if (currentSel.IsDirty()) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+           ("TSF: 0x%p   TSFTextStore::LockedContent(), FAILED, due to "
+            "CurrentSelection() failure", this));
     mLockedContent.Clear();
     return mLockedContent;
   }
 
   if (!mLockedContent.IsInitialized()) {
     nsAutoString text;
     if (NS_WARN_IF(!GetCurrentText(text))) {
+      MOZ_LOG(sTextStoreLog, LogLevel::Error,
+             ("TSF: 0x%p   TSFTextStore::LockedContent(), FAILED, due to "
+              "GetCurrentText() failure", this));
       mLockedContent.Clear();
       return mLockedContent;
     }
 
     mLockedContent.Init(text);
+    // Basically, the locked content should be cleared after the document is
+    // unlocked.  However, in e10s mode, content will be modified
+    // asynchronously.  In such case, mDeferClearingLockedContent may be
+    // true even after the document is unlocked.
+    mDeferClearingLockedContent = false;
   }
 
   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
          ("TSF: 0x%p   TSFTextStore::LockedContent(): "
-          "mLockedContent={ mText.Length()=%d, mLastCompositionString=\"%s\", "
+          "mLockedContent={ mText=\"%s\" (Length()=%u), "
+          "mLastCompositionString=\"%s\" (Length()=%u), "
           "mMinTextModifiedOffset=%u }",
-          this, mLockedContent.Text().Length(),
+          this, mLockedContent.Text().Length() <= 20 ?
+            NS_ConvertUTF16toUTF8(mLockedContent.Text()).get() : "<omitted>",
+          mLockedContent.Text().Length(),
           NS_ConvertUTF16toUTF8(mLockedContent.LastCompositionString()).get(),
+          mLockedContent.LastCompositionString().Length(),
           mLockedContent.MinTextModifiedOffset()));
 
   return mLockedContent;
 }
 
 bool
 TSFTextStore::GetCurrentText(nsAString& aTextContent)
 {
   if (mLockedContent.IsInitialized()) {
     aTextContent = mLockedContent.Text();
     return true;
   }
 
   MOZ_ASSERT(mWidget && !mWidget->Destroyed());
 
+  MOZ_LOG(sTextStoreLog, LogLevel::Debug,
+         ("TSF: 0x%p   TSFTextStore::GetCurrentText(): "
+          "retrieving text from the content...", this));
+
   WidgetQueryContentEvent queryText(true, NS_QUERY_TEXT_CONTENT, mWidget);
   queryText.InitForQueryTextContent(0, UINT32_MAX);
   mWidget->InitEvent(queryText);
   DispatchEvent(queryText);
   if (NS_WARN_IF(!queryText.mSucceeded)) {
+    MOZ_LOG(sTextStoreLog, LogLevel::Error,
+           ("TSF: 0x%p   TSFTextStore::GetCurrentText(), FAILED, due to "
+            "NS_QUERY_TEXT_CONTENT failure", this));
     aTextContent.Truncate();
     return false;
   }
 
   aTextContent = queryText.mReply.mString;
   return true;
 }
 
@@ -4661,17 +4692,19 @@ TSFTextStore::NotifyTSFOfLayoutChange(bo
 nsresult
 TSFTextStore::OnUpdateCompositionInternal()
 {
   MOZ_LOG(sTextStoreLog, LogLevel::Debug,
     ("TSF: 0x%p   TSFTextStore::OnUpdateCompositionInternal(), "
      "mDeferNotifyingTSF=%s",
      this, GetBoolName(mDeferNotifyingTSF)));
 
-  mPendingClearLockedContent = true;
+  // 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)
--- a/widget/windows/TSFTextStore.h
+++ b/widget/windows/TSFTextStore.h
@@ -788,19 +788,19 @@ protected:
   // calculated yet, these methods return TS_E_NOLAYOUT.  Then, RequestLock()
   // will call mSink->OnLayoutChange() and
   // ITfContextOwnerServices::OnLayoutChange() after the layout is fixed and
   // the document is unlocked.
   bool                         mPendingOnLayoutChange;
   // During the documet is locked, we shouldn't destroy the instance.
   // If this is true, the instance will be destroyed after unlocked.
   bool                         mPendingDestroy;
-  // If this is true, MaybeFlushPendingNotifications() will clear the
+  // If this is false, MaybeFlushPendingNotifications() will clear the
   // mLockedContent.
-  bool                         mPendingClearLockedContent;
+  bool                         mDeferClearingLockedContent;
   // While there is native caret, this is true.  Otherwise, false.
   bool                         mNativeCaretIsCreated;
   // While the instance is dispatching events, the event may not be handled
   // synchronously in e10s mode.  So, in such case, in strictly speaking,
   // we shouldn't query layout information.  However, TS_E_NOLAYOUT bugs of
   // ITextStoreAPC::GetTextExt() blocks us to behave ideally.
   // For preventing it to be called, we should put off notifying TSF of
   // anything until layout information becomes available.