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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aEvent.mInput.mRelativeToInsertionPoint) {
|
bool isRelativeToInsertionPoint = aEvent.mInput.mRelativeToInsertionPoint;
|
||||||
|
if (isRelativeToInsertionPoint) {
|
||||||
if (aWidget->PluginHasFocus()) {
|
if (aWidget->PluginHasFocus()) {
|
||||||
if (NS_WARN_IF(!aEvent.mInput.MakeOffsetAbsolute(0))) {
|
if (NS_WARN_IF(!aEvent.mInput.MakeOffsetAbsolute(0))) {
|
||||||
return false;
|
return false;
|
||||||
@@ -605,9 +606,14 @@ ContentCacheInParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent,
|
|||||||
"FAILED because mSelection is not valid", this));
|
"FAILED because mSelection is not valid", this));
|
||||||
return true;
|
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 (aEvent.mInput.mLength) {
|
||||||
if (NS_WARN_IF(!GetUnionTextRects(aEvent.mInput.mOffset,
|
if (NS_WARN_IF(!GetUnionTextRects(aEvent.mInput.mOffset,
|
||||||
aEvent.mInput.mLength,
|
aEvent.mInput.mLength,
|
||||||
|
isRelativeToInsertionPoint,
|
||||||
aEvent.mReply.mRect))) {
|
aEvent.mReply.mRect))) {
|
||||||
// XXX We don't have cache for this request.
|
// XXX We don't have cache for this request.
|
||||||
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
||||||
@@ -618,6 +624,7 @@ ContentCacheInParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent,
|
|||||||
} else {
|
} else {
|
||||||
// If the length is 0, we should return caret rect instead.
|
// If the length is 0, we should return caret rect instead.
|
||||||
if (NS_WARN_IF(!GetCaretRect(aEvent.mInput.mOffset,
|
if (NS_WARN_IF(!GetCaretRect(aEvent.mInput.mOffset,
|
||||||
|
isRelativeToInsertionPoint,
|
||||||
aEvent.mReply.mRect))) {
|
aEvent.mReply.mRect))) {
|
||||||
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
||||||
("ContentCacheInParent: 0x%p HandleQueryContentEvent(), "
|
("ContentCacheInParent: 0x%p HandleQueryContentEvent(), "
|
||||||
@@ -659,7 +666,12 @@ ContentCacheInParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent,
|
|||||||
"FAILED because mSelection is not valid", this));
|
"FAILED because mSelection is not valid", this));
|
||||||
return true;
|
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,
|
if (NS_WARN_IF(!GetCaretRect(aEvent.mInput.mOffset,
|
||||||
|
isRelativeToInsertionPoint,
|
||||||
aEvent.mReply.mRect))) {
|
aEvent.mReply.mRect))) {
|
||||||
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
MOZ_LOG(sContentCacheLog, LogLevel::Error,
|
||||||
("ContentCacheInParent: 0x%p HandleQueryContentEvent(), "
|
("ContentCacheInParent: 0x%p HandleQueryContentEvent(), "
|
||||||
@@ -692,13 +704,16 @@ ContentCacheInParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent,
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
ContentCacheInParent::GetTextRect(uint32_t aOffset,
|
ContentCacheInParent::GetTextRect(uint32_t aOffset,
|
||||||
|
bool aRoundToExistingOffset,
|
||||||
LayoutDeviceIntRect& aTextRect) const
|
LayoutDeviceIntRect& aTextRect) const
|
||||||
{
|
{
|
||||||
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
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 }, "
|
"mTextRectArray={ mStart=%u, mRects.Length()=%u }, "
|
||||||
"mSelection={ mAnchor=%u, mFocus=%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));
|
mSelection.mAnchor, mSelection.mFocus));
|
||||||
|
|
||||||
if (!aOffset) {
|
if (!aOffset) {
|
||||||
@@ -717,26 +732,44 @@ ContentCacheInParent::GetTextRect(uint32_t aOffset,
|
|||||||
return !aTextRect.IsEmpty();
|
return !aTextRect.IsEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t offset = aOffset;
|
||||||
if (!mTextRectArray.InRange(aOffset)) {
|
if (!mTextRectArray.InRange(aOffset)) {
|
||||||
|
if (!aRoundToExistingOffset) {
|
||||||
aTextRect.SetEmpty();
|
aTextRect.SetEmpty();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
aTextRect = mTextRectArray.GetRect(aOffset);
|
if (!mTextRectArray.IsValid()) {
|
||||||
return true;
|
// 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(offset);
|
||||||
|
return !aTextRect.IsEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ContentCacheInParent::GetUnionTextRects(
|
ContentCacheInParent::GetUnionTextRects(
|
||||||
uint32_t aOffset,
|
uint32_t aOffset,
|
||||||
uint32_t aLength,
|
uint32_t aLength,
|
||||||
|
bool aRoundToExistingOffset,
|
||||||
LayoutDeviceIntRect& aUnionTextRect) const
|
LayoutDeviceIntRect& aUnionTextRect) const
|
||||||
{
|
{
|
||||||
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
||||||
("ContentCacheInParent: 0x%p GetUnionTextRects(aOffset=%u, "
|
("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 }",
|
"mSelection={ mAnchor=%u, mFocus=%u }",
|
||||||
this, aOffset, aLength, mTextRectArray.mStart,
|
this, aOffset, aLength, GetBoolName(aRoundToExistingOffset),
|
||||||
mTextRectArray.mRects.Length(), mSelection.mAnchor, mSelection.mFocus));
|
mTextRectArray.mStart, mTextRectArray.mRects.Length(),
|
||||||
|
mSelection.mAnchor, mSelection.mFocus));
|
||||||
|
|
||||||
CheckedInt<uint32_t> endOffset =
|
CheckedInt<uint32_t> endOffset =
|
||||||
CheckedInt<uint32_t>(aOffset) + aLength;
|
CheckedInt<uint32_t>(aOffset) + aLength;
|
||||||
@@ -780,9 +813,11 @@ ContentCacheInParent::GetUnionTextRects(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mTextRectArray.IsOverlappingWith(aOffset, aLength)) {
|
if ((aRoundToExistingOffset && mTextRectArray.IsValid()) ||
|
||||||
|
mTextRectArray.IsOverlappingWith(aOffset, aLength)) {
|
||||||
aUnionTextRect =
|
aUnionTextRect =
|
||||||
mTextRectArray.GetUnionRectAsFarAsPossible(aOffset, aLength);
|
mTextRectArray.GetUnionRectAsFarAsPossible(aOffset, aLength,
|
||||||
|
aRoundToExistingOffset);
|
||||||
} else {
|
} else {
|
||||||
aUnionTextRect.SetEmpty();
|
aUnionTextRect.SetEmpty();
|
||||||
}
|
}
|
||||||
@@ -801,15 +836,18 @@ ContentCacheInParent::GetUnionTextRects(
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
ContentCacheInParent::GetCaretRect(uint32_t aOffset,
|
ContentCacheInParent::GetCaretRect(uint32_t aOffset,
|
||||||
|
bool aRoundToExistingOffset,
|
||||||
LayoutDeviceIntRect& aCaretRect) const
|
LayoutDeviceIntRect& aCaretRect) const
|
||||||
{
|
{
|
||||||
MOZ_LOG(sContentCacheLog, LogLevel::Info,
|
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={ "
|
"mCaret={ mOffset=%u, mRect=%s, IsValid()=%s }, mTextRectArray={ "
|
||||||
"mStart=%u, mRects.Length()=%u }, mSelection={ mAnchor=%u, mFocus=%u, "
|
"mStart=%u, mRects.Length()=%u }, mSelection={ mAnchor=%u, mFocus=%u, "
|
||||||
"mWritingMode=%s, mAnchorCharRect=%s, mFocusCharRect=%s }, "
|
"mWritingMode=%s, mAnchorCharRect=%s, mFocusCharRect=%s }, "
|
||||||
"mFirstCharRect=%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,
|
GetBoolName(mCaret.IsValid()), mTextRectArray.mStart,
|
||||||
mTextRectArray.mRects.Length(), mSelection.mAnchor, mSelection.mFocus,
|
mTextRectArray.mRects.Length(), mSelection.mAnchor, mSelection.mFocus,
|
||||||
GetWritingModeName(mSelection.mWritingMode).get(),
|
GetWritingModeName(mSelection.mWritingMode).get(),
|
||||||
@@ -823,10 +861,11 @@ ContentCacheInParent::GetCaretRect(uint32_t aOffset,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Guess caret rect from the text rect if it's stored.
|
// 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
|
// There might be previous character rect in the cache. If so, we can
|
||||||
// guess the caret rect with it.
|
// guess the caret rect with it.
|
||||||
if (!aOffset || !GetTextRect(aOffset - 1, aCaretRect)) {
|
if (!aOffset ||
|
||||||
|
!GetTextRect(aOffset - 1, aRoundToExistingOffset, aCaretRect)) {
|
||||||
aCaretRect.SetEmpty();
|
aCaretRect.SetEmpty();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1114,14 +1153,21 @@ ContentCache::TextRectArray::GetUnionRect(uint32_t aOffset,
|
|||||||
LayoutDeviceIntRect
|
LayoutDeviceIntRect
|
||||||
ContentCache::TextRectArray::GetUnionRectAsFarAsPossible(
|
ContentCache::TextRectArray::GetUnionRectAsFarAsPossible(
|
||||||
uint32_t aOffset,
|
uint32_t aOffset,
|
||||||
uint32_t aLength) const
|
uint32_t aLength,
|
||||||
|
bool aRoundToExistingOffset) const
|
||||||
{
|
{
|
||||||
LayoutDeviceIntRect rect;
|
LayoutDeviceIntRect rect;
|
||||||
if (!IsOverlappingWith(aOffset, aLength)) {
|
if (!aRoundToExistingOffset && !IsOverlappingWith(aOffset, aLength)) {
|
||||||
return rect;
|
return rect;
|
||||||
}
|
}
|
||||||
uint32_t startOffset = std::max(aOffset, mStart);
|
uint32_t startOffset = std::max(aOffset, mStart);
|
||||||
|
if (aRoundToExistingOffset && startOffset >= EndOffset()) {
|
||||||
|
startOffset = EndOffset() - 1;
|
||||||
|
}
|
||||||
uint32_t endOffset = std::min(aOffset + aLength, EndOffset());
|
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++) {
|
for (uint32_t i = 0; i < endOffset - startOffset; i++) {
|
||||||
rect = rect.Union(mRects[startOffset - mStart + i]);
|
rect = rect.Union(mRects[startOffset - mStart + i]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,6 +108,18 @@ protected:
|
|||||||
"The caller should check if the selection is valid");
|
"The caller should check if the selection is valid");
|
||||||
return Reversed() ? mAnchor - mFocus : mFocus - mAnchor;
|
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;
|
} mSelection;
|
||||||
|
|
||||||
bool IsSelectionValid() const
|
bool IsSelectionValid() const
|
||||||
@@ -213,8 +225,9 @@ protected:
|
|||||||
}
|
}
|
||||||
LayoutDeviceIntRect GetRect(uint32_t aOffset) const;
|
LayoutDeviceIntRect GetRect(uint32_t aOffset) const;
|
||||||
LayoutDeviceIntRect GetUnionRect(uint32_t aOffset, uint32_t aLength) const;
|
LayoutDeviceIntRect GetUnionRect(uint32_t aOffset, uint32_t aLength) const;
|
||||||
LayoutDeviceIntRect GetUnionRectAsFarAsPossible(uint32_t aOffset,
|
LayoutDeviceIntRect GetUnionRectAsFarAsPossible(
|
||||||
uint32_t aLength) const;
|
uint32_t aOffset, uint32_t aLength,
|
||||||
|
bool aRoundToExistingOffset) const;
|
||||||
} mTextRectArray;
|
} mTextRectArray;
|
||||||
|
|
||||||
LayoutDeviceIntRect mEditorRect;
|
LayoutDeviceIntRect mEditorRect;
|
||||||
@@ -371,11 +384,20 @@ private:
|
|||||||
|
|
||||||
bool mIsComposing;
|
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 GetTextRect(uint32_t aOffset,
|
||||||
|
bool aRoundToExistingOffset,
|
||||||
LayoutDeviceIntRect& aTextRect) const;
|
LayoutDeviceIntRect& aTextRect) const;
|
||||||
bool GetUnionTextRects(uint32_t aOffset,
|
bool GetUnionTextRects(uint32_t aOffset,
|
||||||
uint32_t aLength,
|
uint32_t aLength,
|
||||||
|
bool aRoundToExistingOffset,
|
||||||
LayoutDeviceIntRect& aUnionTextRect) const;
|
LayoutDeviceIntRect& aUnionTextRect) const;
|
||||||
|
|
||||||
void FlushPendingNotifications(nsIWidget* aWidget);
|
void FlushPendingNotifications(nsIWidget* aWidget);
|
||||||
|
|||||||
Reference in New Issue
Block a user