1/*
2 * This file is part of the KDE libraries
3 * Copyright (C) 2004 Apple Computer, Inc.
4 * Copyright (C) 2005 Zack Rusin <zack@kde.org>
5 * Copyright (C) 2007, 2008 Maksim Orlovich <maksim@kde.org>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include "kjs_context2d.h"
23
24#include "kjs_html.h"
25
26#include <misc/loader.h>
27#include <dom/dom_exception.h>
28#include <xml/dom2_eventsimpl.h>
29#include <xml/dom_textimpl.h>
30#include <html/html_baseimpl.h>
31#include <html/html_blockimpl.h>
32#include <html/html_canvasimpl.h>
33#include <html/html_documentimpl.h>
34#include <html/html_formimpl.h>
35#include <html/html_headimpl.h>
36#include <html/html_imageimpl.h>
37#include <html/html_inlineimpl.h>
38#include <html/html_listimpl.h>
39#include <html/html_objectimpl.h>
40#include <html/html_tableimpl.h>
41
42#include <imload/imagemanager.h>
43
44#include <khtml_part.h>
45#include <khtmlview.h>
46
47#include "kjs_css.h"
48#include "kjs_window.h"
49#include "kjs_events.h"
50#include "kjs_proxy.h"
51#include <kjs/operations.h>
52
53#include <rendering/render_canvasimage.h>
54#include <rendering/render_object.h>
55#include <rendering/render_layer.h>
56
57#include "khtml_debug.h"
58
59#include <css/cssparser.h>
60#include <css/css_stylesheetimpl.h>
61#include <css/css_ruleimpl.h>
62
63using namespace DOM;
64
65#include "kjs_context2d.lut.h"
66
67namespace KJS
68{
69
70////////////////////// Context2D Object ////////////////////////
71
72KJS_DEFINE_PROTOTYPE(Context2DProto)
73KJS_IMPLEMENT_PROTOFUNC(Context2DFunction)
74KJS_IMPLEMENT_PROTOTYPE("CanvasRenderingContext2DProto", Context2DProto, Context2DFunction, ObjectPrototype)
75
76/*
77 @begin Context2DProtoTable 48
78 #
79 # state ops
80 save Context2D::Save DontDelete|Function 0
81 restore Context2D::Restore DontDelete|Function 0
82 #
83 # transformations
84 scale Context2D::Scale DontDelete|Function 2
85 rotate Context2D::Rotate DontDelete|Function 2
86 translate Context2D::Translate DontDelete|Function 1
87 transform Context2D::Transform DontDelete|Function 6
88 setTransform Context2D::SetTransform DontDelete|Function 6
89 #
90 # colors and styles
91 createLinearGradient Context2D::CreateLinearGradient DontDelete|Function 4
92 createRadialGradient Context2D::CreateRadialGradient DontDelete|Function 6
93 createPattern Context2D::CreatePattern DontDelete|Function 2
94 #
95 # rectangle ops
96 clearRect Context2D::ClearRect DontDelete|Function 4
97 fillRect Context2D::FillRect DontDelete|Function 4
98 strokeRect Context2D::StrokeRect DontDelete|Function 4
99 #
100 # paths
101 beginPath Context2D::BeginPath DontDelete|Function 0
102 closePath Context2D::ClosePath DontDelete|Function 0
103 moveTo Context2D::MoveTo DontDelete|Function 2
104 lineTo Context2D::LineTo DontDelete|Function 2
105 quadraticCurveTo Context2D::QuadraticCurveTo DontDelete|Function 4
106 bezierCurveTo Context2D::BezierCurveTo DontDelete|Function 6
107 arcTo Context2D::ArcTo DontDelete|Function 5
108 rect Context2D::Rect DontDelete|Function 4
109 arc Context2D::Arc DontDelete|Function 6
110 fill Context2D::Fill DontDelete|Function 0
111 isPointInPath Context2D::IsPointInPath DontDelete|Function 2
112 stroke Context2D::Stroke DontDelete|Function 0
113 clip Context2D::Clip DontDelete|Function 0
114 #
115 # images. Lots of overloading here!
116 drawImage Context2D::DrawImage DontDelete|Function 3
117 #
118 # pixel ops.
119 getImageData Context2D::GetImageData DontDelete|Function 4
120 putImageData Context2D::PutImageData DontDelete|Function 3
121 createImageData Context2D::CreateImageData DontDelete|Function 2
122 @end
123*/
124
125IMPLEMENT_PSEUDO_CONSTRUCTOR(Context2DPseudoCtor, "CanvasRenderingContext2D", Context2DProto)
126
127Context2D::Context2D(ExecState *exec, DOM::CanvasContext2DImpl *ctx):
128 WrapperBase(Context2DProto::self(exec), ctx)
129{}
130
131// Checks count and sets an exception if needed
132static bool enoughArguments(ExecState *exec, const List &args, int limit)
133{
134 if (args.size() < limit) {
135 setDOMException(exec, DOMException::NOT_SUPPORTED_ERR);
136 return false;
137 }
138 return true;
139}
140
141// Verifies that a float value is not NaN or a plus/minus infinity (unless infOK)
142static bool valFloatOK(ExecState *exec, const JSValue *v, bool infOK)
143{
144 float val = v->toFloat(exec);
145 if (KJS::isNaN(val) || (!infOK && KJS::isInf(val))) {
146 setDOMException(exec, DOMException::NOT_SUPPORTED_ERR);
147 return false;
148 }
149 return true;
150}
151
152// Verifies that float arguments are not NaN or a plus/minus infinity (unless infOK)
153static bool argFloatsOK(ExecState *exec, const List &args, int minArg, int maxArg, bool infOK)
154{
155 for (int c = minArg; c <= maxArg; ++c) {
156 if (!valFloatOK(exec, args[c], infOK)) {
157 return false;
158 }
159 }
160 return true;
161}
162
163// Checks if the JSValue is Inf or NaN
164static bool argFloatIsInforNaN(ExecState *exec, const JSValue *v)
165{
166 float val = v->toFloat(exec);
167 if (KJS::isNaN(val) || KJS::isInf(val)) {
168 return true;
169 }
170 return false;
171}
172
173// Checks if one the arguments if Inf or NaN
174static bool argumentsContainInforNaN(ExecState *exec, const List &args, int minArg, int maxArg)
175{
176 for (int c = minArg; c <= maxArg; ++c) {
177 if (argFloatIsInforNaN(exec, args[c])) {
178 return true;
179 }
180 }
181 return false;
182}
183
184// HTML5-style checking
185#define KJS_REQUIRE_ARGS(n) do { if (!enoughArguments(exec, args,n)) return jsUndefined(); } while(0);
186#define KJS_CHECK_FLOAT_ARGS(min,max) do { if (!argFloatsOK(exec, args, min, max, false )) return jsUndefined(); } while(0);
187#define KJS_CHECK_FLOAT_OR_INF_ARGS(min,max) do { if (!argFloatsOK(exec, args, min, max, true)) return jsUndefined(); } while(0);
188#define KJS_CHECK_FLOAT_VAL(v) if (!valFloatOK(exec, v, false)) return;
189
190// Unlike the above checks, ignore the invalid(Inf/NaN) values,
191// without throwing an exception
192#define KJS_CHECK_FLOAT_IGNORE_INVALID(v) do { if (argFloatIsInforNaN(exec, v)) return; } while(0)
193#define KJS_CHECK_FLOAT_ARGUMENTS_IGNORE_INVALID(min,max) do { if (argumentsContainInforNaN(exec, args, min, max)) return jsUndefined(); } while(0)
194
195JSValue *KJS::Context2DFunction::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
196{
197 KJS_CHECK_THIS(Context2D, thisObj);
198
199#ifdef KJS_VERBOSE
200 qCDebug(KHTML_LOG) << "KJS::Context2DFunction::callAsFunction " << functionName().qstring();
201#endif
202
203 Context2D *jsContextObject = static_cast<KJS::Context2D *>(thisObj);
204 CanvasContext2DImpl *ctx = jsContextObject->impl();
205 DOMExceptionTranslator exception(exec);
206
207 switch (id) {
208 // State ops
209 /////////////
210 case Context2D::Save: {
211 ctx->save();
212 break;
213 }
214
215 case Context2D::Restore: {
216 ctx->restore();
217 break;
218 }
219
220 // Transform ops. These have NaN inf handled specially in the impl
221 case Context2D::Scale: {
222 KJS_REQUIRE_ARGS(2);
223 KJS_CHECK_FLOAT_OR_INF_ARGS(0, 1);
224
225 ctx->scale(args[0]->toFloat(exec), args[1]->toFloat(exec));
226 break;
227 }
228
229 case Context2D::Rotate: {
230 KJS_REQUIRE_ARGS(1);
231 // Rotate actually rejects NaN/infinity as well
232 KJS_CHECK_FLOAT_ARGUMENTS_IGNORE_INVALID(0, 0);
233
234 ctx->rotate(args[0]->toFloat(exec));
235 break;
236 }
237
238 case Context2D::Translate: {
239 KJS_REQUIRE_ARGS(2);
240 KJS_CHECK_FLOAT_OR_INF_ARGS(0, 1);
241
242 ctx->translate(args[0]->toFloat(exec), args[1]->toFloat(exec));
243 break;
244 }
245
246 case Context2D::Transform: {
247 KJS_REQUIRE_ARGS(6);
248 KJS_CHECK_FLOAT_OR_INF_ARGS(0, 5);
249
250 ctx->transform(args[0]->toFloat(exec), args[1]->toFloat(exec),
251 args[2]->toFloat(exec), args[3]->toFloat(exec),
252 args[4]->toFloat(exec), args[5]->toFloat(exec));
253
254 break;
255 }
256
257 case Context2D::SetTransform: {
258 KJS_REQUIRE_ARGS(6);
259 KJS_CHECK_FLOAT_OR_INF_ARGS(0, 5);
260
261 ctx->setTransform(args[0]->toFloat(exec), args[1]->toFloat(exec),
262 args[2]->toFloat(exec), args[3]->toFloat(exec),
263 args[4]->toFloat(exec), args[5]->toFloat(exec));
264 break;
265 }
266
267 // Composition state is properties --- not in prototype
268
269 // Color and style info..
270 case Context2D::CreateLinearGradient: {
271 KJS_REQUIRE_ARGS(4);
272 KJS_CHECK_FLOAT_ARGS(0, 3);
273
274 CanvasGradientImpl *grad = ctx->createLinearGradient(
275 args[0]->toFloat(exec), args[1]->toFloat(exec),
276 args[2]->toFloat(exec), args[3]->toFloat(exec));
277 return getWrapper<CanvasGradient>(exec, grad);
278 }
279
280 case Context2D::CreateRadialGradient: {
281 KJS_REQUIRE_ARGS(6);
282 KJS_CHECK_FLOAT_ARGS(0, 5);
283
284 CanvasGradientImpl *grad = ctx->createRadialGradient(
285 args[0]->toFloat(exec), args[1]->toFloat(exec),
286 args[2]->toFloat(exec), args[3]->toFloat(exec),
287 args[4]->toFloat(exec), args[5]->toFloat(exec),
288 exception);
289
290 return getWrapper<CanvasGradient>(exec, grad);
291 }
292
293 case Context2D::CreatePattern: {
294 KJS_REQUIRE_ARGS(2);
295
296 ElementImpl *el = toElement(args[0]);
297 if (!el) {
298 setDOMException(exec, DOMException::TYPE_MISMATCH_ERR);
299 return jsUndefined();
300 }
301
302 CanvasPatternImpl *pat = ctx->createPattern(el, valueToStringWithNullCheck(exec, args[1]),
303 exception);
304
305 return getWrapper<CanvasPattern>(exec, pat);
306 }
307
308 // Line properties are all... properties!
309
310 // Rectangle ops
311 case Context2D::ClearRect: {
312 KJS_REQUIRE_ARGS(4);
313 KJS_CHECK_FLOAT_ARGUMENTS_IGNORE_INVALID(0, 3);
314
315 ctx->clearRect(args[0]->toFloat(exec), args[1]->toFloat(exec),
316 args[2]->toFloat(exec), args[3]->toFloat(exec),
317 exception);
318
319 break;
320 }
321
322 case Context2D::FillRect: {
323 KJS_REQUIRE_ARGS(4);
324 KJS_CHECK_FLOAT_ARGUMENTS_IGNORE_INVALID(0, 3);
325
326 ctx->fillRect(args[0]->toFloat(exec), args[1]->toFloat(exec),
327 args[2]->toFloat(exec), args[3]->toFloat(exec),
328 exception);
329
330 break;
331 }
332
333 case Context2D::StrokeRect: {
334 KJS_REQUIRE_ARGS(4);
335 KJS_CHECK_FLOAT_ARGUMENTS_IGNORE_INVALID(0, 3);
336
337 ctx->strokeRect(args[0]->toFloat(exec), args[1]->toFloat(exec),
338 args[2]->toFloat(exec), args[3]->toFloat(exec),
339 exception);
340
341 break;
342 }
343
344 // Path ops
345 case Context2D::BeginPath: {
346 ctx->beginPath();
347 break;
348 }
349
350 case Context2D::ClosePath: {
351 ctx->closePath();
352 break;
353 }
354
355 case Context2D::MoveTo: {
356 KJS_REQUIRE_ARGS(2);
357 KJS_CHECK_FLOAT_ARGUMENTS_IGNORE_INVALID(0, 1);
358
359 ctx->moveTo(args[0]->toFloat(exec), args[1]->toFloat(exec));
360 break;
361 }
362
363 case Context2D::LineTo: {
364 KJS_REQUIRE_ARGS(2);
365 KJS_CHECK_FLOAT_ARGUMENTS_IGNORE_INVALID(0, 1);
366
367 ctx->lineTo(args[0]->toFloat(exec), args[1]->toFloat(exec));
368 break;
369 }
370
371 case Context2D::QuadraticCurveTo: {
372 KJS_REQUIRE_ARGS(4);
373 KJS_CHECK_FLOAT_ARGUMENTS_IGNORE_INVALID(0, 3);
374
375 ctx->quadraticCurveTo(args[0]->toFloat(exec), args[1]->toFloat(exec),
376 args[2]->toFloat(exec), args[3]->toFloat(exec));
377 break;
378 }
379
380 case Context2D::BezierCurveTo: {
381 KJS_REQUIRE_ARGS(6);
382 KJS_CHECK_FLOAT_ARGUMENTS_IGNORE_INVALID(0, 5);
383
384 ctx->bezierCurveTo(args[0]->toFloat(exec), args[1]->toFloat(exec),
385 args[2]->toFloat(exec), args[3]->toFloat(exec),
386 args[4]->toFloat(exec), args[5]->toFloat(exec));
387 break;
388 }
389
390 case Context2D::ArcTo: {
391 KJS_REQUIRE_ARGS(5);
392 KJS_CHECK_FLOAT_ARGUMENTS_IGNORE_INVALID(0, 4);
393
394 ctx->arcTo(args[0]->toFloat(exec), args[1]->toFloat(exec),
395 args[2]->toFloat(exec), args[3]->toFloat(exec),
396 args[4]->toFloat(exec), exception);
397 break;
398 }
399
400 case Context2D::Rect: {
401 KJS_REQUIRE_ARGS(4);
402 KJS_CHECK_FLOAT_ARGUMENTS_IGNORE_INVALID(0, 3);
403
404 ctx->rect(args[0]->toFloat(exec), args[1]->toFloat(exec),
405 args[2]->toFloat(exec), args[3]->toFloat(exec),
406 exception);
407 break;
408 }
409
410 case Context2D::Arc: {
411 KJS_REQUIRE_ARGS(6);
412 KJS_CHECK_FLOAT_ARGUMENTS_IGNORE_INVALID(0, 5);
413
414 ctx->arc(args[0]->toFloat(exec), args[1]->toFloat(exec),
415 args[2]->toFloat(exec), args[3]->toFloat(exec),
416 args[4]->toFloat(exec), args[5]->toBoolean(exec),
417 exception);
418 break;
419 }
420
421 case Context2D::Fill: {
422 ctx->fill();
423 break;
424 }
425
426 case Context2D::Stroke: {
427 ctx->stroke();
428 break;
429 }
430
431 case Context2D::Clip: {
432 ctx->clip();
433 break;
434 }
435
436 case Context2D::IsPointInPath: {
437 KJS_REQUIRE_ARGS(2);
438 if (argumentsContainInforNaN(exec, args, 0, 1)) {
439 return jsBoolean(false);
440 }
441 return jsBoolean(ctx->isPointInPath(args[0]->toFloat(exec),
442 args[1]->toFloat(exec)));
443 }
444
445 case Context2D::DrawImage: {
446 ElementImpl *el = toElement(args[0]);
447 if (!el) {
448 setDOMException(exec, DOMException::TYPE_MISMATCH_ERR);
449 break;
450 }
451
452 if (args.size() < 3) {
453 setDOMException(exec, DOMException::NOT_SUPPORTED_ERR);
454 break;
455 }
456
457 if (args.size() < 5) { // 3 or 4 arguments
458 KJS_CHECK_FLOAT_ARGUMENTS_IGNORE_INVALID(1, 2);
459 ctx->drawImage(el,
460 args[1]->toFloat(exec),
461 args[2]->toFloat(exec),
462 exception);
463 } else if (args.size() < 9) { // 5 through 9 arguments
464 KJS_CHECK_FLOAT_ARGUMENTS_IGNORE_INVALID(1, 4);
465 ctx->drawImage(el,
466 args[1]->toFloat(exec),
467 args[2]->toFloat(exec),
468 args[3]->toFloat(exec),
469 args[4]->toFloat(exec),
470 exception);
471 } else { // 9 or more arguments
472 KJS_CHECK_FLOAT_ARGUMENTS_IGNORE_INVALID(1, 8);
473 ctx->drawImage(el,
474 args[1]->toFloat(exec),
475 args[2]->toFloat(exec),
476 args[3]->toFloat(exec),
477 args[4]->toFloat(exec),
478 args[5]->toFloat(exec),
479 args[6]->toFloat(exec),
480 args[7]->toFloat(exec),
481 args[8]->toFloat(exec),
482 exception);
483 }
484 break;
485 }
486 case Context2D::GetImageData: {
487 KJS_REQUIRE_ARGS(4);
488 KJS_CHECK_FLOAT_ARGS(0, 3);
489 CanvasImageDataImpl *id = ctx->getImageData(args[0]->toFloat(exec), args[1]->toFloat(exec),
490 args[2]->toFloat(exec), args[3]->toFloat(exec),
491 exception);
492 return getWrapper<CanvasImageData>(exec, id);
493 break;
494 }
495 case Context2D::PutImageData: {
496 KJS_REQUIRE_ARGS(3);
497 KJS_CHECK_FLOAT_ARGS(1, 2);
498 SharedPtr<CanvasImageDataImpl> id = toCanvasImageData(exec, args[0]); // may need to delete..
499 ctx->putImageData(id.get(), args[1]->toFloat(exec), args[2]->toFloat(exec), exception);
500 break;
501 }
502 case Context2D::CreateImageData: {
503 KJS_REQUIRE_ARGS(2);
504 KJS_CHECK_FLOAT_ARGS(0, 1);
505 CanvasImageDataImpl *id = ctx->createImageData(args[0]->toFloat(exec),
506 args[1]->toFloat(exec),
507 exception);
508 return getWrapper<CanvasImageData>(exec, id);
509 }
510
511 }
512
513 return jsUndefined();
514}
515
516const ClassInfo Context2D::info = { "CanvasRenderingContext2D", nullptr, &Context2DTable, nullptr };
517
518/*
519 @begin Context2DTable 11
520 canvas Context2D::Canvas DontDelete|ReadOnly
521 #
522 # compositing
523 globalAlpha Context2D::GlobalAlpha DontDelete
524 globalCompositeOperation Context2D::GlobalCompositeOperation DontDelete
525 #
526 # colors and styles
527 strokeStyle Context2D::StrokeStyle DontDelete
528 fillStyle Context2D::FillStyle DontDelete
529 #
530 # line drawing properties
531 lineWidth Context2D::LineWidth DontDelete
532 lineCap Context2D::LineCap DontDelete
533 lineJoin Context2D::LineJoin DontDelete
534 miterLimit Context2D::MiterLimit DontDelete
535 # shadow properties
536 shadowOffsetX Context2D::ShadowOffsetX DontDelete
537 shadowOffsetY Context2D::ShadowOffsetY DontDelete
538 shadowBlur Context2D::ShadowBlur DontDelete
539 shadowColor Context2D::ShadowColor DontDelete
540 @end
541*/
542
543bool Context2D::getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot)
544{
545 return getStaticValueSlot<Context2D, DOMObject>(exec, &Context2DTable, this, propertyName, slot);
546}
547
548static JSValue *encodeStyle(ExecState *exec, CanvasStyleBaseImpl *style)
549{
550 switch (style->type()) {
551 case CanvasStyleBaseImpl::Color:
552 return jsString(UString(static_cast<CanvasColorImpl *>(style)->toString()));
553 case CanvasStyleBaseImpl::Gradient:
554 return getWrapper<CanvasGradient>(exec, static_cast<CanvasGradientImpl *>(style));
555 case CanvasStyleBaseImpl::Pattern:
556 return getWrapper<CanvasPattern>(exec, static_cast<CanvasPatternImpl *>(style));
557 }
558
559 return jsNull();
560}
561
562// ### TODO: test how non-string things are handled in other browsers.
563static CanvasStyleBaseImpl *decodeStyle(ExecState *exec, JSValue *v)
564{
565 if (v->isObject() && static_cast<JSObject *>(v)->inherits(&CanvasGradient::info)) {
566 return static_cast<CanvasGradient *>(v)->impl();
567 } else if (v->isObject() && static_cast<JSObject *>(v)->inherits(&CanvasPattern::info)) {
568 return static_cast<CanvasPattern *>(v)->impl();
569 } else {
570 return CanvasColorImpl::fromString(v->toString(exec).domString());
571 }
572}
573
574JSValue *Context2D::getValueProperty(ExecState *exec, int token) const
575{
576 const CanvasContext2DImpl *ctx = impl();
577 switch (token) {
578 case Canvas:
579 return getDOMNode(exec, ctx->canvas());
580
581 case GlobalAlpha:
582 return jsNumber(ctx->globalAlpha());
583
584 case GlobalCompositeOperation:
585 return jsString(ctx->globalCompositeOperation());
586
587 case StrokeStyle:
588 return encodeStyle(exec, ctx->strokeStyle());
589
590 case FillStyle:
591 return encodeStyle(exec, ctx->fillStyle());
592
593 case LineWidth:
594 return jsNumber(ctx->lineWidth());
595
596 case LineCap:
597 return jsString(ctx->lineCap());
598
599 case LineJoin:
600 return jsString(ctx->lineJoin());
601
602 case MiterLimit:
603 return jsNumber(ctx->miterLimit());
604
605 case ShadowOffsetX:
606 return jsNumber(ctx->shadowOffsetX());
607
608 case ShadowOffsetY:
609 return jsNumber(ctx->shadowOffsetY());
610
611 case ShadowBlur:
612 return jsNumber(ctx->shadowBlur());
613
614 case ShadowColor:
615 return jsString(ctx->shadowColor());
616
617 default:
618 assert(0);
619 return jsUndefined();
620 }
621}
622
623void Context2D::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
624{
625 lookupPut<Context2D, DOMObject>(exec, propertyName, value, attr, &Context2DTable, this);
626}
627
628void Context2D::putValueProperty(ExecState *exec, int token, JSValue *value, int /*attr*/)
629{
630 CanvasContext2DImpl *ctx = impl();
631 switch (token) {
632 case GlobalAlpha:
633 KJS_CHECK_FLOAT_IGNORE_INVALID(value);
634 ctx->setGlobalAlpha(value->toFloat(exec));
635 break;
636 case GlobalCompositeOperation:
637 ctx->setGlobalCompositeOperation(value->toString(exec).domString());
638 break;
639 case StrokeStyle:
640 ctx->setStrokeStyle(decodeStyle(exec, value));
641 break;
642 case FillStyle:
643 ctx->setFillStyle(decodeStyle(exec, value));
644 break;
645 case LineWidth:
646 KJS_CHECK_FLOAT_IGNORE_INVALID(value);
647 ctx->setLineWidth(value->toFloat(exec));
648 break;
649 case LineCap:
650 ctx->setLineCap(value->toString(exec).domString());
651 break;
652 case LineJoin:
653 ctx->setLineJoin(value->toString(exec).domString());
654 break;
655 case MiterLimit:
656 KJS_CHECK_FLOAT_IGNORE_INVALID(value);
657 ctx->setMiterLimit(value->toFloat(exec));
658 break;
659 case ShadowOffsetX:
660 KJS_CHECK_FLOAT_IGNORE_INVALID(value);
661 ctx->setShadowOffsetX(value->toFloat(exec));
662 break;
663 case ShadowOffsetY:
664 KJS_CHECK_FLOAT_IGNORE_INVALID(value);
665 ctx->setShadowOffsetY(value->toFloat(exec));
666 break;
667 case ShadowBlur:
668 KJS_CHECK_FLOAT_IGNORE_INVALID(value);
669 ctx->setShadowBlur(value->toFloat(exec));
670 break;
671 case ShadowColor:
672 ctx->setShadowColor(value->toString(exec).domString());
673 break;
674 default: {
675 } // huh?
676 }
677}
678
679////////////////////// CanvasGradient Object ////////////////////////
680const ClassInfo KJS::CanvasGradient::info = { "CanvasGradient", nullptr, nullptr, nullptr };
681
682KJS_DEFINE_PROTOTYPE(CanvasGradientProto)
683KJS_IMPLEMENT_PROTOFUNC(CanvasGradientFunction)
684KJS_IMPLEMENT_PROTOTYPE("CanvasGradientProto", CanvasGradientProto, CanvasGradientFunction, ObjectPrototype)
685
686/*
687 @begin CanvasGradientProtoTable 1
688 addColorStop CanvasGradient::AddColorStop DontDelete|Function 2
689 @end
690*/
691
692JSValue *CanvasGradientFunction::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
693{
694 KJS_CHECK_THIS(CanvasGradient, thisObj);
695
696 CanvasGradientImpl *impl = static_cast<KJS::CanvasGradient *>(thisObj)->impl();
697
698 DOMExceptionTranslator exception(exec);
699 switch (id) {
700 case CanvasGradient::AddColorStop:
701 KJS_REQUIRE_ARGS(2);
702 impl->addColorStop(args[0]->toFloat(exec), args[1]->toString(exec).domString(), exception);
703 break;
704 default:
705 assert(0);
706 }
707
708 return jsUndefined();
709}
710
711CanvasGradient::CanvasGradient(ExecState *exec, DOM::CanvasGradientImpl *impl) :
712 WrapperBase(CanvasGradientProto::self(exec), impl)
713{}
714
715////////////////////// CanvasPattern Object ////////////////////////
716
717const ClassInfo CanvasPattern::info = { "CanvasPattern", nullptr, nullptr, nullptr };
718
719// Provide an empty prototype in case people want to hack it
720KJS_DEFINE_PROTOTYPE(CanvasPatternProto)
721KJS_IMPLEMENT_PROTOFUNC(CanvasPatternFunction)
722KJS_IMPLEMENT_PROTOTYPE("CanvasPatternProto", CanvasPatternProto, CanvasPatternFunction, ObjectPrototype)
723
724/*
725 @begin CanvasPatternProtoTable 0
726 @end
727*/
728
729JSValue *CanvasPatternFunction::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args)
730{
731 Q_UNUSED(exec);
732 Q_UNUSED(thisObj);
733 Q_UNUSED(args);
734 assert(0);
735 return nullptr;
736}
737
738CanvasPattern::CanvasPattern(ExecState *exec, DOM::CanvasPatternImpl *impl) :
739 WrapperBase(CanvasPatternProto::self(exec), impl)
740{}
741
742////////////////////// CanvasImageData[Array] Object /////////////////
743
744const ClassInfo CanvasImageData::info = { "ImageData", nullptr, nullptr, nullptr };
745
746CanvasImageData::CanvasImageData(ExecState *exec, DOM::CanvasImageDataImpl *impl) :
747 WrapperBase(exec->lexicalInterpreter()->builtinObjectPrototype(), impl)
748{
749 data = new CanvasImageDataArray(exec, this);
750 // Set out properties from the image info..
751 putDirect("width", jsNumber(impl->width()), DontDelete | ReadOnly);
752 putDirect("height", jsNumber(impl->height()), DontDelete | ReadOnly);
753 putDirect("data", data, DontDelete | ReadOnly);
754}
755
756void CanvasImageData::mark()
757{
758 JSObject::mark();
759 if (!data->marked()) {
760 data->mark();
761 }
762}
763
764JSObject *CanvasImageData::valueClone(Interpreter *targetCtx) const
765{
766 return static_cast<JSObject *>(getWrapper<CanvasImageData>(targetCtx->globalExec(), impl()->clone()));
767}
768
769const ClassInfo CanvasImageDataArray::info = { "ImageDataArray", nullptr, nullptr, nullptr };
770
771CanvasImageDataArray::CanvasImageDataArray(ExecState *exec, CanvasImageData *p) :
772 JSObject(exec->lexicalInterpreter()->builtinArrayPrototype()), parent(p)
773{
774 size = p->impl()->width() * p->impl()->height() * 4;
775 putDirect(exec->propertyNames().length, jsNumber(size), DontDelete | ReadOnly);
776}
777
778void CanvasImageDataArray::mark()
779{
780 JSObject::mark();
781 if (!parent->marked()) {
782 parent->mark();
783 }
784}
785
786JSValue *CanvasImageDataArray::indexGetter(ExecState *exec, unsigned index)
787{
788 Q_UNUSED(exec);
789 if (index >= size) { // paranoia..
790 return jsNull();
791 }
792
793 unsigned pixel = index / 4;
794 unsigned comp = index % 4;
795 QColor color = parent->impl()->pixel(pixel);
796 //"... with each pixel's red, green, blue, and alpha components being given in that order"
797 switch (comp) {
798 case 0: return jsNumber(color.red());
799 case 1: return jsNumber(color.green());
800 case 2: return jsNumber(color.blue());
801 default: // aka case 3, for quietness purposes
802 return jsNumber(color.alpha());
803 }
804}
805
806bool CanvasImageDataArray::getOwnPropertySlot(ExecState *exec, const Identifier &propertyName, PropertySlot &slot)
807{
808 // ### this doesn't behave exactly like array does --- should we care?
809 bool ok;
810 unsigned index = propertyName.toArrayIndex(&ok);
811 if (ok && index < size) {
812 slot.setCustomIndex(this, index, indexGetterAdapter<CanvasImageDataArray>);
813 return true;
814 }
815
816 return JSObject::getOwnPropertySlot(exec, propertyName, slot);
817}
818
819bool CanvasImageDataArray::getOwnPropertySlot(ExecState *exec, unsigned index, PropertySlot &slot)
820{
821 if (index < size) {
822 slot.setCustomIndex(this, index, indexGetterAdapter<CanvasImageDataArray>);
823 return true;
824 }
825
826 return JSObject::getOwnPropertySlot(exec, index, slot);
827}
828
829void CanvasImageDataArray::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr)
830{
831 bool ok;
832 unsigned index = propertyName.toArrayIndex(&ok);
833 if (ok) {
834 put(exec, index, value, attr);
835 return;
836 }
837
838 JSObject::put(exec, propertyName, value, attr);
839}
840
841unsigned char CanvasImageDataArray::decodeComponent(ExecState *exec, JSValue *jsVal)
842{
843 double val = jsVal->toNumber(exec);
844
845 if (jsVal->isUndefined()) {
846 val = 0.0;
847 } else if (val < 0.0) {
848 val = 0.0;
849 } else if (val > 255.0) {
850 val = 255.0;
851 }
852
853 // ### fixme: round to even
854 return (unsigned char)qRound(val);
855}
856
857void CanvasImageDataArray::put(ExecState *exec, unsigned index, JSValue *value, int attr)
858{
859 if (index < size) {
860 unsigned char componentValue = decodeComponent(exec, value);
861 unsigned int pixel = index / 4;
862 unsigned int comp = index % 4;
863 parent->impl()->setComponent(pixel, comp, componentValue);
864 return;
865 }
866
867 // Must use the string version here since numberic one will fall back to
868 // us again.
869 JSObject::put(exec, Identifier::from(index), value, attr);
870}
871
872DOM::CanvasImageDataImpl *toCanvasImageData(ExecState *exec, JSValue *val)
873{
874 JSObject *obj = val->getObject();
875 if (!obj) {
876 return nullptr;
877 }
878
879 if (obj->inherits(&CanvasImageData::info)) {
880 return static_cast<CanvasImageData *>(val)->impl();
881 }
882
883 // Uff. May be a fake one.
884 bool ok = true;
885 uint32_t width = obj->get(exec, "width")->toUInt32(exec, ok);
886 if (!ok || !width || exec->hadException()) {
887 return nullptr;
888 }
889 uint32_t height = obj->get(exec, "height")->toUInt32(exec, ok);
890 if (!ok || !height || exec->hadException()) {
891 return nullptr;
892 }
893
894 // Perform safety check on the size.
895 if (!khtmlImLoad::ImageManager::isAcceptableSize(width, height)) {
896 return nullptr;
897 }
898
899 JSObject *data = obj->get(exec, "data")->getObject();
900 if (!data) {
901 return nullptr;
902 }
903
904 uint32_t length = data->get(exec, "length")->toUInt32(exec, ok);
905 if (!ok || !length || exec->hadException()) {
906 return nullptr;
907 }
908
909 if (length != 4 * width * height) {
910 return nullptr;
911 }
912
913 // Uff. Well, it sounds sane enough for us to decode..
914 CanvasImageDataImpl *id = new CanvasImageDataImpl(width, height);
915 for (unsigned pixel = 0; pixel < width * height; ++pixel) {
916 unsigned char r = CanvasImageDataArray::decodeComponent(exec, data->get(exec, pixel * 4));
917 unsigned char g = CanvasImageDataArray::decodeComponent(exec, data->get(exec, pixel * 4 + 1));
918 unsigned char b = CanvasImageDataArray::decodeComponent(exec, data->get(exec, pixel * 4 + 2));
919 unsigned char a = CanvasImageDataArray::decodeComponent(exec, data->get(exec, pixel * 4 + 3));
920 id->setPixel(pixel, QColor(r, g, b, a));
921 }
922 return id;
923}
924
925// This is completely fake!
926IMPLEMENT_PSEUDO_CONSTRUCTOR(SVGAnglePseudoCtor, "SVGAngle", Context2DProto)
927
928} // namespace
929
930