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

--HG--
extra : rebase_source : 8b1572e01cc56e5596ffba4eac0f7d5818b85a89
This commit is contained in:
Masayuki Nakano 2016-06-22 14:16:59 +09:00
parent 634475c80e
commit e195616e12
2 changed files with 89 additions and 21 deletions

View File

@ -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]);
}

View File

@ -108,6 +108,18 @@ protected:
"The caller should check if the selection is valid");
return Reversed() ? mAnchor - mFocus : mFocus - mAnchor;
}
LayoutDeviceIntRect StartCharRect() const
{
NS_ASSERTION(IsValid(),
"The caller should check if the selection is valid");
return Reversed() ? mFocusCharRect : mAnchorCharRect;
}
LayoutDeviceIntRect EndCharRect() const
{
NS_ASSERTION(IsValid(),
"The caller should check if the selection is valid");
return Reversed() ? mAnchorCharRect : mFocusCharRect;
}
} mSelection;
bool IsSelectionValid() const
@ -213,8 +225,9 @@ protected:
}
LayoutDeviceIntRect GetRect(uint32_t aOffset) const;
LayoutDeviceIntRect GetUnionRect(uint32_t aOffset, uint32_t aLength) const;
LayoutDeviceIntRect GetUnionRectAsFarAsPossible(uint32_t aOffset,
uint32_t aLength) const;
LayoutDeviceIntRect GetUnionRectAsFarAsPossible(
uint32_t aOffset, uint32_t aLength,
bool aRoundToExistingOffset) const;
} mTextRectArray;
LayoutDeviceIntRect mEditorRect;
@ -371,11 +384,20 @@ private:
bool mIsComposing;
bool GetCaretRect(uint32_t aOffset, LayoutDeviceIntRect& aCaretRect) const;
/**
* When following methods' aRoundToExistingOffset is true, even if specified
* offset or range is out of bounds, the result is computed with the existing
* cache forcibly.
*/
bool GetCaretRect(uint32_t aOffset,
bool aRoundToExistingOffset,
LayoutDeviceIntRect& aCaretRect) const;
bool GetTextRect(uint32_t aOffset,
bool aRoundToExistingOffset,
LayoutDeviceIntRect& aTextRect) const;
bool GetUnionTextRects(uint32_t aOffset,
uint32_t aLength,
bool aRoundToExistingOffset,
LayoutDeviceIntRect& aUnionTextRect) const;
void FlushPendingNotifications(nsIWidget* aWidget);