Files
GTASource/rage/scaleform/Src/GFxPlayer/XML/GFxASXmlNode.cpp

1686 lines
63 KiB
C++
Raw Normal View History

2025-02-23 17:40:52 +08:00
/**********************************************************************
Filename : GFxASXmlNode.cpp
Content : XMLNode reference class for ActionScript 2.0
Created : 11/30/2007
Authors : Prasad Silva
Copyright : (c) 2005-2007 Scaleform Corp. All Rights Reserved.
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE.
**********************************************************************/
//
// Known issues and notes:
//
// - Custom properties cannot be set and retrieved. The properties will
// be destroyed once the ref count goes to 0.
// - After appending a child that already has parent, the child's
// namespace is not resolved in Flash. GFx fixes this obvious 'bug'.
//
#include "GConfig.h"
#ifndef GFC_NO_XML_SUPPORT
#include "GFxPlayerImpl.h"
#include "GFxAction.h"
#include "XML/GFxXMLShadowRef.h"
#include "XML/GFxASXmlNode.h"
#include "AS/GASArrayObject.h"
#include "GHeapNew.h"
//
// Temporary descriptor of XMLNode properties
//
struct GASXmlNodeObject_MemberTableType
{
const char * pName;
GASXmlNodeObject::StandardMember Id;
};
//
// Mapping between const char* and standard member enum
//
GASXmlNodeObject_MemberTableType GASXmlNodeObject_MemberTable[] =
{
{ "attributes", GASXmlNodeObject::M_attributes },
{ "childNodes", GASXmlNodeObject::M_childNodes },
{ "firstChild", GASXmlNodeObject::M_firstChild },
{ "lastChild", GASXmlNodeObject::M_lastChild },
{ "localName", GASXmlNodeObject::M_localName },
{ "namespaceURI", GASXmlNodeObject::M_namespaceURI },
{ "nextSibling", GASXmlNodeObject::M_nextSibling },
{ "nodeName", GASXmlNodeObject::M_nodeName },
{ "nodeType", GASXmlNodeObject::M_nodeType },
{ "nodeValue", GASXmlNodeObject::M_nodeValue },
{ "parentNode", GASXmlNodeObject::M_parentNode },
{ "prefix", GASXmlNodeObject::M_prefix },
{ "previousSibling", GASXmlNodeObject::M_previousSibling },
// Done.
{ 0, GASXmlNodeObject::M_InvalidMember }
};
//
// Looks up an XMLNode standard member and returns M_InvalidMember if it is not found.
//
GASXmlNodeObject::StandardMember GASXmlNodeObject::GetStandardMemberConstant(GASEnvironment* penv,
const GASString& memberName) const
{
SByte memberConstant = M_InvalidMember;
if (memberName.IsStandardMember())
{
GASXmlNodeProto* xmlNodeProto =
static_cast<GASXmlNodeProto*>(penv->GetPrototype(GASBuiltin_XMLNode));
GASSERT(xmlNodeProto);
xmlNodeProto->XMLNodeMemberMap.GetCaseCheck(memberName, &memberConstant, true);
}
return (StandardMember) memberConstant;
}
//
// Initialize the XMLNode members in the global contexts
//
// This will be used in GetMember/SetMember for fast member comparison
//
void GASXmlNodeObject::InitializeStandardMembers(GASGlobalContext& gc, GASStringHash<SByte>& hash)
{
GCOMPILER_ASSERT( (sizeof(GASXmlNodeObject_MemberTable)/sizeof(GASXmlNodeObject_MemberTable[0]))
== M_XMLNodeMemberCount + 1);
// Add all standard members.
GASXmlNodeObject_MemberTableType* pentry;
GASStringManager* pstrManager = gc.GetStringManager();
hash.SetCapacity(M_XMLNodeMemberCount);
for (pentry = GASXmlNodeObject_MemberTable; pentry->pName; pentry++)
{
GASString name(pstrManager->CreateConstString(pentry->pName, strlen(pentry->pName),
GASString::Flag_StandardMember));
hash.Add(name, (SByte)pentry->Id);
}
}
//
// Setup the attributes of an element node. This method offloads any
// attributes defined in the C++ side to the AS side for correct behavior.
//
static void SetupAttributes(GASEnvironment* penv, GFxXMLElementNode* preal)
{
// Special logic to handle attributes
// Since attributes can hold object references as values, the functionality
// provided by the C++ DOM is inadaquete and not compatible. Therefore we will
// offload all attributes to the AS Object.
GFxXMLShadowRef* pshadow = preal->pShadow;
GASStringContext* psc = penv->GetSC();
pshadow->pAttributes = *GHEAP_NEW(penv->GetHeap()) GASObject(penv);
if (preal->HasAttributes())
{
// this code will execute once for unprocessed nodes
for (GFxXMLAttribute* attr = preal->FirstAttribute; attr != NULL; attr = attr->Next)
{
pshadow->pAttributes->SetMember(penv, psc->CreateString(attr->Name.ToCStr()),
psc->CreateString(attr->Value.ToCStr()));
}
preal->ClearAttributes();
}
}
//
// Setup the given AS xml node object as a shadow for a real node.
//
static void SetupShadow(GASEnvironment* penv, GFxXMLNode* preal, GASXmlNodeObject* asobj)
{
GASSERT(preal);
if (!preal->pShadow)
{
preal->pShadow = preal->MemoryManager->CreateShadowRef();
if (preal->Type == GFxXMLElementNodeType)
{
GFxXMLElementNode* pnode = static_cast<GFxXMLElementNode*>(preal);
SetupAttributes(penv, pnode);
}
}
GFxXMLShadowRef* pshadow = preal->pShadow;
pshadow->pASNode = asobj;
asobj->pRealNode = preal;
}
//
// Create an actionscript shadow for a real node (C++).
//
static GPtr<GASXmlNodeObject> CreateShadow(GASEnvironment* penv, GFxXMLNode* preal, GFxASXMLRootNode* proot)
{
GASSERT(preal);
GPtr<GASXmlNodeObject> asobj = *GHEAP_NEW(penv->GetHeap()) GASXmlNodeObject(penv);
SetupShadow(penv, preal, asobj);
// Setup root node
if (proot)
asobj->pRootNode = proot;
else
asobj->pRootNode = *preal->MemoryManager->CreateRootNode(preal);
return asobj;
}
//
// Fill the GASObject with id-name -> XMLNode mappings
//
void GFx_Xml_CreateIDMap(GASEnvironment* penv, GFxXMLElementNode* elemNode, GFxASXMLRootNode* proot, GASObject* pobj)
{
GFxXMLDOMStringCompareWrapper idstr("id", 2);
for (GFxXMLNode* child = elemNode->FirstChild; child != NULL; child = child->NextSibling)
{
if (child->Type == GFxXMLElementNodeType)
{
GFx_Xml_CreateIDMap(penv, (GFxXMLElementNode*)child, proot, pobj);
// Find attribute with id name
GFxXMLElementNode* pnode = static_cast<GFxXMLElementNode*>(child);
for (GFxXMLAttribute* attr = pnode->FirstAttribute; attr != NULL; attr = attr->Next)
{
if (attr->Name == idstr)
{
GFxString value(attr->Value.ToCStr());
GPtr<GASXmlNodeObject> pchildobj = NULL;
// pnode does not have a shadow; create one
if (NULL == pnode->pShadow)
pchildobj = CreateShadow(penv, pnode, proot);
else
{
// pnode does not have an AS counterpart
GFxXMLShadowRef* pshadow = pnode->pShadow;
pchildobj = pshadow->pASNode;
if (NULL == pchildobj.GetPtr())
{
pchildobj = *GHEAP_NEW(penv->GetHeap()) GASXmlNodeObject(penv);
SetupShadow(penv, pnode, pchildobj);
}
}
pobj->SetMember(penv, penv->CreateString(value),
GASValue(pchildobj));
// Break on first attribute matched
break;
}
}
}
}
}
//
// Resolve the namespace of the given element node. This method first looks
// at the attributes of the node itself to determine a namespace declaration.
// If a matching namespace is not found, then it traverses its parent chain to
// find one.
//
static void ResolveNamespace(GASEnvironment* penv, GFxXMLElementNode* elemNode, GFxASXMLRootNode* proot)
{
GASStringContext* psc = penv->GetSC();
// Create the property query
GASString pfxquery = psc->CreateString("xmlns", 5);
if (elemNode->Prefix.GetSize() > 0)
{
pfxquery += ":";
pfxquery += elemNode->Prefix.ToCStr();
}
GASValue qval(GASValue::UNDEFINED);
// -- Reset the namespace
GASSERT(elemNode);
elemNode->Namespace = elemNode->MemoryManager->EmptyString();
// -- Look for namespace declaration in self first
GASSERT(elemNode->pShadow);
GFxXMLShadowRef* pshadow = elemNode->pShadow;
GASSERT(pshadow);
GASObject* pattribs = pshadow->pAttributes;
GASSERT(pattribs);
pattribs->GetMember(penv, pfxquery, &qval);
if (!qval.IsUndefined())
{
// Found ns declaration in self; bail
GASString ns = qval.ToString(penv);
elemNode->Namespace = elemNode->MemoryManager->CreateString(ns.ToCStr(),
ns.GetSize());
return;
}
// -- Traverse up parent chain and look for a namespace
GFxXMLElementNode* parent = static_cast<GFxXMLElementNode*>(elemNode->Parent);
while (parent)
{
// create a shadow if one does not exist
if (NULL == parent->pShadow)
CreateShadow(penv, parent, proot);
pshadow = parent->pShadow;
GASSERT(pshadow);
GASObject* pattribs = pshadow->pAttributes;
GASSERT(pattribs);
// Lookup prefix in properties
pattribs->GetMember(penv, pfxquery, &qval);
if (!qval.IsUndefined())
{
GASString ns = qval.ToString(penv);
elemNode->Namespace = elemNode->MemoryManager->CreateString(ns.ToCStr(),
ns.GetSize());
break;
}
parent = static_cast<GFxXMLElementNode*>(parent->Parent);
}
}
//
// Constructor
//
GASXmlNodeObject::GASXmlNodeObject(GASEnvironment* penv)
: GASObject(penv), pRealNode(NULL)
{
Set__proto__(penv->GetSC(), penv->GetPrototype(GASBuiltin_XMLNode));
}
//
// Destructor
//
GASXmlNodeObject::~GASXmlNodeObject()
{
// Remove itself as the realnode's (C++) shadow reference
if (pRealNode && pRealNode->pShadow)
{
GFxXMLShadowRef* pshadow = pRealNode->pShadow;
pshadow->pASNode = NULL;
}
}
//
// Overloaded SetMember function to intercept and process builtin properties
// Since the actual data is stored in the realnode, each property needs to be
// intercepted and processed individually.
//
bool GASXmlNodeObject::SetMember(GASEnvironment* penv, const GASString& name,
const GASValue& val,
const GASPropFlags& flags)
{
GFxLog* log = penv->GetLog();
// Try to handle this property
if (pRealNode)
{
StandardMember member = GetStandardMemberConstant(penv, name);
switch (member)
{
// ***** XMLNode.nodeName
case M_nodeName:
if (pRealNode)
{
// Flash Doc: If the XML object is an XML element (nodeType == 1), nodeName is
// the name of the tag that represents the node in the XML file. For example,
// TITLE is the nodeName of an HTML TITLE tag. If the XML object is a text node
// (nodeType == 3), nodeName is null.
//
if (pRealNode->Type == GFxXMLElementNodeType)
{
GFxXMLElementNode* elemNode = static_cast<GFxXMLElementNode*>(pRealNode);
// -- Look for prefix declaration, if one exists in node name
GFxXMLDOMString prefix( pRealNode->MemoryManager->EmptyString() );
GASString str = val.ToString(penv);
const char* data = str.ToCStr();
const char* colon = strchr(data, ':');
if (colon)
{
// Check prefix
prefix = pRealNode->MemoryManager->CreateString(data, colon-data);
// Set the localname
pRealNode->Value = pRealNode->MemoryManager->CreateString(
colon+1, G_strlen(colon+1));
}
else
{
// If node already contains default namespace, it is preserved.
pRealNode->Value = pRealNode->MemoryManager->CreateString(str.ToCStr(),
str.GetSize());
}
elemNode->Prefix = prefix;
ResolveNamespace(penv, elemNode, pRootNode);
}
else
{
if (log != NULL)
log->LogScriptWarning("Warning: XMLNodeObject::SetMember"
" - cannot set nodeName of node type %d. Only type 1 allowed\n",
pRealNode->Type);
}
}
else
{
if (log != NULL)
log->LogScriptWarning("Warning: XMLNodeObject::SetMember"
" - cannot set nodeName of a malformed node\n");
}
return true;
// ***** XMLNode.nodeValue
case M_nodeValue:
if (pRealNode && pRealNode->Type != GFxXMLElementNodeType)
{
// Flash Doc: If the XML object is a text node, the nodeType is 3, and the nodeValue
// is the text of the node. If the XML object is an XML element (nodeType is 1),
// nodeValue is null and read-only
//
GASString str = val.ToString(penv);
pRealNode->Value = pRealNode->MemoryManager->CreateString(str.ToCStr(),
str.GetSize());
}
else
{
if (log != NULL)
log->LogScriptWarning("Warning! XMLNodeObject::SetMember"
" - cannot set nodeValue of a malformed node\n");
}
return true;
// ***** XMLNode.attributes
case M_attributes:
if (pRealNode)
{
// Flash Doc: The XML.attributes object contains one variable for each attribute
// of the XML instance. Because these variables are defined as part of the
// object, they are generally referred to as properties of the object. The value
// of each attribute is stored in the corresponding property as a string.
//
if (pRealNode->Type == GFxXMLElementNodeType)
{
GASSERT(pRealNode);
GFxXMLShadowRef* pshadow = pRealNode->pShadow;
GASSERT(pshadow);
pshadow->pAttributes = val.ToObject(penv);
}
else
{
if (log != NULL)
log->LogScriptWarning("Warning: XMLNodeObject::SetMember"
" - cannot set attributes of node type %d. Only type 1 allowed\n",
pRealNode->Type);
}
}
else
{
if (log != NULL)
log->LogScriptWarning("Warning: XMLNodeObject::SetMember"
" - cannot set attributes of a malformed node\n");
}
return true;
default:
{
// Pass along to GASObject
}
}
}
return GASObject::SetMember(penv, name, val, flags);
}
//
// Overloaded GetMember function to intercept and process builtin properties
// Since the actual data is stored in the realnode, each property needs to be
// intercepted and processed individually.
//
bool GASXmlNodeObject::GetMember(GASEnvironment* penv, const GASString& name, GASValue* val)
{
GASStringContext* psc = penv->GetSC();
// Try to handle this property
if (pRealNode)
{
StandardMember member = GetStandardMemberConstant(penv, name);
switch (member)
{
// ***** XMLNode.firstChild
case M_firstChild:
if (pRealNode)
{
if (pRealNode->Type == GFxXMLElementNodeType)
{
// Flash Doc: This property is null if the node does not have
// children. This property is undefined if the node is a text node.
// This is a read-only property and cannot be used to manipulate
// child nodes; use the appendChild(), insertBefore(), and
// removeNode() methods to manipulate child nodes.
//
GFxXMLElementNode* pnode = static_cast<GFxXMLElementNode*>(pRealNode);
if (pnode->HasChildren())
{
GFxXMLShadowRef* pshadow = pnode->FirstChild->pShadow;
if (NULL == pshadow || NULL == pshadow->pASNode)
val->SetAsObject(CreateShadow(penv, pnode->FirstChild, pRootNode));
else
val->SetAsObject(static_cast<GASObject*>(pshadow->pASNode));
}
else
val->SetNull();
}
else
val->SetNull();
}
else
val->SetUndefined();
return true;
// ***** XMLNode.nextSibling
case M_nextSibling:
if (pRealNode)
{
// Flash Doc: This property is null if the node does not have
// a next sibling node. This property cannot be used to manipulate
// child nodes; use the appendChild(), insertBefore(), and
// removeNode() methods to manipulate child nodes.
//
GFxXMLNode* pnode = static_cast<GFxXMLNode*>(pRealNode);
if (pnode->NextSibling)
{
GFxXMLShadowRef* pshadow = pnode->NextSibling->pShadow;
if (NULL == pshadow || NULL == pshadow->pASNode)
val->SetAsObject(CreateShadow(penv, pnode->NextSibling, pRootNode));
else
val->SetAsObject(static_cast<GASObject*>(pshadow->pASNode));
}
else
val->SetNull();
}
else
val->SetUndefined();
return true;
// ***** XMLNode.childNodes
case M_childNodes:
if (pRealNode && pRealNode->Type == GFxXMLElementNodeType)
{
// Flash Doc: Each element in the array is a reference to an XML object
// that represents a child node. This is a read-only property and cannot
// be used to manipulate child nodes. Use the appendChild(), insertBefore(),
// and removeNode() methods to manipulate child nodes. This property is
// undefined for text nodes (nodeType == 3).
//
// fill array with children
GPtr<GASArrayObject> parr = *GHEAP_NEW(penv->GetHeap()) GASArrayObject(penv);
GFxXMLElementNode* pnode = static_cast<GFxXMLElementNode*>(pRealNode);
for (GFxXMLNode* child = pnode->FirstChild; child != NULL;
child = child->NextSibling)
{
GFxXMLShadowRef* pshadow = child->pShadow;
if (NULL == pshadow || NULL == pshadow->pASNode)
parr->PushBack(GASValue(CreateShadow(penv, child, pRootNode)));
else
parr->PushBack( pshadow->pASNode );
}
val->SetAsObject(parr);
}
else
val->SetUndefined();
return true;
// ***** XMLNode.nodeName
case M_nodeName:
if (pRealNode)
{
// Flash Doc: If the XML object is an XML element (nodeType == 1), nodeName is
// the name of the tag that represents the node in the XML file. For example,
// TITLE is the nodeName of an HTML TITLE tag. If the XML object is a text node
// (nodeType == 3), nodeName is null.
//
if (pRealNode->Type == GFxXMLElementNodeType)
{
GFxXMLElementNode* pelem = static_cast<GFxXMLElementNode*>(pRealNode);
if (pelem->Prefix.GetSize() > 0)
{
GASString str = psc->CreateString(pelem->Prefix.ToCStr());
//GASString str = psc->CreateConstString(pelem->Prefix.ToCStr());
str += ":";
str += pelem->Value.ToCStr();
val->SetString(str);
}
else
{
val->SetString(psc->CreateString(pelem->Value.ToCStr()));
//val->SetString(psc->CreateConstString(pelem->Value.ToCStr()));
}
}
else
val->SetNull();
}
else
val->SetUndefined();
return true;
// ***** XMLNode.nodeValue
case M_nodeValue:
if (pRealNode)
{
// Flash Doc: If the XML object is a text node, the nodeType is 3, and the nodeValue
// is the text of the node. If the XML object is an XML element (nodeType is 1),
// nodeValue is null and read-only
//
if (pRealNode->Type == GFxXMLElementNodeType)
val->SetNull();
else
val->SetString(psc->CreateString(pRealNode->Value.ToCStr()));
//val->SetString(psc->CreateConstString(pRealNode->Value.ToCStr()));
}
else
val->SetUndefined();
return true;
// ***** XMLNode.attributes
case M_attributes:
if (pRealNode)
{
// Flash Doc: The XML.attributes object contains one variable for each attribute
// of the XML instance. Because these variables are defined as part of the
// object, they are generally referred to as properties of the object. The value
// of each attribute is stored in the corresponding property as a string.
//
GASSERT(pRealNode);
GFxXMLShadowRef* pshadow = pRealNode->pShadow;
GASSERT(pshadow);
if (NULL == pshadow->pAttributes.GetPtr())
pshadow->pAttributes = *GHEAP_NEW(penv->GetHeap()) GASObject(penv);
val->SetAsObject(pshadow->pAttributes);
}
else
val->SetUndefined();
return true;
// ***** XMLNode.lastChild
case M_lastChild:
if (pRealNode)
{
if (pRealNode->Type == GFxXMLElementNodeType)
{
// Flash Doc: The XML.lastChild property is null if the node
// does not have children. This property cannot be used to
// manipulate child nodes; use the appendChild(), insertBefore(),
// and removeNode() methods to manipulate child nodes.
//
GFxXMLElementNode* pnode = static_cast<GFxXMLElementNode*>(pRealNode);
if (pnode->HasChildren())
{
GFxXMLShadowRef* pshadow = pnode->LastChild->pShadow;
if (NULL == pshadow || NULL == pshadow->pASNode)
val->SetAsObject(CreateShadow(penv, pnode->LastChild, pRootNode));
else
val->SetAsObject(static_cast<GASObject*>(pshadow->pASNode));
}
else
val->SetNull();
}
else
val->SetNull();
}
else
val->SetUndefined();
return true;
// ***** XMLNode.previousSibling
case M_previousSibling:
if (pRealNode)
{
// Flash Doc: The property has a value of null if the node does not
// have a previous sibling node. This property cannot be used to
// manipulate child nodes; use the appendChild(), insertBefore(),
// and removeNode() methods to manipulate child nodes.
//
GFxXMLNode* pnode = static_cast<GFxXMLNode*>(pRealNode);
if (pnode->PrevSibling)
{
GFxXMLShadowRef* pshadow = pnode->PrevSibling->pShadow;
if (NULL == pshadow || NULL == pshadow->pASNode)
val->SetAsObject(CreateShadow(penv, pnode->PrevSibling, pRootNode));
else
val->SetAsObject(static_cast<GASObject*>(pshadow->pASNode));
}
else
val->SetNull();
}
else
val->SetUndefined();
return true;
// ***** XMLNode.parentNode
case M_parentNode:
if (pRealNode)
{
// Flash Doc: An XMLNode value that references the parent node of
// the specified XML object, or returns null if the node has no
// parent. This is a read-only property and cannot be used to
// manipulate child nodes; use the appendChild(), insertBefore(),
// and removeNode() methods to manipulate child nodes.
//
GFxXMLNode* pnode = static_cast<GFxXMLNode*>(pRealNode);
if (pnode->Parent)
{
GFxXMLShadowRef* pshadow = pnode->Parent->pShadow;
if (NULL == pshadow || NULL == pshadow->pASNode)
val->SetAsObject(CreateShadow(penv, pnode->Parent, pRootNode));
else
val->SetAsObject(static_cast<GASObject*>(pshadow->pASNode));
}
else
val->SetNull();
}
else
val->SetUndefined();
return true;
// ***** XMLNode.localName
case M_localName:
if (pRealNode && pRealNode->Type == GFxXMLElementNodeType)
{
// Flash Doc: This is the element name without the namespace
// prefix. For example, the node
// <contact:mailbox/>bob@example.com</contact:mailbox> has the
// local name "mailbox", and the prefix "contact", which comprise
// the full element name "contact.mailbox". You can access the
// namespace prefix via the prefix property of the XML node object.
// The nodeName property returns the full name (including the
// prefix and the local name).
//
val->SetString(psc->CreateString(pRealNode->Value.ToCStr()));
//val->SetString(psc->CreateConstString(pRealNode->Value.ToCStr()));
}
else
val->SetUndefined();
return true;
// ***** XMLNode.namespaceURI
case M_namespaceURI:
if (pRealNode && pRealNode->Type == GFxXMLElementNodeType)
{
// Flash Doc: If the XML node has a prefix, namespaceURI is the
// value of the xmlns declaration for that prefix (the URI),
// which is typically called the namespace URI. The xmlns
// declaration is in the current node or in a node higher in
// the XML hierarchy. If the XML node does not have a prefix,
// the value of the namespaceURI property depends on whether
// there is a default namespace defined (as in
// xmlns="http://www.example.com/"). If there is a default
// namespace, the value of the namespaceURI property is the
// value of the default namespace. If there is no default
// namespace, the namespaceURI property for that node is an
// empty string (""). You can use the getNamespaceForPrefix()
// method to identify the namespace associated with a specific
// prefix. The namespaceURI* [*PPS: this should be 'prefix']
// property returns the prefix associated with the node name.
//
GFxXMLElementNode* pnode = static_cast<GFxXMLElementNode*>(pRealNode);
val->SetString(psc->CreateString(pnode->Namespace.ToCStr()));
//val->SetString(psc->CreateConstString(pnode->Namespace.ToCStr()));
}
else
val->SetUndefined();
return true;
// ***** XMLNode.prefix
case M_prefix:
if (pRealNode && pRealNode->Type == GFxXMLElementNodeType)
{
// Flash Doc: For example, the node
// <contact:mailbox/>bob@example.com</contact:mailbox>
// prefix "contact" and the local name "mailbox", which comprise
// the full element name "contact.mailbox". The nodeName property
// of an XML node object returns the full name (including the
// prefix and the local name). You can access the local name
// portion of the element's name via the localName property.
//
GFxXMLElementNode* pnode = static_cast<GFxXMLElementNode*>(pRealNode);
val->SetString(psc->CreateString(pnode->Prefix.ToCStr()));
//val->SetString(psc->CreateConstString(pnode->Prefix.ToCStr()));
}
else
val->SetUndefined();
return true;
// ***** XMLNode.nodeType
case M_nodeType:
// Flash Doc: A nodeType value, either 1 for an XML element or 3 for a text node.
// The nodeType is a numeric value from the NodeType enumeration in the W3C DOM
// Level 1 recommendation:
// www.w3.org/tr/1998/REC-DOM-Level-1-19981001/level-one-core.html.
//
if (pRealNode)
val->SetNumber(pRealNode->Type);
else
val->SetUndefined();
return true;
default:
{
// Pass along to GASObject
}
}
}
// if property wasn't gobbled up by the custom handler, pass it along to the base
// class
return GASObject::GetMember(penv, name, val);
}
//
// AS to GFx function mapping
//
const GASNameFunction GASXmlNodeProto::FunctionTable[] =
{
{ "appendChild", &GASXmlNodeProto::AppendChild },
{ "cloneNode", &GASXmlNodeProto::CloneNode },
{ "getNamespaceForPrefix", &GASXmlNodeProto::GetNamespaceForPrefix },
{ "getPrefixForNamespace", &GASXmlNodeProto::GetPrefixForNamespace },
{ "hasChildNodes", &GASXmlNodeProto::HasChildNodes },
{ "insertBefore", &GASXmlNodeProto::InsertBefore },
{ "removeNode", &GASXmlNodeProto::RemoveNode },
{ "toString", &GASXmlNodeProto::ToString },
{ 0, 0 }
};
//
// GASXmlNode prototype constructor
//
GASXmlNodeProto::GASXmlNodeProto(GASStringContext *psc, GASObject* prototype,
const GASFunctionRef& constructor) :
GASPrototype<GASXmlNodeObject>(psc, prototype, constructor)
{
// make the functions enumerable
InitFunctionMembers(psc, FunctionTable, GASPropFlags::PropFlag_ReadOnly | GASPropFlags::PropFlag_DontDelete);
GASXmlNodeObject::SetMemberRaw(psc, psc->CreateConstString("attributes"),
GASValue::UNDEFINED, GASPropFlags::PropFlag_DontDelete);
GASXmlNodeObject::SetMemberRaw(psc, psc->CreateConstString("childNodes"),
GASValue::UNDEFINED, GASPropFlags::PropFlag_DontDelete |
GASPropFlags::PropFlag_ReadOnly);
GASXmlNodeObject::SetMemberRaw(psc, psc->CreateConstString("firstChild"),
GASValue::UNDEFINED, GASPropFlags::PropFlag_DontDelete |
GASPropFlags::PropFlag_ReadOnly);
GASXmlNodeObject::SetMemberRaw(psc, psc->CreateConstString("lastChild"),
GASValue::UNDEFINED, GASPropFlags::PropFlag_DontDelete |
GASPropFlags::PropFlag_ReadOnly);
GASXmlNodeObject::SetMemberRaw(psc, psc->CreateConstString("localName"),
GASValue::UNDEFINED, GASPropFlags::PropFlag_DontDelete |
GASPropFlags::PropFlag_ReadOnly);
GASXmlNodeObject::SetMemberRaw(psc, psc->CreateConstString("namespaceURI"),
GASValue::UNDEFINED, GASPropFlags::PropFlag_DontDelete |
GASPropFlags::PropFlag_ReadOnly);
GASXmlNodeObject::SetMemberRaw(psc, psc->CreateConstString("nextSibling"),
GASValue::UNDEFINED, GASPropFlags::PropFlag_DontDelete |
GASPropFlags::PropFlag_ReadOnly);
GASXmlNodeObject::SetMemberRaw(psc, psc->CreateConstString("nodeName"),
GASValue::UNDEFINED, GASPropFlags::PropFlag_DontDelete);
GASXmlNodeObject::SetMemberRaw(psc, psc->CreateConstString("nodeType"),
GASValue::UNDEFINED, GASPropFlags::PropFlag_DontDelete |
GASPropFlags::PropFlag_ReadOnly);
GASXmlNodeObject::SetMemberRaw(psc, psc->CreateConstString("nodeValue"),
GASValue::UNDEFINED, GASPropFlags::PropFlag_DontDelete);
GASXmlNodeObject::SetMemberRaw(psc, psc->CreateConstString("parentNode"),
GASValue::UNDEFINED, GASPropFlags::PropFlag_DontDelete |
GASPropFlags::PropFlag_ReadOnly);
GASXmlNodeObject::SetMemberRaw(psc, psc->CreateConstString("prefix"),
GASValue::UNDEFINED, GASPropFlags::PropFlag_DontDelete |
GASPropFlags::PropFlag_ReadOnly);
GASXmlNodeObject::SetMemberRaw(psc, psc->CreateConstString("previousSibling"),
GASValue::UNDEFINED, GASPropFlags::PropFlag_DontDelete |
GASPropFlags::PropFlag_ReadOnly);
}
//
// XMLNode.appendChild(newChild:XMLNode) : Void
//
void GASXmlNodeProto::AppendChild(const GASFnCall& fn)
{
// Flash Doc: Appends the specified node to the XML object's child list. This method
// operates directly on the node referenced by the childNode parameter; it does not
// append a copy of the node. If the node to be appended already exists in another
// tree structure, appending the node to the new location will remove it from its
// current location. If the childNode parameter refers to a node that already exists
// in another XML tree structure, the appended child node is placed in the new tree
// structure after it is removed from its existing parent node.
//
CHECK_THIS_PTR2(fn, XMLNode, XML);
GASXmlNodeObject* pthis = (GASXmlNodeObject*)fn.ThisPtr;
GASSERT(pthis);
if (!pthis) return;
GFxLog* log = fn.GetLog();
if (pthis->pRealNode)
{
if (pthis->pRealNode->Type == GFxXMLElementNodeType)
{
// Only works for element nodes
GFxXMLElementNode* elemNode = (GFxXMLElementNode*) pthis->pRealNode;
// Get new child
if (fn.NArgs > 0)
{
GASObject* p = fn.Arg(0).ToObject(fn.Env);
if ( NULL == p || p->GetObjectType() != Object_XMLNode )
{
if (log != NULL)
log->LogScriptWarning("Warning! XMLNode::appendChild"
" - trying to add a child that is not of type XMLNode\n");
// Do nothing (not an XMLNode object)
return;
}
GASXmlNodeObject* pchild = static_cast<GASXmlNodeObject*>(p);
if (!pchild->pRealNode)
{
// Do nothing (malformed object)
return;
}
// Check to see if child is parent of this node
// If so, it is a circular reference
GFxXMLNode* ptopmost = pthis->pRealNode->Parent;
while ( ptopmost && ptopmost->Parent != NULL)
{
ptopmost = ptopmost->Parent;
}
if (ptopmost == pchild->pRealNode)
{
// If child contains this node, then circular reference
// if appended.
if (log != NULL)
log->LogScriptWarning("Warning! XMLNode::appendChild"
" - trying to add a child that is the root of the current tree\n");
return;
}
// Keep a ref here else subtree will be nuked
GPtr<GFxXMLNode> selfptr = pchild->pRealNode;
// Check if child already has a parent.
if (pchild->pRealNode->Parent)
{
// Flash removes the child from its tree and allows
// it to be appended to the new tree
pchild->pRealNode->Parent->RemoveChild(pchild->pRealNode);
}
elemNode->AppendChild(pchild->pRealNode);
// Setup root node
pchild->pRootNode = pthis->pRootNode;
// Resolve floating prefix of child
if (pchild->pRealNode->Type == GFxXMLElementNodeType)
{
GFxXMLElementNode* elemChild =
static_cast<GFxXMLElementNode*>(pchild->pRealNode);
// [PPS] Flash appendChild does not resolve namespace, if namespace
// is already assigned to the node
if (elemChild->Namespace.GetSize() == 0)
{
ResolveNamespace(fn.Env, elemChild, pthis->pRootNode);
}
}
}
}
else
{
// Adding to text nodes is non-deterministic behavior
if (log != NULL)
log->LogScriptWarning("Warning: XMLNode::appendChild"
" - trying to add a child to a text node\n");
}
}
}
//
// XMLNode.cloneNode(deep:Boolean) : XMLNode
//
void GASXmlNodeProto::CloneNode(const GASFnCall& fn)
{
// Flash Doc: Constructs and returns a new XML node of the same type,
// name, value, and attributes as the specified XML object. If deep is
// set to true, all child nodes are recursively cloned, resulting in an
// exact copy of the original object's document tree. The clone of the
// node that is returned is no longer associated with the tree of the
// cloned item. Consequently, nextSibling, parentNode, and previousSibling
// all have a value of null. If the deep parameter is set to false, or
// the my_xml node has no child nodes, firstChild and lastChild are also
// null.
//
CHECK_THIS_PTR2(fn, XMLNode, XML);
GASXmlNodeObject* pthis = (GASXmlNodeObject*)fn.ThisPtr;
GASSERT(pthis);
if (!pthis) return;
if (pthis->pRealNode)
{
bool deepCopy = false;
if ( fn.NArgs > 0 )
deepCopy = fn.Arg(0).ToBool(fn.Env);
if (pthis->pRealNode->Type == GFxXMLElementNodeType)
{
// Set the real node
GPtr<GFxXMLNode> realclone = *pthis->pRealNode->Clone(deepCopy);
// Make a copy of the AS node
GPtr<GASXmlNodeObject> clone = CreateShadow(fn.Env, realclone, NULL);
// Set return value
fn.Result->SetAsObject(clone);
}
else
{
// Text nodes and unknown nodes have no deep copy semantics
GFxXMLTextNode* textNode = (GFxXMLTextNode*) pthis->pRealNode;
// Set the real node
GPtr<GFxXMLNode> realclone = *pthis->pRealNode->Clone(deepCopy);
// Make a copy of the AS node
GPtr<GASXmlNodeObject> clone = CreateShadow(fn.Env, realclone, NULL);
// Set type since node may not be text node
realclone->Type = textNode->Type;
// Set return value
fn.Result->SetAsObject(clone);
}
}
}
//
// XMLNode.getNamespaceForPrefix(prefix:String) : String
//
void GASXmlNodeProto::GetNamespaceForPrefix(const GASFnCall& fn)
{
// Flash Doc: Returns the namespace URI that is associated with the specified
// prefix for the node. To determine the URI, getPrefixForNamespace() searches up
// the XML hierarchy from the node, as necessary, and returns the namespace URI
// of the first xmlns declaration for the given prefix. If no namespace is defined
// for the specified prefix, the method returns null. If you specify an empty string
// ("") as the prefix and there is a default namespace defined for the node (as
// in xmlns="http://www.example.com/"), the method returns that default namespace
// URI.
CHECK_THIS_PTR2(fn, XMLNode, XML);
GASXmlNodeObject* pthis = (GASXmlNodeObject*)fn.ThisPtr;
GASSERT(pthis);
if (!pthis) return;
fn.Result->SetNull();
GFxLog* log = fn.GetLog();
if (pthis->pRealNode)
{
if (pthis->pRealNode->Type == GFxXMLElementNodeType)
{
GFxXMLElementNode* elemNode =
(GFxXMLElementNode*) pthis->pRealNode;
// Get query string
if (fn.NArgs < 1)
return;
GASString str = fn.Arg(0).ToString(fn.Env);
// Create the property query
GASString pfxquery = fn.Env->GetSC()->CreateString("xmlns", 5);
if (str.GetSize() > 0)
{
pfxquery += ":";
pfxquery += str.ToCStr();
}
GASValue qval(GASValue::UNDEFINED);
// -- Look for namespace declaration in self first
GASSERT(elemNode);
GASSERT(elemNode->pShadow);
GFxXMLShadowRef* pshadow = elemNode->pShadow;
GASSERT(pshadow);
GASObject* pattribs = pshadow->pAttributes;
GASSERT(pattribs);
pattribs->GetMember(fn.Env, pfxquery, &qval);
if (!qval.IsUndefined())
{
fn.Result->SetString(qval.ToString(fn.Env));
return;
}
// -- Traverse up parent chain and look for a namespace
GFxXMLElementNode* parent =
static_cast<GFxXMLElementNode*>(elemNode->Parent);
while (parent)
{
// Create a shadow if one does not exist
if (NULL == parent->pShadow)
CreateShadow(fn.Env, parent, pthis->pRootNode);
pshadow = parent->pShadow;
GASSERT(pshadow);
pattribs = pshadow->pAttributes;
GASSERT(pattribs);
// lookup prefix in properties
pattribs->GetMember(fn.Env, pfxquery, &qval);
if (!qval.IsUndefined())
{
fn.Result->SetString(qval.ToString(fn.Env));
return;
}
parent = static_cast<GFxXMLElementNode*>(parent->Parent);
}
}
else
{
if (log != NULL)
log->LogScriptWarning("Warning: XMLNodeProto::"
"GetNamespaceForPrefix - only element nodes support this method.\n");
}
}
}
//
// Member visitor to traverse the attributes.
// This is required because the namespace declarations are in the
// attributes.
//
struct XMLPrefixQuerier : public GASObject::MemberVisitor
{
GASEnvironment* pEnv;
GASString &pKey;
GASValue &pVal;
XMLPrefixQuerier(GASEnvironment* penv, GASString& pkey, GASValue& pval)
: pEnv(penv), pKey(pkey), pVal(pval) {}
XMLPrefixQuerier& operator=( const XMLPrefixQuerier& x )
{
pEnv = x.pEnv;
pKey = x.pKey;
pVal = x.pVal;
return *this;
}
void Visit(const GASString& name, const GASValue& val, UByte flags)
{
GUNUSED(flags);
GASString valstr = val.ToString(pEnv);
if (valstr.Compare_CaseCheck(pKey, true) &&
!strncmp(name.ToCStr(), "xmlns", 5))
{
pVal.SetString(name);
}
}
};
//
// XMLNode.getPrefixForNamespace(nsURI:String) : String
//
void GASXmlNodeProto::GetPrefixForNamespace(const GASFnCall& fn)
{
// Flash Doc: Returns the prefix that is associated with the specified
// namespace URI for the node. To determine the prefix,
// getPrefixForNamespace() searches up the XML hierarchy from the node,
// as necessary, and returns the prefix of the first xmlns declaration
// with a namespace URI that matches nsURI. If there is no xmlns
// assignment for the given URI, the method returns null. If there is an
// xmlns assignment for the given URI but no prefix is associated with the
// assignment, the method returns an empty string ("").
CHECK_THIS_PTR2(fn, XMLNode, XML);
GASXmlNodeObject* pthis = (GASXmlNodeObject*)fn.ThisPtr;
GASSERT(pthis);
if (!pthis) return;
fn.Result->SetNull();
GASStringContext* psc = fn.Env->GetSC();
GFxLog* log = fn.GetLog();
if (pthis->pRealNode)
{
if (pthis->pRealNode->Type == GFxXMLElementNodeType)
{
GFxXMLElementNode* elemNode =
(GFxXMLElementNode*) pthis->pRealNode;
// Get query string
if (fn.NArgs < 1)
return;
GASString str = fn.Arg(0).ToString(fn.Env);
GASValue qval(GASValue::UNDEFINED);
XMLPrefixQuerier pq(fn.Env, str, qval);
// -- Look for prefix declaration in self first
GASSERT(elemNode);
GASSERT(elemNode->pShadow);
GFxXMLShadowRef* pshadow = elemNode->pShadow;
GASSERT(pshadow);
GASObject* pattribs = pshadow->pAttributes;
GASSERT(pattribs);
pattribs->VisitMembers(psc, &pq, 0);
if (!qval.IsUndefined())
{
fn.Result->SetString(qval.ToString(fn.Env));
}
// Get root node ref
// -- Traverse up parent chain and look for a namespace
GFxXMLElementNode* parent =
static_cast<GFxXMLElementNode*>(elemNode->Parent);
while (fn.Result->IsNull() && parent)
{
// Create a shadow if one does not exist
if (NULL == parent->pShadow)
CreateShadow(fn.Env, parent, pthis->pRootNode);
pshadow = parent->pShadow;
GASSERT(pshadow);
pattribs = pshadow->pAttributes;
GASSERT(pattribs);
// lookup prefix in properties
pattribs->VisitMembers(psc, &pq, 0);
if (!qval.IsUndefined())
{
fn.Result->SetString(qval.ToString(fn.Env));
}
parent = static_cast<GFxXMLElementNode*>(parent->Parent);
}
if (!fn.Result->IsNull())
{
// -- Remove the xmlns part
GASString pfx = fn.Result->ToString(fn.Env);
const char* data = pfx.ToCStr();
const char* colon = strchr(data, ':');
// Bounds checking is not needed because the string is at least 6 chars if
// there is a colon and at least 5 chars without. this is guaranteed by the
// member visitor constraint.
if (colon)
fn.Result->SetString(psc->CreateString(data+6, pfx.GetSize()-6));
else
fn.Result->SetString(psc->CreateString(data+5, pfx.GetSize()-5));
}
}
else
{
if (log != NULL)
log->LogScriptWarning("Warning: XMLNodeProto::"
"GetNamespaceForPrefix - only element nodes support this method.\n");
}
}
}
//
// XMLNode.hasChildNodes() : Boolean
//
void GASXmlNodeProto::HasChildNodes(const GASFnCall& fn)
{
CHECK_THIS_PTR2(fn, XMLNode, XML);
GASXmlNodeObject* pthis = (GASXmlNodeObject*)fn.ThisPtr;
GASSERT(pthis);
if (!pthis) return;
fn.Result->SetBool(false);
if (pthis->pRealNode)
{
if (pthis->pRealNode->Type == GFxXMLElementNodeType)
{
GFxXMLElementNode* elemNode = (GFxXMLElementNode*) pthis->pRealNode;
fn.Result->SetBool(elemNode->HasChildren());
}
}
}
//
// XMLNode.insertBefore(newChild:XMLNode, insertPoint:XMLNode) : Void
//
void GASXmlNodeProto::InsertBefore(const GASFnCall& fn)
{
CHECK_THIS_PTR2(fn, XMLNode, XML);
GASXmlNodeObject* pthis = (GASXmlNodeObject*)fn.ThisPtr;
GASSERT(pthis);
if (!pthis) return;
// Flash Doc: Inserts a newChild node into the XML object's child list,
// before the insertPoint node. If insertPoint is not a child of the
// XMLNode object, the insertion fails
if (pthis->pRealNode && pthis->pRealNode->Type == GFxXMLElementNodeType)
{
// Only works for element nodes
GFxXMLElementNode* elemNode = (GFxXMLElementNode*) pthis->pRealNode;
// Get new child and insertion point
if (fn.NArgs > 1)
{
GASObject* child = fn.Arg(0).ToObject(fn.Env);
GASObject* insert = fn.Arg(1).ToObject(fn.Env);
if ( NULL == child || child->GetObjectType() != Object_XMLNode )
{
// Do nothing (not an XMLNode object)
return;
}
// If insert is invalid, the child is appended. This is
// Flash behavior.
GASXmlNodeObject* pchild = static_cast<GASXmlNodeObject*>(child);
GASXmlNodeObject* pinsert = static_cast<GASXmlNodeObject*>(insert);
if ( pinsert && pinsert->GetObjectType() == Object_XMLNode &&
pinsert->pRealNode && pinsert->pRealNode->Parent &&
pinsert->pRealNode->Parent == elemNode)
{
// Insertion node is a child of this node
if (pchild->pRealNode)
{
// Keep a ref here else subtree will be nuked
GPtr<GFxXMLNode> selfptr = pchild->pRealNode;
if (pchild->pRealNode->Parent)
{
// Flash removes the child from its tree and allows
// it to be appended to the new tree
pchild->pRealNode->Parent->RemoveChild(pchild->pRealNode);
}
elemNode->InsertBefore(pchild->pRealNode, pinsert->pRealNode);
pchild->pRootNode = pthis->pRootNode;
}
}
else if (pchild->pRealNode)
{
// Keep a ref here else subtree will be nuked
GPtr<GFxXMLNode> selfptr = pchild->pRealNode;
// Simply append
if (pchild->pRealNode->Parent)
{
// Flash removes the child from its tree and allows
// it to be appended to the new tree
pchild->pRealNode->Parent->RemoveChild(pchild->pRealNode);
}
elemNode->AppendChild(pchild->pRealNode);
pchild->pRootNode = pthis->pRootNode;
}
}
}
}
//
// XMLNode.removeNode() : Void
//
void GASXmlNodeProto::RemoveNode(const GASFnCall& fn)
{
CHECK_THIS_PTR2(fn, XMLNode, XML);
GASXmlNodeObject* pthis = (GASXmlNodeObject*)fn.ThisPtr;
GASSERT(pthis);
if (!pthis) return;
// Flash Doc: Removes the specified XML object from its parent. Also
// deletes all descendants of the node.
if (pthis->pRealNode)
{
GFxXMLElementNode* parent = pthis->pRealNode->Parent;
if (parent)
{
pthis->pRootNode = *parent->MemoryManager->CreateRootNode(pthis->pRealNode);
parent->RemoveChild(pthis->pRealNode);
}
}
}
//
// Member visitor to traverse the attributes
//
struct XMLAttributeStringBuilder : public GASObject::MemberVisitor
{
GASEnvironment* pEnv;
GStringBuffer& Dest;
XMLAttributeStringBuilder(GASEnvironment* penv, GStringBuffer& dest)
: pEnv(penv), Dest(dest) {}
XMLAttributeStringBuilder& operator=( const XMLAttributeStringBuilder& x )
{
pEnv = x.pEnv;
Dest = x.Dest;
return *this;
}
void Visit(const GASString& name, const GASValue& val, UByte flags)
{
GUNUSED(flags);
Dest += " ";
Dest += name.ToCStr();
Dest += "=\"";
Dest += val.ToString(pEnv).ToCStr();
Dest += "\"";
}
};
//
// Create an XML string by traversing the DOM tree
//
static void BuildXMLString(GASEnvironment* penv, GFxXMLNode* proot, GStringBuffer& dest)
{
switch (proot->Type)
{
case GFxXMLElementNodeType:
{
GFxXMLElementNode* pnode = static_cast<GFxXMLElementNode*>(proot);
GFxXMLShadowRef* pshadow = proot->pShadow;
// If document node, do not print it out - just visit children
if (pshadow && pshadow->pASNode)
{
if (pshadow->pASNode->GetObjectType() == GASObject::Object_XML)
{
GASXmlNodeObject* pobj = pshadow->pASNode;
// Print xmldecl
GASValue xmldecl;
pobj->GetMember(penv,
penv->GetSC()->CreateConstString("xmlDecl"),
&xmldecl);
if (!xmldecl.IsUndefined())
{
dest += xmldecl.ToString(penv).ToCStr();
GASValue ignorews;
pobj->GetMember(penv,
penv->GetSC()->CreateConstString("ignoreWhite"),
&ignorews);
if (!ignorews.ToBool(penv))
{
dest += "\n";
}
}
// Print children
for (GFxXMLNode* child = pnode->FirstChild;
child != NULL; child = child->NextSibling)
BuildXMLString(penv, child, dest);
break;
}
}
dest += "<";
if (pnode->Prefix.GetSize() > 0)
{
dest += pnode->Prefix.ToCStr();
dest += ":";
}
dest += proot->Value.ToCStr();
if (pshadow && pshadow->pAttributes)
{
// Shadow is present, get the attributes from the shadow
XMLAttributeStringBuilder attrvis(penv, dest);
GASSERT(pshadow->pAttributes);
GASObject* attrs = pshadow->pAttributes;
if (attrs)
attrs->VisitMembers(penv->GetSC(), &attrvis, 0);
}
else
{
// Shadow is not present, get attributes from real node
for (GFxXMLAttribute* attr = pnode->FirstAttribute;
attr != NULL; attr = attr->Next)
{
dest += " ";
dest += attr->Name.ToCStr();
dest += "=\"";
dest += attr->Value.ToCStr();
dest += "\"";
}
}
if (pnode->HasChildren())
dest += ">";
else
dest += " />";
for (GFxXMLNode* child = pnode->FirstChild; child != NULL;
child = child->NextSibling)
BuildXMLString(penv, child, dest);
if (pnode->HasChildren())
{
dest += "</";
if (pnode->Prefix.GetSize() > 0)
{
dest += pnode->Prefix.ToCStr();
dest += ":";
}
dest += proot->Value.ToCStr();
dest += ">";
}
break;
}
default: // Text nodes and unknowns
{
dest += proot->Value.ToCStr();
}
}
}
//
// XMLNode.toString() : String
//
void GASXmlNodeProto::ToString(const GASFnCall& fn)
{
CHECK_THIS_PTR2(fn, XMLNode, XML);
GASXmlNodeObject* pthis = (GASXmlNodeObject*)fn.ThisPtr;
GASSERT(pthis);
if (!pthis) return;
GStringBuffer str;
if (pthis->pRealNode)
{
if (pthis->pRealNode->Type == GFxXMLElementNodeType)
{
GFxXMLElementNode* elemNode =
(GFxXMLElementNode*) pthis->pRealNode;
BuildXMLString(fn.Env, elemNode, str);
}
else // Text nodes and unknown types
{
str += pthis->pRealNode->Value.ToCStr();
}
fn.Result->SetString(fn.Env->CreateString(str.ToCStr(),str.GetSize()));
}
else
{
// Flash has inconsistent behavior when a node is created
// with a type but no value. This malformed node returns
// '[type Object] : undefined' when toString() is called on it.
// GFx simply returns undefined, since this case should not happen
// in production code.
fn.Result->SetUndefined();
}
}
//
// Constructor func ctor
//
GASXmlNodeCtorFunction::GASXmlNodeCtorFunction(GASStringContext *psc)
: GASCFunctionObject(psc, GlobalCtor)
{
GUNUSED(psc);
}
//
// Called when the constructor is invoked for the XMLNode class
// (new XMLNode(...))
//
void GASXmlNodeCtorFunction::GlobalCtor(const GASFnCall& fn)
{
GPtr<GASXmlNodeObject> pnode;
// Need to check if object is of type XML as well
if (fn.ThisPtr &&
(fn.ThisPtr->GetObjectType() == GASObject::Object_XMLNode ||
fn.ThisPtr->GetObjectType() == GASObject::Object_XML))
pnode = static_cast<GASXmlNodeObject*>(fn.ThisPtr);
else
pnode = *GHEAP_NEW(fn.Env->GetHeap()) GASXmlNodeObject(fn.Env);
GFxLog* log = fn.GetLog();
// Handle the constructor parameters
GASValue nodeType = GASValue::UNDEFINED;
GASValue nodeValue = GASValue::UNDEFINED;
if (fn.NArgs > 0)
{
nodeType = fn.Arg(0);
if (fn.NArgs > 1)
nodeValue = fn.Arg(1);
}
// Since the XMLNode is created from scratch, a real node must be
// explictly created
if ( !nodeType.IsUndefined() )
{
GASNumber nt = nodeType.ToNumber(fn.Env);
// If node type is defined, a node value is required. if not,
// default to GASObject
if (!nodeValue.IsUndefined())
{
// A shadow should not already be assigned to this node.
GASSERT(pnode->pRealNode == NULL);
// Get the global (to movie root) object manager
GFxMovieRoot* pmovieroot = fn.Env->GetMovieRoot();
GPtr<GFxXMLObjectManager> memMgr;
if (!pmovieroot->pXMLObjectManager)
{
memMgr = *GHEAP_NEW(fn.Env->GetHeap()) GFxXMLObjectManager(pmovieroot);
pmovieroot->pXMLObjectManager = memMgr;
}
else
memMgr = (GFxXMLObjectManager*)pmovieroot->pXMLObjectManager;
GFxXMLDOMString localname(memMgr->EmptyString());
GFxXMLDOMString prefix(memMgr->EmptyString());
GASString s = nodeValue.ToString(fn.Env);
if (nt == GFxXMLElementNodeType)
{
// Name may have a prefix
const char* pfx = strchr(s.ToCStr(), ':');
if (pfx)
{
// Create a prefix mapping local to this node.
// this will have to be resolved if node is attached
// to a tree.
prefix = memMgr->CreateString(s.ToCStr(), pfx-s.ToCStr());
localname = memMgr->CreateString(pfx+1, G_strlen(pfx));
}
else
localname = memMgr->CreateString(s.ToCStr(), s.GetSize());
GPtr<GFxXMLElementNode> en = *memMgr->CreateElementNode(localname);
pnode->pRealNode = en;
pnode->pRootNode = *memMgr->CreateRootNode(pnode->pRealNode);
GFxXMLElementNode* elemNode =
static_cast<GFxXMLElementNode*>(pnode->pRealNode);
elemNode->Prefix = prefix;
}
else if (nt == GFxXMLTextNodeType)
{
localname = memMgr->CreateString(s.ToCStr(), s.GetSize());
GPtr<GFxXMLTextNode> tn = *memMgr->CreateTextNode(localname);
pnode->pRealNode = tn;
pnode->pRootNode = *memMgr->CreateRootNode(pnode->pRealNode);
}
else
{
// If node type is neither 1 or 3, then the values
// should not matter. But AS 2.0 converts them into text nodes..
// Flash Docs: "In Flash Player, the XML class only supports
// node types 1 (ELEMENT_NODE) and 3 (TEXT_NODE)."
GPtr<GFxXMLTextNode> tn = *memMgr->CreateTextNode(localname);
pnode->pRealNode = tn;
pnode->pRootNode = *memMgr->CreateRootNode(pnode->pRealNode);
pnode->pRealNode->Type = static_cast<UByte>(nt);
}
SetupShadow(fn.Env, pnode->pRealNode, pnode);
}
else
{
if (log != NULL)
log->LogScriptWarning("Warning: XMLNodeCtorFunction::"
"GlobalCtor - malformed XMLNode object\n");
}
}
else
{
if (log != NULL)
log->LogScriptWarning("Warning: XMLNodeCtorFunction::"
"GlobalCtor - node type not specified\n");
}
fn.Result->SetAsObject(pnode.GetPtr());
}
GASObject* GASXmlNodeCtorFunction::CreateNewObject(GASEnvironment* penv) const
{
return GHEAP_NEW(penv->GetHeap()) GASXmlNodeObject(penv);
}
GASFunctionRef GASXmlNodeCtorFunction::Register(GASGlobalContext* pgc)
{
GASStringContext sc(pgc, 8);
GASFunctionRef ctor(*GHEAP_NEW(pgc->GetHeap()) GASXmlNodeCtorFunction(&sc));
GPtr<GASXmlNodeProto> proto =
*GHEAP_NEW(pgc->GetHeap()) GASXmlNodeProto(&sc, pgc->GetPrototype(GASBuiltin_Object), ctor);
pgc->SetPrototype(GASBuiltin_XMLNode, proto);
pgc->pGlobal->SetMemberRaw(&sc, pgc->GetBuiltin(GASBuiltin_XMLNode), GASValue(ctor));
// Preload the XML and XMLNode member names as GASStrings and
// store them locally for fast access
GASXmlNodeObject::InitializeStandardMembers(*pgc, proto->XMLNodeMemberMap);
return ctor;
}
#endif // #ifndef GFC_NO_XML_SUPPORT