Bug 1678553 - part 3: Make `TSFTextStore::Content` store the latest composition range with `Maybe` and new class which stores start and end offsets r=m_kato

They are not always set. Therefore, it should be managed with `Maybe`.
And this creates a new class `StartAndEndOffsets` and it's similar to
`OffsetAndData` class so that similar API makes the code easier to read.

Differential Revision: https://phabricator.services.mozilla.com/D97949
This commit is contained in:
Masayuki Nakano 2020-11-26 12:37:42 +00:00
Родитель 960c5da1e7
Коммит 8e6587ecde
4 изменённых файлов: 119 добавлений и 72 удалений

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

@ -462,6 +462,8 @@ enum class TextRangeType : RawTextRangeType;
// IMEData.h
template <typename IntType>
class StartAndEndOffsets;
template <typename IntType>
class OffsetAndData;

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

@ -36,6 +36,69 @@ class MOZ_STACK_CLASS PrintStringDetail : public nsAutoCString {
static nsCString PrintCharData(char32_t aChar);
};
// StartAndEndOffsets represents a range in flat-text.
template <typename IntType>
class StartAndEndOffsets {
protected:
static IntType MaxOffset() { return std::numeric_limits<IntType>::max(); }
public:
StartAndEndOffsets() = delete;
explicit StartAndEndOffsets(IntType aStartOffset, IntType aEndOffset)
: mStartOffset(aStartOffset),
mEndOffset(aStartOffset <= aEndOffset ? aEndOffset : aStartOffset) {
MOZ_ASSERT(aStartOffset <= mEndOffset);
}
IntType StartOffset() const { return mStartOffset; }
IntType Length() const { return mEndOffset - mStartOffset; }
IntType EndOffset() const { return mEndOffset; }
bool IsOffsetInRange(IntType aOffset) const {
return aOffset >= mStartOffset && aOffset < mEndOffset;
}
bool IsOffsetInRangeOrEndOffset(IntType aOffset) const {
return aOffset >= mStartOffset && aOffset <= mEndOffset;
}
void MoveTo(IntType aNewStartOffset) {
auto delta = static_cast<int64_t>(mStartOffset) - aNewStartOffset;
mStartOffset += delta;
mEndOffset += delta;
}
void SetOffsetAndLength(IntType aNewOffset, IntType aNewLength) {
mStartOffset = aNewOffset;
CheckedInt<IntType> endOffset(aNewOffset + aNewLength);
mEndOffset = endOffset.isValid() ? endOffset.value() : MaxOffset();
}
void SetEndOffset(IntType aEndOffset) {
MOZ_ASSERT(mStartOffset <= aEndOffset);
mEndOffset = std::max(aEndOffset, mStartOffset);
}
void SetStartAndEndOffsets(IntType aStartOffset, IntType aEndOffset) {
MOZ_ASSERT(aStartOffset <= aEndOffset);
mStartOffset = aStartOffset;
mEndOffset = aStartOffset <= aEndOffset ? aEndOffset : aStartOffset;
}
void SetLength(IntType aNewLength) {
CheckedInt<IntType> endOffset(mStartOffset + aNewLength);
mEndOffset = endOffset.isValid() ? endOffset.value() : MaxOffset();
}
friend std::ostream& operator<<(
std::ostream& aStream,
const StartAndEndOffsets<IntType>& aStartAndEndOffsets) {
aStream << "{ mStartOffset=" << aStartAndEndOffsets.mStartOffset
<< ", mEndOffset=" << aStartAndEndOffsets.mEndOffset
<< ", Length()=" << aStartAndEndOffsets.Length() << " }";
return aStream;
}
private:
IntType mStartOffset;
IntType mEndOffset;
};
// OffsetAndData class is designed for storing composition string and its
// start offset. Length() and EndOffset() return only valid length or
// offset. I.e., if the string is too long for inserting at the offset,
@ -58,6 +121,9 @@ class OffsetAndData {
return endOffset.isValid() ? mData.Length() : MaxOffset() - mOffset;
}
IntType EndOffset() const { return mOffset + Length(); }
StartAndEndOffsets<IntType> CreateStartAndEndOffsets() const {
return StartAndEndOffsets<IntType>(StartOffset(), EndOffset());
}
const nsString& DataRef() const {
// In strictly speaking, we should return substring which may be shrunken
// for rounding to the max offset. However, it's unrealistic edge case,

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

@ -4341,36 +4341,27 @@ TSFTextStore::GetACPFromPoint(TsViewCookie vcView, const POINT* pt,
STDMETHODIMP
TSFTextStore::GetTextExt(TsViewCookie vcView, LONG acpStart, LONG acpEnd,
RECT* prc, BOOL* pfClipped) {
MOZ_LOG(
sTextStoreLog, LogLevel::Info,
("0x%p TSFTextStore::GetTextExt(vcView=%ld, "
"acpStart=%ld, acpEnd=%ld, prc=0x%p, pfClipped=0x%p), "
"IsHandlingCompositionInParent()=%s, "
"IsHandlingCompositionInContent()=%s, "
"mContentForTSF={ MinOffsetOfLayoutChanged()=%u, "
"LatestCompositionStartOffset()=%d, LatestCompositionEndOffset()=%d, "
"mSelection={ acpStart=%ld, acpEnd=%ld, style.ase=%s, "
"style.fInterimChar=%s } "
"}, mComposition=%s, "
"mDeferNotifyingTSF=%s, mWaitingQueryLayout=%s, "
"IMEHandler::IsA11yHandlingNativeCaret()=%s",
this, vcView, acpStart, acpEnd, prc, pfClipped,
GetBoolName(IsHandlingCompositionInParent()),
GetBoolName(IsHandlingCompositionInContent()),
mContentForTSF.MinOffsetOfLayoutChanged(),
mContentForTSF.HasOrHadComposition()
? mContentForTSF.LatestCompositionStartOffset()
: -1,
mContentForTSF.HasOrHadComposition()
? mContentForTSF.LatestCompositionEndOffset()
: -1,
mContentForTSF.Selection().StartOffset(),
mContentForTSF.Selection().EndOffset(),
GetActiveSelEndName(mContentForTSF.Selection().ActiveSelEnd()),
GetBoolName(mContentForTSF.Selection().IsInterimChar()),
ToString(mComposition).c_str(), GetBoolName(mDeferNotifyingTSF),
GetBoolName(mWaitingQueryLayout),
GetBoolName(IMEHandler::IsA11yHandlingNativeCaret())));
MOZ_LOG(sTextStoreLog, LogLevel::Info,
("0x%p TSFTextStore::GetTextExt(vcView=%ld, "
"acpStart=%ld, acpEnd=%ld, prc=0x%p, pfClipped=0x%p), "
"IsHandlingCompositionInParent()=%s, "
"IsHandlingCompositionInContent()=%s, mContentForTSF=%s, "
"mSelection={ acpStart=%ld, acpEnd=%ld, style.ase=%s, "
"style.fInterimChar=%s } "
"}, mComposition=%s, "
"mDeferNotifyingTSF=%s, mWaitingQueryLayout=%s, "
"IMEHandler::IsA11yHandlingNativeCaret()=%s",
this, vcView, acpStart, acpEnd, prc, pfClipped,
GetBoolName(IsHandlingCompositionInParent()),
GetBoolName(IsHandlingCompositionInContent()),
mozilla::ToString(mContentForTSF).c_str(),
mContentForTSF.Selection().StartOffset(),
mContentForTSF.Selection().EndOffset(),
GetActiveSelEndName(mContentForTSF.Selection().ActiveSelEnd()),
GetBoolName(mContentForTSF.Selection().IsInterimChar()),
ToString(mComposition).c_str(), GetBoolName(mDeferNotifyingTSF),
GetBoolName(mWaitingQueryLayout),
GetBoolName(IMEHandler::IsA11yHandlingNativeCaret())));
if (!IsReadLocked()) {
MOZ_LOG(sTextStoreLog, LogLevel::Error,
@ -4471,7 +4462,7 @@ TSFTextStore::GetTextExt(TsViewCookie vcView, LONG acpStart, LONG acpEnd,
// yet, ContentCacheInParent is still open for relative offset query from
// the latest composition.
options.mRelativeToInsertionPoint = true;
startOffset -= mContentForTSF.LatestCompositionStartOffset();
startOffset -= mContentForTSF.LatestCompositionRange()->StartOffset();
} else if (!CanAccessActualContentDirectly()) {
// If TSF/TIP cannot access actual content directly, there may be pending
// text and/or selection changes which have not been notified TSF yet.
@ -4588,10 +4579,10 @@ bool TSFTextStore::MaybeHackNoErrorLayoutBugs(LONG& aACPStart, LONG& aACPEnd) {
MOZ_ASSERT(!mComposition.IsComposing() ||
mComposition.StartOffset() ==
mContentForTSF.LatestCompositionStartOffset());
mContentForTSF.LatestCompositionRange()->StartOffset());
MOZ_ASSERT(!mComposition.IsComposing() ||
mComposition.EndOffset() ==
mContentForTSF.LatestCompositionEndOffset());
mContentForTSF.LatestCompositionRange()->EndOffset());
// If TSF does not have the bug, we need to hack only with a few TIPs.
static const bool sAlllowToStopHackingIfFine =
@ -4708,10 +4699,10 @@ bool TSFTextStore::MaybeHackNoErrorLayoutBugs(LONG& aACPStart, LONG& aACPEnd) {
}
// If the range is in the composition string, we should return rectangle
// in it as far as possible.
if (aACPStart < mContentForTSF.LatestCompositionStartOffset() ||
aACPStart > mContentForTSF.LatestCompositionEndOffset() ||
aACPEnd < mContentForTSF.LatestCompositionStartOffset() ||
aACPEnd > mContentForTSF.LatestCompositionEndOffset()) {
if (!mContentForTSF.LatestCompositionRange()->IsOffsetInRangeOrEndOffset(
aACPStart) ||
!mContentForTSF.LatestCompositionRange()->IsOffsetInRangeOrEndOffset(
aACPEnd)) {
return false;
}
break;
@ -4725,10 +4716,10 @@ bool TSFTextStore::MaybeHackNoErrorLayoutBugs(LONG& aACPStart, LONG& aACPEnd) {
DoNotReturnNoLayoutErrorToJapanist10OfCompositionString()) {
return false;
}
if (aACPStart < mContentForTSF.LatestCompositionStartOffset() ||
aACPStart > mContentForTSF.LatestCompositionEndOffset() ||
aACPEnd < mContentForTSF.LatestCompositionStartOffset() ||
aACPEnd > mContentForTSF.LatestCompositionEndOffset()) {
if (!mContentForTSF.LatestCompositionRange()->IsOffsetInRangeOrEndOffset(
aACPStart) ||
!mContentForTSF.LatestCompositionRange()->IsOffsetInRangeOrEndOffset(
aACPEnd)) {
return false;
}
break;
@ -4742,7 +4733,7 @@ bool TSFTextStore::MaybeHackNoErrorLayoutBugs(LONG& aACPStart, LONG& aACPEnd) {
if (!TSFPrefs::DoNotReturnNoLayoutErrorToFreeChangJie()) {
return false;
}
aACPEnd = mContentForTSF.LatestCompositionStartOffset();
aACPEnd = mContentForTSF.LatestCompositionRange()->StartOffset();
aACPStart = std::min(aACPStart, aACPEnd);
break;
// Some Traditional Chinese TIPs of Microsoft don't show candidate window
@ -4757,7 +4748,7 @@ bool TSFTextStore::MaybeHackNoErrorLayoutBugs(LONG& aACPStart, LONG& aACPEnd) {
!TSFPrefs::DoNotReturnNoLayoutErrorToMSTraditionalTIP()) {
return false;
}
aACPEnd = mContentForTSF.LatestCompositionStartOffset();
aACPEnd = mContentForTSF.LatestCompositionRange()->StartOffset();
aACPStart = std::min(aACPStart, aACPEnd);
break;
// Some Simplified Chinese TIPs of Microsoft don't show candidate window
@ -4774,7 +4765,7 @@ bool TSFTextStore::MaybeHackNoErrorLayoutBugs(LONG& aACPStart, LONG& aACPEnd) {
!TSFPrefs::DoNotReturnNoLayoutErrorToMSSimplifiedTIP()) {
return false;
}
aACPEnd = mContentForTSF.LatestCompositionStartOffset();
aACPEnd = mContentForTSF.LatestCompositionRange()->StartOffset();
aACPStart = std::min(aACPStart, aACPEnd);
break;
default:
@ -4794,14 +4785,15 @@ bool TSFTextStore::MaybeHackNoErrorLayoutBugs(LONG& aACPStart, LONG& aACPEnd) {
static_cast<int32_t>(mContentForTSF.MinOffsetOfLayoutChanged());
LONG lastUnmodifiedOffset = std::max(firstModifiedOffset - 1, 0);
if (mContentForTSF.IsLayoutChangedAt(aACPStart)) {
if (aACPStart >= mContentForTSF.LatestCompositionStartOffset()) {
if (aACPStart >= mContentForTSF.LatestCompositionRange()->StartOffset()) {
// If mContentForTSF has last composition string and current
// composition string, we can assume that ContentCacheInParent has
// cached rects of composition string at least length of current
// composition string. Otherwise, we can assume that rect for
// first character of composition string is stored since it was
// selection start or caret position.
LONG maxCachedOffset = mContentForTSF.LatestCompositionEndOffset();
LONG maxCachedOffset =
mContentForTSF.LatestCompositionRange()->EndOffset();
if (mContentForTSF.LastComposition().isSome()) {
maxCachedOffset = std::min(
maxCachedOffset, mContentForTSF.LastComposition()->EndOffset());
@ -4831,8 +4823,8 @@ bool TSFTextStore::MaybeHackNoErrorLayoutBugs(LONG& aACPStart, LONG& aACPEnd) {
// should keep using the original aACPEnd. Otherwise, we should set
// aACPEnd to larger value of aACPStart and lastUnmodifiedOffset.
else if (mContentForTSF.IsLayoutChangedAt(aACPEnd) &&
(aACPEnd < mContentForTSF.LatestCompositionStartOffset() ||
aACPEnd > mContentForTSF.LatestCompositionEndOffset())) {
!mContentForTSF.LatestCompositionRange()->IsOffsetInRangeOrEndOffset(
aACPEnd)) {
aACPEnd = std::max(aACPStart, lastUnmodifiedOffset);
}
@ -7122,7 +7114,7 @@ void TSFTextStore::Content::ReplaceTextWith(LONG aStart, LONG aLength,
mMinTextModifiedOffset = firstDifferentOffset =
mComposition.EndOffset();
}
mLatestCompositionEndOffset = mComposition.EndOffset();
mLatestCompositionRange = Some(mComposition.CreateStartAndEndOffsets());
MOZ_LOG(
sTextStoreLog, LogLevel::Debug,
("0x%p TSFTextStore::Content::ReplaceTextWith(aStart=%d, "
@ -7159,8 +7151,7 @@ void TSFTextStore::Content::StartComposition(
aCompositionView, aCompStart.mSelectionStart,
GetSubstring(static_cast<uint32_t>(aCompStart.mSelectionStart),
static_cast<uint32_t>(aCompStart.mSelectionLength)));
mLatestCompositionStartOffset = mComposition.StartOffset();
mLatestCompositionEndOffset = mComposition.EndOffset();
mLatestCompositionRange = Some(mComposition.CreateStartAndEndOffsets());
if (!aPreserveSelection) {
// XXX Do we need to set a new writing-mode here when setting a new
// selection? Currently, we just preserve the existing value.
@ -7188,8 +7179,7 @@ void TSFTextStore::Content::RestoreCommittedComposition(
// Restore the committed string as composing string.
mComposition.Start(aCompositionView, aCanceledCompositionEnd.mSelectionStart,
aCanceledCompositionEnd.mData);
mLatestCompositionStartOffset = mComposition.StartOffset();
mLatestCompositionEndOffset = mComposition.EndOffset();
mLatestCompositionRange = Some(mComposition.CreateStartAndEndOffsets());
}
void TSFTextStore::Content::EndComposition(const PendingAction& aCompEnd) {

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

@ -825,7 +825,7 @@ class TSFTextStore final : public ITextStoreACP,
mLastComposition.reset();
}
mMinTextModifiedOffset = NOT_MODIFIED;
mLatestCompositionStartOffset = mLatestCompositionEndOffset = LONG_MAX;
mLatestCompositionRange.reset();
mInitialized = true;
}
@ -883,15 +883,9 @@ class TSFTextStore final : public ITextStoreACP,
MOZ_ASSERT(mInitialized);
return mMinTextModifiedOffset;
}
LONG LatestCompositionStartOffset() const {
const Maybe<StartAndEndOffsets<LONG>>& LatestCompositionRange() const {
MOZ_ASSERT(mInitialized);
MOZ_ASSERT(HasOrHadComposition());
return mLatestCompositionStartOffset;
}
LONG LatestCompositionEndOffset() const {
MOZ_ASSERT(mInitialized);
MOZ_ASSERT(HasOrHadComposition());
return mLatestCompositionEndOffset;
return mLatestCompositionRange;
}
// Returns true if layout of the character at the aOffset has not been
@ -910,8 +904,7 @@ class TSFTextStore final : public ITextStoreACP,
}
bool HasOrHadComposition() const {
return mInitialized && mLatestCompositionStartOffset != LONG_MAX &&
mLatestCompositionEndOffset != LONG_MAX;
return mLatestCompositionRange.isSome();
}
TSFTextStore::Composition& Composition() { return mComposition; }
@ -924,10 +917,8 @@ class TSFTextStore final : public ITextStoreACP,
PrintStringDetail::kMaxLengthForEditor)
.get()
<< ", mLastComposition=" << aContent.mLastComposition
<< ", mLatestCompositionStartOffset="
<< aContent.mLatestCompositionStartOffset
<< ", mLatestCompositionEndOffset="
<< aContent.mLatestCompositionEndOffset
<< ", mLatestCompositionRange="
<< aContent.mLatestCompositionRange
<< ", mMinTextModifiedOffset=" << aContent.mMinTextModifiedOffset
<< ", mInitialized=" << aContent.mInitialized << " }";
return aStream;
@ -944,10 +935,8 @@ class TSFTextStore final : public ITextStoreACP,
TSFTextStore::Composition& mComposition;
TSFTextStore::Selection& mSelection;
// The latest composition's start and end offset. If composition hasn't
// been started since this instance is initialized, they are LONG_MAX.
LONG mLatestCompositionStartOffset;
LONG mLatestCompositionEndOffset;
// The latest composition's start and end offset.
Maybe<StartAndEndOffsets<LONG>> mLatestCompositionRange;
// The minimum offset of modified part of the text.
enum : uint32_t { NOT_MODIFIED = UINT32_MAX };