From e38df119b8bd60f9e925f25d606c43030a318850 Mon Sep 17 00:00:00 2001 From: "vidur%netscape.com" Date: Thu, 20 Apr 2006 03:36:50 +0000 Subject: [PATCH] First cut of XMLSerializer and XMLHttpRequest. This is WIP and not part of the Mozilla/Seamonkey build. --- content/base/public/nsIDOMSerializer.idl | 67 ++ content/base/public/nsIXMLHttpRequest.idl | 60 ++ content/base/src/nsDOMSerializer.cpp | 670 ++++++++++++++++ content/base/src/nsDOMSerializer.h | 86 +++ content/base/src/nsXMLHttpRequest.cpp | 887 ++++++++++++++++++++++ content/base/src/nsXMLHttpRequest.h | 111 +++ 6 files changed, 1881 insertions(+) create mode 100644 content/base/public/nsIDOMSerializer.idl create mode 100644 content/base/public/nsIXMLHttpRequest.idl create mode 100644 content/base/src/nsDOMSerializer.cpp create mode 100644 content/base/src/nsDOMSerializer.h create mode 100644 content/base/src/nsXMLHttpRequest.cpp create mode 100644 content/base/src/nsXMLHttpRequest.h diff --git a/content/base/public/nsIDOMSerializer.idl b/content/base/public/nsIDOMSerializer.idl new file mode 100644 index 00000000000..6940d6eafa1 --- /dev/null +++ b/content/base/public/nsIDOMSerializer.idl @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +#include "nsISupports.idl" + +interface nsIOutputStream; +interface nsIDOMNode; + +/** + * The nsIDOMSerializer interface is really a placeholder till the W3C + * DOM Working Group defines a mechanism for serializing DOM nodes. + * An instance of this interface can be used to serialize a DOM document + * or any DOM subtree. + */ + +[scriptable, uuid(a6cf9123-15b3-11d2-932e-00805f8add32)] +interface nsIDOMSerializer : nsISupports { + /** + * The subtree rooted by the specified element is serialized to + * a string. + * + * @param root The root of the subtree to be serialized. This could + * be any node, including a Document. + * @returns The serialized subtree in the form of a Unicode string + */ + wstring serializeToString(in nsIDOMNode root); + + /** + * The subtree rooted by the specified element is serialized to + * a byte stream using the character set specified. + * @param root The root of the subtree to be serialized. This could + * be any node, including a Document. + * @param stream The byte stream to which the subtree is serialized. + * @param charset The name of the character set to use for the encoding + * to a byte stream. + */ + void serializeToStream(in nsIDOMNode root, in nsIOutputStream stream, in string charset); +}; + +%{ C++ +#define NS_XMLSERIALIZER_CID \ + { /* a6cf9124-15b3-11d2-932e-00805f8add32 */ \ + 0xa6cf9124, 0x15b3, 0x11d2, \ + {0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32} } +#define NS_XMLSERIALIZER_PROGID \ +"component://netscape/xmlextras/xmlserializer" +%} + diff --git a/content/base/public/nsIXMLHttpRequest.idl b/content/base/public/nsIXMLHttpRequest.idl new file mode 100644 index 00000000000..1b0580ff4b2 --- /dev/null +++ b/content/base/public/nsIXMLHttpRequest.idl @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +#include "nsISupports.idl" + +interface nsIDOMDocument; +interface nsIDOMEventListener; + +[scriptable, uuid(b7215e70-4157-11d4-9a42-000064657374)] +interface nsIXMLHttpRequest : nsISupports { + noscript void addEventListener(in string type, + in nsIDOMEventListener listener); + noscript void removeEventListener(in string type, + in nsIDOMEventListener listener); + attribute nsISupports onload; + attribute nsISupports onerror; + readonly attribute nsIDOMDocument responseXML; + readonly attribute unsigned long status; + readonly attribute string statusText; + + void abort(); + string getAllResponseHeaders(); + string getResponseHeader(in string header); + noscript void openRequest(in string method, + in wstring url, + in boolean async, + in string user, + in string password); + void open(in string method, in wstring url); + void send(in nsISupports body); + void setRequestHeader(in string header, in string value); +}; + +%{ C++ +#define NS_XMLHTTPREQUEST_CID \ + { /* d164e770-4157-11d4-9a42-000064657374 */ \ + 0xd164e770, 0x4157, 0x11d4, \ + {0x9a, 0x42, 0x00, 0x00, 0x64, 0x65, 0x73, 0x74} } +#define NS_XMLHTTPREQUEST_PROGID \ +"component://netscape/xmlextras/xmlhttprequest" +%} diff --git a/content/base/src/nsDOMSerializer.cpp b/content/base/src/nsDOMSerializer.cpp new file mode 100644 index 00000000000..97f00804bbb --- /dev/null +++ b/content/base/src/nsDOMSerializer.cpp @@ -0,0 +1,670 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +#include "nsDOMSerializer.h" +#include "nsIDOMNode.h" +#include "nsIDOMElement.h" +#include "nsIDOMDocumentType.h" +#include "nsIDOMText.h" +#include "nsIDOMCDATASection.h" +#include "nsIDOMComment.h" +#include "nsIDOMProcessingInstruction.h" +#include "nsIDOMNamedNodeMap.h" +#include "nsIDOMAttr.h" +#include "nsIDOMNodeList.h" + +#include "nsIOutputStream.h" +#include "nsIUnicodeEncoder.h" +#include "nsIServiceManager.h" +#include "nsICharsetConverterManager.h" + +#define USE_NSICONTENT +#ifdef USE_NSICONTENT +#include "nsIContent.h" +#include "nsIDocument.h" +#include "nsINameSpaceManager.h" +#endif + +static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID); + +typedef struct { + nsString mPrefix; + nsString mURI; + nsIDOMElement* mOwner; +} NameSpaceDecl; + +nsDOMSerializer::nsDOMSerializer() +{ + NS_INIT_ISUPPORTS(); + mPrefixIndex = 0; +} + +nsDOMSerializer::~nsDOMSerializer() +{ +} + +NS_IMPL_ISUPPORTS1(nsDOMSerializer, nsIDOMSerializer) + +void +nsDOMSerializer::SerializeText(nsIDOMText* aText, nsString& aStr) +{ + nsAutoString data; + if (aText) { + aText->GetData(data); + aStr.Append(data); + } +} + +void +nsDOMSerializer::SerializeCDATASection(nsIDOMCDATASection* aCDATASection, + nsString& aStr) +{ + nsAutoString data; + if (aCDATASection) { + aCDATASection->GetData(data); + aStr.AppendWithConversion(""); + } +} + +void +nsDOMSerializer::SerializeProcessingInstruction(nsIDOMProcessingInstruction* aPI, + nsString& aStr) +{ + nsAutoString target, data; + if (aPI) { + aStr.AppendWithConversion("GetTarget(target); + aStr.Append(target); + aPI->GetData(data); + if (data.Length() > 0) { + aStr.AppendWithConversion(" "); + aStr.Append(data); + } + aStr.AppendWithConversion(">"); + } +} + +void +nsDOMSerializer::SerializeComment(nsIDOMComment* aComment, + nsString& aStr) +{ + nsAutoString data; + if (aComment) { + aStr.AppendWithConversion(""); + } +} + +void +nsDOMSerializer::SerializeDoctype(nsIDOMDocumentType* aDoctype, + nsString& aStr) +{ + if (aDoctype) { + nsAutoString name, publicId, systemId, internalSubset; + + aDoctype->GetName(name); + aDoctype->GetPublicId(publicId); + aDoctype->GetSystemId(publicId); + aDoctype->GetInternalSubset(publicId); + + aStr.AppendWithConversion(" 0) { + aStr.AppendWithConversion(" PUBLIC \""); + aStr.Append(publicId); + aStr.AppendWithConversion("\" \""); + aStr.Append(systemId); + aStr.AppendWithConversion("\""); + } + else if (systemId.Length() > 0) { + aStr.AppendWithConversion(" SYSTEM \""); + aStr.Append(systemId); + aStr.AppendWithConversion("\""); + } + + if (internalSubset.Length() > 0) { + aStr.AppendWithConversion(" "); + aStr.Append(internalSubset); + } + + aStr.AppendWithConversion(">"); + } +} + + +static const char* kXMLNS = "xmlns"; + +void +nsDOMSerializer::PushNameSpaceDecl(nsString& aPrefix, + nsString& aURI, + nsIDOMElement* aOwner) +{ + NameSpaceDecl* decl = new NameSpaceDecl(); + if (decl) { + decl->mPrefix.Assign(aPrefix); + decl->mURI.Assign(aURI); + // Don't addref - this weak reference will be removed when + // we pop the stack + decl->mOwner = aOwner; + + mNameSpaceStack.AppendElement((void*)decl); + } +} + +void +nsDOMSerializer::PopNameSpaceDeclsFor(nsIDOMElement* aOwner) +{ + PRInt32 index, count; + + count = mNameSpaceStack.Count(); + for (index = count; index >= 0; index--) { + NameSpaceDecl* decl = (NameSpaceDecl*)mNameSpaceStack.ElementAt(index); + if (decl) { + if (decl->mOwner != aOwner) { + break; + } + mNameSpaceStack.RemoveElementAt(index); + delete decl; + } + } +} + +PRBool +nsDOMSerializer::ConfirmPrefix(nsString& aPrefix, + nsString& aURI) +{ + if (aPrefix.EqualsWithConversion(kXMLNS)) { + return PR_FALSE; + } + if (aURI.Length() == 0) { + aPrefix.Truncate(); + return PR_FALSE; + } + PRInt32 index, count; + nsAutoString closestURIMatch; + PRBool uriMatch = PR_FALSE; + + count = mNameSpaceStack.Count(); + for (index = count; index >= 0; index--) { + NameSpaceDecl* decl = (NameSpaceDecl*)mNameSpaceStack.ElementAt(index); + if (decl) { + // Check if we've found a prefix match + if (aPrefix.Equals(decl->mPrefix)) { + + // If the URI's match, we don't have to add a namespace decl + if (aURI.Equals(decl->mURI)) { + return PR_FALSE; + } + // If they don't, we can't use this prefix + else { + aPrefix.Truncate(); + } + } + // If we've found a URI match, then record the first one + else if (!uriMatch && aURI.Equals(decl->mURI)) { + uriMatch = PR_TRUE; + closestURIMatch.Assign(decl->mPrefix); + } + } + } + + // There are no namespace declarations that match the prefix, uri pair. + // If there's another prefix that matches that URI, us it. + if (uriMatch) { + aPrefix.Assign(closestURIMatch); + return PR_FALSE; + } + // If we don't have a prefix, create one + else if (aPrefix.Length() == 0) { + aPrefix.AssignWithConversion("a"); + aPrefix.AppendInt(mPrefixIndex++); + } + + // Indicate that we need to create a namespace decl for the + // final prefix + return PR_TRUE; +} + +void +nsDOMSerializer::SerializeAttr(nsString& aPrefix, + nsString& aName, + nsString& aValue, + nsString& aStr) +{ + aStr.AppendWithConversion(" "); + if (aPrefix.Length() > 0) { + aStr.Append(aPrefix); + aStr.AppendWithConversion(":"); + } + aStr.Append(aName); + + aStr.AppendWithConversion("=\""); + aStr.Append(aValue); + aStr.AppendWithConversion("\""); +} + +void +nsDOMSerializer::SerializeElementStart(nsIDOMElement* aElement, nsString& aStr) +{ + if (aElement) { + nsAutoString tagPrefix, tagLocalName, tagNamespaceURI; + nsAutoString xmlnsStr, defaultnsStr; + xmlnsStr.AssignWithConversion(kXMLNS); + defaultnsStr.AssignWithConversion(""); + + aElement->GetPrefix(tagPrefix); + aElement->GetLocalName(tagLocalName); + aElement->GetNamespaceURI(tagNamespaceURI); + +#ifdef USE_NSICONTENT + nsCOMPtr content = do_QueryInterface(aElement); + PRInt32 index, count; + nsAutoString nameStr, prefixStr, uriStr, valueStr; + PRInt32 namespaceID; + nsCOMPtr attrName, attrPrefix; + if (content) { + content->GetAttributeCount(count); + + // First scan for namespace declarations, pushing each on the stack + for (index = 0; index < count; index++) { + + content->GetAttributeNameAt(index, + namespaceID, + *getter_AddRefs(attrName), + *getter_AddRefs(attrPrefix)); + + if (attrPrefix) { + attrPrefix->ToString(prefixStr); + } + else { + prefixStr.Truncate(); + } + attrName->ToString(nameStr); + + content->GetAttribute(namespaceID, attrName, uriStr); + if ((namespaceID == kNameSpaceID_XMLNS) || + prefixStr.EqualsWithConversion(kXMLNS)) { + PushNameSpaceDecl(nameStr, uriStr, aElement); + } + else if (nameStr.EqualsWithConversion(kXMLNS)) { + PushNameSpaceDecl(defaultnsStr, uriStr, aElement); + } + } + } +#else + PRInt32 index, count; + nsAutoString nameStr, prefixStr, uriStr, valueStr; + nsCOMPtr attrMap; + nsCOMPtr attrNode; + nsCOMPtr attrObj; + + aElement->GetAttributes(getter_AddRefs(attrMap)); + if (attrMap) { + attrMap->GetLength((PRUint32*)&count); + + // First scan for namespace declarations, pushing each on the stack + for (index = 0; index < count; index++) { + attrMap->Item(index, getter_AddRefs(attrNode)); + attrObj = do_QueryInterface(attrNode); + if (attrObj) { + attrObj->GetPrefix(prefixStr); + attrObj->GetLocalName(nameStr); + attrObj->GetNamespaceURI(uriStr); + if (prefixStr.EqualsWithConversion(kXMLNS)) { + PushNameSpaceDecl(nameStr, uriStr, aElement); + } + else if (nameStr.EqualsWithConversion(kXMLNS)) { + PushNameSpaceDecl(nsAutoString(), uriStr, aElement); + } + } + } + } +#endif + + PRBool addNSAttr; + + // Serialize the qualified name of the element + addNSAttr = ConfirmPrefix(tagPrefix, tagNamespaceURI); + aStr.AppendWithConversion("<"); + if (tagPrefix.Length() > 0) { + aStr.Append(tagPrefix); + aStr.AppendWithConversion(":"); + } + aStr.Append(tagLocalName); + + // If we had to add a new namespace declaration, serialize + // and push it on the namespace stack + if (addNSAttr) { + SerializeAttr(xmlnsStr, tagPrefix, tagNamespaceURI, aStr); + PushNameSpaceDecl(tagPrefix, tagNamespaceURI, aElement); + } + +#ifdef USE_NSICONTENT + if (content) { + // Now serialize each of the attributes + // XXX Unfortunately we need a namespace manager to get + // attribute URIs. + nsCOMPtr document; + nsCOMPtr nsmanager; + content->GetDocument(*getter_AddRefs(document)); + if (document) { + document->GetNameSpaceManager(*getter_AddRefs(nsmanager)); + } + + for (index = 0; index < count; index++) { + content->GetAttributeNameAt(index, + namespaceID, + *getter_AddRefs(attrName), + *getter_AddRefs(attrPrefix)); + if (attrPrefix) { + attrPrefix->ToString(prefixStr); + } + else { + prefixStr.Truncate(); + } + + addNSAttr = PR_FALSE; + if (kNameSpaceID_XMLNS == namespaceID) { + prefixStr.AssignWithConversion(kXMLNS); + } + else if (nsmanager) { + nsmanager->GetNameSpaceURI(namespaceID, uriStr); + addNSAttr = ConfirmPrefix(prefixStr, uriStr); + } + + content->GetAttribute(namespaceID, attrName, valueStr); + attrName->ToString(nameStr); + + SerializeAttr(prefixStr, nameStr, valueStr, aStr); + + if (addNSAttr) { + SerializeAttr(xmlnsStr, prefixStr, uriStr, aStr); + PushNameSpaceDecl(prefixStr, uriStr, aElement); + } + } + } +#else + if (attrMap) { + // Now serialize each of the attributes + for (index = 0; index < count; index++) { + aStr.AppendWithConversion(" "); + attrMap->Item(index, getter_AddRefs(attrNode)); + attrObj = do_QueryInterface(attrNode); + if (attrObj) { + attrObj->GetPrefix(prefixStr); + attrObj->GetLocalName(nameStr); + attrObj->GetNamespaceURI(uriStr); + + addNSAttr = ConfirmPrefix(prefixStr, uriStr); + attrObj->GetNodeValue(valueStr); + + SerializeAttr(prefixStr, nameStr, valueStr, aStr); + + if (addNSAttr) { + SerializeAttr(xmlnsStr, prefixStr, uriStr, aStr); + PushNameSpaceDecl(prefixStr, uriStr, aElement); + } + } + } + } +#endif + + aStr.AppendWithConversion(">"); + } +} + +void +nsDOMSerializer::SerializeElementEnd(nsIDOMElement* aElement, nsString& aStr) +{ + if (aElement) { + nsAutoString tagPrefix, tagLocalName, tagNamespaceURI; + + aElement->GetPrefix(tagPrefix); + aElement->GetLocalName(tagLocalName); + aElement->GetNamespaceURI(tagNamespaceURI); + + ConfirmPrefix(tagPrefix, tagNamespaceURI); + aStr.AppendWithConversion(" 0) { + aStr.Append(tagPrefix); + aStr.AppendWithConversion(":"); + } + aStr.Append(tagLocalName); + aStr.AppendWithConversion(">"); + + PopNameSpaceDeclsFor(aElement); + } +} + + +nsresult +nsDOMSerializer::SerializeNodeStart(nsIDOMNode* aNode, nsString& aStr) +{ + PRUint16 type; + + aNode->GetNodeType(&type); + switch (type) { + case nsIDOMNode::ELEMENT_NODE: + { + nsCOMPtr element = do_QueryInterface(aNode); + SerializeElementStart(element, aStr); + break; + } + case nsIDOMNode::TEXT_NODE: + { + nsCOMPtr text = do_QueryInterface(aNode); + SerializeText(text, aStr); + break; + } + case nsIDOMNode::CDATA_SECTION_NODE: + { + nsCOMPtr cdata = do_QueryInterface(aNode); + SerializeCDATASection(cdata, aStr); + break; + } + case nsIDOMNode::PROCESSING_INSTRUCTION_NODE: + { + nsCOMPtr pi = do_QueryInterface(aNode); + SerializeProcessingInstruction(pi, aStr); + break; + } + case nsIDOMNode::COMMENT_NODE: + { + nsCOMPtr comment = do_QueryInterface(aNode); + SerializeComment(comment, aStr); + break; + } + case nsIDOMNode::DOCUMENT_TYPE_NODE: + { + nsCOMPtr doctype = do_QueryInterface(aNode); + SerializeDoctype(doctype, aStr); + break; + } + } + + return NS_OK; +} + +nsresult +nsDOMSerializer::SerializeNodeEnd(nsIDOMNode* aNode, nsString& aStr) +{ + PRUint16 type; + + aNode->GetNodeType(&type); + switch (type) { + case nsIDOMNode::ELEMENT_NODE: + { + nsCOMPtr element = do_QueryInterface(aNode); + SerializeElementEnd(element, aStr); + break; + } + } + + return NS_OK; +} + +nsresult +nsDOMSerializer::SerializeToStringRecursive(nsIDOMNode* aNode, nsString& aStr) +{ + nsresult rv = SerializeNodeStart(aNode, aStr); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr childNodes; + rv = aNode->GetChildNodes(getter_AddRefs(childNodes)); + NS_ENSURE_SUCCESS(rv, rv); + + if (childNodes) { + PRInt32 index, count; + + childNodes->GetLength((PRUint32*)&count); + for (index = 0; index < count; index++) { + nsCOMPtr child; + + rv = childNodes->Item(index, getter_AddRefs(child)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = SerializeToStringRecursive(child, aStr); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + rv = SerializeNodeEnd(aNode, aStr); + return rv; +} + +NS_IMETHODIMP +nsDOMSerializer::SerializeToString(nsIDOMNode *root, PRUnichar **_retval) +{ + NS_ENSURE_ARG_POINTER(root); + NS_ENSURE_ARG_POINTER(_retval); + nsresult rv; + nsAutoString str; + + rv = SerializeToStringRecursive(root, str); + NS_ENSURE_SUCCESS(rv, rv); + *_retval = str.ToNewUnicode(); + if (nsnull == *_retval) { + return NS_ERROR_OUT_OF_MEMORY; + } + + return NS_OK; +} + +static nsresult +ConvertAndWrite(nsString& aString, + nsIOutputStream* aStream, + nsIUnicodeEncoder* aEncoder) +{ + NS_ENSURE_ARG_POINTER(aStream); + NS_ENSURE_ARG_POINTER(aEncoder); + nsresult rv; + PRInt32 charLength; + PRUnichar* unicodeBuf = (PRUnichar*)aString.GetUnicode(); + PRInt32 unicodeLength = aString.Length(); + + rv = aEncoder->GetMaxLength(unicodeBuf, unicodeLength, &charLength); + if (NS_SUCCEEDED(rv)) { + nsCAutoString charXferString; + charXferString.SetCapacity(charLength); + char* charXferBuf = (char*)charXferString.GetBuffer(); + + rv = aEncoder->Convert(unicodeBuf, &unicodeLength, charXferBuf, &charLength); + if (NS_SUCCEEDED(rv)) { + PRUint32 written; + rv = aStream->Write(charXferBuf, charLength, &written); + } + } + + return rv; +} + +nsresult +nsDOMSerializer::SerializeToStreamRecursive(nsIDOMNode* aNode, + nsIOutputStream* aStream, + nsIUnicodeEncoder* aEncoder) +{ + nsAutoString start; + nsresult rv = SerializeNodeStart(aNode, start); + NS_ENSURE_SUCCESS(rv, rv); + + rv = ConvertAndWrite(start, aStream, aEncoder); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr childNodes; + rv = aNode->GetChildNodes(getter_AddRefs(childNodes)); + NS_ENSURE_SUCCESS(rv, rv); + + if (childNodes) { + PRInt32 index, count; + + childNodes->GetLength((PRUint32*)&count); + for (index = 0; index < count; index++) { + nsCOMPtr child; + + rv = childNodes->Item(index, getter_AddRefs(child)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = SerializeToStreamRecursive(child, aStream, aEncoder); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + nsAutoString end; + rv = SerializeNodeEnd(aNode, end); + NS_ENSURE_SUCCESS(rv, rv); + + rv = ConvertAndWrite(end, aStream, aEncoder); + return rv; +} + +NS_IMETHODIMP +nsDOMSerializer::SerializeToStream(nsIDOMNode *root, + nsIOutputStream *stream, + const char *charset) +{ + NS_ENSURE_ARG_POINTER(root); + NS_ENSURE_ARG_POINTER(stream); + NS_ENSURE_ARG_POINTER(charset); + + nsresult rv; + nsCOMPtr encoder; + + NS_WITH_SERVICE(nsICharsetConverterManager, + charsetConv, + kCharsetConverterManagerCID, + &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString charsetStr; + charsetStr.AssignWithConversion(charset); + rv = charsetConv->GetUnicodeEncoder(&charsetStr, + getter_AddRefs(encoder)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = SerializeToStreamRecursive(root, stream, encoder); + + return rv; +} diff --git a/content/base/src/nsDOMSerializer.h b/content/base/src/nsDOMSerializer.h new file mode 100644 index 00000000000..01ba91ff917 --- /dev/null +++ b/content/base/src/nsDOMSerializer.h @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +#ifndef nsDOMSerializer_h__ +#define nsDOMSerializer_h__ + +#include "nsIDOMSerializer.h" +#include "nsISupportsUtils.h" +#include "nsCOMPtr.h" +#include "nsVoidArray.h" +#include "nsString.h" + +class nsIDOMElement; +class nsIDOMDocumentType; +class nsIDOMText; +class nsIDOMCDATASection; +class nsIDOMComment; +class nsIDOMProcessingInstruction; +class nsIUnicodeEncoder; + +class nsDOMSerializer : public nsIDOMSerializer { +public: + nsDOMSerializer(); + virtual ~nsDOMSerializer(); + + NS_DECL_ISUPPORTS + + // nsIDOMSerializer + NS_IMETHOD SerializeToString(nsIDOMNode *root, PRUnichar **_retval); + NS_IMETHOD SerializeToStream(nsIDOMNode *root, + nsIOutputStream *stream, + const char *charset); + +protected: + void SerializeText(nsIDOMText* aText, nsString& aStr); + void SerializeCDATASection(nsIDOMCDATASection* aCDATASection, nsString& aStr); + void SerializeProcessingInstruction(nsIDOMProcessingInstruction* aPI, + nsString& aStr); + void SerializeComment(nsIDOMComment* aComment, nsString& aStr); + void SerializeDoctype(nsIDOMDocumentType* aDoctype, nsString& aStr); + void PushNameSpaceDecl(nsString& aPrefix, + nsString& aURI, + nsIDOMElement* aOwner); + void PopNameSpaceDeclsFor(nsIDOMElement* aOwner); + PRBool ConfirmPrefix(nsString& aPrefix, + nsString& aURI); + void SerializeAttr(nsString& aPrefix, + nsString& aName, + nsString& aValue, + nsString& aStr); + void SerializeElementStart(nsIDOMElement* aElement, nsString& aStr); + void SerializeElementEnd(nsIDOMElement* aElement, nsString& aStr); + nsresult SerializeNodeStart(nsIDOMNode* aNode, nsString& aStr); + nsresult SerializeNodeEnd(nsIDOMNode* aNode, nsString& aStr); + nsresult SerializeToStringRecursive(nsIDOMNode* aNode, nsString& aStr); + nsresult SerializeToStreamRecursive(nsIDOMNode* aNode, + nsIOutputStream* aStream, + nsIUnicodeEncoder* aEncoder); + +protected: + PRInt32 mPrefixIndex; + nsVoidArray mNameSpaceStack; +}; + + +#endif + diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp new file mode 100644 index 00000000000..b0cf1379963 --- /dev/null +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -0,0 +1,887 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +#include "nsXMLHttpRequest.h" +#include "nsIStreamObserver.h" +#include "nsISimpleEnumerator.h" +#include "nsIHTTPHeader.h" +#include "nsIXPConnect.h" +#include "nsIByteArrayInputStream.h" +#include "nsIUnicodeEncoder.h" +#include "nsIServiceManager.h" +#include "nsICharsetConverterManager.h" +#include "nsLayoutCID.h" +#include "nsIDOMDOMImplementation.h" +#include "nsIPrivateDOMImplementation.h" +#include "nsXPIDLString.h" +#include "nsIURI.h" +#include "nsILoadGroup.h" +#include "nsNetUtil.h" +#include "nsIDOMSerializer.h" +#include "nsISupportsPrimitives.h" +#include "nsIDOMEventTarget.h" +#include "prprf.h" +#include "nsIDOMEventListener.h" +#include "nsIJSContextStack.h" + +static const char* kLoadAsData = "loadAsData"; +static const char* kLoadStr = "load"; +static const char* kErrorStr = "error"; + +static NS_DEFINE_CID(kIDOMDOMImplementationCID, NS_DOM_IMPLEMENTATION_CID); +static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID); + +static JSContext* +GetSafeContext() +{ + // Get the "safe" JSContext: our JSContext of last resort + nsresult rv; + NS_WITH_SERVICE(nsIJSContextStack, stack, "nsThreadJSContextStack", + &rv); + if (NS_FAILED(rv)) + return nsnull; + nsCOMPtr tcs = do_QueryInterface(stack); + JSContext* cx; + if (NS_FAILED(tcs->GetSafeJSContext(&cx))) { + return nsnull; + } + return cx; +} + +static JSContext* +GetCurrentContext() +{ + // Get JSContext from stack. + nsresult rv; + NS_WITH_SERVICE(nsIJSContextStack, stack, "nsThreadJSContextStack", + &rv); + if (NS_FAILED(rv)) + return nsnull; + JSContext *cx; + if (NS_FAILED(stack->Peek(&cx))) + return nsnull; + return cx; +} + +nsXMLHttpRequestScriptListener::nsXMLHttpRequestScriptListener(JSObject* aScopeObj, JSObject* aFunctionObj) +{ + NS_INIT_ISUPPORTS(); + // We don't have to add a GC root for the scope object + // since we'll go away if it goes away + mScopeObj = aScopeObj; + mFunctionObj = aFunctionObj; + JSContext* cx; + cx = GetSafeContext(); + if (cx) { + JS_AddNamedRoot(cx, &mFunctionObj, "nsXMLHttpRequest"); + } +} + +nsXMLHttpRequestScriptListener::~nsXMLHttpRequestScriptListener() +{ + JSContext* cx; + cx = GetSafeContext(); + if (cx) { + JS_RemoveRoot(cx, &mFunctionObj); + } +} + +NS_IMPL_ISUPPORTS2(nsXMLHttpRequestScriptListener, nsIDOMEventListener, nsIPrivateJSEventListener) + +nsresult +nsXMLHttpRequestScriptListener::HandleEvent(nsIDOMEvent* aEvent) +{ + JSContext* cx; + cx = GetCurrentContext(); + if (!cx) { + cx = GetSafeContext(); + } + if (cx) { + jsval val; + + // Hmmm...we can't pass along the nsIDOMEvent because + // we may not have the right type of context (required + // to get a JSObject from a nsIScriptObjectOwner) + JS_CallFunctionValue(cx, mScopeObj, + OBJECT_TO_JSVAL(mFunctionObj), + 0, nsnull, &val); + } + return NS_OK; +} + +NS_IMETHODIMP +nsXMLHttpRequestScriptListener::GetFunctionObj(JSObject** aObj) +{ + NS_ENSURE_ARG_POINTER(aObj); + + *aObj = mFunctionObj; + return NS_OK; +} + +///////////////////////////////////////////// +// +// +///////////////////////////////////////////// + +nsXMLHttpRequest::nsXMLHttpRequest() +{ + NS_INIT_ISUPPORTS(); + mComplete = PR_FALSE; +} + +nsXMLHttpRequest::~nsXMLHttpRequest() +{ +} + +NS_IMPL_ISUPPORTS2(nsXMLHttpRequest, nsIXMLHttpRequest, nsIDOMLoadListener) + +/* noscript void addEventListener (in string type, in nsIDOMEventListener listener); */ +NS_IMETHODIMP +nsXMLHttpRequest::AddEventListener(const char *type, + nsIDOMEventListener *listener) +{ + NS_ENSURE_ARG(type); + NS_ENSURE_ARG(listener); + nsresult rv; + + // I know, I know - strcmp's. But it's only for a couple of + // cases...and they are short strings. :-) + if (nsCRT::strcmp(type, kLoadStr) == 0) { + if (!mLoadEventListeners) { + rv = NS_NewISupportsArray(getter_AddRefs(mLoadEventListeners)); + NS_ENSURE_SUCCESS(rv, rv); + } + mLoadEventListeners->AppendElement(listener); + } + else if (nsCRT::strcmp(type, kErrorStr) == 0) { + if (!mErrorEventListeners) { + rv = NS_NewISupportsArray(getter_AddRefs(mErrorEventListeners)); + NS_ENSURE_SUCCESS(rv, rv); + } + mErrorEventListeners->AppendElement(listener); + } + else { + return NS_ERROR_INVALID_ARG; + } + + return NS_OK; +} + +/* noscript void removeEventListener (in string type, in nsIDOMEventListener listener); */ +NS_IMETHODIMP +nsXMLHttpRequest::RemoveEventListener(const char *type, + nsIDOMEventListener *listener) +{ + NS_ENSURE_ARG(type); + NS_ENSURE_ARG(listener); + + if (nsCRT::strcmp(type, kLoadStr) == 0) { + if (mLoadEventListeners) { + mLoadEventListeners->RemoveElement(listener); + } + } + else if (nsCRT::strcmp(type, kErrorStr) == 0) { + if (mErrorEventListeners) { + mErrorEventListeners->RemoveElement(listener); + } + } + else { + return NS_ERROR_INVALID_ARG; + } + + return NS_OK; +} + +nsresult +nsXMLHttpRequest::MakeScriptEventListener(nsISupports* aObject, + nsIDOMEventListener** aListener) +{ + nsresult rv; + + *aListener = nsnull; + + nsCOMPtr cc; + NS_WITH_SERVICE(nsIXPConnect, xpc, nsIXPConnect::GetCID(), &rv); + if(NS_SUCCEEDED(rv)) { + rv = xpc->GetCurrentNativeCallContext(getter_AddRefs(cc)); + } + + if (NS_SUCCEEDED(rv) && cc) { + nsCOMPtr jsobjholder = do_QueryInterface(aObject); + if (jsobjholder) { + JSObject* funobj; + rv = jsobjholder->GetJSObject(&funobj); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; + + JSContext* cx; + rv = cc->GetJSContext(&cx); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; + + JSFunction* fun = JS_ValueToFunction(cx, OBJECT_TO_JSVAL(funobj)); + if (!fun) { + return NS_ERROR_INVALID_ARG; + } + + nsCOMPtr wrapper; + rv = cc->GetCalleeWrapper(getter_AddRefs(wrapper)); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; + + JSObject* scopeobj; + rv = wrapper->GetJSObject(&scopeobj); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; + + nsXMLHttpRequestScriptListener* listener = new nsXMLHttpRequestScriptListener(scopeobj, funobj); + if (!listener) { + return NS_ERROR_OUT_OF_MEMORY; + } + *aListener = listener; + NS_ADDREF(*aListener); + } + } + + return NS_OK; +} + +static PRBool +CheckForScriptListener(nsISupports* aElement, void *aData) +{ + nsCOMPtr jsel = do_QueryInterface(aElement); + if (jsel) { + nsIDOMEventListener** retval = (nsIDOMEventListener**)aData; + + aElement->QueryInterface(NS_GET_IID(nsIDOMEventListener), (void**)retval); + return PR_FALSE; + } + return PR_TRUE; +} + +void +nsXMLHttpRequest::GetScriptEventListener(nsISupportsArray* aList, + nsIDOMEventListener** aListener) +{ + aList->EnumerateForwards(CheckForScriptListener, (void*)aListener); +} + +PRBool +nsXMLHttpRequest::StuffReturnValue(nsIDOMEventListener* aListener) +{ + nsresult rv; + + nsCOMPtr cc; + NS_WITH_SERVICE(nsIXPConnect, xpc, nsIXPConnect::GetCID(), &rv); + if(NS_SUCCEEDED(rv)) { + rv = xpc->GetCurrentNativeCallContext(getter_AddRefs(cc)); + } + + // If we're being called through JS, stuff the return value + if (NS_SUCCEEDED(rv) && cc) { + jsval* val; + rv = cc->GetRetValPtr(&val); + if (NS_SUCCEEDED(rv)) { + JSObject* obj; + nsCOMPtr jsel = do_QueryInterface(aListener); + if (jsel) { + jsel->GetFunctionObj(&obj); + *val = OBJECT_TO_JSVAL(obj); + return PR_TRUE; + } + } + } + + return PR_FALSE; +} + +/* attribute nsIDOMEventListener onload; */ +NS_IMETHODIMP +nsXMLHttpRequest::GetOnload(nsISupports * *aOnLoad) +{ + NS_ENSURE_ARG_POINTER(aOnLoad); + + *aOnLoad = nsnull; + if (mLoadEventListeners) { + nsCOMPtr listener; + + GetScriptEventListener(mLoadEventListeners, getter_AddRefs(listener)); + if (listener) { + StuffReturnValue(listener); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXMLHttpRequest::SetOnload(nsISupports * aOnLoad) +{ + NS_ENSURE_ARG(aOnLoad); + + nsresult rv; + nsCOMPtr listener; + + rv = MakeScriptEventListener(aOnLoad, getter_AddRefs(listener)); + if (NS_FAILED(rv)) return rv; + + if (listener) { + nsCOMPtr oldListener; + + // Remove any old script event listener that exists since + // we can only have one + if (mLoadEventListeners) { + GetScriptEventListener(mLoadEventListeners, getter_AddRefs(oldListener)); + RemoveEventListener(kLoadStr, oldListener); + } + } + else { + // If it's not a script event listener, try to directly QI it to + // an actual event listener + listener = do_QueryInterface(aOnLoad); + if (!listener) { + return NS_ERROR_INVALID_ARG; + } + } + + return AddEventListener(kLoadStr, listener); +} + +/* attribute nsIDOMEventListener onerror; */ +NS_IMETHODIMP +nsXMLHttpRequest::GetOnerror(nsISupports * *aOnerror) +{ + NS_ENSURE_ARG_POINTER(aOnerror); + + *aOnerror = nsnull; + if (mErrorEventListeners) { + nsCOMPtr listener; + + GetScriptEventListener(mErrorEventListeners, getter_AddRefs(listener)); + if (listener) { + StuffReturnValue(listener); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXMLHttpRequest::SetOnerror(nsISupports * aOnerror) +{ + NS_ENSURE_ARG(aOnerror); + + nsresult rv; + nsCOMPtr listener; + + rv = MakeScriptEventListener(aOnerror, getter_AddRefs(listener)); + if (NS_FAILED(rv)) return rv; + + if (listener) { + nsCOMPtr oldListener; + + // Remove any old script event listener that exists since + // we can only have one + if (mErrorEventListeners) { + GetScriptEventListener(mErrorEventListeners, + getter_AddRefs(oldListener)); + RemoveEventListener(kErrorStr, oldListener); + } + } + else { + // If it's not a script event listener, try to directly QI it to + // an actual event listener + listener = do_QueryInterface(aOnerror); + if (!listener) { + return NS_ERROR_INVALID_ARG; + } + } + + return AddEventListener(kErrorStr, listener); +} + +/* readonly attribute nsIDOMDocument responseXML; */ +NS_IMETHODIMP nsXMLHttpRequest::GetResponseXML(nsIDOMDocument **aResponseXML) +{ + NS_ENSURE_ARG_POINTER(aResponseXML); + *aResponseXML = nsnull; + if (mComplete && mDocument) { + *aResponseXML = mDocument; + NS_ADDREF(*aResponseXML); + } + + return NS_OK; +} + +/* readonly attribute unsigned long status; */ +NS_IMETHODIMP +nsXMLHttpRequest::GetStatus(PRUint32 *aStatus) +{ + if (mChannel) { + return mChannel->GetResponseStatus(aStatus); + } + + return NS_OK; +} + +/* readonly attribute string statusText; */ +NS_IMETHODIMP +nsXMLHttpRequest::GetStatusText(char * *aStatusText) +{ + if (mChannel) { + return mChannel->GetResponseString(aStatusText); + } + + return NS_OK; +} + +/* void abort (); */ +NS_IMETHODIMP +nsXMLHttpRequest::Abort() +{ + if (mChannel) { + return mChannel->Cancel(NS_BINDING_ABORTED); + } + + return NS_OK; +} + +/* string getAllResponseHeaders (); */ +NS_IMETHODIMP +nsXMLHttpRequest::GetAllResponseHeaders(char **_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + if (mChannel) { + nsCOMPtr enumerator; + nsCAutoString headers; + + nsresult rv = mChannel->GetResponseHeaderEnumerator(getter_AddRefs(enumerator)); + if (NS_FAILED(rv)) { + return rv; + } + + PRBool hasMore; + while(NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) { + nsCOMPtr isup; + + rv = enumerator->GetNext(getter_AddRefs(isup)); + if (NS_FAILED(rv)) { + break; + } + nsCOMPtr header = do_QueryInterface(isup); + if (header) { + nsXPIDLCString name, value; + header->GetFieldName(getter_Copies(name)); + header->GetValue(getter_Copies(value)); + headers.Append((const char*)name); + headers.Append(": "); + headers.Append((const char*)value); + headers.Append("\n"); + } + } + *_retval = headers.ToNewCString(); + } + + return NS_OK; +} + +/* string getResponseHeader (in string header); */ +NS_IMETHODIMP +nsXMLHttpRequest::GetResponseHeader(const char *header, char **_retval) +{ + NS_ENSURE_ARG(header); + NS_ENSURE_ARG_POINTER(_retval); + + if (mChannel) { + nsCOMPtr headerAtom = dont_AddRef(NS_NewAtom(header)); + return mChannel->GetResponseHeader(headerAtom, _retval); + } + + return NS_OK; +} + +/* noscript void openRequest (in string method, in wstring url, in boolean async, in string user, in string password); */ +NS_IMETHODIMP +nsXMLHttpRequest::OpenRequest(const char *method, + const PRUnichar *url, + PRBool async, + const char *user, + const char *password) +{ + NS_ENSURE_ARG(method); + NS_ENSURE_ARG(url); + + nsresult rv; + nsCOMPtr uri, baseURI; + nsCOMPtr loadGroup; + PRBool authp = PR_FALSE; + + mAsync = async; + + // If we have a base document, use it for the base URL and loadgroup + if (mBaseDocument) { + baseURI = dont_AddRef(mBaseDocument->GetDocumentURL()); + rv = mBaseDocument->GetDocumentLoadGroup(getter_AddRefs(loadGroup)); + if (NS_FAILED(rv)) return rv; + } + + nsAutoString urlStr(url); + rv = NS_NewURI(getter_AddRefs(uri), urlStr, baseURI); + if (NS_FAILED(rv)) return rv; + + // Only http URLs are allowed + nsXPIDLCString protocol; + uri->GetScheme(getter_Copies(protocol)); + if (nsCRT::strcmp("http", (const char*)protocol) != 0) { + return NS_ERROR_INVALID_ARG; + } + + if (user) { + nsCAutoString prehost; + prehost.Assign(user); + if (password) { + prehost.Append(":"); + prehost.Append(password); + } + uri->SetPreHost(prehost.GetBuffer()); + authp = PR_TRUE; + } + + nsCOMPtr channel; + rv = NS_OpenURI(getter_AddRefs(channel), uri, nsnull, loadGroup); + if (NS_FAILED(rv)) return rv; + + mChannel = do_QueryInterface(channel); + if (!mChannel) { + return NS_ERROR_INVALID_ARG; + } + + mChannel->SetAuthTriedWithPrehost(authp); + nsCOMPtr methodAtom = dont_AddRef(NS_NewAtom(method)); + if (methodAtom) { + rv = mChannel->SetRequestMethod(methodAtom); + } + + return rv; +} + +/* void open (in string method, in wstring url); */ +NS_IMETHODIMP +nsXMLHttpRequest::Open(const char *method, const PRUnichar *url) +{ + nsresult rv; + PRBool async = PR_TRUE; + char* user = nsnull; + char* password = nsnull; + + nsCOMPtr cc; + NS_WITH_SERVICE(nsIXPConnect, xpc, nsIXPConnect::GetCID(), &rv); + if(NS_SUCCEEDED(rv)) { + rv = xpc->GetCurrentNativeCallContext(getter_AddRefs(cc)); + } + + if (NS_SUCCEEDED(rv) && cc) { + PRUint32 argc; + rv = cc->GetArgc(&argc); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; + + jsval* argv; + rv = cc->GetArgvPtr(&argv); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; + + JSContext* cx; + rv = cc->GetJSContext(&cx); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; + + if (argc > 2) { + JSBool bool; + JS_ValueToBoolean(cx, argv[2], &bool); + async = (PRBool)bool; + + if (argc > 3) { + JSString* userStr; + + userStr = JS_ValueToString(cx, argv[3]); + if (userStr) { + user = JS_GetStringBytes(userStr); + } + + if (argc > 4) { + JSString* passwordStr; + + passwordStr = JS_ValueToString(cx, argv[4]); + if (passwordStr) { + password = JS_GetStringBytes(passwordStr); + } + } + } + } + } + + return OpenRequest(method, url, async, user, password); +} + +nsresult +nsXMLHttpRequest::GetStreamForWString(const PRUnichar* aStr, + PRInt32 aLength, + nsIInputStream** aStream) +{ + nsresult rv; + nsCOMPtr encoder; + nsAutoString charsetStr; + char* postData; + + // We want to encode the string as utf-8, so get the right encoder + NS_WITH_SERVICE(nsICharsetConverterManager, + charsetConv, + kCharsetConverterManagerCID, + &rv); + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); + + charsetStr.AssignWithConversion("UTF-8"); + rv = charsetConv->GetUnicodeEncoder(&charsetStr, + getter_AddRefs(encoder)); + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); + + // Convert to utf-8 + PRInt32 charLength; + const PRUnichar* unicodeBuf = aStr; + PRInt32 unicodeLength = aLength; + + rv = encoder->GetMaxLength(unicodeBuf, unicodeLength, &charLength); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; + +#define MAX_HEADER_SIZE 128 + + // Allocate extra space for the header and trailing CRLF + postData = (char*)nsMemory::Alloc(MAX_HEADER_SIZE + charLength + 3); + if (!postData) { + return NS_ERROR_OUT_OF_MEMORY; + } + rv = encoder->Convert(unicodeBuf, + &unicodeLength, postData+MAX_HEADER_SIZE, &charLength); + if (NS_FAILED(rv)) { + nsMemory::Free(postData); + return NS_ERROR_FAILURE; + } + + // Now that we know the real content length we can create the header + PR_snprintf(postData, + MAX_HEADER_SIZE, + "Content-type: text/xml\015\012Content-Length: %d\015\012\015\012", + charLength); + PRInt32 headerSize = nsCRT::strlen(postData); + + // Copy the post data to immediately follow the header + nsCRT::memcpy(postData+headerSize, postData+MAX_HEADER_SIZE, charLength); + + // Shove in the traling CRLF + postData[headerSize+charLength] = CR; + postData[headerSize+charLength+1] = LF; + postData[headerSize+charLength+2] = '\0'; + + // The new stream takes ownership of the buffer + rv = NS_NewByteArrayInputStream((nsIByteArrayInputStream**)aStream, + postData, + headerSize+charLength+2); + if (NS_FAILED(rv)) { + nsMemory::Free(postData); + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +/* void send (in nsISupports body); */ +NS_IMETHODIMP +nsXMLHttpRequest::Send(nsISupports *body) +{ + NS_ENSURE_ARG(body); + + nsresult rv; + nsCOMPtr postDataStream; + + if (!mChannel) { + return NS_ERROR_NOT_INITIALIZED; + } + + nsCOMPtr doc = do_QueryInterface(body); + if (doc) { + // Get an XML serializer + nsCOMPtr serializer = do_CreateInstance(NS_XMLSERIALIZER_PROGID, &rv); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; + + // Serialize the current document to string + nsXPIDLString serial; + rv = serializer->SerializeToString(doc, getter_Copies(serial)); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; + + // Convert to a byte stream + rv = GetStreamForWString((const PRUnichar*)serial, + nsCRT::strlen((const PRUnichar*)serial), + getter_AddRefs(postDataStream)); + if (NS_FAILED(rv)) return rv; + } + else { + nsCOMPtr stream = do_QueryInterface(body); + if (stream) { + postDataStream = stream; + } + else { + nsCOMPtr wstr = do_QueryInterface(body); + if (wstr) { + nsXPIDLString holder; + wstr->GetData(getter_Copies(holder)); + rv = GetStreamForWString((const PRUnichar*)holder, + nsCRT::strlen((const PRUnichar*)holder), + getter_AddRefs(postDataStream)); + if (NS_FAILED(rv)) return rv; + } + } + } + + if (postDataStream) { + rv = mChannel->SetUploadStream(postDataStream); + } + + // Get and initialize a DOMImplementation + nsCOMPtr implementation = do_CreateInstance(kIDOMDOMImplementationCID, &rv); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; + + if (mBaseDocument) { + nsCOMPtr privImpl = do_QueryInterface(implementation); + if (privImpl) { + privImpl->Init(mBaseDocument); + } + } + + // Create an empty document from it + nsAutoString emptyStr; + rv = implementation->CreateDocument(emptyStr, + emptyStr, + nsnull, + getter_AddRefs(mDocument)); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; + + // Register as a load listener on the document + nsCOMPtr target = do_QueryInterface(mDocument); + if (target) { + nsAutoString loadStr; + loadStr.AssignWithConversion("load"); + rv = target->AddEventListener(loadStr, + NS_STATIC_CAST(nsIDOMEventListener*, this), + PR_FALSE); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; + } + + // Tell the document to start loading + nsCOMPtr listener; + nsCOMPtr document = do_QueryInterface(mDocument); + if (!document) { + return NS_ERROR_FAILURE; + } + rv = document->StartDocumentLoad(kLoadAsData, mChannel, + nsnull, nsnull, + getter_AddRefs(listener), + PR_FALSE); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; + + // Start reading from the channel + rv = mChannel->AsyncRead(listener, nsnull); + if (!mAsync) { + // XXX spin an event loop here and wait + } + + return rv; +} + +/* void setRequestHeader (in string header, in string value); */ +NS_IMETHODIMP +nsXMLHttpRequest::SetRequestHeader(const char *header, const char *value) +{ + if (mChannel) { + nsCOMPtr headerAtom = dont_AddRef(NS_NewAtom(header)); + return mChannel->SetRequestHeader(headerAtom, value); + } + + return NS_OK; +} + +// nsIDOMEventListener +nsresult +nsXMLHttpRequest::HandleEvent(nsIDOMEvent* aEvent) +{ + return NS_OK; +} + + +// nsIDOMLoadListener +nsresult +nsXMLHttpRequest::Load(nsIDOMEvent* aEvent) +{ + mComplete = PR_TRUE; + if (mLoadEventListeners) { + PRUint32 index, count; + + mLoadEventListeners->Count(&count); + for (index = 0; index < count; index++) { + nsCOMPtr current = dont_AddRef(mLoadEventListeners->ElementAt(index)); + if (current) { + nsCOMPtr listener = do_QueryInterface(current); + if (listener) { + listener->HandleEvent(aEvent); + } + } + } + } + + return NS_OK; +} + +nsresult +nsXMLHttpRequest::Unload(nsIDOMEvent* aEvent) +{ + return NS_OK; +} + +nsresult +nsXMLHttpRequest::Abort(nsIDOMEvent* aEvent) +{ + return NS_OK; +} + +nsresult +nsXMLHttpRequest::Error(nsIDOMEvent* aEvent) +{ + if (mErrorEventListeners) { + PRUint32 index, count; + + mErrorEventListeners->Count(&count); + for (index = 0; index < count; index++) { + nsCOMPtr current = dont_AddRef(mErrorEventListeners->ElementAt(index)); + if (current) { + nsCOMPtr listener = do_QueryInterface(current); + if (listener) { + listener->HandleEvent(aEvent); + } + } + } + } + + return NS_OK; +} + diff --git a/content/base/src/nsXMLHttpRequest.h b/content/base/src/nsXMLHttpRequest.h new file mode 100644 index 00000000000..4e6730da060 --- /dev/null +++ b/content/base/src/nsXMLHttpRequest.h @@ -0,0 +1,111 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + */ + +#ifndef nsXMLHttpRequest_h__ +#define nsXMLHttpRequest_h__ + +#include "nsIXMLHttpRequest.h" +#include "nsISupportsUtils.h" +#include "nsCOMPtr.h" +#include "nsString.h" +#include "nsIDOMLoadListener.h" +#include "nsIDOMDocument.h" +#include "nsIHTTPChannel.h" +#include "nsIDocument.h" +#include "nsISupportsArray.h" +#include "jsapi.h" + +class nsXMLHttpRequest : public nsIXMLHttpRequest, + public nsIDOMLoadListener +{ +public: + nsXMLHttpRequest(); + virtual ~nsXMLHttpRequest(); + + NS_DECL_ISUPPORTS + + // nsIXMLHttpRequest + NS_DECL_NSIXMLHTTPREQUEST + + // nsIDOMEventListener + virtual nsresult HandleEvent(nsIDOMEvent* aEvent); + + // nsIDOMLoadListener + virtual nsresult Load(nsIDOMEvent* aEvent); + virtual nsresult Unload(nsIDOMEvent* aEvent); + virtual nsresult Abort(nsIDOMEvent* aEvent); + virtual nsresult Error(nsIDOMEvent* aEvent); + +protected: + nsresult MakeScriptEventListener(nsISupports* aObject, + nsIDOMEventListener** aListener); + void GetScriptEventListener(nsISupportsArray* aList, + nsIDOMEventListener** aListener); + PRBool StuffReturnValue(nsIDOMEventListener* aListener); + + + nsresult GetStreamForWString(const PRUnichar* aStr, + PRInt32 aLength, + nsIInputStream** aStream); + + nsCOMPtr mChannel; + nsCOMPtr mDocument; + nsCOMPtr mBaseDocument; + nsCOMPtr mLoadEventListeners; + nsCOMPtr mErrorEventListeners; + PRBool mComplete; + PRBool mAsync; +}; + +#define NS_IPRIVATEJSEVENTLISTENER_IID \ + { /* d47a6550-4327-11d4-9a45-000064657374 */ \ + 0xd47a6550, 0x4327, 0x11d4, \ + {0x9a, 0x45, 0x00, 0x00, 0x64, 0x65, 0x73, 0x74} } + +class nsIPrivateJSEventListener : public nsISupports { +public: + NS_DEFINE_STATIC_IID_ACCESSOR(NS_IPRIVATEJSEVENTLISTENER_IID) + + NS_IMETHOD GetFunctionObj(JSObject** aObj) = 0; +}; + +class nsXMLHttpRequestScriptListener : public nsIDOMEventListener, + public nsIPrivateJSEventListener +{ +public: + nsXMLHttpRequestScriptListener(JSObject* aScopeObj, JSObject* aFunctionObj); + virtual ~nsXMLHttpRequestScriptListener(); + + NS_DECL_ISUPPORTS + + // nsIDOMEventListener + virtual nsresult HandleEvent(nsIDOMEvent* aEvent); + + // nsIPrivateJSEventListener + NS_IMETHOD GetFunctionObj(JSObject** aObj); + +protected: + JSObject* mScopeObj; + JSObject* mFunctionObj; +}; + +#endif