Bug 338209 Make spellchecker use thicker wavy underlines instead of dotted underlines r+sr=roc
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 03 Apr 2009 16:26:28 +0900
changeset 26883 8f2fc4d53842579e586cf1a1984459366ead85c8
parent 26882 eeba21944aaa4d74200d20b46b3e226edd233cd5
child 26884 5a4343f645ba6afe3f32bb90ccd4099667903c35
push idunknown
push userunknown
push dateunknown
bugs338209
milestone1.9.2a1pre
Bug 338209 Make spellchecker use thicker wavy underlines instead of dotted underlines r+sr=roc
content/events/src/nsContentEventHandler.cpp
layout/Makefile.in
layout/base/nsCSSRendering.cpp
layout/base/nsCSSRendering.h
layout/base/tests/Makefile.in
layout/base/tests/decoration_line_rendering.js
layout/generic/nsBlockFrame.cpp
layout/generic/nsHTMLContainerFrame.cpp
layout/generic/nsLineLayout.cpp
layout/generic/nsLineLayout.h
layout/generic/nsTextFrame.h
layout/generic/nsTextFrameThebes.cpp
layout/generic/test/Makefile.in
layout/generic/test/frame_selection_underline-ref.xhtml
layout/generic/test/frame_selection_underline.css
layout/generic/test/frame_selection_underline.xhtml
layout/generic/test/test_selection_underline.html
layout/reftests/fonts/Makefile.in
layout/reftests/fonts/mplus/Makefile.in
layout/xul/base/src/nsTextBoxFrame.cpp
widget/public/nsILookAndFeel.h
widget/src/beos/nsLookAndFeel.cpp
widget/src/cocoa/nsLookAndFeel.mm
widget/src/gtk2/nsLookAndFeel.cpp
widget/src/os2/nsLookAndFeel.cpp
widget/src/photon/nsLookAndFeel.cpp
widget/src/qt/nsLookAndFeel.cpp
widget/src/windows/nsLookAndFeel.cpp
widget/src/xpwidgets/nsXPLookAndFeel.cpp
--- a/content/events/src/nsContentEventHandler.cpp
+++ b/content/events/src/nsContentEventHandler.cpp
@@ -83,17 +83,22 @@ nsContentEventHandler::Init(nsQueryConte
   if (mSelection)
     return NS_OK;
 
   aEvent->mSucceeded = PR_FALSE;
 
   if (!mPresShell)
     return NS_ERROR_NOT_AVAILABLE;
 
-  nsresult rv = mPresShell->GetSelectionForCopy(getter_AddRefs(mSelection));
+  // If text frame which has overflowing selection underline is dirty,
+  // we need to flush the pending reflow here.
+  nsresult rv = mPresShell->FlushPendingNotifications(Flush_Layout);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mPresShell->GetSelectionForCopy(getter_AddRefs(mSelection));
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ASSERTION(mSelection,
                "GetSelectionForCopy succeeded, but the result is null");
 
   PRBool isCollapsed;
   rv = mSelection->GetIsCollapsed(&isCollapsed);
   if (NS_FAILED(rv))
     return NS_ERROR_NOT_AVAILABLE;
--- a/layout/Makefile.in
+++ b/layout/Makefile.in
@@ -83,11 +83,12 @@ PARALLEL_DIRS += \
   xul/base/test \
   $(NULL)
 
 TOOL_DIRS      += tools/reftest
 DIRS           += tools/pageloader
 ifndef MOZ_ENABLE_LIBXUL
 TOOL_DIRS	+= html/tests
 endif
+TOOL_DIRS	+= reftests/fonts reftests/fonts/mplus
 endif
 
 include $(topsrcdir)/config/rules.mk
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -329,23 +329,16 @@ static void DrawBorderImageComponent(nsI
                                      PRUint8 aHFill,
                                      PRUint8 aVFill,
                                      const nsSize& aUnitSize);
 
 static nscolor MakeBevelColor(PRIntn whichSide, PRUint8 style,
                               nscolor aBackgroundColor,
                               nscolor aBorderColor);
 
-static gfxRect GetTextDecorationRectInternal(const gfxPoint& aPt,
-                                             const gfxSize& aLineSize,
-                                             const gfxFloat aAscent,
-                                             const gfxFloat aOffset,
-                                             const PRUint8 aDecoration,
-                                             const PRUint8 aStyle);
-
 /* Returns FALSE iff all returned aTwipsRadii == 0, TRUE otherwise */
 static PRBool GetBorderRadiusTwips(const nsStyleCorners& aBorderRadius,
                                    const nscoord& aFrameWidth,
                                    nscoord aTwipsRadii[8]);
 
 static InlineBackgroundData* gInlineBGData = nsnull;
 
 // Initialize any static variables used by nsCSSRendering.
@@ -2502,16 +2495,18 @@ nsCSSRendering::PaintDecorationLine(gfxC
                                     const nscolor aColor,
                                     const gfxPoint& aPt,
                                     const gfxSize& aLineSize,
                                     const gfxFloat aAscent,
                                     const gfxFloat aOffset,
                                     const PRUint8 aDecoration,
                                     const PRUint8 aStyle)
 {
+  NS_ASSERTION(aStyle != DECORATION_STYLE_NONE, "aStyle is none");
+
   gfxRect rect =
     GetTextDecorationRectInternal(aPt, aLineSize, aAscent, aOffset,
                                   aDecoration, aStyle);
   if (rect.IsEmpty())
     return;
 
   if (aDecoration != NS_STYLE_TEXT_DECORATION_UNDERLINE &&
       aDecoration != NS_STYLE_TEXT_DECORATION_OVERLINE &&
@@ -2523,79 +2518,171 @@ nsCSSRendering::PaintDecorationLine(gfxC
 
   gfxFloat lineHeight = PR_MAX(NS_round(aLineSize.height), 1.0);
   PRBool contextIsSaved = PR_FALSE;
 
   gfxFloat oldLineWidth;
   nsRefPtr<gfxPattern> oldPattern;
 
   switch (aStyle) {
-    case NS_STYLE_BORDER_STYLE_SOLID:
-    case NS_STYLE_BORDER_STYLE_DOUBLE:
+    case DECORATION_STYLE_SOLID:
+    case DECORATION_STYLE_DOUBLE:
       oldLineWidth = aGfxContext->CurrentLineWidth();
       oldPattern = aGfxContext->GetPattern();
       break;
-    case NS_STYLE_BORDER_STYLE_DASHED: {
+    case DECORATION_STYLE_DASHED: {
       aGfxContext->Save();
       contextIsSaved = PR_TRUE;
+      aGfxContext->Clip(rect);
       gfxFloat dashWidth = lineHeight * DOT_LENGTH * DASH_LENGTH;
       gfxFloat dash[2] = { dashWidth, dashWidth };
       aGfxContext->SetLineCap(gfxContext::LINE_CAP_BUTT);
       aGfxContext->SetDash(dash, 2, 0.0);
+      // We should continue to draw the last dash even if it is not in the rect.
+      rect.size.width += dashWidth;
       break;
     }
-    case NS_STYLE_BORDER_STYLE_DOTTED: {
+    case DECORATION_STYLE_DOTTED: {
       aGfxContext->Save();
       contextIsSaved = PR_TRUE;
+      aGfxContext->Clip(rect);
       gfxFloat dashWidth = lineHeight * DOT_LENGTH;
       gfxFloat dash[2];
       if (lineHeight > 2.0) {
         dash[0] = 0.0;
         dash[1] = dashWidth * 2.0;
         aGfxContext->SetLineCap(gfxContext::LINE_CAP_ROUND);
       } else {
         dash[0] = dashWidth;
         dash[1] = dashWidth;
       }
       aGfxContext->SetDash(dash, 2, 0.0);
+      // We should continue to draw the last dot even if it is not in the rect.
+      rect.size.width += dashWidth;
       break;
     }
+    case DECORATION_STYLE_WAVY:
+      aGfxContext->Save();
+      contextIsSaved = PR_TRUE;
+      aGfxContext->Clip(rect);
+      if (lineHeight > 2.0) {
+        aGfxContext->SetAntialiasMode(gfxContext::MODE_COVERAGE);
+      } else {
+        // Don't use anti-aliasing here.  Because looks like lighter color wavy
+        // line at this case.  And probably, users don't think the
+        // non-anti-aliased wavy line is not pretty.
+        aGfxContext->SetAntialiasMode(gfxContext::MODE_ALIASED);
+      }
+      break;
     default:
       NS_ERROR("Invalid style value!");
       return;
   }
 
   // The y position should be set to the middle of the line.
   rect.pos.y += lineHeight / 2;
 
   aGfxContext->SetColor(gfxRGBA(aColor));
   aGfxContext->SetLineWidth(lineHeight);
   switch (aStyle) {
-    case NS_STYLE_BORDER_STYLE_SOLID:
+    case DECORATION_STYLE_SOLID:
       aGfxContext->NewPath();
       aGfxContext->MoveTo(rect.TopLeft());
       aGfxContext->LineTo(rect.TopRight());
       aGfxContext->Stroke();
       break;
-    case NS_STYLE_BORDER_STYLE_DOUBLE:
+    case DECORATION_STYLE_DOUBLE:
+      /**
+       *  We are drawing double line as:
+       *
+       * +-------------------------------------------+
+       * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
+       * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
+       * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
+       * |                                           |
+       * |                                           |
+       * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
+       * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
+       * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
+       * +-------------------------------------------+
+       */
       aGfxContext->NewPath();
       aGfxContext->MoveTo(rect.TopLeft());
       aGfxContext->LineTo(rect.TopRight());
       rect.size.height -= lineHeight;
       aGfxContext->MoveTo(rect.BottomLeft());
       aGfxContext->LineTo(rect.BottomRight());
       aGfxContext->Stroke();
       break;
-    case NS_STYLE_BORDER_STYLE_DOTTED:
-    case NS_STYLE_BORDER_STYLE_DASHED:
+    case DECORATION_STYLE_DOTTED:
+    case DECORATION_STYLE_DASHED:
       aGfxContext->NewPath();
       aGfxContext->MoveTo(rect.TopLeft());
       aGfxContext->LineTo(rect.TopRight());
       aGfxContext->Stroke();
       break;
+    case DECORATION_STYLE_WAVY: {
+      /**
+       *  We are drawing wavy line as:
+       *
+       *  P: Path, X: Painted pixel
+       *
+       *     +---------------------------------------+
+       *   XX|X            XXXXXX            XXXXXX  |
+       *   PP|PX          XPPPPPPX          XPPPPPPX |    ^
+       *   XX|XPX        XPXXXXXXPX        XPXXXXXXPX|    |
+       *     | XPX      XPX      XPX      XPX      XP|X   |adv
+       *     |  XPXXXXXXPX        XPXXXXXXPX        X|PX  |
+       *     |   XPPPPPPX          XPPPPPPX          |XPX v
+       *     |    XXXXXX            XXXXXX           | XX
+       *     +---------------------------------------+
+       *      <---><--->                                ^
+       *      adv  flatLengthAtVertex                   rightMost
+       *
+       *  1. Always starts from top-left of the drawing area, however, we need
+       *     to draw  the line from outside of the rect.  Because the start
+       *     point of the line is not good style if we draw from inside it.
+       *  2. First, draw horizontal line from outside the rect to top-left of
+       *     the rect;
+       *  3. Goes down to bottom of the area at 45 degrees.
+       *  4. Slides to right horizontaly, see |flatLengthAtVertex|.
+       *  5. Goes up to top of the area at 45 degrees.
+       *  6. Slides to right horizontaly.
+       *  7. Repeat from 2 until reached to right-most edge of the area.
+       */
+
+      rect.pos.x += lineHeight / 2.0;
+      aGfxContext->NewPath();
+
+      gfxPoint pt(rect.pos);
+      gfxFloat rightMost = pt.x + rect.Width() + lineHeight;
+      gfxFloat adv = rect.Height() - lineHeight;
+      gfxFloat flatLengthAtVertex = PR_MAX((lineHeight - 1.0) * 2.0, 1.0);
+
+      pt.x -= lineHeight;
+      aGfxContext->MoveTo(pt); // 1
+
+      pt.x = rect.pos.x;
+      aGfxContext->LineTo(pt); // 2
+
+      PRBool goDown = PR_TRUE;
+      while (pt.x < rightMost) {
+        pt.x += adv;
+        pt.y += goDown ? adv : -adv;
+
+        aGfxContext->LineTo(pt); // 3 and 5
+
+        pt.x += flatLengthAtVertex;
+        aGfxContext->LineTo(pt); // 4 and 6
+
+        goDown = !goDown;
+      }
+      aGfxContext->Stroke();
+      break;
+    }
     default:
       NS_ERROR("Invalid style value!");
       break;
   }
 
   if (contextIsSaved) {
     aGfxContext->Restore();
   } else {
@@ -2608,65 +2695,105 @@ nsRect
 nsCSSRendering::GetTextDecorationRect(nsPresContext* aPresContext,
                                       const gfxSize& aLineSize,
                                       const gfxFloat aAscent,
                                       const gfxFloat aOffset,
                                       const PRUint8 aDecoration,
                                       const PRUint8 aStyle)
 {
   NS_ASSERTION(aPresContext, "aPresContext is null");
+  NS_ASSERTION(aStyle != DECORATION_STYLE_NONE, "aStyle is none");
 
   gfxRect rect =
     GetTextDecorationRectInternal(gfxPoint(0, 0), aLineSize, aAscent, aOffset,
                                   aDecoration, aStyle);
   // The rect values are already rounded to nearest device pixels.
   nsRect r;
   r.x = aPresContext->GfxUnitsToAppUnits(rect.X());
   r.y = aPresContext->GfxUnitsToAppUnits(rect.Y());
   r.width = aPresContext->GfxUnitsToAppUnits(rect.Width());
   r.height = aPresContext->GfxUnitsToAppUnits(rect.Height());
   return r;
 }
 
-static gfxRect
-GetTextDecorationRectInternal(const gfxPoint& aPt,
-                              const gfxSize& aLineSize,
-                              const gfxFloat aAscent,
-                              const gfxFloat aOffset,
-                              const PRUint8 aDecoration,
-                              const PRUint8 aStyle)
+gfxRect
+nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt,
+                                              const gfxSize& aLineSize,
+                                              const gfxFloat aAscent,
+                                              const gfxFloat aOffset,
+                                              const PRUint8 aDecoration,
+                                              const PRUint8 aStyle)
 {
+  NS_ASSERTION(aStyle <= DECORATION_STYLE_WAVY, "Invalid aStyle value");
+
+  if (aStyle == DECORATION_STYLE_NONE)
+    return gfxRect(0, 0, 0, 0);
+
   gfxRect r;
   r.pos.x = NS_floor(aPt.x + 0.5);
   r.size.width = NS_round(aLineSize.width);
 
-  gfxFloat basesize = NS_round(aLineSize.height);
-  basesize = PR_MAX(basesize, 1.0);
-  r.size.height = basesize;
-  if (aStyle == NS_STYLE_BORDER_STYLE_DOUBLE) {
-    gfxFloat gap = NS_round(basesize / 2.0);
+  gfxFloat lineHeight = NS_round(aLineSize.height);
+  lineHeight = PR_MAX(lineHeight, 1.0);
+  gfxFloat underlineOffsetAdjust = 0.0;
+  r.size.height = lineHeight;
+  if (aStyle == DECORATION_STYLE_DOUBLE) {
+    /**
+     *  We will draw double line as:
+     *
+     * +-------------------------------------------+
+     * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
+     * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
+     * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
+     * |                                           | ^
+     * |                                           | | gap
+     * |                                           | v
+     * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
+     * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
+     * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
+     * +-------------------------------------------+
+     */
+    gfxFloat gap = NS_round(lineHeight / 2.0);
     gap = PR_MAX(gap, 1.0);
-    r.size.height = basesize * 2.0 + gap;
-  } else {
-    r.size.height = basesize;
+    r.size.height = lineHeight * 2.0 + gap;
+  } else if (aStyle == DECORATION_STYLE_WAVY) {
+    /**
+     *  We will draw wavy line as:
+     *
+     * +-------------------------------------------+
+     * |XXXXX            XXXXXX            XXXXXX  | ^
+     * |XXXXXX          XXXXXXXX          XXXXXXXX | | lineHeight
+     * |XXXXXXX        XXXXXXXXXX        XXXXXXXXXX| v
+     * |     XXX      XXX      XXX      XXX      XX|
+     * |      XXXXXXXXXX        XXXXXXXXXX        X|
+     * |       XXXXXXXX          XXXXXXXX          |
+     * |        XXXXXX            XXXXXX           |
+     * +-------------------------------------------+
+     */
+    r.size.height = lineHeight > 2.0 ? lineHeight * 4.0 : lineHeight * 3.0;
+    // If this is underline, the middle of the rect should be aligned to the
+    // specified underline offset.  So, wavy line's top edge can overlap to
+    // baseline.  Because even if the wavy line overlaps the baseline of the
+    // text, that shouldn't cause unreadability.
+    underlineOffsetAdjust = r.Height() / 2.0;
   }
 
   gfxFloat baseline = NS_floor(aPt.y + aAscent + 0.5);
-  gfxFloat offset = 0;
+  gfxFloat offset = 0.0;
   switch (aDecoration) {
     case NS_STYLE_TEXT_DECORATION_UNDERLINE:
-      offset = aOffset;
+      offset = aOffset + underlineOffsetAdjust;
       break;
     case NS_STYLE_TEXT_DECORATION_OVERLINE:
-      offset = aOffset - basesize + r.Height();
+      offset = aOffset - lineHeight + r.Height();
       break;
     case NS_STYLE_TEXT_DECORATION_LINE_THROUGH: {
       gfxFloat extra = NS_floor(r.Height() / 2.0 + 0.5);
-      extra = PR_MAX(extra, basesize);
-      offset = aOffset - basesize + extra;
+      extra = PR_MAX(extra, lineHeight);
+      offset = aOffset - lineHeight + extra;
       break;
     }
     default:
       NS_ERROR("Invalid decoration value!");
   }
   r.pos.y = baseline - NS_floor(offset + 0.5);
   return r;
 }
--- a/layout/base/nsCSSRendering.h
+++ b/layout/base/nsCSSRendering.h
@@ -197,16 +197,25 @@ struct nsCSSRendering {
                                      const nsStyleBackground* aBGColor,
                                      const nsRect&        aBorderRect,
                                      PRInt32              aAppUnitsPerCSSPixel,
                                      PRUint8              aStartBevelSide = 0,
                                      nscoord              aStartBevelOffset = 0,
                                      PRUint8              aEndBevelSide = 0,
                                      nscoord              aEndBevelOffset = 0);
 
+  enum {
+    DECORATION_STYLE_NONE   = 0,
+    DECORATION_STYLE_SOLID  = 1,
+    DECORATION_STYLE_DOTTED = 2,
+    DECORATION_STYLE_DASHED = 3,
+    DECORATION_STYLE_DOUBLE = 4,
+    DECORATION_STYLE_WAVY   = 5
+  };
+
   /**
    * Function for painting the decoration lines for the text.
    * NOTE: aPt, aLineSize, aAscent and aOffset are non-rounded device pixels,
    *       not app units.
    *   input:
    *     @param aGfxContext
    *     @param aColor            the color of the decoration line
    *     @param aPt               the top/left edge of the text
@@ -215,21 +224,18 @@ struct nsCSSRendering {
    *     @param aAscent           the ascent of the text
    *     @param aOffset           the offset of the decoration line from
    *                              the baseline of the text (if the value is
    *                              positive, the line is lifted up)
    *     @param aDecoration       which line will be painted. The value can be
    *                              NS_STYLE_TEXT_DECORATION_UNDERLINE or
    *                              NS_STYLE_TEXT_DECORATION_OVERLINE or
    *                              NS_STYLE_TEXT_DECORATION_LINE_THROUGH.
-   *     @param aStyle            the style of the decoration line. The value
-   *                              can be NS_STYLE_BORDER_STYLE_SOLID or
-   *                              NS_STYLE_BORDER_STYLE_DOTTED or
-   *                              NS_STYLE_BORDER_STYLE_DASHED or
-   *                              NS_STYLE_BORDER_STYLE_DOUBLE.
+   *     @param aStyle            the style of the decoration line (See above
+   *                              enum names).
    */
   static void PaintDecorationLine(gfxContext* aGfxContext,
                                   const nscolor aColor,
                                   const gfxPoint& aPt,
                                   const gfxSize& aLineSize,
                                   const gfxFloat aAscent,
                                   const gfxFloat aOffset,
                                   const PRUint8 aDecoration,
@@ -246,31 +252,36 @@ struct nsCSSRendering {
    *     @param aAscent           the ascent of the text
    *     @param aOffset           the offset of the decoration line from
    *                              the baseline of the text (if the value is
    *                              positive, the line is lifted up)
    *     @param aDecoration       which line will be painted. The value can be
    *                              NS_STYLE_TEXT_DECORATION_UNDERLINE or
    *                              NS_STYLE_TEXT_DECORATION_OVERLINE or
    *                              NS_STYLE_TEXT_DECORATION_LINE_THROUGH.
-   *     @param aStyle            the style of the decoration line. The value
-   *                              can be NS_STYLE_BORDER_STYLE_SOLID or
-   *                              NS_STYLE_BORDER_STYLE_DOTTED or
-   *                              NS_STYLE_BORDER_STYLE_DASHED or
-   *                              NS_STYLE_BORDER_STYLE_DOUBLE.
+   *     @param aStyle            the style of the decoration line (See above
+   *                              enum names).
    *   output:
    *     @return                  the decoration line rect for the input,
    *                              the each values are app units.
    */
   static nsRect GetTextDecorationRect(nsPresContext* aPresContext,
                                       const gfxSize& aLineSize,
                                       const gfxFloat aAscent,
                                       const gfxFloat aOffset,
                                       const PRUint8 aDecoration,
                                       const PRUint8 aStyle);
+
+protected:
+  static gfxRect GetTextDecorationRectInternal(const gfxPoint& aPt,
+                                               const gfxSize& aLineSize,
+                                               const gfxFloat aAscent,
+                                               const gfxFloat aOffset,
+                                               const PRUint8 aDecoration,
+                                               const PRUint8 aStyle);
 };
 
 /*
  * nsContextBoxBlur
  * Creates an 8-bit alpha channel context for callers to draw in, blurs the
  * contents of that context and applies it as a 1-color mask on a
  * different existing context. Uses gfxAlphaBoxBlur as its back end.
  *
--- a/layout/base/tests/Makefile.in
+++ b/layout/base/tests/Makefile.in
@@ -67,16 +67,17 @@ DEFINES += -D_IMPL_NS_LAYOUT
 		test_bug465448.xul \
 		test_bug469170.html \
 		test_bug471126.html \
 		test_bug435293-scale.html \
 		test_bug435293-interaction.html \
 		test_bug435293-skew.html \
 		test_printpreview.html \
 		test_bug482976.html \
+		decoration_line_rendering.js \
 		$(NULL)
 # test_bug396024.html is currently disabled because it interacts badly with
 # the "You can't print-preview while the page is loading" dialog.
 # (See bug 407080)
 
 # Tests for bugs 441782 and 467672 don't pass reliably on Windows, because of bug 469208
 ifeq (,$(filter windows,$(MOZ_WIDGET_TOOLKIT)))
 _TEST_FILES += \
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/decoration_line_rendering.js
@@ -0,0 +1,180 @@
+function addPoint(aSVGElement, aPolylineElement, aX, aY) {
+  var pt = aSVGElement.createSVGPoint();
+  pt.x = aX;
+  pt.y = aY;
+  aPolylineElement.points.appendItem(pt);
+}
+
+function round(aFloat)
+{
+  return aFloat >= 0.0 ? Math.floor(aFloat + 0.5) : Math.ceil(aFloat - 0.5);
+}
+
+const kDecorationStyleNone   = 0;
+const kDecorationStyleSolid  = 1;
+const kDecorationStyleDotted = 2;
+const kDecorationStyleDashed = 3;
+const kDecorationStyleDouble = 4;
+const kDecorationStyleWavy   = 5;
+
+const kDotLength  = 1.0;
+const kDashLength = 3.0;
+
+const kSVGNS = "http://www.w3.org/2000/svg";
+
+// XXX following functions only support to draw underline now.
+
+function drawDecorationLine(aDocument, aColor, aPt, aLineSize, aAscent, aOffset, aStyle)
+{
+  var rect = getTextDecorationRect(aPt, aLineSize, aAscent, aOffset, aStyle);
+  if (rect.width == 0 || rect.height == 0)
+    return;
+
+  var root = aDocument.documentElement;
+  var container = aDocument.createElementNS(kSVGNS, "svg");
+  root.appendChild(container);
+
+  var line1 = aDocument.createElementNS(kSVGNS, "polyline");
+  var line2;
+
+  var style = "position: absolute;";
+  style += "left: " + rect.x + "px;";
+  style += "top: " + rect.y + "px;";
+  style += "width: " + rect.width + "px;";
+  style += "height: " + rect.height + "px;";
+  container.setAttribute("style", style);
+  rect.x = rect.y = 0;
+
+  var lineHeight = Math.max(round(aLineSize.height), 1.0);
+
+  switch (aStyle) {
+    case kDecorationStyleDouble:
+      line2 = aDocument.createElementNS(kSVGNS, "polyline");
+      container.appendChild(line2);
+    case kDecorationStyleSolid:
+      container.appendChild(line1);
+      break;
+    case kDecorationStyleDashed:
+      container.appendChild(line1);
+      var dashWidth = lineHeight * kDotLength * kDashLength;
+      var dash = "stroke-dasharray: " + dashWidth + ", " + dashWidth + ";";
+      var lineCap = "stroke-linecap: butt;"
+      line1.setAttribute("style", dash + lineCap);
+      rect.width += dashWidth;
+      break;
+    case kDecorationStyleDotted:
+      container.appendChild(line1);
+      var dashWidth = lineHeight * kDotLength;
+      var dash = "stroke-dasharray: ";
+      var lineCap = "";
+      if (lineHeight > 2.0) {
+        dash += "0.0, " + dashWidth * 2.0 + ";";
+        lineCap = "stroke-linecap: round;";
+      } else {
+        dash += dashWidth + ", " + dashWidth + ";";
+      }
+      rect.width += dashWidth;
+      line1.setAttribute("style", dash + lineCap);
+      break;
+    case kDecorationStyleWavy:
+      container.appendChild(line1);
+      if (lineHeight > 2.0) {
+        //
+      } else {
+        line1.setAttribute("shape-rendering", "optimizeSpeed");
+      }
+      break;
+  }
+
+  rect.y += lineHeight / 2;
+
+  line1.setAttribute("fill", "none");
+  line1.setAttribute("stroke", aColor);
+  line1.setAttribute("stroke-width", lineHeight);
+  if (line2) {
+    line2.setAttribute("fill", "none");
+    line2.setAttribute("stroke", aColor);
+    line2.setAttribute("stroke-width", lineHeight);
+  }
+
+  switch (aStyle) {
+    case kDecorationStyleSolid:
+      addPoint(container, line1, rect.x, rect.y);
+      addPoint(container, line1, rect.x + rect.width, rect.y);
+      break;
+    case kDecorationStyleDouble:
+      addPoint(container, line1, rect.x, rect.y);
+      addPoint(container, line1, rect.x + rect.width, rect.y);
+      rect.height -= lineHeight;
+      addPoint(container, line2, rect.x, rect.y + rect.height);
+      addPoint(container, line2, rect.x + rect.width, rect.y + rect.height);
+      break;
+    case kDecorationStyleDotted:
+    case kDecorationStyleDashed:
+      addPoint(container, line1, rect.x, rect.y);
+      addPoint(container, line1, rect.x + rect.width, rect.y);
+      break;
+    case kDecorationStyleWavy:
+      rect.x += lineHeight / 2.0;
+
+      var pt = { x: rect.x, y: rect.y };
+      var rightMost = pt.x + rect.width + lineHeight;
+      var adv = rect.height - lineHeight;
+      var flatLengthAtVertex = Math.max((lineHeight - 1.0) * 2.0, 1.0);
+
+      var points = "";
+
+      pt.x -= lineHeight;
+      addPoint(container, line1, pt.x, pt.y);
+
+      pt.x = rect.x;
+      addPoint(container, line1, pt.x, pt.y);
+
+      var goDown = true;
+      while (pt.x < rightMost) {
+        pt.x += adv;
+        pt.y += goDown ? adv : -adv;
+
+        addPoint(container, line1, pt.x, pt.y);
+
+        pt.x += flatLengthAtVertex;
+        addPoint(container, line1, pt.x, pt.y);
+
+        goDown = !goDown;
+      }
+      break;
+  }
+}
+
+function getTextDecorationRect(aPt, aLineSize, aAscent, aOffset, aStyle)
+{
+  if (aStyle == kDecorationStyleNone)
+    return { x: 0, y: 0, width: 0, height: 0 };
+
+  var r = {};
+  r.x = Math.floor(aPt.x + 0.5);
+  r.width = round(aLineSize.width);
+
+  var lineHeight = round(aLineSize.height);
+  lineHeight = Math.max(lineHeight, 1.0);
+  var underlineOffsetAdjust = 0.0;
+  r.height = lineHeight;
+  if (aStyle == kDecorationStyleDouble) {
+    var gap = round(lineHeight / 2.0);
+    gap = Math.max(gap, 1.0);
+    r.height = lineHeight * 2.0 + gap;
+  } else if (aStyle == kDecorationStyleWavy) {
+    r.height = lineHeight > 2.0 ? lineHeight * 4.0 : lineHeight * 3.0;
+    underlineOffsetAdjust = r.height / 2.0;
+  }
+
+  var baseline = Math.floor(aPt.y + aAscent + 0.5);
+  var offset = 0.0;
+
+  offset = aOffset + underlineOffsetAdjust;
+
+  r.y = baseline - Math.floor(offset + 0.5);
+
+  return r;
+}
+
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -5982,17 +5982,17 @@ nsBlockFrame::PaintTextDecorationLine(gf
   // Only paint if we have a positive width
   if (width > 0) {
     gfxPoint pt(PresContext()->AppUnitsToGfxUnits(start + aPt.x),
                 PresContext()->AppUnitsToGfxUnits(aLine->mBounds.y + aPt.y));
     gfxSize size(PresContext()->AppUnitsToGfxUnits(width), aSize);
     nsCSSRendering::PaintDecorationLine(
       aCtx, aColor, pt, size,
       PresContext()->AppUnitsToGfxUnits(aLine->GetAscent()),
-      aOffset, aDecoration, NS_STYLE_BORDER_STYLE_SOLID);
+      aOffset, aDecoration, nsCSSRendering::DECORATION_STYLE_SOLID);
   }
 }
 
 #ifdef DEBUG
 static void DebugOutputDrawLine(PRInt32 aDepth, nsLineBox* aLine, PRBool aDrawn) {
   if (nsBlockFrame::gNoisyDamageRepair) {
     nsFrame::IndentBy(stdout, aDepth+1);
     nsRect lineArea = aLine->GetCombinedArea();
--- a/layout/generic/nsHTMLContainerFrame.cpp
+++ b/layout/generic/nsHTMLContainerFrame.cpp
@@ -356,17 +356,17 @@ nsHTMLContainerFrame::PaintTextDecoratio
       bp.side(side) = 0;
     }
   }
   nscoord innerWidth = mRect.width - bp.left - bp.right;
   gfxPoint pt(PresContext()->AppUnitsToGfxUnits(bp.left + aPt.x),
               PresContext()->AppUnitsToGfxUnits(bp.top + aPt.y));
   gfxSize size(PresContext()->AppUnitsToGfxUnits(innerWidth), aSize);
   nsCSSRendering::PaintDecorationLine(aCtx, aColor, pt, size, aAscent, aOffset,
-                                      aDecoration, NS_STYLE_BORDER_STYLE_SOLID);
+                    aDecoration, nsCSSRendering::DECORATION_STYLE_SOLID);
 }
 
 void
 nsHTMLContainerFrame::GetTextDecorations(nsPresContext* aPresContext, 
                                          PRBool aIsBlock,
                                          PRUint8& aDecorations,
                                          nscolor& aUnderColor, 
                                          nscolor& aOverColor, 
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -2650,68 +2650,8 @@ nsLineLayout::RelativePositionFrames(Per
   // NS_FRAME_OUTSIDE_CHILDREN bit..
   if (psd->mFrame) {
     PerFrameData* spanPFD = psd->mFrame;
     nsIFrame* frame = spanPFD->mFrame;
     frame->FinishAndStoreOverflow(&combinedAreaResult, frame->GetSize());
   }
   aCombinedArea = combinedAreaResult;
 }
-
-void
-nsLineLayout::CombineTextDecorations(nsPresContext* aPresContext,
-                                     PRUint8 aDecorations,
-                                     nsIFrame* aFrame,
-                                     nsRect& aCombinedArea,
-                                     nscoord aAscentOverride,
-                                     float aUnderlineSizeRatio)
-{
-  if (!(aDecorations & (NS_STYLE_TEXT_DECORATION_UNDERLINE |
-                        NS_STYLE_TEXT_DECORATION_OVERLINE |
-                        NS_STYLE_TEXT_DECORATION_LINE_THROUGH)))
-    return;
-
-  nsCOMPtr<nsIFontMetrics> fm;
-  nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm));
-  nsIThebesFontMetrics* tfm = static_cast<nsIThebesFontMetrics*>(fm.get());
-  gfxFontGroup* fontGroup = tfm->GetThebesFontGroup();
-  gfxFont* firstFont = fontGroup->GetFontAt(0);
-  if (!firstFont)
-    return; // OOM
-  const gfxFont::Metrics& metrics = firstFont->GetMetrics();
-
-  gfxFloat ascent = aAscentOverride == 0 ? metrics.maxAscent :
-                      aPresContext->AppUnitsToGfxUnits(aAscentOverride);
-  nsRect decorationArea;
-  if (aDecorations & (NS_STYLE_TEXT_DECORATION_UNDERLINE |
-                      NS_STYLE_TEXT_DECORATION_OVERLINE)) {
-    gfxSize size(aPresContext->AppUnitsToGfxUnits(aCombinedArea.width),
-                 metrics.underlineSize);
-    if (aDecorations & NS_STYLE_TEXT_DECORATION_OVERLINE) {
-      decorationArea =
-        nsCSSRendering::GetTextDecorationRect(aPresContext, size, ascent,
-                          metrics.maxAscent, NS_STYLE_TEXT_DECORATION_OVERLINE,
-                          NS_STYLE_BORDER_STYLE_SOLID);
-      aCombinedArea.UnionRect(aCombinedArea, decorationArea);
-    }
-    if (aDecorations & NS_STYLE_TEXT_DECORATION_UNDERLINE) {
-      aUnderlineSizeRatio = PR_MAX(aUnderlineSizeRatio, 1.0f);
-      size.height *= aUnderlineSizeRatio;
-      gfxFloat underlineOffset = fontGroup->GetUnderlineOffset();
-      decorationArea =
-        nsCSSRendering::GetTextDecorationRect(aPresContext, size, ascent,
-                          underlineOffset,
-                          NS_STYLE_TEXT_DECORATION_UNDERLINE,
-                          NS_STYLE_BORDER_STYLE_SOLID);
-      aCombinedArea.UnionRect(aCombinedArea, decorationArea);
-    }
-  }
-  if (aDecorations & NS_STYLE_TEXT_DECORATION_LINE_THROUGH) {
-    gfxSize size(aPresContext->AppUnitsToGfxUnits(aCombinedArea.width),
-                 metrics.strikeoutSize);
-    decorationArea =
-      nsCSSRendering::GetTextDecorationRect(aPresContext, size, ascent,
-                        metrics.strikeoutOffset,
-                        NS_STYLE_TEXT_DECORATION_LINE_THROUGH,
-                        NS_STYLE_BORDER_STYLE_SOLID);
-    aCombinedArea.UnionRect(aCombinedArea, decorationArea);
-  }
-}
--- a/layout/generic/nsLineLayout.h
+++ b/layout/generic/nsLineLayout.h
@@ -139,22 +139,16 @@ public:
 
   /**
    * Handle all the relative positioning in the line, compute the
    * combined area (== overflow area) for the line, and handle view
    * sizing/positioning and the setting of NS_FRAME_OUTSIDE_CHILDREN.
    */
   void RelativePositionFrames(nsRect& aCombinedArea);
 
-  static void CombineTextDecorations(nsPresContext* aPresContext,
-                                     PRUint8 aDecorations,
-                                     nsIFrame* aFrame,
-                                     nsRect& aCombinedArea,
-                                     nscoord aAscentOverride = 0,
-                                     float aUnderlineSizeRatio = 1.0f);
   //----------------------------------------
 
   // Supporting methods and data for flags
 protected:
 #define LL_FIRSTLETTERSTYLEOK          0x00000008
 #define LL_ISTOPOFPAGE                 0x00000010
 #define LL_IMPACTEDBYFLOATS            0x00000040
 #define LL_LASTFLOATWASLETTERFRAME     0x00000080
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -437,15 +437,17 @@ protected:
       return !!(mDecorations & NS_STYLE_TEXT_DECORATION_OVERLINE);
     }
     PRBool HasStrikeout() {
       return !!(mDecorations & NS_STYLE_TEXT_DECORATION_LINE_THROUGH);
     }
   };
   TextDecorations GetTextDecorations(nsPresContext* aPresContext);
 
-  PRBool HasSelectionOverflowingDecorations(nsPresContext* aPresContext,
-                                            float* aRatio = nsnull);
+  // Set non empty rect to aRect, it should be overflow rect or frame rect.
+  // If the result rect is larger than the given rect, this returns PR_TRUE.
+  PRBool CombineSelectionUnderlineRect(nsPresContext* aPresContext,
+                                       nsRect& aRect);
 
   PRBool IsFloatingFirstLetterChild();
 };
 
 #endif
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -249,30 +249,57 @@ public:
   PRBool GetSelectionColors(nscolor* aForeColor,
                             nscolor* aBackColor);
   void GetHighlightColors(nscolor* aForeColor,
                           nscolor* aBackColor);
   void GetIMESelectionColors(PRInt32  aIndex,
                              nscolor* aForeColor,
                              nscolor* aBackColor);
   // if this returns PR_FALSE, we don't need to draw underline.
-  PRBool GetIMEUnderline(PRInt32  aIndex,
-                         nscolor* aLineColor,
-                         float*   aRelativeSize,
-                         PRUint8* aStyle);
+  PRBool GetSelectionUnderlineForPaint(PRInt32  aIndex,
+                                       nscolor* aLineColor,
+                                       float*   aRelativeSize,
+                                       PRUint8* aStyle);
+
+  // if this returns PR_FALSE, we don't need to draw underline.
+  static PRBool GetSelectionUnderline(nsPresContext* aPresContext,
+                                      PRInt32 aIndex,
+                                      nscolor* aLineColor,
+                                      float* aRelativeSize,
+                                      PRUint8* aStyle);
 
   nsPresContext* PresContext() { return mPresContext; }
 
   enum {
     eIndexRawInput = 0,
     eIndexSelRawText,
     eIndexConvText,
-    eIndexSelConvText
+    eIndexSelConvText,
+    eIndexSpellChecker
   };
 
+  static PRInt32 GetUnderlineStyleIndexForSelectionType(PRInt32 aSelectionType)
+  {
+    switch (aSelectionType) {
+      case nsISelectionController::SELECTION_IME_RAWINPUT:
+        return eIndexRawInput;
+      case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT:
+        return eIndexSelRawText;
+      case nsISelectionController::SELECTION_IME_CONVERTEDTEXT:
+        return eIndexConvText;
+      case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT:
+        return eIndexSelConvText;
+      case nsISelectionController::SELECTION_SPELLCHECK:
+        return eIndexSpellChecker;
+      default:
+        NS_WARNING("non-IME selection type");
+        return eIndexRawInput;
+    }
+  }
+
 protected:
   nsTextFrame*   mFrame;
   nsPresContext* mPresContext;
   PRPackedBool   mInitCommonColors;
   PRPackedBool   mInitSelectionColors;
 
   // Selection data
 
@@ -280,34 +307,35 @@ protected:
   nscolor      mSelectionTextColor;
   nscolor      mSelectionBGColor;
 
   // Common data
 
   PRInt32 mSufficientContrast;
   nscolor mFrameBackgroundColor;
 
-  // IME selection colors and underline info
-  struct nsIMEStyle {
+  // selection colors and underline info, the colors are resolved colors,
+  // i.e., the foreground color and background color are swapped if it's needed.
+  // And also line color will be resolved from them.
+  struct nsSelectionStyle {
     PRBool mInit;
     nscolor mTextColor;
     nscolor mBGColor;
     nscolor mUnderlineColor;
     PRUint8 mUnderlineStyle;
+    float   mUnderlineRelativeSize;
   };
-  nsIMEStyle mIMEStyle[4];
-  // indices
-  float mIMEUnderlineRelativeSize;
+  nsSelectionStyle mSelectionStyle[5];
 
   // Color initializations
   void InitCommonColors();
   PRBool InitSelectionColors();
 
-  nsIMEStyle* GetIMEStyle(PRInt32 aIndex);
-  void InitIMEStyle(PRInt32 aIndex);
+  nsSelectionStyle* GetSelectionStyle(PRInt32 aIndex);
+  void InitSelectionStyle(PRInt32 aIndex);
 
   PRBool EnsureSufficientContrast(nscolor *aForeColor, nscolor *aBackColor);
 
   nscolor GetResolvedForeColor(nscolor aColor, nscolor aDefaultForeColor,
                                nscolor aBackColor);
 };
 
 static void
@@ -2942,19 +2970,18 @@ ShouldDarkenColors(nsPresContext* aPresC
 }
 
 nsTextPaintStyle::nsTextPaintStyle(nsTextFrame* aFrame)
   : mFrame(aFrame),
     mPresContext(aFrame->PresContext()),
     mInitCommonColors(PR_FALSE),
     mInitSelectionColors(PR_FALSE)
 {
-  for (int i = 0; i < 4; i++)
-    mIMEStyle[i].mInit = PR_FALSE;
-  mIMEUnderlineRelativeSize = -1.0f;
+  for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(mSelectionStyle); i++)
+    mSelectionStyle[i].mInit = PR_FALSE;
 }
 
 PRBool
 nsTextPaintStyle::EnsureSufficientContrast(nscolor *aForeColor, nscolor *aBackColor)
 {
   InitCommonColors();
 
   // If the combination of selection background color and frame background color
@@ -3022,42 +3049,42 @@ nsTextPaintStyle::GetHighlightColors(nsc
 
 void
 nsTextPaintStyle::GetIMESelectionColors(PRInt32  aIndex,
                                         nscolor* aForeColor,
                                         nscolor* aBackColor)
 {
   NS_ASSERTION(aForeColor, "aForeColor is null");
   NS_ASSERTION(aBackColor, "aBackColor is null");
-  NS_ASSERTION(aIndex >= 0 && aIndex < 4, "Index out of range");
-
-  nsIMEStyle* IMEStyle = GetIMEStyle(aIndex);
-  *aForeColor = IMEStyle->mTextColor;
-  *aBackColor = IMEStyle->mBGColor;
+  NS_ASSERTION(aIndex >= 0 && aIndex < 5, "Index out of range");
+
+  nsSelectionStyle* selectionStyle = GetSelectionStyle(aIndex);
+  *aForeColor = selectionStyle->mTextColor;
+  *aBackColor = selectionStyle->mBGColor;
 }
 
 PRBool
-nsTextPaintStyle::GetIMEUnderline(PRInt32  aIndex,
-                                  nscolor* aLineColor,
-                                  float*   aRelativeSize,
-                                  PRUint8* aStyle)
+nsTextPaintStyle::GetSelectionUnderlineForPaint(PRInt32  aIndex,
+                                                nscolor* aLineColor,
+                                                float*   aRelativeSize,
+                                                PRUint8* aStyle)
 {
   NS_ASSERTION(aLineColor, "aLineColor is null");
   NS_ASSERTION(aRelativeSize, "aRelativeSize is null");
-  NS_ASSERTION(aIndex >= 0 && aIndex < 4, "Index out of range");
-
-  nsIMEStyle* IMEStyle = GetIMEStyle(aIndex);
-  if (IMEStyle->mUnderlineStyle == NS_STYLE_BORDER_STYLE_NONE ||
-      IMEStyle->mUnderlineColor == NS_TRANSPARENT ||
-      mIMEUnderlineRelativeSize <= 0.0f)
+  NS_ASSERTION(aIndex >= 0 && aIndex < 5, "Index out of range");
+
+  nsSelectionStyle* selectionStyle = GetSelectionStyle(aIndex);
+  if (selectionStyle->mUnderlineStyle == NS_STYLE_BORDER_STYLE_NONE ||
+      selectionStyle->mUnderlineColor == NS_TRANSPARENT ||
+      selectionStyle->mUnderlineRelativeSize <= 0.0f)
     return PR_FALSE;
 
-  *aLineColor = IMEStyle->mUnderlineColor;
-  *aRelativeSize = mIMEUnderlineRelativeSize;
-  *aStyle = IMEStyle->mUnderlineStyle;
+  *aLineColor = selectionStyle->mUnderlineColor;
+  *aRelativeSize = selectionStyle->mUnderlineRelativeSize;
+  *aStyle = selectionStyle->mUnderlineStyle;
   return PR_TRUE;
 }
 
 void
 nsTextPaintStyle::InitCommonColors()
 {
   if (mInitCommonColors)
     return;
@@ -3175,101 +3202,154 @@ nsTextPaintStyle::InitSelectionColors()
     mSelectionTextColor = EnsureDifferentColors(mFrame->GetStyleColor()->mColor,
                                                 mSelectionBGColor);
   } else {
     EnsureSufficientContrast(&mSelectionTextColor, &mSelectionBGColor);
   }
   return PR_TRUE;
 }
 
-nsTextPaintStyle::nsIMEStyle*
-nsTextPaintStyle::GetIMEStyle(PRInt32 aIndex)
-{
-  InitIMEStyle(aIndex);
-  return &mIMEStyle[aIndex];
+nsTextPaintStyle::nsSelectionStyle*
+nsTextPaintStyle::GetSelectionStyle(PRInt32 aIndex)
+{
+  InitSelectionStyle(aIndex);
+  return &mSelectionStyle[aIndex];
 }
 
 struct StyleIDs {
   nsILookAndFeel::nsColorID mForeground, mBackground, mLine;
   nsILookAndFeel::nsMetricID mLineStyle;
+  nsILookAndFeel::nsMetricFloatID mLineRelativeSize;
 };
-static StyleIDs IMEStyleIDs[] = {
+static StyleIDs SelectionStyleIDs[] = {
   { nsILookAndFeel::eColor_IMERawInputForeground,
     nsILookAndFeel::eColor_IMERawInputBackground,
     nsILookAndFeel::eColor_IMERawInputUnderline,
-    nsILookAndFeel::eMetric_IMERawInputUnderlineStyle },
+    nsILookAndFeel::eMetric_IMERawInputUnderlineStyle,
+    nsILookAndFeel::eMetricFloat_IMEUnderlineRelativeSize },
   { nsILookAndFeel::eColor_IMESelectedRawTextForeground,
     nsILookAndFeel::eColor_IMESelectedRawTextBackground,
     nsILookAndFeel::eColor_IMESelectedRawTextUnderline,
-    nsILookAndFeel::eMetric_IMESelectedRawTextUnderlineStyle },
+    nsILookAndFeel::eMetric_IMESelectedRawTextUnderlineStyle,
+    nsILookAndFeel::eMetricFloat_IMEUnderlineRelativeSize },
   { nsILookAndFeel::eColor_IMEConvertedTextForeground,
     nsILookAndFeel::eColor_IMEConvertedTextBackground,
     nsILookAndFeel::eColor_IMEConvertedTextUnderline,
-    nsILookAndFeel::eMetric_IMEConvertedTextUnderlineStyle },
+    nsILookAndFeel::eMetric_IMEConvertedTextUnderlineStyle,
+    nsILookAndFeel::eMetricFloat_IMEUnderlineRelativeSize },
   { nsILookAndFeel::eColor_IMESelectedConvertedTextForeground,
     nsILookAndFeel::eColor_IMESelectedConvertedTextBackground,
     nsILookAndFeel::eColor_IMESelectedConvertedTextUnderline,
-    nsILookAndFeel::eMetric_IMESelectedConvertedTextUnderline }
+    nsILookAndFeel::eMetric_IMESelectedConvertedTextUnderline,
+    nsILookAndFeel::eMetricFloat_IMEUnderlineRelativeSize },
+  { nsILookAndFeel::eColor_LAST_COLOR,
+    nsILookAndFeel::eColor_LAST_COLOR,
+    nsILookAndFeel::eColor_SpellCheckerUnderline,
+    nsILookAndFeel::eMetric_SpellCheckerUnderlineStyle,
+    nsILookAndFeel::eMetricFloat_SpellCheckerUnderlineRelativeSize }
 };
 
 static PRUint8 sUnderlineStyles[] = {
-  NS_STYLE_BORDER_STYLE_NONE,   // NS_UNDERLINE_STYLE_NONE   0
-  NS_STYLE_BORDER_STYLE_DOTTED, // NS_UNDERLINE_STYLE_DOTTED 1
-  NS_STYLE_BORDER_STYLE_DASHED, // NS_UNDERLINE_STYLE_DASHED 2
-  NS_STYLE_BORDER_STYLE_SOLID,  // NS_UNDERLINE_STYLE_SOLID  3
-  NS_STYLE_BORDER_STYLE_DOUBLE  // NS_UNDERLINE_STYLE_DOUBLE 4
+  nsCSSRendering::DECORATION_STYLE_NONE,   // NS_UNDERLINE_STYLE_NONE   0
+  nsCSSRendering::DECORATION_STYLE_DOTTED, // NS_UNDERLINE_STYLE_DOTTED 1
+  nsCSSRendering::DECORATION_STYLE_DASHED, // NS_UNDERLINE_STYLE_DASHED 2
+  nsCSSRendering::DECORATION_STYLE_SOLID,  // NS_UNDERLINE_STYLE_SOLID  3
+  nsCSSRendering::DECORATION_STYLE_DOUBLE, // NS_UNDERLINE_STYLE_DOUBLE 4
+  nsCSSRendering::DECORATION_STYLE_WAVY    // NS_UNDERLINE_STYLE_WAVY   5
 };
 
 void
-nsTextPaintStyle::InitIMEStyle(PRInt32 aIndex)
-{
-  nsIMEStyle* IMEStyle = &mIMEStyle[aIndex];
-  if (IMEStyle->mInit)
+nsTextPaintStyle::InitSelectionStyle(PRInt32 aIndex)
+{
+  NS_ASSERTION(aIndex >= 0 && aIndex < 5, "aIndex is invalid");
+  nsSelectionStyle* selectionStyle = &mSelectionStyle[aIndex];
+  if (selectionStyle->mInit)
     return;
 
-  StyleIDs* styleIDs = &IMEStyleIDs[aIndex];
+  StyleIDs* styleIDs = &SelectionStyleIDs[aIndex];
 
   nsILookAndFeel* look = mPresContext->LookAndFeel();
-  nscolor foreColor, backColor, lineColor;
-  PRInt32 lineStyle;
-  look->GetColor(styleIDs->mForeground, foreColor);
-  look->GetColor(styleIDs->mBackground, backColor);
-  look->GetColor(styleIDs->mLine, lineColor);
-  look->GetMetric(styleIDs->mLineStyle, lineStyle);
+  nscolor foreColor, backColor;
+  if (styleIDs->mForeground == nsILookAndFeel::eColor_LAST_COLOR) {
+    foreColor = NS_SAME_AS_FOREGROUND_COLOR;
+  } else {
+    look->GetColor(styleIDs->mForeground, foreColor);
+  }
+  if (styleIDs->mBackground == nsILookAndFeel::eColor_LAST_COLOR) {
+    backColor = NS_TRANSPARENT;
+  } else {
+    look->GetColor(styleIDs->mBackground, backColor);
+  }
 
   // Convert special color to actual color
   NS_ASSERTION(foreColor != NS_TRANSPARENT,
                "foreColor cannot be NS_TRANSPARENT");
   NS_ASSERTION(backColor != NS_SAME_AS_FOREGROUND_COLOR,
                "backColor cannot be NS_SAME_AS_FOREGROUND_COLOR");
   NS_ASSERTION(backColor != NS_40PERCENT_FOREGROUND_COLOR,
                "backColor cannot be NS_40PERCENT_FOREGROUND_COLOR");
 
   foreColor = GetResolvedForeColor(foreColor, GetTextColor(), backColor);
 
   if (NS_GET_A(backColor) > 0)
     EnsureSufficientContrast(&foreColor, &backColor);
 
+  nscolor lineColor;
+  float relativeSize;
+  PRUint8 lineStyle;
+  GetSelectionUnderline(mPresContext, aIndex,
+                        &lineColor, &relativeSize, &lineStyle);
   lineColor = GetResolvedForeColor(lineColor, foreColor, backColor);
 
-  if (!NS_IS_VALID_UNDERLINE_STYLE(lineStyle))
-    lineStyle = NS_UNDERLINE_STYLE_SOLID;
-
-  IMEStyle->mTextColor       = foreColor;
-  IMEStyle->mBGColor         = backColor;
-  IMEStyle->mUnderlineColor  = lineColor;
-  IMEStyle->mUnderlineStyle  = sUnderlineStyles[lineStyle];
-  IMEStyle->mInit            = PR_TRUE;
-
-  if (mIMEUnderlineRelativeSize == -1.0f) {
-    look->GetMetric(nsILookAndFeel::eMetricFloat_IMEUnderlineRelativeSize,
-                    mIMEUnderlineRelativeSize);
-    NS_ASSERTION(mIMEUnderlineRelativeSize >= 0.0f,
-                 "underline size must be larger than 0");
-  }
+  selectionStyle->mTextColor       = foreColor;
+  selectionStyle->mBGColor         = backColor;
+  selectionStyle->mUnderlineColor  = lineColor;
+  selectionStyle->mUnderlineStyle  = lineStyle;
+  selectionStyle->mUnderlineRelativeSize = relativeSize;
+  selectionStyle->mInit            = PR_TRUE;
+}
+
+/* static */ PRBool
+nsTextPaintStyle::GetSelectionUnderline(nsPresContext* aPresContext,
+                                        PRInt32 aIndex,
+                                        nscolor* aLineColor,
+                                        float* aRelativeSize,
+                                        PRUint8* aStyle)
+{
+  NS_ASSERTION(aPresContext, "aPresContext is null");
+  NS_ASSERTION(aRelativeSize, "aRelativeSize is null");
+  NS_ASSERTION(aStyle, "aStyle is null");
+  NS_ASSERTION(aIndex >= 0 && aIndex < 5, "Index out of range");
+
+  nsILookAndFeel* look = aPresContext->LookAndFeel();
+
+  StyleIDs& styleID = SelectionStyleIDs[aIndex];
+  nscolor color;
+  float size;
+  PRInt32 style;
+
+  look->GetColor(styleID.mLine, color);
+  look->GetMetric(styleID.mLineStyle, style);
+  if (!NS_IS_VALID_UNDERLINE_STYLE(style)) {
+    NS_ERROR("Invalid underline style value is specified");
+    style = NS_UNDERLINE_STYLE_SOLID;
+  }
+  look->GetMetric(styleID.mLineRelativeSize, size);
+
+  NS_ASSERTION(size, "selection underline relative size must be larger than 0");
+
+  if (aLineColor) {
+    *aLineColor = color;
+  }
+  *aRelativeSize = size;
+  *aStyle = sUnderlineStyles[style];
+
+  return sUnderlineStyles[style] != nsCSSRendering::DECORATION_STYLE_NONE &&
+         color != NS_TRANSPARENT &&
+         size > 0.0f;
 }
 
 inline nscolor Get40PercentColor(nscolor aForeColor, nscolor aBackColor)
 {
   nscolor foreColor = NS_RGBA(NS_GET_R(aForeColor),
                               NS_GET_G(aForeColor),
                               NS_GET_B(aForeColor),
                               (PRUint8)(255 * 0.4f));
@@ -3935,24 +4015,20 @@ nsTextFrame::UnionTextDecorationOverflow
     fm->GetMaxAscent(fontAscent);
     fm->GetMaxHeight(fontHeight);
     nsRect fontRect(0, mAscent - fontAscent, GetSize().width, fontHeight);
     aOverflowRect->UnionRect(*aOverflowRect, fontRect);
   }
 
   // When this frame is not selected, the text-decoration area must be in
   // frame bounds.
-  float ratio;
+  nsRect decorationRect;
   if (!(GetStateBits() & NS_FRAME_SELECTED_CONTENT) ||
-      !HasSelectionOverflowingDecorations(aPresContext, &ratio))
+      !CombineSelectionUnderlineRect(aPresContext, *aOverflowRect))
     return;
-
-  nsLineLayout::CombineTextDecorations(aPresContext,
-                  NS_STYLE_TEXT_DECORATION_UNDERLINE,
-                  this, *aOverflowRect, mAscent, ratio);
   AddStateBits(TEXT_SELECTION_UNDERLINE_OVERFLOWED);
 }
 
 void 
 nsTextFrame::PaintTextDecorations(gfxContext* aCtx, const gfxRect& aDirtyRect,
                                   const gfxPoint& aFramePt,
                                   const gfxPoint& aTextBaselinePt,
                                   nsTextPaintStyle& aTextPaintStyle,
@@ -3976,102 +4052,94 @@ nsTextFrame::PaintTextDecorations(gfxCon
   gfxFloat ascent = gfxFloat(mAscent) / app;
 
   nscolor lineColor;
   if (decorations.HasOverline()) {
     lineColor = aOverrideColor ? *aOverrideColor : decorations.mOverColor;
     size.height = fontMetrics.underlineSize;
     nsCSSRendering::PaintDecorationLine(
       aCtx, lineColor, pt, size, ascent, fontMetrics.maxAscent,
-      NS_STYLE_TEXT_DECORATION_OVERLINE, NS_STYLE_BORDER_STYLE_SOLID);
+      NS_STYLE_TEXT_DECORATION_OVERLINE,
+      nsCSSRendering::DECORATION_STYLE_SOLID);
   }
   if (decorations.HasUnderline()) {
     lineColor = aOverrideColor ? *aOverrideColor : decorations.mUnderColor;
     size.height = fontMetrics.underlineSize;
     gfxFloat offset = aProvider.GetFontGroup()->GetUnderlineOffset();
     nsCSSRendering::PaintDecorationLine(
       aCtx, lineColor, pt, size, ascent, offset,
-      NS_STYLE_TEXT_DECORATION_UNDERLINE, NS_STYLE_BORDER_STYLE_SOLID);
+      NS_STYLE_TEXT_DECORATION_UNDERLINE,
+      nsCSSRendering::DECORATION_STYLE_SOLID);
   }
   if (decorations.HasStrikeout()) {
     lineColor = aOverrideColor ? *aOverrideColor : decorations.mStrikeColor;
     size.height = fontMetrics.strikeoutSize;
     gfxFloat offset = fontMetrics.strikeoutOffset;
     nsCSSRendering::PaintDecorationLine(
       aCtx, lineColor, pt, size, ascent, offset,
-      NS_STYLE_TEXT_DECORATION_LINE_THROUGH, NS_STYLE_BORDER_STYLE_SOLID);
+      NS_STYLE_TEXT_DECORATION_LINE_THROUGH,
+      nsCSSRendering::DECORATION_STYLE_SOLID);
   }
 }
 
 // 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 void DrawIMEUnderline(gfxContext* aContext, PRInt32 aIndex,
