aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaximilian Goldstein <max.goldstein@qt.io>2020-04-02 15:48:17 +0200
committerMaximilian Goldstein <max.goldstein@qt.io>2020-04-08 14:53:30 +0200
commitfaa3e0b41e12ad36bb45272dbcb3991fd99f3836 (patch)
tree98bc1937b82b514699fd64785df38fbce79ca9a0
parent399ebb5635efc897d29efba90f92f931843b266a (diff)
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>
-rw-r--r--src/qml/.prev_CMakeLists.txt1
-rw-r--r--src/qml/CMakeLists.txt1
-rw-r--r--src/qml/jsruntime/jsruntime.pri2
-rw-r--r--src/qml/jsruntime/qv4engine.cpp15
-rw-r--r--src/qml/jsruntime/qv4engine_p.h9
-rw-r--r--src/qml/jsruntime/qv4global_p.h1
-rw-r--r--src/qml/jsruntime/qv4managed.cpp3
-rw-r--r--src/qml/jsruntime/qv4managed_p.h1
-rw-r--r--src/qml/jsruntime/qv4urlobject.cpp689
-rw-r--r--src/qml/jsruntime/qv4urlobject_p.h214
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp1
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp191
12 files changed, 1128 insertions, 0 deletions
diff --git a/src/qml/.prev_CMakeLists.txt b/src/qml/.prev_CMakeLists.txt
index 147419711f..eea934fc64 100644
--- a/src/qml/.prev_CMakeLists.txt
+++ b/src/qml/.prev_CMakeLists.txt
@@ -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
diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt
index cb49452a26..df85a87c69 100644
--- a/src/qml/CMakeLists.txt
+++ b/src/qml/CMakeLists.txt
@@ -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
diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri
index 0c84886da4..adea21969d 100644
--- a/src/qml/jsruntime/jsruntime.pri
+++ b/src/qml/jsruntime/jsruntime.pri
@@ -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 \
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index d4f55f6e7f..55884aa42c 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -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());
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index e7ba6976e6..ebf2fcd55a 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -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);
diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h
index c6a737b467..7234a3456f 100644
--- a/src/qml/jsruntime/qv4global_p.h
+++ b/src/qml/jsruntime/qv4global_p.h
@@ -141,6 +141,7 @@ namespace Heap {
struct ArgumentsObject;
struct QObjectWrapper;
struct RegExpObject;
+ struct UrlObject;
struct RegExp;
struct EvalFunction;
diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp
index d51b03d90b..8a0e5509c4 100644
--- a/src/qml/jsruntime/qv4managed.cpp
+++ b/src/qml/jsruntime/qv4managed.cpp
@@ -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";
diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h
index 4f22dc7330..910eb3a5a1 100644
--- a/src/qml/jsruntime/qv4managed_p.h
+++ b/src/qml/jsruntime/qv4managed_p.h
@@ -144,6 +144,7 @@ public:
Type_JsonObject,
Type_MathObject,
Type_ProxyObject,
+ Type_UrlObject,
Type_ExecutionContext,
Type_InternalClass,
diff --git a/src/qml/jsruntime/qv4urlobject.cpp b/src/qml/jsruntime/qv4urlobject.cpp
new file mode 100644
index 0000000000..15e827680e
--- /dev/null
+++ b/src/qml/jsruntime/qv4urlobject.cpp
@@ -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();
+}
diff --git a/src/qml/jsruntime/qv4urlobject_p.h b/src/qml/jsruntime/qv4urlobject_p.h
new file mode 100644
index 0000000000..bc066818b6
--- /dev/null
+++ b/src/qml/jsruntime/qv4urlobject_p.h
@@ -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
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index 8c30e64a15..ae667ce361 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -1105,6 +1105,7 @@ void tst_QJSEngine::globalObjectProperties_enumerate()
<< "Proxy"
<< "Atomics"
<< "Promise"
+ << "URL"
;
QSet<QString> actualNames;
{
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 94a51f7f7b..a2a56fd04e 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -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"