Bug 1677684 - part 2: Make `ContentCache` cache character rects in last commit composition string for undoing the commit r=m_kato

Usually, IME sets selection and considers candidate list position at starting
new composition.  However, Apple Japanese IME sometimes consider the candidate
list position at retrieving the character rects before setting selection.
Therefore, we need to store last commit string's character rects, but don't
need to store it in long time because Kakutei-Undo is supported by Japanese
IMEs and they work only immediately after committing a composition.  E.g.,
after moving caret, it won't be available.

Depends on D97838

Differential Revision: https://phabricator.services.mozilla.com/D97839
This commit is contained in:
Masayuki Nakano 2020-11-24 01:31:46 +00:00
Родитель 0869a8137b
Коммит 85b84f8248
8 изменённых файлов: 240 добавлений и 55 удалений

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

@ -15,10 +15,10 @@
#include "mozilla/EventDispatcher.h"
#include "mozilla/IMEStateManager.h"
#include "mozilla/MiscEvents.h"
#include "mozilla/Preferences.h"
#include "mozilla/PresShell.h"
#include "mozilla/RangeBoundary.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_intl.h"
#include "mozilla/TextComposition.h"
#include "mozilla/TextEvents.h"
#include "mozilla/Unused.h"
@ -71,8 +71,8 @@ TextComposition::TextComposition(nsPresContext* aPresContext, nsINode* aNode,
mHasDispatchedDOMTextEvent(false),
mHasReceivedCommitEvent(false),
mWasNativeCompositionEndEventDiscarded(false),
mAllowControlCharacters(Preferences::GetBool(
"dom.compositionevent.allow_control_characters", false)),
mAllowControlCharacters(
StaticPrefs::dom_compositionevent_allow_control_characters()),
mWasCompositionStringEmpty(true) {
MOZ_ASSERT(aCompositionEvent->mNativeIMEContext.IsValid());
}
@ -233,6 +233,18 @@ static void RemoveControlCharactersFrom(nsAString& aStr,
aStr.SetLength(curDest - dest);
}
nsString TextComposition::CommitStringIfCommittedAsIs() const {
nsString result(mLastData);
if (!mAllowControlCharacters) {
RemoveControlCharactersFrom(result, nullptr);
}
if (StaticPrefs::intl_ime_remove_placeholder_character_at_commit() &&
mLastData == IDEOGRAPHIC_SPACE) {
return EmptyString();
}
return result;
}
void TextComposition::DispatchCompositionEvent(
WidgetCompositionEvent* aCompositionEvent, nsEventStatus* aStatus,
EventDispatchingCallback* aCallBack, bool aIsSynthesized) {
@ -279,9 +291,8 @@ void TextComposition::DispatchCompositionEvent(
aCompositionEvent->mRanges = nullptr;
NS_ASSERTION(aCompositionEvent->mData.IsEmpty(),
"mData of eCompositionCommitAsIs should be empty string");
bool removePlaceholderCharacter = Preferences::GetBool(
"intl.ime.remove_placeholder_character_at_commit", false);
if (removePlaceholderCharacter && mLastData == IDEOGRAPHIC_SPACE) {
if (StaticPrefs::intl_ime_remove_placeholder_character_at_commit() &&
mLastData == IDEOGRAPHIC_SPACE) {
// If the last data is an ideographic space (FullWidth space), it might be
// a placeholder character of some Chinese IME. So, committing with
// this data might not be expected by users. Let's use empty string.

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

@ -55,6 +55,8 @@ class TextComposition final {
// The latest CompositionEvent.data value except compositionstart event.
// This value is modified at dispatching compositionupdate.
const nsString& LastData() const { return mLastData; }
// Returns commit string if it'll be commited as-is.
nsString CommitStringIfCommittedAsIs() const;
// The composition string which is already handled by the focused editor.
// I.e., this value must be same as the composition string on the focused
// editor. This value is modified at a call of

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

@ -1574,6 +1574,16 @@
value: false
mirror: always
# Allow control characters appear in composition string.
# When this is false, control characters except
# CHARACTER TABULATION (horizontal tab) are removed from
# both composition string and data attribute of compositionupdate
# and compositionend events.
- name: dom.compositionevent.allow_control_characters
type: bool
value: false
mirror: always
# Is support for CSSPseudoElement enabled?
- name: dom.css_pseudo_element.enabled
type: bool
@ -5071,6 +5081,15 @@
value: @IS_ANDROID@
mirror: always
# If you use legacy Chinese IME which puts an ideographic space to composition
# string as placeholder, this pref might be useful. If this is true and when
# web contents forcibly commits composition (e.g., moving focus), the
# ideographic space will be ignored (i.e., commits with empty string).
- name: intl.ime.remove_placeholder_character_at_commit
type: bool
value: false
mirror: always
#---------------------------------------------------------------------------
# Prefs starting with "javascript."
#---------------------------------------------------------------------------

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

@ -1997,12 +1997,6 @@ pref("intl.fallbackCharsetList.ISO-8859-1", "windows-1252");
pref("font.language.group", "chrome://global/locale/intl.properties");
pref("font.cjk_pref_fallback_order", "zh-cn,zh-hk,zh-tw,ja,ko");
// If you use legacy Chinese IME which puts an ideographic space to composition
// string as placeholder, this pref might be useful. If this is true and when
// web contents forcibly commits composition (e.g., moving focus), the
// ideographic space will be ignored (i.e., commits with empty string).
pref("intl.ime.remove_placeholder_character_at_commit", false);
pref("intl.uidirection", -1); // -1 to set from locale; 0 for LTR; 1 for RTL
// use en-US hyphenation by default for content tagged with plain lang="en"
@ -4343,13 +4337,6 @@ pref("narrate.voice", " { \"default\": \"automatic\" }");
// Only make voices that match content language available.
pref("narrate.filter-voices", true);
// Allow control characters appear in composition string.
// When this is false, control characters except
// CHARACTER TABULATION (horizontal tab) are removed from
// both composition string and data attribute of compositionupdate
// and compositionend events.
pref("dom.compositionevent.allow_control_characters", false);
pref("memory.report_concurrency", 10);
// Add Mozilla AudioChannel APIs.

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

@ -33,7 +33,7 @@ static const char* GetNotificationName(const IMENotification* aNotification) {
return ToChar(aNotification->mMessage);
}
class GetRectText : public nsAutoCString {
class MOZ_STACK_CLASS GetRectText : public nsAutoCString {
public:
explicit GetRectText(const LayoutDeviceIntRect& aRect) {
AssignLiteral("{ x=");
@ -49,7 +49,7 @@ class GetRectText : public nsAutoCString {
virtual ~GetRectText() = default;
};
class GetWritingModeName : public nsAutoCString {
class MOZ_STACK_CLASS GetWritingModeName : public nsAutoCString {
public:
explicit GetWritingModeName(const WritingMode& aWritingMode) {
if (!aWritingMode.IsVertical()) {
@ -65,7 +65,8 @@ class GetWritingModeName : public nsAutoCString {
virtual ~GetWritingModeName() = default;
};
class GetEscapedUTF8String final : public NS_ConvertUTF16toUTF8 {
class MOZ_STACK_CLASS GetEscapedUTF8String final
: public NS_ConvertUTF16toUTF8 {
public:
explicit GetEscapedUTF8String(const nsAString& aString)
: NS_ConvertUTF16toUTF8(aString) {
@ -98,20 +99,64 @@ LazyLogModule sContentCacheLog("ContentCacheWidgets");
* mozilla::ContentCacheInChild
*****************************************************************************/
ContentCacheInChild::ContentCacheInChild() : ContentCache() {}
void ContentCacheInChild::Clear() {
MOZ_LOG(sContentCacheLog, LogLevel::Info, ("0x%p Clear()", this));
mCompositionStart.reset();
mLastCommitStringStart.reset();
mLastCommitString.Truncate();
mText.Truncate();
mSelection.Clear();
mFirstCharRect.SetEmpty();
mCaret.Clear();
mTextRectArray.Clear();
mLastCommitStringTextRectArray.Clear();
mEditorRect.SetEmpty();
}
void ContentCacheInChild::OnCompositionEvent(
const WidgetCompositionEvent& aCompositionEvent) {
if (aCompositionEvent.CausesDOMCompositionEndEvent()) {
RefPtr<TextComposition> composition =
IMEStateManager::GetTextCompositionFor(aCompositionEvent.mWidget);
if (composition) {
if (aCompositionEvent.mMessage == eCompositionCommitAsIs) {
mLastCommitString = composition->CommitStringIfCommittedAsIs();
} else {
mLastCommitString = aCompositionEvent.mData;
}
// We don't need to store canceling information because this is required
// by undoing of last commit (Kakutei-Undo of Japanese IME).
if (!mLastCommitString.IsEmpty()) {
mLastCommitStringStart =
Some(composition->NativeOffsetOfStartComposition());
MOZ_LOG(
sContentCacheLog, LogLevel::Debug,
("0x%p OnCompositionEvent(), stored last composition string data "
"(aCompositionEvent={ mMessage=%s, mData=\"%s\"}, "
"mLastCommitStringStart=%u, mLastCommitString=\"%s\")",
this, ToChar(aCompositionEvent.mMessage),
GetEscapedUTF8String(aCompositionEvent.mData).get(),
mLastCommitStringStart.value(),
GetEscapedUTF8String(mLastCommitString).get()));
return;
}
}
}
if (mLastCommitStringStart.isSome()) {
MOZ_LOG(sContentCacheLog, LogLevel::Debug,
("0x%p OnCompositionEvent(), resetting the last composition string "
"data (aCompositionEvent={ mMessage=%s, mData=\"%s\"}, "
"mLastCommitStringStart=%u, mLastCommitString=\"%s\")",
this, ToChar(aCompositionEvent.mMessage),
GetEscapedUTF8String(aCompositionEvent.mData).get(),
mLastCommitStringStart.value(),
GetEscapedUTF8String(mLastCommitString).get()));
mLastCommitStringStart.reset();
mLastCommitString.Truncate();
}
}
bool ContentCacheInChild::CacheAll(nsIWidget* aWidget,
const IMENotification* aNotification) {
MOZ_LOG(sContentCacheLog, LogLevel::Info,
@ -242,6 +287,25 @@ bool ContentCacheInChild::CacheText(nsIWidget* aWidget,
sContentCacheLog, LogLevel::Info,
("0x%p CacheText(), Succeeded, mText.Length()=%u", this, mText.Length()));
// Forget last commit range if string in the range is different from the
// last commit string.
if (mLastCommitStringStart.isSome() &&
nsDependentSubstring(mText, mLastCommitStringStart.value(),
mLastCommitString.Length()) != mLastCommitString) {
MOZ_LOG(sContentCacheLog, LogLevel::Debug,
("0x%p CacheText(), resetting the last composition string data "
"(mLastCommitStringStart=%u, mLastCommitString=\"%s\", current "
"string=\"%s\")",
this, mLastCommitStringStart.value(),
GetEscapedUTF8String(mLastCommitString).get(),
GetEscapedUTF8String(
nsDependentSubstring(mText, mLastCommitStringStart.value(),
mLastCommitString.Length()))
.get()));
mLastCommitStringStart.reset();
mLastCommitString.Truncate();
}
return CacheSelection(aWidget, aNotification);
}
@ -293,6 +357,7 @@ bool ContentCacheInChild::CacheTextRects(nsIWidget* aWidget,
mCompositionStart.reset();
mTextRectArray.Clear();
mLastCommitStringTextRectArray.Clear();
mSelection.ClearAnchorCharRects();
mSelection.ClearFocusCharRects();
mSelection.mRect.SetEmpty();
@ -430,6 +495,29 @@ bool ContentCacheInChild::CacheTextRects(nsIWidget* aWidget,
}
}
if (mLastCommitStringStart.isSome()) {
mLastCommitStringTextRectArray.mStart = mLastCommitStringStart.value();
if (mLastCommitString.Length() == 1) {
MOZ_ASSERT(mSelection.Collapsed());
MOZ_ASSERT(mSelection.mAnchor - 1 == mLastCommitStringStart.value());
mLastCommitStringTextRectArray.mRects.AppendElement(
mSelection.mAnchorCharRects[ePrevCharRect]);
} else if (NS_WARN_IF(!QueryCharRectArray(
aWidget, mLastCommitStringTextRectArray.mStart,
mLastCommitString.Length(),
mLastCommitStringTextRectArray.mRects))) {
MOZ_LOG(sContentCacheLog, LogLevel::Error,
("0x%p CacheTextRects(), FAILED, "
"couldn't retrieve text rect array of the last commit string",
this));
mLastCommitStringTextRectArray.Clear();
mLastCommitStringStart.reset();
mLastCommitString.Truncate();
}
MOZ_ASSERT(mLastCommitStringTextRectArray.mRects.Length() ==
mLastCommitString.Length());
}
MOZ_LOG(
sContentCacheLog, LogLevel::Info,
("0x%p CacheTextRects(), Succeeded, "
@ -437,7 +525,8 @@ bool ContentCacheInChild::CacheTextRects(nsIWidget* aWidget,
" }, mSelection={ mAnchor=%u, mAnchorCharRects[eNextCharRect]=%s, "
"mAnchorCharRects[ePrevCharRect]=%s, mFocus=%u, "
"mFocusCharRects[eNextCharRect]=%s, mFocusCharRects[ePrevCharRect]=%s, "
"mRect=%s }, mFirstCharRect=%s",
"mRect=%s }, mFirstCharRect=%s, mLastCommitStringTextRectArray={ "
"mStart=%u, mRects.Length()=%zu }",
this, mText.Length(), mTextRectArray.mStart,
mTextRectArray.mRects.Length(), mSelection.mAnchor,
GetRectText(mSelection.mAnchorCharRects[eNextCharRect]).get(),
@ -445,7 +534,9 @@ bool ContentCacheInChild::CacheTextRects(nsIWidget* aWidget,
mSelection.mFocus,
GetRectText(mSelection.mFocusCharRects[eNextCharRect]).get(),
GetRectText(mSelection.mFocusCharRects[ePrevCharRect]).get(),
GetRectText(mSelection.mRect).get(), GetRectText(mFirstCharRect).get()));
GetRectText(mSelection.mRect).get(), GetRectText(mFirstCharRect).get(),
mLastCommitStringTextRectArray.mStart,
mLastCommitStringTextRectArray.mRects.Length()));
return true;
}
@ -468,6 +559,24 @@ void ContentCacheInChild::SetSelection(nsIWidget* aWidget,
}
mSelection.mWritingMode = aWritingMode;
if (mLastCommitStringStart.isSome()) {
// Forget last commit string range if selection is not collapsed
// at end of the last commit string.
if (!mSelection.Collapsed() ||
mSelection.mAnchor !=
mLastCommitStringStart.value() + mLastCommitString.Length()) {
MOZ_LOG(sContentCacheLog, LogLevel::Debug,
("0x%p SetSelection(), forgetting last commit composition data "
"(mSelection={ mAnchor=%u, mFocus=%u, Collapsed()=%s } "
"mLastCommitStringStart=%u, mLastCommitString={ Length()=%u }",
this, mSelection.mAnchor, mSelection.mFocus,
GetBoolName(mSelection.Collapsed()),
mLastCommitStringStart.value(), mLastCommitString.Length()));
mLastCommitStringStart.reset();
mLastCommitString.Truncate();
}
}
if (NS_WARN_IF(!CacheCaret(aWidget))) {
return;
}
@ -497,6 +606,7 @@ void ContentCacheInParent::AssignContent(const ContentCache& aOther,
mFirstCharRect = aOther.mFirstCharRect;
mCaret = aOther.mCaret;
mTextRectArray = aOther.mTextRectArray;
mLastCommitStringTextRectArray = aOther.mLastCommitStringTextRectArray;
mEditorRect = aOther.mEditorRect;
// Only when there is one composition, the TextComposition instance in this
@ -538,7 +648,8 @@ void ContentCacheInParent::AssignContent(const ContentCache& aOther,
"mFirstCharRect=%s, mCaret={ mOffset=%u, mRect=%s }, mTextRectArray={ "
"mStart=%u, mRects.Length()=%zu }, mWidgetHasComposition=%s, "
"mPendingCompositionCount=%u, mCompositionStart=%u, "
"mPendingCommitLength=%u, mEditorRect=%s",
"mPendingCommitLength=%u, mEditorRect=%s, "
"mLastCommitStringTextRectArray={ mStart=%u, mRects.Length()=%zu }",
this, GetNotificationName(aNotification), mText.Length(),
mSelection.mAnchor, mSelection.mFocus,
GetWritingModeName(mSelection.mWritingMode).get(),
@ -550,7 +661,9 @@ void ContentCacheInParent::AssignContent(const ContentCache& aOther,
mCaret.mOffset, GetRectText(mCaret.mRect).get(), mTextRectArray.mStart,
mTextRectArray.mRects.Length(), GetBoolName(mWidgetHasComposition),
mPendingCompositionCount, mCompositionStart.valueOr(UINT32_MAX),
mPendingCommitLength, GetRectText(mEditorRect).get()));
mPendingCommitLength, GetRectText(mEditorRect).get(),
mLastCommitStringTextRectArray.mStart,
mLastCommitStringTextRectArray.mRects.Length()));
}
bool ContentCacheInParent::HandleQueryContentEvent(
@ -858,10 +971,13 @@ bool ContentCacheInParent::GetTextRect(uint32_t aOffset,
("0x%p GetTextRect(aOffset=%u, "
"aRoundToExistingOffset=%s), "
"mTextRectArray={ mStart=%u, mRects.Length()=%zu }, "
"mSelection={ mAnchor=%u, mFocus=%u }",
"mSelection={ mAnchor=%u, mFocus=%u }, "
"mLastCommitStringTextRectArray={ mStart=%u, mRects.Length()=%zu }",
this, aOffset, GetBoolName(aRoundToExistingOffset),
mTextRectArray.mStart, mTextRectArray.mRects.Length(),
mSelection.mAnchor, mSelection.mFocus));
mSelection.mAnchor, mSelection.mFocus,
mLastCommitStringTextRectArray.mStart,
mLastCommitStringTextRectArray.mRects.Length()));
if (!aOffset) {
NS_WARNING_ASSERTION(!mFirstCharRect.IsEmpty(), "empty rect");
@ -893,24 +1009,39 @@ bool ContentCacheInParent::GetTextRect(uint32_t aOffset,
return !aTextRect.IsEmpty();
}
if (mTextRectArray.InRange(aOffset)) {
aTextRect = mTextRectArray.GetRect(aOffset);
return !aTextRect.IsEmpty();
}
if (mLastCommitStringTextRectArray.InRange(aOffset)) {
aTextRect = mLastCommitStringTextRectArray.GetRect(aOffset);
return !aTextRect.IsEmpty();
}
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();
}
// Although we may have mLastCommitStringTextRectArray here and it must have
// previous character rects at selection. However, we should stop using it
// because it's stored really short time after commiting a composition.
// So, multiple query may return different rect and it may cause flickerling
// the IME UI.
uint32_t offset = aOffset;
if (!mTextRectArray.InRange(aOffset)) {
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;
}
if (offset < mTextRectArray.StartOffset()) {
offset = mTextRectArray.StartOffset();
} else {
offset = mTextRectArray.EndOffset() - 1;
}
aTextRect = mTextRectArray.GetRect(offset);
return !aTextRect.IsEmpty();
@ -923,10 +1054,13 @@ bool ContentCacheInParent::GetUnionTextRects(
("0x%p GetUnionTextRects(aOffset=%u, "
"aLength=%u, aRoundToExistingOffset=%s), mTextRectArray={ "
"mStart=%u, mRects.Length()=%zu }, "
"mSelection={ mAnchor=%u, mFocus=%u }",
"mSelection={ mAnchor=%u, mFocus=%u }, "
"mLastCommitStringTextRectArray={ mStart=%u, mRects.Length()=%zu }",
this, aOffset, aLength, GetBoolName(aRoundToExistingOffset),
mTextRectArray.mStart, mTextRectArray.mRects.Length(),
mSelection.mAnchor, mSelection.mFocus));
mSelection.mAnchor, mSelection.mFocus,
mLastCommitStringTextRectArray.mStart,
mLastCommitStringTextRectArray.mRects.Length()));
CheckedInt<uint32_t> endOffset = CheckedInt<uint32_t>(aOffset) + aLength;
if (!endOffset.isValid()) {
@ -978,19 +1112,29 @@ bool ContentCacheInParent::GetUnionTextRects(
// in most cases.
if (!aOffset && aOffset != mSelection.mAnchor &&
aOffset != mSelection.mFocus && !mTextRectArray.InRange(aOffset)) {
aOffset != mSelection.mFocus && !mTextRectArray.InRange(aOffset) &&
!mLastCommitStringTextRectArray.InRange(aOffset)) {
// The first character rect isn't cached.
return false;
}
if ((aRoundToExistingOffset && mTextRectArray.HasRects()) ||
mTextRectArray.IsOverlappingWith(aOffset, aLength)) {
aUnionTextRect = mTextRectArray.GetUnionRectAsFarAsPossible(
// Use mLastCommitStringTextRectArray only when it overlaps with aOffset
// even if aROundToExistingOffset is true for avoiding flickerling IME UI.
// See the last comment in GetTextRect() for the detail.
if (mLastCommitStringTextRectArray.IsOverlappingWith(aOffset, aLength)) {
aUnionTextRect = mLastCommitStringTextRectArray.GetUnionRectAsFarAsPossible(
aOffset, aLength, aRoundToExistingOffset);
} else {
aUnionTextRect.SetEmpty();
}
if ((aRoundToExistingOffset && mTextRectArray.HasRects()) ||
mTextRectArray.IsOverlappingWith(aOffset, aLength)) {
aUnionTextRect =
aUnionTextRect.Union(mTextRectArray.GetUnionRectAsFarAsPossible(
aOffset, aLength, aRoundToExistingOffset));
}
if (!aOffset) {
aUnionTextRect = aUnionTextRect.Union(mFirstCharRect);
}

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

@ -215,7 +215,9 @@ class ContentCache {
LayoutDeviceIntRect GetUnionRect(uint32_t aOffset, uint32_t aLength) const;
LayoutDeviceIntRect GetUnionRectAsFarAsPossible(
uint32_t aOffset, uint32_t aLength, bool aRoundToExistingOffset) const;
} mTextRectArray;
};
TextRectArray mTextRectArray;
TextRectArray mLastCommitStringTextRectArray;
LayoutDeviceIntRect mEditorRect;
@ -225,7 +227,13 @@ class ContentCache {
class ContentCacheInChild final : public ContentCache {
public:
ContentCacheInChild();
ContentCacheInChild() = default;
/**
* Called when composition event will be dispatched in this process from
* PuppetWidget.
*/
void OnCompositionEvent(const WidgetCompositionEvent& aCompositionEvent);
/**
* When IME loses focus, this should be called and making this forget the
@ -264,6 +272,13 @@ class ContentCacheInChild final : public ContentCache {
const IMENotification* aNotification = nullptr);
bool CacheTextRects(nsIWidget* aWidget,
const IMENotification* aNotification = nullptr);
// Once composition is committed, all of the commit string may be composed
// again by Kakutei-Undo of Japanese IME. Therefore, we need to keep
// storing the last composition start to cache all character rects of the
// last commit string.
Maybe<uint32_t> mLastCommitStringStart;
nsString mLastCommitString;
};
class ContentCacheInParent final : public ContentCache {

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

@ -342,6 +342,7 @@ nsresult PuppetWidget::DispatchEvent(WidgetGUIEvent* aEvent,
}
#endif // #ifdef DEBUG
mNativeIMEContext = compositionEvent->mNativeIMEContext;
mContentCache.OnCompositionEvent(*compositionEvent);
}
// If the event is a composition event or a keyboard event, it should be

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

@ -1051,6 +1051,8 @@ struct ParamTraits<mozilla::ContentCache> {
WriteParam(aMsg, aParam.mCaret.mRect);
WriteParam(aMsg, aParam.mTextRectArray.mStart);
WriteParam(aMsg, aParam.mTextRectArray.mRects);
WriteParam(aMsg, aParam.mLastCommitStringTextRectArray.mStart);
WriteParam(aMsg, aParam.mLastCommitStringTextRectArray.mRects);
WriteParam(aMsg, aParam.mEditorRect);
}
@ -1071,6 +1073,10 @@ struct ParamTraits<mozilla::ContentCache> {
ReadParam(aMsg, aIter, &aResult->mCaret.mRect) &&
ReadParam(aMsg, aIter, &aResult->mTextRectArray.mStart) &&
ReadParam(aMsg, aIter, &aResult->mTextRectArray.mRects) &&
ReadParam(aMsg, aIter,
&aResult->mLastCommitStringTextRectArray.mStart) &&
ReadParam(aMsg, aIter,
&aResult->mLastCommitStringTextRectArray.mRects) &&
ReadParam(aMsg, aIter, &aResult->mEditorRect);
}
};