Bug 1074735 pt 2 - Support drawing CSS text decorations (underline, overline, strikeout) on vertical text frames. r=smontagu
☠☠ backed out by be609374852a ☠ ☠
authorJonathan Kew <jkew@mozilla.com>
Wed, 08 Oct 2014 10:32:55 +0100
changeset 232552 32c4d5043b1805cc6cddd2a2fa05ae222585ae66
parent 232551 1632a36342d4fd4926960144903ccdac12f48f5c
child 232553 be609374852ab834c55bb10121d0be2c65368aa4
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmontagu
bugs1074735
milestone35.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1074735 pt 2 - Support drawing CSS text decorations (underline, overline, strikeout) on vertical text frames. r=smontagu
layout/base/nsCSSRendering.cpp
layout/base/nsCSSRendering.h
layout/generic/nsTextFrame.cpp
layout/xul/nsTextBoxFrame.cpp
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -3812,192 +3812,215 @@ nsCSSRendering::DrawTableBorderSegment(n
   }
 
   ctx->SetAntialiasMode(oldMode);
 }
 
 // End table border-collapsing section
 
 gfxRect
-nsCSSRendering::ExpandPaintingRectForDecorationLine(nsIFrame* aFrame,
-                                                    const uint8_t aStyle,
-                                                    const gfxRect& aClippedRect,
-                                                    const gfxFloat aXInFrame,
-                                                    const gfxFloat aCycleLength)
+nsCSSRendering::ExpandPaintingRectForDecorationLine(
+                  nsIFrame* aFrame,
+                  const uint8_t aStyle,
+                  const gfxRect& aClippedRect,
+                  const gfxFloat aICoordInFrame,
+                  const gfxFloat aCycleLength,
+                  bool aVertical)
 {
   switch (aStyle) {
     case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED:
     case NS_STYLE_TEXT_DECORATION_STYLE_DASHED:
     case NS_STYLE_TEXT_DECORATION_STYLE_WAVY:
       break;
     default:
       NS_ERROR("Invalid style was specified");
       return aClippedRect;
   }
 
   nsBlockFrame* block = nullptr;
   // Note that when we paint the decoration lines in relative positioned
   // box, we should paint them like all of the boxes are positioned as static.
-  nscoord frameXInBlockAppUnits = 0;
+  nscoord framePosInBlockAppUnits = 0;
   for (nsIFrame* f = aFrame; f; f = f->GetParent()) {
     block = do_QueryFrame(f);
     if (block) {
       break;
     }
-    frameXInBlockAppUnits += f->GetNormalPosition().x;
+    framePosInBlockAppUnits += aVertical ?
+      f->GetNormalPosition().y : f->GetNormalPosition().x;
   }
 
   NS_ENSURE_TRUE(block, aClippedRect);
 
   nsPresContext *pc = aFrame->PresContext();
-  gfxFloat frameXInBlock = pc->AppUnitsToGfxUnits(frameXInBlockAppUnits);
-  int32_t rectXInBlock = int32_t(NS_round(frameXInBlock + aXInFrame));
-  int32_t extraLeft =
-    rectXInBlock - (rectXInBlock / int32_t(aCycleLength) * aCycleLength);
+  gfxFloat framePosInBlock = pc->AppUnitsToGfxUnits(framePosInBlockAppUnits);
+  int32_t rectPosInBlock =
+    int32_t(NS_round(framePosInBlock + aICoordInFrame));
+  int32_t extraStartEdge =
+    rectPosInBlock - (rectPosInBlock / int32_t(aCycleLength) * aCycleLength);
   gfxRect rect(aClippedRect);
-  rect.x -= extraLeft;
-  rect.width += extraLeft;
+  if (aVertical) {
+    rect.y -= extraStartEdge;
+    rect.height += extraStartEdge;
+  } else {
+    rect.x -= extraStartEdge;
+    rect.width += extraStartEdge;
+  }
   return rect;
 }
 
 void
 nsCSSRendering::PaintDecorationLine(nsIFrame* aFrame,
                                     gfxContext* aGfxContext,
                                     const gfxRect& aDirtyRect,
                                     const nscolor aColor,
                                     const gfxPoint& aPt,
-                                    const gfxFloat aXInFrame,
+                                    const gfxFloat aICoordInFrame,
                                     const gfxSize& aLineSize,
                                     const gfxFloat aAscent,
                                     const gfxFloat aOffset,
                                     const uint8_t aDecoration,
                                     const uint8_t aStyle,
+                                    bool aVertical,
                                     const gfxFloat aDescentLimit)
 {
   NS_ASSERTION(aStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE, "aStyle is none");
 
   gfxRect rect =
     GetTextDecorationRectInternal(aPt, aLineSize, aAscent, aOffset,
-                                  aDecoration, aStyle, aDescentLimit);
+                                  aDecoration, aStyle, aVertical,
+                                  aDescentLimit);
   if (rect.IsEmpty() || !rect.Intersects(aDirtyRect)) {
     return;
   }
 
   if (aDecoration != NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE &&
       aDecoration != NS_STYLE_TEXT_DECORATION_LINE_OVERLINE &&
       aDecoration != NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
     NS_ERROR("Invalid decoration value!");
     return;
   }
 
-  gfxFloat lineHeight = std::max(NS_round(aLineSize.height), 1.0);
+  gfxFloat lineThickness = std::max(NS_round(aLineSize.height), 1.0);
   bool contextIsSaved = false;
 
   gfxFloat oldLineWidth;
   nsRefPtr<gfxPattern> oldPattern;
 
   switch (aStyle) {
     case NS_STYLE_TEXT_DECORATION_STYLE_SOLID:
     case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE:
       oldLineWidth = aGfxContext->CurrentLineWidth();
       oldPattern = aGfxContext->GetPattern();
       break;
     case NS_STYLE_TEXT_DECORATION_STYLE_DASHED: {
       aGfxContext->Save();
       contextIsSaved = true;
       aGfxContext->Clip(rect);
-      gfxFloat dashWidth = lineHeight * DOT_LENGTH * DASH_LENGTH;
+      gfxFloat dashWidth = lineThickness * DOT_LENGTH * DASH_LENGTH;
       gfxFloat dash[2] = { dashWidth, dashWidth };
       aGfxContext->SetLineCap(gfxContext::LINE_CAP_BUTT);
       aGfxContext->SetDash(dash, 2, 0.0);
       rect = ExpandPaintingRectForDecorationLine(aFrame, aStyle, rect,
-                                                 aXInFrame, dashWidth * 2);
+                                                 aICoordInFrame,
+                                                 dashWidth * 2,
+                                                 aVertical);
       // We should continue to draw the last dash even if it is not in the rect.
       rect.width += dashWidth;
       break;
     }
     case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED: {
       aGfxContext->Save();
       contextIsSaved = true;
       aGfxContext->Clip(rect);
-      gfxFloat dashWidth = lineHeight * DOT_LENGTH;
+      gfxFloat dashWidth = lineThickness * DOT_LENGTH;
       gfxFloat dash[2];
-      if (lineHeight > 2.0) {
+      if (lineThickness > 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);
       rect = ExpandPaintingRectForDecorationLine(aFrame, aStyle, rect,
-                                                 aXInFrame, dashWidth * 2);
+                                                 aICoordInFrame,
+                                                 dashWidth * 2,
+                                                 aVertical);
       // We should continue to draw the last dot even if it is not in the rect.
       rect.width += dashWidth;
       break;
     }
     case NS_STYLE_TEXT_DECORATION_STYLE_WAVY:
       aGfxContext->Save();
       contextIsSaved = true;
       aGfxContext->Clip(rect);
