Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant
++
++ #include "qv4arrayiterator_p.h"
++ #include "qv4urlobject_p.h"
++
++ #include <QtCore/QUrl>
++
++ #include <qv4jscall_p.h>
++ #include <qv4objectiterator_p.h>
++
++ using namespace QV4;
++
++ DEFINE_OBJECT_VTABLE(UrlObject);
++ DEFINE_OBJECT_VTABLE(UrlCtor);
++
++ DEFINE_OBJECT_VTABLE(UrlSearchParamsObject);
++ DEFINE_OBJECT_VTABLE(UrlSearchParamsCtor);
++
++
++ void Heap::UrlCtor::init(QV4::ExecutionEngine *engine)
++ {
++ Heap::FunctionObject::init(engine, QLatin1String("URL"));
++ }
++
++ void UrlPrototype::init(ExecutionEngine *engine, Object *ctor)
++ {
++ Q_UNUSED(ctor);
++
++ Scope scope(engine);
++ ScopedObject o(scope);
++ ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
++
++ defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
++ defineDefaultProperty(engine->id_toString(), method_getHref);
++ defineDefaultProperty(QLatin1String("toJSON"), method_getHref);
++
++ defineAccessorProperty(QLatin1String("hash"), method_getHash, method_setHash);
++ defineAccessorProperty(QLatin1String("host"), method_getHost, method_setHost);
++ defineAccessorProperty(QLatin1String("hostname"), method_getHostname, method_setHostname);
++ defineAccessorProperty(QLatin1String("href"), method_getHref, method_setHref);
++ defineAccessorProperty(QLatin1String("origin"), method_getOrigin, nullptr);
++ defineAccessorProperty(QLatin1String("password"), method_getPassword, method_setPassword);
++ defineAccessorProperty(QLatin1String("pathname"), method_getPathname, method_setPathname);
++ defineAccessorProperty(QLatin1String("port"), method_getPort, method_setPort);
++ defineAccessorProperty(QLatin1String("protocol"), method_getProtocol, method_setProtocol);
++ defineAccessorProperty(QLatin1String("search"), method_getSearch, method_setSearch);
++ defineAccessorProperty(QLatin1String("searchParams"), method_getSearchParams, nullptr);
++ defineAccessorProperty(QLatin1String("username"), method_getUsername, method_setUsername);
++ }
++
++ bool UrlObject::setHash(const QString &hash)
++ {
++ return updateUrl([&](QUrl *url) {
++ url->setFragment(hash.startsWith(QLatin1Char('#')) ? hash.mid(1) : hash);
++ });
++ }
++
++ bool UrlObject::setHostname(const QString &hostName)
++ {
++ return updateUrl([&](QUrl *url) {
++ url->setHost(hostName);
++ });
++ }
++
++ bool UrlObject::setHost(const QString &host)
++ {
++ return updateUrl([&](QUrl *url) {
++ qsizetype colon = host.indexOf(QLatin1Char(':'));
++ if (colon == -1) {
++ url->setHost(host);
++ url->setPort(-1);
++ } else {
++ url->setHost(host.left(colon));
++ url->setPort(QStringView(host).mid(colon + 1).toInt());
++ }
++ });
++ }
++
++ bool UrlObject::setHref(const QString &href)
++ {
++ QUrl replacement(href);
++ if (!replacement.isValid() || replacement.isRelative())
++ return false;
++
++ d()->setUrl(std::move(replacement));
++ return true;
++ }
++
++ void UrlObject::setUrl(const QUrl &url)
++ {
++ d()->setUrl(QUrl(url));
++ }
++
++ bool UrlObject::setPassword(const QString &password)
++ {
++ return updateUrl([&](QUrl *url) {
++ url->setPassword(password);
++ });
++ }
++
++ bool UrlObject::setPathname(const QString &pathname)
++ {
++ return updateUrl([&](QUrl *url) {
++ url->setPath(pathname);
++ });
++ }
++
++ bool UrlObject::setPort(const QString &port)
++ {
++ return updateUrl([&](QUrl *url) {
++ url->setPort(port.isEmpty() ? -1 : port.toInt());
++ });
++ }
++
++ bool UrlObject::setProtocol(const QString &protocolOrScheme)
++ {
++ // If there is one or several ':' in the protocolOrScheme,
++ // everything from the first colon is removed.
++
++ return updateUrl([&](QUrl *url) {
++ const qsizetype colon = protocolOrScheme.indexOf(QLatin1Char(':'));
++ url->setScheme(colon == -1 ? protocolOrScheme : protocolOrScheme.left(colon));
++ });
++ }
++
++ bool UrlObject::setSearch(const QString &search)
++ {
++ return updateUrl([&](QUrl *url) {
++ if (search.startsWith(QLatin1Char('?'))) {
++ url->setQuery(search.mid(1));
++ return;
++ }
++
++ if (search.isEmpty()) {
++ // In JS, setting an empty query removes the '?' as well. QUrl only does that for a
++ // null QString. The way to get a lone '?' in JS is to set the search property to "?".
++ // That is why this is in the else branch.
++ url->setQuery(QString()); // it's null now
++ return;
++ }
++
++ url->setQuery(search);
++ });
++ }
++
++ bool UrlObject::setUsername(const QString &username)
++ {
++ return updateUrl([&](QUrl *url) {
++ url->setUserName(username);
++ });
++ }
++
++ QString UrlObject::search() const
++ {
++ const QUrl url = d()->url();
++ if (!url.hasQuery() || url.query().isEmpty())
++ return QString();
++
++ constexpr auto options = QUrl::ComponentFormattingOption::EncodeSpaces
++ | QUrl::ComponentFormattingOption::EncodeUnicode
++ | QUrl::ComponentFormattingOption::EncodeReserved;
++ return QLatin1Char('?') + url.query(options);
++ }
++
++ static bool checkUrlObjectType(ExecutionEngine *v4, const Scoped<UrlObject> &r)
++ {
++ if (r)
++ return true;
++
++ v4->throwTypeError(QStringLiteral("Value of \"this\" must be of type URL"));
++ return false;
++ }
++
++ ReturnedValue UrlPrototype::method_getHash(const FunctionObject *b, const Value *thisObject,
++ const Value *, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ return Encode(v4->newString(r->hash()));
++ }
++
++ ReturnedValue UrlPrototype::method_setHash(const FunctionObject *b, const Value *thisObject,
++ const Value *argv, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ ScopedValue arg(scope, argv[0]);
++ String *stringValue = arg->stringValue();
++
++ if (stringValue == nullptr)
++ return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ r->setHash(stringValue->toQString());
++
++ return Encode::undefined();
++ }
++
++ ReturnedValue UrlPrototype::method_getHost(const FunctionObject *b, const Value *thisObject,
++ const Value *, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ return Encode(v4->newString(r->host()));
++ }
++
++ ReturnedValue UrlPrototype::method_setHost(const FunctionObject *b, const Value *thisObject,
++ const Value *argv, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ ScopedValue arg(scope, argv[0]);
++ String *stringValue = arg->stringValue();
++
++ if (stringValue == nullptr)
++ return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ QString host = stringValue->toQString();
++ if (!r->setHost(host))
++ return v4->throwTypeError(QLatin1String("Invalid host: %1").arg(host));
++
++ return Encode::undefined();
++ }
++
++ ReturnedValue UrlPrototype::method_getHostname(const FunctionObject *b, const Value *thisObject,
++ const Value *, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ return Encode(v4->newString(r->hostname()));
++ }
++
++ ReturnedValue UrlPrototype::method_setHostname(const FunctionObject *b, const Value *thisObject,
++ const Value *argv, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ ScopedValue arg(scope, argv[0]);
++ String *stringValue = arg->stringValue();
++
++ if (stringValue == nullptr)
++ return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ QString hostname = stringValue->toQString();
++ if (!r->setHostname(hostname))
++ return v4->throwTypeError(QLatin1String("Invalid hostname: %1").arg(hostname));
++
++ return Encode::undefined();
++ }
++
++ ReturnedValue UrlPrototype::method_getHref(const FunctionObject *b, const Value *thisObject,
++ const Value *, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ return Encode(v4->newString(r->href()));
++ }
++
++ ReturnedValue UrlPrototype::method_setHref(const FunctionObject *b, const Value *thisObject,
++ const Value *argv, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ ScopedValue arg(scope, argv[0]);
++ String *stringValue = arg->stringValue();
++
++ if (stringValue == nullptr)
++ return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ QString href = stringValue->toQString();
++ if (!r->setHref(href))
++ return v4->throwTypeError(QLatin1String("Invalid URL: %1").arg(href));
++
++ return Encode::undefined();
++ }
++
++ ReturnedValue UrlPrototype::method_getOrigin(const FunctionObject *b, const Value *thisObject,
++ const Value *, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ return Encode(v4->newString(r->origin()));
++ }
++
++ ReturnedValue UrlPrototype::method_getPassword(const FunctionObject *b, const Value *thisObject,
++ const Value *, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ return Encode(v4->newString(r->password()));
++ }
++
++ ReturnedValue UrlPrototype::method_setPassword(const FunctionObject *b, const Value *thisObject,
++ const Value *argv, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ ScopedValue arg(scope, argv[0]);
++ String *stringValue = arg->stringValue();
++
++ if (stringValue == nullptr)
++ return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ r->setPassword(stringValue->toQString());
++
++ return Encode::undefined();
++ }
++
++ ReturnedValue UrlPrototype::method_getPathname(const FunctionObject *b, const Value *thisObject,
++ const Value *, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ return Encode(v4->newString(r->pathname()));
++ }
++
++ ReturnedValue UrlPrototype::method_setPathname(const FunctionObject *b, const Value *thisObject,
++ const Value *argv, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ ScopedValue arg(scope, argv[0]);
++ String *stringValue = arg->stringValue();
++
++ if (stringValue == nullptr)
++ return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ r->setPathname(stringValue->toQString());
++
++ return Encode::undefined();
++ }
++
++ ReturnedValue UrlPrototype::method_getPort(const FunctionObject *b, const Value *thisObject,
++ const Value *, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ return Encode(v4->newString(r->port()));
++ }
++
++ ReturnedValue UrlPrototype::method_setPort(const FunctionObject *b, const Value *thisObject,
++ const Value *argv, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ ScopedValue arg(scope, argv[0]);
++ String *stringValue = arg->stringValue();
++
++ QString port;
++
++ if (stringValue != nullptr)
++ port = stringValue->toQString();
++ else if (arg->isInt32())
++ port = QString::number(arg->toInt32());
++ else
++ return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ if (!r->setPort(port))
++ return v4->throwTypeError(QLatin1String("Invalid port: %1").arg(port));
++
++ return Encode::undefined();
++ }
++
++ ReturnedValue UrlPrototype::method_getProtocol(const FunctionObject *b, const Value *thisObject,
++ const Value *, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ return Encode(v4->newString(r->protocol()));
++ }
++
++ ReturnedValue UrlPrototype::method_setProtocol(const FunctionObject *b, const Value *thisObject,
++ const Value *argv, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ ScopedValue arg(scope, argv[0]);
++ String *stringValue = arg->stringValue();
++
++ if (stringValue == nullptr)
++ return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ r->setProtocol(stringValue->toQString());
++
++ return Encode::undefined();
++ }
++
++ ReturnedValue UrlPrototype::method_getSearch(const FunctionObject *b, const Value *thisObject,
++ const Value *, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ return Encode(v4->newString(r->search()));
++ }
++
++ ReturnedValue UrlPrototype::method_setSearch(const FunctionObject *b, const Value *thisObject,
++ const Value *argv, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ ScopedValue arg(scope, argv[0]);
++ String *stringValue = arg->stringValue();
++
++ if (stringValue == nullptr)
++ return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ r->setSearch(stringValue->toQString());
++
++ return Encode::undefined();
++ }
++
++ ReturnedValue UrlPrototype::method_getUsername(const FunctionObject *b, const Value *thisObject,
++ const Value *, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ return Encode(v4->newString(r->username()));
++ }
++
++ ReturnedValue UrlPrototype::method_setUsername(const FunctionObject *b, const Value *thisObject,
++ const Value *argv, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ ScopedValue arg(scope, argv[0]);
++ String *stringValue = arg->stringValue();
++
++ if (stringValue == nullptr)
++ return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ r->setUsername(stringValue->toQString());
++
++ return Encode::undefined();
++ }
++
++ ReturnedValue UrlPrototype::method_getSearchParams(const FunctionObject *b, const Value *thisObject,
++ const Value *, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ Scoped<UrlObject> r(scope, thisObject);
++ if (!checkUrlObjectType(v4, r))
++ return Encode::undefined();
++
++ Scoped<UrlSearchParamsObject> usp(scope, v4->newUrlSearchParamsObject());
++
++ usp->setUrlObject(thisObject->as<UrlObject>());
++ usp->initializeParams(r->search());
++
++ return usp->asReturnedValue();
++ }
++
++ ReturnedValue UrlCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv,
++ int argc, const Value *newTarget)
++ {
++ ExecutionEngine *v4 = that->engine();
++
++ if (argc < 1 || argc > 2)
++ return v4->throwError(QLatin1String("Invalid amount of arguments"));
++
++ Scope scope(v4);
++
++ ScopedValue arg1(scope, argv[0]);
++
++ QString arg1String = arg1->toQString();
++ QString urlString;
++
++ if (argc == 2) {
++ ScopedValue arg2(scope, argv[1]);
++ String *arg2StringValue = arg2->stringValue();
++
++ if (arg2StringValue == nullptr)
++ return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
++
++ QUrl url = QUrl(arg2StringValue->toQString());
++ QUrl relativeUrl = QUrl(arg1String);
++
++ QString baseUrlPath = url.path();
++ QString relativePath = relativeUrl.path();
++
++ // If the base URL contains a path the last section of it is discarded
++ int lastSlash = baseUrlPath.lastIndexOf(QLatin1Char('/'));
++ if (lastSlash != -1)
++ baseUrlPath.truncate(lastSlash);
++
++ if (!relativePath.startsWith(QLatin1Char('/')))
++ relativePath = relativePath.prepend(QLatin1Char('/'));
++
++ url.setPath(baseUrlPath + relativePath);
++ url.setFragment(relativeUrl.fragment());
++ url.setQuery(relativeUrl.query());
++
++ urlString = url.toString();
++ } else {
++ urlString = arg1String;
++ }
++
++ ReturnedValue o = Encode(v4->newUrlObject());
++
++ if (!newTarget)
++ return o;
++
++ ScopedObject obj(scope, o);
++ obj->setProtoFromNewTarget(newTarget);
++
++ UrlObject *urlObject = obj->as<UrlObject>();
++
++ if (!urlObject->setHref(urlString))
++ return v4->throwTypeError(QLatin1String("Invalid URL: %1").arg(urlString));
++
++ return obj->asReturnedValue();
++ }
++
++
++ void Heap::UrlSearchParamsCtor::init(QV4::ExecutionEngine *engine)
++ {
++ Heap::FunctionObject::init(engine, QLatin1String("URLSearchParams"));
++ }
++
++ void UrlSearchParamsPrototype::init(ExecutionEngine *engine, Object *ctor)
++ {
++ Q_UNUSED(ctor);
++
++ Scope scope(engine);
++ ScopedObject o(scope);
++ ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
++
++ defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
++ defineDefaultProperty(engine->id_toString(), method_toString);
++ defineDefaultProperty(QLatin1String("sort"), method_sort);
++ defineDefaultProperty(QLatin1String("append"), method_append);
++ defineDefaultProperty(QLatin1String("delete"), method_delete);
++ defineDefaultProperty(QLatin1String("has"), method_has);
++ defineDefaultProperty(QLatin1String("set"), method_set);
++ defineDefaultProperty(QLatin1String("get"), method_get);
++ defineDefaultProperty(QLatin1String("getAll"), method_getAll);
++ defineDefaultProperty(QLatin1String("forEach"), method_forEach);
++ defineDefaultProperty(QLatin1String("entries"), method_entries);
++ defineDefaultProperty(QLatin1String("keys"), method_keys);
++ defineDefaultProperty(QLatin1String("values"), method_values);
++ }
++
++ ReturnedValue UrlSearchParamsCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv,
++ int argc, const Value *newTarget)
++ {
++ ExecutionEngine *v4 = that->engine();
++
++ if (argc > 1)
++ return v4->throwError(QLatin1String("Invalid amount of arguments"));
++
++ Scope scope(v4);
++
++ ScopedValue arg(scope, argv[0]);
++ ArrayObject *argArrayObject = arg->as<ArrayObject>();
++ Object *argObject = arg->as<Object>();
++
++ ReturnedValue o = Encode(v4->newUrlSearchParamsObject());
++
++ if (!newTarget)
++ return o;
++
++ ScopedObject obj(scope, o);
++ obj->setProtoFromNewTarget(newTarget);
++
++ UrlSearchParamsObject *urlSearchParamsObject = obj->as<UrlSearchParamsObject>();
++
++ if (argArrayObject != nullptr) {
++ ScopedArrayObject argArray(scope, argArrayObject);
++
++ uint len = argArray->getLength();
++
++ for (uint i = 0; i < len; i++) {
++ // safe to user fromReturnedValue: Normal array, which will ensure marking
++ QV4::Value pair = Value::fromReturnedValue(argArray->get(i));
++ auto *pairArrayObject = pair.as<ArrayObject>();
++
++ if (pairArrayObject == nullptr) {
++ return v4->throwTypeError(
++ QLatin1String("element %1 is not a pair").arg(QString::number(i)));
++ }
++
++
++ ScopedArrayObject pairArray(scope, pairArrayObject);
++
++
++ uint pairLen = pairArray->getLength();
++
++
++ if (pairLen != 2) {
++ return v4->throwTypeError(QLatin1String("pair %1 has %2 elements instead of 2")
++ .arg(QString::number(i))
++ .arg(QString::number(pairLen)));
++ }
++ }
++
++ urlSearchParamsObject->initializeParams(argArray);
++ } else if (argObject != nullptr) {
++ ScopedObject scopedObject(scope, argObject);
++ urlSearchParamsObject->initializeParams(scopedObject);
++ } else {
++ QString value = argc > 0 ? arg->toQString() : QLatin1String("");
++ urlSearchParamsObject->initializeParams(value);
++ }
++
++ return obj->asReturnedValue();
++ }
++
++ void UrlSearchParamsObject::initializeParams()
++ {
++ auto *arrayObject = engine()->newArrayObject(0);
++ auto *keys = engine()->newArrayObject(0);
++ auto *values = engine()->newArrayObject(0);
++
++ d()->params.set(engine(), arrayObject);
++ d()->keys.set(engine(), keys);
++ d()->values.set(engine(), values);
++ }
++
++ void UrlSearchParamsObject::initializeParams(QString value)
++ {
++ Q_ASSERT(d()->params == nullptr);
++
++ initializeParams();
++
++ if (value.startsWith(QLatin1Char('?')))
++ value = value.mid(1);
++
++ const QStringList params = value.split(QLatin1Char('&'));
++
++ for (const QString& param : params) {
++ if (param.isEmpty())
++ continue;
++
++ QString key, value;
++
++ int equalsIndex = param.indexOf(QLatin1Char('='));
++ if (equalsIndex != -1) {
++ key = param.left(equalsIndex);
++ value = param.mid(equalsIndex+1);
++ } else {
++ key = param;
++ }
++
++ append(engine()->newString(key), engine()->newString(value));
++ }
++ }
++
++ void UrlSearchParamsObject::initializeParams(ScopedArrayObject& params)
++ {
++ Q_ASSERT(d()->params == nullptr);
++
++ Scope scope(engine());
++
++ uint len = params->getLength();
++ auto *keys = engine()->newArrayObject(len);
++ auto *values = engine()->newArrayObject(len);
++
++ ScopedArrayObject scopedKeys(scope, keys);
++ ScopedArrayObject scopedValues(scope, values);
++
++ for (uint i = 0; i < len; i++)
++ {
++ // fromReturnedValue is safe; everything is reachable via params
++ // so the gc won't collect it; we control params, so there can't be
++ // any weird proxy magic
++ QV4::Value pair = Value::fromReturnedValue(params->get(i));
++ auto *pairArrayObject = pair.as<ArrayObject>();
++
++ QV4::Value key = Value::fromReturnedValue(pairArrayObject->get(uint(0)));
++ QV4::Value value = Value::fromReturnedValue(pairArrayObject->get(uint(1)));
++
++ scopedKeys->put(i, key);
++ scopedValues->put(i, value);
++ }
++
++
++ d()->params.set(engine(), params->d());
++ d()->keys.set(engine(), keys);
++ d()->values.set(engine(), values);
++ }
++
++ void UrlSearchParamsObject::initializeParams(ScopedObject& params)
++ {
++ Q_ASSERT(d()->params == nullptr);
++
++ initializeParams();
++
++ Scope scope(engine());
++ ObjectIterator it(scope, params, ObjectIterator::EnumerableOnly);
++
++ ScopedValue name(scope);
++ ScopedValue val(scope);
++
++ while (true) {
++ name = it.nextPropertyNameAsString(val);
++ if (name->isNull())
++ break;
++
++ Heap::String *nameStr = name->as<String>()->d();
++ Heap::String *valStr = val->toString(engine());
++
++ append(nameStr, valStr);
++ }
++ }
++
++ void UrlSearchParamsObject::setParams(const QList<QStringList> ¶ms)
++ {
++ auto *arrayObject = engine()->newArrayObject(0);
++ auto *keys = engine()->newArrayObject(0);
++ auto *values = engine()->newArrayObject(0);
++
++ Scope scope(engine());
++
++ ScopedArrayObject scopedArray(scope, arrayObject);
++
++ ScopedArrayObject scopedKeys(scope, keys);
++ ScopedArrayObject scopedValues(scope, values);
++
++ uint len = 0;
++
++ for (const QStringList& param : params) {
++
++ auto *valuePair = engine()->newArrayObject(2);
++
++ ScopedArrayObject valuePairObject(scope, valuePair);
++
++ ScopedValue key(scope, Value::fromHeapObject(engine()->newString(param[0])));
++ ScopedValue value(scope, Value::fromHeapObject(engine()->newString(param[1])));
++ valuePairObject->put(uint(0), key);
++ valuePairObject->put(uint(1), value);
++
++ scopedKeys->put(len, key);
++ scopedValues->put(len, value);
++
++ scopedArray->put(len, valuePairObject);
++ len++;
++ }
++
++ d()->params.set(engine(), arrayObject);
++ d()->keys.set(engine(), keys);
++ d()->values.set(engine(), values);
++ }
++
++ void UrlSearchParamsObject::setUrlObject(const UrlObject *url)
++ {
++ d()->url.set(engine(), url->d());
++ }
++
++ void UrlSearchParamsObject::append(Heap::String *name, Heap::String *value)
++ {
++ Scope scope(engine());
++
++ ScopedArrayObject scopedArray(scope, d()->params);
++ ScopedArrayObject scopedKeys(scope, d()->keys);
++ ScopedArrayObject scopedValues(scope, d()->values);
++
++ auto *valuePair = engine()->newArrayObject(2);
++
++ ScopedArrayObject valuePairObject(scope, valuePair);
++
++ ScopedValue keyScoped(scope, Value::fromHeapObject(name));
++ ScopedValue valueScoped(scope, Value::fromHeapObject(value));
++ valuePairObject->put(uint(0), keyScoped);
++ valuePairObject->put(uint(1), valueScoped);
++
++ uint len = scopedArray->getLength();
++
++ scopedKeys->put(len, keyScoped);
++ scopedValues->put(len, valueScoped);
++
++ scopedArray->put(len, valuePairObject);
++ }
++
++ QList<QStringList> UrlSearchParamsObject::params() const
++ {
++ auto *arrayObject = d()->params.get();
++ Scope scope(engine());
++ ScopedArrayObject scopedArray(scope, arrayObject);
++
++ QList<QStringList> result;
++
++ uint len = scopedArray->getLength();
++
++ for (uint i = 0; i < len; i++) {
++ QV4::Value pair = Value::fromReturnedValue(scopedArray->get(i));
++ auto *pairArrayObject = pair.as<ArrayObject>();
++
++ QV4::Value key = Value::fromReturnedValue(pairArrayObject->get(uint(0)));
++ QV4::Value value = Value::fromReturnedValue(pairArrayObject->get(uint(1)));
++
++ result << QStringList { key.toQString(), value.toQString() };
++ }
++
++ return result;
++ }
++
++ Heap::UrlObject *UrlSearchParamsObject::urlObject() const
++ {
++ return d()->url.get();
++ }
++
++ QString UrlSearchParamsObject::searchString() const
++ {
++ QString search = QLatin1String("");
++ auto params = this->params();
++ auto len = params.size();
++ for (int i = 0; i < len; ++i) {
++ const QStringList ¶m = params[i];
++ search += param[0] + QLatin1Char('=') + param[1];
++ if (i != len - 1)
++ search += QLatin1Char('&');
++ }
++ return search;
++ }
++
++ int UrlSearchParamsObject::length() const
++ {
++ auto *arrayObject = d()->params.get();
++ Scope scope(engine());
++ ScopedArrayObject scopedArray(scope, arrayObject);
++
++ return scopedArray->getLength();
++ }
++
++ int UrlSearchParamsObject::indexOf(QString name, int last) const
++ {
++ auto *arrayObject = d()->params.get();
++ Scope scope(engine());
++ ScopedArrayObject scopedArray(scope, arrayObject);
++
++ int len = scopedArray->getLength();
++
++ for (int i = last + 1; i < len; i++) {
++ // fromReturnedValue is safe, scopedArray is a normal array and takes care of marking
++ QV4::Value pair = Value::fromReturnedValue(scopedArray->get(i));
++ auto *pairArrayObject = pair.as<ArrayObject>();
++
++ QV4::Value key = Value::fromReturnedValue(pairArrayObject->get(uint(0)));
++
++ if (key.toQString() == name)
++ return i;
++ }
++
++ return -1;
++ }
++
++ QString UrlSearchParamsObject::stringAt(int index, int pairIndex) const
++ {
++ auto *arrayObject = d()->params.get();
++ Scope scope(engine());
++ ScopedArrayObject scopedArray(scope, arrayObject);
++
++ if (index >= scopedArray->getLength())
++ return {};
++
++ QV4::Value pair = Value::fromReturnedValue(scopedArray->get(index));
++ auto *pairArrayObject = pair.as<ArrayObject>();
++
++ QV4::Value value = Value::fromReturnedValue(pairArrayObject->get(pairIndex));
++
++ return value.toQString();
++ }
++
++ QV4::Heap::String * UrlSearchParamsObject::stringAtRaw(int index, int pairIndex) const
++ {
++ auto *arrayObject = d()->params.get();
++ Scope scope(engine());
++ ScopedArrayObject scopedArray(scope, arrayObject);
++
++ if (index >= scopedArray->getLength())
++ return nullptr;
++
++ QV4::Value pair = Value::fromReturnedValue(scopedArray->get(index));
++ auto *pairArrayObject = pair.as<ArrayObject>();
++
++ QV4::Value value = Value::fromReturnedValue(pairArrayObject->get(pairIndex));
++
++ return value.as<String>()->d();
++ }
++
++ QString UrlSearchParamsObject::nameAt(int index) const
++ {
++ return stringAt(index, 0);
++ }
++
++ QV4::Heap::String * UrlSearchParamsObject::nameAtRaw(int index) const
++ {
++ return stringAtRaw(index, 0);
++ }
++
++
++ QString UrlSearchParamsObject::valueAt(int index) const
++ {
++ return stringAt(index, 1);
++ }
++
++ QV4::Heap::String * UrlSearchParamsObject::valueAtRaw(int index) const
++ {
++ return stringAtRaw(index, 1);
++ }
++
++ namespace {
++ struct UrlSearchParamsObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
++ {
++ ~UrlSearchParamsObjectOwnPropertyKeyIterator() override = default;
++ PropertyKey next(const QV4::Object *o, Property *pd = nullptr,
++ PropertyAttributes *attrs = nullptr) override;
++ };
++ } // namespace
++
++ PropertyKey UrlSearchParamsObjectOwnPropertyKeyIterator::next(const QV4::Object *o, Property *pd,
++ PropertyAttributes *attrs)
++ {
++ const UrlSearchParamsObject *usp = static_cast<const UrlSearchParamsObject *>(o);
++
++ Scope scope(usp);
++
++ uint len = usp->length();
++ if (arrayIndex < len) {
++ uint index = arrayIndex;
++ ++arrayIndex;
++ if (attrs)
++ *attrs = Attr_NotConfigurable | Attr_NotWritable;
++ if (pd)
++ pd->value = usp->engine()->newString(usp->nameAt(index));
++ return PropertyKey::fromArrayIndex(index);
++ }
++
++ return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
++ }
++
++ OwnPropertyKeyIterator *UrlSearchParamsObject::virtualOwnPropertyKeys(const Object *m,
++ Value *target)
++ {
++ *target = *m;
++ return new UrlSearchParamsObjectOwnPropertyKeyIterator;
++ }
++
++ PropertyAttributes UrlSearchParamsObject::virtualGetOwnProperty(const Managed *m, PropertyKey id,
++ Property *p)
++ {
++ PropertyAttributes attributes = Object::virtualGetOwnProperty(m, id, p);
++ if (attributes != Attr_Invalid)
++ return attributes;
++
++ if (id.isArrayIndex()) {
++ const int index = id.asArrayIndex();
++ const auto usp = static_cast<const UrlSearchParamsObject *>(m);
++ if (index < usp->length()) {
++ if (p)
++ p->value = usp->engine()->newString(usp->nameAt(index));
++ return Attr_NotConfigurable | Attr_NotWritable;
++ }
++ }
++
++ return Object::virtualGetOwnProperty(m, id, p);
++ }
++
++ static bool checkSearchParamsType(ExecutionEngine *v4, const Scoped<UrlSearchParamsObject> &o)
++ {
++ if (o)
++ return true;
++
++ v4->throwTypeError(QStringLiteral("Value of \"this\" must be of type URLSearchParams"));
++ return false;
++ }
++
++ ReturnedValue UrlSearchParamsPrototype::method_toString(
++ const FunctionObject *b, const Value *thisObject, const Value *, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ Scoped<UrlSearchParamsObject> o(scope, thisObject);
++ if (!checkSearchParamsType(v4, o))
++ return Encode::undefined();
++
++ const auto params = o->params();
++
++ QString value;
++
++ for (const QStringList &pair : params)
++ value += QLatin1String("%1=%2&").arg(QString::fromUtf8(QUrl::toPercentEncoding(pair[0])),
++ QString::fromUtf8(QUrl::toPercentEncoding(pair[1])));
++
++ value.chop(1);
++
++ return Encode(v4->newString(value));
++ }
++
++ ReturnedValue UrlSearchParamsPrototype::method_sort(const FunctionObject *b, const Value *thisObject,
++ const Value *, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ Scoped<UrlSearchParamsObject> o(scope, thisObject);
++ if (!checkSearchParamsType(v4, o))
++ return Encode::undefined();
++
++ QList<QStringList> params = o->params();
++ std::stable_sort(params.begin(), params.end(), [](QStringList a, QStringList b) { return a[0] < b[0]; });
++
++ o->setParams(params);
++
++ return Encode::undefined();
++ }
++
++ ReturnedValue UrlSearchParamsPrototype::method_append(const FunctionObject *b, const Value *thisObject,
++ const Value *argv, int argc)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ if (argc != 2)
++ return v4->throwError(QLatin1String("Bad amount of arguments"));
++
++ ScopedValue argName(scope, argv[0]);
++ ScopedValue argValue(scope, argv[1]);
++
++ String *argNameString = argName->stringValue();
++
++ if (argNameString == nullptr)
++ return v4->throwTypeError(QLatin1String("Invalid argument provided"));
++
++ ScopedString name(scope, argName->as<String>());
++ ScopedString value(scope, argValue->toString(v4));
++
++ Scoped<UrlSearchParamsObject> o(scope, thisObject);
++ if (!checkSearchParamsType(v4, o))
++ return Encode::undefined();
++
++ o->append(name->d(), value->d());
++
++ return Encode::undefined();
++ }
++
++ ReturnedValue UrlSearchParamsPrototype::method_delete(const FunctionObject *b, const Value *thisObject,
++ const Value *argv, int argc)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ if (argc != 1)
++ return v4->throwError(QLatin1String("Bad amount of arguments"));
++
++ ScopedValue argName(scope, argv[0]);
++
++ String *argNameString = argName->stringValue();
++
++ if (argNameString == nullptr)
++ return v4->throwTypeError(QLatin1String("Invalid argument provided"));
++
++ QString name = argNameString->toQString();
++
++ Scoped<UrlSearchParamsObject> o(scope, thisObject);
++ if (!checkSearchParamsType(v4, o))
++ return Encode::undefined();
++
++ QList<QStringList> params = o->params();
++ params.removeIf([&name](const auto &pair) { return pair.at(0) == name; });
++
++ o->setParams(params);
++
++ return Encode::undefined();
++ }
++
++ ReturnedValue UrlSearchParamsPrototype::method_has(const FunctionObject *b, const Value *thisObject,
++ const Value *argv, int argc)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ if (argc != 1)
++ return v4->throwError(QLatin1String("Bad amount of arguments"));
++
++ ScopedValue argName(scope, argv[0]);
++
++ String *argNameString = argName->stringValue();
++
++ if (argNameString == nullptr)
++ return v4->throwTypeError(QLatin1String("Invalid argument provided"));
++
++ Scoped<UrlSearchParamsObject> o(scope, thisObject);
++ if (!checkSearchParamsType(v4, o))
++ return Encode::undefined();
++
++ QString name = argNameString->toQString();
++
++ return Encode(o->indexOf(name) != -1);
++ }
++
++ ReturnedValue UrlSearchParamsPrototype::method_set(const FunctionObject *b, const Value *thisObject,
++ const Value *argv, int argc)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ if (argc != 2)
++ return v4->throwError(QLatin1String("Bad amount of arguments"));
++
++ ScopedValue argName(scope, argv[0]);
++ ScopedValue argValue(scope, argv[1]);
++
++ String *argNameString = argName->stringValue();
++
++ if (argNameString == nullptr)
++ return v4->throwTypeError(QLatin1String("Invalid argument provided"));
++
++ Scoped<UrlSearchParamsObject> o(scope, thisObject);
++ if (!checkSearchParamsType(v4, o))
++ return Encode::undefined();
++
++ QString name = argNameString->toQString();
++ QString value = argValue->toQString();
++
++ auto params = o->params();
++
++ bool matched = false;
++
++ for (auto it = params.begin(); it != params.end();) {
++ QStringList ¶m = *it;
++ if (param[0] == name) {
++ if (!matched) {
++ param[1] = value;
++ matched = true;
++ } else {
++ it = params.erase(it);
++ continue;
++ }
++ }
++ it++;
++ }
++
++ if (!matched)
++ params << QStringList { name, value };
++
++ o->setParams(params);
++
++ Scoped<UrlObject> scopedUrlObject(scope, o->d()->url.get());
++ if (scopedUrlObject)
++ scopedUrlObject->setSearch(o->searchString());
++
++ return Encode::undefined();
++ }
++
++ ReturnedValue UrlSearchParamsPrototype::method_get(const FunctionObject *b, const Value *thisObject,
++ const Value *argv, int argc)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ if (argc != 1)
++ return v4->throwError(QLatin1String("Bad amount of arguments"));
++
++ ScopedValue argName(scope, argv[0]);
++
++ String *argNameString = argName->stringValue();
++
++ if (argNameString == nullptr)
++ return v4->throwTypeError(QLatin1String("Invalid argument provided"));
++
++ Scoped<UrlSearchParamsObject> o(scope, thisObject);
++ if (!checkSearchParamsType(v4, o))
++ return Encode::undefined();
++
++ QString name = argNameString->toQString();
++
++ int index = o->indexOf(name);
++
++ if (index == -1)
++ return Encode::null();
++
++ return Encode(o->valueAtRaw(index));
++ }
++
++ ReturnedValue UrlSearchParamsPrototype::method_getAll(const FunctionObject *b,
++ const Value *thisObject, const Value *argv,
++ int argc)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ if (argc != 1)
++ return v4->throwError(QLatin1String("Bad amount of arguments"));
++
++ ScopedValue argName(scope, argv[0]);
++
++ String *argNameString = argName->stringValue();
++
++ if (argNameString == nullptr)
++ return v4->throwTypeError(QLatin1String("Invalid argument provided"));
++
++ Scoped<UrlSearchParamsObject> o(scope, thisObject);
++ if (!checkSearchParamsType(v4, o))
++ return Encode::undefined();
++
++ QString name = argNameString->toQString();
++
++ auto *arrayObject = v4->newArrayObject(0);
++ ScopedArrayObject result(scope, arrayObject);
++
++ int i = 0;
++ for (int index = o->indexOf(name); index != -1; index = o->indexOf(name, index)) {
++ ScopedValue value(scope, Value::fromHeapObject(o->valueAtRaw(index)));
++ result->put(i++, value);
++ }
++
++ return Encode(arrayObject);
++ }
++
++ ReturnedValue UrlSearchParamsPrototype::method_forEach(const FunctionObject *b,
++ const Value *thisObject, const Value *argv,
++ int argc)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ if (argc != 1)
++ return v4->throwError(QLatin1String("Bad amount of arguments"));
++
++ ScopedValue argFunc(scope, argv[0]);
++
++ FunctionObject *func = argFunc->as<FunctionObject>();
++
++ if (func == nullptr)
++ return v4->throwTypeError(QLatin1String("Invalid argument: must be a function"));
++
++ Scoped<UrlSearchParamsObject> o(scope, thisObject);
++ if (!checkSearchParamsType(v4, o))
++ return Encode::undefined();
++
++ for (int i = 0; i < o->length(); i++) {
++ Scoped<String> name(scope, o->nameAtRaw(i));
++ Scoped<String> value(scope, o->valueAtRaw(i));
++
++ QV4::JSCallArguments calldata(scope, 2);
++
++ calldata.args[0] = value;
++ calldata.args[1] = name;
++
++ func->call(calldata);
++ }
++
++ return Encode::undefined();
++ }
++
++ ReturnedValue UrlSearchParamsPrototype::method_entries(const FunctionObject *b,
++ const Value *thisObject, const Value *,
++ int argc)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ if (argc != 0)
++ return v4->throwError(QLatin1String("Bad amount of arguments"));
++
++ Scoped<UrlSearchParamsObject> o(scope, thisObject);
++ if (!checkSearchParamsType(v4, o))
++ return Encode::undefined();
++
++ ScopedObject params(scope, o->d()->params.get());
++
++ Scoped<ArrayIteratorObject> paramsIterator(scope, v4->newArrayIteratorObject(params));
++ paramsIterator->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
++ return paramsIterator->asReturnedValue();
++ }
++
++ ReturnedValue UrlSearchParamsPrototype::method_keys(const FunctionObject *b,
++ const Value *thisObject, const Value *,
++ int argc)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ if (argc != 0)
++ return v4->throwError(QLatin1String("Bad amount of arguments"));
++
++ Scoped<UrlSearchParamsObject> o(scope, thisObject);
++ if (!checkSearchParamsType(v4, o))
++ return Encode::undefined();
++
++ ScopedObject keys(scope, o->d()->keys.get());
++
++ Scoped<ArrayIteratorObject> keysIterator(scope, v4->newArrayIteratorObject(keys));
++ keysIterator->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
++ return keysIterator->asReturnedValue();
++ }
++
++ ReturnedValue UrlSearchParamsPrototype::method_values(const FunctionObject *b,
++ const Value *thisObject, const Value *,
++ int argc)
++ {
++ ExecutionEngine *v4 = b->engine();
++ Scope scope(v4);
++
++ if (argc != 0)
++ return v4->throwError(QLatin1String("Bad amount of arguments"));
++
++ Scoped<UrlSearchParamsObject> o(scope, thisObject);
++ if (!checkSearchParamsType(v4, o))
++ return Encode::undefined();
++
++ ScopedObject values(scope, o->d()->values.get());
++
++ Scoped<ArrayIteratorObject> valuesIterator(scope, v4->newArrayIteratorObject(values));
++ valuesIterator->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
++ return valuesIterator->asReturnedValue();
++ }