f7c712c043f7bd1786b9d7bf855273403726d578
[WebKit-https.git] / Source / WebCore / html / canvas / CanvasRenderingContext2D.cpp
1 /*
2  * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
3  * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies)
4  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
5  * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
6  * Copyright (C) 2008 Dirk Schulze <krit@webkit.org>
7  * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
8  * Copyright (C) 2012 Intel Corporation. All rights reserved.
9  * Copyright (C) 2013, 2014 Adobe Systems Incorporated. All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
21  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
28  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "config.h"
34 #include "CanvasRenderingContext2D.h"
35
36 #include "BitmapImage.h"
37 #include "CSSFontSelector.h"
38 #include "CSSParser.h"
39 #include "CSSPropertyNames.h"
40 #include "CachedImage.h"
41 #include "CanvasGradient.h"
42 #include "CanvasPattern.h"
43 #include "DOMPath.h"
44 #include "DisplayListRecorder.h"
45 #include "DisplayListReplayer.h"
46 #include "FloatQuad.h"
47 #include "HTMLImageElement.h"
48 #include "HTMLVideoElement.h"
49 #include "ImageBuffer.h"
50 #include "ImageData.h"
51 #include "RenderElement.h"
52 #include "RenderImage.h"
53 #include "RenderLayer.h"
54 #include "RenderTheme.h"
55 #include "SecurityOrigin.h"
56 #include "StrokeStyleApplier.h"
57 #include "StyleProperties.h"
58 #include "StyleResolver.h"
59 #include "TextMetrics.h"
60 #include "TextRun.h"
61 #include "TextStream.h"
62 #include <wtf/CheckedArithmetic.h>
63 #include <wtf/MathExtras.h>
64 #include <wtf/NeverDestroyed.h>
65 #include <wtf/text/StringBuilder.h>
66
67 #if USE(CG) && !PLATFORM(IOS)
68 #include <ApplicationServices/ApplicationServices.h>
69 #endif
70
71 namespace WebCore {
72
73 using namespace HTMLNames;
74
75 #if USE(CG)
76 const CanvasRenderingContext2D::ImageSmoothingQuality defaultSmoothingQuality = CanvasRenderingContext2D::ImageSmoothingQuality::Low;
77 #else
78 const CanvasRenderingContext2D::ImageSmoothingQuality defaultSmoothingQuality = CanvasRenderingContext2D::ImageSmoothingQuality::Medium;
79 #endif
80
81 static const int defaultFontSize = 10;
82 static const char* const defaultFontFamily = "sans-serif";
83 static const char* const defaultFont = "10px sans-serif";
84
85 struct DisplayListDrawingContext {
86     WTF_MAKE_FAST_ALLOCATED;
87 public:
88     GraphicsContext context;
89     DisplayList::Recorder recorder;
90     DisplayList::DisplayList displayList;
91     
92     DisplayListDrawingContext(const FloatRect& clip)
93         : recorder(context, displayList, clip, AffineTransform())
94     {
95     }
96 };
97
98 typedef HashMap<const CanvasRenderingContext2D*, std::unique_ptr<DisplayList::DisplayList>> ContextDisplayListHashMap;
99
100 static ContextDisplayListHashMap& contextDisplayListMap()
101 {
102     static NeverDestroyed<ContextDisplayListHashMap> sharedHashMap;
103     return sharedHashMap;
104 }
105
106 class CanvasStrokeStyleApplier : public StrokeStyleApplier {
107 public:
108     CanvasStrokeStyleApplier(CanvasRenderingContext2D* canvasContext)
109         : m_canvasContext(canvasContext)
110     {
111     }
112
113     void strokeStyle(GraphicsContext* c) override
114     {
115         c->setStrokeThickness(m_canvasContext->lineWidth());
116         c->setLineCap(m_canvasContext->getLineCap());
117         c->setLineJoin(m_canvasContext->getLineJoin());
118         c->setMiterLimit(m_canvasContext->miterLimit());
119         const Vector<float>& lineDash = m_canvasContext->getLineDash();
120         DashArray convertedLineDash(lineDash.size());
121         for (size_t i = 0; i < lineDash.size(); ++i)
122             convertedLineDash[i] = static_cast<DashArrayElement>(lineDash[i]);
123         c->setLineDash(convertedLineDash, m_canvasContext->lineDashOffset());
124     }
125
126 private:
127     CanvasRenderingContext2D* m_canvasContext;
128 };
129
130 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement& canvas, bool usesCSSCompatibilityParseMode, bool usesDashboardCompatibilityMode)
131     : CanvasRenderingContext(canvas)
132     , m_stateStack(1)
133     , m_usesCSSCompatibilityParseMode(usesCSSCompatibilityParseMode)
134 #if ENABLE(DASHBOARD_SUPPORT)
135     , m_usesDashboardCompatibilityMode(usesDashboardCompatibilityMode)
136 #endif
137 {
138 #if !ENABLE(DASHBOARD_SUPPORT)
139     ASSERT_UNUSED(usesDashboardCompatibilityMode, !usesDashboardCompatibilityMode);
140 #endif
141 }
142
143 void CanvasRenderingContext2D::unwindStateStack()
144 {
145     // Ensure that the state stack in the ImageBuffer's context
146     // is cleared before destruction, to avoid assertions in the
147     // GraphicsContext dtor.
148     if (size_t stackSize = m_stateStack.size()) {
149         if (GraphicsContext* context = canvas().existingDrawingContext()) {
150             while (--stackSize)
151                 context->restore();
152         }
153     }
154 }
155
156 CanvasRenderingContext2D::~CanvasRenderingContext2D()
157 {
158 #if !ASSERT_DISABLED
159     unwindStateStack();
160 #endif
161
162     if (UNLIKELY(tracksDisplayListReplay()))
163         contextDisplayListMap().remove(this);
164 }
165
166 bool CanvasRenderingContext2D::isAccelerated() const
167 {
168 #if USE(IOSURFACE_CANVAS_BACKING_STORE) || ENABLE(ACCELERATED_2D_CANVAS)
169     if (!canvas().hasCreatedImageBuffer())
170         return false;
171     auto* context = drawingContext();
172     return context && context->isAcceleratedContext();
173 #else
174     return false;
175 #endif
176 }
177
178 void CanvasRenderingContext2D::reset()
179 {
180     unwindStateStack();
181     m_stateStack.resize(1);
182     m_stateStack.first() = State();
183     m_path.clear();
184     m_unrealizedSaveCount = 0;
185     
186     m_recordingContext = nullptr;
187 }
188
189 CanvasRenderingContext2D::State::State()
190     : strokeStyle(Color::black)
191     , fillStyle(Color::black)
192     , lineWidth(1)
193     , lineCap(ButtCap)
194     , lineJoin(MiterJoin)
195     , miterLimit(10)
196     , shadowBlur(0)
197     , shadowColor(Color::transparent)
198     , globalAlpha(1)
199     , globalComposite(CompositeSourceOver)
200     , globalBlend(BlendModeNormal)
201     , hasInvertibleTransform(true)
202     , lineDashOffset(0)
203     , imageSmoothingEnabled(true)
204     , imageSmoothingQuality(defaultSmoothingQuality)
205     , textAlign(StartTextAlign)
206     , textBaseline(AlphabeticTextBaseline)
207     , direction(Direction::Inherit)
208     , unparsedFont(defaultFont)
209 {
210 }
211
212 CanvasRenderingContext2D::State::State(const State& other)
213     : unparsedStrokeColor(other.unparsedStrokeColor)
214     , unparsedFillColor(other.unparsedFillColor)
215     , strokeStyle(other.strokeStyle)
216     , fillStyle(other.fillStyle)
217     , lineWidth(other.lineWidth)
218     , lineCap(other.lineCap)
219     , lineJoin(other.lineJoin)
220     , miterLimit(other.miterLimit)
221     , shadowOffset(other.shadowOffset)
222     , shadowBlur(other.shadowBlur)
223     , shadowColor(other.shadowColor)
224     , globalAlpha(other.globalAlpha)
225     , globalComposite(other.globalComposite)
226     , globalBlend(other.globalBlend)
227     , transform(other.transform)
228     , hasInvertibleTransform(other.hasInvertibleTransform)
229     , lineDashOffset(other.lineDashOffset)
230     , imageSmoothingEnabled(other.imageSmoothingEnabled)
231     , imageSmoothingQuality(other.imageSmoothingQuality)
232     , textAlign(other.textAlign)
233     , textBaseline(other.textBaseline)
234     , direction(other.direction)
235     , unparsedFont(other.unparsedFont)
236     , font(other.font)
237 {
238 }
239
240 CanvasRenderingContext2D::State& CanvasRenderingContext2D::State::operator=(const State& other)
241 {
242     if (this == &other)
243         return *this;
244
245     unparsedStrokeColor = other.unparsedStrokeColor;
246     unparsedFillColor = other.unparsedFillColor;
247     strokeStyle = other.strokeStyle;
248     fillStyle = other.fillStyle;
249     lineWidth = other.lineWidth;
250     lineCap = other.lineCap;
251     lineJoin = other.lineJoin;
252     miterLimit = other.miterLimit;
253     shadowOffset = other.shadowOffset;
254     shadowBlur = other.shadowBlur;
255     shadowColor = other.shadowColor;
256     globalAlpha = other.globalAlpha;
257     globalComposite = other.globalComposite;
258     globalBlend = other.globalBlend;
259     transform = other.transform;
260     hasInvertibleTransform = other.hasInvertibleTransform;
261     imageSmoothingEnabled = other.imageSmoothingEnabled;
262     imageSmoothingQuality = other.imageSmoothingQuality;
263     textAlign = other.textAlign;
264     textBaseline = other.textBaseline;
265     direction = other.direction;
266     unparsedFont = other.unparsedFont;
267     font = other.font;
268
269     return *this;
270 }
271
272 CanvasRenderingContext2D::FontProxy::~FontProxy()
273 {
274     if (realized())
275         m_font.fontSelector()->unregisterForInvalidationCallbacks(*this);
276 }
277
278 CanvasRenderingContext2D::FontProxy::FontProxy(const FontProxy& other)
279     : m_font(other.m_font)
280 {
281     if (realized())
282         m_font.fontSelector()->registerForInvalidationCallbacks(*this);
283 }
284
285 auto CanvasRenderingContext2D::FontProxy::operator=(const FontProxy& other) -> FontProxy&
286 {
287     if (realized())
288         m_font.fontSelector()->unregisterForInvalidationCallbacks(*this);
289
290     m_font = other.m_font;
291
292     if (realized())
293         m_font.fontSelector()->registerForInvalidationCallbacks(*this);
294
295     return *this;
296 }
297
298 inline void CanvasRenderingContext2D::FontProxy::update(FontSelector& selector)
299 {
300     ASSERT(&selector == m_font.fontSelector()); // This is an invariant. We should only ever be registered for callbacks on m_font.m_fonts.m_fontSelector.
301     if (realized())
302         m_font.fontSelector()->unregisterForInvalidationCallbacks(*this);
303     m_font.update(&selector);
304     if (realized())
305         m_font.fontSelector()->registerForInvalidationCallbacks(*this);
306     ASSERT(&selector == m_font.fontSelector());
307 }
308
309 void CanvasRenderingContext2D::FontProxy::fontsNeedUpdate(FontSelector& selector)
310 {
311     ASSERT_ARG(selector, &selector == m_font.fontSelector());
312     ASSERT(realized());
313
314     update(selector);
315 }
316
317 inline void CanvasRenderingContext2D::FontProxy::initialize(FontSelector& fontSelector, const RenderStyle& newStyle)
318 {
319     // Beware! m_font.fontSelector() might not point to document.fontSelector()!
320     ASSERT(newStyle.fontCascade().fontSelector() == &fontSelector);
321     if (realized())
322         m_font.fontSelector()->unregisterForInvalidationCallbacks(*this);
323     m_font = newStyle.fontCascade();
324     m_font.update(&fontSelector);
325     ASSERT(&fontSelector == m_font.fontSelector());
326     m_font.fontSelector()->registerForInvalidationCallbacks(*this);
327 }
328
329 inline FontMetrics CanvasRenderingContext2D::FontProxy::fontMetrics() const
330 {
331     return m_font.fontMetrics();
332 }
333
334 inline const FontCascadeDescription& CanvasRenderingContext2D::FontProxy::fontDescription() const
335 {
336     return m_font.fontDescription();
337 }
338
339 inline float CanvasRenderingContext2D::FontProxy::width(const TextRun& textRun) const
340 {
341     return m_font.width(textRun);
342 }
343
344 inline void CanvasRenderingContext2D::FontProxy::drawBidiText(GraphicsContext& context, const TextRun& run, const FloatPoint& point, FontCascade::CustomFontNotReadyAction action) const
345 {
346     context.drawBidiText(m_font, run, point, action);
347 }
348
349 void CanvasRenderingContext2D::realizeSaves()
350 {
351     if (m_unrealizedSaveCount)
352         realizeSavesLoop();
353
354     if (m_unrealizedSaveCount) {
355         static NeverDestroyed<String> consoleMessage(ASCIILiteral("CanvasRenderingContext2D.save() has been called without a matching restore() too many times. Ignoring save()."));
356         canvas().document().addConsoleMessage(MessageSource::Rendering, MessageLevel::Error, consoleMessage);
357     }
358 }
359
360 void CanvasRenderingContext2D::realizeSavesLoop()
361 {
362     ASSERT(m_unrealizedSaveCount);
363     ASSERT(m_stateStack.size() >= 1);
364     GraphicsContext* context = drawingContext();
365     do {
366         if (m_stateStack.size() > MaxSaveCount)
367             break;
368         m_stateStack.append(state());
369         if (context)
370             context->save();
371     } while (--m_unrealizedSaveCount);
372 }
373
374 void CanvasRenderingContext2D::restore()
375 {
376     if (m_unrealizedSaveCount) {
377         --m_unrealizedSaveCount;
378         return;
379     }
380     ASSERT(m_stateStack.size() >= 1);
381     if (m_stateStack.size() <= 1)
382         return;
383     m_path.transform(state().transform);
384     m_stateStack.removeLast();
385     if (std::optional<AffineTransform> inverse = state().transform.inverse())
386         m_path.transform(inverse.value());
387     GraphicsContext* c = drawingContext();
388     if (!c)
389         return;
390     c->restore();
391 }
392
393 void CanvasRenderingContext2D::setStrokeStyle(CanvasStyle style)
394 {
395     if (!style.isValid())
396         return;
397
398     if (state().strokeStyle.isValid() && state().strokeStyle.isEquivalentColor(style))
399         return;
400
401     if (style.isCurrentColor()) {
402         if (style.hasOverrideAlpha()) {
403             // FIXME: Should not use RGBA32 here.
404             style = CanvasStyle(colorWithOverrideAlpha(currentColor(&canvas()).rgb(), style.overrideAlpha()));
405         } else
406             style = CanvasStyle(currentColor(&canvas()));
407     } else
408         checkOrigin(style.canvasPattern());
409
410     realizeSaves();
411     State& state = modifiableState();
412     state.strokeStyle = style;
413     GraphicsContext* c = drawingContext();
414     if (!c)
415         return;
416     state.strokeStyle.applyStrokeColor(*c);
417     state.unparsedStrokeColor = String();
418 }
419
420 void CanvasRenderingContext2D::setFillStyle(CanvasStyle style)
421 {
422     if (!style.isValid())
423         return;
424
425     if (state().fillStyle.isValid() && state().fillStyle.isEquivalentColor(style))
426         return;
427
428     if (style.isCurrentColor()) {
429         if (style.hasOverrideAlpha()) {
430             // FIXME: Should not use RGBA32 here.
431             style = CanvasStyle(colorWithOverrideAlpha(currentColor(&canvas()).rgb(), style.overrideAlpha()));
432         } else
433             style = CanvasStyle(currentColor(&canvas()));
434     } else
435         checkOrigin(style.canvasPattern());
436
437     realizeSaves();
438     State& state = modifiableState();
439     state.fillStyle = style;
440     GraphicsContext* c = drawingContext();
441     if (!c)
442         return;
443     state.fillStyle.applyFillColor(*c);
444     state.unparsedFillColor = String();
445 }
446
447 float CanvasRenderingContext2D::lineWidth() const
448 {
449     return state().lineWidth;
450 }
451
452 void CanvasRenderingContext2D::setLineWidth(float width)
453 {
454     if (!(std::isfinite(width) && width > 0))
455         return;
456     if (state().lineWidth == width)
457         return;
458     realizeSaves();
459     modifiableState().lineWidth = width;
460     GraphicsContext* c = drawingContext();
461     if (!c)
462         return;
463     c->setStrokeThickness(width);
464 }
465
466 String CanvasRenderingContext2D::lineCap() const
467 {
468     return lineCapName(state().lineCap);
469 }
470
471 void CanvasRenderingContext2D::setLineCap(const String& s)
472 {
473     LineCap cap;
474     if (!parseLineCap(s, cap))
475         return;
476     if (state().lineCap == cap)
477         return;
478     realizeSaves();
479     modifiableState().lineCap = cap;
480     GraphicsContext* c = drawingContext();
481     if (!c)
482         return;
483     c->setLineCap(cap);
484 }
485
486 String CanvasRenderingContext2D::lineJoin() const
487 {
488     return lineJoinName(state().lineJoin);
489 }
490
491 void CanvasRenderingContext2D::setLineJoin(const String& s)
492 {
493     LineJoin join;
494     if (!parseLineJoin(s, join))
495         return;
496     if (state().lineJoin == join)
497         return;
498     realizeSaves();
499     modifiableState().lineJoin = join;
500     GraphicsContext* c = drawingContext();
501     if (!c)
502         return;
503     c->setLineJoin(join);
504 }
505
506 float CanvasRenderingContext2D::miterLimit() const
507 {
508     return state().miterLimit;
509 }
510
511 void CanvasRenderingContext2D::setMiterLimit(float limit)
512 {
513     if (!(std::isfinite(limit) && limit > 0))
514         return;
515     if (state().miterLimit == limit)
516         return;
517     realizeSaves();
518     modifiableState().miterLimit = limit;
519     GraphicsContext* c = drawingContext();
520     if (!c)
521         return;
522     c->setMiterLimit(limit);
523 }
524
525 float CanvasRenderingContext2D::shadowOffsetX() const
526 {
527     return state().shadowOffset.width();
528 }
529
530 void CanvasRenderingContext2D::setShadowOffsetX(float x)
531 {
532     if (!std::isfinite(x))
533         return;
534     if (state().shadowOffset.width() == x)
535         return;
536     realizeSaves();
537     modifiableState().shadowOffset.setWidth(x);
538     applyShadow();
539 }
540
541 float CanvasRenderingContext2D::shadowOffsetY() const
542 {
543     return state().shadowOffset.height();
544 }
545
546 void CanvasRenderingContext2D::setShadowOffsetY(float y)
547 {
548     if (!std::isfinite(y))
549         return;
550     if (state().shadowOffset.height() == y)
551         return;
552     realizeSaves();
553     modifiableState().shadowOffset.setHeight(y);
554     applyShadow();
555 }
556
557 float CanvasRenderingContext2D::shadowBlur() const
558 {
559     return state().shadowBlur;
560 }
561
562 void CanvasRenderingContext2D::setShadowBlur(float blur)
563 {
564     if (!(std::isfinite(blur) && blur >= 0))
565         return;
566     if (state().shadowBlur == blur)
567         return;
568     realizeSaves();
569     modifiableState().shadowBlur = blur;
570     applyShadow();
571 }
572
573 String CanvasRenderingContext2D::shadowColor() const
574 {
575     return Color(state().shadowColor).serialized();
576 }
577
578 void CanvasRenderingContext2D::setShadowColor(const String& colorString)
579 {
580     Color color = parseColorOrCurrentColor(colorString, &canvas());
581     if (!color.isValid())
582         return;
583     if (state().shadowColor == color)
584         return;
585     realizeSaves();
586     modifiableState().shadowColor = color;
587     applyShadow();
588 }
589
590 const Vector<float>& CanvasRenderingContext2D::getLineDash() const
591 {
592     return state().lineDash;
593 }
594
595 static bool lineDashSequenceIsValid(const Vector<float>& dash)
596 {
597     for (size_t i = 0; i < dash.size(); i++) {
598         if (!std::isfinite(dash[i]) || dash[i] < 0)
599             return false;
600     }
601     return true;
602 }
603
604 void CanvasRenderingContext2D::setLineDash(const Vector<float>& dash)
605 {
606     if (!lineDashSequenceIsValid(dash))
607         return;
608
609     realizeSaves();
610     modifiableState().lineDash = dash;
611     // Spec requires the concatenation of two copies the dash list when the
612     // number of elements is odd
613     if (dash.size() % 2)
614         modifiableState().lineDash.appendVector(dash);
615
616     applyLineDash();
617 }
618
619 void CanvasRenderingContext2D::setWebkitLineDash(const Vector<float>& dash)
620 {
621     if (!lineDashSequenceIsValid(dash))
622         return;
623
624     realizeSaves();
625     modifiableState().lineDash = dash;
626
627     applyLineDash();
628 }
629
630 float CanvasRenderingContext2D::lineDashOffset() const
631 {
632     return state().lineDashOffset;
633 }
634
635 void CanvasRenderingContext2D::setLineDashOffset(float offset)
636 {
637     if (!std::isfinite(offset) || state().lineDashOffset == offset)
638         return;
639
640     realizeSaves();
641     modifiableState().lineDashOffset = offset;
642     applyLineDash();
643 }
644
645 void CanvasRenderingContext2D::applyLineDash() const
646 {
647     GraphicsContext* c = drawingContext();
648     if (!c)
649         return;
650     DashArray convertedLineDash(state().lineDash.size());
651     for (size_t i = 0; i < state().lineDash.size(); ++i)
652         convertedLineDash[i] = static_cast<DashArrayElement>(state().lineDash[i]);
653     c->setLineDash(convertedLineDash, state().lineDashOffset);
654 }
655
656 float CanvasRenderingContext2D::globalAlpha() const
657 {
658     return state().globalAlpha;
659 }
660
661 void CanvasRenderingContext2D::setGlobalAlpha(float alpha)
662 {
663     if (!(alpha >= 0 && alpha <= 1))
664         return;
665     if (state().globalAlpha == alpha)
666         return;
667     realizeSaves();
668     modifiableState().globalAlpha = alpha;
669     GraphicsContext* c = drawingContext();
670     if (!c)
671         return;
672     c->setAlpha(alpha);
673 }
674
675 String CanvasRenderingContext2D::globalCompositeOperation() const
676 {
677     return compositeOperatorName(state().globalComposite, state().globalBlend);
678 }
679
680 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation)
681 {
682     CompositeOperator op = CompositeSourceOver;
683     BlendMode blendMode = BlendModeNormal;
684     if (!parseCompositeAndBlendOperator(operation, op, blendMode))
685         return;
686     if ((state().globalComposite == op) && (state().globalBlend == blendMode))
687         return;
688     realizeSaves();
689     modifiableState().globalComposite = op;
690     modifiableState().globalBlend = blendMode;
691     GraphicsContext* c = drawingContext();
692     if (!c)
693         return;
694     c->setCompositeOperation(op, blendMode);
695 }
696
697 void CanvasRenderingContext2D::scale(float sx, float sy)
698 {
699     GraphicsContext* c = drawingContext();
700     if (!c)
701         return;
702     if (!state().hasInvertibleTransform)
703         return;
704
705     if (!std::isfinite(sx) || !std::isfinite(sy))
706         return;
707
708     AffineTransform newTransform = state().transform;
709     newTransform.scaleNonUniform(sx, sy);
710     if (state().transform == newTransform)
711         return;
712
713     realizeSaves();
714
715     if (!sx || !sy) {
716         modifiableState().hasInvertibleTransform = false;
717         return;
718     }
719
720     modifiableState().transform = newTransform;
721     c->scale(FloatSize(sx, sy));
722     m_path.transform(AffineTransform().scaleNonUniform(1.0 / sx, 1.0 / sy));
723 }
724
725 void CanvasRenderingContext2D::rotate(float angleInRadians)
726 {
727     GraphicsContext* c = drawingContext();
728     if (!c)
729         return;
730     if (!state().hasInvertibleTransform)
731         return;
732
733     if (!std::isfinite(angleInRadians))
734         return;
735
736     AffineTransform newTransform = state().transform;
737     newTransform.rotate(angleInRadians / piDouble * 180.0);
738     if (state().transform == newTransform)
739         return;
740
741     realizeSaves();
742
743     modifiableState().transform = newTransform;
744     c->rotate(angleInRadians);
745     m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0));
746 }
747
748 void CanvasRenderingContext2D::translate(float tx, float ty)
749 {
750     GraphicsContext* c = drawingContext();
751     if (!c)
752         return;
753     if (!state().hasInvertibleTransform)
754         return;
755
756     if (!std::isfinite(tx) | !std::isfinite(ty))
757         return;
758
759     AffineTransform newTransform = state().transform;
760     newTransform.translate(tx, ty);
761     if (state().transform == newTransform)
762         return;
763
764     realizeSaves();
765
766     modifiableState().transform = newTransform;
767     c->translate(tx, ty);
768     m_path.transform(AffineTransform().translate(-tx, -ty));
769 }
770
771 void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy)
772 {
773     GraphicsContext* c = drawingContext();
774     if (!c)
775         return;
776     if (!state().hasInvertibleTransform)
777         return;
778
779     if (!std::isfinite(m11) | !std::isfinite(m21) | !std::isfinite(dx) | !std::isfinite(m12) | !std::isfinite(m22) | !std::isfinite(dy))
780         return;
781
782     AffineTransform transform(m11, m12, m21, m22, dx, dy);
783     AffineTransform newTransform = state().transform * transform;
784     if (state().transform == newTransform)
785         return;
786
787     realizeSaves();
788
789     if (auto inverse = transform.inverse()) {
790         modifiableState().transform = newTransform;
791         c->concatCTM(transform);
792         m_path.transform(inverse.value());
793         return;
794     }
795     modifiableState().hasInvertibleTransform = false;
796 }
797
798 void CanvasRenderingContext2D::setTransform(float m11, float m12, float m21, float m22, float dx, float dy)
799 {
800     GraphicsContext* c = drawingContext();
801     if (!c)
802         return;
803
804     if (!std::isfinite(m11) | !std::isfinite(m21) | !std::isfinite(dx) | !std::isfinite(m12) | !std::isfinite(m22) | !std::isfinite(dy))
805         return;
806
807     resetTransform();
808     transform(m11, m12, m21, m22, dx, dy);
809 }
810
811 void CanvasRenderingContext2D::resetTransform()
812 {
813     GraphicsContext* c = drawingContext();
814     if (!c)
815         return;
816
817     AffineTransform ctm = state().transform;
818     bool hasInvertibleTransform = state().hasInvertibleTransform;
819
820     realizeSaves();
821
822     c->setCTM(canvas().baseTransform());
823     modifiableState().transform = AffineTransform();
824
825     if (hasInvertibleTransform)
826         m_path.transform(ctm);
827
828     modifiableState().hasInvertibleTransform = true;
829 }
830
831 void CanvasRenderingContext2D::setStrokeColor(const String& color, std::optional<float> alpha)
832 {
833     if (alpha) {
834         setStrokeStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha.value()));
835         return;
836     }
837
838     if (color == state().unparsedStrokeColor)
839         return;
840
841     realizeSaves();
842     setStrokeStyle(CanvasStyle::createFromString(color, &canvas().document()));
843     modifiableState().unparsedStrokeColor = color;
844 }
845
846 void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha)
847 {
848     if (state().strokeStyle.isValid() && state().strokeStyle.isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha))
849         return;
850     setStrokeStyle(CanvasStyle(grayLevel, alpha));
851 }
852
853 void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a)
854 {
855     if (state().strokeStyle.isValid() && state().strokeStyle.isEquivalentRGBA(r, g, b, a))
856         return;
857     setStrokeStyle(CanvasStyle(r, g, b, a));
858 }
859
860 void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a)
861 {
862     if (state().strokeStyle.isValid() && state().strokeStyle.isEquivalentCMYKA(c, m, y, k, a))
863         return;
864     setStrokeStyle(CanvasStyle(c, m, y, k, a));
865 }
866
867 void CanvasRenderingContext2D::setFillColor(const String& color, std::optional<float> alpha)
868 {
869     if (alpha) {
870         setFillStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha.value()));
871         return;
872     }
873
874     if (color == state().unparsedFillColor)
875         return;
876
877     realizeSaves();
878     setFillStyle(CanvasStyle::createFromString(color, &canvas().document()));
879     modifiableState().unparsedFillColor = color;
880 }
881
882 void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha)
883 {
884     if (state().fillStyle.isValid() && state().fillStyle.isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha))
885         return;
886     setFillStyle(CanvasStyle(grayLevel, alpha));
887 }
888
889 void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a)
890 {
891     if (state().fillStyle.isValid() && state().fillStyle.isEquivalentRGBA(r, g, b, a))
892         return;
893     setFillStyle(CanvasStyle(r, g, b, a));
894 }
895
896 void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a)
897 {
898     if (state().fillStyle.isValid() && state().fillStyle.isEquivalentCMYKA(c, m, y, k, a))
899         return;
900     setFillStyle(CanvasStyle(c, m, y, k, a));
901 }
902
903 void CanvasRenderingContext2D::beginPath()
904 {
905     m_path.clear();
906 }
907
908 static bool validateRectForCanvas(float& x, float& y, float& width, float& height)
909 {
910     if (!std::isfinite(x) | !std::isfinite(y) | !std::isfinite(width) | !std::isfinite(height))
911         return false;
912
913     if (!width && !height)
914         return false;
915
916     if (width < 0) {
917         width = -width;
918         x -= width;
919     }
920
921     if (height < 0) {
922         height = -height;
923         y -= height;
924     }
925
926     return true;
927 }
928
929 inline void CanvasRenderingContext2D::clearPathForDashboardBackwardCompatibilityMode()
930 {
931 #if ENABLE(DASHBOARD_SUPPORT)
932     if (m_usesDashboardCompatibilityMode)
933         m_path.clear();
934 #endif
935 }
936
937 static bool isFullCanvasCompositeMode(CompositeOperator op)
938 {
939     // See 4.8.11.1.3 Compositing
940     // CompositeSourceAtop and CompositeDestinationOut are not listed here as the platforms already
941     // implement the specification's behavior.
942     return op == CompositeSourceIn || op == CompositeSourceOut || op == CompositeDestinationIn || op == CompositeDestinationAtop;
943 }
944
945 static WindRule toWindRule(CanvasRenderingContext2D::WindingRule rule)
946 {
947     return rule == CanvasRenderingContext2D::WindingRule::Nonzero ? RULE_NONZERO : RULE_EVENODD;
948 }
949
950 void CanvasRenderingContext2D::fill(WindingRule windingRule)
951 {
952     fillInternal(m_path, windingRule);
953     clearPathForDashboardBackwardCompatibilityMode();
954 }
955
956 void CanvasRenderingContext2D::stroke()
957 {
958     strokeInternal(m_path);
959     clearPathForDashboardBackwardCompatibilityMode();
960 }
961
962 void CanvasRenderingContext2D::clip(WindingRule windingRule)
963 {
964     clipInternal(m_path, windingRule);
965     clearPathForDashboardBackwardCompatibilityMode();
966 }
967
968 void CanvasRenderingContext2D::fill(DOMPath& path, WindingRule windingRule)
969 {
970     fillInternal(path.path(), windingRule);
971 }
972
973 void CanvasRenderingContext2D::stroke(DOMPath& path)
974 {
975     strokeInternal(path.path());
976 }
977
978 void CanvasRenderingContext2D::clip(DOMPath& path, WindingRule windingRule)
979 {
980     clipInternal(path.path(), windingRule);
981 }
982
983 void CanvasRenderingContext2D::fillInternal(const Path& path, WindingRule windingRule)
984 {
985     auto* c = drawingContext();
986     if (!c)
987         return;
988     if (!state().hasInvertibleTransform)
989         return;
990
991     // If gradient size is zero, then paint nothing.
992     auto* gradient = c->fillGradient();
993     if (gradient && gradient->isZeroSize())
994         return;
995
996     if (!path.isEmpty()) {
997         auto savedFillRule = c->fillRule();
998         c->setFillRule(toWindRule(windingRule));
999
1000         if (isFullCanvasCompositeMode(state().globalComposite)) {
1001             beginCompositeLayer();
1002             c->fillPath(path);
1003             endCompositeLayer();
1004             didDrawEntireCanvas();
1005         } else if (state().globalComposite == CompositeCopy) {
1006             clearCanvas();
1007             c->fillPath(path);
1008             didDrawEntireCanvas();
1009         } else {
1010             c->fillPath(path);
1011             didDraw(path.fastBoundingRect());
1012         }
1013         
1014         c->setFillRule(savedFillRule);
1015     }
1016 }
1017
1018 void CanvasRenderingContext2D::strokeInternal(const Path& path)
1019 {
1020     auto* c = drawingContext();
1021     if (!c)
1022         return;
1023     if (!state().hasInvertibleTransform)
1024         return;
1025
1026     // If gradient size is zero, then paint nothing.
1027     auto* gradient = c->strokeGradient();
1028     if (gradient && gradient->isZeroSize())
1029         return;
1030
1031     if (!path.isEmpty()) {
1032         if (isFullCanvasCompositeMode(state().globalComposite)) {
1033             beginCompositeLayer();
1034             c->strokePath(path);
1035             endCompositeLayer();
1036             didDrawEntireCanvas();
1037         } else if (state().globalComposite == CompositeCopy) {
1038             clearCanvas();
1039             c->strokePath(path);
1040             didDrawEntireCanvas();
1041         } else {
1042             FloatRect dirtyRect = path.fastBoundingRect();
1043             inflateStrokeRect(dirtyRect);
1044             c->strokePath(path);
1045             didDraw(dirtyRect);
1046         }
1047     }
1048 }
1049
1050 void CanvasRenderingContext2D::clipInternal(const Path& path, WindingRule windingRule)
1051 {
1052     auto* c = drawingContext();
1053     if (!c)
1054         return;
1055     if (!state().hasInvertibleTransform)
1056         return;
1057
1058     realizeSaves();
1059     c->canvasClip(path, toWindRule(windingRule));
1060 }
1061
1062 inline void CanvasRenderingContext2D::beginCompositeLayer()
1063 {
1064 #if !USE(CAIRO)
1065     drawingContext()->beginTransparencyLayer(1);
1066 #endif
1067 }
1068
1069 inline void CanvasRenderingContext2D::endCompositeLayer()
1070 {
1071 #if !USE(CAIRO)
1072     drawingContext()->endTransparencyLayer();    
1073 #endif
1074 }
1075
1076 bool CanvasRenderingContext2D::isPointInPath(float x, float y, WindingRule windingRule)
1077 {
1078     return isPointInPathInternal(m_path, x, y, windingRule);
1079 }
1080
1081 bool CanvasRenderingContext2D::isPointInStroke(float x, float y)
1082 {
1083     return isPointInStrokeInternal(m_path, x, y);
1084 }
1085
1086 bool CanvasRenderingContext2D::isPointInPath(DOMPath& path, float x, float y, WindingRule windingRule)
1087 {
1088     return isPointInPathInternal(path.path(), x, y, windingRule);
1089 }
1090
1091 bool CanvasRenderingContext2D::isPointInStroke(DOMPath& path, float x, float y)
1092 {
1093     return isPointInStrokeInternal(path.path(), x, y);
1094 }
1095
1096 bool CanvasRenderingContext2D::isPointInPathInternal(const Path& path, float x, float y, WindingRule windingRule)
1097 {
1098     auto* c = drawingContext();
1099     if (!c)
1100         return false;
1101     if (!state().hasInvertibleTransform)
1102         return false;
1103
1104     auto transformedPoint = state().transform.inverse().value_or(AffineTransform()).mapPoint(FloatPoint(x, y));
1105
1106     if (!std::isfinite(transformedPoint.x()) || !std::isfinite(transformedPoint.y()))
1107         return false;
1108
1109     return path.contains(transformedPoint, toWindRule(windingRule));
1110 }
1111
1112 bool CanvasRenderingContext2D::isPointInStrokeInternal(const Path& path, float x, float y)
1113 {
1114     auto* c = drawingContext();
1115     if (!c)
1116         return false;
1117     if (!state().hasInvertibleTransform)
1118         return false;
1119
1120     auto transformedPoint = state().transform.inverse().value_or(AffineTransform()).mapPoint(FloatPoint(x, y));
1121     if (!std::isfinite(transformedPoint.x()) || !std::isfinite(transformedPoint.y()))
1122         return false;
1123
1124     CanvasStrokeStyleApplier applier(this);
1125     return path.strokeContains(&applier, transformedPoint);
1126 }
1127
1128 void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height)
1129 {
1130     if (!validateRectForCanvas(x, y, width, height))
1131         return;
1132     auto* context = drawingContext();
1133     if (!context)
1134         return;
1135     if (!state().hasInvertibleTransform)
1136         return;
1137     FloatRect rect(x, y, width, height);
1138
1139     bool saved = false;
1140     if (shouldDrawShadows()) {
1141         context->save();
1142         saved = true;
1143         context->setLegacyShadow(FloatSize(), 0, Color::transparent);
1144     }
1145     if (state().globalAlpha != 1) {
1146         if (!saved) {
1147             context->save();
1148             saved = true;
1149         }
1150         context->setAlpha(1);
1151     }
1152     if (state().globalComposite != CompositeSourceOver) {
1153         if (!saved) {
1154             context->save();
1155             saved = true;
1156         }
1157         context->setCompositeOperation(CompositeSourceOver);
1158     }
1159     context->clearRect(rect);
1160     if (saved)
1161         context->restore();
1162     didDraw(rect);
1163 }
1164
1165 void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height)
1166 {
1167     if (!validateRectForCanvas(x, y, width, height))
1168         return;
1169
1170     auto* c = drawingContext();
1171     if (!c)
1172         return;
1173     if (!state().hasInvertibleTransform)
1174         return;
1175
1176     // from the HTML5 Canvas spec:
1177     // If x0 = x1 and y0 = y1, then the linear gradient must paint nothing
1178     // If x0 = x1 and y0 = y1 and r0 = r1, then the radial gradient must paint nothing
1179     auto* gradient = c->fillGradient();
1180     if (gradient && gradient->isZeroSize())
1181         return;
1182
1183     FloatRect rect(x, y, width, height);
1184
1185     if (rectContainsCanvas(rect)) {
1186         c->fillRect(rect);
1187         didDrawEntireCanvas();
1188     } else if (isFullCanvasCompositeMode(state().globalComposite)) {
1189         beginCompositeLayer();
1190         c->fillRect(rect);
1191         endCompositeLayer();
1192         didDrawEntireCanvas();
1193     } else if (state().globalComposite == CompositeCopy) {
1194         clearCanvas();
1195         c->fillRect(rect);
1196         didDrawEntireCanvas();
1197     } else {
1198         c->fillRect(rect);
1199         didDraw(rect);
1200     }
1201 }
1202
1203 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height)
1204 {
1205     if (!validateRectForCanvas(x, y, width, height))
1206         return;
1207
1208     auto* c = drawingContext();
1209     if (!c)
1210         return;
1211     if (!state().hasInvertibleTransform)
1212         return;
1213     if (!(state().lineWidth >= 0))
1214         return;
1215
1216     // If gradient size is zero, then paint nothing.
1217     auto* gradient = c->strokeGradient();
1218     if (gradient && gradient->isZeroSize())
1219         return;
1220
1221     FloatRect rect(x, y, width, height);
1222     if (isFullCanvasCompositeMode(state().globalComposite)) {
1223         beginCompositeLayer();
1224         c->strokeRect(rect, state().lineWidth);
1225         endCompositeLayer();
1226         didDrawEntireCanvas();
1227     } else if (state().globalComposite == CompositeCopy) {
1228         clearCanvas();
1229         c->strokeRect(rect, state().lineWidth);
1230         didDrawEntireCanvas();
1231     } else {
1232         FloatRect boundingRect = rect;
1233         boundingRect.inflate(state().lineWidth / 2);
1234         c->strokeRect(rect, state().lineWidth);
1235         didDraw(boundingRect);
1236     }
1237 }
1238
1239 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& colorString, std::optional<float> alpha)
1240 {
1241     Color color = Color::transparent;
1242     if (!colorString.isNull()) {
1243         color = parseColorOrCurrentColor(colorString, &canvas());
1244         if (!color.isValid())
1245             return;
1246     }
1247     // FIXME: Should not use RGBA32 here.
1248     setShadow(FloatSize(width, height), blur, colorWithOverrideAlpha(color.rgb(), alpha));
1249 }
1250
1251 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha)
1252 {
1253     setShadow(FloatSize(width, height), blur, Color(grayLevel, grayLevel, grayLevel, alpha));
1254 }
1255
1256 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a)
1257 {
1258     setShadow(FloatSize(width, height), blur, Color(r, g, b, a));
1259 }
1260
1261 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a)
1262 {
1263     setShadow(FloatSize(width, height), blur, Color(c, m, y, k, a));
1264 }
1265
1266 void CanvasRenderingContext2D::clearShadow()
1267 {
1268     setShadow(FloatSize(), 0, Color::transparent);
1269 }
1270
1271 void CanvasRenderingContext2D::setShadow(const FloatSize& offset, float blur, const Color& color)
1272 {
1273     if (state().shadowOffset == offset && state().shadowBlur == blur && state().shadowColor == color)
1274         return;
1275     bool wasDrawingShadows = shouldDrawShadows();
1276     realizeSaves();
1277     modifiableState().shadowOffset = offset;
1278     modifiableState().shadowBlur = blur;
1279     modifiableState().shadowColor = color;
1280     if (!wasDrawingShadows && !shouldDrawShadows())
1281         return;
1282     applyShadow();
1283 }
1284
1285 void CanvasRenderingContext2D::applyShadow()
1286 {
1287     auto* c = drawingContext();
1288     if (!c)
1289         return;
1290
1291     if (shouldDrawShadows()) {
1292         float width = state().shadowOffset.width();
1293         float height = state().shadowOffset.height();
1294         c->setLegacyShadow(FloatSize(width, -height), state().shadowBlur, state().shadowColor);
1295     } else
1296         c->setLegacyShadow(FloatSize(), 0, Color::transparent);
1297 }
1298
1299 bool CanvasRenderingContext2D::shouldDrawShadows() const
1300 {
1301     return state().shadowColor.isVisible() && (state().shadowBlur || !state().shadowOffset.isZero());
1302 }
1303
1304 enum class ImageSizeType { AfterDevicePixelRatio, BeforeDevicePixelRatio };
1305 static LayoutSize size(HTMLImageElement& element, ImageSizeType sizeType = ImageSizeType::BeforeDevicePixelRatio)
1306 {
1307     LayoutSize size;
1308     if (auto* cachedImage = element.cachedImage()) {
1309         size = cachedImage->imageSizeForRenderer(element.renderer(), 1.0f); // FIXME: Not sure about this.
1310         if (sizeType == ImageSizeType::AfterDevicePixelRatio && is<RenderImage>(element.renderer()) && cachedImage->image() && !cachedImage->image()->hasRelativeWidth())
1311             size.scale(downcast<RenderImage>(*element.renderer()).imageDevicePixelRatio());
1312     }
1313     return size;
1314 }
1315
1316 static inline FloatSize size(HTMLCanvasElement& canvasElement)
1317 {
1318     return canvasElement.size();
1319 }
1320
1321 #if ENABLE(VIDEO)
1322
1323 static inline FloatSize size(HTMLVideoElement& video)
1324 {
1325     auto* player = video.player();
1326     if (!player)
1327         return { };
1328     return player->naturalSize();
1329 }
1330
1331 #endif
1332
1333 static inline FloatRect normalizeRect(const FloatRect& rect)
1334 {
1335     return FloatRect(std::min(rect.x(), rect.maxX()),
1336         std::min(rect.y(), rect.maxY()),
1337         std::max(rect.width(), -rect.width()),
1338         std::max(rect.height(), -rect.height()));
1339 }
1340
1341 ExceptionOr<void> CanvasRenderingContext2D::drawImage(CanvasImageSource&& image, float dx, float dy)
1342 {
1343     return WTF::switchOn(image,
1344         [&] (RefPtr<HTMLImageElement>& imageElement) -> ExceptionOr<void> {
1345             LayoutSize destRectSize = size(*imageElement, ImageSizeType::AfterDevicePixelRatio);
1346             LayoutSize sourceRectSize = size(*imageElement, ImageSizeType::BeforeDevicePixelRatio);
1347             return this->drawImage(*imageElement, FloatRect { 0, 0, sourceRectSize.width(), sourceRectSize.height() }, FloatRect { dx, dy, destRectSize.width(), destRectSize.height() });
1348         },
1349         [&] (auto& element) -> ExceptionOr<void> {
1350             FloatSize elementSize = size(*element);
1351             return this->drawImage(*element, FloatRect { 0, 0, elementSize.width(), elementSize.height() }, FloatRect { dx, dy, elementSize.width(), elementSize.height() });
1352         }
1353     );
1354 }
1355
1356 ExceptionOr<void> CanvasRenderingContext2D::drawImage(CanvasImageSource&& image, float dx, float dy, float dw, float dh)
1357 {
1358     return WTF::switchOn(image,
1359         [&] (auto& element) -> ExceptionOr<void> {
1360             FloatSize elementSize = size(*element);
1361             return this->drawImage(*element, FloatRect { 0, 0, elementSize.width(), elementSize.height() }, FloatRect { dx, dy, dw, dh });
1362         }
1363     );
1364 }
1365
1366 ExceptionOr<void> CanvasRenderingContext2D::drawImage(CanvasImageSource&& image, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh)
1367 {
1368     return WTF::switchOn(image,
1369         [&] (auto& element) -> ExceptionOr<void> {
1370             return this->drawImage(*element, FloatRect { sx, sy, sw, sh }, FloatRect { dx, dy, dw, dh });
1371         }
1372     );
1373 }
1374
1375 ExceptionOr<void> CanvasRenderingContext2D::drawImage(HTMLImageElement& imageElement, const FloatRect& srcRect, const FloatRect& dstRect)
1376 {
1377     return drawImage(imageElement, srcRect, dstRect, state().globalComposite, state().globalBlend);
1378 }
1379
1380 ExceptionOr<void> CanvasRenderingContext2D::drawImage(HTMLImageElement& imageElement, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator& op, const BlendMode& blendMode)
1381 {
1382     if (!std::isfinite(dstRect.x()) || !std::isfinite(dstRect.y()) || !std::isfinite(dstRect.width()) || !std::isfinite(dstRect.height())
1383         || !std::isfinite(srcRect.x()) || !std::isfinite(srcRect.y()) || !std::isfinite(srcRect.width()) || !std::isfinite(srcRect.height()))
1384         return { };
1385
1386     if (!dstRect.width() || !dstRect.height())
1387         return { };
1388
1389     if (!imageElement.complete())
1390         return { };
1391
1392     FloatRect normalizedSrcRect = normalizeRect(srcRect);
1393     FloatRect normalizedDstRect = normalizeRect(dstRect);
1394
1395     FloatRect imageRect = FloatRect(FloatPoint(), size(imageElement, ImageSizeType::BeforeDevicePixelRatio));
1396     if (!srcRect.width() || !srcRect.height())
1397         return Exception { INDEX_SIZE_ERR };
1398
1399     // When the source rectangle is outside the source image, the source rectangle must be clipped
1400     // to the source image and the destination rectangle must be clipped in the same proportion.
1401     FloatRect originalNormalizedSrcRect = normalizedSrcRect;
1402     normalizedSrcRect.intersect(imageRect);
1403     if (normalizedSrcRect.isEmpty())
1404         return { };
1405
1406     if (normalizedSrcRect != originalNormalizedSrcRect) {
1407         normalizedDstRect.setWidth(normalizedDstRect.width() * normalizedSrcRect.width() / originalNormalizedSrcRect.width());
1408         normalizedDstRect.setHeight(normalizedDstRect.height() * normalizedSrcRect.height() / originalNormalizedSrcRect.height());
1409         if (normalizedDstRect.isEmpty())
1410             return { };
1411     }
1412
1413     GraphicsContext* c = drawingContext();
1414     if (!c)
1415         return { };
1416     if (!state().hasInvertibleTransform)
1417         return { };
1418
1419     CachedImage* cachedImage = imageElement.cachedImage();
1420     if (!cachedImage)
1421         return { };
1422
1423     Image* image = cachedImage->imageForRenderer(imageElement.renderer());
1424     if (!image)
1425         return { };
1426
1427     ImageObserver* observer = image->imageObserver();
1428
1429     if (image->isSVGImage()) {
1430         image->setImageObserver(nullptr);
1431         image->setContainerSize(imageRect.size());
1432     }
1433
1434     if (image->isBitmapImage())
1435         downcast<BitmapImage>(*image).updateFromSettings(imageElement.document().settings());
1436
1437     if (rectContainsCanvas(normalizedDstRect)) {
1438         c->drawImage(*image, normalizedDstRect, normalizedSrcRect, ImagePaintingOptions(op, blendMode));
1439         didDrawEntireCanvas();
1440     } else if (isFullCanvasCompositeMode(op)) {
1441         fullCanvasCompositedDrawImage(*image, normalizedDstRect, normalizedSrcRect, op);
1442         didDrawEntireCanvas();
1443     } else if (op == CompositeCopy) {
1444         clearCanvas();
1445         c->drawImage(*image, normalizedDstRect, normalizedSrcRect, ImagePaintingOptions(op, blendMode));
1446         didDrawEntireCanvas();
1447     } else {
1448         c->drawImage(*image, normalizedDstRect, normalizedSrcRect, ImagePaintingOptions(op, blendMode));
1449         didDraw(normalizedDstRect);
1450     }
1451     
1452     if (image->isSVGImage())
1453         image->setImageObserver(observer);
1454
1455     checkOrigin(&imageElement);
1456
1457     return { };
1458 }
1459
1460 ExceptionOr<void> CanvasRenderingContext2D::drawImage(HTMLCanvasElement& sourceCanvas, const FloatRect& srcRect, const FloatRect& dstRect)
1461 {
1462     FloatRect srcCanvasRect = FloatRect(FloatPoint(), sourceCanvas.size());
1463
1464     if (!srcCanvasRect.width() || !srcCanvasRect.height())
1465         return Exception { INVALID_STATE_ERR };
1466
1467     if (!srcRect.width() || !srcRect.height())
1468         return Exception { INDEX_SIZE_ERR };
1469
1470     if (!srcCanvasRect.contains(normalizeRect(srcRect)) || !dstRect.width() || !dstRect.height())
1471         return { };
1472
1473     GraphicsContext* c = drawingContext();
1474     if (!c)
1475         return { };
1476     if (!state().hasInvertibleTransform)
1477         return { };
1478
1479     // FIXME: Do this through platform-independent GraphicsContext API.
1480     ImageBuffer* buffer = sourceCanvas.buffer();
1481     if (!buffer)
1482         return { };
1483
1484     checkOrigin(&sourceCanvas);
1485
1486 #if ENABLE(ACCELERATED_2D_CANVAS)
1487     // If we're drawing from one accelerated canvas 2d to another, avoid calling sourceCanvas.makeRenderingResultsAvailable()
1488     // as that will do a readback to software.
1489     CanvasRenderingContext* sourceContext = sourceCanvas.renderingContext();
1490     // FIXME: Implement an accelerated path for drawing from a WebGL canvas to a 2d canvas when possible.
1491     if (!isAccelerated() || !sourceContext || !sourceContext->isAccelerated() || !sourceContext->is2d())
1492         sourceCanvas.makeRenderingResultsAvailable();
1493 #else
1494     sourceCanvas.makeRenderingResultsAvailable();
1495 #endif
1496
1497     if (rectContainsCanvas(dstRect)) {
1498         c->drawImageBuffer(*buffer, dstRect, srcRect, ImagePaintingOptions(state().globalComposite, state().globalBlend));
1499         didDrawEntireCanvas();
1500     } else if (isFullCanvasCompositeMode(state().globalComposite)) {
1501         fullCanvasCompositedDrawImage(*buffer, dstRect, srcRect, state().globalComposite);
1502         didDrawEntireCanvas();
1503     } else if (state().globalComposite == CompositeCopy) {
1504         clearCanvas();
1505         c->drawImageBuffer(*buffer, dstRect, srcRect, ImagePaintingOptions(state().globalComposite, state().globalBlend));
1506         didDrawEntireCanvas();
1507     } else {
1508         c->drawImageBuffer(*buffer, dstRect, srcRect, ImagePaintingOptions(state().globalComposite, state().globalBlend));
1509         didDraw(dstRect);
1510     }
1511
1512     return { };
1513 }
1514
1515 #if ENABLE(VIDEO)
1516
1517 ExceptionOr<void> CanvasRenderingContext2D::drawImage(HTMLVideoElement& video, const FloatRect& srcRect, const FloatRect& dstRect)
1518 {
1519     if (video.readyState() == HTMLMediaElement::HAVE_NOTHING || video.readyState() == HTMLMediaElement::HAVE_METADATA)
1520         return { };
1521
1522     FloatRect videoRect = FloatRect(FloatPoint(), size(video));
1523     if (!srcRect.width() || !srcRect.height())
1524         return Exception { INDEX_SIZE_ERR };
1525
1526     if (!videoRect.contains(normalizeRect(srcRect)) || !dstRect.width() || !dstRect.height())
1527         return { };
1528
1529     GraphicsContext* c = drawingContext();
1530     if (!c)
1531         return { };
1532     if (!state().hasInvertibleTransform)
1533         return { };
1534
1535     checkOrigin(&video);
1536
1537 #if USE(CG) || (ENABLE(ACCELERATED_2D_CANVAS) && USE(GSTREAMER_GL) && USE(CAIRO))
1538     if (NativeImagePtr image = video.nativeImageForCurrentTime()) {
1539         c->drawNativeImage(image, FloatSize(video.videoWidth(), video.videoHeight()), dstRect, srcRect);
1540         if (rectContainsCanvas(dstRect))
1541             didDrawEntireCanvas();
1542         else
1543             didDraw(dstRect);
1544
1545         return { };
1546     }
1547 #endif
1548
1549     GraphicsContextStateSaver stateSaver(*c);
1550     c->clip(dstRect);
1551     c->translate(dstRect.x(), dstRect.y());
1552     c->scale(FloatSize(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height()));
1553     c->translate(-srcRect.x(), -srcRect.y());
1554     video.paintCurrentFrameInContext(*c, FloatRect(FloatPoint(), size(video)));
1555     stateSaver.restore();
1556     didDraw(dstRect);
1557
1558     return { };
1559 }
1560
1561 #endif
1562
1563 void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement& imageElement, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh, const String& compositeOperation)
1564 {
1565     CompositeOperator op;
1566     auto blendOp = BlendModeNormal;
1567     if (!parseCompositeAndBlendOperator(compositeOperation, op, blendOp) || blendOp != BlendModeNormal)
1568         op = CompositeSourceOver;
1569     drawImage(imageElement, FloatRect { sx, sy, sw, sh }, FloatRect { dx, dy, dw, dh }, op, BlendModeNormal);
1570 }
1571
1572 void CanvasRenderingContext2D::setAlpha(float alpha)
1573 {
1574     setGlobalAlpha(alpha);
1575 }
1576
1577 void CanvasRenderingContext2D::setCompositeOperation(const String& operation)
1578 {
1579     setGlobalCompositeOperation(operation);
1580 }
1581
1582 void CanvasRenderingContext2D::clearCanvas()
1583 {
1584     auto* c = drawingContext();
1585     if (!c)
1586         return;
1587
1588     c->save();
1589     c->setCTM(canvas().baseTransform());
1590     c->clearRect(FloatRect(0, 0, canvas().width(), canvas().height()));
1591     c->restore();
1592 }
1593
1594 Path CanvasRenderingContext2D::transformAreaToDevice(const Path& path) const
1595 {
1596     Path transformed(path);
1597     transformed.transform(state().transform);
1598     transformed.transform(canvas().baseTransform());
1599     return transformed;
1600 }
1601
1602 Path CanvasRenderingContext2D::transformAreaToDevice(const FloatRect& rect) const
1603 {
1604     Path path;
1605     path.addRect(rect);
1606     return transformAreaToDevice(path);
1607 }
1608
1609 bool CanvasRenderingContext2D::rectContainsCanvas(const FloatRect& rect) const
1610 {
1611     FloatQuad quad(rect);
1612     FloatQuad canvasQuad(FloatRect(0, 0, canvas().width(), canvas().height()));
1613     return state().transform.mapQuad(quad).containsQuad(canvasQuad);
1614 }
1615
1616 template<class T> IntRect CanvasRenderingContext2D::calculateCompositingBufferRect(const T& area, IntSize* croppedOffset)
1617 {
1618     IntRect canvasRect(0, 0, canvas().width(), canvas().height());
1619     canvasRect = canvas().baseTransform().mapRect(canvasRect);
1620     Path path = transformAreaToDevice(area);
1621     IntRect bufferRect = enclosingIntRect(path.fastBoundingRect());
1622     IntPoint originalLocation = bufferRect.location();
1623     bufferRect.intersect(canvasRect);
1624     if (croppedOffset)
1625         *croppedOffset = originalLocation - bufferRect.location();
1626     return bufferRect;
1627 }
1628
1629 std::unique_ptr<ImageBuffer> CanvasRenderingContext2D::createCompositingBuffer(const IntRect& bufferRect)
1630 {
1631     return ImageBuffer::create(bufferRect.size(), isAccelerated() ? Accelerated : Unaccelerated);
1632 }
1633
1634 void CanvasRenderingContext2D::compositeBuffer(ImageBuffer& buffer, const IntRect& bufferRect, CompositeOperator op)
1635 {
1636     IntRect canvasRect(0, 0, canvas().width(), canvas().height());
1637     canvasRect = canvas().baseTransform().mapRect(canvasRect);
1638
1639     auto* c = drawingContext();
1640     if (!c)
1641         return;
1642
1643     c->save();
1644     c->setCTM(AffineTransform());
1645     c->setCompositeOperation(op);
1646
1647     c->save();
1648     c->clipOut(bufferRect);
1649     c->clearRect(canvasRect);
1650     c->restore();
1651     c->drawImageBuffer(buffer, bufferRect.location(), state().globalComposite);
1652     c->restore();
1653 }
1654
1655 static void drawImageToContext(Image& image, GraphicsContext& context, const FloatRect& dest, const FloatRect& src, CompositeOperator op)
1656 {
1657     context.drawImage(image, dest, src, op);
1658 }
1659
1660 static void drawImageToContext(ImageBuffer& imageBuffer, GraphicsContext& context, const FloatRect& dest, const FloatRect& src, CompositeOperator op)
1661 {
1662     context.drawImageBuffer(imageBuffer, dest, src, op);
1663 }
1664
1665 template<class T> void CanvasRenderingContext2D::fullCanvasCompositedDrawImage(T& image, const FloatRect& dest, const FloatRect& src, CompositeOperator op)
1666 {
1667     ASSERT(isFullCanvasCompositeMode(op));
1668
1669     IntSize croppedOffset;
1670     auto bufferRect = calculateCompositingBufferRect(dest, &croppedOffset);
1671     if (bufferRect.isEmpty()) {
1672         clearCanvas();
1673         return;
1674     }
1675
1676     auto buffer = createCompositingBuffer(bufferRect);
1677     if (!buffer)
1678         return;
1679
1680     auto* c = drawingContext();
1681     if (!c)
1682         return;
1683
1684     FloatRect adjustedDest = dest;
1685     adjustedDest.setLocation(FloatPoint(0, 0));
1686     AffineTransform effectiveTransform = c->getCTM();
1687     IntRect transformedAdjustedRect = enclosingIntRect(effectiveTransform.mapRect(adjustedDest));
1688     buffer->context().translate(-transformedAdjustedRect.location().x(), -transformedAdjustedRect.location().y());
1689     buffer->context().translate(croppedOffset.width(), croppedOffset.height());
1690     buffer->context().concatCTM(effectiveTransform);
1691     drawImageToContext(image, buffer->context(), adjustedDest, src, CompositeSourceOver);
1692
1693     compositeBuffer(*buffer, bufferRect, op);
1694 }
1695
1696 void CanvasRenderingContext2D::prepareGradientForDashboard(CanvasGradient& gradient) const
1697 {
1698 #if ENABLE(DASHBOARD_SUPPORT)
1699     if (m_usesDashboardCompatibilityMode)
1700         gradient.setDashboardCompatibilityMode();
1701 #else
1702     UNUSED_PARAM(gradient);
1703 #endif
1704 }
1705
1706 static CanvasRenderingContext2D::Style toStyle(const CanvasStyle& style)
1707 {
1708     if (auto* gradient = style.canvasGradient())
1709         return RefPtr<CanvasGradient> { gradient };
1710     if (auto* pattern = style.canvasPattern())
1711         return RefPtr<CanvasPattern> { pattern };
1712     return style.color();
1713 }
1714
1715 CanvasRenderingContext2D::Style CanvasRenderingContext2D::strokeStyle() const
1716 {
1717     return toStyle(state().strokeStyle);
1718 }
1719
1720 void CanvasRenderingContext2D::setStrokeStyle(CanvasRenderingContext2D::Style&& style)
1721 {
1722     WTF::switchOn(style,
1723         [this] (const String& string) { this->setStrokeColor(string); },
1724         [this] (const RefPtr<CanvasGradient>& gradient) { this->setStrokeStyle(CanvasStyle(*gradient)); },
1725         [this] (const RefPtr<CanvasPattern>& pattern) { this->setStrokeStyle(CanvasStyle(*pattern)); }
1726     );
1727 }
1728
1729 CanvasRenderingContext2D::Style CanvasRenderingContext2D::fillStyle() const
1730 {
1731     return toStyle(state().fillStyle);
1732 }
1733
1734 void CanvasRenderingContext2D::setFillStyle(CanvasRenderingContext2D::Style&& style)
1735 {
1736     WTF::switchOn(style,
1737         [this] (const String& string) { this->setFillColor(string); },
1738         [this] (const RefPtr<CanvasGradient>& gradient) { this->setFillStyle(CanvasStyle(*gradient)); },
1739         [this] (const RefPtr<CanvasPattern>& pattern) { this->setFillStyle(CanvasStyle(*pattern)); }
1740     );
1741 }
1742
1743 ExceptionOr<Ref<CanvasGradient>> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1)
1744 {
1745     if (!std::isfinite(x0) || !std::isfinite(y0) || !std::isfinite(x1) || !std::isfinite(y1))
1746         return Exception { NOT_SUPPORTED_ERR };
1747
1748     auto gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1));
1749     prepareGradientForDashboard(gradient.get());
1750     return WTFMove(gradient);
1751 }
1752
1753 ExceptionOr<Ref<CanvasGradient>> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1)
1754 {
1755     if (!std::isfinite(x0) || !std::isfinite(y0) || !std::isfinite(r0) || !std::isfinite(x1) || !std::isfinite(y1) || !std::isfinite(r1))
1756         return Exception { NOT_SUPPORTED_ERR };
1757
1758     if (r0 < 0 || r1 < 0)
1759         return Exception { INDEX_SIZE_ERR };
1760
1761     auto gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
1762     prepareGradientForDashboard(gradient.get());
1763     return WTFMove(gradient);
1764 }
1765
1766 ExceptionOr<RefPtr<CanvasPattern>> CanvasRenderingContext2D::createPattern(CanvasImageSource&& image, const String& repetition)
1767 {
1768     bool repeatX, repeatY;
1769     if (!CanvasPattern::parseRepetitionType(repetition, repeatX, repeatY))
1770         return Exception { SYNTAX_ERR };
1771
1772     return WTF::switchOn(image,
1773         [&] (auto& element) -> ExceptionOr<RefPtr<CanvasPattern>> { return this->createPattern(*element, repeatX, repeatY); }
1774     );
1775 }
1776
1777 ExceptionOr<RefPtr<CanvasPattern>> CanvasRenderingContext2D::createPattern(HTMLImageElement& imageElement, bool repeatX, bool repeatY)
1778 {
1779     auto* cachedImage = imageElement.cachedImage();
1780
1781     // If the image loading hasn't started or the image is not complete, it is not fully decodable.
1782     if (!cachedImage || !imageElement.complete())
1783         return nullptr;
1784
1785     if (cachedImage->status() == CachedResource::LoadError)
1786         return Exception { INVALID_STATE_ERR };
1787
1788     bool originClean = cachedImage->isOriginClean(canvas().securityOrigin());
1789
1790     // FIXME: SVG images with animations can switch between clean and dirty (leaking cross-origin
1791     // data). We should either:
1792     //   1) Take a fixed snapshot of an SVG image when creating a pattern and determine then whether
1793     //      the origin is clean.
1794     //   2) Dynamically verify the origin checks at draw time, and dirty the canvas accordingly.
1795     // To be on the safe side, taint the origin for all patterns containing SVG images for now.
1796     if (cachedImage->image()->isSVGImage())
1797         originClean = false;
1798
1799     return RefPtr<CanvasPattern> { CanvasPattern::create(*cachedImage->imageForRenderer(imageElement.renderer()), repeatX, repeatY, originClean) };
1800 }
1801
1802 ExceptionOr<RefPtr<CanvasPattern>> CanvasRenderingContext2D::createPattern(HTMLCanvasElement& canvas, bool repeatX, bool repeatY)
1803 {
1804     if (!canvas.width() || !canvas.height() || !canvas.buffer())
1805         return Exception { INVALID_STATE_ERR };
1806
1807     return RefPtr<CanvasPattern> { CanvasPattern::create(*canvas.copiedImage(), repeatX, repeatY, canvas.originClean()) };
1808 }
1809     
1810 #if ENABLE(VIDEO)
1811
1812 ExceptionOr<RefPtr<CanvasPattern>> CanvasRenderingContext2D::createPattern(HTMLVideoElement& videoElement, bool repeatX, bool repeatY)
1813 {
1814     if (videoElement.readyState() < HTMLMediaElement::HAVE_CURRENT_DATA)
1815         return nullptr;
1816     
1817     checkOrigin(&videoElement);
1818     bool originClean = canvas().originClean();
1819
1820 #if USE(CG) || (ENABLE(ACCELERATED_2D_CANVAS) && USE(GSTREAMER_GL) && USE(CAIRO))
1821     if (auto nativeImage = videoElement.nativeImageForCurrentTime())
1822         return RefPtr<CanvasPattern> { CanvasPattern::create(BitmapImage::create(WTFMove(nativeImage)), repeatX, repeatY, originClean) };
1823 #endif
1824
1825     auto imageBuffer = ImageBuffer::create(size(videoElement), drawingContext() ? drawingContext()->renderingMode() : Accelerated);
1826     videoElement.paintCurrentFrameInContext(imageBuffer->context(), FloatRect(FloatPoint(), size(videoElement)));
1827     
1828     return RefPtr<CanvasPattern> { CanvasPattern::create(ImageBuffer::sinkIntoImage(WTFMove(imageBuffer), Unscaled).releaseNonNull(), repeatX, repeatY, originClean) };
1829 }
1830
1831 #endif
1832
1833 void CanvasRenderingContext2D::didDrawEntireCanvas()
1834 {
1835     didDraw(FloatRect(FloatPoint::zero(), canvas().size()), CanvasDidDrawApplyClip);
1836 }
1837
1838 void CanvasRenderingContext2D::didDraw(const FloatRect& r, unsigned options)
1839 {
1840     auto* c = drawingContext();
1841     if (!c)
1842         return;
1843     if (!state().hasInvertibleTransform)
1844         return;
1845
1846 #if ENABLE(ACCELERATED_2D_CANVAS)
1847     // If we are drawing to hardware and we have a composited layer, just call contentChanged().
1848     if (isAccelerated()) {
1849         RenderBox* renderBox = canvas().renderBox();
1850         if (renderBox && renderBox->hasAcceleratedCompositing()) {
1851             renderBox->contentChanged(CanvasPixelsChanged);
1852             canvas().clearCopiedImage();
1853             canvas().notifyObserversCanvasChanged(r);
1854             return;
1855         }
1856     }
1857 #endif
1858
1859     FloatRect dirtyRect = r;
1860     if (options & CanvasDidDrawApplyTransform) {
1861         AffineTransform ctm = state().transform;
1862         dirtyRect = ctm.mapRect(r);
1863     }
1864
1865     if (options & CanvasDidDrawApplyShadow && state().shadowColor.isVisible()) {
1866         // The shadow gets applied after transformation
1867         FloatRect shadowRect(dirtyRect);
1868         shadowRect.move(state().shadowOffset);
1869         shadowRect.inflate(state().shadowBlur);
1870         dirtyRect.unite(shadowRect);
1871     }
1872
1873     if (options & CanvasDidDrawApplyClip) {
1874         // FIXME: apply the current clip to the rectangle. Unfortunately we can't get the clip
1875         // back out of the GraphicsContext, so to take clip into account for incremental painting,
1876         // we'd have to keep the clip path around.
1877     }
1878
1879     canvas().didDraw(dirtyRect);
1880 }
1881
1882 void CanvasRenderingContext2D::setTracksDisplayListReplay(bool tracksDisplayListReplay)
1883 {
1884     if (tracksDisplayListReplay == m_tracksDisplayListReplay)
1885         return;
1886
1887     m_tracksDisplayListReplay = tracksDisplayListReplay;
1888     if (!m_tracksDisplayListReplay)
1889         contextDisplayListMap().remove(this);
1890 }
1891
1892 String CanvasRenderingContext2D::displayListAsText(DisplayList::AsTextFlags flags) const
1893 {
1894     if (!m_recordingContext)
1895         return { };
1896     return m_recordingContext->displayList.asText(flags);
1897 }
1898
1899 String CanvasRenderingContext2D::replayDisplayListAsText(DisplayList::AsTextFlags flags) const
1900 {
1901     auto* displayList = contextDisplayListMap().get(this);
1902     if (!displayList)
1903         return { };
1904     return displayList->asText(flags);
1905 }
1906
1907 void CanvasRenderingContext2D::paintRenderingResultsToCanvas()
1908 {
1909     if (UNLIKELY(m_usesDisplayListDrawing)) {
1910         if (!m_recordingContext)
1911             return;
1912
1913         FloatRect clip(FloatPoint::zero(), canvas().size());
1914         DisplayList::Replayer replayer(*canvas().drawingContext(), m_recordingContext->displayList);
1915
1916         if (UNLIKELY(m_tracksDisplayListReplay)) {
1917             auto replayList = replayer.replay(clip, m_tracksDisplayListReplay);
1918             contextDisplayListMap().add(this, WTFMove(replayList));
1919         } else
1920             replayer.replay(clip);
1921
1922         m_recordingContext->displayList.clear();
1923     }
1924 }
1925
1926 GraphicsContext* CanvasRenderingContext2D::drawingContext() const
1927 {
1928     if (UNLIKELY(m_usesDisplayListDrawing)) {
1929         if (!m_recordingContext)
1930             m_recordingContext = std::make_unique<DisplayListDrawingContext>(FloatRect(FloatPoint::zero(), canvas().size()));
1931         return &m_recordingContext->context;
1932     }
1933
1934     return canvas().drawingContext();
1935 }
1936
1937 static RefPtr<ImageData> createEmptyImageData(const IntSize& size)
1938 {
1939     auto data = ImageData::create(size);
1940     if (data)
1941         data->data()->zeroFill();
1942     return data;
1943 }
1944
1945 ExceptionOr<RefPtr<ImageData>> CanvasRenderingContext2D::createImageData(ImageData* imageData) const
1946 {
1947     if (!imageData)
1948         return Exception { NOT_SUPPORTED_ERR };
1949
1950     return createEmptyImageData(imageData->size());
1951 }
1952
1953 ExceptionOr<RefPtr<ImageData>> CanvasRenderingContext2D::createImageData(float sw, float sh) const
1954 {
1955     if (!sw || !sh)
1956         return Exception { INDEX_SIZE_ERR };
1957
1958     FloatSize logicalSize(std::abs(sw), std::abs(sh));
1959     if (!logicalSize.isExpressibleAsIntSize())
1960         return nullptr;
1961
1962     IntSize size = expandedIntSize(logicalSize);
1963     if (size.width() < 1)
1964         size.setWidth(1);
1965     if (size.height() < 1)
1966         size.setHeight(1);
1967
1968     return createEmptyImageData(size);
1969 }
1970
1971 ExceptionOr<RefPtr<ImageData>> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh) const
1972 {
1973     return getImageData(ImageBuffer::LogicalCoordinateSystem, sx, sy, sw, sh);
1974 }
1975
1976 ExceptionOr<RefPtr<ImageData>> CanvasRenderingContext2D::webkitGetImageDataHD(float sx, float sy, float sw, float sh) const
1977 {
1978     return getImageData(ImageBuffer::BackingStoreCoordinateSystem, sx, sy, sw, sh);
1979 }
1980
1981 ExceptionOr<RefPtr<ImageData>> CanvasRenderingContext2D::getImageData(ImageBuffer::CoordinateSystem coordinateSystem, float sx, float sy, float sw, float sh) const
1982 {
1983     if (!canvas().originClean()) {
1984         static NeverDestroyed<String> consoleMessage(ASCIILiteral("Unable to get image data from canvas because the canvas has been tainted by cross-origin data."));
1985         canvas().document().addConsoleMessage(MessageSource::Security, MessageLevel::Error, consoleMessage);
1986         return Exception { SECURITY_ERR };
1987     }
1988
1989     if (!sw || !sh)
1990         return Exception { INDEX_SIZE_ERR };
1991
1992     if (sw < 0) {
1993         sx += sw;
1994         sw = -sw;
1995     }    
1996     if (sh < 0) {
1997         sy += sh;
1998         sh = -sh;
1999     }
2000
2001     FloatRect logicalRect(sx, sy, sw, sh);
2002     if (logicalRect.width() < 1)
2003         logicalRect.setWidth(1);
2004     if (logicalRect.height() < 1)
2005         logicalRect.setHeight(1);
2006     if (!logicalRect.isExpressibleAsIntRect())
2007         return nullptr;
2008
2009     IntRect imageDataRect = enclosingIntRect(logicalRect);
2010     ImageBuffer* buffer = canvas().buffer();
2011     if (!buffer)
2012         return createEmptyImageData(imageDataRect.size());
2013
2014     auto byteArray = buffer->getUnmultipliedImageData(imageDataRect, nullptr, coordinateSystem);
2015     if (!byteArray) {
2016         StringBuilder consoleMessage;
2017         consoleMessage.appendLiteral("Unable to get image data from canvas. Requested size was ");
2018         consoleMessage.appendNumber(imageDataRect.width());
2019         consoleMessage.appendLiteral(" x ");
2020         consoleMessage.appendNumber(imageDataRect.height());
2021
2022         canvas().document().addConsoleMessage(MessageSource::Rendering, MessageLevel::Error, consoleMessage.toString());
2023         return Exception { INVALID_STATE_ERR };
2024     }
2025
2026     return ImageData::create(imageDataRect.size(), byteArray.releaseNonNull());
2027 }
2028
2029 void CanvasRenderingContext2D::putImageData(ImageData& data, float dx, float dy)
2030 {
2031     putImageData(data, dx, dy, 0, 0, data.width(), data.height());
2032 }
2033
2034 void CanvasRenderingContext2D::webkitPutImageDataHD(ImageData& data, float dx, float dy)
2035 {
2036     webkitPutImageDataHD(data, dx, dy, 0, 0, data.width(), data.height());
2037 }
2038
2039 void CanvasRenderingContext2D::putImageData(ImageData& data, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight)
2040 {
2041     putImageData(data, ImageBuffer::LogicalCoordinateSystem, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight);
2042 }
2043
2044 void CanvasRenderingContext2D::webkitPutImageDataHD(ImageData& data, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight)
2045 {
2046     putImageData(data, ImageBuffer::BackingStoreCoordinateSystem, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight);
2047 }
2048
2049 void CanvasRenderingContext2D::drawFocusIfNeeded(Element& element)
2050 {
2051     drawFocusIfNeededInternal(m_path, element);
2052 }
2053
2054 void CanvasRenderingContext2D::drawFocusIfNeeded(DOMPath& path, Element& element)
2055 {
2056     drawFocusIfNeededInternal(path.path(), element);
2057 }
2058
2059 void CanvasRenderingContext2D::drawFocusIfNeededInternal(const Path& path, Element& element)
2060 {
2061     auto* context = drawingContext();
2062     if (!element.focused() || !state().hasInvertibleTransform || path.isEmpty() || !element.isDescendantOf(canvas()) || !context)
2063         return;
2064     context->drawFocusRing(path, 1, 1, RenderTheme::focusRingColor());
2065 }
2066
2067 void CanvasRenderingContext2D::putImageData(ImageData& data, ImageBuffer::CoordinateSystem coordinateSystem, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight)
2068 {
2069     ImageBuffer* buffer = canvas().buffer();
2070     if (!buffer)
2071         return;
2072
2073     if (dirtyWidth < 0) {
2074         dirtyX += dirtyWidth;
2075         dirtyWidth = -dirtyWidth;
2076     }
2077
2078     if (dirtyHeight < 0) {
2079         dirtyY += dirtyHeight;
2080         dirtyHeight = -dirtyHeight;
2081     }
2082
2083     FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
2084     clipRect.intersect(IntRect(0, 0, data.width(), data.height()));
2085     IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy));
2086     IntRect destRect = enclosingIntRect(clipRect);
2087     destRect.move(destOffset);
2088     destRect.intersect(IntRect(IntPoint(), coordinateSystem == ImageBuffer::LogicalCoordinateSystem ? buffer->logicalSize() : buffer->internalSize()));
2089     if (destRect.isEmpty())
2090         return;
2091     IntRect sourceRect(destRect);
2092     sourceRect.move(-destOffset);
2093     sourceRect.intersect(IntRect(0, 0, data.width(), data.height()));
2094
2095     if (!sourceRect.isEmpty())
2096         buffer->putByteArray(Unmultiplied, data.data(), IntSize(data.width(), data.height()), sourceRect, IntPoint(destOffset), coordinateSystem);
2097
2098     didDraw(destRect, CanvasDidDrawApplyNone); // ignore transform, shadow and clip
2099 }
2100
2101 String CanvasRenderingContext2D::font() const
2102 {
2103     if (!state().font.realized())
2104         return defaultFont;
2105
2106     StringBuilder serializedFont;
2107     const auto& fontDescription = state().font.fontDescription();
2108
2109     if (fontDescription.italic())
2110         serializedFont.appendLiteral("italic ");
2111     if (fontDescription.variantCaps() == FontVariantCaps::Small)
2112         serializedFont.appendLiteral("small-caps ");
2113
2114     serializedFont.appendNumber(fontDescription.computedPixelSize());
2115     serializedFont.appendLiteral("px");
2116
2117     for (unsigned i = 0; i < fontDescription.familyCount(); ++i) {
2118         if (i)
2119             serializedFont.append(',');
2120         // FIXME: We should append family directly to serializedFont rather than building a temporary string.
2121         String family = fontDescription.familyAt(i);
2122         if (family.startsWith("-webkit-"))
2123             family = family.substring(8);
2124         if (family.contains(' '))
2125             family = makeString('"', family, '"');
2126
2127         serializedFont.append(' ');
2128         serializedFont.append(family);
2129     }
2130
2131     return serializedFont.toString();
2132 }
2133
2134 void CanvasRenderingContext2D::setFont(const String& newFont)
2135 {
2136     if (newFont == state().unparsedFont && state().font.realized())
2137         return;
2138
2139     auto parsedStyle = MutableStyleProperties::create();
2140     CSSParser::parseValue(parsedStyle, CSSPropertyFont, newFont, true, strictToCSSParserMode(!m_usesCSSCompatibilityParseMode));
2141     if (parsedStyle->isEmpty())
2142         return;
2143
2144     String fontValue = parsedStyle->getPropertyValue(CSSPropertyFont);
2145
2146     // According to http://lists.w3.org/Archives/Public/public-html/2009Jul/0947.html,
2147     // the "inherit" and "initial" values must be ignored.
2148     if (fontValue == "inherit" || fontValue == "initial")
2149         return;
2150
2151     // The parse succeeded.
2152     String newFontSafeCopy(newFont); // Create a string copy since newFont can be deleted inside realizeSaves.
2153     realizeSaves();
2154     modifiableState().unparsedFont = newFontSafeCopy;
2155
2156     // Map the <canvas> font into the text style. If the font uses keywords like larger/smaller, these will work
2157     // relative to the canvas.
2158     auto newStyle = RenderStyle::createPtr();
2159
2160     Document& document = canvas().document();
2161     document.updateStyleIfNeeded();
2162
2163     if (auto* computedStyle = canvas().computedStyle())
2164         newStyle->setFontDescription(computedStyle->fontDescription());
2165     else {
2166         FontCascadeDescription defaultFontDescription;
2167         defaultFontDescription.setOneFamily(defaultFontFamily);
2168         defaultFontDescription.setSpecifiedSize(defaultFontSize);
2169         defaultFontDescription.setComputedSize(defaultFontSize);
2170
2171         newStyle->setFontDescription(defaultFontDescription);
2172     }
2173
2174     newStyle->fontCascade().update(&document.fontSelector());
2175
2176     // Now map the font property longhands into the style.
2177     StyleResolver& styleResolver = canvas().styleResolver();
2178     styleResolver.applyPropertyToStyle(CSSPropertyFontFamily, parsedStyle->getPropertyCSSValue(CSSPropertyFontFamily).get(), WTFMove(newStyle));
2179     styleResolver.applyPropertyToCurrentStyle(CSSPropertyFontStyle, parsedStyle->getPropertyCSSValue(CSSPropertyFontStyle).get());
2180     styleResolver.applyPropertyToCurrentStyle(CSSPropertyFontVariantCaps, parsedStyle->getPropertyCSSValue(CSSPropertyFontVariantCaps).get());
2181     styleResolver.applyPropertyToCurrentStyle(CSSPropertyFontWeight, parsedStyle->getPropertyCSSValue(CSSPropertyFontWeight).get());
2182
2183     // As described in BUG66291, setting font-size and line-height on a font may entail a CSSPrimitiveValue::computeLengthDouble call,
2184     // which assumes the fontMetrics are available for the affected font, otherwise a crash occurs (see http://trac.webkit.org/changeset/96122).
2185     // The updateFont() calls below update the fontMetrics and ensure the proper setting of font-size and line-height.
2186     styleResolver.updateFont();
2187     styleResolver.applyPropertyToCurrentStyle(CSSPropertyFontSize, parsedStyle->getPropertyCSSValue(CSSPropertyFontSize).get());
2188     styleResolver.updateFont();
2189     styleResolver.applyPropertyToCurrentStyle(CSSPropertyLineHeight, parsedStyle->getPropertyCSSValue(CSSPropertyLineHeight).get());
2190
2191     modifiableState().font.initialize(document.fontSelector(), *styleResolver.style());
2192 }
2193
2194 String CanvasRenderingContext2D::textAlign() const
2195 {
2196     return textAlignName(state().textAlign);
2197 }
2198
2199 void CanvasRenderingContext2D::setTextAlign(const String& s)
2200 {
2201     TextAlign align;
2202     if (!parseTextAlign(s, align))
2203         return;
2204     if (state().textAlign == align)
2205         return;
2206     realizeSaves();
2207     modifiableState().textAlign = align;
2208 }
2209
2210 String CanvasRenderingContext2D::textBaseline() const
2211 {
2212     return textBaselineName(state().textBaseline);
2213 }
2214
2215 void CanvasRenderingContext2D::setTextBaseline(const String& s)
2216 {
2217     TextBaseline baseline;
2218     if (!parseTextBaseline(s, baseline))
2219         return;
2220     if (state().textBaseline == baseline)
2221         return;
2222     realizeSaves();
2223     modifiableState().textBaseline = baseline;
2224 }
2225
2226 inline TextDirection CanvasRenderingContext2D::toTextDirection(Direction direction, const RenderStyle** computedStyle) const
2227 {
2228     auto* style = (computedStyle || direction == Direction::Inherit) ? canvas().computedStyle() : nullptr;
2229     if (computedStyle)
2230         *computedStyle = style;
2231     switch (direction) {
2232     case Direction::Inherit:
2233         return style ? style->direction() : LTR;
2234     case Direction::RTL:
2235         return RTL;
2236     case Direction::LTR:
2237         return LTR;
2238     }
2239     ASSERT_NOT_REACHED();
2240     return LTR;
2241 }
2242
2243 String CanvasRenderingContext2D::direction() const
2244 {
2245     if (state().direction == Direction::Inherit)
2246         canvas().document().updateStyleIfNeeded();
2247     return toTextDirection(state().direction) == RTL ? ASCIILiteral("rtl") : ASCIILiteral("ltr");
2248 }
2249
2250 void CanvasRenderingContext2D::setDirection(const String& directionString)
2251 {
2252     Direction direction;
2253     if (directionString == "inherit")
2254         direction = Direction::Inherit;
2255     else if (directionString == "rtl")
2256         direction = Direction::RTL;
2257     else if (directionString == "ltr")
2258         direction = Direction::LTR;
2259     else
2260         return;
2261
2262     if (state().direction == direction)
2263         return;
2264
2265     realizeSaves();
2266     modifiableState().direction = direction;
2267 }
2268
2269 void CanvasRenderingContext2D::fillText(const String& text, float x, float y, std::optional<float> maxWidth)
2270 {
2271     drawTextInternal(text, x, y, true, maxWidth);
2272 }
2273
2274 void CanvasRenderingContext2D::strokeText(const String& text, float x, float y, std::optional<float> maxWidth)
2275 {
2276     drawTextInternal(text, x, y, false, maxWidth);
2277 }
2278
2279 static inline bool isSpaceThatNeedsReplacing(UChar c)
2280 {
2281     // According to specification all space characters should be replaced with 0x0020 space character.
2282     // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#text-preparation-algorithm
2283     // The space characters according to specification are : U+0020, U+0009, U+000A, U+000C, and U+000D.
2284     // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#space-character
2285     // This function returns true for 0x000B also, so that this is backward compatible.
2286     // Otherwise, the test LayoutTests/canvas/philip/tests/2d.text.draw.space.collapse.space.html will fail
2287     return c == 0x0009 || c == 0x000A || c == 0x000B || c == 0x000C || c == 0x000D;
2288 }
2289
2290 static void normalizeSpaces(String& text)
2291 {
2292     size_t i = text.find(isSpaceThatNeedsReplacing);
2293     if (i == notFound)
2294         return;
2295
2296     unsigned textLength = text.length();
2297     Vector<UChar> charVector(textLength);
2298     StringView(text).getCharactersWithUpconvert(charVector.data());
2299
2300     charVector[i++] = ' ';
2301
2302     for (; i < textLength; ++i) {
2303         if (isSpaceThatNeedsReplacing(charVector[i]))
2304             charVector[i] = ' ';
2305     }
2306     text = String::adopt(WTFMove(charVector));
2307 }
2308
2309 Ref<TextMetrics> CanvasRenderingContext2D::measureText(const String& text)
2310 {
2311     Ref<TextMetrics> metrics = TextMetrics::create();
2312
2313     String normalizedText = text;
2314     normalizeSpaces(normalizedText);
2315
2316     metrics->setWidth(fontProxy().width(TextRun(normalizedText)));
2317
2318     return metrics;
2319 }
2320
2321 void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, float y, bool fill, std::optional<float> maxWidth)
2322 {
2323     auto& fontProxy = this->fontProxy();
2324     const auto& fontMetrics = fontProxy.fontMetrics();
2325
2326     auto* c = drawingContext();
2327     if (!c)
2328         return;
2329     if (!state().hasInvertibleTransform)
2330         return;
2331     if (!std::isfinite(x) | !std::isfinite(y))
2332         return;
2333     if (maxWidth && (!std::isfinite(maxWidth.value()) || maxWidth.value() <= 0))
2334         return;
2335
2336     // If gradient size is zero, then paint nothing.
2337     auto* gradient = c->strokeGradient();
2338     if (!fill && gradient && gradient->isZeroSize())
2339         return;
2340
2341     gradient = c->fillGradient();
2342     if (fill && gradient && gradient->isZeroSize())
2343         return;
2344
2345     String normalizedText = text;
2346     normalizeSpaces(normalizedText);
2347
2348     // FIXME: Need to turn off font smoothing.
2349
2350     const RenderStyle* computedStyle;
2351     auto direction = toTextDirection(state().direction, &computedStyle);
2352     bool isRTL = direction == RTL;
2353     bool override = computedStyle ? isOverride(computedStyle->unicodeBidi()) : false;
2354
2355     TextRun textRun(normalizedText, 0, 0, AllowTrailingExpansion, direction, override, true);
2356     // Draw the item text at the correct point.
2357     FloatPoint location(x, y);
2358     switch (state().textBaseline) {
2359     case TopTextBaseline:
2360     case HangingTextBaseline:
2361         location.setY(y + fontMetrics.ascent());
2362         break;
2363     case BottomTextBaseline:
2364     case IdeographicTextBaseline:
2365         location.setY(y - fontMetrics.descent());
2366         break;
2367     case MiddleTextBaseline:
2368         location.setY(y - fontMetrics.descent() + fontMetrics.height() / 2);
2369         break;
2370     case AlphabeticTextBaseline:
2371     default:
2372          // Do nothing.
2373         break;
2374     }
2375
2376     float fontWidth = fontProxy.width(TextRun(normalizedText, 0, 0, AllowTrailingExpansion, direction, override));
2377
2378     bool useMaxWidth = maxWidth && maxWidth.value() < fontWidth;
2379     float width = useMaxWidth ? maxWidth.value() : fontWidth;
2380
2381     auto align = state().textAlign;
2382     if (align == StartTextAlign)
2383         align = isRTL ? RightTextAlign : LeftTextAlign;
2384     else if (align == EndTextAlign)
2385         align = isRTL ? LeftTextAlign : RightTextAlign;
2386
2387     switch (align) {
2388     case CenterTextAlign:
2389         location.setX(location.x() - width / 2);
2390         break;
2391     case RightTextAlign:
2392         location.setX(location.x() - width);
2393         break;
2394     default:
2395         break;
2396     }
2397
2398     // The slop built in to this mask rect matches the heuristic used in FontCGWin.cpp for GDI text.
2399     FloatRect textRect = FloatRect(location.x() - fontMetrics.height() / 2, location.y() - fontMetrics.ascent() - fontMetrics.lineGap(),
2400         width + fontMetrics.height(), fontMetrics.lineSpacing());
2401     if (!fill)
2402         inflateStrokeRect(textRect);
2403
2404 #if USE(CG)
2405     const CanvasStyle& drawStyle = fill ? state().fillStyle : state().strokeStyle;
2406     if (drawStyle.canvasGradient() || drawStyle.canvasPattern()) {
2407         IntRect maskRect = enclosingIntRect(textRect);
2408
2409         // If we have a shadow, we need to draw it before the mask operation.
2410         // Follow a procedure similar to paintTextWithShadows in TextPainter.
2411
2412         if (shouldDrawShadows()) {
2413             GraphicsContextStateSaver stateSaver(*c);
2414
2415             FloatSize offset(0, 2 * maskRect.height());
2416
2417             FloatSize shadowOffset;
2418             float shadowRadius;
2419             Color shadowColor;
2420             c->getShadow(shadowOffset, shadowRadius, shadowColor);
2421
2422             FloatRect shadowRect(maskRect);
2423             shadowRect.inflate(shadowRadius * 1.4);
2424             shadowRect.move(shadowOffset * -1);
2425             c->clip(shadowRect);
2426
2427             shadowOffset += offset;
2428
2429             c->setLegacyShadow(shadowOffset, shadowRadius, shadowColor);
2430
2431             if (fill)
2432                 c->setFillColor(Color::black);
2433             else
2434                 c->setStrokeColor(Color::black);
2435
2436             fontProxy.drawBidiText(*c, textRun, location + offset, FontCascade::UseFallbackIfFontNotReady);
2437         }
2438
2439         auto maskImage = ImageBuffer::createCompatibleBuffer(maskRect.size(), ColorSpaceSRGB, *c);
2440         if (!maskImage)
2441             return;
2442
2443         auto& maskImageContext = maskImage->context();
2444
2445         if (fill)
2446             maskImageContext.setFillColor(Color::black);
2447         else {
2448             maskImageContext.setStrokeColor(Color::black);
2449             maskImageContext.setStrokeThickness(c->strokeThickness());
2450         }
2451
2452         maskImageContext.setTextDrawingMode(fill ? TextModeFill : TextModeStroke);
2453
2454         if (useMaxWidth) {
2455             maskImageContext.translate(location.x() - maskRect.x(), location.y() - maskRect.y());
2456             // We draw when fontWidth is 0 so compositing operations (eg, a "copy" op) still work.
2457             maskImageContext.scale(FloatSize((fontWidth > 0 ? (width / fontWidth) : 0), 1));
2458             fontProxy.drawBidiText(maskImageContext, textRun, FloatPoint(0, 0), FontCascade::UseFallbackIfFontNotReady);
2459         } else {
2460             maskImageContext.translate(-maskRect.x(), -maskRect.y());
2461             fontProxy.drawBidiText(maskImageContext, textRun, location, FontCascade::UseFallbackIfFontNotReady);
2462         }
2463
2464         GraphicsContextStateSaver stateSaver(*c);
2465         c->clipToImageBuffer(*maskImage, maskRect);
2466         drawStyle.applyFillColor(*c);
2467         c->fillRect(maskRect);
2468         return;
2469     }
2470 #endif
2471
2472     c->setTextDrawingMode(fill ? TextModeFill : TextModeStroke);
2473
2474     GraphicsContextStateSaver stateSaver(*c);
2475     if (useMaxWidth) {
2476         c->translate(location.x(), location.y());
2477         // We draw when fontWidth is 0 so compositing operations (eg, a "copy" op) still work.
2478         c->scale(FloatSize((fontWidth > 0 ? (width / fontWidth) : 0), 1));
2479         location = FloatPoint();
2480     }
2481
2482     if (isFullCanvasCompositeMode(state().globalComposite)) {
2483         beginCompositeLayer();
2484         fontProxy.drawBidiText(*c, textRun, location, FontCascade::UseFallbackIfFontNotReady);
2485         endCompositeLayer();
2486         didDrawEntireCanvas();
2487     } else if (state().globalComposite == CompositeCopy) {
2488         clearCanvas();
2489         fontProxy.drawBidiText(*c, textRun, location, FontCascade::UseFallbackIfFontNotReady);
2490         didDrawEntireCanvas();
2491     } else {
2492         fontProxy.drawBidiText(*c, textRun, location, FontCascade::UseFallbackIfFontNotReady);
2493         didDraw(textRect);
2494     }
2495 }
2496
2497 void CanvasRenderingContext2D::inflateStrokeRect(FloatRect& rect) const
2498 {
2499     // Fast approximation of the stroke's bounding rect.
2500     // This yields a slightly oversized rect but is very fast
2501     // compared to Path::strokeBoundingRect().
2502     static const float root2 = sqrtf(2);
2503     float delta = state().lineWidth / 2;
2504     if (state().lineJoin == MiterJoin)
2505         delta *= state().miterLimit;
2506     else if (state().lineCap == SquareCap)
2507         delta *= root2;
2508     rect.inflate(delta);
2509 }
2510
2511 auto CanvasRenderingContext2D::fontProxy() -> const FontProxy&
2512 {
2513     canvas().document().updateStyleIfNeeded();
2514     if (!state().font.realized())
2515         setFont(state().unparsedFont);
2516     return state().font;
2517 }
2518
2519 #if ENABLE(ACCELERATED_2D_CANVAS)
2520
2521 PlatformLayer* CanvasRenderingContext2D::platformLayer() const
2522 {
2523     return canvas().buffer() ? canvas().buffer()->platformLayer() : nullptr;
2524 }
2525
2526 #endif
2527
2528 static inline InterpolationQuality smoothingToInterpolationQuality(CanvasRenderingContext2D::ImageSmoothingQuality quality)
2529 {
2530     switch (quality) {
2531     case CanvasRenderingContext2D::ImageSmoothingQuality::Low:
2532         return InterpolationLow;
2533     case CanvasRenderingContext2D::ImageSmoothingQuality::Medium:
2534         return InterpolationMedium;
2535     case CanvasRenderingContext2D::ImageSmoothingQuality::High:
2536         return InterpolationHigh;
2537     }
2538
2539     ASSERT_NOT_REACHED();
2540     return InterpolationLow;
2541 };
2542
2543 auto CanvasRenderingContext2D::imageSmoothingQuality() const -> ImageSmoothingQuality
2544 {
2545     return state().imageSmoothingQuality;
2546 }
2547
2548 void CanvasRenderingContext2D::setImageSmoothingQuality(ImageSmoothingQuality quality)
2549 {
2550     if (quality == state().imageSmoothingQuality)
2551         return;
2552
2553     realizeSaves();
2554     modifiableState().imageSmoothingQuality = quality;
2555
2556     if (!state().imageSmoothingEnabled)
2557         return;
2558
2559     if (auto* context = drawingContext())
2560         context->setImageInterpolationQuality(smoothingToInterpolationQuality(quality));
2561 }
2562
2563 bool CanvasRenderingContext2D::imageSmoothingEnabled() const
2564 {
2565     return state().imageSmoothingEnabled;
2566 }
2567
2568 void CanvasRenderingContext2D::setImageSmoothingEnabled(bool enabled)
2569 {
2570     if (enabled == state().imageSmoothingEnabled)
2571         return;
2572
2573     realizeSaves();
2574     modifiableState().imageSmoothingEnabled = enabled;
2575     auto* c = drawingContext();
2576     if (c)
2577         c->setImageInterpolationQuality(enabled ? smoothingToInterpolationQuality(state().imageSmoothingQuality) : InterpolationNone);
2578 }
2579
2580 } // namespace WebCore