Backed out changeset c1f9baaa7b63 (bug 1406510) because it was already landed on inbound while autoland was closed.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 12 Oct 2017 18:29:11 -0400
changeset 385951 2b746fdf27e0d696aaa3da5339c8fa369e947339
parent 385950 e4ce7eacfdfe81bb559aa6db60f3ecb73868bbe3
child 385952 2ad9ea63f3cc4413097e3fa2ab978b25836954f1
push id32672
push userarchaeopteryx@coole-files.de
push dateFri, 13 Oct 2017 09:00:05 +0000
treeherdermozilla-central@3efcb26e5f37 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1406510
milestone58.0a1
backs outc1f9baaa7b63ce9fde52044adf8bef2d9e42cb86
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
Backed out changeset c1f9baaa7b63 (bug 1406510) because it was already landed on inbound while autoland was closed.
gfx/webrender_bindings/WebRenderAPI.cpp
gfx/webrender_bindings/WebRenderAPI.h
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/webrender_ffi_generated.h
layout/generic/TextDrawTarget.h
layout/generic/TextOverflow.cpp
layout/generic/nsTextFrame.cpp
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -632,20 +632,16 @@ DisplayListBuilder::DisplayListBuilder(P
 }
 
 DisplayListBuilder::~DisplayListBuilder()
 {
   MOZ_COUNT_DTOR(DisplayListBuilder);
   wr_state_delete(mWrState);
 }
 
