+module Qt.labs.folderlistmodel
plugin qmlfolderlistmodelplugin
-plugin qmllocalstorageplugin
\ No newline at end of file
+module QtQuick.LocalStorage
+plugin qmllocalstorageplugin
+module QtQuick.Particles
plugin particlesplugin
+module QtQuick
plugin qtquick2plugin
+module QtTest
plugin qmltestplugin
TestCase 1.0 TestCase.qml
SignalSpy 1.0 SignalSpy.qml
+module QtQuick.Window
plugin windowplugin
-plugin qmlxmllistmodelplugin
\ No newline at end of file
+module QtQuick.XmlListModel
+plugin qmlxmllistmodelplugin
_scripts.clear();
int lineNumber = 0;
+ bool firstLine = true;
const QChar *ch = source.constData();
while (!ch->isNull()) {
++lineNumber;
+ bool invalidLine = false;
const QChar *lineStart = ch;
scanSpace(ch);
} else {
reportError(lineNumber, start-lineStart, QLatin1String("unexpected token"));
scanToEnd(ch);
+ invalidLine = true;
break;
}
scanSpace(ch);
if (!ch->isNull())
++ch;
- if (sectionCount == 0) {
+ if (invalidLine) {
+ reportError(lineNumber, -1,
+ QString::fromUtf8("invalid qmldir directive contains too many tokens"));
+ continue;
+ } else if (sectionCount == 0) {
continue; // no sections, no party.
+ } else if (sections[0] == QLatin1String("module")) {
+ if (sectionCount != 2) {
+ reportError(lineNumber, -1,
+ QString::fromUtf8("module directive requires one argument, but %1 were provided").arg(sectionCount - 1));
+ continue;
+ }
+ if (!_typeNamespace.isEmpty()) {
+ reportError(lineNumber, -1,
+ QString::fromUtf8("only one module directive may be defined in a qmldir file"));
+ continue;
+ }
+ if (!firstLine) {
+ reportError(lineNumber, -1,
+ QString::fromUtf8("module directive must be the first directive in a qmldir file"));
+ continue;
+ }
+
+ _typeNamespace = sections[1];
+
} else if (sections[0] == QLatin1String("plugin")) {
if (sectionCount < 2) {
reportError(lineNumber, -1,
reportError(lineNumber, -1,
QString::fromUtf8("a component declaration requires two or three arguments, but %1 were provided").arg(sectionCount));
}
+
+ firstLine = false;
}
return hasError();
QList<QQmlError> QQmlDirParser::errors(const QString &uri) const
{
+ QUrl url(uri);
QList<QQmlError> errors = _errors;
for (int i = 0; i < errors.size(); ++i) {
QQmlError &e = errors[i];
QString description = e.description();
description.replace(QLatin1String("$$URI$$"), uri);
e.setDescription(description);
+ e.setUrl(url);
}
return errors;
}
+QString QQmlDirParser::typeNamespace() const
+{
+ return _typeNamespace;
+}
+
+void QQmlDirParser::setTypeNamespace(const QString &s)
+{
+ _typeNamespace = s;
+}
+
QList<QQmlDirParser::Plugin> QQmlDirParser::plugins() const
{
return _plugins;
class QQmlError;
class QQmlEngine;
-class QQmlDirParser
+class Q_AUTOTEST_EXPORT QQmlDirParser
{
Q_DISABLE_COPY(QQmlDirParser)
void setError(const QQmlError &);
QList<QQmlError> errors(const QString &uri) const;
+ QString typeNamespace() const;
+ void setTypeNamespace(const QString &s);
+
struct Plugin
{
Plugin() {}
private:
QList<QQmlError> _errors;
+ QString _typeNamespace;
QHash<QHashedStringRef,Component> _components; // multi hash
QList<Script> _scripts;
QList<Plugin> _plugins;
bool QQmlEngine::importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors)
{
Q_D(QQmlEngine);
- return d->importDatabase.importPlugin(filePath, uri, errors);
+ return d->importDatabase.importPlugin(filePath, uri, QString(), errors);
}
/*!
#include <QtCore/qfileinfo.h>
#include <QtCore/qpluginloader.h>
#include <QtCore/qlibraryinfo.h>
+#include <QtCore/qreadwritelock.h>
#include <QtQml/qqmlextensioninterface.h>
#include <private/qqmlglobal_p.h>
#include <private/qqmltypenamecache_p.h>
QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath,
plugin.path, plugin.name);
if (!resolvedFilePath.isEmpty()) {
- if (!database->importPlugin(resolvedFilePath, uri, errors)) {
+ if (!database->importPlugin(resolvedFilePath, uri, qmldir->typeNamespace(), errors)) {
if (errors) {
// XXX TODO: should we leave the import plugin error alone?
// Here, we pop it off the top and coalesce it into this error's message.
/*!
\internal
*/
-bool QQmlImportDatabase::importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors)
+bool QQmlImportDatabase::importPlugin(const QString &filePath, const QString &uri, const QString &typeNamespace, QList<QQmlError> *errors)
{
if (qmlImportTrace())
qDebug().nospace() << "QQmlImportDatabase::importPlugin: " << uri << " from " << filePath;
const char *moduleId = bytes.constData();
if (!typesRegistered) {
- // XXX thread this code should probably be protected with a mutex.
qmlEnginePluginsWithRegisteredTypes()->insert(absoluteFilePath, uri);
- iface->registerTypes(moduleId);
+
+ QStringList registrationFailures;
+
+ {
+ QWriteLocker lock(QQmlMetaType::typeRegistrationLock());
+
+ if (!typeNamespace.isEmpty()) {
+ // This is a 'strict' module
+ if (typeNamespace != uri) {
+ // The namespace for type registrations must match the URI for locating the module
+ QQmlError error;
+ error.setDescription(tr("Module namespace '%1' does not match import URI '%2'").arg(typeNamespace).arg(uri));
+ errors->prepend(error);
+ return false;
+ }
+
+ if (QQmlMetaType::namespaceContainsRegistrations(typeNamespace)) {
+ // Other modules have already installed to this namespace
+ QQmlError error;
+ error.setDescription(tr("Namespace '%1' has already been used for type registration").arg(typeNamespace));
+ errors->prepend(error);
+ return false;
+ } else {
+ QQmlMetaType::protectNamespace(typeNamespace);
+ }
+ } else {
+ // This is not a stict module - provide a warning
+ qWarning().nospace() << qPrintable(tr("Module '%1' does not contain a module directive - it cannot be protected from external registrations.").arg(uri));
+ }
+
+ QQmlMetaType::setTypeRegistrationNamespace(typeNamespace);
+
+ iface->registerTypes(moduleId);
+
+ registrationFailures = QQmlMetaType::typeRegistrationFailures();
+ QQmlMetaType::setTypeRegistrationNamespace(QString());
+ }
+
+ if (!registrationFailures.isEmpty()) {
+ foreach (const QString &failure, registrationFailures) {
+ QQmlError error;
+ error.setDescription(failure);
+ errors->prepend(error);
+ }
+ return false;
+ }
}
if (!engineInitialized) {
// things on the engine (eg. adding new global objects) have to be done for every
QQmlImportDatabase(QQmlEngine *);
~QQmlImportDatabase();
- bool importPlugin(const QString &filePath, const QString &uri, QList<QQmlError> *errors);
+ bool importPlugin(const QString &filePath, const QString &uri, const QString &importNamespace, QList<QQmlError> *errors);
QStringList importPathList(PathType type = LocalOrRemote) const;
void setImportPathList(const QStringList &paths);
QBitArray lists;
QList<QQmlPrivate::AutoParentFunction> parentFunctions;
+
+ QSet<QString> protectedNamespaces;
+
+ QString typeRegistrationNamespace;
+ QStringList typeRegistrationFailures;
};
class QQmlTypeModulePrivate
};
Q_GLOBAL_STATIC(QQmlMetaTypeData, metaTypeData)
-Q_GLOBAL_STATIC(QReadWriteLock, metaTypeDataLock)
+Q_GLOBAL_STATIC_WITH_ARGS(QReadWriteLock, metaTypeDataLock, (QReadWriteLock::Recursive))
static uint qHash(const QQmlMetaTypeData::VersionedUri &v)
{
// Avoid multiple fromUtf8(), copies and hashing of the module name.
// This is only called when metaTypeDataLock is locked.
-static QHashedString moduletoUtf8(const char *module)
+static QHashedString moduleFromUtf8(const char *module)
{
if (!module)
return QHashedString();
QQmlType::QQmlType(int index, const QQmlPrivate::RegisterType &type)
: d(new QQmlTypePrivate)
{
- d->m_module = moduletoUtf8(type.uri);
+ d->m_module = moduleFromUtf8(type.uri);
d->m_elementName = QString::fromUtf8(type.elementName);
d->m_version_maj = type.versionMajor;
QWriteLocker lock(metaTypeDataLock());
QQmlMetaTypeData *data = metaTypeData();
+
+ if (type.uri && type.elementName) {
+ QString nameSpace = moduleFromUtf8(type.uri);
+
+ if (!data->typeRegistrationNamespace.isEmpty()) {
+ // We can only install types into the registered namespace
+ if (nameSpace != data->typeRegistrationNamespace) {
+ QString failure(QCoreApplication::translate("qmlRegisterType",
+ "Cannot install element '%1' into unregistered namespace '%2'"));
+ data->typeRegistrationFailures.append(failure.arg(QString::fromUtf8(type.elementName)).arg(nameSpace));
+ return -1;
+ }
+ } else if (data->typeRegistrationNamespace != nameSpace) {
+ // Is the target namespace protected against further registrations?
+ if (data->protectedNamespaces.contains(nameSpace)) {
+ QString failure(QCoreApplication::translate("qmlRegisterType",
+ "Cannot install element '%1' into protected namespace '%2'"));
+ data->typeRegistrationFailures.append(failure.arg(QString::fromUtf8(type.elementName)).arg(nameSpace));
+ return -1;
+ }
+ }
+ }
+
int index = data->types.count();
QQmlType *dtype = new QQmlType(index, type);
return -1;
}
+bool QQmlMetaType::namespaceContainsRegistrations(const QString &uri)
+{
+ QQmlMetaTypeData *data = metaTypeData();
+
+ // Has any type previously been installed to this namespace?
+ QHashedString nameSpace(uri);
+ foreach (const QQmlType *type, data->types)
+ if (type->module() == nameSpace)
+ return true;
+
+ return false;
+}
+
+void QQmlMetaType::protectNamespace(const QString &uri)
+{
+ QQmlMetaTypeData *data = metaTypeData();
+
+ data->protectedNamespaces.insert(uri);
+}
+
+void QQmlMetaType::setTypeRegistrationNamespace(const QString &uri)
+{
+ QQmlMetaTypeData *data = metaTypeData();
+
+ data->typeRegistrationNamespace = uri;
+ data->typeRegistrationFailures.clear();
+}
+
+QStringList QQmlMetaType::typeRegistrationFailures()
+{
+ QQmlMetaTypeData *data = metaTypeData();
+
+ return data->typeRegistrationFailures;
+}
+
+QReadWriteLock *QQmlMetaType::typeRegistrationLock()
+{
+ return metaTypeDataLock();
+}
+
/*
Returns true if a module \a uri of any version is installed.
*/
class QQmlTypeModule;
class QHashedString;
class QHashedStringRef;
+class QReadWriteLock;
class Q_QML_PRIVATE_EXPORT QQmlMetaType
{
static ModuleApi moduleApi(const QString &, int, int);
static QHash<QString, QList<ModuleApi> > moduleApis();
+ static bool namespaceContainsRegistrations(const QString &);
+
+ static void protectNamespace(const QString &);
+
+ static void setTypeRegistrationNamespace(const QString &);
+ static QStringList typeRegistrationFailures();
+
+ static QReadWriteLock *typeRegistrationLock();
+
private:
static CompareFunction anchorLineCompareFunction;
};
return m_parser.errors(uri);
}
+QString QQmlTypeLoader::QmldirContent::typeNamespace() const
+{
+ return m_parser.typeNamespace();
+}
+
void QQmlTypeLoader::QmldirContent::setContent(const QString &location, const QString &content)
{
m_location = location;
bool hasError() const;
QList<QQmlError> errors(const QString &uri) const;
+ QString typeNamespace() const;
+
QQmlDirComponents components() const;
QQmlDirScripts scripts() const;
QQmlDirPlugins plugins() const;
+module bundletest
plugin plugin1
--- /dev/null
+module foo bar
--- /dev/null
+plugin foo bar baz
--- /dev/null
+foo bar baz qux
--- /dev/null
+foo 100 bar
--- /dev/null
+
+#
+# Comment
+
+module ModuleNamespace
+
+plugin PluginA plugina.so # More comment
+
+ComponentA 1.0 componenta-1_0.qml
+ScriptA 1.0 scripta-1_0.js
+
+#
+ComponentA 1.5 componenta-1_5.qml
+ComponentB 1.5 componentb-1_5.qml
+
--- /dev/null
+plugin foo bar
--- /dev/null
+plugin foo
--- /dev/null
+
+# only empty lines
+ # and comments
+
--- /dev/null
+plugin foo
+module bar
--- /dev/null
+module foo
+module bar
--- /dev/null
+foo 33.66 bar
--- /dev/null
+foo 33.66 bar.js
--- /dev/null
+CONFIG += testcase
+TARGET = tst_qqmldirparser
+QT += qml testlib v8-private
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_qqmldirparser.cpp
+
+include (../../shared/util.pri)
+
+CONFIG += parallel_test
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "../../shared/util.h"
+
+#include <qtest.h>
+#include <QObject>
+#include <QQmlEngine>
+#include <QQmlComponent>
+#include <private/v8.h>
+#include <private/qqmldirparser_p.h>
+#include <QDebug>
+
+// Test the parsing of qmldir files
+
+class tst_qqmldirparser : public QQmlDataTest
+{
+ Q_OBJECT
+public:
+ tst_qqmldirparser();
+
+private slots:
+ void parse_data();
+ void parse();
+};
+
+tst_qqmldirparser::tst_qqmldirparser()
+{
+}
+
+namespace {
+
+ QStringList toStringList(const QList<QQmlError> &errors)
+ {
+ QStringList rv;
+
+ foreach (const QQmlError &e, errors)
+ rv.append(e.toString());
+
+ return rv;
+ }
+
+ QString toString(const QQmlDirParser::Plugin &p)
+ {
+ return p.name + "|" + p.path;
+ }
+
+ QStringList toStringList(const QList<QQmlDirParser::Plugin> &plugins)
+ {
+ QStringList rv;
+
+ foreach (const QQmlDirParser::Plugin &p, plugins)
+ rv.append(toString(p));
+
+ return rv;
+ }
+
+ QString toString(const QQmlDirParser::Component &c)
+ {
+ return c.typeName + "|" + c.fileName + "|" + QString::number(c.majorVersion) + "|" + QString::number(c.minorVersion) + "|" + (c.internal ? "true" : "false");
+ }
+
+ QStringList toStringList(const QQmlDirComponents &components)
+ {
+ QStringList rv;
+
+ foreach (const QQmlDirParser::Component &c, components.values())
+ rv.append(toString(c));
+
+ qSort(rv);
+ return rv;
+ }
+
+ QString toString(const QQmlDirParser::Script &s)
+ {
+ return s.nameSpace + "|" + s.fileName + "|" + QString::number(s.majorVersion) + "|" + QString::number(s.minorVersion);
+ }
+
+ QStringList toStringList(const QList<QQmlDirParser::Script> &scripts)
+ {
+ QStringList rv;
+
+ foreach (const QQmlDirParser::Script &s, scripts)
+ rv.append(toString(s));
+
+ return rv;
+ }
+}
+
+void tst_qqmldirparser::parse_data()
+{
+ QTest::addColumn<QString>("file");
+ QTest::addColumn<QStringList>("errors");
+ QTest::addColumn<QStringList>("plugins");
+ QTest::addColumn<QStringList>("components");
+ QTest::addColumn<QStringList>("scripts");
+
+ QTest::newRow("empty")
+ << "empty/qmldir"
+ << QStringList()
+ << QStringList()
+ << QStringList()
+ << QStringList();
+
+ QTest::newRow("no-content")
+ << "no-content/qmldir"
+ << QStringList()
+ << QStringList()
+ << QStringList()
+ << QStringList();
+
+ QTest::newRow("one-section")
+ << "one-section/qmldir"
+ << (QStringList() << "qmldir:1: a component declaration requires two or three arguments, but 1 were provided")
+ << QStringList()
+ << QStringList()
+ << QStringList();
+
+ QTest::newRow("four-sections")
+ << "four-sections/qmldir"
+ << (QStringList() << "qmldir:1:12: unexpected token"
+ << "qmldir:1: invalid qmldir directive contains too many tokens")
+ << QStringList()
+ << QStringList()
+ << QStringList();
+
+ QTest::newRow("incomplete-module")
+ << "incomplete-module/qmldir"
+ << (QStringList() << "qmldir:1: module directive requires one argument, but 0 were provided")
+ << QStringList()
+ << QStringList()
+ << QStringList();
+
+ QTest::newRow("excessive-module")
+ << "excessive-module/qmldir"
+ << (QStringList() << "qmldir:1: module directive requires one argument, but 2 were provided")
+ << QStringList()
+ << QStringList()
+ << QStringList();
+
+ QTest::newRow("repeated-module")
+ << "repeated-module/qmldir"
+ << (QStringList() << "qmldir:2: only one module directive may be defined in a qmldir file")
+ << QStringList()
+ << QStringList()
+ << QStringList();
+
+ QTest::newRow("non-first-module")
+ << "non-first-module/qmldir"
+ << (QStringList() << "qmldir:2: module directive must be the first directive in a qmldir file")
+ << (QStringList() << "foo|")
+ << QStringList()
+ << QStringList();
+
+ QTest::newRow("incomplete-plugin")
+ << "incomplete-plugin/qmldir"
+ << (QStringList() << "qmldir:1: plugin directive requires one or two arguments, but 0 were provided")
+ << QStringList()
+ << QStringList()
+ << QStringList();
+
+ QTest::newRow("excessive-plugin")
+ << "excessive-plugin/qmldir"
+ << (QStringList() << "qmldir:1:15: unexpected token"
+ << "qmldir:1: invalid qmldir directive contains too many tokens")
+ << QStringList()
+ << QStringList()
+ << QStringList();
+
+ QTest::newRow("name-plugin")
+ << "name-plugin/qmldir"
+ << QStringList()
+ << (QStringList() << "foo|")
+ << QStringList()
+ << QStringList();
+
+ QTest::newRow("name-path-plugin")
+ << "name-path-plugin/qmldir"
+ << QStringList()
+ << (QStringList() << "foo|bar")
+ << QStringList()
+ << QStringList();
+
+ QTest::newRow("unversioned-component")
+ << "unversioned-component/qmldir"
+ << QStringList()
+ << QStringList()
+ << (QStringList() << "foo|bar|-1|-1|false")
+ << QStringList();
+
+ QTest::newRow("invalid-versioned-component")
+ << "invalid-versioned-component/qmldir"
+ << (QStringList() << "qmldir:1: expected '.'")
+ << QStringList()
+ << QStringList()
+ << QStringList();
+
+ QTest::newRow("versioned-component")
+ << "versioned-component/qmldir"
+ << QStringList()
+ << QStringList()
+ << (QStringList() << "foo|bar|33|66|false")
+ << QStringList();
+
+ QTest::newRow("versioned-script")
+ << "versioned-script/qmldir"
+ << QStringList()
+ << QStringList()
+ << QStringList()
+ << (QStringList() << "foo|bar.js|33|66");
+
+ QTest::newRow("multiple")
+ << "multiple/qmldir"
+ << QStringList()
+ << (QStringList() << "PluginA|plugina.so")
+ << (QStringList() << "ComponentA|componenta-1_0.qml|1|0|false"
+ << "ComponentA|componenta-1_5.qml|1|5|false"
+ << "ComponentB|componentb-1_5.qml|1|5|false")
+ << (QStringList() << "ScriptA|scripta-1_0.js|1|0");
+}
+
+void tst_qqmldirparser::parse()
+{
+ QFETCH(QString, file);
+ QFETCH(QStringList, errors);
+ QFETCH(QStringList, plugins);
+ QFETCH(QStringList, components);
+ QFETCH(QStringList, scripts);
+
+ QFile f(testFile(file));
+ f.open(QIODevice::ReadOnly);
+
+ QQmlDirParser p;
+ p.parse(f.readAll());
+
+ if (errors.isEmpty()) {
+ QCOMPARE(p.hasError(), false);
+ } else {
+ QCOMPARE(p.hasError(), true);
+ QCOMPARE(toStringList(p.errors("qmldir")), errors);
+ }
+
+ QCOMPARE(toStringList(p.plugins()), plugins);
+ QCOMPARE(toStringList(p.components()), components);
+ QCOMPARE(toStringList(p.scripts()), scripts);
+}
+
+QTEST_MAIN(tst_qqmldirparser)
+
+#include "tst_qqmldirparser.moc"
1:12:unexpected token
-1:-1:expected '.'
+1:-1:invalid qmldir directive contains too many tokens
2:17:unexpected token
+2:-1:invalid qmldir directive contains too many tokens
--- /dev/null
+TEMPLATE = lib
+CONFIG += plugin
+SOURCES = plugin.cpp
+QT = core qml
+DESTDIR = ../imports/com/nokia/InvalidNamespaceModule
+
+QT += core-private gui-private qml-private
+
+IMPORT_FILES = \
+ qmldir
+
+include (../../../shared/imports.pri)
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QStringList>
+#include <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqml.h>
+#include <QDebug>
+
+class MyPluginType : public QObject
+{
+ Q_OBJECT
+public:
+ MyPluginType(QObject *parent=0) : QObject(parent) {}
+};
+
+
+class MyPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
+
+public:
+ MyPlugin() {}
+
+ void registerTypes(const char *uri)
+ {
+ Q_ASSERT(QLatin1String(uri) == "com.nokia.InvalidStrictModule");
+ qmlRegisterType<MyPluginType>("com.nokia.SomeOtherModule", 1, 0, "MyPluginType");
+ }
+};
+
+#include "plugin.moc"
--- /dev/null
+module com.nokia.AwesomeModule
+plugin invalidNamespaceModule
--- /dev/null
+TEMPLATE = lib
+CONFIG += plugin
+SOURCES = plugin.cpp
+QT = core qml
+DESTDIR = ../imports/com/nokia/InvalidStrictModule
+
+QT += core-private gui-private qml-private
+
+IMPORT_FILES = \
+ qmldir
+
+include (../../../shared/imports.pri)
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QStringList>
+#include <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqml.h>
+#include <QDebug>
+
+class MyPluginType : public QObject
+{
+ Q_OBJECT
+public:
+ MyPluginType(QObject *parent=0) : QObject(parent) {}
+};
+
+
+class MyPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
+
+public:
+ MyPlugin() {}
+
+ void registerTypes(const char *uri)
+ {
+ Q_ASSERT(QLatin1String(uri) == "com.nokia.InvalidStrictModule");
+ qmlRegisterType<MyPluginType>("com.nokia.SomeOtherModule", 1, 0, "MyPluginType");
+ }
+};
+
+#include "plugin.moc"
--- /dev/null
+module com.nokia.InvalidStrictModule
+plugin invalidStrictModule
--- /dev/null
+TEMPLATE = lib
+CONFIG += plugin
+SOURCES = plugin.cpp
+QT = core qml
+DESTDIR = ../imports/com/nokia/NonstrictModule
+
+QT += core-private gui-private qml-private
+
+IMPORT_FILES = \
+ qmldir
+
+include (../../../shared/imports.pri)
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QStringList>
+#include <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqml.h>
+#include <QDebug>
+
+class MyPluginType : public QObject
+{
+ Q_OBJECT
+public:
+ MyPluginType(QObject *parent=0) : QObject(parent) {}
+};
+
+
+class MyPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
+
+public:
+ MyPlugin() {}
+
+ void registerTypes(const char *uri)
+ {
+ Q_ASSERT(QLatin1String(uri) == "com.nokia.NonstrictModule");
+
+ // Install into a namespace that should be protected
+ qmlRegisterType<MyPluginType>("com.nokia.StrictModule", 1, 0, "MyPluginType");
+ }
+};
+
+#include "plugin.moc"
--- /dev/null
+plugin nonstrictModule
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QStringList>
+#include <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqml.h>
+#include <QDebug>
+
+class MyPluginType : public QObject
+{
+ Q_OBJECT
+public:
+ MyPluginType(QObject *parent=0) : QObject(parent) {}
+};
+
+
+class MyPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
+
+public:
+ MyPlugin() {}
+
+ void registerTypes(const char *uri)
+ {
+ Q_ASSERT(QLatin1String(uri) == "com.nokia.PreemptedStrictModule");
+ qmlRegisterType<MyPluginType>(uri, 1, 0, "MyPluginType");
+ }
+};
+
+#include "plugin.moc"
--- /dev/null
+TEMPLATE = lib
+CONFIG += plugin
+SOURCES = plugin.cpp
+QT = core qml
+DESTDIR = ../imports/com/nokia/PreemptedStrictModule
+
+QT += core-private gui-private qml-private
+
+IMPORT_FILES = \
+ qmldir
+
+include (../../../shared/imports.pri)
--- /dev/null
+module com.nokia.PreemptedStrictModule
+plugin preemptedStrictModule
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QStringList>
+#include <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqml.h>
+#include <QDebug>
+
+class MyPluginType : public QObject
+{
+ Q_OBJECT
+public:
+ MyPluginType(QObject *parent=0) : QObject(parent) {}
+};
+
+
+class MyPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
+
+public:
+ MyPlugin() {}
+
+ void registerTypes(const char *uri)
+ {
+ Q_ASSERT(QLatin1String(uri) == "com.nokia.PreemptiveModule");
+ qmlRegisterType<MyPluginType>("com.nokia.PreemptiveModule", 1, 0, "MyPluginType");
+
+ // Install into another namespace that should be protected
+ qmlRegisterType<MyPluginType>("com.nokia.PreemptedStrictModule", 1, 0, "MyPluginType");
+ }
+};
+
+#include "plugin.moc"
--- /dev/null
+TEMPLATE = lib
+CONFIG += plugin
+SOURCES = plugin.cpp
+QT = core qml
+DESTDIR = ../imports/com/nokia/PreemptiveModule
+
+QT += core-private gui-private qml-private
+
+IMPORT_FILES = \
+ qmldir
+
+include (../../../shared/imports.pri)
--- /dev/null
+plugin preemptiveModule
QT = core
TEMPLATE = subdirs
-SUBDIRS = plugin plugin.2 plugin.2.1 pluginWrongCase pluginWithQmlFile pluginMixed pluginVersion nestedPlugin
+SUBDIRS =\
+ plugin\
+ plugin.2\
+ plugin.2.1\
+ pluginWrongCase\
+ pluginWithQmlFile\
+ pluginMixed\
+ pluginVersion\
+ nestedPlugin\
+ strictModule\
+ invalidStrictModule\
+ nonstrictModule\
+ preemptiveModule\
+ preemptedStrictModule\
+ invalidNamespaceModule
+
tst_qqmlmoduleplugin_pro.depends += plugin
SUBDIRS += tst_qqmlmoduleplugin.pro
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QStringList>
+#include <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqml.h>
+#include <QDebug>
+
+class MyPluginType : public QObject
+{
+ Q_OBJECT
+public:
+ MyPluginType(QObject *parent=0) : QObject(parent) {}
+};
+
+
+class MyPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
+
+public:
+ MyPlugin() {}
+
+ void registerTypes(const char *uri)
+ {
+ Q_ASSERT(QLatin1String(uri) == "com.nokia.StrictModule");
+ qmlRegisterType<MyPluginType>(uri, 1, 0, "MyPluginType");
+ }
+};
+
+#include "plugin.moc"
--- /dev/null
+module com.nokia.StrictModule
+plugin strictModule
--- /dev/null
+TEMPLATE = lib
+CONFIG += plugin
+SOURCES = plugin.cpp
+QT = core qml
+DESTDIR = ../imports/com/nokia/StrictModule
+
+QT += core-private gui-private qml-private
+
+IMPORT_FILES = \
+ qmldir
+
+include (../../../shared/imports.pri)
#define SERVER_ADDR "http://127.0.0.1:14456"
#define SERVER_PORT 14456
+// Note: this test does not use module directives in the qmldir files, because
+// it would result in repeated attempts to insert types into the same namespace.
+// This occurs because type registration is process-global, while the test
+// cases should really be run in proper per-process isolation.
class tst_qqmlmoduleplugin : public QQmlDataTest
{
void importsNested_data();
void importLocalModule();
void importLocalModule_data();
+ void importStrictModule();
+ void importStrictModule_data();
private:
QString m_importsDirectory;
engine.addImportPath(m_importsDirectory);
QTest::ignoreMessage(QtWarningMsg, "plugin created");
QTest::ignoreMessage(QtWarningMsg, "import worked");
+ QTest::ignoreMessage(QtWarningMsg, "Module 'com.nokia.AutoTestQmlPluginType' does not contain a module directive - it cannot be protected from external registrations.");
QQmlComponent component(&engine, testFileUrl(QStringLiteral("works.qml")));
foreach (QQmlError err, component.errors())
qWarning() << err;
engine.addImportPath(m_importsDirectory);
QTest::ignoreMessage(QtWarningMsg, "plugin2 created");
QTest::ignoreMessage(QtWarningMsg, "import2 worked");
+ QTest::ignoreMessage(QtWarningMsg, "Module 'com.nokia.AutoTestQmlPluginType' does not contain a module directive - it cannot be protected from external registrations.");
QQmlComponent component(&engine, testFileUrl(QStringLiteral("works2.qml")));
foreach (QQmlError err, component.errors())
qWarning() << err;
engine.addImportPath(m_importsDirectory);
QTest::ignoreMessage(QtWarningMsg, "plugin2.1 created");
QTest::ignoreMessage(QtWarningMsg, "import2.1 worked");
+ QTest::ignoreMessage(QtWarningMsg, "Module 'com.nokia.AutoTestQmlPluginType' does not contain a module directive - it cannot be protected from external registrations.");
QQmlComponent component(&engine, testFileUrl(QStringLiteral("works21.qml")));
foreach (QQmlError err, component.errors())
qWarning() << err;
QQmlEngine engine;
engine.addImportPath(path);
+ QTest::ignoreMessage(QtWarningMsg, "Module 'com.nokia.AutoTestPluginWithQmlFile' does not contain a module directive - it cannot be protected from external registrations.");
+
QQmlComponent component(&engine, testFileUrl(QStringLiteral("pluginWithQmlFile.qml")));
foreach (QQmlError err, component.errors())
qWarning() << err;
QQmlEngine engine;
engine.addImportPath(m_importsDirectory);
+ QTest::ignoreMessage(QtWarningMsg, "Module 'com.nokia.AutoTestQmlMixedPluginType' does not contain a module directive - it cannot be protected from external registrations.");
+
{
QQmlComponent component(&engine, testFileUrl(QStringLiteral("importsMixedQmlCppPlugin.qml")));
QQmlEngine engine;
engine.addImportPath(m_importsDirectory);
+ static int count = 0;
+ if (++count == 1)
+ QTest::ignoreMessage(QtWarningMsg, "Module 'com.nokia.AutoTestQmlVersionPluginType' does not contain a module directive - it cannot be protected from external registrations.");
+
QQmlComponent component(&engine, testFileUrl(file));
VERIFY_ERRORS(errorFile.toLatin1().constData());
}
QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready");
}
+ static int count = 0;
+ if (++count == 1)
+ QTest::ignoreMessage(QtWarningMsg, "Module 'com.nokia.AutoTestQmlNestedPluginType' does not contain a module directive - it cannot be protected from external registrations.");
+
QQmlComponent component(&engine, testFile(file));
QObject *obj = component.create();
<< 2 << 0;
}
+void tst_qqmlmoduleplugin::importStrictModule()
+{
+ QFETCH(QString, qml);
+ QFETCH(QString, warning);
+ QFETCH(QString, error);
+
+ if (!warning.isEmpty())
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
+
+ QQmlEngine engine;
+ engine.addImportPath(m_importsDirectory);
+
+ QUrl url(testFileUrl("empty.qml"));
+
+ QQmlComponent component(&engine);
+ component.setData(qml.toUtf8(), url);
+
+ if (error.isEmpty()) {
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(object != 0);
+ } else {
+ QVERIFY(!component.isReady());
+ QCOMPARE(component.errors().count(), 1);
+ QCOMPARE(component.errors().first().toString(), url.toString() + error);
+ }
+}
+
+void tst_qqmlmoduleplugin::importStrictModule_data()
+{
+ QTest::addColumn<QString>("qml");
+ QTest::addColumn<QString>("warning");
+ QTest::addColumn<QString>("error");
+
+ QTest::newRow("success")
+ << "import com.nokia.StrictModule 1.0\n"
+ "MyPluginType {}"
+ << QString()
+ << QString();
+
+ QTest::newRow("wrong target")
+ << "import com.nokia.InvalidStrictModule 1.0\n"
+ "MyPluginType {}"
+ << QString()
+ << ":1:1: plugin cannot be loaded for module \"com.nokia.InvalidStrictModule\": Cannot install element 'MyPluginType' into unregistered namespace 'com.nokia.SomeOtherModule'";
+
+ QTest::newRow("non-strict clash")
+ << "import com.nokia.NonstrictModule 1.0\n"
+ "MyPluginType {}"
+ << "Module 'com.nokia.NonstrictModule' does not contain a module directive - it cannot be protected from external registrations."
+ << ":1:1: plugin cannot be loaded for module \"com.nokia.NonstrictModule\": Cannot install element 'MyPluginType' into protected namespace 'com.nokia.StrictModule'";
+
+ QTest::newRow("non-strict preemption")
+ << "import com.nokia.PreemptiveModule 1.0\n"
+ "import com.nokia.PreemptedStrictModule 1.0\n"
+ "MyPluginType {}"
+ << "Module 'com.nokia.PreemptiveModule' does not contain a module directive - it cannot be protected from external registrations."
+ << ":2:1: plugin cannot be loaded for module \"com.nokia.PreemptedStrictModule\": Namespace 'com.nokia.PreemptedStrictModule' has already been used for type registration";
+
+ QTest::newRow("invalid namespace")
+ << "import com.nokia.InvalidNamespaceModule 1.0\n"
+ "MyPluginType {}"
+ << QString()
+ << ":1:1: plugin cannot be loaded for module \"com.nokia.InvalidNamespaceModule\": Module namespace 'com.nokia.AwesomeModule' does not match import URI 'com.nokia.InvalidNamespaceModule'";
+}
+
QTEST_MAIN(tst_qqmlmoduleplugin)
#include "tst_qqmlmoduleplugin.moc"
QVERIFY(!animation.isPaused());
QCOMPARE(spy.count(), 2);
- qmlRegisterType<QQuickPropertyAnimation>("QtQuick",2,0,"PropertyAnimation"); //make sure QQuickPropertyAnimation has correct qml type name
+ // Load QtQuick to ensure that QQuickPropertyAnimation is registered as PropertyAnimation
+ {
+ QQmlEngine engine;
+ QQmlComponent component(&engine);
+ component.setData("import QtQuick 2.0\nQtObject {}\n", QUrl());
+ }
+
QByteArray message = "<Unknown File>: QML PropertyAnimation: setPaused() cannot be used when animation isn't running.";
QTest::ignoreMessage(QtWarningMsg, message);
animation.pause();
import QtQuick 2.0
import QtQuick.Window 2.0 as Window
+import Test 1.0
Window.Window {
RootItemAccessor {
void tst_qquickwindow::ownershipRootItem()
{
- qmlRegisterType<RootItemAccessor>("QtQuick", 2, 0, "RootItemAccessor");
+ qmlRegisterType<RootItemAccessor>("Test", 1, 0, "RootItemAccessor");
QQmlEngine engine;
QQmlComponent component(&engine);