diff options
author | Maximilian Goldstein <[email protected]> | 2020-04-02 15:48:17 +0200 |
---|---|---|
committer | Maximilian Goldstein <[email protected]> | 2020-04-08 14:53:30 +0200 |
commit | faa3e0b41e12ad36bb45272dbcb3991fd99f3836 (patch) | |
tree | 98bc1937b82b514699fd64785df38fbce79ca9a0 | |
parent | 399ebb5635efc897d29efba90f92f931843b266a (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 <[email protected]>
-rw-r--r-- | src/qml/.prev_CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/qml/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/jsruntime.pri | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 15 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine_p.h | 9 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4global_p.h | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4managed.cpp | 3 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4managed_p.h | 1 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4urlobject.cpp | 689 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4urlobject_p.h | 214 | ||||
-rw-r--r-- | tests/auto/qml/qjsengine/tst_qjsengine.cpp | 1 | ||||
-rw-r--r-- | tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 191 |
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:[email protected]: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:[email protected]: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:[email protected]: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:[email protected]: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://[email protected]:1111/a/b/c"); + + // password + QVERIFY(EVALUATE("this.url.password = 'pword';")); + + QCOMPARE(url->password(), "pword"); + QCOMPARE(url->href(), "https://uname:[email protected]:1111/a/b/c"); + + // pathname + QVERIFY(EVALUATE("this.url.pathname = '/c/b/a';")); + + QCOMPARE(url->pathname(), "/c/b/a"); + QCOMPARE(url->href(), "https://uname:[email protected]:1111/c/b/a"); + + // search + QVERIFY(EVALUATE("this.url.search = '?key=test';")); + + QCOMPARE(url->search(), "?key=test"); + QCOMPARE(url->href(), "https://uname:[email protected]:1111/c/b/a?key=test"); + + // hash + QVERIFY(EVALUATE("this.url.hash = '#foo';")); + + QCOMPARE(url->hash(), "#foo"); + QCOMPARE(url->href(), "https://uname:[email protected]:1111/c/b/a?key=test#foo"); + + // href + QVERIFY(EVALUATE( + "this.url.href = " + "'https://username:[email protected]: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:[email protected]: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" |