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