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 248099 1fc59f36a868060f92eb23e8b88a33b8a3f4276a
parent 248098 d3bbe13bbd085772208cfb0dcb3bc458f8ee7d5c
child 248100 5179ff7d75735edf3fc036aad7378e6178452c57
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmontagu
bugs1113684
milestone37.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 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(),