[WTF] Add makeUnique<T>, which ensures T is fast-allocated, makeUnique / makeUniqueWi...
[WebKit-https.git] / Source / WebCore / html / HTMLCanvasElement.cpp
1 /*
2  * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
3  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
4  * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
26  */
27
28 #include "config.h"
29 #include "HTMLCanvasElement.h"
30
31 #include "Blob.h"
32 #include "BlobCallback.h"
33 #include "CanvasGradient.h"
34 #include "CanvasPattern.h"
35 #include "CanvasRenderingContext2D.h"
36 #include "Document.h"
37 #include "Frame.h"
38 #include "FrameLoaderClient.h"
39 #include "GPUBasedCanvasRenderingContext.h"
40 #include "GeometryUtilities.h"
41 #include "GraphicsContext.h"
42 #include "HTMLNames.h"
43 #include "HTMLParserIdioms.h"
44 #include "ImageBitmapRenderingContext.h"
45 #include "ImageBuffer.h"
46 #include "ImageData.h"
47 #include "InspectorInstrumentation.h"
48 #include "JSDOMConvertDictionary.h"
49 #include "MIMETypeRegistry.h"
50 #include "RenderElement.h"
51 #include "RenderHTMLCanvas.h"
52 #include "ResourceLoadObserver.h"
53 #include "RuntimeEnabledFeatures.h"
54 #include "ScriptController.h"
55 #include "Settings.h"
56 #include "StringAdaptors.h"
57 #include <JavaScriptCore/JSCInlines.h>
58 #include <JavaScriptCore/JSLock.h>
59 #include <math.h>
60 #include <wtf/IsoMallocInlines.h>
61 #include <wtf/RAMSize.h>
62 #include <wtf/text/StringBuilder.h>
63
64 #if ENABLE(MEDIA_STREAM)
65 #include "CanvasCaptureMediaStreamTrack.h"
66 #include "MediaStream.h"
67 #endif
68
69 #if ENABLE(WEBGL)
70 #include "WebGLContextAttributes.h"
71 #include "WebGLRenderingContext.h"
72 #endif
73
74 #if ENABLE(WEBGL2)
75 #include "WebGL2RenderingContext.h"
76 #endif
77
78 #if ENABLE(WEBGPU)
79 #include "GPUCanvasContext.h"
80 #endif
81
82 #if PLATFORM(COCOA)
83 #include "MediaSampleAVFObjC.h"
84 #include <pal/cf/CoreMediaSoftLink.h>
85 #endif
86
87 namespace WebCore {
88
89 WTF_MAKE_ISO_ALLOCATED_IMPL(HTMLCanvasElement);
90
91 using namespace PAL;
92 using namespace HTMLNames;
93
94 // These values come from the WhatWG/W3C HTML spec.
95 const int defaultWidth = 300;
96 const int defaultHeight = 150;
97
98 // Firefox limits width/height to 32767 pixels, but slows down dramatically before it
99 // reaches that limit. We limit by area instead, giving us larger maximum dimensions,
100 // in exchange for a smaller maximum canvas size. The maximum canvas size is in device pixels.
101 #if PLATFORM(IOS_FAMILY)
102 const unsigned maxCanvasArea = 4096 * 4096;
103 #else
104 const unsigned maxCanvasArea = 16384 * 16384;
105 #endif
106
107 #if USE(CG)
108 // FIXME: It seems strange that the default quality is not the one that is literally named "default".
109 // Should fix names to make this easier to understand, or write an excellent comment here explaining why not.
110 const InterpolationQuality defaultInterpolationQuality = InterpolationLow;
111 #else
112 const InterpolationQuality defaultInterpolationQuality = InterpolationDefault;
113 #endif
114
115 static size_t activePixelMemory = 0;
116
117 HTMLCanvasElement::HTMLCanvasElement(const QualifiedName& tagName, Document& document)
118     : HTMLElement(tagName, document)
119     , m_size(defaultWidth, defaultHeight)
120 {
121     ASSERT(hasTagName(canvasTag));
122 }
123
124 Ref<HTMLCanvasElement> HTMLCanvasElement::create(Document& document)
125 {
126     return adoptRef(*new HTMLCanvasElement(canvasTag, document));
127 }
128
129 Ref<HTMLCanvasElement> HTMLCanvasElement::create(const QualifiedName& tagName, Document& document)
130 {
131     return adoptRef(*new HTMLCanvasElement(tagName, document));
132 }
133
134 static void removeFromActivePixelMemory(size_t pixelsReleased)
135 {
136     if (!pixelsReleased)
137         return;
138
139     if (pixelsReleased < activePixelMemory)
140         activePixelMemory -= pixelsReleased;
141     else
142         activePixelMemory = 0;
143 }
144     
145 HTMLCanvasElement::~HTMLCanvasElement()
146 {
147     notifyObserversCanvasDestroyed();
148
149     m_context = nullptr; // Ensure this goes away before the ImageBuffer.
150
151     releaseImageBufferAndContext();
152 }
153
154 void HTMLCanvasElement::parseAttribute(const QualifiedName& name, const AtomString& value)
155 {
156     if (name == widthAttr || name == heightAttr)
157         reset();
158     HTMLElement::parseAttribute(name, value);
159 }
160
161 RenderPtr<RenderElement> HTMLCanvasElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition& insertionPosition)
162 {
163     RefPtr<Frame> frame = document().frame();
164     if (frame && frame->script().canExecuteScripts(NotAboutToExecuteScript))
165         return createRenderer<RenderHTMLCanvas>(*this, WTFMove(style));
166     return HTMLElement::createElementRenderer(WTFMove(style), insertionPosition);
167 }
168
169 bool HTMLCanvasElement::canContainRangeEndPoint() const
170 {
171     return false;
172 }
173
174 bool HTMLCanvasElement::canStartSelection() const
175 {
176     return false;
177 }
178
179 ExceptionOr<void> HTMLCanvasElement::setHeight(unsigned value)
180 {
181     if (m_context && m_context->isPlaceholder())
182         return Exception { InvalidStateError };
183     setAttributeWithoutSynchronization(heightAttr, AtomString::number(limitToOnlyHTMLNonNegative(value, defaultHeight)));
184     return { };
185 }
186
187 ExceptionOr<void> HTMLCanvasElement::setWidth(unsigned value)
188 {
189     if (m_context && m_context->isPlaceholder())
190         return Exception { InvalidStateError };
191     setAttributeWithoutSynchronization(widthAttr, AtomString::number(limitToOnlyHTMLNonNegative(value, defaultWidth)));
192     return { };
193 }
194
195 static inline size_t maxActivePixelMemory()
196 {
197     static size_t maxPixelMemory;
198     static std::once_flag onceFlag;
199     std::call_once(onceFlag, [] {
200 #if PLATFORM(IOS_FAMILY)
201         maxPixelMemory = ramSize() / 4;
202 #else
203         maxPixelMemory = std::max(ramSize() / 4, 2151 * MB);
204 #endif
205     });
206     return maxPixelMemory;
207 }
208
209 ExceptionOr<Optional<RenderingContext>> HTMLCanvasElement::getContext(JSC::ExecState& state, const String& contextId, Vector<JSC::Strong<JSC::Unknown>>&& arguments)
210 {
211     if (m_context) {
212         if (m_context->isPlaceholder())
213             return Exception { InvalidStateError };
214
215         if (m_context->is2d()) {
216             if (!is2dType(contextId))
217                 return Optional<RenderingContext> { WTF::nullopt };
218             return Optional<RenderingContext> { RefPtr<CanvasRenderingContext2D> { &downcast<CanvasRenderingContext2D>(*m_context) } };
219         }
220
221         if (m_context->isBitmapRenderer()) {
222             if (!isBitmapRendererType(contextId))
223                 return Optional<RenderingContext> { WTF::nullopt };
224             return Optional<RenderingContext> { RefPtr<ImageBitmapRenderingContext> { &downcast<ImageBitmapRenderingContext>(*m_context) } };
225         }
226
227 #if ENABLE(WEBGL)
228         if (m_context->isWebGL()) {
229             if (!isWebGLType(contextId))
230                 return Optional<RenderingContext> { WTF::nullopt };
231             if (is<WebGLRenderingContext>(*m_context))
232                 return Optional<RenderingContext> { RefPtr<WebGLRenderingContext> { &downcast<WebGLRenderingContext>(*m_context) } };
233 #if ENABLE(WEBGL2)
234             ASSERT(is<WebGL2RenderingContext>(*m_context));
235             return Optional<RenderingContext> { RefPtr<WebGL2RenderingContext> { &downcast<WebGL2RenderingContext>(*m_context) } };
236 #endif
237         }
238 #endif
239
240 #if ENABLE(WEBGPU)
241         if (m_context->isWebGPU()) {
242             if (!isWebGPUType(contextId))
243                 return Optional<RenderingContext> { WTF::nullopt };
244             return Optional<RenderingContext> { RefPtr<GPUCanvasContext> { &downcast<GPUCanvasContext>(*m_context) } };
245         }
246 #endif
247
248         ASSERT_NOT_REACHED();
249         return Optional<RenderingContext> { WTF::nullopt };
250     }
251
252     if (is2dType(contextId)) {
253         auto context = createContext2d(contextId);
254         if (!context)
255             return Optional<RenderingContext> { WTF::nullopt };
256         return Optional<RenderingContext> { RefPtr<CanvasRenderingContext2D> { context } };
257     }
258
259     if (isBitmapRendererType(contextId)) {
260         auto scope = DECLARE_THROW_SCOPE(state.vm());
261         auto attributes = convert<IDLDictionary<ImageBitmapRenderingContextSettings>>(state, !arguments.isEmpty() ? arguments[0].get() : JSC::jsUndefined());
262         RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
263
264         auto context = createContextBitmapRenderer(contextId, WTFMove(attributes));
265         if (!context)
266             return Optional<RenderingContext> { WTF::nullopt };
267         return Optional<RenderingContext> { RefPtr<ImageBitmapRenderingContext> { context } };
268     }
269
270 #if ENABLE(WEBGL)
271     if (isWebGLType(contextId)) {
272         auto scope = DECLARE_THROW_SCOPE(state.vm());
273         auto attributes = convert<IDLDictionary<WebGLContextAttributes>>(state, !arguments.isEmpty() ? arguments[0].get() : JSC::jsUndefined());
274         RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });
275
276         auto context = createContextWebGL(contextId, WTFMove(attributes));
277         if (!context)
278             return Optional<RenderingContext> { WTF::nullopt };
279
280         if (is<WebGLRenderingContext>(*context))
281             return Optional<RenderingContext> { RefPtr<WebGLRenderingContext> { &downcast<WebGLRenderingContext>(*context) } };
282 #if ENABLE(WEBGL2)
283         ASSERT(is<WebGL2RenderingContext>(*context));
284         return Optional<RenderingContext> { RefPtr<WebGL2RenderingContext> { &downcast<WebGL2RenderingContext>(*context) } };
285 #endif
286     }
287 #endif
288
289 #if ENABLE(WEBGPU)
290     if (isWebGPUType(contextId)) {
291         auto context = createContextWebGPU(contextId);
292         if (!context)
293             return Optional<RenderingContext> { WTF::nullopt };
294         return Optional<RenderingContext> { RefPtr<GPUCanvasContext> { context } };
295     }
296 #endif
297
298     return Optional<RenderingContext> { WTF::nullopt };
299 }
300
301 CanvasRenderingContext* HTMLCanvasElement::getContext(const String& type)
302 {
303     if (HTMLCanvasElement::is2dType(type))
304         return getContext2d(type);
305
306     if (HTMLCanvasElement::isBitmapRendererType(type))
307         return getContextBitmapRenderer(type);
308
309 #if ENABLE(WEBGL)
310     if (HTMLCanvasElement::isWebGLType(type))
311         return getContextWebGL(type);
312 #endif
313
314 #if ENABLE(WEBGPU)
315     if (HTMLCanvasElement::isWebGPUType(type))
316         return getContextWebGPU(type);
317 #endif
318
319     return nullptr;
320 }
321
322 bool HTMLCanvasElement::is2dType(const String& type)
323 {
324     return type == "2d";
325 }
326
327 CanvasRenderingContext2D* HTMLCanvasElement::createContext2d(const String& type)
328 {
329     ASSERT_UNUSED(HTMLCanvasElement::is2dType(type), type);
330     ASSERT(!m_context);
331
332     // Make sure we don't use more pixel memory than the system can support.
333     size_t requestedPixelMemory = 4 * width() * height();
334     if (activePixelMemory + requestedPixelMemory > maxActivePixelMemory()) {
335         StringBuilder stringBuilder;
336         stringBuilder.appendLiteral("Total canvas memory use exceeds the maximum limit (");
337         stringBuilder.appendNumber(maxActivePixelMemory() / 1024 / 1024);
338         stringBuilder.appendLiteral(" MB).");
339         document().addConsoleMessage(MessageSource::JS, MessageLevel::Warning, stringBuilder.toString());
340         return nullptr;
341     }
342
343     m_context = CanvasRenderingContext2D::create(*this, document().inQuirksMode());
344
345     downcast<CanvasRenderingContext2D>(*m_context).setUsesDisplayListDrawing(m_usesDisplayListDrawing);
346     downcast<CanvasRenderingContext2D>(*m_context).setTracksDisplayListReplay(m_tracksDisplayListReplay);
347
348 #if USE(IOSURFACE_CANVAS_BACKING_STORE) || ENABLE(ACCELERATED_2D_CANVAS)
349     // Need to make sure a RenderLayer and compositing layer get created for the Canvas.
350     invalidateStyleAndLayerComposition();
351 #endif
352
353     return static_cast<CanvasRenderingContext2D*>(m_context.get());
354 }
355
356 CanvasRenderingContext2D* HTMLCanvasElement::getContext2d(const String& type)
357 {
358     ASSERT_UNUSED(HTMLCanvasElement::is2dType(type), type);
359
360     if (m_context && !m_context->is2d())
361         return nullptr;
362
363     if (!m_context)
364         return createContext2d(type);
365     return static_cast<CanvasRenderingContext2D*>(m_context.get());
366 }
367
368 #if ENABLE(WEBGL)
369
370 static bool requiresAcceleratedCompositingForWebGL()
371 {
372 #if PLATFORM(GTK) || PLATFORM(WIN_CAIRO)
373     return false;
374 #else
375     return true;
376 #endif
377
378 }
379 static bool shouldEnableWebGL(const Settings& settings)
380 {
381     if (!settings.webGLEnabled())
382         return false;
383
384     if (!requiresAcceleratedCompositingForWebGL())
385         return true;
386
387     return settings.acceleratedCompositingEnabled();
388 }
389
390 bool HTMLCanvasElement::isWebGLType(const String& type)
391 {
392     // Retain support for the legacy "webkit-3d" name.
393     return type == "webgl" || type == "experimental-webgl"
394 #if ENABLE(WEBGL2)
395         || type == "webgl2"
396 #endif
397         || type == "webkit-3d";
398 }
399
400 WebGLRenderingContextBase* HTMLCanvasElement::createContextWebGL(const String& type, WebGLContextAttributes&& attrs)
401 {
402     ASSERT(HTMLCanvasElement::isWebGLType(type));
403     ASSERT(!m_context);
404
405     if (!shouldEnableWebGL(document().settings()))
406         return nullptr;
407
408     m_context = WebGLRenderingContextBase::create(*this, attrs, type);
409     if (m_context) {
410         // Need to make sure a RenderLayer and compositing layer get created for the Canvas.
411         invalidateStyleAndLayerComposition();
412     }
413
414     return downcast<WebGLRenderingContextBase>(m_context.get());
415 }
416
417 WebGLRenderingContextBase* HTMLCanvasElement::getContextWebGL(const String& type, WebGLContextAttributes&& attrs)
418 {
419     ASSERT(HTMLCanvasElement::isWebGLType(type));
420
421     if (!shouldEnableWebGL(document().settings()))
422         return nullptr;
423
424     if (m_context && !m_context->isWebGL())
425         return nullptr;
426
427     if (!m_context)
428         return createContextWebGL(type, WTFMove(attrs));
429     return &downcast<WebGLRenderingContextBase>(*m_context);
430 }
431
432 #endif // ENABLE(WEBGL)
433
434 #if ENABLE(WEBGPU)
435
436 bool HTMLCanvasElement::isWebGPUType(const String& type)
437 {
438     return type == "gpu";
439 }
440
441 GPUCanvasContext* HTMLCanvasElement::createContextWebGPU(const String& type)
442 {
443     ASSERT_UNUSED(type, HTMLCanvasElement::isWebGPUType(type));
444     ASSERT(!m_context);
445
446     if (!RuntimeEnabledFeatures::sharedFeatures().webGPUEnabled())
447         return nullptr;
448
449     m_context = GPUCanvasContext::create(*this);
450     if (m_context) {
451         // Need to make sure a RenderLayer and compositing layer get created for the Canvas.
452         invalidateStyleAndLayerComposition();
453     }
454
455     return static_cast<GPUCanvasContext*>(m_context.get());
456 }
457
458 GPUCanvasContext* HTMLCanvasElement::getContextWebGPU(const String& type)
459 {
460     ASSERT_UNUSED(type, HTMLCanvasElement::isWebGPUType(type));
461
462     if (!RuntimeEnabledFeatures::sharedFeatures().webGPUEnabled())
463         return nullptr;
464
465     if (m_context && !m_context->isWebGPU())
466         return nullptr;
467
468     if (!m_context)
469         return createContextWebGPU(type);
470     return static_cast<GPUCanvasContext*>(m_context.get());
471 }
472
473 #endif // ENABLE(WEBGPU)
474
475 bool HTMLCanvasElement::isBitmapRendererType(const String& type)
476 {
477     return type == "bitmaprenderer";
478 }
479
480 ImageBitmapRenderingContext* HTMLCanvasElement::createContextBitmapRenderer(const String& type, ImageBitmapRenderingContextSettings&& settings)
481 {
482     ASSERT_UNUSED(type, HTMLCanvasElement::isBitmapRendererType(type));
483     ASSERT(!m_context);
484
485     m_context = ImageBitmapRenderingContext::create(*this, WTFMove(settings));
486
487 #if USE(IOSURFACE_CANVAS_BACKING_STORE) || ENABLE(ACCELERATED_2D_CANVAS)
488     // Need to make sure a RenderLayer and compositing layer get created for the Canvas.
489     invalidateStyleAndLayerComposition();
490 #endif
491
492     return static_cast<ImageBitmapRenderingContext*>(m_context.get());
493 }
494
495 ImageBitmapRenderingContext* HTMLCanvasElement::getContextBitmapRenderer(const String& type, ImageBitmapRenderingContextSettings&& settings)
496 {
497     ASSERT_UNUSED(type, HTMLCanvasElement::isBitmapRendererType(type));
498     if (!m_context)
499         return createContextBitmapRenderer(type, WTFMove(settings));
500     return static_cast<ImageBitmapRenderingContext*>(m_context.get());
501 }
502
503 void HTMLCanvasElement::didDraw(const FloatRect& rect)
504 {
505     clearCopiedImage();
506
507     FloatRect dirtyRect = rect;
508     if (auto* renderer = renderBox()) {
509         FloatRect destRect;
510         if (is<RenderReplaced>(renderer))
511             destRect = downcast<RenderReplaced>(renderer)->replacedContentRect();
512         else
513             destRect = renderer->contentBoxRect();
514
515         // Inflate dirty rect to cover antialiasing on image buffers.
516         if (drawingContext() && drawingContext()->shouldAntialias())
517             dirtyRect.inflate(1);
518
519         FloatRect r = mapRect(dirtyRect, FloatRect(0, 0, size().width(), size().height()), destRect);
520         r.intersect(destRect);
521
522         if (!r.isEmpty() && !m_dirtyRect.contains(r)) {
523             m_dirtyRect.unite(r);
524             renderer->repaintRectangle(enclosingIntRect(m_dirtyRect));
525         }
526     }
527     notifyObserversCanvasChanged(dirtyRect);
528 }
529
530 void HTMLCanvasElement::reset()
531 {
532     if (m_ignoreReset)
533         return;
534
535     bool hadImageBuffer = hasCreatedImageBuffer();
536
537     int w = limitToOnlyHTMLNonNegative(attributeWithoutSynchronization(widthAttr), defaultWidth);
538     int h = limitToOnlyHTMLNonNegative(attributeWithoutSynchronization(heightAttr), defaultHeight);
539
540     if (m_contextStateSaver) {
541         // Reset to the initial graphics context state.
542         m_contextStateSaver->restore();
543         m_contextStateSaver->save();
544     }
545
546     if (is<CanvasRenderingContext2D>(m_context.get()))
547         downcast<CanvasRenderingContext2D>(*m_context).reset();
548
549     IntSize oldSize = size();
550     IntSize newSize(w, h);
551     // If the size of an existing buffer matches, we can just clear it instead of reallocating.
552     // This optimization is only done for 2D canvases for now.
553     if (m_hasCreatedImageBuffer && oldSize == newSize && m_context && m_context->is2d()) {
554         if (!m_didClearImageBuffer)
555             clearImageBuffer();
556         return;
557     }
558
559     setSurfaceSize(newSize);
560
561     if (isGPUBased() && oldSize != size())
562         downcast<GPUBasedCanvasRenderingContext>(*m_context).reshape(width(), height());
563
564     auto renderer = this->renderer();
565     if (is<RenderHTMLCanvas>(renderer)) {
566         auto& canvasRenderer = downcast<RenderHTMLCanvas>(*renderer);
567         if (oldSize != size()) {
568             canvasRenderer.canvasSizeChanged();
569             if (canvasRenderer.hasAcceleratedCompositing())
570                 canvasRenderer.contentChanged(CanvasChanged);
571         }
572         if (hadImageBuffer)
573             canvasRenderer.repaint();
574     }
575
576     notifyObserversCanvasResized();
577 }
578
579 bool HTMLCanvasElement::paintsIntoCanvasBuffer() const
580 {
581     ASSERT(m_context);
582 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
583     if (m_context->is2d() || m_context->isBitmapRenderer())
584         return true;
585 #endif
586
587     if (!m_context->isAccelerated())
588         return true;
589
590     if (renderBox() && renderBox()->hasAcceleratedCompositing())
591         return false;
592
593     return true;
594 }
595
596
597 void HTMLCanvasElement::paint(GraphicsContext& context, const LayoutRect& r)
598 {
599     // Clear the dirty rect
600     m_dirtyRect = FloatRect();
601
602     if (!context.paintingDisabled()) {
603         bool shouldPaint = true;
604
605         if (m_context) {
606             shouldPaint = paintsIntoCanvasBuffer() || document().printing();
607             if (shouldPaint)
608                 m_context->paintRenderingResultsToCanvas();
609         }
610
611         if (shouldPaint) {
612             if (hasCreatedImageBuffer()) {
613                 if (m_presentedImage)
614                     context.drawImage(*m_presentedImage, snappedIntRect(r), renderer()->imageOrientation());
615                 else if (ImageBuffer* imageBuffer = buffer())
616                     context.drawImageBuffer(*imageBuffer, snappedIntRect(r));
617             }
618
619             if (isGPUBased())
620                 downcast<GPUBasedCanvasRenderingContext>(*m_context).markLayerComposited();
621         }
622     }
623
624     if (UNLIKELY(m_context && m_context->callTracingActive()))
625         InspectorInstrumentation::didFinishRecordingCanvasFrame(*m_context);
626 }
627
628 bool HTMLCanvasElement::isGPUBased() const
629 {
630     return m_context && m_context->isGPUBased();
631 }
632
633 void HTMLCanvasElement::makeRenderingResultsAvailable()
634 {
635     if (m_context)
636         m_context->paintRenderingResultsToCanvas();
637 }
638
639 void HTMLCanvasElement::makePresentationCopy()
640 {
641     if (!m_presentedImage) {
642         // The buffer contains the last presented data, so save a copy of it.
643         m_presentedImage = buffer()->copyImage(CopyBackingStore, PreserveResolution::Yes);
644     }
645 }
646
647 void HTMLCanvasElement::clearPresentationCopy()
648 {
649     m_presentedImage = nullptr;
650 }
651
652 void HTMLCanvasElement::releaseImageBufferAndContext()
653 {
654     m_contextStateSaver = nullptr;
655     setImageBuffer(nullptr);
656 }
657     
658 void HTMLCanvasElement::setSurfaceSize(const IntSize& size)
659 {
660     m_size = size;
661     m_hasCreatedImageBuffer = false;
662     releaseImageBufferAndContext();
663     clearCopiedImage();
664 }
665
666 static String toEncodingMimeType(const String& mimeType)
667 {
668     if (!MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType))
669         return "image/png"_s;
670     return mimeType.convertToASCIILowercase();
671 }
672
673 // https://html.spec.whatwg.org/multipage/canvas.html#a-serialisation-of-the-bitmap-as-a-file
674 static Optional<double> qualityFromJSValue(JSC::JSValue qualityValue)
675 {
676     if (!qualityValue.isNumber())
677         return WTF::nullopt;
678
679     double qualityNumber = qualityValue.asNumber();
680     if (qualityNumber < 0 || qualityNumber > 1)
681         return WTF::nullopt;
682
683     return qualityNumber;
684 }
685
686 ExceptionOr<UncachedString> HTMLCanvasElement::toDataURL(const String& mimeType, JSC::JSValue qualityValue)
687 {
688     if (!originClean())
689         return Exception { SecurityError };
690
691     if (m_size.isEmpty() || !buffer())
692         return UncachedString { "data:,"_s };
693     if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
694         ResourceLoadObserver::shared().logCanvasRead(document());
695
696     auto encodingMIMEType = toEncodingMimeType(mimeType);
697     auto quality = qualityFromJSValue(qualityValue);
698
699 #if USE(CG)
700     // Try to get ImageData first, as that may avoid lossy conversions.
701     if (auto imageData = getImageData())
702         return UncachedString { dataURL(*imageData, encodingMIMEType, quality) };
703 #endif
704
705     makeRenderingResultsAvailable();
706
707     return UncachedString { buffer()->toDataURL(encodingMIMEType, quality) };
708 }
709
710 ExceptionOr<UncachedString> HTMLCanvasElement::toDataURL(const String& mimeType)
711 {
712     return toDataURL(mimeType, { });
713 }
714
715 ExceptionOr<void> HTMLCanvasElement::toBlob(ScriptExecutionContext& context, Ref<BlobCallback>&& callback, const String& mimeType, JSC::JSValue qualityValue)
716 {
717     if (!originClean())
718         return Exception { SecurityError };
719
720     if (m_size.isEmpty() || !buffer()) {
721         callback->scheduleCallback(context, nullptr);
722         return { };
723     }
724     if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
725         ResourceLoadObserver::shared().logCanvasRead(document());
726
727     auto encodingMIMEType = toEncodingMimeType(mimeType);
728     auto quality = qualityFromJSValue(qualityValue);
729
730 #if USE(CG)
731     if (auto imageData = getImageData()) {
732         RefPtr<Blob> blob;
733         Vector<uint8_t> blobData = data(*imageData, encodingMIMEType, quality);
734         if (!blobData.isEmpty())
735             blob = Blob::create(context.sessionID(), WTFMove(blobData), encodingMIMEType);
736         callback->scheduleCallback(context, WTFMove(blob));
737         return { };
738     }
739 #endif
740
741     makeRenderingResultsAvailable();
742
743     RefPtr<Blob> blob;
744     Vector<uint8_t> blobData = buffer()->toData(encodingMIMEType, quality);
745     if (!blobData.isEmpty())
746         blob = Blob::create(context.sessionID(), WTFMove(blobData), encodingMIMEType);
747     callback->scheduleCallback(context, WTFMove(blob));
748     return { };
749 }
750
751 RefPtr<ImageData> HTMLCanvasElement::getImageData()
752 {
753 #if ENABLE(WEBGL)
754     if (is<WebGLRenderingContextBase>(m_context.get())) {
755         if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
756             ResourceLoadObserver::shared().logCanvasRead(document());
757         return downcast<WebGLRenderingContextBase>(*m_context).paintRenderingResultsToImageData();
758     }
759 #endif
760     return nullptr;
761 }
762
763 #if ENABLE(MEDIA_STREAM)
764
765 RefPtr<MediaSample> HTMLCanvasElement::toMediaSample()
766 {
767     auto* imageBuffer = buffer();
768     if (!imageBuffer)
769         return nullptr;
770     if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
771         ResourceLoadObserver::shared().logCanvasRead(document());
772
773 #if PLATFORM(COCOA)
774     makeRenderingResultsAvailable();
775     return MediaSampleAVFObjC::createImageSample(imageBuffer->toBGRAData(), width(), height());
776 #else
777     return nullptr;
778 #endif
779 }
780
781 ExceptionOr<Ref<MediaStream>> HTMLCanvasElement::captureStream(Document& document, Optional<double>&& frameRequestRate)
782 {
783     if (!originClean())
784         return Exception(SecurityError, "Canvas is tainted"_s);
785     if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
786         ResourceLoadObserver::shared().logCanvasRead(this->document());
787
788     if (frameRequestRate && frameRequestRate.value() < 0)
789         return Exception(NotSupportedError, "frameRequestRate is negative"_s);
790
791     auto track = CanvasCaptureMediaStreamTrack::create(document, *this, WTFMove(frameRequestRate));
792     auto stream =  MediaStream::create(document);
793     stream->addTrack(track);
794     return stream;
795 }
796 #endif
797
798 SecurityOrigin* HTMLCanvasElement::securityOrigin() const
799 {
800     return &document().securityOrigin();
801 }
802
803 bool HTMLCanvasElement::shouldAccelerate(const IntSize& size) const
804 {
805     auto& settings = document().settings();
806
807     auto area = size.area<RecordOverflow>();
808     if (area.hasOverflowed())
809         return false;
810
811     if (area > settings.maximumAccelerated2dCanvasSize())
812         return false;
813
814 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
815     return settings.canvasUsesAcceleratedDrawing();
816 #elif ENABLE(ACCELERATED_2D_CANVAS)
817     if (m_context && !m_context->is2d())
818         return false;
819
820     if (!settings.accelerated2dCanvasEnabled())
821         return false;
822
823     if (area < settings.minimumAccelerated2dCanvasSize())
824         return false;
825
826     return true;
827 #else
828     UNUSED_PARAM(size);
829     return false;
830 #endif
831 }
832
833 size_t HTMLCanvasElement::memoryCost() const
834 {
835     // memoryCost() may be invoked concurrently from a GC thread, and we need to be careful
836     // about what data we access here and how. We need to hold a lock to prevent m_imageBuffer
837     // from being changed while we access it.
838     auto locker = holdLock(m_imageBufferAssignmentLock);
839     if (!m_imageBuffer)
840         return 0;
841     return m_imageBuffer->memoryCost();
842 }
843
844 size_t HTMLCanvasElement::externalMemoryCost() const
845 {
846     // externalMemoryCost() may be invoked concurrently from a GC thread, and we need to be careful
847     // about what data we access here and how. We need to hold a lock to prevent m_imageBuffer
848     // from being changed while we access it.
849     auto locker = holdLock(m_imageBufferAssignmentLock);
850     if (!m_imageBuffer)
851         return 0;
852     return m_imageBuffer->externalMemoryCost();
853 }
854
855 void HTMLCanvasElement::setUsesDisplayListDrawing(bool usesDisplayListDrawing)
856 {
857     if (usesDisplayListDrawing == m_usesDisplayListDrawing)
858         return;
859     
860     m_usesDisplayListDrawing = usesDisplayListDrawing;
861
862     if (is<CanvasRenderingContext2D>(m_context.get()))
863         downcast<CanvasRenderingContext2D>(*m_context).setUsesDisplayListDrawing(m_usesDisplayListDrawing);
864 }
865
866 void HTMLCanvasElement::setTracksDisplayListReplay(bool tracksDisplayListReplay)
867 {
868     if (tracksDisplayListReplay == m_tracksDisplayListReplay)
869         return;
870
871     m_tracksDisplayListReplay = tracksDisplayListReplay;
872
873     if (is<CanvasRenderingContext2D>(m_context.get()))
874         downcast<CanvasRenderingContext2D>(*m_context).setTracksDisplayListReplay(m_tracksDisplayListReplay);
875 }
876
877 String HTMLCanvasElement::displayListAsText(DisplayList::AsTextFlags flags) const
878 {
879     if (is<CanvasRenderingContext2D>(m_context.get()))
880         return downcast<CanvasRenderingContext2D>(*m_context).displayListAsText(flags);
881
882     return String();
883 }
884
885 String HTMLCanvasElement::replayDisplayListAsText(DisplayList::AsTextFlags flags) const
886 {
887     if (is<CanvasRenderingContext2D>(m_context.get()))
888         return downcast<CanvasRenderingContext2D>(*m_context).replayDisplayListAsText(flags);
889
890     return String();
891 }
892
893 void HTMLCanvasElement::createImageBuffer() const
894 {
895     ASSERT(!m_imageBuffer);
896
897     m_hasCreatedImageBuffer = true;
898     m_didClearImageBuffer = true;
899
900     // Perform multiplication as floating point to avoid overflow
901     if (float(width()) * height() > maxCanvasArea) {
902         StringBuilder stringBuilder;
903         stringBuilder.appendLiteral("Canvas area exceeds the maximum limit (width * height > ");
904         stringBuilder.appendNumber(maxCanvasArea);
905         stringBuilder.appendLiteral(").");
906         document().addConsoleMessage(MessageSource::JS, MessageLevel::Warning, stringBuilder.toString());
907         return;
908     }
909     
910     // Make sure we don't use more pixel memory than the system can support.
911     size_t requestedPixelMemory = 4 * width() * height();
912     if (activePixelMemory + requestedPixelMemory > maxActivePixelMemory()) {
913         StringBuilder stringBuilder;
914         stringBuilder.appendLiteral("Total canvas memory use exceeds the maximum limit (");
915         stringBuilder.appendNumber(maxActivePixelMemory() / 1024 / 1024);
916         stringBuilder.appendLiteral(" MB).");
917         document().addConsoleMessage(MessageSource::JS, MessageLevel::Warning, stringBuilder.toString());
918         return;
919     }
920
921     if (!width() || !height())
922         return;
923
924     RenderingMode renderingMode = shouldAccelerate(size()) ? Accelerated : Unaccelerated;
925
926     auto hostWindow = (document().view() && document().view()->root()) ? document().view()->root()->hostWindow() : nullptr;
927     setImageBuffer(ImageBuffer::create(size(), renderingMode, 1, ColorSpaceSRGB, hostWindow));
928 }
929
930 void HTMLCanvasElement::setImageBuffer(std::unique_ptr<ImageBuffer>&& buffer) const
931 {
932     size_t previousMemoryCost = memoryCost();
933     removeFromActivePixelMemory(previousMemoryCost);
934
935     {
936         auto locker = holdLock(m_imageBufferAssignmentLock);
937         m_contextStateSaver = nullptr;
938         m_imageBuffer = WTFMove(buffer);
939     }
940
941     if (m_imageBuffer && m_size != m_imageBuffer->internalSize())
942         m_size = m_imageBuffer->internalSize();
943
944     size_t currentMemoryCost = memoryCost();
945     activePixelMemory += currentMemoryCost;
946
947     if (m_context && m_imageBuffer && previousMemoryCost != currentMemoryCost)
948         InspectorInstrumentation::didChangeCanvasMemory(*m_context);
949
950     if (!m_imageBuffer)
951         return;
952     m_imageBuffer->context().setShadowsIgnoreTransforms(true);
953     m_imageBuffer->context().setImageInterpolationQuality(defaultInterpolationQuality);
954     m_imageBuffer->context().setStrokeThickness(1);
955     m_contextStateSaver = makeUnique<GraphicsContextStateSaver>(m_imageBuffer->context());
956
957     JSC::JSLockHolder lock(HTMLElement::scriptExecutionContext()->vm());
958     HTMLElement::scriptExecutionContext()->vm().heap.reportExtraMemoryAllocated(memoryCost());
959
960 #if USE(IOSURFACE_CANVAS_BACKING_STORE) || ENABLE(ACCELERATED_2D_CANVAS)
961     if (m_context && m_context->is2d()) {
962         // Recalculate compositing requirements if acceleration state changed.
963         const_cast<HTMLCanvasElement*>(this)->invalidateStyleAndLayerComposition();
964     }
965 #endif
966 }
967
968 void HTMLCanvasElement::setImageBufferAndMarkDirty(std::unique_ptr<ImageBuffer>&& buffer)
969 {
970     m_hasCreatedImageBuffer = true;
971     setImageBuffer(WTFMove(buffer));
972     didDraw(FloatRect(FloatPoint(), m_size));
973 }
974
975 GraphicsContext* HTMLCanvasElement::drawingContext() const
976 {
977     if (m_context && !m_context->is2d())
978         return nullptr;
979
980     return buffer() ? &m_imageBuffer->context() : nullptr;
981 }
982
983 GraphicsContext* HTMLCanvasElement::existingDrawingContext() const
984 {
985     if (!m_hasCreatedImageBuffer)
986         return nullptr;
987
988     return drawingContext();
989 }
990
991 ImageBuffer* HTMLCanvasElement::buffer() const
992 {
993     if (!m_hasCreatedImageBuffer)
994         createImageBuffer();
995     return m_imageBuffer.get();
996 }
997
998 Image* HTMLCanvasElement::copiedImage() const
999 {
1000     if (!m_copiedImage && buffer()) {
1001         if (m_context)
1002             m_context->paintRenderingResultsToCanvas();
1003         m_copiedImage = buffer()->copyImage(CopyBackingStore, PreserveResolution::Yes);
1004     }
1005     return m_copiedImage.get();
1006 }
1007
1008 void HTMLCanvasElement::clearImageBuffer() const
1009 {
1010     ASSERT(m_hasCreatedImageBuffer);
1011     ASSERT(!m_didClearImageBuffer);
1012     ASSERT(m_context);
1013
1014     m_didClearImageBuffer = true;
1015
1016     if (is<CanvasRenderingContext2D>(*m_context)) {
1017         // No need to undo transforms/clip/etc. because we are called right after the context is reset.
1018         downcast<CanvasRenderingContext2D>(*m_context).clearRect(0, 0, width(), height());
1019     }
1020 }
1021
1022 void HTMLCanvasElement::clearCopiedImage()
1023 {
1024     m_copiedImage = nullptr;
1025     m_didClearImageBuffer = false;
1026 }
1027
1028 AffineTransform HTMLCanvasElement::baseTransform() const
1029 {
1030     ASSERT(m_hasCreatedImageBuffer);
1031     return m_imageBuffer->baseTransform();
1032 }
1033
1034 }