Bug 1529992 - don't apply shadow adjustment to text bounds in gecko with WR r=mattwoodrow
authorAlexis Beingessner <a.beingessner@gmail.com>
Mon, 15 Apr 2019 23:13:47 +0000
changeset 469584 472d0da9b5a6
parent 469583 a5ccc6802a97
child 469585 951d2c0a477d
push id35874
push userccoroiu@mozilla.com
push dateTue, 16 Apr 2019 04:04:58 +0000
treeherdermozilla-central@be3f40425b52 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1529992
milestone68.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 1529992 - don't apply shadow adjustment to text bounds in gecko with WR r=mattwoodrow And tell webrender to do them itself (they have fundamental mismatches in how they define shadow bounds). Differential Revision: https://phabricator.services.mozilla.com/D26845
layout/generic/nsTextFrame.cpp
layout/generic/nsTextFrame.h
layout/painting/nsDisplayList.cpp
layout/reftests/bugs/1529992-1-ref.html
layout/reftests/bugs/1529992-1.html
layout/reftests/bugs/1529992-2-ref.html
layout/reftests/bugs/1529992-2.html
layout/reftests/bugs/reftest.list
layout/reftests/text-shadow/reftest.list
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -5244,17 +5244,18 @@ nsRect nsTextFrame::UpdateTextEmphasis(W
   SetProperty(EmphasisMarkProperty(), info);
   return overflowRect.GetPhysicalRect(aWM, frameSize.GetPhysicalSize(aWM));
 }
 
 void nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
                                           nsIFrame* aBlock,
                                           PropertyProvider& aProvider,
                                           nsRect* aVisualOverflowRect,
-                                          bool aIncludeTextDecorations) {
+                                          bool aIncludeTextDecorations,
+                                          bool aIncludeShadows) {
   const WritingMode wm = GetWritingMode();
   bool verticalRun = mTextRun->IsVertical();
   const gfxFloat appUnitsPerDevUnit = aPresContext->AppUnitsPerDevPixel();
 
   if (IsFloatingFirstLetterChild()) {
     bool inverted = wm.IsLineInverted();
     // The underline/overline drawable area must be contained in the overflow
     // rect when this is in floating first letter frame at *both* modes.
@@ -5404,19 +5405,21 @@ void nsTextFrame::UnionAdditionalOverflo
     // Inflate rect by stroke-width/2; we add an extra pixel to allow for
     // antialiasing, rounding errors, etc.
     nsRect strokeRect = *aVisualOverflowRect;
     strokeRect.Inflate(textStrokeWidth / 2 + appUnitsPerDevUnit);
     aVisualOverflowRect->UnionRect(*aVisualOverflowRect, strokeRect);
   }
 
   // Text-shadow overflows
-  nsRect shadowRect =
-      nsLayoutUtils::GetTextShadowRectsUnion(*aVisualOverflowRect, this);
-  aVisualOverflowRect->UnionRect(*aVisualOverflowRect, shadowRect);
+  if (aIncludeShadows) {
+    nsRect shadowRect =
+        nsLayoutUtils::GetTextShadowRectsUnion(*aVisualOverflowRect, this);
+    aVisualOverflowRect->UnionRect(*aVisualOverflowRect, shadowRect);
+  }
 
   // When this frame is not selected, the text-decoration area must be in
   // frame bounds.
   if (!IsSelected() ||
       !CombineSelectionUnderlineRect(aPresContext, *aVisualOverflowRect))
     return;
   AddStateBits(TEXT_SELECTION_UNDERLINE_OVERFLOWED);
 }