-      if (lineHeight > 2.0) {
+      if (lineThickness > 2.0) {
         aGfxContext->SetAntialiasMode(AntialiasMode::SUBPIXEL);
       } 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(AntialiasMode::NONE);
       }
       break;
     default:
       NS_ERROR("Invalid style value!");
       return;
   }
 
   // The y position should be set to the middle of the line.
-  rect.y += lineHeight / 2;
+  rect.y += lineThickness / 2;
 
   aGfxContext->SetColor(gfxRGBA(aColor));
-  aGfxContext->SetLineWidth(lineHeight);
+  aGfxContext->SetLineWidth(lineThickness);
   switch (aStyle) {
     case NS_STYLE_TEXT_DECORATION_STYLE_SOLID:
+    case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED:
+    case NS_STYLE_TEXT_DECORATION_STYLE_DASHED:
       aGfxContext->NewPath();
-      aGfxContext->MoveTo(rect.TopLeft());
-      aGfxContext->LineTo(rect.TopRight());
+      if (aVertical) {
+        aGfxContext->MoveTo(rect.TopLeft());
+        aGfxContext->LineTo(rect.BottomLeft());
+      } else {
+        aGfxContext->MoveTo(rect.TopLeft());
+        aGfxContext->LineTo(rect.TopRight());
+      }
       aGfxContext->Stroke();
       break;
     case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE:
       /**
        *  We are drawing double line as:
        *
        * +-------------------------------------------+
        * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
-       * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
+       * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
        * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
        * |                                           |
        * |                                           |
        * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
-       * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
+       * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
        * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
        * +-------------------------------------------+
        */
       aGfxContext->NewPath();
-      aGfxContext->MoveTo(rect.TopLeft());
-      aGfxContext->LineTo(rect.TopRight());
-      rect.height -= lineHeight;
-      aGfxContext->MoveTo(rect.BottomLeft());
-      aGfxContext->LineTo(rect.BottomRight());
-      aGfxContext->Stroke();
-      break;
-    case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED:
-    case NS_STYLE_TEXT_DECORATION_STYLE_DASHED:
-      aGfxContext->NewPath();
-      aGfxContext->MoveTo(rect.TopLeft());
-      aGfxContext->LineTo(rect.TopRight());
+      if (aVertical) {
+        aGfxContext->MoveTo(rect.TopLeft());
+        aGfxContext->LineTo(rect.BottomLeft());
+        rect.width -= lineThickness;
+        aGfxContext->MoveTo(rect.TopRight());
+        aGfxContext->LineTo(rect.BottomRight());
+      } else {
+        aGfxContext->MoveTo(rect.TopLeft());
+        aGfxContext->LineTo(rect.TopRight());
+        rect.height -= lineThickness;
+        aGfxContext->MoveTo(rect.BottomLeft());
+        aGfxContext->LineTo(rect.BottomRight());
+      }
       aGfxContext->Stroke();
       break;
     case NS_STYLE_TEXT_DECORATION_STYLE_WAVY: {
       /**
        *  We are drawing wavy line as:
        *
        *  P: Path, X: Painted pixel
        *
@@ -4018,68 +4041,88 @@ nsCSSRendering::PaintDecorationLine(nsIF
        *     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.
+       *
+       * In the vertical case, swap horizontal and vertical coordinates and
+       * directions in the above description.
        */
 
-      gfxFloat adv = rect.Height() - lineHeight;
-      gfxFloat flatLengthAtVertex = std::max((lineHeight - 1.0) * 2.0, 1.0);
+      gfxFloat& rectICoord = aVertical ? rect.y : rect.x;
+      gfxFloat& rectISize = aVertical ? rect.height : rect.width;
+      const gfxFloat rectBSize = aVertical ? rect.width : rect.height;
+
+      const gfxFloat adv = rectBSize - lineThickness;
+      const gfxFloat flatLengthAtVertex =
+        std::max((lineThickness - 1.0) * 2.0, 1.0);
 
       // Align the start of wavy lines to the nearest ancestor block.
-      gfxFloat cycleLength = 2 * (adv + flatLengthAtVertex);
+      const gfxFloat cycleLength = 2 * (adv + flatLengthAtVertex);
       rect = ExpandPaintingRectForDecorationLine(aFrame, aStyle, rect,
-                                                 aXInFrame, cycleLength);
+                                                 aICoordInFrame, cycleLength,
+                                                 aVertical);
       // figure out if we can trim whole cycles from the left and right edges
       // of the line, to try and avoid creating an unnecessarily long and
       // complex path
-      int32_t skipCycles = floor((aDirtyRect.x - rect.x) / cycleLength);
+      const gfxFloat dirtyRectICoord = aVertical ? aDirtyRect.y : aDirtyRect.x;
+      int32_t skipCycles = floor((dirtyRectICoord - rectICoord) / cycleLength);
       if (skipCycles > 0) {
-        rect.x += skipCycles * cycleLength;
-        rect.width -= skipCycles * cycleLength;
+        rectICoord += skipCycles * cycleLength;
+        rectISize -= skipCycles * cycleLength;
       }
 
-      rect.x += lineHeight / 2.0;
+      rectICoord += lineThickness / 2.0;
       gfxPoint pt(rect.TopLeft());
-      gfxFloat rightMost = pt.x + rect.Width() + lineHeight;
-
-      skipCycles = floor((rightMost - aDirtyRect.XMost()) / cycleLength);
+      gfxFloat& ptICoord = aVertical ? pt.y : pt.x;
+      gfxFloat& ptBCoord = aVertical ? pt.x : pt.y;
+      if (aVertical) {
+        ptBCoord += adv + lineThickness / 2.0;
+      }
+      gfxFloat iCoordLimit = ptICoord + rectISize + lineThickness;
+
+      const gfxFloat dirtyRectIMost = aVertical ?
+        aDirtyRect.YMost() : aDirtyRect.XMost();
+      skipCycles = floor((iCoordLimit - dirtyRectIMost) / cycleLength);
       if (skipCycles > 0) {
-        rightMost -= skipCycles * cycleLength;
+        iCoordLimit -= skipCycles * cycleLength;
       }
 
       aGfxContext->NewPath();
 
-      pt.x -= lineHeight;
+      ptICoord -= lineThickness;
       aGfxContext->MoveTo(pt); // 1
 
-      pt.x = rect.X();
+      ptICoord = rectICoord;
       aGfxContext->LineTo(pt); // 2
 
-      bool goDown = true;
+      // In vertical mode, to go "down" relative to the text we need to
+      // decrease the block coordinate, whereas in horizontal we increase
+      // it. So the sense of this flag is effectively inverted.
+      bool goDown = aVertical ? false : true;
       uint32_t iter = 0;
-      while (pt.x < rightMost) {
+      while (ptICoord < iCoordLimit) {
         if (++iter > 1000) {
           // stroke the current path and start again, to avoid pathological
           // behavior in cairo with huge numbers of path segments
           aGfxContext->Stroke();
           aGfxContext->NewPath();
           aGfxContext->MoveTo(pt);
           iter = 0;
         }
-        pt.x += adv;
-        pt.y += goDown ? adv : -adv;
+        ptICoord += adv;
+        ptBCoord += goDown ? adv : -adv;
 
         aGfxContext->LineTo(pt); // 3 and 5
 
-        pt.x += flatLengthAtVertex;
+        ptICoord += flatLengthAtVertex;
         aGfxContext->LineTo(pt); // 4 and 6
 
         goDown = !goDown;
       }
       aGfxContext->Stroke();
       break;
     }
     default:
@@ -4096,163 +4139,178 @@ nsCSSRendering::PaintDecorationLine(nsIF
 }
 
 void
 nsCSSRendering::DecorationLineToPath(nsIFrame* aFrame,
                                      gfxContext* aGfxContext,
                                      const gfxRect& aDirtyRect,
                                      const nscolor aColor,
                                      const gfxPoint& aPt,
-                                     const gfxFloat aXInFrame,
+                                     const gfxFloat aICoordInFrame,
                                      const gfxSize& aLineSize,
                                      const gfxFloat aAscent,
                                      const gfxFloat aOffset,
                                      const uint8_t aDecoration,
                                      const uint8_t aStyle,
+                                     bool aVertical,
                                      const gfxFloat aDescentLimit)
 {
   NS_ASSERTION(aStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE, "aStyle is none");
 
   aGfxContext->NewPath();
 
   gfxRect rect =
     GetTextDecorationRectInternal(aPt, aLineSize, aAscent, aOffset,
-                                  aDecoration, aStyle, aDescentLimit);
+                                  aDecoration, aStyle, aVertical,
+                                  aDescentLimit);
   if (rect.IsEmpty() || !rect.Intersects(aDirtyRect)) {
     return;
   }
 
   if (aDecoration != NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE &&
       aDecoration != NS_STYLE_TEXT_DECORATION_LINE_OVERLINE &&
       aDecoration != NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
     NS_ERROR("Invalid decoration value!");
     return;
   }
 
   if (aStyle != NS_STYLE_TEXT_DECORATION_STYLE_SOLID) {
     // For the moment, we support only solid text decorations.
     return;
   }
 
-  gfxFloat lineHeight = std::max(NS_round(aLineSize.height), 1.0);
+  gfxFloat lineThickness = std::max(NS_round(aLineSize.height), 1.0);
 
   // The y position should be set to the middle of the line.
-  rect.y += lineHeight / 2;
+  rect.y += lineThickness / 2;
 
   aGfxContext->Rectangle
-    (gfxRect(gfxPoint(rect.TopLeft() - gfxPoint(0.0, lineHeight / 2)),
-             gfxSize(rect.Width(), lineHeight)));
+    (gfxRect(gfxPoint(rect.TopLeft() - gfxPoint(0.0, lineThickness / 2)),
+             gfxSize(rect.Width(), lineThickness)));
 }
 
 nsRect
 nsCSSRendering::GetTextDecorationRect(nsPresContext* aPresContext,
                                       const gfxSize& aLineSize,
                                       const gfxFloat aAscent,
                                       const gfxFloat aOffset,
                                       const uint8_t aDecoration,
                                       const uint8_t aStyle,
+                                      bool aVertical,
                                       const gfxFloat aDescentLimit)
 {
   NS_ASSERTION(aPresContext, "aPresContext is null");
   NS_ASSERTION(aStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE, "aStyle is none");
 
   gfxRect rect =
     GetTextDecorationRectInternal(gfxPoint(0, 0), aLineSize, aAscent, aOffset,
-                                  aDecoration, aStyle, aDescentLimit);
+                                  aDecoration, aStyle, aVertical,
+                                  aDescentLimit);
   // 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;
 }
 
 gfxRect
 nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt,
                                               const gfxSize& aLineSize,
                                               const gfxFloat aAscent,
                                               const gfxFloat aOffset,
                                               const uint8_t aDecoration,
                                               const uint8_t aStyle,
