Implement strict mode for qmldir modules
authorMatthew Vogt <matthew.vogt@nokia.com>
Wed, 25 Jul 2012 06:59:17 +0000 (16:59 +1000)
committerQt by Nokia <qt-info@nokia.com>
Mon, 30 Jul 2012 22:22:36 +0000 (00:22 +0200)
Allow a module's qmldir to contain a module directive, which when
present specifies 'strict mode' import processing.  In strict mode,
type registrations are only permitted into the namespace identified
in the qmldir file's module directive.  In addition, any type
registrations to that namespace originating from other modules are
treated as error conditions.

Task-number: QTBUG-26551

Change-Id: I081bde2d3b83d3f28524440177fb2cd1ccee34ad
Reviewed-by: Chris Adams <christopher.adams@nokia.com>
Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
60 files changed:
src/imports/folderlistmodel/qmldir
src/imports/localstorage/qmldir
src/imports/particles/qmldir
src/imports/qtquick2/qmldir
src/imports/testlib/qmldir
src/imports/window/qmldir
src/imports/xmllistmodel/qmldir
src/qml/qml/qqmldirparser.cpp
src/qml/qml/qqmldirparser_p.h
src/qml/qml/qqmlengine.cpp
src/qml/qml/qqmlimport.cpp
src/qml/qml/qqmlimport_p.h
src/qml/qml/qqmlmetatype.cpp
src/qml/qml/qqmlmetatype_p.h
src/qml/qml/qqmltypeloader.cpp
src/qml/qml/qqmltypeloader_p.h
tests/auto/qml/qqmlbundle/data/imports/bundletest/bundledata/qmldir
tests/auto/qml/qqmldirparser/data/empty/qmldir [new file with mode: 0644]
tests/auto/qml/qqmldirparser/data/excessive-module/qmldir [new file with mode: 0644]
tests/auto/qml/qqmldirparser/data/excessive-plugin/qmldir [new file with mode: 0644]
tests/auto/qml/qqmldirparser/data/four-sections/qmldir [new file with mode: 0644]
tests/auto/qml/qqmldirparser/data/incomplete-module/qmldir [new file with mode: 0644]
tests/auto/qml/qqmldirparser/data/incomplete-plugin/qmldir [new file with mode: 0644]
tests/auto/qml/qqmldirparser/data/invalid-versioned-component/qmldir [new file with mode: 0644]
tests/auto/qml/qqmldirparser/data/multiple/qmldir [new file with mode: 0644]
tests/auto/qml/qqmldirparser/data/name-path-plugin/qmldir [new file with mode: 0644]
tests/auto/qml/qqmldirparser/data/name-plugin/qmldir [new file with mode: 0644]
tests/auto/qml/qqmldirparser/data/no-content/qmldir [new file with mode: 0644]
tests/auto/qml/qqmldirparser/data/non-first-module/qmldir [new file with mode: 0644]
tests/auto/qml/qqmldirparser/data/one-section/qmldir [new file with mode: 0644]
tests/auto/qml/qqmldirparser/data/repeated-module/qmldir [new file with mode: 0644]
tests/auto/qml/qqmldirparser/data/unversioned-component/qmldir [new file with mode: 0644]
tests/auto/qml/qqmldirparser/data/versioned-component/qmldir [new file with mode: 0644]
tests/auto/qml/qqmldirparser/data/versioned-script/qmldir [new file with mode: 0644]
tests/auto/qml/qqmldirparser/qqmldirparser.pro [new file with mode: 0644]
tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/data/implicit2/implicitQmldir.2.errors.txt
tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/invalidNamespaceModule.pro [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/plugin.cpp [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/qmldir [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/invalidStrictModule.pro [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/plugin.cpp [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/qmldir [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/nonstrictModule/nonstrictModule.pro [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/nonstrictModule/plugin.cpp [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/nonstrictModule/qmldir [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/plugin.cpp [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/preemptedStrictModule.pro [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/qmldir [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/preemptiveModule/plugin.cpp [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/preemptiveModule/preemptiveModule.pro [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/preemptiveModule/qmldir [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro
tests/auto/qml/qqmlmoduleplugin/strictModule/plugin.cpp [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/strictModule/qmldir [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/strictModule/strictModule.pro [new file with mode: 0644]
tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp
tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
tests/auto/quick/qquickwindow/data/ownershipRootItem.qml
tests/auto/quick/qquickwindow/tst_qquickwindow.cpp

index 6e115bb..bbd9135 100644 (file)
@@ -1 +1,2 @@
+module Qt.labs.folderlistmodel
 plugin qmlfolderlistmodelplugin
index 33288a1..8bb2c3b 100644 (file)
@@ -1 +1,2 @@
-plugin qmllocalstorageplugin
\ No newline at end of file
+module QtQuick.LocalStorage
+plugin qmllocalstorageplugin
index 593915f..25d58de 100644 (file)
@@ -1 +1,2 @@
+module QtQuick.Particles
 plugin particlesplugin
index bb4d33a..1fcbb13 100644 (file)
@@ -1 +1,2 @@
+module QtQuick
 plugin qtquick2plugin
index 9e872f9..d126464 100644 (file)
@@ -1,3 +1,4 @@
+module QtTest
 plugin qmltestplugin
 TestCase 1.0 TestCase.qml
 SignalSpy 1.0 SignalSpy.qml
index 32844a6..2dad388 100644 (file)
@@ -1 +1,2 @@
+module QtQuick.Window
 plugin windowplugin
index dd39bcd..945d15a 100644 (file)
@@ -1 +1,2 @@
-plugin qmlxmllistmodelplugin
\ No newline at end of file
+module QtQuick.XmlListModel
+plugin qmlxmllistmodelplugin
index 0805a24..705f715 100644 (file)
@@ -99,11 +99,13 @@ bool QQmlDirParser::parse(const QString &source)
     _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);
@@ -129,6 +131,7 @@ bool QQmlDirParser::parse(const QString &source)
             } else {
                 reportError(lineNumber, start-lineStart, QLatin1String("unexpected token"));
                 scanToEnd(ch);
+                invalidLine = true;
                 break;
             }
             scanSpace(ch);
@@ -137,9 +140,32 @@ bool QQmlDirParser::parse(const QString &source)
         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,
@@ -209,6 +235,8 @@ bool QQmlDirParser::parse(const QString &source)
             reportError(lineNumber, -1, 
                         QString::fromUtf8("a component declaration requires two or three arguments, but %1 were provided").arg(sectionCount));
         }
+
+        firstLine = false;
     }
 
     return hasError();
@@ -239,16 +267,28 @@ void QQmlDirParser::setError(const QQmlError &e)
 
 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;
index c38a3e1..7a90781 100644 (file)
@@ -62,7 +62,7 @@ QT_BEGIN_NAMESPACE
 
 class QQmlError;
 class QQmlEngine;
-class QQmlDirParser
+class Q_AUTOTEST_EXPORT QQmlDirParser
 {
     Q_DISABLE_COPY(QQmlDirParser)
 
@@ -76,6 +76,9 @@ public:
     void setError(const QQmlError &);
     QList<QQmlError> errors(const QString &uri) const;
 
+    QString typeNamespace() const;
+    void setTypeNamespace(const QString &s);
+
     struct Plugin
     {
         Plugin() {}
@@ -139,6 +142,7 @@ private:
 
 private:
     QList<QQmlError> _errors;
+    QString _typeNamespace;
     QHash<QHashedStringRef,Component> _components; // multi hash
     QList<Script> _scripts;
     QList<Plugin> _plugins;
index 72c1c35..65eeb24 100644 (file)
@@ -1729,7 +1729,7 @@ void QQmlEngine::setPluginPathList(const QStringList &paths)
 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);
 }
 
 /*!
index 20da154..e713399 100644 (file)
@@ -47,6 +47,7 @@
 #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>
@@ -734,7 +735,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath,
             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.
@@ -1590,7 +1591,7 @@ void QQmlImportDatabase::setImportPathList(const QStringList &paths)
 /*!
     \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;
@@ -1635,9 +1636,53 @@ bool QQmlImportDatabase::importPlugin(const QString &filePath, const QString &ur
             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 
index dbff6fd..2a1fe48 100644 (file)
@@ -143,7 +143,7 @@ public:
     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);
index c1147b0..b3a16b0 100644 (file)
@@ -108,6 +108,11 @@ struct QQmlMetaTypeData
     QBitArray lists;
 
     QList<QQmlPrivate::AutoParentFunction> parentFunctions;
+
+    QSet<QString> protectedNamespaces;
+
+    QString typeRegistrationNamespace;
+    QStringList typeRegistrationFailures;
 };
 
 class QQmlTypeModulePrivate
@@ -128,7 +133,7 @@ public:
 };
 
 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)
 {
@@ -193,7 +198,7 @@ public:
 
 // 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();
@@ -241,7 +246,7 @@ QQmlType::QQmlType(int index, const QQmlPrivate::RegisterInterface &interface)
 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;
@@ -902,6 +907,29 @@ int registerType(const QQmlPrivate::RegisterType &type)
 
     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);
@@ -985,6 +1013,46 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
     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.
 */
index fbba733..6f76c95 100644 (file)
@@ -69,6 +69,7 @@ class QQmlTypePrivate;
 class QQmlTypeModule;
 class QHashedString;
 class QHashedStringRef;
+class QReadWriteLock;
 
 class Q_QML_PRIVATE_EXPORT QQmlMetaType
 {
@@ -139,6 +140,15 @@ public:
     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;
 };
index c66cd28..0790b76 100644 (file)
@@ -1381,6 +1381,11 @@ QList<QQmlError> QQmlTypeLoader::QmldirContent::errors(const QString &uri) const
     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;
index 6461c1c..47f7742 100644 (file)
@@ -302,6 +302,8 @@ public:
         bool hasError() const;
         QList<QQmlError> errors(const QString &uri) const;
 
+        QString typeNamespace() const;
+
         QQmlDirComponents components() const;
         QQmlDirScripts scripts() const;
         QQmlDirPlugins plugins() const;
diff --git a/tests/auto/qml/qqmldirparser/data/empty/qmldir b/tests/auto/qml/qqmldirparser/data/empty/qmldir
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/auto/qml/qqmldirparser/data/excessive-module/qmldir b/tests/auto/qml/qqmldirparser/data/excessive-module/qmldir
new file mode 100644 (file)
index 0000000..c4fdbd4
--- /dev/null
@@ -0,0 +1 @@
+module foo bar
diff --git a/tests/auto/qml/qqmldirparser/data/excessive-plugin/qmldir b/tests/auto/qml/qqmldirparser/data/excessive-plugin/qmldir
new file mode 100644 (file)
index 0000000..4acde71
--- /dev/null
@@ -0,0 +1 @@
+plugin foo bar baz
diff --git a/tests/auto/qml/qqmldirparser/data/four-sections/qmldir b/tests/auto/qml/qqmldirparser/data/four-sections/qmldir
new file mode 100644 (file)
index 0000000..03b37a0
--- /dev/null
@@ -0,0 +1 @@
+foo bar baz qux
diff --git a/tests/auto/qml/qqmldirparser/data/incomplete-module/qmldir b/tests/auto/qml/qqmldirparser/data/incomplete-module/qmldir
new file mode 100644 (file)
index 0000000..0cca093
--- /dev/null
@@ -0,0 +1 @@
+module
diff --git a/tests/auto/qml/qqmldirparser/data/incomplete-plugin/qmldir b/tests/auto/qml/qqmldirparser/data/incomplete-plugin/qmldir
new file mode 100644 (file)
index 0000000..8cb205e
--- /dev/null
@@ -0,0 +1 @@
+plugin
diff --git a/tests/auto/qml/qqmldirparser/data/invalid-versioned-component/qmldir b/tests/auto/qml/qqmldirparser/data/invalid-versioned-component/qmldir
new file mode 100644 (file)
index 0000000..c322b0f
--- /dev/null
@@ -0,0 +1 @@
+foo 100 bar
diff --git a/tests/auto/qml/qqmldirparser/data/multiple/qmldir b/tests/auto/qml/qqmldirparser/data/multiple/qmldir
new file mode 100644 (file)
index 0000000..e74bad5
--- /dev/null
@@ -0,0 +1,15 @@
+
+#
+# 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
+
diff --git a/tests/auto/qml/qqmldirparser/data/name-path-plugin/qmldir b/tests/auto/qml/qqmldirparser/data/name-path-plugin/qmldir
new file mode 100644 (file)
index 0000000..5cf8bd3
--- /dev/null
@@ -0,0 +1 @@
+plugin foo bar
diff --git a/tests/auto/qml/qqmldirparser/data/name-plugin/qmldir b/tests/auto/qml/qqmldirparser/data/name-plugin/qmldir
new file mode 100644 (file)
index 0000000..fb12cab
--- /dev/null
@@ -0,0 +1 @@
+plugin foo
diff --git a/tests/auto/qml/qqmldirparser/data/no-content/qmldir b/tests/auto/qml/qqmldirparser/data/no-content/qmldir
new file mode 100644 (file)
index 0000000..3ce87ad
--- /dev/null
@@ -0,0 +1,4 @@
+
+# only empty lines
+        # and comments
+
diff --git a/tests/auto/qml/qqmldirparser/data/non-first-module/qmldir b/tests/auto/qml/qqmldirparser/data/non-first-module/qmldir
new file mode 100644 (file)
index 0000000..932e43a
--- /dev/null
@@ -0,0 +1,2 @@
+plugin foo
+module bar
diff --git a/tests/auto/qml/qqmldirparser/data/one-section/qmldir b/tests/auto/qml/qqmldirparser/data/one-section/qmldir
new file mode 100644 (file)
index 0000000..257cc56
--- /dev/null
@@ -0,0 +1 @@
+foo
diff --git a/tests/auto/qml/qqmldirparser/data/repeated-module/qmldir b/tests/auto/qml/qqmldirparser/data/repeated-module/qmldir
new file mode 100644 (file)
index 0000000..80c3e0c
--- /dev/null
@@ -0,0 +1,2 @@
+module foo
+module bar
diff --git a/tests/auto/qml/qqmldirparser/data/unversioned-component/qmldir b/tests/auto/qml/qqmldirparser/data/unversioned-component/qmldir
new file mode 100644 (file)
index 0000000..d675fa4
--- /dev/null
@@ -0,0 +1 @@
+foo bar
diff --git a/tests/auto/qml/qqmldirparser/data/versioned-component/qmldir b/tests/auto/qml/qqmldirparser/data/versioned-component/qmldir
new file mode 100644 (file)
index 0000000..a2afd18
--- /dev/null
@@ -0,0 +1 @@
+foo 33.66 bar
diff --git a/tests/auto/qml/qqmldirparser/data/versioned-script/qmldir b/tests/auto/qml/qqmldirparser/data/versioned-script/qmldir
new file mode 100644 (file)
index 0000000..1345a68
--- /dev/null
@@ -0,0 +1 @@
+foo 33.66 bar.js
diff --git a/tests/auto/qml/qqmldirparser/qqmldirparser.pro b/tests/auto/qml/qqmldirparser/qqmldirparser.pro
new file mode 100644 (file)
index 0000000..8efb415
--- /dev/null
@@ -0,0 +1,10 @@
+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
diff --git a/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp b/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp
new file mode 100644 (file)
index 0000000..82616ff
--- /dev/null
@@ -0,0 +1,288 @@
+/****************************************************************************
+**
+** 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"
index 9cafb78..08492af 100644 (file)
@@ -1,3 +1,4 @@
 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
diff --git a/tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/invalidNamespaceModule.pro b/tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/invalidNamespaceModule.pro
new file mode 100644 (file)
index 0000000..36b0540
--- /dev/null
@@ -0,0 +1,12 @@
+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)
diff --git a/tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/plugin.cpp
new file mode 100644 (file)
index 0000000..d682510
--- /dev/null
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** 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"
diff --git a/tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/qmldir b/tests/auto/qml/qqmlmoduleplugin/invalidNamespaceModule/qmldir
new file mode 100644 (file)
index 0000000..5f34970
--- /dev/null
@@ -0,0 +1,2 @@
+module com.nokia.AwesomeModule
+plugin invalidNamespaceModule
diff --git a/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/invalidStrictModule.pro b/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/invalidStrictModule.pro
new file mode 100644 (file)
index 0000000..7caa6c4
--- /dev/null
@@ -0,0 +1,12 @@
+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)
diff --git a/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/plugin.cpp
new file mode 100644 (file)
index 0000000..d682510
--- /dev/null
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** 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"
diff --git a/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/qmldir b/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/qmldir
new file mode 100644 (file)
index 0000000..45752a9
--- /dev/null
@@ -0,0 +1,2 @@
+module com.nokia.InvalidStrictModule
+plugin invalidStrictModule
diff --git a/tests/auto/qml/qqmlmoduleplugin/nonstrictModule/nonstrictModule.pro b/tests/auto/qml/qqmlmoduleplugin/nonstrictModule/nonstrictModule.pro
new file mode 100644 (file)
index 0000000..e295784
--- /dev/null
@@ -0,0 +1,12 @@
+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)
diff --git a/tests/auto/qml/qqmlmoduleplugin/nonstrictModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/nonstrictModule/plugin.cpp
new file mode 100644 (file)
index 0000000..73e1056
--- /dev/null
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** 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"
diff --git a/tests/auto/qml/qqmlmoduleplugin/nonstrictModule/qmldir b/tests/auto/qml/qqmlmoduleplugin/nonstrictModule/qmldir
new file mode 100644 (file)
index 0000000..7371ee7
--- /dev/null
@@ -0,0 +1 @@
+plugin nonstrictModule
diff --git a/tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/plugin.cpp
new file mode 100644 (file)
index 0000000..f7f7c77
--- /dev/null
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** 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"
diff --git a/tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/preemptedStrictModule.pro b/tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/preemptedStrictModule.pro
new file mode 100644 (file)
index 0000000..76934e7
--- /dev/null
@@ -0,0 +1,12 @@
+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)
diff --git a/tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/qmldir b/tests/auto/qml/qqmlmoduleplugin/preemptedStrictModule/qmldir
new file mode 100644 (file)
index 0000000..c82acd2
--- /dev/null
@@ -0,0 +1,2 @@
+module com.nokia.PreemptedStrictModule
+plugin preemptedStrictModule
diff --git a/tests/auto/qml/qqmlmoduleplugin/preemptiveModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/preemptiveModule/plugin.cpp
new file mode 100644 (file)
index 0000000..0b3dd78
--- /dev/null
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** 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"
diff --git a/tests/auto/qml/qqmlmoduleplugin/preemptiveModule/preemptiveModule.pro b/tests/auto/qml/qqmlmoduleplugin/preemptiveModule/preemptiveModule.pro
new file mode 100644 (file)
index 0000000..e50c722
--- /dev/null
@@ -0,0 +1,12 @@
+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)
diff --git a/tests/auto/qml/qqmlmoduleplugin/preemptiveModule/qmldir b/tests/auto/qml/qqmlmoduleplugin/preemptiveModule/qmldir
new file mode 100644 (file)
index 0000000..d240caf
--- /dev/null
@@ -0,0 +1 @@
+plugin preemptiveModule
index 6da8832..c020ce9 100644 (file)
@@ -1,6 +1,21 @@
 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
 
diff --git a/tests/auto/qml/qqmlmoduleplugin/strictModule/plugin.cpp b/tests/auto/qml/qqmlmoduleplugin/strictModule/plugin.cpp
new file mode 100644 (file)
index 0000000..4bc8108
--- /dev/null
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** 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"
diff --git a/tests/auto/qml/qqmlmoduleplugin/strictModule/qmldir b/tests/auto/qml/qqmlmoduleplugin/strictModule/qmldir
new file mode 100644 (file)
index 0000000..ff06446
--- /dev/null
@@ -0,0 +1,2 @@
+module com.nokia.StrictModule
+plugin strictModule
diff --git a/tests/auto/qml/qqmlmoduleplugin/strictModule/strictModule.pro b/tests/auto/qml/qqmlmoduleplugin/strictModule/strictModule.pro
new file mode 100644 (file)
index 0000000..9c6d83f
--- /dev/null
@@ -0,0 +1,12 @@
+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)
index 67b3d00..99e2874 100644 (file)
 #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
 {
@@ -74,6 +78,8 @@ private slots:
     void importsNested_data();
     void importLocalModule();
     void importLocalModule_data();
+    void importStrictModule();
+    void importStrictModule_data();
 
 private:
     QString m_importsDirectory;
@@ -135,6 +141,7 @@ void tst_qqmlmoduleplugin::importsPlugin()
     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;
@@ -151,6 +158,7 @@ void tst_qqmlmoduleplugin::importsPlugin2()
     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;
@@ -167,6 +175,7 @@ void tst_qqmlmoduleplugin::importsPlugin21()
     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;
@@ -215,6 +224,8 @@ void tst_qqmlmoduleplugin::importPluginWithQmlFile()
     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;
@@ -275,6 +286,8 @@ void tst_qqmlmoduleplugin::importsMixedQmlCppPlugin()
     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")));
 
@@ -314,6 +327,10 @@ void tst_qqmlmoduleplugin::versionNotInstalled()
     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());
 }
@@ -382,6 +399,10 @@ void tst_qqmlmoduleplugin::importsNested()
         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();
 
@@ -446,6 +467,71 @@ void tst_qqmlmoduleplugin::importLocalModule_data()
         << 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"
index b5ab04c..56fb1e9 100644 (file)
@@ -642,7 +642,13 @@ void tst_qquickanimations::resume()
     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();
index dfc4159..4b892b2 100644 (file)
@@ -1,5 +1,6 @@
 import QtQuick 2.0
 import QtQuick.Window 2.0 as Window
+import Test 1.0
 
 Window.Window {
 RootItemAccessor {
index 01d6036..f29df69 100644 (file)
@@ -1046,7 +1046,7 @@ void tst_qquickwindow::ignoreUnhandledMouseEvents()
 
 void tst_qquickwindow::ownershipRootItem()
 {
-    qmlRegisterType<RootItemAccessor>("QtQuick", 2, 0, "RootItemAccessor");
+    qmlRegisterType<RootItemAccessor>("Test", 1, 0, "RootItemAccessor");
 
     QQmlEngine engine;
     QQmlComponent component(&engine);