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 "V8CanvasRenderingContext2D.h" 33 34 #include "CanvasGradient.h" 35 #include "CanvasRenderingContext2D.h" 36 #include "CanvasPattern.h" 37 #include "CanvasStyle.h" 38 #include "ExceptionCode.h" 39 #include "FloatRect.h" 40 41 #include "V8Binding.h" 42 #include "V8CanvasGradient.h" 43 #include "V8CanvasPattern.h" 44 #include "V8HTMLCanvasElement.h" 45 #include "V8HTMLImageElement.h" 46 #include "V8HTMLVideoElement.h" 47 #include "V8ImageData.h" 48 #include "V8Proxy.h" 49 50 namespace WebCore { 51 52 static v8::Handle<v8::Value> toV8Object(CanvasStyle* style) 53 { 54 if (style->canvasGradient()) 55 return toV8(style->canvasGradient()); 56 57 if (style->canvasPattern()) 58 return toV8(style->canvasPattern()); 59 60 return v8String(style->color()); 61 } 62 63 static PassRefPtr<CanvasStyle> toCanvasStyle(v8::Handle<v8::Value> value) 64 { 65 if (value->IsString()) 66 return CanvasStyle::create(toWebCoreString(value)); 67 68 if (V8CanvasGradient::HasInstance(value)) 69 return CanvasStyle::create(V8CanvasGradient::toNative(v8::Handle<v8::Object>::Cast(value))); 70 71 if (V8CanvasPattern::HasInstance(value)) 72 return CanvasStyle::create(V8CanvasPattern::toNative(v8::Handle<v8::Object>::Cast(value))); 73 74 return 0; 75 } 76 77 v8::Handle<v8::Value> V8CanvasRenderingContext2D::strokeStyleAccessorGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info) 78 { 79 CanvasRenderingContext2D* impl = V8CanvasRenderingContext2D::toNative(info.Holder()); 80 return toV8Object(impl->strokeStyle()); 81 } 82 83 void V8CanvasRenderingContext2D::strokeStyleAccessorSetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info) 84 { 85 CanvasRenderingContext2D* impl = V8CanvasRenderingContext2D::toNative(info.Holder()); 86 impl->setStrokeStyle(toCanvasStyle(value)); 87 } 88 89 v8::Handle<v8::Value> V8CanvasRenderingContext2D::fillStyleAccessorGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info) 90 { 91 CanvasRenderingContext2D* impl = V8CanvasRenderingContext2D::toNative(info.Holder()); 92 return toV8Object(impl->fillStyle()); 93 } 94 95 void V8CanvasRenderingContext2D::fillStyleAccessorSetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info) 96 { 97 CanvasRenderingContext2D* impl = V8CanvasRenderingContext2D::toNative(info.Holder()); 98 impl->setFillStyle(toCanvasStyle(value)); 99 } 100 101 // TODO: SetStrokeColor and SetFillColor are similar except function names, 102 // consolidate them into one. 103 v8::Handle<v8::Value> V8CanvasRenderingContext2D::setStrokeColorCallback(const v8::Arguments& args) 104 { 105 INC_STATS("DOM.CanvasRenderingContext2D.setStrokeColor()"); 106 CanvasRenderingContext2D* context = V8CanvasRenderingContext2D::toNative(args.Holder()); 107 switch (args.Length()) { 108 case 1: 109 if (args[0]->IsString()) 110 context->setStrokeColor(toWebCoreString(args[0])); 111 else 112 context->setStrokeColor(toFloat(args[0])); 113 break; 114 case 2: 115 if (args[0]->IsString()) 116 context->setStrokeColor(toWebCoreString(args[0]), toFloat(args[1])); 117 else 118 context->setStrokeColor(toFloat(args[0]), toFloat(args[1])); 119 break; 120 case 4: 121 context->setStrokeColor(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toFloat(args[3])); 122 break; 123 case 5: 124 context->setStrokeColor(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4])); 125 break; 126 default: 127 V8Proxy::throwError(V8Proxy::SyntaxError, "setStrokeColor: Invalid number of arguments"); 128 break; 129 } 130 return v8::Undefined(); 131 } 132 133 v8::Handle<v8::Value> V8CanvasRenderingContext2D::setFillColorCallback(const v8::Arguments& args) 134 { 135 INC_STATS("DOM.CanvasRenderingContext2D.setFillColor()"); 136 CanvasRenderingContext2D* context = V8CanvasRenderingContext2D::toNative(args.Holder()); 137 switch (args.Length()) { 138 case 1: 139 if (args[0]->IsString()) 140 context->setFillColor(toWebCoreString(args[0])); 141 else 142 context->setFillColor(toFloat(args[0])); 143 break; 144 case 2: 145 if (args[0]->IsString()) 146 context->setFillColor(toWebCoreString(args[0]), toFloat(args[1])); 147 else 148 context->setFillColor(toFloat(args[0]), toFloat(args[1])); 149 break; 150 case 4: 151 context->setFillColor(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toFloat(args[3])); 152 break; 153 case 5: 154 context->setFillColor(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4])); 155 break; 156 default: 157 V8Proxy::throwError(V8Proxy::SyntaxError, "setFillColor: Invalid number of arguments"); 158 break; 159 } 160 return v8::Undefined(); 161 } 162 163 v8::Handle<v8::Value> V8CanvasRenderingContext2D::strokeRectCallback(const v8::Arguments& args) 164 { 165 INC_STATS("DOM.CanvasRenderingContext2D.strokeRect()"); 166 CanvasRenderingContext2D* context = V8CanvasRenderingContext2D::toNative(args.Holder()); 167 if (args.Length() == 5) 168 context->strokeRect(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4])); 169 else if (args.Length() == 4) 170 context->strokeRect(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toFloat(args[3])); 171 else { 172 V8Proxy::setDOMException(INDEX_SIZE_ERR); 173 return notHandledByInterceptor(); 174 } 175 return v8::Undefined(); 176 } 177 178 v8::Handle<v8::Value> V8CanvasRenderingContext2D::setShadowCallback(const v8::Arguments& args) 179 { 180 INC_STATS("DOM.CanvasRenderingContext2D.setShadow()"); 181 CanvasRenderingContext2D* context = V8CanvasRenderingContext2D::toNative(args.Holder()); 182 183 switch (args.Length()) { 184 case 3: 185 context->setShadow(toFloat(args[0]), toFloat(args[1]), toFloat(args[2])); 186 break; 187 case 4: 188 if (args[3]->IsString()) 189 context->setShadow(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toWebCoreString(args[3])); 190 else 191 context->setShadow(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toFloat(args[3])); 192 break; 193 case 5: 194 if (args[3]->IsString()) 195 context->setShadow(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toWebCoreString(args[3]), toFloat(args[4])); 196 else 197 context->setShadow(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4])); 198 break; 199 case 7: 200 context->setShadow(toFloat(args[0]), toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4]), toFloat(args[5]), toFloat(args[6])); 201 break; 202 case 8: 203 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])); 204 break; 205 default: 206 V8Proxy::throwError(V8Proxy::SyntaxError, "setShadow: Invalid number of arguments"); 207 break; 208 } 209 210 return v8::Undefined(); 211 } 212 213 v8::Handle<v8::Value> V8CanvasRenderingContext2D::drawImageCallback(const v8::Arguments& args) 214 { 215 INC_STATS("DOM.CanvasRenderingContext2D.drawImage()"); 216 CanvasRenderingContext2D* context = V8CanvasRenderingContext2D::toNative(args.Holder()); 217 218 v8::Handle<v8::Value> arg = args[0]; 219 220 if (V8HTMLImageElement::HasInstance(arg)) { 221 ExceptionCode ec = 0; 222 HTMLImageElement* imageElement = V8HTMLImageElement::toNative(v8::Handle<v8::Object>::Cast(arg)); 223 switch (args.Length()) { 224 case 3: 225 context->drawImage(imageElement, toFloat(args[1]), toFloat(args[2])); 226 break; 227 case 5: 228 context->drawImage(imageElement, toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4]), ec); 229 if (ec != 0) { 230 V8Proxy::setDOMException(ec); 231 return notHandledByInterceptor(); 232 } 233 break; 234 case 9: 235 context->drawImage(imageElement, 236 FloatRect(toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4])), 237 FloatRect(toFloat(args[5]), toFloat(args[6]), toFloat(args[7]), toFloat(args[8])), 238 ec); 239 if (ec != 0) { 240 V8Proxy::setDOMException(ec); 241 return notHandledByInterceptor(); 242 } 243 break; 244 default: 245 return throwError("drawImage: Invalid number of arguments", V8Proxy::SyntaxError); 246 } 247 return v8::Undefined(); 248 } 249 250 // HTMLCanvasElement 251 if (V8HTMLCanvasElement::HasInstance(arg)) { 252 ExceptionCode ec = 0; 253 HTMLCanvasElement* canvasElement = V8HTMLCanvasElement::toNative(v8::Handle<v8::Object>::Cast(arg)); 254 switch (args.Length()) { 255 case 3: 256 context->drawImage(canvasElement, toFloat(args[1]), toFloat(args[2])); 257 break; 258 case 5: 259 context->drawImage(canvasElement, toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4]), ec); 260 if (ec != 0) { 261 V8Proxy::setDOMException(ec); 262 return notHandledByInterceptor(); 263 } 264 break; 265 case 9: 266 context->drawImage(canvasElement, 267 FloatRect(toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4])), 268 FloatRect(toFloat(args[5]), toFloat(args[6]), toFloat(args[7]), toFloat(args[8])), 269 ec); 270 if (ec != 0) { 271 V8Proxy::setDOMException(ec); 272 return notHandledByInterceptor(); 273 } 274 break; 275 default: 276 return throwError("drawImage: Invalid number of arguments", V8Proxy::SyntaxError); 277 } 278 return v8::Undefined(); 279 } 280 281 #if ENABLE(VIDEO) 282 // HTMLVideoElement 283 if (V8HTMLVideoElement::HasInstance(arg)) { 284 ExceptionCode ec = 0; 285 HTMLVideoElement* videoElement = V8HTMLVideoElement::toNative(v8::Handle<v8::Object>::Cast(arg)); 286 switch (args.Length()) { 287 case 3: 288 context->drawImage(videoElement, toFloat(args[1]), toFloat(args[2])); 289 break; 290 case 5: 291 context->drawImage(videoElement, toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4]), ec); 292 if (ec != 0) { 293 V8Proxy::setDOMException(ec); 294 return notHandledByInterceptor(); 295 } 296 break; 297 case 9: 298 context->drawImage(videoElement, 299 FloatRect(toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4])), 300 FloatRect(toFloat(args[5]), toFloat(args[6]), toFloat(args[7]), toFloat(args[8])), 301 ec); 302 if (ec != 0) { 303 V8Proxy::setDOMException(ec); 304 return notHandledByInterceptor(); 305 } 306 break; 307 default: 308 return throwError("drawImage: Invalid number of arguments", V8Proxy::SyntaxError); 309 } 310 return v8::Undefined(); 311 } 312 #endif 313 314 V8Proxy::setDOMException(TYPE_MISMATCH_ERR); 315 return notHandledByInterceptor(); 316 } 317 318 319 v8::Handle<v8::Value> V8CanvasRenderingContext2D::drawImageFromRectCallback(const v8::Arguments& args) 320 { 321 INC_STATS("DOM.CanvasRenderingContext2D.drawImageFromRect()"); 322 CanvasRenderingContext2D* context = V8CanvasRenderingContext2D::toNative(args.Holder()); 323 324 v8::Handle<v8::Value> arg = args[0]; 325 326 if (V8HTMLImageElement::HasInstance(arg)) { 327 HTMLImageElement* imageElement = V8HTMLImageElement::toNative(v8::Handle<v8::Object>::Cast(arg)); 328 context->drawImageFromRect(imageElement, 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])); 329 } else 330 V8Proxy::throwError(V8Proxy::TypeError, "drawImageFromRect: Invalid type of arguments"); 331 332 return v8::Undefined(); 333 } 334 335 v8::Handle<v8::Value> V8CanvasRenderingContext2D::createPatternCallback(const v8::Arguments& args) 336 { 337 INC_STATS("DOM.CanvasRenderingContext2D.createPattern()"); 338 CanvasRenderingContext2D* context = V8CanvasRenderingContext2D::toNative(args.Holder()); 339 340 v8::Handle<v8::Value> arg = args[0]; 341 342 if (V8HTMLImageElement::HasInstance(arg)) { 343 HTMLImageElement* imageElement = V8HTMLImageElement::toNative(v8::Handle<v8::Object>::Cast(arg)); 344 ExceptionCode ec = 0; 345 RefPtr<CanvasPattern> pattern = context->createPattern(imageElement, toWebCoreStringWithNullCheck(args[1]), ec); 346 if (ec != 0) { 347 V8Proxy::setDOMException(ec); 348 return notHandledByInterceptor(); 349 } 350 return toV8(pattern.release()); 351 } 352 353 if (V8HTMLCanvasElement::HasInstance(arg)) { 354 HTMLCanvasElement* canvasElement = V8HTMLCanvasElement::toNative(v8::Handle<v8::Object>::Cast(arg)); 355 ExceptionCode ec = 0; 356 RefPtr<CanvasPattern> pattern = context->createPattern(canvasElement, toWebCoreStringWithNullCheck(args[1]), ec); 357 if (ec != 0) { 358 V8Proxy::setDOMException(ec); 359 return notHandledByInterceptor(); 360 } 361 return toV8(pattern.release()); 362 } 363 364 V8Proxy::setDOMException(TYPE_MISMATCH_ERR); 365 return notHandledByInterceptor(); 366 } 367 368 v8::Handle<v8::Value> V8CanvasRenderingContext2D::fillTextCallback(const v8::Arguments& args) 369 { 370 INC_STATS("DOM.CanvasRenderingContext2D.fillText()"); 371 372 CanvasRenderingContext2D* context = V8CanvasRenderingContext2D::toNative(args.Holder()); 373 374 // Two forms: 375 // * fillText(text, x, y) 376 // * fillText(text, x, y, maxWidth) 377 if (args.Length() < 3 || args.Length() > 4) { 378 V8Proxy::setDOMException(SYNTAX_ERR); 379 return notHandledByInterceptor(); 380 } 381 382 String text = toWebCoreString(args[0]); 383 float x = toFloat(args[1]); 384 float y = toFloat(args[2]); 385 386 if (args.Length() == 4) { 387 float maxWidth = toFloat(args[3]); 388 context->fillText(text, x, y, maxWidth); 389 } else 390 context->fillText(text, x, y); 391 392 return v8::Undefined(); 393 } 394 395 v8::Handle<v8::Value> V8CanvasRenderingContext2D::strokeTextCallback(const v8::Arguments& args) 396 { 397 INC_STATS("DOM.CanvasRenderingContext2D.strokeText()"); 398 CanvasRenderingContext2D* context = V8CanvasRenderingContext2D::toNative(args.Holder()); 399 400 // Two forms: 401 // * strokeText(text, x, y) 402 // * strokeText(text, x, y, maxWidth) 403 if (args.Length() < 3 || args.Length() > 4) { 404 V8Proxy::setDOMException(SYNTAX_ERR); 405 return notHandledByInterceptor(); 406 } 407 408 String text = toWebCoreString(args[0]); 409 float x = toFloat(args[1]); 410 float y = toFloat(args[2]); 411 412 if (args.Length() == 4) { 413 float maxWidth = toFloat(args[3]); 414 context->strokeText(text, x, y, maxWidth); 415 } else 416 context->strokeText(text, x, y); 417 418 return v8::Undefined(); 419 } 420 421 v8::Handle<v8::Value> V8CanvasRenderingContext2D::putImageDataCallback(const v8::Arguments& args) 422 { 423 INC_STATS("DOM.CanvasRenderingContext2D.putImageData()"); 424 425 // Two froms: 426 // * putImageData(ImageData, x, y) 427 // * putImageData(ImageData, x, y, dirtyX, dirtyY, dirtyWidth, dirtyHeight) 428 if (args.Length() != 3 && args.Length() != 7) { 429 V8Proxy::setDOMException(SYNTAX_ERR); 430 return notHandledByInterceptor(); 431 } 432 433 CanvasRenderingContext2D* context = V8CanvasRenderingContext2D::toNative(args.Holder()); 434 435 ImageData* imageData = 0; 436 437 // Need to check that the argument is of the correct type, since 438 // toNative() expects it to be correct. If the argument was incorrect 439 // we leave it null, and putImageData() will throw the correct exception 440 // (TYPE_MISMATCH_ERR). 441 if (V8DOMWrapper::isWrapperOfType(args[0], V8ClassIndex::IMAGEDATA)) 442 imageData = V8ImageData::toNative(v8::Handle<v8::Object>::Cast(args[0])); 443 444 ExceptionCode ec = 0; 445 446 if (args.Length() == 7) 447 context->putImageData(imageData, toFloat(args[1]), toFloat(args[2]), toFloat(args[3]), toFloat(args[4]), toFloat(args[5]), toFloat(args[6]), ec); 448 else 449 context->putImageData(imageData, toFloat(args[1]), toFloat(args[2]), ec); 450 451 if (ec != 0) { 452 V8Proxy::setDOMException(ec); 453 return notHandledByInterceptor(); 454 } 455 456 return v8::Undefined(); 457 } 458 459 } // namespace WebCore 460