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