Update with current status
[gnash.git] / libcore / asobj / XMLNode_as.cpp
blob6b3f43aa5264e98a51771f42ef98d9d623aea5d0
1 // XMLNode_as.cpp: ActionScript "XMLNode" class, for Gnash.
2 //
3 // Copyright (C) 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 #include "XMLNode_as.h"
22 #include <functional>
23 #include <string>
24 #include <sstream>
25 #include <vector>
26 #include <algorithm>
28 #include "XML_as.h"
29 #include "VM.h"
30 #include "log.h"
31 #include "fn_call.h"
32 #include "Global_as.h"
33 #include "NativeFunction.h"
34 #include "PropertyList.h"
35 #include "Global_as.h"
36 #include "Object.h"
37 #include "Array_as.h"
38 #include "namedStrings.h"
40 namespace gnash {
42 // Function Prototypes
43 namespace {
44 typedef std::pair<std::string, std::string> StringPair;
45 typedef std::vector<StringPair> StringPairs;
46 void enumerateAttributes(const XMLNode_as& node,
47 StringPairs& attributes);
48 bool prefixMatches(const StringPairs::value_type& val,
49 const std::string& prefix);
50 bool namespaceMatches(
51 const StringPairs::value_type& val,
52 const std::string& ns);
54 as_value xmlnode_new(const fn_call& fn);
55 as_value xmlnode_nodeName(const fn_call& fn);
56 as_value xmlnode_nodeValue(const fn_call& fn);
57 as_value xmlnode_nodeType(const fn_call& fn);
58 as_value xmlnode_attributes(const fn_call& fn);
59 as_value xmlnode_appendChild(const fn_call& fn);
60 as_value xmlnode_cloneNode(const fn_call& fn);
61 as_value xmlnode_lastChild(const fn_call& fn);
62 as_value xmlnode_firstChild(const fn_call& fn);
63 as_value xmlnode_nextSibling(const fn_call& fn);
64 as_value xmlnode_childNodes(const fn_call& fn);
65 as_value xmlnode_previousSibling(const fn_call& fn);
66 as_value xmlnode_parentNode(const fn_call& fn);
67 as_value xmlnode_getNamespaceForPrefix(const fn_call& fn);
68 as_value xmlnode_getPrefixForNamespace(const fn_call& fn);
69 as_value xmlnode_namespaceURI(const fn_call& fn);
70 as_value xmlnode_hasChildNodes(const fn_call& fn);
71 as_value xmlnode_insertBefore(const fn_call& fn);
72 as_value xmlnode_removeNode(const fn_call& fn);
73 as_value xmlnode_toString(const fn_call& fn);
74 as_value xmlnode_localName(const fn_call& fn);
75 as_value xmlnode_prefix(const fn_call& fn);
76 void attachXMLNodeInterface(as_object& o);
79 XMLNode_as::XMLNode_as(Global_as& gl)
81 _global(gl),
82 _object(nullptr),
83 _parent(nullptr),
84 _attributes(new as_object(gl)),
85 _childNodes(nullptr),
86 _type(Element)
90 XMLNode_as::XMLNode_as(const XMLNode_as& tpl, bool deep)
92 _global(tpl._global),
93 _object(nullptr),
94 _parent(nullptr),
95 _attributes(new as_object(_global)),
96 _childNodes(nullptr),
97 _name(tpl._name),
98 _value(tpl._value),
99 _type(tpl._type)
101 // only clone children if in deep mode
102 if (deep) {
103 const Children& from=tpl._children;
104 for (const auto& child : from) {
105 XMLNode_as* copy = new XMLNode_as(*child, deep);
106 copy->setParent(this);
107 _children.push_back(copy);
112 XMLNode_as::~XMLNode_as()
114 // In practice it is quite likely that the child will be garbage-collected
115 // before the parent. See Savannah bug #39404.
116 if (_parent ) {
117 // NOTE: do not removeChild as it makes too much
118 // noise including calls to string_table
119 // (due to updateChildNodes)
120 // See https://savannah.gnu.org/bugs/?40439
121 _parent->_children.remove(this);
122 _parent = nullptr;
125 clearChildren();
128 as_object*
129 XMLNode_as::object()
132 // This is almost the same as if the XMLNode constructor were called,
133 // but not quite. There is no __constructor__ property, and when we
134 // override _global.XMLNode, we can show that it is not called.
135 if (!_object) {
136 as_object* o = createObject(_global);
137 as_object* xn =
138 toObject(getMember(_global, NSV::CLASS_XMLNODE), getVM(_global));
139 if (xn) {
140 o->set_prototype(getMember(*xn, NSV::PROP_PROTOTYPE));
141 o->init_member(NSV::PROP_CONSTRUCTOR, xn);
143 o->setRelay(this);
144 setObject(o);
146 return _object;
149 void
150 XMLNode_as::updateChildNodes()
152 if (!_childNodes) return;
154 // Clear array of all elements.
155 _childNodes->set_member(NSV::PROP_LENGTH, 0.0);
157 if (_children.empty()) return;
159 VM& vm = getVM(_global);
161 // Set up the array without calling push()!
162 const size_t size = _children.size();
163 Children::const_iterator it = _children.begin();
164 for (size_t i = 0; i != size; ++i, ++it) {
165 XMLNode_as* node = *it;
166 const ObjectURI& key = arrayKey(vm, i);
167 _childNodes->set_member(key, node->object());
169 // All elements are set to readonly.
170 _childNodes->set_member_flags(key, PropFlags::readOnly);
174 as_object*
175 XMLNode_as::childNodes()
177 if (!_childNodes) {
178 _childNodes = _global.createArray();
179 updateChildNodes();
181 return _childNodes;
184 bool
185 XMLNode_as::hasChildNodes() const
187 return !_children.empty();
190 XMLNode_as*
191 XMLNode_as::firstChild() const
193 if (_children.empty()) return nullptr;
194 return _children.front();
197 XMLNode_as*
198 XMLNode_as::cloneNode(bool deep) const
200 XMLNode_as* newnode = new XMLNode_as(*this, deep);
201 return newnode;
204 XMLNode_as*
205 XMLNode_as::lastChild() const
207 if (_children.empty()) {
208 return nullptr;
210 return _children.back();
213 void
214 XMLNode_as::removeChild(XMLNode_as* node)
216 node->setParent(nullptr);
217 _children.remove(node);
218 updateChildNodes();
221 void
222 XMLNode_as::appendChild(XMLNode_as* node)
224 assert(node);
225 node->setParent(this);
226 _children.push_back(node);
227 updateChildNodes();
230 bool
231 XMLNode_as::descendsFrom(XMLNode_as* node) const
233 if (node == this) {
234 return true;
236 XMLNode_as* parent = getParent();
237 if (parent) {
238 return parent->descendsFrom(node);
241 return false;
244 void
245 XMLNode_as::insertBefore(XMLNode_as* newnode, XMLNode_as* pos)
247 assert(_object);
249 // find iterator for positional parameter
250 Children::iterator it = std::find(_children.begin(), _children.end(), pos);
251 if (it == _children.end()) {
252 IF_VERBOSE_ASCODING_ERRORS(
253 log_aserror(_("XMLNode.insertBefore(): positional parameter "
254 "is not a child of this node"));
256 return;
259 _children.insert(it, newnode);
261 XMLNode_as* parent = newnode->getParent();
262 if (parent) {
263 parent->removeChild(newnode);
266 newnode->setParent(this);
267 updateChildNodes();
270 XMLNode_as*
271 XMLNode_as::previousSibling() const
273 if (!_parent) return nullptr;
274 if (_parent->_children.size() <= 1) return nullptr;
276 XMLNode_as *previous_node = nullptr;
277 for (XMLNode_as * child : _parent->_children) {
279 if (child == this) return previous_node;
281 previous_node = child;
284 return nullptr;
287 XMLNode_as*
288 XMLNode_as::nextSibling() const
291 if (!_parent) return nullptr;
293 if (_parent->_children.size() <= 1) return nullptr;
295 XMLNode_as *previous_node = nullptr;
296 for (Children::reverse_iterator itx = _parent->_children.rbegin();
297 itx != _parent->_children.rend(); ++itx) {
299 if (*itx == this) return previous_node;
300 previous_node = *itx;
303 return nullptr;
306 void
307 XMLNode_as::toString(std::ostream& xmlout, bool encode) const
309 stringify(*this, xmlout, encode);
312 void
313 XMLNode_as::setAttribute(const std::string& name, const std::string& value)
315 if (_attributes) {
316 VM& vm = getVM(_global);
317 _attributes->set_member(getURI(vm, name), value);
321 bool
322 XMLNode_as::getPrefixForNamespace(const std::string& ns, std::string& prefix)
323 const
325 const XMLNode_as* node = this;
326 StringPairs::const_iterator it;
327 StringPairs attrs;
329 while (node) {
330 enumerateAttributes(*node, attrs);
331 if (!attrs.empty())
333 it = std::find_if(attrs.begin(), attrs.end(),
334 std::bind(namespaceMatches, std::placeholders::_1, ns));
335 if (it != attrs.end()) break;
337 node = node->getParent();
340 // None found.
341 if (!node) return false;
343 // Return the matching prefix
344 const std::string& name = it->first;
346 if (name.length() == 5) {
347 return true;
350 assert (name.length() >= 6);
352 if (name[5] != ':') return false;
354 // Can also be empty.
355 prefix = name.substr(6);
356 return true;
359 void
360 XMLNode_as::getNamespaceForPrefix(const std::string& prefix, std::string& ns)
361 const
363 const XMLNode_as* node = this;
364 StringPairs::const_iterator it;
365 StringPairs attrs;
367 while (node) {
369 enumerateAttributes(*node, attrs);
371 if (!attrs.empty()) {
373 it = std::find_if(attrs.begin(), attrs.end(),
374 std::bind(prefixMatches, std::placeholders::_1, prefix));
375 if (it != attrs.end()) break;
377 node = node->getParent();
380 // None found; return undefined
381 if (!node) return;
383 // Return the matching namespace
384 ns = it->second;
388 bool
389 XMLNode_as::extractPrefix(std::string& prefix) const
391 prefix.clear();
392 if (_name.empty()) return false;
394 std::string::size_type pos = _name.find(':');
395 if (pos == std::string::npos || pos == _name.size() - 1) {
396 return false;
399 prefix = _name.substr(0, pos);
400 return true;
403 void
404 XMLNode_as::clearChildren()
406 for (XMLNode_as* node : _children) {
408 node->setParent(nullptr);
409 if (!node->_object) {
410 // The node is not GC'd because it has no associated object.
411 // See XMLNode_as class docs.
412 delete node;
415 _children.clear();
417 // Reset so that it is reinitialized on next access.
418 _childNodes = nullptr;
421 void
422 XMLNode_as::stringify(const XMLNode_as& xml, std::ostream& xmlout, bool encode)
425 const std::string& nodeValue = xml.nodeValue();
426 const std::string& nodeName = xml.nodeName();
427 NodeType type = xml.nodeType();
429 #ifdef GNASH_DEBUG
430 log_debug("Stringifying node %p with name %s, as_value %s, %u "
431 "attributes and %u children", (void*)&xml, nodeName,
432 nodeValue, xml._attributes.size(), xml._children.size());
433 #endif
435 if (!nodeName.empty() || type == Element) {
437 xmlout << "<" << nodeName;
439 // Process the attributes, if any
440 StringPairs attrs;
441 enumerateAttributes(xml, attrs);
442 if (!attrs.empty()) {
444 for (auto& attr : attrs) {
445 escapeXML(attr.second);
446 xmlout << " " << attr.first << "=\"" << attr.second << "\"";
450 // If the node has no content, just close the tag now
451 if (nodeValue.empty() && xml._children.empty()) {
452 xmlout << " />";
453 return;
455 else {
456 // Will use a closing tag later
457 xmlout << ">";
461 // Node as_value first, then children
462 if (type == Text)
464 Global_as& gl = xml._global;
466 // Insert entities.
467 std::string escaped(nodeValue);
468 escapeXML(escaped);
469 const std::string& val = encode ?
470 callMethod(&gl, NSV::PROP_ESCAPE, escaped).to_string() :
471 escaped;
473 xmlout << val;
476 // Childs, after node as_value.
477 for (XMLNode_as* child : xml._children) {
479 child->toString(xmlout, encode);
482 if (!nodeName.empty() || type == Element) {
483 xmlout << "</" << nodeName << ">";
487 void
488 XMLNode_as::setReachable()
490 // If there is a parent, make sure its object is reachable. This goes
491 // up towards the root node of tree without marking the XMLNode
492 // resources (which would cause infinite recursion).
493 if (_parent && _parent->_object) _parent->_object->setReachable();
495 // Mark children
496 std::for_each(_children.begin(), _children.end(),
497 std::mem_fn(&XMLNode_as::setReachable));
499 // Mark attributes object
500 if (_attributes) _attributes->setReachable();
502 if (_object) _object->setReachable();
504 if (_childNodes) _childNodes->setReachable();
507 void
508 registerXMLNodeNative(as_object& where)
510 VM& vm = getVM(where);
511 vm.registerNative(xmlnode_cloneNode, 253, 1);
512 vm.registerNative(xmlnode_removeNode, 253, 2);
513 vm.registerNative(xmlnode_insertBefore, 253, 3);
514 vm.registerNative(xmlnode_appendChild, 253, 4);
515 vm.registerNative(xmlnode_hasChildNodes, 253, 5);
516 vm.registerNative(xmlnode_toString, 253, 6);
517 vm.registerNative(xmlnode_getNamespaceForPrefix, 253, 7);
518 vm.registerNative(xmlnode_getPrefixForNamespace, 253, 8);
521 void
522 xmlnode_class_init(as_object& where, const ObjectURI& uri)
524 Global_as& gl = getGlobal(where);
525 as_object* proto = createObject(gl);
526 attachXMLNodeInterface(*proto);
527 as_object* cl = gl.createClass(&xmlnode_new, proto);
529 where.init_member(uri, cl, as_object::DefaultFlags);
533 namespace {
535 void
536 attachXMLNodeInterface(as_object& o)
539 VM& vm = getVM(o);
541 const int noFlags = 0;
543 // No prop flags:
544 o.init_member("cloneNode", vm.getNative(253, 1), noFlags);
545 o.init_member("removeNode", vm.getNative(253, 2), noFlags);
546 o.init_member("insertBefore", vm.getNative(253, 3), noFlags);
547 o.init_member("appendChild", vm.getNative(253, 4), noFlags);
548 o.init_member("hasChildNodes", vm.getNative(253, 5), noFlags);
549 o.init_member("toString", vm.getNative(253, 6), noFlags);
550 o.init_member("getNamespaceForPrefix", vm.getNative(253, 7), noFlags);
551 o.init_member("getPrefixForNamespace", vm.getNative(253, 8), noFlags);
553 const int protectedFlags = 0;
555 // Just the protected flag:
557 o.init_readonly_property("attributes", &xmlnode_attributes, protectedFlags);
558 o.init_readonly_property("childNodes", &xmlnode_childNodes, protectedFlags);
559 o.init_readonly_property("firstChild", &xmlnode_firstChild, protectedFlags);
560 o.init_readonly_property("lastChild", &xmlnode_lastChild, protectedFlags);
561 o.init_readonly_property("nextSibling",
562 &xmlnode_nextSibling, protectedFlags);
563 o.init_property("nodeName", &xmlnode_nodeName,
564 &xmlnode_nodeName, protectedFlags);
565 o.init_readonly_property("nodeType", &xmlnode_nodeType, protectedFlags);
566 o.init_property("nodeValue", &xmlnode_nodeValue,
567 &xmlnode_nodeValue, protectedFlags);
568 o.init_readonly_property("parentNode", &xmlnode_parentNode, protectedFlags);
569 o.init_readonly_property("previousSibling",
570 &xmlnode_previousSibling, protectedFlags);
571 o.init_readonly_property("prefix", &xmlnode_prefix, protectedFlags);
572 o.init_readonly_property("localName", &xmlnode_localName, protectedFlags);
573 o.init_readonly_property("namespaceURI",
574 &xmlnode_namespaceURI, protectedFlags);
578 as_value
579 xmlnode_new(const fn_call& fn)
582 as_object* obj = ensure<ValidThis>(fn);
584 if (!fn.nargs) {
585 return as_value();
588 std::unique_ptr<XMLNode_as> xml(new XMLNode_as(getGlobal(fn)));
589 xml->nodeTypeSet(XMLNode_as::NodeType(toInt(fn.arg(0), getVM(fn))));
591 if (fn.nargs > 1) {
592 const std::string& str = fn.arg(1).to_string();
593 switch (xml->nodeType())
595 case XMLNode_as::Element:
596 xml->nodeNameSet(str);
597 break;
598 default:
599 xml->nodeValueSet(str);
600 break;
604 // This sets the relay!
605 xml->setObject(obj);
606 obj->setRelay(xml.release());
608 return as_value();
612 as_value
613 xmlnode_appendChild(const fn_call& fn)
616 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
618 if (!fn.nargs) {
619 IF_VERBOSE_ASCODING_ERRORS(
620 log_aserror(_("XMLNode::appendChild() needs at least one "
621 "argument"));
623 return as_value();
626 XMLNode_as* node;
627 if (!isNativeType(toObject(fn.arg(0), getVM(fn)), node)) {
628 IF_VERBOSE_ASCODING_ERRORS(
629 log_aserror(_("First argument to XMLNode::appendChild() is not "
630 "an XMLNode"));
632 return as_value();
635 if (ptr->descendsFrom(node)) {
636 IF_VERBOSE_ASCODING_ERRORS(
637 log_aserror(_("XMLNode.appendChild(): attempted to move a node to "
638 "among its own descendants."));
640 return as_value();
643 XMLNode_as* parent = node->getParent();
644 if (parent) {
645 parent->removeChild(node);
647 ptr->appendChild(node);
649 return as_value();
653 as_value
654 xmlnode_cloneNode(const fn_call& fn)
656 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
658 bool deep = false;
659 if (fn.nargs > 0) deep = toBool(fn.arg(0), getVM(fn));
661 as_object* newnode = ptr->cloneNode(deep)->object();
662 return as_value(newnode);
666 as_value
667 xmlnode_insertBefore(const fn_call& fn)
669 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
671 if ( fn.nargs < 2 )
673 IF_VERBOSE_ASCODING_ERRORS(
674 std::stringstream ss; fn.dump_args(ss);
675 log_aserror(_("XMLNode.insertBefore(%s) needs at least two "
676 "arguments"), ss.str());
678 return as_value();
681 XMLNode_as* newnode;
683 if (!isNativeType(toObject(fn.arg(0), getVM(fn)), newnode)) {
684 IF_VERBOSE_ASCODING_ERRORS(
685 std::stringstream ss; fn.dump_args(ss);
686 log_aserror(_("First argument to XMLNode.insertBefore(%s) is not "
687 "an XMLNode"), ss.str());
689 return as_value();
692 XMLNode_as* pos;
694 if (!isNativeType(toObject(fn.arg(1), getVM(fn)), pos)) {
695 IF_VERBOSE_ASCODING_ERRORS(
696 std::stringstream ss; fn.dump_args(ss);
697 log_aserror(_("Second argument to XMLNode.insertBefore(%s) is not "
698 "an XMLNode"), ss.str());
700 return as_value();
703 if (pos->descendsFrom(newnode)) {
704 IF_VERBOSE_ASCODING_ERRORS(
705 log_aserror(_("XMLNode.insertBefore(): attempted to move a node to "
706 "among its own descendants."));
708 return as_value();
711 ptr->insertBefore(newnode, pos);
712 return as_value();
717 as_value
718 xmlnode_getNamespaceForPrefix(const fn_call& fn)
720 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
721 if (!fn.nargs) {
722 return as_value();
725 std::string ns;
727 ptr->getNamespaceForPrefix(fn.arg(0).to_string(), ns);
728 if (ns.empty()) return as_value();
729 return as_value(ns);
733 as_value
734 xmlnode_getPrefixForNamespace(const fn_call& fn)
736 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
737 if (!fn.nargs) {
738 return as_value();
741 std::string prefix;
743 // Return undefined if none found; otherwise the prefix string found.
744 // This can be empty if it is a standard namespace.
745 if (!ptr->getPrefixForNamespace(fn.arg(0).to_string(), prefix)) {
746 return as_value();
748 return as_value(prefix);
751 /// If the node has a prefix, return the matching namespace. Otherwise,
752 /// returns a namespaceURI set with the xmlns attribute, searching upwards
753 /// through parent nodes if necessary.
755 /// This standard namespace can only be set during XML parsing and cannot
756 /// be changed or set using attributes.
758 /// Conversely, the similar getNamespaceForPrefix("") can be set and changed
759 /// through attributes.
760 as_value
761 xmlnode_namespaceURI(const fn_call& fn)
763 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
765 // Read-only property
767 const std::string& name = ptr->nodeName();
769 if (name.empty()) {
770 as_value null;
771 null.set_null();
772 return null;
775 std::string prefix;
776 if (ptr->extractPrefix(prefix)) {
777 std::string ns;
778 ptr->getNamespaceForPrefix(prefix, ns);
779 return as_value(ns);
782 // Search recursively for a namespace. Return an empty string
783 // if none found.
784 XMLNode_as* node = ptr;
785 while (node && node->getNamespaceURI().empty()) {
786 node = node->getParent();
788 if (!node) return as_value("");
790 return as_value(node->getNamespaceURI());
794 // Return the prefix part of the node name. If there is no colon, or one
795 // colon at the end of the string, this is empty. Otherwise it is the part
796 // up to the first colon.
797 as_value
798 xmlnode_prefix(const fn_call& fn)
800 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
802 // Read-only property
804 if (ptr->nodeName().empty()) {
805 as_value null;
806 null.set_null();
807 return null;
810 std::string prefix;
811 if (!ptr->extractPrefix(prefix)) return as_value("");
812 return as_value(prefix);
816 // The local part of a node name. If there is no colon or a single colon
817 // at the end of the string, this is the whole string. Otherwise all of the
818 // string after the first colon.
819 as_value
820 xmlnode_localName(const fn_call& fn)
822 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
824 // Read-only property
826 if (ptr->nodeName().empty()) {
827 as_value null;
828 null.set_null();
829 return null;
832 const std::string& nodeName = ptr->nodeName();
833 if (nodeName.empty()) return as_value("");
835 std::string::size_type pos = nodeName.find(':');
836 if (pos == std::string::npos || pos == nodeName.size() - 1) {
837 return as_value(nodeName);
840 return as_value(nodeName.substr(pos + 1));
844 as_value
845 xmlnode_removeNode(const fn_call& fn)
847 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
849 XMLNode_as* parent = ptr->getParent();
850 if (parent) parent->removeChild(ptr);
851 return as_value();
855 as_value
856 xmlnode_toString(const fn_call& fn)
859 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
861 std::stringstream ss;
862 ptr->toString(ss);
864 return as_value(ss.str());
868 as_value
869 xmlnode_hasChildNodes(const fn_call& fn)
871 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
872 return as_value(ptr->hasChildNodes());
876 as_value
877 xmlnode_nodeValue(const fn_call& fn)
879 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
880 as_value rv;
881 rv.set_null();
883 if ( fn.nargs == 0 )
885 const std::string& val = ptr->nodeValue();
886 if ( ! val.empty() ) rv = val;
888 else
890 ptr->nodeValueSet(fn.arg(0).to_string());
892 return rv;
896 as_value
897 xmlnode_nodeName(const fn_call& fn)
899 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
900 as_value rv;
901 rv.set_null();
903 if (!fn.nargs) {
904 const std::string& val = ptr->nodeName();
905 if ( ! val.empty() ) rv = val;
907 else {
908 ptr->nodeNameSet(fn.arg(0).to_string());
910 return rv;
914 as_value
915 xmlnode_nodeType(const fn_call& fn)
917 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
918 return as_value(ptr->nodeType());
922 as_value
923 xmlnode_attributes(const fn_call& fn)
925 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
927 as_object* attrs = ptr->getAttributes();
928 if (attrs) return as_value(attrs);
929 return as_value();
933 /// Read-only property; evaluates the specified XML object and
934 /// references the first child in the parent node's child
935 /// list. This property is null if the node does not have
936 /// children. This property is undefined if the node is a text
937 /// node. This is a read-only property and cannot be used to
938 /// manipulate child nodes; use the appendChild(), insertBefore(),
939 /// and removeNode() methods to manipulate child nodes.
941 as_value
942 xmlnode_firstChild(const fn_call& fn)
944 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
945 as_value rv;
946 rv.set_null();
948 XMLNode_as* node = ptr->firstChild();
949 if (node) {
950 rv = node->object();
953 return rv;
957 /// Read-only property; an XMLNode as_value that references the last
958 /// child in the node's child list. The XML.lastChild property
959 /// is null if the node does not have children. This property cannot
960 /// be used to manipulate child nodes; use the appendChild(),
961 /// insertBefore(), and removeNode() methods to manipulate child
962 /// nodes.
963 as_value
964 xmlnode_lastChild(const fn_call& fn)
966 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
967 as_value rv;
968 rv.set_null();
970 XMLNode_as* node = ptr->lastChild();
971 if (node) {
972 rv = node->object();
975 return rv;
979 as_value
980 xmlnode_nextSibling(const fn_call& fn)
982 as_value rv;
983 rv.set_null();
985 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
986 XMLNode_as *node = ptr->nextSibling();
987 if (node) {
988 rv = node->object();
990 return rv;
994 as_value
995 xmlnode_previousSibling(const fn_call& fn)
997 as_value rv;
998 rv.set_null();
1000 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
1001 XMLNode_as *node = ptr->previousSibling();
1002 if (node) {
1003 rv = node->object();
1005 return rv;
1009 as_value
1010 xmlnode_parentNode(const fn_call& fn)
1012 as_value rv;
1013 rv.set_null();
1015 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
1016 XMLNode_as* node = ptr->getParent();
1017 if (node) {
1018 rv = node->object();
1020 return rv;
1023 as_value
1024 xmlnode_childNodes(const fn_call& fn)
1026 XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
1027 return ptr->childNodes();
1031 void
1032 enumerateAttributes(const XMLNode_as& node, StringPairs& pairs)
1034 pairs.clear();
1036 as_object* obj = node.getAttributes();
1037 if (obj) {
1038 string_table& st = getStringTable(*obj);
1039 SortedPropertyList attrs = enumerateProperties(*obj);
1040 for (SortedPropertyList::const_reverse_iterator i = attrs.rbegin(),
1041 e = attrs.rend(); i != e; ++i) {
1042 // TODO: second argument should take version.
1043 pairs.push_back(
1044 std::make_pair(i->first.toString(st), i->second.to_string()));
1050 /// Return true if this attribute is a namespace specifier and the
1051 /// namespace matches.
1052 bool
1053 namespaceMatches(const StringPairs::value_type& val,
1054 const std::string& ns)
1056 StringNoCaseEqual noCaseCompare;
1057 return (noCaseCompare(val.first.substr(0, 5), "xmlns") &&
1058 noCaseCompare(val.second, ns));
1062 bool
1063 prefixMatches(const StringPairs::value_type& val,
1064 const std::string& prefix)
1066 const std::string& name = val.first;
1067 StringNoCaseEqual noCaseCompare;
1069 // An empty prefix searches for a standard namespace specifier.
1070 // Attributes are stored with no trailing or leading whitespace,
1071 // so a simple comparison should do. TODO: what about "xmlns:"?
1072 if (prefix.empty()) {
1073 return noCaseCompare(name, "xmlns") || noCaseCompare(name, "xmlns:");
1076 if (!noCaseCompare(name.substr(0, 6), "xmlns:")) return false;
1078 return noCaseCompare(prefix, name.substr(6));
1081 } // anonymous namespace
1082 } // gnash namespace
1083 // local Variables:
1084 // mode: C++
1085 // indent-tabs-mode: t
1086 // End: