use the VERSION instead of master in the revno.h generated from a src tarball
[gnash.git] / libcore / MovieLoader.cpp
bloba2a3418e43d8b6a14d68f389caef7f46e6fe1e56
1 //
2 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
3 // Foundation, Inc
4 //
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.
9 //
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.
14 //
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"
22 #include <memory>
23 #include <boost/bind.hpp>
24 #include <algorithm>
26 #include "log.h"
27 #include "MovieFactory.h"
28 #include "movie_root.h"
29 #include "DisplayObject.h"
30 #include "as_value.h"
31 #include "as_object.h"
32 #include "movie_definition.h"
33 #include "Movie.h"
34 #include "MovieClip.h"
35 #include "URL.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
44 namespace gnash {
46 MovieLoader::MovieLoader(movie_root& mr)
48 _movieRoot(mr),
49 _thread(0),
50 _barrier(2) // main and loader thread
54 // private
55 // runs in loader thread
56 void
57 MovieLoader::processRequests()
59 // let _thread assignment happen before going on
60 _barrier.wait();
62 #ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
63 log_debug("Starting movie loader thread");
64 #endif
67 while (1) {
69 // check for shutdown/cancel request
70 if (killed()) {
71 #ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
72 log_debug("Loader thread killed");
73 #endif
74 return;
77 #ifdef GNASH_DEBUG_LOCKING
78 log_debug("processRequests: lock on requests: trying");
79 #endif
81 boost::mutex::scoped_lock lock(_requestsMutex);
83 #ifdef GNASH_DEBUG_LOCKING
84 log_debug("processRequests: lock on requests: obtained");
85 #endif
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));
92 if (it == endIt) {
94 #ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
95 log_debug("Movie loader thread getting to sleep (nothing more to do)");
96 #endif
97 // all completed, we can get to sleep
98 _wakeup.wait(lock);
100 #ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
101 log_debug("Movie loader thread waked up");
102 #endif
104 #ifdef GNASH_DEBUG_LOCKING
105 log_debug("processRequests: lock on requests: release");
106 #endif
108 continue;
111 Request& lr = *it;
113 #ifdef GNASH_DEBUG_LOCKING
114 log_debug("processRequests: lock on requests: release");
115 #endif
117 lock.unlock(); // now main thread can continue to push requests
119 processRequest(lr);
125 // private
126 // runs in loader thread
127 void
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",
136 r.getTarget());
137 #endif
139 boost::intrusive_ptr<movie_definition> md (
140 MovieFactory::makeMovie(url, _movieRoot.runResources(),
141 NULL, true, postdata)
143 r.setCompleted(md);
145 #ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
146 log_debug("Movie loader thread completed request for target %s",
147 r.getTarget());
148 #endif
151 // public
152 // runs in main thread
153 void
154 MovieLoader::clear()
156 if (_thread.get()) {
158 #ifdef GNASH_DEBUG_LOCKING
159 log_debug("clear: lock on requests: trying");
160 #endif
162 boost::mutex::scoped_lock requestsLock(_requestsMutex);
164 #ifdef GNASH_DEBUG_LOCKING
165 log_debug("clear: lock on requests: obtained");
166 #endif
168 #ifdef GNASH_DEBUG_LOCKING
169 log_debug("clear: lock on kill: trying");
170 #endif
172 boost::mutex::scoped_lock lock(_killMutex);
174 #ifdef GNASH_DEBUG_LOCKING
175 log_debug("clear: lock on kill: obtained");
176 #endif
178 _killed = true;
180 #ifdef GNASH_DEBUG_LOCKING
181 log_debug("clear: lock on kill: release for kill");
182 #endif
184 lock.unlock();
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 ater notify_all");
192 #endif
193 requestsLock.unlock(); // allow the thread to die
195 log_debug("MovieLoader notified, joining");
196 _thread->join();
197 log_debug("MovieLoader joined");
198 _thread.reset();
201 // no thread now, can clean w/out locking
202 clearRequests();
204 #ifdef GNASH_DEBUG_LOCKING
205 log_debug("clear: lock on requests: release if not after notify_all");
206 #endif
209 // private, no locking
210 // runs in main thread
211 void
212 MovieLoader::clearRequests()
214 _requests.clear();
217 // private
218 // runs in main thread
219 bool
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();
231 if (!md) {
233 if (targetDO && handler) {
234 // Signal load error
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).
247 as_value arg3(0.0);
249 callMethod(handler, NSV::PROP_BROADCAST_MESSAGE, "onLoadError",
250 arg1, arg2, arg3);
252 return true; // nothing to do, but completed
255 const URL& url = r.getURL();
257 Movie* extern_movie = md->createMovie(*_movieRoot.getVM().getGlobal());
258 if (!extern_movie) {
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);
269 if (targetDO) {
270 targetDO->getLoadedMovie(extern_movie);
272 else {
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);
281 else {
282 log_debug("Target %s of a loadMovie request doesn't exist at "
283 "load complete time", target);
284 return true;
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);
330 return true;
334 // private
335 // runs in main thread
336 void
337 MovieLoader::processCompletedRequests()
339 //GNASH_REPORT_FUNCTION;
341 for (;;) {
343 #ifdef GNASH_DEBUG_LOCKING
344 log_debug("processCompletedRequests: lock on requests: trying");
345 #endif
347 boost::mutex::scoped_lock requestsLock(_requestsMutex);
349 #ifdef GNASH_DEBUG_LOCKING
350 log_debug("processCompletedRequests: lock on requests: obtained");
351 #endif
353 #ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
354 log_debug("Checking %d requests for completeness",
355 _requests.size());
356 #endif
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");
367 #endif
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());
375 #endif
377 bool checkit = processCompletedRequest(firstCompleted);
378 assert(checkit);
380 #ifdef GNASH_DEBUG_LOCKING
381 log_debug("processCompletedRequests: lock on requests for removal: "
382 "trying");
383 #endif
385 requestsLock.lock();
387 #ifdef GNASH_DEBUG_LOCKING
388 log_debug("processCompletedRequests: lock on requests for removal: "
389 "obtained");
390 #endif
392 _requests.erase(it);
394 #ifdef GNASH_DEBUG_LOCKING
395 log_debug("processCompletedRequests: lock on requests for removal: "
396 "release");
397 #endif
401 // private
402 // runs in loader thread
403 bool
404 MovieLoader::killed()
406 boost::mutex::scoped_lock lock(_killMutex);
407 return _killed;
410 // public
411 // runs in main thread
412 void
413 MovieLoader::loadMovie(const std::string& urlstr,
414 const std::string& target,
415 const std::string& data,
416 MovieClip::VariablesMethod method,
417 as_object* handler)
420 /// URL security is checked in StreamProvider::getStream() down the
421 /// chain.
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
437 : 0;
439 #ifdef GNASH_DEBUG_LOCKING
440 log_debug("loadMovie: lock on requests: trying");
441 #endif
443 boost::mutex::scoped_lock lock(_requestsMutex);
445 #ifdef GNASH_DEBUG_LOCKING
446 log_debug("loadMovie: lock on requests: obtained");
447 #endif
449 _requests.push_front(
450 new Request(url, target, postdata, handler)
453 // Start or wake up the loader thread
454 if (!_thread.get()) {
455 _killed=false;
456 _thread.reset(new boost::thread(boost::bind(
457 &MovieLoader::processRequests, this)));
458 _barrier.wait(); // let execution start before proceeding
460 else {
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");
467 #endif
470 // public
471 // runs in main thread
472 MovieLoader::~MovieLoader()
474 clear(); // will kill the thread
477 // public
478 // runs in main thread
479 void
480 MovieLoader::setReachable() const
483 #ifdef GNASH_DEBUG_LOCKING
484 log_debug("setReachable: lock on requests: trying");
485 #endif
487 boost::mutex::scoped_lock lock(_requestsMutex);
489 #ifdef GNASH_DEBUG_LOCKING
490 log_debug("setReachable: lock on requests: obtained");
491 #endif
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");
498 #endif
502 } // namespace gnash