Bug 811755 - Buffer and coalesce IME selection and text changes; r=cpeterson

This commit is contained in:
Jim Chen 2012-12-05 11:09:56 -05:00
parent 29d213ac6c
commit 0a92b8105d
4 changed files with 162 additions and 17 deletions

View File

@ -665,6 +665,13 @@ AndroidGeckoEvent::Init(int aType)
mType = aType;
}
void
AndroidGeckoEvent::Init(int aType, int aAction)
{
mType = aType;
mAction = aAction;
}
void
AndroidGeckoEvent::Init(int x1, int y1, int x2, int y2)
{

View File

@ -633,6 +633,9 @@ public:
AndroidGeckoEvent(int aType) {
Init(aType);
}
AndroidGeckoEvent(int aType, int aAction) {
Init(aType, aAction);
}
AndroidGeckoEvent(int x1, int y1, int x2, int y2) {
Init(x1, y1, x2, y2);
}
@ -648,6 +651,7 @@ public:
void Init(JNIEnv *jenv, jobject jobj);
void Init(int aType);
void Init(int aType, int aAction);
void Init(int x1, int y1, int x2, int y2);
void Init(int aType, const nsIntRect &aRect);
void Init(AndroidGeckoEvent *aResizeEvent);
@ -816,7 +820,8 @@ public:
IME_ADD_COMPOSITION_RANGE = 3,
IME_UPDATE_COMPOSITION = 4,
IME_REMOVE_COMPOSITION = 5,
IME_ACKNOWLEDGE_FOCUS = 6
IME_ACKNOWLEDGE_FOCUS = 6,
IME_FLUSH_CHANGES = 7
};
};

View File

