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.
14 //
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
18 //
19 
20 #include "XMLNode_as.h"
21 
22 #include <functional>
23 #include <string>
24 #include <sstream>
25 #include <vector>
26 #include <algorithm>
27 
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"
39 
40 namespace gnash {
41 
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);
53 
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);
77 }
78 
XMLNode_as(Global_as & gl)79 XMLNode_as::XMLNode_as(Global_as& gl)
80     :
81     _global(gl),
82     _object(nullptr),
83     _parent(nullptr),
84     _attributes(new as_object(gl)),
85     _childNodes(nullptr),
86     _type(Element)
87 {
88 }
89 
XMLNode_as(const XMLNode_as & tpl,bool deep)90 XMLNode_as::XMLNode_as(const XMLNode_as& tpl, bool deep)
91     :
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)
100 {
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);
108         }
109     }
110 }
111 
~XMLNode_as()112 XMLNode_as::~XMLNode_as()
113 {
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;
123     }
124 
125     clearChildren();
126 }
127 
128 as_object*
object()129 XMLNode_as::object()
130 {
131 
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);
142         }
143         o->setRelay(this);
144         setObject(o);
145     }
146     return _object;
147 }
148 
149 void
updateChildNodes()150 XMLNode_as::updateChildNodes()
151 {
152     if (!_childNodes) return;
153 
154     // Clear array of all elements.
155     _childNodes->set_member(NSV::PROP_LENGTH, 0.0);
156 
157     if (_children.empty()) return;
158 
159     VM& vm = getVM(_global);
160 
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());
168 
169         // All elements are set to readonly.
170         _childNodes->set_member_flags(key, PropFlags::readOnly);
171     }
172 }
173 
174 as_object*
childNodes()175 XMLNode_as::childNodes()
176 {
177     if (!_childNodes) {
178         _childNodes = _global.createArray();
179         updateChildNodes();
180     }
181     return _childNodes;
182 }
183 
184 bool
hasChildNodes() const185 XMLNode_as::hasChildNodes() const
186 {
187     return !_children.empty();
188 }
189 
190 XMLNode_as*
firstChild() const191 XMLNode_as::firstChild() const
192 {
193     if (_children.empty()) return nullptr;
194     return _children.front();
195 }
196 
197 XMLNode_as*
cloneNode(bool deep) const198 XMLNode_as::cloneNode(bool deep) const
199 {
200     XMLNode_as* newnode = new XMLNode_as(*this, deep);
201     return newnode;
202 }
203 
204 XMLNode_as*
lastChild() const205 XMLNode_as::lastChild() const
206 {
207 	if (_children.empty()) {
208         return nullptr;
209 	}
210 	return _children.back();
211 }
212 
213 void
removeChild(XMLNode_as * node)214 XMLNode_as::removeChild(XMLNode_as* node)
215 {
216     node->setParent(nullptr);
217     _children.remove(node);
218     updateChildNodes();
219 }
220 
221 void
appendChild(XMLNode_as * node)222 XMLNode_as::appendChild(XMLNode_as* node)
223 {
224     assert(node);
225     node->setParent(this);
226     _children.push_back(node);
227     updateChildNodes();
228 }
229 
230 bool
descendsFrom(XMLNode_as * node) const231 XMLNode_as::descendsFrom(XMLNode_as* node) const
232 {
233     if (node == this) {
234         return true;
235     }
236     XMLNode_as* parent = getParent();
237     if (parent) {
238         return parent->descendsFrom(node);
239     }
240 
241     return false;
242 }
243 
244 void
insertBefore(XMLNode_as * newnode,XMLNode_as * pos)245 XMLNode_as::insertBefore(XMLNode_as* newnode, XMLNode_as* pos)
246 {
247     assert(_object);
248 
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"));
255         );
256         return;
257     }
258 
259     _children.insert(it, newnode);
260 
261     XMLNode_as* parent = newnode->getParent();
262     if (parent) {
263         parent->removeChild(newnode);
264     }
265 
266     newnode->setParent(this);
267     updateChildNodes();
268 }
269 
270 XMLNode_as*
previousSibling() const271 XMLNode_as::previousSibling() const
272 {
273     if (!_parent) return nullptr;
274  	if (_parent->_children.size() <= 1) return nullptr;
275 
276     XMLNode_as *previous_node = nullptr;
277     for (XMLNode_as * child : _parent->_children) {
278 
279         if (child == this) return previous_node;
280 
281         previous_node = child;
282     }
283 
284     return nullptr;
285 }
286 
287 XMLNode_as*
nextSibling() const288 XMLNode_as::nextSibling() const
289 {
290 
291     if (!_parent) return nullptr;
292 
293     if (_parent->_children.size() <= 1) return nullptr;
294 
295     XMLNode_as *previous_node = nullptr;
296     for (Children::reverse_iterator itx = _parent->_children.rbegin();
297             itx != _parent->_children.rend(); ++itx) {
298 
299         if (*itx == this) return previous_node;
300 		previous_node = *itx;
301     }
302 
303     return nullptr;
304 }
305 
306 void
toString(std::ostream & xmlout,bool encode) const307 XMLNode_as::toString(std::ostream& xmlout, bool encode) const
308 {
309     stringify(*this, xmlout, encode);
310 }
311 
312 void
setAttribute(const std::string & name,const std::string & value)313 XMLNode_as::setAttribute(const std::string& name, const std::string& value)
314 {
315     if (_attributes) {
316         VM& vm = getVM(_global);
317         _attributes->set_member(getURI(vm, name), value);
318     }
319 }
320 
321 bool
getPrefixForNamespace(const std::string & ns,std::string & prefix) const322 XMLNode_as::getPrefixForNamespace(const std::string& ns, std::string& prefix)
323     const
324 {
325     const XMLNode_as* node = this;
326     StringPairs::const_iterator it;
327     StringPairs attrs;
328 
329     while (node) {
330         enumerateAttributes(*node, attrs);
331         if (!attrs.empty())
332         {
333             it = std::find_if(attrs.begin(), attrs.end(),
334                         std::bind(namespaceMatches, std::placeholders::_1, ns));
335             if (it != attrs.end()) break;
336         }
337         node = node->getParent();
338     }
339 
340     // None found.
341     if (!node) return false;
342 
343     // Return the matching prefix
344     const std::string& name = it->first;
345 
346     if (name.length() == 5) {
347         return true;
348     }
349 
350     assert (name.length() >= 6);
351 
352     if (name[5] != ':') return false;
353 
354     // Can also be empty.
355     prefix = name.substr(6);
356     return true;
357 }
358 
359 void
getNamespaceForPrefix(const std::string & prefix,std::string & ns) const360 XMLNode_as::getNamespaceForPrefix(const std::string& prefix, std::string& ns)
361     const
362 {
363     const XMLNode_as* node = this;
364     StringPairs::const_iterator it;
365     StringPairs attrs;
366 
367     while (node) {
368 
369         enumerateAttributes(*node, attrs);
370 
371         if (!attrs.empty()) {
372 
373             it = std::find_if(attrs.begin(), attrs.end(),
374                         std::bind(prefixMatches, std::placeholders::_1, prefix));
375             if (it != attrs.end()) break;
376         }
377         node = node->getParent();
378     }
379 
380     // None found; return undefined
381     if (!node) return;
382 
383     // Return the matching namespace
384     ns = it->second;
385 
386 }
387 
388 bool
extractPrefix(std::string & prefix) const389 XMLNode_as::extractPrefix(std::string& prefix) const
390 {
391     prefix.clear();
392     if (_name.empty()) return false;
393 
394     std::string::size_type pos = _name.find(':');
395     if (pos == std::string::npos || pos == _name.size() - 1) {
396         return false;
397     }
398 
399     prefix = _name.substr(0, pos);
400     return true;
401 }
402 
403 void
clearChildren()404 XMLNode_as::clearChildren()
405 {
406     for (XMLNode_as* node : _children) {
407 
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;
413         }
414     }
415     _children.clear();
416 
417     // Reset so that it is reinitialized on next access.
418     _childNodes = nullptr;
419 }
420 
421 void
stringify(const XMLNode_as & xml,std::ostream & xmlout,bool encode)422 XMLNode_as::stringify(const XMLNode_as& xml, std::ostream& xmlout, bool encode)
423 {
424 
425     const std::string& nodeValue = xml.nodeValue();
426     const std::string& nodeName = xml.nodeName();
427     NodeType type = xml.nodeType();
428 
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
434 
435     if (!nodeName.empty() || type == Element) {
436 
437         xmlout << "<" << nodeName;
438 
439         // Process the attributes, if any
440         StringPairs attrs;
441         enumerateAttributes(xml, attrs);
442         if (!attrs.empty()) {
443 
444             for (auto& attr : attrs) {
445                 escapeXML(attr.second);
446                 xmlout << " " << attr.first << "=\"" << attr.second << "\"";
447             }
448         }
449 
450         // If the node has no content, just close the tag now
451         if (nodeValue.empty() && xml._children.empty()) {
452             xmlout << " />";
453             return;
454         }
455         else {
456             // Will use a closing tag later
457             xmlout << ">";
458         }
459     }
460 
461     // Node as_value first, then children
462     if (type == Text)
463     {
464         Global_as& gl = xml._global;
465 
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;
472 
473 	    xmlout << val;
474     }
475 
476     // Childs, after node as_value.
477     for (XMLNode_as* child : xml._children) {
478 
479         child->toString(xmlout, encode);
480     }
481 
482     if (!nodeName.empty() || type == Element) {
483         xmlout << "</" << nodeName << ">";
484     }
485 }
486 
487 void
setReachable()488 XMLNode_as::setReachable()
489 {
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();
494 
495 	// Mark children
496     std::for_each(_children.begin(), _children.end(),
497             std::mem_fn(&XMLNode_as::setReachable));
498 
499 	// Mark attributes object
500 	if (_attributes) _attributes->setReachable();
501 
502     if (_object) _object->setReachable();
503 
504     if (_childNodes) _childNodes->setReachable();
505 }
506 
507 void
registerXMLNodeNative(as_object & where)508 registerXMLNodeNative(as_object& where)
509 {
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);
519 }
520 
521 void
xmlnode_class_init(as_object & where,const ObjectURI & uri)522 xmlnode_class_init(as_object& where, const ObjectURI& uri)
523 {
524     Global_as& gl = getGlobal(where);
525     as_object* proto = createObject(gl);
526     attachXMLNodeInterface(*proto);
527     as_object* cl = gl.createClass(&xmlnode_new, proto);
528 
529     where.init_member(uri, cl, as_object::DefaultFlags);
530 
531 }
532 
533 namespace {
534 
535 void
attachXMLNodeInterface(as_object & o)536 attachXMLNodeInterface(as_object& o)
537 {
538 
539     VM& vm = getVM(o);
540 
541     const int noFlags = 0;
542 
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);
552 
553     const int protectedFlags = 0;
554 
555     // Just the protected flag:
556 
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);
575 }
576 
577 
578 as_value
xmlnode_new(const fn_call & fn)579 xmlnode_new(const fn_call& fn)
580 {
581 
582     as_object* obj = ensure<ValidThis>(fn);
583 
584     if (!fn.nargs) {
585         return as_value();
586     }
587 
588     std::unique_ptr<XMLNode_as> xml(new XMLNode_as(getGlobal(fn)));
589     xml->nodeTypeSet(XMLNode_as::NodeType(toInt(fn.arg(0), getVM(fn))));
590 
591     if (fn.nargs > 1) {
592         const std::string& str = fn.arg(1).to_string();
593         switch (xml->nodeType())
594         {
595             case XMLNode_as::Element:
596                 xml->nodeNameSet(str);
597                 break;
598             default:
599                 xml->nodeValueSet(str);
600                 break;
601         }
602     }
603 
604     // This sets the relay!
605     xml->setObject(obj);
606     obj->setRelay(xml.release());
607 
608     return as_value();
609 }
610 
611 
612 as_value
xmlnode_appendChild(const fn_call & fn)613 xmlnode_appendChild(const fn_call& fn)
614 {
615 
616 	XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
617 
618 	if (!fn.nargs) {
619 		IF_VERBOSE_ASCODING_ERRORS(
620             log_aserror(_("XMLNode::appendChild() needs at least one "
621                     "argument"));
622 		);
623 		return as_value();
624 	}
625 
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"));
631 		);
632 		return as_value();
633 	}
634 
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."));
639         );
640         return as_value();
641     }
642 
643     XMLNode_as* parent = node->getParent();
644     if (parent) {
645         parent->removeChild(node);
646     }
647 	ptr->appendChild(node);
648 
649 	return as_value();
650 
651 }
652 
653 as_value
xmlnode_cloneNode(const fn_call & fn)654 xmlnode_cloneNode(const fn_call& fn)
655 {
656     XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
657 
658     bool deep = false;
659     if (fn.nargs > 0) deep = toBool(fn.arg(0), getVM(fn));
660 
661     as_object* newnode = ptr->cloneNode(deep)->object();
662     return as_value(newnode);
663 }
664 
665 
666 as_value
xmlnode_insertBefore(const fn_call & fn)667 xmlnode_insertBefore(const fn_call& fn)
668 {
669 	XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
670 
671 	if ( fn.nargs < 2 )
672 	{
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());
677 		);
678 		return as_value();
679 	}
680 
681 	XMLNode_as* newnode;
682 
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());
688 		);
689 		return as_value();
690 	}
691 
692 	XMLNode_as* pos;
693 
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());
699 		);
700 		return as_value();
701 	}
702 
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."));
707         );
708         return as_value();
709     }
710 
711     ptr->insertBefore(newnode, pos);
712     return as_value();
713 
714 }
715 
716 
717 as_value
xmlnode_getNamespaceForPrefix(const fn_call & fn)718 xmlnode_getNamespaceForPrefix(const fn_call& fn)
719 {
720     XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
721     if (!fn.nargs) {
722         return as_value();
723     }
724 
725     std::string ns;
726 
727     ptr->getNamespaceForPrefix(fn.arg(0).to_string(), ns);
728     if (ns.empty()) return as_value();
729     return as_value(ns);
730 }
731 
732 
733 as_value
xmlnode_getPrefixForNamespace(const fn_call & fn)734 xmlnode_getPrefixForNamespace(const fn_call& fn)
735 {
736     XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
737     if (!fn.nargs) {
738         return as_value();
739     }
740 
741     std::string prefix;
742 
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();
747     }
748     return as_value(prefix);
749 }
750 
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.
754 //
755 /// This standard namespace can only  be set during XML parsing and cannot
756 /// be changed or set using attributes.
757 //
758 /// Conversely, the similar getNamespaceForPrefix("") can be set and changed
759 /// through attributes.
760 as_value
xmlnode_namespaceURI(const fn_call & fn)761 xmlnode_namespaceURI(const fn_call& fn)
762 {
763     XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
764 
765     // Read-only property
766 
767     const std::string& name = ptr->nodeName();
768 
769     if (name.empty()) {
770         as_value null;
771         null.set_null();
772         return null;
773     }
774 
775     std::string prefix;
776     if (ptr->extractPrefix(prefix)) {
777         std::string ns;
778         ptr->getNamespaceForPrefix(prefix, ns);
779         return as_value(ns);
780     }
781 
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();
787     }
788     if (!node) return as_value("");
789 
790     return as_value(node->getNamespaceURI());
791 }
792 
793 
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
xmlnode_prefix(const fn_call & fn)798 xmlnode_prefix(const fn_call& fn)
799 {
800     XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
801 
802     // Read-only property
803 
804     if (ptr->nodeName().empty()) {
805         as_value null;
806         null.set_null();
807         return null;
808     }
809 
810     std::string prefix;
811     if (!ptr->extractPrefix(prefix)) return as_value("");
812     return as_value(prefix);
813 }
814 
815 
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
xmlnode_localName(const fn_call & fn)820 xmlnode_localName(const fn_call& fn)
821 {
822     XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
823 
824     // Read-only property
825 
826     if (ptr->nodeName().empty()) {
827         as_value null;
828         null.set_null();
829         return null;
830     }
831 
832     const std::string& nodeName = ptr->nodeName();
833     if (nodeName.empty()) return as_value("");
834 
835     std::string::size_type pos = nodeName.find(':');
836     if (pos == std::string::npos || pos == nodeName.size() - 1) {
837         return as_value(nodeName);
838     }
839 
840     return as_value(nodeName.substr(pos + 1));
841 }
842 
843 
844 as_value
xmlnode_removeNode(const fn_call & fn)845 xmlnode_removeNode(const fn_call& fn)
846 {
847     XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
848 
849     XMLNode_as* parent = ptr->getParent();
850     if (parent) parent->removeChild(ptr);
851     return as_value();
852 }
853 
854 
855 as_value
xmlnode_toString(const fn_call & fn)856 xmlnode_toString(const fn_call& fn)
857 {
858 
859     XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
860 
861     std::stringstream ss;
862     ptr->toString(ss);
863 
864     return as_value(ss.str());
865 }
866 
867 
868 as_value
xmlnode_hasChildNodes(const fn_call & fn)869 xmlnode_hasChildNodes(const fn_call& fn)
870 {
871     XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
872     return as_value(ptr->hasChildNodes());
873 }
874 
875 
876 as_value
xmlnode_nodeValue(const fn_call & fn)877 xmlnode_nodeValue(const fn_call& fn)
878 {
879     XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
880     as_value rv;
881     rv.set_null();
882 
883     if ( fn.nargs == 0 )
884     {
885         const std::string& val = ptr->nodeValue();
886         if ( ! val.empty() ) rv = val;
887     }
888     else
889     {
890         ptr->nodeValueSet(fn.arg(0).to_string());
891     }
892     return rv;
893 }
894 
895 
896 as_value
xmlnode_nodeName(const fn_call & fn)897 xmlnode_nodeName(const fn_call& fn)
898 {
899     XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
900     as_value rv;
901     rv.set_null();
902 
903     if (!fn.nargs) {
904         const std::string& val = ptr->nodeName();
905         if ( ! val.empty() ) rv = val;
906     }
907     else {
908         ptr->nodeNameSet(fn.arg(0).to_string());
909     }
910     return rv;
911 }
912 
913 
914 as_value
xmlnode_nodeType(const fn_call & fn)915 xmlnode_nodeType(const fn_call& fn)
916 {
917     XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
918     return as_value(ptr->nodeType());
919 }
920 
921 
922 as_value
xmlnode_attributes(const fn_call & fn)923 xmlnode_attributes(const fn_call& fn)
924 {
925     XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
926 
927     as_object* attrs = ptr->getAttributes();
928     if (attrs) return as_value(attrs);
929     return as_value();
930 }
931 
932 
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.
940 ///
941 as_value
xmlnode_firstChild(const fn_call & fn)942 xmlnode_firstChild(const fn_call& fn)
943 {
944     XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
945     as_value rv;
946     rv.set_null();
947 
948     XMLNode_as* node = ptr->firstChild();
949     if (node) {
950         rv = node->object();
951     }
952 
953     return rv;
954 }
955 
956 
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
xmlnode_lastChild(const fn_call & fn)964 xmlnode_lastChild(const fn_call& fn)
965 {
966     XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
967     as_value rv;
968     rv.set_null();
969 
970     XMLNode_as* node = ptr->lastChild();
971     if (node) {
972         rv = node->object();
973     }
974 
975     return rv;
976 }
977 
978 
979 as_value
xmlnode_nextSibling(const fn_call & fn)980 xmlnode_nextSibling(const fn_call& fn)
981 {
982     as_value rv;
983     rv.set_null();
984 
985     XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
986     XMLNode_as *node = ptr->nextSibling();
987     if (node) {
988         rv = node->object();
989     }
990     return rv;
991 }
992 
993 
994 as_value
xmlnode_previousSibling(const fn_call & fn)995 xmlnode_previousSibling(const fn_call& fn)
996 {
997     as_value rv;
998     rv.set_null();
999 
1000     XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
1001     XMLNode_as *node = ptr->previousSibling();
1002     if (node) {
1003         rv = node->object();
1004     }
1005     return rv;
1006 }
1007 
1008 
1009 as_value
xmlnode_parentNode(const fn_call & fn)1010 xmlnode_parentNode(const fn_call& fn)
1011 {
1012     as_value rv;
1013     rv.set_null();
1014 
1015     XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
1016     XMLNode_as* node = ptr->getParent();
1017     if (node) {
1018         rv = node->object();
1019     }
1020     return rv;
1021 }
1022 
1023 as_value
xmlnode_childNodes(const fn_call & fn)1024 xmlnode_childNodes(const fn_call& fn)
1025 {
1026     XMLNode_as* ptr = ensure<ThisIsNative<XMLNode_as> >(fn);
1027     return ptr->childNodes();
1028 }
1029 
1030 
1031 void
enumerateAttributes(const XMLNode_as & node,StringPairs & pairs)1032 enumerateAttributes(const XMLNode_as& node, StringPairs& pairs)
1033 {
1034     pairs.clear();
1035 
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()));
1045         }
1046     }
1047 
1048 }
1049 
1050 /// Return true if this attribute is a namespace specifier and the
1051 /// namespace matches.
1052 bool
namespaceMatches(const StringPairs::value_type & val,const std::string & ns)1053 namespaceMatches(const StringPairs::value_type& val,
1054         const std::string& ns)
1055 {
1056     StringNoCaseEqual noCaseCompare;
1057     return (noCaseCompare(val.first.substr(0, 5), "xmlns") &&
1058                 noCaseCompare(val.second, ns));
1059 }
1060 
1061 
1062 bool
prefixMatches(const StringPairs::value_type & val,const std::string & prefix)1063 prefixMatches(const StringPairs::value_type& val,
1064         const std::string& prefix)
1065 {
1066     const std::string& name = val.first;
1067     StringNoCaseEqual noCaseCompare;
1068 
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:");
1074     }
1075 
1076     if (!noCaseCompare(name.substr(0, 6), "xmlns:")) return false;
1077 
1078     return noCaseCompare(prefix, name.substr(6));
1079 }
1080 
1081 } // anonymous namespace
1082 } // gnash namespace
1083 // local Variables:
1084 // mode: C++
1085 // indent-tabs-mode: t
1086 // End:
1087 
1088