/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the QtDeclarative module of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** GNU Lesser General Public License Usage | |
** This file may be used under the terms of the GNU Lesser General Public | |
** License version 2.1 as published by the Free Software Foundation and | |
** appearing in the file LICENSE.LGPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU Lesser | |
** General Public License version 2.1 requirements will be met: | |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. | |
** | |
** In addition, as a special exception, Nokia gives you certain additional | |
** rights. These rights are described in the Nokia Qt LGPL Exception | |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** Other Usage | |
** Alternatively, this file may be used in accordance with the terms and | |
** conditions contained in a signed written agreement between you and Nokia. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include "private/qdeclarativexmlhttprequest_p.h" | |
#include "qdeclarativeengine.h" | |
#include "private/qdeclarativeengine_p.h" | |
#include "private/qdeclarativerefcount_p.h" | |
#include "private/qdeclarativeengine_p.h" | |
#include "private/qdeclarativeexpression_p.h" | |
#include "qdeclarativeglobal_p.h" | |
#include <QtCore/qobject.h> | |
#include <QtScript/qscriptvalue.h> | |
#include <QtScript/qscriptcontext.h> | |
#include <QtScript/qscriptengine.h> | |
#include <QtNetwork/qnetworkreply.h> | |
#include <QtCore/qtextcodec.h> | |
#include <QtCore/qxmlstream.h> | |
#include <QtCore/qstack.h> | |
#include <QtCore/qdebug.h> | |
#ifndef QT_NO_XMLSTREAMREADER | |
// From DOM-Level-3-Core spec | |
// http://www.w3.org/TR/DOM-Level-3-Core/core.html | |
#define INDEX_SIZE_ERR 1 | |
#define DOMSTRING_SIZE_ERR 2 | |
#define HIERARCHY_REQUEST_ERR 3 | |
#define WRONG_DOCUMENT_ERR 4 | |
#define INVALID_CHARACTER_ERR 5 | |
#define NO_DATA_ALLOWED_ERR 6 | |
#define NO_MODIFICATION_ALLOWED_ERR 7 | |
#define NOT_FOUND_ERR 8 | |
#define NOT_SUPPORTED_ERR 9 | |
#define INUSE_ATTRIBUTE_ERR 10 | |
#define INVALID_STATE_ERR 11 | |
#define SYNTAX_ERR 12 | |
#define INVALID_MODIFICATION_ERR 13 | |
#define NAMESPACE_ERR 14 | |
#define INVALID_ACCESS_ERR 15 | |
#define VALIDATION_ERR 16 | |
#define TYPE_MISMATCH_ERR 17 | |
#define THROW_DOM(error, desc) \ | |
{ \ | |
QScriptValue errorValue = context->throwError(QLatin1String(desc)); \ | |
errorValue.setProperty(QLatin1String("code"), error); \ | |
return errorValue; \ | |
} | |
#define THROW_SYNTAX(desc) \ | |
return context->throwError(QScriptContext::SyntaxError, QLatin1String(desc)); | |
#define THROW_REFERENCE(desc) \ | |
return context->throwError(QScriptContext::ReferenceError, QLatin1String(desc)); | |
#define D(arg) (arg)->release() | |
#define A(arg) (arg)->addref() | |
QT_BEGIN_NAMESPACE | |
DEFINE_BOOL_CONFIG_OPTION(xhrDump, QML_XHR_DUMP); | |
class DocumentImpl; | |
class NodeImpl | |
{ | |
public: | |
NodeImpl() : type(Element), document(0), parent(0) {} | |
virtual ~NodeImpl() { | |
for (int ii = 0; ii < children.count(); ++ii) | |
delete children.at(ii); | |
for (int ii = 0; ii < attributes.count(); ++ii) | |
delete attributes.at(ii); | |
} | |
// These numbers are copied from the Node IDL definition | |
enum Type { | |
Attr = 2, | |
CDATA = 4, | |
Comment = 8, | |
Document = 9, | |
DocumentFragment = 11, | |
DocumentType = 10, | |
Element = 1, | |
Entity = 6, | |
EntityReference = 5, | |
Notation = 12, | |
ProcessingInstruction = 7, | |
Text = 3 | |
}; | |
Type type; | |
QString namespaceUri; | |
QString name; | |
QString data; | |
void addref(); | |
void release(); | |
DocumentImpl *document; | |
NodeImpl *parent; | |
QList<NodeImpl *> children; | |
QList<NodeImpl *> attributes; | |
}; | |
class DocumentImpl : public QDeclarativeRefCount, public NodeImpl | |
{ | |
public: | |
DocumentImpl() : root(0) { type = Document; } | |
virtual ~DocumentImpl() { | |
if (root) delete root; | |
} | |
QString version; | |
QString encoding; | |
bool isStandalone; | |
NodeImpl *root; | |
void addref() { QDeclarativeRefCount::addref(); } | |
void release() { QDeclarativeRefCount::release(); } | |
}; | |
class NamedNodeMap | |
{ | |
public: | |
// JS API | |
static QScriptValue length(QScriptContext *context, QScriptEngine *engine); | |
// C++ API | |
static QScriptValue prototype(QScriptEngine *); | |
static QScriptValue create(QScriptEngine *, NodeImpl *, QList<NodeImpl *> *); | |
NamedNodeMap(); | |
NamedNodeMap(const NamedNodeMap &); | |
~NamedNodeMap(); | |
bool isNull(); | |
NodeImpl *d; | |
QList<NodeImpl *> *list; | |
private: | |
NamedNodeMap &operator=(const NamedNodeMap &); | |
}; | |
class NamedNodeMapClass : public QScriptClass | |
{ | |
public: | |
NamedNodeMapClass(QScriptEngine *engine) : QScriptClass(engine) {} | |
virtual QueryFlags queryProperty(const QScriptValue &object, const QScriptString &name, QueryFlags flags, uint *id); | |
virtual QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); | |
}; | |
class NodeList | |
{ | |
public: | |
// JS API | |
static QScriptValue length(QScriptContext *context, QScriptEngine *engine); | |
// C++ API | |
static QScriptValue prototype(QScriptEngine *); | |
static QScriptValue create(QScriptEngine *, NodeImpl *); | |
NodeList(); | |
NodeList(const NodeList &); | |
~NodeList(); | |
bool isNull(); | |
NodeImpl *d; | |
private: | |
NodeList &operator=(const NodeList &); | |
}; | |
class NodeListClass : public QScriptClass | |
{ | |
public: | |
NodeListClass(QScriptEngine *engine) : QScriptClass(engine) {} | |
virtual QueryFlags queryProperty(const QScriptValue &object, const QScriptString &name, QueryFlags flags, uint *id); | |
virtual QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); | |
}; | |
class Node | |
{ | |
public: | |
// JS API | |
static QScriptValue nodeName(QScriptContext *context, QScriptEngine *engine); | |
static QScriptValue nodeValue(QScriptContext *context, QScriptEngine *engine); | |
static QScriptValue nodeType(QScriptContext *context, QScriptEngine *engine); | |
static QScriptValue parentNode(QScriptContext *context, QScriptEngine *engine); | |
static QScriptValue childNodes(QScriptContext *context, QScriptEngine *engine); | |
static QScriptValue firstChild(QScriptContext *context, QScriptEngine *engine); | |
static QScriptValue lastChild(QScriptContext *context, QScriptEngine *engine); | |
static QScriptValue previousSibling(QScriptContext *context, QScriptEngine *engine); | |
static QScriptValue nextSibling(QScriptContext *context, QScriptEngine *engine); | |
static QScriptValue attributes(QScriptContext *context, QScriptEngine *engine); | |
//static QScriptValue ownerDocument(QScriptContext *context, QScriptEngine *engine); | |
//static QScriptValue namespaceURI(QScriptContext *context, QScriptEngine *engine); | |
//static QScriptValue prefix(QScriptContext *context, QScriptEngine *engine); | |
//static QScriptValue localName(QScriptContext *context, QScriptEngine *engine); | |
//static QScriptValue baseURI(QScriptContext *context, QScriptEngine *engine); | |
//static QScriptValue textContent(QScriptContext *context, QScriptEngine *engine); | |
// C++ API | |
static QScriptValue prototype(QScriptEngine *); | |
static QScriptValue create(QScriptEngine *, NodeImpl *); | |
Node(); | |
Node(const Node &o); | |
~Node(); | |
bool isNull() const; | |
NodeImpl *d; | |
private: | |
Node &operator=(const Node &); | |
}; | |
class Element : public Node | |
{ | |
public: | |
// C++ API | |
static QScriptValue prototype(QScriptEngine *); | |
}; | |
class Attr : public Node | |
{ | |
public: | |
// JS API | |
static QScriptValue name(QScriptContext *context, QScriptEngine *engine); | |
static QScriptValue specified(QScriptContext *context, QScriptEngine *engine); | |
static QScriptValue value(QScriptContext *context, QScriptEngine *engine); | |
static QScriptValue ownerElement(QScriptContext *context, QScriptEngine *engine); | |
static QScriptValue schemaTypeInfo(QScriptContext *context, QScriptEngine *engine); | |
static QScriptValue isId(QScriptContext *context, QScriptEngine *engine); | |
// C++ API | |
static QScriptValue prototype(QScriptEngine *); | |
}; | |
class CharacterData : public Node | |
{ | |
public: | |
// JS API | |
static QScriptValue length(QScriptContext *context, QScriptEngine *engine); | |
// C++ API | |
static QScriptValue prototype(QScriptEngine *); | |
}; | |
class Text : public CharacterData | |
{ | |
public: | |
// JS API | |
static QScriptValue isElementContentWhitespace(QScriptContext *context, QScriptEngine *engine); | |
static QScriptValue wholeText(QScriptContext *context, QScriptEngine *engine); | |
// C++ API | |
static QScriptValue prototype(QScriptEngine *); | |
}; | |
class CDATA : public Text | |
{ | |
public: | |
// C++ API | |
static QScriptValue prototype(QScriptEngine *); | |
}; | |
class Document : public Node | |
{ | |
public: | |
// JS API | |
static QScriptValue xmlVersion(QScriptContext *context, QScriptEngine *engine); | |
static QScriptValue xmlEncoding(QScriptContext *context, QScriptEngine *engine); | |
static QScriptValue xmlStandalone(QScriptContext *context, QScriptEngine *engine); | |
static QScriptValue documentElement(QScriptContext *context, QScriptEngine *engine); | |
// C++ API | |
static QScriptValue prototype(QScriptEngine *); | |
static QScriptValue load(QScriptEngine *engine, const QByteArray &data); | |
}; | |
QT_END_NAMESPACE | |
Q_DECLARE_METATYPE(Node) | |
Q_DECLARE_METATYPE(NodeList) | |
Q_DECLARE_METATYPE(NamedNodeMap) | |
QT_BEGIN_NAMESPACE | |
void NodeImpl::addref() | |
{ | |
A(document); | |
} | |
void NodeImpl::release() | |
{ | |
D(document); | |
} | |
QScriptValue Node::nodeName(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Node node = qscriptvalue_cast<Node>(context->thisObject()); | |
if (node.isNull()) return engine->undefinedValue(); | |
switch (node.d->type) { | |
case NodeImpl::Document: | |
return QScriptValue(QLatin1String("#document")); | |
case NodeImpl::CDATA: | |
return QScriptValue(QLatin1String("#cdata-section")); | |
case NodeImpl::Text: | |
return QScriptValue(QLatin1String("#text")); | |
default: | |
return QScriptValue(node.d->name); | |
} | |
} | |
QScriptValue Node::nodeValue(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Node node = qscriptvalue_cast<Node>(context->thisObject()); | |
if (node.isNull()) return engine->undefinedValue(); | |
if (node.d->type == NodeImpl::Document || | |
node.d->type == NodeImpl::DocumentFragment || | |
node.d->type == NodeImpl::DocumentType || | |
node.d->type == NodeImpl::Element || | |
node.d->type == NodeImpl::Entity || | |
node.d->type == NodeImpl::EntityReference || | |
node.d->type == NodeImpl::Notation) | |
return engine->nullValue(); | |
return QScriptValue(node.d->data); | |
} | |
QScriptValue Node::nodeType(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Node node = qscriptvalue_cast<Node>(context->thisObject()); | |
if (node.isNull()) return engine->undefinedValue(); | |
return QScriptValue(node.d->type); | |
} | |
QScriptValue Node::parentNode(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Node node = qscriptvalue_cast<Node>(context->thisObject()); | |
if (node.isNull()) return engine->undefinedValue(); | |
if (node.d->parent) return Node::create(engine, node.d->parent); | |
else return engine->nullValue(); | |
} | |
QScriptValue Node::childNodes(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Node node = qscriptvalue_cast<Node>(context->thisObject()); | |
if (node.isNull()) return engine->undefinedValue(); | |
return NodeList::create(engine, node.d); | |
} | |
QScriptValue Node::firstChild(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Node node = qscriptvalue_cast<Node>(context->thisObject()); | |
if (node.isNull()) return engine->undefinedValue(); | |
if (node.d->children.isEmpty()) return engine->nullValue(); | |
else return Node::create(engine, node.d->children.first()); | |
} | |
QScriptValue Node::lastChild(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Node node = qscriptvalue_cast<Node>(context->thisObject()); | |
if (node.isNull()) return engine->undefinedValue(); | |
if (node.d->children.isEmpty()) return engine->nullValue(); | |
else return Node::create(engine, node.d->children.last()); | |
} | |
QScriptValue Node::previousSibling(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Node node = qscriptvalue_cast<Node>(context->thisObject()); | |
if (node.isNull()) return engine->undefinedValue(); | |
if (!node.d->parent) return engine->nullValue(); | |
for (int ii = 0; ii < node.d->parent->children.count(); ++ii) { | |
if (node.d->parent->children.at(ii) == node.d) { | |
if (ii == 0) return engine->nullValue(); | |
else return Node::create(engine, node.d->parent->children.at(ii - 1)); | |
} | |
} | |
return engine->nullValue(); | |
} | |
QScriptValue Node::nextSibling(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Node node = qscriptvalue_cast<Node>(context->thisObject()); | |
if (node.isNull()) return engine->undefinedValue(); | |
if (!node.d->parent) return engine->nullValue(); | |
for (int ii = 0; ii < node.d->parent->children.count(); ++ii) { | |
if (node.d->parent->children.at(ii) == node.d) { | |
if ((ii + 1) == node.d->parent->children.count()) return engine->nullValue(); | |
else return Node::create(engine, node.d->parent->children.at(ii + 1)); | |
} | |
} | |
return engine->nullValue(); | |
} | |
QScriptValue Node::attributes(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Node node = qscriptvalue_cast<Node>(context->thisObject()); | |
if (node.isNull()) return engine->undefinedValue(); | |
if (node.d->type != NodeImpl::Element) | |
return engine->nullValue(); | |
else | |
return NamedNodeMap::create(engine, node.d, &node.d->attributes); | |
} | |
QScriptValue Node::prototype(QScriptEngine *engine) | |
{ | |
QScriptValue proto = engine->newObject(); | |
proto.setProperty(QLatin1String("nodeName"), engine->newFunction(nodeName), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
proto.setProperty(QLatin1String("nodeValue"), engine->newFunction(nodeValue), QScriptValue::ReadOnly | QScriptValue::PropertyGetter | QScriptValue::PropertySetter); | |
proto.setProperty(QLatin1String("nodeType"), engine->newFunction(nodeType), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
proto.setProperty(QLatin1String("parentNode"), engine->newFunction(parentNode), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
proto.setProperty(QLatin1String("childNodes"), engine->newFunction(childNodes), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
proto.setProperty(QLatin1String("firstChild"), engine->newFunction(firstChild), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
proto.setProperty(QLatin1String("lastChild"), engine->newFunction(lastChild), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
proto.setProperty(QLatin1String("previousSibling"), engine->newFunction(previousSibling), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
proto.setProperty(QLatin1String("nextSibling"), engine->newFunction(nextSibling), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
proto.setProperty(QLatin1String("attributes"), engine->newFunction(attributes), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
return proto; | |
} | |
QScriptValue Node::create(QScriptEngine *engine, NodeImpl *data) | |
{ | |
QScriptValue instance = engine->newObject(); | |
switch (data->type) { | |
case NodeImpl::Attr: | |
instance.setPrototype(Attr::prototype(engine)); | |
break; | |
case NodeImpl::Comment: | |
case NodeImpl::Document: | |
case NodeImpl::DocumentFragment: | |
case NodeImpl::DocumentType: | |
case NodeImpl::Entity: | |
case NodeImpl::EntityReference: | |
case NodeImpl::Notation: | |
case NodeImpl::ProcessingInstruction: | |
return QScriptValue(); | |
case NodeImpl::CDATA: | |
instance.setPrototype(CDATA::prototype(engine)); | |
break; | |
case NodeImpl::Text: | |
instance.setPrototype(Text::prototype(engine)); | |
break; | |
case NodeImpl::Element: | |
instance.setPrototype(Element::prototype(engine)); | |
break; | |
} | |
Node node; | |
node.d = data; | |
if (data) A(data); | |
return engine->newVariant(instance, qVariantFromValue(node)); | |
} | |
QScriptValue Element::prototype(QScriptEngine *engine) | |
{ | |
QScriptValue proto = engine->newObject(); | |
proto.setPrototype(Node::prototype(engine)); | |
proto.setProperty(QLatin1String("tagName"), engine->newFunction(nodeName), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
return proto; | |
} | |
QScriptValue Attr::prototype(QScriptEngine *engine) | |
{ | |
QScriptValue proto = engine->newObject(); | |
proto.setPrototype(Node::prototype(engine)); | |
proto.setProperty(QLatin1String("name"), engine->newFunction(name), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
proto.setProperty(QLatin1String("value"), engine->newFunction(value), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
proto.setProperty(QLatin1String("ownerElement"), engine->newFunction(ownerElement), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
return proto; | |
} | |
QScriptValue Attr::name(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Node node = qscriptvalue_cast<Node>(context->thisObject()); | |
if (node.isNull()) return engine->undefinedValue(); | |
return QScriptValue(node.d->name); | |
} | |
QScriptValue Attr::value(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Node node = qscriptvalue_cast<Node>(context->thisObject()); | |
if (node.isNull()) return engine->undefinedValue(); | |
return QScriptValue(node.d->data); | |
} | |
QScriptValue Attr::ownerElement(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Node node = qscriptvalue_cast<Node>(context->thisObject()); | |
if (node.isNull()) return engine->undefinedValue(); | |
return Node::create(engine, node.d->parent); | |
} | |
QScriptValue CharacterData::length(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Node node = qscriptvalue_cast<Node>(context->thisObject()); | |
if (node.isNull()) return engine->undefinedValue(); | |
return QScriptValue(node.d->data.length()); | |
} | |
QScriptValue CharacterData::prototype(QScriptEngine *engine) | |
{ | |
QScriptValue proto = engine->newObject(); | |
proto.setPrototype(Node::prototype(engine)); | |
proto.setProperty(QLatin1String("data"), engine->newFunction(nodeValue), QScriptValue::ReadOnly | QScriptValue::PropertyGetter | QScriptValue::PropertySetter); | |
proto.setProperty(QLatin1String("length"), engine->newFunction(length), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
return proto; | |
} | |
QScriptValue Text::isElementContentWhitespace(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Node node = qscriptvalue_cast<Node>(context->thisObject()); | |
if (node.isNull()) return engine->undefinedValue(); | |
return node.d->data.trimmed().isEmpty(); | |
} | |
QScriptValue Text::wholeText(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Node node = qscriptvalue_cast<Node>(context->thisObject()); | |
if (node.isNull()) return engine->undefinedValue(); | |
return node.d->data; | |
} | |
QScriptValue Text::prototype(QScriptEngine *engine) | |
{ | |
QScriptValue proto = engine->newObject(); | |
proto.setPrototype(CharacterData::prototype(engine)); | |
proto.setProperty(QLatin1String("isElementContentWhitespace"), engine->newFunction(isElementContentWhitespace), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
proto.setProperty(QLatin1String("wholeText"), engine->newFunction(wholeText), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
return proto; | |
} | |
QScriptValue CDATA::prototype(QScriptEngine *engine) | |
{ | |
QScriptValue proto = engine->newObject(); | |
proto.setPrototype(Text::prototype(engine)); | |
return proto; | |
} | |
QScriptValue Document::prototype(QScriptEngine *engine) | |
{ | |
QScriptValue proto = engine->newObject(); | |
proto.setPrototype(Node::prototype(engine)); | |
proto.setProperty(QLatin1String("xmlVersion"), engine->newFunction(xmlVersion), QScriptValue::ReadOnly | QScriptValue::PropertyGetter | QScriptValue::PropertySetter); | |
proto.setProperty(QLatin1String("xmlEncoding"), engine->newFunction(xmlEncoding), QScriptValue::ReadOnly | QScriptValue::PropertyGetter | QScriptValue::PropertySetter); | |
proto.setProperty(QLatin1String("xmlStandalone"), engine->newFunction(xmlStandalone), QScriptValue::ReadOnly | QScriptValue::PropertyGetter | QScriptValue::PropertySetter); | |
proto.setProperty(QLatin1String("documentElement"), engine->newFunction(documentElement), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
return proto; | |
} | |
QScriptValue Document::load(QScriptEngine *engine, const QByteArray &data) | |
{ | |
Q_ASSERT(engine); | |
DocumentImpl *document = 0; | |
QStack<NodeImpl *> nodeStack; | |
QXmlStreamReader reader(data); | |
while (!reader.atEnd()) { | |
switch (reader.readNext()) { | |
case QXmlStreamReader::NoToken: | |
break; | |
case QXmlStreamReader::Invalid: | |
break; | |
case QXmlStreamReader::StartDocument: | |
Q_ASSERT(!document); | |
document = new DocumentImpl; | |
document->document = document; | |
document->version = reader.documentVersion().toString(); | |
document->encoding = reader.documentEncoding().toString(); | |
document->isStandalone = reader.isStandaloneDocument(); | |
break; | |
case QXmlStreamReader::EndDocument: | |
break; | |
case QXmlStreamReader::StartElement: | |
{ | |
Q_ASSERT(document); | |
NodeImpl *node = new NodeImpl; | |
node->document = document; | |
node->namespaceUri = reader.namespaceUri().toString(); | |
node->name = reader.name().toString(); | |
if (nodeStack.isEmpty()) { | |
document->root = node; | |
} else { | |
node->parent = nodeStack.top(); | |
node->parent->children.append(node); | |
} | |
nodeStack.append(node); | |
foreach (const QXmlStreamAttribute &a, reader.attributes()) { | |
NodeImpl *attr = new NodeImpl; | |
attr->document = document; | |
attr->type = NodeImpl::Attr; | |
attr->namespaceUri = a.namespaceUri().toString(); | |
attr->name = a.name().toString(); | |
attr->data = a.value().toString(); | |
attr->parent = node; | |
node->attributes.append(attr); | |
} | |
} | |
break; | |
case QXmlStreamReader::EndElement: | |
nodeStack.pop(); | |
break; | |
case QXmlStreamReader::Characters: | |
{ | |
NodeImpl *node = new NodeImpl; | |
node->document = document; | |
node->type = reader.isCDATA()?NodeImpl::CDATA:NodeImpl::Text; | |
node->parent = nodeStack.top(); | |
node->parent->children.append(node); | |
node->data = reader.text().toString(); | |
} | |
break; | |
case QXmlStreamReader::Comment: | |
break; | |
case QXmlStreamReader::DTD: | |
break; | |
case QXmlStreamReader::EntityReference: | |
break; | |
case QXmlStreamReader::ProcessingInstruction: | |
break; | |
} | |
} | |
if (!document || reader.hasError()) { | |
if (document) D(document); | |
return engine->nullValue(); | |
} | |
QScriptValue instance = engine->newObject(); | |
instance.setPrototype(Document::prototype(engine)); | |
Node documentNode; | |
documentNode.d = document; | |
return engine->newVariant(instance, qVariantFromValue(documentNode)); | |
} | |
Node::Node() | |
: d(0) | |
{ | |
} | |
Node::Node(const Node &o) | |
: d(o.d) | |
{ | |
if (d) A(d); | |
} | |
Node::~Node() | |
{ | |
if (d) D(d); | |
} | |
bool Node::isNull() const | |
{ | |
return d == 0; | |
} | |
QScriptValue NamedNodeMap::length(QScriptContext *context, QScriptEngine *engine) | |
{ | |
NamedNodeMap map = qscriptvalue_cast<NamedNodeMap>(context->thisObject().data()); | |
if (map.isNull()) return engine->undefinedValue(); | |
return QScriptValue(map.list->count()); | |
} | |
QScriptValue NamedNodeMap::prototype(QScriptEngine *engine) | |
{ | |
QScriptValue proto = engine->newObject(); | |
proto.setProperty(QLatin1String("length"), engine->newFunction(length), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
return proto; | |
} | |
QScriptValue NamedNodeMap::create(QScriptEngine *engine, NodeImpl *data, QList<NodeImpl *> *list) | |
{ | |
QScriptValue instance = engine->newObject(); | |
instance.setPrototype(NamedNodeMap::prototype(engine)); | |
NamedNodeMap map; | |
map.d = data; | |
map.list = list; | |
if (data) A(data); | |
instance.setData(engine->newVariant(qVariantFromValue(map))); | |
if (!QDeclarativeScriptEngine::get(engine)->namedNodeMapClass) | |
QDeclarativeScriptEngine::get(engine)->namedNodeMapClass= new NamedNodeMapClass(engine); | |
instance.setScriptClass(QDeclarativeScriptEngine::get(engine)->namedNodeMapClass); | |
return instance; | |
} | |
NamedNodeMap::NamedNodeMap() | |
: d(0), list(0) | |
{ | |
} | |
NamedNodeMap::NamedNodeMap(const NamedNodeMap &o) | |
: d(o.d), list(o.list) | |
{ | |
if (d) A(d); | |
} | |
NamedNodeMap::~NamedNodeMap() | |
{ | |
if (d) D(d); | |
} | |
bool NamedNodeMap::isNull() | |
{ | |
return d == 0; | |
} | |
QScriptValue NodeList::length(QScriptContext *context, QScriptEngine *engine) | |
{ | |
NodeList list = qscriptvalue_cast<NodeList>(context->thisObject().data()); | |
if (list.isNull()) return engine->undefinedValue(); | |
return QScriptValue(list.d->children.count()); | |
} | |
QScriptValue NodeList::prototype(QScriptEngine *engine) | |
{ | |
QScriptValue proto = engine->newObject(); | |
proto.setProperty(QLatin1String("length"), engine->newFunction(length), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
return proto; | |
} | |
QScriptValue NodeList::create(QScriptEngine *engine, NodeImpl *data) | |
{ | |
QScriptValue instance = engine->newObject(); | |
instance.setPrototype(NodeList::prototype(engine)); | |
NodeList list; | |
list.d = data; | |
if (data) A(data); | |
instance.setData(engine->newVariant(qVariantFromValue(list))); | |
if (!QDeclarativeScriptEngine::get(engine)->nodeListClass) | |
QDeclarativeScriptEngine::get(engine)->nodeListClass= new NodeListClass(engine); | |
instance.setScriptClass(QDeclarativeScriptEngine::get(engine)->nodeListClass); | |
return instance; | |
} | |
NodeList::NodeList() | |
: d(0) | |
{ | |
} | |
NodeList::NodeList(const NodeList &o) | |
: d(o.d) | |
{ | |
if (d) A(d); | |
} | |
NodeList::~NodeList() | |
{ | |
if (d) D(d); | |
} | |
bool NodeList::isNull() | |
{ | |
return d == 0; | |
} | |
NamedNodeMapClass::QueryFlags NamedNodeMapClass::queryProperty(const QScriptValue &object, const QScriptString &name, QueryFlags flags, uint *id) | |
{ | |
if (!(flags & HandlesReadAccess)) | |
return 0; | |
NamedNodeMap map = qscriptvalue_cast<NamedNodeMap>(object.data()); | |
Q_ASSERT(!map.isNull()); | |
bool ok = false; | |
QString nameString = name.toString(); | |
uint index = nameString.toUInt(&ok); | |
if (ok) { | |
if ((uint)map.list->count() <= index) | |
return 0; | |
*id = index; | |
return HandlesReadAccess; | |
} else { | |
for (int ii = 0; ii < map.list->count(); ++ii) { | |
if (map.list->at(ii) && map.list->at(ii)->name == nameString) { | |
*id = ii; | |
return HandlesReadAccess; | |
} | |
} | |
} | |
return 0; | |
} | |
QScriptValue NamedNodeMapClass::property(const QScriptValue &object, const QScriptString &, uint id) | |
{ | |
NamedNodeMap map = qscriptvalue_cast<NamedNodeMap>(object.data()); | |
return Node::create(engine(), map.list->at(id)); | |
} | |
NodeListClass::QueryFlags NodeListClass::queryProperty(const QScriptValue &object, const QScriptString &name, QueryFlags flags, uint *id) | |
{ | |
if (!(flags & HandlesReadAccess)) | |
return 0; | |
bool ok = false; | |
uint index = name.toString().toUInt(&ok); | |
if (!ok) | |
return 0; | |
NodeList list = qscriptvalue_cast<NodeList>(object.data()); | |
if (list.isNull() || (uint)list.d->children.count() <= index) | |
return 0; // ### I think we're meant to raise an exception | |
*id = index; | |
return HandlesReadAccess; | |
} | |
QScriptValue NodeListClass::property(const QScriptValue &object, const QScriptString &, uint id) | |
{ | |
NodeList list = qscriptvalue_cast<NodeList>(object.data()); | |
return Node::create(engine(), list.d->children.at(id)); | |
} | |
QScriptValue Document::documentElement(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Node document = qscriptvalue_cast<Node>(context->thisObject()); | |
if (document.isNull() || document.d->type != NodeImpl::Document) return engine->undefinedValue(); | |
return Node::create(engine, static_cast<DocumentImpl *>(document.d)->root); | |
} | |
QScriptValue Document::xmlStandalone(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Node document = qscriptvalue_cast<Node>(context->thisObject()); | |
if (document.isNull() || document.d->type != NodeImpl::Document) return engine->undefinedValue(); | |
return QScriptValue(static_cast<DocumentImpl *>(document.d)->isStandalone); | |
} | |
QScriptValue Document::xmlVersion(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Node document = qscriptvalue_cast<Node>(context->thisObject()); | |
if (document.isNull() || document.d->type != NodeImpl::Document) return engine->undefinedValue(); | |
return QScriptValue(static_cast<DocumentImpl *>(document.d)->version); | |
} | |
QScriptValue Document::xmlEncoding(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Node document = qscriptvalue_cast<Node>(context->thisObject()); | |
if (document.isNull() || document.d->type != NodeImpl::Document) return engine->undefinedValue(); | |
return QScriptValue(static_cast<DocumentImpl *>(document.d)->encoding); | |
} | |
class QDeclarativeXMLHttpRequest : public QObject | |
{ | |
Q_OBJECT | |
public: | |
enum State { Unsent = 0, | |
Opened = 1, HeadersReceived = 2, | |
Loading = 3, Done = 4 }; | |
QDeclarativeXMLHttpRequest(QNetworkAccessManager *manager); | |
virtual ~QDeclarativeXMLHttpRequest(); | |
bool sendFlag() const; | |
bool errorFlag() const; | |
quint32 readyState() const; | |
int replyStatus() const; | |
QString replyStatusText() const; | |
QScriptValue open(QScriptValue *me, const QString &, const QUrl &); | |
void addHeader(const QString &, const QString &); | |
QString header(const QString &name); | |
QString headers(); | |
QScriptValue send(QScriptValue *me, const QByteArray &); | |
QScriptValue abort(QScriptValue *me); | |
QString responseBody(); | |
const QByteArray & rawResponseBody() const; | |
bool receivedXml() const; | |
private slots: | |
void downloadProgress(qint64); | |
void error(QNetworkReply::NetworkError); | |
void finished(); | |
private: | |
void requestFromUrl(const QUrl &url); | |
State m_state; | |
bool m_errorFlag; | |
bool m_sendFlag; | |
QString m_method; | |
QUrl m_url; | |
QByteArray m_responseEntityBody; | |
QByteArray m_data; | |
int m_redirectCount; | |
typedef QPair<QByteArray, QByteArray> HeaderPair; | |
typedef QList<HeaderPair> HeadersList; | |
HeadersList m_headersList; | |
void fillHeadersList(); | |
bool m_gotXml; | |
QByteArray m_mime; | |
QByteArray m_charset; | |
QTextCodec *m_textCodec; | |
#ifndef QT_NO_TEXTCODEC | |
QTextCodec* findTextCodec() const; | |
#endif | |
void readEncoding(); | |
QScriptValue m_me; // Set to the data object while a send() is ongoing (to access the callback) | |
QScriptValue dispatchCallback(QScriptValue *me); | |
void printError(const QScriptValue&); | |
int m_status; | |
QString m_statusText; | |
QNetworkRequest m_request; | |
QDeclarativeGuard<QNetworkReply> m_network; | |
void destroyNetwork(); | |
QNetworkAccessManager *m_nam; | |
QNetworkAccessManager *networkAccessManager() { return m_nam; } | |
}; | |
QDeclarativeXMLHttpRequest::QDeclarativeXMLHttpRequest(QNetworkAccessManager *manager) | |
: m_state(Unsent), m_errorFlag(false), m_sendFlag(false), | |
m_redirectCount(0), m_gotXml(false), m_textCodec(0), m_network(0), m_nam(manager) | |
{ | |
} | |
QDeclarativeXMLHttpRequest::~QDeclarativeXMLHttpRequest() | |
{ | |
destroyNetwork(); | |
} | |
bool QDeclarativeXMLHttpRequest::sendFlag() const | |
{ | |
return m_sendFlag; | |
} | |
bool QDeclarativeXMLHttpRequest::errorFlag() const | |
{ | |
return m_errorFlag; | |
} | |
quint32 QDeclarativeXMLHttpRequest::readyState() const | |
{ | |
return m_state; | |
} | |
int QDeclarativeXMLHttpRequest::replyStatus() const | |
{ | |
return m_status; | |
} | |
QString QDeclarativeXMLHttpRequest::replyStatusText() const | |
{ | |
return m_statusText; | |
} | |
QScriptValue QDeclarativeXMLHttpRequest::open(QScriptValue *me, const QString &method, const QUrl &url) | |
{ | |
destroyNetwork(); | |
m_sendFlag = false; | |
m_errorFlag = false; | |
m_responseEntityBody = QByteArray(); | |
m_method = method; | |
m_url = url; | |
m_state = Opened; | |
return dispatchCallback(me); | |
} | |
void QDeclarativeXMLHttpRequest::addHeader(const QString &name, const QString &value) | |
{ | |
QByteArray utfname = name.toUtf8(); | |
if (m_request.hasRawHeader(utfname)) { | |
m_request.setRawHeader(utfname, m_request.rawHeader(utfname) + ',' + value.toUtf8()); | |
} else { | |
m_request.setRawHeader(utfname, value.toUtf8()); | |
} | |
} | |
QString QDeclarativeXMLHttpRequest::header(const QString &name) | |
{ | |
QByteArray utfname = name.toLower().toUtf8(); | |
foreach (const HeaderPair &header, m_headersList) { | |
if (header.first == utfname) | |
return QString::fromUtf8(header.second); | |
} | |
return QString(); | |
} | |
QString QDeclarativeXMLHttpRequest::headers() | |
{ | |
QString ret; | |
foreach (const HeaderPair &header, m_headersList) { | |
if (ret.length()) | |
ret.append(QString::fromUtf8("\r\n")); | |
ret.append(QString::fromUtf8(header.first)); | |
ret.append(QString::fromUtf8(": ")); | |
ret.append(QString::fromUtf8(header.second)); | |
} | |
return ret; | |
} | |
void QDeclarativeXMLHttpRequest::fillHeadersList() | |
{ | |
QList<QByteArray> headerList = m_network->rawHeaderList(); | |
m_headersList.clear(); | |
foreach (const QByteArray &header, headerList) { | |
HeaderPair pair (header.toLower(), m_network->rawHeader(header)); | |
if (pair.first == "set-cookie" || | |
pair.first == "set-cookie2") | |
continue; | |
m_headersList << pair; | |
} | |
} | |
void QDeclarativeXMLHttpRequest::requestFromUrl(const QUrl &url) | |
{ | |
QNetworkRequest request = m_request; | |
request.setUrl(url); | |
if(m_method == QLatin1String("POST") || | |
m_method == QLatin1String("PUT")) { | |
QVariant var = request.header(QNetworkRequest::ContentTypeHeader); | |
if (var.isValid()) { | |
QString str = var.toString(); | |
int charsetIdx = str.indexOf(QLatin1String("charset=")); | |
if (charsetIdx == -1) { | |
// No charset - append | |
if (!str.isEmpty()) str.append(QLatin1Char(';')); | |
str.append(QLatin1String("charset=UTF-8")); | |
} else { | |
charsetIdx += 8; | |
int n = 0; | |
int semiColon = str.indexOf(QLatin1Char(';'), charsetIdx); | |
if (semiColon == -1) { | |
n = str.length() - charsetIdx; | |
} else { | |
n = semiColon - charsetIdx; | |
} | |
str.replace(charsetIdx, n, QLatin1String("UTF-8")); | |
} | |
request.setHeader(QNetworkRequest::ContentTypeHeader, str); | |
} else { | |
request.setHeader(QNetworkRequest::ContentTypeHeader, | |
QLatin1String("text/plain;charset=UTF-8")); | |
} | |
} | |
if (xhrDump()) { | |
qWarning().nospace() << "XMLHttpRequest: " << qPrintable(m_method) << " " << qPrintable(url.toString()); | |
if (!m_data.isEmpty()) { | |
qWarning().nospace() << " " | |
<< qPrintable(QString::fromUtf8(m_data)); | |
} | |
} | |
if (m_method == QLatin1String("GET")) | |
m_network = networkAccessManager()->get(request); | |
else if (m_method == QLatin1String("HEAD")) | |
m_network = networkAccessManager()->head(request); | |
else if(m_method == QLatin1String("POST")) | |
m_network = networkAccessManager()->post(request, m_data); | |
else if(m_method == QLatin1String("PUT")) | |
m_network = networkAccessManager()->put(request, m_data); | |
QObject::connect(m_network, SIGNAL(downloadProgress(qint64,qint64)), | |
this, SLOT(downloadProgress(qint64))); | |
QObject::connect(m_network, SIGNAL(error(QNetworkReply::NetworkError)), | |
this, SLOT(error(QNetworkReply::NetworkError))); | |
QObject::connect(m_network, SIGNAL(finished()), | |
this, SLOT(finished())); | |
} | |
QScriptValue QDeclarativeXMLHttpRequest::send(QScriptValue *me, const QByteArray &data) | |
{ | |
m_errorFlag = false; | |
m_sendFlag = true; | |
m_redirectCount = 0; | |
m_data = data; | |
m_me = *me; | |
requestFromUrl(m_url); | |
return QScriptValue(); | |
} | |
QScriptValue QDeclarativeXMLHttpRequest::abort(QScriptValue *me) | |
{ | |
destroyNetwork(); | |
m_responseEntityBody = QByteArray(); | |
m_errorFlag = true; | |
m_request = QNetworkRequest(); | |
if (!(m_state == Unsent || | |
(m_state == Opened && !m_sendFlag) || | |
m_state == Done)) { | |
m_state = Done; | |
m_sendFlag = false; | |
QScriptValue cbv = dispatchCallback(me); | |
if (cbv.isError()) return cbv; | |
} | |
m_state = Unsent; | |
return QScriptValue(); | |
} | |
void QDeclarativeXMLHttpRequest::downloadProgress(qint64 bytes) | |
{ | |
Q_UNUSED(bytes) | |
m_status = | |
m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); | |
m_statusText = | |
QString::fromUtf8(m_network->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray()); | |
// ### We assume if this is called the headers are now available | |
if (m_state < HeadersReceived) { | |
m_state = HeadersReceived; | |
fillHeadersList (); | |
QScriptValue cbv = dispatchCallback(&m_me); | |
if (cbv.isError()) printError(cbv); | |
} | |
bool wasEmpty = m_responseEntityBody.isEmpty(); | |
m_responseEntityBody.append(m_network->readAll()); | |
if (wasEmpty && !m_responseEntityBody.isEmpty()) { | |
m_state = Loading; | |
QScriptValue cbv = dispatchCallback(&m_me); | |
if (cbv.isError()) printError(cbv); | |
} | |
} | |
void QDeclarativeXMLHttpRequest::error(QNetworkReply::NetworkError error) | |
{ | |
Q_UNUSED(error) | |
m_status = | |
m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); | |
m_statusText = | |
QString::fromUtf8(m_network->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray()); | |
m_responseEntityBody = QByteArray(); | |
m_request = QNetworkRequest(); | |
m_data.clear(); | |
destroyNetwork(); | |
if (error == QNetworkReply::ContentAccessDenied || | |
error == QNetworkReply::ContentOperationNotPermittedError || | |
error == QNetworkReply::ContentNotFoundError || | |
error == QNetworkReply::AuthenticationRequiredError || | |
error == QNetworkReply::ContentReSendError) { | |
m_state = Loading; | |
QScriptValue cbv = dispatchCallback(&m_me); | |
if (cbv.isError()) printError(cbv); | |
} else { | |
m_errorFlag = true; | |
} | |
m_state = Done; | |
QScriptValue cbv = dispatchCallback(&m_me); | |
if (cbv.isError()) printError(cbv); | |
} | |
#define XMLHTTPREQUEST_MAXIMUM_REDIRECT_RECURSION 15 | |
void QDeclarativeXMLHttpRequest::finished() | |
{ | |
m_redirectCount++; | |
if (m_redirectCount < XMLHTTPREQUEST_MAXIMUM_REDIRECT_RECURSION) { | |
QVariant redirect = m_network->attribute(QNetworkRequest::RedirectionTargetAttribute); | |
if (redirect.isValid()) { | |
QUrl url = m_network->url().resolved(redirect.toUrl()); | |
destroyNetwork(); | |
requestFromUrl(url); | |
return; | |
} | |
} | |
m_status = | |
m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); | |
m_statusText = | |
QString::fromUtf8(m_network->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray()); | |
if (m_state < HeadersReceived) { | |
m_state = HeadersReceived; | |
fillHeadersList (); | |
QScriptValue cbv = dispatchCallback(&m_me); | |
if (cbv.isError()) printError(cbv); | |
} | |
m_responseEntityBody.append(m_network->readAll()); | |
readEncoding(); | |
if (xhrDump()) { | |
qWarning().nospace() << "XMLHttpRequest: RESPONSE " << qPrintable(m_url.toString()); | |
if (!m_responseEntityBody.isEmpty()) { | |
qWarning().nospace() << " " | |
<< qPrintable(QString::fromUtf8(m_responseEntityBody)); | |
} | |
} | |
m_data.clear(); | |
destroyNetwork(); | |
if (m_state < Loading) { | |
m_state = Loading; | |
QScriptValue cbv = dispatchCallback(&m_me); | |
if (cbv.isError()) printError(cbv); | |
} | |
m_state = Done; | |
QScriptValue cbv = dispatchCallback(&m_me); | |
if (cbv.isError()) printError(cbv); | |
m_me = QScriptValue(); | |
} | |
void QDeclarativeXMLHttpRequest::readEncoding() | |
{ | |
foreach (const HeaderPair &header, m_headersList) { | |
if (header.first == "content-type") { | |
int separatorIdx = header.second.indexOf(';'); | |
if (separatorIdx == -1) { | |
m_mime == header.second; | |
} else { | |
m_mime = header.second.mid(0, separatorIdx); | |
int charsetIdx = header.second.indexOf("charset="); | |
if (charsetIdx != -1) { | |
charsetIdx += 8; | |
separatorIdx = header.second.indexOf(';', charsetIdx); | |
m_charset = header.second.mid(charsetIdx, separatorIdx >= 0 ? separatorIdx : header.second.length()); | |
} | |
} | |
break; | |
} | |
} | |
if (m_mime.isEmpty() || m_mime == "text/xml" || m_mime == "application/xml" || m_mime.endsWith("+xml")) | |
m_gotXml = true; | |
} | |
bool QDeclarativeXMLHttpRequest::receivedXml() const | |
{ | |
return m_gotXml; | |
} | |
#ifndef QT_NO_TEXTCODEC | |
QTextCodec* QDeclarativeXMLHttpRequest::findTextCodec() const | |
{ | |
QTextCodec *codec = 0; | |
if (!m_charset.isEmpty()) | |
codec = QTextCodec::codecForName(m_charset); | |
if (!codec && m_gotXml) { | |
QXmlStreamReader reader(m_responseEntityBody); | |
reader.readNext(); | |
codec = QTextCodec::codecForName(reader.documentEncoding().toString().toUtf8()); | |
} | |
if (!codec && m_mime == "text/html") | |
codec = QTextCodec::codecForHtml(m_responseEntityBody, 0); | |
if (!codec) | |
codec = QTextCodec::codecForUtfText(m_responseEntityBody, 0); | |
if (!codec) | |
codec = QTextCodec::codecForName("UTF-8"); | |
return codec; | |
} | |
#endif | |
QString QDeclarativeXMLHttpRequest::responseBody() | |
{ | |
#ifndef QT_NO_TEXTCODEC | |
if (!m_textCodec) | |
m_textCodec = findTextCodec(); | |
if (m_textCodec) | |
return m_textCodec->toUnicode(m_responseEntityBody); | |
#endif | |
return QString::fromUtf8(m_responseEntityBody); | |
} | |
const QByteArray &QDeclarativeXMLHttpRequest::rawResponseBody() const | |
{ | |
return m_responseEntityBody; | |
} | |
QScriptValue QDeclarativeXMLHttpRequest::dispatchCallback(QScriptValue *me) | |
{ | |
QScriptValue v = me->property(QLatin1String("callback")); | |
return v.call(); | |
} | |
void QDeclarativeXMLHttpRequest::printError(const QScriptValue& sv) | |
{ | |
QDeclarativeError error; | |
QDeclarativeExpressionPrivate::exceptionToError(sv.engine(), error); | |
QDeclarativeEnginePrivate::warning(QDeclarativeEnginePrivate::get(sv.engine()), error); | |
} | |
void QDeclarativeXMLHttpRequest::destroyNetwork() | |
{ | |
if (m_network) { | |
m_network->disconnect(); | |
m_network->deleteLater(); | |
m_network = 0; | |
} | |
} | |
// XMLHttpRequest methods | |
static QScriptValue qmlxmlhttprequest_open(QScriptContext *context, QScriptEngine *engine) | |
{ | |
QScriptValue dataObject = context->thisObject().data(); | |
QDeclarativeXMLHttpRequest *request = qobject_cast<QDeclarativeXMLHttpRequest *>(dataObject.toQObject()); | |
if (!request) | |
THROW_REFERENCE("Not an XMLHttpRequest object"); | |
if (context->argumentCount() < 2 || context->argumentCount() > 5) | |
THROW_DOM(SYNTAX_ERR, "Incorrect argument count"); | |
// Argument 0 - Method | |
QString method = context->argument(0).toString().toUpper(); | |
if (method != QLatin1String("GET") && | |
method != QLatin1String("PUT") && | |
method != QLatin1String("HEAD") && | |
method != QLatin1String("POST")) | |
THROW_DOM(SYNTAX_ERR, "Unsupported HTTP method type"); | |
// Argument 1 - URL | |
QUrl url = QUrl::fromEncoded(context->argument(1).toString().toUtf8()); | |
if (url.isRelative()) { | |
url = QDeclarativeScriptEngine::get(engine)->resolvedUrl(context,url); | |
} | |
// Argument 2 - async (optional) | |
if (context->argumentCount() > 2 && !context->argument(2).toBoolean()) | |
THROW_DOM(NOT_SUPPORTED_ERR, "Synchronous XMLHttpRequest calls are not supported"); | |
// Argument 3/4 - user/pass (optional) | |
QString username, password; | |
if (context->argumentCount() > 3) | |
username = context->argument(3).toString(); | |
if (context->argumentCount() > 4) | |
password = context->argument(4).toString(); | |
// Clear the fragment (if any) | |
url.setFragment(QString()); | |
// Set username/password | |
if (!username.isNull()) url.setUserName(username); | |
if (!password.isNull()) url.setPassword(password); | |
return request->open(&dataObject, method, url); | |
} | |
static QScriptValue qmlxmlhttprequest_setRequestHeader(QScriptContext *context, QScriptEngine *engine) | |
{ | |
QDeclarativeXMLHttpRequest *request = qobject_cast<QDeclarativeXMLHttpRequest *>(context->thisObject().data().toQObject()); | |
if (!request) | |
THROW_REFERENCE("Not an XMLHttpRequest object"); | |
if (context->argumentCount() != 2) | |
THROW_DOM(SYNTAX_ERR, "Incorrect argument count"); | |
if (request->readyState() != QDeclarativeXMLHttpRequest::Opened || | |
request->sendFlag()) | |
THROW_DOM(INVALID_STATE_ERR, "Invalid state"); | |
QString name = context->argument(0).toString(); | |
QString value = context->argument(1).toString(); | |
// ### Check that name and value are well formed | |
QString nameUpper = name.toUpper(); | |
if (nameUpper == QLatin1String("ACCEPT-CHARSET") || | |
nameUpper == QLatin1String("ACCEPT-ENCODING") || | |
nameUpper == QLatin1String("CONNECTION") || | |
nameUpper == QLatin1String("CONTENT-LENGTH") || | |
nameUpper == QLatin1String("COOKIE") || | |
nameUpper == QLatin1String("COOKIE2") || | |
nameUpper == QLatin1String("CONTENT-TRANSFER-ENCODING") || | |
nameUpper == QLatin1String("DATE") || | |
nameUpper == QLatin1String("EXPECT") || | |
nameUpper == QLatin1String("HOST") || | |
nameUpper == QLatin1String("KEEP-ALIVE") || | |
nameUpper == QLatin1String("REFERER") || | |
nameUpper == QLatin1String("TE") || | |
nameUpper == QLatin1String("TRAILER") || | |
nameUpper == QLatin1String("TRANSFER-ENCODING") || | |
nameUpper == QLatin1String("UPGRADE") || | |
nameUpper == QLatin1String("USER-AGENT") || | |
nameUpper == QLatin1String("VIA") || | |
nameUpper.startsWith(QLatin1String("PROXY-")) || | |
nameUpper.startsWith(QLatin1String("SEC-"))) | |
return engine->undefinedValue(); | |
request->addHeader(nameUpper, value); | |
return engine->undefinedValue(); | |
} | |
static QScriptValue qmlxmlhttprequest_send(QScriptContext *context, QScriptEngine *) | |
{ | |
QScriptValue dataObject = context->thisObject().data(); | |
QDeclarativeXMLHttpRequest *request = qobject_cast<QDeclarativeXMLHttpRequest *>(dataObject.toQObject()); | |
if (!request) | |
THROW_REFERENCE("Not an XMLHttpRequest object"); | |
if (request->readyState() != QDeclarativeXMLHttpRequest::Opened) | |
THROW_DOM(INVALID_STATE_ERR, "Invalid state"); | |
if (request->sendFlag()) | |
THROW_DOM(INVALID_STATE_ERR, "Invalid state"); | |
QByteArray data; | |
if (context->argumentCount() > 0) | |
data = context->argument(0).toString().toUtf8(); | |
return request->send(&dataObject, data); | |
} | |
static QScriptValue qmlxmlhttprequest_abort(QScriptContext *context, QScriptEngine *) | |
{ | |
QScriptValue dataObject = context->thisObject().data(); | |
QDeclarativeXMLHttpRequest *request = qobject_cast<QDeclarativeXMLHttpRequest *>(dataObject.toQObject()); | |
if (!request) | |
THROW_REFERENCE("Not an XMLHttpRequest object"); | |
return request->abort(&dataObject); | |
} | |
static QScriptValue qmlxmlhttprequest_getResponseHeader(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Q_UNUSED(engine) | |
QDeclarativeXMLHttpRequest *request = qobject_cast<QDeclarativeXMLHttpRequest *>(context->thisObject().data().toQObject()); | |
if (!request) | |
THROW_REFERENCE("Not an XMLHttpRequest object"); | |
if (context->argumentCount() != 1) | |
THROW_DOM(SYNTAX_ERR, "Incorrect argument count"); | |
if (request->readyState() != QDeclarativeXMLHttpRequest::Loading && | |
request->readyState() != QDeclarativeXMLHttpRequest::Done && | |
request->readyState() != QDeclarativeXMLHttpRequest::HeadersReceived) | |
THROW_DOM(INVALID_STATE_ERR, "Invalid state"); | |
QString headerName = context->argument(0).toString(); | |
return QScriptValue(request->header(headerName)); | |
} | |
static QScriptValue qmlxmlhttprequest_getAllResponseHeaders(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Q_UNUSED(engine) | |
QDeclarativeXMLHttpRequest *request = qobject_cast<QDeclarativeXMLHttpRequest *>(context->thisObject().data().toQObject()); | |
if (!request) | |
THROW_REFERENCE("Not an XMLHttpRequest object"); | |
if (context->argumentCount() != 0) | |
THROW_DOM(SYNTAX_ERR, "Incorrect argument count"); | |
if (request->readyState() != QDeclarativeXMLHttpRequest::Loading && | |
request->readyState() != QDeclarativeXMLHttpRequest::Done && | |
request->readyState() != QDeclarativeXMLHttpRequest::HeadersReceived) | |
THROW_DOM(INVALID_STATE_ERR, "Invalid state"); | |
return QScriptValue(request->headers()); | |
} | |
// XMLHttpRequest properties | |
static QScriptValue qmlxmlhttprequest_readyState(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Q_UNUSED(engine) | |
QDeclarativeXMLHttpRequest *request = qobject_cast<QDeclarativeXMLHttpRequest *>(context->thisObject().data().toQObject()); | |
if (!request) | |
THROW_REFERENCE("Not an XMLHttpRequest object"); | |
return QScriptValue(request->readyState()); | |
} | |
static QScriptValue qmlxmlhttprequest_status(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Q_UNUSED(engine) | |
QDeclarativeXMLHttpRequest *request = qobject_cast<QDeclarativeXMLHttpRequest *>(context->thisObject().data().toQObject()); | |
if (!request) | |
THROW_REFERENCE("Not an XMLHttpRequest object"); | |
if (request->readyState() == QDeclarativeXMLHttpRequest::Unsent || | |
request->readyState() == QDeclarativeXMLHttpRequest::Opened) | |
THROW_DOM(INVALID_STATE_ERR, "Invalid state"); | |
if (request->errorFlag()) | |
return QScriptValue(0); | |
else | |
return QScriptValue(request->replyStatus()); | |
} | |
static QScriptValue qmlxmlhttprequest_statusText(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Q_UNUSED(engine) | |
QDeclarativeXMLHttpRequest *request = qobject_cast<QDeclarativeXMLHttpRequest *>(context->thisObject().data().toQObject()); | |
if (!request) | |
THROW_REFERENCE("Not an XMLHttpRequest object"); | |
if (request->readyState() == QDeclarativeXMLHttpRequest::Unsent || | |
request->readyState() == QDeclarativeXMLHttpRequest::Opened) | |
THROW_DOM(INVALID_STATE_ERR, "Invalid state"); | |
if (request->errorFlag()) | |
return QScriptValue(0); | |
else | |
return QScriptValue(request->replyStatusText()); | |
} | |
static QScriptValue qmlxmlhttprequest_responseText(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Q_UNUSED(engine) | |
QDeclarativeXMLHttpRequest *request = qobject_cast<QDeclarativeXMLHttpRequest *>(context->thisObject().data().toQObject()); | |
if (!request) | |
THROW_REFERENCE("Not an XMLHttpRequest object"); | |
if (request->readyState() != QDeclarativeXMLHttpRequest::Loading && | |
request->readyState() != QDeclarativeXMLHttpRequest::Done) | |
return QScriptValue(QString()); | |
else | |
return QScriptValue(request->responseBody()); | |
} | |
static QScriptValue qmlxmlhttprequest_responseXML(QScriptContext *context, QScriptEngine *engine) | |
{ | |
QDeclarativeXMLHttpRequest *request = qobject_cast<QDeclarativeXMLHttpRequest *>(context->thisObject().data().toQObject()); | |
if (!request) | |
THROW_REFERENCE("Not an XMLHttpRequest object"); | |
if (!request->receivedXml() || | |
(request->readyState() != QDeclarativeXMLHttpRequest::Loading && | |
request->readyState() != QDeclarativeXMLHttpRequest::Done)) | |
return engine->nullValue(); | |
else | |
return Document::load(engine, request->rawResponseBody()); | |
} | |
static QScriptValue qmlxmlhttprequest_onreadystatechange(QScriptContext *context, QScriptEngine *engine) | |
{ | |
Q_UNUSED(engine); | |
QScriptValue dataObject = context->thisObject().data(); | |
QDeclarativeXMLHttpRequest *request = qobject_cast<QDeclarativeXMLHttpRequest *>(dataObject.toQObject()); | |
if (!request) | |
THROW_REFERENCE("Not an XMLHttpRequest object"); | |
if (context->argumentCount()) { | |
QScriptValue v = context->argument(0); | |
dataObject.setProperty(QLatin1String("callback"), v); | |
return v; | |
} else { | |
return dataObject.property(QLatin1String("callback")); | |
} | |
} | |
// Constructor | |
static QScriptValue qmlxmlhttprequest_new(QScriptContext *context, QScriptEngine *engine) | |
{ | |
if (context->isCalledAsConstructor()) { | |
context->thisObject().setData(engine->newQObject(new QDeclarativeXMLHttpRequest(QDeclarativeScriptEngine::get(engine)->networkAccessManager()), QScriptEngine::ScriptOwnership)); | |
} | |
return engine->undefinedValue(); | |
} | |
void qt_add_qmlxmlhttprequest(QScriptEngine *engine) | |
{ | |
QScriptValue prototype = engine->newObject(); | |
// Methods | |
prototype.setProperty(QLatin1String("open"), engine->newFunction(qmlxmlhttprequest_open, 2)); | |
prototype.setProperty(QLatin1String("setRequestHeader"), engine->newFunction(qmlxmlhttprequest_setRequestHeader, 2)); | |
prototype.setProperty(QLatin1String("send"), engine->newFunction(qmlxmlhttprequest_send)); | |
prototype.setProperty(QLatin1String("abort"), engine->newFunction(qmlxmlhttprequest_abort)); | |
prototype.setProperty(QLatin1String("getResponseHeader"), engine->newFunction(qmlxmlhttprequest_getResponseHeader, 1)); | |
prototype.setProperty(QLatin1String("getAllResponseHeaders"), engine->newFunction(qmlxmlhttprequest_getAllResponseHeaders)); | |
// Read-only properties | |
prototype.setProperty(QLatin1String("readyState"), engine->newFunction(qmlxmlhttprequest_readyState), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
prototype.setProperty(QLatin1String("status"), engine->newFunction(qmlxmlhttprequest_status), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
prototype.setProperty(QLatin1String("statusText"), engine->newFunction(qmlxmlhttprequest_statusText), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
prototype.setProperty(QLatin1String("responseText"), engine->newFunction(qmlxmlhttprequest_responseText), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
prototype.setProperty(QLatin1String("responseXML"), engine->newFunction(qmlxmlhttprequest_responseXML), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); | |
prototype.setProperty(QLatin1String("onreadystatechange"), engine->newFunction(qmlxmlhttprequest_onreadystatechange), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); | |
// State values | |
prototype.setProperty(QLatin1String("UNSENT"), 0, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
prototype.setProperty(QLatin1String("OPENED"), 1, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
prototype.setProperty(QLatin1String("HEADERS_RECEIVED"), 2, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
prototype.setProperty(QLatin1String("LOADING"), 3, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
prototype.setProperty(QLatin1String("DONE"), 4, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
// Constructor | |
QScriptValue constructor = engine->newFunction(qmlxmlhttprequest_new, prototype); | |
constructor.setProperty(QLatin1String("UNSENT"), 0, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
constructor.setProperty(QLatin1String("OPENED"), 1, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
constructor.setProperty(QLatin1String("HEADERS_RECEIVED"), 2, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
constructor.setProperty(QLatin1String("LOADING"), 3, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
constructor.setProperty(QLatin1String("DONE"), 4, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
engine->globalObject().setProperty(QLatin1String("XMLHttpRequest"), constructor); | |
// DOM Exception | |
QScriptValue domExceptionPrototype = engine->newObject(); | |
domExceptionPrototype.setProperty(QLatin1String("INDEX_SIZE_ERR"), INDEX_SIZE_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
domExceptionPrototype.setProperty(QLatin1String("DOMSTRING_SIZE_ERR"), DOMSTRING_SIZE_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
domExceptionPrototype.setProperty(QLatin1String("HIERARCHY_REQUEST_ERR"), HIERARCHY_REQUEST_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
domExceptionPrototype.setProperty(QLatin1String("WRONG_DOCUMENT_ERR"), WRONG_DOCUMENT_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
domExceptionPrototype.setProperty(QLatin1String("INVALID_CHARACTER_ERR"), INVALID_CHARACTER_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
domExceptionPrototype.setProperty(QLatin1String("NO_DATA_ALLOWED_ERR"), NO_DATA_ALLOWED_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
domExceptionPrototype.setProperty(QLatin1String("NO_MODIFICATION_ALLOWED_ERR"), NO_MODIFICATION_ALLOWED_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
domExceptionPrototype.setProperty(QLatin1String("NOT_FOUND_ERR"), NOT_FOUND_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
domExceptionPrototype.setProperty(QLatin1String("NOT_SUPPORTED_ERR"), NOT_SUPPORTED_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
domExceptionPrototype.setProperty(QLatin1String("INUSE_ATTRIBUTE_ERR"), INUSE_ATTRIBUTE_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
domExceptionPrototype.setProperty(QLatin1String("INVALID_STATE_ERR"), INVALID_STATE_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
domExceptionPrototype.setProperty(QLatin1String("SYNTAX_ERR"), SYNTAX_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
domExceptionPrototype.setProperty(QLatin1String("INVALID_MODIFICATION_ERR"), INVALID_MODIFICATION_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
domExceptionPrototype.setProperty(QLatin1String("NAMESPACE_ERR"), NAMESPACE_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
domExceptionPrototype.setProperty(QLatin1String("INVALID_ACCESS_ERR"), INVALID_ACCESS_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
domExceptionPrototype.setProperty(QLatin1String("VALIDATION_ERR"), VALIDATION_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
domExceptionPrototype.setProperty(QLatin1String("TYPE_MISMATCH_ERR"), TYPE_MISMATCH_ERR, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); | |
engine->globalObject().setProperty(QLatin1String("DOMException"), domExceptionPrototype); | |
} | |
QT_END_NAMESPACE | |
#endif // QT_NO_XMLSTREAMREADER | |
#include <qdeclarativexmlhttprequest.moc> |