set y and not x when y should be set
[swfdec.git] / libswfdec / swfdec_js_movie.c
blobb903313d2c1a837392d4371c64f2fe6bfae80311
1 /* Swfdec
2 * Copyright (C) 2003-2006 David Schleef <ds@schleef.org>
3 * 2005-2006 Eric Anholt <eric@anholt.net>
4 * 2006 Benjamin Otte <otte@gnome.org>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include <string.h>
26 #include <math.h>
28 #include <js/jsapi.h>
29 #include "swfdec_js.h"
30 #include "swfdec_movie.h"
31 #include "swfdec_bits.h"
32 #include "swfdec_debug.h"
33 #include "swfdec_decoder.h"
34 #include "swfdec_player_internal.h"
35 #include "swfdec_root_movie.h"
36 #include "swfdec_sprite.h"
37 #include "swfdec_sprite_movie.h"
38 #include "swfdec_swf_decoder.h"
40 JSBool swfdec_js_global_eval (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
42 static void
43 movie_finalize (JSContext *cx, JSObject *obj)
45 SwfdecMovie *movie;
47 movie = JS_GetPrivate (cx, obj);
48 /* since we also finalize the class, not everyone has a private object */
49 if (movie) {
50 g_assert (movie->jsobj != NULL);
52 SWFDEC_LOG ("destroying JSObject %p for movie %p", obj, movie);
53 movie->jsobj = NULL;
54 g_object_unref (movie);
55 } else {
56 SWFDEC_LOG ("destroying JSObject %p", obj);
60 static JSClass movieclip_class = {
61 "MovieClip", JSCLASS_HAS_PRIVATE,
62 JS_PropertyStub, JS_PropertyStub,
63 JS_PropertyStub, JS_PropertyStub,
64 JS_EnumerateStub, JS_ResolveStub,
65 JS_ConvertStub, movie_finalize,
66 JSCLASS_NO_OPTIONAL_MEMBERS
69 static JSBool
70 mc_play (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
72 SwfdecMovie *movie;
74 movie = JS_GetPrivate(cx, obj);
75 g_assert (movie);
76 movie->stopped = FALSE;
78 return JS_TRUE;
81 static JSBool
82 mc_stop (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
84 SwfdecMovie *movie;
86 movie = JS_GetPrivate(cx, obj);
87 g_assert (movie);
88 movie->stopped = TRUE;
90 return JS_TRUE;
93 static JSBool
94 mc_getBytesLoaded (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
96 SwfdecMovie *movie;
97 SwfdecDecoder *dec;
99 movie = JS_GetPrivate(cx, obj);
100 dec = SWFDEC_ROOT_MOVIE (movie->root)->decoder;
102 *rval = INT_TO_JSVAL(MIN (dec->bytes_loaded, dec->bytes_total));
104 return JS_TRUE;
107 static JSBool
108 mc_getBytesTotal(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
110 SwfdecMovie *movie;
111 SwfdecDecoder *dec;
113 movie = JS_GetPrivate(cx, obj);
114 dec = SWFDEC_ROOT_MOVIE (movie->root)->decoder;
116 *rval = INT_TO_JSVAL (dec->bytes_total);
118 return JS_TRUE;
121 static JSBool
122 mc_do_goto (JSContext *cx, SwfdecMovie *movie, jsval target)
124 int32 frame;
126 if (JSVAL_IS_STRING (target)) {
127 const char *label = swfdec_js_to_string (cx, target);
128 frame = swfdec_sprite_get_frame (SWFDEC_SPRITE_MOVIE (movie)->sprite, label);
129 /* FIXME: nonexisting frames? */
130 if (frame == -1)
131 return JS_TRUE;
132 frame++;
133 } else if (!JS_ValueToInt32 (cx, target, &frame)) {
134 return JS_FALSE;
136 /* FIXME: how to handle overflow? */
137 frame = CLAMP (frame, 1, (int) movie->n_frames) - 1;
139 swfdec_movie_goto (movie, frame);
140 return JS_TRUE;
143 static JSBool
144 mc_gotoAndPlay (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
146 SwfdecMovie *movie;
148 movie = JS_GetPrivate(cx, obj);
149 g_assert (movie);
151 if (!mc_do_goto (cx, movie, argv[0]))
152 return JS_FALSE;
153 movie->stopped = FALSE;
154 return JS_TRUE;
157 static JSBool
158 mc_gotoAndStop (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
160 SwfdecMovie *movie;
162 movie = JS_GetPrivate(cx, obj);
163 g_assert (movie);
165 if (!mc_do_goto (cx, movie, argv[0]))
166 return JS_FALSE;
167 movie->stopped = TRUE;
168 return JS_TRUE;
171 static JSBool
172 swfdec_js_nextFrame (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
174 SwfdecMovie *movie;
175 jsval frame;
177 movie = JS_GetPrivate(cx, obj);
178 g_assert (movie);
180 frame = INT_TO_JSVAL (movie->frame + 2); /* 1-indexed */
181 if (!mc_do_goto (cx, movie, frame))
182 return JS_FALSE;
183 movie->stopped = TRUE;
184 return JS_TRUE;
187 static JSBool
188 swfdec_js_prevFrame (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
190 SwfdecMovie *movie;
191 jsval frame;
193 movie = JS_GetPrivate(cx, obj);
194 g_assert (movie);
196 if (movie->frame == 0)
197 frame = INT_TO_JSVAL (movie->n_frames);
198 else
199 frame = INT_TO_JSVAL (movie->frame); /* 1-indexed */
200 if (!mc_do_goto (cx, movie, frame))
201 return JS_FALSE;
202 movie->stopped = TRUE;
203 return JS_TRUE;
206 static JSBool
207 mc_hitTest (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
209 SwfdecMovie *movie;
211 movie = JS_GetPrivate(cx, obj);
212 g_assert (movie);
214 if (argc == 1) {
215 SwfdecMovie *other;
216 other = swfdec_js_val_to_movie (cx, argv[0]);
217 if (other == NULL) {
218 g_assert_not_reached ();
219 return JS_TRUE;
221 other = SWFDEC_MOVIE (JS_GetPrivate(cx, JSVAL_TO_OBJECT (argv[0])));
222 swfdec_movie_update (movie);
223 swfdec_movie_update (other);
224 /* FIXME */
225 g_assert (movie->parent == other->parent);
226 #if 0
227 g_print ("%g %g %g %g --- %g %g %g %g\n",
228 SWFDEC_OBJECT (movie)->extents.x0, SWFDEC_OBJECT (movie)->extents.y0,
229 SWFDEC_OBJECT (movie)->extents.x1, SWFDEC_OBJECT (movie)->extents.y1,
230 SWFDEC_OBJECT (other)->extents.x0, SWFDEC_OBJECT (other)->extents.y0,
231 SWFDEC_OBJECT (other)->extents.x1, SWFDEC_OBJECT (other)->extents.y1);
232 #endif
233 if (swfdec_rect_intersect (NULL, &movie->extents, &other->extents)) {
234 *rval = BOOLEAN_TO_JSVAL (JS_TRUE);
235 } else {
236 *rval = BOOLEAN_TO_JSVAL (JS_FALSE);
238 } else if (argc == 3) {
239 g_assert_not_reached ();
240 } else {
241 return JS_FALSE;
244 return JS_TRUE;
247 static JSPropertySpec movieclip_props[];
249 static JSBool
250 swfdec_js_getProperty (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
252 uint32 id;
253 SwfdecMovie *movie;
254 jsval tmp;
256 swfdec_js_global_eval (cx, obj, 1, argv, &tmp);
257 movie = swfdec_js_val_to_movie (cx, tmp);
258 if (movie == NULL) {
259 SWFDEC_WARNING ("specified target does not reference a movie clip");
260 return JS_TRUE;
262 if (!JS_ValueToECMAUint32 (cx, argv[1], &id))
263 return JS_FALSE;
265 if (id > 19)
266 return JS_FALSE;
268 if (movie->jsobj == NULL &&
269 !swfdec_js_add_movie (movie))
270 return JS_FALSE;
271 return movieclip_props[id].getter (cx, movie->jsobj, INT_TO_JSVAL (id) /* FIXME */, rval);
274 static JSBool
275 swfdec_js_setProperty (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
277 uint32 id;
278 SwfdecMovie *movie;
279 jsval tmp;
281 swfdec_js_global_eval (cx, obj, 1, argv, &tmp);
282 movie = swfdec_js_val_to_movie (cx, tmp);
283 if (movie == NULL) {
284 SWFDEC_WARNING ("specified target does not reference a movie clip");
285 return JS_TRUE;
287 if (!JS_ValueToECMAUint32 (cx, argv[1], &id))
288 return JS_FALSE;
290 if (id > 19)
291 return JS_FALSE;
293 if (movie->jsobj == NULL &&
294 !swfdec_js_add_movie (movie))
295 return JS_FALSE;
296 *rval = argv[2];
297 return movieclip_props[id].setter (cx, movie->jsobj, INT_TO_JSVAL (id) /* FIXME */, rval);
300 static JSBool
301 swfdec_js_startDrag (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
303 SwfdecMovie *movie;
304 JSBool center = JS_FALSE;
305 SwfdecRect rect;
307 movie = JS_GetPrivate (cx, obj);
308 g_assert (movie);
309 if (argc > 0) {
310 if (!JS_ValueToBoolean (cx, argv[0], &center))
311 return JS_FALSE;
313 if (argc >= 5) {
314 if (!JS_ValueToNumber (cx, argv[1], &rect.x0) ||
315 !JS_ValueToNumber (cx, argv[2], &rect.y0) ||
316 !JS_ValueToNumber (cx, argv[3], &rect.x1) ||
317 !JS_ValueToNumber (cx, argv[4], &rect.y1))
318 return JS_FALSE;
319 swfdec_rect_scale (&rect, &rect, SWFDEC_TWIPS_SCALE_FACTOR);
320 swfdec_player_set_drag_movie (SWFDEC_ROOT_MOVIE (movie->root)->player, movie,
321 center, &rect);
322 } else {
323 swfdec_player_set_drag_movie (SWFDEC_ROOT_MOVIE (movie->root)->player, movie,
324 center, NULL);
327 return JS_TRUE;
330 static JSBool
331 swfdec_js_stopDrag (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
333 SwfdecMovie *movie;
334 SwfdecPlayer *player;
336 movie = JS_GetPrivate (cx, obj);
337 g_assert (movie);
338 player = SWFDEC_ROOT_MOVIE (movie->root)->player;
339 swfdec_player_set_drag_movie (player, NULL, FALSE, NULL);
340 return JS_TRUE;
343 static JSBool
344 swfdec_js_movie_swapDepths (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
346 SwfdecMovie *movie;
347 SwfdecMovie *other;
348 int depth;
350 movie = JS_GetPrivate (cx, obj);
351 g_assert (movie);
353 if (JSVAL_IS_OBJECT (argv[0])) {
354 other = swfdec_js_val_to_movie (cx, argv[0]);
355 if (other == NULL)
356 return JS_TRUE;
357 if (other->parent != movie->parent)
358 return JS_TRUE;
359 swfdec_movie_invalidate (other);
360 depth = other->depth;
361 other->depth = movie->depth;
362 } else {
363 if (!JS_ValueToECMAInt32 (cx, argv[0], &depth))
364 return JS_FALSE;
365 other = swfdec_movie_find (movie->parent, depth);
366 if (other) {
367 swfdec_movie_invalidate (other);
368 other->depth = movie->depth;
371 swfdec_movie_invalidate (movie);
372 movie->depth = depth;
373 movie->parent->list = g_list_sort (movie->parent->list, swfdec_movie_compare_depths);
374 return JS_TRUE;
377 static void
378 swfdec_js_copy_props (SwfdecMovie *target, SwfdecMovie *src)
380 target->matrix = src->matrix;
381 target->color_transform = src->color_transform;
382 swfdec_movie_queue_update (target, SWFDEC_MOVIE_INVALID_MATRIX);
385 static JSBool
386 swfdec_js_movie_duplicateMovieClip (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
388 SwfdecMovie *movie, *ret;
389 const char *name;
390 int depth;
391 SwfdecContent *content;
393 movie = JS_GetPrivate (cx, obj);
394 g_assert (movie);
396 #if 0
397 /* FIXME: is this still valid? */
398 if (argc > 2) {
399 const char *str;
400 jsval val;
401 str = swfdec_js_to_string (cx, argv[0]);
402 if (!str)
403 return JS_TRUE;
404 val = swfdec_js_eval (cx, obj, str);
405 movie = swfdec_js_val_to_movie (cx, val);
406 if (!movie)
407 return JS_TRUE;
408 argv++;
409 argc--;
411 #endif
412 name = swfdec_js_to_string (cx, argv[0]);
413 if (name == NULL)
414 return JS_FALSE;
415 if (!JS_ValueToECMAInt32 (cx, argv[1], &depth))
416 return JS_FALSE;
417 if (swfdec_depth_classify (depth) == SWFDEC_DEPTH_CLASS_EMPTY)
418 return JS_TRUE;
419 g_assert (movie->parent);
420 ret = swfdec_movie_find (movie->parent, depth);
421 if (ret)
422 swfdec_movie_remove (ret);
423 content = swfdec_content_new (depth);
424 *content = *movie->content;
425 if (content->events)
426 content->events = swfdec_event_list_copy (content->events);
427 content->depth = depth;
428 content->clip_depth = 0; /* FIXME: check this */
429 content->name = g_strdup (name);
430 content->sequence = content;
431 content->start = 0;
432 content->end = G_MAXUINT;
433 ret = swfdec_movie_new (movie->parent, content);
434 g_object_weak_ref (G_OBJECT (ret), (GWeakNotify) swfdec_content_free, content);
435 /* must be set by now, the movie has a name */
436 if (ret->jsobj == NULL)
437 return JS_FALSE;
438 swfdec_js_copy_props (ret, movie);
439 SWFDEC_LOG ("duplicated %s as %s to depth %u", movie->name, ret->name, ret->depth);
440 *rval = OBJECT_TO_JSVAL (ret->jsobj);
441 return JS_TRUE;
444 static JSBool
445 swfdec_js_movie_removeMovieClip (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
447 SwfdecMovie *movie;
449 movie = JS_GetPrivate (cx, obj);
450 g_assert (movie);
452 if (swfdec_depth_classify (movie->depth) == SWFDEC_DEPTH_CLASS_DYNAMIC)
453 swfdec_movie_remove (movie);
454 return JS_TRUE;
457 static JSBool
458 swfdec_js_getURL (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
460 const char *url;
461 const char *target;
462 SwfdecMovie *movie;
464 movie = JS_GetPrivate (cx, obj);
465 g_assert (movie);
466 url = swfdec_js_to_string (cx, argv[0]);
467 if (!url)
468 return FALSE;
469 if (argc > 1) {
470 target = swfdec_js_to_string (cx, argv[1]);
471 if (!target)
472 return JS_FALSE;
473 } else {
474 /* FIXME: figure out default target */
475 g_assert_not_reached ();
477 if (argc > 2) {
478 /* variables not implemented yet */
479 g_assert_not_reached ();
481 swfdec_root_movie_load (SWFDEC_ROOT_MOVIE (movie->root), url, target);
482 return JS_TRUE;
485 static GString *
486 get_name (SwfdecMovie *movie)
488 GString *s;
490 if (movie->parent) {
491 s = get_name (movie->parent);
492 g_string_append_c (s, '.');
493 g_string_append (s, movie->name);
494 } else {
495 /* the name can be changed */
496 s = g_string_new ("_level");
497 g_string_append_printf (s, "%u", movie->depth + 16384);
499 return s;
502 static JSBool
503 swfdec_js_movie_to_string (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
505 GString *s;
506 JSString *string;
507 SwfdecMovie *movie;
509 movie = JS_GetPrivate (cx, obj);
510 g_assert (movie);
512 s = get_name (movie);
513 string = JS_NewStringCopyZ (cx, s->str);
514 g_string_free (s, TRUE);
515 if (string == NULL)
516 return JS_FALSE;
517 *rval = STRING_TO_JSVAL (string);
518 return JS_TRUE;
521 static JSFunctionSpec movieclip_methods[] = {
522 { "duplicateMovieClip", swfdec_js_movie_duplicateMovieClip, 2, 0, 0 },
523 { "eval", swfdec_js_global_eval, 1, 0, 0 },
524 { "getBytesLoaded", mc_getBytesLoaded, 0, 0, 0 },
525 { "getBytesTotal", mc_getBytesTotal, 0, 0, 0 },
526 { "getProperty", swfdec_js_getProperty, 2, 0, 0 },
527 { "getURL", swfdec_js_getURL, 2, 0, 0 },
528 { "gotoAndPlay", mc_gotoAndPlay, 1, 0, 0 },
529 { "gotoAndStop", mc_gotoAndStop, 1, 0, 0 },
530 { "hitTest", mc_hitTest, 1, 0, 0 },
531 { "nextFrame", swfdec_js_nextFrame, 0, 0, 0 },
532 { "play", mc_play, 0, 0, 0 },
533 { "prevFrame", swfdec_js_prevFrame, 0, 0, 0 },
534 { "removeMovieClip", swfdec_js_movie_removeMovieClip,0, 0, 0 },
535 { "setProperty", swfdec_js_setProperty, 3, 0, 0 },
536 { "startDrag", swfdec_js_startDrag, 0, 0, 0 },
537 { "stop", mc_stop, 0, 0, 0 },
538 { "stopDrag", swfdec_js_stopDrag, 0, 0, 0 },
539 { "swapDepths", swfdec_js_movie_swapDepths, 1, 0, 0 },
540 { "toString", swfdec_js_movie_to_string, 0, 0, 0 },
541 { NULL }
544 static JSBool
545 mc_x_get(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
547 SwfdecMovie *movie;
548 double d;
550 movie = JS_GetPrivate (cx, obj);
551 g_assert (movie);
553 d = SWFDEC_TWIPS_TO_DOUBLE (movie->matrix.x0);
554 return JS_NewNumberValue (cx, d, vp);
557 static JSBool
558 mc_x_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
560 SwfdecMovie *movie;
561 double d;
563 movie = JS_GetPrivate (cx, obj);
564 g_assert (movie);
566 if (!JS_ValueToNumber (cx, *vp, &d))
567 return JS_FALSE;
568 if (!finite (d)) {
569 SWFDEC_WARNING ("trying to move x to a non-finite value, ignoring");
570 return JS_TRUE;
572 movie->modified = TRUE;
573 d = SWFDEC_DOUBLE_TO_TWIPS (d);
574 if (d != movie->matrix.x0) {
575 movie->matrix.x0 = d;
576 swfdec_movie_queue_update (movie, SWFDEC_MOVIE_INVALID_MATRIX);
579 return JS_TRUE;
582 static JSBool
583 mc_y_get(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
585 SwfdecMovie *movie;
586 double d;
588 movie = JS_GetPrivate (cx, obj);
589 g_assert (movie);
591 swfdec_movie_update (movie);
592 d = SWFDEC_TWIPS_TO_DOUBLE (movie->matrix.y0);
593 return JS_NewNumberValue (cx, d, vp);
596 static JSBool
597 mc_y_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
599 SwfdecMovie *movie;
600 double d;
602 movie = JS_GetPrivate (cx, obj);
603 g_assert (movie);
605 if (!JS_ValueToNumber (cx, *vp, &d))
606 return JS_FALSE;
607 if (!finite (d)) {
608 SWFDEC_WARNING ("trying to move y to a non-finite value, ignoring");
609 return JS_TRUE;
611 movie->modified = TRUE;
612 d = SWFDEC_DOUBLE_TO_TWIPS (d);
613 if (d != movie->matrix.y0) {
614 movie->matrix.y0 = d;
615 swfdec_movie_queue_update (movie, SWFDEC_MOVIE_INVALID_MATRIX);
618 return JS_TRUE;
621 static JSBool
622 mc_xscale_get (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
624 SwfdecMovie *movie;
625 double d;
627 movie = JS_GetPrivate (cx, obj);
628 g_assert (movie);
630 d = movie->xscale;
631 return JS_NewNumberValue (cx, d, vp);
634 static JSBool
635 mc_xscale_set (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
637 SwfdecMovie *movie;
638 double d;
640 movie = JS_GetPrivate (cx, obj);
641 g_assert (movie);
643 if (!JS_ValueToNumber (cx, *vp, &d))
644 return JS_FALSE;
645 if (!finite (d)) {
646 SWFDEC_WARNING ("trying to set xscale to a non-finite value, ignoring");
647 return JS_TRUE;
649 movie->modified = TRUE;
650 movie->xscale = d;
651 swfdec_movie_queue_update (movie, SWFDEC_MOVIE_INVALID_MATRIX);
653 return JS_TRUE;
656 static JSBool
657 mc_yscale_get (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
659 SwfdecMovie *movie;
660 double d;
662 movie = JS_GetPrivate (cx, obj);
663 g_assert (movie);
665 d = movie->yscale;
666 return JS_NewNumberValue (cx, d, vp);
669 static JSBool
670 mc_yscale_set (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
672 SwfdecMovie *movie;
673 double d;
675 movie = JS_GetPrivate (cx, obj);
676 g_assert (movie);
678 if (!JS_ValueToNumber (cx, *vp, &d))
679 return JS_FALSE;
680 if (!finite (d)) {
681 SWFDEC_WARNING ("trying to set yscale to a non-finite value, ignoring");
682 return JS_TRUE;
684 movie->modified = TRUE;
685 movie->yscale = d;
686 swfdec_movie_queue_update (movie, SWFDEC_MOVIE_INVALID_MATRIX);
688 return JS_TRUE;
691 static JSBool
692 mc_currentframe (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
694 SwfdecMovie *movie;
696 movie = JS_GetPrivate (cx, obj);
697 g_assert (movie);
699 *vp = INT_TO_JSVAL (movie->frame + 1);
701 return JS_TRUE;
704 static JSBool
705 mc_framesloaded (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
707 SwfdecMovie *movie;
708 guint loaded;
710 movie = JS_GetPrivate (cx, obj);
711 g_assert (movie);
713 /* only root movies can be partially loaded */
714 if (SWFDEC_IS_ROOT_MOVIE (movie) ||
715 SWFDEC_IS_ROOT_MOVIE (movie->parent)) {
716 SwfdecDecoder *dec = SWFDEC_ROOT_MOVIE (movie->root)->decoder;
717 loaded = dec->frames_loaded;
718 g_assert (loaded <= movie->n_frames);
719 } else {
720 loaded = movie->n_frames;
722 *vp = INT_TO_JSVAL (loaded);
724 return JS_TRUE;
727 static JSBool
728 mc_name_get (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
730 SwfdecMovie *movie;
731 JSString *string;
733 movie = JS_GetPrivate (cx, obj);
734 g_assert (movie);
736 if (movie->has_name)
737 string = JS_NewStringCopyZ (cx, movie->name);
738 else
739 string = JS_NewStringCopyZ (cx, "");
740 if (string == NULL)
741 return JS_FALSE;
742 *vp = STRING_TO_JSVAL (string);
744 return JS_TRUE;
747 static JSBool
748 mc_name_set (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
750 SwfdecMovie *movie;
751 const char *str;
753 movie = JS_GetPrivate (cx, obj);
754 g_assert (movie);
756 str = swfdec_js_to_string (cx, *vp);
757 if (str == NULL)
758 return JS_FALSE;
759 if (!SWFDEC_IS_ROOT_MOVIE (movie))
760 swfdec_js_movie_remove_property (movie);
761 g_free (movie->name);
762 movie->name = g_strdup (str);
763 movie->has_name = TRUE;
764 if (!SWFDEC_IS_ROOT_MOVIE (movie))
765 swfdec_js_movie_add_property (movie);
767 return JS_TRUE;
770 static JSBool
771 mc_totalframes (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
773 SwfdecMovie *movie;
775 movie = JS_GetPrivate (cx, obj);
776 g_assert (movie);
778 *vp = INT_TO_JSVAL (movie->n_frames);
780 return JS_TRUE;
783 static JSBool
784 mc_alpha_get (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
786 SwfdecMovie *movie;
787 double d;
789 movie = JS_GetPrivate (cx, obj);
790 g_assert (movie);
792 d = movie->color_transform.aa * 100.0 / 256.0;
793 return JS_NewNumberValue (cx, d, vp);
796 static JSBool
797 mc_alpha_set (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
799 SwfdecMovie *movie;
800 double d;
801 int alpha;
803 movie = JS_GetPrivate (cx, obj);
804 g_assert (movie);
806 if (!JS_ValueToNumber (cx, *vp, &d))
807 return JS_TRUE;
808 alpha = d * 256.0 / 100.0;
809 if (alpha != movie->color_transform.aa) {
810 movie->color_transform.aa = alpha;
811 swfdec_movie_invalidate (movie);
813 return JS_TRUE;
816 static JSBool
817 mc_visible_get (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
819 SwfdecMovie *movie;
821 movie = JS_GetPrivate (cx, obj);
822 g_assert (movie);
824 *vp = BOOLEAN_TO_JSVAL (movie->visible ? JS_TRUE : JS_FALSE);
825 return JS_TRUE;
828 static JSBool
829 mc_visible_set (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
831 SwfdecMovie *movie;
832 JSBool b;
834 movie = JS_GetPrivate (cx, obj);
835 g_assert (movie);
837 if (!JS_ValueToBoolean (cx, *vp, &b))
838 return JS_TRUE;
839 /* is there an xor in C? :o */
840 if ((b && !movie->visible) || (!b && movie->visible)) {
841 movie->visible = !movie->visible;
842 swfdec_movie_invalidate (movie);
844 return JS_TRUE;
847 static JSBool
848 mc_width_get (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
850 SwfdecMovie *movie;
851 double d;
853 movie = JS_GetPrivate (cx, obj);
854 g_assert (movie);
856 swfdec_movie_update (movie);
857 d = SWFDEC_TWIPS_TO_DOUBLE ((SwfdecTwips) (movie->extents.x1 - movie->extents.x0));
858 return JS_NewNumberValue (cx, d, vp);
861 static JSBool
862 mc_width_set (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
864 SwfdecMovie *movie;
865 double d, cur;
867 movie = JS_GetPrivate (cx, obj);
868 g_assert (movie);
870 /* property was readonly in Flash 4 and before */
871 if (SWFDEC_SWF_DECODER (SWFDEC_ROOT_MOVIE (movie->root)->decoder)->version < 5)
872 return JS_TRUE;
873 if (!JS_ValueToNumber (cx, *vp, &d))
874 return JS_FALSE;
875 if (!finite (d)) {
876 SWFDEC_WARNING ("trying to set height to a non-finite value, ignoring");
877 return JS_TRUE;
879 swfdec_movie_update (movie);
880 movie->modified = TRUE;
881 cur = SWFDEC_TWIPS_TO_DOUBLE ((SwfdecTwips) (movie->extents.x1 - movie->extents.x0));
882 if (cur != 0) {
883 movie->xscale *= d / cur;
884 } else {
885 movie->xscale = 0;
886 movie->yscale = 0;
888 swfdec_movie_queue_update (movie, SWFDEC_MOVIE_INVALID_MATRIX);
890 return JS_TRUE;
893 static JSBool
894 mc_height_get (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
896 SwfdecMovie *movie;
897 double d;
899 movie = JS_GetPrivate (cx, obj);
900 g_assert (movie);
902 swfdec_movie_update (movie);
903 d = SWFDEC_TWIPS_TO_DOUBLE ((SwfdecTwips) (movie->extents.y1 - movie->extents.y0));
904 return JS_NewNumberValue (cx, d, vp);
907 static JSBool
908 mc_height_set (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
910 SwfdecMovie *movie;
911 double d, cur;
913 movie = JS_GetPrivate (cx, obj);
914 g_assert (movie);
916 /* property was readonly in Flash 4 and before */
917 if (SWFDEC_SWF_DECODER (SWFDEC_ROOT_MOVIE (movie->root)->decoder)->version < 5)
918 return JS_TRUE;
919 if (!JS_ValueToNumber (cx, *vp, &d))
920 return JS_FALSE;
921 if (!finite (d)) {
922 SWFDEC_WARNING ("trying to set height to a non-finite value, ignoring");
923 return JS_TRUE;
925 swfdec_movie_update (movie);
926 movie->modified = TRUE;
927 cur = SWFDEC_TWIPS_TO_DOUBLE ((SwfdecTwips) (movie->extents.y1 - movie->extents.y0));
928 if (cur != 0) {
929 movie->yscale *= d / cur;
930 } else {
931 movie->xscale = 0;
932 movie->yscale = 0;
934 swfdec_movie_queue_update (movie, SWFDEC_MOVIE_INVALID_MATRIX);
936 return JS_TRUE;
939 static JSBool
940 mc_rotation_get (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
942 SwfdecMovie *movie;
944 movie = JS_GetPrivate (cx, obj);
945 g_assert (movie);
947 return JS_NewNumberValue (cx, movie->rotation, vp);
950 static JSBool
951 mc_rotation_set (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
953 SwfdecMovie *movie;
954 double d;
956 movie = JS_GetPrivate (cx, obj);
957 g_assert (movie);
959 /* FIXME: Flash 4 handles this differently */
960 if (!JS_ValueToNumber (cx, *vp, &d))
961 return JS_FALSE;
962 d = fmod (d, 360.0);
963 if (d > 180.0)
964 d -= 360.0;
965 if (d < -180.0)
966 d += 360.0;
967 if (SWFDEC_SWF_DECODER (SWFDEC_ROOT_MOVIE (movie->root)->decoder)->version < 5) {
968 if (!finite (d))
969 return JS_TRUE;
970 SWFDEC_ERROR ("FIXME: implement correct rounding errors here");
972 movie->modified = TRUE;
973 movie->rotation = d;
974 swfdec_movie_queue_update (movie, SWFDEC_MOVIE_INVALID_MATRIX);
976 return JS_TRUE;
979 static JSBool
980 mc_parent (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
982 SwfdecMovie *movie;
984 movie = JS_GetPrivate (cx, obj);
985 g_assert (movie);
987 /* FIXME: what do we do if we're the root movie? */
988 if (movie->parent)
989 movie = movie->parent;
991 if (movie->jsobj == NULL)
992 swfdec_js_add_movie (movie);
993 if (movie->jsobj == NULL)
994 return JS_FALSE;
996 *vp = OBJECT_TO_JSVAL (movie->jsobj);
998 return JS_TRUE;
1001 static JSBool
1002 mc_root (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1004 SwfdecMovie *movie;
1006 movie = JS_GetPrivate (cx, obj);
1007 g_assert (movie);
1009 movie = movie->root;
1010 if (movie->jsobj == NULL) {
1011 /* the root movie only holds this as long as there's no parent */
1012 movie = movie->list->data;
1013 if (movie->jsobj == NULL)
1014 swfdec_js_add_movie (movie);
1016 if (movie->jsobj == NULL)
1017 return JS_FALSE;
1019 *vp = OBJECT_TO_JSVAL (movie->jsobj);
1021 return JS_TRUE;
1024 /* Movie AS standard class */
1026 enum {
1027 PROP_X,
1028 PROP_Y,
1029 PROP_XSCALE,
1030 PROP_YSCALE,
1031 PROP_CURRENTFRAME,
1032 PROP_TOTALFRAMES,
1033 PROP_ALPHA,
1034 PROP_VISIBLE,
1035 PROP_WIDTH,
1036 PROP_HEIGHT,
1037 PROP_ROTATION,
1038 PROP_FRAMESLOADED,
1039 PROP_NAME,
1040 PROP_DROPTARGET,
1041 PROP_URL,
1042 PROP_HIGHQUALITY,
1043 PROP_FOCUSRECT,
1044 PROP_SOUNDBUFTIME,
1045 PROP_QUALITY,
1046 PROP_XMOUSE,
1047 PROP_YMOUSE
1050 static JSBool
1051 not_reached (JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1053 #ifndef SWFDEC_DISABLE_DEBUG
1054 const char *str = swfdec_js_to_string (cx, id);
1055 #endif
1056 SWFDEC_ERROR ("reading and writing property %s is not implemented", str);
1057 return JS_TRUE;
1060 /* NB: order needs to be kept for GetProperty/SetProperty actions */
1061 #define MC_PROP_ATTRS (JSPROP_PERMANENT|JSPROP_SHARED)
1062 static JSPropertySpec movieclip_props[] = {
1063 {"_x", -1, MC_PROP_ATTRS, mc_x_get, mc_x_set },
1064 {"_y", -1, MC_PROP_ATTRS, mc_y_get, mc_y_set },
1065 {"_xscale", -1, MC_PROP_ATTRS, mc_xscale_get, mc_xscale_set },
1066 {"_yscale", -1, MC_PROP_ATTRS, mc_yscale_get, mc_yscale_set },
1067 {"_currentframe", -1, MC_PROP_ATTRS | JSPROP_READONLY, mc_currentframe, NULL },
1068 {"_totalframes", -1, MC_PROP_ATTRS | JSPROP_READONLY, mc_totalframes, NULL },
1069 {"_alpha", -1, MC_PROP_ATTRS, mc_alpha_get, mc_alpha_set },
1070 {"_visible", -1, MC_PROP_ATTRS, mc_visible_get, mc_visible_set },
1071 {"_width", -1, MC_PROP_ATTRS, mc_width_get, mc_width_set },
1072 {"_height", -1, MC_PROP_ATTRS, mc_height_get, mc_height_set },
1073 {"_rotation", -1, MC_PROP_ATTRS, mc_rotation_get, mc_rotation_set },
1074 {"_target", -1, MC_PROP_ATTRS, not_reached, not_reached },
1075 {"_framesloaded", -1, MC_PROP_ATTRS | JSPROP_READONLY, mc_framesloaded, NULL },
1076 {"_name", -1, MC_PROP_ATTRS, mc_name_get, mc_name_set },
1077 {"_droptarget", -1, MC_PROP_ATTRS, not_reached, not_reached },
1078 {"_url", -1, MC_PROP_ATTRS, not_reached, not_reached },
1079 {"_highquality", -1, MC_PROP_ATTRS, not_reached, not_reached },
1080 {"_focusrect", -1, MC_PROP_ATTRS, not_reached, not_reached },
1081 {"_soundbuftime", -1, MC_PROP_ATTRS, not_reached, not_reached },
1082 {"_xmouse", -1, MC_PROP_ATTRS, not_reached, not_reached },
1083 {"_ymouse", -1, MC_PROP_ATTRS, not_reached, not_reached },
1084 {"_parent", -1, MC_PROP_ATTRS | JSPROP_READONLY, mc_parent, NULL},
1085 {"_root", -1, MC_PROP_ATTRS | JSPROP_READONLY, mc_root, NULL},
1086 {NULL}
1089 #if 0
1090 JSObject *
1091 movieclip_find (SwfdecActionContext *context,
1092 SwfdecMovie *movie)
1094 GList *g;
1095 struct mc_list_entry *listentry;
1097 for (g = g_list_first (context->movielist); g; g = g_list_next (g)) {
1098 listentry = (struct mc_list_entry *)g->data;
1100 if (listentry->movie == movie)
1101 return listentry->mc;
1104 return NULL;
1107 static void
1108 swfdec_native_ASSetPropFlags (SwfdecActionContext *context, int num_args,
1109 ActionVal *_this)
1111 ActionVal *a;
1112 ActionVal *b;
1113 ActionVal *c;
1114 ActionVal *d;
1115 int allowFalse = 0;
1116 int flags;
1118 a = stack_pop (context); /* obj */
1119 action_val_convert_to_object (a);
1120 b = stack_pop (context); /* property list */
1121 c = stack_pop (context); /* flags */
1122 action_val_convert_to_number (c);
1123 if (num_args >= 4) {
1124 d = stack_pop (context); /* allowFalse */
1125 action_val_convert_to_boolean (d);
1126 allowFalse = d->number;
1127 action_val_free (d);
1130 flags = (int)c->number & 0x7;
1131 /* The flags appear to be 0x1 for DontEnum, 0x2 for DontDelete, and 0x4 for
1132 * DontWrite, though the tables I found on the web are poorly written.
1135 if (ACTIONVAL_IS_NULL(b)) {
1136 GList *g;
1138 SWFDEC_DEBUG("%d args", num_args);
1140 for (g = g_list_first (a->obj->properties); g; g = g_list_next (g)) {
1141 ScriptObjectProperty *prop = g->data;
1142 if (allowFalse) {
1143 prop->flags = flags;
1144 } else {
1145 prop->flags |= flags;
1148 } else {
1149 action_val_convert_to_string (b);
1150 SWFDEC_WARNING("ASSetPropFlags not implemented (properties %s, flags 0x%x)",
1151 b->string, (int)c->number);
1154 action_val_free (a);
1155 action_val_free (b);
1156 action_val_free (c);
1158 a = action_val_new ();
1159 a->type = ACTIONVAL_TYPE_UNDEF;
1160 stack_push (context, a);
1162 #endif
1164 static JSBool
1165 swfdec_js_movieclip_new (JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1167 SWFDEC_ERROR ("This should not exist, but currently has to for instanceof to work");
1168 return JS_FALSE;
1172 * swfdec_js_add_movieclip_class:
1173 * @player: a @SwfdecPlayer
1175 * Adds the movieclip class to the JS Context of @player.
1177 void
1178 swfdec_js_add_movieclip_class (SwfdecPlayer *player)
1180 JS_InitClass (player->jscx, player->jsobj, NULL,
1181 &movieclip_class, swfdec_js_movieclip_new, 0, movieclip_props, movieclip_methods,
1182 NULL, NULL);
1185 void
1186 swfdec_js_movie_add_property (SwfdecMovie *movie)
1188 JSBool state;
1189 jsval val;
1190 JSObject *jsobj;
1191 JSContext *cx;
1193 if (movie->jsobj == NULL) {
1194 if (!swfdec_js_add_movie (movie))
1195 return;
1197 val = OBJECT_TO_JSVAL (movie->jsobj);
1198 cx = SWFDEC_ROOT_MOVIE (movie->root)->player->jscx;
1199 if (movie->parent) {
1200 jsobj = movie->parent->jsobj;
1201 if (jsobj == NULL)
1202 return;
1203 SWFDEC_LOG ("setting %s as property for %s", movie->name,
1204 movie->parent->name);
1205 } else {
1206 jsobj = SWFDEC_ROOT_MOVIE (movie->root)->player->jsobj;
1207 SWFDEC_LOG ("setting %s as property for _global", movie->name);
1209 state = swfdec_js_push_state (movie);
1210 JS_SetProperty (cx, jsobj, movie->name, &val);
1211 swfdec_js_pop_state (movie, state);
1214 void
1215 swfdec_js_movie_remove_property (SwfdecMovie *movie)
1217 JSBool state;
1218 JSObject *jsobj;
1219 JSContext *cx;
1221 if (movie->jsobj == NULL)
1222 return;
1224 cx = SWFDEC_ROOT_MOVIE (movie->root)->player->jscx;
1225 if (movie->parent) {
1226 jsobj = movie->parent->jsobj;
1227 if (jsobj == NULL)
1228 return;
1229 } else {
1230 jsobj = SWFDEC_ROOT_MOVIE (movie->root)->player->jsobj;
1233 SWFDEC_LOG ("removing %s as property", movie->name);
1234 state = swfdec_js_push_state (movie);
1235 JS_DeleteProperty (cx, jsobj, movie->name);
1236 swfdec_js_pop_state (movie, state);
1240 * swfdec_js_add_movie:
1241 * @movie: a #SwfdecMovie
1243 * Ensures that a JSObject for the given @movie exists.
1245 gboolean
1246 swfdec_js_add_movie (SwfdecMovie *movie)
1248 JSContext *cx;
1249 GList *walk;
1251 g_return_val_if_fail (SWFDEC_IS_MOVIE (movie), FALSE);
1252 g_return_val_if_fail (movie->jsobj == NULL, FALSE);
1254 cx = SWFDEC_ROOT_MOVIE (movie->root)->player->jscx;
1256 movie->jsobj = JS_NewObject (cx, &movieclip_class, NULL, NULL);
1257 if (movie->jsobj == NULL) {
1258 SWFDEC_ERROR ("failed to create JS object for movie %p", movie);
1259 return FALSE;
1261 SWFDEC_LOG ("created JSObject %p for movie %p", movie->jsobj, movie);
1262 g_object_ref (movie);
1263 JS_SetPrivate (cx, movie->jsobj, movie);
1264 /* add all children */
1265 for (walk = movie->list; walk; walk = walk->next) {
1266 SwfdecMovie *child = walk->data;
1267 if (child->has_name)
1268 swfdec_js_movie_add_property (child);
1270 return TRUE;
1274 * swfdec_js_val_to_movie:
1275 * @cx: the relevant #JSContext
1276 * @val: value hat might reference a #SwfdecMovie
1278 * Extracts the #SwfdecMovie referenced by @val and returns it. This function
1279 * performs all the necessary error checking to ensure that @val really
1280 * references a movie.
1282 * Returns: the movie referenced or NULL if no movie was referenced.
1284 SwfdecMovie *
1285 swfdec_js_val_to_movie (JSContext *cx, jsval val)
1287 JSObject *object;
1289 if (!JSVAL_IS_OBJECT (val))
1290 return NULL;
1291 object = JSVAL_TO_OBJECT (val);
1292 if (JS_GetClass (object) != &movieclip_class)
1293 return NULL;
1294 return JS_GetPrivate (cx, object);