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
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(Object *thisObject, const QString &, const QUrl &, LoadType);
994 ReturnedValue send(Object *thisObject, const QQmlRefPointer<QQmlContextData> &context,
995 const QByteArray &);
996 ReturnedValue abort(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:
1018 void readyRead();
1019 void error(QNetworkReply::NetworkError);
1020 void finished();
1021
1022private:
1023 void requestFromUrl(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(Object *thisObj);
1053 static void dispatchCallbackNow(Object *thisObj, bool done, bool error);
1054 void dispatchCallbackSafely();
1055
1056 int m_status;
1057 QString m_statusText;
1058 QNetworkRequest m_request;
1059 QStringList m_addedHeaders;
1060 QPointer<QNetworkReply> m_network;
1061 void destroyNetwork();
1062
1063 QNetworkAccessManager *m_nam;
1064 QNetworkAccessManager *networkAccessManager() { return m_nam; }
1065
1066 QString m_responseType;
1067 QV4::PersistentValue m_parsedDocument;
1068};
1069
1070QQmlXMLHttpRequest::QQmlXMLHttpRequest(QNetworkAccessManager *manager, QV4::ExecutionEngine *v4)
1071 : m_state(Unsent), m_errorFlag(false), m_sendFlag(false)
1072 , m_redirectCount(0), m_gotXml(false), m_network(nullptr), m_nam(manager)
1073{
1074 m_wasConstructedWithQmlContext = !v4->callingQmlContext().isNull();
1075}
1076
1078{
1079 destroyNetwork();
1080}
1081
1083{
1084 return m_sendFlag;
1085}
1086
1088{
1089 return m_errorFlag;
1090}
1091
1093{
1094 return m_state;
1095}
1096
1098{
1099 return m_status;
1100}
1101
1103{
1104 return m_statusText;
1105}
1106
1107ReturnedValue QQmlXMLHttpRequest::open(Object *thisObject, const QString &method, const QUrl &url, LoadType loadType)
1108{
1109 destroyNetwork();
1110 m_sendFlag = false;
1111 m_errorFlag = false;
1112 m_responseEntityBody = QByteArray();
1113 m_method = method;
1114 m_url = url;
1115 m_request.setAttribute(QNetworkRequest::SynchronousRequestAttribute, loadType == SynchronousLoad);
1116 m_state = Opened;
1117 m_addedHeaders.clear();
1118 dispatchCallbackNow(thisObject);
1119 return Encode::undefined();
1120}
1121
1122void QQmlXMLHttpRequest::addHeader(const QString &name, const QString &value)
1123{
1124 QByteArray utfname = name.toUtf8();
1125
1126 if (m_addedHeaders.contains(name, Qt::CaseInsensitive)) {
1127 m_request.setRawHeader(utfname, m_request.rawHeader(utfname) + ',' + value.toUtf8());
1128 } else {
1129 m_request.setRawHeader(utfname, value.toUtf8());
1130 m_addedHeaders.append(name);
1131 }
1132}
1133
1134QString QQmlXMLHttpRequest::header(const QString &name) const
1135{
1136 if (!m_headersList.isEmpty()) {
1137 const QByteArray utfname = name.toLower().toUtf8();
1138 for (const HeaderPair &header : m_headersList) {
1139 if (header.first == utfname)
1140 return QString::fromUtf8(header.second);
1141 }
1142 }
1143 return QString();
1144}
1145
1147{
1148 QString ret;
1149
1150 for (const HeaderPair &header : m_headersList) {
1151 if (ret.size())
1152 ret.append(QLatin1String("\r\n"));
1153 ret += QString::fromUtf8(header.first) + QLatin1String(": ")
1154 + QString::fromUtf8(header.second);
1155 }
1156 return ret;
1157}
1158
1159void QQmlXMLHttpRequest::fillHeadersList()
1160{
1161 const QList<QByteArray> headerList = m_network->rawHeaderList();
1162
1163 m_headersList.clear();
1164 for (const QByteArray &header : headerList) {
1165 HeaderPair pair (header.toLower(), m_network->rawHeader(header));
1166 if (pair.first == "set-cookie" ||
1167 pair.first == "set-cookie2")
1168 continue;
1169
1170 m_headersList << pair;
1171 }
1172}
1173
1174void QQmlXMLHttpRequest::requestFromUrl(const QUrl &url)
1175{
1176 m_url = url;
1177 QNetworkRequest request = m_request;
1178
1179 if (QQmlFile::isLocalFile(url)) {
1180 if (m_method == QLatin1String("PUT"))
1181 {
1182 if (!xhrFileWrite()) {
1183 qWarning("XMLHttpRequest: Using PUT on a local file is disabled by default.\n"
1184 "Set QML_XHR_ALLOW_FILE_WRITE to 1 to enable this feature.");
1185 return;
1186 }
1187 } else if (m_method == QLatin1String("GET")) {
1188 if (!xhrFileRead()) {
1189 qWarning("XMLHttpRequest: Using GET on a local file is disabled by default.\n"
1190 "Set QML_XHR_ALLOW_FILE_READ to 1 to enable this feature.");
1191 return;
1192 }
1193 } else {
1194 qWarning("XMLHttpRequest: Unsupported method used on a local file");
1195 return;
1196 }
1197 }
1198
1199 request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::ManualRedirectPolicy);
1200 request.setUrl(url);
1201 if(m_method == QLatin1String("POST") ||
1202 m_method == QLatin1String("PUT")) {
1203 QVariant var = request.header(QNetworkRequest::ContentTypeHeader);
1204 if (var.isValid()) {
1205 QString str = var.toString();
1206 int charsetIdx = str.indexOf(QLatin1String("charset="));
1207 if (charsetIdx == -1) {
1208 // No charset - append
1209 if (!str.isEmpty()) str.append(QLatin1Char(';'));
1210 str.append(QLatin1String("charset=UTF-8"));
1211 } else {
1212 charsetIdx += 8;
1213 int n = 0;
1214 int semiColon = str.indexOf(QLatin1Char(';'), charsetIdx);
1215 if (semiColon == -1) {
1216 n = str.size() - charsetIdx;
1217 } else {
1218 n = semiColon - charsetIdx;
1219 }
1220
1221 str.replace(charsetIdx, n, QLatin1String("UTF-8"));
1222 }
1223 request.setHeader(QNetworkRequest::ContentTypeHeader, str);
1224 } else {
1225 request.setHeader(QNetworkRequest::ContentTypeHeader,
1226 QLatin1String("text/plain;charset=UTF-8"));
1227 }
1228 }
1229
1230 if (xhrDump()) {
1231 qWarning().nospace() << "XMLHttpRequest: " << qPrintable(m_method) << ' ' << qPrintable(url.toString());
1232 if (!m_data.isEmpty()) {
1233 qWarning().nospace() << " "
1234 << qPrintable(QString::fromUtf8(m_data));
1235 }
1236 }
1237
1238 if (m_method == QLatin1String("GET")) {
1239 m_network = networkAccessManager()->get(request);
1240 } else if (m_method == QLatin1String("HEAD")) {
1241 m_network = networkAccessManager()->head(request);
1242 } else if (m_method == QLatin1String("POST")) {
1243 m_network = networkAccessManager()->post(request, m_data);
1244 } else if (m_method == QLatin1String("PUT")) {
1245 m_network = networkAccessManager()->put(request, m_data);
1246 } else if (m_method == QLatin1String("DELETE")) {
1247 m_network = networkAccessManager()->deleteResource(request);
1248 } else if ((m_method == QLatin1String("OPTIONS")) ||
1249 m_method == QLatin1String("PROPFIND") ||
1250 m_method == QLatin1String("PATCH")) {
1251 QBuffer *buffer = new QBuffer;
1252 buffer->setData(m_data);
1253 buffer->open(QIODevice::ReadOnly);
1254 m_network = networkAccessManager()->sendCustomRequest(request, QByteArray(m_method.toUtf8().constData()), buffer);
1255 buffer->setParent(m_network);
1256 }
1257
1258 if (m_request.attribute(QNetworkRequest::SynchronousRequestAttribute).toBool()) {
1259 if (m_network->bytesAvailable() > 0)
1260 readyRead();
1261
1262 QNetworkReply::NetworkError networkError = m_network->error();
1263 if (networkError != QNetworkReply::NoError) {
1264 error(networkError);
1265 } else {
1266 finished();
1267 }
1268 } else {
1269 QObject::connect(m_network, SIGNAL(readyRead()),
1270 this, SLOT(readyRead()));
1271 QObject::connect(m_network, SIGNAL(errorOccurred(QNetworkReply::NetworkError)),
1272 this, SLOT(error(QNetworkReply::NetworkError)));
1273 QObject::connect(m_network, SIGNAL(finished()),
1274 this, SLOT(finished()));
1275 }
1276}
1277
1279 Object *thisObject, const QQmlRefPointer<QQmlContextData> &context, const QByteArray &data)
1280{
1281 m_errorFlag = false;
1282 m_sendFlag = true;
1283 m_redirectCount = 0;
1284 m_data = data;
1285
1286 m_thisObject = thisObject;
1287 m_qmlContext = context;
1288
1289 requestFromUrl(m_url);
1290
1291 return Encode::undefined();
1292}
1293
1295{
1296 destroyNetwork();
1297 m_responseEntityBody = QByteArray();
1298 m_errorFlag = true;
1299 m_request = QNetworkRequest();
1300
1301 if (!(m_state == Unsent ||
1302 (m_state == Opened && !m_sendFlag) ||
1303 m_state == Done)) {
1304
1305 m_state = Done;
1306 m_sendFlag = false;
1307 dispatchCallbackNow(thisObject);
1308 }
1309
1310 m_state = Unsent;
1311
1312 return Encode::undefined();
1313}
1314
1315void QQmlXMLHttpRequest::readyRead()
1316{
1317 m_status =
1318 m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
1319 m_statusText =
1320 QString::fromUtf8(m_network->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray());
1321
1322 // ### We assume if this is called the headers are now available
1323 if (m_state < HeadersReceived) {
1324 m_state = HeadersReceived;
1325 fillHeadersList();
1326 dispatchCallbackSafely();
1327 }
1328
1329 bool wasEmpty = m_responseEntityBody.isEmpty();
1330 m_responseEntityBody.append(m_network->readAll());
1331 if (wasEmpty && !m_responseEntityBody.isEmpty())
1332 m_state = Loading;
1333
1334 dispatchCallbackSafely();
1335}
1336
1337static const char *errorToString(QNetworkReply::NetworkError error)
1338{
1339 int idx = QNetworkReply::staticMetaObject.indexOfEnumerator("NetworkError");
1340 if (idx == -1) return "EnumLookupFailed";
1341
1342 QMetaEnum e = QNetworkReply::staticMetaObject.enumerator(idx);
1343
1344 const char *name = e.valueToKey(error);
1345 if (!name) return "EnumLookupFailed";
1346 else return name;
1347}
1348
1349void QQmlXMLHttpRequest::error(QNetworkReply::NetworkError error)
1350{
1351 m_status =
1352 m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
1353 m_statusText =
1354 QString::fromUtf8(m_network->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray());
1355
1356 m_request = QNetworkRequest();
1357 m_data.clear();
1358 destroyNetwork();
1359
1360 if (xhrDump()) {
1361 qWarning().nospace() << "XMLHttpRequest: ERROR " << qPrintable(m_url.toString());
1362 qWarning().nospace() << " " << error << ' ' << errorToString(error) << ' ' << m_statusText;
1363 }
1364
1365 if (error == QNetworkReply::ContentAccessDenied ||
1366 error == QNetworkReply::ContentOperationNotPermittedError ||
1367 error == QNetworkReply::ContentNotFoundError ||
1368 error == QNetworkReply::AuthenticationRequiredError ||
1369 error == QNetworkReply::ContentReSendError ||
1370 error == QNetworkReply::ContentConflictError ||
1371 error == QNetworkReply::ContentGoneError ||
1372 error == QNetworkReply::UnknownContentError ||
1373 error == QNetworkReply::ProtocolInvalidOperationError ||
1374 error == QNetworkReply::InternalServerError ||
1375 error == QNetworkReply::OperationNotImplementedError ||
1376 error == QNetworkReply::ServiceUnavailableError ||
1377 error == QNetworkReply::UnknownServerError) {
1378 m_state = Loading;
1379 dispatchCallbackSafely();
1380 } else {
1381 m_errorFlag = true;
1382 m_responseEntityBody = QByteArray();
1383 }
1384
1385 m_state = Done;
1386 dispatchCallbackSafely();
1387}
1388
1389#define XMLHTTPREQUEST_MAXIMUM_REDIRECT_RECURSION 15
1390void QQmlXMLHttpRequest::finished()
1391{
1392 m_redirectCount++;
1393 if (m_redirectCount < XMLHTTPREQUEST_MAXIMUM_REDIRECT_RECURSION) {
1394 QVariant redirect = m_network->attribute(QNetworkRequest::RedirectionTargetAttribute);
1395 if (redirect.isValid()) {
1396 QUrl url = m_network->url().resolved(redirect.toUrl());
1397 if (!QQmlFile::isLocalFile(url)) {
1398 // See http://www.ietf.org/rfc/rfc2616.txt, section 10.3.4 "303 See Other":
1399 // Result of 303 redirection should be a new "GET" request.
1400 const QVariant code = m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute);
1401 if (code.isValid() && code.toInt() == 303 && m_method != QLatin1String("GET"))
1402 m_method = QStringLiteral("GET");
1403 destroyNetwork();
1404
1405 // Discard redirect response body
1406 m_responseEntityBody = QByteArray();
1407
1408 requestFromUrl(url);
1409 return;
1410 }
1411 }
1412 }
1413
1414 m_status =
1415 m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
1416 m_statusText =
1417 QString::fromUtf8(m_network->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray());
1418
1419 if (m_state < HeadersReceived) {
1420 m_state = HeadersReceived;
1421 fillHeadersList ();
1422 dispatchCallbackSafely();
1423 }
1424 m_responseEntityBody.append(m_network->readAll());
1425 readEncoding();
1426
1427 if (xhrDump()) {
1428 qWarning().nospace() << "XMLHttpRequest: RESPONSE " << qPrintable(m_url.toString());
1429 if (!m_responseEntityBody.isEmpty()) {
1430 qWarning().nospace() << " "
1431 << qPrintable(QString::fromUtf8(m_responseEntityBody));
1432 }
1433 }
1434
1435 m_data.clear();
1436 destroyNetwork();
1437 if (m_state < Loading) {
1438 m_state = Loading;
1439 dispatchCallbackSafely();
1440 }
1441 m_state = Done;
1442
1443 dispatchCallbackSafely();
1444
1445 m_thisObject.clear();
1446 m_qmlContext.reset();
1447}
1448
1449
1450void QQmlXMLHttpRequest::readEncoding()
1451{
1452 for (const HeaderPair &header : std::as_const(m_headersList)) {
1453 if (header.first == "content-type") {
1454 int separatorIdx = header.second.indexOf(';');
1455 if (separatorIdx == -1) {
1456 m_mime = header.second;
1457 } else {
1458 m_mime = header.second.mid(0, separatorIdx);
1459 int charsetIdx = header.second.indexOf("charset=");
1460 if (charsetIdx != -1) {
1461 charsetIdx += 8;
1462 separatorIdx = header.second.indexOf(';', charsetIdx);
1463 m_charset = header.second.mid(charsetIdx, separatorIdx >= 0 ? separatorIdx : header.second.size());
1464 }
1465 }
1466 break;
1467 }
1468 }
1469
1470 const auto mime = mimeType();
1471 if (mime.isEmpty() || mime == "text/xml" || mime == "application/xml" || mime.endsWith("+xml"))
1472 m_gotXml = true;
1473}
1474
1476{
1477 return m_gotXml;
1478}
1479
1481{
1482 return m_url;
1483}
1484
1486{
1487 // Final MIME type is the override MIME type unless that is null in which
1488 // case it is the response MIME type.
1489 return m_overrideMime.isEmpty() ? m_mime : m_overrideMime;
1490}
1491
1493{
1494 // Final charset is the override charset unless that is null in which case
1495 // it is the response charset.
1496 return m_overrideCharset.isEmpty() ? m_charset : m_overrideCharset;
1497}
1498
1500{
1501 return m_responseType;
1502}
1503
1504void QQmlXMLHttpRequest::setResponseType(const QString &responseType)
1505{
1506 m_responseType = responseType;
1507}
1508
1510{
1511 if (m_parsedDocument.isEmpty()) {
1512 Scope scope(engine);
1513
1514 QJsonParseError error;
1515 const QString& jtext = responseBody();
1516 JsonParser parser(scope.engine, jtext.constData(), jtext.size());
1517 ScopedValue jsonObject(scope, parser.parse(&error));
1518 if (error.error != QJsonParseError::NoError)
1519 return engine->throwSyntaxError(QStringLiteral("JSON.parse: Parse error"));
1520
1521 m_parsedDocument.set(scope.engine, jsonObject);
1522 }
1523
1524 return m_parsedDocument.value();
1525}
1526
1528{
1529 if (m_parsedDocument.isEmpty()) {
1530 m_parsedDocument.set(engine, Document::load(engine, rawResponseBody()));
1531 }
1532
1533 return m_parsedDocument.value();
1534}
1535
1536QStringDecoder QQmlXMLHttpRequest::findTextDecoder() const
1537{
1538 QStringDecoder decoder;
1539
1540 if (!charset().isEmpty())
1541 decoder = QStringDecoder(charset());
1542
1543 if (!decoder.isValid() && m_gotXml) {
1544 QXmlStreamReader reader(m_responseEntityBody);
1545 reader.readNext();
1546 decoder = QStringDecoder(reader.documentEncoding().toString().toUtf8());
1547 }
1548
1549 if (!decoder.isValid() && mimeType() == "text/html")
1550 decoder = QStringDecoder::decoderForHtml(m_responseEntityBody);
1551
1552 if (!decoder.isValid()) {
1553 auto encoding = QStringConverter::encodingForData(m_responseEntityBody);
1554 if (encoding)
1555 decoder = QStringDecoder(*encoding);
1556 }
1557
1558 if (!decoder.isValid())
1559 decoder = QStringDecoder(QStringDecoder::Utf8);
1560
1561 return decoder;
1562}
1563
1565{
1566 QStringDecoder toUtf16 = findTextDecoder();
1567 return toUtf16(m_responseEntityBody);
1568}
1569
1571{
1572 return m_responseEntityBody;
1573}
1574
1575void QQmlXMLHttpRequest::dispatchCallbackNow(Object *thisObj)
1576{
1577 dispatchCallbackNow(thisObj, m_state == Done, m_errorFlag);
1578}
1579
1580void QQmlXMLHttpRequest::dispatchCallbackNow(Object *thisObj, bool done, bool error)
1581{
1582 Q_ASSERT(thisObj);
1583
1584 const auto dispatch = [thisObj](const QString &eventName) {
1585 QV4::Scope scope(thisObj->engine());
1586 ScopedString s(scope, scope.engine->newString(eventName));
1587 ScopedFunctionObject callback(scope, thisObj->get(s));
1588 // not an error, but no event handler to call.
1589 if (!callback)
1590 return;
1591
1592 QV4::JSCallArguments jsCallData(scope);
1593 callback->call(jsCallData);
1594
1595 if (scope.hasException()) {
1596 QQmlError error = scope.engine->catchExceptionAsQmlError();
1597 QQmlEnginePrivate *qmlEnginePrivate = scope.engine->qmlEngine() ? QQmlEnginePrivate::get(scope.engine->qmlEngine()) : nullptr;
1598 QQmlEnginePrivate::warning(qmlEnginePrivate, error);
1599 }
1600 };
1601
1602 dispatch(QStringLiteral("onreadystatechange"));
1603 if (done) {
1604 if (error)
1605 dispatch(QStringLiteral("onerror"));
1606 else
1607 dispatch(QStringLiteral("onload"));
1608 dispatch(QStringLiteral("onloadend"));
1609 }
1610}
1611
1612void QQmlXMLHttpRequest::dispatchCallbackSafely()
1613{
1614 if (m_wasConstructedWithQmlContext && m_qmlContext.isNull()) {
1615 // if the calling context object is no longer valid, then it has been
1616 // deleted explicitly (e.g., by a Loader deleting the itemContext when
1617 // the source is changed). We do nothing in this case, as the evaluation
1618 // cannot succeed.
1619 return;
1620 }
1621
1622 dispatchCallbackNow(m_thisObject.as<Object>());
1623}
1624
1625void QQmlXMLHttpRequest::destroyNetwork()
1626{
1627 if (m_network) {
1628 m_network->disconnect();
1629 m_network->deleteLater();
1630 m_network = nullptr;
1631 }
1632}
1633
1634namespace QV4 {
1635namespace Heap {
1636
1638 void init(QQmlXMLHttpRequest *request) {
1639 Object::init();
1640 this->request = request;
1641 }
1642
1643 void destroy() {
1644 delete request;
1645 Object::destroy();
1646 }
1648};
1649
1650#define QQmlXMLHttpRequestCtorMembers(class, Member)
1651 Member(class, Pointer, Object *, proto)
1652
1654 DECLARE_MARKOBJECTS(QQmlXMLHttpRequestCtor)
1655 void init(ExecutionEngine *engine);
1656};
1657
1658}
1659
1661{
1662 V4_OBJECT2(QQmlXMLHttpRequestWrapper, Object)
1664};
1665
1666// https://xhr.spec.whatwg.org/
1668{
1669 V4_OBJECT2(QQmlXMLHttpRequestCtor, FunctionObject)
1670
1683
1684 static ReturnedValue virtualCall(const FunctionObject *, const Value *, const Value *, int) {
1685 return Encode::undefined();
1686 }
1687
1689
1690 static ReturnedValue method_open(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1691 static ReturnedValue method_setRequestHeader(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1692 static ReturnedValue method_send(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1693 static ReturnedValue method_abort(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1694 static ReturnedValue method_getResponseHeader(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1695 static ReturnedValue method_getAllResponseHeaders(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1696 static ReturnedValue method_overrideMimeType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1697
1698 static ReturnedValue method_get_readyState(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1699 static ReturnedValue method_get_status(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1700 static ReturnedValue method_get_statusText(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1701 static ReturnedValue method_get_responseText(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1702 static ReturnedValue method_get_responseXML(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1703 static ReturnedValue method_get_response(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1704 static ReturnedValue method_get_responseType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1705 static ReturnedValue method_set_responseType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1706 static ReturnedValue method_get_responseURL(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
1707};
1708
1709}
1710
1712
1713void Heap::QQmlXMLHttpRequestCtor::init(ExecutionEngine *engine)
1714{
1715 Heap::FunctionObject::init(engine, QStringLiteral("XMLHttpRequest"));
1716 Scope scope(engine);
1717 Scoped<QV4::QQmlXMLHttpRequestCtor> ctor(scope, this);
1718
1719 ctor->defineReadonlyProperty(QStringLiteral("UNSENT"), Value::fromInt32(QQmlXMLHttpRequest::Unsent));
1720 ctor->defineReadonlyProperty(QStringLiteral("OPENED"), Value::fromInt32(QQmlXMLHttpRequest::Opened));
1721 ctor->defineReadonlyProperty(QStringLiteral("HEADERS_RECEIVED"), Value::fromInt32(QQmlXMLHttpRequest::HeadersReceived));
1722 ctor->defineReadonlyProperty(QStringLiteral("LOADING"), Value::fromInt32(QQmlXMLHttpRequest::Loading));
1723 ctor->defineReadonlyProperty(QStringLiteral("DONE"), Value::fromInt32(QQmlXMLHttpRequest::Done));
1724
1725 if (!ctor->d()->proto)
1726 ctor->setupProto();
1727 ScopedString s(scope, engine->id_prototype());
1728 ctor->defineDefaultProperty(s, ScopedObject(scope, ctor->d()->proto));
1729}
1730
1732
1733void QQmlXMLHttpRequestCtor::setupProto()
1734{
1735 ExecutionEngine *v4 = engine();
1736 Scope scope(v4);
1737 ScopedObject p(scope, v4->newObject());
1738 d()->proto.set(scope.engine, p->d());
1739
1740 // Methods
1741 p->defineDefaultProperty(QStringLiteral("open"), method_open);
1742 p->defineDefaultProperty(QStringLiteral("setRequestHeader"), method_setRequestHeader);
1743 p->defineDefaultProperty(QStringLiteral("send"), method_send);
1744 p->defineDefaultProperty(QStringLiteral("abort"), method_abort);
1745 p->defineDefaultProperty(QStringLiteral("getResponseHeader"), method_getResponseHeader);
1746 p->defineDefaultProperty(QStringLiteral("getAllResponseHeaders"), method_getAllResponseHeaders);
1747 p->defineDefaultProperty(QStringLiteral("overrideMimeType"), method_overrideMimeType);
1748
1749 // Read-only properties
1750 p->defineAccessorProperty(QStringLiteral("readyState"), method_get_readyState, nullptr);
1751 p->defineAccessorProperty(QStringLiteral("status"),method_get_status, nullptr);
1752 p->defineAccessorProperty(QStringLiteral("statusText"),method_get_statusText, nullptr);
1753 p->defineAccessorProperty(QStringLiteral("responseText"),method_get_responseText, nullptr);
1754 p->defineAccessorProperty(QStringLiteral("responseXML"),method_get_responseXML, nullptr);
1755 p->defineAccessorProperty(QStringLiteral("response"),method_get_response, nullptr);
1756 p->defineAccessorProperty(QStringLiteral("responseURL"),method_get_responseURL, nullptr);
1757
1758 // Read-write properties
1759 p->defineAccessorProperty(QStringLiteral("responseType"), method_get_responseType, method_set_responseType);
1760
1761 // State values
1762 p->defineReadonlyProperty(QStringLiteral("UNSENT"), Value::fromInt32(0));
1763 p->defineReadonlyProperty(QStringLiteral("OPENED"), Value::fromInt32(1));
1764 p->defineReadonlyProperty(QStringLiteral("HEADERS_RECEIVED"), Value::fromInt32(2));
1765 p->defineReadonlyProperty(QStringLiteral("LOADING"), Value::fromInt32(3));
1766 p->defineReadonlyProperty(QStringLiteral("DONE"), Value::fromInt32(4));
1767}
1768
1769
1770// XMLHttpRequest methods
1771ReturnedValue QQmlXMLHttpRequestCtor::method_open(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1772{
1773 Scope scope(b);
1774 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
1775 if (!w)
1776 V4THROW_REFERENCE("Not an XMLHttpRequest object");
1777 QQmlXMLHttpRequest *r = w->d()->request;
1778
1779 if (argc < 2 || argc > 5)
1780 THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count");
1781
1782 // Argument 0 - Method
1783 QString method = argv[0].toQStringNoThrow().toUpper();
1784 if (method != QLatin1String("GET") &&
1785 method != QLatin1String("PUT") &&
1786 method != QLatin1String("HEAD") &&
1787 method != QLatin1String("POST") &&
1788 method != QLatin1String("DELETE") &&
1789 method != QLatin1String("OPTIONS") &&
1790 method != QLatin1String("PROPFIND") &&
1791 method != QLatin1String("PATCH"))
1792 THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Unsupported HTTP method type");
1793
1794 // Argument 1 - URL
1795 QUrl url = QUrl(argv[1].toQStringNoThrow());
1796
1797 if (url.isRelative()) {
1798 if (QQmlRefPointer<QQmlContextData> qmlContextData = scope.engine->callingQmlContext())
1799 url = qmlContextData->resolvedUrl(url);
1800 else
1801 url = scope.engine->resolvedUrl(url.url());
1802 }
1803
1804 bool async = true;
1805 // Argument 2 - async (optional)
1806 if (argc > 2) {
1807 async = argv[2].booleanValue();
1808 }
1809
1810 // Argument 3/4 - user/pass (optional)
1811 QString username, password;
1812 if (argc > 3)
1813 username = argv[3].toQStringNoThrow();
1814 if (argc > 4)
1815 password = argv[4].toQStringNoThrow();
1816
1817 // Clear the fragment (if any)
1818 url.setFragment(QString());
1819
1820 // Set username/password
1821 if (!username.isNull()) url.setUserName(username);
1822 if (!password.isNull()) url.setPassword(password);
1823
1824 return r->open(w, method, url, async ? QQmlXMLHttpRequest::AsynchronousLoad : QQmlXMLHttpRequest::SynchronousLoad);
1825}
1826
1827ReturnedValue QQmlXMLHttpRequestCtor::method_setRequestHeader(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1828{
1829 Scope scope(b);
1830 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
1831 if (!w)
1832 V4THROW_REFERENCE("Not an XMLHttpRequest object");
1833 QQmlXMLHttpRequest *r = w->d()->request;
1834
1835 if (argc != 2)
1836 THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count");
1837
1838 if (r->readyState() != QQmlXMLHttpRequest::Opened || r->sendFlag())
1839 THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
1840
1841 QString name = argv[0].toQStringNoThrow();
1842 QString value = argv[1].toQStringNoThrow();
1843
1844 // ### Check that name and value are well formed
1845
1846 QString nameUpper = name.toUpper();
1847 if (nameUpper == QLatin1String("ACCEPT-CHARSET") ||
1848 nameUpper == QLatin1String("ACCEPT-ENCODING") ||
1849 nameUpper == QLatin1String("CONNECTION") ||
1850 nameUpper == QLatin1String("CONTENT-LENGTH") ||
1851 nameUpper == QLatin1String("COOKIE") ||
1852 nameUpper == QLatin1String("COOKIE2") ||
1853 nameUpper == QLatin1String("CONTENT-TRANSFER-ENCODING") ||
1854 nameUpper == QLatin1String("DATE") ||
1855 nameUpper == QLatin1String("EXPECT") ||
1856 nameUpper == QLatin1String("HOST") ||
1857 nameUpper == QLatin1String("KEEP-ALIVE") ||
1858 nameUpper == QLatin1String("REFERER") ||
1859 nameUpper == QLatin1String("TE") ||
1860 nameUpper == QLatin1String("TRAILER") ||
1861 nameUpper == QLatin1String("TRANSFER-ENCODING") ||
1862 nameUpper == QLatin1String("UPGRADE") ||
1863 nameUpper == QLatin1String("VIA") ||
1864 nameUpper.startsWith(QLatin1String("PROXY-")) ||
1865 nameUpper.startsWith(QLatin1String("SEC-")))
1866 RETURN_UNDEFINED();
1867
1868 r->addHeader(name, value);
1869
1870 RETURN_UNDEFINED();
1871}
1872
1873ReturnedValue QQmlXMLHttpRequestCtor::method_send(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1874{
1875 Scope scope(b);
1876 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
1877 if (!w)
1878 V4THROW_REFERENCE("Not an XMLHttpRequest object");
1879 QQmlXMLHttpRequest *r = w->d()->request;
1880
1881 if (r->readyState() != QQmlXMLHttpRequest::Opened ||
1882 r->sendFlag())
1883 THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
1884
1885 QByteArray data;
1886 if (argc > 0) {
1887 if (const ArrayBuffer *buffer = argv[0].as<ArrayBuffer>()) {
1888 data = buffer->asByteArray();
1889 } else {
1890 data = argv[0].toQStringNoThrow().toUtf8();
1891 }
1892 }
1893
1894 return r->send(w, scope.engine->callingQmlContext(), data);
1895}
1896
1897ReturnedValue QQmlXMLHttpRequestCtor::method_abort(const FunctionObject *b, const Value *thisObject, const Value *, int)
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 return r->abort(w);
1906}
1907
1908ReturnedValue QQmlXMLHttpRequestCtor::method_getResponseHeader(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1909{
1910 Scope scope(b);
1911 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
1912 if (!w)
1913 V4THROW_REFERENCE("Not an XMLHttpRequest object");
1914 QQmlXMLHttpRequest *r = w->d()->request;
1915
1916 if (argc != 1)
1917 THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count");
1918
1919 if (r->readyState() != QQmlXMLHttpRequest::Loading &&
1920 r->readyState() != QQmlXMLHttpRequest::Done &&
1921 r->readyState() != QQmlXMLHttpRequest::HeadersReceived)
1922 THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
1923
1924 return Encode(scope.engine->newString(r->header(argv[0].toQStringNoThrow())));
1925}
1926
1927ReturnedValue QQmlXMLHttpRequestCtor::method_getAllResponseHeaders(const FunctionObject *b, const Value *thisObject, const Value *, int argc)
1928{
1929 Scope scope(b);
1930 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
1931 if (!w)
1932 V4THROW_REFERENCE("Not an XMLHttpRequest object");
1933 QQmlXMLHttpRequest *r = w->d()->request;
1934
1935 if (argc != 0)
1936 THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count");
1937
1938 if (r->readyState() != QQmlXMLHttpRequest::Loading &&
1939 r->readyState() != QQmlXMLHttpRequest::Done &&
1940 r->readyState() != QQmlXMLHttpRequest::HeadersReceived)
1941 THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
1942
1943 return Encode(scope.engine->newString(r->headers()));
1944}
1945
1946// XMLHttpRequest properties
1947ReturnedValue QQmlXMLHttpRequestCtor::method_get_readyState(const FunctionObject *b, const Value *thisObject, const Value *, int)
1948{
1949 Scope scope(b);
1950 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
1951 if (!w)
1952 V4THROW_REFERENCE("Not an XMLHttpRequest object");
1953 QQmlXMLHttpRequest *r = w->d()->request;
1954
1955 return Encode(r->readyState());
1956}
1957
1958ReturnedValue QQmlXMLHttpRequestCtor::method_get_status(const FunctionObject *b, const Value *thisObject, const Value *, int)
1959{
1960 Scope scope(b);
1961 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
1962 if (!w)
1963 V4THROW_REFERENCE("Not an XMLHttpRequest object");
1964 QQmlXMLHttpRequest *r = w->d()->request;
1965
1966 if (r->readyState() == QQmlXMLHttpRequest::Unsent ||
1967 r->readyState() == QQmlXMLHttpRequest::Opened)
1968 THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
1969
1970 if (r->errorFlag())
1971 return Encode(0);
1972 else
1973 return Encode(r->replyStatus());
1974}
1975
1976ReturnedValue QQmlXMLHttpRequestCtor::method_get_statusText(const FunctionObject *b, const Value *thisObject, const Value *, int)
1977{
1978 Scope scope(b);
1979 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
1980 if (!w)
1981 V4THROW_REFERENCE("Not an XMLHttpRequest object");
1982 QQmlXMLHttpRequest *r = w->d()->request;
1983
1984 if (r->readyState() == QQmlXMLHttpRequest::Unsent ||
1985 r->readyState() == QQmlXMLHttpRequest::Opened)
1986 THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
1987
1988 if (r->errorFlag())
1989 return Encode(scope.engine->newString(QString()));
1990 else
1991 return Encode(scope.engine->newString(r->replyStatusText()));
1992}
1993
1994ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseText(const FunctionObject *b, const Value *thisObject, const Value *, int)
1995{
1996 Scope scope(b);
1997 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
1998 if (!w)
1999 V4THROW_REFERENCE("Not an XMLHttpRequest object");
2000 QQmlXMLHttpRequest *r = w->d()->request;
2001
2002 if (r->readyState() != QQmlXMLHttpRequest::Loading &&
2003 r->readyState() != QQmlXMLHttpRequest::Done)
2004 return Encode(scope.engine->newString(QString()));
2005 else
2006 return Encode(scope.engine->newString(r->responseBody()));
2007}
2008
2009ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseXML(const FunctionObject *b, const Value *thisObject, const Value *, int)
2010{
2011 Scope scope(b);
2012 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
2013 if (!w)
2014 V4THROW_REFERENCE("Not an XMLHttpRequest object");
2015 QQmlXMLHttpRequest *r = w->d()->request;
2016
2017 if (!r->receivedXml() ||
2018 (r->readyState() != QQmlXMLHttpRequest::Loading &&
2019 r->readyState() != QQmlXMLHttpRequest::Done)) {
2020 return Encode::null();
2021 } else {
2022 if (r->responseType().isEmpty())
2023 r->setResponseType(QLatin1String("document"));
2024 return r->xmlResponseBody(scope.engine);
2025 }
2026}
2027
2028ReturnedValue QQmlXMLHttpRequestCtor::method_get_response(const FunctionObject *b, const Value *thisObject, const Value *, int)
2029{
2030 Scope scope(b);
2031 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
2032 if (!w)
2033 V4THROW_REFERENCE("Not an XMLHttpRequest object");
2034 QQmlXMLHttpRequest *r = w->d()->request;
2035
2036 if (r->readyState() != QQmlXMLHttpRequest::Loading &&
2037 r->readyState() != QQmlXMLHttpRequest::Done)
2038 RETURN_RESULT(scope.engine->newString(QString()));
2039
2040 const QString& responseType = r->responseType();
2041 if (responseType.compare(QLatin1String("text"), Qt::CaseInsensitive) == 0 || responseType.isEmpty()) {
2042 RETURN_RESULT(scope.engine->newString(r->responseBody()));
2043 } else if (responseType.compare(QLatin1String("arraybuffer"), Qt::CaseInsensitive) == 0) {
2044 RETURN_RESULT(scope.engine->newArrayBuffer(r->rawResponseBody()));
2045 } else if (responseType.compare(QLatin1String("json"), Qt::CaseInsensitive) == 0) {
2046 RETURN_RESULT(r->jsonResponseBody(scope.engine));
2047 } else if (responseType.compare(QLatin1String("document"), Qt::CaseInsensitive) == 0) {
2048 RETURN_RESULT(r->xmlResponseBody(scope.engine));
2049 } else {
2050 RETURN_RESULT(scope.engine->newString(QString()));
2051 }
2052}
2053
2054
2055ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseType(const FunctionObject *b, const Value *thisObject, const Value *, int)
2056{
2057 Scope scope(b);
2058 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
2059 if (!w)
2060 V4THROW_REFERENCE("Not an XMLHttpRequest object");
2061 QQmlXMLHttpRequest *r = w->d()->request;
2062 return Encode(scope.engine->newString(r->responseType()));
2063}
2064
2065ReturnedValue QQmlXMLHttpRequestCtor::method_set_responseType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
2066{
2067 Scope scope(b);
2068 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
2069 if (!w)
2070 V4THROW_REFERENCE("Not an XMLHttpRequest object");
2071 QQmlXMLHttpRequest *r = w->d()->request;
2072
2073 if (argc < 1)
2074 THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count");
2075
2076 // Argument 0 - response type
2077 r->setResponseType(argv[0].toQStringNoThrow());
2078
2079 return Encode::undefined();
2080}
2081
2082ReturnedValue QQmlXMLHttpRequestCtor::method_get_responseURL(const FunctionObject *b, const Value *thisObject, const Value *, int)
2083{
2084 Scope scope(b);
2085 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
2086 if (!w)
2087 V4THROW_REFERENCE("Not an XMLHttpRequest object");
2088 QQmlXMLHttpRequest *r = w->d()->request;
2089
2090 if (r->readyState() != QQmlXMLHttpRequest::Loading &&
2091 r->readyState() != QQmlXMLHttpRequest::Done) {
2092 return Encode(scope.engine->newString(QString()));
2093 } else {
2094 QUrl url = r->url();
2095 url.setFragment(QString());
2096 return Encode(scope.engine->newString(url.toString()));
2097 }
2098}
2099
2100ReturnedValue QQmlXMLHttpRequestCtor::method_overrideMimeType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
2101{
2102 Scope scope(b);
2103 Scoped<QQmlXMLHttpRequestWrapper> w(scope, thisObject->as<QQmlXMLHttpRequestWrapper>());
2104 if (!w)
2105 V4THROW_REFERENCE("Not an XMLHttpRequest object");
2106 QQmlXMLHttpRequest *r = w->d()->request;
2107
2108 if (argc != 1)
2109 THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "Incorrect argument count");
2110
2111 // If state is loading or done, throw an InvalidStateError exception.
2112 if (r->readyState() == QQmlXMLHttpRequest::Loading ||
2113 r->readyState() == QQmlXMLHttpRequest::Done)
2114 THROW_DOM(DOMEXCEPTION_INVALID_STATE_ERR, "Invalid state");
2115
2116 // Set override MIME type to `application/octet-stream`.
2117 r->setOverrideMimeType(QStringLiteral("application/octet-stream"));
2118 const auto parts = argv[0].toQStringNoThrow().split(QLatin1Char(';'));
2119 const auto type = parts.at(0).trimmed();
2120
2121 const auto mimeInvalidCharacter = [](QChar uni) {
2122 if (uni.unicode() > 127) // Only accept ASCII
2123 return true;
2124 const char ch = char(uni.unicode());
2125 return !(ch == '-' || ch == '/' || isAsciiLetterOrNumber(ch));
2126 };
2127
2128 // If mime is a parsable MIME type, ...
2129 if (type.count(QLatin1Char('/')) == 1
2130 && std::find_if(type.begin(), type.end(), mimeInvalidCharacter) == type.end()) {
2131 // ... then set override MIME type to its MIME type portion.
2132 r->setOverrideMimeType(type);
2133 }
2134 for (const auto &part : parts) {
2135 const QLatin1String charset("charset=");
2136 // If override MIME type has a `charset` parameter, ...
2137 if (part.trimmed().startsWith(charset)) {
2138 // ... then set override charset to its value.
2139 const int offset(part.indexOf(charset) + charset.size());
2140 r->setOverrideCharset(part.sliced(offset).trimmed());
2141 }
2142 }
2143
2144 return Encode::undefined();
2145}
2146
2147void qt_rem_qmlxmlhttprequest(ExecutionEngine * /* engine */, void *d)
2148{
2150 delete data;
2151}
2152
2153void *qt_add_qmlxmlhttprequest(ExecutionEngine *v4)
2154{
2155 Scope scope(v4);
2156
2157 Scoped<QQmlXMLHttpRequestCtor> ctor(scope, v4->memoryManager->allocate<QQmlXMLHttpRequestCtor>(v4));
2158 ScopedString s(scope, v4->newString(QStringLiteral("XMLHttpRequest")));
2159 v4->globalObject->defineReadonlyProperty(s, ctor);
2160
2162 return data;
2163}
2164
2165QT_END_NAMESPACE
2166
2167#include <qqmlxmlhttprequest.moc>
friend class QQmlEnginePrivate
void setOverrideCharset(QStringView charset)
void setResponseType(const QString &)
void setOverrideMimeType(QStringView mimeType)
ReturnedValue abort(Object *thisObject)
QQmlXMLHttpRequest(QNetworkAccessManager *manager, QV4::ExecutionEngine *v4)
QV4::ReturnedValue jsonResponseBody(QV4::ExecutionEngine *)
const QByteArray charset() const
const QByteArray & rawResponseBody() const
void addHeader(const QString &, const QString &)
QString header(const QString &name) const
QString replyStatusText() const
const QString & responseType() const
const QByteArray mimeType() const
QV4::ReturnedValue xmlResponseBody(QV4::ExecutionEngine *)
ReturnedValue send(Object *thisObject, const QQmlRefPointer< QQmlContextData > &context, const QByteArray &)
ReturnedValue open(Object *thisObject, const QString &, const QUrl &, LoadType)
static ReturnedValue prototype(ExecutionEngine *)
static ReturnedValue method_name(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_value(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_ownerElement(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue prototype(ExecutionEngine *v4)
static ReturnedValue prototype(ExecutionEngine *v4)
static ReturnedValue method_length(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_documentElement(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_xmlStandalone(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue load(ExecutionEngine *engine, const QByteArray &data)
static ReturnedValue prototype(ExecutionEngine *)
static ReturnedValue method_xmlVersion(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_xmlEncoding(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue prototype(ExecutionEngine *)
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
QList< NodeImpl * > attributes
QList< NodeImpl * > children
DocumentImpl * document
static ReturnedValue create(ExecutionEngine *, NodeImpl *)
static ReturnedValue method_get_childNodes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_nodeValue(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_previousSibling(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_namespaceUri(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue getProto(ExecutionEngine *v4)
static ReturnedValue method_get_parentNode(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_nextSibling(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_nodeType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_lastChild(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_firstChild(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_attributes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_nodeName(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_wholeText(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue prototype(ExecutionEngine *)
static ReturnedValue method_isElementContentWhitespace(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
DECLARE_HEAP_OBJECT(QmlContext, ExecutionContext)
Definition qjsvalue.h: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 method_set_responseType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_setRequestHeader(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_readyState(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_responseText(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_open(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_overrideMimeType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_getResponseHeader(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_responseType(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_responseXML(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_abort(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_statusText(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_get_status(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
static ReturnedValue method_send(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)