-    nsTextPaintStyle& aTextPaintStyle, const gfxPoint& aPt, gfxFloat aWidth,
-    gfxFloat aAscent, gfxFloat aSize, gfxFloat aOffset)
-{
-  nscolor color;
-  float relativeSize;
-  PRUint8 style;
-  if (!aTextPaintStyle.GetIMEUnderline(aIndex, &color, &relativeSize, &style))
-    return;
-
-  gfxFloat actualSize = relativeSize * aSize;
-  gfxFloat width = PR_MAX(0, aWidth - 2.0 * aSize);
-  gfxPoint pt(aPt.x + 1.0, aPt.y);
-  nsCSSRendering::PaintDecorationLine(
-    aContext, color, pt, gfxSize(width, actualSize), aAscent, aOffset,
-    NS_STYLE_TEXT_DECORATION_UNDERLINE, style);
-}
-
 /**
  * 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,
     gfxFloat aAscent, const gfxFont::Metrics& aFontMetrics)
 {
+  gfxPoint pt(aPt);
   gfxSize size(aWidth, aFontMetrics.underlineSize);
 
   switch (aType) {
+    case nsISelectionController::SELECTION_IME_RAWINPUT:
+    case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT:
+    case nsISelectionController::SELECTION_IME_CONVERTEDTEXT:
+    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: {
-      nsCSSRendering::PaintDecorationLine(
-        aContext, NS_RGB(255,0,0),
-        aPt, size, aAscent, aFontMetrics.underlineOffset,
-        NS_STYLE_TEXT_DECORATION_UNDERLINE, NS_STYLE_BORDER_STYLE_DOTTED);
+      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);
+      }
       break;
     }
-
-    case nsISelectionController::SELECTION_IME_RAWINPUT:
-      DrawIMEUnderline(aContext, nsTextPaintStyle::eIndexRawInput,
-                       aTextPaintStyle, aPt, aWidth, aAscent, size.height,
-                       aFontMetrics.underlineOffset);
-      break;
-    case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT:
-      DrawIMEUnderline(aContext, nsTextPaintStyle::eIndexSelRawText,
-                       aTextPaintStyle, aPt, aWidth, aAscent, size.height,
-                       aFontMetrics.underlineOffset);
-      break;
-    case nsISelectionController::SELECTION_IME_CONVERTEDTEXT:
-      DrawIMEUnderline(aContext, nsTextPaintStyle::eIndexConvText,
-                       aTextPaintStyle, aPt, aWidth, aAscent, size.height,
-                       aFontMetrics.underlineOffset);
-      break;
-    case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT:
-      DrawIMEUnderline(aContext, nsTextPaintStyle::eIndexSelConvText,
-                       aTextPaintStyle, aPt, aWidth, aAscent, size.height,
-                       aFontMetrics.underlineOffset);
-      break;
-
     default:
       NS_WARNING("Requested selection decorations when there aren't any");
       break;
   }
 }
 
 /**
  * This function encapsulates all knowledge of how selections affect foreground
@@ -4086,30 +4154,22 @@ static PRBool GetSelectionTextColors(Sel
 {
   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:
-      aTextPaintStyle.GetIMESelectionColors(nsTextPaintStyle::eIndexRawInput,
-                                            aForeground, aBackground);
-      return PR_TRUE;
     case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT:
-      aTextPaintStyle.GetIMESelectionColors(nsTextPaintStyle::eIndexSelRawText,
-                                            aForeground, aBackground);
-      return PR_TRUE;
     case nsISelectionController::SELECTION_IME_CONVERTEDTEXT:
-      aTextPaintStyle.GetIMESelectionColors(nsTextPaintStyle::eIndexConvText,
-                                            aForeground, aBackground);
-      return PR_TRUE;
     case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT:
-      aTextPaintStyle.GetIMESelectionColors(nsTextPaintStyle::eIndexSelConvText,
-                                            aForeground, aBackground);
+      aTextPaintStyle.GetIMESelectionColors(
+        nsTextPaintStyle::GetUnderlineStyleIndexForSelectionType(aType),
+        aForeground, aBackground);
       return PR_TRUE;
       
     default:
       *aForeground = aTextPaintStyle.GetTextColor();
       *aBackground = NS_RGBA(0,0,0,0);
       return PR_FALSE;
   }
 }
@@ -4702,39 +4762,63 @@ nsTextFrame::CalcContentOffsetsFromFrame
 
   offsets.content = GetContent();
   offsets.offset = offsets.secondaryOffset = selectedOffset;
   offsets.associateWithNext = mContentOffset == offsets.offset;
   return offsets;
 }
 
 PRBool
-nsTextFrame::HasSelectionOverflowingDecorations(nsPresContext* aPresContext,
-                                                float* aRatio)
-{
-  float ratio;
-  nsILookAndFeel* look = aPresContext->LookAndFeel();
-  look->GetMetric(nsILookAndFeel::eMetricFloat_IMEUnderlineRelativeSize, ratio);
-  if (aRatio)
-    *aRatio = ratio;
-  if (ratio <= 1.0f)
+nsTextFrame::CombineSelectionUnderlineRect(nsPresContext* aPresContext,
+                                           nsRect& aRect)
+{
+  if (aRect.IsEmpty())
     return PR_FALSE;
 
+  nsRect givenRect = aRect;
+
+  nsCOMPtr<nsIFontMetrics> fm;
+  nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
+  nsIThebesFontMetrics* tfm = static_cast<nsIThebesFontMetrics*>(fm.get());
+  gfxFontGroup* fontGroup = tfm->GetThebesFontGroup();
+  gfxFont* firstFont = fontGroup->GetFontAt(0);
+  if (!firstFont)
+    return PR_FALSE; // OOM
+  const gfxFont::Metrics& metrics = firstFont->GetMetrics();
+  gfxFloat underlineOffset = fontGroup->GetUnderlineOffset();
+  gfxFloat ascent = aPresContext->AppUnitsToGfxUnits(mAscent);
+
   SelectionDetails *details = GetSelectionDetails();
-  PRBool retval = PR_FALSE;
   for (SelectionDetails *sd = details; sd; sd = sd->mNext) {
-    if (sd->mStart != sd->mEnd &&
-        sd->mType & SelectionTypesWithDecorations) {
-      retval = PR_TRUE;
-      break;
+    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;
     }
+    nsRect decorationArea;
+    gfxSize size(aPresContext->AppUnitsToGfxUnits(aRect.width),
+                 metrics.underlineSize);
+    relativeSize = PR_MAX(relativeSize, 1.0f);
+    size.height *= relativeSize;
+    decorationArea =
+      nsCSSRendering::GetTextDecorationRect(aPresContext, size,
+                                            ascent, underlineOffset,
+                                            NS_STYLE_TEXT_DECORATION_UNDERLINE,
+                                            style);
+    aRect.UnionRect(aRect, decorationArea);
   }
   DestroySelectionDetails(details);
-  
-  return retval;
+
+  return !aRect.IsEmpty() && !givenRect.Contains(aRect);
 }
 
 //null range means the whole thing
 NS_IMETHODIMP
 nsTextFrame::SetSelected(nsPresContext* aPresContext,
                          nsIDOMRange *aRange,
                          PRBool aSelected,
                          nsSpread aSpread,
@@ -4816,19 +4900,19 @@ nsTextFrame::SetSelected(nsPresContext* 
     }
   }
   if (found) {
     // If the selection state is changed in this content, we need to reflow
     // to recompute the overflow area for underline of spellchecking or IME if
     // their underline is thicker than normal decoration line.
     PRBool didHaveSelectionUnderline =
              !!(mState & TEXT_SELECTION_UNDERLINE_OVERFLOWED);
-    PRBool willHaveSelectionUnderline =
-             aSelected && HasSelectionOverflowingDecorations(PresContext());
-    if (didHaveSelectionUnderline != willHaveSelectionUnderline) {
+    nsRect r(nsPoint(0, 0), GetSize());
+    if (didHaveSelectionUnderline != aSelected ||
+        (aSelected && CombineSelectionUnderlineRect(PresContext(), r))) {
       PresContext()->PresShell()->FrameNeedsReflow(this,
                                                    nsIPresShell::eStyleChange,
                                                    NS_FRAME_IS_DIRTY);
     }
     // Selection might change anything. Invalidate the overflow area.
     InvalidateOverflowRect();
   }
   if (aSpread == eSpreadDown)
--- a/layout/generic/test/Makefile.in
+++ b/layout/generic/test/Makefile.in
@@ -65,12 +65,16 @@ include $(topsrcdir)/config/rules.mk
 		test_bug448860.html \
 		test_bug460532.html \
 		test_bug468167.html \
 		test_bug469613.xul \
 		test_bug470212.html \
 		test_character_movement.html \
 		test_word_movement.html \
 		test_backspace_delete.xul \
+		test_selection_underline.html \
+		frame_selection_underline.xhtml \
+		frame_selection_underline-ref.xhtml \
+		frame_selection_underline.css \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/layout/generic/test/frame_selection_underline-ref.xhtml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" class="willBeRemoved">
+<head>
+<link rel="stylesheet" type="text/css" href="frame_selection_underline.css"/>
+<script type="text/javascript" src="decorationline.js"></script>
+</head>
+<body class="reference">
+  <div id="selectionSpellChecker" class="selection"><div
+    class="fontsize size1"><div
+      class="font1"><span id="t0" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t1" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div><div
+    class="fontsize size2"><div
+      class="font1"><span id="t2" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t3" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div><div
+    class="fontsize size3"><div
+      class="font1"><span id="t4" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t5" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div></div>
+  <div id="selectionIMERawInput" class="selection IMEselection"><div
+    class="fontsize size1"><div
+      class="font1"><span id="t6" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t7" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div><div
+    class="fontsize size2"><div
+      class="font1"><span id="t8" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t9" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div><div
+    class="fontsize size3"><div
+      class="font1"><span id="t10">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t11">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div></div>
+  <div id="selectionIMESelectedRawText" class="selection IMEselection"><div
+    class="fontsize size1"><div
+      class="font1"><span id="t12">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t13">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div><div
+    class="fontsize size2"><div
+      class="font1"><span id="t14">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t15">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div><div
+    class="fontsize size3"><div
+      class="font1"><span id="t16">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t17">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div></div>
+  <div id="selectionIMEConvertedText" class="selection IMEselection"><div
+    class="fontsize size1"><div
+      class="font1"><span id="t18">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t19">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div><div
+    class="fontsize size2"><div
+      class="font1"><span id="t20">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t21">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div><div
+    class="fontsize size3"><div
+      class="font1"><span id="t22">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t23">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div></div>
+  <div id="selectionIMESelectedConvertedText" class="selection IMEselection"><div
+    class="fontsize size1"><div
+      class="font1"><span id="t24">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t25">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div><div
+    class="fontsize size2"><div
+      class="font1"><span id="t26">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t27">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div><div
+    class="fontsize size3"><div
+      class="font1"><span id="t28">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t29">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/generic/test/frame_selection_underline.css
@@ -0,0 +1,69 @@
+html, body, div, span {
+  margin: 0;
+  padding: 0;
+  line-height: 1;
+}
+
+div.selection {
+  overflow: hidden;
+  display: block;
+  width: auto;
+  height: 100px;
+  white-space: nowrap;
+}
+
+div.fontsize {
+  display: inline-block;
+  width: auto;
+  height: auto;
+}
+
+div.fontsize.size1 {
+  font-size: 16px;
+}
+
+div.fontsize.size2 {
+  font-size: 32px;
+}
+
+div.fontsize.size3 {
+  font-size: 52px;
+}
+
+@font-face {
+  font-family: "AhemTest";
+  src: url(../../../fonts/Ahem.ttf);
+}
+
+@font-face {
+  font-family: "mplusTest";
+  src: url(../../../fonts/mplus/mplus-1p-regular.ttf);
+}
+
+div.font1,
+div.font2 {
+  display: inline-block;
+  width: 120px;
+  height: 140px;
+  margin: 10px 1px;
+  overflow: hidden;
+}
+
+div.font1 {
+  font-family: "AhemTest";
+}
+
+div.font2 {
+  font-family: "mplusTest";
+}
+
+span {
+  text-align: top;
+}
+
+body.reference div.IMEselection span,
+body.reference div.IMEselection span,
+body.reference div.IMEselection span,
+body.reference div.IMEselection span {
+  background-color: black;
+}
new file mode 100644
--- /dev/null
+++ b/layout/generic/test/frame_selection_underline.xhtml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" class="willBeRemoved">
+<head>
+<link rel="stylesheet" type="text/css" href="frame_selection_underline.css"/>
+<script type="text/javascript" src="decorationline.js"></script>
+<script type="text/javascript">
+<![CDATA[
+
+function onLoad()
+{
+  netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+
+  var docShell =
+    window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+          .getInterface(Components.interfaces.nsIWebNavigation)
+          .QueryInterface(Components.interfaces.nsIDocShell);
+  var controller =
+    docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+            .getInterface(Components.interfaces.nsISelectionDisplay)
+            .QueryInterface(Components.interfaces.nsISelectionController);
+
+  const selections = [
+    {
+      id: "selectionSpellChecker",
+      selectionType: controller.SELECTION_SPELLCHECK
+    },
+    {
+      id: "selectionIMERawInput",
+      selectionType: controller.SELECTION_IME_RAWINPUT
+    },
+    {
+      id: "selectionIMESelectedRawText",
+      selectionType: controller.SELECTION_IME_SELECTEDRAWTEXT
+    },
+    {
+      id: "selectionIMEConvertedText",
+      selectionType: controller.SELECTION_IME_CONVERTEDTEXT
+    },
+    {
+      id: "selectionIMESelectedConvertedText",
+      selectionType: controller.SELECTION_IME_SELECTEDCONVERTEDTEXT
+    },
+  ];
+
+  for (var i = 0; i < selections.length; i++) {
+    var sel = controller.getSelection(selections[i].selectionType);
+    var range = document.createRange();
+    range.selectNodeContents(document.getElementById(selections[i].id));
+    sel.addRange(range);
+  }
+
+  document.documentElement.removeAttribute("class");
+}
+
+]]>
+</script>
+</head>
+<body class="test" onload="onLoad();">
+  <div id="selectionSpellChecker" class="selection"><div
+    class="fontsize size1"><div
+      class="font1"><span id="t0" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t1" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div><div
+    class="fontsize size2"><div
+      class="font1"><span id="t2" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t3" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div><div
+    class="fontsize size3"><div
+      class="font1"><span id="t4" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t5" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div></div>
+  <div id="selectionIMERawInput" class="selection IMEselection"><div
+    class="fontsize size1"><div
+      class="font1"><span id="t6" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t7" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div><div
+    class="fontsize size2"><div
+      class="font1"><span id="t8" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t9" >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div><div
+    class="fontsize size3"><div
+      class="font1"><span id="t10">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t11">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div></div>
+  <div id="selectionIMESelectedRawText" class="selection IMEselection"><div
+    class="fontsize size1"><div
+      class="font1"><span id="t12">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t13">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div><div
+    class="fontsize size2"><div
+      class="font1"><span id="t14">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t15">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div><div
+    class="fontsize size3"><div
+      class="font1"><span id="t16">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t17">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div></div>
+  <div id="selectionIMEConvertedText" class="selection IMEselection"><div
+    class="fontsize size1"><div
+      class="font1"><span id="t18">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t19">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div><div
+    class="fontsize size2"><div
+      class="font1"><span id="t20">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t21">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div><div
+    class="fontsize size3"><div
+      class="font1"><span id="t22">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t23">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div></div>
+  <div id="selectionIMESelectedConvertedText" class="selection IMEselection"><div
+    class="fontsize size1"><div
+      class="font1"><span id="t24">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t25">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div><div
+    class="fontsize size2"><div
+      class="font1"><span id="t26">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t27">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div><div
+    class="fontsize size3"><div
+      class="font1"><span id="t28">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div><div
+      class="font2"><span id="t29">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></div></div></div>
+</body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/layout/generic/test/test_selection_underline.html
@@ -0,0 +1,412 @@
+<html>
+
+<head>
+  <title>Test for selection underline</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
+  <script type="text/javascript" src="../../base/tests/decoration_line_rendering.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<script type="text/javascript">
+
+// Canvas related code stolen from layout/base/tests/bidi_numeral_test.js which
+// stole from http://developer.mozilla.org/en/docs/Code_snippets:Canvas
+
+const kStyleNames = [ "none", "dotted", "dashed", "solid", "double", "wavy" ];
+
+var RemoteCanvas = function(aURL, aIsReference, aStyle, aRelativeSize) {
+  this.url = aURL;
+  this.id = kStyleNames[aStyle] + aRelativeSize;
+  if (aIsReference)
+    this.id = "ref-" + this.id;
+  this.isReference = aIsReference;
+  this.underlineStyle = aStyle;
+  this.underlineRelativeSize = aRelativeSize;
+  this.snapshot = null;
+};
+
+RemoteCanvas.CANVAS_WIDTH = 820;
+RemoteCanvas.CANVAS_HEIGHT = 500;
+
+RemoteCanvas.prototype.compare = function(otherCanvas, expected) {
+  var ret = compareSnapshots(this.snapshot, otherCanvas.snapshot, expected);
+  this.snapshotDataURL = ret[1];
+  otherCanvas.snapshotDataURL = ret[2];
+  return ret[0];
+}
+
+RemoteCanvas.prototype.load = function(callback) {
+  var iframe = document.createElement("iframe");
+  iframe.id = this.id;
+  iframe.width = RemoteCanvas.CANVAS_WIDTH + "px";
+  iframe.height = RemoteCanvas.CANVAS_HEIGHT + "px";
+  iframe.src = this.url;
+  var me = this;
+  iframe.addEventListener("load", function() {
+      me.remotePageLoaded(callback);
+    }, false);
+  window.document.body.appendChild(iframe);
+};
+
+const kIsMac = navigator.platform.indexOf("Mac") == 0;
+const kIsWin = navigator.platform.indexOf("Win") == 0;
+const kIsLinux = navigator.platform.indexOf("Linux") == 0;
+
+/**
+ * gFontMetrics has predictable font metrics:
+ *   0: font-family: Ahem.ttf;             font-size: 16px;
+ *   1: font-family: mplus-1p-regular.ttf; font-size: 16px;
+ *   2: font-family: Ahem.ttf;             font-size: 32px;
+ *   3: font-family: mplus-1p-regular.ttf; font-size: 32px;
+ *   4: font-family: Ahem.ttf;             font-size: 52px;
+ *   5: font-family: mplus-1p-regular.ttf; font-size: 52px;
+ */
+var gFontMetrics = [];
+if (kIsWin) {
+  gFontMetrics = [
+    { ascent: 13, offset: -2, lineHeight: 1 },
+    { ascent: 17, offset: -2, lineHeight: 1 },
+    { ascent: 26, offset: -4, lineHeight: 1 },
+    { ascent: 34, offset: -4, lineHeight: 2 },
+    { ascent: 42, offset: -7, lineHeight: 1 },
+    { ascent: 56, offset: -7, lineHeight: 3 }
+  ];
+}
+
+/*
+
+if (kIsMac) {
+  // XXX mnakano: I got following metrics on my Mac (both 10.4 and 10.5).
+  // However, on tinderbox machines, they are different values.  I'm not sure
+  // the reason.
+  gFontMetrics = [
+    { ascent: 13, offset: -2.127930, lineHeight: 1.000000 },
+    { ascent: 18, offset: -2.000000, lineHeight: 1.000000 },
+    { ascent: 26, offset: -4.255859, lineHeight: 1.000000 },
+    { ascent: 35, offset: -4.000000, lineHeight: 1.600098 },
+    { ascent: 42, offset: -6.915771, lineHeight: 1.040222 },
+    { ascent: 56, offset: -6.500000, lineHeight: 2.600159 }
+  ];
+}
+
+if (kIsLinux) {
+  // XXX mnakano: I got following metrics on my Ubuntu 8.10.  However, on
+  // tinderbox machines, they are different.  Probably, the values depend on
+  // the version of FreeType and Pango.  I bet we cannot test this on Linux
+  // without the way to get the actual font metrics from javascript.
+  gFontMetrics = [
+    { ascent: 13, offset: -1.015625, lineHeight: 1.000000 },
+    { ascent: 18, offset: -1.015625, lineHeight: 1.000000 },
+    { ascent: 26, offset: -2.031250, lineHeight: 1.000000 },
+    { ascent: 35, offset: -2.031250, lineHeight: 1.600003 },
+    { ascent: 42, offset: -3.300781, lineHeight: 1.040001 },
+    { ascent: 56, offset: -3.300781, lineHeight: 2.600002 }
+  ];
+}
+
+*/
+
+const kUnderlineStyles = [
+  { color: "rgb(100%,  0%,  0%)", isIMESelection: false },
+  { color: "rgb(  0%,100%,  0%)", isIMESelection: true  },
+  { color: "rgb(  0%,  0%,100%)", isIMESelection: true  },
+  { color: "rgb(100%,100%,  0%)", isIMESelection: true  },
+  { color: "rgb(  0%,100%,100%)", isIMESelection: true  }
+];
+
+function drawSelectionDecorationLines(aDocument, aStyle, aRelativeSize)
+{
+  var index = 0;
+  for (var i = 0; i < kUnderlineStyles.length; i++) {
+    var underlineStyle = kUnderlineStyles[i];
+    for (var j = 0; j < gFontMetrics.length; j++) {
+      var fontMetrics = gFontMetrics[j];
+      var element = aDocument.getElementById("t" + index++);
+      var pt = { x: element.getBoundingClientRect().left,
+                 y: element.getBoundingClientRect().top };
+      var width = 120;
+      if (underlineStyle.isIMESelection) {
+        pt.x += 1;
+        width -= 1;
+      }
+      drawDecorationLine(aDocument, underlineStyle.color, pt,
+        { width: width, height: fontMetrics.lineHeight * aRelativeSize },
+        fontMetrics.ascent, fontMetrics.offset, aStyle);
+    }
+  }
+}
+
+RemoteCanvas.prototype.remotePageLoaded = function(callback) {
+  var ldrFrame = document.getElementById(this.id);
+  if (this.isReference) {
+    var doc = ldrFrame.contentDocument;
+    drawSelectionDecorationLines(doc, this.underlineStyle,
+                                 this.underlineRelativeSize);
+    doc.documentElement.removeAttribute("class");
+  }
+  this.snapshot = snapshotWindow(ldrFrame.contentWindow);
+  callback(this);
+};
+
+var gPrefs = [
+  {
+    name: "ui.SpellCheckerUnderline",
+    type: "char",
+    newValue: "#ff0000"
+  },
+  {
+    name: "ui.IMERawInputBackground",
+    type: "char",
+    newValue: "#ffffff"
+  },
+  {
+    name: "ui.IMERawInputForeground",
+    type: "char",
+    newValue: "#000000"
+  },
+  {
+    name: "ui.IMERawInputUnderline",
+    type: "char",
+    newValue: "#00ff00"
+  },
+  {
+    name: "ui.IMESelectedRawTextBackground",
+    type: "char",
+    newValue: "#ffffff"
+  },
+  {
+    name: "ui.IMESelectedRawTextForeground",
+    type: "char",
+    newValue: "#000000"
+  },
+  {
+    name: "ui.IMESelectedRawTextUnderline",
+    type: "char",
+    newValue: "#0000ff"
+  },
+  {
+    name: "ui.IMEConvertedTextBackground",
+    type: "char",
+    newValue: "#ffffff"
+  },
+  {
+    name: "ui.IMEConvertedTextForeground",
+    type: "char",
+    newValue: "#000000"
+  },
+  {
+    name: "ui.IMEConvertedTextUnderline",
+    type: "char",
+    newValue: "#ffff00"
+  },
+  {
+    name: "ui.IMESelectedConvertedTextBackground",
+    type: "char",
+    newValue: "#ffffff"
+  },
+  {
+    name: "ui.IMESelectedConvertedTextForeground",
+    type: "char",
+    newValue: "#000000"
+  },
+  {
+    name: "ui.IMESelectedConvertedTextUnderline",
+    type: "char",
+    newValue: "#00ffff"
+  },
+  {
+    name: "ui.SpellCheckerUnderlineStyle",
+    type: "int",
+    newValue: 0
+  },
+  {
+    name: "ui.IMERawInputUnderlineStyle",
+    type: "int",
+    newValue: 0
+  },
+  {
+    name: "ui.IMESelectedRawTextUnderlineStyle",
+    type: "int",
+    newValue: 0
+  },
+  {
+    name: "ui.IMEConvertedTextUnderlineStyle",
+    type: "int",
+    newValue: 0
+  },
+  {
+    name: "ui.IMESelectedConvertedTextUnderlineStyle",
+    type: "int",
+    newValue: 0
+  },
+  {
+    name: "ui.SpellCheckerUnderlineRelativeSize",
+    type: "float",
+    newValue: 1.0
+  },
+  {
+    name: "ui.IMEUnderlineRelativeSize",
+    type: "float",
+    newValue: 1.0
+  }
+];
+
+function setPrefValue(aPrefs, aName, aType, aValue)
+{
+  if (aType == "char")
+    aPrefs.setCharPref(aName, aValue);
+  else if (aType == "int")
+    aPrefs.setIntPref(aName, aValue);
+  else if (aType == "float")
+    aPrefs.setIntPref(aName, aValue * 100);
+}
+
+const kPrefStyles = [ 0, 3, 1, 2, 4, 5 ];
+
+var gTests = [
+ { size: 1.0, style: kDecorationStyleNone   },
+ { size: 1.0, style: kDecorationStyleSolid  },
+ { size: 1.0, style: kDecorationStyleDotted },
+ { size: 1.0, style: kDecorationStyleDashed },
+ { size: 1.0, style: kDecorationStyleDouble },
+ { size: 1.0, style: kDecorationStyleWavy   },
+ { size: 2.0, style: kDecorationStyleNone   },
+ { size: 2.0, style: kDecorationStyleSolid  },
+ { size: 2.0, style: kDecorationStyleDotted },
+ { size: 2.0, style: kDecorationStyleDashed },
+ { size: 2.0, style: kDecorationStyleDouble },
+ { size: 2.0, style: kDecorationStyleWavy   }
+];
+
+function run()
+{
+  var test = gTests.shift();
+
+  if (!test) {
+    SimpleTest.finish();
+    cleanup();
+    return;
+  }
+
+  netscape.security.PrivilegeManager.enablePrivilege(
+   'UniversalPreferencesRead UniversalPreferencesWrite UniversalXPConnect');
+
+  var prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                        .getService(Components.interfaces.nsIPrefBranch);
+
+  setPrefValue(prefs, "ui.SpellCheckerUnderlineRelativeSize", "float",
+               test.size);
+  setPrefValue(prefs, "ui.IMEUnderlineRelativeSize", "float",
+               test.size);
+  setPrefValue(prefs, "ui.SpellCheckerUnderlineStyle", "int",
+               kPrefStyles[test.style]);
+  setPrefValue(prefs, "ui.IMERawInputUnderlineStyle", "int",
+               kPrefStyles[test.style]);
+  setPrefValue(prefs, "ui.IMESelectedRawTextUnderlineStyle", "int",
+               kPrefStyles[test.style]);
+  setPrefValue(prefs, "ui.IMEConvertedTextUnderlineStyle", "int",
+               kPrefStyles[test.style]);
+  setPrefValue(prefs, "ui.IMESelectedConvertedTextUnderlineStyle", "int",
+               kPrefStyles[test.style]);
+
+  doTest(test.style, test.size);
+}
+
+function doTest(aStyle, aSize)
+{
+
+  var canvases = [];
+  function callbackTestCanvas(canvas)
+  {
+    canvases.push(canvas);
+
+    if (canvases.length != 2)
+      return;
+
+    var result = !canvases[0].isReference ? canvases[0] : canvases[1];
+    var reference = canvases[0].isReference ? canvases[0] : canvases[1];
+
+    // when both canvases are loaded
+    ok(result.compare(reference, true),
+         "Rendering of reftest (style: " + kStyleNames[aStyle] +
+         ", size: " + aSize + ") is different\n" +
+         "RESULT=" + result.snapshotDataURL + "\n" +
+         "REFERENCE=" + reference.snapshotDataURL + "\n");
+
+    var iframe = window.document.getElementById(canvases[0].id);
+    iframe.parentNode.removeChild(iframe);
+    iframe = window.document.getElementById(canvases[1].id);
+    iframe.parentNode.removeChild(iframe);
+
+    canvases = [];
+
+    setTimeout(run, 0);
+  }
+
+  var testCanvas = new RemoteCanvas("frame_selection_underline.xhtml",
+                                    false, aStyle, aSize);
+  testCanvas.load(callbackTestCanvas);
+
+  var refFile = "frame_selection_underline-ref.xhtml";
+  var refCanvas = new RemoteCanvas(refFile, true, aStyle, aSize);
+  refCanvas.load(callbackTestCanvas);
+}
+
+function onLoad()
+{
+  SimpleTest.waitForExplicitFinish();
+
+  if (gFontMetrics.length == 0) {
+    todo(false, "test_selection_underline doesn't support this platform");
+    SimpleTest.finish();
+    return;
+  }
+
+  netscape.security.PrivilegeManager.enablePrivilege(
+   'UniversalPreferencesRead UniversalPreferencesWrite UniversalXPConnect');
+
+  var prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                        .getService(Components.interfaces.nsIPrefBranch);
+
+  for (var i = 0; i < gPrefs.length; i++) {
+    gPrefs[i].wasUserSetValue = prefs.prefHasUserValue(gPrefs[i].name);
+    if (gPrefs[i].wasUserSetValue) {
+      if (gPrefs[i].type == "char")
+        gPrefs[i].oldValue = prefs.getCharPref(gPrefs[i].name);
+      else if (gPrefs[i].type == "int")
+        gPrefs[i].oldValue = prefs.getIntPref(gPrefs[i].name);
+      else if (gPrefs[i].type == "float")
+        gPrefs[i].oldValue = prefs.getIntPref(gPrefs[i].name) / 100;
+    }
+    setPrefValue(prefs, gPrefs[i].name, gPrefs[i].type, gPrefs[i].newValue);
+  }
+
+  run();
+}
+
+function cleanup()
+{
+  netscape.security.PrivilegeManager.enablePrivilege(
+   'UniversalPreferencesRead UniversalPreferencesWrite UniversalXPConnect');
+
+  var prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                        .getService(Components.interfaces.nsIPrefBranch);
+
+  for (var i = 0; i < gPrefs.length; i++) {
+    prefs.clearUserPref(gPrefs[i].name);
+    if (gPrefs[i].wasUserSetValue)
+      setPrefValue(prefs, gPrefs[i].name, gPrefs[i].type, gPrefs[i].oldValue);
+  }
+}
+
+</script>
+
+</head>
+<body onload="onLoad();">
+
+<pre id="test">
+</pre>
+
+</body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/layout/reftests/fonts/Makefile.in
@@ -0,0 +1,50 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2007
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# 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 *****
+
+DEPTH		= ../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES =	Ahem.ttf \
+		$(NULL)
+
+libs:: $(_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/fonts
new file mode 100644
--- /dev/null
+++ b/layout/reftests/fonts/mplus/Makefile.in
@@ -0,0 +1,50 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Mozilla Foundation.
+# Portions created by the Initial Developer are Copyright (C) 2007
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# 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 *****
+
+DEPTH		= ../../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES =	mplus-1p-regular.ttf \
+		$(NULL)
+
+libs:: $(_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/fonts/mplus
--- a/layout/xul/base/src/nsTextBoxFrame.cpp
+++ b/layout/xul/base/src/nsTextBoxFrame.cpp
@@ -466,27 +466,27 @@ nsTextBoxFrame::DrawText(nsIRenderingCon
     // general case -- see below.)
     if (decorations & (NS_FONT_DECORATION_OVERLINE |
                        NS_FONT_DECORATION_UNDERLINE)) {
       fontMet->GetUnderline(offset, size);
       gfxFloat offsetPixel = presContext->AppUnitsToGfxUnits(offset);
       gfxFloat sizePixel = presContext->AppUnitsToGfxUnits(size);
       if (decorations & NS_FONT_DECORATION_UNDERLINE) {
         nsCSSRendering::PaintDecorationLine(ctx, underColor,
-                                            pt, gfxSize(width, sizePixel),
-                                            ascentPixel, offsetPixel,
-                                            NS_STYLE_TEXT_DECORATION_UNDERLINE,
-                                            NS_STYLE_BORDER_STYLE_SOLID);
+                          pt, gfxSize(width, sizePixel),
+                          ascentPixel, offsetPixel,
+                          NS_STYLE_TEXT_DECORATION_UNDERLINE,
+                          nsCSSRendering::DECORATION_STYLE_SOLID);
       }
       if (decorations & NS_FONT_DECORATION_OVERLINE) {
         nsCSSRendering::PaintDecorationLine(ctx, overColor,
-                                            pt, gfxSize(width, sizePixel),
-                                            ascentPixel, ascentPixel,
-                                            NS_STYLE_TEXT_DECORATION_OVERLINE,
-                                            NS_STYLE_BORDER_STYLE_SOLID);
+                          pt, gfxSize(width, sizePixel),
+                          ascentPixel, ascentPixel,
+                          NS_STYLE_TEXT_DECORATION_OVERLINE,
+                          nsCSSRendering::DECORATION_STYLE_SOLID);
       }
     }
 
     aRenderingContext.SetFont(fontMet);
 
     CalculateUnderline(aRenderingContext);
 
     aRenderingContext.SetColor(aOverrideColor ? *aOverrideColor : GetStyleColor()->mColor);
@@ -550,20 +550,19 @@ nsTextBoxFrame::DrawText(nsIRenderingCon
 
     // Strikeout is drawn on top of the text, per
     // http://www.w3.org/TR/CSS21/zindex.html point 7.2.1.4.1.1.
     if (decorations & NS_FONT_DECORATION_LINE_THROUGH) {
       fontMet->GetStrikeout(offset, size);
       gfxFloat offsetPixel = presContext->AppUnitsToGfxUnits(offset);
       gfxFloat sizePixel = presContext->AppUnitsToGfxUnits(size);
       nsCSSRendering::PaintDecorationLine(ctx, strikeColor,
-                                          pt, gfxSize(width, sizePixel),
-                                          ascentPixel, offsetPixel,
-                                          NS_STYLE_TEXT_DECORATION_LINE_THROUGH,
-                                          NS_STYLE_BORDER_STYLE_SOLID);
+                        pt, gfxSize(width, sizePixel), ascentPixel, offsetPixel,
+                        NS_STYLE_TEXT_DECORATION_LINE_THROUGH,
+                        nsCSSRendering::DECORATION_STYLE_SOLID);
     }
 }
 
 void nsTextBoxFrame::PaintOneShadow(gfxContext*      aCtx,
                                     const nsRect&    aTextRect,
                                     nsCSSShadowItem* aShadowDetails,
                                     const nscolor&   aForegroundColor,
                                     const nsRect&    aDirtyRect) {
--- a/widget/public/nsILookAndFeel.h
+++ b/widget/public/nsILookAndFeel.h
@@ -39,21 +39,20 @@
 #define __nsILookAndFeel
 #include "nsISupports.h"
 #include "nsColor.h"
 
   // for |#ifdef NS_DEBUG|
 struct nsSize;
 
 
-// {6672E0EA-C936-11DC-9BB7-0014850B592F}
+// {EE288A5F-B98B-4105-B4AE-884F21588A92}
 #define NS_ILOOKANDFEEL_IID \
-{ 0x6672e0ea, 0xc936, 0x11dc, \
-    { 0x9b, 0xb7, 0x00, 0x14, 0x85, 0x0b, 0x59, 0x2f} }
-
+{ 0xee288a5f, 0xb98b, 0x4105, \
+    { 0xb4, 0xae, 0x88, 0x4f, 0x21, 0x58, 0x8a, 0x92 } }
 
 class nsILookAndFeel: public nsISupports {
 public:
     NS_DECLARE_STATIC_IID_ACCESSOR(NS_ILOOKANDFEEL_IID)
 
   // When modifying this list, also modify nsXPLookAndFeel::sColorPrefs
   // in widget/src/xpwidgts/nsXPLookAndFeel.cpp.
   typedef enum {
@@ -86,16 +85,18 @@ public:
     eColor_IMESelectedRawTextUnderline,
     eColor_IMEConvertedTextBackground,
     eColor_IMEConvertedTextForeground,
     eColor_IMEConvertedTextUnderline,
     eColor_IMESelectedConvertedTextBackground,
     eColor_IMESelectedConvertedTextForeground,
     eColor_IMESelectedConvertedTextUnderline,
 
+    eColor_SpellCheckerUnderline,
+
     // New CSS 2 color definitions
     eColor_activeborder,
     eColor_activecaption,
     eColor_appworkspace,
     eColor_background,
     eColor_buttonface,
     eColor_buttonhighlight,
     eColor_buttonshadow,
@@ -290,23 +291,24 @@ public:
      * If true, clicking on a scrollbar (not as in dragging the thumb) defaults
      * to scrolling the view corresponding to the clicked point. Otherwise, we
      * only do so if the scrollbar is clicked using the middle mouse button or
      * if shift is pressed when the scrollbar is clicked.
      */
     eMetric_ScrollToClick,
 
     /**
-     * IME underline styles, the values should be NS_DECORATION_LINE_STYLE_*.
-     * They are defined below.
+     * IME and spell checker underline styles, the values should be
+     * NS_DECORATION_LINE_STYLE_*.  They are defined below.
      */
     eMetric_IMERawInputUnderlineStyle,
     eMetric_IMESelectedRawTextUnderlineStyle,
     eMetric_IMEConvertedTextUnderlineStyle,
     eMetric_IMESelectedConvertedTextUnderline,
+    eMetric_SpellCheckerUnderlineStyle,
 
     /**
      * If this metric != 0, show icons in menus.
      */
     eMetric_ImagesInMenus
   } nsMetricID;
 
   enum {
@@ -336,16 +338,17 @@ public:
     eMetricFloat_TextFieldHorizontalInsidePadding,
     eMetricFloat_TextAreaVerticalInsidePadding,
     eMetricFloat_TextAreaHorizontalInsidePadding,
     eMetricFloat_ListVerticalInsidePadding,
     eMetricFloat_ListHorizontalInsidePadding,
     eMetricFloat_ButtonVerticalInsidePadding,
     eMetricFloat_ButtonHorizontalInsidePadding,
     eMetricFloat_IMEUnderlineRelativeSize,
+    eMetricFloat_SpellCheckerUnderlineRelativeSize,
 
     // The width/height ratio of the cursor. If used, the CaretWidth int metric
     // should be added to the calculated caret width.
     eMetricFloat_CaretAspectRatio
   } nsMetricFloatID;
 
   NS_IMETHOD GetColor(const nsColorID aID, nscolor &aColor) = 0;
   NS_IMETHOD GetMetric(const nsMetricID aID, PRInt32 & aMetric) = 0;
@@ -389,42 +392,43 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsILookAnd
 
 
 	// On the Mac, GetColor(eColor_TextSelectForeground, color) returns this
 	// constant to specify that the foreground color should not be changed
 	// (ie. a colored text keeps its colors  when selected).
 	// Of course if other plaforms work like the Mac, they can use it too.
 #define NS_DONT_CHANGE_COLOR 	NS_RGB(0x01, 0x01, 0x01)
 
-// --------------------------------
-//  Special colors for eColor_IME*
-// --------------------------------
+// -----------------------------------------------------------------
+//  Special colors for eColor_IME* and eColor_SpellCheckerUnderline
+// -----------------------------------------------------------------
 
 // For background color only.
 #define NS_TRANSPARENT                NS_RGBA(0x01, 0x00, 0x00, 0x00)
 // For foreground color only.
 #define NS_SAME_AS_FOREGROUND_COLOR   NS_RGBA(0x02, 0x00, 0x00, 0x00)
 #define NS_40PERCENT_FOREGROUND_COLOR NS_RGBA(0x03, 0x00, 0x00, 0x00)
 
-#define NS_IS_IME_SPECIAL_COLOR(c) ((c) == NS_TRANSPARENT || \
-                                    (c) == NS_SAME_AS_FOREGROUND_COLOR || \
-                                    (c) == NS_40PERCENT_FOREGROUND_COLOR)
+#define NS_IS_SELECTION_SPECIAL_COLOR(c) ((c) == NS_TRANSPARENT || \
+                                          (c) == NS_SAME_AS_FOREGROUND_COLOR || \
+                                          (c) == NS_40PERCENT_FOREGROUND_COLOR)
 
 // -------------------------------------------------
 //  Underline styles for eMetric_IME*UnderlineStyle
 // -------------------------------------------------
 
 #define NS_UNDERLINE_STYLE_NONE   0
 #define NS_UNDERLINE_STYLE_DOTTED 1
 #define NS_UNDERLINE_STYLE_DASHED 2
 #define NS_UNDERLINE_STYLE_SOLID  3
 #define NS_UNDERLINE_STYLE_DOUBLE 4
