Bug 759568 - Part 3. Render text-selection beneath background image; draft
authorCJKu <cku@mozilla.com>
Tue, 12 Apr 2016 00:19:45 +0800
changeset 349449 5873616e0f648ce7cdd8c9071d47fd8840106848
parent 349448 8a40cdebe650dab91f413fdba92927f7d1e9a48a
child 349450 b66a58fe270deceb48ba0528711f008adc685728
push id15089
push usercku@mozilla.com
push dateMon, 11 Apr 2016 16:21:20 +0000
bugs759568
milestone48.0a1
Bug 759568 - Part 3. Render text-selection beneath background image; MozReview-Commit-ID: IZvMfZYqmnZ
layout/generic/nsTextFrame.cpp
layout/generic/nsTextFrame.h
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -93,16 +93,26 @@
 #ifdef DrawText
 #undef DrawText
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
+void
+nsTextFrame::DrawPathCallbacks::NotifySelectionBackgroundNeedsFill(
+                                                    const Rect& aBackgroundRect,
+                                                    nscolor aColor,
+                                                    DrawTarget& aDrawTarget)
+{
+  ColorPattern color(ToDeviceColor(aColor));
+  aDrawTarget.FillRect(aBackgroundRect, color);
+}
+
 struct TabWidth {
   TabWidth(uint32_t aOffset, uint32_t aWidth)
     : mOffset(aOffset), mWidth(float(aWidth))
   { }
 
   uint32_t mOffset; // DOM offset relative to the current frame's offset.
   float    mWidth;  // extra space to be added at this position (in app units)
 };
@@ -5917,17 +5927,16 @@ nsTextFrame::PaintOneShadow(const PaintS
     shadowColor = aShadowDetails->mColor;
     decorationOverrideColor = &shadowColor;
   } else {
     shadowColor = aParams.foregroundColor;
     decorationOverrideColor = nullptr;
   }
 
   aParams.context->Save();
-  aParams.context->NewPath();
   aParams.context->SetColor(Color::FromABGR(shadowColor));
 
   // Draw the text onto our alpha-only surface to capture the alpha values.
   // Remember that the box blur context has a device offset on it, so we don't need to
   // translate any coordinates to fit on the surface.
   gfxFloat advanceWidth;
   nsTextPaintStyle textPaintStyle(this);
   DrawTextParams params(shadowContext);
@@ -6072,17 +6081,17 @@ nsTextFrame::PaintTextWithSelectionColor
     gfxPoint textBaselinePt = vertical ?
       gfxPoint(aParams.textBaselinePt.x, aParams.framePt.y + iOffset) :
       gfxPoint(aParams.framePt.x + iOffset, aParams.textBaselinePt.y);
 
     // Determine what shadow, if any, to draw - either from textStyle
     // or from the ::-moz-selection pseudo-class if specified there
     nsCSSShadowArray* shadow = textStyle->GetTextShadow();
     GetSelectionTextShadow(this, type, *aParams.textPaintStyle, &shadow);
-    if (shadow) {
+    if (shadow && !aParams.callbacks) {
       nscoord startEdge = iOffset;
       if (mTextRun->IsInlineReversed()) {
         startEdge -= hyphenWidth +
           mTextRun->GetAdvanceWidth(range, aParams.provider);
       }
       shadowParams.range = range;
       shadowParams.textBaselinePt = textBaselinePt;
       shadowParams.foregroundColor = foreground;
@@ -6476,16 +6485,49 @@ nsTextFrame::PaintShadows(nsCSSShadowArr
   }
 
   for (uint32_t i = aShadow->Length(); i > 0; --i) {
     PaintOneShadow(aParams, aShadow->ShadowAt(i - 1),
                    shadowMetrics.mBoundingBox, blurFlags);
   }
 }
 
+static bool
+ShouldDrawSelection(const nsIFrame* aFrame,
+                    const nsTextFrame::PaintTextParams& aParams)
+{
+  // Normal text-with-selection rendering sequence is:
+  //   * Paint background > Paint text-selection-color > Paint text
+  // When we have an parent frame with background-clip-text style, rendering
+  // sequence changes to:
+  //   * Paint text-selection-color > Paint background > Paint text
+  //
+  // If there is a parent frame has background-clip:text style,
+  // text-selection-color should be drawn with the background of that parent
+  // frame, so we should not draw it again while painting text frames.
+  //
+  // "aParams.callbacks != nullptr": it means we are currently painting
+  // background. We should paint text-selection-color.
+  // "aParams.callbacks == nullptr": it means we are currently painting text
+  // frame itself. We should not paint text-selection-color.
+  if (!aFrame || aParams.callbacks) {
+    return true;
+  }
+
+  const nsStyleBackground* bg = aFrame->StyleContext()->StyleBackground();
+  const nsStyleImageLayers& layers = bg->mImage;
+  NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, layers) {
+     if (layers.mLayers[i].mClip == NS_STYLE_IMAGELAYER_CLIP_TEXT) {
+       return false;
+    }
+  }
+
+  return ShouldDrawSelection(aFrame->GetParent(), aParams);
+}
+
 void
 nsTextFrame::PaintText(const PaintTextParams& aParams,
                        const nsCharClipDisplayItem& aItem,
                        float aOpacity /* = 1.0f */)
 {
   // Don't pass in the rendering context here, because we need a
   // *reference* context and rendering context might have some transform
   // in it
@@ -6539,17 +6581,18 @@ nsTextFrame::PaintText(const PaintTextPa
     textBaselinePt.x += reversed ? -snappedEndEdge : snappedStartEdge;
   }
   nsCharClipDisplayItem::ClipEdges clipEdges(aItem, snappedStartEdge,
                                              snappedEndEdge);
   nsTextPaintStyle textPaintStyle(this);
   textPaintStyle.SetResolveColors(!aParams.callbacks);
 
   // Fork off to the (slower) paint-with-selection path if necessary.
-  if (aItem.mIsFrameSelected.value()) {
+  if (aItem.mIsFrameSelected.value() &&
+      ShouldDrawSelection(this->GetParent(), aParams)) {
     MOZ_ASSERT(aOpacity == 1.0f, "We don't support opacity with selections!");
     gfxSkipCharsIterator tmp(provider.GetStart());
     Range contentRange(
       uint32_t(tmp.ConvertSkippedToOriginal(startOffset)),
       uint32_t(tmp.ConvertSkippedToOriginal(startOffset + maxLength)));
     PaintTextSelectionParams params(aParams);
     params.textBaselinePt = textBaselinePt;
     params.provider = &provider;
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -344,17 +344,17 @@ public:
     }
 
     /**
      * Called to have the selection highlight drawn before the text is drawn
      * over the top.
      */
     virtual void NotifySelectionBackgroundNeedsFill(const Rect& aBackgroundRect,
                                                     nscolor aColor,
-                                                    DrawTarget& aDrawTarget) { }
+                                                    DrawTarget& aDrawTarget);
 
     /**
      * Called before (for under/over-line) or after (for line-through) the text
      * is drawn to have a text decoration line drawn.
      */
     virtual void PaintDecorationLine(Rect aPath, nscolor aColor) { }
 
     /**