NS_IMETHODIMP TextComposition::CompositionEventDispatcher::Run() { if (!mPresContext->GetPresShell() || mPresContext->GetPresShell()->IsDestroying()) { return NS_OK; // cannot dispatch any events anymore } nsEventStatus status = nsEventStatus_eIgnore; switch (mEventMessage) { case NS_COMPOSITION_START: { nsCompositionEvent compStart(true, NS_COMPOSITION_START, mWidget); nsQueryContentEvent selectedText(true, NS_QUERY_SELECTED_TEXT, mWidget); nsContentEventHandler handler(mPresContext); handler.OnQuerySelectedText(&selectedText); NS_ASSERTION(selectedText.mSucceeded, "Failed to get selected text"); compStart.data = selectedText.mReply.mString; nsIMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext, &compStart, &status, nullptr); break; } case NS_COMPOSITION_UPDATE: case NS_COMPOSITION_END: { nsCompositionEvent compEvent(true, mEventMessage, mWidget); compEvent.data = mData; nsIMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext, &compEvent, &status, nullptr); break; } case NS_TEXT_TEXT: { nsTextEvent textEvent(true, NS_TEXT_TEXT, mWidget); textEvent.theText = mData; nsIMEStateManager::DispatchCompositionEvent(mEventTarget, mPresContext, &textEvent, &status, nullptr); break; } default: MOZ_NOT_REACHED("Unsupported event"); break; } return NS_OK; }
NS_IMETHODIMP TextComposition::CompositionEventDispatcher::Run() { nsRefPtr<nsPresContext> presContext = mTextComposition->mPresContext; if (!presContext || !presContext->GetPresShell() || presContext->GetPresShell()->IsDestroying()) { return NS_OK; // cannot dispatch any events anymore } // The widget can be different from the widget which has dispatched // composition events because GetWidget() returns a widget which is proper // for calling NotifyIME(). However, this must no be problem since both // widget should share native IME context. Therefore, even if an event // handler uses the widget for requesting IME to commit or cancel, it works. nsCOMPtr<nsIWidget> widget(mTextComposition->GetWidget()); if (NS_WARN_IF(!widget)) { return NS_OK; // cannot dispatch any events anymore } nsEventStatus status = nsEventStatus_eIgnore; switch (mEventMessage) { case NS_COMPOSITION_START: { WidgetCompositionEvent compStart(true, NS_COMPOSITION_START, widget); WidgetQueryContentEvent selectedText(true, NS_QUERY_SELECTED_TEXT, widget); ContentEventHandler handler(presContext); handler.OnQuerySelectedText(&selectedText); NS_ASSERTION(selectedText.mSucceeded, "Failed to get selected text"); compStart.data = selectedText.mReply.mString; compStart.mFlags.mIsSynthesizedForTests = mTextComposition->IsSynthesizedForTests(); IMEStateManager::DispatchCompositionEvent(mEventTarget, presContext, &compStart, &status, nullptr, mIsSynthesizedEvent); break; } case NS_COMPOSITION_UPDATE: case NS_COMPOSITION_END: { WidgetCompositionEvent compEvent(true, mEventMessage, widget); compEvent.data = mData; compEvent.mFlags.mIsSynthesizedForTests = mTextComposition->IsSynthesizedForTests(); IMEStateManager::DispatchCompositionEvent(mEventTarget, presContext, &compEvent, &status, nullptr, mIsSynthesizedEvent); break; } case NS_TEXT_TEXT: { WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget); textEvent.theText = mData; textEvent.mFlags.mIsSynthesizedForTests = mTextComposition->IsSynthesizedForTests(); IMEStateManager::DispatchCompositionEvent(mEventTarget, presContext, &textEvent, &status, nullptr, mIsSynthesizedEvent); break; } default: MOZ_CRASH("Unsupported event"); } return NS_OK; }
nsresult TextComposition::RequestToCommit(nsIWidget* aWidget, bool aDiscard) { // If this composition is already requested to be committed or canceled, // we don't need to request it again because even if the first request // failed, new request won't success, probably. And we shouldn't synthesize // events for committing or canceling composition twice or more times. if (mRequestedToCommitOrCancel) { return NS_OK; } nsRefPtr<TextComposition> kungFuDeathGrip(this); const nsAutoString lastData(mLastData); { AutoRestore<bool> saveRequestingCancel(mIsRequestingCancel); AutoRestore<bool> saveRequestingCommit(mIsRequestingCommit); if (aDiscard) { mIsRequestingCancel = true; mIsRequestingCommit = false; } else { mIsRequestingCancel = false; mIsRequestingCommit = true; } if (!mIsSynthesizedForTests) { // FYI: CompositionEvent and TextEvent caused by a call of NotifyIME() // may be discarded by PresShell if it's not safe to dispatch the // event. nsresult rv = aWidget->NotifyIME(IMENotification(aDiscard ? REQUEST_TO_CANCEL_COMPOSITION : REQUEST_TO_COMMIT_COMPOSITION)); if (rv == NS_ERROR_NOT_IMPLEMENTED) { return rv; } if (NS_WARN_IF(NS_FAILED(rv))) { 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; WidgetTextEvent textEvent(true, NS_TEXT_TEXT, widget); textEvent.theText = commitData; textEvent.mFlags.mIsSynthesizedForTests = true; MaybeDispatchCompositionUpdate(&textEvent); // If changing the data or committing string isn't empty, we need to // dispatch text event for setting the composition string without // IME selection. if (!Destroyed() && !widget->Destroyed() && (changingData || !commitData.IsEmpty())) { nsEventStatus status = nsEventStatus_eIgnore; widget->DispatchEvent(&textEvent, status); } if (!Destroyed() && !widget->Destroyed()) { nsEventStatus status = nsEventStatus_eIgnore; WidgetCompositionEvent endEvent(true, NS_COMPOSITION_END, widget); endEvent.data = 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); bool changingData = lastData != data; if (changingData) { DispatchCompositionEventRunnable(NS_COMPOSITION_UPDATE, data, true); } // If the last composition string and new data are different, we need to // dispatch text 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 text event. if (changingData || !data.IsEmpty()) { DispatchCompositionEventRunnable(NS_TEXT_TEXT, data, true); } DispatchCompositionEventRunnable(NS_COMPOSITION_END, data, true); return NS_OK; }