Bug 1269971 - Part 3. Handle selection text color and selection backgrond painting; draft
authorCJKu <cku@mozilla.com>
Fri, 13 May 2016 00:08:53 +0800
changeset 366372 223dd5b4ee852c5237082dce3e3ed7efba3b7089
parent 366371 f1193e084e23184819c8781cb3239372758fd091
child 366373 a0040eaa1236d0b78937c56e1ce14fdec35d1f7a
push id17966
push usercku@mozilla.com
push dateThu, 12 May 2016 16:10:23 +0000
bugs1269971
milestone49.0a1
Bug 1269971 - Part 3. Handle selection text color and selection backgrond painting; MozReview-Commit-ID: CQmqiCmvygr
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/base/nsLayoutUtils.cpp
layout/generic/nsFrame.cpp
layout/generic/nsTextFrame.cpp
layout/generic/nsTextFrame.h
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -494,24 +494,40 @@ AddAnimationsForProperty(nsIFrame* aFram
 
 static void
 GenerateAndPushTextMask(nsIFrame* aFrame, nsRenderingContext* aContext,
                         const nsRect& aFillRect)
 {
   // The main function of enabling background-clip:text property value.
   // When a nsDisplayBackgroundImage detects "text" bg-clip style, it will call
   // this function to
-  // 1. Generate a mask by all descendant text frames
-  // 2. Push the generated mask into aContext.
+  // 1. Paint background color of the selection text if any.
+  // 2. Generate a mask by all descendant text frames
+  // 3. Push the generated mask into aContext.
+  //
+  // TBD: we actually generate display list of aFrame twice here. It's better
+  // to reuse the same display list and paint that one twice, one for selection
+  // background, one for generating text mask.
 
   gfxContext* sourceCtx = aContext->ThebesContext();
   gfxRect bounds =
     nsLayoutUtils::RectToGfxRect(aFillRect,
                                  aFrame->PresContext()->AppUnitsPerDevPixel());
 
+  {
+    // Paint text selection background into sourceCtx.
+    gfxContextMatrixAutoSaveRestore save(sourceCtx);
+    sourceCtx->SetMatrix(sourceCtx->CurrentMatrix().Translate(bounds.TopLeft()));
+
+    nsLayoutUtils::PaintFrame(aContext, aFrame,
+                              nsRect(nsPoint(0, 0), aFrame->GetSize()),
+                              NS_RGB(255, 255, 255),
+                              nsDisplayListBuilderMode::PAINTING_SELECTION_BACKGROUND);
+  }
+
   // Evaluate required surface size.
   IntRect drawRect;
   {
     gfxContextMatrixAutoSaveRestore matRestore(sourceCtx);
 
     sourceCtx->SetMatrix(gfxMatrix());
     gfxRect clipRect = sourceCtx->GetClipExtents();
     drawRect = RoundedOut(ToRect(clipRect));
@@ -2430,17 +2446,18 @@ SetBackgroundClipRegion(DisplayListClipS
 
 /*static*/ bool
 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuilder,
                                                      nsIFrame* aFrame,
                                                      const nsRect& aBackgroundRect,
                                                      nsDisplayList* aList,
                                                      bool aAllowWillPaintBorderOptimization)
 {
-  if (aBuilder->IsForGenerateGlyphPath()) {
+  if (aBuilder->IsForGenerateGlyphMask() ||
+      aBuilder->IsForPaintingSelectionBG()) {
     return true;
   }
 
   nsStyleContext* bgSC = nullptr;
   const nsStyleBackground* bg = nullptr;
   nsRect bgRect = aBackgroundRect + aBuilder->ToReferenceFrame(aFrame);
   nsPresContext* presContext = aFrame->PresContext();
   bool isThemed = aFrame->IsThemed();
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -153,17 +153,18 @@ struct AnimatedGeometryRoot
 };
 
 enum class nsDisplayListBuilderMode : uint8_t {
   PAINTING,
   EVENT_DELIVERY,
   PLUGIN_GEOMETRY,
   FRAME_VISIBILITY,
   TRANSFORM_COMPUTATION,
-  GENERATE_GLYPH
+  GENERATE_GLYPH,
+  PAINTING_SELECTION_BACKGROUND
 };
 
 /**
  * This manages a display list and is passed as a parameter to
  * nsIFrame::BuildDisplayList.
  * It contains the parameters that don't change from frame to frame and manages
  * the display list memory using a PLArena. It also establishes the reference
  * coordinate system for all display list items. Some of the parameters are
@@ -283,25 +284,32 @@ public:
    */
   bool IsForFrameVisibility()
   {
     return mMode == nsDisplayListBuilderMode::FRAME_VISIBILITY;
   }
 
   /**
    * @return true if the display list is being built for creating the glyph
-   * path from text items. While painting the display list, all text display
-   * items should only create glyph paths in target context, instead of
-   * drawing text into it.
+   * mask from text items.
    */
   bool IsForGenerateGlyphMask()
   {
     return mMode == nsDisplayListBuilderMode::GENERATE_GLYPH;
   }
 
+  /**
+   * @return true if the display list is being built for painting selection
+   * background.
+   */
+  bool IsForPaintingSelectionBG()
+  {
+    return mMode == nsDisplayListBuilderMode::PAINTING_SELECTION_BACKGROUND;
+  }
+
   bool WillComputePluginGeometry() { return mWillComputePluginGeometry; }
   /**
    * @return true if "painting is suppressed" during page load and we
    * should paint only the background of the document.
    */
   bool IsBackgroundOnly() {
     NS_ASSERTION(mPresShellStates.Length() > 0,
                  "don't call this if we're not in a presshell");
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -3438,17 +3438,17 @@ nsLayoutUtils::PaintFrame(nsRenderingCon
 
     nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(&builder, id);
 
     PROFILER_LABEL("nsLayoutUtils", "PaintFrame::BuildDisplayList",
       js::ProfileEntry::Category::GRAPHICS);
 
     aFrame->BuildDisplayListForStackingContext(&builder, dirtyRect, &list);
 #ifdef DEBUG
-    if (builder.IsForGenerateGlyphMask()) {
+    if (builder.IsForGenerateGlyphMask() || builder.IsForPaintingSelectionBG()) {
       // PaintFrame is called to generate text glyph by
       // nsDisplayBackgroundImage::Paint or nsDisplayBackgroundColor::Paint.
       //
       // Assert that we do not generate and put nsDisplayBackgroundImage or
       // nsDisplayBackgroundColor into the list again, which would lead to
       // infinite recursion.
       for (nsDisplayItem* i = list.GetBottom(); i; i = i->GetAbove()) {
         MOZ_ASSERT(nsDisplayItem::TYPE_BACKGROUND != i->GetType() &&
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2505,17 +2505,18 @@ nsIFrame::BuildDisplayListForStackingCon
     const nsIFrame* outerReferenceFrame = this;
     if (this != aBuilder->RootReferenceFrame()) {
       outerReferenceFrame =
         aBuilder->FindReferenceFrameFor(GetParent(), &toOuterReferenceFrame);
     }
     buildingDisplayList.SetReferenceFrameAndCurrentOffset(outerReferenceFrame,
       GetOffsetToCrossDoc(outerReferenceFrame));
 
-    if (!aBuilder->IsForGenerateGlyphMask()) {
+    if (!aBuilder->IsForGenerateGlyphMask() &&
+        !aBuilder->IsForPaintingSelectionBG()) {
       nsDisplayTransform *transformItem =
         new (aBuilder) nsDisplayTransform(aBuilder, this, &resultList, dirtyRect);
       resultList.AppendNewToTop(transformItem);
     }
 
     if (HasPerspective()) {
       if (!useFixedPosition && !useStickyPosition) {
         clipState.ExitStackingContextContents(&containerItemScrollClip);
@@ -2599,17 +2600,18 @@ nsIFrame::BuildDisplayListForChild(nsDis
                                    const nsRect&           aDirtyRect,
                                    const nsDisplayListSet& aLists,
                                    uint32_t                aFlags) {
   // If painting is restricted to just the background of the top level frame,
   // then we have nothing to do here.
   if (aBuilder->IsBackgroundOnly())
     return;
 
-  if (aBuilder->IsForGenerateGlyphMask()) {
+  if (aBuilder->IsForGenerateGlyphMask() ||
+      aBuilder->IsForPaintingSelectionBG()) {
     if (nsGkAtoms::textFrame != aChild->GetType() && aChild->IsLeaf()) {
       return;
     }
   }
 
   nsIFrame* child = aChild;
   if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
     return;
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -4766,17 +4766,18 @@ nsDisplayText::Paint(nsDisplayListBuilde
   gfxContext* ctx = aCtx->ThebesContext();
   gfxContextAutoSaveRestore save(ctx);
 
   gfxRect pixelVisible(extraVisible.x, extraVisible.y,
                        extraVisible.width, extraVisible.height);
   pixelVisible.Inflate(2);
   pixelVisible.RoundOut();
 
-  if (!aBuilder->IsForGenerateGlyphMask()) {
+  if (!aBuilder->IsForGenerateGlyphMask() &&
+      !aBuilder->IsForPaintingSelectionBG()) {
     ctx->NewPath();
     ctx->Rectangle(pixelVisible);
     ctx->Clip();
   }
 
   NS_ASSERTION(mVisIStartEdge >= 0, "illegal start edge");
   NS_ASSERTION(mVisIEndEdge >= 0, "illegal end edge");
 
@@ -4794,16 +4795,17 @@ nsDisplayText::Paint(nsDisplayListBuilde
     }
   }
   nsTextFrame::PaintTextParams params(aCtx->ThebesContext());
   params.framePt = gfxPoint(framePt.x, framePt.y);
 
   params.dirtyRect = extraVisible;
   nsTextFrame::DrawPathCallbacks callbacks;
   params.generateTextMask = aBuilder->IsForGenerateGlyphMask();
+  params.paintSelectionBackground = aBuilder->IsForPaintingSelectionBG();
   f->PaintText(params, *this, mOpacity);
 }
 
 void
 nsTextFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                               const nsRect&           aDirtyRect,
                               const nsDisplayListSet& aLists)
 {
@@ -5987,17 +5989,18 @@ nsTextFrame::PaintTextWithSelectionColor
   const gfxFloat startIOffset = vertical ?
     aParams.textBaselinePt.y - aParams.framePt.y :
     aParams.textBaselinePt.x - aParams.framePt.x;
   gfxFloat iOffset, hyphenWidth;
   Range range; // in transformed string
   SelectionType type;
   TextRangeStyle rangeStyle;
   // Draw background colors
-  if (anyBackgrounds) {
+  if (anyBackgrounds && (!aParams.generateTextMask ||
+                         aParams.paintSelectionBackground)) {
     int32_t appUnitsPerDevPixel =
       aParams.textPaintStyle->PresContext()->AppUnitsPerDevPixel();
     SelectionIterator iterator(prevailingSelections, contentRange,
                                *aParams.provider, mTextRun, startIOffset);
     while (iterator.GetNextSegment(&iOffset, &range, &hyphenWidth,
                                    &type, &rangeStyle)) {
       nscolor foreground, background;
       GetSelectionTextColors(type, *aParams.textPaintStyle, rangeStyle,
@@ -6019,16 +6022,20 @@ nsTextFrame::PaintTextWithSelectionColor
           *aParams.context->GetDrawTarget(), background, aParams.dirtyRect,
           LayoutDeviceRect::FromAppUnits(bgRect, appUnitsPerDevPixel),
           aParams.callbacks);
       }
       iterator.UpdateWithAdvance(advance);
     }
   }
 
+  if (aParams.paintSelectionBackground) {
+    return true;
+  }
+
   gfxFloat advance;
   DrawTextParams params(aParams.context);
   params.dirtyRect = aParams.dirtyRect;
   params.framePt = aParams.framePt;
   params.provider = aParams.provider;
   params.textStyle = aParams.textPaintStyle;
   params.clipEdges = &aClipEdges;
   params.advanceWidth = &advance;
@@ -6040,18 +6047,23 @@ nsTextFrame::PaintTextWithSelectionColor
 
   // Draw text
   const nsStyleText* textStyle = StyleText();
   SelectionIterator iterator(prevailingSelections, contentRange,
                              *aParams.provider, mTextRun, startIOffset);
   while (iterator.GetNextSegment(&iOffset, &range, &hyphenWidth,
                                  &type, &rangeStyle)) {
     nscolor foreground, background;
-    GetSelectionTextColors(type, *aParams.textPaintStyle, rangeStyle,
-                           &foreground, &background);
+    if (aParams.generateTextMask) {
+      foreground = NS_RGBA(0, 0, 0, 255);
+    } else {
+      GetSelectionTextColors(type, *aParams.textPaintStyle, rangeStyle,
+                             &foreground, &background);
+    }
+
     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);
@@ -6475,46 +6487,41 @@ 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)
+ShouldDrawSelection(const nsIFrame* aFrame)
 {
   // 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) {
+
+  if (!aFrame) {
     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);
+  return ShouldDrawSelection(aFrame->GetParent());
 }
 
 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
@@ -6571,32 +6578,37 @@ nsTextFrame::PaintText(const PaintTextPa
   }
   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() &&
-      ShouldDrawSelection(this->GetParent(), aParams)) {
+      (aParams.paintSelectionBackground ||
+       ShouldDrawSelection(this->GetParent()))) {
     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;
     params.contentRange = contentRange;
     params.textPaintStyle = &textPaintStyle;
     if (PaintTextWithSelection(params, clipEdges)) {
       return;
     }
   }
 
+  if (aParams.paintSelectionBackground) {
+    return;
+  }
+
   nscolor foregroundColor = aParams.generateTextMask
                             ? NS_RGBA(0, 0, 0, 255)
                             : textPaintStyle.GetTextColor();
   if (aOpacity != 1.0f) {
     gfx::Color gfxColor = gfx::Color::FromABGR(foregroundColor);
     gfxColor.a *= aOpacity;
     foregroundColor = gfxColor.ToABGR();
   }
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -388,16 +388,17 @@ public:
   struct PaintTextParams
   {
     gfxContext* context;
     gfxPoint framePt;
     LayoutDeviceRect dirtyRect;
     gfxTextContextPaint* contextPaint = nullptr;
     DrawPathCallbacks* callbacks = nullptr;
     bool generateTextMask = false;
+    bool paintSelectionBackground = false;
     explicit PaintTextParams(gfxContext* aContext) : context(aContext) {}
   };
 
   struct PaintTextSelectionParams : PaintTextParams
   {
     gfxPoint textBaselinePt;
     PropertyProvider* provider = nullptr;
     Range contentRange;