Bug 482138 [TSF] nsTextFrame has to draw composition string by TIP specified style r+sr=roc
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 23 Apr 2009 12:27:12 +0900
changeset 27654 7dd1622f62d9d7342f4daff979fd573277ed62f2
parent 27653 98786ba84d6e7887d2e5f3a847fcad7b827c8f66
child 27655 31881edd7c5dcb55a26549985b6f440a7943c84d
push idunknown
push userunknown
push dateunknown
bugs482138
milestone1.9.2a1pre
Bug 482138 [TSF] nsTextFrame has to draw composition string by TIP specified style r+sr=roc
content/base/public/nsISelectionPrivate.idl
content/events/public/nsIPrivateTextRange.h
content/events/src/nsDOMTextEvent.cpp
content/events/src/nsPrivateTextRange.cpp
content/events/src/nsPrivateTextRange.h
editor/libeditor/base/IMETextTxn.cpp
layout/generic/nsFrameSelection.h
layout/generic/nsSelection.cpp
layout/generic/nsTextFrameThebes.cpp
widget/public/nsGUIEvent.h
widget/src/windows/nsTextStore.cpp
--- a/content/base/public/nsISelectionPrivate.idl
+++ b/content/base/public/nsISelectionPrivate.idl
@@ -42,25 +42,27 @@
 interface nsIDOMRange;
 interface nsISelectionListener;
 interface nsIContent;
 
 %{C++
 class nsFrameSelection;
 class nsIFrame;
 class nsIPresShell;
+struct nsTextRangeStyle;
 struct nsPoint;
 %}
 
 [ptr] native nsFrameSelection(nsFrameSelection);
 [ptr] native nsIFrame(nsIFrame);
 [ptr] native nsIPresShell(nsIPresShell);
+[ref] native constTextRangeStyleRef(const nsTextRangeStyle);
 [ref] native nsPointRef(nsPoint);
 
-[scriptable, uuid(b416c692-eeb8-4186-addd-c444e81b68e5)]
+[scriptable, uuid(98552206-ad7a-4d2d-8ce3-b6fa2389298b)]
 interface nsISelectionPrivate : nsISupports
  {
     const short ENDOFPRECEDINGLINE=0;
     const short STARTOFNEXTLINE=1;
     
     attribute boolean interlinePosition;
 
     /* startBatchChanges
@@ -113,10 +115,18 @@ interface nsISelectionPrivate : nsISuppo
     [noscript] void getCachedFrameOffset(in nsIFrame aFrame, in PRInt32 inOffset, in nsPointRef aPoint);
 
     /* getFrameSelection
      * Returnes a reference to the frame selection associated with this selection 
      */
     [noscript] nsFrameSelection getFrameSelection();
 
     [noscript] void setAncestorLimiter(in nsIContent aContent);
+
+    /**
+     * Set the painting style for the range. The range must be a range in
+     * the selection. The textRangeStyle will be used by text frame
+     * when it is painting the selection.
+     */
+    [noscript] void setTextRangeStyle(in nsIDOMRange range,
+                      in constTextRangeStyleRef textRangeStyle);
 };
 
--- a/content/events/public/nsIPrivateTextRange.h
+++ b/content/events/public/nsIPrivateTextRange.h
@@ -36,20 +36,22 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsIPrivateTextRange_h__
 #define nsIPrivateTextRange_h__
 
 #include "nsISupports.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
+#include "nsGUIEvent.h"
 
+// {B13C4BD8-CB9F-4763-A020-E99ABC9C2803}
 #define NS_IPRIVATETEXTRANGE_IID \
-{0xb471ab41, 0x2a79, 0x11d3, \
-{ 0x9e, 0xa4, 0x0, 0x60, 0x8, 0x9f, 0xe5, 0x9b } } 
+{ 0xb13c4bd8, 0xcb9f, 0x4763, \
+{ 0xa0, 0x20, 0xe9, 0x9a, 0xbc, 0x9c, 0x28, 0x3 } }
 
 class nsIPrivateTextRange : public nsISupports {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IPRIVATETEXTRANGE_IID)
 
   // Note that the range array may not specify a caret position; in that
   // case there will be no range of type TEXTRANGE_CARETPOSITION in the array.
   enum {
@@ -63,16 +65,18 @@ public:
   NS_IMETHOD    GetRangeStart(PRUint16* aRangeStart)=0;
   NS_IMETHOD    SetRangeStart(PRUint16 aRangeStart)=0;
 
   NS_IMETHOD    GetRangeEnd(PRUint16* aRangeEnd)=0;
   NS_IMETHOD    SetRangeEnd(PRUint16 aRangeEnd)=0;
 
   NS_IMETHOD    GetRangeType(PRUint16* aRangeType)=0;
   NS_IMETHOD    SetRangeType(PRUint16 aRangeType)=0;
+
+  NS_IMETHOD    GetRangeStyle(nsTextRangeStyle* aTextRangeStyle)=0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIPrivateTextRange, NS_IPRIVATETEXTRANGE_IID)
 
 #define NS_IPRIVATETEXTRANGELIST_IID \
 {0xb5a04b19, 0xed33, 0x4cd0, \
 {0x82, 0xa8, 0xb7, 0x00, 0x83, 0xef, 0xc4, 0x91}}
 
--- a/content/events/src/nsDOMTextEvent.cpp
+++ b/content/events/src/nsDOMTextEvent.cpp
@@ -68,19 +68,17 @@ nsDOMTextEvent::nsDOMTextEvent(nsPresCon
   // isn't persistent
   //
   mTextRange = new nsPrivateTextRangeList(te->rangeCount);
   if (mTextRange) {
     PRUint16 i;
 
     for(i = 0; i < te->rangeCount; i++) {
       nsRefPtr<nsPrivateTextRange> tempPrivateTextRange = new
-        nsPrivateTextRange(te->rangeArray[i].mStartOffset,
-                           te->rangeArray[i].mEndOffset,
-                           te->rangeArray[i].mRangeType);
+        nsPrivateTextRange(te->rangeArray[i]);
 
       if (tempPrivateTextRange) {
         mTextRange->AppendTextRange(tempPrivateTextRange);
       }
     }
   }
 }
 