+#define NS_UNDERLINE_STYLE_WAVY   5
 
 #define NS_IS_VALID_UNDERLINE_STYLE(s) \
-  (NS_UNDERLINE_STYLE_NONE <= (s) && (s) <= NS_UNDERLINE_STYLE_DOUBLE)
+  (NS_UNDERLINE_STYLE_NONE <= (s) && (s) <= NS_UNDERLINE_STYLE_WAVY)
 
 // ------------------------------------------
 //  Bits for eMetric_AlertNotificationOrigin
 // ------------------------------------------
 
 #define NS_ALERT_HORIZONTAL 1
 #define NS_ALERT_LEFT       2
 #define NS_ALERT_TOP        4
--- a/widget/src/beos/nsLookAndFeel.cpp
+++ b/widget/src/beos/nsLookAndFeel.cpp
@@ -125,16 +125,19 @@ nsresult nsLookAndFeel::NativeGetColor(c
     case eColor_IMERawInputUnderline:
     case eColor_IMEConvertedTextUnderline:
       aColor = NS_SAME_AS_FOREGROUND_COLOR;
       break;
     case eColor_IMESelectedRawTextUnderline:
     case eColor_IMESelectedConvertedTextUnderline:
       aColor = NS_TRANSPARENT;
       break;
+    case eColor_SpellCheckerUnderline:
+      aColor = NS_RGB(0xff, 0, 0);
+      break;
 	// two following colors get initialisation in XPLookAndFeel.
 	//eColor_TextSelectBackgroundDisabled,
     //eColor_TextSelectBackgroundAttention,
     
     //  CSS 2 Colors
     case eColor_activeborder:
       aColor = NS_RGB(0x88, 0x88, 0x88);
       break;
@@ -436,16 +439,19 @@ NS_IMETHODIMP nsLookAndFeel::GetMetric(c
     case eMetric_IMERawInputUnderlineStyle:
     case eMetric_IMEConvertedTextUnderlineStyle:
       aMetric = NS_UNDERLINE_STYLE_SOLID;
       break;
     case eMetric_IMESelectedRawTextUnderlineStyle:
     case eMetric_IMESelectedConvertedTextUnderline:
       aMetric = NS_UNDERLINE_STYLE_NONE;
       break;
+    case eMetric_SpellCheckerUnderlineStyle:
+      aMetric = NS_UNDERLINE_STYLE_WAVY;
+      break;
     default:
         aMetric = 0;
         res = NS_ERROR_FAILURE;
     }
   return res;
 }
 
 NS_IMETHODIMP nsLookAndFeel::GetMetric(const nsMetricFloatID aID, float & aMetric)
