KHtml

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

KDE's Doxygen guidelines are available online.