+                                              bool aVertical,
                                               const gfxFloat aDescentLimit)
 {
   NS_ASSERTION(aStyle <= NS_STYLE_TEXT_DECORATION_STYLE_WAVY,
                "Invalid aStyle value");
 
   if (aStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE)
     return gfxRect(0, 0, 0, 0);
 
   bool canLiftUnderline = aDescentLimit >= 0.0;
 
-  const gfxFloat left  = floor(aPt.x + 0.5),
-                 right = floor(aPt.x + aLineSize.width + 0.5);
+  gfxFloat iCoord = aVertical ? aPt.y : aPt.x;
+  gfxFloat bCoord = aVertical ? aPt.x : aPt.y;
+
+  // 'left' and 'right' are relative to the line, so for vertical writing modes
+  // they will actually become top and bottom of the rendered line.
+  // Similarly, aLineSize.width and .height are actually length and thickness
+  // of the line, which runs horizontally or vertically according to aVertical.
+  const gfxFloat left  = floor(iCoord + 0.5),
+                 right = floor(iCoord + aLineSize.width + 0.5);
+
+  // We compute |r| as if for a horizontal text run, and then swap vertical
+  // and horizontal coordinates at the end if vertical was requested.
   gfxRect r(left, 0, right - left, 0);
 
-  gfxFloat lineHeight = NS_round(aLineSize.height);
-  lineHeight = std::max(lineHeight, 1.0);
+  gfxFloat lineThickness = NS_round(aLineSize.height);
+  lineThickness = std::max(lineThickness, 1.0);
 
   gfxFloat ascent = NS_round(aAscent);
   gfxFloat descentLimit = floor(aDescentLimit);
 
   gfxFloat suggestedMaxRectHeight = std::max(std::min(ascent, descentLimit), 1.0);
-  r.height = lineHeight;
+  r.height = lineThickness;
   if (aStyle == NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE) {
     /**
      *  We will draw double line as:
      *
      * +-------------------------------------------+
      * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
-     * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
+     * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
      * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
      * |                                           | ^
      * |                                           | | gap
      * |                                           | v
      * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| ^
-     * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineHeight
+     * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| | lineThickness
      * |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX| v
      * +-------------------------------------------+
      */
-    gfxFloat gap = NS_round(lineHeight / 2.0);
+    gfxFloat gap = NS_round(lineThickness / 2.0);
     gap = std::max(gap, 1.0);
-    r.height = lineHeight * 2.0 + gap;
+    r.height = lineThickness * 2.0 + gap;
     if (canLiftUnderline) {
       if (r.Height() > suggestedMaxRectHeight) {
         // Don't shrink the line height, because the thickness has some meaning.
         // We can just shrink the gap at this time.
-        r.height = std::max(suggestedMaxRectHeight, lineHeight * 2.0 + 1.0);
+        r.height = std::max(suggestedMaxRectHeight, lineThickness * 2.0 + 1.0);
       }
     }
   } else if (aStyle == NS_STYLE_TEXT_DECORATION_STYLE_WAVY) {
     /**
      *  We will draw wavy line as:
      *
      * +-------------------------------------------+
      * |XXXXX            XXXXXX            XXXXXX  | ^
-     * |XXXXXX          XXXXXXXX          XXXXXXXX | | lineHeight
+     * |XXXXXX          XXXXXXXX          XXXXXXXX | | lineThickness
      * |XXXXXXX        XXXXXXXXXX        XXXXXXXXXX| v
      * |     XXX      XXX      XXX      XXX      XX|
      * |      XXXXXXXXXX        XXXXXXXXXX        X|
      * |       XXXXXXXX          XXXXXXXX          |
      * |        XXXXXX            XXXXXX           |
      * +-------------------------------------------+
      */
-    r.height = lineHeight > 2.0 ? lineHeight * 4.0 : lineHeight * 3.0;
+    r.height = lineThickness > 2.0 ? lineThickness * 4.0 : lineThickness * 3.0;
     if (canLiftUnderline) {
       if (r.Height() > suggestedMaxRectHeight) {
         // Don't shrink the line height even if there is not enough space,
         // because the thickness has some meaning.  E.g., the 1px wavy line and
         // 2px wavy line can be used for different meaning in IME selections
         // at same time.
-        r.height = std::max(suggestedMaxRectHeight, lineHeight * 2.0);
+        r.height = std::max(suggestedMaxRectHeight, lineThickness * 2.0);
       }
     }
   }
 
-  gfxFloat baseline = floor(aPt.y + aAscent + 0.5);
+  gfxFloat baseline = floor(bCoord + aAscent + 0.5);
   gfxFloat offset = 0.0;
   switch (aDecoration) {
     case NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE:
       offset = aOffset;
       if (canLiftUnderline) {
         if (descentLimit < -offset + r.Height()) {
           // If we can ignore the offset and the decoration line is overflowing,
           // we should align the bottom edge of the decoration line rect if it's
@@ -4260,28 +4318,37 @@ nsCSSRendering::GetTextDecorationRectInt
           // far as possible.
           gfxFloat offsetBottomAligned = -descentLimit + r.Height();
           gfxFloat offsetTopAligned = 0.0;
           offset = std::min(offsetBottomAligned, offsetTopAligned);
         }
       }
       break;
     case NS_STYLE_TEXT_DECORATION_LINE_OVERLINE:
-      offset = aOffset - lineHeight + r.Height();
+      offset = aOffset - lineThickness + r.Height();
       break;
     case NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH: {
       gfxFloat extra = floor(r.Height() / 2.0 + 0.5);
-      extra = std::max(extra, lineHeight);
-      offset = aOffset - lineHeight + extra;
+      extra = std::max(extra, lineThickness);
+      offset = aOffset - lineThickness + extra;
       break;
     }
     default:
       NS_ERROR("Invalid decoration value!");
   }
-  r.y = baseline - floor(offset + 0.5);
+
+  if (aVertical) {
+    r.y = baseline + floor(aOffset + 0.5); // this will need updating when we
+                                           // support sideways-left orientation
+    Swap(r.x, r.y);
+    Swap(r.width, r.height);
+  } else {
+    r.y = baseline - floor(aOffset + 0.5);
+  }
+
   return r;
 }
 
 // ------------------
 // ImageRenderer
 // ------------------
 nsImageRenderer::nsImageRenderer(nsIFrame* aForFrame,
                                  const nsStyleImage* aImage,
--- a/layout/base/nsCSSRendering.h
+++ b/layout/base/nsCSSRendering.h
@@ -593,33 +593,39 @@ struct nsCSSRendering {
                                      nscoord              aStartBevelOffset = 0,
                                      uint8_t              aEndBevelSide = 0,
                                      nscoord              aEndBevelOffset = 0);
 
   /**
    * Function for painting the decoration lines for the text.
    * NOTE: aPt, aLineSize, aAscent and aOffset are non-rounded device pixels,
    *       not app units.
+   * NOTE: aLineSize is a "logical" size in textRun orientation, so that for
+   *       a vertical textrun, aLineSize.width (which is the decoration line
+   *       length) will actually be a physical height; and conversely,
+   *       aLineSize.height [thickness] will be a physical width. The alternate
+   *       names in [brackets] in the comments here apply to the vertical case.
+   *
    *   input:
    *     @param aFrame            the frame which needs the decoration line
    *     @param aGfxContext
    *     @param aDirtyRect        no need to paint outside this rect
    *     @param aColor            the color of the decoration line
    *     @param aPt               the top/left edge of the text
-   *     @param aXInFrame         the distance between aPt.x and left edge of
-   *                              aFrame.  If the decoration line is for shadow,
-   *                              set the distance between the left edge of
-   *                              the aFrame and the position of the text as
+   *     @param aICoordInFrame    the distance between aPt.x [y] and left [top]
+   *                              edge of aFrame. If the decoration line is for
+   *                              shadow, set the distance between the left edge
+   *                              of the aFrame and the position of the text as
    *                              positioned without offset of the shadow.
-   *     @param aLineSize         the width and the height of the decoration
-   *                              line
+   *     @param aLineSize         the width [length] and the height [thickness]
+   *                              of the decoration line
    *     @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)
+   *                              positive, the line is lifted up [right])
    *     @param aDecoration       which line will be painted. The value can be
    *                              NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE or
    *                              NS_STYLE_TEXT_DECORATION_LINE_OVERLINE or
    *                              NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH.
    *     @param aStyle            the style of the decoration line such as
    *                              NS_STYLE_TEXT_DECORATION_STYLE_*.
    *     @param aDescentLimit     If aDescentLimit is zero or larger and the
    *                              underline overflows from the descent space,
@@ -633,42 +639,44 @@ struct nsCSSRendering {
    *                              if it's possible.  Therefore, this value is
    *                              used for strikeout line and overline too.
    */
   static void PaintDecorationLine(nsIFrame* aFrame,
                                   gfxContext* aGfxContext,
                                   const gfxRect& aDirtyRect,
                                   const nscolor aColor,
                                   const gfxPoint& aPt,
-                                  const gfxFloat aXInFrame,
+                                  const gfxFloat aICoordInFrame,
                                   const gfxSize& aLineSize,
                                   const gfxFloat aAscent,
                                   const gfxFloat aOffset,
                                   const uint8_t aDecoration,
                                   const uint8_t aStyle,
+                                  bool aVertical,
                                   const gfxFloat aDescentLimit = -1.0);
 
   /**
    * Adds a path corresponding to the outline of the decoration line to
    * the specified context.  Arguments have the same meaning as for
    * PaintDecorationLine.  Currently this only works for solid
    * decorations; for other decoration styles, an empty path is added
    * to the context.
    */
   static void DecorationLineToPath(nsIFrame* aFrame,
                                    gfxContext* aGfxContext,
                                    const gfxRect& aDirtyRect,
                                    const nscolor aColor,
                                    const gfxPoint& aPt,
-                                   const gfxFloat aXInFrame,
+                                   const gfxFloat aICoordInFrame,
                                    const gfxSize& aLineSize,
                                    const gfxFloat aAscent,
                                    const gfxFloat aOffset,
                                    const uint8_t aDecoration,
                                    const uint8_t aStyle,
+                                   bool aVertical,
                                    const gfxFloat aDescentLimit = -1.0);
 
   /**
    * Function for getting the decoration line rect for the text.
    * NOTE: aLineSize, aAscent and aOffset are non-rounded device pixels,
    *       not app units.
    *   input:
    *     @param aPresContext
@@ -700,16 +708,17 @@ struct nsCSSRendering {
    *                              the each values are app units.
    */
   static nsRect GetTextDecorationRect(nsPresContext* aPresContext,
                                       const gfxSize& aLineSize,
                                       const gfxFloat aAscent,
                                       const gfxFloat aOffset,
                                       const uint8_t aDecoration,
                                       const uint8_t aStyle,
+                                      bool aVertical,
                                       const gfxFloat aDescentLimit = -1.0);
 
   static gfxContext::GraphicsOperator GetGFXBlendMode(uint8_t mBlendMode) {
     switch (mBlendMode) {
       case NS_STYLE_BLEND_NORMAL:      return gfxContext::OPERATOR_OVER;
       case NS_STYLE_BLEND_MULTIPLY:    return gfxContext::OPERATOR_MULTIPLY;
       case NS_STYLE_BLEND_SCREEN:      return gfxContext::OPERATOR_SCREEN;
       case NS_STYLE_BLEND_OVERLAY:     return gfxContext::OPERATOR_OVERLAY;
@@ -733,43 +742,45 @@ struct nsCSSRendering {
 
 protected:
   static gfxRect GetTextDecorationRectInternal(const gfxPoint& aPt,
                                                const gfxSize& aLineSize,
                                                const gfxFloat aAscent,
                                                const gfxFloat aOffset,
                                                const uint8_t aDecoration,
                                                const uint8_t aStyle,
-                                               const gfxFloat aDscentLimit);
+                                               bool aVertical,
+                                               const gfxFloat aDescentLimit);
 
   /**
    * Returns inflated rect for painting a decoration line.
    * Complex style decoration lines should be painted from leftmost of nearest
    * ancestor block box because that makes better look of connection of lines
    * for different nodes.  ExpandPaintingRectForDecorationLine() returns
    * a rect for actual painting rect for the clipped rect.
    *
    * input:
    *     @param aFrame            the frame which needs the decoration line.
    *     @param aStyle            the style of the complex decoration line
    *                              NS_STYLE_TEXT_DECORATION_STYLE_DOTTED or
    *                              NS_STYLE_TEXT_DECORATION_STYLE_DASHED or
    *                              NS_STYLE_TEXT_DECORATION_STYLE_WAVY.
    *     @param aClippedRect      the clipped rect for the decoration line.
    *                              in other words, visible area of the line.
-   *     @param aXInFrame         the distance between left edge of aFrame and
-   *                              aClippedRect.pos.x.
+   *     @param aICoordInFrame  the distance between inline-start edge of aFrame
+   *                              and aClippedRect.pos.
    *     @param aCycleLength      the width of one cycle of the line style.
    */
   static gfxRect ExpandPaintingRectForDecorationLine(
                    nsIFrame* aFrame,
                    const uint8_t aStyle,
                    const gfxRect &aClippedRect,
-                   const gfxFloat aXInFrame,
-                   const gfxFloat aCycleLength);
+                   const gfxFloat aICoordInFrame,
+                   const gfxFloat aCycleLength,
+                   bool aVertical);
 };
 
 /*
  * 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/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -1786,22 +1786,23 @@ GetHyphenTextRun(gfxTextRun* aTextRun, g
   if (!ctx)
     return nullptr;
 
   return aTextRun->GetFontGroup()->
     MakeHyphenTextRun(ctx, aTextRun->GetAppUnitsPerDevUnit());
 }
 
 static gfxFont::Metrics
-GetFirstFontMetrics(gfxFontGroup* aFontGroup)
+GetFirstFontMetrics(gfxFontGroup* aFontGroup, bool aVertical)
 {
   if (!aFontGroup)
     return gfxFont::Metrics();
   gfxFont* font = aFontGroup->GetFirstValidFont();
-  return font->GetMetrics(gfxFont::eHorizontal); // XXX vertical
+  return font->GetMetrics(aVertical ? gfxFont::eVertical :
+                                      gfxFont::eHorizontal);
 }
 
 PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_NORMAL == 0);
 PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_PRE == 1);
 PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_NOWRAP == 2);
 PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_PRE_WRAP == 3);
 PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_PRE_LINE == 4);
 PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_PRE_SPACE == 5);
@@ -3112,18 +3113,19 @@ static gfxFloat
 ComputeTabWidthAppUnits(nsIFrame* aFrame, gfxTextRun* aTextRun)
 {
   // Get the number of spaces from CSS -moz-tab-size
   const nsStyleText* textStyle = aFrame->StyleText();
   
   // Round the space width when converting to appunits the same way
   // textruns do
   gfxFloat spaceWidthAppUnits =
-    NS_round(GetFirstFontMetrics(aTextRun->GetFontGroup()).spaceWidth *
-              aTextRun->GetAppUnitsPerDevUnit());
+    NS_round(GetFirstFontMetrics(aTextRun->GetFontGroup(),
+                                 aTextRun->IsVertical()).spaceWidth *
+             aTextRun->GetAppUnitsPerDevUnit());
   return textStyle->mTabSize * spaceWidthAppUnits;
 }
 
 // aX and the result are in whole appunits.
 static gfxFloat
 AdvanceToNextTab(gfxFloat aX, nsIFrame* aFrame,
                  gfxTextRun* aTextRun, gfxFloat* aCachedTabWidth)
 {
@@ -4885,16 +4887,17 @@ nsTextFrame::UnionAdditionalOverflow(nsP
                                      PropertyProvider& aProvider,
                                      nsRect* aVisualOverflowRect,
                                      bool aIncludeTextDecorations)
 {
   // Text-shadow overflows
   nsRect shadowRect =
     nsLayoutUtils::GetTextShadowRectsUnion(*aVisualOverflowRect, this);
   aVisualOverflowRect->UnionRect(*aVisualOverflowRect, shadowRect);
+  bool vertical = mTextRun->IsVertical();
 
   if (IsFloatingFirstLetterChild()) {
     // The underline/overline drawable area must be contained in the overflow
     // rect when this is in floating first letter frame at *both* modes.
     // In this case, aBlock is the ::first-letter frame.
     uint8_t decorationStyle = aBlock->StyleContext()->
                                 StyleTextReset()->GetDecorationStyle();
     // If the style is none, let's include decoration line rect as solid style
@@ -4904,29 +4907,31 @@ nsTextFrame::UnionAdditionalOverflow(nsP
       decorationStyle = NS_STYLE_TEXT_DECORATION_STYLE_SOLID;
     }
     nsFontMetrics* fontMetrics = aProvider.GetFontMetrics();
     nscoord underlineOffset, underlineSize;
     fontMetrics->GetUnderline(underlineOffset, underlineSize);
     nscoord maxAscent = fontMetrics->MaxAscent();
 
     gfxFloat appUnitsPerDevUnit = aPresContext->AppUnitsPerDevPixel();
-    gfxFloat gfxWidth = aVisualOverflowRect->width / appUnitsPerDevUnit;
+    gfxFloat gfxWidth =
+      (vertical ? aVisualOverflowRect->height : aVisualOverflowRect->width) /
+      appUnitsPerDevUnit;
     gfxFloat gfxAscent = gfxFloat(mAscent) / appUnitsPerDevUnit;
     gfxFloat gfxMaxAscent = maxAscent / appUnitsPerDevUnit;
     gfxFloat gfxUnderlineSize = underlineSize / appUnitsPerDevUnit;
     gfxFloat gfxUnderlineOffset = underlineOffset / appUnitsPerDevUnit;
     nsRect underlineRect =
       nsCSSRendering::GetTextDecorationRect(aPresContext,
         gfxSize(gfxWidth, gfxUnderlineSize), gfxAscent, gfxUnderlineOffset,
-        NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, decorationStyle);
+        NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, decorationStyle, vertical);
     nsRect overlineRect =
       nsCSSRendering::GetTextDecorationRect(aPresContext,
         gfxSize(gfxWidth, gfxUnderlineSize), gfxAscent, gfxMaxAscent,
-        NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, decorationStyle);
+        NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, decorationStyle, vertical);
 
     aVisualOverflowRect->UnionRect(*aVisualOverflowRect, underlineRect);
     aVisualOverflowRect->UnionRect(*aVisualOverflowRect, overlineRect);
 
     // XXX If strikeoutSize is much thicker than the underlineSize, it may
     //     cause overflowing from the overflow rect.  However, such case
     //     isn't realistic, we don't need to compute it now.
   }
@@ -4936,19 +4941,19 @@ nsTextFrame::UnionAdditionalOverflow(nsP
     // maxima and minima are required to reliably generate the rectangle for
     // them
     TextDecorations textDecs;
     GetTextDecorations(aPresContext, eResolvedColors, textDecs);
     if (textDecs.HasDecorationLines()) {
       nscoord inflationMinFontSize =
         nsLayoutUtils::InflationMinFontSizeFor(aBlock);
 
-      const nscoord width = GetSize().width;
+      const nscoord measure = vertical ? GetSize().height : GetSize().width;
       const gfxFloat appUnitsPerDevUnit = aPresContext->AppUnitsPerDevPixel(),
-                     gfxWidth = width / appUnitsPerDevUnit,
+                     gfxWidth = measure / appUnitsPerDevUnit,
                      ascent = gfxFloat(mAscent) / appUnitsPerDevUnit;
       nscoord top(nscoord_MAX), bottom(nscoord_MIN);
       // Below we loop through all text decorations and compute the rectangle
       // containing all of them, in this frame's coordinate space
       for (uint32_t i = 0; i < textDecs.mUnderlines.Length(); ++i) {
         const LineDecoration& dec = textDecs.mUnderlines[i];
         uint8_t decorationStyle = dec.mStyle;
         // If the style is solid, let's include decoration line rect of solid
@@ -4956,23 +4961,25 @@ nsTextFrame::UnionAdditionalOverflow(nsP
         // doesn't cause reflow.
         if (decorationStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
           decorationStyle = NS_STYLE_TEXT_DECORATION_STYLE_SOLID;
         }
 
         float inflation =
           GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
         const gfxFont::Metrics metrics =
-          GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation));
+          GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
+                              vertical);
 
         const nsRect decorationRect =
           nsCSSRendering::GetTextDecorationRect(aPresContext,
             gfxSize(gfxWidth, metrics.underlineSize),
             ascent, metrics.underlineOffset,
-            NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, decorationStyle) +
+            NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, decorationStyle,
+            vertical) +
           nsPoint(0, -dec.mBaselineOffset);
 
         top = std::min(decorationRect.y, top);
         bottom = std::max(decorationRect.YMost(), bottom);
       }
       for (uint32_t i = 0; i < textDecs.mOverlines.Length(); ++i) {
         const LineDecoration& dec = textDecs.mOverlines[i];
         uint8_t decorationStyle = dec.mStyle;
@@ -4981,23 +4988,25 @@ nsTextFrame::UnionAdditionalOverflow(nsP
         // doesn't cause reflow.
         if (decorationStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
           decorationStyle = NS_STYLE_TEXT_DECORATION_STYLE_SOLID;
         }
 
         float inflation =
           GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
         const gfxFont::Metrics metrics =
-          GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation));
+          GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
+                              vertical);
 
         const nsRect decorationRect =
           nsCSSRendering::GetTextDecorationRect(aPresContext,
             gfxSize(gfxWidth, metrics.underlineSize),
             ascent, metrics.maxAscent,
-            NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, decorationStyle) +
+            NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, decorationStyle,
+            vertical) +
           nsPoint(0, -dec.mBaselineOffset);
 
         top = std::min(decorationRect.y, top);
         bottom = std::max(decorationRect.YMost(), bottom);
       }
       for (uint32_t i = 0; i < textDecs.mStrikes.Length(); ++i) {
         const LineDecoration& dec = textDecs.mStrikes[i];
         uint8_t decorationStyle = dec.mStyle;
@@ -5006,30 +5015,34 @@ nsTextFrame::UnionAdditionalOverflow(nsP
         // doesn't cause reflow.
         if (decorationStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
           decorationStyle = NS_STYLE_TEXT_DECORATION_STYLE_SOLID;
         }
 
         float inflation =
           GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
         const gfxFont::Metrics metrics =
-          GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation));
+          GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
+                              vertical);
 
         const nsRect decorationRect =
           nsCSSRendering::GetTextDecorationRect(aPresContext,
             gfxSize(gfxWidth, metrics.strikeoutSize),
             ascent, metrics.strikeoutOffset,
-            NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH, decorationStyle) +
+            NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH, decorationStyle,
+            vertical) +
           nsPoint(0, -dec.mBaselineOffset);
         top = std::min(decorationRect.y, top);
         bottom = std::max(decorationRect.YMost(), bottom);
       }
 
       aVisualOverflowRect->UnionRect(*aVisualOverflowRect,
-                                     nsRect(0, top, width, bottom - top));
+                                     vertical ?
+                                       nsRect(top, 0, bottom - top, measure) :
+                                       nsRect(0, top, measure, bottom - top));
     }
   }
   // When this frame is not selected, the text-decoration area must be in
   // frame bounds.
   if (!IsSelected() ||
       !CombineSelectionUnderlineRect(aPresContext, *aVisualOverflowRect))
     return;
   AddStateBits(TEXT_SELECTION_UNDERLINE_OVERFLOWED);
@@ -5099,61 +5112,63 @@ enum DecorationType {
 
 static void
 PaintDecorationLine(nsIFrame* aFrame,
                     gfxContext* const aCtx,
                     const gfxRect& aDirtyRect,
                     nscolor aColor,
                     const nscolor* aOverrideColor,
                     const gfxPoint& aPt,
-                    gfxFloat aXInFrame,
+                    gfxFloat aICoordInFrame,
                     const gfxSize& aLineSize,
                     gfxFloat aAscent,
                     gfxFloat aOffset,
                     uint8_t aDecoration,
                     uint8_t aStyle,
                     DecorationType aDecorationType,
                     nsTextFrame::DrawPathCallbacks* aCallbacks,
+                    bool aVertical,
                     gfxFloat aDescentLimit = -1.0)
 {
   nscolor lineColor = aOverrideColor ? *aOverrideColor : aColor;
   if (aCallbacks) {
     if (aDecorationType == eNormalDecoration) {
       aCallbacks->NotifyBeforeDecorationLine(lineColor);
     } else {
       aCallbacks->NotifyBeforeSelectionDecorationLine(lineColor);
     }
     nsCSSRendering::DecorationLineToPath(aFrame, aCtx, aDirtyRect, lineColor,
-      aPt, aXInFrame, aLineSize, aAscent, aOffset, aDecoration, aStyle,
+      aPt, aICoordInFrame, aLineSize, aAscent, aOffset, aDecoration, aStyle,
       aDescentLimit);
     if (aDecorationType == eNormalDecoration) {
       aCallbacks->NotifyDecorationLinePathEmitted();
     } else {
       aCallbacks->NotifySelectionDecorationLinePathEmitted();
     }
   } else {
     nsCSSRendering::PaintDecorationLine(aFrame, aCtx, aDirtyRect, lineColor,
-      aPt, aXInFrame, aLineSize, aAscent, aOffset, aDecoration, aStyle,
-      aDescentLimit);
+      aPt, aICoordInFrame, aLineSize, aAscent, aOffset, aDecoration, aStyle,
+      aVertical, aDescentLimit);
   }
 }
 
 /**
  * This, plus SelectionTypesWithDecorations, encapsulates all knowledge about
  * drawing text decoration for selections.
  */
 static void DrawSelectionDecorations(gfxContext* aContext,
     const gfxRect& aDirtyRect,
     SelectionType aType,
     nsTextFrame* aFrame,
     nsTextPaintStyle& aTextPaintStyle,
     const TextRangeStyle &aRangeStyle,
-    const gfxPoint& aPt, gfxFloat aXInFrame, gfxFloat aWidth,
+    const gfxPoint& aPt, gfxFloat aICoordInFrame, gfxFloat aWidth,
     gfxFloat aAscent, const gfxFont::Metrics& aFontMetrics,
-    nsTextFrame::DrawPathCallbacks* aCallbacks)
+    nsTextFrame::DrawPathCallbacks* aCallbacks,
+    bool aVertical)
 {
   gfxPoint pt(aPt);
   gfxSize size(aWidth,
                ComputeSelectionUnderlineHeight(aTextPaintStyle.PresContext(),
                                                aFontMetrics, aType));
   gfxFloat descentLimit =
     ComputeDescentLimitForSelectionUnderline(aTextPaintStyle.PresContext(),
                                              aFrame, aFontMetrics);
@@ -5220,19 +5235,20 @@ static void DrawSelectionDecorations(gfx
         return;
       break;
     default:
       NS_WARNING("Requested selection decorations when there aren't any");
       return;
   }
   size.height *= relativeSize;
   PaintDecorationLine(aFrame, aContext, aDirtyRect, color, nullptr, pt,
-    pt.x - aPt.x + aXInFrame, size, aAscent, aFontMetrics.underlineOffset,
+    (aVertical ? (pt.y - aPt.y) : (pt.x - aPt.x)) + aICoordInFrame,
+    size, aAscent, aFontMetrics.underlineOffset,
     NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, style, eSelectionDecoration,
-    aCallbacks, descentLimit);
+    aCallbacks, aVertical, descentLimit);
 }
 
 /**
  * This function encapsulates all knowledge of how selections affect foreground
  * and background colors.
  * @return true if the selection affects colors, false otherwise
  * @param aForeground the foreground color to use
  * @param aBackground the background color to use, or RGBA(0,0,0,0) if no
@@ -5675,46 +5691,63 @@ nsTextFrame::PaintTextSelectionDecoratio
       for (int32_t i = start; i < end; ++i) {
         selectedChars[i] = sdptr;
       }
     }
     sdptr = sdptr->mNext;
   }
 
   gfxFont* firstFont = aProvider.GetFontGroup()->GetFirstValidFont();
+  bool vertical = mTextRun->IsVertical();
   gfxFont::Metrics
-    decorationMetrics(firstFont->GetMetrics(gfxFont::eHorizontal)); // XXX vertical?
-  decorationMetrics.underlineOffset =
-    aProvider.GetFontGroup()->GetUnderlineOffset();
-
-  gfxFloat startXOffset = aTextBaselinePt.x - aFramePt.x;
+    decorationMetrics(firstFont->GetMetrics(vertical ?
+      gfxFont::eVertical : gfxFont::eHorizontal));
+  if (!vertical) {
+    // The potential adjustment from using gfxFontGroup::GetUnderlineOffset
+    // is only valid for horizontal text.
+    decorationMetrics.underlineOffset =
+      aProvider.GetFontGroup()->GetUnderlineOffset();
+  }
+
+  gfxFloat startIOffset =
+    vertical ? aTextBaselinePt.y - aFramePt.y : aTextBaselinePt.x - aFramePt.x;
   SelectionIterator iterator(selectedChars, aContentOffset, aContentLength,
-                             aProvider, mTextRun, startXOffset);
-  gfxFloat xOffset, hyphenWidth;
+                             aProvider, mTextRun, startIOffset);
+  gfxFloat iOffset, hyphenWidth;
   uint32_t offset, length;
   int32_t app = aTextPaintStyle.PresContext()->AppUnitsPerDevPixel();
   // XXX aTextBaselinePt is in AppUnits, shouldn't it be nsFloatPoint?
-  gfxPoint pt(0.0, (aTextBaselinePt.y - mAscent) / app);
+  gfxPoint pt;
+  if (vertical) {
+    pt.x = (aTextBaselinePt.x - mAscent) / app;
+  } else {
+    pt.y = (aTextBaselinePt.y - mAscent) / app;
+  }
   gfxRect dirtyRect(aDirtyRect.x / app, aDirtyRect.y / app,
                     aDirtyRect.width / app, aDirtyRect.height / app);
   SelectionType type;
   TextRangeStyle selectedStyle;
-  while (iterator.GetNextSegment(&xOffset, &offset, &length, &hyphenWidth,
+  while (iterator.GetNextSegment(&iOffset, &offset, &length, &hyphenWidth,
                                  &type, &selectedStyle)) {
     gfxFloat advance = hyphenWidth +
       mTextRun->GetAdvanceWidth(offset, length, &aProvider);
     if (type == aSelectionType) {
-      pt.x = (aFramePt.x + xOffset -
-             (mTextRun->IsRightToLeft() ? advance : 0)) / app;
+      if (vertical) {
+        pt.y = (aFramePt.y + iOffset -
+               (mTextRun->IsRightToLeft() ? advance : 0)) / app;
+      } else {
+        pt.x = (aFramePt.x + iOffset -
+               (mTextRun->IsRightToLeft() ? advance : 0)) / app;
+      }
       gfxFloat width = Abs(advance) / app;
       gfxFloat xInFrame = pt.x - (aFramePt.x / app);
       DrawSelectionDecorations(aCtx, dirtyRect, aSelectionType, this,
                                aTextPaintStyle, selectedStyle, pt, xInFrame,
                                width, mAscent / app, decorationMetrics,
-                               aCallbacks);
+                               aCallbacks, vertical);
     }
     iterator.UpdateWithAdvance(advance);
   }
 }
 
 bool
 nsTextFrame::PaintTextWithSelection(gfxContext* aCtx,
     const gfxPoint& aFramePt,
@@ -6091,20 +6124,21 @@ nsTextFrame::DrawTextRunAndDecorations(
     gfxFloat& aAdvanceWidth,
     bool aDrawSoftHyphen,
     const TextDecorations& aDecorations,
     const nscolor* const aDecorationOverrideColor,
     gfxTextContextPaint* aContextPaint,
     nsTextFrame::DrawPathCallbacks* aCallbacks)
 {
     const gfxFloat app = aTextStyle.PresContext()->AppUnitsPerDevPixel();
+    bool vertical = mTextRun->IsVertical();
 
     // XXX aFramePt is in AppUnits, shouldn't it be nsFloatPoint?
     nscoord x = NSToCoordRound(aFramePt.x);
-    nscoord width = GetRect().width;
+    nscoord width = vertical ? GetRect().height : GetRect().width;
     aClipEdges.Intersect(&x, &width);
 
     gfxPoint decPt(x / app, 0);
     gfxSize decSize(width / app, 0);
     const gfxFloat ascent = gfxFloat(mAscent) / app;
     const gfxFloat frameTop = aFramePt.y;
 
     gfxRect dirtyRect(aDirtyRect.x / app, aDirtyRect.y / app,
@@ -6118,45 +6152,47 @@ nsTextFrame::DrawTextRunAndDecorations(
       const LineDecoration& dec = aDecorations.mUnderlines[i];
       if (dec.mStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
         continue;
       }
 
       float inflation =
         GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
       const gfxFont::Metrics metrics =
-        GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation));
+        GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
+                            vertical);
 
       decSize.height = metrics.underlineSize;
       decPt.y = (frameTop - dec.mBaselineOffset) / app;
 
       PaintDecorationLine(this, aCtx, dirtyRect, dec.mColor,
         aDecorationOverrideColor, decPt, 0.0, decSize, ascent,
         metrics.underlineOffset, NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
-        dec.mStyle, eNormalDecoration, aCallbacks);
+        dec.mStyle, eNormalDecoration, aCallbacks, vertical);
     }
     // Overlines
     for (uint32_t i = aDecorations.mOverlines.Length(); i-- > 0; ) {
       const LineDecoration& dec = aDecorations.mOverlines[i];
       if (dec.mStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
         continue;
       }
 
       float inflation =
         GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
       const gfxFont::Metrics metrics =
-        GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation));
+        GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
+                            vertical);
 
       decSize.height = metrics.underlineSize;
       decPt.y = (frameTop - dec.mBaselineOffset) / app;
 
       PaintDecorationLine(this, aCtx, dirtyRect, dec.mColor,
         aDecorationOverrideColor, decPt, 0.0, decSize, ascent,
         metrics.maxAscent, NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, dec.mStyle,
-        eNormalDecoration, aCallbacks);
+        eNormalDecoration, aCallbacks, vertical);
     }
 
     // CSS 2.1 mandates that text be painted after over/underlines, and *then*
     // line-throughs
     DrawTextRun(aCtx, aTextBaselinePt, aOffset, aLength, aProvider, aTextColor,
                 aAdvanceWidth, aDrawSoftHyphen, aContextPaint, aCallbacks);
 
     // Line-throughs
@@ -6164,25 +6200,26 @@ nsTextFrame::DrawTextRunAndDecorations(
       const LineDecoration& dec = aDecorations.mStrikes[i];
       if (dec.mStyle == NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
         continue;
       }
 
       float inflation =
         GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
       const gfxFont::Metrics metrics =
-        GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation));
+        GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation),
+                            vertical);
 
       decSize.height = metrics.strikeoutSize;
       decPt.y = (frameTop - dec.mBaselineOffset) / app;
 
       PaintDecorationLine(this, aCtx, dirtyRect, dec.mColor,
         aDecorationOverrideColor, decPt, 0.0, decSize, ascent,
         metrics.strikeoutOffset, NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
-        dec.mStyle, eNormalDecoration, aCallbacks);
+        dec.mStyle, eNormalDecoration, aCallbacks, vertical);
     }
 }
 
 void
 nsTextFrame::DrawText(
     gfxContext* const aCtx, const gfxRect& aDirtyRect,
     const gfxPoint& aFramePt, const gfxPoint& aTextBaselinePt,
     uint32_t aOffset, uint32_t aLength,
@@ -6368,18 +6405,19 @@ nsTextFrame::CombineSelectionUnderlineRe
 
   nsRect givenRect = aRect;
 
   nsRefPtr<nsFontMetrics> fm;
   nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
                                         GetFontSizeInflation());
   gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
   gfxFont* firstFont = fontGroup->GetFirstValidFont();
+  bool vertical = GetWritingMode().IsVertical();
   const gfxFont::Metrics& metrics =
-    firstFont->GetMetrics(gfxFont::eHorizontal); // XXX vertical?
+    firstFont->GetMetrics(vertical ? gfxFont::eVertical : gfxFont::eHorizontal);
   gfxFloat underlineOffset = fontGroup->GetUnderlineOffset();
   gfxFloat ascent = aPresContext->AppUnitsToGfxUnits(mAscent);
   gfxFloat descentLimit =
     ComputeDescentLimitForSelectionUnderline(aPresContext, this, metrics);
 
   SelectionDetails *details = GetSelectionDetails();
   for (SelectionDetails *sd = details; sd; sd = sd->mNext) {
     if (sd->mStart == sd->mEnd || !(sd->mType & SelectionTypesWithDecorations))
@@ -6414,17 +6452,17 @@ nsTextFrame::CombineSelectionUnderlineRe
     gfxSize size(aPresContext->AppUnitsToGfxUnits(aRect.width),
                  ComputeSelectionUnderlineHeight(aPresContext,
                                                  metrics, sd->mType));
     relativeSize = std::max(relativeSize, 1.0f);
     size.height *= relativeSize;
     decorationArea =
       nsCSSRendering::GetTextDecorationRect(aPresContext, size,
         ascent, underlineOffset, NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
-        style, descentLimit);
+        style, vertical, descentLimit);
     aRect.UnionRect(aRect, decorationArea);
   }
   DestroySelectionDetails(details);
 
   return !aRect.IsEmpty() && !givenRect.Contains(aRect);
 }
 
 bool
--- a/layout/xul/nsTextBoxFrame.cpp
+++ b/layout/xul/nsTextBoxFrame.cpp
@@ -394,16 +394,18 @@ nsTextBoxFrame::DrawText(nsRenderingCont
     uint8_t underStyle;
     uint8_t strikeStyle;
 
     // Begin with no decorations
     uint8_t decorations = NS_STYLE_TEXT_DECORATION_LINE_NONE;
     // A mask of all possible decorations.
     uint8_t decorMask = NS_STYLE_TEXT_DECORATION_LINE_LINES_MASK;
 
+    bool vertical = GetWritingMode().IsVertical();
+
     nsIFrame* f = this;
     do {  // find decoration colors
       nsStyleContext* context = f->StyleContext();
       if (!context->HasTextDecorationLines()) {
         break;
       }
       const nsStyleTextReset* styleText = context->StyleTextReset();
       
@@ -472,24 +474,26 @@ nsTextBoxFrame::DrawText(nsRenderingCont
       fontMet->GetUnderline(offset, size);
       gfxFloat offsetPixel = presContext->AppUnitsToGfxUnits(offset);
       gfxFloat sizePixel = presContext->AppUnitsToGfxUnits(size);
       if ((decorations & NS_FONT_DECORATION_UNDERLINE) &&
           underStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
         nsCSSRendering::PaintDecorationLine(this, ctx, dirtyRect, underColor,
                           pt, xInFrame, gfxSize(width, sizePixel),
                           ascentPixel, offsetPixel,
-                          NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, underStyle);
+                          NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, underStyle,
+                          vertical);
       }
       if ((decorations & NS_FONT_DECORATION_OVERLINE) &&
           overStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
         nsCSSRendering::PaintDecorationLine(this, ctx, dirtyRect, overColor,
                           pt, xInFrame, gfxSize(width, sizePixel),
                           ascentPixel, ascentPixel,
-                          NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, overStyle);
+                          NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, overStyle,
+                          vertical);
       }
     }
 
     nsRefPtr<nsRenderingContext> refContext =
         PresContext()->PresShell()->CreateReferenceRenderingContext();
 
     aRenderingContext.SetFont(fontMet);
     refContext->SetFont(fontMet);
@@ -557,17 +561,17 @@ nsTextBoxFrame::DrawText(nsRenderingCont
     if ((decorations & NS_FONT_DECORATION_LINE_THROUGH) &&
         strikeStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
       fontMet->GetStrikeout(offset, size);
       gfxFloat offsetPixel = presContext->AppUnitsToGfxUnits(offset);
       gfxFloat sizePixel = presContext->AppUnitsToGfxUnits(size);
       nsCSSRendering::PaintDecorationLine(this, ctx, dirtyRect, strikeColor,
                         pt, xInFrame, gfxSize(width, sizePixel), ascentPixel,
                         offsetPixel, NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
-                        strikeStyle);
+                        strikeStyle, vertical);
     }
 }
 
 void
 nsTextBoxFrame::CalculateUnderline(nsRenderingContext& aRenderingContext)
 {
     if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
          // Calculate all fields of mAccessKeyInfo which