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