Bug 549674 part.1 Commit composition string at setting value of <input> or <textarea> r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 18 Jun 2015 23:56:20 +0900
changeset 281964 2a8ff86a5392763ac26fbb81b6c3c8da968836c2
parent 281963 db0f91911578db121cd5d5e26ad13ccabb1f7a79
child 281965 541e2c3f465f826f1e1e66d204e86f2d9cf10c69
push id897
push userjlund@mozilla.com
push dateMon, 14 Sep 2015 18:56:12 +0000
treeherdermozilla-release@9411e2d2b214 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs549674
milestone41.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
Bug 549674 part.1 Commit composition string at setting value of <input> or <textarea> r=smaug
dom/html/HTMLInputElement.cpp
dom/html/HTMLInputElement.h
dom/html/HTMLTextAreaElement.cpp
dom/html/HTMLTextAreaElement.h
dom/html/nsTextEditorState.cpp
dom/html/nsTextEditorState.h
widget/tests/window_composition_text_querycontent.xul
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -980,17 +980,17 @@ HTMLInputElement::Clone(mozilla::dom::No
   switch (GetValueMode()) {
     case VALUE_MODE_VALUE:
       if (mValueChanged) {
         // We don't have our default value anymore.  Set our value on
         // the clone.
         nsAutoString value;
         GetValueInternal(value);
         // SetValueInternal handles setting the VALUE_CHANGED bit for us
-        rv = it->SetValueInternal(value, false, true);
+        rv = it->SetValueInternal(value, nsTextEditorState::eSetValue_Notify);
         NS_ENSURE_SUCCESS(rv, rv);
       }
       break;
     case VALUE_MODE_FILENAME:
       if (it->OwnerDoc()->IsStaticDocument()) {
         // We're going to be used in print preview.  Since the doc is static
         // we can just grab the pretty string and use it as wallpaper
         GetDisplayFileName(it->mStaticDocFileList);
@@ -1165,41 +1165,44 @@ HTMLInputElement::AfterSetAttr(int32_t a
         // prevented there being a valid step in range). Changing @max to/from
         // 1 and a number greater than on equal to 3 should change whether we
         // have a step mismatch or not.
         // The value may also need to change between a value that results in
         // a step mismatch and a value that results in overflow. For example,
         // if @max in the example above were to change from 1 to -1.
         nsAutoString value;
         GetValue(value);
-        nsresult rv = SetValueInternal(value, false, false);
+        nsresult rv =
+          SetValueInternal(value, nsTextEditorState::eSetValue_Internal);
         NS_ENSURE_SUCCESS(rv, rv);
         MOZ_ASSERT(!GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
                    "HTML5 spec does not allow this");
       }
     } else if (aName == nsGkAtoms::min) {
       UpdateHasRange();
       UpdateRangeUnderflowValidityState();
       UpdateStepMismatchValidityState();
       if (mType == NS_FORM_INPUT_RANGE) {
         // See @max comment
         nsAutoString value;
         GetValue(value);
-        nsresult rv = SetValueInternal(value, false, false);
+        nsresult rv =
+          SetValueInternal(value, nsTextEditorState::eSetValue_Internal);
         NS_ENSURE_SUCCESS(rv, rv);
         MOZ_ASSERT(!GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
                    "HTML5 spec does not allow this");
       }
     } else if (aName == nsGkAtoms::step) {
       UpdateStepMismatchValidityState();
       if (mType == NS_FORM_INPUT_RANGE) {
         // See @max comment
         nsAutoString value;
         GetValue(value);
-        nsresult rv = SetValueInternal(value, false, false);
+        nsresult rv =
+          SetValueInternal(value, nsTextEditorState::eSetValue_Internal);
         NS_ENSURE_SUCCESS(rv, rv);
         MOZ_ASSERT(!GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW),
                    "HTML5 spec does not allow this");
       }
     } else if (aName == nsGkAtoms::dir &&
                aValue && aValue->Equals(nsGkAtoms::_auto, eIgnoreCase)) {
       SetDirectionIfAuto(true, aNotify);
     } else if (aName == nsGkAtoms::lang) {
@@ -1577,27 +1580,31 @@ HTMLInputElement::SetValue(const nsAStri
       // event, we should keep it that way. Otherwise, we should make sure the
       // element will not fire any event because of the script interaction.
       //
       // NOTE: this is currently quite expensive work (too much string
       // manipulation). We should probably optimize that.
       nsAutoString currentValue;
       GetValue(currentValue);
 
-      nsresult rv = SetValueInternal(aValue, false, true);
+      nsresult rv =
+        SetValueInternal(aValue, nsTextEditorState::eSetValue_ByContent |
+                                 nsTextEditorState::eSetValue_Notify);
       if (NS_FAILED(rv)) {
         aRv.Throw(rv);
         return;
       }
 
       if (mFocusedValue.Equals(currentValue)) {
         GetValue(mFocusedValue);
       }
     } else {
-      nsresult rv = SetValueInternal(aValue, false, true);
+      nsresult rv =
+        SetValueInternal(aValue, nsTextEditorState::eSetValue_ByContent |
+                                 nsTextEditorState::eSetValue_Notify);
       if (NS_FAILED(rv)) {
         aRv.Throw(rv);
         return;
       }
     }
   }
 }
 
@@ -2199,17 +2206,19 @@ HTMLInputElement::SetUserInput(const nsA
     if (!list.AppendElement(aValue, fallible)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     ErrorResult rv;
     MozSetFileNameArray(list, rv);
     return rv.StealNSResult();
   } else {
-    nsresult rv = SetValueInternal(aValue, true, true);
+    nsresult rv =
+      SetValueInternal(aValue, nsTextEditorState::eSetValue_BySetUserInput |
+                               nsTextEditorState::eSetValue_Notify);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
                                        static_cast<nsIDOMHTMLInputElement*>(this),
                                        NS_LITERAL_STRING("input"), true,
                                        true);
 
@@ -2507,19 +2516,17 @@ HTMLInputElement::UpdateFileList()
       }
     }
   }
 
   return NS_OK;
 }
 
 nsresult
