mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 10:00:54 +00:00
Bug 811755 - Buffer and coalesce IME selection and text changes; r=cpeterson
This commit is contained in:
parent
72f1716ec4
commit
3f7bd3e61f
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user