aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml
diff options
context:
space:
mode:
authorMatthew Vogt <matthew.vogt@nokia.com>2012-07-25 16:59:17 +1000
committerQt by Nokia <qt-info@nokia.com>2012-07-31 00:22:36 +0200
commitc9b7582a2e7ad9fcd03dd999c3b7a16b72803238 (patch)
treed1ffdb193576fef0c243600f46c69b180d2ad2a8 /src/qml/qml
parent2e6accbbbb9783ff6e5ad171f179d5021b0761af (diff)
Implement strict mode for qmldir modules
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>
Diffstat (limited to 'src/qml/qml')
-rw-r--r--src/qml/qml/qqmldirparser.cpp42
-rw-r--r--src/qml/qml/qqmldirparser_p.h6
-rw-r--r--src/qml/qml/qqmlengine.cpp2
-rw-r--r--src/qml/qml/qqmlimport.cpp53
-rw-r--r--src/qml/qml/qqmlimport_p.h2
-rw-r--r--src/qml/qml/qqmlmetatype.cpp74
-rw-r--r--src/qml/qml/qqmlmetatype_p.h10
-rw-r--r--src/qml/qml/qqmltypeloader.cpp5
-rw-r--r--src/qml/qml/qqmltypeloader_p.h2
9 files changed, 185 insertions, 11 deletions
diff --git a/src/qml/qml/qqmldirparser.cpp b/src/qml/qml/qqmldirparser.cpp
index 0805a24e6d..705f715a00 100644
--- a/src/qml/qml/qqmldirparser.cpp
+++ b/src/qml/qml/qqmldirparser.cpp
@@ -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;
diff --git a/src/qml/qml/qqmldirparser_p.h b/src/qml/qml/qqmldirparser_p.h
index c38a3e1791..7a9078180e 100644
--- a/src/qml/qml/qqmldirparser_p.h
+++ b/src/qml/qml/qqmldirparser_p.h
@@ -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;
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 72c1c35244..65eeb242bb 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -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);
}
/*!
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index 20da154673..e7133992d9 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -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
diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h
index dbff6fd219..2a1fe48b6e 100644
--- a/src/qml/qml/qqmlimport_p.h
+++ b/src/qml/qml/qqmlimport_p.h
@@ -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);
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index c1147b025b..b3a16b034c 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -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.
*/
diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h
index fbba733018..6f76c95544 100644
--- a/src/qml/qml/qqmlmetatype_p.h
+++ b/src/qml/qml/qqmlmetatype_p.h
@@ -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;
};
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index c66cd283da..0790b7664e 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -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;
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index 6461c1c484..47f7742cf0 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -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;