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