Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlxmlhttprequest.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include "qqmlengine.h"
7#include "qqmlengine_p.h"
8#include <private/qqmlrefcount_p.h>
9#include "qqmlengine_p.h"
10#include "qqmlglobal_p.h"
11#include <private/qv4domerrors_p.h>
12#include <private/qv4engine_p.h>
13#include <private/qv4functionobject_p.h>
14#include <private/qv4scopedvalue_p.h>
15#include <private/qv4jscall_p.h>
16
17#include <QtCore/qobject.h>
18#include <QtQml/qjsvalue.h>
19#include <QtQml/qjsengine.h>
20#include <QtQml/qqmlfile.h>
21#include <QtNetwork/qnetworkreply.h>
22
23#include <QtCore/qpointer.h>
24#include <QtCore/qstringconverter.h>
25#include <QtCore/qxmlstream.h>
26#include <QtCore/qstack.h>
27#include <QtCore/qdebug.h>
28#include <QtCore/qbuffer.h>
29
30#include <private/qv4objectproto_p.h>
31#include <private/qv4scopedvalue_p.h>
32#include <private/qv4arraybuffer_p.h>
33#include <private/qv4jsonobject_p.h>
34
35using namespace QV4;
36
37#define V4THROW_REFERENCE(string)
38 do {
39 ScopedObject error(scope, scope.engine->newReferenceErrorObject(QStringLiteral(string)));
40 return scope.engine->throwError(error);
41 } while (false)
42
43QT_BEGIN_NAMESPACE
44
45DEFINE_BOOL_CONFIG_OPTION(xhrDump, QML_XHR_DUMP);
46DEFINE_BOOL_CONFIG_OPTION(xhrFileWrite, QML_XHR_ALLOW_FILE_WRITE);
47DEFINE_BOOL_CONFIG_OPTION(xhrFileRead, QML_XHR_ALLOW_FILE_READ);
48
63
64static inline QQmlXMLHttpRequestData *xhrdata(ExecutionEngine *v4)
65{
66 return (QQmlXMLHttpRequestData *)v4->xmlHttpRequestData();
67}
68
72
76
77namespace QV4 {
78
79class DocumentImpl;
81{
82public:
83 NodeImpl() : type(Element), document(nullptr), parent(nullptr) {}
84 virtual ~NodeImpl() {
85 qDeleteAll(children);
86 qDeleteAll(attributes);
87 }
88
89 // These numbers are copied from the Node IDL definition
105
108
110
111 void addref();
112 void release();
113
114 DocumentImpl *document;
116
119};
120
121class DocumentImpl final : public QQmlRefCounted<DocumentImpl>, public NodeImpl
122{
123 using Base1 = QQmlRefCounted<DocumentImpl>;
124public:
125 DocumentImpl() : root(nullptr) { type = Document; }
126 ~DocumentImpl() override {
127 delete root;
128 }
129
133
135
136 void addref() { Base1::addref(); }
137 void release() { Base1::release(); }
138};
139
140namespace Heap {
141
143 void init(NodeImpl *data, const QList<NodeImpl *> &list);
144 void destroy() {
145 delete listPtr;
146 if (d)
148 Object::destroy();
149 }
151 if (listPtr == nullptr)
152 listPtr = new QList<NodeImpl *>;
153 return *listPtr;
154 }
155
156 QList<NodeImpl *> *listPtr; // Only used in NamedNodeMap
158};
159
160struct NodeList : Object {
161 void init(NodeImpl *data);
162 void destroy() {
163 if (d)
165 Object::destroy();
166 }
168};
169
171 void init();
172};
173
174struct Node : Object {
175 void init(NodeImpl *data);
176 void destroy() {
177 if (d)
179 Object::destroy();
180 }
182};
183
184}
185
186class NamedNodeMap : public Object
187{
188public:
189 V4_OBJECT2(NamedNodeMap, Object)
191
192 // C++ API
194
195 // JS API
196 static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
197};
198
199void Heap::NamedNodeMap::init(NodeImpl *data, const QList<NodeImpl *> &list)
200{
201 Object::init();
202 d = data;
203 this->list() = list;
204 if (d)
206}
207
209
210class NodeList : public Object
211{
212public:
213 V4_OBJECT2(NodeList, Object)
215
216 // JS API
218
219 // C++ API
220 static ReturnedValue create(ExecutionEngine *, NodeImpl *);
221
222};
223
225{
226 Object::init();
227 d = data;
228 if (d)
230}
231
233
234class NodePrototype : public Object
235{
236public:
237 V4_OBJECT2(NodePrototype, Object)
238
240
241 // JS API
242 static ReturnedValue method_get_nodeName(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
243 static ReturnedValue method_get_nodeValue(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
244 static ReturnedValue method_get_nodeType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
245 static ReturnedValue method_get_namespaceUri(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
246
247 static ReturnedValue method_get_parentNode(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
248 static ReturnedValue method_get_childNodes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
249 static ReturnedValue method_get_firstChild(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
250 static ReturnedValue method_get_lastChild(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
251 static ReturnedValue method_get_previousSibling(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
252 static ReturnedValue method_get_nextSibling(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
253 static ReturnedValue method_get_attributes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
254
255 //static ReturnedValue ownerDocument(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
256 //static ReturnedValue namespaceURI(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
257 //static ReturnedValue prefix(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
258 //static ReturnedValue localName(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
259 //static ReturnedValue baseURI(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
260 //static ReturnedValue textContent(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
261
262 static ReturnedValue getProto(ExecutionEngine *v4);
263
264};
265
267{
268 Object::init();
269 Scope scope(internalClass->engine);
270 ScopedObject o(scope, this);
271
272 o->defineAccessorProperty(QStringLiteral("nodeName"), QV4::NodePrototype::method_get_nodeName, nullptr);
273 o->defineAccessorProperty(QStringLiteral("nodeValue"), QV4::NodePrototype::method_get_nodeValue, nullptr);
274 o->defineAccessorProperty(QStringLiteral("nodeType"), QV4::NodePrototype::method_get_nodeType, nullptr);
275 o->defineAccessorProperty(QStringLiteral("namespaceUri"), QV4::NodePrototype::method_get_namespaceUri, nullptr);
276
277 o->defineAccessorProperty(QStringLiteral("parentNode"), QV4::NodePrototype::method_get_parentNode, nullptr);
278 o->defineAccessorProperty(QStringLiteral("childNodes"), QV4::NodePrototype::method_get_childNodes, nullptr);
279 o->defineAccessorProperty(QStringLiteral("firstChild"), QV4::NodePrototype::method_get_firstChild, nullptr);
280 o->defineAccessorProperty(QStringLiteral("lastChild"), QV4::NodePrototype::method_get_lastChild, nullptr);
281 o->defineAccessorProperty(QStringLiteral("previousSibling"), QV4::NodePrototype::method_get_previousSibling, nullptr);
282 o->defineAccessorProperty(QStringLiteral("nextSibling"), QV4::NodePrototype::method_get_nextSibling, nullptr);
283 o->defineAccessorProperty(QStringLiteral("attributes"), QV4::NodePrototype::method_get_attributes, nullptr);
284}
285
286
288
289struct Node : public Object
290{
291 V4_OBJECT2(Node, Object)
293
294 // C++ API
296
297 bool isNull() const;
298};
299
300void Heap::Node::init(NodeImpl *data)
301{
302 Object::init();
303 d = data;
304 if (d)
306}
307
309
310class Element : public Node
311{
312public:
313 // C++ API
314 static ReturnedValue prototype(ExecutionEngine *);
315};
316
317class Attr : public Node
318{
319public:
320 // JS API
321 static ReturnedValue method_name(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
322// static void specified(CallContext *);
323 static ReturnedValue method_value(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
324 static ReturnedValue method_ownerElement(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
325// static void schemaTypeInfo(CallContext *);
326// static void isId(CallContext *c);
327
328 // C++ API
329 static ReturnedValue prototype(ExecutionEngine *);
330};
331
332class CharacterData : public Node
333{
334public:
335 // JS API
336 static ReturnedValue method_length(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
337
338 // C++ API
339 static ReturnedValue prototype(ExecutionEngine *v4);
340};
341
342class Text : public CharacterData
343{
344public:
345 // JS API
346 static ReturnedValue method_isElementContentWhitespace(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
347 static ReturnedValue method_wholeText(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
348
349 // C++ API
350 static ReturnedValue prototype(ExecutionEngine *);
351};
352
353class CDATA : public Text
354{
355public:
356 // C++ API
357 static ReturnedValue prototype(ExecutionEngine *v4);
358};
359
360class Document : public Node
361{
362public:
363 // JS API
364 static ReturnedValue method_xmlVersion(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
365 static ReturnedValue method_xmlEncoding(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
366 static ReturnedValue method_xmlStandalone(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
367 static ReturnedValue method_documentElement(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
368
369 // C++ API
370 static ReturnedValue prototype(ExecutionEngine *);
371 static ReturnedValue load(ExecutionEngine *engine, const QByteArray &data);
372};
373
374}
375
376void NodeImpl::addref()
377{
378 document->addref();
379}
380
381void NodeImpl::release()
382{
383 document->release();
384}
385
386ReturnedValue NodePrototype::method_get_nodeName(const FunctionObject *b, const Value *thisObject, const Value *, int)
387{
388 Scope scope(b);
389 Scoped<Node> r(scope, thisObject->as<Node>());
390 if (!r)
391 THROW_TYPE_ERROR();
392
393 QString name;
394 switch (r->d()->d->type) {
395 case NodeImpl::Document:
396 name = QStringLiteral("#document");
397 break;
398 case NodeImpl::CDATA:
399 name = QStringLiteral("#cdata-section");
400 break;
401 case NodeImpl::Text:
402 name = QStringLiteral("#text");
403 break;
404 default:
405 name = r->d()->d->name;
406 break;
407 }
408 return Encode(scope.engine->newString(name));
409}
410
411ReturnedValue NodePrototype::method_get_nodeValue(const FunctionObject *b, const Value *thisObject, const Value *, int)
412{
413 QV4::Scope scope(b);
414 Scoped<Node> r(scope, thisObject->as<Node>());
415 if (!r)
416 THROW_TYPE_ERROR();
417
418 if (r->d()->d->type == NodeImpl::Document ||
419 r->d()->d->type == NodeImpl::DocumentFragment ||
420 r->d()->d->type == NodeImpl::DocumentType ||
421 r->d()->d->type == NodeImpl::Element ||
422 r->d()->d->type == NodeImpl::Entity ||
423 r->d()->d->type == NodeImpl::EntityReference ||
424 r->d()->d->type == NodeImpl::Notation)
425 RETURN_RESULT(Encode::null());
426
427 return Encode(scope.engine->newString(r->d()->d->data));
428}
429
430ReturnedValue NodePrototype::method_get_nodeType(const FunctionObject *b, const Value *thisObject, const Value *, int)
431{
432 QV4::Scope scope(b);
433 Scoped<Node> r(scope, thisObject->as<Node>());
434 if (!r)
435 THROW_TYPE_ERROR();
436
437 return Encode(r->d()->d->type);
438}
439
440ReturnedValue NodePrototype::method_get_namespaceUri(const FunctionObject *b, const Value *thisObject, const Value *, int)
441{
442 QV4::Scope scope(b);
443 Scoped<Node> r(scope, thisObject->as<Node>());
444 if (!r)
445 THROW_TYPE_ERROR();
446
447 return Encode(scope.engine->newString(r->d()->d->namespaceUri));
448}
449
450ReturnedValue NodePrototype::method_get_parentNode(const FunctionObject *b, const Value *thisObject, const Value *, int)
451{
452 QV4::Scope scope(b);
453 Scoped<Node> r(scope, thisObject->as<Node>());
454 if (!r)
455 THROW_TYPE_ERROR();
456
457 if (r->d()->d->parent)
458 return Node::create(scope.engine, r->d()->d->parent);
459 else
460 return Encode::null();
461}
462
463ReturnedValue NodePrototype::method_get_childNodes(const FunctionObject *b, const Value *thisObject, const Value *, int)
464{
465 QV4::Scope scope(b);
466 Scoped<Node> r(scope, thisObject->as<Node>());
467 if (!r)
468 THROW_TYPE_ERROR();
469
470 return NodeList::create(scope.engine, r->d()->d);
471}
472
473ReturnedValue NodePrototype::method_get_firstChild(const FunctionObject *b, const Value *thisObject, const Value *, int)
474{
475 QV4::Scope scope(b);
476 Scoped<Node> r(scope, thisObject->as<Node>());
477 if (!r)
478 THROW_TYPE_ERROR();
479
480 if (r->d()->d->children.isEmpty())
481 return Encode::null();
482 else
483 return Node::create(scope.engine, r->d()->d->children.constFirst());
484}
485
486ReturnedValue NodePrototype::method_get_lastChild(const FunctionObject *b, const Value *thisObject, const Value *, int)
487{
488 QV4::Scope scope(b);
489 Scoped<Node> r(scope, thisObject->as<Node>());
490 if (!r)
491 THROW_TYPE_ERROR();
492
493 if (r->d()->d->children.isEmpty())
494 return Encode::null();
495 else
496 return Node::create(scope.engine, r->d()->d->children.constLast());
497}
498
499ReturnedValue NodePrototype::method_get_previousSibling(const FunctionObject *b, const Value *thisObject, const Value *, int)
500{
501 QV4::Scope scope(b);
502 Scoped<Node> r(scope, thisObject->as<Node>());
503 if (!r)
504 THROW_TYPE_ERROR();
505
506 if (!r->d()->d->parent)
507 RETURN_RESULT(Encode::null());
508
509 for (int ii = 0; ii < r->d()->d->parent->children.size(); ++ii) {
510 if (r->d()->d->parent->children.at(ii) == r->d()->d) {
511 if (ii == 0)
512 return Encode::null();
513 else
514 return Node::create(scope.engine, r->d()->d->parent->children.at(ii - 1));
515 }
516 }
517
518 return Encode::null();
519}
520
521ReturnedValue NodePrototype::method_get_nextSibling(const FunctionObject *b, const Value *thisObject, const Value *, int)
522{
523 QV4::Scope scope(b);
524 Scoped<Node> r(scope, thisObject->as<Node>());
525 if (!r)
526 THROW_TYPE_ERROR();
527
528 if (!r->d()->d->parent)
529 RETURN_RESULT(Encode::null());
530
531 for (int ii = 0; ii < r->d()->d->parent->children.size(); ++ii) {
532 if (r->d()->d->parent->children.at(ii) == r->d()->d) {
533 if ((ii + 1) == r->d()->d->parent->children.size())
534 return Encode::null();
535 else
536 return Node::create(scope.engine, r->d()->d->parent->children.at(ii + 1));
537 }
538 }
539
540 return Encode::null();
541}
542
543ReturnedValue NodePrototype::method_get_attributes(const FunctionObject *b, const Value *thisObject, const Value *, int)
544{
545 QV4::Scope scope(b);
546 Scoped<Node> r(scope, thisObject->as<Node>());
547 if (!r)
548 THROW_TYPE_ERROR();
549
550 if (r->d()->d->type != NodeImpl::Element)
551 return Encode::null();
552 else
553 return NamedNodeMap::create(scope.engine, r->d()->d, r->d()->d->attributes);
554}
555
556ReturnedValue NodePrototype::getProto(ExecutionEngine *v4)
557{
558 Scope scope(v4);
559 QQmlXMLHttpRequestData *d = xhrdata(v4);
560 if (d->nodePrototype.isUndefined()) {
561 ScopedObject p(scope, v4->memoryManager->allocate<NodePrototype>());
562 d->nodePrototype.set(v4, p);
563 v4->freezeObject(p);
564 }
565 return d->nodePrototype.value();
566}
567
568ReturnedValue Node::create(ExecutionEngine *v4, NodeImpl *data)
569{
570 Scope scope(v4);
571
572 Scoped<Node> instance(scope, v4->memoryManager->allocate<Node>(data));
573 ScopedObject p(scope);
574
575 switch (data->type) {
576 case NodeImpl::Attr:
577 instance->setPrototypeUnchecked((p = Attr::prototype(v4)));
578 break;
579 case NodeImpl::Comment:
580 case NodeImpl::Document:
581 case NodeImpl::DocumentFragment:
582 case NodeImpl::DocumentType:
583 case NodeImpl::Entity:
584 case NodeImpl::EntityReference:
585 case NodeImpl::Notation:
586 case NodeImpl::ProcessingInstruction:
587 return Encode::undefined();
588 case NodeImpl::CDATA:
589 instance->setPrototypeUnchecked((p = CDATA::prototype(v4)));
590 break;
591 case NodeImpl::Text:
592 instance->setPrototypeUnchecked((p = Text::prototype(v4)));
593 break;
594 case NodeImpl::Element:
595 instance->setPrototypeUnchecked((p = Element::prototype(v4)));
596 break;
597 }
598
599 return instance.asReturnedValue();
600}
601
602ReturnedValue Element::prototype(ExecutionEngine *engine)
603{
604 QQmlXMLHttpRequestData *d = xhrdata(engine);
605 if (d->elementPrototype.isUndefined()) {
606 Scope scope(engine);
607 ScopedObject p(scope, engine->newObject());
608 ScopedObject pp(scope);
609 p->setPrototypeUnchecked((pp = NodePrototype::getProto(engine)));
610 p->defineAccessorProperty(QStringLiteral("tagName"), NodePrototype::method_get_nodeName, nullptr);
611 d->elementPrototype.set(engine, p);
612 engine->freezeObject(p);
613 }
614 return d->elementPrototype.value();
615}
616
617ReturnedValue Attr::prototype(ExecutionEngine *engine)
618{
619 QQmlXMLHttpRequestData *d = xhrdata(engine);
620 if (d->attrPrototype.isUndefined()) {
621 Scope scope(engine);
622 ScopedObject p(scope, engine->newObject());
623 ScopedObject pp(scope);
624 p->setPrototypeUnchecked((pp = NodePrototype::getProto(engine)));
625 p->defineAccessorProperty(QStringLiteral("name"), method_name, nullptr);
626 p->defineAccessorProperty(QStringLiteral("value"), method_value, nullptr);
627 p->defineAccessorProperty(QStringLiteral("ownerElement"), method_ownerElement, nullptr);
628 d->attrPrototype.set(engine, p);
629 engine->freezeObject(p);
630 }
631 return d->attrPrototype.value();
632}
633
634ReturnedValue Attr::method_name(const FunctionObject *b, const Value *thisObject, const Value *, int)
635{
636 QV4::Scope scope(b);
637 Scoped<Node> r(scope, thisObject->as<Node>());
638 if (!r)
639 RETURN_UNDEFINED();
640
641 return Encode(scope.engine->newString(r->d()->d->name));
642}
643
644ReturnedValue Attr::method_value(const FunctionObject *b, const Value *thisObject, const Value *, int)
645{
646 QV4::Scope scope(b);
647 Scoped<Node> r(scope, thisObject->as<Node>());
648 if (!r)
649 RETURN_UNDEFINED();
650
651 return Encode(scope.engine->newString(r->d()->d->data));
652}
653
654ReturnedValue Attr::method_ownerElement(const FunctionObject *b, const Value *thisObject, const Value *, int)
655{
656 QV4::Scope scope(b);
657 Scoped<Node> r(scope, thisObject->as<Node>());
658 if (!r)
659 RETURN_UNDEFINED();
660
661 return Node::create(scope.engine, r->d()->d->parent);
662}
663
664ReturnedValue CharacterData::method_length(const FunctionObject *b, const Value *thisObject, const Value *, int)
665{
666 QV4::Scope scope(b);
667 Scoped<Node> r(scope, thisObject->as<Node>());
668 if (!r)
669 RETURN_UNDEFINED();
670
671 return Encode(int(r->d()->d->data.size()));
672}
673
674ReturnedValue CharacterData::prototype(ExecutionEngine *v4)
675{
676 QQmlXMLHttpRequestData *d = xhrdata(v4);
677 if (d->characterDataPrototype.isUndefined()) {
678 Scope scope(v4);
679 ScopedObject p(scope, v4->newObject());
680 ScopedObject pp(scope);
681 p->setPrototypeUnchecked((pp = NodePrototype::getProto(v4)));
682 p->defineAccessorProperty(QStringLiteral("data"), NodePrototype::method_get_nodeValue, nullptr);
683 p->defineAccessorProperty(QStringLiteral("length"), method_length, nullptr);
684 d->characterDataPrototype.set(v4, p);
685 v4->freezeObject(p);
686 }
687 return d->characterDataPrototype.value();
688}
689
690ReturnedValue Text::method_isElementContentWhitespace(const FunctionObject *b, const Value *thisObject, const Value *, int)
691{
692 QV4::Scope scope(b);
693 Scoped<Node> r(scope, thisObject->as<Node>());
694 if (!r)
695 RETURN_UNDEFINED();
696
697 return Encode(QStringView(r->d()->d->data).trimmed().isEmpty());
698}
699
700ReturnedValue Text::method_wholeText(const FunctionObject *b, const Value *thisObject, const Value *, int)
701{
702 QV4::Scope scope(b);
703 Scoped<Node> r(scope, thisObject->as<Node>());
704 if (!r)
705 RETURN_UNDEFINED();
706
707 return Encode(scope.engine->newString(r->d()->d->data));
708}
709
710ReturnedValue Text::prototype(ExecutionEngine *v4)
711{
712 QQmlXMLHttpRequestData *d = xhrdata(v4);
713 if (d->textPrototype.isUndefined()) {
714 Scope scope(v4);
715 ScopedObject p(scope, v4->newObject());
716 ScopedObject pp(scope);
717 p->setPrototypeUnchecked((pp = CharacterData::prototype(v4)));
718 p->defineAccessorProperty(QStringLiteral("isElementContentWhitespace"), method_isElementContentWhitespace, nullptr);
719 p->defineAccessorProperty(QStringLiteral("wholeText"), method_wholeText, nullptr);
720 d->textPrototype.set(v4, p);
721 v4->freezeObject(p);
722 }
723 return d->textPrototype.value();
724}
725
726ReturnedValue CDATA::prototype(ExecutionEngine *v4)
727{
728 // ### why not just use TextProto???
729 QQmlXMLHttpRequestData *d = xhrdata(v4);
730 if (d->cdataPrototype.isUndefined()) {
731 Scope scope(v4);
732 ScopedObject p(scope, v4->newObject());
733 ScopedObject pp(scope);
734 p->setPrototypeUnchecked((pp = Text::prototype(v4)));
735 d->cdataPrototype.set(v4, p);
736 v4->freezeObject(p);
737 }
738 return d->cdataPrototype.value();
739}
740
741ReturnedValue Document::prototype(ExecutionEngine *v4)
742{
743 QQmlXMLHttpRequestData *d = xhrdata(v4);
744 if (d->documentPrototype.isUndefined()) {
745 Scope scope(v4);
746 ScopedObject p(scope, v4->newObject());
747 ScopedObject pp(scope);
748 p->setPrototypeUnchecked((pp = NodePrototype::getProto(v4)));
749 p->defineAccessorProperty(QStringLiteral("xmlVersion"), method_xmlVersion, nullptr);
750 p->defineAccessorProperty(QStringLiteral("xmlEncoding"), method_xmlEncoding, nullptr);
751 p->defineAccessorProperty(QStringLiteral("xmlStandalone"), method_xmlStandalone, nullptr);
752 p->defineAccessorProperty(QStringLiteral("documentElement"), method_documentElement, nullptr);
753 d->documentPrototype.set(v4, p);
754 v4->freezeObject(p);
755 }
756 return d->documentPrototype.value();
757}
758
759ReturnedValue Document::load(ExecutionEngine *v4, const QByteArray &data)
760{
761 Scope scope(v4);
762
763 DocumentImpl *document = nullptr;
764 QStack<NodeImpl *> nodeStack;
765
766 QXmlStreamReader reader(data);
767
768 while (!reader.atEnd()) {
769 switch (reader.readNext()) {
770 case QXmlStreamReader::NoToken:
771 break;
772 case QXmlStreamReader::Invalid:
773 break;
774 case QXmlStreamReader::StartDocument:
775 Q_ASSERT(!document);
776 document = new DocumentImpl;
777 document->document = document;
778 document->version = reader.documentVersion().toString();
779 document->encoding = reader.documentEncoding().toString();
780 document->isStandalone = reader.isStandaloneDocument();
781 break;
782 case QXmlStreamReader::EndDocument:
783 break;
784 case QXmlStreamReader::StartElement:
785 {
786 Q_ASSERT(document);
787 NodeImpl *node = new NodeImpl;
788 node->document = document;
789 node->namespaceUri = reader.namespaceUri().toString();
790 node->name = reader.name().toString();
791 if (nodeStack.isEmpty()) {
792 document->root = node;
793 } else {
794 node->parent = nodeStack.top();
795 node->parent->children.append(node);
796 }
797 nodeStack.append(node);
798
799 const auto attributes = reader.attributes();
800 for (const QXmlStreamAttribute &a : attributes) {
801 NodeImpl *attr = new NodeImpl;
802 attr->document = document;
803 attr->type = NodeImpl::Attr;
804 attr->namespaceUri = a.namespaceUri().toString();
805 attr->name = a.name().toString();
806 attr->data = a.value().toString();
807 attr->parent = node;
808 node->attributes.append(attr);
809 }
810 }
811 break;
812 case QXmlStreamReader::EndElement:
813 nodeStack.pop();
814 break;
815 case QXmlStreamReader::Characters:
816 {
817 NodeImpl *node = new NodeImpl;
818 node->document = document;
819 node->type = reader.isCDATA()?NodeImpl::CDATA:NodeImpl::Text;
820 node->parent = nodeStack.top();
821 node->parent->children.append(node);
822 node->data = reader.text().toString();
823 }
824 break;
825 case QXmlStreamReader::Comment:
826 break;
827 case QXmlStreamReader::DTD:
828 break;
829 case QXmlStreamReader::EntityReference:
830 break;
831 case QXmlStreamReader::ProcessingInstruction:
832 break;
833 }
834 }
835
836 if (!document || reader.hasError()) {
837 if (document)
838 document->release();
839 return Encode::null();
840 }
841
842 ScopedObject instance(scope, v4->memoryManager->allocate<Node>(document));
843 document->release(); // the GC should own the NodeImpl via Node now
844 ScopedObject p(scope);
845 instance->setPrototypeUnchecked((p = Document::prototype(v4)));
846 return instance.asReturnedValue();
847}
848
849bool Node::isNull() const
850{
851 return d()->d == nullptr;
852}
853
854ReturnedValue NamedNodeMap::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
855{
856 Q_ASSERT(m->as<NamedNodeMap>());
857
858 const NamedNodeMap *r = static_cast<const NamedNodeMap *>(m);
859 QV4::ExecutionEngine *v4 = r->engine();
860
861 if (id.isArrayIndex()) {
862 uint index = id.asArrayIndex();
863
864 if ((int)index < r->d()->list().size()) {
865 if (hasProperty)
866 *hasProperty = true;
867 return Node::create(v4, r->d()->list().at(index));
868 }
869 if (hasProperty)
870 *hasProperty = false;
871 return Encode::undefined();
872 }
873
874 if (id.isSymbol())
875 return Object::virtualGet(m, id, receiver, hasProperty);
876
877 if (id == v4->id_length()->propertyKey())
878 return Value::fromInt32(r->d()->list().size()).asReturnedValue();
879
880 QString str = id.toQString();
881 for (int ii = 0; ii < r->d()->list().size(); ++ii) {
882 if (r->d()->list().at(ii)->name == str) {
883 if (hasProperty)
884 *hasProperty = true;
885 return Node::create(v4, r->d()->list().at(ii));
886 }
887 }
888
889 if (hasProperty)
890 *hasProperty = false;
891 return Encode::undefined();
892}
893
894ReturnedValue NamedNodeMap::create(ExecutionEngine *v4, NodeImpl *data, const QList<NodeImpl *> &list)
895{
896 return (v4->memoryManager->allocate<NamedNodeMap>(data, list))->asReturnedValue();
897}
898
899ReturnedValue NodeList::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
900{
901 Q_ASSERT(m->as<NodeList>());
902 const NodeList *r = static_cast<const NodeList *>(m);
903 QV4::ExecutionEngine *v4 = r->engine();
904
905 if (id.isArrayIndex()) {
906 uint index = id.asArrayIndex();
907 if ((int)index < r->d()->d->children.size()) {
908 if (hasProperty)
909 *hasProperty = true;
910 return Node::create(v4, r->d()->d->children.at(index));
911 }
912 if (hasProperty)
913 *hasProperty = false;
914 return Encode::undefined();
915 }
916
917 if (id == v4->id_length()->propertyKey())
918 return Value::fromInt32(r->d()->d->children.size()).asReturnedValue();
919 return Object::virtualGet(m, id, receiver, hasProperty);
920}
921
922ReturnedValue NodeList::create(ExecutionEngine *v4, NodeImpl *data)
923{
924 return (v4->memoryManager->allocate<NodeList>(data))->asReturnedValue();
925}
926
927ReturnedValue Document::method_documentElement(const FunctionObject *b, const Value *thisObject, const Value *, int)
928{
929 Scope scope(b);
930 Scoped<Node> r(scope, thisObject->as<Node>());
931 if (!r || r->d()->d->type != NodeImpl::Document)
932 RETURN_UNDEFINED();
933
934 return Node::create(scope.engine, static_cast<DocumentImpl *>(r->d()->d)->root);
935}
936
937ReturnedValue Document::method_xmlStandalone(const FunctionObject *b, const Value *thisObject, const Value *, int)
938{
939 Scope scope(b);
940 Scoped<Node> r(scope, thisObject->as<Node>());
941 if (!r || r->d()->d->type != NodeImpl::Document)
942 RETURN_UNDEFINED();
943
944 return Encode(static_cast<DocumentImpl *>(r->d()->d)->isStandalone);
945}
946
947ReturnedValue Document::method_xmlVersion(const FunctionObject *b, const Value *thisObject, const Value *, int)
948{
949 Scope scope(b);
950 Scoped<Node> r(scope, thisObject->as<Node>());
951 if (!r || r->d()->d->type != NodeImpl::Document)
952 RETURN_UNDEFINED();
953
954 return Encode(scope.engine->newString(static_cast<DocumentImpl *>(r->d()->d)->version));
955}
956
957ReturnedValue Document::method_xmlEncoding(const FunctionObject *b, const Value *thisObject, const Value *, int)
958{
959 Scope scope(b);
960 Scoped<Node> r(scope, thisObject->as<Node>());
961 if (!r || r->d()->d->type != NodeImpl::Document)
962 RETURN_UNDEFINED();
963
964 return Encode(scope.engine->newString(static_cast<DocumentImpl *>(r->d()->d)->encoding));
965}
966
968{
970public:
975 enum State {
981 };
982
983 QQmlXMLHttpRequest(QNetworkAccessManager *manager, QV4::ExecutionEngine *v4);
985
986 bool sendFlag() const;
987 bool errorFlag() const;
989 int replyStatus() const;
991
992 ReturnedValue open(Object *thisObject, const QString &, const QUrl &, LoadType);
993 ReturnedValue send(Object *thisObject, const QQmlRefPointer<QQmlContextData> &context,
994 const QByteArray &);
995 ReturnedValue abort(Object *thisObject);
996
997 void addHeader(const QString &, const QString &);
998 QString header(const QString &name) const;
1000
1003 bool receivedXml() const;
1004 QUrl url() const;
1005
1006 const QString & responseType() const;
1007 void setResponseType(const QString &);
1008 void setOverrideMimeType(QStringView mimeType) { m_overrideMime = mimeType.toUtf8(); }
1009 void setOverrideCharset(QStringView charset) { m_overrideCharset = charset.toUtf8(); }
1010
1011 const QByteArray mimeType() const;
1012 const QByteArray charset() const;
1013
1014 QV4::ReturnedValue jsonResponseBody(QV4::ExecutionEngine*);
1015 QV4::ReturnedValue xmlResponseBody(QV4::ExecutionEngine*);
1016private slots:
1017 void readyRead();
1018 void error(QNetworkReply::NetworkError);
1019 void finished();
1020
1021private:
1022 void requestFromUrl(const QUrl &url);
1023
1024 State m_state;
1025 bool m_errorFlag;
1026 bool m_sendFlag;
1027 QString m_method;
1028 QUrl m_url;
1029 QByteArray m_responseEntityBody;
1030 QByteArray m_data;
1031 int m_redirectCount;
1032
1033 typedef std::pair<QByteArray, QByteArray> HeaderPair;
1034 typedef QList<HeaderPair> HeadersList;
1035 HeadersList m_headersList;
1036 void fillHeadersList();
1037
1038 bool m_gotXml;
1039 QByteArray m_mime;
1040 QByteArray m_charset;
1041 QByteArray m_overrideMime;
1042 QByteArray m_overrideCharset;
1043
1044 QStringDecoder findTextDecoder() const;
1045 void readEncoding();
1046
1047 PersistentValue m_thisObject;
1048 QQmlRefPointer<QQmlContextData> m_qmlContext;
1049 bool m_wasConstructedWithQmlContext = true;
1050
1051 void dispatchCallbackNow(Object *thisObj);
1052 static void dispatchCallbackNow(Object *thisObj, bool done, bool error);
1053 void dispatchCallbackSafely();
1054
1055 int m_status;
1056 QString m_statusText;
1057 QNetworkRequest m_request;
1058 QStringList m_addedHeaders;
1059 QPointer<QNetworkReply> m_network;
1060 void destroyNetwork();
1061
1062 QNetworkAccessManager *m_nam;
1063 QNetworkAccessManager *networkAccessManager() { return m_nam; }
1064
1065 QString m_responseType;
1066 QV4::PersistentValue m_parsedDocument;
1067};
1068
1069QQmlXMLHttpRequest::QQmlXMLHttpRequest(QNetworkAccessManager *manager, QV4::ExecutionEngine *v4)
1070 : m_state(Unsent), m_errorFlag(false), m_sendFlag(false)
1071 , m_redirectCount(0), m_gotXml(false), m_network(nullptr), m_nam(manager)
1072{
1073 m_wasConstructedWithQmlContext = !v4->callingQmlContext().isNull();
1074}
1075
1077{
1078 destroyNetwork();
1079}
1080
1082{
1083 return m_sendFlag;
1084}
1085
1087{
1088 return m_errorFlag;
1089}
1090
1092{
1093 return m_state;
1094}
1095
1097{
1098 return m_status;
1099}
1100
1102{
1103 return m_statusText;
1104}
1105
1106ReturnedValue QQmlXMLHttpRequest::open(Object *thisObject, const QString &method, const QUrl &url, LoadType loadType)
1107{
1108 destroyNetwork();
1109 m_sendFlag = false;
1110 m_errorFlag = false;
1111 m_responseEntityBody = QByteArray();
1112 m_method = method;
1113 m_url = url;
1114 m_request.setAttribute(QNetworkRequest::SynchronousRequestAttribute, loadType == SynchronousLoad);
1115 m_state = Opened;
1116 m_addedHeaders.clear();
1117 dispatchCallbackNow(thisObject);
1118 return Encode::undefined();
1119}
1120
1121void QQmlXMLHttpRequest::addHeader(const QString &name, const QString &value)
1122{
1123 QByteArray utfname = name.toUtf8();
1124
1125 if (m_addedHeaders.contains(name, Qt::CaseInsensitive)) {
1126 m_request.setRawHeader(utfname, m_request.rawHeader(utfname) + ',' + value.toUtf8());
1127 } else {
1128 m_request.setRawHeader(utfname, value.toUtf8());
1129 m_addedHeaders.append(name);
1130 }
1131}
1132
1133QString QQmlXMLHttpRequest::header(const QString &name) const
1134{
1135 if (!m_headersList.isEmpty()) {
1136 const QByteArray utfname = name.toLower().toUtf8();
1137 for (const HeaderPair &header : m_headersList) {
1138 if (header.first == utfname)
1139 return QString::fromUtf8(header.second);
1140 }
1141 }
1142 return QString();
1143}
1144
1146{
1147 QString ret;
1148
1149 for (const HeaderPair &header : m_headersList) {
1150 if (ret.size())
1151 ret.append(QLatin1String("\r\n"));
1152 ret += QString::fromUtf8(header.first) + QLatin1String(": ")
1153 + QString::fromUtf8(header.second);
1154 }
1155 return ret;
1156}
1157
1158void QQmlXMLHttpRequest::fillHeadersList()
1159{
1160 const QList<QByteArray> headerList = m_network->rawHeaderList();
1161
1162 m_headersList.clear();
1163 for (const QByteArray &header : headerList) {
1164 HeaderPair pair (header.toLower(), m_network->rawHeader(header));
1165 if (pair.first == "set-cookie" ||
1166 pair.first == "set-cookie2")
1167 continue;
1168
1169 m_headersList << pair;
1170 }
1171}
1172
1173void QQmlXMLHttpRequest::requestFromUrl(const QUrl &url)
1174{
1175 m_url = url;
1176 QNetworkRequest request = m_request;
1177
1178 if (QQmlFile::isLocalFile(url)) {
1179 if (m_method == QLatin1String("PUT"))
1180 {
1181 if (!xhrFileWrite()) {
1182 qWarning("XMLHttpRequest: Using PUT on a local file is disabled by default.\n"
1183 "Set QML_XHR_ALLOW_FILE_WRITE to 1 to enable this feature.");
1184 return;
1185 }
1186 } else if (m_method == QLatin1String("GET")) {
1187 if (!xhrFileRead()) {
1188 qWarning("XMLHttpRequest: Using GET on a local file is disabled by default.\n"
1189 "Set QML_XHR_ALLOW_FILE_READ to 1 to enable this feature.");
1190 return;
1191 }
1192 } else {
1193 qWarning("XMLHttpRequest: Unsupported method used on a local file");
1194 return;
1195 }
1196 }
1197
1198 request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy);
1199 request.setUrl(url);
1200 if(m_method == QLatin1String("POST") ||
1201 m_method == QLatin1String("PUT")) {
1202 QVariant var = request.header(QNetworkRequest::ContentTypeHeader);
1203 if (var.isValid()) {
1204 QString str = var.toString();
1205 int charsetIdx = str.indexOf(QLatin1String("charset="));
1206 if (charsetIdx == -1) {
1207 // No charset - append
1208 if (!str.isEmpty()) str.append(QLatin1Char(';'));
1209 str.append(QLatin1String("charset=UTF-8"));
1210 } else {
1211 charsetIdx += 8;
1212 int n = 0;
1213 int semiColon = str.indexOf(QLatin1Char(';'), charsetIdx);
1214 if (semiColon == -1) {
1215 n = str.size() - charsetIdx;
1216 } else {
1217 n = semiColon - charsetIdx;
1218 }
1219
1220 str.replace(charsetIdx, n, QLatin1String("UTF-8"));
1221 }
1222 request.setHeader(QNetworkRequest::ContentTypeHeader, str);
1223 } else {
1224 request.setHeader(QNetworkRequest::ContentTypeHeader,
1225 QLatin1String("text/plain;charset=UTF-8"));
1226 }
1227 }
1228
1229 if (xhrDump()) {
1230 qWarning().nospace() << "XMLHttpRequest: " << qPrintable(m_method) << ' ' << qPrintable(url.toString());
1231 if (!m_data.isEmpty()) {
1232 qWarning().nospace() << " "
1233 << qPrintable(QString::fromUtf8(m_data));
1234 }
1235 }
1236
1237 if (m_method == QLatin1String("GET")) {
1238 m_network = networkAccessManager()->get(request);
1239 } else if (m_method == QLatin1String("HEAD")) {
1240 m_network = networkAccessManager()->head(request);
1241 } else if (m_method == QLatin1String("POST")) {
1242 m_network = networkAccessManager()->post(request, m_data);
1243 } else if (m_method == QLatin1String("PUT")) {
1244 m_network = networkAccessManager()->put(request, m_data);
1245 } else if (m_method == QLatin1String("DELETE")) {
1246 m_network = networkAccessManager()->deleteResource(request);
1247 } else if ((m_method == QLatin1String("OPTIONS")) ||
1248 m_method == QLatin1String("PROPFIND") ||
1249 m_method == QLatin1String("PATCH")) {
1250 QBuffer *buffer = new QBuffer;
1251 buffer->setData(m_data);
1252 buffer->open(QIODevice::ReadOnly);
1253 m_network = networkAccessManager()->sendCustomRequest(request, QByteArray(m_method.toUtf8().constData()), buffer);
1254 buffer->setParent(m_network);
1255 }
1256
1257 if (m_request.attribute(QNetworkRequest::SynchronousRequestAttribute).toBool()) {
1258 if (m_network->bytesAvailable() > 0)
1259 readyRead();
1260
1261 QNetworkReply::NetworkError networkError = m_network->error();
1262 if (networkError != QNetworkReply::NoError) {
1263 error(networkError);
1264 } else {
1265 finished();
1266 }
1267 } else {
1268 QObject::connect(m_network, SIGNAL(readyRead()),
1269 this, SLOT(readyRead()));
1270 QObject::connect(m_network, SIGNAL(errorOccurred(QNetworkReply::NetworkError)),
1271 this, SLOT(error(QNetworkReply::NetworkError)));
1272 QObject::connect(m_network, SIGNAL(finished()),
1273 this, SLOT(finished()));
1274 }
1275}
1276
1278 Object *thisObject, const QQmlRefPointer<QQmlContextData> &context, const QByteArray &data)
1279{
1280 m_errorFlag = false;
1281 m_sendFlag = true;
1282 m_redirectCount = 0;
1283 m_data = data;
1284
1285 m_thisObject = thisObject;
1286 m_qmlContext = context;
1287
1288 requestFromUrl(m_url);
1289
1290 return Encode::undefined();
1291}
1292
1294{
1295 destroyNetwork();
1296 m_responseEntityBody = QByteArray();
1297 m_errorFlag = true;
1298 m_request = QNetworkRequest();
1299
1300 if (!(m_state == Unsent ||
1301 (m_state == Opened && !m_sendFlag) ||
1302 m_state == Done)) {
1303
1304 m_state = Done;
1305 m_sendFlag = false;
1306 dispatchCallbackNow(thisObject);
1307 }
1308
1309 m_state = Unsent;
1310
1311 return Encode::undefined();
1312}
1313
1314void QQmlXMLHttpRequest::readyRead()
1315{
1316 m_status =
1317 m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
1318 m_statusText =
1319 QString::fromUtf8(m_network->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray());
1320
1321 // ### We assume if this is called the headers are now available
1322 if (m_state < HeadersReceived) {
1323 m_state = HeadersReceived;
1324 fillHeadersList();
1325 dispatchCallbackSafely();
1326 }
1327
1328 bool wasEmpty = m_responseEntityBody.isEmpty();
1329 m_responseEntityBody.append(m_network->readAll());
1330 if (wasEmpty && !m_responseEntityBody.isEmpty())
1331 m_state = Loading;
1332
1333 dispatchCallbackSafely();
1334}
1335
1336static const char *errorToString(QNetworkReply::NetworkError error)
1337{
1338 int idx = QNetworkReply::staticMetaObject.indexOfEnumerator("NetworkError");
1339 if (idx == -1) return "EnumLookupFailed";
1340
1341 QMetaEnum e = QNetworkReply::staticMetaObject.enumerator(idx);
1342
1343 const char *name = e.valueToKey(error);
1344 if (!name) return "EnumLookupFailed";
1345 else return name;
1346}
1347
1348void QQmlXMLHttpRequest::error(QNetworkReply::NetworkError error)
1349{
1350 m_status =
1351 m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
1352 m_statusText =
1353 QString::fromUtf8(m_network->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray());
1354
1355 m_request = QNetworkRequest();
1356 m_data.clear();
1357 destroyNetwork();
1358
1359 if (xhrDump()) {
1360 qWarning().nospace() << "XMLHttpRequest: ERROR " << qPrintable(m_url.toString());
1361 qWarning().nospace() << " " << error << ' ' << errorToString(error) << ' ' << m_statusText;
1362 }
1363
1364 if (error == QNetworkReply::ContentAccessDenied ||
1365 error == QNetworkReply::ContentOperationNotPermittedError ||
1366 error == QNetworkReply::ContentNotFoundError ||
1367 error == QNetworkReply::AuthenticationRequiredError ||
1368 error == QNetworkReply::ContentReSendError ||
1369 error == QNetworkReply::UnknownContentError ||
1370 error == QNetworkReply::ProtocolInvalidOperationError ||
1371 error == QNetworkReply::InternalServerError ||
1372 error == QNetworkReply::OperationNotImplementedError ||
1373 error == QNetworkReply::ServiceUnavailableError ||
1374 error == QNetworkReply::UnknownServerError) {
1375 m_state = Loading;
1376 dispatchCallbackSafely();
1377 } else {
1378 m_errorFlag = true;
1379 m_responseEntityBody = QByteArray();
1380 }
1381
1382 m_state = Done;
1383 dispatchCallbackSafely();
1384}
1385
1386#define XMLHTTPREQUEST_MAXIMUM_REDIRECT_RECURSION 15
1387void QQmlXMLHttpRequest::finished()
1388{
1389 m_redirectCount++;
1390 if (m_redirectCount < XMLHTTPREQUEST_MAXIMUM_REDIRECT_RECURSION) {
1391 QVariant redirect = m_network->attribute(QNetworkRequest::RedirectionTargetAttribute);
1392 if (redirect.isValid()) {
1393 QUrl url = m_network->url().resolved(redirect.toUrl());
1394 if (!QQmlFile::isLocalFile(url)) {
1395 // See http://www.ietf.org/rfc/rfc2616.txt, section 10.3.4 "303 See Other":
1396 // Result of 303 redirection should be a new "GET" request.
1397 const QVariant code = m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute);
1398 if (code.isValid() && code.toInt() == 303 && m_method != QLatin1String("GET"))
1399 m_method = QStringLiteral("GET");
1400 destroyNetwork();
1401
1402 // Discard redirect response body
1403 m_responseEntityBody = QByteArray();
1404
1405 requestFromUrl(url);
1406 return;
1407 }
1408 }
1409 }
1410
1411 m_status =
1412 m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
1413 m_statusText =
1414 QString::fromUtf8(m_network->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray());
1415
1416 if (m_state < HeadersReceived) {
1417 m_state = HeadersReceived;
1418 fillHeadersList ();
1419 dispatchCallbackSafely();
1420 }
1421 m_responseEntityBody.append(m_network->readAll());
1422 readEncoding();
1423
1424 if (xhrDump()) {
1425 qWarning().nospace() << "XMLHttpRequest: RESPONSE " << qPrintable(m_url.toString());
1426 if (!m_responseEntityBody.isEmpty()) {
1427 qWarning().nospace() << " "
1428 << qPrintable(QString::fromUtf8(m_responseEntityBody));
1429 }
1430 }
1431
1432 m_data.clear();
1433 destroyNetwork();
1434 if (m_state < Loading) {
1435 m_state = Loading;
1436 dispatchCallbackSafely();
1437 }
1438 m_state = Done;
1439
1440 dispatchCallbackSafely();
1441
1442 m_thisObject.clear();
1443 m_qmlContext.reset();
1444}
1445
1446
1447void QQmlXMLHttpRequest::readEncoding()
1448{
1449 for (const HeaderPair &header : std::as_const(m_headersList)) {
1450 if (header.first == "content-type") {
1451 int separatorIdx = header.second.indexOf(';');
1452 if (separatorIdx == -1) {
1453 m_mime = header.second;
1454 } else {
1455 m_mime = header.second.mid(0, separatorIdx);
1456 int charsetIdx = header.second.indexOf("charset=");
1457 if (charsetIdx != -1) {
1458 charsetIdx += 8;
1459 separatorIdx = header.second.indexOf(';', charsetIdx);
1460 m_charset = header.second.mid(charsetIdx, separatorIdx >= 0 ? separatorIdx : header.second.size());
1461 }
1462 }
1463 break;
1464 }
1465 }
1466
1467 const auto mime = mimeType();
1468 if (mime.isEmpty() || mime == "text/xml" || mime == "application/xml" || mime.endsWith("+xml"))
1469 m_gotXml = true;
1470}
1471
1473{
1474 return m_gotXml;
1475}
1476
1478{
1479 return m_url;
1480}
1481
1483{
1484 // Final MIME type is the override MIME type unless that is null in which
1485 // case it is the response MIME type.
1486 return m_overrideMime.isEmpty() ? m_mime : m_overrideMime;
1487}
1488
1490{
1491 // Final charset is the override charset unless that is null in which case
1492 // it is the response charset.
1493 return m_overrideCharset.isEmpty() ? m_charset : m_overrideCharset;
1494}
1495
1497{
1498 return m_responseType;
1499}
1500
1501void QQmlXMLHttpRequest::setResponseType(const QString &responseType)
1502{
1503 m_responseType = responseType;
1504}
1505
1507{
1508 if (m_parsedDocument.isEmpty()) {
1509 Scope scope(engine);
1510
1511 QJsonParseError error;
1512 const QString& jtext = responseBody();
1513 JsonParser parser(scope.engine, jtext.constData(), jtext.size());
1514 ScopedValue jsonObject(scope, parser.parse(&error));
1515 if (error.error != QJsonParseError::NoError)
1516 return engine->throwSyntaxError(QStringLiteral("JSON.parse: Parse error"));
1517
1518 m_parsedDocument.set(scope.engine, jsonObject);
1519 }
1520
1521 return m_parsedDocument.value();
1522}
1523
1525{
1526 if (m_parsedDocument.isEmpty()) {
1527 m_parsedDocument.set(engine, Document::load(engine, rawResponseBody()));
1528 }
1529
1530 return m_parsedDocument.value();
1531}
1532
1533QStringDecoder QQmlXMLHttpRequest::findTextDecoder() const
1534{
1535 QStringDecoder decoder;
1536
1537 if (!charset().isEmpty())
1538 decoder = QStringDecoder(charset());
1539
1540 if (!decoder.isValid() && m_gotXml) {
1541 QXmlStreamReader reader(m_responseEntityBody);
1542 reader.readNext();
1543 decoder = QStringDecoder(reader.documentEncoding().toString().toUtf8());
1544 }
1545
1546 if (!decoder.isValid() && mimeType() == "text/html")
1547 decoder = QStringDecoder::decoderForHtml(m_responseEntityBody);
1548
1549 if (!decoder.isValid()) {
1550 auto encoding = QStringConverter::encodingForData(m_responseEntityBody);
1551 if (encoding)
1552 decoder = QStringDecoder(*encoding);
1553 }
1554
1555 if (!decoder.isValid())
1556 decoder = QStringDecoder(QStringDecoder::Utf8);
1557
1558 return decoder;
1559}
1560
1562{
1563 QStringDecoder toUtf16 = findTextDecoder();
1564 return toUtf16(m_responseEntityBody);
1565}
1566
1568{
1569 return m_responseEntityBody;
1570}
1571
1572void QQmlXMLHttpRequest::dispatchCallbackNow(Object *thisObj)
1573{
1574 dispatchCallbackNow(thisObj, m_state == Done, m_errorFlag);
1575}
1576
1577void QQmlXMLHttpRequest::dispatchCallbackNow(Object *thisObj, bool done, bool error)
1578{
1579 Q_ASSERT(thisObj);
1580
1581 const auto dispatch = [thisObj](const QString &eventName) {
1582 QV4::Scope scope(thisObj->engine());
1583 ScopedString s(scope, scope.engine->newString(eventName));
1584 ScopedFunctionObject callback(scope, thisObj->get(s));
1585 // not an error, but no event handler to call.
1586 if (!callback)
1587 return;
1588
1589 QV4::JSCallArguments jsCallData(scope);
1590 callback->call(jsCallData);
1591
1592 if (scope.hasException()) {
1593 QQmlError error = scope.engine->catchExceptionAsQmlError();
1594 QQmlEnginePrivate *qmlEnginePrivate = scope.engine->qmlEngine() ? QQmlEnginePrivate::get(scope.engine->qmlEngine()) : nullptr;
1595 QQmlEnginePrivate::warning(qmlEnginePrivate, error);
1596 }
1597 };
1598
1599 dispatch(QStringLiteral("onreadystatechange"));
1600 if (done) {
1601 if (error)
1602 dispatch(QStringLiteral("onerror"));
1603 else
1604 dispatch(QStringLiteral("onload"));
1605 dispatch(QStringLiteral("onloadend"));
1606 }
1607}
1608
1609void QQmlXMLHttpRequest::dispatchCallbackSafely()
1610{
1611 if (m_wasConstructedWithQmlContext && m_qmlContext.isNull()) {
1612 // if the calling context object is no longer valid, then it has been
1613 // deleted explicitly (e.g., by a Loader deleting the itemContext when
1614 // the source is changed). We do nothing in this case, as the evaluation
1615 // cannot succeed.
1616 return;
1617 }
1618
1619 dispatchCallbackNow(m_thisObject.as<Object>());
1620}
1621
1622void QQmlXMLHttpRequest::destroyNetwork()
1623{
1624 if (m_network) {
1625 m_network->disconnect();
1626 m_network->deleteLater();
1627 m_network = nullptr;
1628 }
1629}
1630
1631namespace QV4 {
1632namespace Heap {
1633
1635 void init(QQmlXMLHttpRequest *request) {
1636 Object::init();
1637 this->request = request;
1638 }
1639
1640 void destroy() {
1641 delete request;
1642 Object::destroy();
1643 }
1645};
1646
1647#define QQmlXMLHttpRequestCtorMembers(class, Member)
1648 Member(class, Pointer, Object *, proto)
1649
1651 DECLARE_MARKOBJECTS(QQmlXMLHttpRequestCtor)
1652 void init(ExecutionEngine *engine);
1653};
1654
1655}
1656
1658{
1659 V4_OBJECT2(QQmlXMLHttpRequestWrapper, Object)
1661};
1662
1663// https://xhr.spec.whatwg.org/
1665{
1666 V4_OBJECT2(QQmlXMLHttpRequestCtor, FunctionObject)
1667
1680
1681 static ReturnedValue virtualCall(const FunctionObject *, const Value *, const Value *, int) {
1682 return Encode::undefined();
1683 }
1684
1686
1687 static ReturnedValue method_open(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1688 static ReturnedValue method_setRequestHeader(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1689 static ReturnedValue method_send(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1690 static ReturnedValue method_abort(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1691 static ReturnedValue method_getResponseHeader(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1692 static ReturnedValue method_getAllResponseHeaders(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1693 static ReturnedValue method_overrideMimeType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1694
1695 static ReturnedValue method_get_readyState(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1696 static ReturnedValue method_get_status(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1697 static ReturnedValue method_get_statusText(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1698 static ReturnedValue method_get_responseText(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1699 static ReturnedValue method_get_responseXML(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1700 static ReturnedValue method_get_response(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1701 static ReturnedValue method_get_responseType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1702 static ReturnedValue method_set_responseType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1703 static ReturnedValue method_get_responseURL(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1704};
1705
1706}
1707
1709
1710void Heap::QQmlXMLHttpRequestCtor::init(ExecutionEngine *engine)
1711{
1712 Heap::FunctionObject::init(engine, QStringLiteral("XMLHttpRequest"));
1713 Scope scope(engine);
1714 Scoped<QV4::QQmlXMLHttpRequestCtor> ctor(scope, this);
1715
1716 ctor->defineReadonlyProperty(QStringLiteral("UNSENT"), Value::fromInt32(QQmlXMLHttpRequest::Unsent));
1717 ctor->defineReadonlyProperty(QStringLiteral("OPENED"), Value::fromInt32(QQmlXMLHttpRequest::Opened));
1718 ctor->defineReadonlyProperty(QStringLiteral("HEADERS_RECEIVED"), Value::fromInt32(QQmlXMLHttpRequest::HeadersReceived));
1719 ctor->defineReadonlyProperty(QStringLiteral("LOADING"), Value::fromInt32(QQmlXMLHttpRequest::Loading));
1720 ctor->defineReadonlyProperty(QStringLiteral("DONE"), Value::fromInt32(QQmlXMLHttpRequest::Done));
1721
1722 if (!ctor->d()->proto)
1723 ctor->setupProto();
1724 ScopedString s(scope, engine->id_prototype());
1725 ctor->defineDefaultProperty(s, ScopedObject(scope, ctor->d()->proto));
1726}
1727
1729
1730void QQmlXMLHttpRequestCtor::setupProto()
1731{
1732 ExecutionEngine *v4 = engine();
1733 Scope scope(v4);
1734 ScopedObject p(scope, v4->newObject());
1735 d()->proto.set(scope.engine, p->d());
1736
1737 // Methods
1738 p->defineDefaultProperty(QStringLiteral("open"), method_open);
1739 p->defineDefaultProperty(QStringLiteral("setRequestHeader"), method_setRequestHeader);
1740 p->defineDefaultProperty(QStringLiteral("send"), method_send);
1741 p->defineDefaultProperty(QStringLiteral("abort"), method_abort);
1742 p->defineDefaultProperty(QStringLiteral("getResponseHeader"), method_getResponseHeader);
1743 p->defineDefaultProperty(QStringLiteral("getAllResponseHeaders"), method_getAllResponseHeaders);
1744 p->defineDefaultProperty(QStringLiteral("overrideMimeType"), method_overrideMimeType);
1745
1746 // Read-only properties
1747 p->defineAccessorProperty(QStringLiteral("readyState"), method_get_readyState, nullptr);
1748 p->defineAccessorProperty(QStringLiteral("status"),method_get_status, nullptr);
1749 p->defineAccessorProperty(QStringLiteral("statusText"),method_get_statusText, nullptr);
1750 p->defineAccessorProperty(QStringLiteral("responseText"),method_get_responseText, nullptr);
1751 p->defineAccessorProperty(QStringLiteral("responseXML"),method_get_responseXML, nullptr);
1752 p->defineAccessorProperty(QStringLiteral("response"),method_get_response, nullptr);
1753 p->defineAccessorProperty(QStringLiteral("responseURL"),method_get_responseURL, nullptr);
1754
1755 // Read-write properties
1756 p->defineAccessorProperty(QStringLiteral("responseType"), method_get_responseType, method_set_responseType);
1757
1758 // State values
1759 p->defineReadonlyProperty(QStringLiteral("UNSENT"), Value::fromInt32(0));
1760 p->defineReadonlyProperty(QStringLiteral("OPENED"), Value::fromInt32(1));
1761 p->defineReadonlyProperty(QStringLiteral("HEADERS_RECEIVED"), Value::fromInt32(2));
1762 p->defineReadonlyProperty(QStringLiteral("LOADING"), Value::fromInt32(3));
1763 p->defineReadonlyProperty(QStringLiteral("DONE"), Value::fromInt32(4));
1764}
1765
1766
1767// XMLHttpRequest methods
1768ReturnedValue QQmlXMLHttpRequestCtor::method_open(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1769{
1770 Scope scope(b);
1771 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
1772 if (!w)
1773 V4THROW_REFERENCE("Not an XMLHttpRequest object");
1774 QQmlXMLHttpRequest *r = w->d()->request;
1775
1776 if (argc < 2 || argc > 5)
1777 THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count");
1778
1779 // Argument 0 - Method
1780 QString method = argv[0].toQStringNoThrow().toUpper();
1781 if (method != QLatin1String("GET") &&
1782 method != QLatin1String("PUT") &&
1783 method != QLatin1String("HEAD") &&
1784 method != QLatin1String("POST") &&
1785 method != QLatin1String("DELETE") &&
1786 method != QLatin1String("OPTIONS") &&
1787 method != QLatin1String("PROPFIND") &&
1788 method != QLatin1String("PATCH"))
1789 THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Unsupported HTTP method type");
1790
1791 // Argument 1 - URL
1792 QUrl url = QUrl(argv[1].toQStringNoThrow());
1793
1794 if (url.isRelative()) {
1795 if (QQmlRefPointer<QQmlContextData> qmlContextData = scope.engine->callingQmlContext())
1796 url = qmlContextData->resolvedUrl(url);
1797 else
1798 url = scope.engine->resolvedUrl(url.url());
1799 }
1800
1801 bool async = true;
1802 // Argument 2 - async (optional)
1803 if (argc > 2) {
1804 async = argv[2].booleanValue();
1805 }
1806
1807 // Argument 3/4 - user/pass (optional)
1808 QString username, password;
1809 if (argc > 3)
1810 username = argv[3].toQStringNoThrow();
1811 if (argc > 4)
1812 password = argv[4].toQStringNoThrow();
1813
1814 // Clear the fragment (if any)
1815 url.setFragment(QString());
1816
1817 // Set username/password
1818 if (!username.isNull()) url.setUserName(username);
1819 if (!password.isNull()) url.setPassword(password);
1820
1821 return r->open(w, method, url, async ? QQmlXMLHttpRequest::AsynchronousLoad : QQmlXMLHttpRequest::SynchronousLoad);
1822}
1823
1824ReturnedValue QQmlXMLHttpRequestCtor::method_setRequestHeader(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1825{
1826 Scope scope(b);
1827 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
1828 if (!w)
1829 V4THROW_REFERENCE("Not an XMLHttpRequest object");
1830 QQmlXMLHttpRequest *r = w->d()->request;
1831
1832 if (argc != 2)
1833 THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count");
1834
1835 if (r->readyState() != QQmlXMLHttpRequest::Opened || r->sendFlag())
1836 THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
1837
1838 QString name = argv[0].toQStringNoThrow();
1839 QString value = argv[1].toQStringNoThrow();
1840
1841 // ### Check that name and value are well formed
1842
1843 QString nameUpper = name.toUpper();
1844 if (nameUpper == QLatin1String("ACCEPT-CHARSET") ||
1845 nameUpper == QLatin1String("ACCEPT-ENCODING") ||
1846 nameUpper == QLatin1String("CONNECTION") ||
1847 nameUpper == QLatin1String("CONTENT-LENGTH") ||
1848 nameUpper == QLatin1String("COOKIE") ||
1849 nameUpper == QLatin1String("COOKIE2") ||
1850 nameUpper == QLatin1String("CONTENT-TRANSFER-ENCODING") ||
1851 nameUpper == QLatin1String("DATE") ||
1852 nameUpper == QLatin1String("EXPECT") ||
1853 nameUpper == QLatin1String("HOST") ||
1854 nameUpper == QLatin1String("KEEP-ALIVE") ||
1855 nameUpper == QLatin1String("REFERER") ||
1856 nameUpper == QLatin1String("TE") ||
1857 nameUpper == QLatin1String("TRAILER") ||
1858 nameUpper == QLatin1String("TRANSFER-ENCODING") ||
1859 nameUpper == QLatin1String("UPGRADE") ||
1860 nameUpper == QLatin1String("VIA") ||
1861 nameUpper.startsWith(QLatin1String("PROXY-")) ||
1862 nameUpper.startsWith(QLatin1String("SEC-")))
1863 RETURN_UNDEFINED();
1864
1865 r->addHeader(name, value);
1866
1867 RETURN_UNDEFINED();
1868}
1869
1870ReturnedValue QQmlXMLHttpRequestCtor::method_send(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1871{
1872 Scope scope(b);
1873 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
1874 if (!w)
1875 V4THROW_REFERENCE("Not an XMLHttpRequest object");
1876 QQmlXMLHttpRequest *r = w->d()->request;
1877
1878 if (r->readyState() != QQmlXMLHttpRequest::Opened ||
1879 r->sendFlag())
1880 THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
1881
1882 QByteArray data;
1883 if (argc > 0) {
1884 if (const ArrayBuffer *buffer = argv[0].as<ArrayBuffer>()) {
1885 data = buffer->asByteArray();
1886 } else {
1887 data = argv[0].toQStringNoThrow().toUtf8();
1888 }
1889 }
1890
1891 return r->send(w, scope.engine->callingQmlContext(), data);
1892}
1893
1894ReturnedValue QQmlXMLHttpRequestCtor::method_abort(const FunctionObject *b, const Value *thisObject, const Value *, int)
1895{
1896 Scope scope(b);
1897 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
1898 if (!w)
1899 V4THROW_REFERENCE("Not an XMLHttpRequest object");
1900 QQmlXMLHttpRequest *r = w->d()->request;
1901
1902 return r->abort(w);
1903}
1904
1905ReturnedValue QQmlXMLHttpRequestCtor::method_getResponseHeader(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1906{
1907 Scope scope(b);
1908 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
1909 if (!w)
1910 V4THROW_REFERENCE("Not an XMLHttpRequest object");
1911 QQmlXMLHttpRequest *r = w->d()->request;
1912
1913 if (argc != 1)
1914 THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count");
1915
1916 if (r->readyState() != QQmlXMLHttpRequest::Loading &&
1917 r->readyState() != QQmlXMLHttpRequest::Done &&
1918 r->readyState() != QQmlXMLHttpRequest::HeadersReceived)
1919 THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
1920
1921 return Encode(scope.engine->newString(r->header(argv[0].toQStringNoThrow())));
1922}
1923
1924ReturnedValue QQmlXMLHttpRequestCtor::method_getAllResponseHeaders(const FunctionObject *b, const Value *thisObject, const Value *, int argc)
1925{
1926 Scope scope(b);
1927 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
1928 if (!w)
1929 V4THROW_REFERENCE("Not an XMLHttpRequest object");
1930 QQmlXMLHttpRequest *r = w->d()->request;
1931
1932 if (argc != 0)
1933 THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count");
1934
1935 if (r->readyState() != QQmlXMLHttpRequest::Loading &&
1936 r->readyState() != QQmlXMLHttpRequest::Done &&
1937 r->readyState() != QQmlXMLHttpRequest::HeadersReceived)
1938 THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
1939
1940 return Encode(scope.engine->newString(r->headers()));
1941}
1942
1943// XMLHttpRequest properties
1944ReturnedValue QQmlXMLHttpRequestCtor::method_get_readyState(const FunctionObject *b, const Value *thisObject, const Value *, int)
1945{
1946 Scope scope(b);
1947 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
1948 if (!w)
1949 V4THROW_REFERENCE("Not an XMLHttpRequest object");
1950 QQmlXMLHttpRequest *r = w->d()->request;
1951
1952 return Encode(r->readyState());
1953}
1954
1955ReturnedValue QQmlXMLHttpRequestCtor::method_get_status(const FunctionObject *b, const Value *thisObject, const Value *, int)
1956{
1957 Scope scope(b);
1958 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
1959 if (!w)
1960 V4THROW_REFERENCE("Not an XMLHttpRequest object");
1961 QQmlXMLHttpRequest *r = w->d()->request;
1962
1963 if (r->readyState() == QQmlXMLHttpRequest::Unsent ||
1964 r->readyState() == QQmlXMLHttpRequest::Opened)
1965 THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
1966
1967 if (r->errorFlag())
1968 return Encode(0);
1969 else
1970 return Encode(r->replyStatus());
1971}
1972
1973ReturnedValue QQmlXMLHttpRequestCtor::method_get_statusText(const FunctionObject *b, const Value *thisObject, const Value *, int)
1974{
1975 Scope scope(b);
1976 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
1977 if (!w)
1978 V4THROW_REFERENCE("Not an XMLHttpRequest object");
1979 QQmlXMLHttpRequest *r = w->d()->request;
1980
1981 if (r->readyState() == QQmlXMLHttpRequest::Unsent ||
1982 r->readyState() == QQmlXMLHttpRequest::Opened)
1983 THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
1984
1985 if (r->errorFlag())
1986 return Encode(scope.engine->newString(QString()));
1987 else
1988 return Encode(scope.engine->newString(r->replyStatusText()));
1989}
1990
1991ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseText(const FunctionObject *b, const Value *thisObject, const Value *, int)
1992{
1993 Scope scope(b);
1994 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
1995 if (!w)
1996 V4THROW_REFERENCE("Not an XMLHttpRequest object");
1997 QQmlXMLHttpRequest *r = w->d()->request;
1998
1999 if (r->readyState() != QQmlXMLHttpRequest::Loading &&
2000 r->readyState() != QQmlXMLHttpRequest::Done)
2001 return Encode(scope.engine->newString(QString()));
2002 else
2003 return Encode(scope.engine->newString(r->responseBody()));
2004}
2005
2006ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseXML(const FunctionObject *b, const Value *thisObject, const Value *, int)
2007{
2008 Scope scope(b);
2009 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
2010 if (!w)
2011 V4THROW_REFERENCE("Not an XMLHttpRequest object");
2012 QQmlXMLHttpRequest *r = w->d()->request;
2013
2014 if (!r->receivedXml() ||
2015 (r->readyState() != QQmlXMLHttpRequest::Loading &&
2016 r->readyState() != QQmlXMLHttpRequest::Done)) {
2017 return Encode::null();
2018 } else {
2019 if (r->responseType().isEmpty())
2020 r->setResponseType(QLatin1String("document"));
2021 return r->xmlResponseBody(scope.engine);
2022 }
2023}
2024
2025ReturnedValue QQmlXMLHttpRequestCtor::method_get_response(const FunctionObject *b, const Value *thisObject, const Value *, int)
2026{
2027 Scope scope(b);
2028 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
2029 if (!w)
2030 V4THROW_REFERENCE("Not an XMLHttpRequest object");
2031 QQmlXMLHttpRequest *r = w->d()->request;
2032
2033 if (r->readyState() != QQmlXMLHttpRequest::Loading &&
2034 r->readyState() != QQmlXMLHttpRequest::Done)
2035 RETURN_RESULT(scope.engine->newString(QString()));
2036
2037 const QString& responseType = r->responseType();
2038 if (responseType.compare(QLatin1String("text"), Qt::CaseInsensitive) == 0 || responseType.isEmpty()) {
2039 RETURN_RESULT(scope.engine->newString(r->responseBody()));
2040 } else if (responseType.compare(QLatin1String("arraybuffer"), Qt::CaseInsensitive) == 0) {
2041 RETURN_RESULT(scope.engine->newArrayBuffer(r->rawResponseBody()));
2042 } else if (responseType.compare(QLatin1String("json"), Qt::CaseInsensitive) == 0) {
2043 RETURN_RESULT(r->jsonResponseBody(scope.engine));
2044 } else if (responseType.compare(QLatin1String("document"), Qt::CaseInsensitive) == 0) {
2045 RETURN_RESULT(r->xmlResponseBody(scope.engine));
2046 } else {
2047 RETURN_RESULT(scope.engine->newString(QString()));
2048 }
2049}
2050
2051
2052ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseType(const FunctionObject *b, const Value *thisObject, const Value *, int)
2053{
2054 Scope scope(b);
2055 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
2056 if (!w)
2057 V4THROW_REFERENCE("Not an XMLHttpRequest object");
2058 QQmlXMLHttpRequest *r = w->d()->request;
2059 return Encode(scope.engine->newString(r->responseType()));
2060}
2061
2062ReturnedValue QQmlXMLHttpRequestCtor::method_set_responseType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
2063{
2064 Scope scope(b);
2065 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
2066 if (!w)
2067 V4THROW_REFERENCE("Not an XMLHttpRequest object");
2068 QQmlXMLHttpRequest *r = w->d()->request;
2069
2070 if (argc < 1)
2071 THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count");
2072
2073 // Argument 0 - response type
2074 r->setResponseType(argv[0].toQStringNoThrow());
2075
2076 return Encode::undefined();
2077}
2078
2079ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseURL(const FunctionObject *b, const Value *thisObject, const Value *, int)
2080{
2081 Scope scope(b);
2082 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
2083 if (!w)
2084 V4THROW_REFERENCE("Not an XMLHttpRequest object");
2085 QQmlXMLHttpRequest *r = w->d()->request;
2086
2087 if (r->readyState() != QQmlXMLHttpRequest::Loading &&
2088 r->readyState() != QQmlXMLHttpRequest::Done) {
2089 return Encode(scope.engine->newString(QString()));
2090 } else {
2091 QUrl url = r->url();
2092 url.setFragment(QString());
2093 return Encode(scope.engine->newString(url.toString()));
2094 }
2095}
2096
2097ReturnedValue QQmlXMLHttpRequestCtor::method_overrideMimeType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
2098{
2099 Scope scope(b);
2100 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
2101 if (!w)
2102 V4THROW_REFERENCE("Not an XMLHttpRequest object");
2103 QQmlXMLHttpRequest *r = w->d()->request;
2104
2105 if (argc != 1)
2106 THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count");
2107
2108 // If state is loading or done, throw an InvalidStateError exception.
2109 if (r->readyState() == QQmlXMLHttpRequest::Loading ||
2110 r->readyState() == QQmlXMLHttpRequest::Done)
2111 THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
2112
2113 // Set override MIME type to `application/octet-stream`.
2114 r->setOverrideMimeType(QStringLiteral("application/octet-stream"));
2115 const auto parts = argv[0].toQStringNoThrow().split(QLatin1Char(';'));
2116 const auto type = parts.at(0).trimmed();
2117
2118 const auto mimeInvalidCharacter = [](QChar uni) {
2119 if (uni.unicode() > 127) // Only accept ASCII
2120 return true;
2121 const char ch = char(uni.unicode());
2122 return !(ch == '-' || ch == '/' || isAsciiLetterOrNumber(ch));
2123 };
2124
2125 // If mime is a parsable MIME type, ...
2126 if (type.count(QLatin1Char('/')) == 1
2127 && std::find_if(type.begin(), type.end(), mimeInvalidCharacter) == type.end()) {
2128 // ... then set override MIME type to its MIME type portion.
2129 r->setOverrideMimeType(type);
2130 }
2131 for (const auto &part : parts) {
2132 const QLatin1String charset("charset=");
2133 // If override MIME type has a `charset` parameter, ...
2134 if (part.trimmed().startsWith(charset)) {
2135 // ... then set override charset to its value.
2136 const int offset(part.indexOf(charset) + charset.size());
2137 r->setOverrideCharset(part.sliced(offset).trimmed());
2138 }
2139 }
2140
2141 return Encode::undefined();
2142}
2143
2144void qt_rem_qmlxmlhttprequest(ExecutionEngine * /* engine */, void *d)
2145{
2147 delete data;
2148}
2149
2150void *qt_add_qmlxmlhttprequest(ExecutionEngine *v4)
2151{
2152 Scope scope(v4);
2153
2154 Scoped<QQmlXMLHttpRequestCtor> ctor(scope, v4->memoryManager->allocate<QQmlXMLHttpRequestCtor>(v4));
2155 ScopedString s(scope, v4->newString(QStringLiteral("XMLHttpRequest")));
2156 v4->globalObject->defineReadonlyProperty(s, ctor);
2157
2159 return data;
2160}
2161
2162QT_END_NAMESPACE
2163
2164#include <qqmlxmlhttprequest.moc>
friend class QQmlEnginePrivate
void setOverrideCharset(QStringView charset)
void setResponseType(const QString &)
void setOverrideMimeType(QStringView mimeType)
ReturnedValue abort(Object *thisObject)
QQmlXMLHttpRequest(QNetworkAccessManager *manager, QV4::ExecutionEngine *v4)
QV4::ReturnedValue jsonResponseBody(QV4::ExecutionEngine *)
const QByteArray charset() const
const QByteArray & rawResponseBody() const
void addHeader(const QString &, const QString &)
QString header(const QString &name) const
QString replyStatusText() const
const QString & responseType() const
const QByteArray mimeType() const
QV4::ReturnedValue xmlResponseBody(QV4::ExecutionEngine *)
ReturnedValue send(Object *thisObject, const QQmlRefPointer< QQmlContextData > &context, const QByteArray &)
ReturnedValue open(Object *thisObject, const QString &, const QUrl &, LoadType)
static ReturnedValue prototype(ExecutionEngine *)
static ReturnedValue method_name(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_value(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_ownerElement(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue prototype(ExecutionEngine *v4)
static ReturnedValue prototype(ExecutionEngine *v4)
static ReturnedValue method_length(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_documentElement(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_xmlStandalone(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue load(ExecutionEngine *engine, const QByteArray &data)
static ReturnedValue prototype(ExecutionEngine *)
static ReturnedValue method_xmlVersion(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_xmlEncoding(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue prototype(ExecutionEngine *)
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
QList< NodeImpl * > attributes
QList< NodeImpl * > children
DocumentImpl * document
static ReturnedValue create(ExecutionEngine *, NodeImpl *)
static ReturnedValue method_get_childNodes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_nodeValue(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_previousSibling(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_namespaceUri(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue getProto(ExecutionEngine *v4)
static ReturnedValue method_get_parentNode(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_nextSibling(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_nodeType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_lastChild(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_firstChild(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_attributes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_nodeName(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_wholeText(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue prototype(ExecutionEngine *)
static ReturnedValue method_isElementContentWhitespace(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
DECLARE_HEAP_OBJECT(QmlContext, ExecutionContext)
Definition qjsvalue.h:23
DEFINE_OBJECT_VTABLE(Node)
DEFINE_OBJECT_VTABLE(NodeList)
DEFINE_OBJECT_VTABLE(NamedNodeMap)
DEFINE_OBJECT_VTABLE(NodePrototype)
#define DEFINE_BOOL_CONFIG_OPTION(name, var)
static const char * errorToString(QNetworkReply::NetworkError error)
DEFINE_OBJECT_VTABLE(QQmlXMLHttpRequestWrapper)
static QQmlXMLHttpRequestData * xhrdata(ExecutionEngine *v4)
void * qt_add_qmlxmlhttprequest(ExecutionEngine *v4)
DEFINE_OBJECT_VTABLE(QQmlXMLHttpRequestCtor)
void qt_rem_qmlxmlhttprequest(ExecutionEngine *, void *d)
#define XMLHTTPREQUEST_MAXIMUM_REDIRECT_RECURSION
#define V4THROW_REFERENCE(string)
PersistentValue characterDataPrototype
PersistentValue documentPrototype
QList< NodeImpl * > * listPtr
void init(NodeImpl *data, const QList< NodeImpl * > &list)
QList< NodeImpl * > & list()
void init(NodeImpl *data)
void init(NodeImpl *data)
void init(QQmlXMLHttpRequest *request)
bool isNull() const
static ReturnedValue method_getAllResponseHeaders(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_response(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)
static ReturnedValue method_set_responseType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_setRequestHeader(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_responseText(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_open(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_getResponseHeader(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_get_responseXML(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_abort(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_statusText(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)
static ReturnedValue method_send(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)