don't add (LIBLTDL) to LDFLAGS, libltdl is part of libgnashbase.
[gnash.git] / libcore / ExternalInterface.cpp
blob0fcddfaa9788ec10fb28279955fcda348e58d1a1
1 // ExternalInterface.cpp: ActionScript "ExternalInterface" support
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
4 // Foundation, Inc
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
11 // This program 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
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "ExternalInterface.h"
23 #include <map>
24 #include <vector>
25 #include <sstream>
26 #include <boost/algorithm/string/erase.hpp>
27 #include <boost/shared_ptr.hpp>
28 #include <boost/scoped_array.hpp>
29 #include <algorithm>
31 #include "GnashSystemNetHeaders.h"
32 #include "GnashSystemFDHeaders.h"
34 #include "StringPredicates.h"
35 #include "fn_call.h"
36 #include "Global_as.h"
37 #include "smart_ptr.h" // for boost intrusive_ptr
38 #include "VM.h"
39 #include "rc.h"
40 #include "as_value.h"
41 #include "as_object.h"
42 #include "XML_as.h"
43 #include "Array_as.h"
44 #include "namedStrings.h"
45 #include "Global_as.h"
46 #include "PropertyList.h"
47 #include "movie_root.h"
48 #include "log.h"
50 namespace gnash {
52 namespace {
54 class Enumerator : public KeyVisitor
56 public:
57 Enumerator(std::vector<ObjectURI>& uris) : _uris(uris) {}
58 void operator()(const ObjectURI& u) {
59 _uris.push_back(u);
61 private:
62 std::vector<ObjectURI>& _uris;
67 /// Convert an AS object to an XML string.
68 std::string
69 ExternalInterface::_objectToXML(as_object *obj)
71 // GNASH_REPORT_FUNCTION;
73 if ( ! _visited.insert(obj).second ) {
74 return "<circular/>";
77 std::stringstream ss;
79 ss << "<object>";
81 if (obj) {
82 // Get all the properties
83 VM& vm = getVM(*obj);
84 string_table& st = vm.getStringTable();
85 typedef std::vector<ObjectURI> URIs;
86 URIs uris;
87 Enumerator en(uris);
88 obj->visitKeys(en);
89 for (URIs::const_reverse_iterator i = uris.rbegin(), e = uris.rend();
90 i != e; ++i) {
91 as_value val = getMember(*obj, *i);
92 const std::string& id = i->toString(st);
93 ss << "<property id=\"" << id << "\">";
94 ss << _toXML(val);
95 ss << "</property>";
99 ss << "</object>";
101 return ss.str();
104 /// Convert an AS object to an XML string.
105 std::string
106 ExternalInterface::_toXML(const as_value &val)
108 // GNASH_REPORT_FUNCTION;
110 std::stringstream ss;
112 if (val.is_string()) {
113 ss << "<string>" << val.to_string() << "</string>";
114 } else if (val.is_number()) {
115 ss << "<number>" << val.to_string() << "</number>";
116 } else if (val.is_undefined()) {
117 ss << "<undefined/>";
118 } else if (val.is_null()) {
119 ss << "<null/>";
120 // Exception isn't listed in any docs, but we'll use it for
121 // marshallExceptions.
122 } else if (val.is_exception()) {
123 ss << "<exception>" << val.to_string()<< "</exception>";
124 } else if (val.is_bool()) {
125 ss << (val.to_bool(8) ? "<true/>" : "<false/>");
126 // Function also isn't listed, but it's the only other type
127 // supported by as_value, so leaving it out doesn't seem right.
128 } else if (val.is_function()) {
129 ss << "<function>" << val.to_string() << "</function>";
130 } else if (val.is_object()) {
131 as_object *obj = val.get_object();
132 ss << _objectToXML(obj);
133 } else {
134 log_error("Can't convert unknown type %d", val.to_string());
137 return ss.str();
140 boost::shared_ptr<ExternalInterface::invoke_t>
141 ExternalInterface::ExternalEventCheck(int fd)
143 // GNASH_REPORT_FUNCTION;
145 boost::shared_ptr<ExternalInterface::invoke_t> error;
147 if (fd > 0) {
148 int bytes = 0;
149 ioctlSocket(fd, FIONREAD, &bytes);
150 if (bytes == 0) {
151 return error;
153 log_debug("There are %d bytes in the network buffer", bytes);
154 boost::scoped_array<char> buffer(new char[bytes+1]);
155 // Since we know how bytes are in the network buffer, allocate
156 // some memory to read the data.
157 // terminate incase we want to treat the data like a string.
158 buffer[bytes+1] = 0;
159 const int ret = ::read(fd, buffer.get(), bytes);
160 if (ret) {
161 return parseInvoke(buffer.get());
165 return error;
168 // Parse the XML Invoke message, which looks like this:
170 // <invoke name="LoadMovie" returntype="xml">
171 // <arguments>
172 // <number>2</number>
173 // <string>bogus</string>
174 // </arguments>
175 // </invoke>
177 boost::shared_ptr<ExternalInterface::invoke_t>
178 ExternalInterface::parseInvoke(const std::string &xml)
180 // GNASH_REPORT_FUNCTION;
182 boost::shared_ptr<ExternalInterface::invoke_t> invoke;
183 if (xml.empty()) {
184 return invoke;
187 invoke.reset(new ExternalInterface::invoke_t);
188 std::string::size_type start = 0;
189 std::string::size_type end;
190 std::string tag;
192 // Look for the ending > in the first part of the data for the tag
193 end = xml.find(">");
194 if (end != std::string::npos) {
195 end++; // go past the > character
196 tag = xml.substr(start, end);
197 // Look for the easy ones first
198 if (tag.substr(0, 7) == "<invoke") {
199 // extract the name of the method to invoke
200 start = tag.find("name=") + 5;
201 end = tag.find(" ", start);
202 invoke->name = tag.substr(start, end-start);
203 // Ignore any quote characters around the string
204 boost::erase_first(invoke->name, "\"");
205 boost::erase_last(invoke->name, "\"");
207 // extract the return type of the method
208 start = tag.find("returntype=") + 11;
209 end = tag.find(">", start);
210 invoke->type = tag.substr(start, end-start);
211 // Ignore any quote characters around the string
212 boost::erase_first(invoke->type, "\"");
213 boost::erase_last(invoke->type, "\"");
215 // extract the arguments to the method
216 start = xml.find("<arguments>");
217 end = xml.find("</invoke");
218 tag = xml.substr(start, end-start);
219 invoke->args = ExternalInterface::parseArguments(tag);
223 return invoke;
226 as_value
227 ExternalInterface::parseXML(const std::string &xml)
229 // GNASH_REPORT_FUNCTION;
231 if (xml.empty()) {
232 return as_value();
235 std::string::size_type start = 0;
236 std::string::size_type end;
237 std::string tag;
238 as_value value;
240 // Look for the ending > in the first part of the data for the tag
241 end = xml.find(">");
242 if (end != std::string::npos) {
243 end++; // go past the > character
244 tag = xml.substr(start, end);
245 // Look for the easy ones first
246 if (tag == "<null/>") {
247 value.set_null();
248 } else if (tag == "<void/>") {
249 value.set_undefined();
250 } else if (tag == "<true/>") {
251 value.set_bool(true);
252 } else if (tag == "<false/>") {
253 value.set_bool(false);
254 } else if (tag == "<number>") {
255 start = end;
256 end = xml.find("</number>");
257 std::string str = xml.substr(start, end-start);
258 double num = strtod(str.c_str(), NULL);
259 value.set_double(num);
260 } else if (tag == "<string>") {
261 start = end;
262 end = xml.find("</string>");
263 std::string str = xml.substr(start, end-start);
264 value.set_string(str);
268 // log_debug("Argument is: %s", value.to_string());
269 return value;
272 std::vector<as_value>
273 ExternalInterface::parseArguments(const std::string &xml)
275 // GNASH_REPORT_FUNCTION;
277 std::vector<as_value> args;
278 std::string::size_type start = 0;
279 std::string::size_type end;
281 std::string name;
282 std::string data = xml;
283 std::string tag = "<arguments>";
284 start = data.find(tag);
285 if (start != std::string::npos) {
286 data.erase(0, tag.size());
288 while (!data.empty()) {
289 // Extract the data
290 start = data.find("<", 1); // start past the opening <
291 end = data.find(">", start) + 1;
292 std::string sub = data.substr(0, end);
293 if (data == "</arguments>") {
294 break;
296 args.push_back(parseXML(sub));
297 data.erase(0, end);
300 return args;
303 // Create an Invoke message for the standalone Gnash
304 std::string
305 ExternalInterface::makeInvoke (const std::string &method,
306 const std::vector<as_value> &args)
308 std::stringstream ss;
309 std::vector<as_value>::const_iterator it;
311 ss << "<invoke name=\"" << method << "\" returntype=\"xml\">";
312 ss << "<arguments>";
313 for (it=args.begin(); it != args.end(); ++it) {
314 // Should we avoid re-serializing the same object ?
315 ss << toXML(*it);
318 ss << "</arguments>";
319 ss << "</invoke>";
321 // Add a CR on the end so the output is more readable on the other
322 // end. XL should be ignoring the CR anyway.
323 ss << std::endl;
325 return ss.str();
328 std::string
329 ExternalInterface::makeNull ()
331 return "<null/>";
334 std::string
335 ExternalInterface::makeTrue ()
337 return "<true/>";
340 std::string
341 ExternalInterface::makeFalse ()
343 return "<false/>";
346 std::string
347 ExternalInterface::makeString (const std::string &str)
349 std::stringstream ss;
351 ss << "<string>" << str << "</string>";
353 return ss.str();
357 std::string
358 ExternalInterface::makeProperty (const std::string &id, double num)
360 std::stringstream ss;
361 ss << num;
362 return makeProperty(id, ss.str());
365 std::string
366 ExternalInterface::makeProperty (const std::string &id, int num)
368 std::stringstream ss;
369 ss << num;
370 return makeProperty(id, ss.str());
373 std::string
374 ExternalInterface::makeProperty (const std::string &id, const std::string &data)
376 std::stringstream ss;
378 ss << "<property id=\"" << id << "\">" << data << "</property>";
380 return ss.str();
383 std::string
384 ExternalInterface::makeNumber (double num)
386 std::stringstream ss;
388 ss << "<number>" << num << "</number>";
390 return ss.str();
393 std::string
394 ExternalInterface::makeNumber (int num)
396 std::stringstream ss;
398 ss << "<number>" << num << "</number>";
400 return ss.str();
403 std::string
404 ExternalInterface::makeNumber (unsigned int num)
406 std::stringstream ss;
408 ss << "<number>" << num << "</number>";
410 return ss.str();
413 std::string
414 ExternalInterface::makeArray (std::vector<std::string> &args)
416 std::stringstream ss;
417 std::vector<std::string>::iterator it;
418 int index = 0;
420 ss << "<array>";
421 for (it=args.begin(); it != args.end(); ++it) {
422 ss << "<property id=\"" << index << "\">" << *it << "</property>";
423 index++;
426 ss << "</array>";
428 return ss.str();
431 std::string
432 ExternalInterface::makeObject (std::map<std::string, std::string> &args)
434 std::stringstream ss;
435 std::map<std::string, std::string>::iterator it;
437 ss << "<object>";
438 for (it = args.begin(); it != args.end(); ++it) {
439 ss << "<property id=\"" << it->first << "\">" << it->second << "</property>";
441 ss << "</object>";
443 return ss.str();
446 size_t
447 ExternalInterface::writeBrowser(int fd, const std::string &data)
449 if (fd > 0) {
450 return ::write(fd, data.c_str(), data.size());
453 return -1;
456 std::string
457 ExternalInterface::readBrowser(int fd)
459 std::string empty;
460 // Wait for some data from the player
461 int bytes = 0;
463 ioctlSocket(fd, FIONREAD, &bytes);
465 // No data yet
466 if (bytes == 0) {
467 return empty;
470 log_debug("There are %d bytes in the network buffer", bytes);
472 std::string buf(bytes, '\0');
474 const int ret = ::read(fd, &buf[0], bytes);
475 if (ret <= 0) {
476 return empty;
479 if (ret < bytes) {
480 buf.resize(ret);
483 std::cout << buf << std::endl;
485 return buf;
488 } // end of gnash namespace
490 // local Variables:
491 // mode: C++
492 // indent-tabs-mode: nil
493 // End: