1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef TextDrawTarget_h
8 #define TextDrawTarget_h
9 
10 #include "mozilla/gfx/2D.h"
11 #include "mozilla/layers/RenderRootStateManager.h"
12 #include "mozilla/layers/WebRenderLayerManager.h"
13 #include "mozilla/layers/WebRenderBridgeChild.h"
14 #include "mozilla/webrender/WebRenderAPI.h"
15 #include "mozilla/layers/StackingContextHelper.h"
16 #include "mozilla/layers/IpcResourceUpdateQueue.h"
17 
18 namespace mozilla {
19 namespace layout {
20 
21 using namespace gfx;
22 
23 // This class is a fake DrawTarget, used to intercept text draw calls, while
24 // also collecting up the other aspects of text natively.
25 //
26 // When using advanced-layers in nsDisplayText's constructor, we construct this
27 // and run the full painting algorithm with this as the DrawTarget. This is
28 // done to avoid having to massively refactor gecko's text painting code (which
29 // has lots of components shared between other rendering algorithms).
30 //
31 // In some phases of the painting algorithm, we can grab the relevant values
32 // and feed them directly into TextDrawTarget. For instance, selections,
33 // decorations, and shadows are handled in this manner. In those cases we can
34 // also short-circuit the painting algorithm to save work.
35 //
36 // In other phases, the computed values are sufficiently buried in complex
37 // code that it's best for us to just intercept the final draw calls. This
38 // is how we handle computing the glyphs of the main text and text-emphasis
39 // (see our overloaded FillGlyphs implementation).
40 //
41 // To be clear: this is a big hack. With time we hope to refactor the codebase
42 // so that all the elements of text are handled directly by TextDrawTarget,
43 // which is to say everything is done like we do selections and shadows now.
44 // This design is a good step for doing this work incrementally.
45 //
46 // This is also likely to be a bit buggy (missing or misinterpreted info)
47 // while we further develop the design.
48 //
49 // TextDrawTarget doesn't yet support all features. See mHasUnsupportedFeatures
50 // for details.
51 class TextDrawTarget : public DrawTarget {
52  public:
53   explicit TextDrawTarget(wr::DisplayListBuilder& aBuilder,
54                           wr::IpcResourceUpdateQueue& aResources,
55                           const layers::StackingContextHelper& aSc,
56                           layers::RenderRootStateManager* aManager,
57                           nsDisplayItem* aItem, nsRect& aBounds,
58                           bool aCallerDoesSaveRestore = false)
mCallerDoesSaveRestore(aCallerDoesSaveRestore)59       : mCallerDoesSaveRestore(aCallerDoesSaveRestore), mBuilder(aBuilder) {
60     Reinitialize(aResources, aSc, aManager, aItem, aBounds);
61   }
62 
63   // Prevent this from being copied
64   TextDrawTarget(const TextDrawTarget& src) = delete;
65   TextDrawTarget& operator=(const TextDrawTarget&) = delete;
66 
~TextDrawTarget()67   ~TextDrawTarget() { MOZ_ASSERT(mFinished); }
68 
Reinitialize(wr::IpcResourceUpdateQueue & aResources,const layers::StackingContextHelper & aSc,layers::RenderRootStateManager * aManager,nsDisplayItem * aItem,nsRect & aBounds)69   void Reinitialize(wr::IpcResourceUpdateQueue& aResources,
70                     const layers::StackingContextHelper& aSc,
71                     layers::RenderRootStateManager* aManager,
72                     nsDisplayItem* aItem, nsRect& aBounds) {
73     mResources = &aResources;
74     mSc = &aSc;
75     mManager = aManager;
76     mHasUnsupportedFeatures = false;
77     mHasShadows = false;
78 
79     SetPermitSubpixelAA(!aItem->IsSubpixelAADisabled());
80 
81     // Compute clip/bounds
82     auto appUnitsPerDevPixel =
83         aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
84     LayoutDeviceRect layoutBoundsRect =
85         LayoutDeviceRect::FromAppUnits(aBounds, appUnitsPerDevPixel);
86     LayoutDeviceRect layoutClipRect = layoutBoundsRect;
87     mBoundsRect = wr::ToLayoutRect(layoutBoundsRect);
88 
89     // Add 1 pixel of dirty area around clip rect to allow us to paint
90     // antialiased pixels beyond the measured text extents.
91     layoutClipRect.Inflate(1);
92     mSize = IntSize::Ceil(layoutClipRect.Width(), layoutClipRect.Height());
93     mClipStack.ClearAndRetainStorage();
94     mClipStack.AppendElement(layoutClipRect);
95 
96     mBackfaceVisible = !aItem->BackfaceIsHidden();
97 
98     if (!mCallerDoesSaveRestore) {
99       mBuilder.Save();
100     }
101   }
102 
FoundUnsupportedFeature()103   void FoundUnsupportedFeature() { mHasUnsupportedFeatures = true; }
CheckHasUnsupportedFeatures()104   bool CheckHasUnsupportedFeatures() {
105     MOZ_ASSERT(mCallerDoesSaveRestore);
106 #ifdef DEBUG
107     MOZ_ASSERT(!mFinished);
108     mFinished = true;
109 #endif
110     return mHasUnsupportedFeatures;
111   }
112 
Finish()113   bool Finish() {
114     MOZ_ASSERT(!mCallerDoesSaveRestore);
115 #ifdef DEBUG
116     mFinished = true;
117 #endif
118     if (mHasUnsupportedFeatures) {
119       mBuilder.Restore();
120       return false;
121     }
122     mBuilder.ClearSave();
123     return true;
124   }
125 
GetWRGlyphFlags()126   wr::FontInstanceFlags GetWRGlyphFlags() const { return mWRGlyphFlags; }
SetWRGlyphFlags(wr::FontInstanceFlags aFlags)127   void SetWRGlyphFlags(wr::FontInstanceFlags aFlags) { mWRGlyphFlags = aFlags; }
128 
129   class AutoRestoreWRGlyphFlags {
130    public:
~AutoRestoreWRGlyphFlags()131     ~AutoRestoreWRGlyphFlags() {
132       if (mTarget) {
133         mTarget->SetWRGlyphFlags(mFlags);
134       }
135     }
136 
Save(TextDrawTarget * aTarget)137     void Save(TextDrawTarget* aTarget) {
138       // This allows for recursive saves, in case the flags need to be modified
139       // under multiple conditions (i.e. transforms and synthetic italics),
140       // since the flags will be restored to the first saved value in the
141       // destructor on scope exit.
142       if (!mTarget) {
143         // Only record the first save with the original flags that will be
144         // restored.
145         mTarget = aTarget;
146         mFlags = aTarget->GetWRGlyphFlags();
147       } else {
148         // Ensure that this is actually a recursive save to the same target
149         MOZ_ASSERT(
150             mTarget == aTarget,
151             "Recursive save of WR glyph flags to different TextDrawTargets");
152       }
153     }
154 
155    private:
156     TextDrawTarget* mTarget = nullptr;
157     wr::FontInstanceFlags mFlags = {0};
158   };
159 
160   // This overload just stores the glyphs/font/color.
FillGlyphs(ScaledFont * aFont,const GlyphBuffer & aBuffer,const Pattern & aPattern,const DrawOptions & aOptions)161   void FillGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
162                   const Pattern& aPattern,
163                   const DrawOptions& aOptions) override {
164     // Make sure we're only given boring color patterns
165     MOZ_RELEASE_ASSERT(aOptions.mCompositionOp == CompositionOp::OP_OVER);
166     MOZ_RELEASE_ASSERT(aOptions.mAlpha == 1.0f);
167     MOZ_RELEASE_ASSERT(aPattern.GetType() == PatternType::COLOR);
168 
169     // Make sure the font exists, and can be serialized
170     MOZ_RELEASE_ASSERT(aFont);
171     if (!aFont->CanSerialize()) {
172       FoundUnsupportedFeature();
173       return;
174     }
175 
176     auto* colorPat = static_cast<const ColorPattern*>(&aPattern);
177     auto color = wr::ToColorF(colorPat->mColor);
178     MOZ_ASSERT(aBuffer.mNumGlyphs);
179     auto glyphs = Range<const wr::GlyphInstance>(
180         reinterpret_cast<const wr::GlyphInstance*>(aBuffer.mGlyphs),
181         aBuffer.mNumGlyphs);
182     // MSVC won't let us use offsetof on the following directly so we give it a
183     // name with typedef
184     typedef std::remove_reference<decltype(aBuffer.mGlyphs[0])>::type GlyphType;
185     // Compare gfx::Glyph and wr::GlyphInstance to make sure that they are
186     // structurally equivalent to ensure that our cast above was ok
187     static_assert(
188         std::is_same<decltype(aBuffer.mGlyphs[0].mIndex),
189                      decltype(glyphs[0].index)>() &&
190             std::is_same<decltype(aBuffer.mGlyphs[0].mPosition.x),
191                          decltype(glyphs[0].point.x)>() &&
192             std::is_same<decltype(aBuffer.mGlyphs[0].mPosition.y),
193                          decltype(glyphs[0].point.y)>() &&
194             offsetof(GlyphType, mIndex) == offsetof(wr::GlyphInstance, index) &&
195             offsetof(GlyphType, mPosition) ==
196                 offsetof(wr::GlyphInstance, point) &&
197             offsetof(decltype(aBuffer.mGlyphs[0].mPosition), x) ==
198                 offsetof(decltype(glyphs[0].point), x) &&
199             offsetof(decltype(aBuffer.mGlyphs[0].mPosition), y) ==
200                 offsetof(decltype(glyphs[0].point), y) &&
201             std::is_standard_layout<
202                 std::remove_reference<decltype(aBuffer.mGlyphs[0])>>::value &&
203             std::is_standard_layout<
204                 std::remove_reference<decltype(glyphs[0])>>::value &&
205             sizeof(aBuffer.mGlyphs[0]) == sizeof(glyphs[0]) &&
206             sizeof(aBuffer.mGlyphs[0].mPosition) == sizeof(glyphs[0].point),
207         "glyph buf types don't match");
208 
209     wr::GlyphOptions glyphOptions;
210     glyphOptions.render_mode =
211         wr::ToFontRenderMode(aOptions.mAntialiasMode, GetPermitSubpixelAA());
212     glyphOptions.flags = mWRGlyphFlags;
213 
214     mManager->WrBridge()->PushGlyphs(mBuilder, glyphs, aFont, color, *mSc,
215                                      mBoundsRect, ClipRect(), mBackfaceVisible,
216                                      &glyphOptions);
217   }
218 
PushClipRect(const Rect & aRect)219   void PushClipRect(const Rect& aRect) override {
220     LayoutDeviceRect rect = LayoutDeviceRect::FromUnknownRect(aRect);
221     rect = rect.Intersect(mClipStack.LastElement());
222     mClipStack.AppendElement(rect);
223   }
224 
PopClip()225   void PopClip() override { mClipStack.RemoveLastElement(); }
226 
GetSize()227   IntSize GetSize() const override { return mSize; }
228 
AppendShadow(const wr::Shadow & aShadow,bool aInflate)229   void AppendShadow(const wr::Shadow& aShadow, bool aInflate) {
230     mBuilder.PushShadow(mBoundsRect, ClipRect(), mBackfaceVisible, aShadow,
231                         aInflate);
232     mHasShadows = true;
233   }
234 
TerminateShadows()235   void TerminateShadows() {
236     if (mHasShadows) {
237       mBuilder.PopAllShadows();
238       mHasShadows = false;
239     }
240   }
241 
AppendSelectionRect(const LayoutDeviceRect & aRect,const DeviceColor & aColor)242   void AppendSelectionRect(const LayoutDeviceRect& aRect,
243                            const DeviceColor& aColor) {
244     auto rect = wr::ToLayoutRect(aRect);
245     auto color = wr::ToColorF(aColor);
246     mBuilder.PushRect(rect, ClipRect(), mBackfaceVisible, color);
247   }
248 
249   // This function is basically designed to slide into the decoration drawing
250   // code of nsCSSRendering with minimum disruption, to minimize the
251   // chances of implementation drift. As such, it mostly looks like a call
252   // to a skia-style StrokeLine method: two end-points, with a thickness
253   // and style. Notably the end-points are *centered* in the block direction,
254   // even though webrender wants a rect-like representation, where the points
255   // are on corners.
256   //
257   // So we mangle the format here in a single centralized place, where neither
258   // webrender nor nsCSSRendering has to care about this mismatch.
259   //
260   // NOTE: we assume the points are axis-aligned, and aStart should be used
261   // as the top-left corner of the rect.
AppendDecoration(const Point & aStart,const Point & aEnd,const float aThickness,const bool aVertical,const DeviceColor & aColor,const uint8_t aStyle)262   void AppendDecoration(const Point& aStart, const Point& aEnd,
263                         const float aThickness, const bool aVertical,
264                         const DeviceColor& aColor, const uint8_t aStyle) {
265     auto pos = LayoutDevicePoint::FromUnknownPoint(aStart);
266     LayoutDeviceSize size;
267 
268     if (aVertical) {
269       pos.x -= aThickness / 2;  // adjust from center to corner
270       size = LayoutDeviceSize(aThickness, aEnd.y - aStart.y);
271     } else {
272       pos.y -= aThickness / 2;  // adjust from center to corner
273       size = LayoutDeviceSize(aEnd.x - aStart.x, aThickness);
274     }
275 
276     wr::Line decoration;
277     decoration.bounds = wr::ToLayoutRect(LayoutDeviceRect(pos, size));
278     decoration.wavyLineThickness = 0;  // dummy value, unused
279     decoration.color = wr::ToColorF(aColor);
280     decoration.orientation = aVertical ? wr::LineOrientation::Vertical
281                                        : wr::LineOrientation::Horizontal;
282 
283     switch (aStyle) {
284       case NS_STYLE_TEXT_DECORATION_STYLE_SOLID:
285         decoration.style = wr::LineStyle::Solid;
286         break;
287       case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED:
288         decoration.style = wr::LineStyle::Dotted;
289         break;
290       case NS_STYLE_TEXT_DECORATION_STYLE_DASHED:
291         decoration.style = wr::LineStyle::Dashed;
292         break;
293       // Wavy lines should go through AppendWavyDecoration
294       case NS_STYLE_TEXT_DECORATION_STYLE_WAVY:
295       // Double lines should be lowered to two solid lines
296       case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE:
297       default:
298         MOZ_CRASH("TextDrawTarget received unsupported line style");
299     }
300 
301     mBuilder.PushLine(ClipRect(), mBackfaceVisible, decoration);
302   }
303 
304   // Seperated out from AppendDecoration because Wavy Lines are completely
305   // different, and trying to merge the concept is more of a mess than it's
306   // worth.
AppendWavyDecoration(const Rect & aBounds,const float aThickness,const bool aVertical,const DeviceColor & aColor)307   void AppendWavyDecoration(const Rect& aBounds, const float aThickness,
308                             const bool aVertical, const DeviceColor& aColor) {
309     wr::Line decoration;
310 
311     decoration.bounds =
312         wr::ToLayoutRect(LayoutDeviceRect::FromUnknownRect(aBounds));
313     decoration.wavyLineThickness = aThickness;
314     decoration.color = wr::ToColorF(aColor);
315     decoration.orientation = aVertical ? wr::LineOrientation::Vertical
316                                        : wr::LineOrientation::Horizontal;
317     decoration.style = wr::LineStyle::Wavy;
318 
319     mBuilder.PushLine(ClipRect(), mBackfaceVisible, decoration);
320   }
321 
WrBridge()322   layers::WebRenderBridgeChild* WrBridge() { return mManager->WrBridge(); }
WrLayerManager()323   layers::WebRenderLayerManager* WrLayerManager() {
324     return mManager->LayerManager();
325   }
326 
DefineImage(const IntSize & aSize,uint32_t aStride,SurfaceFormat aFormat,const uint8_t * aData)327   Maybe<wr::ImageKey> DefineImage(const IntSize& aSize, uint32_t aStride,
328                                   SurfaceFormat aFormat, const uint8_t* aData) {
329     wr::ImageKey key = mManager->WrBridge()->GetNextImageKey();
330     wr::ImageDescriptor desc(aSize, aStride, aFormat);
331     Range<uint8_t> bytes(const_cast<uint8_t*>(aData), aStride * aSize.height);
332     if (mResources->AddImage(key, desc, bytes)) {
333       return Some(key);
334     }
335     return Nothing();
336   }
337 
PushImage(wr::ImageKey aKey,const Rect & aBounds,const Rect & aClip,wr::ImageRendering aFilter,const wr::ColorF & aColor)338   void PushImage(wr::ImageKey aKey, const Rect& aBounds, const Rect& aClip,
339                  wr::ImageRendering aFilter, const wr::ColorF& aColor) {
340     if (!aClip.Intersects(GeckoClipRect().ToUnknownRect())) {
341       return;
342     }
343     mBuilder.PushImage(wr::ToLayoutRect(aBounds), wr::ToLayoutRect(aClip), true,
344                        aFilter, aKey, true, aColor);
345   }
346 
347  private:
ClipRect()348   wr::LayoutRect ClipRect() {
349     return wr::ToLayoutRect(mClipStack.LastElement());
350   }
GeckoClipRect()351   LayoutDeviceRect GeckoClipRect() { return mClipStack.LastElement(); }
352   // Whether anything unsupported was encountered. This will result in this
353   // text being emitted as a blob, which means subpixel-AA can't be used and
354   // that performance will probably be a bit worse. At this point, we've
355   // properly implemented everything that shows up a lot, so you can assume
356   // that the remaining things we don't implement are fairly rare. The complete
357   // set of things that we don't implement are as follows:
358   //
359   // * Unserializable Fonts: WR lives across an IPC boundary
360   // * Text-Combine-Upright Squishing: no one's really bothered to impl it yet
361   // * Text-Stroke: not a real standard (exists for webcompat)
362   // * SVG Glyphs: not a real standard (we got overzealous with svg)
363   // * Color Glyphs (Emoji) With Transparency: requires us to apply transparency
364   //   with a composited layer (a single emoji can be many single-color glyphs)
365   //
366   // The transparent colored-glyphs issue is probably the most valuable to fix,
367   // since ideally it would also result in us fixing transparency for all
368   // intersecting glyphs (which currently look bad with or without webrender,
369   // so there's no fallback like with emoji). Specifically, transparency
370   // looks bad for "cursive" fonts where glyphs overlap at the seams. Since
371   // this is more common for non-latin scripts (e.g. मनीष), this amounts to us
372   // treating non-latin scripts poorly... unless they're emoji. Yikes!
373   bool mHasUnsupportedFeatures = false;
374 
375   // The caller promises to call Save/Restore on the builder as needed.
376   bool mCallerDoesSaveRestore = false;
377 #ifdef DEBUG
378   bool mFinished = false;
379 #endif
380 
381   // Whether PopAllShadows needs to be called
382   bool mHasShadows = false;
383 
384   // Things used to push to webrender
385   wr::DisplayListBuilder& mBuilder;
386   wr::IpcResourceUpdateQueue* mResources;
387   const layers::StackingContextHelper* mSc;
388   layers::RenderRootStateManager* mManager;
389 
390   // Computed facts
391   IntSize mSize;
392   wr::LayoutRect mBoundsRect;
393   AutoTArray<LayoutDeviceRect, 3> mClipStack;
394   bool mBackfaceVisible;
395 
396   wr::FontInstanceFlags mWRGlyphFlags = {0};
397 
398   // The rest of this is dummy implementations of DrawTarget's API
399  public:
GetType()400   DrawTargetType GetType() const override {
401     return DrawTargetType::SOFTWARE_RASTER;
402   }
403 
GetBackendType()404   BackendType GetBackendType() const override {
405     return BackendType::WEBRENDER_TEXT;
406   }
407 
IsRecording()408   bool IsRecording() const override { return true; }
IsCaptureDT()409   bool IsCaptureDT() const override { return false; }
410 
Snapshot()411   already_AddRefed<SourceSurface> Snapshot() override {
412     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
413     return nullptr;
414   }
415 
IntoLuminanceSource(LuminanceType aLuminanceType,float aOpacity)416   already_AddRefed<SourceSurface> IntoLuminanceSource(
417       LuminanceType aLuminanceType, float aOpacity) override {
418     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
419     return nullptr;
420   }
421 
Flush()422   void Flush() override {
423     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
424   }
425 
DrawCapturedDT(DrawTargetCapture * aCaptureDT,const Matrix & aTransform)426   void DrawCapturedDT(DrawTargetCapture* aCaptureDT,
427                       const Matrix& aTransform) override {
428     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
429   }
430 
DrawSurface(SourceSurface * aSurface,const Rect & aDest,const Rect & aSource,const DrawSurfaceOptions & aSurfOptions,const DrawOptions & aOptions)431   void DrawSurface(SourceSurface* aSurface, const Rect& aDest,
432                    const Rect& aSource, const DrawSurfaceOptions& aSurfOptions,
433                    const DrawOptions& aOptions) override {
434     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
435   }
436 
DrawFilter(FilterNode * aNode,const Rect & aSourceRect,const Point & aDestPoint,const DrawOptions & aOptions)437   void DrawFilter(FilterNode* aNode, const Rect& aSourceRect,
438                   const Point& aDestPoint,
439                   const DrawOptions& aOptions) override {
440     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
441   }
442 
DrawSurfaceWithShadow(SourceSurface * aSurface,const Point & aDest,const DeviceColor & aColor,const Point & aOffset,Float aSigma,CompositionOp aOperator)443   void DrawSurfaceWithShadow(SourceSurface* aSurface, const Point& aDest,
444                              const DeviceColor& aColor, const Point& aOffset,
445                              Float aSigma, CompositionOp aOperator) override {
446     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
447   }
448 
ClearRect(const Rect & aRect)449   void ClearRect(const Rect& aRect) override {
450     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
451   }
452 
CopySurface(SourceSurface * aSurface,const IntRect & aSourceRect,const IntPoint & aDestination)453   void CopySurface(SourceSurface* aSurface, const IntRect& aSourceRect,
454                    const IntPoint& aDestination) override {
455     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
456   }
457 
458   void FillRect(const Rect& aRect, const Pattern& aPattern,
459                 const DrawOptions& aOptions = DrawOptions()) override {
460     MOZ_RELEASE_ASSERT(aPattern.GetType() == PatternType::COLOR);
461 
462     if (!aRect.Intersects(GeckoClipRect().ToUnknownRect())) {
463       return;
464     }
465     auto rect = wr::ToLayoutRect(LayoutDeviceRect::FromUnknownRect(aRect));
466     auto color =
467         wr::ToColorF(static_cast<const ColorPattern&>(aPattern).mColor);
468     mBuilder.PushRect(rect, ClipRect(), mBackfaceVisible, color);
469   }
470 
StrokeRect(const Rect & aRect,const Pattern & aPattern,const StrokeOptions & aStrokeOptions,const DrawOptions & aOptions)471   void StrokeRect(const Rect& aRect, const Pattern& aPattern,
472                   const StrokeOptions& aStrokeOptions,
473                   const DrawOptions& aOptions) override {
474     MOZ_RELEASE_ASSERT(aPattern.GetType() == PatternType::COLOR &&
475                        aStrokeOptions.mDashLength == 0);
476 
477     wr::LayoutSideOffsets widths = {
478         aStrokeOptions.mLineWidth, aStrokeOptions.mLineWidth,
479         aStrokeOptions.mLineWidth, aStrokeOptions.mLineWidth};
480     wr::ColorF color =
481         wr::ToColorF(static_cast<const ColorPattern&>(aPattern).mColor);
482     wr::BorderSide sides[4] = {{color, wr::BorderStyle::Solid},
483                                {color, wr::BorderStyle::Solid},
484                                {color, wr::BorderStyle::Solid},
485                                {color, wr::BorderStyle::Solid}};
486     wr::BorderRadius radius = {{0, 0}, {0, 0}, {0, 0}, {0, 0}};
487     LayoutDeviceRect rect = LayoutDeviceRect::FromUnknownRect(aRect);
488     rect.Inflate(aStrokeOptions.mLineWidth / 2);
489     if (!rect.Intersects(GeckoClipRect())) {
490       return;
491     }
492     wr::LayoutRect bounds = wr::ToLayoutRect(rect);
493     mBuilder.PushBorder(bounds, ClipRect(), true, widths,
494                         Range<const wr::BorderSide>(sides, 4), radius);
495   }
496 
StrokeLine(const Point & aStart,const Point & aEnd,const Pattern & aPattern,const StrokeOptions & aStrokeOptions,const DrawOptions & aOptions)497   void StrokeLine(const Point& aStart, const Point& aEnd,
498                   const Pattern& aPattern, const StrokeOptions& aStrokeOptions,
499                   const DrawOptions& aOptions) override {
500     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
501   }
502 
Stroke(const Path * aPath,const Pattern & aPattern,const StrokeOptions & aStrokeOptions,const DrawOptions & aOptions)503   void Stroke(const Path* aPath, const Pattern& aPattern,
504               const StrokeOptions& aStrokeOptions,
505               const DrawOptions& aOptions) override {
506     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
507   }
508 
Fill(const Path * aPath,const Pattern & aPattern,const DrawOptions & aOptions)509   void Fill(const Path* aPath, const Pattern& aPattern,
510             const DrawOptions& aOptions) override {
511     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
512   }
513 
StrokeGlyphs(ScaledFont * aFont,const GlyphBuffer & aBuffer,const Pattern & aPattern,const StrokeOptions & aStrokeOptions,const DrawOptions & aOptions)514   void StrokeGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
515                     const Pattern& aPattern,
516                     const StrokeOptions& aStrokeOptions,
517                     const DrawOptions& aOptions) override {
518     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
519   }
520 
Mask(const Pattern & aSource,const Pattern & aMask,const DrawOptions & aOptions)521   void Mask(const Pattern& aSource, const Pattern& aMask,
522             const DrawOptions& aOptions) override {
523     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
524   }
525 
MaskSurface(const Pattern & aSource,SourceSurface * aMask,Point aOffset,const DrawOptions & aOptions)526   void MaskSurface(const Pattern& aSource, SourceSurface* aMask, Point aOffset,
527                    const DrawOptions& aOptions) override {
528     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
529   }
530 
Draw3DTransformedSurface(SourceSurface * aSurface,const Matrix4x4 & aMatrix)531   bool Draw3DTransformedSurface(SourceSurface* aSurface,
532                                 const Matrix4x4& aMatrix) override {
533     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
534   }
535 
PushClip(const Path * aPath)536   void PushClip(const Path* aPath) override {
537     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
538   }
539 
PushDeviceSpaceClipRects(const IntRect * aRects,uint32_t aCount)540   void PushDeviceSpaceClipRects(const IntRect* aRects,
541                                 uint32_t aCount) override {
542     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
543   }
544 
PushLayer(bool aOpaque,Float aOpacity,SourceSurface * aMask,const Matrix & aMaskTransform,const IntRect & aBounds,bool aCopyBackground)545   void PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
546                  const Matrix& aMaskTransform, const IntRect& aBounds,
547                  bool aCopyBackground) override {
548     // Fine to pretend we do this
549   }
550 
PopLayer()551   void PopLayer() override {
552     // Fine to pretend we do this
553   }
554 
CreateSourceSurfaceFromData(unsigned char * aData,const IntSize & aSize,int32_t aStride,SurfaceFormat aFormat)555   already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(
556       unsigned char* aData, const IntSize& aSize, int32_t aStride,
557       SurfaceFormat aFormat) const override {
558     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
559     return nullptr;
560   }
561 
OptimizeSourceSurface(SourceSurface * aSurface)562   already_AddRefed<SourceSurface> OptimizeSourceSurface(
563       SourceSurface* aSurface) const override {
564     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
565     return nullptr;
566   }
567 
CreateSourceSurfaceFromNativeSurface(const NativeSurface & aSurface)568   already_AddRefed<SourceSurface> CreateSourceSurfaceFromNativeSurface(
569       const NativeSurface& aSurface) const override {
570     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
571     return nullptr;
572   }
573 
CreateSimilarDrawTarget(const IntSize & aSize,SurfaceFormat aFormat)574   already_AddRefed<DrawTarget> CreateSimilarDrawTarget(
575       const IntSize& aSize, SurfaceFormat aFormat) const override {
576     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
577     return nullptr;
578   }
579 
CanCreateSimilarDrawTarget(const IntSize & aSize,SurfaceFormat aFormat)580   bool CanCreateSimilarDrawTarget(const IntSize& aSize,
581                                   SurfaceFormat aFormat) const override {
582     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
583     return false;
584   }
585 
CreateClippedDrawTarget(const Rect & aBounds,SurfaceFormat aFormat)586   virtual RefPtr<DrawTarget> CreateClippedDrawTarget(
587       const Rect& aBounds, SurfaceFormat aFormat) override {
588     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
589     return nullptr;
590   }
591 
CreatePathBuilder(FillRule aFillRule)592   already_AddRefed<PathBuilder> CreatePathBuilder(
593       FillRule aFillRule) const override {
594     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
595     return nullptr;
596   }
597 
CreateFilter(FilterType aType)598   already_AddRefed<FilterNode> CreateFilter(FilterType aType) override {
599     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
600     return nullptr;
601   }
602 
CreateGradientStops(GradientStop * aStops,uint32_t aNumStops,ExtendMode aExtendMode)603   already_AddRefed<GradientStops> CreateGradientStops(
604       GradientStop* aStops, uint32_t aNumStops,
605       ExtendMode aExtendMode) const override {
606     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
607     return nullptr;
608   }
609 
DetachAllSnapshots()610   void DetachAllSnapshots() override {
611     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
612   }
613 };
614 
615 }  // namespace layout
616 }  // namespace mozilla
617 
618 #endif
619