Flag an error if the FB gui is configured without the rawfb device. For #108015.
[gnash.git] / libcore / as_object.cpp
blobf3a06ffc43c988b8035bcdbb28cc2372d0317a98
1 // as_object.cpp: ActionScript Object class and its properties, 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
20 #include "as_object.h"
22 #include <set>
23 #include <string>
24 #include <boost/algorithm/string/case_conv.hpp>
25 #include <utility> // for std::pair
27 #include "RunResources.h"
28 #include "log.h"
29 #include "as_function.h"
30 #include "as_environment.h"
31 #include "movie_root.h"
32 #include "event_id.h"
33 #include "Property.h"
34 #include "VM.h"
35 #include "GnashException.h"
36 #include "fn_call.h"
37 #include "Array_as.h"
38 #include "as_function.h"
39 #include "Global_as.h"
40 #include "GnashAlgorithm.h"
41 #include "DisplayObject.h"
42 #include "namedStrings.h"
44 namespace gnash {
45 template<typename T>
46 class
47 as_object::PrototypeRecursor
49 public:
50 PrototypeRecursor(as_object* top, const ObjectURI& uri, T cmp = T())
52 _object(top),
53 _uri(uri),
54 _iterations(0),
55 _condition(cmp)
57 _visited.insert(top);
60 /// Iterate to the next object in the inheritance chain.
62 /// This function throws an ActionLimitException when the maximum
63 /// number of recursions is reached.
65 /// @return false if there is no next object. In this case calling
66 /// the other functions will abort.
67 bool operator()()
69 ++_iterations;
71 // See swfdec/prototype-recursion-get-?.swf
72 if (_iterations > 256) {
73 throw ActionLimitException("Lookup depth exceeded.");
76 _object = _object->get_prototype();
78 // TODO: there is recursion prevention anyway; is this extra
79 // check for circularity really necessary?
80 if (!_visited.insert(_object).second) return 0;
81 return _object && !_object->displayObject();
84 /// Return the wanted property if it exists and satisfies the predicate.
86 /// This will abort if there is no current object.
87 Property* getProperty(as_object** owner = 0) const {
89 assert(_object);
90 Property* prop = _object->_members.getProperty(_uri);
92 if (prop && _condition(*prop)) {
93 if (owner) *owner = _object;
94 return prop;
96 return 0;
99 private:
100 as_object* _object;
101 const ObjectURI& _uri;
102 std::set<const as_object*> _visited;
103 size_t _iterations;
104 T _condition;
107 // Anonymous namespace used for module-static defs
108 namespace {
110 as_function*
111 getConstructor(as_object& o)
113 as_value ctorVal;
114 if (!o.get_member(NSV::PROP_uuCONSTRUCTORuu, &ctorVal)) {
115 return 0;
117 return ctorVal.to_function();
120 /// 'super' is a special kind of object
122 /// See http://wiki.gnashdev.org/wiki/index.php/ActionScriptSuper
124 /// We make it derive from as_function instead of as_object
125 /// to avoid touching too many files (ie: an as_object is not considered
126 /// something that can be called by current Gnash code). We may want
127 /// to change this in the future to implement what ECMA-262 refers to
128 /// as the [[Call]] property of objects.
130 class as_super : public as_object
132 public:
134 as_super(Global_as& gl, as_object* super)
136 as_object(gl),
137 _super(super)
139 set_prototype(prototype());
142 virtual bool isSuper() const { return true; }
144 virtual as_object* get_super(const ObjectURI& fname);
146 // Fetching members from 'super' yelds a lookup on the associated prototype
147 virtual bool get_member(const ObjectURI& uri, as_value* val)
149 as_object* proto = prototype();
150 if (proto) return proto->get_member(uri, val);
151 log_debug("Super has no associated prototype");
152 return false;
155 /// Dispatch.
156 virtual as_value call(const fn_call& fn)
159 // TODO: this is a hack to make sure objects are constructed, not
160 // converted (fn.isInstantiation() must be true).
161 fn_call::Args::container_type argsIn(fn.getArgs());
162 fn_call::Args args;
163 args.swap(argsIn);
165 fn_call fn2(fn.this_ptr, fn.env(), args, fn.super, true);
166 assert(fn2.isInstantiation());
167 as_function* ctor = constructor();
168 if (ctor) return ctor->call(fn2);
169 log_debug("Super has no associated constructor");
170 return as_value();
173 protected:
175 virtual void markReachableResources() const
177 if (_super) _super->setReachable();
178 as_object::markReachableResources();
181 private:
183 as_object* prototype() {
184 return _super ? _super->get_prototype() : 0;
187 as_function* constructor() {
188 return _super ? getConstructor(*_super) : 0;
191 as_object* _super;
194 as_object*
195 as_super::get_super(const ObjectURI& fname)
197 // Super references the super class of our class prototype.
198 // Our class prototype is __proto__.
199 // Our class superclass prototype is __proto__.__proto__
201 // Our class prototype is __proto__.
202 as_object* proto = get_prototype();
203 if (!proto) return new as_super(getGlobal(*this), 0);
205 if (fname.empty() || getSWFVersion(*this) <= 6) {
206 return new as_super(getGlobal(*this), proto);
209 as_object* owner = 0;
210 proto->findProperty(fname, &owner);
211 if (!owner) return 0;
213 if (owner == proto) return new as_super(getGlobal(*this), proto);
215 as_object* tmp = proto;
216 while (tmp && tmp->get_prototype() != owner) {
217 tmp = tmp->get_prototype();
219 // ok, now 'tmp' should be the object whose __proto__ member
220 // contains the actual named method.
222 // in the C:B:A:F case this would be B when calling
223 // super.myName() from C.prototype.myName()
225 // well, since we found the property, it must be somewhere!
226 assert(tmp);
228 if (tmp != proto) { return new as_super(getGlobal(*this), tmp); }
229 return new as_super(getGlobal(*this), owner);
233 /// A PropertyList visitor copying properties to an object
234 class PropsCopier : public PropertyVisitor
237 public:
239 /// \brief
240 /// Initialize a PropsCopier instance associating it
241 /// with a target object (an object whose members has to be set)
243 PropsCopier(as_object& tgt)
245 _tgt(tgt)
249 /// Set *inherited* properties of the given target object
250 bool accept(const ObjectURI& uri, const as_value& val) {
251 if (getName(uri) == NSV::PROP_uuPROTOuu) return true;
252 _tgt.set_member(uri, val);
253 return true;
255 private:
256 as_object& _tgt;
259 class PropertyEnumerator : public PropertyVisitor
261 public:
262 PropertyEnumerator(SortedPropertyList& to)
264 _to(to)
267 bool accept(const ObjectURI& uri, const as_value& val) {
268 _to.push_back(std::make_pair(uri, val));
269 return true;
271 private:
272 SortedPropertyList& _to;
275 } // anonymous namespace
278 const int as_object::DefaultFlags;
280 as_object::as_object(const Global_as& gl)
282 GcResource(getRoot(gl).gc()),
283 _displayObject(0),
284 _array(false),
285 _relay(0),
286 _vm(getVM(gl)),
287 _members(*this)
291 as_object::as_object(VM& vm)
293 GcResource(vm.getRoot().gc()),
294 _displayObject(0),
295 _array(false),
296 _relay(0),
297 _vm(vm),
298 _members(*this)
302 as_value
303 as_object::call(const fn_call& /*fn*/)
305 throw ActionTypeError();
308 std::string
309 as_object::stringValue() const
311 return "[object Object]";
314 std::pair<bool,bool>
315 as_object::delProperty(const ObjectURI& uri)
317 return _members.delProperty(uri);
321 void
322 as_object::add_property(const std::string& name, as_function& getter,
323 as_function* setter)
325 const ObjectURI& uri = getURI(vm(), name);
327 Property* prop = _members.getProperty(uri);
329 if (prop) {
330 const as_value& cacheVal = prop->getCache();
331 // Used to return the return value of addGetterSetter, but this
332 // is always true.
333 _members.addGetterSetter(uri, getter, setter, cacheVal);
334 return;
335 // NOTE: watch triggers not called when adding a new
336 // getter-setter property
338 else {
340 _members.addGetterSetter(uri, getter, setter, as_value());
342 // Nothing more to do if there are no triggers.
343 if (!_trigs.get()) return;
345 // check if we have a trigger, if so, invoke it
346 // and set val to its return
347 TriggerContainer::iterator trigIter = _trigs->find(uri);
349 if (trigIter != _trigs->end()) {
351 Trigger& trig = trigIter->second;
353 log_debug("add_property: property %s is being watched", name);
354 as_value v = trig.call(as_value(), as_value(), *this);
356 // The trigger call could have deleted the property,
357 // so we check for its existence again, and do NOT put
358 // it back in if it was deleted
359 prop = _members.getProperty(uri);
360 if (!prop) {
361 log_debug("Property %s deleted by trigger on create (getter-setter)", name);
362 return;
364 prop->setCache(v);
366 return;
371 /// Order of property lookup:
373 /// 1. Visible own properties.
374 /// 2. If DisplayObject, magic properties
375 /// 3. Visible own properties of all __proto__ objects (a DisplayObject
376 /// ends the chain).
377 /// 4. __resolve property of this object and all __proto__ objects (a Display
378 /// Object ends the chain). This should ignore visibility but doesn't.
379 bool
380 as_object::get_member(const ObjectURI& uri, as_value* val)
382 assert(val);
384 const int version = getSWFVersion(*this);
386 PrototypeRecursor<IsVisible> pr(this, uri, IsVisible(version));
388 Property* prop = pr.getProperty();
389 if (!prop) {
390 if (displayObject()) {
391 DisplayObject* d = displayObject();
392 if (getDisplayObjectProperty(*d, uri, *val)) return true;
394 while (pr()) {
395 if ((prop = pr.getProperty())) break;
399 // If the property isn't found or doesn't apply to any objects in the
400 // inheritance chain, try the __resolve property.
401 if (!prop) {
403 PrototypeRecursor<Exists> pr(this, NSV::PROP_uuRESOLVE);
405 as_value resolve;
407 for (;;) {
408 Property* res = pr.getProperty();
409 if (res) {
410 resolve = res->isGetterSetter() ? res->getCache() :
411 res->getValue(*this);
412 if (version < 7) break;
413 if (resolve.is_object()) break;
415 // Finished searching.
416 if (!pr()) return false;
419 // If __resolve exists, call it with the name of the undefined
420 // property.
421 string_table& st = getStringTable(*this);
422 const std::string& undefinedName = st.value(getName(uri));
424 fn_call::Args args;
425 args += undefinedName;
427 // Invoke the __resolve property.
428 *val = invoke(resolve, as_environment(getVM(*this)), this, args);
430 return true;
433 try {
434 *val = prop->getValue(*this);
435 return true;
437 catch (const ActionTypeError& exc) {
438 IF_VERBOSE_ASCODING_ERRORS(
439 log_aserror(_("Caught exception: %s"), exc.what());
441 return false;
446 as_object*
447 as_object::get_super(const ObjectURI& fname)
449 // Super references the super class of our class prototype.
450 // Our class prototype is __proto__.
451 // Our class superclass prototype is __proto__.__proto__
453 // Our class prototype is __proto__.
454 as_object* proto = get_prototype();
456 if ( ! fname.empty() && getSWFVersion(*this) > 6) {
457 as_object* owner = 0;
458 findProperty(fname, &owner);
459 // should be 0 if findProperty returned 0
460 if (owner != this) proto = owner;
463 as_object* super = new as_super(getGlobal(*this), proto);
465 return super;
468 as_object*
469 as_object::get_super()
471 // Our class prototype is __proto__.
472 as_object* proto = get_prototype();
473 as_object* super = new as_super(getGlobal(*this), proto);
475 return super;
478 Property*
479 as_object::findProperty(const ObjectURI& uri, as_object** owner)
482 const int version = getSWFVersion(*this);
484 PrototypeRecursor<IsVisible> pr(this, uri, IsVisible(version));
486 do {
487 Property* prop = pr.getProperty(owner);
488 if (prop) return prop;
489 } while (pr());
491 // No Property found
492 return 0;
495 Property*
496 as_object::findUpdatableProperty(const ObjectURI& uri)
499 PrototypeRecursor<Exists> pr(this, uri);
501 Property* prop = pr.getProperty();
503 // We won't scan the inheritance chain if we find a member,
504 // even if invisible.
505 if (prop) return prop;
507 const int swfVersion = getSWFVersion(*this);
509 while (pr()) {
510 if ((prop = pr.getProperty())) {
511 if (prop->isGetterSetter() && visible(*prop, swfVersion)) {
512 return prop;
516 return 0;
519 void
520 as_object::set_prototype(const as_value& proto)
522 // TODO: check what happens if __proto__ is set as a user-defined
523 // getter/setter
524 // TODO: check triggers !!
525 _members.setValue(NSV::PROP_uuPROTOuu, proto, as_object::DefaultFlags);
528 void
529 as_object::executeTriggers(Property* prop, const ObjectURI& uri,
530 const as_value& val)
533 // check if we have a trigger, if so, invoke it
534 // and set val to its return
535 TriggerContainer::iterator trigIter;
537 // If there are no triggers or the trigger is not found, just set
538 // the property.
539 if (!_trigs.get() || (trigIter = _trigs->find(uri)) == _trigs->end()) {
540 if (prop) {
541 prop->setValue(*this, val);
542 prop->clearVisible(getSWFVersion(*this));
544 return;
547 Trigger& trig = trigIter->second;
549 if (trig.dead()) {
550 _trigs->erase(trigIter);
551 return;
554 // WARNING: getValue might itself invoke a trigger
555 // (getter-setter)... ouch ?
556 // TODO: in this case, return the underlying value !
557 const as_value& curVal = prop ? prop->getCache() : as_value();
558 const as_value& newVal = trig.call(curVal, val, *this);
560 // This is a particularly clear and concise way of removing dead triggers.
561 EraseIf(*_trigs, boost::bind(boost::mem_fn(&Trigger::dead),
562 boost::bind(&TriggerContainer::value_type::second, _1)));
564 // The trigger call could have deleted the property,
565 // so we check for its existence again, and do NOT put
566 // it back in if it was deleted
567 prop = findUpdatableProperty(uri);
568 if (!prop) return;
570 prop->setValue(*this, newVal);
571 prop->clearVisible(getSWFVersion(*this));
575 /// Order of property lookup:
577 /// 0. MovieClip textfield variables. TODO: this is a hack and should be
578 /// eradicated.
579 /// 1. Own properties even if invisible or not getter-setters.
580 /// 2. If DisplayObject, magic properties
581 /// 3. Visible own getter-setter properties of all __proto__ objects
582 /// (a DisplayObject ends the chain).
583 bool
584 as_object::set_member(const ObjectURI& uri, const as_value& val, bool ifFound)
587 bool tfVarFound = false;
588 if (displayObject()) {
589 MovieClip* mc = dynamic_cast<MovieClip*>(displayObject());
590 if (mc) tfVarFound = mc->setTextFieldVariables(uri, val);
591 // We still need to set the member.
594 // Handle the length property for arrays. NB: checkArrayLength() will
595 // call this function again if the key is a valid index.
596 if (array()) checkArrayLength(*this, uri, val);
598 PrototypeRecursor<Exists> pr(this, uri);
600 Property* prop = pr.getProperty();
602 // We won't scan the inheritance chain if we find a member,
603 // even if invisible.
604 if (!prop) {
606 if (displayObject()) {
607 DisplayObject* d = displayObject();
608 if (setDisplayObjectProperty(*d, uri, val)) return true;
609 // TODO: should we execute triggers?
612 const int version = getSWFVersion(*this);
613 while (pr()) {
614 if ((prop = pr.getProperty())) {
615 if ((prop->isGetterSetter()) && visible(*prop, version)) {
616 break;
618 else prop = 0;
623 if (prop) {
624 if (readOnly(*prop)) {
625 IF_VERBOSE_ASCODING_ERRORS(
626 ObjectURI::Logger l(getStringTable(*this));
627 log_aserror(_("Attempt to set read-only property '%s'"),
628 l(uri));
630 return true;
633 try {
634 executeTriggers(prop, uri, val);
636 catch (const ActionTypeError& exc) {
637 IF_VERBOSE_ASCODING_ERRORS(
638 log_aserror(
639 _("%s: %s"), getStringTable(*this).value(getName(uri)), exc.what());
643 return true;
646 // Else, add new property...
647 if (ifFound) return false;
649 // Property does not exist, so it won't be read-only. Set it.
650 if (!_members.setValue(uri, val)) {
652 IF_VERBOSE_ASCODING_ERRORS(
653 ObjectURI::Logger l(getStringTable(*this));
654 log_aserror(_("Unknown failure in setting property '%s' on "
655 "object '%p'"), l(uri), (void*) this);
657 return false;
660 executeTriggers(prop, uri, val);
662 // Return true if we found a textfield variable.
663 if (tfVarFound) return true;
665 return false;
669 void
670 as_object::init_member(const std::string& key1, const as_value& val, int flags)
672 const ObjectURI& uri(getURI(vm(), key1));
673 init_member(uri, val, flags);
676 void
677 as_object::init_member(const ObjectURI& uri, const as_value& val, int flags)
680 // Set (or create) a SimpleProperty
681 if (!_members.setValue(uri, val, flags)) {
682 ObjectURI::Logger l(getStringTable(*this));
683 log_error(_("Attempt to initialize read-only property '%s'"
684 " on object '%p' twice"), l(uri), (void*)this);
685 // We shouldn't attempt to initialize a member twice, should we ?
686 abort();
690 void
691 as_object::init_property(const std::string& name, as_function& getter,
692 as_function& setter, int flags)
694 const ObjectURI& uri = getURI(vm(), name);
695 init_property(uri, getter, setter, flags);
698 void
699 as_object::init_property(const ObjectURI& uri, as_function& getter,
700 as_function& setter, int flags)
702 _members.addGetterSetter(uri, getter, &setter, as_value(), flags);
705 void
706 as_object::init_property(const std::string& name, as_c_function_ptr getter,
707 as_c_function_ptr setter, int flags)
709 const ObjectURI& uri = getURI(vm(), name);
710 init_property(uri, getter, setter, flags);
713 void
714 as_object::init_property(const ObjectURI& uri, as_c_function_ptr getter,
715 as_c_function_ptr setter, int flags)
717 _members.addGetterSetter(uri, getter, setter, flags);
720 bool
721 as_object::init_destructive_property(const ObjectURI& uri, as_function& getter,
722 int flags)
724 return _members.addDestructiveGetter(uri, getter, flags);
727 bool
728 as_object::init_destructive_property(const ObjectURI& uri,
729 as_c_function_ptr getter, int flags)
731 return _members.addDestructiveGetter(uri, getter, flags);
734 void
735 as_object::init_readonly_property(const std::string& name, as_function& getter,
736 int initflags)
738 const ObjectURI& uri = getURI(vm(), name);
740 init_property(uri, getter, getter, initflags | PropFlags::readOnly);
741 assert(_members.getProperty(uri));
744 void
745 as_object::init_readonly_property(const std::string& name,
746 as_c_function_ptr getter, int initflags)
748 const ObjectURI& uri = getURI(vm(), name);
749 init_property(uri, getter, getter, initflags | PropFlags::readOnly);
750 assert(_members.getProperty(uri));
753 void
754 as_object::set_member_flags(const ObjectURI& uri, int setTrue, int setFalse)
756 _members.setFlags(uri, setTrue, setFalse);
759 void
760 as_object::addInterface(as_object* obj)
762 assert(obj);
763 if (std::find(_interfaces.begin(), _interfaces.end(), obj) ==
764 _interfaces.end()) {
765 _interfaces.push_back(obj);
769 bool
770 as_object::instanceOf(as_object* ctor)
773 /// An object is never an instance of a null prototype.
774 if (!ctor) return false;
776 as_value protoVal;
777 if (!ctor->get_member(NSV::PROP_PROTOTYPE, &protoVal)) {
778 #ifdef GNASH_DEBUG_INSTANCE_OF
779 log_debug("Object %p can't be an instance of an object (%p) with no 'prototype'",
780 (void*)this, (void*)ctor);
781 #endif
782 return false;
785 as_object* ctorProto = toObject(protoVal, getVM(*this));
786 if (!ctorProto) {
787 #ifdef GNASH_DEBUG_INSTANCE_OF
788 log_debug("Object %p can't be an instance of an object (%p) with non-object 'prototype' (%s)",
789 (void*)this, (void*)ctor, protoVal);
790 #endif
791 return false;
794 // TODO: cleanup the iteration, make it more readable ...
795 std::set<as_object*> visited;
797 as_object* obj = this;
798 while (obj && visited.insert(obj).second) {
799 as_object* thisProto = obj->get_prototype();
800 if (!thisProto) {
801 break;
804 // Check our proto
805 if (thisProto == ctorProto) {
806 #ifdef GNASH_DEBUG_INSTANCE_OF
807 log_debug("Object %p is an instance of constructor %p as the constructor exposes our __proto__ %p",
808 (void*)obj, (void*)ctor, (void*)thisProto);
809 #endif
810 return true;
813 // Check our proto interfaces
814 if (std::find(thisProto->_interfaces.begin(),
815 thisProto->_interfaces.end(), ctorProto)
816 != thisProto->_interfaces.end()) {
818 #ifdef GNASH_DEBUG_INSTANCE_OF
819 log_debug("Object %p __proto__ %p had one interface matching with the constructor prototype %p",
820 (void*)obj, (void*)thisProto, (void*)ctorProto);
821 #endif
822 return true;
825 obj = thisProto;
828 return false;
831 bool
832 as_object::prototypeOf(as_object& instance)
834 as_object* obj = &instance;
836 std::set<as_object*> visited;
838 while (obj && visited.insert(obj).second ) {
839 if (obj->get_prototype() == this) return true;
840 obj = obj->get_prototype();
843 // See actionscript.all/Inheritance.as for a way to trigger this
844 IF_VERBOSE_ASCODING_ERRORS(
845 if (obj) log_aserror(_("Circular inheritance chain detected "
846 "during isPrototypeOf call"));
849 return false;
852 void
853 as_object::dump_members()
855 log_debug("%d members of object %p follow", _members.size(),
856 static_cast<const void*>(this));
857 _members.dump();
860 void
861 as_object::setPropFlags(const as_value& props_val, int set_false, int set_true)
864 if (props_val.is_null()) {
865 // Take all the members of the object
866 _members.setFlagsAll(set_true, set_false);
867 return;
870 std::string propstr = props_val.to_string();
872 for (;;) {
874 std::string prop;
875 size_t next_comma=propstr.find(",");
876 if (next_comma == std::string::npos) {
877 prop = propstr;
879 else {
880 prop = propstr.substr(0,next_comma);
881 propstr = propstr.substr(next_comma+1);
884 // set_member_flags will take care of case conversion
885 set_member_flags(getURI(vm(), prop), set_true, set_false);
887 if (next_comma == std::string::npos) {
888 break;
891 return;
895 void
896 as_object::copyProperties(const as_object& o)
898 PropsCopier copier(*this);
900 // TODO: check if non-visible properties should be also copied !
901 o.visitProperties<Exists>(copier);
904 void
905 as_object::visitKeys(KeyVisitor& visitor) const
907 // Hack to handle MovieClips.
908 if (displayObject()) {
909 displayObject()->visitNonProperties(visitor);
912 // this set will keep track of visited objects,
913 // to avoid infinite loops
914 std::set<const as_object*> visited;
916 PropertyList::PropertyTracker doneList;
918 const as_object* current(this);
919 while (current && visited.insert(current).second) {
920 current->_members.visitKeys(visitor, doneList);
921 current = current->get_prototype();
926 Property*
927 as_object::getOwnProperty(const ObjectURI& uri)
929 return _members.getProperty(uri);
932 as_object*
933 as_object::get_prototype() const
935 int swfVersion = getSWFVersion(*this);
937 Property* prop = _members.getProperty(NSV::PROP_uuPROTOuu);
938 if (!prop) return 0;
939 if (!visible(*prop, swfVersion)) return 0;
941 const as_value& proto = prop->getValue(*this);
943 return toObject(proto, getVM(*this));
946 std::string
947 getURLEncodedVars(as_object& o)
949 SortedPropertyList props = enumerateProperties(o);
951 std::string data;
952 string_table& st = getStringTable(o);
954 for (SortedPropertyList::const_reverse_iterator i = props.rbegin(),
955 e = props.rend(); i != e; ++i) {
957 const std::string& name = i->first.toString(st);
958 const std::string& value = i->second.to_string();
960 // see bug #22006
961 if (!name.empty() && name[0] == '$') continue;
963 URL::encode(value);
964 if (i != props.rbegin()) data += '&';
966 data += name + "=" + value;
969 return data;
972 bool
973 as_object::watch(const ObjectURI& uri, as_function& trig,
974 const as_value& cust)
977 std::string propname = getStringTable(*this).value(getName(uri));
979 if (!_trigs.get()) _trigs.reset(new TriggerContainer);
981 TriggerContainer::iterator it = _trigs->find(uri);
982 if (it == _trigs->end()) {
983 return _trigs->insert(
984 std::make_pair(uri, Trigger(propname, trig, cust))).second;
986 it->second = Trigger(propname, trig, cust);
987 return true;
990 bool
991 as_object::unwatch(const ObjectURI& uri)
993 if (!_trigs.get()) return false;
995 TriggerContainer::iterator trigIter = _trigs->find(uri);
996 if (trigIter == _trigs->end()) {
997 log_debug("No watch for property %s",
998 getStringTable(*this).value(getName(uri)));
999 return false;
1001 Property* prop = _members.getProperty(uri);
1002 if (prop && prop->isGetterSetter()) {
1003 log_debug("Watch on %s not removed (is a getter-setter)",
1004 getStringTable(*this).value(getName(uri)));
1005 return false;
1007 trigIter->second.kill();
1008 return true;
1011 void
1012 as_object::markReachableResources() const
1014 _members.setReachable();
1016 if (_trigs.get()) {
1017 for (TriggerContainer::const_iterator it = _trigs->begin();
1018 it != _trigs->end(); ++it) {
1019 it->second.setReachable();
1023 // Mark interfaces reachable.
1024 std::for_each(_interfaces.begin(), _interfaces.end(),
1025 std::mem_fun(&as_object::setReachable));
1027 // Proxy objects can contain references to other as_objects.
1028 if (_relay) _relay->setReachable();
1029 if (_displayObject) _displayObject->setReachable();
1032 void
1033 Trigger::setReachable() const
1035 _func->setReachable();
1036 _customArg.setReachable();
1039 as_value
1040 Trigger::call(const as_value& oldval, const as_value& newval,
1041 as_object& this_obj)
1043 assert(!_dead);
1045 if (_executing) return newval;
1047 _executing = true;
1049 try {
1051 const as_environment env(getVM(this_obj));
1053 fn_call::Args args;
1054 args += _propname, oldval, newval, _customArg;
1056 fn_call fn(&this_obj, env, args);
1057 as_value ret = _func->call(fn);
1058 _executing = false;
1060 return ret;
1063 catch (const GnashException&) {
1064 _executing = false;
1065 throw;
1069 SortedPropertyList
1070 enumerateProperties(as_object& obj)
1073 // this set will keep track of visited objects,
1074 // to avoid infinite loops
1075 std::set<as_object*> visited;
1077 SortedPropertyList to;
1078 PropertyEnumerator e(to);
1079 as_object* current(&obj);
1081 while (current && visited.insert(current).second) {
1082 current->visitProperties<IsEnumerable>(e);
1083 current = current->get_prototype();
1085 return to;
1089 as_object*
1090 getPathElement(as_object& o, const ObjectURI& uri)
1092 as_value tmp;
1093 if (!o.get_member(uri, &tmp)) return 0;
1094 if (!tmp.is_object()) return 0;
1095 return toObject(tmp, getVM(o));
1099 void
1100 sendEvent(as_object& o, const as_environment& env, const ObjectURI& name)
1102 Property* prop = o.findProperty(name);
1103 if (prop) {
1104 fn_call::Args args;
1105 invoke(prop->getValue(o), env, &o, args);
1109 as_object*
1110 getObjectWithPrototype(Global_as& gl, const ObjectURI& c)
1112 as_object* ctor = toObject(getMember(gl, c), getVM(gl));
1113 as_object* proto = ctor ?
1114 toObject(getMember(*ctor, NSV::PROP_PROTOTYPE), getVM(gl)) : 0;
1116 as_object* o = createObject(gl);
1117 o->set_prototype(proto ? proto : as_value());
1118 return o;
1121 /// Get the VM from an as_object
1123 getVM(const as_object& o)
1125 return o.vm();
1128 /// Get the movie_root from an as_object
1129 movie_root&
1130 getRoot(const as_object& o)
1132 return o.vm().getRoot();
1135 /// Get the string_table from an as_object
1136 string_table&
1137 getStringTable(const as_object& o)
1139 return o.vm().getStringTable();
1142 const RunResources&
1143 getRunResources(const as_object& o)
1145 return o.vm().getRoot().runResources();
1149 getSWFVersion(const as_object& o)
1151 return o.vm().getSWFVersion();
1154 Global_as&
1155 getGlobal(const as_object& o)
1157 return *o.vm().getGlobal();
1160 } // end of gnash namespace
1162 // local Variables:
1163 // mode: C++
1164 // indent-tabs-mode: nil
1165 // End: