CVSROOT: /sources/gnash Module name: gnash Changes by: Sandro Santilli <strk> 07/09/11 17:01:24
Modified files: . : ChangeLog server : array.cpp array.h as_object.cpp as_object.h server/asobj : Global.cpp Makefile.am testsuite/actionscript.all: AsBroadcaster.as Added files: server/asobj : AsBroadcaster.cpp AsBroadcaster.h Log message: * server/array.{cpp,h}: add removeFirst and visitAll methods. * server/as_object.{cpp,h}: add getMember and callMethod memebers for ActionScript-like coding. * server/asobj/: Makefile.am, Global.cpp, AsBroadcaster.{cpp,h}: First draft at AsBroadcaster. NOTE: this is not 100% compatible but contains some special code to handle corner cases for compatbility sake (mostly to avoid breaking swfdec testcases). The normal case will still be faster then the average one. CVSWeb URLs: http://cvs.savannah.gnu.org/viewcvs/gnash/ChangeLog?cvsroot=gnash&r1=1.4271&r2=1.4272 http://cvs.savannah.gnu.org/viewcvs/gnash/server/array.cpp?cvsroot=gnash&r1=1.76&r2=1.77 http://cvs.savannah.gnu.org/viewcvs/gnash/server/array.h?cvsroot=gnash&r1=1.33&r2=1.34 http://cvs.savannah.gnu.org/viewcvs/gnash/server/as_object.cpp?cvsroot=gnash&r1=1.59&r2=1.60 http://cvs.savannah.gnu.org/viewcvs/gnash/server/as_object.h?cvsroot=gnash&r1=1.66&r2=1.67 http://cvs.savannah.gnu.org/viewcvs/gnash/server/asobj/Global.cpp?cvsroot=gnash&r1=1.67&r2=1.68 http://cvs.savannah.gnu.org/viewcvs/gnash/server/asobj/Makefile.am?cvsroot=gnash&r1=1.43&r2=1.44 http://cvs.savannah.gnu.org/viewcvs/gnash/server/asobj/AsBroadcaster.cpp?cvsroot=gnash&rev=1.1 http://cvs.savannah.gnu.org/viewcvs/gnash/server/asobj/AsBroadcaster.h?cvsroot=gnash&rev=1.1 http://cvs.savannah.gnu.org/viewcvs/gnash/testsuite/actionscript.all/AsBroadcaster.as?cvsroot=gnash&r1=1.1&r2=1.2 Patches: Index: ChangeLog =================================================================== RCS file: /sources/gnash/gnash/ChangeLog,v retrieving revision 1.4271 retrieving revision 1.4272 diff -u -b -r1.4271 -r1.4272 --- ChangeLog 11 Sep 2007 15:35:42 -0000 1.4271 +++ ChangeLog 11 Sep 2007 17:01:22 -0000 1.4272 @@ -1,3 +1,14 @@ +2007-09-11 Sandro Santilli <[EMAIL PROTECTED]> + + * server/array.{cpp,h}: add removeFirst and visitAll methods. + * server/as_object.{cpp,h}: add getMember and callMethod memebers + for ActionScript-like coding. + * server/asobj/: Makefile.am, Global.cpp, AsBroadcaster.{cpp,h}: + First draft at AsBroadcaster. NOTE: this is not 100% compatible + but contains some special code to handle corner cases for compatbility + sake (mostly to avoid breaking swfdec testcases). The normal case + will still be faster then the average one. + 2007-09-11 Benjamin Wolsey <[EMAIL PROTECTED]> * server/gnash.h: add more keycodes. Index: server/array.cpp =================================================================== RCS file: /sources/gnash/gnash/server/array.cpp,v retrieving revision 1.76 retrieving revision 1.77 diff -u -b -r1.76 -r1.77 --- server/array.cpp 1 Sep 2007 01:59:33 -0000 1.76 +++ server/array.cpp 11 Sep 2007 17:01:23 -0000 1.77 @@ -765,6 +765,20 @@ return ret; } +bool +as_array_object::removeFirst(const as_value& v, as_environment& env) +{ + for (iterator it = elements.begin(); it != elements.end(); ++it) + { + if ( v.equals(*it, env) ) + { + elements.erase(it); + return true; + } + } + return false; +} + /* virtual public, overriding as_object::get_member */ bool as_array_object::get_member(const std::string& name, as_value *val) Index: server/array.h =================================================================== RCS file: /sources/gnash/gnash/server/array.h,v retrieving revision 1.33 retrieving revision 1.34 diff -u -b -r1.33 -r1.34 --- server/array.h 30 Aug 2007 01:02:49 -0000 1.33 +++ server/array.h 11 Sep 2007 17:01:23 -0000 1.34 @@ -60,6 +60,17 @@ typedef container::const_iterator const_iterator; typedef container::iterator iterator; + /// Visit all elements + // + /// The visitor class will have to expose a visit(as_value&) method + /// + template<class V> void visitAll(V& v) + { + for (iterator i=elements.begin(), ie=elements.end(); i!=ie; ++i) + { + v.visit(*i); + } + } /// Sort flags enum SortFlags { @@ -162,6 +173,18 @@ std::auto_ptr<as_array_object> slice( unsigned int start, unsigned int one_past_end); + /// Remove first element matching the given value + // + /// Return true if any element was removed, false otherwise + /// + /// @param v + /// The value to compare elements against + /// + /// @param env + /// The environment to use when comparing (needed by as_value::equals) + /// + bool removeFirst(const as_value& v, as_environment& env); + /// \brief /// Substitute 'len' elements from 'start' with elements from /// the given array. Index: server/as_object.cpp =================================================================== RCS file: /sources/gnash/gnash/server/as_object.cpp,v retrieving revision 1.59 retrieving revision 1.60 diff -u -b -r1.59 -r1.60 --- server/as_object.cpp 2 Sep 2007 00:12:48 -0000 1.59 +++ server/as_object.cpp 11 Sep 2007 17:01:23 -0000 1.60 @@ -30,9 +30,7 @@ #include "GnashException.h" #include "fn_call.h" // for generic methods #include "Object.h" // for getObjectInterface -#ifdef NEW_KEY_LISTENER_LIST_DESIGN - #include "action.h" // for call_method -#endif +#include "action.h" // for call_method #include <set> #include <string> #include <boost/algorithm/string/case_conv.hpp> @@ -661,4 +659,67 @@ } #endif +as_value +as_object::getMember(const std::string& name) +{ + as_value ret; + get_member(PROPNAME(name), &ret); + return ret; +} + +as_value +as_object::callMethod(const std::string& methodName, as_environment& env) +{ + as_value ret; + as_value method; + + if ( ! get_member(methodName, &method) ) + { + return ret; + } + + return call_method(method, &env, this, 0, env.stack_size()); +} + +as_value +as_object::callMethod(const std::string& methodName, as_environment& env, const as_value& arg0) +{ + as_value ret; + as_value method; + + if ( ! get_member(methodName, &method) ) + { + return ret; + } + + env.push(arg0); + + ret = call_method(method, &env, this, 1, env.stack_size()-1); + + env.drop(1); + + return ret; +} + +as_value +as_object::callMethod(const std::string& methodName, as_environment& env, const as_value& arg0, const as_value& arg1) +{ + as_value ret; + as_value method; + + if ( ! get_member(methodName, &method) ) + { + return ret; + } + + env.push(arg0); + env.push(arg1); + + ret = call_method(method, &env, this, 2, env.stack_size()-2); + + env.drop(2); + + return ret; +} + } // end of gnash namespace Index: server/as_object.h =================================================================== RCS file: /sources/gnash/gnash/server/as_object.h,v retrieving revision 1.66 retrieving revision 1.67 diff -u -b -r1.66 -r1.67 --- server/as_object.h 2 Sep 2007 00:12:48 -0000 1.66 +++ server/as_object.h 11 Sep 2007 17:01:23 -0000 1.67 @@ -274,6 +274,49 @@ return get_member_default(name, val); } + /// Get a member as_value by name in an AS-compatible way + // + /// NOTE that this method is non-const becase a property + /// could also be a getter/setter and we can't promise + /// that the 'getter' won't change this object trough + /// use of the 'this' reference. + /// + /// @param name + /// Name of the property. Will be converted to lowercase + /// if the current VM is initialized for a target + /// up to SWF6. + /// + /// @return value of the member (possibly undefined), + /// or undefined if not found. Use get_member if you + /// need to know wheter it was found or not. + /// + as_value getMember(const std::string& name); + + /// Call a method of this object in an AS-compatible way + // + /// @param name + /// Name of the method. Will be converted to lowercase + /// if the current VM is initialized for a target + /// up to SWF6. + /// + /// @param env + /// The environment to use for setting up call frame stack + /// + /// @param nargs + /// Number of arguments + /// + /// @param ... + /// nargs as_value references + /// + /// @return value of the member (possibly undefined), + /// or undefined if not found. Use get_member if you + /// need to know wheter it was found or not. + /// + as_value callMethod(const std::string& name, as_environment& env); + as_value callMethod(const std::string& name, as_environment& env, const as_value& arg0); + as_value callMethod(const std::string& name, as_environment& env, const as_value& arg0, const as_value& arg1); + as_value callMethod(const std::string& name, as_environment& env, const as_value& arg0, const as_value& arg1, const as_value& arg2); + /// Delete a property of this object, unless protected from deletion. // /// This function does *not* recurse in this object's prototype. Index: server/asobj/Global.cpp =================================================================== RCS file: /sources/gnash/gnash/server/asobj/Global.cpp,v retrieving revision 1.67 retrieving revision 1.68 diff -u -b -r1.67 -r1.68 --- server/asobj/Global.cpp 11 Sep 2007 05:46:31 -0000 1.67 +++ server/asobj/Global.cpp 11 Sep 2007 17:01:23 -0000 1.68 @@ -17,7 +17,7 @@ // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // -/* $Id: Global.cpp,v 1.67 2007/09/11 05:46:31 zoulunkai Exp $ */ +/* $Id: Global.cpp,v 1.68 2007/09/11 17:01:23 strk Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -28,6 +28,7 @@ #include "as_value.h" #include "as_function.h" // for function_class_init #include "array.h" +#include "AsBroadcaster.h" #include "Boolean.h" #include "Camera.h" #include "Color.h" @@ -437,6 +438,10 @@ init_member("NaN", as_value(NAN)); init_member("Infinity", as_value(INFINITY)); + // TODO: check if this is available in SWF4 + // (for SWF5 it just exists as a useless function, it seems) + AsBroadcaster_init(*this); + if ( vm.getSWFVersion() < 6 ) goto extscan; //----------------------- // SWF6 Index: server/asobj/Makefile.am =================================================================== RCS file: /sources/gnash/gnash/server/asobj/Makefile.am,v retrieving revision 1.43 retrieving revision 1.44 diff -u -b -r1.43 -r1.44 --- server/asobj/Makefile.am 27 Aug 2007 18:13:43 -0000 1.43 +++ server/asobj/Makefile.am 11 Sep 2007 17:01:23 -0000 1.44 @@ -15,7 +15,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -# $Id: Makefile.am,v 1.43 2007/08/27 18:13:43 cmusick Exp $ +# $Id: Makefile.am,v 1.44 2007/09/11 17:01:23 strk Exp $ AUTOMAKE_OPTIONS = @@ -43,6 +43,7 @@ libgnashasobjs_la_SOURCES = \ + AsBroadcaster.cpp \ BevelFilter_as.cpp \ BitmapFilter_as.cpp \ Boolean.cpp \ @@ -85,6 +86,7 @@ $(NULL) noinst_HEADERS = \ + AsBroadcaster.h \ BevelFilter_as.h \ BitmapFilter_as.h \ Boolean.h \ Index: testsuite/actionscript.all/AsBroadcaster.as =================================================================== RCS file: /sources/gnash/gnash/testsuite/actionscript.all/AsBroadcaster.as,v retrieving revision 1.1 retrieving revision 1.2 diff -u -b -r1.1 -r1.2 --- testsuite/actionscript.all/AsBroadcaster.as 30 Aug 2007 09:47:36 -0000 1.1 +++ testsuite/actionscript.all/AsBroadcaster.as 11 Sep 2007 17:01:23 -0000 1.2 @@ -19,7 +19,7 @@ // compile this test case with Ming makeswf, and then // execute it like this gnash -1 -r 0 -v out.swf -rcsid="$Id: AsBroadcaster.as,v 1.1 2007/08/30 09:47:36 strk Exp $"; +rcsid="$Id: AsBroadcaster.as,v 1.2 2007/09/11 17:01:23 strk Exp $"; #include "check.as" @@ -28,19 +28,23 @@ note(); note("AsBroadcaster exists but doesn't provide any 'prototype' or 'initialize' for SWF < 6"); -xcheck_equals(typeof(AsBroadcaster), 'function'); // ??? -check_equals(typeof(AsBroadcaster.prototype), 'undefined'); +check_equals(typeof(AsBroadcaster), 'function'); // ??? +xcheck_equals(typeof(AsBroadcaster.prototype), 'undefined'); check_equals(typeof(AsBroadcaster.initialize), 'undefined'); #else // OUTPUT_VERSION >= 6 -xcheck_equals(typeof(AsBroadcaster), 'function'); // ??? -xcheck_equals(typeof(AsBroadcaster.prototype), 'object'); -xcheck_equals(typeof(AsBroadcaster.initialize), 'function'); +check_equals(typeof(AsBroadcaster), 'function'); // ??? +check_equals(typeof(AsBroadcaster.prototype), 'object'); +check_equals(AsBroadcaster.__proto__, Function.prototype); +check_equals(typeof(AsBroadcaster.initialize), 'function'); +check(AsBroadcaster.hasOwnProperty('initialize')); +check(!AsBroadcaster.prototype.hasOwnProperty('initialize')); bc = new AsBroadcaster; -xcheck_equals(typeof(bc), 'object'); -xcheck(bc instanceof AsBroadcaster); +check_equals(typeof(bc), 'object'); +check(bc instanceof AsBroadcaster); +check(bc instanceof Object); check_equals(typeof(bc.addListener), 'undefined'); check_equals(typeof(bc.removeListener), 'undefined'); check_equals(typeof(bc.broadcastMessage), 'undefined'); @@ -55,77 +59,77 @@ AsBroadcaster.initialize(bcast); -xcheck_equals(typeof(bcast._listeners), 'object'); -xcheck(bcast._listeners instanceof Array); -xcheck_equals(bcast._listeners.length, 0); -xcheck_equals(typeof(bcast.addListener), 'function'); -xcheck_equals(typeof(bcast.removeListener), 'function'); -xcheck_equals(typeof(bcast.broadcastMessage), 'function'); +check_equals(typeof(bcast._listeners), 'object'); +check(bcast._listeners instanceof Array); +check_equals(bcast._listeners.length, 0); +check_equals(typeof(bcast.addListener), 'function'); +check_equals(typeof(bcast.removeListener), 'function'); +check_equals(typeof(bcast.broadcastMessage), 'function'); //-------------------------------- // Some insane calls... //-------------------------------- ret = bcast.addListener(); -xcheck_equals(typeof(ret), 'boolean'); -xcheck_equals(ret, true); -xcheck_equals(bcast._listeners.length, 1); // !! +check_equals(typeof(ret), 'boolean'); +check_equals(ret, true); +check_equals(bcast._listeners.length, 1); // !! ret = bcast.addListener(); -xcheck_equals(bcast._listeners.length, 1); // undefined was already there as an element... +check_equals(bcast._listeners.length, 1); // undefined was already there as an element... ret = bcast.addListener(2); -xcheck_equals(typeof(ret), 'boolean'); -xcheck_equals(ret, true); -xcheck_equals(bcast._listeners.length, 2); // !! +check_equals(typeof(ret), 'boolean'); +check_equals(ret, true); +check_equals(bcast._listeners.length, 2); // !! ret = bcast.addListener(2); -xcheck_equals(bcast._listeners.length, 2); // 2 was already there as an element ... +check_equals(bcast._listeners.length, 2); // 2 was already there as an element ... ret = bcast.addListener(3); -xcheck_equals(bcast._listeners.length, 3); // 3 is a new element +check_equals(bcast._listeners.length, 3); // 3 is a new element ret = bcast.removeListener(); // will remove the undefined value ! -xcheck_equals(typeof(ret), 'boolean'); -xcheck_equals(ret, true); -xcheck_equals(bcast._listeners.length, 2); // element 'undefined' was removed +check_equals(typeof(ret), 'boolean'); +check_equals(ret, true); +check_equals(bcast._listeners.length, 2); // element 'undefined' was removed ret = bcast.removeListener(2); // will remove the element number:2 ! -xcheck_equals(typeof(ret), 'boolean'); -xcheck_equals(ret, true); -xcheck_equals(bcast._listeners.length, 1); // element '2' was removed +check_equals(typeof(ret), 'boolean'); +check_equals(ret, true); +check_equals(bcast._listeners.length, 1); // element '2' was removed ret = bcast.removeListener(3); // will remove the element number:3 ! -xcheck_equals(typeof(ret), 'boolean'); -xcheck_equals(ret, true); -xcheck_equals(bcast._listeners.length, 0); // element '3' was removed +check_equals(typeof(ret), 'boolean'); +check_equals(ret, true); +check_equals(bcast._listeners.length, 0); // element '3' was removed ret = bcast.removeListener(); // no such element ? -xcheck_equals(typeof(ret), 'boolean'); -xcheck_equals(ret, false); +check_equals(typeof(ret), 'boolean'); +check_equals(ret, false); o = new Object; o.valueOf = function() { return 'yes I am'; }; bcast.addListener(o); -xcheck_equals(bcast._listeners.length, 1); +check_equals(bcast._listeners.length, 1); ret = bcast.removeListener('yes I am'); // valueOf invoked -xcheck_equals(typeof(ret), 'boolean'); -xcheck_equals(ret, true); -xcheck_equals(bcast._listeners.length, 0); // element '3' was removed +check_equals(typeof(ret), 'boolean'); +check_equals(ret, true); +check_equals(bcast._listeners.length, 0); // element '3' was removed o.addListener = bcast.addListener; check_equals(typeof(o._listeners), 'undefined'); check_equals(typeof(o.removeListenerCalled), 'undefined'); ret = o.addListener(); // automatically attempts to call o.removeListener() -xcheck_equals(typeof(ret), 'boolean'); -xcheck_equals(ret, true); +check_equals(typeof(ret), 'boolean'); +check_equals(ret, true); check_equals(typeof(o._listeners), 'undefined'); o.removeListener = function() { this.removeListenerCalled = true; }; ret = o.addListener(); // automatically calls o.removeListener() -xcheck_equals(typeof(ret), 'boolean'); -xcheck_equals(ret, true); +check_equals(typeof(ret), 'boolean'); +check_equals(ret, true); check_equals(typeof(o._listeners), 'undefined'); -xcheck_equals(typeof(o.removeListenerCalled), 'boolean'); -xcheck_equals(o.removeListenerCalled, true); +check_equals(typeof(o.removeListenerCalled), 'boolean'); +check_equals(o.removeListenerCalled, true); o.removeListener = bcast.removeListener; o._listeners = new Object(); @@ -134,9 +138,10 @@ o._listeners.length = 1; o._listeners['0'] = 5; ret = o.addListener(5); // automatically calls o._listeners.splice and o._listeners.push -xcheck_equals(o._listeners.pushCalled, true); -xcheck_equals(o._listeners.length, 2); -xcheck_equals(o._listeners.spliceCalled, true); +// Gnash fails as it gives up if _listeners isn't an array +check_equals(o._listeners.pushCalled, true); +check_equals(o._listeners.length, 2); +check_equals(o._listeners.spliceCalled, true); //-------------------------------- @@ -155,69 +160,69 @@ b = new Object; b.name = 'b'; b.onTest = onTest; ret = bcast.addListener(a); -xcheck_equals(typeof(ret), 'boolean'); -xcheck_equals(ret, true); +check_equals(typeof(ret), 'boolean'); +check_equals(ret, true); ret = bcast.addListener(b); -xcheck_equals(typeof(ret), 'boolean'); -xcheck_equals(ret, true); +check_equals(typeof(ret), 'boolean'); +check_equals(ret, true); //note("Broadcasting"); ret = bcast.broadcastMessage('onTest'); -xcheck_equals(typeof(ret), 'boolean'); -xcheck_equals(ret, true); -xcheck_equals(a.order, 1); -xcheck_equals(b.order, 2); +check_equals(typeof(ret), 'boolean'); +check_equals(ret, true); +check_equals(a.order, 1); +check_equals(b.order, 2); ret = bcast.addListener(b); // b is not added again -xcheck_equals(typeof(ret), 'boolean'); -xcheck_equals(ret, true); +check_equals(typeof(ret), 'boolean'); +check_equals(ret, true); //note("Broadcasting"); bcast.broadcastMessage('onTest'); -xcheck_equals(a.order, 3); -xcheck_equals(b.order, 4); +check_equals(a.order, 3); +check_equals(b.order, 4); ret = bcast.addListener(a); // listener a is moved from first to last position to _listeners -xcheck_equals(typeof(ret), 'boolean'); -xcheck_equals(ret, true); +check_equals(typeof(ret), 'boolean'); +check_equals(ret, true); //note("Broadcasting"); bcast.broadcastMessage('onTest'); -xcheck_equals(b.order, 5); -xcheck_equals(a.order, 6); +check_equals(b.order, 5); +check_equals(a.order, 6); bcast._listeners.push(a); // force double a listener //note("Broadcasting"); bcast.broadcastMessage('onTest'); -xcheck_equals(b.order, 7); -xcheck_equals(a.order, 9); // a.order was set twice +check_equals(b.order, 7); +check_equals(a.order, 9); // a.order was set twice bcast.addListener(a); // first a is moved from first to last position to _listeners //note("Broadcasting"); ret = bcast.broadcastMessage('onTest'); -xcheck_equals(typeof(ret), 'boolean'); -xcheck_equals(ret, true); -xcheck_equals(b.order, 10); -xcheck_equals(a.order, 12); // a is still set twice +check_equals(typeof(ret), 'boolean'); +check_equals(ret, true); +check_equals(b.order, 10); +check_equals(a.order, 12); // a is still set twice bcast._listeners.push(b); // force double b, order should now be: b,a,a,b //note("Broadcasting"); bcast.broadcastMessage('onTest'); -xcheck_equals(b.order, 16); -xcheck_equals(a.order, 15); +check_equals(b.order, 16); +check_equals(a.order, 15); ret = bcast.addListener(b); // *first* b is removed, another one added, new order is a,a,b,b -xcheck_equals(typeof(ret), 'boolean'); -xcheck_equals(ret, true); +check_equals(typeof(ret), 'boolean'); +check_equals(ret, true); //note("Broadcasting"); bcast.broadcastMessage('onTest'); -xcheck_equals(a.order, 18); -xcheck_equals(b.order, 20); +check_equals(a.order, 18); +check_equals(b.order, 20); ret = bcast.removeListener(b); // only first is removed -xcheck_equals(typeof(ret), 'boolean'); -xcheck_equals(ret, true); -xcheck_equals(bcast._listeners.length, 3); // expect: a,a,b +check_equals(typeof(ret), 'boolean'); +check_equals(ret, true); +check_equals(bcast._listeners.length, 3); // expect: a,a,b bcast.broadcastMessage('onTest'); -xcheck_equals(a.order, 22); -xcheck_equals(b.order, 23); +check_equals(a.order, 22); +check_equals(b.order, 23); // TODO: test broadcastMessage with additional arguments // and effects of overriding Function.apply Index: server/asobj/AsBroadcaster.cpp =================================================================== RCS file: server/asobj/AsBroadcaster.cpp diff -N server/asobj/AsBroadcaster.cpp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ server/asobj/AsBroadcaster.cpp 11 Sep 2007 17:01:23 -0000 1.1 @@ -0,0 +1,369 @@ +// AsBroadcaster.cpp - AsBroadcaster AS interface +// +// Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "array.h" // for _listeners construction +#include "log.h" +#include "AsBroadcaster.h" +#include "fn_call.h" +#include "builtin_function.h" +#include "VM.h" // for getPlayerVersion() +#include "Object.h" // for getObjectInterface + +#include <boost/algorithm/string/case_conv.hpp> // for PROPNAME + +namespace gnash { + +class BroadcasterVisitor +{ + + /// Name of the event being broadcasted + /// appropriately cased based on SWF version + /// of the current VM + std::string _eventName; + + /// Environment to use for marhalling and functions invokation + as_environment& _env; + + // These two will be needed for consistency checking + //size_t _origEnvStackSize; + //size_t _origEnvCallStackSize; + +public: + + /// @param eName name of event, will be converted to lowercase if needed + /// + /// @param env Environment to use for marhalling and functions invocation. + /// Note that visit() will push values on it ! + /// + BroadcasterVisitor(const std::string& eName, as_environment& env) + : + _eventName(PROPNAME(eName)), + _env(env) + { + } + + /// Call a method on the given value + void visit(as_value& v) + { + boost::intrusive_ptr<as_object> o = v.to_object(); + if ( ! o ) return; + +#ifndef NDEBUG + size_t oldStackSize = _env.stack_size(); +#endif + /*as_value ret =*/ o->callMethod(_eventName, _env); + assert ( _env.stack_size() == oldStackSize ); + } +}; + +void +AsBroadcaster::initialize(as_object& o) +{ + log_debug("Initializing object %p as an AsBroadcaster", (void*)&o); + // TODO: reserch on protection flags for these methods + o.set_member(PROPNAME("addListener"), new builtin_function(AsBroadcaster::addListener_method)); + o.set_member(PROPNAME("removeListener"), new builtin_function(AsBroadcaster::removeListener_method)); + o.set_member(PROPNAME("broadcastMessage"), new builtin_function(AsBroadcaster::broadcastMessage_method)); + o.set_member("_listeners", new as_array_object()); + +#ifndef NDEBUG + as_value tmp; + assert(o.get_member("_listeners", &tmp)); + assert(tmp.is_object()); + assert(o.get_member(PROPNAME("addListener"), &tmp)); + assert(tmp.is_function()); + assert(o.get_member(PROPNAME("removeListener"), &tmp)); + assert(tmp.is_function()); + assert(o.get_member(PROPNAME("broadcastMessage"), &tmp)); + assert(tmp.is_function()); +#endif +} + +as_value +AsBroadcaster::initialize_method(const fn_call& fn) +{ + // TODO: initialize first arg object as an AsBroadcaster + // (call the AsBroadcaster::initialize(as_object*) static ?) + if ( fn.nargs < 1 ) + { + IF_VERBOSE_ASCODING_ERRORS( + log_aserror(_("AsBroadcaster.initialize() requires one argument, none given")); + ); + return as_value(); + } + + // TODO: check if automatic primitive to object conversion apply here + as_value& tgtval = fn.arg(0); + if ( ! tgtval.is_object() ) + { + IF_VERBOSE_ASCODING_ERRORS( + log_aserror(_("AsBroadcaster.initialize(%s): first arg is not an object"), tgtval.to_debug_string().c_str()); + ); + return as_value(); + } + + boost::intrusive_ptr<as_object> tgt = tgtval.to_object(); + + AsBroadcaster::initialize(*tgt); + + log_debug("AsBroadcaster.initialize(%s): TESTING", tgtval.to_debug_string().c_str()); + + return as_value(); +} + +as_value +AsBroadcaster::addListener_method(const fn_call& fn) +{ + boost::intrusive_ptr<as_object> obj = fn.this_ptr; + + as_value newListener; assert(newListener.is_undefined()); + if ( fn.nargs ) newListener = fn.arg(0); + + obj->callMethod(PROPNAME("removeListener"), fn.env(), newListener); + + as_value listenersValue; + + // TODO: test if we're supposed to crawl the target object's + // inheritance chain in case it's own property _listeners + // has been deleted while another one is found in any base + // class. + if ( ! obj->get_member("_listeners", &listenersValue) ) + { + IF_VERBOSE_ASCODING_ERRORS( + log_aserror(_("%p.addListener(%s): this object has no _listeners member"), + (void*)fn.this_ptr.get(), + fn.dump_args().c_str()); + ); + return as_value(true); // odd, but seems the case.. + } + + // assuming no automatic primitive-to-object cast will return an array... + if ( ! listenersValue.is_object() ) + { + IF_VERBOSE_ASCODING_ERRORS( + log_aserror(_("%p.addListener(%s): this object's _listener isn't an object: %s"), + (void*)fn.this_ptr.get(), + fn.dump_args().c_str(), listenersValue.to_debug_string().c_str()); + ); + return as_value(false); // TODO: check this + } + + boost::intrusive_ptr<as_object> listenersObj = listenersValue.to_object(); + assert(listenersObj); + + boost::intrusive_ptr<as_array_object> listeners = boost::dynamic_pointer_cast<as_array_object>(listenersObj); + if ( ! listeners ) + { + IF_VERBOSE_ASCODING_ERRORS( + log_aserror(_("%p.addListener(%s): this object's _listener isn't an array: %s -- will call 'push' on it anyway"), + (void*)fn.this_ptr.get(), + fn.dump_args().c_str(), listenersValue.to_debug_string().c_str()); + ); + + listenersObj->callMethod(PROPNAME("push"), fn.env(), newListener); + + } + else + { + listeners->push(newListener); + } + + log_debug("%p.addListener(%s) TESTING", (void*)fn.this_ptr.get(), fn.dump_args().c_str()); + return as_value(true); + +} + +as_value +AsBroadcaster::removeListener_method(const fn_call& fn) +{ + boost::intrusive_ptr<as_object> obj = fn.this_ptr; + + as_value listenersValue; + + // TODO: test if we're supposed to crawl the target object's + // inheritance chain in case it's own property _listeners + // has been deleted while another one is found in any base + // class. + if ( ! obj->get_member("_listeners", &listenersValue) ) + { + IF_VERBOSE_ASCODING_ERRORS( + log_aserror(_("%p.addListener(%s): this object has no _listeners member"), + (void*)fn.this_ptr.get(), + fn.dump_args().c_str()); + ); + return as_value(false); // TODO: check this + } + + // assuming no automatic primitive-to-object cast will return an array... + if ( ! listenersValue.is_object() ) + { + IF_VERBOSE_ASCODING_ERRORS( + log_aserror(_("%p.addListener(%s): this object's _listener isn't an object: %s"), + (void*)fn.this_ptr.get(), + fn.dump_args().c_str(), listenersValue.to_debug_string().c_str()); + ); + return as_value(false); // TODO: check this + } + + boost::intrusive_ptr<as_object> listenersObj = listenersValue.to_object(); + assert(listenersObj); + + as_value listenerToRemove; assert(listenerToRemove.is_undefined()); + if ( fn.nargs ) listenerToRemove = fn.arg(0); + + boost::intrusive_ptr<as_array_object> listeners = boost::dynamic_pointer_cast<as_array_object>(listenersObj); + if ( ! listeners ) + { + IF_VERBOSE_ASCODING_ERRORS( + log_aserror(_("%p.addListener(%s): this object's _listener isn't an array: %s"), + (void*)fn.this_ptr.get(), + fn.dump_args().c_str(), listenersValue.to_debug_string().c_str()); + ); + + // TODO: implement brute force scan of pseudo-array + unsigned int length = listenersObj->getMember("length").to_int(fn.env()); + for (unsigned int i=0; i<length; ++i) + { + as_value iVal(i); + std::string n = iVal.to_string(&(fn.env())); + as_value v = listenersObj->getMember(n); + if ( v.equals(listenerToRemove, fn.env()) ) + { + listenersObj->callMethod("splice", fn.env(), iVal, as_value(1)); + return as_value(true); + } + } + + return as_value(false); // TODO: check this + } + else + { + // Remove the first listener matching the new value + // See http://www.senocular.com/flash/tutorials/listenersasbroadcaster/?page=2 + // TODO: make this call as a normal (don't want to rely on _listeners type at all) + bool removed = listeners->removeFirst(listenerToRemove, fn.env()); + return as_value(removed); + } + +} + +as_value +AsBroadcaster::broadcastMessage_method(const fn_call& fn) +{ + boost::intrusive_ptr<as_object> obj = fn.this_ptr; + + as_value listenersValue; + + // TODO: test if we're supposed to crawl the target object's + // inheritance chain in case it's own property _listeners + // has been deleted while another one is found in any base + // class. + if ( ! obj->get_member("_listeners", &listenersValue) ) + { + IF_VERBOSE_ASCODING_ERRORS( + log_aserror(_("%p.addListener(%s): this object has no _listeners member"), + (void*)fn.this_ptr.get(), + fn.dump_args().c_str()); + ); + return as_value(); // TODO: check this + } + + // assuming no automatic primitive-to-object cast will return an array... + if ( ! listenersValue.is_object() ) + { + IF_VERBOSE_ASCODING_ERRORS( + log_aserror(_("%p.addListener(%s): this object's _listener isn't an object: %s"), + (void*)fn.this_ptr.get(), + fn.dump_args().c_str(), listenersValue.to_debug_string().c_str()); + ); + return as_value(); // TODO: check this + } + + boost::intrusive_ptr<as_array_object> listeners = boost::dynamic_pointer_cast<as_array_object>(listenersValue.to_object()); + if ( ! listeners ) + { + IF_VERBOSE_ASCODING_ERRORS( + log_aserror(_("%p.addListener(%s): this object's _listener isn't an array: %s"), + (void*)fn.this_ptr.get(), + fn.dump_args().c_str(), listenersValue.to_debug_string().c_str()); + ); + return as_value(); // TODO: check this + } + + if ( ! fn.nargs ) + { + IF_VERBOSE_ASCODING_ERRORS( + log_aserror("%p.broadcastMessage() needs an argument", (void*)fn.this_ptr.get()); + ); + return as_value(); + } + + BroadcasterVisitor visitor(fn.arg(0).to_string(), fn.env()); + listeners->visitAll(visitor); + + log_debug("AsBroadcaster.broadcastMessage TESTING"); + + return as_value(true); +} + +static as_object* +getAsBroadcasterInterface() +{ + static boost::intrusive_ptr<as_object> o=NULL; + if ( o == NULL ) + { + o = new as_object(getObjectInterface()); + VM::get().addStatic(o.get()); + } + return o.get(); +} + +static as_value +AsBroadcaster_ctor(const fn_call& /*fn*/) +{ + as_object* obj = new as_object(getAsBroadcasterInterface()); + return as_value(obj); // will keep alive +} + +void +AsBroadcaster_init(as_object& global) +{ + // _global.AsBroadcaster is NOT a class, but a simple object + + VM& vm = VM::get(); + int swfVersion = vm.getSWFVersion(); + + static boost::intrusive_ptr<as_object> obj = NULL; + if ( ! obj ) + { + obj = new builtin_function(AsBroadcaster_ctor, getAsBroadcasterInterface()); + VM::get().addStatic(obj.get()); // correct ? + if ( swfVersion >= 6 ) + { + obj->init_member("initialize", new builtin_function(AsBroadcaster::initialize_method)); + } + } + global.init_member("AsBroadcaster", obj.get()); +} + +} // end of gnash namespace Index: server/asobj/AsBroadcaster.h =================================================================== RCS file: server/asobj/AsBroadcaster.h diff -N server/asobj/AsBroadcaster.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ server/asobj/AsBroadcaster.h 11 Sep 2007 17:01:23 -0000 1.1 @@ -0,0 +1,74 @@ +// +// Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +// +// +// + +#ifndef __ASBROADCASTER_H__ +#define __ASBROADCASTER_H__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +// Forward declarations +namespace gnash { + class as_value; + class as_object; + class fn_call; +} + +namespace gnash { + +/// AsBroadcaster facilities +class AsBroadcaster { + +public: + + /// Initialize the given object as an AsBroadcaster + // + /// This method set the addListener,removeListener and broadcastMessage + /// AS methods with the object, and set the _listners array member. + /// + /// It is exposed so that Stage,TextField,Key,Mouse and Selection + /// can call this internally. + /// + /// The AsBroadcaster_init will take care of registering + /// the _global.AsBroadcaster object and it's 'initialize' + /// method for user-defined broadcasters initialization + /// + static void initialize(as_object& obj); + + /// AsBroadcaster.initialize() AS method + static as_value initialize_method(const fn_call& fn); + +private: + + static as_value addListener_method(const fn_call& fn); + static as_value removeListener_method(const fn_call& fn); + static as_value broadcastMessage_method(const fn_call& fn); + +}; + +void AsBroadcaster_init(as_object& global); + +} // end of gnash namespace + +// __ASBROADCASTER_H__ +#endif + _______________________________________________ Gnash-commit mailing list Gnash-commit@gnu.org http://lists.gnu.org/mailman/listinfo/gnash-commit