1 // Global.cpp: Global ActionScript class setup, for Gnash.
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software 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.
10 //
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.
15 //
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
19 //
20
21 #ifdef HAVE_CONFIG_H
22 #include "gnashconfig.h"
23 #endif
24
25 #include "Global_as.h"
26
27 #include <map>
28 #include <limits>
29 #include <sstream>
30 #include <boost/lexical_cast.hpp>
31
32 #include "as_object.h"
33 #include "builtin_function.h"
34 #include "movie_root.h"
35 #include "PropFlags.h"
36 #include "as_value.h"
37 #include "as_function.h"
38 #include "NativeFunction.h"
39 #include "AsBroadcaster.h"
40 #include "Boolean_as.h"
41 #include "Color_as.h"
42 #include "Date_as.h"
43 #include "Array_as.h"
44 #include "Error_as.h"
45 #include "String_as.h"
46 #include "Selection_as.h"
47 #include "Number_as.h"
48 #include "Math_as.h"
49 #include "Accessibility_as.h"
50 #include "ContextMenu_as.h"
51 #include "ContextMenuItem_as.h"
52 #include "Key_as.h"
53 #include "Mouse_as.h"
54 #include "Microphone_as.h"
55 #include "Sound_as.h"
56 #include "Camera_as.h"
57 #include "Stage_as.h"
58 #include "MovieClip_as.h"
59 #include "Function_as.h"
60 #include "flash/display/BitmapData_as.h"
61 #include "flash/filters/BitmapFilter_as.h"
62 #include "flash/geom/ColorTransform_as.h"
63 #include "LocalConnection_as.h"
64 #include "XMLSocket_as.h"
65 #include "SharedObject_as.h"
66 #include "NetConnection_as.h"
67 #include "NetStream_as.h"
68 #include "System_as.h"
69 #include "TextSnapshot_as.h"
70 #include "TextField_as.h"
71 #include "TextFormat_as.h"
72 #include "flash/text/TextRenderer_as.h"
73 #include "XML_as.h"
74 #include "XMLNode_as.h"
75 #include "flash/external/ExternalInterface_as.h"
76 #include "MovieClipLoader.h"
77 #include "movie_definition.h"
78 #include "Video_as.h"
79 #include "extension.h"
80 #include "VM.h"
81 #include "Timers.h"
82 #include "URL.h"
83 #include "rc.h"
84 #include "ClassHierarchy.h"
85 #include "namedStrings.h"
86 #include "GnashNumeric.h" // for isfinite replacement
87 #include "flash_pkg.h"
88 #include "fn_call.h"
89 #include "Button.h"
90 #include "LoadVars_as.h"
91 #include "Object.h"
92 #include "LoadableObject.h"
93
94 // Common code to warn and return if a required single arg is not present
95 // and to warn if there are extra args.
96 #define ASSERT_FN_ARGS_IS_1 \
97 if (fn.nargs < 1) { \
98 IF_VERBOSE_ASCODING_ERRORS( \
99 log_aserror(_("%s needs one argument"), __FUNCTION__); \
100 ) \
101 return as_value(); \
102 } \
103 IF_VERBOSE_ASCODING_ERRORS( \
104 if (fn.nargs > 1) \
105 log_aserror(_("%s has more than one argument"), __FUNCTION__); \
106 )
107
108 namespace gnash {
109
110 namespace {
111
112 const ClassHierarchy::NativeClasses& avm1Classes();
113
114 as_value global_trace(const fn_call& fn);
115 as_value global_isNaN(const fn_call& fn);
116 as_value global_isfinite(const fn_call& fn);
117 as_value global_unescape(const fn_call& fn);
118 as_value global_escape(const fn_call& fn);
119 as_value global_parsefloat(const fn_call& fn);
120 as_value global_parseint(const fn_call& fn);
121 as_value global_assetpropflags(const fn_call& fn);
122 as_value global_assetuperror(const fn_call& fn);
123 as_value global_asnative(const fn_call& fn);
124 as_value global_asnew(const fn_call& fn);
125 as_value global_assetnative(const fn_call& fn);
126 as_value global_assetnativeaccessor(const fn_call& fn);
127 as_value global_asconstructor(const fn_call& fn);
128 as_value global_updateAfterEvent(const fn_call& fn);
129 as_value global_setTimeout(const fn_call& fn);
130 as_value global_clearInterval(const fn_call& fn);
131 as_value global_setInterval(const fn_call& fn);
132
133 // These are present in the standalone, not sure about the plugin.
134 as_value global_enableDebugConsole(const fn_call& fn);
135 as_value global_showRedrawRegions(const fn_call& fn);
136
137 // This is a help function for the silly AsSetupError function.
138 as_value local_errorConstructor(const fn_call& fn);
139
140 void registerNatives(as_object& global);
141 }
142
Global_as(VM & vm)143 Global_as::Global_as(VM& vm)
144 :
145 as_object(vm),
146 #ifdef USE_EXTENSIONS
147 _et(new Extension()),
148 #endif
149 _classes(this),
150 _objectProto(new as_object(*this))
151 {
152 }
153
~Global_as()154 Global_as::~Global_as()
155 {
156 }
157
158 as_function*
createFunction(Global_as::ASFunction function)159 Global_as::createFunction(Global_as::ASFunction function)
160 {
161 as_object* proto = createObject(*this);
162 builtin_function* f = new builtin_function(*this, function);
163
164 proto->init_member(NSV::PROP_CONSTRUCTOR, f);
165
166 f->init_member(NSV::PROP_PROTOTYPE, proto);
167
168 as_function* fun =
169 gnash::getOwnProperty(*this, NSV::CLASS_FUNCTION).to_function();
170 if (fun) {
171 const int flags = as_object::DefaultFlags | PropFlags::onlySWF6Up;
172 f->init_member(NSV::PROP_uuPROTOuu, getMember(*fun,
173 NSV::PROP_PROTOTYPE), flags);
174 f->init_member(NSV::PROP_CONSTRUCTOR, fun);
175 }
176 return f;
177 }
178
179 as_object*
createClass(Global_as::ASFunction ctor,as_object * prototype)180 Global_as::createClass(Global_as::ASFunction ctor, as_object* prototype)
181 {
182 as_object* cl = new builtin_function(*this, ctor);
183
184 if (prototype) {
185 prototype->init_member(NSV::PROP_CONSTRUCTOR, cl);
186 cl->init_member(NSV::PROP_PROTOTYPE, prototype);
187 }
188 as_function* fun =
189 gnash::getOwnProperty(*this, NSV::CLASS_FUNCTION).to_function();
190
191 if (fun) {
192 const int flags = as_object::DefaultFlags | PropFlags::onlySWF6Up;
193 cl->init_member(NSV::PROP_uuPROTOuu, getMember(*fun,
194 NSV::PROP_PROTOTYPE), flags);
195 cl->init_member(NSV::PROP_CONSTRUCTOR, fun);
196 }
197 return cl;
198 }
199
200
201 /// Construct an Array.
202 //
203 /// This uses the _global Array class to initialize the "constructor" and
204 /// "__proto__" properties. If Array.prototype is undefined, those properties
205 /// are not added.
206 as_object*
createArray()207 Global_as::createArray()
208 {
209 as_object* array = new as_object(*this);
210
211 as_value ctor = getMember(*this, NSV::CLASS_ARRAY);
212 as_object* obj = toObject(ctor, gnash::getVM(*this));
213 if (obj) {
214 as_value proto;
215 if (obj->get_member(NSV::PROP_PROTOTYPE, &proto)) {
216 array->init_member(NSV::PROP_CONSTRUCTOR, ctor);
217 array->set_prototype(getMember(*obj, NSV::PROP_PROTOTYPE));
218 }
219 }
220
221 array->init_member(NSV::PROP_LENGTH, 0.0);
222 array->setArray();
223 return array;
224 }
225
226 void
markReachableResources() const227 Global_as::markReachableResources() const
228 {
229 _classes.markReachableResources();
230 _objectProto->setReachable();
231 as_object::markReachableResources();
232 }
233
234 void
registerClasses()235 Global_as::registerClasses()
236 {
237 registerNatives(*this);
238
239 function_class_init(*this, NSV::CLASS_FUNCTION);
240 initObjectClass(_objectProto, *this, NSV::CLASS_OBJECT);
241
242 string_class_init(*this, NSV::CLASS_STRING);
243 array_class_init(*this, NSV::CLASS_ARRAY);
244
245 // No idea why, but it seems there's a NULL _global.o
246 // defined at player startup...
247 // Probably due to the AS-based initialization
248 // Not enumerable but overridable and deletable.
249 //
250 as_value nullVal; nullVal.set_null();
251 init_member("o", nullVal, PropFlags::dontEnum);
252
253
254 VM& vm = getVM();
255
256 // _global functions.
257 // These functions are only available in SWF6+, but this is just
258 // because SWF5 or lower did not have a "_global"
259 // reference at all.
260 init_member("ASnative", createFunction(global_asnative));
261 init_member("ASconstructor", createFunction(global_asconstructor));
262 init_member("ASSetPropFlags", vm.getNative(1, 0));
263 init_member("ASSetNative", vm.getNative(4, 0));
264 init_member("ASSetNativeAccessor", vm.getNative(4, 1));
265 init_member("AsSetupError", createFunction(global_assetuperror));
266 init_member("updateAfterEvent", vm.getNative(9, 0));
267 init_member("trace", vm.getNative(100, 4));
268
269 init_member("setInterval", vm.getNative(250, 0));
270 init_member("clearInterval", vm.getNative(250, 1));
271 init_member("setTimeout", vm.getNative(250, 2));
272
273 // This is an odd function with no properties. There ought to be
274 // a better way of implementing this. See also TextFormat.getTextExtent.
275 as_function* edc = createFunction(global_enableDebugConsole);
276 edc->clearProperties();
277 init_member("enableDebugConsole", edc);
278 init_member("showRedrawRegions", vm.getNative(1021, 1));
279
280 init_member("clearTimeout", getMember(*this, getURI(vm, "clearInterval")));
281
282 _classes.declareAll(avm1Classes());
283
284 // SWF8 visibility:
285 const ObjectURI& flash = getURI(vm, "flash");
286 flash_package_init(*this, flash);
287
288 init_member("escape", vm.getNative(100, 0));
289 init_member("unescape", vm.getNative(100, 1));
290 init_member("parseInt", vm.getNative(100, 2));
291 init_member("parseFloat", vm.getNative(100, 3));
292 init_member("isNaN", vm.getNative(200, 18));
293 init_member("isFinite", vm.getNative(200, 19));
294
295 init_member("NaN", as_value(NaN));
296 init_member("Infinity", as_value(
297 std::numeric_limits<double>::infinity()));
298
299 loadExtensions();
300 }
301
302 as_object*
createObject(const Global_as & gl)303 createObject(const Global_as& gl)
304 {
305 as_object* obj = new as_object(gl);
306 gl.makeObject(*obj);
307 return obj;
308 }
309
310 //-----------------------
311 // Extensions
312 //-----------------------
313 // Scan the plugin directories for all plugins, and load them now.
314 // FIXME: this should actually be done dynamically, and only
315 // if a plugin defines a class that a movie actually wants to
316 // use.
317 void
loadExtensions()318 Global_as::loadExtensions()
319 {
320 #if USE_EXTENSIONS
321 if (_et.get() && RcInitFile::getDefaultInstance().enableExtensions()) {
322 log_security(_("Extensions enabled, scanning plugin dir for load"));
323 _et->scanAndLoad(*this);
324 return;
325 }
326 #endif
327 log_security(_("Extensions disabled"));
328 }
329
330 void
makeObject(as_object & o) const331 Global_as::makeObject(as_object& o) const
332 {
333 o.set_prototype(_objectProto);
334 }
335
336 namespace {
337
338 const ClassHierarchy::NativeClasses&
avm1Classes()339 avm1Classes()
340 {
341 typedef ClassHierarchy::NativeClass N;
342
343 // Since we maintain separate lists for AVM1 and AVM2, these are all
344 // considered to be in the 'Global' namespace (AVM1 has no namespaces)
345 // An ObjectURI constructed without a namespace is in the global namespace.
346 static const ClassHierarchy::NativeClasses s = {
347 N(system_class_init, NSV::CLASS_SYSTEM, 1),
348 N(stage_class_init, NSV::CLASS_STAGE, 1),
349 N(movieclip_class_init, NSV::CLASS_MOVIE_CLIP, 3),
350 N(textfield_class_init, NSV::CLASS_TEXT_FIELD, 3),
351 N(math_class_init, NSV::CLASS_MATH, 4),
352 N(boolean_class_init, NSV::CLASS_BOOLEAN, 5),
353 N(button_class_init, NSV::CLASS_BUTTON, 5),
354 N(color_class_init, NSV::CLASS_COLOR, 5),
355 N(selection_class_init, NSV::CLASS_SELECTION, 5),
356 N(sound_class_init, NSV::CLASS_SOUND, 5),
357 N(xmlsocket_class_init, NSV::CLASS_XMLSOCKET, 5),
358 N(date_class_init, NSV::CLASS_DATE, 5),
359 N(xmlnode_class_init, NSV::CLASS_XMLNODE, 5),
360 N(xml_class_init, NSV::CLASS_XML, 5),
361 N(mouse_class_init, NSV::CLASS_MOUSE, 5),
362 N(number_class_init, NSV::CLASS_NUMBER, 5),
363 N(textformat_class_init, NSV::CLASS_TEXT_FORMAT, 5),
364 N(key_class_init, NSV::CLASS_KEY, 5),
365 N(AsBroadcaster::init, NSV::CLASS_AS_BROADCASTER, 5),
366 N(textsnapshot_class_init, NSV::CLASS_TEXT_SNAPSHOT, 5),
367 N(video_class_init, NSV::CLASS_VIDEO, 6),
368 N(camera_class_init, NSV::CLASS_CAMERA, 5),
369 N(microphone_class_init, NSV::CLASS_MICROPHONE, 5),
370 N(sharedobject_class_init, NSV::CLASS_SHARED_OBJECT, 5),
371 N(loadvars_class_init, NSV::CLASS_LOAD_VARS, 5),
372 N(localconnection_class_init, NSV::CLASS_LOCALCONNECTION, 6),
373 N(netconnection_class_init, NSV::CLASS_NET_CONNECTION, 6),
374 N(netstream_class_init, NSV::CLASS_NET_STREAM, 6),
375 N(contextmenu_class_init, NSV::CLASS_CONTEXTMENU, 5),
376 N(contextmenuitem_class_init, NSV::CLASS_CONTEXTMENUITEM, 5),
377 N(moviecliploader_class_init, NSV::CLASS_MOVIE_CLIP_LOADER, 5),
378 N(Error_class_init, NSV::CLASS_ERROR, 5),
379 N(accessibility_class_init, NSV::CLASS_ACCESSIBILITY, 5)
380 };
381
382 return s;
383 }
384
385 as_value
global_trace(const fn_call & fn)386 global_trace(const fn_call& fn)
387 {
388 ASSERT_FN_ARGS_IS_1
389
390 // Log our argument.
391 //
392 // @@ what if we get extra args?
393 //
394 // @@ Nothing needs special treatment,
395 // as_value::to_string() will take care of everything
396 log_trace("%s", fn.arg(0).to_string());
397
398 return as_value();
399 }
400
401
402 as_value
global_isNaN(const fn_call & fn)403 global_isNaN(const fn_call& fn)
404 {
405 ASSERT_FN_ARGS_IS_1
406
407 return as_value(static_cast<bool>(isNaN(
408 toNumber(fn.arg(0), getVM(fn)))));
409 }
410
411
412 as_value
global_isfinite(const fn_call & fn)413 global_isfinite(const fn_call& fn)
414 {
415 ASSERT_FN_ARGS_IS_1
416
417 return as_value(static_cast<bool>(isFinite(
418 toNumber(fn.arg(0), getVM(fn)))));
419 }
420
421 /// \brief Encode a string to URL-encoded format
422 /// converting all dodgy DisplayObjects to %AB hex sequences
423 //
424 /// Characters that need escaping are:
425 /// - ASCII control DisplayObjects: 0-31 and 127
426 /// - Non-ASCII chars: 128-255
427 /// - URL syntax DisplayObjects: $ & + , / : ; = ? @
428 /// - Unsafe DisplayObjects: SPACE " < > # % { } | \ ^ ~ [ ] `
429 /// Encoding is a % followed by two hexadecimal DisplayObjects, case insensitive.
430 /// See RFC1738 http://www.rfc-editor.org/rfc/rfc1738.txt,
431 /// Section 2.2 "URL Character Encoding Issues"
432 as_value
global_escape(const fn_call & fn)433 global_escape(const fn_call& fn)
434 {
435 ASSERT_FN_ARGS_IS_1
436
437 std::string input = fn.arg(0).to_string();
438 URL::encode(input);
439 return as_value(input);
440 }
441
442 /// \brief Decode a string from URL-encoded format
443 /// converting all hexadecimal sequences to ASCII DisplayObjects.
444 //
445 /// A sequence to convert is % followed by two case-independent hexadecimal
446 /// digits, which is replaced by the equivalent ASCII DisplayObject.
447 /// See RFC1738 http://www.rfc-editor.org/rfc/rfc1738.txt,
448 /// Section 2.2 "URL Character Encoding Issues"
449 as_value
global_unescape(const fn_call & fn)450 global_unescape(const fn_call& fn)
451 {
452 ASSERT_FN_ARGS_IS_1
453
454 std::string input = fn.arg(0).to_string();
455 URL::decode(input);
456 return as_value(input);
457 }
458
459 // parseFloat (string)
460 as_value
global_parsefloat(const fn_call & fn)461 global_parsefloat(const fn_call& fn)
462 {
463 ASSERT_FN_ARGS_IS_1
464
465 std::istringstream s(fn.arg(0).to_string());
466 double result;
467
468 if (!(s >> result)) {
469 return as_value(NaN);
470 }
471
472 return as_value(result);
473 }
474
475 // parseInt(string[, base])
476 // The second argument, if supplied, is the base.
477 // If none is supplied, we have to work out the
478 // base from the string. Decimal, octal and hexadecimal are
479 // possible, according to the following rules:
480 // 1. If the string starts with 0x or 0X, the number is hex.
481 // 2. The 0x or 0X may be *followed* by '-' or '+' to indicate sign. A number
482 // with no sign is positive.
483 // 3. If the string starts with 0, -0 or +0 and contains only the DisplayObjects
484 // 0-7.
485 // 4. If the string starts with *any* other sequence of DisplayObjects, including
486 // whitespace, it is decimal.
487 as_value
global_parseint(const fn_call & fn)488 global_parseint(const fn_call& fn)
489 {
490 if (!fn.nargs) {
491 IF_VERBOSE_ASCODING_ERRORS(
492 log_aserror(_("%s needs at least one argument"), __FUNCTION__);
493 )
494 return as_value();
495 }
496
497 IF_VERBOSE_ASCODING_ERRORS(
498 if (fn.nargs > 2) {
499 log_aserror(_("%s has more than two arguments"), __FUNCTION__);
500 }
501 )
502
503 const std::string& expr = fn.arg(0).to_string();
504
505 // A second argument specifies the base.
506 // Parsing still starts after any positive/negative
507 // sign or hex identifier (parseInt("0x123", 8) gives
508 // 83, not 0; parseInt(" 0x123", 8) is 0), which is
509 // why we do this here.
510 size_t base;
511 if (fn.nargs > 1) {
512 base = toInt(fn.arg(1), getVM(fn));
513
514 // Bases from 2 to 36 are valid, otherwise return NaN
515 if (base < 2 || base > 36) return as_value(NaN);
516 }
517 else {
518 /// No radix specified, so try parsing as octal or hexadecimal
519 try {
520 double d;
521 if (parseNonDecimalInt(expr, d, false)) return d;
522 }
523 catch (const boost::bad_lexical_cast&) {
524 return as_value(NaN);
525 }
526
527 /// The number is not hex or octal, so we'll assume it's base-10.
528 base = 10;
529
530 }
531
532 std::string::const_iterator it = expr.begin();
533
534 // Check for expectional case "-0x" or "+0x", which
535 // return NaN
536 if ((expr.length() > 2) && (*it == '-' || *it == '+') &&
537 *(it + 1) == '0' && std::toupper(*(it + 2)) == 'X') {
538 return as_value(NaN);
539 }
540
541 // Try hexadecimal first
542 if (expr.substr(0, 2) == "0x" || expr.substr(0, 2) == "0X") it += 2;
543 else {
544 // Skip leading whitespace
545 while(*it == ' ' || *it == '\n' || *it == '\t' || *it == '\r') {
546 ++it;
547 }
548 if (it == expr.end()) return as_value(NaN);
549 }
550
551 bool negative = false;
552 if (*it == '-' || *it == '+') {
553 if (*it == '-') negative = true;
554
555 ++it;
556 if (it == expr.end()) return as_value(NaN);
557 }
558
559 // Now we have the base, parse the digits. The iterator should
560 // be pointing at the first digit.
561
562 const std::string digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
563
564 // Check to see if the first digit is valid, otherwise
565 // return NaN.
566 std::string::size_type digit = digits.find(toupper(*it));
567
568 if (digit >= base || digit == std::string::npos) return as_value(NaN);
569
570 // The first digit was valid, so continue from the present position
571 // until we reach the end of the string or an invalid DisplayObject,
572 // adding valid DisplayObjects to our result.
573 // Which DisplayObjects are invalid depends on the base.
574 double result = digit;
575 ++it;
576
577 while (it != expr.end() && (digit = digits.find(toupper(*it))) < base
578 && digit != std::string::npos) {
579 result = result * base + digit;
580 ++it;
581 }
582
583 // Now return the parsed string as an integer.
584 return negative ? as_value(-result) : as_value(result);
585 }
586
587 // ASSetPropFlags function
588 as_value
global_assetpropflags(const fn_call & fn)589 global_assetpropflags(const fn_call& fn)
590 {
591 if (fn.nargs < 3) {
592 IF_VERBOSE_ASCODING_ERRORS(
593 log_aserror(_("%s needs at least three arguments"), __FUNCTION__);
594 )
595 return as_value();
596 }
597
598 IF_VERBOSE_ASCODING_ERRORS(
599 if (fn.nargs > 4) {
600 log_aserror(_("%s has more than four arguments"), "AsSetPropFlags");
601 }
602 );
603
604 // object
605 as_object* obj = toObject(fn.arg(0), getVM(fn));
606 if (!obj) {
607 IF_VERBOSE_ASCODING_ERRORS(
608 log_aserror(_("Invalid call to ASSetPropFlags: "
609 "first argument is not an object: %s"),
610 fn.arg(0));
611 );
612 return as_value();
613 }
614
615 // list of child names
616
617 const as_value& props = fn.arg(1);
618
619 const int flagsMask = PropFlags::dontEnum |
620 PropFlags::dontDelete |
621 PropFlags::readOnly |
622 PropFlags::onlySWF6Up |
623 PropFlags::ignoreSWF6 |
624 PropFlags::onlySWF7Up |
625 PropFlags::onlySWF8Up |
626 PropFlags::onlySWF9Up;
627
628 // a number which represents three bitwise flags which
629 // are used to determine whether the list of child names should be hidden,
630 // un-hidden, protected from over-write, un-protected from over-write,
631 // protected from deletion and un-protected from deletion
632 const int setTrue = int(toNumber(fn.arg(2), getVM(fn))) & flagsMask;
633
634 // Is another integer bitmask that works like set_true,
635 // except it sets the attributes to false. The
636 // set_false bitmask is applied before set_true is applied
637
638 // ASSetPropFlags was exposed in Flash 5, however the fourth argument
639 // 'set_false' was not required as it always defaulted to the value '~0'.
640 const int setFalse = (fn.nargs < 4 ? 0 : toInt(fn.arg(3), getVM(fn))) &
641 flagsMask;
642
643 obj->setPropFlags(props, setFalse, setTrue);
644
645 return as_value();
646 }
647
648 // ASconstructor function
649 // See: http://osflash.org/flashcoders/undocumented/asnative?s=asnative
650 as_value
global_asconstructor(const fn_call & fn)651 global_asconstructor(const fn_call& fn)
652 {
653 if (fn.nargs < 2) {
654 IF_VERBOSE_ASCODING_ERRORS(
655 std::ostringstream ss; fn.dump_args(ss);
656 log_aserror(_("ASNative(%s): needs at least two arguments"),
657 ss.str());
658 )
659 return as_value();
660 }
661
662 const int sx = toInt(fn.arg(0), getVM(fn));
663 const int sy = toInt(fn.arg(1), getVM(fn));
664
665 if (sx < 0 || sy < 0) {
666 IF_VERBOSE_ASCODING_ERRORS(
667 std::ostringstream ss; fn.dump_args(ss);
668 log_aserror(_("ASconstructor(%s): args must be 0 or above"),
669 ss.str());
670 )
671 return as_value();
672 }
673
674 const unsigned int x = static_cast<unsigned int>(sx);
675 const unsigned int y = static_cast<unsigned int>(sy);
676
677 VM& vm = getVM(fn);
678 as_function* fun = vm.getNative(x, y);
679 if (!fun) {
680 log_debug("No ASnative(%d, %d) registered with the VM", x, y);
681 return as_value();
682 }
683
684 Global_as& gl = getGlobal(fn);
685 fun->init_member(NSV::PROP_PROTOTYPE, createObject(gl));
686
687 return as_value(fun);
688
689 }
690
691 // ASNative function
692 // See: http://osflash.org/flashcoders/undocumented/asnative?s=asnative
693 as_value
global_asnative(const fn_call & fn)694 global_asnative(const fn_call& fn)
695 {
696 if (fn.nargs < 2) {
697 IF_VERBOSE_ASCODING_ERRORS(
698 std::ostringstream ss; fn.dump_args(ss);
699 log_aserror(_("ASNative(%s): needs at least two arguments"),
700 ss.str());
701 )
702 return as_value();
703 }
704
705 const int sx = toInt(fn.arg(0), getVM(fn));
706 const int sy = toInt(fn.arg(1), getVM(fn));
707
708 if (sx < 0 || sy < 0) {
709 IF_VERBOSE_ASCODING_ERRORS(
710 std::ostringstream ss; fn.dump_args(ss);
711 log_aserror(_("ASnative(%s): args must be 0 or above"),
712 ss.str());
713 )
714 return as_value();
715 }
716
717 const unsigned int x = static_cast<unsigned int>(sx);
718 const unsigned int y = static_cast<unsigned int>(sy);
719
720 VM& vm = getVM(fn);
721 as_function* fun = vm.getNative(x, y);
722 if (!fun) {
723 log_debug("No ASnative(%d, %d) registered with the VM", x, y);
724 return as_value();
725 }
726 return as_value(fun);
727 }
728
729 // Obsolete ASnew function (exists only as ASnative(2, 0))
730 as_value
global_asnew(const fn_call &)731 global_asnew(const fn_call& /*fn*/)
732 {
733 LOG_ONCE(log_unimpl(_("ASNative (2, 0) - old ASnew")));
734 return as_value();
735 }
736
737 /// ASSetNative(targetObject, major, properties, minor)
738 //
739 /// Sets a series of properties on targetObject using the native table.
740 /// The third argument is generally documented to be an array, but in fact
741 /// it is always converted to a string and parsed.
742 as_value
global_assetnative(const fn_call & fn)743 global_assetnative(const fn_call& fn)
744 {
745 if (fn.nargs < 3) {
746 return as_value();
747 }
748
749 as_object* targetObject = toObject(fn.arg(0), getVM(fn));
750 if (!targetObject) {
751 return as_value();
752 }
753
754 const int major = toInt(fn.arg(1), getVM(fn));
755 if (major < 0) return as_value();
756
757 const std::string& props = fn.arg(2).to_string();
758 const int minor =
759 fn.nargs > 3 ? std::max<std::int32_t>(toInt(fn.arg(3), getVM(fn)), 0) : 0;
760
761 std::string::const_iterator pos = props.begin();
762
763 VM& vm = getVM(fn);
764
765 size_t i = 0;
766
767 // pos is always the position after the last located property.
768 while (pos != props.end()) {
769
770 // If there are no further commas, find the end of the string.
771 std::string::const_iterator comma = std::find(pos, props.end(), ',');
772
773 const char num = *pos;
774
775 int flag;
776
777 switch (num) {
778 case '6':
779 flag = PropFlags::onlySWF6Up;
780 ++pos;
781 break;
782 case '7':
783 flag = PropFlags::onlySWF7Up;
784 ++pos;
785 break;
786 case '8':
787 flag = PropFlags::onlySWF8Up;
788 ++pos;
789 break;
790 case '9':
791 flag = PropFlags::onlySWF9Up;
792 ++pos;
793 break;
794 default:
795 flag = 0;
796
797 }
798 const std::string& property = std::string(pos, comma);
799 if (!property.empty()) {
800 targetObject->init_member(property,
801 vm.getNative(major, minor + i), flag);
802 }
803 if (comma == props.end()) break;
804 pos = comma + 1;
805 ++i;
806 }
807 return as_value();
808 }
809
810 // This is like ASSetNative, but attaches getter/setters.
811 as_value
global_assetnativeaccessor(const fn_call & fn)812 global_assetnativeaccessor(const fn_call& fn)
813 {
814 if (fn.nargs < 3) {
815 return as_value();
816 }
817
818 as_object* targetObject = toObject(fn.arg(0), getVM(fn));
819 if (!targetObject) {
820 return as_value();
821 }
822
823 const int major = toInt(fn.arg(1), getVM(fn));
824 if (major < 0) return as_value();
825
826 const std::string& props = fn.arg(2).to_string();
827 const int minor =
828 fn.nargs > 3 ? std::max<std::int32_t>(toInt(fn.arg(3), getVM(fn)), 0) : 0;
829
830 std::string::const_iterator pos = props.begin();
831
832 VM& vm = getVM(fn);
833
834 size_t i = 0;
835
836 // pos is always the position after the last located property.
837 while (pos != props.end()) {
838
839 // If there are no further commas, find the end of the string.
840 std::string::const_iterator comma = std::find(pos, props.end(), ',');
841
842 const char num = *pos;
843
844 int flag;
845
846 switch (num) {
847 case '6':
848 flag = PropFlags::onlySWF6Up;
849 ++pos;
850 break;
851 case '7':
852 flag = PropFlags::onlySWF7Up;
853 ++pos;
854 break;
855 case '8':
856 flag = PropFlags::onlySWF8Up;
857 ++pos;
858 break;
859 case '9':
860 flag = PropFlags::onlySWF9Up;
861 ++pos;
862 break;
863 default:
864 flag = 0;
865
866 }
867 const std::string& property = std::string(pos, comma);
868 if (!property.empty()) {
869 NativeFunction* getset = vm.getNative(major, minor + i);
870 targetObject->init_property(property, *getset, *getset, flag);
871 }
872 if (comma == props.end()) break;
873 pos = comma + 1;
874 ++i;
875 }
876 return as_value();
877 }
878
879 // updateAfterEvent function
880 as_value
global_updateAfterEvent(const fn_call &)881 global_updateAfterEvent(const fn_call& /*fn*/)
882 {
883 LOG_ONCE(log_unimpl(_("updateAfterEvent()")));
884 return as_value();
885 }
886
887 as_value
local_errorConstructor(const fn_call & fn)888 local_errorConstructor(const fn_call& fn)
889 {
890 as_object* obj = ensure<ValidThis>(fn);
891 const as_value& arg = fn.nargs ? fn.arg(0) : as_value();
892 VM& vm = getVM(fn);
893 obj->set_member(getURI(vm, "message"), arg);
894 return as_value();
895 }
896
897
898 /// Sets a range of Error subclasses.
899 as_value
global_assetuperror(const fn_call & fn)900 global_assetuperror(const fn_call& fn)
901 {
902 if (!fn.nargs) return as_value();
903
904 // This should actually call String.split, but since our Array is
905 // wrong we may as well do it like this for now.
906 const std::string& errors = fn.arg(0).to_string();
907
908 std::string::const_iterator pos = errors.begin();
909
910 Global_as& gl = getGlobal(fn);
911
912 // pos is always the position after the last located error.
913 for (;;) {
914
915 // If there are no further commas, find the end of the string.
916 std::string::const_iterator comma = std::find(pos, errors.end(), ',');
917
918 const std::string& err = std::string(pos, comma);
919
920 VM& vm = getVM(fn);
921
922 as_function* ctor = getMember(gl, NSV::CLASS_ERROR).to_function();
923 if (ctor) {
924 fn_call::Args args;
925 as_object* proto = constructInstance(*ctor, fn.env(), args);
926
927 // Not really sure what the point of this is.
928 gl.createClass(local_errorConstructor, proto);
929 proto->set_member(getURI(vm, "name"), err);
930 proto->set_member(getURI(vm, "message"), err);
931 }
932
933 if (comma == errors.end()) break;
934 pos = comma + 1;
935 }
936 return as_value();
937 }
938
939 as_value
global_setInterval(const fn_call & fn)940 global_setInterval(const fn_call& fn)
941 {
942 if (fn.nargs < 2) {
943 IF_VERBOSE_ASCODING_ERRORS(
944 std::stringstream ss; fn.dump_args(ss);
945 log_aserror(_("Invalid call to setInterval(%s) "
946 "- need at least 2 arguments"),
947 ss.str());
948 );
949 return as_value();
950 }
951
952 unsigned timer_arg = 1;
953
954 as_object* obj = toObject(fn.arg(0), getVM(fn));
955 if (!obj) {
956
957 IF_VERBOSE_ASCODING_ERRORS(
958 std::stringstream ss; fn.dump_args(ss);
959 log_aserror(_("Invalid call to setInterval(%s) "
960 "- first argument is not an object or function"),
961 ss.str());
962 );
963 return as_value();
964 }
965
966 ObjectURI methodName;
967
968 // Get interval function
969 as_function* as_func = obj->to_function();
970 if (!as_func) {
971 methodName = getURI(getVM(fn), fn.arg(1).to_string());
972 timer_arg = 2;
973 }
974
975
976 if (fn.nargs < timer_arg + 1) {
977 IF_VERBOSE_ASCODING_ERRORS(
978 std::stringstream ss; fn.dump_args(ss);
979 log_aserror(_("Invalid call to setInterval(%s) "
980 "- missing timeout argument"),
981 ss.str());
982 );
983 return as_value();
984 }
985
986 // Get interval time
987 unsigned long ms =
988 static_cast<unsigned long>(toNumber(fn.arg(timer_arg), getVM(fn)));
989 // TODO: check validity of interval time number ?
990
991 // Parse arguments
992 fn_call::Args args;
993 for (unsigned i = timer_arg + 1; i < fn.nargs; ++i) {
994 args += fn.arg(i);
995 }
996
997 std::unique_ptr<Timer> timer;
998 if (as_func) {
999 timer.reset(new Timer(*as_func, ms, fn.this_ptr, args));
1000 }
1001 else {
1002 timer.reset(new Timer(obj, methodName, ms, args));
1003 }
1004
1005 movie_root& root = getRoot(fn);
1006
1007 // TODO: check what should happen to overflows.
1008 const int id = root.addIntervalTimer(std::move(timer));
1009 return as_value(id);
1010 }
1011
1012 as_value
global_setTimeout(const fn_call & fn)1013 global_setTimeout(const fn_call& fn)
1014 {
1015 if (fn.nargs < 2) {
1016 IF_VERBOSE_ASCODING_ERRORS(
1017 std::stringstream ss; fn.dump_args(ss);
1018 log_aserror(_("Invalid call to setTimeout(%s) "
1019 "- need at least 2 arguments"),
1020 ss.str());
1021 );
1022 return as_value();
1023 }
1024
1025 unsigned timer_arg = 1;
1026
1027 as_object* obj = toObject(fn.arg(0), getVM(fn));
1028 if (!obj) {
1029 IF_VERBOSE_ASCODING_ERRORS(
1030 std::stringstream ss; fn.dump_args(ss);
1031 log_aserror(_("Invalid call to setInterval(%s) "
1032 "- first argument is not an object or function"),
1033 ss.str());
1034 );
1035 return as_value();
1036 }
1037
1038 ObjectURI methodName;
1039
1040 // Get interval function
1041 as_function* as_func = obj->to_function();
1042 if (!as_func) {
1043 methodName = getURI(getVM(fn), fn.arg(1).to_string());
1044 timer_arg = 2;
1045 }
1046
1047
1048 if (fn.nargs < timer_arg + 1) {
1049 IF_VERBOSE_ASCODING_ERRORS(
1050 std::stringstream ss; fn.dump_args(ss);
1051 log_aserror(_("Invalid call to setTimeout(%s): missing "
1052 "timeout argument"), ss.str());
1053 );
1054 return as_value();
1055 }
1056
1057 // Get interval time
1058 unsigned long ms =
1059 static_cast<unsigned long>(toNumber(fn.arg(timer_arg), getVM(fn)));
1060
1061 // Parse arguments
1062 fn_call::Args args;
1063 for (unsigned i = timer_arg + 1; i < fn.nargs; ++i) {
1064 args += fn.arg(i);
1065 }
1066
1067 std::unique_ptr<Timer> timer;
1068 if (as_func) {
1069 timer.reset(new Timer(*as_func, ms, fn.this_ptr, args, true));
1070 }
1071 else {
1072 timer.reset(new Timer(obj, methodName, ms, args, true));
1073 }
1074
1075 movie_root& root = getRoot(fn);
1076
1077 // TODO: check what should happen to overflows.
1078 const int id = root.addIntervalTimer(std::move(timer));
1079 return as_value(id);
1080 }
1081
1082 as_value
global_clearInterval(const fn_call & fn)1083 global_clearInterval(const fn_call& fn)
1084 {
1085 if (!fn.nargs) {
1086 IF_VERBOSE_ASCODING_ERRORS(
1087 log_aserror(_("clearInterval requires one argument, got none"));
1088 );
1089 return as_value();
1090 }
1091
1092 const std::uint32_t id = toInt(fn.arg(0), getVM(fn));
1093
1094 movie_root& root = getRoot(fn);
1095 return as_value(root.clearIntervalTimer(id));
1096 }
1097
1098 as_value
global_showRedrawRegions(const fn_call &)1099 global_showRedrawRegions(const fn_call& /*fn*/)
1100 {
1101 LOG_ONCE(log_unimpl(_("_global.showRedrawRegions")));
1102 return as_value();
1103 }
1104
1105 as_value
global_enableDebugConsole(const fn_call &)1106 global_enableDebugConsole(const fn_call& /*fn*/)
1107 {
1108 LOG_ONCE(log_unimpl(_("_global.enableDebugConsole")));
1109 return as_value();
1110 }
1111
1112 void
registerNatives(as_object & global)1113 registerNatives(as_object& global)
1114 {
1115 VM& vm = getVM(global);
1116
1117 // ASNew was dropped as an API function but exists
1118 // as ASnative.
1119 vm.registerNative(global_assetpropflags, 1, 0);
1120 vm.registerNative(global_asnew, 2, 0);
1121 vm.registerNative(global_assetnative, 4, 0);
1122 vm.registerNative(global_assetnativeaccessor, 4, 1);
1123 vm.registerNative(global_updateAfterEvent, 9, 0);
1124 vm.registerNative(global_escape, 100, 0);
1125 vm.registerNative(global_unescape, 100, 1);
1126 vm.registerNative(global_parseint, 100, 2);
1127 vm.registerNative(global_parsefloat, 100, 3);
1128 vm.registerNative(global_trace, 100, 4);
1129 vm.registerNative(global_isNaN, 200, 18);
1130 vm.registerNative(global_isfinite, 200, 19);
1131 vm.registerNative(global_setInterval, 250, 0);
1132 vm.registerNative(global_clearInterval, 250, 1);
1133 vm.registerNative(global_setTimeout, 250, 2);
1134
1135 vm.registerNative(global_showRedrawRegions, 1021, 1);
1136
1137 registerObjectNative(global);
1138 registerFunctionNative(global);
1139 registerStringNative(global);
1140 registerArrayNative(global);
1141 registerNumberNative(global);
1142 registerBooleanNative(global);
1143 registerMovieClipNative(global);
1144 registerSelectionNative(global);
1145 registerColorNative(global);
1146 registerMathNative(global);
1147 registerSystemNative(global);
1148 registerAccessibilityNative(global);
1149 registerStageNative(global);
1150 registerTextFieldNative(global);
1151 registerButtonNative(global);
1152 registerVideoNative(global);
1153 registerMovieClipLoaderNative(global);
1154 registerXMLSocketNative(global);
1155 registerSharedObjectNative(global);
1156 registerKeyNative(global);
1157 registerNetStreamNative(global);
1158 registerCameraNative(global);
1159 registerMicrophoneNative(global);
1160 registerTextSnapshotNative(global);
1161 registerSoundNative(global);
1162 registerLocalConnectionNative(global);
1163 registerBitmapFilterNative(global);
1164 registerColorTransformNative(global);
1165 registerExternalInterfaceNative(global);
1166 registerBitmapDataNative(global);
1167
1168 AsBroadcaster::registerNative(global);
1169 registerTextFormatNative(global);
1170 registerDateNative(global);
1171 Mouse_as::registerNative(global);
1172
1173 // LoadableObject has natives shared between LoadVars and XML, so
1174 // should be registered first.
1175 registerLoadableNative(global);
1176 registerXMLNative(global);
1177 registerXMLNodeNative(global);
1178 }
1179
1180 } // anonymous namespace
1181 } // namespace gnash
1182
1183