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