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
qv4urlobject.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
7
8#include <QtCore/QUrl>
9
10#include <qv4jscall_p.h>
11#include <qv4objectiterator_p.h>
12
13using namespace QV4;
14
17
20
21
22void Heap::UrlCtor::init(QV4::ExecutionEngine *engine)
23{
24 Heap::FunctionObject::init(engine, QLatin1String("URL"));
25}
26
27void UrlPrototype::init(ExecutionEngine *engine, Object *ctor)
28{
29 Q_UNUSED(ctor);
30
31 Scope scope(engine);
32 ScopedObject o(scope);
33 ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
34
35 defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
36 defineDefaultProperty(engine->id_toString(), method_getHref);
37 defineDefaultProperty(QLatin1String("toJSON"), method_getHref);
38
39 defineAccessorProperty(QLatin1String("hash"), method_getHash, method_setHash);
40 defineAccessorProperty(QLatin1String("host"), method_getHost, method_setHost);
41 defineAccessorProperty(QLatin1String("hostname"), method_getHostname, method_setHostname);
42 defineAccessorProperty(QLatin1String("href"), method_getHref, method_setHref);
43 defineAccessorProperty(QLatin1String("origin"), method_getOrigin, nullptr);
44 defineAccessorProperty(QLatin1String("password"), method_getPassword, method_setPassword);
45 defineAccessorProperty(QLatin1String("pathname"), method_getPathname, method_setPathname);
46 defineAccessorProperty(QLatin1String("port"), method_getPort, method_setPort);
47 defineAccessorProperty(QLatin1String("protocol"), method_getProtocol, method_setProtocol);
48 defineAccessorProperty(QLatin1String("search"), method_getSearch, method_setSearch);
49 defineAccessorProperty(QLatin1String("searchParams"), method_getSearchParams, nullptr);
50 defineAccessorProperty(QLatin1String("username"), method_getUsername, method_setUsername);
51}
52
53bool UrlObject::setHash(const QString &hash)
54{
55 return updateUrl([&](QUrl *url) {
56 url->setFragment(hash.startsWith(QLatin1Char('#')) ? hash.mid(1) : hash);
57 });
58}
59
60bool UrlObject::setHostname(const QString &hostName)
61{
62 return updateUrl([&](QUrl *url) {
63 url->setHost(hostName);
64 });
65}
66
67bool UrlObject::setHost(const QString &host)
68{
69 return updateUrl([&](QUrl *url) {
70 qsizetype colon = host.indexOf(QLatin1Char(':'));
71 if (colon == -1) {
72 url->setHost(host);
73 url->setPort(-1);
74 } else {
75 url->setHost(host.left(colon));
76 url->setPort(QStringView(host).mid(colon + 1).toInt());
77 }
78 });
79}
80
81bool UrlObject::setHref(const QString &href)
82{
83 QUrl replacement(href);
84 if (!replacement.isValid() || replacement.isRelative())
85 return false;
86
87 d()->setUrl(std::move(replacement));
88 return true;
89}
90
91void UrlObject::setUrl(const QUrl &url)
92{
93 d()->setUrl(QUrl(url));
94}
95
96bool UrlObject::setPassword(const QString &password)
97{
98 return updateUrl([&](QUrl *url) {
99 url->setPassword(password);
100 });
101}
102
103bool UrlObject::setPathname(const QString &pathname)
104{
105 return updateUrl([&](QUrl *url) {
106 url->setPath(pathname);
107 });
108}
109
110bool UrlObject::setPort(const QString &port)
111{
112 return updateUrl([&](QUrl *url) {
113 url->setPort(port.isEmpty() ? -1 : port.toInt());
114 });
115}
116
117bool UrlObject::setProtocol(const QString &protocolOrScheme)
118{
119 // If there is one or several ':' in the protocolOrScheme,
120 // everything from the first colon is removed.
121
122 return updateUrl([&](QUrl *url) {
123 const qsizetype colon = protocolOrScheme.indexOf(QLatin1Char(':'));
124 url->setScheme(colon == -1 ? protocolOrScheme : protocolOrScheme.left(colon));
125 });
126}
127
128bool UrlObject::setSearch(const QString &search)
129{
130 return updateUrl([&](QUrl *url) {
131 if (search.startsWith(QLatin1Char('?'))) {
132 url->setQuery(search.mid(1));
133 return;
134 }
135
136 if (search.isEmpty()) {
137 // In JS, setting an empty query removes the '?' as well. QUrl only does that for a
138 // null QString. The way to get a lone '?' in JS is to set the search property to "?".
139 // That is why this is in the else branch.
140 url->setQuery(QString()); // it's null now
141 return;
142 }
143
144 url->setQuery(search);
145 });
146}
147
148bool UrlObject::setUsername(const QString &username)
149{
150 return updateUrl([&](QUrl *url) {
151 url->setUserName(username);
152 });
153}
154
155QString UrlObject::search() const
156{
157 const QUrl url = d()->url();
158 if (!url.hasQuery() || url.query().isEmpty())
159 return QString();
160
161 constexpr auto options = QUrl::ComponentFormattingOption::EncodeSpaces
162 | QUrl::ComponentFormattingOption::EncodeUnicode
163 | QUrl::ComponentFormattingOption::EncodeReserved;
164 return QLatin1Char('?') + url.query(options);
165}
166
167static bool checkUrlObjectType(ExecutionEngine *v4, const Scoped<UrlObject> &r)
168{
169 if (r)
170 return true;
171
172 v4->throwTypeError(QStringLiteral("Value of \"this\" must be of type URL"));
173 return false;
174}
175
176ReturnedValue UrlPrototype::method_getHash(const FunctionObject *b, const Value *thisObject,
177 const Value *, int)
178{
179 ExecutionEngine *v4 = b->engine();
180 Scope scope(v4);
181
182 Scoped<UrlObject> r(scope, thisObject);
183 if (!checkUrlObjectType(v4, r))
184 return Encode::undefined();
185
186 return Encode(v4->newString(r->hash()));
187}
188
189ReturnedValue UrlPrototype::method_setHash(const FunctionObject *b, const Value *thisObject,
190 const Value *argv, int)
191{
192 ExecutionEngine *v4 = b->engine();
193 Scope scope(v4);
194
195 ScopedValue arg(scope, argv[0]);
196 String *stringValue = arg->stringValue();
197
198 if (stringValue == nullptr)
199 return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
200
201 Scoped<UrlObject> r(scope, thisObject);
202 if (!checkUrlObjectType(v4, r))
203 return Encode::undefined();
204
205 r->setHash(stringValue->toQString());
206
207 return Encode::undefined();
208}
209
210ReturnedValue UrlPrototype::method_getHost(const FunctionObject *b, const Value *thisObject,
211 const Value *, int)
212{
213 ExecutionEngine *v4 = b->engine();
214 Scope scope(v4);
215
216 Scoped<UrlObject> r(scope, thisObject);
217 if (!checkUrlObjectType(v4, r))
218 return Encode::undefined();
219
220 return Encode(v4->newString(r->host()));
221}
222
223ReturnedValue UrlPrototype::method_setHost(const FunctionObject *b, const Value *thisObject,
224 const Value *argv, int)
225{
226 ExecutionEngine *v4 = b->engine();
227 Scope scope(v4);
228
229 ScopedValue arg(scope, argv[0]);
230 String *stringValue = arg->stringValue();
231
232 if (stringValue == nullptr)
233 return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
234
235 Scoped<UrlObject> r(scope, thisObject);
236 if (!checkUrlObjectType(v4, r))
237 return Encode::undefined();
238
239 QString host = stringValue->toQString();
240 if (!r->setHost(host))
241 return v4->throwTypeError(QLatin1String("Invalid host: %1").arg(host));
242
243 return Encode::undefined();
244}
245
246ReturnedValue UrlPrototype::method_getHostname(const FunctionObject *b, const Value *thisObject,
247 const Value *, int)
248{
249 ExecutionEngine *v4 = b->engine();
250 Scope scope(v4);
251
252 Scoped<UrlObject> r(scope, thisObject);
253 if (!checkUrlObjectType(v4, r))
254 return Encode::undefined();
255
256 return Encode(v4->newString(r->hostname()));
257}
258
259ReturnedValue UrlPrototype::method_setHostname(const FunctionObject *b, const Value *thisObject,
260 const Value *argv, int)
261{
262 ExecutionEngine *v4 = b->engine();
263 Scope scope(v4);
264
265 ScopedValue arg(scope, argv[0]);
266 String *stringValue = arg->stringValue();
267
268 if (stringValue == nullptr)
269 return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
270
271 Scoped<UrlObject> r(scope, thisObject);
272 if (!checkUrlObjectType(v4, r))
273 return Encode::undefined();
274
275 QString hostname = stringValue->toQString();
276 if (!r->setHostname(hostname))
277 return v4->throwTypeError(QLatin1String("Invalid hostname: %1").arg(hostname));
278
279 return Encode::undefined();
280}
281
282ReturnedValue UrlPrototype::method_getHref(const FunctionObject *b, const Value *thisObject,
283 const Value *, int)
284{
285 ExecutionEngine *v4 = b->engine();
286 Scope scope(v4);
287
288 Scoped<UrlObject> r(scope, thisObject);
289 if (!checkUrlObjectType(v4, r))
290 return Encode::undefined();
291
292 return Encode(v4->newString(r->href()));
293}
294
295ReturnedValue UrlPrototype::method_setHref(const FunctionObject *b, const Value *thisObject,
296 const Value *argv, int)
297{
298 ExecutionEngine *v4 = b->engine();
299 Scope scope(v4);
300
301 ScopedValue arg(scope, argv[0]);
302 String *stringValue = arg->stringValue();
303
304 if (stringValue == nullptr)
305 return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
306
307 Scoped<UrlObject> r(scope, thisObject);
308 if (!checkUrlObjectType(v4, r))
309 return Encode::undefined();
310
311 QString href = stringValue->toQString();
312 if (!r->setHref(href))
313 return v4->throwTypeError(QLatin1String("Invalid URL: %1").arg(href));
314
315 return Encode::undefined();
316}
317
318ReturnedValue UrlPrototype::method_getOrigin(const FunctionObject *b, const Value *thisObject,
319 const Value *, int)
320{
321 ExecutionEngine *v4 = b->engine();
322 Scope scope(v4);
323
324 Scoped<UrlObject> r(scope, thisObject);
325 if (!checkUrlObjectType(v4, r))
326 return Encode::undefined();
327
328 return Encode(v4->newString(r->origin()));
329}
330
331ReturnedValue UrlPrototype::method_getPassword(const FunctionObject *b, const Value *thisObject,
332 const Value *, int)
333{
334 ExecutionEngine *v4 = b->engine();
335 Scope scope(v4);
336
337 Scoped<UrlObject> r(scope, thisObject);
338 if (!checkUrlObjectType(v4, r))
339 return Encode::undefined();
340
341 return Encode(v4->newString(r->password()));
342}
343
344ReturnedValue UrlPrototype::method_setPassword(const FunctionObject *b, const Value *thisObject,
345 const Value *argv, int)
346{
347 ExecutionEngine *v4 = b->engine();
348 Scope scope(v4);
349
350 ScopedValue arg(scope, argv[0]);
351 String *stringValue = arg->stringValue();
352
353 if (stringValue == nullptr)
354 return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
355
356 Scoped<UrlObject> r(scope, thisObject);
357 if (!checkUrlObjectType(v4, r))
358 return Encode::undefined();
359
360 r->setPassword(stringValue->toQString());
361
362 return Encode::undefined();
363}
364
365ReturnedValue UrlPrototype::method_getPathname(const FunctionObject *b, const Value *thisObject,
366 const Value *, int)
367{
368 ExecutionEngine *v4 = b->engine();
369 Scope scope(v4);
370
371 Scoped<UrlObject> r(scope, thisObject);
372 if (!checkUrlObjectType(v4, r))
373 return Encode::undefined();
374
375 return Encode(v4->newString(r->pathname()));
376}
377
378ReturnedValue UrlPrototype::method_setPathname(const FunctionObject *b, const Value *thisObject,
379 const Value *argv, int)
380{
381 ExecutionEngine *v4 = b->engine();
382 Scope scope(v4);
383
384 ScopedValue arg(scope, argv[0]);
385 String *stringValue = arg->stringValue();
386
387 if (stringValue == nullptr)
388 return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
389
390 Scoped<UrlObject> r(scope, thisObject);
391 if (!checkUrlObjectType(v4, r))
392 return Encode::undefined();
393
394 r->setPathname(stringValue->toQString());
395
396 return Encode::undefined();
397}
398
399ReturnedValue UrlPrototype::method_getPort(const FunctionObject *b, const Value *thisObject,
400 const Value *, int)
401{
402 ExecutionEngine *v4 = b->engine();
403 Scope scope(v4);
404
405 Scoped<UrlObject> r(scope, thisObject);
406 if (!checkUrlObjectType(v4, r))
407 return Encode::undefined();
408
409 return Encode(v4->newString(r->port()));
410}
411
412ReturnedValue UrlPrototype::method_setPort(const FunctionObject *b, const Value *thisObject,
413 const Value *argv, int)
414{
415 ExecutionEngine *v4 = b->engine();
416 Scope scope(v4);
417
418 ScopedValue arg(scope, argv[0]);
419 String *stringValue = arg->stringValue();
420
421 QString port;
422
423 if (stringValue != nullptr)
424 port = stringValue->toQString();
425 else if (arg->isInt32())
426 port = QString::number(arg->toInt32());
427 else
428 return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
429
430 Scoped<UrlObject> r(scope, thisObject);
431 if (!checkUrlObjectType(v4, r))
432 return Encode::undefined();
433
434 if (!r->setPort(port))
435 return v4->throwTypeError(QLatin1String("Invalid port: %1").arg(port));
436
437 return Encode::undefined();
438}
439
440ReturnedValue UrlPrototype::method_getProtocol(const FunctionObject *b, const Value *thisObject,
441 const Value *, int)
442{
443 ExecutionEngine *v4 = b->engine();
444 Scope scope(v4);
445
446 Scoped<UrlObject> r(scope, thisObject);
447 if (!checkUrlObjectType(v4, r))
448 return Encode::undefined();
449
450 return Encode(v4->newString(r->protocol()));
451}
452
453ReturnedValue UrlPrototype::method_setProtocol(const FunctionObject *b, const Value *thisObject,
454 const Value *argv, int)
455{
456 ExecutionEngine *v4 = b->engine();
457 Scope scope(v4);
458
459 ScopedValue arg(scope, argv[0]);
460 String *stringValue = arg->stringValue();
461
462 if (stringValue == nullptr)
463 return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
464
465 Scoped<UrlObject> r(scope, thisObject);
466 if (!checkUrlObjectType(v4, r))
467 return Encode::undefined();
468
469 r->setProtocol(stringValue->toQString());
470
471 return Encode::undefined();
472}
473
474ReturnedValue UrlPrototype::method_getSearch(const FunctionObject *b, const Value *thisObject,
475 const Value *, int)
476{
477 ExecutionEngine *v4 = b->engine();
478 Scope scope(v4);
479
480 Scoped<UrlObject> r(scope, thisObject);
481 if (!checkUrlObjectType(v4, r))
482 return Encode::undefined();
483
484 return Encode(v4->newString(r->search()));
485}
486
487ReturnedValue UrlPrototype::method_setSearch(const FunctionObject *b, const Value *thisObject,
488 const Value *argv, int)
489{
490 ExecutionEngine *v4 = b->engine();
491 Scope scope(v4);
492
493 ScopedValue arg(scope, argv[0]);
494 String *stringValue = arg->stringValue();
495
496 if (stringValue == nullptr)
497 return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
498
499 Scoped<UrlObject> r(scope, thisObject);
500 if (!checkUrlObjectType(v4, r))
501 return Encode::undefined();
502
503 r->setSearch(stringValue->toQString());
504
505 return Encode::undefined();
506}
507
508ReturnedValue UrlPrototype::method_getUsername(const FunctionObject *b, const Value *thisObject,
509 const Value *, int)
510{
511 ExecutionEngine *v4 = b->engine();
512 Scope scope(v4);
513
514 Scoped<UrlObject> r(scope, thisObject);
515 if (!checkUrlObjectType(v4, r))
516 return Encode::undefined();
517
518 return Encode(v4->newString(r->username()));
519}
520
521ReturnedValue UrlPrototype::method_setUsername(const FunctionObject *b, const Value *thisObject,
522 const Value *argv, int)
523{
524 ExecutionEngine *v4 = b->engine();
525 Scope scope(v4);
526
527 ScopedValue arg(scope, argv[0]);
528 String *stringValue = arg->stringValue();
529
530 if (stringValue == nullptr)
531 return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
532
533 Scoped<UrlObject> r(scope, thisObject);
534 if (!checkUrlObjectType(v4, r))
535 return Encode::undefined();
536
537 r->setUsername(stringValue->toQString());
538
539 return Encode::undefined();
540}
541
542ReturnedValue UrlPrototype::method_getSearchParams(const FunctionObject *b, const Value *thisObject,
543 const Value *, int)
544{
545 ExecutionEngine *v4 = b->engine();
546 Scope scope(v4);
547
548 Scoped<UrlObject> r(scope, thisObject);
549 if (!checkUrlObjectType(v4, r))
550 return Encode::undefined();
551
552 Scoped<UrlSearchParamsObject> usp(scope, v4->newUrlSearchParamsObject());
553
554 usp->setUrlObject(thisObject->as<UrlObject>());
555 usp->initializeParams(r->search());
556
557 return usp->asReturnedValue();
558}
559
560ReturnedValue UrlCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv,
561 int argc, const Value *newTarget)
562{
563 ExecutionEngine *v4 = that->engine();
564
565 if (argc < 1 || argc > 2)
566 return v4->throwError(QLatin1String("Invalid amount of arguments"));
567
568 Scope scope(v4);
569
570 ScopedValue arg1(scope, argv[0]);
571
572 QString arg1String = arg1->toQString();
573 QString urlString;
574
575 if (argc == 2) {
576 ScopedValue arg2(scope, argv[1]);
577 String *arg2StringValue = arg2->stringValue();
578
579 if (arg2StringValue == nullptr)
580 return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
581
582 QUrl url = QUrl(arg2StringValue->toQString());
583 QUrl relativeUrl = QUrl(arg1String);
584
585 QString baseUrlPath = url.path();
586 QString relativePath = relativeUrl.path();
587
588 // If the base URL contains a path the last section of it is discarded
589 int lastSlash = baseUrlPath.lastIndexOf(QLatin1Char('/'));
590 if (lastSlash != -1)
591 baseUrlPath.truncate(lastSlash);
592
593 if (!relativePath.startsWith(QLatin1Char('/')))
594 relativePath = relativePath.prepend(QLatin1Char('/'));
595
596 url.setPath(baseUrlPath + relativePath);
597 url.setFragment(relativeUrl.fragment());
598 url.setQuery(relativeUrl.query());
599
600 urlString = url.toString();
601 } else {
602 urlString = arg1String;
603 }
604
605 ReturnedValue o = Encode(v4->newUrlObject());
606
607 if (!newTarget)
608 return o;
609
610 ScopedObject obj(scope, o);
611 obj->setProtoFromNewTarget(newTarget);
612
613 UrlObject *urlObject = obj->as<UrlObject>();
614
615 if (!urlObject->setHref(urlString))
616 return v4->throwTypeError(QLatin1String("Invalid URL: %1").arg(urlString));
617
618 return obj->asReturnedValue();
619}
620
621
622void Heap::UrlSearchParamsCtor::init(QV4::ExecutionEngine *engine)
623{
624 Heap::FunctionObject::init(engine, QLatin1String("URLSearchParams"));
625}
626
627void UrlSearchParamsPrototype::init(ExecutionEngine *engine, Object *ctor)
628{
629 Q_UNUSED(ctor);
630
631 Scope scope(engine);
632 ScopedObject o(scope);
633 ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
634
635 defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
636 defineDefaultProperty(engine->id_toString(), method_toString);
637 defineDefaultProperty(QLatin1String("sort"), method_sort);
638 defineDefaultProperty(QLatin1String("append"), method_append);
639 defineDefaultProperty(QLatin1String("delete"), method_delete);
640 defineDefaultProperty(QLatin1String("has"), method_has);
641 defineDefaultProperty(QLatin1String("set"), method_set);
642 defineDefaultProperty(QLatin1String("get"), method_get);
643 defineDefaultProperty(QLatin1String("getAll"), method_getAll);
644 defineDefaultProperty(QLatin1String("forEach"), method_forEach);
645 defineDefaultProperty(QLatin1String("entries"), method_entries);
646 defineDefaultProperty(QLatin1String("keys"), method_keys);
647 defineDefaultProperty(QLatin1String("values"), method_values);
648}
649
650ReturnedValue UrlSearchParamsCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv,
651 int argc, const Value *newTarget)
652{
653 ExecutionEngine *v4 = that->engine();
654
655 if (argc > 1)
656 return v4->throwError(QLatin1String("Invalid amount of arguments"));
657
658 Scope scope(v4);
659
660 ScopedValue arg(scope, argv[0]);
661 ArrayObject *argArrayObject = arg->as<ArrayObject>();
662 Object *argObject = arg->as<Object>();
663
664 ReturnedValue o = Encode(v4->newUrlSearchParamsObject());
665
666 if (!newTarget)
667 return o;
668
669 ScopedObject obj(scope, o);
670 obj->setProtoFromNewTarget(newTarget);
671
672 UrlSearchParamsObject *urlSearchParamsObject = obj->as<UrlSearchParamsObject>();
673
674 if (argArrayObject != nullptr) {
675 ScopedArrayObject argArray(scope, argArrayObject);
676
677 uint len = argArray->getLength();
678
679 ScopedArrayObject pairArray(scope);
680 for (uint i = 0; i < len; i++) {
681 // safe to user fromReturnedValue: Normal array, which will ensure marking
682 QV4::Value pair = Value::fromReturnedValue(argArray->get(i));
683 auto *pairArrayObject = pair.as<ArrayObject>();
684
685 if (pairArrayObject == nullptr) {
686 return v4->throwTypeError(
687 QLatin1String("element %1 is not a pair").arg(QString::number(i)));
688 }
689
690
691 pairArray = pairArrayObject;
692
693
694 uint pairLen = pairArray->getLength();
695
696
697 if (pairLen != 2) {
698 return v4->throwTypeError(QLatin1String("pair %1 has %2 elements instead of 2")
699 .arg(QString::number(i))
700 .arg(QString::number(pairLen)));
701 }
702 }
703
704 urlSearchParamsObject->initializeParams(argArray);
705 } else if (argObject != nullptr) {
706 ScopedObject scopedObject(scope, argObject);
707 urlSearchParamsObject->initializeParams(scopedObject);
708 } else {
709 QString value = argc > 0 ? arg->toQString() : QLatin1String("");
710 urlSearchParamsObject->initializeParams(value);
711 }
712
713 return obj->asReturnedValue();
714}
715
716void UrlSearchParamsObject::initializeParams()
717{
718 auto *arrayObject = engine()->newArrayObject(0);
719 auto *keys = engine()->newArrayObject(0);
720 auto *values = engine()->newArrayObject(0);
721
722 d()->params.set(engine(), arrayObject);
723 d()->keys.set(engine(), keys);
724 d()->values.set(engine(), values);
725}
726
727void UrlSearchParamsObject::initializeParams(QString value)
728{
729 Q_ASSERT(d()->params == nullptr);
730
731 initializeParams();
732
733 if (value.startsWith(QLatin1Char('?')))
734 value = value.mid(1);
735
736 const QStringList params = value.split(QLatin1Char('&'));
737
738 for (const QString& param : params) {
739 if (param.isEmpty())
740 continue;
741
742 QString key, value;
743
744 int equalsIndex = param.indexOf(QLatin1Char('='));
745 if (equalsIndex != -1) {
746 key = param.left(equalsIndex);
747 value = param.mid(equalsIndex+1);
748 } else {
749 key = param;
750 }
751
752 append(engine()->newString(key), engine()->newString(value));
753 }
754}
755
756void UrlSearchParamsObject::initializeParams(ScopedArrayObject& params)
757{
758 Q_ASSERT(d()->params == nullptr);
759
760 Scope scope(engine());
761
762 uint len = params->getLength();
763 auto *keys = engine()->newArrayObject(len);
764 auto *values = engine()->newArrayObject(len);
765
766 ScopedArrayObject scopedKeys(scope, keys);
767 ScopedArrayObject scopedValues(scope, values);
768
769 for (uint i = 0; i < len; i++)
770 {
771 // fromReturnedValue is safe; everything is reachable via params
772 // so the gc won't collect it; we control params, so there can't be
773 // any weird proxy magic
774 QV4::Value pair = Value::fromReturnedValue(params->get(i));
775 auto *pairArrayObject = pair.as<ArrayObject>();
776
777 QV4::Value key = Value::fromReturnedValue(pairArrayObject->get(uint(0)));
778 QV4::Value value = Value::fromReturnedValue(pairArrayObject->get(uint(1)));
779
780 scopedKeys->put(i, key);
781 scopedValues->put(i, value);
782 }
783
784
785 d()->params.set(engine(), params->d());
786 d()->keys.set(engine(), keys);
787 d()->values.set(engine(), values);
788}
789
790void UrlSearchParamsObject::initializeParams(ScopedObject& params)
791{
792 Q_ASSERT(d()->params == nullptr);
793
794 initializeParams();
795
796 Scope scope(engine());
797 ObjectIterator it(scope, params, ObjectIterator::EnumerableOnly);
798
799 ScopedValue name(scope);
800 ScopedValue val(scope);
801
802 while (true) {
803 name = it.nextPropertyNameAsString(val);
804 if (name->isNull())
805 break;
806
807 Heap::String *nameStr = name->as<String>()->d();
808 Heap::String *valStr = val->toString(engine());
809
810 append(nameStr, valStr);
811 }
812}
813
814void UrlSearchParamsObject::setParams(const QList<QStringList> &params)
815{
816 auto *arrayObject = engine()->newArrayObject(0);
817 auto *keys = engine()->newArrayObject(0);
818 auto *values = engine()->newArrayObject(0);
819
820 Scope scope(engine());
821
822 ScopedArrayObject scopedArray(scope, arrayObject);
823
824 ScopedArrayObject scopedKeys(scope, keys);
825 ScopedArrayObject scopedValues(scope, values);
826 ScopedArrayObject valuePairObject(scope);
827 ScopedValue key(scope);
828 ScopedValue value(scope);
829
830 uint len = 0;
831
832 for (const QStringList& param : params) {
833
834 auto *valuePair = engine()->newArrayObject(2);
835
836 valuePairObject = valuePair;
837
838 key = Value::fromHeapObject(engine()->newString(param[0]));
839 value = Value::fromHeapObject(engine()->newString(param[1]));
840 valuePairObject->put(uint(0), key);
841 valuePairObject->put(uint(1), value);
842
843 scopedKeys->put(len, key);
844 scopedValues->put(len, value);
845
846 scopedArray->put(len, valuePairObject);
847 len++;
848 }
849
850 d()->params.set(engine(), arrayObject);
851 d()->keys.set(engine(), keys);
852 d()->values.set(engine(), values);
853}
854
855void UrlSearchParamsObject::setUrlObject(const UrlObject *url)
856{
857 d()->url.set(engine(), url->d());
858}
859
860void UrlSearchParamsObject::append(Heap::String *name, Heap::String *value)
861{
862 Scope scope(engine());
863
864 ScopedArrayObject scopedArray(scope, d()->params);
865 ScopedArrayObject scopedKeys(scope, d()->keys);
866 ScopedArrayObject scopedValues(scope, d()->values);
867
868 auto *valuePair = engine()->newArrayObject(2);
869
870 ScopedArrayObject valuePairObject(scope, valuePair);
871
872 ScopedValue keyScoped(scope, Value::fromHeapObject(name));
873 ScopedValue valueScoped(scope, Value::fromHeapObject(value));
874 valuePairObject->put(uint(0), keyScoped);
875 valuePairObject->put(uint(1), valueScoped);
876
877 uint len = scopedArray->getLength();
878
879 scopedKeys->put(len, keyScoped);
880 scopedValues->put(len, valueScoped);
881
882 scopedArray->put(len, valuePairObject);
883}
884
885QList<QStringList> UrlSearchParamsObject::params() const
886{
887 auto *arrayObject = d()->params.get();
888 Scope scope(engine());
889 ScopedArrayObject scopedArray(scope, arrayObject);
890
891 QList<QStringList> result;
892
893 uint len = scopedArray->getLength();
894
895 for (uint i = 0; i < len; i++) {
896 QV4::Value pair = Value::fromReturnedValue(scopedArray->get(i));
897 auto *pairArrayObject = pair.as<ArrayObject>();
898
899 QV4::Value key = Value::fromReturnedValue(pairArrayObject->get(uint(0)));
900 QV4::Value value = Value::fromReturnedValue(pairArrayObject->get(uint(1)));
901
902 result << QStringList { key.toQString(), value.toQString() };
903 }
904
905 return result;
906}
907
908Heap::UrlObject *UrlSearchParamsObject::urlObject() const
909{
910 return d()->url.get();
911}
912
913QString UrlSearchParamsObject::searchString() const
914{
915 QString search = QLatin1String("");
916 auto params = this->params();
917 auto len = params.size();
918 for (int i = 0; i < len; ++i) {
919 const QStringList &param = params[i];
920 search += param[0] + QLatin1Char('=') + param[1];
921 if (i != len - 1)
922 search += QLatin1Char('&');
923 }
924 return search;
925}
926
927int UrlSearchParamsObject::length() const
928{
929 auto *arrayObject = d()->params.get();
930 Scope scope(engine());
931 ScopedArrayObject scopedArray(scope, arrayObject);
932
933 return scopedArray->getLength();
934}
935
936int UrlSearchParamsObject::indexOf(QString name, int last) const
937{
938 auto *arrayObject = d()->params.get();
939 Scope scope(engine());
940 ScopedArrayObject scopedArray(scope, arrayObject);
941
942 int len = scopedArray->getLength();
943
944 for (int i = last + 1; i < len; i++) {
945 // fromReturnedValue is safe, scopedArray is a normal array and takes care of marking
946 QV4::Value pair = Value::fromReturnedValue(scopedArray->get(i));
947 auto *pairArrayObject = pair.as<ArrayObject>();
948
949 QV4::Value key = Value::fromReturnedValue(pairArrayObject->get(uint(0)));
950
951 if (key.toQString() == name)
952 return i;
953 }
954
955 return -1;
956}
957
958QString UrlSearchParamsObject::stringAt(int index, int pairIndex) const
959{
960 auto *arrayObject = d()->params.get();
961 Scope scope(engine());
962 ScopedArrayObject scopedArray(scope, arrayObject);
963
964 if (index >= scopedArray->getLength())
965 return {};
966
967 QV4::Value pair = Value::fromReturnedValue(scopedArray->get(index));
968 auto *pairArrayObject = pair.as<ArrayObject>();
969
970 QV4::Value value = Value::fromReturnedValue(pairArrayObject->get(pairIndex));
971
972 return value.toQString();
973}
974
975QV4::Heap::String * UrlSearchParamsObject::stringAtRaw(int index, int pairIndex) const
976{
977 auto *arrayObject = d()->params.get();
978 Scope scope(engine());
979 ScopedArrayObject scopedArray(scope, arrayObject);
980
981 if (index >= scopedArray->getLength())
982 return nullptr;
983
984 QV4::Value pair = Value::fromReturnedValue(scopedArray->get(index));
985 auto *pairArrayObject = pair.as<ArrayObject>();
986
987 QV4::Value value = Value::fromReturnedValue(pairArrayObject->get(pairIndex));
988
989 return value.as<String>()->d();
990}
991
992QString UrlSearchParamsObject::nameAt(int index) const
993{
994 return stringAt(index, 0);
995}
996
997QV4::Heap::String * UrlSearchParamsObject::nameAtRaw(int index) const
998{
999 return stringAtRaw(index, 0);
1000}
1001
1002
1003QString UrlSearchParamsObject::valueAt(int index) const
1004{
1005 return stringAt(index, 1);
1006}
1007
1008QV4::Heap::String * UrlSearchParamsObject::valueAtRaw(int index) const
1009{
1010 return stringAtRaw(index, 1);
1011}
1012
1013namespace {
1014struct UrlSearchParamsObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
1015{
1016 ~UrlSearchParamsObjectOwnPropertyKeyIterator() override = default;
1017 PropertyKey next(const QV4::Object *o, Property *pd = nullptr,
1018 PropertyAttributes *attrs = nullptr) override;
1019};
1020} // namespace
1021
1022PropertyKey UrlSearchParamsObjectOwnPropertyKeyIterator::next(const QV4::Object *o, Property *pd,
1023 PropertyAttributes *attrs)
1024{
1025 const UrlSearchParamsObject *usp = static_cast<const UrlSearchParamsObject *>(o);
1026
1027 Scope scope(usp);
1028
1029 uint len = usp->length();
1030 if (arrayIndex < len) {
1031 uint index = arrayIndex;
1032 ++arrayIndex;
1033 if (attrs)
1034 *attrs = Attr_NotConfigurable | Attr_NotWritable;
1035 if (pd)
1036 pd->value = usp->engine()->newString(usp->nameAt(index));
1037 return PropertyKey::fromArrayIndex(index);
1038 }
1039
1040 return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
1041}
1042
1043OwnPropertyKeyIterator *UrlSearchParamsObject::virtualOwnPropertyKeys(const Object *m,
1044 Value *target)
1045{
1046 *target = *m;
1047 return new UrlSearchParamsObjectOwnPropertyKeyIterator;
1048}
1049
1050PropertyAttributes UrlSearchParamsObject::virtualGetOwnProperty(const Managed *m, PropertyKey id,
1051 Property *p)
1052{
1053 PropertyAttributes attributes = Object::virtualGetOwnProperty(m, id, p);
1054 if (attributes != Attr_Invalid)
1055 return attributes;
1056
1057 if (id.isArrayIndex()) {
1058 const int index = id.asArrayIndex();
1059 const auto usp = static_cast<const UrlSearchParamsObject *>(m);
1060 if (index < usp->length()) {
1061 if (p)
1062 p->value = usp->engine()->newString(usp->nameAt(index));
1063 return Attr_NotConfigurable | Attr_NotWritable;
1064 }
1065 }
1066
1067 return Object::virtualGetOwnProperty(m, id, p);
1068}
1069
1070static bool checkSearchParamsType(ExecutionEngine *v4, const Scoped<UrlSearchParamsObject> &o)
1071{
1072 if (o)
1073 return true;
1074
1075 v4->throwTypeError(QStringLiteral("Value of \"this\" must be of type URLSearchParams"));
1076 return false;
1077}
1078
1079ReturnedValue UrlSearchParamsPrototype::method_toString(
1080 const FunctionObject *b, const Value *thisObject, const Value *, int)
1081{
1082 ExecutionEngine *v4 = b->engine();
1083 Scope scope(v4);
1084
1085 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1086 if (!checkSearchParamsType(v4, o))
1087 return Encode::undefined();
1088
1089 const auto params = o->params();
1090
1091 QString value;
1092
1093 for (const QStringList &pair : params)
1094 value += QLatin1String("%1=%2&").arg(QString::fromUtf8(QUrl::toPercentEncoding(pair[0])),
1095 QString::fromUtf8(QUrl::toPercentEncoding(pair[1])));
1096
1097 value.chop(1);
1098
1099 return Encode(v4->newString(value));
1100}
1101
1102ReturnedValue UrlSearchParamsPrototype::method_sort(const FunctionObject *b, const Value *thisObject,
1103 const Value *, int)
1104{
1105 ExecutionEngine *v4 = b->engine();
1106 Scope scope(v4);
1107
1108 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1109 if (!checkSearchParamsType(v4, o))
1110 return Encode::undefined();
1111
1112 QList<QStringList> params = o->params();
1113 std::stable_sort(params.begin(), params.end(), [](QStringList a, QStringList b) { return a[0] < b[0]; });
1114
1115 o->setParams(params);
1116
1117 return Encode::undefined();
1118}
1119
1120ReturnedValue UrlSearchParamsPrototype::method_append(const FunctionObject *b, const Value *thisObject,
1121 const Value *argv, int argc)
1122{
1123 ExecutionEngine *v4 = b->engine();
1124 Scope scope(v4);
1125
1126 if (argc != 2)
1127 return v4->throwError(QLatin1String("Bad amount of arguments"));
1128
1129 ScopedValue argName(scope, argv[0]);
1130 ScopedValue argValue(scope, argv[1]);
1131
1132 String *argNameString = argName->stringValue();
1133
1134 if (argNameString == nullptr)
1135 return v4->throwTypeError(QLatin1String("Invalid argument provided"));
1136
1137 ScopedString name(scope, argName->as<String>());
1138 ScopedString value(scope, argValue->toString(v4));
1139
1140 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1141 if (!checkSearchParamsType(v4, o))
1142 return Encode::undefined();
1143
1144 o->append(name->d(), value->d());
1145
1146 return Encode::undefined();
1147}
1148
1149ReturnedValue UrlSearchParamsPrototype::method_delete(const FunctionObject *b, const Value *thisObject,
1150 const Value *argv, int argc)
1151{
1152 ExecutionEngine *v4 = b->engine();
1153 Scope scope(v4);
1154
1155 if (argc != 1)
1156 return v4->throwError(QLatin1String("Bad amount of arguments"));
1157
1158 ScopedValue argName(scope, argv[0]);
1159
1160 String *argNameString = argName->stringValue();
1161
1162 if (argNameString == nullptr)
1163 return v4->throwTypeError(QLatin1String("Invalid argument provided"));
1164
1165 QString name = argNameString->toQString();
1166
1167 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1168 if (!checkSearchParamsType(v4, o))
1169 return Encode::undefined();
1170
1171 QList<QStringList> params = o->params();
1172 params.removeIf([&name](const auto &pair) { return pair.at(0) == name; });
1173
1174 o->setParams(params);
1175
1176 return Encode::undefined();
1177}
1178
1179ReturnedValue UrlSearchParamsPrototype::method_has(const FunctionObject *b, const Value *thisObject,
1180 const Value *argv, int argc)
1181{
1182 ExecutionEngine *v4 = b->engine();
1183 Scope scope(v4);
1184
1185 if (argc != 1)
1186 return v4->throwError(QLatin1String("Bad amount of arguments"));
1187
1188 ScopedValue argName(scope, argv[0]);
1189
1190 String *argNameString = argName->stringValue();
1191
1192 if (argNameString == nullptr)
1193 return v4->throwTypeError(QLatin1String("Invalid argument provided"));
1194
1195 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1196 if (!checkSearchParamsType(v4, o))
1197 return Encode::undefined();
1198
1199 QString name = argNameString->toQString();
1200
1201 return Encode(o->indexOf(name) != -1);
1202}
1203
1204ReturnedValue UrlSearchParamsPrototype::method_set(const FunctionObject *b, const Value *thisObject,
1205 const Value *argv, int argc)
1206{
1207 ExecutionEngine *v4 = b->engine();
1208 Scope scope(v4);
1209
1210 if (argc != 2)
1211 return v4->throwError(QLatin1String("Bad amount of arguments"));
1212
1213 ScopedValue argName(scope, argv[0]);
1214 ScopedValue argValue(scope, argv[1]);
1215
1216 String *argNameString = argName->stringValue();
1217
1218 if (argNameString == nullptr)
1219 return v4->throwTypeError(QLatin1String("Invalid argument provided"));
1220
1221 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1222 if (!checkSearchParamsType(v4, o))
1223 return Encode::undefined();
1224
1225 QString name = argNameString->toQString();
1226 QString value = argValue->toQString();
1227
1228 auto params = o->params();
1229
1230 bool matched = false;
1231
1232 for (auto it = params.begin(); it != params.end();) {
1233 QStringList &param = *it;
1234 if (param[0] == name) {
1235 if (!matched) {
1236 param[1] = value;
1237 matched = true;
1238 } else {
1239 it = params.erase(it);
1240 continue;
1241 }
1242 }
1243 it++;
1244 }
1245
1246 if (!matched)
1247 params << QStringList { name, value };
1248
1249 o->setParams(params);
1250
1251 Scoped<UrlObject> scopedUrlObject(scope, o->d()->url.get());
1252 if (scopedUrlObject)
1253 scopedUrlObject->setSearch(o->searchString());
1254
1255 return Encode::undefined();
1256}
1257
1258ReturnedValue UrlSearchParamsPrototype::method_get(const FunctionObject *b, const Value *thisObject,
1259 const Value *argv, int argc)
1260{
1261 ExecutionEngine *v4 = b->engine();
1262 Scope scope(v4);
1263
1264 if (argc != 1)
1265 return v4->throwError(QLatin1String("Bad amount of arguments"));
1266
1267 ScopedValue argName(scope, argv[0]);
1268
1269 String *argNameString = argName->stringValue();
1270
1271 if (argNameString == nullptr)
1272 return v4->throwTypeError(QLatin1String("Invalid argument provided"));
1273
1274 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1275 if (!checkSearchParamsType(v4, o))
1276 return Encode::undefined();
1277
1278 QString name = argNameString->toQString();
1279
1280 int index = o->indexOf(name);
1281
1282 if (index == -1)
1283 return Encode::null();
1284
1285 return Encode(o->valueAtRaw(index));
1286}
1287
1288ReturnedValue UrlSearchParamsPrototype::method_getAll(const FunctionObject *b,
1289 const Value *thisObject, const Value *argv,
1290 int argc)
1291{
1292 ExecutionEngine *v4 = b->engine();
1293 Scope scope(v4);
1294
1295 if (argc != 1)
1296 return v4->throwError(QLatin1String("Bad amount of arguments"));
1297
1298 ScopedValue argName(scope, argv[0]);
1299
1300 String *argNameString = argName->stringValue();
1301
1302 if (argNameString == nullptr)
1303 return v4->throwTypeError(QLatin1String("Invalid argument provided"));
1304
1305 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1306 if (!checkSearchParamsType(v4, o))
1307 return Encode::undefined();
1308
1309 QString name = argNameString->toQString();
1310
1311 auto *arrayObject = v4->newArrayObject(0);
1312 ScopedArrayObject result(scope, arrayObject);
1313
1314 int i = 0;
1315 ScopedValue value(scope);
1316 for (int index = o->indexOf(name); index != -1; index = o->indexOf(name, index)) {
1317 value = Value::fromHeapObject(o->valueAtRaw(index));
1318 result->put(i++, value);
1319 }
1320
1321 return Encode(arrayObject);
1322}
1323
1324ReturnedValue UrlSearchParamsPrototype::method_forEach(const FunctionObject *b,
1325 const Value *thisObject, const Value *argv,
1326 int argc)
1327{
1328 ExecutionEngine *v4 = b->engine();
1329 Scope scope(v4);
1330
1331 if (argc != 1)
1332 return v4->throwError(QLatin1String("Bad amount of arguments"));
1333
1334 ScopedValue argFunc(scope, argv[0]);
1335
1336 FunctionObject *func = argFunc->as<FunctionObject>();
1337
1338 if (func == nullptr)
1339 return v4->throwTypeError(QLatin1String("Invalid argument: must be a function"));
1340
1341 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1342 if (!checkSearchParamsType(v4, o))
1343 return Encode::undefined();
1344
1345 Scoped<String> name(scope);
1346 Scoped<String> value(scope);
1347 QV4::JSCallArguments calldata(scope, 2);
1348 for (int i = 0; i < o->length(); i++) {
1349 name = o->nameAtRaw(i);
1350 value = o->valueAtRaw(i);
1351
1352 calldata.args[0] = value;
1353 calldata.args[1] = name;
1354
1355 func->call(calldata);
1356 }
1357
1358 return Encode::undefined();
1359}
1360
1361ReturnedValue UrlSearchParamsPrototype::method_entries(const FunctionObject *b,
1362 const Value *thisObject, const Value *,
1363 int argc)
1364{
1365 ExecutionEngine *v4 = b->engine();
1366 Scope scope(v4);
1367
1368 if (argc != 0)
1369 return v4->throwError(QLatin1String("Bad amount of arguments"));
1370
1371 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1372 if (!checkSearchParamsType(v4, o))
1373 return Encode::undefined();
1374
1375 ScopedObject params(scope, o->d()->params.get());
1376
1377 Scoped<ArrayIteratorObject> paramsIterator(scope, v4->newArrayIteratorObject(params));
1378 paramsIterator->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
1379 return paramsIterator->asReturnedValue();
1380}
1381
1382ReturnedValue UrlSearchParamsPrototype::method_keys(const FunctionObject *b,
1383 const Value *thisObject, const Value *,
1384 int argc)
1385{
1386 ExecutionEngine *v4 = b->engine();
1387 Scope scope(v4);
1388
1389 if (argc != 0)
1390 return v4->throwError(QLatin1String("Bad amount of arguments"));
1391
1392 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1393 if (!checkSearchParamsType(v4, o))
1394 return Encode::undefined();
1395
1396 ScopedObject keys(scope, o->d()->keys.get());
1397
1398 Scoped<ArrayIteratorObject> keysIterator(scope, v4->newArrayIteratorObject(keys));
1399 keysIterator->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
1400 return keysIterator->asReturnedValue();
1401}
1402
1403ReturnedValue UrlSearchParamsPrototype::method_values(const FunctionObject *b,
1404 const Value *thisObject, const Value *,
1405 int argc)
1406{
1407 ExecutionEngine *v4 = b->engine();
1408 Scope scope(v4);
1409
1410 if (argc != 0)
1411 return v4->throwError(QLatin1String("Bad amount of arguments"));
1412
1413 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1414 if (!checkSearchParamsType(v4, o))
1415 return Encode::undefined();
1416
1417 ScopedObject values(scope, o->d()->values.get());
1418
1419 Scoped<ArrayIteratorObject> valuesIterator(scope, v4->newArrayIteratorObject(values));
1420 valuesIterator->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
1421 return valuesIterator->asReturnedValue();
1422}
Definition qjsvalue.h:24
DEFINE_OBJECT_VTABLE(UrlCtor)
DEFINE_OBJECT_VTABLE(UrlSearchParamsObject)
DEFINE_OBJECT_VTABLE(UrlSearchParamsCtor)
static bool checkSearchParamsType(ExecutionEngine *v4, const Scoped< UrlSearchParamsObject > &o)
static bool checkUrlObjectType(ExecutionEngine *v4, const Scoped< UrlObject > &r)
DEFINE_OBJECT_VTABLE(UrlObject)