@@ -5854,17 +5857,17 @@ void nsTextFrame::PaintOneShadow(const P
   nscolor shadowColor =
       aShadowDetails->mColor.CalcColor(aParams.foregroundColor);
 
   if (auto* textDrawer = aParams.context->GetTextDrawer()) {
     wr::Shadow wrShadow;
 
     // Gecko already inflates the bounding rect of text shadows,
     // so tell WR not to inflate again.
-    wrShadow.should_inflate = false;
+    wrShadow.should_inflate = true;
 
     wrShadow.offset = {
         PresContext()->AppUnitsToFloatDevPixels(aShadowDetails->mXOffset),
         PresContext()->AppUnitsToFloatDevPixels(aShadowDetails->mYOffset)};
 
     wrShadow.blur_radius = PresContext()->AppUnitsToFloatDevPixels(blurRadius);
     wrShadow.color = wr::ToColorF(ToDeviceColor(shadowColor));
 
@@ -6928,16 +6931,30 @@ void nsTextFrame::DrawText(Range aRange,
     DrawTextRun(aRange, aTextBaselinePt, aParams);
   }
 
   if (auto* textDrawer = aParams.context->GetTextDrawer()) {
     textDrawer->TerminateShadows();
   }
 }
 
+NS_DECLARE_FRAME_PROPERTY_DELETABLE(WebRenderTextBounds, nsRect)
+
+nsRect nsTextFrame::WebRenderBounds() {
+  nsRect* cachedBounds = GetProperty(WebRenderTextBounds());
+  if (!cachedBounds) {
+    nsOverflowAreas overflowAreas;
+    ComputeCustomOverflowInternal(overflowAreas, false);
+    cachedBounds = new nsRect();
+    *cachedBounds = overflowAreas.VisualOverflow();
+    SetProperty(WebRenderTextBounds(), cachedBounds);
+  }
+  return *cachedBounds;
+}
+
 int16_t nsTextFrame::GetSelectionStatus(int16_t* aSelectionFlags) {
   // get the selection controller
   nsCOMPtr<nsISelectionController> selectionController;
   nsresult rv = GetSelectionController(PresContext(),
                                        getter_AddRefs(selectionController));
   if (NS_FAILED(rv) || !selectionController)
     return nsISelectionController::SELECTION_OFF;
 
@@ -8697,17 +8714,17 @@ void nsTextFrame::ReflowText(nsLineLayou
   /////////////////////////////////////////////////////////////////////
 
   // Clear out the reflow input flags in mState. We also clear the whitespace
   // flags because this can change whether the frame maps whitespace-only text
   // or not. We also clear the flag that tracks whether we had a pending
   // reflow request from CharacterDataChanged (since we're reflowing now).
   RemoveStateBits(TEXT_REFLOW_FLAGS | TEXT_WHITESPACE_FLAGS);
   mReflowRequestedForCharDataChange = false;
-
+  DeleteProperty(WebRenderTextBounds());
   // Temporarily map all possible content while we construct our new textrun.
   // so that when doing reflow our styles prevail over any part of the
   // textrun we look at. Note that next-in-flows may be mapping the same
   // content; gfxTextRun construction logic will ensure that we take priority.
   int32_t maxContentLength = GetInFlowContentLength();
 
   // We don't need to reflow if there is no content.
   if (!maxContentLength) {
@@ -9148,17 +9165,17 @@ void nsTextFrame::ReflowText(nsLineLayou
   }
   aMetrics.SetOverflowAreasToDesiredBounds();
   aMetrics.VisualOverflow().UnionRect(aMetrics.VisualOverflow(), boundingBox);
 
   // When we have text decorations, we don't need to compute their overflow now
   // because we're guaranteed to do it later
   // (see nsLineLayout::RelativePositionFrames)
   UnionAdditionalOverflow(presContext, aLineLayout.LineContainerRI()->mFrame,
-                          provider, &aMetrics.VisualOverflow(), false);
+                          provider, &aMetrics.VisualOverflow(), false, true);
 
   /////////////////////////////////////////////////////////////////////
   // Clean up, update state
   /////////////////////////////////////////////////////////////////////
 
   // If all our characters are discarded or collapsed, then trimmable width
   // from the last textframe should be preserved. Otherwise the trimmable width
   // from this textframe overrides. (Currently in CSS trimmable width can be
@@ -9359,17 +9376,20 @@ nsTextFrame::TrimOutput nsTextFrame::Tri
 
 #ifdef NOISY_TRIM
   ListTag(stdout);
   printf(": trim => %d\n", result.mDeltaWidth);
 #endif
   return result;
 }
 
-nsOverflowAreas nsTextFrame::RecomputeOverflow(nsIFrame* aBlockFrame) {
+nsOverflowAreas nsTextFrame::RecomputeOverflow(nsIFrame* aBlockFrame,
+                                               bool aIncludeShadows) {
+  DeleteProperty(WebRenderTextBounds());
+
   nsRect bounds(nsPoint(0, 0), GetSize());
   nsOverflowAreas result(bounds, bounds);
 
   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
   if (!mTextRun) return result;
 
   PropertyProvider provider(this, iter, nsTextFrame::eInflated, mFontMetrics);
   // Don't trim trailing space, in case we need to paint it as selected.
@@ -9385,17 +9405,18 @@ nsOverflowAreas nsTextFrame::RecomputeOv
   boundingBox += nsPoint(0, mAscent);
   if (mTextRun->IsVertical()) {
     // Swap line-relative textMetrics dimensions to physical coordinates.
     Swap(boundingBox.x, boundingBox.y);
     Swap(boundingBox.width, boundingBox.height);
   }
   nsRect& vis = result.VisualOverflow();
   vis.UnionRect(vis, boundingBox);
-  UnionAdditionalOverflow(PresContext(), aBlockFrame, provider, &vis, true);
+  UnionAdditionalOverflow(PresContext(), aBlockFrame, provider, &vis, true,
+      aIncludeShadows);
   return result;
 }
 
 static void TransformChars(nsTextFrame* aFrame, const nsStyleText* aStyle,
                            const gfxTextRun* aTextRun, uint32_t aSkippedOffset,
                            const nsTextFragment* aFrag, int32_t aFragOffset,
                            int32_t aFragLen, nsAString& aOut) {
   nsAutoString fragString;
@@ -9779,16 +9800,21 @@ bool nsTextFrame::HasAnyNoncollapsedChar
   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
   int32_t offset = GetContentOffset(), offsetEnd = GetContentEnd();
   int32_t skippedOffset = iter.ConvertOriginalToSkipped(offset);
   int32_t skippedOffsetEnd = iter.ConvertOriginalToSkipped(offsetEnd);
   return skippedOffset != skippedOffsetEnd;
 }
 
 bool nsTextFrame::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) {
+  return ComputeCustomOverflowInternal(aOverflowAreas, true);
+}
+
+bool nsTextFrame::ComputeCustomOverflowInternal(nsOverflowAreas& aOverflowAreas,
+                                                bool aIncludeShadows) {
   if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
     return true;
   }
 
   nsIFrame* decorationsBlock;
   if (IsFloatingFirstLetterChild()) {
     decorationsBlock = GetParent();
   } else {
@@ -9803,17 +9829,17 @@ bool nsTextFrame::ComputeCustomOverflow(
       f = f->GetParent();
       if (!f) {
         NS_ERROR("Couldn't find any block ancestor (for text decorations)");
         return nsFrame::ComputeCustomOverflow(aOverflowAreas);
       }
     }
   }
 
-  aOverflowAreas = RecomputeOverflow(decorationsBlock);
+  aOverflowAreas = RecomputeOverflow(decorationsBlock, aIncludeShadows);
   return nsFrame::ComputeCustomOverflow(aOverflowAreas);
 }
 
 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(JustificationAssignmentProperty, int32_t)
 
 void nsTextFrame::AssignJustificationGaps(
     const mozilla::JustificationAssignment& aAssign) {
   int32_t encoded = (aAssign.mGapsAtStart << 8) | aAssign.mGapsAtEnd;
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -265,17 +265,18 @@ class nsTextFrame : public nsFrame {
   };
   TrimOutput TrimTrailingWhiteSpace(DrawTarget* aDrawTarget);
   RenderedText GetRenderedText(
       uint32_t aStartOffset = 0, uint32_t aEndOffset = UINT32_MAX,
       TextOffsetType aOffsetType = TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
       TrailingWhitespace aTrimTrailingWhitespace =
           TrailingWhitespace::TRIM_TRAILING_WHITESPACE) final;
 
-  nsOverflowAreas RecomputeOverflow(nsIFrame* aBlockFrame);
+  nsOverflowAreas RecomputeOverflow(nsIFrame* aBlockFrame,
+                                    bool aIncludeShadows = true);
 
   enum TextRunType {
     // Anything in reflow (but not intrinsic width calculation) or
     // painting should use the inflated text run (i.e., with font size
     // inflation applied).
     eInflated,
     // Intrinsic width calculation should use the non-inflated text run.
     // When there is font size inflation, it will be different.
@@ -584,16 +585,18 @@ class nsTextFrame : public nsFrame {
                   DrawTarget* aDrawTarget, ReflowOutput& aMetrics,
                   nsReflowStatus& aStatus);
 
   bool IsFloatingFirstLetterChild() const;
 
   bool IsInitialLetterChild() const;
 
   bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) final;
+  bool ComputeCustomOverflowInternal(nsOverflowAreas& aOverflowAreas,
+                                     bool aIncludeShadows);
 
   void AssignJustificationGaps(const mozilla::JustificationAssignment& aAssign);
   mozilla::JustificationAssignment GetJustificationAssignment() const;
 
   uint32_t CountGraphemeClusters() const;
 
   bool HasAnyNoncollapsedCharacters() final;
 
@@ -604,16 +607,18 @@ class nsTextFrame : public nsFrame {
    */
   void NotifyNativeAnonymousTextnodeChange(uint32_t aOldLength);
 
   void SetInflatedFontMetrics(nsFontMetrics* aMetrics) {
     mFontMetrics = aMetrics;
   }
   nsFontMetrics* InflatedFontMetrics() const { return mFontMetrics; }
 
+  nsRect WebRenderBounds();
+
  protected:
   virtual ~nsTextFrame();
 
   friend class nsDisplayTextGeometry;
   friend class nsDisplayText;
 
   RefPtr<nsFontMetrics> mFontMetrics;
   RefPtr<gfxTextRun> mTextRun;
@@ -641,17 +646,18 @@ class nsTextFrame : public nsFrame {
    */
   bool IsFrameSelected() const final;
 
   mozilla::UniquePtr<SelectionDetails> GetSelectionDetails();
 
   void UnionAdditionalOverflow(nsPresContext* aPresContext, nsIFrame* aBlock,
                                PropertyProvider& aProvider,
                                nsRect* aVisualOverflowRect,
-                               bool aIncludeTextDecorations);
+                               bool aIncludeTextDecorations,
+                               bool aIncludeShadows);
 
   // Update information of emphasis marks, and return the visial
   // overflow rect of the emphasis marks.
   nsRect UpdateTextEmphasis(mozilla::WritingMode aWM,
                             PropertyProvider& aProvider);
 
   struct PaintShadowParams {
     gfxTextRun::Range range;
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -8948,32 +8948,45 @@ void nsDisplayText::Paint(nsDisplayListB
   RenderToContext(aCtx, aBuilder);
 }
 
 bool nsDisplayText::CreateWebRenderCommands(
     mozilla::wr::DisplayListBuilder& aBuilder,
     mozilla::wr::IpcResourceUpdateQueue& aResources,
     const StackingContextHelper& aSc, RenderRootStateManager* aManager,
     nsDisplayListBuilder* aDisplayListBuilder) {
-  if (mBounds.IsEmpty()) {
+  auto* f = static_cast<nsTextFrame*>(mFrame);
+  auto appUnitsPerDevPixel = f->PresContext()->AppUnitsPerDevPixel();
+
+  nsRect bounds = f->WebRenderBounds() + ToReferenceFrame();
+  // Bug 748228
+  bounds.Inflate(appUnitsPerDevPixel);
+
+  if (bounds.IsEmpty()) {
     return true;
   }
 
-  auto appUnitsPerDevPixel = Frame()->PresContext()->AppUnitsPerDevPixel();
   gfx::Point deviceOffset =
-      LayoutDevicePoint::FromAppUnits(mBounds.TopLeft(), appUnitsPerDevPixel)
+      LayoutDevicePoint::FromAppUnits(bounds.TopLeft(), appUnitsPerDevPixel)
           .ToUnknownPoint();
 
-  nsRect visible = GetPaintRect();
-  visible.Inflate(3 * appUnitsPerDevPixel);
-
-  visible = visible.Intersect(mBounds);
+  // Clipping the bounds to the PaintRect (factoring in what's covered by parent
+  // frames) let's us early reject a bunch of things, but it can produce
+  // incorrect results for shadows, because they can translate things back into
+  // view. Also if we're selected we might have some shadows from the
+  // ::selected and ::inctive-selected pseudo-selectors. So don't do this
+  // optimization if we have shadows or a selection.
+  if (!(IsSelected() || f->StyleText()->GetTextShadow())) {
+    nsRect visible = GetPaintRect();
+    visible.Inflate(3 * appUnitsPerDevPixel);
+    bounds = bounds.Intersect(visible);
+  }
 
   RefPtr<gfxContext> textDrawer = aBuilder.GetTextContext(
-      aResources, aSc, aManager, this, visible, deviceOffset);
+      aResources, aSc, aManager, this, bounds, deviceOffset);
 
   RenderToContext(textDrawer, aDisplayListBuilder, true);
 
   return textDrawer->GetTextDrawer()->Finish();
 }
 
 void nsDisplayText::RenderToContext(gfxContext* aCtx,
                                     nsDisplayListBuilder* aBuilder,
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1529992-1-ref.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html><head>
+<style>
+body {
+    font-size: 60px;
+    position: absolute;
+    margin: 0px;
+    padding: 0px;
+}
+div {
+    position: absolute;
+    margin: 0px;
+    padding: 0px;
+}
+#shadow1 {
+    top: -30px;
+    left: -60px;
+}
+#shadow2 {
+    top: 100px;
+    left: -60px;
+}
+#shadow3 {
+    top: -30px;
+    left: 100px;
+}
+#shadow4 {
+    top: 200px;
+    left: -60px;
+}
+#shadow5 {
+    top: -30px;
+    left: 250px;
+}
+#shadow1-ref {
+    top: 20px;
+    left: 20px;
+}
+#shadow2-ref {
+    color: transparent;
+    text-shadow: 0px 0px 10px black;
+    top: 150px;
+    left: 20px;
+}
+#shadow3-ref {
+    color: transparent;
+    text-shadow: 0px 0px 10px black;
+    top: 20px;
+    left: 180px;
+}
+#shadow4-ref {
+    color: transparent;
+    top: 205px;
+    left: -40px;
+    text-shadow: 0px 0px 10px black;
+}
+#shadow5-ref {
+    color: transparent;
+    top: -25px;
+    left: 270px;
+    text-shadow: 0px 0px 10px black;
+}
+</style>
+</head><body>
+    <div id="shadow1-ref">hello</div>
+    <div id="shadow2-ref">hello</div>
+    <div id="shadow3-ref">hello</div>
+    <div id="shadow4-ref">hello</div>
+    <div id="shadow5-ref">hello</div>
+    <div id="shadow1">hello</div>
+    <div id="shadow2">hello</div>
+    <div id="shadow3">hello</div>
+    <div id="shadow4">hello&nbsp;&nbsp;</div>
+    <div id="shadow5">hello&nbsp;&nbsp;</div>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1529992-1.html
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+
+<!--
+This is a bunch of tests that verify we properly handle weird corner cases
+in shadow bounding boxes.
+
+Problem 1: parts of text that are seemingly hidden (as in overflow:hidden)
+may actually be visible in shadows, because they include an offset. We can
+mess this up in two ways:
+
+    1a. we completely culled a glyph from existence for being out of view
+
+    1b. we have all the glyphs but are clipping parts out in the shadow
+
+Problem 2: shadows that are defined by the ::selection pseudo-class are
+trapped inside the local selection rect of the item. This means they
+*can and should* sometimes get weirdly clipped (shadow6 and shadow7 in
+version 2 of this test). Also just more generally selections take a bunch
+of special paths in text styling so they are included to make sure both
+paths are tested.
+
+We messed all of these up for webrender because it handles shadows in a very
+different way from non-webrender.
+
+The selection-box is inherently implementation-specific, so I apologize in
+advanced if this test breaks when you tweak how selections work. Hopefully
+only shadow6 needs to be tweaked?
+-->
+
+<html class="reftest-wait"><head>
+<script type="text/javascript">
+function onload() {
+  var range = document.createRange();
+  range.selectNodeContents(document.getElementById("selectMe"));
+  var sel = window.getSelection();
+  sel.removeAllRanges();
+  sel.addRange(range);
+
+  document.documentElement.className = '';
+}
+</script>
+<style>
+body {
+    font-size: 60px;
+    position: absolute;
+    margin: 0px;
+    padding: 0px;
+}
+div {
+    position: absolute;
+    margin: 0px;
+    padding: 0px;
+}
+div::selection {
+    /* hide all selection boxes for convenience */
+    background-color: transparent;
+}
+#shadow1 {
+    text-shadow: 80px 50px 0px;
+    top: -30px;
+    left: -60px;
+}
+#shadow2 {
+    text-shadow: 80px 50px 10px;
+    top: 100px;
+    left: -60px;
+}
+#shadow3 {
+    text-shadow: 80px 50px 10px;
+    top: -30px;
+    left: 100px;
+}
+#shadow4 {
+    top: 200px;
+    left: -60px;
+}
+#shadow4::selection {
+    text-shadow: 20px 5px 10px;
+}
+#shadow5 {
+    top: -30px;
+    left: 250px;
+}
+#shadow5::selection {
+    text-shadow: 20px 5px 10px;
+}
+</style>
+</head><body id="selectMe" onload="onload()">
+    <div id="shadow1">hello</div>
+    <div id="shadow2">hello</div>
+    <div id="shadow3">hello</div>
+    <div id="shadow4">hello&nbsp;&nbsp;</div>
+    <div id="shadow5">hello&nbsp;&nbsp;</div>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1529992-2-ref.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html><head>
+<style>
+body {
+    font-size: 60px;
+    position: absolute;
+    margin: 0px;
+    padding: 0px;
+}
+div {
+    position: absolute;
+    margin: 0px;
+    padding: 0px;
+}
+#shadow6 {
+    color: transparent;
+}
+#shadow7 {
+    color: transparent;
+}
+#shadow6-ref {
+    color: green;
+    top: 200px;
+    left: 230px;
+}
+#shadow7-ref {
+    color: transparent;
+    top: 200px;
+    left: 530px;
+    text-shadow: 0px 0px 5px green;
+}
+</style>
+</head><body>
+    <div id="shadow6-ref">hello</div>
+    <div id="shadow7-ref">hello</div>
+    <div id="shadow6">hello&nbsp;i</div>
+    <div id="shadow7">hello&nbsp;i</div>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1529992-2.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+
+<!--
+see version 1 of this test for details
+-->
+
+<html class="reftest-wait"><head>
+<script type="text/javascript">
+function onload() {
+  var range = document.createRange();
+  range.selectNodeContents(document.getElementById("selectMe"));
+  var sel = window.getSelection();
+  sel.removeAllRanges();
+  sel.addRange(range);
+
+  document.documentElement.className = '';
+}
+</script>
+<style>
+body {
+    font-size: 60px;
+    position: absolute;
+    margin: 0px;
+    padding: 0px;
+}
+div {
+    position: absolute;
+    margin: 0px;
+    padding: 0px;
+}
+div::selection {
+    /* hide all selection boxes for convenience */
+    background-color: transparent;
+}
+#shadow6 {
+    top: 200px;
+    left: 200px;
+}
+#shadow6::selection {
+    color: transparent;
+    text-shadow: 30px 0px 0px green;
+}
+#shadow7 {
+    top: 200px;
+    left: 500px;
+}
+#shadow7::selection {
+    color: transparent;
+    text-shadow: 30px 0px 5px green;
+}
+</style>
+</head><body id="selectMe" onload="onload()">
+    <div id="shadow6">hello&nbsp;i</div>
+    <div id="shadow7">hello&nbsp;i</div>
+</body></html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -2099,9 +2099,11 @@ fuzzy(0-1,0-625) == 1466638-1.html 14666
 == 1483649-1.xul 1483649-1-ref.xul
 test-pref(layout.css.contain.enabled,true) == 1483946.html 1483946-ref.html
 test-pref(layout.css.visited_links_enabled,false) == 1488155.html 1488155-ref.html
 == 1492660-1.html 1492660-1-ref.html
 pref(layout.css.supports-selector.enabled,true) == 1499386.html 1499386-ref.html
 pref(layout.css.supports-selector.enabled,false) != 1499386.html 1499386-ref.html
 == 1509425-1.html 1509425-1-ref.html
 == 1511570.html 1511570-ref.html
