2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
3 // 2011 Free Software Foundation, Inc
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 #include "MovieLoader.h"
23 #include <boost/bind.hpp>
27 #include "MovieFactory.h"
28 #include "movie_root.h"
29 #include "DisplayObject.h"
31 #include "as_object.h"
32 #include "movie_definition.h"
34 #include "MovieClip.h"
36 #include "namedStrings.h"
37 #include "ExecutableCode.h"
38 #include "RunResources.h"
39 #include "StreamProvider.h"
41 //#define GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING 1
42 //#define GNASH_DEBUG_LOCKING 1
46 MovieLoader::MovieLoader(movie_root
& mr
)
50 _barrier(2) // main and loader thread
55 // runs in loader thread
57 MovieLoader::processRequests()
59 // let _thread assignment happen before going on
62 #ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
63 log_debug("Starting movie loader thread");
69 // check for shutdown/cancel request
71 #ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
72 log_debug("Loader thread killed");
77 #ifdef GNASH_DEBUG_LOCKING
78 log_debug("processRequests: lock on requests: trying");
81 boost::mutex::scoped_lock
lock(_requestsMutex
);
83 #ifdef GNASH_DEBUG_LOCKING
84 log_debug("processRequests: lock on requests: obtained");
87 // Find first non-completed request (the others we'll wait)
88 Requests::iterator endIt
= _requests
.end();
89 Requests::iterator it
= find_if(_requests
.begin(), endIt
,
90 boost::bind(&Request::pending
, _1
));
94 #ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
95 log_debug("Movie loader thread getting to sleep (nothing more to do)");
97 // all completed, we can get to sleep
100 #ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
101 log_debug("Movie loader thread waked up");
104 #ifdef GNASH_DEBUG_LOCKING
105 log_debug("processRequests: lock on requests: release");
113 #ifdef GNASH_DEBUG_LOCKING
114 log_debug("processRequests: lock on requests: release");
117 lock
.unlock(); // now main thread can continue to push requests
126 // runs in loader thread
128 MovieLoader::processRequest(Request
& r
)
130 const URL
& url
= r
.getURL();
131 bool usePost
= r
.usePost();
132 const std::string
* postdata
= usePost
? &(r
.getPostData()) : 0;
134 #ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
135 log_debug("Movie loader thread processing request for target %s",
139 boost::intrusive_ptr
<movie_definition
> md (
140 MovieFactory::makeMovie(url
, _movieRoot
.runResources(),
141 NULL
, true, postdata
)
145 #ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
146 log_debug("Movie loader thread completed request for target %s",
152 // runs in main thread
158 #ifdef GNASH_DEBUG_LOCKING
159 log_debug("clear: lock on requests: trying");
162 boost::mutex::scoped_lock
requestsLock(_requestsMutex
);
164 #ifdef GNASH_DEBUG_LOCKING
165 log_debug("clear: lock on requests: obtained");
168 #ifdef GNASH_DEBUG_LOCKING
169 log_debug("clear: lock on kill: trying");
172 boost::mutex::scoped_lock
lock(_killMutex
);
174 #ifdef GNASH_DEBUG_LOCKING
175 log_debug("clear: lock on kill: obtained");
180 #ifdef GNASH_DEBUG_LOCKING
181 log_debug("clear: lock on kill: release for kill");
186 log_debug("waking up loader thread");
188 _wakeup
.notify_all(); // in case it was sleeping
190 #ifdef GNASH_DEBUG_LOCKING
191 log_debug("clear: lock on requests: release after notify_all");
193 requestsLock
.unlock(); // allow the thread to die
195 log_debug("MovieLoader notified, joining");
197 log_debug("MovieLoader joined");
201 // no thread now, can clean w/out locking
204 #ifdef GNASH_DEBUG_LOCKING
205 log_debug("clear: lock on requests: release if not after notify_all");
209 // private, no locking
210 // runs in main thread
212 MovieLoader::clearRequests()
218 // runs in main thread
220 MovieLoader::processCompletedRequest(const Request
& r
)
222 //GNASH_REPORT_FUNCTION;
224 boost::intrusive_ptr
<movie_definition
> md
;
225 if (!r
.getCompleted(md
)) return false; // not completed yet
227 const std::string
& target
= r
.getTarget();
228 DisplayObject
* targetDO
= _movieRoot
.findCharacterByTarget(target
);
229 as_object
* handler
= r
.getHandler();
233 if (targetDO
&& handler
) {
235 // Tested not to happen if target isn't found at time of loading
238 as_value
arg1(getObject(targetDO
));
240 // FIXME: docs suggest the string can be either "URLNotFound" or
241 // "LoadNeverCompleted". This is neither of them:
242 as_value
arg2("Failed to load movie or jpeg");
244 // FIXME: The last argument is HTTP status, or 0 if no connection
245 // was attempted (sandbox) or no status information is available
246 // (supposedly the Adobe mozilla plugin).
249 callMethod(handler
, NSV::PROP_BROADCAST_MESSAGE
, "onLoadError",
252 return true; // nothing to do, but completed
255 const URL
& url
= r
.getURL();
257 Movie
* extern_movie
= md
->createMovie(*_movieRoot
.getVM().getGlobal());
259 log_error(_("Can't create Movie instance "
260 "for definition loaded from %s"), url
);
261 return true; // completed in any case...
264 // Parse query string
265 MovieClip::MovieVariables vars
;
266 url
.parse_querystring(url
.querystring(), vars
);
267 extern_movie
->setVariables(vars
);
270 targetDO
->getLoadedMovie(extern_movie
);
273 unsigned int levelno
;
274 const int version
= _movieRoot
.getVM().getSWFVersion();
275 if (isLevelTarget(version
, target
, levelno
)) {
276 log_debug("processCompletedRequest: _level loading "
277 "(level %u)", levelno
);
278 extern_movie
->set_depth(levelno
+ DisplayObject::staticDepthOffset
);
279 _movieRoot
.setLevel(levelno
, extern_movie
);
282 log_debug("Target %s of a loadMovie request doesn't exist at "
283 "load complete time", target
);
288 if (handler
&& targetDO
) {
289 // Dispatch onLoadStart
290 // FIXME: should be signalled before starting to load
291 // (0/-1 bytes loaded/total) but still with *new*
292 // display object as target (ie: the target won't
293 // contain members set either before or after loadClip.
294 callMethod(handler
, NSV::PROP_BROADCAST_MESSAGE
, "onLoadStart",
295 getObject(targetDO
));
297 // Dispatch onLoadProgress
298 // FIXME: should be signalled on every readNonBlocking()
299 // with a buffer size of 65535 bytes.
301 size_t bytesLoaded
= md
->get_bytes_loaded();
302 size_t bytesTotal
= md
->get_bytes_total();
303 callMethod(handler
, NSV::PROP_BROADCAST_MESSAGE
, "onLoadProgress",
304 getObject(targetDO
), bytesLoaded
, bytesTotal
);
306 // Dispatch onLoadComplete
307 // FIXME: find semantic of last arg
308 callMethod(handler
, NSV::PROP_BROADCAST_MESSAGE
, "onLoadComplete",
309 getObject(targetDO
), as_value(0.0));
312 // Dispatch onLoadInit
314 // This event must be dispatched when actions
315 // in first frame of loaded clip have been executed.
317 // Since getLoadedMovie or setLevel above will invoke
318 // construct() and thus queue all actions in first
319 // frame, we'll queue the
320 // onLoadInit call next, so it happens after the former.
322 std::auto_ptr
<ExecutableCode
> code(
323 new DelayedFunctionCall(targetDO
, handler
,
324 NSV::PROP_BROADCAST_MESSAGE
,
325 "onLoadInit", getObject(targetDO
)));
327 getRoot(*handler
).pushAction(code
, movie_root::PRIORITY_DOACTION
);
335 // runs in main thread
337 MovieLoader::processCompletedRequests()
339 //GNASH_REPORT_FUNCTION;
343 #ifdef GNASH_DEBUG_LOCKING
344 log_debug("processCompletedRequests: lock on requests: trying");
347 boost::mutex::scoped_lock
requestsLock(_requestsMutex
);
349 #ifdef GNASH_DEBUG_LOCKING
350 log_debug("processCompletedRequests: lock on requests: obtained");
353 #ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
354 log_debug("Checking %d requests for completeness",
358 Requests::iterator endIt
= _requests
.end();
359 Requests::iterator it
= find_if(_requests
.begin(), endIt
,
360 boost::bind(&Request::completed
, _1
));
362 // Releases scoped lock.
363 if (it
== endIt
) break;
365 #ifdef GNASH_DEBUG_LOCKING
366 log_debug("processCompletedRequests: lock on requests: releasing");
368 requestsLock
.unlock();
370 Request
& firstCompleted
= *it
;
372 #ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
373 log_debug("Load request for target %s completed",
374 firstCompleted
->getTarget());
377 bool checkit
= processCompletedRequest(firstCompleted
);
380 #ifdef GNASH_DEBUG_LOCKING
381 log_debug("processCompletedRequests: lock on requests for removal: "
387 #ifdef GNASH_DEBUG_LOCKING
388 log_debug("processCompletedRequests: lock on requests for removal: "
394 #ifdef GNASH_DEBUG_LOCKING
395 log_debug("processCompletedRequests: lock on requests for removal: "
402 // runs in loader thread
404 MovieLoader::killed()
406 boost::mutex::scoped_lock
lock(_killMutex
);
411 // runs in main thread
413 MovieLoader::loadMovie(const std::string
& urlstr
,
414 const std::string
& target
,
415 const std::string
& data
,
416 MovieClip::VariablesMethod method
,
420 /// URL security is checked in StreamProvider::getStream() down the
422 URL
url(urlstr
, _movieRoot
.runResources().streamProvider().baseURL());
424 /// If the method is MovieClip::METHOD_NONE, we send no data.
425 if (method
== MovieClip::METHOD_GET
)
427 /// GET: append data to query string.
428 const std::string
& qs
= url
.querystring();
429 std::string
varsToSend(qs
.empty() ? "?" : "&");
430 varsToSend
.append(urlstr
);
431 url
.set_querystring(qs
+ varsToSend
);
434 log_debug("MovieLoader::loadMovie(%s, %s)", url
.str(), target
);
436 const std::string
* postdata
= (method
== MovieClip::METHOD_POST
) ? &data
439 #ifdef GNASH_DEBUG_LOCKING
440 log_debug("loadMovie: lock on requests: trying");
443 boost::mutex::scoped_lock
lock(_requestsMutex
);
445 #ifdef GNASH_DEBUG_LOCKING
446 log_debug("loadMovie: lock on requests: obtained");
449 _requests
.push_front(
450 new Request(url
, target
, postdata
, handler
)
453 // Start or wake up the loader thread
454 if (!_thread
.get()) {
456 _thread
.reset(new boost::thread(boost::bind(
457 &MovieLoader::processRequests
, this)));
458 _barrier
.wait(); // let execution start before proceeding
461 log_debug("loadMovie: waking up existing thread");
462 _wakeup
.notify_all();
465 #ifdef GNASH_DEBUG_LOCKING
466 log_debug("loadMovie: lock on requests: release");
471 // runs in main thread
472 MovieLoader::~MovieLoader()
474 clear(); // will kill the thread
478 // runs in main thread
480 MovieLoader::setReachable() const
483 #ifdef GNASH_DEBUG_LOCKING
484 log_debug("setReachable: lock on requests: trying");
487 boost::mutex::scoped_lock
lock(_requestsMutex
);
489 #ifdef GNASH_DEBUG_LOCKING
490 log_debug("setReachable: lock on requests: obtained");
493 std::for_each(_requests
.begin(), _requests
.end(),
494 boost::mem_fn(&Request::setReachable
));
496 #ifdef GNASH_DEBUG_LOCKING
497 log_debug("setReachable: lock on requests: release");