2009-07-07 Nate Chapin <japhet@chromium.org>
[WebKit-https.git] / WebCore / bindings / v8 / custom / V8CanvasRenderingContext2DCustom.cpp
1 /*
2  * Copyright (C) 2009 Google Inc. All rights reserved.
3  * 
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  * 
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  * 
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "CanvasRenderingContext2D.h"
33
34 #include "CanvasGradient.h"
35 #include "CanvasPattern.h"
36 #include "CanvasStyle.h"
37 #include "ExceptionCode.h"
38 #include "FloatRect.h"
39
40 #include "V8Binding.h"
41 #include "V8CanvasGradient.h"
42 #include "V8CanvasPattern.h"
43 #include "V8CustomBinding.h"
44 #include "V8HTMLCanvasElement.h"
45 #include "V8HTMLImageElement.h"
46 #include "V8Proxy.h"
47
48 namespace WebCore {
49
50 static v8::Handle<v8::Value> toV8(CanvasStyle* style)
51 {
52     if (style->canvasGradient())
53         return V8Proxy::convertToV8Object(V8ClassIndex::CANVASGRADIENT, style->canvasGradient());
54
55     if (style->canvasPattern())
56         return V8Proxy::convertToV8Object(V8ClassIndex::CANVASPATTERN, style->canvasPattern());
57
58     return v8String(style->color());
59 }
60
61 static PassRefPtr<CanvasStyle> toCanvasStyle(v8::Handle<v8::Value> value)
62 {
63     if (value->IsString())
64         return CanvasStyle::create(toWebCoreString(value));
65
66     if (V8CanvasGradient::HasInstance(value))
67         return CanvasStyle::create(V8Proxy::convertDOMWrapperToNative<CanvasGradient>(value));
68
69     if (V8CanvasPattern::HasInstance(value))
70         return CanvasStyle::create(V8Proxy::convertDOMWrapperToNative<CanvasPattern>(value));
71
72     return 0;
73 }
74
75 ACCESSOR_GETTER(CanvasRenderingContext2DStrokeStyle)
76 {
77     CanvasRenderingContext2D* impl = V8Proxy::convertDOMWrapperToNative<CanvasRenderingContext2D>(info.Holder());
78     return toV8(impl->strokeStyle());
79 }
80
81 ACCESSOR_SETTER(CanvasRenderingContext2DStrokeStyle)
82 {
83     CanvasRenderingContext2D* impl = V8Proxy::convertDOMWrapperToNative<CanvasRenderingContext2D>(info.Holder());
84     impl->setStrokeStyle(toCanvasStyle(value));
85 }
86
87 ACCESSOR_GETTER(CanvasRenderingContext2DFillStyle)
88 {
89     CanvasRenderingContext2D* impl = V8Proxy::convertDOMWrapperToNative<CanvasRenderingContext2D>(info.Holder());
90     return toV8(impl->fillStyle());
91 }
92
93 ACCESSOR_SETTER(CanvasRenderingContext2DFillStyle)
94 {
95     CanvasRenderingContext2D* impl = V8Proxy::convertDOMWrapperToNative<CanvasRenderingContext2D>(info.Holder());
96     impl->setFillStyle(toCanvasStyle(value));
97 }
98
99 // TODO: SetStrokeColor and SetFillColor are similar except function names,
100 // consolidate them into one.
101 CALLBACK_FUNC_DECL(CanvasRenderingContext2DSetStrokeColor)
102 {
103     INC_STATS("DOM.CanvasRenderingContext2D.setStrokeColor()");
104     CanvasRenderingContext2D* context = V8Proxy::convertToNativeObject<CanvasRenderingContext2D>(V8ClassIndex::CANVASRENDERINGCONTEXT2D, args.Holder());
105     switch (args.Length()) {
106     case 1:
107         if (args[0]->IsString())
108             context->setStrokeColor(toWebCoreString(args[0]));
109         else
110             context->setStrokeColor(toFloat(args[0]));
111         break;
112     case 2:
113         if (args[0]->IsString())
114             context->setStrokeColor(toWebCoreString(args[0]), toFloat(args[1]));
115         else
116             context->setStrokeColor(toFloat(args[0]), toFloat(args[1]));
117         break;
118     case 4:
119         context->setStrokeColor(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toFloat(args[3]));
120         break;
121     case 5:
122         context->setStrokeColor(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4]));
123         break;
124     default:
125         V8Proxy::throwError(V8Proxy::SyntaxError, "setStrokeColor: Invalid number of arguments");
126         break;
127     }
128     return v8::Undefined();
129 }
130
131 CALLBACK_FUNC_DECL(CanvasRenderingContext2DSetFillColor)
132 {
133     INC_STATS("DOM.CanvasRenderingContext2D.setFillColor()");
134     CanvasRenderingContext2D* context = V8Proxy::convertToNativeObject<CanvasRenderingContext2D>(V8ClassIndex::CANVASRENDERINGCONTEXT2D, args.Holder());
135     switch (args.Length()) {
136     case 1:
137         if (args[0]->IsString())
138             context->setFillColor(toWebCoreString(args[0]));
139         else 
140             context->setFillColor(toFloat(args[0]));
141         break;
142     case 2:
143         if (args[0]->IsString())
144             context->setFillColor(toWebCoreString(args[0]), toFloat(args[1]));
145         else
146             context->setFillColor(toFloat(args[0]), toFloat(args[1]));
147         break;
148     case 4:
149         context->setFillColor(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toFloat(args[3]));
150         break;
151     case 5:
152         context->setFillColor(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4]));
153         break;
154     default:
155         V8Proxy::throwError(V8Proxy::SyntaxError, "setFillColor: Invalid number of arguments");
156         break;
157     }
158     return v8::Undefined();
159 }
160
161 CALLBACK_FUNC_DECL(CanvasRenderingContext2DStrokeRect)
162 {
163     INC_STATS("DOM.CanvasRenderingContext2D.strokeRect()");
164     CanvasRenderingContext2D* context = V8Proxy::convertToNativeObject<CanvasRenderingContext2D>(V8ClassIndex::CANVASRENDERINGCONTEXT2D, args.Holder());
165     if (args.Length() == 5)
166         context->strokeRect(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4]));
167     else if (args.Length() == 4)
168         context->strokeRect(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toFloat(args[3]));
169     else {
170         V8Proxy::setDOMException(INDEX_SIZE_ERR);
171         return notHandledByInterceptor();
172     }
173     return v8::Undefined();
174 }
175
176 CALLBACK_FUNC_DECL(CanvasRenderingContext2DSetShadow)
177 {
178     INC_STATS("DOM.CanvasRenderingContext2D.setShadow()");
179     CanvasRenderingContext2D* context = V8Proxy::convertToNativeObject<CanvasRenderingContext2D>(V8ClassIndex::CANVASRENDERINGCONTEXT2D, args.Holder());
180
181     switch (args.Length()) {
182     case 3:
183         context->setShadow(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]));
184         break;
185     case 4:
186         if (args[3]->IsString())
187             context->setShadow(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toWebCoreString(args[3]));
188         else
189             context->setShadow(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toFloat(args[3]));
190         break;
191     case 5:
192         if (args[3]->IsString())
193             context->setShadow(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toWebCoreString(args[3]), toFloat(args[4]));
194         else
195             context->setShadow(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4]));
196         break;
197     case 7:
198         context->setShadow(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4]), toFloat(args[5]), toFloat(args[6]));
199         break;
200     case 8:
201         context->setShadow(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4]), toFloat(args[5]), toFloat(args[6]), toFloat(args[7]));
202         break;
203     default:
204         V8Proxy::throwError(V8Proxy::SyntaxError, "setShadow: Invalid number of arguments");
205         break;
206     }
207
208     return v8::Undefined();
209 }
210
211 CALLBACK_FUNC_DECL(CanvasRenderingContext2DDrawImage)
212 {
213     INC_STATS("DOM.CanvasRenderingContext2D.drawImage()");
214     CanvasRenderingContext2D* context = V8Proxy::convertToNativeObject<CanvasRenderingContext2D>(V8ClassIndex::CANVASRENDERINGCONTEXT2D, args.Holder());
215
216     v8::Handle<v8::Value> arg = args[0];
217
218     if (V8HTMLImageElement::HasInstance(arg)) {
219         ExceptionCode ec = 0;
220         HTMLImageElement* image_element = V8Proxy::convertDOMWrapperToNode<HTMLImageElement>(arg);
221         switch (args.Length()) {
222         case 3:
223             context->drawImage(image_element, toFloat(args[1]), toFloat(args[2]));
224             break;
225         case 5:
226             context->drawImage(image_element, toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4]), ec);
227             if (ec != 0) {
228                 V8Proxy::setDOMException(ec);
229                 return notHandledByInterceptor();
230             }
231             break;
232         case 9:
233             context->drawImage(image_element, 
234                 FloatRect(toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4])), 
235                 FloatRect(toFloat(args[5]), toFloat(args[6]), toFloat(args[7]), toFloat(args[8])),
236                 ec);
237             if (ec != 0) {
238                 V8Proxy::setDOMException(ec);
239                 return notHandledByInterceptor();
240             }
241             break;
242         default:
243             V8Proxy::throwError(V8Proxy::SyntaxError, "drawImage: Invalid number of arguments");
244             return v8::Undefined();
245         }
246         return v8::Undefined();
247     }
248
249     // HTMLCanvasElement
250     if (V8HTMLCanvasElement::HasInstance(arg)) {
251         ExceptionCode ec = 0;
252         HTMLCanvasElement* canvas_element = V8Proxy::convertDOMWrapperToNode<HTMLCanvasElement>(arg);
253         switch (args.Length()) {
254         case 3:
255             context->drawImage(canvas_element, toFloat(args[1]), toFloat(args[2]));
256             break;
257         case 5:
258             context->drawImage(canvas_element, toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4]), ec);
259             if (ec != 0) {
260                 V8Proxy::setDOMException(ec);
261                 return notHandledByInterceptor();
262             }
263             break;
264         case 9:
265             context->drawImage(canvas_element,
266                 FloatRect(toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4])),
267                 FloatRect(toFloat(args[5]), toFloat(args[6]), toFloat(args[7]), toFloat(args[8])),
268                 ec);
269             if (ec != 0) {
270                 V8Proxy::setDOMException(ec);
271                 return notHandledByInterceptor();
272             }
273             break;
274         default:
275             V8Proxy::throwError(V8Proxy::SyntaxError, "drawImage: Invalid number of arguments");
276             return v8::Undefined();
277         }
278         return v8::Undefined();
279     }
280
281     V8Proxy::setDOMException(TYPE_MISMATCH_ERR);
282     return notHandledByInterceptor();
283 }
284
285 CALLBACK_FUNC_DECL(CanvasRenderingContext2DDrawImageFromRect)
286 {
287     INC_STATS("DOM.CanvasRenderingContext2D.drawImageFromRect()");
288     CanvasRenderingContext2D* context = V8Proxy::convertToNativeObject<CanvasRenderingContext2D>(V8ClassIndex::CANVASRENDERINGCONTEXT2D, args.Holder());
289
290     v8::Handle<v8::Value> arg = args[0];
291
292     if (V8HTMLImageElement::HasInstance(arg)) {
293         HTMLImageElement* image_element = V8Proxy::convertDOMWrapperToNode<HTMLImageElement>(arg);
294         context->drawImageFromRect(image_element,  toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4]), toFloat(args[5]), toFloat(args[6]), toFloat(args[7]), toFloat(args[8]), toWebCoreString(args[9]));
295     } else
296         V8Proxy::throwError(V8Proxy::TypeError, "drawImageFromRect: Invalid type of arguments");
297
298     return v8::Undefined();
299 }
300
301 CALLBACK_FUNC_DECL(CanvasRenderingContext2DCreatePattern)
302 {
303     INC_STATS("DOM.CanvasRenderingContext2D.createPattern()");
304     CanvasRenderingContext2D* context = V8Proxy::convertToNativeObject<CanvasRenderingContext2D>(V8ClassIndex::CANVASRENDERINGCONTEXT2D, args.Holder());
305
306     v8::Handle<v8::Value> arg = args[0];
307
308     if (V8HTMLImageElement::HasInstance(arg)) {
309         HTMLImageElement* image_element = V8Proxy::convertDOMWrapperToNode<HTMLImageElement>(arg);
310         ExceptionCode ec = 0;
311         RefPtr<CanvasPattern> pattern = context->createPattern(image_element, toWebCoreStringWithNullCheck(args[1]), ec);
312         if (ec != 0) {
313             V8Proxy::setDOMException(ec);
314             return notHandledByInterceptor();
315         }
316         return V8Proxy::convertToV8Object(V8ClassIndex::CANVASPATTERN, pattern.get());
317     }
318
319     if (V8HTMLCanvasElement::HasInstance(arg)) {
320         HTMLCanvasElement* canvas_element = V8Proxy::convertDOMWrapperToNode<HTMLCanvasElement>(arg);
321         ExceptionCode ec = 0;
322         RefPtr<CanvasPattern> pattern = context->createPattern(canvas_element, toWebCoreStringWithNullCheck(args[1]), ec);
323         if (ec != 0) {
324             V8Proxy::setDOMException(ec);
325             return notHandledByInterceptor();
326         }
327         return V8Proxy::convertToV8Object(V8ClassIndex::CANVASPATTERN, pattern.get());
328     }
329
330     V8Proxy::setDOMException(TYPE_MISMATCH_ERR);
331     return notHandledByInterceptor();
332 }
333
334 CALLBACK_FUNC_DECL(CanvasRenderingContext2DFillText)
335 {
336     INC_STATS("DOM.CanvasRenderingContext2D.fillText()");
337
338     CanvasRenderingContext2D* context = V8Proxy::convertToNativeObject<CanvasRenderingContext2D>(V8ClassIndex::CANVASRENDERINGCONTEXT2D, args.Holder());
339
340     // Two forms:
341     // * fillText(text, x, y)
342     // * fillText(text, x, y, maxWidth)
343     if (args.Length() < 3 || args.Length() > 4) {
344         V8Proxy::setDOMException(SYNTAX_ERR);
345         return notHandledByInterceptor();
346     }
347
348     String text = toWebCoreString(args[0]);
349     float x = toFloat(args[1]);
350     float y = toFloat(args[2]);
351
352     if (args.Length() == 4) {
353         float maxWidth = toFloat(args[3]);
354         context->fillText(text, x, y, maxWidth);
355     } else
356         context->fillText(text, x, y);
357
358     return v8::Undefined();
359 }
360
361 CALLBACK_FUNC_DECL(CanvasRenderingContext2DStrokeText)
362 {
363     INC_STATS("DOM.CanvasRenderingContext2D.strokeText()");
364     CanvasRenderingContext2D* context = V8Proxy::convertToNativeObject<CanvasRenderingContext2D>(V8ClassIndex::CANVASRENDERINGCONTEXT2D, args.Holder());
365
366     // Two forms:
367     // * strokeText(text, x, y)
368     // * strokeText(text, x, y, maxWidth)
369     if (args.Length() < 3 || args.Length() > 4) {
370         V8Proxy::setDOMException(SYNTAX_ERR);
371         return notHandledByInterceptor();
372     }
373
374     String text = toWebCoreString(args[0]);
375     float x = toFloat(args[1]);
376     float y = toFloat(args[2]);
377
378     if (args.Length() == 4) {
379         float maxWidth = toFloat(args[3]);
380         context->strokeText(text, x, y, maxWidth);
381     } else
382         context->strokeText(text, x, y);
383
384     return v8::Undefined();
385 }
386
387 CALLBACK_FUNC_DECL(CanvasRenderingContext2DPutImageData)
388 {
389     INC_STATS("DOM.CanvasRenderingContext2D.putImageData()");
390
391     // Two froms:
392     // * putImageData(ImageData, x, y)
393     // * putImageData(ImageData, x, y, dirtyX, dirtyY, dirtyWidth, dirtyHeight)
394     if (args.Length() != 3 && args.Length() != 7) {
395         V8Proxy::setDOMException(SYNTAX_ERR);
396         return notHandledByInterceptor();
397     }
398
399     CanvasRenderingContext2D* context = V8Proxy::convertToNativeObject<CanvasRenderingContext2D>(V8ClassIndex::CANVASRENDERINGCONTEXT2D, args.Holder());
400
401     ImageData* imageData = 0;
402
403     // Need to check that the argument is of the correct type, since
404     // convertToNativeObject() expects it to be correct. If the argument was incorrect
405     // we leave it null, and putImageData() will throw the correct exception
406     // (TYPE_MISMATCH_ERR).
407     if (V8Proxy::isWrapperOfType(args[0], V8ClassIndex::IMAGEDATA))
408         imageData = V8Proxy::convertToNativeObject<ImageData>(V8ClassIndex::IMAGEDATA, args[0]);
409
410     ExceptionCode ec = 0;
411
412     if (args.Length() == 7)
413         context->putImageData(imageData, toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4]), toFloat(args[5]), toFloat(args[6]), ec);
414     else
415         context->putImageData(imageData, toFloat(args[1]), toFloat(args[2]), ec);
416
417     if (ec != 0) {
418         V8Proxy::setDOMException(ec);
419         return notHandledByInterceptor();
420     }
421
422     return v8::Undefined();
423 }
424
425 } // namespace WebCore