@@ -478,14 +484,17 @@ NS_IMETHODIMP nsLookAndFeel::GetMetric(c
         aMetric = 0.25f;
         break;
     case eMetricFloat_ButtonHorizontalInsidePadding:
         aMetric = 0.25f;
         break;
     case eMetricFloat_IMEUnderlineRelativeSize:
         aMetric = 1.0f;
         break;
+    case eMetricFloat_SpellCheckerUnderlineRelativeSize:
+        aMetric = 1.0f;
+        break;
     default:
         aMetric = -1.0;
         res = NS_ERROR_FAILURE;
     }
   return res;
 }
--- a/widget/src/cocoa/nsLookAndFeel.mm
+++ b/widget/src/cocoa/nsLookAndFeel.mm
@@ -150,17 +150,20 @@ nsresult nsLookAndFeel::NativeGetColor(c
     case eColor_IMERawInputUnderline:
     case eColor_IMEConvertedTextUnderline:
       aColor = NS_40PERCENT_FOREGROUND_COLOR;
       break;
     case eColor_IMESelectedRawTextUnderline:
     case eColor_IMESelectedConvertedTextUnderline:
       aColor = NS_SAME_AS_FOREGROUND_COLOR;
       break;
-      
+    case eColor_SpellCheckerUnderline:
+      aColor = NS_RGB(0xff, 0, 0);
+      break;
+
       //
       // css2 system colors http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
       //
       // It's really hard to effectively map these to the Appearance Manager properly,
       // since they are modeled word for word after the win32 system colors and don't have any 
       // real counterparts in the Mac world. I'm sure we'll be tweaking these for 
       // years to come. 
       //
@@ -665,16 +668,19 @@ NS_IMETHODIMP nsLookAndFeel::GetMetric(c
     }
       break;
     case eMetric_IMERawInputUnderlineStyle:
     case eMetric_IMEConvertedTextUnderlineStyle:
     case eMetric_IMESelectedRawTextUnderlineStyle:
     case eMetric_IMESelectedConvertedTextUnderline:
       aMetric = NS_UNDERLINE_STYLE_SOLID;
       break;
+    case eMetric_SpellCheckerUnderlineStyle:
+      aMetric = NS_UNDERLINE_STYLE_DOTTED;
+      break;
     default:
       aMetric = 0;
       res = NS_ERROR_FAILURE;
   }
   return res;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
