Implement URL object

Implements the JavaScript URL object (https://url.spec.whatwg.org/#api).
Except that it does not currently implement the searchParams field.

Task-number: QTBUG-54988
Change-Id: I19abc69e075cbf84bd15e6791be195ce16f3fe73
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Maximilian Goldstein 2020-04-02 15:48:17 +02:00
parent 399ebb5635
commit faa3e0b41e
12 changed files with 1128 additions and 0 deletions

View File

@ -150,6 +150,7 @@ qt_add_module(Qml
jsruntime/qv4stringobject.cpp jsruntime/qv4stringobject_p.h
jsruntime/qv4symbol.cpp jsruntime/qv4symbol_p.h
jsruntime/qv4typedarray.cpp jsruntime/qv4typedarray_p.h
jsruntime/qv4urlobject.cpp jsruntime/qv4urlobject_p.h
jsruntime/qv4value.cpp jsruntime/qv4value_p.h
jsruntime/qv4variantobject.cpp jsruntime/qv4variantobject_p.h
jsruntime/qv4vme_moth.cpp jsruntime/qv4vme_moth_p.h

View File

@ -150,6 +150,7 @@ qt_add_module(Qml
jsruntime/qv4stringobject.cpp jsruntime/qv4stringobject_p.h
jsruntime/qv4symbol.cpp jsruntime/qv4symbol_p.h
jsruntime/qv4typedarray.cpp jsruntime/qv4typedarray_p.h
jsruntime/qv4urlobject.cpp jsruntime/qv4urlobject_p.h
jsruntime/qv4value.cpp jsruntime/qv4value_p.h
jsruntime/qv4variantobject.cpp jsruntime/qv4variantobject_p.h
jsruntime/qv4vme_moth.cpp jsruntime/qv4vme_moth_p.h

View File

@ -59,6 +59,7 @@ SOURCES += \
$$PWD/qv4module.cpp \
$$PWD/qv4promiseobject.cpp \
$$PWD/qv4runtime.cpp \
$$PWD/qv4urlobject.cpp \
$$PWD/qv4value.cpp \
$$PWD/qv4compilationunitmapper.cpp \
$$PWD/qv4executablecompilationunit.cpp \
@ -135,6 +136,7 @@ HEADERS += \
$$PWD/qv4module_p.h \
$$PWD/qv4promiseobject_p.h \
$$PWD/qv4runtime_p.h \
$$PWD/qv4urlobject_p.h \
$$PWD/qv4value_p.h \
$$PWD/qv4compilationunitmapper_p.h \
$$PWD/qv4executablecompilationunit_p.h \

View File

@ -91,6 +91,7 @@
#include "qv4proxy_p.h"
#include "qv4stackframe_p.h"
#include "qv4atomics_p.h"
#include "qv4urlobject_p.h"
#if QT_CONFIG(qml_sequence_object)
#include "qv4sequenceobject_p.h"
@ -650,6 +651,13 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
ic = newInternalClass(StringIteratorPrototype::staticVTable(), iteratorPrototype());
jsObjects[StringIteratorProto] = memoryManager->allocObject<StringIteratorPrototype>(ic);
//
// url
//
jsObjects[Url_Ctor] = memoryManager->allocate<UrlCtor>(global);
jsObjects[UrlProto] = memoryManager->allocate<UrlPrototype>();
str = newString(QStringLiteral("get [Symbol.species]"));
jsObjects[GetSymbolSpecies] = FunctionObject::createBuiltinFunction(this, str, ArrayPrototype::method_get_species, 0);
@ -671,6 +679,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
static_cast<SyntaxErrorPrototype *>(syntaxErrorPrototype())->init(this, syntaxErrorCtor());
static_cast<TypeErrorPrototype *>(typeErrorPrototype())->init(this, typeErrorCtor());
static_cast<URIErrorPrototype *>(uRIErrorPrototype())->init(this, uRIErrorCtor());
static_cast<UrlPrototype *>(urlPrototype())->init(this, urlCtor());
static_cast<IteratorPrototype *>(iteratorPrototype())->init(this);
static_cast<ForInIteratorPrototype *>(forInIteratorPrototype())->init(this);
@ -760,6 +769,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
globalObject->defineDefaultProperty(QStringLiteral("TypeError"), *typeErrorCtor());
globalObject->defineDefaultProperty(QStringLiteral("URIError"), *uRIErrorCtor());
globalObject->defineDefaultProperty(QStringLiteral("Promise"), *promiseCtor());
globalObject->defineDefaultProperty(QStringLiteral("URL"), *urlCtor());
globalObject->defineDefaultProperty(QStringLiteral("SharedArrayBuffer"), *sharedArrayBufferCtor());
globalObject->defineDefaultProperty(QStringLiteral("ArrayBuffer"), *arrayBufferCtor());
@ -1049,6 +1059,11 @@ Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegularExpression &r
}
#endif
Heap::UrlObject *ExecutionEngine::newUrlObject()
{
return memoryManager->allocate<UrlObject>();
}
Heap::Object *ExecutionEngine::newErrorObject(const Value &value)
{
return ErrorObject::create<ErrorObject>(this, value, errorCtor());

View File

@ -240,6 +240,7 @@ public:
MapIteratorProto,
ArrayIteratorProto,
StringIteratorProto,
UrlProto,
Object_Ctor,
String_Ctor,
@ -267,6 +268,7 @@ public:
WeakMap_Ctor,
Map_Ctor,
IntrinsicTypedArray_Ctor,
Url_Ctor,
GetSymbolSpecies,
@ -307,6 +309,10 @@ public:
FunctionObject *weakMapCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + WeakMap_Ctor); }
FunctionObject *mapCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Map_Ctor); }
FunctionObject *intrinsicTypedArrayCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + IntrinsicTypedArray_Ctor); }
FunctionObject *urlCtor() const
{
return reinterpret_cast<FunctionObject *>(jsObjects + Url_Ctor);
}
FunctionObject *typedArrayCtors;
FunctionObject *getSymbolSpecies() const { return reinterpret_cast<FunctionObject *>(jsObjects + GetSymbolSpecies); }
@ -354,6 +360,7 @@ public:
Object *mapIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + MapIteratorProto); }
Object *arrayIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayIteratorProto); }
Object *stringIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + StringIteratorProto); }
Object *urlPrototype() const { return reinterpret_cast<Object *>(jsObjects + UrlProto); }
EvalFunction *evalFunction() const { return reinterpret_cast<EvalFunction *>(jsObjects + Eval_Function); }
FunctionObject *getStackFunction() const { return reinterpret_cast<FunctionObject *>(jsObjects + GetStack_Function); }
@ -583,6 +590,8 @@ public:
Heap::RegExpObject *newRegExpObject(const QRegularExpression &re);
#endif
Heap::UrlObject *newUrlObject();
Heap::Object *newErrorObject(const Value &value);
Heap::Object *newErrorObject(const QString &message);
Heap::Object *newSyntaxErrorObject(const QString &message, const QString &fileName, int line, int column);