-void DisplayListBuilder::Save() { wr_dp_save(mWrState); }
-void DisplayListBuilder::Restore() { wr_dp_restore(mWrState); }
-void DisplayListBuilder::ClearSave() { wr_dp_clear_save(mWrState); }
-
 void
 DisplayListBuilder::Finalize(wr::LayoutSize& aOutContentSize,
                              BuiltDisplayList& aOutDisplayList)
 {
   wr_api_finalize_builder(mWrState,
                           &aOutContentSize,
                           &aOutDisplayList.dl_desc,
                           &aOutDisplayList.dl.inner);
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -203,20 +203,16 @@ class DisplayListBuilder {
 public:
   explicit DisplayListBuilder(wr::PipelineId aId,
                               const wr::LayoutSize& aContentSize,
                               size_t aCapacity = 0);
   DisplayListBuilder(DisplayListBuilder&&) = default;
 
   ~DisplayListBuilder();
 
-  void Save();
-  void Restore();
-  void ClearSave();
-
   void Finalize(wr::LayoutSize& aOutContentSize,
                 wr::BuiltDisplayList& aOutDisplayList);
 
   void PushStackingContext(const wr::LayoutRect& aBounds, // TODO: We should work with strongly typed rects
                            const uint64_t& aAnimationId,
                            const float* aOpacity,
                            const gfx::Matrix4x4* aTransform,
                            wr::TransformStyle aTransformStyle,
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1093,31 +1093,16 @@ pub extern "C" fn wr_state_delete(state:
     assert!(unsafe { !is_in_render_thread() });
 
     unsafe {
         Box::from_raw(state);
     }
 }
 
 #[no_mangle]
-pub extern "C" fn wr_dp_save(state: &mut WrState) {
-    state.frame_builder.dl_builder.save();
-}
-
-#[no_mangle]
-pub extern "C" fn wr_dp_restore(state: &mut WrState) {
-    state.frame_builder.dl_builder.restore();
-}
-
-#[no_mangle]
-pub extern "C" fn wr_dp_clear_save(state: &mut WrState) {
-    state.frame_builder.dl_builder.clear_save();
-}
-
-#[no_mangle]
 pub extern "C" fn wr_dp_push_stacking_context(state: &mut WrState,
                                               bounds: LayoutRect,
                                               animation_id: u64,
                                               opacity: *const f32,
                                               transform: *const LayoutTransform,
                                               transform_style: TransformStyle,
                                               perspective: *const LayoutTransform,
                                               mix_blend_mode: MixBlendMode,
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -906,20 +906,16 @@ void wr_api_update_resources(DocumentHan
                              ResourceUpdates *aResources)
 WR_FUNC;
 
 WR_INLINE
 void wr_dec_ref_arc(const VecU8 *aArc)
 WR_FUNC;
 
 WR_INLINE
-void wr_dp_clear_save(WrState *aState)
-WR_FUNC;
-
-WR_INLINE
 uint64_t wr_dp_define_clip(WrState *aState,
                            LayoutRect aClipRect,
                            const ComplexClipRegion *aComplex,
                            size_t aComplexCount,
                            const WrImageMask *aMask)
 WR_FUNC;
 
 WR_INLINE
@@ -1174,24 +1170,16 @@ void wr_dp_push_yuv_planar_image(WrState
                                  bool aIsBackfaceVisible,
                                  WrImageKey aImageKey0,
                                  WrImageKey aImageKey1,
                                  WrImageKey aImageKey2,
                                  WrYuvColorSpace aColorSpace,
                                  ImageRendering aImageRendering)
 WR_FUNC;
 
-WR_INLINE
-void wr_dp_restore(WrState *aState)
-WR_FUNC;
-
-WR_INLINE
-void wr_dp_save(WrState *aState)
-WR_FUNC;
-
 extern bool wr_moz2d_render_cb(ByteSlice aBlob,
                                uint32_t aWidth,
                                uint32_t aHeight,
                                ImageFormat aFormat,
                                const uint16_t *aTileSize,
                                const TileOffset *aTileOffset,
                                MutByteSlice aOutput);
 
--- a/layout/generic/TextDrawTarget.h
+++ b/layout/generic/TextDrawTarget.h
@@ -12,16 +12,69 @@
 #include "mozilla/webrender/WebRenderAPI.h"
 #include "mozilla/layers/StackingContextHelper.h"
 
 namespace mozilla {
 namespace layout {
 
 using namespace gfx;
 
+// This is used by all Advanced Layers users, so we use plain gfx types
+struct TextRunFragment {
+  ScaledFont* font;
+  wr::ColorF color;
+  nsTArray<wr::GlyphInstance> glyphs;
+};
+
+// Only webrender handles this, so we use webrender types
+struct SelectionFragment {
+  wr::ColorF color;
+  wr::LayoutRect rect;
+};
+
+// Selections are used in nsTextFrame to hack in sub-frame style changes.
+// Most notably text-shadows can be changed by selections, and so we need to
+// group all the glyphs and decorations attached to a shadow. We do this by
+// having shadows apply to an entire SelectedTextRunFragment, and creating
+// one for each "piece" of selection.
+//
+// For instance, this text:
+//
+// Hello [there] my name [is Mega]man
+//          ^                ^
+//  normal selection      Ctrl+F highlight selection (yeah it's very overloaded)
+//
+// Would be broken up into 5 SelectedTextRunFragments
+//
+// ["Hello ", "there", " my name ", "is Mega", "man"]
+//
+// For almost all nsTextFrames, there will be only one SelectedTextRunFragment.
+struct SelectedTextRunFragment {
+  Maybe<SelectionFragment> selection;
+  AutoTArray<wr::Shadow, 1> shadows;
+  AutoTArray<TextRunFragment, 1> text;
+  AutoTArray<wr::Line, 1> beforeDecorations;
+  AutoTArray<wr::Line, 1> afterDecorations;
+};
+
+}
+}
+
+// AutoTArray is bad
+template<>
+struct nsTArray_CopyChooser<mozilla::layout::SelectedTextRunFragment>
+{
+  typedef nsTArray_CopyWithConstructors<mozilla::layout::SelectedTextRunFragment> Type;
+};
+
+namespace mozilla {
+namespace layout {
+
+using namespace gfx;
+
 // This class is fake DrawTarget, used to intercept text draw calls, while
 // also collecting up the other aspects of text natively.
 //
 // When using advanced-layers in nsDisplayText's constructor, we construct this
 // and run the full painting algorithm with this as the DrawTarget. This is
 // done to avoid having to massively refactor gecko's text painting code (which
 // has lots of components shared between other rendering algorithms).
 //
@@ -38,205 +91,293 @@ using namespace gfx;
 // To be clear: this is a big hack. With time we hope to refactor the codebase
 // so that all the elements of text are handled directly by TextDrawTarget,
 // which is to say everything is done like we do selections and shadows now.
 // This design is a good step for doing this work incrementally.
 //
 // This is also likely to be a bit buggy (missing or misinterpreted info)
 // while we further develop the design.
 //
-// TextDrawTarget doesn't yet support all features. See mHasUnsupportedFeatures
-// for details.
+// This does not currently support SVG text effects.
 class TextDrawTarget : public DrawTarget
 {
 public:
-  explicit TextDrawTarget(wr::DisplayListBuilder& aBuilder,
-                          const layers::StackingContextHelper& aSc,
-                          layers::WebRenderLayerManager* aManager,
-                          nsDisplayItem* aItem,
-                          nsRect& aBounds)
-    : mBuilder(aBuilder), mSc(aSc), mManager(aManager)
-  {
+  // The different phases of drawing the text we're in
+  // Each should only happen once, and in the given order.
+  enum class Phase : uint8_t {
+    eSelection, eUnderline, eOverline, eGlyphs, eEmphasisMarks, eLineThrough
+  };
 
-    // Compute clip/bounds
-    auto appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
-    LayoutDeviceRect layoutBoundsRect = LayoutDeviceRect::FromAppUnits(
-        aBounds, appUnitsPerDevPixel);
-    LayoutDeviceRect layoutClipRect = layoutBoundsRect;
-
-    auto clip = aItem->GetClip();
-    if (clip.HasClip()) {
-      layoutClipRect = LayoutDeviceRect::FromAppUnits(
-                  clip.GetClipRect(), appUnitsPerDevPixel);
-    }
-
-    mBoundsRect = aSc.ToRelativeLayoutRect(LayerRect::FromUnknownRect(layoutBoundsRect.ToUnknownRect()));
-    mClipRect = aSc.ToRelativeLayoutRect(LayerRect::FromUnknownRect(layoutClipRect.ToUnknownRect()));
-
-    mBackfaceVisible = !aItem->BackfaceIsHidden();
-
-    mBuilder.Save();
+  explicit TextDrawTarget(const layers::StackingContextHelper& aSc)
+  : mCurrentlyDrawing(Phase::eSelection),
+    mHasUnsupportedFeatures(false),
+    mSc(aSc)
+  {
+    SetSelectionIndex(0);
   }
 
   // Prevent this from being copied
   TextDrawTarget(const TextDrawTarget& src) = delete;
   TextDrawTarget& operator=(const TextDrawTarget&) = delete;
 
-  ~TextDrawTarget()
-  {
-    if (mHasUnsupportedFeatures) {
-      mBuilder.Restore();
-    } else {
-      mBuilder.ClearSave();
+  // Change the phase of text we're drawing.
+  void StartDrawing(Phase aPhase) { mCurrentlyDrawing = aPhase; }
+  void FoundUnsupportedFeature() { mHasUnsupportedFeatures = true; }
+
+  void SetSelectionIndex(size_t i) {
+    // i should only be accessed if i-1 has already been
+    MOZ_ASSERT(i <= mParts.Length());
+
+    if (mParts.Length() == i){
+      mParts.AppendElement();
     }
+
+    mCurrentPart = &mParts[i];
   }
 
-  void FoundUnsupportedFeature() { mHasUnsupportedFeatures = true; }
-  bool HasUnsupportedFeatures() { return mHasUnsupportedFeatures; }
-
   // This overload just stores the glyphs/font/color.
   void
   FillGlyphs(ScaledFont* aFont,
              const GlyphBuffer& aBuffer,
              const Pattern& aPattern,
              const DrawOptions& aOptions,
              const GlyphRenderingOptions* aRenderingOptions) override
   {
-    // FIXME(?): Deal with AA on the DrawOptions, and the GlyphRenderingOptions
-
-    // Make sure we're only given boring color patterns
+    // FIXME: figure out which of these asserts are real
     MOZ_RELEASE_ASSERT(aOptions.mCompositionOp == CompositionOp::OP_OVER);
     MOZ_RELEASE_ASSERT(aOptions.mAlpha == 1.0f);
+
+    // Make sure we're only given color patterns
     MOZ_RELEASE_ASSERT(aPattern.GetType() == PatternType::COLOR);
-    auto* colorPat = static_cast<const ColorPattern*>(&aPattern);
-    auto color = wr::ToColorF(colorPat->mColor);
+    const ColorPattern* colorPat = static_cast<const ColorPattern*>(&aPattern);
 
-    // Make sure the font exists, and can be serialized
+    // Make sure the font exists
     MOZ_RELEASE_ASSERT(aFont);
-    if (!aFont->CanSerialize()) {
-      FoundUnsupportedFeature();
-      return;
+
+    // FIXME(?): Deal with AA on the DrawOptions, and the GlyphRenderingOptions
+
+    if (mCurrentlyDrawing != Phase::eGlyphs &&
+        mCurrentlyDrawing != Phase::eEmphasisMarks) {
+      MOZ_CRASH("TextDrawTarget received glyphs in wrong phase");
     }
 
-    // 170 is the maximum size gfxFont is expected to hand us
-    AutoTArray<wr::GlyphInstance, 170> glyphs;
-    glyphs.SetLength(aBuffer.mNumGlyphs);
+    // We need to push a new TextRunFragment whenever the font/color changes
+    // (usually this implies some font fallback from mixing languages/emoji)
+    TextRunFragment* fragment;
+    if (mCurrentPart->text.IsEmpty() ||
+        mCurrentPart->text.LastElement().font != aFont ||
+        !(mCurrentPart->text.LastElement().color ==  wr::ToColorF(colorPat->mColor))) {
+      fragment = mCurrentPart->text.AppendElement();
+      fragment->font = aFont;
+      fragment->color = wr::ToColorF(colorPat->mColor);
+    } else {
+      fragment = &mCurrentPart->text.LastElement();
+    }
+
+    nsTArray<wr::GlyphInstance>& glyphs = fragment->glyphs;
+
+    size_t oldLength = glyphs.Length();
+    glyphs.SetLength(oldLength + aBuffer.mNumGlyphs);
 
     for (size_t i = 0; i < aBuffer.mNumGlyphs; i++) {
-      wr::GlyphInstance& targetGlyph = glyphs[i];
+      wr::GlyphInstance& targetGlyph = glyphs[oldLength + i];
       const gfx::Glyph& sourceGlyph = aBuffer.mGlyphs[i];
       targetGlyph.index = sourceGlyph.mIndex;
       targetGlyph.point = mSc.ToRelativeLayoutPoint(
               LayerPoint::FromUnknownPoint(sourceGlyph.mPosition));
     }
-
-    mManager->WrBridge()->PushGlyphs(mBuilder, glyphs, aFont,
-                                     color, mSc, mBoundsRect, mClipRect,
-                                     mBackfaceVisible);
-  }
-
-  void
-  AppendShadow(const wr::Shadow& aShadow)
-  {
-    mBuilder.PushShadow(mBoundsRect, mClipRect, mBackfaceVisible, aShadow);
-    mShadowCount++;
   }
 
   void
-  TerminateShadows()
-  {
-    for (size_t i = 0; i < mShadowCount; ++i) {
-      mBuilder.PopShadow();
-    }
-    mShadowCount = 0;
+  AppendShadow(const wr::Shadow& aShadow) {
+    mCurrentPart->shadows.AppendElement(aShadow);
   }
 
   void
-  AppendSelectionRect(const LayoutDeviceRect& aRect, const Color& aColor)
+  SetSelectionRect(const LayoutDeviceRect& aRect, const Color& aColor)
   {
-    auto rect = wr::ToLayoutRect(aRect);
-    auto color = wr::ToColorF(aColor);
-    mBuilder.PushRect(rect, mClipRect, mBackfaceVisible, color);
+    SelectionFragment frag;
+    frag.rect = wr::ToLayoutRect(aRect);
+    frag.color = wr::ToColorF(aColor);
+    mCurrentPart->selection = Some(frag);
   }
 
   void
   AppendDecoration(const Point& aStart,
                    const Point& aEnd,
                    const float aThickness,
                    const bool aVertical,
                    const Color& aColor,
                    const uint8_t aStyle)
   {
-    wr::Line decoration;
+    wr::Line* decoration;
+
+    switch (mCurrentlyDrawing) {
+      case Phase::eUnderline:
+      case Phase::eOverline:
+        decoration = mCurrentPart->beforeDecorations.AppendElement();
+        break;
+      case Phase::eLineThrough:
+        decoration = mCurrentPart->afterDecorations.AppendElement();
+        break;
+      default:
+        MOZ_CRASH("TextDrawTarget received Decoration in wrong phase");
+    }
 
     // This function is basically designed to slide into the decoration drawing
     // code of nsCSSRendering with minimum disruption, to minimize the
     // chances of implementation drift. As such, it mostly looks like a call
     // to a skia-style StrokeLine method: two end-points, with a thickness
     // and style. Notably the end-points are *centered* in the block direction,
     // even though webrender wants a rect-like representation, where the points
     // are on corners.
     //
     // So we mangle the format here in a single centralized place, where neither
     // webrender nor nsCSSRendering has to care about this mismatch.
-    decoration.baseline = (aVertical ? aStart.x : aStart.y) - aThickness / 2;
-    decoration.start = aVertical ? aStart.y : aStart.x;
-    decoration.end = aVertical ? aEnd.y : aEnd.x;
-    decoration.width = aThickness;
-    decoration.color = wr::ToColorF(aColor);
-    decoration.orientation = aVertical
+    decoration->baseline = (aVertical ? aStart.x : aStart.y) - aThickness / 2;
+    decoration->start = aVertical ? aStart.y : aStart.x;
+    decoration->end = aVertical ? aEnd.y : aEnd.x;
+    decoration->width = aThickness;
+    decoration->color = wr::ToColorF(aColor);
+    decoration->orientation = aVertical
       ? wr::LineOrientation::Vertical
       : wr::LineOrientation::Horizontal;
 
     switch (aStyle) {
       case NS_STYLE_TEXT_DECORATION_STYLE_SOLID:
-        decoration.style = wr::LineStyle::Solid;
+        decoration->style = wr::LineStyle::Solid;
         break;
       case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED:
-        decoration.style = wr::LineStyle::Dotted;
+        decoration->style = wr::LineStyle::Dotted;
         break;
       case NS_STYLE_TEXT_DECORATION_STYLE_DASHED:
-        decoration.style = wr::LineStyle::Dashed;
+        decoration->style = wr::LineStyle::Dashed;
         break;
       case NS_STYLE_TEXT_DECORATION_STYLE_WAVY:
-        decoration.style = wr::LineStyle::Wavy;
+        decoration->style = wr::LineStyle::Wavy;
         break;
       // Double lines should be lowered to two solid lines
       case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE:
       default:
         MOZ_CRASH("TextDrawTarget received unsupported line style");
     }
 
-    mBuilder.PushLine(mClipRect, mBackfaceVisible, decoration);
+
+  }
+
+  const nsTArray<SelectedTextRunFragment>& GetParts() { return mParts; }
+
+  bool
+  CanSerializeFonts()
+  {
+    if (mHasUnsupportedFeatures) {
+      return false;
+    }
+
+    for (const SelectedTextRunFragment& part : GetParts()) {
+      for (const TextRunFragment& frag : part.text) {
+        if (!frag.font->CanSerialize()) {
+          return false;
+        }
+      }
+    }
+    return true;
+  }
+
+  bool
+  CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+                          const layers::StackingContextHelper& aSc,
+                          layers::WebRenderLayerManager* aManager,
+                          nsDisplayItem* aItem,
+                          nsRect& aBounds) {
+
+  if (!CanSerializeFonts()) {
+    return false;
   }
 
+  // Drawing order: selections,
+  //                shadows,
+  //                underline, overline, [grouped in one array]
+  //                text, emphasisText,  [grouped in one array]
+  //                lineThrough
+
+  // Compute clip/bounds
+  auto appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
+  LayoutDeviceRect layoutBoundsRect = LayoutDeviceRect::FromAppUnits(
+      aBounds, appUnitsPerDevPixel);
+  LayoutDeviceRect layoutClipRect = layoutBoundsRect;
+  auto clip = aItem->GetClip();
+  if (clip.HasClip()) {
+    layoutClipRect = LayoutDeviceRect::FromAppUnits(
+                clip.GetClipRect(), appUnitsPerDevPixel);
+  }
+
+  LayerRect boundsRect = LayerRect::FromUnknownRect(layoutBoundsRect.ToUnknownRect());
+  LayerRect clipRect = LayerRect::FromUnknownRect(layoutClipRect.ToUnknownRect());
+
+  bool backfaceVisible = !aItem->BackfaceIsHidden();
+
+  wr::LayoutRect wrBoundsRect = aSc.ToRelativeLayoutRect(boundsRect);
+  wr::LayoutRect wrClipRect = aSc.ToRelativeLayoutRect(clipRect);
+
+
+  // Create commands
+  for (auto& part : GetParts()) {
+    if (part.selection) {
+      auto selection = part.selection.value();
+      aBuilder.PushRect(selection.rect, wrClipRect, backfaceVisible, selection.color);
+    }
+  }
+
+  for (auto& part : GetParts()) {
+    // WR takes the shadows in CSS-order (reverse of rendering order),
+    // because the drawing of a shadow actually occurs when it's popped.
+    for (const wr::Shadow& shadow : part.shadows) {
+      aBuilder.PushShadow(wrBoundsRect, wrClipRect, backfaceVisible, shadow);
+    }
+
+    for (const wr::Line& decoration : part.beforeDecorations) {
+      aBuilder.PushLine(wrClipRect, backfaceVisible, decoration);
+    }
+
+    for (const mozilla::layout::TextRunFragment& text : part.text) {
+      aManager->WrBridge()->PushGlyphs(aBuilder, text.glyphs, text.font,
+                                       text.color, aSc, wrBoundsRect, wrClipRect,
+                                       backfaceVisible);
+    }
+
+    for (const wr::Line& decoration : part.afterDecorations) {
+      aBuilder.PushLine(wrClipRect, backfaceVisible, decoration);
+    }
+
+    for (size_t i = 0; i < part.shadows.Length(); ++i) {
+      aBuilder.PopShadow();
+    }
+  }
+
+  return true;
+}
+
 private:
-  // Whether anything unsupported was encountered. Currently:
-  //
-  // * Synthetic bold/italics
-  // * SVG fonts
-  // * Unserializable fonts
-  // * Tofu glyphs
-  // * Pratial ligatures
-  // * Text writing-mode
-  // * Text stroke
-  bool mHasUnsupportedFeatures = false;
+  // The part of the text we're currently drawing (glyphs, underlines, etc.)
+  Phase mCurrentlyDrawing;
+
+  // Which chunk of mParts is actively being populated
+  SelectedTextRunFragment* mCurrentPart;
 
-  // Number of shadows currently pushed, so we can pop them later.
-  size_t mShadowCount = 0;
+  // Chunks of the text, grouped by selection
+  AutoTArray<SelectedTextRunFragment, 1> mParts;
 
-  // Things used to push to webrender
-  wr::DisplayListBuilder& mBuilder;
+  // Whether Tofu or SVG fonts were encountered
+  bool mHasUnsupportedFeatures;
+
+  // Needs to be saved so FillGlyphs can use this to offset glyphs to
+  // relative space. Shouldn't be used otherwise (may dangle if we move
+  // to retaining TextDrawTargets)
   const layers::StackingContextHelper& mSc;
-  layers::WebRenderLayerManager* mManager;
-
-  // Computed facts
-  wr::LayerRect mBoundsRect;
-  wr::LayerRect mClipRect;
-  bool mBackfaceVisible;
 
   // The rest of this is dummy implementations of DrawTarget's API
 public:
   DrawTargetType GetType() const override {
     return DrawTargetType::SOFTWARE_RASTER;
   }
 
   BackendType GetBackendType() const override {
--- a/layout/generic/TextOverflow.cpp
+++ b/layout/generic/TextOverflow.cpp
@@ -301,22 +301,23 @@ nsDisplayTextOverflowMarker::CreateWebRe
 
   bool snap;
   nsRect bounds = GetBounds(aDisplayListBuilder, &snap);
   if (bounds.IsEmpty()) {
     return true;
   }
 
   // Run the rendering algorithm to capture the glyphs and shadows
-  RefPtr<TextDrawTarget> textDrawer = new TextDrawTarget(aBuilder, aSc, aManager, this, bounds);
+  RefPtr<TextDrawTarget> textDrawer = new TextDrawTarget(aSc);
   RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(textDrawer);
+  // TextOverflowMarker only draws glyphs
+  textDrawer->StartDrawing(TextDrawTarget::Phase::eGlyphs);
   Paint(aDisplayListBuilder, captureCtx);
-  textDrawer->TerminateShadows();
 
-  return !textDrawer->HasUnsupportedFeatures();
+  return textDrawer->CreateWebRenderCommands(aBuilder, aSc, aManager, this, bounds);
 }
 
 
 TextOverflow::TextOverflow(nsDisplayListBuilder* aBuilder,
                            nsIFrame* aBlockFrame)
   : mContentArea(aBlockFrame->GetWritingMode(),
                  aBlockFrame->GetContentRectRelativeToSelf(),
                  aBlockFrame->GetSize())
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -5130,23 +5130,23 @@ nsDisplayText::CreateWebRenderCommands(m
   if (!gfxPrefs::LayersAllowTextLayers()) {
     return false;
   }
 
   if (mBounds.IsEmpty()) {
     return true;
   }
 
-  RefPtr<TextDrawTarget> textDrawer = new TextDrawTarget(aBuilder, aSc, aManager, this, mBounds);
+  RefPtr<TextDrawTarget> textDrawer = new TextDrawTarget(aSc);
   RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(textDrawer);
 
   // TODO: Paint() checks mDisableSubpixelAA, we should too.
   RenderToContext(captureCtx, aDisplayListBuilder, true);
 
-  return !textDrawer->HasUnsupportedFeatures();
+  return textDrawer->CreateWebRenderCommands(aBuilder, aSc, aManager, this, mBounds);
 }
 
 void
 nsDisplayText::RenderToContext(gfxContext* aCtx, nsDisplayListBuilder* aBuilder, bool aIsRecording)
 {
   nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
 
   // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
@@ -5946,16 +5946,24 @@ nsTextFrame::DrawSelectionDecorations(gf
   params.vertical = aVertical;
   params.sidewaysLeft = mTextRun->IsSidewaysLeft();
   params.descentLimit =
     ComputeDescentLimitForSelectionUnderline(aTextPaintStyle.PresContext(),
                                              aFontMetrics);
 
   float relativeSize;
 
+  auto* textDrawer = aContext->GetTextDrawer();
+
+  // Since this happens after text, all we *should* be allowed to do is strikeThrough.
+  // If this isn't true, we're at least bug-compatible with gecko!
+  if (textDrawer) {
+    textDrawer->StartDrawing(TextDrawTarget::Phase::eLineThrough);
+  }
+
   switch (aSelectionType) {
     case SelectionType::eIMERawClause:
     case SelectionType::eIMESelectedRawClause:
     case SelectionType::eIMEConvertedClause:
     case SelectionType::eIMESelectedClause:
     case SelectionType::eSpellCheck: {
       int32_t index = nsTextPaintStyle::
         GetUnderlineStyleIndexForSelectionType(aSelectionType);
@@ -6264,34 +6272,16 @@ nsTextFrame::PaintOneShadow(const PaintS
                             nsCSSShadowItem* aShadowDetails,
                             gfxRect& aBoundingBox, uint32_t aBlurFlags)
 {
   AUTO_PROFILER_LABEL("nsTextFrame::PaintOneShadow", GRAPHICS);
 
   gfxPoint shadowOffset(aShadowDetails->mXOffset, aShadowDetails->mYOffset);
   nscoord blurRadius = std::max(aShadowDetails->mRadius, 0);
 
-  nscolor shadowColor = aShadowDetails->mHasColor ? aShadowDetails->mColor
-                                                  : aParams.foregroundColor;
-
-  if (auto* textDrawer = aParams.context->GetTextDrawer()) {
-    wr::Shadow wrShadow;
-
-    wrShadow.offset = {
-      PresContext()->AppUnitsToFloatDevPixels(aShadowDetails->mXOffset),
-      PresContext()->AppUnitsToFloatDevPixels(aShadowDetails->mYOffset)
-    };
-
-    wrShadow.blur_radius = PresContext()->AppUnitsToFloatDevPixels(blurRadius);
-    wrShadow.color = wr::ToColorF(ToDeviceColor(shadowColor));
-
-    textDrawer->AppendShadow(wrShadow);
-    return;
-  }
-
   // This rect is the box which is equivalent to where the shadow will be painted.
   // The origin of aBoundingBox is the text baseline left, so we must translate it by
   // that much in order to make the origin the top-left corner of the text bounding box.
   // Note that aLeftSideOffset is line-left, so actually means top offset in
   // vertical writing modes.
   gfxRect shadowGfxRect;
   WritingMode wm = GetWritingMode();
   if (wm.IsVertical()) {
@@ -6317,16 +6307,35 @@ nsTextFrame::PaintOneShadow(const PaintS
   nsContextBoxBlur contextBoxBlur;
   const auto A2D = PresContext()->AppUnitsPerDevPixel();
   gfxContext* shadowContext = contextBoxBlur.Init(
     shadowRect, 0, blurRadius, A2D, aParams.context,
     LayoutDevicePixel::ToAppUnits(aParams.dirtyRect, A2D), nullptr, aBlurFlags);
   if (!shadowContext)
     return;
 
+  nscolor shadowColor = aShadowDetails->mHasColor ? aShadowDetails->mColor
+                                                  : aParams.foregroundColor;
+
+  auto* textDrawer = aParams.context->GetTextDrawer();
+  if (textDrawer) {
+    wr::Shadow wrShadow;
+
+    wrShadow.offset = {
+      PresContext()->AppUnitsToFloatDevPixels(aShadowDetails->mXOffset),
+      PresContext()->AppUnitsToFloatDevPixels(aShadowDetails->mYOffset)
+    };
+
+    wrShadow.blur_radius = PresContext()->AppUnitsToFloatDevPixels(aShadowDetails->mRadius);
+    wrShadow.color = wr::ToColorF(ToDeviceColor(shadowColor));
+
+    textDrawer->AppendShadow(wrShadow);
+    return;
+  }
+
   aParams.context->Save();
   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);
@@ -6420,18 +6429,23 @@ nsTextFrame::PaintTextWithSelectionColor
   auto* textDrawer = aParams.context->GetTextDrawer();
 
   if (anyBackgrounds && !aParams.IsGenerateTextMask()) {
     int32_t appUnitsPerDevPixel =
       aParams.textPaintStyle->PresContext()->AppUnitsPerDevPixel();
     SelectionIterator iterator(prevailingSelections, contentRange,
                                *aParams.provider, mTextRun, startIOffset);
     SelectionType selectionType;
+    size_t selectionIndex = 0;
     while (iterator.GetNextSegment(&iOffset, &range, &hyphenWidth,
                                    &selectionType, &rangeStyle)) {
+      if (textDrawer) {
+        textDrawer->SetSelectionIndex(selectionIndex);
+      }
+
       nscolor foreground, background;
       GetSelectionTextColors(selectionType, *aParams.textPaintStyle,
                              rangeStyle, &foreground, &background);
       // Draw background color
       gfxFloat advance = hyphenWidth +
         mTextRun->GetAdvanceWidth(range, aParams.provider);
       if (NS_GET_A(background) > 0) {
         nsRect bgRect;
@@ -6443,24 +6457,25 @@ nsTextFrame::PaintTextWithSelectionColor
           bgRect = nsRect(aParams.framePt.x + offs, aParams.framePt.y,
                           advance, GetSize().height);
         }
 
         LayoutDeviceRect selectionRect =
           LayoutDeviceRect::FromAppUnits(bgRect, appUnitsPerDevPixel);
 
         if (textDrawer) {
-          textDrawer->AppendSelectionRect(selectionRect, ToDeviceColor(background));
+          textDrawer->SetSelectionRect(selectionRect, ToDeviceColor(background));
         } else {
           PaintSelectionBackground(
             *aParams.context->GetDrawTarget(), background, aParams.dirtyRect,
             selectionRect, aParams.callbacks);
         }
       }
       iterator.UpdateWithAdvance(advance);
+      ++selectionIndex;
     }
   }
 
   if (aParams.IsPaintBGColor()) {
     return true;
   }
 
   gfxFloat advance;
@@ -6477,18 +6492,24 @@ nsTextFrame::PaintTextWithSelectionColor
   shadowParams.provider = aParams.provider;
   shadowParams.clipEdges = &aClipEdges;
 
   // Draw text
   const nsStyleText* textStyle = StyleText();
   SelectionIterator iterator(prevailingSelections, contentRange,
                              *aParams.provider, mTextRun, startIOffset);
   SelectionType selectionType;
+
+  size_t selectionIndex = 0;
   while (iterator.GetNextSegment(&iOffset, &range, &hyphenWidth,
                                  &selectionType, &rangeStyle)) {
+    if (textDrawer) {
+      textDrawer->SetSelectionIndex(selectionIndex);
+    }
+
     nscolor foreground, background;
     if (aParams.IsGenerateTextMask()) {
       foreground = NS_RGBA(0, 0, 0, 255);
     } else {
       GetSelectionTextColors(selectionType, *aParams.textPaintStyle,
                              rangeStyle, &foreground, &background);
     }
 
@@ -6517,16 +6538,17 @@ nsTextFrame::PaintTextWithSelectionColor
     // Draw text segment
     params.textColor = foreground;
     params.textStrokeColor = aParams.textPaintStyle->GetWebkitTextStrokeColor();
     params.textStrokeWidth = aParams.textPaintStyle->GetWebkitTextStrokeWidth();
     params.drawSoftHyphen = hyphenWidth > 0;
     DrawText(range, textBaselinePt, params);
     advance += hyphenWidth;
     iterator.UpdateWithAdvance(advance);
+    ++selectionIndex;
   }
   return true;
 }
 
 void
 nsTextFrame::PaintTextSelectionDecorations(
     const PaintTextSelectionParams& aParams,
     const UniquePtr<SelectionDetails>& aDetails,
@@ -6592,18 +6614,24 @@ nsTextFrame::PaintTextSelectionDecoratio
   if (verticalRun) {
     pt.x = (aParams.textBaselinePt.x - mAscent) / app;
   } else {
     pt.y = (aParams.textBaselinePt.y - mAscent) / app;
   }
   SelectionType nextSelectionType;
   TextRangeStyle selectedStyle;
 
+  size_t selectionIndex = 0;
+  auto* textDrawer = aParams.context->GetTextDrawer();
+
   while (iterator.GetNextSegment(&iOffset, &range, &hyphenWidth,
                                  &nextSelectionType, &selectedStyle)) {
+    if (textDrawer) {
+      textDrawer->SetSelectionIndex(selectionIndex);
+    }
     gfxFloat advance = hyphenWidth +
       mTextRun->GetAdvanceWidth(range, aParams.provider);
     if (nextSelectionType == aSelectionType) {
       if (verticalRun) {
         pt.y = (aParams.framePt.y + iOffset -
                (mTextRun->IsInlineReversed() ? advance : 0)) / app;
       } else {
         pt.x = (aParams.framePt.x + iOffset -
@@ -6613,16 +6641,17 @@ nsTextFrame::PaintTextSelectionDecoratio
       gfxFloat xInFrame = pt.x - (aParams.framePt.x / app);
       DrawSelectionDecorations(
         aParams.context, aParams.dirtyRect, aSelectionType,
         *aParams.textPaintStyle, selectedStyle, pt, xInFrame,
         width, mAscent / app, decorationMetrics, aParams.callbacks,
         verticalRun, kDecoration);
     }
     iterator.UpdateWithAdvance(advance);
+    ++selectionIndex;
   }
 }
 
 bool
 nsTextFrame::PaintTextWithSelection(
     const PaintTextSelectionParams& aParams,
     const nsCharClipDisplayItem::ClipEdges& aClipEdges)
 {
@@ -7136,16 +7165,21 @@ DrawTextRun(const gfxTextRun* aTextRun,
 }
 
 void
 nsTextFrame::DrawTextRun(Range aRange, const gfxPoint& aTextBaselinePt,
                          const DrawTextRunParams& aParams)
 {
   MOZ_ASSERT(aParams.advanceWidth, "Must provide advanceWidth");
 
+  auto* textDrawer = aParams.context->GetTextDrawer();
+  if (textDrawer) {
+    textDrawer->StartDrawing(TextDrawTarget::Phase::eGlyphs);
+  }
+
   ::DrawTextRun(mTextRun, aTextBaselinePt, aRange, aParams);
 
   if (aParams.drawSoftHyphen) {
     // Don't use ctx as the context, because we need a reference context here,
     // ctx may be transformed.
     RefPtr<gfxTextRun> hyphenTextRun =
       GetHyphenTextRun(mTextRun, nullptr, this);
     if (hyphenTextRun) {
@@ -7263,24 +7297,33 @@ nsTextFrame::DrawTextRunAndDecorations(R
       bCoord = (frameBStart - dec.mBaselineOffset) / app;
 
       params.color = dec.mColor;
       params.offset = metrics.*lineOffset;
       params.style = dec.mStyle;
       PaintDecorationLine(params);
     };
 
+    auto* textDrawer = aParams.context->GetTextDrawer();
+
     // Underlines
+    if (textDrawer && aDecorations.mUnderlines.Length() > 0) {
+      textDrawer->StartDrawing(TextDrawTarget::Phase::eUnderline);
+    }
+    //
     params.decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
     for (const LineDecoration& dec : Reversed(aDecorations.mUnderlines)) {
       paintDecorationLine(dec, &Metrics::underlineSize,
                           &Metrics::underlineOffset);
     }
 
     // Overlines
+    if (textDrawer && aDecorations.mOverlines.Length() > 0) {
+      textDrawer->StartDrawing(TextDrawTarget::Phase::eOverline);
+    }
     params.decoration = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
     for (const LineDecoration& dec : Reversed(aDecorations.mOverlines)) {
       paintDecorationLine(dec, &Metrics::underlineSize, &Metrics::maxAscent);
     }
 
     {
       gfxContextMatrixAutoSaveRestore unscaledRestorer;
       if (scaledRestorer.HasMatrix()) {
@@ -7289,21 +7332,27 @@ nsTextFrame::DrawTextRunAndDecorations(R
       }
 
       // CSS 2.1 mandates that text be painted after over/underlines,
       // and *then* line-throughs
       DrawTextRun(aRange, aTextBaselinePt, aParams);
     }
 
     // Emphasis marks
+    if (textDrawer) {
+      textDrawer->StartDrawing(TextDrawTarget::Phase::eEmphasisMarks);
+    }
     DrawEmphasisMarks(aParams.context, wm,
                       aTextBaselinePt, aParams.framePt, aRange,
                       aParams.decorationOverrideColor, aParams.provider);
 
     // Line-throughs
+    if (textDrawer && aDecorations.mStrikes.Length() > 0) {
+      textDrawer->StartDrawing(TextDrawTarget::Phase::eLineThrough);
+    }
     params.decoration = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
     for (const LineDecoration& dec : Reversed(aDecorations.mStrikes)) {
       paintDecorationLine(dec, &Metrics::strikeoutSize,
                           &Metrics::strikeoutOffset);
     }
 }
 
 void
@@ -7319,20 +7368,16 @@ nsTextFrame::DrawText(Range aRange, cons
   const bool drawDecorations =
     !aParams.provider->GetFontGroup()->ShouldSkipDrawing() &&
     (decorations.HasDecorationLines() || StyleText()->HasTextEmphasis());
   if (drawDecorations) {
     DrawTextRunAndDecorations(aRange, aTextBaselinePt, aParams, decorations);
   } else {
     DrawTextRun(aRange, aTextBaselinePt, aParams);
   }
-
-  if (auto* textDrawer = aParams.context->GetTextDrawer()) {
-    textDrawer->TerminateShadows();
-  }
 }
 
 int16_t
 nsTextFrame::GetSelectionStatus(int16_t* aSelectionFlags)
 {
   // get the selection controller
   nsCOMPtr<nsISelectionController> selectionController;
   nsresult rv = GetSelectionController(PresContext(),