Bug 1113684 - part 1 - Handle vertical writing modes when displaying alt-text alongside a broken-image icon. r=smontagu
authorJonathan Kew <jkew@mozilla.com>
Tue, 06 Jan 2015 20:56:03 +0000
changeset 239011 1fc59f36a868060f92eb23e8b88a33b8a3f4276a
parent 239010 d3bbe13bbd085772208cfb0dcb3bc458f8ee7d5c
child 239012 5179ff7d75735edf3fc036aad7378e6178452c57
push id7472
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 20:36:27 +0000
treeherdermozilla-aurora@300ca104f8fb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmontagu
bugs1113684
milestone37.0a1
Bug 1113684 - part 1 - Handle vertical writing modes when displaying alt-text alongside a broken-image icon. r=smontagu
layout/generic/nsImageFrame.cpp
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1095,63 +1095,105 @@ nsImageFrame::DisplayAltText(nsPresConte
   nsRefPtr<nsFontMetrics> fm;
   nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
     nsLayoutUtils::FontSizeInflationFor(this));
 
   // Format the text to display within the formatting rect
 
   nscoord maxAscent = fm->MaxAscent();
   nscoord maxDescent = fm->MaxDescent();
-  nscoord height = fm->MaxHeight();
+  nscoord lineHeight = fm->MaxHeight(); // line-relative, so an x-coordinate
+                                        // length if writing mode is vertical
+
+  WritingMode wm = GetWritingMode();
+  bool isVertical = wm.IsVertical();
+
+  fm->SetVertical(isVertical);
+  fm->SetTextOrientation(StyleVisibility()->mTextOrientation);
 
   // XXX It would be nice if there was a way to have the font metrics tell
   // use where to break the text given a maximum width. At a minimum we need
   // to be able to get the break character...
   const char16_t* str = aAltText.get();
-  int32_t          strLen = aAltText.Length();
-  nscoord          y = aRect.y;
+  int32_t strLen = aAltText.Length();
+  nsPoint pt = wm.IsVerticalRL() ? aRect.TopRight() - nsPoint(lineHeight, 0)
+                                 : aRect.TopLeft();
+  nscoord iSize = isVertical ? aRect.height : aRect.width;
 
   if (!aPresContext->BidiEnabled() && HasRTLChars(aAltText)) {
     aPresContext->SetBidiEnabled();
   }
 
   // Always show the first line, even if we have to clip it below
   bool firstLine = true;