-HTMLInputElement::SetValueInternal(const nsAString& aValue,
-                                   bool aUserInput,
-                                   bool aSetValueChanged)
+HTMLInputElement::SetValueInternal(const nsAString& aValue, uint32_t aFlags)
 {
   NS_PRECONDITION(GetValueMode() != VALUE_MODE_FILENAME,
                   "Don't call SetValueInternal for file inputs");
 
   switch (GetValueMode()) {
     case VALUE_MODE_VALUE:
     {
       // At the moment, only single line text control have to sanitize their value
@@ -2527,31 +2534,32 @@ HTMLInputElement::SetValueInternal(const
       // it if it's useless.
       nsAutoString value(aValue);
 
       if (!mParserCreating) {
         SanitizeValue(value);
       }
       // else DoneCreatingElement calls us again once mParserCreating is false
 
-      if (aSetValueChanged) {
+      bool setValueChanged = !!(aFlags & nsTextEditorState::eSetValue_Notify);
+      if (setValueChanged) {
         SetValueChanged(true);
       }
 
       if (IsSingleLineTextControl(false)) {
-        if (!mInputData.mState->SetValue(value, aUserInput, aSetValueChanged)) {
+        if (!mInputData.mState->SetValue(value, aFlags)) {
           return NS_ERROR_OUT_OF_MEMORY;
         }
         if (mType == NS_FORM_INPUT_EMAIL) {
           UpdateAllValidityStates(mParserCreating);
         }
       } else {
         free(mInputData.mValue);
         mInputData.mValue = ToNewUnicode(value);
-        if (aSetValueChanged) {
+        if (setValueChanged) {
           SetValueChanged(true);
         }
         if (mType == NS_FORM_INPUT_NUMBER) {
           // This has to happen before OnValueChanged is called because that
           // method needs the new value of our frame's anon text control.
           nsNumberControlFrame* numberControlFrame =
             do_QueryFrame(GetPrimaryFrame());
           if (numberControlFrame) {
@@ -3149,17 +3157,18 @@ HTMLInputElement::PreHandleEvent(EventCh
   // Fire onchange (if necessary), before we do the blur, bug 357684.
   if (aVisitor.mEvent->message == NS_BLUR_CONTENT) {
     // Experimental mobile types rely on the system UI to prevent users to not
     // set invalid values but we have to be extra-careful. Especially if the
     // option has been enabled on desktop.
     if (IsExperimentalMobileType(mType)) {
       nsAutoString aValue;
       GetValueInternal(aValue);
-      nsresult rv = SetValueInternal(aValue, false, false);
+      nsresult rv =
+        SetValueInternal(aValue, nsTextEditorState::eSetValue_Internal);
       NS_ENSURE_SUCCESS(rv, rv);
     }
     FireChangeEventIfNeeded();
   }
 
   if (mType == NS_FORM_INPUT_RANGE &&
       (aVisitor.mEvent->message == NS_FOCUS_CONTENT ||
        aVisitor.mEvent->message == NS_BLUR_CONTENT)) {
@@ -3270,17 +3279,19 @@ HTMLInputElement::PreHandleEvent(EventCh
     }
     if (textControl && aVisitor.mEvent->originalTarget == textControl) {
       if (aVisitor.mEvent->message == NS_EDITOR_INPUT) {
         // Propogate the anon text control's new value to our HTMLInputElement:
         nsAutoString value;
         numberControlFrame->GetValueOfAnonTextControl(value);
         numberControlFrame->HandlingInputEvent(true);
         nsWeakFrame weakNumberControlFrame(numberControlFrame);
-        rv = SetValueInternal(value, true, true);
+        rv = SetValueInternal(value,
+                              nsTextEditorState::eSetValue_BySetUserInput |
+                              nsTextEditorState::eSetValue_Notify);
         NS_ENSURE_SUCCESS(rv, rv);
         if (weakNumberControlFrame.IsAlive()) {
           numberControlFrame->HandlingInputEvent(false);
         }
       }
       else if (aVisitor.mEvent->message == NS_FORM_CHANGE) {
         // We cancel the DOM 'change' event that is fired for any change to our
         // anonymous text control since we fire our own 'change' events and
@@ -3347,17 +3358,18 @@ HTMLInputElement::CancelRangeThumbDrag(b
   } else {
     // Don't dispatch an 'input' event - at least not using
     // DispatchTrustedEvent.
     // TODO: decide what we should do here - bug 851782.
     nsAutoString val;
     ConvertNumberToString(mRangeThumbDragStartValue, val);
     // TODO: What should we do if SetValueInternal fails?  (The allocation
     // is small, so we should be fine here.)
-    SetValueInternal(val, true, true);
+    SetValueInternal(val, nsTextEditorState::eSetValue_BySetUserInput |
+                          nsTextEditorState::eSetValue_Notify);
     nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame());
     if (frame) {
       frame->UpdateForValueChange();
     }
     nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
       new AsyncEventDispatcher(this, NS_LITERAL_STRING("input"), true, false);
     asyncDispatcher->RunDOMEventWhenSafe();
   }
@@ -3367,17 +3379,18 @@ void
 HTMLInputElement::SetValueOfRangeForUserEvent(Decimal aValue)
 {
   MOZ_ASSERT(aValue.isFinite());
 
   nsAutoString val;
   ConvertNumberToString(aValue, val);
   // TODO: What should we do if SetValueInternal fails?  (The allocation
   // is small, so we should be fine here.)
-  SetValueInternal(val, true, true);
+  SetValueInternal(val, nsTextEditorState::eSetValue_BySetUserInput |
+                        nsTextEditorState::eSetValue_Notify);
   nsRangeFrame* frame = do_QueryFrame(GetPrimaryFrame());
   if (frame) {
     frame->UpdateForValueChange();
   }
   nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
                                        static_cast<nsIDOMHTMLInputElement*>(this),
                                        NS_LITERAL_STRING("input"), true,
                                        false);
@@ -3460,17 +3473,18 @@ HTMLInputElement::StepNumberControlForUs
   if (NS_FAILED(rv) || !newValue.isFinite()) {
     return; // value should not or will not change
   }
 
   nsAutoString newVal;
   ConvertNumberToString(newValue, newVal);
   // TODO: What should we do if SetValueInternal fails?  (The allocation
   // is small, so we should be fine here.)
-  SetValueInternal(newVal, true, true);
+  SetValueInternal(newVal, nsTextEditorState::eSetValue_BySetUserInput |
+                           nsTextEditorState::eSetValue_Notify);
 
   nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
                                        static_cast<nsIDOMHTMLInputElement*>(this),
                                        NS_LITERAL_STRING("input"), true,
                                        false);
 }
 
 static bool
@@ -4274,17 +4288,17 @@ HTMLInputElement::HandleTypeChange(uint8
         if (aOldValueMode != VALUE_MODE_VALUE) {
           GetAttr(kNameSpaceID_None, nsGkAtoms::value, value);
         } else {
           value = aOldValue;
         }
         // TODO: What should we do if SetValueInternal fails?  (The allocation
         // may potentially be big, but most likely we've failed to allocate
         // before the type change.)
-        SetValueInternal(value, false, false);
+        SetValueInternal(value, nsTextEditorState::eSetValue_Internal);
       }
       break;
     case VALUE_MODE_FILENAME:
     default:
       // We don't care about the value.
       // There is no value sanitizing algorithm for elements in this mode.
       break;
   }
@@ -4957,17 +4971,18 @@ HTMLInputElement::SetRangeText(const nsA
         aSelectionEnd = state->GetSelectionProperties().mEnd;
         aRv = NS_OK;
       }
     }
   }
 
   if (aStart <= aEnd) {
     value.Replace(aStart, aEnd - aStart, aReplacement);
-    nsresult rv = SetValueInternal(value, false, false);
+    nsresult rv =
+      SetValueInternal(value, nsTextEditorState::eSetValue_ByContent);
     if (NS_FAILED(rv)) {
       aRv.Throw(rv);
       return;
     }
   }
 
   uint32_t newEnd = aStart + aReplacement.Length();
   int32_t delta =  aReplacement.Length() - (aEnd - aStart);
@@ -5282,17 +5297,17 @@ HTMLInputElement::SetDefaultValueAsValue
                "GetValueMode() should return VALUE_MODE_VALUE!");
 
   // The element has a content attribute value different from it's value when
   // it's in the value mode value.
   nsAutoString resetVal;
   GetDefaultValue(resetVal);
 
   // SetValueInternal is going to sanitize the value.
-  return SetValueInternal(resetVal, false, false);
+  return SetValueInternal(resetVal, nsTextEditorState::eSetValue_Internal);
 }
 
 void
 HTMLInputElement::SetDirectionIfAuto(bool aAuto, bool aNotify)
 {
   if (aAuto) {
     SetHasDirAuto();
     if (IsSingleLineTextControl(true)) {
@@ -5523,17 +5538,17 @@ HTMLInputElement::DoneCreatingElement()
 
   // Sanitize the value.
   if (GetValueMode() == VALUE_MODE_VALUE) {
     nsAutoString aValue;
     GetValue(aValue);
     // TODO: What should we do if SetValueInternal fails?  (The allocation
     // may potentially be big, but most likely we've failed to allocate
     // before the type change.)
-    SetValueInternal(aValue, false, false);
+    SetValueInternal(aValue, nsTextEditorState::eSetValue_Internal);
   }
 
   mShouldInitChecked = false;
 }
 
 EventStates
 HTMLInputElement::IntrinsicState() const
 {
@@ -5682,17 +5697,18 @@ HTMLInputElement::RestoreState(nsPresSta
         if (GetValueMode() == VALUE_MODE_DEFAULT &&
             mType != NS_FORM_INPUT_HIDDEN) {
           break;
         }
 
         // TODO: What should we do if SetValueInternal fails?  (The allocation
         // may potentially be big, but most likely we've failed to allocate
         // before the type change.)
-        SetValueInternal(inputState->GetValue(), false, true);
+        SetValueInternal(inputState->GetValue(),
+                         nsTextEditorState::eSetValue_Notify);
         break;
     }
   }
 
   if (aState->IsDisabledSet()) {
     SetDisabled(aState->GetDisabled());
   }
 
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -814,19 +814,24 @@ protected:
    * @param aLen    the length of the sub-string.
    * @param aResult the parsed number.
    * @return whether the sub-string has been parsed successfully.
    */
   static bool DigitSubStringToNumber(const nsAString& aValue, uint32_t aStart,
                                      uint32_t aLen, uint32_t* aResult);
 
   // Helper method
-  nsresult SetValueInternal(const nsAString& aValue,
-                            bool aUserInput,
-                            bool aSetValueChanged);
+
+  /**
+   * Setting the value.
+   *
+   * @param aValue      String to set.
+   * @param aFlags      See nsTextEditorState::SetValueFlags.
+   */
+  nsresult SetValueInternal(const nsAString& aValue, uint32_t aFlags);
 
   nsresult GetValueInternal(nsAString& aValue) const;
 
   /**
    * Returns whether the current value is the empty string.
    *
    * @return whether the current value is the empty string.
    */
--- a/dom/html/HTMLTextAreaElement.cpp
+++ b/dom/html/HTMLTextAreaElement.cpp
@@ -292,23 +292,24 @@ HTMLTextAreaElement::UpdatePlaceholderVi
 NS_IMETHODIMP_(bool)
 HTMLTextAreaElement::GetPlaceholderVisibility()
 {
   return mState.GetPlaceholderVisibility();
 }
 
 nsresult
 HTMLTextAreaElement::SetValueInternal(const nsAString& aValue,
-                                      bool aUserInput)
+                                      uint32_t aFlags)
 {
   // Need to set the value changed flag here, so that
   // nsTextControlFrame::UpdateValueDisplay retrieves the correct value
   // if needed.
   SetValueChanged(true);
-  if (!mState.SetValue(aValue, aUserInput, true)) {
+  aFlags |= nsTextEditorState::eSetValue_Notify;
+  if (!mState.SetValue(aValue, aFlags)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 HTMLTextAreaElement::SetValue(const nsAString& aValue)
@@ -318,33 +319,34 @@ HTMLTextAreaElement::SetValue(const nsAS
   // event, we should keep it that way. Otherwise, we should make sure the
   // element will not fire any event because of the script interaction.
   //
   // NOTE: this is currently quite expensive work (too much string
   // manipulation). We should probably optimize that.
   nsAutoString currentValue;
   GetValueInternal(currentValue, true);
 
-  nsresult rv = SetValueInternal(aValue, false);
+  nsresult rv =
+    SetValueInternal(aValue, nsTextEditorState::eSetValue_ByContent);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (mFocusedValue.Equals(currentValue)) {
     GetValueInternal(mFocusedValue, true);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP 
 HTMLTextAreaElement::SetUserInput(const nsAString& aValue)
 {
   if (!nsContentUtils::IsCallerChrome()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
-  return SetValueInternal(aValue, true);
+  return SetValueInternal(aValue, nsTextEditorState::eSetValue_BySetUserInput);
 }
 
 NS_IMETHODIMP
 HTMLTextAreaElement::SetValueChanged(bool aValueChanged)
 {
   bool previousValue = mValueChanged;
 
   mValueChanged = aValueChanged;
@@ -963,17 +965,18 @@ HTMLTextAreaElement::SetRangeText(const 
         aSelectionEnd = mState.GetSelectionProperties().mEnd;
         aRv = NS_OK;
       }
     }
   }
 
   if (aStart <= aEnd) {
     value.Replace(aStart, aEnd - aStart, aReplacement);
-    nsresult rv = SetValueInternal(value, false);
+    nsresult rv =
+      SetValueInternal(value, nsTextEditorState::eSetValue_ByContent);
     if (NS_FAILED(rv)) {
       aRv.Throw(rv);
       return;
     }
   }
 
   uint32_t newEnd = aStart + aReplacement.Length();
   int32_t delta =  aReplacement.Length() - (aEnd - aStart);
--- a/dom/html/HTMLTextAreaElement.h
+++ b/dom/html/HTMLTextAreaElement.h
@@ -317,18 +317,24 @@ protected:
    * Get the value, whether it is from the content or the frame.
    * @param aValue the value [out]
    * @param aIgnoreWrap whether to ignore the wrap attribute when getting the
    *        value.  If this is true, linebreaks will not be inserted even if
    *        wrap=hard.
    */
   void GetValueInternal(nsAString& aValue, bool aIgnoreWrap) const;
 
-  nsresult SetValueInternal(const nsAString& aValue,
-                            bool aUserInput);
+  /**
+   * Setting the value.
+   *
+   * @param aValue      String to set.
+   * @param aFlags      See nsTextEditorState::SetValueFlags.
+   */
+  nsresult SetValueInternal(const nsAString& aValue, uint32_t aFlags);
+
   nsresult GetSelectionRange(int32_t* aSelectionStart, int32_t* aSelectionEnd);
 
   /**
    * Common method to call from the various mutation observer methods.
    * aContent is a content node that's either the one that changed or its
    * parent; we should only respond to the change if aContent is non-anonymous.
    */
   void ContentChanged(nsIContent* aContent);
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -22,16 +22,17 @@
 #include "nsIDOMHTMLInputElement.h"
 #include "nsIDOMHTMLTextAreaElement.h"
 #include "nsITransactionManager.h"
 #include "nsIControllerContext.h"
 #include "nsAttrValue.h"
 #include "nsAttrValueInlines.h"
 #include "nsGenericHTMLElement.h"
 #include "nsIDOMEventListener.h"
+#include "nsIEditorIMESupport.h"
 #include "nsIEditorObserver.h"
 #include "nsIWidget.h"
 #include "nsIDocumentEncoder.h"
 #include "nsISelectionPrivate.h"
 #include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIEditor.h"
 #include "nsTextEditRules.h"
@@ -1010,25 +1011,26 @@ nsTextInputListener::UpdateTextInputComm
   return domWindow->UpdateCommands(commandsToUpdate, sel, reason);
 }
 
 // END nsTextInputListener
 
 // nsTextEditorState
 
 nsTextEditorState::nsTextEditorState(nsITextControlElement* aOwningElement)
-  : mTextCtrlElement(aOwningElement),
-    mBoundFrame(nullptr),
-    mEverInited(false),
-    mEditorInitialized(false),
-    mInitializing(false),
-    mValueTransferInProgress(false),
-    mSelectionCached(true),
-    mSelectionRestoreEagerInit(false),
-    mPlaceholderVisibility(false)
+  : mTextCtrlElement(aOwningElement)
+  , mBoundFrame(nullptr)
+  , mEverInited(false)
+  , mEditorInitialized(false)
+  , mInitializing(false)
+  , mValueTransferInProgress(false)
+  , mSelectionCached(true)
+  , mSelectionRestoreEagerInit(false)
+  , mPlaceholderVisibility(false)
+  , mIsCommittingComposition(false)
 {
   MOZ_COUNT_CTOR(nsTextEditorState);
 }
 
 nsTextEditorState::~nsTextEditorState()
 {
   MOZ_COUNT_DTOR(nsTextEditorState);
   Clear();
@@ -1436,17 +1438,17 @@ nsTextEditorState::PrepareEditor(const n
     // Now call SetValue() which will make the necessary editor calls to set
     // the default value.  Make sure to turn off undo before setting the default
     // value, and turn it back on afterwards. This will make sure we can't undo
     // past the default value.
 
     rv = newEditor->EnableUndo(false);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    bool success = SetValue(defaultValue, false, false);
+    bool success = SetValue(defaultValue, eSetValue_Internal);
     NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
 
     rv = newEditor->EnableUndo(true);
     NS_ASSERTION(NS_SUCCEEDED(rv),"Transaction Manager must have failed");
 
     // Now restore the original editor flags.
     rv = newEditor->SetFlags(editorFlags);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -1700,17 +1702,17 @@ nsTextEditorState::UnbindFromFrame(nsTex
     mTextListener = nullptr;
   }
 
   mBoundFrame = nullptr;
 
   // Now that we don't have a frame any more, store the value in the text buffer.
   // The only case where we don't do this is if a value transfer is in progress.
   if (!mValueTransferInProgress) {
-    bool success = SetValue(value, false, false);
+    bool success = SetValue(value, eSetValue_Internal);
     // TODO Find something better to do if this fails...
     NS_ENSURE_TRUE_VOID(success);
   }
 
   if (mRootNode && mMutationObserver) {
     mRootNode->RemoveMutationObserver(mMutationObserver);
     mMutationObserver = nullptr;
   }
@@ -1855,16 +1857,25 @@ nsTextEditorState::GetMaxLength(int32_t*
   }
 
   return false;
 }
 
 void
 nsTextEditorState::GetValue(nsAString& aValue, bool aIgnoreWrap) const
 {
+  // While SetValue() is being called and requesting to commit composition to
+  // IME, GetValue() may be called for appending text or something.  Then, we
+  // need to return the latest aValue of SetValue() since the value hasn't
+  // been set to the editor yet.
+  if (mIsCommittingComposition) {
+    aValue = mValueBeingSet;
+    return;
+  }
+
   if (mEditor && mBoundFrame && (mEditorInitialized || !IsSingleLineTextControl())) {
     bool canCache = aIgnoreWrap && !IsSingleLineTextControl();
     if (canCache && !mCachedValue.IsEmpty()) {
       aValue = mCachedValue;
       return;
     }
 
     aValue.Truncate(); // initialize out param
@@ -1916,19 +1927,83 @@ nsTextEditorState::GetValue(nsAString& a
       mTextCtrlElement->GetDefaultValueFromContent(aValue);
     } else {
       aValue = NS_ConvertUTF8toUTF16(*mValue);
     }
   }
 }
 
 bool
-nsTextEditorState::SetValue(const nsAString& aValue, bool aUserInput,
-                            bool aSetValueChanged)
+nsTextEditorState::SetValue(const nsAString& aValue, uint32_t aFlags)
 {
+  nsAutoString newValue(aValue);
+
+  // While mIsCommittingComposition is true (that means that some event
+  // handlers which are fired during committing composition are the caller of
+  // this method), GetValue() uses mValueBeingSet for its result because the
+  // first calls of this methods hasn't set the value yet.  So, when it's true,
+  // we need to modify mValueBeingSet.  In this case, we will back to the first
+  // call of this method, then, mValueBeingSet will be truncated when
+  // mIsCommittingComposition is set false.  See below.
+  if (mIsCommittingComposition) {
+    mValueBeingSet = aValue;
+  }
+
+  // Note that if this may be called during reframe of the editor.  In such
+  // case, we shouldn't commit composition.  Therefore, when this is called
+  // for internal processing, we shouldn't commit the composition.
+  if (aFlags & (eSetValue_BySetUserInput | eSetValue_ByContent)) {
+    if (EditorHasComposition()) {
+      // When this is called recursively, there shouldn't be composition.
+      if (NS_WARN_IF(mIsCommittingComposition)) {
+        // Don't request to commit composition again.  But if it occurs,
+        // we should skip to set the new value to the editor here.  It should
+        // be set later with the updated mValueBeingSet.
+        return true;
+      }
+      // If there is composition, need to commit composition first because
+      // other browsers do that.
+      // NOTE: We don't need to block nested calls of this because input nor
+      //       other events won't be fired by setting values and script blocker
+      //       is used during setting the value to the editor.  IE also allows
+      //       to set the editor value on the input event which is caused by
+      //       forcibly committing composition.
+      if (nsContentUtils::IsSafeToRunScript()) {
+        WeakPtr<nsTextEditorState> self(this);
+        // WARNING: During this call, compositionupdate, compositionend, input
+        // events will be fired.  Therefore, everything can occur.  E.g., the
+        // document may be unloaded.
+        mValueBeingSet = aValue;
+        mIsCommittingComposition = true;
+        nsCOMPtr<nsIEditorIMESupport> editorIMESupport =
+                                        do_QueryInterface(mEditor);
+        MOZ_RELEASE_ASSERT(editorIMESupport);
+        nsresult rv = editorIMESupport->ForceCompositionEnd();
+        if (!self.get()) {
+          return true;
+        }
+        mIsCommittingComposition = false;
+        // If this is called recursively during committing composition and
+        // some of them may be skipped above.  Therefore, we need to set
+        // value to the editor with the aValue of the latest call.
+        newValue = mValueBeingSet;
+        // When mIsCommittingComposition is false, mValueBeingSet won't be
+        // used.  Therefore, let's clear it.
+        mValueBeingSet.Truncate();
+        if (NS_FAILED(rv)) {
+          NS_WARNING("nsTextEditorState failed to commit composition");
+          return true;
+        }
+      } else {
+        NS_WARNING("SetValue() is called when there is composition but "
+                   "it's not safe to request to commit the composition");
+      }
+    }
+  }
+
   if (mEditor && mBoundFrame) {
     // The InsertText call below might flush pending notifications, which
     // could lead into a scheduled PrepareEditor to be called.  That will
     // lead to crashes (or worse) because we'd be initializing the editor
     // before InsertText returns.  This script blocker makes sure that
     // PrepareEditor cannot be called prematurely.
     nsAutoScriptBlocker scriptBlocker;
 
@@ -1940,29 +2015,23 @@ nsTextEditorState::SetValue(const nsAStr
 #endif
 
     nsAutoString currentValue;
     mBoundFrame->GetText(currentValue);
 
     nsWeakFrame weakFrame(mBoundFrame);
 
     // this is necessary to avoid infinite recursion
-    if (!currentValue.Equals(aValue))
+    if (!currentValue.Equals(newValue))
     {
       ValueSetter valueSetter(mEditor);
 
       // \r is an illegal character in the dom, but people use them,
       // so convert windows and mac platform linebreaks to \n:
-      // Unfortunately aValue is declared const, so we have to copy
-      // in order to do this substitution.
-      nsString newValue;
-      if (!newValue.Assign(aValue, fallible)) {
-        return false;
-      }
-      if (aValue.FindChar(char16_t('\r')) != -1) {
+      if (newValue.FindChar(char16_t('\r')) != -1) {
         if (!nsContentUtils::PlatformToDOMLineBreaks(newValue, fallible)) {
           return false;
         }
       }
 
       nsCOMPtr<nsIDOMDocument> domDoc;
       mEditor->GetDocument(getter_AddRefs(domDoc));
       if (!domDoc) {
@@ -2015,17 +2084,18 @@ nsTextEditorState::SetValue(const nsAStr
         mEditor->GetFlags(&savedFlags);
         flags = savedFlags;
         flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
         flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
         flags |= nsIPlaintextEditor::eEditorDontEchoPassword;
         mEditor->SetFlags(flags);
 
         mTextListener->SettingValue(true);
-        mTextListener->SetValueChanged(aSetValueChanged);
+        bool notifyValueChanged = !!(aFlags & eSetValue_Notify);
+        mTextListener->SetValueChanged(notifyValueChanged);
 
         // Also don't enforce max-length here
         int32_t savedMaxLength;
         plaintextEditor->GetMaxTextLength(&savedMaxLength);
         plaintextEditor->SetMaxTextLength(-1);
 
         if (insertValue.IsEmpty()) {
           mEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
@@ -2038,17 +2108,17 @@ nsTextEditorState::SetValue(const nsAStr
 
         if (!weakFrame.IsAlive()) {
           // If the frame was destroyed because of a flush somewhere inside
           // InsertText, mBoundFrame here will be false.  But it's also possible
           // for the frame to go away because of another reason (such as deleting
           // the existing selection -- see bug 574558), in which case we don't
           // need to reset the value here.
           if (!mBoundFrame) {
-            return SetValue(newValue, false, aSetValueChanged);
+            return SetValue(newValue, aFlags & eSetValue_Notify);
           }
           return true;
         }
 
         if (!IsSingleLineTextControl()) {
           if (!mCachedValue.Assign(newValue, fallible)) {
             return false;
           }
@@ -2060,17 +2130,17 @@ nsTextEditorState::SetValue(const nsAStr
           selPriv->EndBatchChanges();
       }
     }
   } else {
     if (!mValue) {
       mValue = new nsCString;
     }
     nsString value;
-    if (!value.Assign(aValue, fallible)) {
+    if (!value.Assign(newValue, fallible)) {
       return false;
     }
     if (!nsContentUtils::PlatformToDOMLineBreaks(value, fallible)) {
       return false;
     }
     if (!CopyUTF16toUTF8(value, *mValue, fallible)) {
       return false;
     }
@@ -2160,16 +2230,26 @@ nsTextEditorState::HideSelectionIfBlurre
 {
   MOZ_ASSERT(mSelCon, "Should have a selection controller if we have a frame!");
   nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
   if (!nsContentUtils::IsFocusedContent(content)) {
     mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
   }
 }
 
+bool
+nsTextEditorState::EditorHasComposition()
+{
+  bool isComposing = false;
+  nsCOMPtr<nsIEditorIMESupport> editorIMESupport = do_QueryInterface(mEditor);
+  return editorIMESupport &&
+         NS_SUCCEEDED(editorIMESupport->GetComposing(&isComposing)) &&
+         isComposing;
+}
+
 NS_IMPL_ISUPPORTS(nsAnonDivObserver, nsIMutationObserver)
 
 void
 nsAnonDivObserver::CharacterDataChanged(nsIDocument*             aDocument,
                                         nsIContent*              aContent,
                                         CharacterDataChangeInfo* aInfo)
 {
   mTextEditorState->ClearValueCache();
--- a/dom/html/nsTextEditorState.h
+++ b/dom/html/nsTextEditorState.h
@@ -138,19 +138,30 @@ public:
   nsIEditor* GetEditor();
   nsISelectionController* GetSelectionController() const;
   nsFrameSelection* GetConstFrameSelection();
   nsresult BindToFrame(nsTextControlFrame* aFrame);
   void UnbindFromFrame(nsTextControlFrame* aFrame);
   nsresult PrepareEditor(const nsAString *aValue = nullptr);
   void InitializeKeyboardEventListeners();
 
+  enum SetValueFlags
+  {
+    // The call is for internal processing.
+    eSetValue_Internal              = 0,
+    // The value is changed by a call of setUserInput() from chrome.
+    eSetValue_BySetUserInput        = 1 << 0,
+    // The value is changed by changing value attribute of the element or
+    // something like setRangeText().
+    eSetValue_ByContent             = 1 << 1,
+    // Whether the value change should be notified to the frame/contet nor not.
+    eSetValue_Notify                = 1 << 2
+  };
   MOZ_WARN_UNUSED_RESULT bool SetValue(const nsAString& aValue,
-                                       bool aUserInput,
-                                       bool aSetValueAsChanged);
+                                       uint32_t aFlags);
   void GetValue(nsAString& aValue, bool aIgnoreWrap) const;
   void EmptyValue() { if (mValue) mValue->Truncate(); }
   bool IsEmpty() const { return mValue ? mValue->IsEmpty() : true; }
 
   nsresult CreatePlaceholderNode();
 
   mozilla::dom::Element* GetRootNode() {
     if (!mRootNode)
@@ -239,16 +250,18 @@ private:
   void Clear();
 
   nsresult InitializeRootNode();
 
   void FinishedRestoringSelection();
 
   mozilla::dom::HTMLInputElement* GetParentNumberControl(nsFrame* aFrame) const;
 
+  bool EditorHasComposition();
+
   class InitializationGuard {
   public:
     explicit InitializationGuard(nsTextEditorState& aState) :
       mState(aState),
       mGuardSet(false)
     {
       if (!mState.mInitializing) {
         mGuardSet = true;
@@ -278,24 +291,30 @@ private:
   nsCOMPtr<nsIEditor> mEditor;
   nsCOMPtr<mozilla::dom::Element> mRootNode;
   nsCOMPtr<mozilla::dom::Element> mPlaceholderDiv;
   nsTextControlFrame* mBoundFrame;
   nsRefPtr<nsTextInputListener> mTextListener;
   nsAutoPtr<nsCString> mValue;
   nsRefPtr<nsAnonDivObserver> mMutationObserver;
   mutable nsString mCachedValue; // Caches non-hard-wrapped value on a multiline control.
+  // mValueBeingSet is available only while SetValue() is requesting to commit
+  // composition.  I.e., this is valid only while mIsCommittingComposition is
+  // true.  While active composition is being committed, GetValue() needs
+  // the latest value which is set by SetValue().  So, this is cache for that.
+  nsString mValueBeingSet;
+  SelectionProperties mSelectionProperties;
   bool mEverInited; // Have we ever been initialized?
   bool mEditorInitialized;
   bool mInitializing; // Whether we're in the process of initialization
   bool mValueTransferInProgress; // Whether a value is being transferred to the frame
   bool mSelectionCached; // Whether mSelectionProperties is valid
   mutable bool mSelectionRestoreEagerInit; // Whether we're eager initing because of selection restore
-  SelectionProperties mSelectionProperties;
   bool mPlaceholderVisibility;
+  bool mIsCommittingComposition;
 };
 
 inline void
 ImplCycleCollectionUnlink(nsTextEditorState& aField)
 {
   aField.Unlink();
 }
 
--- a/widget/tests/window_composition_text_querycontent.xul
+++ b/widget/tests/window_composition_text_querycontent.xul
@@ -2805,23 +2805,322 @@ function runForceCommitTest()
   ok(!getHTMLEditorIMESupport(iframe3.contentWindow).isComposing,
      "runForceCommitTest: the other editable document has composition #11");
   ok(iframe2.contentDocument.body.innerHTML != iframe2BodyInnerHTML &&
      iframe2.contentDocument.body.innerHTML.indexOf("\u306E") >= 0,
      "runForceCommitTest: the editable document doesn't have the committed text #11");
   is(iframe3.contentDocument.body.innerHTML, iframe3BodyInnerHTML,
      "runForceCommitTest: the other editable document has the committed text? #11");
 
+  input.focus();
+  input.value = "";
+
+  synthesizeCompositionChange(
+    { "composition":
+      { "string": "\u306E",
+        "clauses":
+        [
+          { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+        ]
+      },
+      "caret": { "start": 1, "length": 0 }
+    });
+
+  events = [];
+  input.value = "set value";
+
+  is(events.length, 3,
+     "runForceCommitTest: wrong event count #12");
+  is(events[0].type, "text",
+     "runForceCommitTest: the 1st event must be text #12");
+  is(events[1].type, "compositionend",
+     "runForceCommitTest: the 2nd event must be compositionend #12");
+  is(events[2].type, "input",
+     "runForceCommitTest: the 3rd event must be input #12");
+  is(events[1].data, "\u306E",
+     "runForceCommitTest: compositionend has wrong data #12");
+  is(events[0].target, input,
+     "runForceCommitTest: The 1st event was fired on wrong event target #12");
+  is(events[1].target, input,
+     "runForceCommitTest: The 2nd event was fired on wrong event target #12");
+  is(events[2].target, input,
+     "runForceCommitTest: The 3rd event was fired on wrong event target #12");
+  ok(!getEditorIMESupport(input).isComposing,
+     "runForceCommitTest: the input still has composition #12");
+  is(input.value, "set value",
+     "runForceCommitTest: the input doesn't have the set text #12");
+
+  textarea.focus();
+  textarea.value = "";
+
+  synthesizeCompositionChange(
+    { "composition":
+      { "string": "\u306E",
+        "clauses":
+        [
+          { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+        ]
+      },
+      "caret": { "start": 1, "length": 0 }
+    });
+
+  events = [];
+  textarea.value = "set value";
+
+  is(events.length, 3,
+     "runForceCommitTest: wrong event count #13");
+  is(events[0].type, "text",
+     "runForceCommitTest: the 1st event must be text #13");
+  is(events[1].type, "compositionend",
+     "runForceCommitTest: the 2nd event must be compositionend #13");
+  is(events[2].type, "input",
+     "runForceCommitTest: the 3rd event must be input #13");
+  is(events[1].data, "\u306E",
+     "runForceCommitTest: compositionend has wrong data #13");
+  is(events[0].target, textarea,
+     "runForceCommitTest: The 1st event was fired on wrong event target #13");
+  is(events[1].target, textarea,
+     "runForceCommitTest: The 2nd event was fired on wrong event target #13");
+  is(events[2].target, textarea,
+     "runForceCommitTest: The 3rd event was fired on wrong event target #13");
+  ok(!getEditorIMESupport(textarea).isComposing,
+     "runForceCommitTest: the textarea still has composition #13");
+  is(textarea.value, "set value",
+     "runForceCommitTest: the textarea doesn't have the set text #13");
+
+  input.focus();
+  input.value = "";
+
+  synthesizeCompositionChange(
+    { "composition":
+      { "string": "\u306E",
+        "clauses":
+        [
+          { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+        ]
+      },
+      "caret": { "start": 1, "length": 0 }
+    });
+
+  events = [];
+  input.value += " appended value";
+
+  is(events.length, 3,
+     "runForceCommitTest: wrong event count #14");
+  is(events[0].type, "text",
+     "runForceCommitTest: the 1st event must be text #14");
+  is(events[1].type, "compositionend",
+     "runForceCommitTest: the 2nd event must be compositionend #14");
+  is(events[2].type, "input",
+     "runForceCommitTest: the 3rd event must be input #14");
+  is(events[1].data, "\u306E",
+     "runForceCommitTest: compositionend has wrong data #14");
+  is(events[0].target, input,
+     "runForceCommitTest: The 1st event was fired on wrong event target #14");
+  is(events[1].target, input,
+     "runForceCommitTest: The 2nd event was fired on wrong event target #14");
+  is(events[2].target, input,
+     "runForceCommitTest: The 3rd event was fired on wrong event target #14");
+  ok(!getEditorIMESupport(input).isComposing,
+     "runForceCommitTest: the input still has composition #14");
+  is(input.value, "\u306E appended value",
+     "runForceCommitTest: the input should have both composed text and appended text #14");
+
   window.removeEventListener("compositionstart", eventHandler, true);
   window.removeEventListener("compositionupdate", eventHandler, true);
   window.removeEventListener("compositionend", eventHandler, true);
   window.removeEventListener("input", eventHandler, true);
   window.removeEventListener("text", eventHandler, true);
 }
 
+function runNestedSettingValue()
+{
+  var isTesting = false;
+  var events = [];
+  function eventHandler(aEvent)
+  {
+    events.push(aEvent);
+    if (isTesting) {
+      aEvent.target.value += aEvent.type + ", ";
+    }
+  }
+  window.addEventListener("compositionstart", eventHandler, true);
+  window.addEventListener("compositionupdate", eventHandler, true);
+  window.addEventListener("compositionend", eventHandler, true);
+  window.addEventListener("input", eventHandler, true);
+  window.addEventListener("text", eventHandler, true);
+
+  textarea.focus();
+  textarea.value = "";
+
+  synthesizeCompositionChange(
+    { "composition":
+      { "string": "\u306E",
+        "clauses":
+        [
+          { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+        ]
+      },
+      "caret": { "start": 1, "length": 0 }
+    });
+
+  events = [];
+  isTesting = true;
+  textarea.value = "first setting value, ";
+  isTesting = false;
+
+  is(events.length, 3,
+     "runNestedSettingValue: wrong event count #1");
+  is(events[0].type, "text",
+     "runNestedSettingValue: the 1st event must be text #1");
+  is(events[1].type, "compositionend",
+     "runNestedSettingValue: the 2nd event must be compositionend #1");
+  is(events[2].type, "input",
+     "runNestedSettingValue: the 3rd event must be input #1");
+  is(events[1].data, "\u306E",
+     "runNestedSettingValue: compositionend has wrong data #1");
+  is(events[0].target, textarea,
+     "runNestedSettingValue: The 1st event was fired on wrong event target #1");
+  is(events[1].target, textarea,
+     "runNestedSettingValue: The 2nd event was fired on wrong event target #1");
+  is(events[2].target, textarea,
+     "runNestedSettingValue: The 3rd event was fired on wrong event target #1");
+  ok(!getEditorIMESupport(textarea).isComposing,
+     "runNestedSettingValue: the textarea still has composition #1");
+  is(textarea.value, "first setting value, text, compositionend, input, ",
+     "runNestedSettingValue: the textarea should have all string set to value attribute");
+
+  input.focus();
+  input.value = "";
+
+  synthesizeCompositionChange(
+    { "composition":
+      { "string": "\u306E",
+        "clauses":
+        [
+          { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+        ]
+      },
+      "caret": { "start": 1, "length": 0 }
+    });
+
+  events = [];
+  isTesting = true;
+  input.value = "first setting value, ";
+  isTesting = false;
+
+  is(events.length, 3,
+     "runNestedSettingValue: wrong event count #2");
+  is(events[0].type, "text",
+     "runNestedSettingValue: the 1st event must be text #2");
+  is(events[1].type, "compositionend",
+     "runNestedSettingValue: the 2nd event must be compositionend #2");
+  is(events[2].type, "input",
+     "runNestedSettingValue: the 3rd event must be input #2");
+  is(events[1].data, "\u306E",
+     "runNestedSettingValue: compositionend has wrong data #2");
+  is(events[0].target, input,
+     "runNestedSettingValue: The 1st event was fired on wrong event target #2");
+  is(events[1].target, input,
+     "runNestedSettingValue: The 2nd event was fired on wrong event target #2");
+  is(events[2].target, input,
+     "runNestedSettingValue: The 3rd event was fired on wrong event target #2");
+  ok(!getEditorIMESupport(input).isComposing,
+     "runNestedSettingValue: the input still has composition #2");
+  is(textarea.value, "first setting value, text, compositionend, input, ",
+     "runNestedSettingValue: the input should have all string set to value attribute #2");
+
+  textarea.focus();
+  textarea.value = "";
+
+  synthesizeCompositionChange(
+    { "composition":
+      { "string": "\u306E",
+        "clauses":
+        [
+          { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+        ]
+      },
+      "caret": { "start": 1, "length": 0 }
+    });
+
+  events = [];
+  isTesting = true;
+  textarea.setRangeText("first setting value, ");
+  isTesting = false;
+
+  is(events.length, 3,
+     "runNestedSettingValue: wrong event count #3");
+  is(events[0].type, "text",
+     "runNestedSettingValue: the 1st event must be text #3");
+  is(events[1].type, "compositionend",
+     "runNestedSettingValue: the 2nd event must be compositionend #3");
+  is(events[2].type, "input",
+     "runNestedSettingValue: the 3rd event must be input #3");
+  is(events[1].data, "\u306E",
+     "runNestedSettingValue: compositionend has wrong data #3");
+  is(events[0].target, textarea,
+     "runNestedSettingValue: The 1st event was fired on wrong event target #3");
+  is(events[1].target, textarea,
+     "runNestedSettingValue: The 2nd event was fired on wrong event target #3");
+  is(events[2].target, textarea,
+     "runNestedSettingValue: The 3rd event was fired on wrong event target #3");
+  ok(!getEditorIMESupport(textarea).isComposing,
+     "runNestedSettingValue: the textarea still has composition #3");
+  is(textarea.value, "\u306Efirst setting value, text, compositionend, input, ",
+     "runNestedSettingValue: the textarea should have appended by setRangeText() and all string set to value attribute #3");
+
+  input.focus();
+  input.value = "";
+
+  synthesizeCompositionChange(
+    { "composition":
+      { "string": "\u306E",
+        "clauses":
+        [
+          { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+        ]
+      },
+      "caret": { "start": 1, "length": 0 }
+    });
+
+  events = [];
+  isTesting = true;
+  input.setRangeText("first setting value, ");
+  isTesting = false;
+
+  is(events.length, 3,
+     "runNestedSettingValue: wrong event count #4");
+  is(events[0].type, "text",
+     "runNestedSettingValue: the 1st event must be text #4");
+  is(events[1].type, "compositionend",
+     "runNestedSettingValue: the 2nd event must be compositionend #4");
+  is(events[2].type, "input",
+     "runNestedSettingValue: the 3rd event must be input #4");
+  is(events[1].data, "\u306E",
+     "runNestedSettingValue: compositionend has wrong data #4");
+  is(events[0].target, input,
+     "runNestedSettingValue: The 1st event was fired on wrong event target #4");
+  is(events[1].target, input,
+     "runNestedSettingValue: The 2nd event was fired on wrong event target #4");
+  is(events[2].target, input,
+     "runNestedSettingValue: The 3rd event was fired on wrong event target #4");
+  ok(!getEditorIMESupport(input).isComposing,
+     "runNestedSettingValue: the input still has composition #4");
+  is(textarea.value, "\u306Efirst setting value, text, compositionend, input, ",
+     "runNestedSettingValue: the input should have all string appended by setRangeText() and set to value attribute #4");
+
+  window.removeEventListener("compositionstart", eventHandler, true);
+  window.removeEventListener("compositionupdate", eventHandler, true);
+  window.removeEventListener("compositionend", eventHandler, true);
+  window.removeEventListener("input", eventHandler, true);
+  window.removeEventListener("text", eventHandler, true);
+
+}
+
 function runAsyncForceCommitTest(aNextTest)
 {
   var events;
   function eventHandler(aEvent)
   {
     events.push(aEvent);
   };
 
@@ -4160,16 +4459,17 @@ function runTest()
   runCompositionCommitAsIsTest();
   runCompositionCommitTest();
   runCompositionTest();
   runCompositionEventTest();
   runCharAtPointTest(textarea, "textarea in the document");
   runCharAtPointAtOutsideTest();
   runBug722639Test();
   runForceCommitTest();
+  runNestedSettingValue();
   runBug811755Test();
   runIsComposingTest();
   runRedundantChangeTest();
   runNotRedundantChangeTest();
   runControlCharTest();
   runEditorReframeTests(function () {
     runAsyncForceCommitTest(function () {
       runRemoveContentTest(function () {