Bug 1269971 - Part 3. Handle selection text color and selection backgrond painting;
MozReview-Commit-ID: CQmqiCmvygr
--- 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;