diff options
Diffstat (limited to 'src/qml')
-rw-r--r-- | src/qml/qml/qqmldirparser.cpp | 42 | ||||
-rw-r--r-- | src/qml/qml/qqmldirparser_p.h | 6 | ||||
-rw-r--r-- | src/qml/qml/qqmlengine.cpp | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmlimport.cpp | 53 | ||||
-rw-r--r-- | src/qml/qml/qqmlimport_p.h | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmlmetatype.cpp | 74 | ||||
-rw-r--r-- | src/qml/qml/qqmlmetatype_p.h | 10 | ||||
-rw-r--r-- | src/qml/qml/qqmltypeloader.cpp | 5 | ||||
-rw-r--r-- | src/qml/qml/qqmltypeloader_p.h | 2 |
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; |