@@ -709,15 +715,18 @@ NS_IMETHODIMP nsLookAndFeel::GetMetric(c
       aMetric = 0.5f;
       break;
     case eMetricFloat_ButtonHorizontalInsidePadding:
       aMetric = 0.5f;
       break;
     case eMetricFloat_IMEUnderlineRelativeSize:
       aMetric = 2.0f;
       break;
+    case eMetricFloat_SpellCheckerUnderlineRelativeSize:
+      aMetric = 2.0f;
+      break;
     default:
       aMetric = -1.0;
       res = NS_ERROR_FAILURE;
   }
 
   return res;
 }
--- a/widget/src/gtk2/nsLookAndFeel.cpp
+++ b/widget/src/gtk2/nsLookAndFeel.cpp
@@ -148,16 +148,19 @@ nsresult nsLookAndFeel::NativeGetColor(c
     case eColor_IMERawInputUnderline:
     case eColor_IMEConvertedTextUnderline:
         aColor = NS_SAME_AS_FOREGROUND_COLOR;
         break;
     case eColor_IMESelectedRawTextUnderline:
     case eColor_IMESelectedConvertedTextUnderline:
         aColor = NS_TRANSPARENT;
         break;
+    case eColor_SpellCheckerUnderline:
+      aColor = NS_RGB(0xff, 0, 0);
+      break;
 
         // css2  http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
     case eColor_activeborder:
         // active window border
         aColor = GDK_COLOR_TO_NS_RGB(mStyle->bg[GTK_STATE_NORMAL]);
         break;
     case eColor_activecaption:
         // active window caption background
@@ -573,16 +576,19 @@ NS_IMETHODIMP nsLookAndFeel::GetMetric(c
     case eMetric_IMERawInputUnderlineStyle:
     case eMetric_IMEConvertedTextUnderlineStyle:
         aMetric = NS_UNDERLINE_STYLE_SOLID;
         break;
     case eMetric_IMESelectedRawTextUnderlineStyle:
     case eMetric_IMESelectedConvertedTextUnderline:
         aMetric = NS_UNDERLINE_STYLE_NONE;
         break;
+    case eMetric_SpellCheckerUnderlineStyle:
+        aMetric = NS_UNDERLINE_STYLE_WAVY;
+        break;
     case eMetric_ImagesInMenus:
         aMetric = moz_gtk_images_in_menus();
         break;
     default:
         aMetric = 0;
         res     = NS_ERROR_FAILURE;
     }
 
@@ -621,16 +627,19 @@ NS_IMETHODIMP nsLookAndFeel::GetMetric(c
         aMetric = 0.25f;
         break;
     case eMetricFloat_ButtonHorizontalInsidePadding:
         aMetric = 0.25f;
         break;
     case eMetricFloat_IMEUnderlineRelativeSize:
         aMetric = 1.0f;
         break;
+    case eMetricFloat_SpellCheckerUnderlineRelativeSize:
+        aMetric = 1.0f;
+        break;
     case eMetricFloat_CaretAspectRatio:
         aMetric = sCaretRatio;
         break;
     default:
         aMetric = -1.0;
         res = NS_ERROR_FAILURE;
     }
     return res;
--- a/widget/src/os2/nsLookAndFeel.cpp
+++ b/widget/src/os2/nsLookAndFeel.cpp
@@ -109,16 +109,19 @@ nsresult nsLookAndFeel::NativeGetColor(c
     case eColor_IMERawInputUnderline:
     case eColor_IMEConvertedTextUnderline:
         aColor = NS_SAME_AS_FOREGROUND_COLOR;
         return NS_OK;
     case eColor_IMESelectedRawTextUnderline:
     case eColor_IMESelectedConvertedTextUnderline:
         aColor = NS_TRANSPARENT;
         return NS_OK;
+    case eColor_SpellCheckerUnderline:
+        aColor = NS_RGB(0xff, 0, 0);
+        return NS_OK;
 
     // New CSS 2 Color definitions
     case eColor_activeborder:
       idx = SYSCLR_ACTIVEBORDER;
       break;
     case eColor_activecaption:
       idx = SYSCLR_ACTIVETITLETEXT;
       break;
@@ -384,16 +387,19 @@ NS_IMETHODIMP nsLookAndFeel::GetMetric(c
     case eMetric_IMERawInputUnderlineStyle:
     case eMetric_IMEConvertedTextUnderlineStyle:
         aMetric = NS_UNDERLINE_STYLE_SOLID;
         break;
     case eMetric_IMESelectedRawTextUnderlineStyle:
     case eMetric_IMESelectedConvertedTextUnderline:
         aMetric = NS_UNDERLINE_STYLE_NONE;
         break;
+    case eMetric_SpellCheckerUnderlineStyle:
+        aMetric = NS_UNDERLINE_STYLE_WAVY;
+        break;
 
     default:
         aMetric = 0;
         res = NS_ERROR_FAILURE;
   }
   return res;
 }
 
@@ -427,16 +433,19 @@ NS_IMETHODIMP nsLookAndFeel::GetMetric(c
         aMetric = 0.25f;
         break;
     case eMetricFloat_ButtonHorizontalInsidePadding:
         aMetric = 0.25f;
         break;
     case eMetricFloat_IMEUnderlineRelativeSize:
         aMetric = 1.0f;
         break;
+    case eMetricFloat_SpellCheckerUnderlineRelativeSize:
+        aMetric = 1.0f;
+        break;
     default:
         aMetric = -1.0;
         res = NS_ERROR_FAILURE;
   }
   return res;
 }
 
 
