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 for (uint i = 0; i < len; i++) {
680 // safe to user fromReturnedValue: Normal array, which will ensure marking
681 QV4::Value pair = Value::fromReturnedValue(argArray->get(i));
682 auto *pairArrayObject = pair.as<ArrayObject>();
683
684 if (pairArrayObject == nullptr) {
685 return v4->throwTypeError(
686 QLatin1String("element %1 is not a pair").arg(QString::number(i)));
687 }
688
689
690 ScopedArrayObject pairArray(scope, pairArrayObject);
691
692
693 uint pairLen = pairArray->getLength();
694
695
696 if (pairLen != 2) {
697 return v4->throwTypeError(QLatin1String("pair %1 has %2 elements instead of 2")
698 .arg(QString::number(i))
699 .arg(QString::number(pairLen)));
700 }
701 }
702
703 urlSearchParamsObject->initializeParams(argArray);
704 } else if (argObject != nullptr) {
705 ScopedObject scopedObject(scope, argObject);
706 urlSearchParamsObject->initializeParams(scopedObject);
707 } else {
708 QString value = argc > 0 ? arg->toQString() : QLatin1String("");
709 urlSearchParamsObject->initializeParams(value);
710 }
711
712 return obj->asReturnedValue();
713}
714
715void UrlSearchParamsObject::initializeParams()
716{
717 auto *arrayObject = engine()->newArrayObject(0);
718 auto *keys = engine()->newArrayObject(0);
719 auto *values = engine()->newArrayObject(0);
720
721 d()->params.set(engine(), arrayObject);
722 d()->keys.set(engine(), keys);
723 d()->values.set(engine(), values);
724}
725
726void UrlSearchParamsObject::initializeParams(QString value)
727{
728 Q_ASSERT(d()->params == nullptr);
729
730 initializeParams();
731
732 if (value.startsWith(QLatin1Char('?')))
733 value = value.mid(1);
734
735 const QStringList params = value.split(QLatin1Char('&'));
736
737 for (const QString& param : params) {
738 if (param.isEmpty())
739 continue;
740
741 QString key, value;
742
743 int equalsIndex = param.indexOf(QLatin1Char('='));
744 if (equalsIndex != -1) {
745 key = param.left(equalsIndex);
746 value = param.mid(equalsIndex+1);
747 } else {
748 key = param;
749 }
750
751 append(engine()->newString(key), engine()->newString(value));
752 }
753}
754
755void UrlSearchParamsObject::initializeParams(ScopedArrayObject& params)
756{
757 Q_ASSERT(d()->params == nullptr);
758
759 Scope scope(engine());
760
761 uint len = params->getLength();
762 auto *keys = engine()->newArrayObject(len);
763 auto *values = engine()->newArrayObject(len);
764
765 ScopedArrayObject scopedKeys(scope, keys);
766 ScopedArrayObject scopedValues(scope, values);
767
768 for (uint i = 0; i < len; i++)
769 {
770 // fromReturnedValue is safe; everything is reachable via params
771 // so the gc won't collect it; we control params, so there can't be
772 // any weird proxy magic
773 QV4::Value pair = Value::fromReturnedValue(params->get(i));
774 auto *pairArrayObject = pair.as<ArrayObject>();
775
776 QV4::Value key = Value::fromReturnedValue(pairArrayObject->get(uint(0)));
777 QV4::Value value = Value::fromReturnedValue(pairArrayObject->get(uint(1)));
778
779 scopedKeys->put(i, key);
780 scopedValues->put(i, value);
781 }
782
783
784 d()->params.set(engine(), params->d());
785 d()->keys.set(engine(), keys);
786 d()->values.set(engine(), values);
787}
788
789void UrlSearchParamsObject::initializeParams(ScopedObject& params)
790{
791 Q_ASSERT(d()->params == nullptr);
792
793 initializeParams();
794
795 Scope scope(engine());
796 ObjectIterator it(scope, params, ObjectIterator::EnumerableOnly);
797
798 ScopedValue name(scope);
799 ScopedValue val(scope);
800
801 while (true) {
802 name = it.nextPropertyNameAsString(val);
803 if (name->isNull())
804 break;
805
806 Heap::String *nameStr = name->as<String>()->d();
807 Heap::String *valStr = val->toString(engine());
808
809 append(nameStr, valStr);
810 }
811}
812
813void UrlSearchParamsObject::setParams(QList<QStringList> params)
814{
815 auto *arrayObject = engine()->newArrayObject(0);
816 auto *keys = engine()->newArrayObject(0);
817 auto *values = engine()->newArrayObject(0);
818
819 Scope scope(engine());
820
821 ScopedArrayObject scopedArray(scope, arrayObject);
822
823 ScopedArrayObject scopedKeys(scope, keys);
824 ScopedArrayObject scopedValues(scope, values);
825
826 uint len = 0;
827
828 for (const QStringList& param : params) {
829
830 auto *valuePair = engine()->newArrayObject(2);
831
832 ScopedArrayObject valuePairObject(scope, valuePair);
833
834 ScopedValue key(scope, Value::fromHeapObject(engine()->newString(param[0])));
835 ScopedValue value(scope, Value::fromHeapObject(engine()->newString(param[1])));
836 valuePairObject->put(uint(0), key);
837 valuePairObject->put(uint(1), value);
838
839 scopedKeys->put(len, key);
840 scopedValues->put(len, value);
841
842 scopedArray->put(len, valuePairObject);
843 len++;
844 }
845
846 d()->params.set(engine(), arrayObject);
847 d()->keys.set(engine(), keys);
848 d()->values.set(engine(), values);
849}
850
851void UrlSearchParamsObject::setUrlObject(const UrlObject *url)
852{
853 d()->url.set(engine(), url->d());
854}
855
856void UrlSearchParamsObject::append(Heap::String *name, Heap::String *value)
857{
858 Scope scope(engine());
859
860 ScopedArrayObject scopedArray(scope, d()->params);
861 ScopedArrayObject scopedKeys(scope, d()->keys);
862 ScopedArrayObject scopedValues(scope, d()->values);
863
864 auto *valuePair = engine()->newArrayObject(2);
865
866 ScopedArrayObject valuePairObject(scope, valuePair);
867
868 ScopedValue keyScoped(scope, Value::fromHeapObject(name));
869 ScopedValue valueScoped(scope, Value::fromHeapObject(value));
870 valuePairObject->put(uint(0), keyScoped);
871 valuePairObject->put(uint(1), valueScoped);
872
873 uint len = scopedArray->getLength();
874
875 scopedKeys->put(len, keyScoped);
876 scopedValues->put(len, valueScoped);
877
878 scopedArray->put(len, valuePairObject);
879}
880
881QList<QStringList> UrlSearchParamsObject::params() const
882{
883 auto *arrayObject = d()->params.get();
884 Scope scope(engine());
885 ScopedArrayObject scopedArray(scope, arrayObject);
886
887 QList<QStringList> result;
888
889 uint len = scopedArray->getLength();
890
891 for (uint i = 0; i < len; i++) {
892 QV4::Value pair = Value::fromReturnedValue(scopedArray->get(i));
893 auto *pairArrayObject = pair.as<ArrayObject>();
894
895 QV4::Value key = Value::fromReturnedValue(pairArrayObject->get(uint(0)));
896 QV4::Value value = Value::fromReturnedValue(pairArrayObject->get(uint(1)));
897
898 result << QStringList { key.toQString(), value.toQString() };
899 }
900
901 return result;
902}
903
904Heap::UrlObject *UrlSearchParamsObject::urlObject() const
905{
906 return d()->url.get();
907}
908
909QString UrlSearchParamsObject::searchString() const
910{
911 QString search = QLatin1String("");
912 auto params = this->params();
913 auto len = params.size();
914 for (int i = 0; i < len; ++i) {
915 const QStringList &param = params[i];
916 search += param[0] + QLatin1Char('=') + param[1];
917 if (i != len - 1)
918 search += QLatin1Char('&');
919 }
920 return search;
921}
922
923int UrlSearchParamsObject::length() const
924{
925 auto *arrayObject = d()->params.get();
926 Scope scope(engine());
927 ScopedArrayObject scopedArray(scope, arrayObject);
928
929 return scopedArray->getLength();
930}
931
932int UrlSearchParamsObject::indexOf(QString name, int last) const
933{
934 auto *arrayObject = d()->params.get();
935 Scope scope(engine());
936 ScopedArrayObject scopedArray(scope, arrayObject);
937
938 int len = scopedArray->getLength();
939
940 for (int i = last + 1; i < len; i++) {
941 // fromReturnedValue is safe, scopedArray is a normal array and takes care of marking
942 QV4::Value pair = Value::fromReturnedValue(scopedArray->get(i));
943 auto *pairArrayObject = pair.as<ArrayObject>();
944
945 QV4::Value key = Value::fromReturnedValue(pairArrayObject->get(uint(0)));
946
947 if (key.toQString() == name)
948 return i;
949 }
950
951 return -1;
952}
953
954QString UrlSearchParamsObject::stringAt(int index, int pairIndex) const
955{
956 auto *arrayObject = d()->params.get();
957 Scope scope(engine());
958 ScopedArrayObject scopedArray(scope, arrayObject);
959
960 if (index >= scopedArray->getLength())
961 return {};
962
963 QV4::Value pair = Value::fromReturnedValue(scopedArray->get(index));
964 auto *pairArrayObject = pair.as<ArrayObject>();
965
966 QV4::Value value = Value::fromReturnedValue(pairArrayObject->get(pairIndex));
967
968 return value.toQString();
969}
970
971QV4::Heap::String * UrlSearchParamsObject::stringAtRaw(int index, int pairIndex) const
972{
973 auto *arrayObject = d()->params.get();
974 Scope scope(engine());
975 ScopedArrayObject scopedArray(scope, arrayObject);
976
977 if (index >= scopedArray->getLength())
978 return nullptr;
979
980 QV4::Value pair = Value::fromReturnedValue(scopedArray->get(index));
981 auto *pairArrayObject = pair.as<ArrayObject>();
982
983 QV4::Value value = Value::fromReturnedValue(pairArrayObject->get(pairIndex));
984
985 return value.as<String>()->d();
986}
987
988QString UrlSearchParamsObject::nameAt(int index) const
989{
990 return stringAt(index, 0);
991}
992
993QV4::Heap::String * UrlSearchParamsObject::nameAtRaw(int index) const
994{
995 return stringAtRaw(index, 0);
996}
997
998
999QString UrlSearchParamsObject::valueAt(int index) const
1000{
1001 return stringAt(index, 1);
1002}
1003
1004QV4::Heap::String * UrlSearchParamsObject::valueAtRaw(int index) const
1005{
1006 return stringAtRaw(index, 1);
1007}
1008
1009namespace {
1010struct UrlSearchParamsObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
1011{
1012 ~UrlSearchParamsObjectOwnPropertyKeyIterator() override = default;
1013 PropertyKey next(const QV4::Object *o, Property *pd = nullptr,
1014 PropertyAttributes *attrs = nullptr) override;
1015};
1016} // namespace
1017
1018PropertyKey UrlSearchParamsObjectOwnPropertyKeyIterator::next(const QV4::Object *o, Property *pd,
1019 PropertyAttributes *attrs)
1020{
1021 const UrlSearchParamsObject *usp = static_cast<const UrlSearchParamsObject *>(o);
1022
1023 Scope scope(usp);
1024
1025 uint len = usp->length();
1026 if (arrayIndex < len) {
1027 uint index = arrayIndex;
1028 ++arrayIndex;
1029 if (attrs)
1030 *attrs = Attr_NotConfigurable | Attr_NotWritable;
1031 if (pd)
1032 pd->value = usp->engine()->newString(usp->nameAt(index));
1033 return PropertyKey::fromArrayIndex(index);
1034 }
1035
1036 return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
1037}
1038
1039OwnPropertyKeyIterator *UrlSearchParamsObject::virtualOwnPropertyKeys(const Object *m,
1040 Value *target)
1041{
1042 *target = *m;
1043 return new UrlSearchParamsObjectOwnPropertyKeyIterator;
1044}
1045
1046PropertyAttributes UrlSearchParamsObject::virtualGetOwnProperty(const Managed *m, PropertyKey id,
1047 Property *p)
1048{
1049 PropertyAttributes attributes = Object::virtualGetOwnProperty(m, id, p);
1050 if (attributes != Attr_Invalid)
1051 return attributes;
1052
1053 if (id.isArrayIndex()) {
1054 const int index = id.asArrayIndex();
1055 const auto usp = static_cast<const UrlSearchParamsObject *>(m);
1056 if (index < usp->length()) {
1057 if (p)
1058 p->value = usp->engine()->newString(usp->nameAt(index));
1059 return Attr_NotConfigurable | Attr_NotWritable;
1060 }
1061 }
1062
1063 return Object::virtualGetOwnProperty(m, id, p);
1064}
1065
1066static bool checkSearchParamsType(ExecutionEngine *v4, const Scoped<UrlSearchParamsObject> &o)
1067{
1068 if (o)
1069 return true;
1070
1071 v4->throwTypeError(QStringLiteral("Value of \"this\" must be of type URLSearchParams"));
1072 return false;
1073}
1074
1075ReturnedValue UrlSearchParamsPrototype::method_toString(
1076 const FunctionObject *b, const Value *thisObject, const Value *, int)
1077{
1078 ExecutionEngine *v4 = b->engine();
1079 Scope scope(v4);
1080
1081 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1082 if (!checkSearchParamsType(v4, o))
1083 return Encode::undefined();
1084
1085 auto params = o->params();
1086
1087 QString value;
1088
1089 for (const QStringList &pair : params)
1090 value += QLatin1String("%1=%2&").arg(QString::fromUtf8(QUrl::toPercentEncoding(pair[0])),
1091 QString::fromUtf8(QUrl::toPercentEncoding(pair[1])));
1092
1093 value.chop(1);
1094
1095 return Encode(v4->newString(value));
1096}
1097
1098ReturnedValue UrlSearchParamsPrototype::method_sort(const FunctionObject *b, const Value *thisObject,
1099 const Value *, int)
1100{
1101 ExecutionEngine *v4 = b->engine();
1102 Scope scope(v4);
1103
1104 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1105 if (!checkSearchParamsType(v4, o))
1106 return Encode::undefined();
1107
1108 QList<QStringList> params = o->params();
1109 std::stable_sort(params.begin(), params.end(), [](QStringList a, QStringList b) { return a[0] < b[0]; });
1110
1111 o->setParams(params);
1112
1113 return Encode::undefined();
1114}
1115
1116ReturnedValue UrlSearchParamsPrototype::method_append(const FunctionObject *b, const Value *thisObject,
1117 const Value *argv, int argc)
1118{
1119 ExecutionEngine *v4 = b->engine();
1120 Scope scope(v4);
1121
1122 if (argc != 2)
1123 return v4->throwError(QLatin1String("Bad amount of arguments"));
1124
1125 ScopedValue argName(scope, argv[0]);
1126 ScopedValue argValue(scope, argv[1]);
1127
1128 String *argNameString = argName->stringValue();
1129
1130 if (argNameString == nullptr)
1131 return v4->throwTypeError(QLatin1String("Invalid argument provided"));
1132
1133 ScopedString name(scope, argName->as<String>());
1134 ScopedString value(scope, argValue->toString(v4));
1135
1136 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1137 if (!checkSearchParamsType(v4, o))
1138 return Encode::undefined();
1139
1140 o->append(name->d(), value->d());
1141
1142 return Encode::undefined();
1143}
1144
1145ReturnedValue UrlSearchParamsPrototype::method_delete(const FunctionObject *b, const Value *thisObject,
1146 const Value *argv, int argc)
1147{
1148 ExecutionEngine *v4 = b->engine();
1149 Scope scope(v4);
1150
1151 if (argc != 1)
1152 return v4->throwError(QLatin1String("Bad amount of arguments"));
1153
1154 ScopedValue argName(scope, argv[0]);
1155
1156 String *argNameString = argName->stringValue();
1157
1158 if (argNameString == nullptr)
1159 return v4->throwTypeError(QLatin1String("Invalid argument provided"));
1160
1161 QString name = argNameString->toQString();
1162
1163 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1164 if (!checkSearchParamsType(v4, o))
1165 return Encode::undefined();
1166
1167 QList<QStringList> params = o->params();
1168 params.removeIf([&name](const auto &pair) { return pair.at(0) == name; });
1169
1170 o->setParams(params);
1171
1172 return Encode::undefined();
1173}
1174
1175ReturnedValue UrlSearchParamsPrototype::method_has(const FunctionObject *b, const Value *thisObject,
1176 const Value *argv, int argc)
1177{
1178 ExecutionEngine *v4 = b->engine();
1179 Scope scope(v4);
1180
1181 if (argc != 1)
1182 return v4->throwError(QLatin1String("Bad amount of arguments"));
1183
1184 ScopedValue argName(scope, argv[0]);
1185
1186 String *argNameString = argName->stringValue();
1187
1188 if (argNameString == nullptr)
1189 return v4->throwTypeError(QLatin1String("Invalid argument provided"));
1190
1191 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1192 if (!checkSearchParamsType(v4, o))
1193 return Encode::undefined();
1194
1195 QString name = argNameString->toQString();
1196
1197 return Encode(o->indexOf(name) != -1);
1198}
1199
1200ReturnedValue UrlSearchParamsPrototype::method_set(const FunctionObject *b, const Value *thisObject,
1201 const Value *argv, int argc)
1202{
1203 ExecutionEngine *v4 = b->engine();
1204 Scope scope(v4);
1205
1206 if (argc != 2)
1207 return v4->throwError(QLatin1String("Bad amount of arguments"));
1208
1209 ScopedValue argName(scope, argv[0]);
1210 ScopedValue argValue(scope, argv[1]);
1211
1212 String *argNameString = argName->stringValue();
1213
1214 if (argNameString == nullptr)
1215 return v4->throwTypeError(QLatin1String("Invalid argument provided"));
1216
1217 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1218 if (!checkSearchParamsType(v4, o))
1219 return Encode::undefined();
1220
1221 QString name = argNameString->toQString();
1222 QString value = argValue->toQString();
1223
1224 auto params = o->params();
1225
1226 bool matched = false;
1227
1228 for (auto it = params.begin(); it != params.end();) {
1229 QStringList &param = *it;
1230 if (param[0] == name) {
1231 if (!matched) {
1232 param[1] = value;
1233 matched = true;
1234 } else {
1235 it = params.erase(it);
1236 continue;
1237 }
1238 }
1239 it++;
1240 }
1241
1242 if (!matched)
1243 params << QStringList { name, value };
1244
1245 o->setParams(params);
1246
1247 Scoped<UrlObject> scopedUrlObject(scope, o->d()->url.get());
1248 if (scopedUrlObject)
1249 scopedUrlObject->setSearch(o->searchString());
1250
1251 return Encode::undefined();
1252}
1253
1254ReturnedValue UrlSearchParamsPrototype::method_get(const FunctionObject *b, const Value *thisObject,
1255 const Value *argv, int argc)
1256{
1257 ExecutionEngine *v4 = b->engine();
1258 Scope scope(v4);
1259
1260 if (argc != 1)
1261 return v4->throwError(QLatin1String("Bad amount of arguments"));
1262
1263 ScopedValue argName(scope, argv[0]);
1264
1265 String *argNameString = argName->stringValue();
1266
1267 if (argNameString == nullptr)
1268 return v4->throwTypeError(QLatin1String("Invalid argument provided"));
1269
1270 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1271 if (!checkSearchParamsType(v4, o))
1272 return Encode::undefined();
1273
1274 QString name = argNameString->toQString();
1275
1276 int index = o->indexOf(name);
1277
1278 if (index == -1)
1279 return Encode::null();
1280
1281 return Encode(o->valueAtRaw(index));
1282}
1283
1284ReturnedValue UrlSearchParamsPrototype::method_getAll(const FunctionObject *b,
1285 const Value *thisObject, const Value *argv,
1286 int argc)
1287{
1288 ExecutionEngine *v4 = b->engine();
1289 Scope scope(v4);
1290
1291 if (argc != 1)
1292 return v4->throwError(QLatin1String("Bad amount of arguments"));
1293
1294 ScopedValue argName(scope, argv[0]);
1295
1296 String *argNameString = argName->stringValue();
1297
1298 if (argNameString == nullptr)
1299 return v4->throwTypeError(QLatin1String("Invalid argument provided"));
1300
1301 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1302 if (!checkSearchParamsType(v4, o))
1303 return Encode::undefined();
1304
1305 QString name = argNameString->toQString();
1306
1307 auto *arrayObject = v4->newArrayObject(0);
1308 ScopedArrayObject result(scope, arrayObject);
1309
1310 int i = 0;
1311 for (int index = o->indexOf(name); index != -1; index = o->indexOf(name, index)) {
1312 ScopedValue value(scope, Value::fromHeapObject(o->valueAtRaw(index)));
1313 result->put(i++, value);
1314 }
1315
1316 return Encode(arrayObject);
1317}
1318
1319ReturnedValue UrlSearchParamsPrototype::method_forEach(const FunctionObject *b,
1320 const Value *thisObject, const Value *argv,
1321 int argc)
1322{
1323 ExecutionEngine *v4 = b->engine();
1324 Scope scope(v4);
1325
1326 if (argc != 1)
1327 return v4->throwError(QLatin1String("Bad amount of arguments"));
1328
1329 ScopedValue argFunc(scope, argv[0]);
1330
1331 FunctionObject *func = argFunc->as<FunctionObject>();
1332
1333 if (func == nullptr)
1334 return v4->throwTypeError(QLatin1String("Invalid argument: must be a function"));
1335
1336 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1337 if (!checkSearchParamsType(v4, o))
1338 return Encode::undefined();
1339
1340 for (int i = 0; i < o->length(); i++) {
1341 Scoped<String> name(scope, o->nameAtRaw(i));
1342 Scoped<String> value(scope, o->valueAtRaw(i));
1343
1344 QV4::JSCallArguments calldata(scope, 2);
1345
1346 calldata.args[0] = value;
1347 calldata.args[1] = name;
1348
1349 func->call(calldata);
1350 }
1351
1352 return Encode::undefined();
1353}
1354
1355ReturnedValue UrlSearchParamsPrototype::method_entries(const FunctionObject *b,
1356 const Value *thisObject, const Value *,
1357 int argc)
1358{
1359 ExecutionEngine *v4 = b->engine();
1360 Scope scope(v4);
1361
1362 if (argc != 0)
1363 return v4->throwError(QLatin1String("Bad amount of arguments"));
1364
1365 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1366 if (!checkSearchParamsType(v4, o))
1367 return Encode::undefined();
1368
1369 ScopedObject params(scope, o->d()->params.get());
1370
1371 Scoped<ArrayIteratorObject> paramsIterator(scope, v4->newArrayIteratorObject(params));
1372 paramsIterator->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
1373 return paramsIterator->asReturnedValue();
1374}
1375
1376ReturnedValue UrlSearchParamsPrototype::method_keys(const FunctionObject *b,
1377 const Value *thisObject, const Value *,
1378 int argc)
1379{
1380 ExecutionEngine *v4 = b->engine();
1381 Scope scope(v4);
1382
1383 if (argc != 0)
1384 return v4->throwError(QLatin1String("Bad amount of arguments"));
1385
1386 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1387 if (!checkSearchParamsType(v4, o))
1388 return Encode::undefined();
1389
1390 ScopedObject keys(scope, o->d()->keys.get());
1391
1392 Scoped<ArrayIteratorObject> keysIterator(scope, v4->newArrayIteratorObject(keys));
1393 keysIterator->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
1394 return keysIterator->asReturnedValue();
1395}
1396
1397ReturnedValue UrlSearchParamsPrototype::method_values(const FunctionObject *b,
1398 const Value *thisObject, const Value *,
1399 int argc)
1400{
1401 ExecutionEngine *v4 = b->engine();
1402 Scope scope(v4);
1403
1404 if (argc != 0)
1405 return v4->throwError(QLatin1String("Bad amount of arguments"));
1406
1407 Scoped<UrlSearchParamsObject> o(scope, thisObject);
1408 if (!checkSearchParamsType(v4, o))
1409 return Encode::undefined();
1410
1411 ScopedObject values(scope, o->d()->values.get());
1412
1413 Scoped<ArrayIteratorObject> valuesIterator(scope, v4->newArrayIteratorObject(values));
1414 valuesIterator->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
1415 return valuesIterator->asReturnedValue();
1416}
Definition qjsvalue.h:23
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)