-  while ((strLen > 0) && (firstLine || (y + maxDescent) < aRect.YMost())) {
+  while (strLen > 0) {
+    if (!firstLine) {
+      // If we've run out of space, break out of the loop
+      if ((!isVertical && (pt.y + maxDescent) >= aRect.YMost()) ||
+          (wm.IsVerticalRL() && (pt.x + maxDescent < aRect.x)) ||
+          (wm.IsVerticalLR() && (pt.x + maxDescent >= aRect.XMost()))) {
+        break;
+      }
+    }
+
     // Determine how much of the text to display on this line
     uint32_t  maxFit;  // number of characters that fit
-    nscoord strWidth = MeasureString(str, strLen, aRect.width, maxFit,
+    nscoord strWidth = MeasureString(str, strLen, iSize, maxFit,
                                      aRenderingContext, *fm);
-    
+
     // Display the text
     nsresult rv = NS_ERROR_FAILURE;
 
     if (aPresContext->BidiEnabled()) {
-      const nsStyleVisibility* vis = StyleVisibility();
-      if (vis->mDirection == NS_STYLE_DIRECTION_RTL)
-        rv = nsBidiPresUtils::RenderText(str, maxFit, NSBIDI_RTL,
-                                         aPresContext, aRenderingContext,
-                                         aRenderingContext, *fm,
-                                         aRect.XMost() - strWidth, y + maxAscent);
-      else
-        rv = nsBidiPresUtils::RenderText(str, maxFit, NSBIDI_LTR,
-                                         aPresContext, aRenderingContext,
-                                         aRenderingContext, *fm,
-                                         aRect.x, y + maxAscent);
+      nsBidiDirection dir;
+      nscoord x, y;
+
+      if (isVertical) {
+        x = pt.x + maxDescent; // XXX will need update for sideways-left
+        if (wm.IsBidiLTR()) {
+          y = aRect.y;
+          dir = NSBIDI_LTR;
+        } else {
+          y = aRect.YMost() - strWidth;
+          dir = NSBIDI_RTL;
+        }
+      } else {
+        y = pt.y + maxAscent;
+        if (wm.IsBidiLTR()) {
+          x = aRect.x;
+          dir = NSBIDI_LTR;
+        } else {
+          x = aRect.XMost() - strWidth;
+          dir = NSBIDI_RTL;
+        }
+      }
+
+      rv = nsBidiPresUtils::RenderText(str, maxFit, dir,
+                                       aPresContext, aRenderingContext,
+                                       aRenderingContext, *fm, x, y);
     }
     if (NS_FAILED(rv)) {
       nsLayoutUtils::DrawUniDirString(str, maxFit,
-                                      nsPoint(aRect.x, y + maxAscent), *fm,
-                                      aRenderingContext);
+                                      isVertical
+                                        ? nsPoint(pt.x + maxDescent, pt.y)
+                                        : nsPoint(pt.x, pt.y + maxAscent),
+                                      *fm, aRenderingContext);
     }
 
     // Move to the next line
     str += maxFit;
     strLen -= maxFit;
-    y += height;
+    if (wm.IsVerticalRL()) {
+      pt.x -= lineHeight;
+    } else if (wm.IsVerticalLR()) {
+      pt.x += lineHeight;
+    } else {
+      pt.y += lineHeight;
+    }
+
     firstLine = false;
   }
 }
 
 struct nsRecessedBorder : public nsStyleBorder {
   nsRecessedBorder(nscoord aBorderWidth, nsPresContext* aPresContext)
     : nsStyleBorder(aPresContext)
   {
@@ -1242,54 +1284,54 @@ nsImageFrame::DisplayAltFeedback(nsRende
 
   // Clip so we don't render outside the inner rect
   gfx->Save();
   gfx->Clip(NSRectToSnappedRect(inner, PresContext()->AppUnitsPerDevPixel(),
                                 *drawTarget));
 
   // Check if we should display image placeholders
   if (gIconLoad->mPrefShowPlaceholders) {
-    const nsStyleVisibility* vis = StyleVisibility();
     nscoord size = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE);
 
     bool iconUsed = false;
 
     // If we weren't previously displaying an icon, register ourselves
     // as an observer for load and animation updates and flag that we're
     // doing so now.
     if (aRequest && !mDisplayingIcon) {
       gIconLoad->AddIconObserver(this);
       mDisplayingIcon = true;
     }
 
+    WritingMode wm = GetWritingMode();
+    bool flushRight =
+      (!wm.IsVertical() && !wm.IsBidiLTR()) || wm.IsVerticalRL();
 
     // If the icon in question is loaded and decoded, draw it
     uint32_t imageStatus = 0;
     if (aRequest)
       aRequest->GetImageStatus(&imageStatus);
     if (imageStatus & imgIRequest::STATUS_LOAD_COMPLETE) {
       nsCOMPtr<imgIContainer> imgCon;
       aRequest->GetImage(getter_AddRefs(imgCon));
       MOZ_ASSERT(imgCon, "Load complete, but no image container?");
-      nsRect dest((vis->mDirection == NS_STYLE_DIRECTION_RTL) ?
-                  inner.XMost() - size : inner.x,
+      nsRect dest(flushRight ? inner.XMost() - size : inner.x,
                   inner.y, size, size);
       nsLayoutUtils::DrawSingleImage(*gfx, PresContext(), imgCon,
         nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect,
         nullptr, imgIContainer::FLAG_SYNC_DECODE);
       iconUsed = true;
     }
 
     // if we could not draw the icon, flag that we're waiting for it and
     // just draw some graffiti in the mean time
     if (!iconUsed) {
       ColorPattern color(ToDeviceColor(Color(1.f, 0.f, 0.f, 1.f)));
 
-      nscoord iconXPos = (vis->mDirection ==   NS_STYLE_DIRECTION_RTL) ?
-                         inner.XMost() - size : inner.x;
+      nscoord iconXPos = flushRight ? inner.XMost() - size : inner.x;
 
       // stroked rect:
       nsRect rect(iconXPos, inner.y, size, size);
       Rect devPxRect =
         ToRect(nsLayoutUtils::RectToGfxRect(rect, PresContext()->AppUnitsPerDevPixel()));
       drawTarget->StrokeRect(devPxRect, color);
 
       // filled circle in bottom right quadrant of stroked rect:
@@ -1301,20 +1343,27 @@ nsImageFrame::DisplayAltFeedback(nsRende
       RefPtr<PathBuilder> builder = drawTarget->CreatePathBuilder();
       AppendEllipseToPath(builder, devPxRect.Center(), devPxRect.Size());
       RefPtr<Path> ellipse = builder->Finish();
       drawTarget->Fill(ellipse, color);
     }
 
     // Reduce the inner rect by the width of the icon, and leave an
     // additional ICON_PADDING pixels for padding
-    int32_t iconWidth = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE + ICON_PADDING);
-    if (vis->mDirection != NS_STYLE_DIRECTION_RTL)
-      inner.x += iconWidth;
-    inner.width -= iconWidth;
+    int32_t paddedIconSize =
+      nsPresContext::CSSPixelsToAppUnits(ICON_SIZE + ICON_PADDING);
+    if (wm.IsVertical()) {
+      inner.y += paddedIconSize;
+      inner.height -= paddedIconSize;
+    } else {
+      if (!flushRight) {
+        inner.x += paddedIconSize;
+      }
+      inner.width -= paddedIconSize;
+    }
   }
 
   // If there's still room, display the alt-text
   if (!inner.IsEmpty()) {
     nsIContent* content = GetContent();
     if (content) {
       nsXPIDLString altText;
       nsCSSFrameConstructor::GetAlternateTextFor(content, content->Tag(),