--- a/widget/src/photon/nsLookAndFeel.cpp
+++ b/widget/src/photon/nsLookAndFeel.cpp
@@ -114,16 +114,19 @@ nsresult nsLookAndFeel::NativeGetColor(c
     case eColor_IMERawInputUnderline:
     case eColor_IMEConvertedTextUnderline:
       aColor = NS_SAME_AS_FOREGROUND_COLOR;
       break;
     case eColor_IMESelectedRawTextUnderline:
     case eColor_IMESelectedConvertedTextUnderline:
       aColor = NS_TRANSPARENT;
       break;
+    case eColor_SpellCheckerUnderline:
+      aColor = NS_RGB(0xff, 0, 0);
+      break;
 
 		// css2  http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
 	  case eColor_activeborder:
 		aColor = PH_TO_NS_RGB(Pg_BLACK);
 		break;
 	  case eColor_activecaption:
 		aColor = PH_TO_NS_RGB(Pg_YELLOW);
 		break;
@@ -376,16 +379,19 @@ NS_IMETHODIMP nsLookAndFeel::GetMetric(c
   case eMetric_IMERawInputUnderlineStyle:
   case eMetric_IMEConvertedTextUnderlineStyle:
     aMetric = NS_UNDERLINE_STYLE_SOLID;
     break;
   case eMetric_IMESelectedRawTextUnderlineStyle:
   case eMetric_IMESelectedConvertedTextUnderline:
     aMetric = NS_UNDERLINE_STYLE_NONE;
     break;
+  case eMetric_SpellCheckerUnderlineStyle:
+    aMetric = NS_UNDERLINE_STYLE_WAVY;
+    break;
 
   default:
     aMetric = 0;
     res     = NS_ERROR_FAILURE;
   }
 
   return res;
 }