+fuzzy-if(!webrender,1-5,144-547) == 1529992-1.html 1529992-1-ref.html
+fuzzy-if(!webrender,0-6,0-34) fails-if(webrender) == 1529992-2.html 1529992-2-ref.html
 == 1535040-1.html 1535040-1-ref.html
--- a/layout/reftests/text-shadow/reftest.list
+++ b/layout/reftests/text-shadow/reftest.list
@@ -1,16 +1,16 @@
 == 723669.html 723669-ref.html
 
 == basic.xul basic-ref.xul
 random-if(Android) == basic-negcoord.xul basic-negcoord-ref.xul
 != blur.xul blur-notref.xul
 == color-inherit.xul color-inherit-ref.xul
 == multiple-noblur.xul multiple-noblur-ref.xul
-== blur-opacity.html blur-opacity-ref.html
+fuzzy-if(webrender&&gtkWidget,128-128,160-160) == blur-opacity.html blur-opacity-ref.html
 
 == basic.html basic-ref.html
 == basic-negcoord.html basic-negcoord-ref.html
 == basic-opacity.html basic-opacity-ref.html
 != blur.html blur-notref.html
 == color-inherit.html color-inherit-ref.html
 == color-parserorder.html color-parserorder-ref.html
 == decorations-multiple-zorder.html decorations-multiple-zorder-ref.html