aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmlxmlhttprequest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/qqmlxmlhttprequest.cpp')
-rw-r--r--src/qml/qml/qqmlxmlhttprequest.cpp323
1 files changed, 207 insertions, 116 deletions
diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp
index 4db0562c0e..b4800584a3 100644
--- a/src/qml/qml/qqmlxmlhttprequest.cpp
+++ b/src/qml/qml/qqmlxmlhttprequest.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qqmlxmlhttprequest_p.h"
@@ -43,7 +7,6 @@
#include "qqmlengine_p.h"
#include <private/qqmlrefcount_p.h>
#include "qqmlengine_p.h"
-#include "qqmlexpression_p.h"
#include "qqmlglobal_p.h"
#include <private/qv4domerrors_p.h>
#include <private/qv4engine_p.h>
@@ -54,8 +17,11 @@
#include <QtCore/qobject.h>
#include <QtQml/qjsvalue.h>
#include <QtQml/qjsengine.h>
+#include <QtQml/qqmlfile.h>
#include <QtNetwork/qnetworkreply.h>
-#include <QtCore/qtextcodec.h>
+
+#include <QtCore/qpointer.h>
+#include <QtCore/qstringconverter.h>
#include <QtCore/qxmlstream.h>
#include <QtCore/qstack.h>
#include <QtCore/qdebug.h>
@@ -77,6 +43,8 @@ using namespace QV4;
QT_BEGIN_NAMESPACE
DEFINE_BOOL_CONFIG_OPTION(xhrDump, QML_XHR_DUMP);
+DEFINE_BOOL_CONFIG_OPTION(xhrFileWrite, QML_XHR_ALLOW_FILE_WRITE);
+DEFINE_BOOL_CONFIG_OPTION(xhrFileRead, QML_XHR_ALLOW_FILE_READ);
struct QQmlXMLHttpRequestData {
QQmlXMLHttpRequestData();
@@ -150,11 +118,12 @@ public:
QList<NodeImpl *> attributes;
};
-class DocumentImpl : public QQmlRefCount, public NodeImpl
+class DocumentImpl final : public QQmlRefCounted<DocumentImpl>, public NodeImpl
{
+ using Base1 = QQmlRefCounted<DocumentImpl>;
public:
DocumentImpl() : root(nullptr) { type = Document; }
- virtual ~DocumentImpl() {
+ ~DocumentImpl() override {
delete root;
}
@@ -164,8 +133,8 @@ public:
NodeImpl *root;
- void addref() { QQmlRefCount::addref(); }
- void release() { QQmlRefCount::release(); }
+ void addref() { Base1::addref(); }
+ void release() { Base1::release(); }
};
namespace Heap {
@@ -537,7 +506,7 @@ ReturnedValue NodePrototype::method_get_previousSibling(const FunctionObject *b,
if (!r->d()->d->parent)
RETURN_RESULT(Encode::null());
- for (int ii = 0; ii < r->d()->d->parent->children.count(); ++ii) {
+ for (int ii = 0; ii < r->d()->d->parent->children.size(); ++ii) {
if (r->d()->d->parent->children.at(ii) == r->d()->d) {
if (ii == 0)
return Encode::null();
@@ -559,9 +528,9 @@ ReturnedValue NodePrototype::method_get_nextSibling(const FunctionObject *b, con
if (!r->d()->d->parent)
RETURN_RESULT(Encode::null());
- for (int ii = 0; ii < r->d()->d->parent->children.count(); ++ii) {
+ for (int ii = 0; ii < r->d()->d->parent->children.size(); ++ii) {
if (r->d()->d->parent->children.at(ii) == r->d()->d) {
- if ((ii + 1) == r->d()->d->parent->children.count())
+ if ((ii + 1) == r->d()->d->parent->children.size())
return Encode::null();
else
return Node::create(scope.engine, r->d()->d->parent->children.at(ii + 1));
@@ -699,7 +668,7 @@ ReturnedValue CharacterData::method_length(const FunctionObject *b, const Value
if (!r)
RETURN_UNDEFINED();
- return Encode(r->d()->d->data.length());
+ return Encode(int(r->d()->d->data.size()));
}
ReturnedValue CharacterData::prototype(ExecutionEngine *v4)
@@ -725,7 +694,7 @@ ReturnedValue Text::method_isElementContentWhitespace(const FunctionObject *b, c
if (!r)
RETURN_UNDEFINED();
- return Encode(QStringRef(&r->d()->d->data).trimmed().isEmpty());
+ return Encode(QStringView(r->d()->d->data).trimmed().isEmpty());
}
ReturnedValue Text::method_wholeText(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -892,7 +861,7 @@ ReturnedValue NamedNodeMap::virtualGet(const Managed *m, PropertyKey id, const V
if (id.isArrayIndex()) {
uint index = id.asArrayIndex();
- if ((int)index < r->d()->list().count()) {
+ if ((int)index < r->d()->list().size()) {
if (hasProperty)
*hasProperty = true;
return Node::create(v4, r->d()->list().at(index));
@@ -906,10 +875,10 @@ ReturnedValue NamedNodeMap::virtualGet(const Managed *m, PropertyKey id, const V
return Object::virtualGet(m, id, receiver, hasProperty);
if (id == v4->id_length()->propertyKey())
- return Value::fromInt32(r->d()->list().count()).asReturnedValue();
+ return Value::fromInt32(r->d()->list().size()).asReturnedValue();
QString str = id.toQString();
- for (int ii = 0; ii < r->d()->list().count(); ++ii) {
+ for (int ii = 0; ii < r->d()->list().size(); ++ii) {
if (r->d()->list().at(ii)->name == str) {
if (hasProperty)
*hasProperty = true;
@@ -935,7 +904,7 @@ ReturnedValue NodeList::virtualGet(const Managed *m, PropertyKey id, const Value
if (id.isArrayIndex()) {
uint index = id.asArrayIndex();
- if ((int)index < r->d()->d->children.count()) {
+ if ((int)index < r->d()->d->children.size()) {
if (hasProperty)
*hasProperty = true;
return Node::create(v4, r->d()->d->children.at(index));
@@ -946,7 +915,7 @@ ReturnedValue NodeList::virtualGet(const Managed *m, PropertyKey id, const Value
}
if (id == v4->id_length()->propertyKey())
- return Value::fromInt32(r->d()->d->children.count()).asReturnedValue();
+ return Value::fromInt32(r->d()->d->children.size()).asReturnedValue();
return Object::virtualGet(m, id, receiver, hasProperty);
}
@@ -1003,9 +972,13 @@ public:
AsynchronousLoad,
SynchronousLoad
};
- enum State { Unsent = 0,
- Opened = 1, HeadersReceived = 2,
- Loading = 3, Done = 4 };
+ enum State {
+ Unsent = 0,
+ Opened = 1,
+ HeadersReceived = 2,
+ Loading = 3,
+ Done = 4
+ };
QQmlXMLHttpRequest(QNetworkAccessManager *manager, QV4::ExecutionEngine *v4);
virtual ~QQmlXMLHttpRequest();
@@ -1017,7 +990,8 @@ public:
QString replyStatusText() const;
ReturnedValue open(Object *thisObject, const QString &, const QUrl &, LoadType);
- ReturnedValue send(Object *thisObject, QQmlContextData *context, const QByteArray &);
+ ReturnedValue send(Object *thisObject, const QQmlRefPointer<QQmlContextData> &context,
+ const QByteArray &);
ReturnedValue abort(Object *thisObject);
void addHeader(const QString &, const QString &);
@@ -1027,9 +1001,15 @@ public:
QString responseBody();
const QByteArray & rawResponseBody() const;
bool receivedXml() const;
+ QUrl url() const;
const QString & responseType() const;
void setResponseType(const QString &);
+ void setOverrideMimeType(QStringView mimeType) { m_overrideMime = mimeType.toUtf8(); }
+ void setOverrideCharset(QStringView charset) { m_overrideCharset = charset.toUtf8(); }
+
+ const QByteArray mimeType() const;
+ const QByteArray charset() const;
QV4::ReturnedValue jsonResponseBody(QV4::ExecutionEngine*);
QV4::ReturnedValue xmlResponseBody(QV4::ExecutionEngine*);
@@ -1058,14 +1038,14 @@ private:
bool m_gotXml;
QByteArray m_mime;
QByteArray m_charset;
- QTextCodec *m_textCodec;
-#if QT_CONFIG(textcodec)
- QTextCodec* findTextCodec() const;
-#endif
+ QByteArray m_overrideMime;
+ QByteArray m_overrideCharset;
+
+ QStringDecoder findTextDecoder() const;
void readEncoding();
PersistentValue m_thisObject;
- QQmlContextDataRef m_qmlContext;
+ QQmlRefPointer<QQmlContextData> m_qmlContext;
bool m_wasConstructedWithQmlContext = true;
void dispatchCallbackNow(Object *thisObj);
@@ -1088,11 +1068,9 @@ private:
QQmlXMLHttpRequest::QQmlXMLHttpRequest(QNetworkAccessManager *manager, QV4::ExecutionEngine *v4)
: m_state(Unsent), m_errorFlag(false), m_sendFlag(false)
- , m_redirectCount(0), m_gotXml(false), m_textCodec(nullptr), m_network(nullptr), m_nam(manager)
- , m_responseType()
- , m_parsedDocument()
+ , m_redirectCount(0), m_gotXml(false), m_network(nullptr), m_nam(manager)
{
- m_wasConstructedWithQmlContext = v4->callingQmlContext() != nullptr;
+ m_wasConstructedWithQmlContext = !v4->callingQmlContext().isNull();
}
QQmlXMLHttpRequest::~QQmlXMLHttpRequest()
@@ -1169,7 +1147,7 @@ QString QQmlXMLHttpRequest::headers() const
QString ret;
for (const HeaderPair &header : m_headersList) {
- if (ret.length())
+ if (ret.size())
ret.append(QLatin1String("\r\n"));
ret += QString::fromUtf8(header.first) + QLatin1String(": ")
+ QString::fromUtf8(header.second);
@@ -1194,7 +1172,30 @@ void QQmlXMLHttpRequest::fillHeadersList()
void QQmlXMLHttpRequest::requestFromUrl(const QUrl &url)
{
+ m_url = url;
QNetworkRequest request = m_request;
+
+ if (QQmlFile::isLocalFile(url)) {
+ if (m_method == QLatin1String("PUT"))
+ {
+ if (!xhrFileWrite()) {
+ qWarning("XMLHttpRequest: Using PUT on a local file is disabled by default.\n"
+ "Set QML_XHR_ALLOW_FILE_WRITE to 1 to enable this feature.");
+ return;
+ }
+ } else if (m_method == QLatin1String("GET")) {
+ if (!xhrFileRead()) {
+ qWarning("XMLHttpRequest: Using GET on a local file is disabled by default.\n"
+ "Set QML_XHR_ALLOW_FILE_READ to 1 to enable this feature.");
+ return;
+ }
+ } else {
+ qWarning("XMLHttpRequest: Unsupported method used on a local file");
+ return;
+ }
+ }
+
+ request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy);
request.setUrl(url);
if(m_method == QLatin1String("POST") ||
m_method == QLatin1String("PUT")) {
@@ -1211,7 +1212,7 @@ void QQmlXMLHttpRequest::requestFromUrl(const QUrl &url)
int n = 0;
int semiColon = str.indexOf(QLatin1Char(';'), charsetIdx);
if (semiColon == -1) {
- n = str.length() - charsetIdx;
+ n = str.size() - charsetIdx;
} else {
n = semiColon - charsetIdx;
}
@@ -1266,14 +1267,15 @@ void QQmlXMLHttpRequest::requestFromUrl(const QUrl &url)
} else {
QObject::connect(m_network, SIGNAL(readyRead()),
this, SLOT(readyRead()));
- QObject::connect(m_network, SIGNAL(error(QNetworkReply::NetworkError)),
+ QObject::connect(m_network, SIGNAL(errorOccurred(QNetworkReply::NetworkError)),
this, SLOT(error(QNetworkReply::NetworkError)));
QObject::connect(m_network, SIGNAL(finished()),
this, SLOT(finished()));
}
}
-ReturnedValue QQmlXMLHttpRequest::send(Object *thisObject, QQmlContextData *context, const QByteArray &data)
+ReturnedValue QQmlXMLHttpRequest::send(
+ Object *thisObject, const QQmlRefPointer<QQmlContextData> &context, const QByteArray &data)
{
m_errorFlag = false;
m_sendFlag = true;
@@ -1319,7 +1321,7 @@ void QQmlXMLHttpRequest::readyRead()
// ### We assume if this is called the headers are now available
if (m_state < HeadersReceived) {
m_state = HeadersReceived;
- fillHeadersList ();
+ fillHeadersList();
dispatchCallbackSafely();
}
@@ -1389,13 +1391,17 @@ void QQmlXMLHttpRequest::finished()
QVariant redirect = m_network->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (redirect.isValid()) {
QUrl url = m_network->url().resolved(redirect.toUrl());
- if (url.scheme() != QLatin1String("file")) {
+ if (!QQmlFile::isLocalFile(url)) {
// See http://www.ietf.org/rfc/rfc2616.txt, section 10.3.4 "303 See Other":
// Result of 303 redirection should be a new "GET" request.
const QVariant code = m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute);
if (code.isValid() && code.toInt() == 303 && m_method != QLatin1String("GET"))
m_method = QStringLiteral("GET");
destroyNetwork();
+
+ // Discard redirect response body
+ m_responseEntityBody = QByteArray();
+
requestFromUrl(url);
return;
}
@@ -1434,13 +1440,13 @@ void QQmlXMLHttpRequest::finished()
dispatchCallbackSafely();
m_thisObject.clear();
- m_qmlContext.setContextData(nullptr);
+ m_qmlContext.reset();
}
void QQmlXMLHttpRequest::readEncoding()
{
- for (const HeaderPair &header : qAsConst(m_headersList)) {
+ for (const HeaderPair &header : std::as_const(m_headersList)) {
if (header.first == "content-type") {
int separatorIdx = header.second.indexOf(';');
if (separatorIdx == -1) {
@@ -1451,14 +1457,15 @@ void QQmlXMLHttpRequest::readEncoding()
if (charsetIdx != -1) {
charsetIdx += 8;
separatorIdx = header.second.indexOf(';', charsetIdx);
- m_charset = header.second.mid(charsetIdx, separatorIdx >= 0 ? separatorIdx : header.second.length());
+ m_charset = header.second.mid(charsetIdx, separatorIdx >= 0 ? separatorIdx : header.second.size());
}
}
break;
}
}
- if (m_mime.isEmpty() || m_mime == "text/xml" || m_mime == "application/xml" || m_mime.endsWith("+xml"))
+ const auto mime = mimeType();
+ if (mime.isEmpty() || mime == "text/xml" || mime == "application/xml" || mime.endsWith("+xml"))
m_gotXml = true;
}
@@ -1467,6 +1474,25 @@ bool QQmlXMLHttpRequest::receivedXml() const
return m_gotXml;
}
+QUrl QQmlXMLHttpRequest::url() const
+{
+ return m_url;
+}
+
+const QByteArray QQmlXMLHttpRequest::mimeType() const
+{
+ // Final MIME type is the override MIME type unless that is null in which
+ // case it is the response MIME type.
+ return m_overrideMime.isEmpty() ? m_mime : m_overrideMime;
+}
+
+const QByteArray QQmlXMLHttpRequest::charset() const
+{
+ // Final charset is the override charset unless that is null in which case
+ // it is the response charset.
+ return m_overrideCharset.isEmpty() ? m_charset : m_overrideCharset;
+}
+
const QString & QQmlXMLHttpRequest::responseType() const
{
return m_responseType;
@@ -1484,7 +1510,7 @@ QV4::ReturnedValue QQmlXMLHttpRequest::jsonResponseBody(QV4::ExecutionEngine* en
QJsonParseError error;
const QString& jtext = responseBody();
- JsonParser parser(scope.engine, jtext.constData(), jtext.length());
+ JsonParser parser(scope.engine, jtext.constData(), jtext.size());
ScopedValue jsonObject(scope, parser.parse(&error));
if (error.error != QJsonParseError::NoError)
return engine->throwSyntaxError(QStringLiteral("JSON.parse: Parse error"));
@@ -1504,43 +1530,38 @@ QV4::ReturnedValue QQmlXMLHttpRequest::xmlResponseBody(QV4::ExecutionEngine* eng
return m_parsedDocument.value();
}
-#if QT_CONFIG(textcodec)
-QTextCodec* QQmlXMLHttpRequest::findTextCodec() const
+QStringDecoder QQmlXMLHttpRequest::findTextDecoder() const
{
- QTextCodec *codec = nullptr;
+ QStringDecoder decoder;
- if (!m_charset.isEmpty())
- codec = QTextCodec::codecForName(m_charset);
+ if (!charset().isEmpty())
+ decoder = QStringDecoder(charset());
- if (!codec && m_gotXml) {
+ if (!decoder.isValid() && m_gotXml) {
QXmlStreamReader reader(m_responseEntityBody);
reader.readNext();
- codec = QTextCodec::codecForName(reader.documentEncoding().toString().toUtf8());
+ decoder = QStringDecoder(reader.documentEncoding().toString().toUtf8());
}
- if (!codec && m_mime == "text/html")
- codec = QTextCodec::codecForHtml(m_responseEntityBody, nullptr);
+ if (!decoder.isValid() && mimeType() == "text/html")
+ decoder = QStringDecoder::decoderForHtml(m_responseEntityBody);
+
+ if (!decoder.isValid()) {
+ auto encoding = QStringConverter::encodingForData(m_responseEntityBody);
+ if (encoding)
+ decoder = QStringDecoder(*encoding);
+ }
- if (!codec)
- codec = QTextCodec::codecForUtfText(m_responseEntityBody, nullptr);
+ if (!decoder.isValid())
+ decoder = QStringDecoder(QStringDecoder::Utf8);
- if (!codec)
- codec = QTextCodec::codecForName("UTF-8");
- return codec;
+ return decoder;
}
-#endif
-
QString QQmlXMLHttpRequest::responseBody()
{
-#if QT_CONFIG(textcodec)
- if (!m_textCodec)
- m_textCodec = findTextCodec();
- if (m_textCodec)
- return m_textCodec->toUnicode(m_responseEntityBody);
-#endif
-
- return QString::fromUtf8(m_responseEntityBody);
+ QStringDecoder toUtf16 = findTextDecoder();
+ return toUtf16(m_responseEntityBody);
}
const QByteArray &QQmlXMLHttpRequest::rawResponseBody() const
@@ -1565,10 +1586,10 @@ void QQmlXMLHttpRequest::dispatchCallbackNow(Object *thisObj, bool done, bool er
if (!callback)
return;
- QV4::JSCallData jsCallData(scope);
+ QV4::JSCallArguments jsCallData(scope);
callback->call(jsCallData);
- if (scope.engine->hasException) {
+ if (scope.hasException()) {
QQmlError error = scope.engine->catchExceptionAsQmlError();
QQmlEnginePrivate *qmlEnginePrivate = scope.engine->qmlEngine() ? QQmlEnginePrivate::get(scope.engine->qmlEngine()) : nullptr;
QQmlEnginePrivate::warning(qmlEnginePrivate, error);
@@ -1587,12 +1608,13 @@ void QQmlXMLHttpRequest::dispatchCallbackNow(Object *thisObj, bool done, bool er
void QQmlXMLHttpRequest::dispatchCallbackSafely()
{
- if (m_wasConstructedWithQmlContext && !m_qmlContext.contextData())
+ if (m_wasConstructedWithQmlContext && m_qmlContext.isNull()) {
// if the calling context object is no longer valid, then it has been
// deleted explicitly (e.g., by a Loader deleting the itemContext when
// the source is changed). We do nothing in this case, as the evaluation
// cannot succeed.
return;
+ }
dispatchCallbackNow(m_thisObject.as<Object>());
}
@@ -1626,7 +1648,7 @@ struct QQmlXMLHttpRequestWrapper : Object {
Member(class, Pointer, Object *, proto)
DECLARE_HEAP_OBJECT(QQmlXMLHttpRequestCtor, FunctionObject) {
- DECLARE_MARKOBJECTS(QQmlXMLHttpRequestCtor);
+ DECLARE_MARKOBJECTS(QQmlXMLHttpRequestCtor)
void init(ExecutionEngine *engine);
};
@@ -1638,6 +1660,7 @@ struct QQmlXMLHttpRequestWrapper : public Object
V4_NEEDS_DESTROY
};
+// https://xhr.spec.whatwg.org/
struct QQmlXMLHttpRequestCtor : public FunctionObject
{
V4_OBJECT2(QQmlXMLHttpRequestCtor, FunctionObject)
@@ -1647,7 +1670,7 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject
Scope scope(f->engine());
const QQmlXMLHttpRequestCtor *ctor = static_cast<const QQmlXMLHttpRequestCtor *>(f);
- QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->qmlEngine()->networkAccessManager(), scope.engine);
+ QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->networkAccessManager(scope.engine), scope.engine);
Scoped<QQmlXMLHttpRequestWrapper> w(scope, scope.engine->memoryManager->allocate<QQmlXMLHttpRequestWrapper>(r));
ScopedObject proto(scope, ctor->d()->proto);
w->setPrototypeUnchecked(proto);
@@ -1666,6 +1689,7 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject
static ReturnedValue method_abort(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_getResponseHeader(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_getAllResponseHeaders(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_overrideMimeType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_readyState(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_status(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
@@ -1675,6 +1699,7 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject
static ReturnedValue method_get_response(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_responseType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_set_responseType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_responseURL(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
};
}
@@ -1683,15 +1708,16 @@ DEFINE_OBJECT_VTABLE(QQmlXMLHttpRequestWrapper);
void Heap::QQmlXMLHttpRequestCtor::init(ExecutionEngine *engine)
{
- Heap::FunctionObject::init(engine->rootContext(), QStringLiteral("XMLHttpRequest"));
+ Heap::FunctionObject::init(engine, QStringLiteral("XMLHttpRequest"));
Scope scope(engine);
Scoped<QV4::QQmlXMLHttpRequestCtor> ctor(scope, this);
- ctor->defineReadonlyProperty(QStringLiteral("UNSENT"), Value::fromInt32(0));
- ctor->defineReadonlyProperty(QStringLiteral("OPENED"), Value::fromInt32(1));
- ctor->defineReadonlyProperty(QStringLiteral("HEADERS_RECEIVED"), Value::fromInt32(2));
- ctor->defineReadonlyProperty(QStringLiteral("LOADING"), Value::fromInt32(3));
- ctor->defineReadonlyProperty(QStringLiteral("DONE"), Value::fromInt32(4));
+ ctor->defineReadonlyProperty(QStringLiteral("UNSENT"), Value::fromInt32(QQmlXMLHttpRequest::Unsent));
+ ctor->defineReadonlyProperty(QStringLiteral("OPENED"), Value::fromInt32(QQmlXMLHttpRequest::Opened));
+ ctor->defineReadonlyProperty(QStringLiteral("HEADERS_RECEIVED"), Value::fromInt32(QQmlXMLHttpRequest::HeadersReceived));
+ ctor->defineReadonlyProperty(QStringLiteral("LOADING"), Value::fromInt32(QQmlXMLHttpRequest::Loading));
+ ctor->defineReadonlyProperty(QStringLiteral("DONE"), Value::fromInt32(QQmlXMLHttpRequest::Done));
+
if (!ctor->d()->proto)
ctor->setupProto();
ScopedString s(scope, engine->id_prototype());
@@ -1714,6 +1740,7 @@ void QQmlXMLHttpRequestCtor::setupProto()
p->defineDefaultProperty(QStringLiteral("abort"), method_abort);
p->defineDefaultProperty(QStringLiteral("getResponseHeader"), method_getResponseHeader);
p->defineDefaultProperty(QStringLiteral("getAllResponseHeaders"), method_getAllResponseHeaders);
+ p->defineDefaultProperty(QStringLiteral("overrideMimeType"), method_overrideMimeType);
// Read-only properties
p->defineAccessorProperty(QStringLiteral("readyState"), method_get_readyState, nullptr);
@@ -1722,6 +1749,7 @@ void QQmlXMLHttpRequestCtor::setupProto()
p->defineAccessorProperty(QStringLiteral("responseText"),method_get_responseText, nullptr);
p->defineAccessorProperty(QStringLiteral("responseXML"),method_get_responseXML, nullptr);
p->defineAccessorProperty(QStringLiteral("response"),method_get_response, nullptr);
+ p->defineAccessorProperty(QStringLiteral("responseURL"),method_get_responseURL, nullptr);
// Read-write properties
p->defineAccessorProperty(QStringLiteral("responseType"), method_get_responseType, method_set_responseType);
@@ -1763,8 +1791,7 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_open(const FunctionObject *b, const
QUrl url = QUrl(argv[1].toQStringNoThrow());
if (url.isRelative()) {
- QQmlContextData *qmlContextData = scope.engine->callingQmlContext();
- if (qmlContextData)
+ if (QQmlRefPointer<QQmlContextData> qmlContextData = scope.engine->callingQmlContext())
url = qmlContextData->resolvedUrl(url);
else
url = scope.engine->resolvedUrl(url.url());
@@ -1829,7 +1856,6 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_setRequestHeader(const FunctionObje
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-")))
@@ -2049,6 +2075,71 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_set_responseType(const FunctionObje
return Encode::undefined();
}
+ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseURL(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
+ if (!w)
+ V4THROW_REFERENCE("Not an XMLHttpRequest object");
+ QQmlXMLHttpRequest *r = w->d()->request;
+
+ if (r->readyState() != QQmlXMLHttpRequest::Loading &&
+ r->readyState() != QQmlXMLHttpRequest::Done) {
+ return Encode(scope.engine->newString(QString()));
+ } else {
+ QUrl url = r->url();
+ url.setFragment(QString());
+ return Encode(scope.engine->newString(url.toString()));
+ }
+}
+
+ReturnedValue QQmlXMLHttpRequestCtor::method_overrideMimeType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
+ if (!w)
+ V4THROW_REFERENCE("Not an XMLHttpRequest object");
+ QQmlXMLHttpRequest *r = w->d()->request;
+
+ if (argc != 1)
+ THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count");
+
+ // If state is loading or done, throw an InvalidStateError exception.
+ if (r->readyState() == QQmlXMLHttpRequest::Loading ||
+ r->readyState() == QQmlXMLHttpRequest::Done)
+ THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
+
+ // Set override MIME type to `application/octet-stream`.
+ r->setOverrideMimeType(QStringLiteral("application/octet-stream"));
+ const auto parts = argv[0].toQStringNoThrow().split(QLatin1Char(';'));
+ const auto type = parts.at(0).trimmed();
+
+ const auto mimeInvalidCharacter = [](QChar uni) {
+ if (uni.unicode() > 127) // Only accept ASCII
+ return true;
+ const char ch = char(uni.unicode());
+ return !(ch == '-' || ch == '/' || isAsciiLetterOrNumber(ch));
+ };
+
+ // If mime is a parsable MIME type, ...
+ if (type.count(QLatin1Char('/')) == 1
+ && std::find_if(type.begin(), type.end(), mimeInvalidCharacter) == type.end()) {
+ // ... then set override MIME type to its MIME type portion.
+ r->setOverrideMimeType(type);
+ }
+ for (const auto &part : parts) {
+ const QLatin1String charset("charset=");
+ // If override MIME type has a `charset` parameter, ...
+ if (part.trimmed().startsWith(charset)) {
+ // ... then set override charset to its value.
+ const int offset(part.indexOf(charset) + charset.size());
+ r->setOverrideCharset(part.sliced(offset).trimmed());
+ }
+ }
+
+ return Encode::undefined();
+}
+
void qt_rem_qmlxmlhttprequest(ExecutionEngine * /* engine */, void *d)
{
QQmlXMLHttpRequestData *data = (QQmlXMLHttpRequestData *)d;