Bug 1275914 part.2 Modify TextComposition::mCompositionStartOffset after every composition event dispatch r=smaug

When composition string hasn't been non-empty, insertion point of the composition string can be changed by a DOM event handler.  E.g., compositionstart, first compositionupdate and first text.  Therefore, TextComposition should update the composition start offset cache after every event dispatch.

MozReview-Commit-ID: FOPewPTRuCn

--HG--
extra : rebase_source : 95fbba8130a1d21e957cee305b3b2a433bfae56a
This commit is contained in:
Masayuki Nakano 2016-06-10 19:32:49 +09:00
parent 7716923106
commit b44f10c1f3
2 changed files with 61 additions and 17 deletions

View File

@ -69,6 +69,7 @@ TextComposition::TextComposition(nsPresContext* aPresContext,
, mAllowControlCharacters(
Preferences::GetBool("dom.compositionevent.allow_control_characters",
false))
, mWasCompositionStringEmpty(true)
{
MOZ_ASSERT(aCompositionEvent->mNativeIMEContext.IsValid());
}
@ -152,6 +153,8 @@ TextComposition::DispatchEvent(WidgetCompositionEvent* aDispatchEvent,
EventDispatcher::Dispatch(mNode, mPresContext,
aDispatchEvent, nullptr, aStatus, aCallBack);
OnCompositionEventDispatched(aDispatchEvent);
}
void
@ -241,6 +244,8 @@ TextComposition::DispatchCompositionEvent(
EventDispatchingCallback* aCallBack,
bool aIsSynthesized)
{
mWasCompositionStringEmpty = mString.IsEmpty();
// If the content is a container of TabParent, composition should be in the
// remote process.
if (mTabParent) {
@ -402,7 +407,7 @@ TextComposition::DispatchCompositionEvent(
MOZ_ASSERT(!HasEditor(), "Why does the editor still keep to hold this?");
}
OnCompositionEventHandled(aCompositionEvent);
MaybeNotifyIMEOfCompositionEventHandled(aCompositionEvent);
}
// static
@ -430,32 +435,57 @@ TextComposition::HandleSelectionEvent(nsPresContext* aPresContext,
}
void
TextComposition::OnCompositionEventHandled(
TextComposition::OnCompositionEventDispatched(
const WidgetCompositionEvent* aCompositionEvent)
{
MOZ_RELEASE_ASSERT(!mTabParent);
nsEventStatus status;
if (!IsValidStateForComposition(aCompositionEvent->mWidget)) {
return;
}
// When compositon start, notify the rect of first offset character.
// When not compositon start, notify the rect of selected composition
// string if compositionchange event.
if (aCompositionEvent->mMessage == eCompositionStart) {
// Every composition event may cause changing composition start offset,
// especially when there is no composition string. Therefore, we need to
// update mCompositionStartOffset with the latest offset.
MOZ_ASSERT(aCompositionEvent->mMessage != eCompositionStart ||
mWasCompositionStringEmpty,
"mWasCompositionStringEmpty should be true if the dispatched "
"event is eCompositionStart");
if (mWasCompositionStringEmpty &&
!aCompositionEvent->CausesDOMCompositionEndEvent()) {
// If there was no composition string, current selection start may be the
// offset for inserting composition string.
nsCOMPtr<nsIWidget> widget = mPresContext->GetRootWidget();
// Update composition start offset
WidgetQueryContentEvent selectedTextEvent(true, eQuerySelectedText, widget);
widget->DispatchEvent(&selectedTextEvent, status);
if (selectedTextEvent.mSucceeded) {
mCompositionStartOffset = selectedTextEvent.mReply.mOffset;
nsEventStatus status = nsEventStatus_eIgnore;
if (mString.IsEmpty()) {
widget->DispatchEvent(&selectedTextEvent, status);
} else {
// Unknown offset
NS_WARNING("Cannot get start offset of IME composition");
MOZ_ASSERT(aCompositionEvent->mMessage == eCompositionChange);
// TODO: Update mCompositionStartOffset with first IME selection
}
if (NS_WARN_IF(!selectedTextEvent.mSucceeded)) {
// XXX Is this okay?
mCompositionStartOffset = 0;
} else {
mCompositionStartOffset = selectedTextEvent.mReply.mOffset;
}
mTargetClauseOffsetInComposition = 0;
} else if (aCompositionEvent->CausesDOMTextEvent()) {
}
if (aCompositionEvent->CausesDOMTextEvent()) {
mTargetClauseOffsetInComposition = aCompositionEvent->TargetClauseOffset();
} else {
}
}
void
TextComposition::MaybeNotifyIMEOfCompositionEventHandled(
const WidgetCompositionEvent* aCompositionEvent)
{
if (aCompositionEvent->mMessage != eCompositionStart &&
!aCompositionEvent->CausesDOMTextEvent()) {
return;
}

View File

@ -258,6 +258,10 @@ private:
// and compositionend events.
bool mAllowControlCharacters;
// mWasCompositionStringEmpty is true if the composition string was empty
// when DispatchCompositionEvent() is called.
bool mWasCompositionStringEmpty;
// Hide the default constructor and copy constructor.
TextComposition()
: mPresContext(nullptr)
@ -272,6 +276,7 @@ private:
, mRequestedToCommitOrCancel(false)
, mWasNativeCompositionEndEventDiscarded(false)
, mAllowControlCharacters(false)
, mWasCompositionStringEmpty(true)
{}
TextComposition(const TextComposition& aOther);
@ -375,9 +380,18 @@ private:
void OnCompositionEventDiscarded(WidgetCompositionEvent* aCompositionEvent);
/**
* Calculate composition offset then notify composition update to widget
* OnCompositionEventDispatched() is called after a composition event is
* dispatched.
*/
void OnCompositionEventHandled(
void OnCompositionEventDispatched(
const WidgetCompositionEvent* aDispatchEvent);
/**
* MaybeNotifyIMEOfCompositionEventHandled() notifies IME of composition
* event handled. This should be called after dispatching a composition
* event which came from widget.
*/
void MaybeNotifyIMEOfCompositionEventHandled(
const WidgetCompositionEvent* aCompositionEvent);
/**