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

source code of qtdeclarative/src/qml/qml/qqmlxmlhttprequest.cpp