aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/qqmltypeloader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/qqmltypeloader.cpp')
-rw-r--r--src/qml/qml/qqmltypeloader.cpp312
1 files changed, 226 insertions, 86 deletions
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index abe2c0bfa4..2e8f17e6cd 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -53,9 +53,10 @@
#include <QtCore/qdebug.h>
#include <QtCore/qmutex.h>
#include <QtCore/qthread.h>
+#include <QtQml/qqmlfile.h>
#include <QtCore/qdiriterator.h>
-#include <QtCore/qwaitcondition.h>
#include <QtQml/qqmlcomponent.h>
+#include <QtCore/qwaitcondition.h>
#include <QtQml/qqmlextensioninterface.h>
#if defined (Q_OS_UNIX)
@@ -92,6 +93,8 @@
#endif
+DEFINE_BOOL_CONFIG_OPTION(dumpErrors, QML_DUMP_ERRORS);
+
QT_BEGIN_NAMESPACE
// This is a lame object that we need to ensure that slots connected to
@@ -453,6 +456,11 @@ void QQmlDataBlob::setError(const QList<QQmlError> &errors)
m_errors = errors; // Must be set before the m_data fence
m_data.setStatus(Error);
+ if (dumpErrors()) {
+ qWarning().nospace() << "Errors for " << m_finalUrl.toString();
+ for (int ii = 0; ii < errors.count(); ++ii)
+ qWarning().nospace() << " " << qPrintable(errors.at(ii).toString());
+ }
cancelAllWaitingFor();
if (!m_inCallback)
@@ -486,7 +494,7 @@ void QQmlDataBlob::addDependency(QQmlDataBlob *blob)
}
/*!
-\fn void QQmlDataBlob::dataReceived(const QByteArray &data)
+\fn void QQmlDataBlob::dataReceived(const Data &data)
Invoked when data for the blob is received. Implementors should use this callback
to determine a blob's dependencies. Within this callback you may call setError()
@@ -1018,28 +1026,22 @@ void QQmlDataLoader::loadThread(QQmlDataBlob *blob)
return;
}
- QString lf = QQmlEnginePrivate::urlToLocalFileOrQrc(blob->m_url);
+ if (QQmlFile::isSynchronous(blob->m_url)) {
+ QQmlFile file(m_engine, blob->m_url);
- if (!lf.isEmpty()) {
- if (!QQml_isFileCaseCorrect(lf)) {
+ if (file.isError()) {
QQmlError error;
error.setUrl(blob->m_url);
- error.setDescription(QLatin1String("File name case mismatch"));
+ error.setDescription(file.error());
blob->setError(error);
return;
}
- QFile file(lf);
- if (file.open(QFile::ReadOnly)) {
- QByteArray data = file.readAll();
- blob->m_data.setProgress(0xFF);
- if (blob->m_data.isAsync())
- m_thread->callDownloadProgressChanged(blob, 1.);
+ blob->m_data.setProgress(0xFF);
+ if (blob->m_data.isAsync())
+ m_thread->callDownloadProgressChanged(blob, 1.);
- setData(blob, data);
- } else {
- blob->networkError(QNetworkReply::ContentNotFoundError);
- }
+ setData(blob, &file);
} else {
@@ -1138,9 +1140,23 @@ void QQmlDataLoader::initializeEngine(QQmlExtensionInterface *iface,
void QQmlDataLoader::setData(QQmlDataBlob *blob, const QByteArray &data)
{
+ QQmlDataBlob::Data d;
+ d.d = &data;
+ setData(blob, d);
+}
+
+void QQmlDataLoader::setData(QQmlDataBlob *blob, QQmlFile *file)
+{
+ QQmlDataBlob::Data d;
+ d.d = file;
+ setData(blob, d);
+}
+
+void QQmlDataLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::Data &d)
+{
blob->m_inCallback = true;
- blob->dataReceived(data);
+ blob->dataReceived(d);
if (!blob->isError() && !blob->isWaiting())
blob->allDependenciesDone();
@@ -1187,8 +1203,8 @@ Returns a QQmlTypeData for the specified \a url. The QQmlTypeData may be cached
QQmlTypeData *QQmlTypeLoader::get(const QUrl &url, Mode mode)
{
Q_ASSERT(!url.isRelative() &&
- (QQmlEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() ||
- !QDir::isRelativePath(QQmlEnginePrivate::urlToLocalFileOrQrc(url))));
+ (QQmlFile::urlToLocalFileOrQrc(url).isEmpty() ||
+ !QDir::isRelativePath(QQmlFile::urlToLocalFileOrQrc(url))));
lock();
@@ -1231,8 +1247,8 @@ Return a QQmlScriptBlob for \a url. The QQmlScriptData may be cached.
QQmlScriptBlob *QQmlTypeLoader::getScript(const QUrl &url)
{
Q_ASSERT(!url.isRelative() &&
- (QQmlEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() ||
- !QDir::isRelativePath(QQmlEnginePrivate::urlToLocalFileOrQrc(url))));
+ (QQmlFile::urlToLocalFileOrQrc(url).isEmpty() ||
+ !QDir::isRelativePath(QQmlFile::urlToLocalFileOrQrc(url))));
lock();
@@ -1257,8 +1273,8 @@ Returns a QQmlQmldirData for \a url. The QQmlQmldirData may be cached.
QQmlQmldirData *QQmlTypeLoader::getQmldir(const QUrl &url)
{
Q_ASSERT(!url.isRelative() &&
- (QQmlEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() ||
- !QDir::isRelativePath(QQmlEnginePrivate::urlToLocalFileOrQrc(url))));
+ (QQmlFile::urlToLocalFileOrQrc(url).isEmpty() ||
+ !QDir::isRelativePath(QQmlFile::urlToLocalFileOrQrc(url))));
lock();
@@ -1278,6 +1294,84 @@ QQmlQmldirData *QQmlTypeLoader::getQmldir(const QUrl &url)
}
/*!
+Returns a QQmlBundleData for \a identifier.
+*/
+QQmlBundleData *QQmlTypeLoader::getBundle(const QString &identifier)
+{
+ return getBundle(QHashedStringRef(identifier));
+}
+
+QQmlBundleData *QQmlTypeLoader::getBundle(const QHashedStringRef &identifier)
+{
+ lock();
+
+ QQmlBundleData *rv = 0;
+ QQmlBundleData **bundle = m_bundleCache.value(identifier);
+ if (bundle) {
+ rv = *bundle;
+ rv->addref();
+ }
+
+ unlock();
+
+ return rv;
+}
+
+QQmlBundleData::QQmlBundleData(const QString &file)
+: QQmlBundle(file), fileName(file)
+{
+}
+
+// XXX check for errors etc.
+void QQmlTypeLoader::addBundle(const QString &identifier, const QString &fileName)
+{
+ lock();
+ addBundleNoLock(identifier, fileName);
+ unlock();
+}
+
+void QQmlTypeLoader::addBundleNoLock(const QString &identifier, const QString &fileName)
+{
+ QQmlBundleData *data = new QQmlBundleData(fileName);
+ if (data->open()) {
+
+ m_bundleCache.insert(identifier, data);
+
+ } else {
+ data->release();
+ }
+}
+
+QString QQmlTypeLoader::bundleIdForQmldir(const QString &name, const QString &uriHint)
+{
+ lock();
+ QString *bundleId = m_qmldirBundleIdCache.value(name);
+ if (!bundleId) {
+ QString newBundleId = QLatin1String("qml.") + uriHint.toLower() /* XXX toLower()? */;
+ if (m_qmldirBundleIdCache.contains(newBundleId))
+ newBundleId += QString::number(m_qmldirBundleIdCache.count());
+ m_qmldirBundleIdCache.insert(name, newBundleId);
+ addBundleNoLock(newBundleId, name);
+ unlock();
+ return newBundleId;
+ } else {
+ unlock();
+ return *bundleId;
+ }
+}
+
+bool QQmlEngine::addNamedBundle(const QString &name, const QString &fileName)
+{
+ Q_D(QQmlEngine);
+
+ if (name.startsWith(QLatin1String("qml."))) // reserved
+ return false;
+
+ d->typeLoader.addBundle(name, fileName);
+ return true;
+}
+
+/*!
Returns the absolute filename of path via a directory cache for files named
"qmldir", "*.qml", "*.js", and plugins.
Returns a empty string if the path does not exist.
@@ -1366,21 +1460,79 @@ bool QQmlTypeLoader::directoryExists(const QString &path)
/*!
Return a QQmlDirParser for absoluteFilePath. The QQmlDirParser may be cached.
+
+\a filePath is either a bundle URL, or a local file path.
*/
-const QQmlDirParser *QQmlTypeLoader::qmlDirParser(const QString &absoluteFilePath)
+const QQmlDirParser *QQmlTypeLoader::qmlDirParser(const QString &filePath,
+ const QString &uriHint,
+ QString *outUrl)
{
- QQmlDirParser *qmldirParser;
- QQmlDirParser **val = m_importQmlDirCache.value(absoluteFilePath);
+ DirParser *qmldirParser;
+ DirParser **val = m_importQmlDirCache.value(filePath);
if (!val) {
- qmldirParser = new QQmlDirParser;
- qmldirParser->setFileSource(absoluteFilePath);
- qmldirParser->setUrl(QUrl::fromLocalFile(absoluteFilePath));
- qmldirParser->parse();
- m_importQmlDirCache.insert(absoluteFilePath, qmldirParser);
+ qmldirParser = new DirParser;
+
+#define ERROR(description) { QQmlError e; e.setDescription(description); qmldirParser->setError(e); }
+#define NOT_READABLE_ERROR QString(QLatin1String("module \"$$URI$$\" definition \"%1\" not readable"))
+#define CASE_MISMATCH_ERROR QString(QLatin1String("cannot load module \"$$URI$$\": File name case mismatch for \"%1\""))
+
+ if (QQmlFile::isBundle(filePath)) {
+
+ QUrl url(filePath);
+
+ QQmlFile file(engine(), url);
+ if (file.isError()) {
+ ERROR(NOT_READABLE_ERROR.arg(filePath));
+ } else {
+ qmldirParser->setSource(QString::fromUtf8(file.data(), file.size()));
+ qmldirParser->parse();
+ }
+
+ } else {
+
+ QFile file(filePath);
+ if (!QQml_isFileCaseCorrect(filePath)) {
+ ERROR(CASE_MISMATCH_ERROR.arg(filePath));
+ } else if (file.open(QFile::ReadOnly)) {
+ QByteArray data = file.read(QQmlBundle::bundleHeaderLength());
+
+ if (QQmlBundle::isBundleHeader(data.constData(), data.length())) {
+ QString id = bundleIdForQmldir(filePath, uriHint);
+
+ QString bundleUrl = QLatin1String("bundle://") + id + QLatin1String("/");
+ qmldirParser->adjustedUrl = bundleUrl;
+
+ QUrl url(bundleUrl + QLatin1String("qmldir"));
+
+ QQmlFile file(engine(), url);
+ if (file.isError()) {
+ ERROR(NOT_READABLE_ERROR.arg(filePath));
+ } else {
+ qmldirParser->setSource(QString::fromUtf8(file.data(), file.size()));
+ qmldirParser->parse();
+ }
+ } else {
+ data += file.readAll();
+ qmldirParser->setSource(QString::fromUtf8(data));
+ qmldirParser->parse();
+ }
+ } else {
+ ERROR(NOT_READABLE_ERROR.arg(filePath));
+ }
+
+ }
+
+#undef ERROR
+#undef NOT_READABLE_ERROR
+#undef CASE_MISMATCH_ERROR
+
+ m_importQmlDirCache.insert(filePath, qmldirParser);
} else {
qmldirParser = *val;
}
+ if (!qmldirParser->adjustedUrl.isEmpty())
+ *outUrl = qmldirParser->adjustedUrl;
return qmldirParser;
}
@@ -1531,9 +1683,14 @@ void QQmlTypeData::completed()
}
}
-void QQmlTypeData::dataReceived(const QByteArray &data)
+void QQmlTypeData::dataReceived(const Data &data)
{
- if (!scriptParser.parse(data, finalUrl(), finalUrlString())) {
+ QString code = QString::fromUtf8(data.data(), data.size());
+ QByteArray preparseData;
+
+ if (data.isFile()) preparseData = data.asFile()->metaData(QLatin1String("qml:preparse"));
+
+ if (!scriptParser.parse(code, preparseData, finalUrl(), finalUrlString())) {
setError(scriptParser.errors());
return;
}
@@ -1543,7 +1700,7 @@ void QQmlTypeData::dataReceived(const QByteArray &data)
foreach (const QQmlScript::Import &import, scriptParser.imports()) {
if (import.type == QQmlScript::Import::File && import.qualifier.isEmpty()) {
QUrl importUrl = finalUrl().resolved(QUrl(import.uri + QLatin1String("/qmldir")));
- if (QQmlEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) {
+ if (QQmlFile::urlToLocalFileOrQrc(importUrl).isEmpty()) {
QQmlQmldirData *data = typeLoader()->getQmldir(importUrl);
addDependency(data);
m_qmldirs << data;
@@ -1563,7 +1720,7 @@ void QQmlTypeData::dataReceived(const QByteArray &data)
if (!finalUrl().scheme().isEmpty()) {
QUrl importUrl = finalUrl().resolved(QUrl(QLatin1String("qmldir")));
- if (QQmlEnginePrivate::urlToLocalFileOrQrc(importUrl).isEmpty()) {
+ if (QQmlFile::urlToLocalFileOrQrc(importUrl).isEmpty()) {
QQmlQmldirData *data = typeLoader()->getQmldir(importUrl);
addDependency(data);
m_qmldirs << data;
@@ -1613,30 +1770,15 @@ void QQmlTypeData::resolveTypes()
// For local urls, add an implicit import "." as first (most overridden) lookup.
// This will also trigger the loading of the qmldir and the import of any native
// types from available plugins.
- QList<QQmlError> errors;
+ QList<QQmlError> implicitImportErrors;
if (QQmlQmldirData *qmldir = qmldirForUrl(finalUrl().resolved(QUrl(QLatin1String("./qmldir"))))) {
- m_imports.addImport(importDatabase, QLatin1String("."),
- QString(), -1, -1, QQmlScript::Import::File,
- qmldir->dirComponents(), &errors);
+ m_imports.addImplicitImport(importDatabase, qmldir->dirComponents(), &implicitImportErrors);
} else {
- m_imports.addImport(importDatabase, QLatin1String("."),
- QString(), -1, -1, QQmlScript::Import::File,
- QQmlDirComponents(), &errors);
- }
-
- // remove any errors which are due to the implicit import which aren't real errors.
- // for example, if the implicitly included qmldir file doesn't exist, that is not an error.
- QList<QQmlError> realErrors;
- for (int i = 0; i < errors.size(); ++i) {
- if (errors.at(i).description() != QQmlImportDatabase::tr("import \".\" has no qmldir and no namespace")
- && errors.at(i).description() != QQmlImportDatabase::tr("\".\": no such directory")) {
- realErrors.prepend(errors.at(i)); // this is a real error.
- }
+ m_imports.addImplicitImport(importDatabase, QQmlDirComponents(), &implicitImportErrors);
}
- // report any real errors which occurred during plugin loading or qmldir parsing.
- if (!realErrors.isEmpty()) {
- setError(realErrors);
+ if (!implicitImportErrors.isEmpty()) {
+ setError(implicitImportErrors);
return;
}
@@ -1651,13 +1793,10 @@ void QQmlTypeData::resolveTypes()
qmldircomponentsnetwork = qmldir->dirComponents();
}
- int vmaj = -1;
- int vmin = -1;
- import.extractVersion(&vmaj, &vmin);
-
QList<QQmlError> errors;
- if (m_imports.addImport(importDatabase, import.uri, import.qualifier, vmaj, vmin,
- import.type, qmldircomponentsnetwork, &errors).isNull()) {
+ if (!m_imports.addImport(importDatabase, import.uri, import.qualifier, import.majorVersion,
+ import.minorVersion, import.type, qmldircomponentsnetwork, 0,
+ &errors)) {
QQmlError error;
if (errors.size()) {
error = errors.takeFirst();
@@ -1703,7 +1842,7 @@ void QQmlTypeData::resolveTypes()
QString url;
int majorVersion;
int minorVersion;
- QQmlImportedNamespace *typeNamespace = 0;
+ QQmlImportNamespace *typeNamespace = 0;
QList<QQmlError> errors;
if (!m_imports.resolveType(parserRef->name, &ref.type, &url, &majorVersion, &minorVersion,
@@ -1824,12 +1963,12 @@ QQmlScriptData *QQmlScriptBlob::scriptData() const
return m_scriptData;
}
-void QQmlScriptBlob::dataReceived(const QByteArray &data)
+void QQmlScriptBlob::dataReceived(const Data &data)
{
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_typeLoader->engine());
QQmlImportDatabase *importDatabase = &ep->importDatabase;
- m_source = QString::fromUtf8(data);
+ m_source = QString::fromUtf8(data.data(), data.size());
QQmlScript::Parser::JavaScriptMetaData metadata =
QQmlScript::Parser::extractMetaData(m_source);
@@ -1853,14 +1992,11 @@ void QQmlScriptBlob::dataReceived(const QByteArray &data)
m_scripts << ref;
} else {
Q_ASSERT(import.type == QQmlScript::Import::Library);
- int vmaj = -1;
- int vmin = -1;
- import.extractVersion(&vmaj, &vmin);
-
QList<QQmlError> errors;
- QString importUrl = m_imports.addImport(importDatabase, import.uri, import.qualifier, vmaj, vmin,
- import.type, QQmlDirComponents(), &errors);
- if (importUrl.isNull()) {
+ QString importUrl;
+ if (!m_imports.addImport(importDatabase, import.uri, import.qualifier, import.majorVersion,
+ import.minorVersion, import.type, QQmlDirComponents(), &importUrl,
+ &errors)) {
QQmlError error = errors.takeFirst();
// description should be set by addImport().
error.setUrl(m_imports.baseUrl());
@@ -1873,19 +2009,23 @@ void QQmlScriptBlob::dataReceived(const QByteArray &data)
}
// Does this library contain any scripts?
- QUrl libraryUrl(importUrl);
- const QQmlDirParser *dirParser = typeLoader()->qmlDirParser(libraryUrl.path() + QLatin1String("qmldir"));
- foreach (const QQmlDirParser::Script &script, dirParser->scripts()) {
- QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName));
- QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl);
- addDependency(blob);
-
- ScriptReference ref;
- ref.location = import.location.start;
- ref.qualifier = script.nameSpace;
- ref.nameSpace = import.qualifier;
- ref.script = blob;
- m_scripts << ref;
+ if (!importUrl.isEmpty()) {
+ QUrl libraryUrl(importUrl);
+ // XXX is this logic even correct???
+ QString adjustedUrl;
+ const QQmlDirParser *dirParser = typeLoader()->qmlDirParser(libraryUrl.path() + QLatin1String("qmldir"), QString(), &adjustedUrl);
+ foreach (const QQmlDirParser::Script &script, dirParser->scripts()) {
+ QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName));
+ QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl);
+ addDependency(blob);
+
+ ScriptReference ref;
+ ref.location = import.location.start;
+ ref.qualifier = script.nameSpace;
+ ref.nameSpace = import.qualifier;
+ ref.script = blob;
+ m_scripts << ref;
+ }
}
}
}
@@ -1951,10 +2091,10 @@ const QQmlDirComponents &QQmlQmldirData::dirComponents() const
return m_components;
}
-void QQmlQmldirData::dataReceived(const QByteArray &data)
+void QQmlQmldirData::dataReceived(const Data &data)
{
QQmlDirParser parser;
- parser.setSource(QString::fromUtf8(data));
+ parser.setSource(QString::fromUtf8(data.data(), data.size()));
parser.parse();
m_components = parser.components();
}