Web Inspector: Instrument active pixel memory used by canvases
[WebKit-https.git] / Source / WebCore / html / HTMLCanvasElement.cpp
1 /*
2  * Copyright (C) 2004, 2006, 2007, 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 "ExceptionCode.h"
38 #include "Frame.h"
39 #include "FrameLoaderClient.h"
40 #include "GPUBasedCanvasRenderingContext.h"
41 #include "GeometryUtilities.h"
42 #include "GraphicsContext.h"
43 #include "HTMLNames.h"
44 #include "HTMLParserIdioms.h"
45 #include "ImageData.h"
46 #include "InspectorInstrumentation.h"
47 #include "MIMETypeRegistry.h"
48 #include "RenderHTMLCanvas.h"
49 #include "RuntimeEnabledFeatures.h"
50 #include "ScriptController.h"
51 #include "Settings.h"
52 #include <math.h>
53 #include <runtime/JSCInlines.h>
54 #include <runtime/JSLock.h>
55 #include <wtf/RAMSize.h>
56 #include <wtf/text/StringBuilder.h>
57
58 #if ENABLE(MEDIA_STREAM)
59 #include "CanvasCaptureMediaStreamTrack.h"
60 #include "MediaStream.h"
61 #endif
62
63 #if ENABLE(WEBGL)
64 #include "WebGLContextAttributes.h"
65 #include "WebGLRenderingContextBase.h"
66 #endif
67
68 #if ENABLE(WEBGPU)
69 #include "WebGPURenderingContext.h"
70 #endif
71
72 #if PLATFORM(COCOA)
73 #include "MediaSampleAVFObjC.h"
74
75 #include "CoreMediaSoftLink.h"
76 #endif
77
78 namespace WebCore {
79
80 using namespace HTMLNames;
81
82 // These values come from the WhatWG/W3C HTML spec.
83 const int defaultWidth = 300;
84 const int defaultHeight = 150;
85
86 // Firefox limits width/height to 32767 pixels, but slows down dramatically before it
87 // reaches that limit. We limit by area instead, giving us larger maximum dimensions,
88 // in exchange for a smaller maximum canvas size. The maximum canvas size is in device pixels.
89 #if PLATFORM(IOS)
90 const unsigned maxCanvasArea = 4096 * 4096;
91 #elif PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101100
92 const unsigned maxCanvasArea = 8192 * 8192;
93 #else
94 const unsigned maxCanvasArea = 16384 * 16384;
95 #endif
96
97 #if USE(CG)
98 // FIXME: It seems strange that the default quality is not the one that is literally named "default".
99 // Should fix names to make this easier to understand, or write an excellent comment here explaining why not.
100 const InterpolationQuality defaultInterpolationQuality = InterpolationLow;
101 #else
102 const InterpolationQuality defaultInterpolationQuality = InterpolationDefault;
103 #endif
104
105 static size_t activePixelMemory = 0;
106
107 HTMLCanvasElement::HTMLCanvasElement(const QualifiedName& tagName, Document& document)
108     : HTMLElement(tagName, document)
109     , m_size(defaultWidth, defaultHeight)
110 {
111     ASSERT(hasTagName(canvasTag));
112 }
113
114 Ref<HTMLCanvasElement> HTMLCanvasElement::create(Document& document)
115 {
116     return adoptRef(*new HTMLCanvasElement(canvasTag, document));
117 }
118
119 Ref<HTMLCanvasElement> HTMLCanvasElement::create(const QualifiedName& tagName, Document& document)
120 {
121     return adoptRef(*new HTMLCanvasElement(tagName, document));
122 }
123
124 static void removeFromActivePixelMemory(size_t pixelsReleased)
125 {
126     if (!pixelsReleased)
127         return;
128
129     if (pixelsReleased < activePixelMemory)
130         activePixelMemory -= pixelsReleased;
131     else
132         activePixelMemory = 0;
133 }
134     
135 HTMLCanvasElement::~HTMLCanvasElement()
136 {
137     for (auto& observer : m_observers)
138         observer->canvasDestroyed(*this);
139
140     m_context = nullptr; // Ensure this goes away before the ImageBuffer.
141
142     releaseImageBufferAndContext();
143 }
144
145 void HTMLCanvasElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
146 {
147     if (name == widthAttr || name == heightAttr)
148         reset();
149     HTMLElement::parseAttribute(name, value);
150 }
151
152 RenderPtr<RenderElement> HTMLCanvasElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition& insertionPosition)
153 {
154     Frame* frame = document().frame();
155     if (frame && frame->script().canExecuteScripts(NotAboutToExecuteScript))
156         return createRenderer<RenderHTMLCanvas>(*this, WTFMove(style));
157     return HTMLElement::createElementRenderer(WTFMove(style), insertionPosition);
158 }
159
160 bool HTMLCanvasElement::canContainRangeEndPoint() const
161 {
162     return false;
163 }
164
165 bool HTMLCanvasElement::canStartSelection() const
166 {
167     return false;
168 }
169
170 void HTMLCanvasElement::addObserver(CanvasObserver& observer)
171 {
172     m_observers.add(&observer);
173 }
174
175 void HTMLCanvasElement::removeObserver(CanvasObserver& observer)
176 {
177     m_observers.remove(&observer);
178 }
179
180 void HTMLCanvasElement::setHeight(unsigned value)
181 {
182     setAttributeWithoutSynchronization(heightAttr, AtomicString::number(limitToOnlyHTMLNonNegative(value, defaultHeight)));
183 }
184
185 void HTMLCanvasElement::setWidth(unsigned value)
186 {
187     setAttributeWithoutSynchronization(widthAttr, AtomicString::number(limitToOnlyHTMLNonNegative(value, defaultWidth)));
188 }
189
190 static inline size_t maxActivePixelMemory()
191 {
192     static size_t maxPixelMemory;
193     static std::once_flag onceFlag;
194     std::call_once(onceFlag, [] {
195         maxPixelMemory = std::max(ramSize() / 4, 2151 * MB);
196     });
197     return maxPixelMemory;
198 }
199
200 CanvasRenderingContext* HTMLCanvasElement::getContext(const String& type)
201 {
202     if (HTMLCanvasElement::is2dType(type))
203         return getContext2d(type);
204
205 #if ENABLE(WEBGPU)
206     if (HTMLCanvasElement::isWebGPUType(type) && RuntimeEnabledFeatures::sharedFeatures().webGPUEnabled())
207         return getContextWebGPU(type);
208 #endif
209
210 #if ENABLE(WEBGL)
211     if (HTMLCanvasElement::is3dType(type))
212         return getContextWebGL(type);
213 #endif
214
215     return nullptr;
216 }
217
218 bool HTMLCanvasElement::is2dType(const String& type)
219 {
220     return type == "2d";
221 }
222
223 CanvasRenderingContext* HTMLCanvasElement::getContext2d(const String& type)
224 {
225     ASSERT_UNUSED(HTMLCanvasElement::is2dType(type), type);
226
227     if (m_context && !m_context->is2d())
228         return nullptr;
229     if (!m_context) {
230         bool usesDashboardCompatibilityMode = false;
231 #if ENABLE(DASHBOARD_SUPPORT)
232         usesDashboardCompatibilityMode = document().settings().usesDashboardBackwardCompatibilityMode();
233 #endif
234
235         // Make sure we don't use more pixel memory than the system can support.
236         size_t requestedPixelMemory = 4 * width() * height();
237         if (activePixelMemory + requestedPixelMemory > maxActivePixelMemory()) {
238             StringBuilder stringBuilder;
239             stringBuilder.appendLiteral("Total canvas memory use exceeds the maximum limit (");
240             stringBuilder.appendNumber(maxActivePixelMemory() / 1024 / 1024);
241             stringBuilder.appendLiteral(" MB).");
242             document().addConsoleMessage(MessageSource::JS, MessageLevel::Warning, stringBuilder.toString());
243             return nullptr;
244         }
245
246         m_context = std::make_unique<CanvasRenderingContext2D>(*this, document().inQuirksMode(), usesDashboardCompatibilityMode);
247
248         downcast<CanvasRenderingContext2D>(*m_context).setUsesDisplayListDrawing(m_usesDisplayListDrawing);
249         downcast<CanvasRenderingContext2D>(*m_context).setTracksDisplayListReplay(m_tracksDisplayListReplay);
250
251         InspectorInstrumentation::didCreateCanvasRenderingContext(*this);
252
253 #if USE(IOSURFACE_CANVAS_BACKING_STORE) || ENABLE(ACCELERATED_2D_CANVAS)
254         // Need to make sure a RenderLayer and compositing layer get created for the Canvas
255         invalidateStyleAndLayerComposition();
256 #endif
257     }
258
259     return m_context.get();
260 }
261
262 #if ENABLE(WEBGL)
263 static bool requiresAcceleratedCompositingForWebGL()
264 {
265 #if PLATFORM(GTK)
266     return false;
267 #else
268     return true;
269 #endif
270
271 }
272 static bool shouldEnableWebGL(const Settings& settings)
273 {
274     if (!settings.webGLEnabled())
275         return false;
276
277     if (!requiresAcceleratedCompositingForWebGL())
278         return true;
279
280     return settings.acceleratedCompositingEnabled();
281 }
282
283 bool HTMLCanvasElement::is3dType(const String& type)
284 {
285     // Retain support for the legacy "webkit-3d" name.
286     return type == "webgl" || type == "experimental-webgl"
287 #if ENABLE(WEBGL2)
288         || type == "webgl2"
289 #endif
290         || type == "webkit-3d";
291 }
292
293 CanvasRenderingContext* HTMLCanvasElement::getContextWebGL(const String& type, WebGLContextAttributes&& attrs)
294 {
295     ASSERT(HTMLCanvasElement::is3dType(type));
296
297     if (!shouldEnableWebGL(document().settings()))
298         return nullptr;
299
300     if (m_context && !m_context->isWebGL())
301         return nullptr;
302
303     if (!m_context) {
304         m_context = WebGLRenderingContextBase::create(*this, attrs, type);
305         if (m_context) {
306             // Need to make sure a RenderLayer and compositing layer get created for the Canvas
307             invalidateStyleAndLayerComposition();
308
309             InspectorInstrumentation::didCreateCanvasRenderingContext(*this);
310         }
311     }
312
313     return m_context.get();
314 }
315 #endif
316
317 #if ENABLE(WEBGPU)
318 bool HTMLCanvasElement::isWebGPUType(const String& type)
319 {
320     return type == "webgpu";
321 }
322
323 CanvasRenderingContext* HTMLCanvasElement::getContextWebGPU(const String& type)
324 {
325     ASSERT_UNUSED(type, HTMLCanvasElement::isWebGPUType(type));
326
327     if (!RuntimeEnabledFeatures::sharedFeatures().webGPUEnabled())
328         return nullptr;
329
330     if (m_context && !m_context->isWebGPU())
331         return nullptr;
332
333     if (!m_context) {
334         m_context = WebGPURenderingContext::create(*this);
335         if (m_context) {
336             // Need to make sure a RenderLayer and compositing layer get created for the Canvas
337             invalidateStyleAndLayerComposition();
338         }
339     }
340
341     return m_context.get();
342 }
343 #endif
344
345 void HTMLCanvasElement::didDraw(const FloatRect& rect)
346 {
347     clearCopiedImage();
348
349     FloatRect dirtyRect = rect;
350     if (RenderBox* ro = renderBox()) {
351         FloatRect destRect = ro->contentBoxRect();
352         // Inflate dirty rect to cover antialiasing on image buffers.
353         if (drawingContext() && drawingContext()->shouldAntialias())
354             dirtyRect.inflate(1);
355         FloatRect r = mapRect(dirtyRect, FloatRect(0, 0, size().width(), size().height()), destRect);
356         r.intersect(destRect);
357         if (!r.isEmpty() && !m_dirtyRect.contains(r)) {
358             m_dirtyRect.unite(r);
359             ro->repaintRectangle(enclosingIntRect(m_dirtyRect));
360         }
361     }
362     notifyObserversCanvasChanged(dirtyRect);
363 }
364
365 void HTMLCanvasElement::notifyObserversCanvasChanged(const FloatRect& rect)
366 {
367     for (auto& observer : m_observers)
368         observer->canvasChanged(*this, rect);
369 }
370
371 void HTMLCanvasElement::reset()
372 {
373     if (m_ignoreReset)
374         return;
375
376     bool hadImageBuffer = hasCreatedImageBuffer();
377
378     int w = limitToOnlyHTMLNonNegative(attributeWithoutSynchronization(widthAttr), defaultWidth);
379     int h = limitToOnlyHTMLNonNegative(attributeWithoutSynchronization(heightAttr), defaultHeight);
380
381     if (m_contextStateSaver) {
382         // Reset to the initial graphics context state.
383         m_contextStateSaver->restore();
384         m_contextStateSaver->save();
385     }
386
387     if (is<CanvasRenderingContext2D>(m_context.get()))
388         downcast<CanvasRenderingContext2D>(*m_context).reset();
389
390     IntSize oldSize = size();
391     IntSize newSize(w, h);
392     // If the size of an existing buffer matches, we can just clear it instead of reallocating.
393     // This optimization is only done for 2D canvases for now.
394     if (m_hasCreatedImageBuffer && oldSize == newSize && m_context && m_context->is2d()) {
395         if (!m_didClearImageBuffer)
396             clearImageBuffer();
397         return;
398     }
399
400     setSurfaceSize(newSize);
401
402     if (isGPUBased() && oldSize != size())
403         downcast<GPUBasedCanvasRenderingContext>(*m_context).reshape(width(), height());
404
405     auto renderer = this->renderer();
406     if (is<RenderHTMLCanvas>(renderer)) {
407         auto& canvasRenderer = downcast<RenderHTMLCanvas>(*renderer);
408         if (oldSize != size()) {
409             canvasRenderer.canvasSizeChanged();
410             if (canvasRenderer.hasAcceleratedCompositing())
411                 canvasRenderer.contentChanged(CanvasChanged);
412         }
413         if (hadImageBuffer)
414             canvasRenderer.repaint();
415     }
416
417     for (auto& observer : m_observers)
418         observer->canvasResized(*this);
419 }
420
421 bool HTMLCanvasElement::paintsIntoCanvasBuffer() const
422 {
423     ASSERT(m_context);
424 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
425     if (m_context->is2d())
426         return true;
427 #endif
428
429     if (!m_context->isAccelerated())
430         return true;
431
432     if (renderBox() && renderBox()->hasAcceleratedCompositing())
433         return false;
434
435     return true;
436 }
437
438
439 void HTMLCanvasElement::paint(GraphicsContext& context, const LayoutRect& r)
440 {
441     // Clear the dirty rect
442     m_dirtyRect = FloatRect();
443
444     if (context.paintingDisabled())
445         return;
446     
447     if (m_context) {
448         if (!paintsIntoCanvasBuffer() && !document().printing())
449             return;
450
451         m_context->paintRenderingResultsToCanvas();
452     }
453
454     if (hasCreatedImageBuffer()) {
455         ImageBuffer* imageBuffer = buffer();
456         if (imageBuffer) {
457             if (m_presentedImage) {
458                 ImageOrientationDescription orientationDescription;
459 #if ENABLE(CSS_IMAGE_ORIENTATION)
460                 orientationDescription.setImageOrientationEnum(renderer()->style().imageOrientation());
461 #endif 
462                 context.drawImage(*m_presentedImage, snappedIntRect(r), ImagePaintingOptions(orientationDescription));
463             } else
464                 context.drawImageBuffer(*imageBuffer, snappedIntRect(r));
465         }
466     }
467
468     if (isGPUBased())
469         downcast<GPUBasedCanvasRenderingContext>(*m_context).markLayerComposited();
470 }
471
472 bool HTMLCanvasElement::isGPUBased() const
473 {
474     return m_context && m_context->isGPUBased();
475 }
476
477 void HTMLCanvasElement::makeRenderingResultsAvailable()
478 {
479     if (m_context)
480         m_context->paintRenderingResultsToCanvas();
481 }
482
483 void HTMLCanvasElement::makePresentationCopy()
484 {
485     if (!m_presentedImage) {
486         // The buffer contains the last presented data, so save a copy of it.
487         m_presentedImage = buffer()->copyImage(CopyBackingStore, Unscaled);
488     }
489 }
490
491 void HTMLCanvasElement::clearPresentationCopy()
492 {
493     m_presentedImage = nullptr;
494 }
495
496 void HTMLCanvasElement::releaseImageBufferAndContext()
497 {
498     m_contextStateSaver = nullptr;
499     setImageBuffer(nullptr);
500 }
501     
502 void HTMLCanvasElement::setSurfaceSize(const IntSize& size)
503 {
504     m_size = size;
505     m_hasCreatedImageBuffer = false;
506     releaseImageBufferAndContext();
507     clearCopiedImage();
508 }
509
510 String HTMLCanvasElement::toEncodingMimeType(const String& mimeType)
511 {
512     if (!MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType))
513         return ASCIILiteral("image/png");
514     return mimeType.convertToASCIILowercase();
515 }
516
517 ExceptionOr<String> HTMLCanvasElement::toDataURL(const String& mimeType, std::optional<double> quality)
518 {
519     if (!m_originClean)
520         return Exception { SECURITY_ERR };
521
522     if (m_size.isEmpty() || !buffer())
523         return String { ASCIILiteral { "data:," } };
524
525     String encodingMIMEType = toEncodingMimeType(mimeType);
526
527 #if USE(CG)
528     // Try to get ImageData first, as that may avoid lossy conversions.
529     if (auto imageData = getImageData())
530         return dataURL(*imageData, encodingMIMEType, quality);
531 #endif
532
533     makeRenderingResultsAvailable();
534
535     return buffer()->toDataURL(encodingMIMEType, quality);
536 }
537
538 ExceptionOr<void> HTMLCanvasElement::toBlob(ScriptExecutionContext& context, Ref<BlobCallback>&& callback, const String& mimeType, JSC::JSValue qualityValue)
539 {
540     if (!m_originClean)
541         return Exception { SECURITY_ERR };
542
543     if (m_size.isEmpty() || !buffer()) {
544         callback->scheduleCallback(context, nullptr);
545         return { };
546     }
547
548     String encodingMIMEType = toEncodingMimeType(mimeType);
549     std::optional<double> quality;
550     if (qualityValue.isNumber())
551         quality = qualityValue.toNumber(context.execState());
552
553 #if USE(CG)
554     if (auto imageData = getImageData()) {
555         RefPtr<Blob> blob;
556         Vector<uint8_t> blobData = data(*imageData, encodingMIMEType, quality);
557         if (!blobData.isEmpty())
558             blob = Blob::create(WTFMove(blobData), encodingMIMEType);
559         callback->scheduleCallback(context, WTFMove(blob));
560         return { };
561     }
562 #endif
563
564     makeRenderingResultsAvailable();
565
566     RefPtr<Blob> blob;
567     Vector<uint8_t> blobData = buffer()->toData(encodingMIMEType, quality);
568     if (!blobData.isEmpty())
569         blob = Blob::create(WTFMove(blobData), encodingMIMEType);
570     callback->scheduleCallback(context, WTFMove(blob));
571     return { };
572 }
573
574 RefPtr<ImageData> HTMLCanvasElement::getImageData()
575 {
576 #if ENABLE(WEBGL)
577     if (!m_context || !m_context->isWebGL())
578         return nullptr;
579
580     WebGLRenderingContextBase* ctx = static_cast<WebGLRenderingContextBase*>(m_context.get());
581
582     return ctx->paintRenderingResultsToImageData();
583 #else
584     return nullptr;
585 #endif
586 }
587
588 #if ENABLE(MEDIA_STREAM)
589
590 RefPtr<MediaSample> HTMLCanvasElement::toMediaSample()
591 {
592     auto* imageBuffer = buffer();
593     if (!imageBuffer)
594         return nullptr;
595
596 #if PLATFORM(COCOA)
597     makeRenderingResultsAvailable();
598     return MediaSampleAVFObjC::createImageSample(imageBuffer->toBGRAData(), width(), height());
599 #else
600     return nullptr;
601 #endif
602 }
603
604 ExceptionOr<Ref<MediaStream>> HTMLCanvasElement::captureStream(ScriptExecutionContext& context, std::optional<double>&& frameRequestRate)
605 {
606     if (!originClean())
607         return Exception(SECURITY_ERR, ASCIILiteral("Canvas is tainted"));
608
609     if (frameRequestRate && frameRequestRate.value() < 0)
610         return Exception(NOT_SUPPORTED_ERR, ASCIILiteral("frameRequestRate is negative"));
611
612     auto track = CanvasCaptureMediaStreamTrack::create(context, *this, WTFMove(frameRequestRate));
613     auto stream =  MediaStream::create(context);
614     stream->addTrack(track);
615     return WTFMove(stream);
616 }
617 #endif
618
619 SecurityOrigin* HTMLCanvasElement::securityOrigin() const
620 {
621     return &document().securityOrigin();
622 }
623
624 bool HTMLCanvasElement::shouldAccelerate(const IntSize& size) const
625 {
626     auto& settings = document().settings();
627
628     auto area = size.area<RecordOverflow>();
629     if (area.hasOverflowed())
630         return false;
631
632     if (area > settings.maximumAccelerated2dCanvasSize())
633         return false;
634
635 #if USE(IOSURFACE_CANVAS_BACKING_STORE)
636     return settings.canvasUsesAcceleratedDrawing();
637 #elif ENABLE(ACCELERATED_2D_CANVAS)
638     if (m_context && !m_context->is2d())
639         return false;
640
641     if (!settings.accelerated2dCanvasEnabled())
642         return false;
643
644     if (area < settings.minimumAccelerated2dCanvasSize())
645         return false;
646
647     return true;
648 #else
649     UNUSED_PARAM(size);
650     return false;
651 #endif
652 }
653
654 size_t HTMLCanvasElement::memoryCost() const
655 {
656     if (!m_imageBuffer)
657         return 0;
658     return m_imageBuffer->memoryCost();
659 }
660
661 size_t HTMLCanvasElement::externalMemoryCost() const
662 {
663     if (!m_imageBuffer)
664         return 0;
665     return m_imageBuffer->externalMemoryCost();
666 }
667
668 void HTMLCanvasElement::setUsesDisplayListDrawing(bool usesDisplayListDrawing)
669 {
670     if (usesDisplayListDrawing == m_usesDisplayListDrawing)
671         return;
672     
673     m_usesDisplayListDrawing = usesDisplayListDrawing;
674
675     if (is<CanvasRenderingContext2D>(m_context.get()))
676         downcast<CanvasRenderingContext2D>(*m_context).setUsesDisplayListDrawing(m_usesDisplayListDrawing);
677 }
678
679 void HTMLCanvasElement::setTracksDisplayListReplay(bool tracksDisplayListReplay)
680 {
681     if (tracksDisplayListReplay == m_tracksDisplayListReplay)
682         return;
683
684     m_tracksDisplayListReplay = tracksDisplayListReplay;
685
686     if (is<CanvasRenderingContext2D>(m_context.get()))
687         downcast<CanvasRenderingContext2D>(*m_context).setTracksDisplayListReplay(m_tracksDisplayListReplay);
688 }
689
690 String HTMLCanvasElement::displayListAsText(DisplayList::AsTextFlags flags) const
691 {
692     if (is<CanvasRenderingContext2D>(m_context.get()))
693         return downcast<CanvasRenderingContext2D>(*m_context).displayListAsText(flags);
694
695     return String();
696 }
697
698 String HTMLCanvasElement::replayDisplayListAsText(DisplayList::AsTextFlags flags) const
699 {
700     if (is<CanvasRenderingContext2D>(m_context.get()))
701         return downcast<CanvasRenderingContext2D>(*m_context).replayDisplayListAsText(flags);
702
703     return String();
704 }
705
706 void HTMLCanvasElement::createImageBuffer() const
707 {
708     ASSERT(!m_imageBuffer);
709
710     m_hasCreatedImageBuffer = true;
711     m_didClearImageBuffer = true;
712
713     // Perform multiplication as floating point to avoid overflow
714     if (float(width()) * height() > maxCanvasArea) {
715         StringBuilder stringBuilder;
716         stringBuilder.appendLiteral("Canvas area exceeds the maximum limit (width * height > ");
717         stringBuilder.appendNumber(maxCanvasArea);
718         stringBuilder.appendLiteral(").");
719         document().addConsoleMessage(MessageSource::JS, MessageLevel::Warning, stringBuilder.toString());
720         return;
721     }
722     
723     // Make sure we don't use more pixel memory than the system can support.
724     size_t requestedPixelMemory = 4 * width() * height();
725     if (activePixelMemory + requestedPixelMemory > maxActivePixelMemory()) {
726         StringBuilder stringBuilder;
727         stringBuilder.appendLiteral("Total canvas memory use exceeds the maximum limit (");
728         stringBuilder.appendNumber(maxActivePixelMemory() / 1024 / 1024);
729         stringBuilder.appendLiteral(" MB).");
730         document().addConsoleMessage(MessageSource::JS, MessageLevel::Warning, stringBuilder.toString());
731         return;
732     }
733
734     if (!width() || !height())
735         return;
736
737     RenderingMode renderingMode = shouldAccelerate(size()) ? Accelerated : Unaccelerated;
738
739     setImageBuffer(ImageBuffer::create(size(), renderingMode));
740     if (!m_imageBuffer)
741         return;
742     m_imageBuffer->context().setShadowsIgnoreTransforms(true);
743     m_imageBuffer->context().setImageInterpolationQuality(defaultInterpolationQuality);
744     m_imageBuffer->context().setStrokeThickness(1);
745     m_contextStateSaver = std::make_unique<GraphicsContextStateSaver>(m_imageBuffer->context());
746
747     JSC::JSLockHolder lock(scriptExecutionContext()->vm());
748     scriptExecutionContext()->vm().heap.reportExtraMemoryAllocated(memoryCost());
749
750 #if USE(IOSURFACE_CANVAS_BACKING_STORE) || ENABLE(ACCELERATED_2D_CANVAS)
751     if (m_context && m_context->is2d())
752         // Recalculate compositing requirements if acceleration state changed.
753         const_cast<HTMLCanvasElement*>(this)->invalidateStyleAndLayerComposition();
754 #endif
755 }
756
757 void HTMLCanvasElement::setImageBuffer(std::unique_ptr<ImageBuffer> buffer) const
758 {
759     size_t previousMemoryCost = memoryCost();
760     removeFromActivePixelMemory(previousMemoryCost);
761
762     m_imageBuffer = WTFMove(buffer);
763
764     size_t currentMemoryCost = memoryCost();
765     activePixelMemory += currentMemoryCost;
766
767     if (m_imageBuffer && previousMemoryCost != currentMemoryCost)
768         InspectorInstrumentation::didChangeCanvasMemory(const_cast<HTMLCanvasElement&>(*this));
769 }
770
771 GraphicsContext* HTMLCanvasElement::drawingContext() const
772 {
773     return buffer() ? &m_imageBuffer->context() : nullptr;
774 }
775
776 GraphicsContext* HTMLCanvasElement::existingDrawingContext() const
777 {
778     if (!m_hasCreatedImageBuffer)
779         return nullptr;
780
781     return drawingContext();
782 }
783
784 ImageBuffer* HTMLCanvasElement::buffer() const
785 {
786     if (!m_hasCreatedImageBuffer)
787         createImageBuffer();
788     return m_imageBuffer.get();
789 }
790
791 Image* HTMLCanvasElement::copiedImage() const
792 {
793     if (!m_copiedImage && buffer()) {
794         if (m_context)
795             m_context->paintRenderingResultsToCanvas();
796         m_copiedImage = buffer()->copyImage(CopyBackingStore, Unscaled);
797     }
798     return m_copiedImage.get();
799 }
800
801 void HTMLCanvasElement::clearImageBuffer() const
802 {
803     ASSERT(m_hasCreatedImageBuffer);
804     ASSERT(!m_didClearImageBuffer);
805     ASSERT(m_context);
806
807     m_didClearImageBuffer = true;
808
809     if (is<CanvasRenderingContext2D>(*m_context)) {
810         // No need to undo transforms/clip/etc. because we are called right after the context is reset.
811         downcast<CanvasRenderingContext2D>(*m_context).clearRect(0, 0, width(), height());
812     }
813 }
814
815 void HTMLCanvasElement::clearCopiedImage()
816 {
817     m_copiedImage = nullptr;
818     m_didClearImageBuffer = false;
819 }
820
821 AffineTransform HTMLCanvasElement::baseTransform() const
822 {
823     ASSERT(m_hasCreatedImageBuffer);
824     return m_imageBuffer->baseTransform();
825 }
826
827 }