Bug 1746104 - part 3-7: make `IMContextWrapper` handle the cases there is no selection range r=m_kato

Perhaps, we should disable IME when DOM Selection does not have range.  However,
it's out of scope of this bug.  This patch makes `IMContextWrapper` try to
keep working with no selection range except at "compositionstart".

Differential Revision: https://phabricator.services.mozilla.com/D137420
This commit is contained in:
Masayuki Nakano 2022-02-07 22:33:36 +00:00
Родитель 0d30bd43ae
Коммит f27a9abf8b
2 изменённых файлов: 74 добавлений и 41 удалений

Просмотреть файл

@ -1492,13 +1492,12 @@ void IMContextWrapper::OnSelectionChange(
// event handler. So, we're dispatching eCompositionStart,
// we should ignore selection change notification.
if (mCompositionState == eCompositionState_CompositionStartDispatched) {
if (NS_WARN_IF(mSelection.isNothing()) ||
NS_WARN_IF(!mSelection->IsValid())) {
if (NS_WARN_IF(mSelection.isNothing())) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p OnSelectionChange(), FAILED, "
"new offset is too large, cannot keep composing",
this));
} else {
} else if (mSelection->HasRange()) {
// Modify the selection start offset with new offset.
mCompositionStart = mSelection->OffsetAndDataRef().StartOffset();
// XXX We should modify mSelectedStringRemovedByComposition?
@ -1510,6 +1509,12 @@ void IMContextWrapper::OnSelectionChange(
this, mCompositionStart));
// And don't reset the IM context.
return;
} else {
MOZ_LOG(
gIMELog, LogLevel::Debug,
("0x%p OnSelectionChange(), ignored, because of no selection range",
this));
return;
}
// Otherwise, reset the IM context due to impossible to keep composing.
}
@ -2131,6 +2136,14 @@ bool IMContextWrapper::DispatchCompositionStart(GtkIMContext* aContext) {
return false;
}
if (NS_WARN_IF(!mSelection->HasRange())) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p DispatchCompositionStart(), FAILED, "
"due to no selection",
this));
return false;
}
mComposingContext = static_cast<GtkIMContext*>(g_object_ref(aContext));
MOZ_ASSERT(mComposingContext);
@ -2252,10 +2265,13 @@ bool IMContextWrapper::DispatchCompositionChangeEvent(
if (NS_WARN_IF(
!EnsureToCacheSelection(&mSelectedStringRemovedByComposition))) {
// XXX How should we behave in this case??
} else {
} else if (mSelection->HasRange()) {
// XXX We should assume, for now, any web applications don't change
// selection at handling this compositionchange event.
mCompositionStart = mSelection->OffsetAndDataRef().StartOffset();
} else {
// If there is no selection range, we should keep previously storing
// mCompositionStart.
}
}
@ -2359,8 +2375,10 @@ bool IMContextWrapper::DispatchCompositionCommitEvent(
// apps, i.e., selection range is same as what selection expects, we
// shouldn't reset IME because the trigger of causing this commit may be an
// input for next composition and we shouldn't cancel it.
if (mSelection.isSome() && mSelection->IsValid()) {
mSelection->Collapse(mSelection->OffsetAndDataRef().StartOffset() +
if (mSelection.isSome()) {
mSelection->Collapse((mSelection->HasRange()
? mSelection->OffsetAndDataRef().StartOffset()
: mCompositionStart) +
aCommitString->Length());
MOZ_LOG(gIMELog, LogLevel::Info,
("0x%p DispatchCompositionCommitEvent(), mSelection=%s", this,
@ -2817,13 +2835,20 @@ void IMContextWrapper::SetCursorPosition(GtkIMContext* aContext) {
bool useCaret = false;
if (!mCompositionTargetRange.IsValid()) {
if (mSelection.isNothing() || !mSelection->IsValid()) {
if (mSelection.isNothing()) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p SetCursorPosition(), FAILED, "
"mCompositionTargetRange and mSelection are invalid",
this));
return;
}
if (!mSelection->HasRange()) {
MOZ_LOG(gIMELog, LogLevel::Warning,
("0x%p SetCursorPosition(), FAILED, "
"mCompositionTargetRange is invalid and there is no selection",
this));
return;
}
useCaret = true;
}
@ -2918,8 +2943,14 @@ nsresult IMContextWrapper::GetCurrentParagraph(nsAString& aText,
return NS_ERROR_FAILURE;
}
selOffset = mSelection->OffsetAndDataRef().StartOffset();
selLength = mSelection->OffsetAndDataRef().Length();
if (mSelection.isSome() && mSelection->HasRange()) {
selOffset = mSelection->OffsetAndDataRef().StartOffset();
selLength = mSelection->OffsetAndDataRef().Length();
} else {
// If there is no range, let's get all text instead...
selOffset = 0u;
selLength = INT32_MAX; // TODO: Change to UINT32_MAX, but see below
}
}
MOZ_LOG(gIMELog, LogLevel::Debug,
@ -3031,6 +3062,12 @@ nsresult IMContextWrapper::DeleteText(GtkIMContext* aContext, int32_t aOffset,
this));
return NS_ERROR_FAILURE;
}
if (!mSelection->HasRange()) {
MOZ_LOG(gIMELog, LogLevel::Debug,
("0x%p DeleteText(), does nothing, due to no selection range",
this));
return NS_OK;
}
selOffset = mSelection->OffsetAndDataRef().StartOffset();
}
@ -3165,8 +3202,8 @@ bool IMContextWrapper::EnsureToCacheSelection(nsAString* aSelectedString) {
aSelectedString->Truncate();
}
if (mSelection.isSome() && mSelection->IsValid()) {
if (aSelectedString) {
if (mSelection.isSome()) {
if (mSelection->HasRange() && aSelectedString) {
aSelectedString->Assign(mSelection->OffsetAndDataRef().DataRef());
}
return true;
@ -3194,17 +3231,10 @@ bool IMContextWrapper::EnsureToCacheSelection(nsAString* aSelectedString) {
}
mSelection = Some(Selection(querySelectedTextEvent));
if (!mSelection->IsValid()) {
MOZ_LOG(gIMELog, LogLevel::Error,
("0x%p EnsureToCacheSelection(), FAILED, due to "
"failure of query selection event (invalid result)",
this));
mSelection.reset();
return false;
}
if (!mSelection->OffsetAndDataRef().IsDataEmpty() && aSelectedString) {
aSelectedString->Assign(querySelectedTextEvent.mReply->DataRef());
if (mSelection->HasRange()) {
if (!mSelection->OffsetAndDataRef().IsDataEmpty() && aSelectedString) {
aSelectedString->Assign(querySelectedTextEvent.mReply->DataRef());
}
}
MOZ_LOG(gIMELog, LogLevel::Debug,

Просмотреть файл

@ -386,46 +386,49 @@ class IMContextWrapper final : public TextEventDispatcherListener {
class Selection final {
public:
Selection()
: mOffsetAndData(UINT32_MAX, EmptyString(),
OffsetAndDataFor::SelectedString) {}
Selection() = default;
explicit Selection(
const IMENotification::SelectionChangeDataBase& aSelectionChangeData)
: mOffsetAndData(aSelectionChangeData.ToUint32OffsetAndData()),
: mOffsetAndData(Some(aSelectionChangeData.ToUint32OffsetAndData())),
mWritingMode(aSelectionChangeData.GetWritingMode()) {}
explicit Selection(const WidgetQueryContentEvent& aSelectedTextEvent)
: mOffsetAndData(aSelectedTextEvent.mReply->StartOffset(),
aSelectedTextEvent.mReply->DataRef(),
OffsetAndDataFor::SelectedString),
: mOffsetAndData(Some(
OffsetAndData<uint32_t>(aSelectedTextEvent.mReply->StartOffset(),
aSelectedTextEvent.mReply->DataRef(),
OffsetAndDataFor::SelectedString))),
mWritingMode(aSelectedTextEvent.mReply->WritingModeRef()) {
MOZ_ASSERT(aSelectedTextEvent.mMessage == eQuerySelectedText);
MOZ_ASSERT(aSelectedTextEvent.mReply->mOffsetAndData.isSome());
}
Selection(uint32_t aOffset, const WritingMode& aWritingMode)
: mOffsetAndData(aOffset, EmptyString(),
OffsetAndDataFor::SelectedString),
: mOffsetAndData(Some(OffsetAndData<uint32_t>(
aOffset, EmptyString(), OffsetAndDataFor::SelectedString))),
mWritingMode(aWritingMode) {}
const OffsetAndData<uint32_t>& OffsetAndDataRef() const {
return mOffsetAndData;
return mOffsetAndData.ref();
}
void Collapse(uint32_t aOffset) { mOffsetAndData.Collapse(aOffset); }
void Collapse(uint32_t aOffset) {
if (mOffsetAndData.isSome()) {
mOffsetAndData->Collapse(aOffset);
} else {
mOffsetAndData.emplace(aOffset, EmptyString(),
OffsetAndDataFor::SelectedString);
}
}
void Clear() {
mOffsetAndData.SetOffsetAndData(UINT32_MAX, EmptyString());
mOffsetAndData.reset();
mWritingMode = WritingMode();
}
bool IsValid() const {
return mOffsetAndData.IsValid() &&
mOffsetAndData.StartOffset() != UINT32_MAX;
}
bool HasRange() const { return mOffsetAndData.isSome(); }
const WritingMode& WritingModeRef() const { return mWritingMode; }
friend std::ostream& operator<<(std::ostream& aStream,
const Selection& aSelection) {
if (aSelection.IsValid()) {
return aStream << "{ IsValid()=false }";
if (aSelection.HasRange()) {
return aStream << "{ HasRange()=false }";
}
aStream << "{ mOffsetAndData=" << aSelection.mOffsetAndData
<< ", mWritingMode=" << aSelection.mWritingMode << " }";
@ -433,7 +436,7 @@ class IMContextWrapper final : public TextEventDispatcherListener {
}
private:
OffsetAndData<uint32_t> mOffsetAndData;
Maybe<OffsetAndData<uint32_t>> mOffsetAndData;
WritingMode mWritingMode;
};
// If mSelection is Nothing, it means that EnsureToCacheSelection failed to