| Index: third_party/WebKit/Source/core/editing/InputMethodController.cpp | 
| diff --git a/third_party/WebKit/Source/core/editing/InputMethodController.cpp b/third_party/WebKit/Source/core/editing/InputMethodController.cpp | 
| index b70befe6440a692d856ddb169fc145088ea45c63..bce455ebbcc63d4b2a2b92977150d405b19b892c 100644 | 
| --- a/third_party/WebKit/Source/core/editing/InputMethodController.cpp | 
| +++ b/third_party/WebKit/Source/core/editing/InputMethodController.cpp | 
| @@ -44,6 +44,80 @@ | 
|  | 
| namespace blink { | 
|  | 
| +namespace { | 
| + | 
| +void dispatchCompositionUpdateEvent(LocalFrame& frame, const String& text) | 
| +{ | 
| +    Element* target = frame.document()->focusedElement(); | 
| +    if (!target) | 
| +        return; | 
| + | 
| +    CompositionEvent* event = CompositionEvent::create(EventTypeNames::compositionupdate, frame.domWindow(), text); | 
| +    target->dispatchEvent(event); | 
| +} | 
| + | 
| +void dispatchCompositionEndEvent(LocalFrame& frame, const String& text) | 
| +{ | 
| +    Element* target = frame.document()->focusedElement(); | 
| +    if (!target) | 
| +        return; | 
| + | 
| +    CompositionEvent* event = CompositionEvent::create(EventTypeNames::compositionend, frame.domWindow(), text); | 
| +    target->dispatchEvent(event); | 
| +} | 
| + | 
| +// Used to insert/replace text during composition update and confirm composition. | 
| +// Procedure: | 
| +//   1. Fire 'beforeinput' event for (TODO(chongz): deleted composed text) and inserted text | 
| +//   2. Fire 'compositionupdate' event | 
| +//   3. Fire TextEvent and modify DOM | 
| +//   TODO(chongz): 4. Fire 'input' event | 
| +void insertTextDuringCompositionWithEvents(LocalFrame& frame, const String& text, TypingCommand::Options options, TypingCommand::TextCompositionType compositionType) | 
| +{ | 
| +    DCHECK(compositionType == TypingCommand::TextCompositionType::TextCompositionUpdate || compositionType == TypingCommand::TextCompositionType::TextCompositionConfirm) | 
| +        << "compositionType should be TextCompositionUpdate or TextCompositionConfirm, but got " << static_cast<int>(compositionType); | 
| +    if (!frame.document()) | 
| +        return; | 
| + | 
| +    Element* target = frame.document()->focusedElement(); | 
| +    if (!target) | 
| +        return; | 
| + | 
| +    // TODO(chongz): Fire 'beforeinput' for the composed text being replaced/deleted. | 
| + | 
| +    // Only the last confirmed text is cancelable. | 
| +    InputEvent::EventCancelable beforeInputCancelable = (compositionType == TypingCommand::TextCompositionType::TextCompositionUpdate) ? InputEvent::EventCancelable::NotCancelable : InputEvent::EventCancelable::IsCancelable; | 
| +    DispatchEventResult result = dispatchBeforeInputFromComposition(target, InputEvent::InputType::InsertText, text, beforeInputCancelable); | 
| + | 
| +    if (beforeInputCancelable == InputEvent::EventCancelable::IsCancelable && result != DispatchEventResult::NotCanceled) | 
| +        return; | 
| + | 
| +    // 'beforeinput' event handler may destroy document. | 
| +    if (!frame.document()) | 
| +        return; | 
| + | 
| +    dispatchCompositionUpdateEvent(frame, text); | 
| +    // 'compositionupdate' event handler may destroy document. | 
| +    if (!frame.document()) | 
| +        return; | 
| + | 
| +    switch (compositionType) { | 
| +    case TypingCommand::TextCompositionType::TextCompositionUpdate: | 
| +        TypingCommand::insertText(*frame.document(), text, options, compositionType); | 
| +        break; | 
| +    case TypingCommand::TextCompositionType::TextCompositionConfirm: | 
| +        // TODO(chongz): Use TypingCommand::insertText after TextEvent was removed. (Removed from spec since 2012) | 
| +        // See TextEvent.idl. | 
| +        frame.eventHandler().handleTextInputEvent(text, 0, TextEventInputComposition); | 
| +        break; | 
| +    default: | 
| +        NOTREACHED(); | 
| +    } | 
| +    // TODO(chongz): Fire 'input' event. | 
| +} | 
| + | 
| +} // anonymous namespace | 
| + | 
| InputMethodController::SelectionOffsetsScope::SelectionOffsetsScope(InputMethodController* inputMethodController) | 
| : m_inputMethodController(inputMethodController) | 
| , m_offsets(inputMethodController->getSelectionOffsets()) | 
| @@ -96,11 +170,6 @@ void InputMethodController::documentDetached() | 
| m_compositionRange = nullptr; | 
| } | 
|  | 
| -bool InputMethodController::insertTextForConfirmedComposition(const String& text) | 
| -{ | 
| -    return frame().eventHandler().handleTextInputEvent(text, 0, TextEventInputComposition); | 
| -} | 
| - | 
| void InputMethodController::selectComposition() const | 
| { | 
| const EphemeralRange range = compositionEphemeralRange(); | 
| @@ -119,19 +188,6 @@ bool InputMethodController::confirmComposition() | 
| return confirmComposition(composingText()); | 
| } | 
|  | 
| -static void dispatchCompositionEndEvent(LocalFrame& frame, const String& text) | 
| -{ | 
| -    // We should send this event before sending a TextEvent as written in | 
| -    // Section 6.2.2 and 6.2.3 of the DOM Event specification. | 
| -    Element* target = frame.document()->focusedElement(); | 
| -    if (!target) | 
| -        return; | 
| - | 
| -    CompositionEvent* event = | 
| -        CompositionEvent::create(EventTypeNames::compositionend, frame.domWindow(), text); | 
| -    target->dispatchEvent(event); | 
| -} | 
| - | 
| bool InputMethodController::confirmComposition(const String& text, ConfirmCompositionBehavior confirmBehavior) | 
| { | 
| if (!hasComposition()) | 
| @@ -155,8 +211,6 @@ bool InputMethodController::confirmComposition(const String& text, ConfirmCompos | 
| if (frame().selection().isNone()) | 
| return false; | 
|  | 
| -    dispatchCompositionEndEvent(frame(), text); | 
| - | 
| if (!frame().document()) | 
| return false; | 
|  | 
| @@ -168,12 +222,13 @@ bool InputMethodController::confirmComposition(const String& text, ConfirmCompos | 
|  | 
| clear(); | 
|  | 
| -    // TODO(chongz): DOM update should happen before 'compositionend' and along with 'compositionupdate'. | 
| -    // https://crbug.com/575294 | 
| -    if (dispatchBeforeInputInsertText(frame().document()->focusedElement(), text) != DispatchEventResult::NotCanceled) | 
| +    insertTextDuringCompositionWithEvents(frame(), text, 0, TypingCommand::TextCompositionType::TextCompositionConfirm); | 
| +    // Event handler might destroy document. | 
| +    if (!frame().document()) | 
| return false; | 
|  | 
| -    insertTextForConfirmedComposition(text); | 
| +    // No DOM update after 'compositionend'. | 
| +    dispatchCompositionEndEvent(frame(), text); | 
|  | 
| return true; | 
| } | 
| @@ -213,13 +268,22 @@ void InputMethodController::cancelComposition() | 
| if (frame().selection().isNone()) | 
| return; | 
|  | 
| -    dispatchCompositionEndEvent(frame(), emptyString()); | 
| clear(); | 
| -    insertTextForConfirmedComposition(emptyString()); | 
| + | 
| +    // TODO(chongz): Update InputType::DeleteComposedCharacter with latest discussion. | 
| +    dispatchBeforeInputFromComposition(frame().document()->focusedElement(), InputEvent::InputType::DeleteComposedCharacter, emptyString(), InputEvent::EventCancelable::NotCancelable); | 
| +    dispatchCompositionUpdateEvent(frame(), emptyString()); | 
| +    insertTextDuringCompositionWithEvents(frame(), emptyString(), 0, TypingCommand::TextCompositionType::TextCompositionConfirm); | 
| +    // Event handler might destroy document. | 
| +    if (!frame().document()) | 
| +        return; | 
|  | 
| // An open typing command that disagrees about current selection would cause | 
| // issues with typing later on. | 
| TypingCommand::closeTyping(m_frame); | 
| + | 
| +    // No DOM update after 'compositionend'. | 
| +    dispatchCompositionEndEvent(frame(), emptyString()); | 
| } | 
|  | 
| void InputMethodController::cancelCompositionIfSelectionIsInvalid() | 
| @@ -253,58 +317,52 @@ void InputMethodController::setComposition(const String& text, const Vector<Comp | 
| if (frame().selection().isNone()) | 
| return; | 
|  | 
| -    if (Element* target = frame().document()->focusedElement()) { | 
| -        // Dispatch an appropriate composition event to the focused node. | 
| -        // We check the composition status and choose an appropriate composition event since this | 
| -        // function is used for three purposes: | 
| -        // 1. Starting a new composition. | 
| -        //    Send a compositionstart and a compositionupdate event when this function creates | 
| -        //    a new composition node, i.e. | 
| -        //    !hasComposition() && !text.isEmpty(). | 
| -        //    Sending a compositionupdate event at this time ensures that at least one | 
| -        //    compositionupdate event is dispatched. | 
| -        // 2. Updating the existing composition node. | 
| -        //    Send a compositionupdate event when this function updates the existing composition | 
| -        //    node, i.e. hasComposition() && !text.isEmpty(). | 
| -        // 3. Canceling the ongoing composition. | 
| -        //    Send a compositionend event when function deletes the existing composition node, i.e. | 
| -        //    !hasComposition() && test.isEmpty(). | 
| -        CompositionEvent* event = nullptr; | 
| -        if (!hasComposition()) { | 
| -            // We should send a compositionstart event only when the given text is not empty because this | 
| -            // function doesn't create a composition node when the text is empty. | 
| -            if (!text.isEmpty()) { | 
| -                target->dispatchEvent(CompositionEvent::create(EventTypeNames::compositionstart, frame().domWindow(), frame().selectedText())); | 
| -                event = CompositionEvent::create(EventTypeNames::compositionupdate, frame().domWindow(), text); | 
| -            } | 
| -        } else { | 
| -            if (!text.isEmpty()) | 
| -                event = CompositionEvent::create(EventTypeNames::compositionupdate, frame().domWindow(), text); | 
| -            else | 
| -                event = CompositionEvent::create(EventTypeNames::compositionend, frame().domWindow(), text); | 
| -        } | 
| -        if (event) { | 
| -            // TODO(chongz): Support canceling IME composition. | 
| -            // TODO(chongz): Should fire InsertText or DeleteComposedCharacter based on action. | 
| -            if (event->type() == EventTypeNames::compositionupdate) | 
| -                dispatchBeforeInputFromComposition(target, InputEvent::InputType::InsertText, text); | 
| -            target->dispatchEvent(event); | 
| -        } | 
| -    } | 
| +    Element* target = frame().document()->focusedElement(); | 
| +    if (!target) | 
| +        return; | 
|  | 
| -    // If text is empty, then delete the old composition here. If text is non-empty, InsertTextCommand::input | 
| -    // will delete the old composition with an optimized replace operation. | 
| +    // Dispatch an appropriate composition event to the focused node. | 
| +    // We check the composition status and choose an appropriate composition event since this | 
| +    // function is used for three purposes: | 
| +    // 1. Starting a new composition. | 
| +    //    Send a compositionstart and a compositionupdate event when this function creates | 
| +    //    a new composition node, i.e. | 
| +    //    !hasComposition() && !text.isEmpty(). | 
| +    //    Sending a compositionupdate event at this time ensures that at least one | 
| +    //    compositionupdate event is dispatched. | 
| +    // 2. Updating the existing composition node. | 
| +    //    Send a compositionupdate event when this function updates the existing composition | 
| +    //    node, i.e. hasComposition() && !text.isEmpty(). | 
| +    // 3. Canceling the ongoing composition. | 
| +    //    Send a compositionend event when function deletes the existing composition node, i.e. | 
| +    //    !hasComposition() && test.isEmpty(). | 
| if (text.isEmpty()) { | 
| -        DCHECK(frame().document()); | 
| +        if (hasComposition()) { | 
| +            confirmComposition(emptyString()); | 
| +            return; | 
| +        } | 
| +        // It's weird to call |setComposition()| with empty text outside composition, however some IME | 
| +        // (e.g. Japanese IBus-Anthy) did this, so we simply delete selection without sending extra events. | 
| TypingCommand::deleteSelection(*frame().document(), TypingCommand::PreventSpellChecking); | 
| +        return; | 
| } | 
|  | 
| +    // We should send a 'compositionstart' event only when the given text is not empty because this | 
| +    // function doesn't create a composition node when the text is empty. | 
| +    if (!hasComposition()) { | 
| +        target->dispatchEvent(CompositionEvent::create(EventTypeNames::compositionstart, frame().domWindow(), frame().selectedText())); | 
| +        if (!frame().document()) | 
| +            return; | 
| +    } | 
| + | 
| +    DCHECK(!text.isEmpty()); | 
| + | 
| clear(); | 
|  | 
| -    if (text.isEmpty()) | 
| +    insertTextDuringCompositionWithEvents(frame(), text, TypingCommand::SelectInsertedText | TypingCommand::PreventSpellChecking, TypingCommand::TextCompositionUpdate); | 
| +    // Event handlers might destroy document. | 
| +    if (!frame().document()) | 
| return; | 
| -    DCHECK(frame().document()); | 
| -    TypingCommand::insertText(*frame().document(), text, TypingCommand::SelectInsertedText | TypingCommand::PreventSpellChecking, TypingCommand::TextCompositionUpdate); | 
|  | 
| // Find out what node has the composition now. | 
| Position base = mostForwardCaretPosition(frame().selection().base()); | 
|  |