tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 905d0f6bed2095a68e076ac1d619c35459bde6cb
parent 5a9aae988c77e85395f94537fd06f4be51cf197d
Author: Masayuki Nakano <masayuki@d-toybox.com>
Date:   Wed, 12 Nov 2025 22:52:08 +0000

Bug 1824143 - part 2: Make `TSFTextStore::FlushPendingActions()` set `mDeferNotifyingTSFUntilNextUpdate` before dispatching composition events r=m_kato

This fixes a regression of bug 1137561.  That made `TSFTextStore` stop
dispatching composition events directly.  However, its dispatcher sets
`mDeferNotifyingTSFUntilNextUpdate` to notify TSF of anything until
it receives `NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED` since
`GetTextExt()` etc will return error if the layout has not be updated.
So, we started return `E_FAIL` a lot from various `ITextStoreACP`
methods if TSF/TIP queries something immediately after updating
the composition.

Differential Revision: https://phabricator.services.mozilla.com/D271560

Diffstat:
Mwidget/windows/TSFTextStore.cpp | 34++++++++++++++++++++++++++++++++++
Mwidget/windows/TSFTextStoreBase.cpp | 5-----
2 files changed, 34 insertions(+), 5 deletions(-)

diff --git a/widget/windows/TSFTextStore.cpp b/widget/windows/TSFTextStore.cpp @@ -353,7 +353,12 @@ void TSFTextStore::FlushPendingActions() { break; } + const bool hadDeferredNotifyingTSFUnTilNextUpdate = + mDeferNotifyingTSFUntilNextUpdate; if (action.mAdjustSelection) { + // Don't notify TSF until we receive + // NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED. + mDeferNotifyingTSFUntilNextUpdate = true; // Select composition range so the new composition replaces the range WidgetSelectionEvent selectionSet(true, eSetSelection, widget); widget->InitEvent(selectionSet); @@ -371,6 +376,8 @@ void TSFTextStore::FlushPendingActions() { ("0x%p TSFTextStore::FlushPendingActions() " "FAILED due to eSetSelection failure", this)); + mDeferNotifyingTSFUntilNextUpdate = + hadDeferredNotifyingTSFUnTilNextUpdate; break; } } @@ -379,6 +386,9 @@ void TSFTextStore::FlushPendingActions() { // NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED. Therefore, we should // wait to clear mContentForTSF until it's notified. mDeferClearingContentForTSF = true; + // Don't notify TSF until we receive + // NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED. + mDeferNotifyingTSFUntilNextUpdate = true; MOZ_LOG(gIMELog, LogLevel::Debug, ("0x%p TSFTextStore::FlushPendingActions() " @@ -394,6 +404,8 @@ void TSFTextStore::FlushPendingActions() { "FAILED to dispatch compositionstart event, " "IsHandlingCompositionInContent()=%s", this, TSFUtils::BoolToChar(IsHandlingCompositionInContent()))); + mDeferNotifyingTSFUntilNextUpdate = + hadDeferredNotifyingTSFUnTilNextUpdate; // XXX Is this right? If there is a composition in content, // shouldn't we wait NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED? mDeferClearingContentForTSF = !IsHandlingCompositionInContent(); @@ -436,6 +448,11 @@ void TSFTextStore::FlushPendingActions() { this)); WidgetEventTime eventTime = widget->CurrentMessageWidgetEventTime(); nsEventStatus status; + // Don't notify TSF until we receive + // NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED. + const bool hadDeferredNotifyingTSFUnTilNextUpdate = + mDeferNotifyingTSFUntilNextUpdate; + mDeferNotifyingTSFUntilNextUpdate = true; rv = mDispatcher->FlushPendingComposition(status, &eventTime); if (NS_WARN_IF(NS_FAILED(rv))) { MOZ_LOG( @@ -444,6 +461,8 @@ void TSFTextStore::FlushPendingActions() { "FAILED to dispatch compositionchange event, " "IsHandlingCompositionInContent()=%s", this, TSFUtils::BoolToChar(IsHandlingCompositionInContent()))); + mDeferNotifyingTSFUntilNextUpdate = + hadDeferredNotifyingTSFUnTilNextUpdate; // XXX Is this right? If there is a composition in content, // shouldn't we wait NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED? mDeferClearingContentForTSF = !IsHandlingCompositionInContent(); @@ -473,6 +492,11 @@ void TSFTextStore::FlushPendingActions() { this)); WidgetEventTime eventTime = widget->CurrentMessageWidgetEventTime(); nsEventStatus status; + // Don't notify TSF until we receive + // NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED. + const bool hadDeferredNotifyingTSFUnTilNextUpdate = + mDeferNotifyingTSFUntilNextUpdate; + mDeferNotifyingTSFUntilNextUpdate = true; rv = mDispatcher->CommitComposition(status, &action.mData, &eventTime); if (NS_WARN_IF(NS_FAILED(rv))) { MOZ_LOG( @@ -481,6 +505,8 @@ void TSFTextStore::FlushPendingActions() { "FAILED to dispatch compositioncommit event, " "IsHandlingCompositionInContent()=%s", this, TSFUtils::BoolToChar(IsHandlingCompositionInContent()))); + mDeferNotifyingTSFUntilNextUpdate = + hadDeferredNotifyingTSFUnTilNextUpdate; // XXX Is this right? If there is a composition in content, // shouldn't we wait NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED? mDeferClearingContentForTSF = !IsHandlingCompositionInContent(); @@ -505,6 +531,12 @@ void TSFTextStore::FlushPendingActions() { break; } + // Don't notify TSF until we receive + // NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED. + const bool hadDeferredNotifyingTSFUnTilNextUpdate = + mDeferNotifyingTSFUntilNextUpdate; + mDeferNotifyingTSFUntilNextUpdate = true; + WidgetSelectionEvent selectionSet(true, eSetSelection, widget); selectionSet.mOffset = static_cast<uint32_t>(action.mSelectionStart); selectionSet.mLength = static_cast<uint32_t>(action.mSelectionLength); @@ -519,6 +551,8 @@ void TSFTextStore::FlushPendingActions() { ("0x%p TSFTextStore::FlushPendingActions() " "FAILED due to eSetSelection failure", this)); + mDeferNotifyingTSFUntilNextUpdate = + hadDeferredNotifyingTSFUnTilNextUpdate; break; } break; diff --git a/widget/windows/TSFTextStoreBase.cpp b/widget/windows/TSFTextStoreBase.cpp @@ -313,11 +313,6 @@ void TSFTextStoreBase::DispatchEvent(WidgetGUIEvent& aEvent) { if (NS_WARN_IF(!mWidget) || NS_WARN_IF(mWidget->Destroyed())) { return; } - // If the event isn't a query content event, the event may be handled - // asynchronously. So, we should put off to answer from GetTextExt() etc. - if (!aEvent.AsQueryContentEvent()) { - mDeferNotifyingTSFUntilNextUpdate = true; - } mWidget->DispatchWindowEvent(aEvent); }