--- a/content/events/src/nsPrivateTextRange.cpp
+++ b/content/events/src/nsPrivateTextRange.cpp
@@ -33,20 +33,21 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsPrivateTextRange.h"
 
 
-nsPrivateTextRange::nsPrivateTextRange(PRUint16 aRangeStart, PRUint16 aRangeEnd, PRUint16 aRangeType)
-:	mRangeStart(aRangeStart),
-	mRangeEnd(aRangeEnd),
-	mRangeType(aRangeType)
+nsPrivateTextRange::nsPrivateTextRange(const nsTextRange &aTextRange)
+  : mRangeStart(PRUint16(aTextRange.mStartOffset)),
+    mRangeEnd(PRUint16(aTextRange.mEndOffset)),
+    mRangeType(PRUint16(aTextRange.mRangeType)),
+    mRangeStyle(aTextRange.mRangeStyle)
 {
 }
 
 nsPrivateTextRange::~nsPrivateTextRange(void)
 {
 }
 
 NS_IMPL_ISUPPORTS1(nsPrivateTextRange, nsIPrivateTextRange)
@@ -82,16 +83,23 @@ NS_METHOD nsPrivateTextRange::GetRangeTy
 }
 
 NS_METHOD nsPrivateTextRange::SetRangeType(PRUint16 aRangeType)
 {
 	mRangeType = aRangeType;
 	return NS_OK;
 }
 
+NS_METHOD nsPrivateTextRange::GetRangeStyle(nsTextRangeStyle* aTextRangeStyle)
+{
+	NS_ENSURE_ARG_POINTER(aTextRangeStyle);
+	*aTextRangeStyle = mRangeStyle;
+	return NS_OK;
+}
+
 NS_IMPL_ISUPPORTS1(nsPrivateTextRangeList, nsIPrivateTextRangeList)
 
 void nsPrivateTextRangeList::AppendTextRange(nsRefPtr<nsPrivateTextRange>& aRange)
 {
 	mList.AppendElement(aRange);
 }
 
 NS_METHOD_(PRUint16) nsPrivateTextRangeList::GetLength()
--- a/content/events/src/nsPrivateTextRange.h
+++ b/content/events/src/nsPrivateTextRange.h
@@ -42,33 +42,36 @@
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 
 class nsPrivateTextRange : public nsIPrivateTextRange 
 {
 	NS_DECL_ISUPPORTS
 public:
 
-	nsPrivateTextRange(PRUint16 aRangeStart, PRUint16 aRangeEnd, PRUint16 aRangeType);
+	nsPrivateTextRange(const nsTextRange &aTextRange);
 	virtual ~nsPrivateTextRange(void);
 
 	NS_IMETHOD    GetRangeStart(PRUint16* aRangeStart);
 	NS_IMETHOD    SetRangeStart(PRUint16 aRangeStart);
 
 	NS_IMETHOD    GetRangeEnd(PRUint16* aRangeEnd);
 	NS_IMETHOD    SetRangeEnd(PRUint16 aRangeEnd);
 
 	NS_IMETHOD    GetRangeType(PRUint16* aRangeType);
 	NS_IMETHOD    SetRangeType(PRUint16 aRangeType);
 
+	NS_IMETHOD    GetRangeStyle(nsTextRangeStyle* aRangeStyle);
+
 protected:
 
 	PRUint16	mRangeStart;
 	PRUint16	mRangeEnd;
 	PRUint16	mRangeType;
+	nsTextRangeStyle mRangeStyle;
 };
 
 class nsPrivateTextRangeList: public nsIPrivateTextRangeList 
 {
 	NS_DECL_ISUPPORTS
 public:
 	nsPrivateTextRangeList(PRUint16 aLength) : mList(aLength) {}
 
--- a/editor/libeditor/base/IMETextTxn.cpp
+++ b/editor/libeditor/base/IMETextTxn.cpp
@@ -332,31 +332,48 @@ NS_IMETHODIMP IMETextTxn::CollapseTextSe
                 break;
 
              nsCOMPtr<nsIDOMRange> newRange = do_CreateInstance(
                                       "@mozilla.org/content/range;1", &result);
              NS_ASSERTION(NS_SUCCEEDED(result), "Cannot create new nsIDOMRange");
              if(NS_FAILED(result))
                 break;
 
-             newRange->SetStart(mElement,mOffset+selectionStart);
+             result = newRange->SetStart(mElement,mOffset+selectionStart);
              NS_ASSERTION(NS_SUCCEEDED(result), "Cannot SetStart");
              if(NS_FAILED(result))
                 break;
 
-             newRange->SetEnd(mElement,mOffset+selectionEnd);
+             result = newRange->SetEnd(mElement,mOffset+selectionEnd);
              NS_ASSERTION(NS_SUCCEEDED(result), "Cannot SetEnd");
              if(NS_FAILED(result))
                 break;
 
-             imeSel->AddRange(newRange);
+             result = imeSel->AddRange(newRange);
              NS_ASSERTION(NS_SUCCEEDED(result), "Cannot AddRange");
              if(NS_FAILED(result))
                 break;
 
+             nsCOMPtr<nsISelectionPrivate> imeSelPriv(
+                                             do_QueryInterface(imeSel));
+             if (imeSelPriv) {
+               nsTextRangeStyle textRangeStyle;
+               result = textRange->GetRangeStyle(&textRangeStyle);
+               NS_ASSERTION(NS_SUCCEEDED(result),
+                            "nsIPrivateTextRange::GetRangeStyle failed");
+               if (NS_FAILED(result))
+                 break;
+               result = imeSelPriv->SetTextRangeStyle(newRange, textRangeStyle);
+               NS_ASSERTION(NS_SUCCEEDED(result),
+                 "nsISelectionPrivate::SetTextRangeStyle failed");
+               if (NS_FAILED(result))
+                 break;
+             } else {
+               NS_WARNING("IME selection doesn't have nsISelectionPrivate");
+             }
           } // if GetRangeEnd
         } // for textRangeListLength
         if(! setCaret) {
           // set cursor
           result = selection->Collapse(mElement,mOffset+mStringToInsert.Length());
           NS_ASSERTION(NS_SUCCEEDED(result), "Cannot Collapse");
         }
         result = selPriv->EndBatchChanges();
--- a/layout/generic/nsFrameSelection.h
+++ b/layout/generic/nsFrameSelection.h
@@ -44,20 +44,20 @@
 #include "nsIScrollableViewProvider.h"
 #include "nsITableLayout.h"
 #include "nsITableCellLayout.h"
 #include "nsIDOMElement.h"
 #include "nsGUIEvent.h"
 #include "nsIRange.h"
 
 // IID for the nsFrameSelection interface
-// d78edc5a-28d0-48f0-8abb-1597b1591556
+// 0ea74459-e3f9-48b0-8aa4-5dfef53bf1f7
 #define NS_FRAME_SELECTION_IID      \
