diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index aafc50db9b77477bd15103f6c591834f73ed0005..9b2926ec7ec4ef92f25c2b5529cb46bed96f8c68 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -51,6 +51,7 @@ SOURCES += \ $$PWD/qqmlmemoryprofiler.cpp \ $$PWD/qqmlplatform.cpp \ $$PWD/qqmlbinding.cpp \ + $$PWD/qqmlabstracturlinterceptor.cpp \ $$PWD/qqmlapplicationengine.cpp HEADERS += \ @@ -123,6 +124,7 @@ HEADERS += \ $$PWD/qqmlplatform_p.h \ $$PWD/qqmlbinding_p.h \ $$PWD/qqmlextensionplugin_p.h \ + $$PWD/qqmlabstracturlinterceptor_p.h \ $$PWD/qqmlapplicationengine_p.h \ $$PWD/qqmlapplicationengine.h diff --git a/src/qml/qml/qqmlabstracturlinterceptor.cpp b/src/qml/qml/qqmlabstracturlinterceptor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a68d5f7489cdb84f38e69b35e3539507323926fb --- /dev/null +++ b/src/qml/qml/qqmlabstracturlinterceptor.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Research In Motion. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QQmlAbstractUrlInterceptor + \inmodule QtQml + \brief allows you to control QML file loading. + + \note This class is in an extended validation period and still subject to change. It should be treated as private API for 5.1 + + QQmlAbstractUrlInterceptor is an interface which can be used to alter URLs + before they are used by the QML engine. This is primarily useful for altering + file urls into other file urls, such as selecting different graphical assets + for the current platform. + + Relative URLs are intercepted after being resolved against the file path of the + current QML context. URL interception also occurs after setting the base path for + a loaded QML file. This means that the content loaded for that QML file uses the + intercepted URL, but inside the file the pre-intercepted URL is used for resolving + relative paths. This allows for interception of .qml file loading without needing + all paths (or local types) inside intercepted content to insert a different relative path. + + Compared to setNetworkAccessManagerFactory, QQmlAbstractUrlInterceptor affects all URLs + and paths, including local files and embedded resource files. QQmlAbstractUrlInterceptor + is synchronous, and for asynchronous files must return a url with an asynchronous scheme + (such as http or a custom scheme handled by your own custom QNetworkAccessManager). You + can use a QQmlAbstractUrlInterceptor to change file URLs into networked URLs which are + handled by your own custom QNetworkAccessManager. + + To implement support for a custom networked scheme, see setNetworkAccessManagerFactory. +*/ + +/* + \enum QQmlAbstractUrlInterceptor::DataType + + Specifies where URL interception is taking place place. + + Because QML loads qmldir files for locating types, there are two URLs involved in loading a QML type. The URL of the (possibly implicit) qmldir used for locating the type and the URL of the file which defines the type. Intercepting + both leads to either complex URL replacement or double URL replacements for the same file. + + \value QmldirFile The URL being intercepted is for a Qmldir file. Intercepting this, but not the QmlFile, allows for swapping out entire sub trees. + \value JavaScriptFile The URL being intercepted is an import for a Javascript file. + \value QmlFile The URL being intercepted is for a Qml file. Intercepting this, but not the Qmldir file, leaves the base dir of a QML file untouched and acts like replacing the file with another file. + \value UrlString The URL being intercepted is a url property in a QML file, and not being used to load a file through the engine. + +*/ + +/*! + \fn QUrl QQmlAbstractUrlInterceptor::intercept(const QUrl& url, DataType type) + + A pure virtual function where you can intercept the url. The returned value is taken as the + new value for the url. The type of url being intercepted is given by the type variable. +*/ diff --git a/src/qml/qml/qqmlabstracturlinterceptor_p.h b/src/qml/qml/qqmlabstracturlinterceptor_p.h new file mode 100644 index 0000000000000000000000000000000000000000..186d59e3018062e79799200f694857c2e9c98de7 --- /dev/null +++ b/src/qml/qml/qqmlabstracturlinterceptor_p.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Research In Motion. +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +//Private API for 5.1 (at least) +#ifndef QQMLABSTRACTURLINTERCEPTOR_H +#define QQMLABSTRACTURLINTERCEPTOR_H + +#include <QtCore/qurl.h> + +QT_BEGIN_NAMESPACE + +class Q_QML_EXPORT QQmlAbstractUrlInterceptor +{ + Q_FLAGS(InterceptionPoint) +public: + enum DataType { //Matches QQmlDataBlob::Type + QmlFile = 0, + JavaScriptFile = 1, + QmldirFile = 2, + UrlString = 0x1000 + }; + + QQmlAbstractUrlInterceptor() {} + virtual ~QQmlAbstractUrlInterceptor() {} + virtual QUrl intercept(const QUrl &path, DataType type) = 0; +}; + +QT_END_NAMESPACE +#endif diff --git a/src/qml/qml/qqmlcompiler.cpp b/src/qml/qml/qqmlcompiler.cpp index 6951c8c3879bb95d0db66fce94c91c2cb3618bef..7b27a4c314c12219bb5f3608e091f3729dfc8cfa 100644 --- a/src/qml/qml/qqmlcompiler.cpp +++ b/src/qml/qml/qqmlcompiler.cpp @@ -60,6 +60,7 @@ #include "qqmlscriptstring.h" #include "qqmlglobal_p.h" #include "qqmlbinding_p.h" +#include "qqmlabstracturlinterceptor_p.h" #include <private/qv4compiler_p.h> #include <QDebug> @@ -517,6 +518,10 @@ void QQmlCompiler::genLiteralAssignment(QQmlScript::Property *prop, // Encoded dir-separators defeat QUrl processing - decode them first string.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive); QUrl u = string.isEmpty() ? QUrl() : output->url.resolved(QUrl(string)); + // Apply URL interceptor + if (engine->urlInterceptor()) + u = engine->urlInterceptor()->intercept(u, + QQmlAbstractUrlInterceptor::UrlString); instr.propertyIndex = prop->index; instr.value = output->indexForUrl(u); output->addInstruction(instr); diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp index e0a16d1f448270794f6b2bcd735f683215aad5a3..7fa2472335d8d8e9e56fe830801b8e81bef5665a 100644 --- a/src/qml/qml/qqmlcontext.cpp +++ b/src/qml/qml/qqmlcontext.cpp @@ -48,6 +48,7 @@ #include "qqmlengine_p.h" #include "qqmlengine.h" #include "qqmlinfo.h" +#include "qqmlabstracturlinterceptor_p.h" #include <private/qv4bindings_p.h> #include <private/qv8bindings_p.h> @@ -431,6 +432,7 @@ QUrl QQmlContextData::resolvedUrl(const QUrl &src) { QQmlContextData *ctxt = this; + QUrl resolved; if (src.isRelative() && !src.isEmpty()) { if (ctxt) { while(ctxt) { @@ -441,14 +443,20 @@ QUrl QQmlContextData::resolvedUrl(const QUrl &src) } if (ctxt) - return ctxt->url.resolved(src); + resolved = ctxt->url.resolved(src); else if (engine) - return engine->baseUrl().resolved(src); + resolved = engine->baseUrl().resolved(src); } - return QUrl(); } else { - return src; + resolved = src; } + + if (resolved.isEmpty()) //relative but no ctxt + return resolved; + + if (engine && engine->urlInterceptor()) + resolved = engine->urlInterceptor()->intercept(resolved, QQmlAbstractUrlInterceptor::UrlString); + return resolved; } diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 1c3aff68923768d9258c60af163e6981de836a57..9d2ad8c4c367dc22fe59461c4626ec5ac8a71751 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -66,6 +66,7 @@ #include <private/qv8debugservice_p.h> #include <private/qdebugmessageservice_p.h> #include "qqmlincubator.h" +#include "qqmlabstracturlinterceptor_p.h" #include <private/qv8profilerservice_p.h> #include <private/qqmlboundsignal_p.h> @@ -509,7 +510,7 @@ QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e) outputWarningsToStdErr(true), sharedContext(0), sharedScope(0), cleanup(0), erroredBindings(0), inProgressCreations(0), workerScriptEngine(0), activeVME(0), - networkAccessManager(0), networkAccessManagerFactory(0), + networkAccessManager(0), networkAccessManagerFactory(0), urlInterceptor(0), scarceResourcesRefCount(0), typeLoader(e), importDatabase(e), uniqueId(1), incubatorCount(0), incubationController(0), mutex(QMutex::Recursive) { @@ -924,6 +925,35 @@ QQmlContext *QQmlEngine::rootContext() const return d->rootContext; } +/*! + \internal + This API is private for 5.1 + + Sets the \a urlInterceptor to be used when resolving URLs in QML. + This also applies to URLs used for loading script files and QML types. + This should not be modifed while the engine is loading files, or URL + selection may be inconsistent. +*/ +void QQmlEngine::setUrlInterceptor(QQmlAbstractUrlInterceptor *urlInterceptor) +{ + Q_D(QQmlEngine); + d->urlInterceptor = urlInterceptor; +} + +/*! + \internal + This API is private for 5.1 + + Returns the current QQmlAbstractUrlInterceptor. It must not be modified outside + the GUI thread. +*/ +QQmlAbstractUrlInterceptor *QQmlEngine::urlInterceptor() const +{ + Q_D(const QQmlEngine); + return d->urlInterceptor; +} + + /*! Sets the \a factory to use for creating QNetworkAccessManager(s). diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h index 45826a4a67b05d5ef312af9f707490b8bbb874c7..ab25e062355aaa61c45f0353c95432518ff17989 100644 --- a/src/qml/qml/qqmlengine.h +++ b/src/qml/qml/qqmlengine.h @@ -51,6 +51,7 @@ QT_BEGIN_NAMESPACE +class QQmlAbstractUrlInterceptor; class Q_QML_EXPORT QQmlImageProviderBase { @@ -119,6 +120,9 @@ public: QNetworkAccessManager *networkAccessManager() const; + void setUrlInterceptor(QQmlAbstractUrlInterceptor* urlInterceptor); + QQmlAbstractUrlInterceptor* urlInterceptor() const; + void addImageProvider(const QString &id, QQmlImageProviderBase *); QQmlImageProviderBase *imageProvider(const QString &id) const; void removeImageProvider(const QString &id); diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index b5af0bb7cdea38bf9f493c200795b41cc2810219..b745d6a96398df099773bda4863074d97cf249cd 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -173,6 +173,8 @@ public: QHash<QString,QSharedPointer<QQmlImageProviderBase> > imageProviders; + QQmlAbstractUrlInterceptor* urlInterceptor; + // Scarce resources are "exceptionally high cost" QVariant types where allowing the // normal JavaScript GC to clean them up is likely to lead to out-of-memory or other // out-of-resource situations. When such a resource is passed into JavaScript we diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index bbce8e625e97b2a784a6c301ecf29a2cfd820591..8d8503f34412573967de0c084f3ac6876800ea9b 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qqmltypeloader_p.h" +#include "qqmlabstracturlinterceptor_p.h" #include <private/qqmlengine_p.h> #include <private/qqmlglobal_p.h> @@ -249,6 +250,23 @@ QQmlDataBlob::~QQmlDataBlob() cancelAllWaitingFor(); } +/*! + Sets the manager, and does stuff like selection which needs access to the manager. + Must be called before loading can occur. +*/ +void QQmlDataBlob::startLoading(QQmlDataLoader *manager) +{ + Q_ASSERT(status() == QQmlDataBlob::Null); + Q_ASSERT(m_manager == 0); + m_data.setStatus(QQmlDataBlob::Loading); + m_manager = manager; + + //Set here because we need to get the engine from the manager + if (manager && manager->engine() && manager->engine()->urlInterceptor()) + m_url = manager->engine()->urlInterceptor()->intercept(m_url, + (QQmlAbstractUrlInterceptor::DataType)m_type); +} + /*! Returns the type provided to the constructor. */ @@ -892,12 +910,7 @@ void QQmlDataLoader::load(QQmlDataBlob *blob, Mode mode) qWarning("QQmlDataLoader::load(%s): %s thread", qPrintable(blob->m_url.toString()), m_thread->isThisThread()?"Compile":"Engine"); #endif - - Q_ASSERT(blob->status() == QQmlDataBlob::Null); - Q_ASSERT(blob->m_manager == 0); - - blob->m_data.setStatus(QQmlDataBlob::Loading); - blob->m_manager = this; + blob->startLoading(this); if (m_thread->isThisThread()) { unlock(); @@ -930,11 +943,7 @@ void QQmlDataLoader::loadWithStaticData(QQmlDataBlob *blob, const QByteArray &da m_thread->isThisThread()?"Compile":"Engine"); #endif - Q_ASSERT(blob->status() == QQmlDataBlob::Null); - Q_ASSERT(blob->m_manager == 0); - - blob->m_data.setStatus(QQmlDataBlob::Loading); - blob->m_manager = this; + blob->startLoading(this); if (m_thread->isThisThread()) { unlock(); diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 68b8f33f889ec0224c30d8aed1a687ca8f1d3745..1bd07661f7d228fdeeb458886425193df4a76e6c 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -68,6 +68,7 @@ #include <private/qqmldirparser_p.h> #include <private/qqmlbundle_p.h> #include <private/qflagpointer_p.h> +#include <private/qqmlabstracturlinterceptor_p.h> QT_BEGIN_NAMESPACE @@ -92,15 +93,17 @@ public: Error // Error }; - enum Type { - QmlFile, - JavaScriptFile, - QmldirFile + enum Type { //Matched in QQmlAbstractUrlInterceptor + QmlFile = QQmlAbstractUrlInterceptor::QmlFile, + JavaScriptFile = QQmlAbstractUrlInterceptor::JavaScriptFile, + QmldirFile = QQmlAbstractUrlInterceptor::QmldirFile }; QQmlDataBlob(const QUrl &, Type); virtual ~QQmlDataBlob(); + void startLoading(QQmlDataLoader* manager); + Type type() const; Status status() const; diff --git a/tests/auto/qml/qqmlengine/data/interception/qmldir/Intercepted.qml b/tests/auto/qml/qqmlengine/data/interception/qmldir/Intercepted.qml new file mode 100644 index 0000000000000000000000000000000000000000..0331a01ad41ae69d3e5f514a2ecfd5efcc9975a7 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/qmldir/Intercepted.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + property string myStr: "intercepted" +} diff --git a/tests/auto/qml/qqmlengine/data/interception/qmldir/intercepted.js b/tests/auto/qml/qqmlengine/data/interception/qmldir/intercepted.js new file mode 100644 index 0000000000000000000000000000000000000000..6f54ebcdc56863d17938637fbce1b9373acdfbe0 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/qmldir/intercepted.js @@ -0,0 +1 @@ +var myStr = "base file" diff --git a/tests/auto/qml/qqmlengine/data/interception/qmldir/intercepted/Intercepted.qml b/tests/auto/qml/qqmlengine/data/interception/qmldir/intercepted/Intercepted.qml new file mode 100644 index 0000000000000000000000000000000000000000..ef5c28f87ba9d16517df7a07d1cc0792f89c9b3d --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/qmldir/intercepted/Intercepted.qml @@ -0,0 +1,6 @@ +import QtQml 2.0 + +QtObject { + property string myStr: "intercepted" + property Intercepted2 compilationIsTest: Intercepted2{} +} diff --git a/tests/auto/qml/qqmlengine/data/interception/qmldir/intercepted/Intercepted2.qml b/tests/auto/qml/qqmlengine/data/interception/qmldir/intercepted/Intercepted2.qml new file mode 100644 index 0000000000000000000000000000000000000000..0331a01ad41ae69d3e5f514a2ecfd5efcc9975a7 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/qmldir/intercepted/Intercepted2.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + property string myStr: "intercepted" +} diff --git a/tests/auto/qml/qqmlengine/data/interception/qmldir/intercepted/intercepted.js b/tests/auto/qml/qqmlengine/data/interception/qmldir/intercepted/intercepted.js new file mode 100644 index 0000000000000000000000000000000000000000..6eeee6e72f34baf454e5d15fe82658884efe7dda --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/qmldir/intercepted/intercepted.js @@ -0,0 +1 @@ +var myStr = "intercepted" diff --git a/tests/auto/qml/qqmlengine/data/interception/qmldir/intercepted/urlInterceptor.qml b/tests/auto/qml/qqmlengine/data/interception/qmldir/intercepted/urlInterceptor.qml new file mode 100644 index 0000000000000000000000000000000000000000..bd4aee056af9e483ba571fa85a14a93ac6bd4e02 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/qmldir/intercepted/urlInterceptor.qml @@ -0,0 +1,11 @@ +import QtQml 2.0 +import "intercepted.js" as Script + +QtObject { + property url filePath: "FailsTest" + property url resolvedUrl: Qt.resolvedUrl("FailsTest"); + property url absoluteUrl: Qt.resolvedUrl("file:///FailsTest"); + property string childString: child.myStr + property string scriptString: Script.myStr + property Intercepted child: Intercepted {} +} diff --git a/tests/auto/qml/qqmlengine/data/interception/qmldir/urlInterceptor.qml b/tests/auto/qml/qqmlengine/data/interception/qmldir/urlInterceptor.qml new file mode 100644 index 0000000000000000000000000000000000000000..22a09e5522bbc9a9d7f1df09b36f3df1435ada1b --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/qmldir/urlInterceptor.qml @@ -0,0 +1,12 @@ +import QtQml 2.0 +import "." +import "intercepted.js" as Script + +QtObject { + property url filePath: "doesNotExist.file" + property url resolvedUrl: Qt.resolvedUrl("doesNotExist.file"); + property url absoluteUrl: Qt.resolvedUrl("file:///doesNotExist.file"); + property string childString: child.myStr + property string scriptString: Script.myStr + property Intercepted child: Intercepted {} +} diff --git a/tests/auto/qml/qqmlengine/data/interception/strings/Intercepted.qml b/tests/auto/qml/qqmlengine/data/interception/strings/Intercepted.qml new file mode 100644 index 0000000000000000000000000000000000000000..449207e0e3827f3284e4c627b05158aa183482d9 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/strings/Intercepted.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + property string myStr: "base file" +} diff --git a/tests/auto/qml/qqmlengine/data/interception/strings/intercepted.js b/tests/auto/qml/qqmlengine/data/interception/strings/intercepted.js new file mode 100644 index 0000000000000000000000000000000000000000..6f54ebcdc56863d17938637fbce1b9373acdfbe0 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/strings/intercepted.js @@ -0,0 +1 @@ +var myStr = "base file" diff --git a/tests/auto/qml/qqmlengine/data/interception/strings/intercepted/Intercepted.qml b/tests/auto/qml/qqmlengine/data/interception/strings/intercepted/Intercepted.qml new file mode 100644 index 0000000000000000000000000000000000000000..0331a01ad41ae69d3e5f514a2ecfd5efcc9975a7 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/strings/intercepted/Intercepted.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + property string myStr: "intercepted" +} diff --git a/tests/auto/qml/qqmlengine/data/interception/strings/intercepted/intercepted.js b/tests/auto/qml/qqmlengine/data/interception/strings/intercepted/intercepted.js new file mode 100644 index 0000000000000000000000000000000000000000..6eeee6e72f34baf454e5d15fe82658884efe7dda --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/strings/intercepted/intercepted.js @@ -0,0 +1 @@ +var myStr = "intercepted" diff --git a/tests/auto/qml/qqmlengine/data/interception/strings/intercepted/urlInterceptor.qml b/tests/auto/qml/qqmlengine/data/interception/strings/intercepted/urlInterceptor.qml new file mode 100644 index 0000000000000000000000000000000000000000..bd4aee056af9e483ba571fa85a14a93ac6bd4e02 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/strings/intercepted/urlInterceptor.qml @@ -0,0 +1,11 @@ +import QtQml 2.0 +import "intercepted.js" as Script + +QtObject { + property url filePath: "FailsTest" + property url resolvedUrl: Qt.resolvedUrl("FailsTest"); + property url absoluteUrl: Qt.resolvedUrl("file:///FailsTest"); + property string childString: child.myStr + property string scriptString: Script.myStr + property Intercepted child: Intercepted {} +} diff --git a/tests/auto/qml/qqmlengine/data/interception/strings/urlInterceptor.qml b/tests/auto/qml/qqmlengine/data/interception/strings/urlInterceptor.qml new file mode 100644 index 0000000000000000000000000000000000000000..be86195bd8ec874269b41fac9300ba4a4d4aa01f --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/strings/urlInterceptor.qml @@ -0,0 +1,11 @@ +import QtQml 2.0 +import "intercepted.js" as Script + +QtObject { + property url filePath: "doesNotExist.file" + property url resolvedUrl: Qt.resolvedUrl("doesNotExist.file"); + property url absoluteUrl: Qt.resolvedUrl("file:///doesNotExist.file"); + property string childString: child.myStr + property string scriptString: Script.myStr + property Intercepted child: Intercepted {} +} diff --git a/tests/auto/qml/qqmlengine/data/interception/types/Intercepted.qml b/tests/auto/qml/qqmlengine/data/interception/types/Intercepted.qml new file mode 100644 index 0000000000000000000000000000000000000000..449207e0e3827f3284e4c627b05158aa183482d9 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/types/Intercepted.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + property string myStr: "base file" +} diff --git a/tests/auto/qml/qqmlengine/data/interception/types/Intercepted2.qml b/tests/auto/qml/qqmlengine/data/interception/types/Intercepted2.qml new file mode 100644 index 0000000000000000000000000000000000000000..0331a01ad41ae69d3e5f514a2ecfd5efcc9975a7 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/types/Intercepted2.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + property string myStr: "intercepted" +} diff --git a/tests/auto/qml/qqmlengine/data/interception/types/intercepted.js b/tests/auto/qml/qqmlengine/data/interception/types/intercepted.js new file mode 100644 index 0000000000000000000000000000000000000000..6f54ebcdc56863d17938637fbce1b9373acdfbe0 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/types/intercepted.js @@ -0,0 +1 @@ +var myStr = "base file" diff --git a/tests/auto/qml/qqmlengine/data/interception/types/intercepted/Intercepted.qml b/tests/auto/qml/qqmlengine/data/interception/types/intercepted/Intercepted.qml new file mode 100644 index 0000000000000000000000000000000000000000..ef5c28f87ba9d16517df7a07d1cc0792f89c9b3d --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/types/intercepted/Intercepted.qml @@ -0,0 +1,6 @@ +import QtQml 2.0 + +QtObject { + property string myStr: "intercepted" + property Intercepted2 compilationIsTest: Intercepted2{} +} diff --git a/tests/auto/qml/qqmlengine/data/interception/types/intercepted/Intercepted2.qml b/tests/auto/qml/qqmlengine/data/interception/types/intercepted/Intercepted2.qml new file mode 100644 index 0000000000000000000000000000000000000000..0331a01ad41ae69d3e5f514a2ecfd5efcc9975a7 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/types/intercepted/Intercepted2.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + property string myStr: "intercepted" +} diff --git a/tests/auto/qml/qqmlengine/data/interception/types/intercepted/intercepted.js b/tests/auto/qml/qqmlengine/data/interception/types/intercepted/intercepted.js new file mode 100644 index 0000000000000000000000000000000000000000..6eeee6e72f34baf454e5d15fe82658884efe7dda --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/types/intercepted/intercepted.js @@ -0,0 +1 @@ +var myStr = "intercepted" diff --git a/tests/auto/qml/qqmlengine/data/interception/types/intercepted/urlInterceptor.qml b/tests/auto/qml/qqmlengine/data/interception/types/intercepted/urlInterceptor.qml new file mode 100644 index 0000000000000000000000000000000000000000..be86195bd8ec874269b41fac9300ba4a4d4aa01f --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/types/intercepted/urlInterceptor.qml @@ -0,0 +1,11 @@ +import QtQml 2.0 +import "intercepted.js" as Script + +QtObject { + property url filePath: "doesNotExist.file" + property url resolvedUrl: Qt.resolvedUrl("doesNotExist.file"); + property url absoluteUrl: Qt.resolvedUrl("file:///doesNotExist.file"); + property string childString: child.myStr + property string scriptString: Script.myStr + property Intercepted child: Intercepted {} +} diff --git a/tests/auto/qml/qqmlengine/data/interception/types/urlInterceptor.qml b/tests/auto/qml/qqmlengine/data/interception/types/urlInterceptor.qml new file mode 100644 index 0000000000000000000000000000000000000000..bd4aee056af9e483ba571fa85a14a93ac6bd4e02 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/interception/types/urlInterceptor.qml @@ -0,0 +1,11 @@ +import QtQml 2.0 +import "intercepted.js" as Script + +QtObject { + property url filePath: "FailsTest" + property url resolvedUrl: Qt.resolvedUrl("FailsTest"); + property url absoluteUrl: Qt.resolvedUrl("file:///FailsTest"); + property string childString: child.myStr + property string scriptString: Script.myStr + property Intercepted child: Intercepted {} +} diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index 9177ff58f7c16d123b9618d3761fb6c8662e5b9c..d604118b5849bb09546b3579b852829e9608b645 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -54,6 +54,7 @@ #include <QQmlExpression> #include <QQmlIncubationController> #include <private/qqmlengine_p.h> +#include <private/qqmlabstracturlinterceptor_p.h> class tst_qqmlengine : public QQmlDataTest { @@ -79,6 +80,8 @@ private slots: void multipleEngines(); void qtqmlModule_data(); void qtqmlModule(); + void urlInterceptor_data(); + void urlInterceptor(); public slots: QObject *createAQObjectForOwnershipTest () @@ -674,6 +677,99 @@ void tst_qqmlengine::qtqmlModule() } } +class CustomSelector : public QQmlAbstractUrlInterceptor +{ +public: + virtual QUrl intercept(const QUrl &url, QQmlAbstractUrlInterceptor::DataType d) + { + if (url.scheme() != QStringLiteral("file")) + return url; + if (!m_interceptionPoints.contains(d)) + return url; + + QString alteredPath = url.path(); + int a = alteredPath.lastIndexOf('/'); + if (a < 0) + a = 0; + alteredPath.insert(a, QStringLiteral("/intercepted")); + + QUrl ret = url; + ret.setPath(alteredPath); + return ret; + } + QList<QQmlAbstractUrlInterceptor::DataType> m_interceptionPoints; +}; + +Q_DECLARE_METATYPE(QList<QQmlAbstractUrlInterceptor::DataType>); +void tst_qqmlengine::urlInterceptor_data() +{ + QTest::addColumn<QUrl>("testFile"); + QTest::addColumn<QList<QQmlAbstractUrlInterceptor::DataType> >("interceptionPoint"); + QTest::addColumn<QString>("expectedFilePath"); + QTest::addColumn<QString>("expectedChildString"); + QTest::addColumn<QString>("expectedScriptString"); + QTest::addColumn<QString>("expectedResolvedUrl"); + QTest::addColumn<QString>("expectedAbsoluteUrl"); + + QTest::newRow("InterceptTypes") + << testFileUrl("interception/types/urlInterceptor.qml") + << (QList<QQmlAbstractUrlInterceptor::DataType>() << QQmlAbstractUrlInterceptor::QmlFile << QQmlAbstractUrlInterceptor::JavaScriptFile << QQmlAbstractUrlInterceptor::UrlString) + << testFileUrl("interception/types/intercepted/doesNotExist.file").toString() + << QStringLiteral("intercepted") + << QStringLiteral("intercepted") + << testFileUrl("interception/types/intercepted/doesNotExist.file").toString() + << QStringLiteral("file:///intercepted/doesNotExist.file"); + + QTest::newRow("InterceptQmlDir") + << testFileUrl("interception/qmldir/urlInterceptor.qml") + << (QList<QQmlAbstractUrlInterceptor::DataType>() << QQmlAbstractUrlInterceptor::QmldirFile << QQmlAbstractUrlInterceptor::UrlString) + << testFileUrl("interception/qmldir/intercepted/doesNotExist.file").toString() + << QStringLiteral("intercepted") + << QStringLiteral("base file") + << testFileUrl("interception/qmldir/intercepted/doesNotExist.file").toString() + << QStringLiteral("file:///intercepted/doesNotExist.file"); + + QTest::newRow("InterceptStrings") + << testFileUrl("interception/strings/urlInterceptor.qml") + << (QList<QQmlAbstractUrlInterceptor::DataType>() << QQmlAbstractUrlInterceptor::UrlString) + << testFileUrl("interception/strings/intercepted/doesNotExist.file").toString() + << QStringLiteral("base file") + << QStringLiteral("base file") + << testFileUrl("interception/strings/intercepted/doesNotExist.file").toString() + << QStringLiteral("file:///intercepted/doesNotExist.file"); +} + +void tst_qqmlengine::urlInterceptor() +{ + + QFETCH(QUrl, testFile); + QFETCH(QList<QQmlAbstractUrlInterceptor::DataType>, interceptionPoint); + QFETCH(QString, expectedFilePath); + QFETCH(QString, expectedChildString); + QFETCH(QString, expectedScriptString); + QFETCH(QString, expectedResolvedUrl); + QFETCH(QString, expectedAbsoluteUrl); + + QQmlEngine e; + CustomSelector cs; + cs.m_interceptionPoints = interceptionPoint; + e.setUrlInterceptor(&cs); + QQmlComponent c(&e, testFile); //Note that this can get intercepted too + QObject *o = c.create(); + if (!o) + qDebug() << c.errorString(); + QVERIFY(o); + //Test a URL as a property initialization + QCOMPARE(o->property("filePath").toString(), expectedFilePath); + //Test a URL as a Type location + QCOMPARE(o->property("childString").toString(), expectedChildString); + //Test a URL as a Script location + QCOMPARE(o->property("scriptString").toString(), expectedScriptString); + //Test a URL as a resolveUrl() call + QCOMPARE(o->property("resolvedUrl").toString(), expectedResolvedUrl); + QCOMPARE(o->property("absoluteUrl").toString(), expectedAbsoluteUrl); +} + QTEST_MAIN(tst_qqmlengine) #include "tst_qqmlengine.moc"