diff options
Diffstat (limited to 'src/qml/qml/qqmlxmlhttprequest.cpp')
-rw-r--r-- | src/qml/qml/qqmlxmlhttprequest.cpp | 323 |
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; |