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
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
);
43 movie_finalize (JSContext
*cx
, JSObject
*obj
)
47 movie
= JS_GetPrivate (cx
, obj
);
48 /* since we also finalize the class, not everyone has a private object */
50 g_assert (movie
->jsobj
!= NULL
);
52 SWFDEC_LOG ("destroying JSObject %p for movie %p", obj
, movie
);
54 g_object_unref (movie
);
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
70 mc_play (JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
74 movie
= JS_GetPrivate(cx
, obj
);
76 movie
->stopped
= FALSE
;
82 mc_stop (JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
86 movie
= JS_GetPrivate(cx
, obj
);
88 movie
->stopped
= TRUE
;
94 mc_getBytesLoaded (JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
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
));
108 mc_getBytesTotal(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
113 movie
= JS_GetPrivate(cx
, obj
);
114 dec
= SWFDEC_ROOT_MOVIE (movie
->root
)->decoder
;
116 *rval
= INT_TO_JSVAL (dec
->bytes_total
);
122 mc_do_goto (JSContext
*cx
, SwfdecMovie
*movie
, jsval target
)
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? */
133 } else if (!JS_ValueToInt32 (cx
, target
, &frame
)) {
136 /* FIXME: how to handle overflow? */
137 frame
= CLAMP (frame
, 1, (int) movie
->n_frames
) - 1;
139 swfdec_movie_goto (movie
, frame
);
144 mc_gotoAndPlay (JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
148 movie
= JS_GetPrivate(cx
, obj
);
151 if (!mc_do_goto (cx
, movie
, argv
[0]))
153 movie
->stopped
= FALSE
;
158 mc_gotoAndStop (JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
162 movie
= JS_GetPrivate(cx
, obj
);
165 if (!mc_do_goto (cx
, movie
, argv
[0]))
167 movie
->stopped
= TRUE
;
172 swfdec_js_nextFrame (JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
177 movie
= JS_GetPrivate(cx
, obj
);
180 frame
= INT_TO_JSVAL (movie
->frame
+ 2); /* 1-indexed */
181 if (!mc_do_goto (cx
, movie
, frame
))
183 movie
->stopped
= TRUE
;
188 swfdec_js_prevFrame (JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
193 movie
= JS_GetPrivate(cx
, obj
);
196 if (movie
->frame
== 0)
197 frame
= INT_TO_JSVAL (movie
->n_frames
);
199 frame
= INT_TO_JSVAL (movie
->frame
); /* 1-indexed */
200 if (!mc_do_goto (cx
, movie
, frame
))
202 movie
->stopped
= TRUE
;
207 mc_hitTest (JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
211 movie
= JS_GetPrivate(cx
, obj
);
216 other
= swfdec_js_val_to_movie (cx
, argv
[0]);
218 g_assert_not_reached ();
221 other
= SWFDEC_MOVIE (JS_GetPrivate(cx
, JSVAL_TO_OBJECT (argv
[0])));
222 swfdec_movie_update (movie
);
223 swfdec_movie_update (other
);
225 g_assert (movie
->parent
== other
->parent
);
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
);
233 if (swfdec_rect_intersect (NULL
, &movie
->extents
, &other
->extents
)) {
234 *rval
= BOOLEAN_TO_JSVAL (JS_TRUE
);
236 *rval
= BOOLEAN_TO_JSVAL (JS_FALSE
);
238 } else if (argc
== 3) {
239 g_assert_not_reached ();
247 static JSPropertySpec movieclip_props
[];
250 swfdec_js_getProperty (JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
256 swfdec_js_global_eval (cx
, obj
, 1, argv
, &tmp
);
257 movie
= swfdec_js_val_to_movie (cx
, tmp
);
259 SWFDEC_WARNING ("specified target does not reference a movie clip");
262 if (!JS_ValueToECMAUint32 (cx
, argv
[1], &id
))
268 if (movie
->jsobj
== NULL
&&
269 !swfdec_js_add_movie (movie
))
271 return movieclip_props
[id
].getter (cx
, movie
->jsobj
, INT_TO_JSVAL (id
) /* FIXME */, rval
);
275 swfdec_js_setProperty (JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
281 swfdec_js_global_eval (cx
, obj
, 1, argv
, &tmp
);
282 movie
= swfdec_js_val_to_movie (cx
, tmp
);
284 SWFDEC_WARNING ("specified target does not reference a movie clip");
287 if (!JS_ValueToECMAUint32 (cx
, argv
[1], &id
))
293 if (movie
->jsobj
== NULL
&&
294 !swfdec_js_add_movie (movie
))
297 return movieclip_props
[id
].setter (cx
, movie
->jsobj
, INT_TO_JSVAL (id
) /* FIXME */, rval
);
301 swfdec_js_startDrag (JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
304 JSBool center
= JS_FALSE
;
307 movie
= JS_GetPrivate (cx
, obj
);
310 if (!JS_ValueToBoolean (cx
, argv
[0], ¢er
))
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
))
319 swfdec_rect_scale (&rect
, &rect
, SWFDEC_TWIPS_SCALE_FACTOR
);
320 swfdec_player_set_drag_movie (SWFDEC_ROOT_MOVIE (movie
->root
)->player
, movie
,
323 swfdec_player_set_drag_movie (SWFDEC_ROOT_MOVIE (movie
->root
)->player
, movie
,
331 swfdec_js_stopDrag (JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
334 SwfdecPlayer
*player
;
336 movie
= JS_GetPrivate (cx
, obj
);
338 player
= SWFDEC_ROOT_MOVIE (movie
->root
)->player
;
339 swfdec_player_set_drag_movie (player
, NULL
, FALSE
, NULL
);
344 swfdec_js_movie_swapDepths (JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
350 movie
= JS_GetPrivate (cx
, obj
);
353 if (JSVAL_IS_OBJECT (argv
[0])) {
354 other
= swfdec_js_val_to_movie (cx
, argv
[0]);
357 if (other
->parent
!= movie
->parent
)
359 swfdec_movie_invalidate (other
);
360 depth
= other
->depth
;
361 other
->depth
= movie
->depth
;
363 if (!JS_ValueToECMAInt32 (cx
, argv
[0], &depth
))
365 other
= swfdec_movie_find (movie
->parent
, depth
);
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
);
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
);
386 swfdec_js_movie_duplicateMovieClip (JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
388 SwfdecMovie
*movie
, *ret
;
391 SwfdecContent
*content
;
393 movie
= JS_GetPrivate (cx
, obj
);
397 /* FIXME: is this still valid? */
401 str
= swfdec_js_to_string (cx
, argv
[0]);
404 val
= swfdec_js_eval (cx
, obj
, str
);
405 movie
= swfdec_js_val_to_movie (cx
, val
);
412 name
= swfdec_js_to_string (cx
, argv
[0]);
415 if (!JS_ValueToECMAInt32 (cx
, argv
[1], &depth
))
417 if (swfdec_depth_classify (depth
) == SWFDEC_DEPTH_CLASS_EMPTY
)
419 g_assert (movie
->parent
);
420 ret
= swfdec_movie_find (movie
->parent
, depth
);
422 swfdec_movie_remove (ret
);
423 content
= swfdec_content_new (depth
);
424 *content
= *movie
->content
;
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
;
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
)
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
);
445 swfdec_js_movie_removeMovieClip (JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
449 movie
= JS_GetPrivate (cx
, obj
);
452 if (swfdec_depth_classify (movie
->depth
) == SWFDEC_DEPTH_CLASS_DYNAMIC
)
453 swfdec_movie_remove (movie
);
458 swfdec_js_getURL (JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
464 movie
= JS_GetPrivate (cx
, obj
);
466 url
= swfdec_js_to_string (cx
, argv
[0]);
470 target
= swfdec_js_to_string (cx
, argv
[1]);
474 /* FIXME: figure out default target */
475 g_assert_not_reached ();
478 /* variables not implemented yet */
479 g_assert_not_reached ();
481 swfdec_root_movie_load (SWFDEC_ROOT_MOVIE (movie
->root
), url
, target
);
486 get_name (SwfdecMovie
*movie
)
491 s
= get_name (movie
->parent
);
492 g_string_append_c (s
, '.');
493 g_string_append (s
, movie
->name
);
495 /* the name can be changed */
496 s
= g_string_new ("_level");
497 g_string_append_printf (s
, "%u", movie
->depth
+ 16384);
503 swfdec_js_movie_to_string (JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
509 movie
= JS_GetPrivate (cx
, obj
);
512 s
= get_name (movie
);
513 string
= JS_NewStringCopyZ (cx
, s
->str
);
514 g_string_free (s
, TRUE
);
517 *rval
= STRING_TO_JSVAL (string
);
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 },
545 mc_x_get(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
550 movie
= JS_GetPrivate (cx
, obj
);
553 d
= SWFDEC_TWIPS_TO_DOUBLE (movie
->matrix
.x0
);
554 return JS_NewNumberValue (cx
, d
, vp
);
558 mc_x_set(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
563 movie
= JS_GetPrivate (cx
, obj
);
566 if (!JS_ValueToNumber (cx
, *vp
, &d
))
569 SWFDEC_WARNING ("trying to move x to a non-finite value, ignoring");
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
);
583 mc_y_get(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
588 movie
= JS_GetPrivate (cx
, obj
);
591 swfdec_movie_update (movie
);
592 d
= SWFDEC_TWIPS_TO_DOUBLE (movie
->matrix
.y0
);
593 return JS_NewNumberValue (cx
, d
, vp
);
597 mc_y_set(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
602 movie
= JS_GetPrivate (cx
, obj
);
605 if (!JS_ValueToNumber (cx
, *vp
, &d
))
608 SWFDEC_WARNING ("trying to move y to a non-finite value, ignoring");
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
);
622 mc_xscale_get (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
627 movie
= JS_GetPrivate (cx
, obj
);
631 return JS_NewNumberValue (cx
, d
, vp
);
635 mc_xscale_set (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
640 movie
= JS_GetPrivate (cx
, obj
);
643 if (!JS_ValueToNumber (cx
, *vp
, &d
))
646 SWFDEC_WARNING ("trying to set xscale to a non-finite value, ignoring");
649 movie
->modified
= TRUE
;
651 swfdec_movie_queue_update (movie
, SWFDEC_MOVIE_INVALID_MATRIX
);
657 mc_yscale_get (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
662 movie
= JS_GetPrivate (cx
, obj
);
666 return JS_NewNumberValue (cx
, d
, vp
);
670 mc_yscale_set (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
675 movie
= JS_GetPrivate (cx
, obj
);
678 if (!JS_ValueToNumber (cx
, *vp
, &d
))
681 SWFDEC_WARNING ("trying to set yscale to a non-finite value, ignoring");
684 movie
->modified
= TRUE
;
686 swfdec_movie_queue_update (movie
, SWFDEC_MOVIE_INVALID_MATRIX
);
692 mc_currentframe (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
696 movie
= JS_GetPrivate (cx
, obj
);
699 *vp
= INT_TO_JSVAL (movie
->frame
+ 1);
705 mc_framesloaded (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
710 movie
= JS_GetPrivate (cx
, obj
);
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
);
720 loaded
= movie
->n_frames
;
722 *vp
= INT_TO_JSVAL (loaded
);
728 mc_name_get (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
733 movie
= JS_GetPrivate (cx
, obj
);
737 string
= JS_NewStringCopyZ (cx
, movie
->name
);
739 string
= JS_NewStringCopyZ (cx
, "");
742 *vp
= STRING_TO_JSVAL (string
);
748 mc_name_set (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
753 movie
= JS_GetPrivate (cx
, obj
);
756 str
= swfdec_js_to_string (cx
, *vp
);
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
);
771 mc_totalframes (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
775 movie
= JS_GetPrivate (cx
, obj
);
778 *vp
= INT_TO_JSVAL (movie
->n_frames
);
784 mc_alpha_get (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
789 movie
= JS_GetPrivate (cx
, obj
);
792 d
= movie
->color_transform
.aa
* 100.0 / 256.0;
793 return JS_NewNumberValue (cx
, d
, vp
);
797 mc_alpha_set (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
803 movie
= JS_GetPrivate (cx
, obj
);
806 if (!JS_ValueToNumber (cx
, *vp
, &d
))
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
);
817 mc_visible_get (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
821 movie
= JS_GetPrivate (cx
, obj
);
824 *vp
= BOOLEAN_TO_JSVAL (movie
->visible
? JS_TRUE
: JS_FALSE
);
829 mc_visible_set (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
834 movie
= JS_GetPrivate (cx
, obj
);
837 if (!JS_ValueToBoolean (cx
, *vp
, &b
))
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
);
848 mc_width_get (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
853 movie
= JS_GetPrivate (cx
, obj
);
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
);
862 mc_width_set (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
867 movie
= JS_GetPrivate (cx
, obj
);
870 /* property was readonly in Flash 4 and before */
871 if (SWFDEC_SWF_DECODER (SWFDEC_ROOT_MOVIE (movie
->root
)->decoder
)->version
< 5)
873 if (!JS_ValueToNumber (cx
, *vp
, &d
))
876 SWFDEC_WARNING ("trying to set height to a non-finite value, ignoring");
879 swfdec_movie_update (movie
);
880 movie
->modified
= TRUE
;
881 cur
= SWFDEC_TWIPS_TO_DOUBLE ((SwfdecTwips
) (movie
->extents
.x1
- movie
->extents
.x0
));
883 movie
->xscale
*= d
/ cur
;
888 swfdec_movie_queue_update (movie
, SWFDEC_MOVIE_INVALID_MATRIX
);
894 mc_height_get (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
899 movie
= JS_GetPrivate (cx
, obj
);
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
);
908 mc_height_set (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
913 movie
= JS_GetPrivate (cx
, obj
);
916 /* property was readonly in Flash 4 and before */
917 if (SWFDEC_SWF_DECODER (SWFDEC_ROOT_MOVIE (movie
->root
)->decoder
)->version
< 5)
919 if (!JS_ValueToNumber (cx
, *vp
, &d
))
922 SWFDEC_WARNING ("trying to set height to a non-finite value, ignoring");
925 swfdec_movie_update (movie
);
926 movie
->modified
= TRUE
;
927 cur
= SWFDEC_TWIPS_TO_DOUBLE ((SwfdecTwips
) (movie
->extents
.y1
- movie
->extents
.y0
));
929 movie
->yscale
*= d
/ cur
;
934 swfdec_movie_queue_update (movie
, SWFDEC_MOVIE_INVALID_MATRIX
);
940 mc_rotation_get (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
944 movie
= JS_GetPrivate (cx
, obj
);
947 return JS_NewNumberValue (cx
, movie
->rotation
, vp
);
951 mc_rotation_set (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
956 movie
= JS_GetPrivate (cx
, obj
);
959 /* FIXME: Flash 4 handles this differently */
960 if (!JS_ValueToNumber (cx
, *vp
, &d
))
967 if (SWFDEC_SWF_DECODER (SWFDEC_ROOT_MOVIE (movie
->root
)->decoder
)->version
< 5) {
970 SWFDEC_ERROR ("FIXME: implement correct rounding errors here");
972 movie
->modified
= TRUE
;
974 swfdec_movie_queue_update (movie
, SWFDEC_MOVIE_INVALID_MATRIX
);
980 mc_parent (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
984 movie
= JS_GetPrivate (cx
, obj
);
987 /* FIXME: what do we do if we're the root movie? */
989 movie
= movie
->parent
;
991 if (movie
->jsobj
== NULL
)
992 swfdec_js_add_movie (movie
);
993 if (movie
->jsobj
== NULL
)
996 *vp
= OBJECT_TO_JSVAL (movie
->jsobj
);
1002 mc_root (JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
1006 movie
= JS_GetPrivate (cx
, obj
);
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
)
1019 *vp
= OBJECT_TO_JSVAL (movie
->jsobj
);
1024 /* Movie AS standard class */
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
);
1056 SWFDEC_ERROR ("reading and writing property %s is not implemented", str
);
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
},
1091 movieclip_find (SwfdecActionContext
*context
,
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
;
1108 swfdec_native_ASSetPropFlags (SwfdecActionContext
*context
, int num_args
,
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
)) {
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
;
1143 prop
->flags
= flags
;
1145 prop
->flags
|= flags
;
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
);
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");
1172 * swfdec_js_add_movieclip_class:
1173 * @player: a @SwfdecPlayer
1175 * Adds the movieclip class to the JS Context of @player.
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
,
1186 swfdec_js_movie_add_property (SwfdecMovie
*movie
)
1193 if (movie
->jsobj
== NULL
) {
1194 if (!swfdec_js_add_movie (movie
))
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
;
1203 SWFDEC_LOG ("setting %s as property for %s", movie
->name
,
1204 movie
->parent
->name
);
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
);
1215 swfdec_js_movie_remove_property (SwfdecMovie
*movie
)
1221 if (movie
->jsobj
== NULL
)
1224 cx
= SWFDEC_ROOT_MOVIE (movie
->root
)->player
->jscx
;
1225 if (movie
->parent
) {
1226 jsobj
= movie
->parent
->jsobj
;
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.
1246 swfdec_js_add_movie (SwfdecMovie
*movie
)
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
);
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
);
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.
1285 swfdec_js_val_to_movie (JSContext
*cx
, jsval val
)
1289 if (!JSVAL_IS_OBJECT (val
))
1291 object
= JSVAL_TO_OBJECT (val
);
1292 if (JS_GetClass (object
) != &movieclip_class
)
1294 return JS_GetPrivate (cx
, object
);