Bug 1280182 ContentCache should return non-empty rect as far as possible if query content event is relative to insertion point r=m_kato
ContentCache may store composition string's rects which are inserted by one or more older composition event. Even in such case, native IME, especially TIP of TSF, expects non-empty rects. Therefore, if native IME handler uses "insertion point relative query", ContentCache should return non-empty rect as far as possible. For example, even if query offset or range is not in its rect array of composition string, ContentCache should adjust the offset into the stored range. MozReview-Commit-ID: 7hcIqxOWFk2
This commit is contained in:
@@ -495,7 +495,8 @@ ContentCacheInParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aEvent.mInput.mRelativeToInsertionPoint) {
|
||||
bool isRelativeToInsertionPoint = aEvent.mInput.mRelativeToInsertionPoint;
|
||||
if (isRelativeToInsertionPoint) {
|
||||
if (aWidget->PluginHasFocus()) {
|
||||
if (NS_WARN_IF(!aEvent.mInput.MakeOffsetAbsolute(0))) {
|
||||
return false;
|
||||
@@ -605,9 +606,14 @@ ContentCacheInParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent,
|
||||
"FAILED because mSelection is not valid", this));
|
||||
return true;
|
||||
}
|
||||
// Note that if the query is relative to insertion point, the query was
|
||||
// probably requested by native IME. In such case, we should return
|
||||
// non-empty rect since returning failure causes IME showing its window
|
||||
// at odd position.
|
||||
if (aEvent.mInput.mLength) {
|
||||
if (NS_WARN_IF(!GetUnionTextRects(aEvent.mInput.mOffset,
|
||||
aEvent.mInput.mLength,
|
||||
isRelativeToInsertionPoint,
|
||||
aEvent.mReply.mRect))) {
|
||||
// XXX We don't have cache for this request.
|
||||
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
||||
@@ -618,6 +624,7 @@ ContentCacheInParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent,
|
||||
} else {
|
||||
// If the length is 0, we should return caret rect instead.
|
||||
if (NS_WARN_IF(!GetCaretRect(aEvent.mInput.mOffset,
|
||||
isRelativeToInsertionPoint,
|
||||
aEvent.mReply.mRect))) {
|
||||
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
||||
("ContentCacheInParent: 0x%p HandleQueryContentEvent(), "
|
||||
@@ -659,7 +666,12 @@ ContentCacheInParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent,
|
||||
"FAILED because mSelection is not valid", this));
|
||||
return true;
|
||||
}
|
||||
// Note that if the query is relative to insertion point, the query was
|
||||
// probably requested by native IME. In such case, we should return
|
||||
// non-empty rect since returning failure causes IME showing its window
|
||||
// at odd position.
|
||||
if (NS_WARN_IF(!GetCaretRect(aEvent.mInput.mOffset,
|
||||
isRelativeToInsertionPoint,
|
||||
aEvent.mReply.mRect))) {
|
||||
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
||||
("ContentCacheInParent: 0x%p HandleQueryContentEvent(), "
|
||||
@@ -692,13 +704,16 @@ ContentCacheInParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent,
|
||||
|
||||
bool
|
||||
ContentCacheInParent::GetTextRect(uint32_t aOffset,
|
||||
bool aRoundToExistingOffset,
|
||||
LayoutDeviceIntRect& aTextRect) const
|
||||
{
|
||||
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
||||
("ContentCacheInParent: 0x%p GetTextRect(aOffset=%u), "
|
||||
("ContentCacheInParent: 0x%p GetTextRect(aOffset=%u, "
|
||||
"aRoundToExistingOffset=%s), "
|
||||
"mTextRectArray={ mStart=%u, mRects.Length()=%u }, "
|
||||
"mSelection={ mAnchor=%u, mFocus=%u }",
|
||||
this, aOffset, mTextRectArray.mStart, mTextRectArray.mRects.Length(),
|
||||
this, aOffset, GetBoolName(aRoundToExistingOffset),
|
||||
mTextRectArray.mStart, mTextRectArray.mRects.Length(),
|
||||
mSelection.mAnchor, mSelection.mFocus));
|
||||
|
||||
if (!aOffset) {
|
||||
@@ -717,26 +732,44 @@ ContentCacheInParent::GetTextRect(uint32_t aOffset,
|
||||
return !aTextRect.IsEmpty();
|
||||
}
|
||||
|
||||
uint32_t offset = aOffset;
|
||||
if (!mTextRectArray.InRange(aOffset)) {
|
||||
aTextRect.SetEmpty();
|
||||
return false;
|
||||
if (!aRoundToExistingOffset) {
|
||||
aTextRect.SetEmpty();
|
||||
return false;
|
||||
}
|
||||
if (!mTextRectArray.IsValid()) {
|
||||
// If there are no rects in mTextRectArray, we should refer the start of
|
||||
// the selection because IME must query a char rect around it if there is
|
||||
// no composition.
|
||||
aTextRect = mSelection.StartCharRect();
|
||||
return !aTextRect.IsEmpty();
|
||||
}
|
||||
if (offset < mTextRectArray.StartOffset()) {
|
||||
offset = mTextRectArray.StartOffset();
|
||||
} else {
|
||||
offset = mTextRectArray.EndOffset() - 1;
|
||||
}
|
||||
}
|
||||
aTextRect = mTextRectArray.GetRect(aOffset);
|
||||
return true;
|
||||
aTextRect = mTextRectArray.GetRect(offset);
|
||||
return !aTextRect.IsEmpty();
|
||||
}
|
||||
|
||||
bool
|
||||
ContentCacheInParent::GetUnionTextRects(
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
bool aRoundToExistingOffset,
|
||||
LayoutDeviceIntRect& aUnionTextRect) const
|
||||
{
|
||||
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
||||
("ContentCacheInParent: 0x%p GetUnionTextRects(aOffset=%u, "
|
||||
"aLength=%u), mTextRectArray={ mStart=%u, mRects.Length()=%u }, "
|
||||
"aLength=%u, aRoundToExistingOffset=%s), mTextRectArray={ "
|
||||
"mStart=%u, mRects.Length()=%u }, "
|
||||
"mSelection={ mAnchor=%u, mFocus=%u }",
|
||||
this, aOffset, aLength, mTextRectArray.mStart,
|
||||
mTextRectArray.mRects.Length(), mSelection.mAnchor, mSelection.mFocus));
|
||||
this, aOffset, aLength, GetBoolName(aRoundToExistingOffset),
|
||||
mTextRectArray.mStart, mTextRectArray.mRects.Length(),
|
||||
mSelection.mAnchor, mSelection.mFocus));
|
||||
|
||||
CheckedInt<uint32_t> endOffset =
|
||||
CheckedInt<uint32_t>(aOffset) + aLength;
|
||||
@@ -780,9 +813,11 @@ ContentCacheInParent::GetUnionTextRects(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mTextRectArray.IsOverlappingWith(aOffset, aLength)) {
|
||||
if ((aRoundToExistingOffset && mTextRectArray.IsValid()) ||
|
||||
mTextRectArray.IsOverlappingWith(aOffset, aLength)) {
|
||||
aUnionTextRect =
|
||||
mTextRectArray.GetUnionRectAsFarAsPossible(aOffset, aLength);
|
||||
mTextRectArray.GetUnionRectAsFarAsPossible(aOffset, aLength,
|
||||
aRoundToExistingOffset);
|
||||
} else {
|
||||
aUnionTextRect.SetEmpty();
|
||||
}
|
||||
@@ -801,15 +836,18 @@ ContentCacheInParent::GetUnionTextRects(
|
||||
|
||||
bool
|
||||
ContentCacheInParent::GetCaretRect(uint32_t aOffset,
|
||||
bool aRoundToExistingOffset,
|
||||
LayoutDeviceIntRect& aCaretRect) const
|
||||
{
|
||||
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
||||
("ContentCacheInParent: 0x%p GetCaretRect(aOffset=%u), "
|
||||
("ContentCacheInParent: 0x%p GetCaretRect(aOffset=%u, "
|
||||
"aRoundToExistingOffset=%s), "
|
||||
"mCaret={ mOffset=%u, mRect=%s, IsValid()=%s }, mTextRectArray={ "
|
||||
"mStart=%u, mRects.Length()=%u }, mSelection={ mAnchor=%u, mFocus=%u, "
|
||||
"mWritingMode=%s, mAnchorCharRect=%s, mFocusCharRect=%s }, "
|
||||
"mFirstCharRect=%s",
|
||||
this, aOffset, mCaret.mOffset, GetRectText(mCaret.mRect).get(),
|
||||
this, aOffset, GetBoolName(aRoundToExistingOffset),
|
||||
mCaret.mOffset, GetRectText(mCaret.mRect).get(),
|
||||
GetBoolName(mCaret.IsValid()), mTextRectArray.mStart,
|
||||
mTextRectArray.mRects.Length(), mSelection.mAnchor, mSelection.mFocus,
|
||||
GetWritingModeName(mSelection.mWritingMode).get(),
|
||||
@@ -823,10 +861,11 @@ ContentCacheInParent::GetCaretRect(uint32_t aOffset,
|
||||
}
|
||||
|
||||
// Guess caret rect from the text rect if it's stored.
|
||||
if (!GetTextRect(aOffset, aCaretRect)) {
|
||||
if (!GetTextRect(aOffset, aRoundToExistingOffset, aCaretRect)) {
|
||||
// There might be previous character rect in the cache. If so, we can
|
||||
// guess the caret rect with it.
|
||||
if (!aOffset || !GetTextRect(aOffset - 1, aCaretRect)) {
|
||||
if (!aOffset ||
|
||||
!GetTextRect(aOffset - 1, aRoundToExistingOffset, aCaretRect)) {
|
||||
aCaretRect.SetEmpty();
|
||||
return false;
|
||||
}
|
||||
@@ -1114,14 +1153,21 @@ ContentCache::TextRectArray::GetUnionRect(uint32_t aOffset,
|
||||
LayoutDeviceIntRect
|
||||
ContentCache::TextRectArray::GetUnionRectAsFarAsPossible(
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength) const
|
||||
uint32_t aLength,
|
||||
bool aRoundToExistingOffset) const
|
||||
{
|
||||
LayoutDeviceIntRect rect;
|
||||
if (!IsOverlappingWith(aOffset, aLength)) {
|
||||
if (!aRoundToExistingOffset && !IsOverlappingWith(aOffset, aLength)) {
|
||||
return rect;
|
||||
}
|
||||
uint32_t startOffset = std::max(aOffset, mStart);
|
||||
if (aRoundToExistingOffset && startOffset >= EndOffset()) {
|
||||
startOffset = EndOffset() - 1;
|
||||
}
|
||||
uint32_t endOffset = std::min(aOffset + aLength, EndOffset());
|
||||
if (aRoundToExistingOffset && endOffset < mStart + 1) {
|
||||
endOffset = mStart + 1;
|
||||
}
|
||||
for (uint32_t i = 0; i < endOffset - startOffset; i++) {
|
||||
rect = rect.Union(mRects[startOffset - mStart + i]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user