@ -1763,8 +1763,14 @@ nsWindow::OnIMEEvent(AndroidGeckoEvent *ae)
return;
}
switch (ae->Action()) {
case AndroidGeckoEvent::IME_FLUSH_CHANGES:
{
FlushIMEChanges();
}
break;
case AndroidGeckoEvent::IME_SYNCHRONIZE:
{
FlushIMEChanges();
AndroidBridge::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT, 0);
}
break;
@ -1807,6 +1813,7 @@ nsWindow::OnIMEEvent(AndroidGeckoEvent *ae)
event.data = ae->Characters();
DispatchEvent(&event);
}
FlushIMEChanges();
AndroidBridge::NotifyIME(AndroidBridge::NOTIFY_IME_REPLY_EVENT, 0);
}
break;
@ -2046,13 +2053,18 @@ nsWindow::OnIMEFocusChange(bool aFocus)
ALOGIME("IME: OnIMEFocusChange: f=%d", aFocus);
if (aFocus) {
mIMETextChanges.Clear();
mIMESelectionChange = IMEChange();
// OnIMETextChange also notifies selection
OnIMETextChange(0, INT32_MAX, INT32_MAX);
OnIMESelectionChange();
FlushIMEChanges();
} else {
// Mask events because we lost focus. On the next focus event, Gecko will notify
// Java, and Java will send an acknowledge focus event back to Gecko. That is
// where we unmask event handling
mIMEMaskEventsCount++;
mIMEComposing = false;
mIMEComposingText.Truncate();
}
AndroidBridge::NotifyIME(AndroidBridge::NOTIFY_IME_FOCUSCHANGE,
@ -2061,6 +2073,51 @@ nsWindow::OnIMEFocusChange(bool aFocus)
return NS_OK;
}
void
nsWindow::PostFlushIMEChanges()
{
if (!mIMETextChanges.IsEmpty() || !mIMESelectionChange.IsEmpty()) {
// Already posted
return;
}
AndroidGeckoEvent *event = new AndroidGeckoEvent(
AndroidGeckoEvent::IME_EVENT, AndroidGeckoEvent::IME_FLUSH_CHANGES);
nsAppShell::gAppShell->PostEvent(event);
}
void
nsWindow::FlushIMEChanges()
{
nsRefPtr<nsWindow> kungFuDeathGrip(this);
for (uint32_t i = 0; i < mIMETextChanges.Length(); i++) {
IMEChange &change = mIMETextChanges[i];
MOZ_ASSERT(change.IsTextChange());
nsQueryContentEvent event(true, NS_QUERY_TEXT_CONTENT, this);
InitEvent(event, nullptr);
event.InitForQueryTextContent(change.mStart,
change.mNewEnd - change.mStart);
DispatchEvent(&event);
if (!event.mSucceeded)
return;
AndroidBridge::NotifyIMEChange(event.mReply.mString.get(),
event.mReply.mString.Length(),
change.mStart,
change.mOldEnd,
change.mNewEnd);
}
mIMETextChanges.Clear();
if (!mIMESelectionChange.IsEmpty()) {
MOZ_ASSERT(!mIMESelectionChange.IsTextChange());
AndroidBridge::NotifyIMEChange(nullptr, 0,
mIMESelectionChange.mStart,
mIMESelectionChange.mOldEnd, -1);
mIMESelectionChange = IMEChange();
}
}
NS_IMETHODIMP
nsWindow::OnIMETextChange(uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd)
{
@ -2070,21 +2127,67 @@ nsWindow::OnIMETextChange(uint32_t aStart, uint32_t aOldEnd, uint32_t aNewEnd)
ALOGIME("IME: OnIMETextChange: s=%d, oe=%d, ne=%d",
aStart, aOldEnd, aNewEnd);
nsRefPtr<nsWindow> kungFuDeathGrip(this);
nsQueryContentEvent event(true, NS_QUERY_TEXT_CONTENT, this);
InitEvent(event, nullptr);
event.InitForQueryTextContent(aStart, aNewEnd - aStart);
DispatchEvent(&event);
if (!event.mSucceeded)
return NS_OK;
AndroidBridge::NotifyIMEChange(event.mReply.mString.get(),
event.mReply.mString.Length(),
aStart, aOldEnd, aNewEnd);
/* Make sure Java's selection is up-to-date */
mIMESelectionChange = IMEChange();
OnIMESelectionChange();
PostFlushIMEChanges();
mIMETextChanges.AppendElement(IMEChange(aStart, aOldEnd, aNewEnd));
// Now that we added a new range we need to go back and
// update all the ranges before that.
// Ranges that have offsets which follow this new range
// need to be updated to reflect new offsets
int32_t delta = (int32_t)(aNewEnd - aOldEnd);
for (int32_t i = mIMETextChanges.Length() - 2; i >= 0; i--) {
IMEChange &previousChange = mIMETextChanges[i];
if (previousChange.mStart > (int32_t)aOldEnd) {
previousChange.mStart += delta;
previousChange.mOldEnd += delta;
previousChange.mNewEnd += delta;
}
}
// Now go through all ranges to merge any ranges that are connected
// srcIndex is the index of the range to merge from
// dstIndex is the index of the range to potentially merge into
int32_t srcIndex = mIMETextChanges.Length() - 1;
int32_t dstIndex = srcIndex;
while (--dstIndex >= 0) {
IMEChange &src = mIMETextChanges[srcIndex];
IMEChange &dst = mIMETextChanges[dstIndex];
// When merging a more recent change into an older
// change, we need to compare recent change's (start, oldEnd)
// range to the older change's (start, newEnd)
if (src.mOldEnd < dst.mStart || dst.mNewEnd < src.mStart) {
// No overlap between ranges
continue;
}
// When merging two ranges, there are generally four posibilities:
// [----(----]----), (----[----]----),
// [----(----)----], (----[----)----]
// where [----] is the first range and (----) is the second range
// As seen above, the start of the merged range is always the lesser
// of the two start offsets. OldEnd and NewEnd then need to be
// adjusted separately depending on the case. In any case, the change
// in text length of the merged range should be the sum of text length
// changes of the two original ranges, i.e.,
// newNewEnd - newOldEnd == newEnd1 - oldEnd1 + newEnd2 - oldEnd2
dst.mStart = std::min(dst.mStart, src.mStart);
if (src.mOldEnd < dst.mNewEnd) {
// New range overlaps or is within previous range; merge
dst.mNewEnd += src.mNewEnd - src.mOldEnd;
} else { // src.mOldEnd >= dst.mNewEnd
// New range overlaps previous range; merge
dst.mOldEnd += src.mOldEnd - dst.mNewEnd;
dst.mNewEnd = src.mNewEnd;
}
// src merged to dst; delete src.
mIMETextChanges.RemoveElementAt(srcIndex);
// Any ranges that we skip over between src and dst are not mergeable
// so we can safely continue the merge starting at dst
srcIndex = dstIndex;
}
return NS_OK;
}
@ -2104,8 +2207,9 @@ nsWindow::OnIMESelectionChange(void)
if (!event.mSucceeded)
return NS_OK;
AndroidBridge::NotifyIMEChange(nullptr, 0, int(event.GetSelectionStart()),
int(event.GetSelectionEnd()), -1);
PostFlushIMEChanges();
mIMESelectionChange = IMEChange((int32_t)event.GetSelectionStart(),
(int32_t)event.GetSelectionEnd());
return NS_OK;
}

View File

@ -167,6 +167,8 @@ protected:
bool DrawTo(gfxASurface *targetSurface, const nsIntRect &aRect);
bool IsTopLevel();
void RemoveIMEComposition();
void PostFlushIMEChanges();
void FlushIMEChanges();
// Call this function when the users activity is the direct cause of an
// event (like a keypress or mouse click).
@ -188,6 +190,33 @@ protected:
nsString mIMEComposingText;
nsAutoTArray<nsTextRange, 4> mIMERanges;
struct IMEChange {
int32_t mStart, mOldEnd, mNewEnd;
IMEChange() :
mStart(-1), mOldEnd(-1), mNewEnd(-1)
{
}
IMEChange(int32_t start, int32_t oldEnd, int32_t newEnd) :
mStart(start), mOldEnd(oldEnd), mNewEnd(newEnd)
{
}
IMEChange(int32_t start, int32_t end) :
mStart(start), mOldEnd(end), mNewEnd(-1)
{
}
bool IsEmpty()
{
return mStart < 0;
}
bool IsTextChange()
{
return mNewEnd >= 0;
}
};
nsAutoTArray<IMEChange, 4> mIMETextChanges;
IMEChange mIMESelectionChange;
InputContext mInputContext;
static void DumpWindows();