-{ 0xd78edc5a, 0x28d0, 0x48f0, \
-  { 0x8a, 0xbb, 0x15, 0x97, 0xb1, 0x59, 0x15, 0x56 } }
+{ 0xea74459, 0xe3f9, 0x48b0, \
+  { 0x8a, 0xa4, 0x5d, 0xfe, 0xf5, 0x3b, 0xf1, 0xf7 } }
 
 #ifdef IBMBIDI // Constant for Set/Get CaretBidiLevel
 #define BIDI_LEVEL_UNDEFINED 0x80
 #endif
 
 //----------------------------------------------------------------------
 
 // Selection interface
@@ -70,16 +70,17 @@ struct SelectionDetails
   }
   ~SelectionDetails() {
     MOZ_COUNT_DTOR(SelectionDetails);
   }
 #endif
   PRInt32 mStart;
   PRInt32 mEnd;
   SelectionType mType;
+  nsTextRangeStyle mTextRangeStyle;
   SelectionDetails *mNext;
 };
 
 class nsIPresShell;
 
 enum EWordMovementType { eStartWord, eEndWord, eDefaultBehavior };
 
 /** PeekOffsetStruct is used to group various arguments (both input and output)
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -159,16 +159,17 @@ struct CachedOffsetForFrame {
 
 struct RangeData
 {
   RangeData(nsIRange* aRange, PRInt32 aEndIndex) :
     mRange(aRange), mEndIndex(aEndIndex) {}
 
   nsCOMPtr<nsIRange> mRange;
   PRInt32 mEndIndex; // index into mRangeEndings of this item
+  nsTextRangeStyle mTextRangeStyle;
 };
 
 static RangeData sEmptyData(nsnull, 0);
 
 // Note, the ownership of nsTypedSelection depends on which way the object is
 // created. When nsFrameSelection has created nsTypedSelection,
 // addreffing/releasing nsTypedSelection object is aggregated to
 // nsFrameSelection. Otherwise normal addref/release is used.
@@ -352,16 +353,17 @@ private:
                                PRBool aUseBeginning);
   PRInt32 FindRangeGivenPoint(nsINode* aBeginNode, PRInt32 aBeginOffset,
                               nsINode* aEndNode, PRInt32 aEndOffset,
                               PRInt32 aStartSearchingHere);
   nsresult GetRangesForIntervalCOMArray(nsINode* aBeginNode, PRInt32 aBeginOffset,
                                         nsINode* aEndNode, PRInt32 aEndOffset,
                                         PRBool aAllowAdjacent,
                                         nsCOMArray<nsIRange>* aRanges);
+  RangeData* FindRangeData(nsIDOMRange* aRange);
 
   nsTArray<RangeData> mRanges;
   nsTArray<PRInt32> mRangeEndings;    // references info mRanges
   nsCOMPtr<nsIRange> mAnchorFocusRange;
   nsRefPtr<nsFrameSelection> mFrameSelection;
   nsWeakPtr mPresShellWeak;
   nsRefPtr<nsAutoScrollTimer> mAutoScrollTimer;
   nsCOMArray<nsISelectionListener> mSelectionListeners;
@@ -4537,16 +4539,20 @@ nsTypedSelection::LookUpSelection(nsICon
     SelectionDetails* details = new SelectionDetails;
     if (! details)
       return NS_ERROR_OUT_OF_MEMORY;
 
     details->mNext = *aReturnDetails;
     details->mStart = start;
     details->mEnd = end;
     details->mType = aType;
+    RangeData *rd = FindRangeData(range);
+    if (rd) {
+      details->mTextRangeStyle = rd->mTextRangeStyle;
+    }
     *aReturnDetails = details;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsTypedSelection::Repaint(nsPresContext* aPresContext)
 {
@@ -4642,16 +4648,39 @@ nsTypedSelection::GetFrameSelection(nsFr
 NS_IMETHODIMP
 nsTypedSelection::SetAncestorLimiter(nsIContent *aContent)
 {
   if (mFrameSelection)
     mFrameSelection->SetAncestorLimiter(aContent);
   return NS_OK;
 }
 
+RangeData*
+nsTypedSelection::FindRangeData(nsIDOMRange* aRange)
+{
+  NS_ENSURE_TRUE(aRange, nsnull);
+  for (PRUint32 i = 0; i < mRanges.Length(); i++) {
+    if (mRanges[i].mRange == aRange)
+      return &mRanges[i];
+  }
+  return nsnull;
+}
+
+NS_IMETHODIMP
+nsTypedSelection::SetTextRangeStyle(nsIDOMRange *aRange,
+                                    const nsTextRangeStyle &aTextRangeStyle)
+{
+  NS_ENSURE_ARG_POINTER(aRange);
+  RangeData *rd = FindRangeData(aRange);
+  if (rd) {
+    rd->mTextRangeStyle = aTextRangeStyle;
+  }
+  return NS_OK;
+}
+
 nsresult
 nsTypedSelection::StartAutoScrollTimer(nsPresContext *aPresContext,
                                        nsIView *aView,
                                        nsPoint& aPoint,
                                        PRUint32 aDelay)
 {
   NS_PRECONDITION(aView, "Need a view");
 
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -4085,96 +4085,167 @@ nsTextFrame::PaintTextDecorations(gfxCon
 // Make sure this stays in sync with DrawSelectionDecorations below
 static const SelectionType SelectionTypesWithDecorations =
   nsISelectionController::SELECTION_SPELLCHECK |
   nsISelectionController::SELECTION_IME_RAWINPUT |
   nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT |
   nsISelectionController::SELECTION_IME_CONVERTEDTEXT |
   nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT;
 
+static PRUint8
+GetTextDecorationStyle(const nsTextRangeStyle &aRangeStyle)
+{
+  NS_PRECONDITION(aRangeStyle.IsLineStyleDefined(),
+                  "aRangeStyle.mLineStyle have to be defined");
+  switch (aRangeStyle.mLineStyle) {
+    case nsTextRangeStyle::LINESTYLE_NONE:
+      return nsCSSRendering::DECORATION_STYLE_NONE;
+    case nsTextRangeStyle::LINESTYLE_SOLID:
+      return nsCSSRendering::DECORATION_STYLE_SOLID;
+    case nsTextRangeStyle::LINESTYLE_DOTTED:
+      return nsCSSRendering::DECORATION_STYLE_DOTTED;
+    case nsTextRangeStyle::LINESTYLE_DASHED:
+      return nsCSSRendering::DECORATION_STYLE_DASHED;
+    case nsTextRangeStyle::LINESTYLE_DOUBLE:
+      return nsCSSRendering::DECORATION_STYLE_DOUBLE;
+    case nsTextRangeStyle::LINESTYLE_WAVY:
+      return nsCSSRendering::DECORATION_STYLE_WAVY;
+    default:
+      NS_WARNING("Requested underline style is not valid");
+      return nsCSSRendering::DECORATION_STYLE_SOLID;
+  }
+}
+
 /**
  * This, plus SelectionTypesWithDecorations, encapsulates all knowledge about
  * drawing text decoration for selections.
  */
 static void DrawSelectionDecorations(gfxContext* aContext, SelectionType aType,
-    nsTextPaintStyle& aTextPaintStyle, const gfxPoint& aPt, gfxFloat aWidth,
+    nsTextPaintStyle& aTextPaintStyle,
+    const nsTextRangeStyle &aRangeStyle,
+    const gfxPoint& aPt, gfxFloat aWidth,
     gfxFloat aAscent, const gfxFont::Metrics& aFontMetrics)
 {
   gfxPoint pt(aPt);
   gfxSize size(aWidth, aFontMetrics.underlineSize);
   gfxFloat descentLimit = aFontMetrics.maxDescent;
 
+  float relativeSize;
+  PRUint8 style;
+  nscolor color;
+  PRInt32 index =
+    nsTextPaintStyle::GetUnderlineStyleIndexForSelectionType(aType);
+  PRBool weDefineSelectionUnderline =
+    aTextPaintStyle.GetSelectionUnderlineForPaint(index, &color,
+                                                  &relativeSize, &style);
+
   switch (aType) {
     case nsISelectionController::SELECTION_IME_RAWINPUT:
     case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT:
     case nsISelectionController::SELECTION_IME_CONVERTEDTEXT:
-    case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT:
+    case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT: {
       // IME decoration lines should not be drawn on the both ends, i.e., we
       // need to cut both edges of the decoration lines.  Because same style
       // IME selections can adjoin, but the users need to be able to know
       // where are the boundaries of the selections.
       //
       //  X: underline
       //
       //     IME selection #1        IME selection #2      IME selection #3
       //  |                     |                      |                    
       //  | XXXXXXXXXXXXXXXXXXX | XXXXXXXXXXXXXXXXXXXX | XXXXXXXXXXXXXXXXXXX
       //  +---------------------+----------------------+--------------------
       //   ^                   ^ ^                    ^ ^
       //  gap                  gap                    gap
       pt.x += 1.0;
       size.width -= 2.0;
-    case nsISelectionController::SELECTION_SPELLCHECK: {
-      float relativeSize;
-      PRUint8 style;
-      nscolor color;
-      PRInt32 index =
-        nsTextPaintStyle::GetUnderlineStyleIndexForSelectionType(aType);
-      if (aTextPaintStyle.GetSelectionUnderlineForPaint(index, &color,
-                                                        &relativeSize,
-                                                        &style)) {
-        size.height *= relativeSize;
-        nsCSSRendering::PaintDecorationLine(
-          aContext, color, pt, size, aAscent, aFontMetrics.underlineOffset,
-          NS_STYLE_TEXT_DECORATION_UNDERLINE, style, descentLimit);
+      if (aRangeStyle.IsDefined()) {
+        // If IME defines the style, that should override our definition.
+        if (aRangeStyle.IsLineStyleDefined()) {
+          if (aRangeStyle.mLineStyle == nsTextRangeStyle::LINESTYLE_NONE) {
+            return;
+          }
+          style = GetTextDecorationStyle(aRangeStyle);
+          relativeSize = aRangeStyle.mIsBoldLine ? 2.0f : 1.0f;
+        } else if (!weDefineSelectionUnderline) {
+          // There is no underline style definition.
+          return;
+        }
+        if (aRangeStyle.IsUnderlineColorDefined()) {
+          color = aRangeStyle.mUnderlineColor;
+        } else if (aRangeStyle.IsForegroundColorDefined()) {
+          color = aRangeStyle.mForegroundColor;
+        } else {
+          NS_ASSERTION(!aRangeStyle.IsBackgroundColorDefined(),
+                       "Only the background color is defined");
+          color = aTextPaintStyle.GetTextColor();
+        }
+      } else if (!weDefineSelectionUnderline) {
+        // IME doesn't specify the selection style and we don't define selection
+        // underline.
+        return;
       }
       break;
     }
+    case nsISelectionController::SELECTION_SPELLCHECK:
+      if (!weDefineSelectionUnderline)
+        return;
+      break;
     default:
       NS_WARNING("Requested selection decorations when there aren't any");
-      break;
-  }
+      return;
+  }
+  size.height *= relativeSize;
+  nsCSSRendering::PaintDecorationLine(
+    aContext, color, pt, size, aAscent, aFontMetrics.underlineOffset,
+    NS_STYLE_TEXT_DECORATION_UNDERLINE, style, descentLimit);
 }
 
 /**
  * This function encapsulates all knowledge of how selections affect foreground
  * and background colors.
  * @return true if the selection affects colors, false otherwise
  * @param aForeground the foreground color to use
  * @param aBackground the background color to use, or RGBA(0,0,0,0) if no
  * background should be painted
  */
-static PRBool GetSelectionTextColors(SelectionType aType, nsTextPaintStyle& aTextPaintStyle,
+static PRBool GetSelectionTextColors(SelectionType aType,
+                                     nsTextPaintStyle& aTextPaintStyle,
+                                     const nsTextRangeStyle &aRangeStyle,
                                      nscolor* aForeground, nscolor* aBackground)
 {
   switch (aType) {
     case nsISelectionController::SELECTION_NORMAL:
       return aTextPaintStyle.GetSelectionColors(aForeground, aBackground);
     case nsISelectionController::SELECTION_FIND:
       aTextPaintStyle.GetHighlightColors(aForeground, aBackground);
       return PR_TRUE;
     case nsISelectionController::SELECTION_IME_RAWINPUT:
     case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT:
     case nsISelectionController::SELECTION_IME_CONVERTEDTEXT:
     case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT:
+      if (aRangeStyle.IsDefined()) {
+        *aForeground = aTextPaintStyle.GetTextColor();
+        *aBackground = NS_RGBA(0,0,0,0);
+        if (!aRangeStyle.IsForegroundColorDefined() &&
+            !aRangeStyle.IsBackgroundColorDefined()) {
+          return PR_FALSE;
+        }
+        if (aRangeStyle.IsForegroundColorDefined()) {
+          *aForeground = aRangeStyle.mForegroundColor;
+        }
+        if (aRangeStyle.IsBackgroundColorDefined()) {
+          *aBackground = aRangeStyle.mBackgroundColor;
+        }
+        return PR_TRUE;
+      }
       aTextPaintStyle.GetIMESelectionColors(
         nsTextPaintStyle::GetUnderlineStyleIndexForSelectionType(aType),
         aForeground, aBackground);
       return PR_TRUE;
-      
     default:
       *aForeground = aTextPaintStyle.GetTextColor();
       *aBackground = NS_RGBA(0,0,0,0);
       return PR_FALSE;
   }
 }
 
 /**
@@ -4182,79 +4253,88 @@ static PRBool GetSelectionTextColors(Sel
  * observing cluster boundaries, in content order, maintaining the current
  * x-offset as we go, and telling whether the text chunk has a hyphen after
  * it or not. The caller is responsible for actually computing the advance
  * width of each chunk.
  */
 class SelectionIterator {
 public:
   /**
-   * aStart and aLength are in the original string. aSelectionBuffer is
+   * aStart and aLength are in the original string. aSelectionDetails is
    * according to the original string.
    */
-  SelectionIterator(SelectionType* aSelectionBuffer, PRInt32 aStart,
-                    PRInt32 aLength, PropertyProvider& aProvider,
-                    gfxTextRun* aTextRun);
-  
+  SelectionIterator(SelectionDetails** aSelectionDetails,
+                    PRInt32 aStart, PRInt32 aLength,
+                    PropertyProvider& aProvider, gfxTextRun* aTextRun);
+
   /**
    * Returns the next segment of uniformly selected (or not) text.
    * @param aXOffset the offset from the origin of the frame to the start
    * of the text (the left baseline origin for LTR, the right baseline origin
    * for RTL)
    * @param aOffset the transformed string offset of the text for this segment
    * @param aLength the transformed string length of the text for this segment
    * @param aHyphenWidth if a hyphen is to be rendered after the text, the
    * width of the hyphen, otherwise zero
    * @param aType the selection type for this segment
+   * @param aStyle the selection style for this segment
    * @return false if there are no more segments
    */
   PRBool GetNextSegment(gfxFloat* aXOffset, PRUint32* aOffset, PRUint32* aLength,
-                        gfxFloat* aHyphenWidth, SelectionType* aType);
+                        gfxFloat* aHyphenWidth, SelectionType* aType,
+                        nsTextRangeStyle* aStyle);
   void UpdateWithAdvance(gfxFloat aAdvance) {
     mXOffset += aAdvance*mTextRun->GetDirection();
   }
 
 private:
-  SelectionType*          mSelectionBuffer;
+  SelectionDetails**      mSelectionDetails;
   PropertyProvider&       mProvider;
   gfxTextRun*             mTextRun;
   gfxSkipCharsIterator    mIterator;
   PRInt32                 mOriginalStart;
   PRInt32                 mOriginalEnd;
   gfxFloat                mXOffset;
 };
 
-SelectionIterator::SelectionIterator(SelectionType* aSelectionBuffer,
+SelectionIterator::SelectionIterator(SelectionDetails** aSelectionDetails,
     PRInt32 aStart, PRInt32 aLength, PropertyProvider& aProvider,
     gfxTextRun* aTextRun)
-  : mSelectionBuffer(aSelectionBuffer), mProvider(aProvider),
+  : mSelectionDetails(aSelectionDetails), mProvider(aProvider),
     mTextRun(aTextRun), mIterator(aProvider.GetStart()),
     mOriginalStart(aStart), mOriginalEnd(aStart + aLength),
     mXOffset(mTextRun->IsRightToLeft() ? aProvider.GetFrame()->GetSize().width : 0)
 {
   mIterator.SetOriginalOffset(aStart);
 }
 
 PRBool SelectionIterator::GetNextSegment(gfxFloat* aXOffset,
-    PRUint32* aOffset, PRUint32* aLength, gfxFloat* aHyphenWidth, SelectionType* aType)
+    PRUint32* aOffset, PRUint32* aLength, gfxFloat* aHyphenWidth,
+    SelectionType* aType, nsTextRangeStyle* aStyle)
 {
   if (mIterator.GetOriginalOffset() >= mOriginalEnd)
     return PR_FALSE;
   
   // save offset into transformed string now
   PRUint32 runOffset = mIterator.GetSkippedOffset();
   
   PRInt32 index = mIterator.GetOriginalOffset() - mOriginalStart;
-  SelectionType type = mSelectionBuffer[index];
+  SelectionDetails* sdptr = mSelectionDetails[index];
+  SelectionType type =
+    sdptr ? sdptr->mType : nsISelectionController::SELECTION_NONE;
+  nsTextRangeStyle style;
+  if (sdptr) {
+    style = sdptr->mTextRangeStyle;
+  }
   for (++index; mOriginalStart + index < mOriginalEnd; ++index) {
-    if (mSelectionBuffer[index] != type)
+    if (sdptr != mSelectionDetails[index])
       break;
   }
   mIterator.SetOriginalOffset(index + mOriginalStart);
-  
+
   // Advance to the next cluster boundary
   while (mIterator.GetOriginalOffset() < mOriginalEnd &&
          !mIterator.IsOriginalCharSkipped() &&
          !mTextRun->IsClusterStart(mIterator.GetSkippedOffset())) {
     mIterator.AdvanceOriginal(1);
   }
 
   PRBool haveHyphenBreak =
@@ -4262,16 +4342,17 @@ PRBool SelectionIterator::GetNextSegment
   *aOffset = runOffset;
   *aLength = mIterator.GetSkippedOffset() - runOffset;
   *aXOffset = mXOffset;
   *aHyphenWidth = 0;
   if (mIterator.GetOriginalOffset() == mOriginalEnd && haveHyphenBreak) {
     *aHyphenWidth = mProvider.GetHyphenWidth();
   }
   *aType = type;
+  *aStyle = style;
   return PR_TRUE;
 }
 
 static void
 AddHyphenToMetrics(nsTextFrame* aTextFrame, gfxTextRun* aBaseTextRun,
                    gfxTextRun::Metrics* aMetrics,
                    gfxFont::BoundingBoxType aBoundingBoxType,
                    gfxContext* aContext)
@@ -4357,83 +4438,89 @@ nsTextFrame::PaintTextWithSelectionColor
     const gfxPoint& aTextBaselinePt, const gfxRect& aDirtyRect,
     PropertyProvider& aProvider, nsTextPaintStyle& aTextPaintStyle,
     SelectionDetails* aDetails, SelectionType* aAllTypes)
 {
   PRInt32 contentOffset = aProvider.GetStart().GetOriginalOffset();
   PRInt32 contentLength = aProvider.GetOriginalLength();
 
   // Figure out which selections control the colors to use for each character.
-  nsAutoTArray<SelectionType,BIG_TEXT_NODE_SIZE> prevailingSelectionsBuffer;
+  nsAutoTArray<SelectionDetails*,BIG_TEXT_NODE_SIZE> prevailingSelectionsBuffer;
   if (!prevailingSelectionsBuffer.AppendElements(contentLength))
     return;
-  SelectionType* prevailingSelections = prevailingSelectionsBuffer.Elements();
+  SelectionDetails** prevailingSelections = prevailingSelectionsBuffer.Elements();
+
   PRInt32 i;
   SelectionType allTypes = 0;
   for (i = 0; i < contentLength; ++i) {
-    prevailingSelections[i] = nsISelectionController::SELECTION_NONE;
+    prevailingSelections[i] = nsnull;
   }
 
   SelectionDetails *sdptr = aDetails;
   PRBool anyBackgrounds = PR_FALSE;
   while (sdptr) {
     PRInt32 start = PR_MAX(0, sdptr->mStart - contentOffset);
     PRInt32 end = PR_MIN(contentLength, sdptr->mEnd - contentOffset);
     SelectionType type = sdptr->mType;
     if (start < end) {
       allTypes |= type;
       // Ignore selections that don't set colors
       nscolor foreground, background;
-      if (GetSelectionTextColors(type, aTextPaintStyle, &foreground, &background)) {
+      if (GetSelectionTextColors(type, aTextPaintStyle, sdptr->mTextRangeStyle,
+                                 &foreground, &background)) {
         if (NS_GET_A(background) > 0) {
           anyBackgrounds = PR_TRUE;
         }
         for (i = start; i < end; ++i) {
-          PRInt16 currentPrevailingSelection = prevailingSelections[i];
           // Favour normal selection over IME selections
-          if (currentPrevailingSelection == nsISelectionController::SELECTION_NONE ||
-              type < currentPrevailingSelection) {
-            prevailingSelections[i] = type;
+          if (!prevailingSelections[i] ||
+              type < prevailingSelections[i]->mType) {
+            prevailingSelections[i] = sdptr;
           }
         }
       }
     }
     sdptr = sdptr->mNext;
   }
   *aAllTypes = allTypes;
 
   gfxFloat xOffset, hyphenWidth;
   PRUint32 offset, length; // in transformed string
   SelectionType type;
+  nsTextRangeStyle rangeStyle;
   // Draw background colors
   if (anyBackgrounds) {
     SelectionIterator iterator(prevailingSelections, contentOffset, contentLength,
                                aProvider, mTextRun);
-    while (iterator.GetNextSegment(&xOffset, &offset, &length, &hyphenWidth, &type)) {
+    while (iterator.GetNextSegment(&xOffset, &offset, &length, &hyphenWidth,
+                                   &type, &rangeStyle)) {
       nscolor foreground, background;
-      GetSelectionTextColors(type, aTextPaintStyle, &foreground, &background);
+      GetSelectionTextColors(type, aTextPaintStyle, rangeStyle,
+                             &foreground, &background);
       // Draw background color
       gfxFloat advance = hyphenWidth +
         mTextRun->GetAdvanceWidth(offset, length, &aProvider);
       if (NS_GET_A(background) > 0) {
         gfxFloat x = xOffset - (mTextRun->IsRightToLeft() ? advance : 0);
         FillClippedRect(aCtx, aTextPaintStyle.PresContext(),
                         background, aDirtyRect,
                         gfxRect(aFramePt.x + x, aFramePt.y, advance, GetSize().height));
       }
       iterator.UpdateWithAdvance(advance);
     }
   }
   
   // Draw text
   SelectionIterator iterator(prevailingSelections, contentOffset, contentLength,
                              aProvider, mTextRun);
-  while (iterator.GetNextSegment(&xOffset, &offset, &length, &hyphenWidth, &type)) {
+  while (iterator.GetNextSegment(&xOffset, &offset, &length, &hyphenWidth,
+                                 &type, &rangeStyle)) {
     nscolor foreground, background;
-    GetSelectionTextColors(type, aTextPaintStyle, &foreground, &background);
+    GetSelectionTextColors(type, aTextPaintStyle, rangeStyle,
+                           &foreground, &background);
     // Draw text segment
     aCtx->SetColor(gfxRGBA(foreground));
     gfxFloat advance;
 
     DrawText(aCtx, gfxPoint(aFramePt.x + xOffset, aTextBaselinePt.y),
              offset, length, &aDirtyRect, &aProvider,
              advance, hyphenWidth > 0);
     if (hyphenWidth) {
@@ -4448,34 +4535,33 @@ nsTextFrame::PaintTextSelectionDecoratio
     const gfxPoint& aFramePt,
     const gfxPoint& aTextBaselinePt, const gfxRect& aDirtyRect,
     PropertyProvider& aProvider, nsTextPaintStyle& aTextPaintStyle,
     SelectionDetails* aDetails, SelectionType aSelectionType)
 {
   PRInt32 contentOffset = aProvider.GetStart().GetOriginalOffset();
   PRInt32 contentLength = aProvider.GetOriginalLength();
 
-  // Figure out which characters will be decorated for this selection. Here
-  // we just fill the buffer with either SELECTION_NONE or aSelectionType.
-  nsAutoTArray<SelectionType,BIG_TEXT_NODE_SIZE> selectedCharsBuffer;
+  // Figure out which characters will be decorated for this selection.
+  nsAutoTArray<SelectionDetails*, BIG_TEXT_NODE_SIZE> selectedCharsBuffer;
   if (!selectedCharsBuffer.AppendElements(contentLength))
     return;
-  SelectionType* selectedChars = selectedCharsBuffer.Elements();
+  SelectionDetails** selectedChars = selectedCharsBuffer.Elements();
   PRInt32 i;
   for (i = 0; i < contentLength; ++i) {
-    selectedChars[i] = nsISelectionController::SELECTION_NONE;
+    selectedChars[i] = nsnull;
   }
 
   SelectionDetails *sdptr = aDetails;
   while (sdptr) {
     if (sdptr->mType == aSelectionType) {
       PRInt32 start = PR_MAX(0, sdptr->mStart - contentOffset);
       PRInt32 end = PR_MIN(contentLength, sdptr->mEnd - contentOffset);
       for (i = start; i < end; ++i) {
-        selectedChars[i] = aSelectionType;
+        selectedChars[i] = sdptr;
       }
     }
     sdptr = sdptr->mNext;
   }
 
   gfxFont* firstFont = aProvider.GetFontGroup()->GetFontAt(0);
   if (!firstFont)
     return; // OOM
@@ -4486,24 +4572,27 @@ nsTextFrame::PaintTextSelectionDecoratio
   SelectionIterator iterator(selectedChars, contentOffset, contentLength,
                              aProvider, mTextRun);
   gfxFloat xOffset, hyphenWidth;
   PRUint32 offset, length;
   PRInt32 app = aTextPaintStyle.PresContext()->AppUnitsPerDevPixel();
   // XXX aTextBaselinePt is in AppUnits, shouldn't it be nsFloatPoint?
   gfxPoint pt(0.0, (aTextBaselinePt.y - mAscent) / app);
   SelectionType type;
-  while (iterator.GetNextSegment(&xOffset, &offset, &length, &hyphenWidth, &type)) {
+  nsTextRangeStyle selectedStyle;
+  while (iterator.GetNextSegment(&xOffset, &offset, &length, &hyphenWidth,
+                                 &type, &selectedStyle)) {
     gfxFloat advance = hyphenWidth +
       mTextRun->GetAdvanceWidth(offset, length, &aProvider);
     if (type == aSelectionType) {
       pt.x = (aFramePt.x + xOffset -
              (mTextRun->IsRightToLeft() ? advance : 0)) / app;
       gfxFloat width = PR_ABS(advance) / app;
       DrawSelectionDecorations(aCtx, aSelectionType, aTextPaintStyle,
+                               selectedStyle,
                                pt, width, mAscent / app, decorationMetrics);
     }
     iterator.UpdateWithAdvance(advance);
   }
 }
 
 PRBool
 nsTextFrame::PaintTextWithSelection(gfxContext* aCtx,
@@ -4794,19 +4883,36 @@ nsTextFrame::CombineSelectionUnderlineRe
   for (SelectionDetails *sd = details; sd; sd = sd->mNext) {
     if (sd->mStart == sd->mEnd || !(sd->mType & SelectionTypesWithDecorations))
       continue;
 
     PRUint8 style;
     float relativeSize;
     PRInt32 index =
       nsTextPaintStyle::GetUnderlineStyleIndexForSelectionType(sd->mType);
-    if (!nsTextPaintStyle::GetSelectionUnderline(aPresContext, index, nsnull,
-                                                &relativeSize, &style)) {
-      continue;
+    if (sd->mType == nsISelectionController::SELECTION_SPELLCHECK) {
+      if (!nsTextPaintStyle::GetSelectionUnderline(aPresContext, index, nsnull,
+                                                   &relativeSize, &style)) {
+        continue;
+      }
+    } else {
+      // IME selections
+      nsTextRangeStyle& rangeStyle = sd->mTextRangeStyle;
+      if (rangeStyle.IsDefined()) {
+        if (!rangeStyle.IsLineStyleDefined() ||
+            rangeStyle.mLineStyle == nsTextRangeStyle::LINESTYLE_NONE) {
+          continue;
+        }
+        style = GetTextDecorationStyle(rangeStyle);
+        relativeSize = rangeStyle.mIsBoldLine ? 2.0f : 1.0f;
+      } else if (!nsTextPaintStyle::GetSelectionUnderline(aPresContext, index,
+                                                          nsnull, &relativeSize,
+                                                          &style)) {
+        continue;
+      }
     }
     nsRect decorationArea;
     gfxSize size(aPresContext->AppUnitsToGfxUnits(aRect.width),
                  metrics.underlineSize);
     relativeSize = PR_MAX(relativeSize, 1.0f);
     size.height *= relativeSize;
     decorationArea =
       nsCSSRendering::GetTextDecorationRect(aPresContext, size,
--- a/widget/public/nsGUIEvent.h
+++ b/widget/public/nsGUIEvent.h
@@ -838,26 +838,118 @@ public:
   nsTArray<nsAlternativeCharCode> alternativeCharCodes;
   // indicates whether the event signifies a printable character
   PRBool          isChar;
 };
 
 /**
  * IME Related Events
  */
+ 
+struct nsTextRangeStyle
+{
+  enum {
+    LINESTYLE_NONE   = 0,
+    LINESTYLE_SOLID  = 1,
+    LINESTYLE_DOTTED = 2,
+    LINESTYLE_DASHED = 3,
+    LINESTYLE_DOUBLE = 4,
+    LINESTYLE_WAVY   = 5
+  };
+
+  enum {
+    DEFINED_NONE             = 0x00,
+    DEFINED_LINESTYLE        = 0x01,
+    DEFINED_FOREGROUND_COLOR = 0x02,
+    DEFINED_BACKGROUND_COLOR = 0x04,
+    DEFINED_UNDERLINE_COLOR  = 0x08
+  };
+
+  // Initialize all members, because nsTextRange instances may be compared by
+  // memcomp.
+  nsTextRangeStyle() :
+    mDefinedStyles(DEFINED_NONE), mLineStyle(LINESTYLE_NONE),
+    mIsBoldLine(PR_FALSE), mForegroundColor(NS_RGBA(0, 0, 0, 0)),
+    mBackgroundColor(NS_RGBA(0, 0, 0, 0)), mUnderlineColor(NS_RGBA(0, 0, 0, 0))
+  {
+  }
+
+  PRBool IsDefined() const { return mDefinedStyles != DEFINED_NONE; }
+
+  PRBool IsLineStyleDefined() const
+  {
+    return (mDefinedStyles & DEFINED_LINESTYLE) != 0;
+  }
+
+  PRBool IsForegroundColorDefined() const
+  {
+    return (mDefinedStyles & DEFINED_FOREGROUND_COLOR) != 0;
+  }
+
+  PRBool IsBackgroundColorDefined() const
+  {
+    return (mDefinedStyles & DEFINED_BACKGROUND_COLOR) != 0;
+  }
+
+  PRBool IsUnderlineColorDefined() const
+  {
+    return (mDefinedStyles & DEFINED_UNDERLINE_COLOR) != 0;
+  }
+
+  PRBool Equals(const nsTextRangeStyle& aOther)
+  {
+    if (mDefinedStyles != aOther.mDefinedStyles)
+      return PR_FALSE;
+    if (IsLineStyleDefined() && (mLineStyle != aOther.mLineStyle ||
+                                 !mIsBoldLine != !aOther.mIsBoldLine))
+      return PR_FALSE;
+    if (IsForegroundColorDefined() &&
+        (mForegroundColor != aOther.mForegroundColor))
+      return PR_FALSE;
+    if (IsBackgroundColorDefined() &&
+        (mBackgroundColor != aOther.mBackgroundColor))
+      return PR_FALSE;
+    if (IsUnderlineColorDefined() &&
+        (mUnderlineColor != aOther.mUnderlineColor))
+      return PR_FALSE;
+    return PR_TRUE;
+  }
+
+  PRBool operator !=(const nsTextRangeStyle &aOther)
+  {
+    return !Equals(aOther);
+  }
+
+  PRBool operator ==(const nsTextRangeStyle &aOther)
+  {
+    return Equals(aOther);
+  }
+
+  PRUint8 mDefinedStyles;
+  PRUint8 mLineStyle;        // DEFINED_LINESTYLE
+
+  PRPackedBool mIsBoldLine;  // DEFINED_LINESTYLE
+
+  nscolor mForegroundColor;  // DEFINED_FOREGROUND_COLOR
+  nscolor mBackgroundColor;  // DEFINED_BACKGROUND_COLOR
+  nscolor mUnderlineColor;   // DEFINED_UNDERLINE_COLOR
+};
+
 struct nsTextRange
 {
   nsTextRange()
     : mStartOffset(0), mEndOffset(0), mRangeType(0)
   {
   }
 
   PRUint32 mStartOffset;
   PRUint32 mEndOffset;
   PRUint32 mRangeType;
+
+  nsTextRangeStyle mRangeStyle;
 };
 
 typedef nsTextRange* nsTextRangeArray;
 
 struct nsTextEventReply
 {
   nsTextEventReply()
     : mCursorIsCollapsed(PR_FALSE), mReferenceWidget(nsnull)
--- a/widget/src/windows/nsTextStore.cpp
+++ b/widget/src/windows/nsTextStore.cpp
@@ -616,16 +616,60 @@ nsTextStore::UpdateCompositionExtent(ITf
     PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
            ("TSF: UpdateCompositionExtent, range=%ld-%ld\n",
             compStart, compStart + compLength));
   }
 
   return S_OK;
 }
 
+static PRBool
+GetColor(const TF_DA_COLOR &aTSFColor, nscolor &aResult)
+{
+  switch (aTSFColor.type) {
+    case TF_CT_SYSCOLOR: {
+      DWORD sysColor = ::GetSysColor(aTSFColor.nIndex);
+      aResult = NS_RGB(GetRValue(sysColor), GetGValue(sysColor),
+                       GetBValue(sysColor));
+      return PR_TRUE;
+    }
+    case TF_CT_COLORREF:
+      aResult = NS_RGB(GetRValue(aTSFColor.cr), GetGValue(aTSFColor.cr),
+                       GetBValue(aTSFColor.cr));
+      return PR_TRUE;
+    case TF_CT_NONE:
+    default:
+      return PR_FALSE;
+  }
+}
+
+static PRBool
+GetLineStyle(TF_DA_LINESTYLE aTSFLineStyle, PRUint8 &aTextRangeLineStyle)
+{
+  switch (aTSFLineStyle) {
+    case TF_LS_NONE:
+      aTextRangeLineStyle = nsTextRangeStyle::LINESTYLE_NONE;
+      return PR_TRUE;
+    case TF_LS_SOLID:
+      aTextRangeLineStyle = nsTextRangeStyle::LINESTYLE_SOLID;
+      return PR_TRUE;
+    case TF_LS_DOT:
+      aTextRangeLineStyle = nsTextRangeStyle::LINESTYLE_DOTTED;
+      return PR_TRUE;
+    case TF_LS_DASH:
+      aTextRangeLineStyle = nsTextRangeStyle::LINESTYLE_DASHED;
+      return PR_TRUE;
+    case TF_LS_SQUIGGLE:
+      aTextRangeLineStyle = nsTextRangeStyle::LINESTYLE_WAVY;
+      return PR_TRUE;
+    default:
+      return PR_FALSE;
+  }
+}
+
 HRESULT
 nsTextStore::SendTextEventForCompositionString()
 {
   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
          ("TSF: SendTextEventForCompositionString\n"));
 
   NS_ENSURE_TRUE(mCompositionView, E_FAIL);
 
@@ -672,25 +716,46 @@ nsTextStore::SendTextEventForComposition
 
   nsRefPtr<ITfRange> range;
   while (S_OK == enumRanges->Next(1, getter_AddRefs(range), NULL) && range) {
 
     LONG start = 0, length = 0;
     if (FAILED(GetRangeExtent(range, &start, &length)))
       continue;
 
+    nsTextRange newRange;
     newRange.mStartOffset = PRUint32(start - mCompositionStart);
     // The end of the last range in the array is
     // always kept at the end of composition
     newRange.mEndOffset = mCompositionString.Length();
 
     TF_DISPLAYATTRIBUTE attr;
     hr = GetDisplayAttribute(attrPropetry, range, &attr);
-    newRange.mRangeType =
-      SUCCEEDED(hr) ? GetGeckoSelectionValue(attr) : NS_TEXTRANGE_RAWINPUT;
+    if (FAILED(hr)) {
+      newRange.mRangeType = NS_TEXTRANGE_RAWINPUT;
+    } else {
+      newRange.mRangeType = GetGeckoSelectionValue(attr);
+      if (GetColor(attr.crText, newRange.mRangeStyle.mForegroundColor)) {
+        newRange.mRangeStyle.mDefinedStyles |=
+                               nsTextRangeStyle::DEFINED_FOREGROUND_COLOR;
+      }
+      if (GetColor(attr.crBk, newRange.mRangeStyle.mBackgroundColor)) {
+        newRange.mRangeStyle.mDefinedStyles |=
+                               nsTextRangeStyle::DEFINED_BACKGROUND_COLOR;
+      }
+      if (GetColor(attr.crLine, newRange.mRangeStyle.mUnderlineColor)) {
+        newRange.mRangeStyle.mDefinedStyles |=
+                               nsTextRangeStyle::DEFINED_UNDERLINE_COLOR;
+      }
+      if (GetLineStyle(attr.lsStyle, newRange.mRangeStyle.mLineStyle)) {
+        newRange.mRangeStyle.mDefinedStyles |=
+                               nsTextRangeStyle::DEFINED_LINESTYLE;
+        newRange.mRangeStyle.mIsBoldLine = attr.fBoldLine != 0;
+      }
+    }
 
     nsTextRange& lastRange = textRanges[textRanges.Length() - 1];
     if (lastRange.mStartOffset == newRange.mStartOffset) {
       // Replace range if last range is the same as this one
       // So that ranges don't overlap and confuse the editor
       lastRange = newRange;
     } else {
       lastRange.mEndOffset = newRange.mStartOffset;