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