@@ -422,15 +428,18 @@ NS_IMETHODIMP nsLookAndFeel::GetMetric(c
     aMetric = 0.25f;
     break;
   case eMetricFloat_ButtonHorizontalInsidePadding:
     aMetric = 0.25f;
     break;
   case eMetricFloat_IMEUnderlineRelativeSize:
     aMetric = 1.0f;
     break;
+  case eMetricFloat_SpellCheckerUnderlineRelativeSize:
+    aMetric = 1.0f;
+    break;
   default:
     aMetric = -1.0;
     res = NS_ERROR_FAILURE;
   }
  
   return res;
 }
--- a/widget/src/qt/nsLookAndFeel.cpp
+++ b/widget/src/qt/nsLookAndFeel.cpp
@@ -137,16 +137,20 @@ nsresult nsLookAndFeel::NativeGetColor(c
       aColor = NS_SAME_AS_FOREGROUND_COLOR;
       break;
 
     case eColor_IMESelectedRawTextUnderline:
     case eColor_IMESelectedConvertedTextUnderline:
       aColor = NS_TRANSPARENT;
       break;
 
+    case eColor_SpellCheckerUnderline:
+      aColor = NS_RGB(0xff, 0, 0);
+      break;
+
     case eColor_activeborder:
       aColor = QCOLOR_TO_NS_RGB(palette.color(QPalette::Normal, QPalette::Window));
       break;
 
     case eColor_activecaption:
       aColor = QCOLOR_TO_NS_RGB(palette.color(QPalette::Normal, QPalette::Window));
       break;
 
@@ -476,16 +480,20 @@ NS_IMETHODIMP nsLookAndFeel::GetMetric(c
       aMetric = eMetric_ScrollThumbStyleProportional;
       break;
 
     case eMetric_WindowsDefaultTheme:
       aMetric = 0;
       res = NS_ERROR_NOT_IMPLEMENTED;
       break;
 
+    case eMetric_SpellCheckerUnderlineStyle:
+      aMetric = NS_UNDERLINE_STYLE_WAVY;
+      break;
+
     default:
       aMetric = 0;
       res = NS_ERROR_FAILURE;
   }
   return res;
 }
 
 #ifdef NS_LOOKANDFEEL_DEBUG
