[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Gnash-commit] gnash/server as_function.h asobj/LoadVars.cpp
From: |
Sandro Santilli |
Subject: |
[Gnash-commit] gnash/server as_function.h asobj/LoadVars.cpp |
Date: |
Fri, 23 Feb 2007 11:28:10 +0000 |
CVSROOT: /sources/gnash
Module name: gnash
Changes by: Sandro Santilli <strk> 07/02/23 11:28:10
Modified files:
server : as_function.h
server/asobj : LoadVars.cpp
Log message:
* server/as_function.h: add a call() function
to make operator() calls nicer when having
an as_function pointer.
* server/asobj/LoadVars.cpp: implement getBytesLoaded,
getBytesTotal, onLoad and onData events, load() method.
CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/gnash/server/as_function.h?cvsroot=gnash&r1=1.7&r2=1.8
http://cvs.savannah.gnu.org/viewcvs/gnash/server/asobj/LoadVars.cpp?cvsroot=gnash&r1=1.5&r2=1.6
Patches:
Index: as_function.h
===================================================================
RCS file: /sources/gnash/gnash/server/as_function.h,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -b -r1.7 -r1.8
--- as_function.h 2 Jan 2007 12:51:32 -0000 1.7
+++ as_function.h 23 Feb 2007 11:28:10 -0000 1.8
@@ -74,6 +74,9 @@
/// Dispatch.
virtual void operator()(const fn_call& fn)=0;
+ /// Alias for operator()
+ void call(const fn_call& fn) { return operator()(fn); }
+
/// Get this function's "prototype" member (exported interface).
//
/// This is never NULL, and created on purpose if not provided
Index: asobj/LoadVars.cpp
===================================================================
RCS file: /sources/gnash/gnash/server/asobj/LoadVars.cpp,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -b -r1.5 -r1.6
--- asobj/LoadVars.cpp 18 Jan 2007 22:53:21 -0000 1.5
+++ asobj/LoadVars.cpp 23 Feb 2007 11:28:10 -0000 1.6
@@ -1,5 +1,5 @@
//
-// Copyright (C) 2005, 2006 Free Software Foundation, Inc.
+// 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
@@ -26,34 +26,233 @@
#include "fn_call.h"
#include "smart_ptr.h" // for boost intrusive_ptr
#include "builtin_function.h" // need builtin_function
+#include "GnashException.h" // for ActionException in ensureLoadVars
+#include "as_function.h" // for calling event handlers
+#include "as_environment.h" // for setting up a fn_call
+#include "as_value.h" // for setting up a fn_call
+#include "StreamProvider.h"
+#include "URL.h"
+#include "gnash.h" // for get_base_url
+#include "tu_file.h"
+
+#include <boost/thread/thread.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/bind.hpp>
+#include <memory>
+#include <list>
namespace gnash {
-void loadvars_addrequestheader(const fn_call& fn);
-void loadvars_decode(const fn_call& fn);
-void loadvars_getbytesloaded(const fn_call& fn);
-void loadvars_getbytestotal(const fn_call& fn);
-void loadvars_load(const fn_call& fn);
-void loadvars_send(const fn_call& fn);
-void loadvars_sendandload(const fn_call& fn);
-void loadvars_tostring(const fn_call& fn);
-void loadvars_ctor(const fn_call& fn);
+static void loadvars_addrequestheader(const fn_call& fn);
+static void loadvars_decode(const fn_call& fn);
+static void loadvars_load(const fn_call& fn);
+static void loadvars_send(const fn_call& fn);
+static void loadvars_sendandload(const fn_call& fn);
+static void loadvars_tostring(const fn_call& fn);
+static void loadvars_ctor(const fn_call& fn);
+//static as_object* getLoadVarsInterface();
+//static void attachLoadVarsInterface(as_object& o);
-static void
-attachLoadVarsInterface(as_object& o)
+
+/// LoadVars ActionScript class
+//
+class LoadVars: public as_object
+{
+
+public:
+
+ LoadVars()
+ :
+ as_object(getLoadVarsInterface()),
+ _env(0),
+ _bytesTotal(0),
+ _bytesLoaded(0),
+ _loadRequests(),
+ _currentLoad(_loadRequests.end())
+ {}
+
+ /// Load data from given URL
+ //
+ /// @param env
+ /// The environment to use when invoking event
+ /// handlers (onLoad, onData)
+ ///
+ void load(const std::string& url, as_environment* env);
+
+ static as_object* getLoadVarsInterface();
+
+ static void attachLoadVarsInterface(as_object& o);
+
+ // override from as_object ?
+ //const char* get_text_value() const { return "LoadVars"; }
+
+ // override from as_object ?
+ //double get_numeric_value() const { return 0; }
+
+ size_t getBytesLoaded() const {
+ return _bytesLoaded;
+ }
+
+ size_t getBytesTotal() const {
+ return _bytesTotal;
+ }
+
+private:
+
+ /// Return true if a load is currently in progress.
+ //
+ /// NOTE: doesn't lock the _loadRequestsMutex !
+ ///
+ bool isLoading() const
+ {
+ return _currentLoad != _loadRequests.end();
+ }
+
+
+ /// Process current load request
+ //
+ /// This function is called by addLoadRequest()
+ /// if the just-added request is the only one in the queue
+ /// and by endCurrentLoad() if any other request is
+ /// in the queue.
+ ///
+ /// It will start a separate thread to process
+ /// the request
+ ///
+ void processCurrentLoadRequest();
+
+ /// Mark current load as done
+ //
+ /// This function removes the request from
+ /// the _loadRequests list and resets the
+ /// _currentLoad iterator.
+ /// If _loadRequests is not empty, it processes
+ /// the next request.
+ ///
+ void endCurrentLoad();
+
+ /// \brief
+ /// Add a load request to the queue, processing it
+ /// if no other loads are in progress.
+ ///
+ void addLoadRequest(const std::string& urlstr);
+
+ /// Load all data from the _stream input.
+ //
+ /// This function should be run by a separate thread.
+ ///
+ void completeLoad();
+
+ /// Since I haven't found a way to pass boost::thread
+ /// constructor a non-static function, this is here to
+ /// workaround that limitation (in either boost or more
+ /// likely my own knowledge of it)
+ static void execCompleteLoad(LoadVars* lv) {
+ lv->completeLoad();
+ }
+
+ /// Parse an url-encoded query string
+ //
+ /// Variables in the string will be added as properties
+ /// of this object.
+ ///
+ /// @param querystring
+ /// An url-encoded query string.
+ /// The string will be parsed using URL::parse_querystring
+ ///
+ /// @return the number of variables found in the string
+ ///
+ size_t parse(const std::string& querystring);
+
+ /// Dispatch load event, if any
+ void dispatchLoadEvent();
+
+ /// Dispatch data event, if any
+ void dispatchDataEvent();
+
+ void setLoadHandler(as_function* fn) {
+ _onLoad = fn;
+ }
+
+ as_function* getLoadHandler() {
+ return _onLoad.get();
+ }
+
+ as_function* getDataHandler() {
+ return _onData.get();
+ }
+
+ void setDataHandler(as_function* fn) {
+ _onData = fn;
+ }
+
+ static void onData_getset(const fn_call& fn);
+
+ static void onLoad_getset(const fn_call& fn);
+
+ static void getBytesLoaded_method(const fn_call& fn);
+
+ static void getBytesTotal_method(const fn_call& fn);
+
+ boost::intrusive_ptr<as_function> _onLoad;
+
+ boost::intrusive_ptr<as_function> _onData;
+
+ boost::thread* _loaderThread;
+
+ as_environment* _env;
+
+ size_t _bytesTotal;
+
+ size_t _bytesLoaded;
+
+ /// List of load requests
+ typedef std::list<std::string> LoadRequests;
+
+ /// Load requests queue
+ //
+ /// Scheduling loads are needed because LoadVars
+ /// exposes a getBytesLoaded() and getBytesTotal()
+ /// which prevent parallel loads to properly work
+ /// (ie: values from *which* load should be reported?)
+ ///
+ LoadRequests _loadRequests;
+
+ /// The load currently in progress.
+ //
+ /// When _currentLoad == _loadRequests.end()
+ /// no load is in progress.
+ ///
+ LoadRequests::iterator _currentLoad;
+
+ std::auto_ptr<tu_file> _stream;
+
+ mutable boost::mutex _loadRequestsMutex;
+};
+
+void
+LoadVars::attachLoadVarsInterface(as_object& o)
{
o.init_member("addRequestHeader", &loadvars_addrequestheader);
o.init_member("decode", &loadvars_decode);
- o.init_member("getBytesLoaded", &loadvars_getbytesloaded);
- o.init_member("getBytesTotal", &loadvars_getbytestotal);
+ o.init_member("getBytesLoaded", &LoadVars::getBytesLoaded_method);
+ o.init_member("getBytesTotal", &LoadVars::getBytesTotal_method);
o.init_member("load", &loadvars_load);
o.init_member("send", &loadvars_send);
o.init_member("sendAndLoad", &loadvars_sendandload);
o.init_member("toString", &loadvars_tostring);
+
+ boost::intrusive_ptr<builtin_function> gettersetter;
+
+ gettersetter = new builtin_function(&LoadVars::onLoad_getset, NULL);
+ o.init_property("onLoad", *gettersetter, *gettersetter);
+
+ gettersetter = new builtin_function(&LoadVars::onData_getset, NULL);
+ o.init_property("onData", *gettersetter, *gettersetter);
}
-static as_object*
-getLoadVarsInterface()
+as_object*
+LoadVars::getLoadVarsInterface()
{
static boost::intrusive_ptr<as_object> o;
if ( ! o )
@@ -64,68 +263,340 @@
return o.get();
}
-class loadvars_as_object: public as_object
+/*private*/
+void
+LoadVars::dispatchDataEvent()
{
+ if ( ! _onData ) return;
-public:
+ log_msg("Calling _onData func");
+ // This would be the function calls "context"
+ // will likely be the same to all events
+ as_value ret;
+ fn_call fn(&ret, this, _env, 0, 0);
- loadvars_as_object()
- :
- as_object(getLoadVarsInterface())
- {}
+ _onData->call(fn);
+}
- // override from as_object ?
- //const char* get_text_value() const { return "LoadVars"; }
+/* private */
+void
+LoadVars::dispatchLoadEvent()
+{
+ if ( ! _onLoad ) return;
- // override from as_object ?
- //double get_numeric_value() const { return 0; }
-};
+ log_msg("Calling _onLoad func");
+ // This would be the function calls "context"
+ // will likely be the same to all events
+ as_value ret;
+ fn_call fn(&ret, this, _env, 0, 0);
-void loadvars_addrequestheader(const fn_call& /*fn*/) {
- log_warning("%s: unimplemented \n", __FUNCTION__);
+ _onLoad->call(fn);
}
-void loadvars_decode(const fn_call& /*fn*/) {
- log_warning("%s: unimplemented \n", __FUNCTION__);
+
+/* private */
+size_t
+LoadVars::parse(const std::string& str)
+{
+ using std::map;
+ using std::string;
+
+ typedef map<string, string> ValuesMap;
+
+ ValuesMap vals;
+ URL::parse_querystring(str, vals);
+
+ for (ValuesMap::iterator it=vals.begin(), itEnd=vals.end();
+ it != itEnd; ++it)
+ {
+ set_member(it->first, as_value(it->second.c_str()));
+ //log_msg("Setting %s == %s", it->first.c_str(),
it->second.c_str());
+ }
+
+ return vals.size();
}
-void loadvars_getbytesloaded(const fn_call& /*fn*/) {
- log_warning("%s: unimplemented \n", __FUNCTION__);
+
+/* private */
+void
+LoadVars::completeLoad()
+{
+ using std::string;
+
+ assert(isLoading());
+
+ // TODO: how to set _bytesTotal ?
+
+ // this is going to override any previous setting,
+ // better do this inside a subclass (in a separate thread)
+ _bytesLoaded = 0;
+
+ //log_msg("completeLoad called");
+
+ string toparse;
+
+ size_t CHUNK_SIZE = 1024;
+ char buf[CHUNK_SIZE];
+ unsigned int parsedLines = 0;
+ while ( size_t read = _stream->read_bytes(buf, CHUNK_SIZE) )
+ {
+ // TODO: use read_string ?
+ string chunk(buf, read);
+ toparse += chunk;
+
+ //log_msg("toparse: %s", toparse.c_str());
+
+ // parse remainder
+ size_t lastamp = toparse.rfind('&');
+ if ( lastamp != string::npos )
+ {
+ string parseable = toparse.substr(0, lastamp);
+ //log_msg("parseable: %s", parseable.c_str());
+ parse(parseable);
+ //log_msg("Parsed %d vals", parsed);
+ toparse = toparse.substr(lastamp+1);
+ ++parsedLines;
+ }
+
+ _bytesLoaded += read;
+ dispatchDataEvent();
+
+ // found newline, discard anything before that
+ if ( strchr(buf, '\n') )
+ {
+ if ( parsedLines ) break;
+ else toparse.clear();
+ }
+
+ // eof, get out !
+ if ( _stream->get_eof() ) break;
+ }
+
+ if ( ! toparse.empty() )
+ {
+ parse(toparse);
+ }
+
+ _stream->go_to_end();
+ _bytesLoaded = _stream->get_position();
+ _bytesTotal = _bytesLoaded;
+
+ dispatchLoadEvent();
+
+ endCurrentLoad();
+}
+
+void
+LoadVars::endCurrentLoad()
+{
+ boost::mutex::scoped_lock lock(_loadRequestsMutex);
+ _loadRequests.erase(_currentLoad);
+ if ( _loadRequests.empty() )
+ {
+ _currentLoad = _loadRequests.end();
+ }
+ else
+ {
+ _currentLoad = _loadRequests.begin();
+ processCurrentLoadRequest();
+ }
}
-void loadvars_getbytestotal(const fn_call& /*fn*/) {
- log_warning("%s: unimplemented \n", __FUNCTION__);
+
+void
+LoadVars::processCurrentLoadRequest()
+{
+
+ std::string& urlstr = *_currentLoad;
+ StreamProvider& provider = StreamProvider::getDefaultInstance();
+ URL url(urlstr, get_base_url());
+
+ _stream.reset ( provider.getStream(url) );
+
+ // WARNING: completeLoad must be called by a separate thread,
+ // or we'll end up with a dead lock (completeLoad calls
+ // endCurrentLoad and is called by it!)
+ // When are we going to drop this ?
+ boost::thread thread(boost::bind(LoadVars::execCompleteLoad, this));
+ //boost::thread* thr = new boost::thread
(boost::bind(LoadVars::execCompleteLoad, this));
}
-void loadvars_load(const fn_call& /*fn*/) {
- log_warning("%s: unimplemented \n", __FUNCTION__);
+
+void
+LoadVars::addLoadRequest(const std::string& urlstr)
+{
+ boost::mutex::scoped_lock lock(_loadRequestsMutex);
+ _loadRequests.insert(_loadRequests.end(), urlstr);
+ if ( ! isLoading() )
+ {
+ _currentLoad = _loadRequests.begin();
+ processCurrentLoadRequest();
+ }
}
-void loadvars_send(const fn_call& /*fn*/) {
- log_warning("%s: unimplemented \n", __FUNCTION__);
+
+void
+LoadVars::load(const std::string& urlstr, as_environment* env)
+{
+ // I belive that the environment should be set
+ // once and for all calls
+ //assert(_env == env);
+ _env = env;
+
+ addLoadRequest(urlstr);
+
}
-void loadvars_sendandload(const fn_call& /*fn*/) {
- log_warning("%s: unimplemented \n", __FUNCTION__);
+
+static LoadVars*
+ensureLoadVars(as_object* obj)
+{
+ LoadVars* ret = dynamic_cast<LoadVars*>(obj);
+ if ( ! ret )
+ {
+ throw ActionException("builtin method or gettersetter for
LoadVars objects called against non-LoadVars instance");
+ }
+ return ret;
}
-void loadvars_tostring(const fn_call& /*fn*/) {
- log_warning("%s: unimplemented \n", __FUNCTION__);
+
+/* private static */
+void
+LoadVars::onLoad_getset(const fn_call& fn)
+{
+ LoadVars* ptr = ensureLoadVars(fn.this_ptr);
+
+ if ( fn.nargs == 0 ) // getter
+ {
+ as_function* h = ptr->getLoadHandler();
+ if ( h ) fn.result->set_as_function(h);
+ else fn.result->set_undefined();
+ }
+ else // setter
+ {
+ as_function* h = fn.arg(0).to_as_function();
+ if ( h ) ptr->setLoadHandler(h);
+ }
}
+/* private static */
void
+LoadVars::onData_getset(const fn_call& fn)
+{
+
+ LoadVars* ptr = ensureLoadVars(fn.this_ptr);
+
+ if ( fn.nargs == 0 ) // getter
+ {
+ as_function* h = ptr->getDataHandler();
+ if ( h ) fn.result->set_as_function(h);
+ else fn.result->set_undefined();
+ }
+ else // setter
+ {
+ as_function* h = fn.arg(0).to_as_function();
+ if ( h ) ptr->setDataHandler(h);
+ }
+}
+
+
+static void
+loadvars_addrequestheader(const fn_call& fn)
+{
+ LoadVars* ptr = ensureLoadVars(fn.this_ptr);
+ UNUSED(ptr);
+ log_error("%s: unimplemented", __FUNCTION__);
+}
+
+static void
+loadvars_decode(const fn_call& fn)
+{
+ LoadVars* ptr = ensureLoadVars(fn.this_ptr);
+ UNUSED(ptr);
+ log_error("%s: unimplemented", __FUNCTION__);
+}
+
+void
+LoadVars::getBytesLoaded_method(const fn_call& fn)
+{
+ LoadVars* ptr = ensureLoadVars(fn.this_ptr);
+ fn.result->set_int(ptr->getBytesLoaded());
+}
+
+void
+LoadVars::getBytesTotal_method(const fn_call& fn)
+{
+ LoadVars* ptr = ensureLoadVars(fn.this_ptr);
+ fn.result->set_int(ptr->getBytesTotal());
+}
+
+static void
+loadvars_load(const fn_call& fn)
+{
+ LoadVars* obj = ensureLoadVars(fn.this_ptr);
+
+ if ( fn.nargs < 1 )
+ {
+ IF_VERBOSE_ASCODING_ERRORS(
+ log_aserror("LoadVars.load() requires at least one argument");
+ );
+ fn.result->set_bool(false);
+ return;
+ }
+
+ std::string urlstr = fn.arg(0).to_std_string();
+ if ( urlstr.empty() )
+ {
+ IF_VERBOSE_ASCODING_ERRORS(
+ log_aserror("LoadVars.load(): invalid empty url ");
+ );
+ fn.result->set_bool(false);
+ return;
+ }
+
+ obj->load(urlstr, fn.env);
+ fn.result->set_bool(true);
+
+}
+
+static void
+loadvars_send(const fn_call& fn)
+{
+ LoadVars* ptr = ensureLoadVars(fn.this_ptr);
+ UNUSED(ptr);
+ log_error("%s: unimplemented", __FUNCTION__);
+}
+
+static void
+loadvars_sendandload(const fn_call& fn)
+{
+ LoadVars* ptr = ensureLoadVars(fn.this_ptr);
+ UNUSED(ptr);
+ log_error("%s: unimplemented", __FUNCTION__);
+}
+
+static void
+loadvars_tostring(const fn_call& fn)
+{
+ LoadVars* ptr = ensureLoadVars(fn.this_ptr);
+ UNUSED(ptr);
+ log_error("%s: unimplemented", __FUNCTION__);
+}
+
+static void
loadvars_ctor(const fn_call& fn)
{
- boost::intrusive_ptr<as_object> obj = new loadvars_as_object;
+ boost::intrusive_ptr<as_object> obj = new LoadVars;
fn.result->set_as_object(obj.get()); // will keep alive
}
// extern (used by Global.cpp)
-void loadvars_class_init(as_object& global)
+void
+loadvars_class_init(as_object& global)
{
// This is going to be the global LoadVars "class"/"function"
static boost::intrusive_ptr<builtin_function> cl;
if ( cl == NULL )
{
- cl=new builtin_function(&loadvars_ctor, getLoadVarsInterface());
+ cl=new builtin_function(&loadvars_ctor,
LoadVars::getLoadVarsInterface());
// replicate all interface to class, to be able to access
// all methods as static functions
- attachLoadVarsInterface(*cl);
+ LoadVars::attachLoadVarsInterface(*cl);
}
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Gnash-commit] gnash/server as_function.h asobj/LoadVars.cpp,
Sandro Santilli <=