author | Masayuki Nakano <masayuki@d-toybox.com> |
Tue, 25 Nov 2014 14:02:31 +0900 | |
changeset 241631 | 4783e19c5feb9e3cd0d4ed43d07e0d0866e6babf |
parent 241630 | 151c7c4b14c6f3b1b09cba293453e40a81376dde |
child 241632 | d2ca3f9615ac97aa74ebd3b6f7046b6fbb3300ec |
push id | 4311 |
push user | raliiev@mozilla.com |
push date | Mon, 12 Jan 2015 19:37:41 +0000 |
treeherder | mozilla-beta@150c9fed433b [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | smaug |
bugs | 1077345 |
milestone | 36.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -2133,23 +2133,25 @@ nsDOMWindowUtils::SendCompositionEvent(c // Now we don't support manually dispatching composition update with this // API. A compositionupdate is dispatched when a DOM text event modifies // composition string automatically. For backward compatibility, this // shouldn't return error in this case. NS_WARNING("Don't call nsIDOMWindowUtils.sendCompositionEvent() for " "compositionupdate since it's ignored and the event is " "fired automatically when it's necessary"); return NS_OK; + } else if (aType.EqualsLiteral("compositioncommitasis")) { + msg = NS_COMPOSITION_COMMIT_AS_IS; } else { return NS_ERROR_FAILURE; } WidgetCompositionEvent compositionEvent(true, msg, widget); InitEvent(compositionEvent); - if (msg != NS_COMPOSITION_START) { + if (msg != NS_COMPOSITION_START && msg != NS_COMPOSITION_COMMIT_AS_IS) { compositionEvent.mData = aData; } compositionEvent.mFlags.mIsSynthesizedForTests = true; nsEventStatus status; nsresult rv = widget->DispatchEvent(&compositionEvent, status); NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -814,16 +814,17 @@ EventStateManager::PreHandleEvent(nsPres compositionEvent->widget); DoQuerySelectedText(&selectedText); NS_ASSERTION(selectedText.mSucceeded, "Failed to get selected text"); compositionEvent->mData = selectedText.mReply.mString; } // through to compositionend handling case NS_COMPOSITION_END: case NS_COMPOSITION_CHANGE: + case NS_COMPOSITION_COMMIT_AS_IS: { WidgetCompositionEvent* compositionEvent = aEvent->AsCompositionEvent(); if (IsTargetCrossProcess(compositionEvent)) { // Will not be handled locally, remote the event if (GetCrossProcessTarget()->SendCompositionEvent(*compositionEvent)) { // Cancel local dispatching aEvent->mFlags.mPropagationStopped = true; }
--- a/dom/events/IMEStateManager.cpp +++ b/dom/events/IMEStateManager.cpp @@ -138,16 +138,18 @@ GetEventMessageName(uint32_t aMessage) case NS_COMPOSITION_START: return "NS_COMPOSITION_START"; case NS_COMPOSITION_END: return "NS_COMPOSITION_END"; case NS_COMPOSITION_UPDATE: return "NS_COMPOSITION_UPDATE"; case NS_COMPOSITION_CHANGE: return "NS_COMPOSITION_CHANGE"; + case NS_COMPOSITION_COMMIT_AS_IS: + return "NS_COMPOSITION_COMMIT_AS_IS"; default: return "unacceptable event message"; } } static const char* GetNotifyIMEMessageName(IMEMessage aMessage) {
--- a/dom/events/TextComposition.cpp +++ b/dom/events/TextComposition.cpp @@ -79,38 +79,42 @@ TextComposition::MaybeDispatchCompositio if (mLastData == aCompositionEvent->mData) { return true; } CloneAndDispatchAs(aCompositionEvent, NS_COMPOSITION_UPDATE); return IsValidStateForComposition(aCompositionEvent->widget); } -void +BaseEventFlags TextComposition::CloneAndDispatchAs( const WidgetCompositionEvent* aCompositionEvent, - uint32_t aMessage) + uint32_t aMessage, + nsEventStatus* aStatus, + EventDispatchingCallback* aCallBack) { MOZ_ASSERT(IsValidStateForComposition(aCompositionEvent->widget), "Should be called only when it's safe to dispatch an event"); WidgetCompositionEvent compositionEvent(aCompositionEvent->mFlags.mIsTrusted, aMessage, aCompositionEvent->widget); compositionEvent.time = aCompositionEvent->time; compositionEvent.timeStamp = aCompositionEvent->timeStamp; compositionEvent.mData = aCompositionEvent->mData; compositionEvent.mFlags.mIsSynthesizedForTests = aCompositionEvent->mFlags.mIsSynthesizedForTests; - nsEventStatus status = nsEventStatus_eConsumeNoDefault; + nsEventStatus dummyStatus = nsEventStatus_eConsumeNoDefault; + nsEventStatus* status = aStatus ? aStatus : &dummyStatus; if (aMessage == NS_COMPOSITION_UPDATE) { mLastData = compositionEvent.mData; } EventDispatcher::Dispatch(mNode, mPresContext, - &compositionEvent, nullptr, &status, nullptr); + &compositionEvent, nullptr, status, aCallBack); + return compositionEvent.mFlags; } void TextComposition::OnCompositionEventDiscarded( const WidgetCompositionEvent* aCompositionEvent) { // Note that this method is never called for synthesized events for emulating // commit or cancel composition. @@ -131,16 +135,32 @@ TextComposition::OnCompositionEventDisca void TextComposition::DispatchCompositionEvent( WidgetCompositionEvent* aCompositionEvent, nsEventStatus* aStatus, EventDispatchingCallback* aCallBack, bool aIsSynthesized) { + if (aCompositionEvent->message == NS_COMPOSITION_COMMIT_AS_IS) { + NS_ASSERTION(!aCompositionEvent->mRanges, + "mRanges of NS_COMPOSITION_COMMIT_AS_IS should be null"); + aCompositionEvent->mRanges = nullptr; + NS_ASSERTION(aCompositionEvent->mData.IsEmpty(), + "mData of NS_COMPOSITION_COMMIT_AS_IS should be empty string"); + if (mLastData == IDEOGRAPHIC_SPACE) { + // If the last data is an ideographic space (FullWidth space), it must be + // a placeholder character of some Chinese IME. So, committing with + // this data must not be expected by users. Let's use empty string. + aCompositionEvent->mData.Truncate(); + } else { + aCompositionEvent->mData = mLastData; + } + } + if (!IsValidStateForComposition(aCompositionEvent->widget)) { *aStatus = nsEventStatus_eConsumeNoDefault; return; } // If this instance has requested to commit or cancel composition but // is not synthesizing commit event, that means that the IME commits or // cancels the composition asynchronously. Typically, iBus behaves so. @@ -163,16 +183,17 @@ TextComposition::DispatchCompositionEven // 1. committing string is empty string at requesting commit but the last // data isn't IDEOGRAPHIC SPACE. // 2. non-empty string is committed at requesting cancel. if (!aIsSynthesized && (mIsRequestingCommit || mIsRequestingCancel)) { nsString* committingData = nullptr; switch (aCompositionEvent->message) { case NS_COMPOSITION_END: case NS_COMPOSITION_CHANGE: + case NS_COMPOSITION_COMMIT_AS_IS: committingData = &aCompositionEvent->mData; break; default: NS_WARNING("Unexpected event comes during committing or " "canceling composition"); break; } if (committingData) { @@ -180,42 +201,72 @@ TextComposition::DispatchCompositionEven mLastData != IDEOGRAPHIC_SPACE) { committingData->Assign(mLastData); } else if (mIsRequestingCancel && !committingData->IsEmpty()) { committingData->Truncate(); } } } - if (aCompositionEvent->CausesDOMTextEvent()) { + bool dispatchEvent = true; + bool dispatchDOMTextEvent = aCompositionEvent->CausesDOMTextEvent(); + + // When mIsComposing is false but the committing string is different from + // the last data (E.g., previous NS_COMPOSITION_CHANGE event made the + // composition string empty or didn't have clause information), we don't + // need to dispatch redundant DOM text event. + if (dispatchDOMTextEvent && + aCompositionEvent->message != NS_COMPOSITION_CHANGE && + !mIsComposing && mLastData == aCompositionEvent->mData) { + dispatchEvent = dispatchDOMTextEvent = false; + } + + if (dispatchDOMTextEvent) { if (!MaybeDispatchCompositionUpdate(aCompositionEvent)) { return; } } - EventDispatcher::Dispatch(mNode, mPresContext, - aCompositionEvent, nullptr, aStatus, aCallBack); + if (dispatchEvent) { + // If the composition event should cause a DOM text event, we should + // overwrite the event message as NS_COMPOSITION_CHANGE because due to + // the limitation of mapping between event messages and DOM event types, + // we cannot map multiple event messages to a DOM event type. + if (dispatchDOMTextEvent && + aCompositionEvent->message != NS_COMPOSITION_CHANGE) { + aCompositionEvent->mFlags = + CloneAndDispatchAs(aCompositionEvent, NS_COMPOSITION_CHANGE, + aStatus, aCallBack); + } else { + EventDispatcher::Dispatch(mNode, mPresContext, + aCompositionEvent, nullptr, aStatus, aCallBack); + } + } else { + *aStatus = nsEventStatus_eConsumeNoDefault; + } if (!IsValidStateForComposition(aCompositionEvent->widget)) { return; } // Emulate editor behavior of compositionchange event (DOM text event) handler // if no editor handles composition events. - if (aCompositionEvent->CausesDOMTextEvent() && !HasEditor()) { + if (dispatchDOMTextEvent && !HasEditor()) { EditorWillHandleCompositionChangeEvent(aCompositionEvent); EditorDidHandleCompositionChangeEvent(); } -#ifdef DEBUG - else if (aCompositionEvent->CausesDOMCompositionEndEvent()) { + if (aCompositionEvent->CausesDOMCompositionEndEvent()) { + // Dispatch a compositionend event if it's necessary. + if (aCompositionEvent->message != NS_COMPOSITION_END) { + CloneAndDispatchAs(aCompositionEvent, NS_COMPOSITION_END); + } MOZ_ASSERT(!mIsComposing, "Why is the editor still composing?"); MOZ_ASSERT(!HasEditor(), "Why does the editor still keep to hold this?"); } -#endif // #ifdef DEBUG // Notify composition update to widget if possible NotityUpdateComposition(aCompositionEvent); } void TextComposition::NotityUpdateComposition( const WidgetCompositionEvent* aCompositionEvent) @@ -298,52 +349,62 @@ TextComposition::RequestToCommit(nsIWidg return rv; } } else { // Emulates to commit or cancel the composition // FYI: These events may be discarded by PresShell if it's not safe to // dispatch the event. nsCOMPtr<nsIWidget> widget(aWidget); nsAutoString commitData(aDiscard ? EmptyString() : lastData); - bool changingData = lastData != commitData; + if (commitData == mLastData) { + WidgetCompositionEvent commitEvent(true, NS_COMPOSITION_COMMIT_AS_IS, + widget); + commitEvent.mFlags.mIsSynthesizedForTests = true; + nsEventStatus status = nsEventStatus_eIgnore; + widget->DispatchEvent(&commitEvent, status); + } else { + WidgetCompositionEvent changeEvent(true, NS_COMPOSITION_CHANGE, widget); + changeEvent.mData = commitData; + changeEvent.mFlags.mIsSynthesizedForTests = true; - WidgetCompositionEvent changeEvent(true, NS_COMPOSITION_CHANGE, widget); - changeEvent.mData = commitData; - changeEvent.mFlags.mIsSynthesizedForTests = true; - - MaybeDispatchCompositionUpdate(&changeEvent); + MaybeDispatchCompositionUpdate(&changeEvent); - // If changing the data or committing string isn't empty, we need to - // dispatch compositionchange event for setting the composition string - // without IME selection. - if (IsValidStateForComposition(widget) && - (changingData || !commitData.IsEmpty())) { - nsEventStatus status = nsEventStatus_eIgnore; - widget->DispatchEvent(&changeEvent, status); - } + // If changing the data or committing string isn't empty, we need to + // dispatch compositionchange event for setting the composition string + // without IME selection. + if (IsValidStateForComposition(widget)) { + nsEventStatus status = nsEventStatus_eIgnore; + widget->DispatchEvent(&changeEvent, status); + } - if (IsValidStateForComposition(widget)) { - nsEventStatus status = nsEventStatus_eIgnore; - WidgetCompositionEvent endEvent(true, NS_COMPOSITION_END, widget); - endEvent.mData = commitData; - endEvent.mFlags.mIsSynthesizedForTests = true; - widget->DispatchEvent(&endEvent, status); + if (IsValidStateForComposition(widget)) { + nsEventStatus status = nsEventStatus_eIgnore; + WidgetCompositionEvent endEvent(true, NS_COMPOSITION_END, widget); + endEvent.mData = commitData; + endEvent.mFlags.mIsSynthesizedForTests = true; + widget->DispatchEvent(&endEvent, status); + } } } } mRequestedToCommitOrCancel = true; // If the request is performed synchronously, this must be already destroyed. if (Destroyed()) { return NS_OK; } // Otherwise, synthesize the commit in content. nsAutoString data(aDiscard ? EmptyString() : lastData); + if (data == mLastData) { + DispatchCompositionEventRunnable(NS_COMPOSITION_COMMIT_AS_IS, EmptyString(), + true); + return NS_OK; + } // If the last composition string and new data are different, we need to // dispatch compositionchange event for removing IME selection. However, if // the commit string is empty string and it's not changed from the last data, // we don't need to dispatch compositionchange event. if (lastData != data || !data.IsEmpty()) { DispatchCompositionEventRunnable(NS_COMPOSITION_CHANGE, data, true); } DispatchCompositionEventRunnable(NS_COMPOSITION_END, data, true); @@ -454,19 +515,22 @@ TextComposition::CompositionEventDispatc compStart.mFlags.mIsSynthesizedForTests = mTextComposition->IsSynthesizedForTests(); IMEStateManager::DispatchCompositionEvent(mEventTarget, presContext, &compStart, &status, nullptr, mIsSynthesizedEvent); break; } case NS_COMPOSITION_END: - case NS_COMPOSITION_CHANGE: { + case NS_COMPOSITION_CHANGE: + case NS_COMPOSITION_COMMIT_AS_IS: { WidgetCompositionEvent compEvent(true, mEventMessage, widget); - compEvent.mData = mData; + if (mEventMessage != NS_COMPOSITION_COMMIT_AS_IS) { + compEvent.mData = mData; + } compEvent.mFlags.mIsSynthesizedForTests = mTextComposition->IsSynthesizedForTests(); IMEStateManager::DispatchCompositionEvent(mEventTarget, presContext, &compEvent, &status, nullptr, mIsSynthesizedEvent); break; } default:
--- a/dom/events/TextComposition.h +++ b/dom/events/TextComposition.h @@ -273,20 +273,24 @@ private: * destroying this composition. */ bool MaybeDispatchCompositionUpdate( const WidgetCompositionEvent* aCompositionEvent); /** * CloneAndDispatchAs() dispatches a composition event which is * duplicateed from aCompositionEvent and set the aMessage. + * + * @return Returns BaseEventFlags which is the result of dispatched event. */ - void CloneAndDispatchAs( - const WidgetCompositionEvent* aCompositionEvent, - uint32_t aMessage); + BaseEventFlags CloneAndDispatchAs( + const WidgetCompositionEvent* aCompositionEvent, + uint32_t aMessage, + nsEventStatus* aStatus = nullptr, + EventDispatchingCallback* aCallBack = nullptr); /** * If IME has already dispatched compositionend event but it was discarded * by PresShell due to not safe to dispatch, this returns true. */ bool WasNativeCompositionEndEventDiscarded() const { return mWasNativeCompositionEndEventDiscarded;
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -1032,20 +1032,22 @@ interface nsIDOMWindowUtils : nsISupport /** * Synthesize a composition event to the window. * * Cannot be accessed from unprivileged context (not content-accessible) * Will throw a DOM security error if called without chrome privileges. * * @param aType The event type: "compositionstart", "compositionend" or - * "compositionupdate". + * "compositioncommitasis". * @param aData The data property value. Note that this isn't applied * for compositionstart event because its value is the - * selected text which is automatically computed. + * selected text which is automatically computed. And also + * this isn't applied for compositioncommitasis because + * the last data will be set automatically. * @param aLocale The locale property value. */ void sendCompositionEvent(in AString aType, in AString aData, in AString aLocale); /** * Creating synthesizer of composition string on the window.
--- a/editor/libeditor/nsEditor.cpp +++ b/editor/libeditor/nsEditor.cpp @@ -4910,16 +4910,17 @@ nsEditor::IsAcceptableInputEvent(nsIDOME case NS_USER_DEFINED_EVENT: // If events are not created with proper event interface, their message // are initialized with NS_USER_DEFINED_EVENT. Let's ignore such event. return false; case NS_COMPOSITION_START: case NS_COMPOSITION_END: case NS_COMPOSITION_UPDATE: case NS_COMPOSITION_CHANGE: + case NS_COMPOSITION_COMMIT_AS_IS: // Don't allow composition events whose internal event are not // WidgetCompositionEvent. widgetGUIEvent = aEvent->GetInternalNSEvent()->AsCompositionEvent(); needsWidget = true; break; default: break; }
--- a/testing/mochitest/tests/SimpleTest/EventUtils.js +++ b/testing/mochitest/tests/SimpleTest/EventUtils.js @@ -866,22 +866,23 @@ const COMPOSITION_ATTR_SELECTEDRAWTEXT const COMPOSITION_ATTR_CONVERTEDTEXT = 0x04; const COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT = 0x05; /** * Synthesize a composition event. * * @param aEvent The composition event information. This must * have |type| member. The value must be - * "compositionstart" or "compositionend". + * "compositionstart", "compositionend" or + * "compositioncommitasis". * And also this may have |data| and |locale| which * would be used for the value of each property of - * the composition event. Note that the data would - * be ignored if the event type were - * "compositionstart". + * the composition event. Note that the |data| is + * ignored if the event type is "compositionstart" + * or "compositioncommitasis". * @param aWindow Optional (If null, current |window| will be used) */ function synthesizeComposition(aEvent, aWindow) { var utils = _getDOMWindowUtils(aWindow); if (!utils) { return; }
--- a/widget/BasicEvents.h +++ b/widget/BasicEvents.h @@ -171,27 +171,37 @@ #define NS_MUTATION_CHARACTERDATAMODIFIED (NS_MUTATION_START+6) #define NS_MUTATION_END (NS_MUTATION_START+6) #define NS_USER_DEFINED_EVENT 2000 // composition events #define NS_COMPOSITION_EVENT_START 2200 #define NS_COMPOSITION_START (NS_COMPOSITION_EVENT_START) +// NS_COMPOSITION_END is the message for DOM compositionend event. +// This event should NOT be dispatched from widget if NS_COMPOSITION_COMMIT +// is available. #define NS_COMPOSITION_END (NS_COMPOSITION_EVENT_START + 1) // NS_COMPOSITION_UPDATE is the message for DOM compositionupdate event. // This event should NOT be dispatched from widget since it will be dispatched // by mozilla::TextComposition automatically if NS_COMPOSITION_CHANGE event // will change composition string. #define NS_COMPOSITION_UPDATE (NS_COMPOSITION_EVENT_START + 2) // NS_COMPOSITION_CHANGE is the message for representing a change of // composition string. This should be dispatched from widget even if // composition string isn't changed but the ranges are changed. This causes // a DOM "text" event which is a non-standard DOM event. #define NS_COMPOSITION_CHANGE (NS_COMPOSITION_EVENT_START + 3) +// NS_COMPOSITION_COMMIT_AS_IS is the message for representing a commit of +// composition string. TextComposition will commit composition with the +// last data. TextComposition will dispatch this event to the DOM tree as +// NS_COMPOSITION_CHANGE without clause information. After that, +// NS_COMPOSITION_END will be dispatched automatically. +// Its mData and mRanges should be empty and nullptr. +#define NS_COMPOSITION_COMMIT_AS_IS (NS_COMPOSITION_EVENT_START + 4) // UI events #define NS_UI_EVENT_START 2500 // this is not to be confused with NS_ACTIVATE! #define NS_UI_ACTIVATE (NS_UI_EVENT_START) #define NS_UI_FOCUSIN (NS_UI_EVENT_START + 1) #define NS_UI_FOCUSOUT (NS_UI_EVENT_START + 2)
--- a/widget/EventForwards.h +++ b/widget/EventForwards.h @@ -97,16 +97,17 @@ namespace mozilla { #define NS_ROOT_EVENT_CLASS(aPrefix, aName) NS_EVENT_CLASS(aPrefix, aName) #include "mozilla/EventClassList.h" #undef NS_EVENT_CLASS #undef NS_ROOT_EVENT_CLASS // BasicEvents.h +struct BaseEventFlags; struct EventFlags; // TextEvents.h struct AlternativeCharCode; // TextRange.h struct TextRangeStyle; struct TextRange;
--- a/widget/TextEvents.h +++ b/widget/TextEvents.h @@ -340,22 +340,24 @@ public: uint32_t RangeCount() const { return mRanges ? mRanges->Length() : 0; } bool CausesDOMTextEvent() const { - return message == NS_COMPOSITION_CHANGE; + return message == NS_COMPOSITION_CHANGE || + message == NS_COMPOSITION_COMMIT_AS_IS; } bool CausesDOMCompositionEndEvent() const { - return message == NS_COMPOSITION_END; + return message == NS_COMPOSITION_END || + message == NS_COMPOSITION_COMMIT_AS_IS; } }; /****************************************************************************** * mozilla::WidgetQueryContentEvent ******************************************************************************/ class WidgetQueryContentEvent : public WidgetGUIEvent
--- a/widget/WidgetEventImpl.cpp +++ b/widget/WidgetEventImpl.cpp @@ -135,16 +135,17 @@ WidgetEvent::HasKeyEventMessage() const bool WidgetEvent::HasIMEEventMessage() const { switch (message) { case NS_COMPOSITION_START: case NS_COMPOSITION_END: case NS_COMPOSITION_UPDATE: case NS_COMPOSITION_CHANGE: + case NS_COMPOSITION_COMMIT_AS_IS: return true; default: return false; } } bool WidgetEvent::HasPluginActivationEventMessage() const