/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qv4arrayiterator_p.h" #include "qv4urlobject_p.h" #include "qv4stringobject_p.h" #include #include #include using namespace QV4; DEFINE_OBJECT_VTABLE(UrlObject); DEFINE_OBJECT_VTABLE(UrlCtor); DEFINE_OBJECT_VTABLE(UrlSearchParamsObject); DEFINE_OBJECT_VTABLE(UrlSearchParamsCtor); void Heap::UrlCtor::init(QV4::ExecutionContext *scope) { Heap::FunctionObject::init(scope, QLatin1String("URL")); } void UrlPrototype::init(ExecutionEngine *engine, Object *ctor) { Q_UNUSED(ctor); Scope scope(engine); ScopedObject o(scope); defineDefaultProperty(QLatin1String("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(QString hash) { if (hash.startsWith(QLatin1Char('#'))) hash = hash.mid(1); QUrl url = toQUrl(); url.setFragment(hash); if (!url.isValid()) return false; d()->hash.set(engine(), engine()->newString(url.fragment())); d()->href.set(engine(), engine()->newString(url.toString())); return true; } bool UrlObject::setHostname(QString host) { QUrl url = toQUrl(); url.setHost(host); if (!url.isValid()) return false; d()->hostname.set(engine(), engine()->newString(url.host())); d()->href.set(engine(), engine()->newString(url.toString())); updateOrigin(); updateHost(); return true; } bool UrlObject::setHost(QString hostname) { int port = -1; if (hostname.contains(QLatin1Char(':'))) { const QStringList list = hostname.split(QLatin1Char(':')); hostname = list[0]; port = list[1].toInt(); } QUrl url = toQUrl(); url.setHost(hostname); url.setPort(port); if (!url.isValid()) return false; if (url.port() != -1) d()->port.set(engine(), engine()->newString(QString::number(url.port()))); d()->hostname.set(engine(), engine()->newString(url.host())); d()->href.set(engine(), engine()->newString(url.toString())); updateOrigin(); updateHost(); return true; } bool UrlObject::setHref(QString href) { QUrl url(href); if (!url.isValid() || url.isRelative()) return false; d()->hash.set(engine(), engine()->newString(url.fragment())); d()->hostname.set(engine(), engine()->newString(url.host())); d()->href.set(engine(), engine()->newString(url.toString())); d()->password.set(engine(), engine()->newString(url.password())); d()->pathname.set(engine(), engine()->newString(url.path())); d()->port.set(engine(), engine()->newString(url.port() == -1 ? QLatin1String("") : QString::number(url.port()))); d()->protocol.set(engine(), engine()->newString(url.scheme())); d()->search.set(engine(), engine()->newString(url.query())); d()->username.set(engine(), engine()->newString(url.userName())); updateOrigin(); updateHost(); return true; } bool UrlObject::setPassword(QString password) { QUrl url = toQUrl(); url.setPassword(password); if (!url.isValid()) return false; d()->password.set(engine(), engine()->newString(url.password())); d()->href.set(engine(), engine()->newString(url.toString())); return true; } bool UrlObject::setPathname(QString pathname) { QUrl url = toQUrl(); url.setPath(pathname); if (!url.isValid()) return false; d()->pathname.set(engine(), engine()->newString(url.path())); d()->href.set(engine(), engine()->newString(url.toString())); return true; } bool UrlObject::setPort(QString port) { QUrl url = toQUrl(); url.setPort(port.isEmpty() ? -1 : port.toInt()); if (!url.isValid()) return false; d()->port.set(engine(), engine()->newString(url.port() == -1 ? QLatin1String("") : QString::number(url.port()))); d()->href.set(engine(), engine()->newString(url.toString())); updateOrigin(); updateHost(); return true; } bool UrlObject::setProtocol(QString protocol) { QUrl url = toQUrl(); url.setScheme(protocol); if (!url.isValid()) return false; d()->protocol.set(engine(), engine()->newString(url.scheme())); d()->href.set(engine(), engine()->newString(url.toString())); updateOrigin(); updateHost(); return true; } bool UrlObject::setSearch(QString search) { QUrl url = toQUrl(); if (search.startsWith(QLatin1Char('?'))) search = search.mid(1); url.setQuery(search); if (!url.isValid()) return false; d()->search.set(engine(), engine()->newString(url.query())); d()->href.set(engine(), engine()->newString(url.toString())); return true; } bool UrlObject::setUsername(QString username) { QUrl url = toQUrl(); url.setUserName(username); if (!url.isValid()) return false; d()->username.set(engine(), engine()->newString(url.userName())); d()->href.set(engine(), engine()->newString(url.toString())); return true; } QUrl UrlObject::toQUrl() const { return QUrl(href()); } void UrlObject::updateOrigin() { QUrl url = toQUrl(); QString proto = url.scheme(); // A blob's origin is the origin of the URL that it points to if (proto == QLatin1String("blob")) { url = QUrl(url.path()); proto = url.scheme(); } QString origin; if (proto == QLatin1String("http") || proto == QLatin1String("https") || proto == QLatin1String("ftp")) { origin = QLatin1String("%1://%2").arg(url.scheme(), url.host()); if (url.port() != -1) origin.append(QLatin1String(":") + QString::number(url.port())); } d()->origin.set(engine(), engine()->newString(origin)); } void UrlObject::updateHost() { QUrl url = toQUrl(); QString host = url.host(); if (url.port() != -1) host.append(QLatin1String(":") + QString::number(url.port())); d()->host.set(engine(), engine()->newString(host)); } ReturnedValue UrlPrototype::method_getHash(const FunctionObject *b, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = b->engine(); Scope scope(v4); Scoped r(scope, thisObject->as()); 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 r(scope, thisObject->as()); 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 r(scope, thisObject->as()); 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 r(scope, thisObject->as()); 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 r(scope, thisObject->as()); 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 r(scope, thisObject->as()); 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 r(scope, thisObject->as()); 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 r(scope, thisObject->as()); 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 r(scope, thisObject->as()); 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 r(scope, thisObject->as()); 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 r(scope, thisObject->as()); 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 r(scope, thisObject->as()); 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 r(scope, thisObject->as()); 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 r(scope, thisObject->as()); 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 r(scope, thisObject->as()); 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 r(scope, thisObject->as()); 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 r(scope, thisObject->as()); 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 r(scope, thisObject->as()); 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 r(scope, thisObject->as()); 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 r(scope, thisObject->as()); 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 r(scope, thisObject->as()); 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 r(scope, thisObject->as()); Scoped usp(scope, v4->newUrlSearchParamsObject()); 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(); if (!urlObject->setHref(urlString)) return v4->throwTypeError(QLatin1String("Invalid URL: %1").arg(urlString)); return obj->asReturnedValue(); } void Heap::UrlSearchParamsCtor::init(QV4::ExecutionContext *scope) { Heap::FunctionObject::init(scope, QLatin1String("URLSearchParams")); } void UrlSearchParamsPrototype::init(ExecutionEngine *engine, Object *ctor) { Q_UNUSED(ctor); Scope scope(engine); ScopedObject o(scope); defineDefaultProperty(QLatin1String("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(); Object *argObject = arg->as(); ReturnedValue o = Encode(v4->newUrlSearchParamsObject()); if (!newTarget) return o; ScopedObject obj(scope, o); obj->setProtoFromNewTarget(newTarget); UrlSearchParamsObject *urlSearchParamsObject = obj->as(); if (argArrayObject != nullptr) { ScopedArrayObject argArray(scope, argArrayObject); uint len = argArray->getLength(); for (uint i = 0; i < len; i++) { QV4::Value pair = argArray->get(i); auto *pairArrayObject = pair.as(); 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++) { QV4::Value pair = params->get(i); auto *pairArrayObject = pair.as(); QV4::Value key = pairArrayObject->get(uint(0)); QV4::Value value = 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()->d(); Heap::String *valStr = val->toString(engine()); append(nameStr, valStr); } } void UrlSearchParamsObject::setParams(QList params) { 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::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 UrlSearchParamsObject::params() const { auto *arrayObject = d()->params.get(); Scope scope(engine()); ScopedArrayObject scopedArray(scope, arrayObject); QList result; uint len = scopedArray->getLength(); for (uint i = 0; i < len; i++) { QV4::Value pair = scopedArray->get(i); auto *pairArrayObject = pair.as(); QV4::Value key = pairArrayObject->get(uint(0)); QV4::Value value = pairArrayObject->get(uint(1)); result << QStringList { key.toQString(), value.toQString() }; } return result; } 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++) { QV4::Value pair = scopedArray->get(i); auto *pairArrayObject = pair.as(); QV4::Value key = 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 = scopedArray->get(index); auto *pairArrayObject = pair.as(); QV4::Value value = 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 = scopedArray->get(index); auto *pairArrayObject = pair.as(); QV4::Value value = pairArrayObject->get(pairIndex); return value.as()->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); } struct UrlSearchParamsObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator { ~UrlSearchParamsObjectOwnPropertyKeyIterator() override = default; PropertyKey next(const QV4::Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override; }; PropertyKey UrlSearchParamsObjectOwnPropertyKeyIterator::next(const QV4::Object *o, Property *pd, PropertyAttributes *attrs) { const UrlSearchParamsObject *usp = static_cast(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(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); } ReturnedValue UrlSearchParamsPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = b->engine(); Scope scope(v4); Scoped o(scope, thisObject->as()); 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 o(scope, thisObject->as()); QList 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()); ScopedString value(scope, argValue->toString(v4)); Scoped o(scope, thisObject->as()); 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 o(scope, thisObject->as()); QList params = o->params(); auto to_remove = std::remove_if(params.begin(), params.end(), [&name](QStringList pair) { return pair[0] == name; }); params.erase(to_remove, params.end()); 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 o(scope, thisObject->as()); 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 o(scope, thisObject->as()); 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); 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 o(scope, thisObject->as()); 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 o(scope, thisObject->as()); 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(); if (func == nullptr) return v4->throwTypeError(QLatin1String("Invalid argument: must be a function")); Scoped o(scope, thisObject->as()); for (int i = 0; i < o->length(); i++) { Scoped name(scope, o->nameAtRaw(i)); Scoped value(scope, o->valueAtRaw(i)); QV4::JSCallData 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 o(scope, thisObject->as()); ScopedObject params(scope, o->d()->params.get()); Scoped 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 o(scope, thisObject->as()); ScopedObject keys(scope, o->d()->keys.get()); Scoped 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 o(scope, thisObject->as()); ScopedObject values(scope, o->d()->values.get()); Scoped valuesIterator(scope, v4->newArrayIteratorObject(values)); valuesIterator->d()->iterationKind = IteratorKind::KeyValueIteratorKind; return valuesIterator->asReturnedValue(); }