View File

@ -141,6 +141,7 @@ namespace Heap {
struct ArgumentsObject;
struct QObjectWrapper;
struct RegExpObject;
struct UrlObject;
struct RegExp;
struct EvalFunction;

View File

@ -105,6 +105,9 @@ QString Managed::className() const
case Type_MathObject:
s = "Math";
break;
case Type_UrlObject:
s = "URL";
break;
case Type_ExecutionContext:
s = "__ExecutionContext";

View File

@ -144,6 +144,7 @@ public:
Type_JsonObject,
Type_MathObject,
Type_ProxyObject,
Type_UrlObject,
Type_ExecutionContext,
Type_InternalClass,

View File

@ -0,0 +1,689 @@
/****************************************************************************
**
** 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 "qv4urlobject_p.h"
#include <QtCore/QUrl>
using namespace QV4;
DEFINE_OBJECT_VTABLE(UrlObject);
DEFINE_OBJECT_VTABLE(UrlCtor);
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("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<UrlObject> r(scope, thisObject->as<UrlObject>());
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->as<UrlObject>());
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->as<UrlObject>());
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->as<UrlObject>());
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->as<UrlObject>());
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->as<UrlObject>());
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->as<UrlObject>());
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->as<UrlObject>());
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->as<UrlObject>());
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->as<UrlObject>());
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->as<UrlObject>());
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->as<UrlObject>());
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->as<UrlObject>());
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->as<UrlObject>());
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->as<UrlObject>());
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->as<UrlObject>());
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->as<UrlObject>());
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->as<UrlObject>());
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->as<UrlObject>());
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->as<UrlObject>());
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->as<UrlObject>());
r->setUsername(stringValue->toQString());
return Encode::undefined();
}
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]);
String *arg1StringValue = arg1->stringValue();
if (arg1StringValue == nullptr)
return v4->throwTypeError(QLatin1String("Invalid parameter provided"));
QString arg1String = arg1StringValue->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();
}

View File

@ -0,0 +1,214 @@
/****************************************************************************
**
** 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$
**
****************************************************************************/
#ifndef QV4URLOBJECT_P_H
#define QV4URLOBJECT_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qv4object_p.h"
#include "qv4functionobject_p.h"
#include <QtCore/QString>
#include <QtCore/QUrl>
QT_BEGIN_NAMESPACE
namespace QV4 {
namespace Heap {
// clang-format off
#define UrlObjectMembers(class, Member) \
Member(class, Pointer, String *, hash) \
Member(class, Pointer, String *, host) \
Member(class, Pointer, String *, hostname) \
Member(class, Pointer, String *, href) \
Member(class, Pointer, String *, origin) \
Member(class, Pointer, String *, password) \
Member(class, Pointer, String *, pathname) \
Member(class, Pointer, String *, port) \
Member(class, Pointer, String *, protocol) \
Member(class, Pointer, String *, search) \
Member(class, Pointer, String *, username)
// clang-format on
DECLARE_HEAP_OBJECT(UrlObject, Object)
{
DECLARE_MARKOBJECTS(UrlObject);
void init() { Object::init(); }
};
struct UrlCtor : FunctionObject
{
void init(QV4::ExecutionContext *scope);
};
}
struct UrlObject : Object
{
V4_OBJECT2(UrlObject, Object)
Q_MANAGED_TYPE(UrlObject)
V4_PROTOTYPE(urlPrototype)
QString hash() const { return QLatin1String("#") + d()->hash->toQString(); }
bool setHash(QString hash);
QString host() const { return d()->host->toQString(); }
bool setHost(QString host);
QString hostname() const { return d()->hostname->toQString(); }
bool setHostname(QString hostname);
QString href() const { return d()->href->toQString(); }
bool setHref(QString href);
QString origin() const { return d()->origin->toQString(); }
QString password() const { return d()->password->toQString(); }
bool setPassword(QString password);
QString pathname() const { return d()->pathname->toQString(); }
bool setPathname(QString pathname);
QString port() const { return d()->port->toQString(); }
bool setPort(QString port);
QString protocol() const { return d()->protocol->toQString(); }
bool setProtocol(QString protocol);
QString search() const { return QLatin1String("?") + d()->search->toQString(); }
bool setSearch(QString search);
QString username() const { return d()->username->toQString(); }
bool setUsername(QString username);
private:
QUrl toQUrl() const;
void updateOrigin();
void updateHost();
};
template<>
inline const UrlObject *Value::as() const
{
return isManaged() && m()->internalClass->vtable->type == Managed::Type_UrlObject
? static_cast<const UrlObject *>(this)
: nullptr;
}
struct UrlCtor : FunctionObject
{
V4_OBJECT2(UrlCtor, FunctionObject)
static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv,
int argc, const Value *);
};
struct UrlPrototype : Object
{
V4_PROTOTYPE(objectPrototype)
void init(ExecutionEngine *engine, Object *ctor);
static ReturnedValue method_getHash(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
static ReturnedValue method_setHash(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
static ReturnedValue method_getHost(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
static ReturnedValue method_setHost(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
static ReturnedValue method_getHostname(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
static ReturnedValue method_setHostname(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
static ReturnedValue method_getHref(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
static ReturnedValue method_setHref(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
static ReturnedValue method_getOrigin(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
static ReturnedValue method_getPassword(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
static ReturnedValue method_setPassword(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
static ReturnedValue method_getPathname(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
static ReturnedValue method_setPathname(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
static ReturnedValue method_getPort(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
static ReturnedValue method_setPort(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
static ReturnedValue method_getProtocol(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
static ReturnedValue method_setProtocol(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
static ReturnedValue method_getSearch(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
static ReturnedValue method_setSearch(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
static ReturnedValue method_getUsername(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
static ReturnedValue method_setUsername(const FunctionObject *, const Value *thisObject,
const Value *argv, int argc);
};
}
QT_END_NAMESPACE
#endif // QV4URLOBJECT_P_H

View File

@ -1105,6 +1105,7 @@ void tst_QJSEngine::globalObjectProperties_enumerate()
<< "Proxy"
<< "Atomics"
<< "Promise"
<< "URL"
;
QSet<QString> actualNames;
{

View File

@ -47,6 +47,7 @@
#include <private/qv4alloca_p.h>
#include <private/qv4runtime_p.h>
#include <private/qv4object_p.h>
#include <private/qv4urlobject_p.h>
#include <private/qv4script_p.h>
#include <private/qqmlcomponentattached_p.h>
#include <private/qv4objectiterator_p.h>
@ -382,6 +383,9 @@ private slots:
void semicolonAfterProperty();
void hugeStack();
void bindingOnQProperty();
void urlConstruction();
void urlPropertyInvalid();
void urlPropertySet();
void gcCrashRegressionTest();
@ -9282,6 +9286,193 @@ void tst_qqmlecmascript::bindingOnQProperty()
QVERIFY(qprop.hasBinding());
}
void tst_qqmlecmascript::urlConstruction()
{
QQmlEngine qmlengine;
QObject *o = new QObject(&qmlengine);
QV4::ExecutionEngine *engine = qmlengine.handle();
QV4::Scope scope(engine);
QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine, o));
// Invalid number of arguments
QVERIFY(EVALUATE_ERROR("new URL()"));
QVERIFY(EVALUATE_ERROR("new URL('a', 'b', 'c')"));
// Invalid arguments
QVERIFY(EVALUATE_ERROR("new URL(null)"));
QVERIFY(EVALUATE_ERROR("new URL('a', null)"));
// Invalid URL
QVERIFY(EVALUATE_ERROR("new URL('thisisnotaurl')"));
// Valid URL
QV4::ScopedValue ret(scope,
EVALUATE("new "
"URL('https://username:password@example.com:1234/path/to/"
"something?search=value#hash')"));
QV4::UrlObject *validUrl = ret->as<QV4::UrlObject>();
QVERIFY(validUrl != nullptr);
QCOMPARE(validUrl->protocol(), "https");
QCOMPARE(validUrl->hostname(), "example.com");
QCOMPARE(validUrl->username(), "username");
QCOMPARE(validUrl->password(), "password");
QCOMPARE(validUrl->port(), "1234");
QCOMPARE(validUrl->host(), "example.com:1234");
QCOMPARE(validUrl->origin(), "https://example.com:1234");
QCOMPARE(validUrl->href(),
"https://username:password@example.com:1234/path/to/something?search=value#hash");
QCOMPARE(validUrl->pathname(), "/path/to/something");
QCOMPARE(validUrl->search(), "?search=value");
QCOMPARE(validUrl->hash(), "#hash");
// Valid relative URL
QV4::ScopedValue retRel(scope,
EVALUATE("new URL('/path/to/something?search=value#hash', "
"'https://username:password@example.com:1234')"));
QV4::UrlObject *validRelativeUrl = retRel->as<QV4::UrlObject>();
QVERIFY(validRelativeUrl != nullptr);
QCOMPARE(validRelativeUrl->protocol(), "https");
QCOMPARE(validRelativeUrl->hostname(), "example.com");
QCOMPARE(validRelativeUrl->username(), "username");
QCOMPARE(validRelativeUrl->password(), "password");
QCOMPARE(validRelativeUrl->port(), "1234");
QCOMPARE(validRelativeUrl->host(), "example.com:1234");
QCOMPARE(validRelativeUrl->origin(), "https://example.com:1234");
QCOMPARE(validRelativeUrl->href(),
"https://username:password@example.com:1234/path/to/something?search=value#hash");
QCOMPARE(validRelativeUrl->pathname(), "/path/to/something");
QCOMPARE(validRelativeUrl->search(), "?search=value");
QCOMPARE(validRelativeUrl->hash(), "#hash");
}
void tst_qqmlecmascript::urlPropertyInvalid()
{
QQmlEngine qmlengine;
QObject *o = new QObject(&qmlengine);
QV4::ExecutionEngine *engine = qmlengine.handle();
QV4::Scope scope(engine);
QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine, o));
// Try invalid values on all settable properties
QVERIFY(EVALUATE_ERROR("new URL('https://localhost').hash = null;"));
QVERIFY(EVALUATE_ERROR("new URL('https://localhost').hostname = null;"));
QVERIFY(EVALUATE_ERROR("new URL('https://localhost').href = null;"));
QVERIFY(EVALUATE_ERROR("new URL('https://localhost').password = null;"));
QVERIFY(EVALUATE_ERROR("new URL('https://localhost').pathname = null;"));
QVERIFY(EVALUATE_ERROR("new URL('https://localhost').port = null;"));
QVERIFY(EVALUATE_ERROR("new URL('https://localhost').protocol = null;"));
QVERIFY(EVALUATE_ERROR("new URL('https://localhost').search = null;"));
QVERIFY(EVALUATE_ERROR("new URL('https://localhost').hash = null;"));
// Make sure that origin does not change after trying to set it
QVERIFY(EVALUATE_VALUE("(function() { var url = new URL('https://localhost'); url.origin = "
"'http://example.com'; return url.origin;})()",
QV4::ScopedValue(scope, scope.engine->newString("https://localhost"))));
}
void tst_qqmlecmascript::urlPropertySet()
{
QQmlEngine qmlengine;
QObject *o = new QObject(&qmlengine);
QV4::ExecutionEngine *engine = qmlengine.handle();
QV4::Scope scope(engine);
QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine, o));
QV4::ScopedValue ret(scope, EVALUATE("this.url = new URL('http://localhost/a/b/c');"));
QV4::UrlObject *url = ret->as<QV4::UrlObject>();
QVERIFY(url != nullptr);
// protocol
QVERIFY(EVALUATE("this.url.protocol = 'https';"));
QCOMPARE(url->protocol(), "https");
QCOMPARE(url->href(), "https://localhost/a/b/c");
QCOMPARE(url->origin(), "https://localhost");
// port
QVERIFY(EVALUATE("this.url.port = 4567;"));
QCOMPARE(url->port(), "4567");
QCOMPARE(url->href(), "https://localhost:4567/a/b/c");
QCOMPARE(url->host(), "localhost:4567");
QCOMPARE(url->origin(), "https://localhost:4567");
// hostname
QVERIFY(EVALUATE("this.url.hostname = 'foobar.com';"));
QCOMPARE(url->hostname(), "foobar.com");
QCOMPARE(url->href(), "https://foobar.com:4567/a/b/c");
QCOMPARE(url->origin(), "https://foobar.com:4567");
QCOMPARE(url->host(), "foobar.com:4567");
// host
QVERIFY(EVALUATE("this.url.host = 'test.com:1111';"));
QCOMPARE(url->host(), "test.com:1111");
QCOMPARE(url->hostname(), "test.com");
QCOMPARE(url->href(), "https://test.com:1111/a/b/c");
QCOMPARE(url->origin(), "https://test.com:1111");
// username
QVERIFY(EVALUATE("this.url.username = 'uname';"));
QCOMPARE(url->username(), "uname");
QCOMPARE(url->href(), "https://uname@test.com:1111/a/b/c");
// password
QVERIFY(EVALUATE("this.url.password = 'pword';"));
QCOMPARE(url->password(), "pword");
QCOMPARE(url->href(), "https://uname:pword@test.com:1111/a/b/c");
// pathname
QVERIFY(EVALUATE("this.url.pathname = '/c/b/a';"));
QCOMPARE(url->pathname(), "/c/b/a");
QCOMPARE(url->href(), "https://uname:pword@test.com:1111/c/b/a");
// search
QVERIFY(EVALUATE("this.url.search = '?key=test';"));
QCOMPARE(url->search(), "?key=test");
QCOMPARE(url->href(), "https://uname:pword@test.com:1111/c/b/a?key=test");
// hash
QVERIFY(EVALUATE("this.url.hash = '#foo';"));
QCOMPARE(url->hash(), "#foo");
QCOMPARE(url->href(), "https://uname:pword@test.com:1111/c/b/a?key=test#foo");
// href
QVERIFY(EVALUATE(
"this.url.href = "
"'https://username:password@example.com:1234/path/to/something?search=value#hash';"));
QCOMPARE(url->protocol(), "https");
QCOMPARE(url->hostname(), "example.com");
QCOMPARE(url->username(), "username");
QCOMPARE(url->password(), "password");
QCOMPARE(url->port(), "1234");
QCOMPARE(url->host(), "example.com:1234");
QCOMPARE(url->origin(), "https://example.com:1234");
QCOMPARE(url->href(),
"https://username:password@example.com:1234/path/to/something?search=value#hash");
QCOMPARE(url->pathname(), "/path/to/something");
QCOMPARE(url->search(), "?search=value");
QCOMPARE(url->hash(), "#hash");
}
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"