Implement the HTML5 canvas tainting rules to prevent potential data leakage
[WebKit-https.git] / WebCore / html / CanvasRenderingContext2D.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
3  * Copyright (C) 2007 Trolltech ASA
4  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
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 COMPUTER, 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 COMPUTER, 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 "CanvasRenderingContext2D.h"
30
31 #include "AffineTransform.h"
32 #include "CSSParser.h"
33 #include "CachedImage.h"
34 #include "CanvasGradient.h"
35 #include "CanvasPattern.h"
36 #include "CanvasPixelArray.h"
37 #include "CanvasStyle.h"
38 #include "Document.h"
39 #include "ExceptionCode.h"
40 #include "Frame.h"
41 #include "GraphicsContext.h"
42 #include "HTMLCanvasElement.h"
43 #include "HTMLImageElement.h"
44 #include "HTMLNames.h"
45 #include "ImageBuffer.h"
46 #include "ImageData.h"
47 #include "KURL.h"
48 #include "NotImplemented.h"
49 #include "Page.h"
50 #include "RenderHTMLCanvas.h"
51 #include "SecurityOrigin.h"
52 #include "Settings.h"
53 #include <kjs/interpreter.h>
54 #include <wtf/MathExtras.h>
55
56 #if PLATFORM(QT)
57 #include <QPainter>
58 #include <QPixmap>
59 #include <QPainterPath>
60 #elif PLATFORM(CAIRO)
61 #include "CairoPath.h"
62 #include <cairo.h>
63 #endif
64
65 namespace WebCore {
66
67 using namespace HTMLNames;
68
69 CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas)
70     : RefCounted<CanvasRenderingContext2D>(0)
71     , m_canvas(canvas)
72     , m_stateStack(1)
73 {
74 }
75
76 void CanvasRenderingContext2D::reset()
77 {
78     m_stateStack.resize(1);
79     m_stateStack.first() = State();
80 }
81
82 CanvasRenderingContext2D::State::State()
83     : m_strokeStyle(new CanvasStyle("black"))
84     , m_fillStyle(new CanvasStyle("black"))
85     , m_lineWidth(1)
86     , m_lineCap(ButtCap)
87     , m_lineJoin(MiterJoin)
88     , m_miterLimit(10)
89     , m_shadowBlur(0)
90     , m_shadowColor("black")
91     , m_globalAlpha(1)
92     , m_globalComposite(CompositeSourceOver)
93     , m_appliedStrokePattern(false)
94     , m_appliedFillPattern(false)
95 {
96 }
97
98 void CanvasRenderingContext2D::save()
99 {
100     ASSERT(m_stateStack.size() >= 1);
101     m_stateStack.append(state());
102     GraphicsContext* c = drawingContext();
103     if (!c)
104         return;
105     c->save();
106 }
107
108 void CanvasRenderingContext2D::restore()
109 {
110     ASSERT(m_stateStack.size() >= 1);
111     if (m_stateStack.size() <= 1)
112         return;
113     m_stateStack.removeLast();
114     GraphicsContext* c = drawingContext();
115     if (!c)
116         return;
117     c->restore();
118 }
119
120 CanvasStyle* CanvasRenderingContext2D::strokeStyle() const
121 {
122     return state().m_strokeStyle.get();
123 }
124
125 void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> style)
126 {
127     if (!style)
128         return;
129
130     if (m_canvas->originClean()) {
131         if (CanvasPattern* pattern = style->pattern()) {
132             if (!pattern->originClean())
133                 m_canvas->setOriginTainted();
134         }
135     }
136
137     state().m_strokeStyle = style;
138     GraphicsContext* c = drawingContext();
139     if (!c)
140         return;
141     state().m_strokeStyle->applyStrokeColor(c);
142     state().m_appliedStrokePattern = false;
143 }
144
145 CanvasStyle* CanvasRenderingContext2D::fillStyle() const
146 {
147     return state().m_fillStyle.get();
148 }
149
150 void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> style)
151 {
152     if (!style)
153         return;
154  
155     if (m_canvas->originClean()) {
156         if (CanvasPattern* pattern = style->pattern()) {
157             if (!pattern->originClean())
158                 m_canvas->setOriginTainted();
159         }
160     }
161
162     state().m_fillStyle = style;
163     GraphicsContext* c = drawingContext();
164     if (!c)
165         return;
166 #if PLATFORM(CAIRO)
167     // FIXME: hack to reduce code duplication in CanvasStyle.cpp
168     state().m_fillStyle->applyStrokeColor(c);
169 #else
170     state().m_fillStyle->applyFillColor(c);
171 #endif
172     state().m_appliedFillPattern = false;
173 }
174
175 float CanvasRenderingContext2D::lineWidth() const
176 {
177     return state().m_lineWidth;
178 }
179
180 void CanvasRenderingContext2D::setLineWidth(float width)
181 {
182     if (!(width > 0))
183         return;
184     state().m_lineWidth = width;
185     GraphicsContext* c = drawingContext();
186     if (!c)
187         return;
188     c->setStrokeThickness(width);
189 }
190
191 String CanvasRenderingContext2D::lineCap() const
192 {
193     return lineCapName(state().m_lineCap);
194 }
195
196 void CanvasRenderingContext2D::setLineCap(const String& s)
197 {
198     LineCap cap;
199     if (!parseLineCap(s, cap))
200         return;
201     state().m_lineCap = cap;
202     GraphicsContext* c = drawingContext();
203     if (!c)
204         return;
205     c->setLineCap(cap);
206 }
207
208 String CanvasRenderingContext2D::lineJoin() const
209 {
210     return lineJoinName(state().m_lineJoin);
211 }
212
213 void CanvasRenderingContext2D::setLineJoin(const String& s)
214 {
215     LineJoin join;
216     if (!parseLineJoin(s, join))
217         return;
218     state().m_lineJoin = join;
219     GraphicsContext* c = drawingContext();
220     if (!c)
221         return;
222     c->setLineJoin(join);
223 }
224
225 float CanvasRenderingContext2D::miterLimit() const
226 {
227     return state().m_miterLimit;
228 }
229
230 void CanvasRenderingContext2D::setMiterLimit(float limit)
231 {
232     if (!(limit > 0))
233         return;
234     state().m_miterLimit = limit;
235     GraphicsContext* c = drawingContext();
236     if (!c)
237         return;
238     c->setMiterLimit(limit);
239 }
240
241 float CanvasRenderingContext2D::shadowOffsetX() const
242 {
243     return state().m_shadowOffset.width();
244 }
245
246 void CanvasRenderingContext2D::setShadowOffsetX(float x)
247 {
248     state().m_shadowOffset.setWidth(x);
249     applyShadow();
250 }
251
252 float CanvasRenderingContext2D::shadowOffsetY() const
253 {
254     return state().m_shadowOffset.height();
255 }
256
257 void CanvasRenderingContext2D::setShadowOffsetY(float y)
258 {
259     state().m_shadowOffset.setHeight(y);
260     applyShadow();
261 }
262
263 float CanvasRenderingContext2D::shadowBlur() const
264 {
265     return state().m_shadowBlur;
266 }
267
268 void CanvasRenderingContext2D::setShadowBlur(float blur)
269 {
270     state().m_shadowBlur = blur;
271     applyShadow();
272 }
273
274 String CanvasRenderingContext2D::shadowColor() const
275 {
276     // FIXME: What should this return if you called setShadow with a non-string color?
277     return state().m_shadowColor;
278 }
279
280 void CanvasRenderingContext2D::setShadowColor(const String& color)
281 {
282     state().m_shadowColor = color;
283     applyShadow();
284 }
285
286 float CanvasRenderingContext2D::globalAlpha() const
287 {
288     return state().m_globalAlpha;
289 }
290
291 void CanvasRenderingContext2D::setGlobalAlpha(float alpha)
292 {
293     if (!(alpha >= 0 && alpha <= 1))
294         return;
295     state().m_globalAlpha = alpha;
296     GraphicsContext* c = drawingContext();
297     if (!c)
298         return;
299     c->setAlpha(alpha);
300 }
301
302 String CanvasRenderingContext2D::globalCompositeOperation() const
303 {
304     return compositeOperatorName(state().m_globalComposite);
305 }
306
307 void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation)
308 {
309     CompositeOperator op;
310     if (!parseCompositeOperator(operation, op))
311         return;
312     state().m_globalComposite = op;
313     GraphicsContext* c = drawingContext();
314     if (!c)
315         return;
316     c->setCompositeOperation(op);
317 }
318
319 void CanvasRenderingContext2D::scale(float sx, float sy)
320 {
321     GraphicsContext* c = drawingContext();
322     if (!c)
323         return;
324     c->scale(FloatSize(sx, sy));
325     state().m_path.transform(AffineTransform().scale(1.0/sx, 1.0/sy));
326 }
327
328 void CanvasRenderingContext2D::rotate(float angleInRadians)
329 {
330     GraphicsContext* c = drawingContext();
331     if (!c)
332         return;
333     c->rotate(angleInRadians);
334     state().m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0));
335 }
336
337 void CanvasRenderingContext2D::translate(float tx, float ty)
338 {
339     GraphicsContext* c = drawingContext();
340     if (!c)
341         return;
342     c->translate(tx, ty);
343     state().m_path.transform(AffineTransform().translate(-tx, -ty));
344 }
345
346 void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy)
347 {
348     GraphicsContext* c = drawingContext();
349     if (!c)
350         return;
351     
352     // HTML5 3.14.11.1 -- ignore any calls that pass non-finite numbers
353     if (!isfinite(m11) || !isfinite(m21) || !isfinite(dx) || 
354         !isfinite(m12) || !isfinite(m22) || !isfinite(dy))
355         return;
356     AffineTransform transform(m11, m12, m21, m22, dx, dy);
357     c->concatCTM(transform);
358     state().m_path.transform(transform.inverse());
359 }
360
361 void CanvasRenderingContext2D::setStrokeColor(const String& color)
362 {
363     setStrokeStyle(new CanvasStyle(color));
364 }
365
366 void CanvasRenderingContext2D::setStrokeColor(float grayLevel)
367 {
368     setStrokeStyle(new CanvasStyle(grayLevel, 1));
369 }
370
371 void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha)
372 {
373     setStrokeStyle(new CanvasStyle(color, alpha));
374 }
375
376 void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha)
377 {
378     setStrokeStyle(new CanvasStyle(grayLevel, alpha));
379 }
380
381 void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a)
382 {
383     setStrokeStyle(new CanvasStyle(r, g, b, a));
384 }
385
386 void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a)
387 {
388     setStrokeStyle(new CanvasStyle(c, m, y, k, a));
389 }
390
391 void CanvasRenderingContext2D::setFillColor(const String& color)
392 {
393     setFillStyle(new CanvasStyle(color));
394 }
395
396 void CanvasRenderingContext2D::setFillColor(float grayLevel)
397 {
398     setFillStyle(new CanvasStyle(grayLevel, 1));
399 }
400
401 void CanvasRenderingContext2D::setFillColor(const String& color, float alpha)
402 {
403     setFillStyle(new CanvasStyle(color, 1));
404 }
405
406 void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha)
407 {
408     setFillStyle(new CanvasStyle(grayLevel, alpha));
409 }
410
411 void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a)
412 {
413     setFillStyle(new CanvasStyle(r, g, b, a));
414 }
415
416 void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a)
417 {
418     setFillStyle(new CanvasStyle(c, m, y, k, a));
419 }
420
421 void CanvasRenderingContext2D::beginPath()
422 {
423     state().m_path.clear();
424 }
425
426 void CanvasRenderingContext2D::closePath()
427 {
428     state().m_path.closeSubpath();
429 }
430
431 void CanvasRenderingContext2D::moveTo(float x, float y)
432 {
433     state().m_path.moveTo(FloatPoint(x, y));
434 }
435
436 void CanvasRenderingContext2D::lineTo(float x, float y)
437 {
438     state().m_path.addLineTo(FloatPoint(x, y));
439 }
440
441 void CanvasRenderingContext2D::quadraticCurveTo(float cpx, float cpy, float x, float y)
442 {
443     state().m_path.addQuadCurveTo(FloatPoint(cpx, cpy), FloatPoint(x, y));
444 }
445
446 void CanvasRenderingContext2D::bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y)
447 {
448     state().m_path.addBezierCurveTo(FloatPoint(cp1x, cp1y), FloatPoint(cp2x, cp2y), FloatPoint(x, y));
449 }
450
451 void CanvasRenderingContext2D::arcTo(float x0, float y0, float x1, float y1, float r, ExceptionCode& ec)
452 {
453     ec = 0;
454     if (!(r > 0)) {
455         ec = INDEX_SIZE_ERR;
456         return;
457     }
458     state().m_path.addArcTo(FloatPoint(x0, y0), FloatPoint(x1, y1), r);
459 }
460
461 void CanvasRenderingContext2D::arc(float x, float y, float r, float sa, float ea, bool anticlockwise, ExceptionCode& ec)
462 {
463     ec = 0;
464     if (!(r > 0)) {
465         ec = INDEX_SIZE_ERR;
466         return;
467     }
468     state().m_path.addArc(FloatPoint(x, y), r, sa, ea, anticlockwise);
469 }
470
471 void CanvasRenderingContext2D::rect(float x, float y, float width, float height, ExceptionCode& ec)
472 {
473     ec = 0;
474     if (!(width >= 0 && height >= 0)) {
475         ec = INDEX_SIZE_ERR;
476         return;
477     }
478     state().m_path.addRect(FloatRect(x, y, width, height));
479 }
480
481 void CanvasRenderingContext2D::clearPathForDashboardBackwardCompatibilityMode()
482 {
483     if (m_canvas)
484         if (Settings* settings = m_canvas->document()->settings())
485             if (settings->usesDashboardBackwardCompatibilityMode())
486                 state().m_path.clear();
487 }
488
489 void CanvasRenderingContext2D::fill()
490 {
491     GraphicsContext* c = drawingContext();
492     if (!c)
493         return;
494
495     c->beginPath();
496     c->addPath(state().m_path);
497     if (!state().m_path.isEmpty())
498         willDraw(state().m_path.boundingRect());
499
500 #if PLATFORM(CG)
501     if (state().m_fillStyle->gradient()) {
502         // Shading works on the entire clip region, so convert the current path to a clip.
503         c->save();
504         CGContextClip(c->platformContext());
505         CGContextDrawShading(c->platformContext(), state().m_fillStyle->gradient()->platformShading());        
506         c->restore();
507     } else {
508         if (state().m_fillStyle->pattern())
509             applyFillPattern();
510         CGContextFillPath(c->platformContext());
511     }
512 #elif PLATFORM(QT)
513     QPainterPath* path = state().m_path.platformPath();
514     QPainter* p = static_cast<QPainter*>(c->platformContext());
515     if (state().m_fillStyle->gradient()) {
516         p->fillPath(*path, QBrush(*(state().m_fillStyle->gradient()->platformShading())));
517     } else {
518         if (state().m_fillStyle->pattern())
519             applyFillPattern();
520         p->fillPath(*path, p->brush());
521     }
522 #elif PLATFORM(CAIRO)
523     cairo_t* cr = c->platformContext();
524     cairo_save(cr);
525
526     if (state().m_fillStyle->gradient()) {
527         cairo_set_source(cr, state().m_fillStyle->gradient()->platformShading());
528         cairo_fill(cr);
529     } else {
530         if (state().m_fillStyle->pattern())
531             applyFillPattern();
532         cairo_fill(cr);
533     }
534     cairo_restore(cr);
535 #endif
536
537     clearPathForDashboardBackwardCompatibilityMode();
538 }
539
540 void CanvasRenderingContext2D::stroke()
541 {
542     GraphicsContext* c = drawingContext();
543     if (!c)
544         return;
545     c->beginPath();
546     c->addPath(state().m_path);
547
548     if (!state().m_path.isEmpty()) {
549         // FIXME: This is insufficient, need to use CGContextReplacePathWithStrokedPath to expand to required bounds
550         float lineWidth = state().m_lineWidth;
551         float inset = lineWidth / 2;
552         FloatRect boundingRect = state().m_path.boundingRect();
553         boundingRect.inflate(inset);
554         willDraw(boundingRect);
555     }
556     
557     // FIXME: Do this through platform-independent GraphicsContext API.
558 #if PLATFORM(CG)
559     if (state().m_strokeStyle->gradient()) {
560         // Shading works on the entire clip region, so convert the current path to a clip.
561         c->save();
562         CGContextReplacePathWithStrokedPath(c->platformContext());
563         CGContextClip(c->platformContext());
564         CGContextDrawShading(c->platformContext(), state().m_strokeStyle->gradient()->platformShading());        
565         c->restore();
566     } else {
567         if (state().m_strokeStyle->pattern())
568             applyStrokePattern();
569         CGContextStrokePath(c->platformContext());
570     }
571 #elif PLATFORM(QT)
572     QPainterPath* path = state().m_path.platformPath();
573     QPainter* p = static_cast<QPainter*>(c->platformContext());
574     if (state().m_strokeStyle->gradient()) {
575         p->save();
576         p->setBrush(*(state().m_strokeStyle->gradient()->platformShading()));
577         p->strokePath(*path, p->pen());
578         p->restore();
579     } else {
580         if (state().m_strokeStyle->pattern())
581             applyStrokePattern();
582         p->strokePath(*path, p->pen());
583     }
584 #elif PLATFORM(CAIRO)
585     cairo_t* cr = c->platformContext();
586     cairo_save(cr);
587     if (state().m_strokeStyle->gradient()) {
588         cairo_set_source(cr, state().m_strokeStyle->gradient()->platformShading());
589         c->addPath(state().m_path);
590         cairo_stroke(cr);
591     } else {
592         if (state().m_strokeStyle->pattern())
593             applyStrokePattern();
594         c->addPath(state().m_path);
595         cairo_stroke(cr);
596     }
597     cairo_restore(cr);
598 #endif
599
600     clearPathForDashboardBackwardCompatibilityMode();
601 }
602
603 void CanvasRenderingContext2D::clip()
604 {
605     GraphicsContext* c = drawingContext();
606     if (!c)
607         return;
608     c->clip(state().m_path);
609     clearPathForDashboardBackwardCompatibilityMode();
610 }
611
612 bool CanvasRenderingContext2D::isPointInPath(const float x, const float y)
613 {
614     GraphicsContext* c = drawingContext();
615     if (!c)
616         return false;
617     FloatPoint point(x, y);
618     // We have to invert the current transform to ensure we correctly handle the
619     // transforms applied to the current path.
620     AffineTransform ctm = c->getCTM();
621     if (!ctm.isInvertible())
622         return false;
623     FloatPoint transformedPoint = ctm.inverse().mapPoint(point);
624     return state().m_path.contains(transformedPoint);
625 }
626
627 void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height, ExceptionCode& ec)
628 {
629     ec = 0;
630     if (!(width >= 0 && height >= 0)) {
631         ec = INDEX_SIZE_ERR;
632         return;
633     }
634     GraphicsContext* c = drawingContext();
635     if (!c)
636         return;
637     FloatRect rect(x, y, width, height);
638     willDraw(rect);
639     c->clearRect(rect);
640 }
641
642 void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height, ExceptionCode& ec)
643 {
644     ec = 0;
645
646     if (!(width >= 0 && height >= 0)) {
647         ec = INDEX_SIZE_ERR;
648         return;
649     }
650
651     GraphicsContext* c = drawingContext();
652     if (!c)
653         return;
654
655     FloatRect rect(x, y, width, height);
656     willDraw(rect);
657
658     // FIXME: Do this through platform-independent GraphicsContext API.
659 #if PLATFORM(CG)
660     if (state().m_fillStyle->gradient()) {
661         // Shading works on the entire clip region, so convert the rect to a clip.
662         c->save();
663         CGContextClipToRect(c->platformContext(), rect);
664         CGContextDrawShading(c->platformContext(), state().m_fillStyle->gradient()->platformShading());        
665         c->restore();
666     } else {
667         if (state().m_fillStyle->pattern())
668             applyFillPattern();
669         CGContextFillRect(c->platformContext(), rect);
670     }
671 #elif PLATFORM(QT)
672     QPainter* p = static_cast<QPainter*>(c->platformContext());
673     if (state().m_fillStyle->gradient()) {
674         p->fillRect(rect, QBrush(*(state().m_fillStyle->gradient()->platformShading())));
675     } else {
676         if (state().m_fillStyle->pattern())
677             applyFillPattern();
678         p->fillRect(rect, p->brush());
679     }
680 #elif PLATFORM(CAIRO)
681     cairo_t* cr = c->platformContext();
682     cairo_save(cr);
683     if (state().m_fillStyle->gradient()) {
684         cairo_set_source(cr, state().m_fillStyle->gradient()->platformShading());
685     } else {
686         if (state().m_fillStyle->pattern())
687             applyFillPattern();
688     }
689     cairo_rectangle(cr, x, y, width, height);
690     cairo_fill(cr);
691     cairo_restore(cr);
692 #endif
693 }
694
695 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, ExceptionCode& ec)
696 {
697     strokeRect(x, y, width, height, state().m_lineWidth, ec);
698 }
699
700 void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, float lineWidth, ExceptionCode& ec)
701 {
702     ec = 0;
703
704     if (!(width >= 0 && height >= 0 && lineWidth >= 0)) {
705         ec = INDEX_SIZE_ERR;
706         return;
707     }
708
709     GraphicsContext* c = drawingContext();
710     if (!c)
711         return;
712
713     FloatRect rect(x, y, width, height);
714
715     FloatRect boundingRect = rect;
716     boundingRect.inflate(lineWidth / 2);
717     willDraw(boundingRect);
718
719     // FIXME: No support for gradients!
720     if (state().m_strokeStyle->pattern())
721         applyStrokePattern();
722
723     c->strokeRect(rect, lineWidth);
724 }
725
726 void CanvasRenderingContext2D::setShadow(float width, float height, float blur)
727 {
728     state().m_shadowOffset = FloatSize(width, height);
729     state().m_shadowBlur = blur;
730     state().m_shadowColor = "";
731     applyShadow();
732 }
733
734 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color)
735 {
736     state().m_shadowOffset = FloatSize(width, height);
737     state().m_shadowBlur = blur;
738     state().m_shadowColor = color;
739     applyShadow();
740 }
741
742 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel)
743 {
744     state().m_shadowOffset = FloatSize(width, height);
745     state().m_shadowBlur = blur;
746     state().m_shadowColor = "";
747
748     GraphicsContext* c = drawingContext();
749     if (!c)
750         return;
751     // FIXME: Do this through platform-independent GraphicsContext API.
752 #if PLATFORM(CG)
753     const CGFloat components[2] = { grayLevel, 1 };
754     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
755     CGColorRef color = CGColorCreate(colorSpace, components);
756     CGColorSpaceRelease(colorSpace);
757     CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, color);
758     CGColorRelease(color);
759 #endif
760 }
761
762 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color, float alpha)
763 {
764     state().m_shadowOffset = FloatSize(width, height);
765     state().m_shadowBlur = blur;
766     state().m_shadowColor = color;
767
768     GraphicsContext* c = drawingContext();
769     if (!c)
770         return;
771     // FIXME: Do this through platform-independent GraphicsContext API.
772 #if PLATFORM(CG)
773     RGBA32 rgba = 0; // default is transparent black
774     CSSParser::parseColor(rgba, color);
775     const CGFloat components[4] = {
776         ((rgba >> 16) & 0xFF) / 255.0f,
777         ((rgba >> 8) & 0xFF) / 255.0f,
778         (rgba & 0xFF) / 255.0f,
779         alpha
780     };
781     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
782     CGColorRef shadowColor = CGColorCreate(colorSpace, components);
783     CGColorSpaceRelease(colorSpace);
784     CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, shadowColor);
785     CGColorRelease(shadowColor);
786 #endif
787 }
788
789 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha)
790 {
791     state().m_shadowOffset = FloatSize(width, height);
792     state().m_shadowBlur = blur;
793     state().m_shadowColor = "";
794
795     GraphicsContext* c = drawingContext();
796     if (!c)
797         return;
798     // FIXME: Do this through platform-independent GraphicsContext API.
799 #if PLATFORM(CG)
800     const CGFloat components[2] = { grayLevel, alpha };
801     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
802     CGColorRef color = CGColorCreate(colorSpace, components);
803     CGColorSpaceRelease(colorSpace);
804     CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, color);
805     CGColorRelease(color);
806 #endif
807 }
808
809 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a)
810 {
811     state().m_shadowOffset = FloatSize(width, height);
812     state().m_shadowBlur = blur;
813     state().m_shadowColor = "";
814
815     GraphicsContext* c = drawingContext();
816     if (!c)
817         return;
818     // FIXME: Do this through platform-independent GraphicsContext API.
819 #if PLATFORM(CG)
820     const CGFloat components[4] = { r, g, b, a };
821     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
822     CGColorRef shadowColor = CGColorCreate(colorSpace, components);
823     CGColorSpaceRelease(colorSpace);
824     CGContextSetShadowWithColor(c->platformContext(), CGSizeMake(width, height), blur, shadowColor);
825     CGColorRelease(shadowColor);
826 #endif
827 }
828
829 void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a)
830 {
831     state().m_shadowOffset = FloatSize(width, height);
832     state().m_shadowBlur = blur;
833     state().m_shadowColor = "";
834
835     GraphicsContext* dc = drawingContext();
836     if (!dc)
837         return;
838     // FIXME: Do this through platform-independent GraphicsContext API.
839 #if PLATFORM(CG)
840     const CGFloat components[5] = { c, m, y, k, a };
841     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceCMYK();
842     CGColorRef shadowColor = CGColorCreate(colorSpace, components);
843     CGColorSpaceRelease(colorSpace);
844     CGContextSetShadowWithColor(dc->platformContext(), CGSizeMake(width, height), blur, shadowColor);
845     CGColorRelease(shadowColor);
846 #endif
847 }
848
849 void CanvasRenderingContext2D::clearShadow()
850 {
851     state().m_shadowOffset = FloatSize();
852     state().m_shadowBlur = 0;
853     state().m_shadowColor = "";
854     applyShadow();
855 }
856
857 void CanvasRenderingContext2D::applyShadow()
858 {
859     GraphicsContext* c = drawingContext();
860     if (!c)
861         return;
862     // FIXME: Do this through platform-independent GraphicsContext API.
863 #if PLATFORM(CG)
864     RGBA32 rgba = 0; // default is transparent black
865     if (!state().m_shadowColor.isEmpty())
866         CSSParser::parseColor(rgba, state().m_shadowColor);
867     const CGFloat components[4] = {
868         ((rgba >> 16) & 0xFF) / 255.0f,
869         ((rgba >> 8) & 0xFF) / 255.0f,
870         (rgba & 0xFF) / 255.0f,
871         ((rgba >> 24) & 0xFF) / 255.0f
872     };
873     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
874     CGColorRef color = CGColorCreate(colorSpace, components);
875     CGColorSpaceRelease(colorSpace);
876     CGContextSetShadowWithColor(c->platformContext(), state().m_shadowOffset, state().m_shadowBlur, color);
877     CGColorRelease(color);
878 #endif
879 }
880
881 static IntSize size(HTMLImageElement* image)
882 {
883     if (CachedImage* cachedImage = image->cachedImage())
884         return cachedImage->imageSize();
885     return IntSize();
886 }
887
888 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, float x, float y)
889 {
890     ASSERT(image);
891     IntSize s = size(image);
892     ExceptionCode ec;
893     drawImage(image, x, y, s.width(), s.height(), ec);
894 }
895
896 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image,
897     float x, float y, float width, float height, ExceptionCode& ec)
898 {
899     ASSERT(image);
900     IntSize s = size(image);
901     drawImage(image, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec);
902 }
903
904 void CanvasRenderingContext2D::checkOrigin(const KURL& url)
905 {
906     RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url.protocol(), url.host(), url.port(), 0);
907     SecurityOrigin::Reason reason;
908     if (!m_canvas->document()->securityOrigin()->canAccess(origin.get(), reason))
909         m_canvas->setOriginTainted();
910 }
911
912 void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect,
913     ExceptionCode& ec)
914 {
915     ASSERT(image);
916
917     ec = 0;
918
919     FloatRect imageRect = FloatRect(FloatPoint(), size(image));
920     if (!(imageRect.contains(srcRect) && srcRect.width() >= 0 && srcRect.height() >= 0 
921             && dstRect.width() >= 0 && dstRect.height() >= 0)) {
922         ec = INDEX_SIZE_ERR;
923         return;
924     }
925
926     if (srcRect.isEmpty() || dstRect.isEmpty())
927         return;
928
929     GraphicsContext* c = drawingContext();
930     if (!c)
931         return;
932
933     CachedImage* cachedImage = image->cachedImage();
934     if (!cachedImage)
935         return;
936
937     if (m_canvas->originClean())
938         checkOrigin(KURL(cachedImage->url()));
939
940     FloatRect sourceRect = c->roundToDevicePixels(srcRect);
941     FloatRect destRect = c->roundToDevicePixels(dstRect);
942     willDraw(destRect);
943     c->drawImage(cachedImage->image(), destRect, sourceRect, state().m_globalComposite);
944 }
945
946 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, float x, float y)
947 {
948     ASSERT(canvas);
949     ExceptionCode ec;
950     drawImage(canvas, x, y, canvas->width(), canvas->height(), ec);
951 }
952
953 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas,
954     float x, float y, float width, float height, ExceptionCode& ec)
955 {
956     ASSERT(canvas);
957     drawImage(canvas, FloatRect(0, 0, canvas->width(), canvas->height()), FloatRect(x, y, width, height), ec);
958 }
959
960 void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, const FloatRect& srcRect,
961     const FloatRect& dstRect, ExceptionCode& ec)
962 {
963     ASSERT(canvas);
964
965     ec = 0;
966
967     FloatRect srcCanvasRect = FloatRect(FloatPoint(), canvas->size());
968     if (!(srcCanvasRect.contains(srcRect) && srcRect.width() >= 0 && srcRect.height() >= 0 
969             && dstRect.width() >= 0 && dstRect.height() >= 0)) {
970         ec = INDEX_SIZE_ERR;
971         return;
972     }
973
974     if (srcRect.isEmpty() || dstRect.isEmpty())
975         return;
976
977     GraphicsContext* c = drawingContext();
978     if (!c)
979         return;
980         
981     FloatRect sourceRect = c->roundToDevicePixels(srcRect);
982     FloatRect destRect = c->roundToDevicePixels(dstRect);
983         
984     // FIXME: Do this through platform-independent GraphicsContext API.
985     ImageBuffer* buffer = canvas->buffer();
986     if (!buffer)
987         return;
988
989     if (!canvas->originClean())
990         m_canvas->setOriginTainted();
991
992     willDraw(destRect);
993     c->drawImage(buffer, sourceRect, destRect);
994 }
995
996 // FIXME: Why isn't this just another overload of drawImage? Why have a different name?
997 void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image,
998     float sx, float sy, float sw, float sh,
999     float dx, float dy, float dw, float dh,
1000     const String& compositeOperation)
1001 {
1002     if (!image)
1003         return;
1004
1005     CachedImage* cachedImage = image->cachedImage();
1006     if (!cachedImage)
1007         return;
1008
1009     if (m_canvas->originClean())
1010         checkOrigin(KURL(cachedImage->url()));
1011
1012     GraphicsContext* c = drawingContext();
1013     if (!c)
1014         return;
1015
1016     CompositeOperator op;
1017     if (!parseCompositeOperator(compositeOperation, op))
1018         op = CompositeSourceOver;
1019
1020     FloatRect destRect = FloatRect(dx, dy, dw, dh);
1021     willDraw(destRect);
1022     c->drawImage(cachedImage->image(), destRect, FloatRect(sx, sy, sw, sh), op);
1023 }
1024
1025 void CanvasRenderingContext2D::setAlpha(float alpha)
1026 {
1027     setGlobalAlpha(alpha);
1028 }
1029
1030 void CanvasRenderingContext2D::setCompositeOperation(const String& operation)
1031 {
1032     setGlobalCompositeOperation(operation);
1033 }
1034
1035 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1)
1036 {
1037     return new CanvasGradient(FloatPoint(x0, y0), FloatPoint(x1, y1));
1038 }
1039
1040 PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1)
1041 {
1042     return new CanvasGradient(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
1043 }
1044
1045 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageElement* image,
1046     const String& repetitionType, ExceptionCode& ec)
1047 {
1048     bool repeatX, repeatY;
1049     ec = 0;
1050     CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1051     if (ec)
1052         return 0;
1053     
1054     bool originClean = true;
1055     if (CachedImage* cachedImage = image->cachedImage()) {
1056         KURL url(cachedImage->url());
1057         RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url.protocol(), url.host(), url.port(), 0);
1058         SecurityOrigin::Reason reason;
1059         originClean = m_canvas->document()->securityOrigin()->canAccess(origin.get(), reason);
1060     }
1061     return new CanvasPattern(image->cachedImage(), repeatX, repeatY, originClean);
1062 }
1063
1064 PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElement* canvas,
1065     const String& repetitionType, ExceptionCode& ec)
1066 {
1067     bool repeatX, repeatY;
1068     ec = 0;
1069     CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec);
1070     if (ec)
1071         return 0;
1072     // FIXME: Do this through platform-independent GraphicsContext API.
1073 #if PLATFORM(CG)
1074     CGImageRef image = canvas->createPlatformImage();
1075     if (!image)
1076         return 0;
1077     PassRefPtr<CanvasPattern> pattern = new CanvasPattern(image, repeatX, repeatY, canvas->originClean());
1078     CGImageRelease(image);
1079     return pattern;
1080 #elif PLATFORM(CAIRO)
1081     cairo_surface_t* surface = canvas->createPlatformImage();
1082     if (!surface)
1083         return 0;
1084     PassRefPtr<CanvasPattern> pattern = new CanvasPattern(surface, repeatX, repeatY, canvas->originClean());
1085     cairo_surface_destroy(surface);
1086     return pattern;
1087 #else
1088     notImplemented();
1089     return 0;
1090 #endif
1091 }
1092
1093 void CanvasRenderingContext2D::willDraw(const FloatRect& r)
1094 {
1095     if (!m_canvas)
1096         return;
1097     GraphicsContext* c = drawingContext();
1098     if (!c)
1099         return;
1100
1101     m_canvas->willDraw(c->getCTM().mapRect(r));
1102 }
1103
1104 GraphicsContext* CanvasRenderingContext2D::drawingContext() const
1105 {
1106     if (!m_canvas)
1107         return 0;
1108     return m_canvas->drawingContext();
1109 }
1110
1111 void CanvasRenderingContext2D::applyStrokePattern()
1112 {
1113     GraphicsContext* c = drawingContext();
1114     if (!c)
1115         return;
1116
1117 #if PLATFORM(CG)
1118     // Check for case where the pattern is already set.
1119     CGAffineTransform m = CGContextGetCTM(c->platformContext());
1120     if (state().m_appliedStrokePattern
1121             && CGAffineTransformEqualToTransform(m, state().m_strokeStylePatternTransform))
1122         return;
1123
1124     CanvasPattern* pattern = state().m_strokeStyle->pattern();
1125     if (!pattern)
1126         return;
1127
1128     CGPatternRef platformPattern = pattern->createPattern(m);
1129     if (!platformPattern)
1130         return;
1131
1132     CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(0);
1133     CGContextSetStrokeColorSpace(c->platformContext(), patternSpace);
1134     CGColorSpaceRelease(patternSpace);
1135
1136     const CGFloat patternAlpha = 1;
1137     CGContextSetStrokePattern(c->platformContext(), platformPattern, &patternAlpha);
1138     CGPatternRelease(platformPattern);
1139
1140     state().m_strokeStylePatternTransform = m;
1141 #elif PLATFORM(QT)
1142     fprintf(stderr, "FIXME: CanvasRenderingContext2D::applyStrokePattern\n");
1143 #elif PLATFORM(CAIRO)
1144     CanvasPattern* pattern = state().m_strokeStyle->pattern();
1145     if (!pattern)
1146         return;
1147
1148     cairo_t* cr = c->platformContext();
1149     cairo_matrix_t m;
1150     cairo_get_matrix(cr, &m);
1151
1152     cairo_pattern_t* platformPattern = pattern->createPattern(m);
1153     if (!platformPattern)
1154         return;
1155
1156     cairo_set_source(cr, platformPattern);
1157     cairo_pattern_destroy(platformPattern);
1158 #endif
1159     state().m_appliedStrokePattern = true;
1160 }
1161
1162 void CanvasRenderingContext2D::applyFillPattern()
1163 {
1164     GraphicsContext* c = drawingContext();
1165     if (!c)
1166         return;
1167
1168 #if PLATFORM(CG)
1169     // Check for case where the pattern is already set.
1170     CGAffineTransform m = CGContextGetCTM(c->platformContext());
1171     if (state().m_appliedFillPattern
1172             && CGAffineTransformEqualToTransform(m, state().m_fillStylePatternTransform))
1173         return;
1174
1175     CanvasPattern* pattern = state().m_fillStyle->pattern();
1176     if (!pattern)
1177         return;
1178
1179     CGPatternRef platformPattern = pattern->createPattern(m);
1180     if (!platformPattern)
1181         return;
1182
1183     CGColorSpaceRef patternSpace = CGColorSpaceCreatePattern(0);
1184     CGContextSetFillColorSpace(c->platformContext(), patternSpace);
1185     CGColorSpaceRelease(patternSpace);
1186
1187     const CGFloat patternAlpha = 1;
1188     CGContextSetFillPattern(c->platformContext(), platformPattern, &patternAlpha);
1189     CGPatternRelease(platformPattern);
1190
1191     state().m_fillStylePatternTransform = m;
1192 #elif PLATFORM(QT)
1193     fprintf(stderr, "FIXME: CanvasRenderingContext2D::applyFillPattern\n");
1194 #elif PLATFORM(CAIRO)
1195     CanvasPattern* pattern = state().m_fillStyle->pattern();
1196     if (!pattern)
1197         return;
1198
1199     cairo_t* cr = c->platformContext();
1200     cairo_matrix_t m;
1201     cairo_get_matrix(cr, &m);
1202
1203     cairo_pattern_t* platformPattern = pattern->createPattern(m);
1204     if (!platformPattern)
1205         return;
1206
1207     cairo_set_source(cr, platformPattern);
1208     cairo_pattern_destroy(platformPattern);
1209 #endif
1210     state().m_appliedFillPattern = true;
1211 }
1212
1213 static PassRefPtr<ImageData> createEmptyImageData(const IntSize& size)
1214 {
1215     PassRefPtr<ImageData> data = ImageData::create(size.width(), size.height());
1216     memset(data->data()->data().data(), 0, data->data()->length());
1217     return data;
1218 }
1219
1220 PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float sh) const
1221 {
1222     FloatSize unscaledSize(sw, sh);
1223     IntSize scaledSize;
1224     if (m_canvas)
1225         scaledSize = m_canvas->convertLogicalToDevice(unscaledSize);
1226     else
1227         scaledSize = IntSize(ceilf(sw), ceilf(sh));
1228     if (scaledSize.width() < 1)
1229         scaledSize.setWidth(1);
1230     if (scaledSize.height() < 1)
1231         scaledSize.setHeight(1);
1232     
1233     return createEmptyImageData(scaledSize);
1234 }
1235
1236 void CanvasRenderingContext2D::printSecurityExceptionMessage() const
1237 {
1238     static const char* message = "Call to getImageData failed due to tainted canvas.\n";
1239
1240     Frame* frame = m_canvas->document()->frame();
1241     if (!frame)
1242         return;
1243     if (frame->settings()->privateBrowsingEnabled())
1244         return;
1245     if (KJS::Interpreter::shouldPrintExceptions())
1246         printf("%s", message);
1247     if (Page* page = frame->page())
1248         page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, 1, String()); // FIXME: provide a real line number and source URL.
1249 }
1250
1251 PassRefPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh) const
1252 {
1253     if (!m_canvas->originClean()) {
1254         // FIXME: the WHATWG specification says that this should raise a "security exception", but does not currently
1255         // define what one is.  For now, we will silently fail with only a log message.
1256         printSecurityExceptionMessage();
1257         return 0;
1258     }
1259     
1260     FloatRect unscaledRect(sx, sy, sw, sh);
1261     IntRect scaledRect = m_canvas ? m_canvas->convertLogicalToDevice(unscaledRect) : enclosingIntRect(unscaledRect);
1262     if (scaledRect.width() < 1)
1263         scaledRect.setWidth(1);
1264     if (scaledRect.height() < 1)
1265         scaledRect.setHeight(1);
1266     ImageBuffer* buffer = m_canvas ? m_canvas->buffer() : 0;
1267     if (!buffer)
1268         return createEmptyImageData(scaledRect.size());
1269     return buffer->getImageData(scaledRect);
1270 }
1271
1272 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, ExceptionCode& ec)
1273 {
1274     if (!data) {
1275         ec = TYPE_MISMATCH_ERR;
1276         return;
1277     }
1278     putImageData(data, dx, dy, 0, 0, data->width(), data->height(), ec);
1279 }
1280
1281 void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, float dirtyX, float dirtyY, 
1282                                             float dirtyWidth, float dirtyHeight, ExceptionCode& ec)
1283 {
1284     if (!data) {
1285         ec = TYPE_MISMATCH_ERR;
1286         return;
1287     }
1288     if (!isfinite(dx) || !isfinite(dy) || !isfinite(dirtyX) || 
1289         !isfinite(dirtyY) || !isfinite(dirtyWidth) || !isfinite(dirtyHeight)) {
1290         ec = INDEX_SIZE_ERR;
1291         return;
1292     }
1293
1294     ImageBuffer* buffer = m_canvas ? m_canvas->buffer() : 0;
1295     if (!buffer)
1296         return;
1297
1298     if (dirtyWidth < 0) {
1299         dirtyX += dirtyWidth;
1300         dirtyWidth = -dirtyWidth;
1301     }
1302
1303     if (dirtyHeight < 0) {
1304         dirtyY += dirtyHeight;
1305         dirtyHeight = -dirtyHeight;
1306     }
1307
1308     FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
1309     clipRect.intersect(IntRect(0, 0, data->width(), data->height()));
1310     IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy));
1311     IntRect sourceRect = enclosingIntRect(clipRect);
1312     sourceRect.move(destOffset);
1313     sourceRect.intersect(IntRect(IntPoint(), buffer->size()));
1314     if (sourceRect.isEmpty())
1315         return;
1316     willDraw(sourceRect);
1317     sourceRect.move(-destOffset);
1318     IntPoint destPoint(destOffset.width(), destOffset.height());
1319     
1320     buffer->putImageData(data, sourceRect, destPoint);
1321 }
1322
1323 } // namespace WebCore