@@ -546,15 +554,19 @@ NS_IMETHODIMP nsLookAndFeel::GetMetric(c
     case eMetricFloat_ButtonHorizontalInsidePadding:
       aMetric = 0.25f;
       break;
 
     case eMetricFloat_IMEUnderlineRelativeSize:
       aMetric = 1.0f;
       break;
 
+    case eMetricFloat_SpellCheckerUnderlineRelativeSize:
+      aMetric = 1.0f;
+      break;
+
     default:
       aMetric = -1.0;
       res = NS_ERROR_FAILURE;
       break;
   }
   return res;
 }
--- a/widget/src/windows/nsLookAndFeel.cpp
+++ b/widget/src/windows/nsLookAndFeel.cpp
@@ -157,16 +157,19 @@ nsresult nsLookAndFeel::NativeGetColor(c
     case eColor_IMERawInputUnderline:
     case eColor_IMEConvertedTextUnderline:
         aColor = NS_SAME_AS_FOREGROUND_COLOR;
         return NS_OK;
     case eColor_IMESelectedRawTextUnderline:
     case eColor_IMESelectedConvertedTextUnderline:
         aColor = NS_TRANSPARENT;
         return NS_OK;
+    case eColor_SpellCheckerUnderline:
+        aColor = NS_RGB(0xff, 0, 0);
+        return NS_OK;
 
     // New CSS 2 Color definitions
     case eColor_activeborder:
       idx = COLOR_ACTIVEBORDER;
       break;
     case eColor_activecaption:
       idx = COLOR_ACTIVECAPTION;
       break;
@@ -594,16 +597,19 @@ NS_IMETHODIMP nsLookAndFeel::GetMetric(c
     case eMetric_IMERawInputUnderlineStyle:
     case eMetric_IMEConvertedTextUnderlineStyle:
         aMetric = NS_UNDERLINE_STYLE_DASHED;
         break;
     case eMetric_IMESelectedRawTextUnderlineStyle:
     case eMetric_IMESelectedConvertedTextUnderline:
         aMetric = NS_UNDERLINE_STYLE_NONE;
         break;
+    case eMetric_SpellCheckerUnderlineStyle:
+        aMetric = NS_UNDERLINE_STYLE_WAVY;
+        break;
     default:
         aMetric = 0;
         res = NS_ERROR_FAILURE;
     }
   return res;
 }
 
 NS_IMETHODIMP nsLookAndFeel::GetMetric(const nsMetricFloatID aID, float & aMetric)
@@ -636,16 +642,19 @@ NS_IMETHODIMP nsLookAndFeel::GetMetric(c
         aMetric = 0.25f;
         break;
     case eMetricFloat_ButtonHorizontalInsidePadding:
         aMetric = 0.25f;
         break;
     case eMetricFloat_IMEUnderlineRelativeSize:
         aMetric = 1.0f;
         break;
+    case eMetricFloat_SpellCheckerUnderlineRelativeSize:
+        aMetric = 1.0f;
+        break;
     default:
         aMetric = -1.0;
         res = NS_ERROR_FAILURE;
     }
   return res;
 }
 
 /* virtual */
--- a/widget/src/xpwidgets/nsXPLookAndFeel.cpp
+++ b/widget/src/xpwidgets/nsXPLookAndFeel.cpp
@@ -116,16 +116,18 @@ nsLookAndFeelIntPref nsXPLookAndFeel::sI
   { "ui.IMERawInputUnderlineStyle",
     eMetric_IMERawInputUnderlineStyle, PR_FALSE, nsLookAndFeelTypeInt, 0 },
   { "ui.IMESelectedRawTextUnderlineStyle",
     eMetric_IMESelectedRawTextUnderlineStyle, PR_FALSE, nsLookAndFeelTypeInt, 0 },
   { "ui.IMEConvertedTextUnderlineStyle",
     eMetric_IMEConvertedTextUnderlineStyle, PR_FALSE, nsLookAndFeelTypeInt, 0 },
   { "ui.IMESelectedConvertedTextUnderlineStyle",
     eMetric_IMESelectedConvertedTextUnderline, PR_FALSE, nsLookAndFeelTypeInt, 0 },
+  { "ui.SpellCheckerUnderlineStyle",
+    eMetric_SpellCheckerUnderlineStyle, PR_FALSE, nsLookAndFeelTypeInt, 0 },
 };
 
 nsLookAndFeelFloatPref nsXPLookAndFeel::sFloatPrefs[] =
 {
   { "ui.textFieldVerticalInsidePadding",
     eMetricFloat_TextFieldVerticalInsidePadding, PR_FALSE, nsLookAndFeelTypeFloat, 0 },
   { "ui.textFieldHorizontalInsidePadding",
     eMetricFloat_TextFieldHorizontalInsidePadding, PR_FALSE, nsLookAndFeelTypeFloat, 0 },
@@ -138,16 +140,19 @@ nsLookAndFeelFloatPref nsXPLookAndFeel::
   { "ui.listHorizontalInsidePadding",
     eMetricFloat_ListHorizontalInsidePadding, PR_FALSE, nsLookAndFeelTypeFloat, 0 },
   { "ui.buttonVerticalInsidePadding", eMetricFloat_ButtonVerticalInsidePadding,
     PR_FALSE, nsLookAndFeelTypeFloat, 0 },
   { "ui.buttonHorizontalInsidePadding", eMetricFloat_ButtonHorizontalInsidePadding,
     PR_FALSE, nsLookAndFeelTypeFloat, 0 },
   { "ui.IMEUnderlineRelativeSize", eMetricFloat_IMEUnderlineRelativeSize,
     PR_FALSE, nsLookAndFeelTypeFloat, 0 },
+  { "ui.SpellCheckerUnderlineRelativeSize",
+    eMetricFloat_SpellCheckerUnderlineRelativeSize, PR_FALSE,
+    nsLookAndFeelTypeFloat, 0 },
   { "ui.caretAspectRatio", eMetricFloat_CaretAspectRatio, PR_FALSE,
     nsLookAndFeelTypeFloat, 0 },
 };
 
 
 // This array MUST be kept in the same order as the color list in nsILookAndFeel.h.
 /* XXX If you add any strings longer than
  * "ui.IMESelectedConvertedTextBackground"
@@ -179,16 +184,17 @@ const char nsXPLookAndFeel::sColorPrefs[
   "ui.IMESelectedRawTextForeground",
   "ui.IMESelectedRawTextUnderline",
   "ui.IMEConvertedTextBackground",
   "ui.IMEConvertedTextForeground",
   "ui.IMEConvertedTextUnderline",
   "ui.IMESelectedConvertedTextBackground",
   "ui.IMESelectedConvertedTextForeground",
   "ui.IMESelectedConvertedTextUnderline",
+  "ui.SpellCheckerUnderline",
   "ui.activeborder",
   "ui.activecaption",
   "ui.appworkspace",
   "ui.background",
   "ui.buttonface",
   "ui.buttonhighlight",
   "ui.buttonshadow",
   "ui.buttontext",
@@ -480,17 +486,18 @@ nsXPLookAndFeel::IsSpecialColor(const ns
     case eColor_IMESelectedRawTextForeground:
     case eColor_IMESelectedConvertedTextForeground:
     case eColor_IMERawInputForeground:
     case eColor_IMEConvertedTextForeground:
     case eColor_IMERawInputUnderline:
     case eColor_IMEConvertedTextUnderline:
     case eColor_IMESelectedRawTextUnderline:
     case eColor_IMESelectedConvertedTextUnderline:
-      return NS_IS_IME_SPECIAL_COLOR(aColor);
+    case eColor_SpellCheckerUnderline:
+      return NS_IS_SELECTION_SPECIAL_COLOR(aColor);
     default:
       /*
        * In GetColor(), every color that is not a special color is color
        * corrected. Use PR_FALSE to make other colors color corrected.
        */
       return PR_FALSE;
   }
   return PR_FALSE;